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] "
+ 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:
+
+
TG updated:
Some icons by Yusuke Kamiyamane. All rights reserved. Licensed under a Creative Commons Attribution 3.0 License.