Merge pull request #10598 from phil235/CatchThrownPieFix

Changes to throwing
This commit is contained in:
Cheridan
2015-07-20 20:26:35 -05:00
38 changed files with 274 additions and 352 deletions
+6 -5
View File
@@ -42,11 +42,12 @@
the atom/checkpass() proc uses them (tables will call movable atom checkpass(PASSTABLE) for example)
*/
//flags for pass_flags
#define PASSTABLE 1
#define PASSGLASS 2
#define PASSGRILLE 4
#define PASSBLOB 8
#define PASSMOB 16
#define PASSTABLE 1
#define PASSGLASS 2
#define PASSGRILLE 4
#define PASSBLOB 8
#define PASSMOB 16
#define LETPASSTHROW 32
//flags for species
+5 -15
View File
@@ -23,7 +23,7 @@
* If you are in the same turf, always true
* If you are vertically/horizontally adjacent, ensure there are no border objects
* If you are diagonally adjacent, ensure you can pass through at least one of the mutually adjacent square.
* Passing through in this case ignores anything with the throwpass flag, such as tables, racks, and morgue trays.
* Passing through in this case ignores anything with the LETPASSTHROW pass flag, such as tables, racks, and morgue trays.
*/
/turf/Adjacent(var/atom/neighbor, var/atom/target = null)
var/turf/T0 = get_turf(neighbor)
@@ -80,28 +80,18 @@
/*
This checks if you there is uninterrupted airspace between that turf and this one.
This is defined as any dense ON_BORDER object, or any dense object without throwpass.
This is defined as any dense ON_BORDER object, or any dense object without LETPASSTHROW.
The border_only flag allows you to not objects (for source and destination squares)
*/
/turf/proc/ClickCross(target_dir, border_only, target_atom = null)
for(var/obj/O in src)
if( !O.density || O == target_atom || O.throwpass) //check if there's a dense object present on the turf
continue // throwpass is used for anything you can click through (or the firedoor special case, see above)
if( !O.density || O == target_atom || (O.pass_flags & LETPASSTHROW)) //check if there's a dense object present on the turf
continue // LETPASSTHROW is used for anything you can click through (or the firedoor special case, see above)
if( O.flags&ON_BORDER) // windows have throwpass but are on border, check them first
if( O.flags&ON_BORDER) // windows are on border, check them first
if( O.dir & target_dir || O.dir & (O.dir-1) ) // full tile windows are just diagonals mechanically
return 0 //O.dir&(O.dir-1) is false for any cardinal direction, but true for diagonal ones
else if( !border_only ) // dense, not on border, cannot pass over
return 0
return 1
/*
Aside: throwpass does not do what I thought it did originally, and is only used for checking whether or not
a thrown object should stop after already successfully entering a square. Currently the throw code involved
only seems to affect hitting mobs, because the checks performed against objects are already performed when
entering or leaving the square. Since throwpass isn't used on mobs, but only on objects, it is effectively
useless. Throwpass may later need to be removed and replaced with a passcheck (bitfield on movable atom passflags).
Since I don't want to complicate the click code rework by messing with unrelated systems it won't be changed here.
*/
+4 -25
View File
@@ -6,8 +6,6 @@
var/list/fingerprintshidden
var/fingerprintslast = null
var/list/blood_DNA
var/last_bumped = 0
var/throwpass = 0
///Chemistry.
var/datum/reagents/reagents = null
@@ -50,26 +48,6 @@
return 0
/atom/proc/throw_impact(atom/hit_atom,mob/thrower)
if(istype(hit_atom,/mob/living))
var/mob/living/M = hit_atom
M.hitby(src,thrower)
else if(isobj(hit_atom))
var/obj/O = hit_atom
if(!O.anchored)
step(O, src.dir)
O.hitby(src)
else if(isturf(hit_atom))
var/turf/T = hit_atom
if(T.density)
spawn(2)
step(src, turn(src.dir, 180))
if(istype(src,/mob/living))
var/mob/living/M = src
M.take_organ_damage(20)
/atom/proc/attack_hulk(mob/living/carbon/human/hulk, do_attack_animation = 0)
if(do_attack_animation)
hulk.changeNext_move(CLICK_CD_MELEE)
@@ -280,9 +258,10 @@ its easier to just keep the beam vertical.
/atom/proc/fire_act()
return
/atom/proc/hitby(atom/movable/AM as mob|obj)
return
/atom/proc/hitby(atom/movable/AM, skip, var/hitpush)
if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav.
spawn(2)
step(AM, turn(AM.dir, 180))
var/list/blood_splatter_icons = list()
+67 -46
View File
@@ -93,12 +93,12 @@
/atom/movable/Crossed(atom/movable/AM)
return
/atom/movable/Bump(var/atom/A as mob|obj|turf|area, yes)
if(throwing)
throw_impact(A)
throwing = 0
if ((A && yes))
A.last_bumped = world.time
/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump().
if((A && yes))
if(throwing)
throwing = 0
throw_impact(A)
. = 1
A.Bumped(src)
@@ -143,7 +143,6 @@
if(!direction)
return 1
var/old_dir = dir
. = step(src, direction)
dir = old_dir
@@ -151,70 +150,92 @@
/atom/movable/proc/checkpass(passflag)
return pass_flags&passflag
/atom/movable/proc/hit_check(mob/thrower) // todo: this is partly obsolete due to passflags already, add throwing stuff to mob CanPass and finish it
if(src.throwing)
for(var/atom/A in get_turf(src))
if(A == src) continue
if(istype(A,/mob/living))
if(A:lying) continue
src.throw_impact(A,thrower)
if(src.throwing == 1)
src.throwing = 0
if(isobj(A))
if(A.density && !A.throwpass) // **TODO: Better behaviour for windows which are dense, but shouldn't always stop movement
src.throw_impact(A,thrower)
src.throwing = 0
/atom/movable/proc/throw_impact(atom/hit_atom)
return hit_atom.hitby(src)
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, var/spin = 1)
/atom/movable/hitby(atom/movable/AM, skip, var/hitpush = 1)
if(!anchored && hitpush)
step(src, AM.dir)
return ..()
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
if(!target || !src || (flags & NODROP)) return 0
//use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target
src.throwing = 1
if(spin) // turns out 1000+ spinning objects being thrown at the singularity creates lag - Iamgoofball
throwing = 1
if(spin) //if we don't want the /atom/movable to spin.
SpinAnimation(5, 1)
var/dist_travelled = 0
var/dist_since_sleep = 0
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)
var/dx = (target.x > src.x) ? EAST : WEST
var/dy = (target.y > src.y) ? NORTH : SOUTH
var/dist_travelled = 0
var/dist_since_sleep = 0
var/tdist_x = dist_x;
var/tdist_y = dist_y;
var/tdx = dx;
var/tdy = dy;
var/pure_diagonal = 0
if(dist_x == dist_y)
pure_diagonal = 1
if(dist_x <= dist_y)
tdist_x = dist_y;
tdist_y = dist_x;
tdx = dy;
tdy = dx;
var/olddist_x = dist_x
var/olddx = dx
dist_x = dist_y
dist_y = olddist_x
dx = dy
dy = olddx
var/error = tdist_x/2 - tdist_y
while(target && (((((dist_x > dist_y) && ((src.x < target.x && dx == EAST) || (src.x > target.x && dx == WEST))) || ((dist_x <= dist_y) && ((src.y < target.y && dy == NORTH) || (src.y > target.y && dy == SOUTH))) || (src.x > target.x && dx == WEST)) && dist_travelled < range) || !has_gravity(src)))
// 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(!src.throwing) break
if(!istype(src.loc, /turf)) break
var/error = dist_x/2 - dist_y
var/atom/finalturf = get_turf(target)
var/hit = 0
var/atom/step = get_step(src, (error < 0) ? tdy : tdx)
while(target && ((dist_travelled < range && loc != finalturf) || !has_gravity(src))) //stop if we reached our destination (or max range) and aren't floating
if(!istype(loc, /turf))
hit = 1
break
var/atom/step = get_step(src, get_dir(src, target))
if(!pure_diagonal && !diagonals_first) // not a purely diagonal trajectory and we don't want all diagonal moves to be done first
if(error >= 0 && get_dist(src, finalturf) > 1)
step = get_step(src, dx)
error += (error < 0) ? dist_x/2 : -dist_y
if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
break
src.Move(step, get_dir(loc, step))
hit_check(thrower)
error += (error < 0) ? tdist_x : -tdist_y;
Move(step, get_dir(loc, step))
if(!throwing) // we hit something during our move
hit = 1
break
dist_travelled++
dist_since_sleep++
if(dist_since_sleep >= speed)
dist_since_sleep = 0
sleep(1)
//done throwing, either because it hit something or it finished moving
src.throwing = 0
if(isobj(src))
src.throw_impact(get_turf(src),thrower)
if(!dist_since_sleep && hitcheck()) //to catch sneaky things moving on our tile during our sleep(1)
hit = 1
break
//done throwing, either because it hit something or it finished moving
throwing = 0
if(!hit)
for(var/atom/A in get_turf(src)) //looking for our target on the turf we land on.
if(A == target)
hit = 1
throw_impact(A)
return 1
throw_impact(get_turf(src)) // we haven't hit something yet and we still must, let's hit the ground.
return 1
/atom/movable/proc/hitcheck()
for(var/atom/movable/AM in get_turf(src))
if(AM == src)
continue
if(AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags & ON_BORDER))
throw_impact(AM)
return 1
//Overlays
/atom/movable/overlay
@@ -24,17 +24,6 @@
name = "desk"
desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl"
icon_state = "tomealtar"
// luminosity = 5
//sprites for this no longer exist -Pete
//(they were stolen from another game anyway)
/*
/obj/structure/cult/pillar
name = "Pillar"
desc = "This should not exist"
icon_state = "pillar"
icon = 'magic_pillar.dmi'
*/
/obj/effect/gateway
name = "gateway"
@@ -44,13 +33,3 @@
density = 1
unacidable = 1
anchored = 1.0
/obj/effect/gateway/Bumped(mob/M as mob|obj)
spawn(0)
return
return
/obj/effect/gateway/Crossed(AM as mob|obj)
spawn(0)
return
return
-8
View File
@@ -397,14 +397,6 @@ Auto Patrol[]"},
return 1
return 0
/* terrible
/obj/machinery/bot/ed209/Bumped(atom/movable/M as mob|obj)
spawn(0)
if (M)
var/turf/T = get_turf(src)
M:loc = T
*/
/obj/machinery/bot/ed209/explode()
walk_to(src,0)
visible_message("<span class='boldannounce'>[src] blows apart!</span>")
@@ -410,6 +410,7 @@
var/mob/living/carbon/M = hit_atom
playsound(src, 'sound/items/dodgeball.ogg', 50, 1)
M.apply_damage(10, STAMINA)
loc = get_turf(hit_atom) //drop at the target's feet
if(prob(5))
M.Weaken(3)
visible_message("<span class='danger'>[M] is knocked right off \his feet!</span>", 3)
@@ -421,7 +422,6 @@
icon_state = "hoop"
anchored = 1
density = 1
throwpass = 1
/obj/structure/holohoop/attackby(obj/item/weapon/W, mob/user, params)
if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2)
+2 -2
View File
@@ -45,8 +45,8 @@
/obj/machinery/door/Bumped(atom/AM)
if(operating || emagged) return
if(ismob(AM))
var/mob/M = AM
if(isliving(AM))
var/mob/living/M = AM
if(world.time - M.last_bumped <= 10) return //Can bump-open one airlock per second. This is to prevent shock spam.
M.last_bumped = world.time
if(!M.restrained())
+4 -2
View File
@@ -16,8 +16,10 @@
closingLayer = 3.11
/obj/machinery/door/firedoor/Bumped(atom/AM)
if(p_open || operating) return
if(!density) return ..()
if(p_open || operating)
return
if(!density)
return ..()
return 0
+2
View File
@@ -101,6 +101,8 @@ var/const/SAFETY_COOLDOWN = 100
if(stat & (BROKEN|NOPOWER))
return
if(!anchored)
return
if(safety_mode)
return
+5 -8
View File
@@ -15,17 +15,14 @@
if(isanimal(AM))
var/mob/living/simple_animal/SA = AM
if(!SA.flying)
Bumped(SA)
triggermine(SA)
else
Bumped(AM)
/obj/effect/mine/Bumped(AM as mob|obj)
if(triggered) return
visible_message("<span class='danger'>[AM] sets off \icon[src] [src]!</span>")
triggermine(AM)
triggermine(AM)
/obj/effect/mine/proc/triggermine(mob/victim)
if(triggered)
return
visible_message("<span class='danger'>[victim] sets off \icon[src] [src]!</span>")
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(3, 1, src)
s.start()
+11 -18
View File
@@ -55,6 +55,8 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s
var/suittoggled = 0
var/hooded = 0
var/mob/thrownby = null
/obj/item/mouse_drag_pointer = MOUSE_ACTIVE_POINTER //the icon to indicate this object is being dragged
//So items can have custom embedd values
@@ -469,26 +471,17 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s
if(!findtext(desc, "it looks slightly melted...")) //it looks slightly melted... it looks slightly melted... it looks slightly melted... etc.
desc += " it looks slightly melted..." //needs a space at the start, formatting
/obj/item/throw_impact(atom/A)
var/itempush = 1
if(w_class < 4)
itempush = 0 //too light to push anything
return A.hitby(src,thrownby, 0, itempush)
/obj/item/throw_impact(A)
if(throw_speed >= EMBED_THROWSPEED_THRESHOLD)
if(istype(A, /mob/living/carbon/human))
var/mob/living/carbon/human/H = A
if(can_embed(src))
if(prob(embed_chance) && !(PIERCEIMMUNE in H.dna.species.specflags))
H.throw_alert("embeddedobject")
var/obj/item/organ/limb/L = pick(H.organs)
L.embedded_objects |= src
add_blood(H)//it embedded itself in you, of course it's bloody!
loc = H
L.take_damage(w_class*embedded_impact_pain_multiplier)
H.visible_message("<span class='danger'>\the [name] embeds itself in [H]'s [L.getDisplayName()]!</span>","<span class='userdanger'>\the [name] embeds itself in your [L.getDisplayName()]!</span>")
return
//Reset regardless of if we hit a human.
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1)
thrownby = thrower
. = ..()
throw_speed = initial(throw_speed) //explosions change this.
..()
/obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/weapon/storage
if(!newLoc)
+46 -43
View File
@@ -44,41 +44,40 @@
/obj/item/toy/balloon/afterattack(atom/A as mob|obj, mob/user, proximity)
if(!proximity) return
if (istype(A, /obj/structure/reagent_dispensers/watertank) && get_dist(src,A) <= 1)
A.reagents.trans_to(src, 10)
user << "<span class='notice'>You fill the balloon with the contents of [A].</span>"
src.desc = "A translucent balloon with some form of liquid sloshing around in it."
src.update_icon()
return
if (istype(A, /obj/structure/reagent_dispensers))
var/obj/structure/reagent_dispensers/RD = A
if(RD.reagents.total_volume <= 0)
user << "<span class='warning'>[RD] is empty.</span>"
else if(reagents.total_volume >= 10)
user << "<span class='warning'>[src] is full.</span>"
else
A.reagents.trans_to(src, 10)
user << "<span class='notice'>You fill the balloon with the contents of [A].</span>"
desc = "A translucent balloon with some form of liquid sloshing around in it."
update_icon()
/obj/item/toy/balloon/attackby(obj/O, mob/user, params)
if(istype(O, /obj/item/weapon/reagent_containers/glass))
if(O.reagents)
if(O.reagents.total_volume < 1)
user << "<span class='notice'>The [O] is empty.</span>"
else if(O.reagents.total_volume >= 1)
if(O.reagents.has_reagent("facid", 1))
user << "<span class='warning'>The acid chews through the balloon!</span>"
O.reagents.reaction(user)
qdel(src)
else
src.desc = "A translucent balloon with some form of liquid sloshing around in it."
user << "<span class='notice'>You fill the balloon with the contents of [O].</span>"
O.reagents.trans_to(src, 10)
src.update_icon()
return
if(O.reagents.total_volume <= 0)
user << "<span class='warning'>[O] is empty.</span>"
else if(reagents.total_volume >= 10)
user << "<span class='warning'>[src] is full.</span>"
else
desc = "A translucent balloon with some form of liquid sloshing around in it."
user << "<span class='notice'>You fill the balloon with the contents of [O].</span>"
O.reagents.trans_to(src, 10)
update_icon()
/obj/item/toy/balloon/throw_impact(atom/hit_atom)
if(src.reagents.total_volume >= 1)
src.visible_message("<span class='danger'>\The [src] bursts!</span>","<span class='italics'>You hear a pop and a splash.</span>")
src.reagents.reaction(get_turf(hit_atom))
for(var/atom/A in get_turf(hit_atom))
src.reagents.reaction(A)
src.icon_state = "burst"
spawn(5)
if(src)
qdel(src)
return
if(!..()) //was it caught by a mob?
if(reagents.total_volume >= 1)
visible_message("<span class='danger'>[src] bursts!</span>","<span class='italics'>You hear a pop and a splash.</span>")
reagents.reaction(get_turf(hit_atom))
for(var/atom/A in get_turf(hit_atom))
reagents.reaction(A)
icon_state = "burst"
qdel(src)
/obj/item/toy/balloon/update_icon()
if(src.reagents.total_volume >= 1)
@@ -514,20 +513,24 @@
icon_state = "snappop"
w_class = 1
/obj/item/toy/snappop/fire_act()
throw_impact()
return
/obj/item/toy/snappop/throw_impact(atom/hit_atom)
..()
/obj/item/toy/snappop/proc/pop_burst()
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(3, 1, src)
s.start()
new /obj/effect/decal/cleanable/ash(src.loc)
src.visible_message("<span class='suicide'>The [src.name] explodes!</span>","<span class='italics'>You hear a snap!</span>")
new /obj/effect/decal/cleanable/ash(loc)
visible_message("<span class='warning'>The [src.name] explodes!</span>","<span class='italics'>You hear a snap!</span>")
playsound(src, 'sound/effects/snap.ogg', 50, 1)
qdel(src)
/obj/item/toy/snappop/fire_act()
pop_burst()
return
/obj/item/toy/snappop/throw_impact(atom/hit_atom)
if(!..())
pop_burst()
/obj/item/toy/snappop/Crossed(H as mob|obj)
if((ishuman(H))) //i guess carp and shit shouldn't set them off
var/mob/living/carbon/M = H
@@ -1100,12 +1103,12 @@
w_class = 2.0
/obj/item/toy/minimeteor/throw_impact(atom/hit_atom)
..()
playsound(src, 'sound/effects/meteorimpact.ogg', 40, 1)
for(var/mob/M in range(10, src))
if(!M.stat && !istype(M, /mob/living/silicon/ai))\
shake_camera(M, 3, 1)
qdel(src)
if(!..())
playsound(src, 'sound/effects/meteorimpact.ogg', 40, 1)
for(var/mob/M in range(10, src))
if(!M.stat && !istype(M, /mob/living/silicon/ai))\
shake_camera(M, 3, 1)
qdel(src)
/*
* Carp plushie
+1 -1
View File
@@ -56,7 +56,7 @@
/obj/item/weapon/dice/attack_self(mob/user)
diceroll(user)
/obj/item/weapon/dice/throw_at(atom/target, range, speed, mob/user)
/obj/item/weapon/dice/throw_at(atom/target, range, speed, mob/user, spin=1)
if(!..())
return
diceroll(user)
@@ -79,15 +79,20 @@
return 1
/obj/structure/closet/proc/dump_contents()
for(var/obj/O in src)
O.loc = loc
if(throwing) //you keep some momentum when getting out of a thrown closet
step(O, dir)
for(var/mob/M in src)
M.loc = loc
if(M.client)
M.client.eye = M.client.mob
M.client.perspective = MOB_PERSPECTIVE
if(throwing)
step(M, dir)
if(throwing)
throwing = 0
/obj/structure/closet/proc/take_contents()
@@ -100,14 +105,13 @@
return 0
if(!can_open())
return 0
dump_contents()
opened = 1
if(istype(src, /obj/structure/closet/body_bag))
playsound(loc, 'sound/items/zip.ogg', 15, 1, -3)
else
playsound(loc, 'sound/machines/click.ogg', 15, 1, -3)
density = 0
dump_contents()
update_icon()
return 1
@@ -65,9 +65,9 @@
/obj/structure/mineral_door/proc/TryToSwitchState(atom/user)
if(isSwitchingStates) return
if(ismob(user))
var/mob/M = user
if(world.time - user.last_bumped <= 60) return //NOTE do we really need that?
if(isliving(user))
var/mob/living/M = user
if(world.time - M.last_bumped <= 60) return //NOTE do we really need that?
if(M.client)
if(iscarbon(M))
var/mob/living/carbon/C = M
+10 -1
View File
@@ -226,7 +226,7 @@ Crematorium Switch
layer = 2.9
var/obj/structure/bodycontainer/connected = null
anchored = 1.0
throwpass = 1
pass_flags = LETPASSTHROW
/obj/structure/tray/Destroy()
if(connected)
@@ -278,3 +278,12 @@ Crematorium Switch
desc = "Apply corpse before closing."
icon_state = "morguet"
/obj/structure/tray/m_tray/CanPass(atom/movable/mover, turf/target, height=0)
if(height==0) return 1
if(istype(mover) && mover.checkpass(PASSTABLE))
return 1
if(locate(/obj/structure/table) in get_turf(mover))
return 1
else
return 0
+2 -2
View File
@@ -20,7 +20,7 @@
density = 1
anchored = 1.0
layer = 2.8
throwpass = 1 //You can throw objects over this, despite it's density.")
pass_flags = LETPASSTHROW //You can throw objects over this, despite it's density.")
var/frame = /obj/structure/table_frame
var/framestack = /obj/item/stack/rods
var/buildstack = /obj/item/stack/sheet/metal
@@ -364,7 +364,7 @@
icon_state = "rack"
density = 1
anchored = 1.0
throwpass = 1 //You can throw objects over this, despite it's density.
pass_flags = LETPASSTHROW //You can throw objects over this, despite it's density.
var/health = 5
/obj/structure/rack/ex_act(severity, target)
-5
View File
@@ -9,8 +9,3 @@
hitsound = 'sound/items/welder.ogg'
if(damtype == "brute")
hitsound = "swing_hit"
/obj/item/weapon/Bump(mob/M)
spawn(0)
..()
return
+2 -43
View File
@@ -171,47 +171,6 @@ Gunshots/explosions/opening doors/less rare audio (done)
name = "alien hunter ([rand(1, 1000)])"
return
/obj/effect/hallucination/simple/xeno/throw_at(atom/target, range, speed) // TODO : Make diagonal trhow into proc/property
if(!target || !src || (flags & NODROP)) return 0
src.throwing = 1
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)
var/dist_travelled = 0
var/dist_since_sleep = 0
var/tdist_x = dist_x;
var/tdist_y = dist_y;
if(dist_x <= dist_y)
tdist_x = dist_y;
tdist_y = dist_x;
var/error = tdist_x/2 - tdist_y
while(target && (((((dist_x > dist_y) && ((src.x < target.x) || (src.x > target.x))) || ((dist_x <= dist_y) && ((src.y < target.y) || (src.y > target.y))) || (src.x > target.x)) && dist_travelled < range) || !has_gravity(src)))
if(!src.throwing) break
if(!istype(src.loc, /turf)) break
var/atom/step = get_step(src, get_dir(src,target))
if(!step)
break
src.Move(step, get_dir(src, step))
hit_check()
error += (error < 0) ? tdist_x : -tdist_y;
dist_travelled++
dist_since_sleep++
if(dist_since_sleep >= speed)
dist_since_sleep = 0
sleep(1)
src.throwing = 0
src.throw_impact(get_turf(src))
return 1
/obj/effect/hallucination/simple/xeno/throw_impact(A)
update_icon("alienh_pounce")
if(A == target)
@@ -232,10 +191,10 @@ Gunshots/explosions/opening doors/less rare audio (done)
xeno = new(pump.loc,target)
sleep(10)
xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi',-32,-32)
xeno.throw_at(target,7,1)
xeno.throw_at(target,7,1, spin = 0, diagonals_first = 1)
sleep(10)
xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi',-32,-32)
xeno.throw_at(pump,7,1)
xeno.throw_at(pump,7,1, spin = 0, diagonals_first = 1)
sleep(10)
var/xeno_name = xeno.name
target << "<span class='notice'>[xeno_name] begins climbing into the ventilation system...</span>"
+5 -4
View File
@@ -18,10 +18,11 @@
filling_color = "#F0E68C"
/obj/item/weapon/reagent_containers/food/snacks/egg/throw_impact(atom/hit_atom)
..()
new/obj/effect/decal/cleanable/egg_smudge(src.loc)
reagents.reaction(hit_atom, TOUCH)
del(src) // Not qdel, because it'll hit other mobs then the floor for runtimes.
if(!..()) //was it caught by a mob?
var/turf/T = get_turf(hit_atom)
new/obj/effect/decal/cleanable/egg_smudge(T)
reagents.reaction(hit_atom, TOUCH)
qdel(src)
/obj/item/weapon/reagent_containers/food/snacks/egg/attackby(obj/item/weapon/W, mob/user, params)
if(istype( W, /obj/item/toy/crayon ))
+5 -4
View File
@@ -23,10 +23,11 @@
list_reagents = list("nutriment" = 6, "banana" = 5, "vitamin" = 2)
/obj/item/weapon/reagent_containers/food/snacks/pie/cream/throw_impact(atom/hit_atom)
..()
new/obj/effect/decal/cleanable/pie_smudge(src.loc)
reagents.reaction(hit_atom, TOUCH)
del(src) // Not qdel, because it'll hit other mobs then the floor for runtimes.
if(!..()) //was it caught by a mob?
var/turf/T = get_turf(hit_atom)
new/obj/effect/decal/cleanable/pie_smudge(T)
reagents.reaction(hit_atom, TOUCH)
qdel(src)
/obj/item/weapon/reagent_containers/food/snacks/pie/berryclafoutis
+9 -9
View File
@@ -806,17 +806,16 @@ obj/item/weapon/reagent_containers/food/snacks/grown/shell/eggy/add_juice()
bitesize = 1 + round(reagents.total_volume / 2, 1)
/obj/item/weapon/reagent_containers/food/snacks/grown/tomato/proc/squish(atom/target)
new splat(src.loc)
src.visible_message("The [src.name] has been squashed.","<span class='italics'>You hear a smack.</span>")
var/turf/T = get_turf(target)
new splat(T)
visible_message("The [src.name] has been squashed.","<span class='italics'>You hear a smack.</span>")
for(var/atom/A in get_turf(target))
src.reagents.reaction(A)
reagents.reaction(A)
/obj/item/weapon/reagent_containers/food/snacks/grown/tomato/throw_impact(atom/hit_atom)
..()
squish(hit_atom)
del(src) // Not qdel, because it'll hit other mobs then the floor for runtimes.
return
if(!..()) //was it caught by a mob?
squish(hit_atom)
qdel(src)
/obj/item/weapon/reagent_containers/food/snacks/grown/tomato/killer
seed = /obj/item/seeds/killertomatoseed
@@ -906,7 +905,8 @@ obj/item/weapon/reagent_containers/food/snacks/grown/shell/eggy/add_juice()
..()
var/teleport_radius = potency / 10
if(isliving(squishee))
new /obj/effect/decal/cleanable/molten_item(squishee.loc) //Leave a pile of goo behind for dramatic effect...
var/turf/T = get_turf(squishee)
new /obj/effect/decal/cleanable/molten_item(T) //Leave a pile of goo behind for dramatic effect...
do_teleport(squishee, get_turf(squishee), teleport_radius)
@@ -1,5 +1,5 @@
/mob/living/carbon/alien/hitby(atom/movable/AM)
..(AM, 1)
..(AM, skip = 1)
/*Code for aliens attacking aliens. Because aliens act on a hivemind, I don't see them as very aggressive with each other.
@@ -82,11 +82,11 @@
else //Maybe uses plasma in the future, although that wouldn't make any sense...
leaping = 1
update_icons()
throw_at(A,MAX_ALIEN_LEAP_DIST,1)
throw_at(A,MAX_ALIEN_LEAP_DIST,1, spin=0, diagonals_first = 1)
leaping = 0
update_icons()
/mob/living/carbon/alien/humanoid/hunter/throw_impact(A)
/mob/living/carbon/alien/humanoid/hunter/throw_impact(atom/A)
if(!leaping)
return ..()
@@ -103,60 +103,19 @@
pounce_cooldown = !pounce_cooldown
spawn(pounce_cooldown_time) //3s by default
pounce_cooldown = !pounce_cooldown
else
else if(A.density && !A.CanPass(src))
visible_message("<span class ='danger'>[src] smashes into [A]!</span>", "<span class ='alertalien'>[src] smashes into [A]!</span>")
weakened = 2
if(leaping)
leaping = 0
update_icons()
update_canmove()
/mob/living/carbon/alien/humanoid/float(on)
if(leaping)
return
..()
//Modified throw_at() that will use diagonal dirs where appropriate
//instead of locking it to cardinal dirs
/mob/living/carbon/alien/humanoid/throw_at(atom/target, range, speed)
if(!target || !src || (flags & NODROP)) return 0
src.throwing = 1
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)
var/dist_travelled = 0
var/dist_since_sleep = 0
var/tdist_x = dist_x;
var/tdist_y = dist_y;
if(dist_x <= dist_y)
tdist_x = dist_y;
tdist_y = dist_x;
var/error = tdist_x/2 - tdist_y
while(target && (((((dist_x > dist_y) && ((src.x < target.x) || (src.x > target.x))) || ((dist_x <= dist_y) && ((src.y < target.y) || (src.y > target.y))) || (src.x > target.x)) && dist_travelled < range) || !has_gravity(src)))
if(!src.throwing) break
if(!istype(src.loc, /turf)) break
var/atom/step = get_step(src, get_dir(src,target))
if(!step)
break
src.Move(step, get_dir(src, step))
hit_check()
error += (error < 0) ? tdist_x : -tdist_y;
dist_travelled++
dist_since_sleep++
if(dist_since_sleep >= speed)
dist_since_sleep = 0
sleep(1)
src.throwing = 0
return 1
@@ -8,8 +8,8 @@
put_in_active_hand(I)
visible_message("<span class='warning'>[src] catches [I]!</span>")
throw_mode_off()
return
..()
return 1
return ..()
/mob/living/carbon/attackby(obj/item/I, mob/user, params)
@@ -455,4 +455,18 @@ emp_act
else
..()
return
/mob/living/carbon/human/hitby(atom/movable/AM)
if(throw_speed >= EMBED_THROWSPEED_THRESHOLD)
if(istype(AM, /obj/item))
var/obj/item/I = AM
if(can_embed(I))
if(prob(I.embed_chance) && !(dna && (PIERCEIMMUNE in dna.species.specflags)))
throw_alert("embeddedobject")
var/obj/item/organ/limb/L = pick(organs)
L.embedded_objects |= I
I.add_blood(src)//it embedded itself in you, of course it's bloody!
I.loc = src
L.take_damage(I.w_class*I.embedded_impact_pain_multiplier)
visible_message("<span class='danger'>\the [I.name] embeds itself in [src]'s [L.getDisplayName()]!</span>","<span class='userdanger'>\the [I.name] embeds itself in your [L.getDisplayName()]!</span>")
return
return ..()
@@ -45,7 +45,7 @@
return shoes && shoes.negates_gravity()
/mob/living/carbon/human/Move(NewLoc, direct)
..()
. = ..()
if(dna)
for(var/datum/mutation/human/HM in dna.mutations)
HM.on_move(src, NewLoc)
+6 -1
View File
@@ -52,13 +52,14 @@ Sorry Giacom. Please don't be mad :(
//Generic Bump(). Override MobBump() and ObjBump() instead of this.
/mob/living/Bump(atom/A, yes)
if(..()) //we are thrown onto something
return
if (buckled || !yes || now_pushing)
return
if(ismob(A))
var/mob/M = A
if(MobBump(M))
return
..()
if(isobj(A))
var/obj/O = A
if(ObjBump(O))
@@ -68,6 +69,10 @@ Sorry Giacom. Please don't be mad :(
if(PushAM(AM))
return
/mob/living/Bumped(atom/movable/AM)
..()
last_bumped = world.time
//Called when we bump onto a mob
/mob/living/proc/MobBump(mob/M)
//Even if we don't push/swap places, we "touched" them, so spread fire
+12 -4
View File
@@ -44,7 +44,13 @@
else
return 0
/mob/living/hitby(atom/movable/AM,mob/thrower)//Standardization and logging -Sieve
/mob/living/throw_impact(atom/hit_atom)
. = ..()
if(hit_atom.density)
Weaken(1)
take_organ_damage(10)
/mob/living/hitby(atom/movable/AM)
if(istype(AM, /obj/item))
var/obj/item/I = AM
var/zone = ran_zone("chest", 65)//Hits a random part of the body, geared towards the chest
@@ -71,9 +77,11 @@
"<span class='userdanger'>[src] has been hit by [I].</span>")
var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration)
apply_damage(I.throwforce, dtype, zone, armor, I)
if(thrower)
add_logs(thrower, src, "hit", I)
if(I.thrownby)
add_logs(I.thrownby, src, "hit", I)
else
playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1)
return ..()
/mob/living/mech_melee_attack(obj/mecha/M)
if(M.occupant.a_intent == "harm")
+3 -1
View File
@@ -47,4 +47,6 @@
var/smoke_delay = 0 //used to prevent spam with smoke reagent reaction on mob.
var/list/say_log = list() //a log of what we've said, plain text, no spans or junk, essentially just each individual "message"
var/list/say_log = list() //a log of what we've said, plain text, no spans or junk, essentially just each individual "message"
var/last_bumped = 0
+6 -8
View File
@@ -1,18 +1,16 @@
/mob/CanPass(atom/movable/mover, turf/target, height=0)
if(height==0) return 1
if(istype(mover) && mover.checkpass(PASSMOB))
if(height==0)
return 1
if(istype(mover, /obj/item/projectile))
if(istype(mover, /obj/item/projectile) || mover.throwing)
return (!density || lying)
if(mover.checkpass(PASSMOB))
return 1
if(ismob(mover))
var/mob/moving_mob = mover
if ((other_mobs && moving_mob.other_mobs))
return 1
return (!mover.density || !density || lying)
else
return (!mover.density || !density || lying)
return
return (!mover.density || !density || lying)
/client/Northeast()
@@ -43,7 +43,7 @@
/obj/machinery/field/containment/Crossed(obj/mover)
if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha))
bump(mover)
bump_field(mover)
/obj/machinery/field/containment/proc/set_master(master1,master2)
if(!master1 || !master2)
@@ -77,7 +77,7 @@
if((istype(mover, /obj/machinery) && !istype(mover, /obj/singularity)) || \
istype(mover, /obj/structure) || \
istype(mover, /obj/mecha))
bump(mover)
bump_field(mover)
return 0
return ..()
@@ -106,13 +106,13 @@
"<span class='italics'>You hear an electrical crack.</span>")
user.updatehealth()
bump(user)
bump_field(user)
spawn(5)
hasShocked = 0
return
/obj/machinery/field/proc/bump(atom/movable/AM as mob|obj)
/obj/machinery/field/proc/bump_field(atom/movable/AM as mob|obj)
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(5, 1, AM.loc)
s.start()
@@ -356,6 +356,6 @@ field_generator power level display
if(fields.len)
..()
/obj/machinery/field/generator/bump(atom/movable/AM as mob|obj)
/obj/machinery/field/generator/bump_field(atom/movable/AM as mob|obj)
if(fields.len)
..()
+1 -1
View File
@@ -92,7 +92,7 @@
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume
/obj/item/projectile/Bump(atom/A, yes)
if(!yes) //prevents multi bumps.
if(!yes) //prevents double bumps.
return
if(A == firer || A == src)
loc = A.loc
+5 -6
View File
@@ -79,8 +79,8 @@
reagents.handle_reactions()
..()
/obj/item/weapon/reagent_containers/throw_impact(atom/target,mob/thrower)
..()
/obj/item/weapon/reagent_containers/throw_impact(atom/target)
. = ..()
if(!reagents.total_volume || !spillable)
return
@@ -96,12 +96,11 @@
R += A.id + " ("
R += num2text(A.volume) + "),"
if(thrownby)
add_logs(thrownby, M, "splashed", R)
reagents.reaction(target, TOUCH)
if(thrower)
add_logs(thrower, M, "splashed", R)
else if((!target.density || target.throwpass) && thrower && thrower.mind && thrower.mind.assigned_role == "Bartender")
else if((target.CanPass(src, get_turf(src))) && thrownby && thrownby.mind && thrownby.mind.assigned_role == "Bartender")
visible_message("<span class='notice'>[src] lands onto the [target.name] without spilling a single drop.</span>")
return
+8 -7
View File
@@ -28,13 +28,14 @@
do_teleport(L, get_turf(L), blink_range, asoundin = 'sound/effects/phasein.ogg')
/obj/item/bluespace_crystal/throw_impact(atom/hit_atom)
..()
visible_message("<span class='notice'>[src] fizzles and disappears upon impact!</span>")
PoolOrNew(/obj/effect/effect/sparks, loc)
playsound(src.loc, "sparks", 50, 1)
if(isliving(hit_atom))
blink_mob(hit_atom)
qdel(src)
if(!..()) // not caught in mid-air
visible_message("<span class='notice'>[src] fizzles and disappears upon impact!</span>")
var/turf/T = get_turf(hit_atom)
PoolOrNew(/obj/effect/effect/sparks, T)
playsound(src.loc, "sparks", 50, 1)
if(isliving(hit_atom))
blink_mob(hit_atom)
qdel(src)
// Artifical bluespace crystal, doesn't give you much research.
@@ -0,0 +1,8 @@
author: phil235
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
delete-after: True
changes:
- tweak: "Thrown things no longer bounces off walls, unless they're in no gravity. Heavy items and mobs thrown can push the mobs and non anchored objects they land on. Throwing a mob onto a mob in no gravity will push each one in opposite direction. Thrown mobs landing on wall, or dense object or mob gets stunned and a bit injured. The damage when mob hit a wall is nerfed."