diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 35232e6ab9c..278b5912329 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -253,6 +253,7 @@ Whitespace:Seperator;"}
if(BE_WIZARD) roletext="wizard"
if(BE_REV) roletext="revolutionary"
if(BE_CULTIST) roletext="cultist"
+ if(BE_MEME) roletext="meme"
for(var/mob/new_player/player in world)
diff --git a/code/game/gamemodes/meme/meme.dm b/code/game/gamemodes/meme/meme.dm
new file mode 100644
index 00000000000..39ac61d7aaa
--- /dev/null
+++ b/code/game/gamemodes/meme/meme.dm
@@ -0,0 +1,155 @@
+/datum/game_mode/meme
+ name = "meme"
+ config_tag = "meme"
+ //required_players = 6
+ required_players = 2 // for testing
+ restricted_jobs = list("AI", "Cyborg")
+ recommended_enemies = 2 // need at least a meme and a host
+
+
+ var
+ list/memes = list()
+
+ var/datum/mind/first_host = null
+ const
+ prob_int_murder_target = 50 // intercept names the assassination target half the time
+ prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target
+ prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target
+
+ prob_int_item = 50 // intercept names the theft target half the time
+ prob_right_item_l = 25 // lower bound on probability of naming right theft target
+ prob_right_item_h = 50 // upper bound on probability of naming the right theft target
+
+ prob_int_sab_target = 50 // intercept names the sabotage target half the time
+ prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target
+ prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target
+
+ prob_right_killer_l = 25 //lower bound on probability of naming the right operative
+ prob_right_killer_h = 50 //upper bound on probability of naming the right operative
+ prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly
+ prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly
+
+ waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds)
+ waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds)
+
+/datum/game_mode/meme/announce()
+ world << "The current game mode is - Meme!"
+ world << "An unknown creature has infested the mind of a crew member. Find and destroy it by what means necessary."
+
+/datum/game_mode/meme/can_start()
+ if(!..())
+ return 0
+
+ var/list/datum/mind/possible_memes = get_players_for_role(BE_MEME)
+
+ if(possible_memes.len < 2)
+ log_admin("MODE FAILURE: MEME. NOT ENOUGH MEME CANDIDATES.")
+ return 0 // not enough candidates for meme
+
+ var/datum/mind/meme = pick(possible_memes)
+ possible_memes.Remove(meme)
+
+ first_host = pick(possible_memes)
+
+ modePlayer += meme
+ modePlayer += first_host
+
+ meme.assigned_role = "MODE" //So they aren't chosen for other jobs.
+ meme.special_role = "Meme"
+
+ return 1
+
+/datum/game_mode/meme/pre_setup()
+ return 1
+
+
+/datum/game_mode/meme/post_setup()
+ // create a meme and enter it
+ for(var/datum/mind/meme in memes)
+ var/mob/living/parasite/meme/M = new
+ meme.transfer_to(M)
+ M.enter_host(first_host)
+ break
+
+ // TODO: make it possible to have multiple memes with multiple hosts
+
+ spawn (rand(waittime_l, waittime_h))
+ send_intercept()
+ ..()
+ return
+
+
+/datum/game_mode/proc/forge_meme_objectives(var/datum/mind/meme)
+ // meme always needs to attune X hosts
+ var/datum/objective/meme_attune/attune_objective = new
+ attune_objective.owner = meme
+ attune_objective.gen_amount_goal(3,6)
+ meme.objectives += attune_objective
+
+ // generate some random objectives, use standard traitor objectives
+ var/job = first_host.assigned_role
+
+ for(var/datum/objective/o in SelectObjectives(job, meme))
+ o.owner = meme
+ meme.objectives += o
+
+ return
+
+/datum/game_mode/proc/greet_meme(var/datum/mind/meme, var/you_are=1)
+ if (you_are)
+ meme.current << "\red You are a meme!"
+
+ var/obj_count = 1
+ for(var/datum/objective/objective in meme.objectives)
+ meme.current << "Objective #[obj_count]: [objective.explanation_text]"
+ obj_count++
+ return
+
+/datum/game_mode/meme/check_finished()
+ var/memes_alive = 0
+ for(var/datum/mind/meme in memes)
+ if(!istype(meme.current,/mob/living/parasite/meme))
+ continue
+ if(meme.current.stat==2)
+ continue
+ memes_alive++
+
+ if (memes_alive)
+ return ..()
+ else
+ return 1
+
+/datum/game_mode/proc/auto_declare_completion_meme()
+ for(var/datum/mind/meme in memes)
+ var/memewin = 1
+ var/meme_name
+ var/attuned = 0
+ if((meme.current) && istype(meme.current,/mob/living/parasite/meme))
+ meme_name = "[meme.key] (The Meme)"
+ world << "The meme was [meme.current.key]."
+ world << "The first host was [first_host.key]."
+ world << "The last host was [meme.current:host.key]."
+ world << "Hosts attuned: [attuned]"
+
+ var/count = 1
+ for(var/datum/objective/objective in meme.objectives)
+ if(objective.check_completion())
+ world << "Objective #[count]: [objective.explanation_text] \green Success"
+ feedback_add_details("meme_objective","[objective.type]|SUCCESS")
+ else
+ world << "Objective #[count]: [objective.explanation_text] \red Failed"
+ feedback_add_details("meme_objective","[objective.type]|FAIL")
+ memewin = 0
+ count++
+
+ else
+ meme_name = "[meme.key] (character destroyed)"
+ memewin = 0
+
+ if(memewin)
+ world << "The meme was successful!"
+ feedback_add_details("meme_success","SUCCESS")
+ else
+ world << "The meme has failed!"
+ feedback_add_details("meme_success","FAIL")
+ return 1
\ No newline at end of file
diff --git a/code/game/gamemodes/newobjective.dm b/code/game/gamemodes/newobjective.dm
index 5d92803d2f3..de5514ddd89 100644
--- a/code/game/gamemodes/newobjective.dm
+++ b/code/game/gamemodes/newobjective.dm
@@ -1226,6 +1226,20 @@ datum
else
return 0
+ meme_attune
+ var/target_amount
+ proc/gen_amount_goal(var/lowbound = 4, var/highbound = 6)
+ target_amount = rand (lowbound,highbound)
+
+ explanation_text = "Attune [target_amount] humanoid brains."
+ return target_amount
+
+ check_completion()
+ if(owner && owner.current && istype(owner.current,/mob/living/parasite/meme) && (owner.current:indoctrinated.len >= target_amount))
+ return 1
+ else
+ return 0
+
download
var/target_amount
proc/gen_amount_goal()
diff --git a/code/modules/mob/living/parasite/meme.dm b/code/modules/mob/living/parasite/meme.dm
index 34a4dc6c4e2..53bf995f0db 100644
--- a/code/modules/mob/living/parasite/meme.dm
+++ b/code/modules/mob/living/parasite/meme.dm
@@ -468,10 +468,14 @@ mob/living/parasite/meme/verb/Dormant()
usr << "\red You have regained all points and exited dormant mode!"
+// Game mode helpers, used for theft objectives
+// --------------------------------------------
+mob/living/parasite/check_contents_for(t)
+ if(!host) return 0
-// TEST CODE
-client/verb/become_meme(target as mob in world)
- var/mob/living/parasite/meme/M = new
- M.enter_host(target)
- src.mob = M
-// END TEST CODE
\ No newline at end of file
+ return host.check_contents_for(t)
+
+mob/living/parasite/check_contents_for_reagent(t)
+ if(!host) return 0
+
+ return host.check_contents_for_reagent(t)
\ No newline at end of file
diff --git a/code/modules/mob/new_player/preferences.dm b/code/modules/mob/new_player/preferences.dm
index 0c649035342..d1ecf5d15a5 100644
--- a/code/modules/mob/new_player/preferences.dm
+++ b/code/modules/mob/new_player/preferences.dm
@@ -39,6 +39,7 @@ var/const
BE_CULTIST =(1<<7)
BE_MONKEY =(1<<8)
BE_PAI =(1<<9)
+ BE_MEME =(1<<10)