Files
Bubberstation/code/modules/recycling/disposal-structures.dm
phil235 b146131a34 Second commits.
This contains the changes to item_attack.dm
Obj/attackby() now follows a similar structure as mob/living/attackby. It calls attack_obj() (like attack() but for obj) which calls attacked_by (just like attack() does)
The use of the NOBLUDGEON flag changes a bit, it is now used to signify the item cannot be used as a melee weapon at all. No attack animation, no attack message. I've given this bitflag to many items that have an afterattack() so as to not both attack and do the special action (among those items: the rcd)

There's also the code changes to attacking machines: attacking any machine now give a proper message and a sound. And with this, I made more machines breakable (using a health var and the very little used BROKEN stat). Most notably, tables can now be attacked when on harm intent and be destroyed.

The newly destroyable machines have a take_damage() proc used by all sorts of attack (weapon, xeno, animal, hulk, mech melee, gun projectile, thrown items).

There's some more stuff in there, see the PR's description and comments.
2016-04-24 20:37:00 +02:00

803 lines
22 KiB
Plaintext

// virtual disposal object
// travels through pipes in lieu of actual items
// contents will be items flushed by the disposal
// this allows the gas flushed to be tracked
/obj/structure/disposalholder
invisibility = 101
var/datum/gas_mixture/gas = null // gas used to flush, will appear at exit point
var/active = 0 // true if the holder is moving, otherwise inactive
dir = 0
var/count = 1000 //*** can travel 1000 steps before going inactive (in case of loops)
var/destinationTag = 0 // changes if contains a delivery container
var/tomail = 0 //changes if contains wrapped package
var/hasmob = 0 //If it contains a mob
/obj/structure/disposalholder/Destroy()
qdel(gas)
active = 0
return ..()
// initialize a holder from the contents of a disposal unit
/obj/structure/disposalholder/proc/init(obj/machinery/disposal/D)
gas = D.air_contents// transfer gas resv. into holder object
//Check for any living mobs trigger hasmob.
//hasmob effects whether the package goes to cargo or its tagged destination.
for(var/mob/living/M in D)
if(M.client)
M.reset_perspective(src)
hasmob = 1
//Checks 1 contents level deep. This means that players can be sent through disposals...
//...but it should require a second person to open the package. (i.e. person inside a wrapped locker)
for(var/obj/O in D)
if(O.contents)
for(var/mob/living/M in O.contents)
hasmob = 1
// now everything inside the disposal gets put into the holder
// note AM since can contain mobs or objs
for(var/atom/movable/AM in D)
AM.loc = src
if(istype(AM, /obj/structure/bigDelivery) && !hasmob)
var/obj/structure/bigDelivery/T = AM
src.destinationTag = T.sortTag
if(istype(AM, /obj/item/smallDelivery) && !hasmob)
var/obj/item/smallDelivery/T = AM
src.destinationTag = T.sortTag
// start the movement process
// argument is the disposal unit the holder started in
/obj/structure/disposalholder/proc/start(obj/machinery/disposal/D)
if(!D.trunk)
D.expel(src) // no trunk connected, so expel immediately
return
loc = D.trunk
active = 1
dir = DOWN
move()
return
// movement process, persists while holder is moving through pipes
/obj/structure/disposalholder/proc/move()
set waitfor = 0
var/obj/structure/disposalpipe/last
while(active)
var/obj/structure/disposalpipe/curr = loc
last = curr
curr = curr.transfer(src)
if(!curr && active)
last.expel(src, loc, dir)
stoplag()
if(!(count--))
active = 0
return
// find the turf which should contain the next pipe
/obj/structure/disposalholder/proc/nextloc()
return get_step(loc,dir)
// find a matching pipe on a turf
/obj/structure/disposalholder/proc/findpipe(turf/T)
if(!T)
return null
var/fdir = turn(dir, 180) // flip the movement direction
for(var/obj/structure/disposalpipe/P in T)
if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir
return P
// if no matching pipe, return null
return null
// merge two holder objects
// used when a a holder meets a stuck holder
/obj/structure/disposalholder/proc/merge(obj/structure/disposalholder/other)
for(var/atom/movable/AM in other)
AM.loc = src // move everything in other holder to this one
if(ismob(AM))
var/mob/M = AM
M.reset_perspective(src) // if a client mob, update eye to follow this holder
qdel(other)
// called when player tries to move while in a pipe
/obj/structure/disposalholder/relaymove(mob/user)
if (user.stat)
return
if (src.loc)
for (var/mob/M in get_hearers_in_view(src.loc.loc))
M.show_message("<FONT size=[max(0, 5 - get_dist(src, M))]>CLONG, clong!</FONT>", 2)
playsound(src.loc, 'sound/effects/clang.ogg', 50, 0, 0)
// called to vent all gas in holder to a location
/obj/structure/disposalholder/proc/vent_gas(turf/T)
T.assume_air(gas)
T.air_update_turf()
/obj/structure/disposalholder/allow_drop()
return 1
// Disposal pipes
/obj/structure/disposalpipe
icon = 'icons/obj/atmospherics/pipes/disposal.dmi'
name = "disposal pipe"
desc = "An underfloor disposal pipe."
anchored = 1
density = 0
on_blueprints = TRUE
level = 1 // underfloor only
var/dpdir = 0 // bitmask of pipe directions
dir = 0 // dir will contain dominant direction for junction pipes
var/health = 10 // health points 0-10
layer = 2.3 // slightly lower than wires and other pipes
var/base_icon_state // initial icon state on map
var/obj/structure/disposalconstruct/stored
// new pipe, set the icon_state as on map
/obj/structure/disposalpipe/New(loc,var/obj/structure/disposalconstruct/make_from)
..()
if(make_from && !qdeleted(make_from))
base_icon_state = make_from.base_state
dir = make_from.dir
dpdir = make_from.dpdir
make_from.loc = src
stored = make_from
else
base_icon_state = icon_state
stored = new /obj/structure/disposalconstruct(src,direction=dir)
switch(base_icon_state)
if("pipe-s")
stored.ptype = DISP_PIPE_STRAIGHT
if("pipe-c")
stored.ptype = DISP_PIPE_BENT
if("pipe-j1")
stored.ptype = DISP_JUNCTION
if("pipe-j2")
stored.ptype = DISP_JUNCTION_FLIP
if("pipe-y")
stored.ptype = DISP_YJUNCTION
if("pipe-t")
stored.ptype = DISP_END_TRUNK
if("pipe-j1s")
stored.ptype = DISP_SORTJUNCTION
if("pipe-j2s")
stored.ptype = DISP_SORTJUNCTION_FLIP
return
// pipe is deleted
// ensure if holder is present, it is expelled
/obj/structure/disposalpipe/Destroy()
var/obj/structure/disposalholder/H = locate() in src
if(H)
// holder was present
H.active = 0
var/turf/T = src.loc
if(T.density)
// deleting pipe is inside a dense turf (wall)
// this is unlikely, but just dump out everything into the turf in case
for(var/atom/movable/AM in H)
AM.forceMove(src.loc)
AM.pipe_eject(0)
qdel(H)
return ..()
// otherwise, do normal expel from turf
else
expel(H, T, 0)
return ..()
// returns the direction of the next pipe object, given the entrance dir
// by default, returns the bitmask of remaining directions
/obj/structure/disposalpipe/proc/nextdir(fromdir)
return dpdir & (~turn(fromdir, 180))
// transfer the holder through this pipe segment
// overriden for special behaviour
//
/obj/structure/disposalpipe/proc/transfer(obj/structure/disposalholder/H)
var/nextdir = nextdir(H.dir)
return transfer_to_dir(H, nextdir)
/obj/structure/disposalpipe/proc/transfer_to_dir(obj/structure/disposalholder/H, nextdir)
H.dir = nextdir
var/turf/T = H.nextloc()
var/obj/structure/disposalpipe/P = H.findpipe(T)
if(P)
// find other holder in next loc, if inactive merge it with current
var/obj/structure/disposalholder/H2 = locate() in P
if(H2 && !H2.active)
H.merge(H2)
H.loc = P
return P
else // if wasn't a pipe, then they're now in our turf
H.loc = get_turf(src)
return null
// update the icon_state to reflect hidden status
/obj/structure/disposalpipe/proc/update()
var/turf/T = src.loc
hide(T.intact && !istype(T,/turf/open/space)) // space never hides pipes
// hide called by levelupdate if turf intact status changes
// change visibility status and force update of icon
/obj/structure/disposalpipe/hide(var/intact)
invisibility = intact ? 101: 0 // hide if floor is intact
updateicon()
// update actual icon_state depending on visibility
// if invisible, append "f" to icon_state to show faded version
// this will be revealed if a T-scanner is used
// if visible, use regular icon_state
/obj/structure/disposalpipe/proc/updateicon()
if(invisibility)
icon_state = "[base_icon_state]f"
else
icon_state = base_icon_state
return
// expel the held objects into a turf
// called when there is a break in the pipe
//
/obj/structure/disposalpipe/proc/expel(obj/structure/disposalholder/H, turf/T, direction)
var/turf/target
if(istype(T, /turf/open/floor)) //intact floor, pop the tile
var/turf/open/floor/myturf = T
if(myturf.builtin_tile)
myturf.builtin_tile.loc = T
myturf.builtin_tile = null
myturf.make_plating()
if(direction) // direction is specified
if(istype(T, /turf/open/space)) // if ended in space, then range is unlimited
target = get_edge_target_turf(T, direction)
else // otherwise limit to 10 tiles
target = get_ranged_target_turf(T, direction, 10)
playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
if(H)
for(var/atom/movable/AM in H)
AM.forceMove(src.loc)
AM.pipe_eject(direction)
AM.throw_at_fast(target, 10, 1)
else // no specified direction, so throw in random direction
playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
if(H)
for(var/atom/movable/AM in H)
target = get_offset_target_turf(T, rand(5)-rand(5), rand(5)-rand(5))
AM.forceMove(src.loc)
AM.pipe_eject(0)
AM.throw_at_fast(target, 5, 1)
H.vent_gas(T)
qdel(H)
return
// call to break the pipe
// will expel any holder inside at the time
// then delete the pipe
// remains : set to leave broken pipe pieces in place
/obj/structure/disposalpipe/proc/broken(remains = 0)
if(remains)
for(var/D in cardinal)
if(D & dpdir)
var/obj/structure/disposalpipe/broken/P = new(src.loc)
P.dir = D
src.invisibility = 101 // make invisible (since we won't delete the pipe immediately)
var/obj/structure/disposalholder/H = locate() in src
if(H)
// holder was present
H.active = 0
var/turf/T = src.loc
if(T.density)
// broken pipe is inside a dense turf (wall)
// this is unlikely, but just dump out everything into the turf in case
for(var/atom/movable/AM in H)
AM.forceMove(src.loc)
AM.pipe_eject(0)
qdel(H)
return
// otherwise, do normal expel from turf
if(H)
expel(H, T, 0)
spawn(2) // delete pipe after 2 ticks to ensure expel proc finished
qdel(src)
// pipe affected by explosion
/obj/structure/disposalpipe/ex_act(severity, target)
//pass on ex_act to our contents before calling it on ourself
var/obj/structure/disposalholder/H = locate() in src
if(H)
H.contents_explosion(severity, target)
switch(severity)
if(1)
broken(0)
return
if(2)
health -= rand(5,15)
healthcheck()
return
if(3)
health -= rand(0,15)
healthcheck()
return
// test health for brokenness
/obj/structure/disposalpipe/proc/healthcheck()
if(health < -2)
broken(0)
else if(health<1)
broken(1)
return
//attack by item
//weldingtool: unfasten and convert to obj/disposalconstruct
/obj/structure/disposalpipe/attackby(obj/item/I, mob/user, params)
var/turf/T = src.loc
if(T.intact)
return // prevent interaction with T-scanner revealed pipes
add_fingerprint(user)
if(istype(I, /obj/item/weapon/weldingtool))
var/obj/item/weapon/weldingtool/W = I
if(can_be_deconstructed(user))
if(W.remove_fuel(0,user))
playsound(src.loc, 'sound/items/Welder2.ogg', 100, 1)
user << "<span class='notice'>You start slicing the disposal pipe...</span>"
// check if anything changed over 2 seconds
if(do_after(user,30, target = src))
if(!src || !W.isOn()) return
Deconstruct()
user << "<span class='notice'>You slice the disposal pipe.</span>"
else
return ..()
//checks if something is blocking the deconstruction (e.g. trunk with a bin still linked to it)
/obj/structure/disposalpipe/proc/can_be_deconstructed()
. = 1
// called when pipe is cut with welder
/obj/structure/disposalpipe/Deconstruct()
if(stored)
var/turf/T = loc
stored.loc = T
transfer_fingerprints_to(stored)
stored.dir = dir
stored.density = 0
stored.anchored = 1
stored.update_icon()
..()
/obj/structure/disposalpipe/singularity_pull(S, current_size)
if(current_size >= STAGE_FIVE)
Deconstruct()
// *** TEST verb
//client/verb/dispstop()
// for(var/obj/structure/disposalholder/H in world)
// H.active = 0
// a straight or bent segment
/obj/structure/disposalpipe/segment
icon_state = "pipe-s"
/obj/structure/disposalpipe/segment/New()
..()
if(stored.ptype == DISP_PIPE_STRAIGHT)
dpdir = dir | turn(dir, 180)
else
dpdir = dir | turn(dir, -90)
update()
return
//a three-way junction with dir being the dominant direction
/obj/structure/disposalpipe/junction
icon_state = "pipe-j1"
/obj/structure/disposalpipe/junction/New()
..()
switch(stored.ptype)
if(DISP_JUNCTION)
dpdir = dir | turn(dir, -90) | turn(dir,180)
if(DISP_JUNCTION_FLIP)
dpdir = dir | turn(dir, 90) | turn(dir,180)
if(DISP_YJUNCTION)
dpdir = dir | turn(dir,90) | turn(dir, -90)
update()
return
// next direction to move
// if coming in from secondary dirs, then next is primary dir
// if coming in from primary dir, then next is equal chance of other dirs
/obj/structure/disposalpipe/junction/nextdir(fromdir)
var/flipdir = turn(fromdir, 180)
if(flipdir != dir) // came from secondary dir
return dir // so exit through primary
else // came from primary
// so need to choose either secondary exit
var/mask = ..(fromdir)
// find a bit which is set
var/setbit = 0
if(mask & NORTH)
setbit = NORTH
else if(mask & SOUTH)
setbit = SOUTH
else if(mask & EAST)
setbit = EAST
else
setbit = WEST
if(prob(50)) // 50% chance to choose the found bit or the other one
return setbit
else
return mask & (~setbit)
//a three-way junction that sorts objects
/obj/structure/disposalpipe/sortjunction
desc = "An underfloor disposal pipe with a package sorting mechanism."
icon_state = "pipe-j1s"
var/sortType = 0
// To be set in map editor.
// Supports both singular numbers and strings of numbers similar to access level strings.
// Look at the list called TAGGERLOCATIONS in /_globalvars/lists/flavor_misc.dm
var/list/sortTypes = list()
var/posdir = 0
var/negdir = 0
var/sortdir = 0
/obj/structure/disposalpipe/sortjunction/examine(mob/user)
..()
if(sortTypes.len>0)
user << "It is tagged with the following tags:"
for(var/t in sortTypes)
user << TAGGERLOCATIONS[t]
else
user << "It has no sorting tags set."
/obj/structure/disposalpipe/sortjunction/proc/updatedir()
posdir = dir
negdir = turn(posdir, 180)
if(stored.ptype == DISP_SORTJUNCTION)
sortdir = turn(posdir, -90)
else
icon_state = "pipe-j2s"
sortdir = turn(posdir, 90)
dpdir = sortdir | posdir | negdir
/obj/structure/disposalpipe/sortjunction/New()
..()
// Generate a list of soring tags.
if(sortType)
if(isnum(sortType))
sortTypes |= sortType
else if(istext(sortType))
var/list/sorts = splittext(sortType,";")
for(var/x in sorts)
var/n = text2num(x)
if(n)
sortTypes |= n
updatedir()
update()
return
/obj/structure/disposalpipe/sortjunction/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/device/destTagger))
var/obj/item/device/destTagger/O = I
if(O.currTag > 0)// Tag set
if(O.currTag in sortTypes)
sortTypes -= O.currTag
user << "<span class='notice'>Removed \"[TAGGERLOCATIONS[O.currTag]]\" filter.</span>"
else
sortTypes |= O.currTag
user << "<span class='notice'>Added \"[TAGGERLOCATIONS[O.currTag]]\" filter.</span>"
playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1)
else
return ..()
// next direction to move
// if coming in from negdir, then next is primary dir or sortdir
// if coming in from posdir, then flip around and go back to posdir
// if coming in from sortdir, go to posdir
/obj/structure/disposalpipe/sortjunction/nextdir(fromdir, sortTag)
//var/flipdir = turn(fromdir, 180)
if(fromdir != sortdir) // probably came from the negdir
if(sortTag in sortTypes) //if destination matches filtered type...
return sortdir // exit through sortdirection
else
return posdir
else // came from sortdir
// so go with the flow to positive direction
return posdir
/obj/structure/disposalpipe/sortjunction/transfer(obj/structure/disposalholder/H)
var/nextdir = nextdir(H.dir, H.destinationTag)
return transfer_to_dir(H, nextdir)
//a three-way junction that sorts objects destined for the mail office mail table (tomail = 1)
/obj/structure/disposalpipe/wrapsortjunction
desc = "An underfloor disposal pipe which sorts wrapped and unwrapped objects."
icon_state = "pipe-j1s"
var/posdir = 0
var/negdir = 0
var/sortdir = 0
/obj/structure/disposalpipe/wrapsortjunction/New()
..()
posdir = dir
if(stored.ptype == DISP_SORTJUNCTION)
sortdir = turn(posdir, -90)
negdir = turn(posdir, 180)
else
icon_state = "pipe-j2s"
sortdir = turn(posdir, 90)
negdir = turn(posdir, 180)
dpdir = sortdir | posdir | negdir
update()
return
// next direction to move
// if coming in from negdir, then next is primary dir or sortdir
// if coming in from posdir, then flip around and go back to posdir
// if coming in from sortdir, go to posdir
/obj/structure/disposalpipe/wrapsortjunction/nextdir(fromdir, istomail)
//var/flipdir = turn(fromdir, 180)
if(fromdir != sortdir) // probably came from the negdir
if(istomail) //if destination matches filtered type...
return sortdir // exit through sortdirection
else
return posdir
else // came from sortdir
// so go with the flow to positive direction
return posdir
/obj/structure/disposalpipe/wrapsortjunction/transfer(obj/structure/disposalholder/H)
var/nextdir = nextdir(H.dir, H.tomail)
return transfer_to_dir(H, nextdir)
//a trunk joining to a disposal bin or outlet on the same turf
/obj/structure/disposalpipe/trunk
icon_state = "pipe-t"
var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet
/obj/structure/disposalpipe/trunk/New()
..()
dpdir = dir
spawn(1)
getlinked()
update()
return
/obj/structure/disposalpipe/trunk/Destroy()
if(linked)
if(istype(linked, /obj/structure/disposaloutlet))
var/obj/structure/disposaloutlet/D = linked
D.trunk = null
else if(istype(linked, /obj/machinery/disposal))
var/obj/machinery/disposal/D = linked
D.trunk = null
return ..()
/obj/structure/disposalpipe/trunk/proc/getlinked()
linked = null
var/obj/machinery/disposal/D = locate() in src.loc
if(D)
linked = D
if (!D.trunk)
D.trunk = src
var/obj/structure/disposaloutlet/O = locate() in src.loc
if(O)
linked = O
update()
return
/obj/structure/disposalpipe/trunk/can_be_deconstructed(mob/user)
if(linked)
user << "<span class='warning'>You need to deconstruct disposal machinery above this pipe!</span>"
else
. = 1
// would transfer to next pipe segment, but we are in a trunk
// if not entering from disposal bin,
// transfer to linked object (outlet or bin)
/obj/structure/disposalpipe/trunk/transfer(obj/structure/disposalholder/H)
if(H.dir == DOWN) // we just entered from a disposer
return ..() // so do base transfer proc
// otherwise, go to the linked object
if(linked)
var/obj/structure/disposaloutlet/O = linked
if(istype(O) && (H))
O.expel(H) // expel at outlet
else
var/obj/machinery/disposal/D = linked
if(H)
D.expel(H) // expel at disposal
else
if(H)
src.expel(H, get_turf(src), 0) // expel at turf
return null
/obj/structure/disposalpipe/trunk/nextdir(fromdir)
if(fromdir == DOWN)
return dir
else
return 0
// a broken pipe
/obj/structure/disposalpipe/broken
icon_state = "pipe-b"
dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes
// i.e. will be treated as an empty turf
desc = "A broken piece of disposal pipe."
/obj/structure/disposalpipe/broken/New()
..()
update()
return
// the disposal outlet machine
/obj/structure/disposalpipe/broken/Deconstruct()
qdel(src)
/obj/structure/disposaloutlet
name = "disposal outlet"
desc = "An outlet for the pneumatic disposal system."
icon = 'icons/obj/atmospherics/pipes/disposal.dmi'
icon_state = "outlet"
density = 1
anchored = 1
var/active = 0
var/turf/target // this will be where the output objects are 'thrown' to.
var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk
var/obj/structure/disposalconstruct/stored
var/mode = 0
var/start_eject = 0
var/eject_range = 2
/obj/structure/disposaloutlet/New(loc, var/obj/structure/disposalconstruct/make_from)
..()
if(make_from)
dir = make_from.dir
make_from.loc = src
stored = make_from
else
stored = new (src, DISP_END_OUTLET,dir)
spawn(1)
target = get_ranged_target_turf(src, dir, 10)
trunk = locate() in src.loc
if(trunk)
trunk.linked = src // link the pipe trunk to self
/obj/structure/disposaloutlet/Destroy()
if(trunk)
trunk.linked = null
return ..()
// expel the contents of the holder object, then delete it
// called when the holder exits the outlet
/obj/structure/disposaloutlet/proc/expel(obj/structure/disposalholder/H)
var/turf/T = get_turf(src)
flick("outlet-open", src)
if((start_eject + 30) < world.time)
start_eject = world.time
playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0)
sleep(20)
playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0)
else
sleep(20)
if(H)
for(var/atom/movable/AM in H)
AM.forceMove(T)
AM.pipe_eject(dir)
AM.throw_at_fast(target, eject_range, 1)
H.vent_gas(T)
qdel(H)
return
/obj/structure/disposaloutlet/attackby(obj/item/I, mob/user, params)
add_fingerprint(user)
if(istype(I, /obj/item/weapon/screwdriver))
if(mode==0)
mode=1
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
user << "<span class='notice'>You remove the screws around the power connection.</span>"
else if(mode==1)
mode=0
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
user << "<span class='notice'>You attach the screws around the power connection.</span>"
else if(istype(I,/obj/item/weapon/weldingtool) && mode==1)
var/obj/item/weapon/weldingtool/W = I
if(W.remove_fuel(0,user))
playsound(src.loc, 'sound/items/Welder2.ogg', 100, 1)
user << "<span class='notice'>You start slicing the floorweld off \the [src]...</span>"
if(do_after(user,20/I.toolspeed, target = src))
if(!src || !W.isOn()) return
user << "<span class='notice'>You slice the floorweld off \the [src].</span>"
stored.loc = loc
src.transfer_fingerprints_to(stored)
stored.update_icon()
stored.anchored = 0
stored.density = 1
qdel(src)
else
return ..()
// called when movable is expelled from a disposal pipe or outlet
// by default does nothing, override for special behaviour
/atom/movable/proc/pipe_eject(direction)
return
/obj/effect/decal/cleanable/blood/gibs/pipe_eject(direction)
var/list/dirs
if(direction)
dirs = list( direction, turn(direction, -45), turn(direction, 45))
else
dirs = alldirs.Copy()
src.streak(dirs)
/obj/effect/decal/cleanable/robot_debris/gib/pipe_eject(direction)
var/list/dirs
if(direction)
dirs = list( direction, turn(direction, -45), turn(direction, 45))
else
dirs = alldirs.Copy()
src.streak(dirs)