From f9ed344491aa86b31bb867ba0e2b73682fe86eb3 Mon Sep 17 00:00:00 2001 From: Contrabang <91113370+Contrabang@users.noreply.github.com> Date: Tue, 20 Feb 2024 03:42:49 -0500 Subject: [PATCH 01/49] Jumping out of a dropship heading towards an LZ now throws your body at the ground, instead of deleting it. (#5584) # About the pull request Jumping out of a dropship heading towards an LZ now throws your body at the ground, instead of deleting it. This does around 500 damage, and permanently kills you. ![image](https://github.com/cmss13-devs/cmss13/assets/91113370/6bbd7926-1f8b-4078-a1ea-2dc583725ece) These people falling out of the sky, can land anywhere where there is no ceiling (or a glass one). Basically anywhere you can mortar, medevac, or laze something. This only works if the dropship is heading towards one of the LZs, if not, then they're deleted like normal. I also fixed the throwing, so it doesn't appear like you're just standing in space for 0.5 seconds before being disappeared. I also removed some unused arguments in `take_overall_damage` and `take_overall_armored_damage`. # Explain why it's good for the game It adds a bit of depth to the game, instead of your character being just outright deleted, their body actually ends up somewhere. # Testing Photographs and Procedure
Screenshots & Videos https://github.com/cmss13-devs/cmss13/assets/91113370/a6df19dc-7f9f-4c8d-8169-2db1497b6c54 (It does play sounds when you land, you just cant really hear them in this clip because they died.) ![image](https://github.com/cmss13-devs/cmss13/assets/91113370/b7f1bf66-fdce-4082-97c2-3fa841e168eb) ![image](https://github.com/cmss13-devs/cmss13/assets/91113370/f0819fea-4ac8-47b5-a012-f8ea12f08799) https://github.com/cmss13-devs/cmss13/assets/91113370/f727b8f7-1df3-4725-bb82-f16dc8294eea (Items do a little bounce when they land too)
# Changelog :cl: add: Falling out of a Dropship en route to an LZ, now lets your corpse plummet to the ground instead of being deleted. /:cl: --------- Co-authored-by: private-tristan <54422837+private-tristan@users.noreply.github.com> --- code/__DEFINES/traits.dm | 2 + code/controllers/subsystem/shuttles.dm | 16 +-- code/game/turfs/transit.dm | 114 ++++++++++++++++-- .../mob/living/carbon/human/human_damage.dm | 12 +- .../modules/mob/living/living_health_procs.dm | 2 +- code/modules/shuttle/shuttle.dm | 12 ++ code/modules/shuttle/shuttles/dropship.dm | 10 +- 7 files changed, 138 insertions(+), 30 deletions(-) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 2551872d95b5..039536491f9d 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -466,6 +466,8 @@ GLOBAL_LIST(trait_name_map) #define XENO_WEED_TRAIT "xeno_weed" /// traits associated with actively interacted machinery #define INTERACTION_TRAIT "interaction" +/// traits associated with interacting with a dropship +#define TRAIT_SOURCE_DROPSHIP_INTERACTION "dropship_interaction" /// traits bound by stunned status effects #define STUNNED_TRAIT "stunned" /// traits bound by knocked_down status effect diff --git a/code/controllers/subsystem/shuttles.dm b/code/controllers/subsystem/shuttles.dm index 3e59744cff31..db0d449b4dd7 100644 --- a/code/controllers/subsystem/shuttles.dm +++ b/code/controllers/subsystem/shuttles.dm @@ -164,9 +164,6 @@ SUBSYSTEM_DEF(shuttle) // First, determine the size of the needed zone // Because of shuttle rotation, the "width" of the shuttle is not // always x. - var/travel_dir = M.preferred_direction - // Remember, the direction is the direction we appear to be - // coming from var/dock_angle = dir2angle(M.preferred_direction) + dir2angle(M.port_direction) + 180 var/dock_dir = angle2dir(dock_angle) @@ -185,19 +182,10 @@ SUBSYSTEM_DEF(shuttle) /* to_chat(world, "The attempted transit dock will be [transit_width] width, and \) - [transit_height] in height. The travel dir is [travel_dir]." + [transit_height] in height. The travel dir is [M.preferred_direction]." */ - var/transit_path = /turf/open/space/transit - switch(travel_dir) - if(NORTH) - transit_path = /turf/open/space/transit/north - if(SOUTH) - transit_path = /turf/open/space/transit/south - if(EAST) - transit_path = /turf/open/space/transit/east - if(WEST) - transit_path = /turf/open/space/transit/west + var/transit_path = M.get_transit_path_type() var/datum/turf_reservation/proposal = SSmapping.RequestBlockReservation(transit_width, transit_height, null, /datum/turf_reservation/transit, transit_path) diff --git a/code/game/turfs/transit.dm b/code/game/turfs/transit.dm index bae6718cfd59..dd6a8d920f6f 100644 --- a/code/game/turfs/transit.dm +++ b/code/game/turfs/transit.dm @@ -11,19 +11,117 @@ if(isobserver(crosser) || crosser.anchored) return - if(!(isitem(crosser) || isliving(crosser))) + if(!isitem(crosser) && !isliving(crosser)) return - var/turf/open/floor/floor = old_loc - if(istype(floor)) - var/fling_dir = get_dir(floor, crosser.loc) - - var/turf/near_turf = get_step(crosser.loc, get_step(crosser.loc, fling_dir)) - var/turf/projected = get_ranged_target_turf(near_turf, fling_dir, 50) + if(!istype(old_loc, /turf/open/space)) + var/turf/projected = get_ranged_target_turf(crosser.loc, dir, 10) INVOKE_ASYNC(crosser, TYPE_PROC_REF(/atom/movable, throw_atom), projected, 50, SPEED_FAST, null, TRUE) - QDEL_IN(crosser, 0.5 SECONDS) + addtimer(CALLBACK(src, PROC_REF(handle_crosser), crosser), 0.5 SECONDS) + +/turf/open/space/transit/proc/handle_crosser(atom/movable/crosser) + if(QDELETED(crosser)) + return + qdel(crosser) + +/turf/open/space/transit/dropship + var/shuttle_tag + +/turf/open/space/transit/dropship/handle_crosser(atom/movable/crosser) + if(QDELETED(crosser)) + return + if(!shuttle_tag) + return ..() + + var/obj/docking_port/mobile/marine_dropship/dropship = SSshuttle.getShuttle(shuttle_tag) + if(!istype(dropship) || dropship.mode != SHUTTLE_CALL) + return ..() + + if(dropship.destination.id != DROPSHIP_LZ1 && dropship.destination.id != DROPSHIP_LZ2) + return ..() // we're not heading towards the LZs + + // you just jumped out of a dropship heading towards the LZ, have fun living on the way down! + var/list/ground_z_levels = SSmapping.levels_by_trait(ZTRAIT_GROUND) + if(!length(ground_z_levels)) + return ..() + + var/list/area/potential_areas = shuffle(SSmapping.areas_in_z["[ground_z_levels[1]]"]) + + for(var/area/maybe_this_area in potential_areas) + if(CEILING_IS_PROTECTED(maybe_this_area.ceiling, CEILING_PROTECTION_TIER_1)) // prevents out of bounds too + continue + if(istype(maybe_this_area, /area/space)) // make sure its not space, just in case + continue + + var/turf/open/possible_turf = null + var/list/area_turfs = get_area_turfs(maybe_this_area) + for(var/i in 1 to 10) + possible_turf = pick_n_take(area_turfs) + // we're looking for an open, non-dense, and non-space turf. + if(!istype(possible_turf) || is_blocked_turf(possible_turf) || istype(possible_turf, /turf/open/space)) + continue + + if(!istype(possible_turf) || is_blocked_turf(possible_turf) || istype(possible_turf, /turf/open/space)) + continue // couldnt find one in 10 loops, check another area + + // we found a good turf, lets drop em + INVOKE_ASYNC(src, PROC_REF(handle_drop), crosser, possible_turf, dropship.name) + return + + return ..() // they couldn't be dropped, just delete them + +/turf/open/space/transit/dropship/proc/handle_drop(atom/movable/crosser, turf/target, dropship_name) + if(QDELETED(crosser)) + return + ADD_TRAIT(crosser, TRAIT_IMMOBILIZED, TRAIT_SOURCE_DROPSHIP_INTERACTION) + + crosser.pixel_z = 360 + crosser.forceMove(target) + crosser.visible_message(SPAN_WARNING("[crosser] falls out of the sky."), SPAN_HIGHDANGER("As you fall out of the [dropship_name], you plummet towards the ground.")) + animate(crosser, time = 6, pixel_z = 0, flags = ANIMATION_PARALLEL) + + REMOVE_TRAIT(crosser, TRAIT_IMMOBILIZED, TRAIT_SOURCE_DROPSHIP_INTERACTION) + if(isitem(crosser)) + var/obj/item/item = crosser + item.explosion_throw(200) // give it a bit of a kick + return + + if(!isliving(crosser)) + return // don't know how you got here, but you shouldnt be here. + var/mob/living/fallen_mob = crosser + + playsound(target, "punch", rand(20, 70), TRUE) + playsound(target, "punch", rand(20, 70), TRUE) + playsound(target, "bone_break", rand(20, 70), TRUE) + playsound(target, "bone_break", rand(20, 70), TRUE) + + fallen_mob.KnockDown(10) // 10 seconds + fallen_mob.Stun(3) // 3 seconds + + + if(ishuman(fallen_mob)) + var/mob/living/carbon/human/human = fallen_mob + human.last_damage_data = create_cause_data("falling from [dropship_name]", human) + // I'd say falling from space is pretty much like getting hit by an explosion + human.take_overall_armored_damage(300, ARMOR_BOMB, limb_damage_chance = 100) + // but just in case, you will still take a ton of damage. + human.take_overall_damage(200, used_weapon = "falling", limb_damage_chance = 100) + if(human.stat != DEAD) + human.death(human.last_damage_data) + fallen_mob.status_flags |= PERMANENTLY_DEAD + return + // take a little bit more damage otherwise + fallen_mob.take_overall_damage(400, used_weapon = "falling", limb_damage_chance = 100) + +/turf/open/space/transit/dropship/alamo + shuttle_tag = DROPSHIP_ALAMO + dir = SOUTH + +/turf/open/space/transit/dropship/normandy + shuttle_tag = DROPSHIP_NORMANDY + dir = SOUTH /turf/open/space/transit/south dir = SOUTH diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 942c20482230..2a03b4f0abff 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -311,18 +311,18 @@ In most cases it makes more sense to use apply_damage() instead! And make sure t if(update) UpdateDamageIcon() // damage MANY limbs, in random order -/mob/living/carbon/human/take_overall_damage(brute, burn, sharp = 0, edge = 0, used_weapon = null) +/mob/living/carbon/human/take_overall_damage(brute, burn, used_weapon = null, limb_damage_chance = 80) if(status_flags & GODMODE) return //godmode - var/list/obj/limb/parts = get_damageable_limbs(80) + var/list/obj/limb/parts = get_damageable_limbs(limb_damage_chance) var/amount_of_parts = length(parts) for(var/obj/limb/L as anything in parts) - L.take_damage(brute / amount_of_parts, burn / amount_of_parts, sharp, edge, used_weapon) + L.take_damage(brute / amount_of_parts, burn / amount_of_parts, sharp = FALSE, edge = FALSE, used_weapon = used_weapon) updatehealth() UpdateDamageIcon() -// damage MANY LIMBS, in random order -/mob/living/carbon/human/proc/take_overall_armored_damage(damage, armour_type = ARMOR_MELEE, damage_type = BRUTE, limb_damage_chance = 80, penetration = 0, armour_break_pr_pen = 0, armour_break_flat = 0) +// damage MANY LIMBS, in random order, but consider armor +/mob/living/carbon/human/proc/take_overall_armored_damage(damage, armour_type = ARMOR_MELEE, damage_type = BRUTE, limb_damage_chance = 80, penetration = 0) if(status_flags & GODMODE) return //godmode var/list/obj/limb/parts = get_damageable_limbs(limb_damage_chance) @@ -330,6 +330,8 @@ In most cases it makes more sense to use apply_damage() instead! And make sure t var/armour_config = GLOB.marine_ranged if(armour_type == ARMOR_MELEE) armour_config = GLOB.marine_melee + if(armour_type == ARMOR_BOMB) + armour_config = GLOB.marine_explosive for(var/obj/limb/L as anything in parts) var/armor = getarmor(L, armour_type) var/modified_damage = armor_damage_reduction(armour_config, damage, armor, penetration, 0, 0) diff --git a/code/modules/mob/living/living_health_procs.dm b/code/modules/mob/living/living_health_procs.dm index f81baca85975..dd11eb8c7faf 100644 --- a/code/modules/mob/living/living_health_procs.dm +++ b/code/modules/mob/living/living_health_procs.dm @@ -496,7 +496,7 @@ src.updatehealth() // damage MANY limbs, in random order -/mob/living/proc/take_overall_damage(brute, burn, used_weapon = null) +/mob/living/proc/take_overall_damage(brute, burn, used_weapon = null, limb_damage_chance = 80) if(status_flags & GODMODE) return 0 //godmode apply_damage(brute, BRUTE) apply_damage(burn, BURN) diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 85fc38bf5f1b..6f02bf00e89d 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -1004,3 +1004,15 @@ to_chat(user, SPAN_WARNING("Shuttle already in transit.")) return FALSE return TRUE + +/obj/docking_port/mobile/proc/get_transit_path_type() + . = /turf/open/space/transit + switch(preferred_direction) + if(NORTH) + return /turf/open/space/transit/north + if(SOUTH) + return /turf/open/space/transit/south + if(EAST) + return /turf/open/space/transit/east + if(WEST) + return /turf/open/space/transit/west diff --git a/code/modules/shuttle/shuttles/dropship.dm b/code/modules/shuttle/shuttles/dropship.dm index f741df301bbb..ce9ba7c623bb 100644 --- a/code/modules/shuttle/shuttles/dropship.dm +++ b/code/modules/shuttle/shuttles/dropship.dm @@ -121,12 +121,18 @@ /obj/docking_port/mobile/marine_dropship/alamo name = "Alamo" id = DROPSHIP_ALAMO - preferred_direction = SOUTH + preferred_direction = SOUTH // If you are changing this, please update the dir of the path below as well + +/obj/docking_port/mobile/marine_dropship/alamo/get_transit_path_type() + return /turf/open/space/transit/dropship/alamo /obj/docking_port/mobile/marine_dropship/normandy name = "Normandy" id = DROPSHIP_NORMANDY - preferred_direction = SOUTH + preferred_direction = SOUTH // If you are changing this, please update the dir of the path below as well + +/obj/docking_port/mobile/marine_dropship/normandy/get_transit_path_type() + return /turf/open/space/transit/dropship/normandy /obj/docking_port/mobile/marine_dropship/check() . = ..() From 7b583990b8d0be5676616726e8099e1e7b59dbf9 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Tue, 20 Feb 2024 08:54:41 +0000 Subject: [PATCH 02/49] Automatic changelog for PR #5584 [ci skip] --- html/changelogs/AutoChangeLog-pr-5584.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5584.yml diff --git a/html/changelogs/AutoChangeLog-pr-5584.yml b/html/changelogs/AutoChangeLog-pr-5584.yml new file mode 100644 index 000000000000..7d2f7b8e156c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5584.yml @@ -0,0 +1,4 @@ +author: "Contrabang" +delete-after: True +changes: + - rscadd: "Falling out of a Dropship en route to an LZ, now lets your corpse plummet to the ground instead of being deleted." \ No newline at end of file From fe0134eee0c220b5b4a896689630fa9e14d92f55 Mon Sep 17 00:00:00 2001 From: Vile Beggar Date: Tue, 20 Feb 2024 20:09:43 +0100 Subject: [PATCH 03/49] Adds holo-targetting Vulture rounds (#5719) # About the pull request Adds holo-targetting rounds for the Vulture. Their characteristics are: - They deal SIGNIFICANTLY LESS damage, down from 400 to 60. - They cannot penetrate walls nor mobs. - They do not slow down mobs that are hit. - When they hit a target, they will "mark them for death", inflicting a holo stack value of 333. This means that they will take 33% more damage from all sources. A balloon alert will be shown to all nearby mobs indicating the mark, along with the holo-target aura itself. - **They have IFF.** ![image](https://github.com/cmss13-devs/cmss13/assets/17518895/69fc22e2-316d-43b5-87fd-08e2a5e9ba2b) The bullet increases the target's max capacity for holo stacks increases by 233 (to 333 in total). Any holo-targetting rounds that hit the mob will be able utilize this cap increase. The holo stacks will completely deplete after 35-ish seconds from the initial impact. It takes 5 seconds for the stack drain to begin, with every second passing after that removing 10 stacks. Other holo-targetting rounds will not modify the rate of stack depletion, keeping it at a firm 10 stacks per second. They will, however, stop the drain for 5 seconds. ![image](https://github.com/cmss13-devs/cmss13/assets/17518895/ad85d159-344b-407a-8158-23eafbf0824a) # Explain why it's good for the game This gun is really fun for the users, encouraging a buddy system that I feel the game desperately needs more of. I will admit that being hit with 400 damage, 50 AP from 2 screens away is not engaging for the opposing side, giving them no counterplay at all. With these bullets, the Vulture can take a supporting role for the marines instead of being an off-screen death cannon. 33% is a decent damage increase, but it requires multiple marines around the target to truly take advantage of it. Compared to the payoff that a regular Vulture round gives, the lack of IFF for those bullets is understandable. However, given that you NEED friendly troops to get any use out of these rounds, I've given the rounds IFF (with Zonespace's blessing) to broaden the areas and situations where it could get used. The ammo is still limited as ever, so the sniper team will have to pick their shots. Given how weak the rounds are in comparison to the standard ones, they should rarely be able to snipe off targets on their own (in a rare circumstance that they spot a half-crit xeno, an M4RA with a 4x scope can finish them off just the same). Is it weak? Too situational? Maybe, but the weapon is very interesting mechanically and I would just like to see it in play a bit more. The weapon is still ADMIN-ONLY. This will only give the admins a less blunt variant of the Vulture if they choose to spawn in it. # Testing Photographs and Procedure
Screenshots & Videos How being hit feels: https://github.com/cmss13-devs/cmss13/assets/17518895/a576629e-7961-4421-9932-d707e12477f1 General test: https://github.com/cmss13-devs/cmss13/assets/17518895/e2fbb117-1cf9-4945-b87a-d63e68f62a10 Bonus damage testing. The M41A does a base damage of 44. A holo-stack of 333 will result in roughly 44 * 1.33 = 59 damage. ![image](https://github.com/cmss13-devs/cmss13/assets/17518895/d230fc11-34e6-468e-89c3-394696c9b3e1)
# Changelog :cl: add: The Vulture can now be loaded with holo-targetting rounds which have severely hampered ballistic performance, but mark any target that is hit, increasing their damage taken by 33% for a brief period of time. /:cl: --- code/datums/ammo/bullet/rifle.dm | 9 +++-- code/datums/ammo/bullet/sniper.dm | 22 ++++++++++++ code/datums/ammo/bullet/special_ammo.dm | 10 ++++-- code/datums/ammo/shrapnel.dm | 13 +++++-- code/datums/components/bonus_damage_stack.dm | 34 +++++++++++++----- code/modules/cm_marines/equipment/guncases.dm | 24 +++++++++++++ code/modules/projectiles/guns/boltaction.dm | 6 ++++ code/modules/projectiles/magazines/rifles.dm | 8 +++++ .../weapons/guns/ammo_by_faction/uscm.dmi | Bin 24224 -> 23780 bytes sound/weapons/gun_vulture_mark.ogg | Bin 0 -> 15100 bytes 10 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 sound/weapons/gun_vulture_mark.ogg diff --git a/code/datums/ammo/bullet/rifle.dm b/code/datums/ammo/bullet/rifle.dm index ab30599eeb12..279a828ed786 100644 --- a/code/datums/ammo/bullet/rifle.dm +++ b/code/datums/ammo/bullet/rifle.dm @@ -21,11 +21,16 @@ /datum/ammo/bullet/rifle/holo_target name = "holo-targeting rifle bullet" damage = 30 + /// inflicts this many holo stacks per bullet hit var/holo_stacks = 10 + /// modifies the default cap limit of 100 by this amount + var/bonus_damage_cap_increase = 0 + /// multiplies the default drain of 5 holo stacks per second by this amount + var/stack_loss_multiplier = 1 -/datum/ammo/bullet/rifle/holo_target/on_hit_mob(mob/M, obj/projectile/P) +/datum/ammo/bullet/rifle/holo_target/on_hit_mob(mob/hit_mob, obj/projectile/bullet) . = ..() - M.AddComponent(/datum/component/bonus_damage_stack, holo_stacks, world.time) + hit_mob.AddComponent(/datum/component/bonus_damage_stack, holo_stacks, world.time, bonus_damage_cap_increase, stack_loss_multiplier) /datum/ammo/bullet/rifle/holo_target/hunting name = "holo-targeting hunting bullet" diff --git a/code/datums/ammo/bullet/sniper.dm b/code/datums/ammo/bullet/sniper.dm index a82f00631608..22371972e623 100644 --- a/code/datums/ammo/bullet/sniper.dm +++ b/code/datums/ammo/bullet/sniper.dm @@ -139,6 +139,28 @@ BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_penetrating/heavy) )) +/datum/ammo/bullet/sniper/anti_materiel/vulture/holo_target + name = "holo-targeting anti-materiel sniper bullet" + damage = 60 // it's a big bullet but its purpose is to support marines, not to kill enemies by itself + /// inflicts this many holo stacks per bullet hit + var/holo_stacks = 333 + /// modifies the default cap limit of 100 by this amount + var/bonus_damage_cap_increase = 233 + /// multiplies the default drain of 5 holo stacks per second by this amount + var/stack_loss_multiplier = 2 + +/datum/ammo/bullet/sniper/anti_materiel/vulture/holo_target/on_hit_mob(mob/hit_mob, obj/projectile/bullet) + hit_mob.AddComponent(/datum/component/bonus_damage_stack, holo_stacks, world.time, bonus_damage_cap_increase, stack_loss_multiplier) + playsound(hit_mob, 'sound/weapons/gun_vulture_mark.ogg', 40) + to_chat(hit_mob, isxeno(hit_mob) ? SPAN_XENOHIGHDANGER("It feels as if we were MARKED FOR DEATH!") : SPAN_HIGHDANGER("It feels as if you were MARKED FOR DEATH!")) + hit_mob.balloon_alert_to_viewers("marked for death!") + +// the effect should be limited to one target, with IFF to compensate how hard it will be to hit these shots +/datum/ammo/bullet/sniper/anti_materiel/vulture/holo_target/set_bullet_traits() + LAZYADD(traits_to_give, list( + BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_iff) + )) + /datum/ammo/bullet/sniper/elite name = "supersonic sniper bullet" diff --git a/code/datums/ammo/bullet/special_ammo.dm b/code/datums/ammo/bullet/special_ammo.dm index 3d53c6b0c0d0..97c1bf5735f9 100644 --- a/code/datums/ammo/bullet/special_ammo.dm +++ b/code/datums/ammo/bullet/special_ammo.dm @@ -46,12 +46,16 @@ /datum/ammo/bullet/smartgun/holo_target //Royal marines smartgun bullet has only diff between regular ammo is this one does holostacks name = "holo-targeting smartgun bullet" damage = 30 - ///Stuff for the HRP holotargetting stacks + /// inflicts this many holo stacks per bullet hit var/holo_stacks = 15 + /// modifies the default cap limit of 100 by this amount + var/bonus_damage_cap_increase = 0 + /// multiplies the default drain of 5 holo stacks per second by this amount + var/stack_loss_multiplier = 1 -/datum/ammo/bullet/smartgun/holo_target/on_hit_mob(mob/M, obj/projectile/P) +/datum/ammo/bullet/smartgun/holo_target/on_hit_mob(mob/hit_mob, obj/projectile/bullet) . = ..() - M.AddComponent(/datum/component/bonus_damage_stack, holo_stacks, world.time) + hit_mob.AddComponent(/datum/component/bonus_damage_stack, holo_stacks, world.time, bonus_damage_cap_increase, stack_loss_multiplier) /datum/ammo/bullet/smartgun/holo_target/ap name = "armor-piercing smartgun bullet" diff --git a/code/datums/ammo/shrapnel.dm b/code/datums/ammo/shrapnel.dm index e27caa4b277d..39b0813fad25 100644 --- a/code/datums/ammo/shrapnel.dm +++ b/code/datums/ammo/shrapnel.dm @@ -43,10 +43,17 @@ shrapnel_chance = 0 shell_speed = AMMO_SPEED_TIER_3//she fast af boi penetration = ARMOR_PENETRATION_TIER_5 - -/datum/ammo/bullet/shrapnel/hornet_rounds/on_hit_mob(mob/M, obj/projectile/P) + /// inflicts this many holo stacks per bullet hit + var/holo_stacks = 10 + /// modifies the default cap limit of 100 by this amount + var/bonus_damage_cap_increase = 0 + /// multiplies the default drain of 5 holo stacks per second by this amount + var/stack_loss_multiplier = 1 + +/datum/ammo/bullet/shrapnel/hornet_rounds/on_hit_mob(mob/hit_mob, obj/projectile/bullet) . = ..() - M.AddComponent(/datum/component/bonus_damage_stack, 10, world.time) + hit_mob.AddComponent(/datum/component/bonus_damage_stack, holo_stacks, world.time, bonus_damage_cap_increase, stack_loss_multiplier) + /datum/ammo/bullet/shrapnel/incendiary name = "flaming shrapnel" diff --git a/code/datums/components/bonus_damage_stack.dm b/code/datums/components/bonus_damage_stack.dm index faf4813541b8..78da5e036ce4 100644 --- a/code/datums/components/bonus_damage_stack.dm +++ b/code/datums/components/bonus_damage_stack.dm @@ -15,15 +15,21 @@ var/bonus_damage_cap = 100 /// Last world.time that the afflicted was hit by a holo-targeting round. var/last_stack + /// extra cap limit added by more powerful bullets + var/bonus_damage_cap_increase = 0 + /// multiplies the BONUS_DAMAGE_STACK_LOSS_PER_SECOND calculation, modifying how fast we lose holo stacks + var/stack_loss_multiplier = 1 -/datum/component/bonus_damage_stack/Initialize(bonus_damage_stacks, time) +/datum/component/bonus_damage_stack/Initialize(bonus_damage_stacks, time, bonus_damage_cap_increase, stack_loss_multiplier) . = ..() src.bonus_damage_stacks = bonus_damage_stacks + src.stack_loss_multiplier = stack_loss_multiplier + src.bonus_damage_cap = initial(bonus_damage_cap) + bonus_damage_cap_increase // this way it will never increase over the intended limit if(!time) time = world.time src.last_stack = time -/datum/component/bonus_damage_stack/InheritComponent(datum/component/bonus_damage_stack/BDS, i_am_original, bonus_damage_stacks, time) +/datum/component/bonus_damage_stack/InheritComponent(datum/component/bonus_damage_stack/BDS, i_am_original, bonus_damage_stacks, time, bonus_damage_cap_increase, stack_loss_multiplier) . = ..() if(!BDS) src.bonus_damage_stacks += bonus_damage_stacks @@ -32,22 +38,32 @@ src.bonus_damage_stacks += BDS.bonus_damage_stacks src.last_stack = BDS.last_stack - src.bonus_damage_stacks = min(src.bonus_damage_stacks, bonus_damage_cap) + // if a different type of holo targetting bullet hits a mob and has a bigger bonus cap, it will get applied. + if(src.bonus_damage_cap_increase < bonus_damage_cap_increase) + src.bonus_damage_cap_increase = bonus_damage_cap_increase + src.bonus_damage_cap = initial(bonus_damage_cap) + src.bonus_damage_cap_increase + + // however, if it has a worse stack_loss_multiplier, it will get applied instead. + // this way, if a weapon is meant to have a big bonus cap but holo stacks that rapidly deplete, it will not be messed up by a weapon that a low stack_loss_multiplier. + if(src.stack_loss_multiplier < stack_loss_multiplier) + src.stack_loss_multiplier = stack_loss_multiplier + + src.bonus_damage_stacks = min(src.bonus_damage_stacks, src.bonus_damage_cap) /datum/component/bonus_damage_stack/process(delta_time) if(last_stack + 5 SECONDS < world.time) - bonus_damage_stacks = bonus_damage_stacks - BONUS_DAMAGE_STACK_LOSS_PER_SECOND * delta_time + bonus_damage_stacks = bonus_damage_stacks - BONUS_DAMAGE_STACK_LOSS_PER_SECOND * stack_loss_multiplier * delta_time if(bonus_damage_stacks <= 0) qdel(src) var/color = COLOR_BONUS_DAMAGE - var/intensity = bonus_damage_stacks / (bonus_damage_cap * 2) - color += num2text(BONUS_DAMAGE_MAX_ALPHA * intensity, 2, 16) - + var/intensity = bonus_damage_stacks / (initial(bonus_damage_cap) * 2) + // if intensity is too high of a value, the hex code will become invalid + color += num2text(BONUS_DAMAGE_MAX_ALPHA * clamp(intensity, 0, 0.5), 1, 16) if(parent) var/atom/A = parent - A.add_filter("bonus_damage_stacks", 2, list("type" = "outline", "color" = color, "size" = 1)) + A.add_filter("bonus_damage_stacks", 2, list("type" = "outline", "color" = color, "size" = 1 + clamp(intensity, 0, 1))) /datum/component/bonus_damage_stack/RegisterWithParent() START_PROCESSING(SSdcs, src) @@ -67,7 +83,7 @@ SIGNAL_HANDLER L += "Bonus Damage Taken: [bonus_damage_stacks * 0.1]%" -/datum/component/bonus_damage_stack/proc/get_bonus_damage(mob/M, list/damage_data) // 10% damage bonus at most +/datum/component/bonus_damage_stack/proc/get_bonus_damage(mob/M, list/damage_data) // 10% damage bonus in most instances SIGNAL_HANDLER damage_data["bonus_damage"] = damage_data["damage"] * (min(bonus_damage_stacks, bonus_damage_cap) / 1000) diff --git a/code/modules/cm_marines/equipment/guncases.dm b/code/modules/cm_marines/equipment/guncases.dm index 0cf097cb9b4c..22999e97fda2 100644 --- a/code/modules/cm_marines/equipment/guncases.dm +++ b/code/modules/cm_marines/equipment/guncases.dm @@ -348,6 +348,30 @@ new /obj/item/device/vulture_spotter_scope/skillless(src, WEAKREF(rifle)) new /obj/item/tool/screwdriver(src) // Spotter scope needs a screwdriver to disassemble +/obj/item/storage/box/guncase/vulture/holo_target + name = "\improper M707 holo-targetting anti-materiel rifle case" + desc = "A gun case containing the M707 \"Vulture\" anti-materiel rifle and its requisite spotting tools. This variant is pre-loaded with IFF-CAPABLE holo-targeting rounds." + +/obj/item/storage/box/guncase/vulture/holo_target/fill_preset_inventory() + var/obj/item/weapon/gun/boltaction/vulture/holo_target/rifle = new(src) + new /obj/item/ammo_magazine/rifle/boltaction/vulture/holo_target(src) + new /obj/item/device/vulture_spotter_tripod(src) + new /obj/item/device/vulture_spotter_scope(src, WEAKREF(rifle)) + new /obj/item/tool/screwdriver(src) + new /obj/item/pamphlet/trait/vulture(src) + new /obj/item/pamphlet/trait/vulture(src) + +/obj/item/storage/box/guncase/vulture/holo_target/skillless + storage_slots = 5 + +/obj/item/storage/box/guncase/vulture/holo_target/skillless/fill_preset_inventory() + var/obj/item/weapon/gun/boltaction/vulture/holo_target/skillless/rifle = new(src) + new /obj/item/ammo_magazine/rifle/boltaction/vulture/holo_target(src) + new /obj/item/device/vulture_spotter_tripod(src) + new /obj/item/device/vulture_spotter_scope/skillless(src, WEAKREF(rifle)) + new /obj/item/tool/screwdriver(src) + + /obj/item/storage/box/guncase/xm51 name = "\improper XM51 breaching scattergun case" desc = "A gun case containing the XM51 Breaching Scattergun. Comes with two spare magazines, two spare shell boxes, an optional stock and a belt to holster the weapon." diff --git a/code/modules/projectiles/guns/boltaction.dm b/code/modules/projectiles/guns/boltaction.dm index a06131f98ce0..16ccb3438e23 100644 --- a/code/modules/projectiles/guns/boltaction.dm +++ b/code/modules/projectiles/guns/boltaction.dm @@ -279,3 +279,9 @@ /obj/item/weapon/gun/boltaction/vulture/skillless bypass_trait = TRUE + +/obj/item/weapon/gun/boltaction/vulture/holo_target + current_mag = /obj/item/ammo_magazine/rifle/boltaction/vulture/holo_target + +/obj/item/weapon/gun/boltaction/vulture/holo_target/skillless + bypass_trait = TRUE diff --git a/code/modules/projectiles/magazines/rifles.dm b/code/modules/projectiles/magazines/rifles.dm index bfc411a2ea63..d12f390ccd95 100644 --- a/code/modules/projectiles/magazines/rifles.dm +++ b/code/modules/projectiles/magazines/rifles.dm @@ -457,6 +457,14 @@ max_rounds = 4 gun_type = /obj/item/weapon/gun/boltaction/vulture w_class = SIZE_MEDIUM // maybe small? This shit's >4 inches long mind you + ammo_band_icon = "+vulture_band" + ammo_band_icon_empty = "+vulture_band_e" + +/obj/item/ammo_magazine/rifle/boltaction/vulture/holo_target + name = "\improper M707 \"Vulture\" holo-target magazine (20x102mm)" + desc = "A magazine for the M707 \"Vulture\" anti-matieriel rifle. Contains up to 4 massively oversized IFF-CAPABLE holo-targeting rounds, which excel at marking heavy targets to be attacked by allied ground forces. The logistical requirements for such capabilities heavily hinder the performance and stopping power of this round." + default_ammo = /datum/ammo/bullet/sniper/anti_materiel/vulture/holo_target + ammo_band_color = AMMO_BAND_COLOR_HOLOTARGETING //=ROYAL MARINES=\\ diff --git a/icons/obj/items/weapons/guns/ammo_by_faction/uscm.dmi b/icons/obj/items/weapons/guns/ammo_by_faction/uscm.dmi index e177d85dc726d268ecd3793a023b10a4e23da896..8b3b5e0f1c80acbc315725bcac45cdbf7694e246 100644 GIT binary patch literal 23780 zcmagF1yoe;8a;dlkWfNEq@>fJBn4sUkVX*6Q9-(q5{99aE=9UgLP`XrLqJNpyK@E! zVTgelzQga{`~Tnjt#7SwEtr8h=Y3B-v-k7tXTRY(S}J7M8Lk5WK&Gaus0RR$R`55Q zm=OF#dbs8;0N`i7)qnm*@wK~!o2|F53p@7ES`ypP}i&1rez<35?1GtQ>@ zFWDs*B|9f)JyFs}`f-}k1y=UY<>!dInNV}HY7=wb(Tj>%Cr7TxGu#>RzIOr6 zuIQI#b*8pqmTRem&#DmZ*GG1OVPvImAk(Ii^9kwgAu6(v`47=4&Xr8>zVIGWT#vkm z|JAnd^US-VC+yd6^kr)W^W&f)Hbmbj8F$Eo;>Zl$rrt~C2J9|egs!x$zTGB_E>=h% zCK77ho!xy2$f>TWlSf`qUfT@YonW|{OWj>K%c+m6IDcNk;Z%@%Te zq1JsnF?L`0iA}6H=57Mrny*$6iCMgAr@_W@l+)krYcJeOI}g!Y1xnH1Kh~Ao#_^Wo zAd0=731@eeA$B<~hdS>xkmm{1*S93oNkY$fiFlnjb_q@I;;H>@d5*^((;?@!L6`%u zyRt&YV`~@RvC7&%@<~ZO%AJ1gdGR%?1JcoJ{VO+Gjx5xC(JXu8cJ7ub$suQ7{7CDV zjj7phC1nbuS!u$l{-1y0S1Z17W60(0_%r;?74B-JwplEkBVXZurW&&me{`N4O`DuYTv>5(}o-JAf|3AiGi z<=`zkv7h)Pg$&rAv^>K++P~OT+8^kpgglZxcrBTpf;w=+{Whhl=@#oH`4!R}{`iB= zr0)w3Zg|JLN=nNcS_x)iTX@cXJ$1DXw|Y_s(MfN}Qa|rn6+Jmp+V=~{xJP1oJKb0N z$1@hf84-j){^7W#1(mxj_wN14EYCwHe9py}AMPg1S+rg^WW#^WSd|f8=sjdOv>_Nj zHA^~G(yyC+cl2RW9<+l?)H-r|zb8FqBK2ZqXVLdLBCZ0bV0gJPXYrhRIq+8Y4`yR% zSlKz{?+`{8Ta-y1burW}W6A{kNbwOrH9IBeZHZ$hqb6sbXP1ALI*Cniq*(pOcZA@FIZz?zWOzk5PtEH1L2CUDF zxt9j2?lkk9!m=uhzUh`30RR|5)D-3Py=VSp`1svIV=rMh-p@_$ez-|R`~;7f*N(FB z3p!H2nA`LoD>U*MUv+iCmp_Ir)#Ed7Yn~NP6i4v#1!Yg%h|OlDN;vNeByk`nneK-( zZk%*ky`S#qp0==(X^<-4T#fItlo6IH&zMdbv|q>?)zjA}kSAvAa3EdgVhy?ZN?GgR zllOKM?8$;HV+3=>>CB+iyy1-O_Hr9WDUyo=ZB_Q%Y59)|G22V404#kb^xT2>ZO4kE z=dzQ(a@vPsD|KQvP5}QX6c?rG7!?1?zqwECzQWjQa>z~jHxFg^UJc1=KA`H?7Wnuf zwyQh#raXHqpdebN8V!)Jg(Tyf83%sV)E1MgR7NN>rQMVddx3vMbD)$;LM2ZuR0P48 zSWd(6lXYU$h}l9EEeTYw-tgqSf@?i<_HIXC`i!TeP2Fy2PaIOphmo+i>T(EwY7y2R zTpcH9ki03cs?WGoeS=orIx{oWm-f?(&M!#-;Q2>D*zstS>1>#QgpKop#FjI8@`qV8 zGH=#(%_h32U)wS0`&lWh>iH{IUbfDP?QJ8y{P~E*lAUKG<_N4ud)7~%!wofdbadkc z)H~N$?f$)c4=+ru`;yt-<+6N(^bRiylwq!yJ2Kiv5Z#{&i{Vxci_iZ^n$2irWHB@` z5!I<&aC4M8N1)9mz%Jc=najqmP-S{yzE?~-dgv(&8;XbtQlHn7moI?f3<)7BDiR}Q zk)bLr?av<*k7!i0X652zIy<8l{L`kc+h~igH!?!Q5y6QKARUStX1V$Nw-&jdX4t{- z;%BwZoXSd?*RLDmQ|%Ni@S-MLChOmDn#roxsi-%x{Ka%hI|@Pu^ucNojWbiJ%pU1E>rd*)3P>`G=ngI z6Tx;-&&~-LhgMu-%y&~bUJiD5jJ!q!fTsig7dUkqHd=FwhUSb0^0q)J4Z<*Ot$l-U zub!xGS4n?9bQlxRu&n;}tt1kK8Xg-9IX(4mXIuyy$r5+Y)IWiG(22Rx)yH^3L<8V8 z^FxcUD%{{@0ey<+eLJM7i2$#fTSm}^VqqHFfd%cdF|0M^X*0XozoNvjLy$H7L}G> z13J3882!t`Mv}^*G9mw*s|Cw5*8BGv9UbrLe!0Y;%1t)<(|DSyF`~_6TTw8|7gecD zY#|{W%Tsarg@xS@Ejmfnr1xUWx75pXhK`0#egopSq+i8r>NG`7q-mq-ut&*Ex%KsJ z7Pca}=>C{#1Y+TGh4=n_{5KCaMD!N-`~&O`8F}@hj#PDGSG1*L?oLwbC$e1hMfg#ctHN)|p?t=H5f8*D|FZ{=h%AUioT^&Ge_u2s zuh~X`n8E%Kj}v^$jc02l0N-r_z!ze&IPfJ00%8K&KSM$YuYh3a3PNr&u!1l0_*aA4 z!k>DR5QqC+zkZzyI7QR^>8>(~7vB?&B*uE6Cfv3ZsIY{!|NcI+Jk~A!9G-m7qBDyA zgDPCU?OJjFd!j`TJe#h1pI1k=A)H^ybQ~pQMF;BksYFD_@*MV@UZ-U$uN6_Wqe)1_ z6X)Jk>Pzgs-I=eRPr!uGF+esnCGqdtZg&r61=!M>N33Wv#(hXEql;e%o8aM0cX^a? zdh`)Zmj)#Vbd24G^uaN90b33#Pc@~GdZPzp_$?~CwQ4qCZZubOyOf3Xu-q(Lv9uVM z=QvM_^4IzhAI~;EF_C%?W2gdK|H37RskvFI9dyS)LT3VedCyUPaI+ca?&ktPcoVt@gt-PR@ zX_=W^pPHIwslJpW2vjff^EZ-xjW+S(I%*JPJv}`Y=@PDZ)z#I1>x+))vEk9Uhb2Wo zKs<-?3x#>&Z1Sl(Uvd76oCC~5DKExF!rW|^GRw-@xQvgH`GmC0=YsU}i*M+kF|3zD zofjS#oip~7s|wZn>{GJ)x>XXo@mS^J(x6zy-5b8UMj5nhwNU7cg@4%*`_!$obtl zcj!}|*Y8k#v$kaCa_79s7~9y`C}T!bC12$)*zL?_W@c932rn=y{WiS=MuPkTPHFo@ z8J0LRWBfaun&-izM~`j`Z>M2TD2NG0IWXEMyU$u23yaR_ zvL?G$U*eay*uaMiBU!}9%;rqS@W<-O8AgbeO0%GzB_EnC7UkHYU$}L}5CN8(Yq`C; zB-R!DA4WD$FYgI3-$rCc zHGg+^a|IuJFfy2y93v+lyWda3-hx{r%W z+!Af(n9PLgIXE%l>S`*H#lkzyliidY zf^CT#@t%Ft0rs@~iBVx~;Haq`F;kcA^}+|xFf*wi^f)P-FV1)G-CG$|CT8+p_ftgfQ2YbH1nK1A|BK7IWY)o~FMonLBNf0gVkd?1`fu%+y zI<>r(0hW4uIQBbO{0V^P=UcH5K>K1@rPl2)G0^&_rmqXbgS%sZ=JT5ONUCy}eh9R3 zC7A}b;Lznj00NxNsr?KEbM4^*6UIb7liP7dnD)b18P<8PJu?Z5w&$j<2X)WS_Ye@^ zM+s~d$ArTQTzz!4X%5`Q8>g!^QX+os+B-ctxtuDHb4C*JRXBAqQ2@zsigYL{@B~T# zIPlv8&+3F1GFbfa0B`GY1~16~vOPo?xLruNx9n%V2MPdYYn%;7WX^Ylf!0(}`-C?> zP`+t+_MPVk44y2!p{rZ2OW`~a;L_|5jw#b;Ma{3`kQJGhnt%;!-ms{yZd`#j1T;Wcv>LA^fV1KeifLF}qaa{%Ulz^0P(*jnAQZW!R872a1+2@u-&| zasG_sVrQ4g6<3>6d;DYZ<;Kcu?~Rj6*X2LI52iT0R-uD)vC`E%Cz0Hl2%OKdz*DPc z+_KB%IeWj|ug}7RoeO5Ox*!YhACehlc?2-1z{DEJrXSUXJ^B^YC8N!nnNhmA{}&U#_UsFC&QBl{fiOp`&@0hW$>tU0*)?If`_0|dv- zmr?oWl;)SJVsdHyF(Sn@;MB~&;+1pW$wbPEMl}ySM%~eWbgu$2==lb*3hw z=ci1cF%3~a2yJ))v-cU;1e_7;p+G>#o-_oY^FN^Ygk8s4fprEA4UKI+3#su|WarK# z>)YLeQOk-b72(*&4S1+TXmH6W_luc# zNq9i#nzqgl!vtmk7)$)q2BwX=-%iCY{w6Bv<_VIOO^FkD=tbx2{(cgD$E0n`xpyfKX^0c5;Ur~4YDMPso+NNV0Wffrc z5c&v%@oOitv2!7b#3^nw8dXtu&wfvIZUL)^rNT;PK4~HyLlb-ePTIm#HDnFu=PiYSc?;CS7Dw1Y;y6K!a8EnT_$~0S3@p_y=H9lp z^uF&}Oj=0qhbH(*EIWL7XSRldKdB*bayXA?5P?RHOJI*iD-f4BnRAto{u0d>j$q(A zPi*oH#WjUJHDIt>e@Q~-lY`%7$GE9V; zM@vytlWb*WB`!7f>t{3nvvu%Ndi~uf*E+(f4<<~Um7<%sz)RW8^pXJy2m%Hng7A>vHCw~e0tjDBA!N1@A z9iggC*IJ8Zj8z>gfIP%-1ZO+-`lH7U)q%K_lyI;Bd@-S1(TS#uC*vdI0I{5i+|mDCR7bP z!^ZtP1&O*%XcNF|@f$d`%byeHb|~`&M{jR0>9#wMF*LpGa=RcargBh*k;QL6Fb*Xk z4>g@?u$SuGhzL-b3UTK~kCOVoJVWu!6~&l_b1rDDN~hukGf-We06yZZ5m|JJzMXJxn87 z*-cpi9Pm#)Lv`#TZ~DL0j-rsmwI^+OVtA}z;S+8v)SECR5OG!byksIl4pSz_v#^B_ z7DE08l(aPGG8OcBlaKO5eGeO=mA<1}s}l70fbNV^9^_aJVvrZ6UsO-;FEt!^?^O!C z+b&j7=DDh&#+cIj$-y765^{8Iv8<&mLf^Q4{fmvDymTb5(S;hZZae$XO|?p6q3yoP z3a?4egv2`{8U9u7zxl*fOH2QN#L)Rm=l}?~f5UzMZ>Gf5?_R$$#Ke3|Z7m%qcjxM~ zHLSws3k&hvE$xe40-$wejk&Rrhdgo4DGPPJAa(Q8=4D!BCjjVGzPcKIYbq69cKO;u zNk8+lh93(7UYK_hUJW3jABC48s+U2yM)b3{g5}64P5Yz&K(mOS@|w$#IDSP%`~Jsp zYkVNa%VXKwkoK6xv(5F?r3D3Ceas=|nVgRyA*t}k+Ll6dT)2U3oGJ%0R^;C1?4l%~ z!JA%G=g+!X{ss5cus-_og@-ixv&sAaYHof?&%RS*s!Ubj<>32m*Sx%<4?+|WDgB)o~D!O!^&pJ z%)BqvDLao*pHF6%G% zDHkX5C87+K0+1dF;>zo1)|MGaT$3=lh5gezR1f{iS?UzFvsb-~%u*?Fq5x3e>0U>LJ|%;VAKT1%v+K;>74V)+#L z2=S<1eV6%q*D~{+9c*3GWFGoreai_EdhV}{>e;VjR@)quY`sb6jx$IW?0qj}p{3;i zYtkECq@wwSrA)aniG=yvQEifmX%lL{u8h{vF~i0lb<5Lny?b|k0lm=_Eps*FVjM?< zn-5d-WZuz5Y=Ym~neRy@II}iBX<$UZb6#CXj9xJfSY+mpQt({Kk@0!5e45`XD{rVE zQLdYhM)7_>K%$1Hr)g*~DlL)$m1Qjtvj>^5NZ@U*;^jvyd85Q;RDjG$k3W&5ly9zl zi^C;tt-}Hg4`Vuw6A)>*Ji0wFo&?Wo;%>4~Y))#15n`VXriOK{ zmIcTpNl4Y8Evg1F{ZzV7UE*37lG$Q|ooKJlpD2GLmS)`2Tz1b6t?*Dbw4E)QS(?GK zfyu+eL&9|>B!}#!BC;V}>$#)Y7cQ&d->+?;kB2cmLmxjbum#=hGfUpO>a&9!P$gJoyMmS_6ha&_g?&B<~a z?39LfQ>b2MZa&e?8^#tC_^bXCE00`P2<+^CHRgm*WZ8JF1dZMSH5A>an{B~d2l*r7 z5wf`w(uB=gVP3FF9;fpR*g5QQHZ5ULG1wpJavxaA=eb;-#GUxvHOB${9NYg&+4fWfg(gJ-e?91V~-}=qkq|^lldTanGR@ki>f~5}(-=-m8MRf2A^z z?hy+ZXNAxz_hI%|t#?nJA1CG0=AvP>SQX~qxRG1~MDO6`PQ{J@pD~&Rvuzps8etos zJ4h@@AjV8^Jl>OWNexJ7esLB%<-uqsvfI*Qn;o)onFFBjX zyKSWizdPZ1!Ub-m8`{O&o5(gi2$>~P!k!^sSTThMk%Bc3l{ZgU+T$D#nz7yB{_ZXuw?ti=R&zb~`&%V;~msi=0cda>FNCb$<8p)$u8ZR71JvkL~aIXvS zW$y*t9{=u~dXC{y3XvfbR6X9EiK#eSU2=1J-)|-E_dpAbsMXe)*7rV7Vq#CQ420!=J&W z^PtjQ`=kc;1L^ibb}t*P`qNU=DbPjQa5#8v4D|83ojiJr#Q0;Q#L+{nA&TS=@W?2^ z&*Tx~2=mIm7Ej2z2^A>zb@-KF2JPz^%O>2j}lFs;*3pZhlhuw_twnTo8t#%!jeup*ovhl zH!)qWojJg7VR-m~_wlr$eEs$~CNMh`SCTJwfh?2oGm>T&*b8sp;xq9p8}##29ZQFT z;wvNaOh+Mw(pm>UDw^qi(Dr59jlDiOgH=kP71ypML4LBN*mg4u1$^6=y6p6oaw0oZ z9RG)&;Q6JcPoHA5$n85W4NP<>se>s_;K9g^g~LDAOmy!Tzj!U%s(;PL1g5v##@HQV zk87;)0R9@orFCgnI-yq`0VptgBcBcl7DY4@+@!*G@NnleRbRW3;9k#^cBj#vYKTHa zgL<@>t^pG8H;g~Tez6tLo?w2iN};8;{|^^Q=Ql833Ja0Lp3Q!%zDu50TnsOR?z82t zTv%9yvfW3C>`YhUtHd&UwCq%&Q?cRH_+|LA<_ny)rsSGOSWyI8UA7Pt5-0fU;jIJL z-N(y6y#isdW%*2I%Is8Gc3J80=;(u#h3VUw_=b&xL*Z*_si@gq z6Xc0u7c~JL~ zvP}pHIincq-JZLr>-Rl?4}v?Z*FI2w6!k^@R+ey>5~M3C?~K|YDk3^?tpH^`)k@>x zCAXZawd`Zc7uMFa%`<2rr+PO{St6X>WY^Zt&h@j$?Q#gi!J8LCC`H+<*>M?y3iFH;?sKr6yrwAmof28&CsdsU|tgBShqXGn-;x{M#^ zD+}rh=S#!)S(P^MN{%#`rtTDWaq@G*zDojge(kLU3n$=>eTi*CX$U#y0gVo48-aEu{<7^Z~WYAA4!Se=R^E{ zC*_D6s+Ub!SdiA*b7i6A6muSti!;c*Z#&J_9OjF(w$F;CdVkDTSf4n7#T*OpZIJcp z-wDH$@DC&f-8%Lgum)MYCCWgMa#NHScT8LM345?^_BsT~8KQ)(FCQORU;3}v-#+rU zY7ygkV2rRVk8OniIk}j*>?69|z-id8p}IP1VeGiYkp?`>9Q=24xu4hvG@K3UPynhU z={pXpA41mjy|R99bSfu&O593?(3P^N-m+e6okS9YF$6a;56x=^b#&!eX0nEP{?BYt zyx7w<&Z?SfdnyQ_>5t>pMe@UcQ(rk|5Frgxl~S<3X?j6m|5t5A-)l7h_L}Swss?+Nug9==G1cP`q*4I0<%@M+hMGiEBvw zPo*Xxs5JVKj0oFtwixPHTm{|bFhKnR%B++sXlikzUJ z?PG?dQu?eh0la*CDEG_Xe7RW1Ykz7W{->U9vamNDE{@u!RsQR}N%%9cm;rX9%W*?P zla0PQ?#}}u_7jb^;rdt^3fNvd+M})k_O+29psnuk)8)zTOqseN@i1mt{;50ld$Kda z3`hPqKk9T~M5K&=qiv7+wfy}v2Pd-JERtp4D@rMqYuFShr1kOm9pPiI-!3t#lw3hq zi^Qppopjn&{#<|4W>@*r0Vfd~Iw&O{sf=}D6Z53wmpCDPjXf-F0COa(F%N_tgDHPP zV&b(iqKR_ip`8>~&46xDIRJJsM0Nc9$#UN92C%_llPXD1LqpnEtD)JSFT&z{WviOE zv*Zl%P0I+gX7MsIg6CO@52(-jzk@=fURsQ@WxdOu&MX3i$N=l8_Lz3^5=7~say5iDwLESqqGZ2r*SEp{V7FIb^A+vyusbsiUI@ zMLXzgA9&&N#4ia{quQ@ntO5uiTD;~De|N(t=qS$z$t2%KmWTt76mT4)MM2%s(8&;Q z3>^yRC1LxSqV4yJYGw7+vYX_;9R<=@1I>&Zw77kEWIQH68ZaW_r28)}!OtXqxPm4T zx(go1W^Zc;eh^JG#P1AQ7{64~FD*ZYh{mswuqg!`gsa&&JCa0FyGq>A)6$A5ih3@_ zvX>IypQ{~a)YCI?oU;37f}ScRe1g_F;N>$SOR>X`&SLaFZu-L8kwG4Uik#Gcv`k%6 z@!f_CG{6(eGFhn@rk}^Wq=tMaS0$YkXq2O(4+KX$`j-TgK;M%y-4hiT=U`)FLmpEqhy?4s=IO^V%P#{;@TrvjzBg_-^zt3>piNP!NL`UtOzRMe56m3U~5@&%$3wq8g8%#*2B z$t+`O`A%SpDgor73JlV3MRq);kVvGs7V)H#Fa+Mp{VnpIH+02RYTPHDC(!%L)hp8@6EEcGRfgjdqwaHX-4bcS|7k0z!EK z@sy{&+UpaZM`_P1(jlo^w`o9aDu#e#>l!Ej?JrzJ@ldBml^T35>LDL*utT*#xs>MI z+{2DoK)hc-I6KIg8Q@XAy{p(=Dhqco>gJpX=fITV55b*@D!1IjT$oW@!EIO?2o z6%s`q$`}6r<}IJH?@(zX6k!PAOWgMg+FQ4tAXixONMToD!1DwaOBI$)`mH6ujEtZ5 ziTdwTL1fexYH$(OxV%rFNBI{=Z5hD-4_DvGBJb{QMp}6Uv7jP_nY+sy%tKqQn{!67r?6Fw8IzG$n2S|KNsa zFzBTESps|G_JW8bxh^PJPql#y>pee}&Wg64D8rU>ji9wj zHVC7%HROHmZ-B+v{WVB-vjkk8YW00iQy%qID%Ve2eRunZ&rObTe#HF&ovjwcQqBQ*Jb4Rp?2b^k}C6!cM} z1m_Gn%-p;%cNifjG|x8O}|0NJ;uQYPR^k33%1S`v&qy*m`ne5(qH z5dMlThj|BgT@z%#_tyh=sy`8nJG7>xbCm?UVvnY$BS?-H0)dr4FEpII@k*#Xw+yio z{*`h+(PWKRhk`nxye@U*t5_^L)LB&yM7BYiO`F>}WS{q#Xb7p<2+fIj(X2B3x5d(! zY@gl#M`H&&$tP!AqM?{q=%4K0aG;zScl>8I)P3L)&sSmCnd9SrJ9I4(B)eBPycFgT zV+R*b%@$B7`d1^oyQ<+Z;9`dWEJp-~cRRR~er!%;<@l()$EJL-BDATV9{Mm5m6&Pe z=Xkd(F&RGMTWTQSyP z?ZWCk2s$Q3=YdxV z=wT23@gGkg|4_>FugyIvp`gw1BD@+CFl#xc!6KzM-Q{Z{fg-XD03)C3fx@zzXzRKi z065I+`MD7lsZ2`JBO}MbAc6!O=Wu*GSn8pT+l)XZ(8e9-Z~^I*r7bqY?Au?}b~jOT zu6Iv={Vw%B>=$pZfBBK}DYN6ns~W06GbvdDLK{Psf*OAvrEE7!ecK*6uhf2~_-^No zxAO1`@RS1`XMd=HJoA`7u#lmp@npfbz6Ot`6gqSx?7~E`-hVwV5&~rT>yu=QzB1Q3 z5`-GMQ@aqHuIC#0E4{enpP>}OKp2;}7Bj=Op+*!yH;cG=Pn4gi$~gYj$rBJ?*<<+l zc!=0-939b9xBp;}v^NVX2Uf3j5FbrwtEv4QD|-3~4bn)6lycwEes{#l!EdEX!l+he zqXU1%ZSd|~^+&FN<=tVK%BQ@)pB1DsdNqrz4;azEkmuBa^pudWi7ZKvuU(?g7~>>Y zqn^RhH^TD1Uau^xee#^3Fx3C)Q|f2s=A{>lb91D^m7aePeDB^ZegrR~2bSnt)v3=R z{@7Ji+*a~>6zBm_32@^siIH8?m?AC|c((XJ=|x8iZ5&t~eNmKG1ZTrV(A<}3(?M_( z04jca9Fq;(LaIN^F>?KcY}Rc^Ngj@KGhJATe-&*YWivpz$bHKZBc~n8`Syqm895qz z%r4kQ%c{F2nq0lm6{9b~!b)-vU+)mt;Cw&$O`2R*tqC)}ET{wcPEU|0)vIeIpuh}L zh~8~JZ;nXU98Y$6?kri_3$zO~FJ5XT;lgs!L>!=iVCU$lWN%*_SL4uxCThUZc9koD zAmW^m#kF=aX|mYOdvmDlwe>fP@DYII;9%pAo8-&t>BkZ2U+;YKUU8h2Y znwZ=dX&048WTRTBtkLF|TjP_A(hEFY{IIHScAa`@E5(R*OdVy)+`{vMWG}2D&x4n3>N%VOq z-L%$}Ou`EzB2sG_K~IT&UOug=*ex1Z~FIuCKt zTxx~!1M>OnFIYIb2ekq|e{3%G67fS{1}0w{gh^FpwwFTrrS~4E3%$P1o#{#lyja|w zuLZm4kNFScIaKyX)CG*9Mi470)=Nc*yw6Fv7eg0eFA2B6W6jM)30ZU|w-Ga%@<;Wv zT-{EvV0#J6AUj6_*^rL|j~NZf?=SFr`OpPsVzjI?9!k}lwCJUp{x)@K zK-yQwbL-|4iuwXTQeQWaWzmxnSo%bBU-0!`%SN{~;#`BQxNkzOVDhRiFOQe5RdB0c zxp2UHvdsKlsYwK+c9T=R(s+4BFRHrO5aKeZ!N>S>r$S2p9v-PV%!EVm{Or8FkLEX@ z+pWR^l9=9s&hC;28jm*b00Hn*7l-Ladf=PyrT_fk)2im_yI^1S6*ZV@^@ar_GY?!%;KWjsIuU9N-=S9bug3HrN)9m=+W8e zpri(lkm$?iKTz_5bEUi%s757S4Cn70~Vd z1fc9;<43TiDK9Ut|M=*cJp-7$K7I&$gaqA_F1tszKmgdSk4iUH2j$jkLYg|f&G=a~kN?nysf z2TdnmzPzT)+jA3raaH=WE7$Sr$@mBVUe77_7X(?qY@r%Iwv$KcbF+A$qU)X0RJu>J zUyMqb2b(S&K&OGbKFVILm2hfef}4wnr`@X3r-`Q4Bm~KXr7r^KY_-#TntQ#Pq(@xV zQ;YwqtpG*`&8TY~L&A!9QZd9LPj+$gU>pOu{LFE?Q#CE*kGkFHvHRwEh6iOS3$FoKdyoQ^T&2Whd!h`q8urx#Tfr?@hv(@9&4mys{vsjyQjM#lRP zSv$LXKz2oi?)1deRM^;<{u@tE<6Hw+OzWB+OY8i#?k!^d&oUgcftZKjp`KLC6=ap1 z*gm!zZCTQJv>@pv$v;(pjmQHhlZ!rAa6XjMGk8}!^?YaUN2xf2%vq|LG)3z?cO{ zO&4WG(a^w78u%E{<5g8v+11tKwZuajB_<6s?6mDZj2iy_%raQZroSy-l!28sxVs6i zyd0XBhYPtXV1Hl#5xY6`j?e4ydoRYn`q!YC-i1%_$WB)1CG243i-LWf%Nyvoz-O{0 z`HTULvD@)%ct>ahG$SW5k@aJxwEbT%%XjW%dnF#*3{em%Mwip`W7gK-pxr;c9_>|y zv?n1O8szN&Bjn*Sj#Rlxvv?~QOsPpox{_S$X!1vmHixf|l43+;<+vquj@T&rc97AM zRegR%&gspWc{MJUbh#b`8--unO753Y-G)RA6+o^(lmoB>56e_KwXyrdpq-jsR&=lB zTx@1`Ml%oW6Dx3t+*|$9jKdfY{-ND@o*PmaAA08_?AY|HB!- zNhQ>|!mC%gV>2`NtOOl|8Q9Qy>abY$C%1j6O25rGfX){1mD$4y^s~ICvZ-@@B7&q) zX5CN$TS)N?;jEw&1n`nhf4uV?O#DW-?4z%$L@yd}T?+9+r2w7T7_TvCYy?H7knaC# zPjzsDeI}9{QBDDEi78j6brQy?hg?67IYE+Dd#)1n_Kl+Tn#z^OeN^$^UJa1WLevbz6;hu>mAe&LV@U1T%Gd3v?Eg{?guWp!cxkh`HPtB_H2{h)QrT*H4IE>B(Tfrg| z@n0T~kgqJHImqI(>604?q|R>2R6zC|eQ<@?iP!AY*`U*$?c}(OaK_8ifNf+wNQlmY zhvDJj5TK(oJy*ZYaQsf!g^xRDlP0M803972M?DUb4J|@DC;2ky>LCgg`_Re*+oSpAj8sIZ`nzL>xcLz#zqN@h(>G3M>H@$N=!gs+xyN= zLXC6@2=L8$5i+`!_4dG4RZC0R-CejdQ|vaN<6roA<*0oIkdkW9@!Fx|PWScK$tsuf zB-OJv>_8UB_gi=MHb4LpP2X?F^UAl(<0BAF$K1P@mNl=Qyk$k5-@9H~QlkGOCD5R} zyrR2Qa9xrZ{ljL`P$k=KZ94-jiw2O^%O#$S2gm(L_S*XzIhSbb&;;7}RhU86*yUaJ zCtz$3Z9BA$9kx&(ZGLkO4_NB5(Mo}2@yJ@#uF|Z!9QIT^&NJbqpZMS zQ{ouvUiK!z{B4s-BF88Sx(E9xZ-U=`G7KBz3l)`6k+(-SubV(QbF{DEOFm&M=dFts z76^RTS-oZSe7bvrC?Ip-hkqnp)2P?pLiButS6)G-X62%6l|L&T%~^^xUakx}PY!B{ z53V|p1FIb;p45bt?F!P<$_5S-7Jy1`M98X-F1BiWwBTgg`FA2;vJ71Ehr@JGM8x%e ze|)e*Vd;l29X3`VgXT~Nr>UC#b6cA1CJ>`Pxe~fbL2)WPoQv2RVrx16s9BJkizm>y zAH)Sl9g~&(Sb@JKQbiHky7(CTQYVX1`1KS0Em|5H5>aY!+RnCXlV^7#D+df>-`yzt zXb%6gcpLdN3$t9#GC_EXEH37A-!P>`p;PHaSb*&HtM8kIW zw?3-Ckx!wG=yn;rTaK(Ef)>N>N4NQ6`A(XsuYuMC?9j3w*maPzJ`oR#e)MJVdHO$> zp#pQq*U->NFg7uv7mbf&7nN;D%y_F2&uQ5Bn zUm}KKf+X>3#Z(YkE2A)7D|iRhK;i(FglI^b1K8XHm5h(9L0dgrV7t>zxnvlK_pe^P zic7c|EA)s%h})VI{0}3ld}XL;EtHd{Uj>DkcRPK{`4Y|?M2%b_H>F=v7QP1oIeF!= z$;=ETHT9@9KYuC**iNzez}sCyn4KA+*x6mz+D2KrDG2&4Dj*R~B)ocyeT3hTXwlne zGhSX^d2Oy^7HQKgg;(`RPU;fq8u(PdCcfaUAd^_{Z#0S002LH$E2=!$UuTq!52UbL zjH}059_p>&Bb7Dicioz&(y3}rF$w%Sc!hp{_l8C$n1?sZ-?^U6F3+>@?P)B1EiU_@ z9OKrq_3PK?)q^e;aS`g=<_gThJ_eOH2pSkUia+>UqXVijp&#)Nzn{zJ566T?2Gdu|WKk^u4a^14soLad02jCljf zv^-{Sx4-b-z58>n&K*ol49h#KY)2nFM!<6w@^X7w_{<^@kI}dMtKQt@UauEknnfJ) zF3i<-S+q3lK1WtGFRu#hV`rT@TQYr^#RcjHEN0=c&}&s5%M*;{AHOobCG3kT0)-aZ zah2KgEz~jwykLFAf(K$R-6I zl@hAr@0$TxbO@ZV70puWZ~nGVm76Q|N{iySi%>K2bPqAX??e)kB0GCfKeRTGr((^lEU?_(&Mx6go?pQSbKUKuDd#sn;3rbgP{@yof({a2?PE@+xyjVg4O7uz zE)8cxP;kTzUbfBd05kET51Ea^;b_Jf!_W->fuTDRUzy-P)S|Jl%eZ?ZYoP0;F{&(y zx>SHMYdTI7W=7EGA26fg!wLOdX+4-2!(y+yTal-Nls@)7pGVg>I_SVkngwE*i}v!$ zX_1FS0jpicA!Z1UBT-ksOK?-3;Y|4ctf20sTBXpn)RS zD4&4JjG)Ajz)#liB-zI_90i? zd}`Twqc=FFP{lFGqwMXd2wpsBzTM8=nRufd91^z`3yjCd)pOJjgDY&$S$p#*K~yh# zcxl6rNA5XBJKV(`LT4G^wgH;|;xxMpo+CPAP#2^ z^8aK=`wp_3(+WOdc+6045=q2}_<0o+X)nMC6QP0FOV_Y)K;?7$fN}bqix%AtT7Ha` z513>Tz>f0ap-S7wvq)#+(-Y(B3!0W)E`}w5 zB9xgahe#^l1}@)saS^{S-12*4K-u_h>SC5{p>ylSFL6~|GAr3ofVl3NH?G+ST!_}u zH^@RFFfuawZ8dU5X4^VLymavmA_wvz&Ir_Fo^<{Vwk0 z`eUcCunRWVU%l_vF18@OMKS_-_@9#=P35A4g``2{pati2gFp2{aI{n)C^nA$Kb2f} zR8viy4IoWG1f)wB#X^_R2`Ej9G?gZXk0J!5gQ0~g7CMT8Acl@gN1Bv`B1HruEr?X< zO+pJuAYq4Z&z`e;&i=9c=R5D6_vX#anYnX+_g*znMEb?xJg1CoIAoCq@@WWg-|YS@ zO9PzF(hfJK>*B8r^!@cOq%Yi5kkYj-4kALI`s3oPmDdC+ly7#A)1JhGxIK&AQC== z#)z3M3+S+dQctz9~yrU*9#qpy+o?|D+%Sd{K~2(TWtA)hPQ< z4=>FsINP->j-IH(s+nW_mK*_z3J6fa&P%V*StnHLe2lBc!9h@<(`^ChhQGv$n*gC& zF>PTc&3#=0m=a>#Zd8TcZ06`%JIdTf)T;|1R-neajIvkvC${9jpNt6$k)_^y(HT|C znkp3@w(vCR0-x}}@X=t&y^h`a0zPH-RAZUpw$QO74Zwf$A=JPP2r5n(5Qt7TXQ;hr zQj}X1SY7?5+;+a>aQvp&>gryvJm#k{OiKn9nAedRjx62hQx+>zmG6zjSi(oT0_lfv z=Tn|$!ADjLO?tI%qG+6UPS>*a_OJ-us^SGhsk^2;-)0>C%~;V8)Q(;p0Rv8IJlrD` zEfm{D>wS4`Be!QJuN?5f?VVXdbHG2kYy=bX{!$STcUYK~7CW}Mlp{|L4GnXZhtT(D zQL>VAWvtbmeCHO{DvDlPSmHF8Ncr|cR0fFW=d~ie6YG0Hn6V2V-&8vCv(1dHMVDo@N>)N9dz;f zgmN)EYco0y_#B*V0hMK7X!zRW*_AWDv5NJ!LScm0me(E*vvLMRbAAzbkx8(pgiVv9 z-(zu=ns*EH(g*HZ+QrzWNp+SQa?d5IZv3c$CAgC9O=13`B>t6Bbk?h4r=wL-*jpgx zP_ulOS2BGtWAlCK*(?e_Lfh7vb*AH}1b-b&TcB~&IG+Cl#7}u(hzj~QCsu|`m5`Xh z;Lp{cSl;w09tI@L>r0uE!RT7V-WVaZrcbxU_!acBAq>7Q#3jW^1{VGFv%7jUkc3|% zbRKj6t@Knm4o$a)Gc8JEw6-@+5B!>l&wKqE#n-f(wz0+&ts7y<-MdC^F@NE^ zwsoNmP+8sm-*jH*v6o49B-_R`-06ryYScrW$T-#xNB3Km4r-88mHNn^4Fx=vbF;4r zwhwHggpB-zhdf#8eG(sqMXnZ?TLvK*AB2k3<156fo&}|wjIg<&7s|H%*RRkQhF+z) z3iHs^y`S(9TL&Zg?p`W-A*WSJIR2V{VJ1*gPEO97+~ze`@FzqTF?hi_#N3>nkDvcP z2Qj8EQ$e*ze|WU*Xn0aM%HtHr9mVJWg#g{c{F(Y~4o`2?qjrA3fs~9!c$j|_a;+f?`m^( zJ;z%*ejYnrrQy@HRLed zZ)4;bZ>jkif*LegT>5aQ*Xz&f-Hq9==15o*C@NI|&i>`^#``P{s3c0S-W1f-ztA(A zRJ#=zRLqhmzBltRL*O z{ZfELMAp)du;dnY+WBweBWX0NXo2PoYz79n+ywOyHL@@8_vEpqd`<4L&(6yXy<_=pCqdW_JiY_s-n>8&xW@!HI^c49-nGVcgY+UhLcxlfMjDn86#VD5{|`!(Q_ zIh?+VtjsHcZ+jtcGUX9#u6$w}V83I6Qr^D$PE`nqDW3H`w3I`Qog41R2BxQr9pTW- zW|OT?k8~HB**=_)>6tESU5Fa8rU#TX*!aQ1QoK67X`>H)qQBs zvtj!*O%3+%QxygUUVuaATCjW^Uhz}?#h1IK`0E9NGQf0sV@ zka7v(rK)c%VBVp;XxKPVlLf%k2;IHlU^(qY=W&|y^%WbF?QyLH}de^AH?r5_+o-4XB+_0#6N7Zxn%u}~xisedLs z)W{U&O7;$;Jja}}py#QkVwdBI=8Sv_w6sD_7TZwAJQ*AKB)J9oMBztm_vod?&L0fP z$rK}#2WRe``ouY9mVVy(W18IRpMR%16@!pf28}I;Zp7E!Q|6~WiMw%_^Cu`)i6HED z0bPD}7ODs@)ubPXp1m1ptv=v)-XuIx(gq8o@dovz21%f8SVA`$Cnav46stka6X2!5>qBSP;J#yFem)`D2H zS2-MXH6}kZjgxpC{j)u;x160mSL0^QwVYYxiVA0Lu}pPIrFstK7_7?k)RvPG5p0`<-HLbHmXeZRmDo^X zowJq6iFXAz_Wda5r$t$z#-JXM&R9zgSd#F)OJ$nsDT7C=dxN@(U$7n>+64W>rF`3OU7%8c7|=% z#|f~hBs~vkwITbgB-hbw(tb$#t9vB!3Qvv+g9t`c-D`&?!o3ta;oI@r{6hN7Wf~np z8*OP#?+%|~?qcu=Stpnygx%*mr=`Iw$2qlLFSxtAcGgfbMXn#5AUXm1HXF3b%%p;R zZ1!vWwni@a7m>F*`?=7+d#m>nlR=QOo~_+nAw?w_6>>7mQ-wlC%m4o!zhFAvsLo>d&Z z%ExO!&%!aS?Uv77K}Fr7jy&p>3*TZf2e?)0UC>)xCl_~rQweT~ZTlpbth0YgZNFV$%FdA>Y_nI=JE0e>1s0f+!(X=JDD$dEWz=>m*OT z58@1yK8A~IZrk8?xu=#6GVp>-@fVB*bFTD1`Vf>$(Y0F21e=u zA&D%~zWd(UH+N~#Tpyc=I1#Jhq$hP01S5j?^wyS-2ny5O9KBS|R##q2Z1niO@AVQg zQ=ZnC*A|0p9EFodnV$0Gln@f`kq`>^nhyLw;q(a^jV4@?p3kizknV%cZ{3LydN<|e z$)CB{PLbdMn$0>k24c+$-4+^$ojio=v>;M?2SYBX1tfKpFK}RKJnuiIKCb7m3T9Os zEGJ`4#Rk)}HActA`gTT>-?51S<2)lIu>PkVk>jwDYd)c;O*#CzL&eI!c1p7sbJtLTwGAGkj_^r1viCka-3q%EMxkw;v&P4|c*-Cj5FG=3< z=rUvU;=bj5z9aiP5=MWY*rM9;9v!m3(~Y76YK&t3=3$Bgle6BQs8=eJB6eROUM-Yo zZf9r9Nrx%IO(`*e_<~)aD-rR)L`m-I_vZDHu1ZH#yh&;K{AItJLtfqf|I9z+hS;}y zm5{k2b@{>q{x=kdD$F-AFrd!1Q-rMYmlMn`9IkwO?m7GQ#m_pg>1>2eh0`~71h~{H zqIHjwvJ}&GVr>ulEcNyFCGV+gf19r%2$^S6+ZWuJt&ST<<45OJJjeOoVXSd*Xgr0l zxrsOx5=eLlBA`BZL4xwCApSwxuD;rZQ<%*|#ak3z2Mk}cQldcgT=PCebATXyW0#YS zE|*Zuu-ow&Gs3@Otsbi>t9F{@tkM|s@jKZf5ou|h<2aN1Cv#ICA z_A84@uW1eJV6`R`Z$A{ZaN^eBl6rU5BN|$IHtB8v?J(;J`$jKzQJM4Yi>+=o59V6S zQ08ntWwDDQ^70lhemK=yDpB2;v$eoPXJlj~8+E6J85<{RbyEQ}i_BRy>nhDDHcq zNNRCcQPPT?V=hHm)cO@+dg6!C$s#1xzk6M{t}?X7?~-SaDsPta!-unVukHzZ-Qg+= z-11LiSTD{}-739oVd|%!Y0hp9w;R5!7cR)=NPvQ&)><)HTfU+5n9KC;Bte5rqks+} zJ;iuSywIeKAS?IAa}^XpR3{+sRR;d?`l&bqiO4QTIFKAo*t17O@{1;CPyR~ zCxA_xD#(6alet?6bF4PQ= zGPzD?W!XU-Uc260{M{yi5|tu0oG*ws^6DBO1x_4J%Qkn#N}&iJdPCF)iHAIECSkLc zQ$L6Ec?|k;PK$4w>xGUZ*ENj^N`)tF=gZ9SAGOSTO(~wNr=i3|NN zbBh_-i&hgAQ@EJhJ`*z8LSk8XAMyUEG={WKUIWa99zeEnhKxc6c{8Qw!_#6`7twApmXX8-LF?L3O5D)I`8 zaZ?ZxE^Re`d(qb^U`w34L4H!f3j(aOE)wHWvO+1OxH9Ym^SYy^{VkUY)R3OU_~-BK zp`Zi333IsmfRH}KVPTT@UQF8Mi^C(60u_vW1t3_46g~U>$C2@nDD}Ak5XyX|$F>zhKTR8O1b7n(lCvPLE6`fT>+C z0qt)O6>Qjg4B6oQ4YxUHT8i8DfLR*pK}CpLZDz-*nwVkZ7{Ack8|NQozA-yXOc-G; zgk`yVJ2IP_7y_<9h+-qaoK!)rsTg78OO6j6;x#z0x?&xs+Y}4WGlNqAq*A_TI-Vt)3 zc);|Y_B~~5|D*J{>9#dqCCduDzJ9;5hzioUC%75Z?S zmp=$;fG{_|S@(Of5j(kO3;!9k1wVKK@^e{$$_Au@iWjAmse999qmfi1Ph|ehxBP$Q zTK@a6z7xN_k58rlObgYEwAVn;^O0FbT%4BUqS4HCHSn=s=ZNbtP`oYt-xD%L&tqMl zgp-M*1>as>avb?|7gZ}RF881ChHu6QMhOkcbl#QYnylfY2rA zJX0LVojc1eU==p%e@RR6;tW8);-_XcQ0-MG_5B-xqrBDweM;+-zOkZJnI}!24TLg8J);yELJFd;GFJ#Y0pQ>8@1wGT*X& zIf%u3G#U4xJY?UrGxE;R(d;SW+(kNnW|>H|uRhPnc8veB?UG$C`uoh#tKsR#&zG}K zcH~Zq$F@;j&ujK~>R|ob4zH3Ox8^l0Lq5&ao$eS_#I1Zk*pWj{zGO>Vnm;9tv9pHD z{P;XzeveHE)9Ue}>hmMJ$#OE~)^CGmIn1A*Zuv9cZnH>xj!(80nr3eJHZp=EB8sT(|TC4cZ{5z+^RN^@ofzXoW z!-`wt-L6RvdFr-F7pgS2llPOfnF({2rDLUgLR|Z9S=0ANW%U<#Xi{k!GKq|<*^^$q zg{gd6uEO3K6w5ol#M;pkOYp*Eq#=x3!CwAC zBf}Lwj?=ksMNgYpZ$W+&M&-#=1rm15aNccjZp1H8Y3r^N3vu!KdKx7XKTFPR5HWG# z{eE`OwDf?1N~f5!Fs5#2pKNl8v+!AcY(v&&V8}Ar7 zr&=x99r)(0G8&>eNVd(%t8rEAm~&j|3kB(X6ydnC;qVxX5v{sa=J43d*jmd$^oN4` zal>)dzp`pb>&53ji8piyRt&_|8GQ|q&)^bT95*k07cOqv^@@MG!l-zW`<(oG$o)>5PSRm@qzN|y<4p- zLHZO<{-<$=2s33BvS)~i1G9@y%)UC|`Zw5YEB!vol1e8^O69-4Lnp)eBu3VE6L{#{V^JALLNFbw(|iX zz53~@A@8GmwYUGkXv;-pv=v&AbqY>W} z6-lhAo^*u96v@1F;|e`T7oR_|lB_ZF^0bndk=b2cjBVff)|b$^wJNvU_hv;j{=8oc6+C{c}Co}w-=aHjD+Hwo_*Qf5RTkEUrXF;4~oSE#I1 z4ns_--VLyg0r1J2ym=)`-Uk%&6lFmK23(f%VB_duZ9+-r2R#T9uy+6cOonf=Yen3d zA*$r1KY24YBm=p_VK1l^Fe028ggm$o_@RhWP4j-^iz|>=C;jbxDzO$R_(brgQ)rz_ zESAy8xlm-)pK7!zYUdCfD1oXTOaGcy!r<&V-NEq0b!BDse6W0=vKF9UY4g?_6D*6e zb-icRSIrI-VO_FpOSl?j4%4_*Xb4N7ZDhE|SzpcxW8!+9NYyMK=77{|3q8{-% z`~`<9%|-EcLY4XV>36fW<9tzmt4Ut&SjyfMs7W=jJU<5U{0TpLRVP-EnR#`|pYk41 zytTSxk8EM{dk|7x$)ojkK$C`-#D9-I`;V$#6nqV%yfl9&@#_eIXH{O6sY z?Zc0SR7+||KM{9l+NKfkwz9wlQOOos7g$r`2saz`P(J#VZ zY4=St%M9yuvfp9;lXlzbl@+D&zYbG|voFA@6^kUu2+jFp~mnxDp)TvL9iP+dVY%9Es zdkAxl^&7sS1a0B=J3%a(8ujw$>*2X9rsRn52>+9B)b@6D){~62$6c>stK=syQ&aJ2 zEM>rsUEGCZ-UwgodH2}Lh!nVa=QiV5z7ZX*rHipK=Q9yi9=VYrviEYtOAZcurNQ=6 z0bN~PsbgNF0R7shiTXuIolCFHF5cj;Q~a_~Ij|2 zEcgI!dHJKG*&LWBYG1e#gY~(@?z@UVc>i0;&+pNT^I4x&j&9WXVe7H|jHZPLt-21(4|%#Kj+CK$JxG^(+2Jwv=f(rZ+k&u;mO4? z+YGGaxrxd4@k7|_bDw7~Uy=rE_DC=*N-D(=rzjj2rM_Bvq870sDKOf(5pJLReqk@5 zuG&O+|DUgnT9L(mI`Wod1`X5)MQf5}&X`z8^vv!aspQSh6b0+GbH2H4NtxPc==nNI z7eb~0P9FhNlP^z_5N3y5OOqG0ASl;Sig0f5qLh2Bz#C^$l+z-?Z8?z}cZ$aGj}P!V5^5upjCRKRTk ztBL%xfw<#Asj?a}YNC^5cVk#!IQ>rvHqPle{Qdxs!}GISG%V!Jgxo#>)Wa4VS)Vh; zr-QqKECu7x>F-S;E9E@49eA5r@-F7`wGdlYK_i|hKG zk4}@3A4}bD(WumKwi%E?#oRnVaZn9b~@KwCo7_G%hxlM?ylvZI((d)|yM3VHr3=o9vy? z&C|L8@4|2A{P70uNpo`STl*COmG$x-8|mp_E3dfk6XE+|0J;3R522Lz8sa)t?&Yaz z`5a`gPu}~|=abcpBbeZ9?M@gPWVh>eb4^dvv_iDZmXsR$}c9B=%U%-A+v52U+OR-ni zk0w=^z)*R&yXM{QRlS1NqGFXo#TlSAwY1bGSjuuaQQfiEh;urNK1K7bdwSLGwrVg- zZ5N`vKoL<&${TViPw^B!TN~fC*YsN~33}zI=pyS^yx=bywn7^Z zo}rNdaJF3}B<9vtF@Uv2&<()T<4ZFa{?pHN)6>)53(xa%HJV9UJ`JYHfQ;p5{IMAT4QTZ?!4BfYgS*u#EI4@j-4F&H`i zmDeYh#~_i&5!juzaltn|``KR)9EX5)@3-Pt*4CT)XZt6I@|aq5EJ8qla%}aJSv>mn z9dcP&5QHWh)H77?0%#>epHi|5i3sz`rM-Z(IQfszS`s^QA;sd?yp~Nns7@?%NLh? zcr&^wwP=DE+lutYTc(%7Nx9Q}zx&vHMwsS)U_{b!FZ^I}d-(9J<;m9g!Z=D_9J0C{ zO+ZE-QEA0hi?#~mz*>85hjf1e!FE~{LU1ZA5feOMLaA4pl z23^d})6bu3Rj-W)NfR z4Cy6AJQK?#aVZP;=JL_nUhS#&uormjT&M0{^$n1v4v;_ktlDx3t1@;^yY%k zVAQzvb7ujFq!RcRK#*c~c;?eo(-riO->OB|nvFOocxo`(3~xvRMWD3}E(Pr0-RTQ_ zQ^J++mmyJ-0$L$(PhD|6%6alYfLCK3RAZlKb-~fs~GD!n6iWuqVpt z+^smR&~lkvsFp^HDGvP9&l(F)UqkiJl&KVI3wL^UZh#Hm!59!7w+ScTb+ zn6zBJ>rJ>?5GxN3m;SZXw88P(uU?=ZsIa~uRHaeTL@kaXsjA6#nWE)cJsNlFFL%g& zv0T1uJR_&rRj(=9QtaE+WYL!WY|#9Nm;74qM234!*8_k{j}PD85*HjF(7QatLIBzG z_LRA=hYtW?r`jJ+Lhk%?PW7z&tpDlT?$1KRfFbOB*?Zyb4FKqqTlL{PqSKHK=oUhP z!0^raK}?|b-P*G=z6E7?HFaEjbnD1$!uvAkI(*^Nlk6!~tihkRG{Y{?6gB80UR2>nj_H2C?5_gZrof z@u+<7t!C+AvGRpOh=>1S*`vezbjQcXYg=0?AodBRUSNuT>eoO(K9?y>WJqj$T*@Ow;pTt@~2)>RvLsS=P{Zj10B7d%C!7 z&D9prhB;4v%Ve^IW!>+U>CRXBwsskJKXKxgf~ud2?l8=h0r}g<%{K~VH03d7M7yhd4d~pQNu0*V?7NEi)~~l!pWFSEbI0PaT(YxDfiBXD zO#aU0pZasCVF{NpET8n6!9ecfz@THIYtu;Z=nNm|ra&HvCE;IiaVN_DuY;$1RN<)e zn1dEJ!w!Y9CnY7}+NI)gSCChpMqDl3`RFf!yALzcjwB&0@vvM@^f8Nu}PL?IF&i=5({j{{4LO ze|;!vzDNp)lgAIwo}U-HMLh z1H>st)tz7JuYdc;iAX{Yl$MC0X8|NYLP7$Fw+Q?mT>lnDnR5#o9!jL4j5yvL%hO*w zJUP7HkJ0#Y7f;vN=NjfP1v|Gl-(1ZF7e~E=(Ck)w{nOg!JQ5oKjR03r?NOj`b@>=X zr}B5QoWXAp$Awf(y~X++DZbFr+4$hNcXIfk9!q!qNka&eaX1#W3N?I>WW^MTaR2Qb z3kwVHrhANPiTw;Fu}=mON}J3USHMUsO5L#Qn=yI$qBoEitrw*`zHu9p+?Tg*%w`k=+BT3HY?JkrAk2-lKjCA^n!>lL;TXL zzm-DKCVBxVh4ru*H~V5ueJ{nrY$ekm*efPQD>#9C68rY0{h-iUF#0?t#U zDF|iqfgrz90S^cI=kWjaQP23Ls!1Qwtc1X@1k|-(%{Ir`3OPB!kUtED1z8VoDzykE zgMma=(kABq-IRVhBz1bO|A(UEflt(F*F!??^8%=g4XSUVmxRCBq55M@QRid%25j0P zx6v5Vy3_|G!92I_+_{o3!S)y5(?mz5z#Dt|@M*yw33CIs3B1oQ)N|hUScQTAMy#5X zsf|bEXgbhf&E{eRyccLX-zz6}Q!ZJKyIgO~b?#)Y9TDi$!gk5z19T=}Y%CYVBkJ(}!+CxN?u5 zM$gVGvqdvoVJSHfYn%#D+#1;B{B_*6>~?{6VnnipM{$H2ylz``_Iu=-Tf+vpoK=#) zU#z2nP&DJC+!sRhd=9?kVuFMJq{iDi?>-xW`u19$ZS+tNyzpZMT%>;B3ahbxnDb%= z+L(G27vrC4qtmwFMX+WfZXIH3Alk{rY@=W3?rW5dAf~B_H`a@0qQsi|S4oMFQ{c`Y z2hYrx`5j%o0R}YOl}G+J=t1ktEbqV!m^Lx9c}kvI%horm`bvvUhMt{ODrS4RyW6ci ztbFez=?KrK8vY;$wP<(PIJJ4#6BdhwBF(Cw{-$ezdjvncM;6JiF$ zNBPE|9yAL{0edHjEGJuioGGqW`|jdmpuU*;IlxYrT4PdYO$ESSfzb47E>do#5ytq_*| z-rsmsdw!Ws!?cREsv1DydJ$~^WB!0;Dy)55IWZA_3id{+>fJ!eqT-ClUM+fjd^}#% zx;M2rwvXY8GghUB{PVX>T!>ASjZp1gR9_w=pFv`Sz587K4|@HAzGACqXKoA7QDTPY zb%2I^4YY~oA#@MTA1ggYR>o>!zph&*Bvt2E*8_gRC)k-&Ve1)Q!?HcAd=J`HFDdqg z7#`)8$Vdv;ju@V5FG%yGh6fAf^vAu2Q(Ec77%eucCK|0`Je(dj+zeI@;HZVl;9iK=^|IADvlgXP=F>Rw1=gEkl zRi6puoW(8*`?!dgPL0-iIXJRR7YV~ z22q{2%atMsucAsY>$NO@%?eC14 zz?0G$<9F*XVSm~?iZJgiLieai6!cGwQ&*~A77l@SF@4T$eTG~dGzEW$U&ea{j_)|G(H%W+J~`+6DwHrt%*5`A=EP@uMVgtPuuIAw2q(@; zGt(eS)sX9rVu5#wjxKK;weSN2pU4!yFyXAP#jRVn=Ibay4-!nORVMlj7ub+6{!d2e zF6uRoU{dj3n+8J4A~XN8M{A>o#++OKL_R>NPw|}T=06YAa*{}kkI(Z~G9HkCZxJy4 zr%WC7lJm56eqasyB(bHuoXXwvWKQz)GCuH3wQa5p;(D{R?2H4mIcnF^+R7~~%-ILU z`s&GCJq-(F#5ng|&;q8gHmh1sZYok1Q8X32>O3T83cI^vL=Jp;J+Fm(z~|?NqJrz6 zE56vlPOW&NHtMC`XhEq8Il`O27}cPt`p(Bn6-{U1t*Qvduh^LEgoimx{a%{W#E z5KGBvF6_Il15RfgM2PG{mgw2-pVWSk<^PZQhc_~uF2s4kg4!+$@qjQHBk+a$`p%)V zW{K!*YODpELg!{&tv?D+Wl$aMUmXJrF1grbDEL&9BOt`}O>gPI7%^<}GH^RVG>8hK zU_5h`Bv6?FUex$`U}lD)Jf%N52455BU|QPR&NyaAvbG-G(o^b0lm?GsaE^$y5KVmz zGI)ja5^6Vm6KEjtll<%a0zfif@VP%*FXI~)edAynjMfF!j~M)ZzdI#A%$xV1KWRa~ zW8=I#s3SsBwwwY0#+z(Tq<4QZ)SjkTN~mgpzUZ85lx*jci)-lfG>J9KC{gld;Z?|B zphDy4wB!3E!cuAVp6H)=IeB?(DIA`2%bPdCN_oJm)I5F^o$wXZr!cPtze6nRpc{CV z4&MBD_X^5n2TNaJeCVXs?O%l*%GuxD zLQh+XGm47TBGsP|0kw6HuuR+gC&cExT?~w%vU?@Cmla`HV72swImjX^ZF;P;_*%2a znYUWEN-q4!=cP+hE~RaiyC0(+m4s>td?I8uy`Ww?bNK-Ul z3PVAQ%bM!yfuW(G!`>Q&fde@?IWr@Iy%XZ}j53zD4?AyyKn=TztiKUuO?ejs;$wSO z<|T~B<|wN)386Nj?xG#3*JM**1+w#{wH>zedHa)Xuanz#6N^6Pgxm$(C8^J%_B$6A%)16!UlR3AUh z_op=j2xb4FGAgoq5OSsC;qiVD$!H|G4Uv{?K9p^&hodBNK7H@2$#+iJOKE;ffOM?P z_Su)i%F+y@paPL`@o$OXqaB7o`~H$iU|w(KPFuYtm1-FLg~>fkW796NIQBdmGgR^< zaUU%#oXm}x?cO1`tqO~uPn77C7_~Nm;Mteu>IP*Obwjb;3xtF{>TqM`zY@a*-0foL+ z5Q7xMnWkYg z?IuvThDP+!dC4V)$<@_$Fq<7d^yk-ulgps#AhQ$@11Va4UsasuQ7(luCsx4kiuuqS z>n+;8so5=AnTPVW$KUbCXTJNsCyx;fb*}fHgA2H|52GLE?iUX_=)Ou+OrpB#uh}62 zUy_ZDy#26Q&gSrW>r4#*)<#KMHST`(Qo|jxdg{M}T^BOtn!u7$u(z*7F=AIIPZlQl zzlYR5DLwPw?@<8MSk&xGF7kJHdv>X_>THOU@cio?g*Xy=XU6(BLyMV!}tPp3~AB#BqHPea7L zFx6d$hOCzai1Y=*!PB72Q@cU>#0nGgJJ+sVvrc-r{xEeE{6b4Npit<18fZpJCmd)D z1@#;yKwv^5wY!rf^8(JO`WT+$Ul|fK#qfftFnEL+|u6K zWL3h~V;2GdpOg^e*es z(7|JcyV&Y7`%3JdsE7#Y>K^P&rb+&!;@l(Pap;!>gWfIGMj4uf04~AmhXl2vG@_s` z2+C<~H?H%@3o-_FhdB2AW)R&@-qMJ6`!mKR_zu!vWY!$>;ai|d{SHA)uvyB?%>oeC zO~X*X`|to|eI5!88M(R^TFoDdv~vMhp1g78l& zI^@d~SI)d@5vC}2?#h)9!e(bTTiUKK^v?@5zx>35QveulIwN1M+t?B|?1C(hD*pJ( zm$3F4!>8IJbafypqv_=(32O2m1vw>Z(cjxeKJMaPcsq}arw*@c&gH)z1`sQFYU=Pm zESl!#BIXu!%`cLFhk%J`=dYb8bT4p#oivdy=!b`$z4V;d=g!`@LXiK>6=<+s1Zzl!;m6--y7c z@mHi+p0+fRua$z`#VrW0QsA*H9>BoxY48j*QF24#J6FQ~qo>3I-FKIwfTJx!6?J4f zFg!IFX{KDSs9S?5@WfMpYWgR}ncfjWi?#wSjL@9Anp}0u{J$g!_U~Y(C&>5;oGk{0P9i;Vt z+UFID5>kYQ7mLwilqB^q(12!X0wUbtVi?SG=9&g`DuUGUBy6w`)T&I9DLd1C|Ws6oLs0f_{yOf4qSzSVj8WUg@0+^k2fJul99q*meMrO%u* zR+zNocPVe-TvX01T*)Z?I*>p*gELAR(i3u&@*qzrFEvBRPJE>TFj~8BW)PcmtX(yQ zZN4w*BMuDk@$Xe&|1_ZnkXRf?=wL1a%x!D)SH42XqV1INIuQRQ&GuPMR&(%x9yYwa zlh^mR?!PhILRh$fA^R=nIOsbxaAh$xEu(}StYyLRbJ^oX$4>o%6Z(XPGQM!D8pSKa}T1$=~_q>$6*@31{NYJ5QBJ(~fKFc|UpjWFi3aEsf2%Tzv za+JR!7unWv@Utq&@u2x|$#;t+ibeGLk|hNTVq$W#b?KD`E0|qF4!Beq@&aoEGy(t6 zEPZr}F3s=6`h;onJKUCl_x1cPi6@PcS5RT^|CpAH4~*Con!9NlZ|@tZ&kUe?sx&Z*KV>QPu0~a!4`|%mLE-f ziSh6>4o*&z133i+CtV6hHsKAR8V{LYjt)NTvr}J83{XL&*b}vkQy-;l+Ujf~$cz7> zc4$Xn;-t3#aY@O?P*i+8V}ALqtKO0|EAy+Mbvuxhlw6GT;{#0fg!%_Rq`$Sk%gX~= z-E4}E!y*q@qZFW^eg7U{3g&A}1=1Yj;rW}D$z{Q5J&>1rv-x;mKJ06&85!1vA&}5t-<@u zz!Pb#k^o3~>Bbd_q@K>gp}bD8gs8Jhy%!(3?t@(@EO5@`PTd#1Qj6;8>G5`MCwNIz z0B3Y6`~Fayn_*d1Zt)eK-sV;u>J1veQUq7P`;z4rKvg8m%x5tuicQjPjO=|on$w*7 zPPeJ90+@LK&QdoQDa+Zv>=PbB7qAN9JTPySrb}wW51qG^P&@tR?*%`f9U+vh}rAkVV-k;nW3Qf;U{{7@KC|XE%OW?d6ebXE2Fek=Li zLT@}^E)6<`qw;gDZEMlie0+S$4)*rS1_p^f(tOuQ0{h85v8{F`V7^X(SbeWn&<*T4 zGXm32mp%Yxj*5!nA(s7XhZT9@YhUG#eysA*)Pe>;+Xb}IJadzmV$qB^i}1HUfpdW; z=n6UF2+>#<2zJ_D{gV#vMUH z`gji`JgyL!y#aqiG-cF^jkETj*Z;*e_h_g9P6s#AIuo>AYK zq%bI4s>3U1o8@`Q1I%*}{PHfC5t_hYoP$4gwq~?%Gt>f_T4V1`Oh(AM-%qLtkeS0V zVVHyPFQ1YiiY^uxzgb+!179$2&PRu$vvpj8ne=sHwT{`+fs zP4pT!Wel~(1dYn<*H7;ogYQXR{L|6&eSx#3!bPj1Cy3aTPpSIXX(DI64DW`NeT8N0 zAm?}Ra4V|#Z6|=J&|NplYMyj{@a|px?x||2=Qh&g9_TBrvWjx_KI$G5-1e^EifF$^ zlW8`)?!$WFAKPb9_B{=J_`(<3Msev}2@n8pU7Py{#zVkWw_o0UL0u^@9{3oJid_i_ zoN``z4~zttxeRT{YgBs#kGaITT#4tXcx!Na&;2~*^6>CFxN_X94A-`|qc&dmSIoU8 z$0ukMV5l|dj_c<(Mm*^TJ*+EC2fs-j5C7cJzfP+gNv!{X94@wn=9{ZoTqOpGnOPO333ZAz;jFM;rV{*t!D(kgLEa*ZigP64K5NiMt|8 z_b!{4-O{qM2&<~9zLTgpDK`8;;lHw6O-)a~6824k-3=eX7T1fU@u1?L$-7cZW-fpR zv7WEYk9HHjI$lNcbXP1T3*ygHYirAgiXFd56-AOrV(=EZlPj{`C$;i6{ zOG%XK3wel$bR=axpOb;vlg2lDbmWxxS5JH1Ev?1!9G{%%U%7H+&aJA&hDzli(0Tjo z$e+4AdcRbEP2RL^nfzof!7n}nH(-}%Lhz&vqNajpsM{*oK?7&H zai3NT?8pIe2{x2MLGUvj7~X*R7C_{_a|4otvMkBiZ^7!cCV~RKDt3JRwjh3|RBSQi zS)a1^XDJTj05x*dIUglsxW2xAOK@;-iI6EU6*#B8*Gb+6BHTL8z>g)KJ$6g0(ajlb z{IMy0-CRyPwCGFc4&YVEc-9g1-Z1oQr?ZZmsx{iwYqNuJFIHNV*d4nIYe~eNNFwdM9eyS+3&0c6bum*m;o#TsN5!Wp?dqsd`CF9=T z#Tl7CZ$P?-3-}8umR|jFkSN^$dH>t!cyuxh1QK@YaRs^lyA{PsSTLs&Ts<#t9}zYR zXk$qN*(@>|cAe2~P{7dbCz-0FXXIoZ`DS)s@W^r`t^=Cw{j`64OR=24$+MC>WF(LP zhjKSwCB?Q$t#`5a_EnADUqj^2Tu$vBy|L(1zH0sZ(Yk^T(=V@Ea+9NFX@fT`2T<=m8L{fqIS#DfBd zrGOX)j2pHQ?N^MRDz3D|>Uu9v6~~xxs{At10zfe}TFP}n#n$`x7vaHE&?TF14FC8F z4WnD*jFdeV|An;b@B#n>`5$=w-@Bxb6(Z*NO2r^$4MI$Pl(!*1c}saY2i?NYiNu(} zbkApH{h(xYE^yYff?U#L7}ZF5``B~0Nh_*CRRD~g+*f|Hl$Mu2YIbyVG#)B_Fhka} z?B-jhGz7^4TgdyNAaYzvHq&u{u$10USQj zE3t|?s>{l{w&lT^P-c&HymkAcK3sn;8*Vf|!m3VHvoBOCmN!9;2S)KPK^GOG4LN7r zJ3sgNb1;5=y@p*!n|{a7Cvs(WM)%mVWIRARj0Y83OMJCY0S6o*jeL;7CM1XmZ`Zzw z2?T)QiAQV}sHC^(&yh;^Tu~pxV_zaf%2J*c1HYbJ;^u zLsp?bl&N%A6Hh4R`>4Ug&cb}K$jymeynU6tUX?OqN!q%Oyg=r9-PxZOPttaC4SIB+ z#hJ+9Y!sN2ra$lQ?&%{xDZ_o_@xhu1s`!Nzt{${(qqX>Pf)bX8>#<;|aEdNg0V4(mqrF=5u1N%)~aX?%`!VX3Q z1|S5V$#oKY^1AXs+#%_T2XObW*1_@z(%(hqPLq@khh|+!7z+F9Z#7EB&8c-X(j?gkoTRv@Nm^KAoE1#6~6Fhdc<9yF8U|mWLW^0oSzh@(_SgMo7EE& z`n4Aud0!pEpxC%R`Au;R$L*eU9ZZR0V^}z&6B2#BI6ohflq7dHByRgf7%PZ6oQIoD z6*`3-qdyaIVA`X(R1FO&uFx&+qYexCFVs8Gk4VOZR#z+NA6ZWMusj41w{iqG9e*V!En!t?F+#<~EtSZjs)hX51&E3}bVb~BzRQ^r)B7}aJQl0{+ipx%`Do|p8 zD=9gsb(dFog>0WLpL9Pf-s|5Y=!>CSj_DO@Q?-skw4BO7Ab9sgDX ziWd9KeKdx|X#f22L$L@pg5Uq`6aSq%64$;aYs6F>!H0tr1KSU;^TcWz^Bx zbMVHZm+#A$Ec%nOm+ZMFBNS?d+)IxeD?vBg+5XLdaH)V;rp9Y0+z$EDrRuBj_QLLMWBFU6bS7_# zPPRlJmPxraJCJ7YyMG_}Mb>2u0!0o}{N zb8bAyhS$6KxlPZPML_52q!{jtOvst3=o^OJi@TxGW^GL9PyK!j@7Ar1qNDHTKVOzS zc#T9VJb(T?DC_a(+Xs0Vb{d67)yL)*7WV-)b#+4Eo4mjNSI>(h+syCZ)ARBkmn`;8 zQhSTgB=)=q)8K)->nQTF;rria@8Gm_uP%Dj`NhMI9xkrIUtq4~UX2%Z`tir+A{hM1 zjvpqbk(KDpe#ptGeA!+ej9P7T?zw?(SmhtRt;rH%fV-nW>e$A=ZBhe>r(KNw_#t$r ze*W#yrNJt@-4-~GZ@s-wU2)#m1tb~@nBe-C25hXr6Qy}D<8#mrYj`qUZyB{sjF>4n zL1-J+FLrlzY3k^N&J=he4^_XKjI1Jt^~X$~+}o|ilcT+R`#neV$HU$sF)(wYWGlV6 z*tMawvQo<3!*=pnSOpjG)o2E~wXsnMc^dHm`E7Jmr?jkWePehiF%^4rHI9-L^byLac$oW)p+ia4r*B*(J;LR`}MR z;?xz35SWLDxUuj0wUI0M0_adOh1v_<2zuzeS&`q}pNa}%mpyIH$>(|lZdK2}tArpk zGlT7`GO}hh!8jV&)v*!$Je0@ql?7#GHj*nIzgdWUI-tA<%>6{Of+-^P3r%qqvlKY{ ze`2g4$46tw{Xby=Crq@l1^`bAc!EJY1@Y!v#Qs1ib^kzt$)@NGc<6=^JfK)MyPZ)g zbT2Tbxw@8?KyoPs(0ow~&}EyD0ea|#xUWY8ZIfISZ{NN(H!xtU$n%_mHR<76d=aZv z#+Jqi5@4W?l-IN3(U%2!OX;xe2%1qTZdSB)kk@?Qqy{s2A)t~#M!QZT$OQEHGmmbo z-#^zHIwR%%ip$>Ln;{A$A)TGcJ}%f;*uD{N^Y59X5T05|1Wr4;@vnBI7DaR9gisJz z1YO?mC|!Q3x$nxNrIq@K+&7zD<(qt3eS{e zw?^Fog|bBg9pqtwAj|dh&b}cH72<05){OS*o|berXPUSx3wVI1y|iG>8h&4JQnGE{ zjQ8WA#V4?X!=pqFMwGv>`ChgasG!minKhy|Tfcl^ZfR-ZzQw~?HJ%ZZC`TqNEQ|+v zeicVW9vvOUq@cbZ(_6`kLGRz!IaS^VS{DvQK)gcd&}=O%TSI?_Wim$di$X>*o`{$PX z`y%Dn&vLTpyjh>Ira)?P%*cDXwe|Hh9T#6Bo*CPlI6`>^%x4H=CD10!7W-oJlA~RQ zGKv7&Z$fD!fplEj25nRG`jFF;Z&F~o=2}1P&>8YL>2>V4Z;>E97%As<2T0w}`*aHh zrY3|(&5yy}fkfx`5-^qCb@b=Un?gdWuCBs>Y0p>jFTxfNIwmbaf~=Fw{4r3%%eKur!*c|fWf znIOu|En>&*eeQEAMqA86bL^YX_1~w^U5eoB6fa5wXCz9C+LI^8nvY;wG}Ux2_8eI0 z0l;Y$y&RWlY5T_Kb6hXfJW0)r)Fm5<{$iX!z02=%5C`UEquji88aKC1z!^_R&h65F zr5LCEX@?iC`-yFrAHWrfogb~aCqj)A<3IZBzacRZpcp6XWfH}bbmHn?cH*3DZ0;)| zt(;)~CFu|!dU4Vk|Bj>z~tET;~nx3VJXwE5By1H!kI;YVt&e5 z!OrbbccTI&u%`!y`=AIavBC~^H|Xc@c`z+rhG1oz_`=T&Pgb$xme_p_6Bm8Yw2YN% zpX{{1RrBtWbw7d4CU?~M*3TPL=FwxUW0k6X>lMLG07?EUK66dOe2A`7rn6wyoC+b{3@%n88wZjd`dH;HolsH0KnW z$W5}~uy+%C6F5(8jW}LYF9u)6tLui6DZu_i4brG&W>}wR-vo+@i$7dh$dWwJmU-F_ z(F5yJ=?|VFa~g<}41Y+v|7_39D0bBby(#s2dpriP>d4 z&*ivL`#q$(-o^r?In65-ZDwkHo>{y~Hmj5BKW7~qQ)wv#J%)kHH`A9Kr> z146C<+MPi*JcCIY3J%0D345b_3qxf0>DAhk3Hw&93_10gMf1V^LOA;I7#H68@$sd3 zIr;{&9%j7EMVKl-d`4V!T(lc7D*0uTIOH&ES@r$9&uz%kH_1on8_}OrOR_udt71EK zu2O8?_dC_xx~ISDH91=7xBBx^6{MQsQ^gK%*EYv;fseb;emF&MITqNP8k)z#K3)tx ze_*!Op9T_rg~7kW0f03T_x}R4rtxXR#yB>xvroV-q@?>P)Iz{>W}7W9ORUmrYuQ^{ zTLA>_FhpA<%U6fbgPar}QP1JO99S^B>)HEpmfd%6wZA@?K2B`k$BaUZ52sI|-L;{T z@M^k>DIYdtpOgDjM>G-t1_LQRm+4GnjSJ$u4Qtkvl; zv!p8ovs$oxw!O>AJcmuGiT9(#ZaTvkHfF0m_-9bsgs?L``S?387wQ}}uSmwV0Bd|kV4 zReRe)0fk5mD@ZSZEtpQ(d^2#tt2Y6i$8JVI(6vK&v7aR1atObCoEj2j;(QZA7O45_ zi;K*my^T$bJcssW=KhGdVBt9x=RrL$Yr;MJ_Acas3?*svUZWPEDyoT%UnR4N?ZpII zBmjsD4LKM}&jjA7x~X|EAU);XJ`EshcB&ui!WB>9h(RpEC}5$$3v~gR-y9<=$69_$ zHq*Jky9spx_2k7D7t6PfquK9WITL9pzlLwZk>9GTYQI1Ht_(n=d}~EwI%v7an|k_2 zvnpn}^yA?l|5q1R9TipA?hoCif~15Zij*`G1Ja;$gMG<$7fs}UVQEIs=f6> zOPrjbo12m)?lN4TAUCQ*#`v)9$K2}1&RGQ7^2=m7263z<9ZVg|N+16ziU6~#e?qr0 zbSITcwb`~+^x*9#7W>-lNeF}cQ3}G4Kt3o)^1(ywEeHuAQ1N&}E#uRum;Cly+d-|* zzDMg(d0Msj`=UM&-JQU(gHWn8QK_#Wi?bJhv*tMg+Kh7{2$1xyOqx01L0m&~9`Ivt z5pi57=4*X(Y?TzrR?)l>G=zuFa&PG^4Fk7hhhYK)hG_}dZzEw4`3t$YyMw0qpo2t| zr`ix;rggNnY0mr=m$HmRI@P>DE+(DIa?J`Q_#?X?^lZa)VBgR1y1*jl{(WLFEAi?7JL7p4uJ88RNB;w2v7QE?4t@hXFN@rb>HcWT83QckK;Vrl-Sw$OZpsgTV zSzpP?^Equ8-EiZ-Lx-)ls~f?7kkfP3Eqzb*Zj+4)}|%U!}| zH+fy85ZC9dBRI!#PZ523X7ji0CC*44@W6f#j9cOZ8f(_P>^(7<<8Vsj5MDAFGf{2H zqu_v-felM1(saFnFqWZl+;UV9TygcZZTp<2BnFeOzQV|n{5XvdD~T^flirozfN=8g z%TihWVJj^l*FK?cH!gv)4O z8@?5_|6jTM$Emj^_#k@6B=qY-+aaRqx8neXAOY~(%q-!b^jL!;`aB{$7k>WGIFjIH zc78(xHv9VsPLV7erQ}dG4Fde_{G@@l=DNC5)Ijf$hJ2{Lyz&d_L!a6E;VfY$ZP(YH z+OiJHbH?BpptJjxbLi&rF8uFTXvVLQ;ERYE**#cUY2p6mtMaW|k)&KQqoZT)udKdK z)z>C7l;NK`uf+d()N%eWu@6ETP9aZ&S)&`_Ck4TCCEWvjknSQcn$X{`YVE+C8F$+D zeR>A^7!ZCuzdF%!p4&+1M#{FV%v)uCv}$>y3X*G#uC&%rpG1nh^tR^1m-Vu2j$7h} zXhIvKJLhn?IRoEM^|ul~J+{*KsQ!^dy{|>5r9pHplGb*D_SzzH9FL$uZk^|tk2I5R z`9LR3J9UDWi0$-Ygx%9%9?}oyU;AJ~r?v+Uv^|^yPDDcG(V~B&GiM zo_VkzE`k)NL_XsCci~O>oG|5>=&pwggYq5f62huKd;^8GCcSMZcwO)prqN&Tj+XBs zRR+*uQtlEz+sL(ZB$gIt(NVqREAE&**>j;gb<@L~(UEl~?q{Jiw)1;?Yr8a}awZ5T zJaAX2omU=D&M2h~2-wNtVCFVte*T*taCY^CFws8+yx?|rf?jH>Zv zS1^b5nZOVvBT)G8=x8_z`nZ;T2Cp?_O9a7U#L$lJ7XklXKeYV%2<|Cv12Tz)_6j}= zUfHC8X|~HgX7UpSjpU9uPDG;M7Mz@Ro?I!sfN_TD@zEPm$Q92^Z>0?~h!(3~k2T-q#fV zof#)Bb@_&?OqQ;GI!6F~dzWt?y{%3G(6!Pp$Fc|H{`Pej$3_<0NSaQSBx^l3OULqP zi!IO2dU*`ZGNb=yv%Y%uN^ML}Utd>Om-D%_Q6MF@rElzV^Qx@qNa*_rdWj2RGic`d z@Fy7-K_gr2fW=&_S=79JxC{ML5xGwzF2{&)3T*2OdTS=Lmynckm%izcm#3REk4o^1 zy=KRmOv7X)#-wS%U+i1CH(Zfi&gcy@$h?Az6GV{n2J{sCYY5O zj@NWluGD75(IHytXF)dHJ}%2|w)MNTuiqyN;y} zd{Ji^P+{h$csXH>%R&q7btO4OJWfu;v+gva^9xVf4@92Gp@qJqL%K;;jKv62aea0$ z=hgK$Uey!f*&b1VlMe+o7-27RtV@;&O>4$w<4dV#5Ev7gcPS|vL=a%>PQNv{C!KlFAxJ1n@{WVYHP3x+ygcSlpFZKli}+=`vXSbE_tnP|KYw-yF(}9172CG& z9ed*3Yr;<>V%$Vt<$RJo_0LUDDf-(D&duh?IlZMJa;Wb*caMZnxgCb?*$Qcq7!@pQ zb|~2}<{AdI7DIu-0tAOAx~3QJbgvZ28Df_2-a@4A%U~mDhU6!vzR?#3SyU1RnN)DG zx|l#~3g^FEPRP$wUaTIN8QgjDM@;tNL$cCt6_l{>@bBMGteLYcMK&EyS#s$uGO|_3 z>T)Zn$@`^*leG%K9`HwWLCwz`pwEr0*@7%@_7mFnIX@i@{Rc53e$0nzlgE2-bf zLI=m{xOF!+x(YblT9CSr&L5loIVN*aoh%KyQSx zhnaU%FS#*807!>D2B9ZF$52<`#TK%t0QKBA=iDCGjl^;%MIzKbM4{z{GaDEKIE+ts zBfss`RvPBY`mGlG4I#rd53*;RSZ$NZ$KIDF;}Z@0b!l;+zuz~oY3i(aMErSSE7+1G z6YCOaoFc91vpitfSW#6y^~TT1X*<{v7nM#E)*5K1cBG(NIOXx}Lcsma$cg0CczJ72MMb6SG}Xyx=Dg2FQsq5N%RE1=ELUE9M? z>i#nw{2M6JdAG-#G?&Ok-kL~8~atmYy{dBVi-2C zMP()B{mp)^YQ_~#^P#%S#y2gY3--Om)=9T(U#I2TgOfSIp`uv>RkVEP&KJ&!U+B(} zz%l4He=Sd(oRo3Xon?IYBZz)W5PTvblwJIL76InI$R#F8-$FWM=84799^agl^&N4S z&V9-NtF;aHrzb@p=b=FZ38qWi{wENWs92hUYLOLaYBQ?|r~-+tD|wKv#9f3ipy00g zJl%9+28D)|7cJsffMhgCBvk%@>{j~Gi(AqRxmd3Yoee%y0J(24E zdNSSU+Zvo=4Ky`vAb!)l`wV%Drf+I{b{D&t&k%gPxs_tbkGIe3Z0gOLW)JEp3ct(z zd4$C^Ix(?wF4llXVNcp53$|AG+zHNaVr7Lp63n%jWYZ5R>~Q+gY3QQnv*b77w|Nmjvg5~#NBd%k=Yy}`*Z z3-)vbi;D8&KrnlLXzcrlt6e8Z@jVe`sx%-5*J+Ke+Vnf9{9*vKcFrr!LQllFh`ud? z78eao?G9sUO_Er|;&-O^V4ZYOKM+o>tJKs5Yh({VJ(A4Fqma9n@jC|@%ixf&aWdzm z%?kj)VCHfw40g{N(9`=%@4%)ZOb6Do9Hdg$!Wl;|SAb&@xT1^8Z0%6wxl34UVRLj) zwE^?gh9%i+Vl>CHqYGjdy+=PpA@$Gs5zV?TPOr<|9GB_$#xV&q^c!m>m4G|2}2r7v2%9}Y=Sr9e!ZR_B{MdlpLqRM&VvFpCJd(6Qph zfd}o7FI)M@th3<%Um#vIPeb<3&X#vu4iGm99c9d~2L|8|2IW?-&a^y**=Wlr@B&%L@$WH3>+7ITmh$O`I;+9a98G1g70+4<|F<|1E}T7P3E} z3KTD+sv61X+SO-&lD>aO54%S1aV^%=%&%C@lY%ych(B({Sd}EqbXB9qr-kAxc4|J& z=-T4mjE9R2X++qsamC@ssgy%8!ZhPgj^~vWmnDEc^N$yeAwl3>t-q@1A?DkVz7Asd zGZ|6uo!N{3^<-@&_SfY`xb-vv*CgaM*^UV7^2$oPSxdI!MNjk*Z|{?^HL%Te-T_3e|D3j zcI4>5_0Uyr5T5m&Xks>+_o)hxwvs>rb=&UN1GYRBvGlzLQ*SGwm3D7wt;q8%rLYyk z!ONdE(j20A?8$zDbvFL>=4~`pdx)kHg_WCkbxr+geY1&XYY!S3z0s20Ya1&C+eW~N zd-@;Z#SCsKtxN-q+z3cD%fbVp=`vCr`@NW0qOPTjxv%&te`x_g;<}r;oIH;7V^*pS zT*flymtsV7E)O?%+XRZ#T%|kMxbi+XH=a%zlQ<0`eZssjXAf-o(Ftn82cKwB;lfHQ zT~hZzGDks;R?AjtQpiH|Oz)2u-t7V9VfY#wK@Wi;ZP*y+Rat{;`sV@m*37wIX#2S! z>wOiH9SPUY?t(D@xV?@KE-?P)=`S4<6)t$oX0mygwFq6%M`~6c?e+?fyMP*0>Kg6& z^x*3Aq;(hac0i-xSjDNEzA=xGMISR6N3T6f-)l(O(lT?ZI%Tqj0stRJW&`EmZPr_X z!J!mj=M!?LbmsEb+_&j^MkSZnzZseiEUJt>Sb>wGWbRSar1jVA0Z3v`Q*nq*f^n|9gvVnX%l>H`>GlpVw ztMsY2%2O^Tzf`dCu92Bps?DCm#0(X%I6a0=OZf`5+a|cU%{E>~9*)@^y)kHieI-jt zY*ROLFx_U#`rbuw9=l7WPUz|5NTy1|PK;m}7G;o9f;%KWNsSmkp6#&KNrVQ;M72Kh z4^d_DUFfT$+1!dLTk&}CLqiY_RrOEXb_-22Z#dEXxNL{&EFbG`F-iM`EJv1TD+&s5 zbT^x1eA?_&cFd{JRjaNwVeV?sUNBYO5U^?YUska)VghninoNR5V+UW%{8K*?u^B{G zQD!zw60Aw_;^oEY)7#*%SRT!)_T|xti#wB|mh{8SjV{M0nL$!T8TyXsq|LE?8Jf(E zqtiSiEb`M$M=P#4MWS??gdp|=I|T1f*d-p-f=_jwVIIM+8p>2gAKzo${DEFO)Y|wE zbG*6~2aJi1HiYpaiY0J4kwcK6mv`#B+~f1ki-|%x*_?@s#Sk5z?OW$H0C5B{D0h-> zH!xXmwi3Bul8=;>lZ3MFu&}^vBVA(_z9_N>y9NT0)5VcKKAbHT30~|8ZoAxQda{m` z$WYd28ba-Ooz6xand1|x+}WE?h&k$(j7RNn*>54e9zJ^XNO@=Vg0Un4I_K(>8ms&~ zaErg3Vr2;*)UEj+2>|XI3>i+8lm|o;5qjuaZ7mdeo|1 z-xW5d@L#SfsLI4x3ZC~%y$e+&AXVk!p2~Zy0Y56k-)!A~`_4Sb_C|{m>*F-~&lGP` znS~d&HuiFfIpXi`#P4k)3ywEQbe4jvSFK|^`7jpY7LG5(b|^@!HN0p&?;@^$Fr_dw z+*8qU6$%Nux+B|9SMfIc8fQb3XiPrjIE4=?y=nLeF~Mf1%IQtMi1 zfel>(zDmR?UV>KF!;&#g5X!vI!_!qDc4KaM%;oS&-AsmfJLiGI7JgjjI~xJ?wqWb; z`|(9fE~0IZl0W8-9!Jeb6*L3y1E4OiS!?oxd>qxE9V0K6uH1Hd?`z*MrKF|;fOH6k z5_n(c5|7I5I!`MIvUlctUKZf>e8~D>kbm~fPOt5*HGaBv!}O+dSq;Oek4aZWYhy|GBI!%`K$5z+29Khe!Y6`3;YzIn=$!*Sj!@DMie(I)KI;*}Y}X?q7~lP- zWBo23m3+xpueaUfY;@jEcTC{U%0>tr;5Ym;5KR!_=kaRsABR6%y`ho>8(oha^2?@H zf&NKBlES=2Pi4jrzhm1eF&3(``4_bYml~e(69LQ;*_RKdzGbhCy5*Pk6!+*$_#M9b zeS4va5;9QB|_#3~u-PsU5BorCd$IUT23=ue&1VAm^2^i31}IWW|> z(_DH7CSMC>s%{4K5pCQ9gJJXUF6;=c%IKZmlR4KoGBO{&xSo6-z)HS!*>6`b5Co>N zWQ^2R5i!Q|05(lvEyNhBvA#YllfFXjer@QEgi7|Xy=Ns4IV#i09P}Wmw}VJxdBMTi z^t3WEjNbPL_!^xrO}!anh@wm=8DhA1goZ8lcG{c+i9-+2H)dyVe3bG%efOIEmL>~2 z&FhHN)pa5a`mi7k4i~)C`_|}t`0b23Q)BeC5aSR}S1G=9U6{LbZ81a-lDI9N8?|Gh2}*DL8zk;G@L7oW!pMJK3nL+5?W9NeUG=c}f4lNv zzo)VWA=pvdBbRu`|4EFE;s*ReKy)Q8vs`9rx!ls4XzCjtwlg$p>z_o%*2JjzZCkg5 zZ%r^;m{|wB!?%WR3k#25?Gcg~YO{QawcQe`C6)MlZV2M{znP;`00cmR&{PkioY$Yr z0su4s1RI*E6-2n2)fQHn?kO&GSt!XZGm8r|<%G~|YS%w9qIDP!0N?=6RPk{2W7p_D z`#%XLhvO^lqrB`+5)}l@DSO4MAB{t|*0t5eZ0$2MLa7EKKmq|Y7yWds??T4q>t_(a z?2u0U4gHg4@k(S-Ws1_W8^lzPWzT8~yjDCDRUcmQ+_bIVQQ4wx(9MH75)gXo5_04I z9ewss%Kpw2f31TEK8qt#GYsvBT9}@1R#zMkPW5|PM1Th_6Ihprb(LVdB|5zWhTd}& zriRUc6~oWplLcNoH?B_%A4}XhmY5V+oE~&GDe!E1(0KaB&*>qAjK9;Dqz9iD(yyfx zAwZ>pebFN`AxTR(Z7+nSWiumzG374u1E`PHs<5z(ijj(0>hbZqHvp>(&X`~m^l z4_|m1pfako?*B*;fkol}eWFF)AOkdzme=ESuE!ZNd=0P1Qxtwt_yz#-l;%s3$6K%4 zVSRnaDln}}px-R?=>6ca=pRg2U^@WNs5aLeVUIzUYgx1kyn=Vm+t6uRVl6gUD~65T%oCc5Ny+YQl{3W83MvZ4VEg@ zPq4wUf&HbAMdz?!DYNjnN&7kf-}C(~7WJFT`a10&f+I5T9FIAtxDhOU4J&)ytGLki z@8UxP`4%!x=qgVkCLKm{vHErT1$qfb)Cwx8H_U4Eat=GY)u;OSYMRt0r2JYr5dhNA zzohsd*)LK4EyXqY0`e`>)}CeEl!dJETHlHn&DYRQDpXL4DK?-K=d?er*m26Q8V{`}pCEb3f!&El{)4(zd zKdaR&f7bfMErG{&PM%5MIKK12Ski;BoXv|f{#~&CsW||sG-lIPCR2ott@v?k$F%gOuJmYw!2g0~!pbwWl9$;{a~w5I)?`cH@vu_z)|6Br$xV zBRuF#Qeem53iDfS9*ph$7tL8v5t9AkM=tsB|InOD)5FtthYeiw4FmIy_Z0n^Sz2FG z@vL5`^lzFIDL7dyILQ}0;U6~5EQ-o36`vEv-a7T-{C}JNsX6Q74Z#Fz4kO<1Uo=Nb z)o}u~sfFVC{`VSJ4}b(sU@ZPu1^|FGSH=C;KEm|181c0k@wH(2ul}z!2J9WN_8hVX z340U(Gy!0yrfztznmRYvKGPu*lHta?NCpo)!T4SMpXDGTYbG2_e@_D<8t{hv+6mF+R{nLX4hxX;D|LL&X32!|?!s2T=fcfW|G_KcA`EMI8X_^1}i_|H36d90Ksd11l5&AU}MM z-hV#L|9$JfA%yTu0pO63jo+@*4LuwhNXw@v2L}S4u-#fTEMx%~y6|+`)<$>*Q|mAz z0F3~qwqv^ot(h}+MYU1F8*vo(A$ndo=mcewtid9t;>n&m#oO<}b(;!=QBcslfMiE3 zkli-YjiTlgdBY7L8UoUXc15#$gr)+}e-ht{qjbNgGqIk2tc}MWv5TH$3QbGv#OIfQ zh;IKfQJ`}dS^CdrYfmh=3xm7y#>J(NGZ$tMi%%(okVDp+X4C?-JQbfPQOqe$DTe(p9(rrv9FaziAx55#+`K zp^Um0cvLSID}l8kkWlFh{;pX3;%dhN{l|8OOfC@r#d#@6_nqOIXBW&jMHv9Rw*vsV zIb>&Aflwj^4IOmBUZzn|W}wf7)UQE1x|pTs{_o-s;elg#=)X@Ww%H+nm*SNE_0;ko z=a>JNrEyM7Q+ohF=u3yu0@HG=8V$>Fhmni5TGRoQBFJ?IKJ}?#VZNIi&N18G)aYmt zN6_TRr=n_WhB>iR{B3)Ms+vNMz^r2$WDwW}E`_r}S--ZBlV#CCI3BAHaxDgI3$=1< z2iw4<#8BMMfo&2wAtr}mdOT>MH>SzPY<7pcWP}si*lt{%oxjl5*gi@s^j+44^#e<# zX_j4|389VDC!7NHt6%i|EI{BGP!Tm;(tXN0I-yMbdAYAR=9Uvlb{=YjwMBp^PM}UO|7yRe~>_XvJUcXQRtH1N#9kn1L zu<>`(0wS>b_k6z)75b8YSHbNUwy^sdkS%0hR;N}$*I}1=(0=JGctS%fNZLj@4cYOx zTwR--oV6ZT){MI<=(n@o!-)>JT;0l3P-e*b^lWl%aY2z|cd@Su*PNP=O+g7- z0B+eS006@*(?DTq$;m2!9nB>d3MeV7=w4&cXiM~~rIp(~>`cmS0l3-Py&>!^;QU z-XOm$Aq{|_P`?kH3w0FgcSJ-={WTs~h=?qh#n9hSl1y~BClNU_0-2Scf^ydu!YwzC=UFzt%4Sf@7QMnyv3EW}neExYskGqTY>TJ$H zLD_kJ(zYuvIuqBA+}e^|G4r<1qa-}PB)q-j^oY`ijzzw;Zmw~|g9H8I{GLXCIE;r0 zRaI?3zKD=4AMUb_RSC)r^kmRqS!9%Jc|lOb=&0&m+fu%WOoZ};1E>zwrahmrdcGag6Cka*(qw-dAs znH`V%ZC$R~<^#NV0_Wl4jE?S4mL183Y1fM3+JGq%ut+xDR~fs{2B?81g9d2JkXwh? z&8iWm!>TQs7O$vk-Eun7Gw0Pv2C}Y}!QJz^K|I}lvH{$E#bUbD9HAneG`pg(CK0-1BTePyBlo)kngWKbRrjsD)<*(TgtnaaiSstf5*h>pVfkZXMk%tq zkYzxnsjaY+ND}fz9A%qkqUUL9x3wnx-TM;5N(G+u2FmERMHu^P+LWT|^NSZVbd_k(D)?RiP=X^q@KL?&$JY7HOSKzgIc5uq?L-Fv5dq;5PYZk3d zlKK6WMT6=94aceiR@(MlgjVNmQ=kzKMFJl7K;tVnh2!MyQ~&tC+qllzc$rF zFDgI{;UU4$kb1Dy%CH@bF%z>;(csdzcR5)lxAYxjxm#DIuQHQX8C&DhDSCh>4B4b;k#*+!4^=hk)?+m5aslB8Di% zR7>Jzu5=UXp?B$KfPel*>)3*I+gfl(!xpOsUBOw;t%{EiyED#t*;FiOf|-*uaP+1U zZfJSH>6Hn7-VI_?j`FA~6QI!sp8C^SjQx&`&+967F1-OMeX7%}HFJkh^R3#;?48jH zg+}B#TV(xDk)PZCwE$088a`}l<>a!Dd~zkq&d!dXY2WNb&X?xFvM^RuE9y;P7|Rr9 z;5tRz5i!@lY`bQhrLV7gO0F|Jy?NlgYV^p8Iz)6byEQ_NR&E!dNT^D&h{IxF%R5%6 z7NZ-h*&=c!YVh>-TT#^0KVE(G(ogRi)3X#xAfa`w+oRP;eXBB8RlRxCn`2|}zW&MU zv>DN$TT9gZyvyNDV-fs-HS2;jQxAUZk4LTgy1r;s@E~*C3xY{mE8+9SWRey`mZAig z@*tMW*S9!hwVTLVS@;wFDv&*!dt)AiB5x<6{0_AEyw=iQM$z%8F*$g^f7hn>N5n9ymCputt|=K{-)!$wv3m7Ut^(i~6wn^%jX zSD;s-_}$5N3=c}Wp^J`=jP2o(U7~3mOdW3HG-ix+jaqwZ4{efjwd(ZoD;q+hHV(H% zq(%u?4qK|6;0!7r2V@JJ5f04e;T}CKiGx)kq(ZFm{iqI94$X^AR=zTO_sQ;$eMhnD zthG1oW07jzp1eyq%4Fpr8v^K|G_*ZJk|7Y1NW}d|NuH@2oL=u66K@o|y68B^7(k(H z_8NRv|C!GDr}O!6<6V<_6^t%ahd_vVbZor>!3aVu1k%!qqjekkVzFr$on`-CK)+UX z!ePbU-*16uI{Hj#>;p z8T;T%eo}H`_qpYG9os|x7Yw*@_I%G|SLyL$0|kYXS(*0eENT=^7R{#AMYZEzsth%^ zip$4Cl<=L+%qCHTF;eyyA4(1K)NZ*sf)B zrKWZ1o+3r4DPb4D`w(~vpy(MYri?<7GQ0&cG;5r+$Q*&ra%QQBiKI%h5XbUil|SuA z5zr7jS~3ivp?&=1rCfh%B*R~Yny373V4Lno(M%*49oruaB;PU%Xg>YcRWTK1PV}K7 zFhkZ`&{)lqYz`PBjOccnI%52gyDJ+&=DXF+JWd_!t1Dv}rSoX_Z$8U#lp=pB>eltp zJe#{U(q|4h9x}7?(RevW*FU#3*OBQLke;-{&B*vMf1F8HP&kCm*OkF_;J$GJ9*?|$D)sK@Mb4Hn~2tCpfNEr0euz|N>}hbdxDGNSMsA@_&d-o zd2+4(Dr)eGM3)M8bxEm<#xscdukq+N4ExYe-TtaE~}eLJ1fH2{M$4{!yV`=hIZI|o!Xs!)OTyk2`3#zg{`9;RKvGaI^Y+h) z_GuzWfKfJu8~cc1-Ksqcr>*l4eT;E53er^aHV$fe{mH_)o!u}%5<4Ei0SW2^Eb2%n zy5{Jg79uXT3BmhxW*=&9gqYBzC|(?GZ*|P;+Oe{)H|-QK--_$cFdKq+#|?zS zI(9o`=sa6wq3VQIaX;>P%~}O!-ta z(1`QLs#dlWa>$j@swgpE7~M=11^eT2AaFP&d16;HqYa9*{F1|0a(J@N6Ox zObY@vqjlEvM3~7r8@y@lyqCv(AMx(MxCY!H&)R4(Qgakr{VSa zBaK-NTPAaFCHqNWS+xtU1{D-74GpW4%`hu7QVOA+-O0xdIX7}cW3~x+A$KC9L?KZj zcY>=~%po61Cq5;Ut=`R=s1~2o(p@M+A|x z7SBF>(O9&55MnQ0x+tC8S#jq?Y^KvsY^=i|914Y^IGAVY7VWizX*wgpk_r+ zrg#J`>)RxEmZcyhVnPf?QcQ@Z#!~R@t)?XWzI__)61FFUDmrztgaoXhI$H-I91#9( zTFPWG630T*5nnc~j=vEt^;AMIM)(N8db;)8^he>VKL{_En42PbJFod@Xd6Vvrm|PO zUKvXFs5Bp`LWWdawm9S39zT5D`|%|+o07XL5^sm^Jh5w%ps%q0yYo!BSvbk@U@5V- zXxo`yp6qMfOMR=W>X`EKpLxqqh|MoXNkT*QWBXafTI+M>98<>&X_)|djD0`FNKeD%CodNQ zuv16!&N(d=0)Iy=qW&XdvGBHS@ST62&b-aMDR|q>ljoO!@rpHItYYDR#k|2hb<^J7 z_v_Kq$ZHx~pV{2r?7-Jn@jm+p+%509MZLLFU{Om-LOVolI^J5Yqli8kZ|M9|+N0JZ z<5Gx^bx|&8n=UQo09>ellTu1BwtBq8mM`~j5Qw7)_zoXsc{7u4**w_v=!Qek2Sy^+ z|8u+sQWRAHf#Q|uGm~%pNjxSUUd>eHW|=B!HP{9-%n5J`k@EB9x?10rKSKdzjE@Bn z`G+LM86NAOmo#~o#~blQ188Mu4VSOVVT`96vT^QNYLt_f+mt(t? z@zZCHeg5c2rvqpVecuq;u;v*^Kozz!5kOKnp7wXPdqJ1dD%Tx1znrNXQO-WPG{^RG z#OHf5wN|gjTgRNWH@AMjpF8>bBRc6a6tIggyw&Nj!73d*6sT)huH<7{39T>*p@3gb zNEvaqu;q(KQpOu_4VUx*CIncs7%(uE`L02r)~mP=q5UW-+H}hB%X&Z8h|~c#ghlZ^ z`Emam0Fw~u55WNa{9u;kohSO>4eS1avC^0XMCi-4#4C`-Xjuo-sWC{Tg{&m9+n~;L zI2GU#QUhYp@YoTk`2cC{eb=(D5?fuywT4#{4}K(FDD&NGU2g3vSR!oEcFB0kH(d2s zi(02!>qUCv8wL7f(@E_T={f~;<)(u1HAfe#2eHgu*WR>>J&PMgyjKFwo0n{(POU{| zY--X>R!ew(C>S-KXL9d(vLXShv*}yUb`4Q+@Vjbg?QNCi$pS_ETJl>6iD48Y5HP!Y=&+WY zU=yI4!EFbl^mjBxDdSusw>TIkbUuU_aX!gv{OI+14lWh482${)ek@&sGA=;zy}&oi zaGWX$DsXo2p<^*wISzDMY;U4B@QmG5*!7CO*DDhZIF4p@UiU2fki7Q~;KIa=t!wCX zjBWWz4E8;Iu>}C9Wd$2twR-*8UHR?|;cI}KE9Wrlr}5>{rsdU)Xy&4Aqo)zdb;>n< zUlTE>hSON@S9NYIa9S^pC4`^hm zq5$Nx1Az^=ApanVXa%1XC2sN-`zRWmlKpYJK5(YvTw9V&h+E(M)W_$`H#hv8_#v5bk4L8nD(Q~8hlYUAB|-V#?R~c@a;1< zLO=o*rxqJfBaHC|5nL))AtJv7t`gnJ#@Tf7$an~l&1fRP%`3?%p3x)XsF01S?zo4! zqH_wVY8YH2LVx-L1*uHp>S}2Z?9U`Tbrro-dTNbA3HW%an2;TS=_WCB;)$dTPb3&6 zq=;Ug@k$U2a1fYI`>w|DxVa{5zOjvr>B{`TGrBn)v$%Wk=RsG4aU`9=GmQBeJWI*# zs1Vhf#!Sv;W^wd$ZB9J)IAe%9N@ZwYlkXVQmtWeaOL~5tVDQ(Kw}>REU#VHr3Zcm7qf2NL^oP?GMiP^MmF{ zI1Ak-77>6s=8`!ko&Vxw%A-x<-8XZOxD2r>mEM&7REd7F^b{7jt!#+Dy4UGjVE&Z{ zYpcxmDcDTh8FAwiWY0qeZxnhkll7+j=E=)1>>o%Ryz{x@hJ${GZCJe=`Nm|;`BOpk zou`%`)==2syhZ2D*a+m!{?ZDlPY!rMjnQ`=jg<8HK2 zo+s}PA2!H0lsbR}^UZY69%l+tyJbBKzn&hhp@{X$e@iSSMJ^L)4F@;#sq9n{Mn*6f zq?884Hn(*Oa_SJMXI`7PGHTbnpI7XksfGj-`BCFFgm6u}+_twE7(IbJ&StV24}Z$P zn2AEhz^lpr(cc{bp(US4jt&_IpjuTo1KGp7XcOoJVxI`^&XEu>v{(uX8Z75DJv)!M zxMPjq;y7voFDbkJOL6RHgLdAF*3Ed6*4t}z)K}eC9<-UQ>yBBs-lb#0Ct&`V%F-zjFRt1Csv=Nv zQ&&xmctE=7uE*=`+ktnm15<09!tUi_M!?sfn;}3S{)XQxKdA#P+m1z!z=u+e@u3mi ze0VS}l+G9yEAotUdy#`}2NZV?6nv3z{ zB3nfzB1e*g!l#07E_jG?fQxYQY1G6AG$Z6Z$y4iI;=N@&elHmOM8jAt53Hq6jH%EG zuFP)cM@t3l#z-LY#7GL!ltX9A@PqE(3imI80ZKeTq{WbjJ7wm5elwBtq+P=ZRtkPO zfa>VDcx1gFUtnX(6KgfxIusgl;E~Pd_UZngMHSAEt|OVwYfE`~`#!z9wEX?fbIV`-e7+y=7&kvrUc2kbeQ(`%0vKS_ zow=RskUKag_#Tm{@YN~|UGt%R`ttICrAC~a#{l5C!8&d|-w&g4GT!BA{?#6CTAm$_ z+DdQ*d@w7ob&)zUg13d9tjp1=iz0jy@h?RHKmZ>Xg6f7DIMPgMhq&6Teqjn6^wj^kOI`Qq&T=>z4XHFMQecv~|H~*;P1s75!$9-GoBSS>}s|yw+ z(1bDo3BiDqE!Lh|1Yzqy{ULL_LIDh^V-|MR>=-C!y&5sTj>I2RmrCxgk{BDo2N&J{ zu0jgO@AL&fw*YspEa0n03ABE^6BIRhv7DVP=a-Z~&`cJ(Js3-aSSthQ;l4JZhby5a6Zb!Xl>MN}xewV8}Z3Zg=U02umbB2+Lm={P&p##m_-jUIy9 zw2wy0kuX3@rArW<1@VyyD}~g-yRq+U`Arf*XK*$b9*Srpk(c&zF*tzEV(G4sP*EKz zmO{n#`+yhQ%08+{5yMMl0R2b&-d$v#+a%UNjPz-)q;ZsQdDBt1k5E zpn}~^g=1&0Jb>s$=}s>uCK10UYyY$K9R`?ch(__b4*;N5I)N-!Q&W?vfhWr%fxNh+ z*d7PbKtdzbi3aYDO%h}ly8$IEQUQuTjdRIMu{EAI{PeDuwkPr`SZ>?Qq)WyyhsMY}h4g*RlJgeH=C`}11h^@)Cq!vMu#M&FeS z_P)_-UTU@vsrQxx8&BZO6y8Yy)qd)I+(y*0MLJ3FHhhZLU>N-B`{ObJD^Jwc^mzAX3hgwZbM zBlG8jH=Y}O{%!BWx3Ip=XZk_cgh#!6F^Sh$l4c^~wJH9bmS?--*ZBu;pCuqiE)|&v;MUh#XB67o48}0V3w`lcnxLT$r;~tI>z?8%4Q{=^& za=zRp0&mdSEI??$+NmBZS=n0V(b*1Y8)r0CKoyGEZCdoUj(|fXB-UV@L!*kigbYBr zQXoeO5m^CQkgQSSmwm2|oqOq72|pit_vSyaA+;2qQomCBb@O}hOFNdqC=uBp9@oOA zk~j+1l*AD~eLl8wD5gTQai!Pt5Vf0YYt@T>;QvG_1j(*G zZar3-R#$$e+gReY*M2Q!S0}6mu2+nMPEB^tD=Q-tU}zmF@ClSF1*Tw~oO1l^y}2*u-AHguVzg&cdWEn=ew zLjqC7`(q3GsK^v&L^dOP5%kY*Wnh8F#^_bbVKa`C%e8jVj@-FZ|w zs+7Xl;?|-j4>)xF><9>5Qo8i!SRRp?3HX{&7Wr->KD*?y+DB=2vLm5y)Ftqg)L!_g zab~Z|&HR*$p(ErCiz5qG??~xUQR=_^Y^{EQ^sPT(`hd3bN>dHp3Z-j^ z`ZHrLsU^{m{U}F=bagP>rZaVm^}EUhneuU?PHcl#1|k-Bt+}!;B=mHOmN+H7H-G}U z=6rlhvo-kDgK=HDkw~1*P2S0MpxFqu5=fO$fY5|27l^^A3$we$|9T0tO&}0g3IsXM zh=E9)s!)lGC?`n8NCb;QV-AZw$@n6?5XQ0ucx)ViX13eDwl%$4v($u#@lO!hnkE(j z#OT%#GD-xUWc(Ez1$g_Jr)f=`=pu<7I!A*)mAlOu6&U)f(&1=7$yPR%2u9x=Mm{Wo zQ`v+X6(>t=2F|_jMO}s6kZ8>vH54!FX}uaW?U9X8YBZ#xba>61pcn)kJ3b-Tr%7A57obv?)7C7)}^2>H;PpX>t)XOvP9>R+ibStFV zK4|9f%{zur?-7yfvX)%MlebGWvg}tMg;7`XOJEw7Rzu*XR>P5NQMk>{!vVu7$>sPpIi7_9U@%ezp#2bkuT)yn zTHg+PQ?U01>uO~AhXYT(4Q-D4`Zi#tJ-$14o3eIuS!6*{Bn9GJdBbJHM6l|qk+IKz ziO-zfuMaHQt}Y2emJqkmx6UmcL+B|RUoXWq<3Jl?uE2m@=qV7Ke^O$7K?-^9-67i0 zXR-`hVSAUh@sT3aOyPkiY-{DIcXUx<&XL8rqOd@}k2M@KCxoUt_^!>L%jK#S+C4W+ zFSoR^+IOr(QzQ{~2_(*}CXR#@jcykPj~GjhuSNJ%EIb=i*_~Zr6t#bP`bHnSIKLvD zUTJm?>(%h!`>?5#I+Wke&B8R0UN@iYF)A&}Ow@QK{!ChaCH>O=dv|B=QBG`+8CWK3 zO#Iq+nYY^vBX+J7hg(2jwOnxXG71^d-O|qk_>P+Bf|GVw=_o%M1J~QbMc2$|7OFT_ zIli4Bt$VCg1=aL1K~`2nq+6eM-`k<-(&McEwGwViYvhk}FUeDafro5$RG7rZx3t}6 zIQ}LXb2DS=vYOfnsM03JUCXr!kBEI|s88C(XG0FCbbRe7||9;i3``rSG2_ zS+P4Xab)AR{wob-88MLR!eprstC5!HneE=S3!01M>i~H7D3c!_50z~V-7S5sda?HY zT_9ikPRlVRm3pYj#Wm)efaYdP4os_J<{gBCK+*b^N)wMfPy2w?W9hVgPFD&3X*IKA z9Zum@lDt#AJ-^|QN1&)q>T7Pa&n^uN7^jke6> zRxOXb3J=}Ck-zi1vF=mR;xp7`L>jQW&M(Jy#uIBsVuT}N*-pFk2fmG8hCs$&eDi&N zdJ!C%6zQr3T;6H@V~PG&o=I-{@T+(02tHejblN5rgOakhQ^a@pKF5rj9H>CB=Gw95 z94D(o%+@<&muArYJ>S?FlZlJ}47)TkHgV#OegEjrbq_kZ6Is@8YB_-?n>4k?)ZAjw z-DFc$cY?f|3?p^}rYc-Wc4K3q4t=EIYQilj*pF;@TlUB-Aug+aeW|)jT&O%NFI@E% ze6Tm+v_m5#`u)mj{N5I#Zkhx5efVIW26nZ+u|R0aM)cdEV9Q(69L}DF@OER{WhU#D zsO#6?|D-CbzVt-edMm`!N;R-$TIz|nv=p0f$`E*HlkZ8vFMZg1?U)4IjhS~b1@(^( zLh8&RpFW@8Icu+Bd*q4z=GWhM$cL9we?mT-XCEbMLdY&O^EQqprKKTQvm*FGI fJC_s!mLFam2O_mp04i`%`4mmR%I02DJMg~%KrSkP literal 0 HcmV?d00001 From 907cb00f353ea9c3e4bb70f5cf7f0f322668cff8 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:18:34 +0000 Subject: [PATCH 04/49] Automatic changelog for PR #5719 [ci skip] --- html/changelogs/AutoChangeLog-pr-5719.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5719.yml diff --git a/html/changelogs/AutoChangeLog-pr-5719.yml b/html/changelogs/AutoChangeLog-pr-5719.yml new file mode 100644 index 000000000000..53e98cc6f85e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5719.yml @@ -0,0 +1,4 @@ +author: "VileBeggar" +delete-after: True +changes: + - rscadd: "The Vulture can now be loaded with holo-targetting rounds which have severely hampered ballistic performance, but mark any target that is hit, increasing their damage taken by 33% for a brief period of time." \ No newline at end of file From f18edfd552114b6ba2c44cbd7069ffc47e67f320 Mon Sep 17 00:00:00 2001 From: private-tristan <54422837+private-tristan@users.noreply.github.com> Date: Tue, 20 Feb 2024 14:27:25 -0500 Subject: [PATCH 05/49] Removes VAIMS backpack burn line, adds extra mag. (#5670) # About the pull request VAIMS (mil contractor medical specialist) had a redundant burn line (because surgical webbing spawns with burn line). this PR removes it and replaces it with an MAR mag to fill the empty space. # Explain why it's good for the game the burn graft is a waste of space in the backpack. As for the MAR mag I put it in so that the inventory doesn't have a random open space, i'm fine removing it if a maintainer tells me to. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: del: VAIMS (vaipo med spec) no longer gets a redundant burn line balance: VAIMS now gets an extra MAR mag. /:cl: --- code/modules/gear_presets/contractor.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/gear_presets/contractor.dm b/code/modules/gear_presets/contractor.dm index bbea2a74339f..3d812b0f6176 100644 --- a/code/modules/gear_presets/contractor.dm +++ b/code/modules/gear_presets/contractor.dm @@ -289,10 +289,10 @@ new_human.equip_to_slot_or_del(new /obj/item/storage/firstaid/adv, WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/roller, WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/roller/surgical, WEAR_IN_BACK) - new_human.equip_to_slot_or_del(new /obj/item/tool/surgery/synthgraft, WEAR_IN_BACK) //Line in vest. new_human.equip_to_slot_or_del(new /obj/item/device/healthanalyzer, WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/storage/box/packet/smoke, WEAR_IN_BACK) new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/mar40/extended, WEAR_IN_BACK) + new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/mar40, WEAR_IN_BACK) //*****************************************************************************************************/ From 7f9c6c52d4601d41bd6906db0a44bdcfb721889b Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:35:28 +0000 Subject: [PATCH 06/49] Automatic changelog for PR #5670 [ci skip] --- html/changelogs/AutoChangeLog-pr-5670.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5670.yml diff --git a/html/changelogs/AutoChangeLog-pr-5670.yml b/html/changelogs/AutoChangeLog-pr-5670.yml new file mode 100644 index 000000000000..1aa82ff3f6ab --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5670.yml @@ -0,0 +1,5 @@ +author: "private-tristan" +delete-after: True +changes: + - rscdel: "VAIMS (vaipo med spec) no longer gets a redundant burn line" + - balance: "VAIMS now gets an extra MAR mag." \ No newline at end of file From b81570c4e42e372f90982270906c9d7ec14516ff Mon Sep 17 00:00:00 2001 From: private-tristan <54422837+private-tristan@users.noreply.github.com> Date: Tue, 20 Feb 2024 16:20:09 -0500 Subject: [PATCH 07/49] Forsaken/Feral Xenos can use hive status. (#5693) # About the pull request due to #5316, the hive status UI checks for a living queen, meaning that hives without living queens (including those that specifically have allow_no_queen_actions (forsaken xenos) were unable to use hive status. while making this PR I noticed that there were two ways to get hive_status and fixed it(and also carried over the we/you). don't look at the branch name. # Explain why it's good for the game I like ~~buffing~~ fixing forsaken xenos. also being able to use hive status is intended # Testing Photographs and Procedure
Screenshots Are here! Click me! forsaken: ![image](https://github.com/cmss13-devs/cmss13/assets/54422837/63d8053e-8e88-41fd-91a3-07382c3c9f64) normal hive (I changed the we/you after this was made) ![image](https://github.com/cmss13-devs/cmss13/assets/54422837/178a7c33-dfea-4418-b341-75341fbacf29) ![image](https://github.com/cmss13-devs/cmss13/assets/54422837/54c64de5-0f73-4d80-988b-f0df8dc15b1b)
# Changelog :cl: fix: Forsaken and Feral xenos can now (correctly) use hive-status spellcheck: fixed a case where we/our wouldn't be used when attempting to access hive status with no queen /:cl: --- code/datums/keybinding/xenomorph.dm | 18 +++--------------- .../living/carbon/xenomorph/hive_status_ui.dm | 2 +- .../mob/living/carbon/xenomorph/xeno_verbs.dm | 6 +++--- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/code/datums/keybinding/xenomorph.dm b/code/datums/keybinding/xenomorph.dm index 9fa525d50002..64acd876b49f 100644 --- a/code/datums/keybinding/xenomorph.dm +++ b/code/datums/keybinding/xenomorph.dm @@ -189,21 +189,9 @@ . = ..() if(.) return - - var/mob/living/carbon/xenomorph/current_xeno = user?.mob - - if(!current_xeno?.hive) - return - - if((!current_xeno.hive.living_xeno_queen || SSmapping.configs[GROUND_MAP].map_name == MAP_WHISKEY_OUTPOST) && !current_xeno.hive.allow_no_queen_actions) //No Hive status on WO - to_chat(current_xeno, SPAN_WARNING("There is no Queen. We are alone.")) - return - - if(current_xeno.interference) - to_chat(current_xeno, SPAN_WARNING("A headhunter temporarily cut off our psychic connection!")) - return - - current_xeno.hive.hive_ui.open_hive_status(current_xeno) + var/mob/living/carbon/xenomorph/xeno = user.mob + xeno.hive_status() + return TRUE /datum/keybinding/xenomorph/hide hotkey_keys = list("Unbound") diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status_ui.dm b/code/modules/mob/living/carbon/xenomorph/hive_status_ui.dm index 17514a31e502..360b4e8bbdde 100644 --- a/code/modules/mob/living/carbon/xenomorph/hive_status_ui.dm +++ b/code/modules/mob/living/carbon/xenomorph/hive_status_ui.dm @@ -137,7 +137,7 @@ return UI_INTERACTIVE // If the Queen died or is otherwise missing. - if(!assoc_hive.living_xeno_queen) + if(!assoc_hive.living_xeno_queen && !assoc_hive.allow_no_queen_actions) return UI_CLOSE /datum/hive_status_ui/ui_data(mob/user) diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm b/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm index dd722f62df63..271b06973a6b 100644 --- a/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm +++ b/code/modules/mob/living/carbon/xenomorph/xeno_verbs.dm @@ -1,18 +1,18 @@ //// Holds Xeno verbs that don't belong anywhere else. /mob/living/carbon/xenomorph/verb/hive_status() set name = "Hive Status" - set desc = "Check the status of your current hive." + set desc = "Check the status of our current hive." set category = "Alien" if(!hive) return if((!hive.living_xeno_queen || SSmapping.configs[GROUND_MAP].map_name == MAP_WHISKEY_OUTPOST) && !hive.allow_no_queen_actions) //No Hive status on WO - to_chat(src, SPAN_WARNING("There is no Queen. You are alone.")) + to_chat(src, SPAN_WARNING("There is no Queen. We are alone.")) return if(interference) - to_chat(src, SPAN_WARNING("A headhunter temporarily cut off your psychic connection!")) + to_chat(src, SPAN_WARNING("A headhunter temporarily cut off our psychic connection!")) return hive.hive_ui.open_hive_status(src) From b6f56b5fd413f98cf6bb56384656ae9eff2bcc41 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Tue, 20 Feb 2024 21:29:04 +0000 Subject: [PATCH 08/49] Automatic changelog for PR #5693 [ci skip] --- html/changelogs/AutoChangeLog-pr-5693.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5693.yml diff --git a/html/changelogs/AutoChangeLog-pr-5693.yml b/html/changelogs/AutoChangeLog-pr-5693.yml new file mode 100644 index 000000000000..1c3ace35fec2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5693.yml @@ -0,0 +1,5 @@ +author: "private-tristan" +delete-after: True +changes: + - bugfix: "Forsaken and Feral xenos can now (correctly) use hive-status" + - spellcheck: "fixed a case where we/our wouldn't be used when attempting to access hive status with no queen" \ No newline at end of file From f3d3d1513428784fcd6be82d5416fc3202d536e5 Mon Sep 17 00:00:00 2001 From: Staykeu <79605233+Staykeu@users.noreply.github.com> Date: Tue, 20 Feb 2024 16:23:21 -0500 Subject: [PATCH 09/49] Allows the MOU53 to be stored in the shotgun scabbard. (#5705) # About the pull request Lets you store the MOU in the shotgun scabbard. # Explain why it's good for the game Shotgun scabbard is soul. Basically no advantage given, other than the fact that you can keep a slot reserved for your MOU. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: Stakeyng add: mou is now compatible with the shotgun scabbard /:cl: --- code/game/objects/items/storage/large_holster.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/game/objects/items/storage/large_holster.dm b/code/game/objects/items/storage/large_holster.dm index 3f653926f8b3..09885db34fc9 100644 --- a/code/game/objects/items/storage/large_holster.dm +++ b/code/game/objects/items/storage/large_holster.dm @@ -64,6 +64,7 @@ can_hold = list( /obj/item/weapon/gun/shotgun/pump, /obj/item/weapon/gun/shotgun/combat, + /obj/item/weapon/gun/shotgun/double/mou53, ) has_gamemode_skin = TRUE From a2d7bb83123e8a8b78a6b039ddfe808923e0a62b Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Tue, 20 Feb 2024 21:42:53 +0000 Subject: [PATCH 10/49] Automatic changelog for PR #5705 [ci skip] --- html/changelogs/AutoChangeLog-pr-5705.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5705.yml diff --git a/html/changelogs/AutoChangeLog-pr-5705.yml b/html/changelogs/AutoChangeLog-pr-5705.yml new file mode 100644 index 000000000000..1e0f665dc3b3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5705.yml @@ -0,0 +1,4 @@ +author: "Stakeyng" +delete-after: True +changes: + - rscadd: "mou is now compatible with the shotgun scabbard" \ No newline at end of file From c2ed945b28dcd8571932f3713e2014b0543df486 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Wed, 21 Feb 2024 01:07:54 +0000 Subject: [PATCH 11/49] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-5584.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5645.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5670.yml | 5 ----- html/changelogs/AutoChangeLog-pr-5693.yml | 5 ----- html/changelogs/AutoChangeLog-pr-5705.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5719.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5752.yml | 4 ---- html/changelogs/AutoChangeLog-pr-5753.yml | 4 ---- html/changelogs/archive/2024-02.yml | 22 ++++++++++++++++++++++ 9 files changed, 22 insertions(+), 34 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5584.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5645.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5670.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5693.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5705.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5719.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5752.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5753.yml diff --git a/html/changelogs/AutoChangeLog-pr-5584.yml b/html/changelogs/AutoChangeLog-pr-5584.yml deleted file mode 100644 index 7d2f7b8e156c..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5584.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Contrabang" -delete-after: True -changes: - - rscadd: "Falling out of a Dropship en route to an LZ, now lets your corpse plummet to the ground instead of being deleted." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5645.yml b/html/changelogs/AutoChangeLog-pr-5645.yml deleted file mode 100644 index b37554dbe5f0..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5645.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "hislittlecuzingames" -delete-after: True -changes: - - maptweak: "tweaked Trijent Dam to add a new choke point." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5670.yml b/html/changelogs/AutoChangeLog-pr-5670.yml deleted file mode 100644 index 1aa82ff3f6ab..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5670.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "private-tristan" -delete-after: True -changes: - - rscdel: "VAIMS (vaipo med spec) no longer gets a redundant burn line" - - balance: "VAIMS now gets an extra MAR mag." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5693.yml b/html/changelogs/AutoChangeLog-pr-5693.yml deleted file mode 100644 index 1c3ace35fec2..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5693.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "private-tristan" -delete-after: True -changes: - - bugfix: "Forsaken and Feral xenos can now (correctly) use hive-status" - - spellcheck: "fixed a case where we/our wouldn't be used when attempting to access hive status with no queen" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5705.yml b/html/changelogs/AutoChangeLog-pr-5705.yml deleted file mode 100644 index 1e0f665dc3b3..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5705.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Stakeyng" -delete-after: True -changes: - - rscadd: "mou is now compatible with the shotgun scabbard" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5719.yml b/html/changelogs/AutoChangeLog-pr-5719.yml deleted file mode 100644 index 53e98cc6f85e..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5719.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "VileBeggar" -delete-after: True -changes: - - rscadd: "The Vulture can now be loaded with holo-targetting rounds which have severely hampered ballistic performance, but mark any target that is hit, increasing their damage taken by 33% for a brief period of time." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5752.yml b/html/changelogs/AutoChangeLog-pr-5752.yml deleted file mode 100644 index 74f574d66b7c..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5752.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - maptweak: "Fixed access for two doors on the Almayer" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5753.yml b/html/changelogs/AutoChangeLog-pr-5753.yml deleted file mode 100644 index 09fe604e5a9a..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5753.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - balance: "Corrected vampire lurker damage values (Effectively reduction by 5 damage for most attacks)" \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index 1feb5e858afc..35e063bd1bdc 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -247,3 +247,25 @@ against your helmet. Zonespace27: - bugfix: You can no longer remove vehicle parts from a vehicle at a distance +2024-02-21: + Contrabang: + - rscadd: Falling out of a Dropship en route to an LZ, now lets your corpse plummet + to the ground instead of being deleted. + Drathek: + - maptweak: Fixed access for two doors on the Almayer + - balance: Corrected vampire lurker damage values (Effectively reduction by 5 damage + for most attacks) + Stakeyng: + - rscadd: mou is now compatible with the shotgun scabbard + VileBeggar: + - rscadd: The Vulture can now be loaded with holo-targetting rounds which have severely + hampered ballistic performance, but mark any target that is hit, increasing + their damage taken by 33% for a brief period of time. + hislittlecuzingames: + - maptweak: tweaked Trijent Dam to add a new choke point. + private-tristan: + - rscdel: VAIMS (vaipo med spec) no longer gets a redundant burn line + - balance: VAIMS now gets an extra MAR mag. + - bugfix: Forsaken and Feral xenos can now (correctly) use hive-status + - spellcheck: fixed a case where we/our wouldn't be used when attempting to access + hive status with no queen From 2abcb7b375c87c5724a2336f832d183ebd2708c6 Mon Sep 17 00:00:00 2001 From: Kaga-404 <103199482+Kaga-404@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:37:32 -0800 Subject: [PATCH 12/49] Fix Select Equipment Presets (#5758) # About the pull request This is literally a one-line fix to an apparently unnoticed bug affecting multiple loadouts, particularly certain ERTs. Explanation below. Select Equipment now spawns in an ID before equipment. # Explain why it's good for the game ID Locks check via the COMSIG_ITEM_ATTEMPTING_EQUIP signal when someone tries to equip them, and searches for an equipped ID to lock with. If there's none, it cancels the equip. /obj/item/proc/mob_can_equip() sends out that signal as part of its testing, and the usual mob equip_to_slot_or_del() proc used in most presets ends up checking the item's mob_can_equip() before equipping. IDs were generated AFTER equipment within /datum/equipment_preset/proc/load_preset() in _select_equipment.dm. This means every single item with an ID lock would be spawned in, the mob would see if it could equip them, the item would return no, cancelling the equip, and the item would be deleted. Pre-equipped Spec loadouts were the most affected, losing all of their armor and ID-locked sights. Most special ERT NVGs were affected as well, being subtypes of the Sniper Spec's NVGs, and sharing the same MOB_LOCK_ON_EQUIP flag. **If there's a reason IDs were spawned in after, this might break something, but I doubt it. Just went tracking once I noticed this bug, and found this as the most effective solution.** An alternative solution was to use the unsafe equip_to_slot() proc instead. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: Kaga fix: Fixed a bug that broke ID-Lockable items in presets. Rejoice, PMC Snipers, pre-equipped USCM Specs, UPP Commandos, and WY Deathsquads. /:cl: --- code/modules/gear_presets/_select_equipment.dm | 2 +- code/modules/gear_presets/clf.dm | 1 - code/modules/gear_presets/cmb.dm | 1 - code/modules/gear_presets/contractor.dm | 2 -- code/modules/gear_presets/upp.dm | 3 +-- 5 files changed, 2 insertions(+), 7 deletions(-) diff --git a/code/modules/gear_presets/_select_equipment.dm b/code/modules/gear_presets/_select_equipment.dm index 5a5ecea2d03d..92c5e5549611 100644 --- a/code/modules/gear_presets/_select_equipment.dm +++ b/code/modules/gear_presets/_select_equipment.dm @@ -149,9 +149,9 @@ load_skills(new_human, mob_client) //skills are set before equipment because of skill restrictions on certain clothes. load_languages(new_human, mob_client) load_age(new_human, mob_client) + load_id(new_human, mob_client) if(show_job_gear) load_gear(new_human, mob_client) - load_id(new_human, mob_client) load_status(new_human, mob_client) load_vanity(new_human, mob_client) load_traits(new_human, mob_client) diff --git a/code/modules/gear_presets/clf.dm b/code/modules/gear_presets/clf.dm index 7748f4e0c558..d9833f0cc038 100644 --- a/code/modules/gear_presets/clf.dm +++ b/code/modules/gear_presets/clf.dm @@ -746,7 +746,6 @@ new_human.set_species(SYNTH_COLONY_GEN_ONE) /datum/equipment_preset/clf/synth/load_gear(mob/living/carbon/human/new_human) - load_name(new_human) var/obj/item/clothing/under/colonist/clf/CLF = new() var/obj/item/clothing/accessory/storage/webbing/W = new() CLF.attach_accessory(new_human, W) diff --git a/code/modules/gear_presets/cmb.dm b/code/modules/gear_presets/cmb.dm index 9de9d11607c2..ae6544fa3d63 100644 --- a/code/modules/gear_presets/cmb.dm +++ b/code/modules/gear_presets/cmb.dm @@ -242,7 +242,6 @@ new_human.set_species(SYNTH_COLONY) /datum/equipment_preset/cmb/synth/load_gear(mob/living/carbon/human/new_human) - load_name(new_human) //backpack new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/security, WEAR_BACK) new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/revolver/cmb/normalpoint, WEAR_IN_BACK) diff --git a/code/modules/gear_presets/contractor.dm b/code/modules/gear_presets/contractor.dm index 3d812b0f6176..3f0cdecb9ac2 100644 --- a/code/modules/gear_presets/contractor.dm +++ b/code/modules/gear_presets/contractor.dm @@ -395,7 +395,6 @@ new_human.set_species(SYNTH_GEN_THREE) /datum/equipment_preset/contractor/duty/synth/load_gear(mob/living/carbon/human/new_human) - load_name(new_human) //back new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/smartpack/black, WEAR_BACK) new_human.equip_to_slot_or_del(new /obj/item/device/defibrillator, WEAR_IN_BACK) //1 @@ -782,7 +781,6 @@ new_human.set_species(SYNTH_GEN_THREE) /datum/equipment_preset/contractor/covert/synth/load_gear(mob/living/carbon/human/new_human) - load_name(new_human) //back new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/smartpack/black, WEAR_BACK) new_human.equip_to_slot_or_del(new /obj/item/device/defibrillator, WEAR_IN_BACK) //1 diff --git a/code/modules/gear_presets/upp.dm b/code/modules/gear_presets/upp.dm index 0b3b4d4ecfb5..c94d7d47843d 100644 --- a/code/modules/gear_presets/upp.dm +++ b/code/modules/gear_presets/upp.dm @@ -2599,7 +2599,7 @@ assignment = JOB_UPP_COMBAT_SYNTH rank = JOB_UPP_COMBAT_SYNTH paygrade = PAY_SHORT_SYN - idtype = /obj/item/card/id/gold + idtype = /obj/item/card/id/dogtag /datum/equipment_preset/upp/synth/load_name(mob/living/carbon/human/new_human, randomise) new_human.gender = pick(50;MALE,50;FEMALE) @@ -2638,7 +2638,6 @@ new_human.set_species(SYNTH_GEN_THREE) /datum/equipment_preset/upp/synth/load_gear(mob/living/carbon/human/new_human) - load_name(new_human) //back new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack/upp, WEAR_BACK) new_human.equip_to_slot_or_del(new /obj/item/device/defibrillator/compact, WEAR_IN_BACK) //1 From bf2ad72e82ca6a552726c4e03be9b2cda2989fea Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 21 Feb 2024 03:46:21 +0000 Subject: [PATCH 13/49] Automatic changelog for PR #5758 [ci skip] --- html/changelogs/AutoChangeLog-pr-5758.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5758.yml diff --git a/html/changelogs/AutoChangeLog-pr-5758.yml b/html/changelogs/AutoChangeLog-pr-5758.yml new file mode 100644 index 000000000000..faa5452042bd --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5758.yml @@ -0,0 +1,4 @@ +author: "Kaga" +delete-after: True +changes: + - bugfix: "Fixed a bug that broke ID-Lockable items in presets. Rejoice, PMC Snipers, pre-equipped USCM Specs, UPP Commandos, and WY Deathsquads." \ No newline at end of file From 07345746f4aa4bde11ad95ddd9c8cb08c31646a5 Mon Sep 17 00:00:00 2001 From: BadAtThisGame <79063506+BadAtThisGame302@users.noreply.github.com> Date: Wed, 21 Feb 2024 05:38:30 +0200 Subject: [PATCH 14/49] Distress beacon response messages now have a chance of being static (#5757) # About the pull request Revives this PR. https://github.com/cmss13-devs/cmss13/pull/5393 Adds a probability of (20) for the distress beacon response message to be full of static and therefore not decipherable. # Explain why it's good for the game Part of the fun of distress beacons was not knowing who's going to show up to your door. This brings that back, to an extent. Less of 'Oh no it's hostile UPP!!' over radio as soon as distress is received. # Changelog :cl: add: Implemented a probability of playing a static-filled transmission as the distress received response. /:cl: Co-authored-by: Jeff Watchson --- code/datums/emergency_calls/emergency_call.dm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/code/datums/emergency_calls/emergency_call.dm b/code/datums/emergency_calls/emergency_call.dm index 7884d93b18bc..69e2c02a9165 100644 --- a/code/datums/emergency_calls/emergency_call.dm +++ b/code/datums/emergency_calls/emergency_call.dm @@ -28,6 +28,10 @@ var/mob_min = 3 var/dispatch_message = "An encrypted signal has been received from a nearby vessel. Stand by." //Msg to display when starting var/arrival_message = "" //Msg to display about when the shuttle arrives + /// Probability that the message will be replaced with static. - prob(chance_hidden) + var/chance_hidden = 20 + /// Message to display when distress beacon is hidden + var/static_message = "**STATIC** %$#&!- *!%^#$$ ^%%$# +_!@* &*%$## **STATIC** &%$#^*! @!*%$# ^%&$#@ *%&$#^ **STATIC** --SIGNAL LOST" var/objectives //Txt of objectives to display to joined. Todo: make this into objective notes var/objective_info //For additional info in the objectives txt var/probability = 0 @@ -305,7 +309,10 @@ candidates = list() if(arrival_message && announce_incoming) - marine_announcement(arrival_message, "Intercepted Transmission:") + if(prob(chance_hidden)) + marine_announcement(static_message, "Intercepted Transmission:") + else + marine_announcement(arrival_message, "Intercepted Transmission:") /datum/emergency_call/proc/add_candidate(mob/M) if(!M.client || (M.mind && (M.mind in candidates)) || istype(M, /mob/living/carbon/xenomorph)) From 606b41d0ca72979c30c0e3b506b32468fb57caef Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 21 Feb 2024 04:00:35 +0000 Subject: [PATCH 15/49] Automatic changelog for PR #5757 [ci skip] --- html/changelogs/AutoChangeLog-pr-5757.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5757.yml diff --git a/html/changelogs/AutoChangeLog-pr-5757.yml b/html/changelogs/AutoChangeLog-pr-5757.yml new file mode 100644 index 000000000000..d227d38fd6c8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5757.yml @@ -0,0 +1,4 @@ +author: "BadAtThisGame302" +delete-after: True +changes: + - rscadd: "Implemented a probability of playing a static-filled transmission as the distress received response." \ No newline at end of file From 783611a711b598b63bfa45c09a7508dfc9221725 Mon Sep 17 00:00:00 2001 From: Drathek <76988376+Drulikar@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:48:16 -0800 Subject: [PATCH 16/49] Reword facehugger temporary mute end message (#5762) # About the pull request This PR is a follow up to #5688 correcting the end message for facehuggers. # Explain why it's good for the game Fixes #5756 # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: Drathek spellcheck: Reworded message when a facehugger's temporary mute ends to not mention hivemind /:cl: --- code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm index d7702b9d8ee1..d98e60fe2177 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm @@ -76,7 +76,7 @@ /datum/component/temporary_mute,\ "We aren't old enough to vocalize anything yet.",\ "We aren't old enough to communicate like this yet.",\ - "We feel old enough to be able to vocalize and speak to the hivemind.",\ + "We feel old enough to be able to vocalize now.",\ 3 MINUTES,\ ) From f19daa01247ae6c8e7869fe626b9305177e1c2fe Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 21 Feb 2024 04:13:27 +0000 Subject: [PATCH 17/49] Automatic changelog for PR #5762 [ci skip] --- html/changelogs/AutoChangeLog-pr-5762.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5762.yml diff --git a/html/changelogs/AutoChangeLog-pr-5762.yml b/html/changelogs/AutoChangeLog-pr-5762.yml new file mode 100644 index 000000000000..e91fb28814b9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5762.yml @@ -0,0 +1,4 @@ +author: "Drathek" +delete-after: True +changes: + - spellcheck: "Reworded message when a facehugger's temporary mute ends to not mention hivemind" \ No newline at end of file From e99b22a4a4a35cfebeefd3b4ab6aa5e38b5c5b49 Mon Sep 17 00:00:00 2001 From: iloveloopers <140007537+iloveloopers@users.noreply.github.com> Date: Tue, 20 Feb 2024 23:50:29 -0400 Subject: [PATCH 18/49] autolathes printing times are now significantly faster (#5735) # About the pull request autolathes printing speed is now based on the item's w_class. minimum printing time of 2 seconds and max of 5 seconds. # Explain why it's good for the game its QOL. printing a ton of small items and they all taking 5 seconds to print is a huge pain. # Testing Photographs and Procedure it works, couldn't find any issues. the sounds are fine too, no overlaping. # Changelog :cl: qol: Autolathes are now significantly faster. /:cl: --- code/game/machinery/autolathe.dm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 6ccb0b5b18f7..cf7a0a6bc1a8 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -419,11 +419,16 @@ ) SStgui.update_uis(src) + //Print speed based on w_class. + var/obj/item/item = making.path + var/size = initial(item.w_class) + var/print_speed = clamp(size, 2, 5) SECONDS + //Fancy autolathe animation. icon_state = "[base_state]_n" playsound(src, 'sound/machines/print.ogg', 25) - sleep(5 SECONDS) + sleep(print_speed) playsound(src, 'sound/machines/print_off.ogg', 25) icon_state = "[base_state]" From 20b2e31b0ec9b358084f519273e7907c73336fe9 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 21 Feb 2024 04:27:33 +0000 Subject: [PATCH 19/49] Automatic changelog for PR #5735 [ci skip] --- html/changelogs/AutoChangeLog-pr-5735.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5735.yml diff --git a/html/changelogs/AutoChangeLog-pr-5735.yml b/html/changelogs/AutoChangeLog-pr-5735.yml new file mode 100644 index 000000000000..5f383f47d5fb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5735.yml @@ -0,0 +1,4 @@ +author: "iloveloopers" +delete-after: True +changes: + - qol: "Autolathes are now significantly faster." \ No newline at end of file From bb79497940e2a3c5a503b1b1ff90a135233fc356 Mon Sep 17 00:00:00 2001 From: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com> Date: Wed, 21 Feb 2024 05:17:00 +0100 Subject: [PATCH 20/49] Makes it possible to view helmet cams of SOs/XOs/COs (#5433) # About the pull request Going to rework the whole console at some point. Not proud of this code, give me some ideas # Explain why it's good for the game Helmet cams good! This can already be done by assigning the CO/XO/SO to a squad # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: add: CIC can now view helmet cameras of Command Staff. Literally 1984 /:cl: --- .../game/jobs/job/command/cic/staffofficer.dm | 13 +- .../computer/groundside_operations.dm | 202 ++++++++++-------- 2 files changed, 118 insertions(+), 97 deletions(-) diff --git a/code/game/jobs/job/command/cic/staffofficer.dm b/code/game/jobs/job/command/cic/staffofficer.dm index 94769de2158f..c73270944c3a 100644 --- a/code/game/jobs/job/command/cic/staffofficer.dm +++ b/code/game/jobs/job/command/cic/staffofficer.dm @@ -23,8 +23,17 @@ total_positions_so_far = positions return positions -/datum/job/command/bridge/generate_entry_message(mob/living/carbon/human/H) - return ..() + +/datum/job/command/bridge/generate_entry_conditions(mob/living/M, whitelist_status) + . = ..() + if(!islist(GLOB.marine_leaders[JOB_SO])) + GLOB.marine_leaders[JOB_SO] = list() + GLOB.marine_leaders[JOB_SO] += M + RegisterSignal(M, COMSIG_PARENT_QDELETING, PROC_REF(cleanup_leader_candidate)) + +/datum/job/command/bridge/proc/cleanup_leader_candidate(mob/M) + SIGNAL_HANDLER + GLOB.marine_leaders[JOB_SO] -= M AddTimelock(/datum/job/command/bridge, list( JOB_SQUAD_LEADER = 1 HOURS, diff --git a/code/game/machinery/computer/groundside_operations.dm b/code/game/machinery/computer/groundside_operations.dm index 95ea46177ca2..52ff558cde89 100644 --- a/code/game/machinery/computer/groundside_operations.dm +++ b/code/game/machinery/computer/groundside_operations.dm @@ -1,3 +1,5 @@ +#define COMMAND_SQUAD "Command" + /obj/structure/machinery/computer/groundside_operations name = "groundside operations console" desc = "This can be used for various important functions." @@ -19,6 +21,7 @@ var/lz_selection = TRUE var/has_squad_overwatch = TRUE var/faction = FACTION_MARINE + var/show_command_squad = FALSE /obj/structure/machinery/computer/groundside_operations/Initialize() if(SSticker.mode && MODE_HAS_FLAG(MODE_FACTION_CLASH)) @@ -69,8 +72,11 @@ dat += "

" if(has_squad_overwatch) - dat += "Current Squad:
[!isnull(current_squad) ? "[current_squad.name]" : "----------"]
" - if(current_squad) + if(show_command_squad) + dat += "Current Squad: Command
" + else + dat += "Current Squad: [!isnull(current_squad) ? "[current_squad.name]" : "----------"]
" + if(current_squad || show_command_squad) dat += get_overwatch_info() dat += "
Close" @@ -104,98 +110,94 @@ "} - if(!current_squad) - dat += "No Squad selected!
" + if(show_command_squad) + dat += format_list_of_marines(list(GLOB.marine_leaders[JOB_CO], GLOB.marine_leaders[JOB_XO]) + GLOB.marine_leaders[JOB_SO], list(JOB_CO, JOB_XO, JOB_SO)) + else if(current_squad) + dat += format_list_of_marines(current_squad.marines_list, list(JOB_SQUAD_LEADER, JOB_SQUAD_SPECIALIST, JOB_SQUAD_MEDIC, JOB_SQUAD_ENGI, JOB_SQUAD_SMARTGUN, JOB_SQUAD_MARINE)) else - var/leader_text = "" - var/tl_text = "" - var/spec_text = "" - var/medic_text = "" - var/engi_text = "" - var/smart_text = "" - var/marine_text = "" - var/misc_text = "" - var/living_count = 0 - var/almayer_count = 0 - var/SSD_count = 0 - var/helmetless_count = 0 - - for(var/X in current_squad.marines_list) - if(!X) - continue //just to be safe - var/mob_name = "unknown" - var/mob_state = "" - var/role = "unknown" - var/act_sl = "" - var/area_name = "???" - var/mob/living/carbon/human/H - if(ishuman(X)) - H = X - mob_name = H.real_name - var/area/A = get_area(H) - var/turf/M_turf = get_turf(H) - if(A) - area_name = sanitize_area(A.name) - - if(H.job) - role = H.job - else if(istype(H.wear_id, /obj/item/card/id)) //decapitated marine is mindless, - var/obj/item/card/id/ID = H.wear_id //we use their ID to get their role. - if(ID.rank) - role = ID.rank - - switch(H.stat) - if(CONSCIOUS) - mob_state = "Conscious" - living_count++ - if(UNCONSCIOUS) - mob_state = "Unconscious" - living_count++ - else - continue - - if(!is_ground_level(M_turf.z)) - almayer_count++ - continue + dat += "No Squad selected!
" + dat += "

" + dat += "Refresh
" + return dat - if(!istype(H.head, /obj/item/clothing/head/helmet/marine)) - helmetless_count++ +/obj/structure/machinery/computer/groundside_operations/proc/format_list_of_marines(list/mob/living/carbon/human/marine_list, list/jobs_in_order) + var/dat = "" + var/list/job_order = list() + + for(var/job in jobs_in_order) + job_order[job] = "" + + var/misc_text = "" + + var/living_count = 0 + var/almayer_count = 0 + var/SSD_count = 0 + var/helmetless_count = 0 + var/total_count = 0 + + for(var/X in marine_list) + if(!X) + continue //just to be safe + total_count++ + var/mob_name = "unknown" + var/mob_state = "" + var/role = "unknown" + var/area_name = "???" + var/mob/living/carbon/human/H + var/act_sl = "" + if(ishuman(X)) + H = X + mob_name = H.real_name + var/area/A = get_area(H) + var/turf/M_turf = get_turf(H) + if(A) + area_name = sanitize_area(A.name) + + if(H.job) + role = H.job + else if(istype(H.wear_id, /obj/item/card/id)) //decapitated marine is mindless, + var/obj/item/card/id/ID = H.wear_id //we use their ID to get their role. + if(ID.rank) + role = ID.rank + + switch(H.stat) + if(CONSCIOUS) + mob_state = "Conscious" + living_count++ + if(UNCONSCIOUS) + mob_state = "Unconscious" + living_count++ + else continue - if(!H.key || !H.client) - SSD_count++ - continue - if(H == current_squad.squad_leader && role != JOB_SQUAD_LEADER) - act_sl = "(ASL)" - - var/marine_infos = "[mob_name][role][act_sl][mob_state][area_name]" - switch(role) - if(JOB_SQUAD_LEADER) - leader_text += marine_infos - if(JOB_SQUAD_TEAM_LEADER) - tl_text += marine_infos - if(JOB_SQUAD_SPECIALIST) - spec_text += marine_infos - if(JOB_SQUAD_MEDIC) - medic_text += marine_infos - if(JOB_SQUAD_ENGI) - engi_text += marine_infos - if(JOB_SQUAD_SMARTGUN) - smart_text += marine_infos - if(JOB_SQUAD_MARINE) - marine_text += marine_infos - else - misc_text += marine_infos - - dat += "Total: [current_squad.marines_list.len] Deployed
" - dat += "Marines detected: [living_count] ([helmetless_count] no helmet, [SSD_count] SSD, [almayer_count] on Almayer)
" - dat += "
Search:
" - dat += "" - dat += "" - dat += leader_text + tl_text + spec_text + medic_text + engi_text + smart_text + marine_text + misc_text - dat += "
NameRoleStateLocation
" - dat += "

" - dat += "Refresh
" + if(!is_ground_level(M_turf.z)) + almayer_count++ + continue + + if(!istype(H.head, /obj/item/clothing/head/helmet/marine)) + helmetless_count++ + continue + + if(!H.key || !H.client) + SSD_count++ + continue + if(current_squad) + if(H == current_squad.squad_leader && role != JOB_SQUAD_LEADER) + act_sl = " (ASL)" + var/marine_infos = "[mob_name][role][act_sl][mob_state][area_name]" + if(role in job_order) + job_order[role] += marine_infos + else + misc_text += marine_infos + dat += "Total: [total_count] Deployed
" + dat += "Marines detected: [living_count] ([helmetless_count] no helmet, [SSD_count] SSD, [almayer_count] on Almayer)
" + dat += "
Search:
" + dat += "" + dat += "" + for(var/job in job_order) + dat += job_order[job] + dat += misc_text + dat += "
NameRoleStateLocation
" return dat /obj/structure/machinery/computer/groundside_operations/Topic(href, href_list) @@ -272,23 +274,31 @@ for(var/datum/squad/S in GLOB.RoleAuthority.squads) if(S.active && S.faction == faction) squad_list += S.name + squad_list += COMMAND_SQUAD var/name_sel = tgui_input_list(usr, "Which squad would you like to look at?", "Pick Squad", squad_list) if(!name_sel) return - var/datum/squad/selected = get_squad_by_name(name_sel) - if(selected) - current_squad = selected + if(name_sel == COMMAND_SQUAD) + show_command_squad = TRUE + current_squad = null + else - to_chat(usr, "[icon2html(src, usr)] [SPAN_WARNING("Invalid input. Aborting.")]") + show_command_squad = FALSE + + var/datum/squad/selected = get_squad_by_name(name_sel) + if(selected) + current_squad = selected + else + to_chat(usr, "[icon2html(src, usr)] [SPAN_WARNING("Invalid input. Aborting.")]") if("use_cam") if(isRemoteControlling(usr)) to_chat(usr, "[icon2html(src, usr)] [SPAN_WARNING("Unable to override console camera viewer. Track with camera instead. ")]") return - if(current_squad) + if(current_squad || show_command_squad) var/mob/cam_target = locate(href_list["cam_target"]) var/obj/structure/machinery/camera/new_cam = get_camera_from_target(cam_target) if(!new_cam || !new_cam.can_use()) @@ -379,3 +389,5 @@ lz_selection = FALSE has_squad_overwatch = FALSE minimap_type = MINIMAP_FLAG_PMC + +#undef COMMAND_SQUAD From f9deecb7070bc9f8c813c03ae40bcb2aefdac96f Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 21 Feb 2024 04:41:17 +0000 Subject: [PATCH 21/49] Automatic changelog for PR #5433 [ci skip] --- html/changelogs/AutoChangeLog-pr-5433.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5433.yml diff --git a/html/changelogs/AutoChangeLog-pr-5433.yml b/html/changelogs/AutoChangeLog-pr-5433.yml new file mode 100644 index 000000000000..89e4d482185e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5433.yml @@ -0,0 +1,4 @@ +author: "TheGamerdk" +delete-after: True +changes: + - rscadd: "CIC can now view helmet cameras of Command Staff. Literally 1984" \ No newline at end of file From 27ddbc5c6be86f273f7be8c77dc5cc5ba0997d40 Mon Sep 17 00:00:00 2001 From: Vicacrov <49321394+Vicacrov@users.noreply.github.com> Date: Wed, 21 Feb 2024 09:22:40 +0100 Subject: [PATCH 22/49] Makes drone castes be able to destroy special resin buildings faster; enables destruction from roundstart (#5721) # About the pull request 1. All xenomorph hives except for ferals now start with special resin structure destruction enabled for leaders AND builders (all drone castes), instead of leaders only. 2. Builders can now destroy every special resin structure with a maximum of 5 hits. # Explain why it's good for the game 1. All xenomorph hives except for ferals now start with special resin structure destruction enabled for leaders AND builders (all drone castes), instead of leaders only. **Why**: This is an old, leftover restriction from back when people actively griefed destroying stuff and the hive had no hive-wide announcements for it. Now it just devolved into "queen plz allow special deconstruction" every single round drone castes try to move things around. It is tiresome for both builders and the queen to always enable this. 2. Builders can now destroy every special resin structure with a maximum of 5 hits. **Why**: Most often drones and hivelords are sicced on destroying backline clusters/recovery nodes, since xenos have a limited amount of special structures they can build. Here are the number of hits you needed before to destroy them: | name | health | # of drone hits | # of carrier hits | # of burrower hits | # hivelord hits | | --- | --- | --- | --- | --- | --- | | hive cluster | 1200 | 16 | 15 | 15 | 16 | | recovery node | 400 | 6 | 5 | 5 | 6 | | egg morpher | 300 | 4 | 4 | 4 | 4 | It is soul-killing to hit things 15-16 times for it to get destroyed. Factor in the extra hits when one has to destroy a cluster _and_ recovery nodes and you can quickly see why this is not fun. This part of the code: ``` var/damage_to_structure = M.melee_damage_upper + XENO_DAMAGE_TIER_7 // Builders can destroy beefy things in maximum 5 hits if(isxeno_builder(M)) health -= max(initial(health) * 0.2, damage_to_structure) else health -= damage_to_structure ``` ensures that if your melee damage is higher than the 1/5th of the special structure's max health, you will deal your melee damage instead. This way, you can still destroy egg morphers in only 4 hits, as opposed to 5. # Testing Photographs and Procedure 1. Spawn in a cluster, recovery node, and an egg morpher 2. Slash them and observe the health change # Changelog :cl: tweak: Xenomorph special structure deconstruction is now enabled for leaders and builders from roundstart instead of leaders only, except for feral hives. qol: Drone castes can now destroy clusters and recovery nodes with a maximum of 5 hits (instead of 15-16 and 6, respectively). /:cl: --- code/modules/cm_aliens/XenoStructures.dm | 7 ++++++- code/modules/mob/living/carbon/xenomorph/hive_status.dm | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/code/modules/cm_aliens/XenoStructures.dm b/code/modules/cm_aliens/XenoStructures.dm index 1f27df25fc52..ab38e59002d8 100644 --- a/code/modules/cm_aliens/XenoStructures.dm +++ b/code/modules/cm_aliens/XenoStructures.dm @@ -94,7 +94,12 @@ else playsound(loc, "alien_resin_break", 25) - health -= (M.melee_damage_upper + 50) //Beef up the damage a bit + var/damage_to_structure = M.melee_damage_upper + XENO_DAMAGE_TIER_7 + // Builders can destroy beefy things in maximum 5 hits + if(isxeno_builder(M)) + health -= max(initial(health) * 0.2, damage_to_structure) + else + health -= damage_to_structure healthcheck() return XENO_ATTACK_ACTION diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm index 4b6c0f4b279e..541b41127095 100644 --- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm +++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm @@ -12,7 +12,7 @@ var/egg_planting_range = 15 var/slashing_allowed = XENO_SLASH_ALLOWED //This initial var allows the queen to turn on or off slashing. Slashing off means harm intent does much less damage. var/construction_allowed = NORMAL_XENO //Who can place construction nodes for special structures - var/destruction_allowed = XENO_LEADER //Who can destroy special structures + var/destruction_allowed = NORMAL_XENO //Who can destroy special structures var/unnesting_allowed = TRUE var/hive_orders = "" //What orders should the hive have var/color = null From 81e95e1b7dd6585e1b9b44382dfb07c1d2253408 Mon Sep 17 00:00:00 2001 From: LaylaMcC <32908370+LaylaMcC@users.noreply.github.com> Date: Wed, 21 Feb 2024 09:42:06 +0000 Subject: [PATCH 23/49] Food processor can be used to remove carpotoxin and acidic blood with master domestic skill (#5577) # About the pull request This PR makes it so that characters with master level domestic skill are able to remove carpotoxin and acidic blood from carp fillets and xenomeat. Xenomeat, carp fillets and recipes including those have had their levels adjusted to avoid adding additional carpotoxin/xenoblood when creating a recipe so a processed ingredient won't result in a dangerous recipe. I've tested this using characters with and without the skill with flour, potatoes, xenomeat and carp fillets to ensure that only the affected processor uses are changed, that processing works correctly and that the skill is required. # Explain why it's good for the game This would open more opportunities for the mess technician to serve the crew with recipes that rarely see the light of day due to being dangerous (while leaving some risk if an MST forgets to process the ingredients). Some marine players might also enjoy the roleplay opportunities around getting the chef to turn their fallen enemies into burgers. # Testing Photographs and Procedure
Screenshots & Videos Here is shown a character with insufficient skill unable to modify the food, before putting it in the soda fountain to show its reagents: https://github.com/cmss13-devs/cmss13/assets/32908370/8138d1c5-77a2-4f7d-92e9-9d7059fa3f05 Here is shown a character with sufficient the skill able to modify the food, before putting it in the soda fountain to show its reagents: https://github.com/cmss13-devs/cmss13/assets/32908370/93bb0c99-fccf-46ca-a786-579be203629b
# Changelog :cl: add: Increased levels of acidic blood and carpotoxin in xenomeat and carp fillets. del: Xenomeat and carp fillet-based recipes no longer add additional acidic blood or carpotoxin. qol: Master level domestic skill allows the use of the food processor to remove acidic blood and carpotoxin from xenomeat and carp fillets. fix: Turning named pieces of human meat into meatballs will retain the human's name. /:cl: --------- Co-authored-by: SabreML <57483089+SabreML@users.noreply.github.com> --- code/game/machinery/kitchen/processor.dm | 33 ++++++++++++++++++- .../items/reagent_containers/food/snacks.dm | 18 +++++----- .../reagent_containers/food/snacks/meat.dm | 9 ++++- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/code/game/machinery/kitchen/processor.dm b/code/game/machinery/kitchen/processor.dm index a18f5db8af43..4918df4d9a5b 100644 --- a/code/game/machinery/kitchen/processor.dm +++ b/code/game/machinery/kitchen/processor.dm @@ -19,15 +19,44 @@ /datum/food_processor_process/process(loc, what) if (src.output && loc) - new src.output(loc) + var/obj/item/reagent_container/food/snacks/created_food = new src.output(loc) + var/obj/item/reagent_container/food/snacks/original_food = what + if(original_food.made_from_player) + created_food.made_from_player = original_food.made_from_player + created_food.name = (created_food.made_from_player + created_food.name) if (what) qdel(what) +/datum/food_processor_process/proc/can_use(mob/user) + // By default, anyone can do it. + return TRUE + /* objs */ + +/datum/food_processor_process/xenomeat + input = /obj/item/reagent_container/food/snacks/meat/xenomeat + output = /obj/item/reagent_container/food/snacks/meat/xenomeat/processed + +/datum/food_processor_process/xenomeat/can_use(mob/user) + if(!skillcheck(user, SKILL_DOMESTIC, SKILL_DOMESTIC_MASTER)) + to_chat(user, SPAN_DANGER("You aren't trained to remove dangerous substances from food!")) + return FALSE + return TRUE + /datum/food_processor_process/meat input = /obj/item/reagent_container/food/snacks/meat output = /obj/item/reagent_container/food/snacks/rawmeatball +/datum/food_processor_process/carpmeat + input = /obj/item/reagent_container/food/snacks/carpmeat + output = /obj/item/reagent_container/food/snacks/carpmeat/processed + +/datum/food_processor_process/carpmeat/can_use(mob/user) + if(!skillcheck(user, SKILL_DOMESTIC, SKILL_DOMESTIC_MASTER)) + to_chat(user, SPAN_DANGER("You aren't trained to remove dangerous substances from food!")) + return FALSE + return TRUE + /datum/food_processor_process/potato input = /obj/item/reagent_container/food/snacks/grown/potato output = /obj/item/reagent_container/food/snacks/rawsticks @@ -88,6 +117,8 @@ if (!P) to_chat(user, SPAN_DANGER("That probably won't blend.")) return 1 + if(!P.can_use(user)) + return 1 user.visible_message("[user] put [what] into [src].", \ "You put [what] into [src].") user.drop_held_item() diff --git a/code/game/objects/items/reagent_containers/food/snacks.dm b/code/game/objects/items/reagent_containers/food/snacks.dm index 3ae57723668f..112a8e40e85b 100644 --- a/code/game/objects/items/reagent_containers/food/snacks.dm +++ b/code/game/objects/items/reagent_containers/food/snacks.dm @@ -641,9 +641,17 @@ /obj/item/reagent_container/food/snacks/carpmeat/Initialize() . = ..() reagents.add_reagent("fish", 3) - reagents.add_reagent("carpotoxin", 3) + reagents.add_reagent("carpotoxin", 6) src.bitesize = 6 +/obj/item/reagent_container/food/snacks/carpmeat/processed + name = "processed carp fillet" + desc = "A fillet of spess carp meat. This one has been processed to remove carpotoxin." + +/obj/item/reagent_container/food/snacks/carpmeat/processed/Initialize() + . = ..() + reagents.remove_reagent("carpotoxin", 6) + /obj/item/reagent_container/food/snacks/fishfingers name = "Fish Fingers" desc = "A finger of fish." @@ -653,7 +661,6 @@ /obj/item/reagent_container/food/snacks/fishfingers/Initialize() . = ..() reagents.add_reagent("fish", 4) - reagents.add_reagent("carpotoxin", 3) bitesize = 3 /obj/item/reagent_container/food/snacks/hugemushroomslice @@ -802,7 +809,6 @@ . = ..() reagents.add_reagent("bread", 3) reagents.add_reagent("fish", 3) - reagents.add_reagent("carpotoxin", 3) bitesize = 3 /obj/item/reagent_container/food/snacks/tofuburger @@ -850,7 +856,6 @@ . = ..() reagents.add_reagent("bread", 3) reagents.add_reagent("meatprotein", 3) - reagents.add_reagent("xenoblood", 3) bitesize = 3 /obj/item/reagent_container/food/snacks/clownburger @@ -1070,7 +1075,6 @@ . = ..() reagents.add_reagent("bread", 4) reagents.add_reagent("meatprotein", 2) - reagents.add_reagent("xenoblood", 4) bitesize = 2 /obj/item/reagent_container/food/snacks/wingfangchu @@ -1084,7 +1088,6 @@ . = ..() reagents.add_reagent("soysauce", 4) reagents.add_reagent("meatprotein", 4) - reagents.add_reagent("xenoblood", 4) bitesize = 2 /obj/item/reagent_container/food/snacks/human/kabob @@ -1133,7 +1136,6 @@ /obj/item/reagent_container/food/snacks/cubancarp/Initialize() . = ..() reagents.add_reagent("fish", 6) - reagents.add_reagent("carpotoxin", 3) reagents.add_reagent("hotsauce", 3) bitesize = 3 @@ -1690,7 +1692,6 @@ /obj/item/reagent_container/food/snacks/fishandchips/Initialize() . = ..() reagents.add_reagent("fish", 6) - reagents.add_reagent("carpotoxin", 3) bitesize = 3 /obj/item/reagent_container/food/snacks/sandwich @@ -2172,7 +2173,6 @@ reagents.add_reagent("bread", 10) reagents.add_reagent("meatprotein", 10) reagents.add_reagent("cheese", 10) - reagents.add_reagent("xenoblood", 10) bitesize = 2 /obj/item/reagent_container/food/snacks/xenomeatbreadslice diff --git a/code/game/objects/items/reagent_containers/food/snacks/meat.dm b/code/game/objects/items/reagent_containers/food/snacks/meat.dm index f459d1b169ae..f68f488f268d 100644 --- a/code/game/objects/items/reagent_containers/food/snacks/meat.dm +++ b/code/game/objects/items/reagent_containers/food/snacks/meat.dm @@ -57,9 +57,16 @@ /obj/item/reagent_container/food/snacks/meat/xenomeat/Initialize() . = ..() - reagents.add_reagent("xenoblood", 3) + reagents.add_reagent("xenoblood", 6) src.bitesize = 6 +/obj/item/reagent_container/food/snacks/meat/xenomeat/processed + desc = "A slab of acrid smelling meat. This one has been processed to remove acid." + +/obj/item/reagent_container/food/snacks/meat/xenomeat/processed/Initialize() + . = ..() + reagents.remove_reagent("xenoblood", 6) + //fishable atoms meat // todo: rewrite this into a procgen'ed item when gutting fish? May be incompatible with recipe code if done that way and not hardcoded. /obj/item/reagent_container/food/snacks/meat/fish From 87f7b4bbe9ffa882df5292b77353614b2b06e01f Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 21 Feb 2024 09:51:02 +0000 Subject: [PATCH 24/49] Automatic changelog for PR #5577 [ci skip] --- html/changelogs/AutoChangeLog-pr-5577.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5577.yml diff --git a/html/changelogs/AutoChangeLog-pr-5577.yml b/html/changelogs/AutoChangeLog-pr-5577.yml new file mode 100644 index 000000000000..ea5029462ec5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5577.yml @@ -0,0 +1,7 @@ +author: "LaylaMcC" +delete-after: True +changes: + - rscadd: "Increased levels of acidic blood and carpotoxin in xenomeat and carp fillets." + - rscdel: "Xenomeat and carp fillet-based recipes no longer add additional acidic blood or carpotoxin." + - qol: "Master level domestic skill allows the use of the food processor to remove acidic blood and carpotoxin from xenomeat and carp fillets." + - bugfix: "Turning named pieces of human meat into meatballs will retain the human's name." \ No newline at end of file From 8caa8b9831be0c89848f8d19146496178826301c Mon Sep 17 00:00:00 2001 From: SabreML <57483089+SabreML@users.noreply.github.com> Date: Wed, 21 Feb 2024 10:11:10 +0000 Subject: [PATCH 25/49] Makes observers able to see the UI of Xeno players (and also action buttons) (#5751) # About the pull request Makes auto-observing work for xenomorphs *and* humans by moving it to `/carbon`, and also makes it show the target's action buttons. I'm pretty confident that this all works correctly, but I would still recommend testmerging it anyway. It's ~~probable~~possible that I missed something somewhere and an observer could get stuck being unable to click anything, or something like that. # Explain why it's good for the game This makes auto-observing a bit more consistent, more interesting for observers, and could actually help newer players to learn how to play some castes. (Which abilities to use and when, where to target, etc.) # Testing Photographs and Procedure
Screenshots & Videos https://github.com/cmss13-devs/cmss13/assets/57483089/54fbb8c6-f9fa-444b-b80c-228b4e6e3480
# Changelog :cl: add: Added 'observe' functionality to Xenomorphs, allowing observers to view the target's UI. add: Made observing a player also show their action buttons. /:cl: --- code/_onclick/hud/screen_objects.dm | 10 ++- code/modules/mob/dead/observer/observer.dm | 78 +++++++------------ code/modules/mob/living/carbon/carbon.dm | 19 +++++ code/modules/mob/living/carbon/human/human.dm | 23 ++++++ strings/metatips.txt | 1 + 5 files changed, 77 insertions(+), 54 deletions(-) diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 1eb555fceaf7..ed30ddaaec96 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -59,9 +59,11 @@ /atom/movable/screen/action_button/attack_ghost(mob/dead/observer/user) return -/atom/movable/screen/action_button/clicked(mob/user) +/atom/movable/screen/action_button/clicked(mob/user, list/mods) if(!user || !source_action) return TRUE + if(source_action.owner != user) + return TRUE if(source_action.can_use_action()) source_action.action_activate() @@ -97,7 +99,7 @@ icon_state = "hide" var/hidden = 0 -/atom/movable/screen/action_button/hide_toggle/clicked(mob/user, mods) +/atom/movable/screen/action_button/hide_toggle/clicked(mob/user, list/mods) user.hud_used.action_buttons_hidden = !user.hud_used.action_buttons_hidden hidden = user.hud_used.action_buttons_hidden if(hidden) @@ -107,7 +109,7 @@ name = "Hide Buttons" icon_state = "hide" user.update_action_buttons() - return 1 + return TRUE /atom/movable/screen/action_button/ghost/minimap/get_button_screen_loc(button_number) return "SOUTH:6,CENTER+1:24" @@ -211,7 +213,7 @@ update_icon(user) return 1 -/atom/movable/screen/clicked(mob/user) +/atom/movable/screen/clicked(mob/user, list/mods) if(!user) return TRUE diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index ae86518a640f..ba84f1cca76d 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -188,37 +188,38 @@ clean_observe_target() /// When the observer target gets a screen, our observer gets a screen minus some game screens we don't want the observer to touch -/mob/dead/observer/proc/observe_target_screen_add(observe_target_mob_client, add_to_screen) +/mob/dead/observer/proc/observe_target_screen_add(observe_target_mob_client, screen_add) SIGNAL_HANDLER - if(!client) - return - - if(istype(add_to_screen, /atom/movable/screen/action_button)) - return - - if(istype(add_to_screen, /atom/movable/screen/fullscreen)) - return + var/static/list/excluded_types = typecacheof(list( + /atom/movable/screen/fullscreen, + /atom/movable/screen/click_catcher, + /atom/movable/screen/escape_menu, + /atom/movable/screen/buildmode, + /obj/effect/detector_blip, + )) - if(istype(add_to_screen, /atom/movable/screen/click_catcher)) + if(!client) return - if(istype(add_to_screen, /atom/movable/screen/escape_menu)) - return + // `screen_add` can sometimes be a list, so it's safest to just handle everything as one. + var/list/stuff_to_add = (islist(screen_add) ? screen_add : list(screen_add)) - if(istype(add_to_screen, /obj/effect/detector_blip)) - return + for(var/item in stuff_to_add) + // Ignore anything that's in `excluded_types`. + if(is_type_in_typecache(item, excluded_types)) + continue - client.add_to_screen(add_to_screen) + client.add_to_screen(screen_add) /// When the observer target loses a screen, our observer loses it as well -/mob/dead/observer/proc/observe_target_screen_remove(observe_target_mob_client, remove_from_screen) +/mob/dead/observer/proc/observe_target_screen_remove(observe_target_mob_client, screen_remove) SIGNAL_HANDLER if(!client) return - client.remove_from_screen(remove_from_screen) + client.remove_from_screen(screen_remove) /// When the observe target ghosts our observer disconnect from their screen updates /mob/dead/observer/proc/observe_target_ghosting(mob/observer_target_mob) @@ -256,48 +257,25 @@ ManualFollow(target) reset_perspective() - if(!ishuman(target) || !client.prefs?.auto_observe) + if(!iscarbon(target) || !client.prefs?.auto_observe) return - var/mob/living/carbon/human/human_target = target - - client.eye = human_target - observe_target_mob = human_target - RegisterSignal(observe_target_mob, COMSIG_PARENT_QDELETING, PROC_REF(clean_observe_target)) - RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(observer_move_react)) - - if(!human_target.hud_used) + var/mob/living/carbon/carbon_target = target + if(!carbon_target.hud_used) return client.clear_screen() - LAZYINITLIST(human_target.observers) - human_target.observers |= src - human_target.hud_used.show_hud(human_target.hud_used.hud_version, src) - - var/list/target_contents = human_target.get_contents() - - //Handles any currently open storage containers the target is looking in when we observe - for(var/obj/item/storage/checked_storage in target_contents) - if(!(human_target in checked_storage.content_watchers)) - continue - - client.add_to_screen(checked_storage.closer) - client.add_to_screen(checked_storage.contents) - - if(checked_storage.storage_slots) - client.add_to_screen(checked_storage.boxes) - else - client.add_to_screen(checked_storage.storage_start) - client.add_to_screen(checked_storage.storage_continue) - client.add_to_screen(checked_storage.storage_end) - - break + client.eye = carbon_target + observe_target_mob = carbon_target + carbon_target.auto_observed(src) + RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(observer_move_react)) + RegisterSignal(observe_target_mob, COMSIG_PARENT_QDELETING, PROC_REF(clean_observe_target)) RegisterSignal(observe_target_mob, COMSIG_MOB_GHOSTIZE, PROC_REF(observe_target_ghosting)) RegisterSignal(observe_target_mob, COMSIG_MOB_NEW_MIND, PROC_REF(observe_target_new_mind)) RegisterSignal(observe_target_mob, COMSIG_MOB_LOGIN, PROC_REF(observe_target_login)) - if(human_target.client) - observe_target_client = human_target.client + if(observe_target_mob.client) + observe_target_client = observe_target_mob.client RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_ADD, PROC_REF(observe_target_screen_add)) RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_REMOVE, PROC_REF(observe_target_screen_remove)) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index a5ef1231a140..c56ccafc85ab 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -416,6 +416,25 @@
"} show_browser(user, dat, name, "mob[name]") +/** + * Called by [/mob/dead/observer/proc/do_observe] when a carbon mob is observed by a ghost with [/datum/preferences/var/auto_observe] enabled. + * + * Any HUD changes past this point are handled by [/mob/dead/observer/proc/observe_target_screen_add] + * and [/mob/dead/observer/proc/observe_target_screen_remove]. + * + * Override on subtype mobs if they have any extra HUD elements/behaviour. + */ +/mob/living/carbon/proc/auto_observed(mob/dead/observer/observer) + SHOULD_CALL_PARENT(TRUE) + + LAZYINITLIST(observers) + observers |= observer + hud_used.show_hud(hud_used.hud_version, observer) + + for(var/datum/action/action as anything in actions) + // Add the action's button (not the action itself) to the observer's screen. + observer.client.add_to_screen(action.button) + //generates realistic-ish pulse output based on preset levels /mob/living/carbon/proc/get_pulse(method) //method 0 is for hands, 1 is for machines, more accurate var/temp = 0 //see setup.dm:694 diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 4c62361ec52e..3ebd199b08d9 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -311,6 +311,29 @@
"} show_browser(user, dat, name, "mob[name]") +/** + * Handles any storage containers that the human is looking inside when auto-observed. + */ +/mob/living/carbon/human/auto_observed(mob/dead/observer/observer) + . = ..() + + // If `src` doesn't have an inventory open. + if(!s_active) + return + + // Add the storage interface to `observer`'s screen. + observer.client.add_to_screen(s_active.closer) + observer.client.add_to_screen(s_active.contents) + + // If the storage has a set number of item slots. + if(s_active.storage_slots) + observer.client.add_to_screen(s_active.boxes) + // If the storage instead has a maximum combined item 'weight'. + else + observer.client.add_to_screen(s_active.storage_start) + observer.client.add_to_screen(s_active.storage_continue) + observer.client.add_to_screen(s_active.storage_end) + // called when something steps onto a human // this handles mulebots and vehicles /mob/living/carbon/human/Crossed(atom/movable/AM) diff --git a/strings/metatips.txt b/strings/metatips.txt index a28c90239593..00bb827de69f 100644 --- a/strings/metatips.txt +++ b/strings/metatips.txt @@ -12,6 +12,7 @@ As a mentor, you can become the imaginary friend of a new player to teach them! You shouldn't ignore what your allies are up to. Sometimes they can be organizing a flank in hivemind/radio, sometimes they can be walking up behind you with a slug-loaded shotgun. Either way, it pays to be alert to what they're doing, as much to as what the enemies are. The Wiki (https://cm-ss13.com/wiki) is a very useful repository of information about the game, such as weapons, equipment, xenomorph castes and their strains. It may not be fully up to date much of the time, but the basics are usually accurate. As an observer, you may see how much remaining hijack time is left in the status panel. +As an observer, you can quickly follow someone by ctrl-clicking on their sprite. You can always AdminHelp with the F1 key to question a member of staff regarding rules or game bugs. As ghost you are given extra tools for spectating the round: you can jump and follow specific players, get notifications about CAS and OB strikes, can see all health bars, and such. You can press ESC key to bring up the game pause menu. It allows you change settings, AdminHelp and MentorHelp, and even access the Web Maps of game by clicking at top right. From 1112b02952e866dcc5504547493b31e7dc3fccea Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 21 Feb 2024 10:25:16 +0000 Subject: [PATCH 26/49] Automatic changelog for PR #5751 [ci skip] --- html/changelogs/AutoChangeLog-pr-5751.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5751.yml diff --git a/html/changelogs/AutoChangeLog-pr-5751.yml b/html/changelogs/AutoChangeLog-pr-5751.yml new file mode 100644 index 000000000000..a2f845c0527e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5751.yml @@ -0,0 +1,5 @@ +author: "SabreML" +delete-after: True +changes: + - rscadd: "Added 'observe' functionality to Xenomorphs, allowing observers to view the target's UI." + - rscadd: "Made observing a player also show their action buttons." \ No newline at end of file From fb5541702d38d1afd57d29b67c41842711198b33 Mon Sep 17 00:00:00 2001 From: Drathek <76988376+Drulikar@users.noreply.github.com> Date: Wed, 21 Feb 2024 02:37:13 -0800 Subject: [PATCH 27/49] Fix broken admin medals panel (#5764) # About the pull request This PR is a follow up to #5493 fixing the Medals Panel. Some reason Fragment is busted if you don't use a key? # Explain why it's good for the game Fixes ![image](https://github.com/cmss13-devs/cmss13/assets/76988376/c811b7ef-c9b5-45e3-b95b-bad416e8ab7a) # Testing Photographs and Procedure
Screenshots & Videos ![image](https://github.com/cmss13-devs/cmss13/assets/76988376/41a05979-eda5-4a1f-ae7d-1f8ddaf50129)
# Changelog :cl: Drathek ui: Fix broken admin Medals Panel /:cl: fixes #5768 --- tgui/packages/tgui/interfaces/MedalsPanel.jsx | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tgui/packages/tgui/interfaces/MedalsPanel.jsx b/tgui/packages/tgui/interfaces/MedalsPanel.jsx index 515dba979c1a..24d6467c9b6f 100644 --- a/tgui/packages/tgui/interfaces/MedalsPanel.jsx +++ b/tgui/packages/tgui/interfaces/MedalsPanel.jsx @@ -1,17 +1,15 @@ import { useBackend, useLocalState } from '../backend'; -import { Tabs, Section, Button, Fragment, Stack, Flex } from '../components'; +import { Tabs, Section, Button, Stack, Flex } from '../components'; import { Window } from '../layouts'; const PAGES = [ { title: 'USCM', - component: () => MedalsPage, color: 'blue', icon: 'medal', }, { title: 'Hive', - component: () => MedalsPage, color: 'purple', icon: 'star', }, @@ -23,8 +21,6 @@ export const MedalsPanel = (props) => { const [pageIndex, setPageIndex] = useLocalState('pageIndex', 1); - const PageComponent = PAGES[pageIndex].component(); - return ( { - {
+ <>
); }; From 5045c706d57c9052691d69333b23f7ea3188409b Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 21 Feb 2024 10:57:45 +0000 Subject: [PATCH 28/49] Automatic changelog for PR #5764 [ci skip] --- html/changelogs/AutoChangeLog-pr-5764.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5764.yml diff --git a/html/changelogs/AutoChangeLog-pr-5764.yml b/html/changelogs/AutoChangeLog-pr-5764.yml new file mode 100644 index 000000000000..1239985de3b2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5764.yml @@ -0,0 +1,4 @@ +author: "Drathek" +delete-after: True +changes: + - ui: "Fix broken admin Medals Panel" \ No newline at end of file From 8b99bc27b2657b4b193a5196efb0caf1fb37bd9d Mon Sep 17 00:00:00 2001 From: Julian56 <117036822+Huffie56@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:05:34 +0100 Subject: [PATCH 29/49] refactor barricades files to hopfully have a more constant interaction when trying to fix them with welder. (#5609) # About the pull request fixes: https://github.com/cmss13-devs/cmss13/issues/5229 1-replace a bunch of single letter variable and in the same movement standardize it for all the parent/child object... 2-create a proc that is a group of check to see if you can start fixing a barricade with a welder. 3-implement this proc on metal,plasteel and deployable to have all the check be the same as we want them to all react the same way... # Explain why it's good for the game # Testing Photographs and Procedure i tested welding different different cades at the same time seem to work i had some weirdness but it's working.
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: refactor: refactored files with the aim of making fixing barricade with a welder more constant. fix: being unable to repair more than one metal barricade at the time. /:cl: --------- Co-authored-by: Julien Co-authored-by: Birdtalon --- .../objects/structures/barricade/barricade.dm | 43 +++++++++++++--- .../structures/barricade/deployable.dm | 14 ++---- .../objects/structures/barricade/handrail.dm | 18 +++---- .../objects/structures/barricade/metal.dm | 22 +++------ .../objects/structures/barricade/plasteel.dm | 49 ++++++++----------- 5 files changed, 77 insertions(+), 69 deletions(-) diff --git a/code/game/objects/structures/barricade/barricade.dm b/code/game/objects/structures/barricade/barricade.dm index 0a37e4bcec59..37975d4a8960 100644 --- a/code/game/objects/structures/barricade/barricade.dm +++ b/code/game/objects/structures/barricade/barricade.dm @@ -5,21 +5,28 @@ climbable = TRUE anchored = TRUE density = TRUE - throwpass = TRUE //You can throw objects over this, despite its density. + /// You can throw objects over this, despite its density. + throwpass = TRUE layer = BELOW_OBJ_LAYER flags_atom = ON_BORDER - var/stack_type //The type of stack the barricade dropped when disassembled if any. - var/stack_amount = 5 //The amount of stack dropped when disassembled at full health - var/destroyed_stack_amount //to specify a non-zero amount of stack to drop when destroyed + /// The type of stack the barricade dropped when disassembled if any. + var/stack_type + /// The amount of stack dropped when disassembled at full health + var/stack_amount = 5 + /// to specify a non-zero amount of stack to drop when destroyed + var/destroyed_stack_amount health = 100 //Pretty tough. Changes sprites at 300 and 150 - var/maxhealth = 100 //Basic code functions + var/maxhealth = 100 /// Used for calculating some stuff related to maxhealth as it constantly changes due to e.g. barbed wire. set to 100 to avoid possible divisions by zero var/starting_maxhealth = 100 - var/crusher_resistant = TRUE //Whether a crusher can ram through it. - var/force_level_absorption = 5 //How much force an item needs to even damage it at all. + /// Whether a crusher can ram through it. + var/crusher_resistant = TRUE + /// How much force an item needs to even damage it at all. + var/force_level_absorption = 5 var/barricade_hitsound var/barricade_type = "barricade" //"metal", "plasteel", etc. - var/wire_icon = 'icons/obj/structures/barricades.dmi' //! Icon file used for the wiring + /// ! Icon file used for the wiring + var/wire_icon = 'icons/obj/structures/barricades.dmi' var/can_change_dmg_state = TRUE var/damage_state = BARRICADE_DMG_NONE var/closed = FALSE @@ -35,6 +42,8 @@ var/burn_flame_multiplier = 1 var/repair_materials = list() var/metallic = TRUE + /// Lower limit of damage beyond which the barricade cannot be fixed by welder. Compared to damage_state. If null it can be repaired at any damage_state. + var/welder_lower_damage_limit = null /obj/structure/barricade/Initialize(mapload, mob/user) . = ..() @@ -466,3 +475,21 @@ nailgun.in_chamber = null nailgun.load_into_chamber() return TRUE + +// This proc is to check a bunch of condition to cancel the action that a welder user is trying to do while giving +// a explanation on why... + +/obj/structure/barricade/proc/attackby_welder(obj/item/item, mob/user) + if(!HAS_TRAIT(item, TRAIT_TOOL_BLOWTORCH)) + to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) + return FALSE + + if(health == maxhealth) + to_chat(user, SPAN_WARNING("[src] doesn't need repairs.")) + return FALSE + + if(!(isnull(damage_state)) && !(isnull(welder_lower_damage_limit)) && damage_state >= welder_lower_damage_limit) + to_chat(user, SPAN_WARNING("[src] has sustained too much structural damage to be repaired.")) + return FALSE + + return TRUE diff --git a/code/game/objects/structures/barricade/deployable.dm b/code/game/objects/structures/barricade/deployable.dm index 0d5275f98a3d..3de5ba1928a2 100644 --- a/code/game/objects/structures/barricade/deployable.dm +++ b/code/game/objects/structures/barricade/deployable.dm @@ -26,17 +26,11 @@ /obj/structure/barricade/deployable/attackby(obj/item/item, mob/user) if(iswelder(item)) - if(!HAS_TRAIT(item, TRAIT_TOOL_BLOWTORCH)) - to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) - return - if(user.action_busy) - return - var/obj/item/tool/weldingtool/welder = item - if(health == maxhealth) - to_chat(user, SPAN_WARNING("[src] doesn't need repairs.")) - return + if(!attackby_welder(item, user)) + return FALSE + - weld_cade(welder, user) + weld_cade(item, user) return else if(HAS_TRAIT(item, TRAIT_TOOL_CROWBAR)) diff --git a/code/game/objects/structures/barricade/handrail.dm b/code/game/objects/structures/barricade/handrail.dm index ae166dbbf985..1d641479c3d9 100644 --- a/code/game/objects/structures/barricade/handrail.dm +++ b/code/game/objects/structures/barricade/handrail.dm @@ -67,7 +67,7 @@ reinforced = !reinforced update_icon() -/obj/structure/barricade/handrail/attackby(obj/item/W, mob/user) +/obj/structure/barricade/handrail/attackby(obj/item/item, mob/user) for(var/obj/effect/xenomorph/acid/A in src.loc) if(A.acid_t == src) to_chat(user, "You can't get near that, it's melting!") @@ -75,7 +75,7 @@ switch(build_state) if(BARRICADE_BSTATE_SECURED) //Non-reinforced. Wrench to unsecure. Screwdriver to disassemble into metal. 1 metal to reinforce. - if(HAS_TRAIT(W, TRAIT_TOOL_WRENCH)) // Make unsecure + if(HAS_TRAIT(item, TRAIT_TOOL_WRENCH)) // Make unsecure if(user.action_busy) return if(!skillcheck(user, SKILL_CONSTRUCTION, SKILL_CONSTRUCTION_TRAINED)) @@ -89,7 +89,7 @@ build_state = BARRICADE_BSTATE_UNSECURED update_icon() return - if(istype(W, /obj/item/stack/sheet/metal)) // Start reinforcing + if(istype(item, /obj/item/stack/sheet/metal)) // Start reinforcing if(!can_be_reinforced) return if(user.action_busy) @@ -97,7 +97,7 @@ if(!skillcheck(user, SKILL_CONSTRUCTION, SKILL_CONSTRUCTION_TRAINED)) to_chat(user, SPAN_WARNING("You are not trained to reinforce [src]...")) return - var/obj/item/stack/sheet/metal/M = W + var/obj/item/stack/sheet/metal/M = item playsound(src.loc, 'sound/items/Screwdriver2.ogg', 25, 1) if(M.amount >= 1 && do_after(user, 30, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) //Shouldnt be possible, but doesnt hurt to check if(!M.use(1)) @@ -109,7 +109,7 @@ return if(BARRICADE_BSTATE_UNSECURED) - if(HAS_TRAIT(W, TRAIT_TOOL_WRENCH)) // Secure again + if(HAS_TRAIT(item, TRAIT_TOOL_WRENCH)) // Secure again if(user.action_busy) return if(!skillcheck(user, SKILL_CONSTRUCTION, SKILL_CONSTRUCTION_TRAINED)) @@ -123,7 +123,7 @@ build_state = BARRICADE_BSTATE_SECURED update_icon() return - if(HAS_TRAIT(W, TRAIT_TOOL_SCREWDRIVER)) // Disassemble into metal + if(HAS_TRAIT(item, TRAIT_TOOL_SCREWDRIVER)) // Disassemble into metal if(user.action_busy) return if(!skillcheck(user, SKILL_CONSTRUCTION, SKILL_CONSTRUCTION_TRAINED)) @@ -141,7 +141,7 @@ if(BARRICADE_BSTATE_FORTIFIED) if(reinforced) - if(HAS_TRAIT(W, TRAIT_TOOL_CROWBAR)) // Un-reinforce + if(HAS_TRAIT(item, TRAIT_TOOL_CROWBAR)) // Un-reinforce if(user.action_busy) return if(!skillcheck(user, SKILL_CONSTRUCTION, SKILL_CONSTRUCTION_TRAINED)) @@ -155,8 +155,8 @@ reinforce() return else - if(iswelder(W)) // Finish reinforcing - if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH)) + if(iswelder(item)) // Finish reinforcing + if(!HAS_TRAIT(item, TRAIT_TOOL_BLOWTORCH)) to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) return if(user.action_busy) diff --git a/code/game/objects/structures/barricade/metal.dm b/code/game/objects/structures/barricade/metal.dm index 4f250eed50e9..59f4ad314878 100644 --- a/code/game/objects/structures/barricade/metal.dm +++ b/code/game/objects/structures/barricade/metal.dm @@ -19,6 +19,8 @@ var/build_state = BARRICADE_BSTATE_SECURED //Look at __game.dm for barricade defines var/upgrade = null + welder_lower_damage_limit = BARRICADE_DMG_HEAVY + /obj/structure/barricade/metal/update_icon() . = ..() if(dir > 2) @@ -44,24 +46,16 @@ /obj/structure/barricade/metal/attackby(obj/item/item, mob/user) if(iswelder(item)) - if(!HAS_TRAIT(item, TRAIT_TOOL_BLOWTORCH)) - to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) - return - if(user.action_busy) - return + if(!attackby_welder(item, user)) + return FALSE + + if(!skillcheck(user, SKILL_ENGINEER, SKILL_ENGINEER_TRAINED)) to_chat(user, SPAN_WARNING("You're not trained to repair [src]...")) - return - var/obj/item/tool/weldingtool/welder = item - if(damage_state == BARRICADE_DMG_HEAVY) - to_chat(user, SPAN_WARNING("[src] has sustained too much structural damage to be repaired.")) - return + return FALSE - if(health == maxhealth) - to_chat(user, SPAN_WARNING("[src] doesn't need repairs.")) - return + weld_cade(item, user) - weld_cade(welder, user) return if(try_nailgun_usage(item, user)) diff --git a/code/game/objects/structures/barricade/plasteel.dm b/code/game/objects/structures/barricade/plasteel.dm index dd95aa3f1baf..bba07832f790 100644 --- a/code/game/objects/structures/barricade/plasteel.dm +++ b/code/game/objects/structures/barricade/plasteel.dm @@ -20,12 +20,15 @@ repair_materials = list("plasteel" = 0.3) var/build_state = BARRICADE_BSTATE_SECURED //Look at __game.dm for barricade defines - var/tool_cooldown = 0 //Delay to apply tools to prevent spamming - var/busy = FALSE //Standard busy check + /// Delay to apply tools to prevent spamming + var/tool_cooldown = 0 + /// Standard busy check + var/busy = FALSE var/linked = 0 var/recentlyflipped = FALSE var/hasconnectionoverlay = TRUE var/linkable = TRUE + welder_lower_damage_limit = BARRICADE_DMG_HEAVY /obj/structure/barricade/plasteel/update_icon() ..() @@ -57,35 +60,25 @@ if(BARRICADE_BSTATE_MOVABLE) . += SPAN_INFO("The protection panel has been removed and the anchor bolts loosened. It's ready to be taken apart.") -/obj/structure/barricade/plasteel/weld_cade(obj/item/W, mob/user) +/obj/structure/barricade/plasteel/weld_cade(obj/item/item, mob/user) busy = TRUE ..() busy = FALSE -/obj/structure/barricade/plasteel/attackby(obj/item/W, mob/user) - if(iswelder(W)) - if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH)) - to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) - return - if(busy || tool_cooldown > world.time) - return - tool_cooldown = world.time + 10 +/obj/structure/barricade/plasteel/attackby(obj/item/item, mob/user) + if(iswelder(item)) + if(!attackby_welder(item, user)) + return FALSE + + if(!skillcheck(user, SKILL_ENGINEER, SKILL_ENGINEER_TRAINED)) to_chat(user, SPAN_WARNING("You're not trained to repair [src]...")) - return - var/obj/item/tool/weldingtool/WT = W - if(damage_state == BARRICADE_DMG_HEAVY) - to_chat(user, SPAN_WARNING("[src] has sustained too much structural damage to be repaired.")) - return - - if(health == maxhealth) - to_chat(user, SPAN_WARNING("[src] doesn't need repairs.")) - return + return FALSE - weld_cade(WT, user) + weld_cade(item, user) return - if(try_nailgun_usage(W, user)) + if(try_nailgun_usage(item, user)) return for(var/obj/effect/xenomorph/acid/A in src.loc) @@ -95,7 +88,7 @@ switch(build_state) if(2) //Fully constructed step. Use screwdriver to remove the protection panels to reveal the bolts - if(HAS_TRAIT(W, TRAIT_TOOL_SCREWDRIVER)) + if(HAS_TRAIT(item, TRAIT_TOOL_SCREWDRIVER)) if(busy || tool_cooldown > world.time) return tool_cooldown = world.time + 10 @@ -113,7 +106,7 @@ playsound(src.loc, 'sound/items/Screwdriver.ogg', 25, 1) build_state = BARRICADE_BSTATE_UNSECURED return - if(HAS_TRAIT(W, TRAIT_TOOL_CROWBAR)) + if(HAS_TRAIT(item, TRAIT_TOOL_CROWBAR)) if(!skillcheck(user, SKILL_ENGINEER, SKILL_ENGINEER_ENGI)) to_chat(user, SPAN_WARNING("You are not trained to modify [src]...")) return @@ -133,7 +126,7 @@ cade.update_icon() update_icon() if(1) //Protection panel removed step. Screwdriver to put the panel back, wrench to unsecure the anchor bolts - if(HAS_TRAIT(W, TRAIT_TOOL_SCREWDRIVER)) + if(HAS_TRAIT(item, TRAIT_TOOL_SCREWDRIVER)) if(busy || tool_cooldown > world.time) return tool_cooldown = world.time + 10 @@ -146,7 +139,7 @@ playsound(src.loc, 'sound/items/Screwdriver.ogg', 25, 1) build_state = BARRICADE_BSTATE_SECURED return - if(HAS_TRAIT(W, TRAIT_TOOL_WRENCH)) + if(HAS_TRAIT(item, TRAIT_TOOL_WRENCH)) if(busy || tool_cooldown > world.time) return tool_cooldown = world.time + 10 @@ -163,7 +156,7 @@ return if(0) //Anchor bolts loosened step. Apply crowbar to unseat the panel and take apart the whole thing. Apply wrench to rescure anchor bolts - if(HAS_TRAIT(W, TRAIT_TOOL_WRENCH)) + if(HAS_TRAIT(item, TRAIT_TOOL_WRENCH)) if(busy || tool_cooldown > world.time) return tool_cooldown = world.time + 10 @@ -182,7 +175,7 @@ build_state = BARRICADE_BSTATE_UNSECURED update_icon() //unanchored changes layer return - if(HAS_TRAIT(W, TRAIT_TOOL_CROWBAR)) + if(HAS_TRAIT(item, TRAIT_TOOL_CROWBAR)) if(busy || tool_cooldown > world.time) return tool_cooldown = world.time + 10 From 57898876a46902667c2a49c4c84d7eec4ab6e626 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:14:00 +0000 Subject: [PATCH 30/49] Automatic changelog for PR #5609 [ci skip] --- html/changelogs/AutoChangeLog-pr-5609.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5609.yml diff --git a/html/changelogs/AutoChangeLog-pr-5609.yml b/html/changelogs/AutoChangeLog-pr-5609.yml new file mode 100644 index 000000000000..4ad1eb6ed5db --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5609.yml @@ -0,0 +1,5 @@ +author: "Huffie56" +delete-after: True +changes: + - refactor: "refactored files with the aim of making fixing barricade with a welder more constant." + - bugfix: "being unable to repair more than one metal barricade at the time." \ No newline at end of file From c15e764038af693dadfa6d912e2ca1473e6ade3e Mon Sep 17 00:00:00 2001 From: Drathek <76988376+Drulikar@users.noreply.github.com> Date: Wed, 21 Feb 2024 12:24:34 -0800 Subject: [PATCH 31/49] Fix CMB ERT & Path Based ERT Calling (#5755) # About the pull request This PR fixes the black market CMB automatic ERT and makes get_specific_call ~~more flexible and~~ efficient. # Explain why it's good for the game Fixes `## ERROR: get_specific_call could not find emergency call 'Inspection - Colonial Marshal Ledger Investigation Team'` caused by a grammar fix from Marshal to Marshals. This should generally be harder to cause since the call can now only be called via a path. # Testing Photographs and Procedure
Screenshots & Videos ![1](https://github.com/cmss13-devs/cmss13/assets/76988376/2beff6de-ea6a-40bd-b4e8-7c13ed618308) ![2](https://github.com/cmss13-devs/cmss13/assets/76988376/4dbd1ba2-6782-4849-87ba-fef0ae57121b) ![4](https://github.com/cmss13-devs/cmss13/assets/76988376/58d5017e-9ebf-4d93-86b5-de0428b5e7bb) ![image](https://github.com/cmss13-devs/cmss13/assets/76988376/74d47e01-5d7c-4fd2-b02e-6ee32d57e357) ![image](https://github.com/cmss13-devs/cmss13/assets/76988376/acd80c2f-100a-41cc-8401-b1803a04ad7d)
# Changelog :cl: Drathek fix: Fixed automatic CMB ERT caused by black market heat code: get_specific_call now only accepts a path or a string of a path /:cl: --- code/datums/emergency_calls/emergency_call.dm | 19 ++++++++++----- .../colonialmarines/colonialmarines.dm | 2 +- .../colonialmarines/whiskey_outpost.dm | 2 +- .../objects/items/handheld_distress_beacon.dm | 23 ++++++++++--------- code/game/supplyshuttle.dm | 2 +- .../cm_tech/techs/marine/tier3/cryo_spec.dm | 2 +- .../cm_tech/techs/marine/tier3/cryorine.dm | 2 +- .../chemistry_properties/prop_special.dm | 2 +- 8 files changed, 31 insertions(+), 23 deletions(-) diff --git a/code/datums/emergency_calls/emergency_call.dm b/code/datums/emergency_calls/emergency_call.dm index 69e2c02a9165..998af015d07c 100644 --- a/code/datums/emergency_calls/emergency_call.dm +++ b/code/datums/emergency_calls/emergency_call.dm @@ -96,12 +96,19 @@ return chosen_call /datum/game_mode/proc/get_specific_call(call_name, quiet_launch = FALSE, announce_incoming = TRUE, info = "") - for(var/datum/emergency_call/E in all_calls) //Loop through all potential candidates - if(E.name == call_name) - var/datum/emergency_call/em_call = new E.type() - em_call.objective_info = info - em_call.activate(quiet_launch, announce_incoming) - return + if(ispath(call_name, /datum/emergency_call)) + var/datum/emergency_call/em_call = new call_name + em_call.objective_info = info + em_call.activate(quiet_launch, announce_incoming) + return + + var/call_path = text2path(call_name) + if(ispath(call_path, /datum/emergency_call)) + var/datum/emergency_call/em_call = new call_path + em_call.objective_info = info + em_call.activate(quiet_launch, announce_incoming) + return + error("get_specific_call could not find emergency call '[call_name]'") return diff --git a/code/game/gamemodes/colonialmarines/colonialmarines.dm b/code/game/gamemodes/colonialmarines/colonialmarines.dm index 192277a250cc..ca0f34e64a7b 100644 --- a/code/game/gamemodes/colonialmarines/colonialmarines.dm +++ b/code/game/gamemodes/colonialmarines/colonialmarines.dm @@ -265,7 +265,7 @@ continue if(groundside_humans > (groundside_xenos * GROUNDSIDE_XENO_MULTIPLIER)) - SSticker.mode.get_specific_call("Xenomorphs Groundside (Forsaken)", TRUE, FALSE) + SSticker.mode.get_specific_call(/datum/emergency_call/forsaken_xenos, TRUE, FALSE) // "Xenomorphs Groundside (Forsaken)" TIMER_COOLDOWN_START(src, COOLDOWN_HIJACK_GROUND_CHECK, 1 MINUTES) diff --git a/code/game/gamemodes/colonialmarines/whiskey_outpost.dm b/code/game/gamemodes/colonialmarines/whiskey_outpost.dm index f6fbb5f4f900..d216ba762a3e 100644 --- a/code/game/gamemodes/colonialmarines/whiskey_outpost.dm +++ b/code/game/gamemodes/colonialmarines/whiskey_outpost.dm @@ -194,7 +194,7 @@ announce_xeno_wave(wave) if(xeno_wave == 7) //Wave when Marines get reinforcements! - get_specific_call("Marine Reinforcements (Squad)", FALSE, TRUE) + get_specific_call(/datum/emergency_call/wo, FALSE, TRUE) // "Marine Reinforcements (Squad)" xeno_wave = min(xeno_wave + 1, WO_MAX_WAVE) diff --git a/code/game/objects/items/handheld_distress_beacon.dm b/code/game/objects/items/handheld_distress_beacon.dm index 91d6a6aa945f..5764604c9a2f 100644 --- a/code/game/objects/items/handheld_distress_beacon.dm +++ b/code/game/objects/items/handheld_distress_beacon.dm @@ -12,9 +12,9 @@ ///Tells the user who the beacon will be sent to IC var/recipient = "the USCSS Royce" ///The name of the ERT that will be passed to get_specific_call - var/list/ert_full_name = list("Weyland-Yutani PMC (Chemical Investigation Squad)") + var/list/ert_paths = list(/datum/emergency_call/pmc/chem_retrieval) // "Weyland-Yutani PMC (Chemical Investigation Squad)" ///The clickable version that will be sent in message_admins - var/list/ert_short_name = list("SEND PMCs") + var/list/ert_short_names = list("SEND PMCs") ///Whether beacon can be used, or has already been used var/active = FALSE @@ -41,13 +41,13 @@ active = TRUE update_icon() - if(!ert_full_name || !ert_short_name || (length(ert_full_name) != length(ert_short_name))) //Make sure they are greater than 0, and both are same length + if(!ert_paths || !ert_short_names || (length(ert_paths) != length(ert_short_names))) //Make sure they are greater than 0, and both are same length to_chat(user, SPAN_BOLDWARNING("[src] is broken!")) CRASH("[src] was improperly set, and has been disabled.") //For the runtime logs var/beacon_call_buttons - for(var/current_ert_num in 1 to length(ert_full_name)) - beacon_call_buttons += "([ert_short_name[current_ert_num]]) " + for(var/current_ert_num in 1 to length(ert_paths)) + beacon_call_buttons += "([ert_short_names[current_ert_num]]) " for(var/client/admin_client in GLOB.admins) if((R_ADMIN|R_MOD) & admin_client.admin_holder.rights) @@ -62,8 +62,9 @@ beacon_type = "CMB beacon" recipient = "Anchorpoint Station" - ert_full_name = list("CMB - Patrol Team - Marshals in Distress (Friendly)", "CMB - Anchorpoint Station Colonial Marine QRF (Friendly)") - ert_short_name = list("SEND CMB", "SEND QRF") + // "CMB - Patrol Team - Marshals in Distress (Friendly)", "CMB - Anchorpoint Station Colonial Marine QRF (Friendly)" + ert_paths = list(/datum/emergency_call/cmb/alt, /datum/emergency_call/cmb/anchorpoint) + ert_short_names = list("SEND CMB", "SEND QRF") // Corporate Lawyer beacon available for 50 points at the CLs briefcase /obj/item/handheld_distress_beacon/lawyer @@ -72,8 +73,8 @@ beacon_type = "Lawyer beacon" recipient = "the Corporate Affairs Division" - ert_full_name = list("Lawyers - Corporate") - ert_short_name = list("SEND LAWYERS") + ert_paths = list(/datum/emergency_call/inspection_wy/lawyer) // "Lawyers - Corporate" + ert_short_names = list("SEND LAWYERS") // Corporate Security Bodyguard beacon available for 50 points at the CLs briefcase /obj/item/handheld_distress_beacon/bodyguard @@ -82,5 +83,5 @@ beacon_type = "Bodyguard beacon" recipient = "the Corporate Security Division" - ert_full_name = list("Weyland-Yutani Goon (Executive Bodyguard Detail)") - ert_short_name = list("SEND BODYGUARD") + ert_paths = list(/datum/emergency_call/goon/bodyguard) // "Weyland-Yutani Goon (Executive Bodyguard Detail)" + ert_short_names = list("SEND BODYGUARD") diff --git a/code/game/supplyshuttle.dm b/code/game/supplyshuttle.dm index 893071c758ed..a059d080e8ee 100644 --- a/code/game/supplyshuttle.dm +++ b/code/game/supplyshuttle.dm @@ -1277,7 +1277,7 @@ GLOBAL_DATUM_INIT(supply_controller, /datum/controller/supply, new()) /datum/controller/supply/proc/black_market_investigation() black_market_heat = -1 - SSticker.mode.get_specific_call("Inspection - Colonial Marshal Ledger Investigation Team", TRUE, TRUE) + SSticker.mode.get_specific_call(/datum/emergency_call/inspection_cmb/black_market, TRUE, TRUE) // "Inspection - Colonial Marshals Ledger Investigation Team" log_game("Black Market Inspection auto-triggered.") /obj/structure/machinery/computer/supplycomp/proc/is_buyable(datum/supply_packs/supply_pack) diff --git a/code/modules/cm_tech/techs/marine/tier3/cryo_spec.dm b/code/modules/cm_tech/techs/marine/tier3/cryo_spec.dm index 16f0f26576a3..acee904af305 100644 --- a/code/modules/cm_tech/techs/marine/tier3/cryo_spec.dm +++ b/code/modules/cm_tech/techs/marine/tier3/cryo_spec.dm @@ -21,4 +21,4 @@ /datum/tech/cryomarine/on_unlock() . = ..() - SSticker.mode.get_specific_call("Marine Cryo Reinforcement (Spec)", TRUE, FALSE) + SSticker.mode.get_specific_call(/datum/emergency_call/cryo_spec, TRUE, FALSE) // "Marine Cryo Reinforcement (Spec)" diff --git a/code/modules/cm_tech/techs/marine/tier3/cryorine.dm b/code/modules/cm_tech/techs/marine/tier3/cryorine.dm index 49b4eea8f525..404fbd07c2ae 100644 --- a/code/modules/cm_tech/techs/marine/tier3/cryorine.dm +++ b/code/modules/cm_tech/techs/marine/tier3/cryorine.dm @@ -23,4 +23,4 @@ /datum/tech/repeatable/cryomarine/on_unlock() . = ..() - SSticker.mode.get_specific_call("Marine Cryo Reinforcements (Tech)", TRUE, FALSE) + SSticker.mode.get_specific_call(/datum/emergency_call/cryo_squad/tech, TRUE, FALSE) // "Marine Cryo Reinforcements (Tech)" diff --git a/code/modules/reagents/chemistry_properties/prop_special.dm b/code/modules/reagents/chemistry_properties/prop_special.dm index 52354f0d6b01..cee75ca58c06 100644 --- a/code/modules/reagents/chemistry_properties/prop_special.dm +++ b/code/modules/reagents/chemistry_properties/prop_special.dm @@ -96,7 +96,7 @@ H.contract_disease(new /datum/disease/xeno_transformation(0),1) //This is the real reason PMCs are being sent to retrieve it. /datum/chem_property/special/DNA_Disintegrating/trigger() - SSticker.mode.get_specific_call("Weyland-Yutani Goon (Chemical Investigation Squad)", TRUE, FALSE, holder.name) + SSticker.mode.get_specific_call(/datum/emergency_call/goon/chem_retrieval, TRUE, FALSE, holder.name) // "Weyland-Yutani Goon (Chemical Investigation Squad)" GLOB.chemical_data.update_credits(10) message_admins("The research department has discovered DNA_Disintegrating in [holder.name] adding 10 bonus tech points.") var/datum/techtree/tree = GET_TREE(TREE_MARINE) From 62c46344980e7f15133fbd5a752e21544e94dea1 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:34:21 +0000 Subject: [PATCH 32/49] Automatic changelog for PR #5755 [ci skip] --- html/changelogs/AutoChangeLog-pr-5755.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5755.yml diff --git a/html/changelogs/AutoChangeLog-pr-5755.yml b/html/changelogs/AutoChangeLog-pr-5755.yml new file mode 100644 index 000000000000..f84131bf8dc7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5755.yml @@ -0,0 +1,5 @@ +author: "Drathek" +delete-after: True +changes: + - bugfix: "Fixed automatic CMB ERT caused by black market heat" + - code_imp: "get_specific_call now only accepts a path or a string of a path" \ No newline at end of file From 06eebf0a3669e92e757443f232587cd5cf8b1bf3 Mon Sep 17 00:00:00 2001 From: SabreML <57483089+SabreML@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:27:13 +0000 Subject: [PATCH 33/49] Gun action (and equip) buttons refactor (#5750) # About the pull request Refactors the 'gun action' buttons and the 'equip' button to have each have their own `/atom/movable/screen` subtype, rather than all of their functionality being done through `if()` checks in `/atom/movable/screen/clicked()`. *The 'gun action' buttons being these:* ![image](https://github.com/cmss13-devs/cmss13/assets/57483089/3c73da3e-4b99-4ad5-b66d-f01077f686d4) *And the 'equip' button being this:* ![image](https://github.com/cmss13-devs/cmss13/assets/57483089/1a60d926-605d-45b4-9a9a-5a6a05cfc657) # Explain why it's good for the game Cleaner code, and the gun buttons now show their names in the status bar when hovered over thanks to this: https://github.com/cmss13-devs/cmss13/blob/1bc0fa54e985fef991e06a85871b0a62cf15e5b2/code/modules/statusbar/statusbar.dm#L23 (That line has been there for a couple of years, but `/atom/movable/screen/gun` didn't exist until now.) # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: refactor: Refactored the 'gun action' buttons, making them show their names in the status bar when hovered over. /:cl: --------- Co-authored-by: harryob --- code/_onclick/hud/human.dm | 36 ++++------- code/_onclick/hud/screen_objects.dm | 92 ++++++++++++++++------------- 2 files changed, 63 insertions(+), 65 deletions(-) diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index 37a858d76699..b8b55b42c028 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -272,18 +272,14 @@ static_inventory += using /datum/hud/human/proc/draw_hand_equip(datum/custom_hud/ui_datum, ui_alpha, ui_color) - var/atom/movable/screen/using = new /atom/movable/screen() - using.name = "equip" - using.icon = ui_datum.ui_style_icon - using.icon_state = "act_equip" - using.screen_loc = ui_datum.ui_equip - using.layer = ABOVE_HUD_LAYER - using.plane = ABOVE_HUD_PLANE + var/atom/movable/screen/equip/equip_button = new() + equip_button.icon = ui_datum.ui_style_icon + equip_button.screen_loc = ui_datum.ui_equip if(ui_color) - using.color = ui_color + equip_button.color = ui_color if(ui_alpha) - using.alpha = ui_alpha - static_inventory += using + equip_button.alpha = ui_alpha + static_inventory += equip_button /datum/hud/human/proc/draw_oxygen(datum/custom_hud/ui_datum) oxygen_icon = new /atom/movable/screen/oxygen() @@ -312,38 +308,28 @@ infodisplay += locate_leader /datum/hud/human/proc/draw_gun_related(datum/custom_hud/ui_datum, ui_alpha) - use_attachment = new /atom/movable/screen() + use_attachment = new /atom/movable/screen/gun/attachment() use_attachment.icon = ui_datum.ui_style_icon - use_attachment.icon_state = "gun_attach" - use_attachment.name = "Activate weapon attachment" use_attachment.screen_loc = ui_datum.ui_gun_attachment static_inventory += use_attachment - toggle_raillight = new /atom/movable/screen() + toggle_raillight = new /atom/movable/screen/gun/rail_light() toggle_raillight.icon = ui_datum.ui_style_icon - toggle_raillight.icon_state = "gun_raillight" - toggle_raillight.name = "Toggle Rail Flashlight" toggle_raillight.screen_loc = ui_datum.ui_gun_railtoggle static_inventory += toggle_raillight - eject_mag = new /atom/movable/screen() + eject_mag = new /atom/movable/screen/gun/eject_magazine() eject_mag.icon = ui_datum.ui_style_icon - eject_mag.icon_state = "gun_loaded" - eject_mag.name = "Eject magazine" eject_mag.screen_loc = ui_datum.ui_gun_eject static_inventory += eject_mag - toggle_burst = new /atom/movable/screen() + toggle_burst = new /atom/movable/screen/gun/toggle_firemode() toggle_burst.icon = ui_datum.ui_style_icon - toggle_burst.icon_state = "gun_burst" - toggle_burst.name = "Toggle burst fire" toggle_burst.screen_loc = ui_datum.ui_gun_burst static_inventory += toggle_burst - unique_action = new /atom/movable/screen() + unique_action = new /atom/movable/screen/gun/unique_action() unique_action.icon = ui_datum.ui_style_icon - unique_action.icon_state = "gun_unique" - unique_action.name = "Use unique action" unique_action.screen_loc = ui_datum.ui_gun_unique static_inventory += unique_action diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index ed30ddaaec96..edf2d44a0714 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -213,54 +213,53 @@ update_icon(user) return 1 -/atom/movable/screen/clicked(mob/user, list/mods) - if(!user) - return TRUE +/atom/movable/screen/gun + /// The proc/verb which should be called on the gun. + var/gun_proc_ref - if(isobserver(user)) - return TRUE +/atom/movable/screen/gun/clicked(mob/user, list/mods) + . = ..() + if(.) + return + // If the user has a gun in their active hand, call `gun_proc_ref` on it. + var/obj/item/weapon/gun/held_item = user.get_held_item() + if(istype(held_item)) + INVOKE_ASYNC(held_item, gun_proc_ref) - switch(name) - if("equip") - if(ishuman(user)) - var/mob/living/carbon/human/human = user - human.quick_equip() - return 1 +/atom/movable/screen/gun/attachment + name = "Activate weapon attachment" + icon_state = "gun_attach" + gun_proc_ref = TYPE_VERB_REF(/obj/item/weapon/gun, activate_attachment_verb) - if("Reset Machine") - user.unset_interaction() - return 1 +/atom/movable/screen/gun/rail_light + name = "Toggle rail flashlight" + icon_state = "gun_raillight" + gun_proc_ref = TYPE_VERB_REF(/obj/item/weapon/gun, activate_rail_attachment_verb) - if("Activate weapon attachment") - var/obj/item/weapon/gun/held_item = user.get_held_item() - if(istype(held_item)) - held_item.activate_attachment_verb() - return 1 +/atom/movable/screen/gun/eject_magazine + name = "Eject magazine" + icon_state = "gun_loaded" + gun_proc_ref = TYPE_VERB_REF(/obj/item/weapon/gun, empty_mag) - if("Toggle Rail Flashlight") - var/obj/item/weapon/gun/held_item = user.get_held_item() - if(istype(held_item)) - held_item.activate_rail_attachment_verb() - return 1 +/atom/movable/screen/gun/toggle_firemode + name = "Toggle firemode" + icon_state = "gun_burst" + gun_proc_ref = TYPE_VERB_REF(/obj/item/weapon/gun, use_toggle_burst) - if("Eject magazine") - var/obj/item/weapon/gun/held_item = user.get_held_item() - if(istype(held_item)) - held_item.empty_mag() - return 1 +/atom/movable/screen/gun/unique_action + name = "Use unique action" + icon_state = "gun_unique" + gun_proc_ref = TYPE_VERB_REF(/obj/item/weapon/gun, use_unique_action) - if("Toggle burst fire") - var/obj/item/weapon/gun/held_item = user.get_held_item() - if(istype(held_item)) - held_item.use_toggle_burst() - return 1 - if("Use unique action") - var/obj/item/weapon/gun/held_item = user.get_held_item() - if(istype(held_item)) - held_item.use_unique_action() - return 1 - return 0 +/atom/movable/screen/clicked(mob/user, list/mods) + if(!user) + return TRUE + + if(isobserver(user)) + return TRUE + + return FALSE /atom/movable/screen/inventory/clicked(mob/user) @@ -587,6 +586,19 @@ vision_define = XENO_VISION_LEVEL_NO_NVG to_chat(owner, SPAN_NOTICE("Night vision mode switched to [vision_define].")) +/atom/movable/screen/equip + name = "equip" + icon_state = "act_equip" + layer = ABOVE_HUD_LAYER + plane = ABOVE_HUD_PLANE + +/atom/movable/screen/equip/clicked(mob/user) + . = ..() + if(. || !ishuman(user)) + return TRUE + var/mob/living/carbon/human/human_user = user + human_user.quick_equip() + /atom/movable/screen/bodytemp name = "body temperature" icon_state = "temp0" From 9c4fec264810ac190b486a4428ad073d87aed4cd Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:48:15 +0000 Subject: [PATCH 34/49] Automatic changelog for PR #5750 [ci skip] --- html/changelogs/AutoChangeLog-pr-5750.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5750.yml diff --git a/html/changelogs/AutoChangeLog-pr-5750.yml b/html/changelogs/AutoChangeLog-pr-5750.yml new file mode 100644 index 000000000000..45c4e629436c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5750.yml @@ -0,0 +1,4 @@ +author: "SabreML" +delete-after: True +changes: + - refactor: "Refactored the 'gun action' buttons, making them show their names in the status bar when hovered over." \ No newline at end of file From 139507d02857045b4513f941649cc62658887be3 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Thu, 22 Feb 2024 01:06:38 +0000 Subject: [PATCH 35/49] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-5433.yml | 4 --- html/changelogs/AutoChangeLog-pr-5577.yml | 7 ----- html/changelogs/AutoChangeLog-pr-5609.yml | 5 ---- html/changelogs/AutoChangeLog-pr-5735.yml | 4 --- html/changelogs/AutoChangeLog-pr-5750.yml | 4 --- html/changelogs/AutoChangeLog-pr-5751.yml | 5 ---- html/changelogs/AutoChangeLog-pr-5755.yml | 5 ---- html/changelogs/AutoChangeLog-pr-5757.yml | 4 --- html/changelogs/AutoChangeLog-pr-5758.yml | 4 --- html/changelogs/AutoChangeLog-pr-5762.yml | 4 --- html/changelogs/AutoChangeLog-pr-5764.yml | 4 --- html/changelogs/archive/2024-02.yml | 35 +++++++++++++++++++++++ 12 files changed, 35 insertions(+), 50 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5433.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5577.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5609.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5735.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5750.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5751.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5755.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5757.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5758.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5762.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-5764.yml diff --git a/html/changelogs/AutoChangeLog-pr-5433.yml b/html/changelogs/AutoChangeLog-pr-5433.yml deleted file mode 100644 index 89e4d482185e..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5433.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "TheGamerdk" -delete-after: True -changes: - - rscadd: "CIC can now view helmet cameras of Command Staff. Literally 1984" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5577.yml b/html/changelogs/AutoChangeLog-pr-5577.yml deleted file mode 100644 index ea5029462ec5..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5577.yml +++ /dev/null @@ -1,7 +0,0 @@ -author: "LaylaMcC" -delete-after: True -changes: - - rscadd: "Increased levels of acidic blood and carpotoxin in xenomeat and carp fillets." - - rscdel: "Xenomeat and carp fillet-based recipes no longer add additional acidic blood or carpotoxin." - - qol: "Master level domestic skill allows the use of the food processor to remove acidic blood and carpotoxin from xenomeat and carp fillets." - - bugfix: "Turning named pieces of human meat into meatballs will retain the human's name." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5609.yml b/html/changelogs/AutoChangeLog-pr-5609.yml deleted file mode 100644 index 4ad1eb6ed5db..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5609.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Huffie56" -delete-after: True -changes: - - refactor: "refactored files with the aim of making fixing barricade with a welder more constant." - - bugfix: "being unable to repair more than one metal barricade at the time." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5735.yml b/html/changelogs/AutoChangeLog-pr-5735.yml deleted file mode 100644 index 5f383f47d5fb..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5735.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "iloveloopers" -delete-after: True -changes: - - qol: "Autolathes are now significantly faster." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5750.yml b/html/changelogs/AutoChangeLog-pr-5750.yml deleted file mode 100644 index 45c4e629436c..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5750.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SabreML" -delete-after: True -changes: - - refactor: "Refactored the 'gun action' buttons, making them show their names in the status bar when hovered over." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5751.yml b/html/changelogs/AutoChangeLog-pr-5751.yml deleted file mode 100644 index a2f845c0527e..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5751.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "SabreML" -delete-after: True -changes: - - rscadd: "Added 'observe' functionality to Xenomorphs, allowing observers to view the target's UI." - - rscadd: "Made observing a player also show their action buttons." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5755.yml b/html/changelogs/AutoChangeLog-pr-5755.yml deleted file mode 100644 index f84131bf8dc7..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5755.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - bugfix: "Fixed automatic CMB ERT caused by black market heat" - - code_imp: "get_specific_call now only accepts a path or a string of a path" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5757.yml b/html/changelogs/AutoChangeLog-pr-5757.yml deleted file mode 100644 index d227d38fd6c8..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5757.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "BadAtThisGame302" -delete-after: True -changes: - - rscadd: "Implemented a probability of playing a static-filled transmission as the distress received response." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5758.yml b/html/changelogs/AutoChangeLog-pr-5758.yml deleted file mode 100644 index faa5452042bd..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5758.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Kaga" -delete-after: True -changes: - - bugfix: "Fixed a bug that broke ID-Lockable items in presets. Rejoice, PMC Snipers, pre-equipped USCM Specs, UPP Commandos, and WY Deathsquads." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5762.yml b/html/changelogs/AutoChangeLog-pr-5762.yml deleted file mode 100644 index e91fb28814b9..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5762.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - spellcheck: "Reworded message when a facehugger's temporary mute ends to not mention hivemind" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-5764.yml b/html/changelogs/AutoChangeLog-pr-5764.yml deleted file mode 100644 index 1239985de3b2..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5764.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - ui: "Fix broken admin Medals Panel" \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index 35e063bd1bdc..f54127866f02 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -269,3 +269,38 @@ - bugfix: Forsaken and Feral xenos can now (correctly) use hive-status - spellcheck: fixed a case where we/our wouldn't be used when attempting to access hive status with no queen +2024-02-22: + BadAtThisGame302: + - rscadd: Implemented a probability of playing a static-filled transmission as the + distress received response. + Drathek: + - bugfix: Fixed automatic CMB ERT caused by black market heat + - code_imp: get_specific_call now only accepts a path or a string of a path + - ui: Fix broken admin Medals Panel + - spellcheck: Reworded message when a facehugger's temporary mute ends to not mention + hivemind + Huffie56: + - refactor: refactored files with the aim of making fixing barricade with a welder + more constant. + - bugfix: being unable to repair more than one metal barricade at the time. + Kaga: + - bugfix: Fixed a bug that broke ID-Lockable items in presets. Rejoice, PMC Snipers, + pre-equipped USCM Specs, UPP Commandos, and WY Deathsquads. + LaylaMcC: + - rscadd: Increased levels of acidic blood and carpotoxin in xenomeat and carp fillets. + - rscdel: Xenomeat and carp fillet-based recipes no longer add additional acidic + blood or carpotoxin. + - qol: Master level domestic skill allows the use of the food processor to remove + acidic blood and carpotoxin from xenomeat and carp fillets. + - bugfix: Turning named pieces of human meat into meatballs will retain the human's + name. + SabreML: + - rscadd: Added 'observe' functionality to Xenomorphs, allowing observers to view + the target's UI. + - rscadd: Made observing a player also show their action buttons. + - refactor: Refactored the 'gun action' buttons, making them show their names in + the status bar when hovered over. + TheGamerdk: + - rscadd: CIC can now view helmet cameras of Command Staff. Literally 1984 + iloveloopers: + - qol: Autolathes are now significantly faster. From dc412e4679dea8410a58d18cc60913e07edf84ec Mon Sep 17 00:00:00 2001 From: InsaneRed <47158596+InsaneRed@users.noreply.github.com> Date: Thu, 22 Feb 2024 12:17:43 +0300 Subject: [PATCH 36/49] Abomination rework and predator plasma rifle buff. (#5432) # About the pull request This pr intends to catch up predalien towards current gameplay by reworking a few abilities and adding new ones. # Explain why it's good for the game Currently the predalien has been untouched (practically) for over 3-4 years now, while being rare and decent enough to hold its own against current year marines and predators, its highly outdated. It's main power being "the gib" which is not fun for marines to be instantly removed from the game as most of the time you will be accompined by a queen and the entire hive. its not being fun being comboed into a queen screech + gib As their natural counter, the predators are currently lacking in a strong way to counter it, i believe the Plasma Rifle thats only available when an abomination is present is very weak, underpowered and generaly not that good. I can go into deeper details if anyone wants me to. So my natural response was to make it set people on fire because generally the predalien will be guarded by an entire hive. This will make it so that atleast the xenomorph hit, will have to back off allowing the predator to breathe. I havent touched the recharge rate because i think this incin bullet will be powerful enough to compensate for the low recharge rate, and only 16 shots (you need 14 dead on shots to kill an abomination, not counting its movement, phermones, and the entire hive guarding it.) below are all the changes ive decided to make; "Feral Frenzy" is a mashup of the old gut ability, it now has a toggle that lets you choose between AOE/SINGLE TARGET. I think this is a defining factor in predalien reworks, because currently it is too focused on fighting predators and not enough on marines. This will help you fend off against a marine group by letting you become devastating against grouped up marines. Or chosing to catch one by switching to single target since it has a root. (Also retaining its ability to fight off predators). "Feral Rush"; Feral rush incrases your armor and speed by a certain number, i wanted to add this instead of the useless pounce as i felt like the pounce was basically not providing you with anything other then just a distance closer, and it didnt fit in with the new gutting abilities, this way you can speed up, armor up and then go inside the marine force and do your aoe ability and get back out. This will ensure that you arent useless while going in since without the armor and speed, you will melt by the time you go in marine numbers, (also remember the AOE gut has a windup of 2 seconds.), The armor is removed 6 seconds after the slow (the speed lasts 3 seconds, so you're just armored for an extra 3 seconds) as to have that extra armor while retreating from more open places. "Stomp" ; the reason ive decided to remove this ability is it felt way out of place for this kit and just simply wasnt good enough (in my opinion) to being there although a slow or aoe root might be needed if this gets approval enough for gametesting. "Frenzy Smash" ; Grabs somebody by the leg and smashes them to the ground, this honestly just felt like a cool ability to add, it lets you keep somebody in place but also keep them moving, this is mostly a flair ability but still VERY deadly, may be buffed to make preds slower for a duration. deals 20 damage and scales with kills THIS IS ALL UP FOR DEBATE, AND I WOULD LIKE TO WATCH THIS ALL INGAME VIA ABOM EVENTS BY ADMINS IF THATS POSSIBLE. # Testing Photographs and Procedure
Screenshots & Videos https://github.com/cmss13-devs/cmss13/assets/47158596/f28a5048-701f-4b9d-a2bf-dd5dbc6d08be https://github.com/cmss13-devs/cmss13/assets/47158596/53da1e18-1a4a-49de-a73d-1ab7f1abb1d8 https://github.com/cmss13-devs/cmss13/assets/47158596/69eeb0e9-cbe6-4712-a15a-b599dbc56109
# Changelog :cl: add: Added a new ability to the predalien "Feral Rush" that increases it's armor and speed for a short duration. add: Added two new abilities to the predalien "Feral Frenzy" and a toggle. The predalien can now switch between a single target GUT and an AOE one, both of which has damage that scale with your kills. del: Removed the predaliens gib ability del: Removed the predaliens "pounce" ability del: Removed the predaliens "Ground Smash" ability balance: Predalien no longer has plasma costs, or plasma. balance: The Plasma Rifle, which is ONLY used to hunt abominations now has a higher ROF and has incin bullets. spellcheck: Re-wrote the predalien text to be more up to date and remove missinformation qol: Everyone can see how many kills the predalien has by examining it. add: Added a "Feral Smash" ability that lets you grab somebody and smash them to the ground, this scales with kills and is a devastating attack. balance: Removed screenshake from predalien's screech balance: Predalien removes fire stacks faster. fix: Girders are now slashable by very_sharp_claws instead of just having a queen chack /:cl: --------- Co-authored-by: InsaneRed Co-authored-by: Birdtalon Co-authored-by: Drathek <76988376+Drulikar@users.noreply.github.com> --- code/__DEFINES/xeno.dm | 3 + code/datums/ammo/energy.dm | 7 + code/modules/cm_preds/yaut_weapons.dm | 2 +- .../predalien/predalien_abilities.dm | 80 +++-- .../abilities/predalien/predalien_macros.dm | 35 +++ .../abilities/predalien/predalien_powers.dm | 283 +++++++++++++----- .../living/carbon/xenomorph/attack_alien.dm | 2 +- .../carbon/xenomorph/castes/Predalien.dm | 80 ++--- colonialmarines.dme | 1 + 9 files changed, 329 insertions(+), 164 deletions(-) create mode 100644 code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_macros.dm diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno.dm index 6f6e4eef7c20..fb9d6bfb4faf 100644 --- a/code/__DEFINES/xeno.dm +++ b/code/__DEFINES/xeno.dm @@ -62,6 +62,9 @@ #define ACID_SPRAY_LINE 0 #define ACID_SPRAY_CONE 1 +/// Defines for Abomination ability /datum/action/xeno_action/activable/feralfrenzy +#define SINGLETARGETGUT 0 +#define AOETARGETGUT 1 #define WARDEN_HEAL_SHIELD 0 #define WARDEN_HEAL_HP 1 diff --git a/code/datums/ammo/energy.dm b/code/datums/ammo/energy.dm index 16cbab378438..1f48806d2d52 100644 --- a/code/datums/ammo/energy.dm +++ b/code/datums/ammo/energy.dm @@ -230,3 +230,10 @@ var/mob/living/carbon/xenomorph/xeno = hit_mob xeno.apply_damage(damage * 0.75, BURN) xeno.interference = 30 + +/datum/ammo/energy/yautja/rifle/bolt/set_bullet_traits() + . = ..() + LAZYADD(traits_to_give, list( + BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_incendiary) + )) + diff --git a/code/modules/cm_preds/yaut_weapons.dm b/code/modules/cm_preds/yaut_weapons.dm index 9cb8a8bef3fc..34233f2ee9cd 100644 --- a/code/modules/cm_preds/yaut_weapons.dm +++ b/code/modules/cm_preds/yaut_weapons.dm @@ -938,7 +938,7 @@ /obj/item/weapon/gun/energy/yautja/plasmarifle/set_gun_config_values() ..() - set_fire_delay(FIRE_DELAY_TIER_6*2) + set_fire_delay(FIRE_DELAY_TIER_4*2) accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_10 accuracy_mult_unwielded = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_10 scatter = SCATTER_AMOUNT_TIER_6 diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_abilities.dm index aa98f063b8d5..b1358e30c26b 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_abilities.dm @@ -1,14 +1,22 @@ -/datum/action/xeno_action/activable/pounce/predalien - name = "Leap" + +/datum/action/xeno_action/onclick/feralrush + name = "Feral Rush" + action_icon_state = "charge_spit" + ability_name = "toughen up" + macro_path = /datum/action/xeno_action/verb/verb_feralrush ability_primacy = XENO_PRIMARY_ACTION_1 + action_type = XENO_ACTION_ACTIVATE + xeno_cooldown = 12 SECONDS + + // Config + var/speed_duration = 3 SECONDS + var/armor_duration = 6 SECONDS + var/speed_buff_amount = 0.8 // Go from shit slow to kindafast + var/armor_buff_amount = 10 // hopefully-minor buff so they can close the distance - knockdown = FALSE + var/speed_buff = FALSE + var/armor_buff = FALSE - distance = 5 - knockdown = FALSE // Should we knock down the target? - slash = FALSE // Do we slash upon reception? - freeze_self = FALSE // Should we freeze ourselves after the lunge? - should_destroy_objects = TRUE // Only used for ravager charge /datum/action/xeno_action/onclick/predalien_roar name = "Roar" @@ -16,37 +24,53 @@ ability_name = "roar" action_type = XENO_ACTION_CLICK ability_primacy = XENO_PRIMARY_ACTION_2 + macro_path = /datum/action/xeno_action/verb/verb_predalien_roar xeno_cooldown = 25 SECONDS - plasma_cost = 50 - var/predalien_roar = list("sound/voice/predalien_roar.ogg") var/bonus_damage_scale = 2.5 var/bonus_speed_scale = 0.05 -/datum/action/xeno_action/onclick/smash - name = "Smash" - action_icon_state = "stomp" - ability_name = "smash" +/datum/action/xeno_action/activable/feral_smash + name = "Feral Smash" + ability_name = "Feral Smash" + action_icon_state = "lunge" action_type = XENO_ACTION_CLICK + macro_path = /datum/action/xeno_action/verb/feral_smash ability_primacy = XENO_PRIMARY_ACTION_3 xeno_cooldown = 20 SECONDS - plasma_cost = 80 - - var/freeze_duration = 1.5 SECONDS - var/activation_delay = 1 SECONDS - var/smash_sounds = list('sound/effects/alien_footstep_charge1.ogg', 'sound/effects/alien_footstep_charge2.ogg', 'sound/effects/alien_footstep_charge3.ogg') + // Configurables + var/grab_range = 4 + var/click_miss_cooldown = 15 + var/twitch_message_cooldown = 0 //apparently this is necessary for a tiny code that makes the lunge message on cooldown not be spammable, doesn't need to be big so 5 will do. + var/smash_damage = 20 + var/smash_scale = 10 + var/stun_duration = 2 SECONDS -/datum/action/xeno_action/activable/devastate - name = "Devastate" - action_icon_state = "gut" - ability_name = "devastate" +/datum/action/xeno_action/activable/feralfrenzy + name = "Feral Frenzy" + action_icon_state = "rav_eviscerate" + ability_name = "Feral Frenzy" action_type = XENO_ACTION_CLICK ability_primacy = XENO_PRIMARY_ACTION_4 - xeno_cooldown = 20 SECONDS - plasma_cost = 110 - - var/activation_delay = 1 SECONDS + macro_path = /datum/action/xeno_action/verb/verb_feralfrenzy + xeno_cooldown = 15 SECONDS + //AOE + var/activation_delay_aoe = 1 SECONDS + var/base_damage_aoe = 15 + var/damage_scale_aoe = 10 + //SINGLE TARGET + var/activation_delay = 0.5 SECONDS var/base_damage = 25 - var/damage_scale = 10 // How much it scales by every kill + var/damage_scale = 10 + var/targeting = SINGLETARGETGUT + /// The orange used for a AOETARGETGUT + var/range = 2 + +/datum/action/xeno_action/onclick/toggle_gut_targeting + name = "Toggle Gutting Type" + action_icon_state = "gut" // starting targetting is SINGLETARGETGUT + macro_path = /datum/action/xeno_action/verb/verb_toggle_gut_targeting + action_type = XENO_ACTION_ACTIVATE + ability_primacy = XENO_PRIMARY_ACTION_5 diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_macros.dm b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_macros.dm new file mode 100644 index 000000000000..450a0ac67745 --- /dev/null +++ b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_macros.dm @@ -0,0 +1,35 @@ +/datum/action/xeno_action/verb/verb_feralfrenzy() + set category = "Alien" + set name = "Feral Frenzy" + set hidden = TRUE + var/action_name = "Feral Frenzy" + handle_xeno_macro(src, action_name) + +/datum/action/xeno_action/verb/verb_toggle_gut_targeting() + set category = "Alien" + set name = "Toggle Gutting Type" + set hidden = TRUE + var/action_name = "Toggle Gutting Type" + handle_xeno_macro(src, action_name) + + +/datum/action/xeno_action/verb/verb_feralrush() + set category = "Alien" + set name = "Feral Rush" + set hidden = TRUE + var/action_name = "Feral Rush" + handle_xeno_macro(src, action_name) + +/datum/action/xeno_action/verb/verb_predalien_roar() + set category = "Alien" + set name = "Predalien Roar" + set hidden = TRUE + var/action_name = "Roar" + handle_xeno_macro(src, action_name) + +/datum/action/xeno_action/verb/feral_smash() + set category = "Alien" + set name = "Feral Smash" + set hidden = TRUE + var/action_name = "Feral Smash" + handle_xeno_macro(src, action_name) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm index cdf0d3840084..6e6fef86a2f4 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm @@ -1,13 +1,10 @@ -/datum/action/xeno_action/activable/pounce/predalien/pre_pounce_effects() - playsound(owner, 'sound/voice/predalien_pounce.ogg', 75, 0, status = 0) - /datum/action/xeno_action/onclick/predalien_roar/use_ability(atom/target) var/mob/living/carbon/xenomorph/xeno = owner - if (!action_cooldown_check()) + if(!action_cooldown_check()) return - if (!xeno.check_state()) + if(!xeno.check_state()) return if(!check_and_use_plasma_owner()) @@ -34,124 +31,258 @@ continue new /datum/effects/xeno_buff(carbon, xeno, ttl = (0.25 SECONDS * behavior.kills + 3 SECONDS), bonus_damage = bonus_damage_scale * behavior.kills, bonus_speed = (bonus_speed_scale * behavior.kills)) - - for(var/mob/M in view(xeno)) - if(M && M.client) - shake_camera(M, 10, 1) - apply_cooldown() return ..() -/datum/action/xeno_action/onclick/smash/use_ability(atom/target) +/datum/action/xeno_action/activable/feralfrenzy/use_ability(atom/target) var/mob/living/carbon/xenomorph/xeno = owner - - if (!action_cooldown_check()) + if(!action_cooldown_check() || xeno.action_busy) + return + if(!xeno.check_state()) return - if (!xeno.check_state()) + var/datum/behavior_delegate/predalien_base/predalienbehavior = xeno.behavior_delegate + if(!istype(predalienbehavior)) return + if(targeting == AOETARGETGUT) + xeno.visible_message(SPAN_XENOHIGHDANGER("[xeno] begins digging in for a massive strike!"), SPAN_XENOHIGHDANGER("We begin digging in for a massive strike!")) + ADD_TRAIT(xeno, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Eviscerate")) + xeno.anchored = TRUE + if (do_after(xeno, (activation_delay_aoe), INTERRUPT_ALL | BEHAVIOR_IMMOBILE, BUSY_ICON_HOSTILE)) + xeno.emote("roar") + xeno.spin_circle() + + for (var/mob/living/carbon/carbon in orange(xeno, range)) + if(!isliving(carbon) || xeno.can_not_harm(carbon)) + continue + + if (carbon.stat == DEAD) + continue + + if(!check_clear_path_to_target(xeno, carbon)) + continue + + xeno.visible_message(SPAN_XENOHIGHDANGER("[xeno] rips open the guts of [carbon]!"), SPAN_XENOHIGHDANGER("We rip open the guts of [carbon]!")) + carbon.spawn_gibs() + xeno.animation_attack_on(carbon) + xeno.spin_circle() + xeno.flick_attack_overlay(carbon, "tail") + playsound(get_turf(carbon), 'sound/effects/gibbed.ogg', 30, 1) + carbon.apply_effect(get_xeno_stun_duration(carbon, 0.5), WEAKEN) + playsound(get_turf(carbon), "alien_claw_flesh", 30, 1) + carbon.apply_armoured_damage(get_xeno_damage_slash(carbon, base_damage_aoe + damage_scale_aoe * predalienbehavior.kills), ARMOR_MELEE, BRUTE, "chest", 20) + playsound(owner, 'sound/voice/predalien_death.ogg', 75, 0, status = 0) + REMOVE_TRAIT(xeno, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Eviscerate")) + xeno.anchored = FALSE + apply_cooldown() + return ..() - var/datum/behavior_delegate/predalien_base/behavior = xeno.behavior_delegate - if(!istype(behavior)) + //single target checks + if(xeno.can_not_harm(target)) + to_chat(xeno, SPAN_XENOWARNING("We must target a hostile!")) return - if(!check_plasma_owner()) + if(!isliving(target)) return - if(!do_after(xeno, activation_delay, INTERRUPT_ALL, BUSY_ICON_GENERIC)) - to_chat(xeno, "Keep still whilst trying to smash into the ground") + if(get_dist_sqrd(target, xeno) > 2) + to_chat(xeno, SPAN_XENOWARNING("[target] is too far away!")) + return - var/real_cooldown = xeno_cooldown + var/mob/living/carbon/carbon = target - xeno_cooldown = 3 SECONDS + if(carbon.stat == DEAD) + to_chat(xeno, SPAN_XENOWARNING("[carbon] is dead, why would we want to touch them?")) + return + if(targeting == SINGLETARGETGUT) // single target + ADD_TRAIT(carbon, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Devastate")) apply_cooldown() - xeno_cooldown = real_cooldown + + ADD_TRAIT(xeno, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Devastate")) + xeno.anchored = TRUE + + if(do_after(xeno, activation_delay, INTERRUPT_ALL | BEHAVIOR_IMMOBILE, BUSY_ICON_HOSTILE)) + xeno.visible_message(SPAN_XENOHIGHDANGER("[xeno] rips open the guts of [carbon]!"), SPAN_XENOHIGHDANGER("We rapidly slice into [carbon]!")) + carbon.spawn_gibs() + playsound(get_turf(carbon), 'sound/effects/gibbed.ogg', 50, 1) + carbon.apply_effect(get_xeno_stun_duration(carbon, 0.5), WEAKEN) + carbon.apply_armoured_damage(get_xeno_damage_slash(carbon, base_damage + damage_scale * predalienbehavior.kills), ARMOR_MELEE, BRUTE, "chest", 20) + + xeno.animation_attack_on(carbon) + xeno.spin_circle() + xeno.flick_attack_overlay(carbon, "tail") + + playsound(owner, 'sound/voice/predalien_growl.ogg', 50, 0, status = 0) + + REMOVE_TRAIT(xeno, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Devastate")) + xeno.anchored = FALSE + unroot_human(carbon, TRAIT_SOURCE_ABILITY("Devastate")) + + return ..() + + +/datum/action/xeno_action/onclick/feralrush/use_ability(atom/A) + var/mob/living/carbon/xenomorph/predatoralien = owner + + if(!action_cooldown_check()) + return + + if(!istype(predatoralien) || !predatoralien.check_state()) + return + + if(armor_buff && speed_buff) + to_chat(predatoralien, SPAN_XENOHIGHDANGER("We cannot stack this!")) return if(!check_and_use_plasma_owner()) return - playsound(xeno.loc, pick(smash_sounds), 50, 0, status = 0) - xeno.visible_message(SPAN_XENOHIGHDANGER("[xeno] smashes into the ground!")) - xeno.create_stomp() + addtimer(CALLBACK(src, PROC_REF(remove_rush_effects)), speed_duration) + addtimer(CALLBACK(src, PROC_REF(remove_armor_effects)), armor_duration) // calculate armor and speed differently, so it's a bit more armored while trying to get out + predatoralien.add_filter("predalien_toughen", 1, list("type" = "outline", "color" = "#421313", "size" = 1)) + to_chat(predatoralien, SPAN_XENOWARNING("We feel our muscles tense as our speed and armor increase!")) + speed_buff = TRUE + armor_buff = TRUE + predatoralien.speed_modifier -= speed_buff_amount + predatoralien.armor_modifier += armor_buff_amount + predatoralien.recalculate_speed() + predatoralien.recalculate_armor() + playsound(predatoralien, 'sound/voice/predalien_growl.ogg', 75, 0, status = 0) + apply_cooldown() - for(var/mob/living/carbon/carbon in oview(round(behavior.kills * 0.5 + 2), xeno)) - if(!xeno.can_not_harm(carbon) && carbon.stat != DEAD) - ADD_TRAIT(carbon, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Smash")) - if (ishuman(carbon)) - var/mob/living/carbon/human/human = carbon - human.update_xeno_hostile_hud() +/datum/action/xeno_action/onclick/feralrush/proc/remove_rush_effects() + SIGNAL_HANDLER + var/mob/living/carbon/xenomorph/predatoralien = owner + if(speed_buff == TRUE) + to_chat(predatoralien, SPAN_XENOWARNING("Our muscles relax as we feel our speed wane.")) + predatoralien.remove_filter("predalien_toughen") + predatoralien.speed_modifier += speed_buff_amount + predatoralien.recalculate_speed() + speed_buff = FALSE - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(unroot_human), carbon, TRAIT_SOURCE_ABILITY("Smash")), get_xeno_stun_duration(carbon, freeze_duration)) +/datum/action/xeno_action/onclick/feralrush/proc/remove_armor_effects() + SIGNAL_HANDLER + var/mob/living/carbon/xenomorph/predatoralien = owner + if(armor_buff) + to_chat(predatoralien, SPAN_XENOWARNING("We are no longer armored.")) + predatoralien.armor_modifier -= armor_buff_amount + predatoralien.recalculate_armor() + armor_buff = FALSE - for(var/mob/M in view(xeno)) - if(M && M.client) - shake_camera(M, 0.2 SECONDS, 1) - apply_cooldown() - return ..() +/datum/action/xeno_action/onclick/toggle_gut_targeting/use_ability(atom/A) -/datum/action/xeno_action/activable/devastate/use_ability(atom/target) var/mob/living/carbon/xenomorph/xeno = owner + var/action_icon_result - if (!action_cooldown_check()) + if(!xeno.check_state()) return - if (!xeno.check_state()) + var/datum/action/xeno_action/activable/feralfrenzy/guttype = get_xeno_action_by_type(xeno, /datum/action/xeno_action/activable/feralfrenzy) + if(!guttype) return - if (!isxeno_human(target) || xeno.can_not_harm(target)) - to_chat(xeno, SPAN_XENOWARNING("We must target a hostile!")) - return + if(guttype.targeting == SINGLETARGETGUT) + action_icon_result = "rav_scissor_cut" + guttype.targeting = AOETARGETGUT + to_chat(xeno, SPAN_XENOWARNING("We will now attack everyone around us during a Feral Frenzy.")) + else + action_icon_result = "gut" + guttype.targeting = SINGLETARGETGUT + to_chat(xeno, SPAN_XENOWARNING("We will now focus our Feral Frenzy on one person!")) + + button.overlays.Cut() + button.overlays += image('icons/mob/hud/actions_xeno.dmi', button, action_icon_result) + return ..() - if (get_dist_sqrd(target, xeno) > 2) - to_chat(xeno, SPAN_XENOWARNING("[target] is too far away!")) - return +/datum/action/xeno_action/activable/feral_smash/use_ability(atom/affected_atom) + var/mob/living/carbon/xenomorph/predalien_smash = owner + var/datum/behavior_delegate/predalien_base/predalienbehavior = predalien_smash.behavior_delegate - var/mob/living/carbon/carbon = target + if(!action_cooldown_check()) + if(twitch_message_cooldown < world.time ) + predalien_smash.visible_message(SPAN_XENOWARNING("[predalien_smash]'s muscles twitch."), SPAN_XENOWARNING("Our claws twitch as we try to grab onto the target but lack the strength. Wait a moment to try again.")) + twitch_message_cooldown = world.time + 5 SECONDS + return //this gives a little feedback on why your lunge didn't hit other than the lunge button going grey. Plus, it might spook marines that almost got lunged if they know why the message appeared, and extra spookiness is always good. - if (carbon.stat == DEAD) - to_chat(xeno, SPAN_XENOWARNING("[carbon] is dead, why would we want to touch them?")) + if(!affected_atom) return - var/datum/behavior_delegate/predalien_base/behavior = xeno.behavior_delegate - if(!istype(behavior)) + if(!isturf(predalien_smash.loc)) + to_chat(predalien_smash, SPAN_XENOWARNING("We can't lunge from here!")) return - if (!check_and_use_plasma_owner()) + if(!predalien_smash.check_state() || predalien_smash.agility) return - ADD_TRAIT(carbon, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Devastate")) - - if (ishuman(carbon)) - var/mob/living/carbon/human/human = carbon - human.update_xeno_hostile_hud() - - apply_cooldown() - - ADD_TRAIT(xeno, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Devastate")) - xeno.anchored = TRUE + if(predalien_smash.can_not_harm(affected_atom) || !ismob(affected_atom)) + apply_cooldown_override(click_miss_cooldown) + return - if (do_after(xeno, activation_delay, INTERRUPT_ALL | BEHAVIOR_IMMOBILE, BUSY_ICON_HOSTILE)) - xeno.visible_message(SPAN_XENOHIGHDANGER("[xeno] rips open the guts of [carbon]!"), SPAN_XENOHIGHDANGER("We rip open the guts of [carbon]!")) - carbon.spawn_gibs() - playsound(get_turf(carbon), 'sound/effects/gibbed.ogg', 75, 1) - carbon.apply_effect(get_xeno_stun_duration(carbon, 0.5), WEAKEN) - carbon.apply_armoured_damage(get_xeno_damage_slash(carbon, base_damage + damage_scale * behavior.kills), ARMOR_MELEE, BRUTE, "chest", 20) - xeno.animation_attack_on(carbon) - xeno.spin_circle() - xeno.flick_attack_overlay(carbon, "tail") + var/mob/living/carbon/carbon = affected_atom + if(carbon.stat == DEAD) + return - playsound(owner, 'sound/voice/predalien_growl.ogg', 75, 0, status = 0) + if(!isliving(affected_atom)) + return - REMOVE_TRAIT(xeno, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Devastate")) - xeno.anchored = FALSE - unroot_human(carbon, TRAIT_SOURCE_ABILITY("Devastate")) + if(!check_and_use_plasma_owner()) + return - xeno.visible_message(SPAN_XENODANGER("[xeno] rapidly slices into [carbon]!")) + apply_cooldown() + predalien_smash.throw_atom(get_step_towards(affected_atom, predalien_smash), grab_range, SPEED_FAST, predalien_smash) + + if(predalien_smash.Adjacent(carbon) && predalien_smash.start_pulling(carbon, TRUE)) + playsound(carbon.pulledby, 'sound/voice/predalien_growl.ogg', 75, 0, status = 0) // bang and roar for dramatic effect + playsound(carbon, 'sound/effects/bang.ogg', 25, 0) + animate(carbon, pixel_y = carbon.pixel_y + 32, time = 4, easing = SINE_EASING) + sleep(4) + playsound(carbon, 'sound/effects/bang.ogg', 25, 0) + playsound(carbon,"slam", 50, 1) + animate(carbon, pixel_y = 0, time = 4, easing = BOUNCE_EASING) //animates the smash + carbon.apply_armoured_damage(get_xeno_damage_slash(carbon, smash_damage + smash_scale * predalienbehavior.kills), ARMOR_MELEE, BRUTE, "chest", 20) + else + predalien_smash.visible_message(SPAN_XENOWARNING("[predalien_smash]'s claws twitch."), SPAN_XENOWARNING("We couldn't grab our target. Wait a moment to try again.")) + + return TRUE + +/mob/living/carbon/xenomorph/predalien/stop_pulling() + if(isliving(pulling) && smashing) + smashing = FALSE // To avoid extreme cases of stopping a lunge then quickly pulling and stopping to pull someone else + var/mob/living/smashed = pulling + smashed.set_effect(0, STUN) + smashed.set_effect(0, WEAKEN) return ..() + +/mob/living/carbon/xenomorph/predalien/start_pulling(atom/movable/movable_atom, feral_smash) + if(!check_state()) + return FALSE + + if(!isliving(movable_atom)) + return FALSE + var/mob/living/living_mob = movable_atom + var/should_neckgrab = !(src.can_not_harm(living_mob)) && feral_smash + + + . = ..(living_mob, feral_smash, should_neckgrab) + + if(.) //successful pull + if(isxeno(living_mob)) + var/mob/living/carbon/xenomorph/xeno = living_mob + if(xeno.tier >= 2) // Tier 2 castes or higher immune to warrior grab stuns + return + + if(should_neckgrab && living_mob.mob_size < MOB_SIZE_BIG) + visible_message(SPAN_XENOWARNING("[src] grabs [living_mob] by the back of their leg and slams them onto the ground!"), \ + SPAN_XENOWARNING("We grab [living_mob] by the back of their leg and slam them onto the ground!")) // more flair + smashing = TRUE + living_mob.drop_held_items() + var/duration = get_xeno_stun_duration(living_mob, 1) + living_mob.KnockDown(duration) + living_mob.Stun(duration) + addtimer(VARSET_CALLBACK(src, smashing, FALSE), duration) diff --git a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm index 2aefff19c684..9f42f8ae8928 100644 --- a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm +++ b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm @@ -911,7 +911,7 @@ return XENO_ATTACK_ACTION /obj/structure/girder/attack_alien(mob/living/carbon/xenomorph/M) - if((M.caste && M.caste.tier < 2 && !isqueen(M)) || unacidable) + if((M.caste && M.caste.tier < 2 && M.claw_type < CLAW_TYPE_VERY_SHARP) || unacidable) to_chat(M, SPAN_WARNING("Our claws aren't sharp enough to damage [src].")) return XENO_NO_DELAY_ACTION M.animation_attack_on(src) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm index ebae6f56830f..841675234e63 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Predalien.dm @@ -6,8 +6,7 @@ melee_damage_upper = XENO_DAMAGE_TIER_5 melee_vehicle_damage = XENO_DAMAGE_TIER_5 max_health = XENO_HEALTH_TIER_9 - plasma_gain = XENO_PLASMA_GAIN_TIER_9 - plasma_max = XENO_PLASMA_TIER_3 + plasma_max = XENO_NO_PLASMA xeno_explosion_resistance = XENO_EXPLOSIVE_ARMOR_TIER_10 armor_deflection = XENO_ARMOR_TIER_3 evasion = XENO_EVASION_NONE @@ -41,6 +40,7 @@ speaking_noise = 'sound/voice/predalien_click.ogg' plasma_types = list(PLASMA_CATECHOLAMINE) faction = FACTION_PREDALIEN + claw_type = CLAW_TYPE_VERY_SHARP wall_smash = TRUE hardcore = FALSE pixel_x = -16 @@ -56,18 +56,19 @@ /datum/action/xeno_action/onclick/regurgitate, /datum/action/xeno_action/watch_xeno, /datum/action/xeno_action/activable/tail_stab, - /datum/action/xeno_action/activable/pounce/predalien, + /datum/action/xeno_action/onclick/feralrush, /datum/action/xeno_action/onclick/predalien_roar, - /datum/action/xeno_action/onclick/smash, - /datum/action/xeno_action/activable/devastate, + /datum/action/xeno_action/activable/feral_smash, + /datum/action/xeno_action/activable/feralfrenzy, + /datum/action/xeno_action/onclick/toggle_gut_targeting, /datum/action/xeno_action/onclick/tacmap, ) weed_food_icon = 'icons/mob/xenos/weeds_64x64.dmi' weed_food_states = list("Predalien_1","Predalien_2","Predalien_3") weed_food_states_flipped = list("Predalien_1","Predalien_2","Predalien_3") + var/smashing = FALSE - var/butcher_time = 6 SECONDS /mob/living/carbon/xenomorph/predalien/Initialize(mapload, mob/living/carbon/xenomorph/oldxeno, h_number) @@ -86,15 +87,20 @@ to_chat(src, {" |______________________| -You are a predator-alien hybrid! -You are a very powerful xenomorph creature that was born of a Yautja warrior body. -You are stronger, faster, and smarter than a regular xenomorph, but you must still listen to the queen. -You have a degree of freedom to where you can hunt and claim the heads of the hive's enemies, so check your verbs. -Your health meter will not regenerate normally, so kill and die for the hive! +You are a yautja-alien hybrid! +You are a xenomorph born from the body of your natural enemy, you are considered an abomination to all of the yautja race and they will do WHATEVER it takes to kill you. +However, being born from one you also harbor their intelligence and strength. You are built to be able to take them on but that does not mean you are invincible. Stay with your hive and overwhelm them with your numbers. Your sisters have sacrificed a lot for you; Do not just wander off and die. +You must still listen to the queen. + |______________________| "}) emote("roar") + +/mob/living/carbon/xenomorph/predalien/resist_fire() + ..() + SetKnockDown(0.1 SECONDS) + /datum/behavior_delegate/predalien_base name = "Base Predalien Behavior Delegate" @@ -118,53 +124,11 @@ Your health meter will not regenerate normally, so kill and die for the hive! Date: Thu, 22 Feb 2024 09:27:12 +0000 Subject: [PATCH 37/49] Automatic changelog for PR #5432 [ci skip] --- html/changelogs/AutoChangeLog-pr-5432.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5432.yml diff --git a/html/changelogs/AutoChangeLog-pr-5432.yml b/html/changelogs/AutoChangeLog-pr-5432.yml new file mode 100644 index 000000000000..73d5a2476ff2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5432.yml @@ -0,0 +1,16 @@ +author: "InsaneRed" +delete-after: True +changes: + - rscadd: "Added a new ability to the predalien \"Feral Rush\" that increases it's armor and speed for a short duration." + - rscadd: "Added two new abilities to the predalien \"Feral Frenzy\" and a toggle. The predalien can now switch between a single target GUT and an AOE one, both of which has damage that scale with your kills." + - rscdel: "Removed the predaliens gib ability" + - rscdel: "Removed the predaliens \"pounce\" ability" + - rscdel: "Removed the predaliens \"Ground Smash\" ability" + - balance: "Predalien no longer has plasma costs, or plasma." + - balance: "The Plasma Rifle, which is ONLY used to hunt abominations now has a higher ROF and has incin bullets." + - spellcheck: "Re-wrote the predalien text to be more up to date and remove missinformation" + - qol: "Everyone can see how many kills the predalien has by examining it." + - rscadd: "Added a \"Feral Smash\" ability that lets you grab somebody and smash them to the ground, this scales with kills and is a devastating attack." + - balance: "Removed screenshake from predalien's screech" + - balance: "Predalien removes fire stacks faster." + - bugfix: "Girders are now slashable by very_sharp_claws instead of just having a queen chack" \ No newline at end of file From 1675e4eeb90fd4051de60688c6d6c5e24dec555b Mon Sep 17 00:00:00 2001 From: SabreML <57483089+SabreML@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:05:18 +0000 Subject: [PATCH 38/49] Fixes xeno tacklestrength (#5773) # About the pull request Fixes #5765. This was caused by me in #5542 deleting a couple of lines from `/mob/living/carbon/xenomorph/proc/recalculate_tackle()`, when I should have only removed the `mutators`/`hive.mutators` part. ![image](https://github.com/cmss13-devs/cmss13/assets/57483089/b54a3e03-03cd-49a3-a093-ab0765059a7b) # Explain why it's good for the game Whoops # Testing Photographs and Procedure
Screenshots & Videos `tacklestrength_min` and `tacklestrength_max` are being applied correctly: ![image](https://github.com/cmss13-devs/cmss13/assets/57483089/8404ddf3-17e0-42a5-a3cf-91a85beb1e50)
# Changelog :cl: fix: Fixed Xenomorphs not having their caste's tackle duration values applied to them. /:cl: --- code/modules/mob/living/carbon/xenomorph/Xenomorph.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index c8d3df91c975..03ba544be6b8 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -839,6 +839,8 @@ tackle_min = caste.tackle_min tackle_max = caste.tackle_max tackle_chance = caste.tackle_chance + tackle_chance_modifier + tacklestrength_min = caste.tacklestrength_min + tacklestrength_max = caste.tacklestrength_max /mob/living/carbon/xenomorph/proc/recalculate_health() var/new_max_health = nocrit ? health_modifier + maxHealth : health_modifier + caste.max_health From 1e7703b5d2072129b3c7a4e076d289f3a6893a7c Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:14:28 +0000 Subject: [PATCH 39/49] Automatic changelog for PR #5773 [ci skip] --- html/changelogs/AutoChangeLog-pr-5773.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5773.yml diff --git a/html/changelogs/AutoChangeLog-pr-5773.yml b/html/changelogs/AutoChangeLog-pr-5773.yml new file mode 100644 index 000000000000..702fda68396b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5773.yml @@ -0,0 +1,4 @@ +author: "SabreML" +delete-after: True +changes: + - bugfix: "Fixed Xenomorphs not having their caste's tackle duration values applied to them." \ No newline at end of file From d72106f7735d71b1a90c817f7c6edc60b664c284 Mon Sep 17 00:00:00 2001 From: SabreML <57483089+SabreML@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:16:11 +0000 Subject: [PATCH 40/49] TGUI input list search fix (#5775) # About the pull request Fixes the search function of `tgui_input_list`, and also makes the search bar automatically focus when toggled. This was caused by the same issue as #5581. # Explain why it's good for the game Bugfix, and a little QOL thing. # Testing Photographs and Procedure
Screenshots & Videos ('Teleport to Area' menu as an example, but it's the same on any others.) **Before:** https://github.com/cmss13-devs/cmss13/assets/57483089/8450adef-38f4-4ae4-963a-11533a328841 **After:** https://github.com/cmss13-devs/cmss13/assets/57483089/ede80ffa-7ce6-47c9-9711-a5f29560d1bd
# Changelog :cl: fix: Fixed the search function of TGUI input lists. qol: Made the TGUI input list search bar automatically focus when toggled. /:cl: --- tgui/packages/tgui/interfaces/ListInput.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tgui/packages/tgui/interfaces/ListInput.jsx b/tgui/packages/tgui/interfaces/ListInput.jsx index 2ed72bcc63bf..324e1f6b64fe 100644 --- a/tgui/packages/tgui/interfaces/ListInput.jsx +++ b/tgui/packages/tgui/interfaces/ListInput.jsx @@ -123,9 +123,9 @@ export const ListInput = (props) => { }} /> }> - {displayedArray.map((button) => ( + {displayedArray.map((button, i) => (