Merge branch 'master' into upstream-merge-6762

This commit is contained in:
Novacat
2020-03-09 01:45:23 -04:00
committed by GitHub
199 changed files with 16129 additions and 12876 deletions

View File

@@ -2,17 +2,17 @@
name = "Jump a Shuttle"
/datum/admin_secret_item/admin_secret/jump_shuttle/can_execute(var/mob/user)
if(!shuttle_controller) return 0
if(!SSshuttles) return 0
return ..()
/datum/admin_secret_item/admin_secret/jump_shuttle/execute(var/mob/user)
. = ..()
if(!.)
return
var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in shuttle_controller.shuttles
var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in SSshuttles.shuttles
if (!shuttle_tag) return
var/datum/shuttle/S = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag]
var/origin_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world
if (!origin_area) return

View File

@@ -2,7 +2,7 @@
name = "Launch a Shuttle"
/datum/admin_secret_item/admin_secret/launch_shuttle/can_execute(var/mob/user)
if(!shuttle_controller) return 0
if(!SSshuttles) return 0
return ..()
/datum/admin_secret_item/admin_secret/launch_shuttle/execute(var/mob/user)
@@ -10,15 +10,15 @@
if(!.)
return
var/list/valid_shuttles = list()
for (var/shuttle_tag in shuttle_controller.shuttles)
if (istype(shuttle_controller.shuttles[shuttle_tag], /datum/shuttle/ferry))
for (var/shuttle_tag in SSshuttles.shuttles)
if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock))
valid_shuttles += shuttle_tag
var/shuttle_tag = input(user, "Which shuttle do you want to launch?") as null|anything in valid_shuttles
if (!shuttle_tag)
return
var/datum/shuttle/ferry/S = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag]
if (S.can_launch())
S.launch(user)
log_and_message_admins("launched the [shuttle_tag] shuttle", user)

View File

@@ -2,7 +2,7 @@
name = "Launch a Shuttle (Forced)"
/datum/admin_secret_item/admin_secret/launch_shuttle_forced/can_execute(var/mob/user)
if(!shuttle_controller) return 0
if(!SSshuttles) return 0
return ..()
/datum/admin_secret_item/admin_secret/launch_shuttle_forced/execute(var/mob/user)
@@ -10,15 +10,15 @@
if(!.)
return
var/list/valid_shuttles = list()
for (var/shuttle_tag in shuttle_controller.shuttles)
if (istype(shuttle_controller.shuttles[shuttle_tag], /datum/shuttle/ferry))
for (var/shuttle_tag in SSshuttles.shuttles)
if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock))
valid_shuttles += shuttle_tag
var/shuttle_tag = input(user, "Which shuttle's launch do you want to force?") as null|anything in valid_shuttles
if (!shuttle_tag)
return
var/datum/shuttle/ferry/S = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag]
if (S.can_force())
S.force_launch(user)
log_and_message_admins("forced the [shuttle_tag] shuttle", user)

View File

@@ -2,7 +2,7 @@
name = "Move a Shuttle"
/datum/admin_secret_item/admin_secret/move_shuttle/can_execute(var/mob/user)
if(!shuttle_controller) return 0
if(!SSshuttles) return 0
return ..()
/datum/admin_secret_item/admin_secret/move_shuttle/execute(var/mob/user)
@@ -13,16 +13,15 @@
if (confirm == "Cancel")
return
var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in shuttle_controller.shuttles
var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in SSshuttles.shuttles
if (!shuttle_tag) return
var/datum/shuttle/S = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag]
var/origin_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world
if (!origin_area) return
var/destination_tag = input(user, "Which landmark do you want to jump to? (IF YOU GET THIS WRONG THINGS WILL BREAK)") as null|anything in SSshuttles.registered_shuttle_landmarks
if (!destination_tag) return
var/destination_location = SSshuttles.get_landmark(destination_tag)
if (!destination_location) return
var/destination_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world
if (!destination_area) return
S.move(origin_area, destination_area)
S.attempt_move(destination_location)
log_and_message_admins("moved the [shuttle_tag] shuttle", user)

View File

@@ -103,15 +103,11 @@
// 'Tactical' processes such as moving a step, meleeing an enemy, firing a projectile, and other fairly cheap actions that need to happen quickly.
/datum/ai_holder/proc/handle_tactics()
if(busy)
return
handle_special_tactic()
handle_stance_tactical()
// 'Strategical' processes that are more expensive on the CPU and so don't get run as often as the above proc, such as A* pathfinding or robust targeting.
/datum/ai_holder/proc/handle_strategicals()
if(busy)
return
handle_special_strategical()
handle_stance_strategical()

View File

@@ -104,12 +104,8 @@
/obj/item/device/assembly_holder/Crossed(atom/movable/AM as mob|obj)
//VOREStation Edit begin: SHADEKIN
var/mob/SK = AM
if(istype(SK))
if(SK.shadekin_phasing_check())
return
//VOREStation Edit end: SHADEKIN
if(AM.is_incorporeal())
return
if(a_left)
a_left.Crossed(AM)
if(a_right)

View File

@@ -242,12 +242,8 @@
return
/obj/effect/beam/i_beam/Crossed(atom/movable/AM as mob|obj)
//VOREStation Edit begin: SHADEKIN
var/mob/SK = AM
if(istype(SK))
if(SK.shadekin_phasing_check())
return
//VOREStation Edit end: SHADEKIN
if(AM.is_incorporeal())
return
if(istype(AM, /obj/effect/beam))
return
spawn(0)

View File

@@ -82,13 +82,9 @@
..()
/obj/item/device/assembly/mousetrap/Crossed(AM as mob|obj)
//VOREStation Edit begin: SHADEKIN
var/mob/SK = AM
if(istype(SK))
if(SK.shadekin_phasing_check())
return
//VOREStation Edit end: SHADEKIN
/obj/item/device/assembly/mousetrap/Crossed(atom/movable/AM as mob|obj)
if(AM.is_incorporeal())
return
if(armed)
if(ishuman(AM))
var/mob/living/carbon/H = AM

View File

@@ -428,6 +428,7 @@ client/verb/character_setup()
//Log, disable
log_debug("[key_name(src)] reloaded VChat.")
winset(src, null, "outputwindow.htmloutput.is-visible=false;outputwindow.oldoutput.is-visible=false;outputwindow.chatloadlabel.is-visible=true")
//The hard way
qdel_null(src.chatOutput)

View File

@@ -526,6 +526,25 @@
src.name = "Frosted Jelly Donut"
reagents.add_reagent("sprinkles", 2)
/obj/item/weapon/reagent_containers/food/snacks/donut/poisonberry
name = "Jelly Donut"
desc = "You jelly?"
icon_state = "jdonut1"
filling_color = "#ED1169"
center_of_mass = list("x"=16, "y"=11)
nutriment_amt = 3
/obj/item/weapon/reagent_containers/food/snacks/donut/poisonberry/Initialize()
. = ..()
reagents.add_reagent("sprinkles", 1)
reagents.add_reagent("poisonberryjuice", 5)
bitesize = 5
if(prob(30))
src.icon_state = "jdonut2"
src.overlay_state = "box-donut2"
src.name = "Frosted Jelly Donut"
reagents.add_reagent("sprinkles", 2)
/obj/item/weapon/reagent_containers/food/snacks/donut/slimejelly
name = "Jelly Donut"
desc = "You jelly?"
@@ -1121,9 +1140,16 @@
/obj/item/weapon/reagent_containers/food/snacks/berryclafoutis/Initialize()
. = ..()
reagents.add_reagent("berryjuice", 5)
bitesize = 3
/obj/item/weapon/reagent_containers/food/snacks/berryclafoutis/berry/Initialize()
. = ..()
reagents.add_reagent("berryjuice", 5)
/obj/item/weapon/reagent_containers/food/snacks/berryclafoutis/poison/Initialize()
. = ..()
reagents.add_reagent("poisonberryjuice", 5)
/obj/item/weapon/reagent_containers/food/snacks/waffles
name = "waffles"
desc = "Mmm, waffles"
@@ -3904,13 +3930,21 @@
filling_color = "#E0CF9B"
center_of_mass = list("x"=17, "y"=4)
nutriment_amt = 6
nutriment_desc = list("sweetness" = 2, "muffin" = 2, "berries" = 2)
nutriment_desc = list("sweetness" = 2, "muffin" = 2)
/obj/item/weapon/reagent_containers/food/snacks/berrymuffin/Initialize()
. = ..()
reagents.add_reagent("nutriment", 6)
bitesize = 2
/obj/item/weapon/reagent_containers/food/snacks/berrymuffin/berry/Initialize()
. = ..()
reagents.add_reagent("berryjuice", 3)
/obj/item/weapon/reagent_containers/food/snacks/berrymuffin/poison/Initialize()
. = ..()
reagents.add_reagent("poisonberryjuice", 3)
/obj/item/weapon/reagent_containers/food/snacks/ghostmuffin
name = "booberry muffin"
desc = "My stomach is a graveyard! No living being can quench my bloodthirst!"
@@ -3925,6 +3959,16 @@
reagents.add_reagent("nutriment", 6)
bitesize = 2
/obj/item/weapon/reagent_containers/food/snacks/ghostmuffin/berry/Initialize()
. = ..()
reagents.add_reagent("berryjuice", 3)
bitesize = 2
/obj/item/weapon/reagent_containers/food/snacks/ghostmuffin/poison/Initialize()
. = ..()
reagents.add_reagent("poisonberryjuice", 3)
bitesize = 2
/obj/item/weapon/reagent_containers/food/snacks/eggroll
name = "egg roll"
desc = "Free with orders over 10 thalers."

View File

@@ -1,280 +1,279 @@
// Chaos cake
/datum/recipe/microwave/chaoscake_layerone
reagents = list("flour" = 300,"milk" = 200, "sugar" = 100, "egg" = 30)
fruit = list("poisonberries" = 15, "cherries" = 15)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/meat/,
/obj/item/weapon/reagent_containers/food/snacks/meat/,
/obj/item/weapon/reagent_containers/food/snacks/meat/,
/obj/item/weapon/reagent_containers/food/snacks/meat/
)
result = /obj/structure/chaoscake
/datum/recipe/microwave/chaoscake_layertwo
reagents = list("flour" = 300, "milk" = 200, "sugar" = 100, "egg" = 30, )
fruit = list("vanilla" = 15, "banana" = 15)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough
)
result = /obj/item/weapon/chaoscake_layer
/datum/recipe/microwave/chaoscake_layerthree
reagents = list("flour" = 240, "milk" = 150, "sugar" = 80, "egg" = 24, "deathbell" = 100)
fruit = list("grapes" = 30)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough
)
result = /obj/item/weapon/chaoscake_layer/three
/datum/recipe/microwave/chaoscake_layerfour
reagents = list("flour" = 240, "milk" = 150, "sugar" = 80, "egg" = 24, "milkshake" = 300)
fruit = list("rice" = 30)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough
)
result = /obj/item/weapon/chaoscake_layer/four
/datum/recipe/microwave/chaoscake_layerfive
reagents = list("flour" = 180, "milk" = 100, "sugar" = 60, "egg" = 18, "blood" = 300)
fruit = list("tomato" = 20)
items = list() //supposed to be made with lobster, still has to be ported.
result = /obj/item/weapon/chaoscake_layer/five
/datum/recipe/microwave/chaoscake_layersix
reagents = list("flour" = 180, "milk" = 100, "sugar" = 60, "egg" = 18, "sprinkles" = 10)
fruit = list("apple" = 30)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/chocolatebar,
/obj/item/weapon/reagent_containers/food/snacks/chocolatebar,
/obj/item/weapon/reagent_containers/food/snacks/chocolatebar
)
result = /obj/item/weapon/chaoscake_layer/six
/datum/recipe/microwave/chaoscake_layerseven
reagents = list("flour" = 120, "milk" = 50, "sugar" = 40, "egg" = 12, "devilskiss" = 200)
fruit = list("potato" = 10)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough
)
result = /obj/item/weapon/chaoscake_layer/seven
/datum/recipe/microwave/chaoscake_layereight
reagents = list("flour" = 120, "milk" = 50, "sugar" = 40, "egg" = 12, "cream" = 200)
fruit = list("lemon" = 10)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough
)
result = /obj/item/weapon/chaoscake_layer/eight
/datum/recipe/microwave/chaoscake_layernine
reagents = list("water" = 100, "blood" = 100)
fruit = list("goldapple" = 50)
items = list()
result = /obj/item/weapon/chaoscake_layer/nine
/obj/structure/chaoscake
name = "An unfinished cake"
desc = "A single layer of a strange cake, you can see the cherry paste ooze, but it feels very incomplete..."
icon = 'icons/obj/food64x64.dmi'
icon_state = "chaoscake_unfinished-1"
pixel_x = -16
var/slices = 6
var/maxslices = 6
var/stage = 1
var/maxstages = 9
var/edible = 0
var/regentime = 1000
var/interval = 0
var/static/list/desclist2 = list(
"The first layer of a strange cake, you can see the cherry paste ooze.",
"The second layer of the cake sits in place now, smelling of pear with delicious colourful cream.",
"The third layer of cake adds a strange purple layer, glazed over with frosting. It smells of grapes, but with a hint of something foul underneath.",
"With the fourth layer added the cake looks happier again. Reeking of vanilla, it brings up memories of childhood joy.",
"The fifth layer is extremely disturbing on that cake. Smelling of pure copper, it seems that bright blood clots are forming on top.",
"The cake is getting closer with the sixth layer added, the pink hue smelling of chocolate, with colourful sprinkles on top.",
"The first pair of triplets rest on the cake, despite being mostly similar to the first three, an evil aura becomes noticable.",
"The second pair of triplets rest on the cake, if you stand on the bright side, you can feel a good aura lifting your mood.",
"A chaos cake. Both a creation of dark and light, the two cakes are kept in a careful balance by that mystical coin in the middle. It's said its effects would dissipate if the balance is ever tipped in favour of one side too much, so both sides much be cut equally."
)
/obj/item/weapon/chaoscake_layer
name = "A layer of cake"
desc = "a layer of cake, it is made out of colourful cream."
icon = 'icons/obj/food.dmi'
icon_state = "chaoscake_layer-2"
var/layer_stage = 1
/obj/item/weapon/chaoscake_layer/three
desc = "a layer of cake, glazed in purple."
icon_state = "chaoscake_layer-3"
layer_stage = 2
/obj/item/weapon/chaoscake_layer/four
desc = "a layer of cake, reminding you of a colouring book."
icon_state = "chaoscake_layer-4"
layer_stage = 3
/obj/item/weapon/chaoscake_layer/five
desc = "A layer of cake, smells like copper."
icon_state = "chaoscake_layer-5"
layer_stage = 4
/obj/item/weapon/chaoscake_layer/six
desc = "A layer of cake, featuring colourful sprinkles."
icon_state = "chaoscake_layer-6"
layer_stage = 5
/obj/item/weapon/chaoscake_layer/seven
desc = "A triplet of evil cake parts."
icon_state = "chaoscake_layer-7"
layer_stage = 6
/obj/item/weapon/chaoscake_layer/eight
desc = "A triplet of good cake parts."
icon_state = "chaoscake_layer-8"
layer_stage = 7
/obj/item/weapon/chaoscake_layer/nine
name = "A coin of balance"
desc = "A very peculiar coin, it seems to stabilise the air around it."
icon_state = "chaoscake_layer-9"
layer_stage = 8
/obj/structure/chaoscake/proc/HasSliceMissing()
..()
if(slices < maxslices)
if(interval >= regentime)
interval = 0
slices++
HasSliceMissing()
else
interval++
HasSliceMissing()
else
return
/obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice
name = "The Chaos Cake Slice"
desc = "A slice from The Chaos Cake, it pulses weirdly, as if angry to be seperated from the whole"
icon_state = "chaoscake_slice-1"
center_of_mass = list("x"=16, "y"=10)
nutriment_desc = list()
nutriment_amt = 4
volume = 80
/obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice/Initialize()
..()
var/i = rand(1,6)
icon_state = "chaoscake_slice-[i]"
switch(i)
if(1)
name = "Slice Of Evil" //Pretty damn poisonous, takes a lot of work to make safe for consumption, useful for medical.
desc = "An odd slice, despite the grease and cherries oozing off the top, it smells delicious."
nutriment_desc = list("The desire to consume" = 10) // You won't even taste the poison.
reagents.add_reagent("neurotoxic_protein", 2)
reagents.add_reagent("shockchem", 2)
reagents.add_reagent("amatoxin", 2)
reagents.add_reagent("carpotoxin", 2)
reagents.add_reagent("spidertoxin", 2)
bitesize = 7
if(2)
name = "Slice Of Evil" //A bad trip
desc = "A mysterious slice, coated in purple frosting that smells like grapes."
nutriment_desc = list("The desire to show off an party" = 10)
reagents.add_reagent("stoxin", 2)
reagents.add_reagent("space_drugs", 10)
reagents.add_reagent("serotrotium", 4)
reagents.add_reagent("cryptobiolin", 8)
reagents.add_reagent("mindbreaker", 10)
reagents.add_reagent("psilocybin", 10)
bitesize = 30 //even a single bite won't make you escape fate.
if(3)
name = "Slice Of Evil" //acidic
desc = "A menacing slice, smelling clearly of copper, blood clots float on top."
nutriment_desc = list("Infernal Rage" = 10)
reagents.add_reagent("blood", 20)
reagents.add_reagent("stomacid", 10)
reagents.add_reagent("mutagen", 4)
reagents.add_reagent("thirteenloko", 20)
reagents.add_reagent("hyperzine", 10)
bitesize = 30
if(4)
name = "Slice Of Good" //anti-tox
desc = "A colourful slice, smelling of pear and coated in delicious cream."
nutriment_desc = list("Hapiness" = 10)
reagents.add_reagent("anti_toxin", 2)
reagents.add_reagent("tricordrazine", 2)
bitesize = 3
if(5)
name = "Slice Of Good" //anti-oxy
desc = "A light slice, it's pretty to look at and smells of vanilla."
nutriment_desc = list("Freedom" = 10)
reagents.add_reagent("dexalinp", 2)
reagents.add_reagent("tricordrazine", 2)
bitesize = 3
if(6)
name = "Slice Of Good" //anti-burn/brute
desc = "A hearty slice, it smells of chocolate and strawberries."
nutriment_desc = list("Love" = 10)
reagents.add_reagent("bicaridine", 2)
reagents.add_reagent("tricordrazine", 2)
reagents.add_reagent("kelotane", 2)
bitesize = 4
/obj/structure/chaoscake/attackby(var/obj/item/weapon/W, var/mob/living/user)
if(istype(W,/obj/item/weapon/material/knife))
if(edible == 1)
HasSliceMissing()
if(slices <= 0)
to_chat(user, "The cake hums away quietly as the chaos powered goodness slowly recovers the large amount of lost mass, best to give it a moment before cutting another slice.")
return
else
to_chat(user, "You cut a slice of the cake. The slice looks like the cake was just baked, and you can see before your eyes as the spot where you cut the slice slowly regenerates!")
slices = slices - 1
icon_state = "chaoscake-[slices]"
new /obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice(src.loc)
else
to_chat(user, "<span class='notice'>It looks so good... But it feels so wrong to eat it before it's finished...</span>")
return
if(istype(W,/obj/item/weapon/chaoscake_layer))
var/obj/item/weapon/chaoscake_layer/C = W
if(C.layer_stage == 8)
to_chat(user, "Finally! The coin on the top, the almighty chaos cake is complete!")
qdel(W)
stage++
desc = desclist2[stage]
icon_state = "chaoscake-6"
edible = 1
name = "The Chaos Cake!"
else if(stage == maxstages)
to_chat(user, "The cake is already done!")
else if(stage == C.layer_stage)
to_chat(user, "You add another layer to the cake, nice.")
qdel(W)
stage++
desc = desclist2[stage]
icon_state = "chaoscake_stage-[stage]"
else
to_chat(user, "Hmm, doesnt seem like this layer is supposed to be added there?")
// Chaos cake
/datum/recipe/microwave/chaoscake_layerone
reagents = list("flour" = 300,"milk" = 200, "sugar" = 100, "egg" = 30)
fruit = list("poisonberries" = 15, "cherries" = 15)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/meat/,
/obj/item/weapon/reagent_containers/food/snacks/meat/,
/obj/item/weapon/reagent_containers/food/snacks/meat/,
/obj/item/weapon/reagent_containers/food/snacks/meat/
)
result = /obj/structure/chaoscake
/datum/recipe/microwave/chaoscake_layertwo
reagents = list("flour" = 300, "milk" = 200, "sugar" = 100, "egg" = 30, )
fruit = list("vanilla" = 15, "banana" = 15)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough
)
result = /obj/item/weapon/chaoscake_layer
/datum/recipe/microwave/chaoscake_layerthree
reagents = list("flour" = 240, "milk" = 150, "sugar" = 80, "egg" = 24, "deathbell" = 100)
fruit = list("grapes" = 30)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough
)
result = /obj/item/weapon/chaoscake_layer/three
/datum/recipe/microwave/chaoscake_layerfour
reagents = list("flour" = 240, "milk" = 150, "sugar" = 80, "egg" = 24, "milkshake" = 300)
fruit = list("rice" = 30)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough
)
result = /obj/item/weapon/chaoscake_layer/four
/datum/recipe/microwave/chaoscake_layerfive
reagents = list("flour" = 180, "milk" = 100, "sugar" = 60, "egg" = 18, "blood" = 300)
fruit = list("tomato" = 20)
items = list() //supposed to be made with lobster, still has to be ported.
result = /obj/item/weapon/chaoscake_layer/five
/datum/recipe/microwave/chaoscake_layersix
reagents = list("flour" = 180, "milk" = 100, "sugar" = 60, "egg" = 18, "sprinkles" = 10)
fruit = list("apple" = 30)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/chocolatebar,
/obj/item/weapon/reagent_containers/food/snacks/chocolatebar,
/obj/item/weapon/reagent_containers/food/snacks/chocolatebar
)
result = /obj/item/weapon/chaoscake_layer/six
/datum/recipe/microwave/chaoscake_layerseven
reagents = list("flour" = 120, "milk" = 50, "sugar" = 40, "egg" = 12, "devilskiss" = 200)
fruit = list("potato" = 10)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough
)
result = /obj/item/weapon/chaoscake_layer/seven
/datum/recipe/microwave/chaoscake_layereight
reagents = list("flour" = 120, "milk" = 50, "sugar" = 40, "egg" = 12, "cream" = 200)
fruit = list("lemon" = 10)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough
)
result = /obj/item/weapon/chaoscake_layer/eight
/datum/recipe/microwave/chaoscake_layernine
reagents = list("water" = 100, "blood" = 100)
fruit = list("goldapple" = 50)
items = list()
result = /obj/item/weapon/chaoscake_layer/nine
/obj/structure/chaoscake
name = "An unfinished cake"
desc = "A single layer of a strange cake, you can see the cherry paste ooze, but it feels very incomplete..."
icon = 'icons/obj/food64x64.dmi'
icon_state = "chaoscake_unfinished-1"
pixel_x = -16
var/slices = 6
var/maxslices = 6
var/stage = 1
var/maxstages = 9
var/edible = 0
var/regentime = 1000
var/interval = 0
var/static/list/desclist2 = list(
"The first layer of a strange cake, you can see the cherry paste ooze.",
"The second layer of the cake sits in place now, smelling of pear with delicious colourful cream.",
"The third layer of cake adds a strange purple layer, glazed over with frosting. It smells of grapes, but with a hint of something foul underneath.",
"With the fourth layer added the cake looks happier again. Reeking of vanilla, it brings up memories of childhood joy.",
"The fifth layer is extremely disturbing on that cake. Smelling of pure copper, it seems that bright blood clots are forming on top.",
"The cake is getting closer with the sixth layer added, the pink hue smelling of chocolate, with colourful sprinkles on top.",
"The first pair of triplets rest on the cake, despite being mostly similar to the first three, an evil aura becomes noticable.",
"The second pair of triplets rest on the cake, if you stand on the bright side, you can feel a good aura lifting your mood.",
"A chaos cake. Both a creation of dark and light, the two cakes are kept in a careful balance by that mystical coin in the middle. It's said its effects would dissipate if the balance is ever tipped in favour of one side too much, so both sides much be cut equally."
)
/obj/item/weapon/chaoscake_layer
name = "A layer of cake"
desc = "a layer of cake, it is made out of colourful cream."
icon = 'icons/obj/food.dmi'
icon_state = "chaoscake_layer-2"
var/layer_stage = 1
/obj/item/weapon/chaoscake_layer/three
desc = "a layer of cake, glazed in purple."
icon_state = "chaoscake_layer-3"
layer_stage = 2
/obj/item/weapon/chaoscake_layer/four
desc = "a layer of cake, reminding you of a colouring book."
icon_state = "chaoscake_layer-4"
layer_stage = 3
/obj/item/weapon/chaoscake_layer/five
desc = "A layer of cake, smells like copper."
icon_state = "chaoscake_layer-5"
layer_stage = 4
/obj/item/weapon/chaoscake_layer/six
desc = "A layer of cake, featuring colourful sprinkles."
icon_state = "chaoscake_layer-6"
layer_stage = 5
/obj/item/weapon/chaoscake_layer/seven
desc = "A triplet of evil cake parts."
icon_state = "chaoscake_layer-7"
layer_stage = 6
/obj/item/weapon/chaoscake_layer/eight
desc = "A triplet of good cake parts."
icon_state = "chaoscake_layer-8"
layer_stage = 7
/obj/item/weapon/chaoscake_layer/nine
name = "A coin of balance"
desc = "A very peculiar coin, it seems to stabilise the air around it."
icon_state = "chaoscake_layer-9"
layer_stage = 8
/obj/structure/chaoscake/proc/HasSliceMissing()
..()
if(slices < maxslices)
if(interval >= regentime)
interval = 0
slices++
HasSliceMissing()
else
interval++
HasSliceMissing()
else
return
/obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice
name = "The Chaos Cake Slice"
desc = "A slice from The Chaos Cake, it pulses weirdly, as if angry to be seperated from the whole"
icon_state = "chaoscake_slice-1"
center_of_mass = list("x"=16, "y"=10)
nutriment_desc = list()
nutriment_amt = 4
volume = 80
/obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice/Initialize()
..()
var/i = rand(1,6)
icon_state = "chaoscake_slice-[i]"
switch(i)
if(1)
name = "Slice Of Evil" //Pretty damn poisonous, takes a lot of work to make safe for consumption, useful for medical.
desc = "An odd slice, despite the grease and cherries oozing off the top, it smells delicious."
nutriment_desc = list("The desire to consume" = 10) // You won't even taste the poison.
reagents.add_reagent("neurotoxic_protein", 2)
reagents.add_reagent("shockchem", 2)
reagents.add_reagent("amatoxin", 2)
reagents.add_reagent("carpotoxin", 2)
reagents.add_reagent("spidertoxin", 2)
bitesize = 7
if(2)
name = "Slice Of Evil" //A bad trip
desc = "A mysterious slice, coated in purple frosting that smells like grapes."
nutriment_desc = list("The desire to show off an party" = 10)
reagents.add_reagent("stoxin", 2)
reagents.add_reagent("space_drugs", 10)
reagents.add_reagent("serotrotium", 4)
reagents.add_reagent("cryptobiolin", 8)
reagents.add_reagent("mindbreaker", 10)
reagents.add_reagent("psilocybin", 10)
bitesize = 30 //even a single bite won't make you escape fate.
if(3)
name = "Slice Of Evil" //acidic
desc = "A menacing slice, smelling clearly of copper, blood clots float on top."
nutriment_desc = list("Infernal Rage" = 10)
reagents.add_reagent("blood", 20)
reagents.add_reagent("stomacid", 10)
reagents.add_reagent("mutagen", 4)
reagents.add_reagent("thirteenloko", 20)
reagents.add_reagent("hyperzine", 10)
bitesize = 30
if(4)
name = "Slice Of Good" //anti-tox
desc = "A colourful slice, smelling of pear and coated in delicious cream."
nutriment_desc = list("Hapiness" = 10)
reagents.add_reagent("anti_toxin", 2)
reagents.add_reagent("tricordrazine", 2)
bitesize = 3
if(5)
name = "Slice Of Good" //anti-oxy
desc = "A light slice, it's pretty to look at and smells of vanilla."
nutriment_desc = list("Freedom" = 10)
reagents.add_reagent("dexalinp", 2)
reagents.add_reagent("tricordrazine", 2)
bitesize = 3
if(6)
name = "Slice Of Good" //anti-burn/brute
desc = "A hearty slice, it smells of chocolate and strawberries."
nutriment_desc = list("Love" = 10)
reagents.add_reagent("bicaridine", 2)
reagents.add_reagent("tricordrazine", 2)
reagents.add_reagent("kelotane", 2)
bitesize = 4
/obj/structure/chaoscake/attackby(var/obj/item/weapon/W, var/mob/living/user)
if(istype(W,/obj/item/weapon/material/knife))
if(edible == 1)
HasSliceMissing()
if(slices <= 0)
to_chat(user, "The cake hums away quietly as the chaos powered goodness slowly recovers the large amount of lost mass, best to give it a moment before cutting another slice.")
return
else
to_chat(user, "You cut a slice of the cake. The slice looks like the cake was just baked, and you can see before your eyes as the spot where you cut the slice slowly regenerates!")
slices = slices - 1
icon_state = "chaoscake-[slices]"
new /obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice(src.loc)
else
to_chat(user, "<span class='notice'>It looks so good... But it feels so wrong to eat it before it's finished...</span>")
return
if(istype(W,/obj/item/weapon/chaoscake_layer))
var/obj/item/weapon/chaoscake_layer/C = W
if(C.layer_stage == 8)
to_chat(user, "Finally! The coin on the top, the almighty chaos cake is complete!")
qdel(W)
stage++
desc = desclist2[stage]
icon_state = "chaoscake-6"
edible = 1
name = "The Chaos Cake!"
else if(stage == maxstages)
to_chat(user, "The cake is already done!")
else if(stage == C.layer_stage)
to_chat(user, "You add another layer to the cake, nice.")
qdel(W)
stage++
desc = desclist2[stage]
icon_state = "chaoscake_unfinished-[stage]"
else
to_chat(user, "Hmm, doesnt seem like this layer is supposed to be added there?")

View File

@@ -55,6 +55,13 @@ I said no!
)
result = /obj/item/weapon/reagent_containers/food/snacks/donut/jelly
/datum/recipe/microwave/jellydonut/poisonberry
reagents = list("poisonberryjuice" = 5, "sugar" = 5)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough
)
result = /obj/item/weapon/reagent_containers/food/snacks/donut/poisonberry
/datum/recipe/microwave/jellydonut/slime
reagents = list("slimejelly" = 5, "sugar" = 5)
items = list(
@@ -308,7 +315,14 @@ I said no!
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
)
result = /obj/item/weapon/reagent_containers/food/snacks/berryclafoutis
result = /obj/item/weapon/reagent_containers/food/snacks/berryclafoutis/berry
/datum/recipe/microwave/poisonberryclafoutis
fruit = list("poisonberries" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
)
result = /obj/item/weapon/reagent_containers/food/snacks/berryclafoutis/poison
/datum/recipe/microwave/wingfangchu
reagents = list("soysauce" = 5)
@@ -639,7 +653,7 @@ I said no!
result = /obj/item/weapon/reagent_containers/food/snacks/toastedsandwich
/datum/recipe/microwave/peanutbutterjellysandwich
reagents = list("berryjuice" = 5, "peanutbutter" = 5)
reagents = list("cherryjelly" = 5, "peanutbutter" = 5)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/slice/bread,
/obj/item/weapon/reagent_containers/food/snacks/slice/bread
@@ -1106,7 +1120,15 @@ I said no!
/obj/item/weapon/reagent_containers/food/snacks/dough
)
fruit = list("berries" = 1)
result = /obj/item/weapon/reagent_containers/food/snacks/berrymuffin
result = /obj/item/weapon/reagent_containers/food/snacks/berrymuffin/berry
/datum/recipe/microwave/poisonberrymuffin
reagents = list("milk" = 5, "sugar" = 5)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough
)
fruit = list("poisonberries" = 1)
result = /obj/item/weapon/reagent_containers/food/snacks/berrymuffin/poison
/datum/recipe/microwave/ghostmuffin
reagents = list("milk" = 5, "sugar" = 5)
@@ -1115,7 +1137,16 @@ I said no!
/obj/item/weapon/ectoplasm
)
fruit = list("berries" = 1)
result = /obj/item/weapon/reagent_containers/food/snacks/ghostmuffin
result = /obj/item/weapon/reagent_containers/food/snacks/ghostmuffin/berry
/datum/recipe/microwave/poisonghostmuffin
reagents = list("milk" = 5, "sugar" = 5)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/ectoplasm
)
fruit = list("poisonberries" = 1)
result = /obj/item/weapon/reagent_containers/food/snacks/ghostmuffin/poison
/datum/recipe/microwave/eggroll
reagents = list("soysauce" = 10)

View File

@@ -157,12 +157,8 @@
overlays |= plant_icon
/obj/item/weapon/reagent_containers/food/snacks/grown/Crossed(var/mob/living/M)
//VOREStation Edit begin: SHADEKIN
var/mob/SK = M
if(istype(SK))
if(SK.shadekin_phasing_check())
return
//VOREStation Edit end: SHADEKIN
if(M.is_incorporeal())
return
if(seed && seed.get_trait(TRAIT_JUICY) == 2)
if(istype(M))

View File

@@ -80,6 +80,7 @@
/datum/seed/berry/poison
name = "poisonberries"
seed_name = "poison berry"
kitchen_tag = "poisonberries"
display_name = "poison berry bush"
mutants = list("deathberries")
chems = list("nutriment" = list(1), "toxin" = list(3,5), "poisonberryjuice" = list(10,5))

View File

@@ -20,12 +20,8 @@
manual_unbuckle(user)
/obj/effect/plant/Crossed(atom/movable/O)
//VOREStation Edit begin: SHADEKIN
var/mob/SK = O
if(istype(SK))
if(SK.shadekin_phasing_check())
return
//VOREStation Edit end: SHADEKIN
if(O.is_incorporeal())
return
if(isliving(O))
trodden_on(O)

View File

@@ -22,7 +22,7 @@
/turf/proc/lighting_clear_overlay()
if(lighting_overlay)
qdel(lighting_overlay)
qdel(lighting_overlay, force = TRUE)
for(var/datum/lighting_corner/C in corners)
C.update_active()

View File

@@ -39,6 +39,9 @@
if (SSatoms.initialized == INITIALIZATION_INSSATOMS)
return // let proper initialisation handle it later
var/prev_shuttle_queue_state = SSshuttles.block_init_queue
SSshuttles.block_init_queue = TRUE
var/list/atom/atoms = list()
var/list/area/areas = list()
var/list/obj/structure/cable/cables = list()
@@ -71,6 +74,9 @@
var/area/A = I
A.power_change()
SSshuttles.block_init_queue = prev_shuttle_queue_state
SSshuttles.process_init_queues() // We will flush the queue unless there were other blockers, in which case they will do it.
admin_notice("<span class='danger'>Submap initializations finished.</span>", R_DEBUG)
/datum/map_template/proc/load_new_z(var/centered = FALSE, var/orientation = 0)

View File

@@ -0,0 +1,13 @@
/obj/structure/fans/hardlight
name = "hardlight shield"
desc = "Retains air, allows passage."
plane = TURF_PLANE
layer = ABOVE_TURF_LAYER
density = FALSE
icon = 'icons/effects/effects_vr.dmi'
icon_state = "hardlight"
buildstackamount = 2
light_range = 3
light_power = 1
light_color = "#FFFFFF"

View File

@@ -0,0 +1,36 @@
// Ignition, but confined to the modifier system.
// This makes it more predictable and thus, easier to balance.
/datum/modifier/fire
name = "on fire"
desc = "You are on fire! You will be harmed until the fire goes out or you extinguish it with water."
mob_overlay_state = "on_fire"
on_created_text = "<span class='danger'>You burst into flames!</span>"
on_expired_text = "<span class='warning'>The fire starts to fade.</span>"
stacks = MODIFIER_STACK_ALLOWED // Multiple instances will hurt a lot.
var/damage_per_tick = 5
/datum/modifier/fire/intense
mob_overlay_state = "on_fire_intense"
damage_per_tick = 10
/datum/modifier/fire/tick()
holder.inflict_heat_damage(damage_per_tick)
/*
* Modifier used by projectiles, like the flamethrower, that rely heavily on fire_stacks to persist.
*/
/datum/modifier/fire/stack_managed/tick()
..()
if(!holder.fire_stacks || holder.fire_stacks < 0)
if(prob(10))
expire()
else if(holder.fire_stacks > 0)
holder.fire_stacks -= 1
/datum/modifier/fire/stack_managed/intense
mob_overlay_state = "on_fire_intense"
damage_per_tick = 10

View File

@@ -224,28 +224,6 @@ the artifact triggers the rage.
accuracy_dispersion = 2 // A combination of fear and immense pain or damage reults in a twitching firing arm. Flee.
// Ignition, but confined to the modifier system.
// This makes it more predictable and thus, easier to balance.
/datum/modifier/fire
name = "on fire"
desc = "You are on fire! You will be harmed until the fire goes out or you extinguish it with water."
mob_overlay_state = "on_fire"
on_created_text = "<span class='danger'>You combust into flames!</span>"
on_expired_text = "<span class='warning'>The fire starts to fade.</span>"
stacks = MODIFIER_STACK_ALLOWED // Multiple instances will hurt a lot.
var/damage_per_tick = 5
/datum/modifier/fire/intense
mob_overlay_state = "on_fire_intense"
damage_per_tick = 10
/datum/modifier/fire/tick()
holder.inflict_heat_damage(damage_per_tick)
// Applied when near something very cold.
// Reduces mobility, attack speed.
/datum/modifier/chilled

View File

@@ -88,23 +88,26 @@
desc = "Shadekin seem to always know what the others are thinking. This is probably why."
speech_verb = "mars"
ask_verb = "mars"
exclaim_verb = "mars"
exclaim_verb = "MARS"
colour = "changeling"
key = "m"
machine_understands = FALSE
flags = WHITELISTED | HIVEMIND
/datum/language/human/slavic
/datum/language/slavic
name = LANGUAGE_SLAVIC
desc = "The official language of the Independent Colonial Confederation of Gilgamesh, originally established in 2122 by the short-lived United Slavic Confederation on Earth."
colour = "solcom"
key = "r"
speech_verb = "govorit" // All Russian for "says" "asks" and "shouts". Mostly here as a joke.
ask_verb = "sprashivaet"
exclaim_verb = "krichit"
whisper_verb = "shepchet"
colour = "attack" // Red for slav!
key = "s"
syllables = list(
"rus", "zem", "ave", "groz", "ski", "ska", "ven", "konst", "pol", "lin", "svy",
"danya", "da", "mied", "zan", "das", "krem", "myka", "to", "st", "no", "na", "ni",
"ko", "ne", "en", "po", "ra", "li", "on", "byl", "cto", "eni", "ost", "ol", "ego",
"ver", "stv", "pro"
"rus", "zem", "ave", "blyat", "cyk", "groz", "ski", "ska", "ven", "konst", "pol", "lin", "svy",
"danya", "da", "mied", "kuz,", "zan", "das", "krem", "myka", "to", "st", "no", "na", "ni",
"ko", "ne", "en", "po", "tro", "ra", "li", "on", "byl", "cto", "eni", "ost", "ol", "ego",
"ver", "stv", "pro", "ski"
)
/datum/language/unathi
@@ -113,8 +116,6 @@
flags = 0
/datum/language/skrell
flags = 0
/datum/language/human
flags = 0
/datum/language/seromi
flags = 0
/datum/language/zaddat

View File

@@ -243,12 +243,8 @@
// this handles mulebots and vehicles
// and now mobs on fire
/mob/living/carbon/human/Crossed(var/atom/movable/AM)
//VOREStation Edit begin: SHADEKIN
var/mob/SK = AM
if(istype(SK))
if(SK.shadekin_phasing_check())
return
//VOREStation Edit end: SHADEKIN
if(AM.is_incorporeal())
return
if(istype(AM, /mob/living/bot/mulebot))
var/mob/living/bot/mulebot/MB = AM
MB.runOver(src)

View File

@@ -224,10 +224,8 @@
// Handle footstep sounds
/mob/living/carbon/human/handle_footstep(var/turf/T)
//VOREStation Edit begin: SHADEKIN
if(shadekin_phasing_check())
if(is_incorporeal())
return
//VOREStation Edit end: SHADEKIN
if(!config.footstep_volume || !T.footstep_sounds || !T.footstep_sounds.len)
return
// Future Upgrades - Multi species support

View File

@@ -618,10 +618,8 @@
//Stuff like the xenomorph's plasma regen happens here.
species.handle_environment_special(src)
//VOREStation Edit begin: SHADEKIN
if(shadekin_phasing_check())
if(is_incorporeal())
return
//VOREStation Edit end: SHADEKIN
//Moved pressure calculations here for use in skip-processing check.
var/pressure = environment.return_pressure()

View File

@@ -1,5 +1,10 @@
/datum/power/shadekin
/mob/living/carbon/human/is_incorporeal()
if(ability_flags & AB_PHASE_SHIFTED) //Shadekin
return TRUE
return ..()
/////////////////////
/// PHASE SHIFT ///
/////////////////////
@@ -62,6 +67,7 @@
//Shifting in
if(ability_flags & AB_PHASE_SHIFTED)
ability_flags &= ~AB_PHASE_SHIFTED
mouse_opacity = 2
name = real_name
for(var/belly in vore_organs)
var/obj/belly/B = belly
@@ -108,6 +114,7 @@
//Shifting out
else
ability_flags |= AB_PHASE_SHIFTED
mouse_opacity = 0
custom_emote(1,"phases out!")
name = "Something"
@@ -130,47 +137,6 @@
density = FALSE
force_max_speed = TRUE
/mob/living/carbon/human/UnarmedAttack()
if(shadekin_phasing_check())
return FALSE //Nope.
. = ..()
/mob/living/carbon/human/can_fall()
if(shadekin_phasing_check())
return FALSE //Nope!
return ..()
/mob/living/carbon/human/zMove(direction)
if(shadekin_phasing_check())
var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src)
if(destination)
forceMove(destination)
return TRUE //Yup.
return ..()
/mob/proc/shadekin_phasing_check()
var/mob/living/simple_mob/shadekin/s_SK = src
if(istype(s_SK))
if(s_SK.ability_flags & AB_PHASE_SHIFTED)
return TRUE
var/mob/living/carbon/human/h_SK = src
if(istype(h_SK))
if(h_SK.ability_flags & AB_PHASE_SHIFTED)
return TRUE
return FALSE
/*
/mob/living/carbon/human/MouseDrop_T(atom/dropping, mob/user)
if(ability_flags & AB_PHASE_SHIFTED)
return FALSE //Nope!
return ..()
*/
//////////////////////////
/// REGENERATE OTHER ///
//////////////////////////

View File

@@ -109,5 +109,9 @@
if(drowsy) apply_effect(drowsy, DROWSY, blocked)
if(agony) apply_effect(agony, AGONY, blocked)
if(flammable) adjust_fire_stacks(flammable)
if(ignite) IgniteMob()
if(ignite)
if(ignite >= 3)
add_modifier(/datum/modifier/fire/stack_managed/intense, 60 SECONDS)
else
add_modifier(/datum/modifier/fire/stack_managed, 45 * ignite SECONDS)
return 1

View File

@@ -54,13 +54,9 @@
icon_rest = "mouse_[body_color]_sleep"
desc = "A small [body_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself."
/mob/living/simple_mob/animal/passive/mouse/Crossed(AM as mob|obj)
//VOREStation Edit begin: SHADEKIN
var/mob/SK = AM
if(istype(SK))
if(SK.shadekin_phasing_check())
return
//VOREStation Edit end: SHADEKIN
/mob/living/simple_mob/animal/passive/mouse/Crossed(atom/movable/AM as mob|obj)
if(AM.is_incorporeal())
return
if( ishuman(AM) )
if(!stat)
var/mob/M = AM

View File

@@ -19,6 +19,7 @@
//Shifting in
if(ability_flags & AB_PHASE_SHIFTED)
ability_flags &= ~AB_PHASE_SHIFTED
mouse_opacity = 2
name = real_name
for(var/belly in vore_organs)
var/obj/belly/B = belly
@@ -70,6 +71,7 @@
//Shifting out
else
ability_flags |= AB_PHASE_SHIFTED
mouse_opacity = 0
custom_emote(1,"phases out!")
real_name = name
name = "Something"

View File

@@ -245,6 +245,11 @@
if((. = ..()))
handle_shade()
/mob/living/simple_mob/shadekin/is_incorporeal()
if(ability_flags & AB_PHASE_SHIFTED)
return TRUE
return FALSE
/mob/living/simple_mob/shadekin/handle_atmos()
if(ability_flags & AB_PHASE_SHIFTED)
return

View File

@@ -389,10 +389,8 @@
///Return 1 for movement 0 for none
/mob/proc/Process_Spacemove(var/check_drift = 0)
//VOREStation Edit begin: SHADEKIN
if(shadekin_phasing_check())
if(is_incorporeal())
return
//VOREStation Edit end: SHADEKIN
if(!Check_Dense_Object()) //Nothing to push off of so end here
update_floating(0)

View File

@@ -29,6 +29,10 @@
to_chat(src, "<span class='notice'>There is nothing of interest in this direction.</span>")
return 0
if(is_incorporeal())
forceMove(destination)
return 1
if(!start.CanZPass(src, direction))
to_chat(src, "<span class='warning'>\The [start] is in the way.</span>")
return 0
@@ -124,7 +128,7 @@
/mob/living/can_ztravel()
if(incapacitated())
return FALSE
return hovering
return (hovering || is_incorporeal())
/mob/living/carbon/human/can_ztravel()
if(incapacitated())
@@ -273,6 +277,8 @@
return FALSE
/mob/living/can_fall()
if(is_incorporeal())
return FALSE
if(hovering)
return FALSE
return ..()
@@ -582,4 +588,4 @@
// And hurt the floor.
if(istype(hit_atom, /turf/simulated/floor))
var/turf/simulated/floor/ground = hit_atom
ground.break_tile()
ground.break_tile()

View File

@@ -150,6 +150,9 @@
var/turf/below = GetBelow(src)
return !below || below.is_space()
/turf/simulated/open/is_solid_structure()
return locate(/obj/structure/lattice, src) //counts as solid structure if it has a lattice (same as space)
/turf/simulated/open/is_safe_to_enter(mob/living/L)
if(L.can_fall())
if(!locate(/obj/structure/stairs) in GetBelow(src))

View File

@@ -3,6 +3,57 @@
//How far from the edge of overmap zlevel could randomly placed objects spawn
#define OVERMAP_EDGE 7
//Dimension of overmap (squares 4 lyfe)
var/global/list/map_sectors = list()
/area/overmap/
name = "System Map"
icon_state = "start"
requires_power = 0
base_turf = /turf/unsimulated/map
/turf/unsimulated/map
icon = 'icons/turf/space.dmi'
icon_state = "map"
/turf/unsimulated/map/edge
opacity = 1
density = 1
/turf/unsimulated/map/New()
..()
name = "[x]-[y]"
var/list/numbers = list()
if(x == 1 || x == global.using_map.overmap_size)
numbers += list("[round(y/10)]","[round(y%10)]")
if(y == 1 || y == global.using_map.overmap_size)
numbers += "-"
if(y == 1 || y == global.using_map.overmap_size)
numbers += list("[round(x/10)]","[round(x%10)]")
for(var/i = 1 to numbers.len)
var/image/I = image('icons/effects/numbers.dmi',numbers[i])
I.pixel_x = 5*i - 2
I.pixel_y = world.icon_size/2 - 3
if(y == 1)
I.pixel_y = 3
I.pixel_x = 5*i + 4
if(y == global.using_map.overmap_size)
I.pixel_y = world.icon_size - 9
I.pixel_x = 5*i + 4
if(x == 1)
I.pixel_x = 5*i - 2
if(x == global.using_map.overmap_size)
I.pixel_x = 5*i + 2
overlays += I
//list used to track which zlevels are being 'moved' by the proc below
var/list/moving_levels = list()
//Proc to 'move' stars in spess
@@ -35,6 +86,7 @@ proc/toggle_move_stars(zlevel, direction)
AM.throw_at(get_step(T,reverse_direction(direction)), 5, 1)
/*
//list used to cache empty zlevels to avoid nedless map bloat
var/list/cached_space = list()
@@ -99,3 +151,4 @@ proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A)
testing("Catching [M] for future use")
source.loc = null
cached_space += source
*/

View File

@@ -0,0 +1,39 @@
/obj/effect/overmap
name = "map object"
icon = 'icons/obj/overmap.dmi'
icon_state = "object"
var/known = 1 //shows up on nav computers automatically
var/scannable //if set to TRUE will show up on ship sensors for detailed scans
//Overlay of how this object should look on other skyboxes
/obj/effect/overmap/proc/get_skybox_representation()
return
/obj/effect/overmap/proc/get_scan_data(mob/user)
return desc
/obj/effect/overmap/Initialize()
. = ..()
if(!global.using_map.use_overmap)
return INITIALIZE_HINT_QDEL
if(known)
//layer = ABOVE_LIGHTING_LAYER
plane = PLANE_LIGHTING_ABOVE
// TODO - Leshana HELM
// for(var/obj/machinery/computer/ship/helm/H in global.machines)
// H.get_known_sectors()
/*
TODO - Leshana - No need for this, we don't have skyboxes
/obj/effect/overmap/Crossed(var/obj/effect/overmap/visitable/other)
if(istype(other))
for(var/obj/effect/overmap/visitable/O in loc)
SSskybox.rebuild_skyboxes(O.map_z)
/obj/effect/overmap/Uncrossed(var/obj/effect/overmap/visitable/other)
if(istype(other))
SSskybox.rebuild_skyboxes(other.map_z)
for(var/obj/effect/overmap/visitable/O in loc)
SSskybox.rebuild_skyboxes(O.map_z)
*/

View File

@@ -1,124 +1,136 @@
//===================================================================================
//Hook for building overmap
//Overmap object representing zlevel(s)
//===================================================================================
var/global/list/map_sectors = list()
/hook/startup/proc/build_map()
if(!config.use_overmap)
return 1
testing("Building overmap...")
var/obj/effect/mapinfo/data
for(var/level in 1 to world.maxz)
data = locate("sector[level]")
if (data)
testing("Located sector \"[data.name]\" at [data.mapx],[data.mapy] corresponding to zlevel [level]")
map_sectors["[level]"] = new data.obj_type(data)
return 1
//===================================================================================
//Metaobject for storing information about sector this zlevel is representing.
//Should be placed only once on every zlevel.
//===================================================================================
/obj/effect/mapinfo/
name = "map info metaobject"
icon = 'icons/mob/screen1.dmi'
icon_state = "x2"
invisibility = 101
var/obj_type //type of overmap object it spawns
var/landing_area //type of area used as inbound shuttle landing, null if no shuttle landing area
var/zlevel
var/mapx //coordinates on the
var/mapy //overmap zlevel
var/known = 1
/obj/effect/mapinfo/New()
tag = "sector[z]"
zlevel = z
loc = null
/obj/effect/mapinfo/sector
name = "generic sector"
obj_type = /obj/effect/map/sector
/obj/effect/mapinfo/ship
name = "generic ship"
obj_type = /obj/effect/map/ship
//===================================================================================
//Overmap object representing zlevel
//===================================================================================
/obj/effect/map
/obj/effect/overmap/visitable
name = "map object"
icon = 'icons/obj/items.dmi'
icon_state = "sheet-plasteel"
var/map_z = 0
var/area/shuttle/shuttle_landing
var/always_known = 1
scannable = TRUE
/obj/effect/map/New(var/obj/effect/mapinfo/data)
map_z = data.zlevel
name = data.name
always_known = data.known
if (data.icon != 'icons/mob/screen1.dmi')
icon = data.icon
icon_state = data.icon_state
if(data.desc)
desc = data.desc
var/new_x = data.mapx ? data.mapx : rand(OVERMAP_EDGE, world.maxx - OVERMAP_EDGE)
var/new_y = data.mapy ? data.mapy : rand(OVERMAP_EDGE, world.maxy - OVERMAP_EDGE)
loc = locate(new_x, new_y, OVERMAP_ZLEVEL)
var/list/map_z = list()
if(data.landing_area)
shuttle_landing = locate(data.landing_area)
var/list/initial_generic_waypoints //store landmark_tag of landmarks that should be added to the actual lists below on init.
var/list/initial_restricted_waypoints //For use with non-automatic landmarks (automatic ones add themselves).
/obj/effect/map/CanPass(atom/movable/A)
testing("[A] attempts to enter sector\"[name]\"")
return 1
var/list/generic_waypoints = list() //waypoints that any shuttle can use
var/list/restricted_waypoints = list() //waypoints for specific shuttles
var/docking_codes
/obj/effect/map/Crossed(atom/movable/A)
testing("[A] has entered sector\"[name]\"")
if (istype(A,/obj/effect/map/ship))
var/obj/effect/map/ship/S = A
S.current_sector = src
var/start_x //Coordinates for self placing
var/start_y //will use random values if unset
/obj/effect/map/Uncrossed(atom/movable/A)
testing("[A] has left sector\"[name]\"")
if (istype(A,/obj/effect/map/ship))
var/obj/effect/map/ship/S = A
S.current_sector = null
var/base = 0 //starting sector, counts as station_levels
var/in_space = 1 //can be accessed via lucky EVA
/obj/effect/map/sector
var/hide_from_reports = FALSE
var/has_distress_beacon
/obj/effect/overmap/visitable/Initialize()
. = ..()
if(. == INITIALIZE_HINT_QDEL)
return
find_z_levels() // This populates map_z and assigns z levels to the ship.
register_z_levels() // This makes external calls to update global z level information.
if(!global.using_map.overmap_z)
build_overmap()
start_x = start_x || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE)
start_y = start_y || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE)
forceMove(locate(start_x, start_y, global.using_map.overmap_z))
docking_codes = "[ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))]"
testing("Located sector \"[name]\" at [start_x],[start_y], containing Z [english_list(map_z)]")
LAZYADD(SSshuttles.sectors_to_initialize, src) //Queued for further init. Will populate the waypoint lists; waypoints not spawned yet will be added in as they spawn.
SSshuttles.process_init_queues()
//This is called later in the init order by SSshuttles to populate sector objects. Importantly for subtypes, shuttles will be created by then.
/obj/effect/overmap/visitable/proc/populate_sector_objects()
// TODO - Leshana - Implement
///obj/effect/overmap/visitable/proc/get_areas()
// return get_filtered_areas(list(/proc/area_belongs_to_zlevels = map_z))
/obj/effect/overmap/visitable/proc/find_z_levels()
map_z = GetConnectedZlevels(z)
/obj/effect/overmap/visitable/proc/register_z_levels()
for(var/zlevel in map_z)
map_sectors["[zlevel]"] = src
global.using_map.player_levels |= map_z
if(!in_space)
global.using_map.sealed_levels |= map_z
if(base)
global.using_map.station_levels |= map_z
global.using_map.contact_levels |= map_z
global.using_map.map_levels |= map_z
//Helper for init.
/obj/effect/overmap/visitable/proc/check_ownership(obj/object)
if((object.z in map_z) && !(get_area(object) in SSshuttles.shuttle_areas))
return 1
//If shuttle_name is false, will add to generic waypoints; otherwise will add to restricted. Does not do checks.
/obj/effect/overmap/visitable/proc/add_landmark(obj/effect/shuttle_landmark/landmark, shuttle_name)
landmark.sector_set(src, shuttle_name)
if(shuttle_name)
LAZYADD(restricted_waypoints[shuttle_name], landmark)
else
generic_waypoints += landmark
/obj/effect/overmap/visitable/proc/remove_landmark(obj/effect/shuttle_landmark/landmark, shuttle_name)
if(shuttle_name)
var/list/shuttles = restricted_waypoints[shuttle_name]
LAZYREMOVE(shuttles, landmark)
else
generic_waypoints -= landmark
/obj/effect/overmap/visitable/proc/get_waypoints(var/shuttle_name)
. = list()
for(var/obj/effect/overmap/visitable/contained in src)
. += contained.get_waypoints(shuttle_name)
for(var/thing in generic_waypoints)
.[thing] = name
if(shuttle_name in restricted_waypoints)
for(var/thing in restricted_waypoints[shuttle_name])
.[thing] = name
/obj/effect/overmap/visitable/proc/generate_skybox()
return
/obj/effect/overmap/visitable/sector
name = "generic sector"
desc = "Sector with some stuff in it."
icon_state = "sector"
anchored = 1
//Space stragglers go here
// Because of the way these are spawned, they will potentially have their invisibility adjusted by the turfs they are mapped on
// prior to being moved to the overmap. This blocks that. Use set_invisibility to adjust invisibility as needed instead.
/obj/effect/overmap/visitable/sector/hide()
/obj/effect/map/sector/temporary
name = "Deep Space"
icon_state = ""
always_known = 0
/proc/build_overmap()
if(!global.using_map.use_overmap)
return 1
/obj/effect/map/sector/temporary/New(var/nx, var/ny, var/nz)
loc = locate(nx, ny, OVERMAP_ZLEVEL)
map_z = nz
map_sectors["[map_z]"] = src
testing("Temporary sector at [x],[y] was created, corresponding zlevel is [map_z].")
testing("Building overmap...")
world.maxz++
global.using_map.overmap_z = world.maxz
/obj/effect/map/sector/temporary/Destroy()
map_sectors["[map_z]"] = null
testing("Temporary sector at [x],[y] was deleted.")
if (can_die())
testing("Associated zlevel disappeared.")
world.maxz--
testing("Putting overmap on [global.using_map.overmap_z]")
var/area/overmap/A = new
for (var/square in block(locate(1,1,global.using_map.overmap_z), locate(global.using_map.overmap_size,global.using_map.overmap_size,global.using_map.overmap_z)))
var/turf/T = square
if(T.x == global.using_map.overmap_size || T.y == global.using_map.overmap_size)
T = T.ChangeTurf(/turf/unsimulated/map/edge)
else
T = T.ChangeTurf(/turf/unsimulated/map)
ChangeArea(T, A)
/obj/effect/map/sector/temporary/proc/can_die(var/mob/observer)
testing("Checking if sector at [map_z] can die.")
for(var/mob/M in player_list)
if(M != observer && M.z == map_z)
testing("There are people on it.")
return 0
global.using_map.sealed_levels |= global.using_map.overmap_z
testing("Overmap build complete.")
return 1

View File

@@ -0,0 +1,114 @@
//list used to cache empty zlevels to avoid nedless map bloat
var/list/cached_space = list()
//Space stragglers go here
/obj/effect/overmap/visitable/sector/temporary
name = "Deep Space"
invisibility = 101
known = 0
/obj/effect/overmap/visitable/sector/temporary/New(var/nx, var/ny, var/nz)
loc = locate(nx, ny, global.using_map.overmap_z)
x = nx
y = ny
map_z += nz
map_sectors["[nz]"] = src
testing("Temporary sector at [x],[y] was created, corresponding zlevel is [nz].")
/obj/effect/overmap/visitable/sector/temporary/Destroy()
map_sectors["[map_z]"] = null
testing("Temporary sector at [x],[y] was deleted.")
/obj/effect/overmap/visitable/sector/temporary/proc/can_die(var/mob/observer)
testing("Checking if sector at [map_z[1]] can die.")
for(var/mob/M in global.player_list)
if(M != observer && (M.z in map_z))
testing("There are people on it.")
return 0
return 1
proc/get_deepspace(x,y)
var/obj/effect/overmap/visitable/sector/temporary/res = locate(x,y,global.using_map.overmap_z)
if(istype(res))
return res
else if(cached_space.len)
res = cached_space[cached_space.len]
cached_space -= res
res.x = x
res.y = y
return res
else
return new /obj/effect/overmap/visitable/sector/temporary(x, y, global.using_map.get_empty_zlevel())
/atom/movable/proc/lost_in_space()
for(var/atom/movable/AM in contents)
if(!AM.lost_in_space())
return FALSE
return TRUE
/mob/lost_in_space()
return isnull(client)
/mob/living/carbon/human/lost_in_space()
return isnull(client) && !key && stat == DEAD
proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A)
if (!T || !A)
return
var/obj/effect/overmap/visitable/M = map_sectors["[T.z]"]
if (!M)
return
if(A.lost_in_space())
if(!QDELETED(A))
qdel(A)
return
var/nx = 1
var/ny = 1
var/nz = 1
if(T.x <= TRANSITIONEDGE)
nx = world.maxx - TRANSITIONEDGE - 2
ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
else if (A.x >= (world.maxx - TRANSITIONEDGE - 1))
nx = TRANSITIONEDGE + 2
ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
else if (T.y <= TRANSITIONEDGE)
ny = world.maxy - TRANSITIONEDGE -2
nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
else if (A.y >= (world.maxy - TRANSITIONEDGE - 1))
ny = TRANSITIONEDGE + 2
nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
testing("[A] spacemoving from [M] ([M.x], [M.y]).")
var/turf/map = locate(M.x,M.y,global.using_map.overmap_z)
var/obj/effect/overmap/visitable/TM
for(var/obj/effect/overmap/visitable/O in map)
if(O != M && O.in_space && prob(50))
TM = O
break
if(!TM)
TM = get_deepspace(M.x,M.y)
nz = pick(TM.map_z)
var/turf/dest = locate(nx,ny,nz)
if(dest)
A.forceMove(dest)
if(ismob(A))
var/mob/D = A
if(D.pulling)
D.pulling.forceMove(dest)
if(istype(M, /obj/effect/overmap/visitable/sector/temporary))
var/obj/effect/overmap/visitable/sector/temporary/source = M
if (source.can_die())
testing("Caching [M] for future use")
source.forceMove(null)
cached_space += source

View File

@@ -148,8 +148,8 @@
// return a knot cable (O-X) if one is present in the turf
// null if there's none
/turf/proc/get_cable_node()
if(!istype(src, /turf/simulated/floor))
return null
//if(!istype(src, /turf/simulated/floor)) //VOREStation Removal - Why?
//return null //VOREStation Removal - Why?
for(var/obj/structure/cable/C in src)
if(C.d1 == 0)
return C

View File

@@ -94,22 +94,22 @@
return 0
/obj/machinery/power/smes/update_icon()
overlays.Cut()
cut_overlays()
if(stat & BROKEN) return
overlays += image('icons/obj/power.dmi', "smes-op[outputting]")
add_overlay("smes-op[outputting]")
if(inputting == 2)
overlays += image('icons/obj/power.dmi', "smes-oc2")
add_overlay("smes-oc2")
else if (inputting == 1)
overlays += image('icons/obj/power.dmi', "smes-oc1")
add_overlay("smes-oc1")
else
if(input_attempt)
overlays += image('icons/obj/power.dmi', "smes-oc0")
add_overlay("smes-oc0")
var/clevel = chargedisplay()
if(clevel>0)
overlays += image('icons/obj/power.dmi', "smes-og[clevel]")
add_overlay("smes-og[clevel]")
return

View File

@@ -0,0 +1,34 @@
/obj/machinery/power/smes/buildable/hybrid
name = "hybrid power storage unit"
desc = "A high-capacity superconducting magnetic energy storage (SMES) unit, modified with alien technology to generate small amounts of power from seemingly nowhere."
icon = 'icons/obj/power_vr.dmi'
var/recharge_rate = 10000
var/overlay_icon = 'icons/obj/power_vr.dmi'
/obj/machinery/power/smes/buildable/hybrid/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
if(W.is_screwdriver() || W.is_wirecutter())
to_chat(user,"<span class='warning'>\The [src] full of weird alien technology that's best not messed with.</span>")
return 0
/obj/machinery/power/smes/buildable/hybrid/update_icon()
cut_overlays()
if(stat & BROKEN) return
add_overlay("smes-op[outputting]")
if(inputting == 2)
add_overlay("smes-oc2")
else if (inputting == 1)
add_overlay("smes-oc1")
else
if(input_attempt)
add_overlay("smes-oc0")
var/clevel = chargedisplay()
if(clevel>0)
add_overlay("smes-og[clevel]")
return
/obj/machinery/power/smes/buildable/hybrid/process()
charge += min(recharge_rate, capacity - charge)
..()

View File

@@ -104,7 +104,7 @@
var/projectile_type = /obj/item/projectile
var/penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate()
//Effects
var/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 maybe later for burst of fire around the impact point. - Mech
var/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 for intense fire. - Mech
var/flammability = 0 //Amount of fire stacks to add for the above.
var/combustion = TRUE //Does this set off flammable objects on fire/hit?
var/stun = 0
@@ -219,12 +219,8 @@
Range()
/obj/item/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a projectile is hit by it.
//VOREStation Edit begin: SHADEKIN
var/mob/SK = AM
if(istype(SK))
if(SK.shadekin_phasing_check())
return
//VOREStation Edit end: SHADEKIN
if(AM.is_incorporeal())
return
..()
if(isliving(AM) && !(pass_flags & PASSMOB))
var/mob/living/L = AM

View File

@@ -245,7 +245,7 @@
icon_state = "bullet_alt"
damage = 15
damage_type = BURN
incendiary = 1
incendiary = 0.5
flammability = 2
/obj/item/projectile/bullet/incendiary/flamethrower
@@ -261,7 +261,9 @@
vacuum_traversal = 0
/obj/item/projectile/bullet/incendiary/flamethrower/large
damage = 15
damage = 5
incendiary = 3
flammability = 2
range = 6
/* Practice rounds and blanks */

View File

@@ -680,6 +680,46 @@
materials = list(DEFAULT_WALL_MATERIAL = 4000, "osmium" = 3000, "glass" = 1000)
build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/prybar
/datum/design/item/mecha/powercutters
name = "hydraulic cable cutter"
desc = "A large, hydraulic cablecutter."
id = "mech_wirecutter"
req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2)
materials = list(DEFAULT_WALL_MATERIAL = 4000, "osmium" = 3000, "glass" = 1000)
build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/cutter
/datum/design/item/mecha/powerscrewdriver
name = "hydraulic screwdriver"
desc = "A large, hydraulic screwdriver."
id = "mech_screwdriver"
req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2)
materials = list(DEFAULT_WALL_MATERIAL = 4000, "osmium" = 3000, "glass" = 1000)
build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/screwdriver
/datum/design/item/mecha/powerwelder
name = "welding laser"
desc = "A large welding laser."
id = "mech_welder"
req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2)
materials = list(DEFAULT_WALL_MATERIAL = 4000, MAT_PHORON = 3000, "glass" = 1000)
build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/welding
/datum/design/item/mecha/inflatables
name = "inflatables deployer"
desc = "A large pneumatic inflatable deployer."
id = "mech_inflatables"
req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2)
materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_PLASTIC = 4000, "glass" = 1000)
build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/inflatables
/datum/design/item/mecha/hardpoint_clamp
name = "hardpoint actuator"
desc = "A complex device used to commandeer equipment from the ground."
id = "mech_hardpoint_clamp"
req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_POWER = 5, TECH_COMBAT = 2, TECH_MAGNET = 4)
materials = list(MAT_PLASTEEL = 2500, MAT_PLASTIC = 3000, MAT_OSMIUM = 1500, MAT_SILVER = 2000)
build_path = /obj/item/mecha_parts/mecha_equipment/hardpoint_actuator
/datum/design/item/mecha/generator_nuclear
name = "Nuclear Reactor"
desc = "Exosuit-held nuclear reactor. Converts uranium and everyone's health to energy."

View File

@@ -22,9 +22,12 @@
/obj/machinery/computer/roguezones/Initialize()
. = ..()
shuttle_control = locate(/obj/machinery/computer/shuttle_control/belter)
return INITIALIZE_HINT_LATELOAD
/obj/machinery/computer/roguezones/LateInitialize()
if(!rm_controller)
rm_controller = new /datum/controller/rogue()
shuttle_control = locate(/obj/machinery/computer/shuttle_control/belter)
/obj/machinery/computer/roguezones/attack_ai(mob/user as mob)
return attack_hand(user)
@@ -116,8 +119,9 @@
var/datum/rogue/zonemaster/ZM_target = rm_controller.prepare_new_zone()
//Update shuttle destination.
var/datum/shuttle/ferry/S = shuttle_controller.shuttles["Belter"]
S.area_offsite = ZM_target.myshuttle
var/datum/shuttle/autodock/ferry/S = SSshuttles.shuttles["Belter"]
S.landmark_offsite = ZM_target.myshuttle_landmark
S.next_location = S.get_location_waypoint(!S.location)
//Re-enable shuttle.
shuttle_control.shuttle_tag = "Belter"
@@ -145,7 +149,7 @@
if(rm_controller.current_zone && rm_controller.current_zone.is_occupied())
return // Not usable if shuttle is in occupied zone
// Okay do it
var/datum/shuttle/ferry/S = shuttle_controller.shuttles["Belter"]
var/datum/shuttle/autodock/ferry/S = SSshuttles.shuttles["Belter"]
S.launch(usr)
/obj/item/weapon/circuitboard/roguezones

View File

@@ -6,7 +6,8 @@
/datum/rogue/zonemaster
//our area
var/area/asteroid/rogue/myarea
var/area/shuttle/belter/myshuttle
// var/area/shuttle/belter/myshuttle
var/obj/effect/shuttle_landmark/myshuttle_landmark
//world.time
var/prepared_at = 0
@@ -32,7 +33,9 @@
/datum/rogue/zonemaster/New(var/area/A)
ASSERT(A)
myarea = A
myshuttle = locate(myarea.shuttle_area)
myshuttle_landmark = locate(/obj/effect/shuttle_landmark) in myarea
if(!istype(myshuttle_landmark))
warning("Zonemaster cannot find a shuttle landmark in its area '[A]'")
spawn(10) //This is called from controller New() and freaks out if this calls back too fast.
rm_controller.mark_clean(src)
@@ -46,7 +49,7 @@
if(H.stat >= DEAD) //Conditions for exclusion here, like if disconnected people start blocking it.
continue
var/area/A = get_area(H)
if((A == myarea) || (A == myshuttle)) //The loc of a turf is the area it is in.
if(A == myarea) //The loc of a turf is the area it is in.
humans++
return humans
@@ -380,6 +383,7 @@
var/ignored = list(
/obj/asteroid_spawner,
/obj/rogue_mobspawner,
/obj/effect/shuttle_landmark,
/obj/effect/step_trigger/teleporter/roguemine_loop/north,
/obj/effect/step_trigger/teleporter/roguemine_loop/south,
/obj/effect/step_trigger/teleporter/roguemine_loop/east,
@@ -389,6 +393,8 @@
if(I.type == /turf/space)
I.overlays.Cut()
continue
else if(!I.simulated)
continue
else if(I.type in ignored)
continue
qdel(I)
@@ -399,6 +405,8 @@
if(I.type == /turf/space)
I.overlays.Cut()
continue
else if(!I.simulated)
continue
else if(I.type in ignored)
continue
qdel(I)

View File

@@ -1,4 +1,22 @@
#define SHUTTLE_FLAGS_NONE 0
#define SHUTTLE_FLAGS_PROCESS 1
#define SHUTTLE_FLAGS_SUPPLY 2
#define SHUTTLE_FLAGS_ALL (~SHUTTLE_FLAGS_NONE)
// Shuttle flags
#define SHUTTLE_FLAGS_NONE 0
#define SHUTTLE_FLAGS_PROCESS 1 // Should be processed by shuttle subsystem
#define SHUTTLE_FLAGS_SUPPLY 2 // This is the supply shuttle. Why is this a tag?
#define SHUTTLE_FLAGS_ZERO_G 4 // Shuttle has no internal gravity generation
#define SHUTTLE_FLAGS_ALL (~SHUTTLE_FLAGS_NONE)
// shuttle_landmark flags
#define SLANDMARK_FLAG_AUTOSET 1 // If set, will set base area and turf type to same as where it was spawned at
#define SLANDMARK_FLAG_ZERO_G 2 // Zero-G shuttles moved here will lose gravity unless the area has ambient gravity.
// Ferry shuttle location constants
#define FERRY_LOCATION_STATION 0
#define FERRY_LOCATION_OFFSITE 1
#define FERRY_GOING_TO_STATION 0
#define FERRY_GOING_TO_OFFSITE 1
#ifndef DEBUG_SHUTTLES
#define log_shuttle(M)
#else
#define log_shuttle(M) log_debug("[M]")
#endif

View File

@@ -3,30 +3,34 @@
//
/datum/shuttle
var/list/crash_areas = null
var/list/crash_locations = null
var/crash_message = "Oops. The shuttle blew up." // Announcement made when shuttle crashes
/datum/shuttle/New()
if(crash_areas)
for(var/i in 1 to crash_areas.len)
crash_areas[i] = locate(crash_areas[i])
if(crash_locations)
var/crash_location_ids = crash_locations
crash_locations = list()
for(var/location_tag in crash_location_ids)
var/obj/effect/shuttle_landmark/L = SSshuttles.get_landmark(location_tag)
if(L)
crash_locations += L
..()
// Return 0 to let the jump continue, 1 to abort the jump.
// Default implementation checks if the shuttle should crash and if so crashes it.
/datum/shuttle/proc/process_longjump(var/area/origin, var/area/intended_destination, var/direction)
if(should_crash())
do_crash(origin)
/datum/shuttle/proc/process_longjump(var/obj/effect/shuttle_landmark/intended_destination)
if(should_crash(intended_destination))
do_crash(intended_destination)
return 1
// Decide if this is the time we crash. Return true for yes
/datum/shuttle/proc/should_crash(var/area/origin, var/area/intended_destination, var/direction)
/datum/shuttle/proc/should_crash(var/obj/effect/shuttle_landmark/intended_destination)
return FALSE
// Actually crash the shuttle
/datum/shuttle/proc/do_crash(var/area/source)
/datum/shuttle/proc/do_crash(var/obj/effect/shuttle_landmark/intended_destination)
// Choose the target
var/area/target = pick(crash_areas)
var/obj/effect/shuttle_landmark/target = pick(crash_locations)
ASSERT(istype(target))
// Blow up the target area?
@@ -34,16 +38,18 @@
//What people are we dealing with here
var/list/victims = list()
for(var/mob/living/L in source)
victims += L
spawn(0)
shake_camera(L,2 SECONDS,4)
for(var/area/A in shuttle_area)
for(var/mob/living/L in A)
victims += L
spawn(0)
shake_camera(L,2 SECONDS,4)
//SHAKA SHAKA SHAKA
sleep(2 SECONDS)
// Move the shuttle
move(source, target)
if (!attempt_move(target))
return // Lucky!
// Hide people
for(var/living in victims)
@@ -54,9 +60,11 @@
L.loc = null
// Blow up the shuttle
var/list/area_turfs = get_area_turfs(target)
var/turf/epicenter = pick(area_turfs)
var/boomsize = area_turfs.len / 10 // Bigger shuttle = bigger boom
var/list/shuttle_turfs = list()
for(var/area/A in shuttle_area)
shuttle_turfs += get_area_turfs(A)
var/turf/epicenter = pick(shuttle_turfs)
var/boomsize = shuttle_turfs.len / 10 // Bigger shuttle = bigger boom
explosion(epicenter, 0, boomsize, boomsize*2, boomsize*3)
moving_status = SHUTTLE_CRASHED
command_announcement.Announce("[crash_message]", "Shuttle Alert")

View File

@@ -1,50 +1,55 @@
/datum/shuttle/ferry/escape_pod
var/datum/computer/file/embedded_program/docking/simple/escape_pod/arming_controller
category = /datum/shuttle/ferry/escape_pod
/datum/shuttle/autodock/ferry/escape_pod
var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/arming_controller
category = /datum/shuttle/autodock/ferry/escape_pod
/datum/shuttle/ferry/escape_pod/New()
/datum/shuttle/autodock/ferry/escape_pod/New()
move_time = move_time + rand(-30, 60)
if(name in emergency_shuttle.escape_pods)
CRASH("An escape pod with the name '[name]' has already been defined.")
emergency_shuttle.escape_pods[name] = src
..()
/datum/shuttle/ferry/escape_pod/init_docking_controllers()
..()
arming_controller = locate(dock_target_station)
//find the arming controller (berth) - If not configured directly, try to read it from current location landmark
var/arming_controller_tag = arming_controller
if(!arming_controller && active_docking_controller)
arming_controller_tag = active_docking_controller.id_tag
arming_controller = SSshuttles.docking_registry[arming_controller_tag]
if(!istype(arming_controller))
warning("warning: escape pod with station dock tag [dock_target_station] could not find it's dock target!")
CRASH("Could not find arming controller for escape pod \"[name]\", tag was '[arming_controller_tag]'.")
if(docking_controller)
var/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/controller_master = docking_controller.master
if(!istype(controller_master))
warning("warning: escape pod with docking tag [docking_controller_tag] could not find it's controller master!")
else
controller_master.pod = src
//find the pod's own controller
var/datum/computer/file/embedded_program/docking/simple/prog = SSshuttles.docking_registry[docking_controller_tag]
var/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/controller_master = prog.master
if(!istype(controller_master))
CRASH("Escape pod \"[name]\" could not find it's controller master! docking_controller_tag=[docking_controller_tag]")
controller_master.pod = src
/datum/shuttle/ferry/escape_pod/can_launch()
/datum/shuttle/autodock/ferry/escape_pod/can_launch()
if(arming_controller && !arming_controller.armed) //must be armed
return 0
if(location)
return 0 //it's a one-way trip.
return ..()
/datum/shuttle/ferry/escape_pod/can_force()
/datum/shuttle/autodock/ferry/escape_pod/can_force()
if (arming_controller.eject_time && world.time < arming_controller.eject_time + 50)
return 0 //dont allow force launching until 5 seconds after the arming controller has reached it's countdown
return ..()
/datum/shuttle/ferry/escape_pod/can_cancel()
/datum/shuttle/autodock/ferry/escape_pod/can_cancel()
return 0
//This controller goes on the escape pod itself
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod
name = "escape pod controller"
var/datum/shuttle/ferry/escape_pod/pod
program = /datum/computer/file/embedded_program/docking/simple
var/datum/shuttle/autodock/ferry/escape_pod/pod
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/computer/file/embedded_program/docking/simple/docking_program = program // Cast to proper type
data = list(
"docking_status" = docking_program.get_docking_status(),
@@ -64,17 +69,18 @@
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/Topic(href, href_list)
if(..())
return 1
if((. = ..()))
return
if("manual_arm")
pod.arming_controller.arm()
return TOPIC_REFRESH
if("force_launch")
if (pod.can_force())
pod.force_launch(src)
else if (emergency_shuttle.departed && pod.can_launch()) //allow players to manually launch ahead of time if the shuttle leaves
pod.launch(src)
return TOPIC_REFRESH
return 0
@@ -82,18 +88,15 @@
//This controller is for the escape pod berth (station side)
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth
name = "escape pod berth controller"
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/Initialize()
. = ..()
docking_program = new/datum/computer/file/embedded_program/docking/simple/escape_pod(src)
program = docking_program
program = /datum/computer/file/embedded_program/docking/simple/escape_pod_berth
/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/computer/file/embedded_program/docking/simple/docking_program = program // Cast to proper type
var/armed = null
if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod))
var/datum/computer/file/embedded_program/docking/simple/escape_pod/P = docking_program
if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod_berth))
var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/P = docking_program
armed = P.armed
data = list(
@@ -114,44 +117,44 @@
if (!emagged)
to_chat(user, "<span class='notice'>You emag the [src], arming the escape pod!</span>")
emagged = 1
if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod))
var/datum/computer/file/embedded_program/docking/simple/escape_pod/P = docking_program
if (istype(program, /datum/computer/file/embedded_program/docking/simple/escape_pod_berth))
var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/P = program
if (!P.armed)
P.arm()
return 1
//A docking controller program for a simple door based docking port
/datum/computer/file/embedded_program/docking/simple/escape_pod
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth
var/armed = 0
var/eject_delay = 10 //give latecomers some time to get out of the way if they don't make it onto the pod
var/eject_time = null
var/closing = 0
/datum/computer/file/embedded_program/docking/simple/escape_pod/proc/arm()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/proc/arm()
if(!armed)
armed = 1
open_door()
/datum/computer/file/embedded_program/docking/simple/escape_pod/receive_user_command(command)
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/receive_user_command(command)
if (!armed)
return
..(command)
return TRUE // Eat all commands.
return ..(command)
/datum/computer/file/embedded_program/docking/simple/escape_pod/process()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/process()
..()
if (eject_time && world.time >= eject_time && !closing)
close_door()
closing = 1
/datum/computer/file/embedded_program/docking/simple/escape_pod/prepare_for_docking()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/prepare_for_docking()
return
/datum/computer/file/embedded_program/docking/simple/escape_pod/ready_for_docking()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/ready_for_docking()
return 1
/datum/computer/file/embedded_program/docking/simple/escape_pod/finish_docking()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/finish_docking()
return //don't do anything - the doors only open when the pod is armed.
/datum/computer/file/embedded_program/docking/simple/escape_pod/prepare_for_undocking()
/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/prepare_for_undocking()
eject_time = world.time + eject_delay*10

View File

@@ -0,0 +1,187 @@
//making this separate from /obj/effect/landmark until that mess can be dealt with
/obj/effect/shuttle_landmark
name = "Nav Point"
icon = 'icons/effects/effects.dmi'
icon_state = "energynet"
anchored = 1
unacidable = 1
simulated = 0
invisibility = 101
//ID of the landmark
var/landmark_tag
//ID of the controller on the dock side (intialize to id_tag, becomes reference)
var/datum/computer/file/embedded_program/docking/docking_controller
//Map of shuttle names to ID of controller used for this landmark for shuttles with multiple ones.
var/list/special_dock_targets
//When the shuttle leaves this landmark, it will leave behind the base area
//also used to determine if the shuttle can arrive here without obstruction
var/area/base_area
//Will also leave this type of turf behind if set.
var/turf/base_turf
//Name of the shuttle, null for generic waypoint
var/shuttle_restricted
// var/flags = 0 - Already defined on /atom ? Is it being used for anything? Can we reuse it safely?
/obj/effect/shuttle_landmark/Initialize()
. = ..()
if(docking_controller)
. = INITIALIZE_HINT_LATELOAD
if(flags & SLANDMARK_FLAG_AUTOSET)
base_area = get_area(src)
var/turf/T = get_turf(src)
if(T)
base_turf = T.type
else
base_area = locate(base_area || world.area)
name = (name + " ([x],[y])")
SSshuttles.register_landmark(landmark_tag, src)
/obj/effect/shuttle_landmark/LateInitialize()
if(!docking_controller)
return
var/docking_tag = docking_controller
docking_controller = SSshuttles.docking_registry[docking_tag]
if(!istype(docking_controller))
log_error("Could not find docking controller for shuttle waypoint '[name]', docking tag was '[docking_tag]'.")
if(using_map.use_overmap)
var/obj/effect/overmap/visitable/location = map_sectors["[z]"]
if(location && location.docking_codes)
docking_controller.docking_codes = location.docking_codes
/obj/effect/shuttle_landmark/forceMove()
var/obj/effect/overmap/visitable/map_origin = map_sectors["[z]"]
. = ..()
var/obj/effect/overmap/visitable/map_destination = map_sectors["[z]"]
if(map_origin != map_destination)
if(map_origin)
map_origin.remove_landmark(src, shuttle_restricted)
if(map_destination)
map_destination.add_landmark(src, shuttle_restricted)
//Called when the landmark is added to an overmap sector.
/obj/effect/shuttle_landmark/proc/sector_set(var/obj/effect/overmap/visitable/O, shuttle_name)
shuttle_restricted = shuttle_name
/obj/effect/shuttle_landmark/proc/is_valid(var/datum/shuttle/shuttle)
if(shuttle.current_location == src)
return FALSE
for(var/area/A in shuttle.shuttle_area)
var/list/translation = get_turf_translation(get_turf(shuttle.current_location), get_turf(src), A.contents)
if(check_collision(base_area, list_values(translation)))
return FALSE
var/conn = GetConnectedZlevels(z)
for(var/w in (z - shuttle.multiz) to z)
if(!(w in conn))
return FALSE
return TRUE
// This creates a graphical warning to where the shuttle is about to land in approximately five seconds.
/obj/effect/shuttle_landmark/proc/create_warning_effect(var/datum/shuttle/shuttle)
if(shuttle.current_location == src)
return // TOO LATE!
for(var/area/A in shuttle.shuttle_area)
var/list/translation = get_turf_translation(get_turf(shuttle.current_location), get_turf(src), A.contents)
for(var/T in list_values(translation))
new /obj/effect/temporary_effect/shuttle_landing(T) // It'll delete itself when needed.
return
// Should return a readable description of why not if it can't depart.
/obj/effect/shuttle_landmark/proc/cannot_depart(datum/shuttle/shuttle)
return FALSE
/obj/effect/shuttle_landmark/proc/shuttle_departed(datum/shuttle/shuttle)
return
/obj/effect/shuttle_landmark/proc/shuttle_arrived(datum/shuttle/shuttle)
return
/proc/check_collision(area/target_area, list/target_turfs)
for(var/target_turf in target_turfs)
var/turf/target = target_turf
if(!target)
return TRUE //collides with edge of map
if(target.loc != target_area)
return TRUE //collides with another area
if(target.density)
return TRUE //dense turf
return FALSE
//
//Self-naming/numbering ones.
//
/obj/effect/shuttle_landmark/automatic
name = "Navpoint"
landmark_tag = "navpoint"
flags = SLANDMARK_FLAG_AUTOSET
/obj/effect/shuttle_landmark/automatic/Initialize()
landmark_tag += "-[x]-[y]-[z]-[random_id("landmarks",1,9999)]"
return ..()
/obj/effect/shuttle_landmark/automatic/sector_set(var/obj/effect/overmap/visitable/O)
..()
name = ("[O.name] - [initial(name)] ([x],[y])")
//Subtype that calls explosion on init to clear space for shuttles
/obj/effect/shuttle_landmark/automatic/clearing
var/radius = 10
/obj/effect/shuttle_landmark/automatic/clearing/Initialize()
..()
return INITIALIZE_HINT_LATELOAD
/obj/effect/shuttle_landmark/automatic/clearing/LateInitialize()
..()
for(var/turf/T in range(radius, src))
if(T.density)
T.ChangeTurf(get_base_turf_by_area(T))
// Subtype that also queues a shuttle datum (for shuttles starting on maps loaded at runtime)
/obj/effect/shuttle_landmark/shuttle_initializer
var/datum/shuttle/shuttle_type
/obj/effect/shuttle_landmark/shuttle_initializer/Initialize()
. = ..()
LAZYADD(SSshuttles.shuttles_to_initialize, shuttle_type) // queue up for init.
//
// Bluespace flare landmark beacon
//
/obj/item/device/spaceflare
name = "bluespace flare"
desc = "Burst transmitter used to broadcast all needed information for shuttle navigation systems. Has a flare attached for marking the spot where you probably shouldn't be standing."
icon_state = "bluflare"
light_color = "#3728ff"
var/active
/obj/item/device/spaceflare/attack_self(var/mob/user)
if(!active)
visible_message("<span class='notice'>[user] pulls the cord, activating the [src].</span>")
activate()
/obj/item/device/spaceflare/proc/activate()
if(active)
return
var/turf/T = get_turf(src)
var/mob/M = loc
if(istype(M) && !M.unEquip(src, T))
return
active = 1
anchored = 1
var/obj/effect/shuttle_landmark/automatic/mark = new(T)
mark.name = ("Beacon signal ([T.x],[T.y])")
T.hotspot_expose(1500, 5)
update_icon()
/obj/item/device/spaceflare/update_icon()
. = ..()
if(active)
icon_state = "bluflare_on"
set_light(0.3, 0.1, 6, 2, "85d1ff")

View File

@@ -1,6 +1,3 @@
//These lists are populated in /datum/controller/subsystem/shuttles/proc/setup_shuttle_docks()
//Shuttle subsystem is instantiated in shuttles.dm.
//shuttle moving state defines are in setup.dm
/datum/shuttle
@@ -8,48 +5,78 @@
var/warmup_time = 0
var/moving_status = SHUTTLE_IDLE
var/docking_controller_tag //tag of the controller used to coordinate docking
var/datum/computer/file/embedded_program/docking/docking_controller //the controller itself. (micro-controller, not game controller)
var/list/shuttle_area // Initial value can be either a single area type or a list of area types
var/obj/effect/shuttle_landmark/current_location //This variable is type-abused initially: specify the landmark_tag, not the actual landmark.
var/arrive_time = 0 //the time at which the shuttle arrives when long jumping
var/depart_time = 0 //Similar to above, set when the shuttle leaves when long jumping, to compare against arrive time.
var/flags = SHUTTLE_FLAGS_PROCESS
var/tmp/arrive_time = 0 //the time at which the shuttle arrives when long jumping
var/flags = SHUTTLE_FLAGS_NONE
var/process_state = IDLE_STATE // Used with SHUTTLE_FLAGS_PROCESS, as well as to store current state.
var/category = /datum/shuttle
var/multiz = 0 //how many multiz levels, starts at 0 TODO Leshana - Are we porting this?
var/ceiling_type = /turf/unsimulated/floor/shuttle_ceiling
var/ceiling_type // Type path of turf to roof over the shuttle when at multi-z landmarks. Ignored if null.
/datum/shuttle/New()
var/sound_takeoff = 'sound/effects/shuttles/shuttle_takeoff.ogg'
var/sound_landing = 'sound/effects/shuttles/shuttle_landing.ogg'
var/knockdown = 1 //whether shuttle downs non-buckled people when it moves
var/defer_initialisation = FALSE //If this this shuttle should be initialised automatically.
//If set to true, you are responsible for initialzing the shuttle manually.
//Useful for shuttles that are initialized by map_template loading, or shuttles that are created in-game or not used.
var/mothershuttle //tag of mothershuttle
var/motherdock //tag of mothershuttle landmark, defaults to starting location
var/tmp/depart_time = 0 //Similar to above, set when the shuttle leaves when long jumping. Used for progress bars.
// Future Thoughts: Baystation put "docking" stuff in a subtype, leaving base type pure and free of docking stuff. Is this best?
/datum/shuttle/New(_name, var/obj/effect/shuttle_landmark/initial_location)
..()
if(src.name in shuttle_controller.shuttles)
if(_name)
src.name = _name
var/list/areas = list()
if(!islist(shuttle_area))
shuttle_area = list(shuttle_area)
for(var/T in shuttle_area)
var/area/A = locate(T)
if(!istype(A))
CRASH("Shuttle \"[name]\" couldn't locate area [T].")
areas += A
shuttle_area = areas
if(initial_location)
current_location = initial_location
else
current_location = SSshuttles.get_landmark(current_location)
if(!istype(current_location))
log_debug("UM whoops, no initial? [src]")
CRASH("Shuttle '[name]' could not find its starting location landmark [current_location].")
if(src.name in SSshuttles.shuttles)
CRASH("A shuttle with the name '[name]' is already defined.")
shuttle_controller.shuttles[src.name] = src
SSshuttles.shuttles[src.name] = src
if(flags & SHUTTLE_FLAGS_PROCESS)
shuttle_controller.process_shuttles += src
SSshuttles.process_shuttles += src
if(flags & SHUTTLE_FLAGS_SUPPLY)
if(supply_controller.shuttle)
CRASH("A supply shuttle is already defined.")
supply_controller.shuttle = src
/datum/shuttle/Destroy()
shuttle_controller.shuttles -= src.name
shuttle_controller.process_shuttles -= src
current_location = null
SSshuttles.shuttles -= src.name
SSshuttles.process_shuttles -= src
SSshuttles.shuttle_logs -= src
if(supply_controller.shuttle == src)
supply_controller.shuttle = null
. = ..()
/datum/shuttle/process()
return
/datum/shuttle/proc/init_docking_controllers()
if(docking_controller_tag)
docking_controller = locate(docking_controller_tag)
if(!istype(docking_controller))
to_world("<span class='danger'>warning: shuttle with docking tag [docking_controller_tag] could not find it's controller!</span>")
// This creates a graphical warning to where the shuttle is about to land, in approximately five seconds.
/datum/shuttle/proc/create_warning_effect(area/landing_area)
for(var/turf/T in landing_area)
new /obj/effect/temporary_effect/shuttle_landing(T) // It'll delete itself when needed.
/datum/shuttle/proc/create_warning_effect(var/obj/effect/shuttle_landmark/destination)
destination.create_warning_effect(src)
// Return false to abort a jump, before the 'warmup' phase.
/datum/shuttle/proc/pre_warmup_checks()
@@ -60,197 +87,274 @@
return TRUE
// If you need an event to occur when the shuttle jumps in short or long jump, override this.
/datum/shuttle/proc/on_shuttle_departure(var/area/origin)
origin.shuttle_departed()
// Keep in mind that destination is the intended destination, the shuttle may or may not actually reach it.s
/datum/shuttle/proc/on_shuttle_departure(var/obj/effect/shuttle_landmark/origin, var/obj/effect/shuttle_landmark/destination)
return
// Similar to above, but when it finishes moving to the target. Short jump generally makes this occur immediately after the above proc.
/datum/shuttle/proc/on_shuttle_arrival(var/area/destination)
destination.shuttle_arrived()
// Keep in mind we might not actually have gotten to destination. Check current_location to be sure where we ended up.
/datum/shuttle/proc/on_shuttle_arrival(var/obj/effect/shuttle_landmark/origin, var/obj/effect/shuttle_landmark/destination)
return
/datum/shuttle/proc/short_jump(var/area/origin,var/area/destination)
/datum/shuttle/proc/short_jump(var/obj/effect/shuttle_landmark/destination)
if(moving_status != SHUTTLE_IDLE)
return
if(!pre_warmup_checks())
return
var/obj/effect/shuttle_landmark/start_location = current_location
// TODO - Figure out exactly when to play sounds. Before warmup_time delay? Should there be a sleep for waiting for sounds? or no?
moving_status = SHUTTLE_WARMUP
spawn(warmup_time*10)
make_sounds(origin, HYPERSPACE_WARMUP)
make_sounds(HYPERSPACE_WARMUP)
create_warning_effect(destination)
sleep(5 SECONDS) // so the sound finishes.
if(!post_warmup_checks())
moving_status = SHUTTLE_IDLE
cancel_launch(null)
if(!fuel_check()) //fuel error (probably out of fuel) occured, so cancel the launch
cancel_launch(null)
if (moving_status == SHUTTLE_IDLE)
make_sounds(origin, HYPERSPACE_END)
make_sounds(HYPERSPACE_END)
return //someone cancelled the launch
on_shuttle_departure(origin)
moving_status = SHUTTLE_INTRANSIT //shouldn't matter but just to be safe
move(origin, destination)
on_shuttle_departure(start_location, destination)
attempt_move(destination)
moving_status = SHUTTLE_IDLE
on_shuttle_arrival(start_location, destination)
on_shuttle_arrival(destination)
make_sounds(HYPERSPACE_END)
make_sounds(destination, HYPERSPACE_END)
/datum/shuttle/proc/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction)
//to_world("shuttle/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]")
// TODO - Far Future - Would be great if this was driven by process too.
/datum/shuttle/proc/long_jump(var/obj/effect/shuttle_landmark/destination, var/obj/effect/shuttle_landmark/interim, var/travel_time)
//to_world("shuttle/long_jump: current_location=[current_location], destination=[destination], interim=[interim], travel_time=[travel_time]")
if(moving_status != SHUTTLE_IDLE)
return
if(!pre_warmup_checks())
return
//it would be cool to play a sound here
var/obj/effect/shuttle_landmark/start_location = current_location
// TODO - Figure out exactly when to play sounds. Before warmup_time delay? Should there be a sleep for waiting for sounds? or no?
moving_status = SHUTTLE_WARMUP
spawn(warmup_time*10)
make_sounds(departing, HYPERSPACE_WARMUP)
make_sounds(HYPERSPACE_WARMUP)
create_warning_effect(interim) // Really doubt someone is gonna get crushed in the interim area but for completeness's sake we'll make the warning.
sleep(5 SECONDS) // so the sound finishes.
if(!post_warmup_checks())
moving_status = SHUTTLE_IDLE
cancel_launch(null)
if (moving_status == SHUTTLE_IDLE)
make_sounds(departing, HYPERSPACE_END)
make_sounds(HYPERSPACE_END)
return //someone cancelled the launch
arrive_time = world.time + travel_time*10
depart_time = world.time
moving_status = SHUTTLE_INTRANSIT
on_shuttle_departure(start_location, destination)
on_shuttle_departure(departing)
if(attempt_move(interim, TRUE))
interim.shuttle_arrived()
move(departing, interim, direction)
interim.shuttle_arrived()
if(process_longjump(current_location, destination)) //VOREStation Edit - To hook custom shuttle code in
return //VOREStation Edit - It handled it for us (shuttle crash or such)
if(process_longjump(departing, destination)) //VOREStation Edit - To hook custom shuttle code in
return //VOREStation Edit - It handled it for us (shuttle crash or such)
var/last_progress_sound = 0
var/made_warning = FALSE
while (world.time < arrive_time)
// Make the shuttle make sounds every four seconds, since the sound file is five seconds.
if(last_progress_sound + 4 SECONDS < world.time)
make_sounds(HYPERSPACE_PROGRESS)
last_progress_sound = world.time
var/last_progress_sound = 0
var/made_warning = FALSE
while (world.time < arrive_time)
// Make the shuttle make sounds every four seconds, since the sound file is five seconds.
if(last_progress_sound + 4 SECONDS < world.time)
make_sounds(interim, HYPERSPACE_PROGRESS)
last_progress_sound = world.time
if(arrive_time - world.time <= 5 SECONDS && !made_warning)
made_warning = TRUE
create_warning_effect(destination)
sleep(5)
if(arrive_time - world.time <= 5 SECONDS && !made_warning)
made_warning = TRUE
create_warning_effect(destination)
sleep(5)
if(!attempt_move(destination))
attempt_move(start_location) //try to go back to where we started. If that fails, I guess we're stuck in the interim location
interim.shuttle_departed()
move(interim, destination, direction)
moving_status = SHUTTLE_IDLE
on_shuttle_arrival(start_location, destination)
make_sounds(HYPERSPACE_END)
on_shuttle_arrival(destination)
make_sounds(destination, HYPERSPACE_END)
//////////////////////////////
// Forward declarations of public procs. They do nothing because this is not auto-dock.
/datum/shuttle/proc/fuel_check()
return 1 //fuel check should always pass in non-overmap shuttles (they have magic engines)
/datum/shuttle/proc/cancel_launch(var/user)
// If we are past warming up its too late to cancel.
if (moving_status == SHUTTLE_WARMUP)
moving_status = SHUTTLE_IDLE
/*
Docking stuff
*/
/datum/shuttle/proc/dock()
if (!docking_controller)
return
var/dock_target = current_dock_target()
if (!dock_target)
return
docking_controller.initiate_docking(dock_target)
return
/datum/shuttle/proc/undock()
if (!docking_controller)
return
docking_controller.initiate_undocking()
return
/datum/shuttle/proc/current_dock_target()
return null
/datum/shuttle/proc/force_undock()
return
/datum/shuttle/proc/skip_docking_checks()
if (!docking_controller || !current_dock_target())
return 1 //shuttles without docking controllers or at locations without docking ports act like old-style shuttles
return 0
// Check if we are docked (or never dock) and thus have properly arrived.
/datum/shuttle/proc/check_docked()
return TRUE
//just moves the shuttle from A to B, if it can be moved
//A note to anyone overriding move in a subtype. move() must absolutely not, under any circumstances, fail to move the shuttle.
// Check if we are undocked and thus probably ready to depart.
/datum/shuttle/proc/check_undocked()
return TRUE
/*****************
* Shuttle Moved Handling * (Observer Pattern Implementation: Shuttle Moved)
* Shuttle Pre Move Handling * (Observer Pattern Implementation: Shuttle Pre Move)
*****************/
// Move the shuttle to destination if possible.
// Returns TRUE if we actually moved, otherwise FALSE.
/datum/shuttle/proc/attempt_move(var/obj/effect/shuttle_landmark/destination, var/interim = FALSE)
if(current_location == destination)
log_shuttle("Shuttle [src] attempted to move to [destination] but is already there!")
return FALSE
if(!destination.is_valid(src))
log_shuttle("Shuttle [src] aborting attempt_move() because destination=[destination] is not valid")
return FALSE
if(current_location.cannot_depart(src))
log_shuttle("Shuttle [src] aborting attempt_move() because current_location=[current_location] refuses.")
return FALSE
log_shuttle("[src] moving to [destination]. Areas are [english_list(shuttle_area)]")
var/list/translation = list()
for(var/area/A in shuttle_area)
log_shuttle("Translating [A]")
translation += get_turf_translation(get_turf(current_location), get_turf(destination), A.contents)
var/old_location = current_location
// Observer pattern pre-move
GLOB.shuttle_pre_move_event.raise_event(src, old_location, destination)
current_location.shuttle_departed(src)
// Actually do it! (This never fails)
perform_shuttle_move(destination, translation)
// Observer pattern post-move
destination.shuttle_arrived(src)
GLOB.shuttle_moved_event.raise_event(src, old_location, destination)
return TRUE
//just moves the shuttle from A to B
//A note to anyone overriding move in a subtype. perform_shuttle_move() must absolutely not, under any circumstances, fail to move the shuttle.
//If you want to conditionally cancel shuttle launches, that logic must go in short_jump() or long_jump()
/datum/shuttle/proc/move(var/area/origin, var/area/destination, var/direction=null)
/datum/shuttle/proc/perform_shuttle_move(var/obj/effect/shuttle_landmark/destination, var/list/turf_translation)
log_shuttle("perform_shuttle_move() current=[current_location] destination=[destination]")
//to_world("move_shuttle() called for [name] leaving [origin] en route to [destination].")
//to_world("area_coming_from: [origin]")
//to_world("destination: [destination]")
ASSERT(current_location != destination)
if(origin == destination)
//to_world("cancelling move, shuttle will overlap.")
return
// If shuttle has no internal gravity, update our gravity with destination gravity
if((flags & SHUTTLE_FLAGS_ZERO_G))
var/new_grav = 1
if(destination.flags & SLANDMARK_FLAG_ZERO_G)
var/area/new_area = get_area(destination)
new_grav = new_area.has_gravity
for(var/area/our_area in shuttle_area)
if(our_area.has_gravity != new_grav)
our_area.gravitychange(new_grav)
if (docking_controller && !docking_controller.undocked())
docking_controller.force_undock()
// TODO - Old code used to throw stuff out of the way instead of squashing. Should we?
var/list/dstturfs = list()
var/throwy = world.maxy
for(var/turf/T in destination)
dstturfs += T
if(T.y < throwy)
throwy = T.y
for(var/turf/T in dstturfs)
var/turf/D = locate(T.x, throwy - 1, T.z)
for(var/atom/movable/AM as mob|obj in T)
AM.Move(D)
for(var/mob/living/carbon/bug in destination)
bug.gib()
for(var/mob/living/simple_mob/pest in destination)
pest.gib()
origin.move_contents_to(destination, direction=direction)
for(var/mob/M in destination)
if(M.client)
spawn(0)
if(M.buckled)
to_chat(M, "<font color='red'>Sudden acceleration presses you into \the [M.buckled]!</font>")
shake_camera(M, 3, 1)
// Move, gib, or delete everything in our way!
for(var/turf/src_turf in turf_translation)
var/turf/dst_turf = turf_translation[src_turf]
if(src_turf.is_solid_structure()) // in case someone put a hole in the shuttle and you were lucky enough to be under it
for(var/atom/movable/AM in dst_turf)
//if(AM.movable_flags & MOVABLE_FLAG_DEL_SHUTTLE)
// qdel(AM)
// continue
if(!AM.simulated)
continue
if(isliving(AM))
var/mob/living/bug = AM
bug.gib()
else
to_chat(M, "<font color='red'>The floor lurches beneath you!</font>")
shake_camera(M, 10, 1)
if(istype(M, /mob/living/carbon))
if(!M.buckled)
M.Weaken(3)
qdel(AM) //it just gets atomized I guess? TODO throw it into space somewhere, prevents people from using shuttles as an atom-smasher
var/list/powernets = list()
for(var/area/A in shuttle_area)
// If there was a zlevel above our origin and we own the ceiling, erase our ceiling now we're leaving
if(ceiling_type && HasAbove(current_location.z))
for(var/turf/TO in A.contents)
var/turf/TA = GetAbove(TO)
if(istype(TA, ceiling_type))
TA.ChangeTurf(get_base_turf_by_area(TA), 1, 1)
if(knockdown)
for(var/mob/living/M in A)
spawn(0)
if(M.buckled)
to_chat(M, "<font color='red'>Sudden acceleration presses you into \the [M.buckled]!</font>")
shake_camera(M, 3, 1)
else
to_chat(M, "<font color='red'>The floor lurches beneath you!</font>")
shake_camera(M, 10, 1)
// TODO - tossing?
//M.visible_message("<span class='warning'>[M.name] is tossed around by the sudden acceleration!</span>")
//M.throw_at_random(FALSE, 4, 1)
if(istype(M, /mob/living/carbon))
M.Weaken(3)
// We only need to rebuild powernets for our cables. No need to check machines because they are on top of cables.
for(var/obj/structure/cable/C in A)
powernets |= C.powernet
// Actually do the movement of everything - This replaces origin.move_contents_to(destination)
translate_turfs(turf_translation, current_location.base_area, current_location.base_turf)
current_location = destination
// If there's a zlevel above our destination, paint in a ceiling on it so we retain our air
if(ceiling_type && HasAbove(current_location.z))
for(var/area/A in shuttle_area)
for(var/turf/TD in A.contents)
var/turf/TA = GetAbove(TD)
if(istype(TA, get_base_turf_by_area(TA)) || isopenspace(TA))
if(get_area(TA) in shuttle_area)
continue
TA.ChangeTurf(ceiling_type, TRUE, TRUE, TRUE)
// Power-related checks. If shuttle contains power related machinery, update powernets.
var/update_power = 0
for(var/obj/machinery/power/P in destination)
update_power = 1
break
// Note: Old way was to rebuild ALL powernets: if(powernets.len) SSmachines.makepowernets()
// New way only rebuilds the powernets we have to
var/list/cables = list()
for(var/datum/powernet/P in powernets)
cables |= P.cables
qdel(P)
SSmachines.setup_powernets_for_cables(cables)
for(var/obj/structure/cable/C in destination)
update_power = 1
break
if(update_power)
SSmachines.makepowernets()
return
//returns 1 if the shuttle has a valid arrive time
/datum/shuttle/proc/has_arrive_time()
return (moving_status == SHUTTLE_INTRANSIT)
/datum/shuttle/proc/make_sounds(var/area/A, var/sound_type)
/datum/shuttle/proc/make_sounds(var/sound_type)
var/sound_to_play = null
switch(sound_type)
if(HYPERSPACE_WARMUP)
@@ -259,9 +363,29 @@
sound_to_play = 'sound/effects/shuttles/hyperspace_progress.ogg'
if(HYPERSPACE_END)
sound_to_play = 'sound/effects/shuttles/hyperspace_end.ogg'
for(var/obj/machinery/door/E in A) //dumb, I know, but playing it on the engines doesn't do it justice
playsound(E, sound_to_play, 50, FALSE)
for(var/area/A in shuttle_area)
for(var/obj/machinery/door/E in A) //dumb, I know, but playing it on the engines doesn't do it justice
playsound(E, sound_to_play, 50, FALSE)
/datum/shuttle/proc/message_passengers(area/A, var/message)
for(var/mob/M in A)
M.show_message(message, 2)
/datum/shuttle/proc/message_passengers(var/message)
for(var/area/A in shuttle_area)
for(var/mob/M in A)
M.show_message(message, 2)
/datum/shuttle/proc/find_children()
. = list()
for(var/shuttle_name in SSshuttles.shuttles)
var/datum/shuttle/shuttle = SSshuttles.shuttles[shuttle_name]
if(shuttle.mothershuttle == name)
. += shuttle
//Returns the areas in shuttle_area that are not actually child shuttles.
/datum/shuttle/proc/find_childfree_areas()
. = shuttle_area.Copy()
for(var/datum/shuttle/child in find_children())
. -= child.shuttle_area
/datum/shuttle/proc/get_location_name()
if(moving_status == SHUTTLE_INTRANSIT)
return "In transit"
return current_location.name

View File

@@ -0,0 +1,220 @@
#define DOCK_ATTEMPT_TIMEOUT 200 //how long in ticks we wait before assuming the docking controller is broken or blown up.
// Subtype of shuttle that handles docking with docking controllers
// Consists of code pulled down from the old /datum/shuttle and up from /datum/shuttle/ferry
// Note: Since all known shuttles extend this type, this really could just be built into /datum/shuttle
// Why isn't it you ask? Eh, baystation did it this way and its convenient to keep the files smaller I guess.
/datum/shuttle/autodock
var/in_use = null // Tells the controller whether this shuttle needs processing, also attempts to prevent double-use
var/last_dock_attempt_time = 0
var/docking_controller_tag = null // ID of the controller on the shuttle (If multiple, this is the default one)
var/datum/computer/file/embedded_program/docking/shuttle_docking_controller // Controller on the shuttle (the one in use)
var/docking_codes
var/tmp/obj/effect/shuttle_landmark/next_location //This is only used internally.
var/datum/computer/file/embedded_program/docking/active_docking_controller // Controller we are docked with (or trying to)
var/obj/effect/shuttle_landmark/landmark_transition //This variable is type-abused initially: specify the landmark_tag, not the actual landmark.
var/move_time = 240 //the time spent in the transition area
category = /datum/shuttle/autodock
flags = SHUTTLE_FLAGS_PROCESS | SHUTTLE_FLAGS_ZERO_G
/datum/shuttle/autodock/New(var/_name, var/obj/effect/shuttle_landmark/start_waypoint)
..(_name, start_waypoint)
//Initial dock
active_docking_controller = current_location.docking_controller
update_docking_target(current_location)
if(active_docking_controller)
set_docking_codes(active_docking_controller.docking_codes)
else if(global.using_map.use_overmap)
var/obj/effect/overmap/visitable/location = map_sectors["[current_location.z]"]
if(location && location.docking_codes)
set_docking_codes(location.docking_codes)
dock()
//Optional transition area
if(landmark_transition)
landmark_transition = SSshuttles.get_landmark(landmark_transition)
/datum/shuttle/autodock/Destroy()
in_use = null
next_location = null
active_docking_controller = null
landmark_transition = null
return ..()
/datum/shuttle/autodock/proc/set_docking_codes(var/code)
docking_codes = code
if(shuttle_docking_controller)
shuttle_docking_controller.docking_codes = code
/datum/shuttle/autodock/perform_shuttle_move()
force_undock() //bye!
..()
// Despite the name this actually updates the SHUTTLE docking conroller, not the active.
/datum/shuttle/autodock/proc/update_docking_target(var/obj/effect/shuttle_landmark/location)
var/current_dock_target
if(location && location.special_dock_targets && location.special_dock_targets[name])
current_dock_target = location.special_dock_targets[name]
else
current_dock_target = docking_controller_tag
shuttle_docking_controller = SSshuttles.docking_registry[current_dock_target]
if(current_dock_target && !shuttle_docking_controller)
to_world("<span class='danger'>warning: shuttle [src] can't find its controller with tag [current_dock_target]!</span>")
/*
Docking stuff
*/
/datum/shuttle/autodock/dock()
if(active_docking_controller && shuttle_docking_controller)
shuttle_docking_controller.initiate_docking(active_docking_controller.id_tag)
last_dock_attempt_time = world.time
/datum/shuttle/autodock/undock()
if(shuttle_docking_controller)
shuttle_docking_controller.initiate_undocking()
/datum/shuttle/autodock/force_undock()
if(shuttle_docking_controller)
shuttle_docking_controller.force_undock()
/datum/shuttle/autodock/check_docked()
if(shuttle_docking_controller)
return shuttle_docking_controller.docked()
return TRUE
/datum/shuttle/autodock/check_undocked()
if(shuttle_docking_controller)
return shuttle_docking_controller.can_launch()
return TRUE
// You also could just directly reference active_docking_controller
/datum/shuttle/autodock/proc/current_dock_target()
if(active_docking_controller)
return active_docking_controller.id_tag
return null
// These checks are built into the check_docked() and check_undocked() procs
/datum/shuttle/autodock/proc/skip_docking_checks()
if (!shuttle_docking_controller || !current_dock_target())
return TRUE //shuttles without docking controllers or at locations without docking ports act like old-style shuttles
return FALSE
/*
Please ensure that long_jump() and short_jump() are only called from here. This applies to subtypes as well.
Doing so will ensure that multiple jumps cannot be initiated in parallel.
*/
/datum/shuttle/autodock/process()
switch(process_state)
if (WAIT_LAUNCH)
if(check_undocked())
//*** ready to go
process_launch()
if (FORCE_LAUNCH)
process_launch()
if (WAIT_ARRIVE)
if (moving_status == SHUTTLE_IDLE)
//*** we made it to the destination, update stuff
process_arrived()
process_state = WAIT_FINISH
if (WAIT_FINISH)
if (world.time > last_dock_attempt_time + DOCK_ATTEMPT_TIMEOUT || check_docked())
//*** all done here
process_state = IDLE_STATE
arrived()
//not to be confused with the arrived() proc
/datum/shuttle/autodock/proc/process_arrived()
active_docking_controller = next_location.docking_controller
update_docking_target(next_location)
dock()
next_location = null
in_use = null //release lock
/datum/shuttle/autodock/proc/get_travel_time()
return move_time
/datum/shuttle/autodock/proc/process_launch()
if(!next_location || !next_location.is_valid(src) || current_location.cannot_depart(src))
process_state = IDLE_STATE
in_use = null
return
if (get_travel_time() && landmark_transition)
. = long_jump(next_location, landmark_transition, get_travel_time())
else
. = short_jump(next_location)
process_state = WAIT_ARRIVE
/*
Guards - (These don't take docking status into account, just the state machine and move safety)
*/
/datum/shuttle/autodock/proc/can_launch()
return (next_location && next_location.is_valid(src) && !current_location.cannot_depart(src) && moving_status == SHUTTLE_IDLE && !in_use)
/datum/shuttle/autodock/proc/can_force()
return (next_location && next_location.is_valid(src) && !current_location.cannot_depart(src) && moving_status == SHUTTLE_IDLE && process_state == WAIT_LAUNCH)
/datum/shuttle/autodock/proc/can_cancel()
return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH)
/*
"Public" procs
*/
// Queue shuttle for undock and launch by shuttle subsystem.
/datum/shuttle/autodock/proc/launch(var/user)
if (!can_launch()) return
in_use = user //obtain an exclusive lock on the shuttle
process_state = WAIT_LAUNCH
undock()
// Queue shuttle for forced undock and launch by shuttle subsystem.
/datum/shuttle/autodock/proc/force_launch(var/user)
if (!can_force()) return
in_use = user //obtain an exclusive lock on the shuttle
process_state = FORCE_LAUNCH
// Cancel queued launch.
/datum/shuttle/autodock/cancel_launch(var/user)
if (!can_cancel()) return
moving_status = SHUTTLE_IDLE
process_state = WAIT_FINISH
in_use = null
//whatever we were doing with docking: stop it, then redock
force_undock()
spawn(1 SECOND)
dock()
//returns 1 if the shuttle is getting ready to move, but is not in transit yet
/datum/shuttle/autodock/proc/is_launching()
return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH)
// /datum/shuttle/autodock/get_location_name() defined in shuttle.dm
/datum/shuttle/autodock/proc/get_destination_name()
if(!next_location)
return "None"
return next_location.name
//This gets called when the shuttle finishes arriving at it's destination
//This can be used by subtypes to do things when the shuttle arrives.
//Note that this is called when the shuttle leaves the WAIT_FINISHED state, the proc name is a little misleading
/datum/shuttle/autodock/proc/arrived()
return //do nothing for now
/obj/effect/shuttle_landmark/transit
flags = SLANDMARK_FLAG_ZERO_G

View File

@@ -8,23 +8,20 @@
var/shuttle_tag // Used to coordinate data in shuttle controller.
var/hacked = 0 // Has been emagged, no access restrictions.
var/ui_template = "shuttle_control_console.tmpl"
/obj/machinery/computer/shuttle_control/attack_hand(user as mob)
if(..(user))
return
//src.add_fingerprint(user) //shouldn't need fingerprints just for looking at it.
if(!allowed(user))
to_chat(user, "<font color='red'>Access Denied.</font>")
to_chat(user, "<span class='warning'>Access Denied.</span>")
return 1
ui_interact(user)
/obj/machinery/computer/shuttle_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag]
if (!istype(shuttle))
return
/obj/machinery/computer/shuttle_control/proc/get_ui_data(var/datum/shuttle/autodock/shuttle)
var/shuttle_state
switch(shuttle.moving_status)
if(SHUTTLE_IDLE) shuttle_state = "idle"
@@ -34,55 +31,100 @@
var/shuttle_status
switch (shuttle.process_state)
if(IDLE_STATE)
var/cannot_depart = shuttle.current_location.cannot_depart(shuttle)
if (shuttle.in_use)
shuttle_status = "Busy."
else if (!shuttle.location)
shuttle_status = "Standing-by at station."
else if(cannot_depart)
shuttle_status = cannot_depart
else
shuttle_status = "Standing-by at offsite location."
shuttle_status = "Standing-by at \the [shuttle.get_location_name()]."
if(WAIT_LAUNCH, FORCE_LAUNCH)
shuttle_status = "Shuttle has received command and will depart shortly."
if(WAIT_ARRIVE)
shuttle_status = "Proceeding to destination."
shuttle_status = "Proceeding to \the [shuttle.get_destination_name()]."
if(WAIT_FINISH)
shuttle_status = "Arriving at destination now."
data = list(
return list(
"shuttle_status" = shuttle_status,
"shuttle_state" = shuttle_state,
"has_docking" = shuttle.docking_controller? 1 : 0,
"docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null,
"docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null,
"has_docking" = shuttle.shuttle_docking_controller ? 1 : 0,
"docking_status" = shuttle.shuttle_docking_controller?.get_docking_status(),
"docking_override" = shuttle.shuttle_docking_controller?.override_enabled,
"can_launch" = shuttle.can_launch(),
"can_cancel" = shuttle.can_cancel(),
"can_force" = shuttle.can_force(),
"docking_codes" = shuttle.docking_codes
)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "shuttle_control_console.tmpl", "[shuttle_tag] Shuttle Control", 470, 310)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
// This is a subset of the actual checks; contains those that give messages to the user.
// This enables us to give nice error messages as well as not even bother proceeding if we can't.
/obj/machinery/computer/shuttle_control/proc/can_move(var/datum/shuttle/autodock/shuttle, var/user)
var/cannot_depart = shuttle.current_location.cannot_depart(shuttle)
if(cannot_depart)
to_chat(user, "<span class='warning'>[cannot_depart]</span>")
log_shuttle("Shuttle [shuttle] cannot depart [shuttle.current_location] because: [cannot_depart].")
return FALSE
if(!shuttle.next_location.is_valid(shuttle))
to_chat(user, "<span class='warning'>Destination zone is invalid or obstructed.</span>")
log_shuttle("Shuttle [shuttle] destination [shuttle.next_location] is invalid.")
return FALSE
return TRUE
/obj/machinery/computer/shuttle_control/Topic(href, href_list)
if(..())
return 1
if((. = ..()))
return
usr.set_machine(src)
src.add_fingerprint(usr)
var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag]
if (!istype(shuttle))
return
var/datum/shuttle/autodock/shuttle = SSshuttles.shuttles[shuttle_tag]
if(!shuttle)
to_chat(usr, "<span class='warning'>Unable to establish link with the shuttle.</span>")
return handle_topic_href(shuttle, href_list, usr)
/obj/machinery/computer/shuttle_control/proc/handle_topic_href(var/datum/shuttle/autodock/shuttle, var/list/href_list, var/user)
if(!istype(shuttle))
return TOPIC_NOACTION
if(href_list["move"])
shuttle.launch(src)
if(can_move(shuttle, user))
shuttle.launch(src)
return TOPIC_REFRESH
return TOPIC_HANDLED
if(href_list["force"])
shuttle.force_launch(src)
else if(href_list["cancel"])
if(can_move(shuttle, user))
shuttle.force_launch(src)
return TOPIC_REFRESH
return TOPIC_HANDLED
if(href_list["cancel"])
shuttle.cancel_launch(src)
return TOPIC_REFRESH
if(href_list["set_codes"])
var/newcode = input("Input new docking codes", "Docking codes", shuttle.docking_codes) as text|null
if (newcode && CanInteract(usr, global.default_state))
shuttle.set_docking_codes(uppertext(newcode))
return TOPIC_REFRESH
// We delegate populating data to another proc to make it easier for overriding types to add their data.
/obj/machinery/computer/shuttle_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/datum/shuttle/autodock/shuttle = SSshuttles.shuttles[shuttle_tag]
if (!istype(shuttle))
to_chat(user, "<span class='warning'>Unable to establish link with the shuttle.</span>")
return
var/list/data = get_ui_data(shuttle)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, ui_template, "[shuttle_tag] Shuttle Control", 470, 310)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/computer/shuttle_control/emag_act(var/remaining_charges, var/mob/user)
if (!hacked)

View File

@@ -0,0 +1,34 @@
/obj/machinery/computer/shuttle_control/multi
ui_template = "shuttle_control_console_multi.tmpl"
/obj/machinery/computer/shuttle_control/multi/get_ui_data(var/datum/shuttle/autodock/multi/shuttle)
. = ..()
if(istype(shuttle))
. += list(
"destination_name" = shuttle.next_location ? shuttle.next_location.name : "No destination set.",
"can_pick" = shuttle.moving_status == SHUTTLE_IDLE,
"can_cloak" = shuttle.can_cloak ? 1 : 0,
"cloaked" = shuttle.cloaked ? 1 : 0,
"legit" = shuttle.legit ? 1 : 0,
// "engines_charging" = ((shuttle.last_move + (shuttle.cooldown SECONDS)) > world.time), // Replaced by longer warmup_time
)
/obj/machinery/computer/shuttle_control/multi/handle_topic_href(var/datum/shuttle/autodock/multi/shuttle, var/list/href_list)
if((. = ..()) != null)
return
if(href_list["pick"])
var/dest_key = input("Choose shuttle destination", "Shuttle Destination") as null|anything in shuttle.get_destinations()
if(dest_key && CanInteract(usr, global.default_state))
shuttle.set_destination(dest_key, usr)
return TOPIC_REFRESH
if(href_list["toggle_cloaked"])
if(!shuttle.can_cloak)
return TOPIC_HANDLED
shuttle.cloaked = !shuttle.cloaked
if(shuttle.legit)
to_chat(usr, "<span class='notice'>Ship ATC inhibitor systems have been [(shuttle.cloaked ? "activated. The station will not" : "deactivated. The station will")] be notified of our arrival.</span>")
else
to_chat(usr, "<span class='warning'>Ship stealth systems have been [(shuttle.cloaked ? "activated. The station will not" : "deactivated. The station will")] be warned of our arrival.</span>")
return TOPIC_REFRESH

View File

@@ -1,21 +1,22 @@
/datum/shuttle/ferry/emergency
category = /datum/shuttle/ferry/emergency
// Formerly /datum/shuttle/ferry/emergency
/datum/shuttle/autodock/ferry/emergency
category = /datum/shuttle/autodock/ferry/emergency
/datum/shuttle/ferry/emergency/New()
/datum/shuttle/autodock/ferry/emergency/New()
..()
if(emergency_shuttle.shuttle)
CRASH("An emergency shuttle has already been defined.")
emergency_shuttle.shuttle = src
..()
/datum/shuttle/ferry/emergency/arrived()
/datum/shuttle/autodock/ferry/emergency/arrived()
. = ..()
if (istype(in_use, /obj/machinery/computer/shuttle_control/emergency))
var/obj/machinery/computer/shuttle_control/emergency/C = in_use
C.reset_authorization()
emergency_shuttle.shuttle_arrived()
/datum/shuttle/ferry/emergency/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction)
//to_world("shuttle/ferry/emergency/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]")
/datum/shuttle/autodock/ferry/emergency/long_jump(var/destination, var/interim, var/travel_time)
if (!location)
travel_time = SHUTTLE_TRANSIT_DURATION_RETURN
else
@@ -25,28 +26,28 @@
move_time = travel_time
emergency_shuttle.launch_time = world.time
..(destination, interim, travel_time, direction)
/datum/shuttle/autodock/ferry/emergency/perform_shuttle_move()
if (current_location == landmark_station) //leaving the station
spawn(0)
emergency_shuttle.departed = 1
var/estimated_time = round(emergency_shuttle.estimate_arrival_time()/60,1)
if (emergency_shuttle.evac)
priority_announcement.Announce(replacetext(replacetext(using_map.emergency_shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s"))
else
priority_announcement.Announce(replacetext(replacetext(using_map.shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s"))
..()
/datum/shuttle/ferry/emergency/move(var/area/origin,var/area/destination)
..(origin, destination)
if (origin == area_station) //leaving the station
emergency_shuttle.departed = 1
var/estimated_time = round(emergency_shuttle.estimate_arrival_time()/60,1)
if (emergency_shuttle.evac)
priority_announcement.Announce(replacetext(replacetext(using_map.emergency_shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s"))
else
priority_announcement.Announce(replacetext(replacetext(using_map.shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s"))
/datum/shuttle/ferry/emergency/can_launch(var/user)
/datum/shuttle/autodock/ferry/emergency/can_launch(var/user)
if (istype(user, /obj/machinery/computer/shuttle_control/emergency))
var/obj/machinery/computer/shuttle_control/emergency/C = user
if (!C.has_authorization())
return 0
return ..()
/datum/shuttle/ferry/emergency/can_force(var/user)
/datum/shuttle/autodock/ferry/emergency/can_force(var/user)
if (istype(user, /obj/machinery/computer/shuttle_control/emergency))
var/obj/machinery/computer/shuttle_control/emergency/C = user
@@ -56,14 +57,14 @@
return 0
return ..()
/datum/shuttle/ferry/emergency/can_cancel(var/user)
/datum/shuttle/autodock/ferry/emergency/can_cancel(var/user)
if (istype(user, /obj/machinery/computer/shuttle_control/emergency))
var/obj/machinery/computer/shuttle_control/emergency/C = user
if (!C.has_authorization())
return 0
return ..()
/datum/shuttle/ferry/emergency/launch(var/user)
/datum/shuttle/autodock/ferry/emergency/launch(var/user)
if (!can_launch(user)) return
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
@@ -77,7 +78,7 @@
..(user)
/datum/shuttle/ferry/emergency/force_launch(var/user)
/datum/shuttle/autodock/ferry/emergency/force_launch(var/user)
if (!can_force(user)) return
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
@@ -91,7 +92,7 @@
..(user)
/datum/shuttle/ferry/emergency/cancel_launch(var/user)
/datum/shuttle/autodock/ferry/emergency/cancel_launch(var/user)
if (!can_cancel(user)) return
if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console
@@ -177,7 +178,7 @@
/obj/machinery/computer/shuttle_control/emergency/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/datum/shuttle/ferry/emergency/shuttle = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/ferry/emergency/shuttle = SSshuttles.shuttles[shuttle_tag]
if (!istype(shuttle))
return
@@ -222,9 +223,9 @@
data = list(
"shuttle_status" = shuttle_status,
"shuttle_state" = shuttle_state,
"has_docking" = shuttle.docking_controller? 1 : 0,
"docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null,
"docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null,
"has_docking" = shuttle.active_docking_controller? 1 : 0,
"docking_status" = shuttle.active_docking_controller? shuttle.active_docking_controller.get_docking_status() : null,
"docking_override" = shuttle.active_docking_controller? shuttle.active_docking_controller.override_enabled : null,
"can_launch" = shuttle.can_launch(src),
"can_cancel" = shuttle.can_cancel(src),
"can_force" = shuttle.can_force(src),

View File

@@ -1,177 +1,50 @@
#define DOCK_ATTEMPT_TIMEOUT 200 //how long in ticks we wait before assuming the docking controller is broken or blown up.
/datum/shuttle/ferry
var/location = 0 //0 = at area_station, 1 = at area_offsite
var/direction = 0 //0 = going to station, 1 = going to offsite.
var/process_state = IDLE_STATE
var/always_process = FALSE
/datum/shuttle/autodock/ferry
var/location = FERRY_LOCATION_STATION //0 = at area_station, 1 = at area_offsite
var/direction = FERRY_GOING_TO_STATION //0 = going to station, 1 = going to offsite.
var/in_use = null //tells the controller whether this shuttle needs processing
var/always_process = FALSE // TODO -why should this exist?
var/area_transition
var/move_time = 0 //the time spent in the transition area
var/transit_direction = null //needed for area/move_contents_to() to properly handle shuttle corners - not exactly sure how it works.
var/obj/effect/shuttle_landmark/landmark_station //This variable is type-abused initially: specify the landmark_tag, not the actual landmark.
var/obj/effect/shuttle_landmark/landmark_offsite //This variable is type-abused initially: specify the landmark_tag, not the actual landmark.
var/area/area_station
var/area/area_offsite
//TODO: change location to a string and use a mapping for area and dock targets.
var/dock_target_station
var/dock_target_offsite
category = /datum/shuttle/autodock/ferry
var/last_dock_attempt_time = 0
category = /datum/shuttle/ferry
/datum/shuttle/autodock/ferry/New(var/_name)
if(landmark_station)
landmark_station = SSshuttles.get_landmark(landmark_station)
if(landmark_offsite)
landmark_offsite = SSshuttles.get_landmark(landmark_offsite)
/datum/shuttle/ferry/New()
area_offsite = locate(area_offsite)
area_station = locate(area_station)
if(area_transition)
area_transition = locate(area_transition)
..()
..(_name, get_location_waypoint(location))
/datum/shuttle/ferry/short_jump(var/area/origin,var/area/destination)
if(isnull(location))
return
next_location = get_location_waypoint(!location)
if(!destination)
destination = get_location_area(!location)
if(!origin)
origin = get_location_area(location)
direction = !location
..(origin, destination)
/datum/shuttle/ferry/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction)
//to_world("shuttle/ferry/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]")
if(isnull(location))
return
if(!destination)
destination = get_location_area(!location)
if(!departing)
departing = get_location_area(location)
direction = !location
..(departing, destination, interim, travel_time, direction)
/datum/shuttle/ferry/move(var/area/origin,var/area/destination)
..(origin, destination)
if (destination == area_station) location = 0
if (destination == area_offsite) location = 1
//if this is a long_jump retain the location we were last at until we get to the new one
/datum/shuttle/ferry/dock()
..()
last_dock_attempt_time = world.time
/datum/shuttle/ferry/proc/get_location_area(location_id = null)
//Gets the shuttle landmark associated with the given location (defaults to current location)
/datum/shuttle/autodock/ferry/proc/get_location_waypoint(location_id = null)
if (isnull(location_id))
location_id = location
if (!location_id)
return area_station
return area_offsite
if (location_id == FERRY_LOCATION_STATION)
return landmark_station
return landmark_offsite
/*
Please ensure that long_jump() and short_jump() are only called from here. This applies to subtypes as well.
Doing so will ensure that multiple jumps cannot be initiated in parallel.
*/
/datum/shuttle/ferry/process()
switch(process_state)
if (WAIT_LAUNCH)
if (skip_docking_checks() || docking_controller.can_launch())
/datum/shuttle/autodock/ferry/short_jump(var/destination)
direction = !location // Heading away from where we currently are
. = ..()
//to_world("shuttle/ferry/process: area_transition=[area_transition], travel_time=[travel_time]")
if (move_time && area_transition)
long_jump(interim=area_transition, travel_time=move_time, direction=transit_direction)
else
short_jump()
/datum/shuttle/autodock/ferry/long_jump(var/destination, var/obj/effect/shuttle_landmark/interim, var/travel_time)
direction = !location // Heading away from where we currently are
. = ..()
process_state = WAIT_ARRIVE
if (FORCE_LAUNCH)
if (move_time && area_transition)
long_jump(interim=area_transition, travel_time=move_time, direction=transit_direction)
else
short_jump()
process_state = WAIT_ARRIVE
if (WAIT_ARRIVE)
if (moving_status == SHUTTLE_IDLE)
dock()
in_use = null //release lock
process_state = WAIT_FINISH
if (WAIT_FINISH)
if (skip_docking_checks() || docking_controller.docked() || world.time > last_dock_attempt_time + DOCK_ATTEMPT_TIMEOUT)
process_state = IDLE_STATE
arrived()
/datum/shuttle/ferry/current_dock_target()
var/dock_target
if (!location) //station
dock_target = dock_target_station
else
dock_target = dock_target_offsite
return dock_target
/datum/shuttle/ferry/proc/launch(var/user)
if (!can_launch()) return
in_use = user //obtain an exclusive lock on the shuttle
process_state = WAIT_LAUNCH
undock()
/datum/shuttle/ferry/proc/force_launch(var/user)
if (!can_force()) return
in_use = user //obtain an exclusive lock on the shuttle
process_state = FORCE_LAUNCH
/datum/shuttle/ferry/proc/cancel_launch(var/user)
if (!can_cancel()) return
moving_status = SHUTTLE_IDLE
process_state = WAIT_FINISH
in_use = null
if (docking_controller && !docking_controller.undocked())
docking_controller.force_undock()
spawn(10)
dock()
return
/datum/shuttle/ferry/proc/can_launch()
if (moving_status != SHUTTLE_IDLE)
return 0
if (in_use)
return 0
return 1
/datum/shuttle/ferry/proc/can_force()
if (moving_status == SHUTTLE_IDLE && process_state == WAIT_LAUNCH)
return 1
return 0
/datum/shuttle/ferry/proc/can_cancel()
if (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH)
return 1
return 0
//returns 1 if the shuttle is getting ready to move, but is not in transit yet
/datum/shuttle/ferry/proc/is_launching()
return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH)
//This gets called when the shuttle finishes arriving at it's destination
//This can be used by subtypes to do things when the shuttle arrives.
/datum/shuttle/ferry/proc/arrived()
return //do nothing for now
/datum/shuttle/autodock/ferry/perform_shuttle_move()
..()
if (current_location == landmark_station) location = FERRY_LOCATION_STATION
if (current_location == landmark_offsite) location = FERRY_LOCATION_OFFSITE
// Once we have arrived where we are going, plot a course back!
/datum/shuttle/autodock/ferry/process_arrived()
..()
next_location = get_location_waypoint(!location)

View File

@@ -4,39 +4,11 @@
req_access = list(access_cent_specops)
/obj/machinery/computer/shuttle_control/specops/attack_ai(user as mob)
to_chat(user, "<font color='red'>Access Denied.</font>")
to_chat(user, "<span class='warning'>Access Denied.</span>")
return 1
//for shuttles that may use a different docking port at each location
/datum/shuttle/ferry/multidock
var/docking_controller_tag_station
var/docking_controller_tag_offsite
var/datum/computer/file/embedded_program/docking/docking_controller_station
var/datum/computer/file/embedded_program/docking/docking_controller_offsite
category = /datum/shuttle/ferry/multidock
/datum/shuttle/ferry/multidock/init_docking_controllers()
if(docking_controller_tag_station)
docking_controller_station = locate(docking_controller_tag_station)
if(!istype(docking_controller_station))
warning("warning: shuttle with docking tag [docking_controller_station] could not find it's controller!")
if(docking_controller_tag_offsite)
docking_controller_offsite = locate(docking_controller_tag_offsite)
if(!istype(docking_controller_offsite))
warning("warning: shuttle with docking tag [docking_controller_offsite] could not find it's controller!")
if (!location)
docking_controller = docking_controller_station
else
docking_controller = docking_controller_offsite
/datum/shuttle/ferry/multidock/move(var/area/origin,var/area/destination)
..(origin, destination)
if (!location)
docking_controller = docking_controller_station
else
docking_controller = docking_controller_offsite
/datum/shuttle/ferry/multidock/specops
// Formerly /datum/shuttle/ferry/multidock/specops
/datum/shuttle/autodock/ferry/specops
var/specops_return_delay = 6000 //After moving, the amount of time that must pass before the shuttle may move again
var/specops_countdown_time = 600 //Length of the countdown when moving the shuttle
@@ -44,19 +16,19 @@
var/reset_time = 0 //the world.time at which the shuttle will be ready to move again.
var/launch_prep = 0
var/cancel_countdown = 0
category = /datum/shuttle/ferry/multidock/specops
category = /datum/shuttle/autodock/ferry/specops
/datum/shuttle/ferry/multidock/specops/New()
/datum/shuttle/autodock/ferry/specops/New()
..()
announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky.
announcer.config(list("Response Team" = 0))
/datum/shuttle/ferry/multidock/specops/proc/radio_announce(var/message)
/datum/shuttle/autodock/ferry/specops/proc/radio_announce(var/message)
if(announcer)
announcer.autosay(message, "A.L.I.C.E.", "Response Team")
/datum/shuttle/ferry/multidock/specops/launch(var/user)
/datum/shuttle/autodock/ferry/specops/launch(var/user)
if (!can_launch())
return
@@ -64,14 +36,14 @@
var/obj/machinery/computer/C = user
if(world.time <= reset_time)
C.visible_message("<font color='blue'>[using_map.boss_name] will not allow the Special Operations shuttle to launch yet.</font>")
C.visible_message("<span class='notice'>[global.using_map.boss_name] will not allow the Special Operations shuttle to launch yet.</span>")
if (((world.time - reset_time)/10) > 60)
C.visible_message("<font color='blue'>[-((world.time - reset_time)/10)/60] minutes remain!</font>")
C.visible_message("<span class='notice'>[-((world.time - reset_time)/10)/60] minutes remain!</span>")
else
C.visible_message("<font color='blue'>[-(world.time - reset_time)/10] seconds remain!</font>")
C.visible_message("<span class='notice'>[-(world.time - reset_time)/10] seconds remain!</span>")
return
C.visible_message("<font color='blue'>The Special Operations shuttle will depart in [(specops_countdown_time/10)] seconds.</font>")
C.visible_message("<span class='notice'>The Special Operations shuttle will depart in [(specops_countdown_time/10)] seconds.</span>")
if (location) //returning
radio_announce("THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN")
@@ -81,31 +53,31 @@
sleep_until_launch()
if (location)
var/obj/machinery/light/small/readylight/light = locate() in get_location_area()
var/obj/machinery/light/small/readylight/light = locate() in shuttle_area
if(light) light.set_state(0)
//launch
radio_announce("ALERT: INITIATING LAUNCH SEQUENCE")
..(user)
/datum/shuttle/ferry/multidock/specops/move(var/area/origin,var/area/destination)
..(origin, destination)
/datum/shuttle/autodock/ferry/specops/perform_shuttle_move()
..()
spawn(20)
spawn(2 SECONDS)
if (!location) //just arrived home
for(var/turf/T in get_area_turfs(destination))
for(var/turf/T in get_area_turfs(shuttle_area))
var/mob/M = locate(/mob) in T
to_chat(M, "<span class='danger'>You have arrived at [using_map.boss_name]. Operation has ended!</span>")
else //just left for the station
launch_mauraders()
for(var/turf/T in get_area_turfs(destination))
for(var/turf/T in get_area_turfs(shuttle_area))
var/mob/M = locate(/mob) in T
to_chat(M, "<span class='danger'>You have arrived at [station_name()]. Commence operation!</span>")
var/obj/machinery/light/small/readylight/light = locate() in T
if(light) light.set_state(1)
/datum/shuttle/ferry/multidock/specops/cancel_launch()
/datum/shuttle/autodock/ferry/specops/cancel_launch()
if (!can_cancel())
return
@@ -113,27 +85,26 @@
radio_announce("ALERT: LAUNCH SEQUENCE ABORTED")
if (istype(in_use, /obj/machinery/computer))
var/obj/machinery/computer/C = in_use
C.visible_message("<font color='red'>Launch sequence aborted.</font>")
C.visible_message("<span class='warning'>Launch sequence aborted.</span>")
..()
/datum/shuttle/ferry/multidock/specops/can_launch()
/datum/shuttle/autodock/ferry/specops/can_launch()
if(launch_prep)
return 0
return ..()
//should be fine to allow forcing. process_state only becomes WAIT_LAUNCH after the countdown is over.
///datum/shuttle/ferry/multidock/specops/can_force()
///datum/shuttle/autodock/ferry/specops/can_force()
// return 0
/datum/shuttle/ferry/multidock/specops/can_cancel()
/datum/shuttle/autodock/ferry/specops/can_cancel()
if(launch_prep)
return 1
return ..()
/datum/shuttle/ferry/multidock/specops/proc/sleep_until_launch()
/datum/shuttle/autodock/ferry/specops/proc/sleep_until_launch()
var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values.
var/launch_time = world.time + specops_countdown_time

View File

@@ -1,82 +1,81 @@
/datum/shuttle/ferry/supply
var/away_location = 1 //the location to hide at while pretending to be in-transit
// Formerly /datum/shuttle/ferry/supply
/datum/shuttle/autodock/ferry/supply
var/away_location = FERRY_LOCATION_OFFSITE //the location to hide at while pretending to be in-transit
var/late_chance = 80
var/max_late_time = 300
category = /datum/shuttle/ferry/supply
flags = SHUTTLE_FLAGS_PROCESS|SHUTTLE_FLAGS_SUPPLY
category = /datum/shuttle/autodock/ferry/supply
/datum/shuttle/ferry/supply/short_jump(var/area/origin,var/area/destination)
/datum/shuttle/autodock/ferry/supply/short_jump(var/obj/effect/shuttle_landmark/destination)
if(moving_status != SHUTTLE_IDLE)
return
if(isnull(location))
return
if(!destination)
destination = get_location_area(!location)
if(!origin)
origin = get_location_area(location)
//it would be cool to play a sound here
moving_status = SHUTTLE_WARMUP
spawn(warmup_time*10)
make_sounds(origin, HYPERSPACE_WARMUP)
make_sounds(HYPERSPACE_WARMUP)
sleep(5 SECONDS) // so the sound finishes.
if (moving_status == SHUTTLE_IDLE)
make_sounds(origin, HYPERSPACE_END)
make_sounds(HYPERSPACE_END)
return //someone cancelled the launch
if (at_station() && forbidden_atoms_check())
//cancel the launch because of forbidden atoms. announce over supply channel?
moving_status = SHUTTLE_IDLE
make_sounds(origin, HYPERSPACE_END)
make_sounds(HYPERSPACE_END)
return
if (!at_station()) //at centcom
supply_controller.buy()
//We pretend it's a long_jump by making the shuttle stay at centcom for the "in-transit" period.
var/area/away_area = get_location_area(away_location)
var/obj/effect/shuttle_landmark/away_waypoint = get_location_waypoint(away_location)
moving_status = SHUTTLE_INTRANSIT
//If we are at the away_area then we are just pretending to move, otherwise actually do the move
if (origin != away_area)
move(origin, away_area)
//If we are at the away_landmark then we are just pretending to move, otherwise actually do the move
if (next_location == away_waypoint)
attempt_move(away_waypoint)
//wait ETA here.
arrive_time = world.time + supply_controller.movetime
while (world.time <= arrive_time)
sleep(5)
if (destination != away_area)
if (next_location != away_waypoint)
//late
if (prob(late_chance))
sleep(rand(0,max_late_time))
move(away_area, destination)
attempt_move(destination)
moving_status = SHUTTLE_IDLE
make_sounds(destination, HYPERSPACE_END)
make_sounds(HYPERSPACE_END)
if (!at_station()) //at centcom
supply_controller.sell()
// returns 1 if the supply shuttle should be prevented from moving because it contains forbidden atoms
/datum/shuttle/ferry/supply/proc/forbidden_atoms_check()
/datum/shuttle/autodock/ferry/supply/proc/forbidden_atoms_check()
if (!at_station())
return 0 //if badmins want to send mobs or a nuke on the supply shuttle from centcom we don't care
return supply_controller.forbidden_atoms_check(get_location_area())
for(var/area/A in shuttle_area)
if(supply_controller.forbidden_atoms_check(A))
return 1
/datum/shuttle/ferry/supply/proc/at_station()
/datum/shuttle/autodock/ferry/supply/proc/at_station()
return (!location)
//returns 1 if the shuttle is idle and we can still mess with the cargo shopping list
/datum/shuttle/ferry/supply/proc/idle()
/datum/shuttle/autodock/ferry/supply/proc/idle()
return (moving_status == SHUTTLE_IDLE)
//returns the ETA in minutes
/datum/shuttle/ferry/supply/proc/eta_minutes()
/datum/shuttle/autodock/ferry/supply/proc/eta_minutes()
var/ticksleft = arrive_time - world.time
return round(ticksleft/600,1)

View File

@@ -1,13 +1,15 @@
//This is a holder for things like the Skipjack and Nuke shuttle.
/datum/shuttle/multi_shuttle
// Formerly /datum/shuttle/multi_shuttle
/datum/shuttle/autodock/multi
var/list/destination_tags
var/list/destinations_cache = list()
var/last_cache_rebuild_time = 0
category = /datum/shuttle/autodock/multi
flags = SHUTTLE_FLAGS_NONE
var/cloaked = FALSE
var/can_cloak = FALSE
var/at_origin = 1
var/returned_home = 0
// var/move_time = 240
var/move_time = 60
var/cooldown = 20
var/last_move = 0 //the time at which we last moved
@@ -15,276 +17,46 @@
var/arrival_message
var/departure_message
var/area/interim
var/area/last_departed
var/start_location
var/last_location
var/list/destinations
var/list/destination_dock_controller_tags = list() //optional, in case the shuttle has multiple docking ports like the ERT shuttle (even though that isn't a multi_shuttle)
var/list/destination_dock_controllers = list()
var/list/destination_dock_targets = list()
var/area/origin
var/return_warning = 0
var/legit = 0 //VOREStation Add - Whether or not a shuttle is a legit NT shuttle.
category = /datum/shuttle/multi_shuttle
var/legit = FALSE
/datum/shuttle/multi_shuttle/New()
origin = locate(origin)
interim = locate(interim)
for(var/destination in destinations)
destinations[destination] = locate(destinations[destination])
/datum/shuttle/autodock/multi/New()
..()
start_location = current_location
last_location = current_location
/datum/shuttle/multi_shuttle/init_docking_controllers()
..()
for(var/destination in destinations)
var/controller_tag = destination_dock_controller_tags[destination]
if(!controller_tag)
destination_dock_controllers[destination] = docking_controller
else
var/datum/computer/file/embedded_program/docking/C = locate(controller_tag)
/datum/shuttle/autodock/multi/proc/set_destination(var/destination_key, mob/user)
if(moving_status != SHUTTLE_IDLE)
return
next_location = destinations_cache[destination_key]
if(!next_location)
warning("Shuttle [src] set to destination we can't find: [destination_key]")
if(!istype(C))
warning("warning: shuttle with docking tag [controller_tag] could not find it's controller!")
else
destination_dock_controllers[destination] = C
/datum/shuttle/autodock/multi/proc/get_destinations()
if (last_cache_rebuild_time < SSshuttles.last_landmark_registration_time)
build_destinations_cache()
return destinations_cache
//might as well set this up here.
if(origin) last_departed = origin
last_location = start_location
//VOREStation Add - Set up origin dock controller
if(!(start_location in destination_dock_controller_tags))
destination_dock_controllers[start_location] = docking_controller
//VOREStation Add End
/datum/shuttle/multi_shuttle/current_dock_target()
return destination_dock_targets[last_location]
/datum/shuttle/autodock/multi/proc/build_destinations_cache()
last_cache_rebuild_time = world.time
destinations_cache.Cut()
for(var/destination_tag in destination_tags)
var/obj/effect/shuttle_landmark/landmark = SSshuttles.get_landmark(destination_tag)
if (istype(landmark))
destinations_cache["[landmark.name]"] = landmark
/datum/shuttle/multi_shuttle/move(var/area/origin, var/area/destination)
/datum/shuttle/autodock/multi/perform_shuttle_move()
..()
last_move = world.time
if (destination == src.origin)
returned_home = 1
docking_controller = destination_dock_controllers[last_location]
/datum/shuttle/multi_shuttle/proc/announce_departure()
/datum/shuttle/autodock/multi/proc/announce_departure()
if(cloaked || isnull(departure_message))
return
command_announcement.Announce(departure_message, (announcer ? announcer : "[using_map.boss_name]"))
command_announcement.Announce(departure_message,(announcer ? announcer : "[using_map.boss_name]"))
/datum/shuttle/multi_shuttle/proc/announce_arrival()
/datum/shuttle/autodock/multi/proc/announce_arrival()
if(cloaked || isnull(arrival_message))
return
command_announcement.Announce(arrival_message,(announcer ? announcer : "[using_map.boss_name]"))
/obj/machinery/computer/shuttle_control/multi
icon_keyboard = "syndie_key"
icon_screen = "syndishuttle"
/obj/machinery/computer/shuttle_control/multi/attack_hand(user as mob)
if(..(user))
return
src.add_fingerprint(user)
var/datum/shuttle/multi_shuttle/MS = shuttle_controller.shuttles[shuttle_tag]
if(!istype(MS)) return
var/dat
dat = "<center>[shuttle_tag] Ship Control<hr>"
if(MS.moving_status != SHUTTLE_IDLE)
dat += "Location: <font color='red'>Moving</font> <br>"
else
var/area/areacheck = get_area(src)
dat += "Location: [areacheck.name]<br>"
if((MS.last_move + MS.cooldown*10) > world.time)
dat += "<font color='red'>Engines charging.</font><br>"
else
dat += "<font color='green'>Engines ready.</font><br>"
//dat += "<br><b><A href='?src=\ref[src];toggle_cloak=[1]'>[MS.legit ? "Inhibit ATC":"Toggle cloak"]</A></b><br>" //VOREStation Edit - Adds legit shuttles. //SYNC Edit: Below deals with cloak.
if(MS.can_cloak)
dat += "<br><b><A href='?src=\ref[src];toggle_cloak=[1]'>Toggle cloaking field</A></b><br>"
dat += "<b><A href='?src=\ref[src];move_multi=[1]'>Move ship</A></b><br>"
dat += "<b><A href='?src=\ref[src];start=[1]'>Return to base</A></b></center>"
//Docking
dat += "<center><br><br>"
if(MS.skip_docking_checks())
dat += "Docking Status: <font color='grey'>Not in use.</font>"
else
var/override_en = MS.docking_controller.override_enabled
var/docking_status = MS.docking_controller.get_docking_status()
dat += "Docking Status: "
switch(docking_status)
if("undocked")
dat += "<font color='[override_en? "red" : "grey"]'>Undocked</font>"
if("docking")
dat += "<font color='[override_en? "red" : "yellow"]'>Docking</font>"
if("undocking")
dat += "<font color='[override_en? "red" : "yellow"]'>Undocking</font>"
if("docked")
dat += "<font color='[override_en? "red" : "green"]'>Docked</font>"
if(override_en) dat += " <font color='red'>(Override Enabled)</font>"
dat += ". <A href='?src=\ref[src];refresh=[1]'>\[Refresh\]</A><br><br>"
switch(docking_status)
if("undocked")
dat += "<b><A href='?src=\ref[src];dock_command=[1]'>Dock</A></b>"
if("docked")
dat += "<b><A href='?src=\ref[src];undock_command=[1]'>Undock</A></b>"
dat += "</center>"
user << browse("[dat]", "window=[shuttle_tag]shuttlecontrol;size=300x600")
//check if we're undocked, give option to force launch
/obj/machinery/computer/shuttle_control/proc/check_docking(datum/shuttle/multi_shuttle/MS)
if(MS.skip_docking_checks() || MS.docking_controller.can_launch())
return 1
var/choice = alert("The shuttle is currently docked! Please undock before continuing.","Error","Cancel","Force Launch")
if(choice == "Cancel")
return 0
choice = alert("Forcing a shuttle launch while docked may result in severe injury, death and/or damage to property. Are you sure you wish to continue?", "Force Launch", "Force Launch", "Cancel")
if(choice == "Cancel")
return 0
return 1
/obj/machinery/computer/shuttle_control/multi/Topic(href, href_list)
if(..())
return 1
usr.set_machine(src)
src.add_fingerprint(usr)
var/datum/shuttle/multi_shuttle/MS = shuttle_controller.shuttles[shuttle_tag]
if(!istype(MS)) return
//to_world("multi_shuttle: last_departed=[MS.last_departed], origin=[MS.origin], interim=[MS.interim], travel_time=[MS.move_time]")
if(href_list["refresh"])
updateUsrDialog()
return
if (MS.moving_status != SHUTTLE_IDLE)
to_chat(usr, "<font color='blue'>[shuttle_tag] vessel is moving.</font>")
return
if(href_list["dock_command"])
MS.dock()
return
if(href_list["undock_command"])
MS.undock()
return
if(href_list["start"])
if(MS.at_origin)
to_chat(usr, "<font color='red'>You are already at the home base.</font>")
return
if((MS.last_move + MS.cooldown*10) > world.time)
to_chat(usr, "<font color='red'>The ship's drive is inoperable while the engines are charging.</font>")
return
if(!check_docking(MS))
updateUsrDialog()
return
if(!MS.return_warning && !MS.legit) //VOREStation Add - Criminals only!
to_chat(usr, "<font color='red'>Returning to your home base will end your mission. If you are sure, press the button again.</font>")
//TODO: Actually end the mission.
MS.return_warning = 1
return
//VOREStation Add - Only long-jump if that's a thing you do
if(MS.interim)
MS.long_jump(MS.last_departed, MS.origin, MS.interim, MS.move_time)
else
MS.short_jump(MS.last_departed, MS.origin)
//VOREStation Add End
// No point giving a warning if it does literally nothing.
// if(!MS.return_warning)
// to_chat(usr, "<font color='red'>Returning to your home base will end your mission. If you are sure, press the button again.</font>")
// //TODO: Actually end the mission.
// MS.return_warning = 1
// return
//MS.long_jump(MS.last_departed, MS.origin, MS.interim, MS.move_time) //VOREStation Edit
MS.last_departed = MS.origin
MS.last_location = MS.start_location
MS.at_origin = 1
if(href_list["toggle_cloak"])
if(!MS.can_cloak)
return
MS.cloaked = !MS.cloaked
to_chat(usr, "<font color='red'> Ship [MS.legit ? "ATC inhibitor":"stealth"] systems have been [(MS.cloaked ? "activated. The station will not" : "deactivated. The station will")] be [MS.legit ? "notified":"warned"] of our arrival.</font>") //VOREStation Edit - Adds legit shuttles.
//to_chat(usr, "<font color='red'>Ship stealth systems have been [(MS.cloaked ? "activated. The station will not" : "deactivated. The station will")] be warned of our arrival.</font>") //VOREStation Edit.
if(href_list["move_multi"])
if((MS.last_move + MS.cooldown*10) > world.time)
to_chat(usr, "<font color='red'>The ship's drive is inoperable while the engines are charging.</font>")
return
if(!check_docking(MS))
updateUsrDialog()
return
var/choice = input("Select a destination.") as null|anything in MS.destinations
if(!choice) return
to_chat(usr, "<font color='blue'>[shuttle_tag] main computer received message.</font>")
if(MS.at_origin)
MS.announce_arrival()
MS.last_departed = MS.origin
MS.at_origin = 0
//VOREStation Add - Only long-jump if that's a thing you do
if(MS.interim)
MS.long_jump(MS.last_departed, MS.destinations[choice], MS.interim, MS.move_time)
else
MS.short_jump(MS.last_departed, MS.destinations[choice])
//VOREStation Add End
MS.last_departed = MS.destinations[choice]
MS.last_location = choice
return
else if(choice == MS.origin)
MS.announce_departure()
//VOREStation Add - Only long-jump if that's a thing you do
if(MS.interim)
MS.long_jump(MS.last_departed, MS.destinations[choice], MS.interim, MS.move_time)
else
MS.short_jump(MS.last_departed, MS.destinations[choice])
return
//VOREStation Add End
//VOREStation Add - Only long-jump if that's a thing you do
if(MS.interim)
MS.long_jump(MS.last_departed, MS.destinations[choice], MS.interim, MS.move_time)
else
MS.short_jump(MS.last_departed, MS.destinations[choice])
//VOREStation Add End
//MS.short_jump(MS.last_departed, MS.destinations[choice]) //VOREStation Removal
MS.last_departed = MS.destinations[choice]
MS.last_location = choice
updateUsrDialog()
command_announcement.Announce(arrival_message, (announcer ? announcer : "[using_map.boss_name]"))

View File

@@ -1,12 +1,11 @@
//This shuttle traverses a "web" of route_datums to have a wider range of places to go and make flying feel like movement is actually occuring.
/datum/shuttle/web_shuttle
flags = SHUTTLE_FLAGS_NONE
/datum/shuttle/autodock/web_shuttle
flags = SHUTTLE_FLAGS_ZERO_G
var/visible_name = null // The pretty name shown to people in announcements, since the regular name var is used internally for other things.
var/cloaked = FALSE
var/can_cloak = FALSE
var/cooldown = 0
var/last_move = 0 //the time at which we last moved
var/area/current_area = null
var/datum/shuttle_web_master/web_master = null
var/web_master_type = null
var/flight_time_modifier = 1.0
@@ -15,11 +14,10 @@
var/autopilot_delay = 60 // How many ticks to not do anything when not following an autopath. Should equal two minutes.
var/autopilot_first_delay = null // If your want your shuttle to stay for a different amount of time for the first time, set this.
var/can_rename = TRUE // Lets the pilot rename the shuttle. Only available once.
category = /datum/shuttle/web_shuttle
category = /datum/shuttle/autodock/web_shuttle
var/list/obj/item/clothing/head/pilot/helmets
/datum/shuttle/web_shuttle/New()
current_area = locate(current_area)
/datum/shuttle/autodock/web_shuttle/New()
web_master = new web_master_type(src)
build_destinations()
if(autopilot)
@@ -31,42 +29,45 @@
helmets = list()
..()
/datum/shuttle/web_shuttle/Destroy()
qdel(web_master)
/datum/shuttle/autodock/web_shuttle/Destroy()
QDEL_NULL(web_master)
helmets.Cut()
return ..()
/datum/shuttle/autodock/web_shuttle/current_dock_target()
// TODO - Probably don't even need to override this right? Debug testing code below will check!
. = web_master?.get_current_destination()?.my_landmark?.docking_controller?.id_tag
if (. != ..())
warning("Web shuttle [src] had current_dock_target()=[.] but autodock.current_dock_target() = [..()]")
/datum/shuttle/web_shuttle/current_dock_target()
if(web_master)
return web_master.current_dock_target()
/datum/shuttle/web_shuttle/move(var/area/origin, var/area/destination)
/datum/shuttle/autodock/web_shuttle/perform_shuttle_move()
..()
last_move = world.time
active_docking_controller = current_location.docking_controller
update_docking_target(current_location)
/datum/shuttle/web_shuttle/short_jump()
/datum/shuttle/autodock/web_shuttle/short_jump()
. = ..()
update_helmets()
/datum/shuttle/web_shuttle/long_jump()
/datum/shuttle/autodock/web_shuttle/long_jump()
. = ..()
update_helmets()
/datum/shuttle/web_shuttle/on_shuttle_departure()
/datum/shuttle/autodock/web_shuttle/on_shuttle_departure()
. = ..()
web_master.on_shuttle_departure()
update_helmets()
/datum/shuttle/web_shuttle/on_shuttle_arrival()
/datum/shuttle/autodock/web_shuttle/on_shuttle_arrival()
. = ..()
web_master.on_shuttle_arrival()
update_helmets()
/datum/shuttle/web_shuttle/proc/build_destinations()
/datum/shuttle/autodock/web_shuttle/proc/build_destinations()
return
/datum/shuttle/web_shuttle/process()
/datum/shuttle/autodock/web_shuttle/process()
update_helmets()
if(moving_status == SHUTTLE_IDLE)
@@ -76,8 +77,8 @@
else // Otherwise we are about to start one or just finished one.
if(autopilot_delay > 0) // Wait for awhile so people can get on and off.
if(docking_controller && !skip_docking_checks()) // Dock to the destination if possible.
var/docking_status = docking_controller.get_docking_status()
if(active_docking_controller && shuttle_docking_controller) // Dock to the destination if possible.
var/docking_status = shuttle_docking_controller.get_docking_status()
if(docking_status == "undocked")
dock()
autopilot_say("Docking.")
@@ -96,8 +97,8 @@
autopilot_delay--
else // Time to go.
if(docking_controller && !skip_docking_checks()) // Undock if possible.
var/docking_status = docking_controller.get_docking_status()
if(active_docking_controller && shuttle_docking_controller) // Undock if possible.
var/docking_status = shuttle_docking_controller.get_docking_status()
if(docking_status == "docked")
undock()
autopilot_say("Undocking.")
@@ -109,13 +110,13 @@
autopilot_say("Taking off.")
web_master.process_autopath()
/datum/shuttle/web_shuttle/proc/update_helmets()
/datum/shuttle/autodock/web_shuttle/proc/update_helmets()
for(var/helm in helmets)
if(!helm)
helmets -= helm
continue
var/obj/item/clothing/head/pilot/H = helm
if(!H.shuttle_comp || get_area(H.shuttle_comp) != get_area(H))
if(QDELETED(H))
helmets -= H
continue
if(!H.shuttle_comp || !(get_area(H) in shuttle_area))
H.shuttle_comp = null
H.audible_message("<span class='warning'>\The [H] pings as it loses it's connection with the ship.</span>")
H.update_hud("discon")
@@ -123,24 +124,24 @@
else
H.update_hud(moving_status)
/datum/shuttle/web_shuttle/proc/adjust_autopilot(on)
/datum/shuttle/autodock/web_shuttle/proc/adjust_autopilot(on)
if(on)
if(autopilot)
return
autopilot = TRUE
autopilot_delay = initial(autopilot_delay)
shuttle_controller.process_shuttles += src
shuttle_controller.process_shuttles |= src
else
if(!autopilot)
return
autopilot = FALSE
shuttle_controller.process_shuttles -= src
/datum/shuttle/web_shuttle/proc/autopilot_say(message) // Makes the autopilot 'talk' to the passengers.
/datum/shuttle/autodock/web_shuttle/proc/autopilot_say(message) // Makes the autopilot 'talk' to the passengers.
var/padded_message = "<span class='game say'><span class='name'>shuttle autopilot</span> states, \"[message]\"</span>"
message_passengers(current_area, padded_message)
message_passengers(padded_message)
/datum/shuttle/web_shuttle/proc/rename_shuttle(mob/user)
/datum/shuttle/autodock/web_shuttle/proc/rename_shuttle(mob/user)
if(!can_rename)
to_chat(user, "<span class='warning'>You can't rename this vessel.</span>")
return
@@ -162,6 +163,8 @@
var/list/my_doors //Should be list("id_tag" = "Pretty Door Name", ...)
var/list/my_sensors //Should be list("id_tag" = "Pretty Sensor Name", ...)
// Note - Searching own area for doors/sensors is fine for legacy web shuttles as they are single-area.
// However if this code is copied to future multi-area shuttles, should search in all shuttle areas
/obj/machinery/computer/shuttle_control/web/Initialize()
. = ..()
var/area/my_area = get_area(src)
@@ -186,7 +189,7 @@
log_debug("[my_area] shuttle computer couldn't find [lost] sensor!")
/obj/machinery/computer/shuttle_control/web/attackby(obj/I, mob/user)
var/datum/shuttle/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag]
if(shuttle && istype(I,/obj/item/clothing/head/pilot))
var/obj/item/clothing/head/pilot/H = I
H.shuttle_comp = src
@@ -208,7 +211,7 @@
/*
// If nanoUI falls over and you want a non-nanoUI UI, feel free to uncomment this section.
var/datum/shuttle/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag]
if(!istype(WS))
message_admins("ERROR: Shuttle computer ([src]) ([shuttle_tag]) could not find their shuttle in the shuttles list.")
return
@@ -272,8 +275,9 @@
/obj/machinery/computer/shuttle_control/web/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
var/list/routes[0]
var/datum/shuttle/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/web_shuttle/shuttle = SSshuttles.shuttles[shuttle_tag]
if(!istype(shuttle))
to_chat(user, "<span class='warning'>Unable to establish link with the shuttle.</span>")
return
var/list/R = shuttle.web_master.get_available_routes()
@@ -333,11 +337,11 @@
"future_location" = future_location,
"shuttle_state" = shuttle_state,
"routes" = routes,
"has_docking" = shuttle.docking_controller? 1 : 0,
"has_docking" = shuttle.shuttle_docking_controller? 1 : 0,
"skip_docking" = shuttle.skip_docking_checks(),
"is_moving" = shuttle.moving_status != SHUTTLE_IDLE,
"docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null,
"docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null,
"docking_status" = shuttle.shuttle_docking_controller? shuttle.shuttle_docking_controller.get_docking_status() : null,
"docking_override" = shuttle.shuttle_docking_controller? shuttle.shuttle_docking_controller.override_enabled : null,
"is_in_transit" = shuttle.has_arrive_time(),
"travel_progress" = between(0, percent_finished, 100),
"time_left" = round( (total_time - elapsed_time) / 10),
@@ -360,13 +364,13 @@
/obj/machinery/computer/shuttle_control/web/Topic(href, href_list)
if(..())
return 1
if((. = ..()))
return
usr.set_machine(src)
src.add_fingerprint(usr)
var/datum/shuttle/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag]
var/datum/shuttle/autodock/web_shuttle/WS = SSshuttles.shuttles[shuttle_tag]
if(!istype(WS))
message_admins("ERROR: Shuttle computer ([src]) ([shuttle_tag]) could not find their shuttle in the shuttles list.")
return
@@ -436,19 +440,38 @@
message_admins("ERROR: Shuttle computer was asked to travel to a nonexistant destination.")
return
WS.next_location = target_destination.my_landmark
if(!can_move(WS, usr))
return
WS.web_master.future_destination = target_destination
to_chat(usr, "<span class='notice'>[WS.visible_name] flight computer received command.</span>")
WS.web_master.reset_autopath() // Deviating from the path will almost certainly confuse the autopilot, so lets just reset its memory.
var/travel_time = new_route.travel_time * WS.flight_time_modifier
// TODO - Leshana - Change this to use proccess stuff of autodock!
if(new_route.interim && new_route.travel_time)
WS.long_jump(WS.current_area, target_destination.my_area, new_route.interim, travel_time / 10)
WS.long_jump(target_destination.my_landmark, new_route.interim, travel_time / 10)
else
WS.short_jump(WS.current_area, target_destination.my_area)
WS.short_jump(target_destination.my_landmark)
ui_interact(usr)
//check if we're undocked, give option to force launch
/obj/machinery/computer/shuttle_control/web/proc/check_docking(datum/shuttle/autodock/MS)
if(MS.skip_docking_checks() || MS.check_undocked())
return 1
var/choice = alert("The shuttle is currently docked! Please undock before continuing.","Error","Cancel","Force Launch")
if(choice == "Cancel")
return 0
choice = alert("Forcing a shuttle launch while docked may result in severe injury, death and/or damage to property. Are you sure you wish to continue?", "Force Launch", "Force Launch", "Cancel")
if(choice == "Cancel")
return 0
return 1
// Props, for now.
/obj/structure/flight_left
name = "flight computer meters"
@@ -474,11 +497,18 @@
/obj/shuttle_connector/Initialize()
. = ..()
SSshuttles.OnDocksInitialized(CALLBACK(src, .proc/setup_routes))
GLOB.shuttle_added.register_global(src, .proc/setup_routes)
/obj/shuttle_connector/proc/setup_routes()
if(destinations && shuttle_name)
var/datum/shuttle/web_shuttle/ES = shuttle_controller.shuttles[shuttle_name]
/obj/shuttle_connector/Destroy()
GLOB.shuttle_added.unregister_global(src, .proc/setup_routes)
. = ..()
// This is called whenever a shuttle is initialized. If its our shuttle, do our thing!
/obj/shuttle_connector/proc/setup_routes(var/new_shuttle)
var/datum/shuttle/autodock/web_shuttle/ES = shuttle_controller.shuttles[shuttle_name]
if(ES != new_shuttle)
return // Its not our shuttle! Ignore!
if(destinations && istype(ES))
var/datum/shuttle_web_master/WM = ES.web_master
for(var/new_dest in destinations)
@@ -487,7 +517,9 @@
for(var/type_to_link in D.routes_to_make)
var/travel_delay = D.routes_to_make[type_to_link]
D.link_destinations(WM.get_destination_by_type(type_to_link), D.preferred_interim_area, travel_delay)
D.link_destinations(WM.get_destination_by_type(type_to_link), D.preferred_interim_tag, travel_delay)
else
warning("[log_info_line()]'s shuttle [global.log_info_line(ES)] initialized but destinations:[destinations]")
qdel(src)

View File

@@ -0,0 +1,124 @@
# "Landmark" Shuttles Conversion
This guide helps with updating maps and shuttle datums from the old "area" based system to the "landmark" based system.
## Summary
The old shuttle datum worked with areas (`/area`). You are probably familiar with every shuttle having a few cookie-cutter shaped areas it travels between.
When the shuttle "moved" it was translated from its current area to the destination area. The areas had to be _exactly_ the same shape so
that each turf in the origin area got translated to the equivalent place at the target.\
Since _all possible_ destinations had to have a dedicated area (and areas in BYOND can't overlap) this means it is impossible for two shuttles to travel to the same spot, even at different times.
In the new system shuttle destinations are represented by "landmark" objects (`/obj/effect/shuttle_landmark`).
When a shuttle is "moved" it is translated from its current landmark to the destination landmark, with each turf keeping its same position _relative_ to the landmarks.
In other words, whatever a turf's x/y/z offsets are from the origin landmark, it will be moved to the same x/y/z offset from the destination landmark.
## Landmark Objects
Shuttle destinations are represented by `/obj/effect/shuttle_landmark` objects on the map.
* `name` - Pretty name of the nav point, used on overmap and in messages and console UI.
* `landmark_tag` - Globally unique ID, used by everything else to refer to this landmark.
* `docking_controller` - ID of the controller on the dock side (initialize to id_tag, becomes reference). Leave null if not applicable.
* `base_area` - Type path of the `/area` that should be here when a shuttle is *not* present.
* `base_turf` - Type path of the `/turf` that should be here when a shuttle is *not* present.
* `shuttle_restricted` - If not null, only the named shuttle is allowed to use this landmark. (TODO: Overmap functionality)
* `flags` - Bitfield - defaults to zero, can be any combination of:
* `SLANDMARK_FLAG_AUTOSET` (1) - If set, will initialize base_area and base_turf to same as where it was spawned at.
* `SLANDMARK_FLAG_ZERO_G` (2) - If set, Zero-G shuttles moved here will lose gravity unless the area has ambient gravity.
* `special_dock_targets` - Used to configure shuttles with multiple docking controllers on the shuttle. Map of shuttle `name` -> `id_tag` of the docking controller it should use for this landmark. (Think of a shuttle with airlocks on both sides, each with their own controller. This would tell it which side to use.)
## Shuttle Types
### Ferry Shuttles
These shuttles go back and forth between two locations (normally called "station" and "offsite").
Examples: Mining shuttle, Arrivals Shuttle, etc.
Old Type Path: `/datum/shuttle/ferry`\
New Type Path: `/datum/shuttle/autodock/ferry`
##### New Vars:
Name|Type|Required?|Info
---|---|---|---
shuttle_area |`/area` typepath(s)|Yes| Can be a single path or list of paths.
##### Replaced vars:
Old|New|Required?|Info
:---:|:---:|:---:|---
area_station |landmark_station |Yes|Tag of the landmark for the "station" location.
area_offsite |landmark_offsite |Yes|Tag of the landmark for the "offsite" location.
area_transition |landmark_transition |No|Tag of the landmark for the "transition" location used during long_jump()
dock_target_station |On landmark |No|`id_tag` docking controller *on the dock* has been moved to the `docking_controller` var on the landmark_station landmark obj.
dock_target_offsite |On landmark |No|`id_tag` docking controller *on the dock* has been moved to the `docking_controller` var on the landmark_offsite landmark obj.
### Multi Shuttles
These shuttles go between a list of configured locations, one of which is its starting location.
Examples: Skipjack, Syndicate Shuttle
Old Type Path: `/datum/shuttle/multi_shuttle`\
New Type Path: `/datum/shuttle/autodock/multi`
##### New Vars:
Name|Type|Required?|Info
---|---|---|---
shuttle_area |`/area` typepath(s)|Yes| Can be a single path or list of paths.
##### Replaced vars:
Old|New|Required?|Info
:---:|:---:|:---:|---
origin |current_location |Yes|Tag of the landmark where the shuttle is at startup.
interim |landmark_transition |No|Tag of the landmark for the "transition" location used during long_jump()
start_location |N/A |No|No longer necessary, automatically determined from the value of `origin`
destinations |destination_tags |Yes|List of destinations the shuttle can travel to. Used to be associative list of *name* -> *area typepath*, now is normal list of landmark tag ids. Name is now read from the landmark obj.
destination_dock_targets|On landmarks |No|Used to be associative list of *name* -> *id_tag* for which docking controller *on the dock* to use at each destination. This is now specified by the `docking_controller` var on each landmark obj.
### Web Shuttles
These shuttles travel along a network of locations connected by routes. Instead of being able to travel to any of its destinations, it can only travel to destinations connected by a route to its current location. Added by Polaris as an upgrade to Multi Shuttles.
Note: While cool, it is likely that the upcoming "overmap" shuttles will be even cooler, and may eventually replace some web shuttles.
Examples: Southern Cross' Ninja Shuttle, Tether's Excursion Shuttle
Old Type Path: `/datum/shuttle/web_shuttle`\
New Type Path: `/datum/shuttle/autodock/web_shuttle`
##### New Vars:
Name|Type|Required?|Info
---|---|---|---
shuttle_area |`/area` typepath(s)|Yes| Can be a single path or list of paths.
##### Replaced vars:
Old|New|Required?|Info
:---:|:---:|:---:|---
current_area |current_location |Yes|Tag of the landmark where the shuttle is at startup.
#### Web Destination Configuration (`/datum/shuttle_destination`)
The network of routes for each web shuttle is configured by defining datums. These are mostly unchanged but use landmarks instead of areas now.
##### Replaced vars:
Old|New|Required?|Info
:---:|:---:|:---:|---
my_area |my_landmark |Yes|Tag of the landmark associated with this destination.
preferred_interim_area |preferred_interim_tag |No|Tag of the landmark for the "transition" location used during long_jump()
dock_target |On landmark |No|`id_tag` docking controller *on the dock* has been moved to the `docking_controller` var on the my_landmark landmark obj.
### Misc Shuttle Types
Other shuttle types that are either unused or unchanged in particular.
#### Escape Pods
Special case of ferry shuttles that use escape pod berth controllers.
Type path changed from `/datum/shuttle/ferry/escape_pod` to `/datum/shuttle/autodock/ferry/escape_pod`
Follow same instructions as for other ferry shuttles.
#### Multidock Ferry Shuttles
`/datum/shuttle/ferry/multidock` was a variant of ferry shuttles that could use a different docking port at each location.
Obsolete since is now natively supported by all dockable shuttles.

View File

@@ -8,7 +8,7 @@
/datum/shuttle_route
var/datum/shuttle_destination/start = null // One of the two sides of this route. Start just means it was the creator of this route.
var/datum/shuttle_destination/end = null // The second side.
var/area/interim = null // Where the shuttle sits during the movement. Make sure no other shuttle shares this or Very Bad Things will happen.
var/var/obj/effect/shuttle_landmark/interim // Where the shuttle sits during the movement. Make sure no other shuttle shares this or Very Bad Things will happen.
var/travel_time = 0 // How long it takes to move from start to end, or end to start. Set to 0 for instant travel.
var/one_way = FALSE // If true, you can't travel from end to start.
@@ -16,7 +16,7 @@
start = _start
end = _end
if(_interim)
interim = locate(_interim)
interim = SSshuttles.get_landmark(_interim)
travel_time = _time
one_way = _oneway
@@ -50,14 +50,12 @@
// This is the second datum, and contains information on all the potential destinations for a specific shuttle.
/datum/shuttle_destination
var/name = "a place" // Name of the destination, used for the flight computer.
var/area/my_area = null // Where the shuttle will move to when it actually arrives.
var/obj/effect/shuttle_landmark/my_landmark = null // Where the shuttle will move to when it actually arrives.
var/datum/shuttle_web_master/master = null // The datum that does the coordination with the actual shuttle datum.
var/list/routes = list() // Routes that are connected to this destination.
var/preferred_interim_area = null // When building a new route, use this interim area.
var/preferred_interim_tag = null // When building a new route, use interim landmark with this tag.
var/skip_me = FALSE // We will not autocreate this one. Some map must be doing it.
var/dock_target = null // The tag_id that the shuttle will use to try to dock to the destination, if able.
var/radio_announce = 0 // Whether it will make a station announcement (0) or a radio announcement (1).
var/announcer = null // The name of the 'announcer' that will say the arrival/departure messages. Defaults to the map's boss name if blank.
// var/arrival_message = null // Message said if the ship enters this destination. Not announced if the ship is cloaked.
@@ -72,7 +70,9 @@
var/list/routes_to_make = list()
/datum/shuttle_destination/New(var/new_master)
my_area = locate(my_area)
my_landmark = SSshuttles.get_landmark(my_landmark)
if(!my_landmark)
log_debug("Web shuttle destination '[name]' could not find its landmark '[my_landmark]'.")
master = new_master
/datum/shuttle_destination/Destroy()
@@ -99,7 +99,7 @@
// Now link our new destination to us.
var/travel_delay = destinations_to_create[type_to_make]
link_destinations(new_dest, preferred_interim_area, travel_delay)
link_destinations(new_dest, preferred_interim_tag, travel_delay)
to_world("SHUTTLES: [name] has linked themselves to [new_dest.name]")
to_world("SHUTTLES: [name] has finished building destinations. already_made list is \[[english_list(already_made)]\].")
@@ -135,14 +135,14 @@
else
global_announcer.autosay(get_arrival_message(),(announcer ? announcer : "[using_map.boss_name]"))
/datum/shuttle_destination/proc/link_destinations(var/datum/shuttle_destination/other_place, var/area/interim_area, var/travel_time = 0)
/datum/shuttle_destination/proc/link_destinations(var/datum/shuttle_destination/other_place, var/interim_tag, var/travel_time = 0)
// First, check to make sure this doesn't cause a duplicate route.
for(var/datum/shuttle_route/R in routes)
if(R.start == other_place || R.end == other_place)
return
// Now we can connect them.
var/datum/shuttle_route/new_route = new(src, other_place, interim_area, travel_time)
var/datum/shuttle_route/new_route = new(src, other_place, interim_tag, travel_time)
routes += new_route
other_place.routes += new_route
@@ -166,7 +166,7 @@
// This is the third and final datum, which coordinates with the shuttle datum to tell it where it is, where it can go, and how long it will take.
// It is also responsible for instancing all the destinations it has control over, and linking them together.
/datum/shuttle_web_master
var/datum/shuttle/web_shuttle/my_shuttle = null // Ref to the shuttle this datum is coordinating with.
var/datum/shuttle/autodock/web_shuttle/my_shuttle = null // Ref to the shuttle this datum is coordinating with.
var/datum/shuttle_destination/current_destination = null // Where the shuttle currently is. Bit of a misnomer.
var/datum/shuttle_destination/future_destination = null // Where it will be in the near future.
var/datum/shuttle_destination/starting_destination = null // Where the shuttle will start at, generally at the home base.
@@ -204,7 +204,7 @@
for(var/datum/shuttle_destination/D in destinations)
for(var/type_to_link in D.routes_to_make)
var/travel_delay = D.routes_to_make[type_to_link]
D.link_destinations(get_destination_by_type(type_to_link), D.preferred_interim_area, travel_delay)
D.link_destinations(get_destination_by_type(type_to_link), D.preferred_interim_tag, travel_delay)
/datum/shuttle_web_master/proc/on_shuttle_departure()
current_destination.exit()
@@ -214,11 +214,6 @@
future_destination.enter()
current_destination = future_destination
future_destination = null
my_shuttle.current_area = current_destination.my_area
/datum/shuttle_web_master/proc/current_dock_target()
if(current_destination)
return current_destination.dock_target
/datum/shuttle_web_master/proc/get_available_routes()
if(current_destination)
@@ -254,10 +249,11 @@
future_destination = R.get_other_side(current_destination)
var/travel_time = R.travel_time * my_shuttle.flight_time_modifier * 2 // Autopilot is less efficent than having someone flying manually.
// TODO - Leshana - Change this to use proccess stuff of autodock!
if(R.interim && R.travel_time > 0)
my_shuttle.long_jump(my_shuttle.current_area, future_destination.my_area, R.interim, travel_time / 10)
my_shuttle.long_jump(future_destination.my_landmark, R.interim, travel_time / 10)
else
my_shuttle.short_jump(my_shuttle.current_area, future_destination.my_area)
my_shuttle.short_jump(future_destination.my_landmark)
return TRUE // Note this will return before the shuttle actually arrives.
/datum/shuttle_web_master/proc/process_autopath()

View File

@@ -16,4 +16,6 @@
/obj/structure/table/darkglass/dismantle(obj/item/weapon/wrench/W, mob/user)
to_chat(user, "<span class='warning'>You cannot dismantle \the [src].</span>")
return
return
/obj/structure/table/alien/blue
icon = 'icons/turf/shuttle_alien_blue.dmi'

View File

@@ -18,8 +18,8 @@
<body>
<div id="loader">
<p>VChat is still loading. If you see this for a very long time, try the OOC 'Reload VChat' verb, or reconnecting.</p>
<p>Sometimes if you're still caching resources, it will take longer than usual.</p>
<p>You probably shouldn't see this page. This generally means chat is very broken.</p>
<p>You can wait a few seconds to see if it loads, or try OOC > Reload VChat.</p>
</div>
<div id="app" @mouseup="on_mouseup" style="display: none;" :class="{ inverted: inverted }">
@@ -41,7 +41,13 @@
<div class="item" @click="editmode" title="Edit Mode">
<i class="icon-cog-outline" :class="{ blinkwarn: editing }"></i>
</div>
<div class="item"><span class="ui circular label" title="VChat Latency (Not exactly network latency)" :class="ping_classes">{{latency}}ms</span></div>
<div class="item" title="Click to test latency">
<span class="ui circular label" :class="ping_classes" @click="do_latency_test">
<template v-if="latency">
{{latency}}ms
</template>
</span>
</div>
</div>
</div>
@@ -78,7 +84,6 @@
</div>
<div class="eight wide column">
<h3>Global Settings</h3>
<p>Clicking anywhere in VChat saves your settings.</p>
<div class="ui form" :class="{ inverted: inverted }">
<div class="grouped fields">
<div class="field">
@@ -116,19 +121,19 @@
<div class="inline fields">
<div class="field" title="Font size (Min 0.2, Max 5, Default 0.9)">
<label>Font Scale</label>
<input class="inputbox" type="number" name="fontsize" placeholder="0.9" v-model.lazy.number="fontsize" required>
<input class="inputbox" type="number" name="fontsize" placeholder="0.9" v-model.lazy.number="fontsize" required @keyup.enter="blur_this($event)">
</div>
</div>
<div class="inline fields">
<div class="field" title="Line height % (Min 100, Max 200, Default 130)">
<label>Line Height %</label>
<input class="inputbox" type="number" name="lineheight" placeholder="130" v-model.lazy.number="lineheight" required>
<input class="inputbox" type="number" name="lineheight" placeholder="130" v-model.lazy.number="lineheight" required @keyup.enter="blur_this($event)">
</div>
</div>
<div class="inline fields">
<div class="field" title="Hides messages for performance (Min 50, Max 2000, Default 200)">
<label># Stored Messages</label>
<input class="inputbox" type="number" name="showingnum" placeholder="200" v-model.lazy.number="showingnum" required>
<input class="inputbox" type="number" name="showingnum" placeholder="200" v-model.lazy.number="showingnum" required @keyup.enter="blur_this($event)">
</div>
</div>
</div>

View File

@@ -24,8 +24,7 @@
//Options for vchat
var vchat_opts = {
pingThisOften: 10000, //ms
pingDropsAllowed: 2,
msBeforeDropped: 30000, //No ping for this long, and the server must be gone
cookiePrefix: "vst-", //If you're another server, you can change this if you want.
alwaysShow: ["vc_looc", "vc_system"] //Categories to always display on every tab
};
@@ -75,11 +74,8 @@ var vchat_state = {
byond_ckey: null,
//Ping status
lastPingAttempt: 0,
lastPingReply: 0,
missedPings: 0,
latency: 0,
reconnecting: false,
lastPingReceived: 0,
latency_sent: 0,
//Last ID
lastId: 0
@@ -99,8 +95,7 @@ function start_vchat() {
doWinset("chatloadlabel", {"is-visible": false});
//Commence the pingening
send_ping();
setInterval(send_ping, vchat_opts.pingThisOften);
setInterval(check_ping, vchat_opts.msBeforeDropped);
//For fun
send_debug("VChat Loaded!");
@@ -125,6 +120,7 @@ function start_vue() {
editing: false, //If we're in settings edit mode
paused: false, //Autoscrolling
latency: 0, //Not necessarily network latency, since the game server has to align the responses into ticks
reconnecting: false, //If we've lost our connection
ext_styles: "", //Styles for chat downloaded files
is_admin: false,
@@ -261,6 +257,13 @@ function start_vue() {
}
},
watch: {
reconnecting: function(newSetting, oldSetting) {
if(newSetting == true && oldSetting == false) {
this.internal_message("Your client has lost connection to the server, or there is severe lag. Your client will reconnect if possible.");
} else if (newSetting == false && oldSetting == true) {
this.internal_message("Your client has reconnected to the server.");
}
},
//Save the inverted setting to LS
inverted: function (newSetting) {
set_storage("darkmode",newSetting);
@@ -335,11 +338,15 @@ function start_vue() {
},
//What color does the latency pip get?
ping_classes: function() {
if(this.latency === 0) { return "grey"; }
if(!this.latency) {
return this.reconnecting ? "red" : "green"; //Standard
}
if (this.latency == "?") { return "grey"; } //Waiting for latency test reply
else if(this.latency < 0 ) {return "red"; }
else if(this.latency <= 200) { return "green"; }
else if(this.latency <= 400) { return "yellow"; }
else { return "red"; }
else { return "grey"; }
},
current_categories: function() {
if(this.active_tab == this.tabs[0]) {
@@ -592,17 +599,46 @@ function start_vue() {
hiddenElement.href = 'data:attachment/text,' + encodeURI(textToSave);
hiddenElement.target = '_blank';
var filename = "chat_export.html";
var fileprefix = "log";
var extension =".html";
var now = new Date();
var hours = String(now.getHours());
if(hours.length < 2) {
hours = "0" + hours;
}
var minutes = String(now.getMinutes());
if(minutes.length < 2) {
minutes = "0" + minutes;
}
var dayofmonth = String(now.getDate());
if(dayofmonth.length < 2) {
dayofmonth = "0" + dayofmonth;
}
var month = String(now.getMonth()+1); //0-11
if(month.length < 2) {
month = "0" + month;
}
var year = String(now.getFullYear());
var datesegment = " "+year+"-"+month+"-"+dayofmonth+" ("+hours+" "+minutes+")";
var filename = fileprefix+datesegment+extension;
//Unlikely to work unfortunately, not supported in any version of IE, only Edge
if (hiddenElement.download !== undefined){
if (hiddenElement.download !== undefined) {
hiddenElement.download = filename;
hiddenElement.click();
//Probably what will end up getting used
} else {
let blob = new Blob([textToSave], {type: 'text/html;charset=utf8;'});
saved = window.navigator.msSaveBlob(blob, filename);
saved = window.navigator.msSaveOrOpenBlob(blob, filename);
}
},
do_latency_test: function() {
send_latency_check();
},
blur_this: function(event) {
event.target.blur();
}
}
});
@@ -613,21 +649,37 @@ function start_vue() {
* Actual Methods
*
************/
//Send a 'ping' to byond and check to see if we got the last one back in a timely manner
function send_ping() {
vchat_state.latency = (Math.min(Math.max(vchat_state.lastPingReply - vchat_state.lastPingAttempt, -1), 999));
//If their last reply was in the previous ping window or earlier.
if(vchat_state.latency < 0) {
vchat_state.missedPings++;
if((vchat_state.missedPings >= vchat_opts.pingDropsAllowed) && !vchat_state.reconnecting) {
system_message("Your client has lost connection with the server. It will reconnect automatically if possible.");
vchat_state.reconnecting = true;
function check_ping() {
var time_ago = Date.now() - vchat_state.lastPingReceived;
if(time_ago > vchat_opts.msBeforeDropped)
vueapp.reconnecting = true;
}
//Send a 'ping' to byond
function send_latency_check() {
if(vchat_state.latency_sent)
return;
vchat_state.latency_sent = Date.now();
vueapp.latency = "?";
push_Topic("ping");
setTimeout(function() {
if(vchat_state.latency_ms == "?") {
vchat_state.latency_ms = 999;
}
}, 1000); // 1 second to reply otherwise we mark it as bad
setTimeout(function() {
vchat_state.latency_sent = 0;
vueapp.latency = 0;
}, 5000); //5 seconds to display ping time overall
}
function get_latency_check() {
if(!vchat_state.latency_sent) {
return; //Too late
}
vueapp.latency = vchat_state.latency;
push_Topic("keepalive_client");
vchat_state.lastPingAttempt = Date.now();
vueapp.latency = Date.now() - vchat_state.latency_sent;
}
//We accept double-url-encoded JSON strings because Byond is garbage and UTF-8 encoded url_encode() text has crazy garbage in it.
@@ -678,7 +730,7 @@ function send_debug(message) {
//A side-channel to send events over that aren't just chat messages, if necessary.
function get_event(event) {
if(!vchat_state.ready) {
push_Topic('not_ready');
push_Topic("not_ready");
return;
}
@@ -704,10 +756,19 @@ function get_event(event) {
break;
//Just a ping.
case 'keepalive_server':
vchat_state.lastPingReply = Date.now();
vchat_state.missedPings = 0;
reconnecting = false;
case 'keepalive':
vchat_state.lastPingReceived = Date.now();
vueapp.reconnecting = false;
break;
//Response to a latency test.
case 'pong':
get_latency_check();
break;
//The server doesn't know if we're loaded or not (we bail above if we're not, so we must be).
case 'availability':
push_Topic("done_loading");
break;
default:

View File

@@ -46,7 +46,6 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
. = ..()
owner = C
update_vis()
/datum/chatOutput/Destroy()
owner = null
@@ -78,14 +77,20 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
become_broken()
return FALSE
//Could be loaded from a previous round, are you still there?
if(winget(owner,"outputwindow.htmloutput","is-visible") == "true") //Winget returns strings
send_event(event = list("evttype" = "availability"))
sleep(3 SECONDS)
if(!owner) // In case the client vanishes before winexists returns
qdel(src)
return FALSE
if(!resources_sent)
send_resources()
load()
if(!loaded)
update_vis()
if(!resources_sent)
send_resources()
load()
return TRUE
@@ -115,7 +120,7 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
owner.chatOutputLoadedAt = world.time
//update_vis() //It does it's own winsets
ping_cycle()
send_playerinfo()
load_database()
@@ -163,9 +168,23 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
/datum/chatOutput/proc/send_event(var/event, var/client/C = owner)
C << output(jsEncode(event), "htmloutput:get_event")
//Looping sleeping proc that just pings the client and dies when we die
/datum/chatOutput/proc/ping_cycle()
set waitfor = FALSE
while(!QDELING(src))
if(!owner)
qdel(src)
return
send_event(event = keep_alive())
sleep(20 SECONDS) //Make sure this makes sense with what the js client is expecting
//Just produces a message for using in keepalives from the server to the client
/datum/chatOutput/proc/keepalive()
return list("evttype" = "keepalive_server")
/datum/chatOutput/proc/keep_alive()
return list("evttype" = "keepalive")
//A response to a latency check from the client
/datum/chatOutput/proc/latency_check()
return list("evttype" = "pong")
//Redirected from client/Topic when the user clicks a link that pertains directly to the chat (when src == "chat")
/datum/chatOutput/Topic(var/href, var/list/href_list)
@@ -197,8 +216,8 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
CRASH("Tried to send a message to [owner.ckey] chatOutput before it was ready!")
if("done_loading")
data = done_loading(arglist(params))
if("keepalive_client")
data = keepalive(arglist(params))
if("ping")
data = latency_check(arglist(params))
if("ident")
data = bancheck(arglist(params))
if("unloading")

View File

@@ -25,7 +25,7 @@
/obj/vehicle/train/Initialize()
. = ..()
for(var/obj/vehicle/train/T in orange(1, src))
latch(T)
latch(T, null)
/obj/vehicle/train/Move()
var/old_loc = get_turf(src)
@@ -149,22 +149,26 @@
//Note: there is a modified version of this in code\modules\vehicles\cargo_train.dm specifically for cargo train engines
/obj/vehicle/train/proc/attach_to(obj/vehicle/train/T, mob/user)
if (get_dist(src, T) > 1)
to_chat(user, "<font color='red'>[src] is too far away from [T] to hitch them together.</font>")
if(user)
to_chat(user, "<font color='red'>[src] is too far away from [T] to hitch them together.</font>")
return
if (lead)
to_chat(user, "<font color='red'>[src] is already hitched to something.</font>")
if(user)
to_chat(user, "<font color='red'>[src] is already hitched to something.</font>")
return
if (T.tow)
to_chat(user, "<font color='red'>[T] is already towing something.</font>")
if(user)
to_chat(user, "<font color='red'>[T] is already towing something.</font>")
return
//check for cycles.
var/obj/vehicle/train/next_car = T
while (next_car)
if (next_car == src)
to_chat(user, "<font color='red'>That seems very silly.</font>")
if(user)
to_chat(user, "<font color='red'>That seems very silly.</font>")
return
next_car = next_car.lead