This commit is contained in:
Asanadas
2013-04-04 01:15:03 -04:00
52 changed files with 1255 additions and 900 deletions
+125 -133
View File
@@ -1,5 +1,5 @@
/obj/effect/bee
/mob/living/simple_animal/bee
name = "bees"
icon = 'icons/obj/apiary_bees_etc.dmi'
icon_state = "bees1"
@@ -11,162 +11,154 @@
var/mob/target_mob
var/obj/machinery/apiary/parent
pass_flags = PASSGRILLE|PASSTABLE
turns_per_move = 6
var/obj/machinery/hydroponics/my_hydrotray
/obj/effect/bee/New(loc, var/obj/machinery/apiary/new_parent)
/mob/living/simple_animal/bee/New(loc, var/obj/machinery/apiary/new_parent)
..()
processing_objects.Add(src)
parent = new_parent
verbs -= /atom/movable/verb/pull
/obj/effect/bee/Del()
processing_objects.Remove(src)
/mob/living/simple_animal/bee/Del()
if(parent)
parent.owned_bee_swarms.Remove(src)
..()
/obj/effect/bee/process()
/mob/living/simple_animal/bee/Life()
..()
//if we're strong enough, sting some people
var/overrun = strength - 5 + feral / 2
if(prob(max( overrun * 10 + feral * 10, 0)))
var/mob/living/carbon/human/M = locate() in src.loc
if(M)
var/sting_prob = 100
var/obj/item/clothing/worn_suit = M.wear_suit
var/obj/item/clothing/worn_helmet = M.head
if(worn_suit)
sting_prob -= worn_suit.armor["bio"]
if(worn_helmet)
sting_prob -= worn_helmet.armor["bio"]
if(stat == CONSCIOUS)
//if we're strong enough, sting some people
var/overrun = strength - 5 + feral / 2
if(prob(max( overrun * 10 + feral * 10, 0)))
var/mob/living/carbon/human/M = pick(range(1,src))
if(M)
var/sting_prob = 100
var/obj/item/clothing/worn_suit = M.wear_suit
var/obj/item/clothing/worn_helmet = M.head
if(worn_suit)
sting_prob -= worn_suit.armor["bio"]
if(worn_helmet)
sting_prob -= worn_helmet.armor["bio"]
if( prob(sting_prob) && (M.stat == CONSCIOUS || (M.stat == UNCONSCIOUS && prob(25))) )
M.apply_damage(overrun / 2 + mut / 2, BRUTE)
M.apply_damage(overrun / 2 + toxic / 2, TOX)
M << "\red You have been stung!"
M.flash_pain()
if( prob(sting_prob) && (M.stat == CONSCIOUS || (M.stat == UNCONSCIOUS && prob(25))) )
M.apply_damage(overrun / 2 + mut / 2, BRUTE)
M.apply_damage(overrun / 2 + toxic / 2, TOX)
M << "\red You have been stung!"
M.flash_pain()
//if we're chasing someone, get a little bit angry
if(target_mob && prob(10))
feral++
//if we're chasing someone, get a little bit angry
if(target_mob && prob(10))
feral++
//calm down a little bit
var/move_prob = 40
if(feral > 0)
if(prob(feral * 10))
feral -= 1
else
//if feral is less than 0, we're becalmed by smoke or steam
if(feral < 0)
feral += 1
if(target_mob)
target_mob = null
target_turf = null
if(strength > 5)
//calm down and spread out a little
var/obj/effect/bee/B = new(get_turf(pick(orange(src,1))))
B.strength = rand(1,5)
src.strength -= B.strength
if(src.strength <= 5)
src.icon_state = "bees[src.strength]"
B.icon_state = "bees[B.strength]"
if(src.parent)
B.parent = src.parent
src.parent.owned_bee_swarms.Add(B)
//make some noise
if(prob(0.5))
src.visible_message("\blue [pick("Buzzzz.","Hmmmmm.","Bzzz.")]")
//smoke, water and steam calms us down
var/calming = 0
var/list/calmers = list(/obj/effect/effect/chem_smoke, /obj/effect/effect/water, /obj/effect/effect/foam, /obj/effect/effect/steam, /obj/effect/mist)
for(var/this_type in calmers)
var/obj/effect/check_effect = locate() in src.loc
if(check_effect.type == this_type)
calming = 1
break
if(calming)
//calm down a little bit
if(feral > 0)
src.visible_message("\blue The bees calm down!")
feral = -10
target_mob = null
target_turf = null
for(var/obj/effect/bee/B in src.loc)
if(B == src)
continue
if(feral > 0)
src.strength += B.strength
del(B)
src.icon_state = "bees[src.strength]"
if(strength > 5)
icon_state = "bees_swarm"
else if(prob(10))
//make the other swarm of bees stronger, then move away
var/total_bees = B.strength + src.strength
if(total_bees < 10)
B.strength = min(5, total_bees)
src.strength = total_bees - B.strength
B.icon_state = "bees[B.strength]"
if(src.strength <= 0)
del(src)
return
src.icon_state = "bees[B.strength]"
var/turf/simulated/floor/T = get_turf(get_step(src, pick(1,2,4,8)))
density = 1
if(T.Enter(src, get_turf(src)))
src.loc = T
density = 0
break
if(target_mob)
if(target_mob in view(src,7))
target_turf = get_turf(target_mob)
if(prob(feral * 10))
feral -= 1
else
for(var/mob/living/carbon/M in view(src,7))
target_mob = M
//if feral is less than 0, we're becalmed by smoke or steam
if(feral < 0)
feral += 1
if(target_mob)
target_mob = null
target_turf = null
if(strength > 5)
//calm down and spread out a little
var/mob/living/simple_animal/bee/B = new(get_turf(pick(orange(src,1))))
B.strength = rand(1,5)
src.strength -= B.strength
if(src.strength <= 5)
src.icon_state = "bees[src.strength]"
B.icon_state = "bees[B.strength]"
if(src.parent)
B.parent = src.parent
src.parent.owned_bee_swarms.Add(B)
//make some noise
if(prob(0.5))
src.visible_message("\blue [pick("Buzzzz.","Hmmmmm.","Bzzz.")]")
//smoke, water and steam calms us down
var/calming = 0
var/list/calmers = list(/obj/effect/effect/chem_smoke, \
/obj/effect/effect/water, \
/obj/effect/effect/foam, \
/obj/effect/effect/steam, \
/obj/effect/mist)
for(var/this_type in calmers)
var/mob/living/simple_animal/check_effect = locate() in src.loc
if(check_effect.type == this_type)
calming = 1
break
if(target_turf)
var/turf/next_turf = get_step(src.loc, get_dir(src,target_turf))
if(calming)
if(feral > 0)
src.visible_message("\blue The bees calm down!")
feral = -10
target_mob = null
target_turf = null
wander = 1
for(var/mob/living/simple_animal/bee/B in src.loc)
if(B == src)
continue
if(feral > 0)
src.strength += B.strength
del(B)
src.icon_state = "bees[src.strength]"
if(strength > 5)
icon_state = "bees_swarm"
else if(prob(10))
//make the other swarm of bees stronger, then move away
var/total_bees = B.strength + src.strength
if(total_bees < 10)
B.strength = min(5, total_bees)
src.strength = total_bees - B.strength
B.icon_state = "bees[B.strength]"
if(src.strength <= 0)
del(src)
return
src.icon_state = "bees[B.strength]"
var/turf/simulated/floor/T = get_turf(get_step(src, pick(1,2,4,8)))
density = 1
if(T.Enter(src, get_turf(src)))
src.loc = T
density = 0
break
if(target_mob)
if(target_mob in view(src,7))
target_turf = get_turf(target_mob)
wander = 0
else
for(var/mob/living/carbon/M in view(src,7))
target_mob = M
break
if(target_turf)
Move(get_step(src, get_dir(src,target_turf)))
//hacky, but w/e
var/old_density = -1
if(target_mob && get_dist(src, target_mob) <= 1)
old_density = target_mob.density
target_mob.density = 0
density = 1
if(next_turf.Enter(src, get_turf(src)))
src.loc = next_turf
density = 0
if(src.loc == target_turf)
target_turf = null
if(target_mob && old_density != -1)
target_mob.density = old_density
wander = 1
else
//find some flowers, harvest
//angry bee swarms don't hang around
if(feral > 0)
move_prob = 60
turns_per_move = rand(1,3)
else if(feral < 0)
move_prob = 0
else
var/obj/machinery/hydroponics/H = locate() in src.loc
if(H)
if(H.planted && !H.dead && H.myseed)
move_prob = 1
//chance to wander around
if(prob(move_prob))
var/turf/simulated/floor/T = get_turf(get_step(src, pick(1,2,4,8)))
density = 1
if(T.Enter(src, get_turf(src)))
src.loc = T
density = 0
turns_since_move = 0
else if(!my_hydrotray || my_hydrotray.loc != src.loc || !my_hydrotray.planted || my_hydrotray.dead || !my_hydrotray.myseed)
var/obj/machinery/hydroponics/my_hydrotray = locate() in src.loc
if(my_hydrotray)
if(my_hydrotray.planted && !my_hydrotray.dead && my_hydrotray.myseed)
turns_per_move = rand(20,50)
else
my_hydrotray = null
pixel_x = rand(-12,12)
pixel_y = rand(-12,12)
+9 -8
View File
@@ -22,6 +22,7 @@
var/bees_in_hive = 0
var/list/owned_bee_swarms = list()
var/hydrotray_type = /obj/machinery/hydroponics
//overwrite this after it's created if the apiary needs a custom machinery sprite
/obj/machinery/apiary/New()
@@ -69,7 +70,7 @@
else
user << "\blue You begin to dislodge the dead apiary from the tray."
if(do_after(user, 50))
new /obj/machinery/hydroponics(src.loc)
new hydrotray_type(src.loc)
new /obj/item/apiary(src.loc)
user << "\red You dislodge the apiary from the tray."
del(src)
@@ -112,11 +113,11 @@
if(swarming > 0)
swarming -= 1
if(swarming <= 0)
for(var/obj/effect/bee/B in src.loc)
for(var/mob/living/simple_animal/bee/B in src.loc)
bees_in_hive += B.strength
del(B)
else if(bees_in_hive < 10)
for(var/obj/effect/bee/B in src.loc)
for(var/mob/living/simple_animal/bee/B in src.loc)
bees_in_hive += B.strength
del(B)
@@ -144,7 +145,7 @@
health += max(nutrilevel - 1, round(-health / 2))
bees_in_hive += max(nutrilevel - 1, round(-bees_in_hive / 2))
if(owned_bee_swarms.len)
var/obj/effect/bee/B = pick(owned_bee_swarms)
var/mob/living/simple_animal/bee/B = pick(owned_bee_swarms)
B.target_turf = get_turf(src)
//clear out some toxins
@@ -161,7 +162,7 @@
//make some new bees
if(bees_in_hive >= 10 && prob(bees_in_hive * 10))
var/obj/effect/bee/B = new(get_turf(src), src)
var/mob/living/simple_animal/bee/B = new(get_turf(src), src)
owned_bee_swarms.Add(B)
B.mut = mut
B.toxic = toxic
@@ -193,7 +194,7 @@
/obj/machinery/apiary/proc/die()
if(owned_bee_swarms.len)
var/obj/effect/bee/B = pick(owned_bee_swarms)
var/mob/living/simple_animal/bee/B = pick(owned_bee_swarms)
B.target_turf = get_turf(src)
B.strength -= 1
if(B.strength <= 0)
@@ -204,7 +205,7 @@
health = 0
/obj/machinery/apiary/proc/angry_swarm(var/mob/M)
for(var/obj/effect/bee/B in owned_bee_swarms)
for(var/mob/living/simple_animal/bee/B in owned_bee_swarms)
B.feral = 50
B.target_mob = M
@@ -215,7 +216,7 @@
if(bees_in_hive >= 5)
spawn_strength = 6
var/obj/effect/bee/B = new(get_turf(src), src)
var/mob/living/simple_animal/bee/B = new(get_turf(src), src)
B.target_mob = M
B.strength = spawn_strength
B.feral = 5
+3 -3
View File
@@ -17,7 +17,7 @@
/obj/item/weapon/bee_net/attack_self(mob/user as mob)
var/turf/T = get_step(get_turf(user), user.dir)
for(var/obj/effect/bee/B in T)
for(var/mob/living/simple_animal/bee/B in T)
if(B.feral < 0)
caught_bees += B.strength
del(B)
@@ -38,7 +38,7 @@
while(caught_bees > 0)
//release a few super massive swarms
while(caught_bees > 5)
var/obj/effect/bee/B = new(src.loc)
var/mob/living/simple_animal/bee/B = new(src.loc)
B.feral = 5
B.target_mob = M
B.strength = 6
@@ -46,7 +46,7 @@
caught_bees -= 6
//what's left over
var/obj/effect/bee/B = new(src.loc)
var/mob/living/simple_animal/bee/B = new(src.loc)
B.strength = caught_bees
B.icon_state = "bees[B.strength]"
B.feral = 5
+1 -1
View File
@@ -117,7 +117,7 @@
laws.add_inherent_law(M.newFreeFormLaw)
usr << "Added a freeform law."
if(istype(P, /obj/item/device/mmi) || istype(P, /obj/item/device/posibrain))
if(istype(P, /obj/item/device/mmi) || istype(P, /obj/item/device/mmi/posibrain))
if(!P:brainmob)
user << "\red Sticking an empty [P] into the frame would sort of defeat the purpose."
return
+1
View File
@@ -779,6 +779,7 @@ obj/machinery/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob)
var/obj/machinery/apiary/A = new(src.loc)
A.icon = src.icon
A.icon_state = src.icon_state
A.hydrotray_type = src.type
del(src)
return
+2 -2
View File
@@ -651,7 +651,7 @@
/obj/mecha/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(istype(W, /obj/item/device/mmi) || istype(W, /obj/item/device/posibrain))
if(istype(W, /obj/item/device/mmi) || istype(W, /obj/item/device/mmi/posibrain))
if(mmi_move_inside(W,user))
user << "[src]-MMI interface initialized successfuly"
else
@@ -1136,7 +1136,7 @@
src.occupant.client.perspective = MOB_PERSPECTIVE
*/
src.occupant << browse(null, "window=exosuit")
if(istype(mob_container, /obj/item/device/mmi) || istype(mob_container, /obj/item/device/posibrain))
if(istype(mob_container, /obj/item/device/mmi) || istype(mob_container, /obj/item/device/mmi/posibrain))
var/obj/item/device/mmi/mmi = mob_container
if(mmi.brainmob)
occupant.loc = mmi
+8 -1
View File
@@ -114,7 +114,8 @@
/obj/effect/spider/spiderling/proc/die()
visible_message("<span class='alert'>[src] dies!</span>")
icon_state = "greenshatter"
new /obj/effect/decal/cleanable/spiderling_remains(src.loc)
del(src)
/obj/effect/spider/spiderling/healthcheck()
if(health <= 0)
@@ -189,6 +190,12 @@
new spawn_type(src.loc)
del(src)
/obj/effect/decal/cleanable/spiderling_remains
name = "spiderling remains"
desc = "Green squishy mess."
icon = 'icons/effects/effects.dmi'
icon_state = "greenshatter"
/obj/effect/spider/cocoon
name = "cocoon"
desc = "Something wrapped in silky spider web"
@@ -210,10 +210,12 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
if (!connection)
return
Broadcast_Message(connection, new /mob/living/silicon/ai(src,null,null,1),
var/mob/living/silicon/ai/A = new /mob/living/silicon/ai(src, null, null, 1)
Broadcast_Message(connection, A,
0, "*garbled automated announcement*", src,
message, from, "Automated Announcement", from, "synthesized voice",
4, 0, 1)
4, 0, list(1), 1459)
del(A)
return
/obj/item/device/radio/talk_into(mob/living/M as mob, message, channel)
+4 -2
View File
@@ -126,9 +126,11 @@ MASS SPECTROMETER
if(M.status_flags & FAKEDEATH)
OX = fake_oxy > 50 ? "\red Severe oxygen deprivation detected\blue" : "Subject bloodstream oxygen level normal"
user.show_message("[OX] | [TX] | [BU] | [BR]")
if (istype(M, /mob/living/carbon/human))
if(M:virus2 || M:reagents.total_volume > 0)
if (istype(M, /mob/living/carbon))
if(M:reagents.total_volume > 0)
user.show_message(text("\red Warning: Unknown substance detected in subject's blood."))
if(M:virus2)
user.show_message(text("\red Warning: Unknown pathogen detected in subject's blood."))
if (M.getCloneLoss())
user.show_message("\red Subject appears to have been imperfectly cloned.")
for(var/datum/disease/D in M.viruses)
+1 -1
View File
@@ -161,7 +161,7 @@
else
user << "\blue You need to attach a flash to it first!"
if(istype(W, /obj/item/device/mmi) || istype(W, /obj/item/device/posibrain))
if(istype(W, /obj/item/device/mmi) || istype(W, /obj/item/device/mmi/posibrain))
var/obj/item/device/mmi/M = W
if(check_completion())
if(!istype(loc,/turf))
@@ -46,7 +46,7 @@
for(var/obj/effect/dummy/chameleon/AD in src)
AD.loc = src.loc
for(var/obj/item/I in src)
for(var/obj/I in src)
I.loc = src.loc
for(var/mob/M in src)
@@ -175,15 +175,29 @@
redlight = "largemetalr"
greenlight = "largemetalg"
/obj/structure/closet/crate/secure/large_reinforced
name = "large crate"
/obj/structure/closet/crate/secure/large/close()
//we can hold up to one large item
var/found = 0
for(var/obj/structure/S in src.loc)
if(S == src)
continue
if(!S.anchored)
found = 1
S.loc = src
break
if(!found)
for(var/obj/machinery/M in src.loc)
if(!M.anchored)
M.loc = src
break
..()
//fluff variant
/obj/structure/closet/crate/secure/large/reinforced
desc = "A hefty, reinforced metal crate with an electronic locking system."
icon = 'icons/obj/storage.dmi'
icon_state = "largermetal"
icon_opened = "largermetalopen"
icon_closed = "largermetal"
redlight = "largemetalr"
greenlight = "largemetalg"
/obj/structure/closet/crate/secure
desc = "A secure crate."
@@ -206,6 +220,23 @@
icon_opened = "largemetalopen"
icon_closed = "largemetal"
/obj/structure/closet/crate/large/close()
//we can hold up to one large item
var/found = 0
for(var/obj/structure/S in src.loc)
if(S == src)
continue
if(!S.anchored)
found = 1
S.loc = src
break
if(!found)
for(var/obj/machinery/M in src.loc)
if(!M.anchored)
M.loc = src
break
..()
/obj/structure/closet/crate/hydroponics
name = "Hydroponics crate"
desc = "All you need to destroy those pesky weeds and pests."
@@ -0,0 +1,621 @@
// Basic transit tubes. Straight pieces, curved sections,
// and basic splits/joins (no routing logic).
// Mappers: you can use "Generate Instances from Icon-states"
// to get the different pieces.
/obj/structure/transit_tube
icon = 'icons/obj/pipes/transit_tube.dmi'
icon_state = "E-W"
density = 1
layer = 3.1
anchored = 1.0
var/list/tube_dirs = null
var/exit_delay = 2
var/enter_delay = 1
// alldirs in global.dm is the same list of directions, but since
// the specific order matters to get a usable icon_state, it is
// copied here so that, in the unlikely case that alldirs is changed,
// this continues to work.
var/global/list/tube_dir_list = list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)
// A place where tube pods stop, and people can get in or out.
// Mappers: use "Generate Instances from Directions" for this
// one.
/obj/structure/transit_tube/station
icon = 'icons/obj/pipes/transit_tube_station.dmi'
icon_state = "closed"
exit_delay = 2
enter_delay = 3
var/pod_moving = 0
var/automatic_launch_time = 100
var/const/OPEN_DURATION = 6
var/const/CLOSE_DURATION = 6
/obj/structure/transit_tube_pod
icon = 'icons/obj/pipes/transit_tube_pod.dmi'
icon_state = "pod"
animate_movement = FORWARD_STEPS
anchored = 1.0
density = 1
var/moving = 0
var/datum/gas_mixture/air_contents = new()
/obj/structure/transit_tube_pod/Del()
for(var/atom/movable/AM in contents)
AM.loc = loc
..()
// When destroyed by explosions, properly handle contents.
obj/structure/ex_act(severity)
switch(severity)
if(1.0)
for(var/atom/movable/AM in contents)
AM.loc = loc
AM.ex_act(severity++)
del(src)
return
if(2.0)
if(prob(50))
for(var/atom/movable/AM in contents)
AM.loc = loc
AM.ex_act(severity++)
del(src)
return
if(3.0)
return
/obj/structure/transit_tube_pod/New(loc)
..(loc)
air_contents.oxygen = MOLES_O2STANDARD * 2
air_contents.nitrogen = MOLES_N2STANDARD
air_contents.temperature = T20C
// Give auto tubes time to align before trying to start moving
spawn(5)
follow_tube()
/obj/structure/transit_tube/New(loc)
..(loc)
if(tube_dirs == null)
init_dirs()
/obj/structure/transit_tube/station/New(loc)
..(loc)
/obj/structure/transit_tube/station/Bumped(mob/AM as mob|obj)
if(!pod_moving && icon_state == "open" && istype(AM, /mob))
for(var/obj/structure/transit_tube_pod/pod in loc)
if(!pod.moving && pod.dir in directions())
AM.loc = pod
return
/obj/structure/transit_tube/station/attack_hand(mob/user as mob)
if(!pod_moving)
for(var/obj/structure/transit_tube_pod/pod in loc)
if(!pod.moving && pod.dir in directions())
if(icon_state == "closed")
open_animation()
else if(icon_state == "open")
close_animation()
/obj/structure/transit_tube/station/proc/open_animation()
if(icon_state == "closed")
icon_state = "opening"
spawn(OPEN_DURATION)
if(icon_state == "opening")
icon_state = "open"
/obj/structure/transit_tube/station/proc/close_animation()
if(icon_state == "open")
icon_state = "closing"
spawn(CLOSE_DURATION)
if(icon_state == "closing")
icon_state = "closed"
/obj/structure/transit_tube/station/proc/launch_pod()
for(var/obj/structure/transit_tube_pod/pod in loc)
if(!pod.moving && pod.dir in directions())
spawn(5)
pod_moving = 1
close_animation()
sleep(CLOSE_DURATION + 2)
if(icon_state == "closed" && pod)
pod.follow_tube()
pod_moving = 0
return
// Called to check if a pod should stop upon entering this tube.
/obj/structure/transit_tube/proc/should_stop_pod(pod, from_dir)
return 0
/obj/structure/transit_tube/station/should_stop_pod(pod, from_dir)
return 1
// Called when a pod stops in this tube section.
/obj/structure/transit_tube/proc/pod_stopped(pod, from_dir)
return
/obj/structure/transit_tube/station/pod_stopped(obj/structure/transit_tube_pod/pod, from_dir)
pod_moving = 1
spawn(5)
open_animation()
sleep(OPEN_DURATION + 2)
pod_moving = 0
pod.mix_air()
if(automatic_launch_time)
var/const/wait_step = 5
var/i = 0
while(i < automatic_launch_time)
sleep(wait_step)
i += wait_step
if(pod_moving || icon_state != "open")
return
launch_pod()
// Returns a /list of directions this tube section can connect to.
// Tubes that have some sort of logic or changing direction might
// override it with additional logic.
/obj/structure/transit_tube/proc/directions()
return tube_dirs
/obj/structure/transit_tube/proc/has_entrance(from_dir)
from_dir = turn(from_dir, 180)
for(var/direction in directions())
if(direction == from_dir)
return 1
return 0
/obj/structure/transit_tube/proc/has_exit(in_dir)
for(var/direction in directions())
if(direction == in_dir)
return 1
return 0
// Searches for an exit direction within 45 degrees of the
// specified dir. Returns that direction, or 0 if none match.
/obj/structure/transit_tube/proc/get_exit(in_dir)
var/near_dir = 0
var/in_dir_cw = turn(in_dir, -45)
var/in_dir_ccw = turn(in_dir, 45)
for(var/direction in directions())
if(direction == in_dir)
return direction
else if(direction == in_dir_cw)
near_dir = direction
else if(direction == in_dir_ccw)
near_dir = direction
return near_dir
// Return how many BYOND ticks to wait before entering/exiting
// the tube section. Default action is to return the value of
// a var, which wouldn't need a proc, but it makes it possible
// for later tube types to interact in more interesting ways
// such as being very fast in one direction, but slow in others
/obj/structure/transit_tube/proc/exit_delay(pod, to_dir)
return exit_delay
/obj/structure/transit_tube/proc/enter_delay(pod, to_dir)
return enter_delay
/obj/structure/transit_tube_pod/proc/follow_tube()
if(moving)
return
moving = 1
spawn()
var/obj/structure/transit_tube/current_tube = null
var/next_dir
var/next_loc
var/last_delay = 0
var/exit_delay
for(var/obj/structure/transit_tube/tube in loc)
if(tube.has_exit(dir))
current_tube = tube
break
while(current_tube)
next_dir = current_tube.get_exit(dir)
if(!next_dir)
break
exit_delay = current_tube.exit_delay(src, dir)
last_delay += exit_delay
sleep(exit_delay)
next_loc = get_step(loc, next_dir)
current_tube = null
for(var/obj/structure/transit_tube/tube in next_loc)
if(tube.has_entrance(next_dir))
current_tube = tube
break
if(current_tube == null)
dir = next_dir
Move(get_step(loc, dir)) // Allow collisions when leaving the tubes.
break
last_delay = current_tube.enter_delay(src, next_dir)
sleep(last_delay)
dir = next_dir
loc = next_loc // When moving from one tube to another, skip collision and such.
density = current_tube.density
if(current_tube && current_tube.should_stop_pod(src, next_dir))
current_tube.pod_stopped(src, dir)
break
density = 1
// If the pod is no longer in a tube, move in a line until stopped or slowed to a halt.
// /turf/inertial_drift appears to only work on mobs, and re-implementing some of the
// logic allows a gradual slowdown and eventual stop when passing over non-space turfs.
if(!current_tube && last_delay <= 10)
do
sleep(last_delay)
if(!istype(loc, /turf/space))
last_delay++
if(last_delay > 10)
break
while(isturf(loc) && Move(get_step(loc, dir)))
moving = 0
// Should I return a copy here? If the caller edits or del()s the returned
// datum, there might be problems if I don't...
/obj/structure/transit_tube_pod/return_air()
var/datum/gas_mixture/GM = new()
GM.oxygen = air_contents.oxygen
GM.carbon_dioxide = air_contents.carbon_dioxide
GM.nitrogen = air_contents.nitrogen
GM.toxins = air_contents.toxins
GM.temperature = air_contents.temperature
return GM
// For now, copying what I found in an unused FEA file (and almost identical in a
// used ZAS file). Means that assume_air and remove_air don't actually alter the
// air contents.
/obj/structure/transit_tube_pod/assume_air(datum/gas_mixture/giver)
return air_contents.merge(giver)
/obj/structure/transit_tube_pod/remove_air(amount)
return air_contents.remove(amount)
// Called when a pod arrives at, and before a pod departs from a station,
// giving it a chance to mix its internal air supply with the turf it is
// currently on.
/obj/structure/transit_tube_pod/proc/mix_air()
var/datum/gas_mixture/environment = loc.return_air()
var/env_pressure = environment.return_pressure()
var/int_pressure = air_contents.return_pressure()
var/total_pressure = env_pressure + int_pressure
if(total_pressure == 0)
return
// Math here: Completely made up, not based on realistic equasions.
// Goal is to balance towards equal pressure, but ensure some gas
// transfer in both directions regardless.
// Feel free to rip this out and replace it with something better,
// I don't really know muhch about how gas transfer rates work in
// SS13.
var/transfer_in = max(0.1, 0.5 * (env_pressure - int_pressure) / total_pressure)
var/transfer_out = max(0.1, 0.3 * (int_pressure - env_pressure) / total_pressure)
var/datum/gas_mixture/from_env = loc.remove_air(environment.total_moles() * transfer_in)
var/datum/gas_mixture/from_int = air_contents.remove(air_contents.total_moles() * transfer_out)
loc.assume_air(from_int)
air_contents.merge(from_env)
// When the player moves, check if the pos is currently stopped at a station.
// if it is, check the direction. If the direction matches the direction of
// the station, try to exit. If the direction matches one of the station's
// tube directions, launch the pod in that direction.
/obj/structure/transit_tube_pod/relaymove(mob/mob, direction)
if(istype(mob, /mob) && mob.client)
// If the pod is not in a tube at all, you can get out at any time.
if(!(locate(/obj/structure/transit_tube) in loc))
mob.loc = loc
mob.client.Move(get_step(loc, direction), direction)
//if(moving && istype(loc, /turf/space))
// Todo: If you get out of a moving pod in space, you should move as well.
// Same direction as pod? Direcion you moved? Halfway between?
if(!moving)
for(var/obj/structure/transit_tube/station/station in loc)
if(dir in station.directions())
if(!station.pod_moving)
if(direction == station.dir)
if(station.icon_state == "open")
mob.loc = loc
mob.client.Move(get_step(loc, direction), direction)
else
station.open_animation()
else if(direction in station.directions())
dir = direction
station.launch_pod()
return
for(var/obj/structure/transit_tube/tube in loc)
if(dir in tube.directions())
if(tube.has_exit(direction))
dir = direction
return
// Parse the icon_state into a list of directions.
// This means that mappers can use Dream Maker's built in
// "Generate Instances from Icon-states" option to get all
// variations. Additionally, as a separate proc, sub-types
// can handle it more intelligently.
/obj/structure/transit_tube/proc/init_dirs()
if(icon_state == "auto")
// Additional delay, for map loading.
spawn(1)
init_dirs_automatic()
else
tube_dirs = parse_dirs(icon_state)
if(copytext(icon_state, 1, 3) == "D-" || findtextEx(icon_state, "Pass"))
density = 0
// Tube station directions are simply 90 to either side of
// the exit.
/obj/structure/transit_tube/station/init_dirs()
tube_dirs = list(turn(dir, 90), turn(dir, -90))
// Initialize dirs by searching for tubes that do/might connect
// on nearby turfs. Create corner pieces if nessecary.
// Pick two directions, preferring tubes that already connect
// to loc, or other auto tubes if there aren't enough connections.
/obj/structure/transit_tube/proc/init_dirs_automatic()
var/list/connected = list()
var/list/connected_auto = list()
for(var/direction in tube_dir_list)
var/location = get_step(loc, direction)
for(var/obj/structure/transit_tube/tube in location)
if(tube.directions() == null && tube.icon_state == "auto")
connected_auto += direction
break
else if(turn(direction, 180) in tube.directions())
connected += direction
break
connected += connected_auto
tube_dirs = select_automatic_dirs(connected)
if(length(tube_dirs) == 2 && tube_dir_list.Find(tube_dirs[1]) > tube_dir_list.Find(tube_dirs[2]))
tube_dirs.Swap(1, 2)
generate_automatic_corners(tube_dirs)
select_automatic_icon_state(tube_dirs)
// Given a list of directions, look a pair that forms a 180 or
// 135 degree angle, and return a list containing the pair.
// If none exist, return list(connected[1], turn(connected[1], 180)
/obj/structure/transit_tube/proc/select_automatic_dirs(connected)
if(length(connected) < 1)
return list()
for(var/i = 1, i <= length(connected), i++)
for(var/j = i + 1, j <= length(connected), j++)
var/d1 = connected[i]
var/d2 = connected[j]
if(d1 == turn(d2, 135) || d1 == turn(d2, 180) || d1 == turn(d2, 225))
return list(d1, d2)
return list(connected[1], turn(connected[1], 180))
/obj/structure/transit_tube/proc/select_automatic_icon_state(directions)
if(length(directions) == 2)
icon_state = "[dir2text_short(directions[1])]-[dir2text_short(directions[2])]"
// Look for diagonal directions, generate the decorative corners in each.
/obj/structure/transit_tube/proc/generate_automatic_corners(directions)
for(var/direction in directions)
if(direction == 5 || direction == 6 || direction == 9 || direction == 10)
if(direction & NORTH)
create_automatic_decorative_corner(get_step(loc, NORTH), direction ^ 3)
else
create_automatic_decorative_corner(get_step(loc, SOUTH), direction ^ 3)
if(direction & EAST)
create_automatic_decorative_corner(get_step(loc, EAST), direction ^ 12)
else
create_automatic_decorative_corner(get_step(loc, WEST), direction ^ 12)
// Generate a corner, if one doesn't exist for the direction on the turf.
/obj/structure/transit_tube/proc/create_automatic_decorative_corner(location, direction)
var/state = "D-[dir2text_short(direction)]"
for(var/obj/structure/transit_tube/tube in location)
if(tube.icon_state == state)
return
var/obj/structure/transit_tube/tube = new(location)
tube.icon_state = state
tube.init_dirs()
// Uses a list() to cache return values. Since they should
// never be edited directly, all tubes with a certain
// icon_state can just reference the same list. In theory,
// reduces memory usage, and improves CPU cache usage.
// In reality, I don't know if that is quite how BYOND works,
// but it is probably safer to assume the existence of, and
// rely on, a sufficiently smart compiler/optimizer.
/obj/structure/transit_tube/proc/parse_dirs(text)
var/global/list/direction_table = list()
if(text in direction_table)
return direction_table[text]
var/list/split_text = stringsplit(text, "-")
// If the first token is D, the icon_state represents
// a purely decorative tube, and doesn't actually
// connect to anything.
if(split_text[1] == "D")
direction_table[text] = list()
return null
var/list/directions = list()
for(var/text_part in split_text)
var/direction = text2dir_extended(text_part)
if(direction > 0)
directions += direction
direction_table[text] = directions
return directions
// A copy of text2dir, extended to accept one and two letter
// directions, and to clearly return 0 otherwise.
/obj/structure/transit_tube/proc/text2dir_extended(direction)
switch(uppertext(direction))
if("NORTH", "N")
return 1
if("SOUTH", "S")
return 2
if("EAST", "E")
return 4
if("WEST", "W")
return 8
if("NORTHEAST", "NE")
return 5
if("NORTHWEST", "NW")
return 9
if("SOUTHEAST", "SE")
return 6
if("SOUTHWEST", "SW")
return 10
else
return 0
// A copy of dir2text, which returns the short one or two letter
// directions used in tube icon states.
/obj/structure/transit_tube/proc/dir2text_short(direction)
switch(direction)
if(1)
return "N"
if(2)
return "S"
if(4)
return "E"
if(8)
return "W"
if(5)
return "NE"
if(6)
return "SE"
if(9)
return "NW"
if(10)
return "SW"
else
return