mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-26 14:39:58 +01:00
Adds Rocket Backblast (#55681)
This PR adds the backblast element, which when attached to a gun, creates giant plumes of fire when said gun is fired. The PM9 rocket launcher that nuke ops can buy kinda sucks and even a direct hit with the standard rockets loaded isn't enough to guarantee a one-hit crit on a direct hit against an armored opponent, which sucks for how much you pay for it. In that vein, I've also buffed the standard rockets a bit, they now do 50 brute up from 30 on a direct hit, and they create flames on their explosion. Also makes a tweak to /proc/get_turf_in_angle(), since tile coordinates start at 1,1 instead of 0,0 that proc is now clamped to min 1,1 rather than 0,0
This commit is contained in:
@@ -680,6 +680,8 @@
|
||||
|
||||
///called in /obj/item/gun/process_fire (user, target, params, zone_override)
|
||||
#define COMSIG_MOB_FIRED_GUN "mob_fired_gun"
|
||||
///called in /obj/item/gun/process_fire (user, target, params, zone_override)
|
||||
#define COMSIG_GUN_FIRED "gun_fired"
|
||||
|
||||
// /obj/item/grenade signals
|
||||
|
||||
|
||||
@@ -181,8 +181,8 @@
|
||||
while(pixel_y < -16)
|
||||
pixel_y += 32
|
||||
new_y--
|
||||
new_x = clamp(new_x, 0, world.maxx)
|
||||
new_y = clamp(new_y, 0, world.maxy)
|
||||
new_x = clamp(new_x, 1, world.maxx)
|
||||
new_y = clamp(new_y, 1, world.maxy)
|
||||
return locate(new_x, new_y, starting.z)
|
||||
|
||||
// Returns a list where [1] is all x values and [2] is all y values that overlap between the given pair of rectangles
|
||||
|
||||
@@ -139,19 +139,19 @@
|
||||
* * O- Our parent, the thing making the shrapnel obviously (grenade or landmine)
|
||||
* * punishable_triggerer- For grenade lances or people who step on the landmines (if we shred the triggerer), we spawn extra shrapnel for them in addition to the normal spread
|
||||
*/
|
||||
/datum/component/pellet_cloud/proc/create_blast_pellets(obj/O, mob/living/punishable_triggerer)
|
||||
SIGNAL_HANDLER_DOES_SLEEP
|
||||
/datum/component/pellet_cloud/proc/create_blast_pellets(obj/O, mob/living/triggerer)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/atom/A = parent
|
||||
|
||||
if(isgrenade(parent)) // handle_martyrs can reduce the radius and thus the number of pellets we produce if someone dives on top of a frag grenade
|
||||
handle_martyrs(punishable_triggerer) // note that we can modify radius in this proc
|
||||
INVOKE_ASYNC(src, .proc/handle_martyrs, triggerer) // note that we can modify radius in this proc
|
||||
else if(islandmine(parent))
|
||||
var/obj/effect/mine/shrapnel/triggered_mine = parent
|
||||
if(triggered_mine.shred_triggerer && istype(punishable_triggerer)) // free shrapnel for the idiot who stepped on it if we're a mine that shreds the triggerer
|
||||
if(triggered_mine.shred_triggerer && istype(triggerer)) // free shrapnel for the idiot who stepped on it if we're a mine that shreds the triggerer
|
||||
pellet_delta += radius // so they don't count against the later total
|
||||
for(var/i in 1 to radius)
|
||||
pew(punishable_triggerer, TRUE)
|
||||
INVOKE_ASYNC(src, .proc/pew, triggerer, TRUE)
|
||||
|
||||
if(radius < 1)
|
||||
return
|
||||
@@ -161,7 +161,7 @@
|
||||
|
||||
for(var/T in all_the_turfs_were_gonna_lacerate)
|
||||
var/turf/shootat_turf = T
|
||||
pew(shootat_turf)
|
||||
INVOKE_ASYNC(src, .proc/pew, shootat_turf)
|
||||
|
||||
/**
|
||||
* handle_martyrs() is used for grenades that shoot shrapnel to check if anyone threw themselves/were thrown on top of the grenade, thus absorbing a good chunk of the shrapnel
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* When attached to a gun and the gun is successfully fired, this element creates a "backblast" of fire and pain, like you'd find in a rocket launcher or recoilless rifle
|
||||
*
|
||||
* The backblast is simulated by a number of fire plumes, or invisible incendiary rounds that will torch anything they come across for a short distance, as well as knocking
|
||||
* back nearby items.
|
||||
*/
|
||||
/datum/element/backblast
|
||||
element_flags = ELEMENT_BESPOKE
|
||||
id_arg_index = 2
|
||||
|
||||
/// How many "pellets" of backblast we're shooting backwards, spread between the angle defined in angle_spread
|
||||
var/plumes
|
||||
/// Assuming we don't just have 1 plume, this is the total angle we'll cover with the plumes, split down the middle directly behind the angle we fired at
|
||||
var/angle_spread
|
||||
/// How far each plume of fire will fly, assuming it doesn't hit a mob
|
||||
var/range
|
||||
|
||||
/datum/element/backblast/Attach(datum/target, plumes = 4, angle_spread = 48, range = 6)
|
||||
. = ..()
|
||||
if(!isgun(target) || plumes < 1 || angle_spread < 1 || range < 1)
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
|
||||
src.plumes = plumes
|
||||
src.angle_spread = angle_spread
|
||||
src.range = range
|
||||
|
||||
if(plumes == 1)
|
||||
RegisterSignal(target, COMSIG_GUN_FIRED, .proc/gun_fired_simple)
|
||||
else
|
||||
RegisterSignal(target, COMSIG_GUN_FIRED, .proc/gun_fired)
|
||||
|
||||
/datum/element/backblast/Detach(datum/source, force)
|
||||
if(source)
|
||||
UnregisterSignal(source, COMSIG_GUN_FIRED)
|
||||
return ..()
|
||||
|
||||
/// For firing multiple plumes behind us, we evenly spread out our projectiles based on the [angle_spread][/datum/element/backblast/var/angle_spread] and [number of plumes][/datum/element/backblast/var/plumes]
|
||||
/datum/element/backblast/proc/gun_fired(obj/item/gun/weapon, mob/living/user, atom/target, params, zone_override)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!weapon.chambered || HAS_TRAIT(user, TRAIT_PACIFISM))
|
||||
return
|
||||
|
||||
var/backwards_angle = Get_Angle(target, user)
|
||||
var/starting_angle = SIMPLIFY_DEGREES(backwards_angle-(angle_spread * 0.5))
|
||||
var/iter_offset = angle_spread / plumes // how much we increment the angle for each plume
|
||||
|
||||
for(var/i in 1 to plumes)
|
||||
var/this_angle = SIMPLIFY_DEGREES(starting_angle + ((i - 1) * iter_offset))
|
||||
var/turf/target_turf = get_turf_in_angle(this_angle, get_turf(user), 10)
|
||||
INVOKE_ASYNC(src, .proc/pew, target_turf, weapon, user)
|
||||
|
||||
/// If we're only firing one plume directly behind us, we don't need to bother with the loop or angles or anything
|
||||
/datum/element/backblast/proc/gun_fired_simple(obj/item/gun/weapon, mob/living/user, atom/target, params, zone_override)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!weapon.chambered || HAS_TRAIT(user, TRAIT_PACIFISM))
|
||||
return
|
||||
|
||||
var/backwards_angle = Get_Angle(target, user)
|
||||
var/turf/target_turf = get_turf_in_angle(backwards_angle, get_turf(user), 10)
|
||||
INVOKE_ASYNC(src, .proc/pew, target_turf, weapon, user)
|
||||
|
||||
/// For firing an actual backblast pellet
|
||||
/datum/element/backblast/proc/pew(turf/target_turf, obj/item/gun/weapon, mob/living/user)
|
||||
//Shooting Code:
|
||||
var/obj/projectile/bullet/incendiary/backblast/P = new (get_turf(user))
|
||||
P.original = target_turf
|
||||
P.range = range
|
||||
P.fired_from = weapon
|
||||
P.firer = user // don't hit ourself that would be really annoying
|
||||
P.impacted = list(user = TRUE) // don't hit the target we hit already with the flak
|
||||
P.preparePixelProjectile(target_turf, weapon)
|
||||
P.fire()
|
||||
@@ -12,6 +12,11 @@
|
||||
icon_state = "84mm-hedp"
|
||||
projectile_type = /obj/projectile/bullet/a84mm
|
||||
|
||||
/obj/item/ammo_casing/caseless/rocket/weak
|
||||
name = "\improper PM-9HE Low-Yield"
|
||||
desc = "An 84mm High Explosive rocket. This one isn't quite as devastating."
|
||||
projectile_type = /obj/projectile/bullet/a84mm_weak
|
||||
|
||||
/obj/item/ammo_casing/caseless/a75
|
||||
desc = "A .75 bullet casing."
|
||||
caliber = "75"
|
||||
|
||||
@@ -322,6 +322,8 @@
|
||||
if(user)
|
||||
SEND_SIGNAL(user, COMSIG_MOB_FIRED_GUN, user, target, params, zone_override)
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_GUN_FIRED, user, target, params, zone_override)
|
||||
|
||||
add_fingerprint(user)
|
||||
|
||||
if(semicd)
|
||||
|
||||
@@ -44,7 +44,8 @@
|
||||
|
||||
/obj/item/gun/ballistic/rocketlauncher
|
||||
name = "\improper PML-9"
|
||||
desc = "A reusable rocket propelled grenade launcher. The words \"NT this way\" and an arrow have been written near the barrel."
|
||||
desc = "A reusable rocket propelled grenade launcher. The words \"NT this way\" and an arrow have been written near the barrel. \
|
||||
A sticker near the cheek rest reads, \"ENSURE AREA BEHIND IS CLEAR BEFORE FIRING\""
|
||||
icon_state = "rocketlauncher"
|
||||
inhand_icon_state = "rocketlauncher"
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/rocketlauncher
|
||||
@@ -61,10 +62,22 @@
|
||||
cartridge_wording = "rocket"
|
||||
empty_indicator = TRUE
|
||||
tac_reloads = FALSE
|
||||
/// Do we shit flames behind us when we fire?
|
||||
var/backblast = TRUE
|
||||
|
||||
/obj/item/gun/ballistic/rocketlauncher/Initialize()
|
||||
. = ..()
|
||||
if(backblast)
|
||||
AddElement(/datum/element/backblast)
|
||||
|
||||
/obj/item/gun/ballistic/rocketlauncher/unrestricted
|
||||
pin = /obj/item/firing_pin
|
||||
|
||||
/obj/item/gun/ballistic/rocketlauncher/nobackblast
|
||||
name = "flameless PML-11"
|
||||
desc = "A reusable rocket propelled grenade launcher. This one has been fitted with a special coolant loop to avoid embarassing teamkill 'accidents' from backblast."
|
||||
backblast = FALSE
|
||||
|
||||
/obj/item/gun/ballistic/rocketlauncher/afterattack()
|
||||
. = ..()
|
||||
magazine.get_round(FALSE) //Hack to clear the mag after it's fired
|
||||
|
||||
@@ -15,3 +15,58 @@
|
||||
if(location)
|
||||
new /obj/effect/hotspot(location)
|
||||
location.hotspot_expose(700, 50, 1)
|
||||
|
||||
/// Used in [the backblast element][/datum/element/backblast]
|
||||
/obj/projectile/bullet/incendiary/backblast
|
||||
damage = 15
|
||||
range = 10 // actually overwritten in the backblast element
|
||||
alpha = 0
|
||||
pass_flags = PASSTABLE | PASSMOB
|
||||
sharpness = SHARP_NONE
|
||||
shrapnel_type = null
|
||||
embedding = null
|
||||
ricochet_chance = 10000
|
||||
ricochets_max = 4
|
||||
ricochet_incidence_leeway = 0
|
||||
suppressed = SUPPRESSED_VERY
|
||||
damage_type = BURN
|
||||
flag = BOMB
|
||||
speed = 1.2
|
||||
wound_bonus = 30
|
||||
bare_wound_bonus = 30
|
||||
wound_falloff_tile = -4
|
||||
fire_stacks = 3
|
||||
|
||||
/// Lazy attempt at knockback, any items this plume hits will be knocked back this far. Decrements with each tile passed.
|
||||
var/knockback_range = 7
|
||||
/// A lazylist of all the items we've already knocked back, so we don't do it again
|
||||
var/list/launched_items
|
||||
|
||||
/// we only try to knock back the first 6 items per tile
|
||||
#define BACKBLAST_MAX_ITEM_KNOCKBACK 6
|
||||
|
||||
/obj/projectile/bullet/incendiary/backblast/Move()
|
||||
. = ..()
|
||||
if(knockback_range <= 0)
|
||||
return
|
||||
knockback_range--
|
||||
var/turf/current_turf = get_turf(src)
|
||||
var/turf/throw_at_turf = get_turf_in_angle(Angle, current_turf, 7)
|
||||
var/thrown_items = 0
|
||||
|
||||
for(var/iter in current_turf.contents)
|
||||
if(thrown_items > BACKBLAST_MAX_ITEM_KNOCKBACK)
|
||||
break
|
||||
if(isitem(iter))
|
||||
var/obj/item/iter_item = iter
|
||||
if(iter_item.anchored || LAZYFIND(launched_items, iter_item) || iter_item.throwing)
|
||||
continue
|
||||
thrown_items++
|
||||
iter_item.throw_at(throw_at_turf, knockback_range, knockback_range)
|
||||
LAZYADD(launched_items, iter_item)
|
||||
else if(isliving(iter))
|
||||
var/mob/living/incineratee = iter
|
||||
incineratee.take_bodypart_damage(0, damage, wound_bonus=wound_bonus, bare_wound_bonus=bare_wound_bonus)
|
||||
incineratee.adjust_fire_stacks(fire_stacks)
|
||||
|
||||
#undef BACKBLAST_MAX_ITEM_KNOCKBACK
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
explosion(target, -1, 0, 2)
|
||||
return BULLET_ACT_HIT
|
||||
|
||||
/// PM9 HEDP rocket
|
||||
/obj/projectile/bullet/a84mm
|
||||
name ="\improper HEDP rocket"
|
||||
desc = "USE A WEEL GUN"
|
||||
@@ -33,11 +34,12 @@
|
||||
S.take_overall_damage(anti_armour_damage*0.75, anti_armour_damage*0.25)
|
||||
return BULLET_ACT_HIT
|
||||
|
||||
/// PM9 standard rocket
|
||||
/obj/projectile/bullet/a84mm_he
|
||||
name ="\improper HE missile"
|
||||
desc = "Boom."
|
||||
icon_state = "missile"
|
||||
damage = 30
|
||||
damage = 50
|
||||
ricochets_max = 0 //it's a MISSILE
|
||||
embedding = null
|
||||
shrapnel_type = null
|
||||
@@ -45,11 +47,30 @@
|
||||
/obj/projectile/bullet/a84mm_he/on_hit(atom/target, blocked=0)
|
||||
..()
|
||||
if(!isliving(target)) //if the target isn't alive, so is a wall or something
|
||||
explosion(target, 0, 1, 2, 4)
|
||||
explosion(target, 0, 1, 2, 4, flame_range = 3)
|
||||
else
|
||||
explosion(target, 0, 0, 2, 4)
|
||||
explosion(target, 0, 0, 2, 4, flame_range = 3)
|
||||
return BULLET_ACT_HIT
|
||||
|
||||
/// PM9 weak rocket
|
||||
/obj/projectile/bullet/a84mm_weak
|
||||
name ="low-yield HE missile"
|
||||
desc = "Boom, but less so."
|
||||
icon_state = "missile"
|
||||
damage = 30
|
||||
ricochets_max = 0 //it's a MISSILE
|
||||
embedding = null
|
||||
shrapnel_type = null
|
||||
|
||||
/obj/projectile/bullet/a84mm_weak/on_hit(atom/target, blocked=0)
|
||||
..()
|
||||
if(!isliving(target)) //if the target isn't alive, so is a wall or something
|
||||
explosion(target, 0, 1, 2, 4, flame_range = 3)
|
||||
else
|
||||
explosion(target, 0, 0, 2, 4, flame_range = 3)
|
||||
return BULLET_ACT_HIT
|
||||
|
||||
/// Mech BRM-6 missile
|
||||
/obj/projectile/bullet/a84mm_br
|
||||
name ="\improper HE missile"
|
||||
desc = "Boom."
|
||||
|
||||
@@ -589,6 +589,7 @@
|
||||
#include "code\datums\elements\_element.dm"
|
||||
#include "code\datums\elements\art.dm"
|
||||
#include "code\datums\elements\atmos_sensitive.dm"
|
||||
#include "code\datums\elements\backblast.dm"
|
||||
#include "code\datums\elements\bed_tucking.dm"
|
||||
#include "code\datums\elements\bsa_blocker.dm"
|
||||
#include "code\datums\elements\cleaning.dm"
|
||||
|
||||
Reference in New Issue
Block a user