diff --git a/.travis.yml b/.travis.yml
index 8fb0e58f4e..e2bc4a5192 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,6 +34,7 @@ install:
script:
- shopt -s globstar
- (! grep 'step_[xy]' maps/**/*.dmm)
+ - (! grep -Pn '( |\t|;|{)tag( ?)=' maps/**/*.dmm)
- (! find nano/templates/ -type f -exec md5sum {} + | sort | uniq -D -w 32 | grep nano)
- (! grep -En "<\s*span\s+class\s*=\s*('[^'>]+|[^'>]+')\s*>" **/*.dm)
- (! grep 'maps\\.*test.*' *.dme)
diff --git a/code/__defines/MC.dm b/code/__defines/MC.dm
index 5620f51b3b..1d41a31209 100644
--- a/code/__defines/MC.dm
+++ b/code/__defines/MC.dm
@@ -27,6 +27,10 @@ if(current_step == this_step || (initial_step && !resumed)) /* So we start at st
#define MC_AVERAGE_FAST(average, current) (0.7 * (average) + 0.3 * (current))
#define MC_AVERAGE(average, current) (0.8 * (average) + 0.2 * (current))
#define MC_AVERAGE_SLOW(average, current) (0.9 * (average) + 0.1 * (current))
+
+#define MC_AVG_FAST_UP_SLOW_DOWN(average, current) (average > current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current))
+#define MC_AVG_SLOW_UP_FAST_DOWN(average, current) (average < current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current))
+
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
#define START_PROCESSING(Processor, Datum) if (!Datum.isprocessing) {Datum.isprocessing = 1;Processor.processing += Datum}
diff --git a/code/__defines/misc_vr.dm b/code/__defines/misc_vr.dm
index f8da0d70ce..ed2f27978a 100644
--- a/code/__defines/misc_vr.dm
+++ b/code/__defines/misc_vr.dm
@@ -31,4 +31,10 @@
#define BLUE_SHIELDED 2 // Shield from bluespace teleportation (telescience)
//Assistant/Visitor/Whatever
-#define USELESS_JOB "Visitor"
\ No newline at end of file
+#define USELESS_JOB "Visitor"
+
+// Bluespace shelter deploy checks
+#define SHELTER_DEPLOY_ALLOWED "allowed"
+#define SHELTER_DEPLOY_BAD_TURFS "bad turfs"
+#define SHELTER_DEPLOY_BAD_AREA "bad area"
+#define SHELTER_DEPLOY_ANCHORED_OBJECTS "anchored objects"
diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm
index 18c8cb1ca7..6d466e9930 100644
--- a/code/__defines/mobs.dm
+++ b/code/__defines/mobs.dm
@@ -306,3 +306,28 @@
//Some mob icon layering defines
#define BODY_LAYER -100
+
+// Clothing flags, organized in roughly top-bottom
+#define EXAMINE_SKIPHELMET 0x0001
+#define EXAMINE_SKIPEARS 0x0002
+#define EXAMINE_SKIPEYEWEAR 0x0004
+#define EXAMINE_SKIPMASK 0x0008
+#define EXAMINE_SKIPJUMPSUIT 0x0010
+#define EXAMINE_SKIPTIE 0x0020
+#define EXAMINE_SKIPHOLSTER 0x0040
+#define EXAMINE_SKIPSUITSTORAGE 0x0080
+#define EXAMINE_SKIPBACKPACK 0x0100
+#define EXAMINE_SKIPGLOVES 0x0200
+#define EXAMINE_SKIPBELT 0x0400
+#define EXAMINE_SKIPSHOES 0x0800
+
+// Body flags
+#define EXAMINE_SKIPHEAD 0x0001
+#define EXAMINE_SKIPEYES 0x0002
+#define EXAMINE_SKIPFACE 0x0004
+#define EXAMINE_SKIPBODY 0x0008
+#define EXAMINE_SKIPGROIN 0x0010
+#define EXAMINE_SKIPARMS 0x0020
+#define EXAMINE_SKIPHANDS 0x0040
+#define EXAMINE_SKIPLEGS 0x0080
+#define EXAMINE_SKIPFEET 0x0100
\ No newline at end of file
diff --git a/code/__defines/mobs_vr.dm b/code/__defines/mobs_vr.dm
index 0d32000341..eee6220f8b 100644
--- a/code/__defines/mobs_vr.dm
+++ b/code/__defines/mobs_vr.dm
@@ -26,3 +26,4 @@
#define SPECIES_XENOHYBRID "Xenomorph Hybrid"
#define SPECIES_ZORREN_FLAT "Flatland Zorren"
#define SPECIES_ZORREN_HIGH "Highlander Zorren"
+#define SPECIES_CUSTOM "Custom Species"
diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm
index 8460f14fce..92c5477a0b 100644
--- a/code/__defines/subsystems.dm
+++ b/code/__defines/subsystems.dm
@@ -28,6 +28,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
#define INIT_ORDER_ATOMS 15
#define INIT_ORDER_MACHINES 10
#define INIT_ORDER_SHUTTLES 3
+#define INIT_ORDER_DEFAULT 0
#define INIT_ORDER_LIGHTING 0
#define INIT_ORDER_AIR -1
#define INIT_ORDER_HOLOMAPS -5
@@ -37,8 +38,14 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
// Subsystem fire priority, from lowest to highest priority
// If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child)
-
- #define FIRE_PRIORITY_OVERLAYS 500
+#define FIRE_PRIORITY_SHUTTLES 5
+#define FIRE_PRIORITY_ORBIT 8
+#define FIRE_PRIORITY_GARBAGE 15
+#define FIRE_PRIORITY_AIRFLOW 30
+#define FIRE_PRIORITY_AIR 35
+#define FIRE_PRIORITY_DEFAULT 50
+#define FIRE_PRIORITY_MACHINES 100
+#define FIRE_PRIORITY_OVERLAYS 500
// Macro defining the actual code applying our overlays lists to the BYOND overlays list. (I guess a macro for speed)
// TODO - I don't really like the location of this macro define. Consider it. ~Leshana
diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm
index b0d1667fc4..903c7d3d68 100644
--- a/code/_helpers/global_lists.dm
+++ b/code/_helpers/global_lists.dm
@@ -32,7 +32,7 @@ var/global/list/all_species[0]
var/global/list/all_languages[0]
var/global/list/language_keys[0] // Table of say codes for all languages
var/global/list/whitelisted_species = list(SPECIES_HUMAN) // Species that require a whitelist check.
-var/global/list/playable_species = list("Custom Species", SPECIES_HUMAN) // A list of ALL playable species, whitelisted, latejoin or otherwise. //VOREStation Edit - Making sure custom species is obvious.
+var/global/list/playable_species = list(SPECIES_CUSTOM, SPECIES_HUMAN) // A list of ALL playable species, whitelisted, latejoin or otherwise. //VOREStation Edit - Making sure custom species is obvious.
var/list/mannequins_
diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm
index e69d047133..492fa8ed75 100644
--- a/code/_helpers/global_lists_vr.dm
+++ b/code/_helpers/global_lists_vr.dm
@@ -11,6 +11,8 @@ var/global/list/positive_traits = list() // Positive custom species traits, inde
var/global/list/traits_costs = list() // Just path = cost list, saves time in char setup
var/global/list/all_traits = list() // All of 'em at once (same instances)
+var/global/list/custom_species_bases = list() // Species that can be used for a Custom Species icon base
+
//stores numeric player size options indexed by name
var/global/list/player_sizes_list = list(
"Macro" = RESIZE_HUGE,
@@ -196,4 +198,14 @@ var/global/list/edible_trash = list(/obj/item/trash,
if(0.1 to INFINITY)
positive_traits[path] = instance
- return 1 // Hooks must return 1
\ No newline at end of file
+ // Custom species icon bases
+ var/list/blacklisted_icons = list(SPECIES_CUSTOM,SPECIES_PROMETHEAN) //Just ones that won't work well.
+ for(var/species_name in playable_species)
+ if(species_name in blacklisted_icons)
+ continue
+ var/datum/species/S = all_species[species_name]
+ if(S.spawn_flags & SPECIES_IS_WHITELISTED)
+ continue
+ custom_species_bases += species_name
+
+ return 1 // Hooks must return 1
diff --git a/code/_macros_vr.dm b/code/_macros_vr.dm
index ca7bccedbe..284b6623fc 100644
--- a/code/_macros_vr.dm
+++ b/code/_macros_vr.dm
@@ -1,3 +1,4 @@
-#define isbelly(A) istype(A, /obj/belly)
-#define isstorage(A) istype(A, /obj/item/weapon/storage)
-#define isitem(A) istype(A, /obj/item)
+#define isbelly(A) istype(A, /obj/belly)
+#define isstorage(A) istype(A, /obj/item/weapon/storage)
+#define isitem(A) istype(A, /obj/item)
+#define isTaurTail(A) istype(A, /datum/sprite_accessory/tail/taur)
diff --git a/code/_onclick/hud/other_mobs.dm b/code/_onclick/hud/other_mobs.dm
index 325a4a4b7e..f1b9165a26 100644
--- a/code/_onclick/hud/other_mobs.dm
+++ b/code/_onclick/hud/other_mobs.dm
@@ -93,9 +93,12 @@
return
-/mob/living/simple_animal/construct/instantiate_hud(var/datum/hud/HUD)
- HUD.construct_hud()
+/mob/living/simple_animal/construct/instantiate_hud(var/datum/hud/HUD)
+ ..(HUD)
+
+// HUD.construct_hud() //Archaic.
+/*
/datum/hud/proc/construct_hud()
var/constructtype
@@ -109,30 +112,28 @@
constructtype = "harvester"
if(constructtype)
- mymob.fire = new /obj/screen()
+
mymob.fire.icon = 'icons/mob/screen1_construct.dmi'
mymob.fire.icon_state = "fire0"
mymob.fire.name = "fire"
mymob.fire.screen_loc = ui_construct_fire
- mymob.healths = new /obj/screen()
mymob.healths.icon = 'icons/mob/screen1_construct.dmi'
mymob.healths.icon_state = "[constructtype]_health0"
mymob.healths.name = "health"
mymob.healths.screen_loc = ui_construct_health
- mymob.pullin = new /obj/screen()
mymob.pullin.icon = 'icons/mob/screen1_construct.dmi'
mymob.pullin.icon_state = "pull0"
mymob.pullin.name = "pull"
mymob.pullin.screen_loc = ui_construct_pull
- mymob.zone_sel = new /obj/screen/zone_sel()
+
mymob.zone_sel.icon = 'icons/mob/screen1_construct.dmi'
mymob.zone_sel.overlays.len = 0
mymob.zone_sel.overlays += image('icons/mob/zone_sel.dmi', "[mymob.zone_sel.selecting]")
- mymob.purged = new /obj/screen()
+
mymob.purged.icon = 'icons/mob/screen1_construct.dmi'
mymob.purged.icon_state = "purge0"
mymob.purged.name = "purged"
@@ -142,3 +143,4 @@
mymob.client.screen += list(mymob.fire, mymob.healths, mymob.pullin, mymob.zone_sel, mymob.purged)
mymob.client.screen += mymob.client.void
+*/
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index e2a4c28c6b..729330ddaa 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -92,9 +92,11 @@
if(spattack_min_range <= 1)
SpecialAtkTarget()
+
else if(melee_damage_upper == 0 && istype(A,/mob/living))
custom_emote(1,"[pick(friendly)] [A]!")
+
else
DoPunch(A)
diff --git a/code/controllers/Processes/planet.dm b/code/controllers/Processes/planet.dm
index e39b5e81b8..9ab541c80d 100644
--- a/code/controllers/Processes/planet.dm
+++ b/code/controllers/Processes/planet.dm
@@ -61,14 +61,40 @@ var/datum/controller/process/planet/planet_controller = null
//Sun light needs changing
if(P.needs_work & PLANET_PROCESS_SUN)
P.needs_work &= ~PLANET_PROCESS_SUN
- //Redraw sun overlay
- var/new_range = P.sun["range"]
+ // Remove old value from corners
+ var/list/sunlit_corners = P.sunlit_corners
+ var/old_lum_r = -P.sun["lum_r"]
+ var/old_lum_g = -P.sun["lum_g"]
+ var/old_lum_b = -P.sun["lum_b"]
+ if(old_lum_r || old_lum_g || old_lum_b)
+ for(var/C in P.sunlit_corners)
+ var/datum/lighting_corner/LC = C
+ LC.update_lumcount(old_lum_r, old_lum_g, old_lum_b)
+ SCHECK
+ sunlit_corners.Cut()
+
+ // Calculate new values to apply
var/new_brightness = P.sun["brightness"]
var/new_color = P.sun["color"]
- for(var/T in P.planet_floors)
- var/turf/simulated/turf = T
- turf.set_light(new_range, new_brightness, new_color)
+ var/lum_r = new_brightness * GetRedPart (new_color) / 255
+ var/lum_g = new_brightness * GetGreenPart(new_color) / 255
+ var/lum_b = new_brightness * GetBluePart (new_color) / 255
+ var/static/update_gen = -1 // Used to prevent double-processing corners. Otherwise would happen when looping over adjacent turfs.
+ for(var/I in P.planet_floors)
+ var/turf/simulated/T = I
+ if(!T.lighting_corners_initialised)
+ T.generate_missing_corners()
+ for(var/C in T.get_corners())
+ var/datum/lighting_corner/LC = C
+ if(LC.update_gen != update_gen && LC.active)
+ sunlit_corners += LC
+ LC.update_gen = update_gen
+ LC.update_lumcount(lum_r, lum_g, lum_b)
SCHECK
+ update_gen--
+ P.sun["lum_r"] = lum_r
+ P.sun["lum_g"] = lum_g
+ P.sun["lum_b"] = lum_b
//Temperature needs updating
if(P.needs_work & PLANET_PROCESS_TEMP)
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index 961f657b2d..997e0fd3e0 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -385,7 +385,7 @@ var/datum/controller/master/Master = new()
if (SS_flags & SS_NO_FIRE)
subsystemstocheck -= SS
continue
- if (!(SS_flags & SS_TICKER) && (SS_flags & SS_KEEP_TIMING) && SS.last_fire + (SS.wait * 0.75) > world.time)
+ if ((SS_flags & (SS_TICKER|SS_KEEP_TIMING)) == SS_KEEP_TIMING && SS.last_fire + (SS.wait * 0.75) > world.time)
continue
SS.enqueue()
. = 1
@@ -428,7 +428,7 @@ var/datum/controller/master/Master = new()
// in those cases, so we just let them run)
if (queue_node_flags & SS_NO_TICK_CHECK)
if (queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_ticker)
- queue_node.queued_priority += queue_priority_count * 0.10
+ queue_node.queued_priority += queue_priority_count * 0.1
queue_priority_count -= queue_node_priority
queue_priority_count += queue_node.queued_priority
current_tick_budget -= queue_node_priority
@@ -446,26 +446,32 @@ var/datum/controller/master/Master = new()
else
tick_precentage = tick_remaining
- current_ticklimit = TICK_USAGE + tick_precentage
+ // Reduce tick allocation for subsystems that overran on their last tick.
+ tick_precentage = max(tick_precentage*0.5, tick_precentage-queue_node.tick_overrun)
+
+ current_ticklimit = round(TICK_USAGE + tick_precentage)
if (!(queue_node_flags & SS_TICKER))
ran_non_ticker = TRUE
ran = TRUE
- tick_usage = TICK_USAGE
+
queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING)
last_type_processed = queue_node
queue_node.state = SS_RUNNING
+ tick_usage = TICK_USAGE
var/state = queue_node.ignite(queue_node_paused)
+ tick_usage = TICK_USAGE - tick_usage
+
if (state == SS_RUNNING)
state = SS_IDLE
current_tick_budget -= queue_node_priority
- tick_usage = TICK_USAGE - tick_usage
+
if (tick_usage < 0)
tick_usage = 0
-
+ queue_node.tick_overrun = max(0, MC_AVG_FAST_UP_SLOW_DOWN(queue_node.tick_overrun, tick_usage-tick_precentage))
queue_node.state = state
if (state == SS_PAUSED)
@@ -494,11 +500,11 @@ var/datum/controller/master/Master = new()
if (queue_node_flags & SS_TICKER)
queue_node.next_fire = world.time + (world.tick_lag * queue_node.wait)
else if (queue_node_flags & SS_POST_FIRE_TIMING)
- queue_node.next_fire = world.time + queue_node.wait
+ queue_node.next_fire = world.time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100))
else if (queue_node_flags & SS_KEEP_TIMING)
queue_node.next_fire += queue_node.wait
else
- queue_node.next_fire = queue_node.queued_time + queue_node.wait
+ queue_node.next_fire = queue_node.queued_time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100))
queue_node.queued_time = 0
diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm
index 437c10424b..dcb52a8cb3 100644
--- a/code/controllers/subsystem.dm
+++ b/code/controllers/subsystem.dm
@@ -1,9 +1,9 @@
/datum/controller/subsystem
// Metadata; you should define these.
name = "fire coderbus" //name of the subsystem
- var/init_order = 0 //order of initialization. Higher numbers are initialized first, lower numbers later. Can be decimal and negative values.
+ var/init_order = INIT_ORDER_DEFAULT //order of initialization. Higher numbers are initialized first, lower numbers later. Can be decimal and negative values.
var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer.
- var/priority = 50 //When mutiple subsystems need to run in the same tick, higher priority subsystems will run first and be given a higher share of the tick before MC_TICK_CHECK triggers a sleep
+ var/priority = FIRE_PRIORITY_DEFAULT //When mutiple subsystems need to run in the same tick, higher priority subsystems will run first and be given a higher share of the tick before MC_TICK_CHECK triggers a sleep
var/flags = 0 //see MC.dm in __DEFINES Most flags must be set on world start to take full effect. (You can also restart the mc to force them to process again)
var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire
@@ -17,6 +17,7 @@
var/next_fire = 0 //scheduled world.time for next fire()
var/cost = 0 //average time to execute
var/tick_usage = 0 //average tick usage
+ var/tick_overrun = 0 //average tick overrun
var/state = SS_IDLE //tracks the current state of the ss, running, paused, etc.
var/paused_ticks = 0 //ticks this ss is taking to run right now.
var/paused_tick_usage //total tick_usage of all of our runs while pausing this run
@@ -167,7 +168,7 @@
if(can_fire && !(SS_NO_FIRE & flags))
- msg = "[round(cost,1)]ms|[round(tick_usage,1)]%|[round(ticks,0.1)]\t[msg]"
+ msg = "[round(cost,1)]ms|[round(tick_usage,1)]%([round(tick_overrun,1)]%)|[round(ticks,0.1)]\t[msg]"
else
msg = "OFFLINE\t[msg]"
diff --git a/code/controllers/subsystems/air.dm b/code/controllers/subsystems/air.dm
index ef0173c33a..8cbe3afaab 100644
--- a/code/controllers/subsystems/air.dm
+++ b/code/controllers/subsystems/air.dm
@@ -9,7 +9,7 @@
SUBSYSTEM_DEF(air)
name = "Air"
init_order = INIT_ORDER_AIR
- priority = 35
+ priority = FIRE_PRIORITY_AIR
wait = 2 SECONDS // seconds (We probably can speed this up actually)
flags = SS_BACKGROUND // TODO - Should this really be background? It might be important.
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
@@ -224,6 +224,9 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
// ZAS might displace objects as the map loads if an air tick is processed mid-load.
/datum/controller/subsystem/air/StartLoadingMap(var/quiet = TRUE)
can_fire = FALSE
+ // Don't let map actually start loading if we are in the middle of firing
+ while(current_step)
+ stoplag()
. = ..()
/datum/controller/subsystem/air/StopLoadingMap(var/quiet = TRUE)
diff --git a/code/controllers/subsystems/airflow.dm b/code/controllers/subsystems/airflow.dm
index c98a02a5ce..4881113623 100644
--- a/code/controllers/subsystems/airflow.dm
+++ b/code/controllers/subsystems/airflow.dm
@@ -14,7 +14,7 @@ SUBSYSTEM_DEF(airflow)
wait = 2
flags = SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
- priority = 30
+ priority = FIRE_PRIORITY_AIRFLOW
var/list/processing = list()
var/list/currentrun = list()
diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm
index b4d43a8d3f..a741869b5b 100644
--- a/code/controllers/subsystems/garbage.dm
+++ b/code/controllers/subsystems/garbage.dm
@@ -3,7 +3,7 @@
//
SUBSYSTEM_DEF(garbage)
name = "Garbage"
- priority = 15
+ priority = FIRE_PRIORITY_GARBAGE
wait = 2 SECONDS
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
diff --git a/code/controllers/subsystems/machines.dm b/code/controllers/subsystems/machines.dm
index f8c353c43d..f579de9563 100644
--- a/code/controllers/subsystems/machines.dm
+++ b/code/controllers/subsystems/machines.dm
@@ -12,7 +12,7 @@
SUBSYSTEM_DEF(machines)
name = "Machines"
- priority = 100
+ priority = FIRE_PRIORITY_MACHINES
init_order = INIT_ORDER_MACHINES
flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
diff --git a/code/controllers/subsystems/mapping_vr.dm b/code/controllers/subsystems/mapping_vr.dm
index 6e095c6b62..0a94cc4634 100644
--- a/code/controllers/subsystems/mapping_vr.dm
+++ b/code/controllers/subsystems/mapping_vr.dm
@@ -9,14 +9,15 @@ SUBSYSTEM_DEF(mapping)
var/obj/effect/landmark/engine_loader/engine_loader
+ var/list/shelter_templates = list()
+
/datum/controller/subsystem/mapping/Recover()
flags |= SS_NO_INIT // Make extra sure we don't initialize twice.
+ shelter_templates = SSmapping.shelter_templates
/datum/controller/subsystem/mapping/Initialize(timeofday)
loadEngine()
- // TODO - This probably should be here
- // // Pick a random away mission.
- // createRandomZlevel()
+ preloadShelterTemplates()
// Mining generation probably should be here too
// TODO - Other stuff related to maps and areas could be moved here too. Look at /tg
if(using_map)
@@ -88,6 +89,15 @@ SUBSYSTEM_DEF(mapping)
else
MT.load_new_z(centered = FALSE)
+/datum/controller/subsystem/mapping/proc/preloadShelterTemplates()
+ for(var/item in subtypesof(/datum/map_template/shelter))
+ var/datum/map_template/shelter/shelter_type = item
+ if(!(initial(shelter_type.mappath)))
+ continue
+ var/datum/map_template/shelter/S = new shelter_type()
+
+ shelter_templates[S.shelter_id] = S
+
/datum/controller/subsystem/mapping/stat_entry(msg)
if (!Debug2)
return // Only show up in stat panel if debugging is enabled.
diff --git a/code/controllers/subsystems/orbits.dm b/code/controllers/subsystems/orbits.dm
index 80c16c9ce5..5cda1ce9dc 100644
--- a/code/controllers/subsystems/orbits.dm
+++ b/code/controllers/subsystems/orbits.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(orbit)
name = "Orbits"
- priority = 8 // FIRE_PRIORITY_ORBIT
+ priority = FIRE_PRIORITY_ORBIT
wait = 2
flags = SS_NO_INIT|SS_TICKER
diff --git a/code/controllers/subsystems/shuttles.dm b/code/controllers/subsystems/shuttles.dm
index 8751e0ffd7..61a4590890 100644
--- a/code/controllers/subsystems/shuttles.dm
+++ b/code/controllers/subsystems/shuttles.dm
@@ -8,7 +8,7 @@ var/global/datum/controller/subsystem/shuttles/shuttle_controller
SUBSYSTEM_DEF(shuttles)
name = "Shuttles"
wait = 2 SECONDS
- priority = 5
+ priority = FIRE_PRIORITY_SHUTTLES
init_order = INIT_ORDER_SHUTTLES
flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK
runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm
index ada64c0b80..a9ba96c3fa 100644
--- a/code/datums/helper_datums/teleport.dm
+++ b/code/datums/helper_datums/teleport.dm
@@ -1,5 +1,5 @@
//wrapper
-/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null)
+/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit
new /datum/teleport/instant/science(arglist(args))
return
@@ -12,15 +12,16 @@
var/soundin //soundfile to play before teleportation
var/soundout //soundfile to play after teleportation
var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation)
+ var/local = TRUE //VOREStation Add - If false, can teleport from/to any z-level
-/datum/teleport/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null)
+/datum/teleport/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit
..()
if(!initTeleport(arglist(args)))
return 0
return 1
-/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout)
+/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout,local) //VOREStation Edit
if(!setTeleatom(ateleatom))
return 0
if(!setDestination(adestination))
@@ -30,6 +31,7 @@
setEffects(aeffectin,aeffectout)
setForceTeleport(afteleport)
setSounds(asoundin,asoundout)
+ src.local = local // VOREStation Add
return 1
//must succeed
@@ -186,7 +188,7 @@
return 0
*/ //VOREStation Removal End
//VOREStation Edit Start
- if(destination.z in using_map.player_levels)
+ if(!local || (destination.z in using_map.player_levels)) //VOREStation Edit
return 1
if(isbelly(destination.loc)) //if we're going to televore
var/obj/belly/B = destination.loc
diff --git a/code/datums/supplypacks/hospitality_vr.dm b/code/datums/supplypacks/hospitality_vr.dm
index 1f69ef1151..a8fb518233 100644
--- a/code/datums/supplypacks/hospitality_vr.dm
+++ b/code/datums/supplypacks/hospitality_vr.dm
@@ -5,7 +5,7 @@
/obj/item/weapon/reagent_containers/food/snacks/cheeseburger,
/obj/item/weapon/reagent_containers/food/snacks/jellyburger,
/obj/item/weapon/reagent_containers/food/snacks/tofuburger,
- /obj/item/weapon/reagent_containers/food/snacks/fishburger
+ /obj/item/weapon/reagent_containers/food/snacks/fries
)
name = "Burger crate"
cost = 25
diff --git a/code/datums/supplypacks/science_vr.dm b/code/datums/supplypacks/science_vr.dm
index 327bc71798..595d2eeba2 100644
--- a/code/datums/supplypacks/science_vr.dm
+++ b/code/datums/supplypacks/science_vr.dm
@@ -28,6 +28,7 @@
/obj/item/weapon/disk/limb/dsi_vulpkanin,
/obj/item/weapon/disk/limb/dsi_akula,
/obj/item/weapon/disk/limb/dsi_spider,
+ /obj/item/weapon/disk/limb/dsi_teshari,
/obj/item/weapon/disk/limb/eggnerdltd,
/obj/item/weapon/disk/limb/eggnerdltdred
)
diff --git a/code/datums/supplypacks/voidsuits.dm b/code/datums/supplypacks/voidsuits.dm
index dda9aa4ed4..699c8ce9ee 100644
--- a/code/datums/supplypacks/voidsuits.dm
+++ b/code/datums/supplypacks/voidsuits.dm
@@ -172,6 +172,7 @@
cost = 40
containertype = "/obj/structure/closet/crate/secure"
containername = "Security Crowd Control voidsuit crate"
+ access = access_armory
/datum/supply_packs/voidsuits/security/alt
name = "Security EVA Riot voidsuits"
@@ -185,6 +186,7 @@
cost = 50
containertype = "/obj/structure/closet/crate/secure"
containername = "Security EVA Riot voidsuit crate"
+ access = access_armory
/datum/supply_packs/voidsuits/supply
name = "Mining voidsuits"
diff --git a/code/game/antagonist/station/renegade.dm b/code/game/antagonist/station/renegade.dm
index 3bf53e55dc..6f7ec89322 100644
--- a/code/game/antagonist/station/renegade.dm
+++ b/code/game/antagonist/station/renegade.dm
@@ -17,6 +17,7 @@ var/datum/antagonist/renegade/renegades
Think through your actions and make the roleplay immersive! Please remember all \
rules aside from those without explicit exceptions apply to antagonists."
flags = ANTAG_SUSPICIOUS | ANTAG_IMPLANT_IMMUNE | ANTAG_RANDSPAWN | ANTAG_VOTABLE
+ can_use_aooc = FALSE
hard_cap = 8
hard_cap_round = 12
diff --git a/code/game/antagonist/station/thug.dm b/code/game/antagonist/station/thug.dm
index 2ea752e3b7..8810fca7d0 100644
--- a/code/game/antagonist/station/thug.dm
+++ b/code/game/antagonist/station/thug.dm
@@ -15,3 +15,4 @@ var/datum/antagonist/thug/thugs
Think through your actions and make the roleplay immersive! Please remember all \
rules aside from those with explicit exceptions apply to antagonists."
flags = ANTAG_SUSPICIOUS | ANTAG_IMPLANT_IMMUNE | ANTAG_RANDSPAWN | ANTAG_VOTABLE
+ can_use_aooc = FALSE
diff --git a/code/game/antagonist/station/traitor.dm b/code/game/antagonist/station/traitor.dm
index 4c96f5ce17..d0a1f54ed2 100644
--- a/code/game/antagonist/station/traitor.dm
+++ b/code/game/antagonist/station/traitor.dm
@@ -5,6 +5,7 @@ var/datum/antagonist/traitor/traitors
id = MODE_TRAITOR
protected_jobs = list("Security Officer", "Warden", "Detective", "Internal Affairs Agent", "Head of Security", "Colony Director")
flags = ANTAG_SUSPICIOUS | ANTAG_RANDSPAWN | ANTAG_VOTABLE
+ can_use_aooc = FALSE
/datum/antagonist/traitor/auto
id = MODE_AUTOTRAITOR
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index bbecbe5f97..4f17952e38 100755
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -497,7 +497,7 @@ area/space/atmosalert()
//names are used
/area/syndicate_station
- name = "\improper Independant Station"
+ name = "\improper Independent Station"
icon_state = "yellow"
requires_power = 0
flags = RAD_SHIELDED
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 5311d642a2..48ffacb335 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -18,6 +18,7 @@
var/icon_scale = 1 // Used to scale icons up or down in update_transform().
var/old_x = 0
var/old_y = 0
+ var/datum/riding/riding_datum //VOREStation Add - Moved from /obj/vehicle
/atom/movable/Destroy()
. = ..()
@@ -37,6 +38,7 @@
if (pulledby.pulling == src)
pulledby.pulling = null
pulledby = null
+ qdel_null(riding_datum) //VOREStation Add
/atom/movable/Bump(var/atom/A, yes)
if(src.throwing)
diff --git a/code/game/atoms_movable_vr.dm b/code/game/atoms_movable_vr.dm
index d529122029..0cc6d144be 100644
--- a/code/game/atoms_movable_vr.dm
+++ b/code/game/atoms_movable_vr.dm
@@ -1,2 +1,12 @@
/atom/movable/proc/Bump_vr(var/atom/A, yes)
return
+
+/atom/movable/set_dir(newdir)
+ . = ..(newdir)
+ if(riding_datum)
+ riding_datum.handle_vehicle_offsets()
+
+/atom/movable/relaymove(mob/user, direction)
+ . = ..()
+ if(riding_datum)
+ riding_datum.handle_ride(user, direction)
diff --git a/code/game/gamemodes/cult/construct_spells.dm b/code/game/gamemodes/cult/construct_spells.dm
new file mode 100644
index 0000000000..f8ff57fc35
--- /dev/null
+++ b/code/game/gamemodes/cult/construct_spells.dm
@@ -0,0 +1,698 @@
+//cast_method flags, needs to be up to date with Technomancer's. They were, for some reason, not working outside it.
+#define CAST_USE 1 // Clicking the spell in your hand.
+#define CAST_MELEE 2 // Clicking an atom in melee range.
+#define CAST_RANGED 4 // Clicking an atom beyond melee range.
+#define CAST_THROW 8 // Throwing the spell and hitting an atom.
+#define CAST_COMBINE 16 // Clicking another spell with this spell.
+#define CAST_INNATE 32 // Activates upon verb usage, used for mobs without hands.
+
+//Aspects
+#define ASPECT_FIRE "fire" //Damage over time and raising body-temp. Firesuits protect from this.
+#define ASPECT_FROST "frost" //Slows down the affected, also involves imbedding with icicles. Winter coats protect from this.
+#define ASPECT_SHOCK "shock" //Energy-expensive, usually stuns. Insulated armor protects from this.
+#define ASPECT_AIR "air" //Mostly involves manipulation of atmos, useless in a vacuum. Magboots protect from this.
+#define ASPECT_FORCE "force" //Manipulates gravity to push things away or towards a location.
+#define ASPECT_TELE "tele" //Teleportation of self, other objects, or other people.
+#define ASPECT_DARK "dark" //Makes all those photons vanish using magic-- WITH SCIENCE. Used for sneaky stuff.
+#define ASPECT_LIGHT "light" //The opposite of dark, usually blinds, makes holo-illusions, or makes laser lightshows.
+#define ASPECT_BIOMED "biomed" //Mainly concerned with healing and restoration.
+#define ASPECT_EMP "emp" //Unused now.
+#define ASPECT_UNSTABLE "unstable" //Heavily RNG-based, causes instability to the victim.
+#define ASPECT_CHROMATIC "chromatic" //Used to combine with other spells.
+#define ASPECT_UNHOLY "unholy" //Involves the dead, blood, and most things against divine beings.
+
+//////////////////////////////Construct Spells/////////////////////////
+
+proc/findNullRod(var/atom/target)
+ if(istype(target,/obj/item/weapon/nullrod))
+ return 1
+ else if(target.contents)
+ for(var/atom/A in target.contents)
+ if(findNullRod(A))
+ return 1
+ return 0
+
+/spell/aoe_turf/conjure/construct
+ name = "Artificer"
+ desc = "This spell conjures a construct which may be controlled by Shades"
+
+ school = "conjuration"
+ charge_max = 600
+ spell_flags = 0
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ summon_type = list(/obj/structure/constructshell)
+
+ hud_state = "artificer"
+
+/spell/aoe_turf/conjure/construct/lesser
+ charge_max = 1800
+ summon_type = list(/obj/structure/constructshell/cult)
+ hud_state = "const_shell"
+ override_base = "const"
+
+/spell/aoe_turf/conjure/floor
+ name = "Floor Construction"
+ desc = "This spell constructs a cult floor"
+
+ charge_max = 20
+ spell_flags = Z2NOCAST | CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+ summon_type = list(/turf/simulated/floor/cult)
+
+ hud_state = "const_floor"
+
+/spell/aoe_turf/conjure/floor/conjure_animation(var/atom/movable/overlay/animation, var/turf/target)
+ animation.icon_state = "cultfloor"
+ flick("cultfloor",animation)
+ spawn(10)
+ qdel(animation)
+
+/spell/aoe_turf/conjure/wall
+ name = "Lesser Construction"
+ desc = "This spell constructs a cult wall"
+
+ charge_max = 100
+ spell_flags = Z2NOCAST | CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+ summon_type = list(/turf/simulated/wall/cult)
+
+ hud_state = "const_wall"
+
+/spell/aoe_turf/conjure/wall/conjure_animation(var/atom/movable/overlay/animation, var/turf/target)
+ animation.icon_state = "cultwall"
+ flick("cultwall",animation)
+ spawn(10)
+ qdel(animation)
+
+/spell/aoe_turf/conjure/wall/reinforced
+ name = "Greater Construction"
+ desc = "This spell constructs a reinforced metal wall"
+
+ charge_max = 300
+ spell_flags = Z2NOCAST
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+ cast_delay = 50
+
+ summon_type = list(/turf/simulated/wall/r_wall)
+
+/spell/aoe_turf/conjure/soulstone
+ name = "Summon Soulstone"
+ desc = "This spell reaches into Nar-Sie's realm, summoning one of the legendary fragments across time and space"
+
+ charge_max = 3000
+ spell_flags = 0
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ summon_type = list(/obj/item/device/soulstone)
+
+ hud_state = "const_stone"
+ override_base = "const"
+
+/spell/aoe_turf/conjure/pylon
+ name = "Red Pylon"
+ desc = "This spell conjures a fragile crystal from Nar-Sie's realm. Makes for a convenient light source."
+
+ charge_max = 200
+ spell_flags = CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ summon_type = list(/obj/structure/cult/pylon)
+
+ hud_state = "const_pylon"
+
+/spell/aoe_turf/conjure/pylon/cast(list/targets)
+ ..()
+ var/turf/spawn_place = pick(targets)
+ for(var/obj/structure/cult/pylon/P in spawn_place.contents)
+ if(P.isbroken)
+ P.repair(usr)
+ continue
+ return
+
+/spell/aoe_turf/conjure/door
+ name = "Stone Door"
+ desc = "This spell conjures a massive stone door."
+
+ charge_max = 100
+ spell_flags = CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ summon_type = list(/obj/structure/simple_door/cult)
+
+ hud_state = "const_door"
+
+/spell/aoe_turf/conjure/grille
+ name = "Arcane Grille"
+ desc = "This spell conjures an airtight grille."
+
+ charge_max = 100
+ spell_flags = CONSTRUCT_CHECK
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+
+ summon_type = list(/obj/structure/grille/cult)
+
+ hud_state = "const_grille"
+
+/spell/aoe_turf/conjure/forcewall/lesser
+ name = "Shield"
+ desc = "Allows you to pull up a shield to protect yourself and allies from incoming threats"
+
+ charge_max = 300
+ spell_flags = 0
+ invocation = "none"
+ invocation_type = SpI_NONE
+ range = 0
+ summon_type = list(/obj/effect/forcefield/cult)
+ duration = 200
+
+ hud_state = "const_juggwall"
+
+//Code for the Juggernaut construct's forcefield, that seemed like a good place to put it.
+/obj/effect/forcefield/cult
+ desc = "That eerie looking obstacle seems to have been pulled from another dimension through sheer force"
+ name = "Juggerwall"
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "m_shield_cult"
+ light_color = "#B40000"
+ light_range = 2
+
+/obj/effect/forcefield/cult/cultify()
+ return
+
+/spell/aoe_turf/knock/harvester
+ name = "Force Doors"
+ desc = "Mortal portals are no match for your occult might."
+
+ spell_flags = CONSTRUCT_CHECK
+
+ charge_max = 100
+ invocation = ""
+ invocation_type = "silent"
+ range = 4
+
+ hud_state = "const_knock"
+
+/spell/aoe_turf/knock/harvester/cast(list/targets)
+/* for(var/turf/T in targets) //Disintigrating doors is bad, okay.
+ for(var/obj/machinery/door/door in T.contents)
+ spawn door.cultify()
+ return */
+ for(var/turf/T in targets)
+ for(var/obj/machinery/door/door in T.contents)
+ spawn(1)
+ if(istype(door,/obj/machinery/door/airlock))
+ var/obj/machinery/door/airlock/AL = door
+ AL.locked = 0 //The spirits of the damned care not for your locks.
+ AL.welded = 0 //Or your welding tools.
+ else if(istype(door, /obj/machinery/door/firedoor))
+ var/obj/machinery/door/firedoor/FD = door
+ FD.blocked = 0
+ door.open(1)
+ return
+
+/*
+ *
+ * Self-targeting spells. Modifiers, auras, instants, etc.
+ *
+ */
+
+/spell/targeted/ethereal_jaunt/shift
+ name = "Phase Shift"
+ desc = "This spell allows you to pass through walls"
+
+ charge_max = 200
+ spell_flags = Z2NOCAST | INCLUDEUSER | CONSTRUCT_CHECK
+ invocation_type = SpI_NONE
+ range = -1
+ duration = 50 //in deciseconds
+
+ hud_state = "const_shift"
+
+/spell/targeted/ethereal_jaunt/shift/jaunt_disappear(var/atom/movable/overlay/animation, var/mob/living/target)
+ animation.icon_state = "phase_shift"
+ animation.dir = target.dir
+ flick("phase_shift",animation)
+
+/spell/targeted/ethereal_jaunt/shift/jaunt_reappear(var/atom/movable/overlay/animation, var/mob/living/target)
+ animation.icon_state = "phase_shift2"
+ animation.dir = target.dir
+ flick("phase_shift2",animation)
+
+/spell/targeted/ethereal_jaunt/shift/jaunt_steam(var/mobloc)
+ return
+
+/*
+ * Harvest has been disabled due to the lack of Nar'Sie. Here for posterity / future rework.
+ */
+
+/*
+/spell/targeted/harvest
+ name = "Harvest"
+ desc = "Back to where I come from, and you're coming with me."
+
+ school = "transmutation"
+ charge_max = 200
+ spell_flags = Z2NOCAST | CONSTRUCT_CHECK | INCLUDEUSER
+ invocation = ""
+ invocation_type = SpI_NONE
+ range = 0
+ max_targets = 0
+
+ overlay = 1
+ overlay_icon = 'icons/effects/effects.dmi'
+ overlay_icon_state = "rune_teleport"
+ overlay_lifespan = 0
+
+ hud_state = "const_harvest"
+
+/spell/targeted/harvest/cast(list/targets, mob/user)//because harvest is already a proc
+ ..()
+
+ var/destination = null
+ for(var/obj/singularity/narsie/large/N in narsie_list)
+ destination = N.loc
+ break
+ if(destination)
+ var/prey = 0
+ for(var/mob/living/M in targets)
+ if(!findNullRod(M))
+ M.forceMove(destination)
+ if(M != user)
+ prey = 1
+ user << "You warp back to Nar-Sie[prey ? " along with your prey":""]."
+ else
+ user << "...something's wrong!"//There shouldn't be an instance of Harvesters when Nar-Sie isn't in the world.
+*/
+
+/spell/targeted/fortify
+ name = "Fortify Shell"
+ desc = "Emit a field of energy around your shell to reduce incoming damage incredibly, while decreasing your mobility."
+
+ range = -1
+ school = "evocation"
+ charge_type = Sp_RECHARGE
+ invocation_type = SpI_NONE
+
+ spell_flags = CONSTRUCT_CHECK | INCLUDEUSER
+
+ hud_state = "const_fortify"
+ smoke_amt = 0
+
+ charge_max = 600
+
+/spell/targeted/fortify/cast(list/targets, mob/living/user)
+ if(findNullRod(user) || user.has_modifier_of_type(/datum/modifier/fortify))
+ charge_counter = 400
+ return
+ user.add_modifier(/datum/modifier/fortify, 1 MINUTES)
+
+/spell/targeted/occult_repair_aura
+ name = "Repair Aura"
+ desc = "Emit a field of energy around your shell to repair nearby constructs at range."
+
+ range = -1
+ school = "evocation"
+ charge_type = Sp_RECHARGE
+ invocation_type = SpI_NONE
+
+ spell_flags = CONSTRUCT_CHECK | INCLUDEUSER
+
+ hud_state = "const_repairaura"
+ smoke_amt = 0
+
+ charge_max = 600
+
+/spell/targeted/occult_repair_aura/cast(list/targets, mob/living/user)
+ if(findNullRod(user) || user.has_modifier_of_type(/datum/modifier/repair_aura))
+ charge_counter = 300
+ return
+ user.add_modifier(/datum/modifier/repair_aura, 30 SECONDS)
+
+/spell/targeted/ambush_mode
+ name = "Toggle Ambush"
+ desc = "Phase yourself mostly out of this reality, minimizing your combat ability, but allowing for employance of ambush tactics."
+
+ range = -1
+ school = "evocation"
+ charge_type = Sp_RECHARGE
+ invocation_type = SpI_NONE
+
+ spell_flags = CONSTRUCT_CHECK | INCLUDEUSER
+
+ hud_state = "const_ambush"
+ smoke_amt = 0
+
+ charge_max = 100
+
+/spell/targeted/ambush_mode/cast(list/targets, mob/living/user)
+ if(findNullRod(user))
+ charge_counter = 50
+ return
+ if(user.has_modifier_of_type(/datum/modifier/ambush))
+ user.remove_modifiers_of_type(/datum/modifier/ambush)
+ return
+ user.add_modifier(/datum/modifier/ambush, 0)
+
+/*
+ *
+ * These are the spells that place spell-objects into the construct's hands akin to technomancers.
+ *
+ */
+
+/spell/targeted/construct_advanced
+ name = "Base Construct Spell"
+ desc = "If you see this, please tell a developer!"
+
+ range = -1
+ school = "evocation"
+ charge_type = Sp_RECHARGE
+ invocation_type = SpI_NONE
+
+ spell_flags = CONSTRUCT_CHECK | INCLUDEUSER
+
+ hud_state = "const_rune"
+ smoke_amt = 0
+
+ charge_max = 10
+
+ var/obj/item/weapon/spell/construct/spell_obj = null //This is the var that determines what Technomancer-style spell is put into their hands.
+
+/spell/targeted/construct_advanced/cast(list/targets, mob/living/user)
+ if(!findNullRod(user))
+ user.place_spell_in_hand(spell_obj)
+
+/spell/targeted/construct_advanced/inversion_beam
+ name = "Inversion Beam"
+ desc = "Fire a searing beam of darkness at your foes."
+
+ hud_state = "const_beam"
+ spell_obj = /obj/item/weapon/spell/construct/projectile/inverted_beam
+
+/spell/targeted/construct_advanced/mend_acolyte
+ name = "Mend Acolyte"
+ desc = "Mend a target acolyte or construct over time."
+
+ charge_max = 100
+
+ hud_state = "const_mend"
+ spell_obj = /obj/item/weapon/spell/construct/mend_occult
+
+/spell/targeted/construct_advanced/agonizing_sphere
+ name = "Sphere of Agony"
+ desc = "Rend a portal into a plane of naught but pain at the target location."
+
+ charge_max = 100
+
+ hud_state = "const_harvest"
+ spell_obj = /obj/item/weapon/spell/construct/spawner/agonizing_sphere
+
+/spell/targeted/construct_advanced/slam
+ name = "Slam"
+ desc = "Empower your FIST."
+
+ charge_max = 300
+
+ hud_state = "const_fist"
+ spell_obj = /obj/item/weapon/spell/construct/slam
+
+/*
+ *
+ * These are the spell objects that go into the construct's hands.
+ *
+ */
+
+/*
+ * Base advanced construct spell types.
+ */
+
+/obj/item/weapon/spell/construct //Energy costs are in units of blood, in the event a cultist gets one of these.
+ name = "unholy energy"
+ desc = "Your hands appear to be screaming. This is a debug text, you should probably tell a developer!"
+ icon = 'icons/obj/spells.dmi'
+ icon_state = "generic"
+ item_icons = list(
+ slot_l_hand_str = 'icons/mob/items/lefthand_spells.dmi',
+ slot_r_hand_str = 'icons/mob/items/righthand_spells.dmi',
+ )
+ throwforce = 0
+ force = 0
+ show_examine = FALSE
+ owner = null
+ core = null
+ cast_methods = null // Controls how the spell is casted.
+ aspect = ASPECT_UNHOLY // Used for combining spells. Pretty much any cult spell is unholy.
+ toggled = 0 // Mainly used for overlays.
+ cooldown = 0 // If set, will add a cooldown overlay and adjust click delay. Must be a multiple of 5 for overlays.
+ cast_sound = null // Sound file played when this is used.
+ var/last_castcheck = null // The last time this spell was cast.
+
+/obj/item/weapon/spell/construct/New()
+ //..() //This kills the spell, because super on this calls the default spell's New, which checks for a core. Can't have that.
+ if(isliving(loc))
+ owner = loc
+ if(!owner)
+ qdel(src)
+ update_icon()
+
+/obj/item/weapon/spell/construct/adjust_instability(var/amount) //The only drawback to the boons of the geometer is the use of a mortal's blood as fuel. Constructs have already paid that price long ago.
+ return
+
+/obj/item/weapon/spell/construct/run_checks()
+ if(owner)
+ if((iscultist(owner) || istype(owner, /mob/living/simple_animal/construct)) && (world.time >= (last_castcheck + cooldown))) //Are they a cultist or a construct, and has the cooldown time passed?
+ last_castcheck = world.time
+ return 1
+ return 0
+
+/obj/item/weapon/spell/construct/pay_energy(var/amount)
+ if(owner)
+ if(istype(owner, /mob/living/simple_animal/construct))
+ return 1
+ if(iscultist(owner) && pay_blood(amount))
+ return 1
+ return 0
+
+/obj/item/weapon/spell/construct/proc/pay_blood(var/amount) //If, for some reason, this is put into the hands of a cultist, by a talisnam or whatever.
+ if(ishuman(owner))
+ var/mob/living/carbon/human/H = owner
+ if(!H.should_have_organ(O_HEART))
+ return 1
+ if(H.vessel.remove_reagent("blood", amount))
+ return 1
+ return 0
+
+/obj/item/weapon/spell/construct/afterattack(atom/target, mob/user, proximity_flag, click_parameters) //Not overriding it caused runtimes, because cooldown checked for core.
+ if(!run_checks())
+ return
+ if(!proximity_flag)
+ if(cast_methods & CAST_RANGED)
+ on_ranged_cast(target, user)
+ else
+ if(istype(target, /obj/item/weapon/spell))
+ var/obj/item/weapon/spell/spell = target
+ if(spell.cast_methods & CAST_COMBINE)
+ spell.on_combine_cast(src, user)
+ return
+ if(cast_methods & CAST_MELEE)
+ on_melee_cast(target, user)
+ else if(cast_methods & CAST_RANGED) //Try to use a ranged method if a melee one doesn't exist.
+ on_ranged_cast(target, user)
+ if(cooldown)
+ var/effective_cooldown = round(cooldown, 5)
+ user.setClickCooldown(effective_cooldown)
+ flick("cooldown_[effective_cooldown]",src)
+
+/obj/item/weapon/spell/construct/projectile //This makes me angry, but we need the template, and we can't use it because special check overrides on the base.
+ name = "construct projectile template"
+ icon_state = "generic"
+ desc = "This is a generic template that shoots projectiles. If you can read this, the game broke!"
+ cast_methods = CAST_RANGED
+ var/obj/item/projectile/spell_projectile = null
+ var/pre_shot_delay = 0
+ var/fire_sound = null
+ var/energy_cost_per_shot = 5
+
+/obj/item/weapon/spell/construct/projectile/on_ranged_cast(atom/hit_atom, mob/living/user)
+ if(set_up(hit_atom, user))
+ var/obj/item/projectile/new_projectile = make_projectile(spell_projectile, user)
+ new_projectile.launch(hit_atom)
+ log_and_message_admins("has casted [src] at \the [hit_atom].")
+ if(fire_sound)
+ playsound(get_turf(src), fire_sound, 75, 1)
+ return 1
+ return 0
+
+/obj/item/weapon/spell/construct/projectile/proc/make_projectile(obj/item/projectile/projectile_type, mob/living/user)
+ var/obj/item/projectile/P = new projectile_type(get_turf(user))
+ return P
+
+/obj/item/weapon/spell/construct/projectile/proc/set_up(atom/hit_atom, mob/living/user)
+ if(spell_projectile)
+ if(pay_energy(energy_cost_per_shot))
+ if(pre_shot_delay)
+ var/image/target_image = image(icon = 'icons/obj/spells.dmi', loc = get_turf(hit_atom), icon_state = "target")
+ user << target_image
+ user.Stun(pre_shot_delay / 10)
+ sleep(pre_shot_delay)
+ qdel(target_image)
+ if(owner)
+ return TRUE
+ return FALSE // We got dropped before the firing occured.
+ return TRUE // No delay, no need to check.
+ return FALSE
+
+/obj/item/weapon/spell/construct/spawner
+ name = "spawner template"
+ desc = "If you see me, someone messed up."
+ icon_state = "darkness"
+ cast_methods = CAST_RANGED
+ var/obj/effect/spawner_type = null
+
+/obj/item/weapon/spell/construct/spawner/on_ranged_cast(atom/hit_atom, mob/user)
+ var/turf/T = get_turf(hit_atom)
+ if(T)
+ new spawner_type(T)
+ to_chat(user, "You shift \the [src] onto \the [T].")
+ log_and_message_admins("has casted [src] at [T.x],[T.y],[T.z].")
+ qdel(src)
+
+//Harvester Laser.
+
+/obj/item/weapon/spell/construct/projectile/inverted_beam
+ name = "inversion beam"
+ icon_state = "generic"
+ desc = "Your manipulators fire searing beams of inverted light."
+ cast_methods = CAST_RANGED
+ spell_projectile = /obj/item/projectile/beam/inversion
+ pre_shot_delay = 0
+ cooldown = 5
+ fire_sound = 'sound/weapons/spiderlunge.ogg'
+
+/obj/item/projectile/beam/inversion
+ name = "inversion beam"
+ icon_state = "invert"
+ fire_sound = 'sound/weapons/spiderlunge.ogg'
+ damage = 15
+ damage_type = BURN
+ check_armour = "laser"
+ armor_penetration = 60
+ light_range = 2
+ light_power = -2
+ light_color = "#FFFFFF"
+
+ muzzle_type = /obj/effect/projectile/inversion/muzzle
+ tracer_type = /obj/effect/projectile/inversion/tracer
+ impact_type = /obj/effect/projectile/inversion/impact
+
+//Harvester Pain Orb
+
+/obj/item/weapon/spell/construct/spawner/agonizing_sphere
+ name = "sphere of agony"
+ desc = "Call forth a portal to a dimension of naught but pain at your target."
+
+ spawner_type = /obj/effect/temporary_effect/pulsar/agonizing_sphere
+
+/obj/item/weapon/spell/construct/spawner/agonizing_sphere/on_ranged_cast(atom/hit_atom, mob/user)
+ if(within_range(hit_atom) && pay_energy(10))
+ ..()
+
+/obj/item/weapon/spell/construct/spawner/agonizing_sphere/on_throw_cast(atom/hit_atom, mob/user)
+ pay_energy(5)
+ if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
+ L.add_modifier(/datum/modifier/agonize, 10 SECONDS)
+
+/obj/effect/temporary_effect/pulsar/agonizing_sphere
+ name = "agonizing sphere"
+ desc = "A portal to some hellish place. Its screams wrack your body with pain.."
+ icon_state = "red_static_sphere"
+ time_to_die = null
+ light_range = 4
+ light_power = 5
+ light_color = "#FF0000"
+ pulses_remaining = 10
+
+/obj/effect/temporary_effect/pulsar/agonizing_sphere/pulse_loop()
+ while(pulses_remaining)
+ sleep(1 SECONDS)
+ spawn()
+ for(var/mob/living/L in view(4,src))
+ if(!iscultist(L) && !istype(L, /mob/living/simple_animal/construct))
+ L.add_modifier(/datum/modifier/agonize, 2 SECONDS)
+ if(L.isSynthetic())
+ to_chat(L, "Your chassis warps as the [src] pulses!")
+ L.adjustFireLoss(4)
+ pulses_remaining--
+ qdel(src)
+
+//Artificer Heal
+
+/obj/item/weapon/spell/construct/mend_occult
+ name = "mend acolyte"
+ desc = "Mend the wounds of a cultist, or construct, over time."
+ icon_state = "mend_wounds"
+ cast_methods = CAST_MELEE
+ aspect = ASPECT_UNHOLY
+ light_color = "#FF5C5C"
+ light_power = -2
+
+/obj/item/weapon/spell/construct/mend_occult/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
+ if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
+ L.add_modifier(/datum/modifier/mend_occult, 150)
+ qdel(src)
+
+//Juggernaut + Behemoth Slam
+/obj/item/weapon/spell/construct/slam
+ name = "slam"
+ desc = "Empower your FIST, to send an opponent flying."
+ icon_state = "toggled_old"
+ cast_methods = CAST_MELEE
+ aspect = ASPECT_UNHOLY
+ light_color = "#FF5C5C"
+ light_power = -2
+ cooldown = 15
+
+/obj/item/weapon/spell/construct/slam/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
+ var/attack_message = "slams"
+ if(istype(user, /mob/living/simple_animal))
+ var/mob/living/simple_animal/S = user
+ attack_message = pick(S.attacktext)
+ if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
+ L.visible_message("\The [user] [attack_message] \the [L], sending them flying!")
+ playsound(src, "punch", 50, 1)
+ L.Weaken(2)
+ L.adjustBruteLoss(rand(30, 50))
+ var/throwdir = get_dir(src, L)
+ L.throw_at(get_edge_target_turf(L, throwdir), 3, 1, src)
+ if(istype(hit_atom, /turf/simulated/wall))
+ var/turf/simulated/wall/W = hit_atom
+ user.visible_message("\The [user] rears its fist, preparing to hit \the [W]!")
+ var/windup = cooldown
+ if(W.reinf_material)
+ windup = cooldown * 2
+ if(do_after(user, windup))
+ W.visible_message("\The [user] [attack_message] \the [W], obliterating it!")
+ W.dismantle_wall(1)
+ else
+ user.visible_message("\The [user] lowers its fist.")
+ return
+ qdel(src)
\ No newline at end of file
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index cecb7b87c7..9d8eb8dc91 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -14,20 +14,21 @@
return
/obj/item/weapon/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone)
- if(iscultist(user))
+ if(iscultist(user) && !istype(user, /mob/living/simple_animal/construct))
return ..()
var/zone = (user.hand ? "l_arm":"r_arm")
if(ishuman(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/external/affecting = H.get_organ(zone)
- user << "An unexplicable force rips through your [affecting.name], tearing the sword from your grasp!"
+ to_chat(user, "An inexplicable force rips through your [affecting.name], tearing the sword from your grasp!")
+ //random amount of damage between half of the blade's force and the full force of the blade.
+ user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1)
+ user.Weaken(5)
+ else if(!istype(user, /mob/living/simple_animal/construct))
+ to_chat(user, "An inexplicable force rips through you, tearing the sword from your grasp!")
else
- user << "An unexplicable force rips through you, tearing the sword from your grasp!"
-
- //random amount of damage between half of the blade's force and the full force of the blade.
- user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1)
- user.Weaken(5)
+ to_chat(user, "The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.")
user.drop_from_inventory(src, src.loc)
throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1,3), throw_speed)
@@ -38,10 +39,11 @@
return 1
/obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob)
- if(!iscultist(user))
- user << "An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly."
+ if(!iscultist(user) && !istype(user, /mob/living/simple_animal/construct))
+ to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly.")
user.make_dizzy(120)
-
+ if(istype(user, /mob/living/simple_animal/construct))
+ to_chat(user, "\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.")
/obj/item/clothing/head/culthood
name = "cult hood"
diff --git a/code/game/gamemodes/endgame/supermatter_cascade/portal.dm b/code/game/gamemodes/endgame/supermatter_cascade/portal.dm
index 2c47b8e162..7f7310f996 100644
--- a/code/game/gamemodes/endgame/supermatter_cascade/portal.dm
+++ b/code/game/gamemodes/endgame/supermatter_cascade/portal.dm
@@ -38,13 +38,13 @@
var/mob/living/L = A
if(L.buckled && istype(L.buckled,/obj/structure/bed/))
var/turf/O = L.buckled
- do_teleport(O, pick(endgame_safespawns))
+ do_teleport(O, pick(endgame_safespawns), local = FALSE) //VOREStation Edit
L.loc = O.loc
else
- do_teleport(L, pick(endgame_safespawns)) //dead-on precision
+ do_teleport(L, pick(endgame_safespawns), local = FALSE) //dead-on precision //VOREStation Edit
else if (istype(A, /obj/mecha/))
- do_teleport(A, pick(endgame_safespawns)) //dead-on precision
+ do_teleport(A, pick(endgame_safespawns), local = FALSE) //dead-on precision //VOREStation Edit
else if (isturf(A))
var/turf/T = A
diff --git a/code/game/gamemodes/technomancer/spell_objs.dm b/code/game/gamemodes/technomancer/spell_objs.dm
index 2a917b4fd8..681d29da50 100644
--- a/code/game/gamemodes/technomancer/spell_objs.dm
+++ b/code/game/gamemodes/technomancer/spell_objs.dm
@@ -19,6 +19,7 @@
#define ASPECT_EMP "emp" //Unused now.
#define ASPECT_UNSTABLE "unstable" //Heavily RNG-based, causes instability to the victim.
#define ASPECT_CHROMATIC "chromatic" //Used to combine with other spells.
+#define ASPECT_UNHOLY "unholy" //Involves the dead, blood, and most things against divine beings.
/obj/item/weapon/spell
name = "glowing particles"
diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm
index 2b2654c068..f154b02094 100644
--- a/code/game/jobs/job/job.dm
+++ b/code/game/jobs/job/job.dm
@@ -61,7 +61,7 @@
if(CLASS_LOWER) income = 0.50
//give them an account in the station database
- var/money_amount = (rand(15,40) + rand(15,40)) * income * economic_modifier * ECO_MODIFIER //VOREStation Edit - Smoothed peaks.
+ var/money_amount = (rand(15,40) + rand(15,40)) * income * economic_modifier * ECO_MODIFIER //VOREStation Edit - Smoothed peaks, ECO_MODIFIER rather than per-species ones.
var/datum/money_account/M = create_account(H.real_name, money_amount, null)
if(H.mind)
var/remembered_info = ""
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 30955879f8..1496ca1a1f 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -142,6 +142,18 @@
add_hiddenprint(user)
destroy()
+/obj/machinery/camera/attack_generic(mob/user as mob)
+ if(isanimal(user))
+ var/mob/living/simple_animal/S = user
+ set_status(0)
+ S.do_attack_animation(src)
+ S.setClickCooldown(user.get_attack_speed())
+ visible_message("\The [user] [pick(S.attacktext)] \the [src]!")
+ playsound(src.loc, S.attack_sound, 100, 1)
+ add_hiddenprint(user)
+ destroy()
+ ..()
+
/obj/machinery/camera/attackby(obj/item/W as obj, mob/living/user as mob)
update_coverage()
// DECONSTRUCTION
diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm
index 54b5513cce..58ce8b282c 100644
--- a/code/game/machinery/doors/blast_door.dm
+++ b/code/game/machinery/doors/blast_door.dm
@@ -7,6 +7,9 @@
// as they lack any ID scanning system, they just handle remote control signals. Subtypes have
// different icons, which are defined by set of variables. Subtypes are on bottom of this file.
+// UPDATE 06.04.2018
+// The emag thing wasn't working as intended, manually overwrote it.
+
/obj/machinery/door/blast
name = "Blast Door"
desc = "That looks like it doesn't open easily."
@@ -56,6 +59,10 @@
radiation_repository.resistance_cache.Remove(get_turf(src))
return
+// Has to be in here, comment at the top is older than the emag_act code on doors proper
+/obj/machinery/door/blast/emag_act()
+ return -1
+
// Proc: force_open()
// Parameters: None
// Description: Opens the door. No checks are done inside this proc.
@@ -197,6 +204,27 @@
return
..()
+// Proc: attack_generic()
+// Parameters: Attacking simple mob, incoming damage.
+// Description: Checks the power or integrity of the blast door, if either have failed, chekcs the damage to determine if the creature would be able to open the door by force. Otherwise, super.
+/obj/machinery/door/blast/attack_generic(var/mob/user, var/damage)
+ if(stat & (BROKEN|NOPOWER))
+ if(damage >= 10)
+ if(src.density)
+ visible_message("\The [user] starts forcing \the [src] open!")
+ if(do_after(user, 5 SECONDS, src))
+ visible_message("\The [user] forces \the [src] open!")
+ force_open(1)
+ else
+ visible_message("\The [user] starts forcing \the [src] closed!")
+ if(do_after(user, 2 SECONDS, src))
+ visible_message("\The [user] forces \the [src] closed!")
+ force_close(1)
+ else
+ visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].")
+ return
+ ..()
+
// Proc: open()
// Parameters: None
// Description: Opens the door. Does necessary checks. Automatically closes if autoclose is true
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index 27ce595a77..e639571cb4 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -25,34 +25,39 @@
var/releasetime = 0 // when world.timeofday reaches it - release the prisoner
var/timing = 1 // boolean, true/1 timer is on, false/0 means it's not timing
var/picture_state // icon_state of alert picture, if not displaying text/numbers
- var/list/obj/machinery/targets = list()
+ var/list/obj/machinery/targets
var/timetoset = 0 // Used to set releasetime upon starting the timer
maptext_height = 26
maptext_width = 32
-/obj/machinery/door_timer/New()
+/obj/machinery/door_timer/initialize()
..()
+ //Doors need to go first, and can't rely on init order, so come back to me.
+ return INITIALIZE_HINT_LATELOAD
- spawn(20)
- for(var/obj/machinery/door/window/brigdoor/M in machines)
- if (M.id == src.id)
- targets += M
+/obj/machinery/door_timer/LateInitialize()
+ . = ..()
- for(var/obj/machinery/flasher/F in machines)
- if(F.id == src.id)
- targets += F
+ for(var/obj/machinery/door/window/brigdoor/M in machines)
+ if (M.id == src.id)
+ LAZYADD(targets,M)
- for(var/obj/structure/closet/secure_closet/brig/C in world)
- if(C.id == src.id)
- targets += C
+ for(var/obj/machinery/flasher/F in machines)
+ if(F.id == src.id)
+ LAZYADD(targets,F)
- if(targets.len==0)
- stat |= BROKEN
- update_icon()
- return
- return
+ for(var/obj/structure/closet/secure_closet/brig/C in world)
+ if(C.id == src.id)
+ LAZYADD(targets,C)
+ if(!LAZYLEN(targets))
+ stat |= BROKEN
+ update_icon()
+
+/obj/machinery/door_timer/Destroy()
+ LAZYCLEARLIST(targets)
+ return ..()
//Main door timer loop, if it's timing and time is >0 reduce time by 1.
// if it's less than 0, open door, reset timer
@@ -95,6 +100,7 @@
// Closes and locks doors, power check
/obj/machinery/door_timer/proc/timer_start()
if(stat & (NOPOWER|BROKEN)) return 0
+ if(!LAZYLEN(targets)) return 0
// Set releasetime
releasetime = world.timeofday + timetoset
@@ -115,6 +121,7 @@
// Opens and unlocks doors, power check
/obj/machinery/door_timer/proc/timer_end()
if(stat & (NOPOWER|BROKEN)) return 0
+ if(!LAZYLEN(targets)) return 0
// Reset releasetime
releasetime = 0
@@ -197,11 +204,12 @@
dat += "- - + +
"
// Mounted flash controls
- for(var/obj/machinery/flasher/F in targets)
- if(F.last_flash && (F.last_flash + 150) > world.time)
- dat += "
Flash Charging"
- else
- dat += "
Activate Flash"
+ if(LAZYLEN(targets))
+ for(var/obj/machinery/flasher/F in targets)
+ if(F.last_flash && (F.last_flash + 150) > world.time)
+ dat += "
Flash Charging"
+ else
+ dat += "
Activate Flash"
dat += "
Close"
dat += "