diff --git a/baystation12.dme b/baystation12.dme
index c835b5add34..6db691a79e0 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -22,7 +22,6 @@
#define FILE_DIR "icons/misc"
#define FILE_DIR "icons/mob"
#define FILE_DIR "icons/mob/human_races"
-#define FILE_DIR "icons/NTOS"
#define FILE_DIR "icons/obj"
#define FILE_DIR "icons/obj/assemblies"
#define FILE_DIR "icons/obj/atmospherics"
@@ -249,6 +248,7 @@
#include "code\game\gamemodes\events\holidays\Holidays.dm"
#include "code\game\gamemodes\events\holidays\Other.dm"
#include "code\game\gamemodes\extended\extended.dm"
+#include "code\game\gamemodes\heist\heist.dm"
#include "code\game\gamemodes\malfunction\Malf_Modules.dm"
#include "code\game\gamemodes\malfunction\malfunction.dm"
#include "code\game\gamemodes\meteor\meteor.dm"
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 85fdca943e9..bb627fd3bcf 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -278,6 +278,7 @@ Implants;
if(BE_REV) roletext="revolutionary"
if(BE_CULTIST) roletext="cultist"
if(BE_NINJA) roletext="ninja"
+ if(BE_RAIDER) roletext="raider"
// Assemble a list of active players without jobbans.
for(var/mob/new_player/player in player_list)
diff --git a/code/game/gamemodes/heist/heist.dm b/code/game/gamemodes/heist/heist.dm
new file mode 100644
index 00000000000..d3a2ce69d3d
--- /dev/null
+++ b/code/game/gamemodes/heist/heist.dm
@@ -0,0 +1,250 @@
+/*
+VOX HEIST ROUNDTYPE
+*/
+
+#define MAX_VOX_KILLS 10 //Number of kills during the round before the Inviolate is broken.
+ //Would be nice to use vox-specific kills but is currently not feasible.
+
+var/global/vox_kills = 0 //Used to check the Inviolate.
+
+/datum/game_mode
+ var/list/datum/mind/raiders = list()
+
+/datum/game_mode/heist
+ name = "heist"
+ config_tag = "heist"
+ required_players = 1
+ required_players_secret = 1
+ required_enemies = 1
+ recommended_enemies = 1
+
+ var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds)
+ var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds)
+
+ var/list/raid_objectives = list() //Raid objectives.
+
+/datum/game_mode/heist/announce()
+ world << "The current game mode is - Heist!"
+ world << "An unidentified bluespace signature has slipped past the Icarus and is approaching [station_name()]!"
+ world << "Whoever they are, they're likely up to no good. Protect the crew and station resources against this dastardly threat!"
+ world << "Raiders: Loot [station_name()] for anything and everything you need."
+ world << "Personnel: Repel the raiders and their low, low prices and/or crossbows."
+
+/datum/game_mode/heist/can_start()
+
+ if(!..())
+ return 0
+
+ var/list/candidates = get_players_for_role(BE_OPERATIVE)
+ var/raider_num = 0
+
+ //Check that we have enough vox.
+ if(candidates.len < required_enemies)
+ return 0
+ else if(candidates.len < recommended_enemies)
+ raider_num = candidates.len
+ else
+ raider_num = recommended_enemies
+
+ //Grab candidates randomly until we have enough.
+ while(raider_num > 0)
+ var/datum/mind/new_raider = pick(candidates)
+ raiders += new_raider
+ candidates -= new_raider
+ raider_num--
+
+ for(var/datum/mind/raider in raiders)
+ raider.assigned_role = "MODE"
+ raider.special_role = "Vox Raider"
+ return 1
+
+/datum/game_mode/heist/pre_setup()
+ return 1
+
+/datum/game_mode/heist/post_setup()
+
+ //Build a list of spawn points.
+ var/list/turf/raider_spawn = list()
+
+ for(var/obj/effect/landmark/L in landmarks_list)
+ if(L.name == "voxstart")
+ raider_spawn += get_turf(L)
+ del(L)
+ continue
+
+ //Generate objectives for the group.
+ raid_objectives = forge_vox_objectives()
+
+ var/index = 1
+
+ //Spawn the vox!
+ for(var/datum/mind/raider in raiders)
+
+ if(index > raider_spawn.len)
+ index = 1
+
+ raider.current.loc = raider_spawn[index]
+ index++
+
+ var/sounds = rand(2,8)
+ var/i = 0
+ var/newname = ""
+
+ while(i<=sounds)
+ i++
+ newname += pick(list("ti","hi","ki","ya","ta","ha","ka","ya","chi","cha","kah"))
+
+ var/mob/living/carbon/human/vox = raider.current
+
+ vox.vox_talk_understand = 1
+ vox.real_name = capitalize(newname)
+ vox.name = vox.real_name
+ vox.age = rand(12,20)
+ vox.dna.mutantrace = "vox"
+ vox.h_style = "Short Vox Quills"
+ vox.equip_vox_raider()
+ vox.regenerate_icons()
+
+ raider.objectives = raid_objectives
+ greet_vox(raider)
+
+ spawn (rand(waittime_l, waittime_h))
+ send_intercept()
+
+/datum/game_mode/heist/declare_completion()
+
+/datum/game_mode/heist/proc/is_raider_crew_safe()
+
+ for(var/datum/mind/raider in raiders)
+ if(raider.current)
+ if (get_area(raider.current) != locate(/area/shuttle/vox/station))
+ return 0
+ return 1
+
+/datum/game_mode/heist/proc/is_raider_crew_alive()
+
+ for(var/datum/mind/raider in raiders)
+ if(raider.current)
+ if(istype(raider.current,/mob/living/carbon/human) && raider.current.stat != 2)
+ return 1
+ return 0
+
+/datum/game_mode/heist/proc/forge_vox_objectives()
+
+
+ //Commented out for testing.
+ /* var/i = 1
+ var/max_objectives = pick(2,2,2,3,3)
+ var/list/objs = list()
+ while(i<= max_objectives)
+ var/list/goals = list("kidnap","loot","salvage")
+ var/goal = pick(goals)
+ var/datum/objective/heist/O
+
+ if(goal == "kidnap")
+ goals -= "kidnap"
+ O = new /datum/objective/heist/kidnap()
+ else if(goal == "loot")
+ O = new /datum/objective/heist/loot()
+ else
+ O = new /datum/objective/heist/salvage()
+ O.choose_target()
+ objs += O
+
+ i++
+
+ //-All- vox raids have these two objectives. Failing them loses the game.
+ objs += new /datum/objective/heist/inviolate_crew
+ objs += new /datum/objective/heist/inviolate_death */
+
+ raid_objectives += new /datum/objective/heist/kidnap
+ raid_objectives += new /datum/objective/heist/loot
+ raid_objectives += new /datum/objective/heist/salvage
+ raid_objectives += new /datum/objective/heist/inviolate_crew
+ raid_objectives += new /datum/objective/heist/inviolate_death
+
+ for(var/datum/objective/heist/O in raid_objectives)
+ O.choose_target()
+
+ return raid_objectives
+
+/datum/game_mode/heist/proc/greet_vox(var/datum/mind/raider)
+ raider.current << "\blue You are a Vox Raider, fresh from the Shoal!"
+ raider.current << "\blue Don't forget to turn on your nitrogen internals."
+ var/obj_count = 1
+ for(var/datum/objective/objective in raider.objectives)
+ raider.current << "Objective #[obj_count]: [objective.explanation_text]"
+ obj_count++
+
+
+/datum/game_mode/heist/declare_completion()
+
+ //No objectives, go straight to the feedback.
+ if(!(raid_objectives.len)) return ..()
+
+ var/win_type = "Major"
+ var/win_group = "Crew"
+ var/win_msg = ""
+
+ var/success = raid_objectives.len
+
+ //Decrease success for failed objectives.
+ for(var/datum/objective/O in raid_objectives)
+ if(!(O.check_completion())) success--
+
+ //Set result by objectives.
+ if(success == raid_objectives.len)
+ win_type = "Major"
+ win_group = "Vox"
+ else if(success > 2)
+ win_type = "Minor"
+ win_group = "Vox"
+ else
+ win_type = "Minor"
+ win_group = "Crew"
+
+ //Now we modify that result by the state of the vox crew.
+ if(!is_raider_crew_alive())
+
+ win_type = "Major"
+ win_group = "Crew"
+ win_msg += "The Vox Raiders have been wiped out!"
+
+ else if(!is_raider_crew_safe())
+
+ if(win_group == "Crew" && win_type == "Minor")
+ win_type = "Major"
+
+ win_group = "Crew"
+ win_msg += "The Vox Raiders have left someone behind!"
+
+ else
+
+ if(win_group == "Vox")
+ if(win_type == "Minor")
+
+ win_type = "Major"
+ win_msg += "The Vox Raiders escaped the station!"
+ else
+ win_msg += "The Vox Raiders were repelled!"
+
+ world << "\red [win_type] [win_group] victory!"
+ world << "[win_msg]"
+ feedback_set_details("round_end_result","heist - [win_type] [win_group]")
+
+ var/count = 1
+ for(var/datum/objective/objective in raid_objectives)
+ if(objective.check_completion())
+ world << "
Objective #[count]: [objective.explanation_text] Success!"
+ feedback_add_details("traitor_objective","[objective.type]|SUCCESS")
+ else
+ world << "
Objective #[count]: [objective.explanation_text] Fail."
+ feedback_add_details("traitor_objective","[objective.type]|FAIL")
+ count++
+
+ ..()
+
+/datum/game_mode/heist/check_finished()
+ if (!(is_raider_crew_alive()) || (vox_shuttle_location && (vox_shuttle_location == "start")))
+ return 1
+ return ..()
\ No newline at end of file
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 1b3552408c5..a8c0e29947a 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -742,3 +742,172 @@ datum/objective/absorb
/*-------ENDOF CULTIST------*/
*/
+
+//Vox heist objectives.
+
+datum/objective/heist
+ proc/choose_target()
+ return
+
+datum/objective/heist/kidnap
+ choose_target()
+ var/list/roles = list("Chief Engineer","Research Director","Roboticist","Chemist","Station Engineer")
+ var/list/possible_targets = list()
+ var/list/priority_targets = list()
+
+ for(var/datum/mind/possible_target in ticker.minds)
+ if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2) && (possible_target.assigned_role != "MODE"))
+ possible_targets += possible_target
+ for(var/role in roles)
+ if(possible_target.assigned_role == role)
+ priority_targets += possible_target
+ continue
+
+ if(priority_targets.len > 0)
+ target = pick(priority_targets)
+ else if(possible_targets.len > 0)
+ target = pick(possible_targets)
+
+ if(target && target.current)
+ explanation_text = "The Shoal has a need for [target.current.real_name], the [target.assigned_role]. Take them alive."
+ else
+ explanation_text = "Free Objective"
+ return target
+
+ check_completion()
+ if(target && target.current)
+ if (target.current.stat == 2)
+ return 0 // They're dead. Fail.
+ //if (!target.current.restrained())
+ // return 0 // They're loose. Close but no cigar.
+
+ var/area/shuttle/vox/station/A = locate()
+ for(var/mob/living/carbon/human/M in A)
+ if(target.current == M)
+ return 1 //They're restrained on the shuttle. Success.
+ else
+ return 0
+
+datum/objective/heist/loot
+ choose_target()
+
+ if(pick("hardware","weapons") == "hardware")
+ target = pick("a complete particle accelerator","a gravitational generator","four emitters","a nuclear bomb")
+ else
+ target = pick("six guns","four energy guns","two laser guns","an ion gun")
+
+ explanation_text = "We are lacking in hardware. Steal [target]."
+
+ switch(target)
+ if("a complete particle accelerator")
+ target = /obj/structure/particle_accelerator
+ target_amount = 6
+ if("a gravitational generator")
+ target = /obj/machinery/the_singularitygen
+ target_amount = 1
+ if("four emitters")
+ target = /obj/machinery/power/emitter
+ target_amount = 4
+ if("a nuclear bomb")
+ target = /obj/machinery/nuclearbomb
+ target_amount = 1
+ if("six guns")
+ target = /obj/item/weapon/gun
+ target_amount = 6
+ if("four energy guns")
+ target = /obj/item/weapon/gun/energy
+ target_amount = 4
+ if("two laser guns")
+ target = /obj/item/weapon/gun/energy/laser
+ target_amount = 2
+ if("an ion gun")
+ target = /obj/item/weapon/gun/energy/ionrifle
+ target_amount = 1
+
+ check_completion()
+
+ var/total_amount = 0
+
+ for(var/obj/O in locate(/area/shuttle/vox/station))
+ if(istype(O,target)) total_amount++
+ if(total_amount >= target_amount) return 1
+
+ var/datum/game_mode/heist/H = ticker.mode
+ for(var/datum/mind/raider in H.raiders)
+ if(raider.current)
+ for(var/obj/O in raider.current.get_contents())
+ if(istype(O,target)) total_amount++
+ if(total_amount >= target_amount) return 1
+
+ return 0
+
+datum/objective/heist/salvage
+
+ choose_target()
+ var/list/possible_items = list("300 metal sheets","200 glass sheets","100 plasteel sheets","100 units of solid plasma","50 silver bars","20 gold bars","20 uranium bars","20 diamonds")
+ target = pick(possible_items)
+ explanation_text = "Ransack the station and escape with [target]."
+
+ switch(target)
+ if("300 metal sheets")
+ target = "metal"
+ target_amount = 300
+ if("200 glass sheets")
+ target = "glass"
+ target_amount = 200
+ if("100 plasteel sheets")
+ target = "plasteel"
+ target_amount = 100
+ if("100 units of solid plasma")
+ target = "plasma"
+ target_amount = 100
+ if("50 silver bars")
+ target = "silver"
+ target_amount = 50
+ if("20 gold bars")
+ target = "gold"
+ target_amount = 20
+ if("20 uranium bars")
+ target = "uranium"
+ target_amount = 20
+ if("20 diamonds")
+ target = "diamond"
+ target_amount = 20
+
+
+ check_completion()
+
+ var/total_amount = 0
+
+ for(var/obj/item/O in locate(/area/shuttle/vox/station))
+ if(istype(O,/obj/item/stack/sheet))
+ if(O.name == target)
+ var/obj/item/stack/sheet/S = O
+ total_amount += S.amount
+
+ var/datum/game_mode/heist/H = ticker.mode
+ for(var/datum/mind/raider in H.raiders)
+ if(raider.current)
+ for(var/obj/item/O in raider.current.get_contents())
+ if(istype(O,/obj/item/stack/sheet))
+ if(O.name == target)
+ var/obj/item/stack/sheet/S = O
+ total_amount += S.amount
+
+ if(total_amount >= target_amount) return 1
+ return 0
+
+
+datum/objective/heist/inviolate_crew
+ explanation_text = "Do not leave any Vox behind, alive or dead."
+
+ check_completion()
+ var/datum/game_mode/heist/H = ticker.mode
+ if(H.is_raider_crew_safe()) return 1
+ return 0
+
+datum/objective/heist/inviolate_death
+ explanation_text = "Follow the Inviolate. Minimise death and loss of resources."
+ check_completion()
+ if(vox_kills>5) return 0
+ return 1
\ No newline at end of file
diff --git a/code/game/machinery/computer/vox_shuttle.dm b/code/game/machinery/computer/vox_shuttle.dm
index 59513acd0b4..fa9d05d7b90 100644
--- a/code/game/machinery/computer/vox_shuttle.dm
+++ b/code/game/machinery/computer/vox_shuttle.dm
@@ -1,7 +1,8 @@
-#define VOX_SHUTTLE_MOVE_TIME 60
-#define VOX_SHUTTLE_COOLDOWN 200
+#define VOX_SHUTTLE_MOVE_TIME 260
+#define VOX_SHUTTLE_COOLDOWN 460
//Copied from Syndicate shuttle.
+var/global/vox_shuttle_location
/obj/machinery/computer/vox_station
name = "vox skipjack terminal"
@@ -11,7 +12,7 @@
var/area/curr_location
var/moving = 0
var/lastMove = 0
-
+ var/warning //Warning about the end of the round.
/obj/machinery/computer/vox_station/New()
curr_location= locate(/area/shuttle/vox/station)
@@ -35,6 +36,7 @@
curr_location.move_contents_to(dest_location)
curr_location = dest_location
moving = 0
+
return 1
@@ -56,7 +58,7 @@
var/dat = {"Location: [curr_location]
Ready to move[max(lastMove + VOX_SHUTTLE_COOLDOWN - world.time, 0) ? " in [max(round((lastMove + VOX_SHUTTLE_COOLDOWN - world.time) * 0.1), 0)] seconds" : ": now"]
- Dark space
+ Return to dark space
Fore port solar |
Aft port solar |
Fore starboard solar
@@ -76,8 +78,15 @@
if(in_range(src, user) || istype(user, /mob/living/silicon))
user.set_machine(src)
+ vox_shuttle_location = "station"
if(href_list["start"])
+ if(ticker && (istype(ticker.mode,/datum/game_mode/heist)))
+ if(!warning)
+ user << "\red Returning to dark space will end your raid and report your success or failure. If you are sure, press the button again."
+ warning = 1
+ return
vox_move_to(/area/shuttle/vox/station)
+ vox_shuttle_location = "start"
else if(href_list["solars_fore_starboard"])
vox_move_to(/area/vox_station/northeast_solars)
else if(href_list["solars_fore_port"])
diff --git a/code/global.dm b/code/global.dm
index ce0fa371e32..348cc8702fa 100644
--- a/code/global.dm
+++ b/code/global.dm
@@ -134,7 +134,6 @@ var/list/prisonsecuritywarp = list() //prison security goes to these
var/list/prisonwarped = list() //list of players already warped
var/list/blobstart = list()
var/list/ninjastart = list()
-var/list/voxstart = list()
// list/traitors = list() //traitor list
var/list/cardinal = list( NORTH, SOUTH, EAST, WEST )
var/list/alldirs = list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index 4c61f2e60b0..90617f3019f 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -54,6 +54,14 @@
dizziness = 0
jitteriness = 0
+ //Check for heist mode kill count.
+ if(ticker.mode && ( istype( ticker.mode,/datum/game_mode/heist) ) )
+ //Check for last assailant's mutantrace.
+ /*if( LAssailant && ( istype( LAssailant,/mob/living/carbon/human ) ) )
+ var/mob/living/carbon/human/V = LAssailant
+ if (V.dna && (V.dna.mutantrace == "vox"))*/ //Not currently feasible due to terrible LAssailant tracking.
+ vox_kills++ //Bad vox. Shouldn't be killing humans.
+
if(!gibbed)
emote("deathgasp") //let the world KNOW WE ARE DEAD
diff --git a/code/setup.dm b/code/setup.dm
index 8afcbe689a5..6759a91e0eb 100644
--- a/code/setup.dm
+++ b/code/setup.dm
@@ -640,6 +640,7 @@ var/list/TAGGERLOCATIONS = list("Disposals",
#define BE_CULTIST 256
#define BE_MONKEY 512
#define BE_NINJA 1024
+#define BE_RAIDER 2048
var/list/be_special_flags = list(
"Traitor" = BE_TRAITOR,
@@ -652,7 +653,8 @@ var/list/be_special_flags = list(
"pAI" = BE_PAI,
"Cultist" = BE_CULTIST,
"Monkey" = BE_MONKEY,
- "Ninja" = BE_NINJA
+ "Ninja" = BE_NINJA,
+ "Raider" = BE_RAIDER
)
#define AGE_MIN 17 //youngest a character can be
@@ -693,4 +695,4 @@ var/list/bradycardics = list("neurotoxin", "cryoxadone", "clonexadone", "space_d
//proc/get_pulse methods
#define GETPULSE_HAND 0 //less accurate (hand)
-#define GETPULSE_TOOL 1 //more accurate (med scanner, sleeper, etc)
+#define GETPULSE_TOOL 1 //more accurate (med scanner, sleeper, etc)