diff --git a/_maps/RandomZLevels/Cabin.dmm b/_maps/RandomZLevels/Cabin.dmm index f4a18fd87c..4d07c02565 100644 --- a/_maps/RandomZLevels/Cabin.dmm +++ b/_maps/RandomZLevels/Cabin.dmm @@ -967,10 +967,6 @@ }, /turf/open/floor/plating, /area/awaymission/cabin) -"dw" = ( -/obj/effect/mapping_helpers/planet_z, -/turf/closed/indestructible/rock/snow, -/area/space) (1,1,1) = {" aa @@ -1227,7 +1223,7 @@ aa aa aa aa -dw +aa "} (2,1,1) = {" aa diff --git a/_maps/RandomZLevels/VR/snowdin_VR.dmm b/_maps/RandomZLevels/VR/snowdin_VR.dmm index 8f6eda450c..bae0b05e3d 100644 --- a/_maps/RandomZLevels/VR/snowdin_VR.dmm +++ b/_maps/RandomZLevels/VR/snowdin_VR.dmm @@ -4,9 +4,15 @@ /turf/closed/indestructible/rock/snow, /area/awaymission/snowdin/cave/mountain) "ab" = ( -/obj/effect/mapping_helpers/planet_z, -/turf/closed/indestructible/rock/snow, -/area/awaymission/snowdin/cave/mountain) +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/mob/living/simple_animal/hostile/netherworld/migo, +/turf/open/floor/plasteel, +/area/awaymission/snowdin/post/mining_dock) "ac" = ( /turf/closed/indestructible/rock/snow, /area/awaymission/snowdin/cave/mountain) @@ -10267,16 +10273,6 @@ /obj/item/shard, /turf/open/floor/plating, /area/awaymission/snowdin/post/mining_dock) -"xA" = ( -/mob/living/simple_animal/hostile/netherworld/migo, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel, -/area/awaymission/snowdin/post/mining_dock) "xB" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -16013,7 +16009,7 @@ ac ac "} (2,1,1) = {" -ab +ac ac ac ac @@ -66518,7 +66514,7 @@ wD wT xe xs -xA +ab xI xN wL diff --git a/_maps/RandomZLevels/caves.dmm b/_maps/RandomZLevels/caves.dmm index d50480c381..96c1a08b1f 100644 --- a/_maps/RandomZLevels/caves.dmm +++ b/_maps/RandomZLevels/caves.dmm @@ -2251,10 +2251,6 @@ initial_gas_mix = "n2=23;o2=14" }, /area/awaymission/caves/BMP_asteroid) -"gW" = ( -/obj/effect/mapping_helpers/planet_z, -/turf/closed/indestructible/rock, -/area/space/nearstation) "gX" = ( /obj/effect/baseturf_helper/lava, /turf/closed/mineral/volcanic, @@ -2536,7 +2532,7 @@ aa aa aa aa -gW +aa "} (2,1,1) = {" aa diff --git a/_maps/RandomZLevels/moonoutpost19.dmm b/_maps/RandomZLevels/moonoutpost19.dmm index ddc8778ac1..8c33326703 100644 --- a/_maps/RandomZLevels/moonoutpost19.dmm +++ b/_maps/RandomZLevels/moonoutpost19.dmm @@ -7215,10 +7215,6 @@ heat_capacity = 1e+006 }, /area/awaymission/moonoutpost19/research) -"oV" = ( -/obj/effect/mapping_helpers/planet_z, -/turf/open/space, -/area/space) "vV" = ( /obj/machinery/door/airlock/external, /obj/effect/mapping_helpers/airlock/cyclelink_helper{ @@ -7512,7 +7508,7 @@ aa aa aa aa -oV +aa "} (2,1,1) = {" aa diff --git a/_maps/RandomZLevels/snowdin.dmm b/_maps/RandomZLevels/snowdin.dmm index 525c8fcf7b..57e03061dd 100644 --- a/_maps/RandomZLevels/snowdin.dmm +++ b/_maps/RandomZLevels/snowdin.dmm @@ -4,9 +4,15 @@ /turf/closed/indestructible/rock/snow, /area/awaymission/snowdin/cave/mountain) "ab" = ( -/obj/effect/mapping_helpers/planet_z, -/turf/closed/indestructible/rock/snow, -/area/awaymission/snowdin/cave/mountain) +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/mob/living/simple_animal/hostile/netherworld/migo, +/turf/open/floor/plasteel, +/area/awaymission/snowdin/post/mining_dock) "ac" = ( /turf/closed/indestructible/rock/snow, /area/awaymission/snowdin/cave/mountain) @@ -10331,16 +10337,6 @@ /obj/item/shard, /turf/open/floor/plating, /area/awaymission/snowdin/post/mining_dock) -"xA" = ( -/mob/living/simple_animal/hostile/netherworld/migo, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel, -/area/awaymission/snowdin/post/mining_dock) "xB" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -16108,7 +16104,7 @@ ac ac "} (2,1,1) = {" -ab +ac ac ac ac @@ -66613,7 +66609,7 @@ wD wT xe xs -xA +ab xI xN wL diff --git a/_maps/RandomZLevels/spacebattle.dmm b/_maps/RandomZLevels/spacebattle.dmm index 21cd37f78f..66d7f556b4 100644 --- a/_maps/RandomZLevels/spacebattle.dmm +++ b/_maps/RandomZLevels/spacebattle.dmm @@ -5,6 +5,16 @@ "ab" = ( /turf/open/space, /area/space) +"ac" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/mob/living/simple_animal/hostile/syndicate/melee/sword, +/turf/open/floor/plasteel, +/area/awaymission/spacebattle/cruiser) "ad" = ( /obj/structure/shuttle/engine/propulsion/right{ dir = 1 @@ -2597,16 +2607,6 @@ /obj/effect/spawner/lootdrop/armory_contraband, /turf/open/floor/plating, /area/awaymission/spacebattle/cruiser) -"jK" = ( -/mob/living/simple_animal/hostile/syndicate/melee/sword, -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/turf/open/floor/plasteel, -/area/awaymission/spacebattle/cruiser) "jL" = ( /obj/machinery/door/poddoor{ id = "spacebattlearmory"; @@ -2894,10 +2894,6 @@ /obj/item/mecha_parts/mecha_equipment/weapon/energy/ion, /turf/open/floor/plating, /area/awaymission/spacebattle/cruiser) -"kM" = ( -/obj/effect/mapping_helpers/planet_z, -/turf/closed/mineral/random, -/area/space/nearstation) "vw" = ( /turf/closed/wall/mineral/plastitanium/nodiagonal, /area/awaymission/spacebattle/syndicate5) @@ -3175,7 +3171,7 @@ aa aa aa aa -kM +aa "} (2,1,1) = {" aa @@ -35686,10 +35682,10 @@ eC cn fL cp -jK +ac fs fO -jK +ac eM eM eM diff --git a/_maps/RandomZLevels/undergroundoutpost45.dmm b/_maps/RandomZLevels/undergroundoutpost45.dmm index f6d46ef97d..56018b943a 100644 --- a/_maps/RandomZLevels/undergroundoutpost45.dmm +++ b/_maps/RandomZLevels/undergroundoutpost45.dmm @@ -25,6 +25,9 @@ "ag" = ( /turf/closed/wall/mineral/titanium, /area/awaymission/undergroundoutpost45/central) +"ah" = ( +/turf/open/space, +/area/space/nearstation) "aj" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/plasteel{ @@ -13945,10 +13948,6 @@ temperature = 363.9 }, /area/awaymission/undergroundoutpost45/caves) -"zi" = ( -/obj/effect/mapping_helpers/planet_z, -/turf/open/space, -/area/space/nearstation) "KE" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 4 @@ -14211,7 +14210,7 @@ aa aa aa aa -zi +ah "} (2,1,1) = {" aa diff --git a/_maps/map_files/Mining/Lavaland.dmm b/_maps/map_files/Mining/Lavaland.dmm index de42e1ac63..99c536bd3c 100644 --- a/_maps/map_files/Mining/Lavaland.dmm +++ b/_maps/map_files/Mining/Lavaland.dmm @@ -3571,14 +3571,6 @@ }, /turf/open/floor/plating/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) -"Wt" = ( -/obj/effect/baseturf_helper/lava_land/surface, -/turf/closed/wall, -/area/mine/laborcamp/security) -"Wz" = ( -/obj/effect/mapping_helpers/planet_z, -/turf/open/lava/smooth/lava_land_surface, -/area/lavaland/surface/outdoors) "WA" = ( /obj/effect/mapping_helpers/airlock/cyclelink_helper{ dir = 8 @@ -3630,26 +3622,6 @@ }, /turf/open/floor/plasteel, /area/mine/production) -"WF" = ( -/obj/effect/baseturf_helper/lava_land/surface, -/turf/closed/wall, -/area/mine/laborcamp) -"WH" = ( -/obj/effect/baseturf_helper/lava_land/surface, -/turf/closed/wall, -/area/mine/eva) -"WI" = ( -/obj/effect/baseturf_helper/lava_land/surface, -/turf/closed/wall, -/area/mine/production) -"WJ" = ( -/obj/effect/baseturf_helper/lava_land/surface, -/turf/closed/wall/r_wall, -/area/mine/maintenance) -"WK" = ( -/obj/effect/baseturf_helper/lava_land/surface, -/turf/closed/wall, -/area/mine/living_quarters) (1,1,1) = {" aa @@ -3906,7 +3878,7 @@ aj aj aj aj -Wz +aj "} (2,1,1) = {" aa @@ -8751,7 +8723,7 @@ aq WB aq bi -WF +aq WC aq bZ @@ -9012,7 +8984,7 @@ az az aq ca -Wt +ca ca aj aj @@ -12102,7 +12074,7 @@ ai cQ dk dA -WJ +cQ ed er eM @@ -13134,7 +13106,7 @@ dR ef es dZ -WK +cM fg cM cM @@ -20320,9 +20292,9 @@ bt bH bV cq -WH +bf +bq bq -WI db bP bP diff --git a/code/__DEFINES/dynamic.dm b/code/__DEFINES/dynamic.dm new file mode 100644 index 0000000000..45c1ba9cb2 --- /dev/null +++ b/code/__DEFINES/dynamic.dm @@ -0,0 +1,14 @@ +#define CURRENT_LIVING_PLAYERS 1 +#define CURRENT_LIVING_ANTAGS 2 +#define CURRENT_DEAD_PLAYERS 3 +#define CURRENT_OBSERVERS 4 + +#define NO_ASSASSIN (1<<0) +#define WAROPS_ALWAYS_ALLOWED (1<<1) + +#define ONLY_RULESET (1<<0) +#define HIGHLANDER_RULESET (1<<1) +#define TRAITOR_RULESET (1<<2) +#define MINOR_RULESET (1<<3) + +#define RULESET_STOP_PROCESSING 1 diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm index 1b42217fe1..73e900226e 100644 --- a/code/__DEFINES/maps.dm +++ b/code/__DEFINES/maps.dm @@ -38,8 +38,6 @@ require only minor tweaks. #define ZTRAIT_SPACE_RUINS "Space Ruins" #define ZTRAIT_LAVA_RUINS "Lava Ruins" #define ZTRAIT_ISOLATED_RUINS "Isolated Ruins" //Placing ruins on z levels with this trait will use turf reservation instead of usual placement. -// prevents certain turfs from being stripped by a singularity -#define ZTRAIT_PLANET "Planet" // number - bombcap is multiplied by this before being applied to bombs #define ZTRAIT_BOMBCAP_MULTIPLIER "Bombcap Multiplier" @@ -60,11 +58,18 @@ require only minor tweaks. // CROSSLINKED - mixed in with the cross-linked space pool #define CROSSLINKED "Cross" +// string - type path of the z-level's baseturf (defaults to space) +#define ZTRAIT_BASETURF "Baseturf" + // default trait definitions, used by SSmapping #define ZTRAITS_CENTCOM list(ZTRAIT_CENTCOM = TRUE) #define ZTRAITS_STATION list(ZTRAIT_LINKAGE = CROSSLINKED, ZTRAIT_STATION = TRUE) #define ZTRAITS_SPACE list(ZTRAIT_LINKAGE = CROSSLINKED, ZTRAIT_SPACE_RUINS = TRUE) -#define ZTRAITS_LAVALAND list(ZTRAIT_MINING = TRUE, ZTRAIT_LAVA_RUINS = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 5) +#define ZTRAITS_LAVALAND list(\ + ZTRAIT_MINING = TRUE, \ + ZTRAIT_LAVA_RUINS = TRUE, \ + ZTRAIT_BOMBCAP_MULTIPLIER = 5, \ + ZTRAIT_BASETURF = /turf/open/lava/smooth/lava_land_surface) #define ZTRAITS_REEBE list(ZTRAIT_REEBE = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 0.5) #define DL_NAME "name" diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index d89743d2d2..d595abd3ec 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -427,6 +427,7 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S //Dummy mob reserve slots #define DUMMY_HUMAN_SLOT_PREFERENCES "dummy_preference_preview" +#define DUMMY_HUMAN_SLOT_HOLOFORM "dummy_holoform_generation" #define DUMMY_HUMAN_SLOT_ADMIN "admintools" #define DUMMY_HUMAN_SLOT_MANIFEST "dummy_manifest_generation" @@ -529,4 +530,10 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define CRYOMOBS 'icons/obj/cryo_mobs.dmi' +#define CUSTOM_HOLOFORM_DELAY 10 SECONDS //prevents spamming to make lag. it's pretty expensive to do this. + +#define HOLOFORM_FILTER_AI "FILTER_AI" +#define HOLOFORM_FILTER_PAI "FILTER_PAI" +#define HOLOFORM_FILTER_STATIC "FILTER_STATIC" + #define CANT_REENTER_ROUND -1 diff --git a/code/__DEFINES/vote.dm b/code/__DEFINES/vote.dm new file mode 100644 index 0000000000..8fb2e6deab --- /dev/null +++ b/code/__DEFINES/vote.dm @@ -0,0 +1,6 @@ +#define PLURALITY_VOTING 0 +#define APPROVAL_VOTING 1 +#define RANKED_CHOICE_VOTING 2 +#define SCORE_VOTING 3 + +GLOBAL_LIST_INIT(vote_score_options,list("Bad","Poor","Acceptable","Good","Great")) diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index c259831f94..3ee77d3edc 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -157,6 +157,8 @@ WRITE_LOG(GLOB.config_error_log, text) SEND_TEXT(world.log, text) +/proc/log_mapping(text) + WRITE_LOG(GLOB.world_map_error_log, text) /* For logging round startup. */ /proc/start_log(log) diff --git a/code/__HELPERS/custom_holoforms.dm b/code/__HELPERS/custom_holoforms.dm new file mode 100644 index 0000000000..0e97314f1f --- /dev/null +++ b/code/__HELPERS/custom_holoforms.dm @@ -0,0 +1,62 @@ +// Generates a holoform appearance +// Equipment list is slot = path. +/proc/generate_custom_holoform_from_prefs(datum/preferences/prefs, list/equipment_by_slot, list/inhand_equipment, copy_job = FALSE, apply_loadout = FALSE) + ASSERT(prefs) + var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy(DUMMY_HUMAN_SLOT_HOLOFORM) + prefs.copy_to(mannequin) + if(apply_loadout && prefs.parent) + SSjob.equip_loadout(prefs.parent.mob, mannequin, bypass_prereqs = TRUE) + if(copy_job) + var/datum/job/highest = prefs.get_highest_job() + if(highest && !istype(highest, /datum/job/ai) && !istype(highest, /datum/job/cyborg)) + highest.equip(mannequin, TRUE, preference_source = prefs.parent) + + if(length(equipment_by_slot)) + for(var/slot in equipment_by_slot) + var/obj/item/I = new equipment_by_slot[slot] + mannequin.equip_to_slot_if_possible(I, slot, TRUE, TRUE, TRUE, TRUE) + if(length(inhand_equipment)) + for(var/path in inhand_equipment) + var/obj/item/I = new path + mannequin.equip_to_slot_if_possible(I, SLOT_HANDS, TRUE, TRUE, TRUE, TRUE) + + + var/icon/combined = new + for(var/d in GLOB.cardinals) + mannequin.setDir(d) + COMPILE_OVERLAYS(mannequin) + CHECK_TICK + var/icon/capture = getFlatIcon(mannequin) + CHECK_TICK + combined.Insert(capture, dir = d) + CHECK_TICK + + unset_busy_human_dummy(DUMMY_HUMAN_SLOT_HOLOFORM) + return combined + +/proc/process_holoform_icon_filter(icon/I, filter_type, clone = TRUE) + if(clone) + I = icon(I) //Clone + switch(filter_type) + if(HOLOFORM_FILTER_AI) + I = getHologramIcon(I) + if(HOLOFORM_FILTER_STATIC) + I = getStaticIcon(I) + if(HOLOFORM_FILTER_PAI) + I = getPAIHologramIcon(I) + return I + +//Errors go to user. +/proc/generate_custom_holoform_from_prefs_safe(datum/preferences/prefs, mob/user) + if(user) + if(user.client.prefs.last_custom_holoform > world.time - CUSTOM_HOLOFORM_DELAY) + to_chat(user, "You are attempting to set your custom holoform too fast!") + return + return generate_custom_holoform_from_prefs(prefs, null, null, TRUE, TRUE) + +//Prompts this client for custom holoform parameters. +/proc/user_interface_custom_holoform(client/C) + var/datum/preferences/target_prefs = C.prefs + ASSERT(target_prefs) + //In the future, maybe add custom path allowances a la admin create outfit but for now.. + return generate_custom_holoform_from_prefs_safe(target_prefs, C.mob) diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 5fb64004c0..1af3143d9d 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -939,7 +939,7 @@ world I.pixel_y++ add_overlay(I)//And finally add the overlay. -/proc/getHologramIcon(icon/A, safety=1)//If safety is on, a new icon is not created. +/proc/getHologramIcon(icon/A, safety = TRUE)//If safety is on, a new icon is not created. var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon. flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. flat_icon.ChangeOpacity(0.5)//Make it half transparent. @@ -947,6 +947,14 @@ world flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. return flat_icon +/proc/getPAIHologramIcon(icon/A, safety = TRUE) + var/icon/flat_icon = safety? A : new(A) + flat_icon.SetIntensity(0.75, 1, 0.75) + flat_icon.ChangeOpacity(0.7) + var/icon/alpha_mask = new('icons/effects/effects.dmi', "scanlineslow")//Scanline effect. + flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. + return flat_icon + //What the mob looks like as animated static //By vg's ComicIronic /proc/getStaticIcon(icon/A, safety = TRUE) diff --git a/code/__HELPERS/level_traits.dm b/code/__HELPERS/level_traits.dm index 55ee069321..3e6e88c8fa 100644 --- a/code/__HELPERS/level_traits.dm +++ b/code/__HELPERS/level_traits.dm @@ -12,6 +12,3 @@ #define is_reserved_level(z) SSmapping.level_trait(z, ZTRAIT_RESERVED) #define is_away_level(z) SSmapping.level_trait(z, ZTRAIT_AWAY) - -// If true, the singularity cannot strip away asteroid turf on this Z -#define is_planet_level(z) SSmapping.level_trait(z, ZTRAIT_PLANET) diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index 8e5da083e3..5ca3513e66 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -28,6 +28,8 @@ GLOBAL_VAR(world_job_debug_log) GLOBAL_PROTECT(world_job_debug_log) GLOBAL_VAR(world_virus_log) GLOBAL_PROTECT(world_virus_log) +GLOBAL_VAR(world_map_error_log) +GLOBAL_PROTECT(world_map_error_log) GLOBAL_LIST_EMPTY(bombers) GLOBAL_PROTECT(bombers) diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm index a5d27819b8..14954524fa 100644 --- a/code/controllers/configuration/configuration.dm +++ b/code/controllers/configuration/configuration.dm @@ -14,6 +14,7 @@ var/list/modes // allowed modes var/list/gamemode_cache var/list/votable_modes // votable modes + var/list/storyteller_cache var/list/mode_names var/list/mode_reports var/list/mode_false_report_weight @@ -37,6 +38,7 @@ CRASH("/datum/controller/configuration/Load() called more than once!") InitEntries() LoadModes() + storyteller_cache = typecacheof(/datum/dynamic_storyteller, TRUE) if(fexists("[directory]/config.txt") && LoadEntries("config.txt") <= 1) var/list/legacy_configs = list("game_options.txt", "dbconfig.txt", "comms.txt") for(var/I in legacy_configs) @@ -227,6 +229,7 @@ for(var/T in gamemode_cache) // I wish I didn't have to instance the game modes in order to look up // their information, but it is the only way (at least that I know of). + // for future reference: just use initial() lol var/datum/game_mode/M = new T() if(M.config_tag) @@ -317,6 +320,14 @@ return new T return new /datum/game_mode/extended() +/datum/controller/configuration/proc/pick_storyteller(storyteller_name) + for(var/T in storyteller_cache) + var/datum/dynamic_storyteller/S = T + var/name = initial(S.name) + if(name && name == storyteller_name) + return T + return /datum/dynamic_storyteller/classic + /datum/controller/configuration/proc/get_runnable_modes() var/list/datum/game_mode/runnable_modes = new var/list/probabilities = Get(/datum/config_entry/keyed_list/probability) diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index 61b5788ef8..b95ef43d37 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -386,6 +386,8 @@ config_entry_value = 72000 min_val = 0 +/datum/config_entry/flag/pai_custom_holoforms + /datum/config_entry/number/marauder_delay_non_reebe config_entry_value = 1800 min_val = 0 diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 1bfdc79d27..2a59558bcd 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -315,7 +315,7 @@ SUBSYSTEM_DEF(air) var/starting_ats = active_turfs.len sleep(world.tick_lag) var/timer = world.timeofday - warning("There are [starting_ats] active turfs at roundstart, this is a mapping error caused by a difference of the air between the adjacent turfs. You can see its coordinates using \"Mapping -> Show roundstart AT list\" verb (debug verbs required)") + log_mapping("There are [starting_ats] active turfs at roundstart caused by a difference of the air between the adjacent turfs. You can see its coordinates using \"Mapping -> Show roundstart AT list\" verb (debug verbs required).") for(var/turf/T in active_turfs) GLOB.active_turfs_startlist += T diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index af7ae825f4..7597a9229a 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -658,7 +658,7 @@ SUBSYSTEM_DEF(job) message_admins(msg) CRASH(msg) -/datum/controller/subsystem/job/proc/equip_loadout(mob/dead/new_player/N, mob/living/M, equipbackpackstuff) +/datum/controller/subsystem/job/proc/equip_loadout(mob/dead/new_player/N, mob/living/M, equipbackpackstuff, bypass_prereqs = FALSE) var/mob/the_mob = N if(!the_mob) the_mob = M // cause this doesn't get assigned if player is a latejoiner @@ -671,7 +671,7 @@ SUBSYSTEM_DEF(job) if(!G) continue var/permitted = TRUE - if(G.restricted_roles && G.restricted_roles.len && !(M.mind.assigned_role in G.restricted_roles)) + if(!bypass_prereqs && G.restricted_roles && G.restricted_roles.len && !(M.mind.assigned_role in G.restricted_roles)) permitted = FALSE if(G.donoritem && !G.donator_ckey_check(the_mob.client.ckey)) permitted = FALSE diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm index 8329493818..e43c8f21a2 100644 --- a/code/controllers/subsystem/persistence.dm +++ b/code/controllers/subsystem/persistence.dm @@ -13,13 +13,14 @@ SUBSYSTEM_DEF(persistence) var/list/saved_messages = list() var/list/saved_modes = list(1,2,3) var/list/saved_dynamic_rules = list(list(),list(),list()) - var/list/saved_threat_levels = list(1,1,1) + var/list/saved_storytellers = list("foo","bar","baz","foo again","bar again") var/list/saved_maps var/list/saved_trophies = list() var/list/spawned_objects = list() var/list/antag_rep = list() var/list/antag_rep_change = list() var/list/picture_logging_information = list() + var/list/saved_votes = list() var/list/obj/structure/sign/picture_frame/photo_frames var/list/obj/item/storage/photo_album/photo_albums @@ -29,9 +30,12 @@ SUBSYSTEM_DEF(persistence) LoadChiselMessages() LoadTrophies() LoadRecentModes() - LoadRecentThreats() + LoadRecentStorytellers() + LoadRecentRulesets() LoadRecentMaps() LoadPhotoPersistence() + for(var/client/C in GLOB.clients) + LoadSavedVote(C.ckey) if(CONFIG_GET(flag/use_antag_rep)) LoadAntagReputation() LoadRandomizedRecipes() @@ -169,14 +173,23 @@ SUBSYSTEM_DEF(persistence) return saved_modes = json["data"] -/datum/controller/subsystem/persistence/proc/LoadRecentThreats() - var/json_file = file("data/RecentThreatLevels.json") +/datum/controller/subsystem/persistence/proc/LoadRecentRulesets() + var/json_file = file("data/RecentRulesets.json") if(!fexists(json_file)) return var/list/json = json_decode(file2text(json_file)) if(!json) return - saved_threat_levels = json["data"] + saved_dynamic_rules = json["data"] + +/datum/controller/subsystem/persistence/proc/LoadRecentStorytellers() + var/json_file = file("data/RecentStorytellers.json") + if(!fexists(json_file)) + return + var/list/json = json_decode(file2text(json_file)) + if(!json) + return + saved_storytellers = json["data"] /datum/controller/subsystem/persistence/proc/LoadRecentMaps() var/json_file = file("data/RecentMaps.json") @@ -197,6 +210,15 @@ SUBSYSTEM_DEF(persistence) return antag_rep = json_decode(json) +/datum/controller/subsystem/persistence/proc/LoadSavedVote(var/ckey) + var/json_file = file("data/player_saves/[copytext(ckey,1,2)]/[ckey]/SavedVotes.json") + if(!fexists(json_file)) + return + var/list/json = json_decode(file2text(json_file)) + if(!json) + return + saved_votes[ckey] = json["data"] + /datum/controller/subsystem/persistence/proc/SetUpTrophies(list/trophy_items) for(var/A in GLOB.trophy_cases) var/obj/structure/displaycase/trophy/T = A @@ -230,7 +252,7 @@ SUBSYSTEM_DEF(persistence) CollectRoundtype() if(istype(SSticker.mode, /datum/game_mode/dynamic)) var/datum/game_mode/dynamic/mode = SSticker.mode - CollectThreatLevel(mode) + CollectStoryteller(mode) CollectRulesets(mode) RecordMaps() SavePhotoPersistence() //THIS IS PERSISTENCE, NOT THE LOGGING PORTION. @@ -388,13 +410,16 @@ SUBSYSTEM_DEF(persistence) fdel(json_file) WRITE_FILE(json_file, json_encode(file_data)) -/datum/controller/subsystem/persistence/proc/CollectThreatLevel(var/datum/game_mode/dynamic/mode) - saved_threat_levels[3] = saved_threat_levels[2] - saved_threat_levels[2] = saved_threat_levels [1] - saved_threat_levels[1] = mode.threat_level - var/json_file = file("data/RecentThreatLevels.json") +/datum/controller/subsystem/persistence/proc/CollectStoryteller(var/datum/game_mode/dynamic/mode) + saved_storytellers.len = 5 + saved_storytellers[5] = saved_storytellers[4] + saved_storytellers[4] = saved_storytellers[3] + saved_storytellers[3] = saved_storytellers[2] + saved_storytellers[2] = saved_storytellers[1] + saved_storytellers[1] = mode.storyteller.name + var/json_file = file("data/RecentStorytellers.json") var/list/file_data = list() - file_data["data"] = saved_threat_levels + file_data["data"] = saved_storytellers fdel(json_file) WRITE_FILE(json_file, json_encode(file_data)) @@ -402,8 +427,9 @@ SUBSYSTEM_DEF(persistence) saved_dynamic_rules[3] = saved_dynamic_rules[2] saved_dynamic_rules[2] = saved_dynamic_rules[1] saved_dynamic_rules[1] = list() - for(var/datum/dynamic_ruleset/ruleset in mode.executed_rules) - saved_dynamic_rules[1] += ruleset.config_tag + for(var/r in mode.executed_rules) + var/datum/dynamic_ruleset/rule = r + saved_dynamic_rules[1] += rule.config_tag var/json_file = file("data/RecentRulesets.json") var/list/file_data = list() file_data["data"] = saved_dynamic_rules @@ -473,3 +499,11 @@ SUBSYSTEM_DEF(persistence) fdel(json_file) WRITE_FILE(json_file, json_encode(file_data)) + +/datum/controller/subsystem/persistence/proc/SaveSavedVotes() + for(var/ckey in saved_votes) + var/json_file = file("data/player_saves/[copytext(ckey,1,2)]/[ckey]/SavedVotes.json") + var/list/file_data = list() + file_data["data"] = saved_votes[ckey] + fdel(json_file) + WRITE_FILE(json_file, json_encode(file_data)) diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index fe40b3f01f..b406a8ccad 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -37,7 +37,7 @@ SUBSYSTEM_DEF(shuttle) var/points = 5000 //number of trade-points we have var/centcom_message = "" //Remarks from CentCom on how well you checked the last order. var/list/discoveredPlants = list() //Typepaths for unusual plants we've already sent CentCom, associated with their potencies - var/passive_supply_points_per_minute = 750 + var/passive_supply_points_per_minute = 500 var/list/supply_packs = list() var/list/shoppinglist = list() diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 974d870a8a..45c8e3c8fe 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -483,7 +483,10 @@ SUBSYSTEM_DEF(ticker) SSticker.timeLeft = 900 SSticker.modevoted = TRUE var/dynamic = CONFIG_GET(flag/dynamic_voting) - SSvote.initiate_vote(dynamic ? "dynamic" : "roundtype","server",TRUE) + if(dynamic) + SSvote.initiate_vote("dynamic","server",hideresults=TRUE,votesystem=SCORE_VOTING,forced=TRUE,vote_time = 2 MINUTES) + else + SSvote.initiate_vote("roundtype","server",hideresults=TRUE,votesystem=PLURALITY_VOTING,forced=TRUE, vote_time = 2 MINUTES) /datum/controller/subsystem/ticker/Recover() current_state = SSticker.current_state diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index dee10e4998..573c89a5af 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -1,3 +1,5 @@ +#define VOTE_COOLDOWN 10 + SUBSYSTEM_DEF(vote) name = "Vote" wait = 10 @@ -8,13 +10,17 @@ SUBSYSTEM_DEF(vote) var/initiator = null var/started_time = null - var/time_remaining = 0 + var/end_time = 0 var/mode = null + var/vote_system = PLURALITY_VOTING var/question = null var/list/choices = list() + var/list/choice_descs = list() // optional descriptions var/list/voted = list() var/list/voting = list() + var/list/saved = list() var/list/generated_actions = list() + var/next_pop = 0 var/obfuscated = FALSE//CIT CHANGE - adds obfuscated/admin-only votes @@ -22,28 +28,30 @@ SUBSYSTEM_DEF(vote) /datum/controller/subsystem/vote/fire() //called by master_controller if(mode) - time_remaining = round((started_time + CONFIG_GET(number/vote_period) - world.time)/10) - - if(time_remaining < 0) + if(end_time < world.time) result() + SSpersistence.SaveSavedVotes() for(var/client/C in voting) C << browse(null, "window=vote;can_close=0") reset() - else + else if(next_pop < world.time) var/datum/browser/client_popup for(var/client/C in voting) - client_popup = new(C, "vote", "Voting Panel") + client_popup = new(C, "vote", "Voting Panel", nwidth=600,nheight=700) client_popup.set_window_options("can_close=0") client_popup.set_content(interface(C)) client_popup.open(0) + next_pop = world.time+VOTE_COOLDOWN + /datum/controller/subsystem/vote/proc/reset() initiator = null - time_remaining = 0 + end_time = 0 mode = null question = null choices.Cut() + choice_descs.Cut() voted.Cut() voting.Cut() obfuscated = FALSE //CIT CHANGE - obfuscated votes @@ -84,17 +92,114 @@ SUBSYSTEM_DEF(vote) . += option return . +/datum/controller/subsystem/vote/proc/calculate_condorcet_votes(var/blackbox_text) + // https://en.wikipedia.org/wiki/Schulze_method#Implementation + var/list/d[][] = new/list(choices.len,choices.len) // the basic vote matrix, how many times a beats b + for(var/ckey in voted) + var/list/this_vote = voted[ckey] + for(var/a in 1 to choices.len) + for(var/b in a+1 to choices.len) + var/a_rank = this_vote.Find(a) + var/b_rank = this_vote.Find(b) + a_rank = a_rank ? a_rank : choices.len+1 + b_rank = b_rank ? b_rank : choices.len+1 + if(a_rankopposite_pref) + p[i][j] = d[i][j] + p[j][i] = 0 + else + p[i][j] = 0 + p[j][i] = d[i][j] + for(var/i in 1 to choices.len) + for(var/j in 1 to choices.len) + if(i != j) + for(var/k in 1 to choices.len) // YEAH O(n^3) !! + if(i != k && j != k) + p[j][k] = max(p[j][k],min(p[j][i], p[i][k])) + //one last pass, now that we've done the math + for(var/i in 1 to choices.len) + for(var/j in 1 to choices.len) + if(i != j) + SSblackbox.record_feedback("nested tally","voting",p[i][j],list(blackbox_text,"Shortest Paths",choices[i],choices[j])) + if(p[i][j] >= p[j][i]) + choices[choices[i]]++ // higher shortest path = better candidate, so we add to choices here + // choices[choices[i]] is the schulze ranking, here, rather than raw vote numbers + +/datum/controller/subsystem/vote/proc/calculate_majority_judgement_vote(var/blackbox_text) + // https://en.wikipedia.org/wiki/Majority_judgment + var/list/scores_by_choice = list() + for(var/choice in choices) + scores_by_choice[choice] = list() + for(var/ckey in voted) + var/list/this_vote = voted[ckey] + var/list/pretty_vote = list() + for(var/choice in this_vote) + sorted_insert(scores_by_choice[choice],this_vote[choice],/proc/cmp_numeric_asc) + // START BALLOT GATHERING + pretty_vote += choice + pretty_vote[choice] = GLOB.vote_score_options[this_vote[choice]] + SSblackbox.record_feedback("associative","voting_ballots",1,pretty_vote) + // END BALLOT GATHERING + for(var/score_name in scores_by_choice) + var/list/score = scores_by_choice[score_name] + for(var/indiv_score in score) + SSblackbox.record_feedback("nested tally","voting",1,list(blackbox_text,"Scores",score_name,GLOB.vote_score_options[indiv_score])) + if(score.len == 0) + scores_by_choice -= score_name + while(scores_by_choice.len > 1) + var/highest_median = 0 + for(var/score_name in scores_by_choice) // first get highest median + var/list/score = scores_by_choice[score_name] + if(!score.len) + scores_by_choice -= score_name + continue + var/median = score[max(1,round(score.len/2))] + if(median >= highest_median) + highest_median = median + for(var/score_name in scores_by_choice) // then, remove + var/list/score = scores_by_choice[score_name] + var/median = score[max(1,round(score.len/2))] + if(median < highest_median) + scores_by_choice -= score_name + for(var/score_name in scores_by_choice) // after removals + var/list/score = scores_by_choice[score_name] + if(score.len == 0) + choices[score_name] += 100 // we're in a tie situation--just go with the first one + return + var/median_pos = max(1,round(score.len/2)) + score.Cut(median_pos,median_pos+1) + choices[score_name]++ + /datum/controller/subsystem/vote/proc/announce_result() - var/list/winners = get_result() + var/vote_title_text var/text + if(question) + text += "[question]" + vote_title_text = "[question]" + else + text += "[capitalize(mode)] Vote" + vote_title_text = "[capitalize(mode)] Vote" + if(vote_system == RANKED_CHOICE_VOTING) + calculate_condorcet_votes(vote_title_text) + if(vote_system == SCORE_VOTING) + calculate_majority_judgement_vote(vote_title_text) + var/list/winners = get_result() var/was_roundtype_vote = mode == "roundtype" || mode == "dynamic" if(winners.len > 0) - if(question) - text += "[question]" - else - text += "[capitalize(mode)] Vote" if(was_roundtype_vote) stored_gamemode_votes = list() + if(!obfuscated && vote_system == RANKED_CHOICE_VOTING) + text += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!" for(var/i=1,i<=choices.len,i++) var/votes = choices[choices[i]] if(!votes) @@ -116,17 +221,25 @@ SUBSYSTEM_DEF(vote) log_vote(text) remove_action_buttons() to_chat(world, "\n[text]") + switch(vote_system) + if(APPROVAL_VOTING,PLURALITY_VOTING) + for(var/i=1,i<=choices.len,i++) + SSblackbox.record_feedback("nested tally","voting",choices[choices[i]],list(vote_title_text,choices[i])) + if(RANKED_CHOICE_VOTING) + for(var/i=1,i<=voted.len,i++) + var/list/myvote = voted[voted[i]] + for(var/j=1,j<=myvote.len,j++) + SSblackbox.record_feedback("nested tally","voting",1,list(vote_title_text,"[j]\th",choices[myvote[j]])) if(obfuscated) //CIT CHANGE - adds obfuscated votes. this messages admins with the vote's true results var/admintext = "Obfuscated results" + if(vote_system == RANKED_CHOICE_VOTING) + admintext += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!" for(var/i=1,i<=choices.len,i++) var/votes = choices[choices[i]] admintext += "\n[choices[i]]: [votes]" message_admins(admintext) return . -#define PEACE "calm" -#define CHAOS "chaotic" - /datum/controller/subsystem/vote/proc/result() . = announce_result() var/restart = 0 @@ -152,33 +265,15 @@ SUBSYSTEM_DEF(vote) if("dynamic") if(SSticker.current_state > GAME_STATE_PREGAME)//Don't change the mode if the round already started. return message_admins("A vote has tried to change the gamemode, but the game has already started. Aborting.") - GLOB.master_mode = "dynamic" - if("extended" in choices) - if(. == "extended") - GLOB.dynamic_forced_extended = TRUE // we still do the rest of the stuff - choices[PEACE] += choices["extended"] - var/mean = 0 - var/voters = 0 - for(var/client/c in GLOB.clients) - var/vote = c.prefs.preferred_chaos - if(vote) - voters += 1 - switch(vote) - if(CHAOS_NONE) - mean -= 0.1 - if(CHAOS_LOW) - mean -= 0.05 - if(CHAOS_HIGH) - mean += 0.05 - if(CHAOS_MAX) - mean += 0.1 - mean/=voters - if(voted.len != 0) - mean += (choices[PEACE]*-1+choices[CHAOS])/voted.len - GLOB.dynamic_curve_centre = mean*20 - GLOB.dynamic_curve_width = CLAMP(2-abs(mean*5),0.5,4) - to_chat(world,"Dynamic curve centre set to [GLOB.dynamic_curve_centre] and width set to [GLOB.dynamic_curve_width].") - log_admin("Dynamic curve centre set to [GLOB.dynamic_curve_centre] and width set to [GLOB.dynamic_curve_width]") + if(. == "Secret") + GLOB.master_mode = "secret" + SSticker.save_mode(.) + message_admins("The gamemode has been voted for, and has been changed to: [GLOB.master_mode]") + log_admin("Gamemode has been voted for and switched to: [GLOB.master_mode].") + else + GLOB.master_mode = "dynamic" + var/datum/dynamic_storyteller/S = config.pick_storyteller(.) + GLOB.dynamic_storyteller_type = S if("map") var/datum/map_config/VM = config.maplist[.] message_admins("The map has been voted for and will change to: [VM.map_name]") @@ -196,27 +291,58 @@ SUBSYSTEM_DEF(vote) else to_chat(world, "Notice:Restart vote will not restart the server automatically because there are active admins on.") message_admins("A restart vote has passed, but there are active admins on with +server, so it has been canceled. If you wish, you may restart the server.") - + return . -/datum/controller/subsystem/vote/proc/submit_vote(vote) +/datum/controller/subsystem/vote/proc/submit_vote(vote, score = 0) if(mode) if(CONFIG_GET(flag/no_dead_vote) && usr.stat == DEAD && !usr.client.holder) return 0 - if(!(usr.ckey in voted)) - if(vote && 1<=vote && vote<=choices.len) - voted += usr.ckey - voted[usr.ckey] = vote - choices[choices[vote]]++ //check this - return vote - else if(vote && 1<=vote && vote<=choices.len) - choices[choices[voted[usr.ckey]]]-- - voted[usr.ckey] = vote - choices[choices[vote]]++ - return vote + if(vote && ISINRANGE(vote, 1, choices.len)) + switch(vote_system) + if(PLURALITY_VOTING) + if(usr.ckey in voted) + choices[choices[voted[usr.ckey]]]-- + voted[usr.ckey] = vote + choices[choices[vote]]++ + return vote + else + voted += usr.ckey + voted[usr.ckey] = vote + choices[choices[vote]]++ //check this + return vote + if(APPROVAL_VOTING) + if(usr.ckey in voted) + if(vote in voted[usr.ckey]) + voted[usr.ckey] -= vote + choices[choices[vote]]-- + else + voted[usr.ckey] += vote + choices[choices[vote]]++ + else + voted += usr.ckey + voted[usr.ckey] = list(vote) + choices[choices[vote]]++ + return vote + if(RANKED_CHOICE_VOTING) + if(usr.ckey in voted) + if(vote in voted[usr.ckey]) + voted[usr.ckey] -= vote + else + voted += usr.ckey + voted[usr.ckey] = list() + voted[usr.ckey] += vote + saved -= usr.ckey + if(SCORE_VOTING) + if(!(usr.ckey in voted)) + voted += usr.ckey + voted[usr.ckey] = list() + voted[usr.ckey][choices[vote]] = score + saved -= usr.ckey return 0 -/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, hideresults)//CIT CHANGE - adds hideresults argument to votes to allow for obfuscated votes +/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, hideresults, votesystem = PLURALITY_VOTING, forced = FALSE,vote_time = -1)//CIT CHANGE - adds hideresults argument to votes to allow for obfuscated votes + vote_system = votesystem if(!mode) if(started_time) var/next_allowed_time = (started_time + CONFIG_GET(number/vote_delay)) @@ -257,11 +383,17 @@ SUBSYSTEM_DEF(vote) if("roundtype") //CIT CHANGE - adds the roundstart secret/extended vote choices.Add("secret", "extended") if("dynamic") - var/saved_threats = SSpersistence.saved_threat_levels - if((saved_threats[1]+saved_threats[2]+saved_threats[3])>150) - choices.Add("extended",PEACE,CHAOS) - else - choices.Add(PEACE,CHAOS) + for(var/T in config.storyteller_cache) + var/datum/dynamic_storyteller/S = T + var/recent_rounds = 0 + for(var/i in 1 to SSpersistence.saved_storytellers.len) + if(SSpersistence.saved_storytellers[i] == initial(S.name)) + recent_rounds++ + if(recent_rounds < initial(S.weight)) + choices.Add(initial(S.name)) + choice_descs.Add(initial(S.desc)) + choices.Add("Secret") + choice_descs.Add("Standard secret. Switches mode if it wins.") if("custom") question = stripped_input(usr,"What is the vote for?") if(!question) @@ -280,9 +412,11 @@ SUBSYSTEM_DEF(vote) if(mode == "custom") text += "\n[question]" log_vote(text) - var/vp = CONFIG_GET(number/vote_period) + var/vp = vote_time + if(vp == -1) + vp = CONFIG_GET(number/vote_period) to_chat(world, "\n[text]\nType vote or click here to place your votes.\nYou have [DisplayTimeText(vp)] to vote.") - time_remaining = round(vp/10) + end_time = started_time+vp for(var/c in GLOB.clients) SEND_SOUND(c, sound('sound/misc/server-ready.ogg')) var/client/C = c @@ -292,6 +426,11 @@ SUBSYSTEM_DEF(vote) C.player_details.player_actions += V V.Grant(C.mob) generated_actions += V + if(forced) + var/datum/browser/popup = new(C, "vote", "Voting Panel",nwidth=600,nheight=700) + popup.set_window_options("can_close=0") + popup.set_content(SSvote.interface(C)) + popup.open(0) return 1 return 0 @@ -311,14 +450,71 @@ SUBSYSTEM_DEF(vote) . += "

Vote: '[question]'

" else . += "

Vote: [capitalize(mode)]

" - . += "Time Left: [time_remaining] s

" + switch(vote_system) + if(PLURALITY_VOTING) + . += "

Vote one.

" + if(APPROVAL_VOTING) + . += "

Vote any number of choices.

" + if(RANKED_CHOICE_VOTING) + . += "

Vote by order of preference. Revoting will demote to the bottom. 1 is your favorite, and higher numbers are worse.

" + if(SCORE_VOTING) + . += "

Grade the candidates by how much you like them.

" + . += "

No-votes have no power--your opinion is only heard if you vote!

" + . += "Time Left: [DisplayTimeText(end_time-world.time)]

" + if(RANKED_CHOICE_VOTING) + var/list/myvote = voted[C.ckey] + for(var/i=1,i<=choices.len,i++) + var/vote = (myvote ? (myvote.Find(i)) : 0) + if(vote) + . += "
  • [choices[i]] ([vote])
  • " + else + . += "
  • [choices[i]]
  • " + if(choice_descs.len >= i) + . += "
  • [choice_descs[i]]
  • " + . += "
    " + if(!(C.ckey in saved)) + . += "(Save vote)" + else + . += "(Saved!)" + . += "(Load vote from save)" + . += "(Reset votes)" + if(SCORE_VOTING) + var/list/myvote = voted[C.ckey] + for(var/i=1,i<=choices.len,i++) + . += "
  • [choices[i]]" + for(var/r in 1 to GLOB.vote_score_options.len) + . += " " + if((choices[i] in myvote) && myvote[choices[i]] == r) + . +="([GLOB.vote_score_options[r]])" + else + . +="[GLOB.vote_score_options[r]]" + . += "" + . += "
  • " + if(choice_descs.len >= i) + . += "
  • [choice_descs[i]]
  • " + . += "
    " + if(!(C.ckey in saved)) + . += "(Save vote)" + else + . += "(Saved!)" + . += "(Load vote from save)" + . += "(Reset votes)" if(admin) . += "(Cancel Vote) " else @@ -376,8 +572,31 @@ SUBSYSTEM_DEF(vote) if("custom") if(usr.client.holder) initiate_vote("custom",usr.key) + if("reset") + if(usr.ckey in voted) + voted -= usr.ckey + if("save") + if(usr.ckey in voted) + if(!(usr.ckey in SSpersistence.saved_votes)) + SSpersistence.saved_votes[usr.ckey] = list() + SSpersistence.saved_votes[usr.ckey][mode] = voted[usr.ckey] + saved += usr.ckey + if("load") + if(!(usr.ckey in SSpersistence.saved_votes)) + SSpersistence.LoadSavedVote(usr.ckey) + if(!(usr.ckey in SSpersistence.saved_votes)) + SSpersistence.saved_votes[usr.ckey] = list() + if(usr.ckey in voted) + SSpersistence.saved_votes[usr.ckey][mode] = voted[usr.ckey] + else + SSpersistence.saved_votes[usr.ckey][mode] = list() + voted[usr.ckey] = SSpersistence.saved_votes[usr.ckey][mode] + saved += usr.ckey else - submit_vote(round(text2num(href_list["vote"]))) + if(vote_system == SCORE_VOTING) + submit_vote(round(text2num(href_list["vote"])),round(text2num(href_list["score"]))) + else + submit_vote(round(text2num(href_list["vote"]))) usr.vote() /datum/controller/subsystem/vote/proc/remove_action_buttons() @@ -392,7 +611,7 @@ SUBSYSTEM_DEF(vote) set category = "OOC" set name = "Vote" - var/datum/browser/popup = new(src, "vote", "Voting Panel") + var/datum/browser/popup = new(src, "vote", "Voting Panel",nwidth=600,nheight=700) popup.set_window_options("can_close=0") popup.set_content(SSvote.interface(client)) popup.open(0) @@ -419,6 +638,3 @@ SUBSYSTEM_DEF(vote) var/datum/player_details/P = GLOB.player_details[owner.ckey] if(P) P.player_actions -= src - -#undef PEACE -#undef CHAOS diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index f0fca5db5a..599c492d4c 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -136,9 +136,10 @@ /datum/emote/sound var/sound //Sound to play when emote is called var/vary = FALSE //used for the honk borg emote + var/volume = 50 mob_type_allowed_typecache = list(/mob/living/brain, /mob/living/silicon) /datum/emote/sound/run_emote(mob/user, params) . = ..() if(.) - playsound(user.loc, sound, 50, vary) + playsound(user.loc, sound, volume, vary) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 773c12b6af..36b701aae1 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -47,7 +47,7 @@ /atom/New(loc, ...) //atom creation method that preloads variables at creation if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() - GLOB._preloader.load(src) + world.preloader_load(src) if(datum_flags & DF_USE_TAG) GenerateTag() diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 0890d81dfb..821c819664 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -1,14 +1,3 @@ -#define CURRENT_LIVING_PLAYERS 1 -#define CURRENT_LIVING_ANTAGS 2 -#define CURRENT_DEAD_PLAYERS 3 -#define CURRENT_OBSERVERS 4 - -#define ONLY_RULESET 1 -#define HIGHLANDER_RULESET 2 -#define TRAITOR_RULESET 4 -#define MINOR_RULESET 8 - -#define RULESET_STOP_PROCESSING 1 // -- Injection delays GLOBAL_VAR_INIT(dynamic_latejoin_delay_min, (10 MINUTES)) @@ -52,6 +41,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_roundstart_ruleset) // Forced threat level, setting this to zero or higher forces the roundstart threat to the value. GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) +GLOBAL_VAR_INIT(dynamic_storyteller_type, null) + /datum/game_mode/dynamic name = "dynamic mode" config_tag = "dynamic" @@ -60,7 +51,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) announce_text = "Dynamic mode!" // This needs to be changed maybe reroll_friendly = FALSE; - + // Current storyteller + var/datum/dynamic_storyteller/storyteller = null // Threat logging vars /// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations var/threat_level = 0 @@ -164,6 +156,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) dat += "Threat to Spend: [threat] \[Adjust\] \[View Log\]
    " dat += "
    " + dat += "Storyteller: [storyteller.name]
    " dat += "Parameters: centre = [GLOB.dynamic_curve_centre] ; width = [GLOB.dynamic_curve_width].
    " dat += "On average, [peaceful_percentage]% of the rounds are more peaceful.
    " dat += "Forced extended: [GLOB.dynamic_forced_extended ? "On" : "Off"]
    " @@ -178,7 +171,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) dat += "[DR.ruletype] - [DR.name]
    " else dat += "none.
    " - dat += "
    Injection Timers: ([get_injection_chance(TRUE)]% chance)
    " + dat += "
    Injection Timers: ([storyteller.get_injection_chance(TRUE)]% chance)
    " dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)] seconds"] \[Now!\]
    " dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)] seconds"] \[Now!\]
    " dat += "Event: [(event_injection_cooldown-world.time)>60*10 ? "[round((event_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(event_injection_cooldown-world.time)] seconds"] \[Now!\]
    " @@ -336,6 +329,9 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) SSblackbox.record_feedback("tally","dynamic_threat",peaceful_percentage,"Percent of same-vote rounds that are more peaceful") /datum/game_mode/dynamic/can_start() + storyteller = new GLOB.dynamic_storyteller_type // this is where all the initialization happens + storyteller.on_start() + SSblackbox.record_feedback("text","dynamic_storyteller",1,storyteller.name) message_admins("Dynamic mode parameters for the round:") message_admins("Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].") message_admins("Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].") @@ -345,19 +341,12 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) if(GLOB.dynamic_forced_threat_level >= 0) threat_level = round(GLOB.dynamic_forced_threat_level, 0.1) threat = threat_level - SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced by admins)") + SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced)") else generate_threat() - var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min) - latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time + storyteller.start_injection_cooldowns() - var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max) - midround_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time - - var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min) - event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time) - log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!") initial_threat_level = threat_level return TRUE @@ -391,7 +380,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) log_game("DYNAMIC: [roundstart_rules.len] rules.") return TRUE SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Roundstart rules considered") - SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Players readied up") + SSblackbox.record_feedback("tally","dynamic",roundstart_pop_ready,"Players readied up") if(GLOB.dynamic_forced_roundstart_ruleset.len > 0) rigged_roundstart() else @@ -429,13 +418,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) if (GLOB.dynamic_forced_extended) log_game("DYNAMIC: Starting a round of forced extended.") return TRUE - var/list/drafted_rules = list() - for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules) - if (rule.acceptable(roundstart_pop_ready, threat_level) && threat >= rule.cost) // If we got the population and threat required - rule.candidates = candidates.Copy() - rule.trim_candidates() - if (rule.ready() && rule.candidates.len > 0) - drafted_rules[rule] = rule.weight + var/list/drafted_rules = storyteller.roundstart_draft() if(!drafted_rules.len) message_admins("Not enough threat level for roundstart antags!") log_game("DYNAMIC: Not enough threat level for roundstart antags!") @@ -653,7 +636,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) if (rule.persistent) current_rules += rule return TRUE - rule.clean_up() stack_trace("The [rule.ruletype] rule \"[rule.name]\" failed to execute.") return FALSE @@ -667,62 +649,41 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) current_rules -= rule SSblackbox.record_feedback("tally","dynamic",1,"Rulesets finished") SSblackbox.record_feedback("associative","dynamic_rulesets_finished",1,rule.get_blackbox_info()) - + + storyteller.do_process() + if (midround_injection_cooldown < world.time) if (GLOB.dynamic_forced_extended) return // Somehow it managed to trigger midround multiple times so this was moved here. // There is no way this should be able to trigger an injection twice now. - var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min) - midround_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max)) + world.time) + midround_injection_cooldown = storyteller.get_midround_cooldown() + world.time // Time to inject some threat into the round if(EMERGENCY_ESCAPED_OR_ENDGAMED) // Unless the shuttle is gone return + if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) // no rules after shuttle is auto-called + return message_admins("DYNAMIC: Checking for midround injection.") log_game("DYNAMIC: Checking for midround injection.") update_playercounts() - if (get_injection_chance()) + if (prob(storyteller.get_injection_chance())) SSblackbox.record_feedback("tally","dynamic",1,"Attempted midround injections") - var/cur_threat_frac = threat/threat_level - var/list/drafted_rules = list() - var/antag_num = current_players[CURRENT_LIVING_ANTAGS].len - for (var/datum/dynamic_ruleset/midround/rule in midround_rules) - // if there are antags OR the rule is an antag rule, antag_acceptable will be true. - if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost) - // Classic secret : only autotraitor/minor roles - if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))) - continue - rule.trim_candidates() - if (rule.ready()) - if(!antag_num) - drafted_rules[rule] = round(rule.get_weight() + (rule.cost * cur_threat_frac)) - else - drafted_rules[rule] = rule.get_weight() - else if(threat < rule.cost) - SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend") + var/list/drafted_rules = storyteller.midround_draft() if (drafted_rules.len > 0) SSblackbox.record_feedback("tally","dynamic",1,"Successful midround injections") picking_midround_latejoin_rule(drafted_rules) - else - midround_injection_cooldown = (midround_injection_cooldown + world.time)/2 + // get_injection_chance can do things on fail if(event_injection_cooldown < world.time) SSblackbox.record_feedback("tally","dynamic",1,"Attempted event injections") - var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min) - event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time) + event_injection_cooldown = storyteller.get_event_cooldown() + world.time message_admins("DYNAMIC: Doing event injection.") log_game("DYNAMIC: Doing event injection.") update_playercounts() - var/list/drafted_rules = list() - for(var/datum/dynamic_ruleset/event/rule in events) - if(rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost) - if(rule.ready()) - drafted_rules[rule] = rule.get_weight() - else if(threat < rule.cost) - SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend") + var/list/drafted_rules = storyteller.event_draft() if(drafted_rules.len > 0) SSblackbox.record_feedback("tally","dynamic",1,"Successful event injections") picking_midround_latejoin_rule(drafted_rules) @@ -748,31 +709,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) continue current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow) -/// Gets the chance for latejoin and midround injection, the dry_run argument is only used for forced injection. -/datum/game_mode/dynamic/proc/get_injection_chance(dry_run = FALSE) - if(forced_injection) - forced_injection = !dry_run - return 100 - var/chance = 0 - // If the high pop override is in effect, we reduce the impact of population on the antag injection chance - var/high_pop_factor = (current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit) - var/max_pop_per_antag = max(5,15 - round(threat_level/10) - round(current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5))) - if (!current_players[CURRENT_LIVING_ANTAGS].len) - chance += 80 // No antags at all? let's boost those odds! - else - var/current_pop_per_antag = current_players[CURRENT_LIVING_PLAYERS].len / current_players[CURRENT_LIVING_ANTAGS].len - if (current_pop_per_antag > max_pop_per_antag) - chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag)) - else - chance += 25-10*(max_pop_per_antag-current_pop_per_antag) - if (current_players[CURRENT_DEAD_PLAYERS].len > current_players[CURRENT_LIVING_PLAYERS].len) - chance -= 30 // More than half the crew died? ew, let's calm down on antags - if (threat > 70) - chance += 15 - if (threat < 30) - chance -= 15 - return round(max(0,chance)) - /// Removes type from the list /datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type) for(var/I in type_list) @@ -803,7 +739,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) return if(EMERGENCY_ESCAPED_OR_ENDGAMED) // No more rules after the shuttle has left return - + if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) // no rules after shuttle is auto-called + return update_playercounts() if (forced_latejoin_rule) @@ -814,28 +751,12 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) picking_midround_latejoin_rule(list(forced_latejoin_rule), forced = TRUE) forced_latejoin_rule = null - else if (latejoin_injection_cooldown < world.time && prob(get_injection_chance())) + else if (latejoin_injection_cooldown < world.time && prob(storyteller.get_injection_chance())) SSblackbox.record_feedback("tally","dynamic",1,"Attempted latejoin injections") - var/list/drafted_rules = list() - for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules) - if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost) - // Classic secret : only autotraitor/minor roles - if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))) - continue - // No stacking : only one round-ender, unless threat level > stacking_limit. - if (threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking) - if(rule.flags & HIGHLANDER_RULESET && highlander_executed) - continue - - rule.candidates = list(newPlayer) - rule.trim_candidates() - if (rule.ready()) - drafted_rules[rule] = rule.get_weight() - + var/list/drafted_rules = storyteller.latejoin_draft(newPlayer) if (drafted_rules.len > 0 && picking_midround_latejoin_rule(drafted_rules)) SSblackbox.record_feedback("tally","dynamic",1,"Successful latejoin injections") - var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min) - latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + world.time + latejoin_injection_cooldown = storyteller.get_latejoin_cooldown() + world.time /// Refund threat, but no more than threat_level. /datum/game_mode/dynamic/proc/refund_threat(regain) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm index 7f6ea088a1..2f143f7233 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm @@ -80,9 +80,13 @@ /// Delay for when execute will get called from the time of post_setup (roundstart) or process (midround/latejoin). /// Make sure your ruleset works with execute being called during the game when using this, and that the clean_up proc reverts it properly in case of faliure. var/delay = 0 + /// List of tags for use in storytellers. + var/list/property_weights = list() /// Whether or not recent-round weight values are taken into account for this ruleset. /// Weight reduction uses the same values as secret's recent-round mode weight reduction. var/always_max_weight = FALSE + /// Weight reduction by recent-rounds. Saved on new. + var/weight_mult = 1 /datum/dynamic_ruleset/New() ..() @@ -96,13 +100,11 @@ var/high_population_requirements = CONFIG_GET(keyed_list/dynamic_high_population_requirement) var/list/repeated_mode_adjust = CONFIG_GET(number_list/repeated_mode_adjust) if(config_tag in weights) - var/weight_mult = 1 if(!always_max_weight && SSpersistence.saved_dynamic_rules.len == 3 && repeated_mode_adjust.len == 3) var/saved_dynamic_rules = SSpersistence.saved_dynamic_rules for(var/i in 1 to 3) if(config_tag in saved_dynamic_rules[i]) weight_mult -= (repeated_mode_adjust[i]/100) - weight = weights[config_tag] * weight_mult if(config_tag in costs) cost = costs[config_tag] if(config_tag in requirementses) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm index b6cd313bfc..23e94c4065 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_events.dm @@ -58,6 +58,7 @@ cost = 10 blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear) requirements = list(70,60,50,50,40,40,40,30,20,15) + property_weights = list("story_potential" = 1, "trust" = 1, "chaos" = 1) high_population_requirement = 15 /datum/dynamic_ruleset/event/pirates/ready(forced = FALSE) @@ -81,6 +82,7 @@ cost = 10 requirements = list(70,60,50,50,40,40,40,30,20,15) high_population_requirement = 15 + property_weights = list("chaos" = 1, "valid" = 1) ////////////////////////////////////////////// // // @@ -100,6 +102,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) // yes, can happen on fake-extended high_population_requirement = 5 repeatable = TRUE + property_weights = list("chaos" = 1, "extended" = 2) /datum/dynamic_ruleset/event/ventclog/ready() if(mode.threat_level > 30 && mode.threat >= 5 && prob(20)) @@ -133,10 +136,11 @@ required_enemies = list(1,1,0,0,0,0,0,0,0,0) weight = 4 // no repeatable weight decrease. too variable to be unfun multiple times in one round - cost = 3 + cost = 1 requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("story_potential" = 1, "extended" = 1) always_max_weight = TRUE ////////////////////////////////////////////// @@ -156,6 +160,7 @@ repeatable_weight_decrease = 2 requirements = list(60,50,40,30,30,30,30,30,30,30) high_population_requirement = 30 + property_weights = list("extended" = -2) /datum/dynamic_ruleset/event/meteor_wave/ready() if(mode.threat_level > 40 && mode.threat >= 25 && prob(20)) @@ -190,6 +195,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/anomaly_flux name = "Anomaly: Hyper-Energetic Flux" @@ -203,6 +209,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/anomaly_gravitational name = "Anomaly: Gravitational" @@ -214,6 +221,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/anomaly_pyroclastic name = "Anomaly: Pyroclastic" @@ -227,6 +235,7 @@ requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/anomaly_vortex name = "Anomaly: Vortex" @@ -240,6 +249,7 @@ requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = 1) ////////////////////////////////////////////// // // @@ -259,6 +269,7 @@ requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = -1, "chaos" = 1) /datum/dynamic_ruleset/event/carp_migration name = "Carp Migration" @@ -270,6 +281,7 @@ requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/communications_blackout name = "Communications Blackout" @@ -283,6 +295,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1, "chaos" = 1) /datum/dynamic_ruleset/event/processor_overload name = "Processor Overload" @@ -296,6 +309,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1, "chaos" = 1) always_max_weight = TRUE /datum/dynamic_ruleset/event/space_dust @@ -310,6 +324,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1) always_max_weight = TRUE /datum/dynamic_ruleset/event/major_dust @@ -324,6 +339,7 @@ requirements = list(10,10,10,10,10,10,10,10,10,10) high_population_requirement = 10 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/electrical_storm name = "Electrical Storm" @@ -337,6 +353,7 @@ requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1) /datum/dynamic_ruleset/event/heart_attack name = "Random Heart Attack" @@ -350,6 +367,7 @@ requirements = list(101,101,101,5,5,5,5,5,5,5) high_population_requirement = 5 repeatable = TRUE + property_weights = list("extended" = 1) always_max_weight = TRUE /datum/dynamic_ruleset/event/radiation_storm @@ -362,3 +380,4 @@ required_enemies = list(1,1,1,1,1,1,1,1,1,1) requirements = list(5,5,5,5,5,5,5,5,5,5) high_population_requirement = 5 + property_weights = list("extended" = 1,"chaos" = 1) diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index fe9347d9d5..f4e9971a05 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -69,8 +69,15 @@ high_population_requirement = 15 repeatable = TRUE flags = TRAITOR_RULESET + property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1) always_max_weight = TRUE +/datum/dynamic_ruleset/latejoin/infiltrator/execute() + . = ..() + for(var/datum/mind/M in assigned) + log_admin("[M.name] was made into a traitor by dynamic.") + message_admins("[M.name] was made into a traitor by dynamic.") + ////////////////////////////////////////////// // // // REVOLUTIONARY PROVOCATEUR // @@ -94,6 +101,7 @@ requirements = list(101,101,70,40,40,40,40,40,40,40) high_population_requirement = 40 flags = HIGHLANDER_RULESET + property_weights = list("trust" = -2, "chaos" = 2, "extended" = -2, "valid" = 2, "conversion" = 1) var/required_heads_of_staff = 3 var/finished = FALSE var/datum/team/revolution/revolution @@ -123,6 +131,8 @@ revolution.update_objectives() revolution.update_heads() SSshuttle.registerHostileEnvironment(src) + log_admin("[M.name] was made into a revolutionary by dynamic.") + message_admins("[M.name] was made into a revolutionary by dynamic.") return TRUE else log_game("DYNAMIC: [ruletype] [name] discarded [M.name] from head revolutionary due to ineligibility.") @@ -187,30 +197,31 @@ ////////////////////////////////////////////// // // -// VAMPIRE // +// BLOODSUCKERS // // // ////////////////////////////////////////////// -/* -/datum/dynamic_ruleset/latejoin/vampire - name = "vampire" - config_tag = "vampire_latejoin" - antag_flag = ROLE_VAMPIRE - antag_datum = ANTAG_DATUM_VAMPIRE - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain") +/datum/dynamic_ruleset/latejoin/bloodsucker + name = "Bloodsucker Infiltrator" + config_tag = "latejoin_bloodsucker" + antag_datum = ANTAG_DATUM_BLOODSUCKER + antag_flag = ROLE_TRAITOR restricted_roles = list("AI", "Cyborg") + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") required_candidates = 1 - weight = 5 - cost = 15 - requirements = list(80,70,60,50,40,20,20,15,15,15) + weight = 3 + cost = 10 + property_weights = list("story_potential" = 2, "extended" = 2, "trust" = -2, "valid" = 1) + requirements = list(70,65,60,55,50,45,40,35,30,30) + high_population_requirement = 30 repeatable = TRUE - high_population_requirement = 15 -/datum/dynamic_ruleset/latejoin/vampire/pre_execute() +/datum/dynamic_ruleset/latejoin/bloodsucker/execute() var/mob/M = pick(candidates) - candidates -= M assigned += M.mind - M.mind.restricted_roles = restricted_roles - M.mind.special_role = ROLE_VAMPIRE + M.mind.special_role = antag_flag + if(mode.make_bloodsucker(M.mind)) + mode.bloodsuckers += M + log_admin("[M.name] was made into a bloodsucker by dynamic.") + message_admins("[M.name] was made into a bloodsucker by dynamic.") return TRUE -*/ diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index 91ac73d142..c89562c9f9 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -108,8 +108,12 @@ candidates = pollGhostCandidates("The mode is looking for volunteers to become a [name]", antag_flag, SSticker.mode, antag_flag, poll_time = 300) - if(!candidates || candidates.len <= required_candidates) + if(!candidates || candidates.len < required_candidates) message_admins("The ruleset [name] did not receive enough applications.") + if(candidates) + message_admins("Only received [candidates.len], needed [required_candidates].") + else + message_admins("There were no candidates.") log_game("DYNAMIC: The ruleset [name] did not receive enough applications.") return FALSE @@ -180,6 +184,7 @@ repeatable = TRUE high_population_requirement = 15 flags = TRAITOR_RULESET + property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1) always_max_weight = TRUE /datum/dynamic_ruleset/midround/autotraitor/acceptable(population = 0, threat = 0) @@ -214,6 +219,8 @@ living_players -= M var/datum/antagonist/traitor/newTraitor = new M.mind.add_antag_datum(newTraitor) + log_admin("[M] was made into a traitor by dynamic.") + message_admins("[M] was made into a traitor by dynamic.") return TRUE @@ -237,6 +244,7 @@ requirements = list(101,101,70,50,50,50,40,30,30,30) high_population_requirement = 30 required_type = /mob/living/silicon/ai + property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2) var/ion_announce = 33 var/removeDontImproveChance = 10 @@ -261,6 +269,8 @@ var/datum/antagonist/traitor/AI = new M.mind.special_role = antag_flag M.mind.add_antag_datum(AI) + log_admin("[M] was made into a malf AI by dynamic.") + message_admins("[M] was made into a malf AI by dynamic.") if(prob(ion_announce)) priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", "ionstorm") if(prob(removeDontImproveChance)) @@ -289,6 +299,7 @@ requirements = list(90,90,70,50,50,50,50,40,30,30) high_population_requirement = 30 repeatable = TRUE + property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2, "extended" = -2) var/datum/mind/wizard /datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE) @@ -337,6 +348,7 @@ cost = 35 requirements = list(90,90,90,80,70,60,50,40,40,40) high_population_requirement = 40 + property_weights = list("story_potential" = 2, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2) var/operative_cap = list(2,2,3,3,4,5,5,5,5,5) var/datum/team/nuclear/nuke_team flags = HIGHLANDER_RULESET @@ -390,6 +402,7 @@ requirements = list(101,101,101,80,60,50,50,50,50,50) high_population_requirement = 50 repeatable = TRUE + property_weights = list("story_potential" = -1, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2) /datum/dynamic_ruleset/midround/from_ghosts/blob/ready(forced = FALSE) if (required_candidates > (dead_players.len + list_observers.len)) @@ -421,6 +434,7 @@ high_population_requirement = 50 repeatable_weight_decrease = 2 repeatable = TRUE + property_weights = list("story_potential" = -1, "trust" = 1, "chaos" = 2, "extended" = -2, "valid" = 2) var/list/vents = list() /datum/dynamic_ruleset/midround/from_ghosts/xenomorph/ready(forced = FALSE) @@ -476,6 +490,7 @@ high_population_requirement = 50 repeatable_weight_decrease = 2 repeatable = TRUE + property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 2) var/list/spawn_locs = list() /datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute() @@ -521,6 +536,7 @@ weight = 4 cost = 5 requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"! + property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2) high_population_requirement = 5 /datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/ready(forced = FALSE) @@ -555,14 +571,18 @@ cost = 5 requirements = list(30,30,30,30,20,15,15,15,15,15) high_population_requirement = 15 + property_weights = list("story_potential" = -2, "extended" = -1) var/list/spawn_locs = list() -/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE) +/datum/dynamic_ruleset/midround/from_ghosts/revenant/acceptable(population = 0,threat = 0) var/deadMobs = 0 for(var/mob/M in GLOB.dead_mob_list) deadMobs++ if(deadMobs < REVENANT_SPAWN_THRESHOLD) return FALSE + return ..() + +/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE) if(required_candidates > (dead_players.len + list_observers.len)) SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts") return FALSE @@ -607,6 +627,7 @@ weight = 4 cost = 15 requirements = list(101,101,101,90,80,70,60,50,40,30) + property_weights = list("story_potential" = -2, "extended" = -2, "integrity" = 2, "valid" = 2, "trust" = 2) high_population_requirement = 30 var/list/spawn_locs = list() @@ -659,6 +680,7 @@ blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear) high_population_requirement = 15 var/datum/team/abductor_team/team + property_weights = list("story_potential" = 1, "extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2) repeatable_weight_decrease = 4 repeatable = TRUE @@ -699,6 +721,7 @@ cost = 15 requirements = list(101,101,101,90,80,70,60,50,40,30) high_population_requirement = 30 + property_weights = list("story_potential" = 1, "extended" = -2, "valid" = 2) var/list/spawn_locs = list() var/spawn_loc @@ -745,31 +768,3 @@ #undef ABDUCTOR_MAX_TEAMS #undef REVENANT_SPAWN_THRESHOLD - -////////////////////////////////////////////// -// // -// BLOODSUCKERS // -// // -////////////////////////////////////////////// - -/datum/dynamic_ruleset/latejoin/bloodsucker - name = "Bloodsucker Infiltrator" - config_tag = "latejoin_bloodsucker" - antag_datum = ANTAG_DATUM_BLOODSUCKER - antag_flag = ROLE_TRAITOR - restricted_roles = list("AI", "Cyborg") - protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") - required_candidates = 1 - weight = 3 - cost = 10 - requirements = list(90,80,70,60,55,50,45,40,35,30) - high_population_requirement = 30 - repeatable = TRUE - -/datum/dynamic_ruleset/latejoin/bloodsucker/execute() - var/mob/M = pick(candidates) - assigned += M.mind - M.mind.special_role = antag_flag - if(mode.make_bloodsucker(M.mind)) - mode.bloodsuckers += M - return TRUE diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index e5bb0af464..abd40a1756 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -21,6 +21,7 @@ requirements = list(50,50,50,50,50,50,50,50,50,50) high_population_requirement = 40 antag_cap = list(1,1,1,1,2,2,2,2,3,3) + property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1, "valid" = 1) always_max_weight = TRUE var/autotraitor_cooldown = 450 // 15 minutes (ticks once per 2 sec) @@ -61,6 +62,7 @@ requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 antag_cap = list(2,2,2,2,2,2,2,2,2,2) // Can pick 3 per team, but rare enough it doesn't matter. + property_weights = list("story_potential" = 1, "trust" = -1, "extended" = 1, "valid" = 1) var/list/datum/team/brother_team/pre_brother_teams = list() var/const/min_team_size = 2 @@ -108,6 +110,7 @@ cost = 15 scaling_cost = 15 requirements = list(101,101,101,101,101,101,101,101,101,101) + property_weights = list("trust" = -2, "valid" = 2) high_population_requirement = 10 antag_cap = list(1,1,1,1,1,2,2,2,2,3) var/team_mode_probability = 30 @@ -160,6 +163,7 @@ cost = 30 requirements = list(101,101,101,60,50,50,50,50,50,50) high_population_requirement = 50 + property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2, "extended" = -2, "valid" = 2) var/list/roundstart_wizards = list() /datum/dynamic_ruleset/roundstart/wizard/acceptable(population=0, threat=0) @@ -222,6 +226,7 @@ weight = 3 cost = 30 requirements = list(101,101,101,80,70,60,50,50,50,50) + property_weights = list("story_potential" = -1, "trust" = -1, "chaos" = 1, "conversion" = 1, "extended" = -2, "valid" = 2) high_population_requirement = 50 flags = HIGHLANDER_RULESET antag_cap = list(2,2,2,3,3,4,4,4,4,4) @@ -283,6 +288,7 @@ high_population_requirement = 50 flags = HIGHLANDER_RULESET antag_cap = list(1,1,2,3,4,5,5,5,5,5) + property_weights = list("story_potential" = 2, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2) var/datum/team/nuclear/nuke_team /datum/dynamic_ruleset/roundstart/nuclear/ready(forced = FALSE) @@ -373,6 +379,7 @@ flags = HIGHLANDER_RULESET // I give up, just there should be enough heads with 35 players... minimum_players = 35 + property_weights = list("trust" = -2, "chaos" = 2, "extended" = -2, "valid" = 2, "conversion" = 1) var/datum/team/revolution/revolution var/finished = FALSE @@ -490,6 +497,7 @@ weight = 3 cost = 0 requirements = list(101,101,101,101,101,101,101,101,101,101) + property_weights = list("extended" = 2) high_population_requirement = 101 /datum/dynamic_ruleset/roundstart/extended/pre_execute() @@ -517,6 +525,7 @@ high_population_requirement = 50 flags = HIGHLANDER_RULESET antag_cap = list(2,3,3,4,4,4,4,4,4,4) + property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "conversion" = 1, "valid" = 2) var/ark_time /datum/dynamic_ruleset/roundstart/clockcult/pre_execute() @@ -616,6 +625,8 @@ antag_leader_datum = /datum/antagonist/nukeop/leader/clownop requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 + property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "story_potential" = 2, "valid" = 2) + /datum/dynamic_ruleset/roundstart/nuclear/clown_ops/pre_execute() . = ..() @@ -647,6 +658,7 @@ requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 antag_cap = list(1,1,1,2,2,2,3,3,3,4) + property_weights = list("extended" = 1) /datum/dynamic_ruleset/roundstart/devil/pre_execute() var/num_devils = antag_cap[indice_pop] @@ -698,6 +710,7 @@ cost = 0 requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 + property_weights = list("extended" = -2, "chaos" = 2, "conversion" = 1, "valid" = 2) var/players_per_carrier = 30 var/monkeys_to_win = 1 var/escaped_monkeys = 0 @@ -759,6 +772,7 @@ cost = 0 requirements = list(101,101,101,101,101,101,101,101,101,101) high_population_requirement = 101 + property_weights = list("extended" = -2, "chaos" = 2, "trust" = 2) var/meteordelay = 2000 var/nometeors = 0 var/rampupdelta = 5 @@ -799,7 +813,8 @@ weight = 2 cost = 15 scaling_cost = 10 - requirements = list(90,80,70,60,50,50,50,50,50,50) + property_weights = list("story_potential" = 1, "extended" = 1, "trust" = -2, "valid" = 1) + requirements = list(70,65,60,55,50,50,50,50,50,50) high_population_requirement = 50 antag_cap = list(1,1,1,1,1,2,2,2,2,2) diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm new file mode 100644 index 0000000000..afa6ed8ba7 --- /dev/null +++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm @@ -0,0 +1,235 @@ +/datum/dynamic_storyteller + var/name = "none" + var/desc = "A coder's idiocy." + var/list/property_weights = list() + var/curve_centre = 0 + var/curve_width = 1.8 + var/forced_threat_level = -1 + var/flags = 0 + var/weight = 3 // how many rounds need to have been recently played for this storyteller to be left out of the vote + var/datum/game_mode/dynamic/mode = null + +/** +Property weights are: +"story_potential" -- essentially how many different ways the antag can be played. +"trust" -- How much it makes the crew trust each other. Negative values means they're suspicious. Team antags are like this. +"chaos" -- How chaotic it makes the round. Has some overlap with "valid" and somewhat contradicts "extended". +"valid" -- How likely the non-antag-enemy crew are to get involved, e.g. nukies encouraging the warden to + let everyone into the armory, wizard moving around and being a nuisance, nightmare busting lights. +"extended" -- How much the antag is conducive to a long round. Nukies and cults are bad for this; Wizard is less bad; and so on. +"conversion" -- Basically a bool. Conversion antags, well, convert. It's its own class for a good reason. +*/ + +/datum/dynamic_storyteller/New() + ..() + if (istype(SSticker.mode, /datum/game_mode/dynamic)) + mode = SSticker.mode + GLOB.dynamic_curve_centre = curve_centre + GLOB.dynamic_curve_width = curve_width + GLOB.dynamic_forced_threat_level = forced_threat_level + +/datum/dynamic_storyteller/proc/start_injection_cooldowns() + var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min) + mode.latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time + + var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max) + mode.midround_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time + + var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min) + mode.event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time) + +/datum/dynamic_storyteller/proc/do_process() + return + +/datum/dynamic_storyteller/proc/on_start() + return + +/datum/dynamic_storyteller/proc/get_midround_cooldown() + var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min) + return round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max)) + +/datum/dynamic_storyteller/proc/get_event_cooldown() + var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min) + return round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + +/datum/dynamic_storyteller/proc/get_latejoin_cooldown() + var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min) + return round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + +/datum/dynamic_storyteller/proc/get_injection_chance(dry_run = FALSE) + if(mode.forced_injection) + mode.forced_injection = !dry_run + return 100 + var/chance = 0 + // If the high pop override is in effect, we reduce the impact of population on the antag injection chance + var/high_pop_factor = (mode.current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit) + var/max_pop_per_antag = max(5,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5))) + if (!mode.current_players[CURRENT_LIVING_ANTAGS].len) + chance += 80 // No antags at all? let's boost those odds! + else + var/current_pop_per_antag = mode.current_players[CURRENT_LIVING_PLAYERS].len / mode.current_players[CURRENT_LIVING_ANTAGS].len + if (current_pop_per_antag > max_pop_per_antag) + chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag)) + else + chance += 25-10*(max_pop_per_antag-current_pop_per_antag) + if (mode.current_players[CURRENT_DEAD_PLAYERS].len > mode.current_players[CURRENT_LIVING_PLAYERS].len) + chance -= 30 // More than half the crew died? ew, let's calm down on antags + if (mode.threat > 70) + chance += 15 + if (mode.threat < 30) + chance -= 15 + return round(max(0,chance)) + +/datum/dynamic_storyteller/proc/roundstart_draft() + var/list/drafted_rules = list() + for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules) + if (rule.acceptable(mode.roundstart_pop_ready, mode.threat_level) && mode.threat >= rule.cost) // If we got the population and threat required + rule.candidates = mode.candidates.Copy() + rule.trim_candidates() + if (rule.ready() && rule.candidates.len > 0) + var/property_weight = 0 + for(var/property in property_weights) + if(property in rule.property_weights) // just treat it as 0 if it's not in there + property_weight += rule.property_weights[property] * property_weights[property] + drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult + return drafted_rules + +/datum/dynamic_storyteller/proc/midround_draft() + var/list/drafted_rules = list() + for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules) + // if there are antags OR the rule is an antag rule, antag_acceptable will be true. + if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost) + // Classic secret : only autotraitor/minor roles + if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))) + continue + rule.trim_candidates() + if (rule.ready()) + var/property_weight = 0 + for(var/property in property_weights) + if(property in rule.property_weights) + property_weight += rule.property_weights[property] * property_weights[property] + drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult + else if(mode.threat < rule.cost) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend") + return drafted_rules + +/datum/dynamic_storyteller/proc/latejoin_draft(mob/living/carbon/human/newPlayer) + var/list/drafted_rules = list() + for (var/datum/dynamic_ruleset/latejoin/rule in mode.latejoin_rules) + if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost) + // Classic secret : only autotraitor/minor roles + if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET))) + continue + // No stacking : only one round-ender, unless threat level > stacking_limit. + if (mode.threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking) + if(rule.flags & HIGHLANDER_RULESET && mode.highlander_executed) + continue + + rule.candidates = list(newPlayer) + rule.trim_candidates() + if (rule.ready()) + var/property_weight = 0 + for(var/property in property_weights) + if(property in rule.property_weights) + property_weight += rule.property_weights[property] * property_weights[property] + drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult + else if(mode.threat < rule.cost) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend") + return drafted_rules + +/datum/dynamic_storyteller/proc/event_draft() + var/list/drafted_rules = list() + for(var/datum/dynamic_ruleset/event/rule in mode.events) + if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost) + if(rule.ready()) + var/property_weight = 0 + for(var/property in property_weights) + if(property in rule.property_weights) + property_weight += rule.property_weights[property] * property_weights[property] + drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult + else if(mode.threat < rule.cost) + SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend") + return drafted_rules + + +/datum/dynamic_storyteller/cowabunga + name = "Chaotic" + curve_centre = 10 + desc = "Chaos: high. Variation: high. Likely antags: clock cult, revs, wizard." + property_weights = list("extended" = -1, "chaos" = 10) + weight = 2 + flags = WAROPS_ALWAYS_ALLOWED + var/refund_cooldown + +/datum/dynamic_storyteller/cowabunga/get_midround_cooldown() + return ..() / 4 + +/datum/dynamic_storyteller/cowabunga/get_latejoin_cooldown() + return ..() / 4 + +/datum/dynamic_storyteller/cowabunga/do_process() + if(refund_cooldown < world.time) + mode.refund_threat(10) + mode.log_threat("Cowabunga it is. Refunded 10 threat. Threat is now [mode.threat].") + refund_cooldown = world.time + 300 SECONDS + +/datum/dynamic_storyteller/team + name = "Teamwork" + desc = "Chaos: high. Variation: low. Likely antags: nukies, clockwork cult, wizard, blob, xenomorph." + curve_centre = 2 + curve_width = 1.5 + weight = 2 + flags = WAROPS_ALWAYS_ALLOWED + property_weights = list("valid" = 3, "trust" = 5) + +/datum/dynamic_storyteller/team/get_injection_chance(dry_run = FALSE) + return (mode.current_players[CURRENT_LIVING_ANTAGS].len ? 0 : ..()) + +/datum/dynamic_storyteller/conversion + name = "Conversion" + desc = "Chaos: high. Variation: medium. Likely antags: cults, bloodsuckers, revs." + curve_centre = 3 + curve_width = 1 + weight = 2 + flags = WAROPS_ALWAYS_ALLOWED + property_weights = list("valid" = 1, "conversion" = 20) + +/datum/dynamic_storyteller/classic + name = "Random" + desc = "Chaos: varies. Variation: highest. No special weights attached." + weight = 6 + curve_width = 4 + +/datum/dynamic_storyteller/memes + name = "Story" + desc = "Chaos: varies. Variation: high. Likely antags: abductors, nukies, wizard, traitor." + curve_width = 4 + property_weights = list("story_potential" = 10) + +/datum/dynamic_storyteller/suspicion + name = "Intrigue" + desc = "Chaos: low. Variation: high. Likely antags: traitor, bloodsucker. Rare: revs, blood cult." + curve_width = 4 + property_weights = list("trust" = -5) + +/datum/dynamic_storyteller/liteextended + name = "Calm" + desc = "Chaos: low. Variation: medium. Likely antags: bloodsuckers, traitors, sentient disease, revenant." + curve_centre = -5 + curve_width = 0.5 + flags = NO_ASSASSIN + weight = 2 + property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10) + +/datum/dynamic_storyteller/liteextended/get_injection_chance(dry_run = FALSE) + return ..()/2 + +/datum/dynamic_storyteller/extended + name = "Extended" + desc = "Chaos: none. Variation: none. Likely antags: none." + curve_centre = -20 + weight = 2 + curve_width = 0.5 + +/datum/dynamic_storyteller/extended/on_start() + GLOB.dynamic_forced_extended = TRUE diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 52b52eb288..03c1bb3bc8 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -180,7 +180,7 @@ limit-- while(!FoundDoor && limit) if (!FoundDoor) - log_world("### MAP WARNING, [src] at [AREACOORD(src)] failed to find a valid airlock to cyclelink with!") + log_mapping("[src] at [AREACOORD(src)] failed to find a valid airlock to cyclelink with!") return FoundDoor.cyclelinkedairlock = src cyclelinkedairlock = FoundDoor diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 1e7d720ee9..b1783ba940 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -500,7 +500,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ else return FALSE -/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf) +/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf, direction) if(LAZYLEN(masters) && masters[user]) var/obj/effect/overlay/holo_pad_hologram/holo = masters[user] var/transfered = FALSE @@ -512,6 +512,8 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ transfered = TRUE //All is good. holo.forceMove(new_turf) + if(direction) + holo.setDir(direction) if(!transfered) update_holoray(user,new_turf) return TRUE diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm index 36708fcf90..ca8e8cecd9 100644 --- a/code/game/mecha/equipment/tools/work_tools.dm +++ b/code/game/mecha/equipment/tools/work_tools.dm @@ -262,14 +262,14 @@ occupant_message("Deconstructing [W]...") if(do_after_cooldown(W)) chassis.spark_system.start() - W.ScrapeAway() + W.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) playsound(W, 'sound/items/deconstruct.ogg', 50, 1) else if(isfloorturf(target)) var/turf/open/floor/F = target occupant_message("Deconstructing [F]...") if(do_after_cooldown(target)) chassis.spark_system.start() - F.ScrapeAway() + F.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) playsound(F, 'sound/items/deconstruct.ogg', 50, 1) else if (istype(target, /obj/machinery/door/airlock)) occupant_message("Deconstructing [target]...") @@ -282,7 +282,7 @@ var/turf/open/space/S = target occupant_message("Building Floor...") if(do_after_cooldown(S)) - S.PlaceOnTop(/turf/open/floor/plating) + S.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) playsound(S, 'sound/items/deconstruct.ogg', 50, 1) chassis.spark_system.start() else if(isfloorturf(target)) diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index 9c3df5395f..416644cada 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -107,7 +107,7 @@ //Wrapper procs that handle sanity and user feedback /atom/movable/proc/user_buckle_mob(mob/living/M, mob/user, check_loc = TRUE) - if(!in_range(user, src) || !isturf(user.loc) || user.incapacitated() || M.anchored) + if(!in_range(user, src) || !isturf(user.loc) || user.incapacitated() || M.anchored || !user.can_buckle_others(M, src)) return FALSE add_fingerprint(user) diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm index 10ee73915c..b6deaa1d06 100644 --- a/code/game/objects/effects/effect_system/effects_foam.dm +++ b/code/game/objects/effects/effect_system/effects_foam.dm @@ -123,7 +123,7 @@ if(metal) var/turf/T = get_turf(src) if(isspaceturf(T)) //Block up any exposed space - T.PlaceOnTop(/turf/open/floor/plating/foam) + T.PlaceOnTop(/turf/open/floor/plating/foam, flags = CHANGETURF_INHERIT_AIR) for(var/direction in GLOB.cardinals) var/turf/cardinal_turf = get_step(T, direction) if(get_area(cardinal_turf) != get_area(T)) //We're at an area boundary, so let's block off this turf! diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index 7dc2f1d60b..3ee4a14a91 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -8,7 +8,8 @@ w_class = WEIGHT_CLASS_SMALL slot_flags = ITEM_SLOT_BELT var/mob/living/silicon/pai/pai - resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE + resistance_flags = FIRE_PROOF | ACID_PROOF + max_integrity = 200 /obj/item/paicard/suicide_act(mob/living/user) user.visible_message("[user] is staring sadly at [src]! [user.p_they()] can't keep living without real human intimacy!") @@ -45,6 +46,8 @@ dat += "Radio Uplink
    " dat += "Transmit: [(pai.radio.wires.is_cut(WIRE_TX)) ? "Disabled" : "Enabled"]
    " dat += "Receive: [(pai.radio.wires.is_cut(WIRE_RX)) ? "Disabled" : "Enabled"]
    " + if(pai.radio_short) + dat += "Reset radio short: \[RESET\]
    " else dat += "Radio Uplink
    " dat += "Radio firmware not loaded. Please install a pAI personality to load firmware.
    " @@ -82,7 +85,6 @@ pai.master = M.real_name pai.master_dna = M.dna.unique_enzymes to_chat(pai, "You have been bound to a new master.") - pai.emittersemicd = FALSE if(href_list["wipe"]) var/confirm = input("Are you CERTAIN you wish to delete the current personality? This action cannot be undone.", "Personality Wipe") in list("Yes", "No") if(confirm == "Yes") @@ -96,6 +98,8 @@ var/wire = text2num(href_list["wires"]) if(pai.radio) pai.radio.wires.cut(wire) + if(href_list["reset_radio_short"]) + pai.unshort_radio() if(href_list["setlaws"]) var/newlaws = copytext(sanitize(input("Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.laws.supplied[1]) as message),1,MAX_MESSAGE_LEN) if(newlaws && pai) diff --git a/code/game/objects/items/grenades/flashbang.dm b/code/game/objects/items/grenades/flashbang.dm index 66897fa722..724cfc8f9d 100644 --- a/code/game/objects/items/grenades/flashbang.dm +++ b/code/game/objects/items/grenades/flashbang.dm @@ -20,10 +20,10 @@ /obj/item/grenade/flashbang/proc/flashbang_mobs(turf/source, range) var/list/banged = get_hearers_in_view(range, source) var/list/flashed = viewers(range, source) - for(var/i in banged) - bang(i, source) - for(var/i in flashed) - flash(i, source) + for(var/mob/living/l in banged) + bang(l, source) + for(var/mob/living/l in flashed) + flash(l, source) /obj/item/grenade/flashbang/proc/bang(mob/living/M, turf/source) if(M.stat == DEAD) //They're dead! diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 4fc31ea674..34146d89b3 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -673,7 +673,8 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \ merge_type = /obj/item/stack/sheet/bone GLOBAL_LIST_INIT(plastic_recipes, list( - new /datum/stack_recipe("plastic flaps", /obj/structure/plasticflaps, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \ + new /datum/stack_recipe("see-through plastic flaps", /obj/structure/plasticflaps, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \ + new /datum/stack_recipe("opaque plastic flaps", /obj/structure/plasticflaps/opaque, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \ new /datum/stack_recipe("water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/empty), \ new /datum/stack_recipe("large water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty,3), \ new /datum/stack_recipe("large trash cart", /obj/structure/closet/crate/bin,50),\ diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index e8bd50f19b..4180681754 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -192,9 +192,15 @@ var/obj/O if(R.max_res_amount > 1) //Is it a stack? O = new R.result_type(usr.drop_location(), R.res_amount * multiplier) + else if(ispath(R.result_type, /turf)) + var/turf/T = usr.drop_location() + if(!isturf(T)) + return + T.PlaceOnTop(R.result_type, flags = CHANGETURF_INHERIT_AIR) else O = new R.result_type(usr.drop_location()) - O.setDir(usr.dir) + if(O) + O.setDir(usr.dir) use(R.req_amount * multiplier) //START: oh fuck i'm so sorry @@ -439,4 +445,4 @@ /datum/stack_recipe_list/New(title, recipes) src.title = title - src.recipes = recipes \ No newline at end of file + src.recipes = recipes diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm index b5c8f6b631..6972587263 100644 --- a/code/game/objects/items/stunbaton.dm +++ b/code/game/objects/items/stunbaton.dm @@ -36,7 +36,7 @@ . = ..() if(preload_cell_type) if(!ispath(preload_cell_type,/obj/item/stock_parts/cell)) - log_world("### MAP WARNING, [src] at [AREACOORD(src)] had an invalid preload_cell_type: [preload_cell_type].") + log_mapping("[src] at [AREACOORD(src)] had an invalid preload_cell_type: [preload_cell_type].") else cell = new preload_cell_type(src) update_icon() diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index dd7d46d0d7..d7891feaa0 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -872,7 +872,7 @@ user.visible_message("[user] blasts \the [target] with \the [src]!") playsound(target, 'sound/magic/disintegrate.ogg', 100, 1) W.break_wall() - W.ScrapeAway() + W.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) return //HF blade diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index b946a06432..a7eaea0d16 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -60,7 +60,7 @@ to_chat(user, "You build a floor.") var/turf/T = src.loc if(isspaceturf(T)) - T.PlaceOnTop(/turf/open/floor/plating) + T.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) qdel(src) return TRUE return FALSE diff --git a/code/game/objects/structures/stairs.dm b/code/game/objects/structures/stairs.dm index bd657fe1e8..f2c4e628e0 100644 --- a/code/game/objects/structures/stairs.dm +++ b/code/game/objects/structures/stairs.dm @@ -102,13 +102,13 @@ /obj/structure/stairs/proc/force_open_above() var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP) if(T && !istype(T)) - T.ChangeTurf(/turf/open/openspace) + T.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR) /obj/structure/stairs/proc/on_multiz_new(turf/source, dir) if(dir == UP) var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP) if(T && !istype(T)) - T.ChangeTurf(/turf/open/openspace) + T.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR) /obj/structure/stairs/intercept_zImpact(atom/movable/AM, levels = 1) . = ..() diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 76c8f7f11f..e2e0f703f7 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -5,6 +5,8 @@ icon_state = "toilet00" density = FALSE anchored = TRUE + can_buckle = TRUE + buckle_lying = 0 var/open = FALSE //if the lid is up var/cistern = 0 //if the cistern bit is open var/w_items = 0 //the combined w_class of all the items in the cistern @@ -682,4 +684,4 @@ else playsound(loc, 'sound/weapons/tap.ogg', 50, 1) if(BURN) - playsound(loc, 'sound/items/welder.ogg', 80, 1) \ No newline at end of file + playsound(loc, 'sound/items/welder.ogg', 80, 1) diff --git a/code/game/turfs/baseturf_skipover.dm b/code/game/turfs/baseturf_skipover.dm index 644714f8ed..4df8c86e62 100644 --- a/code/game/turfs/baseturf_skipover.dm +++ b/code/game/turfs/baseturf_skipover.dm @@ -10,4 +10,9 @@ /turf/baseturf_skipover/shuttle name = "Shuttle baseturf skipover" - desc = "Acts as the bottom of the shuttle, if this isn't here the shuttle floor is broken through." \ No newline at end of file + desc = "Acts as the bottom of the shuttle, if this isn't here the shuttle floor is broken through." + +/turf/baseturf_bottom + name = "Z-level baseturf placeholder" + desc = "Marker for z-level baseturf, usually resolves to space." + baseturfs = /turf/baseturf_bottom diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index 6a055bbd35..bc4cd8df33 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -1,6 +1,7 @@ // This is a list of turf types we dont want to assign to baseturfs unless through initialization or explicitly GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( /turf/open/space, + /turf/baseturf_bottom ))) /turf/proc/empty(turf_type=/turf/open/space, baseturf_type, list/ignore_typecache, flags) @@ -56,12 +57,20 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( // Creates a new turf // new_baseturfs can be either a single type or list of types, formated the same as baseturfs. see turf.dm /turf/proc/ChangeTurf(path, list/new_baseturfs, flags) - if(!path) - return - if(path == /turf/open/space/basic) - // basic doesn't initialize and this will cause issues - // no warning though because this can happen naturaly as a result of it being built on top of - path = /turf/open/space + switch(path) + if(null) + return + if(/turf/baseturf_bottom) + path = SSmapping.level_trait(z, ZTRAIT_BASETURF) || /turf/open/space + if (!ispath(path)) + path = text2path(path) + if (!ispath(path)) + warning("Z-level [z] has invalid baseturf '[SSmapping.level_trait(z, ZTRAIT_BASETURF)]'") + path = /turf/open/space + if(/turf/open/space/basic) + // basic doesn't initialize and this will cause issues + // no warning though because this can happen naturaly as a result of it being built on top of + path = /turf/open/space if(!GLOB.use_preloader && path == type && !(flags & CHANGETURF_FORCEOP)) // Don't no-op if the map loader requires it to be reconstructed return src if(flags & CHANGETURF_SKIP) @@ -128,16 +137,15 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( /turf/open/ChangeTurf(path, list/new_baseturfs, flags) if ((flags & CHANGETURF_INHERIT_AIR) && ispath(path, /turf/open)) SSair.remove_from_active(src) - var/stashed_air = air - air = null // so that it doesn't get deleted + var/datum/gas_mixture/stashed_air = new() + stashed_air.copy_from(air) . = ..() - if (!. || . == src) // changeturf failed or didn't do anything - air = stashed_air + if (!.) // changeturf failed or didn't do anything + QDEL_NULL(stashed_air) return var/turf/open/newTurf = . - if (!istype(newTurf.air, /datum/gas_mixture/immutable/space)) - QDEL_NULL(newTurf.air) - newTurf.air = stashed_air + newTurf.air.copy_from(stashed_air) + QDEL_NULL(stashed_air) SSair.add_to_active(newTurf) else if(ispath(path,/turf/closed)) @@ -215,7 +223,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( newT.assemble_baseturfs(initial(fake_turf_type.baseturfs)) // The baseturfs list is created like roundstart if(!length(newT.baseturfs)) newT.baseturfs = list(baseturfs) - newT.baseturfs -= newT.baseturfs & GLOB.blacklisted_automated_baseturfs + newT.baseturfs -= GLOB.blacklisted_automated_baseturfs newT.baseturfs.Insert(1, old_baseturfs) // The old baseturfs are put underneath return newT if(!length(baseturfs)) @@ -315,5 +323,5 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( SSair.add_to_active(src) /turf/proc/ReplaceWithLattice() - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) new /obj/structure/lattice(locate(x, y, z)) diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm index 6297cadc63..9e593cafce 100644 --- a/code/game/turfs/closed.dm +++ b/code/game/turfs/closed.dm @@ -23,7 +23,7 @@ icon = 'icons/turf/walls.dmi' explosion_block = 50 -/turf/closed/indestructible/TerraformTurf(path, defer_change = FALSE, ignore_air = FALSE) +/turf/closed/indestructible/TerraformTurf(path, new_baseturf, flags, defer_change = FALSE, ignore_air = FALSE) return /turf/closed/indestructible/acid_act(acidpwr, acid_volume, acid_id) diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm index 4bdb13effc..577c45a7f0 100644 --- a/code/game/turfs/open.dm +++ b/code/game/turfs/open.dm @@ -58,7 +58,7 @@ /turf/open/indestructible/singularity_act() return -/turf/open/indestructible/TerraformTurf(path, defer_change = FALSE, ignore_air = FALSE) +/turf/open/indestructible/TerraformTurf(path, new_baseturf, flags, defer_change = FALSE, ignore_air = FALSE) return /turf/open/indestructible/sound diff --git a/code/game/turfs/openspace/openspace.dm b/code/game/turfs/openspace/openspace.dm index e68aabcd08..66507e7053 100644 --- a/code/game/turfs/openspace/openspace.dm +++ b/code/game/turfs/openspace/openspace.dm @@ -106,7 +106,7 @@ qdel(L) playsound(src, 'sound/weapons/genhit.ogg', 50, 1) to_chat(user, "You build a floor.") - PlaceOnTop(/turf/open/floor/plating) + PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) else to_chat(user, "You need one floor tile to build a floor!") else diff --git a/code/game/turfs/simulated/chasm.dm b/code/game/turfs/simulated/chasm.dm index 0a7b507488..dab0c1c376 100644 --- a/code/game/turfs/simulated/chasm.dm +++ b/code/game/turfs/simulated/chasm.dm @@ -38,7 +38,7 @@ switch(passed_mode) if(RCD_FLOORWALL) to_chat(user, "You build a floor.") - PlaceOnTop(/turf/open/floor/plating) + PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) return TRUE return FALSE @@ -70,7 +70,7 @@ playsound(src, 'sound/weapons/genhit.ogg', 50, 1) to_chat(user, "You build a floor.") // Create a floor, which has this chasm underneath it - PlaceOnTop(/turf/open/floor/plating) + PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) else to_chat(user, "You need one floor tile to build a floor!") else diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index d14a4c7385..3f46994da9 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -62,29 +62,29 @@ if(severity != 1 && shielded && target != src) return if(target == src) - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) return if(target != null) severity = 3 switch(severity) if(1) - ScrapeAway(2) + ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR) if(2) switch(pick(1,2;75,3)) if(1) if(!length(baseturfs) || !ispath(baseturfs[baseturfs.len-1], /turf/open/floor)) - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) ReplaceWithLattice() else - ScrapeAway(2) + ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR) if(prob(33)) new /obj/item/stack/sheet/metal(src) if(2) - ScrapeAway(2) + ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR) if(3) if(prob(80)) - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) else break_tile() hotspot_expose(1000,CELL_VOLUME) @@ -135,7 +135,7 @@ burnt = 1 /turf/open/floor/proc/make_plating() - return ScrapeAway() + return ScrapeAway(flags = CHANGETURF_INHERIT_AIR) /turf/open/floor/ChangeTurf(path, new_baseturf, flags) if(!isfloorturf(src)) @@ -213,15 +213,15 @@ /turf/open/floor/narsie_act(force, ignore_mobs, probability = 20) . = ..() if(.) - ChangeTurf(/turf/open/floor/engine/cult) + ChangeTurf(/turf/open/floor/engine/cult, flags = CHANGETURF_INHERIT_AIR) /turf/open/floor/ratvar_act(force, ignore_mobs) . = ..() if(.) - ChangeTurf(/turf/open/floor/clockwork) + ChangeTurf(/turf/open/floor/clockwork, flags = CHANGETURF_INHERIT_AIR) /turf/open/floor/acid_melt() - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) /turf/open/floor/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) switch(the_rcd.mode) @@ -267,7 +267,7 @@ A.autoclose = TRUE return TRUE if(RCD_DECONSTRUCT) - if(ScrapeAway() == src) + if(ScrapeAway(flags = CHANGETURF_INHERIT_AIR) == src) return FALSE to_chat(user, "You deconstruct [src].") return TRUE diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm index ae67edf073..8e0eace9cd 100644 --- a/code/game/turfs/simulated/floor/plating.dm +++ b/code/game/turfs/simulated/floor/plating.dm @@ -11,7 +11,7 @@ name = "plating" icon_state = "plating" intact = FALSE - baseturfs = /turf/open/space + baseturfs = /turf/baseturf_bottom footstep = FOOTSTEP_PLATING barefootstep = FOOTSTEP_HARD_BAREFOOT clawfootstep = FOOTSTEP_HARD_CLAW @@ -61,7 +61,7 @@ to_chat(user, "You begin reinforcing the floor...") if(do_after(user, 30, target = src)) if (R.get_amount() >= 2 && !istype(src, /turf/open/floor/engine)) - PlaceOnTop(/turf/open/floor/engine) + PlaceOnTop(/turf/open/floor/engine, flags = CHANGETURF_INHERIT_AIR) playsound(src, 'sound/items/deconstruct.ogg', 80, 1) R.use(2) to_chat(user, "You reinforce the floor.") @@ -76,7 +76,7 @@ var/obj/item/stack/tile/W = C if(!W.use(1)) return - var/turf/open/floor/T = PlaceOnTop(W.turf_type) + var/turf/open/floor/T = PlaceOnTop(W.turf_type, flags = CHANGETURF_INHERIT_AIR) if(istype(W, /obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow var/obj/item/stack/tile/light/L = W var/turf/open/floor/light/F = T @@ -117,7 +117,7 @@ qdel(L) to_chat(user, "You reinforce the foamed plating with tiling.") playsound(src, 'sound/weapons/Genhit.ogg', 50, TRUE) - ChangeTurf(/turf/open/floor/plating) + ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) else playsound(src, 'sound/weapons/tap.ogg', 100, TRUE) //The attack sound is muffled by the foam itself user.changeNext_move(CLICK_CD_MELEE) @@ -125,7 +125,7 @@ if(prob(I.force * 20 - 25)) user.visible_message("[user] smashes through [src]!", \ "You smash through [src] with [I]!") - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) else to_chat(user, "You hit [src], to no effect!") @@ -136,13 +136,13 @@ /turf/open/floor/plating/foam/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) if(passed_mode == RCD_FLOORWALL) to_chat(user, "You build a floor.") - ChangeTurf(/turf/open/floor/plating) + ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) return TRUE return FALSE /turf/open/floor/plating/foam/ex_act() ..() - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) /turf/open/floor/plating/foam/tool_act(mob/living/user, obj/item/I, tool_type) return diff --git a/code/game/turfs/simulated/floor/plating/asteroid.dm b/code/game/turfs/simulated/floor/plating/asteroid.dm index 9f2da57312..ecc8ee5e8d 100644 --- a/code/game/turfs/simulated/floor/plating/asteroid.dm +++ b/code/game/turfs/simulated/floor/plating/asteroid.dm @@ -76,11 +76,6 @@ for(var/obj/item/stack/ore/O in src) SEND_SIGNAL(W, COMSIG_PARENT_ATTACKBY, O) -/turf/open/floor/plating/asteroid/singularity_act() - if(is_planet_level(z)) - return ..() - ScrapeAway() - /turf/open/floor/plating/asteroid/ex_act(severity, target) . = SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target) contents_explosion(severity, target) @@ -132,6 +127,7 @@ /turf/open/floor/plating/asteroid/airless initial_gas_mix = AIRLESS_ATMOS + baseturfs = /turf/open/floor/plating/asteroid/airless turf_type = /turf/open/floor/plating/asteroid/airless diff --git a/code/game/turfs/simulated/floor/reinf_floor.dm b/code/game/turfs/simulated/floor/reinf_floor.dm index b04f89f8be..28ffbbd1ff 100644 --- a/code/game/turfs/simulated/floor/reinf_floor.dm +++ b/code/game/turfs/simulated/floor/reinf_floor.dm @@ -43,7 +43,7 @@ return TRUE if(floor_tile) new floor_tile(src, 2) - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) return TRUE /turf/open/floor/engine/acid_act(acidpwr, acid_volume) @@ -56,23 +56,23 @@ if(severity != 1 && shielded && target != src) return if(target == src) - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) return switch(severity) if(1) if(prob(80)) if(!length(baseturfs) || !ispath(baseturfs[baseturfs.len-1], /turf/open/floor)) - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) ReplaceWithLattice() else - ScrapeAway(2) + ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR) else if(prob(50)) - ScrapeAway(2) + ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR) else - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) if(2) if(prob(50)) - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) /turf/open/floor/engine/singularity_pull(S, current_size) ..() diff --git a/code/game/turfs/simulated/lava.dm b/code/game/turfs/simulated/lava.dm index 362b410cbb..2f8ae0cb93 100644 --- a/code/game/turfs/simulated/lava.dm +++ b/code/game/turfs/simulated/lava.dm @@ -54,7 +54,7 @@ switch(passed_mode) if(RCD_FLOORWALL) to_chat(user, "You build a floor.") - PlaceOnTop(/turf/open/floor/plating) + PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) return TRUE return FALSE diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index f38f13e657..5278374abd 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -125,7 +125,7 @@ qdel(L) playsound(src, 'sound/weapons/genhit.ogg', 50, 1) to_chat(user, "You build a floor.") - PlaceOnTop(/turf/open/floor/plating) + PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) else to_chat(user, "You need one floor tile to build a floor!") else @@ -212,7 +212,7 @@ switch(passed_mode) if(RCD_FLOORWALL) to_chat(user, "You build a floor.") - PlaceOnTop(/turf/open/floor/plating) + PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) return TRUE return FALSE diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index d618e457b0..d280ba408b 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -9,7 +9,7 @@ // A list will be created in initialization that figures out the baseturf's baseturf etc. // In the case of a list it is sorted from bottom layer to top. // This shouldn't be modified directly, use the helper procs. - var/list/baseturfs = /turf/open/space + var/list/baseturfs = /turf/baseturf_bottom var/temperature = T20C var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed @@ -391,7 +391,7 @@ continue if(O.invisibility == INVISIBILITY_MAXIMUM) O.singularity_act() - ScrapeAway() + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) return(2) /turf/proc/can_have_cabling() @@ -564,4 +564,4 @@ //Whatever happens after high temperature fire dies out or thermite reaction works. //Should return new turf /turf/proc/Melt() - return ScrapeAway() + return ScrapeAway(flags = CHANGETURF_INHERIT_AIR) diff --git a/code/game/world.dm b/code/game/world.dm index 25b8c4d9f3..4043f15f6f 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -108,6 +108,7 @@ GLOBAL_VAR(restart_counter) GLOB.world_href_log = "[GLOB.log_directory]/hrefs.log" GLOB.sql_error_log = "[GLOB.log_directory]/sql.log" GLOB.world_qdel_log = "[GLOB.log_directory]/qdel.log" + GLOB.world_map_error_log = "[GLOB.log_directory]/map_errors.log" GLOB.world_runtime_log = "[GLOB.log_directory]/runtime.log" GLOB.query_debug_log = "[GLOB.log_directory]/query_debug.log" GLOB.world_job_debug_log = "[GLOB.log_directory]/job_debug.log" diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 895f8c9f88..1cecbbd0e5 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -693,7 +693,7 @@ var/prev_dynamic_voting = CONFIG_GET(flag/dynamic_voting) CONFIG_SET(flag/dynamic_voting,!prev_dynamic_voting) if (!prev_dynamic_voting) - to_chat(world, "Vote is now between extended and dynamic chaos.") + to_chat(world, "Vote is now a ranked choice of dynamic storytellers.") else to_chat(world, "Vote is now between extended and secret.") log_admin("[key_name(usr)] [prev_dynamic_voting ? "disabled" : "enabled"] dynamic voting.") diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 2b8d365e15..acd320856a 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -35,6 +35,9 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list( /client/proc/cmd_admin_grantfullaccess, /client/proc/cmd_admin_areatest_all, /client/proc/cmd_admin_areatest_station, + #ifdef TESTING + /client/proc/see_dirty_varedits, + #endif /client/proc/cmd_admin_test_atmos_controllers, /client/proc/cmd_admin_rejuvenate, /datum/admins/proc/show_traitor_panel, @@ -84,8 +87,24 @@ GLOBAL_PROTECT(admin_verbs_debug_mapping) SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range") +#ifdef TESTING +GLOBAL_LIST_EMPTY(dirty_vars) +/client/proc/see_dirty_varedits() + set category = "Mapping" + set name = "Dirty Varedits" + + var/list/dat = list() + dat += "

    Abandon all hope ye who enter here



    " + for(var/thing in GLOB.dirty_vars) + dat += "[thing]
    " + CHECK_TICK + var/datum/browser/popup = new(usr, "dirty_vars", "Dirty Varedits", 900, 750) + popup.set_content(dat.Join()) + popup.open() +#endif + /client/proc/sec_camera_report() set category = "Mapping" set name = "Camera Report" diff --git a/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm b/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm index 0365ae63af..501bcdc1c4 100644 --- a/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm +++ b/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm @@ -139,7 +139,7 @@ var/new_thing_type = fabrication_values["new_obj_type"] if(isturf(target)) //if our target is a turf, we're just going to ChangeTurf it and assume it'll work out. var/turf/T = target - T.ChangeTurf(new_thing_type) + T.ChangeTurf(new_thing_type, flags = CHANGETURF_INHERIT_AIR) else if(new_thing_type) if(fabrication_values["dir_in_new"]) diff --git a/code/modules/antagonists/clockcult/clock_structures/wall_gear.dm b/code/modules/antagonists/clockcult/clock_structures/wall_gear.dm index 32b1b61dd1..d823f19d4a 100644 --- a/code/modules/antagonists/clockcult/clock_structures/wall_gear.dm +++ b/code/modules/antagonists/clockcult/clock_structures/wall_gear.dm @@ -59,7 +59,7 @@ if(anchored) T.PlaceOnTop(/turf/closed/wall/clockwork) else - T.PlaceOnTop(/turf/open/floor/clockwork) + T.PlaceOnTop(/turf/open/floor/clockwork, flags = CHANGETURF_INHERIT_AIR) new /obj/structure/falsewall/brass(T) qdel(src) else diff --git a/code/modules/antagonists/cult/cult_structures.dm b/code/modules/antagonists/cult/cult_structures.dm index f38f379abe..6f340b9271 100644 --- a/code/modules/antagonists/cult/cult_structures.dm +++ b/code/modules/antagonists/cult/cult_structures.dm @@ -239,9 +239,9 @@ var/turf/T = safepick(validturfs) if(T) if(istype(T, /turf/open/floor/plating)) - T.PlaceOnTop(/turf/open/floor/engine/cult) + T.PlaceOnTop(/turf/open/floor/engine/cult, flags = CHANGETURF_INHERIT_AIR) else - T.ChangeTurf(/turf/open/floor/engine/cult) + T.ChangeTurf(/turf/open/floor/engine/cult, flags = CHANGETURF_INHERIT_AIR) else var/turf/open/floor/engine/cult/F = safepick(cultturfs) if(F) diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm index 0a223f8b10..b5ce5538e9 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm @@ -76,9 +76,10 @@ GLOBAL_VAR_INIT(war_declared, FALSE) CONFIG_SET(number/shuttle_refuel_delay, max(CONFIG_GET(number/shuttle_refuel_delay), CHALLENGE_SHUTTLE_DELAY)) if(istype(SSticker.mode, /datum/game_mode/dynamic)) var/datum/game_mode/dynamic/mode = SSticker.mode - var/threat_spent = CONFIG_GET(number/dynamic_warops_cost) - mode.spend_threat(threat_spent) - mode.log_threat("Nuke ops spent [threat_spent] on war ops.") + if(!(mode.storyteller.flags & WAROPS_ALWAYS_ALLOWED)) + var/threat_spent = CONFIG_GET(number/dynamic_warops_cost) + mode.spend_threat(threat_spent) + mode.log_threat("Nuke ops spent [threat_spent] on war ops.") SSblackbox.record_feedback("amount", "nuclear_challenge_mode", 1) qdel(src) @@ -101,12 +102,13 @@ GLOBAL_VAR_INIT(war_declared, FALSE) return FALSE if(istype(SSticker.mode, /datum/game_mode/dynamic)) var/datum/game_mode/dynamic/mode = SSticker.mode - if(mode.threat_level < CONFIG_GET(number/dynamic_warops_requirement)) - to_chat(user, "Due to the dynamic space in which the station resides, you are too deep into Nanotrasen territory to reasonably go loud.") - return FALSE - else if(mode.threat < CONFIG_GET(number/dynamic_warops_cost)) - to_chat(user, "Due to recent threats on the station, Nanotrasen is looking too closely for a war declaration to be wise.") - return FALSE + if(!(mode.storyteller.flags & WAROPS_ALWAYS_ALLOWED)) + if(mode.threat_level < CONFIG_GET(number/dynamic_warops_requirement)) + to_chat(user, "Due to the dynamic space in which the station resides, you are too deep into Nanotrasen territory to reasonably go loud.") + return FALSE + else if(mode.threat < CONFIG_GET(number/dynamic_warops_cost)) + to_chat(user, "Due to recent threats on the station, Nanotrasen is looking too closely for a war declaration to be wise.") + return FALSE return TRUE /obj/item/nuclear_challenge/clownops diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index ac19df3631..40d1712bc3 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -80,6 +80,8 @@ if(istype(SSticker.mode,/datum/game_mode/dynamic)) mode = SSticker.mode is_dynamic = TRUE + if(mode.storyteller.flags & NO_ASSASSIN) + is_hijacker = FALSE if(GLOB.joined_player_list.len>=GLOB.dynamic_high_pop_limit) is_hijacker = (prob(10) && mode.threat_level > CONFIG_GET(number/dynamic_hijack_high_population_requirement)) else @@ -180,7 +182,7 @@ destroy_objective.owner = owner destroy_objective.find_target() add_objective(destroy_objective) - else if(prob(30)) + else if(prob(30) || (mode.storyteller.flags & NO_ASSASSIN)) var/datum/objective/maroon/maroon_objective = new maroon_objective.owner = owner maroon_objective.find_target() diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm index 38178a4339..db8a8e90ad 100644 --- a/code/modules/atmospherics/machinery/datum_pipeline.dm +++ b/code/modules/atmospherics/machinery/datum_pipeline.dm @@ -57,10 +57,10 @@ if(item.parent) var/static/pipenetwarnings = 10 if(pipenetwarnings > 0) - warning("build_pipeline(): [item.type] added to a pipenet while still having one. (pipes leading to the same spot stacking in one turf) Nearby: ([item.x], [item.y], [item.z])") + log_mapping("build_pipeline(): [item.type] added to a pipenet while still having one. (pipes leading to the same spot stacking in one turf) Nearby: ([item.x], [item.y], [item.z]).") pipenetwarnings -= 1 if(pipenetwarnings == 0) - warning("build_pipeline(): further messages about pipenets will be suppressed") + log_mapping("build_pipeline(): further messages about pipenets will be suppressed") members += item possible_expansions += item diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm index d0c78d21b3..329c77ba14 100644 --- a/code/modules/atmospherics/machinery/other/meter.dm +++ b/code/modules/atmospherics/machinery/other/meter.dm @@ -56,7 +56,7 @@ pixel_y = (new_layer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_P_Y /obj/machinery/meter/process_atmos() - if(!target) + if(!(target?.flags_1 & INITIALIZED_1)) icon_state = "meterX" return 0 diff --git a/code/modules/buildmode/submodes/basic.dm b/code/modules/buildmode/submodes/basic.dm index 4f7b176691..fe3d831d81 100644 --- a/code/modules/buildmode/submodes/basic.dm +++ b/code/modules/buildmode/submodes/basic.dm @@ -22,9 +22,9 @@ if(istype(object,/turf) && left_click && !alt_click && !ctrl_click) var/turf/T = object if(isspaceturf(object)) - T.PlaceOnTop(/turf/open/floor/plating) + T.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) else if(isplatingturf(object)) - T.PlaceOnTop(/turf/open/floor/plasteel) + T.PlaceOnTop(/turf/open/floor/plasteel, flags = CHANGETURF_INHERIT_AIR) else if(isfloorturf(object)) T.PlaceOnTop(/turf/closed/wall) else if(iswallturf(object)) @@ -35,7 +35,7 @@ log_admin("Build Mode: [key_name(c)] deleted [object] at [AREACOORD(object)]") if(isturf(object)) var/turf/T = object - T.ScrapeAway() + T.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) else if(isobj(object)) qdel(object) return diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 96a32d50e6..7b7cf3413e 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -21,6 +21,10 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/last_ip var/last_id + var/icon/custom_holoform_icon + var/list/cached_holoform_icons + var/last_custom_holoform = 0 + //Cooldowns for saving/loading. These are four are all separate due to loading code calling these one after another var/saveprefcooldown var/loadprefcooldown @@ -2422,3 +2426,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) return else custom_names[name_id] = sanitized_name + +/datum/preferences/proc/get_filtered_holoform(filter_type) + if(!custom_holoform_icon) + return + LAZYINITLIST(cached_holoform_icons) + if(!cached_holoform_icons[filter_type]) + cached_holoform_icons[filter_type] = process_holoform_icon_filter(custom_holoform_icon, filter_type) + return cached_holoform_icons[filter_type] diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index d9e967e54d..6fd1584e75 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -57,7 +57,6 @@ //No idea what this is but eh -tori var/force_alternate_icon = FALSE - /obj/item/clothing/Initialize() . = ..() if(CHECK_BITFIELD(clothing_flags, VOICEBOX_TOGGLABLE)) diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm index 889706744a..d551222836 100644 --- a/code/modules/holodeck/computer.dm +++ b/code/modules/holodeck/computer.dm @@ -59,9 +59,7 @@ return var/area/AS = get_area(src) if(istype(AS, /area/holodeck)) - log_world("### MAPPING ERROR") - log_world("Holodeck computer cannot be in a holodeck.") - log_world("This would cause circular power dependency.") + log_mapping("Holodeck computer cannot be in a holodeck, This would cause circular power dependency.") qdel(src) return else diff --git a/code/modules/mapping/map_template.dm b/code/modules/mapping/map_template.dm index 1f55f18823..3e361179de 100644 --- a/code/modules/mapping/map_template.dm +++ b/code/modules/mapping/map_template.dm @@ -70,7 +70,7 @@ //initialize things that are normally initialized after map load parsed.initTemplateBounds() smooth_zlevel(world.maxz) - log_game("Z-level [name] loaded at at [x],[y],[world.maxz]") + log_game("Z-level [name] loaded at [x],[y],[world.maxz]") return level @@ -84,6 +84,13 @@ if(T.y+height > world.maxy) return + var/list/border = block(locate(max(T.x-1, 1), max(T.y-1, 1), T.z), + locate(min(T.x+width+1, world.maxx), min(T.y+height+1, world.maxy), T.z)) + for(var/L in border) + var/turf/turf_to_disable = L + SSair.remove_from_active(turf_to_disable) //stop processing turfs along the border to prevent runtimes, we return it in initTemplateBounds() + turf_to_disable.atmos_adjacent_turfs?.Cut() + // Accept cached maps, but don't save them automatically - we don't want // ruins clogging up memory for the whole round. var/datum/parsed_map/parsed = cached_map || new(file(mappath)) @@ -100,7 +107,7 @@ //initialize things that are normally initialized after map load parsed.initTemplateBounds() - log_game("[name] loaded at at [T.x],[T.y],[T.z]") + log_game("[name] loaded at [T.x],[T.y],[T.z]") return bounds /datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE) diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index e2459d780a..a7f84fd71f 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -18,7 +18,7 @@ /obj/effect/baseturf_helper/LateInitialize() if(!baseturf_to_replace) - baseturf_to_replace = typecacheof(/turf/open/space) + baseturf_to_replace = typecacheof(list(/turf/open/space,/turf/baseturf_bottom)) else if(!length(baseturf_to_replace)) baseturf_to_replace = list(baseturf_to_replace = TRUE) else if(baseturf_to_replace[baseturf_to_replace[1]] != TRUE) // It's not associative @@ -45,7 +45,6 @@ thing.PlaceOnBottom(null, baseturf) else if(baseturf_to_replace[thing.baseturfs]) thing.assemble_baseturfs(baseturf) - return else thing.PlaceOnBottom(null, baseturf) @@ -107,16 +106,16 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper/Initialize(mapload) . = ..() if(!mapload) - log_world("### MAP WARNING, [src] spawned outside of mapload!") + log_mapping("[src] spawned outside of mapload!") return var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc if(airlock) if(airlock.cyclelinkeddir) - log_world("### MAP WARNING, [src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!") + log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!") else airlock.cyclelinkeddir = dir else - log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]") + log_mapping("[src] failed to find an airlock at [AREACOORD(src)]") /obj/effect/mapping_helpers/airlock/locked @@ -126,16 +125,16 @@ /obj/effect/mapping_helpers/airlock/locked/Initialize(mapload) . = ..() if(!mapload) - log_world("### MAP WARNING, [src] spawned outside of mapload!") + log_mapping("[src] spawned outside of mapload!") return var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc if(airlock) if(airlock.locked) - log_world("### MAP WARNING, [src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!") + log_mapping("[src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!") else airlock.locked = TRUE else - log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]") + log_mapping("[src] failed to find an airlock at [AREACOORD(src)]") /obj/effect/mapping_helpers/airlock/unres name = "airlock unresctricted side helper" @@ -144,13 +143,13 @@ /obj/effect/mapping_helpers/airlock/unres/Initialize(mapload) . = ..() if(!mapload) - log_world("### MAP WARNING, [src] spawned outside of mapload!") + log_mapping("[src] spawned outside of mapload!") return var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc if(airlock) airlock.unres_sides ^= dir else - log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]") + log_mapping("[src] failed to find an airlock at [AREACOORD(src)]") //needs to do its thing before spawn_rivers() is called @@ -164,17 +163,6 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava) var/turf/T = get_turf(src) T.flags_1 |= NO_LAVA_GEN_1 -/// Adds the map it is on to the z_is_planet list -/obj/effect/mapping_helpers/planet_z - name = "planet z helper" - layer = POINT_LAYER - -/obj/effect/mapping_helpers/planet_z/Initialize() - . = ..() - var/datum/space_level/S = SSmapping.get_level(z) - S.traits[ZTRAIT_PLANET] = TRUE - - //This helper applies components to things on the map directly. /obj/effect/mapping_helpers/component_injector name = "Component Injector" diff --git a/code/modules/mapping/preloader.dm b/code/modules/mapping/preloader.dm index e6fa2421a0..4b61663f66 100644 --- a/code/modules/mapping/preloader.dm +++ b/code/modules/mapping/preloader.dm @@ -8,18 +8,26 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) var/list/attributes var/target_path -/datum/map_preloader/proc/setup(list/the_attributes, path) +/world/proc/preloader_setup(list/the_attributes, path) if(the_attributes.len) GLOB.use_preloader = TRUE - attributes = the_attributes - target_path = path + var/datum/map_preloader/preloader_local = GLOB._preloader + preloader_local.attributes = the_attributes + preloader_local.target_path = path -/datum/map_preloader/proc/load(atom/what) +/world/proc/preloader_load(atom/what) GLOB.use_preloader = FALSE - for(var/attribute in attributes) - var/value = attributes[attribute] + var/datum/map_preloader/preloader_local = GLOB._preloader + for(var/attribute in preloader_local.attributes) + var/value = preloader_local.attributes[attribute] if(islist(value)) value = deepCopyList(value) + #ifdef TESTING + if(what.vars[attribute] == value) + var/message = "[what.type] at [AREACOORD(what)] - VAR: [attribute] = [isnull(value) ? "null" : (isnum(value) ? value : "\"[value]\"")]" + log_mapping("DIRTY VAR: [message]") + GLOB.dirty_vars += message + #endif what.vars[attribute] = value /area/template_noop diff --git a/code/modules/mapping/reader.dm b/code/modules/mapping/reader.dm index 22be4aa246..9b27691dd3 100644 --- a/code/modules/mapping/reader.dm +++ b/code/modules/mapping/reader.dm @@ -306,8 +306,8 @@ //first instance the /area and remove it from the members list index = members.len if(members[index] != /area/template_noop) - GLOB._preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation var/atype = members[index] + world.preloader_setup(members_attributes[index], atype)//preloader for assigning set variables on atom creation var/atom/instance = areaCache[atype] if (!instance) instance = GLOB.areas_by_type[atype] @@ -318,7 +318,7 @@ instance.contents.Add(crds) if(GLOB.use_preloader && instance) - GLOB._preloader.load(instance) + world.preloader_load(instance) //then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect @@ -354,7 +354,7 @@ //Instance an atom at (x,y,z) and gives it the variables in attributes /datum/parsed_map/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf, placeOnTop) - GLOB._preloader.setup(attributes, path) + world.preloader_setup(attributes, path) if(crds) if(ispath(path, /turf)) @@ -368,7 +368,7 @@ . = create_atom(path, crds)//first preloader pass if(GLOB.use_preloader && .)//second preloader pass, for those atoms that don't ..() in New() - GLOB._preloader.load(.) + world.preloader_load(.) //custom CHECK_TICK here because we don't want things created while we're sleeping to not initialize if(TICK_CHECK) diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index ab3b20ac9f..7bef9b652a 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -926,7 +926,7 @@ timer = world.time + create_delay + 1 if(do_after(user, create_delay, target = T)) var/old_name = T.name - if(T.TerraformTurf(turf_type)) + if(T.TerraformTurf(turf_type, flags = CHANGETURF_INHERIT_AIR)) user.visible_message("[user] turns \the [old_name] into [transform_string]!") message_admins("[ADMIN_LOOKUPFLW(user)] fired the lava staff at [ADMIN_VERBOSEJMP(T)]") log_game("[key_name(user)] fired the lava staff at [AREACOORD(T)].") @@ -937,7 +937,7 @@ qdel(L) else var/old_name = T.name - if(T.TerraformTurf(reset_turf_type)) + if(T.TerraformTurf(reset_turf_type, flags = CHANGETURF_INHERIT_AIR)) user.visible_message("[user] turns \the [old_name] into [reset_string]!") timer = world.time + reset_cooldown playsound(T,'sound/magic/fireball.ogg', 200, 1) diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm index 7aae26526e..5b664dc01a 100644 --- a/code/modules/mob/dead/new_player/preferences_setup.dm +++ b/code/modules/mob/dead/new_player/preferences_setup.dm @@ -27,12 +27,7 @@ /datum/preferences/proc/update_preview_icon(equip_job = TRUE) // Determine what job is marked as 'High' priority, and dress them up as such. - var/datum/job/previewJob - var/highest_pref = 0 - for(var/job in job_preferences) - if(job_preferences["[job]"] > highest_pref) - previewJob = SSjob.GetJob(job) - highest_pref = job_preferences["[job]"] + var/datum/job/previewJob = get_highest_job() if(previewJob) // Silicons only need a very basic preview since there is no customization for them. @@ -57,3 +52,11 @@ parent.show_character_previews(new /mutable_appearance(mannequin)) unset_busy_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES) +/datum/preferences/proc/get_highest_job() + var/highest_pref = 0 + var/datum/job/highest_job + for(var/job in job_preferences) + if(job_preferences["[job]"] > highest_pref) + highest_job = SSjob.GetJob(job) + highest_pref = job_preferences["[job]"] + return highest_job diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index e1e079cbae..8e1e5aab8d 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -14,10 +14,9 @@ var/max_grown = 100 var/time_of_birth - rotate_on_lying = 0 + rotate_on_lying = FALSE bodyparts = list(/obj/item/bodypart/chest/larva, /obj/item/bodypart/head/larva) - //This is fine right now, if we're adding organ specific damage this needs to be updated /mob/living/carbon/alien/larva/Initialize() diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index a1184613fe..9629dc3484 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -40,7 +40,7 @@ var/gib_type = /obj/effect/decal/cleanable/blood/gibs - var/rotate_on_lying = 1 + rotate_on_lying = TRUE var/tinttotal = 0 // Total level of visualy impairing items diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm index 345279df29..559c2ebcdd 100644 --- a/code/modules/mob/living/carbon/emote.dm +++ b/code/modules/mob/living/carbon/emote.dm @@ -22,6 +22,7 @@ muzzle_ignore = TRUE restraint_check = TRUE emote_type = EMOTE_AUDIBLE + mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/carbon/clap/run_emote(mob/living/user, params) . = ..() diff --git a/code/modules/mob/living/carbon/update_icons.dm b/code/modules/mob/living/carbon/update_icons.dm index 66be8058f1..0155f2575d 100644 --- a/code/modules/mob/living/carbon/update_icons.dm +++ b/code/modules/mob/living/carbon/update_icons.dm @@ -1,31 +1,3 @@ -//IMPORTANT: Multiple animate() calls do not stack well, so try to do them all at once if you can. -/mob/living/carbon/update_transform() - var/matrix/ntransform = matrix(transform) //aka transform.Copy() - var/final_pixel_y = pixel_y - var/final_dir = dir - var/changed = 0 - if(lying != lying_prev && rotate_on_lying) - changed++ - ntransform.TurnTo(lying_prev,lying) - if(lying == 0) //Lying to standing - final_pixel_y = get_standard_pixel_y_offset() - else //if(lying != 0) - if(lying_prev == 0) //Standing to lying - pixel_y = get_standard_pixel_y_offset() - final_pixel_y = get_standard_pixel_y_offset(lying) - if(dir & (EAST|WEST)) //Facing east or west - final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass - - if(resize != RESIZE_DEFAULT_SIZE) - changed++ - ntransform.Scale(resize) - resize = RESIZE_DEFAULT_SIZE - - if(changed) - animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, dir = final_dir, easing = EASE_IN|EASE_OUT) - setMovetype(movement_type & ~FLOATING) // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life(). - - /mob/living/carbon var/list/overlays_standing[TOTAL_LAYERS] diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 4d2a36907d..783e044492 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -111,4 +111,6 @@ var/list/diseases = list() // list of all diseases in a mob var/list/disease_resistances = list() - var/drag_slowdown = TRUE //Whether the mob is slowed down when dragging another prone mob \ No newline at end of file + var/drag_slowdown = TRUE //Whether the mob is slowed down when dragging another prone mob + + var/rotate_on_lying = FALSE \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 0444458841..1694d04b82 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -260,15 +260,6 @@ viewalerts = 1 src << browse(dat, "window=aialerts&can_close=0") -/mob/living/silicon/ai/proc/ai_roster() - var/dat = "Crew RosterCrew Roster:

    " - - dat += GLOB.data_core.get_manifest() - dat += "" - - src << browse(dat, "window=airoster") - onclose(src, "airoster") - /mob/living/silicon/ai/proc/ai_call_shuttle() if(control_disabled) to_chat(usr, "Wireless control is disabled!") @@ -681,18 +672,23 @@ "green face" = 'icons/mob/ai.dmi', "xeno queen" = 'icons/mob/alien.dmi', "horror" = 'icons/mob/ai.dmi', - "creature" = 'icons/mob/ai.dmi' + "creature" = 'icons/mob/ai.dmi', + "custom" ) input = input("Please select a hologram:") as null|anything in icon_list if(input) qdel(holo_icon) switch(input) - if("xeno queen") + if("custom") + if(client?.prefs?.custom_holoform_icon) + holo_icon = client.prefs.get_filtered_holoform(HOLOFORM_FILTER_AI) + else + holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "female")) + else if("xeno queen") holo_icon = getHologramIcon(icon(icon_list[input],"alienq")) else holo_icon = getHologramIcon(icon(icon_list[input], input)) - return /mob/living/silicon/ai/proc/corereturn() set category = "Malfunction" diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index 7477ba7713..38afaf8313 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -68,7 +68,7 @@ // Use this when setting the aiEye's location. // It will also stream the chunk that the new loc is in. -/mob/camera/aiEye/proc/setLoc(T, force_update = FALSE) +/mob/camera/aiEye/proc/setLoc(T, force_update = FALSE, dir) if(ai) if(!isturf(ai.loc)) return @@ -88,7 +88,7 @@ //Holopad if(istype(ai.current, /obj/machinery/holopad)) var/obj/machinery/holopad/H = ai.current - H.move_hologram(ai, T) + H.move_hologram(ai, T, dir) if(ai.camera_light_on) ai.light_cameras() if(ai.master_multicam) @@ -139,7 +139,7 @@ for(var/i = 0; i < max(user.sprint, initial); i += 20) var/turf/step = get_turf(get_step(user.eyeobj, direct)) if(step) - user.eyeobj.setLoc(step) + user.eyeobj.setLoc(step, null, direct) user.cooldown = world.timeofday + 5 if(user.acceleration) diff --git a/code/modules/mob/living/silicon/custom_holoform.dm b/code/modules/mob/living/silicon/custom_holoform.dm new file mode 100644 index 0000000000..8ce6e9a29a --- /dev/null +++ b/code/modules/mob/living/silicon/custom_holoform.dm @@ -0,0 +1,28 @@ +/mob/living/silicon/verb/clear_custom_holoform() + set name = "Clear Custom Holoform" + set desc = "Clear your current custom holoform" + set category = "OOC" + + if(!client.prefs) + to_chat(src, "No preferences datum on your client, contact an admin/coder!") + return + client.prefs.custom_holoform_icon = null + client.prefs.cached_holoform_icons = null + to_chat(src, "Holoform removed.") + +/mob/living/silicon/verb/set_custom_holoform() + set name = "Set Custom Holoform" + set desc = "Set your custom holoform using your current preferences slot and a specified set of gear." + set category = "OOC" + + if(!client.prefs) + to_chat(src, "No preferences datum on your client, contact an admin/coder!") + return + if(client.prefs.last_custom_holoform > world.time - CUSTOM_HOLOFORM_DELAY) + to_chat(src, "You are attempting to change custom holoforms too fast!") + var/icon/new_holoform = user_interface_custom_holoform(client) + if(new_holoform) + client.prefs.custom_holoform_icon = new_holoform + client.prefs.cached_holoform_icons = null + client.prefs.last_custom_holoform = world.time + to_chat(src, "Holoform set.") diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 7ec63cf415..7055838039 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -58,10 +58,12 @@ var/canholo = TRUE var/obj/item/card/id/access_card = null var/chassis = "repairbot" - var/list/possible_chassis = list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE, - "fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE, "borgi" = FALSE , - "parrot" = FALSE, "bear" = FALSE , "mushroom" = FALSE, "crow" = FALSE , - "fairy" = FALSE , "spiderbot" = FALSE) //assoc value is whether it can be picked up. + var/dynamic_chassis + var/dynamic_chassis_sit = FALSE //whether we're sitting instead of resting spritewise + var/dynamic_chassis_bellyup = FALSE //whether we're lying down bellyup + var/list/possible_chassis //initialized in initialize. + var/list/dynamic_chassis_icons //ditto. + var/list/chassis_pixel_offsets_x //stupid dogborgs var/static/item_head_icon = 'icons/mob/pai_item_head.dmi' var/static/item_lh_icon = 'icons/mob/pai_item_lh.dmi' var/static/item_rh_icon = 'icons/mob/pai_item_rh.dmi' @@ -69,22 +71,20 @@ var/emitterhealth = 20 var/emittermaxhealth = 20 var/emitterregen = 0.25 + var/emitter_next_use = 0 + var/emitter_emp_cd = 300 var/emittercd = 50 var/emitteroverloadcd = 100 - var/emittersemicd = FALSE - var/overload_ventcrawl = 0 - var/overload_bulletblock = 0 //Why is this a good idea? - var/overload_maxhealth = 0 + var/radio_short = FALSE + var/radio_short_cooldown = 5 MINUTES + var/radio_short_timerid + canmove = FALSE var/silent = FALSE var/brightness_power = 5 -/mob/living/silicon/pai/can_unbuckle() - return FALSE - -/mob/living/silicon/pai/can_buckle() - return FALSE + var/icon/custom_holoform_icon /mob/living/silicon/pai/Destroy() if (loc != card) @@ -118,6 +118,13 @@ pda.owner = text("[]", src) pda.name = pda.owner + " (" + pda.ownjob + ")" + possible_chassis = typelist(NAMEOF(src, possible_chassis), list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE, + "fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE, "borgi" = FALSE , + "parrot" = FALSE, "bear" = FALSE , "mushroom" = FALSE, "crow" = FALSE , + "fairy" = FALSE , "spiderbot" = FALSE)) //assoc value is whether it can be picked up. + dynamic_chassis_icons = typelist(NAMEOF(src, dynamic_chassis_icons), initialize_dynamic_chassis_icons()) + chassis_pixel_offsets_x = typelist(NAMEOF(src, chassis_pixel_offsets_x), default_chassis_pixel_offsets_x()) + . = ..() var/datum/action/innate/pai/software/SW = new @@ -133,8 +140,7 @@ AR.Grant(src) AL.Grant(src) ALM.Grant(src) - emittersemicd = TRUE - addtimer(CALLBACK(src, .proc/emittercool), 600) + emitter_next_use = world.time + 10 SECONDS /mob/living/silicon/pai/Life() if(hacking) @@ -228,7 +234,7 @@ /datum/action/innate/pai/shell/Trigger() ..() if(P.holoform) - P.fold_in(0) + P.fold_in(FALSE) else P.fold_out() @@ -292,3 +298,132 @@ /mob/living/silicon/pai/process() emitterhealth = CLAMP((emitterhealth + emitterregen), -50, emittermaxhealth) + +/mob/living/silicon/pai/proc/short_radio() + if(radio_short_timerid) + deltimer(radio_short_timerid) + radio_short = TRUE + to_chat(src, "Your radio shorts out!") + radio_short_timerid = addtimer(CALLBACK(src, .proc/unshort_radio), radio_short_cooldown, flags = TIMER_STOPPABLE) + +/mob/living/silicon/pai/proc/unshort_radio() + radio_short = FALSE + to_chat(src, "You feel your radio is operational once more.") + if(radio_short_timerid) + deltimer(radio_short_timerid) + +/mob/living/silicon/pai/proc/initialize_dynamic_chassis_icons() + . = list() + var/icon/curr //for inserts + + //This is a horrible system and I wish I was not as lazy and did something smarter, like just generating a new icon in memory which is probably more efficient. + + //Basic /tg/ cyborgs + .["Cyborg - Engineering (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "engineer"), HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Engineering (loaderborg)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "loaderborg"), HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Engineering (handyeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "handyeng"), HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Engineering (sleekeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "sleekeng"), HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Engineering (marinaeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinaeng"), HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Medical (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "medical"), HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Medical (marinamed)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinamed"), HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Medical (eyebotmed)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "eyebotmed"), HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Security (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "sec"), HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Security (sleeksec)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "sleeksec"), HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Security (marinasec)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinasec"), HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Clown (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "clown"), HOLOFORM_FILTER_PAI, FALSE) + + //Citadel dogborgs + //Engi + curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-rest"), "rest") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-sit"), "sit") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-bellyup"), "bellyup") + process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Engineering (dog - valeeng)"] = curr + curr = icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-rest"), "rest") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-sit"), "sit") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-bellyup"), "bellyup") + process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Engineering (dog - pupdozer)"] = curr + //Med + curr = icon('modular_citadel/icons/mob/widerobot.dmi', "medihound") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-rest"), "rest") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-sit"), "sit") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-bellyup"), "bellyup") + process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Medical (dog - medihound)"] = curr + curr = icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-rest"), "rest") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-sit"), "sit") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-bellyup"), "bellyup") + process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Medical (dog - medihounddark)"] = curr + curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valemed") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-rest"), "rest") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-sit"), "sit") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-bellyup"), "bellyup") + process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Medical (dog - valemed)"] = curr + //Sec + curr = icon('modular_citadel/icons/mob/widerobot.dmi', "k9") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-rest"), "rest") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-sit"), "sit") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-bellyup"), "bellyup") + process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Security (dog - k9)"] = curr + curr = icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-rest"), "rest") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-sit"), "sit") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-bellyup"), "bellyup") + process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Security (dog - k9dark)"] = curr + curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valesec") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-rest"), "rest") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-sit"), "sit") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-bellyup"), "bellyup") + process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Security (dog - valesec)"] = curr + //Service + curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-rest"), "rest") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-sit"), "sit") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-bellyup"), "bellyup") + process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Service (dog - valeserv)"] = curr + curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-rest"), "rest") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-sit"), "sit") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-bellyup"), "bellyup") + process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Service (dog - valeservdark)"] = curr + //Sci + curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valesci") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-rest"), "rest") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-sit"), "sit") + curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-bellyup"), "bellyup") + process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE) + .["Cyborg - Science (dog - valesci)"] = curr + //Misc + .["Cyborg - Misc (dog - blade)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/widerobot.dmi', "blade"), HOLOFORM_FILTER_PAI, FALSE) + +/mob/living/silicon/pai/proc/default_chassis_pixel_offsets_x() + . = list() + //Engi + .["Cyborg - Engineering (dog - valeeng)"] = -16 + .["Cyborg - Engineering (dog - pupdozer)"] = -16 + //Med + .["Cyborg - Medical (dog - medihound)"] = -16 + .["Cyborg - Medical (dog - medihounddark)"] = -16 + .["Cyborg - Medical (dog - valemed)"] = -16 + //Sec + .["Cyborg - Security (dog - k9)"] = -16 + .["Cyborg - Security (dog - valesec)"] = -16 + .["Cyborg - Security (dog - k9dark)"] = -16 + //Service + .["Cyborg - Service (dog - valeserv)"] = -16 + .["Cyborg - Service (dog - valeservdark)"] = -16 + //Sci + .["Cyborg - Security (dog - valesci)"] = -16 + //Misc + .["Cyborg - Misc (dog - blade)"] = -16 diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm index 5ac841326b..c727e100d8 100644 --- a/code/modules/mob/living/silicon/pai/pai_defense.dm +++ b/code/modules/mob/living/silicon/pai/pai_defense.dm @@ -8,9 +8,10 @@ return take_holo_damage(50/severity) Knockdown(400/severity) - silent = max(30/severity, silent) + silent = max((3 MINUTES)/severity, silent) if(holoform) fold_in(force = TRUE) + emitter_next_use = world.time + emitter_emp_cd //Need more effects that aren't instadeath or permanent law corruption. /mob/living/silicon/pai/ex_act(severity, target) diff --git a/code/modules/mob/living/silicon/pai/pai_shell.dm b/code/modules/mob/living/silicon/pai/pai_shell.dm index 164a3e7389..6577c26402 100644 --- a/code/modules/mob/living/silicon/pai/pai_shell.dm +++ b/code/modules/mob/living/silicon/pai/pai_shell.dm @@ -12,12 +12,11 @@ . = fold_in(force) return - if(emittersemicd) + if(world.time < emitter_next_use) to_chat(src, "Error: Holochassis emitters recycling. Please try again later.") return FALSE - emittersemicd = TRUE - addtimer(CALLBACK(src, .proc/emittercool), emittercd) + emitter_next_use = world.time + emittercd canmove = TRUE density = TRUE if(istype(card.loc, /obj/item/pda)) @@ -46,20 +45,17 @@ visible_message("[src] folds out its holochassis emitter and forms a holoshell around itself!") holoform = TRUE -/mob/living/silicon/pai/proc/emittercool() - emittersemicd = FALSE - /mob/living/silicon/pai/proc/fold_in(force = FALSE) - emittersemicd = TRUE - if(!force) - addtimer(CALLBACK(src, .proc/emittercool), emittercd) - else - addtimer(CALLBACK(src, .proc/emittercool), emitteroverloadcd) + emitter_next_use = world.time + (force? emitteroverloadcd : emittercd) icon_state = "[chassis]" if(!holoform) . = fold_out(force) return - visible_message("[src] deactivates its holochassis emitter and folds back into a compact card!") + if(force) + short_radio() + visible_message("[src] shorts out, collapsing back into their storage card, sparks emitted from their radio antenna!") + else + visible_message("[src] deactivates its holochassis emitter and folds back into a compact card!") stop_pulling() if(client) client.perspective = EYE_PERSPECTIVE @@ -78,28 +74,39 @@ if(!isturf(loc) && loc != card) to_chat(src, "You can not change your holochassis composite while not on the ground or in your card!") return FALSE - var/choice = input(src, "What would you like to use for your holochassis composite?") as null|anything in possible_chassis - if(!choice) + var/list/choices = list("Preset - Basic", "Preset - Dynamic") + if(CONFIG_GET(flag/pai_custom_holoforms)) + choices += "Custom" + var/choicetype = input(src, "What type of chassis do you want to use?") as null|anything in choices + if(!choicetype) return FALSE - chassis = choice - icon_state = "[chassis]" - if(resting) - icon_state = "[chassis]_rest" + switch(choicetype) + if("Custom") + chassis = "custom" + if("Preset - Basic") + var/choice = input(src, "What would you like to use for your holochassis composite?") as null|anything in possible_chassis + if(!choice) + return FALSE + chassis = choice + if("Preset - Dynamic") + var/choice = input(src, "What would you like to use for your holochassis composite?") as null|anything in dynamic_chassis_icons + if(!choice) + return FALSE + chassis = "dynamic" + dynamic_chassis = choice + resist_a_rest(FALSE, TRUE) + update_icon() to_chat(src, "You switch your holochassis projection composite to [chassis]") /mob/living/silicon/pai/lay_down() - ..() - update_resting_icon(resting) - -/mob/living/silicon/pai/proc/update_resting_icon(rest) - if(rest) - icon_state = "[chassis]_rest" - else - icon_state = "[chassis]" + . = ..() if(loc != card) - visible_message("[src] [rest? "lays down for a moment..." : "perks up from the ground"]") + visible_message("[src] [resting? "lays down for a moment..." : "perks up from the ground"]") + update_icon() -/mob/living/silicon/pai/start_pulling(atom/movable/AM) +/mob/living/silicon/pai/start_pulling(atom/movable/AM, gs) + if(ispAI(AM)) + return ..() return FALSE /mob/living/silicon/pai/proc/toggle_integrated_light() @@ -122,3 +129,22 @@ to_chat(user, "[src]'s current form isn't able to be carried!") return FALSE return ..() + +/mob/living/silicon/pai/verb/toggle_chassis_sit() + set name = "Toggle Chassis Sit" + set category = "IC" + set desc = "Whether or not to try to use a sitting icon versus a resting icon. Takes priority over belly-up resting." + dynamic_chassis_sit = !dynamic_chassis_sit + to_chat(usr, "You are now [dynamic_chassis_sit? "sitting" : "lying down"].") + update_icon() + +/mob/living/silicon/pai/verb/toggle_chassis_bellyup() + set name = "Toggle Chassis Belly Up" + set category = "IC" + set desc = "Whether or not to try to use a belly up icon while resting. Overridden by sitting." + dynamic_chassis_bellyup = !dynamic_chassis_bellyup + to_chat(usr, "You are now lying on your [dynamic_chassis_bellyup? "back" : "front"].") + update_icon() + +/mob/living/silicon/pai/can_buckle_others(mob/living/target, atom/buckle_to) + return ispAI(target) && ..() diff --git a/code/modules/mob/living/silicon/pai/say.dm b/code/modules/mob/living/silicon/pai/say.dm index 9f07900a25..deedaa1382 100644 --- a/code/modules/mob/living/silicon/pai/say.dm +++ b/code/modules/mob/living/silicon/pai/say.dm @@ -5,4 +5,11 @@ ..(message) /mob/living/silicon/pai/binarycheck() - return 0 + return FALSE + +/mob/living/silicon/pai/radio(message, message_mode, list/spans, language) + if((message_mode == "robot") || (message_mode in GLOB.radiochannels)) + if(radio_short) + to_chat(src, "Your radio is shorted out!") + return ITALICS | REDUCE_RANGE + return ..() diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 9882d028d1..3ea018ccf6 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -283,7 +283,10 @@ dat += "Refresh
    " // Built-in dat += "Directives
    " - dat += "Radio Configuration
    " + if(radio_short) + dat += "\[RADIO SHORTED - Recalibrating!\]" + else + dat += "Radio Configuration
    " dat += "Screen Display
    " //dat += "Text Messaging
    " dat += "
    " diff --git a/code/modules/mob/living/silicon/pai/update_icon.dm b/code/modules/mob/living/silicon/pai/update_icon.dm new file mode 100644 index 0000000000..9e40b71c5d --- /dev/null +++ b/code/modules/mob/living/silicon/pai/update_icon.dm @@ -0,0 +1,27 @@ +/mob/living/silicon/pai/update_icon() + if(chassis == "custom") //Make sure custom exists if it's set to custom + custom_holoform_icon = client?.prefs?.get_filtered_holoform(HOLOFORM_FILTER_PAI) + if(!custom_holoform_icon) + chassis = pick(possible_chassis - "custom") + if(chassis == "dynamic") //handle dynamic generated icons + icon = dynamic_chassis_icons[dynamic_chassis] + var/list/states = icon_states(icon) + icon_state = "" + if(resting) //The next line is some bullshit but I can make it worse if you want and make it a single line instead of four.. :) + if(dynamic_chassis_sit && ("sit") in states) + icon_state = "sit" + else if(dynamic_chassis_bellyup && ("bellyup") in states) + icon_state = "bellyup" + else if("rest" in states) + icon_state = "rest" + rotate_on_lying = FALSE + else if(chassis == "custom") + icon = custom_holoform_icon + icon_state = "" + rotate_on_lying = TRUE + else + icon = initial(icon) + icon_state = "[chassis][resting? "_rest" : (stat == DEAD? "_dead" : "")]" + rotate_on_lying = FALSE + pixel_x = ((chassis == "dynamic") && chassis_pixel_offsets_x[dynamic_chassis]) || 0 + update_transform() diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 5df24406c5..5f7d3ca243 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1298,4 +1298,12 @@ /mob/living/silicon/robot/adjustStaminaLossBuffered(amount, updating_health = 1) if(istype(cell)) - cell.charge -= amount*5 \ No newline at end of file + cell.charge -= amount*5 + +/mob/living/silicon/robot/verb/viewmanifest() + set category = "Robot Commands" + set name = "View Crew Manifest" + + if(usr.stat == DEAD) + return //won't work if dead + ai_roster() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index b9cee43138..699105ac4c 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -389,6 +389,15 @@ if (aicamera) return aicamera.selectpicture(user) +/mob/living/silicon/proc/ai_roster() + var/dat = "Crew RosterCrew Roster:

    " + + dat += GLOB.data_core.get_manifest() + dat += "" + + src << browse(dat, "window=airoster") + onclose(src, "airoster") + /mob/living/silicon/update_transform() var/matrix/ntransform = matrix(transform) //aka transform.Copy() var/changed = 0 diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index 396c6de166..cae707ce46 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -318,9 +318,9 @@ sleep(50) if(mode == BOT_REPAIRING && src.loc == target_turf) if(autotile) //Build the floor and include a tile. - target_turf.PlaceOnTop(/turf/open/floor/plasteel) + target_turf.PlaceOnTop(/turf/open/floor/plasteel, flags = CHANGETURF_INHERIT_AIR) else //Build a hull plating without a floor tile. - target_turf.PlaceOnTop(/turf/open/floor/plating) + target_turf.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) else var/turf/open/floor/F = target_turf @@ -334,7 +334,7 @@ if(mode == BOT_REPAIRING && F && src.loc == F) F.broken = 0 F.burnt = 0 - F.PlaceOnTop(/turf/open/floor/plasteel) + F.PlaceOnTop(/turf/open/floor/plasteel, flags = CHANGETURF_INHERIT_AIR) if(replacetiles && F.type != initial(tiletype.turf_type) && specialtiles && !isplatingturf(F)) anchored = TRUE @@ -345,7 +345,7 @@ if(mode == BOT_REPAIRING && F && src.loc == F) F.broken = 0 F.burnt = 0 - F.PlaceOnTop(initial(tiletype.turf_type)) + F.PlaceOnTop(initial(tiletype.turf_type), flags = CHANGETURF_INHERIT_AIR) specialtiles -= 1 if(specialtiles == 0) speak("Requesting refill of custom floortiles to continue replacing.") diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 51a9d8d62b..c2c9f5a71f 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -486,10 +486,7 @@ Difficulty: Very Hard if(isturf(Stuff)) var/turf/T = Stuff if((isspaceturf(T) || isfloorturf(T)) && NewTerrainFloors) - var/turf/open/O = T.ChangeTurf(NewTerrainFloors) - if(O.air) - var/datum/gas_mixture/G = O.air - G.copy_from_turf(O) + var/turf/open/O = T.ChangeTurf(NewTerrainFloors, flags = CHANGETURF_INHERIT_AIR) if(prob(florachance) && NewFlora.len && !is_blocked_turf(O, TRUE)) var/atom/Picked = pick(NewFlora) new Picked(O) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/necropolis_tendril.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/necropolis_tendril.dm index e3c81b2639..27ebb71c0e 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/necropolis_tendril.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/necropolis_tendril.dm @@ -91,5 +91,5 @@ visible_message("The tendril falls inward, the ground around it widening into a yawning chasm!") for(var/turf/T in range(2,src)) if(!T.density) - T.TerraformTurf(/turf/open/chasm/lavaland, /turf/open/chasm/lavaland) + T.TerraformTurf(/turf/open/chasm/lavaland, /turf/open/chasm/lavaland, flags = CHANGETURF_INHERIT_AIR) qdel(src) diff --git a/code/modules/mob/living/update_icons.dm b/code/modules/mob/living/update_icons.dm new file mode 100644 index 0000000000..8bf0dc98a9 --- /dev/null +++ b/code/modules/mob/living/update_icons.dm @@ -0,0 +1,26 @@ +//IMPORTANT: Multiple animate() calls do not stack well, so try to do them all at once if you can. +/mob/living/update_transform() + var/matrix/ntransform = matrix(transform) //aka transform.Copy() + var/final_pixel_y = pixel_y + var/final_dir = dir + var/changed = 0 + if(lying != lying_prev && rotate_on_lying) + changed++ + ntransform.TurnTo(lying_prev,lying) + if(lying == 0) //Lying to standing + final_pixel_y = get_standard_pixel_y_offset() + else //if(lying != 0) + if(lying_prev == 0) //Standing to lying + pixel_y = get_standard_pixel_y_offset() + final_pixel_y = get_standard_pixel_y_offset(lying) + if(dir & (EAST|WEST)) //Facing east or west + final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass + + if(resize != RESIZE_DEFAULT_SIZE) + changed++ + ntransform.Scale(resize) + resize = RESIZE_DEFAULT_SIZE + + if(changed) + animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, dir = final_dir, easing = EASE_IN|EASE_OUT) + setMovetype(movement_type & ~FLOATING) // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life(). diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 427e30f2e2..fad9431b61 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -802,11 +802,14 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) //can the mob be buckled to something by default? /mob/proc/can_buckle() - return 1 + return TRUE //can the mob be unbuckled from something by default? /mob/proc/can_unbuckle() - return 1 + return TRUE + +/mob/proc/can_buckle_others(mob/living/target, atom/buckle_to) + return TRUE //Can the mob interact() with an atom? /mob/proc/can_interact_with(atom/A) diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 173d9721a9..791db320a2 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -93,7 +93,7 @@ /obj/item/projectile/magic/door/proc/CreateDoor(turf/T) var/door_type = pick(door_types) var/obj/structure/mineral_door/D = new door_type(T) - T.ChangeTurf(/turf/open/floor/plating) + T.ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) D.Open() /obj/item/projectile/magic/door/proc/OpenDoor(var/obj/machinery/door/D) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 512b8a21a1..bd6eb4244c 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -1870,133 +1870,82 @@ reagent_state = LIQUID color = "#b51d05" taste_description = "carpet" // Your tounge feels furry. + var/carpet_type = /turf/open/floor/carpet /datum/reagent/carpet/reaction_turf(turf/T, reac_volume) if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet) + F.PlaceOnTop(carpet_type, flags = CHANGETURF_INHERIT_AIR) ..() /datum/reagent/carpet/black name = "Liquid Black Carpet" id = "blackcarpet" color = "#363636" - -/datum/reagent/carpet/black/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet/black) - ..() + carpet_type = /turf/open/floor/carpet/black /datum/reagent/carpet/blackred name = "Liquid Red Black Carpet" id = "blackredcarpet" color = "#342125" - -/datum/reagent/carpet/blackred/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet/blackred) - ..() + carpet_type = /turf/open/floor/carpet/blackred /datum/reagent/carpet/monochrome name = "Liquid Monochrome Carpet" id = "monochromecarpet" color = "#b4b4b4" - -/datum/reagent/carpet/monochrome/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet/monochrome) - ..() + carpet_type = /turf/open/floor/carpet/monochrome /datum/reagent/carpet/blue name = "Liquid Blue Carpet" id = "bluecarpet" color = "#1256ff" - -/datum/reagent/carpet/blue/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet/blue) - ..() + carpet_type = /turf/open/floor/carpet/blue /datum/reagent/carpet/cyan name = "Liquid Cyan Carpet" id = "cyancarpet" color = "#3acfb9" - -/datum/reagent/carpet/cyan/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet/cyan) - ..() + carpet_type = /turf/open/floor/carpet/cyan /datum/reagent/carpet/green name = "Liquid Green Carpet" id = "greencarpet" color = "#619b62" - -/datum/reagent/carpet/green/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet/green) - ..() + carpet_type = /turf/open/floor/carpet/green /datum/reagent/carpet/orange name = "Liquid Orange Carpet" id = "orangecarpet" color = "#cc7900" - -/datum/reagent/carpet/orange/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet/orange) - ..() + carpet_type = /turf/open/floor/carpet/orange /datum/reagent/carpet/purple name = "Liquid Purple Carpet" id = "purplecarpet" color = "#6d3392" + carpet_type = /turf/open/floor/carpet/purple -/datum/reagent/carpet/purple/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet/purple) - ..() /datum/reagent/carpet/red name = "Liquid Red Carpet" id = "redcarpet" color = "#871515" + carpet_type = /turf/open/floor/carpet/red -/datum/reagent/carpet/red/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet/red) - ..() /datum/reagent/carpet/royalblack name = "Liquid Royal Black Carpet" id = "royalblackcarpet" color = "#483d05" + carpet_type = /turf/open/floor/carpet/royalblack -/datum/reagent/carpet/royalblack/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet/royalblack) - ..() /datum/reagent/carpet/royalblue name = "Liquid Royal Blue Carpet" id = "royalbluecarpet" color = "#24227e" - -/datum/reagent/carpet/royalblue/reaction_turf(turf/T, reac_volume) - if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) - var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/carpet/royalblue) - ..() + carpet_type = /turf/open/floor/carpet/royalblue // Virology virus food chems. diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm index c170858d98..36621aa662 100644 --- a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm @@ -53,7 +53,7 @@ if(isplatingturf(T)) var/turf/open/floor/plating/F = T if(prob(10 + F.burnt + 5*F.broken)) //broken or burnt plating is more susceptible to being destroyed - F.ScrapeAway() + F.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) if(isfloorturf(T)) var/turf/open/floor/F = T if(prob(reac_volume)) diff --git a/code/modules/reagents/chemistry/recipes/special.dm b/code/modules/reagents/chemistry/recipes/special.dm index 59394e6e4b..08d07b8a52 100644 --- a/code/modules/reagents/chemistry/recipes/special.dm +++ b/code/modules/reagents/chemistry/recipes/special.dm @@ -123,24 +123,11 @@ GLOBAL_LIST_INIT(food_reagents, build_reagents_to_food()) //reagentid = related /datum/chemical_reaction/randomized/proc/LoadOldRecipe(recipe_data) created = text2num(recipe_data["timestamp"]) - - var/req_reag = unwrap_reagent_list(recipe_data["required_reagents"]) - if(!req_reag) - return FALSE - required_reagents = req_reag - - var/req_catalysts = unwrap_reagent_list(recipe_data["required_catalysts"]) - if(!req_catalysts) - return FALSE - required_catalysts = req_catalysts - + required_reagents = SANITIZE_LIST(recipe_data["required_reagents"]) + required_catalysts = SANITIZE_LIST(recipe_data["required_catalysts"]) required_temp = recipe_data["required_temp"] is_cold_recipe = recipe_data["is_cold_recipe"] - - var/temp_results = unwrap_reagent_list(recipe_data["results"]) - if(!temp_results) - return FALSE - results = temp_results + results = SANITIZE_LIST(recipe_data["results"]) var/containerpath = text2path(recipe_data["required_container"]) if(!containerpath) return FALSE diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm index 7701e58616..46ddb1e2ed 100644 --- a/code/modules/recycling/conveyor2.dm +++ b/code/modules/recycling/conveyor2.dm @@ -31,7 +31,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id) /obj/machinery/conveyor/inverted/Initialize(mapload) . = ..() if(mapload && !(dir in GLOB.diagonals)) - log_game("### MAPPING ERROR: [src] at [AREACOORD(src)] spawned without using a diagonal dir. Please replace with a normal version.") + log_mapping("[src] at [AREACOORD(src)] spawned without using a diagonal dir. Please replace with a normal version.") // Auto conveyour is always on unless unpowered diff --git a/code/modules/ruins/objects_and_mobs/sin_ruins.dm b/code/modules/ruins/objects_and_mobs/sin_ruins.dm index caa2ca7adf..76897b5276 100644 --- a/code/modules/ruins/objects_and_mobs/sin_ruins.dm +++ b/code/modules/ruins/objects_and_mobs/sin_ruins.dm @@ -108,7 +108,7 @@ if (levels.len) dest = locate(T.x, T.y, pick(levels)) - T.ChangeTurf(/turf/open/chasm) + T.ChangeTurf(/turf/open/chasm, flags = CHANGETURF_INHERIT_AIR) var/turf/open/chasm/C = T C.set_target(dest) C.drop(user) diff --git a/code/modules/spells/spell_types/conjure.dm b/code/modules/spells/spell_types/conjure.dm index 3ebded7487..38a2d5bddd 100644 --- a/code/modules/spells/spell_types/conjure.dm +++ b/code/modules/spells/spell_types/conjure.dm @@ -31,7 +31,7 @@ if(ispath(summoned_object_type, /turf)) var/turf/O = spawn_place var/N = summoned_object_type - O.ChangeTurf(N) + O.ChangeTurf(N, flags = CHANGETURF_INHERIT_AIR) else var/atom/summoned_object = new summoned_object_type(spawn_place) diff --git a/code/modules/spells/spell_types/devil.dm b/code/modules/spells/spell_types/devil.dm index 8b8328abbe..d97d466e4d 100644 --- a/code/modules/spells/spell_types/devil.dm +++ b/code/modules/spells/spell_types/devil.dm @@ -234,7 +234,7 @@ dancefloor_exists = FALSE for(var/i in 1 to dancefloor_turfs.len) var/turf/T = dancefloor_turfs[i] - T.ChangeTurf(dancefloor_turfs_types[i]) + T.ChangeTurf(dancefloor_turfs_types[i], flags = CHANGETURF_INHERIT_AIR) else var/list/funky_turfs = RANGE_TURFS(1, user) for(var/turf/closed/solid in funky_turfs) @@ -248,7 +248,7 @@ var/turf/T = t dancefloor_turfs[i] = T dancefloor_turfs_types[i] = T.type - T.ChangeTurf((i % 2 == 0) ? /turf/open/floor/light/colour_cycle/dancefloor_a : /turf/open/floor/light/colour_cycle/dancefloor_b) + T.ChangeTurf((i % 2 == 0) ? /turf/open/floor/light/colour_cycle/dancefloor_a : /turf/open/floor/light/colour_cycle/dancefloor_b, flags = CHANGETURF_INHERIT_AIR) i++ /datum/effect_system/smoke_spread/transparent/dancefloor_devil diff --git a/html/changelogs/AutoChangeLog-pr-10189.yml b/html/changelogs/AutoChangeLog-pr-10189.yml new file mode 100644 index 0000000000..5889e127d2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10189.yml @@ -0,0 +1,5 @@ +author: "Putnam3145" +delete-after: True +changes: + - rscadd: "Dynamic storytellers, a new voting paradigm for dynamic" + - rscadd: "Support for approval voting and condorcet (ranked choice) voting in server votes" diff --git a/html/changelogs/AutoChangeLog-pr-10191.yml b/html/changelogs/AutoChangeLog-pr-10191.yml new file mode 100644 index 0000000000..c1366727bd --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10191.yml @@ -0,0 +1,5 @@ +author: "kevinz000" +delete-after: True +changes: + - rscadd: "Custom holoforms have been added for pAIs and AIs. oh and cyborg holograms of specific modules too." + - balance: "pais are no longer indestructible-flagged while in card form. pai radios now short out if they are forcefully collapsed from damage." diff --git a/html/changelogs/AutoChangeLog-pr-10238.yml b/html/changelogs/AutoChangeLog-pr-10238.yml new file mode 100644 index 0000000000..dda40c86c9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10238.yml @@ -0,0 +1,7 @@ +author: "SpaceManiac, bobbahbrown, ShizCalev, SpaceManiac (ported by Ghommie)" +delete-after: True +changes: + - code_imp: "It is now possible to set a different most-base-turf per z-level." + - spellcheck: "Removed unlawful reference to Disney's Star Wars franchise in map logging." + - tweak: "Moved mapping related errors to their own log file." + - bugfix: "Destruction on Lavaland will no longer reveal space in rare situations." diff --git a/html/changelogs/AutoChangeLog-pr-10255.yml b/html/changelogs/AutoChangeLog-pr-10255.yml new file mode 100644 index 0000000000..ebaccd0af6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10255.yml @@ -0,0 +1,4 @@ +author: "Xantholne" +delete-after: True +changes: + - bugfix: "Christmas clothes that where missing stuff should work again" diff --git a/html/changelogs/AutoChangeLog-pr-10270.yml b/html/changelogs/AutoChangeLog-pr-10270.yml new file mode 100644 index 0000000000..b47a249f9a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10270.yml @@ -0,0 +1,4 @@ +author: "Ghommie" +delete-after: True +changes: + - bugfix: "Fixed the secret sauce recipe being randomized every round." diff --git a/html/changelogs/AutoChangeLog-pr-10388.yml b/html/changelogs/AutoChangeLog-pr-10388.yml new file mode 100644 index 0000000000..fd06a5db3f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10388.yml @@ -0,0 +1,4 @@ +author: "CameronWoof" +delete-after: True +changes: + - rscadd: "Robots can now check the crew manifest from anywhere with the \"View Crew Manifest\" verb." diff --git a/html/changelogs/AutoChangeLog-pr-10399.yml b/html/changelogs/AutoChangeLog-pr-10399.yml new file mode 100644 index 0000000000..cbdb00630c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10399.yml @@ -0,0 +1,4 @@ +author: "kevinz000" +delete-after: True +changes: + - balance: "Cargo passive gen is now 500 down from 750." diff --git a/html/changelogs/AutoChangeLog-pr-10403.yml b/html/changelogs/AutoChangeLog-pr-10403.yml new file mode 100644 index 0000000000..321789a546 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10403.yml @@ -0,0 +1,5 @@ +author: "r4d6" +delete-after: True +changes: + - rscadd: "Added a way to make opaque plastic flaps +change: Change the already existing flap recipe to show that they are see-through" diff --git a/html/changelogs/AutoChangeLog-pr-10432.yml b/html/changelogs/AutoChangeLog-pr-10432.yml new file mode 100644 index 0000000000..a0b40baa45 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-10432.yml @@ -0,0 +1,4 @@ +author: "Trilbyspaceclone" +delete-after: True +changes: + - bugfix: "missing sprites with crushed cans" diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index e13ce10347..51e9453087 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index 5d243f93b8..2e53bb24db 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/janitor.dmi b/icons/obj/janitor.dmi index 4886cb8441..cb3870dc61 100644 Binary files a/icons/obj/janitor.dmi and b/icons/obj/janitor.dmi differ diff --git a/modular_citadel/code/modules/mob/cit_emotes.dm b/modular_citadel/code/modules/mob/cit_emotes.dm index 7e7a77538a..d0ad0d3232 100644 --- a/modular_citadel/code/modules/mob/cit_emotes.dm +++ b/modular_citadel/code/modules/mob/cit_emotes.dm @@ -70,113 +70,124 @@ message = "makes a very loud noise." . = ..() -/datum/emote/sound/carbon/snap - key = "snap" - key_third_person = "snaps" - muzzle_ignore = TRUE - restraint_check = TRUE - emote_type = EMOTE_AUDIBLE - sound = 'sound/effects/snap01.ogg' - mob_type_allowed_typecache = list(/mob/living/carbon/) - - /datum/emote/living/snap key = "snap" key_third_person = "snaps" message = "snaps their fingers." emote_type = EMOTE_AUDIBLE + muzzle_ignore = TRUE + restraint_check = TRUE + mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/snap/run_emote(mob/living/user, params) - if(ishuman(user)) - if(user.nextsoundemote >= world.time) - return - user.nextsoundemote = world.time + 7 - playsound(user, 'modular_citadel/sound/voice/snap.ogg', 50, 1, -1) - . = ..() + if(!(. = ..())) + return + if(user.nextsoundemote >= world.time) + return + user.nextsoundemote = world.time + 7 + playsound(user, 'modular_citadel/sound/voice/snap.ogg', 50, 1, -1) /datum/emote/living/snap2 key = "snap2" key_third_person = "snaps twice" message = "snaps twice." emote_type = EMOTE_AUDIBLE + muzzle_ignore = TRUE + restraint_check = TRUE + mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/snap2/run_emote(mob/living/user, params) - if(ishuman(user)) - if(user.nextsoundemote >= world.time) - return - user.nextsoundemote = world.time + 7 - playsound(user, 'modular_citadel/sound/voice/snap2.ogg', 50, 1, -1) - . = ..() + if(!(. = ..())) + return + if(user.nextsoundemote >= world.time) + return + user.nextsoundemote = world.time + 7 + playsound(user, 'modular_citadel/sound/voice/snap2.ogg', 50, 1, -1) /datum/emote/living/snap3 key = "snap3" key_third_person = "snaps thrice" message = "snaps thrice." emote_type = EMOTE_AUDIBLE + muzzle_ignore = TRUE + restraint_check = TRUE + mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/snap3/run_emote(mob/living/user, params) - if(ishuman(user)) - if(user.nextsoundemote >= world.time) - return - user.nextsoundemote = world.time + 7 - playsound(user, 'modular_citadel/sound/voice/snap3.ogg', 50, 1, -1) - . = ..() + if(!(. = ..())) + return + if(user.nextsoundemote >= world.time) + return + user.nextsoundemote = world.time + 7 + playsound(user, 'modular_citadel/sound/voice/snap3.ogg', 50, 1, -1) /datum/emote/living/awoo key = "awoo" key_third_person = "lets out an awoo" message = "lets out an awoo!" emote_type = EMOTE_AUDIBLE + muzzle_ignore = FALSE + restraint_check = FALSE + mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/awoo/run_emote(mob/living/user, params) - if(ishuman(user)) - if(user.nextsoundemote >= world.time) - return - user.nextsoundemote = world.time + 7 - playsound(user, 'modular_citadel/sound/voice/awoo.ogg', 50, 1, -1) - . = ..() + if(!(. = ..())) + return + if(user.nextsoundemote >= world.time) + return + user.nextsoundemote = world.time + 7 + playsound(user, 'modular_citadel/sound/voice/awoo.ogg', 50, 1, -1) /datum/emote/living/nya key = "nya" key_third_person = "lets out a nya" message = "lets out a nya!" emote_type = EMOTE_AUDIBLE + muzzle_ignore = FALSE + restraint_check = FALSE + mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/nya/run_emote(mob/living/user, params) - if(ishuman(user)) - if(user.nextsoundemote >= world.time) - return - user.nextsoundemote = world.time + 7 - playsound(user, 'modular_citadel/sound/voice/nya.ogg', 50, 1, -1) - . = ..() + if(!(. = ..())) + return + if(user.nextsoundemote >= world.time) + return + user.nextsoundemote = world.time + 7 + playsound(user, 'modular_citadel/sound/voice/nya.ogg', 50, 1, -1) /datum/emote/living/weh key = "weh" key_third_person = "lets out a weh" message = "lets out a weh!" emote_type = EMOTE_AUDIBLE + muzzle_ignore = FALSE + restraint_check = FALSE + mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/weh/run_emote(mob/living/user, params) - if(ishuman(user)) - if(user.nextsoundemote >= world.time) - return - user.nextsoundemote = world.time + 7 - playsound(user, 'modular_citadel/sound/voice/weh.ogg', 50, 1, -1) - . = ..() + if(!(. = ..())) + return + if(user.nextsoundemote >= world.time) + return + user.nextsoundemote = world.time + 7 + playsound(user, 'modular_citadel/sound/voice/weh.ogg', 50, 1, -1) /datum/emote/living/peep key = "peep" key_third_person = "peeps like a bird" message = "peeps like a bird!" emote_type = EMOTE_AUDIBLE + muzzle_ignore = FALSE + restraint_check = FALSE + mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/peep/run_emote(mob/living/user, params) - if(ishuman(user)) - if(user.nextsoundemote >= world.time) - return - user.nextsoundemote = world.time + 7 - playsound(user, 'modular_citadel/sound/voice/peep.ogg', 50, 1, -1) - . = ..() + if(!(. = ..())) + return + if(user.nextsoundemote >= world.time) + return + user.nextsoundemote = world.time + 7 + playsound(user, 'modular_citadel/sound/voice/peep.ogg', 50, 1, -1) /datum/emote/living/dab key = "dab" @@ -190,41 +201,49 @@ key_third_person = "lets out a tiny squeak" message = "lets out a tiny squeak!" emote_type = EMOTE_AUDIBLE - mob_type_allowed_typecache = list(/mob/living/carbon) + muzzle_ignore = FALSE + restraint_check = FALSE + mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/mothsqueak/run_emote(mob/living/user, params) - if(ishuman(user)) - if(user.nextsoundemote >= world.time) - return - user.nextsoundemote = world.time + 7 - playsound(user, 'modular_citadel/sound/voice/mothsqueak.ogg', 50, 1, -1) - . = ..() + if(!(. = ..())) + return + if(user.nextsoundemote >= world.time) + return + user.nextsoundemote = world.time + 7 + playsound(user, 'modular_citadel/sound/voice/mothsqueak.ogg', 50, 1, -1) /datum/emote/living/merp key = "merp" key_third_person = "merps" message = "merps!" emote_type = EMOTE_AUDIBLE + muzzle_ignore = FALSE + restraint_check = FALSE + mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/merp/run_emote(mob/living/user, params) - if(ishuman(user)) - if(user.nextsoundemote >= world.time) - return - user.nextsoundemote = world.time + 7 - playsound(user, 'modular_citadel/sound/voice/merp.ogg', 50, 1, -1) - . = ..() + if(!(. = ..())) + return + if(user.nextsoundemote >= world.time) + return + user.nextsoundemote = world.time + 7 + playsound(user, 'modular_citadel/sound/voice/merp.ogg', 50, 1, -1) /datum/emote/living/bark key = "bark" key_third_person = "barks" message = "barks!" emote_type = EMOTE_AUDIBLE + muzzle_ignore = FALSE + restraint_check = FALSE + mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai) /datum/emote/living/bark/run_emote(mob/living/user, params) - if(ishuman(user)) - if(user.nextsoundemote >= world.time) - return - user.nextsoundemote = world.time + 7 - var/sound = pick('modular_citadel/sound/voice/bark1.ogg', 'modular_citadel/sound/voice/bark2.ogg') - playsound(user, sound, 50, 1, -1) - . = ..() \ No newline at end of file + if(!(. = ..())) + return + if(user.nextsoundemote >= world.time) + return + user.nextsoundemote = world.time + 7 + var/sound = pick('modular_citadel/sound/voice/bark1.ogg', 'modular_citadel/sound/voice/bark2.ogg') + playsound(user, sound, 50, 1, -1) diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/astrogen.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/astrogen.dm index d7ff42a9eb..7e094eddda 100644 --- a/modular_citadel/code/modules/reagents/chemistry/reagents/astrogen.dm +++ b/modular_citadel/code/modules/reagents/chemistry/reagents/astrogen.dm @@ -54,7 +54,7 @@ I'd like to point out from my calculations it'll take about 60-80 minutes to die /datum/reagent/fermi/astral/reaction_turf(turf/T, reac_volume) if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel)) var/turf/open/floor/F = T - F.PlaceOnTop(/turf/open/floor/fakespace) + F.PlaceOnTop(/turf/open/floor/fakespace, flags = CHANGETURF_INHERIT_AIR) ..() /datum/reagent/fermi/astral/reaction_obj(obj/O, reac_volume) diff --git a/tgstation.dme b/tgstation.dme index d92fbff5d9..7e7d69d6f1 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -43,6 +43,7 @@ #include "code\__DEFINES\diseases.dm" #include "code\__DEFINES\DNA.dm" #include "code\__DEFINES\donator_groupings.dm" +#include "code\__DEFINES\dynamic.dm" #include "code\__DEFINES\events.dm" #include "code\__DEFINES\exports.dm" #include "code\__DEFINES\fantasy_affixes.dm" @@ -108,6 +109,7 @@ #include "code\__DEFINES\typeids.dm" #include "code\__DEFINES\vehicles.dm" #include "code\__DEFINES\voreconstants.dm" +#include "code\__DEFINES\vote.dm" #include "code\__DEFINES\vv.dm" #include "code\__DEFINES\wall_dents.dm" #include "code\__DEFINES\wires.dm" @@ -118,6 +120,7 @@ #include "code\__HELPERS\areas.dm" #include "code\__HELPERS\AStar.dm" #include "code\__HELPERS\cmp.dm" +#include "code\__HELPERS\custom_holoforms.dm" #include "code\__HELPERS\dates.dm" #include "code\__HELPERS\donator_groupings.dm" #include "code\__HELPERS\files.dm" @@ -579,6 +582,7 @@ #include "code\game\gamemodes\dynamic\dynamic_rulesets_latejoin.dm" #include "code\game\gamemodes\dynamic\dynamic_rulesets_midround.dm" #include "code\game\gamemodes\dynamic\dynamic_rulesets_roundstart.dm" +#include "code\game\gamemodes\dynamic\dynamic_storytellers.dm" #include "code\game\gamemodes\extended\extended.dm" #include "code\game\gamemodes\gangs\dominator.dm" #include "code\game\gamemodes\gangs\dominator_countdown.dm" @@ -2108,6 +2112,7 @@ #include "code\modules\mob\living\say.dm" #include "code\modules\mob\living\status_procs.dm" #include "code\modules\mob\living\taste.dm" +#include "code\modules\mob\living\update_icons.dm" #include "code\modules\mob\living\ventcrawling.dm" #include "code\modules\mob\living\brain\brain.dm" #include "code\modules\mob\living\brain\brain_item.dm" @@ -2214,6 +2219,7 @@ #include "code\modules\mob\living\carbon\monkey\monkey_defense.dm" #include "code\modules\mob\living\carbon\monkey\punpun.dm" #include "code\modules\mob\living\carbon\monkey\update_icons.dm" +#include "code\modules\mob\living\silicon\custom_holoform.dm" #include "code\modules\mob\living\silicon\damage_procs.dm" #include "code\modules\mob\living\silicon\death.dm" #include "code\modules\mob\living\silicon\examine.dm" @@ -2245,6 +2251,7 @@ #include "code\modules\mob\living\silicon\pai\personality.dm" #include "code\modules\mob\living\silicon\pai\say.dm" #include "code\modules\mob\living\silicon\pai\software.dm" +#include "code\modules\mob\living\silicon\pai\update_icon.dm" #include "code\modules\mob\living\silicon\robot\death.dm" #include "code\modules\mob\living\silicon\robot\emote.dm" #include "code\modules\mob\living\silicon\robot\examine.dm"