diff --git a/code/__DEFINES/_globals.dm b/code/__DEFINES/_globals.dm index 6448fab451a3..4da30c08aace 100644 --- a/code/__DEFINES/_globals.dm +++ b/code/__DEFINES/_globals.dm @@ -9,6 +9,21 @@ /// Creates an empty global initializer, do not use #define GLOBAL_UNMANAGED(X) /datum/controller/global_vars/proc/InitGlobal##X() { return; } +#define GLOBAL_MANAGED_MULTIPLE(X)\ +/datum/controller/global_vars/proc/InitGlobal##X(){\ + ##X = CollectGlobal##X();\ +}\ +/datum/controller/global_vars/proc/CollectGlobal##X(){\ + CAN_BE_REDEFINED(TRUE);\ + return list();\ +} + +#define GLOBAL_MULTIPLE_UPDATE(X, NewValue)\ +/datum/controller/global_vars/CollectGlobal##X(){\ + . = ..();\ + . += ##NewValue;\ +} + /// Creates name keyed subtype instance list #define GLOBAL_SUBTYPE_INDEXED(X, TypePath, Index)\ /datum/controller/global_vars/proc/InitGlobal##X(){\ @@ -69,6 +84,8 @@ /// Create a global const var, do not use #define GLOBAL_VAR_CONST(X, InitValue) GLOBAL_RAW(/const/##X) = InitValue; GLOBAL_UNMANAGED(X) +#define GLOBAL_MULTIPLE(X) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED_MULTIPLE(X) + /// Create a list global with an initializer expression #define GLOBAL_LIST_INIT(X, InitValue) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED(X, InitValue) diff --git a/code/__DEFINES/_protect.dm b/code/__DEFINES/_protect.dm index 5dbbd2d51386..f6fa1c12c8b0 100644 --- a/code/__DEFINES/_protect.dm +++ b/code/__DEFINES/_protect.dm @@ -1,4 +1,13 @@ +GLOBAL_MULTIPLE(protected_sentry_procs) +#define SET_PROTECTED_PROC(proc) GLOBAL_MULTIPLE_UPDATE(protected_sentry_procs, proc) + +GLOBAL_MULTIPLE(protected_sentry_datums) +#define SET_PROTECTED_DATUM(datum) GLOBAL_MULTIPLE_UPDATE(protected_sentry_datums, datum) + +GLOBAL_LIST_EMPTY(protected_config_entries) + #define GENERAL_PROTECT_DATUM(Path)\ +SET_PROTECTED_DATUM(Path)\ ##Path/can_vv_get(var_name){\ return FALSE;\ }\ diff --git a/code/__DEFINES/client.dm b/code/__DEFINES/client.dm index b335951aec0f..3ef997774bbf 100644 --- a/code/__DEFINES/client.dm +++ b/code/__DEFINES/client.dm @@ -8,6 +8,6 @@ /// This gathers all the client *procs* that we are pretending are verbs - but only particularly want /// authorized users to be able to use /client/proc/collect_client_verbs() as /list - CAN_BE_REDEFINED(TRUE); + CAN_BE_REDEFINED(TRUE) return list() diff --git a/code/__DEFINES/configuration.dm b/code/__DEFINES/configuration.dm index f0d64efb6dd7..749ec7b9380a 100644 --- a/code/__DEFINES/configuration.dm +++ b/code/__DEFINES/configuration.dm @@ -8,6 +8,8 @@ //flags #define CONFIG_ENTRY_LOCKED (1<<0) //can't edit #define CONFIG_ENTRY_HIDDEN (1<<1) //can't see value +#define CONFIG_ENTRY_SENSITIVE (1<<2) //scrubbed from public logging, if a list assumes value is sensitive +#define CONFIG_ENTRY_SENSITIVE_KEY (1<<3) //scrubbed from public logging, if a list assumes key is sensitive #define ON_CONFIG_LOAD(type) \ ##type/New() { \ diff --git a/code/__DEFINES/conflict.dm b/code/__DEFINES/conflict.dm index 0e915bf200bf..cf6d0a2cf8a2 100644 --- a/code/__DEFINES/conflict.dm +++ b/code/__DEFINES/conflict.dm @@ -50,6 +50,8 @@ #define PROJECTILE_SHRAPNEL (1<<0) /// Apply additional effects upon hitting clicked target #define PROJECTILE_BULLSEYE (1<<1) +/// Reflected projectiles +#define PROJECTILE_REFLECTED (1<<2) //Gun defines for gun related thing. More in the projectile folder. diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm index 689b1748f3e7..50219f1191bb 100644 --- a/code/__DEFINES/job.dm +++ b/code/__DEFINES/job.dm @@ -76,6 +76,8 @@ GLOBAL_LIST_INIT(job_squad_roles, JOB_SQUAD_ROLES_LIST) #define JOB_DOCTOR_ROLES_LIST list(JOB_CMO, JOB_DOCTOR, JOB_SURGEON, JOB_PHARMACIST, JOB_FIELD_DOCTOR) #define JOB_RESEARCH_ROLES /datum/timelock/research #define JOB_RESEARCH_ROLES_LIST list(JOB_RESEARCHER) +/// all roles expected to perform shipside larva removal, used for ares_autodoc_check +#define JOB_SURGERY_ROLES_LIST list(JOB_CMO, JOB_DOCTOR, JOB_SURGEON, JOB_PHARMACIST, JOB_FIELD_DOCTOR, JOB_SYNTH, JOB_SYNTH_ENG, JOB_SYNTH_MED, JOB_SYNTH_INTEL, JOB_SYNTH_MP, JOB_SYNTH_CMD, JOB_SYNTH_SCI) #define JOB_CORPORATE_LIAISON "Corporate Liaison" #define JOB_CORPORATE_BODYGUARD "Corporate Bodyguard" diff --git a/code/__DEFINES/mode.dm b/code/__DEFINES/mode.dm index e800ceb60d0e..63db389b917b 100644 --- a/code/__DEFINES/mode.dm +++ b/code/__DEFINES/mode.dm @@ -305,6 +305,7 @@ DEFINE_BITFIELD(whitelist_status, list( #define FACTION_LIST_SURVIVOR_IASF list(FACTION_SURVIVOR, FACTION_IASF, FACTION_TWE) #define FACTION_LIST_SURVIVOR_PAP list(FACTION_SURVIVOR, FACTION_PAP, FACTION_UPP) #define FACTION_LIST_SURVIVOR_UPP list(FACTION_SURVIVOR, FACTION_UPP) +#define FACTION_LIST_SURVIVOR_HYPERDYNE list(FACTION_SURVIVOR, FACTION_UPP, FACTION_HYPERDYNE) #define FACTION_LIST_MARINE_WY list(FACTION_MARINE, FACTION_PMC, FACTION_WY) #define FACTION_LIST_MARINE_UPP list(FACTION_MARINE, FACTION_UPP) #define FACTION_LIST_MARINE_TWE list(FACTION_MARINE, FACTION_TWE) diff --git a/code/__DEFINES/paygrade_defs/navy.dm b/code/__DEFINES/paygrade_defs/navy.dm index 1ccb820274b0..e09dff6d44cb 100644 --- a/code/__DEFINES/paygrade_defs/navy.dm +++ b/code/__DEFINES/paygrade_defs/navy.dm @@ -1,55 +1,55 @@ // Paygrade shorthand defines, to allow clearer designation. // USCM NAVY -/// NE1, Seaman Recruit +/// NE1, Aerospaceman Recruit #define PAY_SHORT_NE1 "NE1" -/// NE2M, Seaman Apprentice +/// NE2M, Aerospaceman #define PAY_SHORT_NE2 "NE2" -/// NE3, Seaman +/// NE3, Aerospaceman 1st Class #define PAY_SHORT_NE3 "NE3" -/// NE4, Petty Officer 3rd Class +/// NE4, Buck Sergeant #define PAY_SHORT_NE4 "NE4" -/// NE5, Petty Officer 2nd Class +/// NE5, Staff Sergeant #define PAY_SHORT_NE5 "NE5" -/// NE6, Petty Officer 1st Class +/// NE6, Technical Sergeant #define PAY_SHORT_NE6 "NE6" -/// NE7, Chief Petty Officer +/// NE7, Master Sergeant #define PAY_SHORT_NE7 "NE7" -/// NE8, Senior Chief Petty Officer +/// NE8, Senior Master Sergeant #define PAY_SHORT_NE8 "NE8" -/// NE8C, Command Senior Chief Petty Officer +/// NE8C, Command Senior Master Sergeant #define PAY_SHORT_NE8C "NE8C" -/// NE9, Master Chief Petty Officer +/// NE9, Chief Master Sergeant #define PAY_SHORT_NE9 "NE9" -/// NE9C, Command Master Chief Petty Officer +/// NE9C, Command Chief Master Sergeant Of The Aerospace Force #define PAY_SHORT_NE9C "NE9C" -/// NO1, Ensign +/// NO1, 2nd Lieutenant #define PAY_SHORT_NO1 "NO1" -/// NO2, Lieutenant Junior Grade +/// NO2, 1st Lieutenant #define PAY_SHORT_NO2 "NO2" -/// NO3, Lieutenant +/// NO3, Captain #define PAY_SHORT_NO3 "NO3" -/// NO4, Lieutenant Commander +/// NO4, Lieutenant Colonel #define PAY_SHORT_NO4 "NO4" -/// NO5, Commander +/// NO5, Colonel #define PAY_SHORT_NO5 "NO5" -/// NO6, Captain +/// NO6, Major #define PAY_SHORT_NO6 "NO6" /// NO6E, Commodore @@ -58,16 +58,16 @@ /// NO6C, Senior Commodore #define PAY_SHORT_NO6C "NO6C" -/// NO7, Rear Admiral (Lower Half) +/// NO7, Brigadier General #define PAY_SHORT_NO7 "NO7" -/// NO8, Rear Admiral (Upper Half) +/// NO8, Major General #define PAY_SHORT_NO8 "NO8" -/// NO9, Vice Admiral +/// NO9, Lieutenant General #define PAY_SHORT_NO9 "NO9" -/// NO10, Admiral +/// NO10, General #define PAY_SHORT_NO10 "NO10" /// NO10C, Chief of Naval Operations diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 40c38d270b89..7c55f81361ff 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -169,6 +169,8 @@ #define TRAIT_VALKYRIE_ARMORED "trait_valkyrie_armored" /// Prevents mob from riding mobs when buckled onto something #define TRAIT_CANT_RIDE "cant_ride" +/// Makes mob immune to dir lock slowdown. +#define TRAIT_NO_DIR_LOCK_SLOWDOWN "no_dir_lock_slowdown" // SPECIES TRAITS /// Knowledge of Yautja technology @@ -265,6 +267,11 @@ #define TRAIT_ABILITY_BURROWED "t_ability_burrowed" /// Xenos with this trait can toggle long sight while resting. #define TRAIT_ABILITY_SIGHT_IGNORE_REST "t_ability_sight_ignore_rest" +/// Used by shielder to check stance. +#define TRAIT_ABILITY_ENCLOSED_PLATES "t_ability_enclosed_plates" +/// Used by shielder for reflective plates. +#define TRAIT_ABILITY_REFLECTIVE_PLATES "t_ability_reflective_plates" + //-- item traits -- // TOOL TRAITS @@ -344,6 +351,8 @@ GLOBAL_LIST_INIT(mob_traits, list( TRAIT_DEXTROUS, TRAIT_REAGENT_SCANNER, TRAIT_ABILITY_BURROWED, + TRAIT_ABILITY_ENCLOSED_PLATES, + TRAIT_ABILITY_REFLECTIVE_PLATES, TRAIT_VULTURE_USER, TRAIT_IN_TUTORIAL, TRAIT_SPEC_KIT, diff --git a/code/__DEFINES/typecheck/items.dm b/code/__DEFINES/typecheck/items.dm index 5c4d099b8112..4438cd918aa0 100644 --- a/code/__DEFINES/typecheck/items.dm +++ b/code/__DEFINES/typecheck/items.dm @@ -1,9 +1,7 @@ #define iswelder(O) (istype(O, /obj/item/tool/weldingtool)) -#define iscoil(O) (istype(O, /obj/item/stack/cable_coil)) #define iswire(O) (istype(O, /obj/item/stack/cable_coil)) #define isweapon(O) (O && is_type_in_list(O, GLOB.weapons)) #define isgun(O) (istype(O, /obj/item/weapon/gun)) -#define isbanana(O) (istype(O, /obj/item/reagent_container/food/snacks/grown/banana)) #define istool(O) (O && is_type_in_list(O, GLOB.common_tools)) #define ispowerclamp(O) (istype(O, /obj/item/powerloader_clamp)) #define isstorage(O) (istype(O, /obj/item/storage)) @@ -27,7 +25,3 @@ GLOBAL_LIST_INIT(common_tools, list( /obj/item/device/multitool, /obj/item/tool/crowbar )) - -/obj/item/proc/can_pry() - if(pry_capable > IS_PRY_CAPABLE_SIMPLE || HAS_TRAIT(src, TRAIT_TOOL_CROWBAR)) - return TRUE diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno.dm index b6377a47337b..9af7e845818b 100644 --- a/code/__DEFINES/xeno.dm +++ b/code/__DEFINES/xeno.dm @@ -421,6 +421,9 @@ // Lurker strain flags #define LURKER_VAMPIRE "Vampire" +// Warrior strain flags +#define WARRIOR_BULWARK "Bulwark" + // Ravager strain flags #define RAVAGER_HEDGEHOG "Hedgehog" #define RAVAGER_BERSERKER "Berserker" @@ -449,6 +452,7 @@ // Damage - this is applied as a flat nerf/buff to the xeno's average damage #define XENO_DAMAGE_MOD_VERY_SMALL 5 +#define XENO_DAMAGE_MOD_BULWARK 8 #define XENO_DAMAGE_MOD_SMALL 10 #define XENO_DAMAGE_MOD_MED 15 #define XENO_DAMAGE_MOD_LARGE 20 @@ -747,6 +751,8 @@ // dancer defines #define DANCER_DODGE_TIME 7 SECONDS +// bulwark defines +#define BULWARK_REFLECTIVE_TIME 6 SECONDS // drone fruits diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 6c57aa62f2af..fd6f2fa5b5dc 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -4,6 +4,15 @@ GLOBAL_PROTECT(admins) GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client +GLOBAL_DATUM(all_player_keys_regex, /regex) +GLOBAL_LIST_EMPTY(all_player_keys) + +GLOBAL_DATUM(all_player_cids_regex, /regex) +GLOBAL_LIST_EMPTY(all_player_cids) + +GLOBAL_DATUM(all_player_ckeys_regex, /regex) +GLOBAL_LIST_EMPTY(all_player_ckeys) + GLOBAL_LIST_EMPTY(player_list) //all mobs **with clients attached**. GLOBAL_LIST_EMPTY(living_player_list) // all /mob/living with clients diff --git a/code/_onclick/adjacent.dm b/code/_onclick/adjacent.dm index e853760b103a..41ccad15fe3d 100644 --- a/code/_onclick/adjacent.dm +++ b/code/_onclick/adjacent.dm @@ -273,7 +273,7 @@ Quick adjacency (to turf): var/list/cur_dense_blockers = list() for(var/atom/blocker in blockers["fd1"]) if(blocker.flags_barrier & HANDLE_BARRIER_CHANCE) - if(blocker.handle_barrier_chance()) + if(blocker.handle_barrier_chance(attacker)) return blocker else guaranteed_hit = 1 @@ -282,7 +282,7 @@ Quick adjacency (to turf): for(var/atom/blocker in blockers["fd2"]) if(blocker.flags_barrier & HANDLE_BARRIER_CHANCE) - if(blocker.handle_barrier_chance()) + if(blocker.handle_barrier_chance(attacker)) return blocker else guaranteed_hit++ diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 1c180ba16b25..13b08bf3672f 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -105,6 +105,10 @@ if(isxeno(M)) var/mob/living/carbon/xenomorph/X = M power = armor_damage_reduction(GLOB.xeno_melee, power, X.armor_deflection + X.armor_deflection_buff - X.armor_deflection_debuff, 20, 0, 0, X.armor_integrity) + + if(X.melee_vulnerability_mult != 0) + power *= X.melee_vulnerability_mult + var/armor_punch = armor_break_calculation(GLOB.xeno_melee, power, X.armor_deflection + X.armor_deflection_buff - X.armor_deflection_debuff, 20, 0, 0, X.armor_integrity) X.apply_armorbreak(armor_punch) if(hitsound) diff --git a/code/controllers/configuration/config_entry.dm b/code/controllers/configuration/config_entry.dm index 5225bfb63844..f9e618942ff2 100644 --- a/code/controllers/configuration/config_entry.dm +++ b/code/controllers/configuration/config_entry.dm @@ -6,6 +6,8 @@ #define KEY_MODE_TYPE 1 #define KEY_MODE_TEXT_UNALTERED 2 +SET_PROTECTED_DATUM(/datum/config_entry) + /datum/config_entry var/name //read-only, this is determined by the last portion of the derived entry type var/config_entry_value diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm index b74275d2cee2..be0dd7c8d53b 100644 --- a/code/controllers/configuration/configuration.dm +++ b/code/controllers/configuration/configuration.dm @@ -1,3 +1,5 @@ +SET_PROTECTED_DATUM(/datum/controller/configuration) + /datum/controller/configuration name = "Configuration" @@ -162,6 +164,9 @@ _entries[esname] = E _entries_by_type[I] = E + if(E.protection & (CONFIG_ENTRY_SENSITIVE|CONFIG_ENTRY_SENSITIVE_KEY)) + GLOB.protected_config_entries += E + /datum/controller/configuration/proc/RemoveEntry(datum/config_entry/CE) entries -= CE.name diff --git a/code/controllers/configuration/entries/achievements.dm b/code/controllers/configuration/entries/achievements.dm index ca537860691f..036b6e4d1d21 100644 --- a/code/controllers/configuration/entries/achievements.dm +++ b/code/controllers/configuration/entries/achievements.dm @@ -1,10 +1,10 @@ /// The base URL for the achievements API endpoint /datum/config_entry/string/achievements_api_url - protection = CONFIG_ENTRY_HIDDEN + protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_SENSITIVE /// The API key for authenticating with the achievements service /datum/config_entry/string/achievements_api_key - protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED + protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_SENSITIVE /// Additional parameter passed to the backend service /datum/config_entry/string/achievements_instance diff --git a/code/controllers/configuration/entries/dbconfig.dm b/code/controllers/configuration/entries/dbconfig.dm index be4799988f3b..1cf195732a95 100644 --- a/code/controllers/configuration/entries/dbconfig.dm +++ b/code/controllers/configuration/entries/dbconfig.dm @@ -15,7 +15,7 @@ protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED /datum/config_entry/string/db_password - protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED + protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_SENSITIVE /datum/config_entry/flag/db_debug_mode protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 1fde1a7cef87..dbc4732a5531 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -240,7 +240,7 @@ Administrative related. protection = CONFIG_ENTRY_LOCKED /datum/config_entry/string/tgs3_commandline_path - protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN + protection = CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_SENSITIVE config_entry_value = "C:\\Program Files (x86)\\TG Station Server\\TGCommandLine.exe" /datum/config_entry/number/minute_topic_limit @@ -375,14 +375,14 @@ or your package manager The default value assumes youtube-dl is in your system PATH */ /datum/config_entry/string/invoke_youtubedl - protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN + protection = CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_SENSITIVE /datum/config_entry/string/cobalt_base_api - protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN + protection = CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_SENSITIVE /datum/config_entry/string/cobalt_api_key - protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN + protection = CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_SENSITIVE /datum/config_entry/number/error_cooldown // The "cooldown" time for each occurrence of a unique error config_entry_value = 600 @@ -589,7 +589,7 @@ This maintains a list of ip addresses that are able to bypass topic filtering. /datum/config_entry/keyed_list/topic_tokens key_mode = KEY_MODE_TEXT value_mode = VALUE_MODE_TEXT - protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED + protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_SENSITIVE_KEY /datum/config_entry/keyed_list/topic_tokens/ValidateListEntry(key_name, key_value) return key_value != "topic_token" && ..() @@ -627,7 +627,7 @@ This maintains a list of ip addresses that are able to bypass topic filtering. /datum/config_entry/string/redis_connection config_entry_value = "redis://127.0.0.1/" - protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED + protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_SENSITIVE /datum/config_entry/string/instance_name config_entry_value = "game" @@ -724,9 +724,10 @@ This maintains a list of ip addresses that are able to bypass topic filtering. protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN /datum/config_entry/string/sentry_endpoint + protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_SENSITIVE /datum/config_entry/string/sentry_dsn - protection = CONFIG_ENTRY_HIDDEN + protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_SENSITIVE /datum/config_entry/str_list/ignored_cids protection = CONFIG_ENTRY_LOCKED diff --git a/code/controllers/mc/globals.dm b/code/controllers/mc/globals.dm index 59b96c017d10..977975f3f45f 100644 --- a/code/controllers/mc/globals.dm +++ b/code/controllers/mc/globals.dm @@ -42,8 +42,14 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars) gvars_datum_init_order = list() gvars_datum_protected_varlist = list(NAMEOF(src, gvars_datum_protected_varlist) = TRUE) var/list/global_procs = typesof(/datum/controller/global_vars/proc) + + var/global_inits = 0 + for(var/procpath/proc as anything in global_procs) + if(findtext(proc.name, "InitGlobal")) + global_inits++ + var/expected_len = length(vars) - length(gvars_datum_in_built_vars) - if(length(global_procs) != expected_len) + if(global_inits != expected_len) warning("Unable to detect all global initialization procs! Expected [expected_len] got [length(global_procs)]!") if(length(global_procs)) var/list/expected_global_procs = vars - gvars_datum_in_built_vars diff --git a/code/controllers/subsystem/cmtv.dm b/code/controllers/subsystem/cmtv.dm index 03104a886720..22693b69e9b9 100644 --- a/code/controllers/subsystem/cmtv.dm +++ b/code/controllers/subsystem/cmtv.dm @@ -484,27 +484,44 @@ SUBSYSTEM_DEF(cmtv) camera_operator.view = "32x24" /datum/controller/subsystem/cmtv/proc/is_subscriber(client/potential_subscriber) - if(!CONFIG_GET(string/cmtv_api) || !CONFIG_GET(string/cmtv_api_key)) - return FALSE + var/static/lookup_cache = list() + + var/cmtv_subscriber_api = CONFIG_GET(string/cmtv_subscriber_api) + var/cmtv_subscriber_api_key = CONFIG_GET(string/cmtv_subscriber_api_key) - WAIT_DB_READY + if(!CONFIG_GET(string/cmtv_api) || !CONFIG_GET(string/cmtv_api_key) || !cmtv_subscriber_api || !cmtv_subscriber_api_key) + return FALSE UNTIL(initialized) if(!potential_subscriber) return FALSE - var/list/datum/view_record/twitch_link/links = DB_VIEW(/datum/view_record/twitch_link, DB_AND( - DB_COMP("ckey", DB_EQUALS, potential_subscriber.ckey), - DB_COMP("twitch_id", DB_ISNOT) - )) + var/twitch_id = lookup_cache[potential_subscriber.ckey] + if(!twitch_id) + var/datum/http_request/request = new + request.prepare(RUSTG_HTTP_METHOD_GET, "[cmtv_subscriber_api]?ckey=[potential_subscriber.ckey]", null, list("Authorization" = "Bearer [cmtv_subscriber_api_key]")) + request.begin_async() - if(!length(links)) - return FALSE + UNTIL(request.is_complete()) + + var/datum/http_response/response = request.into_response() + + var/decoded + try + decoded = json_decode(response.body) + catch + log_debug("cmtv_subscriber_api returned an invalid response.") + return FALSE - for(var/datum/view_record/twitch_link/link as anything in links) - if(link.twitch_id in subscribers) - return TRUE + twitch_id = decoded["twitch_id"] + if(!twitch_id) + return FALSE + + lookup_cache[potential_subscriber.ckey] = twitch_id + + if(twitch_id in subscribers) + return TRUE return FALSE @@ -655,8 +672,15 @@ SUBSYSTEM_DEF(cmtv) protection = CONFIG_ENTRY_LOCKED /datum/config_entry/string/cmtv_api_key + protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_SENSITIVE + +/datum/config_entry/string/cmtv_subscriber_api + protection = CONFIG_ENTRY_LOCKED + +/datum/config_entry/string/cmtv_subscriber_api_key protection = CONFIG_ENTRY_HIDDEN | CONFIG_ENTRY_LOCKED + /atom/movable/screen/cmtv plane = ESCAPE_MENU_PLANE clear_with_screen = FALSE diff --git a/code/controllers/subsystem/hijack.dm b/code/controllers/subsystem/hijack.dm index 9c9a4447b1c6..b9daf9c2e9ca 100644 --- a/code/controllers/subsystem/hijack.dm +++ b/code/controllers/subsystem/hijack.dm @@ -797,7 +797,7 @@ SUBSYSTEM_DEF(hijack) var/cause_data = create_cause_data("ship explosion") for(var/obj/structure/machinery/power/apc/apc as anything in apcs) var/turf/apc_turf = get_turf(apc) - if(apc_turf && prob(chance)) + if(apc_turf && apc.crash_break_probability && prob(chance)) cell_explosion(apc_turf, 30, 5, explosion_cause_data=cause_data, enviro=TRUE) CHECK_TICK diff --git a/code/controllers/subsystem/sentry.dm b/code/controllers/subsystem/sentry.dm index cd54ec39996a..304d0f55129c 100644 --- a/code/controllers/subsystem/sentry.dm +++ b/code/controllers/subsystem/sentry.dm @@ -19,6 +19,8 @@ SUBSYSTEM_DEF(sentry) can_fire = FALSE return SS_INIT_NO_NEED + return SS_INIT_SUCCESS + /datum/controller/subsystem/sentry/fire(resumed) var/static/list/headers = list( "Content-Type" = "application/x-sentry-envelope", @@ -37,6 +39,8 @@ SUBSYSTEM_DEF(sentry) if(!endpoint) endpoint = ENDPOINT_CONFIG + var/static/regex/ip_regex = regex(@"(((?!25?[6-9])[12]\d|[1-9])?\d\.?\b){4}", "g") + for(var/datum/error_envelope/error as anything in envelopes) var/event_id = get_uuid() @@ -46,12 +50,12 @@ SUBSYSTEM_DEF(sentry) for(var/datum/static_callee/called as anything in error.stacktrace) var/list/parsed_args = list( - "src" = called._src, - "usr" = called._usr, + "src" = isnull(called._src) ? "null" : called._src, + "usr" = isnull(called._usr) ? "null" : called._usr, ) var/index = 1 for(var/arg in called._args) - parsed_args["argument #[index]"] = arg + parsed_args["argument #[index]"] = isnull(arg) ? "null" : arg index++ var/pre_context, context, post_context @@ -72,16 +76,29 @@ SUBSYSTEM_DEF(sentry) var/procpath/proc_path = called.proc - stacktrace += list(list( + var/censor_args = FALSE + if(proc_path.type in GLOB.protected_sentry_procs) + censor_args = TRUE + else + for(var/protected in GLOB.protected_sentry_datums) + if(findtext("[proc_path.type]", "[protected]")) + censor_args = TRUE + break + + var/to_add = list( "filename" = called.file, "function" = proc_path.type, "lineno" = called.line, - "vars" = parsed_args, "pre_context" = pre_context, "context_line" = context, "post_context" = post_context, "source_link" = "https://github.com/cmss13-devs/cmss13/blob/[git_revision]/[called.file]#L[called.line]" - )) + ) + + if(!censor_args) + to_add["vars"] = parsed_args + + stacktrace += list(to_add) var/list/event_parts = list( "event_id" = event_id, @@ -92,17 +109,37 @@ SUBSYSTEM_DEF(sentry) "round_id" = GLOB.round_id, ), "exception" = list( - "type" = error.error, - "value" = "Runtime Error", - "stacktrace" = list("frames" = stacktrace), + "values" = list(list( + "type" = error.error, + "value" = "Runtime Error", + "stacktrace" = list("frames" = stacktrace), + )) ), ) var/event = json_encode(event_parts) + + event = ip_regex.Replace(event, "ip address") + + event = replacetext(event, GLOB.all_player_keys_regex, "player key") + event = replacetext(event, GLOB.all_player_ckeys_regex, "player ckey") + event = replacetext(event, GLOB.all_player_cids_regex, "player computer id") + + for(var/datum/config_entry/protected_entry in GLOB.protected_config_entries) + if(islist(protected_entry.config_entry_value)) + for(var/key, value in protected_entry.config_entry_value) + if(protected_entry.protection & CONFIG_ENTRY_SENSITIVE_KEY || isnull(value)) + event = replacetext(event, key, "config entry key [protected_entry.type]") + else + event = replacetext(event, value, "config entry value [protected_entry.type]") + else + if(length(protected_entry.config_entry_value)) + event = replacetext(event, protected_entry.config_entry_value, "config entry [protected_entry.type]") + var/event_header = "{\"type\":\"event\",\"length\":[length(event)]}" var/assembled = "[header]\n[event_header]\n[event]\n" - rustg_http_request_fire_and_forget(RUSTG_HTTP_METHOD_POST, endpoint, assembled, headers, null) + rustg_http_request_blocking(RUSTG_HTTP_METHOD_POST, endpoint, assembled, headers, null) envelopes.Cut() diff --git a/code/datums/ammo/bullet/shotgun.dm b/code/datums/ammo/bullet/shotgun.dm index b06d42ce42b9..345152076e28 100644 --- a/code/datums/ammo/bullet/shotgun.dm +++ b/code/datums/ammo/bullet/shotgun.dm @@ -209,6 +209,10 @@ /datum/ammo/bullet/shotgun/buckshot/on_hit_mob(mob/M,obj/projectile/P) knockback(M,P) +/datum/ammo/bullet/shotgun/buckshot/turret + flags_ammo_behavior = AMMO_NO_DEFLECT //New Exclusive ammo for shotgun turrets. + bonus_projectiles_type = /datum/ammo/bullet/shotgun/spread/turret + //buckshot variant only used by the masterkey shotgun attachment. /datum/ammo/bullet/shotgun/buckshot/masterkey bonus_projectiles_type = /datum/ammo/bullet/shotgun/spread/masterkey @@ -238,6 +242,9 @@ /datum/ammo/bullet/shotgun/spread/masterkey damage = 20 +/datum/ammo/bullet/shotgun/spread/turret + flags_ammo_behavior = AMMO_NO_DEFLECT //New Exclusive buckshot pellets for shotgun turrets. + /* 8 GAUGE SHOTGUN AMMO */ diff --git a/code/datums/ammo/bullet/special_ammo.dm b/code/datums/ammo/bullet/special_ammo.dm index 19abf007c2e7..7f5d4d9dd1d8 100644 --- a/code/datums/ammo/bullet/special_ammo.dm +++ b/code/datums/ammo/bullet/special_ammo.dm @@ -151,7 +151,7 @@ /datum/ammo/bullet/turret name = "autocannon bullet" icon_state = "redbullet" //Red bullets to indicate friendly fire restriction - flags_ammo_behavior = AMMO_BALLISTIC|AMMO_IGNORE_COVER + flags_ammo_behavior = AMMO_BALLISTIC|AMMO_IGNORE_COVER|AMMO_NO_DEFLECT accurate_range = 22 accuracy_var_low = PROJECTILE_VARIANCE_TIER_8 diff --git a/code/datums/ammo/misc.dm b/code/datums/ammo/misc.dm index 516faadeb21b..8f7659dd9871 100644 --- a/code/datums/ammo/misc.dm +++ b/code/datums/ammo/misc.dm @@ -66,7 +66,7 @@ landingsmoke = null /datum/ammo/flamethrower/sentry_flamer - flags_ammo_behavior = AMMO_IGNORE_ARMOR|AMMO_IGNORE_COVER|AMMO_FLAME + flags_ammo_behavior = AMMO_IGNORE_ARMOR|AMMO_IGNORE_COVER|AMMO_FLAME|AMMO_NO_DEFLECT flamer_reagent_id = "napalmx" accuracy = HIT_ACCURACY_TIER_8 diff --git a/code/datums/emergency_calls/emergency_call.dm b/code/datums/emergency_calls/emergency_call.dm index c6168d7154c1..2857151f592e 100644 --- a/code/datums/emergency_calls/emergency_call.dm +++ b/code/datums/emergency_calls/emergency_call.dm @@ -71,6 +71,9 @@ /// the [/datum/lazy_template] we should attempt to spawn in for the return journey var/home_base = /datum/lazy_template/ert/freelancer_station + /// What sound plays to ghosts when this rolls? Silent if null + var/alert_sound + /datum/game_mode/proc/initialize_emergency_calls() if(length(all_calls)) //It's already been set up. return @@ -139,6 +142,9 @@ give_action(M, /datum/action/join_ert, src) + if(!isnull(alert_sound)) + playsound_client(M.client, alert_sound, vol = 50) + /datum/game_mode/proc/activate_distress() ert_dispatched = TRUE var/datum/emergency_call/random_call = get_random_call() diff --git a/code/datums/emergency_calls/mercs.dm b/code/datums/emergency_calls/mercs.dm index b5157739a601..eed6382b7420 100644 --- a/code/datums/emergency_calls/mercs.dm +++ b/code/datums/emergency_calls/mercs.dm @@ -9,10 +9,11 @@ . = ..() if(isnull(hostility)) hostility = pick(75;FALSE,25;TRUE) - arrival_message = "[MAIN_SHIP_NAME], this is Freelancer shuttle [pick(GLOB.alphabet_lowercase)][pick(GLOB.alphabet_lowercase)]-[rand(1, 99)] responding to your distress call. Prepare for boarding." if(hostility) + arrival_message = "Lovely ship you've got there. Arikara class, huh? Don't mind if we do. \n\nThis is Freelancer shuttle [pick(GLOB.alphabet_lowercase)][pick(GLOB.alphabet_lowercase)]-[rand(1, 99)] responding to your distress. Prepare your valuables for boarding." objectives = "Ransack the [MAIN_SHIP_NAME] and kill anyone who gets in your way. Do what your Warlord says. Ensure your survival at all costs." else + arrival_message = "[MAIN_SHIP_NAME], this is Freelancer shuttle [pick(GLOB.alphabet_lowercase)][pick(GLOB.alphabet_lowercase)]-[rand(1, 99)] responding to your distress call. Prepare for boarding." objectives = "Help the crew of the [MAIN_SHIP_NAME] in exchange for payment, and choose your payment well. Do what your Warlord says. Ensure your survival at all costs." /datum/emergency_call/mercs/friendly //if admins want to specifically call in friendly ones @@ -85,10 +86,11 @@ /datum/emergency_call/heavy_mercs/New() . = ..() hostility = pick(75;FALSE,25;TRUE) - arrival_message = "[MAIN_SHIP_NAME], this is Elite Freelancer shuttle [pick(GLOB.alphabet_lowercase)][pick(GLOB.alphabet_lowercase)]-[rand(1, 99)] responding to your distress call. Prepare for boarding." if(hostility) + arrival_message = "[MAIN_SHIP_NAME], this is Elite Freelancer shuttle [pick(GLOB.alphabet_lowercase)][pick(GLOB.alphabet_lowercase)]-[rand(1, 99)] responding. We've heard your distress and are currently preparing to ransack you for your very lives. Prepare for boarding." objectives = "Ransack the [MAIN_SHIP_NAME] and kill anyone who gets in your way. Do what your Captain says. Ensure your survival at all costs." else + arrival_message = "[MAIN_SHIP_NAME], this is Elite Freelancer shuttle [pick(GLOB.alphabet_lowercase)][pick(GLOB.alphabet_lowercase)]-[rand(1, 99)] responding to your distress call. Prepare for boarding." objectives = "Help the crew of the [MAIN_SHIP_NAME] in exchange for payment, and choose your payment well. Do what your Captain says. Ensure your survival at all costs." /datum/emergency_call/heavy_mercs/hostile @@ -97,7 +99,7 @@ /datum/emergency_call/heavy_mercs/hostile/New() . = ..() hostility = TRUE - arrival_message = "[MAIN_SHIP_NAME], this is Elite Freelancer shuttle [pick(GLOB.alphabet_lowercase)][pick(GLOB.alphabet_lowercase)]-[rand(1, 99)] responding to your distress call. Prepare for boarding." + arrival_message = "[MAIN_SHIP_NAME], this is Elite Freelancer shuttle [pick(GLOB.alphabet_lowercase)][pick(GLOB.alphabet_lowercase)]-[rand(1, 99)] responding. We've heard your distress and are currently preparing to ransack you for your very lives. Prepare for boarding." objectives = "Ransack the [MAIN_SHIP_NAME] and kill anyone who gets in your way. Do what your Captain says. Ensure your survival at all costs." /datum/emergency_call/heavy_mercs/friendly diff --git a/code/datums/emergency_calls/pred_hunt/hunting_calls.dm b/code/datums/emergency_calls/pred_hunt/hunting_calls.dm index 2773e946cd22..e5a252ebc035 100644 --- a/code/datums/emergency_calls/pred_hunt/hunting_calls.dm +++ b/code/datums/emergency_calls/pred_hunt/hunting_calls.dm @@ -8,6 +8,7 @@ shuttle_id = "" ert_message = "Prey is being set loose in the Yautja Hunting Grounds" ignore_ftl_or_crash = TRUE + alert_sound = 'sound/items/pred_bracer.ogg' /// Multiplier on the base RESERVE_HUNT_COOLDOWN when a given ERT is selected; 1 is no change. var/timer_mult = 1 var/hunt_name diff --git a/code/datums/entities/twitch_link.dm b/code/datums/entities/twitch_link.dm deleted file mode 100644 index 5c1ccc4e30a3..000000000000 --- a/code/datums/entities/twitch_link.dm +++ /dev/null @@ -1,73 +0,0 @@ - -/datum/entity/twitch_link - var/ckey - var/access_code - var/twitch_id - -/datum/entity_meta/twitch_link - entity_type = /datum/entity/twitch_link - table_name = "twitch_link" - field_types = list( - "ckey" = DB_FIELDTYPE_STRING_LARGE, - "access_code" = DB_FIELDTYPE_STRING_MEDIUM, - "twitch_id" = DB_FIELDTYPE_STRING_LARGE, - ) - -/datum/view_record/twitch_link - var/ckey - var/access_code - var/twitch_id - var/id - -/datum/entity_view_meta/twitch_link - root_record_type = /datum/entity/twitch_link - destination_entity = /datum/view_record/twitch_link - - fields = list( - "ckey", - "access_code", - "twitch_id", - "id", - ) - -/datum/config_entry/string/twitch_link_url - protection = CONFIG_ENTRY_LOCKED - -CLIENT_VERB(link_twitch) - set name = "Twitch Link" - set category = "OOC" - - var/url = CONFIG_GET(string/twitch_link_url) - if(!url) - to_chat(src, SPAN_WARNING("Twitch linking is not enabled on this server.")) - return - - if(IsGuestKey(key, TRUE)) - to_chat(src, SPAN_WARNING("You must be connected as a BYOND key to connect to Twitch.")) - return - - if(length(DB_VIEW(/datum/view_record/twitch_link, - DB_AND( - DB_COMP("ckey", DB_EQUALS, ckey), - DB_COMP("twitch_id", DB_IS) - )) - )) - to_chat(src, SPAN_WARNING("You have already linked this CKEY to Twitch. Contact support to remove this.")) - return - - var/datum/view_record/twitch_link/existing_link = locate() in DB_VIEW( - DB_COMP("ckey", DB_EQUALS, ckey) - ) - - if(existing_link) - to_chat(src, SPAN_LARGE(SPAN_NOTICE("Please click here to link your CKEY to Twitch."))) - return - - var/datum/entity/twitch_link/new_link = DB_ENTITY(/datum/entity/twitch_link) - new_link.access_code = generate_access_code() - new_link.ckey = ckey - - new_link.save() - new_link.detach() - - to_chat(src, SPAN_LARGE(SPAN_NOTICE("Please click here to link your CKEY to Twitch."))) diff --git a/code/datums/paygrades/factions/uscm/navy.dm b/code/datums/paygrades/factions/uscm/navy.dm index 5b16509313f2..e445a7b42f8f 100644 --- a/code/datums/paygrades/factions/uscm/navy.dm +++ b/code/datums/paygrades/factions/uscm/navy.dm @@ -8,78 +8,78 @@ /datum/paygrade/navy/e1 paygrade = PAY_SHORT_NE1 - name = "Seaman Recruit" - prefix = "SR." + name = "Aeroman Recruit" + prefix = "AER." rank_pin = /obj/item/clothing/accessory/ranks/navy/e1 ranking = 0 /datum/paygrade/navy/e2 paygrade = PAY_SHORT_NE2 - name = "Seaman Apprentice" - prefix = "SA." + name = "Aeroman" + prefix = "AEM." rank_pin = /obj/item/clothing/accessory/ranks/navy/e2 ranking = 1 /datum/paygrade/navy/e3 paygrade = PAY_SHORT_NE3 - name = "Seaman" - prefix = "SN." + name = "Aeroman 1st Class" + prefix = "1AEM." rank_pin = /obj/item/clothing/accessory/ranks/navy/e3 ranking = 2 /datum/paygrade/navy/e4 paygrade = PAY_SHORT_NE4 - name = "Petty Officer 3rd Class" - prefix = "PO3." + name = "Sergeant" + prefix = "Sgt." rank_pin = /obj/item/clothing/accessory/ranks/navy/e4 ranking = 3 /datum/paygrade/navy/e5 paygrade = PAY_SHORT_NE5 - name = "Petty Officer 2nd Class" - prefix = "PO2." + name = "Staff Sergeant" + prefix = "SSgt." rank_pin = /obj/item/clothing/accessory/ranks/navy/e5 ranking = 4 /datum/paygrade/navy/e6 paygrade = PAY_SHORT_NE6 - name = "Petty Officer 1st Class" - prefix = "PO1." + name = "Technical Sergeant" + prefix = "TSgt." rank_pin = /obj/item/clothing/accessory/ranks/navy/e6 ranking = 5 /datum/paygrade/navy/e7 paygrade = PAY_SHORT_NE7 - name = "Chief Petty Officer" - prefix = "CPO." + name = "Master Sergeant" + prefix = "MSgt." rank_pin = /obj/item/clothing/accessory/ranks/navy/e7 ranking = 6 /datum/paygrade/navy/e8 paygrade = PAY_SHORT_NE8 - name = "Senior Chief Petty Officer" - prefix = "SCPO." + name = "Senior Master Sergeant" + prefix = "SMSgt." rank_pin = /obj/item/clothing/accessory/ranks/navy/e8 ranking = 7 /datum/paygrade/navy/e8c paygrade = PAY_SHORT_NE8C - name = "Command Senior Chief Petty Officer" - prefix = "CSCPO." + name = "Command Senior Master Sergeant" + prefix = "CSM." rank_pin = /obj/item/clothing/accessory/ranks/navy/e8c ranking = 8 /datum/paygrade/navy/e9 paygrade = PAY_SHORT_NE9 - name = "Master Chief Petty Officer" - prefix = "MCPO." + name = "Chief Master Sergeant" + prefix = "CMSgt." rank_pin = /obj/item/clothing/accessory/ranks/navy/e9 ranking = 9 /datum/paygrade/navy/e9c paygrade = PAY_SHORT_NE9C - name = "Command Master Chief Petty Officer" - prefix = "CMCPO." + name = "Command Chief Master Sergeant Of The Aerospace Force" + prefix = "CCMSAF." rank_pin = /obj/item/clothing/accessory/ranks/navy/e9c ranking = 10 @@ -87,96 +87,96 @@ /datum/paygrade/navy/o1 paygrade = PAY_SHORT_NO1 - name = "Ensign" - prefix = "ENS." + name = "Second Lieutenant" + prefix = "2LT." rank_pin = /obj/item/clothing/accessory/ranks/navy/o1 ranking = 11 officer_grade = GRADE_OFFICER /datum/paygrade/navy/o2 paygrade = PAY_SHORT_NO2 - name = "Lieutenant Junior Grade" - prefix = "LTJG." + name = "First Lieutenant" + prefix = "1LT." rank_pin = /obj/item/clothing/accessory/ranks/navy/o2 ranking = 12 officer_grade = GRADE_OFFICER /datum/paygrade/navy/o3 paygrade = PAY_SHORT_NO3 - name = "Lieutenant" - prefix = "LT." + name = "Captain" + prefix = "Cpt." rank_pin = /obj/item/clothing/accessory/ranks/navy/o3 ranking = 13 officer_grade = GRADE_OFFICER /datum/paygrade/navy/o4 paygrade = PAY_SHORT_NO4 - name = "Lieutenant Commander" - prefix = "LCDR." + name = "Major" + prefix = "Maj." rank_pin = /obj/item/clothing/accessory/ranks/navy/o4 ranking = 14 officer_grade = GRADE_OFFICER /datum/paygrade/navy/o5 paygrade = PAY_SHORT_NO5 - name = "Commander" - prefix = "CDR." + name = "Lieutenant Colonel" + prefix = "LtCol." rank_pin = /obj/item/clothing/accessory/ranks/navy/o5 ranking = 15 officer_grade = GRADE_OFFICER /datum/paygrade/navy/o6 paygrade = PAY_SHORT_NO6 - name = "Captain" - prefix = "CAPT." + name = "Colonel" + prefix = "Col." rank_pin = /obj/item/clothing/accessory/ranks/navy/o6 ranking = 16 officer_grade = GRADE_OFFICER /datum/paygrade/navy/o6e paygrade = PAY_SHORT_NO6E - name = "Commodore" - prefix = "CDRE." + name = "Wing Commander" + prefix = "WCmd." rank_pin = /obj/item/clothing/accessory/ranks/navy/o6e ranking = 17 officer_grade = GRADE_OFFICER /datum/paygrade/navy/o6c paygrade = PAY_SHORT_NO6C - name = "Senior Commodore" - prefix = "Snr CDRE." + name = "Group Captain" + prefix = "GCpt" rank_pin = /obj/item/clothing/accessory/ranks/navy/o6e ranking = 18 officer_grade = GRADE_OFFICER /datum/paygrade/navy/o7 paygrade = PAY_SHORT_NO7 - name = "Rear Admiral (Lower Half)" - prefix = "RDML." + name = "Brigadier General" + prefix = "BrgGen." rank_pin = /obj/item/clothing/accessory/ranks/navy/o7 ranking = 19 officer_grade = GRADE_FLAG /datum/paygrade/navy/o8 paygrade = PAY_SHORT_NO8 - name = "Rear Admiral (Upper Half)" - prefix = "RADM." + name = "Major General" + prefix = "MajGen." rank_pin = /obj/item/clothing/accessory/ranks/navy/o8 ranking = 20 officer_grade = GRADE_FLAG /datum/paygrade/navy/o9 paygrade = PAY_SHORT_NO9 - name = "Vice Admiral" - prefix = "VADM." + name = "Lieutenant General" + prefix = "LtGen." rank_pin = /obj/item/clothing/accessory/ranks/navy/o9 ranking = 21 officer_grade = GRADE_FLAG /datum/paygrade/navy/o10 paygrade = PAY_SHORT_NO10 - name = "Admiral" - prefix = "ADM." + name = "General" + prefix = "Gen." rank_pin = /obj/item/clothing/accessory/ranks/navy/o10 ranking = 22 officer_grade = GRADE_FLAG diff --git a/code/datums/supply_packs/research.dm b/code/datums/supply_packs/research.dm index e81617357fb2..fa507d3de2d7 100644 --- a/code/datums/supply_packs/research.dm +++ b/code/datums/supply_packs/research.dm @@ -122,3 +122,21 @@ containertype = /obj/structure/closet/crate/secure/weyland containername = "IFF tag crate" group = "Research" + +/datum/supply_packs/ares_autodoc_check //same name as the proc for convenience of finding it :D + name = "ARES Emergency Autodoc Supplies" + contains = list( + /obj/item/research_upgrades/autodoc, // IB + /obj/item/research_upgrades/autodoc, // IB + /obj/item/research_upgrades/autodoc/tier2, // Bone Frac + /obj/item/research_upgrades/autodoc/tier2, // Bone Frac + /obj/item/research_upgrades/autodoc/tier3, // Organ + /obj/item/research_upgrades/autodoc/tier3, // Organ + /obj/item/research_upgrades/autodoc/tier4, // Larva + /obj/item/research_upgrades/autodoc/tier4, // Larva + ) + buyable = 0 + containertype = /obj/structure/closet/crate/secure/phoron + cost = 0 + group = "Research" + diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm index ccac5b44cf67..cd761a0de402 100644 --- a/code/datums/world_topic.dm +++ b/code/datums/world_topic.dm @@ -94,8 +94,8 @@ /datum/world_topic/playerlist/Run(list/input) . = ..() data = list() - for(var/client/C as() in GLOB.clients) - data += C.ckey + for(var/client/current as anything in GLOB.clients) + data += current.ckey statuscode = 200 response = "Player list fetched" diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 2310024e96fc..68ea2b137818 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -376,6 +376,9 @@ * Called from [/atom/movable/proc/keyLoop], this exists to be overwritten by living mobs with a check to see if we're actually alive enough to change directions */ /atom/movable/proc/keybind_face_direction(direction) + if(HAS_TRAIT(src, TRAIT_ABILITY_REFLECTIVE_PLATES)) + if(!do_after(src, 3 DECISECONDS, INTERRUPT_INCAPACITATED, BUSY_ICON_GENERIC)) + setDir(direction) setDir(direction) /atom/movable/proc/onTransitZ(old_z,new_z) diff --git a/code/game/gamemodes/colonialmarines/colonialmarines.dm b/code/game/gamemodes/colonialmarines/colonialmarines.dm index abe9b0e10913..414b00b001bb 100644 --- a/code/game/gamemodes/colonialmarines/colonialmarines.dm +++ b/code/game/gamemodes/colonialmarines/colonialmarines.dm @@ -158,6 +158,7 @@ addtimer(CALLBACK(src, PROC_REF(map_announcement)), 20 SECONDS) addtimer(CALLBACK(src, PROC_REF(start_lz_hazards)), DISTRESS_LZ_HAZARD_START) addtimer(CALLBACK(src, PROC_REF(ares_command_check)), 2 MINUTES) + addtimer(CALLBACK(src, PROC_REF(ares_autodoc_check)), 15 MINUTES) // 5 MINUTE LOBBY + 15 MINUTE DROPSHIP REFUEL addtimer(CALLBACK(SSentity_manager, TYPE_PROC_REF(/datum/controller/subsystem/entity_manager, select), /datum/entity/survivor_survival), 7 MINUTES) GLOB.chemical_data.reroll_chemicals() @@ -448,6 +449,26 @@ message_admins("[key_name(person_in_charge, TRUE)] [ADMIN_JMP_USER(person_in_charge)] has been designated the operation commander.") return +/datum/game_mode/proc/ares_autodoc_check() + var/list/surgery_roles = JOB_SURGERY_ROLES_LIST + var/surgeon_found = FALSE + for(var/mob/living/carbon/human/surgeon in GLOB.alive_human_list) + if(surgeon.job in surgery_roles) + surgeon_found = TRUE + break + if(!surgeon_found) + var/datum/supply_order/new_order = new() + new_order.ordernum = GLOB.supply_controller.ordernum++ + var/actual_type = GLOB.supply_packs_types["ARES Emergency Autodoc Supplies"] + new_order.objects = list(GLOB.supply_packs_datums[actual_type]) + new_order.orderedby = MAIN_AI_SYSTEM + new_order.approvedby = MAIN_AI_SYSTEM + GLOB.supply_controller.shoppinglist += new_order + for(var/obj/structure/machinery/medical_pod/autodoc/target in GLOB.machines) + if(is_mainship_level(target.z)) + target.skilllock = SKILL_SURGERY_DEFAULT // lowers skill-lock to 0 + ai_silent_announcement("WARNING: Cryopod release cycle DELAYED for MEDICAL PERSONNEL. Releasing Emergency Override Disks for AUTODOC Systems.", ".G", TRUE) + return log_admin("No Shipside Doctor found = Autodoc Upgrade Supplies ordered and AutoDoc skill locks released.") /datum/game_mode/colonialmarines/proc/ares_conclude() ai_silent_announcement("Bioscan complete. No unknown lifeform signature detected.", ".V") @@ -469,7 +490,7 @@ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(marine_announcement), "Almayer, this is the Tyrargo Museum civilian evacuation site. We are under assault by a XX-121 cluster, but we are holding our own.\n\nWe have heavy XX-121 waves inbound from the north-east and are under heavy suppression, our evacuation craft are pinned by long range boiler strikes and the western city exits are too dangerous to move towards with ground based evacuation vehicles, we’re requesting you secure the western approach so you can suppress the enemy forces to allow civilian evacuation, over.", "Tyrargo Civilian Evac, 1st Air Cav Headquarters", 'sound/AI/commandreport.ogg'), 15 MINUTES) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(xeno_announcement), "Be on guard my children. I have sensed that the petrid sewers of this so called city could be flooded by the hosts at a moments notice if the hosts restore power to the area. The button to release this putrid water is found in the metal structure the hosts call the sewer treatement plant.", "everything", QUEEN_MOTHER_ANNOUNCE), 15 MINUTES) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(marine_announcement), "Attention: Analysis of city layout plans have identified a possible tactical advantage. A release valve can be triggered within the City Sewer Treatment Plant, this valve will flood the lower sewer tunnels with water, expunging a significant amount of xenobiological growth.\n\nHowever, this valve must be powered by repairing a special APC located within the underground power-substation, located east of the underground sewer treatment plant.", "ARES 3.2 Strategic Notice", 'sound/AI/commandreport.ogg'), 20 MINUTES) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(marine_announcement), "Almayer. We’re seeing increased XX-121 activity at the Tyrargo evac site. Additional strains are inbound from the north.\n\nEnemy Boiler’s have moved close enough to suppress our air support, we’re re-orienting the Longstreet tanks to cover our flanks. Requesting immediate suppression of enemy forces near our location via the western city entrance, over. ", "Tyrargo Civilian Evac, 1st Air Cav Headquarters", 'sound/AI/commandreport.ogg'), 35 MINUTES) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(marine_announcement), "Almayer. We’re seeing increased XX-121 activity at the Tyrargo evac site. Additional strains are inbound from the north.\n\nEnemy Boilers have moved close enough to suppress our air support, we’re re-orienting the Longstreet tanks to cover our flanks. Requesting immediate suppression of enemy forces near our location via the western city entrance, over. ", "Tyrargo Civilian Evac, 1st Air Cav Headquarters", 'sound/AI/commandreport.ogg'), 35 MINUTES) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(marine_announcement), "All elements, more XX-121 clusters are encroaching from our east. We’re under heavy attack from all quarters and have lost half of our Longstreet tank support to Crushers.\n\nWe’ve exhausted our HEAP munitions and have had to switch to soft-point munitions. We can’t take this for much longer, requesting urgent support from Almayer forces, over.", "Tyrargo Civilian Evac, 1st Air Cav Headquarters", 'sound/AI/commandreport.ogg'), 60 MINUTES) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(marine_announcement), "This is Tyrargo. The xenos have begun to encroach from our southern flank. We only have a single tank left. We’re withdrawing to the middle corridor and have relocated the civilians to the inner perimeter.\n\nSituation is dire, we’re getting wasted. We need that support, over.", "Tyrargo Civilian Evac, 1st Air Cav Headquarters", 'sound/AI/commandreport.ogg'), 80 MINUTES) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(marine_announcement), "All elements! This is the Tyrargo evac site, our situation is critical. The bugs have us surrounded on all fronts, our armoured support is destroyed and we’re now being pinned by enemy Ravagers.\n\nWe need urgent fire support, we can’t take it much longer.", "Tyrargo Civilian Evac, 1st Air Cav Headquarters", 'sound/AI/commandreport.ogg'), 100 MINUTES) diff --git a/code/game/machinery/air_alarm.dm b/code/game/machinery/air_alarm.dm index 9189936be0ff..3b917baf393c 100644 --- a/code/game/machinery/air_alarm.dm +++ b/code/game/machinery/air_alarm.dm @@ -984,7 +984,7 @@ table tr:first-child th:first-child { border: none;} return if(1) - if(iscoil(W)) + if(iswire(W)) var/obj/item/stack/cable_coil/C = W if(C.use(5)) to_chat(user, SPAN_NOTICE("You wire \the [src].")) diff --git a/code/game/machinery/colony_floodlights.dm b/code/game/machinery/colony_floodlights.dm index c899b643453d..f6f02fefc737 100644 --- a/code/game/machinery/colony_floodlights.dm +++ b/code/game/machinery/colony_floodlights.dm @@ -283,7 +283,7 @@ GLOBAL_LIST_INIT(all_breaker_switches, list()) to_chat(user, SPAN_WARNING("You need more welding fuel to complete this task.")) return TRUE - else if(iscoil(I)) + else if(iswire(I)) var/obj/item/stack/cable_coil/coil = I if(!skillcheck(user, SKILL_ENGINEER, SKILL_ENGINEER_TRAINED)) to_chat(user, SPAN_WARNING("You have no clue how to repair [src].")) diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index f6ae18e6a339..6b6f9558c339 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -53,7 +53,7 @@ return switch(state) if(CONSTRUCTION_STATE_BEGIN) - if(iscoil(P)) + if(iswire(P)) if(!skillcheck(user, SKILL_CONSTRUCTION, required_skill)) to_chat(user, SPAN_WARNING("You are not trained to build machines...")) return diff --git a/code/game/machinery/kitchen/juicer.dm b/code/game/machinery/kitchen/juicer.dm index 935ca57555b2..2e1d61922c5d 100644 --- a/code/game/machinery/kitchen/juicer.dm +++ b/code/game/machinery/kitchen/juicer.dm @@ -162,7 +162,7 @@ break /obj/structure/machinery/juicer/yautja - name = "bone grinder" + name = "Gibs Juicer" icon = 'icons/obj/structures/machinery/yautja_machines.dmi' /obj/structure/closet/crate/juice diff --git a/code/game/objects/effects/effect_system/smoke.dm b/code/game/objects/effects/effect_system/smoke.dm index df33523d468a..25a1ce72d83a 100644 --- a/code/game/objects/effects/effect_system/smoke.dm +++ b/code/game/objects/effects/effect_system/smoke.dm @@ -47,7 +47,7 @@ src.time_to_live += rand(-1,1) var/area/my_area = get_area(src) - if(my_area.flags_area & AREA_HEAVILY_VENTILATED) + if(my_area?.flags_area & AREA_HEAVILY_VENTILATED) var/new_amount = rand(1,3) src.time_to_live = min(new_amount, src.time_to_live) diff --git a/code/game/objects/effects/landmarks/landmarks.dm b/code/game/objects/effects/landmarks/landmarks.dm index 324bbdbd5105..cb13dbe7656f 100644 --- a/code/game/objects/effects/landmarks/landmarks.dm +++ b/code/game/objects/effects/landmarks/landmarks.dm @@ -152,11 +152,16 @@ /obj/effect/landmark/lizard_spawn name = "lizard spawn" icon_state = "lizard_spawn" + var/lizard_path = /mob/living/simple_animal/hostile/retaliate/giant_lizard + +/obj/effect/landmark/lizard_spawn/bortrough + name = "bortrough spawn" + lizard_path = /mob/living/simple_animal/hostile/retaliate/giant_lizard/bortrough /obj/effect/landmark/lizard_spawn/Initialize(mapload, ...) . = ..() if(prob(66)) - new /mob/living/simple_animal/hostile/retaliate/giant_lizard(loc) + new lizard_path(loc) addtimer(CALLBACK(src, PROC_REF(latespawn_lizard)), rand(35 MINUTES, 50 MINUTES)) /obj/effect/landmark/lizard_spawn/proc/latespawn_lizard() @@ -170,7 +175,7 @@ continue addtimer(CALLBACK(src, PROC_REF(latespawn_lizard)), 1 MINUTES) return - new /mob/living/simple_animal/hostile/retaliate/giant_lizard(loc) + new lizard_path(loc) #undef MAXIMUM_LIZARD_AMOUNT diff --git a/code/game/objects/items/explosives/grenades/chem_grenade.dm b/code/game/objects/items/explosives/grenades/chem_grenade.dm index 9836f06b0dec..d239dc905197 100644 --- a/code/game/objects/items/explosives/grenades/chem_grenade.dm +++ b/code/game/objects/items/explosives/grenades/chem_grenade.dm @@ -209,10 +209,11 @@ var/obj/item/reagent_container/glass/beaker/B1 = new(src) var/obj/item/reagent_container/glass/beaker/B2 = new(src) - B1.reagents.add_reagent("potassium", 20) - B1.reagents.add_reagent("iron", 40) - B2.reagents.add_reagent("water", 20) - B2.reagents.add_reagent("iron", 40) + B1.reagents.add_reagent("cyclonite", 40) + B1.reagents.add_reagent("iron", 20) + B2.reagents.add_reagent("cyclonite", 20) + B2.reagents.add_reagent("anfo", 20) + B2.reagents.add_reagent("iron", 20) detonator = new/obj/item/device/assembly_holder/timer_igniter(src, 2) //~4 second timer @@ -233,10 +234,11 @@ var/obj/item/reagent_container/glass/beaker/B1 = new(src) var/obj/item/reagent_container/glass/beaker/B2 = new(src) - B1.reagents.add_reagent("potassium", 20) - B1.reagents.add_reagent("iron", 40) - B2.reagents.add_reagent("water", 20) - B2.reagents.add_reagent("iron", 30) + B1.reagents.add_reagent("octogen", 10) + B1.reagents.add_reagent("iron", 50) + B2.reagents.add_reagent("octogen", 10) + B2.reagents.add_reagent("iron", 10) + B2.reagents.add_reagent("ethanol", 30) B2.reagents.add_reagent("phoron", 10) detonator = new/obj/item/device/assembly_holder/timer_igniter(src, 2) //~4 second timer diff --git a/code/game/objects/items/frames/camera.dm b/code/game/objects/items/frames/camera.dm index c951e8dfeb5b..67f5765aa2d9 100644 --- a/code/game/objects/items/frames/camera.dm +++ b/code/game/objects/items/frames/camera.dm @@ -58,7 +58,7 @@ if(2) // State 2 - if(iscoil(W)) + if(iswire(W)) var/obj/item/stack/cable_coil/C = W if(C.use(2)) to_chat(user, SPAN_NOTICE("You add wires to the assembly.")) diff --git a/code/game/objects/items/tools/maintenance_tools.dm b/code/game/objects/items/tools/maintenance_tools.dm index 95437a42524c..a6f377e22c52 100644 --- a/code/game/objects/items/tools/maintenance_tools.dm +++ b/code/game/objects/items/tools/maintenance_tools.dm @@ -591,7 +591,12 @@ if(attacked_door.locked) //Bolted to_chat(user, SPAN_DANGER("You can't pry open [attacked_door] while it is bolted shut.")) return - + if(!attacked_door.density && !attacked_door.arePowerSystemsOn()) //If its open and unpowered + attacked_door.close(TRUE) + return + if(attacked_door.density && !attacked_door.arePowerSystemsOn()) // if its closed and unpowered + attacked_door.open(TRUE) + return if(requires_superstrength_pry) if(!HAS_TRAIT(user, TRAIT_SUPER_STRONG)) //basically IS_PRY_CAPABLE_CROWBAR return @@ -600,15 +605,8 @@ return if(user.action_busy) return - if(!attacked_door.density && !attacked_door.arePowerSystemsOn()) //If its open and unpowered - attacked_door.close(TRUE) - return - if(attacked_door.density && !attacked_door.arePowerSystemsOn()) // if its closed and unpowered - attacked_door.open(TRUE) - return if(!attacked_door.density) //If its open return - user.visible_message(SPAN_DANGER("[user] jams [src] into [attacked_door] and starts to pry it open."), SPAN_DANGER("You jam [src] into [attacked_door] and start to pry it open.")) playsound(src, "pry", 15, TRUE) diff --git a/code/game/objects/structures/barricade/barricade.dm b/code/game/objects/structures/barricade/barricade.dm index 20285725c564..38c0916ea634 100644 --- a/code/game/objects/structures/barricade/barricade.dm +++ b/code/game/objects/structures/barricade/barricade.dm @@ -221,9 +221,15 @@ return ..() -/obj/structure/barricade/handle_barrier_chance() +/obj/structure/barricade/handle_barrier_chance(mob/living/attacker) if(!anchored) return FALSE + + if(isxeno(attacker)) + var/mob/living/carbon/xenomorph/xeno = attacker + if(xeno.strain && istype(xeno.strain, /datum/xeno_strain/bulwark)) + return prob(25) //Bulwark can attack through wired cade with 75% chance. + return prob(max(30,(100.0*health)/maxhealth)) /obj/structure/barricade/attack_animal(mob/user as mob) diff --git a/code/game/turfs/walls/wall_types.dm b/code/game/turfs/walls/wall_types.dm index 0b3a6b272860..7ec978aef332 100644 --- a/code/game/turfs/walls/wall_types.dm +++ b/code/game/turfs/walls/wall_types.dm @@ -961,6 +961,7 @@ while(above && istransparentturf(above)) above.update_vis_contents() above = SSmapping.get_turf_above(above) + /turf/closed/wall/resin/process() . = ..() @@ -1044,10 +1045,12 @@ /turf/closed/wall/resin/above/Destroy(force) . = ..() if(wall_below) - wall_below.upper_wall = null //we should not get here naturaly + wall_below.upper_wall = null + wall_below.dismantle_wall() wall_below = null if(door_below) door_below.upper_wall = null + door_below.Dismantle(TRUE) door_below = null var/turf/above = SSmapping.get_turf_above(src) if(above && istransparentturf(above)) @@ -1477,8 +1480,11 @@ var/explosive_multiplier = 0.3 var/reflection_multiplier = 0.5 -/turf/closed/wall/resin/reflective/bullet_act(obj/projectile/P) - if(src in P.permutated) +/turf/closed/wall/resin/reflective/bullet_act(obj/projectile/proj_bullet) + if(proj_bullet.projectile_flags & PROJECTILE_REFLECTED) + return + + if(proj_bullet.ammo.flags_ammo_behavior & AMMO_NO_DEFLECT) return //Ineffective if someone is sitting on the wall @@ -1486,26 +1492,15 @@ return ..() if(!prob(chance_to_reflect)) - if(P.ammo.damage_type == BRUTE) - P.damage *= brute_multiplier + if(proj_bullet.ammo.damage_type == BRUTE) + proj_bullet.damage *= brute_multiplier return ..() - if(P.runtime_iff_group || P.ammo.flags_ammo_behavior & AMMO_NO_DEFLECT) - // Bullet gets absorbed if it has IFF or can't be reflected. - return - - var/obj/projectile/new_proj = new(src, construction_data ? construction_data : create_cause_data(initial(name))) - new_proj.generate_bullet(P.ammo) - new_proj.damage = P.damage * reflection_multiplier // don't make it too punishing - new_proj.accuracy = HIT_ACCURACY_TIER_7 // 35% chance to hit something - // Move back to who fired you. - RegisterSignal(new_proj, COMSIG_BULLET_PRE_HANDLE_TURF, PROC_REF(bullet_ignore_turf)) - new_proj.permutated |= src + var/atom/target = proj_bullet.firer + if(!target) + return ..() - var/angle = Get_Angle(src, P.firer) + rand(30, -30) - var/atom/target = get_angle_target_turf(src, angle, get_dist(src, P.firer)) - new_proj.projectile_flags |= PROJECTILE_SHRAPNEL - new_proj.fire_at(target, P.firer, src, reflect_range, speed = P.ammo.shell_speed) + proj_bullet.reflect_projectile_at_firer(src, proj_bullet, proj_bullet.firer, target, damage_multiplier = reflection_multiplier, accuracy_override = HIT_ACCURACY_TIER_7, range_override = reflect_range, angle_variance = 30) return TRUE diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index ed59ea2fad01..c049dc10bddb 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -166,7 +166,7 @@ GLOBAL_LIST_EMPTY(admin_ranks) //list of all ranks with associated rights protection = CONFIG_ENTRY_LOCKED /datum/config_entry/string/cmdb_api_key - protection = CONFIG_ENTRY_HIDDEN | CONFIG_ENTRY_LOCKED + protection = CONFIG_ENTRY_HIDDEN|CONFIG_ENTRY_LOCKED|CONFIG_ENTRY_SENSITIVE /** * Using the API backed admins/admin_ranks requires a response from the endpoint following this schema: diff --git a/code/modules/admin/tabs/admin_tab.dm b/code/modules/admin/tabs/admin_tab.dm index 3aa5f5fd333e..d82dbdd66830 100644 --- a/code/modules/admin/tabs/admin_tab.dm +++ b/code/modules/admin/tabs/admin_tab.dm @@ -257,6 +257,7 @@ cmd_admin_say(msg) +SET_PROTECTED_PROC(/client/proc/cmd_admin_say) /client/proc/cmd_admin_say(msg as text) set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite set category = "Admin" @@ -385,6 +386,7 @@ var/msg = input(src, null, "asay \"text\"") as text|null cmd_admin_say(msg) +SET_PROTECTED_PROC(/client/proc/cmd_mentor_say) /client/proc/cmd_mentor_say(msg as text) set name = "MentorSay" set category = "Admin.Mentor" diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 935e1a5a2a68..1ad555c40590 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -163,6 +163,8 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) #define WEBHOOK_URGENT 1 #define WEBHOOK_NON_URGENT 2 +SET_PROTECTED_DATUM(/datum/admin_help) + /** * # Adminhelp Ticket */ @@ -804,6 +806,8 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) GLOBAL_DATUM_INIT(admin_help_ui_handler, /datum/admin_help_ui_handler, new) +SET_PROTECTED_DATUM(/datum/admin_help_ui_handler) + /datum/admin_help_ui_handler var/list/ahelp_cooldowns = list() diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 2d3513510c5d..5d05c9fce24d 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -97,6 +97,7 @@ return cmd_admin_pm(whom, msg) +SET_PROTECTED_PROC(/client/proc/cmd_admin_pm) //takes input from cmd_admin_pm_context, cmd_admin_pm_panel or /client/Topic and sends them a PM. //Fetching a message if needed. src is the sender and C is the target client /client/proc/cmd_admin_pm(whom, msg) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index d3bc85d221e9..7e1c788f6b33 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -162,7 +162,7 @@ cur_x = min(cur_x, width_inside) if(cur_y == height_inside) break - cur_x = half_chunk_size + cur_x = half_chunk_size + offset_x cur_y += chunk_size cur_y = min(cur_y, height_inside) @@ -183,7 +183,7 @@ mob.hud_used.show_hud(HUD_STYLE_STANDARD) mob.animate_movement = SLIDE_STEPS // Initial is incorrect - to_chat(usr, "Provide these values when asked for the MapTileImageTool: [width] [height] [half_chunk_size] [world.icon_size]") + to_chat(mob, "Provide these values when asked for the MapTileImageTool: [width] [height] [half_chunk_size] [world.icon_size]") //TODO: merge the vievars version into this or something maybe mayhaps /client/proc/cmd_debug_del_all() diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index c5110033ba16..2f83e1c3be34 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -553,6 +553,15 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list( SEND_GLOBAL_SIGNAL(COMSIG_GLOB_CLIENT_LOGGED_IN, src) SEND_SIGNAL(src, COMSIG_CLIENT_LOGGED_IN) + GLOB.all_player_keys |= key + GLOB.all_player_keys_regex = regex(jointext(GLOB.all_player_keys, "|"), "g") + + GLOB.all_player_ckeys |= ckey + GLOB.all_player_ckeys_regex = regex(jointext(GLOB.all_player_ckeys, "|"), "g") + + GLOB.all_player_cids |= computer_id + GLOB.all_player_cids_regex = regex(jointext(GLOB.all_player_cids, "|"), "g") + if(CONFIG_GET(flag/ooc_country_flags)) spawn if(src) ip2country(address, src) diff --git a/code/modules/client/preferences_gear.dm b/code/modules/client/preferences_gear.dm index a51820fc72c9..0594decac440 100644 --- a/code/modules/client/preferences_gear.dm +++ b/code/modules/client/preferences_gear.dm @@ -224,6 +224,36 @@ GLOBAL_LIST_EMPTY(roles_with_gear) display_name = "Sunglasses" path = /obj/item/clothing/glasses/sunglasses +// Hippie Shades + +/datum/gear/eyewear/sunglasses/hippie_shades + display_name = "Suntex-Sightware rounded shades, pink" + path = /obj/item/clothing/glasses/sunglasses/hippie + +/datum/gear/eyewear/sunglasses/hippie_shades/green + display_name = "Suntex-Sightware rounded shades, green" + path = /obj/item/clothing/glasses/sunglasses/hippie/green + +/datum/gear/eyewear/sunglasses/hippie_shades/sunrise + display_name = "Suntex-Sightware rounded shades, sunrise" + path = /obj/item/clothing/glasses/sunglasses/hippie/sunrise + +/datum/gear/eyewear/sunglasses/hippie_shades/sunset + display_name = "Suntex-Sightware rounded shades, sunset" + path = /obj/item/clothing/glasses/sunglasses/hippie/sunset + +/datum/gear/eyewear/sunglasses/hippie_shades/nightblue + display_name = "Suntex-Sightware rounded shades, nightblue" + path = /obj/item/clothing/glasses/sunglasses/hippie/nightblue + +/datum/gear/eyewear/sunglasses/hippie_shades/midnight + display_name = "Suntex-Sightware rounded shades, midnight" + path = /obj/item/clothing/glasses/sunglasses/hippie/midnight + +/datum/gear/eyewear/sunglasses/hippie_shades/bloodred + display_name = "Suntex-Sightware rounded shades, bloodred" + path = /obj/item/clothing/glasses/sunglasses/hippie/bloodred + /datum/gear/mask category = "Masks and scarves" slot = WEAR_FACE @@ -1362,36 +1392,6 @@ GLOBAL_LIST_EMPTY(roles_with_gear) display_name = "cowboy hat, light-brown" path = /obj/item/clothing/head/cowboy/light -// Hippie Shades - -/datum/gear/eyewear/sunglasses/hippie_shades/pink - display_name = "Suntex-Sightware rounded shades, pink" - path = /obj/item/clothing/glasses/sunglasses/hippie - -/datum/gear/eyewear/sunglasses/hippie_shades/green - display_name = "Suntex-Sightware rounded shades, green" - path = /obj/item/clothing/glasses/sunglasses/hippie/green - -/datum/gear/eyewear/sunglasses/hippie_shades/sunrise - display_name = "Suntex-Sightware rounded shades, sunrise" - path = /obj/item/clothing/glasses/sunglasses/hippie/sunrise - -/datum/gear/eyewear/sunglasses/hippie_shades/sunset - display_name = "Suntex-Sightware rounded shades, sunset" - path = /obj/item/clothing/glasses/sunglasses/hippie/sunset - -/datum/gear/eyewear/sunglasses/hippie_shades/nightblue - display_name = "Suntex-Sightware rounded shades, nightblue" - path = /obj/item/clothing/glasses/sunglasses/hippie/nightblue - -/datum/gear/eyewear/sunglasses/hippie_shades/midnight - display_name = "Suntex-Sightware rounded shades, midnight" - path = /obj/item/clothing/glasses/sunglasses/hippie/midnight - -/datum/gear/eyewear/sunglasses/hippie_shades/bloodred - display_name = "Suntex-Sightware rounded shades, bloodred" - path = /obj/item/clothing/glasses/sunglasses/hippie/bloodred - // Civilian shoes /datum/gear/civilian/shoes diff --git a/code/modules/clothing/under/rank_pins.dm b/code/modules/clothing/under/rank_pins.dm index e988b8cf8f1d..e46279a077de 100644 --- a/code/modules/clothing/under/rank_pins.dm +++ b/code/modules/clothing/under/rank_pins.dm @@ -297,111 +297,125 @@ ################################################*/ //ENLISTED /obj/item/clothing/accessory/ranks/navy/e1 + name = "rank shoulder patch" rank_short = PAY_SHORT_NE1 + icon_state = "ranks_ne1" /obj/item/clothing/accessory/ranks/navy/e2 + name = "rank shoulder patch" rank_short = PAY_SHORT_NE2 + icon_state = "ranks_ne2" /obj/item/clothing/accessory/ranks/navy/e3 + name = "rank shoulder patch" rank_short = PAY_SHORT_NE3 + icon_state = "ranks_ne3" /obj/item/clothing/accessory/ranks/navy/e4 + name = "rank shoulder patch" rank_short = PAY_SHORT_NE4 - icon_state = "ranks_nco" + icon_state = "ranks_ne4" /obj/item/clothing/accessory/ranks/navy/e5 + name = "rank shoulder patch" rank_short = PAY_SHORT_NE5 - icon_state = "ranks_nco" + icon_state = "ranks_ne5" /obj/item/clothing/accessory/ranks/navy/e6 + name = "rank shoulder patch" rank_short = PAY_SHORT_NE6 - icon_state = "ranks_nco" + icon_state = "ranks_ne6" /obj/item/clothing/accessory/ranks/navy/e7 + name = "rank shoulder patch" rank_short = PAY_SHORT_NE7 - icon_state = "ranks_snco" + icon_state = "ranks_ne7" /obj/item/clothing/accessory/ranks/navy/e8 + name = "rank shoulder patch" rank_short = PAY_SHORT_NE8 - icon_state = "ranks_snco" + icon_state = "ranks_ne8" /obj/item/clothing/accessory/ranks/navy/e8c + name = "rank shoulder patch" rank_short = PAY_SHORT_NE8C - icon_state = "ranks_snco" + icon_state = "ranks_ne8" /obj/item/clothing/accessory/ranks/navy/e9 + name = "rank shoulder patch" rank_short = PAY_SHORT_NE9 - icon_state = "ranks_snco" + icon_state = "ranks_ne9" /obj/item/clothing/accessory/ranks/navy/e9c + name = "rank shoulder patch" rank_short = PAY_SHORT_NE9C - icon_state = "ranks_snco" + icon_state = "ranks_ne9" //OFFICERS /obj/item/clothing/accessory/ranks/navy/o1 name = "rank boards" rank_short = PAY_SHORT_NO1 - icon_state = "ranks_officer" + icon_state = "ranks_no1" /obj/item/clothing/accessory/ranks/navy/o2 name = "rank boards" rank_short = PAY_SHORT_NO2 - icon_state = "ranks_officer" + icon_state = "ranks_no2" /obj/item/clothing/accessory/ranks/navy/o3 name = "rank boards" rank_short = PAY_SHORT_NO3 - icon_state = "ranks_officer" + icon_state = "ranks_no3" /obj/item/clothing/accessory/ranks/navy/o4 name = "rank boards" rank_short = PAY_SHORT_NO4 - icon_state = "ranks_seniorofficer" + icon_state = "ranks_no4" /obj/item/clothing/accessory/ranks/navy/o5 name = "rank boards" rank_short = PAY_SHORT_NO5 - icon_state = "ranks_seniorofficer" + icon_state = "ranks_no5" /obj/item/clothing/accessory/ranks/navy/o6 name = "rank boards" rank_short = PAY_SHORT_NO6 - icon_state = "ranks_seniorofficer" + icon_state = "ranks_no6" /obj/item/clothing/accessory/ranks/navy/o6e name = "rank boards" rank_short = PAY_SHORT_NO6E - icon_state = "ranks_seniorofficer" + icon_state = "ranks_no6" /obj/item/clothing/accessory/ranks/navy/o6c name = "rank boards" rank_short = PAY_SHORT_NO6C - icon_state = "ranks_seniorofficer" + icon_state = "ranks_no6" /obj/item/clothing/accessory/ranks/navy/o7 name = "rank boards" rank_short = PAY_SHORT_NO7 - icon_state = "ranks_flagofficer" + icon_state = "ranks_no7" /obj/item/clothing/accessory/ranks/navy/o8 name = "rank boards" rank_short = PAY_SHORT_NO8 - icon_state = "ranks_flagofficer" + icon_state = "ranks_no8" /obj/item/clothing/accessory/ranks/navy/o9 name = "rank boards" rank_short = PAY_SHORT_NO9 - icon_state = "ranks_flagofficer" + icon_state = "ranks_no9" /obj/item/clothing/accessory/ranks/navy/o10 name = "rank boards" rank_short = PAY_SHORT_NO10 - icon_state = "ranks_flagofficer" + icon_state = "ranks_no10" /obj/item/clothing/accessory/ranks/navy/o10c name = "rank boards" rank_short = PAY_SHORT_NO10C - icon_state = "ranks_flagofficer" + icon_state = "ranks_no10" /*################################################ ################# SPECIAL ################# ################################################*/ diff --git a/code/modules/cm_aliens/structures/special_structure.dm b/code/modules/cm_aliens/structures/special_structure.dm index 00ec478a2614..ee4e075956a7 100644 --- a/code/modules/cm_aliens/structures/special_structure.dm +++ b/code/modules/cm_aliens/structures/special_structure.dm @@ -23,6 +23,23 @@ /// Tells the structure if they are being deleted because of hijack var/hijack_delete = FALSE +/obj/effect/alien/resin/special/get_examine_text(mob/user) + . = ..() + if(isxeno(user) || isobserver(user)) + var/health_portion = health/maxhealth + switch(health_portion) + if(0 to 0.25) + . += SPAN_WARNING("[src] is about to fall apart!") + return + if(0.25 to 0.5) + . += SPAN_WARNING("[src] looks severely damaged!") + return + if(0.5 to 0.75) + . += ("[src] is slightly damaged.") + return + else + . += ("[src] is healthy.") + /obj/effect/alien/resin/special/Initialize(mapload, hive_ref) . = ..() maxhealth = health diff --git a/code/modules/cm_marines/dropship_ammo.dm b/code/modules/cm_marines/dropship_ammo.dm index c4048302bdb5..2a46e12a2b44 100644 --- a/code/modules/cm_marines/dropship_ammo.dm +++ b/code/modules/cm_marines/dropship_ammo.dm @@ -261,12 +261,12 @@ /obj/structure/ship_ammo/laser_battery/detonate_on(turf/impact, obj/structure/dropship_equipment/weapon/fired_from) set waitfor = 0 - var/list/turf_list = RANGE_TURFS(3, impact) //This is its area of effect + var/list/turf_list = shuffle(RANGE_TURFS(3, impact)) //This is its area of effect playsound(impact, 'sound/effects/pred_vision.ogg', 20, 1) - for(var/i=1 to 16) //This is how many tiles within that area of effect will be randomly ignited - var/turf/U = pick(turf_list) - turf_list -= U - fire_spread_recur(U, create_cause_data(fired_from.name, source_mob), 1, null, 5, 75, "#EE6515")//Very, very intense, but goes out very quick + var/datum/reagent/fire_reagent = create_fire_reagent(5, 75, "#EE6515") + for(var/i in 1 to 16) //This is how many tiles within that area of effect will be randomly ignited + var/turf/target_turf = turf_list[i] + fire_spread(target_turf, create_cause_data(fired_from.name, source_mob), 1, fire_reagent)//Very, very intense, but goes out very quick if(!ammo_count && !QDELETED(src)) qdel(src) //deleted after last laser beam is fired and impact the ground. @@ -316,8 +316,9 @@ /obj/structure/ship_ammo/rocket/banshee/detonate_on(turf/impact, obj/structure/dropship_equipment/weapon/fired_from) impact.ceiling_debris_check(3) + var/datum/reagent/fire_reagent = create_fire_reagent(15, 50, "#00b8ff") addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cell_explosion), impact, 175, 20, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, create_cause_data(initial(name), source_mob)), 0.5 SECONDS) //Small explosive power with a small fall off for a big explosion range - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fire_spread), impact, create_cause_data(initial(name), source_mob), 4, 15, 50, "#00b8ff"), 0.5 SECONDS) //Very intense but the fire doesn't last very long + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fire_spread), impact, create_cause_data(initial(name), source_mob), 4, fire_reagent), 0.5 SECONDS) //Very intense but the fire doesn't last very long QDEL_IN(src, 0.5 SECONDS) /obj/structure/ship_ammo/rocket/keeper @@ -357,8 +358,9 @@ /obj/structure/ship_ammo/rocket/napalm/detonate_on(turf/impact, obj/structure/dropship_equipment/weapon/fired_from) impact.ceiling_debris_check(3) + var/datum/reagent/fire_reagent = create_fire_reagent(60, 30, "#EE6515") addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cell_explosion), impact, 200, 25, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, create_cause_data(initial(name), source_mob)), 0.5 SECONDS) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fire_spread), impact, create_cause_data(initial(name), source_mob), 6, 60, 30, "#EE6515"), 0.5 SECONDS) //Color changed into napalm's color to better convey how intense the fire actually is. + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fire_spread), impact, create_cause_data(initial(name), source_mob), 6, fire_reagent), 0.5 SECONDS) //Color changed into napalm's color to better convey how intense the fire actually is. QDEL_IN(src, 0.5 SECONDS) /obj/structure/ship_ammo/rocket/thermobaric @@ -371,7 +373,8 @@ /obj/structure/ship_ammo/rocket/thermobaric/detonate_on(turf/impact, obj/structure/dropship_equipment/weapon/fired_from) impact.ceiling_debris_check(3) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fire_spread), impact, create_cause_data(initial(name), source_mob), 4, 25, 50, "#c96500"), 0.5 SECONDS) //Very intense but the fire doesn't last very long + var/datum/reagent/fire_reagent = create_fire_reagent(25, 50, "#c96500") + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fire_spread), impact, create_cause_data(initial(name), source_mob), 4, fire_reagent), 0.5 SECONDS) //Very intense but the fire doesn't last very long for(var/mob/living/carbon/victim in orange(5, impact)) victim.throw_atom(impact, 3, 15, src, TRUE) // Implosion throws affected towards center of vacuum QDEL_IN(src, 0.5 SECONDS) @@ -425,7 +428,8 @@ /obj/structure/ship_ammo/minirocket/incendiary/detonate_on(turf/impact, obj/structure/dropship_equipment/weapon/fired_from) ..() spawn(5) - fire_spread(impact, create_cause_data(initial(name), source_mob), 3, 25, 20, "#EE6515") + var/datum/reagent/fire_reagent = create_fire_reagent(25, 20, "#EE6515") + fire_spread(impact, create_cause_data(initial(name), source_mob), 3, fire_reagent) /obj/structure/ship_ammo/sentry name = "\improper A/C-49-P Air Deployable Sentry" diff --git a/code/modules/cm_marines/orbital_cannon.dm b/code/modules/cm_marines/orbital_cannon.dm index c9de051fc7a6..0bd03ff08f60 100644 --- a/code/modules/cm_marines/orbital_cannon.dm +++ b/code/modules/cm_marines/orbital_cannon.dm @@ -532,7 +532,8 @@ GLOBAL_LIST_EMPTY(orbital_cannon_cancellation) handle_ob_shake(target) sleep(clear_delay) - fire_spread(target, cause_data, distance, fire_level, burn_level, fire_color, fire_type, TURF_PROTECTION_OB) + var/datum/reagent/fire_reagent = create_fire_reagent(fire_level, burn_level, fire_color, fire_type) + fire_spread(target, cause_data, distance, fire_reagent, TURF_PROTECTION_OB) qdel(src) /obj/structure/ob_ammo/warhead/cluster diff --git a/code/modules/cm_marines/overwatch.dm b/code/modules/cm_marines/overwatch.dm index 40a92c1087e8..01d277572ee7 100644 --- a/code/modules/cm_marines/overwatch.dm +++ b/code/modules/cm_marines/overwatch.dm @@ -479,6 +479,7 @@ GLOBAL_LIST_EMPTY_TYPED(active_overwatch_consoles, /obj/structure/machinery/comp /obj/structure/machinery/computer/overwatch/ui_data(mob/user) var/list/data = list() + data = pack_radio_data() data["theme"] = ui_theme if(!current_squad) @@ -527,6 +528,8 @@ GLOBAL_LIST_EMPTY_TYPED(active_overwatch_consoles, /obj/structure/machinery/comp data["theme"] = ui_theme + data = pack_radio_data() + if(!current_squad) data["squad_list"] = list() for(var/datum/squad/current_squad in GLOB.RoleAuthority.squads) @@ -1513,16 +1516,17 @@ GLOBAL_LIST_EMPTY_TYPED(active_overwatch_consoles, /obj/structure/machinery/comp plane_controller.add_filter("overwatch_overlay5", 7, layering_filter(x = 480, y = 0, color=overlay_color, icon = overlay_icon, blend_mode = BLEND_INSET_OVERLAY)) plane_controller.add_filter("overwatch_overlay6", 8, layering_filter(x = 480, y = 480, color=overlay_color, icon = overlay_icon, blend_mode = BLEND_INSET_OVERLAY)) - RegisterSignal(watcher, COMSIG_CLIENT_RESET_VIEW, PROC_REF(clear_overwatch_overlay), watcher) + RegisterSignal(watcher.client, COMSIG_CLIENT_RESET_VIEW, PROC_REF(clear_overwatch_overlay), TRUE) /obj/structure/machinery/computer/overwatch/proc/stop_watching_camera(mob/watcher, atom/target) watcher.reset_view(null) // This will call the below proc via the above registered signal //Why so complicated? Many things may reset our view (resisting being the most common one) -/obj/structure/machinery/computer/overwatch/proc/clear_overwatch_overlay(mob/watcher) +/obj/structure/machinery/computer/overwatch/proc/clear_overwatch_overlay(client/watcher) + SIGNAL_HANDLER UnregisterSignal(watcher, COMSIG_CLIENT_RESET_VIEW) - set_onscreen_text(watcher, null) - var/atom/movable/plane_master_controller/non_master/plane_controller = watcher.hud_used.plane_master_controllers[PLANE_MASTERS_NON_MASTER] + set_onscreen_text(watcher.mob, null) + var/atom/movable/plane_master_controller/non_master/plane_controller = watcher.mob.hud_used.plane_master_controllers[PLANE_MASTERS_NON_MASTER] if(!plane_controller) return @@ -1683,6 +1687,32 @@ GLOBAL_LIST_EMPTY_TYPED(active_overwatch_consoles, /obj/structure/machinery/comp icon_state = "deltadrop" squad = SQUAD_UPP_4 +/obj/structure/machinery/computer/overwatch/proc/get_radio_clarity() + var/ground_z = length(SSmapping.levels_by_trait(ZTRAIT_GROUND)) ? SSmapping.levels_by_trait(ZTRAIT_GROUND)[1] : null + var/current_clarity + if(ground_z && (ground_z in SSradio.get_available_tcomm_zs(COMM_FREQ))) + return 100 + if(SSradio.faction_coms_clarity && SSradio.faction_coms_clarity[faction]) + current_clarity = SSradio.faction_coms_clarity[faction] + if(SSradio.faction_coms_codes && length(SSradio.faction_coms_codes[faction])) + return current_clarity + return 15 + +/obj/structure/machinery/computer/overwatch/proc/pack_radio_data() + var/list/clarity_data = list () + var/clarity = get_radio_clarity() + clarity_data["radio_clarity"] = clarity + if(clarity >= 80) + clarity_data["clarity_color"] = "good" + clarity_data["clarity_status"] = "STABLE" + else if(clarity >= 45) + clarity_data["clarity_color"] = "average" + clarity_data["clarity_status"] = "DEGRADED" + else + clarity_data["clarity_color"] = "bad" + clarity_data["clarity_status"] = "CRITICAL BLACKOUT" + return clarity_data + #undef HIDE_ALMAYER #undef HIDE_GROUND #undef HIDE_NONE diff --git a/code/modules/cm_marines/smartgun_mount.dm b/code/modules/cm_marines/smartgun_mount.dm index a71dc8082051..01b17c45b8bf 100644 --- a/code/modules/cm_marines/smartgun_mount.dm +++ b/code/modules/cm_marines/smartgun_mount.dm @@ -400,6 +400,12 @@ if(istype(O,/obj/item/device/m56d_gun)) //lets mount the MG onto the mount. var/obj/item/device/m56d_gun/MG = O + if(gun_mounted) + to_chat(user, SPAN_WARNING("There is already a gun mounted to this tripod!")) + return + if(MG.has_mount) + to_chat(user, SPAN_WARNING("The gun you're trying to attach already has a mount!")) + return for(var/obj/structure/machinery/machine in long_orange(MG.defense_check_range, loc)) if(istype(machine, /obj/structure/machinery/m56d_hmg) || istype(machine, /obj/structure/machinery/m56d_post)) to_chat(user, SPAN_WARNING("This is too close to [machine]!")) diff --git a/code/modules/cm_preds/yaut_items.dm b/code/modules/cm_preds/yaut_items.dm index 2d25953291cf..820d6105d6b7 100644 --- a/code/modules/cm_preds/yaut_items.dm +++ b/code/modules/cm_preds/yaut_items.dm @@ -1598,6 +1598,11 @@ GLOBAL_VAR_INIT(youngblood_timer_yautja, 0) icon = 'icons/obj/items/hunter/prey_items.dmi' unacidable = TRUE +/obj/item/skull/Initialize(mapload, ...) + . = ..() + if(!icon_state) + return INITIALIZE_HINT_QDEL + /obj/item/skull/queen name = "Queen skull" desc = "Skull of a prime hive ruler, mother to many." @@ -1699,6 +1704,16 @@ GLOBAL_VAR_INIT(youngblood_timer_yautja, 0) desc = "Skull of a highly acidic xenomorph, a venomous ranged attacker." icon_state = "spitter_skull" +/obj/item/skull/abomination + name = "Abomination skull" + desc = "Skull of a mysterious hybrid xenomorph, a horror on the field." + icon_state = "predalien_skull" + +/obj/item/skull/abomination/get_examine_text(mob/user) + . = ..() + if(isyautja(user)) + . += SPAN_RED("Not even this relic can be tolerated. Destroy it.") + // PELTS /obj/item/pelt @@ -1706,6 +1721,11 @@ GLOBAL_VAR_INIT(youngblood_timer_yautja, 0) icon = 'icons/obj/items/hunter/prey_items.dmi' unacidable = TRUE +/obj/item/pelt/Initialize(mapload, ...) + . = ..() + if(!icon_state) + return INITIALIZE_HINT_QDEL + /obj/item/pelt/queen name = "Queen pelt" desc = "The pelt of a prime hive ruler, mother to many." @@ -1811,6 +1831,16 @@ GLOBAL_VAR_INIT(youngblood_timer_yautja, 0) desc = "The hide of a juvenile Xenomorph, a grim trophy from a fledgling that never reached its full potential." icon_state = "larva_pelt" +/obj/item/pelt/abomination + name = "Abomination pelt" + desc = "The pelt of a mysterious hybrid xenomorph, a horror on the field." + icon_state = "predalien_pelt" + +/obj/item/pelt/abomination/get_examine_text(mob/user) + . = ..() + if(isyautja(user)) + . += SPAN_RED("Not even this relic can be tolerated. Destroy it.") + /// TOOLS /obj/item/tool/crowbar/yautja diff --git a/code/modules/cm_tech/hologram.dm b/code/modules/cm_tech/hologram.dm index 7edd7593ef48..cc6b1f1301f9 100644 --- a/code/modules/cm_tech/hologram.dm +++ b/code/modules/cm_tech/hologram.dm @@ -183,7 +183,7 @@ GLOBAL_LIST_EMPTY_TYPED(hologram_list, /mob/hologram) var/turf/new_turf = get_step(loc, direct) forceMove(new_turf) - if(!istype(new_turf, /turf/open_space)) + if(!istransparentturf(new_turf)) UnregisterSignal(linked_mob, COMSIG_MOB_RESET_VIEW) view_registered = FALSE linked_mob.reset_view() diff --git a/code/modules/gear_presets/clf.dm b/code/modules/gear_presets/clf.dm index 754176846c53..de5985a04e8b 100644 --- a/code/modules/gear_presets/clf.dm +++ b/code/modules/gear_presets/clf.dm @@ -72,7 +72,7 @@ spawn_rebel_shoes(new_human) spawn_rebel_gloves(new_human) new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar, WEAR_IN_JACKET) - new_human.equip_to_slot_or_del(new /obj/item/device/flashlight(new_human), WEAR_L_STORE) + new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/combat(new_human), WEAR_L_STORE) new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert(new_human), WEAR_R_STORE) new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/clf_patch, WEAR_ACCESSORY) @@ -97,7 +97,7 @@ list("CLF Head Gear (Random)", 0, /obj/effect/essentials_set/random/clf_head, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY), list("Headset", 0, /obj/item/device/radio/headset/distress/CLF, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY), list("Makeshift Meal", 0, /obj/item/mre_food_packet/clf, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY), - list("Flashlight", 0, /obj/item/device/flashlight, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), + list("Flashlight", 0, /obj/item/device/flashlight/combat, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), list("Combat Pack", 0, /obj/item/storage/backpack/lightpack, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY), list("POUCHES (CHOOSE 2)", 0, null, null, null), @@ -194,7 +194,7 @@ new_human.equip_to_slot_or_del(new /obj/item/explosive/plastic, WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/explosive/grenade/incendiary/molotov, WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/mre_food_packet/clf, WEAR_IN_BACK) - new_human.equip_to_slot_or_del(new /obj/item/device/flashlight, WEAR_IN_BACK) + new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/combat, WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/construction/low_grade_full, WEAR_L_STORE) new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert, WEAR_R_STORE) new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/clf_patch, WEAR_ACCESSORY) @@ -211,7 +211,7 @@ list("Insulated Gloves", 0, /obj/item/clothing/gloves/yellow, MARINE_CAN_BUY_GLOVES, VENDOR_ITEM_MANDATORY), list("Headset", 0, /obj/item/device/radio/headset/distress/CLF/cct, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY), list("Makeshift Meal", 0, /obj/item/mre_food_packet/clf, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY), - list("Flashlight", 0, /obj/item/device/flashlight, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), + list("Flashlight", 0, /obj/item/device/flashlight/combat, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), list("Welderpack", 0, /obj/item/storage/backpack/marine/engineerpack/ert, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY), list("BELT (CHOOSE 1)", 0, null, null, null), @@ -329,7 +329,7 @@ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/hud/health/prescription(new_human), WEAR_EYES) else new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/hud/health(new_human), WEAR_EYES) - new_human.equip_to_slot_or_del(new /obj/item/device/flashlight(new_human), WEAR_L_STORE) + new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/combat(new_human), WEAR_L_STORE) new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/magazine/large(new_human), WEAR_R_STORE) spawn_rebel_smg(new_human) @@ -345,7 +345,7 @@ list("Headset", 0, /obj/item/device/radio/headset/distress/CLF/medic, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY), list("Medical HUD Glasses", 0, /obj/item/clothing/glasses/hud/health, MARINE_CAN_BUY_GLASSES, VENDOR_ITEM_MANDATORY), list("Makeshift Meal", 0, /obj/item/mre_food_packet/clf, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY), - list("Flashlight", 0, /obj/item/device/flashlight, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), + list("Flashlight", 0, /obj/item/device/flashlight/combat, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), list("Combat Pack", 0, /obj/item/storage/backpack/lightpack, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY), list("BELT (CHOOSE 1)", 0, null, null, null), @@ -514,7 +514,7 @@ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/CLF/cct(new_human), WEAR_L_EAR) //standard backpack stuff new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack(new_human), WEAR_BACK) - new_human.equip_to_slot_or_del(new /obj/item/device/flashlight(new_human), WEAR_IN_BACK) + new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/combat(new_human), WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/regular/response(new_human), WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar(new_human), WEAR_IN_BACK) //specialist backpack stuff @@ -546,7 +546,7 @@ list("SWAT Helmet", 0, /obj/item/clothing/head/helmet/swat, MARINE_CAN_BUY_HELMET, VENDOR_ITEM_MANDATORY), list("Headset", 0, /obj/item/device/radio/headset/distress/CLF/cct, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY), list("Makeshift Meal", 0, /obj/item/mre_food_packet/clf, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY), - list("Flashlight", 0, /obj/item/device/flashlight, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), + list("Flashlight", 0, /obj/item/device/flashlight/combat, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), list("Combat Pack", 0, /obj/item/storage/backpack/lightpack, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY), list("POUCHES (CHOOSE 2)", 0, null, null, null), @@ -651,7 +651,7 @@ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/m1911(new_human), WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/m1911(new_human), WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/regular/response(new_human), WEAR_IN_BACK) - new_human.equip_to_slot_or_del(new /obj/item/device/flashlight(new_human), WEAR_L_STORE) + new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/combat(new_human), WEAR_L_STORE) new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert(new_human), WEAR_R_STORE) spawn_weapon(/obj/item/weapon/gun/rifle/mar40, /obj/item/ammo_magazine/rifle/mar40, new_human) @@ -666,7 +666,7 @@ list("CLF Belt (Random)", 0, /obj/effect/essentials_set/random/clf_belt, MARINE_CAN_BUY_BELT, VENDOR_ITEM_MANDATORY), list("Headset", 0, /obj/item/device/radio/headset/distress/CLF/command, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY), list("Makeshift Meal", 0, /obj/item/mre_food_packet/clf, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY), - list("Flashlight", 0, /obj/item/device/flashlight, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), + list("Flashlight", 0, /obj/item/device/flashlight/combat, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), list("Combat Pack", 0, /obj/item/storage/backpack/lightpack, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY), list("HELMET (CHOOSE 1)", 0, null, null, null), @@ -960,7 +960,7 @@ list("Binoculars", 5,/obj/item/device/binoculars, null, VENDOR_ITEM_REGULAR), list("Rangefinder", 8, /obj/item/device/binoculars/range, null, VENDOR_ITEM_REGULAR), list("Laser Designator", 12, /obj/item/device/binoculars/range/designator, null, VENDOR_ITEM_RECOMMENDED), - list("Flashlight", 1, /obj/item/device/flashlight, null, VENDOR_ITEM_RECOMMENDED), + list("Flashlight", 1, /obj/item/device/flashlight/combat, null, VENDOR_ITEM_RECOMMENDED), list("Fulton Recovery Device", 5, /obj/item/stack/fulton, null, VENDOR_ITEM_REGULAR), list("Motion Detector", 5, /obj/item/device/motiondetector, null, VENDOR_ITEM_REGULAR), list("Space Cleaner", 2, /obj/item/reagent_container/spray/cleaner, null, VENDOR_ITEM_REGULAR), @@ -1070,7 +1070,7 @@ list("CLF Smartgunner Belt", 0, /obj/item/storage/belt/gun/smartgunner/clf, MARINE_CAN_BUY_BELT, VENDOR_ITEM_MANDATORY), list("Headset", 0, /obj/item/device/radio/headset/distress/CLF/command, MARINE_CAN_BUY_EAR, VENDOR_ITEM_MANDATORY), list("Makeshift Meal", 0, /obj/item/mre_food_packet/clf, MARINE_CAN_BUY_MRE, VENDOR_ITEM_MANDATORY), - list("Flashlight", 0, /obj/item/device/flashlight, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), + list("Flashlight", 0, /obj/item/device/flashlight/combat, MARINE_CAN_BUY_KIT, VENDOR_ITEM_MANDATORY), list("Combat Pack", 0, /obj/item/storage/backpack/lightpack, MARINE_CAN_BUY_BACKPACK, VENDOR_ITEM_MANDATORY), list("HELMET (CHOOSE 1)", 0, null, null, null), @@ -1220,7 +1220,7 @@ spawn_rebel_shoes(new_human) spawn_rebel_gloves(new_human) new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar, WEAR_IN_JACKET) - new_human.equip_to_slot_or_del(new /obj/item/device/flashlight(new_human), WEAR_L_STORE) + new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/combat(new_human), WEAR_L_STORE) new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert(new_human), WEAR_R_STORE) new_human.equip_to_slot_or_del(new /obj/item/storage/belt/shotgun/full/random(new_human), WEAR_WAIST) new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/shotgun/pump/dual_tube/cmb(new_human), WEAR_BACK) @@ -1256,7 +1256,7 @@ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/m1911(new_human), WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/m1911(new_human), WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/regular/response(new_human), WEAR_IN_BACK) - new_human.equip_to_slot_or_del(new /obj/item/device/flashlight(new_human), WEAR_L_STORE) + new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/combat(new_human), WEAR_L_STORE) new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert(new_human), WEAR_R_STORE) spawn_weapon(/obj/item/weapon/gun/rifle/mar40, /obj/item/ammo_magazine/rifle/mar40, new_human) @@ -1283,7 +1283,7 @@ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/engineerpack/ert, WEAR_BACK) new_human.equip_to_slot_or_del(new /obj/item/explosive/grenade/incendiary/molotov, WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/regular/response(new_human), WEAR_IN_BACK) - new_human.equip_to_slot_or_del(new /obj/item/device/flashlight, WEAR_IN_BACK) + new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/combat, WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/firstaid/ert, WEAR_R_STORE) new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/clf_patch, WEAR_ACCESSORY) @@ -1302,7 +1302,7 @@ new_human.equip_to_slot_or_del(terrorist, WEAR_BODY) new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/clf_patch, WEAR_ACCESSORY) - new_human.equip_to_slot_or_del(new /obj/item/device/flashlight, WEAR_IN_ACCESSORY) + new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/combat, WEAR_IN_ACCESSORY) new_human.equip_to_slot_or_del(new /obj/item/explosive/grenade/incendiary/molotov, WEAR_IN_ACCESSORY) new_human.equip_to_slot_or_del(new /obj/item/explosive/grenade/incendiary/molotov, WEAR_IN_ACCESSORY) new_human.equip_to_slot_or_del(new /obj/item/explosive/grenade/incendiary/molotov, WEAR_IN_ACCESSORY) diff --git a/code/modules/gear_presets/survivors/sorokyne_strata/preset_sorokyne_strata.dm b/code/modules/gear_presets/survivors/sorokyne_strata/preset_sorokyne_strata.dm index 6d1838075825..b89b41090576 100644 --- a/code/modules/gear_presets/survivors/sorokyne_strata/preset_sorokyne_strata.dm +++ b/code/modules/gear_presets/survivors/sorokyne_strata/preset_sorokyne_strata.dm @@ -811,7 +811,7 @@ job_title = JOB_HC_SEC_SYNTH assignment = JOB_HC_SEC_SYNTH role_comm_title = "HC Sec Syn" - faction_group = list(FACTION_HYPERDYNE, FACTION_LIST_SURVIVOR_UPP) + faction_group = FACTION_LIST_SURVIVOR_HYPERDYNE idtype = /obj/item/card/id/silver/cl/hyperdyne survivor_variant = CORPORATE_SURVIVOR minimap_background = "background_hc_management" diff --git a/code/modules/mob/living/carbon/xenomorph/Embryo.dm b/code/modules/mob/living/carbon/xenomorph/Embryo.dm index f29b97fc8fd9..51be269699d2 100644 --- a/code/modules/mob/living/carbon/xenomorph/Embryo.dm +++ b/code/modules/mob/living/carbon/xenomorph/Embryo.dm @@ -300,8 +300,10 @@ notify_ghosts(header = "Burst Imminent", message = "A [new_xeno.hive.prefix]Larva is about to chestburst out of [affected_mob][area_text]!", source = affected_mob) stage = 7 // Begin the autoburst countdown +/mob/living/carbon/xenomorph/proc/cause_unbearable_pain(mob/living/carbon/victim) + return -/mob/living/carbon/xenomorph/larva/proc/cause_unbearable_pain(mob/living/carbon/victim) +/mob/living/carbon/xenomorph/larva/cause_unbearable_pain(mob/living/carbon/victim) if(loc != victim) return victim.emote("scream") @@ -311,7 +313,10 @@ to_chat(victim, message) addtimer(CALLBACK(src, PROC_REF(cause_unbearable_pain), victim), rand(1, 3) SECONDS, TIMER_UNIQUE|TIMER_NO_HASH_WAIT) -/mob/living/carbon/xenomorph/larva/proc/chest_burst(mob/living/carbon/victim) +/mob/living/carbon/xenomorph/proc/chest_burst(mob/living/carbon/victim) + return + +/mob/living/carbon/xenomorph/larva/chest_burst(mob/living/carbon/victim) set waitfor = 0 if(victim.chestburst || loc != victim) return diff --git a/code/modules/mob/living/carbon/xenomorph/Evolution.dm b/code/modules/mob/living/carbon/xenomorph/Evolution.dm index 2b3aa1ea2ebe..83d058a76002 100644 --- a/code/modules/mob/living/carbon/xenomorph/Evolution.dm +++ b/code/modules/mob/living/carbon/xenomorph/Evolution.dm @@ -66,6 +66,12 @@ GLOBAL_LIST_EMPTY(deevolved_ckeys) to_chat(src, SPAN_WARNING("The Hive cannot support this caste yet! ([floor((caste_datum.minimum_evolve_time - ROUND_TIME) / 10)] seconds remaining)")) return + if(hive.restricted_castes && (castepick in hive.restricted_castes)) + var/max_num = hive.restricted_castes[castepick] + if(hive.get_caste_count(castepick) >= max_num) + to_chat(src, SPAN_WARNING("The Hive has reached capacity for this caste!")) + return + if(!evolve_checks()) return @@ -279,7 +285,7 @@ GLOBAL_LIST_EMPTY(deevolved_ckeys) to_chat(src, SPAN_WARNING("We must be at full health to evolve.")) return FALSE - if(agility || fortify || crest_defense || stealth) + if(agility || fortify || crest_defense || stealth || HAS_TRAIT(src, TRAIT_ABILITY_ENCLOSED_PLATES) || HAS_TRAIT(src, TRAIT_ABILITY_REFLECTIVE_PLATES)) to_chat(src, SPAN_WARNING("We cannot evolve while in this stance.")) return FALSE @@ -518,10 +524,10 @@ GLOBAL_LIST_EMPTY(deevolved_ckeys) if(xeno.counts_for_slots) totalXenos++ - if(tier == 1 && (((used_tier_2_slots + used_tier_3_slots) / totalXenos) * hive.tier_slot_multiplier) >= 0.5 && castepick != XENO_CASTE_QUEEN) + if(tier == 1 && (((used_tier_2_slots + used_tier_3_slots) / totalXenos) * hive.tier_slot_divisor) >= 0.5 && castepick != XENO_CASTE_QUEEN) to_chat(src, SPAN_WARNING("The hive cannot support another Tier 2, wait for either more aliens to be born or someone to die.")) return FALSE - else if(tier == 2 && ((used_tier_3_slots / totalXenos) * hive.tier_slot_multiplier) >= 0.20 && castepick != XENO_CASTE_QUEEN) + else if(tier == 2 && ((used_tier_3_slots / totalXenos) * hive.tier_slot_divisor) >= 0.20 && castepick != XENO_CASTE_QUEEN) to_chat(src, SPAN_WARNING("The hive cannot support another Tier 3, wait for either more aliens to be born or someone to die.")) return FALSE diff --git a/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm b/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm index cf25fadeef89..816ba1fb8c7f 100644 --- a/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm +++ b/code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm @@ -109,7 +109,7 @@ //Hot hot Aliens on Aliens action. //Actually just used for eating people. /mob/living/carbon/xenomorph/attack_alien(mob/living/carbon/xenomorph/xeno) - if (xeno.fortify || HAS_TRAIT(xeno, TRAIT_ABILITY_BURROWED)) + if(xeno.fortify || HAS_TRAIT(xeno, TRAIT_ABILITY_BURROWED) || HAS_TRAIT(xeno, TRAIT_ABILITY_REFLECTIVE_PLATES)) return XENO_NO_DELAY_ACTION if(HAS_TRAIT(src, TRAIT_ABILITY_BURROWED)) diff --git a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm index 7896d44e0e7d..a8d19062ff71 100644 --- a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm +++ b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm @@ -782,6 +782,10 @@ to_chat(src, SPAN_XENOBOLDNOTICE("There are no weeds here! Nesting hosts requires hive weeds.")) return + if(supplier_weeds.hivenumber != hivenumber) + to_chat(src, SPAN_XENOBOLDNOTICE("The weeds here do not belong to us!")) + return + if(supplier_weeds.weed_strength < WEED_LEVEL_HIVE) to_chat(src, SPAN_XENOBOLDNOTICE("The weeds here are not strong enough for nesting hosts.")) return @@ -807,7 +811,7 @@ to_chat(src, SPAN_XENONOTICE("There is already a host nested here!")) return - var/obj/structure/bed/nest/applicable_nest = new(get_turf(host_to_nest)) + var/obj/structure/bed/nest/applicable_nest = new(get_turf(host_to_nest), hivenumber) applicable_nest.dir = dir_to_nest if(!applicable_nest.buckle_mob(host_to_nest, src)) qdel(applicable_nest) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index 0f330a9bee1d..f04a028b62cf 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -136,6 +136,8 @@ var/armor_integrity_last_damage_time = 0 var/armor_integrity_immunity_time = 0 + var/melee_vulnerability_mult = 0 + var/pull_multiplier = 1 var/aura_strength = 0 // Pheromone strength var/weed_level = WEED_LEVEL_STANDARD @@ -211,6 +213,8 @@ var/plasmapool_modifier = 1 var/plasmagain_modifier = 0 var/tackle_chance_modifier = 0 + var/tackle_min_modifier = 0 + var/tackle_max_modifier = 0 var/regeneration_multiplier = 1 var/speed_modifier = 0 var/phero_modifier = 0 @@ -221,6 +225,9 @@ var/attack_speed_modifier = 0 var/armor_integrity_modifier = 0 + ///Used to add plasma to strain if caste have 0 plasma_max + var/add_plasma = 0 + var/list/modifier_sources COOLDOWN_DECLARE(next_strain_reset) @@ -248,6 +255,7 @@ /// Caste-based spit windup var/spit_windup = FALSE /// Caste-based spit windup duration (if applicable) + var/spit_delay = 0 var/tileoffset = 0 // How much your view will be offset in the direction that you zoom? var/viewsize = 0 //What size your view will be changed to when you zoom? var/banished = FALSE // Banished xenos can be attacked by all other xenos @@ -318,13 +326,16 @@ var/obj/effect/alien/resin/fruit/selected_fruit = null var/list/built_structures = list() - // Designer stuff + /// Designer related var/obj/effect/alien/resin/design/selected_design = null var/list/available_design = list() var/list/current_design = list() var/max_design_nodes = 0 var/selected_design_mark + var/front_armor + var/side_armor + var/icon_xeno var/icon_xenonid var/xenonid_pixel_x @@ -607,6 +618,9 @@ if(fire_immunity & FIRE_IMMUNITY_XENO_FRENZY) . |= COMPONENT_XENO_FRENZY +/mob/living/carbon/xenomorph/proc/get_reflection_chance(obj/projectile/bullet) + return + //Off-load this proc so it can be called freely //Since Xenos change names like they change shoes, we need somewhere to hammer in all those legos //We set their name first, then update their real_name AND their mind name @@ -939,8 +953,8 @@ recalculate_tackle() /mob/living/carbon/xenomorph/proc/recalculate_tackle() - tackle_min = caste.tackle_min - tackle_max = caste.tackle_max + tackle_min = caste.tackle_min + tackle_min_modifier + tackle_max = caste.tackle_max + tackle_max_modifier tackle_chance = caste.tackle_chance + tackle_chance_modifier tacklestrength_min = caste.tacklestrength_min tacklestrength_max = caste.tacklestrength_max @@ -961,19 +975,23 @@ health = maxHealth /mob/living/carbon/xenomorph/proc/recalculate_plasma() - if(!plasma_max) + var/new_plasma_max = (plasmapool_modifier * caste.plasma_max) + add_plasma + if(!plasma_max && new_plasma_max <= 0) return - var/new_plasma_max = plasmapool_modifier * caste.plasma_max plasma_gain = plasmagain_modifier + caste.plasma_gain if(hive) new_plasma_max += hive.hive_stat_modifier_flat["plasmapool"] new_plasma_max *= hive.hive_stat_modifier_multiplier["plasmapool"] plasma_gain += hive.hive_stat_modifier_flat["plasmagain"] plasma_gain *= hive.hive_stat_modifier_multiplier["plasmagain"] - if (new_plasma_max == plasma_max) + if(new_plasma_max == plasma_max) return - var/plasma_ratio = plasma_stored / plasma_max + + var/plasma_ratio = 0 + if(plasma_max > 0) + plasma_ratio = plasma_stored / plasma_max + plasma_max = new_plasma_max plasma_stored = floor(plasma_max * plasma_ratio + 0.5) //Restore our plasma ratio, so if we're full, we continue to be full, etc. Rounding up (hence the +0.5) if(plasma_stored > plasma_max) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/ability_helper_procs.dm b/code/modules/mob/living/carbon/xenomorph/abilities/ability_helper_procs.dm index e98b6d900dc8..4d008f6dc2b1 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/ability_helper_procs.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/ability_helper_procs.dm @@ -25,7 +25,7 @@ for(var/obj/effect/xenomorph/acid/A in turf) if(acid_type == A.type && A.acid_t == O) - to_chat(src, SPAN_WARNING("[A] is already drenched in acid.")) + to_chat(src, SPAN_WARNING("[O] is already drenched in acid.")) return var/obj/I @@ -103,7 +103,7 @@ // AGAIN BECAUSE SOMETHING COULD'VE ACIDED THE PLACE for(var/obj/effect/xenomorph/acid/A in turf) if(acid_type == A.type && A.acid_t == O) - to_chat(src, SPAN_WARNING("[A] is already drenched in acid.")) + to_chat(src, SPAN_WARNING("[O] is already drenched in acid.")) return if(HAS_TRAIT(src, TRAIT_ABILITY_BURROWED)) //Checked again to account for people trying to place acid while channeling the burrow ability diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm index 9c22bf65e5c8..04fa1f7fe9b9 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm @@ -570,3 +570,289 @@ seethroughComp.toggle_active() apply_cooldown() + + + +/mob/living/carbon/xenomorph/proc/set_orders() + set category = "Alien.Hivemind-Control" + set name = "Set Hive Orders (50)" + set desc = "Give some specific orders to the hive. They can see this on the status pane." + + if(!check_state()) + return + if(last_special > world.time) + return + if(!check_plasma(50)) + return + use_plasma(50) + + var/txt = strip_html(input("Set the hive's orders to what? Leave blank to clear it.", "Hive Orders","")) + if(txt) + xeno_message("The Queen's will overwhelms your instincts...", 3, hivenumber) + xeno_message("\""+txt+"\"", 3, hivenumber) + xeno_maptext(txt, "Hive Orders Updated", hivenumber) + hive.hive_orders = txt + log_hiveorder("[key_name(usr)] has set the Hive Order to: [txt]") + else + hive.hive_orders = "" + + last_special = world.time + 15 SECONDS + +/mob/living/carbon/xenomorph/proc/hive_message() + set category = "Alien.Hivemind" + set name = "Word of the Queen (50)" + set desc = "Send a message to all aliens in the hive that is big and visible." + if(client.prefs.muted & MUTE_IC) + to_chat(src, SPAN_DANGER("You cannot send Announcements (muted).")) + return + if(health <= 0) + to_chat(src, SPAN_WARNING("You can't do that while unconscious.")) + return FALSE + if(!check_plasma(50)) + return FALSE + + // Get a reference to the ability to utilize cooldowns + var/datum/action/xeno_action/onclick/queen_word/word_ability + for(var/datum/action/xeno_action/action in actions) + if(istype(action, /datum/action/xeno_action/onclick/queen_word)) + word_ability = action + if(!word_ability.action_cooldown_check()) + return FALSE + break + + var/input = stripped_multiline_input(src, "This message will be broadcast throughout the hive.", "Word of the Queen", "") + if(!input) + return FALSE + + use_plasma(50) + if(word_ability) + word_ability.apply_cooldown() + + xeno_announcement(input, hivenumber, "The words of the [name] reverberate in our head...") + + message_admins("[key_name_admin(src)] has created a Word of the Queen report:") + log_admin("[key_name_admin(src)] Word of the Queen: [input]") + return TRUE + +/mob/living/carbon/xenomorph/proc/claw_toggle() + set name = "Permit/Disallow Harming" + set desc = "Allows you to permit the hive to harm/slash." + set category = "Alien.Hivemind-Control" + + if(stat) + to_chat(src, SPAN_WARNING("You can't do that now.")) + return + + if(!hive) + to_chat(src, SPAN_WARNING("You can't do that now.")) + CRASH("[src] attempted to toggle slashing without a linked hive") + + if(hive.hive_flags_locked) + to_chat(src, SPAN_WARNING("You can't do that now.")) + return + + if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_TOGGLE_SLASH)) + to_chat(src, SPAN_WARNING("You must wait a bit before you can toggle this again.")) + return + + var/current_setting = null + if(CHECK_MULTIPLE_BITFIELDS(hive.hive_flags, XENO_SLASH_ALLOW_ALL)) + current_setting = "Allowed" + else if(!(hive.hive_flags & XENO_SLASH_INFECTED) && (hive.hive_flags & XENO_SLASH_NORMAL)) + current_setting = "Restricted - Infected Hosts" + else if(!(hive.hive_flags & XENO_SLASH_ALLOW_ALL)) + current_setting = "Forbidden" + + var/choice = tgui_input_list(src, "Choose which level of harming hosts to permit to your hive.", "Harming", list("Forbidden", "Restricted - Infected Hosts", "Allowed"), theme="hive_status", default=current_setting) + if(!choice) + return + + if(choice == "Allowed") + if(current_setting == choice) + to_chat(src, SPAN_XENOWARNING("You already allow harming.")) + return + to_chat(src, SPAN_XENONOTICE("You allow harming.")) + xeno_message(SPAN_XENOANNOUNCE("The Queen has permitted the harming of hosts! Go hog wild!"), hivenumber=hivenumber) + hive.hive_flags |= XENO_SLASH_ALLOW_ALL + else if(choice == "Restricted - Infected Hosts") + if(current_setting == choice) + to_chat(src, SPAN_XENOWARNING("You already forbid harming of infected hosts.")) + return + to_chat(src, SPAN_XENONOTICE("You forbid harming of infected hosts.")) + xeno_message(SPAN_XENOANNOUNCE("The Queen has restricted the harming of hosts. You can no longer slash infected hosts."), hivenumber=hivenumber) + hive.hive_flags &= ~XENO_SLASH_INFECTED + hive.hive_flags |= XENO_SLASH_NORMAL + else if(choice == "Forbidden") + if(current_setting == choice) + to_chat(src, SPAN_XENOWARNING("You already forbid harming entirely.")) + return + to_chat(src, SPAN_XENONOTICE("You forbid harming entirely.")) + xeno_message(SPAN_XENOANNOUNCE("The Queen has forbidden the harming of hosts. You can no longer slash your enemies."), hivenumber=hivenumber) + hive.hive_flags &= ~XENO_SLASH_ALLOW_ALL + + TIMER_COOLDOWN_START(src, COOLDOWN_TOGGLE_SLASH, 30 SECONDS) + +/mob/living/carbon/xenomorph/proc/construction_toggle() + set name = "Permit/Disallow Construction Placement" + set desc = "Allows you to permit the hive to place construction nodes freely." + set category = "Alien.Hivemind-Control" + + if(stat) + to_chat(src, SPAN_WARNING("You can't do that now.")) + return + + if(!hive) + to_chat(src, SPAN_WARNING("You can't do that now.")) + CRASH("[src] attempted to toggle construction without a linked hive") + + if(hive.hive_flags_locked) + to_chat(src, SPAN_WARNING("You can't do that now.")) + return + + if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_TOGGLE_CONSTRUCTION)) + to_chat(src, SPAN_WARNING("You must wait a bit before you can toggle this again.")) + return + + var/current_setting = null + if(CHECK_MULTIPLE_BITFIELDS(hive.hive_flags, XENO_CONSTRUCTION_ALLOW_ALL)) + current_setting = "Anyone" + else if(!(hive.hive_flags & XENO_CONSTRUCTION_NORMAL) && CHECK_MULTIPLE_BITFIELDS(hive.hive_flags, XENO_CONSTRUCTION_QUEEN|XENO_CONSTRUCTION_LEADERS)) + current_setting = "Leaders" + else if(!(hive.hive_flags & (XENO_CONSTRUCTION_LEADERS|XENO_CONSTRUCTION_NORMAL)) && (hive.hive_flags & XENO_CONSTRUCTION_QUEEN)) + current_setting = "Queen" + + var/choice = tgui_input_list(src, "Choose which level of construction placement freedom to permit to your hive.", "Construction", list("Queen", "Leaders", "Anyone"), theme="hive_status", default=current_setting) + if(!choice) + return + + if(choice == "Anyone") + if(current_setting == choice) + to_chat(src, SPAN_XENOWARNING("You already allow construction placement to all builder castes.")) + return + to_chat(src, SPAN_XENONOTICE("You allow construction placement to all builder castes.")) + xeno_message("The Queen has permitted the placement of construction nodes to all builder castes!", hivenumber=hivenumber) + hive.hive_flags |= XENO_CONSTRUCTION_ALLOW_ALL + else if(choice == "Leaders") + if(current_setting == choice) + to_chat(src, SPAN_XENOWARNING("You already restrict construction placement to leaders only.")) + return + to_chat(src, SPAN_XENONOTICE("You restrict construction placement to leaders only.")) + xeno_message("The Queen has restricted the placement of construction nodes to leading builder castes only.", hivenumber=hivenumber) + hive.hive_flags &= ~XENO_CONSTRUCTION_NORMAL + hive.hive_flags |= XENO_CONSTRUCTION_QUEEN|XENO_CONSTRUCTION_LEADERS + else if(choice == "Queen") + if(current_setting == choice) + to_chat(src, SPAN_XENOWARNING("You already forbid construction placement entirely.")) + return + to_chat(src, SPAN_XENONOTICE("You forbid construction placement entirely.")) + xeno_message("The Queen has forbidden the placement of construction nodes to all but herself.", hivenumber=hivenumber) + hive.hive_flags &= ~(XENO_CONSTRUCTION_LEADERS|XENO_CONSTRUCTION_NORMAL) + hive.hive_flags |= XENO_CONSTRUCTION_QUEEN + + TIMER_COOLDOWN_START(src, COOLDOWN_TOGGLE_CONSTRUCTION, 30 SECONDS) + +/mob/living/carbon/xenomorph/proc/destruction_toggle() + set name = "Permit/Disallow Special Structure Destruction" + set desc = "Allows you to permit the hive to destroy special structures freely." + set category = "Alien.Hivemind-Control" + + if(stat) + to_chat(src, SPAN_WARNING("You can't do that now.")) + return + + if(!hive) + to_chat(src, SPAN_WARNING("You can't do that now.")) + CRASH("[src] attempted to toggle deconstruction without a linked hive") + + if(hive.hive_flags_locked) + to_chat(src, SPAN_WARNING("You can't do that now.")) + return + + if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_TOGGLE_DECONSTRUCTION)) + to_chat(src, SPAN_WARNING("You must wait a bit before you can toggle this again.")) + return + + var/current_setting = null + if(CHECK_MULTIPLE_BITFIELDS(hive.hive_flags, XENO_DECONSTRUCTION_ALLOW_ALL)) + current_setting = "Anyone" + else if(!(hive.hive_flags & XENO_DECONSTRUCTION_NORMAL) && CHECK_MULTIPLE_BITFIELDS(hive.hive_flags, XENO_DECONSTRUCTION_QUEEN|XENO_DECONSTRUCTION_LEADERS)) + current_setting = "Leaders" + else if(!(hive.hive_flags & (XENO_DECONSTRUCTION_LEADERS|XENO_DECONSTRUCTION_NORMAL)) && (hive.hive_flags & XENO_DECONSTRUCTION_QUEEN)) + current_setting = "Queen" + + var/choice = tgui_input_list(src, "Choose which level of destruction freedom to permit to your hive.", "Deconstruction", list("Queen", "Leaders", "Anyone"), theme="hive_status", default=current_setting) + if(!choice) + return + + if(choice == "Anyone") + if(current_setting == choice) + to_chat(src, SPAN_XENOWARNING("You already allow special structure destruction to all builder castes and leaders.")) + return + to_chat(src, SPAN_XENONOTICE("You allow special structure destruction to all builder castes and leaders.")) + xeno_message("The Queen has permitted the destruction of special structures to all builder castes and leaders!", hivenumber=hivenumber) + hive.hive_flags |= XENO_DECONSTRUCTION_ALLOW_ALL + else if(choice == "Leaders") + if(current_setting == choice) + to_chat(src, SPAN_XENOWARNING("You already restrict special structure destruction to leaders only.")) + return + to_chat(src, SPAN_XENONOTICE("You restrict special structure destruction to leaders only.")) + xeno_message("The Queen has restricted the destruction of special structures to leaders only.", hivenumber=hivenumber) + hive.hive_flags &= ~XENO_DECONSTRUCTION_NORMAL + hive.hive_flags |= XENO_DECONSTRUCTION_QUEEN|XENO_DECONSTRUCTION_LEADERS + else if(choice == "Queen") + if(current_setting == choice) + to_chat(src, SPAN_XENOWARNING("You already forbid special structure destruction entirely.")) + return + to_chat(src, SPAN_XENONOTICE("You forbid special structure destruction entirely.")) + xeno_message("The Queen has forbidden the destruction of special structures to all but herself.", hivenumber=hivenumber) + hive.hive_flags &= ~(XENO_DECONSTRUCTION_LEADERS|XENO_DECONSTRUCTION_NORMAL) + hive.hive_flags |= XENO_DECONSTRUCTION_QUEEN + + TIMER_COOLDOWN_START(src, COOLDOWN_TOGGLE_DECONSTRUCTION, 30 SECONDS) + +/mob/living/carbon/xenomorph/proc/unnesting_toggle() + set name = "Permit/Disallow Unnesting" + set desc = "Allows you to restrict unnesting to drones." + set category = "Alien.Hivemind-Control" + + if(stat) + to_chat(src, SPAN_WARNING("You can't do that now.")) + + if(!hive) + to_chat(src, SPAN_WARNING("You can't do that now.")) + CRASH("[src] attempted to toggle unnesting without a linked hive") + + if(hive.hive_flags_locked) + to_chat(src, SPAN_WARNING("You can't do that now.")) + return + + if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_TOGGLE_UNNESTING)) + to_chat(src, SPAN_WARNING("You must wait a bit before you can toggle this again.")) + return + + var/current_setting = null + if(!(hive.hive_flags & XENO_UNNESTING_RESTRICTED)) + current_setting = "Anyone" + else if(hive.hive_flags & XENO_UNNESTING_RESTRICTED) + current_setting = "Drone castes" + + var/choice = tgui_input_list(src, "Choose which level of unnesting freedom to permit to your hive.", "Unnesting", list("Drone castes", "Anyone"), theme="hive_status", default=current_setting) + if(!choice) + return + + if(choice == "Anyone") + if(!(hive.hive_flags & XENO_UNNESTING_RESTRICTED)) + to_chat(src, SPAN_XENOWARNING("You have already allowed everyone to unnest hosts.")) + return + to_chat(src, SPAN_XENONOTICE("You have allowed everyone to unnest hosts.")) + xeno_message("The Queen has allowed everyone to unnest hosts.", hivenumber=hivenumber) + hive.hive_flags &= ~XENO_UNNESTING_RESTRICTED + else + if(hive.hive_flags & XENO_UNNESTING_RESTRICTED) + to_chat(src, SPAN_XENOWARNING("You have already forbidden anyone to unnest hosts, except for the drone caste.")) + return + to_chat(src, SPAN_XENONOTICE("You have forbidden anyone to unnest hosts, except for the drone caste.")) + xeno_message("The Queen has forbidden anyone to unnest hosts, except for the drone caste.", hivenumber=hivenumber) + hive.hive_flags |= XENO_UNNESTING_RESTRICTED + + TIMER_COOLDOWN_START(src, COOLDOWN_TOGGLE_UNNESTING, 30 SECONDS) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm index b570e4f068db..8bbbdd50c027 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm @@ -110,6 +110,10 @@ to_chat(src, SPAN_WARNING("We cannot rest while our crest is down!")) return + if(HAS_TRAIT(src, TRAIT_ABILITY_ENCLOSED_PLATES)) + to_chat(src, SPAN_WARNING("We cannot rest when we are encased in plates!")) + return + return ..() /mob/living/carbon/xenomorph/set_lying_down() diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/runner/runner_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/runner/runner_powers.dm index 6ebc429a44ce..3cdbe7be7c8c 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/runner/runner_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/runner/runner_powers.dm @@ -91,7 +91,7 @@ // AGAIN BECAUSE SOMETHING COULD'VE ACIDED THE PLACE for(var/obj/effect/xenomorph/acid/acid in turf) if(acid_type == acid.type && acid.acid_t == affected_atom) - to_chat(src, SPAN_WARNING("[acid] is already drenched in acid.")) + to_chat(src, SPAN_WARNING("[affected_atom] is already drenched in acid.")) return if(!check_state()) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_abilities.dm index 2e175bbc2482..458cb3d3489a 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_abilities.dm @@ -40,3 +40,56 @@ var/base_punch_damage_synth = 30 var/base_punch_damage_pred = 25 var/damage_variance = 5 + +///Bulwark Strain + +/datum/action/xeno_action/onclick/toggle_plates + name = "Toggle Encasing Plates" + action_icon_state = "encased_plates" + macro_path = /datum/action/xeno_action/verb/verb_toggle_plates + action_type = XENO_ACTION_ACTIVATE + xeno_cooldown = 1 SECONDS + ability_primacy = XENO_PRIMARY_ACTION_1 + + var/speed_debuff = 1.35 + +/datum/action/xeno_action/activable/plate_bash + name = "Plate Bash" + action_icon_state = "plate_bash" + macro_path = /datum/action/xeno_action/verb/verb_plate_bash + action_type = XENO_ACTION_CLICK + ability_primacy = XENO_PRIMARY_ACTION_2 + xeno_cooldown = 4 SECONDS + + var/base_damage = 20 + +/datum/action/xeno_action/onclick/tail_swing + name = "Tail Swing" + action_icon_state = "tail_swing" + macro_path = /datum/action/xeno_action/verb/verb_tail_swing + action_type = XENO_ACTION_ACTIVATE + ability_primacy = XENO_PRIMARY_ACTION_3 + plasma_cost = 20 + xeno_cooldown = 10 SECONDS + + var/swing_range = 1 + +/datum/action/xeno_action/onclick/reflective_shield + name = "Reflective Shield" + action_icon_state = "reflective_shield" + macro_path = /datum/action/xeno_action/verb/verb_reflective_shield + action_type = XENO_ACTION_CLICK + ability_primacy = XENO_PRIMARY_ACTION_4 + + /// used to calculate reflective plates refunding. + var/duration = BULWARK_REFLECTIVE_TIME + /// reflective plates addtimer ID (for deletion) + var/reflective_shield_timer_id = TIMER_ID_NULL + /// Used to countdown BULWARK_REFLECTIVE_TIME. + var/reflective_start_time = -1 + /// How much refund we want to get back? 1 is 1s used to 1s cooldown, 2 is 1s used 2s cooldown. + var/reflective_refund_multiplier = 2 + /// Used in calculation, finalized number will be displayed as cooldown. + var/reflective_recharge_time = null + /// Cooldown after activation to prevent accidental double click. + var/reflective_safe_click_cooldown = 0 diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_macros.dm b/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_macros.dm index 63d97bb69aa2..f6bd989d7c46 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_macros.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_macros.dm @@ -19,3 +19,31 @@ set hidden = TRUE var/action_name = "Punch" handle_xeno_macro(src, action_name) + +/datum/action/xeno_action/verb/verb_toggle_plates() + set category = "Alien" + set name = "Toggle Encasing Plates" + set hidden = TRUE + var/action_name = "Toggle Plates Defense" + handle_xeno_macro(src, action_name) + +/datum/action/xeno_action/verb/verb_plate_bash() + set category = "Alien" + set name = "Plate Bash" + set hidden = TRUE + var/action_name = "Plate Bash" + handle_xeno_macro(src, action_name) + +/datum/action/xeno_action/verb/verb_tail_swing() + set category = "Alien" + set name = "Tail Swing" + set hidden = TRUE + var/action_name = "Tail Swing" + handle_xeno_macro(src, action_name) + +/datum/action/xeno_action/verb/verb_reflective_shield() + set category = "Alien" + set name = "Reflective Shield" + set hidden = TRUE + var/action_name = "Reflective Plates" + handle_xeno_macro(src, action_name) diff --git a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm index 9af59c6201dc..41e43e8cc72f 100644 --- a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm +++ b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm @@ -9,7 +9,7 @@ // this proc could use refactoring at some point /mob/living/carbon/human/attack_alien(mob/living/carbon/xenomorph/attacking_xeno, dam_bonus, unblockable = FALSE) - if(attacking_xeno.fortify || HAS_TRAIT(attacking_xeno, TRAIT_ABILITY_BURROWED)) + if(attacking_xeno.fortify || HAS_TRAIT(attacking_xeno, TRAIT_ABILITY_BURROWED) || HAS_TRAIT(attacking_xeno, TRAIT_ABILITY_REFLECTIVE_PLATES)) return XENO_NO_DELAY_ACTION if(HAS_TRAIT(src, TRAIT_HAULED)) @@ -403,7 +403,10 @@ if(is_wired) xeno.visible_message(SPAN_DANGER("The barbed wire slices into [xeno]!"), SPAN_DANGER("The barbed wire slices into us!"), null, 5, CHAT_TYPE_XENO_COMBAT) - xeno.apply_damage(10, enviro=TRUE) + if(istype(xeno.strain, /datum/xeno_strain/bulwark)) + xeno.apply_damage(5, enviro=TRUE) + else + xeno.apply_damage(10, enviro=TRUE) return XENO_ATTACK_ACTION /obj/structure/barricade/handle_tail_stab(mob/living/carbon/xenomorph/xeno, blunt_stab) @@ -1141,8 +1144,7 @@ to_chat(xeno, SPAN_WARNING("It's already damaged.")) return XENO_NO_DELAY_ACTION xeno.animation_attack_on(src) - xeno.visible_message(SPAN_DANGER("[xeno] slashes away at [src]!"), - SPAN_DANGER("We slash and claw at the bright light!"), max_distance = 5, message_flags = CHAT_TYPE_XENO_COMBAT) + xeno.visible_message("[xeno] slashes away at [src]!","We slash and claw at the bright light!", max_distance = 5, message_flags = CHAT_TYPE_XENO_COMBAT) health = max(health - rand(xeno.melee_damage_lower, xeno.melee_damage_upper), 0) if(!health) set_damaged() diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm index 1a1a63a4bb3e..8a3ad059710f 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm @@ -67,6 +67,9 @@ /datum/action/xeno_action/onclick/toggle_gut_targeting, ) + skull = /obj/item/skull/abomination + pelt = /obj/item/pelt/abomination + weed_food_icon = 'icons/mob/xenos/weeds_64x64.dmi' weed_food_states = list("Predalien_1","Predalien_2","Predalien_3") weed_food_states_flipped = list("Predalien_1","Predalien_2","Predalien_3") diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm index af3bf4cd6ca8..d0d141e3125a 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm @@ -335,8 +335,8 @@ /mob/living/carbon/xenomorph/proc/construction_toggle, /mob/living/carbon/xenomorph/proc/destruction_toggle, /mob/living/carbon/xenomorph/proc/unnesting_toggle, - /mob/living/carbon/xenomorph/queen/proc/set_orders, - /mob/living/carbon/xenomorph/queen/proc/hive_message, + /mob/living/carbon/xenomorph/proc/set_orders, + /mob/living/carbon/xenomorph/proc/hive_message, /mob/living/carbon/xenomorph/proc/rename_tunnel, /mob/living/carbon/xenomorph/proc/set_hugger_reserve_for_morpher, ) @@ -493,13 +493,13 @@ if(XENO_NORMAL) name = "[name_prefix]Queen" //Regular if(XENO_MATURE) - name = "[name_prefix]Elder Queen" //Mature + name = "[name_prefix]Empress" //Mature if(XENO_ELDER) name = "[name_prefix]Elder Empress" //Elite if(XENO_ANCIENT) name = "[name_prefix]Ancient Empress" //Ancient if(XENO_PRIME) - name = "[name_prefix]Prime Empress" //Primordial + name = "[name_prefix]Prime Empress" //Prime else age = XENO_NORMAL if(client) @@ -674,290 +674,6 @@ if(queen_age_temp_timer_id != TIMER_ID_NULL) . += "Temporary Maturity: [time2text(timeleft(queen_age_temp_timer_id), "mm:ss")] remaining" -/mob/living/carbon/xenomorph/queen/proc/set_orders() - set category = "Alien.Hivemind-Control" - set name = "Set Hive Orders (50)" - set desc = "Give some specific orders to the hive. They can see this on the status pane." - - if(!check_state()) - return - if(last_special > world.time) - return - if(!check_plasma(50)) - return - use_plasma(50) - - var/txt = strip_html(input("Set the hive's orders to what? Leave blank to clear it.", "Hive Orders","")) - if(txt) - xeno_message("The Queen's will overwhelms your instincts...", 3, hivenumber) - xeno_message("\""+txt+"\"", 3, hivenumber) - xeno_maptext(txt, "Hive Orders Updated", hivenumber) - hive.hive_orders = txt - log_hiveorder("[key_name(usr)] has set the Hive Order to: [txt]") - else - hive.hive_orders = "" - - last_special = world.time + 15 SECONDS - -/mob/living/carbon/xenomorph/queen/proc/hive_message() - set category = "Alien.Hivemind" - set name = "Word of the Queen (50)" - set desc = "Send a message to all aliens in the hive that is big and visible." - if(client.prefs.muted & MUTE_IC) - to_chat(src, SPAN_DANGER("You cannot send Announcements (muted).")) - return - if(health <= 0) - to_chat(src, SPAN_WARNING("You can't do that while unconscious.")) - return FALSE - if(!check_plasma(50)) - return FALSE - - // Get a reference to the ability to utilize cooldowns - var/datum/action/xeno_action/onclick/queen_word/word_ability - for(var/datum/action/xeno_action/action in actions) - if(istype(action, /datum/action/xeno_action/onclick/queen_word)) - word_ability = action - if(!word_ability.action_cooldown_check()) - return FALSE - break - - var/input = stripped_multiline_input(src, "This message will be broadcast throughout the hive.", "Word of the Queen", "") - if(!input) - return FALSE - - use_plasma(50) - if(word_ability) - word_ability.apply_cooldown() - - xeno_announcement(input, hivenumber, "The words of the [name] reverberate in our head...") - - message_admins("[key_name_admin(src)] has created a Word of the Queen report:") - log_admin("[key_name_admin(src)] Word of the Queen: [input]") - return TRUE - -/mob/living/carbon/xenomorph/proc/claw_toggle() - set name = "Permit/Disallow Harming" - set desc = "Allows you to permit the hive to harm/slash." - set category = "Alien.Hivemind-Control" - - if(stat) - to_chat(src, SPAN_WARNING("You can't do that now.")) - return - - if(!hive) - to_chat(src, SPAN_WARNING("You can't do that now.")) - CRASH("[src] attempted to toggle slashing without a linked hive") - - if(hive.hive_flags_locked) - to_chat(src, SPAN_WARNING("You can't do that now.")) - return - - if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_TOGGLE_SLASH)) - to_chat(src, SPAN_WARNING("You must wait a bit before you can toggle this again.")) - return - - var/current_setting = null - if(CHECK_MULTIPLE_BITFIELDS(hive.hive_flags, XENO_SLASH_ALLOW_ALL)) - current_setting = "Allowed" - else if(!(hive.hive_flags & XENO_SLASH_INFECTED) && (hive.hive_flags & XENO_SLASH_NORMAL)) - current_setting = "Restricted - Infected Hosts" - else if(!(hive.hive_flags & XENO_SLASH_ALLOW_ALL)) - current_setting = "Forbidden" - - var/choice = tgui_input_list(src, "Choose which level of harming hosts to permit to your hive.", "Harming", list("Forbidden", "Restricted - Infected Hosts", "Allowed"), theme="hive_status", default=current_setting) - if(!choice) - return - - if(choice == "Allowed") - if(current_setting == choice) - to_chat(src, SPAN_XENOWARNING("You already allow harming.")) - return - to_chat(src, SPAN_XENONOTICE("You allow harming.")) - xeno_message(SPAN_XENOANNOUNCE("The Queen has permitted the harming of hosts! Go hog wild!"), hivenumber=hivenumber) - hive.hive_flags |= XENO_SLASH_ALLOW_ALL - else if(choice == "Restricted - Infected Hosts") - if(current_setting == choice) - to_chat(src, SPAN_XENOWARNING("You already forbid harming of infected hosts.")) - return - to_chat(src, SPAN_XENONOTICE("You forbid harming of infected hosts.")) - xeno_message(SPAN_XENOANNOUNCE("The Queen has restricted the harming of hosts. You can no longer slash infected hosts."), hivenumber=hivenumber) - hive.hive_flags &= ~XENO_SLASH_INFECTED - hive.hive_flags |= XENO_SLASH_NORMAL - else if(choice == "Forbidden") - if(current_setting == choice) - to_chat(src, SPAN_XENOWARNING("You already forbid harming entirely.")) - return - to_chat(src, SPAN_XENONOTICE("You forbid harming entirely.")) - xeno_message(SPAN_XENOANNOUNCE("The Queen has forbidden the harming of hosts. You can no longer slash your enemies."), hivenumber=hivenumber) - hive.hive_flags &= ~XENO_SLASH_ALLOW_ALL - - TIMER_COOLDOWN_START(src, COOLDOWN_TOGGLE_SLASH, 30 SECONDS) - -/mob/living/carbon/xenomorph/proc/construction_toggle() - set name = "Permit/Disallow Construction Placement" - set desc = "Allows you to permit the hive to place construction nodes freely." - set category = "Alien.Hivemind-Control" - - if(stat) - to_chat(src, SPAN_WARNING("You can't do that now.")) - return - - if(!hive) - to_chat(src, SPAN_WARNING("You can't do that now.")) - CRASH("[src] attempted to toggle construction without a linked hive") - - if(hive.hive_flags_locked) - to_chat(src, SPAN_WARNING("You can't do that now.")) - return - - if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_TOGGLE_CONSTRUCTION)) - to_chat(src, SPAN_WARNING("You must wait a bit before you can toggle this again.")) - return - - var/current_setting = null - if(CHECK_MULTIPLE_BITFIELDS(hive.hive_flags, XENO_CONSTRUCTION_ALLOW_ALL)) - current_setting = "Anyone" - else if(!(hive.hive_flags & XENO_CONSTRUCTION_NORMAL) && CHECK_MULTIPLE_BITFIELDS(hive.hive_flags, XENO_CONSTRUCTION_QUEEN|XENO_CONSTRUCTION_LEADERS)) - current_setting = "Leaders" - else if(!(hive.hive_flags & (XENO_CONSTRUCTION_LEADERS|XENO_CONSTRUCTION_NORMAL)) && (hive.hive_flags & XENO_CONSTRUCTION_QUEEN)) - current_setting = "Queen" - - var/choice = tgui_input_list(src, "Choose which level of construction placement freedom to permit to your hive.", "Construction", list("Queen", "Leaders", "Anyone"), theme="hive_status", default=current_setting) - if(!choice) - return - - if(choice == "Anyone") - if(current_setting == choice) - to_chat(src, SPAN_XENOWARNING("You already allow construction placement to all builder castes.")) - return - to_chat(src, SPAN_XENONOTICE("You allow construction placement to all builder castes.")) - xeno_message("The Queen has permitted the placement of construction nodes to all builder castes!", hivenumber=hivenumber) - hive.hive_flags |= XENO_CONSTRUCTION_ALLOW_ALL - else if(choice == "Leaders") - if(current_setting == choice) - to_chat(src, SPAN_XENOWARNING("You already restrict construction placement to leaders only.")) - return - to_chat(src, SPAN_XENONOTICE("You restrict construction placement to leaders only.")) - xeno_message("The Queen has restricted the placement of construction nodes to leading builder castes only.", hivenumber=hivenumber) - hive.hive_flags &= ~XENO_CONSTRUCTION_NORMAL - hive.hive_flags |= XENO_CONSTRUCTION_QUEEN|XENO_CONSTRUCTION_LEADERS - else if(choice == "Queen") - if(current_setting == choice) - to_chat(src, SPAN_XENOWARNING("You already forbid construction placement entirely.")) - return - to_chat(src, SPAN_XENONOTICE("You forbid construction placement entirely.")) - xeno_message("The Queen has forbidden the placement of construction nodes to all but herself.", hivenumber=hivenumber) - hive.hive_flags &= ~(XENO_CONSTRUCTION_LEADERS|XENO_CONSTRUCTION_NORMAL) - hive.hive_flags |= XENO_CONSTRUCTION_QUEEN - - TIMER_COOLDOWN_START(src, COOLDOWN_TOGGLE_CONSTRUCTION, 30 SECONDS) - -/mob/living/carbon/xenomorph/proc/destruction_toggle() - set name = "Permit/Disallow Special Structure Destruction" - set desc = "Allows you to permit the hive to destroy special structures freely." - set category = "Alien.Hivemind-Control" - - if(stat) - to_chat(src, SPAN_WARNING("You can't do that now.")) - return - - if(!hive) - to_chat(src, SPAN_WARNING("You can't do that now.")) - CRASH("[src] attempted to toggle deconstruction without a linked hive") - - if(hive.hive_flags_locked) - to_chat(src, SPAN_WARNING("You can't do that now.")) - return - - if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_TOGGLE_DECONSTRUCTION)) - to_chat(src, SPAN_WARNING("You must wait a bit before you can toggle this again.")) - return - - var/current_setting = null - if(CHECK_MULTIPLE_BITFIELDS(hive.hive_flags, XENO_DECONSTRUCTION_ALLOW_ALL)) - current_setting = "Anyone" - else if(!(hive.hive_flags & XENO_DECONSTRUCTION_NORMAL) && CHECK_MULTIPLE_BITFIELDS(hive.hive_flags, XENO_DECONSTRUCTION_QUEEN|XENO_DECONSTRUCTION_LEADERS)) - current_setting = "Leaders" - else if(!(hive.hive_flags & (XENO_DECONSTRUCTION_LEADERS|XENO_DECONSTRUCTION_NORMAL)) && (hive.hive_flags & XENO_DECONSTRUCTION_QUEEN)) - current_setting = "Queen" - - var/choice = tgui_input_list(src, "Choose which level of destruction freedom to permit to your hive.", "Deconstruction", list("Queen", "Leaders", "Anyone"), theme="hive_status", default=current_setting) - if(!choice) - return - - if(choice == "Anyone") - if(current_setting == choice) - to_chat(src, SPAN_XENOWARNING("You already allow special structure destruction to all builder castes and leaders.")) - return - to_chat(src, SPAN_XENONOTICE("You allow special structure destruction to all builder castes and leaders.")) - xeno_message("The Queen has permitted the destruction of special structures to all builder castes and leaders!", hivenumber=hivenumber) - hive.hive_flags |= XENO_DECONSTRUCTION_ALLOW_ALL - else if(choice == "Leaders") - if(current_setting == choice) - to_chat(src, SPAN_XENOWARNING("You already restrict special structure destruction to leaders only.")) - return - to_chat(src, SPAN_XENONOTICE("You restrict special structure destruction to leaders only.")) - xeno_message("The Queen has restricted the destruction of special structures to leaders only.", hivenumber=hivenumber) - hive.hive_flags &= ~XENO_DECONSTRUCTION_NORMAL - hive.hive_flags |= XENO_DECONSTRUCTION_QUEEN|XENO_DECONSTRUCTION_LEADERS - else if(choice == "Queen") - if(current_setting == choice) - to_chat(src, SPAN_XENOWARNING("You already forbid special structure destruction entirely.")) - return - to_chat(src, SPAN_XENONOTICE("You forbid special structure destruction entirely.")) - xeno_message("The Queen has forbidden the destruction of special structures to all but herself.", hivenumber=hivenumber) - hive.hive_flags &= ~(XENO_DECONSTRUCTION_LEADERS|XENO_DECONSTRUCTION_NORMAL) - hive.hive_flags |= XENO_DECONSTRUCTION_QUEEN - - TIMER_COOLDOWN_START(src, COOLDOWN_TOGGLE_DECONSTRUCTION, 30 SECONDS) - -/mob/living/carbon/xenomorph/proc/unnesting_toggle() - set name = "Permit/Disallow Unnesting" - set desc = "Allows you to restrict unnesting to drones." - set category = "Alien.Hivemind-Control" - - if(stat) - to_chat(src, SPAN_WARNING("You can't do that now.")) - - if(!hive) - to_chat(src, SPAN_WARNING("You can't do that now.")) - CRASH("[src] attempted to toggle unnesting without a linked hive") - - if(hive.hive_flags_locked) - to_chat(src, SPAN_WARNING("You can't do that now.")) - return - - if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_TOGGLE_UNNESTING)) - to_chat(src, SPAN_WARNING("You must wait a bit before you can toggle this again.")) - return - - var/current_setting = null - if(!(hive.hive_flags & XENO_UNNESTING_RESTRICTED)) - current_setting = "Anyone" - else if(hive.hive_flags & XENO_UNNESTING_RESTRICTED) - current_setting = "Drone castes" - - var/choice = tgui_input_list(src, "Choose which level of unnesting freedom to permit to your hive.", "Unnesting", list("Drone castes", "Anyone"), theme="hive_status", default=current_setting) - if(!choice) - return - - if(choice == "Anyone") - if(!(hive.hive_flags & XENO_UNNESTING_RESTRICTED)) - to_chat(src, SPAN_XENOWARNING("You have already allowed everyone to unnest hosts.")) - return - to_chat(src, SPAN_XENONOTICE("You have allowed everyone to unnest hosts.")) - xeno_message("The Queen has allowed everyone to unnest hosts.", hivenumber=hivenumber) - hive.hive_flags &= ~XENO_UNNESTING_RESTRICTED - else - if(hive.hive_flags & XENO_UNNESTING_RESTRICTED) - to_chat(src, SPAN_XENOWARNING("You have already forbidden anyone to unnest hosts, except for the drone caste.")) - return - to_chat(src, SPAN_XENONOTICE("You have forbidden anyone to unnest hosts, except for the drone caste.")) - xeno_message("The Queen has forbidden anyone to unnest hosts, except for the drone caste.", hivenumber=hivenumber) - hive.hive_flags |= XENO_UNNESTING_RESTRICTED - - TIMER_COOLDOWN_START(src, COOLDOWN_TOGGLE_UNNESTING, 30 SECONDS) - /mob/living/carbon/xenomorph/queen/handle_screech_act(mob/self, mob/living/carbon/xenomorph/queen/queen) return COMPONENT_SCREECH_ACT_CANCEL diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm b/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm index 50a2a8e1d05e..0a79fbf32e9a 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Warrior.dm @@ -15,6 +15,8 @@ behavior_delegate_type = /datum/behavior_delegate/warrior_base + available_strains = list(/datum/xeno_strain/bulwark) + evolves_to = list(XENO_CASTE_PRAETORIAN, XENO_CASTE_CRUSHER) deevolves_to = list(XENO_CASTE_DEFENDER) caste_desc = "A powerful front line combatant." @@ -78,12 +80,19 @@ var/emote_cooldown = 0 var/lunging = FALSE // whether or not the warrior is currently lunging (holding) a target +/mob/living/carbon/xenomorph/warrior/handle_special_state() + return HAS_TRAIT(src, TRAIT_ABILITY_ENCLOSED_PLATES) + +/mob/living/carbon/xenomorph/warrior/handle_special_wound_states(severity) + if(HAS_TRAIT(src, TRAIT_ABILITY_ENCLOSED_PLATES)) + return "Warrior_plates_[severity]" + /mob/living/carbon/xenomorph/warrior/throw_item(atom/target) toggle_throw_mode(THROW_MODE_OFF) /mob/living/carbon/xenomorph/warrior/stop_pulling() var/datum/behavior_delegate/warrior_base/warrior_delegate = behavior_delegate - if(isliving(pulling) && warrior_delegate.lunging) + if(isliving(pulling) && istype(warrior_delegate) && warrior_delegate.lunging) warrior_delegate.lunging = FALSE // To avoid extreme cases of stopping a lunge then quickly pulling and stopping to pull someone else var/mob/living/lunged = pulling lunged.set_effect(0, STUN) diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm index ec58bd42843f..0b0e295c43c5 100644 --- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm +++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm @@ -21,9 +21,9 @@ var/color = null var/ui_color = null // Color for hive status collapsible buttons and xeno count list var/prefix = "" - var/queen_leader_limit = 2 - var/list/open_xeno_leader_positions = list(1, 2) // Ordered list of xeno leader positions (indexes in xeno_leader_list) that are not occupied - var/list/xeno_leader_list[2] // Ordered list (i.e. index n holds the nth xeno leader) + var/queen_leader_limit = 4 + var/list/open_xeno_leader_positions = list(1, 2, 3, 4) // Ordered list of xeno leader positions (indexes in xeno_leader_list) that are not occupied + var/list/xeno_leader_list[4] // Ordered list (i.e. index n holds the nth xeno leader) var/stored_larva = 0 ///used by /datum/hive_status/proc/increase_larva_after_burst() to support non-integer increases to larva @@ -49,7 +49,8 @@ var/allowed_nest_distance = 15 //How far away do we allow nests from an ovied Queen. Default 15 tiles. var/obj/effect/alien/resin/special/pylon/core/hive_location = null //Set to ref every time a core is built, for defining the hive location - var/tier_slot_multiplier = 1 + /// Slots are divided by this value to reach final value. + var/tier_slot_divisor = 1 var/larva_gestation_multiplier = 1 var/bonus_larva_spawn_chance = 1 var/hijack_burrowed_surge = FALSE //at hijack, start spawning lots of burrowed @@ -87,7 +88,7 @@ XENO_STRUCTURE_PYLON = 2, ) - var/global/list/hive_structure_types = list( + var/list/hive_structure_types = list( XENO_STRUCTURE_CORE = /datum/construction_template/xenomorph/core, XENO_STRUCTURE_CLUSTER = /datum/construction_template/xenomorph/cluster, XENO_STRUCTURE_EGGMORPH = /datum/construction_template/xenomorph/eggmorph, @@ -100,6 +101,8 @@ /// Lazylist of possible caste defines the hive disallows evolution to var/list/blacklisted_castes = null + /// List of caste defines associated with a maximum capacity number. + var/list/restricted_castes = null var/datum/hive_status_ui/hive_ui var/datum/mark_menu_ui/mark_ui @@ -377,7 +380,7 @@ /datum/hive_status/proc/recalculate_hive() //No leaders for a Hive without a Queen! - queen_leader_limit = living_xeno_queen ? 4 : 0 + queen_leader_limit = living_xeno_queen ? initial(queen_leader_limit) : 0 if (length(xeno_leader_list) > queen_leader_limit) var/diff = 0 @@ -517,6 +520,23 @@ return xeno_counts +/// Returns number of xenos in the given hive that are the searched caste. +/datum/hive_status/proc/get_caste_count(caste_to_check) + if(!caste_to_check) + return + var/caste_count = 0 + for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos) + //don't show xenos in the thunderdome when admins test stuff. + if(should_block_game_interaction(xeno)) + var/area/cur_area = get_area(xeno) + if(!(cur_area.flags_atom & AREA_ALLOW_XENO_JOIN)) + continue + + if(xeno.caste && xeno.counts_for_slots && (xeno.caste.caste_type == caste_to_check)) + caste_count++ + + return caste_count + /// Returns the full minimap icon as base64 string. /datum/hive_status/proc/get_xeno_icons() // Must match hardcoded xeno counts order. @@ -743,10 +763,10 @@ effective_total++ // Tier 3 slots are always 20% of the total xenos in the hive - slots[TIER_3][OPEN_SLOTS] = max(0, ceil(0.20*effective_total/tier_slot_multiplier) - used_tier_3_slots) + slots[TIER_3][OPEN_SLOTS] = max(0, ceil(0.20*effective_total/tier_slot_divisor) - used_tier_3_slots) // Tier 2 slots are between 30% and 50% of the hive, depending // on how many T3s there are. - slots[TIER_2][OPEN_SLOTS] = max(0, ceil(0.5*effective_total/tier_slot_multiplier) - used_tier_2_slots - used_tier_3_slots) + slots[TIER_2][OPEN_SLOTS] = max(0, ceil(0.5*effective_total/tier_slot_divisor) - used_tier_2_slots - used_tier_3_slots) return slots @@ -815,6 +835,8 @@ /datum/hive_status/proc/abandon_on_hijack() var/area/hijacked_dropship = get_area(living_xeno_queen) + if(!hijacked_dropship) + return FALSE var/shipside_humans_weighted_count = 0 var/xenos_count = 0 for(var/name_ref in hive_structures) diff --git a/code/modules/mob/living/carbon/xenomorph/life.dm b/code/modules/mob/living/carbon/xenomorph/life.dm index 706dbdf36e8e..c23a39ece7e2 100644 --- a/code/modules/mob/living/carbon/xenomorph/life.dm +++ b/code/modules/mob/living/carbon/xenomorph/life.dm @@ -599,6 +599,6 @@ Make sure their actual health updates immediately.*/ return TRUE //weeds, yes! if(need_weeds) return FALSE //needs weeds, doesn't have any - if(hive && hive.living_xeno_queen && !is_mainship_level(hive.living_xeno_queen.loc.z) && is_mainship_level(loc.z)) + if((hive && !hive.allow_no_queen_actions) && hive.living_xeno_queen && (!is_mainship_level(hive.living_xeno_queen.loc.z) && is_mainship_level(loc.z))) return FALSE //We are on the ship, but the Queen isn't return TRUE //we have off-weed healing, and either we're on Almayer with the Queen, or we're on non-Almayer, or the Queen is dead, good enough! diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/berserker.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/berserker.dm index 80e265d2fedd..d33475028b00 100644 --- a/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/berserker.dm +++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/berserker.dm @@ -18,7 +18,7 @@ behavior_delegate_type = /datum/behavior_delegate/ravager_berserker /datum/xeno_strain/berserker/apply_strain(mob/living/carbon/xenomorph/ravager/ravager) - ravager.plasma_max = 0 + ravager.plasmapool_modifier = XENO_NO_PLASMA ravager.health_modifier -= XENO_HEALTH_MOD_MED ravager.armor_modifier += XENO_ARMOR_MOD_VERY_SMALL ravager.speed_modifier += XENO_SPEED_FASTMOD_TIER_3 diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/hedgehog.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/hedgehog.dm index 1497e50ef22d..8632830e2172 100644 --- a/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/hedgehog.dm +++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/hedgehog.dm @@ -18,7 +18,7 @@ behavior_delegate_type = /datum/behavior_delegate/ravager_hedgehog /datum/xeno_strain/hedgehog/apply_strain(mob/living/carbon/xenomorph/ravager/ravager) - ravager.plasma_max = 0 + ravager.plasmapool_modifier = XENO_NO_PLASMA ravager.small_explosives_stun = TRUE ravager.explosivearmor_modifier += XENO_EXPOSIVEARMOR_MOD_SMALL ravager.damage_modifier -= XENO_DAMAGE_MOD_SMALL diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/warrior/bulwark.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/warrior/bulwark.dm new file mode 100644 index 000000000000..6bbecc671e41 --- /dev/null +++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/warrior/bulwark.dm @@ -0,0 +1,393 @@ +/datum/xeno_strain/bulwark + name = WARRIOR_BULWARK + description = "You give up all of your normal abilities, as well as some damage, speed, tackle reliability, and you take 50% more melee damage, in exchange for plasma, slightly stronger explosive resistance, and directional defenses. You take 50% less damage from wired cades, have a 75% chance to strike enemies behind wired cades, and gain bonus directional armor with no directional-lock slowdown. Encasing Plates lets you enter a defensive stance that slows your movement and reduces tackle efficiency, but increases directional armor, makes you immune to knockbacks, and allows you to tear openings in walls. Plate Bash dashes up to 3 tiles and strikes a target; while encased, it instead launches the target up to 3 tiles away and knocks them down. Tail Swing trips enemies around you; if used on a grenade instead, it reflects it up to 3 tiles away with a reduced cooldown. Reflective Shield allows you to reflect bullets coming from the front back toward enemies for up to 6 seconds with a 100% reflection chance. While active, it locks your facing direction to the direction it was activated in. You can stop this ability at any time, but its minimum cooldown is 6 seconds, and each additional 1 second of use adds 2 seconds to the cooldown." + flavor_description = "Where there's a sword, there's a shield." + icon_state_prefix = "Bulwark" + + actions_to_remove = list( + /datum/action/xeno_action/activable/warrior_punch, + /datum/action/xeno_action/activable/lunge, + /datum/action/xeno_action/activable/fling, + ) + actions_to_add = list( + /datum/action/xeno_action/onclick/toggle_plates, //1st + /datum/action/xeno_action/activable/plate_bash, //2nd + /datum/action/xeno_action/onclick/tail_swing, //3rd + /datum/action/xeno_action/onclick/reflective_shield, //4th + ) + + behavior_delegate_type = /datum/behavior_delegate/warrior_bulwark + +/datum/xeno_strain/bulwark/apply_strain(mob/living/carbon/xenomorph/warrior/warrior) + warrior.explosivearmor_modifier += XENO_EXPLOSIVE_ARMOR_TIER_1 + warrior.health_modifier += XENO_HEALTH_MOD_VERY_LARGE + warrior.armor_modifier += XENO_ARMOR_MOD_SMALL + warrior.add_plasma += XENO_PLASMA_TIER_2 + warrior.speed += XENO_SPEED_TIER_1 + warrior.tackle_max_modifier += 1 + warrior.melee_vulnerability_mult += 1.5 + + warrior.recalculate_everything() + +// +// bulwark config +// + +#define BULWARK_DIR_ARMOR 10 +#define BULWARK_GRENADE_SWEEP_THROW 3 +#define BULWARK_REFLECTION_CHANCE_FRONT 100 +#define BULWARK_REFLECTED_BULLET_DAMAGE 0.5 +#define BULWARK_REFLECTED_BULLET_ACCURACY 80 + +// +// Passive benefits +// + +/datum/behavior_delegate/warrior_bulwark + name = "Bulwark Warrior Behavior Delegate" + + var/frontal_armor = BULWARK_DIR_ARMOR + var/sided_armor = BULWARK_DIR_ARMOR + +/datum/behavior_delegate/warrior_bulwark/append_to_stat() + . = list() + . += "Front Armor: +[frontal_armor]" + . += "Side Armor: +[sided_armor]" + if(HAS_TRAIT(bound_xeno, TRAIT_ABILITY_ENCLOSED_PLATES)) + . += "Encased Plates: -[XENO_DAMAGE_MOD_BULWARK] Claws Damage." + var/datum/action/xeno_action/onclick/reflective_shield/ability_used = get_action(bound_xeno, /datum/action/xeno_action/onclick/reflective_shield) + if(ability_used.reflective_start_time != -1) + var/time_left = null + time_left = (BULWARK_REFLECTIVE_TIME - (world.time - ability_used.reflective_start_time)) / 10 + . += "Reflective Plates Remaining Time: [time_left] second\s." + return + +/datum/behavior_delegate/warrior_bulwark/add_to_xeno() + RegisterSignal(bound_xeno, COMSIG_XENO_PRE_CALCULATE_ARMOURED_DAMAGE_PROJECTILE, PROC_REF(apply_directional_armor)) + ADD_TRAIT(bound_xeno, TRAIT_NO_DIR_LOCK_SLOWDOWN, TRAIT_SOURCE_ABILITY("no_dir_lock_slowdown")) + +/datum/behavior_delegate/warrior_bulwark/proc/apply_directional_armor(mob/living/carbon/xenomorph/xeno_player, list/damagedata) + SIGNAL_HANDLER + var/projectile_direction = damagedata["direction"] + if(xeno_player.dir & REVERSE_DIR(projectile_direction)) + damagedata["armor"] += frontal_armor + else + for(var/side_direction in get_perpen_dir(xeno_player.dir)) + if(projectile_direction == side_direction) + damagedata["armor"] += sided_armor + return + +/datum/behavior_delegate/warrior_bulwark/on_update_icons() + if(bound_xeno.stat == DEAD) + return + + if(!HAS_TRAIT(bound_xeno, TRAIT_ABILITY_REFLECTIVE_PLATES)) + if(HAS_TRAIT(bound_xeno, TRAIT_ABILITY_ENCLOSED_PLATES) && bound_xeno.health > 0) + bound_xeno.icon_state = "[bound_xeno.get_strain_icon()] Warrior Shield" + return TRUE + + if(HAS_TRAIT(bound_xeno, TRAIT_ABILITY_REFLECTIVE_PLATES) && bound_xeno.health > 0) + bound_xeno.icon_state = "[bound_xeno.get_strain_icon()] Warrior Shield Reflective" + return TRUE + +/datum/behavior_delegate/warrior_bulwark/melee_attack_additional_effects_target(mob/living/carbon/carbon_target) + if(HAS_TRAIT(bound_xeno, TRAIT_ABILITY_ENCLOSED_PLATES)) + bound_xeno.flick_attack_overlay(carbon_target, "punch") // We shmwack them with plates! + +// +// 1st ability +// + +/datum/action/xeno_action/onclick/toggle_plates/use_ability() + var/mob/living/carbon/xenomorph/xeno_player = owner + if(!istype(xeno_player)) + return + + XENO_ACTION_CHECK(xeno_player) + + var/datum/action/xeno_action/onclick/reflective_shield/ability_used = get_action(xeno_player, /datum/action/xeno_action/onclick/reflective_shield) + if(HAS_TRAIT(xeno_player, TRAIT_ABILITY_ENCLOSED_PLATES)) + if(HAS_TRAIT(xeno_player, TRAIT_ABILITY_REFLECTIVE_PLATES)) + to_chat(xeno_player, SPAN_WARNING("We break our reflective stance!")) + ability_used.reflective_safe_click_cooldown = -1 + ability_used.remove_reflective_shield() + disengage_plates() + else + engage_plates() + + return ..() + +/datum/action/xeno_action/onclick/toggle_plates/proc/engage_plates() + var/mob/living/carbon/xenomorph/xeno_player = owner + if(!istype(xeno_player)) + return + + var/datum/behavior_delegate/warrior_bulwark/behavior = xeno_player.behavior_delegate + if(!istype(behavior)) + return + + ADD_TRAIT(xeno_player, TRAIT_ABILITY_ENCLOSED_PLATES, TRAIT_SOURCE_ABILITY("enclosed_plates")) + to_chat(xeno_player, SPAN_XENOWARNING("We raise our plates and form a shield.")) + xeno_player.ability_speed_modifier += speed_debuff + xeno_player.mob_size = MOB_SIZE_BIG //knockback immune + button.icon_state = "template_active" + behavior.frontal_armor += BULWARK_DIR_ARMOR + behavior.sided_armor -= BULWARK_DIR_ARMOR + xeno_player.damage_modifier -= XENO_DAMAGE_MOD_BULWARK + xeno_player.tackle_min_modifier += 2 + + xeno_player.recalculate_tackle() + xeno_player.update_icons() + apply_cooldown() + +/datum/action/xeno_action/onclick/toggle_plates/proc/disengage_plates() + var/mob/living/carbon/xenomorph/xeno_player = owner + if(!istype(xeno_player)) + return + + var/datum/behavior_delegate/warrior_bulwark/behavior = xeno_player.behavior_delegate + if(!istype(behavior)) + return + + REMOVE_TRAIT(xeno_player, TRAIT_ABILITY_ENCLOSED_PLATES, TRAIT_SOURCE_ABILITY("enclosed_plates")) + to_chat(xeno_player, SPAN_XENOWARNING("We lower our plates.")) + xeno_player.ability_speed_modifier -= speed_debuff + xeno_player.mob_size = MOB_SIZE_XENO //no longer knockback immune + button.icon_state = "template_xeno" + behavior.frontal_armor -= BULWARK_DIR_ARMOR + behavior.sided_armor += BULWARK_DIR_ARMOR + xeno_player.damage_modifier += XENO_DAMAGE_MOD_BULWARK + xeno_player.tackle_min_modifier -= 2 + + xeno_player.recalculate_tackle() + xeno_player.update_icons() + apply_cooldown() + +// +// 2nd ability +// + +/datum/action/xeno_action/activable/plate_bash/use_ability(atom/target_atom) + var/mob/living/carbon/xenomorph/xeno_player = owner + + if(!iscarbon(target_atom)) + return + + if(!isxeno_human(target_atom) || xeno_player.can_not_harm(target_atom)) + return + + XENO_ACTION_CHECK_USE_PLASMA(xeno_player) + + var/mob/living/carbon/carbon_target = target_atom + if(carbon_target.stat == DEAD) + return + + var/distance = get_dist(xeno_player, carbon_target) + var/max_distance = 2 + if(distance > max_distance) + return + + if(!HAS_TRAIT(xeno_player, TRAIT_ABILITY_ENCLOSED_PLATES)) + xeno_player.throw_atom(get_step_towards(carbon_target, xeno_player), 2, SPEED_SLOW, xeno_player, tracking=TRUE) + if(!xeno_player.Adjacent(carbon_target)) + on_cooldown_end() + return + + carbon_target.last_damage_data = create_cause_data(xeno_player.caste_type, xeno_player) + var/facing = get_dir(xeno_player, carbon_target) + + if(HAS_TRAIT(xeno_player, TRAIT_ABILITY_ENCLOSED_PLATES)) + xeno_player.throw_carbon(carbon_target, facing, 3, SPEED_VERY_FAST, shake_camera = TRUE, immobilize = TRUE) + carbon_target.KnockDown(1) + else + xeno_player.throw_carbon(carbon_target, facing, 1, SPEED_SLOW, shake_camera = TRUE, immobilize = FALSE) + + apply_custom_cooldown() + + if(carbon_target.stat != DEAD && (!(carbon_target.status_flags & XENO_HOST) || !HAS_TRAIT(carbon_target, TRAIT_NESTED))) + carbon_target.apply_armoured_damage(get_xeno_damage_slash(carbon_target, base_damage), ARMOR_MELEE, BRUTE, "chest", 5) + + xeno_player.visible_message(SPAN_XENOWARNING("[xeno_player] dashes at [carbon_target] with its armored plates!"), + SPAN_XENOWARNING("We dash at [carbon_target] with our armored plates!")) + + xeno_player.face_atom(carbon_target) + xeno_player.animation_attack_on(carbon_target) + xeno_player.flick_attack_overlay(carbon_target, "punch") + playsound(carbon_target,'sound/weapons/alien_claw_block.ogg', 50, 1) + + return ..() + +// +// 3rd ability +// + +/datum/action/xeno_action/onclick/tail_swing/use_ability() + var/mob/living/carbon/xenomorph/xeno_player = owner + + var/datum/action/xeno_action/onclick/reflective_shield/ability_used = get_action(xeno_player, /datum/action/xeno_action/onclick/reflective_shield) + var/datum/action/xeno_action/onclick/toggle_plates/plates_used = get_action(xeno_player, /datum/action/xeno_action/onclick/toggle_plates) + + if(HAS_TRAIT(xeno_player, TRAIT_ABILITY_ENCLOSED_PLATES)) + if(HAS_TRAIT(xeno_player, TRAIT_ABILITY_REFLECTIVE_PLATES)) + to_chat(xeno_player, SPAN_WARNING("We break our reflective stance!")) + ability_used.reflective_safe_click_cooldown = -1 + ability_used.remove_reflective_shield() + to_chat(xeno_player, SPAN_WARNING("We break our defensive stance!")) + plates_used.disengage_plates() + + xeno_player.visible_message(SPAN_XENOWARNING("[xeno_player] swings its tail in a wide circle!"), + SPAN_XENOWARNING("We swing our tail in a wide circle!")) + + XENO_ACTION_CHECK_USE_PLASMA(xeno_player) + + xeno_player.spin_circle() + playsound(xeno_player,'sound/effects/tail_swing.ogg', 25, 1) + + var/hit_enemy = FALSE + for(var/mob/living/carbon/carbon_target in orange(swing_range, get_turf(xeno_player))) + if(!isxeno_human(carbon_target) || xeno_player.can_not_harm(carbon_target)) + continue + if(carbon_target.stat == DEAD) + continue + if(HAS_TRAIT(carbon_target, TRAIT_NESTED)) + continue + + hit_enemy = TRUE + xeno_player.flick_attack_overlay(carbon_target, "punch") + carbon_target.last_damage_data = create_cause_data(xeno_player.caste_type, xeno_player) + carbon_target.apply_armoured_damage(get_xeno_damage_slash(xeno_player, 15), ARMOR_MELEE, BRUTE) + shake_camera(carbon_target, 2, 1) + + if(carbon_target.mob_size < MOB_SIZE_BIG) + carbon_target.apply_effect(get_xeno_stun_duration(carbon_target, 1), WEAKEN) + + to_chat(carbon_target, SPAN_XENOWARNING("You are tripped by [xeno_player]'s tail swing!")) + playsound(carbon_target,'sound/weapons/alien_claw_block.ogg', 50, 1) + + var/hit_grenade = FALSE + for(var/obj/item/explosive/grenade/grenade in orange(swing_range, get_turf(xeno_player))) + hit_grenade = TRUE + var/direction = get_dir(xeno_player, grenade) + var/turf/target_destination = get_ranged_target_turf(grenade, direction, BULWARK_GRENADE_SWEEP_THROW) + if(target_destination) + grenade.throw_atom(target_destination, BULWARK_GRENADE_SWEEP_THROW, SPEED_FAST, grenade) + playsound(xeno_player,'sound/effects/grenade_hit.ogg', 50, 1) + + if(!hit_enemy && hit_grenade) + xeno_cooldown *= 0.3 + + if(!hit_enemy && !hit_grenade) + xeno_cooldown *= 0.3 + + apply_custom_cooldown() + + return ..() + +// +// 4th ability +// + +/datum/action/xeno_action/onclick/reflective_shield/use_ability() + var/mob/living/carbon/xenomorph/warrior/xeno_player = owner + + XENO_ACTION_CHECK(xeno_player) + + if(HAS_TRAIT(xeno_player, TRAIT_ABILITY_REFLECTIVE_PLATES)) + remove_reflective_shield() + return + + if(!HAS_TRAIT(xeno_player, TRAIT_ABILITY_ENCLOSED_PLATES)) + xeno_player.balloon_alert(xeno_player, "we need to tense up our plates!", text_color = "#7d32bb", delay = 1 SECONDS) + return + + if(!check_and_use_plasma_owner(80)) + return + + xeno_player.stop_pulling() + ADD_TRAIT(xeno_player, TRAIT_ABILITY_REFLECTIVE_PLATES, TRAIT_SOURCE_ABILITY("reflective_plates")) + xeno_player.flags_atom |= DIRLOCK + xeno_player.update_icons() + xeno_player.create_shield(BULWARK_REFLECTIVE_TIME, "shield2") + button.icon_state = "template_active" + reflective_start_time = world.time + reflective_safe_click_cooldown = world.time + 1 SECONDS + + to_chat(xeno_player, SPAN_XENOWARNING("We adjust our plates and prepare for incoming frontal attacks!")) + xeno_player.visible_message(SPAN_XENOWARNING("[xeno_player] locks its stance, focusing on incoming frontal attacks!")) + + if(reflective_shield_timer_id != TIMER_ID_NULL) + deltimer(reflective_shield_timer_id) + + reflective_shield_timer_id = addtimer(CALLBACK(src, PROC_REF(remove_reflective_shield)), BULWARK_REFLECTIVE_TIME, TIMER_STOPPABLE) + + apply_cooldown() + return ..() + +/datum/action/xeno_action/onclick/reflective_shield/proc/remove_reflective_shield() + var/mob/living/carbon/xenomorph/warrior/xeno_player = owner + + var/datum/action/xeno_action/onclick/reflective_shield/ability_used = get_action(xeno_player, /datum/action/xeno_action/onclick/reflective_shield) + if(!istype(ability_used)) + return + + if(!HAS_TRAIT(xeno_player, TRAIT_ABILITY_REFLECTIVE_PLATES)) + return + + if(world.time < ability_used.reflective_safe_click_cooldown) + to_chat(xeno_player, SPAN_XENOWARNING("We need a moment before breaking our reflective stance!")) + return + + REMOVE_TRAIT(xeno_player, TRAIT_ABILITY_REFLECTIVE_PLATES, TRAIT_SOURCE_ABILITY("reflective_plates")) + xeno_player.update_icons() + xeno_player.remove_suit_layer() + button.icon_state = "template_xeno" + to_chat(xeno_player, SPAN_XENOWARNING("We adjust our plates and stance back to normal.")) + + if(ability_used.reflective_shield_timer_id != TIMER_ID_NULL) + deltimer(ability_used.reflective_shield_timer_id) + ability_used.reflective_shield_timer_id = TIMER_ID_NULL + + if(ability_used.reflective_start_time > 0) + var/used_ratio = round((world.time - ability_used.reflective_start_time) / ability_used.duration, 0.1) + ability_used.reflective_recharge_time = (BULWARK_REFLECTIVE_TIME * used_ratio * ability_used.reflective_refund_multiplier) + 6 SECONDS + + ability_used.reflective_start_time = -1 + apply_cooldown_override(ability_used.reflective_recharge_time) + +/mob/living/carbon/xenomorph/warrior/get_reflection_chance(obj/projectile/bullet) + var/datum/behavior_delegate/warrior_bulwark/behavior = src.behavior_delegate + if(!istype(behavior)) + return + + if(!HAS_TRAIT(src, TRAIT_ABILITY_REFLECTIVE_PLATES)) + return + + if(body_position == LYING_DOWN || stat == UNCONSCIOUS) + return //we don't want to reflect bullets when we are laying down/unconscious. + + if(bullet.ammo.flags_ammo_behavior & (AMMO_SNIPER|AMMO_ROCKET|AMMO_XENO|AMMO_NO_DEFLECT|AMMO_SKIPS_ALIENS)) + return //we don't want to reflect sniper bullets, rockets, anti-reflection bullets, xeno bullets and friendly bullets. + + var/projectile_dir = 0 + + if(!bullet.firer) + return + + projectile_dir = bullet.dir + + if(src.dir & REVERSE_DIR(projectile_dir)) + return BULWARK_REFLECTION_CHANCE_FRONT + + for(var/side_dir in get_perpen_dir(src.dir)) + if(projectile_dir == side_dir) + return 0 + + return + +// +// Custom Proc(s) +// + +/datum/action/xeno_action/proc/apply_custom_cooldown() + apply_cooldown() + xeno_cooldown = initial(xeno_cooldown) //We revert cooldown back to original value (after it got applied) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/bortrough.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/bortrough.dm new file mode 100644 index 000000000000..23bc779962d3 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/bortrough.dm @@ -0,0 +1,65 @@ +#define ALIGATOR_SPEED_DRAGING 2.8 +#define LIZARD_SPEED_NORMAL 1.2 + + +/mob/living/simple_animal/hostile/retaliate/giant_lizard/bortrough + name = "Aligator" + icon = 'icons/mob/bortrough.dmi' + icon_state = "Bortrough Running" + icon_living = "Bortrough Running" + icon_dead = "Bortrough Dead" + stun_duration = 2 + grab_level = GRAB_CHOKE + base_state = "Bortrough" + death_sound = 'sound/effects/bortrough-die.mp3' + growl_sound = "bortrough-chuff" + hiss_sound = "bortrough-hurt" + wound_icon = 'icons/mob/bortrough.dmi' + pixel_x = -22 + var/pulling_state = "Bortrough Running Open Jaws" + +/mob/living/simple_animal/hostile/retaliate/giant_lizard/bortrough/update_transform(instant_update = FALSE) + . = ..() + if(pulling) + icon_state = "Bortrough Running Open Jaws" + if(istype(loc, /turf/open/gm/river) && stat != DEAD) + icon_state = "Bortrough Submerged" + +/mob/living/simple_animal/hostile/retaliate/giant_lizard/bortrough/Move(NewLoc, direct) + . = ..() + if(istype(loc, /turf/open/gm/river) && stat != DEAD) + icon_state = "Bortrough Submerged" + update_wounds() + else + if(icon_state == "Bortrough Submerged") + update_transform() + update_wounds() + +/mob/living/simple_animal/hostile/retaliate/giant_lizard/bortrough/update_wounds() + . = ..() + if(istype(loc, /turf/open/gm/river) && stat != DEAD) + wound_icon_holder.icon_state = "none" + +/mob/living/simple_animal/hostile/retaliate/giant_lizard/bortrough/try_to_extinguish() + if(istype(get_turf(src), /turf/open/gm/river) || (/obj/effect/blocker/water in loc) || istype(get_turf(src), /turf/open/beach/coastline) || istype(get_turf(src), /turf/open/gm/coast)) + ExtinguishMob() + . = ..() + +/mob/living/simple_animal/hostile/retaliate/giant_lizard/bortrough/pounced_mob(mob/living/pounced_mob) + . = ..() + throwing = 0 + start_pulling(pounced_mob, TRUE, simple_mob = TRUE) + MoveTo(target_mob_ref?.resolve(), 5, TRUE, 2 SECONDS, TRUE) //drag our target away + +/mob/living/simple_animal/hostile/retaliate/giant_lizard/bortrough/start_pulling(atom/movable/clone/AM, lunge, no_msg, simple_mob) + . = ..() + if(.) + update_transform() + speed = ALIGATOR_SPEED_DRAGING +/mob/living/simple_animal/hostile/retaliate/giant_lizard/bortrough/stop_pulling() + . = ..() + speed = LIZARD_SPEED_NORMAL + update_transform() + +#undef ALIGATOR_SPEED_DRAGING +#undef LIZARD_SPEED_NORMAL diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/giant_lizard.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_lizard.dm index 02205b913e01..ef4e36a31207 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/giant_lizard.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/giant_lizard.dm @@ -43,6 +43,17 @@ attack_same = FALSE langchat_color = LIGHT_COLOR_GREEN + ///used for the other icons to be modular + var/base_state = "Giant Lizard" + + var/death_sound = 'sound/effects/giant_lizard_death.ogg' + + var/growl_sound = "giant_lizard_growl" + + var/hiss_sound = "giant_lizard_hiss" + + var/wound_icon = 'icons/mob/mob_64.dmi' + ///Reference to the ZZzzz sleep overlay when resting. var/sleep_overlay ///Reference to the tongue flick overlay. @@ -89,6 +100,8 @@ var/list/acceptable_foods = list(/obj/item/reagent_container/food/snacks/mre_food, /obj/item/reagent_container/food/snacks/resin_fruit) ///Is the mob currently eating the food_target? var/is_eating = FALSE + ///How long do we stun the pounced target for + var/stun_duration = 0.5 ///Cooldown dictating how long the mob will wait between eating food. COOLDOWN_DECLARE(food_cooldown) @@ -148,6 +161,7 @@ /mob/living/simple_animal/hostile/retaliate/giant_lizard/Initialize() . = ..() wound_icon_holder = new(null, src) + wound_icon_holder.icon = wound_icon tongue_icon_holder = new(null, src) tongue_icon_holder.pixel_x = 2 vis_contents += wound_icon_holder @@ -178,7 +192,7 @@ manual_emote("growls at [target_mob].") else manual_emote("growls.") - playsound(loc, "giant_lizard_growl", 60) + playsound(loc, growl_sound, 60) COOLDOWN_START(src, growl_message, rand(10, 14) SECONDS) /mob/living/simple_animal/hostile/retaliate/giant_lizard/get_status_tab_items() @@ -237,9 +251,9 @@ icon_state = icon_dead else if(body_position == LYING_DOWN) if(!HAS_TRAIT(src, TRAIT_INCAPACITATED) && !HAS_TRAIT(src, TRAIT_FLOORED)) - icon_state = "Giant Lizard Sleeping" + icon_state = "[base_state] Sleeping" else - icon_state = "Giant Lizard Knocked Down" + icon_state = "[base_state] Knocked Down" tongue_icon_holder.alpha = 0 //we can't stop an animation that's called via flick(). best we can do is hide it. else @@ -271,11 +285,11 @@ wound_icon_holder.icon_state = "none" else if(body_position == LYING_DOWN) if(!HAS_TRAIT(src, TRAIT_INCAPACITATED) && !HAS_TRAIT(src, TRAIT_FLOORED)) - wound_icon_holder.icon_state = "Giant Lizard [health_threshold] Rest" + wound_icon_holder.icon_state = "[base_state] [health_threshold] Rest" else - wound_icon_holder.icon_state = "Giant Lizard [health_threshold] Stun" + wound_icon_holder.icon_state = "[base_state] [health_threshold] Stun" else - wound_icon_holder.icon_state = "Giant Lizard [health_threshold]" + wound_icon_holder.icon_state = "[base_state] [health_threshold]" #undef NO_WOUNDS #undef SMALL_WOUNDS @@ -313,7 +327,7 @@ return ..() /mob/living/simple_animal/hostile/retaliate/giant_lizard/death(datum/cause_data/cause_data, gibbed = FALSE, deathmessage = "lets out a waning growl....") - playsound(loc, 'sound/effects/giant_lizard_death.ogg', 70) + playsound(loc, death_sound, 70) GLOB.giant_lizards_alive -= src return ..() @@ -355,7 +369,7 @@ COOLDOWN_START(src, emote_cooldown, rand(5, 8) SECONDS) manual_emote(pick(pick(pet_emotes), "stares at [attacking_mob].", "nuzzles [attacking_mob].", "licks [attacking_mob]'s hand."), "nibbles [attacking_mob]'s arm.") if(prob(50)) - playsound(loc, "giant_lizard_hiss", 25) + playsound(loc, hiss_sound, 25) flick("Giant Lizard Tongue", tongue_icon_holder) if(attacking_mob.a_intent == INTENT_DISARM && prob(25)) playsound(loc, 'sound/weapons/alien_knockdown.ogg', 25, 1) @@ -397,7 +411,7 @@ food_target_ref = null is_eating = FALSE manual_emote("hisses in agony!") - playsound(src, "giant_lizard_hiss", 40) + playsound(src, hiss_sound, 40) MoveTo(null, 9, TRUE, 4 SECONDS, FALSE) COOLDOWN_START(src, calm_cooldown, 8 SECONDS) @@ -932,8 +946,8 @@ playsound(loc, 'sound/weapons/alien_knockdown.ogg', 25, 1) return - playsound(loc, "giant_lizard_hiss", 25) - pounced_mob.KnockDown(0.5) + playsound(loc, hiss_sound, 25) + pounced_mob.KnockDown(stun_duration) step_to(src, pounced_mob) if(!client && !(pounced_mob.faction in faction_group)) ravagingattack(pounced_mob) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 779969ddf073..8acd2b9760a5 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -471,11 +471,11 @@ /mob/proc/start_pulling(atom/movable/AM, lunge, no_msg) return -/mob/living/start_pulling(atom/movable/clone/AM, lunge, no_msg) +/mob/living/start_pulling(atom/movable/clone/AM, lunge, no_msg, simple_mob = FALSE) if(istype(AM, /atom/movable/clone)) AM = AM.mstr //If AM is a clone, refer to the real target - if ( QDELETED(AM) || !usr || src==AM || !isturf(loc) || !isturf(AM.loc) ) //if there's no person pulling OR the person is pulling themself OR the object being pulled is inside something: abort! + if ( QDELETED(AM) || (!usr && !simple_mob) || src==AM || !isturf(loc) || !isturf(AM.loc) ) //if there's no person pulling OR the person is pulling themself OR the object being pulled is inside something: abort! return if (AM.anchored || AM.throwing) @@ -725,6 +725,8 @@ note dizziness decrements automatically in the mob's Life() proc. if(!canface()) return 0 if(dir != ndir) + if(HAS_TRAIT(src, TRAIT_ABILITY_REFLECTIVE_PLATES)) + return flags_atom &= ~DIRLOCK setDir(ndir) if(buckled && !buckled.anchored) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index f180e11be404..c8c3d6c08d4c 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -186,7 +186,8 @@ CLIENT_VERB(drop_item) move_delay += mob.next_move_slowdown mob.next_move_slowdown = 0 if((mob.flags_atom & DIRLOCK) && mob.dir != direct) - move_delay += MOVE_REDUCTION_DIRECTION_LOCKED // by Geeves + if(!HAS_TRAIT(mob, TRAIT_NO_DIR_LOCK_SLOWDOWN)) + move_delay += MOVE_REDUCTION_DIRECTION_LOCKED // by Geeves mob.cur_speed = clamp(10/(move_delay + 0.5), MIN_SPEED, MAX_SPEED) next_movement = world.time + MINIMAL_MOVEMENT_INTERVAL // We pre-set this now for the crawling case. If crawling do_after fails, next_movement would be set after the attempt end instead of now. diff --git a/code/modules/projectiles/guns/flamer/flamer.dm b/code/modules/projectiles/guns/flamer/flamer.dm index b4928ff6c07f..8a1147ef368b 100644 --- a/code/modules/projectiles/guns/flamer/flamer.dm +++ b/code/modules/projectiles/guns/flamer/flamer.dm @@ -328,6 +328,7 @@ else var/obj/effect/particle_effect/smoke/chem/checker = new() var/atom/blocked = LinkBlocked(checker, source_turf, turf) + qdel(checker) if(blocked) break @@ -899,38 +900,30 @@ GLOBAL_LIST_EMPTY(flamer_particles) else weather_smothering_strength = 0 -/proc/fire_spread_recur(turf/target, datum/cause_data/cause_data, remaining_distance, direction, fire_lvl, burn_lvl, f_color, burn_sprite = "dynamic", aerial_flame_level) - var/direction_angle = dir2angle(direction) +/proc/create_fire_reagent(fire_lvl, burn_lvl, f_color, burn_sprite = "dynamic") + var/datum/reagent/fire_reag = new() + fire_reag.intensityfire = burn_lvl + fire_reag.durationfire = fire_lvl + fire_reag.burn_sprite = burn_sprite + fire_reag.burncolor = f_color + return fire_reag + +/proc/fire_spread_recur(turf/target, datum/cause_data/cause_data, remaining_distance, direction, datum/reagent/fire_reagent, aerial_flame_level) + set waitfor = FALSE var/obj/flamer_fire/foundflame = locate() in target if(!foundflame) - var/datum/reagent/fire_reag = new() - fire_reag.intensityfire = burn_lvl - fire_reag.durationfire = fire_lvl - fire_reag.burn_sprite = burn_sprite - fire_reag.burncolor = f_color - new/obj/flamer_fire(target, cause_data, fire_reag) + new/obj/flamer_fire(target, cause_data, fire_reagent) if(target.density) return - for(var/spread_direction in GLOB.alldirs) - + for(var/spread_angle in list(-45, 0, 45)) + var/spread_direction = turn(direction, spread_angle) var/spread_power = remaining_distance - - var/spread_direction_angle = dir2angle(spread_direction) - - var/angle = 180 - abs( abs( direction_angle - spread_direction_angle ) - 180 ) // the angle difference between the spread direction and initial direction - - switch(angle) //this reduces power when the explosion is going around corners - if (45) - spread_power *= 0.75 - if (90 to 180) //turns out angles greater than 90 degrees almost never happen. This bit also prevents trying to spread backwards - continue - - switch(spread_direction) - if(NORTH,SOUTH,EAST,WEST) - spread_power-- - else - spread_power -= 1.414 //diagonal spreading + if(abs(spread_angle) == 45) // diagonal + spread_power -= sqrt(2) + spread_power *= 0.75 //this reduces power when the explosion is going around corners + else // cardinal + spread_power -= 1 if (spread_power < 1) continue @@ -942,37 +935,33 @@ GLOBAL_LIST_EMPTY(flamer_particles) if(aerial_flame_level) if(picked_turf.get_pylon_protection_level() >= aerial_flame_level) - break - var/area/picked_area = get_area(picked_turf) + continue + var/area/picked_area = picked_turf.loc // get_area() is slower here when we know we have a turf if(CEILING_IS_PROTECTED(picked_area?.ceiling, get_ceiling_protection_level(aerial_flame_level))) - break - - spawn(0) - fire_spread_recur(picked_turf, cause_data, spread_power, spread_direction, fire_lvl, burn_lvl, f_color, burn_sprite, aerial_flame_level) + continue -/proc/fire_spread(turf/target, datum/cause_data/cause_data, range, fire_lvl, burn_lvl, f_color, burn_sprite = "dynamic", aerial_flame_level = TURF_PROTECTION_NONE) - var/datum/reagent/fire_reag = new() - fire_reag.intensityfire = burn_lvl - fire_reag.durationfire = fire_lvl - fire_reag.burn_sprite = burn_sprite - fire_reag.burncolor = f_color + CHECK_TICK // before the recursive call + fire_spread_recur(picked_turf, cause_data, spread_power, spread_direction, fire_reagent, aerial_flame_level) - new/obj/flamer_fire(target, cause_data, fire_reag) - for(var/direction in GLOB.alldirs) +/proc/fire_spread(turf/target, datum/cause_data/cause_data, range, fire_reagent, aerial_flame_level = TURF_PROTECTION_NONE) + set waitfor = FALSE + new/obj/flamer_fire(target, cause_data, fire_reagent) + for(var/turf/picked_turf in orange(1, target)) + var/direction = get_dir(target, picked_turf) var/spread_power = range switch(direction) if(NORTH,SOUTH,EAST,WEST) spread_power-- else spread_power -= 1.414 //diagonal spreading - var/turf/picked_turf = get_step(target, direction) if(aerial_flame_level) if(picked_turf.get_pylon_protection_level() >= aerial_flame_level) continue var/area/picked_area = get_area(picked_turf) if(CEILING_IS_PROTECTED(picked_area?.ceiling, get_ceiling_protection_level(aerial_flame_level))) continue - fire_spread_recur(picked_turf, cause_data, spread_power, direction, fire_lvl, burn_lvl, f_color, burn_sprite, aerial_flame_level) + fire_spread_recur(picked_turf, cause_data, spread_power, direction, fire_reagent, aerial_flame_level) + CHECK_TICK // don't overrun spreading in just one direction // So it doens't do the spinny animation /obj/flamer_fire/onZImpact(turf/impact_turf, height) diff --git a/code/modules/projectiles/guns/lever_action.dm b/code/modules/projectiles/guns/lever_action.dm index 29dfd3639e94..507df3586769 100644 --- a/code/modules/projectiles/guns/lever_action.dm +++ b/code/modules/projectiles/guns/lever_action.dm @@ -65,30 +65,32 @@ their unique feature is that a direct hit will buff your damage and firerate /obj/item/weapon/gun/lever_action/set_gun_attachment_offsets() attachable_offset = list("muzzle_x" = 33, "muzzle_y" = 19, "rail_x" = 11, "rail_y" = 21, "under_x" = 24, "under_y" = 16, "stock_x" = 15, "stock_y" = 11) -/obj/item/weapon/gun/lever_action/wield(mob/M) +/obj/item/weapon/gun/lever_action/wield(mob/gun_wielder) . = ..() if(. && (flags_gun_lever_action & USES_STREAKS)) - RegisterSignal(M, COMSIG_BULLET_DIRECT_HIT, PROC_REF(direct_hit_buff)) + RegisterSignal(gun_wielder, COMSIG_BULLET_DIRECT_HIT, PROC_REF(direct_hit_buff)) -/obj/item/weapon/gun/lever_action/unwield(mob/M) +/obj/item/weapon/gun/lever_action/unwield(mob/gun_wielder) . = ..() if(. && (flags_gun_lever_action & USES_STREAKS)) - UnregisterSignal(M, COMSIG_BULLET_DIRECT_HIT) + UnregisterSignal(gun_wielder, COMSIG_BULLET_DIRECT_HIT) /obj/item/weapon/gun/lever_action/dropped(mob/user) . = ..() reset_hit_buff(user) addtimer(VARSET_CALLBACK(src, cur_onehand_chance, reset_onehand_chance), 4 SECONDS, TIMER_OVERRIDE|TIMER_UNIQUE) -/obj/item/weapon/gun/lever_action/proc/direct_hit_buff(mob/user, mob/target, one_hand_lever = FALSE) +/obj/item/weapon/gun/lever_action/proc/direct_hit_buff(mob/user, mob/target, obj/item/weapon/gun/projectile_source, one_hand_lever = FALSE) SIGNAL_HANDLER + if(projectile_source != src) + return FALSE var/mob/living/carbon/human/human_user = user if(one_hand_lever && !(flags_gun_lever_action & DANGEROUS_TO_ONEHAND_LEVER)) - return + return FALSE else if(one_hand_lever) //base marines should never be able to easily pass the skillcheck, only specialists and etc. if(prob(cur_onehand_chance) || skillcheck(human_user, SKILL_FIREARMS, SKILL_FIREARMS_SKILLED)) cur_onehand_chance = cur_onehand_chance - 20 //gets steadily worse if you spam it - return + return FALSE else to_chat(user, SPAN_DANGER("Augh! Your hand catches on the [lever_name]!!")) var/obj/limb/O = human_user.get_limb(human_user.hand ? "l_hand" : "r_hand") @@ -98,13 +100,13 @@ their unique feature is that a direct hit will buff your damage and firerate O.fracture() O.status &= ~LIMB_SPLINTED human_user.pain.recalculate_pain() - return + return FALSE if(!istype(target)) - return //sanity... + return FALSE //sanity... else if(target.stat == DEAD || !(flags_gun_lever_action & USES_STREAKS)) - return + return FALSE else if(streak) @@ -114,7 +116,7 @@ their unique feature is that a direct hit will buff your damage and firerate streak++ playsound(user, lever_hitsound, 25, FALSE) if(!(flags_gun_lever_action & USES_STREAKS)) - return + return FALSE apply_hit_buff(user, target, one_hand_lever) //this is a separate proc so it's configgable addtimer(CALLBACK(src, PROC_REF(reset_hit_buff), user, one_hand_lever), hit_buff_reset_cooldown, TIMER_OVERRIDE|TIMER_UNIQUE) @@ -286,7 +288,7 @@ their unique feature is that a direct hit will buff your damage and firerate if(flags_gun_lever_action & MOVES_WHEN_LEVERING) to_chat(user, SPAN_WARNING("You spin \the [src] one-handed! Fuck yeah!")) animation_wrist_flick(src) - direct_hit_buff(user, ,TRUE) + direct_hit_buff(user, projectile_source=src, one_hand_lever=TRUE) /obj/item/weapon/gun/lever_action/reload_into_chamber(mob/user) if(!current_mag) @@ -503,21 +505,22 @@ their unique feature is that a direct hit will buff your damage and firerate /obj/item/weapon/gun/lever_action/xm88/Fire(atom/target, mob/living/user, params, reflex, dual_wield) if(!able_to_fire(user) || !target) //checks here since we don't want to fuck up applying the increase return NONE - if(floating_penetration && in_chamber) //has to go before actual firing - var/obj/projectile/P = in_chamber + if(in_chamber) //has to go before actual firing + var/obj/projectile/projectile_to_shoot = in_chamber switch(floating_penetration) if(FLOATING_PENETRATION_TIER_1) - P.ammo = GLOB.ammo_list[/datum/ammo/bullet/lever_action/xm88/pen20] + projectile_to_shoot.ammo = GLOB.ammo_list[/datum/ammo/bullet/lever_action/xm88/pen20] direct_hit_sound = "sound/weapons/gun_xm88_directhit_low.ogg" if(FLOATING_PENETRATION_TIER_2) - P.ammo = GLOB.ammo_list[/datum/ammo/bullet/lever_action/xm88/pen30] + projectile_to_shoot.ammo = GLOB.ammo_list[/datum/ammo/bullet/lever_action/xm88/pen30] direct_hit_sound = "sound/weapons/gun_xm88_directhit_medium.ogg" if(FLOATING_PENETRATION_TIER_3) - P.ammo = GLOB.ammo_list[/datum/ammo/bullet/lever_action/xm88/pen40] + projectile_to_shoot.ammo = GLOB.ammo_list[/datum/ammo/bullet/lever_action/xm88/pen40] direct_hit_sound = "sound/weapons/gun_xm88_directhit_medium.ogg" if(FLOATING_PENETRATION_TIER_4) - P.ammo = GLOB.ammo_list[/datum/ammo/bullet/lever_action/xm88/pen50] + projectile_to_shoot.ammo = GLOB.ammo_list[/datum/ammo/bullet/lever_action/xm88/pen50] direct_hit_sound = "sound/weapons/gun_xm88_directhit_high.ogg" + projectile_to_shoot.projectile_flags |= PROJECTILE_BULLSEYE return ..() /obj/item/weapon/gun/lever_action/xm88/unload(mob/user) @@ -538,8 +541,8 @@ their unique feature is that a direct hit will buff your damage and firerate cur_onehand_chance = initial(cur_onehand_chance) direct_hit_sound = "sound/weapons/gun_xm88_directhit_low.ogg" if(in_chamber) - var/obj/projectile/P = in_chamber - P.ammo = GLOB.ammo_list[/datum/ammo/bullet/lever_action/xm88] + var/obj/projectile/projectile_to_shoot = in_chamber + projectile_to_shoot.ammo = GLOB.ammo_list[/datum/ammo/bullet/lever_action/xm88] floating_penetration = FLOATING_PENETRATION_TIER_0 //these are init configs and so cannot be initial() set_fire_delay(FIRE_DELAY_TIER_1 + FIRE_DELAY_TIER_12) @@ -549,8 +552,10 @@ their unique feature is that a direct hit will buff your damage and firerate if(one_hand_lever) addtimer(VARSET_CALLBACK(src, cur_onehand_chance, reset_onehand_chance), 4 SECONDS, TIMER_OVERRIDE|TIMER_UNIQUE) -/obj/item/weapon/gun/lever_action/xm88/direct_hit_buff(mob/user, mob/target, one_hand_lever = FALSE) +/obj/item/weapon/gun/lever_action/xm88/direct_hit_buff(mob/user, mob/target, /obj/item/weapon/gun/projectile_source, one_hand_lever = FALSE) . = ..() + if(!.) + return playsound(target, direct_hit_sound, 75) #undef FLOATING_PENETRATION_TIER_0 diff --git a/code/modules/projectiles/magazines/sentries.dm b/code/modules/projectiles/magazines/sentries.dm index 7287b71e16f0..199516286a7d 100644 --- a/code/modules/projectiles/magazines/sentries.dm +++ b/code/modules/projectiles/magazines/sentries.dm @@ -34,7 +34,7 @@ desc = "An ammo drum of 50 12g buckshot drums for the UA 12-G Shotgun Sentry. Just feed it into the sentry gun's ammo port when its ammo is depleted." caliber = "12g" max_rounds = 50 - default_ammo = /datum/ammo/bullet/shotgun/buckshot + default_ammo = /datum/ammo/bullet/shotgun/buckshot/turret /obj/item/ammo_magazine/sentry/wy name = "H20 ammo drum (10x42mm Caseless)" diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 98852846bba6..83ecb9b43ce1 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -360,7 +360,7 @@ var/turf/current_turf = get_turf(src) var/turf/next_turf = popleft(path) - // Terminal projectiles (about to hit) are handled first for retarget logic + // Terminal projectiles (about to hit) are handled firer for retarget logic if((speed * world.tick_lag) >= get_dist(current_turf, target_turf)) SEND_SIGNAL(src, COMSIG_BULLET_TERMINAL) @@ -420,6 +420,8 @@ return FALSE if(turf.density) // Handle wall hit + if(turf in permutated) + return FALSE var/ammo_flags = ammo.flags_ammo_behavior | projectile_override_flags if(SEND_SIGNAL(src, COMSIG_BULLET_PRE_HANDLE_TURF, turf) & COMPONENT_BULLET_PASS_THROUGH) @@ -506,19 +508,33 @@ if(SEND_SIGNAL(src, COMSIG_BULLET_POST_HANDLE_OBJ, obj, .) & COMPONENT_BULLET_PASS_THROUGH) return FALSE -/obj/projectile/proc/handle_mob(mob/living/living) +/obj/projectile/proc/handle_mob(mob/living/target_living) // If we've already handled this atom, don't do it again - if(SEND_SIGNAL(src, COMSIG_BULLET_PRE_HANDLE_MOB, living, .) & COMPONENT_BULLET_PASS_THROUGH) + if(SEND_SIGNAL(src, COMSIG_BULLET_PRE_HANDLE_MOB, target_living, .) & COMPONENT_BULLET_PASS_THROUGH) return FALSE - if((MODE_HAS_MODIFIER(/datum/gamemode_modifier/disable_attacking_corpses) && living.stat == DEAD) || (living in permutated)) + if((MODE_HAS_MODIFIER(/datum/gamemode_modifier/disable_attacking_corpses) && target_living.stat == DEAD) || (target_living in permutated)) return FALSE - permutated |= living - if((ammo.flags_ammo_behavior & AMMO_XENO) && (isfacehugger(living) || living.stat == DEAD)) //xeno ammo is NEVER meant to hit or damage dead people. If you want to add a xeno ammo that DOES then make a new flag that makes it ignore this check. + + if(isxeno(target_living)) + var/mob/living/carbon/xenomorph/xeno = target_living + var/directional_chance = xeno.get_reflection_chance(src) + if(directional_chance > 0 && prob(directional_chance)) + src.reflect_projectile_at_firer( + reflector = xeno, + new_firer = xeno, + damage_multiplier = BULWARK_REFLECTED_BULLET_DAMAGE, + accuracy_override = BULWARK_REFLECTED_BULLET_ACCURACY, + angle_variance = 25 + ) + return TRUE + + permutated |= target_living + if((ammo.flags_ammo_behavior & AMMO_XENO) && (isfacehugger(target_living) || target_living.stat == DEAD)) //xeno ammo is NEVER meant to hit or damage dead people. If you want to add a xeno ammo that DOES then make a new flag that makes it ignore this check. return FALSE - var/hit_chance = living.get_projectile_hit_chance(src) + var/hit_chance = target_living.get_projectile_hit_chance(src) if(hit_chance) // Calculated from combination of both ammo accuracy and gun accuracy @@ -526,86 +542,86 @@ var/direct_hit = FALSE // Wasn't the clicked target - if(original != living) + if(original != target_living) def_zone = rand_zone() // Xenos get a RNG limb miss chance regardless of being clicked target or not, see below - else if(isxeno(living) && hit_roll > hit_chance - 20) + else if(isxeno(target_living) && hit_roll > hit_chance - 20) def_zone = rand_zone() // Other targets do the same roll with penalty - a near hit will hit but redirected to another limb - else if(!isxeno(living) && hit_roll > hit_chance - 20 - GLOB.base_miss_chance[def_zone]) + else if(!isxeno(target_living) && hit_roll > hit_chance - 20 - GLOB.base_miss_chance[def_zone]) def_zone = rand_zone() else direct_hit = TRUE - if(firer) - SEND_SIGNAL(firer, COMSIG_BULLET_DIRECT_HIT, living) + if(firer && projectile_flags & PROJECTILE_BULLSEYE) + SEND_SIGNAL(firer, COMSIG_BULLET_DIRECT_HIT, target_living, shot_from) // At present, Xenos have no inherent effects or localized damage stemming from limb targeting // Therefore we exempt the shooter from direct hit accuracy penalties as well, // simply to avoid them from resetting target to chest every time they want to shoot a xeno - if(!direct_hit || !isxeno(living)) // For normal people or direct hits we apply the limb accuracy penalty + if(!direct_hit || !isxeno(target_living)) // For normal people or direct hits we apply the limb accuracy penalty hit_chance -= GLOB.base_miss_chance[def_zone] // else for direct hits on xenos, we skip it, pretending it's a chest shot with zero penalty #if DEBUG_HIT_CHANCE - to_world(SPAN_DEBUG("([living]) Hit chance: [hit_chance] | Roll: [hit_roll]")) + to_world(SPAN_DEBUG("([target_living]) Hit chance: [hit_chance] | Roll: [hit_roll]")) #endif - if(hit_chance > hit_roll && !(living.status_flags & RECENTSPAWN)) + if(hit_chance > hit_roll && !(target_living.status_flags & RECENTSPAWN)) #if DEBUG_HIT_CHANCE - to_world(SPAN_DEBUG("([living]) Hit.")) + to_world(SPAN_DEBUG("([target_living]) Hit.")) #endif var/ammo_flags = ammo.flags_ammo_behavior | projectile_override_flags // If the ammo should hit the surface of the target and there is a mob blocking // The current turf is the "surface" of the target if(ammo_flags & AMMO_STRIKES_SURFACE) - var/turf/turf = get_turf(living) + var/turf/turf = get_turf(target_living) // We "hit" the current turf but strike the actual blockage ammo.on_hit_turf(get_turf(src),src) turf.bullet_act(src) - else if(living && living.loc && (living.bullet_act(src) != -1)) - ammo.on_hit_mob(living,src, firer) + else if(target_living && target_living.loc && (target_living.bullet_act(src) != -1)) + ammo.on_hit_mob(target_living,src, firer) // If we are a xeno shooting something - if(istype(ammo, /datum/ammo/xeno) && isxeno(firer) && living.stat != DEAD && ammo.apply_delegate) + if(istype(ammo, /datum/ammo/xeno) && isxeno(firer) && target_living.stat != DEAD && ammo.apply_delegate) var/mob/living/carbon/xenomorph/xeno = firer if(xeno.behavior_delegate) var/datum/behavior_delegate/MD = xeno.behavior_delegate - MD.ranged_attack_additional_effects_target(living) - MD.ranged_attack_additional_effects_self(living) + MD.ranged_attack_additional_effects_target(target_living) + MD.ranged_attack_additional_effects_self(target_living) // If the thing we're hitting is a Xeno - if(istype(living, /mob/living/carbon/xenomorph)) - var/mob/living/carbon/xenomorph/xeno = living + if(istype(target_living, /mob/living/carbon/xenomorph)) + var/mob/living/carbon/xenomorph/xeno = target_living if(xeno.behavior_delegate) xeno.behavior_delegate.on_hitby_projectile(ammo) . = TRUE - else if(living.body_position != LYING_DOWN) - animatation_displace_reset(living) + else if(target_living.body_position != LYING_DOWN) + animatation_displace_reset(target_living) if(ammo.sound_miss) - playsound_client(living.client, ammo.sound_miss, get_turf(living), 75, TRUE) - living.visible_message(SPAN_AVOIDHARM("[src] misses [living]!"), + playsound_client(target_living.client, ammo.sound_miss, get_turf(target_living), 75, TRUE) + target_living.visible_message(SPAN_AVOIDHARM("[src] misses [target_living]!"), SPAN_AVOIDHARM("[src] narrowly misses you!"), null, 4, CHAT_TYPE_TAKING_HIT) - var/log_message = "[src] narrowly missed [key_name(living)]" + var/log_message = "[src] narrowly missed [key_name(target_living)]" var/mob/living/carbon/shotby = firer if(istype(shotby)) - living.attack_log += "\[[time_stamp()]\] [src], fired by [key_name(firer)], narrowly missed [key_name(living)]" - shotby.attack_log += "\[[time_stamp()]\] [src], fired by [key_name(shotby)], narrowly missed [key_name(living)]" - log_message = "[src], fired by [key_name(firer)], narrowly missed [key_name(living)]" + target_living.attack_log += "\[[time_stamp()]\] [src], fired by [key_name(firer)], narrowly missed [key_name(target_living)]" + shotby.attack_log += "\[[time_stamp()]\] [src], fired by [key_name(shotby)], narrowly missed [key_name(target_living)]" + log_message = "[src], fired by [key_name(firer)], narrowly missed [key_name(target_living)]" log_attack(log_message) #if DEBUG_HIT_CHANCE - to_world(SPAN_DEBUG("([living]) Missed.")) + to_world(SPAN_DEBUG("([target_living]) Missed.")) #endif - if(SEND_SIGNAL(src, COMSIG_BULLET_POST_HANDLE_MOB, living, .) & COMPONENT_BULLET_PASS_THROUGH) + if(SEND_SIGNAL(src, COMSIG_BULLET_POST_HANDLE_MOB, target_living, .) & COMPONENT_BULLET_PASS_THROUGH) return FALSE /obj/projectile/proc/check_canhit(turf/current_turf, turf/next_turf, list/ignore_list) @@ -1365,6 +1381,60 @@ . = ..() source_pill = null +/** + * explained: + * * reflector - what shoots projectiles back. + * * new_firer - who shoots projectiles back. + * * damage_multiplier - how much bullet damage get reflected, default is 0.5 (50%). + * * accuracy_override - used to override shoot bullet accuracy. + * * angle_variance - how big reflection cone should be, default is 25, final cone will be 50 degrees (-25 and 25) + * * range_override - how far reflected bullet will travel, overridies original projectile value with new one. + * * projectile_flag_override - should only be used if you want to change bullet to other type than shrapnel. + * * ignore_safety - if set to TRUE, allows "target" to reflect reflected bullets. + */ +/obj/projectile/proc/reflect_projectile_at_firer(atom/reflector, atom/new_firer, damage_multiplier = 0.5, accuracy_override, angle_variance = 25, range_override, projectile_flag_override = NONE, ignore_safety = FALSE) + if(!ammo) + return + + if(!firer) + return + + if(!ignore_safety) + if(projectile_flags & PROJECTILE_REFLECTED) // So we cannot reflect reflected, could create infinite* loop. + return + + var/turf/source_turf = get_turf(reflector) + if(!source_turf) + return + + var/obj/projectile/new_proj = new(source_turf,create_cause_data("[reflector]")) + new_proj.generate_bullet(ammo) + new_proj.damage = damage * damage_multiplier + + if(!isnull(accuracy_override)) + new_proj.accuracy = accuracy_override + else + new_proj.accuracy = accuracy + + if(projectile_flag_override) + new_proj.projectile_flags |= projectile_flag_override + else + new_proj.projectile_flags |= PROJECTILE_SHRAPNEL //we make it shrapnel unless overrided. + + new_proj.projectile_flags |= PROJECTILE_REFLECTED + new_proj.permutated |= src + new_proj.permutated |= reflector + + var/angle = Get_Angle(source_turf, firer) + angle += rand(-angle_variance, angle_variance) + var/atom/target = get_angle_target_turf(source_turf,angle,get_dist_sqrd(source_turf, firer)) + + if(!range_override) + range_override = max(ammo.max_range, 1) + + new_proj.fire_at(target,new_firer ? new_firer : firer,reflector,range_override,speed = ammo.shell_speed) + return new_proj + #undef DEBUG_HIT_CHANCE #undef DEBUG_HUMAN_DEFENSE #undef DEBUG_XENO_DEFENSE diff --git a/code/modules/reagents/chemistry_machinery/reagent_grinder.dm b/code/modules/reagents/chemistry_machinery/reagent_grinder.dm index 2f219651475a..187c8610d2fa 100644 --- a/code/modules/reagents/chemistry_machinery/reagent_grinder.dm +++ b/code/modules/reagents/chemistry_machinery/reagent_grinder.dm @@ -477,3 +477,7 @@ /obj/structure/machinery/reagentgrinder/industrial/update_icon() icon_state = "industry"+num2text(!isnull(beaker)) return + +/obj/structure/machinery/reagentgrinder/yautja + name = "Bone Grinder" + icon = 'icons/obj/structures/machinery/yautja_machines.dmi' diff --git a/code/modules/reagents/chemistry_reagents/toxin.dm b/code/modules/reagents/chemistry_reagents/toxin.dm index fef31ace6b8d..238c0e2524ae 100644 --- a/code/modules/reagents/chemistry_reagents/toxin.dm +++ b/code/modules/reagents/chemistry_reagents/toxin.dm @@ -80,7 +80,7 @@ color = "#CF3600" // rgb: 207, 54, 0 custom_metabolism = AMOUNT_PER_TIME(1, 5 SECONDS) chemclass = CHEM_CLASS_HYDRO - properties = list(PROPERTY_HYPOXEMIC = 4, PROPERTY_HYPNOTIC = 1) + properties = list(PROPERTY_HYPOXEMIC = 12, PROPERTY_NEUROTOXIC = 4, PROPERTY_HYPNOTIC = 5) /datum/reagent/toxin/minttoxin name = "Mint Toxin" diff --git a/colonialmarines.dme b/colonialmarines.dme index ef428f6aba0c..ee54c751f2fa 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -618,7 +618,6 @@ #include "code\datums\entities\player_sticky_ban.dm" #include "code\datums\entities\player_times.dm" #include "code\datums\entities\ticket.dm" -#include "code\datums\entities\twitch_link.dm" #include "code\datums\entities\logs\player_times_log.dm" #include "code\datums\factions\clf.dm" #include "code\datums\factions\cmb.dm" @@ -2302,6 +2301,7 @@ #include "code\modules\mob\living\carbon\xenomorph\strains\castes\ravager\berserker.dm" #include "code\modules\mob\living\carbon\xenomorph\strains\castes\ravager\hedgehog.dm" #include "code\modules\mob\living\carbon\xenomorph\strains\castes\runner\acider.dm" +#include "code\modules\mob\living\carbon\xenomorph\strains\castes\warrior\bulwark.dm" #include "code\modules\mob\living\silicon\death.dm" #include "code\modules\mob\living\silicon\login.dm" #include "code\modules\mob\living\silicon\say.dm" @@ -2328,6 +2328,7 @@ #include "code\modules\mob\living\simple_animal\hostile\giant_spider.dm" #include "code\modules\mob\living\simple_animal\hostile\hostile.dm" #include "code\modules\mob\living\simple_animal\hostile\tree.dm" +#include "code\modules\mob\living\simple_animal\hostile\retaliate\bortrough.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\giant_lizard.dm" diff --git a/html/changelogs/AutoChangeLog-pr-11720.yml b/html/changelogs/AutoChangeLog-pr-11720.yml new file mode 100644 index 000000000000..c524ab9ba039 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-11720.yml @@ -0,0 +1,7 @@ +author: "Itus" +delete-after: True +changes: + - code_imp: "Rankpins.Dm got every navy rank shoulder patch renamed appropriately and icon'ed" + - refactor: "Paygrades file got some changes to rank names." + - imageadd: "adds inventory overlays(which work), Onmob sprites for the patches and item sprites." + - spellcheck: "fixed the N36 -> NE6 typo" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-11902.yml b/html/changelogs/AutoChangeLog-pr-11902.yml new file mode 100644 index 000000000000..cc3d101e6d1c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-11902.yml @@ -0,0 +1,4 @@ +author: "hry-gh" +delete-after: True +changes: + - server: "events sent to sentry are now scrubbed for user PII and ignoring sensitive datums, procs" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-11953.yml b/html/changelogs/AutoChangeLog-pr-11953.yml new file mode 100644 index 000000000000..19fb8a905000 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-11953.yml @@ -0,0 +1,6 @@ +author: "Riot" +delete-after: True +changes: + - balance: "Buffs the IEDs used by the CLF" + - balance: "CLF have combat flashlights now." + - balance: "Cyanide kills you faster now" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12119.yml b/html/changelogs/AutoChangeLog-pr-12119.yml new file mode 100644 index 000000000000..ce72d1133cf3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12119.yml @@ -0,0 +1,5 @@ +author: "EmeraldCandy" +delete-after: True +changes: + - rscadd: "Added optional customizable sound alerts for response teams" + - rscadd: "Predator response team calls now produce a bracer noise alert for ghosts." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12162.yml b/html/changelogs/AutoChangeLog-pr-12162.yml new file mode 100644 index 000000000000..daf103692bc3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12162.yml @@ -0,0 +1,4 @@ +author: "cuberound" +delete-after: True +changes: + - bugfix: "explosions on upper xeno walls destroy the lower portion too" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12192.yml b/html/changelogs/AutoChangeLog-pr-12192.yml new file mode 100644 index 000000000000..466671d10776 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12192.yml @@ -0,0 +1,4 @@ +author: "MistChristmas" +delete-after: True +changes: + - bugfix: "Sentry hits no longer count for lever action streaks" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12214.yml b/html/changelogs/AutoChangeLog-pr-12214.yml new file mode 100644 index 000000000000..5e377ba44818 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12214.yml @@ -0,0 +1,4 @@ +author: "Ammoniacres" +delete-after: True +changes: + - rscadd: "made the hostile freelancer boarding message more clear in demonstrating hostile intent." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12278.yml b/html/changelogs/AutoChangeLog-pr-12278.yml new file mode 100644 index 000000000000..86eb786a986f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12278.yml @@ -0,0 +1,5 @@ +author: "kitchenenthusiast" +delete-after: True +changes: + - rscadd: "added an announcement clarity meter to Overwatch consoles so you no longer scream into the void, Lieutenant" + - ui: "added an announcement clarity meter to Overwatch consoles" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12360.yml b/html/changelogs/AutoChangeLog-pr-12360.yml new file mode 100644 index 000000000000..20b3d4493332 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12360.yml @@ -0,0 +1,4 @@ +author: "Drathek" +delete-after: True +changes: + - bugfix: "Fixed a couple errors with the debug mass screenshot verb when handling centered space levels and ever the mob is force aghosted" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12373.yml b/html/changelogs/AutoChangeLog-pr-12373.yml new file mode 100644 index 000000000000..665345299bb5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12373.yml @@ -0,0 +1,4 @@ +author: "Ammoniacres" +delete-after: True +changes: + - bugfix: "fixed unpowered doors being unable to be opened by maintenance jacks (for humans)" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12398.yml b/html/changelogs/AutoChangeLog-pr-12398.yml new file mode 100644 index 000000000000..cc15a17ca818 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12398.yml @@ -0,0 +1,4 @@ +author: "BlitzArde" +delete-after: True +changes: + - bugfix: "Trying to drench an acid-ed object in acid now properly states the object's name" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12402.yml b/html/changelogs/AutoChangeLog-pr-12402.yml new file mode 100644 index 000000000000..be7b4458572d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12402.yml @@ -0,0 +1,4 @@ +author: "ComboTombo" +delete-after: True +changes: + - rscadd: "Added a tip explaining that in-hand activation with a machete can quickly clear out foliage" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12420.yml b/html/changelogs/AutoChangeLog-pr-12420.yml new file mode 100644 index 000000000000..169690e97921 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12420.yml @@ -0,0 +1,4 @@ +author: "BlitzArde, Blundir" +delete-after: True +changes: + - soundtweak: "Replaced the disturbances hazmat joe sound file" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12428.yml b/html/changelogs/AutoChangeLog-pr-12428.yml new file mode 100644 index 000000000000..3a6f7a5099cc --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12428.yml @@ -0,0 +1,5 @@ +author: "DangerRevolution" +delete-after: True +changes: + - rscadd: "At 00:20, if there is no CMO, FD, Doctor, Researcher or Synthetic shipside then autodoc upgrade disks will be spawned on the ASRS elevator." + - rscadd: "At 00:20, if there is no CMO, FD, Doctor, Researcher or Synthetic then autodocs will have their Surgery-1 skill check removed. Anyone can use them in this situation." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12446.yml b/html/changelogs/AutoChangeLog-pr-12446.yml new file mode 100644 index 000000000000..0fa8717dc9da --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12446.yml @@ -0,0 +1,4 @@ +author: "cuberound" +delete-after: True +changes: + - bugfix: "moving under transparent turfs no longer cancels looking up" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12447.yml b/html/changelogs/AutoChangeLog-pr-12447.yml new file mode 100644 index 000000000000..001c13f83ecf --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12447.yml @@ -0,0 +1,4 @@ +author: "Unknownity" +delete-after: True +changes: + - rscadd: "The Hunter Ship has gained a reagent grinder." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-12466.yml b/html/changelogs/AutoChangeLog-pr-12466.yml new file mode 100644 index 000000000000..23b5064f9215 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-12466.yml @@ -0,0 +1,5 @@ +author: "Antlers" +delete-after: True +changes: + - bugfix: "Fixed an issue where a M56D could be attached to a mount that already had a gun" + - bugfix: "Fixed an issue where a disassembled M56D that already had a mount could be attached to another mount." \ No newline at end of file diff --git a/html/changelogs/archive/2026-06.yml b/html/changelogs/archive/2026-06.yml index e35fa410bac6..14c4a8236610 100644 --- a/html/changelogs/archive/2026-06.yml +++ b/html/changelogs/archive/2026-06.yml @@ -240,3 +240,65 @@ kiVts: - rscadd: Research can announce information about their chemicals to everyone with a medical hud. This requires CMO to publish their work first, though. +2026-06-19: + MistChristmas: + - bugfix: Overwatch overlays no longer get stuck +2026-06-20: + Antlers: + - bugfix: Hyperdyne corporate survivors no longer get aired out by LZ turrets +2026-06-22: + Drathek: + - bugfix: Fixed a runtime with custom smoke spawning + MoondancerPony: + - refactor: made fire spread (from OB, flamers, etc) much faster + NHC: + - bugfix: Removed duped sunglasses from loudout menu + Venuska1117: + - rscadd: Add new Warrior Strain "Bulwark". + - rscadd: Bulwark have 600 health, tackles of 2 min - 5max, gains 200 plasma, +10 + explosive and +10 normal armor for decreased speed and takes 50% more melee + damage. + - rscadd: Bulwark have 4 passive abilities, 50% less damage from wired cades (5 + damage instead of 10), 75% for slashes or tailstab to go through wired cades + and hit target behind them, +10 front armor and +10 side armor and no direct + lock slowdown. + - rscadd: Encased Plates, this ability when activated it will reduce your movement + speed by 1.35, increases minimum tackles needed to tackle someone down by +2 + and decreased slash damage by 8, in exchange you gain +10 front armor and lose + -10 side armor (stacks with passive), immunity to knockdowns and ability to + open holes in walls. + - rscadd: Plate Bash, this ability will launch you toward targets if its in range + of 2 tiles (3 tiles), it will hit them 1 tile away and shake their screen, if + encased plates are active, this ability changes to only work on adjecent targets + but in exchange you send them 3 tiles away, knockdown and stun target. + - rscadd: Tail Sweep, this ability will hit all adjecent tiles (including corners) + around you and trip enemies down and briefly stun them, if grenade is present + instead of target, you will send grenade 4 tiles away and your cooldown will + be decreased to 1/3rd of full cooldown, if both target and grenade are hit, + normal cooldown value will be applied. + - rscadd: Reflective Shield, this ability will lock your looking direction when + casted to reflect bullets towards target in 50 degree cone (-25 to 25 angle), + reflected bullets will only deal half of their original damage, your reflection + chance is 100% from front ONLY but you lose ability to attack or help (you can + still use abilities) and drop pulled targets, this ability can be disabled early + like how lurker invisibility works 1s used is 2s cooldown but you gain +6s to + cooldown, minimum 6s cooldown and 18s max, this ability CANNOT reflects sniper + bullets, rockets and anti-reflective ammo (like SHARP darts), xeno projectiles + and turrets. + - balance: Fix Reflective walls, now they reflect bullets properly (uses new bulwark + reflective proc). + cuberound: + - qol: xenos can see reamining durability of special structures on examine + - code_imp: isbanana is no longer + hry-gh: + - server: twitch linking functionality is deprecated in game + realforest2001: + - rscadd: Added Abomination pelt and skull items. + - code_imp: Added code to delete instances of skull and pelt that have no icon. + - rscadd: Almayer crash no longer breaks hardened APCs. + - code_imp: Changed some queen procs to xenomorph base. + - rscadd: Changed Queen naming convention (thought I did this ages ago) + - code_imp: Changed the tier_slot_multiplier var name to divisor because it was + stupidly named. + virtualgirlie: + - spellcheck: changed "enemy boiler's" to "enemy boilers" diff --git a/icons/mob/bortrough.dmi b/icons/mob/bortrough.dmi new file mode 100644 index 000000000000..06dae9c5cee5 Binary files /dev/null and b/icons/mob/bortrough.dmi differ diff --git a/icons/mob/hud/actions_xeno.dmi b/icons/mob/hud/actions_xeno.dmi index 4409d45a1470..1bb981218850 100644 Binary files a/icons/mob/hud/actions_xeno.dmi and b/icons/mob/hud/actions_xeno.dmi differ diff --git a/icons/mob/hud/hud.dmi b/icons/mob/hud/hud.dmi index bde26859a159..6f909a4d05ec 100644 Binary files a/icons/mob/hud/hud.dmi and b/icons/mob/hud/hud.dmi differ diff --git a/icons/mob/humans/onmob/clothing/accessory/ranks.dmi b/icons/mob/humans/onmob/clothing/accessory/ranks.dmi index aaae33ad5c38..677b69136780 100644 Binary files a/icons/mob/humans/onmob/clothing/accessory/ranks.dmi and b/icons/mob/humans/onmob/clothing/accessory/ranks.dmi differ diff --git a/icons/mob/xenos/castes/tier_2/warrior.dmi b/icons/mob/xenos/castes/tier_2/warrior.dmi index af3000a3dd5a..189ebac3e1ab 100644 Binary files a/icons/mob/xenos/castes/tier_2/warrior.dmi and b/icons/mob/xenos/castes/tier_2/warrior.dmi differ diff --git a/icons/obj/items/clothing/accessory/inventory_overlays/ranks.dmi b/icons/obj/items/clothing/accessory/inventory_overlays/ranks.dmi index 73de7673acd6..2413d0628b0a 100644 Binary files a/icons/obj/items/clothing/accessory/inventory_overlays/ranks.dmi and b/icons/obj/items/clothing/accessory/inventory_overlays/ranks.dmi differ diff --git a/icons/obj/items/clothing/accessory/ranks.dmi b/icons/obj/items/clothing/accessory/ranks.dmi index 412a559d2bea..1ce965ff6b83 100644 Binary files a/icons/obj/items/clothing/accessory/ranks.dmi and b/icons/obj/items/clothing/accessory/ranks.dmi differ diff --git a/icons/obj/items/hunter/prey_items.dmi b/icons/obj/items/hunter/prey_items.dmi index 654120767a8f..da22076cd39d 100644 Binary files a/icons/obj/items/hunter/prey_items.dmi and b/icons/obj/items/hunter/prey_items.dmi differ diff --git a/maps/map_files/Hunter_Ship/Hunter_Ship.dmm b/maps/map_files/Hunter_Ship/Hunter_Ship.dmm index ab5f13ea525d..5343c458d209 100644 --- a/maps/map_files/Hunter_Ship/Hunter_Ship.dmm +++ b/maps/map_files/Hunter_Ship/Hunter_Ship.dmm @@ -11350,14 +11350,14 @@ pixel_x = 7; density = 0 }, -/obj/structure/machinery/juicer/yautja{ - pixel_x = -9; - pixel_y = 16 - }, /obj/structure/window/reinforced/toughened{ dir = 4; color = "#912727" }, +/obj/structure/machinery/reagentgrinder/yautja{ + pixel_x = -9; + pixel_y = 16 + }, /turf/open/predship/tile/red4/glow, /area/yautja_ship/middle_deck/research) "zg" = ( diff --git a/sound/effects/bortrough-chuff1.mp3 b/sound/effects/bortrough-chuff1.mp3 new file mode 100644 index 000000000000..d472486f3499 Binary files /dev/null and b/sound/effects/bortrough-chuff1.mp3 differ diff --git a/sound/effects/bortrough-chuff2.mp3 b/sound/effects/bortrough-chuff2.mp3 new file mode 100644 index 000000000000..ffba1d3285fb Binary files /dev/null and b/sound/effects/bortrough-chuff2.mp3 differ diff --git a/sound/effects/bortrough-die.mp3 b/sound/effects/bortrough-die.mp3 new file mode 100644 index 000000000000..c098516a40dc Binary files /dev/null and b/sound/effects/bortrough-die.mp3 differ diff --git a/sound/effects/grenade_hit.ogg b/sound/effects/grenade_hit.ogg new file mode 100644 index 000000000000..f6a4afd789b9 Binary files /dev/null and b/sound/effects/grenade_hit.ogg differ diff --git a/sound/effects/tail_swing.ogg b/sound/effects/tail_swing.ogg new file mode 100644 index 000000000000..36912acfe647 Binary files /dev/null and b/sound/effects/tail_swing.ogg differ diff --git a/sound/voice/joe/disturbance_haz.ogg b/sound/voice/joe/disturbance_haz.ogg index cfe4110d5dfc..366cd7d6d537 100644 Binary files a/sound/voice/joe/disturbance_haz.ogg and b/sound/voice/joe/disturbance_haz.ogg differ diff --git a/strings/marinetips.txt b/strings/marinetips.txt index 5528877bc318..6094a210e45a 100644 --- a/strings/marinetips.txt +++ b/strings/marinetips.txt @@ -124,4 +124,4 @@ M37 is best used on harm intent and at point blank range. If you hit within 1 ti Marines take fewer bullets to kill than xenomorphs. Make sure to bring flares. No one wants to be blind against these things… The best time to kill a marine is when they don’t expect it. Hit and run is the bread and butter of xenos! Always be ready to fight back. Always assume a door will have ambushers behind it. - +You can clear vines, bushes, and tall grass in front of you quickly and efficiently by holding a machete and pressing your 'activate in-hand' hotkey (default: Z). diff --git a/tgui/packages/tgui/interfaces/OverwatchConsole.tsx b/tgui/packages/tgui/interfaces/OverwatchConsole.tsx index 9421b99f7d9f..a67324cb0ba9 100644 --- a/tgui/packages/tgui/interfaces/OverwatchConsole.tsx +++ b/tgui/packages/tgui/interfaces/OverwatchConsole.tsx @@ -8,6 +8,7 @@ import { Input, LabeledControls, NumberInput, + ProgressBar, Section, Stack, Table, @@ -69,6 +70,9 @@ type Data = { ob_safety: Boolean; supply_cooldown: number; operator: string; + radio_clarity: number; + clarity_color: string; + clarity_status: string; }; export const OverwatchConsole = (props) => { @@ -76,7 +80,7 @@ export const OverwatchConsole = (props) => { return ( @@ -213,77 +217,140 @@ const MainDashboard = (props) => { } > - - - PRIMARY ORDERS - SECONDARY ORDERS - - - - {primary_objective ? primary_objective : 'NONE'} - - - {secondary_objective ? secondary_objective : 'NONE'} - - -
- - - {primary_objective && ( - - )} - - {secondary_objective && ( - - )} - - - - - - + + + + + + PRIMARY ORDERS + + SECONDARY ORDERS + + + + {primary_objective ? primary_objective : 'NONE'} + + + {secondary_objective ? secondary_objective : 'NONE'} + + +
+ + + + + + {primary_objective && ( + + )} + + + + + + {secondary_objective && ( + + )} + + + + + + + + + + + +
+
+ + + + SIGNAL CLARITY + + + {data.clarity_status} + + + {''} + + + {data.radio_clarity}% + + + +
); };