diff --git a/code/__DEFINES/assert.dm b/code/__DEFINES/assert.dm new file mode 100644 index 000000000000..cff78107714c --- /dev/null +++ b/code/__DEFINES/assert.dm @@ -0,0 +1,13 @@ +#undef ASSERT + +/// Override BYOND's native ASSERT to optionally specify a message +#define ASSERT(condition, message...) \ + if (!(condition)) { \ + CRASH(assertion_message(__FILE__, __LINE__, #condition, ##message)) \ + } + +/proc/assertion_message(file, line, condition, message) + if (!isnull(message)) + message = " - [message]" + + return "[file]:[line]:Assertion failed: [condition][message]" diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index 3abd79708f7a..de7eb672e87b 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -23,6 +23,8 @@ #define T0C 273.15 // 0degC #define T20C 293.15 // 20degC +#define T90C 363.15 // 90degC +#define T120C 393.15 // 120degC #define TCMB 2.7 // -270.3degC #define ICE_COLONY_TEMPERATURE 223 //-50degC #define SOROKYNE_TEMPERATURE 223 // Same as Ice for now diff --git a/code/__DEFINES/conflict.dm b/code/__DEFINES/conflict.dm index 0820c709cdae..d69f0891ffa0 100644 --- a/code/__DEFINES/conflict.dm +++ b/code/__DEFINES/conflict.dm @@ -109,6 +109,7 @@ #define SHOES_SLOWDOWN -1 #define SLOWDOWN_ARMOR_NONE 0 +#define SLOWDOWN_ARMOR_SUPER_LIGHT 0.10 #define SLOWDOWN_ARMOR_VERY_LIGHT 0.20 #define SLOWDOWN_ARMOR_LIGHT 0.35 #define SLOWDOWN_ARMOR_MEDIUM 0.55 diff --git a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm index bab6064cfdbf..32717a2115f2 100644 --- a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm +++ b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm @@ -1,3 +1,5 @@ +///from base of mob/set_stat(): (new_stat, old_stat) +#define COMSIG_MOB_STATCHANGE "mob_statchange" /// From /obj/structure/machinery/door/airlock/proc/take_damage #define COMSIG_MOB_DESTROY_AIRLOCK "mob_destroy_airlock" @@ -128,3 +130,6 @@ /// From /obj/item/proc/pickup() : (obj/item/picked_up) #define COMSIG_MOB_PICKUP_ITEM "mob_pickup_item" + +/// Cancels all running cloaking effects on target +#define COMSIG_MOB_EFFECT_CLOAK_CANCEL "mob_effect_cloak_cancel" diff --git a/code/__DEFINES/dcs/signals/atom/signals_atom.dm b/code/__DEFINES/dcs/signals/atom/signals_atom.dm index cac9861d09cb..d9bd1202c159 100644 --- a/code/__DEFINES/dcs/signals/atom/signals_atom.dm +++ b/code/__DEFINES/dcs/signals/atom/signals_atom.dm @@ -51,3 +51,6 @@ /// Called when an atom is mouse dropped on another atom, from /client/MouseDrop: (atom/dropped_onto) #define COMSIG_ATOM_DROP_ON "atom_drop_on" + +/// Called when an atom has emp_act called on it, from /atom/emp_act: (severity) +#define COMSIG_ATOM_EMP_ACT "atom_emp_act" diff --git a/code/__DEFINES/dcs/signals/atom/signals_cell.dm b/code/__DEFINES/dcs/signals/atom/signals_cell.dm new file mode 100644 index 000000000000..75e13d8bfdfc --- /dev/null +++ b/code/__DEFINES/dcs/signals/atom/signals_cell.dm @@ -0,0 +1,26 @@ +/// (charge_amount) +#define COMSIG_CELL_USE_CHARGE "cell_use_charge" + #define COMPONENT_CELL_NO_USE_CHARGE (1<<0) + +/// (charge_amount) +#define COMSIG_CELL_ADD_CHARGE "cell_add_charge" + +#define COMSIG_CELL_START_TICK_DRAIN "cell_start_tick_drain" + +#define COMSIG_CELL_STOP_TICK_DRAIN "cell_stop_tick_drain" + +/// (mob/living/user) +#define COMSIG_CELL_TRY_RECHARGING "cell_try_recharging" + #define COMPONENT_CELL_NO_RECHARGE (1<<0) + +#define COMSIG_CELL_OUT_OF_CHARGE "cell_out_of_charge" + +/// (charge_amount) +#define COMSIG_CELL_CHECK_CHARGE "cell_check_charge" + #define COMPONENT_CELL_CHARGE_INSUFFICIENT (1<<0) + +#define COMSIG_CELL_TRY_INSERT_CELL "cell_try_insert_cell" + #define COMPONENT_CANCEL_CELL_INSERT (1<<0) + +/// (mob/living/user) +#define COMSIG_CELL_REMOVE_CELL "cell_remove_cell" diff --git a/code/__DEFINES/dcs/signals/atom/signals_item.dm b/code/__DEFINES/dcs/signals/atom/signals_item.dm index 64f4c24798e1..6024c0524992 100644 --- a/code/__DEFINES/dcs/signals/atom/signals_item.dm +++ b/code/__DEFINES/dcs/signals/atom/signals_item.dm @@ -66,5 +66,5 @@ /// from /obj/item/weapon/gun/proc/load_into_chamber() : () #define COMSIG_GUN_INTERRUPT_FIRE "gun_interrupt_fire" -//Additional procs on items that will be triggered right after the human finishes spawns in +//from /datum/authority/branch/role/proc/equip_role() #define COMSIG_POST_SPAWN_UPDATE "post_spawn_update" diff --git a/code/__DEFINES/dcs/signals/atom/signals_obj.dm b/code/__DEFINES/dcs/signals/atom/signals_obj.dm index aebd0d09d0d2..93579e068ec7 100644 --- a/code/__DEFINES/dcs/signals/atom/signals_obj.dm +++ b/code/__DEFINES/dcs/signals/atom/signals_obj.dm @@ -29,3 +29,5 @@ /// from /obj/proc/afterbuckle() #define COSMIG_OBJ_AFTER_BUCKLE "signal_obj_after_buckle" + +#define COMSIG_STRUCTURE_CRATE_SQUAD_LAUNCHED "structure_crate_squad_launched" diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm index 032a1891a808..dc5e70fcd5ec 100644 --- a/code/__DEFINES/dcs/signals/signals_global.dm +++ b/code/__DEFINES/dcs/signals/signals_global.dm @@ -60,8 +60,11 @@ #define COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING "!groundside_forsaken_handling" /// From -#define COMSIG_GLOB_YAUTJA_ARMORY_OPENED "yautja_armory_opened" +#define COMSIG_GLOB_YAUTJA_ARMORY_OPENED "!yautja_armory_opened" /// From /proc/biohazard_lockdown() -#define COMSIG_GLOB_RESEARCH_LOCKDOWN "research_lockdown_closed" -#define COMSIG_GLOB_RESEARCH_LIFT "research_lockdown_opened" +#define COMSIG_GLOB_RESEARCH_LOCKDOWN "!research_lockdown_closed" +#define COMSIG_GLOB_RESEARCH_LIFT "!research_lockdown_opened" + +/// From /obj/structure/machinery/power/fusion_engine/proc/set_overloading() : (set_overloading) +#define COMSIG_GLOB_GENERATOR_SET_OVERLOADING "!generator_set_overloading" diff --git a/code/__DEFINES/equipment.dm b/code/__DEFINES/equipment.dm index 5f8f27a65711..6628a5c925c2 100644 --- a/code/__DEFINES/equipment.dm +++ b/code/__DEFINES/equipment.dm @@ -80,10 +80,8 @@ #define CAN_DIG_SHRAPNEL (1<<11) /// whether it has an animated icon state of "[icon_state]_on" to be used during surgeries. #define ANIMATED_SURGICAL_TOOL (1<<12) -/// The item goes on top of tables, instead of into them with the overlay system -#define NOTABLEMERGE (1<<13) /// Has heat source but isn't 'on fire' and thus can be stored -#define IGNITING_ITEM (1<<14) +#define IGNITING_ITEM (1<<13) //========================================================================================== diff --git a/code/__DEFINES/hijack.dm b/code/__DEFINES/hijack.dm new file mode 100644 index 000000000000..85d4c227ae70 --- /dev/null +++ b/code/__DEFINES/hijack.dm @@ -0,0 +1,13 @@ +#define EVACUATION_TYPE_NONE 0 +#define EVACUATION_TYPE_ADDITIVE 1 +#define EVACUATION_TYPE_MULTIPLICATIVE 2 + +#define HIJACK_ANNOUNCE "ARES Emergency Procedures" +#define XENO_HIJACK_ANNOUNCE "You sense something unusual..." + +#define EVACUATION_STATUS_NOT_INITIATED 0 +#define EVACUATION_STATUS_INITIATED 1 + +#define HIJACK_OBJECTIVES_NOT_STARTED 0 +#define HIJACK_OBJECTIVES_STARTED 1 +#define HIJACK_OBJECTIVES_COMPLETE 2 diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm index 38e5693dcbe5..deee80c7a91d 100644 --- a/code/__DEFINES/hud.dm +++ b/code/__DEFINES/hud.dm @@ -23,3 +23,5 @@ #define NOTIFY_ATTACK "attack" #define NOTIFY_ORBIT "orbit" #define NOTIFY_JOIN_XENO "join_xeno" +#define NOTIFY_XENO_TACMAP "xeno_tacmap" +#define NOTIFY_USCM_TACMAP "uscm_tacmap" diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm index 89b88f67c038..56062cb0213b 100644 --- a/code/__DEFINES/job.dm +++ b/code/__DEFINES/job.dm @@ -11,6 +11,7 @@ #define SQUAD_MARINE_CRYO "Foxtrot" #define SQUAD_MARINE_INTEL "Intel" #define SQUAD_SOF "SOF" +#define SQUAD_CBRN "CBRN" // Job name defines #define JOB_SQUAD_MARINE "Rifleman" diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm index 88f194bb33df..764282d59765 100644 --- a/code/__DEFINES/keybinding.dm +++ b/code/__DEFINES/keybinding.dm @@ -14,6 +14,7 @@ #define COMSIG_KB_ADMIN_INVISIMINTOGGLE_DOWN "keybinding_admin_invisimintoggle_down" #define COMSIG_KB_ADMIN_DEADMIN_DOWN "keybinding_admin_deadmin_down" #define COMSIG_KB_ADMIN_READMIN_DOWN "keybinding_admin_readmin_down" +#define COMSIG_KB_ADMIN_MENTORSAY_DOWN "keybinding_admin_mentorsay_down" //Carbon #define COMSIG_KB_CARBON_HOLDRUNMOVEINTENT_DOWN "keybinding_carbon_holdrunmoveintent_down" @@ -87,6 +88,7 @@ #define COMSIG_KB_HUMAN_WEAPON_UNLOAD "keybinding_human_weapon_unload" #define COMSIG_KB_HUMAN_WEAPON_ATTACHMENT "keybinding_human_weapon_attachment" #define COMSIG_KB_HUMAN_WEAPON_ATTACHMENT_RAIL "keybinding_human_weapon_attachment_rail" +#define COMSIG_KB_HUMAN_WEAPON_SHOTGUN_TUBE "keybinding_human_weapon_shotgun_tube" #define COMSIG_KB_HUMAN_WEAPON_TOGGLE_IFF "keybinding_human_weapon_toggle_iff" diff --git a/code/__DEFINES/minimap.dm b/code/__DEFINES/minimap.dm index 71d0ed8e7445..003d723600c4 100644 --- a/code/__DEFINES/minimap.dm +++ b/code/__DEFINES/minimap.dm @@ -5,7 +5,17 @@ #define MINIMAP_FLAG_UPP (1<<3) #define MINIMAP_FLAG_CLF (1<<4) #define MINIMAP_FLAG_YAUTJA (1<<5) -#define MINIMAP_FLAG_ALL (1<<6) - 1 +#define MINIMAP_FLAG_XENO_CORRUPTED (1<<6) +#define MINIMAP_FLAG_XENO_ALPHA (1<<7) +#define MINIMAP_FLAG_XENO_BRAVO (1<<8) +#define MINIMAP_FLAG_XENO_CHARLIE (1<<9) +#define MINIMAP_FLAG_XENO_DELTA (1<<10) +#define MINIMAP_FLAG_XENO_FERAL (1<<11) +#define MINIMAP_FLAG_XENO_TAMED (1<<12) +#define MINIMAP_FLAG_XENO_MUTATED (1<<13) +#define MINIMAP_FLAG_XENO_FORSAKEN (1<<14) +#define MINIMAP_FLAG_XENO_RENEGADE (1<<15) +#define MINIMAP_FLAG_ALL (1<<16) - 1 ///Converts the overworld x and y to minimap x and y values #define MINIMAP_SCALE 2 @@ -77,9 +87,3 @@ GLOBAL_LIST_INIT(all_minimap_flags, bitfield2list(MINIMAP_FLAG_ALL)) #define TACMAP_BASE_OCCLUDED "Occluded" #define TACMAP_BASE_OPEN "Open" - -#define TACMAP_DEFAULT "Default" -#define TACMAP_XENO "Xeno" -#define TACMAP_YAUTJA "Yautja" -#define TACMAP_FACTION "Faction" - diff --git a/code/__DEFINES/speech_channels.dm b/code/__DEFINES/speech_channels.dm index 3f6e4720bde9..5a9a74af8ad0 100644 --- a/code/__DEFINES/speech_channels.dm +++ b/code/__DEFINES/speech_channels.dm @@ -1,6 +1,7 @@ // Used to direct channels to speak into. #define SAY_CHANNEL "Say" #define COMMS_CHANNEL "Comms" +#define WHISPER_CHANNEL "Whisper" #define ME_CHANNEL "Me" #define OOC_CHANNEL "OOC" #define LOOC_CHANNEL "LOOC" diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 158c59aff327..301ca0409655 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -109,36 +109,23 @@ // Subsystems shutdown in the reverse of the order they initialize in // The numbers just define the ordering, they are meaningless otherwise. -#define SS_INIT_TICKER_SPAWN 999 #define SS_INIT_INPUT 85 -#define SS_INIT_FAIL_TO_TOPIC 84 #define SS_INIT_TOPIC 83 #define SS_INIT_LOBBYART 82 -#define SS_INIT_RUST 30 #define SS_INIT_INFLUXDRIVER 28 -#define SS_INIT_SUPPLY_SHUTTLE 25 #define SS_INIT_GARBAGE 24 #define SS_INIT_EVENTS 23.5 -#define SS_INIT_JOB 23 +#define SS_INIT_HIJACK 22.6 #define SS_INIT_REDIS 22.5 #define SS_INIT_REAGENTS 22.1 #define SS_INIT_MAPPING 22 #define SS_INIT_NIGHTMARE 21.5 #define SS_INIT_TIMETRACK 21.1 #define SS_INIT_HUMANS 21 -#define SS_INIT_MAP 20 -#define SS_INIT_COMPONENT 19.5 #define SS_INIT_POWER 19 -#define SS_INIT_OBJECT 18 -#define SS_INIT_PIPENET 17.5 -#define SS_INIT_XENOARCH 17 -#define SS_INIT_MORE_INIT 16 -#define SS_INIT_AIR 15 -#define SS_INIT_TELEPORTER 13 #define SS_INIT_INFLUXMCSTATS 12 #define SS_INIT_INFLUXSTATS 11 #define SS_INIT_LIGHTING 10 -#define SS_INIT_DEFCON 9 #define SS_INIT_LAW 6 #define SS_INIT_FZ_TRANSITIONS 5 #define SS_INIT_PROJECTILES 4.1 @@ -152,12 +139,9 @@ #define SS_INIT_RADIO 2 #define SS_INIT_TIMER 100 #define SS_INIT_UNSPECIFIED 0 -#define SS_INIT_EMERGENCY_SHUTTLE -19 #define SS_INIT_ASSETS -20 #define SS_INIT_TICKER -21 #define SS_INIT_VOTE -23 -#define SS_INIT_FINISH -24 -#define SS_INIT_ADMIN -26 #define SS_INIT_DATABASE -27 #define SS_INIT_ENTITYMANAGER -28 #define SS_INIT_PLAYTIME -29 @@ -176,7 +160,6 @@ #define SS_PRIORITY_SOUND 250 #define SS_PRIORITY_TICKER 200 #define SS_PRIORITY_NIGHTMARE 180 -#define SS_PRIORITY_MAPVIEW 170 #define SS_PRIORITY_QUADTREE 160 #define SS_PRIORITY_CHAT 155 #define SS_PRIORITY_STATPANEL 154 @@ -194,20 +177,17 @@ #define SS_PRIORITY_VOTE 110 #define SS_PRIORITY_FAST_OBJECTS 105 #define SS_PRIORITY_OBJECTS 104 -#define SS_PRIORITY_FACEHUGGERS 100 #define SS_PRIORITY_DECORATOR 99 +#define SS_PRIORITY_HIJACK 97 #define SS_PRIORITY_POWER 95 #define SS_PRIORITY_EFFECTS 92 #define SS_PRIORITY_MACHINERY 90 #define SS_PRIORITY_FZ_TRANSITIONS 88 -#define SS_PRIORITY_PIPENET 85 #define SS_PRIORITY_ROUND_RECORDING 83 #define SS_PRIORITY_SHUTTLE 80 -#define SS_PRIORITY_TELEPORTER 75 #define SS_PRIORITY_EVENT 65 #define SS_PRIORITY_DISEASE 60 -#define SS_PRIORITY_FAST_MACHINERY 55 -#define SS_PRIORITY_MIDI 40 +#define SS_PRIORITY_DEFENSES 55 #define SS_PRIORITY_ENTITY 37 #define SS_PRIORITY_DEFCON 35 #define SS_PRIORITY_ACID_PILLAR 34 @@ -226,7 +206,6 @@ #define SS_PRIORITY_INFLUXSTATS 8 #define SS_PRIORITY_PLAYTIME 5 #define SS_PRIORITY_PERFLOGGING 4 -#define SS_PRIORITY_CORPSESPAWNER 3 #define SS_PRIORITY_GARBAGE 2 #define SS_PRIORITY_INACTIVITY 1 #define SS_PRIORITY_ADMIN 0 diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm index d468d6044196..b0e97e05e9b2 100644 --- a/code/__DEFINES/tgs.dm +++ b/code/__DEFINES/tgs.dm @@ -1,6 +1,6 @@ // tgstation-server DMAPI -#define TGS_DMAPI_VERSION "6.6.1" +#define TGS_DMAPI_VERSION "6.7.0" // All functions and datums outside this document are subject to change with any version and should not be relied on. diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index ef500b6af9f8..af8af22ca707 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -1,22 +1,20 @@ -//shamelessly ripped from TG #define SIGNAL_ADDTRAIT(trait_ref) "addtrait [trait_ref]" #define SIGNAL_REMOVETRAIT(trait_ref) "removetrait [trait_ref]" // trait accessor defines -//here be dragons #define ADD_TRAIT(target, trait, source) \ do { \ var/list/_L; \ - if (!target.status_traits) { \ - target.status_traits = list(); \ - _L = target.status_traits; \ + if (!target._status_traits) { \ + target._status_traits = list(); \ + _L = target._status_traits; \ _L[trait] = list(source); \ SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait), trait); \ if(trait in GLOB.traits_with_elements){ \ target.AddElement(GLOB.traits_with_elements[trait]); \ } \ } else { \ - _L = target.status_traits; \ + _L = target._status_traits; \ if (_L[trait]) { \ _L[trait] |= list(source); \ } else { \ @@ -30,16 +28,16 @@ } while (0) #define REMOVE_TRAIT(target, trait, sources) \ do { \ - var/list/_L = target.status_traits; \ + var/list/_L = target._status_traits; \ var/list/_S; \ if (sources && !islist(sources)) { \ _S = list(sources); \ } else { \ _S = sources\ }; \ - if (_L && _L[trait]) { \ + if (_L?[trait]) { \ for (var/_T in _L[trait]) { \ - if ((!_S && (_T != TRAIT_SOURCE_QUIRK)) || (_T in _S)) { \ + if ((!_S && (_T != ROUNDSTART_TRAIT)) || (_T in _S)) { \ _L[trait] -= _T \ } \ };\ @@ -51,13 +49,40 @@ } \ }; \ if (!length(_L)) { \ - target.status_traits = null \ + target._status_traits = null \ + }; \ + } \ + } while (0) +#define REMOVE_TRAIT_NOT_FROM(target, trait, sources) \ + do { \ + var/list/_traits_list = target._status_traits; \ + var/list/_sources_list; \ + if (sources && !islist(sources)) { \ + _sources_list = list(sources); \ + } else { \ + _sources_list = sources\ + }; \ + if (_traits_list?[trait]) { \ + for (var/_trait_source in _traits_list[trait]) { \ + if (!(_trait_source in _sources_list)) { \ + _traits_list[trait] -= _trait_source \ + } \ + };\ + if (!length(_traits_list[trait])) { \ + _traits_list -= trait; \ + SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \ + if(trait in GLOB.traits_with_elements) { \ + target.RemoveElement(GLOB.traits_with_elements[trait]); \ + } \ + }; \ + if (!length(_traits_list)) { \ + target._status_traits = null \ }; \ } \ } while (0) #define REMOVE_TRAITS_NOT_IN(target, sources) \ do { \ - var/list/_L = target.status_traits; \ + var/list/_L = target._status_traits; \ var/list/_S = sources; \ if (_L) { \ for (var/_T in _L) { \ @@ -65,20 +90,20 @@ if (!length(_L[_T])) { \ _L -= _T; \ SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(_T), _T); \ - if(_T in GLOB.traits_with_elements) { \ - target.RemoveElement(GLOB.traits_with_elements[_T]); \ + if(trait in GLOB.traits_with_elements) { \ + target.RemoveElement(GLOB.traits_with_elements[trait]); \ }; \ };\ };\ if (!length(_L)) { \ - target.status_traits = null\ + target._status_traits = null\ };\ }\ } while (0) #define REMOVE_TRAITS_IN(target, sources) \ do { \ - var/list/_L = target.status_traits; \ + var/list/_L = target._status_traits; \ var/list/_S = sources; \ if (sources && !islist(sources)) { \ _S = list(sources); \ @@ -97,40 +122,32 @@ };\ };\ if (!length(_L)) { \ - target.status_traits = null\ + target._status_traits = null\ };\ }\ } while (0) -/// Will 100% nuke a trait regardless of source. Preferably use this as little as possible -#define REMOVE_TRAIT_ALLSOURCES(target, trait) \ - do { \ - var/list/_L = target.status_traits; \ - if (_L?[trait]) { \ - if (length(_L)) { \ - _L -= trait; \ - SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \ - }; \ - else { \ - target.status_traits = null \ - }; \ - } \ - } while (0) - -#define HAS_TRAIT(target, trait) (target.status_traits ? (target.status_traits[trait] ? TRUE : FALSE) : FALSE) -#define HAS_TRAIT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (source in target.status_traits[trait]) : FALSE) : FALSE) -#define HAS_TRAIT_FROM_ONLY(target, trait, source) (\ - target.status_traits ?\ - (target.status_traits[trait] ?\ - ((source in target.status_traits[trait]) && (length(target.status_traits) == 1))\ - : FALSE)\ - : FALSE) -#define HAS_TRAIT_NOT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (length(target.status_traits[trait] - source) > 0) : FALSE) : FALSE) - +#define HAS_TRAIT(target, trait) (target._status_traits?[trait] ? TRUE : FALSE) +#define HAS_TRAIT_FROM(target, trait, source) (HAS_TRAIT(target, trait) && (source in target._status_traits[trait])) +#define HAS_TRAIT_FROM_ONLY(target, trait, source) (HAS_TRAIT(target, trait) && (source in target._status_traits[trait]) && (length(target._status_traits[trait]) == 1)) +#define HAS_TRAIT_NOT_FROM(target, trait, source) (HAS_TRAIT(target, trait) && (length(target._status_traits[trait] - source) > 0)) +/// Returns a list of trait sources for this trait. Only useful for wacko cases and internal futzing +/// You should not be using this +#define GET_TRAIT_SOURCES(target, trait) (target._status_traits?[trait] || list()) +/// Returns the amount of sources for a trait. useful if you don't want to have a "thing counter" stuck around all the time +#define COUNT_TRAIT_SOURCES(target, trait) length(GET_TRAIT_SOURCES(target, trait)) +/// A simple helper for checking traits in a mob's mind +#define HAS_MIND_TRAIT(target, trait) (HAS_TRAIT(target, trait) || (target.mind ? HAS_TRAIT(target.mind, trait) : FALSE)) /// Example trait // #define TRAIT_X "t_x" + //-- mob traits -- +/// Prevents voluntary movement. +#define TRAIT_IMMOBILIZED "immobilized" +/// Apply this to make a mob not dense, and remove it when you want it to no longer make them undense, other sorces of undesity will still apply. Always define a unique source when adding a new instance of this! +#define TRAIT_UNDENSE "undense" + // SPECIES TRAITS /// Knowledge of Yautja technology #define TRAIT_YAUTJA_TECH "t_yautja_tech" @@ -279,6 +296,8 @@ GLOBAL_LIST_INIT(mob_traits, list( */ GLOBAL_LIST_INIT(traits_by_type, list( /mob = list( + "TRAIT_IMMOBILIZED" = TRAIT_IMMOBILIZED, + "TRAIT_UNDENSE" = TRAIT_UNDENSE, "TRAIT_YAUTJA_TECH" = TRAIT_YAUTJA_TECH, "TRAIT_SUPER_STRONG" = TRAIT_SUPER_STRONG, "TRAIT_FOREIGN_BIO" = TRAIT_FOREIGN_BIO, @@ -354,15 +373,17 @@ GLOBAL_LIST(trait_name_map) /// Example trait source // #define TRAIT_SOURCE_Y "t_s_y" #define TRAIT_SOURCE_INHERENT "t_s_inherent" +/// cannot be removed without admin intervention +#define ROUNDSTART_TRAIT "roundstart" //-- mob traits -- +///Status trait coming from lying down through update_canmove() +#define LYING_TRAIT "lying" ///Status trait coming from species. .human/species_gain() #define TRAIT_SOURCE_SPECIES "t_s_species" ///Status trait coming from the hive. #define TRAIT_SOURCE_HIVE "t_s_hive" ///Status trait coming from being buckled. #define TRAIT_SOURCE_BUCKLE "t_s_buckle" -///Status trait coming from roundstart quirks (that don't exist yet). Unremovable by REMOVE_TRAIT -#define TRAIT_SOURCE_QUIRK "t_s_quirk" ///Status trait coming from being assigned as [acting] squad leader. #define TRAIT_SOURCE_SQUAD_LEADER "t_s_squad_leader" ///Status trait coming from their job @@ -379,6 +400,8 @@ GLOBAL_LIST(trait_name_map) #define TRAIT_SOURCE_ABILITY(ability) "t_s_ability_[ability]" ///Status trait forced by the xeno action charge #define TRAIT_SOURCE_XENO_ACTION_CHARGE "t_s_xeno_action_charge" +///Status trait coming from a xeno nest +#define XENO_NEST_TRAIT "xeno_nest" //-- structure traits -- ///Status trait coming from being flipped or unflipped. #define TRAIT_SOURCE_FLIP_TABLE "t_s_flip_table" @@ -390,3 +413,14 @@ GLOBAL_LIST(trait_name_map) //Status trait coming from clothing. #define TRAIT_SOURCE_CLOTHING "t_s_clothing" + +/// traits associated with actively interacted machinery +#define INTERACTION_TRAIT "interaction" +/// trait effect related to active specialist gear +#define SPECIALIST_GEAR_TRAIT "specialist_gear" +/// traits associated with usage of snowflake dropship double seats +#define DOUBLE_SEATS_TRAIT "double_seats" +/// traits associated with xeno on-ground weeds +#define XENO_WEED_TRAIT "xeno_weed" +/// traits from chloroform usage +#define CHLOROFORM_TRAIT "chloroform" diff --git a/code/__DEFINES/urls.dm b/code/__DEFINES/urls.dm index 137095327a2c..5d3fca1a2032 100644 --- a/code/__DEFINES/urls.dm +++ b/code/__DEFINES/urls.dm @@ -1,3 +1,7 @@ +// placeholder strings to be replaced +#define WIKI_PLACEHOLDER "%WIKIURL%" +#define LAW_PLACEHOLDER "%LAWURL%" + // ------ MISC WIKI LINKS ------ // #define URL_WIKI_LAW "Marine_Law" #define URL_WIKI_XENO_QUICKSTART "Xeno_Quickstart_Guide" diff --git a/code/__DEFINES/vehicle.dm b/code/__DEFINES/vehicle.dm index 9c6685085788..8a1617229926 100644 --- a/code/__DEFINES/vehicle.dm +++ b/code/__DEFINES/vehicle.dm @@ -53,5 +53,6 @@ #define VEHICLE_CLASS_LIGHT (1<<2) //light class armor (APC, tank) #define VEHICLE_CLASS_MEDIUM (1<<3) //medium class armor (tank) #define VEHICLE_CLASS_HEAVY (1<<4) //heavy class armor (tank) - -#define TANK_POPLOCK 90 +// Other vehicle flags +/// Vehicle can bypass vehicle blockers, typically going further into maps than intended +#define VEHICLE_BYPASS_BLOCKERS (1<<5) diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 1116f1acb2a8..24e39ff16c89 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -682,8 +682,9 @@ world * * moving - whether or not to use a moving state for the given icon * * sourceonly - if TRUE, only generate the asset and send back the asset url, instead of tags that display the icon to players * * extra_clases - string of extra css classes to use when returning the icon string + * * keyonly - if TRUE, only returns the asset key to use get_asset_url manually. Overrides sourceonly. */ -/proc/icon2html(atom/thing, client/target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null) +/proc/icon2html(atom/thing, client/target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null, keyonly = FALSE) if (!thing) return @@ -714,6 +715,8 @@ world SSassets.transport.register_asset(name, thing) for (var/thing2 in targets) SSassets.transport.send_assets(thing2, name) + if(keyonly) + return name if(sourceonly) return SSassets.transport.get_asset_url(name) return "" @@ -756,6 +759,8 @@ world SSassets.transport.register_asset(key, rsc_ref, file_hash, icon_path) for (var/client_target in targets) SSassets.transport.send_assets(client_target, key) + if(keyonly) + return key if(sourceonly) return SSassets.transport.get_asset_url(key) return "" diff --git a/code/__HELPERS/job.dm b/code/__HELPERS/job.dm index 43902b07cfd9..89fe6877647e 100644 --- a/code/__HELPERS/job.dm +++ b/code/__HELPERS/job.dm @@ -26,13 +26,6 @@ if(I.assignment in GLOB.joblist) return I.assignment return "Unknown" -/proc/FindNameFromID(mob/living/carbon/human/H) - ASSERT(istype(H)) - var/obj/item/card/id/I = H.wear_id - if(istype(I)) return I.registered_name - I = H.get_active_hand() - if(istype(I)) return I.registered_name - /proc/get_all_job_icons() return GLOB.joblist + list("Prisoner")//For all existing HUD icons /obj/proc/GetJobName() //Used in secHUD icon generation diff --git a/code/__HELPERS/traits.dm b/code/__HELPERS/traits.dm new file mode 100644 index 000000000000..ba99b2e1e7ff --- /dev/null +++ b/code/__HELPERS/traits.dm @@ -0,0 +1,43 @@ +#define TRAIT_CALLBACK_ADD(target, trait, source) CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(___TraitAdd), ##target, ##trait, ##source) +#define TRAIT_CALLBACK_REMOVE(target, trait, source) CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(___TraitRemove), ##target, ##trait, ##source) + +///DO NOT USE ___TraitAdd OR ___TraitRemove as a replacement for ADD_TRAIT / REMOVE_TRAIT defines. To be used explicitly for callback. +/proc/___TraitAdd(target,trait,source) + if(!target || !trait || !source) + return + if(islist(target)) + for(var/i in target) + if(!isatom(i)) + continue + var/atom/the_atom = i + ADD_TRAIT(the_atom,trait,source) + else if(isatom(target)) + var/atom/the_atom2 = target + ADD_TRAIT(the_atom2,trait,source) + +///DO NOT USE ___TraitAdd OR ___TraitRemove as a replacement for ADD_TRAIT / REMOVE_TRAIT defines. To be used explicitly for callback. +/proc/___TraitRemove(target,trait,source) + if(!target || !trait || !source) + return + if(islist(target)) + for(var/i in target) + if(!isatom(i)) + continue + var/atom/the_atom = i + REMOVE_TRAIT(the_atom,trait,source) + else if(isatom(target)) + var/atom/the_atom2 = target + REMOVE_TRAIT(the_atom2,trait,source) + + +/// Proc that handles adding multiple traits to a target via a list. Must have a common source and target. +/datum/proc/add_traits(list/list_of_traits, source) + ASSERT(islist(list_of_traits), "Invalid arguments passed to add_traits! Invoked on [src] with [list_of_traits], source being [source].") + for(var/trait in list_of_traits) + ADD_TRAIT(src, trait, source) + +/// Proc that handles removing multiple traits from a target via a list. Must have a common source and target. +/datum/proc/remove_traits(list/list_of_traits, source) + ASSERT(islist(list_of_traits), "Invalid arguments passed to remove_traits! Invoked on [src] with [list_of_traits], source being [source].") + for(var/trait in list_of_traits) + REMOVE_TRAIT(src, trait, source) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 9a6ee4362088..38c02e776735 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -21,16 +21,16 @@ #define between(low, middle, high) (max(min(middle, high), low)) //Offuscate x for coord system -#define obfuscate_x(x) (x + obfs_x) +#define obfuscate_x(x) (x + GLOB.obfs_x) //Offuscate y for coord system -#define obfuscate_y(y) (y + obfs_y) +#define obfuscate_y(y) (y + GLOB.obfs_y) //Deoffuscate x for coord system -#define deobfuscate_x(x) (x - obfs_x) +#define deobfuscate_x(x) (x - GLOB.obfs_x) //Deoffuscate y for coord system -#define deobfuscate_y(y) (y - obfs_y) +#define deobfuscate_y(y) (y - GLOB.obfs_y) #define can_xeno_build(T) (!T.density && !(locate(/obj/structure/fence) in T) && !(locate(/obj/structure/tunnel) in T) && (locate(/obj/effect/alien/weeds) in T)) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 53dd40ff6035..b85aa18fdb6b 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -166,7 +166,6 @@ DEFINE_BITFIELD(flags_item, list( "ITEM_OVERRIDE_NORTHFACE" = ITEM_OVERRIDE_NORTHFACE, "CAN_DIG_SHRAPNEL" = CAN_DIG_SHRAPNEL, "ANIMATED_SURGICAL_TOOL" = ANIMATED_SURGICAL_TOOL, - "NOTABLEMERGE" = NOTABLEMERGE, "IGNITING_ITEM" = IGNITING_ITEM, )) @@ -462,3 +461,11 @@ DEFINE_BITFIELD(vend_flags, list( "VEND_FACTION_THEMES" = VEND_FACTION_THEMES, "VEND_USE_VENDOR_FLAGS" = VEND_USE_VENDOR_FLAGS, )) + +DEFINE_BITFIELD(vehicle_flags, list( + "VEHICLE_CLASS_WEAK" = VEHICLE_CLASS_WEAK, + "VEHICLE_CLASS_LIGHT" = VEHICLE_CLASS_LIGHT, + "VEHICLE_CLASS_MEDIUM" = VEHICLE_CLASS_MEDIUM, + "VEHICLE_CLASS_HEAVY" = VEHICLE_CLASS_HEAVY, + "VEHICLE_BYPASS_BLOCKERS" = VEHICLE_BYPASS_BLOCKERS, +)) diff --git a/code/_globalvars/global_lists.dm b/code/_globalvars/global_lists.dm index 36058a44fc37..3ba92a7c4d0c 100644 --- a/code/_globalvars/global_lists.dm +++ b/code/_globalvars/global_lists.dm @@ -1,6 +1,3 @@ - -var/list/unansweredAhelps = list() //This feels inefficient, but I can't think of a better way. Stores the message indexed by CID - GLOBAL_LIST_EMPTY(PressFaxes) GLOBAL_LIST_EMPTY(WYFaxes) //Departmental faxes GLOBAL_LIST_EMPTY(USCMFaxes) @@ -9,6 +6,14 @@ GLOBAL_LIST_EMPTY(CMBFaxes) GLOBAL_LIST_EMPTY(GeneralFaxes) //Inter-machine faxes GLOBAL_LIST_EMPTY(fax_contents) //List of fax contents to maintain it even if source paper is deleted +//datum containing a reference to the flattend map png url, the actual png is stored in the user's cache. +GLOBAL_LIST_EMPTY(uscm_flat_tacmap_data) +GLOBAL_LIST_EMPTY(xeno_flat_tacmap_data) + +//datum containing the svg overlay coords in array format. +GLOBAL_LIST_EMPTY(uscm_svg_tacmap_data) +GLOBAL_LIST_EMPTY(xeno_svg_tacmap_data) + GLOBAL_LIST_EMPTY(failed_fultons) //A list of fultoned items which weren't collected and fell back down GLOBAL_LIST_EMPTY(larva_burst_by_hive) diff --git a/code/_globalvars/lists/object_lists.dm b/code/_globalvars/lists/object_lists.dm index 3a417625538b..3db9bd28cbfe 100644 --- a/code/_globalvars/lists/object_lists.dm +++ b/code/_globalvars/lists/object_lists.dm @@ -31,3 +31,6 @@ GLOBAL_LIST_EMPTY_TYPED(all_multi_vehicles, /obj/vehicle/multitile) GLOBAL_LIST_EMPTY_TYPED(lifeboat_almayer_docks, /obj/docking_port/stationary/lifeboat_dock) GLOBAL_LIST_EMPTY_TYPED(lifeboat_doors, /obj/structure/machinery/door/airlock/multi_tile/almayer/dropshiprear/lifeboat/blastdoor) + +GLOBAL_LIST_EMPTY_TYPED(teleporters, /datum/teleporter) +GLOBAL_LIST_EMPTY(teleporters_by_id) diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index 646b8ec2c854..cd6708198eae 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -14,6 +14,22 @@ GLOBAL_LIST_INIT(pill_icon_mappings, map_pill_icons()) /// In-round override to default OOC color GLOBAL_VAR(ooc_color_override) +// tacmap cooldown for xenos and marines +GLOBAL_VAR_INIT(uscm_canvas_cooldown, 0) +GLOBAL_VAR_INIT(xeno_canvas_cooldown, 0) + +// getFlatIcon cooldown for xenos and marines +GLOBAL_VAR_INIT(uscm_flatten_map_icon_cooldown, 0) +GLOBAL_VAR_INIT(xeno_flatten_map_icon_cooldown, 0) + +// latest unannounced flat tacmap for xenos and marines +GLOBAL_VAR(uscm_unannounced_map) +GLOBAL_VAR(xeno_unannounced_map) + +//global tacmaps for action button access +GLOBAL_DATUM_INIT(uscm_tacmap_status, /datum/tacmap/drawing/status_tab_view, new) +GLOBAL_DATUM_INIT(xeno_tacmap_status, /datum/tacmap/drawing/status_tab_view/xeno, new) + /// List of roles that can be setup for each gamemode GLOBAL_LIST_INIT(gamemode_roles, list()) @@ -30,3 +46,10 @@ GLOBAL_VAR_INIT(time_offset, setup_offset()) /// The last count of possible candidates in the xeno larva queue (updated via get_alien_candidates) GLOBAL_VAR(xeno_queue_candidate_count) + +//Coordinate obsfucator +//Used by the rangefinders and linked systems to prevent coords collection/prefiring +/// A number between -500 and 500. +GLOBAL_VAR(obfs_x) +/// A number between -500 and 500. +GLOBAL_VAR(obfs_y) diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm index a6754747a019..a99129d09bcd 100644 --- a/code/_onclick/hud/ghost.dm +++ b/code/_onclick/hud/ghost.dm @@ -48,6 +48,14 @@ var/mob/dead/observer/G = usr G.reenter_corpse() +/atom/movable/screen/ghost/toggle_huds + name = "Toggle HUDs" + icon_state = "ghost_hud_toggle" + +/atom/movable/screen/ghost/toggle_huds/Click() + var/client/client = usr.client + client.toggle_ghost_hud() + /datum/hud/ghost/New(mob/owner, ui_style='icons/mob/hud/human_white.dmi', ui_color, ui_alpha = 230) . = ..() var/atom/movable/screen/using @@ -68,6 +76,9 @@ using.screen_loc = ui_ghost_slot4 static_inventory += using + using = new /atom/movable/screen/ghost/toggle_huds() + using.screen_loc = ui_ghost_slot5 + static_inventory += using /datum/hud/ghost/show_hud(version = 0, mob/viewmob) // don't show this HUD if observing; show the HUD of the observee diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm index cfc6c4d034fb..4a23ebd882d3 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -34,6 +34,12 @@ GLOBAL_LIST_EMPTY(radial_menus) /atom/movable/screen/radial/slice/clicked(mob/user) + if(QDELETED(src)) + return + + if(!parent) + CRASH("clicked() called on a radial slice with a null parent while not deleted/deleting") + if(user.client == parent.current_user) if(next_page) parent.next_page() diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index b49bce4111f0..d114aff6b7cb 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -535,7 +535,7 @@ if(!user.hive.living_xeno_queen) to_chat(user, SPAN_WARNING("Without a queen your psychic link is broken!")) return FALSE - if(user.burrow || user.is_mob_incapacitated() || user.buckled) + if(HAS_TRAIT(user, TRAIT_ABILITY_BURROWED) || user.is_mob_incapacitated() || user.buckled) return FALSE user.hive.mark_ui.update_all_data() user.hive.mark_ui.open_mark_menu(user) @@ -583,7 +583,7 @@ if(!user.hive.living_xeno_queen) to_chat(user, SPAN_WARNING("Your hive doesn't have a living queen!")) return FALSE - if(user.burrow || user.is_mob_incapacitated() || user.buckled) + if(HAS_TRAIT(user, TRAIT_ABILITY_BURROWED) || user.is_mob_incapacitated() || user.buckled) return FALSE user.overwatch(user.hive.living_xeno_queen) diff --git a/code/_onclick/human.dm b/code/_onclick/human.dm index cb71e27f9d1a..8f329656ef6c 100644 --- a/code/_onclick/human.dm +++ b/code/_onclick/human.dm @@ -99,7 +99,7 @@ if(xeno.stat != DEAD) // If the Xeno is alive, fight back var/mob/living/carbon/carbon_user = user if(!carbon_user || !carbon_user.ally_of_hivenumber(xeno.hivenumber)) - user.KnockDown(rand(xeno.caste.tacklestrength_min, xeno.caste.tacklestrength_max)) + carbon_user.KnockDown(rand(xeno.caste.tacklestrength_min, xeno.caste.tacklestrength_max)) playsound(user.loc, 'sound/weapons/pierce.ogg', 25, TRUE) user.visible_message(SPAN_WARNING("\The [user] tried to unstrap \the [back_item] from [xeno] but instead gets a tail swipe to the head!")) return diff --git a/code/_onclick/xeno.dm b/code/_onclick/xeno.dm index 62d612790930..adb637dfe8fa 100644 --- a/code/_onclick/xeno.dm +++ b/code/_onclick/xeno.dm @@ -3,7 +3,7 @@ */ /mob/living/carbon/xenomorph/UnarmedAttack(atom/target, proximity, click_parameters, tile_attack = FALSE, ignores_resin = FALSE) - if(lying || burrow) //No attacks while laying down + if(lying || HAS_TRAIT(src, TRAIT_ABILITY_BURROWED)) //No attacks while laying down return FALSE var/mob/alt diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm index e8b010669c0e..147f57fcb1aa 100644 --- a/code/controllers/configuration/configuration.dm +++ b/code/controllers/configuration/configuration.dm @@ -20,7 +20,8 @@ var/policy var/static/regex/ic_filter_regex - var/list/fail_to_topic_whitelisted_ips + + var/is_loaded = FALSE /datum/controller/configuration/proc/admin_reload() if(IsAdminAdvancedProcCall()) @@ -53,7 +54,8 @@ loadmaplist(CONFIG_GROUND_MAPS_FILE, GROUND_MAP) loadmaplist(CONFIG_SHIP_MAPS_FILE, SHIP_MAP) LoadChatFilter() - LoadTopicRateWhitelist() + + is_loaded = TRUE if(Master) Master.OnConfigLoad() @@ -333,18 +335,3 @@ /datum/controller/configuration/proc/DelayedMessageAdmins(text) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(message_admins), text), 0) -/datum/controller/configuration/proc/LoadTopicRateWhitelist() - LAZYINITLIST(fail_to_topic_whitelisted_ips) - if(!fexists("[directory]/topic_rate_limit_whitelist.txt")) - log_config("Error 404: topic_rate_limit_whitelist.txt not found!") - return - - log_config("Loading config file topic_rate_limit_whitelist.txt...") - - for(var/line in file2list("[directory]/topic_rate_limit_whitelist.txt")) - if(!line) - continue - if(findtextEx(line, "#", 1, 2)) - continue - - fail_to_topic_whitelisted_ips[line] = 1 diff --git a/code/controllers/subsystem/admin.dm b/code/controllers/subsystem/admin.dm deleted file mode 100644 index 8aab64b04881..000000000000 --- a/code/controllers/subsystem/admin.dm +++ /dev/null @@ -1,40 +0,0 @@ -SUBSYSTEM_DEF(admin) - name = "Admin" - wait = 5 MINUTES - flags = SS_NO_INIT | SS_KEEP_TIMING - runlevels = RUNLEVELS_DEFAULT|RUNLEVEL_LOBBY - var/list/currentrun = list() - var/times_repeated = 0 - -/datum/controller/subsystem/admin/stat_entry(msg) - msg = "P:[unansweredAhelps.len]" - return ..() - -/datum/controller/subsystem/admin/fire(resumed = FALSE) - if (!resumed) - currentrun = unansweredAhelps.Copy() - - if(!currentrun.len) - times_repeated = 0 - return - - var/msg = "Unheard Ahelps (Repeated [times_repeated] times):" - - while (currentrun.len) - var/ahelp_msg = currentrun[currentrun.len] - currentrun.len-- - - if (!ahelp_msg) - continue - - msg += unansweredAhelps[ahelp_msg] + "\n" - - if (MC_TICK_CHECK) - return - - for(var/client/C in GLOB.admins) - if(C && C.admin_holder && (C.admin_holder.rights & R_MOD)) - if(C.prefs.toggles_sound & SOUND_ADMINHELP) - sound_to(C, 'sound/effects/adminhelp_new.ogg') - to_chat(C, msg) - times_repeated++ diff --git a/code/controllers/subsystem/communications.dm b/code/controllers/subsystem/communications.dm index a5c5271c8d7d..8458436a53e5 100644 --- a/code/controllers/subsystem/communications.dm +++ b/code/controllers/subsystem/communications.dm @@ -107,6 +107,7 @@ var/const/MAX_FREQ = 1468 // --------------------------------------------------- var/const/HC_FREQ = 1471 var/const/SOF_FREQ = 1472 var/const/PVST_FREQ = 1473 +var/const/CBRN_FREQ = 1474 //Ship department channels var/const/SENTRY_FREQ = 1480 @@ -162,6 +163,7 @@ var/list/radiochannels = list( SQUAD_MARINE_5 = ECHO_FREQ, SQUAD_MARINE_CRYO = CRYO_FREQ, SQUAD_SOF = SOF_FREQ, + SQUAD_CBRN = CBRN_FREQ, RADIO_CHANNEL_ALAMO = DS1_FREQ, RADIO_CHANNEL_NORMANDY = DS2_FREQ, @@ -262,6 +264,7 @@ SUBSYSTEM_DEF(radio) "[DELTA_FREQ]" = "deltaradio", "[ECHO_FREQ]" = "echoradio", "[CRYO_FREQ]" = "cryoradio", + "[CBRN_FREQ]" = "hcradio", "[SOF_FREQ]" = "hcradio", "[HC_FREQ]" = "hcradio", "[PVST_FREQ]" = "pvstradio", diff --git a/code/controllers/subsystem/disease.dm b/code/controllers/subsystem/disease.dm index 25200cce11ed..b98187ca252c 100644 --- a/code/controllers/subsystem/disease.dm +++ b/code/controllers/subsystem/disease.dm @@ -1,22 +1,19 @@ -var/list/active_diseases = list() - - SUBSYSTEM_DEF(disease) name = "Disease" wait = 2 SECONDS flags = SS_NO_INIT | SS_KEEP_TIMING priority = SS_PRIORITY_DISEASE - var/list/currentrun = list() + var/list/datum/disease/all_diseases = list() + var/list/datum/disease/currentrun = list() /datum/controller/subsystem/disease/stat_entry(msg) - msg = "P:[active_diseases.len]" + msg = "P:[all_diseases.len]" return ..() - /datum/controller/subsystem/disease/fire(resumed = FALSE) if (!resumed) - currentrun = active_diseases.Copy() + currentrun = all_diseases.Copy() while (currentrun.len) var/datum/disease/D = currentrun[currentrun.len] diff --git a/code/controllers/subsystem/fail_to_topic.dm b/code/controllers/subsystem/fail_to_topic.dm deleted file mode 100644 index 45674683a443..000000000000 --- a/code/controllers/subsystem/fail_to_topic.dm +++ /dev/null @@ -1,81 +0,0 @@ -SUBSYSTEM_DEF(fail_to_topic) - name = "Fail to Topic" - init_order = SS_INIT_FAIL_TO_TOPIC - flags = SS_BACKGROUND - runlevels = ALL - - var/list/rate_limiting = list() - var/list/fail_counts = list() - var/list/active_bans = list() - var/list/currentrun = list() - - var/rate_limit - var/max_fails - var/enabled = FALSE - -/datum/controller/subsystem/fail_to_topic/Initialize(timeofday) - rate_limit = ((CONFIG_GET(number/topic_rate_limit)) SECONDS) - max_fails = CONFIG_GET(number/topic_max_fails) - enabled = CONFIG_GET(flag/topic_enabled) - - if (world.system_type == UNIX && enabled) - enabled = FALSE - WARNING("fail_to_topic subsystem disabled. UNIX is not supported.") - return SS_INIT_NO_NEED - - if (!enabled) - can_fire = FALSE - return SS_INIT_NO_NEED - - return SS_INIT_SUCCESS - -/datum/controller/subsystem/fail_to_topic/fire(resumed = FALSE) - if(!resumed) - currentrun = rate_limiting.Copy() - //cache for sanic speed (lists are references anyways) - var/list/current_run = currentrun - - while(current_run.len) - var/ip = current_run[current_run.len] - var/last_attempt = current_run[ip] - current_run.len-- - - // last_attempt list housekeeping - if(world.time - last_attempt > rate_limit) - rate_limiting -= ip - fail_counts -= ip - - if(MC_TICK_CHECK) - return - -/datum/controller/subsystem/fail_to_topic/proc/IsRateLimited(ip) - if(!enabled) - return FALSE - - var/last_attempt = rate_limiting[ip] - - if (config.fail_to_topic_whitelisted_ips[ip]) - return FALSE - - if (active_bans[ip]) - return TRUE - - rate_limiting[ip] = world.time - - if (isnull(last_attempt)) - return FALSE - - if (world.time - last_attempt > rate_limit) - fail_counts -= ip - return FALSE - else - var/failures = fail_counts[ip] - - if (isnull(failures)) - fail_counts[ip] = 1 - return TRUE - else if (failures > max_fails) - return TRUE - else - fail_counts[ip] = failures + 1 - return TRUE diff --git a/code/controllers/subsystem/fast_machinery.dm b/code/controllers/subsystem/fast_machinery.dm deleted file mode 100644 index 8211b3b5e310..000000000000 --- a/code/controllers/subsystem/fast_machinery.dm +++ /dev/null @@ -1,27 +0,0 @@ -var/list/fast_machines = list() - - -SUBSYSTEM_DEF(fast_machinery) - name = "Fast Machinery" - wait = 0.7 SECONDS - priority = SS_PRIORITY_FAST_MACHINERY - flags = SS_NO_INIT - var/list/currentrun = list() - -/datum/controller/subsystem/fast_machinery/stat_entry(msg) - msg = "FP:[fast_machines.len]" - return ..() - -/datum/controller/subsystem/fast_machinery/fire(resumed = FALSE) - if(!resumed) - currentrun = fast_machines.Copy() - while(currentrun.len) - var/obj/structure/machinery/M = currentrun[currentrun.len] - currentrun.len-- - - if(QDELETED(M)) - continue - - M.process() - if(MC_TICK_CHECK) - return diff --git a/code/controllers/subsystem/game_decorator.dm b/code/controllers/subsystem/game_decorator.dm new file mode 100644 index 000000000000..dd53b647d1a8 --- /dev/null +++ b/code/controllers/subsystem/game_decorator.dm @@ -0,0 +1,35 @@ +// Essentially the same as decorators but that apply to the whole game state instead of individual atoms +SUBSYSTEM_DEF(game_decorator) + name = "Game Decorator" + init_order = SS_INIT_DECORATOR + flags = SS_NO_FIRE + +/datum/controller/subsystem/game_decorator/Initialize() + . = ..() + for(var/decorator_type in subtypesof(/datum/game_decorator)) + var/datum/game_decorator/decorator = new decorator_type() + if(!decorator.is_active_decor()) + continue + if(!decorator.defer_decoration) + decorator.decorate() + CHECK_TICK + + return SS_INIT_SUCCESS + +/datum/game_decorator + var/defer_decoration = TRUE //! So map decoration is done post-setup after nightmare and spawners + +/datum/game_decorator/New() + if(defer_decoration && is_active_decor()) + RegisterSignal(SSdcs, COMSIG_GLOB_MODE_POSTSETUP, PROC_REF(defered_decoration)) + +/datum/game_decorator/proc/is_active_decor() + return FALSE + +/datum/game_decorator/proc/defered_decoration(dcs) + SIGNAL_HANDLER + decorate() + +/datum/game_decorator/proc/decorate() + set waitfor = FALSE + return diff --git a/code/controllers/subsystem/hijack.dm b/code/controllers/subsystem/hijack.dm new file mode 100644 index 000000000000..55b5aa75caf8 --- /dev/null +++ b/code/controllers/subsystem/hijack.dm @@ -0,0 +1,429 @@ +SUBSYSTEM_DEF(hijack) + name = "Hijack" + wait = 2 SECONDS + flags = SS_KEEP_TIMING + priority = SS_PRIORITY_HIJACK + init_order = SS_INIT_HIJACK + + ///Required progress to evacuate safely via lifeboats + var/required_progress = 100 + + ///Current progress towards evacuating safely via lifeboats + var/current_progress = 0 + + /// How much progress is required to early launch + var/early_launch_required_progress = 25 + + ///The estimated time left to get to the safe evacuation point + var/estimated_time_left = 0 + + ///Areas that are marked as having progress, assoc list that is progress_area = boolean, the boolean indicating if it was progressing or not on the last fire() + var/list/area/progress_areas = list() + + ///The areas that need cycled through currently + var/list/area/current_run = list() + + ///The progress of the current run that needs to be added at the end of the current run + var/current_run_progress_additive = 0 + + ///Holds what the current_run_progress_additive should be multiplied by at the end of the current run + var/current_run_progress_multiplicative = 1 + + ///Holds the progress change from last run + var/last_run_progress_change = 0 + + ///Holds the next % point progress should be announced, increments on itself + var/announce_checkpoint = 25 + + ///What stage of evacuation we are currently on + var/evac_status = EVACUATION_STATUS_NOT_INITIATED + + ///What stage of hijack are we currently on + var/hijack_status = HIJACK_OBJECTIVES_NOT_STARTED + + ///Whether or not evacuation has been disabled by admins + var/evac_admin_denied = FALSE + + /// If TRUE, self destruct has been unlocked and is possible with a hold of reactor + var/sd_unlocked = FALSE + + /// Admin var to manually prevent self destruct from occurring + var/admin_sd_blocked = FALSE + + /// Maximum amount of fusion generators that can be overloaded at once for a time benefit + var/maximum_overload_generators = 18 + + /// How many generators are currently overloaded + var/overloaded_generators = 0 + + /// How long the manual self destruct will take on the high end + var/sd_max_time = 15 MINUTES + + /// How long the manual self destruct will take on the low end + var/sd_min_time = 5 MINUTES + + /// How much time left until SD detonates + var/sd_time_remaining = 0 + + /// Roughly what % of the SD countdown remains + var/percent_completion_remaining = 100 + + /// If the engine room has been heated, occurs at 33% SD completion + var/engine_room_heated = FALSE + + /// If the engine room has been superheated, occurs at 66% SD completion + var/engine_room_superheated = FALSE + + /// If the self destruct has/is detonating + var/sd_detonated = FALSE + + /// If a generator has ever been overloaded in the past this round + var/generator_ever_overloaded = FALSE + + /// If ARES has announced the 50% point yet for SD + var/ares_sd_announced = FALSE + +/datum/controller/subsystem/hijack/Initialize(timeofday) + RegisterSignal(SSdcs, COMSIG_GLOB_GENERATOR_SET_OVERLOADING, PROC_REF(on_generator_overload)) + return SS_INIT_SUCCESS + +/datum/controller/subsystem/hijack/stat_entry(msg) + if(!SSticker?.mode?.is_in_endgame) + msg = " Not Hijack" + return ..() + + if(current_progress >= required_progress) + msg = " Complete" + return ..() + + msg = " Progress: [current_progress]% | Last run: [last_run_progress_change]" + return ..() + +/datum/controller/subsystem/hijack/fire(resumed = FALSE) + if(!SSticker?.mode?.is_in_endgame) + return + + if(hijack_status < HIJACK_OBJECTIVES_STARTED) + hijack_status = HIJACK_OBJECTIVES_STARTED + + if(current_progress >= required_progress) + if(hijack_status < HIJACK_OBJECTIVES_COMPLETE) + hijack_status = HIJACK_OBJECTIVES_COMPLETE + + if(sd_unlocked && overloaded_generators) + sd_time_remaining -= wait + if(!engine_room_heated && (sd_time_remaining <= (max((1 - round(overloaded_generators / maximum_overload_generators, 0.01)) * sd_max_time, sd_min_time) * 0.66))) + heat_engine_room() + + if(!ares_sd_announced && (sd_time_remaining <= (max((1 - round(overloaded_generators / maximum_overload_generators, 0.01)) * sd_max_time, sd_min_time) * 0.5))) + announce_sd_halfway() + + if(!engine_room_superheated && (sd_time_remaining <= (max((1 - round(overloaded_generators / maximum_overload_generators, 0.01)) * sd_max_time, sd_min_time) * 0.33))) + superheat_engine_room() + + if((sd_time_remaining <= 0) && !sd_detonated) + detonate_sd() + + return + + if(!resumed) + current_run = progress_areas.Copy() + + for(var/area/almayer/cycled_area as anything in current_run) + current_run -= cycled_area + + if(progress_areas[cycled_area] != cycled_area.power_equip) + progress_areas[cycled_area] = !progress_areas[cycled_area] + announce_area_power_change(cycled_area) + + if(progress_areas[cycled_area]) + switch(cycled_area.hijack_evacuation_type) + if(EVACUATION_TYPE_ADDITIVE) + current_run_progress_additive += cycled_area.hijack_evacuation_weight + if(EVACUATION_TYPE_MULTIPLICATIVE) + current_run_progress_multiplicative *= cycled_area.hijack_evacuation_weight + + if (MC_TICK_CHECK) + return + + last_run_progress_change = current_run_progress_additive * current_run_progress_multiplicative + current_progress += last_run_progress_change + + if(last_run_progress_change) + estimated_time_left = ((required_progress - current_progress) / last_run_progress_change) * wait + else + estimated_time_left = INFINITY + + if(current_progress >= announce_checkpoint) + announce_progress() + announce_checkpoint += initial(announce_checkpoint) + + current_run_progress_additive = 0 + current_run_progress_multiplicative = 1 + +///Called when the xeno dropship crashes into the Almayer and announces the current status of various objectives to marines +/datum/controller/subsystem/hijack/proc/announce_status_on_crash() + var/message = "" + + for(var/area/cycled_area as anything in progress_areas) + message += "[cycled_area] - [cycled_area.power_equip ? "Online" : "Offline"]\n" + progress_areas[cycled_area] = cycled_area.power_equip + + message += "\nDue to low orbit, extra fuel is required for non-surface evacuations.\nMaintain fueling functionality for optimal evacuation conditions." + + marine_announcement(message, HIJACK_ANNOUNCE) + +///Called when an area power status is changed to announce that it has been changed +/datum/controller/subsystem/hijack/proc/announce_area_power_change(area/changed_area) + var/message = "[changed_area] - [changed_area.power_equip ? "Online" : "Offline"]" + + marine_announcement(message, HIJACK_ANNOUNCE) + +///Called to announce to xenos the state of evacuation progression +/datum/controller/subsystem/hijack/proc/announce_progress() + var/announce = announce_checkpoint / initial(announce_checkpoint) + + var/marine_warning_areas = "" + var/xeno_warning_areas = "" + + for(var/area/cycled_area as anything in progress_areas) + if(cycled_area.power_equip) + xeno_warning_areas += "[cycled_area], " + continue + marine_warning_areas += "[cycled_area], " + + if(xeno_warning_areas) + xeno_warning_areas = copytext(xeno_warning_areas, 1, -2) + + if(marine_warning_areas) + marine_warning_areas = copytext(marine_warning_areas, 1, -2) + + var/datum/hive_status/hive + for(var/hivenumber in GLOB.hive_datum) + hive = GLOB.hive_datum[hivenumber] + if(!length(hive.totalXenos)) + continue + + switch(announce) + if(1) + xeno_announcement(SPAN_XENOANNOUNCE("The talls are a quarter of the way towards their goals. Disable the following areas: [xeno_warning_areas]"), hive.hivenumber, XENO_HIJACK_ANNOUNCE) + if(2) + xeno_announcement(SPAN_XENOANNOUNCE("The talls are half way towards their goals. Disable the following areas: [xeno_warning_areas]"), hive.hivenumber, XENO_HIJACK_ANNOUNCE) + if(3) + xeno_announcement(SPAN_XENOANNOUNCE("The talls are three quarters of the way towards their goals. Disable the following areas: [xeno_warning_areas]"), hive.hivenumber, XENO_HIJACK_ANNOUNCE) + if(4) + xeno_announcement(SPAN_XENOANNOUNCE("The talls have completed their goals!"), hive.hivenumber, XENO_HIJACK_ANNOUNCE) + + switch(announce) + if(1) + marine_announcement("Emergency fuel replenishment at 25 percent. Lifeboat emergency early launch now available.[marine_warning_areas ? "\nTo increase speed restore power to the following areas: [marine_warning_areas]" : " All fueling areas operational."]", HIJACK_ANNOUNCE) + if(2) + marine_announcement("Emergency fuel replenishment at 50 percent.[marine_warning_areas ? "\nTo increase speed restore power to the following areas: [marine_warning_areas]" : " All fueling areas operational."]", HIJACK_ANNOUNCE) + if(3) + marine_announcement("Emergency fuel replenishment at 75 percent.[marine_warning_areas ? "\nTo increase speed restore power to the following areas: [marine_warning_areas]" : " All fueling areas operational."]", HIJACK_ANNOUNCE) + if(4) + marine_announcement("Emergency fuel replenishment at 100 percent. Safe utilization of lifeboats now possible.", HIJACK_ANNOUNCE) + if(!admin_sd_blocked) + addtimer(CALLBACK(src, PROC_REF(unlock_self_destruct)), 8 SECONDS) + +/// Passes the ETA for status panels +/datum/controller/subsystem/hijack/proc/get_evac_eta() + switch(hijack_status) + if(HIJACK_OBJECTIVES_STARTED) + if(estimated_time_left == INFINITY) + return "Never" + return "[duration2text_sec(estimated_time_left)]" + + if(HIJACK_OBJECTIVES_COMPLETE) + return "Complete" + +/datum/controller/subsystem/hijack/proc/get_sd_eta() + if(!sd_time_remaining) + return "Complete" + + if(overloaded_generators <= 0) + return "Never" + + return "[duration2text_sec(sd_time_remaining)]" + +//~~~~~~~~~~~~~~~~~~~~~~~~ EVAC STUFF ~~~~~~~~~~~~~~~~~~~~~~~~// + +/// Initiates evacuation by announcing and then prepping all lifepods/lifeboats +/datum/controller/subsystem/hijack/proc/initiate_evacuation() + if(evac_status == EVACUATION_STATUS_NOT_INITIATED && !(evac_admin_denied & FLAGS_EVACUATION_DENY)) + evac_status = EVACUATION_STATUS_INITIATED + ai_announcement("Attention. Emergency. All personnel must evacuate immediately.", 'sound/AI/evacuate.ogg') + + for(var/obj/structure/machinery/status_display/cycled_status_display in machines) + if(is_mainship_level(cycled_status_display.z)) + cycled_status_display.set_picture("evac") + for(var/obj/docking_port/mobile/crashable/escape_shuttle/shuttle in SSshuttle.mobile) + shuttle.prepare_evac() + activate_lifeboats() + return TRUE + +/// Cancels evacuation, tells lifepods/lifeboats and status_displays +/datum/controller/subsystem/hijack/proc/cancel_evacuation() + if(evac_status == EVACUATION_STATUS_INITIATED) + evac_status = EVACUATION_STATUS_NOT_INITIATED + deactivate_lifeboats() + ai_announcement("Evacuation has been cancelled.", 'sound/AI/evacuate_cancelled.ogg') + + for(var/obj/structure/machinery/status_display/cycled_status_display in machines) + if(is_mainship_level(cycled_status_display.z)) + cycled_status_display.set_sec_level_picture() + + for(var/obj/docking_port/mobile/crashable/escape_shuttle/shuttle in SSshuttle.mobile) + shuttle.cancel_evac() + return TRUE + +/// Opens the lifeboat doors and gets them ready to launch +/datum/controller/subsystem/hijack/proc/activate_lifeboats() + for(var/obj/docking_port/stationary/lifeboat_dock/lifeboat_dock in GLOB.lifeboat_almayer_docks) + var/obj/docking_port/mobile/crashable/lifeboat/lifeboat = lifeboat_dock.get_docked() + if(lifeboat && lifeboat.available) + lifeboat.status = LIFEBOAT_ACTIVE + lifeboat_dock.open_dock() + +/// Turns off ability to manually take off lifeboats +/datum/controller/subsystem/hijack/proc/deactivate_lifeboats() + for(var/obj/docking_port/stationary/lifeboat_dock/lifeboat_dock in GLOB.lifeboat_almayer_docks) + var/obj/docking_port/mobile/crashable/lifeboat/lifeboat = lifeboat_dock.get_docked() + if(lifeboat && lifeboat.available) + lifeboat.status = LIFEBOAT_INACTIVE + + +/// Once refueling is done, marines can optionally hold SD for a time for a stalemate instead of a xeno minor +/datum/controller/subsystem/hijack/proc/unlock_self_destruct() + sd_time_remaining = sd_max_time + sd_unlocked = TRUE + marine_announcement("Fuel reserves full. Manual detonation of fuel reserves by overloading the on-board fusion reactors now possible.", HIJACK_ANNOUNCE) + +/datum/controller/subsystem/hijack/proc/on_generator_overload(obj/structure/machinery/power/fusion_engine/source, new_overloading) + SIGNAL_HANDLER + + if(!generator_ever_overloaded) + generator_ever_overloaded = TRUE + var/datum/hive_status/hive + for(var/hivenumber in GLOB.hive_datum) + hive = GLOB.hive_datum[hivenumber] + if(!length(hive.totalXenos)) + continue + + xeno_announcement(SPAN_XENOANNOUNCE("The talls may be attempting to take their ship down with them in Engineering, stop them!"), hive.hivenumber, XENO_HIJACK_ANNOUNCE) + + adjust_generator_overload_count(new_overloading ? 1 : -1) + +/datum/controller/subsystem/hijack/proc/adjust_generator_overload_count(amount = 1) + var/generator_overload_percent = round(overloaded_generators / maximum_overload_generators, 0.01) + var/old_required_time = sd_min_time + ((1 - generator_overload_percent) * (sd_max_time - sd_min_time)) + percent_completion_remaining = sd_time_remaining / old_required_time + overloaded_generators = clamp(overloaded_generators + amount, 0, maximum_overload_generators) + generator_overload_percent = round(overloaded_generators / maximum_overload_generators, 0.01) + var/new_required_time = sd_min_time + ((1 - generator_overload_percent) * (sd_max_time - sd_min_time)) + sd_time_remaining = percent_completion_remaining * new_required_time + +/datum/controller/subsystem/hijack/proc/heat_engine_room() + engine_room_heated = TRUE + var/area/engine_room = GLOB.areas_by_type[/area/almayer/engineering/engine_core] + engine_room.firealert() + engine_room.temperature = T90C + for(var/mob/current_mob as anything in GLOB.mob_list) + var/area/mob_area = get_area(current_mob) + if(istype(mob_area, /area/almayer/engineering/engine_core)) + to_chat(current_mob, SPAN_BOLDWARNING("You feel the heat of the room increase as the fusion engines whirr louder.")) + +/datum/controller/subsystem/hijack/proc/superheat_engine_room() + engine_room_superheated = TRUE + var/area/engine_room = GLOB.areas_by_type[/area/almayer/engineering/engine_core] + engine_room.firealert() + engine_room.temperature = T120C //slowly deals burn at this temp + for(var/mob/current_mob as anything in GLOB.mob_list) + var/area/mob_area = get_area(current_mob) + if(istype(mob_area, /area/almayer/engineering/engine_core)) + to_chat(current_mob, SPAN_BOLDWARNING("The room feels incredibly hot, you can't take much more of this!")) + +/datum/controller/subsystem/hijack/proc/announce_sd_halfway() + ares_sd_announced = TRUE + marine_announcement("ALERT: Fusion reactor meltdown has reached fifty percent.", HIJACK_ANNOUNCE) + +/datum/controller/subsystem/hijack/proc/detonate_sd() + set waitfor = FALSE + sd_detonated = TRUE + var/creak_picked = pick('sound/effects/creak1.ogg', 'sound/effects/creak2.ogg', 'sound/effects/creak3.ogg') + for(var/mob/current_mob as anything in GLOB.mob_list) + var/turf/current_turf = get_turf(current_mob) + if(!current_mob?.loc || !current_mob.client || !current_turf || !is_mainship_level(current_turf.z)) + continue + + to_chat(current_mob, SPAN_BOLDWARNING("The ship's deck worryingly creaks underneath you.")) + playsound_client(current_mob.client, creak_picked, vol = 50) + + sleep(7 SECONDS) + shakeship(2, 10, TRUE) + + marine_announcement("ALERT: Fusion reactors dangerously overloaded. Runaway meltdown in reactor core imminent.", HIJACK_ANNOUNCE) + sleep(5 SECONDS) + + var/sound_picked = pick('sound/theme/nuclear_detonation1.ogg','sound/theme/nuclear_detonation2.ogg') + for(var/client/player as anything in GLOB.clients) + playsound_client(player, sound_picked, 90) + + var/list/alive_mobs = list() //Everyone who will be destroyed on the zlevel(s). + var/list/dead_mobs = list() //Everyone who only needs to see the cinematic. + for(var/mob/current_mob as anything in GLOB.mob_list) //This only does something cool for the people about to die, but should prove pretty interesting. + var/turf/current_turf = get_turf(current_mob) + if(!current_mob?.loc || !current_turf) + continue + + if(current_mob.stat == DEAD) + dead_mobs |= current_mob + continue + + if(is_mainship_level(current_turf.z)) + alive_mobs |= current_mob + shake_camera(current_mob, 110, 4) + + + sleep(10 SECONDS) + /*Hardcoded for now, since this was never really used for anything else. + Would ideally use a better system for showing cutscenes.*/ + var/atom/movable/screen/cinematic/explosion/explosive_cinematic = new() + + for(var/mob/current_mob as anything in (alive_mobs + dead_mobs)) + if(current_mob?.loc && current_mob.client) + current_mob.client.add_to_screen(explosive_cinematic) //They may have disconnected in the mean time. + + sleep(1.5 SECONDS) //Extra 1.5 seconds to look at the ship. + flick("intro_nuke", explosive_cinematic) + + sleep(3.5 SECONDS) + for(var/mob/current_mob as anything in alive_mobs) + var/turf/current_mob_turf = get_turf(current_mob) + if(!current_mob?.loc || !current_mob_turf) //Who knows, maybe they escaped, or don't exist anymore. + continue + + if(is_mainship_level(current_mob_turf.z)) + if(istype(current_mob.loc, /obj/structure/closet/secure_closet/freezer/fridge)) + continue + + current_mob.death(create_cause_data("nuclear explosion")) + else + current_mob.client.remove_from_screen(explosive_cinematic) //those who managed to escape the z level at last second shouldn't have their view obstructed. + + flick("ship_destroyed", explosive_cinematic) + explosive_cinematic.icon_state = "summary_destroyed" + + for(var/client/player as anything in GLOB.clients) + playsound_client(player, 'sound/effects/explosionfar.ogg', 90) + + + sleep(0.5 SECONDS) + if(SSticker.mode) + SSticker.mode.check_win() + + if(!SSticker.mode) //Just a safety, just in case a mode isn't running, somehow. + to_world(SPAN_ROUNDBODY("Resetting in 30 seconds!")) + sleep(30 SECONDS) + log_game("Rebooting due to nuclear detonation.") + world.Reboot() diff --git a/code/controllers/subsystem/htmlui.dm b/code/controllers/subsystem/htmlui.dm deleted file mode 100644 index 5dc885abc625..000000000000 --- a/code/controllers/subsystem/htmlui.dm +++ /dev/null @@ -1,57 +0,0 @@ -// What in the name of god is this? -// You'd think it'd be some form of process for the HTML interface module. -// But it isn't? -// It's some form of proc queue but ??? -// Does anything even *use* this? - -SUBSYSTEM_DEF(html_ui) - name = "HTMLUI" - wait = 1.7 SECONDS - flags = SS_NO_INIT - runlevels = RUNLEVELS_DEFAULT|RUNLEVEL_LOBBY - var/list/update = list() - -/datum/controller/subsystem/html_ui/fire(resumed = FALSE) - if (update.len) - var/list/L = list() - var/key - - for (var/datum/procqueue_item/item in update) - key = "[item.ref]_[item.procname]" - - if (item.args) - key += "(" - var/first = 1 - for (var/a in item.args) - if (!first) - key += "," - key += "[a]" - first = 0 - key += ")" - - if (!(key in L)) - if (item.args) - call(item.ref, item.procname)(arglist(item.args)) - else - call(item.ref, item.procname)() - - L.Add(key) - - update.Cut() - - -/datum/controller/subsystem/html_ui/proc/queue(ref, procname, ...) - var/datum/procqueue_item/item = new - item.ref = ref - item.procname = procname - - if (args.len > 2) - item.args = args.Copy(3) - - update.Insert(1, item) - - -/datum/procqueue_item - var/ref - var/procname - var/list/args diff --git a/code/controllers/subsystem/inactivity.dm b/code/controllers/subsystem/inactivity.dm index 5edd4e8c5294..6b8542444040 100644 --- a/code/controllers/subsystem/inactivity.dm +++ b/code/controllers/subsystem/inactivity.dm @@ -17,7 +17,7 @@ SUBSYSTEM_DEF(inactivity) return for(var/client/current as anything in GLOB.clients) - if(current.admin_holder && current.admin_holder.rights & R_ADMIN) //Skip admins. + if(current.admin_holder && current.admin_holder.rights & R_MOD) //Skip admins. continue if(current.is_afk(INACTIVITY_KICK)) if(!istype(current.mob, /mob/dead)) diff --git a/code/controllers/subsystem/interior.dm b/code/controllers/subsystem/interior.dm index 389e95fe6022..8fb7ffbfeee7 100644 --- a/code/controllers/subsystem/interior.dm +++ b/code/controllers/subsystem/interior.dm @@ -42,7 +42,7 @@ SUBSYSTEM_DEF(interior) continue if(x >= bounds[1].x && x <= bounds[2].x && y >= bounds[1].y && y <= bounds[2].y) return current_interior - return FALSE + return /// Checks if an atom is in an interior /datum/controller/subsystem/interior/proc/in_interior(loc) @@ -51,7 +51,7 @@ SUBSYSTEM_DEF(interior) if(!isturf(loc)) loc = get_turf(loc) - var/datum/turf_reservation/interior/reservation = SSmapping.used_turfs[loc] + var/datum/weakref/reservation = SSmapping.used_turfs[loc] if(!istype(reservation)) return FALSE diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 6f0f060305b2..913e5dcd50d3 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -6,10 +6,13 @@ SUBSYSTEM_DEF(mapping) var/list/datum/map_config/configs var/list/datum/map_config/next_map_configs + ///Name of all maps var/list/map_templates = list() - + ///Name of all shuttles var/list/shuttle_templates = list() var/list/all_shuttle_templates = list() + ///map_id of all tents + var/list/tent_type_templates = list() var/list/areas_in_z = list() @@ -201,6 +204,7 @@ SUBSYSTEM_DEF(mapping) map_templates[T.name] = T preloadShuttleTemplates() + preload_tent_templates() /proc/generateMapList(filename) . = list() @@ -242,6 +246,11 @@ SUBSYSTEM_DEF(mapping) all_shuttle_templates[item] = S map_templates[S.shuttle_id] = S +/datum/controller/subsystem/mapping/proc/preload_tent_templates() + for(var/template in subtypesof(/datum/map_template/tent)) + var/datum/map_template/tent/new_tent = new template() + tent_type_templates[new_tent.map_id] = new_tent + /datum/controller/subsystem/mapping/proc/RequestBlockReservation(width, height, z, type = /datum/turf_reservation, turf_type_override) UNTIL(initialized && !clearing_reserved_turfs) var/datum/turf_reservation/reserve = new type diff --git a/code/controllers/subsystem/midi.dm b/code/controllers/subsystem/midi.dm deleted file mode 100644 index 158d67cf25ac..000000000000 --- a/code/controllers/subsystem/midi.dm +++ /dev/null @@ -1,45 +0,0 @@ -/datum/midi_record - var/target - var/midi - -SUBSYSTEM_DEF(midi) - name = "Midi" - wait = 2 SECONDS - flags = SS_NO_INIT|SS_BACKGROUND - runlevels = RUNLEVELS_DEFAULT|RUNLEVEL_LOBBY - priority = SS_PRIORITY_MIDI - - var/list/datum/midi_record/prepped_midis = list() - - var/list/datum/midi_record/currentrun = list() - - -/datum/controller/subsystem/midi/stat_entry(msg) - msg = "MR:[prepped_midis.len]" - return ..() - - -/datum/controller/subsystem/midi/fire(resumed = FALSE) - if (!resumed) - currentrun = prepped_midis - prepped_midis = list() - - while (currentrun.len) - var/datum/midi_record/E = currentrun[currentrun.len] - currentrun.len-- - - if (!E) - continue - - E.target << E.midi - - if (MC_TICK_CHECK) - return - -/datum/controller/subsystem/midi/proc/queue(target, midi) - if(!prepped_midis) - prepped_midis = list() - var/datum/midi_record/MR = new() - MR.target = target - MR.midi = midi - prepped_midis.Add(MR) diff --git a/code/controllers/subsystem/minimap.dm b/code/controllers/subsystem/minimap.dm index 6f5b9303a91f..d28fe916291a 100644 --- a/code/controllers/subsystem/minimap.dm +++ b/code/controllers/subsystem/minimap.dm @@ -256,8 +256,6 @@ SUBSYSTEM_DEF(minimaps) removal_cbs[target] = CALLBACK(src, PROC_REF(removeimage), blip, target) RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(remove_marker)) - - /** * removes an image from raw tracked lists, invoked by callback */ @@ -322,7 +320,7 @@ SUBSYSTEM_DEF(minimaps) minimaps_by_z["[z_level]"].images_assoc["[flag]"] -= source /** - * Fetches a /atom/movable/screen/minimap instance or creates on if none exists + * Fetches a /atom/movable/screen/minimap instance or creates one if none exists * Note this does not destroy them when the map is unused, might be a potential thing to do? * Arguments: * * zlevel: zlevel to fetch map for @@ -338,6 +336,170 @@ SUBSYSTEM_DEF(minimaps) hashed_minimaps[hash] = map return map +/** + * Fetches the datum containing an announced flattend map png reference. + * + * Arguments: + * * faction: FACTION_MARINE or XENO_HIVE_NORMAL + */ +/proc/get_tacmap_data_png(faction) + var/list/map_list + + if(faction == FACTION_MARINE) + map_list = GLOB.uscm_flat_tacmap_data + else if(faction == XENO_HIVE_NORMAL) + map_list = GLOB.xeno_flat_tacmap_data + else + return null + + var/map_length = length(map_list) + + if(map_length == 0) + return null + + return map_list[map_length] + +/** + * Fetches the datum containing the latest unannounced flattend map png reference. + * + * Arguments: + * * faction: FACTION_MARINE or XENO_HIVE_NORMAL + */ +/proc/get_unannounced_tacmap_data_png(faction) + if(faction == FACTION_MARINE) + return GLOB.uscm_unannounced_map + else if(faction == XENO_HIVE_NORMAL) + return GLOB.xeno_unannounced_map + + return null + +/** + * Fetches the last set of svg coordinates for the tacmap drawing. + * + * Arguments: + * * faction: which faction get the map for: FACTION_MARINE or XENO_HIVE_NORMAL + */ +/proc/get_tacmap_data_svg(faction) + var/list/map_list + + if(faction == FACTION_MARINE) + map_list = GLOB.uscm_svg_tacmap_data + else if(faction == XENO_HIVE_NORMAL) + map_list = GLOB.xeno_svg_tacmap_data + else + return null + + var/map_length = length(map_list) + + if(map_length == 0) + return null + + return map_list[map_length] + +/** + * Re-sends relevant flattened tacmaps to a single client. + * + * Arguments: + * * user: The mob that is either an observer, marine, or xeno + */ +/proc/resend_current_map_png(mob/user) + if(!user.client) + return + + var/is_observer = user.faction == FACTION_NEUTRAL && isobserver(user) + if(is_observer || user.faction == FACTION_MARINE) + // Send marine maps + var/datum/flattened_tacmap/latest = get_tacmap_data_png(FACTION_MARINE) + if(latest) + SSassets.transport.send_assets(user.client, latest.asset_key) + var/datum/flattened_tacmap/unannounced = get_unannounced_tacmap_data_png(FACTION_MARINE) + if(unannounced && (!latest || latest.asset_key != unannounced.asset_key)) + SSassets.transport.send_assets(user.client, unannounced.asset_key) + + var/mob/living/carbon/xenomorph/xeno = user + if(is_observer || istype(xeno) && xeno.hivenumber == XENO_HIVE_NORMAL) + // Send xeno maps + var/datum/flattened_tacmap/latest = get_tacmap_data_png(XENO_HIVE_NORMAL) + if(latest) + SSassets.transport.send_assets(user.client, latest.asset_key) + var/datum/flattened_tacmap/unannounced = get_unannounced_tacmap_data_png(XENO_HIVE_NORMAL) + if(unannounced && (!latest || latest.asset_key != unannounced.asset_key)) + SSassets.transport.send_assets(user.client, unannounced.asset_key) + +/** + * Flattens the current map and then distributes it for the specified faction as an unannounced map. + * + * Arguments: + * * faction: Which faction to distribute the map to: FACTION_MARINE or XENO_HIVE_NORMAL + * Return: + * * Returns a boolean value, TRUE if the operation was successful, FALSE if it was not (on cooldown generally). + */ +/datum/tacmap/drawing/proc/distribute_current_map_png(faction) + if(faction == FACTION_MARINE) + if(!COOLDOWN_FINISHED(GLOB, uscm_flatten_map_icon_cooldown)) + return FALSE + COOLDOWN_START(GLOB, uscm_flatten_map_icon_cooldown, flatten_map_cooldown_time) + else if(faction == XENO_HIVE_NORMAL) + if(!COOLDOWN_FINISHED(GLOB, xeno_flatten_map_icon_cooldown)) + return FALSE + COOLDOWN_START(GLOB, xeno_flatten_map_icon_cooldown, flatten_map_cooldown_time) + else + return FALSE + + var/icon/flat_map = getFlatIcon(map_holder.map, appearance_flags = TRUE) + if(!flat_map) + to_chat(usr, SPAN_WARNING("A critical error has occurred! Contact a coder.")) // tf2heavy: "Oh, this is bad!" + return FALSE + + // Send to only relevant clients + var/list/faction_clients = list() + for(var/client/client as anything in GLOB.clients) + if(!client || !client.mob) + continue + var/mob/client_mob = client.mob + if(client_mob.faction == faction) + faction_clients += client + else if(client_mob.faction == FACTION_NEUTRAL && isobserver(client_mob)) + faction_clients += client + else if(isxeno(client_mob)) + var/mob/living/carbon/xenomorph/xeno = client_mob + if(xeno.hivenumber == faction) + faction_clients += client + + // This may be unnecessary to do this way if the asset url is always the same as the lookup key + var/flat_tacmap_key = icon2html(flat_map, faction_clients, keyonly = TRUE) + if(!flat_tacmap_key) + to_chat(usr, SPAN_WARNING("A critical error has occurred! Contact a coder.")) + return FALSE + var/flat_tacmap_png = SSassets.transport.get_asset_url(flat_tacmap_key) + var/datum/flattened_tacmap/new_flat = new(flat_tacmap_png, flat_tacmap_key) + + if(faction == FACTION_MARINE) + GLOB.uscm_unannounced_map = new_flat + else //if(faction == XENO_HIVE_NORMAL) + GLOB.xeno_unannounced_map = new_flat + + return TRUE + +/** + * Globally stores svg coords for a given faction. + * + * Arguments: + * * faction: which faction to save the data for: FACTION_MARINE or XENO_HIVE_NORMAL + * * svg_coords: an array of coordinates corresponding to an svg. + * * ckey: the ckey of the user who submitted this + */ +/datum/tacmap/drawing/proc/store_current_svg_coords(faction, svg_coords, ckey) + var/datum/svg_overlay/svg_store_overlay = new(svg_coords, ckey) + + if(faction == FACTION_MARINE) + GLOB.uscm_svg_tacmap_data += svg_store_overlay + else if(faction == XENO_HIVE_NORMAL) + GLOB.xeno_svg_tacmap_data += svg_store_overlay + else + qdel(svg_store_overlay) + debug_log("SVG coordinates for [faction] are not implemented!") + /datum/controller/subsystem/minimaps/proc/fetch_tacmap_datum(zlevel, flags) var/hash = "[zlevel]-[flags]" if(hashed_tacmaps[hash]) @@ -442,7 +604,7 @@ SUBSYSTEM_DEF(minimaps) marker_flags = MINIMAP_FLAG_USCM /datum/action/minimap/observer - minimap_flags = MINIMAP_FLAG_XENO|MINIMAP_FLAG_USCM|MINIMAP_FLAG_UPP|MINIMAP_FLAG_CLF|MINIMAP_FLAG_UPP + minimap_flags = MINIMAP_FLAG_ALL marker_flags = NONE hidden = TRUE @@ -452,17 +614,61 @@ SUBSYSTEM_DEF(minimaps) var/targeted_ztrait = ZTRAIT_GROUND var/atom/owner + /// tacmap holder for holding the minimap var/datum/tacmap_holder/map_holder +/datum/tacmap/drawing + /// A url that will point to the wiki map for the current map as a fall back image + var/static/wiki_map_fallback + + /// color selection for the tactical map canvas, defaults to black. + var/toolbar_color_selection = "black" + var/toolbar_updated_selection = "black" + + var/canvas_cooldown_time = 4 MINUTES + var/flatten_map_cooldown_time = 3 MINUTES + + /// boolean value to keep track if the canvas has been updated or not, the value is used in tgui state. + var/updated_canvas = FALSE + /// current flattend map + var/datum/flattened_tacmap/new_current_map + /// previous flattened map + var/datum/flattened_tacmap/old_map + /// current svg + var/datum/svg_overlay/current_svg + + var/action_queue_change = 0 + + /// The last time the map has been flattened - used as a key to trick react into updating the canvas + var/last_update_time = 0 + /// A temporary lock out time before we can open the new canvas tab to allow the tacmap time to fire + var/tacmap_ready_time = 0 + /datum/tacmap/New(atom/source, minimap_type) allowed_flags = minimap_type owner = source +/datum/tacmap/drawing/status_tab_view/New() + var/datum/tacmap/drawing/status_tab_view/uscm_tacmap + allowed_flags = MINIMAP_FLAG_USCM + owner = uscm_tacmap + +/datum/tacmap/drawing/status_tab_view/xeno/New() + var/datum/tacmap/drawing/status_tab_view/xeno/xeno_tacmap + allowed_flags = MINIMAP_FLAG_XENO + owner = xeno_tacmap + /datum/tacmap/Destroy() map_holder = null owner = null return ..() +/datum/tacmap/drawing/Destroy() + new_current_map = null + old_map = null + current_svg = null + return ..() + /datum/tacmap/tgui_interact(mob/user, datum/tgui/ui) if(!map_holder) var/level = SSmapping.levels_by_trait(targeted_ztrait) @@ -476,11 +682,216 @@ SUBSYSTEM_DEF(minimaps) ui = new(user, src, "TacticalMap") ui.open() +/datum/tacmap/drawing/tgui_interact(mob/user, datum/tgui/ui) + var/mob/living/carbon/xenomorph/xeno = user + var/is_xeno = istype(xeno) + var/faction = is_xeno ? xeno.hivenumber : user.faction + if(faction == FACTION_NEUTRAL && isobserver(user)) + faction = allowed_flags == MINIMAP_FLAG_XENO ? XENO_HIVE_NORMAL : FACTION_MARINE + + new_current_map = get_unannounced_tacmap_data_png(faction) + old_map = get_tacmap_data_png(faction) + current_svg = get_tacmap_data_svg(faction) + + var/use_live_map = faction == FACTION_MARINE && skillcheck(user, SKILL_LEADERSHIP, SKILL_LEAD_EXPERT) || is_xeno + + if(use_live_map && !map_holder) + var/level = SSmapping.levels_by_trait(targeted_ztrait) + if(!level[1]) + return + map_holder = SSminimaps.fetch_tacmap_datum(level[1], allowed_flags) + + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + if(!wiki_map_fallback) + var/wiki_url = CONFIG_GET(string/wikiurl) + var/obj/item/map/current_map/new_map = new + if(wiki_url && new_map.html_link) + wiki_map_fallback ="[wiki_url]/[new_map.html_link]" + else + debug_log("Failed to determine fallback wiki map! Attempted '[wiki_url]/[new_map.html_link]'") + qdel(new_map) + + // Ensure we actually have the map image sent + resend_current_map_png(user) + + if(use_live_map) + tacmap_ready_time = SSminimaps.next_fire + 2 SECONDS + addtimer(CALLBACK(src, PROC_REF(on_tacmap_fire), faction), SSminimaps.next_fire - world.time + 1 SECONDS) + user.client.register_map_obj(map_holder.map) + + ui = new(user, src, "TacticalMap") + ui.open() + +/datum/tacmap/drawing/ui_data(mob/user) + var/list/data = list() + + data["newCanvasFlatImage"] = new_current_map?.flat_tacmap + data["oldCanvasFlatImage"] = old_map?.flat_tacmap + data["svgData"] = current_svg?.svg_data + + data["actionQueueChange"] = action_queue_change + + data["toolbarColorSelection"] = toolbar_color_selection + data["toolbarUpdatedSelection"] = toolbar_updated_selection + + if(isxeno(user)) + data["canvasCooldown"] = max(GLOB.xeno_canvas_cooldown - world.time, 0) + else + data["canvasCooldown"] = max(GLOB.uscm_canvas_cooldown - world.time, 0) + + data["nextCanvasTime"] = canvas_cooldown_time + data["updatedCanvas"] = updated_canvas + + data["lastUpdateTime"] = last_update_time + data["tacmapReady"] = world.time > tacmap_ready_time + + return data + /datum/tacmap/ui_static_data(mob/user) var/list/data = list() - data["mapRef"] = map_holder.map_ref + data["mapRef"] = map_holder?.map_ref + data["canDraw"] = FALSE + data["canViewTacmap"] = TRUE + data["canViewCanvas"] = FALSE + data["isXeno"] = FALSE + return data +/datum/tacmap/drawing/ui_static_data(mob/user) + var/list/data = list() + + data["mapRef"] = map_holder?.map_ref + data["canDraw"] = FALSE + data["mapFallback"] = wiki_map_fallback + + var/mob/living/carbon/xenomorph/xeno = user + var/is_xeno = istype(xeno) + var/faction = is_xeno ? xeno.hivenumber : user.faction + + data["isXeno"] = is_xeno + data["canViewTacmap"] = is_xeno + data["canViewCanvas"] = faction == FACTION_MARINE || faction == XENO_HIVE_NORMAL + + if(faction == FACTION_MARINE && skillcheck(user, SKILL_LEADERSHIP, SKILL_LEAD_EXPERT) || faction == XENO_HIVE_NORMAL && isqueen(user)) + data["canDraw"] = TRUE + data["canViewTacmap"] = TRUE + + return data + +/datum/tacmap/drawing/status_tab_view/ui_static_data(mob/user) + var/list/data = list() + data["mapFallback"] = wiki_map_fallback + data["canDraw"] = FALSE + data["canViewTacmap"] = FALSE + data["canViewCanvas"] = TRUE + data["isXeno"] = FALSE + + return data + +/datum/tacmap/drawing/status_tab_view/xeno/ui_static_data(mob/user) + var/list/data = list() + data["mapFallback"] = wiki_map_fallback + data["canDraw"] = FALSE + data["canViewTacmap"] = FALSE + data["canViewCanvas"] = TRUE + data["isXeno"] = TRUE + + return data + +/datum/tacmap/drawing/ui_close(mob/user) + . = ..() + action_queue_change = 0 + updated_canvas = FALSE + toolbar_color_selection = "black" + toolbar_updated_selection = "black" + +/datum/tacmap/drawing/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + + var/mob/user = ui.user + var/mob/living/carbon/xenomorph/xeno = user + var/faction = istype(xeno) ? xeno.hivenumber : user.faction + if(faction == FACTION_NEUTRAL && isobserver(user)) + faction = allowed_flags == MINIMAP_FLAG_XENO ? XENO_HIVE_NORMAL : FACTION_MARINE + + switch (action) + if ("menuSelect") + if(params["selection"] != "new canvas") + if(updated_canvas) + updated_canvas = FALSE + toolbar_updated_selection = toolbar_color_selection // doing this if it == canvas can cause a latency issue with the stroke. + else + distribute_current_map_png(faction) + last_update_time = world.time + // An attempt to get the image to load on first try in the interface, but doesn't seem always reliable + + new_current_map = get_unannounced_tacmap_data_png(faction) + old_map = get_tacmap_data_png(faction) + current_svg = get_tacmap_data_svg(faction) + + if ("updateCanvas") + // forces state change, this will export the svg. + toolbar_updated_selection = "export" + updated_canvas = TRUE + action_queue_change += 1 + + if ("clearCanvas") + toolbar_updated_selection = "clear" + updated_canvas = FALSE + action_queue_change += 1 + + if ("undoChange") + toolbar_updated_selection = "undo" + updated_canvas = FALSE + action_queue_change += 1 + + if ("selectColor") + var/newColor = params["color"] + if(newColor) + toolbar_color_selection = newColor + toolbar_updated_selection = newColor + action_queue_change += 1 + + if ("onDraw") + updated_canvas = FALSE + + if ("selectAnnouncement") + if(!istype(params["image"], /list)) // potentially very serious? + return FALSE + + if(faction == FACTION_MARINE) + GLOB.uscm_flat_tacmap_data += new_current_map + else if(faction == XENO_HIVE_NORMAL) + GLOB.xeno_flat_tacmap_data += new_current_map + + store_current_svg_coords(faction, params["image"], user) + current_svg = get_tacmap_data_svg(faction) + old_map = get_tacmap_data_png(faction) + + if(faction == FACTION_MARINE) + COOLDOWN_START(GLOB, uscm_canvas_cooldown, canvas_cooldown_time) + var/mob/living/carbon/human/human_leader = user + for(var/datum/squad/current_squad in RoleAuthority.squads) + current_squad.send_maptext("Tactical map update in progress...", "Tactical Map:") + human_leader.visible_message(SPAN_BOLDNOTICE("Tactical map update in progress...")) + playsound_client(human_leader.client, "sound/effects/sos-morse-code.ogg") + notify_ghosts(header = "Tactical Map", message = "The USCM tactical map has been updated.", ghost_sound = "sound/effects/sos-morse-code.ogg", notify_volume = 80, action = NOTIFY_USCM_TACMAP, enter_link = "uscm_tacmap=1", enter_text = "View", source = owner) + + else if(faction == XENO_HIVE_NORMAL) + var/mutable_appearance/appearance = mutable_appearance(icon('icons/mob/hud/actions_xeno.dmi'), "toggle_queen_zoom") + COOLDOWN_START(GLOB, xeno_canvas_cooldown, canvas_cooldown_time) + xeno_maptext("The Queen has updated your hive mind map", "You sense something unusual...", faction) + notify_ghosts(header = "Tactical Map", message = "The Xenomorph tactical map has been updated.", ghost_sound = "sound/voice/alien_distantroar_3.ogg", notify_volume = 50, action = NOTIFY_XENO_TACMAP, enter_link = "xeno_tacmap=1", enter_text = "View", source = user, alert_overlay = appearance) + + toolbar_updated_selection = toolbar_color_selection + message_admins("[key_name(user)] has updated the tactical map for [faction].") + updated_canvas = FALSE + + return TRUE + /datum/tacmap/ui_status(mob/user) if(!(isatom(owner))) return UI_INTERACTIVE @@ -493,7 +904,7 @@ SUBSYSTEM_DEF(minimaps) else return UI_CLOSE -/datum/tacmap/xeno/ui_status(mob/user) +/datum/tacmap/drawing/xeno/ui_status(mob/user) if(!isxeno(user)) return UI_CLOSE @@ -516,3 +927,71 @@ SUBSYSTEM_DEF(minimaps) /datum/tacmap_holder/Destroy() map = null return ..() + +/datum/flattened_tacmap + var/flat_tacmap + var/asset_key + var/time + +/datum/flattened_tacmap/New(flat_tacmap, asset_key) + src.flat_tacmap = flat_tacmap + src.asset_key = asset_key + src.time = time_stamp() + +/datum/svg_overlay + var/svg_data + var/ckey + var/name + var/time + +/datum/svg_overlay/New(svg_data, mob/user) + src.svg_data = svg_data + src.ckey = user?.persistent_ckey + src.name = user?.real_name + src.time = time_stamp() + +/// Callback when timer indicates the tacmap is flattenable now +/datum/tacmap/drawing/proc/on_tacmap_fire(faction) + distribute_current_map_png(faction) + last_update_time = world.time + +/// Gets the MINIMAP_FLAG for the provided faction or hivenumber if one exists +/proc/get_minimap_flag_for_faction(faction) + switch(faction) + if(XENO_HIVE_NORMAL) + return MINIMAP_FLAG_XENO + if(FACTION_MARINE) + return MINIMAP_FLAG_USCM + if(FACTION_UPP) + return MINIMAP_FLAG_UPP + if(FACTION_WY) + return MINIMAP_FLAG_USCM + if(FACTION_CLF) + return MINIMAP_FLAG_CLF + if(FACTION_PMC) + return MINIMAP_FLAG_PMC + if(FACTION_YAUTJA) + return MINIMAP_FLAG_YAUTJA + if(XENO_HIVE_CORRUPTED) + return MINIMAP_FLAG_XENO_CORRUPTED + if(XENO_HIVE_ALPHA) + return MINIMAP_FLAG_XENO_ALPHA + if(XENO_HIVE_BRAVO) + return MINIMAP_FLAG_XENO_BRAVO + if(XENO_HIVE_CHARLIE) + return MINIMAP_FLAG_XENO_CHARLIE + if(XENO_HIVE_DELTA) + return MINIMAP_FLAG_XENO_DELTA + if(XENO_HIVE_FERAL) + return MINIMAP_FLAG_XENO_FERAL + if(XENO_HIVE_TAMED) + return MINIMAP_FLAG_XENO_TAMED + if(XENO_HIVE_MUTATED) + return MINIMAP_FLAG_XENO_MUTATED + if(XENO_HIVE_FORSAKEN) + return MINIMAP_FLAG_XENO_FORSAKEN + if(XENO_HIVE_YAUTJA) + return MINIMAP_FLAG_YAUTJA + if(XENO_HIVE_RENEGADE) + return MINIMAP_FLAG_XENO_RENEGADE + return 0 diff --git a/code/controllers/subsystem/processing/defprocess.dm b/code/controllers/subsystem/processing/defprocess.dm new file mode 100644 index 000000000000..3701a0617a7a --- /dev/null +++ b/code/controllers/subsystem/processing/defprocess.dm @@ -0,0 +1,5 @@ +PROCESSING_SUBSYSTEM_DEF(defprocess) + name = "Defenses Processing" + priority = SS_PRIORITY_DEFENSES + flags = SS_NO_INIT + wait = 0.7 SECONDS diff --git a/code/controllers/subsystem/smoke_system.dm b/code/controllers/subsystem/smoke_system.dm deleted file mode 100644 index 2010687fcba2..000000000000 --- a/code/controllers/subsystem/smoke_system.dm +++ /dev/null @@ -1,31 +0,0 @@ -var/list/active_smoke_effects = list() - - -SUBSYSTEM_DEF(smoke_effects) - name = "Smoke Effects" - wait = 1 SECONDS - flags = SS_NO_INIT | SS_KEEP_TIMING - priority = SS_PRIORITY_OBJECTS - - var/list/currentrun = list() - -/datum/controller/subsystem/smoke_effects/stat_entry(msg) - msg = "P:[active_smoke_effects.len]" - return ..() - - -/datum/controller/subsystem/smoke_effects/fire(resumed = FALSE) - if(!resumed) - currentrun = active_smoke_effects.Copy() - - while(currentrun.len) - var/obj/effect/particle_effect/smoke/E = currentrun[currentrun.len] - currentrun.len-- - - if(!E || QDELETED(E)) - continue - - E.process() - - if(MC_TICK_CHECK) - return diff --git a/code/controllers/subsystem/sound.dm b/code/controllers/subsystem/sound.dm index 1935294394e7..4fdfd7935349 100644 --- a/code/controllers/subsystem/sound.dm +++ b/code/controllers/subsystem/sound.dm @@ -41,5 +41,5 @@ SUBSYSTEM_DEF(sound) if(VI?.ready) var/list/bounds = VI.get_middle_coords() if(bounds.len >= 2) - hearers |= SSquadtree.players_in_range(RECT(bounds[1], bounds[2], VI.map_template.height, VI.map_template.width), bounds[3]) + hearers |= SSquadtree.players_in_range(RECT(bounds[1], bounds[2], VI.map_template.width, VI.map_template.height), bounds[3]) template_queue[template] = hearers diff --git a/code/controllers/subsystem/stamina.dm b/code/controllers/subsystem/stamina.dm deleted file mode 100644 index 84d5b4038cd0..000000000000 --- a/code/controllers/subsystem/stamina.dm +++ /dev/null @@ -1,25 +0,0 @@ -var/global/list/active_staminas = list() - -SUBSYSTEM_DEF(stamina) - name = "Stamina" - wait = 2 SECONDS - priority = SS_PRIORITY_STAMINA - flags = SS_NO_INIT - var/list/currentrun = list() - - -/datum/controller/subsystem/stamina/fire(resumed = FALSE) - if (!resumed) - currentrun = active_staminas.Copy() - - while (currentrun.len) - var/datum/stamina/S = currentrun[currentrun.len] - currentrun.len-- - - if (!S || QDELETED(S)) - continue - - S.process() - - if (MC_TICK_CHECK) - return diff --git a/code/controllers/subsystem/stats_collector.dm b/code/controllers/subsystem/stats_collector.dm deleted file mode 100644 index de66e3b2c6b1..000000000000 --- a/code/controllers/subsystem/stats_collector.dm +++ /dev/null @@ -1,17 +0,0 @@ -/// Collects simple round statistics periodically -SUBSYSTEM_DEF(stats_collector) - name = "Round Stats" - wait = 30 SECONDS - priority = SS_PRIORITY_PAGER_STATUS - runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY - flags = SS_KEEP_TIMING | SS_NO_INIT - - var/stat_ticks = 0 - var/players_counter = 0 - -/datum/controller/subsystem/stats_collector/fire(resumed = FALSE) - players_counter += length(GLOB.clients) - stat_ticks++ - -/datum/controller/subsystem/stats_collector/proc/get_avg_players() - return players_counter / stat_ticks diff --git a/code/controllers/subsystem/teleporter.dm b/code/controllers/subsystem/teleporter.dm deleted file mode 100644 index b753bdb0d519..000000000000 --- a/code/controllers/subsystem/teleporter.dm +++ /dev/null @@ -1,10 +0,0 @@ -// Master teleporter controller. -SUBSYSTEM_DEF(teleporter) - name = "Teleporter" - wait = 5 SECONDS - init_order = SS_INIT_TELEPORTER - priority = SS_PRIORITY_TELEPORTER - flags = SS_NO_FIRE|SS_NO_INIT - - var/list/teleporters_by_id = list() // Associative list of teleporters by ID, master list of teleporters to process - var/list/teleporters = list() // Process list (identical contents to teleporters_by_id) diff --git a/code/controllers/subsystem/xenocon.dm b/code/controllers/subsystem/xenocon.dm deleted file mode 100644 index d16e59bd9813..000000000000 --- a/code/controllers/subsystem/xenocon.dm +++ /dev/null @@ -1,18 +0,0 @@ -SUBSYSTEM_DEF(xenocon) - name = "XENOCON" - wait = 5 SECONDS - priority = SS_PRIORITY_INACTIVITY - flags = SS_NO_INIT - var/rewarded = FALSE - -/datum/controller/subsystem/xenocon/fire(resumed = FALSE) - if(rewarded) - return - - var/datum/hive_status/hive - for(var/hivenumber in GLOB.hive_datum) - hive = GLOB.hive_datum[hivenumber] - if(hive.xenocon_points >= XENOCON_THRESHOLD) - var/datum/emergency_call/em_call = new /datum/emergency_call/xenos/platoon() - em_call.activate() - rewarded = TRUE diff --git a/code/datums/agents/tools/chloroform.dm b/code/datums/agents/tools/chloroform.dm index 464533309bcc..c6e3320688eb 100644 --- a/code/datums/agents/tools/chloroform.dm +++ b/code/datums/agents/tools/chloroform.dm @@ -47,8 +47,8 @@ /obj/item/weapon/chloroform/proc/grab_stun(mob/living/M, mob/living/user) M.anchored = TRUE - M.frozen = TRUE - M.density = FALSE + ADD_TRAIT(M, TRAIT_IMMOBILIZED, CHLOROFORM_TRAIT) + ADD_TRAIT(M, TRAIT_UNDENSE, CHLOROFORM_TRAIT) M.able_to_speak = FALSE M.update_canmove() @@ -82,7 +82,8 @@ M.density = TRUE M.able_to_speak = TRUE M.layer = MOB_LAYER - M.unfreeze() + REMOVE_TRAIT(M, TRAIT_IMMOBILIZED, CHLOROFORM_TRAIT) + REMOVE_TRAIT(M, TRAIT_UNDENSE, CHLOROFORM_TRAIT) QDEL_NULL(mask_item) diff --git a/code/datums/ammo/xeno.dm b/code/datums/ammo/xeno.dm index 9d91920ac6f8..75c78298fe4f 100644 --- a/code/datums/ammo/xeno.dm +++ b/code/datums/ammo/xeno.dm @@ -37,7 +37,7 @@ neuro_callback = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(apply_neuro)) -/proc/apply_neuro(mob/M, power, insta_neuro) +/proc/apply_neuro(mob/living/M, power, insta_neuro) if(skillcheck(M, SKILL_ENDURANCE, SKILL_ENDURANCE_MAX) && !insta_neuro) M.visible_message(SPAN_DANGER("[M] withstands the neurotoxin!")) return //endurance 5 makes you immune to weak neurotoxin @@ -69,7 +69,7 @@ M.adjust_effect(1 * power, WEAKEN) // KD them a bit more M.visible_message(SPAN_DANGER("[M] falls prone.")) -/proc/apply_scatter_neuro(mob/M) +/proc/apply_scatter_neuro(mob/living/M) if(ishuman(M)) var/mob/living/carbon/human/H = M if(skillcheck(M, SKILL_ENDURANCE, SKILL_ENDURANCE_MAX)) @@ -317,7 +317,7 @@ shrapnel_type = /obj/item/shard/shrapnel/bone_chips shrapnel_chance = 60 -/datum/ammo/xeno/bone_chips/on_hit_mob(mob/M, obj/projectile/P) +/datum/ammo/xeno/bone_chips/on_hit_mob(mob/living/M, obj/projectile/P) if(iscarbon(M)) var/mob/living/carbon/C = M if((HAS_FLAG(C.status_flags, XENO_HOST) && HAS_TRAIT(C, TRAIT_NESTED)) || C.stat == DEAD) @@ -347,7 +347,7 @@ damage = 10 shrapnel_chance = 0 -/datum/ammo/xeno/bone_chips/spread/runner/on_hit_mob(mob/M, obj/projectile/P) +/datum/ammo/xeno/bone_chips/spread/runner/on_hit_mob(mob/living/M, obj/projectile/P) if(iscarbon(M)) var/mob/living/carbon/C = M if((HAS_FLAG(C.status_flags, XENO_HOST) && HAS_TRAIT(C, TRAIT_NESTED)) || C.stat == DEAD) diff --git a/code/datums/components/autofire/autofire.dm b/code/datums/components/autofire/autofire.dm index d052127effc3..455fb70a9fa1 100644 --- a/code/datums/components/autofire/autofire.dm +++ b/code/datums/components/autofire/autofire.dm @@ -82,6 +82,8 @@ /datum/component/automatedfire/autofire/proc/initiate_shot() SIGNAL_HANDLER if(shooting)//if we are already shooting, it means the shooter is still on cooldown + if(bursting && (world.time > (next_fire + (burstfire_shot_delay * burst_shots_to_fire)))) + hard_reset() return shooting = TRUE process_shot() diff --git a/code/datums/components/cell.dm b/code/datums/components/cell.dm new file mode 100644 index 000000000000..81ef3733e2e2 --- /dev/null +++ b/code/datums/components/cell.dm @@ -0,0 +1,202 @@ +#define UNLIMITED_CHARGE -1 +#define UNLIMITED_DISTANCE -1 + +/datum/component/cell + dupe_mode = COMPONENT_DUPE_UNIQUE + /// Maximum charge of the power cell, set to -1 for infinite charge + var/max_charge = 10000 + /// Initial max charge of the power cell + var/initial_max_charge + /// Current charge of power cell + var/charge = 10000 + /// If the component can be recharged by hitting its parent with a cell + var/hit_charge = FALSE + /// The maximum amount that can be recharged per tick when using a cell to recharge this component + var/max_recharge_tick = 400 + /// If draining charge on process(), how much to drain per process call + var/charge_drain = 10 + /// If the parent should show cell charge on examine + var/display_charge = TRUE + /// From how many tiles at the highest someone can examine the parent to see the charge + var/charge_examine_range = 1 + /// If the component requires a cell to be inserted to work instead of having an integrated one + var/cell_insert = FALSE + /// Ref to an inserted cell. Should only be null if cell_insert is false + var/obj/item/cell/inserted_cell + + +/datum/component/cell/Initialize( + max_charge = 10000, + hit_charge = FALSE, + max_recharge_tick = 400, + charge_drain = 10, + display_charge = TRUE, + charge_examine_range = 1, + cell_insert = FALSE, + ) + + . = ..() + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + + src.max_charge = max_charge + charge = max_charge + src.hit_charge = hit_charge + src.max_recharge_tick = max_recharge_tick + src.charge_drain = charge_drain + src.display_charge = display_charge + src.charge_examine_range = charge_examine_range + src.cell_insert = cell_insert + +/datum/component/cell/Destroy(force, silent) + QDEL_NULL(inserted_cell) + return ..() + + +/datum/component/cell/RegisterWithParent() + ..() + RegisterSignal(parent, list(COMSIG_PARENT_ATTACKBY, COMSIG_ITEM_ATTACKED), PROC_REF(on_object_hit)) + RegisterSignal(parent, COMSIG_CELL_ADD_CHARGE, PROC_REF(add_charge)) + RegisterSignal(parent, COMSIG_CELL_USE_CHARGE, PROC_REF(use_charge)) + RegisterSignal(parent, COMSIG_CELL_CHECK_CHARGE, PROC_REF(has_charge)) + RegisterSignal(parent, COMSIG_CELL_START_TICK_DRAIN, PROC_REF(start_drain)) + RegisterSignal(parent, COMSIG_CELL_STOP_TICK_DRAIN, PROC_REF(stop_drain)) + RegisterSignal(parent, COMSIG_CELL_REMOVE_CELL, PROC_REF(remove_cell)) + RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) + RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp)) + +/datum/component/cell/process() + use_charge(null, charge_drain) + +/datum/component/cell/proc/on_emp(datum/source, severity) + SIGNAL_HANDLER + + use_charge(null, round(max_charge / severity)) + +/datum/component/cell/proc/start_drain(datum/source) + SIGNAL_HANDLER + + START_PROCESSING(SSobj, src) + +/datum/component/cell/proc/stop_drain(datum/source) + SIGNAL_HANDLER + + STOP_PROCESSING(SSobj, src) + +/datum/component/cell/proc/on_examine(datum/source, mob/examiner, list/examine_text) + SIGNAL_HANDLER + + if(!display_charge) + return + + if((charge_examine_range != UNLIMITED_DISTANCE) && get_dist(examiner, parent) > charge_examine_range) + return + + examine_text += "A small gauge in the corner reads \"Power: [round(100 * charge / max_charge)]%\"." + +/datum/component/cell/proc/on_object_hit(datum/source, obj/item/cell/attack_obj, mob/living/attacker, params) + SIGNAL_HANDLER + + if(!hit_charge || !istype(attack_obj)) + return + + if(!cell_insert) + INVOKE_ASYNC(src, PROC_REF(charge_from_cell), attack_obj, attacker) + + else + insert_cell(attack_obj, attacker) + + return COMPONENT_NO_AFTERATTACK|COMPONENT_CANCEL_ITEM_ATTACK + +/datum/component/cell/proc/insert_cell(obj/item/cell/power_cell, mob/living/user) + if(inserted_cell) + to_chat(user, SPAN_WARNING("There's already a power cell in [parent]!")) + return + + if(SEND_SIGNAL(parent, COMSIG_CELL_TRY_INSERT_CELL) & COMPONENT_CANCEL_CELL_INSERT) + return + + power_cell.drop_to_floor(user) + power_cell.forceMove(parent) + inserted_cell = power_cell + charge = power_cell.charge + max_charge = power_cell.maxcharge + +/datum/component/cell/proc/remove_cell(mob/living/user) + SIGNAL_HANDLER + + user.put_in_hands(inserted_cell, TRUE) + to_chat(user, SPAN_NOTICE("You remove [inserted_cell] from [parent].")) + inserted_cell = null + max_charge = initial_max_charge + charge = 0 + +/datum/component/cell/proc/charge_from_cell(obj/item/cell/power_cell, mob/living/user) + if(max_charge == UNLIMITED_CHARGE) + to_chat(user, SPAN_WARNING("[parent] doesn't need more power.")) + return + + while(charge < max_charge) + if(SEND_SIGNAL(parent, COMSIG_CELL_TRY_RECHARGING, user) & COMPONENT_CELL_NO_RECHARGE) + return + + if(power_cell.charge <= 0) + to_chat(user, SPAN_WARNING("[power_cell] is completely dry.")) + return + + if(!do_after(user, 1 SECONDS, (INTERRUPT_ALL & (~INTERRUPT_MOVED)), BUSY_ICON_BUILD, power_cell, INTERRUPT_DIFF_LOC)) + to_chat(user, SPAN_WARNING("You were interrupted.")) + return + + if(power_cell.charge <= 0) + return + + var/to_transfer = min(max_recharge_tick, power_cell.charge, (max_charge - charge)) + if(power_cell.use(to_transfer)) + add_charge(null, to_transfer) + to_chat(user, "You transfer some power between [power_cell] and [parent]. The gauge now reads: [round(100 * charge / max_charge)]%.") + +/datum/component/cell/proc/add_charge(datum/source, charge_add = 0) + SIGNAL_HANDLER + + if(max_charge == UNLIMITED_CHARGE) + return + + if(!charge_add) + return + + charge = clamp(charge + charge_add, 0, max_charge) + +/datum/component/cell/proc/use_charge(datum/source, charge_use = 0) + SIGNAL_HANDLER + + if(max_charge == UNLIMITED_CHARGE) + return + + if(!charge_use) + return + + if(!charge) + return COMPONENT_CELL_NO_USE_CHARGE + + charge = clamp(charge - charge_use, 0, max_charge) + + if(!charge) + on_charge_empty() + return + +/datum/component/cell/proc/has_charge(datum/source, charge_amount = 0) + SIGNAL_HANDLER + + if(!charge) + return COMPONENT_CELL_CHARGE_INSUFFICIENT + + if(charge < charge_amount) + return COMPONENT_CELL_CHARGE_INSUFFICIENT + +/datum/component/cell/proc/on_charge_empty() + stop_drain() + SEND_SIGNAL(parent, COMSIG_CELL_OUT_OF_CHARGE) + +#undef UNLIMITED_CHARGE +#undef UNLIMITED_DISTANCE diff --git a/code/datums/components/crate_tag.dm b/code/datums/components/crate_tag.dm new file mode 100644 index 000000000000..379df82a2084 --- /dev/null +++ b/code/datums/components/crate_tag.dm @@ -0,0 +1,36 @@ +/datum/component/crate_tag + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + /// The crate tag used for notifications and as label + var/name + +/datum/component/crate_tag/Initialize(name, obj/structure/closet/crate/masquarade_type) + var/obj/structure/closet/crate/crate = parent + if(!istype(crate)) + return COMPONENT_INCOMPATIBLE + setup(name, masquarade_type) + RegisterSignal(parent, COMSIG_STRUCTURE_CRATE_SQUAD_LAUNCHED, PROC_REF(notify_squad)) + +/datum/component/crate_tag/InheritComponent(datum/component/C, i_am_original, name, obj/structure/closet/crate/masquarade_type) + . = ..() + setup(name, masquarade_type) + +/datum/component/crate_tag/proc/setup(name, obj/structure/closet/crate/masquarade_type) + var/obj/structure/closet/crate/crate = parent + if(masquarade_type) + crate.name = initial(masquarade_type.name) + crate.desc = initial(masquarade_type.desc) + crate.icon_opened = initial(masquarade_type.icon_opened) + crate.icon_closed = initial(masquarade_type.icon_closed) + if(crate.opened) + crate.icon_state = crate.icon_opened + else + crate.icon_state = crate.icon_closed + if(name) + parent.AddComponent(/datum/component/label, name) + src.name = name // Keep it around additionally for notifications + +/// Handler to notify an overwatched squad that this crate has been dropped for them +/datum/component/crate_tag/proc/notify_squad(datum/source, datum/squad/squad) + SIGNAL_HANDLER + squad.send_message("'[name]' supply drop incoming. Heads up!") + squad.send_maptext(name, "Incoming Supply Drop:") diff --git a/code/datums/components/weed_food.dm b/code/datums/components/weed_food.dm index ce6c17e0af95..16be8665f55b 100644 --- a/code/datums/components/weed_food.dm +++ b/code/datums/components/weed_food.dm @@ -259,7 +259,7 @@ active = FALSE merged = TRUE - parent_mob.density = FALSE + ADD_TRAIT(parent_mob, TRAIT_UNDENSE, XENO_WEED_TRAIT) parent_mob.anchored = TRUE parent_mob.mouse_opacity = MOUSE_OPACITY_TRANSPARENT parent_mob.plane = FLOOR_PLANE diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index b60b20bc9026..213a959296fb 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -133,6 +133,9 @@ GLOBAL_DATUM_INIT(data_core, /datum/datacore, new) continue dept_flags |= FLAG_SHOW_MARINES squad_sublists[squad_name] = TRUE + ///If it is a real squad in the USCM squad list to prevent the crew manifest from breaking + if(!(squad_name in ROLES_SQUAD_ALL)) + continue LAZYSET(marines_by_squad[squad_name][real_rank], name, rank) //here we fill manifest diff --git a/code/datums/datum.dm b/code/datums/datum.dm index b26c6afe4d91..7d497785a72a 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -19,8 +19,8 @@ /// Active timers with this datum as the target var/list/active_timers - /// Status traits attached. - var/list/status_traits + /// Status traits attached to this datum. associative list of the form: list(trait name (string) = list(source1, source2, source3,...)) + var/list/_status_traits /** * Components attached to this datum diff --git a/code/datums/disease.dm b/code/datums/disease.dm index 92986b668b47..d2f466ebeb39 100644 --- a/code/datums/disease.dm +++ b/code/datums/disease.dm @@ -131,7 +131,7 @@ var/list/diseases = typesof(/datum/disease) - /datum/disease /datum/disease/process() if(!holder) - active_diseases -= src + SSdisease.all_diseases -= src return if(prob(65)) spread(holder) @@ -176,7 +176,7 @@ var/list/diseases = typesof(/datum/disease) - /datum/disease /datum/disease/New(process=TRUE)//process = 1 - adding the object to global list. List is processed by master controller. cure_list = list(cure_id) // to add more cures, add more vars to this list in the actual disease's New() if(process) // Viruses in list are considered active. - active_diseases += src + SSdisease.all_diseases += src initial_spread = spread /datum/disease/proc/IsSame(datum/disease/D) @@ -191,5 +191,5 @@ var/list/diseases = typesof(/datum/disease) - /datum/disease /datum/disease/Destroy() affected_mob = null holder = null - active_diseases -= src + SSdisease.all_diseases -= src . = ..() diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index 6440c9734374..ad4703ba65fe 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -391,7 +391,7 @@ var/list/advance_cures = list( D.AssignName(new_name) D.Refresh() - for(var/datum/disease/advance/AD in active_diseases) + for(var/datum/disease/advance/AD in SSdisease.all_diseases) AD.Refresh() for(var/mob/living/carbon/human/H in shuffle(GLOB.alive_mob_list.Copy())) @@ -409,7 +409,7 @@ var/list/advance_cures = list( /* /mob/verb/test() - for(var/datum/disease/D in active_diseases) + for(var/datum/disease/D in SSdisease.all_diseases) to_chat(src, "[D.name] - [D.holder]") */ diff --git a/code/datums/diseases/black_goo.dm b/code/datums/diseases/black_goo.dm index 38a26f3648c7..d4d9b6f50996 100644 --- a/code/datums/diseases/black_goo.dm +++ b/code/datums/diseases/black_goo.dm @@ -211,6 +211,15 @@ . = ..() reagents.add_reagent("antiZed", 30) +/obj/item/reagent_container/glass/bottle/labeled_black_goo_cure + name = "\"Pathogen\" cure bottle" + desc = "The bottle has a biohazard symbol on the front, and has a label, designating its use against Agent A0-3959X.91–15, colloquially known as the \"Black Goo\"." + icon_state = "bottle20" + +/obj/item/reagent_container/glass/bottle/labeled_black_goo_cure/Initialize() + . = ..() + reagents.add_reagent("antiZed", 60) + /datum/language/zombie name = "Zombie" desc = "A growling, guttural method of communication, only Zombies seem to be capable of producing these sounds." diff --git a/code/datums/effects/xeno_strains/boiler_trap.dm b/code/datums/effects/xeno_strains/boiler_trap.dm index 61451391e816..1833b9641a9a 100644 --- a/code/datums/effects/xeno_strains/boiler_trap.dm +++ b/code/datums/effects/xeno_strains/boiler_trap.dm @@ -4,19 +4,17 @@ effect_name = "boiler trap" duration = null flags = INF_DURATION - /// Ghetto flag indicating whether we actually placed the freeze or not, until we have an actual effects system - var/freezer = FALSE /datum/effects/boiler_trap/New(atom/A, mob/from, last_dmg_source, zone) . = ..() if(!QDELETED(src)) var/mob/M = affected_atom - freezer = M.freeze() + ADD_TRAIT(M, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY(effect_name)) /datum/effects/boiler_trap/Destroy(force) - if(ismob(affected_atom) && freezer) + if(ismob(affected_atom)) var/mob/M = affected_atom - M.unfreeze() + REMOVE_TRAIT(M, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY(effect_name)) return ..() /datum/effects/boiler_trap/validate_atom(atom/A) @@ -29,7 +27,5 @@ . = ..() if(!.) return FALSE var/mob/M = affected_atom - if(M.frozen) return TRUE - if(!freezer) - freezer = M.freeze() + ADD_TRAIT(M, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY(effect_name)) return TRUE diff --git a/code/datums/emergency_calls/cbrn.dm b/code/datums/emergency_calls/cbrn.dm new file mode 100644 index 000000000000..3a6b1c640632 --- /dev/null +++ b/code/datums/emergency_calls/cbrn.dm @@ -0,0 +1,80 @@ +/datum/emergency_call/cbrn + name = "CBRN (Squad)" + arrival_message = "A CBRN squad has been dispatched to your ship. Stand by." + objectives = "Handle the chemical, biological, radiological, or nuclear threat. Further orders may be provided." + mob_min = 3 + mob_max = 5 + max_heavies = 0 + max_smartgunners = 0 + +/datum/emergency_call/cbrn/create_member(datum/mind/new_mind, turf/override_spawn_loc) + var/turf/spawn_loc = override_spawn_loc ? override_spawn_loc : get_spawn_point() + + if(!istype(spawn_loc)) + return //Didn't find a useable spawn point. + + var/mob/living/carbon/human/mob = new(spawn_loc) + new_mind.transfer_to(mob, TRUE) + + if(!leader && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(mob.client, JOB_SQUAD_LEADER, time_required_for_job)) + leader = mob + arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/leader, TRUE, TRUE) + to_chat(mob, SPAN_ROLE_HEADER("You are the CBRN Fireteam Leader!")) + + else if(medics < max_medics && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(mob.client, JOB_SQUAD_MEDIC, time_required_for_job)) + medics++ + arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/medic, TRUE, TRUE) + to_chat(mob, SPAN_ROLE_HEADER("You are the CBRN Squad Medic!")) + + else if(engineers < max_engineers && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_ENGINEER) && check_timelock(mob.client, JOB_SQUAD_ENGI, time_required_for_job)) + engineers++ + arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/engineer, TRUE, TRUE) + to_chat(mob, SPAN_ROLE_HEADER("You are the CBRN Squad Engineer!")) + + else + arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/standard, TRUE, TRUE) + to_chat(mob, SPAN_ROLE_HEADER("You are a CBRN Squad Rifleman!")) + + to_chat(mob, SPAN_ROLE_BODY("You are a member of the USCM's CBRN. The CBRN is a force that specializes in handling chemical, biological, radiological, and nuclear threats.")) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) + +/datum/emergency_call/cbrn/ert + name = "CBRN (Distress)" + arrival_message = "Your distress signal has been received and we are dispatching the nearest CBRN squad to board with you now. Stand by." + probability = 10 + +/datum/emergency_call/cbrn/ert/New() + ..() + objectives = "Investigate the distress signal aboard the [MAIN_SHIP_NAME]." + +/datum/emergency_call/cbrn/specialists + name = "CBRN (Specialists)" + mob_min = 2 + mob_max = 5 + max_engineers = 0 + max_medics = 0 + +/datum/emergency_call/cbrn/specialists/New() + var/cbrn_ship_name = "Unit [pick(nato_phonetic_alphabet)]-[rand(1, 99)]" + arrival_message = "[MAIN_SHIP_NAME], CBRN [cbrn_ship_name] has been dispatched. Follow all orders provided by [cbrn_ship_name]." + objectives = "You are a specialist team in [cbrn_ship_name] dispatched to quell a threat to [MAIN_SHIP_NAME]. Further orders may be provided." + +/datum/emergency_call/cbrn/specialists/create_member(datum/mind/new_mind, turf/override_spawn_loc) + var/turf/spawn_loc = override_spawn_loc ? override_spawn_loc : get_spawn_point() + + if(!istype(spawn_loc)) + return //Didn't find a useable spawn point. + + var/mob/living/carbon/human/mob = new(spawn_loc) + new_mind.transfer_to(mob, TRUE) + + if(!leader && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(mob.client, JOB_SQUAD_LEADER, time_required_for_job)) + leader = mob + arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/specialist/lead, TRUE, TRUE) + to_chat(mob, SPAN_ROLE_HEADER("You are the CBRN Specialist Squad Leader!")) + else + arm_equipment(mob, /datum/equipment_preset/uscm/cbrn/specialist, TRUE, TRUE) + to_chat(mob, SPAN_ROLE_HEADER("You are a CBRN Specialist!")) + + to_chat(mob, SPAN_ROLE_BODY("You are a member of the USCM's CBRN. The CBRN is a force that specializes in handling chemical, biological, radiological, and nuclear threats.")) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) diff --git a/code/datums/emergency_calls/deathsquad.dm b/code/datums/emergency_calls/deathsquad.dm index 0bfab8fbf2b7..1cd5bdef6713 100644 --- a/code/datums/emergency_calls/deathsquad.dm +++ b/code/datums/emergency_calls/deathsquad.dm @@ -3,7 +3,7 @@ //Weyland-Yutani Deathsquad - W-Y Deathsquad. Event only /datum/emergency_call/death - name = "Weyland Whiteout Operators" + name = "Weyland Whiteout Operators (!DEATHSQUAD!)" mob_max = 8 mob_min = 5 arrival_message = "'!`2*%slau#*jer t*h$em a!l%. le&*ve n(o^ w&*nes%6es.*v$e %#d ou^'" @@ -18,41 +18,76 @@ // DEATH SQUAD-------------------------------------------------------------------------------- -/datum/emergency_call/death/create_member(datum/mind/M, turf/override_spawn_loc) +/datum/emergency_call/death/create_member(datum/mind/player, turf/override_spawn_loc) var/turf/spawn_loc = override_spawn_loc ? override_spawn_loc : get_spawn_point() if(!istype(spawn_loc)) return //Didn't find a useable spawn point. - var/mob/living/carbon/human/H = new(spawn_loc) - M.transfer_to(H, TRUE) + var/mob/living/carbon/human/person = new(spawn_loc) + player.transfer_to(person, TRUE) - if(!leader && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(H.client, JOB_SQUAD_LEADER, time_required_for_job)) - leader = H - to_chat(H, SPAN_ROLE_HEADER("You are the Whiteout Team Leader!")) - to_chat(H, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) - arm_equipment(H, /datum/equipment_preset/pmc/w_y_whiteout/leader, TRUE, TRUE) - else if(medics < max_medics && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(H.client, JOB_SQUAD_MEDIC, time_required_for_job)) + if(!leader && HAS_FLAG(person.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(person.client, JOB_SQUAD_LEADER, time_required_for_job)) + leader = person + to_chat(person, SPAN_ROLE_HEADER("You are the Whiteout Team Leader!")) + to_chat(person, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) + arm_equipment(person, /datum/equipment_preset/pmc/w_y_whiteout/leader, TRUE, TRUE) + else if(medics < max_medics && HAS_FLAG(person.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(person.client, JOB_SQUAD_MEDIC, time_required_for_job)) medics++ - to_chat(H, SPAN_ROLE_HEADER("You are a Whiteout Team Medic!")) - to_chat(H, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) - arm_equipment(H, /datum/equipment_preset/pmc/w_y_whiteout/medic, TRUE, TRUE) - else if(heavies < max_heavies && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_SMARTGUNNER) && check_timelock(H.client, list(JOB_SQUAD_SPECIALIST, JOB_SQUAD_SMARTGUN), time_required_for_job)) + to_chat(person, SPAN_ROLE_HEADER("You are a Whiteout Team Medic!")) + to_chat(person, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) + arm_equipment(person, /datum/equipment_preset/pmc/w_y_whiteout/medic, TRUE, TRUE) + else if(heavies < max_heavies && HAS_FLAG(person.client.prefs.toggles_ert, PLAY_SMARTGUNNER) && check_timelock(person.client, list(JOB_SQUAD_SPECIALIST, JOB_SQUAD_SMARTGUN), time_required_for_job)) heavies++ - to_chat(H, SPAN_ROLE_HEADER("You are a Whiteout Team Terminator!")) - to_chat(H, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) - arm_equipment(H, /datum/equipment_preset/pmc/w_y_whiteout/terminator, TRUE, TRUE) + to_chat(person, SPAN_ROLE_HEADER("You are a Whiteout Team Terminator!")) + to_chat(person, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) + arm_equipment(person, /datum/equipment_preset/pmc/w_y_whiteout/terminator, TRUE, TRUE) else - to_chat(H, SPAN_ROLE_HEADER("You are a Whiteout Team Operative!")) - to_chat(H, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) - arm_equipment(H, /datum/equipment_preset/pmc/w_y_whiteout, TRUE, TRUE) + to_chat(person, SPAN_ROLE_HEADER("You are a Whiteout Team Operative!")) + to_chat(person, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) + arm_equipment(person, /datum/equipment_preset/pmc/w_y_whiteout, TRUE, TRUE) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), H, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), person, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) + +/datum/emergency_call/death/low_threat + name = "Weyland Whiteout Operators" + +// DEATH SQUAD-------------------------------------------------------------------------------- +/datum/emergency_call/death/low_threat/create_member(datum/mind/player, turf/override_spawn_loc) + var/turf/spawn_loc = override_spawn_loc ? override_spawn_loc : get_spawn_point() + + if(!istype(spawn_loc)) + return //Didn't find a useable spawn point. + + var/mob/living/carbon/human/person = new(spawn_loc) + player.transfer_to(person, TRUE) + + if(!leader && HAS_FLAG(person.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(person.client, JOB_SQUAD_LEADER, time_required_for_job)) + leader = person + to_chat(person, SPAN_ROLE_HEADER("You are the Whiteout Team Leader!")) + to_chat(person, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) + arm_equipment(person, /datum/equipment_preset/pmc/w_y_whiteout/low_threat/leader, TRUE, TRUE) + else if(medics < max_medics && HAS_FLAG(person.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(person.client, JOB_SQUAD_MEDIC, time_required_for_job)) + medics++ + to_chat(person, SPAN_ROLE_HEADER("You are a Whiteout Team Medic!")) + to_chat(person, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) + arm_equipment(person, /datum/equipment_preset/pmc/w_y_whiteout/low_threat/medic, TRUE, TRUE) + else if(heavies < max_heavies && HAS_FLAG(person.client.prefs.toggles_ert, PLAY_SMARTGUNNER) && check_timelock(person.client, list(JOB_SQUAD_SPECIALIST, JOB_SQUAD_SMARTGUN), time_required_for_job)) + heavies++ + to_chat(person, SPAN_ROLE_HEADER("You are a Whiteout Team Terminator!")) + to_chat(person, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) + arm_equipment(person, /datum/equipment_preset/pmc/w_y_whiteout/low_threat/terminator, TRUE, TRUE) + else + to_chat(person, SPAN_ROLE_HEADER("You are a Whiteout Team Operative!")) + to_chat(person, SPAN_ROLE_BODY("Whiteout protocol is in effect for the target, all assets onboard are to be liquidated with expediency unless otherwise instructed by Weyland Yutani personnel holding the position of Director or above.")) + arm_equipment(person, /datum/equipment_preset/pmc/w_y_whiteout/low_threat, TRUE, TRUE) + + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), person, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) //################################################################################################ // Marine commandos - USCM Deathsquad. Event only /datum/emergency_call/marsoc - name = "Marine Raider Strike Team" + name = "Marine Raider Strike Team (!DEATHSQUAD!)" mob_max = 8 mob_min = 5 probability = 0 @@ -81,7 +116,7 @@ return /datum/emergency_call/marsoc_covert - name = "Marine Raider Operatives (Covert)" + name = "Marine Raider Operatives (!DEATHSQUAD! Covert)" mob_max = 8 mob_min = 5 probability = 0 @@ -107,3 +142,27 @@ to_chat(H, SPAN_BOLDNOTICE("You are absolutely loyal to High Command and must follow their directives.")) to_chat(H, SPAN_BOLDNOTICE("Execute the mission assigned to you with extreme prejudice!")) return + + +/datum/emergency_call/marsoc/low_threat + name = "Marine Raider Operatives" + +/datum/emergency_call/marsoc/low_threat/create_member(datum/mind/MIND) + + var/turf/spawn_loc = get_spawn_point() + + if(!istype(spawn_loc)) + return //Didn't find a useable spawn point. + + var/mob/living/carbon/human/player = new(spawn_loc) + MIND.transfer_to(player, TRUE) + if(!leader && HAS_FLAG(player.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(player.client, JOB_SQUAD_LEADER, time_required_for_job)) //First one spawned is always the leader. + leader = player + to_chat(player, SPAN_WARNING(FONT_SIZE_BIG("You are a Marine Raider Team Leader, better than all the rest."))) + arm_equipment(player, /datum/equipment_preset/uscm/marsoc/low_threat/sl, TRUE, TRUE) + else + to_chat(player, SPAN_WARNING(FONT_SIZE_BIG("You are an elite Marine Raider, the best of the best."))) + arm_equipment(player, /datum/equipment_preset/uscm/marsoc/low_threat, TRUE, TRUE) + to_chat(player, SPAN_BOLDNOTICE("You are absolutely loyal to High Command and must follow their directives.")) + to_chat(player, SPAN_BOLDNOTICE("Execute the mission assigned to you with extreme prejudice!")) + return diff --git a/code/datums/emergency_calls/emergency_call.dm b/code/datums/emergency_calls/emergency_call.dm index 4c3dfbbfac2b..9db46955a5ea 100644 --- a/code/datums/emergency_calls/emergency_call.dm +++ b/code/datums/emergency_calls/emergency_call.dm @@ -305,7 +305,7 @@ candidates = list() if(arrival_message && announce_incoming) - marine_announcement(arrival_message, "Intercepted Tranmission:") + marine_announcement(arrival_message, "Intercepted Transmission:") /datum/emergency_call/proc/add_candidate(mob/M) if(!M.client || (M.mind && (M.mind in candidates)) || istype(M, /mob/living/carbon/xenomorph)) diff --git a/code/datums/emergency_calls/upp_commando.dm b/code/datums/emergency_calls/upp_commando.dm index 14c4af46c27b..1bc2b59ba08c 100644 --- a/code/datums/emergency_calls/upp_commando.dm +++ b/code/datums/emergency_calls/upp_commando.dm @@ -1,7 +1,7 @@ //UPP COMMANDOS /datum/emergency_call/upp_commando - name = "UPP Commandos" + name = "UPP Commandos (!DEATHSQUAD!)" mob_max = 6 probability = 0 objectives = "Stealthily assault the ship. Use your silenced weapons, tranquilizers, and night vision to get the advantage on the enemy. Take out the power systems, comms and engine. Stick together and keep a low profile." @@ -51,3 +51,29 @@ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), H, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) +/datum/emergency_call/upp_commando/low_threat + name = "UPP Commandos" + +/datum/emergency_call/upp_commando/create_member(datum/mind/mind, turf/override_spawn_loc) + var/turf/spawn_loc = override_spawn_loc ? override_spawn_loc : get_spawn_point() + + if(!istype(spawn_loc)) + return //Didn't find a useable spawn point. + + var/mob/living/carbon/human/person = new(spawn_loc) + mind.transfer_to(person, TRUE) + + if(!leader && HAS_FLAG(person.client.prefs.toggles_ert, PLAY_LEADER) && check_timelock(person.client, JOB_SQUAD_LEADER, time_required_for_job)) //First one spawned is always the leader. + leader = person + arm_equipment(person, /datum/equipment_preset/upp/commando/leader/low_threat, TRUE, TRUE) + to_chat(person, SPAN_ROLE_HEADER("You are a Commando Team Leader of the Union of Progressive People, a powerful socialist state that rivals the United Americas!")) + else if(medics < max_medics && HAS_FLAG(person.client.prefs.toggles_ert, PLAY_MEDIC) && check_timelock(person.client, JOB_SQUAD_MEDIC, time_required_for_job)) + medics++ + to_chat(person, SPAN_ROLE_HEADER("You are a Commando Medic of the Union of Progressive People, a powerful socialist state that rivals the United Americas!")) + arm_equipment(person, /datum/equipment_preset/upp/commando/medic/low_threat, TRUE, TRUE) + else + to_chat(person, SPAN_ROLE_HEADER("You are a Commando of the Union of Progressive People, a powerful socialist state that rivals the United Americas!")) + arm_equipment(person, /datum/equipment_preset/upp/commando/low_threat, TRUE, TRUE) + print_backstory(person) + + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), person, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) diff --git a/code/datums/factions/uscm.dm b/code/datums/factions/uscm.dm index cf77142ce5d6..0a9b0cff40b9 100644 --- a/code/datums/factions/uscm.dm +++ b/code/datums/factions/uscm.dm @@ -189,6 +189,19 @@ if(JOB_CMB_OBS) marine_rk = "obs" icon_prefix = "cmb_" + // Check squad marines here too, for the unique ones + if(JOB_SQUAD_ENGI) + marine_rk = "engi" + if(JOB_SQUAD_MEDIC) + marine_rk = "med" + if(JOB_SQUAD_SPECIALIST) + marine_rk = "spec" + if(JOB_SQUAD_SMARTGUN) + marine_rk = "gun" + if(JOB_SQUAD_TEAM_LEADER) + marine_rk = "tl" + if(JOB_SQUAD_LEADER) + marine_rk = "leader" if(marine_rk) var/image/I = image('icons/mob/hud/marine_hud.dmi', current_human, "hudsquad") diff --git a/code/datums/keybinding/communication.dm b/code/datums/keybinding/communication.dm index 4164198d4818..e1ba0ab5a31e 100644 --- a/code/datums/keybinding/communication.dm +++ b/code/datums/keybinding/communication.dm @@ -32,7 +32,7 @@ /datum/keybinding/client/communication/whisper hotkey_keys = list("Unbound") classic_keys = list("Unbound") - name = "Whisper" + name = WHISPER_CHANNEL full_name = "IC Whisper" keybind_signal = COMSIG_KB_CLIENT_WHISPER_DOWN @@ -56,4 +56,4 @@ name = MENTOR_CHANNEL full_name = "Mentor Say" description = "Talk with other mentors." - keybind_signal = COMSIG_KB_ADMIN_ASAY_DOWN + keybind_signal = COMSIG_KB_ADMIN_MENTORSAY_DOWN diff --git a/code/datums/keybinding/human_combat.dm b/code/datums/keybinding/human_combat.dm index 2f37efc61438..d30414d68563 100644 --- a/code/datums/keybinding/human_combat.dm +++ b/code/datums/keybinding/human_combat.dm @@ -190,3 +190,20 @@ var/obj/item/weapon/gun/rifle/m46c/COgun = held_item COgun.toggle_iff(human) return TRUE + +/datum/keybinding/human/combat/toggle_shotgun_tube + hotkey_keys = list("Unbound") + classic_keys = list("Unbound") + name = "toggle_shotgun_tube" + full_name = "Toggle Shotgun Tube" + keybind_signal = COMSIG_KB_HUMAN_WEAPON_SHOTGUN_TUBE + +/datum/keybinding/human/combat/toggle_shotgun_tube/down(client/user) + . = ..() + if(.) + return + var/mob/living/carbon/human/human = user.mob + var/obj/item/weapon/gun/shotgun/pump/dual_tube/held_item = human.get_held_item() + if(istype(held_item)) + held_item.toggle_tube() + return TRUE diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm index c6fc23c68eba..11ba15146430 100644 --- a/code/datums/looping_sounds/_looping_sound.dm +++ b/code/datums/looping_sounds/_looping_sound.dm @@ -37,6 +37,15 @@ /// Has the looping started yet? var/loop_started = FALSE + /** + * Let's you make a "loud" sound that "projects." IE you can hear this sound from a further distance away. + * Think of like an air raid siren. They're loud if you're close yeah... but you can hear them from mad far away, bruv + * with a longer "falloff distance." Fixes the extra_range stuff + */ + var/is_sound_projecting = FALSE + ///only applicable to is_sound_projecting: max range till sound volume starts dropping as distance increases + var/falloff_distance = 50 + /* // as of yet unused varen \\ /// How much the sound will be affected by falloff per tile. @@ -130,19 +139,18 @@ sound_to_play.channel = get_free_channel() sound_to_play.volume = volume_override || volume //Use volume as fallback if theres no override SEND_SOUND(parent, sound_to_play) - else - playsound( - parent, - sound_to_play, - volume, - vary, - extra_range//, - // falloff_exponent = falloff_exponent, - // pressure_affected = pressure_affected, - // ignore_walls = ignore_walls, - // falloff_distance = falloff_distance, - // use_reverb = use_reverb - ) + return + if (is_sound_projecting) + playsound(parent, sound_to_play, volume, vary, extra_range, VOLUME_SFX, 0, 0, falloff_distance) + return + + playsound( + parent, + sound_to_play, + volume, + vary, + extra_range + ) /// Returns the sound we should now be playing. /datum/looping_sound/proc/get_sound(_mid_sounds) diff --git a/code/datums/map_config.dm b/code/datums/map_config.dm index 1f3c265ead76..3bf5c601cec9 100644 --- a/code/datums/map_config.dm +++ b/code/datums/map_config.dm @@ -87,12 +87,14 @@ /datum/equipment_preset/synth/survivor/janitor_synth, /datum/equipment_preset/synth/survivor/chef_synth, /datum/equipment_preset/synth/survivor/teacher_synth, + /datum/equipment_preset/synth/survivor/freelancer_synth, + /datum/equipment_preset/synth/survivor/trucker_synth, /datum/equipment_preset/synth/survivor/bartender_synth, /datum/equipment_preset/synth/survivor/detective_synth, /datum/equipment_preset/synth/survivor/cmb_synth, - /datum/equipment_preset/synth/survivor/security_synth, - /datum/equipment_preset/synth/survivor/protection_synth, - /datum/equipment_preset/synth/survivor/corporate_synth, + /datum/equipment_preset/synth/survivor/wy/security_synth, + /datum/equipment_preset/synth/survivor/wy/protection_synth, + /datum/equipment_preset/synth/survivor/wy/corporate_synth, /datum/equipment_preset/synth/survivor/radiation_synth, ) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 2e56b963e88f..9e8279a843a4 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -29,9 +29,14 @@ research_objective_interface = new() /datum/mind/Destroy() + QDEL_NULL(initial_account) QDEL_NULL(objective_memory) QDEL_NULL(objective_interface) QDEL_NULL(research_objective_interface) + current = null + original = null + ghost_mob = null + player_entity = null return ..() /datum/mind/proc/transfer_to(mob/living/new_character, force = FALSE) diff --git a/code/datums/mob_hud.dm b/code/datums/mob_hud.dm index 65c5a47896fa..ff1263510761 100644 --- a/code/datums/mob_hud.dm +++ b/code/datums/mob_hud.dm @@ -283,6 +283,12 @@ var/list/datum/mob_hud/huds = list( return /mob/living/carbon/xenomorph/med_hud_set_health() + if(QDELETED(src)) + return + + if(!(HEALTH_HUD_XENO in hud_list)) + CRASH("hud_list lacks HEALTH_HUD_XENO despite not being deleted in med_hud_set_health()") + var/image/holder = hud_list[HEALTH_HUD_XENO] var/health_hud_type = "xenohealth" @@ -798,7 +804,7 @@ var/global/image/hud_icon_hudfocus tag_holder.overlays += image('icons/mob/hud/hud.dmi', src, "prae_tag") // Hacky, but works. Currently effects are hard to make with precise timings - var/freeze_found = frozen + var/freeze_found = HAS_TRAIT(src, TRAIT_IMMOBILIZED) && !buckled && !lying if (freeze_found) freeze_holder.overlays += image('icons/mob/hud/hud.dmi', src, "xeno_freeze") diff --git a/code/datums/stamina/_stamina.dm b/code/datums/stamina/_stamina.dm index 36705e3be300..e233aaa81676 100644 --- a/code/datums/stamina/_stamina.dm +++ b/code/datums/stamina/_stamina.dm @@ -37,13 +37,11 @@ current_stamina = Clamp(current_stamina - amount, 0, max_stamina) if(current_stamina < max_stamina) - if(!(src in active_staminas)) - active_staminas.Add(src) - + START_PROCESSING(SSobj, src) if(amount > 0) apply_rest_period(STAMINA_REST_PERIOD) else - active_staminas.Remove(src) + STOP_PROCESSING(SSobj, src) update_stamina_level() diff --git a/code/datums/statistics/entities/caste_stats.dm b/code/datums/statistics/entities/caste_stats.dm index 639e1b4a05f5..6bfc18d124b7 100644 --- a/code/datums/statistics/entities/caste_stats.dm +++ b/code/datums/statistics/entities/caste_stats.dm @@ -3,6 +3,10 @@ var/total_hits = 0 var/list/abilities_used = list() // types of /datum/entity/statistic, "tail sweep" = 10, "screech" = 2 +/datum/entity/player_stats/caste/Destroy(force) + . = ..() + QDEL_LIST_ASSOC_VAL(abilities_used) + /datum/entity/player_stats/caste/proc/setup_ability(ability) if(!ability) return diff --git a/code/datums/statistics/entities/death_stats.dm b/code/datums/statistics/entities/death_stats.dm index cb3053a33442..35ff1769b925 100644 --- a/code/datums/statistics/entities/death_stats.dm +++ b/code/datums/statistics/entities/death_stats.dm @@ -140,16 +140,16 @@ new_death.detach() return new_death -/mob/living/carbon/human/track_mob_death(cause, cause_mob) - . = ..(cause, cause_mob, job) +/mob/living/carbon/human/track_mob_death(datum/cause_data/cause_data, turf/death_loc) + . = ..() if(statistic_exempt || !mind) return var/datum/entity/player_stats/human/human_stats = mind.setup_human_stats() if(human_stats && human_stats.death_list) human_stats.death_list.Insert(1, .) -/mob/living/carbon/xenomorph/track_mob_death(cause, cause_mob) - var/datum/entity/statistic/death/new_death = ..(cause, cause_mob, caste_type) +/mob/living/carbon/xenomorph/track_mob_death(datum/cause_data/cause_data, turf/death_loc) + var/datum/entity/statistic/death/new_death = ..() if(!new_death) return new_death.is_xeno = TRUE // this was placed beneath the if below, which meant gibbing as a xeno wouldn't track properly in stats diff --git a/code/datums/statistics/entities/human_stats.dm b/code/datums/statistics/entities/human_stats.dm index 51b07867dfd3..1e15aa1d161b 100644 --- a/code/datums/statistics/entities/human_stats.dm +++ b/code/datums/statistics/entities/human_stats.dm @@ -5,10 +5,17 @@ var/total_shots = 0 var/total_shots_hit = 0 var/total_screams = 0 - var/datum/entity/weapon_stats/top_weapon = null // reference to /datum/entity/weapon_stats (like tac-shotty) - var/list/weapon_stats_list = list() // list of types /datum/entity/weapon_stats - var/list/job_stats_list = list() // list of types /datum/entity/job_stats - var/list/datum/entity/statistic/medal/medal_list = list() // list of all medals earned + var/list/weapon_stats_list = list() //! indexed list of types /datum/entity/weapon_stats + var/list/job_stats_list = list() //! indexed list of types /datum/entity/job_stats + var/datum/entity/weapon_stats/top_weapon //! reference to /datum/entity/weapon_stats (like tac-shotty) + var/list/datum/entity/statistic/medal/medal_list = list() //! list of all medals earned + +/datum/entity/player_stats/human/Destroy(force) + . = ..() + QDEL_LIST_ASSOC_VAL(weapon_stats_list) + QDEL_LIST_ASSOC_VAL(job_stats_list) + QDEL_NULL(top_weapon) + QDEL_LIST(medal_list) /datum/entity/player_stats/human/get_playtime(type) if(!type) diff --git a/code/datums/statistics/entities/job_stats.dm b/code/datums/statistics/entities/job_stats.dm index ecde1c942082..199c2adb3160 100644 --- a/code/datums/statistics/entities/job_stats.dm +++ b/code/datums/statistics/entities/job_stats.dm @@ -1,8 +1,8 @@ /datum/entity/player_stats/job - var/name = null - var/total_friendly_fire = null - var/total_revives = null - var/total_lives_saved = null - var/total_shots = null - var/total_shots_hit = null - var/total_screams = null + var/name + var/total_friendly_fire + var/total_revives + var/total_lives_saved + var/total_shots + var/total_shots_hit + var/total_screams diff --git a/code/datums/statistics/entities/panel_stats.dm b/code/datums/statistics/entities/panel_stats.dm index d6e391e1731f..e507d5d81a8b 100644 --- a/code/datums/statistics/entities/panel_stats.dm +++ b/code/datums/statistics/entities/panel_stats.dm @@ -8,7 +8,7 @@ update_panel_data(round_statistics) ui_interact(user) -/datum/entity/player_entity/proc/ui_interact(mob/user, ui_key = "statistics", datum/nanoui/ui = null, force_open = 1) +/datum/entity/player_entity/proc/ui_interact(mob/user, ui_key = "statistics", datum/nanoui/ui, force_open = 1) data["menu"] = menu data["subMenu"] = subMenu data["dataMenu"] = dataMenu diff --git a/code/datums/statistics/entities/player_entity.dm b/code/datums/statistics/entities/player_entity.dm index 72f4d95d7aa3..f0b3d37ede7e 100644 --- a/code/datums/statistics/entities/player_entity.dm +++ b/code/datums/statistics/entities/player_entity.dm @@ -8,8 +8,8 @@ /datum/entity/player_entity var/name var/ckey // "cakey" - var/list/datum/entity/player_stats = list() - var/list/datum/entity/statistic/death/death_stats = list() + var/list/player_stats = list() //! Indeed list of /datum/entity/player_stats + var/list/death_stats = list() //! Indexed list of /datum/entity/statistic/death var/menu = 0 var/subMenu = 0 var/dataMenu = 0 @@ -18,6 +18,11 @@ var/savefile_version var/save_loaded = FALSE +/datum/entity/player_entity/Destroy(force) + QDEL_LIST_ASSOC_VAL(player_stats) + QDEL_LIST_ASSOC_VAL(death_stats) + return ..() + /datum/entity/player_entity/proc/get_playtime(branch, type) var/playtime = 0 if(player_stats["[branch]"]) diff --git a/code/datums/statistics/entities/player_stats.dm b/code/datums/statistics/entities/player_stats.dm index b378d7c2ea24..d9fbd3b11e03 100644 --- a/code/datums/statistics/entities/player_stats.dm +++ b/code/datums/statistics/entities/player_stats.dm @@ -6,13 +6,21 @@ var/total_rounds_played = 0 var/steps_walked = 0 var/round_played = FALSE - var/datum/entity/statistic/nemesis = null // "runner" = 3 + var/datum/entity/statistic/nemesis // "runner" = 3 var/list/niche_stats = list() // list of type /datum/entity/statistic, "Total Executions" = number var/list/humans_killed = list() // list of type /datum/entity/statistic, "jobname2" = number var/list/xenos_killed = list() // list of type /datum/entity/statistic, "caste" = number var/list/death_list = list() // list of type /datum/entity/death_stats var/display_stat = TRUE +/datum/entity/player_stats/Destroy(force) + QDEL_NULL(nemesis) + QDEL_LIST_ASSOC_VAL(niche_stats) + QDEL_LIST_ASSOC_VAL(humans_killed) + QDEL_LIST_ASSOC_VAL(xenos_killed) + QDEL_LIST_ASSOC_VAL(death_list) + return ..() + /datum/entity/player_stats/proc/get_playtime() return total_playtime diff --git a/code/datums/statistics/entities/round_stats.dm b/code/datums/statistics/entities/round_stats.dm index 0e1fb6e387db..baed6befa912 100644 --- a/code/datums/statistics/entities/round_stats.dm +++ b/code/datums/statistics/entities/round_stats.dm @@ -23,7 +23,7 @@ var/total_slashes = 0 // untracked data - var/datum/entity/statistic/map/current_map = null // reference to current map + var/datum/entity/statistic/map/current_map // reference to current map var/list/datum/entity/statistic/death/death_stats_list = list() var/list/abilities_used = list() // types of /datum/entity/statistic, "tail sweep" = 10, "screech" = 2 @@ -37,8 +37,20 @@ var/list/job_stats_list = list() // list of types /datum/entity/job_stats // nanoui data - var/round_data[0] - var/death_data[0] + var/list/round_data = list() + var/list/death_data = list() + +/datum/entity/statistic/round/Destroy(force) + . = ..() + QDEL_NULL(current_map) + QDEL_LIST(death_stats_list) + QDEL_LIST_ASSOC_VAL(abilities_used) + QDEL_LIST_ASSOC_VAL(final_participants) + QDEL_LIST_ASSOC_VAL(hijack_participants) + QDEL_LIST_ASSOC_VAL(total_deaths) + QDEL_LIST_ASSOC_VAL(caste_stats_list) + QDEL_LIST_ASSOC_VAL(weapon_stats_list) + QDEL_LIST_ASSOC_VAL(job_stats_list) /datum/entity_meta/statistic_round entity_type = /datum/entity/statistic/round diff --git a/code/datums/statistics/entities/weapon_stats.dm b/code/datums/statistics/entities/weapon_stats.dm index 0d8458c20de2..9fff5c514458 100644 --- a/code/datums/statistics/entities/weapon_stats.dm +++ b/code/datums/statistics/entities/weapon_stats.dm @@ -1,16 +1,23 @@ /datum/entity/weapon_stats - var/datum/entity/player = null // "deanthelis" - var/list/niche_stats = list() // list of type /datum/entity/statistic, "Total Reloads" = number - var/list/humans_killed = list() // list of type /datum/entity/statistic, "jobname2" = number - var/list/xenos_killed = list() // list of type /datum/entity/statistic, "caste" = number - var/name = null + var/datum/entity/player + var/list/niche_stats = list() //! Indexed list of /datum/entity/statistic, "Total Reloads" = number + var/list/humans_killed = list() //! Indexed list of /datum/entity/statistic, "jobname2" = number + var/list/xenos_killed = list() //! Indexed list of /datum/entity/statistic, "caste" = number + var/name var/total_kills = 0 - var/total_hits = null - var/total_shots = null - var/total_shots_hit = null - var/total_friendly_fire = null + var/total_hits + var/total_shots + var/total_shots_hit + var/total_friendly_fire var/display_stat = TRUE +/datum/entity/weapon_stats/Destroy(force) + player = null + QDEL_LIST_ASSOC_VAL(niche_stats) + QDEL_LIST_ASSOC_VAL(humans_killed) + QDEL_LIST_ASSOC_VAL(xenos_killed) + return ..() + /datum/entity/weapon_stats/proc/count_human_kill(job_name) if(!job_name) return diff --git a/code/datums/statistics/entities/xeno_stats.dm b/code/datums/statistics/entities/xeno_stats.dm index 9ed327258258..8fff4a2e5dd3 100644 --- a/code/datums/statistics/entities/xeno_stats.dm +++ b/code/datums/statistics/entities/xeno_stats.dm @@ -4,6 +4,12 @@ var/list/caste_stats_list = list() // list of types /datum/entity/player_stats/caste var/list/datum/entity/statistic/medal/medal_list = list() // list of all royal jelly earned +/datum/entity/player_stats/xeno/Destroy(force) + . = ..() + QDEL_NULL(top_caste) + QDEL_LIST_ASSOC_VAL(caste_stats_list) + QDEL_LIST(medal_list) + /datum/entity/player_stats/xeno/get_playtime(type) if(!type || type == FACTION_XENOMORPH) return ..() diff --git a/code/game/area/almayer.dm b/code/game/area/almayer.dm index 6ced81a22b15..742ae7a1addb 100644 --- a/code/game/area/almayer.dm +++ b/code/game/area/almayer.dm @@ -13,6 +13,21 @@ ambience_exterior = AMBIENCE_ALMAYER ceiling_muffle = FALSE + ///Whether this area is used for hijack evacuation progress + var/hijack_evacuation_area = FALSE + + ///The weight this area gives towards hijack evacuation progress + var/hijack_evacuation_weight = 0 + + ///Whether this area is additive or multiplicative towards evacuation progress + var/hijack_evacuation_type = EVACUATION_TYPE_NONE + +/area/almayer/Initialize(mapload, ...) + . = ..() + + if(hijack_evacuation_area) + SShijack.progress_areas[src] = power_equip + /area/shuttle/almayer/elevator_maintenance/upperdeck name = "\improper Maintenance Elevator" icon_state = "shuttle" @@ -160,6 +175,9 @@ fake_zlevel = 2 // lowerdeck soundscape_playlist = SCAPE_PL_ENG soundscape_interval = 15 + hijack_evacuation_area = TRUE + hijack_evacuation_weight = 0.2 + hijack_evacuation_type = EVACUATION_TYPE_ADDITIVE /area/almayer/engineering/starboard_atmos name = "\improper Atmospherics Starboard" @@ -183,6 +201,9 @@ name = "\improper Astronavigational Deck" icon_state = "astronavigation" fake_zlevel = 2 // lowerdeck + hijack_evacuation_area = TRUE + hijack_evacuation_weight = 1.1 + hijack_evacuation_type = EVACUATION_TYPE_MULTIPLICATIVE /area/almayer/shipboard/panic name = "\improper Hangar Panic Room" @@ -712,6 +733,9 @@ icon_state = "lifeboat_pump" requires_power = 1 fake_zlevel = 1 + hijack_evacuation_area = TRUE + hijack_evacuation_weight = 0.1 + hijack_evacuation_type = EVACUATION_TYPE_ADDITIVE /area/almayer/lifeboat_pumps/north1 name = "North West Lifeboat Fuel Pump" diff --git a/code/game/atoms.dm b/code/game/atoms.dm index e1541f8368b8..e0590265840c 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -33,8 +33,11 @@ var/list/filter_data //For handling persistent filters - // Base transform matrix - var/matrix/base_transform = null + /// Base transform matrix, edited by admin tooling and such + var/matrix/base_transform + /// Last transform used before being compound with base_transform + /// This allows us to re-create transform if only base_transform changes + var/matrix/raw_transform ///Chemistry. var/datum/reagents/reagents = null @@ -116,7 +119,14 @@ directive is properly returned. //=========================================================================== - +// TODO make all atoms use set_density, do not rely on it at present +///Setter for the `density` variable to append behavior related to its changing. +/atom/proc/set_density(new_value) + SHOULD_CALL_PARENT(TRUE) + if(density == new_value) + return + . = density + density = new_value //atmos procs @@ -141,15 +151,27 @@ directive is properly returned. if(loc) return loc.return_gas() -// Updates the atom's transform -/atom/proc/apply_transform(matrix/M) - if(!base_transform) - transform = M - return +/// Updates the atom's transform compounding it with [/atom/var/base_transform] +/atom/proc/apply_transform(matrix/new_transform, time = 0, easing = (EASE_IN|EASE_OUT)) + var/matrix/base_copy + if(base_transform) + base_copy = matrix(base_transform) + else + base_copy = matrix() + raw_transform = matrix(new_transform) // Keep a copy to replay if needed - var/matrix/base_copy = matrix(base_transform) // Compose the base and applied transform in that order - transform = base_copy.Multiply(M) + var/matrix/complete = base_copy.Multiply(raw_transform) + + if(!time) + transform = complete + return + animate(src, transform = complete, time = time, easing = easing) + +/// Upates the base_transform which will be compounded with other transforms +/atom/proc/update_base_transform(matrix/new_transform, time = 0) + base_transform = matrix(new_transform) + apply_transform(raw_transform, time) /atom/proc/on_reagent_change() return @@ -183,7 +205,9 @@ directive is properly returned. return /atom/proc/emp_act(severity) - return + SHOULD_CALL_PARENT(TRUE) + + SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity) /atom/proc/in_contents_of(container)//can take class or object instance as argument if(ispath(container)) @@ -223,8 +247,8 @@ directive is properly returned. if(!examine_strings) log_debug("Attempted to create an examine block with no strings! Atom : [src], user : [user]") return - to_chat(user, examine_block(examine_strings.Join("\n"))) SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, examine_strings) + to_chat(user, examine_block(examine_strings.Join("\n"))) /atom/proc/get_examine_text(mob/user) . = list() @@ -699,10 +723,9 @@ Parameters are passed from New. usr.client.cmd_admin_emp(src) if(href_list[VV_HK_MODIFY_TRANSFORM] && check_rights(R_VAREDIT)) - var/result = tgui_input_list(usr, "Choose the transformation to apply","Transform Mod", list("Scale","Translate","Rotate")) + var/result = tgui_input_list(usr, "Choose the transformation to apply","Transform Mod", list("Scale","Translate","Rotate", "Reflect X Axis", "Reflect Y Axis")) if(!result) return - var/matrix/M = transform if(!result) return switch(result) @@ -711,19 +734,37 @@ Parameters are passed from New. var/y = tgui_input_real_number(usr, "Choose y mod","Transform Mod") if(isnull(x) || isnull(y)) return - transform = M.Scale(x,y) + var/matrix/base_matrix = matrix(base_transform) + update_base_transform(base_matrix.Scale(x,y)) if("Translate") var/x = tgui_input_real_number(usr, "Choose x mod (negative = left, positive = right)","Transform Mod") var/y = tgui_input_real_number(usr, "Choose y mod (negative = down, positive = up)","Transform Mod") if(isnull(x) || isnull(y)) return - transform = M.Translate(x,y) + var/matrix/base_matrix = matrix(base_transform) + update_base_transform(base_matrix.Translate(x,y)) if("Rotate") var/angle = tgui_input_real_number(usr, "Choose angle to rotate","Transform Mod") if(isnull(angle)) return - transform = M.Turn(angle) - + var/matrix/base_matrix = matrix(base_transform) + update_base_transform(base_matrix.Turn(angle)) + if("Reflect X Axis") + var/matrix/current = matrix(base_transform) + var/matrix/reflector = matrix() + reflector.a = -1 + reflector.d = 0 + reflector.b = 0 + reflector.e = 1 + update_base_transform(current * reflector) + if("Reflect Y Axis") + var/matrix/current = matrix(base_transform) + var/matrix/reflector = matrix() + reflector.a = 1 + reflector.d = 0 + reflector.b = 0 + reflector.e = -1 + update_base_transform(current * reflector) SEND_SIGNAL(src, COMSIG_ATOM_VV_MODIFY_TRANSFORM) if(href_list[VV_HK_AUTO_RENAME] && check_rights(R_VAREDIT)) diff --git a/code/game/gamemodes/cm_initialize.dm b/code/game/gamemodes/cm_initialize.dm index effd3325f887..17a255009089 100644 --- a/code/game/gamemodes/cm_initialize.dm +++ b/code/game/gamemodes/cm_initialize.dm @@ -423,7 +423,7 @@ Additional game mode variables. for(var/mob_name in picked_hive.banished_ckeys) if(picked_hive.banished_ckeys[mob_name] == xeno_candidate.ckey) to_chat(xeno_candidate, SPAN_WARNING("You are banished from the [picked_hive], you may not rejoin unless the Queen re-admits you or dies.")) - return + return FALSE if(isnewplayer(xeno_candidate)) var/mob/new_player/noob = xeno_candidate noob.close_spawn_windows() @@ -443,9 +443,6 @@ Additional game mode variables. return FALSE new_xeno = userInput - if(!xeno_candidate) - return FALSE - if(!(new_xeno in GLOB.living_xeno_list) || new_xeno.stat == DEAD) to_chat(xeno_candidate, SPAN_WARNING("You cannot join if the xenomorph is dead.")) return FALSE @@ -479,14 +476,14 @@ Additional game mode variables. else new_xeno = pick(available_xenos_non_ssd) //Just picks something at random. if(istype(new_xeno) && xeno_candidate && xeno_candidate.client) if(isnewplayer(xeno_candidate)) - var/mob/new_player/N = xeno_candidate - N.close_spawn_windows() + var/mob/new_player/noob = xeno_candidate + noob.close_spawn_windows() for(var/mob_name in new_xeno.hive.banished_ckeys) if(new_xeno.hive.banished_ckeys[mob_name] == xeno_candidate.ckey) to_chat(xeno_candidate, SPAN_WARNING("You are banished from this hive, You may not rejoin unless the Queen re-admits you or dies.")) - return + return FALSE if(transfer_xeno(xeno_candidate, new_xeno)) - return 1 + return TRUE to_chat(xeno_candidate, "JAS01: Something went wrong, tell a coder.") /datum/game_mode/proc/attempt_to_join_as_facehugger(mob/xeno_candidate) @@ -614,20 +611,21 @@ Additional game mode variables. /datum/game_mode/proc/transfer_xeno(xeno_candidate, mob/living/new_xeno) if(!xeno_candidate || !isxeno(new_xeno) || QDELETED(new_xeno)) return FALSE + var/datum/mind/xeno_candidate_mind if(ismind(xeno_candidate)) xeno_candidate_mind = xeno_candidate else if(ismob(xeno_candidate)) - var/mob/M = xeno_candidate - if(M.mind) - xeno_candidate_mind = M.mind + var/mob/xeno_candidate_mob = xeno_candidate + if(xeno_candidate_mob.mind) + xeno_candidate_mind = xeno_candidate_mob.mind else - xeno_candidate_mind = new /datum/mind(M.key, M.ckey) + xeno_candidate_mind = new /datum/mind(xeno_candidate_mob.key, xeno_candidate_mob.ckey) xeno_candidate_mind.active = TRUE xeno_candidate_mind.current = new_xeno else if(isclient(xeno_candidate)) - var/client/C = xeno_candidate - xeno_candidate_mind = new /datum/mind(C.key, C.ckey) + var/client/xeno_candidate_client = xeno_candidate + xeno_candidate_mind = new /datum/mind(xeno_candidate_client.key, xeno_candidate_client.ckey) xeno_candidate_mind.active = TRUE xeno_candidate_mind.current = new_xeno else diff --git a/code/game/gamemodes/cm_self_destruct.dm b/code/game/gamemodes/cm_self_destruct.dm deleted file mode 100644 index 07c9c43a4768..000000000000 --- a/code/game/gamemodes/cm_self_destruct.dm +++ /dev/null @@ -1,478 +0,0 @@ -/* -TODO -Look into animation screen not showing on self-destruct and other weirdness -Intergrate distress into this controller. -Finish nanoui conversion for comm console. -Make sure people who get nuked and wake up from SSD don't live. -Add flashing lights to evac. //DEFERRED TO BETTER LIGHTING -Finish the game mode announcement thing. -Fix escape doors to work properly. -*/ - -/* -How this works: - -First: All of the linking is done automatically on world start, so nothing needs to be done on that end other than making -sure that objects are actually placed in the game world. If not, the game will error and let you know about it. But you -don't need to modify variables or worry about area placement. It's all done for you. -The rods, for example, configure the time per activation based on their number. Shuttles link their own machines via area. -Nothing in this controller is linked to game mode, so it's stand alone, more or less, but it's best used during a game mode. -Admins have a lot of tools in their disposal via the check antagonist panel, and devs can access the VV of this controller -through that panel. - -Second: The communication console handles most of the IC triggers for activating these functions, the rest is handled elsewhere. -Check communications.dm for that. shuttle_controller.dm handles the set up for the escape pods. escape_pods.dm handles most of the -functions of the escape pods themselves. This file would likely need to be broken down into individual parts at some point in the -future. - -Evacuation takes place when sufficient alert level is reaised and a distress beacon was launched. All of the evac pods come online -and open their doors to allow entry inside. Characters may then get inside of the cryo units to before the shuttles automatically launch. -If wanted, a nearby controller object may launch each individual shuttle early. Only three people may ride on a shuttle to escape, -otherwise the launch will fail and the shuttle will become inoperable. -Any launched shuttles are taken out of the game. If the evacuation is canceled, any persons inside of the cryo tubes will be ejected. -They may temporarily open the door to exit if they are stuck inside after evac is canceled. - -When the self-destruct is enabled, the console comes online. This usually happens during an evacuation. Once the console is -interacted with, it fires up the self-destruct sequence. Several rods rise and must be interacted with in order to arm the system. -Once that happens, the console must be interacted with again to trigger the self-destruct. The self-destruct may also be -canceled from the console. - -The self-destruct may also happen if a nuke is detonated on the ship's zlevel; if it is detonated elsewhere, the ship will not blow up. -Regardless of where it's detonated, or how, a successful detonation will end the round or automatically restart the game. - -All of the necessary difines are stored under mode.dm in defines. -*/ - -var/global/datum/authority/branch/evacuation/EvacuationAuthority //This is initited elsewhere so that the world has a chance to load in. - -/datum/authority/branch/evacuation - var/name = "Evacuation Authority" - var/evac_time //Time the evacuation was initiated. - var/evac_status = EVACUATION_STATUS_STANDING_BY //What it's doing now? It can be standing by, getting ready to launch, or finished. - - var/obj/structure/machinery/self_destruct/console/dest_master //The main console that does the brunt of the work. - var/dest_rods[] //Slave devices to make the explosion work. - var/dest_cooldown //How long it takes between rods, determined by the amount of total rods present. - var/dest_index = 1 //What rod the thing is currently on. - var/dest_status = NUKE_EXPLOSION_INACTIVE - var/dest_started_at = 0 - - var/flags_scuttle = NO_FLAGS - -/datum/authority/branch/evacuation/New() - ..() - dest_master = locate() - if(!dest_master) - log_debug("ERROR CODE SD1: could not find master self-destruct console") - to_world(SPAN_DEBUG("ERROR CODE SD1: could not find master self-destruct console")) - return FALSE - dest_rods = new - for(var/obj/structure/machinery/self_destruct/rod/I in dest_master.loc.loc) dest_rods += I - if(!dest_rods.len) - log_debug("ERROR CODE SD2: could not find any self-destruct rods") - to_world(SPAN_DEBUG("ERROR CODE SD2: could not find any self-destruct rods")) - QDEL_NULL(dest_master) - return FALSE - dest_cooldown = SELF_DESTRUCT_ROD_STARTUP_TIME / dest_rods.len - dest_master.desc = "The main operating panel for a self-destruct system. It requires very little user input, but the final safety mechanism is manually unlocked.\nAfter the initial start-up sequence, [dest_rods.len] control rods must be armed, followed by manually flipping the detonation switch." - -/** - * This proc returns the ship's z level list (or whatever specified), - * when an evac/self-destruct happens. - */ -/datum/authority/branch/evacuation/proc/get_affected_zlevels() - //Nuke is not in progress, end the round on ship only. - if(dest_status < NUKE_EXPLOSION_IN_PROGRESS && SSticker?.mode.is_in_endgame) - . = SSmapping.levels_by_any_trait(list(ZTRAIT_MARINE_MAIN_SHIP)) - return - -//========================================================================================= -//========================================================================================= -//=====================================EVACUATION========================================== -//========================================================================================= -//========================================================================================= - - -/datum/authority/branch/evacuation/proc/initiate_evacuation(force=0) //Begins the evacuation procedure. - if(force || (evac_status == EVACUATION_STATUS_STANDING_BY && !(flags_scuttle & FLAGS_EVACUATION_DENY))) - evac_time = world.time - evac_status = EVACUATION_STATUS_INITIATING - ai_announcement("Attention. Emergency. All personnel must evacuate immediately. You have [round(EVACUATION_ESTIMATE_DEPARTURE/60,1)] minute\s until departure.", 'sound/AI/evacuate.ogg') - xeno_message_all("A wave of adrenaline ripples through the hive. The fleshy creatures are trying to escape!") - - for(var/obj/structure/machinery/status_display/SD in machines) - if(is_mainship_level(SD.z)) - SD.set_picture("evac") - for(var/obj/docking_port/mobile/crashable/escape_shuttle/shuttle in SSshuttle.mobile) - shuttle.prepare_evac() - activate_lifeboats() - process_evacuation() - return TRUE - -/datum/authority/branch/evacuation/proc/cancel_evacuation() //Cancels the evac procedure. Useful if admins do not want the marines leaving. - if(evac_status == EVACUATION_STATUS_INITIATING) - evac_time = null - evac_status = EVACUATION_STATUS_STANDING_BY - deactivate_lifeboats() - ai_announcement("Evacuation has been cancelled.", 'sound/AI/evacuate_cancelled.ogg') - - if(get_security_level() == "red") - for(var/obj/structure/machinery/status_display/SD in machines) - if(is_mainship_level(SD.z)) - SD.set_picture("redalert") - - for(var/obj/docking_port/mobile/crashable/escape_shuttle/shuttle in SSshuttle.mobile) - shuttle.cancel_evac() - return TRUE - -/datum/authority/branch/evacuation/proc/begin_launch() //Launches the pods. - if(evac_status == EVACUATION_STATUS_INITIATING) - evac_status = EVACUATION_STATUS_IN_PROGRESS //Cannot cancel at this point. All shuttles are off. - spawn() //One of the few times spawn() is appropriate. No need for a new proc. - ai_announcement("WARNING: Evacuation order confirmed. Launching escape pods.", 'sound/AI/evacuation_confirmed.ogg') - addtimer(CALLBACK(src, PROC_REF(launch_lifeboats)), 10 SECONDS) // giving some time to board lifeboats - - for(var/obj/docking_port/mobile/crashable/escape_shuttle/shuttle in SSshuttle.mobile) - shuttle.evac_launch() - sleep(50) - - sleep(300) //Sleep 30 more seconds to make sure everyone had a chance to leave. - var/lifesigns = 0 - // lifesigns += P.passengers - var/obj/docking_port/mobile/crashable/lifeboat/lifeboat1 = SSshuttle.getShuttle(MOBILE_SHUTTLE_LIFEBOAT_PORT) - lifeboat1.check_for_survivors() - lifesigns += lifeboat1.survivors - var/obj/docking_port/mobile/crashable/lifeboat/lifeboat2 = SSshuttle.getShuttle(MOBILE_SHUTTLE_LIFEBOAT_STARBOARD) - lifeboat2.check_for_survivors() - lifesigns += lifeboat2.survivors - ai_announcement("ATTENTION: Evacuation complete. Outbound lifesigns detected: [lifesigns ? lifesigns : "none"].", 'sound/AI/evacuation_complete.ogg') - evac_status = EVACUATION_STATUS_COMPLETE - return TRUE - -/datum/authority/branch/evacuation/proc/process_evacuation() //Process the timer. - set background = 1 - - spawn while(evac_status == EVACUATION_STATUS_INITIATING) //If it's not departing, no need to process. - if(world.time >= evac_time + EVACUATION_AUTOMATIC_DEPARTURE) begin_launch() - sleep(10) //One second. - -/datum/authority/branch/evacuation/proc/get_status_panel_eta() - switch(evac_status) - if(EVACUATION_STATUS_INITIATING) - var/eta = EVACUATION_ESTIMATE_DEPARTURE - . = "[(eta / 60) % 60]:[add_zero(num2text(eta % 60), 2)]" - if(EVACUATION_STATUS_IN_PROGRESS) . = "NOW" - -// LIFEBOATS CORNER -/datum/authority/branch/evacuation/proc/activate_lifeboats() - for(var/obj/docking_port/stationary/lifeboat_dock/lifeboat_dock in GLOB.lifeboat_almayer_docks) - var/obj/docking_port/mobile/crashable/lifeboat/lifeboat = lifeboat_dock.get_docked() - if(lifeboat && lifeboat.available) - lifeboat.status = LIFEBOAT_ACTIVE - lifeboat_dock.open_dock() - - -/datum/authority/branch/evacuation/proc/deactivate_lifeboats() - for(var/obj/docking_port/stationary/lifeboat_dock/lifeboat_dock in GLOB.lifeboat_almayer_docks) - var/obj/docking_port/mobile/crashable/lifeboat/lifeboat = lifeboat_dock.get_docked() - if(lifeboat && lifeboat.available) - lifeboat.status = LIFEBOAT_INACTIVE - -/datum/authority/branch/evacuation/proc/launch_lifeboats() - for(var/obj/docking_port/stationary/lifeboat_dock/lifeboat_dock in GLOB.lifeboat_almayer_docks) - var/obj/docking_port/mobile/crashable/lifeboat/lifeboat = lifeboat_dock.get_docked() - if(lifeboat && lifeboat.available) - lifeboat.evac_launch() - -//========================================================================================= -//========================================================================================= -//=====================================SELF DETRUCT======================================== -//========================================================================================= -//========================================================================================= - -/datum/authority/branch/evacuation/proc/enable_self_destruct(force=0) - if(force || (dest_status == NUKE_EXPLOSION_INACTIVE && !(flags_scuttle & FLAGS_SELF_DESTRUCT_DENY))) - dest_status = NUKE_EXPLOSION_ACTIVE - dest_master.lock_or_unlock() - dest_started_at = world.time - set_security_level(SEC_LEVEL_DELTA) //also activate Delta alert, to open the SD shutters. - spawn(0) - for(var/obj/structure/machinery/door/poddoor/shutters/almayer/D in machines) - if(D.id == "sd_lockdown") - D.open() - return TRUE - -//Override is for admins bypassing normal player restrictions. -/datum/authority/branch/evacuation/proc/cancel_self_destruct(override) - if(dest_status == NUKE_EXPLOSION_ACTIVE) - var/obj/structure/machinery/self_destruct/rod/I - var/i - for(i in EvacuationAuthority.dest_rods) - I = i - if(I.active_state == SELF_DESTRUCT_MACHINE_ARMED && !override) - dest_master.state(SPAN_WARNING("WARNING: Unable to cancel detonation. Please disarm all control rods.")) - return FALSE - - dest_status = NUKE_EXPLOSION_INACTIVE - dest_master.in_progress = 1 - dest_started_at = 0 - for(i in dest_rods) - I = i - if(I.active_state == SELF_DESTRUCT_MACHINE_ACTIVE || (I.active_state == SELF_DESTRUCT_MACHINE_ARMED && override)) I.lock_or_unlock(1) - dest_master.lock_or_unlock(1) - dest_index = 1 - ai_announcement("The emergency destruct system has been deactivated.", 'sound/AI/selfdestruct_deactivated.ogg') - if(evac_status == EVACUATION_STATUS_STANDING_BY) //the evac has also been cancelled or was never started. - set_security_level(SEC_LEVEL_RED, TRUE) //both SD and evac are inactive, lowering the security level. - return TRUE - -/datum/authority/branch/evacuation/proc/initiate_self_destruct(override) - if(dest_status < NUKE_EXPLOSION_IN_PROGRESS) - var/obj/structure/machinery/self_destruct/rod/I - var/i - for(i in dest_rods) - I = i - if(I.active_state != SELF_DESTRUCT_MACHINE_ARMED && !override) - dest_master.state(SPAN_WARNING("WARNING: Unable to trigger detonation. Please arm all control rods.")) - return FALSE - dest_master.in_progress = !dest_master.in_progress - for(i in EvacuationAuthority.dest_rods) - I = i - I.in_progress = 1 - ai_announcement("DANGER. DANGER. Self-destruct system activated. DANGER. DANGER. Self-destruct in progress. DANGER. DANGER.") - trigger_self_destruct(,,override) - return TRUE - -/datum/authority/branch/evacuation/proc/trigger_self_destruct(list/z_levels = SSmapping.levels_by_trait(ZTRAIT_MARINE_MAIN_SHIP), origin = dest_master, override = FALSE, end_type = NUKE_EXPLOSION_FINISHED, play_anim = TRUE, end_round = TRUE) - set waitfor = 0 - if(dest_status < NUKE_EXPLOSION_IN_PROGRESS) //One more check for good measure, in case it's triggered through a bomb instead of the destruct mechanism/admin panel. - dest_status = NUKE_EXPLOSION_IN_PROGRESS - playsound(origin, 'sound/machines/Alarm.ogg', 75, 0, 30) - world << pick('sound/theme/nuclear_detonation1.ogg','sound/theme/nuclear_detonation2.ogg') - - var/ship_status = 1 - for(var/i in z_levels) - if(is_mainship_level(i)) - ship_status = 0 //Destroyed. - break - - var/list/alive_mobs = list() //Everyone who will be destroyed on the zlevel(s). - var/list/dead_mobs = list() //Everyone who only needs to see the cinematic. - for(var/mob/current_mob as anything in GLOB.mob_list) //This only does something cool for the people about to die, but should prove pretty interesting. - var/turf/current_turf = get_turf(current_mob) - if(!current_mob || !current_mob.loc || !current_turf) - continue //In case something changes when we sleep(). - if(current_mob.stat == DEAD) - dead_mobs |= current_mob - continue - if(current_turf.z in z_levels) - alive_mobs |= current_mob - shake_camera(current_mob, 110, 4) - - - sleep(100) - /*Hardcoded for now, since this was never really used for anything else. - Would ideally use a better system for showing cutscenes.*/ - var/atom/movable/screen/cinematic/explosion/C = new - - if(play_anim) - for(var/mob/current_mob as anything in alive_mobs + dead_mobs) - if(current_mob && current_mob.loc && current_mob.client) - current_mob.client.add_to_screen(C) //They may have disconnected in the mean time. - - sleep(15) //Extra 1.5 seconds to look at the ship. - flick(override ? "intro_override" : "intro_nuke", C) - sleep(35) - for(var/mob/current_mob in alive_mobs) - if(current_mob && current_mob.loc) //Who knows, maybe they escaped, or don't exist anymore. - var/turf/current_mob_turf = get_turf(current_mob) - if(!current_mob_turf) - continue - if(current_mob_turf.z in z_levels) - if(istype(current_mob.loc, /obj/structure/closet/secure_closet/freezer/fridge)) - continue - current_mob.death(create_cause_data("nuclear explosion")) - else - if(play_anim) - current_mob.client.remove_from_screen(C) //those who managed to escape the z level at last second shouldn't have their view obstructed. - if(play_anim) - flick(ship_status ? "ship_spared" : "ship_destroyed", C) - C.icon_state = ship_status ? "summary_spared" : "summary_destroyed" - world << sound('sound/effects/explosionfar.ogg') - - if(end_round) - dest_status = end_type - - sleep(5) - if(SSticker.mode) - SSticker.mode.check_win() - - if(!SSticker.mode) //Just a safety, just in case a mode isn't running, somehow. - to_world(SPAN_ROUNDBODY("Resetting in 30 seconds!")) - sleep(300) - log_game("Rebooting due to nuclear detonation.") - world.Reboot() - return TRUE - -/datum/authority/branch/evacuation/proc/process_self_destruct() - set background = 1 - - spawn while(dest_master && dest_master.loc && dest_master.active_state == SELF_DESTRUCT_MACHINE_ARMED && dest_status == NUKE_EXPLOSION_ACTIVE && dest_index <= dest_rods.len) - var/obj/structure/machinery/self_destruct/rod/I = dest_rods[dest_index] - if(world.time >= dest_cooldown + I.activate_time) - I.lock_or_unlock() //Unlock it. - if(++dest_index <= dest_rods.len) - I = dest_rods[dest_index]//Start the next sequence. - I.activate_time = world.time - sleep(10) //Checks every second. Could integrate into another controller for better tracking. - -//Generic parent base for the self_destruct items. -/obj/structure/machinery/self_destruct - icon = 'icons/obj/structures/machinery/self_destruct.dmi' - icon_state = "console_1" - var/base_icon_state = "console" - use_power = USE_POWER_NONE //Runs unpowered, may need to change later. - density = FALSE - anchored = TRUE //So it doesn't go anywhere. - unslashable = TRUE - unacidable = TRUE //Cannot C4 it either. - mouse_opacity = FALSE //No need to click or interact with this initially. - var/in_progress = 0 //Cannot interact with while it's doing something, like an animation. - var/active_state = SELF_DESTRUCT_MACHINE_INACTIVE //What step of the process it's on. - -/obj/structure/machinery/self_destruct/Initialize(mapload, ...) - . = ..() - icon_state = "[base_icon_state]_1" - -/obj/structure/machinery/self_destruct/Destroy() - . = ..() - machines -= src - operator = null - -/obj/structure/machinery/self_destruct/ex_act(severity) - return FALSE - -/obj/structure/machinery/self_destruct/attack_hand() - if(..() || in_progress) - return FALSE //This check is backward, ugh. - return TRUE - -//Add sounds. -/obj/structure/machinery/self_destruct/proc/lock_or_unlock(lock) - set waitfor = 0 - in_progress = 1 - flick("[base_icon_state]" + (lock? "_5" : "_2"),src) - sleep(9) - mouse_opacity = !mouse_opacity - icon_state = "[base_icon_state]" + (lock? "_1" : "_3") - in_progress = 0 - active_state = active_state > SELF_DESTRUCT_MACHINE_INACTIVE ? SELF_DESTRUCT_MACHINE_INACTIVE : SELF_DESTRUCT_MACHINE_ACTIVE - -/obj/structure/machinery/self_destruct/console - name = "self-destruct control panel" - icon_state = "console_1" - base_icon_state = "console" - req_one_access = list(ACCESS_MARINE_CO, ACCESS_MARINE_SENIOR) - -/obj/structure/machinery/self_destruct/console/Destroy() - . = ..() - EvacuationAuthority.dest_master = null - EvacuationAuthority.dest_rods = null - -/obj/structure/machinery/self_destruct/console/lock_or_unlock(lock) - playsound(src, 'sound/machines/hydraulics_1.ogg', 25, 1) - ..() - -//TODO: Add sounds. -/obj/structure/machinery/self_destruct/console/attack_hand(mob/user) - if(inoperable()) - return - - tgui_interact(user) - -/obj/structure/machinery/self_destruct/console/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "SelfDestructConsole", name) - ui.open() - -/obj/structure/machinery/sleep_console/ui_status(mob/user, datum/ui_state/state) - . = ..() - if(inoperable()) - return UI_CLOSE - - -/obj/structure/machinery/self_destruct/console/ui_data(mob/user) - var/list/data = list() - - data["dest_status"] = active_state - - return data - -/obj/structure/machinery/self_destruct/console/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) - . = ..() - if(.) - return - - switch(action) - if("dest_start") - to_chat(usr, SPAN_NOTICE("You press a few keys on the panel.")) - to_chat(usr, SPAN_NOTICE("The system must be booting up the self-destruct sequence now.")) - playsound(src.loc, 'sound/items/rped.ogg', 25, TRUE) - sleep(2 SECONDS) - ai_announcement("Danger. The emergency destruct system is now activated. The ship will detonate in T-minus 20 minutes. Automatic detonation is unavailable. Manual detonation is required.", 'sound/AI/selfdestruct.ogg') - active_state = SELF_DESTRUCT_MACHINE_ARMED //Arm it here so the process can execute it later. - var/obj/structure/machinery/self_destruct/rod/I = EvacuationAuthority.dest_rods[EvacuationAuthority.dest_index] - I.activate_time = world.time - EvacuationAuthority.process_self_destruct() - . = TRUE - - if("dest_trigger") - EvacuationAuthority.initiate_self_destruct() - . = TRUE - - if("dest_cancel") - if(!allowed(usr)) - to_chat(usr, SPAN_WARNING("You don't have the necessary clearance to cancel the emergency destruct system!")) - return - EvacuationAuthority.cancel_self_destruct() - . = TRUE - -/obj/structure/machinery/self_destruct/rod - name = "self-destruct control rod" - desc = "It is part of a complicated self-destruct sequence, but relatively simple to operate. Twist to arm or disarm." - icon_state = "rod_1" - base_icon_state = "rod" - layer = BELOW_OBJ_LAYER - var/activate_time - -/obj/structure/machinery/self_destruct/rod/Destroy() - . = ..() - if(EvacuationAuthority && EvacuationAuthority.dest_rods) - EvacuationAuthority.dest_rods -= src - -/obj/structure/machinery/self_destruct/rod/lock_or_unlock(lock) - playsound(src, 'sound/machines/hydraulics_2.ogg', 25, 1) - ..() - if(lock) - activate_time = null - density = FALSE - layer = initial(layer) - else - density = TRUE - layer = ABOVE_OBJ_LAYER - -/obj/structure/machinery/self_destruct/rod/attack_hand(mob/user) - if(..()) - switch(active_state) - if(SELF_DESTRUCT_MACHINE_ACTIVE) - to_chat(user, SPAN_NOTICE("You twist and release the control rod, arming it.")) - playsound(src, 'sound/machines/switch.ogg', 25, 1) - icon_state = "rod_4" - active_state = SELF_DESTRUCT_MACHINE_ARMED - if(SELF_DESTRUCT_MACHINE_ARMED) - to_chat(user, SPAN_NOTICE("You twist and release the control rod, disarming it.")) - playsound(src, 'sound/machines/switch.ogg', 25, 1) - icon_state = "rod_3" - active_state = SELF_DESTRUCT_MACHINE_ACTIVE - else to_chat(user, SPAN_WARNING("The control rod is not ready.")) diff --git a/code/game/gamemodes/colonialmarines/colonialmarines.dm b/code/game/gamemodes/colonialmarines/colonialmarines.dm index df04873ac140..7b1c695ade2b 100644 --- a/code/game/gamemodes/colonialmarines/colonialmarines.dm +++ b/code/game/gamemodes/colonialmarines/colonialmarines.dm @@ -297,29 +297,25 @@ if(SSticker.current_state != GAME_STATE_PLAYING) return - var/living_player_list[] = count_humans_and_xenos(EvacuationAuthority.get_affected_zlevels()) + var/living_player_list[] = count_humans_and_xenos(get_affected_zlevels()) var/num_humans = living_player_list[1] var/num_xenos = living_player_list[2] if(force_end_at && world.time > force_end_at) round_finished = MODE_INFESTATION_X_MINOR - if(EvacuationAuthority.dest_status == NUKE_EXPLOSION_FINISHED) - round_finished = MODE_GENERIC_DRAW_NUKE //Nuke went off, ending the round. - if(EvacuationAuthority.dest_status == NUKE_EXPLOSION_GROUND_FINISHED) - round_finished = MODE_INFESTATION_M_MINOR //Nuke went off, ending the round. - if(EvacuationAuthority.dest_status < NUKE_EXPLOSION_IN_PROGRESS) //If the nuke ISN'T in progress. We do not want to end the round before it detonates. - if(!num_humans && num_xenos) //No humans remain alive. - round_finished = MODE_INFESTATION_X_MAJOR //Evacuation did not take place. Everyone died. - else if(num_humans && !num_xenos) - if(SSticker.mode && SSticker.mode.is_in_endgame) - round_finished = MODE_INFESTATION_X_MINOR //Evacuation successfully took place. - else - SSticker.roundend_check_paused = TRUE - round_finished = MODE_INFESTATION_M_MAJOR //Humans destroyed the xenomorphs. - ares_conclude() - addtimer(VARSET_CALLBACK(SSticker, roundend_check_paused, FALSE), MARINE_MAJOR_ROUND_END_DELAY) - else if(!num_humans && !num_xenos) - round_finished = MODE_INFESTATION_DRAW_DEATH //Both were somehow destroyed. + + if(!num_humans && num_xenos) //No humans remain alive. + round_finished = MODE_INFESTATION_X_MAJOR //Evacuation did not take place. Everyone died. + else if(num_humans && !num_xenos) + if(SSticker.mode && SSticker.mode.is_in_endgame) + round_finished = MODE_INFESTATION_X_MINOR //Evacuation successfully took place. + else + SSticker.roundend_check_paused = TRUE + round_finished = MODE_INFESTATION_M_MAJOR //Humans destroyed the xenomorphs. + ares_conclude() + addtimer(VARSET_CALLBACK(SSticker, roundend_check_paused, FALSE), MARINE_MAJOR_ROUND_END_DELAY) + else if(!num_humans && !num_xenos) + round_finished = MODE_INFESTATION_DRAW_DEATH //Both were somehow destroyed. /datum/game_mode/colonialmarines/check_queen_status(hivenumber) set waitfor = 0 @@ -367,7 +363,7 @@ round_statistics.current_map.total_marine_victories++ round_statistics.current_map.total_marine_majors++ if(MODE_INFESTATION_X_MINOR) - var/list/living_player_list = count_humans_and_xenos(EvacuationAuthority.get_affected_zlevels()) + var/list/living_player_list = count_humans_and_xenos(get_affected_zlevels()) if(living_player_list[1] && !living_player_list[2]) // If Xeno Minor but Xenos are dead and Humans are alive, see which faction is the last standing var/headcount = count_per_faction() var/living = headcount["total_headcount"] diff --git a/code/game/gamemodes/colonialmarines/whiskey_outpost.dm b/code/game/gamemodes/colonialmarines/whiskey_outpost.dm index 6669dc058ee7..9b3ef1df4c15 100644 --- a/code/game/gamemodes/colonialmarines/whiskey_outpost.dm +++ b/code/game/gamemodes/colonialmarines/whiskey_outpost.dm @@ -152,9 +152,6 @@ spawn(0) //Deleting Almayer, for performance! SSitem_cleanup.delete_almayer() - if(SSxenocon) - //Don't need XENOCON - SSxenocon.wait = 30 MINUTES //PROCCESS diff --git a/code/game/gamemodes/colonialmarines/xenovsxeno.dm b/code/game/gamemodes/colonialmarines/xenovsxeno.dm index 5623295f1915..a19c3e3582c1 100644 --- a/code/game/gamemodes/colonialmarines/xenovsxeno.dm +++ b/code/game/gamemodes/colonialmarines/xenovsxeno.dm @@ -79,9 +79,6 @@ spawn(0) //Deleting Almayer, for performance! SSitem_cleanup.delete_almayer() - if(SSxenocon) - //Don't need XENOCON - SSxenocon.wait = 30 MINUTES //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// diff --git a/code/game/gamemodes/extended/infection.dm b/code/game/gamemodes/extended/infection.dm index 04e0545361aa..a6b909022aef 100644 --- a/code/game/gamemodes/extended/infection.dm +++ b/code/game/gamemodes/extended/infection.dm @@ -95,7 +95,7 @@ possible_survivors -= new_survivor //either we drafted a survivor, or we're skipping over someone, either or - remove them /datum/game_mode/infection/check_win() - var/living_player_list[] = count_humans_and_xenos(EvacuationAuthority.get_affected_zlevels()) + var/list/living_player_list = count_humans_and_xenos(get_affected_zlevels()) var/num_humans = living_player_list[1] var/zed = living_player_list[2] diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 5382d80f37a2..e467631c915e 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -111,6 +111,10 @@ var/global/cas_tracking_id_increment = 0 //this var used to assign unique tracki log_game("Server IP: [world.internet_address]:[world.port]") return TRUE +/datum/game_mode/proc/get_affected_zlevels() + if(is_in_endgame) + . = SSmapping.levels_by_any_trait(list(ZTRAIT_MARINE_MAIN_SHIP)) + return ///process() ///Called by the gameticker @@ -119,8 +123,7 @@ var/global/cas_tracking_id_increment = 0 //this var used to assign unique tracki /datum/game_mode/proc/check_finished() //to be called by ticker - if(EvacuationAuthority.dest_status == NUKE_EXPLOSION_FINISHED || EvacuationAuthority.dest_status == NUKE_EXPLOSION_GROUND_FINISHED ) - return TRUE + return /datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. return diff --git a/code/game/jobs/job/civilians/other/liaison.dm b/code/game/jobs/job/civilians/other/liaison.dm index 7f73376a05dd..cbbb87124957 100644 --- a/code/game/jobs/job/civilians/other/liaison.dm +++ b/code/game/jobs/job/civilians/other/liaison.dm @@ -6,7 +6,7 @@ selection_class = "job_cl" flags_startup_parameters = ROLE_ADD_TO_DEFAULT gear_preset = /datum/equipment_preset/uscm_ship/liaison - entry_message_body = "As a representative of Weyland-Yutani Corporation, your job requires you to stay in character at all times. You are not required to follow military orders; however, you cannot give military orders. Your primary job is to observe and report back your findings to Weyland-Yutani. Follow regular game rules unless told otherwise by your superiors. Use your office fax machine to communicate with corporate headquarters or to acquire new directives. You may not receive anything back, and this is normal." + entry_message_body = "As a representative of Weyland-Yutani Corporation, your job requires you to stay in character at all times. You are not required to follow military orders; however, you cannot give military orders. Your primary job is to observe and report back your findings to Weyland-Yutani. Follow regular game rules unless told otherwise by your superiors. Use your office fax machine to communicate with corporate headquarters or to acquire new directives. You may not receive anything back, and this is normal." var/mob/living/carbon/human/active_liaison /datum/job/civilian/liaison/generate_entry_conditions(mob/living/liaison, whitelist_status) diff --git a/code/game/jobs/job/civilians/other/mess_seargent.dm b/code/game/jobs/job/civilians/other/mess_seargent.dm index 4b1975015a95..97578eb1159d 100644 --- a/code/game/jobs/job/civilians/other/mess_seargent.dm +++ b/code/game/jobs/job/civilians/other/mess_seargent.dm @@ -6,7 +6,7 @@ flags_startup_parameters = ROLE_ADD_TO_DEFAULT supervisors = "the auxiliary support officer" gear_preset = /datum/equipment_preset/uscm_ship/chef - entry_message_body = "Your job is to service the marines with excellent food, drinks and entertaining the shipside crew when needed. You have a lot of freedom and it is up to you, to decide what to do with it. Good luck!" + entry_message_body = "Your job is to service the marines with excellent food, drinks and entertaining the shipside crew when needed. You have a lot of freedom and it is up to you, to decide what to do with it. Good luck!" /obj/effect/landmark/start/chef name = JOB_MESS_SERGEANT diff --git a/code/game/jobs/job/civilians/other/survivors.dm b/code/game/jobs/job/civilians/other/survivors.dm index 5c82241c47de..23097e139eda 100644 --- a/code/game/jobs/job/civilians/other/survivors.dm +++ b/code/game/jobs/job/civilians/other/survivors.dm @@ -10,6 +10,8 @@ job_options = SURVIVOR_VARIANT_LIST var/intro_text var/story_text + /// Whether or not the survivor is an inherently hostile to marines. + var/hostile = FALSE /datum/job/civilian/survivor/set_spawn_positions(count) spawn_positions = Clamp((round(count * SURVIVOR_TO_TOTAL_SPAWN_RATIO)), 2, 8) @@ -59,23 +61,32 @@ if(picked_spawner.story_text) story_text = picked_spawner.story_text + + if(picked_spawner.hostile) + hostile = TRUE + new /datum/cm_objective/move_mob/almayer/survivor(H) -/datum/job/civilian/survivor/generate_entry_message(mob/living/carbon/human/H) +/datum/job/civilian/survivor/generate_entry_message(mob/living/carbon/human/survivor) if(intro_text) for(var/line in intro_text) - to_chat(H, line) + to_chat(survivor, line) else - to_chat(H, "