Skip to content

Commit

Permalink
Fix lots of random throwing and explosion runtimes (#7861)
Browse files Browse the repository at this point in the history
# About the pull request

This PR attempts to mitigate various oversights and runtimes that occur
with explosions and throw_atom. They are really getting out of hand.
This was kicked off by a round in watchtower testing that caused the
master controller to crash from recursive throw code.

# Explain why it's good for the game

Should mean aspects like two crushers colliding at each other even is
handled, not to mention aspects that would making throwing an atom
impossible (like trying to throw an atom at an area - why is an area
considered an atom? - why is an area a result in range??). There are
likely performance gains from this, but then again, code not halting
because of runtime could mean more is being done too...

# Testing Photographs and Procedure
<details>
<summary>Screenshots & Videos</summary>


![image](https://github.com/user-attachments/assets/57a89248-a47a-4e40-869c-6fb2d59b2450)

</details>


# Changelog
:cl: Drathek
fix: Fixed various runtimes caused by explosions and throw_atom code
/:cl:

Co-authored-by: harryob <[email protected]>
  • Loading branch information
Drulikar and harryob authored Jan 2, 2025
1 parent f3c228d commit 3839789
Show file tree
Hide file tree
Showing 18 changed files with 47 additions and 36 deletions.
2 changes: 1 addition & 1 deletion code/game/machinery/vending/cm_vending.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1472,7 +1472,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_corresponding_types_list, list(
spawned += new itemspec_item(loc)
if(throw_objects)
for(var/atom/movable/spawned_atom in spawned)
INVOKE_ASYNC(spawned_atom, TYPE_PROC_REF(/atom/movable, throw_atom), pick(orange(src, 4)), 4, SPEED_FAST)
INVOKE_ASYNC(spawned_atom, TYPE_PROC_REF(/atom/movable, throw_atom), pick(ORANGE_TURFS(4, src)), 4, SPEED_FAST)
stat &= ~IN_USE
if(destroy)
qdel(src)
2 changes: 1 addition & 1 deletion code/game/objects/effects/decals/cleanable/blood/blood.dm
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
. = ..()
// Check if the blood is dry and only humans
// can make footprints
if(!amount || !ishuman(AM))
if(!amount || !ishuman(AM) || QDELETED(AM))
return

if(MODE_HAS_MODIFIER(/datum/gamemode_modifier/blood_optimization))
Expand Down
6 changes: 3 additions & 3 deletions code/game/objects/items/shards.dm
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@

/obj/item/large_shrapnel/at_rocket_dud/launch_impact(atom/hit_atom)
. = ..()
var/datum/launch_metadata/LM = src.launch_metadata
var/user = LM.thrower
var/datum/launch_metadata/LM = launch_metadata
var/user = LM?.thrower
if(!detonating && prob(impact_sensitivity))
cause = "manually triggered"
visible_message(SPAN_DANGER("You hear the click of a mechanism triggering inside \the [src]. Uh oh."))
Expand Down Expand Up @@ -172,7 +172,7 @@
playsound(M, 'sound/effects/meteorimpact.ogg', 35)
M.at_munition_interior_explosion_effect(cause_data = create_cause_data("Anti-Tank Rocket", U))
M.interior_crash_effect()
M.ex_act(1000, get_dir(U, T), create_cause_data("Anti-Tank Rocket", U))
M.ex_act(1000)
return TRUE
return FALSE

Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/structures/window.dm
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
anchored = FALSE
update_nearby_icons()
step(src, get_dir(AM, src))
healthcheck(user = AM.launch_metadata.thrower)
healthcheck(user = AM.launch_metadata?.thrower)

/obj/structure/window/attack_hand(mob/user as mob)
if(user.a_intent == INTENT_HARM && ishuman(user))
Expand Down
2 changes: 1 addition & 1 deletion code/game/turfs/open.dm
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@
if(!ismob(AM))
return
var/mob/unlucky_mob = AM
var/turf/target_turf = get_random_turf_in_range(AM.loc, 3, 0)
var/turf/target_turf = get_random_turf_in_range(AM, 3, 0)
var/datum/launch_metadata/LM = new()
LM.target = target_turf
LM.range = get_dist(AM.loc, target_turf)
Expand Down
4 changes: 2 additions & 2 deletions code/game/turfs/transit.dm
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
return

if(!istype(old_loc, /turf/open/space))
var/turf/projected = get_ranged_target_turf(crosser.loc, dir, 10)
var/turf/projected = get_ranged_target_turf(crosser, dir, 10)

INVOKE_ASYNC(crosser, TYPE_PROC_REF(/atom/movable, throw_atom), projected, 50, SPEED_FAST, null, TRUE)

Expand Down Expand Up @@ -106,7 +106,7 @@
//we didn't find a turf to drop them... This shouldn't happen usually
if(crosser.can_paradrop()) //don't delete them if they were supposed to paradrop
to_chat(crosser, SPAN_BOLDWARNING("Your harness got stuck and you got thrown back in the dropship."))
var/turf/projected = get_ranged_target_turf(crosser.loc, turn(dir, 180), 15)
var/turf/projected = get_ranged_target_turf(crosser, turn(dir, 180), 15)
INVOKE_ASYNC(crosser, TYPE_PROC_REF(/atom/movable, throw_atom), projected, 50, SPEED_FAST, null, TRUE)
return
return ..() // they couldn't be dropped, just delete them
Expand Down
4 changes: 2 additions & 2 deletions code/modules/cm_preds/smartdisc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
var/mob/living/L = find_target(user)
icon_state = initial(icon_state) + "_active"
if(L)
throw_atom(L.loc, 4, SPEED_FAST, usr)
throw_atom(usr, 12, SPEED_SLOW, usr)
throw_atom(get_turf(L), 4, SPEED_FAST, user)
throw_atom(user, 12, SPEED_SLOW, user)
addtimer(CALLBACK(src, PROC_REF(clear_boomerang)), 3 SECONDS)

/obj/item/explosive/grenade/spawnergrenade/smartdisc/proc/clear_boomerang()
Expand Down
2 changes: 1 addition & 1 deletion code/modules/fishing/props/fishing_pole.dm
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
var/area/A = get_area(T)

var/obj/item/caught_item = get_fishing_loot(T, A, get_common_weight(), get_uncommon_weight(), get_rare_weight(), get_ultra_rare_weight())
caught_item.throw_atom(M.loc, 2, 2, spin = TRUE, launch_type = HIGH_LAUNCH)
caught_item.throw_atom(get_turf(M), 2, 2, spin = TRUE, launch_type = HIGH_LAUNCH)
playsound(src, fishing_success, 50, 1)
M.visible_message(SPAN_NOTICE("[M] fishes up \the [caught_item]!"), SPAN_NOTICE("You fish up \the [caught_item]!"))

Expand Down
8 changes: 5 additions & 3 deletions code/modules/mob/living/carbon/carbon.dm
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,11 @@
if(legcuffed)
drop_inv_item_on_ground(legcuffed)

var/turf/my_turf = get_turf(src)

for(var/atom/movable/A in stomach_contents)
stomach_contents.Remove(A)
A.forceMove(get_turf(loc))
A.forceMove(my_turf)
A.acid_damage = 0 //Reset the acid damage
if(ismob(A))
visible_message(SPAN_DANGER("[A] bursts out of [src]!"))
Expand All @@ -109,8 +111,8 @@
if(isobj(A))
var/obj/O = A
if(O.unacidable)
O.forceMove(get_turf(loc))
O.throw_atom(pick(range(1, get_turf(loc))), 1, SPEED_FAST)
O.forceMove(my_turf)
O.throw_atom(pick(RANGE_TURFS(1, src)), 1, SPEED_FAST)

. = ..(cause)

Expand Down
7 changes: 4 additions & 3 deletions code/modules/mob/living/carbon/human/human.dm
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,10 @@
//Focus half the blast on one organ
var/mob/attack_source = last_damage_data?.resolve_mob()
var/obj/limb/take_blast = pick(limbs)
update |= take_blast.take_damage(b_loss * 0.5, f_loss * 0.5, used_weapon = "Explosive blast", attack_source = attack_source)
pain.apply_pain(b_loss * 0.5, BRUTE)
pain.apply_pain(f_loss * 0.5, BURN)
if(take_blast)
update |= take_blast.take_damage(b_loss * 0.5, f_loss * 0.5, used_weapon = "Explosive blast", attack_source = attack_source)
pain?.apply_pain(b_loss * 0.5, BRUTE)
pain?.apply_pain(f_loss * 0.5, BURN)

//Distribute the remaining half all limbs equally
b_loss *= 0.5
Expand Down
7 changes: 4 additions & 3 deletions code/modules/mob/living/carbon/human/human_defense.dm
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ Contains most of the procs that are called when a mob is attacked by something

var/obj/O = AM
var/datum/launch_metadata/LM = O.launch_metadata
var/launch_meta_valid = istype(LM)

//empty active hand and we're in throw mode
var/can_catch = (!(O.flags_atom & ITEM_UNCATCHABLE) || isyautja(src))
Expand All @@ -284,7 +285,7 @@ Contains most of the procs that are called when a mob is attacked by something
var/impact_damage = (1 + O.throwforce*THROWFORCE_COEFF)*O.throwforce*THROW_SPEED_IMPACT_COEFF*O.cur_speed

var/zone
if (istype(LM.thrower, /mob/living))
if (launch_meta_valid && istype(LM.thrower, /mob/living))
var/mob/living/L = LM.thrower
zone = check_zone(L.zone_selected)
else
Expand All @@ -295,7 +296,7 @@ Contains most of the procs that are called when a mob is attacked by something
return
O.throwing = FALSE //it hit, so stop moving

if ((LM.thrower != src) && check_shields(impact_damage, "[O]"))
if ((!launch_meta_valid || LM.thrower != src) && check_shields(impact_damage, "[O]"))
return

var/obj/limb/affecting = get_limb(zone)
Expand All @@ -322,7 +323,7 @@ Contains most of the procs that are called when a mob is attacked by something
else
playsound(loc, 'sound/effects/thud.ogg', 25, TRUE, 5, falloff = 2)

if (ismob(LM.thrower))
if (launch_meta_valid && ismob(LM.thrower))
var/mob/M = LM.thrower
var/client/assailant = M.client
if (damage > 5)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
status_flags |= LEAPING

visible_message(SPAN_WARNING("<b>[src]</b> leaps at [T]!"))
var/target = get_step(get_turf(T), get_turf(src))
var/target = get_step(get_turf(T), get_dir(src, T))
throw_atom(target, 5, SPEED_VERY_FAST, src)
playsound(loc, 'sound/voice/shriek1.ogg', 25, 1)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@
abduct_user.visible_message(SPAN_XENODANGER("\The [abduct_user]'s segmented tail starts coiling..."), SPAN_XENODANGER("We begin coiling our tail, aiming towards \the [atom]..."))
abduct_user.emote("roar")

var/throw_target_turf = get_step(abduct_user.loc, facing)
var/throw_target_turf = get_step(abduct_user, facing)

ADD_TRAIT(abduct_user, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Abduct"))
if(!do_after(abduct_user, windup, INTERRUPT_NO_NEEDHAND, BUSY_ICON_HOSTILE, numticks = 1))
Expand Down Expand Up @@ -722,6 +722,9 @@
return ..()

/datum/action/xeno_action/activable/prae_acid_ball/use_ability(atom/A)
if (!A)
return

var/mob/living/carbon/xenomorph/acidball_user = owner
if (!acidball_user.check_state() || acidball_user.action_busy)
return
Expand Down Expand Up @@ -985,8 +988,8 @@
warden.visible_message(SPAN_XENODANGER("[warden] prepares to fire its resin retrieval hook at [A]!"), SPAN_XENODANGER("We prepare to fire our resin retrieval hook at [A]!"))
warden.emote("roar")

var/throw_target_turf = get_step(warden.loc, facing)
var/turf/behind_turf = get_step(warden.loc, reversefacing)
var/throw_target_turf = get_step(warden, facing)
var/turf/behind_turf = get_step(warden, reversefacing)
if(!(behind_turf.density))
throw_target_turf = behind_turf

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,8 @@

// Fling
var/facing = get_dir(xeno, carbon)
var/turf/turf = xeno.loc
var/turf/temp = xeno.loc
var/turf/turf = get_turf(xeno)
var/turf/temp = turf

for (var/step in 0 to fling_distance-1)
temp = get_step(turf, facing)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,8 @@
if(HAS_TRAIT(src, TRAIT_CHARGING))
apply_effect(2, WEAKEN)
xeno.apply_effect(2, WEAKEN)
src.throw_atom(pick(GLOB.cardinals),1,3,xeno,TRUE)
xeno.throw_atom(pick(GLOB.cardinals),1,3,xeno,TRUE)
throw_atom(get_step(src, pick(GLOB.cardinals)), 1, 3, xeno, TRUE)
xeno.throw_atom(get_step(xeno, pick(GLOB.cardinals)), 1, 3, xeno, TRUE)
charger_ability.stop_momentum() // We assume the other crusher'sparks handle_charge_collision() kicks in and stuns us too.
playsound(get_turf(xeno), 'sound/effects/bang.ogg', 25, 0)
return
Expand All @@ -398,7 +398,7 @@
xeno.visible_message(SPAN_DANGER("[xeno] flings [src] over to the side!"),SPAN_DANGER( "You fling [src] out of the way!"))
to_chat(src, SPAN_XENOHIGHDANGER("[xeno] flings you out of its way! Move it!"))
apply_effect(1, WEAKEN) // brief flicker stun
src.throw_atom(src.loc,1,3,xeno,TRUE)
throw_atom(get_turf(src), 1, 3, xeno, TRUE)
step(src, ram_dir, charger_ability.momentum * 0.5)
charger_ability.lose_momentum(CCA_MOMENTUM_LOSS_MIN)
return XENO_CHARGE_TRY_MOVE
Expand Down
12 changes: 8 additions & 4 deletions code/modules/mob/living/living_defense.dm
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@
var/impact_damage = (1 + O.throwforce*THROWFORCE_COEFF)*O.throwforce*THROW_SPEED_IMPACT_COEFF*O.cur_speed

var/datum/launch_metadata/LM = O.launch_metadata
var/launch_meta_valid = istype(LM)

var/dist = 2
if(istype(LM))
if(launch_meta_valid)
dist = LM.dist
var/miss_chance = min(15*(dist - 2), 0)

Expand All @@ -65,7 +67,7 @@
O.throwing = 0 //it hit, so stop moving

var/mob/M
if(ismob(LM.thrower))
if(launch_meta_valid && ismob(LM.thrower))
M = LM.thrower
if(damage_done > 5)
M.track_hit(initial(O.name))
Expand All @@ -89,7 +91,8 @@

/mob/living/obj_launch_collision(obj/O)
var/datum/launch_metadata/LM = launch_metadata
if(!rebounding && LM.thrower != src)
var/launch_meta_valid = istype(LM)
if(!rebounding && (!launch_meta_valid || LM.thrower != src))
var/impact_damage = (1 + MOB_SIZE_COEFF/(mob_size + 1))*THROW_SPEED_DENSE_COEFF*cur_speed
apply_damage(impact_damage)
visible_message(SPAN_DANGER("\The [name] slams into [O]!"), null, null, 5) //feedback to know that you got slammed into a wall and it hurt
Expand All @@ -99,7 +102,8 @@
//This is called when the mob or human is thrown into a dense turf or wall
/mob/living/turf_launch_collision(turf/T)
var/datum/launch_metadata/LM = launch_metadata
if(!rebounding && LM.thrower != src)
var/launch_meta_valid = istype(LM)
if(!rebounding && (!launch_meta_valid || LM.thrower != src))
if(LM.thrower)
last_damage_data = create_cause_data("wall tossing", LM.thrower)
var/impact_damage = (1 + MOB_SIZE_COEFF/(mob_size + 1))*THROW_SPEED_DENSE_COEFF*cur_speed
Expand Down
2 changes: 1 addition & 1 deletion code/modules/movement/launching/launching.dm
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
if (isnull(launch_metadata))
CRASH("launch_impact called without any stored metadata")

var/list/collision_callbacks = launch_metadata.get_collision_callbacks(hit_atom)
var/list/collision_callbacks = launch_metadata?.get_collision_callbacks(hit_atom)
if (islist(collision_callbacks))
for(var/datum/callback/CB as anything in collision_callbacks)
if(istype(CB, /datum/callback/dynamic))
Expand Down
2 changes: 1 addition & 1 deletion code/modules/organs/limbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1534,5 +1534,5 @@ treat_grafted var tells it to apply to grafted but unsalved wounds, for burn kit

owner.visible_message("[owner]'s [owner_helmet] goes flying off from the impact!", SPAN_USERDANGER("Your [owner_helmet] goes flying off from the impact!"))
owner.drop_inv_item_on_ground(owner_helmet)
INVOKE_ASYNC(owner_helmet, TYPE_PROC_REF(/atom/movable, throw_atom), pick(range(1, get_turf(loc))), 1, SPEED_FAST)
INVOKE_ASYNC(owner_helmet, TYPE_PROC_REF(/atom/movable, throw_atom), pick(RANGE_TURFS(1, get_turf(owner))), 1, SPEED_FAST)
playsound(owner, 'sound/effects/helmet_noise.ogg', 100)

0 comments on commit 3839789

Please sign in to comment.