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)