diff --git a/baystation12.dme b/baystation12.dme index 3c844ef9b08..627b48fb13b 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -40,6 +40,7 @@ #define FILE_DIR "code/game/gamemodes/epidemic" #define FILE_DIR "code/game/gamemodes/extended" #define FILE_DIR "code/game/gamemodes/malfunction" +#define FILE_DIR "code/game/gamemodes/meme" #define FILE_DIR "code/game/gamemodes/meteor" #define FILE_DIR "code/game/gamemodes/nuclear" #define FILE_DIR "code/game/gamemodes/revolution" @@ -119,6 +120,7 @@ #define FILE_DIR "code/modules/mob/living/carbon/human" #define FILE_DIR "code/modules/mob/living/carbon/metroid" #define FILE_DIR "code/modules/mob/living/carbon/monkey" +#define FILE_DIR "code/modules/mob/living/parasite" #define FILE_DIR "code/modules/mob/living/silicon" #define FILE_DIR "code/modules/mob/living/silicon/ai" #define FILE_DIR "code/modules/mob/living/silicon/decoy" @@ -441,6 +443,7 @@ #include "code\game\gamemodes\extended\extended.dm" #include "code\game\gamemodes\malfunction\Malf_Modules.dm" #include "code\game\gamemodes\malfunction\malfunction.dm" +#include "code\game\gamemodes\meme\meme.dm" #include "code\game\gamemodes\meteor\meteor.dm" #include "code\game\gamemodes\meteor\meteors.dm" #include "code\game\gamemodes\nuclear\nuclear.dm" @@ -989,6 +992,7 @@ #include "code\modules\mob\living\carbon\monkey\npc.dm" #include "code\modules\mob\living\carbon\monkey\powers.dm" #include "code\modules\mob\living\carbon\monkey\say.dm" +#include "code\modules\mob\living\parasite\meme.dm" #include "code\modules\mob\living\silicon\death.dm" #include "code\modules\mob\living\silicon\say.dm" #include "code\modules\mob\living\silicon\silicon.dm" diff --git a/code/defines/mob/living/carbon/carbon.dm b/code/defines/mob/living/carbon/carbon.dm index 7715ce30bfb..504be351276 100644 --- a/code/defines/mob/living/carbon/carbon.dm +++ b/code/defines/mob/living/carbon/carbon.dm @@ -12,5 +12,8 @@ var/list/datum/disease2/disease/resistances2 = list() var/antibodies = 0 + var/analgesic = 0 // when this is set, the mob isn't affected by shock or pain + // life should decrease this by 1 every tick + mob var/list/disease_symptoms = 0 // a list of disease-incurred symptoms \ No newline at end of file diff --git a/code/defines/procs/gamehelpers.dm b/code/defines/procs/gamehelpers.dm index a8fbd1f67a3..dc04c1e4ca0 100644 --- a/code/defines/procs/gamehelpers.dm +++ b/code/defines/procs/gamehelpers.dm @@ -202,6 +202,11 @@ if(isInSight(source,C)) hear += C + // Parasites(e.g. Meme) + for(var/mob/living/carbon/C in V) + for(var/mob/M in C.parasites) + hear += M + /* -- Handled above. WHY IS THIS HERE? WHYYYYYYY // Personal AIs for(var/obj/item/device/paicard/C in V) diff --git a/code/defines/procs/helpers.dm b/code/defines/procs/helpers.dm index 859e8abd106..e128be4cf8c 100644 --- a/code/defines/procs/helpers.dm +++ b/code/defines/procs/helpers.dm @@ -872,6 +872,8 @@ Turf and target are seperate in case you want to teleport some distance from a t temp_list.Add(M) for(var/mob/living/carbon/human/M in world) temp_list.Add(M) + for(var/mob/living/parasite/P in M.parasites) + temp_list.Add(P) for(var/mob/living/carbon/brain/M in world) temp_list.Add(M) for(var/mob/living/carbon/alien/M in world) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 6153568bcba..42837d308c0 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -252,6 +252,7 @@ Badassery; 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..4db573a6293 --- /dev/null +++ b/code/game/gamemodes/meme/meme.dm @@ -0,0 +1,161 @@ +/datum/game_mode/var/list/memes = list() + +/datum/game_mode/meme + name = "Memetic Anomaly" + config_tag = "meme" + required_players = 6 + restricted_jobs = list("AI", "Cyborg") + recommended_enemies = 2 // need at least a meme and a host + + + var + + 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 any 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 + memes += meme + + 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 + var/mob/original = meme.current + meme.transfer_to(M) + M.clearHUD() + M.enter_host(first_host.current) + forge_meme_objectives(meme, first_host) + + del original + + 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, var/datum/mind/first_host) + // 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 + + greet_meme(meme) + + 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)) + 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/attuned = 0 + if((meme.current) && istype(meme.current,/mob/living/parasite/meme)) + world << "The meme was [meme.current.key]." + world << "The first host was [src:first_host.current.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 + 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/game/objects/devices/flash.dm b/code/game/objects/devices/flash.dm index 9aee8fb1b7a..3508f652fc9 100644 --- a/code/game/objects/devices/flash.dm +++ b/code/game/objects/devices/flash.dm @@ -75,6 +75,11 @@ flick("e_flash", M.flash) if(ishuman(M) && ishuman(user) && M.stat!=DEAD) + // kill all memes + for(var/mob/living/parasite/meme/P in M:parasites) + P << "\red The bright flash is unbearable.. You lose consciousness and fade away.." + P.death() + if(user.mind && user.mind in ticker.mode.head_revolutionaries) var/revsafe = 0 for(var/obj/item/weapon/implant/loyalty/L in M) diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index dab1a9412f6..66137de8578 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -1,6 +1,10 @@ /mob/living/carbon/human/emote(var/act,var/m_type=1,var/message = null) var/param = null + if(!emote_allowed && usr == src) + usr << "You are unable to emote." + return + if (findtext(act, " ", 1, null)) var/t1 = findtext(act, " ", 1, null) param = copytext(act, t1 + 1, length(act) + 1) @@ -534,7 +538,13 @@ if (m_type & 1) for (var/mob/O in viewers(src, null)) + if(istype(O,/mob/living/carbon/human)) + for(var/mob/living/parasite/P in O:parasites) + P.show_message(message, m_type) O.show_message(message, m_type) else if (m_type & 2) for (var/mob/O in hearers(src.loc, null)) + if(istype(O,/mob/living/carbon/human)) + for(var/mob/living/parasite/P in O:parasites) + P.show_message(message, m_type) O.show_message(message, m_type) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 8216b01cfe6..0c0cf53e91a 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -269,6 +269,8 @@ if(reagents.has_reagent("nuka_cola")) return -1 + if(analgesic) return -1 + if (istype(loc, /turf/space)) return -1 // It's hard to be slowed down in space by... anything var/health_deficiency = traumatic_shock diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 730aedd55e3..48f22381d62 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -134,6 +134,9 @@ proc handle_health_updates() + // the analgesic effect wears off slowly + analgesic = max(0, analgesic - 1) + // if the mob has enough health, she should slowly heal if(stat == 1) if(health >= 0) @@ -152,6 +155,8 @@ lying = 1 //Seriously, stay down :x update_clothing() + + clamp_values() SetStunned(min(stunned, 20)) @@ -1449,6 +1454,8 @@ handle_shock() ..() + if(analgesic) return // analgesic avoids all traumatic shock temporarily + if(health < 0) // health 0 makes you immediately collapse shock_stage = max(shock_stage, 61) diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm index 2d5f675b973..c8c291131a2 100644 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ b/code/modules/mob/living/carbon/human/whisper.dm @@ -11,6 +11,10 @@ src << "You are muted." return + if(!speech_allowed && usr == src) + usr << "\red You can't speak." + return + if (src.stat == 2) return src.say_dead(message) @@ -62,11 +66,11 @@ if (O) O.hear_talk(src, message) - var/list/listening = hearers(message_range, src) + var/list/listening = get_mobs_in_view(message_range, src) // listening -= src // listening += src // WAT. - var/list/eavesdropping = hearers(2, src) + var/list/eavesdropping = get_mobs_in_view(message_range, src) eavesdropping -= src eavesdropping -= listening var/list/watching = hearers(5, src) diff --git a/code/modules/mob/living/carbon/shock.dm b/code/modules/mob/living/carbon/shock.dm index f53ca6f66f0..e1b9d6c90c5 100644 --- a/code/modules/mob/living/carbon/shock.dm +++ b/code/modules/mob/living/carbon/shock.dm @@ -14,6 +14,8 @@ src.traumatic_shock -= 200 // make synaptizine function as good painkiller if(src.slurring) src.traumatic_shock -= 20 + if(src.analgesic) + src.traumatic_shock = 0 // broken or ripped off organs will add quite a bit of pain if(istype(src,/mob/living/carbon/human)) diff --git a/code/modules/mob/living/parasite/meme.dm b/code/modules/mob/living/parasite/meme.dm new file mode 100644 index 00000000000..f0c219252fd --- /dev/null +++ b/code/modules/mob/living/parasite/meme.dm @@ -0,0 +1,559 @@ +// === MEMETIC ANOMALY === +// ======================= + +/** +This life form is a form of parasite that can gain a certain level of control +over its host. Its player will share vision and hearing with the host, and it'll +be able to influence the host through various commands. +**/ + +// The maximum amount of points a meme can gather. +var/global/const/MAXIMUM_MEME_POINTS = 750 + + +// === PARASITE === +// ================ + +// a list of all the parasites in the mob +mob/living/carbon/var/list/parasites = list() + +mob/living/parasite + var + mob/living/carbon/host // the host that this parasite occupies + + Login() + ..() + + // make the client see through the host instead + client.eye = host + client.perspective = EYE_PERSPECTIVE + + +mob/living/parasite/proc/enter_host(mob/living/carbon/host) + // by default, parasites can't share a body with other life forms + if(host.parasites.len > 0) + return 0 + + src.host = host + src.loc = host + host.parasites.Add(src) + + if(client) client.eye = host + + return 1 + +mob/living/parasite/proc/exit_host() + src.host.parasites.Remove(src) + src.host = null + src.loc = null + + return 1 + + +// === MEME === +// ============ + +// Memes use points for many actions +mob/living/parasite/meme/var/meme_points = 100 +mob/living/parasite/meme/var/dormant = 0 + +// Memes have a list of indoctrinated hosts +mob/living/parasite/meme/var/list/indoctrinated = list() + +mob/living/parasite/meme/Life() + ..() + + if(client) + if(blinded) client.eye = null + else client.eye = host + + if(!host) return + + // recover meme points slowly + var/gain = 3 + if(dormant) gain = 9 // dormant recovers points faster + + meme_points = min(meme_points + gain, MAXIMUM_MEME_POINTS) + + // if there are sleep toxins in the host's body, that's bad + if(host.reagents.has_reagent("stoxin")) + src << "\red Something in your host's blood makes you lose consciousness.. you fade away.." + src.death() + return + // a host without brain is no good + if(!host.mind) + src << "\red Your host has no mind.. you fade away.." + src.death() + return + if(host.stat == 2) + src << "\red Your host has died.. you fade away.." + src.death() + return + + if(host.blinded) src.blinded = 1 + else src.blinded = 0 + + +mob/living/parasite/meme/death() + // make sure the mob is on the actual map before gibbing + if(host) src.loc = host.loc + src.stat = 2 + ..() + del src + +// When a meme speaks, it speaks through its host +mob/living/parasite/meme/say(message as text) + if(dormant) + usr << "\red You're dormant!" + return + if(!host) + usr << "\red You can't speak without host!" + return + + return host.say(message) + +// Same as speak, just with whisper +mob/living/parasite/meme/whisper(message as text) + if(dormant) + usr << "\red You're dormant!" + return + if(!host) + usr << "\red You can't speak without host!" + return + + return host.whisper(message) + +// Make the host do things +mob/living/parasite/meme/me_verb(message as text) + set name = "Me" + + + if(dormant) + usr << "\red You're dormant!" + return + + if(!host) + usr << "\red You can't emote without host!" + return + + return host.me_verb(message) + +// A meme understands everything their host understands +mob/living/parasite/meme/say_understands(mob/other) + if(!host) return 0 + + return host.say_understands(other) + +// Try to use amount points, return 1 if successful +mob/living/parasite/meme/proc/use_points(amount) + if(dormant) + usr << "\red You're dormant!" + return + if(src.meme_points < amount) + src << "* You don't have enough meme points(need [amount])." + return 0 + + src.meme_points -= round(amount) + return 1 + +// Let the meme choose one of his indoctrinated mobs as target +mob/living/parasite/meme/proc/select_indoctrinated(var/title, var/message) + var/list/candidates + + // Can only affect other mobs thant he host if not blinded + if(blinded) + candidates = list() + src << "\red You are blinded, so you can not affect mobs other than your host." + else + candidates = indoctrinated.Copy() + + candidates.Add(src.host) + + var/mob/target = null + if(candidates.len == 1) + target = candidates[1] + else + var/selected + + var/list/text_candidates = list() + var/list/map_text_to_mob = list() + + for(var/mob/living/carbon/human/M in candidates) + text_candidates += M.real_name + map_text_to_mob[M.real_name] = M + + selected = input(message,title) as null|anything in text_candidates + if(!selected) return null + + target = map_text_to_mob[selected] + + return target + + +// A meme can make people hear things with the thought ability +mob/living/parasite/meme/verb/Thought() + set category = "Meme" + set name = "Thought(150)" + set desc = "Implants a thought into the target, making them think they heard someone talk." + + if(meme_points < 150) + // just call use_points() to give the standard failure message + use_points(150) + return + + var/list/candidates = indoctrinated.Copy() + if(!(src.host in candidates)) + candidates.Add(src.host) + + var/mob/target = select_indoctrinated("Thought", "Select a target which will hear your thought.") + + if(!target) return + + var/speaker = input("Select the voice in which you would like to make yourself heard.", "Voice") as null|text + if(!speaker) return + + var/message = input("What would you like to say?", "Message") as null + if(!message) return + + // Use the points at the end rather than the beginning, because the user might cancel + if(!use_points(150)) return + + message = say_quote(message) + var/rendered = "[speaker] [message]" + target.show_message(rendered) + + usr << "You make [target] hear: [rendered]" + +// Mutes the host +mob/living/parasite/meme/verb/Mute() + set category = "Meme" + set name = "Mute(250)" + set desc = "Prevents your host from talking for a while." + + if(!src.host) return + if(!host.speech_allowed) + usr << "\red Your host already can't speak.." + return + if(!use_points(250)) return + + spawn + // backup the host incase we switch hosts after using the verb + var/mob/host = src.host + + host << "\red Your tongue feels numb.. You lose your ability to speak." + usr << "\red Your host can't speak anymore." + + host.speech_allowed = 0 + + sleep(1200) + + host.speech_allowed = 1 + host << "\red Your tongue has feeling again.." + usr << "\red [host] can speak again." + +// Makes the host unable to emote +mob/living/parasite/meme/verb/Paralyze() + set category = "Meme" + set name = "Paralyze(250)" + set desc = "Prevents your host from using emote for a while." + + if(!src.host) return + if(!host.emote_allowed) + usr << "\red Your host already can't use body language.." + return + if(!use_points(250)) return + + spawn + // backup the host incase we switch hosts after using the verb + var/mob/host = src.host + + host << "\red Your body feels numb.. You lose your ability to use body language." + usr << "\red Your host can't use body language anymore." + + host.emote_allowed = 0 + + sleep(1200) + + host.emote_allowed = 1 + host << "\red Your body has feeling again.." + usr << "\red [host] can use body language again." + + + +// Cause great agony with the host, used for conditioning the host +mob/living/parasite/meme/verb/Agony() + set category = "Meme" + set name = "Agony(200)" + set desc = "Causes significant pain in your host." + + if(!src.host) return + if(!use_points(200)) return + + spawn + // backup the host incase we switch hosts after using the verb + var/mob/host = src.host + + host.paralysis = max(host.paralysis, 2) + + host.flash_weak_pain() + host << "\red You feel excrutiating pain all over your body! It is so bad you can't think or articulate yourself properly.." + + usr << "You send a jolt of agonizing pain through [host], they should be unable to concentrate on anything else for half a minute." + + host.emote("scream") + + for(var/i=0, i<10, i++) + host.stuttering = 2 + sleep(50) + if(prob(80)) host.flash_weak_pain() + if(prob(10)) host.paralysis = max(host.paralysis, 2) + if(prob(15)) host.emote("twitch") + else if(prob(15)) host.emote("scream") + else if(prob(10)) host.emote("collapse") + + if(i == 10) + host << "\red THE PAIN! AGHH, THE PAIN! MAKE IT STOP! ANYTHING TO MAKE IT STOP!" + + host << "\red The pain subsides.." + +// Cause great joy with the host, used for conditioning the host +mob/living/parasite/meme/verb/Joy() + set category = "Meme" + set name = "Joy(200)" + set desc = "Causes significant joy in your host." + + if(!src.host) return + if(!use_points(200)) return + + spawn + var/mob/host = src.host + host.druggy = max(host.druggy, 50) + host.slurring = max(host.slurring, 10) + + usr << "You stimulate [host.name]'s brain, injecting waves of endorphines and dopamine into the tissue. They should now forget all their worries, particularly relating to you, for around a minute." + + host << "\red You are feeling wonderful! Your head is numb and drowsy, and you can't help forgetting all the worries in the world." + + while(host.druggy > 0) + sleep(10) + + host << "\red You are feeling clear-headed again.." + +// Cause the target to hallucinate. +mob/living/parasite/meme/verb/Hallucinate() + set category = "Meme" + set name = "Hallucinate(300)" + set desc = "Makes your host hallucinate, has a short delay." + + var/mob/target = select_indoctrinated("Hallucination", "Who should hallucinate?") + + if(!target) return + if(!use_points(300)) return + + target.hallucination += 100 + + usr << "You make [target] hallucinate." + +// Jump to a closeby target through a whisper +mob/living/parasite/meme/verb/SubtleJump(mob/living/carbon/human/target as mob in world) + set category = "Meme" + set name = "Subtle Jump(350)" + set desc = "Move to a closeby human through a whisper." + + if(!istype(target, /mob/living/carbon/human) || !target.mind) + src << "You can't jump to this creature.." + return + if(!(target in view(1, host)+src)) + src << "The target is not close enough." + return + + // Find out whether we can speak + if (host.silent || (host.disabilities & 64)) + src << "Your host can't speak.." + return + + if(!use_points(350)) return + + for(var/mob/M in view(1, host)) + M.show_message("[host] whispers something incoherent.",2) // 2 stands for hearable message + + // Find out whether the target can hear + if(target.disabilities & 32 || target.ear_deaf) + src << "Your target doesn't seem to hear you.." + return + + src.exit_host() + src.enter_host(target) + + usr << "You successfully jumped to [target]." + +// Jump to a distant target through a shout +mob/living/parasite/meme/verb/ObviousJump(mob/living/carbon/human/target as mob in world) + set category = "Meme" + set name = "Obvious Jump(750)" + set desc = "Move to any mob in view through a shout." + + if(!istype(target, /mob/living/carbon/human) || !target.mind) + src << "You can't jump to this creature.." + return + if(!(target in view(host))) + src << "The target is not close enough." + return + + // Find out whether we can speak + if (host.silent || (host.disabilities & 64)) + src << "Your host can't speak.." + return + + if(!use_points(750)) return + + for(var/mob/M in view(host)+src) + M.show_message("[host] screams something incoherent!",2) // 2 stands for hearable message + + // Find out whether the target can hear + if(target.disabilities & 32 || target.ear_deaf) + src << "Your target doesn't seem to hear you.." + return + + src.exit_host() + src.enter_host(target) + + usr << "You successfully jumped to [target]." + +// Jump to an attuned mob for free +mob/living/parasite/meme/verb/AttunedJump(mob/living/carbon/human/target as mob in world) + set category = "Meme" + set name = "Attuned Jump(0)" + set desc = "Move to a mob in sight that you have already attuned." + + if(!istype(target, /mob/living/carbon/human) || !target.mind) + src << "You can't jump to this creature.." + return + if(!(target in view(host))) + src << "You need to make eye-contact with the target." + return + if(!(target in indoctrinated)) + src << "You need to attune the target first." + return + + src.exit_host() + src.enter_host(target) + + usr << "You successfully jumped to [target]." + +// ATTUNE a mob, adding it to the indoctrinated list +mob/living/parasite/meme/verb/Attune() + set category = "Meme" + set name = "Attune(400)" + set desc = "Change the host's brain structure, making it easier for you to manipulate him." + + if(host in src.indoctrinated) + usr << "You have already attuned this host." + return + + if(!host) return + if(!use_points(400)) return + + src.indoctrinated.Add(host) + + usr << "You successfully indoctrinated [host]." + host << "\red Your head feels a bit roomier.." + +// Enables the mob to take a lot more damage +mob/living/parasite/meme/verb/Analgesic() + set category = "Meme" + set name = "Analgesic(500)" + set desc = "Combat drug that the host to move normally, even under life-threatening pain." + + if(!host) return + if(!(host in indoctrinated)) + usr << "\red You need to attune the host first." + return + if(!use_points(500)) return + + usr << "You inject drugs into [host]." + host << "\red You feel your body strengthen and your pain subside.." + host.analgesic = 60 + while(host.analgesic > 0) + sleep(10) + host << "\red The dizziness wears off, and you can feel pain again.." + + +mob/proc/clearHUD() + update_clothing() + if(client) client.screen.Cut() + +// Take control of the mob +mob/living/parasite/meme/verb/Possession() + set category = "Meme" + set name = "Possession(500)" + set desc = "Take direct control of the host for a while." + + if(!host) return + if(!(host in indoctrinated)) + usr << "\red You need to attune the host first." + return + if(!use_points(500)) return + + usr << "You take control of [host]!" + host << "\red Everything goes black.." + + spawn + var/mob/dummy = new() + dummy.loc = 0 + dummy.sight = BLIND + + var/datum/mind/host_mind = host.mind + var/datum/mind/meme_mind = src.mind + + host_mind.transfer_to(dummy) + meme_mind.transfer_to(host) + host_mind.current.clearHUD() + host.update_clothing() + + sleep(600) + + meme_mind.transfer_to(src) + host_mind.transfer_to(host) + meme_mind.current.clearHUD() + host.update_clothing() + src << "\red You lose control.." + + del dummy + +// Enter dormant mode, increases meme point gain +mob/living/parasite/meme/verb/Dormant() + set category = "Meme" + set name = "Dormant(100)" + set desc = "Speed up point recharging, will force you to cease all actions until all points are recharged." + + if(!host) return + if(!use_points(100)) return + + usr << "You enter dormant mode.. You won't be able to take action until all your points have recharged." + + dormant = 1 + + while(meme_points < MAXIMUM_MEME_POINTS) + sleep(10) + + dormant = 0 + + usr << "\red You have regained all points and exited dormant mode!" + +mob/living/parasite/meme/verb/Show_Points() + set category = "Meme" + + usr << "Meme Points: [src.meme_points]/[MAXIMUM_MEME_POINTS]" + +// Game mode helpers, used for theft objectives +// -------------------------------------------- +mob/living/parasite/check_contents_for(t) + if(!host) return 0 + + 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/living/say.dm b/code/modules/mob/living/say.dm index 6aac5e6ab71..1e9cb3248a3 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -32,6 +32,10 @@ var/message_old = message message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) + if(!speech_allowed && usr == src) + usr << "\red You can't speak." + return + if (!message) return @@ -340,7 +344,6 @@ listening|=M */ - listening = get_mobs_in_view(message_range, src) for(var/mob/M in world) if (!M.client) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index f23dc12c89b..e80a0c2164a 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -70,6 +70,7 @@ var/obj/screen/zone_sel/zone_sel = null var/emote_allowed = 1 + var/speech_allowed = 1 var/computer_id = null var/lastattacker = null var/lastattacked = null diff --git a/code/modules/mob/new_player/preferences.dm b/code/modules/mob/new_player/preferences.dm index 610e8851b8e..735519d936d 100644 --- a/code/modules/mob/new_player/preferences.dm +++ b/code/modules/mob/new_player/preferences.dm @@ -13,6 +13,7 @@ var/global/list/special_roles = list( //keep synced with the defines BE_* in set "pAI candidate" = 1, // -- TLE "cultist" = IS_MODE_COMPILED("cult"), "infested monkey" = IS_MODE_COMPILED("monkey"), + "meme" = IS_MODE_COMPILED("meme"), ) /* var/global/list/special_roles = list( //keep synced with the defines BE_* in setup.dm --rastaf @@ -39,6 +40,7 @@ var/const BE_CULTIST =(1<<7) BE_MONKEY =(1<<8) BE_PAI =(1<<9) + BE_MEME =(1<<10) diff --git a/code/modules/mob/organ/pain.dm b/code/modules/mob/organ/pain.dm index fb0715138c3..21825ce8eaa 100644 --- a/code/modules/mob/organ/pain.dm +++ b/code/modules/mob/organ/pain.dm @@ -38,6 +38,8 @@ mob/living/carbon/human/proc/handle_pain() return if(reagents.has_reagent("oxycodone")) return + if(analgesic) + return var/maxdam = 0 var/datum/organ/external/damaged_organ = null for(var/name in organs) diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index f8e9fb94320..04f1c747853 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -17,7 +17,7 @@ message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - usr.emote("me",1,message) + src.emote("me",1,message) /mob/proc/say_dead(var/message) var/name = src.real_name diff --git a/html/changelog.html b/html/changelog.html index 2c6d46d8209..c4269cd328a 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -48,7 +48,12 @@ should be listed in the changelog upon commit though. Thanks. -->
-

19 May 2012

+

19 May 2012

+

cib updated:

+
    +
  • Implemented Memetic Anomaly game mode. A meme is a kind of parasite that can live in a human host and jump from player to player. They have traitor-like objectives, but must achieve them by controlling hosts, rather than doing it themselves.
  • +
  • Credits for the idea goes to: SCP-1312-1 . Playtesters: Erthilo, Cael_Aislin, AfterShave and critica.
  • +

TG updated:

  • You can now swap hands by clicking with your middle mouse button (you have to click on a visible object though, that's the catch).
  • @@ -131,7 +136,7 @@ should be listed in the changelog upon commit though. Thanks. -->
  • Fixes mech weapons not firing.
  • Added cable restraints, made from 15 lengths of cable coil by right-click a cable coil in-hand and choosing 'Make Cable Restraints'. These works just like handcuffs, but only take 30 seconds to break!
  • The Head of Personnel can now change alert levels.
  • -
  • Fixes lockdown computers being blank.
  • +
  • Fixes lockdown computers being blank.
@@ -387,4 +392,4 @@ Credits Section

Some icons by Yusuke Kamiyamane. All rights reserved. Licensed under a Creative Commons Attribution 3.0 License.

- \ No newline at end of file +