From 48fde5dbbc5387fd49655a6e08ed26addd00cec9 Mon Sep 17 00:00:00 2001 From: Kaga-404 <103199482+Kaga-404@users.noreply.github.com> Date: Fri, 5 Apr 2024 22:41:43 -0700 Subject: [PATCH 01/10] Updated XM43E1 for Sniper Spec (#5774) # About the pull request New Updates: - Using Aimed Shot on a fully-focused target will now change the laser and icon to blue. - - Aimed Shot Damage has been adjusted across the board with two new effects, which stack together. - The first is **current health damage**, which allows it to stay about as dangerous against full-health Xenos, but reduces the pressure at low health, and the likelihood of going from 375 HP to crit immediately. Low-health burst sniping becomes even more unlikely from Aimed Shots when combined with the second effect, **Focused Fire**. The scaling is 20% against most xenos, and 30% against big xenos. - **Focused Fire** triggers whenever Aimed Shot lands on a target, though it only modifies damage applied to most xenos at the moment, except Runners. This effect reduces the bonus Aimed Shot Damage by 50% when hitting a new target. However, subsequent Aimed Shot hits against the same xeno will increase the modifier, with the second going up to 75%, and then the 3 onwards at 100%. **This effect resets when landing an Aimed Shot on another target.** If you're the sniper, consider using normal shots for damage and support to preserve your focus on a priority target. - Defenders are now excluded from the higher-tier size calculations, so they only take 200 + 10% current health damage from Aimed Shots. Focused Fire damage reduction doesn't apply to them because of this, but it might be added in if they still continue to struggle when trying to block shots for allies. However, they may also be targeted less by the ability with this update, and only take normal hits (can survive 4-5 on weeds, which takes the sniper about 16-20s of open sightlines and free shots) because the focus mark is being kept on a more dangerous target. Fixed PR built from the master branch. Thank you to Zonespace for the idea of making a Wall-Penetrating Ammo subtype, with more nuance and falloff mechanics to expand on stemming from the Vulture, and all the help in messing around with the code. The XM43E1 is an experimental anti-materiel rifle that's existed in the code for a while as a test weapon, but had several bugs and insufficient testing. This PR should hopefully allow it to be testmerged in a balanced manner as an M42A sniper sidegrade, without the need for staff to spawn it in. It can be picked as a normal option from Spec Vendors, being mutually exclusive with the M42A, as well as cryo spec kits. Its design purpose is that of a sniper weapon that fires slower, with less direct combat utility, but more supportive utility and higher impact on shots that actually land. To this purpose, in comparison to the normal sniper rifle (M42A), it: - Has around half the magazine of the normal sniper (8 vs 15) - Has slightly less damage potential within the magazine (1000 vs 1050) - Has less DPS than the M42A (125 vs 140, per 3s) - Loses out on combining ammunition types, and their special effects. It cannot set targets on fire, hit around corners with flak, or use their respective special effects from aimed shots (blindness and slows). - Has special damage scaling on its aimed shot, causing it to lose massive amounts of potential damage per magazine when hitting small targets. 1.8x vs Non-Xenos and Runners, 2.4x vs most Xenos, and only the normal 3x vs Big Xenos. Most T3s, the Queen, and Fortified Defenders. This comes out to as little as 1800 damage per magazine from Aimed Shots, compared to Marksman M42A mags dealing up to 3150, with a max single-target potential of 3000 vs 3150. In return, however, it has higher single-shot damage that allows for more surprising burst, and effects of its own to enable alternative support and utility, alongside special balancing to make them more balanced and interesting to play around, explained below. Also included in this PR are several backend fixes to actually make this work properly - without this, the weapon would deal more damage when wallbanging, and also deal multiplied damage to mobs inside of walls, like a Queen building beneath herself. # Explain why it's good for the game The updated XM43E1's design keeps the weapon's wall-and-mob-piercing, but adds in **_falloff_** for balancing and interactivity, **_which applies to everything it hits_.** While it retains massive damage against walls and structures, able to break even Thick Resin in a single shot, normal metal walls in 4, and reinforced in 8, it now loses a huge portion of its energy when doing so, subsequently reducing its damage and penetration power. What this means in practice is that it can no longer theoretically multiply its potential damage by up to 8x if everything lines up. In fact, it can barely even get through 2 walls, doing very little damage after that. Mobs, it can handle a bit better, but bodyblocking for more vulnerable targets is a valid strategy to try and protect allies from the AMR. You take the brunt of the hit, and increase their technical DPS, but greatly blunt the damage to the targets behind you. The actual numbers are 20% reduction in damage for most targets hit, and 60% for dense walls (normal and resin walls) **_and Big Xenos_.** Note that this _does not include Resin Membranes or windows_, which goes into another of the XM43E1's sidegrade utilities: the ability to **_fight Boilers_**, something Sniper Spec has been lacking for years ever since the caste was updated, and a feature this weapon had been originally been designed around. Membranes will still reduces the AMR's effectiveness temporarily, but such defenses break under sustained focus from it, forcing the Boiler to either seek out allies to cover for it (Fortify Defender escort is back!) or shift to new positions and angles, prepared by builders to force the AMR to expend more ammo. Additionally, this also affects the Aimed Shot's potential, stacking with the existing special modifiers. Against single targets, the XM43E1 is still far inferior in most cases, only being able to deal more damage when you can only fire off one shot before needing to run, at the cost of way less actual damage and none of the special effects compared to the M42A's special ammos, but in a crowd it gains some benefits and loses others. The main benefit is that the technical damage it's dealing is increasing. More targets, more hits, more efficiency. **_However, the Aimed Shot target can be covered by others, potentially reducing that multiplier down, or even blocking the shot completely_.** A few big xenos can entirely eat an Aimed Shot for a Queen, **_and they don't take the increased damage_.** Wallbreaking and wallbanging potential is also heavily reduced by this new effect, as the XM43E1 should usually only be able to hit targets at most 2 walls in, and that's within pretty close ranges. At most sniping ranges it'll probably be only one. It can break a Thick Resin Wall in 1 shot, but it'll only slightly hurt the second one behind that, and have to spend another full shot to break that down, and so on. The Sniper Spec also doesn't have thermals, so while true wallbanging is still possible, it's not consistent like the PMC Sniper, nor is it nearly as effective due to the above mentioned falloff. You'll be looking at around like 50 damage after just 1 wall. That one shot can save people, though, like the old-school masterkey supports. Gotta get someone out who got dragged behind a door? Blast away like the XM51 does these days, except from a long-range support position. Its design also emphasizes aiming for structural weakspots like Resin Membranes, glass, and doors. Essentially, it serves as a sidegrade where you trade off superior overall stats, extremely flexible combat utility, and special Aimed Shot effects for: - Slightly worse damage against single target, but better damage against multiple that line up, with diminished effectiveness for each target, at long ranges, and against big xenos. - An Aimed Shot 60% as efficient as a M42A's aimed shot against runners, 80% against most xenos, and only equal efficiency against big xenos. It can potentially still hit and get that sweet multiplier through a few xenos, or if a target ducks behind a wall once the shot goes out but before it hits. However, any blocking, whether mobs or walls, will greatly reduce the final damage, and may even completely block it. No more hitting a full damage Aimed Shot through 7 xenos. - The ability to break singular walls and obstacles with precisely aimed shots, especially resin, but at the cost of a full shot. You only get 8 per mag, and each mag costs the same as a sniper's, and it'll be 3 seconds before you can shoot something else. - A weapon that isn't CAS/Mortar/OB to counter Boilers, by easily breaking their cover and chipping away at them, and performing better against peeking than the M42A's lower single-shot damage. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: Kaga add: Added the XM43E1 Anti-Materiel Rifle as a pickable sidegrade option for USCM Sniper Specs through the vendor, mutually exclusive with the normal M42A Sniper Rifle. The AMR can shred obstacles, blow a hole through most infantry and their equipment, and even hit a collateral on multiple targets or through light cover, but its lack of specialized ammunition and small magazine mean that you'll need to make every shot count. Ammo can be bought from Req, at the same cost per magazine as the M42A. balance: Completely updated the XM43E1 AMR, adding damage falloff to its wall-piercing properties whenever it hits something alongside range loss. Most things reduce its damage by 20%. Dense walls and big xenos reduce both the damage and range 3 times as much. /:cl: --------- Co-authored-by: forest2001 <41653574+realforest2001@users.noreply.github.com> Co-authored-by: Drathek <76988376+Drulikar@users.noreply.github.com> --- code/__DEFINES/weapon_stats.dm | 2 + code/_globalvars/global_lists.dm | 2 + code/datums/ammo/ammo.dm | 1 + code/datums/ammo/bullet/sniper.dm | 111 ++++++++++++++++-- .../elements/bullet_trait/damage_boost.dm | 50 ++++---- .../elements/bullet_trait/penetrating/weak.dm | 60 ++++++++++ code/datums/supply_packs/spec_ammo.dm | 8 +- code/game/machinery/cryopod.dm | 1 + code/game/machinery/vending/cm_vending.dm | 5 + .../squad_prep/squad_specialist.dm | 2 + .../modules/cm_marines/equipment/kit_boxes.dm | 24 ++++ .../mob/living/carbon/human/human_defines.dm | 3 + code/modules/projectiles/gun.dm | 4 + .../projectiles/guns/specialist/sniper.dm | 56 ++++++--- .../projectiles/magazines/specialist.dm | 2 +- code/modules/projectiles/projectile.dm | 17 +++ colonialmarines.dme | 1 + icons/obj/items/weapons/guns/lineart.dmi | Bin 17104 -> 17103 bytes 18 files changed, 291 insertions(+), 58 deletions(-) create mode 100644 code/datums/elements/bullet_trait/penetrating/weak.dm diff --git a/code/__DEFINES/weapon_stats.dm b/code/__DEFINES/weapon_stats.dm index 3a69002a3b93..1c3c09e9b28d 100644 --- a/code/__DEFINES/weapon_stats.dm +++ b/code/__DEFINES/weapon_stats.dm @@ -136,7 +136,9 @@ As such, don't expect any values assigned to common firearms to even consider ho //How many ticks you have to wait between firing. Burst delay uses the same variable! */ +#define FIRE_DELAY_TIER_AMR 30 #define FIRE_DELAY_TIER_VULTURE 20 +#define FIRE_DELAY_TIER_SNIPER 15 #define FIRE_DELAY_TIER_1 12 #define FIRE_DELAY_TIER_2 10 #define FIRE_DELAY_TIER_3 9 diff --git a/code/_globalvars/global_lists.dm b/code/_globalvars/global_lists.dm index 64bf4ba7c901..c23f1fc7931f 100644 --- a/code/_globalvars/global_lists.dm +++ b/code/_globalvars/global_lists.dm @@ -490,6 +490,7 @@ GLOBAL_LIST_EMPTY(timelocks) GLOBAL_LIST_INIT(available_specialist_sets, list( "Scout Set", "Sniper Set", + "Anti-materiel Sniper Set", "Demolitionist Set", "Heavy Grenadier Set", "Pyro Set" @@ -502,6 +503,7 @@ GLOBAL_LIST_INIT(available_specialist_kit_boxes, list( "Sniper" = 2, "Scout" = 2, "Demo" = 2, + "Anti-materiel Sniper" = 2, )) /proc/init_global_referenced_datums() diff --git a/code/datums/ammo/ammo.dm b/code/datums/ammo/ammo.dm index 229c10b31e3a..eba72f2594e2 100644 --- a/code/datums/ammo/ammo.dm +++ b/code/datums/ammo/ammo.dm @@ -230,6 +230,7 @@ P.generate_bullet(GLOB.ammo_list[bonus_projectiles_type]) //No bonus damage or anything. P.accuracy = round(P.accuracy * original_P.accuracy/initial(original_P.accuracy)) //if the gun changes the accuracy of the main projectile, it also affects the bonus ones. original_P.give_bullet_traits(P) + P.bonus_projectile_check = 2 //It's a bonus projectile! var/total_scatter_angle = P.scatter final_angle += rand(-total_scatter_angle, total_scatter_angle) diff --git a/code/datums/ammo/bullet/sniper.dm b/code/datums/ammo/bullet/sniper.dm index 22371972e623..6d0f0792ec53 100644 --- a/code/datums/ammo/bullet/sniper.dm +++ b/code/datums/ammo/bullet/sniper.dm @@ -107,19 +107,104 @@ damage = 125 shell_speed = AMMO_SPEED_TIER_6 -/datum/ammo/bullet/sniper/anti_materiel/on_hit_mob(mob/M,obj/projectile/P) - if((P.projectile_flags & PROJECTILE_BULLSEYE) && M == P.original) - var/mob/living/L = M - var/size_damage_mod = 0.8 - if(isxeno(M)) - var/mob/living/carbon/xenomorph/target = M - if(target.mob_size >= MOB_SIZE_XENO) - size_damage_mod += 0.6 - if(target.mob_size >= MOB_SIZE_BIG) - size_damage_mod += 0.6 - L.apply_armoured_damage(damage*size_damage_mod, ARMOR_BULLET, BRUTE, null, penetration) - // 180% damage to all targets (225), 240% (300) against non-Runner xenos, and 300% against Big xenos (375). -Kaga - to_chat(P.firer, SPAN_WARNING("Bullseye!")) +/datum/ammo/bullet/sniper/anti_materiel/on_hit_mob(mob/target_mob,obj/projectile/aimed_projectile) + + var/mob/living/living_target = target_mob + + if((aimed_projectile.projectile_flags & PROJECTILE_BULLSEYE) && target_mob == aimed_projectile.original) + + var/amr_counter = 0 + var/datum/weakref/old_target = null // This is used to let xenos know when they're no longer targeted. + + var/mob/living/carbon/human/human_firer + var/image/focused_fire_marker_temp = image('icons/mob/hud/hud.dmi', target_mob, "hudeye") + + if(istype(aimed_projectile.firer, /mob/living/carbon/human)) // Preps the Focused Fire marker. + human_firer = aimed_projectile.firer + focused_fire_marker_temp.color = human_firer.assigned_squad?.chat_color + + if(target_mob.icon_size > world.icon_size) // Centers marker on their tile. + focused_fire_marker_temp.pixel_x = (target_mob.icon_size / 4) + + if(istype(aimed_projectile.shot_from, /obj/item/weapon/gun/rifle/sniper/XM43E1)) // Calculates the Focus Counter. + var/obj/item/weapon/gun/rifle/sniper/XM43E1/amr = aimed_projectile.shot_from + + old_target = amr.focused_fire_target + + if(target_mob == (amr.focused_fire_target?.resolve())) + if(amr.focused_fire_counter < 2) // Can stack up to twice. + amr.focused_fire_counter += 1 + else + amr.focused_fire_counter = 2 + else // If it's a new target + amr.focused_fire_counter = 0 // Stacks to 0 + if(human_firer && !(target_mob.is_dead())) + human_firer.client?.images -= human_firer.focused_fire_marker // Remove old marker + qdel(human_firer.focused_fire_marker) + human_firer.focused_fire_marker = focused_fire_marker_temp // Store new marker ref + human_firer.client?.images += focused_fire_marker_temp // Add new marker + + amr_counter = amr.focused_fire_counter + 1 + amr.focused_fire_target = WEAKREF(target_mob) + + var/size_damage_mod = 0.8 // 1.8x vs Non-Xenos (225) + var/size_current_health_damage = 0 // % Current Health calculation, only used for Xeno calculations at the moment. + var/focused_fire_active = 0 // Whether to try and use focused fire calculations or not, for that kind of target. + + if(isxeno(target_mob)) + var/mob/living/carbon/xenomorph/target = target_mob + size_damage_mod -= 0.2 // Down to 1.6x damage, 200. + size_current_health_damage = 0.1 // 1.6x Damage + 10% current health (200 + 10%, 223 vs Runners) + + if(target.mob_size >= MOB_SIZE_XENO && (target.caste_type != XENO_CASTE_DEFENDER)) + size_current_health_damage += 0.1 // 1.6x Damage + 20% current health + focused_fire_active = 1 // Focus Fire Required. Only deals 50% bonus damage on a first Aimed Shot, then 75%, then 100%. Resets with a successful aimed shot on another target. + + if(target.mob_size >= MOB_SIZE_BIG && (target.caste_type != XENO_CASTE_DEFENDER)) + size_damage_mod -= 0.6 // Down to 1x Damage. + size_current_health_damage += 0.1 // 1x Damage + 30% current health. + focused_fire_active = 1 + // Most T3s have around 650 to 700 HP, meaning the health modifier grants a MAXIMUM of around 195-210 damage for a total max of 320-335. This is fully focused (3 shots) and at max HP. + // Queen takes around 275 at max health and unfocused, 425 fully focused. Defender only takes 200 damage, even while fortified, but still causes falloff like a Big Xeno. + // At low health, does little more than a normal shot. Does WORSE than a normal shot if unfocused and hitting through blockers, all of which stack to reduce it. + + var/final_xeno_damage = ((damage * size_damage_mod) + ((target.health + damage) * size_current_health_damage)) + + if(focused_fire_active && amr_counter) // If this is a target that needs to be focus-fired and the gun supports it, reduce bonus damage to 50%, then 75%, then 100% + // If amr_counter is 0, then the gun likely doesn't have the tracker functions, so skip this and deal normal damage. + final_xeno_damage *= (0.25 + (0.25 * amr_counter)) + + living_target.apply_armoured_damage((final_xeno_damage), ARMOR_BULLET, BRUTE, null, penetration) + + else + living_target.apply_armoured_damage((damage*size_damage_mod), ARMOR_BULLET, BRUTE, null, penetration) + + // Base 1.8x damage to non-xeno targets (225), 1.6x + 10% current against Runners and Defenders (223), 1.6x + 20% current health against most non-Runner xenos, and +30% current health against Big xenos. -Kaga + // This applies after pen reductions. After hitting 1 other thing, it deals 80% damage, or 40% after hitting a dense wall or big xeno. + + if((focused_fire_active || isxeno(target_mob)) && !(target_mob.is_dead())) + switch(amr_counter) + if(1) + to_chat(aimed_projectile.firer, SPAN_WARNING("One hit! You begin to carefully track the target's movements.")) + if(isxeno(target_mob) && isxeno(old_target?.resolve())) + var/mob/living/carbon/xenomorph/old_xeno = old_target.resolve() + var/mob/living/carbon/xenomorph/new_xeno = target_mob + if((old_xeno.hive == new_xeno.hive) && !(old_xeno.stat)) // Must be in same hive and conscious + to_chat(old_xeno,SPAN_XENOLEADER("The feeling of looming danger fades as we sense that another sister has been targeted instead.")) + if(2) + to_chat(aimed_projectile.firer, SPAN_WARNING("Two hits! You're starting to get a good read on the target's patterns.")) + if(3) + to_chat(aimed_projectile.firer, SPAN_WARNING("Bullseye! You're fully focused on the target.")) + else + to_chat(aimed_projectile.firer, SPAN_WARNING("Bullseye!")) + else + to_chat(aimed_projectile.firer, SPAN_WARNING("Bullseye!")) + +/datum/ammo/bullet/sniper/anti_materiel/set_bullet_traits() + . = ..() + LAZYADD(traits_to_give, list( + BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_penetrating/weak) + )) /datum/ammo/bullet/sniper/anti_materiel/vulture damage = 400 // Fully intended to vaporize anything smaller than a mini cooper diff --git a/code/datums/elements/bullet_trait/damage_boost.dm b/code/datums/elements/bullet_trait/damage_boost.dm index eee4a8e80f4b..43d9d1b23377 100644 --- a/code/datums/elements/bullet_trait/damage_boost.dm +++ b/code/datums/elements/bullet_trait/damage_boost.dm @@ -28,17 +28,10 @@ GLOBAL_LIST_INIT(damage_boost_vehicles, typecacheof(/obj/vehicle/multitile)) /// A typecache of objs or turfs that, upon being hit, boost the damage of the attached projectile var/list/damage_boosted_atoms - //vars for dealing with interaction issues with the Penetrating trait - var/boosted_hits - var/last_damage_mult - //allows for nuance in Breaching-Resistant interactions var/active_damage_mult var/atom_type - //var for dealing with bonus projectiles - var/bonus_projectile_check - /** * vars: * * damage_mult - the damage multiplier to be applied if the bullet hits an atom whose type is in `breaching_objs` @@ -51,11 +44,8 @@ GLOBAL_LIST_INIT(damage_boost_vehicles, typecacheof(/obj/vehicle/multitile)) src.damage_mult = damage_mult src.damage_boosted_atoms = damage_boosted_atoms - src.boosted_hits = 0 - src.last_damage_mult = 1 src.active_damage_mult = 1 src.atom_type = 0 - src.bonus_projectile_check = 0 RegisterSignal(target, list( COMSIG_BULLET_PRE_HANDLE_OBJ, @@ -68,17 +58,17 @@ GLOBAL_LIST_INIT(damage_boost_vehicles, typecacheof(/obj/vehicle/multitile)) //add more cases for other interactions (switch doesn't seem to work with istype) else return 0 -/datum/element/bullet_trait_damage_boost/proc/handle_bullet(obj/projectile/P, atom/A) +/datum/element/bullet_trait_damage_boost/proc/handle_bullet(obj/projectile/boosted_projectile, atom/hit_atom) SIGNAL_HANDLER - atom_type = check_type(A) + atom_type = check_type(hit_atom) switch(atom_type) if("door") - var/obj/structure/machinery/door/D = A - if(D.masterkey_resist) - if(D.masterkey_mod) - active_damage_mult = damage_mult * D.masterkey_mod + var/obj/structure/machinery/door/hit_door = hit_atom + if(hit_door.masterkey_resist) + if(hit_door.masterkey_mod) + active_damage_mult = damage_mult * hit_door.masterkey_mod else active_damage_mult = 1 //no bonus damage else @@ -87,12 +77,22 @@ GLOBAL_LIST_INIT(damage_boost_vehicles, typecacheof(/obj/vehicle/multitile)) else active_damage_mult = damage_mult - if(boosted_hits > 0) - if(bonus_projectile_check == P.damage) - P.damage = P.damage / last_damage_mult - boosted_hits-- - if(damage_boosted_atoms[A.type]) - P.damage = round(P.damage * active_damage_mult) - last_damage_mult = active_damage_mult - boosted_hits++ - bonus_projectile_check = P.damage + + if(boosted_projectile.damage_boosted && ((boosted_projectile.last_atom_signaled?.resolve()) != hit_atom) && (!boosted_projectile.bonus_projectile_check)) + //If this is after a boosted hit, the last atom that procced this isn't the same as the current target, and this isn't a bonus projectile sharing the same damage_boost + if(!boosted_projectile.last_damage_mult) //Make sure stored mult isn't 0 + boosted_projectile.last_damage_mult = 1 + + boosted_projectile.damage = boosted_projectile.damage / boosted_projectile.last_damage_mult //Reduce the damage back to normal + boosted_projectile.damage_boosted-- //Mark that damage has been returned to normal. + + if(damage_boosted_atoms[hit_atom.type]) //If hitting a valid atom for damage boost + boosted_projectile.damage = round(boosted_projectile.damage * active_damage_mult) //Modify Damage by multiplier + + if (active_damage_mult) + boosted_projectile.last_damage_mult = active_damage_mult //Save multiplier for next check + else + boosted_projectile.last_damage_mult = 1 + + boosted_projectile.damage_boosted++ //Mark that a boosted hit occurred. + boosted_projectile.last_atom_signaled = WEAKREF(hit_atom) //Save the current triggering atom to the projectile diff --git a/code/datums/elements/bullet_trait/penetrating/weak.dm b/code/datums/elements/bullet_trait/penetrating/weak.dm new file mode 100644 index 000000000000..99c94a9c90ba --- /dev/null +++ b/code/datums/elements/bullet_trait/penetrating/weak.dm @@ -0,0 +1,60 @@ +/datum/element/bullet_trait_penetrating/weak + // Generic bullet trait vars + element_flags = ELEMENT_DETACH|ELEMENT_BESPOKE + id_arg_index = 4 + + /// For each thing this hits, how much damage it loses normally. This can be modified by what it penetrates later. + var/damage_percent_lost_per_hit = 20 + // XM43E1 AMR: First target takes full damage, each subsequent target takes at least 20% less damage (increased for large mobs and dense turfs), 25 from base 125 damage. + + /// For each thing this hits, how much distance it loses normally. + distance_loss_per_hit = 4 + // XM43E1 AMR: Hits 7 things at most, at point blank, with no additional modifiers. This greatly increases at actual sniping ranges. + + /// How many times more effective turfs are at slowing down the projectile normally, reducing both range and damage. + var/turf_hit_slow_mult = 3 + // XM43E1 AMR: Unable to hit anything through more than 2 walls, less at maximum ranges. Pens 2 walls at 8 tiles or less, 1 at 20 tiles or less, and can't wallbang through normal walls at maximum range. + // Also loses 75 damage with each normal wall pen. + +/datum/element/bullet_trait_penetrating/weak/Attach(datum/target, distance_loss_per_hit = 4, damage_percent_lost_per_hit = 20, turf_hit_slow_mult = 3) + . = ..() + if(. == ELEMENT_INCOMPATIBLE) + return + + src.damage_percent_lost_per_hit = damage_percent_lost_per_hit + src.turf_hit_slow_mult = turf_hit_slow_mult + +/datum/element/bullet_trait_penetrating/weak/handle_passthrough_movables(obj/projectile/bullet, atom/movable/hit_movable, did_hit) + if(did_hit) + var/slow_mult = 1 + if(ismob(hit_movable)) + var/mob/mob = hit_movable + if(mob.mob_size >= MOB_SIZE_BIG) // Big Xenos (including fortified Defender) can soak hits and greatly reduce penetration. + slow_mult = 2 // 8 tiles of range lost per Big hit. At point blank, this comes out to only 3 targets. At sniping ranges, even a single one can stop the bullet dead. + + bullet.distance_travelled += (distance_loss_per_hit * slow_mult) + + bullet.damage -= (damage_percent_lost_per_hit * slow_mult) + + return COMPONENT_BULLET_PASS_THROUGH + + +/datum/element/bullet_trait_penetrating/weak/handle_passthrough_turf(obj/projectile/bullet, turf/closed/wall/hit_wall) + var/slow_mult = turf_hit_slow_mult + + // Better penetration against Membranes to still be able to counter Boilers at most ranges. Still loses 4 tiles of range and 25 damage per. + if(istype(hit_wall, /turf/closed/wall/resin/membrane)) + if(istype(hit_wall, /turf/closed/wall/resin/membrane/thick)) + slow_mult = 1.5 + else + slow_mult = 1 + + bullet.distance_travelled += (distance_loss_per_hit * slow_mult) + + bullet.damage *= (1 - (damage_percent_lost_per_hit * slow_mult * 0.01)) + + if(!istype(hit_wall)) + return COMPONENT_BULLET_PASS_THROUGH + + if(!hit_wall.hull) + return COMPONENT_BULLET_PASS_THROUGH diff --git a/code/datums/supply_packs/spec_ammo.dm b/code/datums/supply_packs/spec_ammo.dm index f0eb5ab9cea2..889d2e25b8af 100644 --- a/code/datums/supply_packs/spec_ammo.dm +++ b/code/datums/supply_packs/spec_ammo.dm @@ -109,8 +109,6 @@ containername = "M42A Incendiary Magazine Crate" group = "Weapons Specialist Ammo" -//XM43E1 - Disabled during testing per request. -/* /datum/supply_packs/ammo_amr_marksman name = "XM43E1 anti-materiel rifle marksman magazines crate (x5)" contains = list( @@ -122,9 +120,9 @@ ) cost = 30 containertype = /obj/structure/closet/crate/ammo - containername = "XM43E1 Anti-Materiel Magazine Crate" - group = "Specialist Ammo" -*/ + containername = "XM43E1 Marksman Magazine Crate" + group = "Weapons Specialist Ammo" + //M4RA /datum/supply_packs/ammo_scout_mix diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 69c2c897e276..1793b87c72ae 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -342,6 +342,7 @@ GLOBAL_LIST_INIT(frozen_items, list(SQUAD_MARINE_1 = list(), SQUAD_MARINE_2 = li set_name = "Scout Set" if(SKILL_SPEC_SNIPER) set_name = "Sniper Set" + GLOB.available_specialist_sets += "Anti-materiel Sniper Set" if(set_name && !GLOB.available_specialist_sets.Find(set_name)) GLOB.available_specialist_sets += set_name diff --git a/code/game/machinery/vending/cm_vending.dm b/code/game/machinery/vending/cm_vending.dm index 06ec8ddc7520..bfb539043b26 100644 --- a/code/game/machinery/vending/cm_vending.dm +++ b/code/game/machinery/vending/cm_vending.dm @@ -566,6 +566,11 @@ GLOBAL_LIST_EMPTY(vending_products) if("Sniper Set") user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_SNIPER) specialist_assignment = "Sniper" + GLOB.available_specialist_sets -= "Anti-materiel Sniper Set" + if("Anti-materiel Sniper Set") + user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_SNIPER) + specialist_assignment = "Heavy Sniper" + GLOB.available_specialist_sets -= "Sniper Set" if("Demolitionist Set") user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_ROCKET) specialist_assignment = "Demo" diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm index e800fc1efd56..93ec16cbb48e 100644 --- a/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm +++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm @@ -7,6 +7,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_spec, list( list("Pyro Set", 0, /obj/item/storage/box/spec/pyro, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_REGULAR), list("Scout Set", 0, /obj/item/storage/box/spec/scout, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_REGULAR), list("Sniper Set", 0, /obj/item/storage/box/spec/sniper, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_RECOMMENDED), + list("Anti-materiel Sniper Set", 0, /obj/item/storage/box/spec/sniper/anti_materiel, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_RECOMMENDED), list("EXTRA SCOUT AMMUNITION", 0, null, null, null), list("A19 High Velocity Impact Magazine (10x24mm)", 40, /obj/item/ammo_magazine/rifle/m4ra/custom/impact, null, VENDOR_ITEM_REGULAR), @@ -17,6 +18,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_spec, list( list("M42A Flak Magazine (10x28mm)", 40, /obj/item/ammo_magazine/sniper/flak, null, VENDOR_ITEM_REGULAR), list("M42A Incendiary Magazine (10x28mm)", 40, /obj/item/ammo_magazine/sniper/incendiary, null, VENDOR_ITEM_REGULAR), list("M42A Marksman Magazine (10x28mm Caseless)", 40, /obj/item/ammo_magazine/sniper, null, VENDOR_ITEM_REGULAR), + list("XM43E1 Marksman Magazine (10x99mm Caseless)", 40, /obj/item/ammo_magazine/sniper/anti_materiel, null, VENDOR_ITEM_REGULAR), list("EXTRA DEMOLITIONIST AMMUNITION", 0, null, null, null), list("84mm Anti-Armor Rocket", 40, /obj/item/ammo_magazine/rocket/ap, null, VENDOR_ITEM_REGULAR), diff --git a/code/modules/cm_marines/equipment/kit_boxes.dm b/code/modules/cm_marines/equipment/kit_boxes.dm index d99da8f59f2c..c26bafe7123a 100644 --- a/code/modules/cm_marines/equipment/kit_boxes.dm +++ b/code/modules/cm_marines/equipment/kit_boxes.dm @@ -69,6 +69,26 @@ // spotter new /obj/item/storage/box/kit/spotter(src) +/obj/item/storage/box/spec/sniper/anti_materiel/fill_preset_inventory() + name = "\improper AMR equipment case" + desc = "A large case containing an experimental XM43E1, a set of M45 ghillie armor and helmet, a M42 scout sight, ammunition, spotter equipment, and additional pieces of equipment.\nDrag this sprite onto yourself to open it up! NOTE: You cannot put items back inside this case." + new /obj/item/clothing/suit/storage/marine/ghillie(src) + new /obj/item/clothing/head/helmet/marine/ghillie(src) + new /obj/item/clothing/glasses/night/m42_night_goggles(src) + new /obj/item/weapon/gun/rifle/sniper/XM43E1(src) + new /obj/item/ammo_magazine/sniper/anti_materiel(src) + new /obj/item/ammo_magazine/sniper/anti_materiel(src) + new /obj/item/ammo_magazine/sniper/anti_materiel(src) + new /obj/item/ammo_magazine/sniper/anti_materiel(src) + new /obj/item/ammo_magazine/sniper/anti_materiel(src) + new /obj/item/storage/backpack/marine/smock(src) + new /obj/item/weapon/gun/pistol/vp78(src) + new /obj/item/ammo_magazine/pistol/vp78(src) + new /obj/item/ammo_magazine/pistol/vp78(src) + new /obj/item/facepaint/sniper(src) + // spotter + new /obj/item/storage/box/kit/spotter(src) + /obj/item/storage/box/spec/scout name = "\improper Scout equipment case" desc = "A large case containing an M4RA battle rifle, M3-S light armor and helmet, M4RA battle sight, M68 thermal cloak, V3 reactive thermal tarp, improved scout laser designator, ammunition and additional pieces of equipment.\nDrag this sprite onto yourself to open it up! NOTE: You cannot put items back inside this case." @@ -257,6 +277,10 @@ spec_box = new /obj/item/storage/box/spec/sniper(T) specialist_assignment = "Sniper" user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_SNIPER) + if("Anti-materiel Sniper") + spec_box = new /obj/item/storage/box/spec/sniper/anti_materiel(T) + specialist_assignment = "Heavy Sniper" + user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_SNIPER) if("Scout") spec_box = new /obj/item/storage/box/spec/scout(T) specialist_assignment = "Scout" diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 29a9789b5699..e611452e9de7 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -167,6 +167,9 @@ /// static associated list of limb key -> image to avoid unnecessary overlay generation var/static/list/icon_render_image_cache = list() + /// Stored image references associated with focus-fire. + var/image/focused_fire_marker + /client/var/cached_human_playtime /client/proc/get_total_human_playtime(skip_cache = FALSE) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 3f7533f26620..68f169fa2b25 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -1419,6 +1419,10 @@ and you're good to go. BP.generate_bullet(GLOB.ammo_list[projectile_to_fire.ammo.bonus_projectiles_type], 0, NO_FLAGS) BP.accuracy = round(BP.accuracy * projectile_to_fire.accuracy/initial(projectile_to_fire.accuracy)) //Modifies accuracy of pellets per fire_bonus_projectiles. BP.damage *= damage_buff + + BP.bonus_projectile_check = 2 + projectile_to_fire.bonus_projectile_check = 1 + projectile_to_fire.give_bullet_traits(BP) if(bullets_fired > 1) BP.original = attacked_mob //original == the original target of the projectile. If the target is downed and this isn't set, the projectile will try to fly over it. Of course, it isn't going anywhere, but it's the principle of the thing. Very embarrassing. diff --git a/code/modules/projectiles/guns/specialist/sniper.dm b/code/modules/projectiles/guns/specialist/sniper.dm index bdb0ba02f3ab..5b4a81fde273 100644 --- a/code/modules/projectiles/guns/specialist/sniper.dm +++ b/code/modules/projectiles/guns/specialist/sniper.dm @@ -19,6 +19,12 @@ var/sniper_beam_icon = "laser_beam" var/skill_locked = TRUE + /// Variables for Focus Fire and alternate icons for lockon and laser. + var/enable_aimed_shot_icon_alt = FALSE + var/sniper_lockon_icon_max = "sniper_lockon_intense" + var/sniper_beam_icon_max = "laser_beam_intense" + + /obj/item/weapon/gun/rifle/sniper/get_examine_text(mob/user) . = ..() if(!has_aimed_shot) @@ -117,7 +123,24 @@ f_aiming_time *= aim_multiplier - var/image/lockon_icon = image(icon = 'icons/effects/Targeted.dmi', icon_state = sniper_rifle.sniper_lockon_icon) + var/beam + var/lockon + + if(istype(sniper_rifle, /obj/item/weapon/gun/rifle/sniper/XM43E1)) + var/obj/item/weapon/gun/rifle/sniper/XM43E1/amr = sniper_rifle + if((amr.focused_fire_counter >= 1) && (target == amr.focused_fire_target?.resolve())) + sniper_rifle.enable_aimed_shot_icon_alt = TRUE + else + sniper_rifle.enable_aimed_shot_icon_alt = FALSE + + if(sniper_rifle.enable_aimed_shot_icon_alt) + beam = sniper_rifle.sniper_beam_icon_max + lockon = sniper_rifle.sniper_lockon_icon_max + else + beam = sniper_rifle.sniper_beam_icon + lockon = sniper_rifle.sniper_lockon_icon + + var/image/lockon_icon = image(icon = 'icons/effects/Targeted.dmi', icon_state = lockon) var/x_offset = -target.pixel_x + target.base_pixel_x var/y_offset = (target.icon_size - world.icon_size) * 0.5 - target.pixel_y + target.base_pixel_y @@ -128,7 +151,7 @@ var/image/lockon_direction_icon if(!sniper_rifle.enable_aimed_shot_laser) - lockon_direction_icon = image(icon = 'icons/effects/Targeted.dmi', icon_state = "[sniper_rifle.sniper_lockon_icon]_direction", dir = get_cardinal_dir(target, human)) + lockon_direction_icon = image(icon = 'icons/effects/Targeted.dmi', icon_state = "[lockon]_direction", dir = get_cardinal_dir(target, human)) lockon_direction_icon.pixel_x = x_offset lockon_direction_icon.pixel_y = y_offset target.overlays += lockon_direction_icon @@ -138,7 +161,7 @@ var/datum/beam/laser_beam if(sniper_rifle.enable_aimed_shot_laser) - laser_beam = target.beam(human, sniper_rifle.sniper_beam_icon, 'icons/effects/beam.dmi', (f_aiming_time + 1 SECONDS), beam_type = sniper_rifle.sniper_beam_type) + laser_beam = target.beam(human, beam, 'icons/effects/beam.dmi', (f_aiming_time + 1 SECONDS), beam_type = sniper_rifle.sniper_beam_type) laser_beam.visuals.alpha = 0 animate(laser_beam.visuals, alpha = initial(laser_beam.visuals.alpha), f_aiming_time, easing = SINE_EASING|EASE_OUT) @@ -204,12 +227,12 @@ var/blocked = FALSE for(var/turf/T in path) - if(T.density || T.opacity) + if(T.density && T.opacity) blocked = TRUE break for(var/obj/O in T) - if(O.get_projectile_hit_boolean(P)) + if(O.get_projectile_hit_boolean(P) && O.opacity) blocked = TRUE break @@ -330,7 +353,7 @@ /obj/item/weapon/gun/rifle/sniper/M42A/set_gun_config_values() ..() - set_fire_delay(FIRE_DELAY_TIER_7*3) + set_fire_delay(FIRE_DELAY_TIER_SNIPER) set_burst_amount(BURST_AMOUNT_TIER_1) accuracy_mult = BASE_ACCURACY_MULT * 3 //you HAVE to be able to hit scatter = SCATTER_AMOUNT_TIER_8 @@ -339,12 +362,15 @@ /obj/item/weapon/gun/rifle/sniper/XM43E1 name = "\improper XM43E1 experimental anti-materiel rifle" - desc = "An experimental anti-materiel rifle produced by Armat Systems, recently reacquired from the deep storage of an abandoned prototyping facility. This one in particular is currently undergoing field testing. Chambered in 10x99mm Caseless." + desc = "An experimental anti-materiel rifle produced by Armat Systems, recently reacquired from the deep storage of an abandoned prototyping facility. This one in particular is currently undergoing field testing. Chambered in 10x99mm Caseless.\nThis weapon can punch through thin metal plating and walls, though it'll lose most of its lethality in the process. It can even work for demolitions, with experienced users known to disassemble segments of solid, reinforced walls in the field with just a single standard magazine of 10x99mm. In lieu of explosives or an engineer, they instead use each of the 8 shots to break down vital structural supports, taking the wall apart in the process." icon = 'icons/obj/items/weapons/guns/guns_by_faction/uscm.dmi' icon_state = "xm43e1" item_state = "xm43e1" unacidable = TRUE indestructible = 1 + aimed_shot_cooldown_delay = 4.5 SECONDS + var/focused_fire_counter = 0 + var/datum/weakref/focused_fire_target = null fire_sound = 'sound/weapons/sniper_heavy.ogg' current_mag = /obj/item/ammo_magazine/sniper/anti_materiel //Renamed from anti-tank to align with new identity/description. Other references have been changed as well. -Kaga @@ -354,9 +380,9 @@ attachable_allowed = list(/obj/item/attachable/bipod) flags_gun_features = GUN_AUTO_EJECTOR|GUN_SPECIALIST|GUN_WIELDED_FIRING_ONLY|GUN_AMMO_COUNTER starting_attachment_types = list(/obj/item/attachable/pmc_sniperbarrel) - sniper_beam_type = /obj/effect/ebeam/laser/intense - sniper_beam_icon = "laser_beam_intense" - sniper_lockon_icon = "sniper_lockon_intense" + sniper_beam_type = /obj/effect/ebeam/laser + sniper_beam_icon = "laser_beam" + sniper_lockon_icon = "sniper_lockon" /obj/item/weapon/gun/rifle/sniper/XM43E1/handle_starting_attachment() ..() @@ -374,8 +400,9 @@ /obj/item/weapon/gun/rifle/sniper/XM43E1/set_gun_config_values() ..() - set_fire_delay(FIRE_DELAY_TIER_6 * 6 )//Big boy damage, but it takes a lot of time to fire a shot. - //Kaga: Adjusted from 56 (Tier 4, 7*8) -> 30 (Tier 6, 5*6) ticks. 95 really wasn't big-boy damage anymore, although I updated it to 125 to remain consistent with the other 10x99mm caliber weapon (M42C). Now takes only twice as long as the M42A. + set_fire_delay(FIRE_DELAY_TIER_AMR)//Big boy damage, but it takes a lot of time to fire a shot. + //Kaga: Fixed back to half the M42A's firerate (3 seconds), using a new define. + //This outright deals less DPS than the normal sniper rifle, 125 vs 140 per 3s. set_burst_amount(BURST_AMOUNT_TIER_1) accuracy_mult = BASE_ACCURACY_MULT + 2*HIT_ACCURACY_MULT_TIER_10 //Who coded this like this, and why? It just calculates out to 1+1=2. Leaving a note here to check back later. scatter = SCATTER_AMOUNT_TIER_10 @@ -385,10 +412,11 @@ /obj/item/weapon/gun/rifle/sniper/XM43E1/set_bullet_traits() LAZYADD(traits_to_give, list( BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_iff), - BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_penetrating), BULLET_TRAIT_ENTRY_ID("turfs", /datum/element/bullet_trait_damage_boost, 11, GLOB.damage_boost_turfs), BULLET_TRAIT_ENTRY_ID("breaching", /datum/element/bullet_trait_damage_boost, 11, GLOB.damage_boost_breaching), - //At 1375 per shot it'll take 1 shot to break resin turfs, and a full mag of 8 to break reinforced walls. + //At 1375 per shot it'll take 1 shot to break resin turfs usually (thick resin at 1350, RNG may vary), and a full mag of 8 to break reinforced walls. + //However, the second wall it hits will only take 550 damage, unable to even kill a full-health normal resin wall (900). + //Much more effective at breaking resin doors and membranes, which have less HP and slow down the projectile less. BULLET_TRAIT_ENTRY_ID("pylons", /datum/element/bullet_trait_damage_boost, 6, GLOB.damage_boost_pylons) //At 750 per shot it'll take 3 to break a Pylon (1800 HP). No Damage Boost vs other xeno structures yet, those will require a whole new list w/ the damage_boost trait. )) diff --git a/code/modules/projectiles/magazines/specialist.dm b/code/modules/projectiles/magazines/specialist.dm index 761e77305254..698d9efeaca2 100644 --- a/code/modules/projectiles/magazines/specialist.dm +++ b/code/modules/projectiles/magazines/specialist.dm @@ -30,7 +30,7 @@ //XM43E1 Magazine /obj/item/ammo_magazine/sniper/anti_materiel name = "\improper XM43E1 marksman magazine (10x99mm)" - desc = "A magazine of caseless 10x99mm anti-materiel rounds." + desc = "A magazine of caseless 10x99mm anti-materiel rounds, capable of penetrating through most infantry-level materiel. Depending on what you hit, it might even have enough energy to wound anything behind the target." max_rounds = 8 caliber = "10x99mm" default_ammo = /datum/ammo/bullet/sniper/anti_materiel diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index eb9b2686c3d6..3aaeff519456 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -80,6 +80,16 @@ /// How much to make the bullet fall off by accuracy-wise when closer than the ideal range var/accuracy_range_falloff = 10 + /// Is this a lone (0), original (1), or bonus (2) projectile. Used in gun.dm and fire_bonus_projectiles() currently. + var/bonus_projectile_check = 0 + + /// What atom did this last receive a registered signal from? Used by damage_boost.dm + var/datum/weakref/last_atom_signaled = null + + /// Was this projectile affected by damage_boost.dm? If so, what was the last modifier? + var/damage_boosted = 0 + var/last_damage_mult = 1 + /obj/projectile/Initialize(mapload, datum/cause_data/cause_data) . = ..() path = list() @@ -164,6 +174,12 @@ apply_bullet_trait(L) /obj/projectile/proc/calculate_damage() + + if(damage_boosted) + damage = damage / last_damage_mult + damage_boosted-- + last_damage_mult = 1 + if(effective_range_min && distance_travelled < effective_range_min) return max(0, damage - round((effective_range_min - distance_travelled) * damage_buildup)) else if(distance_travelled > effective_range_max) @@ -215,6 +231,7 @@ //If we have the right kind of ammo, we can fire several projectiles at once. if(ammo.bonus_projectiles_amount && ammo.bonus_projectiles_type) ammo.fire_bonus_projectiles(src) + bonus_projectile_check = 1 //Mark this projectile as having spawned a set of bonus projectiles. path = get_line(starting, target_turf) p_x += clamp((rand()-0.5)*scatter*3, -8, 8) diff --git a/colonialmarines.dme b/colonialmarines.dme index 2f859e19fd37..7a0ad67f5f59 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -492,6 +492,7 @@ #include "code\datums\elements\bullet_trait\incendiary.dm" #include "code\datums\elements\bullet_trait\penetrating\heavy.dm" #include "code\datums\elements\bullet_trait\penetrating\penetrating.dm" +#include "code\datums\elements\bullet_trait\penetrating\weak.dm" #include "code\datums\elements\traitbound\_traitbound.dm" #include "code\datums\elements\traitbound\crawler.dm" #include "code\datums\elements\traitbound\gun_silenced.dm" diff --git a/icons/obj/items/weapons/guns/lineart.dmi b/icons/obj/items/weapons/guns/lineart.dmi index 6c1dae5bd5c1c37600b270f22be512343c682756..a746bce1716b31ca6ee35585e290962bec69711e 100644 GIT binary patch delta 545 zcmV++0^a@5g#pin0gxnr26|LjbVOxyV{&P5bZKvH004NLm6y$Kqc9YO*WoEpyC3mm z0xY^2wHkFXeFfwM2M`!rwn>_|AJdFxH#(;~8-23xJ@?0#=k({>ulJAi_4j*nRnAXl zO1{~aSQw#_meq6gMleTL}3Mspm>IxjF53&@(Aay;3czQG2GeHzWun=N2& zTf?*tkeh?RjxVBr&~pwkogWzn8x22L__3VQ(Q3A5{cfHOaaPsPUtONNk||a)2z6mLeE8iL8xUm%vi!fi@V->ua8(z zE2ni)9v6e1al%9fcZCRH5v=uNUgM7DXX9)fu4%Btcl($a!A?FHJR8OtQo2zQqm*X{ zG{0G=wCQe`y!Kg!+oHdf6;79(YA|(oae`5dA3=IzDhqe3YFs!E3^&Lx4oh;iHTLyW ji&A0JK6UjIU+6h~`*{8O3H$@LeBi|IY zPDVgVnY3sX=4fz@wt-aJ6z2e`uFfzcd##2G)}tdG_{+)QbT_FB=EG>?ur4$xTh&9j zSDreQbiQYe?7w5=*)>^)L-fF;GaW<+Hk#v5)_H-kR6x$&wC73LeuIq)`!w21Z`X*q zb&1n5KyHo#JAM&=g_(1Rnf%Bw*r)_4;<`fMblD)RmPzri8H!+Vze-GtMK>bH{bhr= zqcn$6aTYmFYaiYUU0k)f&;e)7$c zsKJ`3i4&1`r{avEhDh|LWC~DqIia`mB_V{4~`Rf6aeBynVcW{R931*wx^WKw1ZavoivmK#A86ivR!s From 283db6e8cb1c4231d1d0aed925acdb2ec1e16781 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 6 Apr 2024 06:48:08 +0100 Subject: [PATCH 02/10] Automatic changelog for PR #5774 [ci skip] --- html/changelogs/AutoChangeLog-pr-5774.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5774.yml diff --git a/html/changelogs/AutoChangeLog-pr-5774.yml b/html/changelogs/AutoChangeLog-pr-5774.yml new file mode 100644 index 000000000000..ec415ec2cd3f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5774.yml @@ -0,0 +1,5 @@ +author: "Kaga" +delete-after: True +changes: + - rscadd: "Added the XM43E1 Anti-Materiel Rifle as a pickable sidegrade option for USCM Sniper Specs through the vendor, mutually exclusive with the normal M42A Sniper Rifle. The AMR can shred obstacles, blow a hole through most infantry and their equipment, and even hit a collateral on multiple targets or through light cover, but its lack of specialized ammunition and small magazine mean that you'll need to make every shot count. Ammo can be bought from Req, at the same cost per magazine as the M42A." + - balance: "Completely updated the XM43E1 AMR, adding damage falloff to its wall-piercing properties whenever it hits something alongside range loss. Most things reduce its damage by 20%. Dense walls and big xenos reduce both the damage and range 3 times as much." \ No newline at end of file From 7609c576eba96ed466b2659845995736c215e15d Mon Sep 17 00:00:00 2001 From: cuberound <122645057+cuberound@users.noreply.github.com> Date: Sat, 6 Apr 2024 07:53:16 +0200 Subject: [PATCH 03/10] reduces impact area from 9x9 to 7x7, lowers DPS slightly (#6031) # About the pull request okey this is basicly copy of my suggested buff from more then a year ago, but now it is a nerf. Main complain on why the GAU is so OP and strong is its spreat ( that used to be the reason why it was so weak and shit). Therfore, lets do some math. Current situation is that the GAU fires 80 rounds per burst at area of 81 tiles( 9x9) ~0,988 round per tile, the proposed change aims the keep the damage on anyone standing in the impact area about the same ( it is a bit lower might need compensation with bringing the damage to pre double bullet values) , by bringing down the impact area to 49 tiles (7x7) and lowering the number of shots fired to 40, we get value of ~0,816 round per tile. meanwhile it keeps the rest like how long the bursts last the just as long. (https://github.com/cmss13-devs/cmss13/pull/4302 the old "buff") # Explain why it's good for the game Making two bullets per bullet was not a bad idea in general, it brought the DPS on competetive level with other weapons, but the huge spread leads to too big compared to other ordinance. Bringing it in line with other ordinance should allow for easier CAS balance around smaller impact FMs. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: balance: reduced GAU spread from 9x9 to 7x7 balance: reduced GAU shots fired per shot back to 1 (bullets per tile from 0.99 to 0.82) /:cl: Co-authored-by: vincibrv --- code/modules/cm_marines/dropship_ammo.dm | 43 ++++++++++++------------ 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/code/modules/cm_marines/dropship_ammo.dm b/code/modules/cm_marines/dropship_ammo.dm index 93768e218375..0926f2bcefff 100644 --- a/code/modules/cm_marines/dropship_ammo.dm +++ b/code/modules/cm_marines/dropship_ammo.dm @@ -144,7 +144,7 @@ ammo_used_per_firing = 40 point_cost = 275 fire_mission_delay = 2 - var/bullet_spread_range = 4 //how far from the real impact turf can bullets land + var/bullet_spread_range = 3 //how far from the real impact turf can bullets land var/shrapnel_type = /datum/ammo/bullet/shrapnel/gau //For siming 30mm bullet impacts. var/directhit_damage = 105 //how much damage is to be inflicted to a mob, this is here so that we can hit resting mobs. var/penetration = 10 //AP value pretty much @@ -169,27 +169,26 @@ for(var/i = 1 to ammo_used_per_firing) sleep(1) - for(var/j in 1 to 2) //rather than halving the sleep, were doubling the bullets shot "bang" - var/turf/impact_tile = pick(turf_list) - var/datum/cause_data/cause_data = create_cause_data(fired_from.name, source_mob) - impact_tile.ex_act(EXPLOSION_THRESHOLD_VLOW, pick(GLOB.alldirs), cause_data) - create_shrapnel(impact_tile,1,0,0,shrapnel_type,cause_data,FALSE,100) //simulates a bullet - for(var/atom/movable/explosion_effect in impact_tile) - if(iscarbon(explosion_effect)) - var/mob/living/carbon/bullet_effect = explosion_effect - explosion_effect.ex_act(EXPLOSION_THRESHOLD_VLOW, null, cause_data) - bullet_effect.apply_armoured_damage(directhit_damage,ARMOR_BULLET,BRUTE,null,penetration) - else - explosion_effect.ex_act(EXPLOSION_THRESHOLD_VLOW) - new /obj/effect/particle_effect/expl_particles(impact_tile) - if(!soundplaycooldown) //so we don't play the same sound 20 times very fast. - playsound(impact_tile, 'sound/effects/gauimpact.ogg',40,1,20) - soundplaycooldown = 3 - soundplaycooldown-- - if(!debriscooldown) - impact_tile.ceiling_debris_check(1) - debriscooldown = 6 - debriscooldown-- + var/turf/impact_tile = pick(turf_list) + var/datum/cause_data/cause_data = create_cause_data(fired_from.name, source_mob) + impact_tile.ex_act(EXPLOSION_THRESHOLD_VLOW, pick(GLOB.alldirs), cause_data) + create_shrapnel(impact_tile,1,0,0,shrapnel_type,cause_data,FALSE,100) //simulates a bullet + for(var/atom/movable/explosion_effect in impact_tile) + if(iscarbon(explosion_effect)) + var/mob/living/carbon/bullet_effect = explosion_effect + explosion_effect.ex_act(EXPLOSION_THRESHOLD_VLOW, null, cause_data) + bullet_effect.apply_armoured_damage(directhit_damage,ARMOR_BULLET,BRUTE,null,penetration) + else + explosion_effect.ex_act(EXPLOSION_THRESHOLD_VLOW) + new /obj/effect/particle_effect/expl_particles(impact_tile) + if(!soundplaycooldown) //so we don't play the same sound 20 times very fast. + playsound(impact_tile, 'sound/effects/gauimpact.ogg',40,1,20) + soundplaycooldown = 3 + soundplaycooldown-- + if(!debriscooldown) + impact_tile.ceiling_debris_check(1) + debriscooldown = 6 + debriscooldown-- sleep(11) //speed of sound simulation playsound(impact, 'sound/effects/gau.ogg',100,1,60) From 437cab4bea2600d5a4aeb013ddffb14806a51f16 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 6 Apr 2024 07:01:35 +0100 Subject: [PATCH 04/10] Automatic changelog for PR #6031 [ci skip] --- html/changelogs/AutoChangeLog-pr-6031.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-6031.yml diff --git a/html/changelogs/AutoChangeLog-pr-6031.yml b/html/changelogs/AutoChangeLog-pr-6031.yml new file mode 100644 index 000000000000..c70d02897d01 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-6031.yml @@ -0,0 +1,5 @@ +author: "cuberound" +delete-after: True +changes: + - balance: "reduced GAU spread from 9x9 to 7x7" + - balance: "reduced GAU shots fired per shot back to 1 (bullets per tile from 0.99 to 0.82)" \ No newline at end of file From fa7fba92d772e9aaaaa0c446e83856780f9edabf Mon Sep 17 00:00:00 2001 From: InsaneRed <47158596+InsaneRed@users.noreply.github.com> Date: Sat, 6 Apr 2024 10:13:33 +0300 Subject: [PATCH 05/10] gives proper light armor slowdown to goon armor (#6059) # About the pull request Gives light_slowdown to the goon armor that spawns on survivors, since it has BETTER THEN LIGHTARMOR without any slowdown # Explain why it's good for the game People are powergaming with survivor armor and i dont like it, also the goon armor is described as being shady and bad. Doesnt make sense it has nearly medium stats with no slowdown at all. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: balance: Goon armor properly has slowdown now /:cl: --------- Co-authored-by: InsaneRed Co-authored-by: Drathek <76988376+Drulikar@users.noreply.github.com> --- code/modules/clothing/suits/marine_armor/ert.dm | 3 +-- code/modules/clothing/under/marine_uniform.dm | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/code/modules/clothing/suits/marine_armor/ert.dm b/code/modules/clothing/suits/marine_armor/ert.dm index 19009606db35..6d2ad9934a40 100644 --- a/code/modules/clothing/suits/marine_armor/ert.dm +++ b/code/modules/clothing/suits/marine_armor/ert.dm @@ -79,13 +79,12 @@ item_state = "armor" item_state_slots = null contained_sprite = TRUE + slowdown = SLOWDOWN_ARMOR_LIGHT flags_armor_protection = BODY_FLAG_CHEST flags_cold_protection = BODY_FLAG_CHEST flags_heat_protection = BODY_FLAG_CHEST - slowdown = SLOWDOWN_ARMOR_NONE // only protects chest, but enables rapid movement - /obj/item/clothing/suit/storage/marine/veteran/pmc/light/corporate/lead desc = "A basic vest with a Weyland-Yutani badge on the right breast. This variant is worn by low-level guards that have elevated in rank due to 'good conduct in the field', also known as corporate bootlicking." icon_state = "lead_armor" diff --git a/code/modules/clothing/under/marine_uniform.dm b/code/modules/clothing/under/marine_uniform.dm index f7dd5be511ce..92eeea638fae 100644 --- a/code/modules/clothing/under/marine_uniform.dm +++ b/code/modules/clothing/under/marine_uniform.dm @@ -896,7 +896,7 @@ item_icons = list( WEAR_BODY = 'icons/mob/humans/onmob/uniform_1.dmi', ) - + /obj/item/clothing/under/marine/reporter/black icon_state = "cc_black" worn_state = "cc_black" From e619c331151897d9230b59c47f04c9e8f2d0b5a4 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 6 Apr 2024 08:19:18 +0100 Subject: [PATCH 06/10] Automatic changelog for PR #6059 [ci skip] --- html/changelogs/AutoChangeLog-pr-6059.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-6059.yml diff --git a/html/changelogs/AutoChangeLog-pr-6059.yml b/html/changelogs/AutoChangeLog-pr-6059.yml new file mode 100644 index 000000000000..e4b85ee392e9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-6059.yml @@ -0,0 +1,4 @@ +author: "InsaneRed" +delete-after: True +changes: + - balance: "Goon armor properly has slowdown now" \ No newline at end of file From c94bda6e80a20e87ccc2d5453871e4f590442594 Mon Sep 17 00:00:00 2001 From: Ben <91219575+Ben10083@users.noreply.github.com> Date: Sat, 6 Apr 2024 03:39:17 -0400 Subject: [PATCH 07/10] Super Strength Grab Changes (Never skip arm day) (#6020) # About the pull request Grabs for those with super strength buffed - passive grabs no longer guarantied resist - target has reduced chance to resist - if the target, _increased_ chance to resist - if both have super strength, works as before (passive grabs guarantied for example) # Explain why it's good for the game Grabs are not often used, even though roles such as WJs are encouraged to do so. This is because grabs are very easy to be resisted out of. This is fine, and is necessary, but in this case, I want to have it so if someone has an advantage in strength, they have advantage in this exchange (both dealing and recieving). I feel this was more an oversight, as logically those who are stronger will have a stronger grip than otherwise. The balance out between the two is to prevent shenanigans between synth and pred, no grab fights there. scenario 1) Synth vs Human Synth has strength, human doesn't advantage synth scenario 2) human vs Pred Pred has strength, human doesnt advantage pred scenario 3) Synth vs Pred Both have strength No advantage (how it is now) # Changelog :cl: balance: Mobs with super strength now have an advantage when resisting of a grab is done, both as the grabber and the one being grabbed (Xenos not affected, works as before). balance: Mobs with super strength no longer have their passive grabs a guaranteed resist (unless both super). /:cl: --------- Co-authored-by: Ben10083 --- .../mob/living/carbon/human/human_helpers.dm | 10 ++--- code/modules/mob/living/living.dm | 37 +++++++++++++------ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index c38ddbcd552c..d6d438441d20 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -206,18 +206,18 @@ return FALSE -/mob/living/carbon/human/is_mob_restrained(check_grab = 1) +/mob/living/carbon/human/is_mob_restrained(check_grab = TRUE) if(check_grab && pulledby && pulledby.grab_level >= GRAB_AGGRESSIVE) - return 1 + return TRUE if (handcuffed) - return 1 + return TRUE if (istype(wear_suit, /obj/item/clothing/suit/straight_jacket)) - return 1 + return TRUE if (HAS_TRAIT(src, TRAIT_NESTED)) return TRUE - return 0 + return FALSE /mob/living/carbon/human/proc/disable_special_flags() status_flags |= CANPUSH diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index d5190318715c..40a06b253c3e 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -224,19 +224,34 @@ /mob/living/resist_grab(moving_resist) if(!pulledby) return - if(pulledby.grab_level) - if(prob(50)) - playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 7) - visible_message(SPAN_DANGER("[src] has broken free of [pulledby]'s grip!"), null, null, 5) - pulledby.stop_pulling() - return 1 - if(moving_resist && client) //we resisted by trying to move - visible_message(SPAN_DANGER("[src] struggles to break free of [pulledby]'s grip!"), null, null, 5) - client.next_movement = world.time + (10*pulledby.grab_level) + client.move_delay - else + // vars for checks of strengh + var/pulledby_is_strong = HAS_TRAIT(pulledby, TRAIT_SUPER_STRONG) + var/src_is_strong = HAS_TRAIT(src, TRAIT_SUPER_STRONG) + + if(!pulledby.grab_level && (!pulledby_is_strong || src_is_strong)) // if passive grab, check if puller is stronger than src, and if not, break free pulledby.stop_pulling() - return 1 + return TRUE + // Chance for person to break free of grip, defaults to 50. + var/chance = 50 + if(src_is_strong && !isxeno(pulledby)) // no extra chance to resist warrior grabs + chance += 30 // you are strong, you can overpower them easier + if(pulledby_is_strong) + chance -= 30 // stronger grip + // above code means that if you are super strong, 80% chance to resist, otherwise, 20 percent. if both are super strong, standard 50. + + if(prob(chance)) + playsound(loc, 'sound/weapons/thudswoosh.ogg', 25, 1, 7) + visible_message(SPAN_DANGER("[src] has broken free of [pulledby]'s grip!"), max_distance = 5) + pulledby.stop_pulling() + return TRUE + if(moving_resist && client) //we resisted by trying to move + visible_message(SPAN_DANGER("[src] struggles to break free of [pulledby]'s grip!"), max_distance = 5) + // +1 delay if super strong, also done as passive grabs would have a modifier of 0 otherwise, causing spam + if(pulledby_is_strong && !src_is_strong) + client.next_movement = world.time + (10*(pulledby.grab_level + 1)) + client.move_delay + else + client.next_movement = world.time + (10*pulledby.grab_level) + client.move_delay /mob/living/movement_delay() . = ..() From 724f306298c1fb79c0b7b0095fb4e23620de5081 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 6 Apr 2024 08:43:59 +0100 Subject: [PATCH 08/10] Automatic changelog for PR #6020 [ci skip] --- html/changelogs/AutoChangeLog-pr-6020.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-6020.yml diff --git a/html/changelogs/AutoChangeLog-pr-6020.yml b/html/changelogs/AutoChangeLog-pr-6020.yml new file mode 100644 index 000000000000..3ac915900c1e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-6020.yml @@ -0,0 +1,5 @@ +author: "Ben10083" +delete-after: True +changes: + - balance: "Mobs with super strength now have an advantage when resisting of a grab is done, both as the grabber and the one being grabbed (Xenos not affected, works as before)." + - balance: "Mobs with super strength no longer have their passive grabs a guaranteed resist (unless both super)." \ No newline at end of file From 0657af97b701d4a910353dd1323281380bc05599 Mon Sep 17 00:00:00 2001 From: Drathek <76988376+Drulikar@users.noreply.github.com> Date: Sat, 6 Apr 2024 01:15:41 -0700 Subject: [PATCH 09/10] Fix runtimes when sending messages for ERT objectives (#6085) # About the pull request This PR fixes ERTs that were sending objectives to the player using an incorrect proc reference. # Explain why it's good for the game Objectives are intended to be given to the player. Fixes ![image](https://github.com/cmss13-devs/cmss13/assets/76988376/27a4f149-5d7c-436e-a501-c49f414db878) # Testing Photographs and Procedure
Screenshots & Videos ![image](https://github.com/cmss13-devs/cmss13/assets/76988376/20ab18c6-87e7-4765-a55a-88093ebb23e9)
# Changelog :cl: Drathek fix: Fixed objectives not getting displayed for CMB and cultist ERTs. /:cl: --- code/datums/emergency_calls/cmb.dm | 4 ++-- code/datums/emergency_calls/inspection.dm | 4 ++-- code/datums/emergency_calls/xeno_cultists.dm | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/datums/emergency_calls/cmb.dm b/code/datums/emergency_calls/cmb.dm index feb31cf0fe16..bbe2e161842a 100644 --- a/code/datums/emergency_calls/cmb.dm +++ b/code/datums/emergency_calls/cmb.dm @@ -54,7 +54,7 @@ print_backstory(mob) - addtimer(CALLBACK(GLOBAL_PROC, PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) /datum/emergency_call/cmb/print_backstory(mob/living/carbon/human/M) @@ -160,7 +160,7 @@ print_backstory(mob) - addtimer(CALLBACK(GLOBAL_PROC, PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) /datum/emergency_call/cmb/anchorpoint/print_backstory(mob/living/carbon/human/M) diff --git a/code/datums/emergency_calls/inspection.dm b/code/datums/emergency_calls/inspection.dm index 031a9f210761..e473466f4fb8 100644 --- a/code/datums/emergency_calls/inspection.dm +++ b/code/datums/emergency_calls/inspection.dm @@ -258,7 +258,7 @@ print_backstory(mob) - addtimer(CALLBACK(GLOBAL_PROC, PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) /datum/emergency_call/inspection_cmb/print_backstory(mob/living/carbon/human/M) @@ -341,4 +341,4 @@ print_backstory(mob) - addtimer(CALLBACK(GLOBAL_PROC, PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), mob, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) diff --git a/code/datums/emergency_calls/xeno_cultists.dm b/code/datums/emergency_calls/xeno_cultists.dm index f112511b5e22..5a69c4002105 100644 --- a/code/datums/emergency_calls/xeno_cultists.dm +++ b/code/datums/emergency_calls/xeno_cultists.dm @@ -34,4 +34,4 @@ arm_equipment(H, /datum/equipment_preset/other/xeno_cultist, TRUE, TRUE) print_backstory(H) - addtimer(CALLBACK(GLOBAL_PROC, PROC_REF(to_chat), H, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), H, SPAN_BOLD("Objectives: [objectives]")), 1 SECONDS) From 00df7a1a85a3e850df6c421bf0e41e8e199aad82 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 6 Apr 2024 09:22:18 +0100 Subject: [PATCH 10/10] Automatic changelog for PR #6085 [ci skip] --- html/changelogs/AutoChangeLog-pr-6085.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-6085.yml diff --git a/html/changelogs/AutoChangeLog-pr-6085.yml b/html/changelogs/AutoChangeLog-pr-6085.yml new file mode 100644 index 000000000000..c6891e04a6f4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-6085.yml @@ -0,0 +1,4 @@ +author: "Drathek" +delete-after: True +changes: + - bugfix: "Fixed objectives not getting displayed for CMB and cultist ERTs." \ No newline at end of file