mirror of
https://github.com/goonstation/goonstation-2020.git
synced 2026-07-10 22:52:21 +01:00
362 lines
13 KiB
Plaintext
362 lines
13 KiB
Plaintext
/atom/var/throw_count = 0 //Counts up for tiles traveled in throw mode. Only resets for mobs.
|
|
/atom/var/throw_unlimited = 0 //Setting this to 1 before throwing will make the object behave as if in space. //If set on turf, the turf will allow infinite throwing over itself.
|
|
/atom/var/throw_return = 0 //When 1 item will return like a boomerang.
|
|
/atom/var/throw_spin = 1 //If the icon spins while thrown
|
|
/atom/var/throw_pixel = 1 //1 if the pixel vars will be adjusted depending on aiming/mouse params, on impact.
|
|
/atom/var/throw_traveled = 0
|
|
/atom/var/last_throw_x = 0
|
|
/atom/var/last_throw_y = 0
|
|
/mob/var/gib_flag = 0 //Sorry about this.
|
|
|
|
/atom/movable/proc/hit_check()
|
|
if(src.throwing)
|
|
for(var/thing in get_turf(src))
|
|
var/atom/A = thing
|
|
if (!src.throwing)
|
|
break
|
|
if(A == src) continue
|
|
if(isliving(A))
|
|
var/mob/living/L = A
|
|
if (!L.throws_can_hit_me) continue
|
|
if (L.lying) continue
|
|
src.throw_impact(A)
|
|
src.throwing = 0
|
|
// **TODO: Better behaviour for windows
|
|
// which are dense, but shouldn't always stop movement
|
|
if(isobj(A))
|
|
if(!A.CanPass(src, src.loc, 1.5))
|
|
src.throw_impact(A)
|
|
src.throwing = 0
|
|
|
|
|
|
/atom/proc/throw_begin(atom/target)
|
|
return
|
|
|
|
/atom/proc/throw_end() //throw ends (callback regardless of whether we impacted something)
|
|
return
|
|
|
|
/atom/movable/proc/throw_impact(atom/hit_atom, list/params)
|
|
var/turf/t = get_turf(hit_atom)
|
|
if( t && t.loc && t.loc:sanctuary ) return
|
|
var/impact_sfx = 0
|
|
|
|
if (isliving(hit_atom))
|
|
impact_sfx = 'sound/impact_sounds/Generic_Hit_2.ogg'
|
|
|
|
if(iscarbon(hit_atom))
|
|
var/mob/living/carbon/human/C = hit_atom //fuck you, monkeys
|
|
var/turf/T = get_turf(C)
|
|
var/turf/U = get_step(C, C.dir)
|
|
|
|
|
|
if(C && istype(src, /atom/movable))
|
|
var/atom/movable/A = src
|
|
if (C.find_type_in_hand(/obj/item/bat))
|
|
if (prob(1))
|
|
A.throw_at(get_edge_target_turf(C,get_dir(C, U)), 50, 60)
|
|
playsound(T, 'sound/items/woodbat.ogg', 50, 1)
|
|
playsound(T, 'sound/items/batcheer.ogg', 50, 1)
|
|
C.visible_message("<span style=\"color:red\">[C] hits the [src.name] with the bat and scores a HOMERUN! Woah!!!!</span>")
|
|
else
|
|
A.throw_at(get_edge_target_turf(C,get_dir(C, U)), 50, 25)
|
|
playsound(T, 'sound/items/woodbat.ogg', 50, 1)
|
|
C.visible_message("<span style=\"color:red\">[C] hits the [src.name] with the bat!</span>")
|
|
|
|
return 1
|
|
|
|
if(src.material) src.material.triggerOnAttack(src, src, hit_atom)
|
|
|
|
if (throw_pixel && islist(params) && params["icon-y"] && params["icon-x"])
|
|
src.pixel_x = text2num(params["icon-x"]) - 16
|
|
src.pixel_y = text2num(params["icon-y"]) - 16
|
|
|
|
for(var/atom/A in hit_atom)
|
|
if(A.material)
|
|
A.material.triggerOnAttacked(A, src, hit_atom, src)
|
|
|
|
if (reagents)
|
|
reagents.physical_shock(20)
|
|
|
|
if (ishuman(hit_atom)) // Haine fix for undefined proc or verb /mob/living/carbon/wall/meatcube/juggling()
|
|
var/mob/living/carbon/human/C = hit_atom //fuck you, monkeys
|
|
|
|
if (!ismob(src))
|
|
if (C.juggling())
|
|
if (prob(40))
|
|
C.visible_message("<span style=\"color:red\"><b>[C]<b> gets hit in the face by [src]!</span>")
|
|
if (hasvar(src, "throwforce"))
|
|
C.TakeDamage("head", src:throwforce, 0)
|
|
else
|
|
if (prob(C.juggling.len * 5)) // might drop stuff while already juggling things
|
|
C.drop_juggle()
|
|
else
|
|
C.add_juggle(src)
|
|
return
|
|
|
|
if(((C.in_throw_mode && C.a_intent == "help") || (C.client && C.client.check_key(KEY_THROW))) && !C.equipped())
|
|
if((C.hand && (!C.limbs.l_arm)) || (!C.hand && (!C.limbs.r_arm)) || C.handcuffed || (prob(60) && C.bioHolder.HasEffect("clumsy")) || ismob(src) || (throw_traveled <= 1 && last_throw_x == src.x && last_throw_y == src.y))
|
|
C.visible_message("<span style=\"color:red\">[C] has been hit by [src].</span>") //you're all thumbs!!!
|
|
// Added log_reagents() calls for drinking glasses. Also the location (Convair880).
|
|
logTheThing("combat", C, null, "is struck by [src] [src.is_open_container() ? "[log_reagents(src)]" : ""] at [log_loc(C)].")
|
|
if(src.vars.Find("throwforce"))
|
|
random_brute_damage(C, src:throwforce)
|
|
|
|
#ifdef DATALOGGER
|
|
game_stats.Increment("violence")
|
|
#endif
|
|
|
|
if(src.vars.Find("throwforce") && src:throwforce >= 40)
|
|
C.throw_at(get_edge_target_turf(C,get_dir(src, C)), 10, 1)
|
|
C.changeStatus("stunned", 3 SECONDS)
|
|
|
|
if(ismob(src)) src:throw_impacted(hit_atom)
|
|
|
|
else
|
|
src.attack_hand(C) // nice catch, hayes. don't ever fuckin do it again
|
|
C.visible_message("<span style=\"color:red\">[C] catches the [src.name]!</span>")
|
|
logTheThing("combat", C, null, "catches [src] [src.is_open_container() ? "[log_reagents(src)]" : ""] at [log_loc(C)].")
|
|
C.throw_mode_off()
|
|
#ifdef DATALOGGER
|
|
game_stats.Increment("catches")
|
|
#endif
|
|
|
|
else //normmal thingy hit me
|
|
if (src.throwing & THROW_CHAIRFLIP)
|
|
C.visible_message("<span style=\"color:red\">[src] slams into [C] midair!</span>")
|
|
else
|
|
C.visible_message("<span style=\"color:red\">[C] has been hit by [src].</span>")
|
|
if(src.vars.Find("throwforce"))
|
|
random_brute_damage(C, src:throwforce)
|
|
|
|
logTheThing("combat", C, null, "is struck by [src] [src.is_open_container() ? "[log_reagents(src)]" : ""] at [log_loc(C)].")
|
|
|
|
//bleed check here
|
|
if (isitem(src))
|
|
if ((src:hit_type == DAMAGE_STAB && prob(20)) || (src:hit_type == DAMAGE_CUT && prob(40)))
|
|
take_bleeding_damage(C, null, 1, src:hit_type)
|
|
impact_sfx = 'sound/impact_sounds/Flesh_Stab_3.ogg'
|
|
|
|
|
|
#ifdef DATALOGGER
|
|
game_stats.Increment("violence")
|
|
#endif
|
|
|
|
if(src.vars.Find("throwforce") && src:throwforce >= 40)
|
|
C.throw_at(get_edge_target_turf(C,get_dir(src, C)), 10, 1)
|
|
C.changeStatus("stunned", 3 SECONDS)
|
|
|
|
if(ismob(src)) src:throw_impacted(hit_atom)
|
|
|
|
|
|
else if(issilicon(hit_atom))
|
|
var/mob/living/silicon/S = hit_atom
|
|
S.visible_message("<span style=\"color:red\">[S] has been hit by [src].</span>")
|
|
logTheThing("combat", S, null, "is struck by [src] [src.is_open_container() ? "[log_reagents(src)]" : ""] at [log_loc(S)].")
|
|
if(src.vars.Find("throwforce"))
|
|
random_brute_damage(S, src:throwforce)
|
|
|
|
#ifdef DATALOGGER
|
|
game_stats.Increment("violence")
|
|
#endif
|
|
|
|
if(src.vars.Find("throwforce") && src:throwforce >= 40)
|
|
S.throw_at(get_edge_target_turf(S,get_dir(src, S)), 10, 1)
|
|
|
|
if(ismob(src)) src:throw_impacted(hit_atom)
|
|
|
|
impact_sfx = impact_sfx = 'sound/impact_sounds/Metal_Clang_3.ogg'
|
|
|
|
|
|
else if(isobj(hit_atom))
|
|
var/obj/O = hit_atom
|
|
if(!O.anchored) step(O, src.dir)
|
|
O.hitby(src)
|
|
if(ismob(src)) src:throw_impacted(hit_atom)
|
|
if(O && src.vars.Find("throwforce") && src:throwforce >= 40)
|
|
if(!O.anchored && !O.throwing)
|
|
O.throw_at(get_edge_target_turf(O,get_dir(src, O)), 10, 1)
|
|
else if(src:throwforce >= 80 && !isrestrictedz(O.z))
|
|
O.meteorhit(src)
|
|
|
|
else if(isturf(hit_atom))
|
|
var/turf/T = hit_atom
|
|
if(T.density)
|
|
//SPAWN_DBG(2 DECI SECONDS) step(src, turn(src.dir, 180))
|
|
if(ismob(src)) src:throw_impacted(hit_atom)
|
|
/*if(istype(hit_atom, /turf/simulated/wall) && isitem(src))
|
|
var/turf/simulated/wall/W = hit_atom
|
|
W.take_hit(src)*/
|
|
if(src.vars.Find("throwforce") && src:throwforce >= 80)
|
|
T.meteorhit(src)
|
|
|
|
impact_sfx = impact_sfx = 'sound/impact_sounds/Generic_Stab_1.ogg'
|
|
|
|
if (impact_sfx && src)
|
|
playsound(src, impact_sfx, 40, 1)
|
|
|
|
/atom/movable/Bump(atom/O)
|
|
if(src.throwing)
|
|
src.throw_impact(O)
|
|
src.throwing = 0
|
|
..()
|
|
|
|
/atom/movable/proc/throw_at(atom/target, range, speed, list/params, turf/thrown_from, throw_type = 1)
|
|
//use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target
|
|
if (!target) return
|
|
if (reagents)
|
|
reagents.physical_shock(14)
|
|
src.throwing = throw_type
|
|
|
|
if (src.throwing & (THROW_CHAIRFLIP | THROW_GUNIMPACT))
|
|
if (ismob(src))
|
|
var/mob/M = src
|
|
M.force_laydown_standup()
|
|
|
|
src.throw_traveled = 0
|
|
src.last_throw_x = src.x
|
|
src.last_throw_y = src.y
|
|
src.throw_begin(target)
|
|
|
|
//Gotta do this in 4 steps or byond decides that the best way to interpolate between (0 and) 180 and 360 is to just flip the icon over, not turn it.
|
|
if(!istype(src)) return
|
|
|
|
var/matrix/transform_original = src.transform
|
|
if (src.throw_spin == 1)
|
|
animate(src, transform = matrix(transform_original, 120, MATRIX_ROTATE | MATRIX_MODIFY), time = 8/3, loop = -1)
|
|
animate(transform = matrix(transform_original, 120, MATRIX_ROTATE | MATRIX_MODIFY), time = 8/3, loop = -1)
|
|
animate(transform = matrix(transform_original, 120, MATRIX_ROTATE | MATRIX_MODIFY), time = 8/3, loop = -1)
|
|
|
|
var/hitAThing = 0
|
|
var/target_true_x = target.x
|
|
var/target_true_y = target.y
|
|
|
|
if (isobj(target.loc))
|
|
var/obj/container = target.loc
|
|
if (target in container.contents)
|
|
target_true_x = container.x
|
|
target_true_y = container.y
|
|
|
|
|
|
var/dist_x = abs(target_true_x - src.x)
|
|
var/dist_y = abs(target_true_y - src.y)
|
|
|
|
var/dx
|
|
if (target_true_x > src.x)
|
|
dx = EAST
|
|
else
|
|
dx = WEST
|
|
|
|
var/dy
|
|
if (target_true_y > src.y)
|
|
dy = NORTH
|
|
else
|
|
dy = SOUTH
|
|
|
|
var/dist_travelled = 0
|
|
var/dist_since_sleep = 0
|
|
|
|
if(dist_x > dist_y)
|
|
var/error = dist_x/2 - dist_y
|
|
var/turf/T = src.loc
|
|
while (target && ( (((src.x < target_true_x && dx == EAST) || (src.x > target_true_x && dx == WEST)) && dist_travelled < range) || (T && T.throw_unlimited) || src.throw_unlimited) && src.throwing && isturf(src.loc))
|
|
// only stop when we've gone the whole distance (or max throw range) and are on a non-space tile, or hit something, or hit the end of the map, or someone picks it up
|
|
if(error < 0)
|
|
var/atom/step = get_step(src, dy)
|
|
if(!step || step == src.loc) // going off the edge of the map makes get_step return null, don't let things go off the edge
|
|
break
|
|
src.glide_size = (32 / (1/speed)) * world.tick_lag
|
|
if (!Move(step)) // Grayshift: Race condition fix. Bump proc calls are delayed past the end of the loop and won't trigger end condition
|
|
hitAThing = 1 // of !throwing on their own, so manually checking if Move failed as end condition
|
|
break
|
|
src.glide_size = (32 / (1/speed)) * world.tick_lag
|
|
hit_check()
|
|
error += dist_x
|
|
dist_travelled++
|
|
src.throw_count++
|
|
dist_since_sleep++
|
|
if(dist_since_sleep >= speed)
|
|
dist_since_sleep = 0
|
|
sleep(1)
|
|
else
|
|
var/atom/step = get_step(src, dx)
|
|
if(!step || step == src.loc) // going off the edge of the map makes get_step return null, don't let things go off the edge
|
|
break
|
|
src.glide_size = (32 / (1/speed)) * world.tick_lag
|
|
if (!Move(step))
|
|
hitAThing = 1
|
|
break
|
|
src.glide_size = (32 / (1/speed)) * world.tick_lag
|
|
hit_check()
|
|
error -= dist_y
|
|
dist_travelled++
|
|
src.throw_count++
|
|
dist_since_sleep++
|
|
if(dist_since_sleep >= speed)
|
|
dist_since_sleep = 0
|
|
sleep(1)
|
|
T = src.loc
|
|
else
|
|
var/error = dist_y/2 - dist_x
|
|
var/turf/T = src.loc
|
|
while (target && ( (((src.y < target_true_y && dy == NORTH) || (src.y > target_true_y && dy == SOUTH)) && dist_travelled < range) || (T && T.throw_unlimited) || src.throw_unlimited) && src.throwing && isturf(src.loc))
|
|
// only stop when we've gone the whole distance (or max throw range) and are on a non-space tile, or hit something, or hit the end of the map, or someone picks it up
|
|
if(error < 0)
|
|
var/atom/step = get_step(src, dx)
|
|
if(!step || step == src.loc) // going off the edge of the map makes get_step return null, don't let things go off the edge
|
|
break
|
|
src.glide_size = (32 / (1/speed)) * world.tick_lag
|
|
if (!Move(step))
|
|
hitAThing = 1
|
|
break
|
|
src.glide_size = (32 / (1/speed)) * world.tick_lag
|
|
hit_check()
|
|
error += dist_y
|
|
dist_travelled++
|
|
src.throw_count++
|
|
dist_since_sleep++
|
|
if(dist_since_sleep >= speed)
|
|
dist_since_sleep = 0
|
|
sleep(1)
|
|
else
|
|
var/atom/step = get_step(src, dy)
|
|
if(!step || step == src.loc) // going off the edge of the map makes get_step return null, don't let things go off the edge
|
|
break
|
|
src.glide_size = (32 / (1/speed)) * world.tick_lag
|
|
if (!Move(step))
|
|
hitAThing = 1
|
|
break
|
|
src.glide_size = (32 / (1/speed)) * world.tick_lag
|
|
hit_check()
|
|
error -= dist_x
|
|
dist_travelled++
|
|
src.throw_count++
|
|
dist_since_sleep++
|
|
if(dist_since_sleep >= speed)
|
|
dist_since_sleep = 0
|
|
sleep(1)
|
|
T = src.loc
|
|
|
|
//done throwing, either because it hit something or it finished moving
|
|
src.throw_end()
|
|
if (!hitAThing) // Bump proc requires throwing flag to be set, so if we hit a thing, leave it on and let Bump turn it off
|
|
src.throwing = 0
|
|
else // if we hit something don't use the pixel x/y from the click params
|
|
params = null
|
|
|
|
src.throw_unlimited = 0
|
|
animate(src, transform = transform_original)
|
|
|
|
//Wire note: Small fix stemming from pie science. Throw a pie at yourself! Whoa!
|
|
//if (target == usr)
|
|
// src.throw_impact(target)
|
|
// src.throwing = 0
|
|
//Somepotato note: this is gross. Way to make wireless killing machines!!!
|
|
|
|
throw_traveled = dist_travelled
|
|
if(isobj(src)) src:throw_impact(get_turf(src), params)
|
|
|
|
if(target != usr && src.throw_return) throw_at(usr, src.throw_range, src.throw_speed)
|
|
//testing boomrang stuff
|
|
//throw_at(atom/target, range, speed)//
|
|
//if(target != usr) throw_at(usr, 10, 1)
|