diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm index b0e97e05e9..fdfec5e8ca 100644 --- a/code/__DEFINES/tgs.dm +++ b/code/__DEFINES/tgs.dm @@ -1,6 +1,6 @@ // tgstation-server DMAPI -#define TGS_DMAPI_VERSION "6.7.0" +#define TGS_DMAPI_VERSION "7.0.2" // All functions and datums outside this document are subject to change with any version and should not be relied on. @@ -73,12 +73,12 @@ #define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3 /// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path. #define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4 -/// Before a BYOND install operation begins. Parameters: [/datum/tgs_version] of the installing BYOND. -#define TGS_EVENT_BYOND_INSTALL_START 5 -/// When a BYOND install operation fails. Parameters: Error message -#define TGS_EVENT_BYOND_INSTALL_FAIL 6 -/// When the active BYOND version changes. Parameters: (Nullable) [/datum/tgs_version] of the current BYOND, [/datum/tgs_version] of the new BYOND. -#define TGS_EVENT_BYOND_ACTIVE_VERSION_CHANGE 7 +/// Before a engine install operation begins. Parameters: Version string of the installing engine. +#define TGS_EVENT_ENGINE_INSTALL_START 5 +/// When a engine install operation fails. Parameters: Error message +#define TGS_EVENT_ENGINE_INSTALL_FAIL 6 +/// When the active engine version changes. Parameters: (Nullable) Version string of the current engine, version string of the new engine. +#define TGS_EVENT_ENGINE_ACTIVE_VERSION_CHANGE 7 /// When the compiler starts running. Parameters: Game directory path, origin commit SHA. #define TGS_EVENT_COMPILE_START 8 /// When a compile is cancelled. No parameters. @@ -108,7 +108,7 @@ // #define TGS_EVENT_DREAM_DAEMON_LAUNCH 22 /// After a single submodule update is performed. Parameters: Updated submodule name. #define TGS_EVENT_REPO_SUBMODULE_UPDATE 23 -/// After CodeModifications are applied, before DreamMaker is run. Parameters: Game directory path, origin commit sha, byond version. +/// After CodeModifications are applied, before DreamMaker is run. Parameters: Game directory path, origin commit sha, version string of the used engine. #define TGS_EVENT_PRE_DREAM_MAKER 24 /// Whenever a deployment folder is deleted from disk. Parameters: Game directory path. #define TGS_EVENT_DEPLOYMENT_CLEANUP 25 @@ -122,6 +122,7 @@ /// The watchdog will restart on reboot. #define TGS_REBOOT_MODE_RESTART 2 +// Note that security levels are currently meaningless in OpenDream /// DreamDaemon Trusted security level. #define TGS_SECURITY_TRUSTED 0 /// DreamDaemon Safe security level. @@ -136,6 +137,11 @@ /// DreamDaemon invisible visibility level. #define TGS_VISIBILITY_INVISIBLE 2 +/// The Build Your Own Net Dream engine. +#define TGS_ENGINE_TYPE_BYOND 0 +/// The OpenDream engine. +#define TGS_ENGINE_TYPE_OPENDREAM 1 + //REQUIRED HOOKS /** @@ -420,6 +426,7 @@ /** * Send a message to connected chats. This function may sleep! + * If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game. * * message - The [/datum/tgs_message_content] to send. * admin_only: If [TRUE], message will be sent to admin connected chats. Vice-versa applies. @@ -429,6 +436,7 @@ /** * Send a private message to a specific user. This function may sleep! + * If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game. * * message - The [/datum/tgs_message_content] to send. * user: The [/datum/tgs_chat_user] to PM. @@ -438,6 +446,7 @@ /** * Send a message to connected chats that are flagged as game-related in TGS. This function may sleep! + * If TGS is offline when called, the message may be placed in a queue to be sent and this function will return immediately. Your message will be sent when TGS reconnects to the game. * * message - The [/datum/tgs_message_content] to send. * channels - Optional list of [/datum/tgs_chat_channel]s to restrict the message to. @@ -449,6 +458,10 @@ /world/proc/TgsVersion() return +/// Returns the running engine type +/world/proc/TgsEngine() + return + /// Returns the current [/datum/tgs_version] of the DMAPI being used if it was activated, null otherwise. This function may sleep if the call to [/world/proc/TgsNew] is sleeping! /world/proc/TgsApiVersion() return diff --git a/code/__DEFINES/xeno_ai.dm b/code/__DEFINES/xeno_ai.dm index 847f649fd5..a132aa28ed 100644 --- a/code/__DEFINES/xeno_ai.dm +++ b/code/__DEFINES/xeno_ai.dm @@ -23,7 +23,7 @@ PROBABILITY CALCULATIONS ARE HERE #define XENO_SLASH 80 -#define XENO_DOOR_BUILDING_CHANCE 25 +#define XENO_DOOR_BUILDING_CHANCE 40 #define PLASMA_RETREAT_PERCENTAGE 10 #define HEALTH_RETREAT_PERCENTAGE 20 @@ -91,3 +91,6 @@ PROBABILITY CALCULATIONS ARE HERE /// Special blockers for pathfinding or obstacle handling #define XENO_AI_SPECIAL_BLOCKERS list(/obj/flamer_fire, /obj/vehicle/multitile, /turf/open/space) + +// Friend-or-foe universal check +#define IS_SAME_HIVENUMBER(A,B) (A.hivenumber == B.hivenumber) diff --git a/code/datums/ammo/bullet/rifle.dm b/code/datums/ammo/bullet/rifle.dm index 4098235ba8..46b655a428 100644 --- a/code/datums/ammo/bullet/rifle.dm +++ b/code/datums/ammo/bullet/rifle.dm @@ -191,8 +191,7 @@ name = "depleted uranium bullet" damage = 60 - penetration = ARMOR_PENETRATION_TIER_10 - var/acid_per_hit = 15 //woe, toxin upon ye + penetration = ARMOR_PENETRATION_TIER_5 /datum/ammo/bullet/rifle/m4ra/du/set_bullet_traits() . = ..() @@ -201,7 +200,7 @@ )) /datum/ammo/bullet/rifle/m4ra/du/on_hit_mob(mob/target, obj/projectile/fired_proj) - target.AddComponent(/datum/component/toxic_buildup, acid_per_hit) + target.AddComponent(/datum/component/toxic_buildup) knockback(target, fired_proj, max_range = 2) diff --git a/code/datums/components/xeno/ai_behavior_overrides/attack_override_behavior.dm b/code/datums/components/xeno/ai_behavior_overrides/attack_override_behavior.dm index 2b2290586e..d996158840 100644 --- a/code/datums/components/xeno/ai_behavior_overrides/attack_override_behavior.dm +++ b/code/datums/components/xeno/ai_behavior_overrides/attack_override_behavior.dm @@ -27,7 +27,7 @@ return FALSE processing_xeno.current_target = parent - processing_xeno.resting = FALSE + processing_xeno.set_resting(FALSE, FALSE, TRUE) if(prob(5)) processing_xeno.emote("hiss") diff --git a/code/datums/components/xeno/ai_behavior_overrides/base_override_behavior.dm b/code/datums/components/xeno/ai_behavior_overrides/base_override_behavior.dm index a5bb0728de..1dbcb353d1 100644 --- a/code/datums/components/xeno/ai_behavior_overrides/base_override_behavior.dm +++ b/code/datums/components/xeno/ai_behavior_overrides/base_override_behavior.dm @@ -36,6 +36,9 @@ GLOBAL_LIST_EMPTY(all_ai_behavior_overrides) game_master.images -= behavior_image QDEL_NULL(behavior_image) + + for(var/assigned_xeno in currently_assigned) + UnregisterSignal(assigned_xeno, COMSIG_PARENT_QDELETING) currently_assigned = null . = ..() @@ -43,6 +46,11 @@ GLOBAL_LIST_EMPTY(all_ai_behavior_overrides) /// Override this to check if we want our behavior to be valid for the checked_xeno, passes the common factor of "distance" which is the distance between the checked_xeno and src parent /datum/component/ai_behavior_override/proc/check_behavior_validity(mob/living/carbon/xenomorph/checked_xeno, distance) if(length(currently_assigned) >= max_assigned && !(checked_xeno in currently_assigned)) + remove_from_queue(checked_xeno) + return FALSE + + if(checked_xeno.stat != CONSCIOUS) + remove_from_queue(checked_xeno) return FALSE return TRUE @@ -51,6 +59,14 @@ GLOBAL_LIST_EMPTY(all_ai_behavior_overrides) /datum/component/ai_behavior_override/proc/process_override_behavior(mob/living/carbon/xenomorph/processing_xeno, delta_time) SHOULD_NOT_SLEEP(TRUE) + RegisterSignal(processing_xeno, COMSIG_PARENT_QDELETING, PROC_REF(remove_from_queue), TRUE) currently_assigned |= processing_xeno return TRUE + +/datum/component/ai_behavior_override/proc/remove_from_queue(mob/removed_xeno) + SIGNAL_HANDLER + if(currently_assigned) + currently_assigned -= removed_xeno + + UnregisterSignal(removed_xeno, COMSIG_PARENT_QDELETING) diff --git a/code/datums/components/xeno/ai_behavior_overrides/build_override_behavior.dm b/code/datums/components/xeno/ai_behavior_overrides/build_override_behavior.dm index 5430c3cd03..a5c4a03982 100644 --- a/code/datums/components/xeno/ai_behavior_overrides/build_override_behavior.dm +++ b/code/datums/components/xeno/ai_behavior_overrides/build_override_behavior.dm @@ -35,6 +35,14 @@ qdel(src) return FALSE + var/area/current_area = get_area(location) + if(!current_area.is_resin_allowed) + for(var/client/game_master in GLOB.game_masters) + to_chat(game_master, SPAN_XENOBOLDNOTICE("This area doesn't allow xenos to build here")) + + qdel(src) + return FALSE + if(distance > 10) return FALSE @@ -46,9 +54,8 @@ if(checked_xeno.get_plasma_percentage() < PLASMA_RETREAT_PERCENTAGE) var/turf/xeno_loc = get_turf(checked_xeno) - if(xeno_loc.weeds && !checked_xeno.resting) - currently_assigned -= checked_xeno - checked_xeno.lay_down() + if(xeno_loc.weeds) + checked_xeno.set_resting(TRUE, FALSE, TRUE) return FALSE @@ -59,16 +66,21 @@ if(!.) return - processing_xeno.resting = FALSE + processing_xeno.set_resting(FALSE, FALSE, TRUE) var/turf/xeno_loc = get_turf(processing_xeno) if(xeno_loc.density) return FALSE // We shouldn't stand in a wall, let's act default var/turf/parent_turf = get_turf(parent) + var/distance = get_dist(processing_xeno, parent) + + var/list/turfs_around = xeno_loc.AdjacentTurfs() + if(turfs_around && distance < 1) // We are gonna be stuck after building at our loc, let's step away + return processing_xeno.move_to_next_turf(pick(turfs_around)) var/is_diagonal = (get_dir(processing_xeno, parent_turf) in diagonals) - if(is_diagonal || get_dist(processing_xeno, parent) > 1) + if(is_diagonal || distance > 1) return processing_xeno.move_to_next_turf(parent_turf) for(var/obj/structure/blocker in parent_turf.contents) @@ -84,19 +96,23 @@ var/list/resin_types = processing_xeno.resin_build_order processing_xeno.selected_resin = locate(/datum/resin_construction/resin_turf/wall) in resin_types - var/wall_nearby + var/wall_nearby = FALSE var/blocked_turfs = 0 for(var/turf/blocked_turf in orange(1, parent_turf) - parent_turf.AdjacentTurfs()) + blocked_turfs++ + if(get_dir(blocked_turf, parent_turf) in diagonals) continue if(blocked_turf.density) wall_nearby = TRUE - blocked_turfs++ + var/obj/effect/alien/weeds/turf_weeds = blocked_turf.weeds + if(turf_weeds && turf_weeds.secreting) + wall_nearby = TRUE // Something is being constructed nearby, let's bet this is a new resin wall - if(blocked_turfs) - if(prob(XENO_DOOR_BUILDING_CHANCE) || (wall_nearby && blocked_turfs == 2)) + if(wall_nearby) + if(prob(XENO_DOOR_BUILDING_CHANCE) || (wall_nearby && blocked_turfs > 1)) processing_xeno.selected_resin = locate(/datum/resin_construction/resin_obj/door) in resin_types var/datum/action/xeno_action/activable/secrete_resin/build_action = locate() in processing_xeno.actions diff --git a/code/datums/components/xeno/ai_behavior_overrides/capture_override_behavior.dm b/code/datums/components/xeno/ai_behavior_overrides/capture_override_behavior.dm index 6c80426960..96eaa7d8fb 100644 --- a/code/datums/components/xeno/ai_behavior_overrides/capture_override_behavior.dm +++ b/code/datums/components/xeno/ai_behavior_overrides/capture_override_behavior.dm @@ -7,10 +7,11 @@ /datum/component/ai_behavior_override/capture/Initialize(...) . = ..() - if(!istype(parent, /mob)) - return COMPONENT_INCOMPATIBLE + if(!ishuman(parent)) + var/mob/living/carbon/human/new_parent = locate() in get_turf(parent) + if(new_parent) + new_parent.AddComponent(/datum/component/ai_behavior_override/capture) - if(isxeno(parent)) return COMPONENT_INCOMPATIBLE /datum/component/ai_behavior_override/capture/check_behavior_validity(mob/living/carbon/xenomorph/checked_xeno, distance) @@ -23,10 +24,10 @@ var/mob/parent_mob = parent - var/stat = parent_mob.stat + var/captee_stat = parent_mob.stat var/mob/pulledby = parent_mob.pulledby - if(stat == DEAD) + if(captee_stat == DEAD) qdel(src) return FALSE @@ -44,7 +45,7 @@ if(distance > 10) return FALSE - if(stat == CONSCIOUS) + if(captee_stat == CONSCIOUS) return FALSE if(isxeno(pulledby) && pulledby != checked_xeno) @@ -58,7 +59,7 @@ return processing_xeno.current_target = parent - processing_xeno.resting = FALSE + processing_xeno.set_resting(FALSE, FALSE, TRUE) if(processing_xeno.get_active_hand()) processing_xeno.swap_hand() diff --git a/code/datums/pain/_pain.dm b/code/datums/pain/_pain.dm index 5a03b8dcb4..42c2760027 100644 --- a/code/datums/pain/_pain.dm +++ b/code/datums/pain/_pain.dm @@ -228,11 +228,7 @@ activate_severe() if(new_level < PAIN_LEVEL_SEVERE) - UnregisterSignal(source_mob, list( - COMSIG_MOB_DRAGGED, - COMSIG_MOB_DEVOURED, - COMSIG_MOVABLE_PRE_THROW - )) + UnregisterSignal(source_mob, COMSIG_MOB_DEVOURED) last_level = new_level addtimer(CALLBACK(src, PROC_REF(before_update)), PAIN_UPDATE_FREQUENCY) diff --git a/code/game/gamemodes/colonialmarines/ai/colonialmarines_ai.dm b/code/game/gamemodes/colonialmarines/ai/colonialmarines_ai.dm index d1d98b7af0..cbe4c24fab 100644 --- a/code/game/gamemodes/colonialmarines/ai/colonialmarines_ai.dm +++ b/code/game/gamemodes/colonialmarines/ai/colonialmarines_ai.dm @@ -43,6 +43,10 @@ . = ..() +/datum/game_mode/colonialmarines/ai/post_setup() + set_lz_resin_allowed(TRUE) + return ..() + /datum/game_mode/colonialmarines/ai/announce_bioscans() return diff --git a/code/game/machinery/vending/vendor_types/crew/medical.dm b/code/game/machinery/vending/vendor_types/crew/medical.dm index 5dfb6b347b..54ea55e8ea 100644 --- a/code/game/machinery/vending/vendor_types/crew/medical.dm +++ b/code/game/machinery/vending/vendor_types/crew/medical.dm @@ -40,6 +40,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_doctor, list( list("UNIFORM (CHOOSE 1)", 0, null, null, null), list("Green Scrubs", 0, /obj/item/clothing/under/rank/medical/green, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_RECOMMENDED), list("Blue Scrubs", 0, /obj/item/clothing/under/rank/medical/blue, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), + list("Light Blue Scrubs", 0, /obj/item/clothing/under/rank/medical/lightblue, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), list("Purple Scrubs", 0, /obj/item/clothing/under/rank/medical/purple, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), list("ARMOR (CHOOSE 1)", 0, null, null, null), @@ -96,7 +97,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_nurse, list( list("Green Scrubs", 0, /obj/item/clothing/under/rank/medical/green, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_RECOMMENDED), list("Blue Scrubs", 0, /obj/item/clothing/under/rank/medical/blue, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), list("Purple Scrubs", 0, /obj/item/clothing/under/rank/medical/purple, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), - list("Medical Nurse Scrubs", 0, /obj/item/clothing/under/rank/medical/nurse, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_MANDATORY), + list("Light Blue Scrubs", 0, /obj/item/clothing/under/rank/medical/lightblue, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_MANDATORY), list("ARMOR (CHOOSE 1)", 0, null, null, null), list("Labcoat", 0, /obj/item/clothing/suit/storage/labcoat, MARINE_CAN_BUY_ARMOR, VENDOR_ITEM_REGULAR), @@ -152,6 +153,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_researcher, list( list("UNIFORM (CHOOSE 1)", 0, null, null, null), list("Green Scrubs", 0, /obj/item/clothing/under/rank/medical/green, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_RECOMMENDED), list("Blue Scrubs", 0, /obj/item/clothing/under/rank/medical/blue, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), + list("Light Blue Scrubs", 0, /obj/item/clothing/under/rank/medical/lightblue, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), list("Purple Scrubs", 0, /obj/item/clothing/under/rank/medical/purple, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), list("Researcher Uniform", 0, /obj/item/clothing/under/marine/officer/researcher, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_MANDATORY), diff --git a/code/game/machinery/vending/vendor_types/crew/senior_officers.dm b/code/game/machinery/vending/vendor_types/crew/senior_officers.dm index 86d4a17b48..b5bc42eabb 100644 --- a/code/game/machinery/vending/vendor_types/crew/senior_officers.dm +++ b/code/game/machinery/vending/vendor_types/crew/senior_officers.dm @@ -222,6 +222,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_cmo, list( list("UNIFORM (CHOOSE 1)", 0, null, null, null), list("Green Scrubs", 0, /obj/item/clothing/under/rank/medical/green, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_RECOMMENDED), list("Blue Scrubs", 0, /obj/item/clothing/under/rank/medical/blue, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), + list("Light Blue Scrubs", 0, /obj/item/clothing/under/rank/medical/lightblue, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), list("Purple Scrubs", 0, /obj/item/clothing/under/rank/medical/purple, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), list("Doctor Uniform", 0, /obj/item/clothing/under/rank/medical, MARINE_CAN_BUY_UNIFORM, VENDOR_ITEM_REGULAR), diff --git a/code/game/machinery/vending/vendor_types/crew/synthetic.dm b/code/game/machinery/vending/vendor_types/crew/synthetic.dm index cec5627a63..1526613988 100644 --- a/code/game/machinery/vending/vendor_types/crew/synthetic.dm +++ b/code/game/machinery/vending/vendor_types/crew/synthetic.dm @@ -181,6 +181,7 @@ GLOBAL_LIST_INIT(cm_vending_clothing_synth, list( GLOBAL_LIST_INIT(cm_vending_clothing_synth_snowflake, list( list("USCM UNIFORMS", 0, null, null, null), list("Medical Scrubs, Blue", 12, /obj/item/clothing/under/rank/medical/blue, null, VENDOR_ITEM_REGULAR), + list("Medical Scrubs, Light Blue", 0, /obj/item/clothing/under/rank/medical/lightblue, null, VENDOR_ITEM_REGULAR), list("Medical Scrubs, Green", 12, /obj/item/clothing/under/rank/medical/green, null, VENDOR_ITEM_REGULAR), list("Medical Scrubs, Purple", 12, /obj/item/clothing/under/rank/medical/purple, null, VENDOR_ITEM_REGULAR), list("Medical Scrubs, White", 12, /obj/item/clothing/under/rank/medical, null, VENDOR_ITEM_REGULAR), diff --git a/code/game/objects/items/devices/helmet_visors.dm b/code/game/objects/items/devices/helmet_visors.dm index 596409c88c..7bdcf2339d 100644 --- a/code/game/objects/items/devices/helmet_visors.dm +++ b/code/game/objects/items/devices/helmet_visors.dm @@ -84,18 +84,6 @@ name = "advanced medical optic" helmet_overlay = "med_sight_left" -/obj/item/device/helmet_visor/medical/advanced/activate_visor(obj/item/clothing/head/helmet/marine/attached_helmet, mob/living/carbon/human/user) - . = ..() - - var/datum/action/item_action/view_publications/helmet_visor/publication_action = new(attached_helmet) - publication_action.give_to(user) - -/obj/item/device/helmet_visor/medical/advanced/deactivate_visor(obj/item/clothing/head/helmet/marine/attached_helmet, mob/living/carbon/human/user) - . = ..() - - var/datum/action/item_action/view_publications/helmet_visor/publication_action = locate() in attached_helmet.actions - qdel(publication_action) - /obj/item/device/helmet_visor/medical/advanced/can_toggle(mob/living/carbon/human/user) . = ..() if(!.) diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index d2451ff29a..1e6d0d9799 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -1526,8 +1526,8 @@ /obj/item/storage/belt/gun/flaregun name = "\improper M276 pattern M82F flare gun holster rig" desc = "The M276 is the standard load-bearing equipment of the USCM. It consists of a modular belt with various clips. This version is for the M82F flare gun." - storage_slots = 17 - max_storage_space = 20 + storage_slots = 28 + max_storage_space = 31 icon_state = "m82f_holster" item_state = "s_marinebelt" can_hold = list( diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index 4a3afa00f3..d544b5b281 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -548,11 +548,11 @@ /obj/item/storage/box/m94 name = "\improper M94 marking flare pack" - desc = "A packet of eight M94 Marking Flares. Carried by USCM soldiers to light dark areas that cannot be reached with the usual TNR Shoulder Lamp." + desc = "A packet of twenty one M94 Marking Flares. Carried by USCM soldiers to light dark areas that cannot be reached with the usual TNR Shoulder Lamp." icon_state = "m94" w_class = SIZE_MEDIUM - storage_slots = 8 - max_storage_space = 8 + storage_slots = 21 + max_storage_space = 21 can_hold = list(/obj/item/device/flashlight/flare,/obj/item/device/flashlight/flare/signal) /obj/item/storage/box/m94/fill_preset_inventory() @@ -568,7 +568,7 @@ /obj/item/storage/box/m94/signal name = "\improper M89-S signal flare pack" - desc = "A packet of eight M89-S Signal Marking Flares." + desc = "A packet of twenty one M89-S Signal Marking Flares." icon_state = "m89" /obj/item/storage/box/m94/signal/fill_preset_inventory() diff --git a/code/game/objects/items/storage/pouch.dm b/code/game/objects/items/storage/pouch.dm index 9bc30c105e..36149138a6 100644 --- a/code/game/objects/items/storage/pouch.dm +++ b/code/game/objects/items/storage/pouch.dm @@ -1084,8 +1084,8 @@ name = "flare pouch" desc = "A pouch designed to hold flares. Refillable with an M94 flare pack." max_w_class = SIZE_SMALL - storage_slots = 8 - max_storage_space = 8 + storage_slots = 21 + max_storage_space = 21 storage_flags = STORAGE_FLAGS_POUCH|STORAGE_USING_DRAWING_METHOD icon_state = "flare" can_hold = list(/obj/item/device/flashlight/flare,/obj/item/device/flashlight/flare/signal) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/cm_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/cm_closets.dm index 818aea5a4c..15bb136f46 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/cm_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/cm_closets.dm @@ -252,6 +252,7 @@ GLOBAL_LIST_EMPTY(co_secure_boxes) new /obj/item/storage/belt/medical/full(src) new /obj/item/clothing/under/rank/medical/green(src) new /obj/item/clothing/under/rank/medical/blue(src) + new /obj/item/clothing/under/rank/medical/lightblue(src) new /obj/item/clothing/under/rank/medical/purple(src) new /obj/item/clothing/mask/surgical(src) new /obj/item/clothing/head/surgery/green(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm index 4244fd8288..80a5664567 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm @@ -93,6 +93,7 @@ new /obj/item/clothing/gloves/latex(src) new /obj/item/clothing/under/rank/medical/green(src) new /obj/item/clothing/under/rank/medical/blue(src) + new /obj/item/clothing/under/rank/medical/lightblue(src) new /obj/item/clothing/under/rank/medical/purple(src) new /obj/item/clothing/head/surgery/green(src) new /obj/item/clothing/head/surgery/blue(src) diff --git a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm index daecf1906c..938a6a23cc 100644 --- a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm +++ b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm @@ -296,6 +296,7 @@ . = ..() new /obj/item/clothing/under/rank/medical(src) new /obj/item/clothing/under/rank/medical(src) + new /obj/item/clothing/under/rank/medical/lightblue(src) new /obj/item/clothing/under/rank/medical/blue(src) new /obj/item/clothing/under/rank/medical/green(src) new /obj/item/clothing/under/rank/medical/purple(src) diff --git a/code/game/objects/structures/crates_lockers/largecrate_supplies.dm b/code/game/objects/structures/crates_lockers/largecrate_supplies.dm index ff35e97d12..94be500526 100644 --- a/code/game/objects/structures/crates_lockers/largecrate_supplies.dm +++ b/code/game/objects/structures/crates_lockers/largecrate_supplies.dm @@ -239,7 +239,7 @@ icon_state = "secure_crate" /obj/structure/largecrate/supply/supplies/flares - name = "Flare supply crate (x200)" + name = "Flare supply crate (x525)" desc = "A supply crate containing two crates of flares." supplies = list(/obj/item/ammo_box/magazine/misc/flares = 2) diff --git a/code/global.dm b/code/global.dm index 26868dd386..1a54b4dba3 100644 --- a/code/global.dm +++ b/code/global.dm @@ -120,7 +120,7 @@ var/list/AAlarmWireColorToIndex #define MAX_EMOTE_LEN 256 #define MAX_PAPER_MESSAGE_LEN 3072 #define MAX_BOOK_MESSAGE_LEN 9216 -#define MAX_NAME_LEN 26 +#define MAX_NAME_LEN 28 /// 3 minutes in the station. #define shuttle_time_in_station 3 MINUTES diff --git a/code/modules/character_traits/hair_dye.dm b/code/modules/character_traits/hair_dye.dm index 1da4ea3640..3c6ce01f8a 100644 --- a/code/modules/character_traits/hair_dye.dm +++ b/code/modules/character_traits/hair_dye.dm @@ -6,7 +6,6 @@ trait_name = "Hair Dye" trait_desc = "Enables hair gradients in the character creation screen." applyable = TRUE - cost = 1 trait_group = /datum/character_trait_group/hair_dye refresh_choices = TRUE refresh_mannequin = TRUE diff --git a/code/modules/client/preferences_gear.dm b/code/modules/client/preferences_gear.dm index 0df471e4af..c090eb94ff 100644 --- a/code/modules/client/preferences_gear.dm +++ b/code/modules/client/preferences_gear.dm @@ -161,6 +161,16 @@ var/global/list/gear_datums_by_name = list() display_name = "USCM balaclava, tan" path = /obj/item/clothing/mask/rebreather/scarf/tan +/datum/gear/mask/uscm/skull_balaclava_blue + display_name = "USCM balaclava, blue skull" + path = /obj/item/clothing/mask/rebreather/skull + cost = 4 + +/datum/gear/mask/uscm/skull_balaclava_black + display_name = "USCM balaclava, black skull" + path = /obj/item/clothing/mask/rebreather/skull/black + cost = 4 + /datum/gear/headwear category = "Headwear" cost = 3 @@ -876,6 +886,11 @@ var/global/list/gear_datums_by_name = list() display_name = "Facepaint, black" path = /obj/item/facepaint/black +/datum/gear/misc/facepaint_skull + display_name = "Facepaint, skull" + path = /obj/item/facepaint/skull + cost = 3 + /datum/gear/misc/facepaint_body display_name = "Fullbody paint" path = /obj/item/facepaint/sniper diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index e4f9bdde48..db4669ae14 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -13,7 +13,7 @@ flags_armor_protection = 0 toggleable = TRUE hud_type = MOB_HUD_MEDICAL_ADVANCED - actions_types = list(/datum/action/item_action/toggle, /datum/action/item_action/view_publications) + actions_types = list(/datum/action/item_action/toggle) req_skill = SKILL_MEDICAL req_skill_level = SKILL_MEDICAL_MEDIC diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm index 5467e94136..a0e95f7f0f 100644 --- a/code/modules/clothing/head/soft_caps.dm +++ b/code/modules/clothing/head/soft_caps.dm @@ -89,6 +89,18 @@ cap_color = "ferret" black_market_value = 25 +/obj/item/clothing/head/soft/trucker + name = "\improper blue trucker hat" + desc = "It's a blue trucker hat." + icon_state = "truckercap_bluesoft" + cap_color = "truckercap_blue" + +/obj/item/clothing/head/soft/trucker/red + name = "\improper red trucker hat" + desc = "It's a red trucker hat." + icon_state = "truckercap_redsoft" + cap_color = "truckercap_red" + /obj/item/clothing/head/soft/sec name = "security cap" desc = "It's baseball hat in tasteful red color." diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index e3b07a76a2..1a49dc7fe1 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -72,7 +72,7 @@ /obj/item/clothing/shoes/sandal desc = "A pair of rather plain, wooden sandals." name = "sandals" - icon_state = "wizard" + icon_state = "sandals" flags_armor_protection = 0 /obj/item/clothing/shoes/sandal/marisa @@ -140,6 +140,9 @@ desc = "The height of fashion, and they're pre-polished!" icon_state = "laceups" +/obj/item/clothing/shoes/laceup/brown + icon_state = "laceups_brown" + /obj/item/clothing/shoes/swimmingfins desc = "Help you swim good." name = "swimming fins" diff --git a/code/modules/clothing/suits/marine_armor.dm b/code/modules/clothing/suits/marine_armor.dm index eae4cd6fd8..70f8ad86a7 100644 --- a/code/modules/clothing/suits/marine_armor.dm +++ b/code/modules/clothing/suits/marine_armor.dm @@ -1475,9 +1475,10 @@ flags_inventory = BLOCKSHARPOBJ|SMARTGUN_HARNESS /obj/item/clothing/suit/storage/CMB - name = "\improper CMB jacket" - desc = "A black jacket worn by Colonial Marshals. The back is enscribed with the powerful letters of 'MARSHAL' representing justice, authority, and protection in the outer rim. The laws of the Earth stretch beyond the Sol." + name = "\improper CMB Deputy jacket" + desc = "A thick and stylish black leather jacket with a Marshal's Deputy badge pinned to it. The back is enscribed with the powerful letters of 'DEPUTY' representing justice, authority, and protection in the outer rim. The laws of the Earth stretch beyond the Sol." icon_state = "CMB_jacket" + item_state = "CMB_jacket" blood_overlay_type = "coat" flags_armor_protection = BODY_FLAG_CHEST|BODY_FLAG_GROIN|BODY_FLAG_ARMS flags_cold_protection = BODY_FLAG_CHEST|BODY_FLAG_GROIN|BODY_FLAG_ARMS @@ -1524,6 +1525,12 @@ ) pockets.max_storage_space = 8 +/obj/item/clothing/suit/storage/CMB/marshal + name = "\improper CMB Marshal jacket" + desc = "A thick and stylish black leather jacket with a Marshal's badge pinned to it. The back is enscribed with the powerful letters of 'MARSHAL' representing justice, authority, and protection in the outer rim. The laws of the Earth stretch beyond the Sol." + icon_state = "CMB_jacket_marshal" + item_state = "CMB_jacket_marshal" + /obj/item/clothing/suit/storage/RO name = "quartermaster jacket" desc = "A green jacket worn by USCM personnel. The back has the flag of the United Americas on it." diff --git a/code/modules/clothing/suits/marine_coat.dm b/code/modules/clothing/suits/marine_coat.dm index 3aa43706c7..6e6752ba7b 100644 --- a/code/modules/clothing/suits/marine_coat.dm +++ b/code/modules/clothing/suits/marine_coat.dm @@ -287,3 +287,85 @@ icon_state = "wc_suit" item_state = "wc_suit" contained_sprite = TRUE + +//==================Corporate Liaison==================\\ + +/obj/item/clothing/suit/storage/jacket/marine/vest + name = "brown vest" + desc = "A casual brown vest." + icon_state = "vest_brown" + item_state = "vest_brown" + has_buttons = FALSE + flags_atom = NO_SNOW_TYPE + +/obj/item/clothing/suit/storage/jacket/marine/vest/tan + name = "tan vest" + desc = "A casual tan vest." + icon_state = "vest_tan" + item_state = "vest_tan" + has_buttons = FALSE + +/obj/item/clothing/suit/storage/jacket/marine/vest/grey + name = "grey vest" + desc = "A casual grey vest." + icon_state = "vest_grey" + item_state = "vest_grey" + has_buttons = FALSE + +/obj/item/clothing/suit/storage/jacket/marine/corporate + name = "khaki suit jacket" + desc = "A khaki suit jacket." + icon_state = "corporate_ivy" + item_state = "corporate_ivy" + has_buttons = FALSE + flags_atom = NO_SNOW_TYPE + +/obj/item/clothing/suit/storage/jacket/marine/corporate/formal + name = "formal suit jacket" + desc = "An ivory suit jacket; a Weyland-Yutani corporate badge is attached to the right lapel." + icon_state = "corporate_formal" + item_state = "corporate_formal" + has_buttons = FALSE + +/obj/item/clothing/suit/storage/jacket/marine/corporate/black + name = "black suit jacket" + desc = "A black suit jacket." + icon_state = "corporate_black" + item_state = "corporate_black" + has_buttons = FALSE + +/obj/item/clothing/suit/storage/jacket/marine/corporate/brown + name = "brown suit jacket" + desc = "A brown suit jacket." + icon_state = "corporate_brown" + item_state = "corporate_brown" + has_buttons = FALSE + +/obj/item/clothing/suit/storage/jacket/marine/corporate/blue + name = "blue suit jacket" + desc = "A blue suit jacket." + icon_state = "corporate_blue" + item_state = "corporate_blue" + has_buttons = FALSE + +/obj/item/clothing/suit/storage/jacket/marine/bomber + name = "khaki bomber jacket" + desc = "A khaki bomber jacket popular among stationeers and blue-collar workers everywhere." + icon_state = "jacket_khaki" + item_state = "jacket_khaki" + has_buttons = FALSE + flags_atom = NO_SNOW_TYPE + +/obj/item/clothing/suit/storage/jacket/marine/bomber/red + name = "red bomber jacket" + desc = "A reddish-brown bomber jacket popular among stationeers and blue-collar workers everywhere." + icon_state = "jacket_red" + item_state = "jacket_red" + has_buttons = FALSE + +/obj/item/clothing/suit/storage/jacket/marine/bomber/grey + name = "grey bomber jacket" + desc = "A blue-grey bomber jacket popular among stationeers and blue-collar workers everywhere." + icon_state = "jacket_grey" + item_state = "jacket_grey" + has_buttons = FALSE diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index b5859bb6f2..54148d4f70 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -143,8 +143,8 @@ valid_accessory_slots = list(ACCESSORY_SLOT_MEDAL) /obj/item/clothing/suit/storage/apron/overalls - name = "coveralls" - desc = "A set of denim overalls." + name = "blue coveralls" + desc = "A pair of denim overalls. With a large pocket in the front these overalls are popular with workers of all kinds." icon_state = "overalls" item_state = "overalls" flags_armor_protection = BODY_FLAG_CHEST|BODY_FLAG_GROIN|BODY_FLAG_LEGS @@ -167,6 +167,18 @@ ) valid_accessory_slots = list(ACCESSORY_SLOT_MEDAL) +/obj/item/clothing/suit/storage/apron/overalls/tan + name = "tan coveralls" + desc = "A pair of tan overalls. With a large pocket in the front these overalls are popular with workers of all kinds." + icon_state = "overalls_tan" + item_state = "overalls_tan" + +/obj/item/clothing/suit/storage/apron/overalls/red + name = "red coveralls" + desc = "A pair of reddish-brown overalls. With a large pocket in the front these overalls are popular with workers of all kinds." + icon_state = "overalls_red" + item_state = "overalls_red" + /obj/item/clothing/suit/syndicatefake name = "red space suit replica" icon_state = "syndicate" diff --git a/code/modules/clothing/under/jobs/medsci.dm b/code/modules/clothing/under/jobs/medsci.dm index 2a6c07e959..6232f0eef4 100644 --- a/code/modules/clothing/under/jobs/medsci.dm +++ b/code/modules/clothing/under/jobs/medsci.dm @@ -159,12 +159,12 @@ armor_internaldamage = CLOTHING_ARMOR_LOW item_state_slots = list(WEAR_BODY = "medical") -/obj/item/clothing/under/rank/medical/nurse - name = "medical nurse scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one features an orange armband." - icon_state = "scrubsnurse" - item_state = "scrubsnurse" - item_state_slots = list(WEAR_BODY = "scrubsnurse") +/obj/item/clothing/under/rank/medical/lightblue + name = "medical scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in light blue." + icon_state = "scrubslightblue" + flags_jumpsuit = FALSE + item_state_slots = list(WEAR_BODY = "scrubslightblue") /obj/item/clothing/under/rank/medical/blue name = "medical scrubs" @@ -187,6 +187,13 @@ flags_jumpsuit = FALSE item_state_slots = list(WEAR_BODY = "scrubspurple") +/obj/item/clothing/under/rank/medical/orange + name = "medical scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in prisoner orange." + icon_state = "scrubsorange" + flags_jumpsuit = FALSE + item_state_slots = list(WEAR_BODY = "scrubsorange") + /obj/item/clothing/under/rank/psych desc = "A basic white jumpsuit. It has turquoise markings that denote the wearer as a psychiatrist." name = "psychiatrist's jumpsuit" diff --git a/code/modules/clothing/under/marine_uniform.dm b/code/modules/clothing/under/marine_uniform.dm index 04473bc62c..a4808bf87a 100644 --- a/code/modules/clothing/under/marine_uniform.dm +++ b/code/modules/clothing/under/marine_uniform.dm @@ -509,6 +509,9 @@ has_sensor = UNIFORM_NO_SENSORS suit_restricted = list(/obj/item/clothing/suit/storage/marine/veteran/bear) + item_icons = list( + WEAR_BODY = 'icons/mob/humans/onmob/uniform_1.dmi', + ) /obj/item/clothing/under/marine/veteran/UPP name = "\improper UPP fatigues" @@ -688,6 +691,40 @@ has_sensor = UNIFORM_HAS_SENSORS sensor_faction = FACTION_COLONIST +/obj/item/clothing/under/colonist/workwear + name = "grey workwear" + desc = "A pair of black slacks and a short-sleeve grey workshirt. Standard uniform for Weyland Yutani employees working in colony operations and administration." + icon_state = "workwear_grey" + worn_state = "workwear_grey" + +/obj/item/clothing/under/colonist/workwear/khaki + name = "khaki workwear" + desc = "A pair of jeans paired with a khaki workshirt. A common pairing among blue-collar workers due to its drab look." + icon_state = "workwear_khaki" + worn_state = "workwear_khaki" + flags_jumpsuit = UNIFORM_SLEEVE_ROLLABLE|UNIFORM_JACKET_REMOVABLE + +/obj/item/clothing/under/colonist/workwear/pink + name = "pink workwear" + desc = "A pair of jeans paired with a pink workshirt. Pink? Your wife might not think so, but such outlandish attire deserves questioning by corporate security. What are you, some kind of free-thinking anarchist?" + icon_state = "workwear_pink" + worn_state = "workwear_pink" + flags_jumpsuit = UNIFORM_SLEEVE_ROLLABLE|UNIFORM_JACKET_REMOVABLE + +/obj/item/clothing/under/colonist/workwear/blue + name = "blue workwear" + desc = "A pair of brown canvas workpants paired with a dark blue workshirt. A common pairing among blue-collar workers." + icon_state = "workwear_blue" + worn_state = "workwear_blue" + flags_jumpsuit = UNIFORM_SLEEVE_ROLLABLE|UNIFORM_JACKET_REMOVABLE + +/obj/item/clothing/under/colonist/workwear/green + name = "green workwear" + desc = "A pair of brown canvas workpants paired with a green workshirt. An common pairing among blue-collar workers." + icon_state = "workwear_green" + worn_state = "workwear_green" + flags_jumpsuit = UNIFORM_SLEEVE_ROLLABLE|UNIFORM_JACKET_REMOVABLE + /obj/item/clothing/under/colonist/clf name = "\improper Colonial Liberation Front uniform" desc = "A stylish grey-green jumpsuit - standard issue for colonists. This version appears to have the symbol of the Colonial Liberation Front emblazoned in select areas." @@ -751,7 +788,7 @@ /obj/item/clothing/under/CM_uniform name = "\improper Colonial Marshal uniform" - desc = "A blue shirt and tan trousers - the official uniform for a Colonial Marshal." + desc = "A pair of off-white slacks and a blue button-down shirt with a dark brown tie; the standard uniform of the Colonial Marshals." icon_state = "marshal" worn_state = "marshal" armor_melee = CLOTHING_ARMOR_LOW @@ -762,7 +799,7 @@ armor_bio = CLOTHING_ARMOR_NONE armor_rad = CLOTHING_ARMOR_NONE armor_internaldamage = CLOTHING_ARMOR_LOW - + flags_jumpsuit = UNIFORM_SLEEVE_ROLLABLE|UNIFORM_JACKET_REMOVABLE /obj/item/clothing/under/liaison_suit name = "liaison's tan suit" @@ -804,6 +841,42 @@ icon_state = "liaison_blue_blazer" worn_state = "liaison_blue_blazer" +/obj/item/clothing/under/liaison_suit/field + name = "corporate casual" + desc = "A pair of dark brown slacks paired with a dark blue button-down shirt. A popular look among those in the corporate world that conduct the majority of their business from night clubs." + icon_state = "corporate_field" + worn_state = "corporate_field" + +/obj/item/clothing/under/liaison_suit/ivy + name = "country club outfit" + desc = "A pair of khaki slacks paired with a light blue button-down shirt. A popular look with those in the corporate world that conduct the majority of their business from country clubs." + icon_state = "corporate_ivy" + worn_state = "corporate_ivy" + +/obj/item/clothing/under/liaison_suit/corporate_formal + name = "white suit pants" + desc = "A pair of ivory slacks paired with a white shirt. A popular pairing for formal corporate events." + icon_state = "corporate_formal" + worn_state = "corporate_formal" + +/obj/item/clothing/under/liaison_suit/black + name = "black suit pants" + desc = "A pair of black slacks paired with a white shirt. The most common pairing among corporate workers." + icon_state = "corporate_black" + worn_state = "corporate_black" + +/obj/item/clothing/under/liaison_suit/brown + name = "brown suit pants" + desc = "A pair of brown slacks paired with a white shirt. A common pairing among corporate workers." + icon_state = "corporate_brown" + worn_state = "corporate_brown" + +/obj/item/clothing/under/liaison_suit/blue + name = "blue suit pants" + desc = "A pair of blue slacks paired with a white shirt. A common pairing among corporate workers." + icon_state = "corporate_blue" + worn_state = "corporate_blue" + /obj/item/clothing/under/marine/reporter name = "combat correspondent uniform" desc = "A relaxed and robust uniform fit for any potential reporting needs." diff --git a/code/modules/clothing/under/ties.dm b/code/modules/clothing/under/ties.dm index 6234bdddf6..c1da290d43 100644 --- a/code/modules/clothing/under/ties.dm +++ b/code/modules/clothing/under/ties.dm @@ -79,6 +79,22 @@ name = "red tie" icon_state = "redtie" +/obj/item/clothing/accessory/green + name = "green tie" + icon_state = "greentie" + +/obj/item/clothing/accessory/black + name = "black tie" + icon_state = "blacktie" + +/obj/item/clothing/accessory/gold + name = "gold tie" + icon_state = "goldtie" + +/obj/item/clothing/accessory/purple + name = "purple tie" + icon_state = "purpletie" + /obj/item/clothing/accessory/horrible name = "horrible tie" desc = "A neosilk clip-on tie. This one is disgusting." @@ -342,6 +358,11 @@ desc = "An armband, worn by the crew to display which department they're assigned to. This one is white and green." icon_state = "medgreen" +/obj/item/clothing/accessory/armband/nurse + name = "nurse armband" + desc = "An armband, worn by the rookie nurses to display they are still not doctors. This one is dark red." + icon_state = "nurse" + //patches /obj/item/clothing/accessory/patch name = "USCM patch" diff --git a/code/modules/cm_marines/Donator_Items.dm b/code/modules/cm_marines/Donator_Items.dm index 6d2f46490d..d1e2011e8c 100644 --- a/code/modules/cm_marines/Donator_Items.dm +++ b/code/modules/cm_marines/Donator_Items.dm @@ -971,8 +971,11 @@ icon_state = null item_state = null min_cold_protection_temperature = ICE_PLANET_MIN_COLD_PROT - //DON'T GRAB STUFF BETWEEN THIS LINE - //AND THIS LINE + + item_icons = list( + WEAR_BODY = 'icons/mob/humans/onmob/uniform_1.dmi', + ) + //END UNIFORM TEMPLATE /obj/item/clothing/under/marine/fluff/marinemedic //UNUSED @@ -1309,8 +1312,8 @@ /obj/item/clothing/shoes/marine/fluff/vintage //CKEY=vintagepalmer name = "Vintage Sandals" desc = "Vintage Sandals, suitable for only the highest class of hipster. DONOR ITEM" - icon_state = "wizard" - item_state = "wizard" + icon_state = "sandals" + item_state = "sandals" /obj/item/clothing/shoes/marine/fluff/feodrich //CKEY=feodrich (UNIQUE) name = "Doom Shoes" diff --git a/code/modules/gear_presets/cmb.dm b/code/modules/gear_presets/cmb.dm index 5786819791..22bf31d42c 100644 --- a/code/modules/gear_presets/cmb.dm +++ b/code/modules/gear_presets/cmb.dm @@ -162,7 +162,7 @@ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/revolver/cmb/normalpoint, WEAR_IN_ACCESSORY) new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/holobadge/cord, WEAR_ACCESSORY) new_human.equip_to_slot_or_del(new /obj/item/clothing/mask/cigarette/cigar, WEAR_FACE) - new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/CMB, WEAR_JACKET) + new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/CMB/marshal, WEAR_JACKET) new_human.equip_to_slot_or_del(new /obj/item/device/binoculars/range, WEAR_IN_JACKET) new_human.equip_to_slot_or_del(new /obj/item/weapon/telebaton, WEAR_IN_JACKET) new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/shotgun/pump/dual_tube/cmb/m3717, WEAR_J_STORE) @@ -312,7 +312,7 @@ //clothes new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/CMB/ICC, WEAR_L_EAR) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/formal, WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/corporate_formal, WEAR_BODY) new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/storage/holster, WEAR_ACCESSORY) new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/pistol/mod88, WEAR_IN_ACCESSORY) new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/pistol/mod88, WEAR_IN_ACCESSORY) @@ -372,7 +372,7 @@ //clothes new_human.equip_to_slot_or_del(new headset_type, WEAR_L_EAR) new_human.equip_to_slot_or_del(new /obj/item/device/flashlight/pen, WEAR_R_EAR) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/suspenders, WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/blue, WEAR_BODY) new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/health/ceramic_plate, WEAR_ACCESSORY) new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/vest, WEAR_JACKET) new_human.equip_to_slot_or_del(new /obj/item/device/flashlight, WEAR_J_STORE) diff --git a/code/modules/gear_presets/corpses.dm b/code/modules/gear_presets/corpses.dm index de24f1f84f..450758c691 100644 --- a/code/modules/gear_presets/corpses.dm +++ b/code/modules/gear_presets/corpses.dm @@ -350,7 +350,7 @@ ) /datum/equipment_preset/corpse/security/liaison/load_gear(mob/living/carbon/human/new_human) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/formal(new_human), WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/black(new_human), WEAR_BODY) if(SSmapping.configs[GROUND_MAP].environment_traits[MAP_COLD]) add_ice_colony_survivor_equipment(new_human) else diff --git a/code/modules/gear_presets/survivors/corsat/preset_corsat.dm b/code/modules/gear_presets/survivors/corsat/preset_corsat.dm index f71439b9d7..3b0e275648 100644 --- a/code/modules/gear_presets/survivors/corsat/preset_corsat.dm +++ b/code/modules/gear_presets/survivors/corsat/preset_corsat.dm @@ -40,7 +40,7 @@ assignment = "Interstellar Commerce Commission Corporate Liaison" /datum/equipment_preset/survivor/interstellar_commerce_commission_liason/corsat/load_gear(mob/living/carbon/human/new_human) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/formal(new_human), WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/corporate_formal(new_human), WEAR_BODY) new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/CMB/limited(new_human), WEAR_L_EAR) new_human.equip_to_slot_or_del(new /obj/item/clothing/head/hardhat/white(new_human), WEAR_HEAD) new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/vest(new_human), WEAR_JACKET) diff --git a/code/modules/gear_presets/survivors/new_varadero/preset_new_varadero.dm b/code/modules/gear_presets/survivors/new_varadero/preset_new_varadero.dm index daa3a8ec01..ef6b3ebf6d 100644 --- a/code/modules/gear_presets/survivors/new_varadero/preset_new_varadero.dm +++ b/code/modules/gear_presets/survivors/new_varadero/preset_new_varadero.dm @@ -43,7 +43,7 @@ assignment = "Interstellar Commerce Commission Corporate Liaison" /datum/equipment_preset/survivor/interstellar_commerce_commission_liason/nv/load_gear(mob/living/carbon/human/new_human) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/formal(new_human), WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/corporate_formal(new_human), WEAR_BODY) new_human.equip_to_slot_or_del(new /obj/item/clothing/head/hardhat/white(new_human), WEAR_HEAD) new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/hazardvest/black(new_human), WEAR_JACKET) new_human.equip_to_slot_or_del(new /obj/item/device/flashlight, WEAR_J_STORE) diff --git a/code/modules/gear_presets/survivors/shivas_snowball/preset_shivas_snowball.dm b/code/modules/gear_presets/survivors/shivas_snowball/preset_shivas_snowball.dm index c1dce212d0..9496573401 100644 --- a/code/modules/gear_presets/survivors/shivas_snowball/preset_shivas_snowball.dm +++ b/code/modules/gear_presets/survivors/shivas_snowball/preset_shivas_snowball.dm @@ -3,7 +3,7 @@ assignment = "Shivas Snowball Corporate Liaison" /datum/equipment_preset/survivor/corporate/shiva/load_gear(mob/living/carbon/human/new_human) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/formal(new_human), WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/black(new_human), WEAR_BODY) new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/WY(new_human), WEAR_L_EAR) new_human.equip_to_slot_or_del(new /obj/item/clothing/head/ushanka(new_human), WEAR_HEAD) new_human.equip_to_slot_or_del(new /obj/item/clothing/mask/rebreather/scarf(new_human), WEAR_FACE) diff --git a/code/modules/gear_presets/survivors/solaris/preset_solaris.dm b/code/modules/gear_presets/survivors/solaris/preset_solaris.dm index c71641a82a..49d16be504 100644 --- a/code/modules/gear_presets/survivors/solaris/preset_solaris.dm +++ b/code/modules/gear_presets/survivors/solaris/preset_solaris.dm @@ -84,7 +84,7 @@ assignment = "Solaris Ridge Corporate Liaison" /datum/equipment_preset/survivor/corporate/solaris/load_gear(mob/living/carbon/human/new_human) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/outing/red(new_human), WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/ivy(new_human), WEAR_BODY) new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/WY(new_human), WEAR_L_EAR) if(new_human.disabilities & NEARSIGHTED) new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses/prescription(new_human), WEAR_EYES) diff --git a/code/modules/gear_presets/survivors/survivors.dm b/code/modules/gear_presets/survivors/survivors.dm index 9cb2c29f11..f2378749b4 100644 --- a/code/modules/gear_presets/survivors/survivors.dm +++ b/code/modules/gear_presets/survivors/survivors.dm @@ -271,7 +271,7 @@ Everything bellow is a parent used as a base for one or multiple maps. access = list(ACCESS_CIVILIAN_PUBLIC,ACCESS_CIVILIAN_COMMAND) /datum/equipment_preset/survivor/interstellar_human_rights_observer/load_gear(mob/living/carbon/human/new_human) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/suspenders(new_human), WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/brown(new_human), WEAR_BODY) if(SSmapping.configs[GROUND_MAP].environment_traits[MAP_COLD]) add_ice_colony_survivor_equipment(new_human) new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/vest(new_human), WEAR_JACKET) @@ -306,7 +306,7 @@ Everything bellow is a parent used as a base for one or multiple maps. survivor_variant = CORPORATE_SURVIVOR /datum/equipment_preset/survivor/corporate/load_gear(mob/living/carbon/human/new_human) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/formal(new_human), WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/field(new_human), WEAR_BODY) if(SSmapping.configs[GROUND_MAP].environment_traits[MAP_COLD]) add_ice_colony_survivor_equipment(new_human) new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/vest(new_human), WEAR_JACKET) diff --git a/code/modules/gear_presets/uscm_medical.dm b/code/modules/gear_presets/uscm_medical.dm index b255874829..54f2c71317 100644 --- a/code/modules/gear_presets/uscm_medical.dm +++ b/code/modules/gear_presets/uscm_medical.dm @@ -130,7 +130,8 @@ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/doc(new_human), WEAR_L_EAR) new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical/nurse(new_human), WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical/lightblue(new_human), WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/armband/nurse(new_human), WEAR_ACCESSORY) new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/white(new_human), WEAR_FEET) if(new_human.disabilities & NEARSIGHTED) diff --git a/code/modules/gear_presets/uscm_ship.dm b/code/modules/gear_presets/uscm_ship.dm index d707831f44..0aa3ac5e2a 100644 --- a/code/modules/gear_presets/uscm_ship.dm +++ b/code/modules/gear_presets/uscm_ship.dm @@ -52,18 +52,18 @@ minimap_icon = "cl" minimap_background = MINIMAP_ICON_BACKGROUND_CIVILIAN - utility_under = list(/obj/item/clothing/under/liaison_suit/outing) + utility_under = list(/obj/item/clothing/under/liaison_suit/black) utility_hat = list() utility_gloves = list() utility_shoes = list(/obj/item/clothing/shoes/laceup) - utility_extra = list(/obj/item/clothing/under/liaison_suit/suspenders) + utility_extra = list(/obj/item/clothing/under/liaison_suit/blue) - service_under = list(/obj/item/clothing/under/liaison_suit) + service_under = list(/obj/item/clothing/under/liaison_suit/field) service_over = list() service_hat = list() service_shoes = list(/obj/item/clothing/shoes/laceup) - dress_under = list(/obj/item/clothing/under/liaison_suit/formal) + dress_under = list(/obj/item/clothing/under/liaison_suit/corporate_formal) dress_over = list() dress_hat = list() dress_gloves = list(/obj/item/clothing/gloves/marine/dress) @@ -80,7 +80,7 @@ //back_item = /obj/item/storage/backpack new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/mcl(new_human), WEAR_L_EAR) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit(new_human), WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/ivy(new_human), WEAR_BODY) new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(new_human), WEAR_FEET) new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK) diff --git a/code/modules/gear_presets/wo.dm b/code/modules/gear_presets/wo.dm index acc795dc68..c52d25d255 100644 --- a/code/modules/gear_presets/wo.dm +++ b/code/modules/gear_presets/wo.dm @@ -599,7 +599,7 @@ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/fedora(new_human), WEAR_HEAD) new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/mcom(new_human), WEAR_L_EAR) - new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/suspenders(new_human), WEAR_BODY) + new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit/black(new_human), WEAR_BODY) new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(new_human), WEAR_FEET) new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK) new_human.equip_to_slot_or_del(new /obj/item/device/camera(new_human), WEAR_L_HAND) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_abilities.dm index ca5f88638a..418393afd5 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_abilities.dm @@ -149,29 +149,29 @@ var/list/possible_charge_dirs = list() - for(var/mob/living/carbon/human/base_checked_human as anything in GLOB.alive_human_list) - var/distance_between_base_human_and_xeno = get_dist(processing_xeno, base_checked_human) + for(var/mob/living/carbon/base_checked_carbon as anything in GLOB.alive_mob_list) + var/distance_between_base_carbon_and_xeno = get_dist(processing_xeno, base_checked_carbon) - if(distance_between_base_human_and_xeno > MAXIMUM_TARGET_DISTANCE) + if(distance_between_base_carbon_and_xeno > MAXIMUM_TARGET_DISTANCE) continue - if(distance_between_base_human_and_xeno < MINIMUM_CHARGE_DISTANCE) + if(distance_between_base_carbon_and_xeno < MINIMUM_CHARGE_DISTANCE) continue - if(!processing_xeno.check_mob_target(base_checked_human)) + if(!base_checked_carbon.ai_can_target(processing_xeno)) continue var/secondary_count = 0 var/secondary_x_sum = 0 var/secondary_y_sum = 0 - for(var/mob/living/carbon/human/secondary_checked_human in range(FLOCK_SCAN_RADIUS, base_checked_human)) - if(!processing_xeno.check_mob_target(secondary_checked_human)) + for(var/mob/living/carbon/secondary_checked_carbon in range(FLOCK_SCAN_RADIUS, base_checked_carbon)) + if(!secondary_checked_carbon.ai_can_target(processing_xeno)) continue secondary_count++ - secondary_x_sum += secondary_checked_human.x - secondary_y_sum += secondary_checked_human.y + secondary_x_sum += secondary_checked_carbon.x + secondary_y_sum += secondary_checked_carbon.y if(secondary_count < MIN_TARGETS_TO_CHARGE) continue @@ -305,6 +305,9 @@ if(hit_human.body_position == LYING_DOWN) continue + if(xeno.can_not_harm(hit_human)) + continue + shake_camera(hit_human, 4, 2) INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(xeno_throw_human), hit_human, xeno, get_dir(xeno, hit_human), 1, FALSE) to_chat(hit_human, SPAN_XENOHIGHDANGER("You fall backwards as [xeno] gives you a glancing blow!")) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm index 74c6dadb72..ca313a69ef 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm @@ -127,7 +127,7 @@ xeno_owner.create_stomp() for(var/mob/living/carbon/carbon_in_range in range(distance, get_turf(xeno_owner))) - if(carbon_in_range.stat == DEAD || xeno_owner.can_not_harm(carbon_in_range)) + if(xeno_owner.can_not_harm(carbon_in_range)) continue var/distance_to_target = get_dist(carbon_in_range, xeno_owner) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_powers.dm index 1ba675f5e6..e262e28b71 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_powers.dm @@ -119,6 +119,7 @@ woyer.flick_attack_overlay(carbone, "disarm") carbone.throw_atom(throw_turf, fling_distance, SPEED_VERY_FAST, woyer, TRUE) + COOLDOWN_RESET(woyer, forced_retarget_cooldown) apply_cooldown() return ..() diff --git a/code/modules/mob/living/carbon/xenomorph/ai/movement/base_define.dm b/code/modules/mob/living/carbon/xenomorph/ai/movement/base_define.dm index e2c8e5cf20..172461bfce 100644 --- a/code/modules/mob/living/carbon/xenomorph/ai/movement/base_define.dm +++ b/code/modules/mob/living/carbon/xenomorph/ai/movement/base_define.dm @@ -25,25 +25,27 @@ if(idle_xeno.throwing) return - if(next_home_search < world.time && (!home_turf || !home_turf.weeds || get_dist(home_turf, idle_xeno) > max_distance_from_home)) + if(next_home_search < world.time && (!home_turf || !home_turf.weeds || !IS_SAME_HIVENUMBER(idle_xeno, home_turf.weeds) || get_dist(home_turf, idle_xeno) > max_distance_from_home)) var/turf/T = get_turf(idle_xeno.loc) next_home_search = world.time + home_search_delay - if(T.weeds) + var/obj/effect/alien/weeds/current_weeds = T.weeds + if(current_weeds && IS_SAME_HIVENUMBER(idle_xeno, current_weeds)) home_turf = T else var/shortest_distance = INFINITY for(var/i in RANGE_TURFS(home_locate_range, T)) var/turf/potential_home = i - if(potential_home.weeds && !potential_home.density && get_dist(idle_xeno, potential_home) < shortest_distance) + var/obj/effect/alien/weeds/potential_weeds = potential_home.weeds + if(potential_weeds && IS_SAME_HIVENUMBER(idle_xeno, potential_weeds) && !potential_home.density && get_dist(idle_xeno, potential_home) < shortest_distance) home_turf = potential_home shortest_distance = get_dist(idle_xeno, potential_home) - + idle_xeno.set_resting(FALSE, FALSE, TRUE) if(!home_turf) return if(idle_xeno.move_to_next_turf(home_turf, home_locate_range)) - if(get_dist(home_turf, idle_xeno) <= 0 && !idle_xeno.resting) - idle_xeno.lay_down() + if(get_dist(home_turf, idle_xeno) <= 0) + idle_xeno.set_resting(TRUE, FALSE, TRUE) else home_turf = null diff --git a/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm b/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm index 086000bed4..263f354c78 100644 --- a/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm +++ b/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm @@ -50,11 +50,10 @@ home_turf = potential_home if(!home_turf) - if(!idle_xeno.resting) - idle_xeno.lay_down() + idle_xeno.set_resting(TRUE, FALSE, TRUE) return - idle_xeno.resting = FALSE + idle_xeno.set_resting(FALSE, FALSE, TRUE) if(home_turf == last_home_turf) blacklisted_turfs += home_turf @@ -79,13 +78,15 @@ if(checked_turf in blacklisted_turfs) return FALSE - if(checked_turf.weeds) + var/obj/effect/alien/weeds/checked_weeds = checked_turf.weeds + if(checked_weeds && IS_SAME_HIVENUMBER(checked_weeds, parent)) return FALSE if(checked_turf.is_weedable() < FULLY_WEEDABLE) return FALSE - if(locate(/obj/effect/alien/weeds/node) in range(3, checked_turf)) + var/obj/effect/alien/weeds/found_weeds = locate(/obj/effect/alien/weeds/node) in range(3, checked_turf) + if(found_weeds && IS_SAME_HIVENUMBER(found_weeds, parent)) return FALSE if(checked_turf.density) diff --git a/code/modules/mob/living/carbon/xenomorph/ai/movement/lurking.dm b/code/modules/mob/living/carbon/xenomorph/ai/movement/lurking.dm index eeeb5b9ba8..3f2e46f5d0 100644 --- a/code/modules/mob/living/carbon/xenomorph/ai/movement/lurking.dm +++ b/code/modules/mob/living/carbon/xenomorph/ai/movement/lurking.dm @@ -271,7 +271,7 @@ registered_turfs += cycled_open_turf var/mob/living/carbon/human/possible_target = locate() in cycled_open_turf - if(possible_target && (!parent.current_target || get_dist(parent, possible_target) < get_dist(parent, parent.current_target)) && parent.check_mob_target(possible_target)) + if(possible_target && (!parent.current_target || get_dist(parent, possible_target) < get_dist(parent, parent.current_target)) && possible_target.ai_can_target(parent)) parent.current_target = possible_target /datum/xeno_ai_movement/linger/lurking/proc/unregister_turf_signals() @@ -286,7 +286,7 @@ return var/mob/living/carbon/human/possible_target = entering_atom - if(!parent.current_target || get_dist(parent, possible_target) < get_dist(parent, parent.current_target) && parent.check_mob_target(possible_target)) + if(!parent.current_target || get_dist(parent, possible_target) < get_dist(parent, parent.current_target) && possible_target.ai_can_target(parent)) parent.current_target = possible_target /datum/xeno_ai_movement/linger/lurking/proc/lurking_parent_moved(atom/movable/moving_atom, atom/oldloc, direction, Forced) diff --git a/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm b/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm index dfb3d28b5a..b15959b777 100644 --- a/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm +++ b/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm @@ -25,6 +25,9 @@ /// The actual cooldown declaration for forceful retargeting, reference forced_retarget_time for time in between checks COOLDOWN_DECLARE(forced_retarget_cooldown) + /// Amount of times no path found has occured + var/no_path_found_amount = 0 + /// The time interval between calculating new paths if we cannot find a path var/no_path_found_period = (2.5 SECONDS) @@ -62,28 +65,23 @@ if(!hive || !get_turf(src)) return TRUE - if(is_mob_incapacitated(TRUE)) - current_path = null - return TRUE - var/datum/component/ai_behavior_override/behavior_override = check_overrides() if(behavior_override?.process_override_behavior(src, delta_time)) return TRUE - var/stat_check = FALSE - if(istype(current_target, /mob)) - var/mob/current_target_mob = current_target - stat_check = (current_target_mob.stat != CONSCIOUS) + if(is_mob_incapacitated(TRUE)) + current_path = null + return TRUE - if(QDELETED(current_target) || stat_check || get_dist(current_target, src) > ai_range || COOLDOWN_FINISHED(src, forced_retarget_cooldown)) + if(QDELETED(current_target) || !current_target.ai_check_stat() || get_dist(current_target, src) > ai_range || COOLDOWN_FINISHED(src, forced_retarget_cooldown)) current_target = get_target(ai_range) COOLDOWN_START(src, forced_retarget_cooldown, forced_retarget_time) if(QDELETED(src)) return TRUE if(current_target) - resting = FALSE + set_resting(FALSE, FALSE, TRUE) if(prob(5)) emote("hiss") @@ -139,6 +137,9 @@ return 0 return INFINITY +/atom/proc/ai_check_stat() + return TRUE // So we aren't trying to find a new target on attack override + // Called whenever an obstacle is encountered but xeno_ai_obstacle returned something else than infinite // and now it is considered a valid path. /atom/proc/xeno_ai_act(mob/living/carbon/xenomorph/X) @@ -168,10 +169,15 @@ return FALSE if(no_path_found) - COOLDOWN_START(src, no_path_found_cooldown, no_path_found_period) + + if(no_path_found_amount > 0) + COOLDOWN_START(src, no_path_found_cooldown, no_path_found_period) no_path_found = FALSE + no_path_found_amount++ return FALSE + no_path_found_amount = 0 + if((!current_path || (next_path_generation < world.time && current_target_turf != T)) && COOLDOWN_FINISHED(src, no_path_found_cooldown)) if(!XENO_CALCULATING_PATH(src) || current_target_turf != T) SSxeno_pathfinding.calculate_path(src, T, max_range, src, CALLBACK(src, PROC_REF(set_path)), list(src, current_target)) @@ -243,24 +249,27 @@ var/atom/movable/closest_target var/smallest_distance = INFINITY - for(var/mob/living/carbon/human/potential_alive_human_target as anything in GLOB.alive_human_list) - if(z != potential_alive_human_target.z) + for(var/mob/living/carbon/potential_target as anything in GLOB.alive_mob_list) + if(!istype(potential_target)) continue - if(!check_mob_target(potential_alive_human_target)) + if(z != potential_target.z) continue - var/distance = get_dist(src, potential_alive_human_target) + if(!potential_target.ai_can_target(src)) + continue + + var/distance = get_dist(src, potential_target) if(distance > ai_range) continue - viable_targets += potential_alive_human_target + viable_targets += potential_target if(smallest_distance <= distance) continue - closest_target = potential_alive_human_target + closest_target = potential_target smallest_distance = distance for(var/obj/vehicle/multitile/potential_vehicle_target as anything in GLOB.all_multi_vehicles) @@ -273,18 +282,24 @@ continue if(potential_vehicle_target.health <= 0) - var/skip_vehicle = TRUE - - var/list/interior_living_mobs = potential_vehicle_target.interior.get_passengers() - for(var/mob/living/carbon/human/human_mob in interior_living_mobs) - if(!check_mob_target(human_mob)) - continue + continue - skip_vehicle = FALSE + var/multitile_faction = potential_vehicle_target.vehicle_faction + if(hive.faction_is_ally(multitile_faction)) + continue - if(skip_vehicle) + var/skip_vehicle + var/list/interior_living_mobs = potential_vehicle_target.interior.get_passengers() + for(var/mob/living/carbon/human/human_mob in interior_living_mobs) + if(!human_mob.ai_can_target(src)) continue + skip_vehicle = FALSE + break + + if(skip_vehicle) + continue + viable_targets += potential_vehicle_target if(smallest_distance <= distance) @@ -323,17 +338,14 @@ #undef EXTRA_CHECK_DISTANCE_MULTIPLIER -/mob/living/carbon/xenomorph/proc/check_mob_target(mob/living/carbon/human/checked_human) - if(checked_human.species.flags & IS_SYNTHETIC) - return FALSE - - if(HAS_TRAIT(checked_human, TRAIT_NESTED)) +/mob/living/carbon/proc/ai_can_target(mob/living/carbon/xenomorph/ai_xeno) + if(!ai_check_stat()) return FALSE - if(can_not_harm(checked_human)) + if(ai_xeno.can_not_harm(src)) return FALSE - if(checked_human.stat != CONSCIOUS) + if(alpha <= 45 && get_dist(ai_xeno, src) > 2) return FALSE return TRUE diff --git a/code/modules/mob/living/carbon/xenomorph/death.dm b/code/modules/mob/living/carbon/xenomorph/death.dm index 9084c9fb3a..e7366df07f 100644 --- a/code/modules/mob/living/carbon/xenomorph/death.dm +++ b/code/modules/mob/living/carbon/xenomorph/death.dm @@ -70,7 +70,6 @@ if(!QDELETED(Q) && Q != src && Q.hivenumber == hivenumber) hive.set_living_xeno_queen(Q) break - hive.on_queen_death() hive.handle_xeno_leader_pheromones() if(SSticker.mode) INVOKE_ASYNC(SSticker.mode, TYPE_PROC_REF(/datum/game_mode, check_queen_status), hivenumber) diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_ai_interaction.dm b/code/modules/mob/living/carbon/xenomorph/xeno_ai_interaction.dm index 0b384c850d..45ef729957 100644 --- a/code/modules/mob/living/carbon/xenomorph/xeno_ai_interaction.dm +++ b/code/modules/mob/living/carbon/xenomorph/xeno_ai_interaction.dm @@ -15,7 +15,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in */// - Morrow -// OBJECTS + +///////////////////////////// +// OBJECTS // +///////////////////////////// /obj/structure/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -43,7 +46,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return ..() -// MINERAL DOOR + +///////////////////////////// +// MINERAL DOOR // +///////////////////////////// /obj/structure/mineral_door/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) return DOOR_PENALTY @@ -53,11 +59,14 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return 0 /obj/structure/mineral_door/resin/xeno_ai_act(mob/living/carbon/xenomorph/acting_xeno) - if(acting_xeno.hivenumber == hivenumber) + if(IS_SAME_HIVENUMBER(acting_xeno, src)) acting_xeno.a_intent = INTENT_HELP . = ..() -/// Platforms + +///////////////////////////// +// PLATFORMS // +///////////////////////////// /obj/structure/platform/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -65,7 +74,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return DOOR_PENALTY -// Poddoors/shutters + +///////////////////////////// +// Poddoors/shutters // +///////////////////////////// /obj/structure/machinery/door/poddoor/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -82,7 +94,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return DOOR_PENALTY -// AIRLOCK + +///////////////////////////// +// AIRLOCK // +///////////////////////////// /obj/structure/machinery/door/airlock/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -96,7 +111,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return DOOR_PENALTY -// TABLES + +///////////////////////////// +// TABLES // +///////////////////////////// /obj/structure/surface/table/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(isfacehugger(X)) @@ -104,7 +122,16 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return -// HUMANS +///////////////////////////// +// MOBS // +///////////////////////////// +/mob/living/ai_check_stat() + return stat == CONSCIOUS + + +///////////////////////////// +// HUMANS // +///////////////////////////// /mob/living/carbon/human/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) if(status_flags & GODMODE) return ..() @@ -115,17 +142,61 @@ At bare minimum, make sure the relevant checks from parent types gets copied in if(status_flags & GODMODE) return + if(X.can_not_harm(src)) + return // No nibbles for friendlies + . = ..() -// XENOS +/mob/living/carbon/human/ai_can_target(mob/living/carbon/xenomorph/ai_xeno) + . = ..() + if(!.) + return FALSE + + if(species.flags & IS_SYNTHETIC) + return FALSE + + if(HAS_TRAIT(src, TRAIT_NESTED)) + return FALSE + + return TRUE + + +///////////////////////////// +// XENOS // +///////////////////////////// /mob/living/carbon/xenomorph/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) return + if(!IS_SAME_HIVENUMBER(X, src)) + return HUMAN_PENALTY + return XENO_PENALTY -// VEHICLES +/mob/living/carbon/xenomorph/xeno_ai_act(mob/living/carbon/xenomorph/X) + if(X.can_not_harm(src)) + return + + . = ..() + +/mob/living/carbon/xenomorph/ai_can_target(mob/living/carbon/xenomorph/ai_xeno) + . = ..() + if(!.) + return FALSE + + if(IS_SAME_HIVENUMBER(ai_xeno, src)) + return FALSE + + return TRUE + +/mob/living/carbon/xenomorph/ai_check_stat() + return stat != DEAD // Should slash enemy xenos, even if they are critted out + + +///////////////////////////// +// VEHICLES // +///////////////////////////// /obj/vehicle/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -133,7 +204,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return VEHICLE_PENALTY -// SENTRY + +///////////////////////////// +// SENTRY // +///////////////////////////// /obj/structure/machinery/defenses/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -141,7 +215,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return VEHICLE_PENALTY -// WINDOW FRAME + +///////////////////////////// +// WINDOW FRAME // +///////////////////////////// /obj/structure/window_frame/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) if(X.claw_type == CLAW_TYPE_VERY_SHARP || (X.claw_type >= CLAW_TYPE_SHARP && !reinforced)) return ..() @@ -154,7 +231,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return DOOR_PENALTY -// Avoid barricades if possible. + +///////////////////////////// +// BARRICADES // +///////////////////////////// /obj/structure/barricade/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -162,14 +242,24 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return BARRICADE_PENALTY -// FIRE + +///////////////////////////// +// FIRE // +///////////////////////////// /obj/flamer_fire/xeno_ai_obstacle(mob/living/carbon/xenomorph/xeno, direction, turf/target) if(xeno.caste?.fire_immunity & (FIRE_IMMUNITY_NO_IGNITE|FIRE_IMMUNITY_NO_DAMAGE)) return 0 return FIRE_PENALTY -/// Open turfs, sometimes open turfs are passed back as obstacles due to platforms and such, generally it's fast so very slight penalty mainly for handling subtypes properly + +///////////////////////////// +// FLOOR // +///////////////////////////// +/* + Sometimes open turfs are passed back as obstacles due to platforms and such, + generally it's fast so very slight penalty mainly for handling subtypes properly +*/ /turf/open/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm index f9fe22caca..1369b1fa53 100644 --- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm +++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm @@ -1034,7 +1034,7 @@ return faction_is_ally(living_mob.faction) -/datum/hive_status/proc/faction_is_ally(faction, ignore_queen_check = FALSE) +/datum/hive_status/proc/faction_is_ally(faction, ignore_queen_check = TRUE) if(faction == internal_faction) return TRUE if(!ignore_queen_check && !living_xeno_queen) @@ -1424,20 +1424,6 @@ /datum/hive_status/corrupted/renegade/faction_is_ally(faction, ignore_queen_check = TRUE) return ..() -/datum/hive_status/proc/on_queen_death() //break alliances on queen's death - if(allow_no_queen_actions || living_xeno_queen) - return - var/broken_alliances = FALSE - for(var/faction in allies) - if(!allies[faction]) - continue - change_stance(faction, FALSE) - broken_alliances = TRUE - - - if(broken_alliances) - xeno_message(SPAN_XENOANNOUNCE("With the death of the Queen, all alliances have been broken."), 3, hivenumber) - /datum/hive_status/proc/change_stance(faction, should_ally) if(faction == name) return diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 4b49a15a6f..3df4c8b83f 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -721,7 +721,7 @@ note dizziness decrements automatically in the mob's Life() proc. // facing verbs /mob/proc/canface() - if(client.moving) return 0 + if(client && client.moving) return 0 if(stat==2) return 0 if(anchored) return 0 if(monkeyizing) return 0 diff --git a/code/modules/projectiles/ammo_boxes/misc_boxes.dm b/code/modules/projectiles/ammo_boxes/misc_boxes.dm index d09a69e5bb..1793c784b1 100644 --- a/code/modules/projectiles/ammo_boxes/misc_boxes.dm +++ b/code/modules/projectiles/ammo_boxes/misc_boxes.dm @@ -88,7 +88,7 @@ var/flare_amount = 0 for(var/obj/item/storage/box/m94/flare_box in contents) flare_amount += flare_box.contents.len - flare_amount = round(flare_amount / 8) //10 packs, 8 flares each, maximum total of 10 flares we can throw out + flare_amount = round(flare_amount / 20) //10 packs, 20 flares each, maximum total of 10 flares we can throw out return flare_amount /obj/item/ammo_box/magazine/misc/flares/process_burning(datum/cause_data/flame_cause_data) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index cec5d2089e..5c762e2dc1 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -13,8 +13,9 @@ anchored = TRUE //You will not have me, space wind! flags_atom = NOINTERACT //No real need for this, but whatever. Maybe this flag will do something useful in the future. mouse_opacity = MOUSE_OPACITY_TRANSPARENT - invisibility = 100 // We want this thing to be invisible when it drops on a turf because it will be on the user's turf. We then want to make it visible as it travels. + alpha = 0 // We want this thing to be transparent when it drops on a turf because it will be on the user's turf. We then want to make it opaque as it travels. layer = FLY_LAYER + animate_movement = NO_STEPS //disables gliding because it fights against what animate() is doing var/datum/ammo/ammo //The ammo data which holds most of the actual info. @@ -48,6 +49,13 @@ var/vis_travelled = 0 /// Origin point for tracing and visual updates var/turf/vis_source + var/vis_source_pixel_x = 0 + var/vis_source_pixel_y = 0 + + /// Starting point of projectile before each flight. + var/turf/process_start_turf + var/process_start_pixel_x = 0 + var/process_start_pixel_y = 0 var/damage = 0 var/accuracy = 85 //Base projectile accuracy. Can maybe be later taken from the mob if desired. @@ -229,9 +237,11 @@ p_x = Clamp(p_x, -16, 16) p_y = Clamp(p_y, -16, 16) - if(source_turf != vis_source) + if(process_start_turf != vis_source) vis_travelled = 0 - vis_source = source_turf + vis_source = process_start_turf || source_turf + vis_source_pixel_x = process_start_pixel_x + vis_source_pixel_y = process_start_pixel_y angle = 0 // Stolen from Get_Angle() basically var/dx = p_x + aim_turf.x * 32 - source_turf.x * 32 // todo account for firer offsets @@ -248,13 +258,14 @@ else if(dx < 0) angle += 360 - var/matrix/rotate = matrix() //Change the bullet angle. - rotate.Turn(angle) - apply_transform(rotate) - /obj/projectile/process(delta_time) . = PROC_RETURN_SLEEP + var/process_start_delta_time = delta_time //easier to take it unaltered than to recalculate it later + process_start_turf = get_turf(src) //obj-level vars so update_angle() can use it without passing it through a ton of procs + process_start_pixel_x = pixel_x + process_start_pixel_y = pixel_y + // Keep going as long as we got speed and time while(speed > 0 && (speed * ((delta_time + time_carry)/10) >= 1)) time_carry -= 1/speed*10 @@ -266,8 +277,72 @@ return PROCESS_KILL time_carry += delta_time + + animate_flight(process_start_turf, process_start_pixel_x, process_start_pixel_y, process_start_delta_time) + return FALSE +//#define LERP(a, b, t) (a + (b - a) * CLAMP01(t)) +#define LERP_UNCLAMPED(a, b, t) (a + (b - a) * t) + +/// Animates the projectile across the process'ed flight. +/obj/projectile/proc/animate_flight(turf/start_turf, start_pixel_x, start_pixel_y, delta_time) + //Get pixelspace coordinates of start and end of visual path + + var/pixel_x_source = vis_source.x * world.icon_size + vis_source_pixel_x + var/pixel_y_source = vis_source.y * world.icon_size + vis_source_pixel_y + + var/turf/vis_target = path[path.len] + var/pixel_x_target = vis_target.x * world.icon_size + p_x + var/pixel_y_target = vis_target.y * world.icon_size + p_y + + //Change the bullet angle to its visual path + + var/vis_angle = get_pixel_angle(x = pixel_x_target - pixel_x_source, y = pixel_y_target - pixel_y_source) //naming vars because the proc takes y then x and that's WEIRD + var/matrix/rotate = matrix() + rotate.Turn(vis_angle) + apply_transform(rotate) + + //Determine apparent position along visual path, then lerp between start and end positions + + var/vis_length = vis_travelled + path.len + var/vis_current = vis_travelled + speed * (time_carry * 0.1) //speed * (time_carry * 0.1) for remainder time movement, visually "catching up" to where it should be + var/vis_interpolant = vis_current / vis_length + + var/pixel_x_lerped = LERP_UNCLAMPED(pixel_x_source, pixel_x_target, vis_interpolant) + var/pixel_y_lerped = LERP_UNCLAMPED(pixel_y_source, pixel_y_target, vis_interpolant) + + //Convert pixelspace to pixel offset relative to current loc + + var/turf/current_turf = get_turf(src) + var/pixel_x_rel_new = pixel_x_lerped - current_turf.x * world.icon_size + var/pixel_y_rel_new = pixel_y_lerped - current_turf.y * world.icon_size + + //Set pixel offset as from current loc to old position, so it appears to start in the old position + + pixel_x = (start_turf.x - current_turf.x) * world.icon_size + start_pixel_x + pixel_y = (start_turf.y - current_turf.y) * world.icon_size + start_pixel_y + + //Determine apparent distance travelled, then lerp for projectile fade-in + + var/dist_current = distance_travelled + speed * (time_carry * 0.1) //speed * (time_carry * 0.1) for remainder time fade-in + var/alpha_interpolant = dist_current - 1 //-1 so it transitions from transparent to opaque between dist 1-2 + var/alpha_new = LERP_UNCLAMPED(0, 255, alpha_interpolant) + + //Animate the visuals from starting position to new position + + if(projectile_flags & PROJECTILE_SHRAPNEL) //there can be a LOT of shrapnel especially from a cluster OB, not important enough for the expense of an animate() + alpha = alpha_new + pixel_x = pixel_x_rel_new + pixel_y = pixel_y_rel_new + return + + var/anim_time = delta_time * 0.1 + animate(src, pixel_x = pixel_x_rel_new, pixel_y = pixel_y_rel_new, alpha = alpha_new, time = anim_time, flags = ANIMATION_END_NOW) + +//#undef LERP +#undef LERP_UNCLAMPED + /// Flies the projectile forward one single turf /obj/projectile/proc/fly() SHOULD_NOT_SLEEP(TRUE) @@ -294,8 +369,6 @@ forceMove(next_turf) distance_travelled++ vis_travelled++ - if(distance_travelled > 1) - invisibility = 0 // Check we're still flying - in the highly unlikely but apparently possible case // we hit something through forceMove callbacks that we didn't pick up in scan_a_turf @@ -320,20 +393,6 @@ p_y *= 2 retarget(aim_turf, keep_angle = TRUE) - // Nowe we update visual offset by tracing the bullet predicted location against real one - // - // Travelled real distance so far - var/dist = vis_travelled * 32 + speed * (time_carry*10) - // Compute where we should be - var/vis_x = vis_source.x * 32 + sin(angle) * dist - var/vis_y = vis_source.y * 32 + cos(angle) * dist - // Get the difference with where we actually are - var/dx = vis_x - loc.x * 32 - var/dy = vis_y - loc.y * 32 - // Clamp and set this as pixel offsets - pixel_x = Clamp(dx, -16, 16) - pixel_y = Clamp(dy, -16, 16) - /obj/projectile/proc/retarget(atom/new_target, keep_angle = FALSE) var/turf/current_turf = get_turf(src) path = getline2(current_turf, new_target) diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm index b9a9f27a28..8be96f2740 100644 --- a/code/modules/tgs/core/core.dm +++ b/code/modules/tgs/core/core.dm @@ -42,11 +42,11 @@ var/datum/tgs_version/max_api_version = TgsMaximumApiVersion(); if(version.suite != null && version.minor != null && version.patch != null && version.deprecated_patch != null && version.deprefixed_parameter > max_api_version.deprefixed_parameter) - TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.") + TGS_ERROR_LOG("Detected unknown Interop API version! Defaulting to latest. Update the DMAPI to fix this problem.") api_datum = /datum/tgs_api/latest if(!api_datum) - TGS_ERROR_LOG("Found unsupported API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.") + TGS_ERROR_LOG("Found unsupported Interop API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.") return TGS_INFO_LOG("Activating API for version [version.deprefixed_parameter]") @@ -107,6 +107,13 @@ if(api) return api.ApiVersion() +/world/TgsEngine() +#ifdef OPENDREAM + return TGS_ENGINE_TYPE_OPENDREAM +#else + return TGS_ENGINE_TYPE_BYOND +#endif + /world/TgsInstanceName() var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) if(api) diff --git a/code/modules/tgs/v5/__interop_version.dm b/code/modules/tgs/v5/__interop_version.dm index 83420d130a..616263098f 100644 --- a/code/modules/tgs/v5/__interop_version.dm +++ b/code/modules/tgs/v5/__interop_version.dm @@ -1 +1 @@ -"5.7.0" +"5.8.0" diff --git a/code/modules/tgs/v5/_defines.dm b/code/modules/tgs/v5/_defines.dm index 48969c0c7d..1c7d67d20c 100644 --- a/code/modules/tgs/v5/_defines.dm +++ b/code/modules/tgs/v5/_defines.dm @@ -8,7 +8,6 @@ #define DMAPI5_TOPIC_REQUEST_LIMIT 65528 #define DMAPI5_TOPIC_RESPONSE_LIMIT 65529 -#define DMAPI5_BRIDGE_COMMAND_PORT_UPDATE 0 #define DMAPI5_BRIDGE_COMMAND_STARTUP 1 #define DMAPI5_BRIDGE_COMMAND_PRIME 2 #define DMAPI5_BRIDGE_COMMAND_REBOOT 3 @@ -18,6 +17,7 @@ #define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier" #define DMAPI5_PARAMETER_CUSTOM_COMMANDS "customCommands" +#define DMAPI5_PARAMETER_TOPIC_PORT "topicPort" #define DMAPI5_CHUNK "chunk" #define DMAPI5_CHUNK_PAYLOAD "payload" diff --git a/code/modules/tgs/v5/api.dm b/code/modules/tgs/v5/api.dm index 7226f29bba..a5c064a8ea 100644 --- a/code/modules/tgs/v5/api.dm +++ b/code/modules/tgs/v5/api.dm @@ -8,8 +8,12 @@ var/reboot_mode = TGS_REBOOT_MODE_NORMAL + /// List of chat messages list()s that attempted to be sent during a topic call. To be bundled in the result of the call var/list/intercepted_message_queue + /// List of chat messages list()s that attempted to be sent during a topic call. To be bundled in the result of the call + var/list/offline_message_queue + var/list/custom_commands var/list/test_merges @@ -17,6 +21,8 @@ var/list/chat_channels var/initialized = FALSE + var/initial_bridge_request_received = FALSE + var/datum/tgs_version/interop_version var/chunked_requests = 0 var/list/chunked_topics = list() @@ -25,7 +31,8 @@ /datum/tgs_api/v5/New() . = ..() - TGS_DEBUG_LOG("V5 API created") + interop_version = version + TGS_DEBUG_LOG("V5 API created: [json_encode(args)]") /datum/tgs_api/v5/ApiVersion() return new /datum/tgs_version( @@ -38,8 +45,8 @@ access_identifier = world.params[DMAPI5_PARAM_ACCESS_IDENTIFIER] var/datum/tgs_version/api_version = ApiVersion() - version = null - var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands())) + version = null // we want this to be the TGS version, not the interop version + var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands(), DMAPI5_PARAMETER_TOPIC_PORT = GetTopicPort())) if(!istype(bridge_response)) TGS_ERROR_LOG("Failed initial bridge request!") return FALSE @@ -53,7 +60,8 @@ TGS_INFO_LOG("DMAPI validation, exiting...") TerminateWorld() - version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION]) + initial_bridge_request_received = TRUE + version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION]) // reassigning this because it can change if TGS updates security_level = runtime_information[DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL] visibility = runtime_information[DMAPI5_RUNTIME_INFORMATION_VISIBILITY] instance_name = runtime_information[DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME] @@ -102,10 +110,17 @@ initialized = TRUE return TRUE +/datum/tgs_api/v5/proc/GetTopicPort() +#if defined(OPENDREAM) && defined(OPENDREAM_TOPIC_PORT_EXISTS) + return "[world.opendream_topic_port]" +#else + return null +#endif + /datum/tgs_api/v5/proc/RequireInitialBridgeResponse() TGS_DEBUG_LOG("RequireInitialBridgeResponse()") var/logged = FALSE - while(!version) + while(!initial_bridge_request_received) if(!logged) TGS_DEBUG_LOG("RequireInitialBridgeResponse: Starting sleep") logged = TRUE @@ -183,17 +198,7 @@ var/datum/tgs_chat_channel/channel = I ids += channel.id - message2 = UpgradeDeprecatedChatMessage(message2) - - if (!length(channels)) - return - - var/list/data = message2._interop_serialize() - data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = ids - if(intercepted_message_queue) - intercepted_message_queue += list(data) - else - Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data)) + SendChatMessageRaw(message2, ids) /datum/tgs_api/v5/ChatTargetedBroadcast(datum/tgs_message_content/message2, admin_only) var/list/channels = list() @@ -202,26 +207,42 @@ if (!channel.is_private_channel && ((channel.is_admin_channel && admin_only) || (!channel.is_admin_channel && !admin_only))) channels += channel.id + SendChatMessageRaw(message2, channels) + +/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message2, datum/tgs_chat_user/user) + SendChatMessageRaw(message2, list(user.channel.id)) + +/datum/tgs_api/v5/proc/SendChatMessageRaw(datum/tgs_message_content/message2, list/channel_ids) message2 = UpgradeDeprecatedChatMessage(message2) - if (!length(channels)) + if (!length(channel_ids)) return var/list/data = message2._interop_serialize() - data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channels + data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channel_ids if(intercepted_message_queue) intercepted_message_queue += list(data) - else - Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data)) + return -/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message2, datum/tgs_chat_user/user) - message2 = UpgradeDeprecatedChatMessage(message2) - var/list/data = message2._interop_serialize() - data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = list(user.channel.id) - if(intercepted_message_queue) - intercepted_message_queue += list(data) + if(offline_message_queue) + offline_message_queue += list(data) + return + + if(detached) + offline_message_queue = list(data) + + WaitForReattach(FALSE) + + data = offline_message_queue + offline_message_queue = null + + for(var/queued_message in data) + SendChatDataRaw(queued_message) else - Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data)) + SendChatDataRaw(data) + +/datum/tgs_api/v5/proc/SendChatDataRaw(list/data) + Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data)) /datum/tgs_api/v5/ChatChannelInfo() RequireInitialBridgeResponse() diff --git a/code/modules/tgs/v5/bridge.dm b/code/modules/tgs/v5/bridge.dm index 37f58bcdf6..a0ab359876 100644 --- a/code/modules/tgs/v5/bridge.dm +++ b/code/modules/tgs/v5/bridge.dm @@ -48,7 +48,9 @@ var/json = CreateBridgeData(command, data, TRUE) var/encoded_json = url_encode(json) - var/url = "http://127.0.0.1:[server_port]/Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]" + var/api_prefix = interop_version.minor >= 8 ? "api/" : "" + + var/url = "http://127.0.0.1:[server_port]/[api_prefix]Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]" return url /datum/tgs_api/v5/proc/CreateBridgeData(command, list/data, needs_auth) @@ -81,11 +83,16 @@ TGS_ERROR_LOG("Failed bridge request: [bridge_request]") return - var/response_json = file2text(export_response["CONTENT"]) - if(!response_json) + var/content = export_response["CONTENT"] + if(!content) TGS_ERROR_LOG("Failed bridge request, missing content!") return + var/response_json = file2text(content) + if(!response_json) + TGS_ERROR_LOG("Failed bridge request, failed to load content!") + return + var/list/bridge_response = json_decode(response_json) if(!bridge_response) TGS_ERROR_LOG("Failed bridge request, bad json: [response_json]") diff --git a/code/modules/tgs/v5/topic.dm b/code/modules/tgs/v5/topic.dm index 2ef0c70a97..05e6c4e1b2 100644 --- a/code/modules/tgs/v5/topic.dm +++ b/code/modules/tgs/v5/topic.dm @@ -175,6 +175,7 @@ var/list/reattach_response = TopicResponse(error_message) reattach_response[DMAPI5_PARAMETER_CUSTOM_COMMANDS] = ListCustomCommands() + reattach_response[DMAPI5_PARAMETER_TOPIC_PORT] = GetTopicPort() return reattach_response if(DMAPI5_TOPIC_COMMAND_SEND_CHUNK) diff --git a/code/modules/tgs/v5/undefs.dm b/code/modules/tgs/v5/undefs.dm index fd1ed7e4cf..d531d4b7b9 100644 --- a/code/modules/tgs/v5/undefs.dm +++ b/code/modules/tgs/v5/undefs.dm @@ -8,7 +8,6 @@ #undef DMAPI5_TOPIC_REQUEST_LIMIT #undef DMAPI5_TOPIC_RESPONSE_LIMIT -#undef DMAPI5_BRIDGE_COMMAND_PORT_UPDATE #undef DMAPI5_BRIDGE_COMMAND_STARTUP #undef DMAPI5_BRIDGE_COMMAND_PRIME #undef DMAPI5_BRIDGE_COMMAND_REBOOT @@ -18,6 +17,7 @@ #undef DMAPI5_PARAMETER_ACCESS_IDENTIFIER #undef DMAPI5_PARAMETER_CUSTOM_COMMANDS +#undef DMAPI5_PARAMETER_TOPIC_PORT #undef DMAPI5_CHUNK #undef DMAPI5_CHUNK_PAYLOAD diff --git a/code/modules/vehicles/hardpoints/hardpoint.dm b/code/modules/vehicles/hardpoints/hardpoint.dm index ee49ebc0ab..acdefca18f 100644 --- a/code/modules/vehicles/hardpoints/hardpoint.dm +++ b/code/modules/vehicles/hardpoints/hardpoint.dm @@ -1,22 +1,20 @@ -/* - Hardpoints are any items that attach to a base vehicle, such as wheels/treads, support systems and guns -*/ - +/** + * Hardpoints are any items that attach to a base vehicle, such as wheels/treads, support systems and guns + */ /obj/item/hardpoint //------MAIN VARS---------- - // Which slot is this hardpoint in - // Purely to check for conflicting hardpoints + /// Which slot is this hardpoint in. Purely to check for conflicting hardpoints. var/slot - // The vehicle this hardpoint is installed on + /// The vehicle this hardpoint is installed on. var/obj/vehicle/multitile/owner health = 100 w_class = SIZE_LARGE - // Determines how much of any incoming damage is actually taken + /// Determines how much of any incoming damage is actually taken. var/damage_multiplier = 1 - // Origin coords of the hardpoint relative to the vehicle + /// Origin coords of the hardpoint relative to the vehicle. var/list/origins = list(0, 0) var/list/buff_multipliers @@ -32,13 +30,13 @@ var/disp_icon //This also differentiates tank vs apc vs other var/disp_icon_state - // List of pixel offsets for each direction + /// List of pixel offsets for each direction. var/list/px_offsets - //visual layer of hardpoint when on vehicle + /// Visual layer of hardpoint when on vehicle. var/hdpt_layer = HDPT_LAYER_WHEELS - // List of offsets for where to place the muzzle flash for each direction + /// List of offsets for where to place the muzzle flash for each direction. var/list/muzzle_flash_pos = list( "1" = list(0, 0), "2" = list(0, 0), @@ -54,33 +52,23 @@ var/const_mz_offset_y = 0 //------SOUNDS VARS---------- - // Sounds to play when the module activated/fired + /// Sounds to play when the module activated/fired. var/list/activation_sounds //------INTERACTION VARS---------- - //which seat can use this module + /// Which seat can use this module. var/allowed_seat = VEHICLE_GUNNER - //Cooldown on use of the hardpoint - var/cooldown = 100 - var/next_use = 0 - - //whether hardpoint has activatable ability like shooting or zooming + /// Whether hardpoint has activatable ability like shooting or zooming. var/activatable = 0 - //used to prevent welder click spam + /// Used to prevent welder click spam. var/being_repaired = FALSE - //current user. We can have only one user at a time. Better never change that - var/user - - //Accuracy of the hardpoint. (which is, in fact, a scatter. Need to change this system) - var/accuracy = 1 - - // The firing arc of this hardpoint + /// The firing arc of this hardpoint. var/firing_arc = 0 //in degrees. 0 skips whole arc of fire check // Muzzleflash @@ -91,17 +79,53 @@ //------AMMUNITION VARS---------- - //Currently loaded ammo that we shoot from + /// Currently loaded ammo that we shoot from. var/obj/item/ammo_magazine/hardpoint/ammo - //spare magazines that we can reload from + /// Spare magazines that we can reload from. var/list/backup_clips - //maximum amount of spare mags + /// Maximum amount of spare mags. var/max_clips = 0 /// An assoc list in the format list(/datum/element/bullet_trait_to_give = list(...args)) - /// that will be given to a projectile fired from the hardpoint + /// that will be given to a projectile fired from the hardpoint. var/list/list/traits_to_give + /// How much the bullet scatters when fired, in degrees. + var/scatter = 0 + /// How many bullets the gun fired while burst firing/auto firing. + var/shots_fired = 0 + /// Delay before a new firing sequence can start. + COOLDOWN_DECLARE(fire_cooldown) + + // Firemodes. + /// Current selected firemode of the gun. + var/gun_firemode = GUN_FIREMODE_SEMIAUTO + /// List of allowed firemodes. + var/list/gun_firemode_list = list( + GUN_FIREMODE_SEMIAUTO, + ) + + // Semi-auto and full-auto. + /// For regular shots, how long to wait before firing again. Use modify_fire_delay and set_fire_delay instead of modifying this on the fly + var/fire_delay = 0 + /// The multiplier for how much slower this should fire in automatic mode. 1 is normal, 1.2 is 20% slower, 2 is 100% slower, etc. Protected due to it never needing to be edited. + var/autofire_slow_mult = 1 + /// If the gun is currently auto firing. + var/auto_firing = FALSE + + // Burst fire. + /// How many shots can the weapon shoot in burst? Anything less than 2 and you cannot toggle burst. Use modify_burst_amount and set_burst_amount instead of modifying this + var/burst_amount = 1 + /// The delay in between shots. Lower = less delay = faster. Use modify_burst_delay and set_burst_delay instead of modifying this + var/burst_delay = 1 + /// When burst-firing, this number is extra time before the weapon can fire again. + var/extra_delay = 0 + /// If the gun is currently burst firing. + var/burst_firing = FALSE + + /// Currently selected target to fire at. Set with set_target(). + var/atom/target + //----------------------------- //------GENERAL PROCS---------- //----------------------------- @@ -109,6 +133,7 @@ /obj/item/hardpoint/Initialize() . = ..() set_bullet_traits() + AddComponent(/datum/component/automatedfire/autofire, fire_delay, burst_delay, burst_amount, gun_firemode, autofire_slow_mult, CALLBACK(src, PROC_REF(set_burst_firing)), CALLBACK(src, PROC_REF(reset_fire)), CALLBACK(src, PROC_REF(fire_wrapper)), callback_set_firing = CALLBACK(src, PROC_REF(set_auto_firing))) /obj/item/hardpoint/Destroy() if(owner) @@ -117,7 +142,7 @@ owner = null QDEL_NULL_LIST(backup_clips) QDEL_NULL(ammo) - + set_target(null) return ..() /obj/item/hardpoint/ex_act(severity) @@ -166,37 +191,64 @@ /obj/item/hardpoint/proc/get_integrity_percent() return 100.0*health/initial(health) -/obj/item/hardpoint/proc/on_install(obj/vehicle/multitile/V) - apply_buff(V) - return +/// Apply hardpoint effects to vehicle and self. +/obj/item/hardpoint/proc/on_install(obj/vehicle/multitile/vehicle) + if(!vehicle) //in loose holder + return + RegisterSignal(vehicle, COMSIG_GUN_RECALCULATE_ATTACHMENT_BONUSES, PROC_REF(recalculate_hardpoint_bonuses)) + apply_buff(vehicle) -/obj/item/hardpoint/proc/on_uninstall(obj/vehicle/multitile/V) - remove_buff(V) - return +/// Remove hardpoint effects from vehicle and self. +/obj/item/hardpoint/proc/on_uninstall(obj/vehicle/multitile/vehicle) + if(!vehicle) //in loose holder + return + UnregisterSignal(vehicle, COMSIG_GUN_RECALCULATE_ATTACHMENT_BONUSES) + remove_buff(vehicle) + //resetting values like set_gun_config_values() would be tidy, but unnecessary as it gets recalc'd on install anyway -//applying passive buffs like damage type resistance, speed, accuracy, cooldowns -/obj/item/hardpoint/proc/apply_buff(obj/vehicle/multitile/V) +/// Applying passive buffs like damage type resistance, speed, accuracy, cooldowns. +/obj/item/hardpoint/proc/apply_buff(obj/vehicle/multitile/vehicle) if(buff_applied) return if(LAZYLEN(type_multipliers)) for(var/type in type_multipliers) - V.dmg_multipliers[type] *= LAZYACCESS(type_multipliers, type) + vehicle.dmg_multipliers[type] *= LAZYACCESS(type_multipliers, type) if(LAZYLEN(buff_multipliers)) for(var/type in buff_multipliers) - V.misc_multipliers[type] *= LAZYACCESS(buff_multipliers, type) + vehicle.misc_multipliers[type] *= LAZYACCESS(buff_multipliers, type) buff_applied = TRUE + SEND_SIGNAL(vehicle, COMSIG_GUN_RECALCULATE_ATTACHMENT_BONUSES) -//removing buffs -/obj/item/hardpoint/proc/remove_buff(obj/vehicle/multitile/V) +/// Removing passive buffs like damage type resistance, speed, accuracy, cooldowns. +/obj/item/hardpoint/proc/remove_buff(obj/vehicle/multitile/vehicle) if(!buff_applied) return if(LAZYLEN(type_multipliers)) for(var/type in type_multipliers) - V.dmg_multipliers[type] *= 1 / LAZYACCESS(type_multipliers, type) + vehicle.dmg_multipliers[type] *= 1 / LAZYACCESS(type_multipliers, type) if(LAZYLEN(buff_multipliers)) for(var/type in buff_multipliers) - V.misc_multipliers[type] *= 1 / LAZYACCESS(buff_multipliers, type) + vehicle.misc_multipliers[type] *= 1 / LAZYACCESS(buff_multipliers, type) buff_applied = FALSE + SEND_SIGNAL(vehicle, COMSIG_GUN_RECALCULATE_ATTACHMENT_BONUSES) + +/// Recalculates hardpoint values based on vehicle modifiers. +/obj/item/hardpoint/proc/recalculate_hardpoint_bonuses() + scatter = initial(scatter) / owner.misc_multipliers["accuracy"] + var/cooldown_mult = owner.misc_multipliers["cooldown"] + set_fire_delay(initial(fire_delay) * cooldown_mult) + set_burst_delay(initial(burst_delay) * cooldown_mult) + extra_delay = initial(extra_delay) * cooldown_mult + +/// Setter for fire_delay. +/obj/item/hardpoint/proc/set_fire_delay(value) + fire_delay = value + SEND_SIGNAL(src, COMSIG_GUN_AUTOFIREDELAY_MODIFIED, fire_delay) + +/// Setter for burst_delay. +/obj/item/hardpoint/proc/set_burst_delay(value) + burst_delay = value + SEND_SIGNAL(src, COMSIG_GUN_BURST_SHOT_DELAY_MODIFIED, burst_delay) //this proc called on each move of vehicle /obj/item/hardpoint/proc/on_move(turf/old, turf/new_turf, move_dir) @@ -253,13 +305,12 @@ return data -// Traces backwards from the gun origin to the vehicle to check for obstacles between the vehicle and the muzzle -/obj/item/hardpoint/proc/clear_los(atom/A) - +/// Traces backwards from the gun origin to the vehicle to check for obstacles between the vehicle and the muzzle. +/obj/item/hardpoint/proc/clear_los() if(origins[1] == 0 && origins[2] == 0) //skipping check for modules we don't need this return TRUE - var/turf/muzzle_turf = locate(owner.x + origins[1], owner.y + origins[2], owner.z) + var/turf/muzzle_turf = get_origin_turf() var/turf/checking_turf = muzzle_turf while(!(owner in checking_turf)) @@ -268,24 +319,24 @@ return FALSE // Ensure that we can pass over all objects in the turf - for(var/obj/O in checking_turf) + for(var/obj/object in checking_turf) // Since vehicles are multitile the - if(O == owner) + if(object == owner) continue // Non-dense objects are irrelevant - if(!O.density) + if(!object.density) continue // Make sure we can pass object from all directions - if(!(O.pass_flags.flags_can_pass_all & PASS_OVER_THROW_ITEM)) - if(!(O.flags_atom & ON_BORDER)) + if(!HAS_FLAG(object.pass_flags.flags_can_pass_all, PASS_OVER_THROW_ITEM)) + if(!HAS_FLAG(object.flags_atom, ON_BORDER)) return FALSE //If we're behind the object, check the behind pass flags - else if(dir == O.dir && !(O.pass_flags.flags_can_pass_behind & PASS_OVER_THROW_ITEM)) + else if(dir == object.dir && !HAS_FLAG(object.pass_flags.flags_can_pass_behind, PASS_OVER_THROW_ITEM)) return FALSE //If we're in front, check front pass flags - else if(dir == turn(O.dir, 180) && !(O.pass_flags.flags_can_pass_front & PASS_OVER_THROW_ITEM)) + else if(dir == turn(object.dir, 180) && !HAS_FLAG(object.pass_flags.flags_can_pass_front, PASS_OVER_THROW_ITEM)) return FALSE // Trace back towards the vehicle @@ -297,47 +348,6 @@ //------INTERACTION PROCS---------- //----------------------------- -//If the hardpoint can be activated by current user -/obj/item/hardpoint/proc/can_activate(mob/user, atom/A) - if(!owner) - return - - var/seat = owner.get_mob_seat(user) - if(!seat) - return - - if(seat != allowed_seat) - to_chat(user, SPAN_WARNING("Only [allowed_seat] can use [name].")) - return - - if(health <= 0) - to_chat(user, SPAN_WARNING("\The [name] is broken!")) - return FALSE - - if(world.time < next_use) - if(cooldown >= 20) //filter out guns with high firerate to prevent message spam. - to_chat(user, SPAN_WARNING("You need to wait [SPAN_HELPFUL((next_use - world.time) / 10)] seconds before [name] can be used again.")) - return FALSE - - if(ammo && ammo.current_rounds <= 0) - to_chat(user, SPAN_WARNING("\The [name] is out of ammo! Magazines: [SPAN_HELPFUL(LAZYLEN(backup_clips))]/[SPAN_HELPFUL(max_clips)]")) - return FALSE - - if(!in_firing_arc(A)) - to_chat(user, SPAN_WARNING("The target is not within your firing arc!")) - return FALSE - - if(!clear_los(A)) - to_chat(user, SPAN_WARNING("You don't have a clear line of sight to the target!")) - return FALSE - - return TRUE - -//Called when you want to activate the hardpoint, by default firing a gun -//This can also be used for some type of temporary buff or toggling mode, up to you -/obj/item/hardpoint/proc/activate(mob/user, atom/A) - fire(user, A) - /obj/item/hardpoint/proc/deactivate() return @@ -490,76 +500,201 @@ user.visible_message(SPAN_NOTICE("[user] stops repairing \the [name]."), SPAN_NOTICE("You stop repairing \the [name]. The integrity of the module is at [SPAN_HELPFUL(round(get_integrity_percent()))]%.")) return -//determines whether something is in firing arc of a hardpoint -/obj/item/hardpoint/proc/in_firing_arc(atom/A) - if(!owner) - return FALSE +/// Setter proc for the automatic firing flag. +/obj/item/hardpoint/proc/set_auto_firing(auto = FALSE) + if(auto_firing != auto) + auto_firing = auto + if(!auto_firing) //end-of-fire, show changed ammo + display_ammo() + +/// Setter proc for the burst firing flag. +/obj/item/hardpoint/proc/set_burst_firing(burst = FALSE) + if(burst_firing != burst) + burst_firing = burst + if(!burst_firing) //end-of-fire, show changed ammo + display_ammo() + +/// Clean all firing references. +/obj/item/hardpoint/proc/reset_fire() + shots_fired = 0 + set_target(null) + set_auto_firing(FALSE) //on abnormal exits automatic fire doesn't call set_auto_firing() + +/// Set the target and take care of hard delete. +/obj/item/hardpoint/proc/set_target(atom/object) + if(object == target || object == loc) + return + if(target) + UnregisterSignal(target, COMSIG_PARENT_QDELETING) + target = object + if(target) + RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(clean_target)) + +/// Set the target to its turf, so we keep shooting even when it was qdeled. +/obj/item/hardpoint/proc/clean_target() + SIGNAL_HANDLER + target = get_turf(target) + +/// Print how much ammo is left to chat. +/obj/item/hardpoint/proc/display_ammo(mob/user) + if(!user) + user = owner.get_seat_mob(allowed_seat) + if(!user) + return - if(!firing_arc) - return TRUE + if(ammo) + to_chat(user, SPAN_WARNING("[name] Ammo: [SPAN_HELPFUL(ammo ? ammo.current_rounds : 0)]/[SPAN_HELPFUL(ammo ? ammo.max_rounds : 0)] | Mags: [SPAN_HELPFUL(LAZYLEN(backup_clips))]/[SPAN_HELPFUL(max_clips)]")) - var/turf/T = get_turf(A) - if(!T) - return FALSE +/// Reset variables used in firing and remove the gun from the autofire system. +/obj/item/hardpoint/proc/stop_fire(datum/source, atom/object, turf/location, control, params) + SEND_SIGNAL(src, COMSIG_GUN_STOP_FIRE) + if(auto_firing) + reset_fire() //automatic fire doesn't reset itself from COMSIG_GUN_STOP_FIRE - var/dx = T.x - (owner.x + origins[1]/2) - var/dy = T.y - (owner.y + origins[2]/2) - - var/deg = 0 - switch(dir) - if(EAST) - deg = 0 - if(NORTH) - deg = -90 - if(WEST) - deg = 180 - if(SOUTH) - deg = 90 - - var/nx = dx * cos(deg) - dy * sin(deg) - var/ny = dx * sin(deg) + dy * cos(deg) - if(nx == 0) - return firing_arc >= 90 - - var/angle = arctan(ny/nx) - if(nx < 0) - angle += 180 - - return abs(angle) <= (firing_arc/2) - -//doing last preparation before actually firing gun -/obj/item/hardpoint/proc/fire(mob/user, atom/A) - if(!ammo) //Prevents a runtime +/// Update the target if you dragged your mouse. +/obj/item/hardpoint/proc/change_target(datum/source, atom/src_object, atom/over_object, turf/src_location, turf/over_location, src_control, over_control, params) + set_target(get_turf_on_clickcatcher(over_object, source, params)) + +/// Check if the gun can fire and add it to bucket autofire system if needed, or just fire the gun if not. +/obj/item/hardpoint/proc/start_fire(datum/source, atom/object, turf/location, control, params) + if(istype(object, /atom/movable/screen)) return - if(ammo.current_rounds <= 0) + + if(QDELETED(object)) return - next_use = world.time + cooldown * owner.misc_multipliers["cooldown"] - if(!prob((accuracy * 100) / owner.misc_multipliers["accuracy"])) - A = get_step(get_turf(A), pick(cardinal)) + if(!auto_firing && !burst_firing && !COOLDOWN_FINISHED(src, fire_cooldown)) + if(max(fire_delay, burst_delay + extra_delay) >= 2.0 SECONDS) //filter out guns with high firerate to prevent message spam. + to_chat(source, SPAN_WARNING("You need to wait [SPAN_HELPFUL(COOLDOWN_SECONDSLEFT(src, fire_cooldown))] seconds before [name] can be used again.")) + return - if(LAZYLEN(activation_sounds)) - playsound(get_turf(src), pick(activation_sounds), 60, 1) + set_target(get_turf_on_clickcatcher(object, source, params)) - fire_projectile(user, A) + if(gun_firemode == GUN_FIREMODE_SEMIAUTO) + var/fire_return = try_fire(object, source, params) + //end-of-fire, show ammo (if changed) + if(fire_return == AUTOFIRE_CONTINUE) + reset_fire() + display_ammo(source) + else + SEND_SIGNAL(src, COMSIG_GUN_FIRE) + +/// Wrapper proc for the autofire system to ensure the important args aren't null. +/obj/item/hardpoint/proc/fire_wrapper(atom/target, mob/living/user, params) + SHOULD_NOT_OVERRIDE(TRUE) + if(!target) + target = src.target + if(!user) + user = owner.get_seat_mob(allowed_seat) + if(!target || !user) + return NONE + + return try_fire(target, user, params) + +/// Tests if firing should be interrupted, otherwise fires. +/obj/item/hardpoint/proc/try_fire(atom/target, mob/living/user, params) + if(health <= 0) + to_chat(user, SPAN_WARNING("\The [name] is broken!")) + return NONE - to_chat(user, SPAN_WARNING("[name] Ammo: [SPAN_HELPFUL(ammo ? ammo.current_rounds : 0)]/[SPAN_HELPFUL(ammo ? ammo.max_rounds : 0)] | Mags: [SPAN_HELPFUL(LAZYLEN(backup_clips))]/[SPAN_HELPFUL(max_clips)]")) + if(ammo && ammo.current_rounds <= 0) + click_empty(user) + return NONE -//finally firing the gun -/obj/item/hardpoint/proc/fire_projectile(mob/user, atom/A) - set waitfor = 0 + if(!in_firing_arc(target)) + to_chat(user, SPAN_WARNING("The target is not within your firing arc!")) + return NONE - var/turf/origin_turf = get_turf(src) - origin_turf = locate(origin_turf.x + origins[1], origin_turf.y + origins[2], origin_turf.z) + if(!clear_los()) + to_chat(user, SPAN_WARNING("The muzzle is obstructed!")) + return NONE - var/obj/projectile/P = generate_bullet(user, origin_turf) - SEND_SIGNAL(P, COMSIG_BULLET_USER_EFFECTS, user) - P.fire_at(A, user, src, P.ammo.max_range, P.ammo.shell_speed) + return handle_fire(target, user, params) - if(use_muzzle_flash) - muzzle_flash(Get_Angle(origin_turf, A)) +/// Actually fires the gun, sets up the projectile and fires it. +/obj/item/hardpoint/proc/handle_fire(atom/target, mob/living/user, params) + var/turf/origin_turf = get_origin_turf() + var/obj/projectile/projectile_to_fire = generate_bullet(user, origin_turf) ammo.current_rounds-- + SEND_SIGNAL(projectile_to_fire, COMSIG_BULLET_USER_EFFECTS, user) + + // turf-targeted projectiles are fired without scatter, because proc would raytrace them further away + var/ammo_flags = projectile_to_fire.ammo.flags_ammo_behavior | projectile_to_fire.projectile_override_flags + if(!HAS_FLAG(ammo_flags, AMMO_HITS_TARGET_TURF) && !HAS_FLAG(ammo_flags, AMMO_EXPLOSIVE)) //AMMO_EXPLOSIVE is also a turf-targeted projectile + projectile_to_fire.scatter = scatter + target = simulate_scatter(projectile_to_fire, target, origin_turf, get_turf(target), user) + + INVOKE_ASYNC(projectile_to_fire, TYPE_PROC_REF(/obj/projectile, fire_at), target, user, src, projectile_to_fire.ammo.max_range, projectile_to_fire.ammo.shell_speed) + projectile_to_fire = null + + shots_fired++ + play_firing_sounds() + if(use_muzzle_flash) + muzzle_flash(Get_Angle(origin_turf, target)) + + set_fire_cooldown(gun_firemode) + + return AUTOFIRE_CONTINUE + +/// Start cooldown to respect delay of firemode. +/obj/item/hardpoint/proc/set_fire_cooldown(firemode) + var/cooldown_time = 0 + switch(firemode) + if(GUN_FIREMODE_SEMIAUTO) + cooldown_time = fire_delay + if(GUN_FIREMODE_BURSTFIRE) + cooldown_time = burst_delay + extra_delay + if(GUN_FIREMODE_AUTOMATIC) + cooldown_time = fire_delay + COOLDOWN_START(src, fire_cooldown, cooldown_time) + +/// Adjust target based on random scatter angle. +/obj/item/hardpoint/proc/simulate_scatter(obj/projectile/projectile_to_fire, atom/target, turf/curloc, turf/targloc) + var/fire_angle = Get_Angle(curloc, targloc) + var/total_scatter_angle = projectile_to_fire.scatter + + //Not if the gun doesn't scatter at all, or negative scatter. + if(total_scatter_angle > 0) + fire_angle += rand(-total_scatter_angle, total_scatter_angle) + target = get_angle_target_turf(curloc, fire_angle, 30) + + return target + +/// Get turf at hardpoint origin offset, used as the muzzle. +/obj/item/hardpoint/proc/get_origin_turf() + return get_offset_target_turf(get_turf(src), origins[1], origins[2]) + +/// Plays 'click' noise and announced to chat. Usually called when weapon empty. +/obj/item/hardpoint/proc/click_empty(mob/user) + playsound(src, 'sound/weapons/gun_empty.ogg', 25, 1, 5) + if(user) + to_chat(user, SPAN_WARNING("*click*")) + +/// Selects and plays a firing sound from the list. +/obj/item/hardpoint/proc/play_firing_sounds() + if(LAZYLEN(activation_sounds)) + playsound(get_turf(src), pick(activation_sounds), 60, 1) + +/// Determines whether something is in firing arc of a hardpoint. +/obj/item/hardpoint/proc/in_firing_arc(atom/target) + if(!firing_arc || !ISINRANGE_EX(firing_arc, 0, 360)) + return TRUE + + var/turf/muzzle_turf = get_origin_turf() + var/turf/target_turf = get_turf(target) + + //same tile angle returns EAST, returning FALSE to ensure consistency + if(muzzle_turf == target_turf) + return FALSE + + var/angle_diff = SIMPLIFY_DEGREES(dir2angle(dir) - get_angle(muzzle_turf, target_turf)) + if(angle_diff < -180) + angle_diff += 360 + else if(angle_diff > 180) + angle_diff -= 360 + + return abs(angle_diff) <= (firing_arc * 0.5) //----------------------------- //------ICON PROCS---------- diff --git a/code/modules/vehicles/hardpoints/holder/holder.dm b/code/modules/vehicles/hardpoints/holder/holder.dm index b14e078a39..fc8e849d10 100644 --- a/code/modules/vehicles/hardpoints/holder/holder.dm +++ b/code/modules/vehicles/hardpoints/holder/holder.dm @@ -43,10 +43,21 @@ for(var/obj/item/hardpoint/H in hardpoints) H.take_damage(damage) -/obj/item/hardpoint/holder/on_install(obj/vehicle/multitile/V) - for(var/obj/item/hardpoint/HP in hardpoints) - HP.owner = V - return +/obj/item/hardpoint/holder/on_install(obj/vehicle/multitile/vehicle) + ..() + if(!vehicle) //in loose holder + return + for(var/obj/item/hardpoint/hardpoint in hardpoints) + hardpoint.owner = vehicle + hardpoint.on_install(vehicle) + +/obj/item/hardpoint/holder/on_uninstall(obj/vehicle/multitile/vehicle) + if(!vehicle) //in loose holder + return + for(var/obj/item/hardpoint/hardpoint in hardpoints) + hardpoint.on_uninstall(vehicle) + hardpoint.owner = null + ..() /obj/item/hardpoint/holder/proc/can_install(obj/item/hardpoint/H) // Can only have 1 hardpoint of each slot type @@ -121,16 +132,17 @@ H.forceMove(src) LAZYADD(hardpoints, H) + H.on_install(owner) H.rotate(turning_angle(H.dir, dir)) /obj/item/hardpoint/holder/proc/remove_hardpoint(obj/item/hardpoint/H, turf/uninstall_to) if(!hardpoints) return - hardpoints -= H H.forceMove(uninstall_to ? uninstall_to : get_turf(src)) + H.on_uninstall(owner) H.reset_rotation() - + hardpoints -= H H.owner = null if(H.health <= 0) diff --git a/code/modules/vehicles/hardpoints/holder/tank_turret.dm b/code/modules/vehicles/hardpoints/holder/tank_turret.dm index 27ab6c9540..896628e609 100644 --- a/code/modules/vehicles/hardpoints/holder/tank_turret.dm +++ b/code/modules/vehicles/hardpoints/holder/tank_turret.dm @@ -13,8 +13,6 @@ density = TRUE //come on, it's huge activatable = TRUE - cooldown = 150 - accuracy = 0.8 ammo = new /obj/item/ammo_magazine/hardpoint/turret_smoke max_clips = 2 @@ -60,6 +58,15 @@ // Used during the windup var/rotating = FALSE + scatter = 4 + gun_firemode = GUN_FIREMODE_BURSTFIRE + gun_firemode_list = list( + GUN_FIREMODE_BURSTFIRE, + ) + burst_amount = 2 + burst_delay = 1.0 SECONDS + extra_delay = 13.0 SECONDS + /obj/item/hardpoint/holder/tank_turret/update_icon() var/broken = (health <= 0) icon_state = "tank_turret_[broken]" @@ -182,12 +189,7 @@ user.client.pixel_x = -1 * AM.view_tile_offset * 32 user.client.pixel_y = 0 -/obj/item/hardpoint/holder/tank_turret/fire(mob/user, atom/A) - if(ammo.current_rounds <= 0) - return - - next_use = world.time + cooldown - +/obj/item/hardpoint/holder/tank_turret/try_fire(atom/target, mob/living/user, params) var/turf/L var/turf/R switch(owner.dir) @@ -204,26 +206,14 @@ L = locate(owner.x - 4, owner.y + 2, owner.z) R = locate(owner.x - 4, owner.y - 2, owner.z) - if(LAZYLEN(activation_sounds)) - playsound(get_turf(src), pick(activation_sounds), 60, 1) - fire_projectile(user, L) + if(shots_fired) + target = R + else + target = L - sleep(10) + return ..() - if(LAZYLEN(activation_sounds)) - playsound(get_turf(src), pick(activation_sounds), 60, 1) - fire_projectile(user, R) - - to_chat(user, SPAN_WARNING("Smoke Screen uses left: [SPAN_HELPFUL(ammo ? ammo.current_rounds / 2 : 0)]/[SPAN_HELPFUL(ammo ? ammo.max_rounds / 2 : 0)] | Mags: [SPAN_HELPFUL(LAZYLEN(backup_clips))]/[SPAN_HELPFUL(max_clips)]")) - -/obj/item/hardpoint/holder/tank_turret/fire_projectile(mob/user, atom/A) - set waitfor = 0 - - var/turf/origin_turf = get_turf(src) - origin_turf = locate(origin_turf.x + origins[1], origin_turf.y + origins[2], origin_turf.z) +/obj/item/hardpoint/holder/tank_turret/get_origin_turf() + var/origin_turf = ..() origin_turf = get_step(get_step(origin_turf, owner.dir), owner.dir) //this should get us tile in front of tank to prevent grenade being stuck under us. - - var/obj/projectile/P = generate_bullet(user, origin_turf) - SEND_SIGNAL(P, COMSIG_BULLET_USER_EFFECTS, owner.seats[VEHICLE_GUNNER]) - P.fire_at(A, owner.seats[VEHICLE_GUNNER], src, get_dist(origin_turf, A) + 1, P.ammo.shell_speed) - ammo.current_rounds-- + return origin_turf diff --git a/code/modules/vehicles/hardpoints/primary/autocannon.dm b/code/modules/vehicles/hardpoints/primary/autocannon.dm index df9224011b..b6dc2cedc6 100644 --- a/code/modules/vehicles/hardpoints/primary/autocannon.dm +++ b/code/modules/vehicles/hardpoints/primary/autocannon.dm @@ -8,8 +8,6 @@ activation_sounds = list('sound/weapons/vehicles/autocannon_fire.ogg') health = 500 - cooldown = 7 - accuracy = 0.98 firing_arc = 60 origins = list(0, -3) @@ -23,3 +21,10 @@ "4" = list(32, 0), "8" = list(-32, 0) ) + + scatter = 1 + gun_firemode = GUN_FIREMODE_AUTOMATIC + gun_firemode_list = list( + GUN_FIREMODE_AUTOMATIC, + ) + fire_delay = 0.7 SECONDS diff --git a/code/modules/vehicles/hardpoints/primary/dual_cannon.dm b/code/modules/vehicles/hardpoints/primary/dual_cannon.dm index d33fc1d628..b76e6ad5cf 100644 --- a/code/modules/vehicles/hardpoints/primary/dual_cannon.dm +++ b/code/modules/vehicles/hardpoints/primary/dual_cannon.dm @@ -12,10 +12,7 @@ damage_multiplier = 0.2 health = 500 - cooldown = 7 - accuracy = 0.98 firing_arc = 60 - var/burst_amount = 2 origins = list(0, -2) @@ -35,27 +32,15 @@ "8" = list(14, 9) ) + scatter = 1 + gun_firemode = GUN_FIREMODE_AUTOMATIC + gun_firemode_list = list( + GUN_FIREMODE_AUTOMATIC, + ) + fire_delay = 0.3 SECONDS + /obj/item/hardpoint/primary/dualcannon/set_bullet_traits() ..() LAZYADD(traits_to_give, list( BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_iff) )) - -/obj/item/hardpoint/primary/dualcannon/fire(mob/user, atom/A) - if(ammo.current_rounds <= 0) - return - - next_use = world.time + cooldown * owner.misc_multipliers["cooldown"] - - for(var/bullets_fired = 1, bullets_fired <= burst_amount, bullets_fired++) - var/atom/T = A - if(!prob((accuracy * 100) / owner.misc_multipliers["accuracy"])) - T = get_step(get_turf(A), pick(cardinal)) - if(LAZYLEN(activation_sounds)) - playsound(get_turf(src), pick(activation_sounds), 60, 1) - fire_projectile(user, T) - if(ammo.current_rounds <= 0) - break - if(bullets_fired < burst_amount) //we need to sleep only if there are more bullets to shoot in the burst - sleep(3) - to_chat(user, SPAN_WARNING("[src] Ammo: [SPAN_HELPFUL(ammo ? ammo.current_rounds : 0)]/[SPAN_HELPFUL(ammo ? ammo.max_rounds : 0)] | Mags: [SPAN_HELPFUL(LAZYLEN(backup_clips))]/[SPAN_HELPFUL(max_clips)]")) diff --git a/code/modules/vehicles/hardpoints/primary/flamer.dm b/code/modules/vehicles/hardpoints/primary/flamer.dm index 929842df23..13beee9dd2 100644 --- a/code/modules/vehicles/hardpoints/primary/flamer.dm +++ b/code/modules/vehicles/hardpoints/primary/flamer.dm @@ -8,8 +8,6 @@ activation_sounds = list('sound/weapons/vehicles/flamethrower.ogg') health = 400 - cooldown = 20 - accuracy = 0.75 firing_arc = 90 origins = list(0, -3) @@ -26,36 +24,19 @@ use_muzzle_flash = FALSE + scatter = 5 + fire_delay = 2.0 SECONDS + /obj/item/hardpoint/primary/flamer/set_bullet_traits() ..() LAZYADD(traits_to_give, list( BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_iff) )) -/obj/item/hardpoint/primary/flamer/can_activate(mob/user, atom/A) - if(!..()) - return FALSE - - var/turf/origin_turf = get_turf(src) - origin_turf = locate(origin_turf.x + origins[1], origin_turf.y + origins[2], origin_turf.z) - if(origin_turf == get_turf(A)) - return FALSE - - return TRUE - -/obj/item/hardpoint/primary/flamer/fire_projectile(mob/user, atom/A) - set waitfor = 0 - - var/turf/origin_turf = get_turf(src) - origin_turf = locate(origin_turf.x + origins[1], origin_turf.y + origins[2], origin_turf.z) - - var/range = get_dist(origin_turf, A) + 1 - - var/obj/projectile/P = generate_bullet(user, origin_turf) - SEND_SIGNAL(P, COMSIG_BULLET_USER_EFFECTS, owner.seats[VEHICLE_GUNNER]) - P.fire_at(A, owner.seats[VEHICLE_GUNNER], src, range < P.ammo.max_range ? range : P.ammo.max_range, P.ammo.shell_speed) - - if(use_muzzle_flash) - muzzle_flash(Get_Angle(owner, A)) +/obj/item/hardpoint/primary/flamer/try_fire(target, user, params) + var/turf/origin_turf = get_origin_turf() + if(origin_turf == get_turf(target)) + to_chat(user, SPAN_WARNING("The target is too close.")) + return NONE - ammo.current_rounds-- + return ..() diff --git a/code/modules/vehicles/hardpoints/primary/ltb.dm b/code/modules/vehicles/hardpoints/primary/ltb.dm index 7c663dc27f..19b5c7e7b9 100644 --- a/code/modules/vehicles/hardpoints/primary/ltb.dm +++ b/code/modules/vehicles/hardpoints/primary/ltb.dm @@ -8,8 +8,6 @@ activation_sounds = list('sound/weapons/vehicles/cannon_fire1.ogg', 'sound/weapons/vehicles/cannon_fire2.ogg') health = 500 - cooldown = 200 - accuracy = 0.97 firing_arc = 60 origins = list(0, -3) @@ -30,3 +28,6 @@ "4" = list(89, -4), "8" = list(-89, -4) ) + + scatter = 2 + fire_delay = 20.0 SECONDS diff --git a/code/modules/vehicles/hardpoints/primary/minigun.dm b/code/modules/vehicles/hardpoints/primary/minigun.dm index c6158f1a3b..3acf37eec2 100644 --- a/code/modules/vehicles/hardpoints/primary/minigun.dm +++ b/code/modules/vehicles/hardpoints/primary/minigun.dm @@ -7,8 +7,6 @@ disp_icon_state = "ltaaap_minigun" health = 350 - cooldown = 8 - accuracy = 0.6 firing_arc = 90 origins = list(0, -3) @@ -30,46 +28,58 @@ "8" = list(-77, 0) ) - //changed minigun mechanic so instead of having lowered cooldown with each shot it now has increased burst size. - //While it's still spammy, user doesn't have to click as fast as possible anymore and has margin of 2 seconds before minigun will start slowing down - - var/chained_shots = 1 //how many quick succession shots we've fired, 1 by default - var/last_shot_time = 0 //when was last shot fired, after 3 seconds we stop barrel - var/list/chain_bursts = list(1, 1, 2, 2, 3, 3, 3, 4, 4, 4) //how many shots per click we do + scatter = 7 + gun_firemode = GUN_FIREMODE_AUTOMATIC + gun_firemode_list = list( + GUN_FIREMODE_AUTOMATIC, + ) + fire_delay = 0.8 SECONDS //base fire rate, modified by stage_delay_mult + activation_sounds = list('sound/weapons/gun_minigun.ogg') + /// Active firing time to reach max spin_stage. + var/spinup_time = 8 SECONDS + /// Grace period before losing spin_stage. + var/spindown_grace_time = 2 SECONDS + COOLDOWN_DECLARE(spindown_grace_cooldown) + /// Cooldown time to reach min spin_stage. + var/spindown_time = 3 SECONDS + /// Index of stage_rate. + var/spin_stage = 1 + /// Shots fired per fire_delay at a particular spin_stage. + var/list/stage_rate = list(1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 5) + /// Fire delay multiplier for current spin_stage. + var/stage_delay_mult = 1 + /// When it was last fired, related to world.time. + var/last_fired = 0 -/obj/item/hardpoint/primary/minigun/fire(mob/user, atom/A) +/obj/item/hardpoint/primary/minigun/set_fire_delay(value) + fire_delay = value + SEND_SIGNAL(src, COMSIG_GUN_AUTOFIREDELAY_MODIFIED, fire_delay * stage_delay_mult) - var/S = 'sound/weapons/vehicles/minigun_stop.ogg' - //check how much time since last shot. 2 seconds are grace period before minigun starts to lose rotation momentum - var/t = world.time - last_shot_time - 2 SECONDS - t = round(t / 10) - if(t > 0) - chained_shots = max(chained_shots - t * 3, 1) //we lose 3 chained_shots per second - else - if(chained_shots < 11) - chained_shots++ - S = 'sound/weapons/vehicles/minigun_loop.ogg' +/obj/item/hardpoint/primary/minigun/set_fire_cooldown() + calculate_stage_delay_mult() //needs to check grace_cooldown before refreshed + last_fired = world.time + COOLDOWN_START(src, spindown_grace_cooldown, spindown_grace_time) + COOLDOWN_START(src, fire_cooldown, fire_delay * stage_delay_mult) - if(chained_shots == 1) - playsound(get_turf(src), 'sound/weapons/vehicles/minigun_start.ogg', 40, 1) +/obj/item/hardpoint/primary/minigun/proc/calculate_stage_delay_mult() + var/stage_rate_len = stage_rate.len + var/delta_time = world.time - last_fired - next_use = world.time + cooldown * owner.misc_multipliers["cooldown"] - - //how many rounds we will shoot in this burst - if(chained_shots > LAZYLEN(chain_bursts)) //5 shots at maximum rotation - t = 5 + var/old_spin_stage = spin_stage + if(auto_firing || burst_firing) //spinup if continuing fire + var/delta_stage = delta_time * (stage_rate_len - 1) + spin_stage += delta_stage / spinup_time + else if(COOLDOWN_FINISHED(src, spindown_grace_cooldown)) //spindown if initiating fire after grace + var/delta_stage = (delta_time - spindown_grace_time) * (stage_rate_len - 1) + spin_stage -= delta_stage / spindown_time else - t = LAZYACCESS(chain_bursts, chained_shots) - for(var/i = 1; i <= t; i++) - var/atom/T = A - if(!prob((accuracy * 100) / owner.misc_multipliers["accuracy"])) - T = get_step(get_turf(T), pick(cardinal)) - fire_projectile(user, T) - if(ammo.current_rounds <= 0) - break - sleep(2) - to_chat(user, SPAN_WARNING("[src] Ammo: [SPAN_HELPFUL(ammo ? ammo.current_rounds : 0)]/[SPAN_HELPFUL(ammo ? ammo.max_rounds : 0)] | Mags: [SPAN_HELPFUL(LAZYLEN(backup_clips))]/[SPAN_HELPFUL(max_clips)]")) + return + spin_stage = Clamp(spin_stage, 1, stage_rate_len) + + var/old_stage_rate = stage_rate[Floor(old_spin_stage)] + var/new_stage_rate = stage_rate[Floor(spin_stage)] - playsound(get_turf(src), S, 40, 1) - last_shot_time = world.time + if(old_stage_rate != new_stage_rate) + stage_delay_mult = 1 / new_stage_rate + SEND_SIGNAL(src, COMSIG_GUN_AUTOFIREDELAY_MODIFIED, fire_delay * stage_delay_mult) diff --git a/code/modules/vehicles/hardpoints/secondary/cupola.dm b/code/modules/vehicles/hardpoints/secondary/cupola.dm index f1f8f23435..f259d6ea26 100644 --- a/code/modules/vehicles/hardpoints/secondary/cupola.dm +++ b/code/modules/vehicles/hardpoints/secondary/cupola.dm @@ -8,10 +8,7 @@ activation_sounds = list('sound/weapons/gun_smartgun1.ogg', 'sound/weapons/gun_smartgun2.ogg', 'sound/weapons/gun_smartgun3.ogg', 'sound/weapons/gun_smartgun4.ogg') health = 350 - cooldown = 15 - accuracy = 0.9 firing_arc = 120 - var/burst_amount = 3 origins = list(0, -2) @@ -25,27 +22,17 @@ "8" = list(-5, 7) ) + scatter = 3 + gun_firemode = GUN_FIREMODE_BURSTFIRE + gun_firemode_list = list( + GUN_FIREMODE_BURSTFIRE, + ) + burst_amount = 3 + burst_delay = 0.3 SECONDS + extra_delay = 0.6 SECONDS + /obj/item/hardpoint/secondary/m56cupola/set_bullet_traits() ..() LAZYADD(traits_to_give, list( BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_iff) )) - -/obj/item/hardpoint/secondary/m56cupola/fire(mob/user, atom/A) - if(ammo.current_rounds <= 0) - return - - next_use = world.time + cooldown * owner.misc_multipliers["cooldown"] - - for(var/bullets_fired = 1, bullets_fired <= burst_amount, bullets_fired++) - var/atom/T = A - if(!prob((accuracy * 100) / owner.misc_multipliers["accuracy"])) - T = get_step(get_turf(A), pick(cardinal)) - if(LAZYLEN(activation_sounds)) - playsound(get_turf(src), pick(activation_sounds), 60, 1) - fire_projectile(user, T) - if(ammo.current_rounds <= 0) - break - if(bullets_fired < burst_amount) //we need to sleep only if there are more bullets to shoot in the burst - sleep(3) - to_chat(user, SPAN_WARNING("[src] Ammo: [SPAN_HELPFUL(ammo ? ammo.current_rounds : 0)]/[SPAN_HELPFUL(ammo ? ammo.max_rounds : 0)] | Mags: [SPAN_HELPFUL(LAZYLEN(backup_clips))]/[SPAN_HELPFUL(max_clips)]")) diff --git a/code/modules/vehicles/hardpoints/secondary/flamer.dm b/code/modules/vehicles/hardpoints/secondary/flamer.dm index 10f7453d8c..5557cfb24e 100644 --- a/code/modules/vehicles/hardpoints/secondary/flamer.dm +++ b/code/modules/vehicles/hardpoints/secondary/flamer.dm @@ -8,8 +8,6 @@ activation_sounds = list('sound/weapons/vehicles/flamethrower.ogg') health = 300 - cooldown = 30 - accuracy = 0.68 firing_arc = 120 origins = list(0, -2) @@ -28,31 +26,20 @@ "8" = list(-3, 18) ) -/obj/item/hardpoint/secondary/small_flamer/fire_projectile(mob/user, atom/A) - set waitfor = 0 - - var/turf/origin_turf = get_turf(src) - origin_turf = locate(origin_turf.x + origins[1], origin_turf.y + origins[2], origin_turf.z) - var/list/turf/turfs = getline2(origin_turf, A) - var/distance = 0 - var/turf/prev_T - - for(var/turf/T in turfs) - if(T == loc) - prev_T = T - continue - if(!ammo.current_rounds) break - if(distance >= max_range) break - if(prev_T && LinkBlocked(prev_T, T)) - break - ammo.current_rounds-- - flame_turf(T, user) - distance++ - prev_T = T - sleep(1) - -/obj/item/hardpoint/secondary/small_flamer/proc/flame_turf(turf/T, mob/user) - if(!istype(T)) return - - if(!locate(/obj/flamer_fire) in T) // No stacking flames! - new/obj/flamer_fire(T, create_cause_data(initial(name), user)) + scatter = 6 + fire_delay = 3.0 SECONDS + +/obj/item/hardpoint/secondary/small_flamer/handle_fire(atom/target, mob/living/user, params) + var/turf/origin_turf = get_origin_turf() + + var/distance = get_dist(origin_turf, get_turf(target)) + var/fire_amount = min(ammo.current_rounds, distance+1, max_range) + ammo.current_rounds -= fire_amount + + new /obj/flamer_fire(origin_turf, create_cause_data(initial(name), user), null, fire_amount, null, FLAMESHAPE_LINE, target, CALLBACK(src, PROC_REF(display_ammo), user)) + + play_firing_sounds() + + COOLDOWN_START(src, fire_cooldown, fire_delay) + + return AUTOFIRE_CONTINUE diff --git a/code/modules/vehicles/hardpoints/secondary/frontal_cannon.dm b/code/modules/vehicles/hardpoints/secondary/frontal_cannon.dm index c7600059d9..f80a1e81af 100644 --- a/code/modules/vehicles/hardpoints/secondary/frontal_cannon.dm +++ b/code/modules/vehicles/hardpoints/secondary/frontal_cannon.dm @@ -11,10 +11,7 @@ damage_multiplier = 0.11 health = 350 - cooldown = 16 - accuracy = 0.8 firing_arc = 120 - var/burst_amount = 4 origins = list(0, -2) @@ -34,27 +31,15 @@ "8" = list(-62, -26) ) + scatter = 4 + gun_firemode = GUN_FIREMODE_AUTOMATIC + gun_firemode_list = list( + GUN_FIREMODE_AUTOMATIC, + ) + fire_delay = 0.3 SECONDS + /obj/item/hardpoint/secondary/frontalcannon/set_bullet_traits() ..() LAZYADD(traits_to_give, list( BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_iff) )) - -/obj/item/hardpoint/secondary/frontalcannon/fire(mob/user, atom/A) - if(ammo.current_rounds <= 0) - return - - next_use = world.time + cooldown * owner.misc_multipliers["cooldown"] - - for(var/bullets_fired = 1, bullets_fired <= burst_amount, bullets_fired++) - var/atom/T = A - if(!prob((accuracy * 100) / owner.misc_multipliers["accuracy"])) - T = get_step(get_turf(A), pick(cardinal)) - if(LAZYLEN(activation_sounds)) - playsound(get_turf(src), pick(activation_sounds), 60, 1) - fire_projectile(user, T) - if(ammo.current_rounds <= 0) - break - if(bullets_fired < burst_amount) //we need to sleep only if there are more bullets to shoot in the burst - sleep(3) - to_chat(user, SPAN_WARNING("[src] Ammo: [SPAN_HELPFUL(ammo ? ammo.current_rounds : 0)]/[SPAN_HELPFUL(ammo ? ammo.max_rounds : 0)] | Mags: [SPAN_HELPFUL(LAZYLEN(backup_clips))]/[SPAN_HELPFUL(max_clips)]")) diff --git a/code/modules/vehicles/hardpoints/secondary/grenade_launcher.dm b/code/modules/vehicles/hardpoints/secondary/grenade_launcher.dm index 8151a1ee50..efd151e93c 100644 --- a/code/modules/vehicles/hardpoints/secondary/grenade_launcher.dm +++ b/code/modules/vehicles/hardpoints/secondary/grenade_launcher.dm @@ -8,8 +8,6 @@ activation_sounds = list('sound/weapons/gun_m92_attachable.ogg') health = 500 - cooldown = 30 - accuracy = 0.4 firing_arc = 90 var/max_range = 7 @@ -27,40 +25,19 @@ "8" = list(-6, 17) ) + scatter = 10 + fire_delay = 3.0 SECONDS + /obj/item/hardpoint/secondary/grenade_launcher/set_bullet_traits() ..() LAZYADD(traits_to_give, list( BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_iff) )) -/obj/item/hardpoint/secondary/grenade_launcher/can_activate(mob/user, atom/A) - if(!..()) - return FALSE - - var/turf/origin_turf = get_turf(src) - origin_turf = locate(origin_turf.x + origins[1], origin_turf.y + origins[2], origin_turf.z) - if(get_dist(origin_turf, A) < 1) - to_chat(usr, SPAN_WARNING("The target is too close.")) - return FALSE - - return TRUE - -/obj/item/hardpoint/secondary/grenade_launcher/fire_projectile(mob/user, atom/A) - set waitfor = 0 - - var/turf/origin_turf = get_turf(src) - origin_turf = locate(origin_turf.x + origins[1], origin_turf.y + origins[2], origin_turf.z) - - //getting distance between supposed target and tank center. - var/range = get_dist(origin_turf, A) + 1 //otherwise nade falls one tile shorter - if(range > max_range) - range = max_range - - var/obj/projectile/P = generate_bullet(user, origin_turf) - SEND_SIGNAL(P, COMSIG_BULLET_USER_EFFECTS, owner.seats[VEHICLE_GUNNER]) - P.fire_at(A, owner.seats[VEHICLE_GUNNER], src, P.ammo.max_range, P.ammo.shell_speed) - - if(use_muzzle_flash) - muzzle_flash(Get_Angle(owner, A)) +/obj/item/hardpoint/secondary/grenade_launcher/try_fire(mob/user, atom/A) + var/turf/origin_turf = get_origin_turf() + if(origin_turf == get_turf(A)) + to_chat(user, SPAN_WARNING("The target is too close.")) + return NONE - ammo.current_rounds-- + return ..() diff --git a/code/modules/vehicles/hardpoints/secondary/tow.dm b/code/modules/vehicles/hardpoints/secondary/tow.dm index 4bdbc6f417..7c58f7970c 100644 --- a/code/modules/vehicles/hardpoints/secondary/tow.dm +++ b/code/modules/vehicles/hardpoints/secondary/tow.dm @@ -7,8 +7,6 @@ disp_icon_state = "towlauncher" health = 500 - cooldown = 150 - accuracy = 0.8 firing_arc = 60 origins = list(0, -2) @@ -29,3 +27,7 @@ "4" = list(5, -8), "8" = list(-5, 10) ) + + scatter = 4 + fire_delay = 15.0 SECONDS + diff --git a/code/modules/vehicles/hardpoints/special/firing_port_weapon.dm b/code/modules/vehicles/hardpoints/special/firing_port_weapon.dm index 9310556ee9..780c195f00 100644 --- a/code/modules/vehicles/hardpoints/special/firing_port_weapon.dm +++ b/code/modules/vehicles/hardpoints/special/firing_port_weapon.dm @@ -10,10 +10,7 @@ activation_sounds = list('sound/weapons/gun_smartgun1.ogg', 'sound/weapons/gun_smartgun2.ogg', 'sound/weapons/gun_smartgun3.ogg', 'sound/weapons/gun_smartgun4.ogg') health = 100 - cooldown = 10 - accuracy = 0.9 firing_arc = 120 - var/burst_amount = 3 //FPWs reload automatically var/reloading = FALSE var/reload_time = 10 SECONDS @@ -30,6 +27,13 @@ underlayer_north_muzzleflash = TRUE + scatter = 3 + gun_firemode = GUN_FIREMODE_AUTOMATIC + gun_firemode_list = list( + GUN_FIREMODE_AUTOMATIC, + ) + fire_delay = 0.3 SECONDS + /obj/item/hardpoint/special/firing_port_weapon/set_bullet_traits() ..() LAZYADD(traits_to_give, list( @@ -49,46 +53,6 @@ return data - -/obj/item/hardpoint/special/firing_port_weapon/can_activate(mob/user, atom/A) - if(!owner) - return FALSE - - var/seat = owner.get_mob_seat(user) - if(!seat) - return FALSE - - if(seat != allowed_seat) - to_chat(user, SPAN_WARNING("Only [allowed_seat] can use [name].")) - return FALSE - - //FPW stop working at 50% hull - if(owner.health < initial(owner.health) * 0.5) - to_chat(user, SPAN_WARNING("\The [owner]'s hull is too damaged!")) - return FALSE - - if(world.time < next_use) - if(cooldown >= 20) //filter out guns with high firerate to prevent message spam. - to_chat(user, SPAN_WARNING("You need to wait [SPAN_HELPFUL((next_use - world.time) / 10)] seconds before [name] can be used again.")) - return FALSE - - if(reloading) - to_chat(user, SPAN_NOTICE("\The [name] is reloading. Wait [SPAN_HELPFUL("[((reload_time_started + reload_time - world.time) / 10)]")] seconds.")) - return FALSE - - if(ammo && ammo.current_rounds <= 0) - if(reloading) - to_chat(user, SPAN_WARNING("\The [name] is out of ammo! You have to wait [(reload_time_started + reload_time - world.time) / 10] seconds before it reloads!")) - else - start_auto_reload(user) - return FALSE - - if(!in_firing_arc(A)) - to_chat(user, SPAN_WARNING("The target is not within your firing arc!")) - return FALSE - - return TRUE - /obj/item/hardpoint/special/firing_port_weapon/reload(mob/user) if(!ammo) ammo = new /obj/item/ammo_magazine/hardpoint/firing_port_weapon @@ -116,27 +80,32 @@ to_chat(user, SPAN_NOTICE("\The [name] reloads automatically.")) return FALSE +/obj/item/hardpoint/special/firing_port_weapon/try_fire(atom/target, mob/living/user, params) + if(!owner) + return NONE + + //FPW stop working at 50% hull + if(owner.health < initial(owner.health) * 0.5) + to_chat(user, SPAN_WARNING("\The [owner]'s hull is too damaged!")) + return NONE -/obj/item/hardpoint/special/firing_port_weapon/fire(mob/user, atom/A) if(user.get_active_hand()) to_chat(user, SPAN_WARNING("You need a free hand to use \the [name].")) - return + return NONE - if(ammo.current_rounds <= 0) - start_auto_reload(user) - return + if(reloading) + to_chat(user, SPAN_NOTICE("\The [name] is reloading. Wait [SPAN_HELPFUL("[((reload_time_started + reload_time - world.time) / 10)]")] seconds.")) + return NONE + + if(ammo && ammo.current_rounds <= 0) + if(reloading) + to_chat(user, SPAN_WARNING("\The [name] is out of ammo! You have to wait [(reload_time_started + reload_time - world.time) / 10] seconds before it reloads!")) + else + start_auto_reload(user) + return NONE + + if(!in_firing_arc(target)) + to_chat(user, SPAN_WARNING("The target is not within your firing arc!")) + return NONE - next_use = world.time + cooldown * owner.misc_multipliers["cooldown"] - - for(var/bullets_fired = 1, bullets_fired <= burst_amount, bullets_fired++) - var/atom/T = A - if(!prob((accuracy * 100) / owner.misc_multipliers["accuracy"])) - T = get_step(get_turf(A), pick(cardinal)) - if(LAZYLEN(activation_sounds)) - playsound(get_turf(src), pick(activation_sounds), 60, 1) - fire_projectile(user, T) - if(ammo.current_rounds <= 0) - break - if(bullets_fired < burst_amount) //we need to sleep only if there are more bullets to shoot in the burst - sleep(3) - to_chat(user, SPAN_WARNING("[src] Ammo: [SPAN_HELPFUL(ammo ? ammo.current_rounds : 0)]/[SPAN_HELPFUL(ammo ? ammo.max_rounds : 0)]")) + return handle_fire(target, user, params) diff --git a/code/modules/vehicles/hardpoints/support/artillery.dm b/code/modules/vehicles/hardpoints/support/artillery.dm index 441817107e..62f977ad32 100644 --- a/code/modules/vehicles/hardpoints/support/artillery.dm +++ b/code/modules/vehicles/hardpoints/support/artillery.dm @@ -14,7 +14,7 @@ var/view_buff = 10 //This way you can VV for more or less fun var/view_tile_offset = 7 -/obj/item/hardpoint/support/artillery_module/activate(mob/user, atom/A) +/obj/item/hardpoint/support/artillery_module/handle_fire(atom/target, mob/living/user, params) if(!user.client) return @@ -62,8 +62,9 @@ user.client.pixel_y = 0 is_active = FALSE -/obj/item/hardpoint/support/artillery_module/can_activate() +/obj/item/hardpoint/support/artillery_module/try_fire(target, user, params) if(health <= 0) to_chat(usr, SPAN_WARNING("\The [src] is broken!")) - return FALSE - return TRUE + return NONE + + return handle_fire(target, user, params) diff --git a/code/modules/vehicles/hardpoints/support/flare.dm b/code/modules/vehicles/hardpoints/support/flare.dm index 2cee40064f..186b6bbaf5 100644 --- a/code/modules/vehicles/hardpoints/support/flare.dm +++ b/code/modules/vehicles/hardpoints/support/flare.dm @@ -13,8 +13,6 @@ activatable = TRUE health = 500 - cooldown = 30 - accuracy = 0.7 firing_arc = 120 origins = list(0, -2) @@ -35,6 +33,9 @@ "8" = list(14, -6) ) + scatter = 6 + fire_delay = 3.0 SECONDS + /obj/item/hardpoint/support/flare_launcher/set_bullet_traits() ..() LAZYADD(traits_to_give, list( diff --git a/code/modules/vehicles/interior/interactable/seats.dm b/code/modules/vehicles/interior/interactable/seats.dm index 2984adda91..017b9e6b58 100644 --- a/code/modules/vehicles/interior/interactable/seats.dm +++ b/code/modules/vehicles/interior/interactable/seats.dm @@ -40,8 +40,8 @@ return if(QDELETED(buckled_mob)) - vehicle.set_seated_mob(seat, null) M.unset_interaction() + vehicle.set_seated_mob(seat, null) if(M.client) M.client.change_view(world_view_size, vehicle) M.client.pixel_x = 0 @@ -174,8 +174,8 @@ return if(QDELETED(buckled_mob)) - vehicle.set_seated_mob(seat, null) M.unset_interaction() + vehicle.set_seated_mob(seat, null) if(M.client) M.client.change_view(world_view_size, vehicle) M.client.pixel_x = 0 @@ -252,8 +252,8 @@ return if(QDELETED(buckled_mob)) - vehicle.set_seated_mob(seat, null) M.unset_interaction() + vehicle.set_seated_mob(seat, null) if(M.client) M.client.change_view(world_view_size, vehicle) M.client.pixel_x = 0 diff --git a/code/modules/vehicles/multitile/multitile.dm b/code/modules/vehicles/multitile/multitile.dm index 3eb03c5cb2..d86555d5b0 100644 --- a/code/modules/vehicles/multitile/multitile.dm +++ b/code/modules/vehicles/multitile/multitile.dm @@ -342,15 +342,24 @@ M.reset_view(src) give_action(M, /datum/action/human_action/vehicle_unbuckle) +/// Get crewmember of seat. /obj/vehicle/multitile/proc/get_seat_mob(seat) return seats[seat] +/// Get seat of crewmember. /obj/vehicle/multitile/proc/get_mob_seat(mob/M) for(var/seat in seats) if(seats[seat] == M) return seat return null +/// Get active hardpoint of crewmember. +/obj/vehicle/multitile/proc/get_mob_hp(mob/crew) + var/seat = get_mob_seat(crew) + if(seat) + return active_hp[seat] + return null + /obj/vehicle/multitile/proc/get_passengers() if(interior) return interior.get_passengers() diff --git a/code/modules/vehicles/multitile/multitile_hardpoints.dm b/code/modules/vehicles/multitile/multitile_hardpoints.dm index b8edba78da..949cc29b80 100644 --- a/code/modules/vehicles/multitile/multitile_hardpoints.dm +++ b/code/modules/vehicles/multitile/multitile_hardpoints.dm @@ -234,47 +234,3 @@ qdel(old) update_icon() - -//proc that fires non selected weaponry -/obj/vehicle/multitile/proc/shoot_other_weapon(mob/living/carbon/human/M, seat, atom/A) - - if(!istype(M)) - return - - var/list/usable_hps = get_hardpoints_with_ammo(seat) - for(var/obj/item/hardpoint/HP in usable_hps) - if(HP == active_hp[seat] || HP.slot != HDPT_PRIMARY && HP.slot != HDPT_SECONDARY) - usable_hps.Remove(HP) - - if(!LAZYLEN(usable_hps)) - to_chat(M, SPAN_WARNING("No other working weapons detected.")) - return - - for(var/obj/item/hardpoint/HP in usable_hps) - if(!HP.can_activate(M, A)) - return - HP.activate(M, A) - break - return - -//proc that activates support module if it can be activated and you meet requirements -/obj/vehicle/multitile/proc/activate_support_module(mob/living/carbon/human/M, seat, atom/A) - - if(!istype(M)) - return - - var/list/usable_hps = get_activatable_hardpoints(seat) - for(var/obj/item/hardpoint/HP in usable_hps) - if(HP.slot != HDPT_SUPPORT) - usable_hps.Remove(HP) - - if(!LAZYLEN(usable_hps)) - to_chat(M, SPAN_WARNING("No activatable support modules detected.")) - return - - for(var/obj/item/hardpoint/HP in usable_hps) - if(!HP.can_activate(M, A)) - return - HP.activate(M, A) - break - return diff --git a/code/modules/vehicles/multitile/multitile_interaction.dm b/code/modules/vehicles/multitile/multitile_interaction.dm index c4f1ed2d3d..466db72102 100644 --- a/code/modules/vehicles/multitile/multitile_interaction.dm +++ b/code/modules/vehicles/multitile/multitile_interaction.dm @@ -337,80 +337,61 @@ healthcheck() -/obj/vehicle/multitile/handle_click(mob/living/user, atom/A, list/mods) - - var/seat - for(var/vehicle_seat in seats) - if(seats[vehicle_seat] == user) - seat = vehicle_seat - break - - if(istype(A, /atom/movable/screen) || !seat) +/obj/vehicle/multitile/on_set_interaction(mob/user) + RegisterSignal(user, COMSIG_MOB_MOUSEDOWN, PROC_REF(crew_mousedown)) + RegisterSignal(user, COMSIG_MOB_MOUSEDRAG, PROC_REF(crew_mousedrag)) + RegisterSignal(user, COMSIG_MOB_MOUSEUP, PROC_REF(crew_mouseup)) + +/obj/vehicle/multitile/on_unset_interaction(mob/user) + UnregisterSignal(user, list(COMSIG_MOB_MOUSEUP, COMSIG_MOB_MOUSEDOWN, COMSIG_MOB_MOUSEDRAG)) + + var/obj/item/hardpoint/hardpoint = get_mob_hp(user) + if(hardpoint) + SEND_SIGNAL(hardpoint, COMSIG_GUN_INTERRUPT_FIRE) //abort fire when crew leaves + +/// Relays crew mouse release to active hardpoint. +/obj/vehicle/multitile/proc/crew_mouseup(datum/source, atom/object, turf/location, control, params) + SIGNAL_HANDLER + var/obj/item/hardpoint/hardpoint = get_mob_hp(source) + if(!hardpoint) return - if(seat == VEHICLE_DRIVER) - if(mods["shift"] && !mods["alt"]) - A.examine(user) - return - - if(mods["ctrl"] && !mods["alt"]) - activate_horn() - return - - var/obj/item/hardpoint/HP = active_hp[seat] - if(!HP) - to_chat(user, SPAN_WARNING("Please select an active hardpoint first.")) - return - - if(!HP.can_activate(user, A)) - return - - HP.activate(user, A) - - if(seat == VEHICLE_GUNNER) - if(mods["shift"] && !mods["middle"]) - if(vehicle_flags & VEHICLE_TOGGLE_SHIFT_CLICK_GUNNER) - shoot_other_weapon(user, seat, A) - else - A.examine(user) - return - if(mods["middle"] && !mods["shift"]) - if(!(vehicle_flags & VEHICLE_TOGGLE_SHIFT_CLICK_GUNNER)) - shoot_other_weapon(user, seat, A) - return - if(mods["alt"]) - toggle_gyrostabilizer() - return - if(mods["ctrl"]) - activate_support_module(user, seat, A) - return + hardpoint.stop_fire(source, object, location, control, params) - var/obj/item/hardpoint/HP = active_hp[seat] - if(!HP) - to_chat(user, SPAN_WARNING("Please select an active hardpoint first.")) - return +/// Relays crew mouse movement to active hardpoint. +/obj/vehicle/multitile/proc/crew_mousedrag(datum/source, atom/src_object, atom/over_object, turf/src_location, turf/over_location, src_control, over_control, params) + SIGNAL_HANDLER + var/obj/item/hardpoint/hardpoint = get_mob_hp(source) + if(!hardpoint) + return - if(!HP.can_activate(user, A)) - return + hardpoint.change_target(source, src_object, over_object, src_location, over_location, src_control, over_control, params) - HP.activate(user, A) +/// Checks for special control keybinds, else relays crew mouse press to active hardpoint. +/obj/vehicle/multitile/proc/crew_mousedown(datum/source, atom/object, turf/location, control, params) + SIGNAL_HANDLER - if(seat == VEHICLE_SUPPORT_GUNNER_ONE || seat == VEHICLE_SUPPORT_GUNNER_TWO) - if(mods["shift"]) - A.examine(user) - return - if(mods["middle"] || mods["alt"] || mods["ctrl"]) - return + var/list/modifiers = params2list(params) + if(modifiers[SHIFT_CLICK] || modifiers[MIDDLE_CLICK] || modifiers[RIGHT_CLICK]) //don't step on examine, point, etc + return - var/obj/item/hardpoint/HP = active_hp[seat] - if(!HP) - to_chat(user, SPAN_WARNING("Please select an active hardpoint first.")) - return + var/seat = get_mob_seat(source) + switch(seat) + if(VEHICLE_DRIVER) + if(modifiers[LEFT_CLICK] && modifiers[CTRL_CLICK]) + activate_horn() + return + if(VEHICLE_GUNNER) + if(modifiers[LEFT_CLICK] && modifiers[ALT_CLICK]) + toggle_gyrostabilizer() + return - if(!HP.can_activate(user, A)) - return + var/obj/item/hardpoint/hardpoint = get_mob_hp(source) + if(!hardpoint) + to_chat(source, SPAN_WARNING("Please select an active hardpoint first.")) + return - HP.activate(user, A) + hardpoint.start_fire(source, object, location, control, params) /obj/vehicle/multitile/proc/handle_player_entrance(mob/M) if(!M || M.client == null) return diff --git a/code/modules/vehicles/multitile/multitile_verbs.dm b/code/modules/vehicles/multitile/multitile_verbs.dm index c7dd29bbf0..3801cd2e17 100644 --- a/code/modules/vehicles/multitile/multitile_verbs.dm +++ b/code/modules/vehicles/multitile/multitile_verbs.dm @@ -30,6 +30,10 @@ if(!HP) return + var/obj/item/hardpoint/old_HP = V.active_hp[seat] + if(old_HP) + SEND_SIGNAL(old_HP, COMSIG_GUN_INTERRUPT_FIRE) //stop fire when switching away from HP + V.active_hp[seat] = HP var/msg = "You select \the [HP]." if(HP.ammo) @@ -66,6 +70,10 @@ if(!HP) return + var/obj/item/hardpoint/old_HP = V.active_hp[seat] + if(old_HP) + SEND_SIGNAL(old_HP, COMSIG_GUN_INTERRUPT_FIRE) //stop fire when switching away from HP + V.active_hp[seat] = HP var/msg = "You select \the [HP]." if(HP.ammo) @@ -225,10 +233,7 @@ 3. \"G: Toggle Turret Gyrostabilizer\" - toggles Turret Gyrostabilizer allowing it to keep current direction ignoring hull turning. (Exists only on vehicles with rotating turret, e.g. M34A2 Longstreet Light Tank)
\ Support Gunner verbs:
1. \"Reload Firing Port Weapon\" - initiates automated reloading process for M56 FPW. Requires a confirmation.
\ Driver shortcuts:
1. \"CTRL + Click\" - activates vehicle horn.
\ - Gunner shortcuts:
1. \"ALT + Click\" - toggles Turret Gyrostabilizer. (Exists only on vehicles with rotating turret, e.g. M34A2 Longstreet Light Tank)
\ - 2. \"CTRL + Click\" - activates not destroyed activatable support module.
\ - 3. \"Middle Mouse Button Click (MMB)\" - default shortcut to shoot currently not selected weapon if possible. Won't work if SHIFT + Click firing is toggled ON.
\ - 4. \"SHIFT + Click\" - examines target as usual, unless \"G: Toggle Middle/Shift Clicking\" verb was used to toggle SHIFT + Click firing ON. In this case, it will fire currently not selected weapon if possible.
" + Gunner shortcuts:
1. \"ALT + Click\" - toggles Turret Gyrostabilizer. (Exists only on vehicles with rotating turret, e.g. M34A2 Longstreet Light Tank)
" show_browser(user, dat, "Vehicle Controls Guide", "vehicle_help", "size=900x500") onclose(user, "vehicle_help") diff --git a/icons/mob/humans/onmob/feet.dmi b/icons/mob/humans/onmob/feet.dmi index 17b4073c74..e0611d5b7e 100644 Binary files a/icons/mob/humans/onmob/feet.dmi and b/icons/mob/humans/onmob/feet.dmi differ diff --git a/icons/mob/humans/onmob/hands.dmi b/icons/mob/humans/onmob/hands.dmi index d8fc38fff8..faaff370ac 100644 Binary files a/icons/mob/humans/onmob/hands.dmi and b/icons/mob/humans/onmob/hands.dmi differ diff --git a/icons/mob/humans/onmob/head_0.dmi b/icons/mob/humans/onmob/head_0.dmi index 51cad85586..b4475e9a8a 100644 Binary files a/icons/mob/humans/onmob/head_0.dmi and b/icons/mob/humans/onmob/head_0.dmi differ diff --git a/icons/mob/humans/onmob/head_1.dmi b/icons/mob/humans/onmob/head_1.dmi index df30d65712..febb96e218 100644 Binary files a/icons/mob/humans/onmob/head_1.dmi and b/icons/mob/humans/onmob/head_1.dmi differ diff --git a/icons/mob/humans/onmob/suit_0.dmi b/icons/mob/humans/onmob/suit_0.dmi index d0f816e2b7..27cc0e9726 100644 Binary files a/icons/mob/humans/onmob/suit_0.dmi and b/icons/mob/humans/onmob/suit_0.dmi differ diff --git a/icons/mob/humans/onmob/suit_1.dmi b/icons/mob/humans/onmob/suit_1.dmi index 636bbf8b9d..c05e998cb1 100644 Binary files a/icons/mob/humans/onmob/suit_1.dmi and b/icons/mob/humans/onmob/suit_1.dmi differ diff --git a/icons/mob/humans/onmob/ties.dmi b/icons/mob/humans/onmob/ties.dmi index 8f9940f639..844c0e98a1 100644 Binary files a/icons/mob/humans/onmob/ties.dmi and b/icons/mob/humans/onmob/ties.dmi differ diff --git a/icons/mob/humans/onmob/uniform_0.dmi b/icons/mob/humans/onmob/uniform_0.dmi index 40305b9413..2a60a33f69 100644 Binary files a/icons/mob/humans/onmob/uniform_0.dmi and b/icons/mob/humans/onmob/uniform_0.dmi differ diff --git a/icons/mob/humans/onmob/uniform_1.dmi b/icons/mob/humans/onmob/uniform_1.dmi index ae84c1a1b9..e04ae35178 100644 Binary files a/icons/mob/humans/onmob/uniform_1.dmi and b/icons/mob/humans/onmob/uniform_1.dmi differ diff --git a/icons/obj/items/clothing/cm_hats.dmi b/icons/obj/items/clothing/cm_hats.dmi index 098231450c..e68f26baae 100644 Binary files a/icons/obj/items/clothing/cm_hats.dmi and b/icons/obj/items/clothing/cm_hats.dmi differ diff --git a/icons/obj/items/clothing/cm_suits.dmi b/icons/obj/items/clothing/cm_suits.dmi index 162d66a2f0..85cd95d5e5 100644 Binary files a/icons/obj/items/clothing/cm_suits.dmi and b/icons/obj/items/clothing/cm_suits.dmi differ diff --git a/icons/obj/items/clothing/gloves.dmi b/icons/obj/items/clothing/gloves.dmi index f1fe9b3030..b290778d2d 100644 Binary files a/icons/obj/items/clothing/gloves.dmi and b/icons/obj/items/clothing/gloves.dmi differ diff --git a/icons/obj/items/clothing/hats.dmi b/icons/obj/items/clothing/hats.dmi index a6a0e6fb90..e94b17f2da 100644 Binary files a/icons/obj/items/clothing/hats.dmi and b/icons/obj/items/clothing/hats.dmi differ diff --git a/icons/obj/items/clothing/shoes.dmi b/icons/obj/items/clothing/shoes.dmi index 4d99b2ea4b..c4e01786e5 100644 Binary files a/icons/obj/items/clothing/shoes.dmi and b/icons/obj/items/clothing/shoes.dmi differ diff --git a/icons/obj/items/clothing/suits.dmi b/icons/obj/items/clothing/suits.dmi index 10fbfff30d..df3a572d9f 100644 Binary files a/icons/obj/items/clothing/suits.dmi and b/icons/obj/items/clothing/suits.dmi differ diff --git a/icons/obj/items/clothing/ties.dmi b/icons/obj/items/clothing/ties.dmi index b02ff9c253..f29883cc87 100644 Binary files a/icons/obj/items/clothing/ties.dmi and b/icons/obj/items/clothing/ties.dmi differ diff --git a/icons/obj/items/clothing/uniforms.dmi b/icons/obj/items/clothing/uniforms.dmi index f05eb3671f..347044d565 100644 Binary files a/icons/obj/items/clothing/uniforms.dmi and b/icons/obj/items/clothing/uniforms.dmi differ diff --git a/maps/map_files/golden_arrow/golden_arrow.dmm b/maps/map_files/golden_arrow/golden_arrow.dmm index 966fcc2cd9..fa093c5b8a 100644 --- a/maps/map_files/golden_arrow/golden_arrow.dmm +++ b/maps/map_files/golden_arrow/golden_arrow.dmm @@ -2080,9 +2080,7 @@ /area/golden_arrow/supply) "mc" = ( /obj/structure/surface/rack, -/obj/item/weapon/gun/rifle/m4ra/pve{ - pixel_y = 8 - }, +/obj/effect/decal/cleanable/dirt, /turf/open/floor/almayer{ icon_state = "plate" }, @@ -2838,17 +2836,6 @@ icon_state = "plate" }, /area/golden_arrow/hangar) -"qK" = ( -/obj/structure/closet/coffin/woodencrate, -/obj/item/ammo_magazine/rifle/m4ra/pve, -/obj/item/ammo_magazine/rifle/m4ra/pve, -/obj/item/ammo_magazine/rifle/m4ra/pve, -/obj/item/ammo_magazine/rifle/m4ra/pve, -/obj/item/ammo_magazine/rifle/m4ra/pve, -/turf/open/floor/almayer{ - icon_state = "plate" - }, -/area/golden_arrow/platoon_sergeant) "qQ" = ( /turf/open/floor/almayer{ icon_state = "cargo_arrow" @@ -6110,8 +6097,13 @@ /turf/open/floor/plating/plating_catwalk, /area/golden_arrow/cryo_cells) "Kl" = ( +/obj/structure/closet/coffin/woodencrate, +/obj/item/ammo_magazine/rifle/m4ra/pve, +/obj/item/ammo_magazine/rifle/m4ra/pve, +/obj/item/ammo_magazine/rifle/m4ra/pve, +/obj/item/ammo_magazine/rifle/m4ra/pve, +/obj/item/ammo_magazine/rifle/m4ra/pve, /obj/effect/decal/cleanable/dirt, -/obj/structure/machinery/photocopier, /turf/open/floor/almayer{ icon_state = "plate" }, @@ -8182,6 +8174,11 @@ /turf/open/floor/plating/plating_catwalk, /area/golden_arrow/squad_one) "UK" = ( +/obj/structure/surface/rack, +/obj/item/weapon/gun/rifle/m4ra/pve{ + pixel_y = 8 + }, +/obj/effect/decal/cleanable/dirt, /obj/structure/machinery/light{ dir = 4 }, @@ -8621,8 +8618,7 @@ "Xu" = ( /obj/structure/machinery/door/airlock/multi_tile/almayer/generic{ dir = 2; - name = "\improper Firing Range"; - req_one_access_txt = "8;12;40" + name = "\improper Firing Range" }, /turf/open/floor/almayer{ icon_state = "test_floor4" @@ -16760,7 +16756,7 @@ gI BK sC mc -qK +KQ bw KQ QS