[READY] New Side Antagonist: The Creep! (#42021)

cl
add: Added a new antagonist, the Creep!
add: Chosen from a random event, the Creep will become obsessed with one person, feeling amazing around them and terrible when they aren't. They will have objectives to steal heirlooms, take pictures, hug, and kill coworkers. They also have to kill the obsession but some objectives can only be completed while the obsession is alive, requiring you to protect the obsession!
/cl

There really is no reason to betray people, generally. Maybe some stuff with security and traitors working things out, but a traitor based around betrayal and the general insane creep idea is just cool.

One Creep can show up, and they get objectives to mess with their obsession leading to the obsession's death. Here is their objectives:

    A random creepy objective. It could be taking a photo with them in it, hugging them, etc
    After your are done with your objectives, kill them. you can't complete the objectives when they are dead!

Creeps do not get the determination mood. Instead, when they are around their obsession, they feel AMAZING! If they are around too long, they will stutter, cough, and generally spaghetti out when talking near their obsession. So if you are going to talk near them, do it when you aren't entranced.
This commit is contained in:
tralezab
2019-01-07 19:18:35 +13:00
committed by oranges
parent e5304a95ee
commit 240cb31e12
18 changed files with 496 additions and 4 deletions
+284
View File
@@ -0,0 +1,284 @@
/datum/antagonist/creep
name = "Creep"
show_in_antagpanel = TRUE
antagpanel_category = "Other"
job_rank = ROLE_CREEP
show_name_in_check_antagonists = TRUE
roundend_category = "creeps"
silent = TRUE //not actually silent, because greet will be called by the trauma anyway.
var/datum/brain_trauma/special/creep/trauma
/datum/antagonist/creep/admin_add(datum/mind/new_owner,mob/admin)
var/mob/living/carbon/C = new_owner.current
if(!istype(C))
to_chat(admin, "[roundend_category] come from a brain trauma, so they need to at least be a carbon!")
return
if(!C.getorgan(/obj/item/organ/brain)) // If only I had a brain
to_chat(admin, "[roundend_category] come from a brain trauma, so they need to HAVE A BRAIN.")
return
message_admins("[key_name_admin(admin)] made [key_name_admin(new_owner)] into [name].")
log_admin("[key_name(admin)] made [key_name(new_owner)] into [name].")
//PRESTO FUCKIN MAJESTO
C.gain_trauma(/datum/brain_trauma/special/creep)//ZAP
/datum/antagonist/creep/greet()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/creepalert.ogg', 100, FALSE, pressure_affected = FALSE)
to_chat(owner, "<span class='boldannounce'>You are the Creep!</span>")
to_chat(owner, "<B>They would call it an obsession. They would call you crazy, because they don't understand your unrequited love.<br>All you know is that you love [trauma.obsession]. And you. will. show them.</B>")
to_chat(owner, "<B>I will surely go insane if I don't spend enough time around [trauma.obsession], but when i'm near them too long it gets too difficult to speak properly, making me look like a CREEP!</B>")
to_chat(owner, "<span class='boldannounce'>The gods would like to remind you that this role, as with all other antags, does not allow you to break ANY server rules, especially Rule 8 (These rules being listed from the \"Rules\" button at the top right of your mind's screen). Feel free to murder and pillage just like any other antag, though.</span>")
owner.announce_objectives()
/datum/antagonist/creep/Destroy()
if(trauma)
qdel(trauma)
. = ..()
/datum/antagonist/creep/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_creep_icons_added(M)
/datum/antagonist/creep/remove_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_creep_icons_removed(M)
/datum/antagonist/creep/proc/forge_objectives(var/datum/mind/obsessionmind)
var/list/objectives_left = list("spendtime", "polaroid", "hug")
var/datum/objective/assassinate/creep/kill = new
kill.owner = owner
kill.target = obsessionmind
var/datum/quirk/family_heirloom/family_heirloom
for(var/datum/quirk/quirky in obsessionmind.current.roundstart_quirks)
if(istype(quirky, /datum/quirk/family_heirloom))
family_heirloom = quirky
break
if(family_heirloom)//oh, they have an heirloom? Well you know we have to steal that.
objectives_left += "heirloom"
if(obsessionmind.assigned_role && obsessionmind.assigned_role != "Captain" && !(obsessionmind.assigned_role in GLOB.nonhuman_positions))
objectives_left += "jealous"//while this will sometimes be a free objective during lowpop, this works fine most of the time and is less intensive
for(var/i in 1 to 3)
var/chosen_objective = pick(objectives_left)
objectives_left.Remove(chosen_objective)
switch(chosen_objective)
if("spendtime")
var/datum/objective/spendtime/spendtime = new
spendtime.owner = owner
spendtime.target = obsessionmind
objectives += spendtime
if("polaroid")
var/datum/objective/polaroid/polaroid = new
polaroid.owner = owner
polaroid.target = obsessionmind
objectives += polaroid
if("hug")
var/datum/objective/hug/hug = new
hug.owner = owner
hug.target = obsessionmind
objectives += hug
if("heirloom")
var/datum/objective/steal/heirloom_thief/heirloom_thief = new
heirloom_thief.owner = owner
heirloom_thief.target = obsessionmind//while you usually wouldn't need this for stealing, we need the name of the obsession
heirloom_thief.steal_target = family_heirloom.heirloom
objectives += heirloom_thief
if("jealous")
var/datum/objective/assassinate/jealous/jealous = new
jealous.owner = owner
jealous.target = obsessionmind//will reroll into a coworker on the objective itself
objectives += jealous
objectives += kill//finally add the assassinate last, because you'd have to complete it last to greentext.
for(var/datum/objective/O in objectives)
O.update_explanation_text()
/datum/antagonist/creep/roundend_report_header()
return "<span class='header'>Someone became a creep!</span><br>"
/datum/antagonist/creep/roundend_report()
var/list/report = list()
if(!owner)
CRASH("antagonist datum without owner")
report += "<b>[printplayer(owner)]</b>"
var/objectives_complete = TRUE
if(objectives.len)
report += printobjectives(objectives)
for(var/datum/objective/objective in objectives)
if(!objective.check_completion())
objectives_complete = FALSE
break
if(trauma)
if(trauma.total_time_creeping > 0)
report += "<span class='greentext'>The [name] spent a total of [DisplayTimeText(trauma.total_time_creeping)] being near [trauma.obsession]!</span>"
else
report += "<span class='redtext'>The [name] did not go near their obsession the entire round! That's extremely impressive, but you are a shit [name]!</span>"
else
report += "<span class='redtext'>The [name] had no trauma attached to their antagonist ways! Either it bugged out or an admin incorrectly gave this good samaritan antag and it broke! You might as well show yourself!!</span>"
if(objectives.len == 0 || objectives_complete)
report += "<span class='greentext big'>The [name] was successful!</span>"
else
report += "<span class='redtext big'>The [name] has failed!</span>"
return report.Join("<br>")
//////////////////////////////////////////////////
///CREEPY objectives (few chosen per obsession)///
//////////////////////////////////////////////////
/datum/objective/assassinate/creep //just a creepy version of assassinate
/datum/objective/assassinate/creep/update_explanation_text()
..()
if(target && target.current)
explanation_text = "Murder [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
else
message_admins("WARNING! [ADMIN_LOOKUPFLW(owner)] creep objectives forged without an obsession!")
explanation_text = "Free Objective"
/datum/objective/assassinate/jealous //assassinate, but it changes the target to someone else in the previous target's department. cool, right?
var/datum/mind/old //the target the coworker was picked from.
/datum/objective/assassinate/jealous/update_explanation_text()
..()
old = find_coworker(target)
if(target && target.current && old)
explanation_text = "Murder [target.name], [old]'s coworker."
else
explanation_text = "Free Objective"
/datum/objective/assassinate/jealous/proc/find_coworker(datum/mind/oldmind)//returning null = free objective
if(!oldmind.assigned_role)
return
var/list/viable_coworkers = list()
var/list/all_coworkers = list()
var/chosen_department
var/their_chosen_department
//note that command and sillycone are gone because borgs can't be obsessions and the heads have their respective department. Sorry cap, your place is more with centcom or something
if(oldmind.assigned_role in GLOB.security_positions)
chosen_department = "security"
if(oldmind.assigned_role in GLOB.engineering_positions)
chosen_department = "engineering"
if(oldmind.assigned_role in GLOB.medical_positions)
chosen_department = "medical"
if(oldmind.assigned_role in GLOB.science_positions)
chosen_department = "science"
if(oldmind.assigned_role in GLOB.supply_positions)
chosen_department = "supply"
if(oldmind.assigned_role in GLOB.civilian_positions)
chosen_department = "civilian"
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
if(!H.mind)
continue
if(!H.mind.assigned_role || H == oldmind.current || H.mind.has_antag_datum(/datum/antagonist/creep)) //the jealousy target has to have a job, and not be the obsession or creep.
continue
//this won't be called often thankfully.
if(H.mind.assigned_role in GLOB.security_positions)
their_chosen_department = "security"
if(H.mind.assigned_role in GLOB.engineering_positions)
their_chosen_department = "engineering"
if(H.mind.assigned_role in GLOB.medical_positions)
their_chosen_department = "medical"
if(H.mind.assigned_role in GLOB.science_positions)
their_chosen_department = "science"
if(H.mind.assigned_role in GLOB.supply_positions)
their_chosen_department = "supply"
if(H.mind.assigned_role in GLOB.civilian_positions)
their_chosen_department = "civilian"
if(their_chosen_department != chosen_department)
continue
viable_coworkers += H
if(viable_coworkers.len > 0)//find someone in the same department
target = pick(viable_coworkers)
else if(all_coworkers.len > 0)//find someone who works on the station
else
return//there is nobody but you and the obsession
return oldmind
/datum/objective/spendtime //spend some time around someone, handled by the creep trauma since that ticks
name = "spendtime"
var/timer = 1800 //5 minutes
/datum/objective/spendtime/update_explanation_text()
if(timer == initial(timer))//just so admins can mess with it
timer += pick(-600, 0)
var/datum/antagonist/creep/creeper = owner.has_antag_datum(/datum/antagonist/creep)
if(target && target.current && creeper)
creeper.trauma.attachedcreepobj = src
explanation_text = "Spend [DisplayTimeText(timer)] around [target.name] while they're alive."
else
explanation_text = "Free Objective"
/datum/objective/spendtime/check_completion()
return timer <= 0 || explanation_text == "Free Objective"
/datum/objective/hug//this objective isn't perfect. hugging the correct amount of times, then switching bodies, might fail the objective anyway. maybe i'll come back and fix this sometime.
name = "hugs"
var/hugs_needed
/datum/objective/hug/update_explanation_text()
..()
if(!hugs_needed)//just so admins can mess with it
hugs_needed = rand(4,6)
var/datum/antagonist/creep/creeper = owner.has_antag_datum(/datum/antagonist/creep)
if(target && target.current && creeper)
explanation_text = "Hug [target.name] [hugs_needed] times while they're alive."
else
explanation_text = "Free Objective"
/datum/objective/hug/check_completion()
var/datum/antagonist/creep/creeper = owner.has_antag_datum(/datum/antagonist/creep)
if(!creeper || !creeper.trauma || !hugs_needed)
return TRUE//free objective
return creeper.trauma.obsession_hug_count >= hugs_needed
/datum/objective/polaroid //take a picture of the target with you in it.
name = "polaroid"
/datum/objective/polaroid/update_explanation_text()
..()
if(target && target.current)
explanation_text = "Take a photo with [target.name] while they're alive."
else
explanation_text = "Free Objective"
/datum/objective/polaroid/check_completion()
var/list/datum/mind/owners = get_owners()
for(var/datum/mind/M in owners)
if(!isliving(M.current))
continue
var/list/all_items = M.current.GetAllContents() //this should get things in cheesewheels, books, etc.
for(var/obj/I in all_items) //Check for wanted items
if(istype(I, /obj/item/photo))
var/obj/item/photo/P = I
if(P.picture.mobs_seen.Find(owner) && P.picture.mobs_seen.Find(target) && !P.picture.dead_seen.Find(target))//you are in the picture, they are but they are not dead.
return TRUE
return FALSE
/datum/objective/steal/heirloom_thief //exactly what it sounds like, steal someone's heirloom.
name = "heirloomthief"
/datum/objective/steal/heirloom_thief/update_explanation_text()
..()
if(steal_target)
explanation_text = "Steal [target.name]'s family heirloom, [steal_target] they cherish."
else
explanation_text = "Free Objective"
/datum/antagonist/creep/proc/update_creep_icons_added(var/mob/living/carbon/human/creep)
var/datum/atom_hud/antag/creephud = GLOB.huds[ANTAG_HUD_CREEP]
creephud.join_hud(creep)
set_antag_hud(creep, "creep")
/datum/antagonist/creep/proc/update_creep_icons_removed(var/mob/living/carbon/human/creep)
var/datum/atom_hud/antag/creephud = GLOB.huds[ANTAG_HUD_CREEP]
creephud.leave_hud(creep)
set_antag_hud(creep, null)
+25
View File
@@ -0,0 +1,25 @@
/datum/round_event_control/creep
name = "Creep Awakening"
typepath = /datum/round_event/creep
max_occurrences = 1
min_players = 20
/datum/round_event/creep
fakeable = FALSE
/datum/round_event/creep/start()
for(var/mob/living/carbon/human/H in shuffle(GLOB.player_list))
if(!H.client)
continue
if(H.stat == DEAD)
continue
if(!H.mind.assigned_role || H.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL] || H.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_ANTAG]) //prevents ashwalkers falling in love with crewmembers they never met
continue
var/alreadycreepy = H.mind.has_antag_datum(/datum/antagonist/creep)
if(alreadycreepy)
continue
if(!H.getorgan(/obj/item/organ/brain))
continue
H.gain_trauma(/datum/brain_trauma/special/creep)
announce_to_ghosts(H)
break
@@ -273,6 +273,8 @@
M.visible_message("<span class='notice'>[M] hugs [src] to make [p_them()] feel better!</span>", \
"<span class='notice'>You hug [src] to make [p_them()] feel better!</span>")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "hug", /datum/mood_event/hug)
for(var/datum/brain_trauma/trauma in M.get_traumas())
trauma.on_hug(M, src)
AdjustStun(-60)
AdjustKnockdown(-60)
AdjustUnconscious(-60)
+7 -1
View File
@@ -1,6 +1,8 @@
/datum/picture
var/picture_name = "picture"
var/picture_desc = "This is a picture."
var/list/mobs_seen = list()
var/list/dead_seen = list()
var/caption
var/icon/picture_image
var/icon/picture_icon
@@ -10,11 +12,15 @@
var/logpath //If the picture has been logged this is the path.
var/id //this var is NOT protected because the worst you can do with this that you couldn't do otherwise is overwrite photos, and photos aren't going to be used as attack logs/investigations anytime soon.
/datum/picture/New(name, desc, image, icon, size_x, size_y, bp, caption_, autogenerate_icon)
/datum/picture/New(name, desc, mobs_spotted, dead_spotted, image, icon, size_x, size_y, bp, caption_, autogenerate_icon)
if(!isnull(name))
picture_name = name
if(!isnull(desc))
picture_desc = desc
if(!isnull(mobs_spotted))
mobs_seen = mobs_spotted
if(!isnull(dead_spotted))
dead_seen = dead_spotted
if(!isnull(image))
picture_image = image
if(!isnull(icon))
+6 -1
View File
@@ -155,6 +155,8 @@
size_x = CLAMP(size_x, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT)
size_y = CLAMP(size_y, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT)
var/list/desc = list("This is a photo of an area of [size_x+1] meters by [size_y+1] meters.")
var/list/mobs_spotted = list()
var/list/dead_spotted = list()
var/ai_user = isAI(user)
var/list/seen
var/list/viewlist = (user && user.client)? getviewsize(user.client.view) : getviewsize(world.view)
@@ -174,6 +176,9 @@
blueprints = TRUE
for(var/i in mobs)
var/mob/M = i
mobs_spotted += M
if(M.stat == DEAD)
dead_spotted += M
desc += M.get_photo_description(src)
var/psize_x = (size_x * 2 + 1) * world.icon_size
@@ -185,7 +190,7 @@
temp.Scale(psize_x, psize_y)
temp.Blend(get_icon, ICON_OVERLAY)
var/datum/picture/P = new("picture", desc.Join(" "), temp, null, psize_x, psize_y, blueprints)
var/datum/picture/P = new("picture", desc.Join(" "), mobs_spotted, dead_spotted, temp, null, psize_x, psize_y, blueprints)
after_picture(user, P, flag)
blending = FALSE