diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 8a9f995d26f..eb2d7b924a7 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -20,6 +20,12 @@ datum/mind
var/rev_cooldown = 0
proc/transfer_to(mob/new_character)
+ // multikey information is stored in the mob, not the mind, so
+ // we need to clean this stuff up to avoid multikey alerts
+ current.lastKnownIP = null
+ current.computer_id = null
+
+
if(current)
current.mind = null
@@ -28,6 +34,9 @@ datum/mind
new_character.key = key
+ // mob/Login() will handle setting the new mob's lastKnownIP and
+ // computer_id for us
+
proc/store_memory(new_text)
memory += "[new_text]
"
diff --git a/code/game/gamemodes/meme/meme.dm b/code/game/gamemodes/meme/meme.dm
index c362caf4d43..a98bc524441 100644
--- a/code/game/gamemodes/meme/meme.dm
+++ b/code/game/gamemodes/meme/meme.dm
@@ -11,7 +11,9 @@
var
- var/datum/mind/first_host = null
+ var/list/datum/mind/first_hosts = list()
+ var/list/assigned_hosts = list()
+
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
@@ -41,23 +43,34 @@
if(!..())
return 0
+ // for every 10 players, get 1 meme, and for each meme, get a host
+ // also make sure that there's at least one meme and one host
+ recommended_enemies = max(src.num_players() / 10 * 2, 2)
+
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)
+ // for each 2 possible memes, add one meme and one host
+ while(possible_memes.len >= 2)
+ var/datum/mind/meme = pick(possible_memes)
+ possible_memes.Remove(meme)
- first_host = pick(possible_memes)
+ var/datum/mind/first_host = pick(possible_memes)
+ possible_memes.Remove(first_host)
- modePlayer += meme
- modePlayer += first_host
- memes += meme
+ modePlayer += meme
+ modePlayer += first_host
+ memes += meme
+ first_hosts += first_host
- meme.assigned_role = "MODE" //So they aren't chosen for other jobs.
- meme.special_role = "Meme"
+ // so that we can later know which host belongs to which meme
+ assigned_hosts[meme.key] = first_host
+
+ meme.assigned_role = "MODE" //So they aren't chosen for other jobs.
+ meme.special_role = "Meme"
return 1
@@ -72,6 +85,9 @@
var/mob/original = meme.current
meme.transfer_to(M)
M.clearHUD()
+
+ // get the host for this meme
+ var/datum/mind/first_host = assigned_hosts[meme.key]
M.enter_host(first_host.current)
forge_meme_objectives(meme, first_host)
@@ -79,7 +95,7 @@
break
- // TODO: make it possible to have multiple memes with multiple hosts
+ log_admin("Created [memes.len] memes.")
spawn (rand(waittime_l, waittime_h))
send_intercept()
@@ -135,7 +151,6 @@
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]"
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 7d9b6d65f85..3d3ab39da8b 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -134,6 +134,26 @@ var/global/BSACooldown = 0
M.change_mob_type( /mob/living/simple_animal/constructwraith , null, null, delmob)
if("shade")
M.change_mob_type( /mob/living/simple_animal/shade , null, null, delmob)
+ if("meme")
+ var/mob/living/parasite/meme/newmeme = new
+ M.mind.transfer_to(newmeme)
+ newmeme.clearHUD()
+
+ var/found = 0
+ for(var/mob/living/carbon/human/H in world) if(H.client && !H.parasites.len)
+ found = 1
+ newmeme.enter_host(H)
+
+ log_admin("[H] has become [newmeme.key]'s host")
+
+ break
+
+ // if there was no host, abort
+ if(!found)
+ newmeme.mind.transfer_to(M)
+ log_admin("Failed to find host for meme [M.key]. Aborting.")
+
+
if(href_list["view_player_info"])
show_player_info(href_list["view_player_info"])
@@ -2435,6 +2455,7 @@ var/global/BSACooldown = 0
body += "Builder , "
body += "Wraith \] "
body += "Shade"
+ body += "Meme"
body += "
"
if (M.client)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 750174757f1..795f2cf8ab4 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -892,7 +892,7 @@
switch(alert("You sure you wish to edit this mob's appearance?",,"Yes","No"))
if("No")
return
- if(istype(M,/mob/living/carbon/human/tajaran) || istype(M,/mob/living/carbon/human/birdman))
+ if(istype(M,/mob/living/carbon/human/tajaran))
usr << "\red Humanoid aliens do not have an editable appearance... yet!"
else
var/new_facial = input("Please select facial hair color.", "Character Generation") as color
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index 4fc203c2f4e..c497f3d023e 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -252,6 +252,28 @@
dat += "
| Changeling not found! |
"
dat += ""
+ if(ticker.mode.memes.len > 0)
+ dat += "
| Memes | | |
"
+ for(var/datum/mind/meme in ticker.mode.memes)
+ var/mob/living/parasite/meme/M = meme.current
+ if(M)
+ dat += "| [M.key][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""] | "
+ dat += "PM | "
+ dat += "Show Objective |
"
+
+ // need this check because the meme may be possessing someone right now
+ if(istype(M))
+ dat += "\tAttuned: "
+ for(var/mob/attuned in M.indoctrinated)
+ if(attuned.key)
+ dat += "[attuned.real_name]([attuned.key]) "
+ else
+ dat += "[attuned.real_name] "
+ else
+ dat += " | | Changeling not found! |
"
+ dat += "
"
+
+
if(ticker.mode.wizards.len > 0)
dat += "
| Wizards | | |
"
for(var/datum/mind/wizard in ticker.mode.wizards)
diff --git a/code/modules/mob/living/parasite/meme.dm b/code/modules/mob/living/parasite/meme.dm
index f0c219252fd..61989ce78b0 100644
--- a/code/modules/mob/living/parasite/meme.dm
+++ b/code/modules/mob/living/parasite/meme.dm
@@ -90,8 +90,8 @@ mob/living/parasite/meme/Life()
src.death()
return
- if(host.blinded) src.blinded = 1
- else src.blinded = 0
+ if(host.blinded && host.stat != 1) src.blinded = 1
+ else src.blinded = 0
mob/living/parasite/meme/death()
@@ -382,10 +382,15 @@ mob/living/parasite/meme/verb/SubtleJump(mob/living/carbon/human/target as mob i
src << "Your target doesn't seem to hear you.."
return
+ if(target.parasites.len > 0)
+ src << "Your target already is possessed by something.."
+ return
+
src.exit_host()
src.enter_host(target)
usr << "You successfully jumped to [target]."
+ log_admin("[src.key] has 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)
@@ -415,10 +420,15 @@ mob/living/parasite/meme/verb/ObviousJump(mob/living/carbon/human/target as mob
src << "Your target doesn't seem to hear you.."
return
+ if(target.parasites.len > 0)
+ src << "Your target already is possessed by something.."
+ return
+
src.exit_host()
src.enter_host(target)
usr << "You successfully jumped to [target]."
+ log_admin("[src.key] has 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)
@@ -441,6 +451,8 @@ mob/living/parasite/meme/verb/AttunedJump(mob/living/carbon/human/target as mob
usr << "You successfully jumped to [target]."
+ log_admin("[src.key] has jumped to [target]")
+
// ATTUNE a mob, adding it to the indoctrinated list
mob/living/parasite/meme/verb/Attune()
set category = "Meme"
@@ -459,6 +471,8 @@ mob/living/parasite/meme/verb/Attune()
usr << "You successfully indoctrinated [host]."
host << "\red Your head feels a bit roomier.."
+ log_admin("[src.key] has attuned [host]")
+
// Enables the mob to take a lot more damage
mob/living/parasite/meme/verb/Analgesic()
set category = "Meme"
@@ -511,8 +525,12 @@ mob/living/parasite/meme/verb/Possession()
host_mind.current.clearHUD()
host.update_clothing()
+ log_admin("[meme_mind.key] has taken possession of [host]([host_mind.key])")
+
sleep(600)
+ log_admin("[meme_mind.key] has lost possession of [host]([host_mind.key])")
+
meme_mind.transfer_to(src)
host_mind.transfer_to(host)
meme_mind.current.clearHUD()