mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-26 22:50:26 +01:00
## About The Pull Request Removes some /New overrides, replacing them either with nothing because they're entirely redundant or with more intuitive/consistent interfaces ## Why It's Good For The Game Code consistency/readability, fixes an obscure potential-bug that would delete non-implanted cybernetic implants that are a part of a loadout/outfit, I'm being haunted by code from 2015 send help ## Changelog Hopefully not player facing. Except this random thing I saw while I was here. 🆑 spellcheck: paywall firing pins no longer set the gun description to the pin's description on removal /🆑
345 lines
13 KiB
Plaintext
345 lines
13 KiB
Plaintext
/// How much to scale the explosion ranges for blastcannon shots.
|
|
#define BLASTCANNON_RANGE_EXP (1 / GLOB.DYN_EX_SCALE)
|
|
/// How much to scale the explosion ranges for blastcannon shots.
|
|
#define BLASTCANNON_RANGE_SCALE PI
|
|
|
|
|
|
/**
|
|
* A gun that consumes a TTV to shoot an projectile with equivalent power.
|
|
*
|
|
* It's basically an immovable rod launcher.
|
|
*/
|
|
/obj/item/gun/blastcannon
|
|
name = "blast cannon"
|
|
desc = "A surprisingly portable device used to concentrate a bomb's blast energy to a narrow wave. Small enough to stow in a bag."
|
|
icon = 'icons/obj/weapons/guns/wide_guns.dmi'
|
|
icon_state = "blastcannon_empty"
|
|
lefthand_file = 'icons/mob/inhands/weapons/64x_guns_left.dmi'
|
|
righthand_file = 'icons/mob/inhands/weapons/64x_guns_right.dmi'
|
|
inhand_x_dimension = 64
|
|
base_pixel_x = -2
|
|
pixel_x = -2
|
|
inhand_icon_state = "blastcannon_empty"
|
|
base_icon_state = "blastcannon"
|
|
w_class = WEIGHT_CLASS_NORMAL
|
|
force = 10
|
|
fire_sound = 'sound/items/weapons/blastcannon.ogg'
|
|
item_flags = NONE
|
|
clumsy_check = FALSE
|
|
randomspread = FALSE
|
|
|
|
// Firing data.
|
|
/// The person who opened the valve on the TTV loaded into this.
|
|
var/datum/weakref/cached_firer
|
|
/// The target from the last click.
|
|
var/datum/weakref/cached_target
|
|
/// The modifiers from the last click.
|
|
var/cached_modifiers
|
|
|
|
// Explosion data:
|
|
/// The TTV this contains that will be used to create the projectile
|
|
var/obj/item/transfer_valve/bomb
|
|
|
|
// For debugging/badminry
|
|
/// Whether you can fire this without a bomb.
|
|
var/bombcheck = TRUE
|
|
/// The range this defaults to without a bomb for debugging and badminry
|
|
var/debug_power = 0
|
|
|
|
|
|
/obj/item/gun/blastcannon/Initialize(mapload)
|
|
. = ..()
|
|
RegisterSignal(src, COMSIG_ATOM_INTERNAL_EXPLOSION, PROC_REF(channel_blastwave))
|
|
AddElement(/datum/element/update_icon_updates_onmob)
|
|
|
|
/obj/item/gun/blastcannon/Destroy()
|
|
if(bomb)
|
|
QDEL_NULL(bomb)
|
|
UnregisterSignal(src, COMSIG_ATOM_INTERNAL_EXPLOSION)
|
|
cached_firer = null
|
|
cached_target = null
|
|
return ..()
|
|
|
|
/obj/item/gun/blastcannon/Exited(atom/movable/gone, direction)
|
|
. = ..()
|
|
if(gone == bomb)
|
|
bomb = null
|
|
update_appearance()
|
|
|
|
/obj/item/gun/blastcannon/assume_air(datum/gas_mixture/giver)
|
|
qdel(giver)
|
|
return null // Required to make the TTV not vent gas directly into the firer.
|
|
|
|
/obj/item/gun/blastcannon/examine(mob/user)
|
|
. = ..()
|
|
if(bomb)
|
|
. += span_notice("A bomb is loaded inside.")
|
|
|
|
/obj/item/gun/blastcannon/attack_self(mob/user)
|
|
if(bomb)
|
|
bomb.forceMove(user.loc)
|
|
user.put_in_hands(bomb)
|
|
user.visible_message(span_warning("[user] detaches [bomb] from [src]."))
|
|
bomb = null
|
|
update_appearance()
|
|
return ..()
|
|
|
|
/obj/item/gun/blastcannon/update_icon_state()
|
|
icon_state = "[base_icon_state]_[bomb ? "loaded" : "empty"]"
|
|
inhand_icon_state = icon_state
|
|
return ..()
|
|
|
|
/obj/item/gun/blastcannon/attackby(obj/item/transfer_valve/bomb_to_attach, mob/user)
|
|
if(!istype(bomb_to_attach))
|
|
return ..()
|
|
|
|
if(bomb)
|
|
to_chat(user, span_warning("[bomb] is already attached to [src]!"))
|
|
return
|
|
if(!bomb_to_attach.ready())
|
|
to_chat(user, span_warning("What good would an incomplete bomb do?"))
|
|
return FALSE
|
|
if(!user.transferItemToLoc(bomb_to_attach, src))
|
|
to_chat(user, span_warning("[bomb_to_attach] seems to be stuck to your hand!"))
|
|
return FALSE
|
|
|
|
user.visible_message(span_warning("[user] attaches [bomb_to_attach] to [src]!"))
|
|
bomb = bomb_to_attach
|
|
update_appearance()
|
|
return TRUE
|
|
|
|
/obj/item/gun/blastcannon/try_fire_gun(atom/target, mob/living/user, params)
|
|
if((!bomb && bombcheck) || isnull(target) || (get_dist(get_turf(target), get_turf(user)) <= 2))
|
|
return ..()
|
|
|
|
cached_target = WEAKREF(target)
|
|
cached_modifiers = params
|
|
if(bomb?.valve_open)
|
|
user.visible_message(
|
|
span_danger("[user] points [src] at [target]!"),
|
|
span_danger("You point [src] at [target]!")
|
|
)
|
|
return FALSE
|
|
|
|
cached_firer = WEAKREF(user)
|
|
if(!bomb)
|
|
fire_debug(target, user, params)
|
|
return TRUE
|
|
|
|
playsound(src, dry_fire_sound, 30, TRUE) // *click
|
|
user.visible_message(
|
|
span_danger("[user] opens [bomb] on [user.p_their()] [src] and points [p_them()] at [target]!"),
|
|
span_danger("You open [bomb] on your [src] and point [p_them()] at [target]!")
|
|
)
|
|
var/turf/current_turf = get_turf(src)
|
|
var/turf/target_turf = get_turf(target)
|
|
message_admins("Blastcannon transfer valve opened by [ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(current_turf)] while aiming at [ADMIN_VERBOSEJMP(target_turf)] (target).")
|
|
user.log_message("opened blastcannon transfer valve at [AREACOORD(current_turf)] while aiming at [AREACOORD(target_turf)] (target).", LOG_GAME)
|
|
bomb.toggle_valve()
|
|
update_appearance()
|
|
return TRUE
|
|
|
|
/**
|
|
* Channels an internal explosion into a blastwave projectile.
|
|
*
|
|
* Arguments:
|
|
* - [blastwave_data][/list]: A list containing all of the data for the blastwave.
|
|
*/
|
|
/obj/item/gun/blastcannon/proc/channel_blastwave(atom/source, list/arguments)
|
|
SIGNAL_HANDLER
|
|
. = COMSIG_CANCEL_EXPLOSION
|
|
|
|
var/heavy = (arguments[EXARG_KEY_DEV_RANGE]**BLASTCANNON_RANGE_EXP) * BLASTCANNON_RANGE_SCALE
|
|
var/medium = (arguments[EXARG_KEY_HEAVY_RANGE]**BLASTCANNON_RANGE_EXP) * BLASTCANNON_RANGE_SCALE
|
|
var/light = (arguments[EXARG_KEY_LIGHT_RANGE]**BLASTCANNON_RANGE_EXP) * BLASTCANNON_RANGE_SCALE
|
|
var/range = max(heavy, medium, light, 0)
|
|
if(!range)
|
|
visible_message(span_warning("[src] lets out a little \"phut\"."))
|
|
return
|
|
|
|
if(!ismob(loc))
|
|
INVOKE_ASYNC(src, PROC_REF(fire_dropped), heavy, medium, light)
|
|
return
|
|
|
|
var/mob/holding = loc
|
|
var/target = cached_target?.resolve()
|
|
if(target && (holding.get_active_held_item() == src) && cached_firer && (holding == cached_firer.resolve()))
|
|
INVOKE_ASYNC(src, PROC_REF(fire_intentionally), target, holding, heavy, medium, light, cached_modifiers)
|
|
else
|
|
INVOKE_ASYNC(src, PROC_REF(fire_accidentally), holding, heavy, medium, light)
|
|
return
|
|
|
|
/**
|
|
* Prepares and fires a blastwave.
|
|
*
|
|
* Arguments:
|
|
* - [target][/atom]: The thing that the blastwave is being fired at.
|
|
* - heavy: The devastation range of the blastwave.
|
|
* - medium: The heavy impact range of the blastwave.
|
|
* - light: The light impact range of the blastwave.
|
|
* - modifiers: Modifiers as a string used while firing this.
|
|
* - spread: How inaccurate the blastwave is.
|
|
*/
|
|
/obj/item/gun/blastcannon/proc/fire_blastwave(atom/target, heavy, medium, light, modifiers, spread = 0)
|
|
var/turf/start_turf = get_turf(src)
|
|
|
|
var/cap_multiplier = SSmapping.level_trait(start_turf.z, ZTRAIT_BOMBCAP_MULTIPLIER)
|
|
if(isnull(cap_multiplier))
|
|
cap_multiplier = 1
|
|
var/capped_heavy = min(GLOB.MAX_EX_DEVESTATION_RANGE * cap_multiplier, heavy)
|
|
var/capped_medium = min(GLOB.MAX_EX_HEAVY_RANGE * cap_multiplier, medium)
|
|
SSexplosions.shake_the_room(start_turf, max(heavy, medium, light, 0), (capped_heavy * 15) + (capped_medium * 20), capped_heavy, capped_medium)
|
|
|
|
var/obj/projectile/blastwave/blastwave = new(loc, heavy, medium, light)
|
|
blastwave.aim_projectile(target, start_turf, params2list(modifiers), spread)
|
|
blastwave.fire()
|
|
cached_firer = null
|
|
cached_target = null
|
|
cached_modifiers = null
|
|
return
|
|
|
|
|
|
/**
|
|
* Handles firing the blastcannon intentionally at a specific target.
|
|
*
|
|
* Arguments:
|
|
* - [firer][/mob]: The mob who is firing this weapon.
|
|
* - [target][/atom]: The target we are firing the
|
|
* - heavy: The devastation range of the blastwave.
|
|
* - medium: The heavy impact range of the blastwave.
|
|
* - light: The light impact range of the blastwave.
|
|
* - modifiers: The modifier string to use when preparing the blastwave.
|
|
*/
|
|
/obj/item/gun/blastcannon/proc/fire_intentionally(atom/target, mob/firer, heavy, medium, light, modifiers)
|
|
firer.visible_message(
|
|
span_danger("[firer] fires a blast wave at [target]!"),
|
|
span_danger("You fire a blast wave at [target]!")
|
|
)
|
|
var/turf/start_turf = get_turf(src)
|
|
var/turf/target_turf = get_turf(target)
|
|
message_admins("Blast wave fired from [ADMIN_VERBOSEJMP(start_turf)] at [ADMIN_VERBOSEJMP(target_turf)] ([target]) by [ADMIN_LOOKUPFLW(firer)] with power [heavy]/[medium]/[light].")
|
|
firer.log_message("fired a blast wave from [AREACOORD(start_turf)] at [AREACOORD(target_turf)] ([target]) with power [heavy]/[medium]/[light].", LOG_GAME)
|
|
firer.log_message("fired a blast wave from [AREACOORD(start_turf)] at [AREACOORD(target_turf)] ([target]) with power [heavy]/[medium]/[light].", LOG_ATTACK, log_globally = FALSE)
|
|
fire_blastwave(target, heavy, medium, light, modifiers)
|
|
return
|
|
|
|
/**
|
|
* Handles firing the blastcannon if it was handed to someone else between opening the valve and the bomb exploding.
|
|
*
|
|
* Arguments:
|
|
* - [holder][/mob]: The person who happened to be holding the cannon when it went off.
|
|
* - heavy: The devastation range of the blastwave.
|
|
* - medium: The heavy impact range of the blastwave.
|
|
* - light: The light impact range of the blastwave.
|
|
*/
|
|
/obj/item/gun/blastcannon/proc/fire_accidentally(mob/holder, heavy, medium, light)
|
|
var/turf/target
|
|
var/holding
|
|
if(holder.is_holding(src))
|
|
target = get_edge_target_turf(holder, holder.dir)
|
|
holding = TRUE
|
|
else
|
|
target = get_edge_target_turf(holder, dir)
|
|
holding = FALSE
|
|
|
|
var/mob/firer = cached_firer?.resolve()
|
|
var/turf/start_turf = get_turf(src)
|
|
holder.visible_message(
|
|
span_danger("[src] suddenly goes off[holding ? " in [holder]'s hands" : null]!"),
|
|
span_danger("[src] suddenly goes off[holding ? " in your hands" : null]!")
|
|
)
|
|
message_admins("Blast wave primed by [ADMIN_LOOKUPFLW(firer)] fired from [ADMIN_VERBOSEJMP(start_turf)] roughly towards [ADMIN_VERBOSEJMP(target)] while being held by [ADMIN_LOOKUPFLW(holder)] with power [heavy]/[medium]/[light].")
|
|
log_game("Blast wave primed by [key_name(firer)] fired from [AREACOORD(start_turf)] roughly towards [AREACOORD(target)] while being held by [key_name(holder)] with power [heavy]/[medium]/[light].")
|
|
return fire_blastwave(target, heavy, medium, light, null, (90 * (rand() - 0.5))) // +- anywhere between 0 and 45 degrees
|
|
|
|
/**
|
|
* Handles firing the blastcannon if it was dropped on the ground or shoved into a backpack.
|
|
*
|
|
* Arguments:
|
|
* - heavy: The devastation range of the blastwave.
|
|
* - medium: The heavy impact range of the blastwave.
|
|
* - light: The light impact range of the blastwave.
|
|
*/
|
|
/obj/item/gun/blastcannon/proc/fire_dropped(heavy, medium, light)
|
|
src.visible_message(span_danger("[src] suddenly goes off!"))
|
|
var/turf/target = get_edge_target_turf(src, dir)
|
|
var/mob/firer = cached_firer.resolve()
|
|
var/turf/start_turf = get_turf(src)
|
|
message_admins("Blast wave primed by [ADMIN_LOOKUPFLW(firer)] fired from [ADMIN_VERBOSEJMP(start_turf)] roughly towards [ADMIN_VERBOSEJMP(target)] at [ADMIN_VERBOSEJMP(loc)] ([loc]) with power [heavy]/[medium]/[light].")
|
|
log_game("Blast wave primed by [key_name(firer)] fired from [AREACOORD(start_turf)] roughly towards [AREACOORD(target)] at [AREACOORD(loc)] ([loc]) with power [heavy]/[medium]/[light].")
|
|
return fire_blastwave(target, heavy, medium, light, null, 360 * rand())
|
|
|
|
/**
|
|
* Handles firing the blastcannon with debug power.
|
|
*
|
|
* Arguments:
|
|
* - [target][/atom]: The thing the blastcannon is being fired at.
|
|
* - [user][/mob]: The person firing the blastcannon.
|
|
* - modifiers: A string containing click data.
|
|
*/
|
|
/obj/item/gun/blastcannon/proc/fire_debug(atom/target, mob/user, modifiers)
|
|
fire_intentionally(target, user, debug_power * 0.25, debug_power * 0.5, debug_power, modifiers)
|
|
return
|
|
|
|
|
|
/// The projectile used by the blastcannon
|
|
/obj/projectile/blastwave
|
|
name = "blast wave"
|
|
icon_state = "blastwave"
|
|
damage = 0
|
|
armor_flag = BOMB // Doesn't actually have any functional purpose. But it makes sense.
|
|
movement_type = FLYING
|
|
projectile_phasing = ALL // just blows up the turfs lmao
|
|
phasing_ignore_direct_target = TRUE // If we don't do this the blastcannon shot can be blocked by random items.
|
|
/// The maximum distance this will inflict [EXPLODE_DEVASTATE]
|
|
var/heavy_ex_range = 0
|
|
/// The maximum distance this will inflict [EXPLODE_HEAVY]
|
|
var/medium_ex_range = 0
|
|
/// The maximum distance this will inflict [EXPLODE_LIGHT]
|
|
var/light_ex_range = 0
|
|
/// Whether or not to care about the explosion block of the things we are passing through.
|
|
var/reactionary
|
|
|
|
/obj/projectile/blastwave/Initialize(mapload, heavy_ex_range, medium_ex_range, light_ex_range, reactionary = CONFIG_GET(flag/reactionary_explosions))
|
|
range = max(heavy_ex_range, medium_ex_range, light_ex_range, 0)
|
|
src.heavy_ex_range = heavy_ex_range
|
|
src.medium_ex_range = medium_ex_range
|
|
src.light_ex_range = light_ex_range
|
|
src.reactionary = reactionary
|
|
return ..()
|
|
|
|
// Though the projectile itself is not damaging its effects are
|
|
/obj/projectile/blastwave/is_hostile_projectile()
|
|
return TRUE
|
|
|
|
/obj/projectile/blastwave/reduce_range()
|
|
. = ..()
|
|
if(QDELETED(src))
|
|
return
|
|
|
|
var/decrement = 1
|
|
var/atom/location = loc
|
|
if (reactionary)
|
|
decrement += location.explosive_resistance
|
|
|
|
range = max(range - decrement + 1, 0) // Already decremented by 1 in the parent. Exists so that if we pass through something with negative block it extends the range.
|
|
heavy_ex_range = max(heavy_ex_range - decrement, 0)
|
|
medium_ex_range = max(medium_ex_range - decrement, 0)
|
|
light_ex_range = max(light_ex_range - decrement, 0)
|
|
|
|
if (heavy_ex_range)
|
|
SSexplosions.highturf += location
|
|
else if(medium_ex_range)
|
|
SSexplosions.medturf += location
|
|
else if(light_ex_range)
|
|
SSexplosions.lowturf += location
|
|
else
|
|
qdel(src)
|
|
return
|
|
|
|
/obj/projectile/blastwave/ex_act()
|
|
return FALSE
|
|
|
|
#undef BLASTCANNON_RANGE_EXP
|
|
#undef BLASTCANNON_RANGE_SCALE
|