mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
## About The Pull Request  Adds the .38 Flare. It does 20 damage. This round highlights the target for 2 minutes, and projectiles hitting the target always hit the limb that the shooter was aiming at. Your shot has effectively perfect limb accuracy, no matter how far the bullet needs to travel. This also affects other projectiles hitting the target that aren't the .38 Flare. To indicate whether or not a round in a magazine is either lead or laser, I've borrowed the implementation of ammo overlays from the C-20r toy magazines Now each bullet in the speedloaders for .38 can be a distinct type visibly. Might be interesting if anyone wants to add additional unique appearances for some of the other .38 rounds. Reduces the overall cost of a lot of the ammunition in the sec and autolathe, such as loose bullets. Also reduces the material quantity within bullet casings. ## Why It's Good For The Game > .38 Flare By popular demand, I've come up with a new idea. And that idea...is an anti-bullet deviation tool. Most players are probably not conscious of bullet deviation. But if you're familiar with the mechanic, it's why sometimes your shots may hit into an arm or a leg even though you were aiming at the head. The way this works is that over the course of a bullets flight, it increases in inaccuracy and the pobability of drifting into a limb. From my last estimate, shooting somewhere around 5 tiles away will usually result in bullet drift. While affected by the flare shot, that does not happen. You shoot at the head, you hit the head. To put it into perspective; you could consider bullet deviation a form of damage loss (its a bit more complicated than this and there are instances where it is positive), and this projectile eliminates that problem for everyone shooting the target. > Ammo cost Some of these were pretty excessive for the cost, and a notable example of this include the .357 loose casings, .310 Surplus loose casings, and most shotgun shells. These should go down to roughly below 3/5th of a sheet of metal when printed from a fully upgraded lathe. Meanwhile, a single bullet casing was like a sheet of metal each, which didn't seem right to me. So they're now by default one fifth of a sheet of metal on recycle. ## Changelog 🆑 add: .38 Flare, a laser bullet! Available in both .38 speedloader and BR-38 magazine once Advanced Beam Weaponry is researched. add: .38 Flare highlights the target in an outline and makes sure your bullets never accidentally hit any limb except the one you are aiming at. Never accidentally hit someone in the arm when you were going for a headshot. balance: Reduces the printing costs of several ammunition types from the autolathe and security lathe. Reduces the overall material contents of said printed ammo/magazines. If you find a material dupe, let a coder know. /🆑 --------- Co-authored-by: projectkepler-RU <99981766+projectkepler-ru@users.noreply.github.com> Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
168 lines
6.8 KiB
Plaintext
168 lines
6.8 KiB
Plaintext
/obj/item/ammo_casing
|
|
name = "bullet casing"
|
|
desc = "A bullet casing."
|
|
icon = 'icons/obj/weapons/guns/ammo.dmi'
|
|
icon_state = "s-casing"
|
|
worn_icon_state = "bullet"
|
|
obj_flags = CONDUCTS_ELECTRICITY
|
|
slot_flags = ITEM_SLOT_BELT
|
|
throwforce = 0
|
|
w_class = WEIGHT_CLASS_TINY
|
|
custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT)
|
|
override_notes = TRUE
|
|
///What sound should play when this ammo is fired
|
|
var/fire_sound = null
|
|
///Which kind of guns it can be loaded into
|
|
var/caliber = null
|
|
///The bullet type to create when New() is called
|
|
var/projectile_type = null
|
|
///the loaded projectile in this ammo casing
|
|
var/obj/projectile/loaded_projectile = null
|
|
///Pellets for spreadshot
|
|
var/pellets = 1
|
|
///Variance for inaccuracy fundamental to the casing
|
|
var/variance = 0
|
|
///Randomspread for automatics
|
|
var/randomspread = 0
|
|
///Delay for energy weapons
|
|
var/delay = 0
|
|
///Override this to make your gun have a faster fire rate, in tenths of a second. 4 is the default gun cooldown.
|
|
var/click_cooldown_override = 0
|
|
///the visual effect appearing when the ammo is fired.
|
|
var/firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect
|
|
///pacifism check for boolet, set to FALSE if bullet is non-lethal
|
|
var/harmful = TRUE
|
|
/// How much force is applied when fired in zero-G
|
|
var/newtonian_force = 1
|
|
|
|
///If set to true or false, this ammunition can or cannot misfire, regardless the gun can_misfire setting
|
|
var/can_misfire = null
|
|
///This is how much misfire probability is added to the gun when it fires this casing.
|
|
var/misfire_increment = 0
|
|
///If set, this casing will damage any gun it's fired from by the specified amount
|
|
var/integrity_damage = 0
|
|
|
|
/// Set when this casing is fired. Only used for checking if it should burn a user's hand when caught from an ejection port.
|
|
var/shot_timestamp = 0
|
|
|
|
/obj/item/ammo_casing/spent
|
|
name = "spent bullet casing"
|
|
loaded_projectile = null
|
|
|
|
/obj/item/ammo_casing/Initialize(mapload)
|
|
. = ..()
|
|
if(projectile_type)
|
|
loaded_projectile = new projectile_type(src)
|
|
pixel_x = base_pixel_x + rand(-10, 10)
|
|
pixel_y = base_pixel_y + rand(-10, 10)
|
|
setDir(pick(GLOB.alldirs))
|
|
update_appearance()
|
|
|
|
/obj/item/ammo_casing/Destroy()
|
|
var/turf/T = get_turf(src)
|
|
if(T && !loaded_projectile && is_station_level(T.z))
|
|
SSblackbox.record_feedback("tally", "station_mess_destroyed", 1, name)
|
|
QDEL_NULL(loaded_projectile)
|
|
return ..()
|
|
|
|
/obj/item/ammo_casing/add_weapon_description()
|
|
AddElement(/datum/element/weapon_description, attached_proc = PROC_REF(add_notes_ammo))
|
|
|
|
/**
|
|
*
|
|
* Outputs type-specific weapon stats for ammunition based on the projectile loaded inside the casing.
|
|
* Distinguishes between critting and stam-critting in separate lines
|
|
*
|
|
*/
|
|
/obj/item/ammo_casing/proc/add_notes_ammo()
|
|
// Try to get a projectile to derive stats from
|
|
var/obj/projectile/exam_proj = projectile_type
|
|
var/initial_damage = initial(exam_proj.damage)
|
|
var/initial_stamina = initial(exam_proj.stamina)
|
|
// projectile damage multiplier for guns with snowflaked damage multipliers
|
|
var/proj_damage_mult = 1
|
|
if(!ispath(exam_proj) || pellets == 0)
|
|
return
|
|
|
|
// are we in an ammo box?
|
|
if(isammobox(loc))
|
|
var/obj/item/ammo_box/our_box = loc
|
|
// is our ammo box in a gun?
|
|
if(isgun(our_box.loc))
|
|
var/obj/item/gun/our_gun = our_box.loc
|
|
// grab the damage multiplier
|
|
proj_damage_mult = our_gun.projectile_damage_multiplier
|
|
// if not, are we just in a gun e.g. chambered
|
|
else if(isgun(loc))
|
|
var/obj/item/gun/our_gun = loc
|
|
// grab the damage multiplier.
|
|
proj_damage_mult = our_gun.projectile_damage_multiplier
|
|
var/list/readout = list()
|
|
if(proj_damage_mult <= 0 || (initial_damage <= 0 && initial_stamina <= 0))
|
|
return "Our legal team has determined the offensive nature of these [span_warning(caliber)] rounds to be esoteric."
|
|
// No dividing by 0
|
|
if(initial_damage)
|
|
readout += "Most monkeys our legal team subjected to these [span_warning(caliber)] rounds succumbed to their wounds after [span_warning("[HITS_TO_CRIT((initial(exam_proj.damage) * proj_damage_mult) * pellets)] shot\s")] at point-blank, taking [span_warning("[pellets] shot\s")] per round."
|
|
if(initial_stamina)
|
|
readout += "[!readout.len ? "Most monkeys" : "More fortunate monkeys"] collapsed from exhaustion after [span_warning("[HITS_TO_CRIT((initial(exam_proj.stamina) * proj_damage_mult) * pellets)] impact\s")] of these [span_warning("[caliber]")] rounds."
|
|
return readout.Join("\n") // Sending over a single string, rather than the whole list
|
|
|
|
/obj/item/ammo_casing/update_icon_state()
|
|
icon_state = "[initial(icon_state)][loaded_projectile ? "-live" : null]"
|
|
return ..()
|
|
|
|
/obj/item/ammo_casing/update_desc()
|
|
desc = "[initial(desc)][loaded_projectile ? null : " This one is spent."]"
|
|
return ..()
|
|
|
|
/*
|
|
* On accidental consumption, 'spend' the ammo, and add in some gunpowder
|
|
*/
|
|
/obj/item/ammo_casing/on_accidental_consumption(mob/living/carbon/victim, mob/living/carbon/user, obj/item/source_item, discover_after = TRUE)
|
|
if(loaded_projectile)
|
|
loaded_projectile = null
|
|
update_appearance()
|
|
victim.reagents?.add_reagent(/datum/reagent/gunpowder, 3)
|
|
source_item?.reagents?.add_reagent(/datum/reagent/gunpowder, source_item.reagents.total_volume*(2/3))
|
|
|
|
return ..()
|
|
|
|
//proc to magically refill a casing with a new projectile
|
|
/obj/item/ammo_casing/proc/newshot() //For energy weapons, syringe gun, shotgun shells and wands (!).
|
|
if(!loaded_projectile)
|
|
loaded_projectile = new projectile_type(src, src)
|
|
|
|
/obj/item/ammo_casing/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
|
|
if(istype(I, /obj/item/ammo_box))
|
|
var/obj/item/ammo_box/box = I
|
|
if(isturf(loc))
|
|
var/boolets = 0
|
|
for(var/obj/item/ammo_casing/bullet in loc)
|
|
if (box.stored_ammo.len >= box.max_ammo)
|
|
break
|
|
if (bullet.loaded_projectile)
|
|
if (box.give_round(bullet, 0))
|
|
boolets++
|
|
else
|
|
continue
|
|
if (boolets > 0)
|
|
box.update_appearance()
|
|
to_chat(user, span_notice("You collect [boolets] shell\s. [box] now contains [box.stored_ammo.len] shell\s."))
|
|
else
|
|
to_chat(user, span_warning("You fail to collect anything!"))
|
|
else
|
|
return ..()
|
|
|
|
/obj/item/ammo_casing/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
|
bounce_away(FALSE, NONE)
|
|
return ..()
|
|
|
|
/obj/item/ammo_casing/proc/bounce_away(still_warm = FALSE, bounce_delay = 3)
|
|
update_appearance()
|
|
SpinAnimation(10, 1)
|
|
var/turf/T = get_turf(src)
|
|
if(still_warm && T?.bullet_sizzle)
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), src, 'sound/items/tools/welder.ogg', 20, 1), bounce_delay) //If the turf is made of water and the shell casing is still hot, make a sizzling sound when it's ejected.
|
|
else if(T?.bullet_bounce_sound)
|
|
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), src, T.bullet_bounce_sound, 20, 1), bounce_delay) //Soft / non-solid turfs that shouldn't make a sound when a shell casing is ejected over them.
|