Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into upstream-merge-33463
This commit is contained in:
@@ -477,6 +477,8 @@
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/action/spell_action/spell
|
||||
|
||||
/datum/action/spell_action/spell/IsAvailable()
|
||||
if(!target)
|
||||
return FALSE
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
/datum/action/item_action/zoom_speed_action
|
||||
name = "Toggle Zooming Speed"
|
||||
icon_icon = 'icons/mob/actions/actions_spells.dmi'
|
||||
button_icon_state = "projectile"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/item_action/zoom_lock_action
|
||||
name = "Switch Zoom Mode"
|
||||
icon_icon = 'icons/mob/actions/actions_items.dmi'
|
||||
button_icon_state = "zoom_mode"
|
||||
background_icon_state = "bg_tech"
|
||||
+3
-26
@@ -373,32 +373,9 @@
|
||||
ion = list()
|
||||
|
||||
/datum/ai_laws/proc/show_laws(who)
|
||||
|
||||
if (devillaws && devillaws.len) //Yes, devil laws go in FRONT of zeroth laws, as the devil must still obey it's ban/obligation.
|
||||
for(var/i in devillaws)
|
||||
to_chat(who, "666. [i]")
|
||||
|
||||
if (zeroth)
|
||||
to_chat(who, "0. [zeroth]")
|
||||
|
||||
for (var/index = 1, index <= ion.len, index++)
|
||||
var/law = ion[index]
|
||||
var/num = ionnum()
|
||||
to_chat(who, "[num]. [law]")
|
||||
|
||||
var/number = 1
|
||||
for (var/index = 1, index <= inherent.len, index++)
|
||||
var/law = inherent[index]
|
||||
|
||||
if (length(law) > 0)
|
||||
to_chat(who, "[number]. [law]")
|
||||
number++
|
||||
|
||||
for (var/index = 1, index <= supplied.len, index++)
|
||||
var/law = supplied[index]
|
||||
if (length(law) > 0)
|
||||
to_chat(who, "[number]. [law]")
|
||||
number++
|
||||
var/list/printable_laws = get_law_list(include_zeroth = TRUE)
|
||||
for(var/law in printable_laws)
|
||||
to_chat(who,law)
|
||||
|
||||
/datum/ai_laws/proc/clear_zeroth_law(force) //only removes zeroth from antag ai if force is 1
|
||||
if(force)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/datum/antagonist/abductor
|
||||
name = "Abductor"
|
||||
roundend_category = "abductors"
|
||||
job_rank = ROLE_ABDUCTOR
|
||||
var/datum/objective_team/abductor_team/team
|
||||
var/datum/team/abductor_team/team
|
||||
var/sub_role
|
||||
var/outfit
|
||||
var/landmark_type
|
||||
@@ -19,7 +20,7 @@
|
||||
landmark_type = /obj/effect/landmark/abductor/scientist
|
||||
greet_text = "Use your stealth technology and equipment to incapacitate humans for your scientist to retrieve."
|
||||
|
||||
/datum/antagonist/abductor/create_team(datum/objective_team/abductor_team/new_team)
|
||||
/datum/antagonist/abductor/create_team(datum/team/abductor_team/new_team)
|
||||
if(!new_team)
|
||||
return
|
||||
if(!istype(new_team))
|
||||
@@ -70,3 +71,65 @@
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
var/datum/species/abductor/A = H.dna.species
|
||||
A.scientist = TRUE
|
||||
|
||||
|
||||
/datum/team/abductor_team
|
||||
member_name = "abductor"
|
||||
var/team_number
|
||||
var/list/datum/mind/abductees = list()
|
||||
|
||||
/datum/team/abductor_team/is_solo()
|
||||
return FALSE
|
||||
|
||||
/datum/team/abductor_team/proc/add_objective(datum/objective/O)
|
||||
O.team = src
|
||||
O.update_explanation_text()
|
||||
objectives += O
|
||||
|
||||
/datum/team/abductor_team/roundend_report()
|
||||
var/list/result = list()
|
||||
|
||||
var/won = TRUE
|
||||
for(var/datum/objective/O in objectives)
|
||||
if(!O.check_completion())
|
||||
won = FALSE
|
||||
if(won)
|
||||
result += "<span class='greentext big'>[name] team fulfilled its mission!</span>"
|
||||
else
|
||||
result += "<span class='redtext big'>[name] team failed its mission.</span>"
|
||||
|
||||
result += "<span class='header'>The abductors of [name] were:</span>"
|
||||
for(var/datum/mind/abductor_mind in members)
|
||||
result += printplayer(abductor_mind)
|
||||
result += printobjectives(abductor_mind)
|
||||
|
||||
return result.Join("<br>")
|
||||
|
||||
|
||||
/datum/antagonist/abductee
|
||||
name = "Abductee"
|
||||
roundend_category = "abductees"
|
||||
|
||||
/datum/antagonist/abductee/on_gain()
|
||||
give_objective()
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/abductee/greet()
|
||||
to_chat(owner, "<span class='warning'><b>Your mind snaps!</b></span>")
|
||||
to_chat(owner, "<big><span class='warning'><b>You can't remember how you got here...</b></span></big>")
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/abductee/proc/give_objective()
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(istype(H))
|
||||
H.gain_trauma_type(BRAIN_TRAUMA_MILD)
|
||||
var/objtype = (prob(75) ? /datum/objective/abductee/random : pick(subtypesof(/datum/objective/abductee/) - /datum/objective/abductee/random))
|
||||
var/datum/objective/abductee/O = new objtype()
|
||||
objectives += O
|
||||
owner.objectives += objectives
|
||||
|
||||
/datum/antagonist/abductee/apply_innate_effects(mob/living/mob_override)
|
||||
SSticker.mode.update_abductor_icons_added(mob_override ? mob_override.mind : owner)
|
||||
|
||||
/datum/antagonist/abductee/remove_innate_effects(mob/living/mob_override)
|
||||
SSticker.mode.update_abductor_icons_removed(mob_override ? mob_override.mind : owner)
|
||||
@@ -2,6 +2,8 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
|
||||
/datum/antagonist
|
||||
var/name = "Antagonist"
|
||||
var/roundend_category = "other antagonists" //Section of roundend report, datums with same category will be displayed together, also default header for the section
|
||||
var/show_in_roundend = TRUE //Set to false to hide the antagonists from roundend report
|
||||
var/datum/mind/owner //Mind that owns this datum
|
||||
var/silent = FALSE //Silent will prevent the gain/lose texts to show
|
||||
var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum
|
||||
@@ -9,6 +11,7 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
var/delete_on_mind_deletion = TRUE
|
||||
var/job_rank
|
||||
var/replace_banned = TRUE //Should replace jobbaned player with ghosts if granted.
|
||||
var/list/objectives = list()
|
||||
|
||||
/datum/antagonist/New(datum/mind/new_owner)
|
||||
GLOB.antagonists += src
|
||||
@@ -46,7 +49,7 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
return
|
||||
|
||||
//Assign default team and creates one for one of a kind team antagonists
|
||||
/datum/antagonist/proc/create_team(datum/objective_team/team)
|
||||
/datum/antagonist/proc/create_team(datum/team/team)
|
||||
return
|
||||
|
||||
//Proc called when the datum is given to a mind.
|
||||
@@ -81,7 +84,7 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
LAZYREMOVE(owner.antag_datums, src)
|
||||
if(!silent && owner.current)
|
||||
farewell()
|
||||
var/datum/objective_team/team = get_team()
|
||||
var/datum/team/team = get_team()
|
||||
if(team)
|
||||
team.remove_member(owner)
|
||||
qdel(src)
|
||||
@@ -96,9 +99,62 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
/datum/antagonist/proc/get_team()
|
||||
return
|
||||
|
||||
//Individual roundend report
|
||||
/datum/antagonist/proc/roundend_report()
|
||||
var/list/report = list()
|
||||
|
||||
if(!owner)
|
||||
CRASH("antagonist datum without owner")
|
||||
|
||||
report += printplayer(owner)
|
||||
|
||||
var/objectives_complete = TRUE
|
||||
if(owner.objectives.len)
|
||||
report += printobjectives(owner)
|
||||
for(var/datum/objective/objective in owner.objectives)
|
||||
if(!objective.check_completion())
|
||||
objectives_complete = FALSE
|
||||
break
|
||||
|
||||
if(owner.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>")
|
||||
|
||||
//Displayed at the start of roundend_category section, default to roundend_category header
|
||||
/datum/antagonist/proc/roundend_report_header()
|
||||
return "<span class='header'>The [roundend_category] were:</span><br>"
|
||||
|
||||
//Displayed at the end of roundend_category section
|
||||
/datum/antagonist/proc/roundend_report_footer()
|
||||
return
|
||||
|
||||
//Should probably be on ticker or job ss ?
|
||||
/proc/get_antagonists(antag_type,specific = FALSE)
|
||||
. = list()
|
||||
for(var/datum/antagonist/A in GLOB.antagonists)
|
||||
if(!specific && istype(A,antag_type) || specific && A.type == antag_type)
|
||||
. += A.owner
|
||||
. += A.owner
|
||||
|
||||
|
||||
|
||||
//This datum will autofill the name with special_role
|
||||
//Used as placeholder for minor antagonists, please create proper datums for these
|
||||
/datum/antagonist/auto_custom
|
||||
|
||||
/datum/antagonist/auto_custom/on_gain()
|
||||
..()
|
||||
name = owner.special_role
|
||||
//Add all objectives not already owned by other datums to this one.
|
||||
var/list/already_registered_objectives = list()
|
||||
for(var/datum/antagonist/A in owner.antag_datums)
|
||||
if(A == src)
|
||||
continue
|
||||
else
|
||||
already_registered_objectives |= A.objectives
|
||||
objectives = owner.objectives - already_registered_objectives
|
||||
|
||||
//This one is created by admin tools for custom objectives
|
||||
/datum/antagonist/custom
|
||||
@@ -2,12 +2,12 @@
|
||||
name = "Brother"
|
||||
job_rank = ROLE_BROTHER
|
||||
var/special_role = "blood brother"
|
||||
var/datum/objective_team/brother_team/team
|
||||
var/datum/team/brother_team/team
|
||||
|
||||
/datum/antagonist/brother/New(datum/mind/new_owner)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/brother/create_team(datum/objective_team/brother_team/new_team)
|
||||
/datum/antagonist/brother/create_team(datum/team/brother_team/new_team)
|
||||
if(!new_team)
|
||||
return
|
||||
if(!istype(new_team))
|
||||
@@ -55,3 +55,71 @@
|
||||
|
||||
/datum/antagonist/brother/proc/finalize_brother()
|
||||
SSticker.mode.update_brother_icons_added(owner)
|
||||
|
||||
|
||||
/datum/team/brother_team
|
||||
name = "brotherhood"
|
||||
member_name = "blood brother"
|
||||
var/meeting_area
|
||||
|
||||
/datum/team/brother_team/is_solo()
|
||||
return FALSE
|
||||
|
||||
/datum/team/brother_team/proc/update_name()
|
||||
var/list/last_names = list()
|
||||
for(var/datum/mind/M in members)
|
||||
var/list/split_name = splittext(M.name," ")
|
||||
last_names += split_name[split_name.len]
|
||||
|
||||
name = last_names.Join(" & ")
|
||||
|
||||
/datum/team/brother_team/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
parts += "<span class='header'>The blood brothers of [name] were:</span>"
|
||||
for(var/datum/mind/M in members)
|
||||
parts += printplayer(M)
|
||||
var/win = TRUE
|
||||
var/objective_count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
|
||||
else
|
||||
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
win = FALSE
|
||||
objective_count++
|
||||
if(win)
|
||||
parts += "<span class='greentext'>The blood brothers were successful!</span>"
|
||||
else
|
||||
parts += "<span class='redtext'>The blood brothers have failed!</span>"
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
|
||||
/datum/team/brother_team/proc/add_objective(datum/objective/O, needs_target = FALSE)
|
||||
O.team = src
|
||||
if(needs_target)
|
||||
O.find_target()
|
||||
O.update_explanation_text()
|
||||
objectives += O
|
||||
|
||||
/datum/team/brother_team/proc/forge_brother_objectives()
|
||||
objectives = list()
|
||||
var/is_hijacker = prob(10)
|
||||
for(var/i = 1 to max(1, CONFIG_GET(number/brother_objectives_amount) + (members.len > 2) - is_hijacker))
|
||||
forge_single_objective()
|
||||
if(is_hijacker)
|
||||
if(!locate(/datum/objective/hijack) in objectives)
|
||||
add_objective(new/datum/objective/hijack)
|
||||
else if(!locate(/datum/objective/escape) in objectives)
|
||||
add_objective(new/datum/objective/escape)
|
||||
|
||||
/datum/team/brother_team/proc/forge_single_objective()
|
||||
if(prob(50))
|
||||
if(LAZYLEN(active_ais()) && prob(100/GLOB.joined_player_list.len))
|
||||
add_objective(new/datum/objective/destroy, TRUE)
|
||||
else if(prob(30))
|
||||
add_objective(new/datum/objective/maroon, TRUE)
|
||||
else
|
||||
add_objective(new/datum/objective/assassinate, TRUE)
|
||||
else
|
||||
add_objective(new/datum/objective/steal, TRUE)
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
/datum/antagonist/changeling
|
||||
name = "Changeling"
|
||||
roundend_category = "changelings"
|
||||
job_rank = ROLE_CHANGELING
|
||||
|
||||
var/you_are_greet = TRUE
|
||||
var/give_objectives = TRUE
|
||||
var/list/objectives = list()
|
||||
var/team_mode = FALSE //Should assign team objectives ?
|
||||
|
||||
//Changeling Stuff
|
||||
@@ -223,7 +223,8 @@
|
||||
if(verbose)
|
||||
to_chat(user, "<span class='warning'>[target] is not compatible with our biology.</span>")
|
||||
return
|
||||
if((target.disabilities & NOCLONE) || (target.disabilities & HUSK))
|
||||
|
||||
if((target.has_disability(DISABILITY_NOCLONE)) || (target.has_disability(DISABILITY_NOCLONE)))
|
||||
if(verbose)
|
||||
to_chat(user, "<span class='warning'>DNA of [target] is ruined beyond usability!</span>")
|
||||
return
|
||||
@@ -478,4 +479,35 @@
|
||||
/datum/antagonist/changeling/xenobio
|
||||
name = "Xenobio Changeling"
|
||||
give_objectives = FALSE
|
||||
show_in_roundend = FALSE //These are here for admin tracking purposes only
|
||||
you_are_greet = FALSE
|
||||
|
||||
/datum/antagonist/changeling/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
var/changelingwin = 1
|
||||
if(!owner.current)
|
||||
changelingwin = 0
|
||||
|
||||
parts += printplayer(owner)
|
||||
|
||||
//Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed.
|
||||
parts += "<b>Changeling ID:</b> [changelingID]."
|
||||
parts += "<b>Genomes Extracted:</b> [absorbedcount]"
|
||||
parts += " "
|
||||
if(objectives.len)
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</b></span>"
|
||||
else
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
changelingwin = 0
|
||||
count++
|
||||
|
||||
if(changelingwin)
|
||||
parts += "<span class='greentext'>The changeling was successful!</span>"
|
||||
else
|
||||
parts += "<span class='redtext'>The changeling has failed.</span>"
|
||||
|
||||
return parts.Join("<br>")
|
||||
@@ -1,8 +1,11 @@
|
||||
//CLOCKCULT PROOF OF CONCEPT
|
||||
/datum/antagonist/clockcult
|
||||
name = "Clock Cultist"
|
||||
var/datum/action/innate/hierophant/hierophant_network = new()
|
||||
roundend_category = "clock cultists"
|
||||
job_rank = ROLE_SERVANT_OF_RATVAR
|
||||
var/datum/action/innate/hierophant/hierophant_network = new()
|
||||
var/datum/team/clockcult/clock_team
|
||||
var/make_team = TRUE //This should be only false for tutorial scarabs
|
||||
|
||||
/datum/antagonist/clockcult/silent
|
||||
silent = TRUE
|
||||
@@ -11,6 +14,22 @@
|
||||
qdel(hierophant_network)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/clockcult/get_team()
|
||||
return clock_team
|
||||
|
||||
/datum/antagonist/clockcult/create_team(datum/team/clockcult/new_team)
|
||||
if(!new_team && make_team)
|
||||
//TODO blah blah same as the others, allow multiple
|
||||
for(var/datum/antagonist/clockcult/H in GLOB.antagonists)
|
||||
if(H.clock_team)
|
||||
clock_team = H.clock_team
|
||||
return
|
||||
clock_team = new /datum/team/clockcult
|
||||
return
|
||||
if(make_team && !istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
clock_team = new_team
|
||||
|
||||
/datum/antagonist/clockcult/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
if(.)
|
||||
@@ -156,7 +175,7 @@
|
||||
SSticker.mode.servants_of_ratvar -= owner
|
||||
SSticker.mode.update_servant_icons_removed(owner)
|
||||
if(!silent)
|
||||
owner.current.visible_message("<span class='big'>[owner] seems to have remembered their true allegiance!</span>", ignored_mob = owner.current)
|
||||
owner.current.visible_message("<span class='deconversion_message'>[owner] seems to have remembered their true allegiance!</span>", ignored_mob = owner.current)
|
||||
to_chat(owner, "<span class='userdanger'>A cold, cold darkness flows through your mind, extinguishing the Justiciar's light and all of your memories as his servant.</span>")
|
||||
owner.current.log_message("<font color=#BE8700>Has renounced the cult of Ratvar!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
owner.wipe_memory()
|
||||
@@ -164,3 +183,35 @@
|
||||
if(iscyborg(owner.current))
|
||||
to_chat(owner.current, "<span class='warning'>Despite your freedom from Ratvar's influence, you are still irreparably damaged and no longer possess certain functions such as AI linking.</span>")
|
||||
. = ..()
|
||||
|
||||
|
||||
/datum/team/clockcult
|
||||
name = "Clockcult"
|
||||
var/list/objective
|
||||
var/datum/mind/eminence
|
||||
|
||||
/datum/team/clockcult/proc/check_clockwork_victory()
|
||||
if(GLOB.clockwork_gateway_activated)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/clockcult/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
if(check_clockwork_victory())
|
||||
parts += "<span class='greentext big'>Ratvar's servants defended the Ark until its activation!</span>"
|
||||
else
|
||||
parts += "<span class='redtext big'>The Ark was destroyed! Ratvar will rust away for all eternity!</span>"
|
||||
parts += " "
|
||||
parts += "<b>The servants' objective was:</b> [CLOCKCULT_OBJECTIVE]."
|
||||
parts += "<b>Construction Value(CV)</b> was: <b>[GLOB.clockwork_construction_value]</b>"
|
||||
for(var/i in SSticker.scripture_states)
|
||||
if(i != SCRIPTURE_DRIVER)
|
||||
parts += "<b>[i] scripture</b> was: <b>[SSticker.scripture_states[i] ? "UN":""]LOCKED</b>"
|
||||
if(eminence)
|
||||
parts += "<span class='header'>The Eminence was:</span> [printplayer(eminence)]"
|
||||
if(members.len)
|
||||
parts += "<span class='header'>Ratvar's servants were:</span>"
|
||||
parts += printplayerlist(members - eminence)
|
||||
|
||||
return "<div class='panel clockborder'>[parts.Join("<br>")]</div>"
|
||||
+197
-62
@@ -2,84 +2,103 @@
|
||||
|
||||
/datum/antagonist/cult
|
||||
name = "Cultist"
|
||||
roundend_category = "cultists"
|
||||
var/datum/action/innate/cult/comm/communion = new
|
||||
var/datum/action/innate/cult/mastervote/vote = new
|
||||
job_rank = ROLE_CULTIST
|
||||
var/ignore_implant = FALSE
|
||||
var/give_equipment = FALSE
|
||||
|
||||
var/datum/team/cult/cult_team
|
||||
|
||||
/datum/antagonist/cult/get_team()
|
||||
return cult_team
|
||||
|
||||
/datum/antagonist/cult/create_team(datum/team/cult/new_team)
|
||||
if(!new_team)
|
||||
//todo remove this and allow admin buttons to create more than one cult
|
||||
for(var/datum/antagonist/cult/H in GLOB.antagonists)
|
||||
if(H.cult_team)
|
||||
cult_team = H.cult_team
|
||||
return
|
||||
cult_team = new /datum/team/cult
|
||||
cult_team.setup_objectives()
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
cult_team = new_team
|
||||
|
||||
/datum/antagonist/cult/proc/add_objectives()
|
||||
objectives |= cult_team.objectives
|
||||
owner.objectives |= objectives
|
||||
|
||||
/datum/antagonist/cult/proc/remove_objectives()
|
||||
owner.objectives -= objectives
|
||||
|
||||
/datum/antagonist/cult/Destroy()
|
||||
QDEL_NULL(communion)
|
||||
QDEL_NULL(vote)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/cult/proc/add_objectives()
|
||||
var/list/target_candidates = list()
|
||||
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
||||
if(player.mind && !player.mind.has_antag_datum(ANTAG_DATUM_CULT) && !is_convertable_to_cult(player) && (player != owner) && player.stat != DEAD)
|
||||
target_candidates += player.mind
|
||||
if(target_candidates.len == 0)
|
||||
message_admins("Cult Sacrifice: Could not find unconvertable target, checking for convertable target.")
|
||||
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
||||
if(player.mind && !player.mind.has_antag_datum(ANTAG_DATUM_CULT) && (player != owner) && player.stat != DEAD)
|
||||
target_candidates += player.mind
|
||||
listclearnulls(target_candidates)
|
||||
if(LAZYLEN(target_candidates))
|
||||
GLOB.sac_mind = pick(target_candidates)
|
||||
if(!GLOB.sac_mind)
|
||||
message_admins("Cult Sacrifice: ERROR - Null target chosen!")
|
||||
else
|
||||
var/datum/job/sacjob = SSjob.GetJob(GLOB.sac_mind.assigned_role)
|
||||
var/datum/preferences/sacface = GLOB.sac_mind.current.client.prefs
|
||||
var/icon/reshape = get_flat_human_icon(null, sacjob, sacface)
|
||||
reshape.Shift(SOUTH, 4)
|
||||
reshape.Shift(EAST, 1)
|
||||
reshape.Crop(7,4,26,31)
|
||||
reshape.Crop(-5,-3,26,30)
|
||||
GLOB.sac_image = reshape
|
||||
else
|
||||
message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!")
|
||||
GLOB.sac_complete = TRUE
|
||||
SSticker.mode.cult_objectives += "sacrifice"
|
||||
if(!GLOB.summon_spots.len)
|
||||
while(GLOB.summon_spots.len < SUMMON_POSSIBILITIES)
|
||||
var/area/summon = pick(GLOB.sortedAreas - GLOB.summon_spots)
|
||||
if(summon && (summon.z in GLOB.station_z_levels) && summon.valid_territory)
|
||||
GLOB.summon_spots += summon
|
||||
SSticker.mode.cult_objectives += "eldergod"
|
||||
|
||||
/datum/antagonist/cult/proc/cult_memorization(datum/mind/cult_mind)
|
||||
var/mob/living/current = cult_mind.current
|
||||
for(var/obj_count = 1,obj_count <= SSticker.mode.cult_objectives.len,obj_count++)
|
||||
var/explanation
|
||||
switch(SSticker.mode.cult_objectives[obj_count])
|
||||
if("sacrifice")
|
||||
if(GLOB.sac_mind)
|
||||
explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it."
|
||||
else
|
||||
explanation = "The veil has already been weakened here, proceed to the final objective."
|
||||
GLOB.sac_complete = TRUE
|
||||
if("eldergod")
|
||||
explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie'. <b>The summoning can only be accomplished in [english_list(GLOB.summon_spots)] - where the veil is weak enough for the ritual to begin.</b>"
|
||||
if(!silent)
|
||||
to_chat(current, "<B>Objective #[obj_count]</B>: [explanation]")
|
||||
cult_mind.memory += "<B>Objective #[obj_count]</B>: [explanation]<BR>"
|
||||
|
||||
/datum/antagonist/cult/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
if(. && !ignore_implant)
|
||||
. = is_convertable_to_cult(new_owner.current)
|
||||
. = is_convertable_to_cult(new_owner.current,cult_team)
|
||||
|
||||
/datum/antagonist/cult/greet()
|
||||
to_chat(owner, "<span class='userdanger'>You are a member of the cult!</span>")
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/bloodcult.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/cult/on_gain()
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(!LAZYLEN(SSticker.mode.cult_objectives))
|
||||
add_objectives()
|
||||
add_objectives()
|
||||
if(give_equipment)
|
||||
equip_cultist()
|
||||
SSticker.mode.cult += owner // Only add after they've been given objectives
|
||||
cult_memorization(owner)
|
||||
SSticker.mode.update_cult_icons_added(owner)
|
||||
current.log_message("<font color=#960000>Has been converted to the cult of Nar'Sie!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
if(GLOB.blood_target && GLOB.blood_target_image && current.client)
|
||||
current.client.images += GLOB.blood_target_image
|
||||
|
||||
if(cult_team.blood_target && cult_team.blood_target_image && current.client)
|
||||
current.client.images += cult_team.blood_target_image
|
||||
|
||||
|
||||
/datum/antagonist/cult/proc/equip_cultist(tome=FALSE)
|
||||
var/mob/living/carbon/H = owner.current
|
||||
if(!istype(H))
|
||||
return
|
||||
if (owner.assigned_role == "Clown")
|
||||
to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
|
||||
H.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
if(tome)
|
||||
. += cult_give_item(/obj/item/tome, H)
|
||||
else
|
||||
. += cult_give_item(/obj/item/paper/talisman/supply, H)
|
||||
to_chat(owner, "These will help you start the cult on this station. Use them well, and remember - you are not the only one.</span>")
|
||||
|
||||
|
||||
/datum/antagonist/cult/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob)
|
||||
var/list/slots = list(
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store
|
||||
)
|
||||
|
||||
var/T = new item_path(mob)
|
||||
var/item_name = initial(item_path.name)
|
||||
var/where = mob.equip_in_one_of_slots(T, slots)
|
||||
if(!where)
|
||||
to_chat(mob, "<span class='userdanger'>Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1).</span>")
|
||||
return 0
|
||||
else
|
||||
to_chat(mob, "<span class='danger'>You have a [item_name] in your [where].</span>")
|
||||
if(where == "backpack")
|
||||
var/obj/item/storage/B = mob.back
|
||||
B.orient2hud(mob)
|
||||
B.show_to(mob)
|
||||
return 1
|
||||
|
||||
/datum/antagonist/cult/apply_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
@@ -89,7 +108,7 @@
|
||||
current.faction |= "cult"
|
||||
current.grant_language(/datum/language/narsie)
|
||||
current.verbs += /mob/living/proc/cult_help
|
||||
if(!GLOB.cult_mastered)
|
||||
if(!cult_team.cult_mastered)
|
||||
vote.Grant(current)
|
||||
communion.Grant(current)
|
||||
current.throw_alert("bloodsense", /obj/screen/alert/bloodsense)
|
||||
@@ -107,6 +126,7 @@
|
||||
current.clear_alert("bloodsense")
|
||||
|
||||
/datum/antagonist/cult/on_removal()
|
||||
remove_objectives()
|
||||
owner.wipe_memory()
|
||||
SSticker.mode.cult -= owner
|
||||
SSticker.mode.update_cult_icons_removed(owner)
|
||||
@@ -114,8 +134,8 @@
|
||||
owner.current.visible_message("<span class='big'>[owner.current] looks like [owner.current.p_they()] just reverted to their old faith!</span>", ignored_mob = owner.current)
|
||||
to_chat(owner.current, "<span class='userdanger'>An unfamiliar white light flashes through your mind, cleansing the taint of the Geometer and all your memories as her servant.</span>")
|
||||
owner.current.log_message("<font color=#960000>Has renounced the cult of Nar'Sie!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
if(GLOB.blood_target && GLOB.blood_target_image && owner.current.client)
|
||||
owner.current.client.images -= GLOB.blood_target_image
|
||||
if(cult_team.blood_target && cult_team.blood_target_image && owner.current.client)
|
||||
owner.current.client.images -= cult_team.blood_target_image
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/cult/master
|
||||
@@ -145,7 +165,7 @@
|
||||
var/mob/living/current = owner.current
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
if(!GLOB.reckoning_complete)
|
||||
if(!cult_team.reckoning_complete)
|
||||
reckoning.Grant(current)
|
||||
bloodmark.Grant(current)
|
||||
throwing.Grant(current)
|
||||
@@ -162,3 +182,118 @@
|
||||
throwing.Remove(current)
|
||||
current.update_action_buttons_icon()
|
||||
current.remove_status_effect(/datum/status_effect/cult_master)
|
||||
|
||||
/datum/team/cult
|
||||
name = "Cult"
|
||||
|
||||
var/blood_target
|
||||
var/image/blood_target_image
|
||||
var/blood_target_reset_timer
|
||||
|
||||
var/cult_vote_called = FALSE
|
||||
var/cult_mastered = FALSE
|
||||
var/reckoning_complete = FALSE
|
||||
|
||||
|
||||
/datum/team/cult/proc/setup_objectives()
|
||||
//SAC OBJECTIVE , todo: move this to objective internals
|
||||
var/list/target_candidates = list()
|
||||
var/datum/objective/sacrifice/sac_objective = new
|
||||
sac_objective.team = src
|
||||
|
||||
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
||||
if(player.mind && !player.mind.has_antag_datum(ANTAG_DATUM_CULT) && !is_convertable_to_cult(player) && player.stat != DEAD)
|
||||
target_candidates += player.mind
|
||||
|
||||
if(target_candidates.len == 0)
|
||||
message_admins("Cult Sacrifice: Could not find unconvertable target, checking for convertable target.")
|
||||
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
||||
if(player.mind && !player.mind.has_antag_datum(ANTAG_DATUM_CULT) && player.stat != DEAD)
|
||||
target_candidates += player.mind
|
||||
listclearnulls(target_candidates)
|
||||
if(LAZYLEN(target_candidates))
|
||||
sac_objective.target = pick(target_candidates)
|
||||
sac_objective.update_explanation_text()
|
||||
|
||||
var/datum/job/sacjob = SSjob.GetJob(sac_objective.target.assigned_role)
|
||||
var/datum/preferences/sacface = sac_objective.target.current.client.prefs
|
||||
var/icon/reshape = get_flat_human_icon(null, sacjob, sacface)
|
||||
reshape.Shift(SOUTH, 4)
|
||||
reshape.Shift(EAST, 1)
|
||||
reshape.Crop(7,4,26,31)
|
||||
reshape.Crop(-5,-3,26,30)
|
||||
sac_objective.sac_image = reshape
|
||||
|
||||
objectives += sac_objective
|
||||
else
|
||||
message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!")
|
||||
|
||||
|
||||
//SUMMON OBJECTIVE
|
||||
|
||||
var/datum/objective/eldergod/summon_objective = new()
|
||||
summon_objective.team = src
|
||||
objectives += summon_objective
|
||||
|
||||
/datum/objective/sacrifice
|
||||
var/sacced = FALSE
|
||||
var/sac_image
|
||||
|
||||
/datum/objective/sacrifice/check_completion()
|
||||
return sacced || completed
|
||||
|
||||
/datum/objective/sacrifice/update_explanation_text()
|
||||
if(target && !sacced)
|
||||
explanation_text = "Sacrifice [target], the [target.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it."
|
||||
else
|
||||
explanation_text = "The veil has already been weakened here, proceed to the final objective."
|
||||
|
||||
/datum/objective/eldergod
|
||||
var/summoned = FALSE
|
||||
var/list/summon_spots = list()
|
||||
|
||||
/datum/objective/eldergod/New()
|
||||
..()
|
||||
var/sanity = 0
|
||||
while(summon_spots.len < SUMMON_POSSIBILITIES && sanity < 100)
|
||||
var/area/summon = pick(GLOB.sortedAreas - summon_spots)
|
||||
if(summon && (summon.z in GLOB.station_z_levels) && summon.valid_territory)
|
||||
summon_spots += summon
|
||||
sanity++
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/eldergod/update_explanation_text()
|
||||
explanation_text = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie'. <b>The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin.</b>"
|
||||
|
||||
/datum/objective/eldergod/check_completion()
|
||||
return summoned || completed
|
||||
|
||||
/datum/team/cult/proc/check_cult_victory()
|
||||
for(var/datum/objective/O in objectives)
|
||||
if(!O.check_completion())
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/team/cult/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
if(check_cult_victory())
|
||||
parts += "<span class='greentext big'>The cult has succeeded! Nar-sie has snuffed out another torch in the void!</span>"
|
||||
else
|
||||
parts += "<span class='redtext big'>The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!</span>"
|
||||
|
||||
if(objectives.len)
|
||||
parts += "<b>The cultists' objectives were:</b>"
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</span>"
|
||||
else
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
count++
|
||||
|
||||
if(members.len)
|
||||
parts += "<span class='header'>The cultists were:</span>"
|
||||
parts += printplayerlist(members)
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
@@ -1,5 +1,6 @@
|
||||
/datum/antagonist/traitor
|
||||
name = "Traitor"
|
||||
roundend_category = "traitors"
|
||||
job_rank = ROLE_TRAITOR
|
||||
var/should_specialise = FALSE //do we split into AI and human, set to true on inital assignment only
|
||||
var/ai_datum = ANTAG_DATUM_TRAITOR_AI
|
||||
@@ -8,7 +9,6 @@
|
||||
var/employer = "The Syndicate"
|
||||
var/give_objectives = TRUE
|
||||
var/should_give_codewords = TRUE
|
||||
var/list/objectives_given = list()
|
||||
|
||||
/datum/antagonist/traitor/human
|
||||
var/should_equip = TRUE
|
||||
@@ -52,9 +52,9 @@
|
||||
if(should_specialise)
|
||||
return ..()//we never did any of this anyway
|
||||
SSticker.mode.traitors -= owner
|
||||
for(var/O in objectives_given)
|
||||
for(var/O in objectives)
|
||||
owner.objectives -= O
|
||||
objectives_given = list()
|
||||
objectives = list()
|
||||
if(!silent && owner.current)
|
||||
to_chat(owner.current,"<span class='userdanger'> You are no longer the [special_role]! </span>")
|
||||
owner.special_role = null
|
||||
@@ -71,11 +71,11 @@
|
||||
|
||||
/datum/antagonist/traitor/proc/add_objective(var/datum/objective/O)
|
||||
owner.objectives += O
|
||||
objectives_given += O
|
||||
objectives += O
|
||||
|
||||
/datum/antagonist/traitor/proc/remove_objective(var/datum/objective/O)
|
||||
owner.objectives -= O
|
||||
objectives_given -= O
|
||||
objectives -= O
|
||||
|
||||
/datum/antagonist/traitor/proc/forge_traitor_objectives()
|
||||
return
|
||||
@@ -294,3 +294,53 @@
|
||||
where = "In your [equipped_slot]"
|
||||
to_chat(mob, "<BR><BR><span class='info'>[where] is a folder containing <b>secret documents</b> that another Syndicate group wants. We have set up a meeting with one of their agents on station to make an exchange. Exercise extreme caution as they cannot be trusted and may be hostile.</span><BR>")
|
||||
|
||||
//TODO Collate
|
||||
/datum/antagonist/traitor/roundend_report()
|
||||
var/list/result = list()
|
||||
|
||||
var/traitorwin = TRUE
|
||||
|
||||
result += printplayer(owner)
|
||||
|
||||
var/TC_uses = 0
|
||||
var/uplink_true = FALSE
|
||||
var/purchases = ""
|
||||
for(var/datum/component/uplink/H in GLOB.uplinks)
|
||||
if(H && H.owner && H.owner == owner.key)
|
||||
TC_uses += H.spent_telecrystals
|
||||
uplink_true = TRUE
|
||||
purchases += H.purchase_log.generate_render(FALSE)
|
||||
|
||||
var/objectives_text = ""
|
||||
if(objectives.len)//If the traitor had no objectives, don't need to process this.
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
|
||||
else
|
||||
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
traitorwin = FALSE
|
||||
count++
|
||||
|
||||
if(uplink_true)
|
||||
var/uplink_text = "(used [TC_uses] TC) [purchases]"
|
||||
if(TC_uses==0 && traitorwin)
|
||||
var/static/icon/badass = icon('icons/badass.dmi', "badass")
|
||||
uplink_text += "<BIG>[icon2html(badass, world)]</BIG>"
|
||||
result += uplink_text
|
||||
|
||||
result += objectives_text
|
||||
|
||||
var/special_role_text = lowertext(name)
|
||||
|
||||
if(traitorwin)
|
||||
result += "<span class='greentext'>The [special_role_text] was successful!</span>"
|
||||
else
|
||||
result += "<span class='redtext'>The [special_role_text] has failed!</span>"
|
||||
SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg')
|
||||
|
||||
return result.Join("<br>")
|
||||
|
||||
/datum/antagonist/traitor/roundend_report_footer()
|
||||
return "<br><b>The code phrases were:</b> <span class='codephrase'>[GLOB.syndicate_code_phrase]</span><br>\
|
||||
<b>The code responses were:</b> <span class='codephrase'>[GLOB.syndicate_code_response]</span><br>"
|
||||
@@ -86,6 +86,7 @@ GLOBAL_LIST_INIT(devil_syllable, list("hal", "ve", "odr", "neit", "ci", "quon",
|
||||
GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", ", the Lord of all things", ", Jr."))
|
||||
/datum/antagonist/devil
|
||||
name = "Devil"
|
||||
roundend_category = "devils"
|
||||
job_rank = ROLE_DEVIL
|
||||
//Don't delete upon mind destruction, otherwise soul re-selling will break.
|
||||
delete_on_mind_deletion = FALSE
|
||||
@@ -508,6 +509,35 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
|
||||
owner.RemoveSpell(S)
|
||||
.=..()
|
||||
|
||||
/datum/antagonist/devil/proc/printdevilinfo()
|
||||
var/list/parts = list()
|
||||
parts += "The devil's true name is: [truename]"
|
||||
parts += "The devil's bans were:"
|
||||
parts += "[GLOB.TAB][GLOB.lawlorify[LORE][ban]]"
|
||||
parts += "[GLOB.TAB][GLOB.lawlorify[LORE][bane]]"
|
||||
parts += "[GLOB.TAB][GLOB.lawlorify[LORE][obligation]]"
|
||||
parts += "[GLOB.TAB][GLOB.lawlorify[LORE][banish]]"
|
||||
return parts.Join("<br>")
|
||||
|
||||
/datum/antagonist/devil/roundend_report()
|
||||
var/list/parts = list()
|
||||
parts += printplayer(owner)
|
||||
parts += printdevilinfo()
|
||||
parts += printobjectives(owner)
|
||||
return parts.Join("<br>")
|
||||
|
||||
/datum/antagonist/devil/roundend_report_footer()
|
||||
//sintouched go here for now as a hack , TODO proper antag datum for these
|
||||
var/list/parts = list()
|
||||
if(SSticker.mode.sintouched.len)
|
||||
parts += "<span class='header'>The sintouched were:</span>"
|
||||
var/list/sintouchedUnique = uniqueList(SSticker.mode.sintouched)
|
||||
for(var/S in sintouchedUnique)
|
||||
var/datum/mind/sintouched_mind = S
|
||||
parts += printplayer(sintouched_mind)
|
||||
parts += printobjectives(sintouched_mind)
|
||||
return parts.Join("<br>")
|
||||
|
||||
//A simple super light weight datum for the codex gigas.
|
||||
/datum/fakeDevil
|
||||
var/truename
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
#define MONKEYS_ESCAPED 1
|
||||
#define MONKEYS_LIVED 2
|
||||
#define MONKEYS_DIED 3
|
||||
#define DISEASE_LIVED 4
|
||||
|
||||
/datum/antagonist/monkey
|
||||
name = "Monkey"
|
||||
job_rank = ROLE_MONKEY
|
||||
roundend_category = "monkeys"
|
||||
var/datum/team/monkey/monkey_team
|
||||
|
||||
/datum/antagonist/monkey/on_gain()
|
||||
. = ..()
|
||||
SSticker.mode.ape_infectees += owner
|
||||
owner.special_role = "Infected Monkey"
|
||||
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever/monkeymode
|
||||
if(!owner.current.HasDisease(D))
|
||||
owner.current.AddDisease(D)
|
||||
else
|
||||
QDEL_NULL(D)
|
||||
|
||||
/datum/antagonist/monkey/greet()
|
||||
to_chat(owner, "<b>You are a monkey now!</b>")
|
||||
to_chat(owner, "<b>Bite humans to infect them, follow the orders of the monkey leaders, and help fellow monkeys!</b>")
|
||||
to_chat(owner, "<b>Ensure at least one infected monkey escapes on the Emergency Shuttle!</b>")
|
||||
to_chat(owner, "<b><i>As an intelligent monkey, you know how to use technology and how to ventcrawl while wearing things.</i></b>")
|
||||
to_chat(owner, "<b>You can use :k to talk to fellow monkeys!</b>")
|
||||
SEND_SOUND(owner.current, sound('sound/ambience/antag/monkey.ogg'))
|
||||
|
||||
/datum/antagonist/monkey/on_removal()
|
||||
. = ..()
|
||||
owner.special_role = null
|
||||
SSticker.mode.ape_infectees -= owner
|
||||
|
||||
var/datum/disease/D = (/datum/disease/transformation/jungle_fever in owner.current.viruses)
|
||||
if(D)
|
||||
D.cure()
|
||||
|
||||
/datum/antagonist/monkey/create_team(datum/team/monkey/new_team)
|
||||
if(!new_team)
|
||||
for(var/datum/antagonist/monkey/N in get_antagonists(/datum/antagonist/monkey, TRUE))
|
||||
if(N.monkey_team)
|
||||
monkey_team = N.monkey_team
|
||||
return
|
||||
monkey_team = new /datum/team/monkey
|
||||
monkey_team.update_objectives()
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
monkey_team = new_team
|
||||
|
||||
/datum/antagonist/monkey/proc/forge_objectives()
|
||||
if(monkey_team)
|
||||
owner.objectives |= monkey_team.objectives
|
||||
|
||||
/datum/antagonist/monkey/leader
|
||||
name = "Monkey Leader"
|
||||
|
||||
/datum/antagonist/monkey/leader/on_gain()
|
||||
. = ..()
|
||||
var/obj/item/organ/heart/freedom/F = new
|
||||
F.Insert(owner.current, drop_if_replaced = FALSE)
|
||||
SSticker.mode.ape_leaders += owner
|
||||
owner.special_role = "Monkey Leader"
|
||||
|
||||
/datum/antagonist/monkey/leader/on_removal()
|
||||
. = ..()
|
||||
SSticker.mode.ape_leaders -= owner
|
||||
var/obj/item/organ/heart/H = new
|
||||
H.Insert(owner.current, drop_if_replaced = FALSE) //replace freedom heart with normal heart
|
||||
|
||||
/datum/antagonist/monkey/leader/greet()
|
||||
to_chat(owner, "<B><span class='notice'>You are the Jungle Fever patient zero!!</B></span>")
|
||||
to_chat(owner, "<b>You have been planted onto this station by the Animal Rights Consortium.</b>")
|
||||
to_chat(owner, "<b>Soon the disease will transform you into an ape. Afterwards, you will be able spread the infection to others with a bite.</b>")
|
||||
to_chat(owner, "<b>While your infection strain is undetectable by scanners, any other infectees will show up on medical equipment.</b>")
|
||||
to_chat(owner, "<b>Your mission will be deemed a success if any of the live infected monkeys reach CentCom.</b>")
|
||||
to_chat(owner, "<b>As an initial infectee, you will be considered a 'leader' by your fellow monkeys.</b>")
|
||||
to_chat(owner, "<b>You can use :k to talk to fellow monkeys!</b>")
|
||||
SEND_SOUND(owner.current, sound('sound/ambience/antag/monkey.ogg'))
|
||||
|
||||
/datum/objective/monkey
|
||||
explanation_text = "Ensure that infected monkeys escape on the emergency shuttle!"
|
||||
martyr_compatible = TRUE
|
||||
var/monkeys_to_win = 1
|
||||
var/escaped_monkeys = 0
|
||||
|
||||
/datum/objective/monkey/check_completion()
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
|
||||
for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list)
|
||||
if (M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase()))
|
||||
escaped_monkeys++
|
||||
if(escaped_monkeys >= monkeys_to_win)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/monkey
|
||||
name = "Monkeys"
|
||||
|
||||
/datum/team/monkey/proc/update_objectives()
|
||||
objectives = list()
|
||||
var/datum/objective/monkey/O = new /datum/objective/monkey()
|
||||
O.team = src
|
||||
objectives += O
|
||||
return
|
||||
|
||||
/datum/team/monkey/proc/infected_monkeys_alive()
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
|
||||
for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list)
|
||||
if(M.HasDisease(D))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/monkey/proc/infected_monkeys_escaped()
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
|
||||
for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list)
|
||||
if(M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase()))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/monkey/proc/infected_humans_escaped()
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
|
||||
for(var/mob/living/carbon/human/M in GLOB.alive_mob_list)
|
||||
if(M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase()))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/monkey/proc/infected_humans_alive()
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
|
||||
for(var/mob/living/carbon/human/M in GLOB.alive_mob_list)
|
||||
if(M.HasDisease(D))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/monkey/proc/get_result()
|
||||
if(infected_monkeys_escaped())
|
||||
return MONKEYS_ESCAPED
|
||||
if(infected_monkeys_alive())
|
||||
return MONKEYS_LIVED
|
||||
if(infected_humans_alive() || infected_humans_escaped())
|
||||
return DISEASE_LIVED
|
||||
return MONKEYS_DIED
|
||||
|
||||
/datum/team/monkey/roundend_report()
|
||||
var/list/parts = list()
|
||||
switch(get_result())
|
||||
if(MONKEYS_ESCAPED)
|
||||
parts += "<span class='greentext big'><B>Monkey Major Victory!</B></span>"
|
||||
parts += "<span class='greentext'><B>Central Command and [station_name()] were taken over by the monkeys! Ook ook!</B></span>"
|
||||
if(MONKEYS_LIVED)
|
||||
parts += "<FONT size = 3><B>Monkey Minor Victory!</B></FONT>"
|
||||
parts += "<span class='greentext'><B>[station_name()] was taken over by the monkeys! Ook ook!</B></span>"
|
||||
if(DISEASE_LIVED)
|
||||
parts += "<span class='redtext big'><B>Monkey Minor Defeat!</B></span>"
|
||||
parts += "<span class='redtext'><B>All the monkeys died, but the disease lives on! The future is uncertain.</B></span>"
|
||||
if(MONKEYS_DIED)
|
||||
parts += "<span class='redtext big'><B>Monkey Major Defeat!</B></span>"
|
||||
parts += "<span class='redtext'><B>All the monkeys died, and Jungle Fever was wiped out!</B></span>"
|
||||
var/list/leaders = get_antagonists(/datum/antagonist/monkey/leader, TRUE)
|
||||
var/list/monkeys = get_antagonists(/datum/antagonist/monkey, TRUE)
|
||||
|
||||
if(LAZYLEN(leaders))
|
||||
parts += "<span class='header'>The monkey leaders were:</span>"
|
||||
parts += printplayerlist(SSticker.mode.ape_leaders)
|
||||
if(LAZYLEN(monkeys))
|
||||
parts += "<span class='header'>The monkeys were:</span>"
|
||||
parts += printplayerlist(SSticker.mode.ape_infectees)
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
@@ -37,19 +37,20 @@
|
||||
else if(M.assigned_role in GLOB.command_positions)
|
||||
possible_targets[M] = 1 //good-guy
|
||||
|
||||
var/list/objectives = list(1,2,3,4)
|
||||
while(owner.objectives.len < quantity)
|
||||
switch(pick_n_take(objectives))
|
||||
var/list/possible_objectives = list(1,2,3,4)
|
||||
|
||||
while(objectives.len < quantity)
|
||||
switch(pick_n_take(possible_objectives))
|
||||
if(1) //research
|
||||
var/datum/objective/download/O = new /datum/objective/download()
|
||||
O.owner = owner
|
||||
O.gen_amount_goal()
|
||||
owner.objectives += O
|
||||
objectives += O
|
||||
|
||||
if(2) //steal
|
||||
var/datum/objective/steal/special/O = new /datum/objective/steal/special()
|
||||
O.owner = owner
|
||||
owner.objectives += O
|
||||
objectives += O
|
||||
|
||||
if(3) //protect/kill
|
||||
if(!possible_targets.len) continue
|
||||
@@ -63,13 +64,13 @@
|
||||
O.owner = owner
|
||||
O.target = M
|
||||
O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]."
|
||||
owner.objectives += O
|
||||
objectives += O
|
||||
else //protect
|
||||
var/datum/objective/protect/O = new /datum/objective/protect()
|
||||
O.owner = owner
|
||||
O.target = M
|
||||
O.explanation_text = "Protect \the [M.current.real_name], the [M.assigned_role], from harm."
|
||||
owner.objectives += O
|
||||
objectives += O
|
||||
if(4) //debrain/capture
|
||||
if(!possible_targets.len) continue
|
||||
var/selected = rand(1,possible_targets.len)
|
||||
@@ -82,17 +83,17 @@
|
||||
O.owner = owner
|
||||
O.target = M
|
||||
O.explanation_text = "Steal the brain of [M.current.real_name]."
|
||||
owner.objectives += O
|
||||
objectives += O
|
||||
else //capture
|
||||
var/datum/objective/capture/O = new /datum/objective/capture()
|
||||
O.owner = owner
|
||||
O.gen_amount_goal()
|
||||
owner.objectives += O
|
||||
objectives += O
|
||||
else
|
||||
break
|
||||
var/datum/objective/O = new /datum/objective/survive()
|
||||
O.owner = owner
|
||||
owner.objectives += O
|
||||
owner.objectives |= objectives
|
||||
|
||||
|
||||
/proc/remove_ninja(mob/living/L)
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
#define NUKE_RESULT_FLUKE 0
|
||||
#define NUKE_RESULT_NUKE_WIN 1
|
||||
#define NUKE_RESULT_CREW_WIN 2
|
||||
#define NUKE_RESULT_CREW_WIN_SYNDIES_DEAD 3
|
||||
#define NUKE_RESULT_DISK_LOST 4
|
||||
#define NUKE_RESULT_DISK_STOLEN 5
|
||||
#define NUKE_RESULT_NOSURVIVORS 6
|
||||
#define NUKE_RESULT_WRONG_STATION 7
|
||||
#define NUKE_RESULT_WRONG_STATION_DEAD 8
|
||||
|
||||
/datum/antagonist/nukeop
|
||||
name = "Nuclear Operative"
|
||||
roundend_category = "syndicate operatives" //just in case
|
||||
job_rank = ROLE_OPERATIVE
|
||||
var/datum/team/nuclear/nuke_team
|
||||
var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
|
||||
var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint.
|
||||
var/nukeop_outfit = /datum/outfit/syndicate
|
||||
|
||||
/datum/antagonist/nukeop/proc/update_synd_icons_added(mob/living/M)
|
||||
var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
|
||||
opshud.join_hud(M)
|
||||
set_antag_hud(M, "synd")
|
||||
|
||||
/datum/antagonist/nukeop/proc/update_synd_icons_removed(mob/living/M)
|
||||
var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
|
||||
opshud.leave_hud(M)
|
||||
set_antag_hud(M, null)
|
||||
|
||||
/datum/antagonist/nukeop/apply_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_synd_icons_added(M)
|
||||
|
||||
/datum/antagonist/nukeop/remove_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_synd_icons_removed(M)
|
||||
|
||||
/datum/antagonist/nukeop/proc/equip_op()
|
||||
if(!ishuman(owner.current))
|
||||
return
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
|
||||
H.set_species(/datum/species/human) //Plasamen burn up otherwise, and lizards are vulnerable to asimov AIs
|
||||
|
||||
H.equipOutfit(nukeop_outfit)
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/nukeop/greet()
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ops.ogg',100,0)
|
||||
to_chat(owner, "<span class='notice'>You are a [nuke_team ? nuke_team.syndicate_name : "syndicate"] agent!</span>")
|
||||
owner.announce_objectives()
|
||||
return
|
||||
|
||||
/datum/antagonist/nukeop/on_gain()
|
||||
give_alias()
|
||||
forge_objectives()
|
||||
. = ..()
|
||||
equip_op()
|
||||
memorize_code()
|
||||
if(send_to_spawnpoint)
|
||||
move_to_spawnpoint()
|
||||
|
||||
/datum/antagonist/nukeop/get_team()
|
||||
return nuke_team
|
||||
|
||||
/datum/antagonist/nukeop/proc/assign_nuke()
|
||||
if(nuke_team && !nuke_team.tracked_nuke)
|
||||
nuke_team.memorized_code = random_nukecode()
|
||||
var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in GLOB.nuke_list
|
||||
if(nuke)
|
||||
nuke_team.tracked_nuke = nuke
|
||||
if(nuke.r_code == "ADMIN")
|
||||
nuke.r_code = nuke_team.memorized_code
|
||||
else //Already set by admins/something else?
|
||||
nuke_team.memorized_code = nuke.r_code
|
||||
else
|
||||
stack_trace("Syndicate nuke not found during nuke team creation.")
|
||||
nuke_team.memorized_code = null
|
||||
|
||||
/datum/antagonist/nukeop/proc/give_alias()
|
||||
if(nuke_team && nuke_team.syndicate_name)
|
||||
var/number = 1
|
||||
number = nuke_team.members.Find(owner)
|
||||
owner.current.real_name = "[nuke_team.syndicate_name] Operative #[number]"
|
||||
|
||||
/datum/antagonist/nukeop/proc/memorize_code()
|
||||
if(nuke_team && nuke_team.tracked_nuke && nuke_team.memorized_code)
|
||||
owner.store_memory("<B>[nuke_team.tracked_nuke] Code</B>: [nuke_team.memorized_code]", 0, 0)
|
||||
to_chat(owner, "The nuclear authorization code is: <B>[nuke_team.memorized_code]</B>")
|
||||
else
|
||||
to_chat(owner, "Unfortunately the syndicate was unable to provide you with nuclear authorization code.")
|
||||
|
||||
/datum/antagonist/nukeop/proc/forge_objectives()
|
||||
if(nuke_team)
|
||||
owner.objectives |= nuke_team.objectives
|
||||
|
||||
/datum/antagonist/nukeop/proc/move_to_spawnpoint()
|
||||
var/team_number = 1
|
||||
if(nuke_team)
|
||||
team_number = nuke_team.members.Find(owner)
|
||||
owner.current.forceMove(GLOB.nukeop_start[((team_number - 1) % GLOB.nukeop_start.len) + 1])
|
||||
|
||||
/datum/antagonist/nukeop/leader/move_to_spawnpoint()
|
||||
owner.current.forceMove(pick(GLOB.nukeop_leader_start))
|
||||
|
||||
/datum/antagonist/nukeop/create_team(datum/team/nuclear/new_team)
|
||||
if(!new_team)
|
||||
if(!always_new_team)
|
||||
for(var/datum/antagonist/nukeop/N in GLOB.antagonists)
|
||||
if(N.nuke_team)
|
||||
nuke_team = N.nuke_team
|
||||
return
|
||||
nuke_team = new /datum/team/nuclear
|
||||
nuke_team.update_objectives()
|
||||
assign_nuke() //This is bit ugly
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
nuke_team = new_team
|
||||
|
||||
/datum/antagonist/nukeop/leader
|
||||
name = "Nuclear Operative Leader"
|
||||
nukeop_outfit = /datum/outfit/syndicate/leader
|
||||
always_new_team = TRUE
|
||||
var/title
|
||||
|
||||
/datum/antagonist/nukeop/leader/memorize_code()
|
||||
..()
|
||||
if(nuke_team && nuke_team.memorized_code)
|
||||
var/obj/item/paper/P = new
|
||||
P.info = "The nuclear authorization code is: <b>[nuke_team.memorized_code]</b>"
|
||||
P.name = "nuclear bomb code"
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(!istype(H))
|
||||
P.forceMove(get_turf(H))
|
||||
else
|
||||
H.put_in_hands(P, TRUE)
|
||||
H.update_icons()
|
||||
|
||||
/datum/antagonist/nukeop/leader/give_alias()
|
||||
title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")
|
||||
if(nuke_team && nuke_team.syndicate_name)
|
||||
owner.current.real_name = "[nuke_team.syndicate_name] [title]"
|
||||
else
|
||||
owner.current.real_name = "Syndicate [title]"
|
||||
|
||||
/datum/antagonist/nukeop/leader/greet()
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ops.ogg',100,0)
|
||||
to_chat(owner, "<B>You are the Syndicate [title] for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors.</B>")
|
||||
to_chat(owner, "<B>If you feel you are not up to this task, give your ID to another operative.</B>")
|
||||
to_chat(owner, "<B>In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it.</B>")
|
||||
owner.announce_objectives()
|
||||
addtimer(CALLBACK(src, .proc/nuketeam_name_assign), 1)
|
||||
|
||||
|
||||
/datum/antagonist/nukeop/leader/proc/nuketeam_name_assign()
|
||||
if(!nuke_team)
|
||||
return
|
||||
nuke_team.rename_team(ask_name())
|
||||
|
||||
/datum/team/nuclear/proc/rename_team(new_name)
|
||||
syndicate_name = new_name
|
||||
name = "[syndicate_name] Team"
|
||||
for(var/I in members)
|
||||
var/datum/mind/synd_mind = I
|
||||
var/mob/living/carbon/human/H = synd_mind.current
|
||||
if(!istype(H))
|
||||
continue
|
||||
var/chosen_name = H.dna.species.random_name(H.gender,0,syndicate_name)
|
||||
H.fully_replace_character_name(H.real_name,chosen_name)
|
||||
|
||||
/datum/antagonist/nukeop/leader/proc/ask_name()
|
||||
var/randomname = pick(GLOB.last_names)
|
||||
var/newname = stripped_input(owner.current,"You are the nuke operative [title]. Please choose a last name for your family.", "Name change",randomname)
|
||||
if (!newname)
|
||||
newname = randomname
|
||||
else
|
||||
newname = reject_bad_name(newname)
|
||||
if(!newname)
|
||||
newname = randomname
|
||||
|
||||
return capitalize(newname)
|
||||
|
||||
/datum/antagonist/nukeop/lone
|
||||
name = "Lone Operative"
|
||||
always_new_team = TRUE
|
||||
send_to_spawnpoint = FALSE //Handled by event
|
||||
nukeop_outfit = /datum/outfit/syndicate/full
|
||||
|
||||
/datum/antagonist/nukeop/lone/assign_nuke()
|
||||
if(nuke_team && !nuke_team.tracked_nuke)
|
||||
nuke_team.memorized_code = random_nukecode()
|
||||
var/obj/machinery/nuclearbomb/selfdestruct/nuke = locate() in GLOB.nuke_list
|
||||
if(nuke)
|
||||
nuke_team.tracked_nuke = nuke
|
||||
if(nuke.r_code == "ADMIN")
|
||||
nuke.r_code = nuke_team.memorized_code
|
||||
else //Already set by admins/something else?
|
||||
nuke_team.memorized_code = nuke.r_code
|
||||
else
|
||||
stack_trace("Station self destruct ot found during lone op team creation.")
|
||||
nuke_team.memorized_code = null
|
||||
|
||||
/datum/team/nuclear
|
||||
var/syndicate_name
|
||||
var/obj/machinery/nuclearbomb/tracked_nuke
|
||||
var/core_objective = /datum/objective/nuclear
|
||||
var/memorized_code
|
||||
|
||||
/datum/team/nuclear/New()
|
||||
..()
|
||||
syndicate_name = syndicate_name()
|
||||
|
||||
/datum/team/nuclear/proc/update_objectives()
|
||||
if(core_objective)
|
||||
var/datum/objective/O = new core_objective
|
||||
O.team = src
|
||||
objectives += O
|
||||
|
||||
/datum/team/nuclear/proc/disk_rescued()
|
||||
for(var/obj/item/disk/nuclear/D in GLOB.poi_list)
|
||||
if(!D.onCentCom())
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/team/nuclear/proc/operatives_dead()
|
||||
for(var/I in members)
|
||||
var/datum/mind/operative_mind = I
|
||||
if(ishuman(operative_mind.current) && (operative_mind.current.stat != DEAD))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/team/nuclear/proc/syndies_escaped()
|
||||
var/obj/docking_port/mobile/S = SSshuttle.getShuttle("syndicate")
|
||||
return (S && (S.z == ZLEVEL_CENTCOM || S.z == ZLEVEL_TRANSIT))
|
||||
|
||||
/datum/team/nuclear/proc/get_result()
|
||||
var/evacuation = SSshuttle.emergency.mode == SHUTTLE_ENDGAME
|
||||
var/disk_rescued = disk_rescued()
|
||||
var/syndies_didnt_escape = !syndies_escaped()
|
||||
var/station_was_nuked = SSticker.mode.station_was_nuked
|
||||
var/nuke_off_station = SSticker.mode.nuke_off_station
|
||||
|
||||
if(nuke_off_station == NUKE_SYNDICATE_BASE)
|
||||
return NUKE_RESULT_FLUKE
|
||||
else if(!disk_rescued && station_was_nuked && !syndies_didnt_escape)
|
||||
return NUKE_RESULT_NUKE_WIN
|
||||
else if (!disk_rescued && station_was_nuked && syndies_didnt_escape)
|
||||
return NUKE_RESULT_NOSURVIVORS
|
||||
else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape)
|
||||
return NUKE_RESULT_WRONG_STATION
|
||||
else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape)
|
||||
return NUKE_RESULT_WRONG_STATION_DEAD
|
||||
else if ((disk_rescued || evacuation) && operatives_dead())
|
||||
return NUKE_RESULT_CREW_WIN_SYNDIES_DEAD
|
||||
else if (disk_rescued)
|
||||
return NUKE_RESULT_CREW_WIN
|
||||
else if (!disk_rescued && operatives_dead())
|
||||
return NUKE_RESULT_DISK_LOST
|
||||
else if (!disk_rescued && evacuation)
|
||||
return NUKE_RESULT_DISK_STOLEN
|
||||
else
|
||||
return //Undefined result
|
||||
|
||||
/datum/team/nuclear/roundend_report()
|
||||
var/list/parts = list()
|
||||
parts += "<span class='header'>[syndicate_name] Operatives:</span>"
|
||||
|
||||
switch(get_result())
|
||||
if(NUKE_RESULT_FLUKE)
|
||||
parts += "<span class='redtext big'>Humiliating Syndicate Defeat</span>"
|
||||
parts += "<B>The crew of [station_name()] gave [syndicate_name] operatives back their bomb! The syndicate base was destroyed!</B> Next time, don't lose the nuke!"
|
||||
if(NUKE_RESULT_NUKE_WIN)
|
||||
parts += "<span class='greentext big'>Syndicate Major Victory!</span>"
|
||||
parts += "<B>[syndicate_name] operatives have destroyed [station_name()]!</B>"
|
||||
if(NUKE_RESULT_NOSURVIVORS)
|
||||
parts += "<span class='neutraltext big'>Total Annihilation</span>"
|
||||
parts += "<B>[syndicate_name] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion.</B> Next time, don't lose the disk!"
|
||||
if(NUKE_RESULT_WRONG_STATION)
|
||||
parts += "<span class='redtext big'>Crew Minor Victory</span>"
|
||||
parts += "<B>[syndicate_name] operatives secured the authentication disk but blew up something that wasn't [station_name()].</B> Next time, don't do that!"
|
||||
if(NUKE_RESULT_WRONG_STATION_DEAD)
|
||||
parts += "<span class='redtext big'>[syndicate_name] operatives have earned Darwin Award!</span>"
|
||||
parts += "<B>[syndicate_name] operatives blew up something that wasn't [station_name()] and got caught in the explosion.</B> Next time, don't do that!"
|
||||
if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD)
|
||||
parts += "<span class='redtext big'>Crew Major Victory!</span>"
|
||||
parts += "<B>The Research Staff has saved the disk and killed the [syndicate_name] Operatives</B>"
|
||||
if(NUKE_RESULT_CREW_WIN)
|
||||
parts += "<span class='redtext big'>Crew Major Victory</span>"
|
||||
parts += "<B>The Research Staff has saved the disk and stopped the [syndicate_name] Operatives!</B>"
|
||||
if(NUKE_RESULT_DISK_LOST)
|
||||
parts += "<span class='neutraltext big'>Neutral Victory!</span>"
|
||||
parts += "<B>The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name] Operatives!</B>"
|
||||
if(NUKE_RESULT_DISK_STOLEN)
|
||||
parts += "<span class='greentext big'>Syndicate Minor Victory!</span>"
|
||||
parts += "<B>[syndicate_name] operatives survived the assault but did not achieve the destruction of [station_name()].</B> Next time, don't lose the disk!"
|
||||
else
|
||||
parts += "<span class='neutraltext big'>Neutral Victory</span>"
|
||||
parts += "<B>Mission aborted!</B>"
|
||||
|
||||
var/text = "<br><span class='header'>The syndicate operatives were:</span>"
|
||||
var/purchases = ""
|
||||
var/TC_uses = 0
|
||||
for(var/I in members)
|
||||
var/datum/mind/syndicate = I
|
||||
for(var/U in GLOB.uplinks)
|
||||
var/datum/component/uplink/H = U
|
||||
if(H.owner == syndicate.key)
|
||||
TC_uses += H.purchase_log.total_spent
|
||||
if(H.purchase_log)
|
||||
purchases += H.purchase_log.generate_render(show_key = FALSE)
|
||||
else
|
||||
stack_trace("WARNING: Nuke Op uplink with no purchase_log Owner: [H.owner]")
|
||||
text += printplayerlist(members)
|
||||
text += "<br>"
|
||||
text += "(Syndicates used [TC_uses] TC) [purchases]"
|
||||
if(TC_uses == 0 && SSticker.mode.station_was_nuked && !operatives_dead())
|
||||
text += "<BIG>[icon2html('icons/badass.dmi', world, "badass")]</BIG>"
|
||||
|
||||
parts += text
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
@@ -1,7 +1,8 @@
|
||||
/datum/antagonist/pirate
|
||||
name = "Space Pirate"
|
||||
job_rank = ROLE_TRAITOR
|
||||
var/datum/objective_team/pirate/crew
|
||||
roundend_category = "space pirates"
|
||||
var/datum/team/pirate/crew
|
||||
|
||||
/datum/antagonist/pirate/greet()
|
||||
to_chat(owner, "<span class='boldannounce'>You are a Space Pirate!</span>")
|
||||
@@ -11,15 +12,16 @@
|
||||
/datum/antagonist/pirate/get_team()
|
||||
return crew
|
||||
|
||||
/datum/antagonist/pirate/create_team(datum/objective_team/pirate/new_team)
|
||||
/datum/antagonist/pirate/create_team(datum/team/pirate/new_team)
|
||||
if(!new_team)
|
||||
for(var/datum/antagonist/pirate/P in GLOB.antagonists)
|
||||
if(P.crew)
|
||||
new_team = P.crew
|
||||
crew = P.crew
|
||||
return
|
||||
if(!new_team)
|
||||
crew = new /datum/objective_team/pirate
|
||||
crew = new /datum/team/pirate
|
||||
crew.forge_objectives()
|
||||
return
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
crew = new_team
|
||||
@@ -34,11 +36,10 @@
|
||||
owner.objectives -= crew.objectives
|
||||
. = ..()
|
||||
|
||||
/datum/objective_team/pirate
|
||||
/datum/team/pirate
|
||||
name = "Pirate crew"
|
||||
var/list/objectives = list()
|
||||
|
||||
/datum/objective_team/pirate/proc/forge_objectives()
|
||||
/datum/team/pirate/proc/forge_objectives()
|
||||
var/datum/objective/loot/getbooty = new()
|
||||
getbooty.team = src
|
||||
getbooty.storage_area = locate(/area/shuttle/pirate/vault) in GLOB.sortedAreas
|
||||
@@ -84,11 +85,11 @@ GLOBAL_LIST_INIT(pirate_loot_cache, typecacheof(list(
|
||||
loot_table[lootname] = count
|
||||
else
|
||||
loot_table[lootname] += count
|
||||
var/text = ""
|
||||
var/list/loot_texts = list()
|
||||
for(var/key in loot_table)
|
||||
var/amount = loot_table[key]
|
||||
text += "[amount] [key][amount > 1 ? "s":""], "
|
||||
return text
|
||||
loot_texts += "[amount] [key][amount > 1 ? "s":""]"
|
||||
return loot_texts.Join(", ")
|
||||
|
||||
/datum/objective/loot/proc/get_loot_value()
|
||||
if(!storage_area)
|
||||
@@ -104,32 +105,25 @@ GLOBAL_LIST_INIT(pirate_loot_cache, typecacheof(list(
|
||||
/datum/objective/loot/check_completion()
|
||||
return ..() || get_loot_value() >= target_value
|
||||
|
||||
/datum/team/pirate/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
//These need removal ASAP as everything is converted to datum antags.
|
||||
/datum/game_mode/proc/auto_declare_completion_pirates()
|
||||
var/list/datum/mind/pirates = get_antagonists(/datum/antagonist/pirate)
|
||||
var/datum/objective_team/pirate/crew
|
||||
var/text = ""
|
||||
if(pirates.len)
|
||||
text += "<br><b>Space Pirates were:</b>"
|
||||
for(var/datum/mind/M in pirates)
|
||||
text += printplayer(M)
|
||||
if(!crew)
|
||||
var/datum/antagonist/pirate/P = M.has_antag_datum(/datum/antagonist/pirate)
|
||||
crew = P.crew
|
||||
if(crew)
|
||||
text += "<br>Loot stolen: "
|
||||
var/datum/objective/loot/L = locate() in crew.objectives
|
||||
text += L.loot_listing()
|
||||
text += "<br>Total loot value : [L.get_loot_value()]/[L.target_value] credits"
|
||||
parts += "<span class='header'>Space Pirates were:</span>"
|
||||
|
||||
var/all_dead = TRUE
|
||||
for(var/datum/mind/M in crew.members)
|
||||
if(considered_alive(M))
|
||||
all_dead = FALSE
|
||||
break
|
||||
if(L.check_completion() && !all_dead)
|
||||
text += "<br><font color='green'><b>The pirate crew was successful!</b></font>"
|
||||
else
|
||||
text += "<br><span class='boldannounce'>The pirate crew has failed.</span>"
|
||||
to_chat(world, text)
|
||||
var/all_dead = TRUE
|
||||
for(var/datum/mind/M in members)
|
||||
if(considered_alive(M))
|
||||
all_dead = FALSE
|
||||
parts += printplayerlist(members)
|
||||
|
||||
parts += "Loot stolen: "
|
||||
var/datum/objective/loot/L = locate() in objectives
|
||||
parts += L.loot_listing()
|
||||
parts += "Total loot value : [L.get_loot_value()]/[L.target_value] credits"
|
||||
|
||||
if(L.check_completion() && !all_dead)
|
||||
parts += "<span class='greentext big'>The pirate crew was successful!</span>"
|
||||
else
|
||||
parts += "<span class='redtext big'>The pirate crew has failed.</span>"
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
/datum/antagonist/rev
|
||||
name = "Revolutionary"
|
||||
roundend_category = "revolutionaries" // if by some miracle revolutionaries without revolution happen
|
||||
job_rank = ROLE_REV
|
||||
var/hud_type = "rev"
|
||||
var/datum/objective_team/revolution/rev_team
|
||||
var/datum/team/revolution/rev_team
|
||||
|
||||
/datum/antagonist/rev/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
@@ -39,17 +40,17 @@
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/rev/greet()
|
||||
to_chat(owner, "<span class='danger'><FONT size = 3> You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!</FONT></span>")
|
||||
to_chat(owner, "<span class='userdanger'>You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!</span>")
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/rev/create_team(datum/objective_team/revolution/new_team)
|
||||
/datum/antagonist/rev/create_team(datum/team/revolution/new_team)
|
||||
if(!new_team)
|
||||
//For now only one revolution at a time
|
||||
for(var/datum/antagonist/rev/head/H in GLOB.antagonists)
|
||||
if(H.rev_team)
|
||||
rev_team = H.rev_team
|
||||
return
|
||||
rev_team = new /datum/objective_team/revolution
|
||||
rev_team = new /datum/team/revolution
|
||||
rev_team.update_objectives()
|
||||
rev_team.update_heads()
|
||||
return
|
||||
@@ -77,7 +78,7 @@
|
||||
old_owner.add_antag_datum(new_revhead,old_team)
|
||||
new_revhead.silent = FALSE
|
||||
to_chat(old_owner, "<span class='userdanger'>You have proved your devotion to revolution! You are a head revolutionary now!</span>")
|
||||
|
||||
|
||||
|
||||
/datum/antagonist/rev/head
|
||||
name = "Head Revolutionary"
|
||||
@@ -131,14 +132,14 @@
|
||||
old_owner.add_antag_datum(new_rev,old_team)
|
||||
new_rev.silent = FALSE
|
||||
to_chat(old_owner, "<span class='userdanger'>Revolution has been disappointed of your leader traits! You are a regular revolutionary now!</span>")
|
||||
|
||||
|
||||
/datum/antagonist/rev/farewell()
|
||||
if(ishuman(owner.current))
|
||||
owner.current.visible_message("[owner.current] looks like they just remembered their real allegiance!")
|
||||
to_chat(owner, "<span class='userdanger'><FONT size = 3>You are no longer a brainwashed revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...</FONT></span>")
|
||||
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like they just remembered their real allegiance!</span>", ignored_mob = owner.current)
|
||||
to_chat(owner, "<span class='userdanger'>You are no longer a brainwashed revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...</span>")
|
||||
else if(issilicon(owner.current))
|
||||
owner.current.visible_message("The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.")
|
||||
to_chat(owner, "<span class='userdanger'><FONT size = 3>The frame's firmware detects and deletes your neural reprogramming! You remember nothing but the name of the one who flashed you.</FONT></span>")
|
||||
owner.current.visible_message("<span class='deconversion_message'>The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.</span>", ignored_mob = owner.current)
|
||||
to_chat(owner, "<span class='userdanger'>The frame's firmware detects and deletes your neural reprogramming! You remember nothing but the name of the one who flashed you.</span>")
|
||||
|
||||
/datum/antagonist/rev/proc/remove_revolutionary(borged, deconverter)
|
||||
log_attack("[owner.current] (Key: [key_name(owner.current)]) has been deconverted from the revolution by [deconverter] (Key: [key_name(deconverter)])!")
|
||||
@@ -163,7 +164,7 @@
|
||||
if(remove_clumsy && owner.assigned_role == "Clown")
|
||||
to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
|
||||
H.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
|
||||
if(give_flash)
|
||||
var/obj/item/device/assembly/flash/T = new(H)
|
||||
var/list/slots = list (
|
||||
@@ -176,18 +177,17 @@
|
||||
to_chat(H, "The Syndicate were unfortunately unable to get you a flash.")
|
||||
else
|
||||
to_chat(H, "The flash in your [where] will help you to persuade the crew to join your cause.")
|
||||
|
||||
|
||||
if(give_hud)
|
||||
var/obj/item/organ/cyberimp/eyes/hud/security/syndicate/S = new(H)
|
||||
S.Insert(H, special = FALSE, drop_if_replaced = FALSE)
|
||||
to_chat(H, "Your eyes have been implanted with a cybernetic security HUD which will help you keep track of who is mindshield-implanted, and therefore unable to be recruited.")
|
||||
|
||||
/datum/objective_team/revolution
|
||||
/datum/team/revolution
|
||||
name = "Revolution"
|
||||
var/list/objectives = list()
|
||||
var/max_headrevs = 3
|
||||
|
||||
/datum/objective_team/revolution/proc/update_objectives(initial = FALSE)
|
||||
/datum/team/revolution/proc/update_objectives(initial = FALSE)
|
||||
var/untracked_heads = SSjob.get_all_heads()
|
||||
for(var/datum/objective/mutiny/O in objectives)
|
||||
untracked_heads -= O.target
|
||||
@@ -199,16 +199,16 @@
|
||||
objectives += new_target
|
||||
for(var/datum/mind/M in members)
|
||||
M.objectives |= objectives
|
||||
|
||||
|
||||
addtimer(CALLBACK(src,.proc/update_objectives),HEAD_UPDATE_PERIOD,TIMER_UNIQUE)
|
||||
|
||||
/datum/objective_team/revolution/proc/head_revolutionaries()
|
||||
/datum/team/revolution/proc/head_revolutionaries()
|
||||
. = list()
|
||||
for(var/datum/mind/M in members)
|
||||
if(M.has_antag_datum(/datum/antagonist/rev/head))
|
||||
. += M
|
||||
|
||||
/datum/objective_team/revolution/proc/update_heads()
|
||||
/datum/team/revolution/proc/update_heads()
|
||||
if(SSticker.HasRoundStarted())
|
||||
var/list/datum/mind/head_revolutionaries = head_revolutionaries()
|
||||
var/list/datum/mind/heads = SSjob.get_all_heads()
|
||||
@@ -227,3 +227,56 @@
|
||||
rev.promote()
|
||||
|
||||
addtimer(CALLBACK(src,.proc/update_heads),HEAD_UPDATE_PERIOD,TIMER_UNIQUE)
|
||||
|
||||
|
||||
/datum/team/revolution/roundend_report()
|
||||
if(!members.len)
|
||||
return
|
||||
|
||||
var/list/result = list()
|
||||
|
||||
result += "<div class='panel redborder'>"
|
||||
|
||||
var/num_revs = 0
|
||||
var/num_survivors = 0
|
||||
for(var/mob/living/carbon/survivor in GLOB.alive_mob_list)
|
||||
if(survivor.ckey)
|
||||
num_survivors++
|
||||
if(survivor.mind)
|
||||
if(is_revolutionary(survivor))
|
||||
num_revs++
|
||||
if(num_survivors)
|
||||
result += "Command's Approval Rating: <B>[100 - round((num_revs/num_survivors)*100, 0.1)]%</B><br>"
|
||||
|
||||
|
||||
var/list/targets = list()
|
||||
var/list/datum/mind/headrevs = get_antagonists(/datum/antagonist/rev/head)
|
||||
var/list/datum/mind/revs = get_antagonists(/datum/antagonist/rev,TRUE)
|
||||
if(headrevs.len)
|
||||
var/list/headrev_part = list()
|
||||
headrev_part += "<span class='header'>The head revolutionaries were:</span>"
|
||||
headrev_part += printplayerlist(headrevs,TRUE)
|
||||
result += headrev_part.Join("<br>")
|
||||
|
||||
if(revs.len)
|
||||
var/list/rev_part = list()
|
||||
rev_part += "<span class='header'>The revolutionaries were:</span>"
|
||||
rev_part += printplayerlist(revs,TRUE)
|
||||
result += rev_part.Join("<br>")
|
||||
|
||||
var/list/heads = SSjob.get_all_heads()
|
||||
if(heads.len)
|
||||
var/head_text = "<span class='header'>The heads of staff were:</span>"
|
||||
head_text += "<ul class='playerlist'>"
|
||||
for(var/datum/mind/head in heads)
|
||||
var/target = (head in targets)
|
||||
head_text += "<li>"
|
||||
if(target)
|
||||
head_text += "<span class='redtext'>Target</span>"
|
||||
head_text += "[printplayer(head, 1)]</li>"
|
||||
head_text += "</ul><br>"
|
||||
result += head_text
|
||||
|
||||
result += "</div>"
|
||||
|
||||
return result.Join()
|
||||
@@ -5,13 +5,13 @@
|
||||
|
||||
/datum/antagonist/wizard
|
||||
name = "Space Wizard"
|
||||
roundend_category = "wizards/witches"
|
||||
job_rank = ROLE_WIZARD
|
||||
var/give_objectives = TRUE
|
||||
var/strip = TRUE //strip before equipping
|
||||
var/allow_rename = TRUE
|
||||
var/hud_version = "wizard"
|
||||
var/datum/objective_team/wizard/wiz_team //Only created if wizard summons apprentices
|
||||
var/list/objectives = list() //this should be base datum antag proc and list, todo make lazy
|
||||
var/datum/team/wizard/wiz_team //Only created if wizard summons apprentices
|
||||
var/move_to_lair = TRUE
|
||||
var/outfit_type = /datum/outfit/wizard
|
||||
var/wiz_age = WIZARD_AGE_MIN /* Wizards by nature cannot be too young. */
|
||||
@@ -33,7 +33,7 @@
|
||||
/datum/antagonist/wizard/proc/unregister()
|
||||
SSticker.mode.wizards -= src
|
||||
|
||||
/datum/antagonist/wizard/create_team(datum/objective_team/wizard/new_team)
|
||||
/datum/antagonist/wizard/create_team(datum/team/wizard/new_team)
|
||||
if(!new_team)
|
||||
return
|
||||
if(!istype(new_team))
|
||||
@@ -43,12 +43,14 @@
|
||||
/datum/antagonist/wizard/get_team()
|
||||
return wiz_team
|
||||
|
||||
/datum/objective_team/wizard
|
||||
/datum/team/wizard
|
||||
name = "wizard team"
|
||||
var/datum/antagonist/wizard/master_wizard
|
||||
|
||||
/datum/antagonist/wizard/proc/create_wiz_team()
|
||||
wiz_team = new(owner)
|
||||
wiz_team.name = "[owner.current.real_name] team"
|
||||
wiz_team.master_wizard = src
|
||||
update_wiz_icons_added(owner.current)
|
||||
|
||||
/datum/antagonist/wizard/proc/send_to_lair()
|
||||
@@ -283,4 +285,46 @@
|
||||
var/datum/objective/new_objective = new("Protect Wizard Academy from the intruders")
|
||||
new_objective.owner = owner
|
||||
owner.objectives += new_objective
|
||||
objectives += new_objective
|
||||
objectives += new_objective
|
||||
|
||||
//Solo wizard report
|
||||
/datum/antagonist/wizard/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
parts += printplayer(owner)
|
||||
|
||||
var/count = 1
|
||||
var/wizardwin = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
|
||||
else
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
wizardwin = 0
|
||||
count++
|
||||
|
||||
if(wizardwin)
|
||||
parts += "<span class='greentext'>The wizard was successful!</span>"
|
||||
else
|
||||
parts += "<span class='redtext'>The wizard has failed!</span>"
|
||||
|
||||
if(owner.spell_list.len>0)
|
||||
parts += "<B>[owner.name] used the following spells: </B>"
|
||||
var/list/spell_names = list()
|
||||
for(var/obj/effect/proc_holder/spell/S in owner.spell_list)
|
||||
spell_names += S.name
|
||||
parts += spell_names.Join(", ")
|
||||
|
||||
return parts.Join("<br>")
|
||||
|
||||
//Wizard with apprentices report
|
||||
/datum/team/wizard/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
parts += "<span class='header'>Wizards/witches of [master_wizard.owner.name] team were:</span>"
|
||||
parts += master_wizard.roundend_report()
|
||||
parts += " "
|
||||
parts += "<span class='header'>[master_wizard.owner.name] apprentices were:</span>"
|
||||
parts += printplayerlist(members - master_wizard.owner)
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
+2
-2
@@ -128,11 +128,11 @@
|
||||
//Position the effect so the beam is one continous line
|
||||
var/a
|
||||
if(abs(Pixel_x)>32)
|
||||
a = Pixel_x > 0 ? round(Pixel_x/32) : Ceiling(Pixel_x/32)
|
||||
a = Pixel_x > 0 ? round(Pixel_x/32) : CEILING(Pixel_x/32, 1)
|
||||
X.x += a
|
||||
Pixel_x %= 32
|
||||
if(abs(Pixel_y)>32)
|
||||
a = Pixel_y > 0 ? round(Pixel_y/32) : Ceiling(Pixel_y/32)
|
||||
a = Pixel_y > 0 ? round(Pixel_y/32) : CEILING(Pixel_y/32, 1)
|
||||
X.y += a
|
||||
Pixel_y %= 32
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/datum/brain_trauma/special/imaginary_friend
|
||||
name = "Imaginary Friend"
|
||||
desc = "Patient can see and hear an imaginary person."
|
||||
scan_desc = "partial schizophrenia"
|
||||
gain_text = "<span class='notice'>You feel in good company, for some reason.</span>"
|
||||
lose_text = "<span class='warning'>You feel lonely again.</span>"
|
||||
var/mob/camera/imaginary_friend/friend
|
||||
var/friend_initialized = FALSE
|
||||
|
||||
/datum/brain_trauma/special/imaginary_friend/on_gain()
|
||||
..()
|
||||
make_friend()
|
||||
get_ghost()
|
||||
|
||||
/datum/brain_trauma/special/imaginary_friend/on_life()
|
||||
if(get_dist(owner, friend) > 9)
|
||||
friend.yank()
|
||||
if(!friend)
|
||||
qdel(src)
|
||||
return
|
||||
if(!friend.client && friend_initialized)
|
||||
addtimer(CALLBACK(src, .proc/reroll_friend), 600)
|
||||
|
||||
/datum/brain_trauma/special/imaginary_friend/on_lose()
|
||||
..()
|
||||
QDEL_NULL(friend)
|
||||
|
||||
//If the friend goes afk, make a brand new friend. Plenty of fish in the sea of imagination.
|
||||
/datum/brain_trauma/special/imaginary_friend/proc/reroll_friend()
|
||||
if(friend.client) //reconnected
|
||||
return
|
||||
friend_initialized = FALSE
|
||||
QDEL_NULL(friend)
|
||||
make_friend()
|
||||
get_ghost()
|
||||
|
||||
/datum/brain_trauma/special/imaginary_friend/proc/make_friend()
|
||||
friend = new(get_turf(src), src)
|
||||
|
||||
/datum/brain_trauma/special/imaginary_friend/proc/get_ghost()
|
||||
set waitfor = FALSE
|
||||
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", ROLE_PAI, null, null, 75, friend)
|
||||
if(LAZYLEN(candidates))
|
||||
var/client/C = pick(candidates)
|
||||
friend.key = C.key
|
||||
friend_initialized = TRUE
|
||||
else
|
||||
qdel(src)
|
||||
|
||||
/mob/camera/imaginary_friend
|
||||
name = "imaginary friend"
|
||||
real_name = "imaginary friend"
|
||||
move_on_shuttle = TRUE
|
||||
desc = "A wonderful yet fake friend."
|
||||
see_in_dark = 0
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
|
||||
sight = NONE
|
||||
see_invisible = SEE_INVISIBLE_LIVING
|
||||
var/icon/human_image
|
||||
var/image/current_image
|
||||
var/mob/living/carbon/owner
|
||||
var/datum/brain_trauma/special/imaginary_friend/trauma
|
||||
|
||||
/mob/camera/imaginary_friend/Login()
|
||||
..()
|
||||
to_chat(src, "<span class='notice'><b>You are the imaginary friend of [owner]!</b></span>")
|
||||
to_chat(src, "<span class='notice'>You are absolutely loyal to your friend, no matter what.</span>")
|
||||
to_chat(src, "<span class='notice'>You cannot directly influence the world around you, but you can see what [owner] cannot.</span>")
|
||||
|
||||
/mob/camera/imaginary_friend/Initialize(mapload, _trauma)
|
||||
. = ..()
|
||||
var/gender = pick(MALE, FEMALE)
|
||||
real_name = random_unique_name(gender)
|
||||
name = real_name
|
||||
trauma = _trauma
|
||||
owner = trauma.owner
|
||||
human_image = get_flat_human_icon(null, pick(SSjob.occupations))
|
||||
Show()
|
||||
|
||||
/mob/camera/imaginary_friend/proc/Show()
|
||||
if(!client) //nobody home
|
||||
return
|
||||
|
||||
//Remove old image from owner and friend
|
||||
if(owner.client)
|
||||
owner.client.images.Remove(current_image)
|
||||
|
||||
client.images.Remove(current_image)
|
||||
|
||||
//Generate image from the static icon and the current dir
|
||||
current_image = image(human_image, src, , MOB_LAYER, dir=src.dir)
|
||||
current_image.override = TRUE
|
||||
current_image.name = name
|
||||
|
||||
//Add new image to owner and friend
|
||||
if(owner.client)
|
||||
owner.client.images |= current_image
|
||||
|
||||
client.images |= current_image
|
||||
|
||||
/mob/camera/imaginary_friend/Destroy()
|
||||
if(owner.client)
|
||||
owner.client.images.Remove(human_image)
|
||||
if(client)
|
||||
client.images.Remove(human_image)
|
||||
return ..()
|
||||
|
||||
/mob/camera/imaginary_friend/proc/yank()
|
||||
if(!client) //don't bother if the friend is braindead
|
||||
return
|
||||
forceMove(get_turf(owner))
|
||||
Show()
|
||||
|
||||
/mob/camera/imaginary_friend/say(message)
|
||||
if (!message)
|
||||
return
|
||||
|
||||
if (src.client)
|
||||
if(client.prefs.muted & MUTE_IC)
|
||||
to_chat(src, "You cannot send IC messages (muted).")
|
||||
return
|
||||
if (src.client.handle_spam_prevention(message,MUTE_IC))
|
||||
return
|
||||
|
||||
friend_talk(message)
|
||||
|
||||
/mob/camera/imaginary_friend/proc/friend_talk(message)
|
||||
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
|
||||
|
||||
if(!message)
|
||||
return
|
||||
|
||||
log_talk(src,"[key_name(src)] : [message]",LOGSAY)
|
||||
|
||||
var/rendered = "<span class='game say'><span class='name'>[name]</span> <span class='message'>[say_quote(message)]</span></span>"
|
||||
var/dead_rendered = "<span class='game say'><span class='name'>[name] (Imaginary friend of [owner])</span> <span class='message'>[say_quote(message)]</span></span>"
|
||||
|
||||
to_chat(owner, "[rendered]")
|
||||
to_chat(src, "[rendered]")
|
||||
|
||||
for(var/mob/M in GLOB.dead_mob_list)
|
||||
var/link = FOLLOW_LINK(M, owner)
|
||||
to_chat(M, "[link] [dead_rendered]")
|
||||
|
||||
/mob/camera/imaginary_friend/emote(act,m_type=1,message = null)
|
||||
return
|
||||
|
||||
/mob/camera/imaginary_friend/forceMove(atom/destination)
|
||||
dir = get_dir(get_turf(src), destination)
|
||||
loc = destination
|
||||
if(get_dist(src, owner) > 9)
|
||||
yank()
|
||||
return
|
||||
Show()
|
||||
|
||||
/mob/camera/imaginary_friend/movement_delay()
|
||||
return 2
|
||||
@@ -42,7 +42,7 @@
|
||||
lose_text = "<span class='notice'>You feel smart again.</span>"
|
||||
|
||||
/datum/brain_trauma/mild/dumbness/on_gain()
|
||||
owner.disabilities |= DUMB
|
||||
owner.add_disability(DISABILITY_DUMB, TRAUMA_DISABILITY)
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/mild/dumbness/on_life()
|
||||
@@ -54,7 +54,7 @@
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/mild/dumbness/on_lose()
|
||||
owner.disabilities &= ~DUMB
|
||||
owner.remove_disability(DISABILITY_DUMB, TRAUMA_DISABILITY)
|
||||
owner.derpspeech = 0
|
||||
..()
|
||||
|
||||
@@ -107,6 +107,26 @@
|
||||
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/mild/healthy
|
||||
name = "Anosognosia"
|
||||
desc = "Patient always feels healthy, regardless of their condition."
|
||||
scan_desc = "self-awareness deficit"
|
||||
gain_text = "<span class='notice'>You feel great!</span>"
|
||||
lose_text = "<span class='warning'>You no longer feel perfectly healthy.</span>"
|
||||
|
||||
/datum/brain_trauma/mild/healthy/on_gain()
|
||||
owner.set_screwyhud(SCREWYHUD_HEALTHY)
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/mild/healthy/on_life()
|
||||
owner.set_screwyhud(SCREWYHUD_HEALTHY) //just in case of hallucinations
|
||||
owner.adjustStaminaLoss(-5) //no pain, no fatigue
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/mild/healthy/on_lose()
|
||||
owner.set_screwyhud(SCREWYHUD_NONE)
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/mild/muscle_weakness
|
||||
name = "Muscle Weakness"
|
||||
desc = "Patient experiences occasional bouts of muscle weakness."
|
||||
@@ -133,3 +153,62 @@
|
||||
to_chat(owner, "<span class='warning'>You feel a sudden weakness in your muscles!</span>")
|
||||
owner.adjustStaminaLoss(50)
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/mild/muscle_spasms
|
||||
name = "Muscle Spasms"
|
||||
desc = "Patient has occasional muscle spasms, causing them to move unintentionally."
|
||||
scan_desc = "nervous fits"
|
||||
gain_text = "<span class='warning'>Your muscles feel oddly faint.</span>"
|
||||
lose_text = "<span class='notice'>You feel in control of your muscles again.</span>"
|
||||
|
||||
/datum/brain_trauma/mild/muscle_spasms/on_life()
|
||||
if(prob(7))
|
||||
switch(rand(1,5))
|
||||
if(1)
|
||||
if(owner.canmove && !isspaceturf(owner.loc))
|
||||
to_chat(owner, "<span class='warning'>Your leg spasms!</span>")
|
||||
step(owner, pick(GLOB.cardinals))
|
||||
if(2)
|
||||
if(owner.incapacitated())
|
||||
return
|
||||
var/obj/item/I = owner.get_active_held_item()
|
||||
if(I)
|
||||
to_chat(owner, "<span class='warning'>Your fingers spasm!</span>")
|
||||
log_attack("[key_name(owner)] used [I] due to a Muscle Spasm.")
|
||||
I.attack_self(owner)
|
||||
if(3)
|
||||
var/prev_intent = owner.a_intent
|
||||
owner.a_intent = INTENT_HARM
|
||||
|
||||
var/range = 1
|
||||
if(istype(owner.get_active_held_item(), /obj/item/gun)) //get targets to shoot at
|
||||
range = 7
|
||||
|
||||
var/list/mob/living/targets = list()
|
||||
for(var/mob/M in oview(owner, range))
|
||||
if(isliving(M))
|
||||
targets += M
|
||||
if(LAZYLEN(targets))
|
||||
to_chat(owner, "<span class='warning'>Your arm spasms!</span>")
|
||||
log_attack("[key_name(owner)] attacked someone due to a Muscle Spasm.") //the following attack will log itself
|
||||
owner.ClickOn(pick(targets))
|
||||
owner.a_intent = prev_intent
|
||||
if(4)
|
||||
var/prev_intent = owner.a_intent
|
||||
owner.a_intent = INTENT_HARM
|
||||
to_chat(owner, "<span class='warning'>Your arm spasms!</span>")
|
||||
log_attack("[key_name(owner)] attacked himself to a Muscle Spasm.")
|
||||
owner.ClickOn(owner)
|
||||
owner.a_intent = prev_intent
|
||||
if(5)
|
||||
if(owner.incapacitated())
|
||||
return
|
||||
var/obj/item/I = owner.get_active_held_item()
|
||||
var/list/turf/targets = list()
|
||||
for(var/turf/T in oview(owner, 3))
|
||||
targets += T
|
||||
if(LAZYLEN(targets) && I)
|
||||
to_chat(owner, "<span class='warning'>Your arm spasms!</span>")
|
||||
log_attack("[key_name(owner)] threw [I] due to a Muscle Spasm.")
|
||||
owner.throw_item(pick(targets))
|
||||
..()
|
||||
@@ -68,7 +68,7 @@
|
||||
return
|
||||
|
||||
/datum/brain_trauma/mild/phobia/on_hear(message, speaker, message_language, raw_message, radio_freq)
|
||||
if(owner.disabilities & DEAF || world.time < next_scare) //words can't trigger you if you can't hear them *taps head*
|
||||
if(owner.has_disability(DISABILITY_DEAF) || world.time < next_scare) //words can't trigger you if you can't hear them *taps head*
|
||||
return message
|
||||
for(var/word in trigger_words)
|
||||
if(findtext(message, word))
|
||||
|
||||
@@ -7,22 +7,39 @@
|
||||
/datum/brain_trauma/severe/mute
|
||||
name = "Mutism"
|
||||
desc = "Patient is completely unable to speak."
|
||||
scan_desc = "extensive damage to the brain's language center"
|
||||
scan_desc = "extensive damage to the brain's speech center"
|
||||
gain_text = "<span class='warning'>You forget how to speak!</span>"
|
||||
lose_text = "<span class='notice'>You suddenly remember how to speak.</span>"
|
||||
|
||||
/datum/brain_trauma/severe/mute/on_gain()
|
||||
owner.disabilities |= MUTE
|
||||
..()
|
||||
|
||||
//no fiddling with genetics to get out of this one
|
||||
/datum/brain_trauma/severe/mute/on_life()
|
||||
if(!(owner.disabilities & MUTE))
|
||||
on_gain()
|
||||
owner.add_disability(DISABILITY_MUTE, TRAUMA_DISABILITY)
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/severe/mute/on_lose()
|
||||
owner.disabilities &= ~MUTE
|
||||
owner.remove_disability(DISABILITY_MUTE, TRAUMA_DISABILITY)
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/severe/aphasia
|
||||
name = "Aphasia"
|
||||
desc = "Patient is unable to speak or understand any language."
|
||||
scan_desc = "extensive damage to the brain's language center"
|
||||
gain_text = "<span class='warning'>You have trouble forming words in your head...</span>"
|
||||
lose_text = "<span class='notice'>You suddenly remember how languages work.</span>"
|
||||
var/datum/language_holder/prev_language
|
||||
var/datum/language_holder/mob_language
|
||||
|
||||
/datum/brain_trauma/severe/aphasia/on_gain()
|
||||
mob_language = owner.get_language_holder()
|
||||
prev_language = mob_language.copy()
|
||||
mob_language.remove_all_languages()
|
||||
mob_language.grant_language(/datum/language/aphasia)
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/severe/aphasia/on_lose()
|
||||
mob_language.remove_language(/datum/language/aphasia)
|
||||
mob_language.copy_known_languages_from(prev_language) //this will also preserve languages learned during the trauma
|
||||
QDEL_NULL(prev_language)
|
||||
mob_language = null
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/severe/blindness
|
||||
@@ -109,7 +126,7 @@
|
||||
stress -= 4
|
||||
|
||||
/datum/brain_trauma/severe/monophobia/proc/check_alone()
|
||||
if(owner.disabilities & BLIND)
|
||||
if(owner.has_disability(DISABILITY_BLIND))
|
||||
return TRUE
|
||||
for(var/mob/M in oview(owner, 7))
|
||||
if(!isliving(M)) //ghosts ain't people
|
||||
@@ -171,9 +188,24 @@
|
||||
lose_text = "<span class='notice'>You feel in control of your hands again.</span>"
|
||||
|
||||
/datum/brain_trauma/severe/discoordination/on_gain()
|
||||
owner.disabilities |= MONKEYLIKE
|
||||
owner.add_disability(DISABILITY_MONKEYLIKE, TRAUMA_DISABILITY)
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/severe/discoordination/on_lose()
|
||||
owner.disabilities &= ~MONKEYLIKE
|
||||
owner.remove_disability(DISABILITY_MONKEYLIKE, TRAUMA_DISABILITY)
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/severe/pacifism
|
||||
name = "Traumatic Non-Violence"
|
||||
desc = "Patient is extremely unwilling to harm others in violent ways."
|
||||
scan_desc = "pacific syndrome"
|
||||
gain_text = "<span class='notice'>You feel oddly peaceful.</span>"
|
||||
lose_text = "<span class='notice'>You no longer feel compelled to not harm.</span>"
|
||||
|
||||
/datum/brain_trauma/severe/pacifism/on_gain()
|
||||
owner.add_disability(DISABILITY_PACIFISM, TRAUMA_DISABILITY)
|
||||
..()
|
||||
|
||||
/datum/brain_trauma/severe/pacifism/on_lose()
|
||||
owner.remove_disability(DISABILITY_PACIFISM, TRAUMA_DISABILITY)
|
||||
..()
|
||||
@@ -9,25 +9,35 @@
|
||||
scan_desc = "god delusion"
|
||||
gain_text = "<span class='notice'>You feel a higher power inside your mind...</span>"
|
||||
lose_text = "<span class='warning'>The divine presence leaves your head, no longer interested.</span>"
|
||||
var/next_speech = 0
|
||||
var/inspiration = FALSE
|
||||
|
||||
/datum/brain_trauma/special/godwoken/on_life()
|
||||
..()
|
||||
if(!inspiration && world.time > next_speech && prob(4))
|
||||
to_chat(owner, "<span class='notice'>[pick("You feel inspired!","You feel power course through you...","You feel something within you itching to speak...")]</span>")
|
||||
inspiration = TRUE
|
||||
if(prob(4))
|
||||
if(prob(33) && (owner.IsStun() || owner.IsKnockdown() || owner.IsUnconscious()))
|
||||
speak("unstun", TRUE)
|
||||
else if(prob(60) && owner.health <= HEALTH_THRESHOLD_CRIT)
|
||||
speak("heal", TRUE)
|
||||
else if(prob(30) && owner.a_intent == INTENT_HARM)
|
||||
speak("aggressive")
|
||||
else
|
||||
speak("neutral", prob(25))
|
||||
|
||||
/datum/brain_trauma/special/godwoken/on_say(message)
|
||||
if(world.time > next_speech && inspiration)
|
||||
playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 300, 1, 5)
|
||||
var/cooldown = voice_of_god(message, owner, list("colossus","yell"), 2)
|
||||
cooldown *= 0.33
|
||||
next_speech = world.time + cooldown
|
||||
inspiration = FALSE
|
||||
return ""
|
||||
else
|
||||
return message
|
||||
/datum/brain_trauma/special/godwoken/proc/speak(type, include_owner = FALSE)
|
||||
var/message
|
||||
switch(type)
|
||||
if("unstun")
|
||||
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_unstun")
|
||||
if("heal")
|
||||
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_heal")
|
||||
if("neutral")
|
||||
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_neutral")
|
||||
if("aggressive")
|
||||
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_aggressive")
|
||||
else
|
||||
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_neutral")
|
||||
|
||||
playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 200, 1, 5)
|
||||
voice_of_god(message, owner, list("colossus","yell"), 2.5, include_owner, FALSE)
|
||||
|
||||
/datum/brain_trauma/special/bluespace_prophet
|
||||
name = "Bluespace Prophecy"
|
||||
|
||||
@@ -23,10 +23,12 @@
|
||||
|
||||
/datum/brain_trauma/severe/split_personality/proc/get_ghost()
|
||||
set waitfor = FALSE
|
||||
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", null, null, null, 75, stranger_backseat)
|
||||
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", ROLE_PAI, null, null, 75, stranger_backseat)
|
||||
if(LAZYLEN(candidates))
|
||||
var/client/C = pick(candidates)
|
||||
stranger_backseat.key = C.key
|
||||
log_game("[key_name(stranger_backseat)] became [key_name(owner)]'s split personality.")
|
||||
message_admins("[key_name_admin(stranger_backseat)] became [key_name_admin(owner)]'s split personality.")
|
||||
else
|
||||
qdel(src)
|
||||
|
||||
@@ -59,7 +61,7 @@
|
||||
current_backseat = owner_backseat
|
||||
free_backseat = stranger_backseat
|
||||
|
||||
log_game("[current_backseat]/([current_backseat.ckey]) assumed control of [owner]/([owner.ckey] due to [src]. (Original owner: [current_controller == OWNER ? owner.ckey : current_backseat.ckey])")
|
||||
log_game("[key_name(current_backseat)] assumed control of [key_name(owner)] due to [src]. (Original owner: [current_controller == OWNER ? owner.ckey : current_backseat.ckey])")
|
||||
to_chat(owner, "<span class='userdanger'>You feel your control being taken away... your other personality is in charge now!</span>")
|
||||
to_chat(current_backseat, "<span class='userdanger'>You manage to take control of your body!</span>")
|
||||
|
||||
@@ -134,6 +136,7 @@
|
||||
/mob/living/split_personality/Login()
|
||||
..()
|
||||
to_chat(src, "<span class='notice'>As a split personality, you cannot do anything but observe. However, you will eventually gain control of your body, switching places with the current personality.</span>")
|
||||
to_chat(src, "<span class='warning'><b>Do not commit suicide or put the body in a deadly position. Behave like you care about it as much as the owner.</b></span>")
|
||||
|
||||
/mob/living/split_personality/say(message)
|
||||
to_chat(src, "<span class='warning'>You cannot speak, your other self is controlling your body!</span>")
|
||||
@@ -189,7 +192,7 @@
|
||||
return //no random switching
|
||||
|
||||
/datum/brain_trauma/severe/split_personality/brainwashing/on_hear(message, speaker, message_language, raw_message, radio_freq)
|
||||
if(owner.disabilities & DEAF || owner == speaker)
|
||||
if(owner.has_disability(DISABILITY_DEAF) || owner == speaker)
|
||||
return message
|
||||
if(findtext(message, codeword))
|
||||
message = replacetext(message, codeword, "<span class='warning'>[codeword]</span>")
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
var/datum/object = GLOBAL_PROC
|
||||
var/delegate
|
||||
var/list/arguments
|
||||
var/datum/weakref/user
|
||||
|
||||
/datum/callback/New(thingtocall, proctocall, ...)
|
||||
if (thingtocall)
|
||||
@@ -55,6 +56,8 @@
|
||||
delegate = proctocall
|
||||
if (length(args) > 2)
|
||||
arguments = args.Copy(3)
|
||||
if(usr)
|
||||
user = WEAKREF(usr)
|
||||
|
||||
/world/proc/ImmediateInvokeAsync(thingtocall, proctocall, ...)
|
||||
set waitfor = FALSE
|
||||
@@ -70,8 +73,16 @@
|
||||
call(thingtocall, proctocall)(arglist(calling_arguments))
|
||||
|
||||
/datum/callback/proc/Invoke(...)
|
||||
if(!usr)
|
||||
var/datum/weakref/W = user
|
||||
if(W)
|
||||
var/mob/M = W.resolve()
|
||||
if(M)
|
||||
return world.PushUsr(M, src)
|
||||
|
||||
if (!object)
|
||||
return
|
||||
|
||||
var/list/calling_arguments = arguments
|
||||
if (length(args))
|
||||
if (length(arguments))
|
||||
@@ -87,8 +98,17 @@
|
||||
//copy and pasted because fuck proc overhead
|
||||
/datum/callback/proc/InvokeAsync(...)
|
||||
set waitfor = FALSE
|
||||
|
||||
if(!usr)
|
||||
var/datum/weakref/W = user
|
||||
if(W)
|
||||
var/mob/M = W.resolve()
|
||||
if(M)
|
||||
return world.PushUsr(M, src)
|
||||
|
||||
if (!object)
|
||||
return
|
||||
|
||||
var/list/calling_arguments = arguments
|
||||
if (length(args))
|
||||
if (length(arguments))
|
||||
|
||||
@@ -40,6 +40,7 @@ Stands have a lot of procs which mimic mob procs. Rather than inserting hooks fo
|
||||
* `COMPONENT_DUPE_HIGHLANDER` (default): Old component will be deleted, new component will first have `/datum/component/proc/InheritComponent(datum/component/old, FALSE)` on it
|
||||
* `COMPONENT_DUPE_ALLOWED`: The components will be treated as separate, `GetComponent()` will return the first added
|
||||
* `COMPONENT_DUPE_UNIQUE`: New component will be deleted, old component will first have `/datum/component/proc/InheritComponent(datum/component/new, TRUE)` on it
|
||||
* `COMPONENT_DUPE_UNIQUE_PASSARGS`: New component will never exist and instead its initialization arguments will be passed on to the old component.
|
||||
1. `/datum/component/var/dupe_type` (protected, type)
|
||||
* Definition of a duplicate component type
|
||||
* `null` means exact match on `type` (default)
|
||||
@@ -66,6 +67,7 @@ Stands have a lot of procs which mimic mob procs. Rather than inserting hooks fo
|
||||
* All components a datum owns are deleted with the datum
|
||||
* Returns the component that was created. Or the old component in a dupe situation where `COMPONENT_DUPE_UNIQUE` was set
|
||||
* If this tries to add an component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it
|
||||
* Properly handles duplicate situations based on the `dupe_mode` var
|
||||
1. `/datum/proc/LoadComponent(component_type(type), ...) -> datum/component` (public, final)
|
||||
* Equivalent to calling `GetComponent(component_type)` where, if the result would be `null`, returns `AddComponent(component_type, ...)` instead
|
||||
1. `/datum/proc/ComponentActivated(datum/component/C)` (abstract, async)
|
||||
@@ -104,9 +106,8 @@ Stands have a lot of procs which mimic mob procs. Rather than inserting hooks fo
|
||||
* Allows the component to react to ownership transfers
|
||||
1. `/datum/component/proc/_RemoveFromParent()` (private, final)
|
||||
* Clears `parent` and removes the component from it's component list
|
||||
1. `/datum/component/proc/_CheckDupesAndJoinParent` (private, final)
|
||||
1. `/datum/component/proc/_JoinParent` (private, final)
|
||||
* Tries to add the component to it's `parent`s `datum_components` list
|
||||
* Properly handles duplicate situations based on the `dupe_mode` var
|
||||
1. `/datum/component/proc/RegisterSignal(signal(string/list of strings), proc_ref(type), override(boolean))` (protected, final) (Consider removing for performance gainz)
|
||||
* If signal is a list it will be as if RegisterSignal was called for each of the entries with the same following arguments
|
||||
* Makes a component listen for the specified `signal` on it's `parent` datum.
|
||||
|
||||
@@ -6,54 +6,16 @@
|
||||
var/datum/parent
|
||||
|
||||
/datum/component/New(datum/P, ...)
|
||||
if(type == /datum/component)
|
||||
qdel(src)
|
||||
CRASH("[type] instantiated!")
|
||||
|
||||
//check for common mishaps
|
||||
if(!isnum(dupe_mode))
|
||||
qdel(src)
|
||||
CRASH("[type]: Invalid dupe_mode!")
|
||||
var/dt = dupe_type
|
||||
if(dt && !ispath(dt))
|
||||
qdel(src)
|
||||
CRASH("[type]: Invalid dupe_type!")
|
||||
|
||||
parent = P
|
||||
var/list/arguments = args.Copy(2)
|
||||
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
|
||||
qdel(src, TRUE, TRUE)
|
||||
return
|
||||
|
||||
_CheckDupesAndJoinParent(P)
|
||||
_JoinParent(P)
|
||||
|
||||
/datum/component/proc/_CheckDupesAndJoinParent()
|
||||
/datum/component/proc/_JoinParent()
|
||||
var/datum/P = parent
|
||||
var/dm = dupe_mode
|
||||
|
||||
var/datum/component/old
|
||||
if(dm != COMPONENT_DUPE_ALLOWED)
|
||||
var/dt = dupe_type
|
||||
if(!dt)
|
||||
old = P.GetExactComponent(type)
|
||||
else
|
||||
old = P.GetComponent(dt)
|
||||
if(old)
|
||||
//One or the other has to die
|
||||
switch(dm)
|
||||
if(COMPONENT_DUPE_UNIQUE)
|
||||
old.InheritComponent(src, TRUE)
|
||||
qdel(src, TRUE, TRUE)
|
||||
return
|
||||
if(COMPONENT_DUPE_HIGHLANDER)
|
||||
InheritComponent(old, FALSE)
|
||||
qdel(old, FALSE, TRUE)
|
||||
|
||||
//provided we didn't eat someone
|
||||
if(!old)
|
||||
//let the others know
|
||||
P.SendSignal(COMSIG_COMPONENT_ADDED, src)
|
||||
|
||||
//lazy init the parent's dc list
|
||||
var/list/dc = P.datum_components
|
||||
if(!dc)
|
||||
@@ -212,10 +174,59 @@
|
||||
return list(.)
|
||||
|
||||
/datum/proc/AddComponent(new_type, ...)
|
||||
var/nt = new_type
|
||||
var/datum/component/nt = new_type
|
||||
var/dm = initial(nt.dupe_mode)
|
||||
var/dt = initial(nt.dupe_type)
|
||||
|
||||
var/datum/component/old_comp
|
||||
var/datum/component/new_comp
|
||||
|
||||
if(ispath(nt))
|
||||
if(nt == /datum/component)
|
||||
CRASH("[nt] attempted instantiation!")
|
||||
if(!isnum(dm))
|
||||
CRASH("[nt]: Invalid dupe_mode ([dm])!")
|
||||
if(dt && !ispath(dt))
|
||||
CRASH("[nt]: Invalid dupe_type ([dt])!")
|
||||
else
|
||||
new_comp = nt
|
||||
|
||||
args[1] = src
|
||||
var/datum/component/C = new nt(arglist(args))
|
||||
return QDELING(C) ? GetExactComponent(new_type) : C
|
||||
|
||||
if(dm != COMPONENT_DUPE_ALLOWED)
|
||||
if(!dt)
|
||||
old_comp = GetExactComponent(nt)
|
||||
else
|
||||
old_comp = GetComponent(dt)
|
||||
if(old_comp)
|
||||
switch(dm)
|
||||
if(COMPONENT_DUPE_UNIQUE)
|
||||
if(!new_comp)
|
||||
new_comp = new nt(arglist(args))
|
||||
if(!QDELETED(new_comp))
|
||||
old_comp.InheritComponent(new_comp, TRUE)
|
||||
QDEL_NULL(new_comp)
|
||||
if(COMPONENT_DUPE_HIGHLANDER)
|
||||
if(!new_comp)
|
||||
new_comp = new nt(arglist(args))
|
||||
if(!QDELETED(new_comp))
|
||||
new_comp.InheritComponent(old_comp, FALSE)
|
||||
QDEL_NULL(old_comp)
|
||||
if(COMPONENT_DUPE_UNIQUE_PASSARGS)
|
||||
if(!new_comp)
|
||||
var/list/arguments = args.Copy(2)
|
||||
old_comp.InheritComponent(null, TRUE, arguments)
|
||||
else
|
||||
old_comp.InheritComponent(new_comp, TRUE)
|
||||
else if(!new_comp)
|
||||
new_comp = new nt(arglist(args)) // There's a valid dupe mode but there's no old component, act like normal
|
||||
else if(!new_comp)
|
||||
new_comp = new nt(arglist(args)) // Dupes are allowed, act like normal
|
||||
|
||||
if(!old_comp && !QDELETED(new_comp)) // Nothing related to duplicate components happened and the new component is healthy
|
||||
SendSignal(COMSIG_COMPONENT_ADDED, new_comp)
|
||||
return new_comp
|
||||
return old_comp
|
||||
|
||||
/datum/proc/LoadComponent(component_type, ...)
|
||||
. = GetComponent(component_type)
|
||||
@@ -235,7 +246,8 @@
|
||||
C._RemoveFromParent()
|
||||
helicopter.SendSignal(COMSIG_COMPONENT_REMOVING, C)
|
||||
C.parent = src
|
||||
C._CheckDupesAndJoinParent()
|
||||
if(C == AddComponent(C))
|
||||
C._JoinParent()
|
||||
|
||||
/datum/proc/TransferComponents(datum/target)
|
||||
var/list/dc = datum_components
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
var/datum/callback/callback
|
||||
|
||||
/datum/component/archaeology/Initialize(_prob2drop, list/_archdrops = list(), datum/callback/_callback)
|
||||
prob2drop = Clamp(_prob2drop, 0, 100)
|
||||
prob2drop = CLAMP(_prob2drop, 0, 100)
|
||||
archdrops = _archdrops
|
||||
callback = _callback
|
||||
RegisterSignal(COMSIG_PARENT_ATTACKBY,.proc/Dig)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/datum/component/cleaning
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
|
||||
|
||||
/datum/component/cleaning/Initialize()
|
||||
if(!ismovableatom(parent))
|
||||
. = COMPONENT_INCOMPATIBLE
|
||||
CRASH("[type] added to a [parent.type]")
|
||||
RegisterSignal(list(COMSIG_MOVABLE_MOVED), .proc/Clean)
|
||||
|
||||
/datum/component/cleaning/proc/Clean()
|
||||
var/atom/movable/AM = parent
|
||||
var/turf/tile = AM.loc
|
||||
if(!isturf(tile))
|
||||
return
|
||||
|
||||
tile.clean_blood()
|
||||
for(var/A in tile)
|
||||
if(is_cleanable(A))
|
||||
qdel(A)
|
||||
else if(istype(A, /obj/item))
|
||||
var/obj/item/cleaned_item = A
|
||||
cleaned_item.clean_blood()
|
||||
else if(ishuman(A))
|
||||
var/mob/living/carbon/human/cleaned_human = A
|
||||
if(cleaned_human.lying)
|
||||
if(cleaned_human.head)
|
||||
cleaned_human.head.clean_blood()
|
||||
cleaned_human.update_inv_head()
|
||||
if(cleaned_human.wear_suit)
|
||||
cleaned_human.wear_suit.clean_blood()
|
||||
cleaned_human.update_inv_wear_suit()
|
||||
else if(cleaned_human.w_uniform)
|
||||
cleaned_human.w_uniform.clean_blood()
|
||||
cleaned_human.update_inv_w_uniform()
|
||||
if(cleaned_human.shoes)
|
||||
cleaned_human.shoes.clean_blood()
|
||||
cleaned_human.update_inv_shoes()
|
||||
cleaned_human.clean_blood()
|
||||
cleaned_human.wash_cream()
|
||||
to_chat(cleaned_human, "<span class='danger'>[AM] cleans your face!</span>")
|
||||
@@ -48,8 +48,7 @@
|
||||
if(old_dir == new_dir)
|
||||
return
|
||||
remove()
|
||||
var/rotation = SimplifyDegrees(dir2angle(new_dir)-dir2angle(old_dir))
|
||||
pic.dir = turn(pic.dir, rotation)
|
||||
pic.dir = turn(pic.dir, dir2angle(old_dir) - dir2angle(new_dir))
|
||||
apply()
|
||||
|
||||
/datum/component/decal/proc/clean_react(strength)
|
||||
@@ -57,4 +56,4 @@
|
||||
qdel(src)
|
||||
|
||||
/datum/component/decal/proc/examine(mob/user)
|
||||
to_chat(user, description)
|
||||
to_chat(user, description)
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/datum/component/forensics
|
||||
var/list/fingerprints //assoc print = print
|
||||
var/list/hiddenprints //assoc ckey = realname/gloves/ckey
|
||||
var/list/blood_DNA //assoc dna = bloodtype
|
||||
var/list/fibers //assoc print = print
|
||||
|
||||
/datum/component/forensics/InheritComponent(datum/component/forensics/F, original) //Use of | and |= being different here is INTENTIONAL.
|
||||
fingerprints = fingerprints | F.fingerprints
|
||||
hiddenprints = hiddenprints | F.hiddenprints
|
||||
blood_DNA = blood_DNA | F.blood_DNA
|
||||
fibers = fibers | F.fibers
|
||||
check_blood()
|
||||
return ..()
|
||||
|
||||
/datum/component/forensics/Initialize(new_fingerprints, new_hiddenprints, new_blood_DNA, new_fibers)
|
||||
if(!isatom(parent))
|
||||
. = COMPONENT_INCOMPATIBLE
|
||||
CRASH("Forensics datum applied incorrectly to non-atom of type [parent.type]!")
|
||||
fingerprints = new_fingerprints
|
||||
hiddenprints = new_hiddenprints
|
||||
blood_DNA = new_blood_DNA
|
||||
fibers = new_fibers
|
||||
check_blood()
|
||||
RegisterSignal(COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_act)
|
||||
|
||||
/datum/component/forensics/proc/wipe_fingerprints()
|
||||
fingerprints = null
|
||||
return TRUE
|
||||
|
||||
/datum/component/forensics/proc/wipe_hiddenprints()
|
||||
return //no.
|
||||
|
||||
/datum/component/forensics/proc/wipe_blood_DNA()
|
||||
blood_DNA = null
|
||||
if(isitem(parent))
|
||||
qdel(parent.GetComponent(/datum/component/decal/blood))
|
||||
return TRUE
|
||||
|
||||
/datum/component/forensics/proc/wipe_fibers()
|
||||
fibers = null
|
||||
return TRUE
|
||||
|
||||
/datum/component/forensics/proc/clean_act(strength)
|
||||
if(strength >= CLEAN_STRENGTH_FINGERPRINTS)
|
||||
wipe_fingerprints()
|
||||
if(strength >= CLEAN_STRENGTH_BLOOD)
|
||||
wipe_blood_DNA()
|
||||
if(strength >= CLEAN_STRENGTH_FIBERS)
|
||||
wipe_fibers()
|
||||
|
||||
/datum/component/forensics/proc/add_fingerprint_list(list/_fingerprints) //list(text)
|
||||
if(!length(_fingerprints))
|
||||
return
|
||||
LAZYINITLIST(fingerprints)
|
||||
for(var/i in _fingerprints) //We use an associative list, make sure we don't just merge a non-associative list into ours.
|
||||
fingerprints[i] = i
|
||||
return TRUE
|
||||
|
||||
/datum/component/forensics/proc/add_fingerprint(mob/living/M, ignoregloves = FALSE)
|
||||
if(!M)
|
||||
return
|
||||
add_hiddenprint(M)
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
add_fibers(H)
|
||||
if(H.gloves) //Check if the gloves (if any) hide fingerprints
|
||||
var/obj/item/clothing/gloves/G = H.gloves
|
||||
if(G.transfer_prints)
|
||||
ignoregloves = TRUE
|
||||
if(!ignoregloves)
|
||||
H.gloves.add_fingerprint(H, TRUE) //ignoregloves = 1 to avoid infinite loop.
|
||||
return
|
||||
var/full_print = md5(H.dna.uni_identity)
|
||||
LAZYSET(fingerprints, full_print, full_print)
|
||||
return TRUE
|
||||
|
||||
/datum/component/forensics/proc/add_fiber_list(list/_fibertext) //list(text)
|
||||
if(!length(_fibertext))
|
||||
return
|
||||
LAZYINITLIST(fibers)
|
||||
for(var/i in _fibertext) //We use an associative list, make sure we don't just merge a non-associative list into ours.
|
||||
fibers[i] = i
|
||||
return TRUE
|
||||
|
||||
/datum/component/forensics/proc/add_fibers(mob/living/carbon/human/M)
|
||||
var/fibertext
|
||||
var/item_multiplier = isitem(src)?1.2:1
|
||||
if(M.wear_suit)
|
||||
fibertext = "Material from \a [M.wear_suit]."
|
||||
if(prob(10*item_multiplier) && !LAZYACCESS(fibers, fibertext))
|
||||
LAZYSET(fibers, fibertext, fibertext)
|
||||
if(!(M.wear_suit.body_parts_covered & CHEST))
|
||||
if(M.w_uniform)
|
||||
fibertext = "Fibers from \a [M.w_uniform]."
|
||||
if(prob(12*item_multiplier) && !LAZYACCESS(fibers, fibertext)) //Wearing a suit means less of the uniform exposed.
|
||||
LAZYSET(fibers, fibertext, fibertext)
|
||||
if(!(M.wear_suit.body_parts_covered & HANDS))
|
||||
if(M.gloves)
|
||||
fibertext = "Material from a pair of [M.gloves.name]."
|
||||
if(prob(20*item_multiplier) && !LAZYACCESS(fibers, fibertext))
|
||||
LAZYSET(fibers, fibertext, fibertext)
|
||||
else if(M.w_uniform)
|
||||
fibertext = "Fibers from \a [M.w_uniform]."
|
||||
if(prob(15*item_multiplier) && !LAZYACCESS(fibers, fibertext))
|
||||
// "Added fibertext: [fibertext]"
|
||||
LAZYSET(fibers, fibertext, fibertext)
|
||||
if(M.gloves)
|
||||
fibertext = "Material from a pair of [M.gloves.name]."
|
||||
if(prob(20*item_multiplier) && !LAZYACCESS(fibers, fibertext))
|
||||
LAZYSET(fibers, fibertext, fibertext)
|
||||
else if(M.gloves)
|
||||
fibertext = "Material from a pair of [M.gloves.name]."
|
||||
if(prob(20*item_multiplier) && !LAZYACCESS(fibers, fibertext))
|
||||
LAZYSET(fibers, fibertext, fibertext)
|
||||
return TRUE
|
||||
|
||||
/datum/component/forensics/proc/add_hiddenprint_list(list/_hiddenprints) //list(ckey = text)
|
||||
if(!length(_hiddenprints))
|
||||
return
|
||||
LAZYINITLIST(hiddenprints)
|
||||
for(var/i in _hiddenprints) //We use an associative list, make sure we don't just merge a non-associative list into ours.
|
||||
hiddenprints[i] = _hiddenprints[i]
|
||||
return TRUE
|
||||
|
||||
/datum/component/forensics/proc/add_hiddenprint(mob/living/M)
|
||||
if(!M || !M.key)
|
||||
return
|
||||
var/hasgloves = ""
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(H.gloves)
|
||||
hasgloves = "(gloves)"
|
||||
var/current_time = time_stamp()
|
||||
if(!LAZYACCESS(hiddenprints, M.key))
|
||||
LAZYSET(hiddenprints, M.key, "First: [M.real_name]\[[current_time]\][hasgloves]. Ckey: [M.ckey]")
|
||||
else
|
||||
var/laststamppos = findtext(LAZYACCESS(hiddenprints, M.key), " Last: ")
|
||||
if(laststamppos)
|
||||
LAZYSET(hiddenprints, M.key, copytext(hiddenprints[M.key], 1, laststamppos))
|
||||
hiddenprints[M.key] += " Last: [M.real_name]\[[current_time]\][hasgloves]. Ckey: [M.ckey]" //made sure to be existing by if(!LAZYACCESS);else
|
||||
parent.fingerprintslast = M.ckey
|
||||
return TRUE
|
||||
|
||||
/datum/component/forensics/proc/add_blood_DNA(list/dna) //list(dna_enzymes = type)
|
||||
if(!length(dna))
|
||||
return
|
||||
LAZYINITLIST(blood_DNA)
|
||||
for(var/i in dna)
|
||||
blood_DNA[i] = dna[i]
|
||||
check_blood()
|
||||
return TRUE
|
||||
|
||||
/datum/component/forensics/proc/check_blood()
|
||||
if(!isitem(parent))
|
||||
return
|
||||
if(!length(blood_DNA))
|
||||
return
|
||||
parent.LoadComponent(/datum/component/decal/blood)
|
||||
@@ -0,0 +1,81 @@
|
||||
/datum/component/jousting
|
||||
var/current_direction = NONE
|
||||
var/max_tile_charge = 5
|
||||
var/min_tile_charge = 2 //tiles before this code gets into effect.
|
||||
var/current_tile_charge = 0
|
||||
var/movement_reset_tolerance = 2 //deciseconds
|
||||
var/unmounted_damage_boost_per_tile = 0
|
||||
var/unmounted_knockdown_chance_per_tile = 0
|
||||
var/unmounted_knockdown_time = 0
|
||||
var/mounted_damage_boost_per_tile = 2
|
||||
var/mounted_knockdown_chance_per_tile = 20
|
||||
var/mounted_knockdown_time = 20
|
||||
var/requires_mob_riding = TRUE //whether this only works if the attacker is riding a mob, rather than anything they can buckle to.
|
||||
var/requires_mount = TRUE //kinda defeats the point of jousting if you're not mounted but whatever.
|
||||
var/mob/current_holder
|
||||
var/datum/component/redirect/listener
|
||||
var/current_timerid
|
||||
|
||||
/datum/component/jousting/Initialize()
|
||||
if(!isitem(parent))
|
||||
. = COMPONENT_INCOMPATIBLE
|
||||
stack_trace("Warning: Jousting component incorrectly applied to invalid parent type [parent.type]")
|
||||
RegisterSignal(COMSIG_ITEM_EQUIPPED, .proc/on_equip)
|
||||
RegisterSignal(COMSIG_ITEM_DROPPED, .proc/on_drop)
|
||||
RegisterSignal(COMSIG_ITEM_ATTACK, .proc/on_attack)
|
||||
|
||||
/datum/component/jousting/Destroy()
|
||||
QDEL_NULL(listener)
|
||||
return ..()
|
||||
|
||||
/datum/component/jousting/proc/on_equip(mob/user, slot)
|
||||
QDEL_NULL(listener)
|
||||
current_holder = user
|
||||
listener = new(user, COMSIG_MOVABLE_MOVED, CALLBACK(src, .proc/mob_move))
|
||||
|
||||
/datum/component/jousting/proc/on_drop(mob/user)
|
||||
QDEL_NULL(listener)
|
||||
current_holder = null
|
||||
current_direction = NONE
|
||||
current_tile_charge = 0
|
||||
|
||||
/datum/component/jousting/proc/on_attack(mob/living/target, mob/user)
|
||||
if(user != current_holder)
|
||||
return
|
||||
var/current = current_tile_charge
|
||||
var/obj/item/I = parent
|
||||
var/target_buckled = target.buckled ? TRUE : FALSE //we don't need the reference of what they're buckled to, just whether they are.
|
||||
if((requires_mount && ((requires_mob_riding && !ismob(user.buckled)) || (!user.buckled))) || !current_direction || (current_tile_charge < min_tile_charge))
|
||||
return
|
||||
var/turf/target_turf = get_step(user, current_direction)
|
||||
if(target in range(1, target_turf))
|
||||
var/knockdown_chance = (target_buckled? mounted_knockdown_chance_per_tile : unmounted_knockdown_chance_per_tile) * current
|
||||
var/knockdown_time = (target_buckled? mounted_knockdown_time : unmounted_knockdown_time)
|
||||
var/damage = (target_buckled? mounted_damage_boost_per_tile : unmounted_damage_boost_per_tile) * current
|
||||
var/sharp = I.is_sharp()
|
||||
var/msg
|
||||
if(damage)
|
||||
msg += "[user] [sharp? "impales" : "slams into"] [target] [sharp? "on" : "with"] their [parent]"
|
||||
target.apply_damage(damage, BRUTE, user.zone_selected, 0)
|
||||
if(prob(knockdown_chance))
|
||||
msg += " and knocks [target] [target_buckled? "off of [target.buckled]" : "down"]"
|
||||
if(target_buckled)
|
||||
target.buckled.unbuckle_mob(target)
|
||||
target.Knockdown(knockdown_time)
|
||||
if(length(msg))
|
||||
user.visible_message("<span class='danger'>[msg]!</span>")
|
||||
|
||||
/datum/component/jousting/proc/mob_move(newloc, dir)
|
||||
if(!current_holder || (requires_mount && ((requires_mob_riding && !ismob(current_holder.buckled)) || (!current_holder.buckled))))
|
||||
return
|
||||
if(dir != current_direction)
|
||||
current_tile_charge = 0
|
||||
current_direction = dir
|
||||
if(current_tile_charge < max_tile_charge)
|
||||
current_tile_charge++
|
||||
if(current_timerid)
|
||||
deltimer(current_timerid)
|
||||
current_timerid = addtimer(CALLBACK(src, .proc/reset_charge), movement_reset_tolerance, TIMER_STOPPABLE)
|
||||
|
||||
/datum/component/jousting/proc/reset_charge()
|
||||
current_tile_charge = 0
|
||||
@@ -0,0 +1,53 @@
|
||||
//Items with these will have a chance to get knocked off when disarming
|
||||
/datum/component/knockoff
|
||||
var/knockoff_chance = 100 //Chance to knockoff
|
||||
var/list/target_zones //Aiming for these zones will cause the knockoff, null means all zones allowed
|
||||
var/list/slots_knockoffable //Can be only knocked off from these slots, null means all slots allowed
|
||||
var/datum/component/redirect/disarm_redirect
|
||||
|
||||
/datum/component/knockoff/Initialize(knockoff_chance,zone_override,slots_knockoffable)
|
||||
if(!isitem(parent))
|
||||
. = COMPONENT_INCOMPATIBLE
|
||||
CRASH("Knockoff component misuse")
|
||||
RegisterSignal(COMSIG_ITEM_EQUIPPED,.proc/OnEquipped)
|
||||
RegisterSignal(COMSIG_ITEM_DROPPED,.proc/OnDropped)
|
||||
|
||||
src.knockoff_chance = knockoff_chance
|
||||
|
||||
if(zone_override)
|
||||
target_zones = zone_override
|
||||
|
||||
if(slots_knockoffable)
|
||||
src.slots_knockoffable = slots_knockoffable
|
||||
|
||||
/datum/component/knockoff/proc/Knockoff(mob/living/attacker,zone)
|
||||
var/obj/item/I = parent
|
||||
var/mob/living/carbon/human/wearer = I.loc
|
||||
if(!istype(wearer))
|
||||
return
|
||||
if(target_zones && !(zone in target_zones))
|
||||
return
|
||||
if(!prob(knockoff_chance))
|
||||
return
|
||||
if(!wearer.dropItemToGround(I))
|
||||
return
|
||||
|
||||
wearer.visible_message("<span class='warning'>[attacker] knocks off [wearer]'s [I.name]!</span>","<span class='userdanger'>[attacker] knocks off your [I.name]!</span>")
|
||||
|
||||
/datum/component/knockoff/proc/OnEquipped(mob/living/carbon/human/H,slot)
|
||||
if(!istype(H))
|
||||
return
|
||||
if(slots_knockoffable && !(slot in slots_knockoffable))
|
||||
if(disarm_redirect)
|
||||
QDEL_NULL(disarm_redirect)
|
||||
return
|
||||
if(!disarm_redirect)
|
||||
disarm_redirect = H.AddComponent(/datum/component/redirect,list(COMSIG_HUMAN_DISARM_HIT),CALLBACK(src,.proc/Knockoff))
|
||||
|
||||
/datum/component/knockoff/proc/OnDropped(mob/living/M)
|
||||
if(disarm_redirect)
|
||||
QDEL_NULL(disarm_redirect)
|
||||
|
||||
/datum/component/knockoff/Destroy()
|
||||
QDEL_NULL(disarm_redirect)
|
||||
. = ..()
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
/datum/component/material_container/proc/OnAttackBy(obj/item/I, mob/living/user)
|
||||
var/list/tc = allowed_typecache
|
||||
if(user.a_intent == INTENT_HARM)
|
||||
if(user.a_intent != INTENT_HELP)
|
||||
return
|
||||
if((I.flags_2 & (HOLOGRAM_2 | NO_MAT_REDEMPTION_2)) || (tc && !is_type_in_typecache(I, tc)))
|
||||
to_chat(user, "<span class='warning'>[parent] won't accept [I]!</span>")
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#define RAD_AMOUNT_EXTREME 1000
|
||||
|
||||
/datum/component/radioactive
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
|
||||
|
||||
var/source
|
||||
|
||||
@@ -47,13 +47,16 @@
|
||||
if(strength <= RAD_BACKGROUND_RADIATION)
|
||||
return PROCESS_KILL
|
||||
|
||||
/datum/component/radioactive/InheritComponent(datum/component/C, i_am_original)
|
||||
/datum/component/radioactive/InheritComponent(datum/component/C, i_am_original, list/arguments)
|
||||
if(!i_am_original)
|
||||
return
|
||||
if(!hl3_release_date) // Permanently radioactive things don't get to grow stronger
|
||||
return
|
||||
var/datum/component/radioactive/other = C
|
||||
strength = max(strength, other.strength)
|
||||
if(C)
|
||||
var/datum/component/radioactive/other = C
|
||||
strength = max(strength, other.strength)
|
||||
else
|
||||
strength = max(strength, arguments[1])
|
||||
|
||||
/datum/component/radioactive/proc/rad_examine(mob/user, atom/thing)
|
||||
var/atom/master = parent
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/component/thermite
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
|
||||
var/amount
|
||||
var/overlay
|
||||
|
||||
@@ -46,10 +46,13 @@
|
||||
master.cut_overlay(overlay)
|
||||
return ..()
|
||||
|
||||
/datum/component/thermite/InheritComponent(datum/component/thermite/newC, i_am_original)
|
||||
/datum/component/thermite/InheritComponent(datum/component/thermite/newC, i_am_original, list/arguments)
|
||||
if(!i_am_original)
|
||||
return
|
||||
amount += newC.amount
|
||||
if(newC)
|
||||
amount += newC.amount
|
||||
else
|
||||
amount += arguments[1]
|
||||
|
||||
/datum/component/thermite/proc/thermite_melt(mob/user)
|
||||
var/turf/master = parent
|
||||
@@ -60,7 +63,7 @@
|
||||
|
||||
if(amount >= 50)
|
||||
var/burning_time = max(100, 100-amount)
|
||||
master = master.ChangeTurf(master.baseturf)
|
||||
master = master.ScrapeAway()
|
||||
master.burn_tile()
|
||||
if(user)
|
||||
master.add_hiddenprint(user)
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
addtimer(CALLBACK(src, .proc/charge), charge_rate)
|
||||
|
||||
/datum/action/innate/dash/proc/charge()
|
||||
current_charges = Clamp(current_charges + 1, 0, max_charges)
|
||||
current_charges = CLAMP(current_charges + 1, 0, max_charges)
|
||||
holder.update_action_buttons_icon()
|
||||
if(recharge_sound)
|
||||
playsound(dashing_item, recharge_sound, 50, 1)
|
||||
|
||||
@@ -3,29 +3,29 @@
|
||||
for(var/thing in viruses)
|
||||
var/datum/disease/DD = thing
|
||||
if(D.IsSame(DD))
|
||||
return 1
|
||||
return 0
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
/mob/proc/CanContractDisease(datum/disease/D)
|
||||
if(stat == DEAD)
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
if(D.GetDiseaseID() in resistances)
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
if(HasDisease(D))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
if(!(type in D.viable_mobtypes))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/proc/ContactContractDisease(datum/disease/D)
|
||||
if(!CanContractDisease(D))
|
||||
return 0
|
||||
return FALSE
|
||||
AddDisease(D)
|
||||
|
||||
|
||||
@@ -53,12 +53,13 @@
|
||||
else
|
||||
DD.vars[V] = D.vars[V]
|
||||
|
||||
DD.after_add()
|
||||
DD.affected_mob.med_hud_set_status()
|
||||
|
||||
|
||||
/mob/living/carbon/ContactContractDisease(datum/disease/D, target_zone)
|
||||
if(!CanContractDisease(D))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
var/obj/item/clothing/Cl = null
|
||||
var/passed = TRUE
|
||||
|
||||
@@ -60,14 +60,14 @@
|
||||
|
||||
/datum/disease/proc/has_cure()
|
||||
if(!(disease_flags & CURABLE))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
. = cures.len
|
||||
for(var/C_id in cures)
|
||||
if(!affected_mob.reagents.has_reagent(C_id))
|
||||
.--
|
||||
if(!. || (needs_all_cures && . < cures.len))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
//Airborne spreading
|
||||
/datum/disease/proc/spread(force_spread = 0)
|
||||
@@ -111,8 +111,8 @@
|
||||
|
||||
/datum/disease/proc/IsSame(datum/disease/D)
|
||||
if(istype(src, D.type))
|
||||
return 1
|
||||
return 0
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
/datum/disease/proc/Copy()
|
||||
@@ -120,6 +120,9 @@
|
||||
D.strain_data = strain_data.Copy()
|
||||
return D
|
||||
|
||||
/datum/disease/proc/after_add()
|
||||
return
|
||||
|
||||
|
||||
/datum/disease/proc/GetDiseaseID()
|
||||
return "[type]"
|
||||
|
||||
@@ -185,10 +185,10 @@
|
||||
if(properties["stealth"] >= 2)
|
||||
visibility_flags = HIDDEN_SCANNER
|
||||
|
||||
SetSpread(Clamp(2 ** (properties["transmittable"] - symptoms.len), VIRUS_SPREAD_BLOOD, VIRUS_SPREAD_AIRBORNE))
|
||||
SetSpread(CLAMP(2 ** (properties["transmittable"] - symptoms.len), VIRUS_SPREAD_BLOOD, VIRUS_SPREAD_AIRBORNE))
|
||||
|
||||
permeability_mod = max(Ceiling(0.4 * properties["transmittable"]), 1)
|
||||
cure_chance = 15 - Clamp(properties["resistance"], -5, 5) // can be between 10 and 20
|
||||
permeability_mod = max(CEILING(0.4 * properties["transmittable"], 1), 1)
|
||||
cure_chance = 15 - CLAMP(properties["resistance"], -5, 5) // can be between 10 and 20
|
||||
stage_prob = max(properties["stage_rate"], 2)
|
||||
SetSeverity(properties["severity"])
|
||||
GenerateCure(properties)
|
||||
@@ -243,7 +243,7 @@
|
||||
// Will generate a random cure, the less resistance the symptoms have, the harder the cure.
|
||||
/datum/disease/advance/proc/GenerateCure()
|
||||
if(properties && properties.len)
|
||||
var/res = Clamp(properties["resistance"] - (symptoms.len / 2), 1, advance_cures.len)
|
||||
var/res = CLAMP(properties["resistance"] - (symptoms.len / 2), 1, advance_cures.len)
|
||||
cures = list(advance_cures[res])
|
||||
|
||||
// Get the cure name from the cure_id
|
||||
|
||||
@@ -61,7 +61,7 @@ Bonus
|
||||
M.become_nearsighted()
|
||||
if(prob(eyes.eye_damage - 10 + 1))
|
||||
if(!remove_eyes)
|
||||
if(M.become_blind())
|
||||
if(!M.has_disability(DISABILITY_BLIND))
|
||||
to_chat(M, "<span class='userdanger'>You go blind!</span>")
|
||||
else
|
||||
M.visible_message("<span class='warning'>[M]'s eyes fall off their sockets!</span>", "<span class='userdanger'>Your eyes fall off their sockets!</span>")
|
||||
@@ -111,18 +111,16 @@ Bonus
|
||||
return
|
||||
switch(A.stage)
|
||||
if(4, 5) //basically oculine
|
||||
if(M.disabilities & BLIND)
|
||||
if(M.has_disability(DISABILITY_BLIND, EYE_DAMAGE))
|
||||
if(prob(20))
|
||||
to_chat(M, "<span class='warning'>Your vision slowly returns...</span>")
|
||||
M.cure_blind()
|
||||
M.cure_nearsighted()
|
||||
M.blur_eyes(35)
|
||||
|
||||
else if(M.disabilities & NEARSIGHT)
|
||||
else if(M.has_disability(DISABILITY_NEARSIGHT, EYE_DAMAGE))
|
||||
to_chat(M, "<span class='warning'>The blackness in your peripheral vision fades.</span>")
|
||||
M.cure_nearsighted()
|
||||
M.blur_eyes(10)
|
||||
|
||||
else if(M.eye_blind || M.eye_blurry)
|
||||
M.set_blindness(0)
|
||||
M.set_blurriness(0)
|
||||
|
||||
@@ -65,8 +65,8 @@
|
||||
|
||||
/datum/disease/transformation/jungle_fever
|
||||
name = "Jungle Fever"
|
||||
cure_text = "Bananas"
|
||||
cures = list("banana")
|
||||
cure_text = "Death."
|
||||
cures = list("adminordrazine")
|
||||
spread_text = "Monkey Bites"
|
||||
spread_flags = VIRUS_SPREAD_SPECIAL
|
||||
viable_mobtypes = list(/mob/living/carbon/monkey, /mob/living/carbon/human)
|
||||
@@ -88,9 +88,12 @@
|
||||
stage5 = list("<span class='warning'>You feel like monkeying around.</span>")
|
||||
|
||||
/datum/disease/transformation/jungle_fever/do_disease_transformation(mob/living/carbon/affected_mob)
|
||||
if(!ismonkey(affected_mob))
|
||||
SSticker.mode.add_monkey(affected_mob.mind)
|
||||
affected_mob.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
|
||||
if(affected_mob.mind && !is_monkey(affected_mob))
|
||||
add_monkey(affected_mob.mind)
|
||||
if(ishuman(affected_mob))
|
||||
var/mob/living/carbon/monkey/M = affected_mob.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
|
||||
M.ventcrawler = VENTCRAWLER_ALWAYS
|
||||
|
||||
|
||||
/datum/disease/transformation/jungle_fever/stage_act()
|
||||
..()
|
||||
@@ -107,9 +110,18 @@
|
||||
affected_mob.say(pick("Eeek, ook ook!", "Eee-eeek!", "Eeee!", "Ungh, ungh."))
|
||||
|
||||
/datum/disease/transformation/jungle_fever/cure()
|
||||
SSticker.mode.remove_monkey(affected_mob.mind)
|
||||
remove_monkey(affected_mob.mind)
|
||||
..()
|
||||
|
||||
/datum/disease/transformation/jungle_fever/monkeymode
|
||||
visibility_flags = HIDDEN_SCANNER|HIDDEN_PANDEMIC
|
||||
disease_flags = CAN_CARRY //no vaccines! no cure!
|
||||
|
||||
/datum/disease/transformation/jungle_fever/monkeymode/after_add()
|
||||
if(affected_mob && !is_monkey_leader(affected_mob.mind))
|
||||
visibility_flags = NONE
|
||||
|
||||
|
||||
|
||||
/datum/disease/transformation/robot
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
var/emote_type = EMOTE_VISIBLE //Whether the emote is visible or audible
|
||||
var/restraint_check = FALSE //Checks if the mob is restrained before performing the emote
|
||||
var/muzzle_ignore = FALSE //Will only work if the emote is EMOTE_AUDIBLE
|
||||
var/list/mob_type_allowed_typecache //Types that are allowed to use that emote
|
||||
var/list/mob_type_allowed_typecache = list(/mob) //Types that are allowed to use that emote
|
||||
var/list/mob_type_blacklist_typecache //Types that are NOT allowed to use that emote
|
||||
var/list/mob_type_ignore_stat_typecache
|
||||
var/stat_allowed = CONSCIOUS
|
||||
var/static/list/emote_list = list()
|
||||
|
||||
@@ -26,6 +27,7 @@
|
||||
emote_list[key_third_person] = src
|
||||
mob_type_allowed_typecache = typecacheof(mob_type_allowed_typecache)
|
||||
mob_type_blacklist_typecache = typecacheof(mob_type_blacklist_typecache)
|
||||
mob_type_ignore_stat_typecache = typecacheof(mob_type_ignore_stat_typecache)
|
||||
|
||||
/datum/emote/proc/run_emote(mob/user, params, type_override)
|
||||
. = TRUE
|
||||
@@ -37,9 +39,10 @@
|
||||
|
||||
msg = replace_pronoun(user, msg)
|
||||
|
||||
var/mob/living/L = user
|
||||
for(var/obj/item/implant/I in L.implants)
|
||||
I.trigger(key, L)
|
||||
if(isliving(user))
|
||||
var/mob/living/L = user
|
||||
for(var/obj/item/implant/I in L.implants)
|
||||
I.trigger(key, L)
|
||||
|
||||
if(!msg)
|
||||
return
|
||||
@@ -97,7 +100,7 @@
|
||||
return FALSE
|
||||
if(is_type_in_typecache(user, mob_type_blacklist_typecache))
|
||||
return FALSE
|
||||
if(status_check)
|
||||
if(status_check && !is_type_in_typecache(user, mob_type_ignore_stat_typecache))
|
||||
if(user.stat > stat_allowed || (user.status_flags & FAKEDEATH))
|
||||
to_chat(user, "<span class='notice'>You cannot [key] while unconscious.</span>")
|
||||
return FALSE
|
||||
|
||||
@@ -55,6 +55,8 @@ GLOBAL_LIST_EMPTY(explosions)
|
||||
var/orig_heavy_range = heavy_impact_range
|
||||
var/orig_light_range = light_impact_range
|
||||
|
||||
var/orig_max_distance = max(devastation_range, heavy_impact_range, light_impact_range, flash_range, flame_range)
|
||||
|
||||
//Zlevel specific bomb cap multiplier
|
||||
var/cap_multiplier = 1
|
||||
switch(epicenter.z)
|
||||
@@ -85,9 +87,16 @@ GLOBAL_LIST_EMPTY(explosions)
|
||||
|
||||
var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flame_range)
|
||||
|
||||
var/area/epi_area = get_area(epicenter)
|
||||
if(adminlog)
|
||||
message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area: [get_area(epicenter)] [ADMIN_COORDJMP(epicenter)]")
|
||||
message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area: [epi_area] [ADMIN_COORDJMP(epicenter)]")
|
||||
log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z])")
|
||||
|
||||
var/x0 = epicenter.x
|
||||
var/y0 = epicenter.y
|
||||
var/z0 = epicenter.z
|
||||
|
||||
SSblackbox.record_feedback("associative", "explosion", 1, list("dev" = devastation_range, "heavy" = heavy_impact_range, "light" = light_impact_range, "flash" = flash_range, "flame" = flame_range, "orig_dev" = orig_dev_range, "orig_heavy" = orig_heavy_range, "orig_light" = orig_light_range, "x" = x0, "y" = y0, "z" = z0, "area" = epi_area.type))
|
||||
|
||||
// Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves.
|
||||
// Stereo users will also hear the direction of the explosion!
|
||||
@@ -99,10 +108,6 @@ GLOBAL_LIST_EMPTY(explosions)
|
||||
far_dist += heavy_impact_range * 5
|
||||
far_dist += devastation_range * 20
|
||||
|
||||
var/x0 = epicenter.x
|
||||
var/y0 = epicenter.y
|
||||
var/z0 = epicenter.z
|
||||
|
||||
if(!silent)
|
||||
var/frequency = get_rand_frequency()
|
||||
var/sound/explosion_sound = sound(get_sfx("explosion"))
|
||||
@@ -116,11 +121,13 @@ GLOBAL_LIST_EMPTY(explosions)
|
||||
// If inside the blast radius + world.view - 2
|
||||
if(dist <= round(max_range + world.view - 2, 1))
|
||||
M.playsound_local(epicenter, null, 100, 1, frequency, falloff = 5, S = explosion_sound)
|
||||
shake_camera(M, 25, min(orig_max_distance - dist, 100))
|
||||
// You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
|
||||
else if(dist <= far_dist)
|
||||
var/far_volume = Clamp(far_dist, 30, 50) // Volume is based on explosion size and dist
|
||||
var/far_volume = CLAMP(far_dist, 30, 50) // Volume is based on explosion size and dist
|
||||
far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion
|
||||
M.playsound_local(epicenter, null, far_volume, 1, frequency, falloff = 5, S = far_explosion_sound)
|
||||
shake_camera(M, 10, min(orig_max_distance - dist, 50))
|
||||
EX_PREPROCESS_CHECK_TICK
|
||||
|
||||
//postpone processing for a bit
|
||||
|
||||
@@ -10,6 +10,7 @@ GLOBAL_LIST_INIT(huds, list(
|
||||
DATA_HUD_MEDICAL_ADVANCED = new/datum/atom_hud/data/human/medical/advanced(),
|
||||
DATA_HUD_DIAGNOSTIC_BASIC = new/datum/atom_hud/data/diagnostic/basic(),
|
||||
DATA_HUD_DIAGNOSTIC_ADVANCED = new/datum/atom_hud/data/diagnostic/advanced(),
|
||||
DATA_HUD_ABDUCTOR = new/datum/atom_hud/abductor(),
|
||||
ANTAG_HUD_CULT = new/datum/atom_hud/antag(),
|
||||
ANTAG_HUD_REV = new/datum/atom_hud/antag(),
|
||||
ANTAG_HUD_OPS = new/datum/atom_hud/antag(),
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
mid_sounds = list('sound/machines/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/fryer/deep_fryer_2.ogg' = 1)
|
||||
mid_length = 2
|
||||
end_sound = 'sound/machines/fryer/deep_fryer_emerge.ogg'
|
||||
volume = 25
|
||||
volume = 15
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
"<span class='userdanger'>[A] slams your chest! You can't breathe!</span>")
|
||||
playsound(get_turf(A), 'sound/effects/hit_punch.ogg', 50, 1, -1)
|
||||
if(D.losebreath <= 10)
|
||||
D.losebreath = Clamp(D.losebreath + 5, 0, 10)
|
||||
D.losebreath = CLAMP(D.losebreath + 5, 0, 10)
|
||||
D.adjustOxyLoss(10)
|
||||
add_logs(A, D, "quickchoked")
|
||||
return 1
|
||||
@@ -112,7 +112,7 @@
|
||||
playsound(get_turf(A), 'sound/effects/hit_punch.ogg', 50, 1, -1)
|
||||
D.apply_damage(5, BRUTE)
|
||||
if(D.silent <= 10)
|
||||
D.silent = Clamp(D.silent + 10, 0, 10)
|
||||
D.silent = CLAMP(D.silent + 10, 0, 10)
|
||||
add_logs(A, D, "neck chopped")
|
||||
return 1
|
||||
|
||||
|
||||
@@ -44,11 +44,9 @@
|
||||
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 40, 1, -1)
|
||||
D.apply_damage(rand(5,10), BRUTE, "head")
|
||||
A.apply_damage(rand(5,10), BRUTE, "head")
|
||||
if(!istype(A.head,/obj/item/clothing/head/helmet/) && !istype(A.head,/obj/item/clothing/head/hardhat))
|
||||
A.adjustBrainLoss(5)
|
||||
if(!istype(D.head,/obj/item/clothing/head/helmet/) && !istype(D.head,/obj/item/clothing/head/hardhat))
|
||||
D.adjustBrainLoss(5)
|
||||
A.Stun(rand(5,30))
|
||||
A.Stun(rand(10,45))
|
||||
D.Stun(rand(5,30))
|
||||
if(5,6)
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
|
||||
/obj/item/twohanded/bostaff/attack(mob/target, mob/living/user)
|
||||
add_fingerprint(user)
|
||||
if((CLUMSY in user.disabilities) && prob(50))
|
||||
if((user.has_disability(DISABILITY_CLUMSY)) && prob(50))
|
||||
to_chat(user, "<span class ='warning'>You club yourself over the head with [src].</span>")
|
||||
user.Knockdown(60)
|
||||
if(ishuman(user))
|
||||
|
||||
+62
-97
@@ -149,7 +149,7 @@
|
||||
return
|
||||
LAZYADD(antag_datums, A)
|
||||
A.create_team(team)
|
||||
var/datum/objective_team/antag_team = A.get_team()
|
||||
var/datum/team/antag_team = A.get_team()
|
||||
if(antag_team)
|
||||
antag_team.add_member(src)
|
||||
A.on_gain()
|
||||
@@ -185,12 +185,6 @@
|
||||
objectives, uplinks, powers etc are all handled.
|
||||
*/
|
||||
|
||||
/datum/mind/proc/remove_objectives()
|
||||
if(objectives.len)
|
||||
for(var/datum/objective/O in objectives)
|
||||
objectives -= O
|
||||
qdel(O)
|
||||
|
||||
/datum/mind/proc/remove_changeling()
|
||||
var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling)
|
||||
if(C)
|
||||
@@ -207,13 +201,12 @@
|
||||
remove_antag_datum(ANTAG_DATUM_BROTHER)
|
||||
SSticker.mode.update_brother_icons_removed(src)
|
||||
|
||||
|
||||
/datum/mind/proc/remove_nukeop()
|
||||
if(src in SSticker.mode.syndicates)
|
||||
SSticker.mode.syndicates -= src
|
||||
SSticker.mode.update_synd_icons_removed(src)
|
||||
special_role = null
|
||||
remove_objectives()
|
||||
remove_antag_equip()
|
||||
var/datum/antagonist/nukeop/nuke = has_antag_datum(/datum/antagonist/nukeop,TRUE)
|
||||
if(nuke)
|
||||
remove_antag_datum(nuke.type)
|
||||
special_role = null
|
||||
|
||||
/datum/mind/proc/remove_wizard()
|
||||
remove_antag_datum(/datum/antagonist/wizard)
|
||||
@@ -223,7 +216,6 @@
|
||||
if(src in SSticker.mode.cult)
|
||||
SSticker.mode.remove_cultist(src, 0, 0)
|
||||
special_role = null
|
||||
remove_objectives()
|
||||
remove_antag_equip()
|
||||
|
||||
/datum/mind/proc/remove_rev()
|
||||
@@ -329,14 +321,19 @@
|
||||
SSticker.mode.add_cultist(src)
|
||||
|
||||
else if(is_revolutionary(creator))
|
||||
var/datum/antagonist/rev/converter = creator.mind.has_antag_datum(/datum/antagonist/rev)
|
||||
var/datum/antagonist/rev/converter = creator.mind.has_antag_datum(/datum/antagonist/rev,TRUE)
|
||||
converter.add_revolutionary(src,FALSE)
|
||||
|
||||
else if(is_servant_of_ratvar(creator))
|
||||
add_servant_of_ratvar(current)
|
||||
|
||||
else if(is_nuclear_operative(creator))
|
||||
make_Nuke(null, null, 0, FALSE)
|
||||
var/datum/antagonist/nukeop/converter = creator.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE)
|
||||
var/datum/antagonist/nukeop/N = new(src)
|
||||
N.send_to_spawnpoint = FALSE
|
||||
N.nukeop_outfit = null
|
||||
add_antag_datum(N,converter.nuke_team)
|
||||
|
||||
|
||||
enslaved_to = creator
|
||||
|
||||
@@ -493,7 +490,8 @@
|
||||
if (SSticker.mode.config_tag=="nuclear")
|
||||
text = uppertext(text)
|
||||
text = "<i><b>[text]</b></i>: "
|
||||
if (src in SSticker.mode.syndicates)
|
||||
var/datum/antagonist/nukeop/N = has_antag_datum(/datum/antagonist/nukeop,TRUE)
|
||||
if(N)
|
||||
text += "<b>OPERATIVE</b> | <a href='?src=[REF(src)];nuclear=clear'>nanotrasen</a>"
|
||||
text += "<br><a href='?src=[REF(src)];nuclear=lair'>To shuttle</a>, <a href='?src=[REF(src)];common=undress'>undress</a>, <a href='?src=[REF(src)];nuclear=dressup'>dress up</a>."
|
||||
var/code
|
||||
@@ -713,7 +711,7 @@
|
||||
out += sections[i]+"<br>"
|
||||
|
||||
|
||||
if(((src in SSticker.mode.traitors) || (src in SSticker.mode.syndicates)) && ishuman(current))
|
||||
if(((src in SSticker.mode.traitors) || is_nuclear_operative(current)) && ishuman(current))
|
||||
text = "Uplink: <a href='?src=[REF(src)];common=uplink'>give</a>"
|
||||
var/datum/component/uplink/U = find_syndicate_uplink()
|
||||
if(U)
|
||||
@@ -769,17 +767,44 @@
|
||||
var/objective_pos
|
||||
var/def_value
|
||||
|
||||
|
||||
|
||||
var/datum/antagonist/target_antag
|
||||
|
||||
if (href_list["obj_edit"])
|
||||
objective = locate(href_list["obj_edit"])
|
||||
if (!objective)
|
||||
return
|
||||
objective_pos = objectives.Find(objective)
|
||||
|
||||
for(var/datum/antagonist/A in antag_datums)
|
||||
if(objective in A.objectives)
|
||||
target_antag = A
|
||||
objective_pos = A.objectives.Find(objective)
|
||||
break
|
||||
|
||||
if(!target_antag) //Shouldn't happen
|
||||
stack_trace("objective without antagonist found")
|
||||
objective_pos = objectives.Find(objective)
|
||||
|
||||
//Text strings are easy to manipulate. Revised for simplicity.
|
||||
var/temp_obj_type = "[objective.type]"//Convert path into a text string.
|
||||
def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword.
|
||||
if(!def_value)//If it's a custom objective, it will be an empty string.
|
||||
def_value = "custom"
|
||||
else
|
||||
switch(antag_datums.len)
|
||||
if(0)
|
||||
target_antag = add_antag_datum(/datum/antagonist/custom)
|
||||
if(1)
|
||||
target_antag = antag_datums[1]
|
||||
else
|
||||
var/datum/antagonist/target = input("Which antagonist gets the objective:", "Antagonist", def_value) as null|anything in antag_datums + "(new custom antag)"
|
||||
if (QDELETED(target))
|
||||
return
|
||||
else if(target == "(new custom antag)")
|
||||
target_antag = add_antag_datum(/datum/antagonist/custom)
|
||||
else
|
||||
target_antag = target
|
||||
|
||||
var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "late-assassinate", "maroon", "debrain", "protect", "destroy", "prevent", "hijack", "escape", "survive", "martyr", "steal", "download", "nuclear", "capture", "absorb", "custom")
|
||||
if (!new_obj_type)
|
||||
@@ -901,11 +926,15 @@
|
||||
return
|
||||
|
||||
if (objective)
|
||||
if(target_antag)
|
||||
target_antag.objectives -= objective
|
||||
objectives -= objective
|
||||
objectives.Insert(objective_pos, new_objective)
|
||||
target_antag.objectives.Insert(objective_pos, new_objective)
|
||||
message_admins("[key_name_admin(usr)] edited [current]'s objective to [new_objective.explanation_text]")
|
||||
log_admin("[key_name(usr)] edited [current]'s objective to [new_objective.explanation_text]")
|
||||
else
|
||||
if(target_antag)
|
||||
target_antag.objectives += new_objective
|
||||
objectives += new_objective
|
||||
message_admins("[key_name_admin(usr)] added a new objective for [current]: [new_objective.explanation_text]")
|
||||
log_admin("[key_name(usr)] added a new objective for [current]: [new_objective.explanation_text]")
|
||||
@@ -914,6 +943,11 @@
|
||||
var/datum/objective/objective = locate(href_list["obj_delete"])
|
||||
if(!istype(objective))
|
||||
return
|
||||
|
||||
for(var/datum/antagonist/A in antag_datums)
|
||||
if(objective in A.objectives)
|
||||
A.objectives -= objective
|
||||
break
|
||||
objectives -= objective
|
||||
message_admins("[key_name_admin(usr)] removed an objective for [current]: [objective.explanation_text]")
|
||||
log_admin("[key_name(usr)] removed an objective for [current]: [objective.explanation_text]")
|
||||
@@ -996,11 +1030,13 @@
|
||||
message_admins("[key_name_admin(usr)] has cult'ed [current].")
|
||||
log_admin("[key_name(usr)] has cult'ed [current].")
|
||||
if("tome")
|
||||
if (!SSticker.mode.equip_cultist(current,1))
|
||||
var/datum/antagonist/cult/C = has_antag_datum(/datum/antagonist/cult,TRUE)
|
||||
if (C.equip_cultist(current,1))
|
||||
to_chat(usr, "<span class='danger'>Spawning tome failed!</span>")
|
||||
|
||||
if("amulet")
|
||||
if (!SSticker.mode.equip_cultist(current))
|
||||
var/datum/antagonist/cult/C = has_antag_datum(/datum/antagonist/cult,TRUE)
|
||||
if (C.equip_cultist(current))
|
||||
to_chat(usr, "<span class='danger'>Spawning amulet failed!</span>")
|
||||
|
||||
else if(href_list["clockcult"])
|
||||
@@ -1071,36 +1107,14 @@
|
||||
message_admins("[key_name_admin(usr)] has de-nuke op'ed [current].")
|
||||
log_admin("[key_name(usr)] has de-nuke op'ed [current].")
|
||||
if("nuclear")
|
||||
if(!(src in SSticker.mode.syndicates))
|
||||
SSticker.mode.syndicates += src
|
||||
SSticker.mode.update_synd_icons_added(src)
|
||||
if (SSticker.mode.syndicates.len==1)
|
||||
SSticker.mode.prepare_syndicate_leader(src)
|
||||
else
|
||||
current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]"
|
||||
if(!has_antag_datum(/datum/antagonist/nukeop,TRUE))
|
||||
add_antag_datum(/datum/antagonist/nukeop)
|
||||
special_role = "Syndicate"
|
||||
assigned_role = "Syndicate"
|
||||
to_chat(current, "<span class='notice'>You are a [syndicate_name()] agent!</span>")
|
||||
SSticker.mode.forge_syndicate_objectives(src)
|
||||
SSticker.mode.greet_syndicate(src)
|
||||
message_admins("[key_name_admin(usr)] has nuke op'ed [current].")
|
||||
log_admin("[key_name(usr)] has nuke op'ed [current].")
|
||||
if("lair")
|
||||
current.forceMove(pick(GLOB.nukeop_start))
|
||||
if("dressup")
|
||||
var/mob/living/carbon/human/H = current
|
||||
qdel(H.belt)
|
||||
qdel(H.back)
|
||||
qdel(H.ears)
|
||||
qdel(H.gloves)
|
||||
qdel(H.head)
|
||||
qdel(H.shoes)
|
||||
qdel(H.wear_id)
|
||||
qdel(H.wear_suit)
|
||||
qdel(H.w_uniform)
|
||||
|
||||
if (!SSticker.mode.equip_syndicate(current))
|
||||
to_chat(usr, "<span class='danger'>Equipping a syndicate failed!</span>")
|
||||
if("tellcode")
|
||||
var/code
|
||||
for (var/obj/machinery/nuclearbomb/bombue in GLOB.machines)
|
||||
@@ -1348,50 +1362,6 @@
|
||||
T.should_specialise = TRUE
|
||||
add_antag_datum(T)
|
||||
|
||||
|
||||
/datum/mind/proc/make_Nuke(turf/spawnloc, nuke_code, leader=0, telecrystals = TRUE)
|
||||
if(!(src in SSticker.mode.syndicates))
|
||||
SSticker.mode.syndicates += src
|
||||
SSticker.mode.update_synd_icons_added(src)
|
||||
assigned_role = "Syndicate"
|
||||
special_role = "Syndicate"
|
||||
SSticker.mode.forge_syndicate_objectives(src)
|
||||
SSticker.mode.greet_syndicate(src)
|
||||
current.faction |= "syndicate"
|
||||
|
||||
if(spawnloc)
|
||||
current.forceMove(spawnloc)
|
||||
|
||||
if(ishuman(current))
|
||||
var/mob/living/carbon/human/H = current
|
||||
qdel(H.belt)
|
||||
qdel(H.back)
|
||||
qdel(H.ears)
|
||||
qdel(H.gloves)
|
||||
qdel(H.head)
|
||||
qdel(H.shoes)
|
||||
qdel(H.wear_id)
|
||||
qdel(H.wear_suit)
|
||||
qdel(H.w_uniform)
|
||||
|
||||
SSticker.mode.equip_syndicate(current, telecrystals)
|
||||
|
||||
if (nuke_code)
|
||||
store_memory("<B>Syndicate Nuclear Bomb Code</B>: [nuke_code]", 0, 0)
|
||||
to_chat(current, "The nuclear authorization code is: <B>[nuke_code]</B>")
|
||||
else
|
||||
var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in GLOB.nuke_list
|
||||
if(nuke)
|
||||
store_memory("<B>Syndicate Nuclear Bomb Code</B>: [nuke.r_code]", 0, 0)
|
||||
to_chat(current, "The nuclear authorization code is: <B>nuke.r_code</B>")
|
||||
else
|
||||
to_chat(current, "You were not provided with a nuclear code. Trying asking your team leader or contacting syndicate command.</B>")
|
||||
|
||||
if (leader)
|
||||
SSticker.mode.prepare_syndicate_leader(src,nuke_code)
|
||||
else
|
||||
current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]"
|
||||
|
||||
/datum/mind/proc/make_Changling()
|
||||
var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling)
|
||||
if(!C)
|
||||
@@ -1407,16 +1377,11 @@
|
||||
|
||||
|
||||
/datum/mind/proc/make_Cultist()
|
||||
if(!(src in SSticker.mode.cult))
|
||||
SSticker.mode.add_cultist(src,FALSE)
|
||||
if(!has_antag_datum(/datum/antagonist/cult,TRUE))
|
||||
SSticker.mode.add_cultist(src,FALSE,equip=TRUE)
|
||||
special_role = "Cultist"
|
||||
to_chat(current, "<font color=\"purple\"><b><i>You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy your world is, you see that it should be open to the knowledge of Nar-Sie.</b></i></font>")
|
||||
to_chat(current, "<font color=\"purple\"><b><i>Assist your new bretheren in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.</b></i></font>")
|
||||
var/datum/antagonist/cult/C
|
||||
C.cult_memorization(src)
|
||||
var/mob/living/carbon/human/H = current
|
||||
if (!SSticker.mode.equip_cultist(current))
|
||||
to_chat(H, "Spawning an amulet from your Master failed.")
|
||||
|
||||
/datum/mind/proc/make_Rev()
|
||||
var/datum/antagonist/rev/head/head = new(src)
|
||||
|
||||
@@ -85,12 +85,12 @@
|
||||
/datum/mutation/human/clumsy/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..())
|
||||
return
|
||||
owner.disabilities |= CLUMSY
|
||||
owner.add_disability(DISABILITY_CLUMSY, GENETIC_MUTATION)
|
||||
|
||||
/datum/mutation/human/clumsy/on_losing(mob/living/carbon/human/owner)
|
||||
if(..())
|
||||
return
|
||||
owner.disabilities &= ~CLUMSY
|
||||
owner.remove_disability(DISABILITY_CLUMSY, GENETIC_MUTATION)
|
||||
|
||||
|
||||
//Tourettes causes you to randomly stand in place and shout.
|
||||
@@ -124,12 +124,12 @@
|
||||
/datum/mutation/human/deaf/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..())
|
||||
return
|
||||
owner.disabilities |= DEAF
|
||||
owner.add_disability(DISABILITY_DEAF, GENETIC_MUTATION)
|
||||
|
||||
/datum/mutation/human/deaf/on_losing(mob/living/carbon/human/owner)
|
||||
if(..())
|
||||
return
|
||||
owner.disabilities &= ~DEAF
|
||||
owner.remove_disability(DISABILITY_DEAF, GENETIC_MUTATION)
|
||||
|
||||
|
||||
//Monified turns you into a monkey.
|
||||
|
||||
@@ -30,12 +30,12 @@
|
||||
/datum/mutation/human/mute/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..())
|
||||
return
|
||||
owner.disabilities |= MUTE
|
||||
owner.add_disability(DISABILITY_MUTE, GENETIC_MUTATION)
|
||||
|
||||
/datum/mutation/human/mute/on_losing(mob/living/carbon/human/owner)
|
||||
if(..())
|
||||
return
|
||||
owner.disabilities &= ~MUTE
|
||||
owner.remove_disability(DISABILITY_MUTE, GENETIC_MUTATION)
|
||||
|
||||
|
||||
/datum/mutation/human/smile
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
//Designed for things that need precision trajectories like projectiles.
|
||||
//Don't use this for anything that you don't absolutely have to use this with (like projectiles!) because it isn't worth using a datum unless you need accuracy down to decimal places in pixels.
|
||||
|
||||
#define RETURN_PRECISE_POSITION(A) new /datum/position(A)
|
||||
#define RETURN_PRECISE_POINT(A) new /datum/point(A)
|
||||
|
||||
/datum/position //For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess.
|
||||
var/x = 0
|
||||
var/y = 0
|
||||
var/z = 0
|
||||
var/pixel_x = 0
|
||||
var/pixel_y = 0
|
||||
|
||||
/datum/position/proc/valid()
|
||||
return x && y && z && !isnull(pixel_x) && !isnull(pixel_y)
|
||||
|
||||
/datum/position/New(_x = 0, _y = 0, _z = 0, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/point.
|
||||
if(istype(_x, /datum/point))
|
||||
var/datum/point/P = _x
|
||||
var/turf/T = P.return_turf()
|
||||
_x = T.x
|
||||
_y = T.y
|
||||
_z = T.z
|
||||
_pixel_x = P.return_px()
|
||||
_pixel_y = P.return_py()
|
||||
else if(isatom(_x))
|
||||
var/atom/A = _x
|
||||
_x = A.x
|
||||
_y = A.y
|
||||
_z = A.z
|
||||
_pixel_x = A.pixel_x
|
||||
_pixel_y = A.pixel_y
|
||||
x = _x
|
||||
y = _y
|
||||
z = _z
|
||||
pixel_x = _pixel_x
|
||||
pixel_y = _pixel_y
|
||||
|
||||
/datum/position/proc/return_turf()
|
||||
return locate(x, y, z)
|
||||
|
||||
/datum/position/proc/return_px()
|
||||
return pixel_x
|
||||
|
||||
/datum/position/proc/return_py()
|
||||
return pixel_y
|
||||
|
||||
/datum/position/proc/return_point()
|
||||
return new /datum/point(src)
|
||||
|
||||
/proc/point_midpoint_points(datum/point/a, datum/point/b) //Obviously will not support multiZ calculations! Same for the two below.
|
||||
var/datum/point/P = new
|
||||
P.x = round(a.x + (b.x - a.x) / 2, 1)
|
||||
P.y = round(a.y + (b.y - a.y) / 2, 1)
|
||||
P.z = a.z
|
||||
return P
|
||||
|
||||
/proc/pixel_length_between_points(datum/point/a, datum/point/b)
|
||||
return sqrt(((b.x - a.x) ** 2) + ((b.y - a.y) ** 2))
|
||||
|
||||
/proc/angle_between_points(datum/point/a, datum/point/b)
|
||||
return ATAN2((b.y - a.y), (b.x - a.x))
|
||||
|
||||
/datum/point //A precise point on the map in absolute pixel locations based on world.icon_size. Pixels are FROM THE EDGE OF THE MAP!
|
||||
var/x = 0
|
||||
var/y = 0
|
||||
var/z = 0
|
||||
|
||||
/datum/point/proc/valid()
|
||||
return x && y && z
|
||||
|
||||
/datum/point/proc/copy_to(datum/point/p = new)
|
||||
p.x = x
|
||||
p.y = y
|
||||
p.z = z
|
||||
return p
|
||||
|
||||
/datum/point/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/position or /atom.
|
||||
if(istype(_x, /datum/position))
|
||||
var/datum/position/P = _x
|
||||
_x = P.x
|
||||
_y = P.y
|
||||
_z = P.z
|
||||
_pixel_x = P.pixel_x
|
||||
_pixel_y = P.pixel_y
|
||||
else if(istype(_x, /atom))
|
||||
var/atom/A = _x
|
||||
_x = A.x
|
||||
_y = A.y
|
||||
_z = A.z
|
||||
_pixel_x = A.pixel_x
|
||||
_pixel_y = A.pixel_y
|
||||
initialize_location(_x, _y, _z, _pixel_x, _pixel_y)
|
||||
|
||||
/datum/point/proc/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
|
||||
if(!isnull(tile_x))
|
||||
x = ((tile_x - 1) * world.icon_size) + world.icon_size / 2 + p_x
|
||||
if(!isnull(tile_y))
|
||||
y = ((tile_y - 1) * world.icon_size) + world.icon_size / 2+ p_y
|
||||
if(!isnull(tile_z))
|
||||
z = tile_z
|
||||
|
||||
/datum/point/proc/return_turf()
|
||||
return locate(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z)
|
||||
|
||||
/datum/point/proc/return_coordinates() //[turf_x, turf_y, z]
|
||||
return list(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z)
|
||||
|
||||
/datum/point/proc/return_position()
|
||||
return new /datum/position(src)
|
||||
|
||||
/datum/point/proc/return_px()
|
||||
return MODULUS(x, world.icon_size) - 16
|
||||
|
||||
/datum/point/proc/return_py()
|
||||
return MODULUS(y, world.icon_size) - 16
|
||||
|
||||
/datum/point/proc/mapcheck()
|
||||
. = FALSE
|
||||
var/maxx = world.icon_size * world.maxx
|
||||
var/maxy = world.icon_size * world.maxy
|
||||
var/move_zx = 0
|
||||
var/move_zy = 0
|
||||
if(x < 0)
|
||||
x += maxx
|
||||
move_zx -= 1
|
||||
if(y < 0)
|
||||
y += maxy
|
||||
move_zy -= 1
|
||||
if(x > maxx)
|
||||
x -= maxx
|
||||
move_zx += 1
|
||||
if(y > maxy)
|
||||
y -= maxy
|
||||
move_zy += 1
|
||||
var/datum/space_level/S = GLOB.z_levels_list["[z]"]
|
||||
if(move_zx != 0)
|
||||
var/datum/space_level/L = S.neigbours["[move_zx < 0? WEST : EAST]"]
|
||||
z = L.z_value
|
||||
. = TRUE
|
||||
if(move_zy != 0)
|
||||
var/datum/space_level/L = S.neigbours["[move_zy < 0? SOUTH : NORTH]"]
|
||||
z = L.z_value
|
||||
. = TRUE
|
||||
|
||||
/datum/point/vector
|
||||
var/speed = 32 //pixels per iteration
|
||||
var/iteration = 0
|
||||
var/angle = 0
|
||||
var/mpx = 0 //calculated x/y movement amounts to prevent having to do trig every step.
|
||||
var/mpy = 0
|
||||
var/starting_x = 0 //just like before, pixels from EDGE of map! This is set in initialize_location().
|
||||
var/starting_y = 0
|
||||
var/starting_z = 0
|
||||
|
||||
/datum/point/vector/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0, _angle, _speed)
|
||||
..()
|
||||
initialize_trajectory(_speed, _angle)
|
||||
|
||||
/datum/point/vector/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
|
||||
. = ..()
|
||||
starting_x = x
|
||||
starting_y = y
|
||||
starting_z = z
|
||||
|
||||
/datum/point/vector/copy_to(datum/point/vector/v = new)
|
||||
..(v)
|
||||
v.speed = speed
|
||||
v.iteration = iteration
|
||||
v.angle = angle
|
||||
v.mpx = mpx
|
||||
v.mpy = mpy
|
||||
v.starting_x = starting_x
|
||||
v.starting_y = starting_y
|
||||
v.starting_z = starting_z
|
||||
return v
|
||||
|
||||
/datum/point/vector/proc/initialize_trajectory(pixel_speed, new_angle)
|
||||
if(!isnull(pixel_speed))
|
||||
speed = pixel_speed
|
||||
set_angle(new_angle)
|
||||
|
||||
/datum/point/vector/proc/set_angle(new_angle) //calculations use "byond angle" where north is 0 instead of 90, and south is 180 instead of 270.
|
||||
if(isnull(angle))
|
||||
return
|
||||
angle = new_angle
|
||||
update_offsets()
|
||||
|
||||
/datum/point/vector/proc/update_offsets()
|
||||
mpx = sin(angle) * speed
|
||||
mpy = cos(angle) * speed
|
||||
|
||||
/datum/point/vector/proc/set_speed(new_speed)
|
||||
if(isnull(new_speed) || speed == new_speed)
|
||||
return
|
||||
speed = new_speed
|
||||
update_offsets()
|
||||
|
||||
/datum/point/vector/proc/increment(multiplier = 1)
|
||||
iteration++
|
||||
x += mpx * 1
|
||||
y += mpy * 1
|
||||
if(mapcheck())
|
||||
on_z_change()
|
||||
|
||||
/datum/point/vector/proc/return_vector_after_increments(amount = 7, multiplier = 1, force_simulate = FALSE)
|
||||
var/datum/point/vector/v = copy_to()
|
||||
if(force_simulate)
|
||||
for(var/i in 1 to amount)
|
||||
v.increment(multiplier)
|
||||
else
|
||||
v.increment(multiplier * amount)
|
||||
return v
|
||||
|
||||
/datum/point/vector/proc/on_z_change()
|
||||
return
|
||||
|
||||
/datum/point/vector/processed //pixel_speed is per decisecond.
|
||||
var/last_process = 0
|
||||
var/last_move = 0
|
||||
var/paused = FALSE
|
||||
|
||||
/datum/point/vector/processed/Destroy()
|
||||
STOP_PROCESSING(SSprojectiles, src)
|
||||
|
||||
/datum/point/vector/processed/proc/start()
|
||||
last_process = world.time
|
||||
last_move = world.time
|
||||
START_PROCESSING(SSprojectiles, src)
|
||||
|
||||
/datum/point/vector/processed/process()
|
||||
if(paused)
|
||||
last_move += world.time - last_process
|
||||
last_process = world.time
|
||||
return
|
||||
var/needed_time = world.time - last_move
|
||||
last_process = world.time
|
||||
last_move = world.time
|
||||
increment(needed_time)
|
||||
@@ -38,7 +38,7 @@
|
||||
if (user.client)
|
||||
user.client.images += bar
|
||||
|
||||
progress = Clamp(progress, 0, goal)
|
||||
progress = CLAMP(progress, 0, goal)
|
||||
bar.icon_state = "prog_bar_[round(((progress / goal) * 100), 5)]"
|
||||
if (!shown)
|
||||
user.client.images += bar
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
var/strength
|
||||
if(steps>1)
|
||||
strength = InverseSquareLaw(intensity, max(range_modifier*steps, 1), 1)
|
||||
strength = INVERSE_SQUARE(intensity, max(range_modifier*steps, 1), 1)
|
||||
else
|
||||
strength = intensity
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
radiate(atoms, Floor(strength))
|
||||
radiate(atoms, FLOOR(strength, 1))
|
||||
|
||||
check_obstructions(atoms) // reduce our overall strength if there are radiation insulators
|
||||
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
if(istype(L)) //this is probably more safety than actually needed
|
||||
var/vanguard = L.stun_absorption["vanguard"]
|
||||
desc = initial(desc)
|
||||
desc += "<br><b>[Floor(vanguard["stuns_absorbed"] * 0.1)]</b> seconds of stuns held back.\
|
||||
[GLOB.ratvar_awakens ? "":"<br><b>[Floor(min(vanguard["stuns_absorbed"] * 0.025, 20))]</b> seconds of stun will affect you."]"
|
||||
desc += "<br><b>[FLOOR(vanguard["stuns_absorbed"] * 0.1, 1)]</b> seconds of stuns held back.\
|
||||
[GLOB.ratvar_awakens ? "":"<br><b>[FLOOR(min(vanguard["stuns_absorbed"] * 0.025, 20), 1)]</b> seconds of stun will affect you."]"
|
||||
..()
|
||||
|
||||
/datum/status_effect/vanguard_shield/Destroy()
|
||||
@@ -87,7 +87,7 @@
|
||||
var/vanguard = owner.stun_absorption["vanguard"]
|
||||
var/stuns_blocked = 0
|
||||
if(vanguard)
|
||||
stuns_blocked = Floor(min(vanguard["stuns_absorbed"] * 0.25, 400))
|
||||
stuns_blocked = FLOOR(min(vanguard["stuns_absorbed"] * 0.25, 400), 1)
|
||||
vanguard["end_time"] = 0 //so it doesn't absorb the stuns we're about to apply
|
||||
if(owner.stat != DEAD)
|
||||
var/message_to_owner = "<span class='warning'>You feel your Vanguard quietly fade...</span>"
|
||||
|
||||
@@ -230,7 +230,7 @@
|
||||
if(prob(severity * 0.15))
|
||||
to_chat(owner, "<span class='sevtug[span_part]'>\"[text2ratvar(pick(mania_messages))]\"</span>")
|
||||
owner.playsound_local(get_turf(motor), hum, severity, 1)
|
||||
owner.adjust_drugginess(Clamp(max(severity * 0.075, 1), 0, max(0, 50 - owner.druggy))) //7.5% of severity per second, minimum 1
|
||||
owner.adjust_drugginess(CLAMP(max(severity * 0.075, 1), 0, max(0, 50 - owner.druggy))) //7.5% of severity per second, minimum 1
|
||||
if(owner.hallucination < 50)
|
||||
owner.hallucination = min(owner.hallucination + max(severity * 0.075, 1), 50) //7.5% of severity per second, minimum 1
|
||||
if(owner.dizziness < 50)
|
||||
@@ -310,7 +310,7 @@
|
||||
var/icon/I = icon(owner.icon, owner.icon_state, owner.dir)
|
||||
var/icon_height = I.Height()
|
||||
bleed_overlay.pixel_x = -owner.pixel_x
|
||||
bleed_overlay.pixel_y = Floor(icon_height * 0.25)
|
||||
bleed_overlay.pixel_y = FLOOR(icon_height * 0.25, 1)
|
||||
bleed_overlay.transform = matrix() * (icon_height/world.icon_size) //scale the bleed overlay's size based on the target's icon size
|
||||
bleed_underlay.pixel_x = -owner.pixel_x
|
||||
bleed_underlay.transform = matrix() * (icon_height/world.icon_size) * 3
|
||||
@@ -479,3 +479,27 @@
|
||||
desc = "Blinding light dances in your vision, stunning and silencing you. <i>Any damage taken will shorten the light's effects!</i>"
|
||||
icon_state = "kindle"
|
||||
alerttooltipstyle = "clockcult"
|
||||
|
||||
|
||||
//Ichorial Stain: Applied to servants revived by a vitality matrix. Prevents them from being revived by one again until the effect fades.
|
||||
/datum/status_effect/ichorial_stain
|
||||
id = "ichorial_stain"
|
||||
status_type = STATUS_EFFECT_UNIQUE
|
||||
duration = 600
|
||||
examine_text = "<span class='warning'>SUBJECTPRONOUN is drenched in thick, blue ichor!</span>"
|
||||
alert_type = /obj/screen/alert/status_effect/ichorial_stain
|
||||
|
||||
/datum/status_effect/ichorial_stain/on_apply()
|
||||
owner.visible_message("<span class='danger'>[owner] gets back up, [owner.p_their()] body dripping blue ichor!</span>", \
|
||||
"<span class='userdanger'>Thick blue ichor covers your body; you can't be revived like this again until it dries!</span>")
|
||||
return TRUE
|
||||
|
||||
/datum/status_effect/ichorial_stain/on_remove()
|
||||
owner.visible_message("<span class='danger'>The blue ichor on [owner]'s body dries out!</span>", \
|
||||
"<span class='boldnotice'>The ichor on your body is dry - you can now be revived by vitality matrices again!</span>")
|
||||
|
||||
/obj/screen/alert/status_effect/ichorial_stain
|
||||
name = "Ichorial Stain"
|
||||
desc = "Your body is covered in blue ichor! You can't be revived by vitality matrices."
|
||||
icon_state = "ichorial_stain"
|
||||
alerttooltipstyle = "clockcult"
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
var/mob/living/owner //The mob affected by the status effect.
|
||||
var/status_type = STATUS_EFFECT_UNIQUE //How many of the effect can be on one mob, and what happens when you try to add another
|
||||
var/on_remove_on_mob_delete = FALSE //if we call on_remove() when the mob is deleted
|
||||
var/examine_text //If defined, this text will appear when the mob is examined - to use he, she etc. use "SUBJECTPRONOUN" and replace it in the examines themselves
|
||||
var/alert_type = /obj/screen/alert/status_effect //the alert thrown by the status effect, contains name and description
|
||||
var/obj/screen/alert/status_effect/linked_alert = null //the alert itself, if it exists
|
||||
|
||||
|
||||
Reference in New Issue
Block a user