diff --git a/aurorastation.dme b/aurorastation.dme index bbc3427b138..4b2a9eda357 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -61,6 +61,7 @@ #include "code\__defines\radio.dm" #include "code\__defines\regex.dm" #include "code\__defines\research.dm" +#include "code\__defines\ruin_tags.dm" #include "code\__defines\rust_g.dm" #include "code\__defines\shuttle.dm" #include "code\__defines\space_sectors.dm" @@ -297,6 +298,7 @@ #include "code\datums\position_point_vector.dm" #include "code\datums\progressbar.dm" #include "code\datums\records.dm" +#include "code\datums\ruins.dm" #include "code\datums\statistic.dm" #include "code\datums\weakref.dm" #include "code\datums\components\_component.dm" @@ -323,6 +325,7 @@ #include "code\datums\looping_sounds\revenant_rift.dm" #include "code\datums\looping_sounds\thermal_drill.dm" #include "code\datums\observation\_debug.dm" +#include "code\datums\observation\death.dm" #include "code\datums\observation\destroyed.dm" #include "code\datums\observation\dir_set.dm" #include "code\datums\observation\entered.dm" @@ -333,6 +336,7 @@ #include "code\datums\observation\observation.dm" #include "code\datums\observation\see_in_dark_set.dm" #include "code\datums\observation\see_invisible_set.dm" +#include "code\datums\observation\shuttle_moved.dm" #include "code\datums\observation\sight_set.dm" #include "code\datums\outfits\outfit.dm" #include "code\datums\outfits\outfit_admin.dm" @@ -360,6 +364,7 @@ #include "code\datums\repositories\crew.dm" #include "code\datums\repositories\decls.dm" #include "code\datums\repositories\repository.dm" +#include "code\datums\repositories\unique.dm" #include "code\datums\trading\_trading_defines.dm" #include "code\datums\trading\ai.dm" #include "code\datums\trading\food.dm" @@ -1127,6 +1132,7 @@ #include "code\game\objects\structures\morgue.dm" #include "code\game\objects\structures\musician.dm" #include "code\game\objects\structures\noticeboard.dm" +#include "code\game\objects\structures\pit.dm" #include "code\game\objects\structures\plasticflaps.dm" #include "code\game\objects\structures\railing.dm" #include "code\game\objects\structures\safe.dm" @@ -1968,7 +1974,12 @@ #include "code\modules\maps\dmm_suite.dm" #include "code\modules\maps\map_template.dm" #include "code\modules\maps\reader.dm" +#include "code\modules\maps\ruins.dm" #include "code\modules\maps\swapmaps.dm" +#include "code\modules\maps\planet_types\barren.dm" +#include "code\modules\maps\planet_types\desert.dm" +#include "code\modules\maps\planet_types\grass.dm" +#include "code\modules\maps\planet_types\snow.dm" #include "code\modules\martial_arts\gunkata.dm" #include "code\modules\martial_arts\martial.dm" #include "code\modules\martial_arts\plasma_fist.dm" @@ -2335,6 +2346,7 @@ #include "code\modules\mob\living\simple_animal\hostile\hivebots\hivebot_projectiles.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\cavern.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm" +#include "code\modules\mob\living\simple_animal\hostile\retaliate\exoplanet.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm" #include "code\modules\mob\living\simple_animal\mechanical\mechanical.dm" #include "code\modules\modular_computers\laptop_vendor.dm" @@ -2515,6 +2527,10 @@ #include "code\modules\overmap\sectors.dm" #include "code\modules\overmap\spacetravel.dm" #include "code\modules\overmap\events\event.dm" +#include "code\modules\overmap\exoplanets\exoplanet.dm" +#include "code\modules\overmap\exoplanets\random_map.dm" +#include "code\modules\overmap\exoplanets\theme.dm" +#include "code\modules\overmap\exoplanets\turfs.dm" #include "code\modules\overmap\ships\landable.dm" #include "code\modules\overmap\ships\ship.dm" #include "code\modules\overmap\ships\computers\engine_control.dm" @@ -3051,6 +3067,7 @@ #include "maps\exodus\code\exodus_holodeck.dm" #include "maps\exodus\code\exodus_shuttles.dm" #include "maps\exodus\code\exodus_unittest.dm" +#include "maps\random_ruins\exoplanets\exoplanet_ruins.dm" #include "maps\runtime\runtime_overmap.dm" #include "maps\runtime\code\runtime.dm" #include "maps\runtime\code\runtime_lifts.dm" diff --git a/code/__defines/_macros.dm b/code/__defines/_macros.dm index 8fcb2f87eb2..e971d18f155 100644 --- a/code/__defines/_macros.dm +++ b/code/__defines/_macros.dm @@ -35,6 +35,8 @@ #define UNDERSCORE_OR_NULL(target) "[target ? "[target]_" : ""]" +#define sequential_id(key) uniqueness_repository.Generate(/datum/uniqueness_generator/id_sequential, key) + #define isAI(A) istype(A, /mob/living/silicon/ai) #define isDrone(A) istype(A, /mob/living/silicon/robot/drone) #define isMatriarchDrone(A) istype(A, /mob/living/silicon/robot/drone/construction/matriarch) diff --git a/code/__defines/atmos.dm b/code/__defines/atmos.dm index 1c4aa2cfe4b..d7c2e13fc22 100644 --- a/code/__defines/atmos.dm +++ b/code/__defines/atmos.dm @@ -110,4 +110,6 @@ #define GAS_NITROGEN "nitrogen" #define GAS_NO2 "nitrodioxide" #define GAS_PHORON "phoron" -#define GAS_HYDROGEN "hydrogen" \ No newline at end of file +#define GAS_HYDROGEN "hydrogen" +#define GAS_ALIEN "aliether" +#define GAS_STEAM "water" \ No newline at end of file diff --git a/code/__defines/color.dm b/code/__defines/color.dm index 339b6884328..aae6248493a 100644 --- a/code/__defines/color.dm +++ b/code/__defines/color.dm @@ -131,3 +131,5 @@ #define BLOB_COLOR_SHIELD "#9ed659" #define BLOB_COLOR_RAV "#d65f42" #define BLOB_COLOR_PULS "#b5ff5b" + +#define RANDOM_RGB rgb(rand(0,255), rand(0,255), rand(0,255)) \ No newline at end of file diff --git a/code/__defines/materials.dm b/code/__defines/materials.dm index 682b02d2488..2df61e56003 100644 --- a/code/__defines/materials.dm +++ b/code/__defines/materials.dm @@ -33,6 +33,7 @@ #define MATERIAL_RUST "rust" #define MATERIAL_CARDBOARD "cardboard" + // Leathers and related. #define MATERIAL_RESIN "resin" #define MATERIAL_LEATHER "leather" diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index bc428ee192c..36038db782c 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -12,6 +12,7 @@ #define MIMIC_NO_AO 16 // If the turf shouldn't apply regular turf AO and only do Z-mimic AO. #define TRANSITIONEDGE 7 // Distance from edge to move to another z-level. +#define RUIN_MAP_EDGE_PAD 15 // Invisibility constants. #define INVISIBILITY_LIGHTING 20 @@ -479,3 +480,17 @@ Define for getting a bitfield of adjacent turfs that meet a condition. #define COOK_CHECK_EXACT 1 #define STATION_TAG "Aurora" + +//Planet habitability class +#define HABITABILITY_IDEAL 1 +#define HABITABILITY_OKAY 2 +#define HABITABILITY_BAD 3 + +//Map template flags +#define TEMPLATE_FLAG_ALLOW_DUPLICATES 1 // Lets multiple copies of the template to be spawned +#define TEMPLATE_FLAG_SPAWN_GUARANTEED 2 // Makes it ignore away site budget and just spawn (only for away sites) +#define TEMPLATE_FLAG_CLEAR_CONTENTS 4 // if it should destroy objects it spawns on top of +#define TEMPLATE_FLAG_NO_RUINS 8 // if it should forbid ruins from spawning on top of it +#define TEMPLATE_FLAG_NO_RADS 16// Removes all radiation from the template after spawning. + + diff --git a/code/__defines/ruin_tags.dm b/code/__defines/ruin_tags.dm new file mode 100644 index 00000000000..caf6e3cf7fb --- /dev/null +++ b/code/__defines/ruin_tags.dm @@ -0,0 +1,8 @@ +//Flags for exoplanet ruin picking + +#define RUIN_HABITAT 1 //long term habitat +#define RUIN_HUMAN 2 //human-made structure +#define RUIN_ALIEN 4 //artificial structure of an unknown origin +#define RUIN_WRECK 8 //crashed vessel +#define RUIN_NATURAL 16 //naturally occuring structure +#define RUIN_WATER 32 //ruin depending on planet having water accessible \ No newline at end of file diff --git a/code/__defines/turfs.dm b/code/__defines/turfs.dm index dd736514f3a..54785bc3d24 100644 --- a/code/__defines/turfs.dm +++ b/code/__defines/turfs.dm @@ -9,6 +9,7 @@ #define TURF_HAS_CORNERS 256 #define TURF_IS_FRAGILE 512 #define TURF_ACID_IMMUNE 1024 +#define TURF_NORUINS 2048 // Roof related flags #define ROOF_FORCE_SPAWN 1 diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index fee53f339ec..abd48a89dd5 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -201,6 +201,7 @@ Proc for attack log creation, because really why not // Returns true if the mob was in neither the dead or living list /mob/proc/add_to_dead_mob_list() return FALSE + /mob/living/add_to_dead_mob_list() if((src in living_mob_list) || (src in dead_mob_list)) return FALSE diff --git a/code/controllers/subsystems/ghostroles.dm b/code/controllers/subsystems/ghostroles.dm index 5cc6a02212e..bdfe5d75dd3 100644 --- a/code/controllers/subsystems/ghostroles.dm +++ b/code/controllers/subsystems/ghostroles.dm @@ -32,7 +32,7 @@ continue //Check if we have a spawnpoint on the current map if(!G.select_spawnlocation(FALSE) && G.loc_type == GS_LOC_POS) - log_debug("ghostroles","Spawner [G.type] got removed from selection because of missing spawnpoint") + log_ss("ghostroles","Spawner [G.type] got removed from selection because of missing spawnpoint") continue spawners[G.short_name] = G diff --git a/code/controllers/subsystems/initialization/misc_late.dm b/code/controllers/subsystems/initialization/misc_late.dm index 3aa12c0918e..b21ab2fcb91 100644 --- a/code/controllers/subsystems/initialization/misc_late.dm +++ b/code/controllers/subsystems/initialization/misc_late.dm @@ -46,6 +46,8 @@ click_catchers = create_click_catcher() + current_map.build_exoplanets() + ..(timeofday) /proc/sorted_add_area(area/A) diff --git a/code/controllers/subsystems/processing/processing.dm b/code/controllers/subsystems/processing/processing.dm index 1ff5afc46d7..6290dcd2fc6 100644 --- a/code/controllers/subsystems/processing/processing.dm +++ b/code/controllers/subsystems/processing/processing.dm @@ -26,7 +26,7 @@ var/datum/controller/subsystem/processing/SSprocessing var/datum/thing = current_run[current_run.len] current_run.len-- if(!QDELETED(thing)) - if (thing.process() == PROCESS_KILL) + if (thing.process(wait, times_fired) == PROCESS_KILL) stop_processing(thing) else processing -= thing diff --git a/code/controllers/subsystems/processing/shuttle.dm b/code/controllers/subsystems/processing/shuttle.dm index cebd6b04894..c65fe577aab 100644 --- a/code/controllers/subsystems/processing/shuttle.dm +++ b/code/controllers/subsystems/processing/shuttle.dm @@ -22,6 +22,7 @@ var/datum/controller/subsystem/processing/shuttle/SSshuttle var/list/landmarks_still_needed = list() //Stores landmark_tags that need to be assigned to the sector (landmark_tag = sector) when registered. var/list/shuttles_to_initialize = list() //A queue for shuttles to initialize at the appropriate time. var/list/sectors_to_initialize //Used to find all sector objects at the appropriate time. + var/list/initialized_sectors = list() var/block_queue = TRUE var/tmp/list/working_shuttles @@ -116,6 +117,8 @@ var/datum/controller/subsystem/processing/shuttle/SSshuttle given_sector.add_landmark(landmark, landmark.shuttle_restricted) landmarks_awaiting_sector -= landmark + initialized_sectors |= given_sector + /datum/controller/subsystem/processing/shuttle/proc/try_add_landmark_tag(landmark_tag, obj/effect/overmap/visitable/given_sector) var/obj/effect/shuttle_landmark/landmark = get_landmark(landmark_tag) if(!landmark) diff --git a/code/datums/observation/death.dm b/code/datums/observation/death.dm new file mode 100644 index 00000000000..3e21b3f1d56 --- /dev/null +++ b/code/datums/observation/death.dm @@ -0,0 +1,22 @@ +// Observer Pattern Implementation: Death +// Registration type: /mob +// +// Raised when: A mob is added to the dead_mob_list +// +// Arguments that the called proc should expect: +// /mob/dead: The mob that was added to the dead_mob_list + +var/datum/observ/death/death_event = new() + +/datum/observ/death + name = "Death" + expected_type = /mob + +/***************** +* Death Handling * +*****************/ + +/mob/living/add_to_dead_mob_list() + . = ..() + if(.) + death_event.raise_event(src) diff --git a/code/datums/observation/shuttle_moved.dm b/code/datums/observation/shuttle_moved.dm new file mode 100644 index 00000000000..89ae37f5496 --- /dev/null +++ b/code/datums/observation/shuttle_moved.dm @@ -0,0 +1,38 @@ +// Observer Pattern Implementation: Shuttle Moved +// Registration type: /datum/shuttle/autodock +// +// Raised when: A shuttle has moved to a new landmark. +// +// Arguments that the called proc should expect: +// /datum/shuttle/shuttle: the shuttle moving +// /obj/effect/shuttle_landmark/old_location: the old location's shuttle landmark +// /obj/effect/shuttle_landmark/new_location: the new location's shuttle landmark + +// Observer Pattern Implementation: Shuttle Pre Move +// Registration type: /datum/shuttle/autodock +// +// Raised when: A shuttle is about to move to a new landmark. +// +// Arguments that the called proc should expect: +// /datum/shuttle/shuttle: the shuttle moving +// /obj/effect/shuttle_landmark/old_location: the old location's shuttle landmark +// /obj/effect/shuttle_landmark/new_location: the new location's shuttle landmark + +var/datum/observ/shuttle_moved/shuttle_moved_event = new() + +/datum/observ/shuttle_moved + name = "Shuttle Moved" + expected_type = /datum/shuttle + +var/datum/observ/shuttle_pre_move/shuttle_pre_move_event = new() + +/datum/observ/shuttle_pre_move + name = "Shuttle Pre Move" + expected_type = /datum/shuttle + +/***************** +* Shuttle Moved/Pre Move Handling * +*****************/ + +// Located in modules/shuttle/shuttle.dm +// Proc: /datum/shuttle/proc/attempt_move() \ No newline at end of file diff --git a/code/datums/repositories/unique.dm b/code/datums/repositories/unique.dm new file mode 100644 index 00000000000..a53111cb24c --- /dev/null +++ b/code/datums/repositories/unique.dm @@ -0,0 +1,70 @@ +var/repository/unique/uniqueness_repository = new() + +/repository/unique + var/list/generators + +/repository/unique/New() + ..() + generators = list() + +/repository/unique/proc/Generate() + var/generator_type = args[1] + var/datum/uniqueness_generator/generator = generators[generator_type] + if(!generator) + generator = new generator_type() + generators[generator_type] = generator + var/list/generator_args = args.Copy() // Cannot cut args directly, BYOND complains about it being readonly. + generator_args -= generator_type + return generator.Generate(arglist(generator_args)) + +/datum/uniqueness_generator/proc/Generate() + return + +/datum/uniqueness_generator/id_sequential + var/list/ids_by_key + +/datum/uniqueness_generator/id_sequential/New() + ..() + ids_by_key = list() + +/datum/uniqueness_generator/id_sequential/Generate(var/key, var/default_id = 100) + var/id = ids_by_key[key] + if(id) + id++ + else + id = default_id + + ids_by_key[key] = id + . = id + +/datum/uniqueness_generator/id_random + var/list/ids_by_key + +/datum/uniqueness_generator/id_random/New() + ..() + ids_by_key = list() + +/datum/uniqueness_generator/id_random/Generate(var/key, var/min, var/max) + var/list/ids = ids_by_key[key] + if(!ids) + ids = list() + ids_by_key[key] = ids + + if(ids.len >= (max - min) + 1) + error("Random ID limit reached for key [key].") + ids.Cut() + + if(ids.len >= 0.6 * ((max-min) + 1)) // if more than 60% of possible ids used + . = list() + for(var/i = min to max) + if(i in ids) + continue + . += i + var/id = pick(.) + ids += id + return id + else + do + . = rand(min, max) + while(. in ids) + ids += . diff --git a/code/datums/ruins.dm b/code/datums/ruins.dm new file mode 100644 index 00000000000..093e5a742f8 --- /dev/null +++ b/code/datums/ruins.dm @@ -0,0 +1,20 @@ +/datum/map_template/ruin + //name = "A Chest of Doubloons" + name = null + var/description = "In the middle of a clearing in the rockface, there's a chest filled with gold coins with Spanish engravings. \ + How is there a wooden container filled with 18th century coinage in the middle of a lavawracked hellscape? \ + It is clearly a mystery." + + var/spawn_weight = 1 + var/cost = null + var/list/sectors = list() //This ruin can only spawn in the sectors in this list. + + var/prefix = null + var/suffix = null + template_flags = 0 // No duplicates by default + +/datum/map_template/ruin/New() + if (suffix) + mappath += (prefix + suffix) + + ..() diff --git a/code/datums/trading/misc.dm b/code/datums/trading/misc.dm index ba708cf0247..09bb64d3d26 100644 --- a/code/datums/trading/misc.dm +++ b/code/datums/trading/misc.dm @@ -26,9 +26,9 @@ /mob/living/simple_animal/mushroom = TRADER_THIS_TYPE, /mob/living/simple_animal/tomato = TRADER_THIS_TYPE, /mob/living/simple_animal/rat/king = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/diyaab = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/shantak = TRADER_THIS_TYPE, - /mob/living/simple_animal/hostile/samak = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/retaliate/diyaab = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/retaliate/shantak = TRADER_THIS_TYPE, + /mob/living/simple_animal/hostile/retaliate/samak = TRADER_THIS_TYPE, /mob/living/simple_animal/hostile/bear = TRADER_ALL, /mob/living/simple_animal/hostile/carp = TRADER_ALL, /mob/living/simple_animal/hostile/biglizard = TRADER_THIS_TYPE, diff --git a/code/defines/gases.dm b/code/defines/gases.dm index 39cc9ada196..3504fedec8f 100644 --- a/code/defines/gases.dm +++ b/code/defines/gases.dm @@ -50,3 +50,31 @@ tile_overlay = "sleeping_agent" overlay_limit = 1 flags = XGM_GAS_OXIDIZER + +/decl/xgm_gas/alium + id = GAS_ALIEN + name = "Aliether" + +/decl/xgm_gas/alium/New() + var/num = rand(100,999) + name = "Compound #[num]" + specific_heat = rand(1, 400) // J/(mol*K) + molar_mass = rand(20,800)/1000 // kg/mol + if(prob(40)) + flags |= XGM_GAS_FUEL + else if(prob(40)) //it's prooobably a bad idea for gas being oxidizer to itself. + flags |= XGM_GAS_OXIDIZER + if(prob(40)) + flags |= XGM_GAS_CONTAMINANT + + if(prob(50)) + tile_color = RANDOM_RGB + overlay_limit = 0.5 + +/decl/xgm_gas/vapor + id = GAS_STEAM + name = "Steam" + tile_overlay = "generic" + overlay_limit = 0.5 + specific_heat = 30 // J/(mol*K) + molar_mass = 0.020 // kg/mol \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/instability.dm b/code/game/gamemodes/technomancer/instability.dm index 3436ee96795..e3b268a960a 100644 --- a/code/game/gamemodes/technomancer/instability.dm +++ b/code/game/gamemodes/technomancer/instability.dm @@ -295,7 +295,7 @@ // This should only be used for EXTERNAL sources of instability, such as from someone or something glowing. /mob/living/proc/receive_radiated_instability(amount) // Energy armor like from the AMI RIG can protect from this. - var/armor_ratio = get_blocked_ratio(BP_CHEST, BURN, damage = 40) //todomatt: look over this again + var/armor_ratio = get_blocked_ratio(BP_CHEST, BURN, damage = 40) amount = amount * armor_ratio if(amount && prob(10)) if(isSynthetic()) diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 0e722b6d29e..fb50bebdb90 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -266,4 +266,20 @@ /obj/effect/landmark/force_spawnpoint/do_landmark_effect() LAZYINITLIST(force_spawnpoints) - LAZYADD(force_spawnpoints[job_tag], loc) \ No newline at end of file + LAZYADD(force_spawnpoints[job_tag], loc) + +var/list/ruin_landmarks = list() + +/obj/effect/landmark/ruin + var/datum/map_template/ruin/ruin_template + +/obj/effect/landmark/ruin/New(loc, my_ruin_template) + name = "ruin_[sequential_id(/obj/effect/landmark/ruin)]" + ..(loc) + ruin_template = my_ruin_template + ruin_landmarks |= src + +/obj/effect/landmark/ruin/Destroy() + ruin_landmarks -= src + ruin_template = null + . = ..() diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index 3447c406b12..b7675e07624 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -523,7 +523,7 @@ /obj/item/trap/animal/medium/Initialize() . = ..() allowed_mobs = list( - /mob/living/simple_animal/cat, /mob/living/simple_animal/corgi, /mob/living/simple_animal/hostile/diyaab, /mob/living/carbon/human/monkey, /mob/living/simple_animal/penguin, /mob/living/simple_animal/crab, + /mob/living/simple_animal/cat, /mob/living/simple_animal/corgi, /mob/living/simple_animal/hostile/retaliate/diyaab, /mob/living/carbon/human/monkey, /mob/living/simple_animal/penguin, /mob/living/simple_animal/crab, /mob/living/simple_animal/chicken, /mob/living/simple_animal/yithian, /mob/living/carbon/alien/diona, /mob/living/silicon/robot/drone, /mob/living/silicon/pai, /mob/living/simple_animal/spiderbot, /mob/living/simple_animal/hostile/tree) diff --git a/code/game/objects/structures/pit.dm b/code/game/objects/structures/pit.dm new file mode 100644 index 00000000000..c27180d3270 --- /dev/null +++ b/code/game/objects/structures/pit.dm @@ -0,0 +1,166 @@ +/obj/structure/pit + name = "pit" + desc = "Watch your step, partner." + icon = 'icons/obj/pit.dmi' + icon_state = "pit1" + blend_mode = BLEND_MULTIPLY + density = FALSE + anchored = TRUE + var/open = 1 + +/obj/structure/pit/attackby(obj/item/W, mob/user) + if(istype(W,/obj/item/shovel)) + visible_message("\The [user] starts [open ? "filling" : "digging open"] \the [src]") + if(do_after(user, 50)) + visible_message("\The [user] [open ? "fills" : "digs open"] \the [src]!") + if(open) + close(user) + else + open() + else + to_chat(user, "You stop shoveling.") + return + if (!open && istype(W,/obj/item/stack/material/wood)) + if(locate(/obj/structure/gravemarker) in src.loc) + to_chat(user, "There's already a grave marker here.") + else + visible_message("\The [user] starts making a grave marker on top of \the [src]") + if( do_after(user, 50) ) + visible_message("\The [user] finishes the grave marker") + var/obj/item/stack/material/wood/plank = W + plank.use(1) + new/obj/structure/gravemarker(src.loc) + else + to_chat(user, "You stop making a grave marker.") + return + ..() + +/obj/structure/pit/update_icon() + icon_state = "pit[open]" + if(istype(loc,/turf/simulated/floor/exoplanet)) + var/turf/simulated/floor/exoplanet/E = loc + if(E.dirt_color) + color = E.dirt_color + +/obj/structure/pit/proc/open() + name = "pit" + desc = "Watch your step, partner." + open = 1 + for(var/atom/movable/A in src) + A.forceMove(src.loc) + update_icon() + +/obj/structure/pit/proc/close(var/user) + name = "mound" + desc = "Some things are better left buried." + open = 0 + for(var/atom/movable/A in src.loc) + if(!A.anchored && A != user) + A.forceMove(src) + update_icon() + +/obj/structure/pit/return_air() + if(open && loc) + return loc.return_air() + else + return null + +/obj/structure/pit/proc/digout(mob/escapee) + var/breakout_time = 1 //2 minutes by default + + if(open) + return + + if(escapee.stat || escapee.restrained()) + return + + escapee.setClickCooldown(100) + to_chat(escapee, "You start digging your way out of \the [src] (this will take about [breakout_time] minute\s)") + visible_message("Something is scratching its way out of \the [src]!") + + for(var/i in 1 to (6*breakout_time * 2)) //minutes * 6 * 5seconds * 2 + playsound(src.loc, 'sound/weapons/bite.ogg', 100, 1) + + if(!do_after(escapee, 50)) + to_chat(escapee, "You have stopped digging.") + return + if(open) + return + + if(i == 6*breakout_time) + to_chat(escapee, "Halfway there...") + + to_chat(escapee, "You successfuly dig yourself out!") + visible_message("\the [escapee] emerges from \the [src]!") + playsound(src.loc, 'sound/effects/squelch1.ogg', 100, 1) + open() + +/obj/structure/pit/closed + name = "mound" + desc = "Some things are better left buried." + open = 0 + +/obj/structure/pit/closed/Initialize() + . = ..() + close() + +//invisible until unearthed first +/obj/structure/pit/closed/hidden + invisibility = INVISIBILITY_OBSERVER + +/obj/structure/pit/closed/hidden/open() + ..() + invisibility = INVISIBILITY_LEVEL_ONE + +//spoooky +/obj/structure/pit/closed/grave + name = "grave" + icon_state = "pit0" + +/obj/structure/pit/closed/grave/Initialize() + var/obj/structure/closet/coffin/C = new(src.loc) + var/obj/effect/decal/remains/human/bones = new(C) + bones.layer = BELOW_MOB_LAYER + var/obj/structure/gravemarker/random/R = new(src.loc) + R.generate() + . = ..() + +/obj/structure/gravemarker + name = "grave marker" + desc = "You're not the first." + icon = 'icons/obj/gravestone.dmi' + icon_state = "wood" + pixel_x = 15 + pixel_y = 8 + anchored = TRUE + var/message = "Unknown." + +/obj/structure/gravemarker/cross + icon_state = "cross" + +/obj/structure/gravemarker/examine(mob/user) + . = ..() + to_chat(user, "It says: '[message]'") + +/obj/structure/gravemarker/random/Initialize() + generate() + . = ..() + +/obj/structure/gravemarker/random/proc/generate() + icon_state = pick("wood","cross") + + + var/nam = random_name(MALE, SPECIES_HUMAN) + message = "Here lies [nam]." + +/obj/structure/gravemarker/attackby(obj/item/W, mob/user) + if(istype(W,/obj/item/material/hatchet)) + visible_message("\The [user] starts hacking away at \the [src] with \the [W].") + if(!do_after(user, 30)) + visible_message("\The [user] hacks \the [src] apart.") + new /obj/item/stack/material/wood(src) + qdel(src) + if(istype(W,/obj/item/pen)) + var/msg = sanitize(input(user, "What should it say?", "Grave marker", message) as text|null) + if(msg) + message = msg \ No newline at end of file diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index ab9f7e63b4d..385ab040088 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -92,6 +92,9 @@ for(var/obj/O in src) O.hide(O.hides_under_flooring() && src.flooring) +/turf/simulated/floor/is_floor() + return TRUE + /turf/simulated/floor/shuttle_ceiling name = "hull plating" icon = 'icons/turf/flooring/tiles.dmi' diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 8e4af6ed086..0c75f20711c 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -258,3 +258,6 @@ W.burn((temperature/4)) for(var/obj/machinery/door/airlock/phoron/D in range(3,src)) D.ignite(temperature/4) + +/turf/simulated/wall/is_wall() + return TRUE diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index b315356b3c0..ae97d5900b0 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -542,3 +542,12 @@ var/const/enterloopsanity = 100 return 15 else return 0 + +/turf/proc/is_wall() + return FALSE + +/turf/proc/is_open() + return FALSE + +/turf/proc/is_floor() + return FALSE diff --git a/code/modules/background/space_sectors/space_sector.dm b/code/modules/background/space_sectors/space_sector.dm index b25c2940881..658b620d42a 100644 --- a/code/modules/background/space_sectors/space_sector.dm +++ b/code/modules/background/space_sectors/space_sector.dm @@ -2,5 +2,6 @@ var/name var/description var/list/possible_erts = list() + var/list/possible_exoplanets = list() var/list/cargo_price_coef = list("nt" = 1, "hpi" = 1, "zhu" = 1, "een" = 1, "get" = 1, "arz" = 1, "blm" = 1, "iac" = 1, "zsc" = 1, "vfc" = 1, "bis" = 1, "xmg" = 1, "npi" = 1) //how much the space sector afffects how expensive is ordering from that cargo supplier diff --git a/code/modules/cargo/randomstock.dm b/code/modules/cargo/randomstock.dm index 614cc3d8f13..8a1474e6328 100644 --- a/code/modules/cargo/randomstock.dm +++ b/code/modules/cargo/randomstock.dm @@ -106,13 +106,13 @@ var/list/global/random_stock_large = list() var/list/infest_mobs_moderate = list( /mob/living/simple_animal/bee/standalone = 1, - /mob/living/simple_animal/hostile/diyaab = 1, + /mob/living/simple_animal/hostile/retaliate/diyaab = 1, /mob/living/simple_animal/hostile/viscerator = 1, /mob/living/simple_animal/hostile/scarybat = 1) var/list/infest_mobs_severe = list( /mob/living/simple_animal/hostile/giant_spider/hunter = 1, - /mob/living/simple_animal/hostile/shantak = 0.7, + /mob/living/simple_animal/hostile/retaliate/shantak = 0.7, /mob/living/simple_animal/hostile/bear = 0.5, /mob/living/simple_animal/hostile/carp = 1.5, /mob/living/simple_animal/hostile/carp/russian = 0.3, diff --git a/code/modules/maps/map_template.dm b/code/modules/maps/map_template.dm index 8d2e599a8af..81b2357aca3 100644 --- a/code/modules/maps/map_template.dm +++ b/code/modules/maps/map_template.dm @@ -1,10 +1,16 @@ /datum/map_template var/name = "Default Template Name" + var/id = null // All maps that should be loadable during runtime need an id var/width = 0 var/height = 0 var/mappath = null var/loaded = 0 // Times loaded this round var/static/dmm_suite/maploader = new + var/list/shuttles_to_initialise = list() + var/list/subtemplates_to_spawn + var/base_turf_for_zs = null + var/accessibility_weight = 0 + var/template_flags = TEMPLATE_FLAG_ALLOW_DUPLICATES /datum/map_template/New(path = null, rename = null) if(path) diff --git a/code/modules/maps/planet_types/barren.dm b/code/modules/maps/planet_types/barren.dm new file mode 100644 index 00000000000..3de18a53d5a --- /dev/null +++ b/code/modules/maps/planet_types/barren.dm @@ -0,0 +1,51 @@ +/obj/effect/overmap/visitable/sector/exoplanet/barren + name = "barren exoplanet" + desc = "An exoplanet that couldn't hold its atmosphere." + color = "#6c6c6c" + planetary_area = /area/exoplanet/barren + rock_colors = list(COLOR_BEIGE, COLOR_GRAY80, COLOR_BROWN) + possible_themes = list(/datum/exoplanet_theme/mountains) + map_generators = list(/datum/random_map/noise/exoplanet/barren, /datum/random_map/noise/ore/rich) + ruin_tags_blacklist = RUIN_HABITAT|RUIN_WATER + features_budget = 6 + surface_color = "#807d7a" + water_color = null + +/obj/effect/overmap/visitable/sector/exoplanet/barren/generate_habitability() + return HABITABILITY_BAD + +/obj/effect/overmap/visitable/sector/exoplanet/barren/generate_atmosphere() + ..() + atmosphere.remove_ratio(0.9) + +/datum/random_map/noise/exoplanet/barren + descriptor = "barren exoplanet" + smoothing_iterations = 4 + land_type = /turf/simulated/floor/exoplanet/barren + flora_prob = 0.1 + flora_diversity = 0 + fauna_prob = 0 + +/datum/random_map/noise/exoplanet/barren/New() + if(prob(10)) + flora_diversity = 1 + ..() + +/turf/simulated/floor/exoplanet/barren + name = "ground" + icon = 'icons/turf/flooring/asteroid.dmi' + icon_state = "asteroid" + +/turf/simulated/floor/exoplanet/barren/update_icon() + overlays.Cut() + if(prob(20)) + overlays += image('icons/turf/flooring/decals.dmi', "asteroid[rand(0,9)]") + +/turf/simulated/floor/exoplanet/barren/Initialize() + . = ..() + update_icon() + +/area/exoplanet/barren + name = "\improper Planetary surface" + ambience = list('sound/effects/wind/wind_2_1.ogg','sound/effects/wind/wind_2_2.ogg','sound/effects/wind/wind_3_1.ogg','sound/effects/wind/wind_4_1.ogg','sound/effects/wind/wind_4_2.ogg','sound/effects/wind/wind_5_1.ogg') + base_turf = /turf/simulated/floor/exoplanet/barren \ No newline at end of file diff --git a/code/modules/maps/planet_types/desert.dm b/code/modules/maps/planet_types/desert.dm new file mode 100644 index 00000000000..a5bdab00cc5 --- /dev/null +++ b/code/modules/maps/planet_types/desert.dm @@ -0,0 +1,155 @@ +/obj/effect/overmap/visitable/sector/exoplanet/desert + name = "desert exoplanet" + desc = "An arid exoplanet with sparse biological resources but rich mineral deposits underground." + color = "#a08444" + planetary_area = /area/exoplanet/desert + rock_colors = list(COLOR_BEIGE, COLOR_PALE_YELLOW, COLOR_GRAY80, COLOR_BROWN) + plant_colors = list("#efdd6f","#7b4a12","#e49135","#ba6222","#5c755e","#420d22") + map_generators = list(/datum/random_map/noise/exoplanet/desert) + surface_color = "#d6cca4" + water_color = null + +/obj/effect/overmap/visitable/sector/exoplanet/desert/generate_map() + if(prob(70)) + lightlevel = rand(5,10)/10 //deserts are usually :lit: + ..() + +/obj/effect/overmap/visitable/sector/exoplanet/desert/generate_atmosphere() + ..() + if(atmosphere) + var/limit = 1000 + if(habitability_class <= HABITABILITY_OKAY) + var/datum/species/human/H = /datum/species/human + limit = initial(H.heat_level_1) - rand(1,10) + atmosphere.temperature = min(T20C + rand(20, 100), limit) + atmosphere.update_values() + +/obj/effect/overmap/visitable/sector/exoplanet/desert/adapt_seed(var/datum/seed/S) + ..() + if(prob(90)) + S.set_trait(TRAIT_REQUIRES_WATER,0) + else + S.set_trait(TRAIT_REQUIRES_WATER,1) + S.set_trait(TRAIT_WATER_CONSUMPTION,1) + if(prob(75)) + S.set_trait(TRAIT_STINGS,1) + if(prob(75)) + S.set_trait(TRAIT_CARNIVOROUS,2) + S.set_trait(TRAIT_SPREAD,0) + +/datum/random_map/noise/exoplanet/desert + descriptor = "desert exoplanet" + smoothing_iterations = 4 + land_type = /turf/simulated/floor/exoplanet/desert + + flora_prob = 5 + flora_diversity = 4 + fauna_types = list(/mob/living/simple_animal/thinbug, /mob/living/simple_animal/tindalos) + +/datum/random_map/noise/exoplanet/desert/get_additional_spawns(var/value, var/turf/T) + ..() + if(is_edge_turf(T)) + return + var/v = noise2value(value) + if(v > 6) + T.icon_state = "desert[v-1]" + if(prob(10)) + new/obj/structure/quicksand(T) + +/area/exoplanet/desert + ambience = list('sound/effects/wind/desert0.ogg','sound/effects/wind/desert1.ogg','sound/effects/wind/desert2.ogg','sound/effects/wind/desert3.ogg','sound/effects/wind/desert4.ogg','sound/effects/wind/desert5.ogg') + base_turf = /turf/simulated/floor/exoplanet/desert + +/obj/structure/quicksand + name = "sand" + icon = 'icons/obj/quicksand.dmi' + icon_state = "intact0" + density = 0 + anchored = 1 + can_buckle = 1 + buckle_dir = SOUTH + var/exposed = 0 + var/busy + +/obj/structure/quicksand/New() + icon_state = "intact[rand(0,2)]" + ..() + +/obj/structure/quicksand/user_unbuckle(mob/user) + if(buckled && !user.stat && !user.restrained()) + if(busy) + to_chat(user, "[buckled] is already getting out, be patient.") + return + var/delay = 60 + if(user == buckled) + delay *=2 + user.visible_message( + "\The [user] tries to climb out of \the [src].", + "You begin to pull yourself out of \the [src].", + "You hear water sloushing." + ) + else + user.visible_message( + "\The [user] begins pulling \the [buckled] out of \the [src].", + "You begin to pull \the [buckled] out of \the [src].", + "You hear water sloushing." + ) + busy = 1 + if(do_after(user, delay, src)) + busy = 0 + if(user == buckled) + if(prob(80)) + to_chat(user, "You slip and fail to get out!") + return + user.visible_message("\The [buckled] pulls himself out of \the [src].") + else + user.visible_message("\The [buckled] has been freed from \the [src] by \the [user].") + unbuckle() + else + busy = 0 + to_chat(user, "You slip and fail to get out!") + return + +/obj/structure/quicksand/unbuckle() + ..() + update_icon() + +/obj/structure/quicksand/buckle(var/mob/L) + ..() + update_icon() + +/obj/structure/quicksand/update_icon() + if(!exposed) + return + icon_state = "open" + overlays.Cut() + if(buckled) + overlays += buckled + var/image/I = image(icon,icon_state="overlay") + I.layer = ABOVE_MOB_LAYER + overlays += I + +/obj/structure/quicksand/proc/expose() + if(exposed) + return + visible_message("The upper crust breaks, exposing treacherous quicksands underneath!") + name = "quicksand" + desc = "There is no candy at the bottom." + exposed = 1 + update_icon() + +/obj/structure/quicksand/attackby(obj/item/W, mob/user) + if(!exposed && W.force) + expose() + else + ..() + +/obj/structure/quicksand/Crossed(var/atom/movable/AM) + if(isliving(AM)) + var/mob/living/L = AM + if(L.throwing) + return + buckle(L) + if(!exposed) + expose() + to_chat(L, SPAN_DANGER("You fall into \the [src]!")) diff --git a/code/modules/maps/planet_types/grass.dm b/code/modules/maps/planet_types/grass.dm new file mode 100644 index 00000000000..0dba7038484 --- /dev/null +++ b/code/modules/maps/planet_types/grass.dm @@ -0,0 +1,60 @@ +/obj/effect/overmap/visitable/sector/exoplanet/grass + name = "lush exoplanet" + desc = "Planet with abundant flora and fauna." + color = "#407c40" + planetary_area = /area/exoplanet/grass + rock_colors = list(COLOR_ASTEROID_ROCK, COLOR_GRAY80, COLOR_BROWN) + plant_colors = list("#0e1e14","#1a3e38","#5a7467","#9eab88","#6e7248", "RANDOM") + map_generators = list(/datum/random_map/noise/exoplanet/grass) + +/obj/effect/overmap/visitable/sector/exoplanet/grass/generate_map() + if(prob(40)) + lightlevel = rand(1,7)/10 //give a chance of twilight jungle + ..() + +/obj/effect/overmap/visitable/sector/exoplanet/grass/generate_atmosphere() + ..() + if(atmosphere) + atmosphere.temperature = T20C + rand(10, 30) + atmosphere.update_values() + +/obj/effect/overmap/visitable/sector/exoplanet/grass/get_surface_color() + return grass_color + +/obj/effect/overmap/visitable/sector/exoplanet/grass/adapt_seed(var/datum/seed/S) + ..() + var/carnivore_prob = rand(100) + if(carnivore_prob < 30) + S.set_trait(TRAIT_CARNIVOROUS,2) + if(prob(75)) + S.get_trait(TRAIT_STINGS, 1) + else if(carnivore_prob < 60) + S.set_trait(TRAIT_CARNIVOROUS,1) + if(prob(50)) + S.get_trait(TRAIT_STINGS) + if(prob(15) || (S.get_trait(TRAIT_CARNIVOROUS) && prob(40))) + S.set_trait(TRAIT_BIOLUM,1) + S.set_trait(TRAIT_BIOLUM_COLOUR,get_random_colour(0,75,190)) + + if(prob(30)) + S.set_trait(TRAIT_PARASITE,1) + +/area/exoplanet/grass + base_turf = /turf/simulated/floor/exoplanet/grass + ambience = list('sound/effects/wind/wind_2_1.ogg','sound/effects/wind/wind_2_2.ogg','sound/effects/wind/wind_3_1.ogg','sound/effects/wind/wind_4_1.ogg','sound/ambience/eeriejungle2.ogg','sound/ambience/eeriejungle1.ogg') + +/area/exoplanet/grass/play_ambience(var/mob/living/L) + ..() + if(!L.ear_deaf && L.client && !L.client.ambience_playing) + L.client.ambience_playing = 1 + L.playsound_to(get_turf(L),sound('sound/ambience/jungle.ogg', repeat = 1, wait = 0, volume = 25)) + +/datum/random_map/noise/exoplanet/grass + descriptor = "grass exoplanet" + smoothing_iterations = 2 + land_type = /turf/simulated/floor/exoplanet/grass + water_type = /turf/simulated/floor/exoplanet/water/shallow + + flora_prob = 10 + flora_diversity = 6 + fauna_types = list(/mob/living/simple_animal/yithian, /mob/living/simple_animal/tindalos, /mob/living/simple_animal/cosmozoan) diff --git a/code/modules/maps/planet_types/snow.dm b/code/modules/maps/planet_types/snow.dm new file mode 100644 index 00000000000..eda5ed04156 --- /dev/null +++ b/code/modules/maps/planet_types/snow.dm @@ -0,0 +1,33 @@ +/obj/effect/overmap/visitable/sector/exoplanet/snow + name = "snow exoplanet" + desc = "Cold planet with limited plant life." + color = "#dcdcdc" + planetary_area = /area/exoplanet/snow + rock_colors = list(COLOR_DARK_BLUE_GRAY, COLOR_GUNMETAL, COLOR_GRAY80, COLOR_DARK_GRAY) + plant_colors = list("#d0fef5","#93e1d8","#93e1d8", "#b2abbf", "#3590f3", "#4b4e6d") + map_generators = list(/datum/random_map/noise/exoplanet/snow) + surface_color = "#e8faff" + water_color = "#b5dfeb" + +/obj/effect/overmap/visitable/sector/exoplanet/snow/generate_atmosphere() + ..() + if(atmosphere) + var/limit = 0 + if(habitability_class <= HABITABILITY_OKAY) + var/datum/species/human/H = /datum/species/human + limit = initial(H.cold_level_1) + rand(1,10) + atmosphere.temperature = max(T0C - rand(10, 100), limit) + atmosphere.update_values() + +/datum/random_map/noise/exoplanet/snow + descriptor = "snow exoplanet" + smoothing_iterations = 1 + flora_prob = 5 + water_level_max = 3 + land_type = /turf/simulated/floor/exoplanet/snow + water_type = /turf/simulated/floor/exoplanet/ice + fauna_types = list(/mob/living/simple_animal/hostile/retaliate/samak, /mob/living/simple_animal/hostile/retaliate/diyaab, /mob/living/simple_animal/hostile/retaliate/shantak) + +/area/exoplanet/snow + ambience = list('sound/effects/wind/tundra0.ogg','sound/effects/wind/tundra1.ogg','sound/effects/wind/tundra2.ogg','sound/effects/wind/spooky0.ogg','sound/effects/wind/spooky1.ogg') + base_turf = /turf/simulated/floor/exoplanet/snow diff --git a/code/modules/maps/ruins.dm b/code/modules/maps/ruins.dm new file mode 100644 index 00000000000..9846281fa55 --- /dev/null +++ b/code/modules/maps/ruins.dm @@ -0,0 +1,85 @@ +var/list/banned_ruin_ids = list() + +/proc/seedRuins(list/zlevels, budget, list/potentialRuins, allowed_area = /area/space, var/maxx = world.maxx, var/maxy = world.maxy) + if (!length(zlevels)) + UNLINT(WARNING("No Z levels provided - Not generating ruins")) + return + + for (var/z in zlevels) + var/turf/check = locate(1, 1, z) + if (!check) + UNLINT(WARNING("Z level [z] does not exist - Not generating ruins")) + return + + var/list/available = list() + var/list/selected = list() + var/remaining = budget + + for(var/datum/map_template/ruin/ruin in potentialRuins) + if (ruin.id in banned_ruin_ids) + continue + if(!(SSatlas.current_sector.name in ruin.sectors) && !length(ruin.sectors)) + continue + available[ruin] = ruin.spawn_weight + + if (!length(available)) + UNLINT(WARNING("No ruins available - Not generating ruins")) + + while (remaining > 0 && length(available)) + var/datum/map_template/ruin/ruin = pickweight(available) + if (ruin.cost > budget) + available -= ruin + continue + + var/width = TRANSITIONEDGE + RUIN_MAP_EDGE_PAD + round(ruin.width / 2) + var/height = TRANSITIONEDGE + RUIN_MAP_EDGE_PAD + round(ruin.height / 2) + if (width > maxx - width || height > maxy - height) + available -= ruin + continue + + for (var/attempts = 20, attempts > 0, --attempts) + var/z = pick(zlevels) + var/turf/choice = locate(rand(width, maxx - width), rand(height, maxy - height), z) + + var/valid = TRUE + for (var/turf/check in ruin.get_affected_turfs(choice, 1)) + var/area/check_area = get_area(check) + if (!istype(check_area, allowed_area) || check.flags & TURF_NORUINS) + if (attempts == 1) + available -= ruin + valid = FALSE + break + if (!valid) + continue + + log_admin("Ruin \"[ruin.name]\" placed at ([choice.x], [choice.y], [choice.z])") + + load_ruin(choice, ruin) + selected += ruin + if (ruin.cost > 0) + remaining -= ruin.cost + if (!(ruin.template_flags & TEMPLATE_FLAG_ALLOW_DUPLICATES)) + banned_ruin_ids += ruin.id + available -= ruin + break + + if (remaining) + log_admin("Ruin loader had no ruins to pick from with [budget] left to spend.") + + if (length(selected)) + log_debug("Finished selecting planet ruins ([english_list(selected)]) for [budget - remaining] cost of [budget] budget.") + + return selected + +/proc/load_ruin(turf/central_turf, datum/map_template/template) + if(!template) + return FALSE + for(var/i in template.get_affected_turfs(central_turf, 1)) + var/turf/T = i + for(var/mob/living/simple_animal/monster in T) + qdel(monster) + template.load(central_turf,centered = TRUE) + var/datum/map_template/ruin = template + if(istype(ruin)) + new /obj/effect/landmark/ruin(central_turf, ruin) + return TRUE diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm index b4116c2928a..bd20932beba 100644 --- a/code/modules/mob/holder.dm +++ b/code/modules/mob/holder.dm @@ -265,13 +265,6 @@ var/list/holder_mob_icon_cache = list() var/list/generate_for_slots = list(slot_l_hand_str, slot_r_hand_str, slot_back_str) slot_flags = SLOT_BACK - -/obj/item/holder/proc/sync(var/mob/living/M) - src.name = M.name - src.overlays = M.overlays - dir = M.dir - reagents = M.reagents - /obj/item/holder/human/sync(var/mob/living/M) cut_overlays() // Generate appropriate on-mob icons. @@ -354,6 +347,19 @@ var/list/holder_mob_icon_cache = list() H.change_animal_name(usr) sync(contained) +/obj/item/holder/proc/sync(var/mob/living/M) + dir = SOUTH + cut_overlays() + icon = M.icon + icon_state = M.icon_state + item_state = M.item_state + color = M.color + name = M.name + desc = M.desc + overlays |= M.overlays + + update_held_icon() + //#TODO-MERGE //Port the reduced-duplication holder method from baystation upstream: //https://github.com/Baystation12/Baystation12/blob/master/code/modules/mob/holder.dm diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index ac2cae6889a..01562146be4 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -409,9 +409,9 @@ var/list/slot_equipment_priority = list( \ return FALSE /mob/proc/delete_inventory(var/include_carried = FALSE) - for(var/entry in get_equipped_items(include_carried)) - drop_from_inventory(entry) - qdel(entry) + for(var/obj/item/I as anything in get_equipped_items(include_carried)) + drop_from_inventory(I) + qdel(I) /mob/proc/get_covering_equipped_items(var/body_parts) . = list() @@ -447,3 +447,10 @@ var/list/slot_equipment_priority = list( \ return FALSE return O.pre_equip(src, visualsOnly) + +// Returns the first item which covers any given body part +/mob/proc/get_covering_equipped_item(var/body_parts) + for(var/entry in get_equipped_items()) + var/obj/item/I = entry + if(I.body_parts_covered & body_parts) + return I diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index b68afe4f2ca..c718e8841fc 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -484,3 +484,8 @@ This saves us from having to call add_fingerprint() any time something is put in return is_noisy = TRUE + +/mob/living/carbon/human/proc/get_covering_equipped_item_by_zone(var/zone) + var/obj/item/organ/external/O = get_organ(zone) + if(O) + return get_covering_equipped_item(O.body_part) diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm index cea389a745d..f474f0a3a0d 100644 --- a/code/modules/mob/living/simple_animal/hostile/alien.dm +++ b/code/modules/mob/living/simple_animal/hostile/alien.dm @@ -98,7 +98,7 @@ icon_state = "toxin" // Xenoarch aliens. -/mob/living/simple_animal/hostile/samak +/mob/living/simple_animal/hostile/retaliate/samak name = "samak" desc = "A fast, armored predator accustomed to hiding and ambushing in cold terrain." faction = "samak" @@ -120,7 +120,7 @@ emote_hear = list("snuffles") mob_size = 10 -/mob/living/simple_animal/hostile/diyaab +/mob/living/simple_animal/hostile/retaliate/diyaab name = "diyaab" desc = "A small pack animal. Although omnivorous, it will hunt meat on occasion." faction = "diyaab" @@ -144,7 +144,7 @@ density = 0 mob_size = 3 -/mob/living/simple_animal/hostile/shantak +/mob/living/simple_animal/hostile/retaliate/shantak name = "shantak" desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. Don't be fooled by its beauty though." faction = "shantak" diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 69264737d4e..a3fb67b25da 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -25,6 +25,7 @@ var/list/targets = list() var/attacked_times = 0 var/list/target_type_validator_map = list() + var/list/tolerated_types = list() var/attack_emote = "stares menacingly at" var/smart_melee = TRUE // This makes melee mobs try to stay two tiles away from their target in combat, lunging in to attack only @@ -171,6 +172,9 @@ mob/living/simple_animal/hostile/hitby(atom/movable/AM as mob|obj,var/speed = TH else return 0 +/mob/living/simple_animal/hostile/proc/on_attack_mob(var/mob/hit_mob) + return + /mob/living/simple_animal/hostile/proc/AttackingTarget() setClickCooldown(attack_delay) if(!Adjacent(target_mob)) @@ -189,6 +193,7 @@ mob/living/simple_animal/hostile/hitby(atom/movable/AM as mob|obj,var/speed = TH if(isliving(target_mob)) var/mob/living/L = target_mob L.attack_generic(src, rand(melee_damage_lower, melee_damage_upper), attacktext) + on_attack_mob(L) target = L else if(istype(target_mob, /obj/machinery/bot)) var/obj/machinery/bot/B = target_mob @@ -409,6 +414,8 @@ mob/living/simple_animal/hostile/hitby(atom/movable/AM as mob|obj,var/speed = TH current_health = M.health if(L.health < current_health) return TRUE + if(tolerated_types[L.type] == TRUE) + return FALSE return FALSE /mob/living/simple_animal/hostile/proc/validator_bot(var/obj/machinery/bot/B, var/atom/current) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm new file mode 100644 index 00000000000..c00bfada241 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/exoplanet.dm @@ -0,0 +1,71 @@ +/mob/living/simple_animal/thinbug + name = "taki" + desc = "It looks like a bunch of legs." + icon_state = "thinbug" + icon_living = "thinbug" + icon_dead = "thinbug_dead" + mob_size = MOB_MINISCULE + density = FALSE + + emote_hear = list("scratches the ground", "rubs its legs together", "chitters") + + +/mob/living/simple_animal/hostile/retaliate/royalcrab + name = "cragenoy" + desc = "It looks like a crustacean with an exceedingly hard carapace. Watch the pinchers!" + faction = "crab" + icon_state = "royalcrab" + icon_living = "royalcrab" + icon_dead = "royalcrab_dead" + move_to_delay = 3 + maxHealth = 150 + health = 150 + speed = 1 + melee_damage_lower = 10 + melee_damage_upper = 15 + natural_armor = list( + melee = ARMOR_MELEE_RESISTANT + ) + + emote_see = list("skitters","oozes liquid from its mouth", "scratches at the ground", "clicks its claws") + +/mob/living/simple_animal/hostile/retaliate/beast/charbaby + name = "charbaby" + desc = "A huge grubby creature." + icon_state = "char" + icon_living = "char" + icon_dead = "char_dead" + mob_size = MOB_LARGE + health = 45 + maxHealth = 45 + speed = 2 + response_help = "pats briefly" + response_disarm = "gently pushes" + response_harm = "strikes" + harm_intent_damage = 1 + melee_damage_lower = 5 + melee_damage_upper = 10 + blood_color = COLOR_RED + natural_armor = list( + laser = ARMOR_LASER_PISTOL + ) + +/mob/living/simple_animal/hostile/retaliate/beast/charbaby/on_attack_mob(var/mob/hit_mob) + . = ..() + if(isliving(hit_mob) && prob(25)) + var/mob/living/L = hit_mob + if(prob(10)) + L.adjust_fire_stacks(1) + L.IgniteMob() + +/mob/living/simple_animal/hostile/retaliate/beast/charbaby/attack_hand(mob/living/carbon/human/H) + . = ..() + reflect_unarmed_damage(H, BURN, "amorphous mass") + +/mob/living/simple_animal/hostile/retaliate/beast/shantak/lava + desc = "A vaguely canine looking beast. It looks as though its fur is made of stone wool." + icon_state = "lavadog" + icon_living = "lavadog" + icon_dead = "lavadog_dead" + + speak = list("Karuph!", "Karump!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm index 93e1eaaf183..df9c14d7435 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm @@ -27,4 +27,26 @@ for(var/mob/living/simple_animal/hostile/retaliate/H in view(world.view, get_turf(src))) if(H.faction == faction) - H.enemies |= M \ No newline at end of file + H.enemies |= M + +/mob/living/simple_animal/proc/name_species() + set name = "Name Alien Species" + set category = "Object" + set src in view() + + if(!current_map.use_overmap) + return + + if(use_check_and_message(usr)) + return + + for(var/obj/effect/overmap/visitable/sector/exoplanet/E in SSshuttle.initialized_sectors) + if(src in E.animals) + var/newname = input("What do you want to name this species?", "Species naming", E.get_random_species_name()) as text|null + newname = sanitizeName(newname, allow_numbers = TRUE) + if(newname && !use_check_and_message(usr)) + if(E.rename_species(type, newname)) + to_chat(usr,"This species will be known from now on as '[newname]'.") + else + to_chat(usr,"This species has already been named!") + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 6ba16b72cf1..5fb834d3576 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -136,6 +136,13 @@ var/psi_pingable = TRUE + var/armor_type = /datum/component/armor + var/list/natural_armor //what armor animal has + + //for simple animals that reflect damage when attacked in melee + var/return_damage_min + var/return_damage_max + /mob/living/simple_animal/proc/update_nutrition_stats() nutrition_step = mob_size * 0.03 * metabolic_factor @@ -160,6 +167,9 @@ udder = new(50) udder.my_atom = src + if(LAZYLEN(natural_armor)) + AddComponent(armor_type, natural_armor) + /mob/living/simple_animal/Move(NewLoc, direct) . = ..() if(.) @@ -940,6 +950,17 @@ else return FALSE +/mob/living/simple_animal/proc/reflect_unarmed_damage(var/mob/living/carbon/human/attacker, var/damage_type, var/description) + if(attacker.a_intent == I_HURT) + var/hand_hurtie + if(attacker.hand) + hand_hurtie = BP_L_HAND + else + hand_hurtie = BP_R_HAND + attacker.apply_damage(rand(return_damage_min, return_damage_max), damage_type, hand_hurtie, used_weapon = description) + if(rand(25)) + to_chat(attacker, SPAN_WARNING("Your attack has no obvious effect on \the [src]'s [description]!")) + #undef BLOOD_NONE #undef BLOOD_LIGHT #undef BLOOD_MEDIUM diff --git a/code/modules/multiz/basic.dm b/code/modules/multiz/basic.dm index 766fde7ba20..e5966f3b630 100644 --- a/code/modules/multiz/basic.dm +++ b/code/modules/multiz/basic.dm @@ -37,9 +37,9 @@ /proc/GetConnectedZlevels(z) . = list(z) for(var/level = z, HAS_BELOW(level), level--) - . += level-1 + . |= level-1 for(var/level = z, HAS_ABOVE(level), level++) - . += level+1 + . |= level+1 /proc/AreConnectedZLevels(var/zA, var/zB) if (zA == zB) diff --git a/code/modules/overmap/exoplanets/exoplanet.dm b/code/modules/overmap/exoplanets/exoplanet.dm new file mode 100644 index 00000000000..783882217d1 --- /dev/null +++ b/code/modules/overmap/exoplanets/exoplanet.dm @@ -0,0 +1,419 @@ +/obj/effect/overmap/visitable/sector/exoplanet + name = "exoplanet" + icon_state = "globe" + in_space = 0 + var/area/planetary_area + var/list/seeds = list() + var/list/animals = list() + var/max_animal_count + var/datum/gas_mixture/atmosphere + var/list/breathgas = list() //list of gases animals/plants require to survive + var/badgas //id of gas that is toxic to life here + + var/lightlevel = 0 //This default makes turfs not generate light. Adjust to have exoplanents be lit. + var/night = TRUE + var/daycycle //How often do we change day and night + var/daycolumn = 0 //Which column's light needs to be updated next? + var/daycycle_column_delay = 10 SECONDS + + var/maxx + var/maxy + var/landmark_type = /obj/effect/shuttle_landmark/automatic + + var/list/rock_colors = list(COLOR_ASTEROID_ROCK) + var/list/plant_colors = list("RANDOM") + var/grass_color + var/surface_color = COLOR_ASTEROID_ROCK + var/water_color = "#436499" + var/image/skybox_image + + var/list/actors = list() //things that appear in engravings on xenoarch finds. + var/list/species = list() //list of names to use for simple animals + + var/repopulating = 0 + var/repopulate_types = list() // animals which have died that may come back + + var/list/possible_themes = list(/datum/exoplanet_theme) + var/list/themes = list() + + var/list/map_generators = list() + + //Flags deciding what features to pick + var/ruin_tags_whitelist + var/ruin_tags_blacklist + var/features_budget = 4 + var/list/possible_features = list() + var/list/spawned_features + + var/habitability_class + + var/list/mobs_to_tolerate = list() + +/obj/effect/overmap/visitable/sector/exoplanet/proc/generate_habitability() + var/roll = rand(1,100) + switch(roll) + if(1 to 10) + habitability_class = HABITABILITY_IDEAL + if(11 to 50) + habitability_class = HABITABILITY_OKAY + else + habitability_class = HABITABILITY_BAD + +/obj/effect/overmap/visitable/sector/exoplanet/New(nloc, max_x, max_y) + if(!current_map.use_overmap) + return + + maxx = max_x ? max_x : world.maxx + maxy = max_y ? max_y : world.maxy + planetary_area = new planetary_area() + + name = "[generate_planet_name()], \a [name]" + + world.maxz++ + forceMove(locate(1,1,world.maxz)) + + if(LAZYLEN(possible_themes)) + var/datum/exoplanet_theme/T = pick(possible_themes) + themes += new T + + for(var/T in subtypesof(/datum/map_template/ruin/exoplanet)) + var/datum/map_template/ruin/exoplanet/ruin = T + if(ruin_tags_whitelist && !(ruin_tags_whitelist & initial(ruin.ruin_tags))) + continue + if(ruin_tags_blacklist & initial(ruin.ruin_tags)) + continue + possible_features += new ruin + ..() + +/obj/effect/overmap/visitable/sector/exoplanet/proc/build_level() + generate_habitability() + generate_atmosphere() + generate_map() + generate_features() + generate_landing(2) + update_biome() + generate_daycycle() + START_PROCESSING(SSprocessing, src) + +//attempt at more consistent history generation for xenoarch finds. +/obj/effect/overmap/visitable/sector/exoplanet/proc/get_engravings() + if(!actors.len) + actors += pick("alien humanoid","an amorphic blob","a short, hairy being","a rodent-like creature","a robot","a primate","a reptilian alien","an unidentifiable object","a statue","a starship","unusual devices","a structure") + actors += pick("alien humanoids","amorphic blobs","short, hairy beings","rodent-like creatures","robots","primates","reptilian aliens") + + var/engravings = "[actors[1]] \ + [pick("surrounded by","being held aloft by","being struck by","being examined by","communicating with")] \ + [actors[2]]" + if(prob(50)) + engravings += ", [pick("they seem to be enjoying themselves","they seem extremely angry","they look pensive","they are making gestures of supplication","the scene is one of subtle horror","the scene conveys a sense of desperation","the scene is completely bizarre")]" + engravings += "." + return engravings + +/obj/effect/overmap/visitable/sector/exoplanet/process(wait, tick) + if(animals.len < 0.5*max_animal_count && !repopulating) + repopulating = 1 + max_animal_count = round(max_animal_count * 0.5) + for(var/zlevel in map_z) + if(repopulating) + for(var/i = 1 to round(max_animal_count - animals.len)) + if(prob(10)) + var/turf/simulated/T = pick_area_turf(planetary_area, list(/proc/not_turf_contains_dense_objects)) + var/mob_type = pick(repopulate_types) + var/mob/S = new mob_type(T) + animals += S + death_event.register(S, src, /obj/effect/overmap/visitable/sector/exoplanet/proc/remove_animal) + destroyed_event.register(S, src, /obj/effect/overmap/visitable/sector/exoplanet/proc/remove_animal) + adapt_animal(S) + if(animals.len >= max_animal_count) + repopulating = 0 + + if(!atmosphere) + continue + var/zone/Z + for(var/i = 1 to maxx) + var/turf/simulated/T = locate(i, 2, zlevel) + if(istype(T) && T.zone && T.zone.contents.len > (maxx*maxy*0.25)) //if it's a zone quarter of zlevel, good enough odds it's planetary main one + Z = T.zone + break + if(Z && !Z.fire_tiles.len && !atmosphere.compare(Z.air)) //let fire die out first if there is one + var/datum/gas_mixture/daddy = new() //make a fake 'planet' zone gas + daddy.copy_from(atmosphere) + daddy.group_multiplier = Z.air.group_multiplier + Z.air.equalize(daddy) + + if(daycycle) + if(tick % round(daycycle / wait) == 0) + night = !night + daycolumn = 1 + if(daycolumn && tick % round(daycycle_column_delay / wait) == 0) + update_daynight() + +/obj/effect/overmap/visitable/sector/exoplanet/proc/update_daynight() + var/light = 0.1 + if(!night) + light = lightlevel + for(var/turf/simulated/floor/exoplanet/T in block(locate(daycolumn,1,min(map_z)),locate(daycolumn,maxy,max(map_z)))) + T.set_light(light, 0.1, 2) + daycolumn++ + if(daycolumn > maxx) + daycolumn = 0 + +/obj/effect/overmap/visitable/sector/exoplanet/proc/remove_animal(var/mob/M) + animals -= M + death_event.unregister(M, src) + destroyed_event.unregister(M, src) + repopulate_types |= M.type + +/obj/effect/overmap/visitable/sector/exoplanet/proc/generate_map() + var/list/grasscolors = plant_colors.Copy() + grasscolors -= "RANDOM" + if(length(grasscolors)) + grass_color = pick(grasscolors) + + for(var/datum/exoplanet_theme/T as anything in themes) + T.before_map_generation(src) + for(var/zlevel in map_z) + for(var/map_type in map_generators) + if(ispath(map_type, /datum/random_map/noise/exoplanet)) + var/datum/random_map/noise/exoplanet/RM = new map_type(null,1,1,zlevel,maxx,maxy,0,1,1,planetary_area, plant_colors) + get_biostuff(RM) + else + new map_type(null,1,1,zlevel,maxx,maxy,0,1,1,planetary_area) + + var/list/edges + edges += block(locate(1, 1, zlevel), locate(TRANSITIONEDGE, maxy, zlevel)) + edges |= block(locate(maxx-TRANSITIONEDGE+1, 1, zlevel),locate(maxx, maxy, zlevel)) + edges |= block(locate(1, 1, zlevel), locate(maxx, TRANSITIONEDGE, zlevel)) + edges |= block(locate(1, maxy-TRANSITIONEDGE+1, zlevel),locate(maxx, maxy, zlevel)) + for(var/turf/T in edges) + T.ChangeTurf(/turf/simulated/planet_edge) + +/obj/effect/overmap/visitable/sector/exoplanet/proc/generate_features() + spawned_features = seedRuins(map_z, features_budget, possible_features, /area/exoplanet, maxx, maxy) + +/obj/effect/overmap/visitable/sector/exoplanet/proc/get_biostuff(var/datum/random_map/noise/exoplanet/random_map) + if(!istype(random_map)) + return + seeds += random_map.small_flora_types + if(random_map.big_flora_types) + seeds += random_map.big_flora_types + for(var/mob/living/simple_animal/A in living_mob_list) + if(A.z in map_z) + animals += A + death_event.register(A, src, /obj/effect/overmap/visitable/sector/exoplanet/proc/remove_animal) + destroyed_event.register(A, src, /obj/effect/overmap/visitable/sector/exoplanet/proc/remove_animal) + max_animal_count = animals.len + for(var/type in random_map.fauna_types) + mobs_to_tolerate[type] = TRUE + +/obj/effect/overmap/visitable/sector/exoplanet/proc/update_biome() + for(var/datum/seed/S as anything in seeds) + adapt_seed(S) + + for(var/mob/living/simple_animal/A as anything in animals) + adapt_animal(A) + +/obj/effect/overmap/visitable/sector/exoplanet/proc/generate_daycycle() + if(lightlevel) + night = FALSE //we start with a day if we have light. + + //When you set daycycle ensure that the minimum is larger than [maxx * daycycle_column_delay]. + //Otherwise the right side of the exoplanet can get stuck in a forever day. + daycycle = rand(10 MINUTES, 40 MINUTES) + +/obj/effect/overmap/visitable/sector/exoplanet/proc/adapt_seed(var/datum/seed/S) + S.set_trait(TRAIT_IDEAL_HEAT, atmosphere.temperature + rand(-5,5),800,70) + S.set_trait(TRAIT_HEAT_TOLERANCE, S.get_trait(TRAIT_HEAT_TOLERANCE) + rand(-5,5),800,70) + S.set_trait(TRAIT_LOWKPA_TOLERANCE, atmosphere.return_pressure() + rand(-5,-50),80,0) + S.set_trait(TRAIT_HIGHKPA_TOLERANCE, atmosphere.return_pressure() + rand(5,50),500,110) + if(S.exude_gasses) + S.exude_gasses -= badgas + if(atmosphere) + if(S.consume_gasses) + S.consume_gasses = list(pick(atmosphere.gas)) // ensure that if the plant consumes a gas, the atmosphere will have it + for(var/g in atmosphere.gas) + if(gas_data.flags[g] & XGM_GAS_CONTAMINANT) + S.set_trait(TRAIT_TOXINS_TOLERANCE, rand(10,15)) + +/obj/effect/overmap/visitable/sector/exoplanet/proc/adapt_animal(var/mob/living/simple_animal/A) + if(species[A.type]) + A.name = species[A.type] + A.real_name = species[A.type] + else + A.name = "alien creature" + A.real_name = "alien creature" + A.verbs |= /mob/living/simple_animal/proc/name_species + if(istype(A, /mob/living/simple_animal/hostile)) + var/mob/living/simple_animal/hostile/AH = A + AH.tolerated_types = mobs_to_tolerate.Copy() + if(atmosphere) + //Set up gases for living things + if(!LAZYLEN(breathgas)) + var/list/goodgases = gas_data.gases.Copy() + var/gasnum = min(rand(1,3), goodgases.len) + for(var/i = 1 to gasnum) + var/gas = pick(goodgases) + breathgas[gas] = round(0.4*goodgases[gas], 0.1) + goodgases -= gas + if(!badgas) + var/list/badgases = gas_data.gases.Copy() + badgases -= atmosphere.gas + badgas = pick(badgases) + + A.minbodytemp = atmosphere.temperature - 20 + A.maxbodytemp = atmosphere.temperature + 30 + A.bodytemperature = (A.maxbodytemp+A.minbodytemp)/2 + +/obj/effect/overmap/visitable/sector/exoplanet/proc/get_random_species_name() + return pick("nol","shan","can","fel","xor")+pick("a","e","o","t","ar")+pick("ian","oid","ac","ese","inian","rd") + +/obj/effect/overmap/visitable/sector/exoplanet/proc/rename_species(var/species_type, var/newname, var/force = FALSE) + if(species[species_type] && !force) + return FALSE + + species[species_type] = newname + log_and_message_admins("renamed [species_type] to [newname]") + for(var/mob/living/simple_animal/A in animals) + if(istype(A,species_type)) + A.name = newname + A.real_name = newname + A.verbs -= /mob/living/simple_animal/proc/name_species + return TRUE + +//This tries to generate "num" landing spots on the map. +// A landing spot is a 20x20 zone where the shuttle can land where each tile has a area of /area/exoplanet and no ruins on top of it +// It makes num*20 attempts to pick a landing spot, during which it attempts to find a area which meets the above criteria. +// If it that does not work, it tries to clear the area +//There is also a sanity check to ensure that the map isnt too small to handle the landing spot +/obj/effect/overmap/visitable/sector/exoplanet/proc/generate_landing(num = 1) + var/places = list() + var/attempts = 20*num + var/new_type = landmark_type + + //sanity-check map size + var/lm_min_x = TRANSITIONEDGE+10 + var/lm_max_x = maxx-TRANSITIONEDGE-10 + var/lm_min_y = TRANSITIONEDGE+10 + var/lm_max_y = maxy-TRANSITIONEDGE-10 + if (lm_max_x < lm_min_x || lm_max_y < lm_min_y) + log_and_message_admins("Map Size is too small to Support Away Mission Shuttle Landmark. [lm_min_x] [lm_max_x] [lm_min_y] [lm_max_y]") + return + + while(num) + attempts-- + var/turf/T = locate(rand(lm_min_x, lm_max_x), rand(lm_min_y, lm_max_y),map_z[map_z.len]) + if(!T || (T in places)) // Two landmarks on one turf is forbidden as the landmark code doesn't work with it. + continue + if(attempts >= 0) // While we have the patience, try to find better spawn points. If out of patience, put them down wherever, so long as there are no repeats. + var/valid = 1 + var/list/block_to_check = block(locate(T.x - 10, T.y - 10, T.z), locate(T.x + 10, T.y + 10, T.z)) + for(var/turf/check in block_to_check) + if(!istype(get_area(check), /area/exoplanet) || check.flags & TURF_NORUINS) + valid = 0 + break + if(attempts >= 10) + if(check_collision(T.loc, block_to_check)) //While we have lots of patience, ensure landability + valid = 0 + else //Running out of patience, but would rather not clear ruins, so switch to clearing landmarks and bypass landability check + new_type = /obj/effect/shuttle_landmark/automatic/clearing + + if(!valid) + continue + + num-- + places += T + new new_type(T) + +/obj/effect/overmap/visitable/sector/exoplanet/proc/generate_atmosphere() + atmosphere = new + if(habitability_class == HABITABILITY_IDEAL) + atmosphere.adjust_gas(GAS_OXYGEN, MOLES_O2STANDARD, 0) + atmosphere.adjust_gas(GAS_NITROGEN, MOLES_N2STANDARD) + else //let the fuckery commence + var/list/newgases = gas_data.gases.Copy() + if(prob(90)) //all phoron planet should be rare + newgases -= GAS_PHORON + if(prob(50)) //alium gas should be slightly less common than mundane shit + newgases -= GAS_ALIEN + newgases -= GAS_STEAM + + var/total_moles = MOLES_CELLSTANDARD * rand(80,120)/100 + var/badflag = 0 + + //Breathable planet + if(habitability_class == HABITABILITY_OKAY) + atmosphere.gas[GAS_OXYGEN] += MOLES_O2STANDARD + total_moles -= MOLES_O2STANDARD + badflag = XGM_GAS_FUEL|XGM_GAS_CONTAMINANT + + var/gasnum = rand(1,4) + var/i = 1 + var/sanity = prob(99.9) + while(i <= gasnum && total_moles && newgases.len) + if(badflag && sanity) + for(var/g in newgases) + if(gas_data.flags[g] & badflag) + newgases -= g + var/ng = pick_n_take(newgases) //pick a gas + if(sanity) //make sure atmosphere is not flammable... always + if(gas_data.flags[ng] & XGM_GAS_OXIDIZER) + badflag |= XGM_GAS_FUEL + if(gas_data.flags[ng] & XGM_GAS_FUEL) + badflag |= XGM_GAS_OXIDIZER + sanity = 0 + + var/part = total_moles * rand(3,80)/100 //allocate percentage to it + if(i == gasnum || !newgases.len) //if it's last gas, let it have all remaining moles + part = total_moles + atmosphere.gas[ng] += part + total_moles = max(total_moles - part, 0) + i++ + +/obj/effect/overmap/visitable/sector/exoplanet/get_scan_data(mob/user) + . = ..() + var/list/extra_data = list("