Merge remote-tracking branch 'upstream/master' into haha
This commit is contained in:
@@ -14,6 +14,8 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
var/list/objectives = list()
|
||||
var/antag_memory = ""//These will be removed with antag datum
|
||||
var/antag_moodlet //typepath of moodlet that the mob will gain with their status
|
||||
var/antag_hud_type
|
||||
var/antag_hud_name
|
||||
/// If above 0, this is the multiplier for the speed at which we hijack the shuttle. Do not directly read, use hijack_speed().
|
||||
var/hijack_speed = 0
|
||||
|
||||
@@ -77,6 +79,17 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
hud.leave_hud(mob_override)
|
||||
set_antag_hud(mob_override, null)
|
||||
|
||||
// Handles adding and removing the clumsy mutation from clown antags. Gets called in apply/remove_innate_effects
|
||||
/datum/antagonist/proc/handle_clown_mutation(mob/living/mob_override, message, removing = TRUE)
|
||||
var/mob/living/carbon/human/H = mob_override
|
||||
if(H && istype(H) && owner.assigned_role == "Clown")
|
||||
if(removing) // They're a clown becoming an antag, remove clumsy
|
||||
H.dna.remove_mutation(CLOWNMUT)
|
||||
if(!silent && message)
|
||||
to_chat(H, "<span class='boldnotice'>[message]</span>")
|
||||
else
|
||||
H.dna.add_mutation(CLOWNMUT) // We're removing their antag status, add back clumsy
|
||||
|
||||
//Assign default team and creates one for one of a kind team antagonists
|
||||
/datum/antagonist/proc/create_team(datum/team/team)
|
||||
return
|
||||
|
||||
@@ -37,14 +37,14 @@ GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear))
|
||||
description = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras."
|
||||
id = "science_tool"
|
||||
build_path = /obj/item/abductor/gizmo
|
||||
/*
|
||||
|
||||
/datum/abductor_gear/advanced_baton
|
||||
name = "Advanced Baton"
|
||||
description = "A quad-mode baton used for incapacitation and restraining of specimens."
|
||||
id = "advanced_baton"
|
||||
cost = 2
|
||||
build_path = /obj/item/melee/baton/abductor //does not exist?
|
||||
*/
|
||||
build_path = /obj/item/abductor/baton //does not exist?
|
||||
|
||||
/datum/abductor_gear/superlingual_matrix
|
||||
name = "Superlingual Matrix"
|
||||
description = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something."
|
||||
|
||||
@@ -30,6 +30,22 @@
|
||||
/// Possible gear to be dispensed
|
||||
var/list/possible_gear
|
||||
|
||||
/obj/machinery/abductor/console/Initialize(mapload)
|
||||
. = ..()
|
||||
possible_gear = get_abductor_gear()
|
||||
|
||||
/**
|
||||
* get_abductor_gear: Returns a list of a filtered abductor gear sorted by categories
|
||||
*/
|
||||
/obj/machinery/abductor/console/proc/get_abductor_gear()
|
||||
var/list/filtered_modules = list()
|
||||
for(var/path in GLOB.abductor_gear)
|
||||
var/datum/abductor_gear/AG = new path
|
||||
if(!filtered_modules[AG.category])
|
||||
filtered_modules[AG.category] = list()
|
||||
filtered_modules[AG.category][AG] = AG
|
||||
return filtered_modules
|
||||
|
||||
/obj/machinery/abductor/console/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
if(!HAS_TRAIT(user, TRAIT_ABDUCTOR_TRAINING) && !HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_TRAINING))
|
||||
to_chat(user, "<span class='warning'>You start mashing alien buttons at random!</span>")
|
||||
|
||||
@@ -45,11 +45,8 @@
|
||||
desc = "A solid wall of slightly twitching tendrils with a reflective glow."
|
||||
damaged_desc = "A wall of twitching tendrils with a reflective glow."
|
||||
icon_state = "blob_glow"
|
||||
flags_ricochet = RICOCHET_SHINY
|
||||
point_return = 8
|
||||
max_integrity = 100
|
||||
brute_resist = 1
|
||||
explosion_block = 2
|
||||
|
||||
/obj/structure/blob/shield/reflective/check_projectile_ricochet(obj/item/projectile/P)
|
||||
return PROJECTILE_RICOCHET_FORCE
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
if(istype(owner.current.loc, /obj/structure/closet/crate/coffin))
|
||||
Torpor_Begin()
|
||||
// Wait before next pass
|
||||
FreeAllVassals() // Free my Vassals! (if I haven't yet)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -308,6 +307,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// HUMAN FOOD
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
ADD_TRAIT(user, TRAIT_PIERCEIMMUNE, "fortitude")
|
||||
ADD_TRAIT(user, TRAIT_NODISMEMBER, "fortitude")
|
||||
ADD_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude")
|
||||
ADD_TRAIT(user, TRAIT_NORUNNING, "fortitude")
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
fortitude_resist = max(0.3, 0.7 - level_current * 0.1)
|
||||
@@ -53,6 +54,7 @@
|
||||
REMOVE_TRAIT(user, TRAIT_PIERCEIMMUNE, "fortitude")
|
||||
REMOVE_TRAIT(user, TRAIT_NODISMEMBER, "fortitude")
|
||||
REMOVE_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude")
|
||||
REMOVE_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude")
|
||||
if(!ishuman(owner))
|
||||
return
|
||||
var/mob/living/carbon/human/H = owner
|
||||
|
||||
@@ -89,17 +89,17 @@
|
||||
creation_message = "<span class='alloy bold'>The cogscarab clicks and whirrs as it hops up and springs to life!</span>"
|
||||
construct_type = /mob/living/simple_animal/drone/cogscarab
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
var/infinite_resources = TRUE
|
||||
var/infinite_resources = FALSE //No.
|
||||
var/static/obj/item/seasonal_hat //Share it with all other scarabs, since we're from the same cult!
|
||||
|
||||
/obj/item/clockwork/construct_chassis/cogscarab/Initialize()
|
||||
. = ..()
|
||||
if(GLOB.servants_active)
|
||||
infinite_resources = FALSE //For any that are somehow spawned in late
|
||||
infinite_resources = FALSE //This check is relatively irrelevant until *someone* makes the infinite resources var default to true again, so, leaving it in.
|
||||
|
||||
/obj/item/clockwork/construct_chassis/cogscarab/pre_spawn()
|
||||
if(infinite_resources)
|
||||
//During rounds where they can't interact with the station, let them experiment with builds
|
||||
//During rounds where they can't interact with the station, let them experiment with builds, if an admin allows them to.
|
||||
construct_type = /mob/living/simple_animal/drone/cogscarab/ratvar
|
||||
if(!seasonal_hat)
|
||||
var/obj/item/drone_shell/D = locate() in GLOB.poi_list
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
desc = "Charges your slab with divine energy, allowing you to overwhelm a target with Ratvar's light."
|
||||
invocations = list("Divinity, show them your light!")
|
||||
whispered = TRUE
|
||||
channel_time = 15 // I think making kindle channel a third of the time less is a good make up for the fact that it silences people for such a little amount of time.
|
||||
channel_time = 25 //2.5 seconds should be a okay compromise between being able to use it when needed, and not being able to just pause in combat for a second and hardstunning your enemy
|
||||
power_cost = 125
|
||||
usage_tip = "The light can be used from up to two tiles away. Damage taken will GREATLY REDUCE the stun's duration."
|
||||
tier = SCRIPTURE_DRIVER
|
||||
@@ -137,6 +137,30 @@
|
||||
quickbind_desc = "Applies handcuffs to a struck target."
|
||||
|
||||
|
||||
//Belligerent: Channeled for up to fifteen times over thirty seconds. Forces non-servants that can hear the chant to walk, doing minor damage. Nar-Sian cultists are burned.
|
||||
/datum/clockwork_scripture/channeled/belligerent
|
||||
descname = "Channeled, Area Slowdown"
|
||||
name = "Belligerent"
|
||||
desc = "Forces all nearby non-servants to walk rather than run, doing minor damage. Chanted every two seconds for up to thirty seconds."
|
||||
chant_invocations = list("Punish their blindness!", "Take time, make slow!", "Kneel before The Justiciar!", "Halt their charges!", "Cease the tides!")
|
||||
chant_amount = 15
|
||||
chant_interval = 20
|
||||
channel_time = 20
|
||||
power_cost = 300
|
||||
usage_tip = "Useful for crowd control in a populated area and disrupting mass movement."
|
||||
tier = SCRIPTURE_DRIVER
|
||||
primary_component = BELLIGERENT_EYE
|
||||
sort_priority = 7
|
||||
quickbind = TRUE
|
||||
quickbind_desc = "Forces nearby non-Servants to walk, doing minor damage with each chant.<br><b>Maximum 15 chants.</b>"
|
||||
|
||||
/datum/clockwork_scripture/channeled/belligerent/chant_effects(chant_number)
|
||||
for(var/mob/living/carbon/C in hearers(7, invoker))
|
||||
C.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
|
||||
new /obj/effect/temp_visual/ratvar/belligerent(get_turf(invoker))
|
||||
return TRUE
|
||||
|
||||
|
||||
//Vanguard: Provides twenty seconds of greatly increased stamina regeneration and stun immunity. At the end of the twenty seconds, 25% of all stuns absorbed aswell as 50% of healed stamloss are applied to the invoker.
|
||||
/datum/clockwork_scripture/vanguard
|
||||
descname = "Self Stun Immunity"
|
||||
@@ -149,7 +173,7 @@
|
||||
usage_tip = "You cannot reactivate Vanguard while still shielded by it."
|
||||
tier = SCRIPTURE_DRIVER
|
||||
primary_component = VANGUARD_COGWHEEL
|
||||
sort_priority = 7
|
||||
sort_priority = 8
|
||||
quickbind = TRUE
|
||||
quickbind_desc = "Allows you to temporarily have quickly regenerating stamina and absorb stuns. Part of the stuns absorbed and staminaloss healed will affect you when disabled."
|
||||
|
||||
@@ -181,7 +205,7 @@
|
||||
usage_tip = "The Compromise is very fast to invoke, and will remove holy water from the target Servant."
|
||||
tier = SCRIPTURE_DRIVER
|
||||
primary_component = VANGUARD_COGWHEEL
|
||||
sort_priority = 8
|
||||
sort_priority = 9
|
||||
quickbind = TRUE
|
||||
quickbind_desc = "Allows you to convert a Servant's brute, burn, and oxygen damage to half toxin damage.<br><b>Click your slab to disable.</b>"
|
||||
slab_overlay = "compromise"
|
||||
@@ -255,7 +279,7 @@
|
||||
tier = SCRIPTURE_DRIVER
|
||||
space_allowed = TRUE
|
||||
primary_component = GEIS_CAPACITOR
|
||||
sort_priority = 10
|
||||
sort_priority = 11
|
||||
important = TRUE
|
||||
quickbind = TRUE
|
||||
quickbind_desc = "Creates a new Clockwork Slab."
|
||||
@@ -276,7 +300,7 @@
|
||||
tier = SCRIPTURE_DRIVER
|
||||
space_allowed = TRUE
|
||||
primary_component = GEIS_CAPACITOR
|
||||
sort_priority = 11
|
||||
sort_priority = 12
|
||||
quickbind = TRUE
|
||||
quickbind_desc = "Creates a pair of Wraith Spectacles, which grant true sight but cause gradual vision loss."
|
||||
|
||||
@@ -295,7 +319,7 @@
|
||||
usage_tip = "This gateway is strictly one-way and will only allow things through the invoker's portal."
|
||||
tier = SCRIPTURE_DRIVER
|
||||
primary_component = GEIS_CAPACITOR
|
||||
sort_priority = 9
|
||||
sort_priority = 10
|
||||
quickbind = TRUE
|
||||
quickbind_desc = "Allows you to create a one-way Spatial Gateway to a living Servant or Clockwork Obelisk."
|
||||
|
||||
|
||||
@@ -459,26 +459,3 @@
|
||||
invoker.light_range = 0
|
||||
invoker.update_light()
|
||||
return ..()
|
||||
|
||||
//Belligerent: Channeled for up to fifteen times over thirty seconds. Forces non-servants that can hear the chant to walk, doing minor damage. Nar-Sian cultists are burned.
|
||||
/datum/clockwork_scripture/channeled/belligerent
|
||||
descname = "Channeled, Area Slowdown"
|
||||
name = "Belligerent"
|
||||
desc = "Forces all nearby non-servants to walk rather than run, doing minor damage. Chanted every two seconds for up to thirty seconds."
|
||||
chant_invocations = list("Punish their blindness!", "Take time, make slow!", "Kneel before The Justiciar!", "Halt their charges!", "Cease the tides!")
|
||||
chant_amount = 15
|
||||
chant_interval = 20
|
||||
channel_time = 20
|
||||
power_cost = 300
|
||||
usage_tip = "Useful for crowd control in a populated area and disrupting mass movement."
|
||||
tier = SCRIPTURE_DRIVER
|
||||
primary_component = BELLIGERENT_EYE
|
||||
sort_priority = 1
|
||||
quickbind = TRUE
|
||||
quickbind_desc = "Forces nearby non-Servants to walk, doing minor damage with each chant.<br><b>Maximum 15 chants.</b>"
|
||||
|
||||
/datum/clockwork_scripture/channeled/belligerent/chant_effects(chant_number)
|
||||
for(var/mob/living/carbon/C in hearers(7, invoker))
|
||||
C.apply_status_effect(STATUS_EFFECT_BELLIGERENT)
|
||||
new /obj/effect/temp_visual/ratvar/belligerent(get_turf(invoker))
|
||||
return TRUE
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
/datum/antagonist/heretic
|
||||
name = "Heretic"
|
||||
roundend_category = "Heretics"
|
||||
antagpanel_category = "Heretic"
|
||||
antag_moodlet = /datum/mood_event/heretics
|
||||
job_rank = ROLE_HERETIC
|
||||
antag_hud_type = ANTAG_HUD_HERETIC
|
||||
antag_hud_name = "heretic"
|
||||
var/give_equipment = TRUE
|
||||
var/list/researched_knowledge = list()
|
||||
var/total_sacrifices = 0
|
||||
var/ascended = FALSE
|
||||
|
||||
/datum/antagonist/heretic/admin_add(datum/mind/new_owner,mob/admin)
|
||||
give_equipment = TRUE
|
||||
new_owner.add_antag_datum(src)
|
||||
message_admins("[key_name_admin(admin)] has heresized [key_name_admin(new_owner)].")
|
||||
log_admin("[key_name(admin)] has heresized [key_name(new_owner)].")
|
||||
|
||||
/datum/antagonist/heretic/greet()
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ecult_op.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change
|
||||
to_chat(owner, "<span class='boldannounce'>You are the Heretic!</span><br>\
|
||||
<B>The old ones gave you these tasks to fulfill:</B>")
|
||||
owner.announce_objectives()
|
||||
to_chat(owner, "<span class='cult'>The book whispers, the forbidden knowledge walks once again!<br>\
|
||||
Your book allows you to research abilities, but be careful, you cannot undo what has been done.<br>\
|
||||
You gain charges by either collecting influences or sacrificing people tracked by the living heart<br> \
|
||||
You can find a basic guide at : https://tgstation13.org/wiki/Heresy_101 </span>")
|
||||
|
||||
/datum/antagonist/heretic/on_gain()
|
||||
var/mob/living/current = owner.current
|
||||
if(ishuman(current))
|
||||
forge_primary_objectives()
|
||||
gain_knowledge(/datum/eldritch_knowledge/spell/basic)
|
||||
gain_knowledge(/datum/eldritch_knowledge/living_heart)
|
||||
gain_knowledge(/datum/eldritch_knowledge/codex_cicatrix)
|
||||
gain_knowledge(/datum/eldritch_knowledge/eldritch_blade)
|
||||
current.log_message("has been converted to the cult of the forgotten ones!", LOG_ATTACK, color="#960000")
|
||||
GLOB.reality_smash_track.AddMind(owner)
|
||||
START_PROCESSING(SSprocessing,src)
|
||||
if(give_equipment)
|
||||
equip_cultist()
|
||||
owner.teach_crafting_recipe(/datum/crafting_recipe/heretic/codex)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/heretic/on_removal()
|
||||
|
||||
for(var/X in researched_knowledge)
|
||||
var/datum/eldritch_knowledge/EK = researched_knowledge[X]
|
||||
EK.on_lose(owner.current)
|
||||
|
||||
if(!silent)
|
||||
to_chat(owner.current, "<span class='userdanger'>Your mind begins to flare as the otherwordly knowledge escapes your grasp!</span>")
|
||||
owner.current.log_message("has renounced the cult of the old ones!", LOG_ATTACK, color="#960000")
|
||||
GLOB.reality_smash_track.RemoveMind(owner)
|
||||
STOP_PROCESSING(SSprocessing,src)
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/antagonist/heretic/proc/equip_cultist()
|
||||
var/mob/living/carbon/H = owner.current
|
||||
if(!istype(H))
|
||||
return
|
||||
. += ecult_give_item(/obj/item/forbidden_book, H)
|
||||
. += ecult_give_item(/obj/item/living_heart, H)
|
||||
|
||||
/datum/antagonist/heretic/proc/ecult_give_item(obj/item/item_path, mob/living/carbon/human/H)
|
||||
var/list/slots = list(
|
||||
"backpack" = SLOT_IN_BACKPACK,
|
||||
"left pocket" = SLOT_L_STORE,
|
||||
"right pocket" = SLOT_R_STORE
|
||||
)
|
||||
|
||||
var/T = new item_path(H)
|
||||
var/item_name = initial(item_path.name)
|
||||
var/where = H.equip_in_one_of_slots(T, slots)
|
||||
if(!where)
|
||||
to_chat(H, "<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 FALSE
|
||||
else
|
||||
to_chat(H, "<span class='danger'>You have a [item_name] in your [where].</span>")
|
||||
if(where == "backpack")
|
||||
SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H)
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/heretic/process()
|
||||
|
||||
for(var/X in researched_knowledge)
|
||||
var/datum/eldritch_knowledge/EK = researched_knowledge[X]
|
||||
EK.on_life(owner.current)
|
||||
|
||||
/datum/antagonist/heretic/proc/forge_primary_objectives()
|
||||
var/list/assasination = list()
|
||||
var/list/protection = list()
|
||||
for(var/i in 1 to 2)
|
||||
var/pck = pick("assasinate","protect")
|
||||
switch(pck)
|
||||
if("assasinate")
|
||||
var/datum/objective/assassinate/A = new
|
||||
A.owner = owner
|
||||
var/list/owners = A.get_owners()
|
||||
A.find_target(owners,protection)
|
||||
assasination += A.target
|
||||
objectives += A
|
||||
if("protect")
|
||||
var/datum/objective/protect/P = new
|
||||
P.owner = owner
|
||||
var/list/owners = P.get_owners()
|
||||
P.find_target(owners,assasination)
|
||||
protection += P.target
|
||||
objectives += P
|
||||
|
||||
|
||||
var/datum/objective/sacrifice_ecult/SE = new
|
||||
SE.owner = owner
|
||||
SE.update_explanation_text()
|
||||
objectives += SE
|
||||
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
objectives += escape_objective
|
||||
|
||||
/datum/antagonist/heretic/apply_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
add_antag_hud(antag_hud_type, antag_hud_name, current)
|
||||
handle_clown_mutation(current, mob_override ? null : "Knowledge described in the book allowed you to overcome your clownish nature, allowing you to use complex items effectively.")
|
||||
current.faction |= "heretics"
|
||||
|
||||
/datum/antagonist/heretic/remove_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
remove_antag_hud(antag_hud_type, current)
|
||||
handle_clown_mutation(current, removing = FALSE)
|
||||
current.faction -= "heretics"
|
||||
|
||||
/datum/antagonist/heretic/get_admin_commands()
|
||||
. = ..()
|
||||
.["Equip"] = CALLBACK(src,.proc/equip_cultist)
|
||||
|
||||
/datum/antagonist/heretic/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
var/cultiewin = TRUE
|
||||
|
||||
parts += printplayer(owner)
|
||||
parts += "<b>Sacrifices Made:</b> [total_sacrifices]"
|
||||
|
||||
if(length(objectives))
|
||||
var/count = 1
|
||||
for(var/o in objectives)
|
||||
var/datum/objective/objective = o
|
||||
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>"
|
||||
cultiewin = FALSE
|
||||
count++
|
||||
if(ascended)
|
||||
parts += "<span class='greentext big'>HERETIC HAS ASCENDED!</span>"
|
||||
else
|
||||
if(cultiewin)
|
||||
parts += "<span class='greentext'>The heretic was successful!</span>"
|
||||
else
|
||||
parts += "<span class='redtext'>The heretic has failed.</span>"
|
||||
|
||||
parts += "<b>Knowledge Researched:</b> "
|
||||
|
||||
var/list/knowledge_message = list()
|
||||
var/list/knowledge = get_all_knowledge()
|
||||
for(var/X in knowledge)
|
||||
var/datum/eldritch_knowledge/EK = knowledge[X]
|
||||
knowledge_message += "[EK.name]"
|
||||
parts += knowledge_message.Join(", ")
|
||||
|
||||
return parts.Join("<br>")
|
||||
////////////////
|
||||
// Knowledge //
|
||||
////////////////
|
||||
|
||||
/datum/antagonist/heretic/proc/gain_knowledge(datum/eldritch_knowledge/EK)
|
||||
if(get_knowledge(EK))
|
||||
return FALSE
|
||||
var/datum/eldritch_knowledge/initialized_knowledge = new EK
|
||||
researched_knowledge[initialized_knowledge.type] = initialized_knowledge
|
||||
initialized_knowledge.on_gain(owner.current)
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/heretic/proc/get_researchable_knowledge()
|
||||
var/list/researchable_knowledge = list()
|
||||
var/list/banned_knowledge = list()
|
||||
for(var/X in researched_knowledge)
|
||||
var/datum/eldritch_knowledge/EK = researched_knowledge[X]
|
||||
researchable_knowledge |= EK.next_knowledge
|
||||
banned_knowledge |= EK.banned_knowledge
|
||||
banned_knowledge |= EK.type
|
||||
researchable_knowledge -= banned_knowledge
|
||||
return researchable_knowledge
|
||||
|
||||
/datum/antagonist/heretic/proc/get_knowledge(wanted)
|
||||
return researched_knowledge[wanted]
|
||||
|
||||
/datum/antagonist/heretic/proc/get_all_knowledge()
|
||||
return researched_knowledge
|
||||
|
||||
////////////////
|
||||
// Objectives //
|
||||
////////////////
|
||||
|
||||
/datum/objective/sacrifice_ecult
|
||||
name = "sacrifice"
|
||||
|
||||
/datum/objective/sacrifice_ecult/update_explanation_text()
|
||||
. = ..()
|
||||
target_amount = rand(2,4)
|
||||
explanation_text = "Sacrifice at least [target_amount] people."
|
||||
|
||||
/datum/objective/sacrifice_ecult/check_completion()
|
||||
if(!owner)
|
||||
return FALSE
|
||||
var/datum/antagonist/heretic/cultie = owner.has_antag_datum(/datum/antagonist/heretic)
|
||||
if(!cultie)
|
||||
return FALSE
|
||||
return cultie.total_sacrifices >= target_amount
|
||||
@@ -0,0 +1,145 @@
|
||||
/obj/item/forbidden_book
|
||||
name = "Codex Cicatrix"
|
||||
desc = "Book describing the secrets of the veil."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "book"
|
||||
item_state = "book"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
///Last person that touched this
|
||||
var/mob/living/last_user
|
||||
///how many charges do we have?
|
||||
var/charge = 0
|
||||
///Where we cannot create the rune?
|
||||
var/static/list/blacklisted_turfs = typecacheof(list(/turf/closed,/turf/open/space,/turf/open/lava))
|
||||
|
||||
/obj/item/forbidden_book/Destroy()
|
||||
last_user = null
|
||||
. = ..()
|
||||
|
||||
|
||||
/obj/item/forbidden_book/examine(mob/user)
|
||||
. = ..()
|
||||
if(!IS_HERETIC(user))
|
||||
return
|
||||
. += "The Tome holds [charge] charges."
|
||||
. += "Use it on the floor to create a transmutation rune, used to perform rituals."
|
||||
. += "Hit an influence in the black part with it to gain a charge."
|
||||
. += "Hit a transmutation rune to destroy it."
|
||||
|
||||
/obj/item/forbidden_book/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(!proximity_flag || !IS_HERETIC(user))
|
||||
return
|
||||
if(istype(target,/obj/effect/eldritch))
|
||||
remove_rune(target,user)
|
||||
if(istype(target,/obj/effect/reality_smash))
|
||||
get_power_from_influence(target,user)
|
||||
if(istype(target,/turf/open))
|
||||
draw_rune(target,user)
|
||||
|
||||
///Gives you a charge and destroys a corresponding influence
|
||||
/obj/item/forbidden_book/proc/get_power_from_influence(atom/target, mob/user)
|
||||
var/obj/effect/reality_smash/RS = target
|
||||
to_chat(target, "<span class='danger'>You start drawing power from influence...</span>")
|
||||
if(do_after(user,10 SECONDS,TRUE,RS))
|
||||
qdel(RS)
|
||||
charge += 1
|
||||
|
||||
///Draws a rune on a selected turf
|
||||
/obj/item/forbidden_book/proc/draw_rune(atom/target,mob/user)
|
||||
|
||||
for(var/turf/T in range(1,target))
|
||||
if(is_type_in_typecache(T, blacklisted_turfs))
|
||||
to_chat(target, "<span class='warning'>The terrain doesn't support runes!</span>")
|
||||
return
|
||||
var/A = get_turf(target)
|
||||
to_chat(user, "<span class='danger'>You start drawing a rune...</span>")
|
||||
|
||||
if(do_after(user,30 SECONDS,FALSE, user))
|
||||
|
||||
new /obj/effect/eldritch/big(A)
|
||||
|
||||
///Removes runes from the selected turf
|
||||
/obj/item/forbidden_book/proc/remove_rune(atom/target,mob/user)
|
||||
|
||||
to_chat(user, "<span class='danger'>You start removing a rune...</span>")
|
||||
if(do_after(user,2 SECONDS,FALSE, user))
|
||||
qdel(target)
|
||||
|
||||
/obj/item/forbidden_book/ui_interact(mob/user, datum/tgui/ui = null)
|
||||
if(!IS_HERETIC(user))
|
||||
return FALSE
|
||||
last_user = user
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
icon_state = "book_open"
|
||||
flick("book_opening", src)
|
||||
ui = new(user, src, "ForbiddenLore", name)
|
||||
ui.open()
|
||||
|
||||
/obj/item/forbidden_book/ui_data(mob/user)
|
||||
var/datum/antagonist/heretic/cultie = user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
var/list/to_know = list()
|
||||
for(var/Y in cultie.get_researchable_knowledge())
|
||||
to_know += new Y
|
||||
var/list/known = cultie.get_all_knowledge()
|
||||
var/list/data = list()
|
||||
var/list/lore = list()
|
||||
|
||||
data["charges"] = charge
|
||||
|
||||
for(var/X in to_know)
|
||||
lore = list()
|
||||
var/datum/eldritch_knowledge/EK = X
|
||||
lore["type"] = EK.type
|
||||
lore["name"] = EK.name
|
||||
lore["cost"] = EK.cost
|
||||
lore["disabled"] = EK.cost <= charge ? FALSE : TRUE
|
||||
lore["path"] = EK.route
|
||||
lore["state"] = "Research"
|
||||
lore["flavour"] = EK.gain_text
|
||||
lore["desc"] = EK.desc
|
||||
data["to_know"] += list(lore)
|
||||
|
||||
for(var/X in known)
|
||||
lore = list()
|
||||
var/datum/eldritch_knowledge/EK = known[X]
|
||||
lore["name"] = EK.name
|
||||
lore["cost"] = EK.cost
|
||||
lore["disabled"] = TRUE
|
||||
lore["path"] = EK.route
|
||||
lore["state"] = "Researched"
|
||||
lore["flavour"] = EK.gain_text
|
||||
lore["desc"] = EK.desc
|
||||
data["to_know"] += list(lore)
|
||||
|
||||
if(!length(data["to_know"]))
|
||||
data["to_know"] = null
|
||||
|
||||
return data
|
||||
|
||||
/obj/item/forbidden_book/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
switch(action)
|
||||
if("research")
|
||||
var/datum/antagonist/heretic/cultie = last_user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
var/ekname = params["name"]
|
||||
for(var/X in cultie.get_researchable_knowledge())
|
||||
var/datum/eldritch_knowledge/EK = X
|
||||
if(initial(EK.name) != ekname)
|
||||
continue
|
||||
if(cultie.gain_knowledge(EK))
|
||||
charge -= text2num(params["cost"])
|
||||
return TRUE
|
||||
|
||||
update_icon() // Not applicable to all objects.
|
||||
|
||||
/obj/item/forbidden_book/ui_close(mob/user)
|
||||
flick("book_closing",src)
|
||||
icon_state = initial(icon_state)
|
||||
return ..()
|
||||
|
||||
/obj/item/forbidden_book/debug
|
||||
charge = 100
|
||||
@@ -0,0 +1,289 @@
|
||||
/obj/effect/eldritch
|
||||
name = "Generic rune"
|
||||
desc = "Weird combination of shapes and symbols etched into the floor itself. The indentation is filled with thick black tar-like fluid."
|
||||
anchored = TRUE
|
||||
icon_state = ""
|
||||
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
layer = SIGIL_LAYER
|
||||
///Used mainly for summoning ritual to prevent spamming the rune to create millions of monsters.
|
||||
var/is_in_use = FALSE
|
||||
|
||||
/obj/effect/eldritch/attack_hand(mob/living/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
try_activate(user)
|
||||
|
||||
/obj/effect/eldritch/proc/try_activate(mob/living/user)
|
||||
if(!IS_HERETIC(user))
|
||||
return
|
||||
if(!is_in_use)
|
||||
INVOKE_ASYNC(src, .proc/activate , user)
|
||||
|
||||
/obj/effect/eldritch/attacked_by(obj/item/I, mob/living/user)
|
||||
. = ..()
|
||||
if(istype(I,/obj/item/nullrod))
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/eldritch/proc/activate(mob/living/user)
|
||||
is_in_use = TRUE
|
||||
// Have fun trying to read this proc.
|
||||
var/datum/antagonist/heretic/cultie = user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
var/list/knowledge = cultie.get_all_knowledge()
|
||||
var/list/atoms_in_range = list()
|
||||
|
||||
for(var/A in range(1, src))
|
||||
var/atom/atom_in_range = A
|
||||
if(istype(atom_in_range,/area))
|
||||
continue
|
||||
if(istype(atom_in_range,/turf)) // we dont want turfs
|
||||
continue
|
||||
if(istype(atom_in_range,/mob/living))
|
||||
var/mob/living/living_in_range = atom_in_range
|
||||
if(living_in_range.stat != DEAD || living_in_range == user) // we only accept corpses, no living beings allowed.
|
||||
continue
|
||||
atoms_in_range += atom_in_range
|
||||
for(var/X in knowledge)
|
||||
var/datum/eldritch_knowledge/current_eldritch_knowledge = knowledge[X]
|
||||
|
||||
//has to be done so that we can freely edit the local_required_atoms without fucking up the eldritch knowledge
|
||||
var/list/local_required_atoms = list()
|
||||
|
||||
if(!current_eldritch_knowledge.required_atoms || current_eldritch_knowledge.required_atoms.len == 0)
|
||||
continue
|
||||
|
||||
local_required_atoms += current_eldritch_knowledge.required_atoms
|
||||
|
||||
var/list/selected_atoms = list()
|
||||
|
||||
if(!current_eldritch_knowledge.recipe_snowflake_check(atoms_in_range,drop_location(),selected_atoms))
|
||||
continue
|
||||
|
||||
for(var/LR in local_required_atoms)
|
||||
var/list/local_required_atom_list = LR
|
||||
|
||||
for(var/LAIR in atoms_in_range)
|
||||
var/atom/local_atom_in_range = LAIR
|
||||
if(is_type_in_list(local_atom_in_range,local_required_atom_list))
|
||||
selected_atoms |= local_atom_in_range
|
||||
local_required_atoms -= list(local_required_atom_list)
|
||||
|
||||
if(length(local_required_atoms) > 0)
|
||||
continue
|
||||
|
||||
flick("[icon_state]_active",src)
|
||||
playsound(user, 'sound/magic/castsummon.ogg', 75, TRUE)
|
||||
//we are doing this since some on_finished_recipe subtract the atoms from selected_atoms making them invisible permanently.
|
||||
var/list/atoms_to_disappear = selected_atoms.Copy()
|
||||
for(var/to_disappear in atoms_to_disappear)
|
||||
var/atom/atom_to_disappear = to_disappear
|
||||
//temporary so we dont have to deal with the bs of someone picking those up when they may be deleted
|
||||
atom_to_disappear.invisibility = INVISIBILITY_ABSTRACT
|
||||
if(current_eldritch_knowledge.on_finished_recipe(user,selected_atoms,loc))
|
||||
current_eldritch_knowledge.cleanup_atoms(selected_atoms)
|
||||
is_in_use = FALSE
|
||||
|
||||
for(var/to_appear in atoms_to_disappear)
|
||||
var/atom/atom_to_appear = to_appear
|
||||
//we need to reappear the item just in case the ritual didnt consume everything... or something.
|
||||
atom_to_appear.invisibility = initial(atom_to_appear.invisibility)
|
||||
|
||||
return
|
||||
is_in_use = FALSE
|
||||
to_chat(user,"<span class='warning'>Your ritual failed! You used either wrong components or are missing something important!</span>")
|
||||
|
||||
/obj/effect/eldritch/big
|
||||
name = "transmutation circle"
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "eldritch_rune1"
|
||||
pixel_x = -32 //So the big ol' 96x96 sprite shows up right
|
||||
pixel_y = -32
|
||||
|
||||
/**
|
||||
* #Reality smash tracker
|
||||
*
|
||||
* Stupid fucking list holder, DONT create new ones, it will break the game, this is automnatically created whenever eldritch cultists are created.
|
||||
*
|
||||
* Tracks relevant data, generates relevant data, useful tool
|
||||
*/
|
||||
/datum/reality_smash_tracker
|
||||
///list of tracked reality smashes
|
||||
var/list/smashes = list()
|
||||
///List of mobs with ability to see the smashes
|
||||
var/list/targets = list()
|
||||
|
||||
/datum/reality_smash_tracker/Destroy(force, ...)
|
||||
if(GLOB.reality_smash_track == src)
|
||||
stack_trace("/datum/reality_smash_tracker was deleted. Heretics may no longer access any influences. Fix it or call coder support")
|
||||
QDEL_LIST(smashes)
|
||||
targets.Cut()
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Automatically fixes the target and smash network
|
||||
*
|
||||
* Fixes any bugs that are caused by late Generate() or exchanging clients
|
||||
*/
|
||||
/datum/reality_smash_tracker/proc/ReworkNetwork()
|
||||
listclearnulls(smashes)
|
||||
for(var/mind in targets)
|
||||
if(isnull(mind))
|
||||
stack_trace("A null somehow landed in a list of minds")
|
||||
continue
|
||||
for(var/X in smashes)
|
||||
var/obj/effect/reality_smash/reality_smash = X
|
||||
reality_smash.AddMind(mind)
|
||||
|
||||
/**
|
||||
* Generates a set amount of reality smashes based on the N value
|
||||
*
|
||||
* Automatically creates more reality smashes
|
||||
*/
|
||||
/datum/reality_smash_tracker/proc/_Generate()
|
||||
var/targ_len = length(targets)
|
||||
var/smash_len = length(smashes)
|
||||
var/number = targ_len * 6 - smash_len
|
||||
|
||||
for(var/i in 0 to number)
|
||||
|
||||
var/turf/chosen_location = get_safe_random_station_turf()
|
||||
//we also dont want them close to each other, at least 1 tile of seperation
|
||||
var/obj/effect/reality_smash/what_if_i_have_one = locate() in range(1, chosen_location)
|
||||
var/obj/effect/broken_illusion/what_if_i_had_one_but_got_used = locate() in range(1, chosen_location)
|
||||
if(what_if_i_have_one || what_if_i_had_one_but_got_used) //we dont want to spawn
|
||||
continue
|
||||
var/obj/effect/reality_smash/RS = new/obj/effect/reality_smash(chosen_location)
|
||||
smashes += RS
|
||||
ReworkNetwork()
|
||||
|
||||
|
||||
/**
|
||||
* Adds a mind to the list of people that can see the reality smashes
|
||||
*
|
||||
* Use this whenever you want to add someone to the list
|
||||
*/
|
||||
/datum/reality_smash_tracker/proc/AddMind(var/datum/mind/M)
|
||||
RegisterSignal(M.current,COMSIG_MOB_CLIENT_LOGIN,.proc/ReworkNetwork)
|
||||
targets |= M
|
||||
_Generate()
|
||||
for(var/X in smashes)
|
||||
var/obj/effect/reality_smash/reality_smash = X
|
||||
reality_smash.AddMind(M)
|
||||
|
||||
|
||||
/**
|
||||
* Removes a mind from the list of people that can see the reality smashes
|
||||
*
|
||||
* Use this whenever you want to remove someone from the list
|
||||
*/
|
||||
/datum/reality_smash_tracker/proc/RemoveMind(var/datum/mind/M)
|
||||
UnregisterSignal(M.current,COMSIG_MOB_CLIENT_LOGIN)
|
||||
targets -= M
|
||||
for(var/obj/effect/reality_smash/RS in smashes)
|
||||
RS.RemoveMind(M)
|
||||
|
||||
/obj/effect/broken_illusion
|
||||
name = "pierced reality"
|
||||
icon = 'icons/effects/eldritch.dmi'
|
||||
icon_state = "pierced_illusion"
|
||||
anchored = TRUE
|
||||
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
|
||||
/obj/effect/broken_illusion/attack_hand(mob/living/user)
|
||||
if(!ishuman(user))
|
||||
return ..()
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
if(IS_HERETIC(human_user))
|
||||
to_chat(human_user,"<span class='boldwarning'>You know better than to tempt forces out of your control.</span>")
|
||||
else
|
||||
var/obj/item/bodypart/arm = human_user.get_active_hand()
|
||||
if(prob(25))
|
||||
to_chat(human_user,"<span class='userdanger'>An otherwordly presence tears your arm apart into atoms as you try to touch the hole in the very fabric of reality!</span>")
|
||||
arm.dismember()
|
||||
qdel(arm)
|
||||
else
|
||||
to_chat(human_user,"<span class='danger'>You pull your hand away from the hole as eldritch energy flails out, trying to latch onto existence itself!</span>")
|
||||
|
||||
/obj/effect/broken_illusion/attack_tk(mob/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
if(IS_HERETIC(human_user))
|
||||
to_chat(human_user,"<span class='boldwarning'>You know better than to tempt forces out of your control.</span>")
|
||||
else
|
||||
//a very elaborate way to suicide
|
||||
to_chat(human_user,"<span class='userdanger'>Eldritch energy lashes out, piercing your fragile mind, tearing it to pieces!</span>")
|
||||
human_user.ghostize()
|
||||
var/obj/item/bodypart/head/head = locate() in human_user.bodyparts
|
||||
if(head)
|
||||
head.dismember()
|
||||
qdel(head)
|
||||
else
|
||||
human_user.gib()
|
||||
|
||||
var/datum/effect_system/reagents_explosion/explosion = new()
|
||||
explosion.set_up(1, get_turf(human_user), 1, 0)
|
||||
explosion.start()
|
||||
|
||||
/obj/effect/broken_illusion/examine(mob/user)
|
||||
if(!IS_HERETIC(user) && ishuman(user))
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
to_chat(human_user,"<span class='userdanger'>Your brain hurts when you look at this!</span>")
|
||||
human_user.adjustOrganLoss(ORGAN_SLOT_BRAIN,30)
|
||||
. = ..()
|
||||
|
||||
/obj/effect/reality_smash
|
||||
name = "/improper reality smash"
|
||||
icon = 'icons/effects/eldritch.dmi'
|
||||
anchored = TRUE
|
||||
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
///We cannot use icon_state since this is invisible, functions the same way but with custom behaviour.
|
||||
var/image_state = "reality_smash"
|
||||
///Who can see us?
|
||||
var/list/minds = list()
|
||||
///Tracked image
|
||||
var/image/img
|
||||
|
||||
/obj/effect/reality_smash/Initialize()
|
||||
. = ..()
|
||||
img = image(icon, src, image_state, OBJ_LAYER)
|
||||
generate_name()
|
||||
|
||||
/obj/effect/reality_smash/Destroy()
|
||||
on_destroy()
|
||||
return ..()
|
||||
|
||||
///Custom effect that happens on destruction
|
||||
/obj/effect/reality_smash/proc/on_destroy()
|
||||
for(var/cm in minds)
|
||||
var/datum/mind/cultie = cm
|
||||
if(cultie.current?.client)
|
||||
cultie.current.client.images -= img
|
||||
//clear the list
|
||||
minds -= cultie
|
||||
GLOB.reality_smash_track.smashes -= src
|
||||
img = null
|
||||
new /obj/effect/broken_illusion(drop_location())
|
||||
|
||||
///Makes the mind able to see this effect
|
||||
/obj/effect/reality_smash/proc/AddMind(var/datum/mind/cultie)
|
||||
minds |= cultie
|
||||
if(cultie.current.client)
|
||||
cultie.current.client.images |= img
|
||||
|
||||
|
||||
|
||||
///Makes the mind not able to see this effect
|
||||
/obj/effect/reality_smash/proc/RemoveMind(var/datum/mind/cultie)
|
||||
minds -= cultie
|
||||
if(cultie.current.client)
|
||||
cultie.current.client.images -= img
|
||||
|
||||
|
||||
|
||||
///Generates random name
|
||||
/obj/effect/reality_smash/proc/generate_name()
|
||||
var/static/list/prefix = list("Omniscient","Thundering","Enlightening","Intrusive","Rejectful","Atomized","Subtle","Rising","Lowering","Fleeting","Towering","Blissful","Arrogant","Threatening","Peaceful","Aggressive")
|
||||
var/static/list/postfix = list("Flaw","Presence","Crack","Heat","Cold","Memory","Reminder","Breeze","Grasp","Sight","Whisper","Flow","Touch","Veil","Thought","Imperfection","Blemish","Blush")
|
||||
|
||||
name = pick(prefix) + " " + pick(postfix)
|
||||
@@ -0,0 +1,142 @@
|
||||
/obj/item/living_heart
|
||||
name = "living heart"
|
||||
desc = "Link to the worlds beyond."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "living_heart"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
///Target
|
||||
var/mob/living/carbon/human/target
|
||||
|
||||
/obj/item/living_heart/attack_self(mob/user)
|
||||
. = ..()
|
||||
if(!IS_HERETIC(user))
|
||||
return
|
||||
if(!target)
|
||||
to_chat(user,"<span class='warning'>No target could be found. Put the living heart on the rune and use the rune to recieve a target.</span>")
|
||||
return
|
||||
var/dist = get_dist(user.loc,target.loc)
|
||||
var/dir = get_dir(user.loc,target.loc)
|
||||
|
||||
switch(dist)
|
||||
if(0 to 15)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is near you. They are to the [dir2text(dir)] of you!</span>")
|
||||
if(16 to 31)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is somewhere in your vicinty. They are to the [dir2text(dir)] of you!</span>")
|
||||
if(32 to 127)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is far away from you. They are to the [dir2text(dir)] of you!</span>")
|
||||
else
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is beyond our reach.</span>")
|
||||
|
||||
if(target.stat == DEAD)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is dead. Bring them onto a transmutation rune!</span>")
|
||||
|
||||
/obj/item/melee/sickly_blade
|
||||
name = "eldritch blade"
|
||||
desc = "A sickly green crescent blade, decorated with an ornamental eye. You feel like you're being watched..."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "eldritch_blade"
|
||||
item_state = "eldritch_blade"
|
||||
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
|
||||
inhand_x_dimension = 64
|
||||
inhand_y_dimension = 64
|
||||
flags_1 = CONDUCT_1
|
||||
sharpness = SHARP_EDGED
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
force = 17
|
||||
throwforce = 10
|
||||
hitsound = 'sound/weapons/bladeslice.ogg'
|
||||
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "rended")
|
||||
|
||||
/obj/item/melee/sickly_blade/attack(mob/living/M, mob/living/user)
|
||||
if(!IS_HERETIC(user))
|
||||
to_chat(user,"<span class='danger'>You feel a pulse of some alien intellect lash out at your mind!</span>")
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
human_user.AdjustParalyzed(5 SECONDS)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/item/melee/sickly_blade/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
var/datum/antagonist/heretic/cultie = user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
if(!cultie || !proximity_flag)
|
||||
return
|
||||
var/list/knowledge = cultie.get_all_knowledge()
|
||||
for(var/X in knowledge)
|
||||
var/datum/eldritch_knowledge/eldritch_knowledge_datum = knowledge[X]
|
||||
eldritch_knowledge_datum.on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
|
||||
/obj/item/melee/sickly_blade/rust
|
||||
name = "rusted blade"
|
||||
desc = "This crescent blade is decrepit, wasting to dust. Yet still it bites, catching flesh with jagged, rotten teeth."
|
||||
icon_state = "rust_blade"
|
||||
item_state = "rust_blade"
|
||||
embedding = list("pain_mult" = 4, "embed_chance" = 75, "fall_chance" = 10, "ignore_throwspeed_threshold" = TRUE)
|
||||
throwforce = 17
|
||||
|
||||
/obj/item/melee/sickly_blade/ash
|
||||
name = "ashen blade"
|
||||
desc = "Molten and unwrought, a hunk of metal warped to cinders and slag. Unmade, it aspires to be more than it is, and shears soot-filled wounds with a blunt edge."
|
||||
icon_state = "ash_blade"
|
||||
item_state = "ash_blade"
|
||||
force = 20
|
||||
|
||||
/obj/item/melee/sickly_blade/flesh
|
||||
name = "flesh blade"
|
||||
desc = "A crescent blade born from a fleshwarped creature. Keenly aware, it seeks to spread to others the excruciations it has endured from dead origins."
|
||||
icon_state = "flesh_blade"
|
||||
item_state = "flesh_blade"
|
||||
wound_bonus = 5
|
||||
bare_wound_bonus = 15
|
||||
|
||||
/obj/item/clothing/neck/eldritch_amulet
|
||||
name = "warm eldritch medallion"
|
||||
desc = "A strange medallion. Peering through the crystalline surface, the world around you melts away. You see your own beating heart, and the pulse of a thousand others."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "eye_medalion"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
///What trait do we want to add upon equipiing
|
||||
var/trait = TRAIT_THERMAL_VISION
|
||||
|
||||
/obj/item/clothing/neck/eldritch_amulet/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(ishuman(user) && user.mind && slot == SLOT_NECK && IS_HERETIC(user))
|
||||
ADD_TRAIT(user, trait, CLOTHING_TRAIT)
|
||||
user.update_sight()
|
||||
|
||||
/obj/item/clothing/neck/eldritch_amulet/dropped(mob/user)
|
||||
. = ..()
|
||||
REMOVE_TRAIT(user, trait, CLOTHING_TRAIT)
|
||||
user.update_sight()
|
||||
|
||||
/obj/item/clothing/neck/eldritch_amulet/piercing
|
||||
name = "piercing eldritch medallion"
|
||||
desc = "A strange medallion. Peering through the crystalline surface, the light refracts into new and terrifying spectrums of color. You see yourself, reflected off cascading mirrors, warped into improbable shapes."
|
||||
trait = TRAIT_XRAY_VISION
|
||||
|
||||
/obj/item/clothing/head/hooded/cult_hoodie/eldritch
|
||||
name = "ominous hood"
|
||||
icon_state = "eldritch"
|
||||
desc = "A torn, dust-caked hood. Strange eyes line the inside."
|
||||
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
|
||||
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
|
||||
flash_protect = 2
|
||||
|
||||
/obj/item/clothing/suit/hooded/cultrobes/eldritch
|
||||
name = "ominous armor"
|
||||
desc = "A ragged, dusty set of robes. Strange eyes line the inside."
|
||||
icon_state = "eldritch_armor"
|
||||
item_state = "eldritch_armor"
|
||||
flags_inv = HIDESHOES|HIDEJUMPSUIT
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS
|
||||
allowed = list(/obj/item/melee/sickly_blade, /obj/item/forbidden_book)
|
||||
hoodtype = /obj/item/clothing/head/hooded/cult_hoodie/eldritch
|
||||
// slightly better than normal cult robes
|
||||
armor = list("melee" = 50, "bullet" = 50, "laser" = 50,"energy" = 50, "bomb" = 35, "bio" = 20, "rad" = 0, "fire" = 20, "acid" = 20)
|
||||
|
||||
/obj/item/reagent_containers/glass/beaker/eldritch
|
||||
name = "flask of eldritch essence"
|
||||
desc = "Toxic to the close minded. Healing to those with knowledge of the beyond."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "eldrich_flask"
|
||||
list_reagents = list(/datum/reagent/eldritch = 50)
|
||||
@@ -0,0 +1,303 @@
|
||||
|
||||
/**
|
||||
* #Eldritch Knwoledge
|
||||
*
|
||||
* Datum that makes eldritch cultist interesting.
|
||||
*
|
||||
* Eldritch knowledge aren't instantiated anywhere roundstart, and are initalized and destroyed as the round goes on.
|
||||
*/
|
||||
/datum/eldritch_knowledge
|
||||
///Name of the knowledge
|
||||
var/name = "Basic knowledge"
|
||||
///Description of the knowledge
|
||||
var/desc = "Basic knowledge of forbidden arts."
|
||||
///What shows up
|
||||
var/gain_text = ""
|
||||
///Cost of knowledge in souls
|
||||
var/cost = 0
|
||||
///Next knowledge in the research tree
|
||||
var/list/next_knowledge = list()
|
||||
///What knowledge is incompatible with this. This will simply make it impossible to research knowledges that are in banned_knowledge once this gets researched.
|
||||
var/list/banned_knowledge = list()
|
||||
///Used with rituals, how many items this needs
|
||||
var/list/required_atoms = list()
|
||||
///What do we get out of this
|
||||
var/list/result_atoms = list()
|
||||
///What path is this on defaults to "Side"
|
||||
var/route = PATH_SIDE
|
||||
|
||||
/datum/eldritch_knowledge/New()
|
||||
. = ..()
|
||||
var/list/temp_list
|
||||
for(var/X in required_atoms)
|
||||
var/atom/A = X
|
||||
temp_list += list(typesof(A))
|
||||
required_atoms = temp_list
|
||||
|
||||
/**
|
||||
* What happens when this is assigned to an antag datum
|
||||
*
|
||||
* This proc is called whenever a new eldritch knowledge is added to an antag datum
|
||||
*/
|
||||
/datum/eldritch_knowledge/proc/on_gain(mob/user)
|
||||
to_chat(user, "<span class='warning'>[gain_text]</span>")
|
||||
return
|
||||
/**
|
||||
* What happens when you loose this
|
||||
*
|
||||
* This proc is called whenever antagonist looses his antag datum, put cleanup code in here
|
||||
*/
|
||||
/datum/eldritch_knowledge/proc/on_lose(mob/user)
|
||||
return
|
||||
/**
|
||||
* What happens every tick
|
||||
*
|
||||
* This proc is called on SSprocess in eldritch cultist antag datum. SSprocess happens roughly every second
|
||||
*/
|
||||
/datum/eldritch_knowledge/proc/on_life(mob/user)
|
||||
return
|
||||
|
||||
/**
|
||||
* Special check for recipes
|
||||
*
|
||||
* If you are adding a more complex summoning or something that requires a special check that parses through all the atoms in an area override this.
|
||||
*/
|
||||
/datum/eldritch_knowledge/proc/recipe_snowflake_check(list/atoms,loc)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* What happens once the recipe is succesfully finished
|
||||
*
|
||||
* By default this proc creates atoms from result_atoms list. Override this is you want something else to happen.
|
||||
*/
|
||||
/datum/eldritch_knowledge/proc/on_finished_recipe(mob/living/user,list/atoms,loc)
|
||||
if(result_atoms.len == 0)
|
||||
return FALSE
|
||||
|
||||
for(var/A in result_atoms)
|
||||
new A(loc)
|
||||
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Used atom cleanup
|
||||
*
|
||||
* Overide this proc if you dont want ALL ATOMS to be destroyed. useful in many situations.
|
||||
*/
|
||||
/datum/eldritch_knowledge/proc/cleanup_atoms(list/atoms)
|
||||
for(var/X in atoms)
|
||||
var/atom/A = X
|
||||
if(!isliving(A))
|
||||
atoms -= A
|
||||
qdel(A)
|
||||
return
|
||||
|
||||
/**
|
||||
* Mansus grasp act
|
||||
*
|
||||
* Gives addtional effects to mansus grasp spell
|
||||
*/
|
||||
/datum/eldritch_knowledge/proc/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
return FALSE
|
||||
|
||||
|
||||
/**
|
||||
* Sickly blade act
|
||||
*
|
||||
* Gives addtional effects to sickly blade weapon
|
||||
*/
|
||||
/datum/eldritch_knowledge/proc/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
return
|
||||
|
||||
//////////////
|
||||
///Subtypes///
|
||||
//////////////
|
||||
|
||||
/datum/eldritch_knowledge/spell
|
||||
var/obj/effect/proc_holder/spell/spell_to_add
|
||||
|
||||
/datum/eldritch_knowledge/spell/on_gain(mob/user)
|
||||
var/obj/effect/proc_holder/S = new spell_to_add
|
||||
user.mind.AddSpell(S)
|
||||
return ..()
|
||||
|
||||
/datum/eldritch_knowledge/spell/on_lose(mob/user)
|
||||
user.mind.RemoveSpell(spell_to_add)
|
||||
return ..()
|
||||
|
||||
/datum/eldritch_knowledge/curse
|
||||
var/timer = 5 MINUTES
|
||||
var/list/fingerprints = list()
|
||||
|
||||
/datum/eldritch_knowledge/curse/recipe_snowflake_check(list/atoms, loc)
|
||||
fingerprints = list()
|
||||
for(var/X in atoms)
|
||||
var/atom/A = X
|
||||
fingerprints |= A.fingerprints
|
||||
listclearnulls(fingerprints)
|
||||
if(fingerprints.len == 0)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/eldritch_knowledge/curse/on_finished_recipe(mob/living/user,list/atoms,loc)
|
||||
|
||||
var/list/compiled_list = list()
|
||||
|
||||
for(var/H in GLOB.human_list)
|
||||
var/mob/living/carbon/human/human_to_check = H
|
||||
if(fingerprints[md5(human_to_check.dna.uni_identity)])
|
||||
compiled_list |= human_to_check.real_name
|
||||
compiled_list[human_to_check.real_name] = human_to_check
|
||||
|
||||
if(compiled_list.len == 0)
|
||||
to_chat(user, "<span class='warning'>The items don't posses required fingerprints.</span>")
|
||||
return FALSE
|
||||
|
||||
var/chosen_mob = input("Select the person you wish to curse","Your target") as null|anything in sortList(compiled_list, /proc/cmp_mob_realname_dsc)
|
||||
if(!chosen_mob)
|
||||
return FALSE
|
||||
curse(compiled_list[chosen_mob])
|
||||
addtimer(CALLBACK(src, .proc/uncurse, compiled_list[chosen_mob]),timer)
|
||||
return TRUE
|
||||
|
||||
/datum/eldritch_knowledge/curse/proc/curse(mob/living/chosen_mob)
|
||||
return
|
||||
|
||||
/datum/eldritch_knowledge/curse/proc/uncurse(mob/living/chosen_mob)
|
||||
return
|
||||
|
||||
/datum/eldritch_knowledge/summon
|
||||
//Mob to summon
|
||||
var/mob/living/mob_to_summon
|
||||
|
||||
|
||||
/datum/eldritch_knowledge/summon/on_finished_recipe(mob/living/user,list/atoms,loc)
|
||||
//we need to spawn the mob first so that we can use it in pollCandidatesForMob, we will move it from nullspace down the code
|
||||
var/mob/living/summoned = new mob_to_summon(loc)
|
||||
message_admins("[summoned.name] is being summoned by [user.real_name] in [loc]")
|
||||
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [summoned.name]", ROLE_HERETIC, null, FALSE, 100, summoned)
|
||||
if(!LAZYLEN(candidates))
|
||||
to_chat(user,"<span class='warning'>No ghost could be found...</span>")
|
||||
qdel(summoned)
|
||||
return FALSE
|
||||
var/mob/dead/observer/C = pick(candidates)
|
||||
log_game("[key_name_admin(C)] has taken control of ([key_name_admin(summoned)]), their master is [user.real_name]")
|
||||
summoned.ghostize(FALSE)
|
||||
summoned.key = C.key
|
||||
summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster)
|
||||
var/datum/antagonist/heretic_monster/heretic_monster = summoned.mind.has_antag_datum(/datum/antagonist/heretic_monster)
|
||||
var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
heretic_monster.set_owner(master)
|
||||
return TRUE
|
||||
|
||||
//Ascension knowledge
|
||||
/datum/eldritch_knowledge/final
|
||||
var/finished = FALSE
|
||||
|
||||
/datum/eldritch_knowledge/final/recipe_snowflake_check(list/atoms, loc,selected_atoms)
|
||||
if(finished)
|
||||
return FALSE
|
||||
var/counter = 0
|
||||
for(var/mob/living/carbon/human/H in atoms)
|
||||
selected_atoms |= H
|
||||
counter++
|
||||
if(counter == 3)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/eldritch_knowledge/final/on_finished_recipe( mob/living/user, list/atoms, loc)
|
||||
finished = TRUE
|
||||
return TRUE
|
||||
|
||||
/datum/eldritch_knowledge/final/cleanup_atoms(list/atoms)
|
||||
. = ..()
|
||||
for(var/mob/living/carbon/human/H in atoms)
|
||||
atoms -= H
|
||||
H.gib()
|
||||
|
||||
|
||||
///////////////
|
||||
///Base lore///
|
||||
///////////////
|
||||
|
||||
/datum/eldritch_knowledge/spell/basic
|
||||
name = "Break of Dawn"
|
||||
desc = "Starts your journey in the mansus. Allows you to select a target using a living heart on a transmutation rune."
|
||||
gain_text = "Gates of Mansus open up to your mind."
|
||||
next_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh)
|
||||
cost = 0
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/mansus_grasp
|
||||
required_atoms = list(/obj/item/living_heart)
|
||||
route = "Start"
|
||||
|
||||
/datum/eldritch_knowledge/spell/basic/recipe_snowflake_check(list/atoms, loc)
|
||||
. = ..()
|
||||
for(var/obj/item/living_heart/LH in atoms)
|
||||
if(!LH.target)
|
||||
return TRUE
|
||||
if(LH.target in atoms)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/eldritch_knowledge/spell/basic/on_finished_recipe(mob/living/user, list/atoms, loc)
|
||||
. = TRUE
|
||||
var/mob/living/carbon/carbon_user = user
|
||||
for(var/obj/item/living_heart/LH in atoms)
|
||||
|
||||
if(LH.target && LH.target.stat == DEAD)
|
||||
to_chat(carbon_user,"<span class='danger'>Your patrons accepts your offer...</span>")
|
||||
var/mob/living/carbon/human/H = LH.target
|
||||
H.become_husk()
|
||||
LH.target = null
|
||||
var/datum/antagonist/heretic/EC = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
|
||||
EC.total_sacrifices++
|
||||
for(var/X in carbon_user.get_all_gear())
|
||||
if(!istype(X,/obj/item/forbidden_book))
|
||||
continue
|
||||
var/obj/item/forbidden_book/FB = X
|
||||
FB.charge++
|
||||
FB.charge++
|
||||
break
|
||||
|
||||
if(!LH.target)
|
||||
var/datum/objective/A = new
|
||||
A.owner = user.mind
|
||||
var/datum/mind/targeted = A.find_target()//easy way, i dont feel like copy pasting that entire block of code
|
||||
LH.target = targeted.current
|
||||
qdel(A)
|
||||
if(LH.target)
|
||||
to_chat(user,"<span class='warning'>Your new target has been selected, go and sacrifice [LH.target.real_name]!</span>")
|
||||
|
||||
else
|
||||
to_chat(user,"<span class='warning'>target could not be found for living heart.</span>")
|
||||
|
||||
/datum/eldritch_knowledge/spell/basic/cleanup_atoms(list/atoms)
|
||||
return
|
||||
|
||||
/datum/eldritch_knowledge/living_heart
|
||||
name = "Living Heart"
|
||||
desc = "Allows you to create additional living hearts, using a heart, a pool of blood and a poppy. Living hearts when used on a transmutation rune will grant you a person to hunt and sacrifice on the rune. Every sacrifice gives you an additional charge in the book."
|
||||
gain_text = "Disconnected, yet it still beats."
|
||||
cost = 0
|
||||
required_atoms = list(/obj/item/organ/heart,/obj/effect/decal/cleanable/blood,/obj/item/reagent_containers/food/snacks/grown/poppy)
|
||||
result_atoms = list(/obj/item/living_heart)
|
||||
route = "Start"
|
||||
|
||||
/datum/eldritch_knowledge/codex_cicatrix
|
||||
name = "Codex Cicatrix"
|
||||
desc = "Allows you to create a spare Codex Cicatrix if you have lost one, using a bible, human skin, a pen and a pair of eyes."
|
||||
gain_text = "Their hands are at your throat, yet you see them not."
|
||||
cost = 0
|
||||
required_atoms = list(/obj/item/organ/eyes,/obj/item/stack/sheet/animalhide/human,/obj/item/storage/book/bible,/obj/item/pen)
|
||||
result_atoms = list(/obj/item/forbidden_book)
|
||||
route = "Start"
|
||||
|
||||
/datum/eldritch_knowledge/eldritch_blade
|
||||
name = "Eldritch Blade"
|
||||
desc = "Allows you to create a sickly, eldritch blade by transmuting a glass shard and a metal rod atop a transmutation rune."
|
||||
gain_text = "The first step starts with sacrifice."
|
||||
cost = 0
|
||||
required_atoms = list(/obj/item/shard,/obj/item/stack/rods)
|
||||
result_atoms = list(/obj/item/melee/sickly_blade)
|
||||
route = "Start"
|
||||
@@ -0,0 +1,668 @@
|
||||
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash
|
||||
name = "Ashen Passage"
|
||||
desc = "Low range spell allowing you to pass through a few walls."
|
||||
school = "transmutation"
|
||||
invocation = "DULK'ES PRE'ZIMAS"
|
||||
invocation_type = "whisper"
|
||||
charge_max = 150
|
||||
range = -1
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "ash_shift"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
jaunt_in_time = 13
|
||||
jaunt_duration = 10
|
||||
jaunt_in_type = /obj/effect/temp_visual/dir_setting/ash_shift
|
||||
jaunt_out_type = /obj/effect/temp_visual/dir_setting/ash_shift/out
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash/long
|
||||
jaunt_duration = 50
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash/play_sound()
|
||||
return
|
||||
|
||||
/obj/effect/temp_visual/dir_setting/ash_shift
|
||||
name = "ash_shift"
|
||||
icon = 'icons/mob/mob.dmi'
|
||||
icon_state = "ash_shift2"
|
||||
duration = 13
|
||||
|
||||
/obj/effect/temp_visual/dir_setting/ash_shift/out
|
||||
icon_state = "ash_shift"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp
|
||||
name = "Mansus Grasp"
|
||||
desc = "Touch spell that allows you to channel the power of the Old Gods through you."
|
||||
hand_path = /obj/item/melee/touch_attack/mansus_fist
|
||||
school = "evocation"
|
||||
charge_max = 150
|
||||
clothes_req = FALSE
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "mansus_grasp"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/item/melee/touch_attack/mansus_fist
|
||||
name = "Mansus Grasp"
|
||||
desc = "A sinister looking aura that distorts the flow of reality around it. Causes knockdown, major stamina damage aswell as some Brute. It gains additional beneficial effects with certain knowledges you can research."
|
||||
icon_state = "disintegrate"
|
||||
item_state = "disintegrate"
|
||||
catchphrase = "T'IESA SIE'KTI VISATA"
|
||||
|
||||
/obj/item/melee/touch_attack/mansus_fist/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
|
||||
if(!proximity_flag || target == user)
|
||||
return
|
||||
playsound(user, 'sound/items/welder.ogg', 75, TRUE)
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/tar = target
|
||||
if(tar.anti_magic_check())
|
||||
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
return ..()
|
||||
var/datum/mind/M = user.mind
|
||||
var/datum/antagonist/heretic/cultie = M.has_antag_datum(/datum/antagonist/heretic)
|
||||
|
||||
var/use_charge = FALSE
|
||||
if(iscarbon(target))
|
||||
use_charge = TRUE
|
||||
var/mob/living/carbon/C = target
|
||||
C.adjustBruteLoss(15)
|
||||
C.DefaultCombatKnockdown(50, override_stamdmg = 0)
|
||||
C.adjustStaminaLoss(60)
|
||||
var/list/knowledge = cultie.get_all_knowledge()
|
||||
|
||||
for(var/X in knowledge)
|
||||
var/datum/eldritch_knowledge/EK = knowledge[X]
|
||||
if(EK.on_mansus_grasp(target, user, proximity_flag, click_parameters))
|
||||
use_charge = TRUE
|
||||
if(use_charge)
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/rust_conversion
|
||||
name = "Aggressive Spread"
|
||||
desc = "Spreads rust onto nearby turfs."
|
||||
school = "transmutation"
|
||||
charge_max = 300 //twice as long as mansus grasp
|
||||
clothes_req = FALSE
|
||||
invocation = "PLI'STI MINO DOMI'KA"
|
||||
invocation_type = "whisper"
|
||||
range = 3
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "corrode"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/rust_conversion/cast(list/targets, mob/user = usr)
|
||||
playsound(user, 'sound/items/welder.ogg', 75, TRUE)
|
||||
for(var/turf/T in targets)
|
||||
///What we want is the 3 tiles around the user and the tile under him to be rusted, so min(dist,1)-1 causes us to get 0 for these tiles, rest of the tiles are based on chance
|
||||
var/chance = 100 - (max(get_dist(T,user),1)-1)*100/(range+1)
|
||||
if(!prob(chance))
|
||||
continue
|
||||
T.rust_heretic_act()
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/rust_conversion/small
|
||||
name = "Rust Conversion"
|
||||
desc = "Spreads rust onto nearby turfs."
|
||||
range = 2
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/touch/blood_siphon
|
||||
name = "Blood Siphon"
|
||||
desc = "Touch spell that heals you while damaging the enemy, has a chance to transfer wounds between you and your enemy."
|
||||
hand_path = /obj/item/melee/touch_attack/blood_siphon
|
||||
school = "evocation"
|
||||
charge_max = 150
|
||||
clothes_req = FALSE
|
||||
invocation_type = "none"
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "blood_siphon"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/item/melee/touch_attack/blood_siphon
|
||||
name = "Blood Siphon"
|
||||
desc = "A sinister looking aura that distorts the flow of reality around it."
|
||||
icon_state = "disintegrate"
|
||||
item_state = "disintegrate"
|
||||
catchphrase = "SUN'AI'KINI'MAS"
|
||||
|
||||
/obj/item/melee/touch_attack/blood_siphon/afterattack(atom/target, mob/user, proximity_flag, proximity)
|
||||
if(!proximity_flag)
|
||||
return
|
||||
playsound(user, 'sound/effects/curseattack.ogg', 75, TRUE)
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/tar = target
|
||||
if(tar.anti_magic_check())
|
||||
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
return ..()
|
||||
var/mob/living/carbon/C2 = user
|
||||
if(isliving(target))
|
||||
var/mob/living/L = target
|
||||
L.adjustBruteLoss(20)
|
||||
C2.adjustBruteLoss(-20)
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/C1 = target
|
||||
for(var/obj/item/bodypart/bodypart in C2.bodyparts)
|
||||
for(var/i in bodypart.wounds)
|
||||
var/datum/wound/iter_wound = i
|
||||
if(prob(50))
|
||||
continue
|
||||
var/obj/item/bodypart/target_bodypart = locate(bodypart.type) in C1.bodyparts
|
||||
if(!target_bodypart)
|
||||
continue
|
||||
iter_wound.remove_wound()
|
||||
iter_wound.apply_wound(target_bodypart)
|
||||
|
||||
C1.blood_volume -= 20
|
||||
if(C2.blood_volume < BLOOD_VOLUME_MAXIMUM) //we dont want to explode after all
|
||||
C2.blood_volume += 20
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/rust_wave
|
||||
name = "Patron's Reach"
|
||||
desc = "Channels energy into your gauntlet - firing it results in a wave of rust being created in it's wake."
|
||||
projectile_type = /obj/item/projectile/magic/spell/rust_wave
|
||||
charge_max = 350
|
||||
clothes_req = FALSE
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
base_icon_state = "rust_wave"
|
||||
action_icon_state = "rust_wave"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
sound = 'sound/effects/curse5.ogg'
|
||||
active_msg = "You extend your hand out, preparing to send out a wave of rust."
|
||||
deactive_msg = "You extinguish that energy, for now..."
|
||||
invocation = "RUD'ZI VAR'ZTAS"
|
||||
invocation_type = "whisper"
|
||||
|
||||
/obj/item/projectile/magic/spell/rust_wave
|
||||
name = "rust bolt"
|
||||
icon_state = "eldritch_projectile"
|
||||
alpha = 180
|
||||
damage = 30
|
||||
damage_type = TOX
|
||||
nodamage = 0
|
||||
hitsound = 'sound/effects/curseattack.ogg'
|
||||
range = 15
|
||||
|
||||
/obj/item/projectile/magic/spell/rust_wave/Moved(atom/OldLoc, Dir)
|
||||
. = ..()
|
||||
playsound(src, 'sound/items/welder.ogg', 75, TRUE)
|
||||
var/list/turflist = list()
|
||||
var/turf/T1
|
||||
turflist += get_turf(src)
|
||||
T1 = get_step(src,turn(dir,90))
|
||||
turflist += T1
|
||||
turflist += get_step(T1,turn(dir,90))
|
||||
T1 = get_step(src,turn(dir,-90))
|
||||
turflist += T1
|
||||
turflist += get_step(T1,turn(dir,-90))
|
||||
for(var/X in turflist)
|
||||
if(!X || prob(25))
|
||||
continue
|
||||
var/turf/T = X
|
||||
T.rust_heretic_act()
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/rust_wave/short
|
||||
name = "Small Patron's Reach"
|
||||
projectile_type = /obj/item/projectile/magic/spell/rust_wave/short
|
||||
|
||||
/obj/item/projectile/magic/spell/rust_wave/short
|
||||
range = 7
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/cleave
|
||||
name = "Cleave"
|
||||
desc = "Causes severe bleeding on a target and people around them"
|
||||
school = "transmutation"
|
||||
charge_max = 350
|
||||
clothes_req = FALSE
|
||||
invocation = "PLES'TI VI'RIBUS"
|
||||
invocation_type = "whisper"
|
||||
range = 9
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "cleave"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/cleave/cast(list/targets, mob/user)
|
||||
if(!targets.len)
|
||||
to_chat(user, "<span class='warning'>No target found in range!</span>")
|
||||
return FALSE
|
||||
if(!can_target(targets[1], user))
|
||||
return FALSE
|
||||
|
||||
for(var/mob/living/carbon/human/C in range(1,targets[1]))
|
||||
targets |= C
|
||||
|
||||
|
||||
for(var/X in targets)
|
||||
var/mob/living/carbon/human/target = X
|
||||
if(target == user)
|
||||
continue
|
||||
if(target.anti_magic_check())
|
||||
to_chat(user, "<span class='warning'>The spell had no effect!</span>")
|
||||
target.visible_message("<span class='danger'>[target]'s veins flash with fire, but their magic protection repulses the blaze!</span>", \
|
||||
"<span class='danger'>Your veins flash with fire, but your magic protection repels the blaze!</span>")
|
||||
continue
|
||||
|
||||
target.visible_message("<span class='danger'>[target]'s veins are shredded from within as an unholy blaze erupts from their blood!</span>", \
|
||||
"<span class='danger'>Your veins burst from within and unholy flame erupts from your blood!</span>")
|
||||
var/obj/item/bodypart/bodypart = pick(target.bodyparts)
|
||||
var/datum/wound/slash/critical/crit_wound = new
|
||||
crit_wound.apply_wound(bodypart)
|
||||
target.adjustFireLoss(20)
|
||||
new /obj/effect/temp_visual/cleave(target.drop_location())
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/cleave/can_target(atom/target, mob/user, silent)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(!istype(target,/mob/living/carbon/human))
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>You are unable to cleave [target]!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/cleave/long
|
||||
charge_max = 650
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/touch/mad_touch
|
||||
name = "Touch of Madness"
|
||||
desc = "Touch spell that drains your enemies sanity."
|
||||
school = "transmutation"
|
||||
charge_max = 150
|
||||
clothes_req = FALSE
|
||||
invocation_type = "none"
|
||||
range = 2
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "mad_touch"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/touch/mad_touch/can_target(atom/target, mob/user, silent)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(!istype(target,/mob/living/carbon/human))
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>You are unable to touch [target]!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/touch/mad_touch/cast(list/targets, mob/user)
|
||||
. = ..()
|
||||
for(var/mob/living/carbon/target in targets)
|
||||
if(ishuman(targets))
|
||||
var/mob/living/carbon/human/tar = target
|
||||
if(tar.anti_magic_check())
|
||||
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
return
|
||||
if(target.mind && !target.mind.has_antag_datum(/datum/antagonist/heretic))
|
||||
to_chat(user,"<span class='warning'>[target.name] has been cursed!</span>")
|
||||
SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/ash_final
|
||||
name = "Nightwatcher's Rite"
|
||||
desc = "Powerful spell that releases 5 streams of fire away from you."
|
||||
school = "transmutation"
|
||||
invocation = "IGNIS'INTI"
|
||||
invocation_type = "whisper"
|
||||
charge_max = 300
|
||||
range = 15
|
||||
clothes_req = FALSE
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "flames"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/ash_final/cast(list/targets, mob/user)
|
||||
for(var/X in targets)
|
||||
var/T
|
||||
T = line_target(-25, range, X, user)
|
||||
INVOKE_ASYNC(src, .proc/fire_line, user,T)
|
||||
T = line_target(10, range, X, user)
|
||||
INVOKE_ASYNC(src, .proc/fire_line, user,T)
|
||||
T = line_target(0, range, X, user)
|
||||
INVOKE_ASYNC(src, .proc/fire_line, user,T)
|
||||
T = line_target(-10, range, X, user)
|
||||
INVOKE_ASYNC(src, .proc/fire_line, user,T)
|
||||
T = line_target(25, range, X, user)
|
||||
INVOKE_ASYNC(src, .proc/fire_line, user,T)
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/ash_final/proc/line_target(offset, range, atom/at , atom/user)
|
||||
if(!at)
|
||||
return
|
||||
var/angle = ATAN2(at.x - user.x, at.y - user.y) + offset
|
||||
var/turf/T = get_turf(user)
|
||||
for(var/i in 1 to range)
|
||||
var/turf/check = locate(user.x + cos(angle) * i, user.y + sin(angle) * i, user.z)
|
||||
if(!check)
|
||||
break
|
||||
T = check
|
||||
return (getline(user, T) - get_turf(user))
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/ash_final/proc/fire_line(atom/source, list/turfs)
|
||||
var/list/hit_list = list()
|
||||
for(var/turf/T in turfs)
|
||||
if(istype(T, /turf/closed))
|
||||
break
|
||||
|
||||
for(var/mob/living/L in T.contents)
|
||||
if(L.anti_magic_check())
|
||||
L.visible_message("<span class='danger'>Spell bounces off of [L]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
continue
|
||||
if(L in hit_list || L == source)
|
||||
continue
|
||||
hit_list += L
|
||||
L.adjustFireLoss(20)
|
||||
to_chat(L, "<span class='userdanger'>You're hit by [source]'s fire breath!</span>")
|
||||
|
||||
new /obj/effect/hotspot(T)
|
||||
T.hotspot_expose(700,50,1)
|
||||
// deals damage to mechs
|
||||
for(var/obj/mecha/M in T.contents)
|
||||
if(M in hit_list)
|
||||
continue
|
||||
hit_list += M
|
||||
M.take_damage(45, BURN, "melee", 1)
|
||||
sleep(1.5)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/shapeshift/eldritch
|
||||
invocation_type = "none"
|
||||
clothes_req = FALSE
|
||||
action_background_icon_state = "bg_ecult"
|
||||
sound = 'sound/magic/enter_blood.ogg'
|
||||
possible_shapes = list(/mob/living/simple_animal/mouse,\
|
||||
/mob/living/simple_animal/pet/dog/corgi,\
|
||||
/mob/living/simple_animal/hostile/carp,\
|
||||
/mob/living/simple_animal/bot/secbot, \
|
||||
/mob/living/simple_animal/pet/fox,\
|
||||
/mob/living/simple_animal/pet/cat )
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/emplosion/eldritch
|
||||
name = "Energetic Pulse"
|
||||
invocation_type = "none"
|
||||
clothes_req = FALSE
|
||||
action_background_icon_state = "bg_ecult"
|
||||
range = -1
|
||||
include_user = TRUE
|
||||
charge_max = 300
|
||||
emp_heavy = 6
|
||||
emp_light = 10
|
||||
sound = 'sound/effects/lingscreech.ogg'
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/fire_cascade
|
||||
name = "Fire Cascade"
|
||||
desc = "creates hot turfs around you."
|
||||
school = "transmutation"
|
||||
charge_max = 300 //twice as long as mansus grasp
|
||||
clothes_req = FALSE
|
||||
invocation = "IGNIS'SAVARIN"
|
||||
invocation_type = "whisper"
|
||||
range = 4
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "fire_ring"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/fire_cascade/cast(list/targets, mob/user = usr)
|
||||
INVOKE_ASYNC(src, .proc/fire_cascade, user,range)
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/fire_cascade/proc/fire_cascade(atom/centre,max_range)
|
||||
playsound(get_turf(centre), 'sound/items/welder.ogg', 75, TRUE)
|
||||
var/_range = 1
|
||||
for(var/i = 0, i <= max_range,i++)
|
||||
for(var/turf/T in spiral_range_turfs(_range,centre))
|
||||
new /obj/effect/hotspot(T)
|
||||
T.hotspot_expose(700,50,1)
|
||||
for(var/mob/living/livies in T.contents - centre)
|
||||
livies.adjustFireLoss(10)
|
||||
_range++
|
||||
sleep(3)
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/fire_cascade/big
|
||||
range = 6
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/telepathy/eldritch
|
||||
invocation = ""
|
||||
invocation_type = "whisper"
|
||||
clothes_req = FALSE
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/fire_sworn
|
||||
name = "Oath of Fire"
|
||||
desc = "For a minute you will passively create a ring of fire around you."
|
||||
invocation = "IGNIS'AISTRA'LISTRE"
|
||||
invocation_type = "whisper"
|
||||
clothes_req = FALSE
|
||||
action_background_icon_state = "bg_ecult"
|
||||
range = -1
|
||||
include_user = TRUE
|
||||
charge_max = 700
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "fire_ring"
|
||||
///how long it lasts
|
||||
var/duration = 1 MINUTES
|
||||
///who casted it right now
|
||||
var/mob/current_user
|
||||
///Determines if you get the fire ring effect
|
||||
var/has_fire_ring = FALSE
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/fire_sworn/cast(list/targets, mob/user)
|
||||
. = ..()
|
||||
current_user = user
|
||||
has_fire_ring = TRUE
|
||||
addtimer(CALLBACK(src, .proc/remove, user), duration, TIMER_OVERRIDE|TIMER_UNIQUE)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/fire_sworn/proc/remove()
|
||||
has_fire_ring = FALSE
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/fire_sworn/process()
|
||||
. = ..()
|
||||
if(!has_fire_ring)
|
||||
return
|
||||
for(var/turf/T in range(1,current_user))
|
||||
new /obj/effect/hotspot(T)
|
||||
T.hotspot_expose(700,50,1)
|
||||
for(var/mob/living/livies in T.contents - current_user)
|
||||
livies.adjustFireLoss(5)
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/worm_contract
|
||||
name = "Force Contract"
|
||||
desc = "Forces all the worm parts to collapse onto a single turf"
|
||||
invocation_type = "none"
|
||||
clothes_req = FALSE
|
||||
action_background_icon_state = "bg_ecult"
|
||||
range = -1
|
||||
include_user = TRUE
|
||||
charge_max = 300
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "worm_contract"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/worm_contract/cast(list/targets, mob/user)
|
||||
. = ..()
|
||||
if(!istype(user,/mob/living/simple_animal/hostile/eldritch/armsy))
|
||||
to_chat(user, "<span class='userdanger'>You try to contract your muscles but nothing happens...</span>")
|
||||
var/mob/living/simple_animal/hostile/eldritch/armsy/armsy = user
|
||||
armsy.contract_next_chain_into_single_tile()
|
||||
|
||||
/obj/effect/temp_visual/cleave
|
||||
icon = 'icons/effects/eldritch.dmi'
|
||||
icon_state = "cleave"
|
||||
duration = 6
|
||||
|
||||
/obj/effect/temp_visual/eldritch_smoke
|
||||
icon = 'icons/effects/eldritch.dmi'
|
||||
icon_state = "smoke"
|
||||
duration = 10
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/fiery_rebirth
|
||||
name = "Nightwatcher's Rebirth"
|
||||
desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a person is in critical condition it finishes them off."
|
||||
invocation = "PETHRO'MINO'IGNI"
|
||||
invocation_type = "whisper"
|
||||
clothes_req = FALSE
|
||||
action_background_icon_state = "bg_ecult"
|
||||
range = -1
|
||||
include_user = TRUE
|
||||
charge_max = 600
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "smoke"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/fiery_rebirth/cast(list/targets, mob/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
for(var/mob/living/carbon/target in view(7,user))
|
||||
if(target.stat == DEAD || !target.on_fire)
|
||||
continue
|
||||
//This is essentially a death mark, use this to finish your opponent quicker.
|
||||
if(target.InCritical())
|
||||
target.death()
|
||||
target.adjustFireLoss(20)
|
||||
new /obj/effect/temp_visual/eldritch_smoke(target.drop_location())
|
||||
human_user.ExtinguishMob()
|
||||
human_user.adjustBruteLoss(-10, FALSE)
|
||||
human_user.adjustFireLoss(-10, FALSE)
|
||||
human_user.adjustStaminaLoss(-10, FALSE)
|
||||
human_user.adjustToxLoss(-10, FALSE)
|
||||
human_user.adjustOxyLoss(-10)
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/manse_link
|
||||
name = "Mansus Link"
|
||||
desc = "Piercing through reality, connecting minds. This spell allows you to add people to a mansus net, allowing them to communicate with eachother"
|
||||
school = "transmutation"
|
||||
charge_max = 300
|
||||
clothes_req = FALSE
|
||||
invocation = "SUSEI' METO MIN'TIS"
|
||||
invocation_type = "whisper"
|
||||
range = 10
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "mansus_link"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/manse_link/can_target(atom/target, mob/user, silent)
|
||||
if(!isliving(target))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/manse_link/cast(list/targets, mob/user)
|
||||
var/mob/living/simple_animal/hostile/eldritch/raw_prophet/originator = user
|
||||
|
||||
var/mob/living/target = targets[1]
|
||||
|
||||
to_chat(originator, "<span class='notice'>You begin linking [target]'s mind to yours...</span>")
|
||||
to_chat(target, "<span class='warning'>You feel your mind being pulled... connected... intertwined with the very fabric of reality...</span>")
|
||||
if(!do_after(originator, 6 SECONDS, target))
|
||||
return
|
||||
if(!originator.link_mob(target))
|
||||
to_chat(originator, "<span class='warning'>You can't seem to link [target]'s mind...</span>")
|
||||
to_chat(target, "<span class='warning'>The foreign presence leaves your mind.</span>")
|
||||
return
|
||||
to_chat(originator, "<span class='notice'>You connect [target]'s mind to your mansus link!</span>")
|
||||
|
||||
|
||||
/datum/action/innate/mansus_speech
|
||||
name = "Mansus Link"
|
||||
desc = "Send a psychic message to everyone connected to your mansus link."
|
||||
button_icon_state = "link_speech"
|
||||
icon_icon = 'icons/mob/actions/actions_slime.dmi'
|
||||
background_icon_state = "bg_ecult"
|
||||
var/mob/living/simple_animal/hostile/eldritch/raw_prophet/originator
|
||||
|
||||
/datum/action/innate/mansus_speech/New(_originator)
|
||||
. = ..()
|
||||
originator = _originator
|
||||
|
||||
/datum/action/innate/mansus_speech/Activate()
|
||||
var/mob/living/living_owner = owner
|
||||
if(!originator?.linked_mobs[living_owner])
|
||||
CRASH("Uh oh the mansus link got somehow activated without it being linked to a raw prophet or the mob not being in a list of mobs that should be able to do it.")
|
||||
|
||||
var/message = sanitize(input("Message:", "Telepathy from the Manse") as text|null)
|
||||
|
||||
if(QDELETED(living_owner))
|
||||
return
|
||||
|
||||
if(!originator?.linked_mobs[living_owner])
|
||||
to_chat(living_owner, "<span class='warning'>The link seems to have been severed...</span>")
|
||||
Remove(living_owner)
|
||||
return
|
||||
if(message)
|
||||
var/msg = "<i><font color=#568b00>\[Mansus Link\] <b>[living_owner]:</b> [message]</font></i>"
|
||||
log_directed_talk(living_owner, originator, msg, LOG_SAY, "Mansus Link")
|
||||
to_chat(originator.linked_mobs, msg)
|
||||
|
||||
for(var/dead_mob in GLOB.dead_mob_list)
|
||||
var/link = FOLLOW_LINK(dead_mob, living_owner)
|
||||
to_chat(dead_mob, "[link] [msg]")
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/trigger/blind/eldritch
|
||||
range = 10
|
||||
invocation = "AK'LIS"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/temp_visual/dir_setting/entropic
|
||||
icon = 'icons/effects/160x160.dmi'
|
||||
icon_state = "entropic_plume"
|
||||
duration = 3 SECONDS
|
||||
|
||||
/obj/effect/temp_visual/dir_setting/entropic/setDir(dir)
|
||||
. = ..()
|
||||
switch(dir)
|
||||
if(NORTH)
|
||||
pixel_x = -64
|
||||
if(SOUTH)
|
||||
pixel_x = -64
|
||||
pixel_y = -128
|
||||
if(EAST)
|
||||
pixel_y = -64
|
||||
if(WEST)
|
||||
pixel_y = -64
|
||||
pixel_x = -128
|
||||
|
||||
/obj/effect/temp_visual/glowing_rune
|
||||
icon = 'icons/effects/eldritch.dmi'
|
||||
icon_state = "small_rune_1"
|
||||
duration = 1 MINUTES
|
||||
layer = LOW_SIGIL_LAYER
|
||||
|
||||
/obj/effect/temp_visual/glowing_rune/Initialize()
|
||||
. = ..()
|
||||
pixel_y = rand(-6,6)
|
||||
pixel_x = rand(-6,6)
|
||||
icon_state = "small_rune_[rand(12)]"
|
||||
update_icon()
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/entropic_plume
|
||||
name = "Entropic Plume"
|
||||
desc = "Spews forth a disorienting plume that causes enemies to strike each other, briefly blinds them(increasing with range) and poisons them(decreasing with range). Also spreads rust in the path of the plume."
|
||||
school = "illusion"
|
||||
invocation = "RU'KAS NU'DYTI"
|
||||
invocation_type = "whisper"
|
||||
clothes_req = FALSE
|
||||
action_background_icon_state = "bg_ecult"
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "entropic_plume"
|
||||
charge_max = 300
|
||||
cone_levels = 5
|
||||
respect_density = TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/cast(list/targets,mob/user = usr)
|
||||
. = ..()
|
||||
new /obj/effect/temp_visual/dir_setting/entropic(get_step(user,user.dir), user.dir)
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/do_turf_cone_effect(turf/target_turf, level)
|
||||
. = ..()
|
||||
target_turf.rust_heretic_act()
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/do_mob_cone_effect(mob/living/victim, level)
|
||||
. = ..()
|
||||
if(victim.anti_magic_check() || IS_HERETIC(victim) || victim.mind?.has_antag_datum(/datum/antagonist/heretic_monster))
|
||||
return
|
||||
victim.apply_status_effect(STATUS_EFFECT_AMOK)
|
||||
victim.apply_status_effect(STATUS_EFFECT_CLOUDSTRUCK, (level*10))
|
||||
if(iscarbon(victim))
|
||||
var/mob/living/carbon/carbon_victim = victim
|
||||
carbon_victim.reagents.add_reagent(/datum/reagent/eldritch, min(1, 6-level))
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/calculate_cone_shape(current_level)
|
||||
if(current_level == cone_levels)
|
||||
return 5
|
||||
else if(current_level == cone_levels-1)
|
||||
return 3
|
||||
else
|
||||
return 2
|
||||
@@ -0,0 +1,43 @@
|
||||
///Tracking reasons
|
||||
/datum/antagonist/heretic_monster
|
||||
name = "Eldritch Horror"
|
||||
roundend_category = "Heretics"
|
||||
antagpanel_category = "Heretic Beast"
|
||||
antag_moodlet = /datum/mood_event/heretics
|
||||
job_rank = ROLE_HERETIC
|
||||
antag_hud_type = ANTAG_HUD_HERETIC
|
||||
antag_hud_name = "heretic_beast"
|
||||
var/datum/antagonist/master
|
||||
|
||||
/datum/antagonist/heretic_monster/admin_add(datum/mind/new_owner,mob/admin)
|
||||
new_owner.add_antag_datum(src)
|
||||
message_admins("[key_name_admin(admin)] has heresized [key_name_admin(new_owner)].")
|
||||
log_admin("[key_name(admin)] has heresized [key_name(new_owner)].")
|
||||
|
||||
/datum/antagonist/heretic_monster/greet()
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ecult_op.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change
|
||||
to_chat(owner, "<span class='boldannounce'>You became an Eldritch Horror!</span>")
|
||||
|
||||
/datum/antagonist/heretic_monster/on_removal()
|
||||
if(owner)
|
||||
to_chat(owner, "<span class='boldannounce'>Your master is no longer [master.owner.current.real_name]</span>")
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/heretic_monster/proc/set_owner(datum/antagonist/_master)
|
||||
master = _master
|
||||
var/datum/objective/master_obj = new
|
||||
master_obj.owner = src
|
||||
master_obj.explanation_text = "Assist your master in any way you can!"
|
||||
objectives += master_obj
|
||||
owner.announce_objectives()
|
||||
to_chat(owner, "<span class='boldannounce'>Your master is [master.owner.current.real_name]</span>")
|
||||
return
|
||||
|
||||
/datum/antagonist/heretic_monster/apply_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
add_antag_hud(antag_hud_type, antag_hud_name, owner.current)
|
||||
|
||||
/datum/antagonist/heretic_monster/remove_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
remove_antag_hud(antag_hud_type, owner.current)
|
||||
@@ -0,0 +1,183 @@
|
||||
/datum/eldritch_knowledge/base_ash
|
||||
name = "Nightwatcher's Secret"
|
||||
desc = "Inducts you into the Path of Ash. Allows you to transmute a match with an eldritch blade into an ashen blade."
|
||||
gain_text = "The City guard knows their watch. If you ask them at night they may tell you about the ashy lantern."
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/final/rust_final,/datum/eldritch_knowledge/final/flesh_final)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/ashen_grasp)
|
||||
required_atoms = list(/obj/item/melee/sickly_blade,/obj/item/match)
|
||||
result_atoms = list(/obj/item/melee/sickly_blade/ash)
|
||||
cost = 1
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/spell/ashen_shift
|
||||
name = "Ashen Shift"
|
||||
gain_text = "Ash is all the same, how can one man master it all?"
|
||||
desc = "A short range jaunt that will enable you to escape from danger."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash
|
||||
next_knowledge = list(/datum/eldritch_knowledge/ash_mark,/datum/eldritch_knowledge/essence,/datum/eldritch_knowledge/ashen_eyes)
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/ashen_grasp
|
||||
name = "Grasp of Ash"
|
||||
gain_text = "Gates have opened, minds have flooded, yet I remain."
|
||||
desc = "Empowers your mansus grasp to knock enemies down and throw them away."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/ashen_shift)
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/ashen_grasp/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(!iscarbon(target))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/C = target
|
||||
var/datum/status_effect/eldritch/E = C.has_status_effect(/datum/status_effect/eldritch/rust) || C.has_status_effect(/datum/status_effect/eldritch/ash) || C.has_status_effect(/datum/status_effect/eldritch/flesh)
|
||||
if(E)
|
||||
. = TRUE
|
||||
E.on_effect()
|
||||
for(var/X in user.mind.spell_list)
|
||||
if(!istype(X,/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp))
|
||||
continue
|
||||
var/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp/MG = X
|
||||
MG.charge_counter = min(round(MG.charge_counter + MG.charge_max * 0.75),MG.charge_max) // refunds 75% of charge.
|
||||
var/atom/throw_target = get_edge_target_turf(C, user.dir)
|
||||
if(!C.anchored)
|
||||
. = TRUE
|
||||
C.throw_at(throw_target, rand(4,8), 14, user)
|
||||
return
|
||||
|
||||
/datum/eldritch_knowledge/ashen_eyes
|
||||
name = "Ashen Eyes"
|
||||
gain_text = "Piercing eyes may guide me through the mundane."
|
||||
desc = "Allows you to craft thermal vision amulet by transmutating eyes with a glass shard."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/ashen_shift,/datum/eldritch_knowledge/flesh_ghoul)
|
||||
required_atoms = list(/obj/item/organ/eyes,/obj/item/shard)
|
||||
result_atoms = list(/obj/item/clothing/neck/eldritch_amulet)
|
||||
|
||||
/datum/eldritch_knowledge/ash_mark
|
||||
name = "Mark of Ash"
|
||||
gain_text = "Spread the famine."
|
||||
desc = "Your sickly blade now applies ash mark on hit. Use your mansus grasp to proc the mark. Mark of Ash causes stamina damage, and fire loss, and spreads to a nearby carbon. Damage decreases with how many times the mark has spread."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/curse/blindness)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/flesh_mark)
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/ash_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
. = ..()
|
||||
if(isliving(target))
|
||||
var/mob/living/living_target = target
|
||||
living_target.apply_status_effect(/datum/status_effect/eldritch/ash,5)
|
||||
|
||||
/datum/eldritch_knowledge/curse/blindness
|
||||
name = "Curse of Blindness"
|
||||
gain_text = "The blind man walks through the world, unnoticed by the masses."
|
||||
desc = "Curse someone with 2 minutes of complete blindness by sacrificing a pair of eyes, a screwdriver and a pool of blood, with an object that the victim has touched with their bare hands."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/item/organ/eyes,/obj/item/screwdriver,/obj/effect/decal/cleanable/blood)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/curse/corrosion,/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/curse/paralysis)
|
||||
timer = 2 MINUTES
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/curse/blindness/curse(mob/living/chosen_mob)
|
||||
. = ..()
|
||||
chosen_mob.become_blind(MAGIC_TRAIT)
|
||||
|
||||
/datum/eldritch_knowledge/curse/blindness/uncurse(mob/living/chosen_mob)
|
||||
. = ..()
|
||||
chosen_mob.cure_blind(MAGIC_TRAIT)
|
||||
|
||||
/datum/eldritch_knowledge/spell/flame_birth
|
||||
name = "Fiery Rebirth"
|
||||
gain_text = "Nightwatcher was a man of principles, and yet he arose from the chaos he vowed to protect from."
|
||||
desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a person is in critical condition it finishes them off."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/fiery_rebirth
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/final/ash_final)
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/ash_blade_upgrade
|
||||
name = "Blazing Steel"
|
||||
gain_text = "May the sun burn the heretics."
|
||||
desc = "Your blade of choice will now add firestacks."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/flame_birth)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/rust_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade)
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/ash_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
. = ..()
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/C = target
|
||||
C.adjust_fire_stacks(1)
|
||||
C.IgniteMob()
|
||||
|
||||
/datum/eldritch_knowledge/curse/corrosion
|
||||
name = "Curse of Corrosion"
|
||||
gain_text = "Cursed land, cursed man, cursed mind."
|
||||
desc = "Curse someone for 2 minutes of vomiting and major organ damage. Using a wirecutter, a spill of blood, a heart, left arm and a right arm, and an item that the victim touched with their bare hands."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/item/wirecutters,/obj/effect/decal/cleanable/blood,/obj/item/organ/heart,/obj/item/bodypart/l_arm,/obj/item/bodypart/r_arm)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/curse/blindness,/datum/eldritch_knowledge/spell/area_conversion)
|
||||
timer = 2 MINUTES
|
||||
|
||||
/datum/eldritch_knowledge/curse/corrosion/curse(mob/living/chosen_mob)
|
||||
. = ..()
|
||||
chosen_mob.apply_status_effect(/datum/status_effect/corrosion_curse)
|
||||
|
||||
/datum/eldritch_knowledge/curse/corrosion/uncurse(mob/living/chosen_mob)
|
||||
. = ..()
|
||||
chosen_mob.remove_status_effect(/datum/status_effect/corrosion_curse)
|
||||
|
||||
/datum/eldritch_knowledge/curse/paralysis
|
||||
name = "Curse of Paralysis"
|
||||
gain_text = "Corrupt their flesh, make them bleed."
|
||||
desc = "Curse someone for 5 minutes of inability to walk. Using a knife, pool of blood, left leg, right leg, a hatchet and an item that the victim touched with their bare hands. "
|
||||
cost = 1
|
||||
required_atoms = list(/obj/item/kitchen/knife,/obj/effect/decal/cleanable/blood,/obj/item/bodypart/l_leg,/obj/item/bodypart/r_leg,/obj/item/hatchet)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/curse/blindness,/datum/eldritch_knowledge/summon/raw_prophet)
|
||||
timer = 5 MINUTES
|
||||
|
||||
/datum/eldritch_knowledge/curse/paralysis/curse(mob/living/chosen_mob)
|
||||
. = ..()
|
||||
ADD_TRAIT(chosen_mob,TRAIT_PARALYSIS_L_LEG,MAGIC_TRAIT)
|
||||
ADD_TRAIT(chosen_mob,TRAIT_PARALYSIS_R_LEG,MAGIC_TRAIT)
|
||||
chosen_mob.update_mobility()
|
||||
|
||||
/datum/eldritch_knowledge/curse/paralysis/uncurse(mob/living/chosen_mob)
|
||||
. = ..()
|
||||
REMOVE_TRAIT(chosen_mob,TRAIT_PARALYSIS_L_LEG,MAGIC_TRAIT)
|
||||
REMOVE_TRAIT(chosen_mob,TRAIT_PARALYSIS_R_LEG,MAGIC_TRAIT)
|
||||
chosen_mob.update_mobility()
|
||||
|
||||
/datum/eldritch_knowledge/spell/cleave
|
||||
name = "Blood Cleave"
|
||||
gain_text = "At first I was unfamiliar with these instruments of war, but the priest told me how to use them."
|
||||
desc = "Grants a spell that will inflict wounds and bleeding upon the target, as well as in a short radius around them."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/pointed/cleave
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/entropic_plume,/datum/eldritch_knowledge/spell/flame_birth)
|
||||
|
||||
/datum/eldritch_knowledge/final/ash_final
|
||||
name = "Ashlord's Rite"
|
||||
gain_text = "The forgotten lords have spoken! The Lord of Ash has come! Fear the flame!"
|
||||
desc = "Bring three corpses onto a transmutation rune, after ascending you will become immune to fire, space, temperature and other environmental hazards. You will develop resistance to all other damages. You will be granted two spells, one which can bring forth a cascade of massive fire, and another which will surround your body in precious flames for a minute."
|
||||
required_atoms = list(/mob/living/carbon/human)
|
||||
cost = 5
|
||||
route = PATH_ASH
|
||||
var/list/trait_list = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOFIRE,TRAIT_RADIMMUNE,TRAIT_GENELESS,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_BOMBIMMUNE)
|
||||
|
||||
/datum/eldritch_knowledge/final/ash_final/on_finished_recipe(mob/living/user, list/atoms, loc)
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the blaze, for Ashbringer [user.real_name] has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/fire_cascade/big)
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/fire_sworn)
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.physiology.brute_mod *= 0.5
|
||||
H.physiology.burn_mod *= 0.5
|
||||
var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
ascension.ascended = TRUE
|
||||
for(var/X in trait_list)
|
||||
ADD_TRAIT(user,X,MAGIC_TRAIT)
|
||||
return ..()
|
||||
@@ -0,0 +1,252 @@
|
||||
/datum/eldritch_knowledge/base_flesh
|
||||
name = "Principle of Hunger"
|
||||
desc = "Inducts you into the Path of Flesh. Allows you to transmute a pool of blood with your eldritch blade into a Blade of Flesh."
|
||||
gain_text = "Hundred's of us starved, but I.. I found the strength in my greed."
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/final/ash_final,/datum/eldritch_knowledge/final/rust_final)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_grasp)
|
||||
required_atoms = list(/obj/item/melee/sickly_blade,/obj/effect/decal/cleanable/blood)
|
||||
result_atoms = list(/obj/item/melee/sickly_blade/flesh)
|
||||
cost = 1
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/flesh_ghoul
|
||||
name = "Imperfect Ritual"
|
||||
desc = "Allows you to resurrect the dead as voiceless dead by sacrificing them on the transmutation rune with a poppy. Voiceless dead are mute and have 50 HP. You can only have 2 at a time."
|
||||
gain_text = "I found notes... notes of a ritual, scraps, unfinished, and yet... I still did it."
|
||||
cost = 1
|
||||
required_atoms = list(/mob/living/carbon/human,/obj/item/reagent_containers/food/snacks/grown/poppy)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_mark,/datum/eldritch_knowledge/armor,/datum/eldritch_knowledge/ashen_eyes)
|
||||
route = PATH_FLESH
|
||||
var/max_amt = 2
|
||||
var/current_amt = 0
|
||||
var/list/ghouls = list()
|
||||
|
||||
/datum/eldritch_knowledge/flesh_ghoul/on_finished_recipe(mob/living/user,list/atoms,loc)
|
||||
var/mob/living/carbon/human/humie = locate() in atoms
|
||||
if(QDELETED(humie) || humie.stat != DEAD)
|
||||
return
|
||||
|
||||
if(length(ghouls) >= max_amt)
|
||||
return
|
||||
|
||||
if(HAS_TRAIT(humie,TRAIT_HUSK))
|
||||
return
|
||||
|
||||
humie.grab_ghost()
|
||||
|
||||
if(!humie.mind || !humie.client)
|
||||
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [humie.real_name], a voiceless dead.", ROLE_HERETIC, null, ROLE_HERETIC, 50,humie)
|
||||
if(!LAZYLEN(candidates))
|
||||
return
|
||||
var/mob/dead/observer/C = pick(candidates)
|
||||
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(humie)]) to replace an AFK player.")
|
||||
humie.ghostize(0)
|
||||
humie.key = C.key
|
||||
|
||||
ADD_TRAIT(humie,TRAIT_MUTE,MAGIC_TRAIT)
|
||||
log_game("[key_name_admin(humie)] has become a voiceless dead, their master is [user.real_name]")
|
||||
humie.revive(full_heal = TRUE, admin_revive = TRUE)
|
||||
humie.setMaxHealth(75)
|
||||
humie.health = 75 // Voiceless dead are much tougher than ghouls
|
||||
humie.become_husk()
|
||||
humie.faction |= "heretics"
|
||||
|
||||
var/datum/antagonist/heretic_monster/heretic_monster = humie.mind.add_antag_datum(/datum/antagonist/heretic_monster)
|
||||
var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
heretic_monster.set_owner(master)
|
||||
atoms -= humie
|
||||
RegisterSignal(humie,COMSIG_MOB_DEATH,.proc/remove_ghoul)
|
||||
ghouls += humie
|
||||
|
||||
/datum/eldritch_knowledge/flesh_ghoul/proc/remove_ghoul(datum/source)
|
||||
var/mob/living/carbon/human/humie = source
|
||||
ghouls -= humie
|
||||
humie.mind.remove_antag_datum(/datum/antagonist/heretic_monster)
|
||||
UnregisterSignal(source,COMSIG_MOB_DEATH)
|
||||
|
||||
/datum/eldritch_knowledge/flesh_grasp
|
||||
name = "Grasp of Flesh"
|
||||
gain_text = "'My newfound desire, it drove me to do great things,' The Priest said."
|
||||
desc = "Empowers your Mansus Grasp to be able to create a single ghoul out of a dead player. You cannot raise the same person twice. Ghouls have only 50 HP and look like husks."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_ghoul)
|
||||
var/ghoul_amt = 6
|
||||
var/list/spooky_scaries
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/flesh_grasp/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(!ishuman(target) || target == user)
|
||||
return
|
||||
var/mob/living/carbon/human/human_target = target
|
||||
var/datum/status_effect/eldritch/eldritch_effect = human_target.has_status_effect(/datum/status_effect/eldritch/rust) || human_target.has_status_effect(/datum/status_effect/eldritch/ash) || human_target.has_status_effect(/datum/status_effect/eldritch/flesh)
|
||||
if(eldritch_effect)
|
||||
. = TRUE
|
||||
eldritch_effect.on_effect()
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts)
|
||||
var/datum/wound/slash/severe/crit_wound = new
|
||||
crit_wound.apply_wound(bodypart)
|
||||
|
||||
if(QDELETED(human_target) || human_target.stat != DEAD)
|
||||
return
|
||||
|
||||
human_target.grab_ghost()
|
||||
|
||||
if(!human_target.mind || !human_target.client)
|
||||
to_chat(user, "<span class='warning'>There is no soul connected to this body...</span>")
|
||||
return
|
||||
|
||||
if(HAS_TRAIT(human_target, TRAIT_HUSK))
|
||||
to_chat(user, "<span class='warning'>You cannot revive a dead ghoul!</span>")
|
||||
return
|
||||
|
||||
if(LAZYLEN(spooky_scaries) >= ghoul_amt)
|
||||
to_chat(user, "<span class='warning'>Your patron cannot support more ghouls on this plane!</span>")
|
||||
return
|
||||
|
||||
LAZYADD(spooky_scaries, human_target)
|
||||
log_game("[key_name_admin(human_target)] has become a ghoul, their master is [user.real_name]")
|
||||
//we change it to true only after we know they passed all the checks
|
||||
. = TRUE
|
||||
RegisterSignal(human_target,COMSIG_MOB_DEATH,.proc/remove_ghoul)
|
||||
human_target.revive(full_heal = TRUE, admin_revive = TRUE)
|
||||
human_target.setMaxHealth(40)
|
||||
human_target.health = 40
|
||||
human_target.become_husk()
|
||||
human_target.faction |= "heretics"
|
||||
var/datum/antagonist/heretic_monster/heretic_monster = human_target.mind.add_antag_datum(/datum/antagonist/heretic_monster)
|
||||
var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
heretic_monster.set_owner(master)
|
||||
return
|
||||
|
||||
|
||||
/datum/eldritch_knowledge/flesh_grasp/proc/remove_ghoul(datum/source)
|
||||
var/mob/living/carbon/human/humie = source
|
||||
spooky_scaries -= humie
|
||||
humie.mind.remove_antag_datum(/datum/antagonist/heretic_monster)
|
||||
UnregisterSignal(source, COMSIG_MOB_DEATH)
|
||||
|
||||
/datum/eldritch_knowledge/flesh_mark
|
||||
name = "Mark of Flesh"
|
||||
gain_text = "I saw them, the marked ones. The screams... the silence."
|
||||
desc = "Your sickly blade now applies a mark of flesh to those cut by it. Once marked, using your Mansus Grasp upon them will cause additional bleeding from the target."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/raw_prophet)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/ash_mark)
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/flesh_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
. = ..()
|
||||
if(isliving(target))
|
||||
var/mob/living/living_target = target
|
||||
living_target.apply_status_effect(/datum/status_effect/eldritch/flesh)
|
||||
|
||||
/datum/eldritch_knowledge/flesh_blade_upgrade
|
||||
name = "Bleeding Steel"
|
||||
gain_text = "It rained blood, that's when I understood the gravekeeper's advice."
|
||||
desc = "Your blade will now cause additional bleeding to those hit by it."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/stalker)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/rust_blade_upgrade)
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/flesh_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
. = ..()
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts)
|
||||
var/datum/wound/slash/severe/crit_wound = new
|
||||
crit_wound.apply_wound(bodypart)
|
||||
|
||||
/datum/eldritch_knowledge/summon/raw_prophet
|
||||
name = "Raw Ritual"
|
||||
gain_text = "The uncanny man walks alone in the valley, I was able to call his aid."
|
||||
desc = "You can now summon a Raw Prophet using eyes, a left arm, right arm and a pool of blood using a transmutation circle. Raw prophets have increased seeing range, and can see through walls. They can jaunt long distances, though they are fragile."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/item/organ/eyes,/obj/item/bodypart/l_arm,/obj/item/bodypart/r_arm,/obj/effect/decal/cleanable/blood)
|
||||
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/raw_prophet
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_blade_upgrade,/datum/eldritch_knowledge/spell/blood_siphon,/datum/eldritch_knowledge/curse/paralysis)
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/summon/stalker
|
||||
name = "Lonely Ritual"
|
||||
gain_text = "I was able to combine my greed and desires to summon an eldritch beast I have not seen before."
|
||||
desc = "You can now summon a Stalker using a knife, a flower, a pen and a piece of paper using a transmutation circle. Stalkers possess the ability to shapeshift into various forms while assuming the vigor and powers of that form."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/item/kitchen/knife,/obj/item/reagent_containers/food/snacks/grown/poppy,/obj/item/pen,/obj/item/paper)
|
||||
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/stalker
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/summon/rusty,/datum/eldritch_knowledge/final/flesh_final)
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/summon/ashy
|
||||
name = "Ashen Ritual"
|
||||
gain_text = "I combined principle of hunger with desire of destruction. The eyeful lords have noticed me."
|
||||
desc = "You can now summon an Ashen One by transmuting a pile of ash, a head and a book using a transmutation circle. They possess the ability to jaunt short distances and create a cascade of flames."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/effect/decal/cleanable/ash,/obj/item/bodypart/head,/obj/item/book)
|
||||
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/ash_spirit
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/stalker,/datum/eldritch_knowledge/spell/flame_birth)
|
||||
|
||||
/datum/eldritch_knowledge/summon/rusty
|
||||
name = "Rusted Ritual"
|
||||
gain_text = "I combined principle of hunger with desire of corruption. The rusted hills call my name."
|
||||
desc = "You can now summon a Rust Walker transmuting a vomit pool, a head, and a book using a transmutation circle. Rust Walkers possess the ability to spread rust and can fire bolts of rust to further corrode the area."
|
||||
cost = 1
|
||||
required_atoms = list(/obj/effect/decal/cleanable/vomit,/obj/item/bodypart/head,/obj/item/book)
|
||||
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/rust_spirit
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/stalker,/datum/eldritch_knowledge/spell/entropic_plume)
|
||||
|
||||
/datum/eldritch_knowledge/spell/blood_siphon
|
||||
name = "Blood Siphon"
|
||||
gain_text = "Our blood is all the same after all, the owl told me."
|
||||
desc = "You are granted a spell that drains some of the targets health, and returns it to you. It also has a chance to transfer any wounds you possess onto the target."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/blood_siphon
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/raw_prophet,/datum/eldritch_knowledge/spell/area_conversion)
|
||||
|
||||
/datum/eldritch_knowledge/final/flesh_final
|
||||
name = "Priest's Final Hymn"
|
||||
gain_text = "Man of this world. Hear me! For the time of the lord of arms has come!"
|
||||
desc = "Bring three corpses to a transmutation rune to either ascend as The Lord of the Night or summon a single Terror of the Night, however you cannot ascend more than once."
|
||||
required_atoms = list(/mob/living/carbon/human)
|
||||
cost = 5
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/final/flesh_final/on_finished_recipe(mob/living/user, list/atoms, loc)
|
||||
var/alert_ = alert(user,"Do you want to ascend as the lord of the night or just summon a terror of the night?","...","Yes","No")
|
||||
user.SetImmobilized(10 HOURS) // no way someone will stand 10 hours in a spot, just so he can move while the alert is still showing.
|
||||
switch(alert_)
|
||||
if("No")
|
||||
var/mob/living/summoned = new /mob/living/simple_animal/hostile/eldritch/armsy(loc)
|
||||
message_admins("[summoned.name] is being summoned by [user.real_name] in [loc]")
|
||||
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [summoned.real_name]", ROLE_HERETIC, null, ROLE_HERETIC, 100,summoned)
|
||||
user.SetImmobilized(0)
|
||||
if(LAZYLEN(candidates) == 0)
|
||||
to_chat(user,"<span class='warning'>No ghost could be found...</span>")
|
||||
qdel(summoned)
|
||||
return FALSE
|
||||
var/mob/dead/observer/ghost_candidate = pick(candidates)
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the dark, for vassal of arms has ascended! Terror of the night has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
log_game("[key_name_admin(ghost_candidate)] has taken control of ([key_name_admin(summoned)]).")
|
||||
summoned.ghostize(FALSE)
|
||||
summoned.key = ghost_candidate.key
|
||||
summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster)
|
||||
var/datum/antagonist/heretic_monster/monster = summoned.mind.has_antag_datum(/datum/antagonist/heretic_monster)
|
||||
var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
monster.set_owner(master)
|
||||
master.ascended = TRUE
|
||||
if("Yes")
|
||||
var/mob/living/summoned = new /mob/living/simple_animal/hostile/eldritch/armsy/prime(loc,TRUE,10)
|
||||
summoned.ghostize(0)
|
||||
user.SetImmobilized(0)
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the dark, for king of arms has ascended! Lord of the night has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
log_game("[user.real_name] ascended as [summoned.real_name]")
|
||||
var/mob/living/carbon/carbon_user = user
|
||||
var/datum/antagonist/heretic/ascension = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
ascension.ascended = TRUE
|
||||
carbon_user.mind.transfer_to(summoned, TRUE)
|
||||
carbon_user.gib()
|
||||
|
||||
return ..()
|
||||
@@ -0,0 +1,209 @@
|
||||
/datum/eldritch_knowledge/base_rust
|
||||
name = "Blacksmith's Tale"
|
||||
desc = "Inducts you into the Path of Rust. Allows you to transmute an eldritch blade with any trash item into a Blade of Rust."
|
||||
gain_text = "'Let me tell you a story,' The Blacksmith said as he gazed into his rusty blade."
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/final/ash_final,/datum/eldritch_knowledge/final/flesh_final)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_fist)
|
||||
required_atoms = list(/obj/item/melee/sickly_blade,/obj/item/trash)
|
||||
result_atoms = list(/obj/item/melee/sickly_blade/rust)
|
||||
cost = 1
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_fist
|
||||
name = "Grasp of Rust"
|
||||
desc = "Empowers your Mansus Grasp to deal 500 damage to non-living matter and rust any structure it touches. Destroys already rusted structures."
|
||||
gain_text = "Rust grows on the ceiling of the mansus."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_regen)
|
||||
var/rust_force = 500
|
||||
var/static/list/blacklisted_turfs = typecacheof(list(/turf/closed,/turf/open/space,/turf/open/lava,/turf/open/chasm,/turf/open/floor/plating/rust))
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_fist/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
var/datum/status_effect/eldritch/E = H.has_status_effect(/datum/status_effect/eldritch/rust) || H.has_status_effect(/datum/status_effect/eldritch/ash) || H.has_status_effect(/datum/status_effect/eldritch/flesh)
|
||||
if(E)
|
||||
E.on_effect()
|
||||
H.adjustOrganLoss(pick(ORGAN_SLOT_BRAIN,ORGAN_SLOT_EARS,ORGAN_SLOT_EYES,ORGAN_SLOT_LIVER,ORGAN_SLOT_LUNGS,ORGAN_SLOT_STOMACH,ORGAN_SLOT_HEART),25)
|
||||
target.rust_heretic_act()
|
||||
target.emp_act(EMP_HEAVY)
|
||||
return TRUE
|
||||
|
||||
/datum/eldritch_knowledge/spell/area_conversion
|
||||
name = "Aggressive Spread"
|
||||
desc = "Spreads rust to nearby turfs. Destroys already rusted walls."
|
||||
gain_text = "All wise men know not to touch the bound king."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/aoe_turf/rust_conversion
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_blade_upgrade,/datum/eldritch_knowledge/curse/corrosion,/datum/eldritch_knowledge/spell/blood_siphon,/datum/eldritch_knowledge/spell/rust_wave)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/spell/rust_wave
|
||||
name = "Patron's Reach"
|
||||
desc = "You can now send a bolt of rust that corrupts the immediate area, and poisons the first target hit."
|
||||
gain_text = "Messengers of hope fear the rustbringer."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/aimed/rust_wave
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_regen
|
||||
name = "Leeching Walk"
|
||||
desc = "Passively heals you when you are on rusted tiles."
|
||||
gain_text = "'The strength was unparalleled, unnatural.' The Blacksmith was smiling."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/armor,/datum/eldritch_knowledge/essence)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_regen/on_life(mob/user)
|
||||
. = ..()
|
||||
var/turf/user_loc_turf = get_turf(user)
|
||||
if(!istype(user_loc_turf, /turf/open/floor/plating/rust) || !isliving(user))
|
||||
return
|
||||
var/mob/living/living_user = user
|
||||
living_user.adjustBruteLoss(-3, FALSE)
|
||||
living_user.adjustFireLoss(-3, FALSE)
|
||||
living_user.adjustToxLoss(-3, FALSE)
|
||||
living_user.adjustOxyLoss(-1, FALSE)
|
||||
living_user.adjustStaminaLoss(-6)
|
||||
|
||||
/datum/eldritch_knowledge/rust_mark
|
||||
name = "Mark of Rust"
|
||||
desc = "Your eldritch blade now applies a rust mark. Rust marks have a chance to deal between 0 to 200 damage to 75% of enemies items. To activate the mark use your Mansus Grasp on it."
|
||||
gain_text = "Lords of the depths help those in dire need at a cost."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/area_conversion)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/ash_mark,/datum/eldritch_knowledge/flesh_mark)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
. = ..()
|
||||
if(isliving(target))
|
||||
var/mob/living/living_target = target
|
||||
living_target.apply_status_effect(/datum/status_effect/eldritch/rust)
|
||||
|
||||
/datum/eldritch_knowledge/rust_blade_upgrade
|
||||
name = "Toxic Steel"
|
||||
gain_text = "Let the blade guide you through the flesh."
|
||||
desc = "Your blade of choice will now add toxin to enemies bloodstream."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/entropic_plume)
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
. = ..()
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
carbon_target.reagents.add_reagent(/datum/reagent/eldritch, 5)
|
||||
|
||||
/datum/eldritch_knowledge/spell/entropic_plume
|
||||
name = "Entropic Plume"
|
||||
desc = "You can now send a befuddling plume that blinds, poisons and makes enemies strike each other, while also converting the immediate area into rust."
|
||||
gain_text = "Messengers of hope fear the rustbringer."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/cone/staggered/entropic_plume
|
||||
next_knowledge = list(/datum/eldritch_knowledge/final/rust_final,/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/rusty)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/armor
|
||||
name = "Armorer's Ritual"
|
||||
desc = "You can now create eldritch armor using a built table and a gas mask on top of a transmutation rune."
|
||||
gain_text = "For I am the heir to the throne of doom."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_regen,/datum/eldritch_knowledge/flesh_ghoul)
|
||||
required_atoms = list(/obj/structure/table,/obj/item/clothing/mask/gas)
|
||||
result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch)
|
||||
|
||||
/datum/eldritch_knowledge/essence
|
||||
name = "Priest's Ritual"
|
||||
desc = "You can now transmute a tank of water into a bottle of eldritch fluid."
|
||||
gain_text = "This is an old recipe, i got it from an owl."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_regen,/datum/eldritch_knowledge/spell/ashen_shift)
|
||||
required_atoms = list(/obj/structure/reagent_dispensers/watertank)
|
||||
result_atoms = list(/obj/item/reagent_containers/glass/beaker/eldritch)
|
||||
|
||||
/datum/eldritch_knowledge/final/rust_final
|
||||
name = "Rustbringer's Oath"
|
||||
desc = "Bring three corpses onto a transmutation rune. After you finish the ritual, rust will now automatically spread from the rune. Your healing on rust is also tripled, while you become more resilient overall."
|
||||
gain_text = "Champion of rust. Corruptor of steel. Fear the dark for Rustbringer has come!"
|
||||
cost = 5
|
||||
required_atoms = list(/mob/living/carbon/human)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/final/rust_final/on_finished_recipe(mob/living/user, list/atoms, loc)
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.physiology.brute_mod *= 0.5
|
||||
H.physiology.burn_mod *= 0.5
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the decay, for Rustbringer [user.real_name] has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
new /datum/rust_spread(loc)
|
||||
var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
ascension.ascended = TRUE
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/eldritch_knowledge/final/rust_final/on_life(mob/user)
|
||||
. = ..()
|
||||
if(!finished)
|
||||
return
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
human_user.adjustBruteLoss(-6, FALSE)
|
||||
human_user.adjustFireLoss(-6, FALSE)
|
||||
human_user.adjustToxLoss(-6, FALSE)
|
||||
human_user.adjustOxyLoss(-6, FALSE)
|
||||
human_user.adjustStaminaLoss(-20)
|
||||
|
||||
|
||||
/**
|
||||
* #Rust spread datum
|
||||
*
|
||||
* Simple datum that automatically spreads rust around it
|
||||
*
|
||||
* Simple implementation of automatically growing entity
|
||||
*/
|
||||
/datum/rust_spread
|
||||
var/list/edge_turfs = list()
|
||||
var/list/turfs = list()
|
||||
var/static/list/blacklisted_turfs = typecacheof(list(/turf/open/indestructible,/turf/closed/indestructible,/turf/open/space,/turf/open/lava,/turf/open/chasm))
|
||||
var/spread_per_tick = 6
|
||||
|
||||
|
||||
/datum/rust_spread/New(loc)
|
||||
. = ..()
|
||||
var/turf/turf_loc = get_turf(loc)
|
||||
turf_loc.rust_heretic_act()
|
||||
turfs += turf_loc
|
||||
START_PROCESSING(SSprocessing,src)
|
||||
|
||||
|
||||
/datum/rust_spread/Destroy(force, ...)
|
||||
STOP_PROCESSING(SSprocessing,src)
|
||||
return ..()
|
||||
|
||||
/datum/rust_spread/process()
|
||||
compile_turfs()
|
||||
var/turf/T
|
||||
for(var/i in 0 to spread_per_tick)
|
||||
T = pick(edge_turfs)
|
||||
T.rust_heretic_act()
|
||||
turfs += get_turf(T)
|
||||
|
||||
/**
|
||||
* Compile turfs
|
||||
*
|
||||
* Recreates all edge_turfs as well as normal turfs.
|
||||
*/
|
||||
/datum/rust_spread/proc/compile_turfs()
|
||||
edge_turfs = list()
|
||||
for(var/X in turfs)
|
||||
if(!istype(X,/turf/closed/wall/rust) && !istype(X,/turf/closed/wall/r_wall/rust) && !istype(X,/turf/open/floor/plating/rust))
|
||||
turfs -=X
|
||||
continue
|
||||
for(var/turf/T in range(1,X))
|
||||
if(T in turfs)
|
||||
continue
|
||||
if(is_type_in_typecache(T,blacklisted_turfs))
|
||||
continue
|
||||
edge_turfs += T
|
||||
@@ -28,6 +28,7 @@
|
||||
throwforce = 0
|
||||
blood_volume = 0
|
||||
has_field_of_vision = FALSE //we are a spoopy ghost
|
||||
rad_flags = RAD_NO_CONTAMINATE | RAD_PROTECT_CONTENTS
|
||||
|
||||
see_in_dark = 8
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
icon_state = "daemon"
|
||||
icon_living = "daemon"
|
||||
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
speed = 1
|
||||
a_intent = INTENT_HARM
|
||||
stop_automated_movement = 1
|
||||
|
||||
@@ -161,12 +161,12 @@
|
||||
|
||||
/datum/spellbook_entry/blind
|
||||
name = "Blind"
|
||||
spell_type = /obj/effect/proc_holder/spell/targeted/trigger/blind
|
||||
spell_type = /obj/effect/proc_holder/spell/pointed/trigger/blind
|
||||
cost = 1
|
||||
|
||||
/datum/spellbook_entry/mindswap
|
||||
name = "Mindswap"
|
||||
spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer
|
||||
spell_type = /obj/effect/proc_holder/spell/pointed/mind_transfer
|
||||
category = "Mobility"
|
||||
|
||||
/datum/spellbook_entry/forcewall
|
||||
@@ -246,7 +246,7 @@
|
||||
|
||||
/datum/spellbook_entry/barnyard
|
||||
name = "Barnyard Curse"
|
||||
spell_type = /obj/effect/proc_holder/spell/targeted/barnyardcurse
|
||||
spell_type = /obj/effect/proc_holder/spell/pointed/barnyardcurse
|
||||
|
||||
/datum/spellbook_entry/charge
|
||||
name = "Charge"
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.")
|
||||
if(APPRENTICE_ROBELESS)
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/pointed/mind_transfer(null))
|
||||
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.")
|
||||
if(APPRENTICE_MARTIAL)
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/touch/nuclear_fist(null))
|
||||
|
||||
@@ -232,23 +232,6 @@
|
||||
/obj/item/organ/genital/proc/get_features(mob/living/carbon/human/H)
|
||||
return
|
||||
|
||||
|
||||
//procs to handle sprite overlays being applied to humans
|
||||
|
||||
/mob/living/carbon/human/equip_to_slot(obj/item/I, slot)
|
||||
. = ..()
|
||||
if(!. && I && slot && !(slot in GLOB.no_genitals_update_slots)) //the item was successfully equipped, and the chosen slot wasn't merely storage, hands or cuffs.
|
||||
update_genitals()
|
||||
|
||||
/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE)
|
||||
var/no_update = FALSE
|
||||
if(!I || I == l_store || I == r_store || I == s_store || I == handcuffed || I == legcuffed || get_held_index_of_item(I)) //stops storages, cuffs and held items from triggering it.
|
||||
no_update = TRUE
|
||||
. = ..()
|
||||
if(!. || no_update)
|
||||
return
|
||||
update_genitals()
|
||||
|
||||
/mob/living/carbon/human/proc/update_genitals()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
|
||||
@@ -122,6 +122,7 @@ obj/item/dildo/flared/huge
|
||||
name = "literal horse cock"
|
||||
desc = "THIS THING IS HUGE!"
|
||||
dildo_size = 4
|
||||
force = 10
|
||||
|
||||
obj/item/dildo/custom
|
||||
name = "customizable dildo"
|
||||
|
||||
@@ -388,3 +388,9 @@
|
||||
Insert("polycrystal", 'icons/obj/telescience.dmi', "polycrystal")
|
||||
..()
|
||||
|
||||
/datum/asset/spritesheet/mafia
|
||||
name = "mafia"
|
||||
|
||||
/datum/asset/spritesheet/mafia/register()
|
||||
InsertAll("", 'icons/obj/mafia.dmi')
|
||||
..()
|
||||
|
||||
@@ -310,7 +310,7 @@
|
||||
/datum/export/gear/combatgloves
|
||||
cost = 80
|
||||
unit_name = "combat gloves"
|
||||
export_types = list(/obj/item/clothing/gloves/tackler/combat, /obj/item/clothing/gloves/tackler/dolphin, /obj/item/clothing/gloves/fingerless/pugilist/rapid, /obj/item/clothing/gloves/krav_maga)
|
||||
export_types = list(/obj/item/clothing/gloves/tackler/combat, /obj/item/clothing/gloves/tackler/dolphin, /obj/item/clothing/gloves/krav_maga)
|
||||
include_subtypes = TRUE
|
||||
|
||||
/datum/export/gear/bonegloves
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
/datum/export/robotics
|
||||
include_subtypes = FALSE
|
||||
k_elasticity = 1/50
|
||||
k_elasticity = 1/200
|
||||
|
||||
/datum/export/implant
|
||||
include_subtypes = FALSE
|
||||
k_elasticity = 1/50
|
||||
k_elasticity = 1/200
|
||||
|
||||
/datum/export/organs
|
||||
include_subtypes = TRUE
|
||||
@@ -34,8 +34,8 @@
|
||||
export_types = list(/obj/item/organ/cyberimp/brain/anti_stun)
|
||||
|
||||
/datum/export/implant/breathtube
|
||||
cost = 150
|
||||
k_elasticity = 300/20 //Large before depleating
|
||||
cost = 175
|
||||
k_elasticity = 1/350 //Large before depleating
|
||||
unit_name = "breath implant"
|
||||
export_types = list(/obj/item/organ/cyberimp/mouth/breathing_tube)
|
||||
|
||||
@@ -71,35 +71,35 @@
|
||||
export_types = list(/obj/item/organ/cyberimp/arm/gun/laser, /obj/item/organ/cyberimp/arm/gun/taser, /obj/item/organ/cyberimp/arm/esword, /obj/item/organ/cyberimp/arm/medibeam, /obj/item/organ/cyberimp/arm/combat, /obj/item/organ/cyberimp/arm/flash, /obj/item/organ/cyberimp/arm/baton)
|
||||
include_subtypes = TRUE
|
||||
|
||||
/datum/export/orgains/heart
|
||||
/datum/export/organs/heart
|
||||
cost = 250
|
||||
unit_name = "heart"
|
||||
export_types = list(/obj/item/organ/heart)
|
||||
exclude_types = list(/obj/item/organ/heart/cursed, /obj/item/organ/heart/cybernetic)
|
||||
|
||||
/datum/export/orgains/tongue
|
||||
/datum/export/organs/tongue
|
||||
cost = 75
|
||||
unit_name = "tongue"
|
||||
export_types = list(/obj/item/organ/tongue)
|
||||
|
||||
/datum/export/orgains/eyes
|
||||
/datum/export/organs/eyes
|
||||
cost = 50 //So many things take your eyes out anyways
|
||||
unit_name = "eyes"
|
||||
export_types = list(/obj/item/organ/eyes)
|
||||
exclude_types = list(/obj/item/organ/eyes/robotic)
|
||||
|
||||
/datum/export/orgains/stomach
|
||||
/datum/export/organs/stomach
|
||||
cost = 50 //can be replaced
|
||||
unit_name = "stomach"
|
||||
export_types = list(/obj/item/organ/stomach)
|
||||
|
||||
/datum/export/orgains/lungs
|
||||
/datum/export/organs/lungs
|
||||
cost = 150
|
||||
unit_name = "lungs"
|
||||
export_types = list(/obj/item/organ/lungs)
|
||||
exclude_types = list(/obj/item/organ/lungs/cybernetic, /obj/item/organ/lungs/cybernetic/upgraded)
|
||||
|
||||
/datum/export/orgains/liver
|
||||
/datum/export/organs/liver
|
||||
cost = 175
|
||||
unit_name = "liver"
|
||||
export_types = list(/obj/item/organ/liver)
|
||||
@@ -116,33 +116,33 @@
|
||||
unit_name = "upgraded cybernetic organ"
|
||||
export_types = list(/obj/item/organ/lungs/cybernetic/upgraded, /obj/item/organ/liver/cybernetic/upgraded)
|
||||
|
||||
/datum/export/organs/tail //Shhh
|
||||
/datum/export/organs/tail // yeah have fun pulling this off someone without catching a bwoink
|
||||
cost = 500
|
||||
unit_name = "error shipment failer"
|
||||
unit_name = "organic tail"
|
||||
export_types = list(/obj/item/organ/tail)
|
||||
|
||||
/datum/export/orgains/vocal_cords
|
||||
/datum/export/organs/vocal_cords
|
||||
cost = 500
|
||||
unit_name = "vocal cords"
|
||||
export_types = list(/obj/item/organ/vocal_cords) //These are gotten via different races
|
||||
|
||||
/datum/export/robotics/lims
|
||||
cost = 30
|
||||
unit_name = "robotic lim replacement"
|
||||
/datum/export/robotics/limbs
|
||||
cost = 60
|
||||
unit_name = "robotic limb replacement"
|
||||
export_types = list(/obj/item/bodypart/l_arm/robot, /obj/item/bodypart/r_arm/robot, /obj/item/bodypart/l_leg/robot, /obj/item/bodypart/r_leg/robot, /obj/item/bodypart/chest/robot, /obj/item/bodypart/head/robot)
|
||||
|
||||
/datum/export/robotics/surpluse
|
||||
cost = 40
|
||||
unit_name = "robotic lim replacement"
|
||||
cost = 50
|
||||
unit_name = "robotic limb replacement"
|
||||
export_types = list(/obj/item/bodypart/l_arm/robot/surplus, /obj/item/bodypart/r_arm/robot/surplus, /obj/item/bodypart/l_leg/robot/surplus, /obj/item/bodypart/r_leg/robot/surplus)
|
||||
|
||||
/datum/export/robotics/surplus_upgraded
|
||||
cost = 50
|
||||
unit_name = "upgraded robotic lim replacement"
|
||||
cost = 80
|
||||
unit_name = "upgraded robotic limb replacement"
|
||||
export_types = list(/obj/item/bodypart/l_arm/robot/surplus_upgraded, /obj/item/bodypart/r_arm/robot/surplus_upgraded, /obj/item/bodypart/l_leg/robot/surplus_upgraded, /obj/item/bodypart/r_leg/robot/surplus_upgraded)
|
||||
|
||||
/datum/export/robotics/surgery_gear_basic
|
||||
cost = 10
|
||||
cost = 50
|
||||
unit_name = "surgery tool"
|
||||
export_types = list(/obj/item/retractor, /obj/item/hemostat, /obj/item/cautery, /obj/item/surgicaldrill, /obj/item/scalpel, /obj/item/circular_saw, /obj/item/bonesetter, /obj/item/surgical_drapes)
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
export_types = list(/obj/item/stock_parts/cell/high/slime/hypercharged)
|
||||
|
||||
//Glass working stuff
|
||||
// i'd just like to say how i despise the previous coder's fetish for their funny glasswork
|
||||
|
||||
/datum/export/glasswork_dish
|
||||
cost = 300
|
||||
|
||||
@@ -138,11 +138,11 @@
|
||||
message = "of bones"
|
||||
export_types = list(/obj/item/stack/sheet/bone)
|
||||
|
||||
/datum/export/stack/bronze
|
||||
/datum/export/stack/sheet/bronze
|
||||
unit_name = "tiles"
|
||||
cost = 5
|
||||
message = "of brozne"
|
||||
export_types = list(/obj/item/stack/tile/bronze)
|
||||
export_types = list(/obj/item/stack/sheet/bronze)
|
||||
|
||||
/datum/export/stack/brass
|
||||
unit_name = "tiles"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/datum/export/weapon/makeshift_shield
|
||||
cost = 30
|
||||
unit_name = "unknown shield"
|
||||
unit_name = "nonstandard shield"
|
||||
export_types = list(/obj/item/shield/riot, /obj/item/shield/riot/roman, /obj/item/shield/riot/buckler, /obj/item/shield/makeshift)
|
||||
|
||||
/datum/export/weapon/riot_shield
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
/datum/export/weapon/taser
|
||||
cost = 200
|
||||
unit_name = "advanced taser"
|
||||
unit_name = "hybrid taser"
|
||||
export_types = list(/obj/item/gun/energy/e_gun/advtaser)
|
||||
|
||||
/datum/export/weapon/laser
|
||||
@@ -104,12 +104,12 @@
|
||||
|
||||
/datum/export/weapon/aeg
|
||||
cost = 200 //Endless power
|
||||
unit_name = "advance engery gun"
|
||||
unit_name = "advanced energy gun"
|
||||
export_types = list(/obj/item/gun/energy/e_gun/nuclear)
|
||||
|
||||
/datum/export/weapon/deconer
|
||||
cost = 600
|
||||
unit_name = "deconer"
|
||||
unit_name = "decloner"
|
||||
export_types = list(/obj/item/gun/energy/decloner)
|
||||
|
||||
/datum/export/weapon/ntsniper
|
||||
@@ -123,9 +123,8 @@
|
||||
export_types = list(/obj/item/gun/syringe/rapidsyringe)
|
||||
|
||||
/datum/export/weapon/temp_gun
|
||||
cost = 175 //Its just smaller
|
||||
cost = 175
|
||||
unit_name = "small temperature gun"
|
||||
k_elasticity = 1/30 //Its just a smaller temperature gun, easy to mass make
|
||||
export_types = list(/obj/item/gun/energy/temperature)
|
||||
|
||||
/datum/export/weapon/flowergun
|
||||
@@ -139,8 +138,7 @@
|
||||
export_types = list(/obj/item/gun/energy/xray)
|
||||
|
||||
/datum/export/weapon/ioncarbine
|
||||
cost = 200
|
||||
k_elasticity = 1/30 //Its just a smaller temperature gun, easy to mass make
|
||||
cost = 200
|
||||
unit_name = "ion carbine"
|
||||
export_types = list(/obj/item/gun/energy/ionrifle/carbine)
|
||||
|
||||
@@ -194,7 +192,7 @@
|
||||
export_types = list(/obj/item/firing_pin/test_range)
|
||||
|
||||
/datum/export/weapon/techslug
|
||||
cost = 25
|
||||
cost = 30
|
||||
k_elasticity = 0
|
||||
unit_name = "advanced shotgun shell"
|
||||
export_types = list(/obj/item/ammo_casing/shotgun/dragonsbreath, /obj/item/ammo_casing/shotgun/meteorslug, /obj/item/ammo_casing/shotgun/pulseslug, /obj/item/ammo_casing/shotgun/frag12, /obj/item/ammo_casing/shotgun/ion, /obj/item/ammo_casing/shotgun/laserslug)
|
||||
@@ -215,7 +213,7 @@
|
||||
|
||||
/datum/export/weapon/bow_teaching
|
||||
cost = 500
|
||||
unit_name = "stone tablets"
|
||||
unit_name = "bowyery tablet"
|
||||
export_types = list(/obj/item/book/granter/crafting_recipe/bone_bow)
|
||||
|
||||
/datum/export/weapon/quiver
|
||||
@@ -230,48 +228,48 @@
|
||||
|
||||
/datum/export/weapon/pistol
|
||||
cost = 120
|
||||
unit_name = "illegal firearm"
|
||||
unit_name = "nonstandard sidearm"
|
||||
export_types = list(/obj/item/gun/ballistic/automatic/pistol)
|
||||
|
||||
/datum/export/weapon/revolver
|
||||
cost = 200
|
||||
unit_name = "large handgun"
|
||||
unit_name = "large-caliber revolver"
|
||||
export_types = list(/obj/item/gun/ballistic/revolver)
|
||||
exclude_types = list(/obj/item/gun/ballistic/revolver/russian, /obj/item/gun/ballistic/revolver/doublebarrel)
|
||||
|
||||
/datum/export/weapon/rocketlauncher
|
||||
cost = 1000
|
||||
unit_name = "rocketlauncher"
|
||||
unit_name = "PML-9 rocket-propelled grenade launcher"
|
||||
export_types = list(/obj/item/gun/ballistic/rocketlauncher)
|
||||
|
||||
/datum/export/weapon/antitank
|
||||
cost = 300
|
||||
unit_name = "hand cannon"
|
||||
unit_name = "anti-tank pistol"
|
||||
export_types = list(/obj/item/gun/ballistic/automatic/pistol/antitank/syndicate)
|
||||
|
||||
/datum/export/weapon/clownstuff
|
||||
cost = 500
|
||||
unit_name = "clown war tech"
|
||||
export_types = list(/obj/item/pneumatic_cannon/pie/selfcharge, /obj/item/shield/energy/bananium, /obj/item/melee/transforming/energy/sword/bananium, )
|
||||
unit_name = "clown combat equipment"
|
||||
export_types = list(/obj/item/pneumatic_cannon/pie/selfcharge, /obj/item/shield/energy/bananium, /obj/item/melee/transforming/energy/sword/bananium)
|
||||
|
||||
/datum/export/weapon/bulldog
|
||||
cost = 400
|
||||
unit_name = "drum loaded shotgun"
|
||||
unit_name = "drum-fed compact combat shotgun"
|
||||
export_types = list(/obj/item/gun/ballistic/automatic/shotgun/bulldog)
|
||||
|
||||
/datum/export/weapon/smg
|
||||
cost = 350
|
||||
unit_name = "automatic c-20r"
|
||||
unit_name = "C-20r sub-machine gun"
|
||||
export_types = list(/obj/item/gun/ballistic/automatic/c20r)
|
||||
|
||||
/datum/export/weapon/duelsaber
|
||||
cost = 360 //Get it?
|
||||
unit_name = "energy saber"
|
||||
cost = 360
|
||||
unit_name = "double-bladed energy saber"
|
||||
export_types = list(/obj/item/dualsaber)
|
||||
|
||||
/datum/export/weapon/esword
|
||||
cost = 130
|
||||
unit_name = "energy sword"
|
||||
unit_name = "energy saber"
|
||||
export_types = list(/obj/item/melee/transforming/energy/sword/cx/traitor, /obj/item/melee/transforming/energy/sword/saber)
|
||||
|
||||
/datum/export/weapon/rapier
|
||||
@@ -286,32 +284,32 @@
|
||||
|
||||
/datum/export/weapon/gloves
|
||||
cost = 90
|
||||
unit_name = "star struck gloves"
|
||||
unit_name = "anomalous armwraps"
|
||||
export_types = list(/obj/item/clothing/gloves/fingerless/pugilist/rapid)
|
||||
|
||||
/datum/export/weapon/l6
|
||||
cost = 500
|
||||
unit_name = "law 6 saw"
|
||||
unit_name = "Aussec Armory L6 SAW"
|
||||
export_types = list(/obj/item/gun/ballistic/automatic/l6_saw)
|
||||
|
||||
/datum/export/weapon/m90
|
||||
cost = 400
|
||||
unit_name = "assault class weapon"
|
||||
unit_name = "M90-gl carbine"
|
||||
export_types = list(/obj/item/gun/ballistic/automatic/m90)
|
||||
|
||||
/datum/export/weapon/powerglove
|
||||
cost = 100
|
||||
unit_name = "hydraulic glove"
|
||||
unit_name = "pneumatic gauntlet"
|
||||
export_types = list(/obj/item/melee/powerfist)
|
||||
|
||||
/datum/export/weapon/sniper
|
||||
cost = 750
|
||||
unit_name = ".50 sniper"
|
||||
unit_name = "anti-materiel rifle"
|
||||
export_types = list(/obj/item/gun/ballistic/automatic/sniper_rifle/syndicate)
|
||||
|
||||
/datum/export/weapon/ebow
|
||||
cost = 600
|
||||
unit_name = "mini crossbow"
|
||||
unit_name = "compact energy crossbow"
|
||||
export_types = list(/obj/item/gun/energy/kinetic_accelerator/crossbow)
|
||||
|
||||
/datum/export/weapon/m10mm
|
||||
@@ -333,12 +331,12 @@
|
||||
|
||||
/datum/export/weapon/smg_mag
|
||||
cost = 45
|
||||
unit_name = "smg magazine"
|
||||
unit_name = "SMG/carbine magazine"
|
||||
export_types = list(/obj/item/ammo_box/magazine/smgm45, /obj/item/ammo_box/magazine/m556)
|
||||
|
||||
/datum/export/weapon/l6sawammo
|
||||
cost = 60
|
||||
unit_name = "law 6 saw ammo box"
|
||||
unit_name = "L6 SAW ammo box"
|
||||
export_types = list(/obj/item/ammo_box/magazine/mm195x129)
|
||||
include_subtypes = TRUE
|
||||
|
||||
@@ -355,13 +353,13 @@
|
||||
|
||||
/datum/export/weapon/fletcher_ammo
|
||||
cost = 60
|
||||
unit_name = "illegal ammo magazines"
|
||||
unit_name = "flechette launcher magazine"
|
||||
export_types = list(/obj/item/ammo_box/magazine/flechette)
|
||||
include_subtypes = TRUE
|
||||
|
||||
/datum/export/weapon/dj_a_pizzabomb
|
||||
cost = -6000
|
||||
unit_name = "Repair Costs"
|
||||
unit_name = "undeclared ordinance and subsequent repair costs"
|
||||
export_types = list(/obj/item/pizzabox/bomb, /obj/item/sbeacondrop/bomb)
|
||||
|
||||
/datum/export/weapon/real_toolbox
|
||||
@@ -371,12 +369,12 @@
|
||||
|
||||
/datum/export/weapon/melee
|
||||
cost = 50
|
||||
unit_name = "unlisted weapon"
|
||||
unit_name = "any other melee weapon"
|
||||
export_types = list(/obj/item/melee)
|
||||
include_subtypes = TRUE
|
||||
|
||||
/datum/export/weapon/gun
|
||||
cost = 50
|
||||
unit_name = "unlisted weapon"
|
||||
unit_name = "any other weapon"
|
||||
export_types = list(/obj/item/gun)
|
||||
include_subtypes = TRUE
|
||||
|
||||
@@ -158,11 +158,10 @@
|
||||
|
||||
/datum/supply_pack/security/armory/russian
|
||||
name = "Russian Surplus Crate"
|
||||
desc = "Hello Comrade, we have the most modern russian military equipment the black market can offer, for the right price of course. Sadly we couldnt remove the lock so it requires Armory access to open."
|
||||
desc = "Hello Comrade, we have the most modern Russian military equipment the black market can offer, for the right price of course. Sadly we couldn't remove the lock so it requires Armory access to open."
|
||||
cost = 7500
|
||||
contraband = TRUE
|
||||
contains = list(/obj/item/reagent_containers/food/snacks/rationpack,
|
||||
/obj/item/ammo_box/magazine/m10mm/rifle,
|
||||
/obj/item/clothing/suit/armor/vest/russian,
|
||||
/obj/item/clothing/head/helmet/rus_helmet,
|
||||
/obj/item/clothing/shoes/russian,
|
||||
@@ -172,7 +171,10 @@
|
||||
/obj/item/clothing/mask/russian_balaclava,
|
||||
/obj/item/clothing/head/helmet/rus_ushanka,
|
||||
/obj/item/clothing/suit/armor/vest/russian_coat,
|
||||
/obj/item/gun/ballistic/automatic/surplus)
|
||||
/obj/effect/spawner/bundle/crate/mosin,
|
||||
/obj/item/storage/toolbox/ammo,
|
||||
/obj/effect/spawner/bundle/crate/surplusrifle,
|
||||
/obj/item/storage/toolbox/ammo/surplus)
|
||||
crate_name = "surplus military crate"
|
||||
|
||||
/datum/supply_pack/security/armory/russian/fill(obj/structure/closet/crate/C)
|
||||
@@ -223,3 +225,10 @@
|
||||
/obj/item/ammo_box/magazine/wt550m9/wtrubber,
|
||||
/obj/item/ammo_box/magazine/wt550m9/wtrubber)
|
||||
crate_name = "auto rifle ammo crate"
|
||||
|
||||
/datum/supply_pack/security/armory/hell_single
|
||||
name = "Hellgun Single-Pack"
|
||||
crate_name = "hellgun crate"
|
||||
desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids."
|
||||
cost = 1500
|
||||
contains = list(/obj/item/gun/energy/laser/hellgun)
|
||||
|
||||
@@ -76,12 +76,6 @@
|
||||
cost = 200
|
||||
contains = list(/obj/item/toy/beach_ball)
|
||||
|
||||
/datum/supply_pack/goody/hell_single
|
||||
name = "Hellgun Single-Pack"
|
||||
desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids."
|
||||
cost = 1500
|
||||
contains = list(/obj/item/gun/energy/laser/hellgun)
|
||||
|
||||
/datum/supply_pack/goody/medipen_twopak
|
||||
name = "Medipen Two-Pak"
|
||||
desc = "Contains one standard epinephrine medipen and one standard emergency first-aid kit medipen. For when you want to prepare for the worst."
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
crate_name = "surplus russian clothing"
|
||||
crate_type = /obj/structure/closet/crate/internals
|
||||
|
||||
/datum/supply_pack/security/russianmosin
|
||||
/datum/supply_pack/security/russian_partisan
|
||||
name = "Russian Partisan Gear"
|
||||
desc = "An old russian partisan equipment crate, comes with a full russian outfit, a loaded surplus rifle and a second magazine."
|
||||
contraband = TRUE
|
||||
@@ -112,12 +112,17 @@
|
||||
/obj/item/clothing/suit/armor/bulletproof,
|
||||
/obj/item/clothing/head/helmet/alt,
|
||||
/obj/item/clothing/gloves/tackler/combat/insulated,
|
||||
/obj/item/clothing/mask/gas,
|
||||
/obj/item/ammo_box/magazine/m10mm/rifle,
|
||||
/obj/item/gun/ballistic/automatic/surplus)
|
||||
/obj/item/clothing/mask/gas)
|
||||
crate_name = "surplus russian gear"
|
||||
crate_type = /obj/structure/closet/crate/internals
|
||||
|
||||
/datum/supply_pack/security/russian_partisan/fill(obj/structure/closet/crate/C)
|
||||
..()
|
||||
if(prob(20))
|
||||
new /obj/effect/spawner/bundle/crate/mosin(C)
|
||||
else
|
||||
new /obj/effect/spawner/bundle/crate/surplusrifle(C)
|
||||
|
||||
/datum/supply_pack/security/sechardsuit
|
||||
name = "Sec Hardsuit"
|
||||
desc = "One Sec Hardsuit with a small air tank and mask."
|
||||
|
||||
@@ -993,3 +993,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
screen -= S
|
||||
qdel(S)
|
||||
char_render_holders = null
|
||||
|
||||
/client/proc/can_have_part(part_name)
|
||||
return prefs.pref_species.mutant_bodyparts[part_name] || (part_name in GLOB.unlocked_mutant_parts)
|
||||
|
||||
@@ -507,310 +507,19 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
|
||||
if(pref_species.mutant_bodyparts["tail_lizard"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Tail</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=tail_lizard;task=input'>[features["tail_lizard"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
|
||||
if(pref_species.mutant_bodyparts["mam_tail"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Tail</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=mam_tail;task=input'>[features["mam_tail"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["tail_human"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Tail</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=tail_human;task=input'>[features["tail_human"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
|
||||
if(pref_species.mutant_bodyparts["meat_type"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Meat Type</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=meats;task=input'>[features["meat_type"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["snout"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Snout</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=snout;task=input'>[features["snout"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["horns"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Horns</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=horns;task=input'>[features["horns"]]</a>"
|
||||
dat += "<span style='border:1px solid #161616; background-color: #[features["horns_color"]];'> </span> <a href='?_src_=prefs;preference=horns_color;task=input'>Change</a><BR>"
|
||||
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
if(pref_species.mutant_bodyparts["frills"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Frills</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=frills;task=input'>[features["frills"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
|
||||
if(pref_species.mutant_bodyparts["spines"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Spines</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=spines;task=input'>[features["spines"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
|
||||
if(pref_species.mutant_bodyparts["body_markings"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Body Markings</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=body_markings;task=input'>[features["body_markings"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["mam_body_markings"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Species Markings</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=mam_body_markings;task=input'>[features["mam_body_markings"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
|
||||
if(pref_species.mutant_bodyparts["mam_ears"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Ears</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=mam_ears;task=input'>[features["mam_ears"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
|
||||
if(pref_species.mutant_bodyparts["ears"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Ears</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=ears;task=input'>[features["ears"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
|
||||
if(pref_species.mutant_bodyparts["mam_snouts"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Snout</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=mam_snouts;task=input'>[features["mam_snouts"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["legs"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Legs</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=legs;task=input'>[features["legs"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["deco_wings"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Decorative wings</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=deco_wings;task=input'>[features["deco_wings"]]</a>"
|
||||
dat += "<span style='border:1px solid #161616; background-color: #[features["wings_color"]];'> </span> <a href='?_src_=prefs;preference=wings_color;task=input'>Change</a><BR>"
|
||||
|
||||
if(pref_species.mutant_bodyparts["insect_wings"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Insect wings</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=insect_wings;task=input'>[features["insect_wings"]]</a>"
|
||||
dat += "<span style='border:1px solid #161616; background-color: #[features["wings_color"]];'> </span> <a href='?_src_=prefs;preference=wings_color;task=input'>Change</a><BR>"
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["insect_fluff"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Insect Fluff</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=insect_fluffs;task=input'>[features["insect_fluff"]]</a>"
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["taur"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Tauric Body</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=taur;task=input'>[features["taur"]]</a>"
|
||||
|
||||
if(pref_species.mutant_bodyparts["insect_markings"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Insect markings</h3>"
|
||||
|
||||
dat += "<a href='?_src_=prefs;preference=insect_markings;task=input'>[features["insect_markings"]]</a><BR>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["wings"] && GLOB.r_wings_list.len >1)
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Wings</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=wings;task=input'>[features["wings"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["xenohead"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Caste Head</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=xenohead;task=input'>[features["xenohead"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["xenotail"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Tail</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=xenotail;task=input'>[features["xenotail"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["xenodorsal"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Dorsal Spines</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=xenodorsal;task=input'>[features["xenodorsal"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["ipc_screen"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Screen</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=ipc_screen;task=input'>[features["ipc_screen"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
if(pref_species.mutant_bodyparts["ipc_antenna"])
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Antenna</h3>"
|
||||
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=ipc_antenna;task=input'>[features["ipc_antenna"]]</a>"
|
||||
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
for(var/mutant_part in GLOB.all_mutant_parts)
|
||||
if(parent.can_have_part(mutant_part))
|
||||
if(!mutant_category)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
dat += "<h3>[GLOB.all_mutant_parts[mutant_part]]</h3>"
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=[mutant_part];task=input'>[features[mutant_part]]</a>"
|
||||
var/color_type = GLOB.colored_mutant_parts[mutant_part] //if it can be coloured, show the appropriate button
|
||||
if(color_type)
|
||||
dat += "<span style='border:1px solid #161616; background-color: #[features[color_type]];'> </span> <a href='?_src_=prefs;preference=[color_type];task=input'>Change</a><BR>"
|
||||
mutant_category++
|
||||
if(mutant_category >= MAX_MUTANT_ROWS)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
|
||||
if(mutant_category)
|
||||
dat += "</td>"
|
||||
@@ -856,7 +565,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/tauric_shape = FALSE
|
||||
if(features["cock_taur"])
|
||||
var/datum/sprite_accessory/penis/P = GLOB.cock_shapes_list[features["cock_shape"]]
|
||||
if(P.taur_icon && pref_species.mutant_bodyparts["taur"])
|
||||
if(P.taur_icon && parent.can_have_part("taur"))
|
||||
var/datum/sprite_accessory/taur/T = GLOB.taur_list[features["taur"]]
|
||||
if(T.taur_mode & P.accepted_taurs)
|
||||
tauric_shape = TRUE
|
||||
@@ -1808,14 +1517,14 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
pref_species = new newtype()
|
||||
//let's ensure that no weird shit happens on species swapping.
|
||||
custom_species = null
|
||||
if(!pref_species.mutant_bodyparts["body_markings"])
|
||||
if(!parent.can_have_part("body_markings"))
|
||||
features["body_markings"] = "None"
|
||||
if(!pref_species.mutant_bodyparts["mam_body_markings"])
|
||||
if(!parent.can_have_part("mam_body_markings"))
|
||||
features["mam_body_markings"] = "None"
|
||||
if(pref_species.mutant_bodyparts["mam_body_markings"])
|
||||
if(parent.can_have_part("mam_body_markings"))
|
||||
if(features["mam_body_markings"] == "None")
|
||||
features["mam_body_markings"] = "Plain"
|
||||
if(pref_species.mutant_bodyparts["tail_lizard"])
|
||||
if(parent.can_have_part("tail_lizard"))
|
||||
features["tail_lizard"] = "Smooth"
|
||||
if(pref_species.id == "felinid")
|
||||
features["mam_tail"] = "Cat"
|
||||
@@ -1943,7 +1652,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
features["tail_human"] = "None"
|
||||
features["tail_lizard"] = "None"
|
||||
|
||||
if("meats")
|
||||
if("meat_type")
|
||||
var/new_meat
|
||||
new_meat = input(user, "Choose your character's meat type:", "Character Preference") as null|anything in GLOB.meat_types
|
||||
if(new_meat)
|
||||
@@ -2048,7 +1757,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if(new_deco_wings)
|
||||
features["deco_wings"] = new_deco_wings
|
||||
|
||||
if("insect_fluffs")
|
||||
if("insect_fluff")
|
||||
var/new_insect_fluff
|
||||
new_insect_fluff = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.insect_fluffs_list
|
||||
if(new_insect_fluff)
|
||||
@@ -2196,7 +1905,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if("cock_shape")
|
||||
var/new_shape
|
||||
var/list/hockeys = list()
|
||||
if(pref_species.mutant_bodyparts["taur"])
|
||||
if(parent.can_have_part("taur"))
|
||||
var/datum/sprite_accessory/taur/T = GLOB.taur_list[features["taur"]]
|
||||
for(var/A in GLOB.cock_shapes_list)
|
||||
var/datum/sprite_accessory/penis/P = GLOB.cock_shapes_list[A]
|
||||
@@ -2805,10 +2514,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
character.dna.nameless = character.nameless
|
||||
character.dna.custom_species = character.custom_species
|
||||
|
||||
if(pref_species.mutant_bodyparts["meat_type"])
|
||||
if((parent && parent.can_have_part("meat_type")) || pref_species.mutant_bodyparts["meat_type"])
|
||||
character.type_of_meat = GLOB.meat_types[features["meat_type"]]
|
||||
|
||||
if(character.dna.species.mutant_bodyparts["legs"] && (character.dna.features["legs"] == "Digitigrade" || character.dna.features["legs"] == "Avian"))
|
||||
if(((parent && parent.can_have_part("legs")) || pref_species.mutant_bodyparts["legs"]) && (character.dna.features["legs"] == "Digitigrade" || character.dna.features["legs"] == "Avian"))
|
||||
pref_species.species_traits |= DIGITIGRADE
|
||||
else
|
||||
pref_species.species_traits -= DIGITIGRADE
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// You do not need to raise this if you are adding new values that have sane defaults.
|
||||
// Only raise this value when changing the meaning/format/name/layout of an existing value
|
||||
// where you would want the updater procs below to run
|
||||
#define SAVEFILE_VERSION_MAX 33
|
||||
#define SAVEFILE_VERSION_MAX 34
|
||||
|
||||
/*
|
||||
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
|
||||
|
||||
@@ -149,6 +149,30 @@
|
||||
icon_state = "eyepatch"
|
||||
item_state = "eyepatch"
|
||||
|
||||
/obj/item/clothing/glasses/eyepatch/syndicate
|
||||
name = "cybernetic eyepatch"
|
||||
desc = "An eyepatch used to enhance one's aim with guns."
|
||||
icon_state = "syndicatepatch"
|
||||
item_state = "syndicatepatch"
|
||||
resistance_flags = ACID_PROOF
|
||||
|
||||
/obj/item/clothing/glasses/eyepatch/syndicate/equipped(mob/living/carbon/human/user, slot)
|
||||
. = ..()
|
||||
if(slot == SLOT_GLASSES)
|
||||
user.visible_message("<span class='warning'>Circuitry from the eyepatch links itself to your brain as you put on the eyepatch.")
|
||||
if(HAS_TRAIT(user, TRAIT_POOR_AIM))
|
||||
user.visible_message("<span class='warning'>You hear a fizzing noise from the circuit. That can't be good.")
|
||||
ADD_TRAIT(user, TRAIT_INSANE_AIM, "SYNDICATE_EYEPATCH_AIM")
|
||||
ADD_TRAIT(src, TRAIT_NODROP, "SYNDICATE_EYEPATCH_NODROP")
|
||||
|
||||
/obj/item/clothing/glasses/eyepatch/syndicate/dropped(mob/living/carbon/human/user)
|
||||
. = ..()
|
||||
REMOVE_TRAIT(user, TRAIT_INSANE_AIM, "SYNDICATE_EYEPATCH_AIM")
|
||||
var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES)
|
||||
if(eyes)
|
||||
eyes.applyOrganDamage(30)
|
||||
user.visible_message("<span class='warning'>Your eye stings as the circuitry is removed from your eye!")
|
||||
|
||||
/obj/item/clothing/glasses/monocle
|
||||
name = "monocle"
|
||||
desc = "Such a dapper eyepiece!"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
desc = "An expensive ring, studded with a diamond. Cultures have used these rings in courtship for a millenia."
|
||||
icon_state = "ringdiamond"
|
||||
item_state = "dring"
|
||||
|
||||
|
||||
/obj/item/clothing/gloves/ring/diamond/attack_self(mob/user)
|
||||
user.visible_message("<span class='warning'>\The [user] gets down on one knee, presenting \the [src].</span>","<span class='warning'>You get down on one knee, presenting \the [src].</span>")
|
||||
|
||||
@@ -30,3 +30,12 @@
|
||||
desc = "A tiny silver ring, sized to wrap around a finger."
|
||||
icon_state = "ringsilver"
|
||||
item_state = "sring"
|
||||
|
||||
/obj/item/clothing/gloves/ring/custom
|
||||
name = "ring"
|
||||
desc = "A ring."
|
||||
gender = NEUTER
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
obj_flags = UNIQUE_RENAME
|
||||
icon_state = "ringsilver"
|
||||
item_state = "sring"
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
name = "advanced hardsuit"
|
||||
desc = "An advanced suit that protects against hazardous, low pressure environments. Shines with a high polish."
|
||||
item_state = "ce_hardsuit"
|
||||
armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 95, "fire" = 100, "acid" = 90, "wound" = 10)
|
||||
armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 90, "wound" = 10)
|
||||
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite
|
||||
|
||||
@@ -543,6 +543,7 @@
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
|
||||
flags_inv = HIDEHAIR|HIDEEARS
|
||||
rad_flags = RAD_NO_CONTAMINATE
|
||||
|
||||
/obj/item/clothing/suit/hooded/wintercoat/centcom
|
||||
name = "centcom winter coat"
|
||||
@@ -1062,4 +1063,4 @@
|
||||
name = "Samurai outfit"
|
||||
desc = "An outfit used by traditional japanese warriors."
|
||||
icon_state = "samurai"
|
||||
item_state = "samurai"
|
||||
item_state = "samurai"
|
||||
|
||||
@@ -369,3 +369,12 @@
|
||||
icon_state = "plastics"
|
||||
armor = list("melee" = 0, "bullet" = 0, "laser" = 20, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = -40)
|
||||
flags_inv = HIDEACCESSORY
|
||||
|
||||
//necklace
|
||||
/obj/item/clothing/accessory/necklace
|
||||
name = "necklace"
|
||||
desc = "A necklace."
|
||||
icon_state = "locket"
|
||||
obj_flags = UNIQUE_RENAME
|
||||
custom_materials = list(/datum/material/iron=100)
|
||||
resistance_flags = FIRE_PROOF
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
search = findtext(text, ":", pos + length(text[pos]))
|
||||
if(search)
|
||||
emoji = lowertext(copytext(text, pos + length(text[pos]), search))
|
||||
var/isthisapath = (emoji[1] == "/") && text2path(emoji)
|
||||
var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat)
|
||||
var/tag = sheet.icon_tag("emoji-[emoji]")
|
||||
if(tag)
|
||||
parsed += "<i style='width:16px !important;height:16px !important;'>[tag]</i>" //evil way of enforcing 16x16
|
||||
pos = search + length(text[pos])
|
||||
else if(ispath(isthisapath, /atom)) //path
|
||||
var/atom/thisisanatom = isthisapath
|
||||
parsed += "[icon2html(initial(thisisanatom.icon), world, initial(thisisanatom.icon_state))]"
|
||||
pos = search + length(text[pos])
|
||||
else
|
||||
parsed += copytext(text, pos, search)
|
||||
pos = search
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
shuffle_inplace(mobs)
|
||||
|
||||
var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new /obj/effect/proc_holder/spell/targeted/mind_transfer
|
||||
var/obj/effect/proc_holder/spell/pointed/mind_transfer/swapper = new /obj/effect/proc_holder/spell/pointed/mind_transfer
|
||||
while(mobs.len > 1)
|
||||
var/mob/living/carbon/human/H = pick(mobs)
|
||||
mobs -= H
|
||||
|
||||
@@ -113,6 +113,27 @@
|
||||
tastes = list("pie" = 1, "meat" = 1)
|
||||
foodtype = GRAIN | MEAT
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/pie/burek
|
||||
name = "Burek"
|
||||
icon = 'icons/obj/food/piecake.dmi'
|
||||
icon_state = "burek"
|
||||
desc = "If you know, you know."
|
||||
slice_path = /obj/item/reagent_containers/food/snacks/pie/burekslice
|
||||
slices_num = 4
|
||||
bonus_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/nutriment/vitamin = 6)
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment= 20, /datum/reagent/consumable/nutriment/vitamin = 6)
|
||||
bitesize = 12
|
||||
tastes = list("meat" = 1, "oil" = 1)
|
||||
foodtype = GRAIN | MEAT
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/pie/burekslice
|
||||
name = "Burek Slice"
|
||||
icon = 'icons/obj/food/piecake.dmi'
|
||||
icon_state = "burekslice"
|
||||
desc = "A slice of Burek, watch out for oil stains!"
|
||||
tastes = list("meat" = 1, "oil" = 1)
|
||||
foodtype = GRAIN | MEAT
|
||||
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/pie/tofupie
|
||||
name = "tofu-pie"
|
||||
|
||||
@@ -130,6 +130,18 @@
|
||||
result = /obj/item/reagent_containers/food/snacks/pie/dulcedebatata
|
||||
subcategory = CAT_PIE
|
||||
|
||||
/datum/crafting_recipe/food/burek
|
||||
name = "Burek"
|
||||
reqs = list(
|
||||
/datum/reagent/consumable/blackpepper = 3,
|
||||
/datum/reagent/consumable/sodiumchloride = 3,
|
||||
/obj/item/reagent_containers/food/snacks/pizzabread = 2,
|
||||
/obj/item/reagent_containers/food/snacks/meat/cutlet/plain = 6,
|
||||
/obj/item/reagent_containers/food/snacks/butter = 1,
|
||||
)
|
||||
result = /obj/item/reagent_containers/food/snacks/pie/burek
|
||||
subcategory = CAT_PIE
|
||||
|
||||
/datum/crafting_recipe/food/meatpie
|
||||
name = "Meat pie"
|
||||
reqs = list(
|
||||
@@ -302,4 +314,4 @@
|
||||
/obj/item/reagent_containers/food/snacks/spiderling = 1
|
||||
)
|
||||
result = /obj/item/reagent_containers/food/snacks/spiderlollipop
|
||||
subcategory = CAT_PIE
|
||||
subcategory = CAT_PIE
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
desc = "An advanced device designed to manipulate plant genetic makeup."
|
||||
icon = 'icons/obj/hydroponics/equipment.dmi'
|
||||
icon_state = "dnamod"
|
||||
density = TRUE
|
||||
circuit = /obj/item/circuitboard/machine/plantgenes
|
||||
pass_flags = PASSTABLE
|
||||
pass_flags = PASSTABLE | LETPASSTHROW
|
||||
flags_1 = DEFAULT_RICOCHET_1
|
||||
|
||||
var/obj/item/seeds/seed
|
||||
var/obj/item/disk/plantgene/disk
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
icon_dead = "cannabis-dead" // Same for the dead icon
|
||||
genes = list(/datum/plant_gene/trait/repeated_harvest)
|
||||
mutatelist = list(/obj/item/seeds/cannabis/rainbow,
|
||||
/obj/item/seeds/cannabis/death,
|
||||
/obj/item/seeds/cannabis/white,
|
||||
/obj/item/seeds/cannabis/ultimate)
|
||||
/obj/item/seeds/cannabis/death)
|
||||
reagents_add = list(/datum/reagent/drug/space_drugs = 0.15, /datum/reagent/toxin/lipolicide = 0.35) // gives u the munchies
|
||||
|
||||
|
||||
@@ -27,7 +25,7 @@
|
||||
species = "megacannabis"
|
||||
plantname = "Rainbow Weed"
|
||||
product = /obj/item/reagent_containers/food/snacks/grown/cannabis/rainbow
|
||||
mutatelist = list()
|
||||
mutatelist = list(/obj/item/seeds/cannabis/ultimate)
|
||||
reagents_add = list(/datum/reagent/toxin/mindbreaker = 0.15, /datum/reagent/toxin/lipolicide = 0.35)
|
||||
rarity = 40
|
||||
|
||||
@@ -38,7 +36,7 @@
|
||||
species = "blackcannabis"
|
||||
plantname = "Deathweed"
|
||||
product = /obj/item/reagent_containers/food/snacks/grown/cannabis/death
|
||||
mutatelist = list()
|
||||
mutatelist = list(/obj/item/seeds/cannabis/white)
|
||||
reagents_add = list(/datum/reagent/toxin/cyanide = 0.35, /datum/reagent/drug/space_drugs = 0.15, /datum/reagent/toxin/lipolicide = 0.15)
|
||||
rarity = 40
|
||||
|
||||
|
||||
@@ -33,26 +33,6 @@
|
||||
filling_color = "#00FF00"
|
||||
juice_results = list(/datum/reagent/consumable/limejuice = 0)
|
||||
|
||||
// Electric Lime
|
||||
/obj/item/seeds/lime/electric
|
||||
name = "pack of electric lime seeds"
|
||||
desc = "Electrically sour seeds."
|
||||
icon_state = "seed-electriclime"
|
||||
species = "electric lime"
|
||||
plantname = "Electric Lime Tree"
|
||||
growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi'
|
||||
icon_grow = "lime-grow"
|
||||
icon_dead = "lime-dead"
|
||||
icon_harvest = "lime-harvest"
|
||||
product = /obj/item/reagent_containers/food/snacks/grown/citrus/lime/electric
|
||||
genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/cell_charge, /datum/plant_gene/trait/glow/green)
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/lime/electric
|
||||
seed = /obj/item/seeds/lime/electric
|
||||
name = "electric lime"
|
||||
desc = "It's so sour, you'll be shocked!"
|
||||
icon_state = "electriclime"
|
||||
|
||||
// Orange
|
||||
/obj/item/seeds/orange
|
||||
name = "pack of orange seeds"
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
potency = 25
|
||||
growthstages = 3
|
||||
growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi'
|
||||
icon_grow = "garlic-grow"
|
||||
icon_harvest = "garlic-harvest"
|
||||
icon_dead = "garlic-dead"
|
||||
reagents_add = list(/datum/reagent/consumable/garlic = 0.15, /datum/reagent/consumable/nutriment = 0.1)
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/grown/garlic
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
icon_grow = "fairygrass-grow"
|
||||
icon_dead = "fairygrass-dead"
|
||||
genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/glow/blue)
|
||||
mutatelist = list (/obj/item/seeds/grass/carpet)
|
||||
reagents_add = list(/datum/reagent/consumable/nutriment = 0.02, /datum/reagent/hydrogen = 0.05, /datum/reagent/drug/space_drugs = 0.15)
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/grown/grass/fairy
|
||||
@@ -99,7 +100,7 @@
|
||||
species = "carpet"
|
||||
plantname = "Carpet"
|
||||
product = /obj/item/reagent_containers/food/snacks/grown/grass/carpet
|
||||
mutatelist = list()
|
||||
mutatelist = list(/obj/item/seeds/grass/fairy)
|
||||
rarity = 10
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/grown/grass/carpet
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
filling_color = "#4582B4"
|
||||
grind_results = list(/datum/reagent/toxin/teapowder = 0, /datum/reagent/medicine/salglu_solution = 0)
|
||||
|
||||
// Kitty drugs
|
||||
// Catnip
|
||||
/obj/item/seeds/tea/catnip
|
||||
name = "pack of catnip seeds"
|
||||
icon_state = "seed-catnip"
|
||||
@@ -52,6 +52,9 @@
|
||||
species = "catnip"
|
||||
plantname = "Catnip Plant"
|
||||
growthstages = 3
|
||||
icon_grow = "catnip-grow"
|
||||
icon_harvest = "catnip-harvest"
|
||||
icon_dead = "tea-dead"
|
||||
product = /obj/item/reagent_containers/food/snacks/grown/tea/catnip
|
||||
reagents_add = list(/datum/reagent/pax/catnip = 0.1, /datum/reagent/consumable/nutriment/vitamin = 0.06, /datum/reagent/toxin/teapowder = 0.3)
|
||||
rarity = 50
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
///how many people can play mafia without issues (running out of spawns, procs not expecting more than this amount of people, etc)
|
||||
#define MAFIA_MAX_PLAYER_COUNT 12
|
||||
|
||||
#define MAFIA_TEAM_TOWN "town"
|
||||
#define MAFIA_TEAM_MAFIA "mafia"
|
||||
#define MAFIA_TEAM_SOLO "solo"
|
||||
|
||||
//types of town roles for random setup gen
|
||||
/// assistants it's just assistants filling up the rest of the roles
|
||||
#define TOWN_OVERFLOW "overflow"
|
||||
/// roles that learn info about others in the game (chaplain, detective, psych)
|
||||
#define TOWN_INVEST "invest"
|
||||
/// roles that keep other roles safe (doctor, and weirdly enough lawyer counts)
|
||||
#define TOWN_PROTECT "protect"
|
||||
/// roles that don't fit into anything else (hop)
|
||||
#define TOWN_MISC "misc"
|
||||
|
||||
//other types (mafia team, neutrals)
|
||||
/// normal vote kill changelings
|
||||
#define MAFIA_REGULAR "regular"
|
||||
/// every other changeling role that has extra abilities
|
||||
#define MAFIA_SPECIAL "special"
|
||||
/// role that wins solo that nobody likes
|
||||
#define NEUTRAL_KILL "kill"
|
||||
/// role that upsets the game aka obsessed, usually worse for town than mafia but they can vote against mafia
|
||||
#define NEUTRAL_DISRUPT "disrupt"
|
||||
|
||||
#define MAFIA_PHASE_SETUP 1
|
||||
#define MAFIA_PHASE_DAY 2
|
||||
#define MAFIA_PHASE_VOTING 3
|
||||
#define MAFIA_PHASE_JUDGEMENT 4
|
||||
#define MAFIA_PHASE_NIGHT 5
|
||||
#define MAFIA_PHASE_VICTORY_LAP 6
|
||||
|
||||
#define MAFIA_ALIVE 1
|
||||
#define MAFIA_DEAD 2
|
||||
|
||||
#define COMSIG_MAFIA_ON_KILL "mafia_onkill"
|
||||
#define MAFIA_PREVENT_KILL 1
|
||||
|
||||
#define COMSIG_MAFIA_CAN_PERFORM_ACTION "mafia_can_perform_action"
|
||||
#define MAFIA_PREVENT_ACTION 1
|
||||
|
||||
//in order of events + game end
|
||||
|
||||
/// when the shutters fall, before the 45 second wait and night event resolution
|
||||
#define COMSIG_MAFIA_SUNDOWN "sundown"
|
||||
/// after the 45 second wait, for actions that must go first
|
||||
#define COMSIG_MAFIA_NIGHT_START "night_start"
|
||||
/// most night actions now resolve
|
||||
#define COMSIG_MAFIA_NIGHT_ACTION_PHASE "night_actions"
|
||||
/// now killing happens from the roles that do that. the reason this is post action phase is to ensure doctors can protect and lawyers can block
|
||||
#define COMSIG_MAFIA_NIGHT_KILL_PHASE "night_kill"
|
||||
/// now undoing states like protection, actions that must happen last, etc. right before shutters raise and the day begins
|
||||
#define COMSIG_MAFIA_NIGHT_END "night_end"
|
||||
|
||||
/// signal sent to roles when the game is confirmed ending
|
||||
#define COMSIG_MAFIA_GAME_END "game_end"
|
||||
|
||||
/// list of ghosts who want to play mafia, every time someone enters the list it checks to see if enough are in
|
||||
GLOBAL_LIST_EMPTY(mafia_signup)
|
||||
/// list of ghosts who want to play mafia that have since disconnected. They are kept in the lobby, but not counted for starting a game.
|
||||
GLOBAL_LIST_EMPTY(mafia_bad_signup)
|
||||
/// the current global mafia game running.
|
||||
GLOBAL_VAR(mafia_game)
|
||||
@@ -0,0 +1,971 @@
|
||||
|
||||
|
||||
/**
|
||||
* The mafia controller handles the mafia minigame in progress.
|
||||
* It is first created when the first ghost signs up to play.
|
||||
*/
|
||||
/datum/mafia_controller
|
||||
///list of observers that should get game updates.
|
||||
var/list/spectators = list()
|
||||
///all roles in the game, dead or alive. check their game status if you only want living or dead.
|
||||
var/list/all_roles = list()
|
||||
///exists to speed up role retrieval, it's a dict. player_role_lookup[player ckey] will give you the role they play
|
||||
var/list/player_role_lookup = list()
|
||||
///what part of the game you're playing in. day phases, night phases, judgement phases, etc.
|
||||
var/phase = MAFIA_PHASE_SETUP
|
||||
///how long the game has gone on for, changes with every sunrise. day one, night one, day two, etc.
|
||||
var/turn = 0
|
||||
///for debugging and testing a full game, or adminbuse. If this is not null, it will use this as a setup. clears when game is over
|
||||
var/list/custom_setup = list()
|
||||
///first day has no voting, and thus is shorter
|
||||
var/first_day_phase_period = 20 SECONDS
|
||||
///talk with others about the last night
|
||||
var/day_phase_period = 1 MINUTES
|
||||
///vote someone to get put on trial
|
||||
var/voting_phase_period = 30 SECONDS
|
||||
///defend yourself! don't get lynched! sometimes skipped if nobody votes.
|
||||
var/judgement_phase_period = 30 SECONDS
|
||||
///guilty or innocent, we want a bit of time for players to process the outcome of the vote
|
||||
var/judgement_lynch_period = 5 SECONDS
|
||||
///mafia talk at night and pick someone to kill, some town roles use their actions, etc etc.
|
||||
var/night_phase_period = 45 SECONDS
|
||||
///like the lynch period, players need to see what the other players in the game's roles were
|
||||
var/victory_lap_period = 20 SECONDS
|
||||
|
||||
///template picked when the game starts. used for the name and desc reading
|
||||
var/datum/map_template/mafia/current_map
|
||||
///map generation tool that deletes the current map after the game finishes
|
||||
var/datum/mapGenerator/massdelete/map_deleter
|
||||
|
||||
///Readable list of roles in current game, sent to the tgui panel for roles list > list("Psychologist x1", "Clown x2")
|
||||
var/list/current_setup_text
|
||||
|
||||
///starting outfit for all mafia players. it's just a grey jumpsuit.
|
||||
var/player_outfit = /datum/outfit/mafia
|
||||
|
||||
///spawn points for players, each one has a house
|
||||
var/list/landmarks = list()
|
||||
///town center for when people get put on trial
|
||||
var/town_center_landmark
|
||||
|
||||
///group voting on one person, like putting people to trial or choosing who to kill as mafia
|
||||
var/list/votes = list()
|
||||
///and these (judgement_innocent_votes, judgement_abstain_votes and judgement_guilty_votes) are the judgement phase votes, aka people sorting themselves into guilty and innocent, and "eh, i don't really care" lists. whichever has more inno or guilty wins!
|
||||
var/list/judgement_abstain_votes = list()
|
||||
var/list/judgement_innocent_votes = list()
|
||||
var/list/judgement_guilty_votes = list()
|
||||
///current role on trial for the judgement phase, will die if guilty is greater than innocent
|
||||
var/datum/mafia_role/on_trial
|
||||
|
||||
///current timer for phase
|
||||
var/next_phase_timer
|
||||
|
||||
///used for debugging in testing (doesn't put people out of the game, some other shit i forgot, who knows just don't set this in live) honestly kinda deprecated
|
||||
var/debug = FALSE
|
||||
|
||||
///Max player count
|
||||
var/max_player = MAFIA_MAX_PLAYER_COUNT
|
||||
///Required player count
|
||||
var/required_player = 5
|
||||
///Prioritizes clients to have cool antag roles
|
||||
var/low_pop_mode = FALSE
|
||||
|
||||
/datum/mafia_controller/New()
|
||||
. = ..()
|
||||
GLOB.mafia_game = src
|
||||
map_deleter = new
|
||||
|
||||
/datum/mafia_controller/Destroy(force, ...)
|
||||
. = ..()
|
||||
GLOB.mafia_game = null
|
||||
end_game()
|
||||
qdel(map_deleter)
|
||||
|
||||
/**
|
||||
* Triggers at beginning of the game when there is a confirmed list of valid, ready players.
|
||||
* Creates a 100% ready game that has NOT started (no players in bodies)
|
||||
* Followed by start game
|
||||
*
|
||||
* Does the following:
|
||||
* * Picks map, and loads it
|
||||
* * Grabs landmarks if it is the first time it's loading
|
||||
* * Sets up the role list
|
||||
* * Puts players in each role randomly
|
||||
* Arguments:
|
||||
* * setup_list: list of all the datum setups (fancy list of roles) that would work for the game
|
||||
* * ready_players: list of filtered, sane players (so not playing or disconnected) for the game to put into roles
|
||||
*/
|
||||
/datum/mafia_controller/proc/prepare_game(setup_list, ready_players)
|
||||
|
||||
var/list/possible_maps = subtypesof(/datum/map_template/mafia)
|
||||
var/turf/spawn_area = get_turf(locate(/obj/effect/landmark/mafia_game_area) in GLOB.landmarks_list)
|
||||
|
||||
current_map = pick(possible_maps)
|
||||
current_map = new current_map
|
||||
|
||||
if(!spawn_area)
|
||||
CRASH("No spawn area detected for Mafia!")
|
||||
var/list/bounds = current_map.load(spawn_area)
|
||||
if(!bounds)
|
||||
CRASH("Loading mafia map failed!")
|
||||
map_deleter.defineRegion(spawn_area, locate(spawn_area.x + 23,spawn_area.y + 23,spawn_area.z), replace = TRUE) //so we're ready to mass delete when round ends
|
||||
|
||||
if(!landmarks.len)//we grab town center when we grab landmarks, if there is none (the first game signed up for let's grab them post load)
|
||||
for(var/obj/effect/landmark/mafia/possible_spawn in GLOB.landmarks_list)
|
||||
if(istype(possible_spawn, /obj/effect/landmark/mafia/town_center))
|
||||
town_center_landmark = possible_spawn
|
||||
else
|
||||
landmarks += possible_spawn
|
||||
|
||||
current_setup_text = list()
|
||||
|
||||
var/list/boring_roles = list()
|
||||
var/list/not_boring_roles = list()
|
||||
|
||||
for(var/rtype in setup_list)
|
||||
for(var/i in 1 to setup_list[rtype])
|
||||
var/datum/mafia_role/role = new rtype(src)
|
||||
all_roles += role
|
||||
if(role.role_type == TOWN_PROTECT || role.role_type == TOWN_INVEST || role.role_type == MAFIA_SPECIAL || role.role_type == MAFIA_REGULAR)
|
||||
not_boring_roles += role
|
||||
else
|
||||
boring_roles += role
|
||||
var/datum/mafia_role/rp = rtype
|
||||
current_setup_text += "[initial(rp.name)] x[setup_list[rtype]]"
|
||||
|
||||
var/list/spawnpoints = landmarks.Copy()
|
||||
|
||||
if(length(ready_players) < 7 || low_pop_mode)
|
||||
//do normal assign
|
||||
for(var/datum/mafia_role/role in not_boring_roles)
|
||||
role.assigned_landmark = pick_n_take(spawnpoints)
|
||||
role.player_key = pick_n_take(ready_players)
|
||||
//shame!
|
||||
for(var/datum/mafia_role/role in boring_roles)
|
||||
role.assigned_landmark = pick_n_take(spawnpoints)
|
||||
role.player_key = pick_n_take(ready_players)
|
||||
|
||||
else //go run the normal one
|
||||
for(var/datum/mafia_role/role in all_roles)
|
||||
role.assigned_landmark = pick_n_take(spawnpoints)
|
||||
if(!debug)
|
||||
role.player_key = pick_n_take(ready_players)
|
||||
else
|
||||
role.player_key = pop(ready_players)
|
||||
|
||||
/datum/mafia_controller/proc/send_message(msg,team)
|
||||
for(var/datum/mafia_role/R in all_roles)
|
||||
if(team && R.team != team)
|
||||
continue
|
||||
to_chat(R.body,msg)
|
||||
var/team_suffix = team ? "([uppertext(team)] CHAT)" : ""
|
||||
for(var/M in GLOB.dead_mob_list)
|
||||
var/mob/spectator = M
|
||||
if(spectator.ckey in spectators) //was in current game, or spectatin' (won't send to living)
|
||||
var/link = FOLLOW_LINK(M, town_center_landmark)
|
||||
to_chat(M, "[link] MAFIA: [msg] [team_suffix]")
|
||||
|
||||
/**
|
||||
* The game by this point is now all set up, and so we can put people in their bodies and start the first phase.
|
||||
*
|
||||
* Does the following:
|
||||
* * Creates bodies for all of the roles with the first proc
|
||||
* * Starts the first day manually (so no timer) with the second proc
|
||||
*/
|
||||
/datum/mafia_controller/proc/start_game()
|
||||
create_bodies()
|
||||
start_day()
|
||||
|
||||
/**
|
||||
* How every day starts.
|
||||
*
|
||||
* What players do in this phase:
|
||||
* * If day one, just a small starting period to see who is in the game and check role, leading to the night phase.
|
||||
* * Otherwise, it's a longer period used to discuss events that happened during the night, leading to the voting phase.
|
||||
*/
|
||||
/datum/mafia_controller/proc/start_day()
|
||||
turn += 1
|
||||
phase = MAFIA_PHASE_DAY
|
||||
if(!check_victory())
|
||||
if(turn == 1)
|
||||
send_message("<span class='notice'><b>The selected map is [current_map.name]!</b></br>[current_map.description]</span>")
|
||||
send_message("<b>Day [turn] started! There is no voting on the first day. Say hello to everybody!</b>")
|
||||
next_phase_timer = addtimer(CALLBACK(src,.proc/check_trial, FALSE),first_day_phase_period,TIMER_STOPPABLE) //no voting period = no votes = instant night
|
||||
else
|
||||
send_message("<b>Day [turn] started! Voting will start in 1 minute.</b>")
|
||||
next_phase_timer = addtimer(CALLBACK(src,.proc/start_voting_phase),day_phase_period,TIMER_STOPPABLE)
|
||||
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/**
|
||||
* Players have finished the discussion period, and now must put up someone to the chopping block.
|
||||
*
|
||||
* What players do in this phase:
|
||||
* * Vote on which player to put up for lynching, leading to the judgement phase.
|
||||
* * If no votes are case, the judgement phase is skipped, leading to the night phase.
|
||||
*/
|
||||
/datum/mafia_controller/proc/start_voting_phase()
|
||||
phase = MAFIA_PHASE_VOTING
|
||||
next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, TRUE),voting_phase_period,TIMER_STOPPABLE) //be verbose!
|
||||
send_message("<b>Voting started! Vote for who you want to see on trial today.</b>")
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/**
|
||||
* Players have voted someone up, and now the person must defend themselves while the town votes innocent or guilty.
|
||||
*
|
||||
* What players do in this phase:
|
||||
* * Vote innocent or guilty, if they are not on trial.
|
||||
* * Defend themselves and wait for judgement, if they are.
|
||||
* * Leads to the lynch phase.
|
||||
* Arguments:
|
||||
* * verbose: boolean, announces whether there were votes or not. after judgement it goes back here with no voting period to end the day.
|
||||
*/
|
||||
/datum/mafia_controller/proc/check_trial(verbose = TRUE)
|
||||
var/datum/mafia_role/loser = get_vote_winner("Day")//, majority_of_town = TRUE)
|
||||
// var/loser_votes = get_vote_count(loser,"Day")
|
||||
if(loser)
|
||||
// if(loser_votes > 12)
|
||||
// loser.body.client?.give_award(/datum/award/achievement/mafia/universally_hated, loser.body)
|
||||
send_message("<b>[loser.body.real_name] wins the day vote, Listen to their defense and vote \"INNOCENT\" or \"GUILTY\"!</b>")
|
||||
//refresh the lists
|
||||
judgement_abstain_votes = list()
|
||||
judgement_innocent_votes = list()
|
||||
judgement_guilty_votes = list()
|
||||
for(var/i in all_roles)
|
||||
var/datum/mafia_role/abstainee = i
|
||||
if(abstainee.game_status == MAFIA_ALIVE && abstainee != loser)
|
||||
judgement_abstain_votes += abstainee
|
||||
on_trial = loser
|
||||
on_trial.body.forceMove(get_turf(town_center_landmark))
|
||||
phase = MAFIA_PHASE_JUDGEMENT
|
||||
next_phase_timer = addtimer(CALLBACK(src, .proc/lynch),judgement_phase_period,TIMER_STOPPABLE)
|
||||
reset_votes("Day")
|
||||
else
|
||||
if(verbose)
|
||||
send_message("<b>Not enough people have voted to put someone on trial, nobody will be lynched today.</b>")
|
||||
if(!check_victory())
|
||||
lockdown()
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/**
|
||||
* Players have voted innocent or guilty on the person on trial, and that person is now killed or returned home.
|
||||
*
|
||||
* What players do in this phase:
|
||||
* * r/watchpeopledie
|
||||
* * If the accused is killed, their true role is revealed to the rest of the players.
|
||||
*/
|
||||
/datum/mafia_controller/proc/lynch()
|
||||
for(var/i in judgement_innocent_votes)
|
||||
var/datum/mafia_role/role = i
|
||||
send_message("<span class='green'>[role.body.real_name] voted innocent.</span>")
|
||||
for(var/ii in judgement_abstain_votes)
|
||||
var/datum/mafia_role/role = ii
|
||||
send_message("<span class='comradio'>[role.body.real_name] abstained.</span>")
|
||||
for(var/iii in judgement_guilty_votes)
|
||||
var/datum/mafia_role/role = iii
|
||||
send_message("<span class='red'>[role.body.real_name] voted guilty.</span>")
|
||||
if(judgement_guilty_votes.len > judgement_innocent_votes.len) //strictly need majority guilty to lynch
|
||||
send_message("<span class='red'><b>Guilty wins majority, [on_trial.body.real_name] has been lynched.</b></span>")
|
||||
on_trial.kill(src, lynch = TRUE)
|
||||
addtimer(CALLBACK(src, .proc/send_home, on_trial),judgement_lynch_period)
|
||||
else
|
||||
send_message("<span class='green'><b>Innocent wins majority, [on_trial.body.real_name] has been spared.</b></span>")
|
||||
on_trial.body.forceMove(get_turf(on_trial.assigned_landmark))
|
||||
on_trial = null
|
||||
//day votes are already cleared, so this will skip the trial and check victory/lockdown/whatever else
|
||||
next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, FALSE),judgement_lynch_period,TIMER_STOPPABLE)// small pause to see the guy dead, no verbosity since we already did this
|
||||
|
||||
/**
|
||||
* Teenie helper proc to move players back to their home.
|
||||
* Used in the above, but also used in the debug button "send all players home"
|
||||
* Arguments:
|
||||
* * role: mafia role that is getting sent back to the game.
|
||||
*/
|
||||
/datum/mafia_controller/proc/send_home(datum/mafia_role/role)
|
||||
role.body.forceMove(get_turf(role.assigned_landmark))
|
||||
|
||||
/**
|
||||
* Checks to see if a faction (or solo antagonist) has won.
|
||||
*
|
||||
* Calculates in this order:
|
||||
* * counts up town, mafia, and solo
|
||||
* * solos can count as town members for the purposes of mafia winning
|
||||
* * sends the amount of living people to the solo antagonists, and see if they won OR block the victory of the teams
|
||||
* * checks if solos won from above, then if town, then if mafia
|
||||
* * starts the end of the game if a faction won
|
||||
* * returns TRUE if someone won the game, halting other procs from continuing in the case of a victory
|
||||
*/
|
||||
/datum/mafia_controller/proc/check_victory()
|
||||
//needed for achievements
|
||||
var/list/total_town = list()
|
||||
var/list/total_mafia = list()
|
||||
|
||||
var/alive_town = 0
|
||||
var/alive_mafia = 0
|
||||
var/list/solos_to_ask = list() //need to ask after because first round is counting team sizes
|
||||
var/list/total_victors = list() //if this list gets filled with anyone, they win. list because side antags can with with people
|
||||
var/blocked_victory = FALSE //if a solo antagonist is stopping the town or mafia from finishing the game.
|
||||
|
||||
///PHASE ONE: TALLY UP ALL NUMBERS OF PEOPLE STILL ALIVE
|
||||
|
||||
for(var/datum/mafia_role/R in all_roles)
|
||||
switch(R.team)
|
||||
if(MAFIA_TEAM_MAFIA)
|
||||
total_mafia += R
|
||||
if(R.game_status == MAFIA_ALIVE)
|
||||
alive_mafia += R.vote_power
|
||||
if(MAFIA_TEAM_TOWN)
|
||||
total_town += R
|
||||
if(R.game_status == MAFIA_ALIVE)
|
||||
alive_town += R.vote_power
|
||||
if(MAFIA_TEAM_SOLO)
|
||||
if(R.game_status == MAFIA_ALIVE)
|
||||
if(R.solo_counts_as_town)
|
||||
alive_town += R.vote_power
|
||||
solos_to_ask += R
|
||||
|
||||
///PHASE TWO: SEND STATS TO SOLO ANTAGS, SEE IF THEY WON OR TEAMS CANNOT WIN
|
||||
|
||||
for(var/datum/mafia_role/solo in solos_to_ask)
|
||||
if(solo.check_total_victory(alive_town, alive_mafia))
|
||||
total_victors += solo
|
||||
if(solo.block_team_victory(alive_town, alive_mafia))
|
||||
blocked_victory = TRUE
|
||||
|
||||
//solo victories!
|
||||
var/solo_end = FALSE
|
||||
for(var/datum/mafia_role/winner in total_victors)
|
||||
send_message("<span class='big comradio'>!! [uppertext(winner.name)] VICTORY !!</span>")
|
||||
// var/client/winner_client = GLOB.directory[winner.player_key]
|
||||
// winner_client?.give_award(winner.winner_award, winner.body)
|
||||
solo_end = TRUE
|
||||
if(solo_end)
|
||||
start_the_end()
|
||||
return TRUE
|
||||
if(blocked_victory)
|
||||
return FALSE
|
||||
if(alive_mafia == 0)
|
||||
// for(var/datum/mafia_role/townie in total_town)
|
||||
// var/client/townie_client = GLOB.directory[townie.player_key]
|
||||
// townie_client?.give_award(townie.winner_award, townie.body)
|
||||
start_the_end("<span class='big green'>!! TOWN VICTORY !!</span>")
|
||||
return TRUE
|
||||
else if(alive_mafia >= alive_town) //guess could change if town nightkill is added
|
||||
start_the_end("<span class='big red'>!! MAFIA VICTORY !!</span>")
|
||||
// for(var/datum/mafia_role/changeling in total_mafia)
|
||||
// var/client/changeling_client = GLOB.directory[changeling.player_key]
|
||||
// changeling_client?.give_award(changeling.winner_award, changeling.body)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* The end of the game is in two procs, because we want a bit of time for players to see eachothers roles.
|
||||
* Because of how check_victory works, the game is halted in other places by this point.
|
||||
*
|
||||
* What players do in this phase:
|
||||
* * See everyone's role postgame
|
||||
* * See who won the game
|
||||
* Arguments:
|
||||
* * message: string, if non-null it sends it to all players. used to announce team victories while solos are handled in check victory
|
||||
*/
|
||||
/datum/mafia_controller/proc/start_the_end(message)
|
||||
SEND_SIGNAL(src,COMSIG_MAFIA_GAME_END)
|
||||
if(message)
|
||||
send_message(message)
|
||||
for(var/datum/mafia_role/R in all_roles)
|
||||
R.reveal_role(src)
|
||||
phase = MAFIA_PHASE_VICTORY_LAP
|
||||
next_phase_timer = addtimer(CALLBACK(src,.proc/end_game),victory_lap_period,TIMER_STOPPABLE)
|
||||
|
||||
/**
|
||||
* Cleans up the game, resetting variables back to the beginning and removing the map with the generator.
|
||||
*/
|
||||
/datum/mafia_controller/proc/end_game()
|
||||
map_deleter.generate() //remove the map, it will be loaded at the start of the next one
|
||||
QDEL_LIST(all_roles)
|
||||
current_setup_text = null
|
||||
custom_setup = list()
|
||||
turn = 0
|
||||
votes = list()
|
||||
//map gen does not deal with landmarks
|
||||
QDEL_LIST(landmarks)
|
||||
QDEL_NULL(town_center_landmark)
|
||||
phase = MAFIA_PHASE_SETUP
|
||||
|
||||
/**
|
||||
* After the voting and judgement phases, the game goes to night shutting the windows and beginning night with a proc.
|
||||
*/
|
||||
/datum/mafia_controller/proc/lockdown()
|
||||
toggle_night_curtains(close=TRUE)
|
||||
start_night()
|
||||
|
||||
/**
|
||||
* Shuts poddoors attached to mafia.
|
||||
* Arguments:
|
||||
* * close: boolean, the state you want the curtains in.
|
||||
*/
|
||||
/datum/mafia_controller/proc/toggle_night_curtains(close)
|
||||
for(var/obj/machinery/door/poddoor/D in GLOB.machines) //I really dislike pathing of these
|
||||
if(D.id != "mafia") //so as to not trigger shutters on station, lol
|
||||
continue
|
||||
if(close)
|
||||
INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/close)
|
||||
else
|
||||
INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/open)
|
||||
|
||||
/**
|
||||
* The actual start of night for players. Mostly info is given at the start of the night as the end of the night is when votes and actions are submitted and tried.
|
||||
*
|
||||
* What players do in this phase:
|
||||
* * Mafia are told to begin voting on who to kill
|
||||
* * Powers that are picked during the day announce themselves right now
|
||||
*/
|
||||
/datum/mafia_controller/proc/start_night()
|
||||
phase = MAFIA_PHASE_NIGHT
|
||||
send_message("<b>Night [turn] started! Lockdown will end in 45 seconds.</b>")
|
||||
SEND_SIGNAL(src,COMSIG_MAFIA_SUNDOWN)
|
||||
next_phase_timer = addtimer(CALLBACK(src, .proc/resolve_night),night_phase_period,TIMER_STOPPABLE)
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/**
|
||||
* The end of the night, and a series of signals for the order of events on a night.
|
||||
*
|
||||
* Order of events, and what they mean:
|
||||
* * Start of resolve (NIGHT_START) is for activating night abilities that MUST go first
|
||||
* * Action phase (NIGHT_ACTION_PHASE) is for non-lethal day abilities
|
||||
* * Mafia then tallies votes and kills the highest voted person (note: one random voter visits that person for the purposes of roleblocking)
|
||||
* * Killing phase (NIGHT_KILL_PHASE) is for lethal night abilities
|
||||
* * End of resolve (NIGHT_END) is for cleaning up abilities that went off and i guess doing some that must go last
|
||||
* * Finally opens the curtains and calls the start of day phase, completing the cycle until check victory returns TRUE
|
||||
*/
|
||||
/datum/mafia_controller/proc/resolve_night()
|
||||
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_START)
|
||||
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_ACTION_PHASE)
|
||||
//resolve mafia kill, todo unsnowflake this
|
||||
var/datum/mafia_role/R = get_vote_winner("Mafia")
|
||||
if(R)
|
||||
var/datum/mafia_role/killer = get_random_voter("Mafia")
|
||||
if(SEND_SIGNAL(killer,COMSIG_MAFIA_CAN_PERFORM_ACTION,src,"mafia killing",R) & MAFIA_PREVENT_ACTION)
|
||||
send_message("<span class='danger'>[killer.body.real_name] was unable to attack [R.body.real_name] tonight!</span>",MAFIA_TEAM_MAFIA)
|
||||
else
|
||||
send_message("<span class='danger'>[killer.body.real_name] has attacked [R.body.real_name]!</span>",MAFIA_TEAM_MAFIA)
|
||||
R.kill(src)
|
||||
reset_votes("Mafia")
|
||||
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_KILL_PHASE)
|
||||
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_END)
|
||||
toggle_night_curtains(close=FALSE)
|
||||
start_day()
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/**
|
||||
* Proc that goes off when players vote for something with their mafia panel.
|
||||
*
|
||||
* If teams, it hides the tally overlay and only sends the vote messages to the team that is voting
|
||||
* Arguments:
|
||||
* * voter: the mafia role that is trying to vote for...
|
||||
* * target: the mafia role that is getting voted for
|
||||
* * vote_type: type of vote submitted (is this the day vote? is this the mafia night vote?)
|
||||
* * teams: see mafia team defines for what to put in, makes the messages only send to a specific team (so mafia night votes only sending messages to mafia at night)
|
||||
*/
|
||||
/datum/mafia_controller/proc/vote_for(datum/mafia_role/voter,datum/mafia_role/target,vote_type, teams)
|
||||
if(!votes[vote_type])
|
||||
votes[vote_type] = list()
|
||||
var/old_vote = votes[vote_type][voter]
|
||||
if(old_vote && old_vote == target)
|
||||
votes[vote_type] -= voter
|
||||
else
|
||||
votes[vote_type][voter] = target
|
||||
if(old_vote && old_vote == target)
|
||||
send_message("<span class='notice'>[voter.body.real_name] retracts their vote for [target.body.real_name]!</span>", team = teams)
|
||||
else
|
||||
send_message("<span class='notice'>[voter.body.real_name] voted for [target.body.real_name]!</span>",team = teams)
|
||||
if(!teams)
|
||||
target.body.update_icon() //Update the vote display if it's a public vote
|
||||
var/datum/mafia_role/old = old_vote
|
||||
if(old)
|
||||
old.body.update_icon()
|
||||
|
||||
/**
|
||||
* Clears out the votes of a certain type (day votes, mafia kill votes) while leaving others untouched
|
||||
*/
|
||||
/datum/mafia_controller/proc/reset_votes(vote_type)
|
||||
var/list/bodies_to_update = list()
|
||||
for(var/vote in votes[vote_type])
|
||||
var/datum/mafia_role/R = votes[vote_type][vote]
|
||||
bodies_to_update += R.body
|
||||
votes[vote_type] = list()
|
||||
for(var/mob/M in bodies_to_update)
|
||||
M.update_icon()
|
||||
|
||||
/**
|
||||
* Returns how many people voted for the role, in whatever vote (day vote, night kill vote)
|
||||
* Arguments:
|
||||
* * role: the mafia role the proc tries to get the amount of votes for
|
||||
* * vote_type: the vote type (getting how many day votes were for the role, or mafia night votes for the role)
|
||||
*/
|
||||
/datum/mafia_controller/proc/get_vote_count(role,vote_type)
|
||||
. = 0
|
||||
for(var/v in votes[vote_type])
|
||||
var/datum/mafia_role/votee = v
|
||||
if(votes[vote_type][votee] == role)
|
||||
. += votee.vote_power
|
||||
|
||||
/**
|
||||
* Returns whichever role got the most votes, in whatever vote (day vote, night kill vote)
|
||||
* returns null if no votes
|
||||
* Arguments:
|
||||
* * vote_type: the vote type (getting the role that got the most day votes, or the role that got the most mafia votes)
|
||||
*/
|
||||
/datum/mafia_controller/proc/get_vote_winner(vote_type)
|
||||
var/list/tally = list()
|
||||
for(var/votee in votes[vote_type])
|
||||
if(!tally[votes[vote_type][votee]])
|
||||
tally[votes[vote_type][votee]] = 1
|
||||
else
|
||||
tally[votes[vote_type][votee]] += 1
|
||||
sortTim(tally,/proc/cmp_numeric_dsc,associative=TRUE)
|
||||
return length(tally) ? tally[1] : null
|
||||
|
||||
/**
|
||||
* Returns a random person who voted for whatever vote (day vote, night kill vote)
|
||||
* Arguments:
|
||||
* * vote_type: vote type (getting a random day voter, or mafia night voter)
|
||||
*/
|
||||
/datum/mafia_controller/proc/get_random_voter(vote_type)
|
||||
if(length(votes[vote_type]))
|
||||
return pick(votes[vote_type])
|
||||
|
||||
/**
|
||||
* Adds mutable appearances to people who get publicly voted on (so not night votes) showing how many people are picking them
|
||||
* Arguments:
|
||||
* * source: the body of the role getting the overlays
|
||||
* * overlay_list: signal var passing the overlay list of the mob
|
||||
*/
|
||||
/datum/mafia_controller/proc/display_votes(atom/source, list/overlay_list)
|
||||
if(phase != MAFIA_PHASE_VOTING)
|
||||
return
|
||||
var/v = get_vote_count(player_role_lookup[source],"Day")
|
||||
var/mutable_appearance/MA = mutable_appearance('icons/obj/mafia.dmi',"vote_[v > 12 ? "over_12" : v]")
|
||||
overlay_list += MA
|
||||
|
||||
/**
|
||||
* Called when the game is setting up, AFTER map is loaded but BEFORE the phase timers start. Creates and places each role's body and gives the correct player key
|
||||
*
|
||||
* Notably:
|
||||
* * Toggles godmode so the mafia players cannot kill themselves
|
||||
* * Adds signals for voting overlays, see display_votes proc
|
||||
* * gives mafia panel
|
||||
* * sends the greeting text (goals, role name, etc)
|
||||
*/
|
||||
/datum/mafia_controller/proc/create_bodies()
|
||||
for(var/datum/mafia_role/role in all_roles)
|
||||
var/mob/living/carbon/human/H = new(get_turf(role.assigned_landmark))
|
||||
H.equipOutfit(player_outfit)
|
||||
H.status_flags |= GODMODE
|
||||
RegisterSignal(H,COMSIG_ATOM_UPDATE_OVERLAYS,.proc/display_votes)
|
||||
var/datum/action/innate/mafia_panel/mafia_panel = new(null,src)
|
||||
mafia_panel.Grant(H)
|
||||
var/client/player_client = GLOB.directory[role.player_key]
|
||||
if(player_client)
|
||||
player_client.prefs.copy_to(H)
|
||||
if(H.dna.species.outfit_important_for_life) //plasmamen
|
||||
H.set_species(/datum/species/human)
|
||||
role.body = H
|
||||
player_role_lookup[H] = role
|
||||
H.key = role.player_key
|
||||
role.greet()
|
||||
|
||||
/datum/mafia_controller/ui_data(mob/user)
|
||||
. = ..()
|
||||
switch(phase)
|
||||
if(MAFIA_PHASE_DAY,MAFIA_PHASE_VOTING,MAFIA_PHASE_JUDGEMENT)
|
||||
.["phase"] = "Day [turn]"
|
||||
if(MAFIA_PHASE_NIGHT)
|
||||
.["phase"] = "Night [turn]"
|
||||
else
|
||||
.["phase"] = "No Game"
|
||||
if(user.client?.holder)
|
||||
.["admin_controls"] = TRUE //show admin buttons to start/setup/stop
|
||||
if(phase == MAFIA_PHASE_JUDGEMENT)
|
||||
.["judgement_phase"] = TRUE //show judgement section
|
||||
else
|
||||
.["judgement_phase"] = FALSE
|
||||
var/datum/mafia_role/user_role = player_role_lookup[user]
|
||||
if(user_role)
|
||||
.["roleinfo"] = list("role" = user_role.name,"desc" = user_role.desc, "action_log" = user_role.role_notes, "hud_icon" = user_role.hud_icon, "revealed_icon" = user_role.revealed_icon)
|
||||
var/actions = list()
|
||||
for(var/action in user_role.actions)
|
||||
if(user_role.validate_action_target(src,action,null))
|
||||
actions += action
|
||||
.["actions"] = actions
|
||||
.["role_theme"] = user_role.special_theme
|
||||
else
|
||||
var/list/lobby_data = list()
|
||||
for(var/key in GLOB.mafia_signup + GLOB.mafia_bad_signup)
|
||||
var/list/lobby_member = list()
|
||||
lobby_member["name"] = key
|
||||
lobby_member["status"] = "Ready"
|
||||
if(key in GLOB.mafia_bad_signup)
|
||||
lobby_member["status"] = "Disconnected"
|
||||
lobby_member["spectating"] = "Ghost"
|
||||
if(key in spectators)
|
||||
lobby_member["spectating"] = "Spectator"
|
||||
lobby_data += list(lobby_member)
|
||||
.["lobbydata"] = lobby_data
|
||||
var/list/player_data = list()
|
||||
for(var/datum/mafia_role/R in all_roles)
|
||||
var/list/player_info = list()
|
||||
var/list/actions = list()
|
||||
if(user_role) //not observer
|
||||
for(var/action in user_role.targeted_actions)
|
||||
if(user_role.validate_action_target(src,action,R))
|
||||
actions += action
|
||||
//Awful snowflake, could use generalizing
|
||||
if(phase == MAFIA_PHASE_VOTING)
|
||||
player_info["votes"] = get_vote_count(R,"Day")
|
||||
if(R.game_status == MAFIA_ALIVE && R != user_role)
|
||||
actions += "Vote"
|
||||
if(phase == MAFIA_PHASE_NIGHT && user_role.team == MAFIA_TEAM_MAFIA && R.game_status == MAFIA_ALIVE && R.team != MAFIA_TEAM_MAFIA)
|
||||
actions += "Kill Vote"
|
||||
player_info["name"] = R.body.real_name
|
||||
player_info["ref"] = REF(R)
|
||||
player_info["actions"] = actions
|
||||
player_info["alive"] = R.game_status == MAFIA_ALIVE
|
||||
player_data += list(player_info)
|
||||
.["players"] = player_data
|
||||
.["timeleft"] = next_phase_timer ? timeleft(next_phase_timer) : 0
|
||||
|
||||
//Not sure on this, should this info be visible
|
||||
.["all_roles"] = current_setup_text
|
||||
|
||||
/datum/mafia_controller/ui_assets(mob/user)
|
||||
return list(
|
||||
get_asset_datum(/datum/asset/spritesheet/mafia),
|
||||
)
|
||||
|
||||
/datum/mafia_controller/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
var/datum/mafia_role/user_role = player_role_lookup[usr]
|
||||
//Admin actions
|
||||
if(usr.client?.holder)
|
||||
switch(action)
|
||||
if("new_game")
|
||||
end_game()
|
||||
basic_setup()
|
||||
if("nuke")
|
||||
end_game()
|
||||
qdel(src)
|
||||
if("next_phase")
|
||||
var/datum/timedevent/timer = SStimer.timer_id_dict[next_phase_timer]
|
||||
if(!timer.spent)
|
||||
var/datum/callback/tc = timer.callBack
|
||||
deltimer(next_phase_timer)
|
||||
tc.InvokeAsync()
|
||||
return TRUE
|
||||
if("players_home")
|
||||
var/list/failed = list()
|
||||
for(var/datum/mafia_role/player in all_roles)
|
||||
if(!player.body)
|
||||
failed += player
|
||||
continue
|
||||
player.body.forceMove(get_turf(player.assigned_landmark))
|
||||
if(failed.len)
|
||||
to_chat(usr, "List of players who no longer had a body (if you see this, the game is runtiming anyway so just hit \"New Game\" to end it)")
|
||||
for(var/i in failed)
|
||||
var/datum/mafia_role/fail = i
|
||||
to_chat(usr, fail.player_key)
|
||||
if("debug_setup")
|
||||
var/list/debug_setup = list()
|
||||
var/list/rolelist_dict = list()
|
||||
var/done = FALSE
|
||||
for(var/p in typesof(/datum/mafia_role))
|
||||
var/datum/mafia_role/path = p
|
||||
rolelist_dict[initial(path.name) + " ([uppertext(initial(path.team))])"] = path
|
||||
rolelist_dict = list("CANCEL", "FINISH") + rolelist_dict
|
||||
while(!done)
|
||||
to_chat(usr, "You have a total player count of [assoc_value_sum(debug_setup)] in this setup.")
|
||||
var/chosen_role_name = input(usr,"Select a role!","Custom Setup Creation",rolelist_dict[1]) as null|anything in rolelist_dict
|
||||
if(chosen_role_name == "CANCEL")
|
||||
return
|
||||
if(chosen_role_name == "FINISH")
|
||||
break
|
||||
var/found_path = rolelist_dict[chosen_role_name]
|
||||
var/role_count = input(usr,"How many? Zero to cancel.","Custom Setup Creation",0) as null|num
|
||||
if(role_count > 0)
|
||||
debug_setup[found_path] = role_count
|
||||
custom_setup = debug_setup
|
||||
if("cancel_setup")
|
||||
custom_setup = list()
|
||||
switch(action) //both living and dead
|
||||
if("mf_lookup")
|
||||
var/role_lookup = params["atype"]
|
||||
var/datum/mafia_role/helper
|
||||
for(var/datum/mafia_role/role in all_roles)
|
||||
if(role_lookup == role.name)
|
||||
helper = role
|
||||
break
|
||||
helper.show_help(usr)
|
||||
if(!user_role)//just the dead
|
||||
var/client/C = ui.user.client
|
||||
switch(action)
|
||||
if("mf_signup")
|
||||
if(!SSticker.HasRoundStarted())
|
||||
to_chat(usr, "<span class='warning'>Wait for the round to start.</span>")
|
||||
return
|
||||
if(GLOB.mafia_signup[C.ckey])
|
||||
GLOB.mafia_signup -= C.ckey
|
||||
to_chat(usr, "<span class='notice'>You unregister from Mafia.</span>")
|
||||
return
|
||||
else
|
||||
GLOB.mafia_signup[C.ckey] = C
|
||||
to_chat(usr, "<span class='notice'>You sign up for Mafia.</span>")
|
||||
if(phase == MAFIA_PHASE_SETUP)
|
||||
check_signups()
|
||||
try_autostart()
|
||||
if("mf_spectate")
|
||||
if(C.ckey in spectators)
|
||||
to_chat(usr, "<span class='notice'>You will no longer get messages from the game.</span>")
|
||||
spectators -= C.ckey
|
||||
else
|
||||
to_chat(usr, "<span class='notice'>You will now get messages from the game.</span>")
|
||||
spectators += C.ckey
|
||||
if(user_role.game_status == MAFIA_DEAD)
|
||||
return
|
||||
//User actions (just living)
|
||||
switch(action)
|
||||
if("mf_action")
|
||||
if(!user_role.actions.Find(params["atype"]))
|
||||
return
|
||||
user_role.handle_action(src,params["atype"],null)
|
||||
return TRUE //vals for self-ui update
|
||||
if("mf_targ_action")
|
||||
var/datum/mafia_role/target = locate(params["target"]) in all_roles
|
||||
if(!istype(target))
|
||||
return
|
||||
switch(params["atype"])
|
||||
if("Vote")
|
||||
if(phase != MAFIA_PHASE_VOTING)
|
||||
return
|
||||
vote_for(user_role,target,vote_type="Day")
|
||||
if("Kill Vote")
|
||||
if(phase != MAFIA_PHASE_NIGHT || user_role.team != MAFIA_TEAM_MAFIA)
|
||||
return
|
||||
vote_for(user_role,target,"Mafia", MAFIA_TEAM_MAFIA)
|
||||
to_chat(user_role.body,"You will vote for [target.body.real_name] for tonights killing.")
|
||||
else
|
||||
if(!user_role.targeted_actions.Find(params["atype"]))
|
||||
return
|
||||
if(!user_role.validate_action_target(src,params["atype"],target))
|
||||
return
|
||||
user_role.handle_action(src,params["atype"],target)
|
||||
return TRUE
|
||||
if(user_role != on_trial)
|
||||
switch(action)
|
||||
if("vote_abstain")
|
||||
if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_abstain_votes))
|
||||
return
|
||||
to_chat(user_role.body,"You have decided to abstain.")
|
||||
judgement_innocent_votes -= user_role
|
||||
judgement_guilty_votes -= user_role
|
||||
judgement_abstain_votes += user_role
|
||||
if("vote_innocent")
|
||||
if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_innocent_votes))
|
||||
return
|
||||
to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as INNOCENT!")
|
||||
judgement_abstain_votes -= user_role//no fakers, and...
|
||||
judgement_guilty_votes -= user_role//no radical centrism
|
||||
judgement_innocent_votes += user_role
|
||||
if("vote_guilty")
|
||||
if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_guilty_votes))
|
||||
return
|
||||
to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as GUILTY!")
|
||||
judgement_abstain_votes -= user_role//no fakers, and...
|
||||
judgement_innocent_votes -= user_role//no radical centrism
|
||||
judgement_guilty_votes += user_role
|
||||
|
||||
/datum/mafia_controller/ui_state(mob/user)
|
||||
return GLOB.always_state
|
||||
|
||||
/datum/mafia_controller/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, null)
|
||||
if(!ui)
|
||||
ui = new(user, src, "MafiaPanel")
|
||||
ui.set_autoupdate(FALSE)
|
||||
ui.open()
|
||||
|
||||
/proc/assoc_value_sum(list/L)
|
||||
. = 0
|
||||
for(var/key in L)
|
||||
. += L[key]
|
||||
|
||||
/**
|
||||
* Returns a semirandom setup, with...
|
||||
* Town, Two invest roles, one protect role, sometimes a misc role, and the rest assistants for town.
|
||||
* Mafia, 2 normal mafia and one special.
|
||||
* Neutral, two disruption roles, sometimes one is a killing.
|
||||
*
|
||||
* See _defines.dm in the mafia folder for a rundown on what these groups of roles include.
|
||||
*/
|
||||
/datum/mafia_controller/proc/generate_random_setup()
|
||||
var/invests_left = 2
|
||||
var/protects_left = 1
|
||||
var/miscs_left = prob(35)
|
||||
var/mafiareg_left = 2
|
||||
var/mafiaspe_left = 1
|
||||
var/killing_role = prob(50)
|
||||
var/disruptors = killing_role ? 1 : 2 //still required to calculate overflow
|
||||
var/overflow_left = max_player - (invests_left + protects_left + miscs_left + mafiareg_left + mafiaspe_left + killing_role + disruptors)
|
||||
|
||||
var/list/random_setup = list()
|
||||
for(var/i in 1 to max_player) //should match the number of roles to add
|
||||
if(overflow_left)
|
||||
add_setup_role(random_setup, TOWN_OVERFLOW)
|
||||
overflow_left--
|
||||
else if(invests_left)
|
||||
add_setup_role(random_setup, TOWN_INVEST)
|
||||
invests_left--
|
||||
else if(protects_left)
|
||||
add_setup_role(random_setup, TOWN_PROTECT)
|
||||
protects_left--
|
||||
else if(miscs_left)
|
||||
add_setup_role(random_setup, TOWN_MISC)
|
||||
miscs_left--
|
||||
else if(mafiareg_left)
|
||||
add_setup_role(random_setup, MAFIA_REGULAR)
|
||||
mafiareg_left--
|
||||
else if(mafiaspe_left)
|
||||
add_setup_role(random_setup, MAFIA_SPECIAL)
|
||||
mafiaspe_left--
|
||||
else if(killing_role)
|
||||
add_setup_role(random_setup, NEUTRAL_KILL)
|
||||
killing_role--
|
||||
else
|
||||
add_setup_role(random_setup, NEUTRAL_DISRUPT)
|
||||
return random_setup
|
||||
|
||||
/**
|
||||
* Helper proc that adds a random role of a type to a setup. if it doesn't exist in the setup, it adds the path to the list and otherwise bumps the path in the list up one
|
||||
*/
|
||||
/datum/mafia_controller/proc/add_setup_role(setup_list, wanted_role_type)
|
||||
var/list/role_type_paths = list()
|
||||
for(var/path in typesof(/datum/mafia_role))
|
||||
var/datum/mafia_role/instance = path
|
||||
if(initial(instance.role_type) == wanted_role_type)
|
||||
role_type_paths += instance
|
||||
|
||||
var/mafia_path = pick(role_type_paths)
|
||||
var/datum/mafia_role/mafia_path_type = mafia_path
|
||||
var/found_role
|
||||
for(var/searched_path in setup_list)
|
||||
var/datum/mafia_role/searched_path_type = searched_path
|
||||
if(initial(mafia_path_type.name) == initial(searched_path_type.name))
|
||||
found_role = searched_path
|
||||
break
|
||||
if(found_role)
|
||||
setup_list[found_role] += 1
|
||||
return
|
||||
setup_list[mafia_path] = 1
|
||||
|
||||
/**
|
||||
* Called when enough players have signed up to fill a setup. DOESN'T NECESSARILY MEAN THE GAME WILL START.
|
||||
*
|
||||
* Checks for a custom setup, if so gets the required players from that and if not it sets the player requirement to required_player(max_player) and generates one IF basic setup starts a game.
|
||||
* Checks if everyone signed up is an observer, and is still connected. If people aren't, they're removed from the list.
|
||||
* If there aren't enough players post sanity, it aborts. otherwise, it selects enough people for the game and starts preparing the game for real.
|
||||
*/
|
||||
/datum/mafia_controller/proc/basic_setup()
|
||||
var/req_players
|
||||
var/list/setup = custom_setup
|
||||
if(!setup.len)
|
||||
req_players = required_player //max_player
|
||||
else
|
||||
req_players = assoc_value_sum(setup)
|
||||
|
||||
//final list for all the players who will be in this game
|
||||
var/list/filtered_keys = list()
|
||||
//cuts invalid players from signups (disconnected/not a ghost)
|
||||
var/list/possible_keys = list()
|
||||
for(var/key in GLOB.mafia_signup)
|
||||
if(GLOB.directory[key])
|
||||
var/client/C = GLOB.directory[key]
|
||||
if(isobserver(C.mob))
|
||||
possible_keys += key
|
||||
continue
|
||||
GLOB.mafia_signup -= key //not valid to play when we checked so remove them from signups
|
||||
|
||||
//if there were not enough players, don't start. we already trimmed the list to now hold only valid signups
|
||||
if(length(possible_keys) < req_players)
|
||||
return
|
||||
else //hacky implementation of max players
|
||||
req_players = clamp(length(possible_keys), 1, max_player)
|
||||
|
||||
//if there were too many players, still start but only make filtered keys as big as it needs to be (cut excess)
|
||||
//also removes people who do get into final player list from the signup so they have to sign up again when game ends
|
||||
for(var/i in 1 to req_players)
|
||||
var/chosen_key = pick_n_take(possible_keys)
|
||||
filtered_keys += chosen_key
|
||||
GLOB.mafia_signup -= chosen_key
|
||||
//small message about not getting into this game for clarity on why they didn't get in
|
||||
for(var/unpicked in possible_keys)
|
||||
var/client/unpicked_client = GLOB.directory[unpicked]
|
||||
to_chat(unpicked_client, "<span class='danger'>Sorry, the starting mafia game has too many players and you were not picked.</span>")
|
||||
to_chat(unpicked_client, "<span class='warning'>You're still signed up, getting messages from the current round, and have another chance to join when the one starting now finishes.</span>")
|
||||
|
||||
if(!setup.len) //don't actually have one yet, so generate a max player random setup. it's good to do this here instead of above so it doesn't generate one every time a game could possibly start.
|
||||
setup = generate_random_setup()
|
||||
prepare_game(setup,filtered_keys)
|
||||
start_game()
|
||||
|
||||
/**
|
||||
* Called when someone signs up, and sees if there are enough people in the signup list to begin.
|
||||
*
|
||||
* Only checks if everyone is actually valid to start (still connected and an observer) if there are enough players (basic_setup)
|
||||
*/
|
||||
/datum/mafia_controller/proc/try_autostart()
|
||||
if(phase != MAFIA_PHASE_SETUP) // || !(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME))
|
||||
return
|
||||
if(GLOB.mafia_signup.len >= max_player || GLOB.mafia_signup.len >= required_player|| custom_setup.len)//enough people to try and make something (or debug mode)
|
||||
basic_setup()
|
||||
|
||||
/**
|
||||
* Filters inactive player into a different list until they reconnect, and removes players who are no longer ghosts.
|
||||
*
|
||||
* If a disconnected player gets a non-ghost mob and reconnects, they will be first put back into mafia_signup then filtered by that.
|
||||
*/
|
||||
/datum/mafia_controller/proc/check_signups()
|
||||
for(var/bad_key in GLOB.mafia_bad_signup)
|
||||
if(GLOB.directory[bad_key])//they have reconnected if we can search their key and get a client
|
||||
GLOB.mafia_bad_signup -= bad_key
|
||||
GLOB.mafia_signup += bad_key
|
||||
for(var/key in GLOB.mafia_signup)
|
||||
var/client/C = GLOB.directory[key]
|
||||
if(!C)//vice versa but in a variable we use later
|
||||
GLOB.mafia_signup -= key
|
||||
GLOB.mafia_bad_signup += key
|
||||
if(!isobserver(C.mob))
|
||||
//they are back to playing the game, remove them from the signups
|
||||
GLOB.mafia_signup -= key
|
||||
|
||||
/datum/action/innate/mafia_panel
|
||||
name = "Mafia Panel"
|
||||
desc = "Use this to play."
|
||||
icon_icon = 'icons/obj/mafia.dmi'
|
||||
button_icon_state = "board"
|
||||
var/datum/mafia_controller/parent
|
||||
|
||||
/datum/action/innate/mafia_panel/New(Target,mf)
|
||||
. = ..()
|
||||
parent = mf
|
||||
|
||||
/datum/action/innate/mafia_panel/Activate()
|
||||
parent.ui_interact(owner)
|
||||
|
||||
/**
|
||||
* Creates the global datum for playing mafia games, destroys the last if that's required and returns the new.
|
||||
*/
|
||||
/proc/create_mafia_game()
|
||||
if(GLOB.mafia_game)
|
||||
QDEL_NULL(GLOB.mafia_game)
|
||||
var/datum/mafia_controller/MF = new()
|
||||
return MF
|
||||
@@ -0,0 +1,79 @@
|
||||
/obj/effect/landmark/mafia_game_area //locations where mafia will be loaded by the datum
|
||||
name = "Mafia Area Spawn"
|
||||
var/game_id = "mafia"
|
||||
|
||||
/obj/effect/landmark/mafia
|
||||
name = "Mafia Player Spawn"
|
||||
var/game_id = "mafia"
|
||||
|
||||
/obj/effect/landmark/mafia/town_center
|
||||
name = "Mafia Town Center"
|
||||
|
||||
//for ghosts/admins
|
||||
/obj/mafia_game_board
|
||||
name = "Mafia Game Board"
|
||||
icon = 'icons/obj/mafia.dmi'
|
||||
icon_state = "board"
|
||||
anchored = TRUE
|
||||
var/game_id = "mafia"
|
||||
var/datum/mafia_controller/MF
|
||||
|
||||
/obj/mafia_game_board/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
if(!MF)
|
||||
MF = GLOB.mafia_game
|
||||
if(!MF)
|
||||
MF = create_mafia_game()
|
||||
MF.ui_interact(user)
|
||||
|
||||
/area/mafia
|
||||
name = "Mafia Minigame"
|
||||
icon_state = "mafia"
|
||||
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
|
||||
requires_power = FALSE
|
||||
has_gravity = STANDARD_GRAVITY
|
||||
flags_1 = NONE
|
||||
// block_suicide = TRUE
|
||||
|
||||
/datum/map_template/mafia
|
||||
var/description = ""
|
||||
|
||||
/datum/map_template/mafia/summerball
|
||||
name = "Summerball 2020"
|
||||
description = "The original, the OG. The 2020 Summer ball was where mafia came from, with this map."
|
||||
mappath = "_maps/map_files/Mafia/mafia_ball.dmm"
|
||||
|
||||
/datum/map_template/mafia/syndicate
|
||||
name = "Syndicate Megastation"
|
||||
description = "Yes, it's a very confusing day at the Megastation. Will the syndicate conflict resolution operatives succeed?"
|
||||
mappath = "_maps/map_files/Mafia/mafia_syndie.dmm"
|
||||
|
||||
/datum/map_template/mafia/lavaland
|
||||
name = "Lavaland Excursion"
|
||||
description = "The station has no idea what's going down on lavaland right now, we got changelings... traitors, and worst of all... lawyers roleblocking you every night."
|
||||
mappath = "_maps/map_files/Mafia/mafia_lavaland.dmm"
|
||||
|
||||
/datum/map_template/mafia/ufo
|
||||
name = "Alien Mothership"
|
||||
description = "The haunted ghost UFO tour has gone south and now it's up to our fine townies and scare seekers to kill the actual real alien changelings..."
|
||||
mappath = "_maps/map_files/Mafia/mafia_ayylmao.dmm"
|
||||
|
||||
/datum/map_template/mafia/spider_clan
|
||||
name = "Spider Clan Kidnapping"
|
||||
description = "New and improved spider clan kidnappings are a lot less boring and have a lot more lynching. Damn westaboos!"
|
||||
mappath = "_maps/map_files/Mafia/mafia_spiderclan.dmm"
|
||||
|
||||
/datum/map_template/mafia/snowy
|
||||
name = "Snowdin"
|
||||
description = "Based off of the icey moon map of the same name, the guy who reworked it pretty much did it for nothing since away missions are disabled but at least he'll get this...?"
|
||||
mappath = "_maps/map_files/Mafia/mafia_snow.dmm"
|
||||
|
||||
/datum/map_template/mafia/gothic
|
||||
name = "Vampire's Castle"
|
||||
description = "Vampires and changelings clash to find out who's the superior bloodsucking monster in this creepy castle map."
|
||||
mappath = "_maps/map_files/Mafia/mafia_gothic.dmm"
|
||||
|
||||
/datum/map_template/mafia/reebe
|
||||
name = "Reebe"
|
||||
description = "Trouble in Reebe station! Copypaste guranteed by ClockCo™"
|
||||
mappath = "_maps/map_files/Mafia/mafia_reebe.dmm"
|
||||
@@ -0,0 +1,108 @@
|
||||
|
||||
//what people wear unrevealed
|
||||
|
||||
/datum/outfit/mafia
|
||||
name = "Mafia Game Outfit"
|
||||
uniform = /obj/item/clothing/under/color/grey
|
||||
shoes = /obj/item/clothing/shoes/sneakers/black
|
||||
|
||||
//town
|
||||
|
||||
/datum/outfit/mafia/assistant
|
||||
name = "Mafia Assistant"
|
||||
|
||||
uniform = /obj/item/clothing/under/color/rainbow
|
||||
|
||||
/datum/outfit/mafia/detective
|
||||
name = "Mafia Detective"
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/security/detective
|
||||
// neck = /obj/item/clothing/neck/tie/detective
|
||||
shoes = /obj/item/clothing/shoes/sneakers/brown
|
||||
suit = /obj/item/clothing/suit/det_suit
|
||||
gloves = /obj/item/clothing/gloves/color/black
|
||||
head = /obj/item/clothing/head/fedora/det_hat
|
||||
mask = /obj/item/clothing/mask/cigarette
|
||||
|
||||
/datum/outfit/mafia/psychologist
|
||||
name = "Mafia Psychologist"
|
||||
|
||||
uniform = /obj/item/clothing/under/suit/black
|
||||
shoes = /obj/item/clothing/shoes/laceup
|
||||
|
||||
/datum/outfit/mafia/md
|
||||
name = "Mafia Medical Doctor"
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/medical/doctor
|
||||
shoes = /obj/item/clothing/shoes/sneakers/white
|
||||
suit = /obj/item/clothing/suit/toggle/labcoat
|
||||
|
||||
/datum/outfit/mafia/chaplain
|
||||
name = "Mafia Chaplain"
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/civilian/chaplain
|
||||
|
||||
/datum/outfit/mafia/lawyer
|
||||
name = "Mafia Lawyer"
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/civilian/lawyer/bluesuit
|
||||
suit = /obj/item/clothing/suit/toggle/lawyer
|
||||
shoes = /obj/item/clothing/shoes/laceup
|
||||
|
||||
/datum/outfit/mafia/hop
|
||||
name = "Mafia Head of Personnel"
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/civilian/head_of_personnel
|
||||
suit = /obj/item/clothing/suit/armor/vest/alt
|
||||
shoes = /obj/item/clothing/shoes/sneakers/brown
|
||||
head = /obj/item/clothing/head/hopcap
|
||||
glasses = /obj/item/clothing/glasses/sunglasses
|
||||
|
||||
//mafia
|
||||
|
||||
/datum/outfit/mafia/changeling
|
||||
name = "Mafia Changeling"
|
||||
|
||||
head = /obj/item/clothing/head/helmet/changeling
|
||||
suit = /obj/item/clothing/suit/armor/changeling
|
||||
|
||||
//solo
|
||||
|
||||
/datum/outfit/mafia/fugitive
|
||||
name = "Mafia Fugitive"
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/prisoner
|
||||
shoes = /obj/item/clothing/shoes/sneakers/orange
|
||||
|
||||
/datum/outfit/mafia/obsessed
|
||||
name = "Mafia Obsessed"
|
||||
uniform = /obj/item/clothing/under/misc/overalls
|
||||
shoes = /obj/item/clothing/shoes/sneakers/white
|
||||
gloves = /obj/item/clothing/gloves/color/latex
|
||||
mask = /obj/item/clothing/mask/surgical
|
||||
suit = /obj/item/clothing/suit/apron
|
||||
|
||||
/datum/outfit/mafia/obsessed/post_equip(mob/living/carbon/human/H)
|
||||
for(var/obj/item/carried_item in H.get_equipped_items(TRUE))
|
||||
carried_item.add_mob_blood(H)//Oh yes, there will be blood...
|
||||
H.regenerate_icons()
|
||||
|
||||
/datum/outfit/mafia/clown
|
||||
name = "Mafia Clown"
|
||||
|
||||
uniform = /obj/item/clothing/under/rank/civilian/clown
|
||||
shoes = /obj/item/clothing/shoes/clown_shoes
|
||||
mask = /obj/item/clothing/mask/gas/clown_hat
|
||||
|
||||
/datum/outfit/mafia/traitor
|
||||
name = "Mafia Traitor"
|
||||
|
||||
mask = /obj/item/clothing/mask/gas/syndicate
|
||||
uniform = /obj/item/clothing/under/syndicate/tacticool
|
||||
shoes = /obj/item/clothing/shoes/jackboots
|
||||
|
||||
/datum/outfit/mafia/nightmare
|
||||
name = "Mafia Nightmare"
|
||||
|
||||
uniform = null
|
||||
shoes = null
|
||||
@@ -0,0 +1,705 @@
|
||||
/datum/mafia_role
|
||||
var/name = "Assistant"
|
||||
var/desc = "You are a crewmember without any special abilities."
|
||||
var/win_condition = "kill all mafia and solo killing roles."
|
||||
var/team = MAFIA_TEAM_TOWN
|
||||
///how the random setup chooses which roles get put in
|
||||
var/role_type = TOWN_OVERFLOW
|
||||
|
||||
var/player_key
|
||||
var/mob/living/carbon/human/body
|
||||
var/obj/effect/landmark/mafia/assigned_landmark
|
||||
|
||||
///how many votes submitted when you vote.
|
||||
var/vote_power = 1
|
||||
var/detect_immune = FALSE
|
||||
var/revealed = FALSE
|
||||
var/datum/outfit/revealed_outfit = /datum/outfit/mafia/assistant //the assistants need a special path to call out they were in fact assistant, everything else can just use job equipment
|
||||
//action = uses
|
||||
var/list/actions = list()
|
||||
var/list/targeted_actions = list()
|
||||
//what the role gets when it wins a game
|
||||
// var/winner_award = /datum/award/achievement/mafia/assistant
|
||||
|
||||
//so mafia have to also kill them to have a majority
|
||||
var/solo_counts_as_town = FALSE //(don't set this for town)
|
||||
var/game_status = MAFIA_ALIVE
|
||||
|
||||
///icon state in the mafia dmi of the hud of the role, used in the mafia ui
|
||||
var/hud_icon = "hudassistant"
|
||||
///icon state in the mafia dmi of the hud of the role, used in the mafia ui
|
||||
var/revealed_icon = "assistant"
|
||||
///set this to something cool for antagonists and their window will look different
|
||||
var/special_theme
|
||||
|
||||
var/list/role_notes = list()
|
||||
|
||||
|
||||
/datum/mafia_role/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
|
||||
/datum/mafia_role/proc/kill(datum/mafia_controller/game,lynch=FALSE)
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_ON_KILL,game,lynch) & MAFIA_PREVENT_KILL)
|
||||
return FALSE
|
||||
game_status = MAFIA_DEAD
|
||||
body.death()
|
||||
if(lynch)
|
||||
reveal_role(game, verbose = TRUE)
|
||||
if(!(player_key in game.spectators)) //people who played will want to see the end of the game more often than not
|
||||
game.spectators += player_key
|
||||
return TRUE
|
||||
|
||||
/datum/mafia_role/Destroy(force, ...)
|
||||
QDEL_NULL(body)
|
||||
. = ..()
|
||||
|
||||
/datum/mafia_role/proc/greet()
|
||||
SEND_SOUND(body, 'sound/ambience/ambifailure.ogg')
|
||||
to_chat(body,"<span class='danger'>You are the [name].</span>")
|
||||
to_chat(body,"<span class='danger'>[desc]</span>")
|
||||
switch(team)
|
||||
if(MAFIA_TEAM_MAFIA)
|
||||
to_chat(body,"<span class='danger'>You and your co-conspirators win if you outnumber crewmembers.</span>")
|
||||
if(MAFIA_TEAM_TOWN)
|
||||
to_chat(body,"<span class='danger'>You are a crewmember. Find out and lynch the changelings!</span>")
|
||||
if(MAFIA_TEAM_SOLO)
|
||||
to_chat(body,"<span class='danger'>You are not aligned to town or mafia. Accomplish your own objectives!</span>")
|
||||
to_chat(body, "<b>Be sure to read <a href=\"https://tgstation13.org/wiki/Mafia\">the wiki page</a> to learn more, if you have no idea what's going on.</b>")
|
||||
|
||||
/datum/mafia_role/proc/reveal_role(datum/mafia_controller/game, verbose = FALSE)
|
||||
if(revealed)
|
||||
return
|
||||
if(verbose)
|
||||
game.send_message("<span class='big bold notice'>It is revealed that the true role of [body] [game_status == MAFIA_ALIVE ? "is" : "was"] [name]!</span>")
|
||||
var/list/oldoutfit = body.get_equipped_items()
|
||||
for(var/thing in oldoutfit)
|
||||
qdel(thing)
|
||||
special_reveal_equip(game)
|
||||
body.equipOutfit(revealed_outfit)
|
||||
revealed = TRUE
|
||||
|
||||
/datum/mafia_role/proc/special_reveal_equip(datum/mafia_controller/game)
|
||||
return
|
||||
|
||||
/datum/mafia_role/proc/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
return
|
||||
|
||||
/datum/mafia_role/proc/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,action,target) & MAFIA_PREVENT_ACTION)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/mafia_role/proc/add_note(note)
|
||||
role_notes += note
|
||||
|
||||
/datum/mafia_role/proc/check_total_victory(alive_town, alive_mafia) //solo antags can win... solo.
|
||||
return FALSE
|
||||
|
||||
/datum/mafia_role/proc/block_team_victory(alive_town, alive_mafia) //solo antags can also block team wins.
|
||||
return FALSE
|
||||
|
||||
/datum/mafia_role/proc/show_help(clueless)
|
||||
var/list/result = list()
|
||||
var/team_desc = ""
|
||||
var/team_span = ""
|
||||
var/the = TRUE
|
||||
switch(team)
|
||||
if(MAFIA_TEAM_TOWN)
|
||||
team_desc = "Town"
|
||||
team_span = "nicegreen"
|
||||
if(MAFIA_TEAM_MAFIA)
|
||||
team_desc = "Mafia"
|
||||
team_span = "red"
|
||||
if(MAFIA_TEAM_SOLO)
|
||||
team_desc = "Nobody"
|
||||
team_span = "comradio"
|
||||
the = FALSE
|
||||
result += "<span class='notice'>The <span class='bold'>[name]</span> is aligned with [the ? "the " : ""]<span class='[team_span]'>[team_desc]</span></span>"
|
||||
result += "<span class='bold notice'>\"[desc]\"</span>"
|
||||
result += "<span class='notice'>[name] wins when they [win_condition]</span>"
|
||||
to_chat(clueless, result.Join("</br>"))
|
||||
|
||||
/datum/mafia_role/detective
|
||||
name = "Detective"
|
||||
desc = "You can investigate a single person each night to learn their team."
|
||||
revealed_outfit = /datum/outfit/mafia/detective
|
||||
role_type = TOWN_INVEST
|
||||
// winner_award = /datum/award/achievement/mafia/detective
|
||||
|
||||
hud_icon = "huddetective"
|
||||
revealed_icon = "detective"
|
||||
|
||||
targeted_actions = list("Investigate")
|
||||
|
||||
var/datum/mafia_role/current_investigation
|
||||
|
||||
/datum/mafia_role/detective/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/investigate)
|
||||
|
||||
/datum/mafia_role/detective/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src
|
||||
|
||||
/datum/mafia_role/detective/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
if(!target || target.game_status != MAFIA_ALIVE)
|
||||
to_chat(body,"<span class='warning'>You can only investigate alive people.</span>")
|
||||
return
|
||||
to_chat(body,"<span class='warning'>You will investigate [target.body.real_name] tonight.</span>")
|
||||
current_investigation = target
|
||||
|
||||
/datum/mafia_role/detective/proc/investigate(datum/mafia_controller/game)
|
||||
var/datum/mafia_role/target = current_investigation
|
||||
if(target)
|
||||
if(target.detect_immune)
|
||||
to_chat(body,"<span class='warning'>Your investigations reveal that [target.body.real_name] is a true member of the station.</span>")
|
||||
add_note("N[game.turn] - [target.body.real_name] - Town")
|
||||
else
|
||||
var/team_text
|
||||
var/fluff
|
||||
switch(target.team)
|
||||
if(MAFIA_TEAM_TOWN)
|
||||
team_text = "Town"
|
||||
fluff = "a true member of the station."
|
||||
if(MAFIA_TEAM_MAFIA)
|
||||
team_text = "Mafia"
|
||||
fluff = "an unfeeling, hideous changeling!"
|
||||
if(MAFIA_TEAM_SOLO)
|
||||
team_text = "Solo"
|
||||
fluff = "a rogue, with their own objectives..."
|
||||
to_chat(body,"<span class='warning'>Your investigations reveal that [target.body.real_name] is [fluff]</span>")
|
||||
add_note("N[game.turn] - [target.body.real_name] - [team_text]")
|
||||
current_investigation = null
|
||||
|
||||
/datum/mafia_role/psychologist
|
||||
name = "Psychologist"
|
||||
desc = "You can visit someone ONCE PER GAME to reveal their true role in the morning!"
|
||||
revealed_outfit = /datum/outfit/mafia/psychologist
|
||||
role_type = TOWN_INVEST
|
||||
// winner_award = /datum/award/achievement/mafia/psychologist
|
||||
|
||||
hud_icon = "hudpsychologist"
|
||||
revealed_icon = "psychologist"
|
||||
|
||||
targeted_actions = list("Reveal")
|
||||
var/datum/mafia_role/current_target
|
||||
var/can_use = TRUE
|
||||
|
||||
/datum/mafia_role/psychologist/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/therapy_reveal)
|
||||
|
||||
/datum/mafia_role/psychologist/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!. || !can_use || game.phase == MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || target.revealed || target == src)
|
||||
return FALSE
|
||||
|
||||
/datum/mafia_role/psychologist/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
to_chat(body,"<span class='warning'>You will reveal [target.body.real_name] tonight.</span>")
|
||||
current_target = target
|
||||
|
||||
/datum/mafia_role/psychologist/proc/therapy_reveal(datum/mafia_controller/game)
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"reveal",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by a lawyer.
|
||||
current_target = null
|
||||
if(current_target)
|
||||
add_note("N[game.turn] - [current_target.body.real_name] - Revealed true identity")
|
||||
to_chat(body,"<span class='warning'>You have revealed the true nature of the [current_target]!</span>")
|
||||
current_target.reveal_role(game, verbose = TRUE)
|
||||
current_target = null
|
||||
can_use = FALSE
|
||||
|
||||
/datum/mafia_role/chaplain
|
||||
name = "Chaplain"
|
||||
desc = "You can communicate with spirits of the dead each night to discover dead crewmember roles."
|
||||
revealed_outfit = /datum/outfit/mafia/chaplain
|
||||
role_type = TOWN_INVEST
|
||||
hud_icon = "hudchaplain"
|
||||
revealed_icon = "chaplain"
|
||||
// winner_award = /datum/award/achievement/mafia/chaplain
|
||||
|
||||
targeted_actions = list("Pray")
|
||||
var/current_target
|
||||
|
||||
/datum/mafia_role/chaplain/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/commune)
|
||||
|
||||
/datum/mafia_role/chaplain/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_DEAD && target != src && !target.revealed
|
||||
|
||||
/datum/mafia_role/chaplain/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
to_chat(body,"<span class='warning'>You will commune with the spirit of [target.body.real_name] tonight.</span>")
|
||||
current_target = target
|
||||
|
||||
/datum/mafia_role/chaplain/proc/commune(datum/mafia_controller/game)
|
||||
var/datum/mafia_role/target = current_target
|
||||
if(target)
|
||||
to_chat(body,"<span class='warning'>You invoke spirit of [target.body.real_name] and learn their role was <b>[target.name]<b>.</span>")
|
||||
add_note("N[game.turn] - [target.body.real_name] - [target.name]")
|
||||
current_target = null
|
||||
|
||||
/datum/mafia_role/md
|
||||
name = "Medical Doctor"
|
||||
desc = "You can protect a single person each night from killing."
|
||||
revealed_outfit = /datum/outfit/mafia/md // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
|
||||
role_type = TOWN_PROTECT
|
||||
hud_icon = "hudmedicaldoctor"
|
||||
revealed_icon = "medicaldoctor"
|
||||
// winner_award = /datum/award/achievement/mafia/md
|
||||
|
||||
targeted_actions = list("Protect")
|
||||
var/datum/mafia_role/current_protected
|
||||
|
||||
/datum/mafia_role/md/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/protect)
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/end_protection)
|
||||
|
||||
/datum/mafia_role/md/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(target.name == "Head of Personnel" && target.revealed)
|
||||
return FALSE
|
||||
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src
|
||||
|
||||
/datum/mafia_role/md/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
if(!target || target.game_status != MAFIA_ALIVE)
|
||||
to_chat(body,"<span class='warning'>You can only protect alive people.</span>")
|
||||
return
|
||||
to_chat(body,"<span class='warning'>You will protect [target.body.real_name] tonight.</span>")
|
||||
current_protected = target
|
||||
|
||||
/datum/mafia_role/md/proc/protect(datum/mafia_controller/game)
|
||||
if(current_protected)
|
||||
RegisterSignal(current_protected,COMSIG_MAFIA_ON_KILL,.proc/prevent_kill)
|
||||
add_note("N[game.turn] - Protected [current_protected.body.real_name]")
|
||||
|
||||
/datum/mafia_role/md/proc/prevent_kill(datum/source)
|
||||
to_chat(body,"<span class='warning'>The person you protected tonight was attacked!</span>")
|
||||
to_chat(current_protected.body,"<span class='userdanger'>You were attacked last night, but someone nursed you back to life!</span>")
|
||||
return MAFIA_PREVENT_KILL
|
||||
|
||||
/datum/mafia_role/md/proc/end_protection(datum/mafia_controller/game)
|
||||
if(current_protected)
|
||||
UnregisterSignal(current_protected,COMSIG_MAFIA_ON_KILL)
|
||||
current_protected = null
|
||||
|
||||
/datum/mafia_role/lawyer
|
||||
name = "Lawyer"
|
||||
desc = "You can choose a person during the day to provide extensive legal advice to during the night, preventing night actions."
|
||||
revealed_outfit = /datum/outfit/mafia/lawyer
|
||||
role_type = TOWN_PROTECT
|
||||
hud_icon = "hudlawyer"
|
||||
revealed_icon = "lawyer"
|
||||
// winner_award = /datum/award/achievement/mafia/lawyer
|
||||
|
||||
targeted_actions = list("Advise")
|
||||
var/datum/mafia_role/current_target
|
||||
|
||||
/datum/mafia_role/lawyer/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/roleblock_text)
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_START,.proc/try_to_roleblock)
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/release)
|
||||
|
||||
/datum/mafia_role/lawyer/proc/roleblock_text(datum/mafia_controller/game)
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"roleblock",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by another lawyer.
|
||||
current_target = null
|
||||
if(current_target)
|
||||
to_chat(current_target.body,"<span class='big bold red'>YOU HAVE BEEN BLOCKED! YOU CANNOT PERFORM ANY ACTIONS TONIGHT.</span>")
|
||||
add_note("N[game.turn] - [current_target.body.real_name] - Blocked")
|
||||
|
||||
/datum/mafia_role/lawyer/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(game.phase == MAFIA_PHASE_NIGHT)
|
||||
return FALSE
|
||||
if(target.game_status != MAFIA_ALIVE)
|
||||
return FALSE
|
||||
|
||||
/datum/mafia_role/lawyer/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(target == current_target)
|
||||
current_target = null
|
||||
to_chat(body,"<span class='warning'>You have decided against blocking anyone tonight.</span>")
|
||||
else
|
||||
current_target = target
|
||||
to_chat(body,"<span class='warning'>You will block [target.body.real_name] tonight.</span>")
|
||||
|
||||
/datum/mafia_role/lawyer/proc/try_to_roleblock(datum/mafia_controller/game)
|
||||
if(current_target)
|
||||
RegisterSignal(current_target,COMSIG_MAFIA_CAN_PERFORM_ACTION, .proc/prevent_action)
|
||||
|
||||
/datum/mafia_role/lawyer/proc/release(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
if(current_target)
|
||||
UnregisterSignal(current_target, COMSIG_MAFIA_CAN_PERFORM_ACTION)
|
||||
current_target = null
|
||||
|
||||
/datum/mafia_role/lawyer/proc/prevent_action(datum/source)
|
||||
if(game_status == MAFIA_ALIVE) //in case we got killed while imprisoning sk - bad luck edge
|
||||
return MAFIA_PREVENT_ACTION
|
||||
|
||||
/datum/mafia_role/hop
|
||||
name = "Head of Personnel"
|
||||
desc = "You can reveal yourself once per game, tripling your vote power but becoming unable to be protected!"
|
||||
revealed_outfit = /datum/outfit/mafia/hop
|
||||
role_type = TOWN_MISC
|
||||
hud_icon = "hudheadofpersonnel"
|
||||
revealed_icon = "headofpersonnel"
|
||||
// winner_award = /datum/award/achievement/mafia/hop
|
||||
|
||||
targeted_actions = list("Reveal")
|
||||
|
||||
/datum/mafia_role/hop/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!. || game.phase == MAFIA_PHASE_NIGHT || game.turn == 1 || target.game_status != MAFIA_ALIVE || target != src || revealed)
|
||||
return FALSE
|
||||
|
||||
/datum/mafia_role/hop/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
reveal_role(game, TRUE)
|
||||
vote_power = 2
|
||||
|
||||
///MAFIA ROLES/// only one until i rework this to allow more, they're the "anti-town" working to kill off townies to win
|
||||
|
||||
/datum/mafia_role/mafia
|
||||
name = "Changeling"
|
||||
desc = "You're a member of the changeling hive. Use ':j' talk prefix to talk to your fellow lings."
|
||||
team = MAFIA_TEAM_MAFIA
|
||||
role_type = MAFIA_REGULAR
|
||||
hud_icon = "hudchangeling"
|
||||
revealed_icon = "changeling"
|
||||
// winner_award = /datum/award/achievement/mafia/changeling
|
||||
|
||||
revealed_outfit = /datum/outfit/mafia/changeling
|
||||
special_theme = "syndicate"
|
||||
win_condition = "become majority over the town and no solo killing role can stop them."
|
||||
|
||||
/datum/mafia_role/mafia/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/mafia_text)
|
||||
|
||||
/datum/mafia_role/mafia/proc/mafia_text(datum/mafia_controller/source)
|
||||
to_chat(body,"<b>Vote for who to kill tonight. The killer will be chosen randomly from voters.</b>")
|
||||
|
||||
//better detective for mafia
|
||||
/datum/mafia_role/mafia/thoughtfeeder
|
||||
name = "Thoughtfeeder"
|
||||
desc = "You're a changeling variant that feeds on the memories of others. Use ':j' talk prefix to talk to your fellow lings, and visit people at night to learn their role."
|
||||
role_type = MAFIA_SPECIAL
|
||||
hud_icon = "hudthoughtfeeder"
|
||||
revealed_icon = "thoughtfeeder"
|
||||
// winner_award = /datum/award/achievement/mafia/thoughtfeeder
|
||||
|
||||
targeted_actions = list("Learn Role")
|
||||
var/datum/mafia_role/current_investigation
|
||||
|
||||
/datum/mafia_role/mafia/thoughtfeeder/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/investigate)
|
||||
|
||||
/datum/mafia_role/mafia/thoughtfeeder/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src
|
||||
|
||||
/datum/mafia_role/mafia/thoughtfeeder/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
|
||||
to_chat(body,"<span class='warning'>You will feast on the memories of [target.body.real_name] tonight.</span>")
|
||||
current_investigation = target
|
||||
|
||||
/datum/mafia_role/mafia/thoughtfeeder/proc/investigate(datum/mafia_controller/game)
|
||||
var/datum/mafia_role/target = current_investigation
|
||||
current_investigation = null
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"thoughtfeed",target) & MAFIA_PREVENT_ACTION)
|
||||
to_chat(body,"<span class='warning'>You were unable to investigate [target.body.real_name].</span>")
|
||||
add_note("N[game.turn] - [target.body.real_name] - Unable to investigate")
|
||||
return
|
||||
if(target)
|
||||
if(target.detect_immune)
|
||||
to_chat(body,"<span class='warning'>[target.body.real_name]'s memories reveal that they are the Assistant.</span>")
|
||||
add_note("N[game.turn] - [target.body.real_name] - Assistant")
|
||||
else
|
||||
to_chat(body,"<span class='warning'>[target.body.real_name]'s memories reveal that they are the [target.name].</span>")
|
||||
add_note("N[game.turn] - [target.body.real_name] - [target.name]")
|
||||
|
||||
|
||||
///SOLO ROLES/// they range from anomalous factors to deranged killers that try to win alone.
|
||||
|
||||
/datum/mafia_role/traitor
|
||||
name = "Traitor"
|
||||
desc = "You're a solo traitor. You are immune to night kills, can kill every night and you win by outnumbering everyone else."
|
||||
win_condition = "kill everyone."
|
||||
team = MAFIA_TEAM_SOLO
|
||||
role_type = NEUTRAL_KILL
|
||||
// winner_award = /datum/award/achievement/mafia/traitor
|
||||
|
||||
targeted_actions = list("Night Kill")
|
||||
revealed_outfit = /datum/outfit/mafia/traitor
|
||||
|
||||
hud_icon = "hudtraitor"
|
||||
revealed_icon = "traitor"
|
||||
special_theme = "neutral"
|
||||
|
||||
var/datum/mafia_role/current_victim
|
||||
|
||||
/datum/mafia_role/traitor/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/nightkill_immunity)
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_KILL_PHASE,.proc/try_to_kill)
|
||||
|
||||
/datum/mafia_role/traitor/check_total_victory(alive_town, alive_mafia) //serial killers just want teams dead
|
||||
return alive_town + alive_mafia <= 1
|
||||
|
||||
/datum/mafia_role/traitor/block_team_victory(alive_town, alive_mafia) //no team can win until they're dead
|
||||
return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point)
|
||||
|
||||
/datum/mafia_role/traitor/proc/nightkill_immunity(datum/source,datum/mafia_controller/game,lynch)
|
||||
if(game.phase == MAFIA_PHASE_NIGHT && !lynch)
|
||||
to_chat(body,"<span class='userdanger'>You were attacked, but they'll have to try harder than that to put you down.</span>")
|
||||
return MAFIA_PREVENT_KILL
|
||||
|
||||
/datum/mafia_role/traitor/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(game.phase != MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || target == src)
|
||||
return FALSE
|
||||
|
||||
/datum/mafia_role/traitor/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
current_victim = target
|
||||
to_chat(body,"<span class='warning'>You will attempt to kill [target.body.real_name] tonight.</span>")
|
||||
|
||||
/datum/mafia_role/traitor/proc/try_to_kill(datum/mafia_controller/source)
|
||||
var/datum/mafia_role/target = current_victim
|
||||
current_victim = null
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"traitor kill",target) & MAFIA_PREVENT_ACTION)
|
||||
return
|
||||
if(game_status == MAFIA_ALIVE && target && target.game_status == MAFIA_ALIVE)
|
||||
if(!target.kill(source))
|
||||
to_chat(body,"<span class='danger'>Your attempt at killing [target.body] was prevented!</span>")
|
||||
|
||||
/datum/mafia_role/nightmare
|
||||
name = "Nightmare"
|
||||
desc = "You're a solo monster that cannot be detected by detective roles. You can flicker lights of another room each night. You can instead decide to hunt, killing everyone in a flickering room. Kill everyone to win."
|
||||
win_condition = "kill everyone."
|
||||
revealed_outfit = /datum/outfit/mafia/nightmare
|
||||
detect_immune = TRUE
|
||||
team = MAFIA_TEAM_SOLO
|
||||
role_type = NEUTRAL_KILL
|
||||
special_theme = "neutral"
|
||||
hud_icon = "hudnightmare"
|
||||
revealed_icon = "nightmare"
|
||||
// winner_award = /datum/award/achievement/mafia/nightmare
|
||||
|
||||
targeted_actions = list("Flicker", "Hunt")
|
||||
var/list/flickering = list()
|
||||
var/datum/mafia_role/flicker_target
|
||||
|
||||
/datum/mafia_role/nightmare/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_KILL_PHASE,.proc/flicker_or_hunt)
|
||||
|
||||
/datum/mafia_role/nightmare/check_total_victory(alive_town, alive_mafia) //nightmares just want teams dead
|
||||
return alive_town + alive_mafia <= 1
|
||||
|
||||
/datum/mafia_role/nightmare/block_team_victory(alive_town, alive_mafia) //no team can win until they're dead
|
||||
return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point)
|
||||
|
||||
/datum/mafia_role/nightmare/special_reveal_equip()
|
||||
body.underwear = "Nude"
|
||||
body.undershirt = "Nude"
|
||||
body.socks = "Nude"
|
||||
body.set_species(/datum/species/shadow)
|
||||
body.update_body()
|
||||
|
||||
/datum/mafia_role/nightmare/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!. || game.phase != MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE)
|
||||
return FALSE
|
||||
if(action == "Flicker")
|
||||
return target != src && !(target in flickering)
|
||||
return target == src
|
||||
|
||||
/datum/mafia_role/nightmare/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(target == flicker_target)
|
||||
to_chat(body,"<span class='warning'>You will do nothing tonight.</span>")
|
||||
flicker_target = null
|
||||
flicker_target = target
|
||||
if(action == "Flicker")
|
||||
to_chat(body,"<span class='warning'>You will attempt to flicker [target.body.real_name]'s room tonight.</span>")
|
||||
else
|
||||
to_chat(body,"<span class='danger'>You will hunt everyone in a flickering room down tonight.</span>")
|
||||
|
||||
/datum/mafia_role/nightmare/proc/flicker_or_hunt(datum/mafia_controller/source)
|
||||
if(game_status != MAFIA_ALIVE || !flicker_target)
|
||||
return
|
||||
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"nightmare actions",flicker_target) & MAFIA_PREVENT_ACTION)
|
||||
to_chat(flicker_target.body, "<span class='warning'>Your actions were prevented!</span>")
|
||||
return
|
||||
var/datum/mafia_role/target = flicker_target
|
||||
flicker_target = null
|
||||
if(target != src) //flicker instead of hunt
|
||||
to_chat(target.body, "<span class='userdanger'>The lights begin to flicker and dim. You're in danger.</span>")
|
||||
flickering += target
|
||||
return
|
||||
for(var/r in flickering)
|
||||
var/datum/mafia_role/role = r
|
||||
if(role && role.game_status == MAFIA_ALIVE)
|
||||
to_chat(role.body, "<span class='userdanger'>A shadowy monster appears out of the darkness!</span>")
|
||||
role.kill(source)
|
||||
flickering -= role
|
||||
|
||||
//just helps read better
|
||||
#define FUGITIVE_NOT_PRESERVING 0//will not become night immune tonight
|
||||
#define FUGITIVE_WILL_PRESERVE 1 //will become night immune tonight
|
||||
|
||||
/datum/mafia_role/fugitive
|
||||
name = "Fugitive"
|
||||
desc = "You're on the run. You can become immune to night kills exactly twice, and you win by surviving to the end of the game with anyone."
|
||||
win_condition = "survive to the end of the game, with anyone"
|
||||
solo_counts_as_town = TRUE //should not count towards mafia victory, they should have the option to work with town
|
||||
revealed_outfit = /datum/outfit/mafia/fugitive
|
||||
team = MAFIA_TEAM_SOLO
|
||||
role_type = NEUTRAL_DISRUPT
|
||||
special_theme = "neutral"
|
||||
hud_icon = "hudfugitive"
|
||||
revealed_icon = "fugitive"
|
||||
// winner_award = /datum/award/achievement/mafia/fugitive
|
||||
|
||||
actions = list("Self Preservation")
|
||||
var/charges = 2
|
||||
var/protection_status = FUGITIVE_NOT_PRESERVING
|
||||
|
||||
|
||||
/datum/mafia_role/fugitive/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/night_start)
|
||||
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/night_end)
|
||||
RegisterSignal(game,COMSIG_MAFIA_GAME_END,.proc/survived)
|
||||
|
||||
/datum/mafia_role/fugitive/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
|
||||
. = ..()
|
||||
if(!charges)
|
||||
to_chat(body,"<span class='danger'>You're out of supplies and cannot protect yourself anymore.</span>")
|
||||
return
|
||||
if(game.phase == MAFIA_PHASE_NIGHT)
|
||||
to_chat(body,"<span class='danger'>You don't have time to prepare, night has already arrived.</span>")
|
||||
return
|
||||
if(protection_status == FUGITIVE_WILL_PRESERVE)
|
||||
to_chat(body,"<span class='danger'>You decide to not prepare tonight.</span>")
|
||||
else
|
||||
to_chat(body,"<span class='danger'>You decide to prepare for a horrible night.</span>")
|
||||
protection_status = !protection_status
|
||||
|
||||
/datum/mafia_role/fugitive/proc/night_start(datum/mafia_controller/game)
|
||||
if(protection_status == FUGITIVE_WILL_PRESERVE)
|
||||
to_chat(body,"<span class='danger'>Your preparations are complete. Nothing could kill you tonight!</span>")
|
||||
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prevent_death)
|
||||
|
||||
/datum/mafia_role/fugitive/proc/night_end(datum/mafia_controller/game)
|
||||
if(protection_status == FUGITIVE_WILL_PRESERVE)
|
||||
charges--
|
||||
UnregisterSignal(src,COMSIG_MAFIA_ON_KILL)
|
||||
to_chat(body,"<span class='danger'>You are no longer protected. You have [charges] use[charges == 1 ? "" : "s"] left of your power.</span>")
|
||||
protection_status = FUGITIVE_NOT_PRESERVING
|
||||
|
||||
/datum/mafia_role/fugitive/proc/prevent_death(datum/mafia_controller/game)
|
||||
to_chat(body,"<span class='userdanger'>You were attacked! Luckily, you were ready for this!</span>")
|
||||
return MAFIA_PREVENT_KILL
|
||||
|
||||
/datum/mafia_role/fugitive/proc/survived(datum/mafia_controller/game)
|
||||
if(game_status == MAFIA_ALIVE)
|
||||
// var/client/winner_client = GLOB.directory[player_key]
|
||||
// winner_client?.give_award(winner_award, body)
|
||||
game.send_message("<span class='big comradio'>!! FUGITIVE VICTORY !!</span>")
|
||||
|
||||
#undef FUGITIVE_NOT_PRESERVING
|
||||
#undef FUGITIVE_WILL_PRESERVE
|
||||
|
||||
/datum/mafia_role/obsessed
|
||||
name = "Obsessed"
|
||||
desc = "You're completely lost in your own mind. You win by lynching your obsession before you get killed in this mess. Obsession assigned on the first night!"
|
||||
win_condition = "lynch their obsession."
|
||||
revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
|
||||
solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective!
|
||||
team = MAFIA_TEAM_SOLO
|
||||
role_type = NEUTRAL_DISRUPT
|
||||
special_theme = "neutral"
|
||||
hud_icon = "hudobsessed"
|
||||
revealed_icon = "obsessed"
|
||||
|
||||
// winner_award = /datum/award/achievement/mafia/obsessed
|
||||
|
||||
revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
|
||||
solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective!
|
||||
var/datum/mafia_role/obsession
|
||||
var/lynched_target = FALSE
|
||||
|
||||
/datum/mafia_role/obsessed/New(datum/mafia_controller/game) //note: obsession is always a townie
|
||||
. = ..()
|
||||
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/find_obsession)
|
||||
|
||||
/datum/mafia_role/obsessed/proc/find_obsession(datum/mafia_controller/game)
|
||||
var/list/all_roles_shuffle = shuffle(game.all_roles)
|
||||
for(var/role in all_roles_shuffle)
|
||||
var/datum/mafia_role/possible = role
|
||||
if(possible.team == MAFIA_TEAM_TOWN && possible.game_status != MAFIA_DEAD)
|
||||
obsession = possible
|
||||
break
|
||||
if(!obsession)
|
||||
obsession = pick(all_roles_shuffle) //okay no town just pick anyone here
|
||||
//if you still don't have an obsession you're playing a single player game like i can't help your dumb ass
|
||||
to_chat(body, "<span class='userdanger'>Your obsession is [obsession.body.real_name]! Get them lynched to win!</span>")
|
||||
add_note("N[game.turn] - I vowed to watch my obsession, [obsession.body.real_name], hang!") //it'll always be N1 but whatever
|
||||
RegisterSignal(obsession,COMSIG_MAFIA_ON_KILL,.proc/check_victory)
|
||||
UnregisterSignal(game,COMSIG_MAFIA_SUNDOWN)
|
||||
|
||||
/datum/mafia_role/obsessed/proc/check_victory(datum/source,datum/mafia_controller/game,lynch)
|
||||
UnregisterSignal(source,COMSIG_MAFIA_ON_KILL)
|
||||
if(game_status == MAFIA_DEAD)
|
||||
return
|
||||
if(lynch)
|
||||
game.send_message("<span class='big comradio'>!! OBSESSED VICTORY !!</span>")
|
||||
// var/client/winner_client = GLOB.directory[player_key]
|
||||
// winner_client?.give_award(winner_award, body)
|
||||
reveal_role(game, FALSE)
|
||||
else
|
||||
to_chat(body, "<span class='userdanger'>You have failed your objective to lynch [obsession.body]!</span>")
|
||||
|
||||
/datum/mafia_role/clown
|
||||
name = "Clown"
|
||||
desc = "If you are lynched you take down one of your voters (guilty or abstain) with you and win. HONK!"
|
||||
win_condition = "get themselves lynched!"
|
||||
revealed_outfit = /datum/outfit/mafia/clown
|
||||
solo_counts_as_town = TRUE
|
||||
team = MAFIA_TEAM_SOLO
|
||||
role_type = NEUTRAL_DISRUPT
|
||||
special_theme = "neutral"
|
||||
hud_icon = "hudclown"
|
||||
revealed_icon = "clown"
|
||||
// winner_award = /datum/award/achievement/mafia/clown
|
||||
|
||||
/datum/mafia_role/clown/New(datum/mafia_controller/game)
|
||||
. = ..()
|
||||
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prank)
|
||||
|
||||
/datum/mafia_role/clown/proc/prank(datum/source,datum/mafia_controller/game,lynch)
|
||||
if(lynch)
|
||||
var/datum/mafia_role/victim = pick(game.judgement_guilty_votes + game.judgement_abstain_votes)
|
||||
game.send_message("<span class='big clown'>[body.real_name] WAS A CLOWN! HONK! They take down [victim.body.real_name] with their last prank.</span>")
|
||||
game.send_message("<span class='big clown'>!! CLOWN VICTORY !!</span>")
|
||||
// var/client/winner_client = GLOB.directory[player_key]
|
||||
// winner_client?.give_award(winner_award, body)
|
||||
victim.kill(game,FALSE)
|
||||
@@ -183,7 +183,6 @@
|
||||
attack_verb = list("stabbed", "diced", "sliced", "cleaved", "chopped", "lacerated", "cut", "jabbed", "punctured")
|
||||
icon_state = "crusher-glaive"
|
||||
item_state = "crusher0-glaive"
|
||||
item_flags = ITEM_CAN_PARRY
|
||||
block_parry_data = /datum/block_parry_data/crusherglaive
|
||||
//ideas: altclick that lets you pummel people with the handguard/handle?
|
||||
//parrying functionality?
|
||||
@@ -217,6 +216,16 @@
|
||||
if(owner.Adjacent(attacker) && (!attacker.anchored || ismegafauna(attacker))) // free backstab, if you perfect parry
|
||||
attacker.dir = get_dir(owner,attacker)
|
||||
|
||||
/// triggered on wield of two handed item
|
||||
/obj/item/kinetic_crusher/glaive/on_wield(obj/item/source, mob/user)
|
||||
wielded = TRUE
|
||||
item_flags |= (ITEM_CAN_PARRY)
|
||||
|
||||
/// triggered on unwield of two handed item
|
||||
/obj/item/kinetic_crusher/glaive/on_unwield(obj/item/source, mob/user)
|
||||
wielded = FALSE
|
||||
item_flags &= ~(ITEM_CAN_PARRY)
|
||||
|
||||
/obj/item/kinetic_crusher/glaive/update_icon_state()
|
||||
item_state = "crusher[wielded]-glaive" // this is not icon_state and not supported by 2hcomponent
|
||||
|
||||
|
||||
@@ -184,6 +184,12 @@
|
||||
extra = TRUE
|
||||
extra_color_src = MUTCOLORS3
|
||||
|
||||
/datum/sprite_accessory/mam_snouts/skulldog
|
||||
name = "Skulldog"
|
||||
icon_state = "skulldog"
|
||||
extra = TRUE
|
||||
extra_color_src = MATRIXED
|
||||
|
||||
/datum/sprite_accessory/mam_snouts/lcanid
|
||||
name = "Mammal, Long"
|
||||
icon_state = "lcanid"
|
||||
|
||||
@@ -348,6 +348,12 @@
|
||||
name = "Cowboy Shirt Shortsleeved Red"
|
||||
icon_state = "cowboyshirt_reds"
|
||||
|
||||
/datum/sprite_accessory/underwear/top/bra_binder
|
||||
name = "Bra (binder)"
|
||||
icon_state = "bra_binder"
|
||||
has_color = TRUE
|
||||
|
||||
|
||||
|
||||
/datum/sprite_accessory/underwear/top/bra_binder_strapless
|
||||
name = "Bra (binder, strapless)"
|
||||
icon_state = "bra_binder_strapless"
|
||||
has_color = TRUE
|
||||
|
||||
@@ -905,6 +905,22 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
else
|
||||
to_chat(usr, "Can't become a pAI candidate while not dead!")
|
||||
|
||||
/mob/dead/observer/verb/mafia_game_signup()
|
||||
set category = "Ghost"
|
||||
set name = "Signup for Mafia"
|
||||
set desc = "Sign up for a game of Mafia to pass the time while dead."
|
||||
mafia_signup()
|
||||
/mob/dead/observer/proc/mafia_signup()
|
||||
if(!client)
|
||||
return
|
||||
if(!isobserver(src))
|
||||
to_chat(usr, "<span class='warning'>You must be a ghost to join mafia!</span>")
|
||||
return
|
||||
var/datum/mafia_controller/game = GLOB.mafia_game //this needs to change if you want multiple mafia games up at once.
|
||||
if(!game)
|
||||
game = create_mafia_game("mafia")
|
||||
game.ui_interact(usr)
|
||||
|
||||
/mob/dead/observer/CtrlShiftClick(mob/user)
|
||||
if(isobserver(user) && check_rights(R_SPAWN))
|
||||
change_mob_type( /mob/living/carbon/human , null, null, TRUE) //always delmob, ghosts shouldn't be left lingering
|
||||
|
||||
@@ -382,10 +382,20 @@
|
||||
|
||||
return 0
|
||||
|
||||
//Outdated but still in use apparently. This should at least be a human proc.
|
||||
//Daily reminder to murder this - Remie.
|
||||
/**
|
||||
* Used to return a list of equipped items on a mob; does not include held items (use get_all_gear)
|
||||
*
|
||||
* Argument(s):
|
||||
* * Optional - include_pockets (TRUE/FALSE), whether or not to include the pockets and suit storage in the returned list
|
||||
*/
|
||||
|
||||
/mob/living/proc/get_equipped_items(include_pockets = FALSE)
|
||||
return
|
||||
var/list/items = list()
|
||||
for(var/obj/item/I in contents)
|
||||
if(I.item_flags & IN_INVENTORY)
|
||||
items += I
|
||||
items -= held_items
|
||||
return items
|
||||
|
||||
/mob/living/proc/unequip_everything()
|
||||
var/list/items = list()
|
||||
@@ -476,5 +486,19 @@
|
||||
hand_bodyparts[i] = BP
|
||||
..() //Don't redraw hands until we have organs for them
|
||||
|
||||
|
||||
//GetAllContents that is reasonable and not stupid
|
||||
/mob/living/carbon/proc/get_all_gear()
|
||||
var/list/processing_list = get_equipped_items(include_pockets = TRUE) + held_items
|
||||
listclearnulls(processing_list) // handles empty hands
|
||||
var/i = 0
|
||||
while(i < length(processing_list) )
|
||||
var/atom/A = processing_list[++i]
|
||||
if(SEND_SIGNAL(A, COMSIG_CONTAINS_STORAGE))
|
||||
var/list/item_stuff = list()
|
||||
SEND_SIGNAL(A, COMSIG_TRY_STORAGE_RETURN_INVENTORY, item_stuff)
|
||||
processing_list += item_stuff
|
||||
return processing_list
|
||||
|
||||
/mob/canReachInto(atom/user, atom/target, list/next, view_only, obj/item/tool)
|
||||
return ..() && (user == src)
|
||||
|
||||
@@ -299,10 +299,10 @@
|
||||
|
||||
|
||||
/mob/living/carbon/human/ex_act(severity, target, origin)
|
||||
if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src))
|
||||
if(TRAIT_BOMBIMMUNE in dna.species.species_traits)
|
||||
return
|
||||
..()
|
||||
if (!severity)
|
||||
if (!severity || QDELETED(src))
|
||||
return
|
||||
var/brute_loss = 0
|
||||
var/burn_loss = 0
|
||||
|
||||
@@ -1,31 +1,17 @@
|
||||
/mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
|
||||
return dna.species.can_equip(I, slot, disable_warning, src, bypass_equip_delay_self, clothing_check, return_warning)
|
||||
|
||||
/**
|
||||
* Used to return a list of equipped items on a human mob; does not include held items (use get_all_gear)
|
||||
*
|
||||
* Argument(s):
|
||||
* * Optional - include_pockets (TRUE/FALSE), whether or not to include the pockets and suit storage in the returned list
|
||||
*/
|
||||
|
||||
/mob/living/carbon/human/get_equipped_items(include_pockets = FALSE)
|
||||
var/list/items = ..()
|
||||
if(belt)
|
||||
items += belt
|
||||
if(ears)
|
||||
items += ears
|
||||
if(glasses)
|
||||
items += glasses
|
||||
if(gloves)
|
||||
items += gloves
|
||||
if(shoes)
|
||||
items += shoes
|
||||
if(wear_id)
|
||||
items += wear_id
|
||||
if(wear_suit)
|
||||
items += wear_suit
|
||||
if(w_uniform)
|
||||
items += w_uniform
|
||||
if(include_pockets)
|
||||
if(l_store)
|
||||
items += l_store
|
||||
if(r_store)
|
||||
items += r_store
|
||||
if(s_store)
|
||||
items += s_store
|
||||
if(!include_pockets)
|
||||
items -= list(l_store, r_store, s_store)
|
||||
return items
|
||||
|
||||
// Return the item currently in the slot ID
|
||||
@@ -169,7 +155,7 @@
|
||||
//Item is handled and in slot, valid to call callback, for this proc should always be true
|
||||
if(!not_handled)
|
||||
I.equipped(src, slot)
|
||||
|
||||
update_genitals()
|
||||
return not_handled //For future deeper overrides
|
||||
|
||||
/mob/living/carbon/human/equipped_speed_mods()
|
||||
@@ -257,6 +243,7 @@
|
||||
s_store = null
|
||||
if(!QDELETED(src))
|
||||
update_inv_s_store()
|
||||
update_genitals()
|
||||
|
||||
/mob/living/carbon/human/wear_mask_update(obj/item/clothing/C, toggle_off = 1)
|
||||
if((C.flags_inv & (HIDEHAIR|HIDEFACIALHAIR)) || (initial(C.flags_inv) & (HIDEHAIR|HIDEFACIALHAIR)))
|
||||
|
||||
@@ -124,6 +124,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
limbs_id = id
|
||||
..()
|
||||
|
||||
//update our mutant bodyparts to include unlocked ones
|
||||
mutant_bodyparts += GLOB.unlocked_mutant_parts
|
||||
|
||||
/proc/generate_selectable_species(clear = FALSE)
|
||||
if(clear)
|
||||
GLOB.roundstart_races = list()
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
brutemod = 1.25 //They're weak to punches
|
||||
attack_type = BURN //burn bish
|
||||
damage_overlay_type = "" //We are too cool for regular damage overlays
|
||||
species_traits = list(MUTCOLORS, NO_UNDERWEAR, HAIR, HAS_FLESH, HAS_BONE) // i mean i guess they have blood so they can have wounds too
|
||||
species_traits = list(MUTCOLORS, HAIR, HAS_FLESH, HAS_BONE) // i mean i guess they have blood so they can have wounds too
|
||||
species_language_holder = /datum/language_holder/ethereal
|
||||
inherent_traits = list(TRAIT_NOHUNGER)
|
||||
sexes = FALSE
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,DIGITIGRADE)
|
||||
inherent_traits = list(TRAIT_CHUNKYFINGERS)
|
||||
mutantlungs = /obj/item/organ/lungs/ashwalker
|
||||
mutanteyes = /obj/item/organ/eyes/night_vision
|
||||
burnmod = 0.9
|
||||
brutemod = 0.9
|
||||
species_language_holder = /datum/language_holder/lizard/ash
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
blacklisted = 0
|
||||
sexes = 0
|
||||
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton
|
||||
species_traits = list(NOBLOOD,NOGENITALS,NOAROUSAL,HAS_BONE)
|
||||
inherent_traits = list(TRAIT_NOBREATH,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_FAKEDEATH, TRAIT_CALCIUM_HEALER)
|
||||
species_traits = list(NOBLOOD,NOGENITALS,NOAROUSAL,HAS_BONE,NOTRANSSTING)
|
||||
inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_CALCIUM_HEALER)
|
||||
inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID
|
||||
mutanttongue = /obj/item/organ/tongue/bone
|
||||
damage_overlay_type = ""//let's not show bloody wounds or burns over bones.
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
/datum/species/skeleton/New()
|
||||
if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) //skeletons are stronger during the spooky season!
|
||||
inherent_traits |= list(TRAIT_RESISTHEAT,TRAIT_RESISTCOLD)
|
||||
inherent_traits |= list(TRAIT_RESISTHEAT, TRAIT_NOBREATH, TRAIT_PIERCEIMMUNE, TRAIT_FAKEDEATH, TRAIT_RESISTCOLD, TRAIT_RADIMMUNE)
|
||||
brutemod = 1
|
||||
burnmod = 1
|
||||
..()
|
||||
|
||||
@@ -117,18 +117,6 @@
|
||||
if(!QDELETED(src))
|
||||
update_inv_legcuffed()
|
||||
|
||||
/mob/living/carbon/get_equipped_items(include_pockets = FALSE)
|
||||
var/list/items = list()
|
||||
if(back)
|
||||
items += back
|
||||
if(head)
|
||||
items += head
|
||||
if(wear_mask)
|
||||
items += wear_mask
|
||||
if(wear_neck)
|
||||
items += wear_neck
|
||||
return items
|
||||
|
||||
//handle stuff to update when a mob equips/unequips a mask.
|
||||
/mob/living/proc/wear_mask_update(obj/item/clothing/C, toggle_off = 1)
|
||||
update_inv_wear_mask()
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
/mob/living/Bump(atom/A)
|
||||
if(..()) //we are thrown onto something
|
||||
return
|
||||
if (buckled || now_pushing)
|
||||
if(buckled || now_pushing)
|
||||
return
|
||||
if(ismob(A))
|
||||
var/mob/M = A
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
DelayNextAction(data.block_end_click_cd_add)
|
||||
return TRUE
|
||||
|
||||
/mob/living/proc/ACTIVE_BLOCK_START(obj/item/I)
|
||||
/mob/living/proc/active_block_start(obj/item/I)
|
||||
if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
|
||||
return FALSE
|
||||
if(!(I in held_items))
|
||||
@@ -109,7 +109,7 @@
|
||||
animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW)
|
||||
return
|
||||
combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING)
|
||||
ACTIVE_BLOCK_START(I)
|
||||
active_block_start(I)
|
||||
|
||||
/**
|
||||
* Gets the first item we can that can block, but if that fails, default to active held item.COMSIG_ENABLE_COMBAT_MODE
|
||||
@@ -180,6 +180,12 @@
|
||||
|
||||
/// Apply the stamina damage to our user, notice how damage argument is stamina_amount.
|
||||
/obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
if(istype(object, /obj/item/projectile))
|
||||
var/obj/item/projectile/P = object
|
||||
if(P.stamina)
|
||||
var/blocked = active_block_calculate_final_damage(owner, object, P.stamina, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return)
|
||||
var/stam = active_block_stamina_cost(owner, object, blocked, attack_text, ATTACK_TYPE_PROJECTILE, armour_penetration, attacker, def_zone, final_block_chance, block_return)
|
||||
stamina_amount += stam
|
||||
var/datum/block_parry_data/data = get_block_parry_data()
|
||||
if(iscarbon(owner))
|
||||
var/mob/living/carbon/C = owner
|
||||
|
||||
@@ -361,7 +361,8 @@
|
||||
"Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekmed"),
|
||||
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamed"),
|
||||
"Eyebot" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "eyebotmed"),
|
||||
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed")
|
||||
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed"),
|
||||
"Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakemedbox")
|
||||
)
|
||||
var/list/L = list("Medihound" = "medihound", "Medihound Dark" = "medihounddark", "Vale" = "valemed")
|
||||
for(var/a in L)
|
||||
@@ -422,6 +423,13 @@
|
||||
moduleselect_icon = "medihound"
|
||||
moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi'
|
||||
dogborg = TRUE
|
||||
if("Drake")
|
||||
cyborg_base_icon = "drakemed"
|
||||
cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
|
||||
sleeper_overlay = "drakemedsleeper"
|
||||
moduleselect_icon = "medihound"
|
||||
moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi'
|
||||
dogborg = TRUE
|
||||
else
|
||||
return FALSE
|
||||
return ..()
|
||||
@@ -478,7 +486,8 @@
|
||||
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "caneng"),
|
||||
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinaeng"),
|
||||
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidereng"),
|
||||
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng")
|
||||
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng"),
|
||||
"Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakeengbox")
|
||||
)
|
||||
var/list/L = list("Pup Dozer" = "pupdozer", "Vale" = "valeeng")
|
||||
for(var/a in L)
|
||||
@@ -536,6 +545,11 @@
|
||||
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
|
||||
sleeper_overlay = "alinasleeper"
|
||||
dogborg = TRUE
|
||||
if("Drake")
|
||||
cyborg_base_icon = "drakeeng"
|
||||
cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
|
||||
sleeper_overlay = "drakesecsleeper"
|
||||
dogborg = TRUE
|
||||
else
|
||||
return FALSE
|
||||
return ..()
|
||||
@@ -574,7 +588,8 @@
|
||||
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "cansec"),
|
||||
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinasec"),
|
||||
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidersec"),
|
||||
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec")
|
||||
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec"),
|
||||
"Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakesecbox")
|
||||
)
|
||||
var/list/L = list("K9" = "k9", "Vale" = "valesec", "K9 Dark" = "k9dark")
|
||||
for(var/a in L)
|
||||
@@ -630,6 +645,11 @@
|
||||
sleeper_overlay = "valesecsleeper"
|
||||
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
|
||||
dogborg = TRUE
|
||||
if("Drake")
|
||||
cyborg_base_icon = "drakesec"
|
||||
cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
|
||||
sleeper_overlay = "drakesecsleeper"
|
||||
dogborg = TRUE
|
||||
else
|
||||
return FALSE
|
||||
return ..()
|
||||
@@ -673,7 +693,8 @@
|
||||
var/static/list/peace_icons = sortList(list(
|
||||
"Default" = image(icon = 'icons/mob/robots.dmi', icon_state = "peace"),
|
||||
"Borgi" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "borgi"),
|
||||
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "whitespider")
|
||||
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "whitespider"),
|
||||
"Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakepeacebox")
|
||||
))
|
||||
var/peace_borg_icon = show_radial_menu(R, R , peace_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE)
|
||||
switch(peace_borg_icon)
|
||||
@@ -689,6 +710,11 @@
|
||||
hat_offset = INFINITY
|
||||
cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi'
|
||||
has_snowflake_deadsprite = TRUE
|
||||
if("Drake")
|
||||
cyborg_base_icon = "drakepeace"
|
||||
cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
|
||||
sleeper_overlay = "drakepeacesleeper"
|
||||
dogborg = TRUE
|
||||
else
|
||||
return FALSE
|
||||
return ..()
|
||||
@@ -827,7 +853,8 @@
|
||||
"(Janitor) Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinajan"),
|
||||
"(Janitor) Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekjan"),
|
||||
"(Janitor) Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canjan"),
|
||||
"(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan")
|
||||
"(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan"),
|
||||
"(Janitor) Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakejanitbox")
|
||||
)
|
||||
var/list/L = list("(Service) DarkK9" = "k50", "(Service) Vale" = "valeserv", "(Service) ValeDark" = "valeservdark",
|
||||
"(Janitor) Scrubpuppy" = "scrubpup")
|
||||
@@ -899,6 +926,11 @@
|
||||
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
|
||||
sleeper_overlay = "jsleeper"
|
||||
dogborg = TRUE
|
||||
if("(Janitor) Drake")
|
||||
cyborg_base_icon = "drakejanit"
|
||||
cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
|
||||
sleeper_overlay = "drakesecsleeper"
|
||||
dogborg = TRUE
|
||||
else
|
||||
return FALSE
|
||||
return ..()
|
||||
@@ -944,7 +976,8 @@
|
||||
"Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekmin"),
|
||||
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamin"),
|
||||
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canmin"),
|
||||
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymin")
|
||||
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymin"),
|
||||
"Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakeminebox")
|
||||
)
|
||||
var/list/L = list("Blade" = "blade", "Vale" = "valemine")
|
||||
for(var/a in L)
|
||||
@@ -988,6 +1021,11 @@
|
||||
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
|
||||
sleeper_overlay = "valeminesleeper"
|
||||
dogborg = TRUE
|
||||
if("Drake")
|
||||
cyborg_base_icon = "drakemine"
|
||||
cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi'
|
||||
sleeper_overlay = "drakeminesleeper"
|
||||
dogborg = TRUE
|
||||
else
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
@@ -430,3 +430,6 @@
|
||||
|
||||
/mob/living/silicon/handle_high_gravity(gravity)
|
||||
return
|
||||
|
||||
/mob/living/silicon/rust_heretic_act()
|
||||
adjustBruteLoss(500)
|
||||
|
||||
@@ -1058,3 +1058,6 @@ Pass a positive integer as an argument to override a bot's default speed.
|
||||
if(I)
|
||||
I.icon_state = null
|
||||
path.Cut(1, 2)
|
||||
|
||||
/mob/living/silicon/rust_heretic_act()
|
||||
adjustBruteLoss(500)
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
/mob/living/simple_animal/hostile/eldritch
|
||||
name = "Demon"
|
||||
real_name = "Demon"
|
||||
desc = ""
|
||||
gender = NEUTER
|
||||
mob_biotypes = NONE
|
||||
speak_emote = list("screams")
|
||||
response_help_continuous = "thinks better of touching"
|
||||
response_help_simple = "think better of touching"
|
||||
response_disarm_continuous = "flails at"
|
||||
response_disarm_simple = "flail at"
|
||||
response_harm_continuous = "reaps"
|
||||
response_harm_simple = "tears"
|
||||
speak_chance = 1
|
||||
icon = 'icons/mob/eldritch_mobs.dmi'
|
||||
speed = 0
|
||||
a_intent = INTENT_HARM
|
||||
stop_automated_movement = 1
|
||||
AIStatus = AI_OFF
|
||||
attack_sound = 'sound/weapons/punch1.ogg'
|
||||
see_in_dark = 7
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
||||
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
minbodytemp = 0
|
||||
maxbodytemp = INFINITY
|
||||
healable = 0
|
||||
movement_type = GROUND
|
||||
pressure_resistance = 100
|
||||
del_on_death = TRUE
|
||||
deathmessage = "implodes into itself"
|
||||
faction = list("heretics")
|
||||
simple_mob_flags = SILENCE_RANGED_MESSAGE
|
||||
///Innate spells that are supposed to be added when a beast is created
|
||||
var/list/spells_to_add
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/Initialize()
|
||||
. = ..()
|
||||
add_spells()
|
||||
|
||||
/**
|
||||
* Add_spells
|
||||
*
|
||||
* Goes through spells_to_add and adds each spell to the mind.
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/eldritch/proc/add_spells()
|
||||
for(var/spell in spells_to_add)
|
||||
AddSpell(new spell())
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/raw_prophet
|
||||
name = "Raw Prophet"
|
||||
real_name = "Raw Prophet"
|
||||
desc = "Abomination made from severed limbs."
|
||||
icon_state = "raw_prophet"
|
||||
status_flags = CANPUSH
|
||||
icon_living = "raw_prophet"
|
||||
melee_damage_lower = 5
|
||||
melee_damage_upper = 10
|
||||
maxHealth = 50
|
||||
health = 50
|
||||
sight = SEE_MOBS|SEE_OBJS|SEE_TURFS
|
||||
spells_to_add = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash/long,/obj/effect/proc_holder/spell/pointed/manse_link,/obj/effect/proc_holder/spell/targeted/telepathy/eldritch,/obj/effect/proc_holder/spell/pointed/trigger/blind/eldritch)
|
||||
|
||||
var/list/linked_mobs = list()
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/raw_prophet/Initialize()
|
||||
. = ..()
|
||||
link_mob(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/raw_prophet/Login()
|
||||
. = ..()
|
||||
client.change_view(10)
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/raw_prophet/proc/link_mob(mob/living/mob_linked)
|
||||
if(QDELETED(mob_linked) || mob_linked.stat == DEAD)
|
||||
return FALSE
|
||||
if(HAS_TRAIT(mob_linked, TRAIT_MINDSHIELD)) //mindshield implant, no dice
|
||||
return FALSE
|
||||
if(mob_linked.anti_magic_check(FALSE, FALSE, TRUE, 0))
|
||||
return FALSE
|
||||
if(linked_mobs[mob_linked])
|
||||
return FALSE
|
||||
|
||||
to_chat(mob_linked, "<span class='notice'>You feel something new enter your sphere of mind, you hear whispers of people far away, screeches of horror and a humming of welcome to [src]'s Mansus Link.</span>")
|
||||
var/datum/action/innate/mansus_speech/action = new(src)
|
||||
linked_mobs[mob_linked] = action
|
||||
action.Grant(mob_linked)
|
||||
RegisterSignal(mob_linked, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING) , .proc/unlink_mob)
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/raw_prophet/proc/unlink_mob(mob/living/mob_linked)
|
||||
if(!linked_mobs[mob_linked])
|
||||
return
|
||||
UnregisterSignal(mob_linked, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING))
|
||||
var/datum/action/innate/mansus_speech/action = linked_mobs[mob_linked]
|
||||
action.Remove(mob_linked)
|
||||
qdel(action)
|
||||
to_chat(mob_linked, "<span class='notice'>Your mind shatters as the [src]'s Mansus Link leaves your mind.</span>")
|
||||
mob_linked.emote("Scream")
|
||||
//micro stun
|
||||
mob_linked.AdjustParalyzed(0.5 SECONDS)
|
||||
linked_mobs -= mob_linked
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/raw_prophet/death(gibbed)
|
||||
for(var/linked_mob in linked_mobs)
|
||||
unlink_mob(linked_mob)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy
|
||||
name = "Terror of the Night"
|
||||
real_name = "Armsy"
|
||||
desc = "Abomination made from severed limbs."
|
||||
icon_state = "armsy_start"
|
||||
icon_living = "armsy_start"
|
||||
maxHealth = 200
|
||||
health = 200
|
||||
obj_damage = 80
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 15
|
||||
move_resist = MOVE_FORCE_OVERPOWERING+1
|
||||
movement_type = GROUND
|
||||
environment_smash = ENVIRONMENT_SMASH_RWALLS
|
||||
sight = SEE_MOBS
|
||||
spells_to_add = list(/obj/effect/proc_holder/spell/targeted/worm_contract)
|
||||
ranged = TRUE
|
||||
///Previous segment in the chain
|
||||
var/mob/living/simple_animal/hostile/eldritch/armsy/back
|
||||
///Next segment in the chain
|
||||
var/mob/living/simple_animal/hostile/eldritch/armsy/front
|
||||
///Your old location
|
||||
var/oldloc
|
||||
///Allow / disallow pulling
|
||||
var/allow_pulling = FALSE
|
||||
///How many arms do we have to eat to expand?
|
||||
var/stacks_to_grow = 2
|
||||
///Currently eaten arms
|
||||
var/current_stacks = 0
|
||||
|
||||
//I tried Initalize but it didnt work, like at all. This proc just wouldnt fire if it was Initalize instead of New
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/Initialize(mapload,spawn_more = TRUE,len = 6)
|
||||
. = ..()
|
||||
if(len < 3)
|
||||
stack_trace("Eldritch Armsy created with invalid len ([len]). Reverting to 3.")
|
||||
len = 3 //code breaks below 3, let's just not allow it.
|
||||
oldloc = loc
|
||||
RegisterSignal(src,COMSIG_MOVABLE_MOVED,.proc/update_chain_links)
|
||||
if(!spawn_more)
|
||||
return
|
||||
allow_pulling = TRUE
|
||||
///next link
|
||||
var/mob/living/simple_animal/hostile/eldritch/armsy/next
|
||||
///previous link
|
||||
var/mob/living/simple_animal/hostile/eldritch/armsy/prev
|
||||
///current link
|
||||
var/mob/living/simple_animal/hostile/eldritch/armsy/current
|
||||
for(var/i in 0 to len)
|
||||
prev = current
|
||||
//i tried using switch, but byond is really fucky and it didnt work as intended. Im sorry
|
||||
if(i == 0)
|
||||
current = new type(drop_location(),FALSE)
|
||||
current.icon_state = "armsy_mid"
|
||||
current.icon_living = "armsy_mid"
|
||||
current.front = src
|
||||
current.AIStatus = AI_OFF
|
||||
back = current
|
||||
else if(i < len)
|
||||
current = new type(drop_location(),FALSE)
|
||||
prev.back = current
|
||||
prev.icon_state = "armsy_mid"
|
||||
prev.icon_living = "armsy_mid"
|
||||
prev.front = next
|
||||
prev.AIStatus = AI_OFF
|
||||
else
|
||||
prev.icon_state = "armsy_end"
|
||||
prev.icon_living = "armsy_end"
|
||||
prev.front = next
|
||||
prev.AIStatus = AI_OFF
|
||||
next = prev
|
||||
|
||||
//we are literally a vessel of otherworldly destruction, we bring our own gravity unto this plane
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/has_gravity(turf/T)
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/can_be_pulled()
|
||||
return FALSE
|
||||
|
||||
///Updates chain links to force move onto a single tile
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/proc/contract_next_chain_into_single_tile()
|
||||
if(back)
|
||||
back.forceMove(loc)
|
||||
back.contract_next_chain_into_single_tile()
|
||||
return
|
||||
|
||||
///Updates the next mob in the chain to move to our last location, fixed the worm if somehow broken.
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/proc/update_chain_links()
|
||||
gib_trail()
|
||||
if(back && back.loc != oldloc)
|
||||
back.Move(oldloc)
|
||||
// self fixing properties if somehow broken
|
||||
if(front && loc != front.oldloc)
|
||||
forceMove(front.oldloc)
|
||||
oldloc = loc
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/proc/gib_trail()
|
||||
if(front) // head makes gibs
|
||||
return
|
||||
var/chosen_decal = pick(typesof(/obj/effect/decal/cleanable/blood/tracks))
|
||||
var/obj/effect/decal/cleanable/blood/gibs/decal = new chosen_decal(drop_location())
|
||||
decal.setDir(dir)
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/Destroy()
|
||||
if(front)
|
||||
front.icon_state = "armsy_end"
|
||||
front.icon_living = "armsy_end"
|
||||
front.back = null
|
||||
if(back)
|
||||
QDEL_NULL(back) // chain destruction baby
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/BiologicalLife(seconds, times_fired)
|
||||
adjustBruteLoss(-2)
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/proc/heal()
|
||||
if(health == maxHealth)
|
||||
if(back)
|
||||
back.heal()
|
||||
return
|
||||
else
|
||||
current_stacks++
|
||||
if(current_stacks >= stacks_to_grow)
|
||||
var/mob/living/simple_animal/hostile/eldritch/armsy/prev = new type(drop_location(),spawn_more = FALSE)
|
||||
icon_state = "armsy_mid"
|
||||
icon_living = "armsy_mid"
|
||||
back = prev
|
||||
prev.icon_state = "armsy_end"
|
||||
prev.icon_living = "armsy_end"
|
||||
prev.front = src
|
||||
prev.AIStatus = AI_OFF
|
||||
current_stacks = 0
|
||||
|
||||
adjustBruteLoss(-maxHealth * 0.5, FALSE)
|
||||
adjustFireLoss(-maxHealth * 0.5 ,FALSE)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/Shoot(atom/targeted_atom)
|
||||
target = targeted_atom
|
||||
AttackingTarget()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/AttackingTarget()
|
||||
if(istype(target,/obj/item/bodypart/r_arm) || istype(target,/obj/item/bodypart/l_arm))
|
||||
qdel(target)
|
||||
heal()
|
||||
return
|
||||
if(target == back || target == front)
|
||||
return
|
||||
if(back)
|
||||
back.target = target
|
||||
back.AttackingTarget()
|
||||
if(!Adjacent(target))
|
||||
return
|
||||
do_attack_animation(target)
|
||||
//have fun
|
||||
//if(istype(target,/turf/closed/wall))
|
||||
//var/turf/closed/wall = target
|
||||
//wall.ScrapeAway()
|
||||
|
||||
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/C = target
|
||||
if(HAS_TRAIT(C, TRAIT_NODISMEMBER))
|
||||
return
|
||||
var/list/parts = list()
|
||||
for(var/X in C.bodyparts)
|
||||
var/obj/item/bodypart/bodypart = X
|
||||
if(bodypart.body_part != HEAD && bodypart.body_part != CHEST)
|
||||
if(bodypart.dismemberable)
|
||||
parts += bodypart
|
||||
if(length(parts) && prob(10))
|
||||
var/obj/item/bodypart/bodypart = pick(parts)
|
||||
bodypart.dismember()
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/prime
|
||||
name = "Lord of the Night"
|
||||
real_name = "Master of Decay"
|
||||
maxHealth = 400
|
||||
health = 400
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 25
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/prime/Initialize(mapload,spawn_more = TRUE,len = 9)
|
||||
. = ..()
|
||||
var/matrix/matrix_transformation = matrix()
|
||||
matrix_transformation.Scale(1.4,1.4)
|
||||
transform = matrix_transformation
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/armsy/primeproc/heal()
|
||||
if(health == maxHealth)
|
||||
if(back)
|
||||
back.heal()
|
||||
return
|
||||
else
|
||||
current_stacks++
|
||||
if(current_stacks >= stacks_to_grow)
|
||||
var/mob/living/simple_animal/hostile/eldritch/armsy/prev = new type(drop_location(),spawn_more = FALSE)
|
||||
icon_state = "armsy_mid"
|
||||
icon_living = "armsy_mid"
|
||||
back = prev
|
||||
prev.icon_state = "armsy_end"
|
||||
prev.icon_living = "armsy_end"
|
||||
prev.front = src
|
||||
prev.AIStatus = AI_OFF
|
||||
current_stacks = 0
|
||||
var/matrix/matrix_transformation = matrix()
|
||||
matrix_transformation.Scale(1.4,1.4)
|
||||
transform = matrix_transformation
|
||||
|
||||
adjustBruteLoss(-maxHealth * 0.5, FALSE)
|
||||
adjustFireLoss(-maxHealth * 0.5 ,FALSE)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/rust_spirit
|
||||
name = "Rust Walker"
|
||||
real_name = "Rusty"
|
||||
desc = "Incomprehensible abomination actively seeping life out of it's surrounding."
|
||||
icon_state = "rust_walker_s"
|
||||
status_flags = CANPUSH
|
||||
icon_living = "rust_walker_s"
|
||||
maxHealth = 75
|
||||
health = 75
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 20
|
||||
sight = SEE_TURFS
|
||||
spells_to_add = list(/obj/effect/proc_holder/spell/aoe_turf/rust_conversion/small,/obj/effect/proc_holder/spell/aimed/rust_wave/short)
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/rust_spirit/setDir(newdir, ismousemovement)
|
||||
. = ..()
|
||||
if(newdir == NORTH)
|
||||
icon_state = "rust_walker_n"
|
||||
else if(newdir == SOUTH)
|
||||
icon_state = "rust_walker_s"
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/rust_spirit/Moved()
|
||||
. = ..()
|
||||
playsound(src, 'sound/effects/footstep/rustystep1.ogg', 100, TRUE)
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/rust_spirit/Life()
|
||||
if(stat == DEAD)
|
||||
return ..()
|
||||
var/turf/T = get_turf(src)
|
||||
if(istype(T,/turf/open/floor/plating/rust))
|
||||
adjustBruteLoss(-3, FALSE)
|
||||
adjustFireLoss(-3, FALSE)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/ash_spirit
|
||||
name = "Ash Man"
|
||||
real_name = "Ashy"
|
||||
desc = "Incomprehensible abomination actively seeping life out of it's surrounding."
|
||||
icon_state = "ash_walker"
|
||||
status_flags = CANPUSH
|
||||
icon_living = "ash_walker"
|
||||
maxHealth = 75
|
||||
health = 75
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 20
|
||||
sight = SEE_TURFS
|
||||
spells_to_add = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash,/obj/effect/proc_holder/spell/pointed/cleave/long,/obj/effect/proc_holder/spell/aoe_turf/fire_cascade)
|
||||
|
||||
/mob/living/simple_animal/hostile/eldritch/stalker
|
||||
name = "Flesh Stalker"
|
||||
real_name = "Flesh Stalker"
|
||||
desc = "Abomination made from severed limbs."
|
||||
icon_state = "stalker"
|
||||
status_flags = CANPUSH
|
||||
icon_living = "stalker"
|
||||
maxHealth = 150
|
||||
health = 150
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 20
|
||||
sight = SEE_MOBS
|
||||
spells_to_add = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash,/obj/effect/proc_holder/spell/targeted/shapeshift/eldritch,/obj/effect/proc_holder/spell/targeted/emplosion/eldritch)
|
||||
@@ -0,0 +1,37 @@
|
||||
/mob/living/simple_animal/opossum
|
||||
name = "opossum"
|
||||
desc = "It's an opossum, a small scavenging marsupial."
|
||||
icon_state = "possum"
|
||||
icon_living = "possum"
|
||||
icon_dead = "possum_dead"
|
||||
speak = list("Hiss!","HISS!","Hissss?")
|
||||
speak_emote = list("hisses")
|
||||
emote_hear = list("hisses.")
|
||||
emote_see = list("runs in a circle.", "shakes.")
|
||||
speak_chance = 1
|
||||
turns_per_move = 3
|
||||
blood_volume = 250
|
||||
see_in_dark = 5
|
||||
maxHealth = 15
|
||||
health = 15
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 1)
|
||||
response_help_continuous = "pets"
|
||||
response_help_simple = "pet"
|
||||
response_disarm_continuous = "gently pushes aside"
|
||||
response_disarm_simple = "gently push aside"
|
||||
response_harm_continuous = "stamps on"
|
||||
response_harm_simple = "stamp"
|
||||
density = FALSE
|
||||
ventcrawler = VENTCRAWLER_ALWAYS
|
||||
pass_flags = PASSTABLE | PASSMOB
|
||||
mob_size = MOB_SIZE_TINY
|
||||
mob_biotypes = MOB_ORGANIC|MOB_BEAST
|
||||
gold_core_spawnable = FRIENDLY_SPAWN
|
||||
|
||||
/mob/living/simple_animal/opossum/poppy
|
||||
name = "Poppy the Safety Possum"
|
||||
desc = "Safety first!"
|
||||
icon_state = "poppypossum"
|
||||
icon_living = "poppypossum"
|
||||
icon_dead = "poppypossum_dead"
|
||||
butcher_results = list(/obj/item/clothing/head/hardhat = 1)
|
||||
@@ -146,6 +146,8 @@
|
||||
var/bare_wound_bonus = 0
|
||||
//If the attacks from this are sharp
|
||||
var/sharpness = SHARP_NONE
|
||||
//Generic flags
|
||||
var/simple_mob_flags = NONE
|
||||
|
||||
/mob/living/simple_animal/Initialize()
|
||||
. = ..()
|
||||
|
||||
@@ -649,8 +649,6 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
|
||||
continue
|
||||
if(overrides.len && (A in overrides))
|
||||
continue
|
||||
if(A.IsObscured())
|
||||
continue
|
||||
statpanel(listed_turf.name, null, A)
|
||||
if(mind)
|
||||
add_spells_to_statpanel(mind.spell_list)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set desc = "Open a list of available news channels"
|
||||
set category = "Ghost"
|
||||
|
||||
var/datum/browser/B = new(src, "ghost_news_list", "Chanenl List", 450, 600)
|
||||
var/datum/browser/B = new(src, "ghost_news_list", "Channel List", 450, 600)
|
||||
B.set_content(render_news_channel_list())
|
||||
B.open()
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
log_game("[user] [key_name(user)] has renamed [O] to [input]")
|
||||
|
||||
if(penchoice == "Change description")
|
||||
var/input = stripped_input(user,"Describe \the [O.name] here", ,"", 100)
|
||||
var/input = stripped_input(user,"Describe \the [O.name] here", ,"", 2048)
|
||||
if(QDELETED(O) || !user.canUseTopic(O, BE_CLOSE))
|
||||
return
|
||||
O.desc = input
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -147,8 +147,16 @@
|
||||
set category = "Debug"
|
||||
|
||||
var/datum/mapGenerator/nature/N = new()
|
||||
var/startInput = input(usr,"Start turf of Map, (X;Y;Z)", "Map Gen Settings", "1;1;1") as text
|
||||
var/endInput = input(usr,"End turf of Map (X;Y;Z)", "Map Gen Settings", "[world.maxx];[world.maxy];[mob ? mob.z : 1]") as text
|
||||
var/startInput = input(usr,"Start turf of Map, (X;Y;Z)", "Map Gen Settings", "1;1;1") as text|null
|
||||
|
||||
if (isnull(startInput))
|
||||
return
|
||||
|
||||
var/endInput = input(usr,"End turf of Map (X;Y;Z)", "Map Gen Settings", "[world.maxx];[world.maxy];[mob ? mob.z : 1]") as text|null
|
||||
|
||||
if (isnull(endInput))
|
||||
return
|
||||
|
||||
//maxx maxy and current z so that if you fuck up, you only fuck up one entire z level instead of the entire universe
|
||||
if(!startInput || !endInput)
|
||||
to_chat(src, "Missing Input")
|
||||
|
||||
@@ -36,6 +36,14 @@
|
||||
if(isgun(fired_from))
|
||||
var/obj/item/gun/G = fired_from
|
||||
BB.damage *= G.projectile_damage_multiplier
|
||||
if(HAS_TRAIT(user, TRAIT_INSANE_AIM))
|
||||
BB.ricochets_max = max(BB.ricochets_max, 10) //bouncy!
|
||||
BB.ricochet_chance = max(BB.ricochet_chance, 100) //it wont decay so we can leave it at 100 for always bouncing
|
||||
BB.ricochet_auto_aim_range = max(BB.ricochet_auto_aim_range, 3)
|
||||
BB.ricochet_auto_aim_angle = max(BB.ricochet_auto_aim_angle, 360) //it can turn full circle and shoot you in the face because our aim? is insane.
|
||||
BB.ricochet_decay_chance = 0
|
||||
BB.ricochet_decay_damage = max(BB.ricochet_decay_damage, 0.1)
|
||||
BB.ricochet_incidence_leeway = 0
|
||||
|
||||
if(reagents && BB.reagents)
|
||||
reagents.trans_to(BB, reagents.total_volume) //For chemical darts/bullets
|
||||
|
||||
@@ -14,9 +14,13 @@
|
||||
/obj/item/ammo_casing/a357/match
|
||||
name = ".357 match bullet casing"
|
||||
desc = "A .357 bullet casing, manufactured to exceedingly high standards."
|
||||
caliber = "357"
|
||||
projectile_type = /obj/item/projectile/bullet/a357/match
|
||||
|
||||
/obj/item/ammo_casing/a357/dumdum
|
||||
name = ".357 DumDum bullet casing"
|
||||
desc = "A .357 bullet casing. Usage of this ammunition will constitute a war crime in your area."
|
||||
projectile_type = /obj/item/projectile/bullet/a357/dumdum
|
||||
|
||||
// 7.62x38mmR (Nagant Revolver)
|
||||
|
||||
/obj/item/ammo_casing/n762
|
||||
@@ -68,4 +72,4 @@
|
||||
/obj/item/ammo_casing/c38/dumdum
|
||||
name = ".38 DumDum bullet casing"
|
||||
desc = "A .38 DumDum bullet casing."
|
||||
projectile_type = /obj/item/projectile/bullet/c38/dumdum
|
||||
projectile_type = /obj/item/projectile/bullet/c38/dumdum
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
name = "speed loader (.357 AP)"
|
||||
ammo_type = /obj/item/ammo_casing/a357/ap
|
||||
|
||||
/obj/item/ammo_box/a357/dumdum
|
||||
name = "speed loader (.357 DumDum)"
|
||||
desc = "Designed to quickly reload revolvers. Usage of these rounds will constitute a war crime in your area."
|
||||
ammo_type = /obj/item/ammo_casing/a357/dumdum
|
||||
|
||||
/obj/item/ammo_box/c38
|
||||
name = "speed loader (.38 rubber)"
|
||||
desc = "Designed to quickly reload revolvers."
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers
|
||||
var/sawn_desc = null //description change if weapon is sawn-off
|
||||
var/sawn_off = FALSE
|
||||
|
||||
|
||||
/// can we be put into a turret
|
||||
var/can_turret = TRUE
|
||||
/// can we be put in a circuit
|
||||
@@ -310,8 +310,6 @@
|
||||
randomized_gun_spread = rand(0, spread)
|
||||
else if(burst_size > 1 && burst_spread)
|
||||
randomized_gun_spread = rand(0, burst_spread)
|
||||
if(HAS_TRAIT(user, TRAIT_POOR_AIM)) //nice shootin' tex
|
||||
bonus_spread += 25
|
||||
var/randomized_bonus_spread = rand(0, bonus_spread)
|
||||
|
||||
if(burst_size > 1)
|
||||
@@ -603,10 +601,16 @@
|
||||
var/penalty = (last_fire + GUN_AIMING_TIME + fire_delay) - world.time
|
||||
if(penalty > 0) //Yet we only penalize users firing it multiple times in a haste. fire_delay isn't necessarily cumbersomeness.
|
||||
aiming_delay = penalty
|
||||
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) //To be removed in favor of something less tactless later.
|
||||
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE) || HAS_TRAIT(user, TRAIT_INSANE_AIM)) //To be removed in favor of something less tactless later.
|
||||
base_inaccuracy /= 1.5
|
||||
if(stamloss > STAMINA_NEAR_SOFTCRIT) //This can null out the above bonus.
|
||||
base_inaccuracy *= 1 + (stamloss - STAMINA_NEAR_SOFTCRIT)/(STAMINA_NEAR_CRIT - STAMINA_NEAR_SOFTCRIT)*0.5
|
||||
if(HAS_TRAIT(user, TRAIT_POOR_AIM)) //nice shootin' tex
|
||||
if(!HAS_TRAIT(user, TRAIT_INSANE_AIM))
|
||||
bonus_spread += 25
|
||||
else
|
||||
//you have both poor aim and insane aim, why?
|
||||
bonus_spread += rand(0,50)
|
||||
var/mult = max((GUN_AIMING_TIME + aiming_delay + user.last_click_move - world.time)/GUN_AIMING_TIME, -0.5) //Yes, there is a bonus for taking time aiming.
|
||||
if(mult < 0) //accurate weapons should provide a proper bonus with negative inaccuracy. the opposite is true too.
|
||||
mult *= 1/inaccuracy_modifier
|
||||
|
||||
@@ -327,16 +327,18 @@
|
||||
if(!trajectory)
|
||||
return
|
||||
var/turf/T = get_turf(A)
|
||||
if(check_ricochet(A) && A.handle_ricochet(src)) //if you can ricochet, attempt to ricochet off the object
|
||||
on_ricochet(A) //if allowed, use autoaim to ricochet into someone, otherwise default to ricocheting off the object from above
|
||||
var/datum/point/pcache = trajectory.copy_to()
|
||||
if(hitscan)
|
||||
store_hitscan_collision(pcache)
|
||||
decayedRange = max(0, decayedRange - reflect_range_decrease)
|
||||
ricochet_chance *= ricochet_decay_chance
|
||||
damage *= ricochet_decay_damage
|
||||
range = decayedRange
|
||||
return TRUE
|
||||
if(check_ricochet_flag(A) && check_ricochet(A)) //if you can ricochet, attempt to ricochet off the object
|
||||
ricochets++
|
||||
if(A.handle_ricochet(src))
|
||||
on_ricochet(A) //if allowed, use autoaim to ricochet into someone, otherwise default to ricocheting off the object from above
|
||||
var/datum/point/pcache = trajectory.copy_to()
|
||||
if(hitscan)
|
||||
store_hitscan_collision(pcache)
|
||||
decayedRange = max(0, decayedRange - reflect_range_decrease)
|
||||
ricochet_chance *= ricochet_decay_chance
|
||||
damage *= ricochet_decay_damage
|
||||
range = decayedRange
|
||||
return TRUE
|
||||
|
||||
var/distance = get_dist(T, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations.
|
||||
if(def_zone && check_zone(def_zone) != BODY_ZONE_CHEST)
|
||||
@@ -680,7 +682,8 @@
|
||||
if(!ignore_source_check && firer)
|
||||
var/mob/M = firer
|
||||
if((target == firer) || ((target == firer.loc) && ismecha(firer.loc)) || (target in firer.buckled_mobs) || (istype(M) && (M.buckled == target)))
|
||||
return FALSE
|
||||
if(!ricochets) //if it has ricocheted, it can hit the firer.
|
||||
return FALSE
|
||||
if(!ignore_loc && (loc != target.loc))
|
||||
return FALSE
|
||||
if(target in passthrough)
|
||||
|
||||
@@ -126,4 +126,15 @@
|
||||
ricochet_auto_aim_angle = 50
|
||||
ricochet_auto_aim_range = 6
|
||||
ricochet_incidence_leeway = 80
|
||||
ricochet_decay_chance = 1
|
||||
ricochet_decay_chance = 1
|
||||
|
||||
/obj/item/projectile/bullet/a357/dumdum
|
||||
name = ".357 DumDum bullet" // the warcrime bullet
|
||||
damage = 40
|
||||
armour_penetration = -20
|
||||
wound_bonus = 45
|
||||
bare_wound_bonus = 45
|
||||
sharpness = SHARP_EDGED
|
||||
embedding = list(embed_chance=90, fall_chance=2, jostle_chance=5, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
|
||||
wound_falloff_tile = -1
|
||||
embed_falloff_tile = -5
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "plasma blast"
|
||||
icon_state = "plasmacutter"
|
||||
damage_type = BRUTE
|
||||
damage = 20
|
||||
damage = 10
|
||||
range = 4
|
||||
dismemberment = 20
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser
|
||||
@@ -32,12 +32,12 @@
|
||||
return BULLET_ACT_FORCE_PIERCE
|
||||
|
||||
/obj/item/projectile/plasma/adv
|
||||
damage = 28
|
||||
damage = 14
|
||||
range = 5
|
||||
mine_range = 5
|
||||
|
||||
/obj/item/projectile/plasma/adv/mech
|
||||
damage = 40
|
||||
damage = 20
|
||||
range = 9
|
||||
mine_range = 3
|
||||
|
||||
@@ -52,4 +52,4 @@
|
||||
dismemberment = 0
|
||||
damage = 10
|
||||
range = 4
|
||||
mine_range = 0
|
||||
mine_range = 0
|
||||
|
||||
@@ -883,7 +883,7 @@
|
||||
if(istype(O, /obj/item/stack/sheet/metal))
|
||||
var/obj/item/stack/sheet/metal/M = O
|
||||
reac_volume = min(reac_volume, M.amount)
|
||||
new/obj/item/stack/tile/bronze(get_turf(M), reac_volume)
|
||||
new/obj/item/stack/sheet/bronze(get_turf(M), reac_volume)
|
||||
M.use(reac_volume)
|
||||
|
||||
/datum/reagent/nitrogen
|
||||
@@ -2331,6 +2331,32 @@
|
||||
M.adjustStaminaLoss(-0.25*REM) // the more wounds, the more stamina regen
|
||||
..()
|
||||
|
||||
datum/reagent/eldritch
|
||||
name = "Eldritch Essence"
|
||||
description = "Strange liquid that defies the laws of physics"
|
||||
taste_description = "Ag'hsj'saje'sh"
|
||||
color = "#1f8016"
|
||||
|
||||
/datum/reagent/eldritch/on_mob_life(mob/living/carbon/M)
|
||||
if(IS_HERETIC(M))
|
||||
M.drowsyness = max(M.drowsyness-5, 0)
|
||||
M.AdjustAllImmobility(-40, FALSE)
|
||||
M.adjustStaminaLoss(-15, FALSE)
|
||||
M.adjustToxLoss(-3, FALSE)
|
||||
M.adjustOxyLoss(-3, FALSE)
|
||||
M.adjustBruteLoss(-3, FALSE)
|
||||
M.adjustFireLoss(-3, FALSE)
|
||||
if(ishuman(M) && M.blood_volume < BLOOD_VOLUME_NORMAL)
|
||||
M.blood_volume += 3
|
||||
else
|
||||
M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3, 150)
|
||||
M.adjustToxLoss(2, FALSE)
|
||||
M.adjustFireLoss(2, FALSE)
|
||||
M.adjustOxyLoss(2, FALSE)
|
||||
M.adjustBruteLoss(2, FALSE)
|
||||
holder.remove_reagent(type, 1)
|
||||
return TRUE
|
||||
|
||||
/datum/reagent/cellulose
|
||||
name = "Cellulose Fibers"
|
||||
description = "A crystaline polydextrose polymer, plants swear by this stuff."
|
||||
|
||||
@@ -36,8 +36,9 @@
|
||||
/obj/machinery/computer/nanite_cloud_controller/proc/eject(mob/living/user)
|
||||
if(!disk)
|
||||
return
|
||||
if(!istype(user) || !Adjacent(user))// ||!user.put_in_active_hand(disk))
|
||||
disk.forceMove(drop_location())
|
||||
disk.forceMove(drop_location())
|
||||
if(istype(user) && user.Adjacent(src))
|
||||
user.put_in_active_hand(disk)
|
||||
disk = null
|
||||
|
||||
/obj/machinery/computer/nanite_cloud_controller/proc/get_backup(cloud_id)
|
||||
|
||||
@@ -53,8 +53,9 @@
|
||||
/obj/machinery/nanite_program_hub/proc/eject(mob/living/user)
|
||||
if(!disk)
|
||||
return
|
||||
if(!istype(user) || !Adjacent(user))// || !user.put_in_active_hand(disk))
|
||||
disk.forceMove(drop_location())
|
||||
disk.forceMove(drop_location())
|
||||
if(istype(user) && Adjacent(user))
|
||||
user.put_in_active_hand(disk)
|
||||
disk = null
|
||||
|
||||
/obj/machinery/nanite_program_hub/AltClick(mob/user)
|
||||
|
||||
@@ -39,8 +39,9 @@
|
||||
/obj/machinery/nanite_programmer/proc/eject(mob/living/user)
|
||||
if(!disk)
|
||||
return
|
||||
if(!istype(user) || !Adjacent(user))// || !user.put_in_active_hand(disk))
|
||||
disk.forceMove(drop_location())
|
||||
disk.forceMove(drop_location())
|
||||
if(istype(user) && user.Adjacent(src))
|
||||
user.put_in_active_hand(disk)
|
||||
disk = null
|
||||
program = null
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
/obj/item/stack/sheet/mineral/adamantine = /datum/species/golem/adamantine,
|
||||
/obj/item/stack/sheet/plastic = /datum/species/golem/plastic,
|
||||
/obj/item/stack/tile/brass = /datum/species/golem/clockwork,
|
||||
/obj/item/stack/tile/bronze = /datum/species/golem/bronze,
|
||||
/obj/item/stack/sheet/bronze = /datum/species/golem/bronze,
|
||||
/obj/item/stack/sheet/cardboard = /datum/species/golem/cardboard,
|
||||
/obj/item/stack/sheet/leather = /datum/species/golem/leather,
|
||||
/obj/item/stack/sheet/bone = /datum/species/golem/bone,
|
||||
|
||||
@@ -224,7 +224,15 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
|
||||
/obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf
|
||||
return
|
||||
|
||||
/obj/effect/proc_holder/spell/proc/can_target(mob/living/target)
|
||||
/**
|
||||
* can_target: Checks if we are allowed to cast the spell on a target.
|
||||
*
|
||||
* Arguments:
|
||||
* * target The atom that is being targeted by the spell.
|
||||
* * user The mob using the spell.
|
||||
* * silent If the checks should not give any feedback messages.
|
||||
*/
|
||||
/obj/effect/proc_holder/spell/proc/can_target(atom/target, mob/user, silent = FALSE)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/proc/start_recharge()
|
||||
@@ -296,6 +304,13 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
|
||||
/obj/effect/proc_holder/spell/proc/cast(list/targets,mob/user = usr)
|
||||
return
|
||||
|
||||
/obj/effect/proc_holder/spell/proc/view_or_range(distance = world.view, center=usr, type="view")
|
||||
switch(type)
|
||||
if("view")
|
||||
. = view(distance,center)
|
||||
if("range")
|
||||
. = range(distance,center)
|
||||
|
||||
/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge
|
||||
switch(charge_type)
|
||||
if("recharge")
|
||||
@@ -345,7 +360,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
|
||||
switch(max_targets)
|
||||
if(0) //unlimited
|
||||
for(var/mob/living/target in view_or_range(range, user, selection_type))
|
||||
if(!can_target(target))
|
||||
if(!can_target(target, user, TRUE))
|
||||
continue
|
||||
targets += target
|
||||
if(1) //single target can be picked
|
||||
@@ -357,7 +372,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
|
||||
for(var/mob/living/M in view_or_range(range, user, selection_type))
|
||||
if(!include_user && user == M)
|
||||
continue
|
||||
if(!can_target(M))
|
||||
if(!can_target(M, user, TRUE))
|
||||
continue
|
||||
possible_targets += M
|
||||
|
||||
@@ -365,7 +380,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
|
||||
//Adds a safety check post-input to make sure those targets are actually in range.
|
||||
var/mob/M
|
||||
if(!random_target)
|
||||
M = input("Choose the target for the spell.", "Targeting") as null|mob in possible_targets
|
||||
M = input("Choose the target for the spell.", "Targeting") as null|mob in sortNames(possible_targets)
|
||||
else
|
||||
switch(random_target_priority)
|
||||
if(TARGET_RANDOM)
|
||||
@@ -385,7 +400,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
|
||||
else
|
||||
var/list/possible_targets = list()
|
||||
for(var/mob/living/target in view_or_range(range, user, selection_type))
|
||||
if(!can_target(target))
|
||||
if(!can_target(target, user, TRUE))
|
||||
continue
|
||||
possible_targets += target
|
||||
for(var/i=1,i<=max_targets,i++)
|
||||
@@ -411,7 +426,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
|
||||
var/list/targets = list()
|
||||
|
||||
for(var/turf/target in view_or_range(range,user,selection_type))
|
||||
if(!can_target(target))
|
||||
if(!can_target(target, user, TRUE))
|
||||
continue
|
||||
if(!(target in view_or_range(inner_radius,user,selection_type)))
|
||||
targets += target
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/obj/effect/proc_holder/spell/cone
|
||||
name = "Cone of Nothing"
|
||||
desc = "Does nothing in a cone! Wow!"
|
||||
school = "evocation"
|
||||
charge_max = 100
|
||||
clothes_req = FALSE
|
||||
invocation = "FUKAN NOTHAN"
|
||||
invocation_type = "shout"
|
||||
sound = 'sound/magic/forcewall.ogg'
|
||||
action_icon_state = "shield"
|
||||
range = -1
|
||||
cooldown_min = 0.5 SECONDS
|
||||
///This controls how many levels the cone has, increase this value to make a bigger cone.
|
||||
var/cone_levels = 3
|
||||
///This value determines if the cone penetrates walls.
|
||||
var/respect_density = FALSE
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/choose_targets(mob/user = usr)
|
||||
perform(null, user=user)
|
||||
|
||||
///This proc creates a list of turfs that are hit by the cone
|
||||
/obj/effect/proc_holder/spell/cone/proc/cone_helper(var/turf/starter_turf, var/dir_to_use, var/cone_levels = 3)
|
||||
var/list/turfs_to_return = list()
|
||||
var/turf/turf_to_use = starter_turf
|
||||
var/turf/left_turf
|
||||
var/turf/right_turf
|
||||
var/right_dir
|
||||
var/left_dir
|
||||
switch(dir_to_use)
|
||||
if(NORTH)
|
||||
left_dir = WEST
|
||||
right_dir = EAST
|
||||
if(SOUTH)
|
||||
left_dir = EAST
|
||||
right_dir = WEST
|
||||
if(EAST)
|
||||
left_dir = NORTH
|
||||
right_dir = SOUTH
|
||||
if(WEST)
|
||||
left_dir = SOUTH
|
||||
right_dir = NORTH
|
||||
|
||||
|
||||
for(var/i in 1 to cone_levels)
|
||||
var/list/level_turfs = list()
|
||||
turf_to_use = get_step(turf_to_use, dir_to_use)
|
||||
level_turfs += turf_to_use
|
||||
if(i != 1)
|
||||
left_turf = get_step(turf_to_use, left_dir)
|
||||
level_turfs += left_turf
|
||||
right_turf = get_step(turf_to_use, right_dir)
|
||||
level_turfs += right_turf
|
||||
for(var/left_i in 1 to i -calculate_cone_shape(i))
|
||||
if(left_turf.density && respect_density)
|
||||
break
|
||||
left_turf = get_step(left_turf, left_dir)
|
||||
level_turfs += left_turf
|
||||
for(var/right_i in 1 to i -calculate_cone_shape(i))
|
||||
if(right_turf.density && respect_density)
|
||||
break
|
||||
right_turf = get_step(right_turf, right_dir)
|
||||
level_turfs += right_turf
|
||||
turfs_to_return += list(level_turfs)
|
||||
if(i == cone_levels)
|
||||
continue
|
||||
if(turf_to_use.density && respect_density)
|
||||
break
|
||||
return turfs_to_return
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/cast(list/targets,mob/user = usr)
|
||||
var/list/cone_turfs = cone_helper(get_turf(user), user.dir, cone_levels)
|
||||
for(var/list/turf_list in cone_turfs)
|
||||
do_cone_effects(turf_list)
|
||||
|
||||
///This proc does obj, mob and turf cone effects on all targets in a list
|
||||
/obj/effect/proc_holder/spell/cone/proc/do_cone_effects(list/target_turf_list, level)
|
||||
for(var/target_turf in target_turf_list)
|
||||
if(!target_turf) //if turf is no longer there
|
||||
continue
|
||||
do_turf_cone_effect(target_turf, level)
|
||||
if(isopenturf(target_turf))
|
||||
var/turf/open/open_turf = target_turf
|
||||
for(var/movable_content in open_turf)
|
||||
if(isobj(movable_content))
|
||||
do_obj_cone_effect(movable_content, level)
|
||||
else if(isliving(movable_content))
|
||||
do_mob_cone_effect(movable_content, level)
|
||||
|
||||
///This proc deterimines how the spell will affect turfs.
|
||||
/obj/effect/proc_holder/spell/cone/proc/do_turf_cone_effect(turf/target_turf, level)
|
||||
return
|
||||
|
||||
///This proc deterimines how the spell will affect objects.
|
||||
/obj/effect/proc_holder/spell/cone/proc/do_obj_cone_effect(obj/target_obj, level)
|
||||
return
|
||||
|
||||
///This proc deterimines how the spell will affect mobs.
|
||||
/obj/effect/proc_holder/spell/cone/proc/do_mob_cone_effect(mob/living/target_mob, level)
|
||||
return
|
||||
|
||||
///This proc adjusts the cones width depending on the level.
|
||||
/obj/effect/proc_holder/spell/cone/proc/calculate_cone_shape(current_level)
|
||||
var/end_taper_start = round(cone_levels * 0.8)
|
||||
if(current_level > end_taper_start)
|
||||
return (current_level % end_taper_start) * 2 //someone more talented and probably come up with a better formula.
|
||||
else
|
||||
return 2
|
||||
|
||||
///This type of cone gradually affects each level of the cone instead of affecting the entire area at once.
|
||||
/obj/effect/proc_holder/spell/cone/staggered
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/cast(list/targets,mob/user = usr)
|
||||
var/level_counter = 0
|
||||
var/list/cone_turfs = cone_helper(get_turf(user), user.dir, cone_levels)
|
||||
for(var/list/turf_list in cone_turfs)
|
||||
level_counter++
|
||||
addtimer(CALLBACK(src, .proc/do_cone_effects, turf_list, level_counter), 2 * level_counter)
|
||||
@@ -49,8 +49,8 @@
|
||||
var/projectile_type = text2path(proj_type)
|
||||
projectile = new projectile_type(user)
|
||||
else if(istype(proj_type, /obj/effect/proc_holder/spell))
|
||||
projectile = new /obj/effect/proc_holder/spell/targeted/trigger(user)
|
||||
var/obj/effect/proc_holder/spell/targeted/trigger/T = projectile
|
||||
projectile = new /obj/effect/proc_holder/spell/pointed/trigger(user)
|
||||
var/obj/effect/proc_holder/spell/pointed/trigger/T = projectile
|
||||
T.linked_spells += proj_type
|
||||
else
|
||||
projectile = new proj_type(user)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
action_icon_state = "jaunt"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast(list/targets,mob/user = usr) //magnets, so mostly hardcoded
|
||||
playsound(get_turf(user), 'sound/magic/ethereal_enter.ogg', 50, 1, -1)
|
||||
play_sound("enter",user)
|
||||
for(var/mob/living/target in targets)
|
||||
INVOKE_ASYNC(src, .proc/do_jaunt, target)
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
ADD_TRAIT(target, TRAIT_MOBILITY_NOMOVE, src)
|
||||
target.update_mobility()
|
||||
holder.reappearing = 1
|
||||
playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 50, 1, -1)
|
||||
play_sound("exit",target)
|
||||
sleep(25 - jaunt_in_time)
|
||||
new jaunt_in_type(mobloc, holder.dir)
|
||||
target.setDir(holder.dir)
|
||||
@@ -63,6 +63,13 @@
|
||||
steam.set_up(10, 0, mobloc)
|
||||
steam.start()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/play_sound(type,mob/living/target)
|
||||
switch(type)
|
||||
if("enter")
|
||||
playsound(get_turf(target), 'sound/magic/ethereal_enter.ogg', 50, TRUE, -1)
|
||||
if("exit")
|
||||
playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
|
||||
|
||||
/obj/effect/dummy/phased_mob/spell_jaunt
|
||||
name = "water"
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/obj/effect/proc_holder/spell/targeted/mind_transfer
|
||||
name = "Mind Transfer"
|
||||
desc = "This spell allows the user to switch bodies with a target."
|
||||
|
||||
school = "transmutation"
|
||||
charge_max = 600
|
||||
clothes_req = NONE
|
||||
invocation = "GIN'YU CAPAN"
|
||||
invocation_type = "whisper"
|
||||
range = 1
|
||||
cooldown_min = 200 //100 deciseconds reduction per rank
|
||||
var/unconscious_amount_caster = 400 //how much the caster is stunned for after the spell
|
||||
var/unconscious_amount_victim = 400 //how much the victim is stunned for after the spell
|
||||
|
||||
action_icon_state = "mindswap"
|
||||
|
||||
/*
|
||||
Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do.
|
||||
Make sure spells that are removed from spell_list are actually removed and deleted when mind transferring.
|
||||
Also, you never added distance checking after target is selected. I've went ahead and did that.
|
||||
*/
|
||||
/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/living/user = usr, distanceoverride, silent = FALSE)
|
||||
if(!targets.len)
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>No mind found!</span>")
|
||||
return
|
||||
|
||||
if(targets.len > 1)
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>Too many minds! You're not a hive damnit!</span>")
|
||||
return
|
||||
|
||||
var/mob/living/target = targets[1]
|
||||
|
||||
var/t_He = target.p_they(TRUE)
|
||||
var/t_is = target.p_are()
|
||||
|
||||
if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>[t_He] [t_is] too far away!</span>")
|
||||
return
|
||||
|
||||
if(ismegafauna(target))
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>This creature is too powerful to control!</span>")
|
||||
return
|
||||
|
||||
if(target.stat == DEAD)
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>You don't particularly want to be dead!</span>")
|
||||
return
|
||||
|
||||
if(!target.key || !target.mind)
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>[t_He] appear[target.p_s()] to be catatonic! Not even magic can affect [target.p_their()] vacant mind.</span>")
|
||||
return
|
||||
|
||||
if(user.suiciding)
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>You're killing yourself! You can't concentrate enough to do this!</span>")
|
||||
return
|
||||
|
||||
var/datum/mind/TM = target.mind
|
||||
if(target.anti_magic_check(TRUE, FALSE) || TM.has_antag_datum(/datum/antagonist/wizard) || TM.has_antag_datum(/datum/antagonist/cult) || TM.has_antag_datum(/datum/antagonist/clockcult) || TM.has_antag_datum(/datum/antagonist/changeling) || TM.has_antag_datum(/datum/antagonist/rev) || target.key[1] == "@")
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>[target.p_their(TRUE)] mind is resisting your spell!</span>")
|
||||
return
|
||||
|
||||
var/mob/living/victim = target//The target of the spell whos body will be transferred to.
|
||||
var/mob/living/caster = user//The wizard/whomever doing the body transferring.
|
||||
|
||||
//MIND TRANSFER BEGIN
|
||||
var/mob/dead/observer/ghost = victim.ghostize(FALSE, TRUE)
|
||||
caster.mind.transfer_to(victim)
|
||||
|
||||
ghost.mind.transfer_to(caster)
|
||||
if(ghost.key)
|
||||
ghost.transfer_ckey(caster) //have to transfer the key since the mind was not active
|
||||
qdel(ghost)
|
||||
|
||||
//MIND TRANSFER END
|
||||
|
||||
//Here we knock both mobs out for a time.
|
||||
caster.Unconscious(unconscious_amount_caster)
|
||||
victim.Unconscious(unconscious_amount_victim)
|
||||
SEND_SOUND(caster, sound('sound/magic/mandswap.ogg'))
|
||||
SEND_SOUND(victim, sound('sound/magic/mandswap.ogg'))// only the caster and victim hear the sounds, that way no one knows for sure if the swap happened
|
||||
return TRUE
|
||||
+27
-24
@@ -1,51 +1,54 @@
|
||||
/obj/effect/proc_holder/spell/targeted/barnyardcurse
|
||||
/obj/effect/proc_holder/spell/pointed/barnyardcurse
|
||||
name = "Curse of the Barnyard"
|
||||
desc = "This spell dooms an unlucky soul to possess the speech and facial attributes of a barnyard animal."
|
||||
school = "transmutation"
|
||||
charge_type = "recharge"
|
||||
charge_max = 150
|
||||
charge_counter = 0
|
||||
clothes_req = NONE
|
||||
stat_allowed = 0
|
||||
clothes_req = FALSE
|
||||
stat_allowed = FALSE
|
||||
invocation = "KN'A FTAGHU, PUCK 'BTHNK!"
|
||||
invocation_type = "shout"
|
||||
range = 7
|
||||
cooldown_min = 30
|
||||
selection_type = "range"
|
||||
var/list/compatible_mobs = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
|
||||
|
||||
ranged_mousepointer = 'icons/effects/mouse_pointers/barn_target.dmi'
|
||||
action_icon_state = "barn"
|
||||
active_msg = "You prepare to curse a target..."
|
||||
deactive_msg = "You dispel the curse..."
|
||||
/// List of mobs which are allowed to be a target of the spell
|
||||
var/static/list/compatible_mobs_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/carbon/monkey))
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/barnyardcurse/cast(list/targets, mob/user = usr)
|
||||
/obj/effect/proc_holder/spell/pointed/barnyardcurse/cast(list/targets, mob/user)
|
||||
if(!targets.len)
|
||||
to_chat(user, "<span class='notice'>No target found in range.</span>")
|
||||
return
|
||||
to_chat(user, "<span class='warning'>No target found in range!</span>")
|
||||
return FALSE
|
||||
if(!can_target(targets[1], user))
|
||||
return FALSE
|
||||
|
||||
var/mob/living/carbon/target = targets[1]
|
||||
|
||||
if(!(target.type in compatible_mobs))
|
||||
to_chat(user, "<span class='notice'>You are unable to curse [target]'s head!</span>")
|
||||
return
|
||||
|
||||
if(!(target in oview(range)))
|
||||
to_chat(user, "<span class='notice'>[target.p_theyre(TRUE)] too far away!</span>")
|
||||
return
|
||||
|
||||
if(target.anti_magic_check())
|
||||
to_chat(user, "<span class='warning'>The spell had no effect!</span>")
|
||||
target.visible_message("<span class='danger'>[target]'s face bursts into flames, which instantly burst outward, leaving [target] unharmed!</span>", \
|
||||
"<span class='danger'>Your face starts burning up, but the flames are repulsed by your anti-magic protection!</span>")
|
||||
return
|
||||
"<span class='danger'>Your face starts burning up, but the flames are repulsed by your anti-magic protection!</span>")
|
||||
return FALSE
|
||||
|
||||
var/list/masks = list(/obj/item/clothing/mask/pig/cursed, /obj/item/clothing/mask/cowmask/cursed, /obj/item/clothing/mask/horsehead/cursed)
|
||||
|
||||
var/choice = pick(masks)
|
||||
var/obj/item/clothing/mask/magichead = new choice(get_turf(target))
|
||||
magichead.flags_inv = null
|
||||
|
||||
target.visible_message("<span class='danger'>[target]'s face bursts into flames, and a barnyard animal's head takes its place!</span>", \
|
||||
"<span class='danger'>Your face burns up, and shortly after the fire you realise you have the face of a barnyard animal!</span>")
|
||||
if(!target.dropItemToGround(target.wear_mask))
|
||||
qdel(target.wear_mask)
|
||||
target.equip_to_slot_if_possible(magichead, SLOT_WEAR_MASK, 1, 1)
|
||||
|
||||
target.equip_to_slot_if_possible(magichead, ITEM_SLOT_MASK, 1, 1)
|
||||
target.flash_act()
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/barnyardcurse/can_target(atom/target, mob/user, silent)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(!is_type_in_typecache(target, compatible_mobs_typecache))
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>You are unable to curse [target]!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user