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:
Ryll Ryll
2021-01-09 23:27:03 -08:00
committed by GitHub
parent b96cdef992
commit 6d186482ef
10 changed files with 185 additions and 12 deletions
+2
View File
@@ -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
+2 -2
View File
@@ -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
+6 -6
View File
@@ -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
+74
View File
@@ -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"
+2
View File
@@ -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."
+1
View File
@@ -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"