Skip to content

Commit

Permalink
Refactor ammo_datums file. (#4682)
Browse files Browse the repository at this point in the history
# About the pull request

<!-- Remove this text and explain what the purpose of your PR is.

Mention if you have tested your changes. If you changed a map, make sure
you used the mapmerge tool.
If this is an Issue Correction, you can type "Fixes Issue #169420" to
link the PR to the corresponding Issue number #169420.

Remember: something that is self-evident to you might not be to others.
Explain your rationale fully, even if you feel it goes without saying.
-->

# Explain why it's good for the game
# Testing Photographs and Procedure
<details>
<summary>Screenshots & Videos</summary>

Put screenshots and videos here with an empty line between the
screenshots and the `<details>` tags.

</details>


# Changelog
:cl:
refactor: refactor ammo_datums file.
/:cl:

---------

Co-authored-by: Julien <[email protected]>
  • Loading branch information
Huffie56 and Julien committed Oct 18, 2023
1 parent 297584d commit 65d9e8e
Show file tree
Hide file tree
Showing 18 changed files with 3,375 additions and 3,448 deletions.
237 changes: 237 additions & 0 deletions code/datums/ammo/ammo.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/datum/ammo
var/name = "generic bullet"
//Icon state when a human is permanently killed with it by execution/suicide.
var/headshot_state = null
var/icon = 'icons/obj/items/weapons/projectiles.dmi'
var/icon_state = "bullet"
/// The icon that is displayed when the bullet bounces off something.
var/ping = "ping_b"
/// When it deals damage.
var/sound_hit
/// When it's blocked by human armor.
var/sound_armor
/// When it misses someone.
var/sound_miss
/// When it bounces off something.
var/sound_bounce
/// When the bullet is absorbed by a xeno_shield
var/sound_shield_hit
/// Snipers use this to simulate poor accuracy at close ranges
var/accurate_range_min = 0
/// How much the ammo scatters when burst fired, added to gun scatter, along with other mods
var/scatter = 0
var/stamina_damage = 0
/// This is the base damage of the bullet as it is fired
var/damage = 0
/// BRUTE, BURN, TOX, OXY, CLONE are the only things that should be in here
var/damage_type = BRUTE
/// How much armor it ignores before calculations take place
var/penetration = 0
/// The % chance it will imbed in a human
var/shrapnel_chance = 0
/// The shrapnel type the ammo will embed, if the chance rolls
var/shrapnel_type = 0
/// Type path of the extra projectiles
var/bonus_projectiles_type
/// How many extra projectiles it shoots out. Works kind of like firing on burst, but all of the projectiles travel together
var/bonus_projectiles_amount = 0
/// Stun,knockdown,knockout,irradiate,stutter,eyeblur,drowsy,agony
var/debilitate[] = null
/// how much armor breaking will be done per point of penetration. This is for weapons that penetrate with their shape (like needle bullets)
var/pen_armor_punch = 0.5
/// how much armor breaking is done by sheer weapon force. This is for big blunt weapons
var/damage_armor_punch = 0.5
/// if we should play a special sound when firing.
var/sound_override = null
var/flags_ammo_behavior = NO_FLAGS

/// This is added to the bullet's base accuracy.
var/accuracy = HIT_ACCURACY_TIER_1
/// How much the accuracy varies when fired. // This REDUCES the lower bound of accuracy variance by 2%, to 96%.
var/accuracy_var_low = PROJECTILE_VARIANCE_TIER_9
/// This INCREASES the upper bound of accuracy variance by 2%, to 107%.
var/accuracy_var_high = PROJECTILE_VARIANCE_TIER_9
/// For most guns, this is where the bullet dramatically looses accuracy. Not for snipers though.
var/accurate_range = 6
/// This will de-increment a counter on the bullet.
var/max_range = 22
/// Same as with accuracy variance.
var/damage_var_low = PROJECTILE_VARIANCE_TIER_9
/// This INCREASES the upper bound of damage variance by 2%, to 107%.
var/damage_var_high = PROJECTILE_VARIANCE_TIER_9
/// How much damage the bullet loses per turf traveled after the effective range
var/damage_falloff = DAMAGE_FALLOFF_TIER_10
/// How much damage the bullet loses per turf away before the effective range
var/damage_buildup = DAMAGE_BUILDUP_TIER_1
/// What minimum range the ammo deals full damage, builds up the closer you get. 0 for no minimum. Added onto gun range as a modifier.
var/effective_range_min = EFFECTIVE_RANGE_OFF
/// What maximum range the ammo deals full damage, tapers off using damage_falloff after hitting this value. 0 for no maximum. Added onto gun range as a modifier.
var/effective_range_max = EFFECTIVE_RANGE_OFF
/// How fast the projectile moves.
var/shell_speed = AMMO_SPEED_TIER_1

var/handful_type = /obj/item/ammo_magazine/handful
var/handful_color
/// custom handful sprite, for shotgun shells or etc.
var/handful_state = "bullet"
/// so handfuls say 'buckshot shells' not 'shell'
var/multiple_handful_name

/// Does this apply xenomorph behaviour delegate?
var/apply_delegate = TRUE

/// An assoc list in the format list(/datum/element/bullet_trait_to_give = list(...args))
/// that will be given to a projectile with the current ammo datum
var/list/list/traits_to_give

var/flamer_reagent_type = /datum/reagent/napalm/ut

/// The flicker that plays when a bullet hits a target. Usually red. Can be nulled so it doesn't show up at all.
var/hit_effect_color = "#FF0000"

/datum/ammo/New()
set_bullet_traits()

/datum/ammo/proc/on_bullet_generation(obj/projectile/generated_projectile, mob/bullet_generator) //NOT used on New(), applied to the projectiles.
return

/// Populate traits_to_give in this proc
/datum/ammo/proc/set_bullet_traits()
return

/datum/ammo/can_vv_modify()
return FALSE

/datum/ammo/proc/do_at_half_range(obj/projectile/P)
SHOULD_NOT_SLEEP(TRUE)
return

/datum/ammo/proc/on_embed(mob/embedded_mob, obj/limb/target_organ)
return

/datum/ammo/proc/do_at_max_range(obj/projectile/P)
SHOULD_NOT_SLEEP(TRUE)
return

/datum/ammo/proc/on_shield_block(mob/M, obj/projectile/P) //Does it do something special when shield blocked? Ie. a flare or grenade that still blows up.
return

/datum/ammo/proc/on_hit_turf(turf/T, obj/projectile/P) //Special effects when hitting dense turfs.
SHOULD_NOT_SLEEP(TRUE)
return

/datum/ammo/proc/on_hit_mob(mob/M, obj/projectile/P, mob/user) //Special effects when hitting mobs.
SHOULD_NOT_SLEEP(TRUE)
return

///Special effects when pointblanking mobs. Ultimately called from /living/attackby(). Return TRUE to end the PB attempt.
/datum/ammo/proc/on_pointblank(mob/living/L, obj/projectile/P, mob/living/user, obj/item/weapon/gun/fired_from)
return

/datum/ammo/proc/on_hit_obj(obj/O, obj/projectile/P) //Special effects when hitting objects.
SHOULD_NOT_SLEEP(TRUE)
return

/datum/ammo/proc/on_near_target(turf/T, obj/projectile/P) //Special effects when passing near something. Range of things that triggers it is controlled by other ammo flags.
return 0 //return 0 means it flies even after being near something. Return 1 means it stops

/datum/ammo/proc/knockback(mob/living/living_mob, obj/projectile/fired_projectile, max_range = 2)
if(!living_mob || living_mob == fired_projectile.firer)
return
if(fired_projectile.distance_travelled > max_range || living_mob.lying)
return //Two tiles away or more, basically.

if(living_mob.mob_size >= MOB_SIZE_BIG)
return //Big xenos are not affected.

shake_camera(living_mob, 3, 4)
knockback_effects(living_mob, fired_projectile)
slam_back(living_mob, fired_projectile)

/datum/ammo/proc/slam_back(mob/living/living_mob, obj/projectile/fired_projectile)
/// Either knockback or slam them into an obstacle.
var/direction = Get_Compass_Dir(fired_projectile.z ? fired_projectile : fired_projectile.firer, living_mob) //More precise than get_dir.
if(!direction) //Same tile.
return
if(!step(living_mob, direction))
living_mob.animation_attack_on(get_step(living_mob, direction))
playsound(living_mob.loc, "punch", 25, 1)
living_mob.visible_message(SPAN_DANGER("[living_mob] slams into an obstacle!"),
isxeno(living_mob) ? SPAN_XENODANGER("You slam into an obstacle!") : SPAN_HIGHDANGER("You slam into an obstacle!"), null, 4, CHAT_TYPE_TAKING_HIT)
living_mob.apply_damage(MELEE_FORCE_TIER_2)

///The applied effects for knockback(), overwrite to change slow/stun amounts for different ammo datums
/datum/ammo/proc/knockback_effects(mob/living/living_mob, obj/projectile/fired_projectile)
if(iscarbonsizexeno(living_mob))
var/mob/living/carbon/xenomorph/target = living_mob
target.apply_effect(0.7, WEAKEN) // 0.9 seconds of stun, per agreement from Balance Team when switched from MC stuns to exact stuns
target.apply_effect(1, SUPERSLOW)
target.apply_effect(2, SLOW)
to_chat(target, SPAN_XENODANGER("You are shaken by the sudden impact!"))
else
living_mob.apply_stamina_damage(fired_projectile.ammo.damage, fired_projectile.def_zone, ARMOR_BULLET)

/datum/ammo/proc/pushback(mob/target_mob, obj/projectile/fired_projectile, max_range = 2)
if(!target_mob || target_mob == fired_projectile.firer || fired_projectile.distance_travelled > max_range || target_mob.lying)
return

if(target_mob.mob_size >= MOB_SIZE_BIG)
return //too big to push

to_chat(target_mob, isxeno(target_mob) ? SPAN_XENODANGER("You are pushed back by the sudden impact!") : SPAN_HIGHDANGER("You are pushed back by the sudden impact!"), null, 4, CHAT_TYPE_TAKING_HIT)
slam_back(target_mob, fired_projectile, max_range)

/datum/ammo/proc/burst(atom/target, obj/projectile/P, damage_type = BRUTE, range = 1, damage_div = 2, show_message = SHOW_MESSAGE_VISIBLE) //damage_div says how much we divide damage
if(!target || !P) return
for(var/mob/living/carbon/M in orange(range,target))
if(P.firer == M)
continue
if(show_message)
var/msg = "You are hit by backlash from \a </b>[P.name]</b>!"
M.visible_message(SPAN_DANGER("[M] is hit by backlash from \a [P.name]!"),isxeno(M) ? SPAN_XENODANGER("[msg]"):SPAN_HIGHDANGER("[msg]"))
var/damage = P.damage/damage_div

var/mob/living/carbon/xenomorph/XNO = null

if(isxeno(M))
XNO = M
var/total_explosive_resistance = XNO.caste.xeno_explosion_resistance + XNO.armor_explosive_buff
damage = armor_damage_reduction(GLOB.xeno_explosive, damage, total_explosive_resistance , 60, 0, 0.5, XNO.armor_integrity)
var/armor_punch = armor_break_calculation(GLOB.xeno_explosive, damage, total_explosive_resistance, 60, 0, 0.5, XNO.armor_integrity)
XNO.apply_armorbreak(armor_punch)

M.apply_damage(damage,damage_type)

if(XNO && XNO.xeno_shields.len)
P.play_shielded_hit_effect(M)
else
P.play_hit_effect(M)

/datum/ammo/proc/fire_bonus_projectiles(obj/projectile/original_P)
set waitfor = 0

var/turf/curloc = get_turf(original_P.shot_from)
var/initial_angle = Get_Angle(curloc, original_P.target_turf)

for(var/i in 1 to bonus_projectiles_amount) //Want to run this for the number of bonus projectiles.
var/final_angle = initial_angle

var/obj/projectile/P = new /obj/projectile(curloc, original_P.weapon_cause_data)
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)

var/total_scatter_angle = P.scatter
final_angle += rand(-total_scatter_angle, total_scatter_angle)
var/turf/new_target = get_angle_target_turf(curloc, final_angle, 30)

P.fire_at(new_target, original_P.firer, original_P.shot_from, P.ammo.max_range, P.ammo.shell_speed, original_P.original) //Fire!

/datum/ammo/proc/drop_flame(turf/T, datum/cause_data/cause_data) // ~Art updated fire 20JAN17
if(!istype(T))
return
if(locate(/obj/flamer_fire) in T)
return

var/datum/reagent/R = new flamer_reagent_type()
new /obj/flamer_fire(T, cause_data, R)
81 changes: 81 additions & 0 deletions code/datums/ammo/bullet/bullet.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
//======
Default Ammo
//======
*/
//Only when things screw up do we use this as a placeholder.
/datum/ammo/bullet
name = "default bullet"
icon_state = "bullet"
headshot_state = HEADSHOT_OVERLAY_LIGHT
flags_ammo_behavior = AMMO_BALLISTIC
sound_hit = "ballistic_hit"
sound_armor = "ballistic_armor"
sound_miss = "ballistic_miss"
sound_bounce = "ballistic_bounce"
sound_shield_hit = "ballistic_shield_hit"

accurate_range_min = 0
damage = 10
shrapnel_chance = SHRAPNEL_CHANCE_TIER_1
shrapnel_type = /obj/item/shard/shrapnel
shell_speed = AMMO_SPEED_TIER_4

/datum/ammo/bullet/proc/handle_battlefield_execution(datum/ammo/firing_ammo, mob/living/hit_mob, obj/projectile/firing_projectile, mob/living/user, obj/item/weapon/gun/fired_from)
SIGNAL_HANDLER

if(!user || hit_mob == user || user.zone_selected != "head" || user.a_intent != INTENT_HARM || !ishuman_strict(hit_mob))
return

if(!skillcheck(user, SKILL_EXECUTION, SKILL_EXECUTION_TRAINED))
to_chat(user, SPAN_DANGER("You don't know how to execute someone correctly."))
return

var/mob/living/carbon/human/execution_target = hit_mob

if(execution_target.status_flags & PERMANENTLY_DEAD)
to_chat(user, SPAN_DANGER("[execution_target] has already been executed!"))
return

INVOKE_ASYNC(src, PROC_REF(attempt_battlefield_execution), src, execution_target, firing_projectile, user, fired_from)

return COMPONENT_CANCEL_AMMO_POINT_BLANK

/datum/ammo/bullet/proc/attempt_battlefield_execution(datum/ammo/firing_ammo, mob/living/carbon/human/execution_target, obj/projectile/firing_projectile, mob/living/user, obj/item/weapon/gun/fired_from)
user.affected_message(execution_target,
SPAN_HIGHDANGER("You aim \the [fired_from] at [execution_target]'s head!"),
SPAN_HIGHDANGER("[user] aims \the [fired_from] directly at your head!"),
SPAN_DANGER("[user] aims \the [fired_from] at [execution_target]'s head!"))

user.next_move += 1.1 SECONDS //PB has no click delay; readding it here to prevent people accidentally queuing up multiple executions.

if(!do_after(user, 1 SECONDS, INTERRUPT_ALL, BUSY_ICON_HOSTILE) || !user.Adjacent(execution_target))
fired_from.delete_bullet(firing_projectile, TRUE)
return

if(!(fired_from.flags_gun_features & GUN_SILENCED))
playsound(user, fired_from.fire_sound, fired_from.firesound_volume, FALSE)
else
playsound(user, fired_from.fire_sound, 25, FALSE)

shake_camera(user, 1, 2)

execution_target.apply_damage(damage * 3, BRUTE, "head", no_limb_loss = TRUE, permanent_kill = TRUE) //Apply gobs of damage and make sure they can't be revived later...
execution_target.apply_damage(200, OXY) //...fill out the rest of their health bar with oxyloss...
execution_target.death(create_cause_data("execution", user)) //...make certain they're properly dead...
shake_camera(execution_target, 3, 4)
execution_target.update_headshot_overlay(headshot_state) //...and add a gory headshot overlay.

execution_target.visible_message(SPAN_HIGHDANGER(uppertext("[execution_target] WAS EXECUTED!")), \
SPAN_HIGHDANGER("You WERE EXECUTED!"))

user.count_niche_stat(STATISTICS_NICHE_EXECUTION, 1, firing_projectile.weapon_cause_data?.cause_name)

var/area/execution_area = get_area(execution_target)

msg_admin_attack(FONT_SIZE_HUGE("[key_name(usr)] has battlefield executed [key_name(execution_target)] in [get_area(usr)] ([usr.loc.x],[usr.loc.y],[usr.loc.z])."), usr.loc.x, usr.loc.y, usr.loc.z)
log_attack("[key_name(usr)] battlefield executed [key_name(execution_target)] at [execution_area.name].")

if(flags_ammo_behavior & AMMO_EXPLOSIVE)
execution_target.gib()

72 changes: 72 additions & 0 deletions code/datums/ammo/bullet/lever_action.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
//======
Lever Action
//======
*/

/datum/ammo/bullet/lever_action
name = "lever-action bullet"

damage = 80
penetration = 0
accuracy = HIT_ACCURACY_TIER_1
shell_speed = AMMO_SPEED_TIER_6
accurate_range = 14
handful_state = "lever_action_bullet"

//unused and not working. need to refactor MD code. Unobtainable.
//intended mechanic is to have xenos hit with it show up very frequently on any MDs around
/datum/ammo/bullet/lever_action/tracker
name = "tracking lever-action bullet"
icon_state = "redbullet"
damage = 70
penetration = ARMOR_PENETRATION_TIER_3
accuracy = HIT_ACCURACY_TIER_1
handful_state = "tracking_lever_action_bullet"

/datum/ammo/bullet/lever_action/tracker/on_hit_mob(mob/M, obj/projectile/P, mob/user)
//SEND_SIGNAL(user, COMSIG_BULLET_TRACKING, user, M)
M.visible_message(SPAN_DANGER("You hear a faint beep under [M]'s [M.mob_size > MOB_SIZE_HUMAN ? "chitin" : "skin"]."))

/datum/ammo/bullet/lever_action/training
name = "lever-action blank"
icon_state = "blank"
damage = 70 //blanks CAN hurt you if shot very close
penetration = 0
accuracy = HIT_ACCURACY_TIER_1
damage_falloff = DAMAGE_FALLOFF_BLANK //not much, though (comparatively)
shell_speed = AMMO_SPEED_TIER_5
handful_state = "training_lever_action_bullet"

//unused, and unobtainable... for now
/datum/ammo/bullet/lever_action/marksman
name = "marksman lever-action bullet"
shrapnel_chance = 0
damage_falloff = 0
accurate_range = 12
damage = 70
penetration = ARMOR_PENETRATION_TIER_6
shell_speed = AMMO_SPEED_TIER_6
handful_state = "marksman_lever_action_bullet"

/datum/ammo/bullet/lever_action/xm88
name = ".458 SOCOM round"

damage = 80
penetration = ARMOR_PENETRATION_TIER_2
accuracy = HIT_ACCURACY_TIER_1
shell_speed = AMMO_SPEED_TIER_6
accurate_range = 14
handful_state = "boomslang_bullet"

/datum/ammo/bullet/lever_action/xm88/pen20
penetration = ARMOR_PENETRATION_TIER_4

/datum/ammo/bullet/lever_action/xm88/pen30
penetration = ARMOR_PENETRATION_TIER_6

/datum/ammo/bullet/lever_action/xm88/pen40
penetration = ARMOR_PENETRATION_TIER_8

/datum/ammo/bullet/lever_action/xm88/pen50
penetration = ARMOR_PENETRATION_TIER_10
Loading

0 comments on commit 65d9e8e

Please sign in to comment.