Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Rocket Backblast #55681

Merged
merged 17 commits into from Jan 10, 2021
Merged
2 changes: 2 additions & 0 deletions code/__DEFINES/dcs/signals.dm
Expand Up @@ -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

Expand Down
12 changes: 6 additions & 6 deletions code/datums/components/pellet_cloud.dm
Expand Up @@ -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
Expand All @@ -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
Expand Down
74 changes: 74 additions & 0 deletions code/datums/elements/backblast.dm
@@ -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()
5 changes: 5 additions & 0 deletions code/modules/projectiles/ammunition/caseless/rocket.dm
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions code/modules/projectiles/gun.dm
Expand Up @@ -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)
Expand Down
15 changes: 14 additions & 1 deletion code/modules/projectiles/guns/ballistic/launchers.dm
Expand Up @@ -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
Expand All @@ -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
Expand Down
52 changes: 52 additions & 0 deletions code/modules/projectiles/projectile/bullets/_incendiary.dm
Expand Up @@ -15,3 +15,55 @@
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 = 20
range = 10 // actually overwritten in the backblast element
alpha = 0
pass_flags = PASSMOB
LemonInTheDark marked this conversation as resolved.
Show resolved Hide resolved
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 = 50
bare_wound_bonus = 30
wound_falloff_tile = -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 5 items per tile
#define BACKBLAST_MAX_ITEM_KNOCKBACK 5

/obj/projectile/bullet/incendiary/backblast/Move()
LemonInTheDark marked this conversation as resolved.
Show resolved Hide resolved
. = ..()
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, 70)
var/thrown_items = 0

for(var/iter in current_turf.contents)
if(isitem(iter))
var/obj/item/I = iter
if(thrown_items < BACKBLAST_MAX_ITEM_KNOCKBACK || I.anchored || LAZYFIND(launched_items, I))
LemonInTheDark marked this conversation as resolved.
Show resolved Hide resolved
continue
thrown_items++
I.throw_at(throw_at_turf, knockback_range, knockback_range)
LAZYADD(launched_items, I)
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
27 changes: 24 additions & 3 deletions code/modules/projectiles/projectile/special/rocket.dm
Expand Up @@ -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"
Expand All @@ -33,23 +34,43 @@
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

/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, 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)
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."
Expand Down
1 change: 1 addition & 0 deletions tgstation.dme
Expand Up @@ -584,6 +584,7 @@
#include "code\datums\diseases\advance\symptoms\youth.dm"
#include "code\datums\elements\_element.dm"
#include "code\datums\elements\art.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"
Expand Down