mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
Merge pull request #2680 from yogstation13/upstream-merge-40296
[MIRROR] [READY] [Gamemode Bounty] Assimilation
This commit is contained in:
@@ -57,6 +57,7 @@
|
|||||||
#define ANTAG_HUD_SOULLESS 21
|
#define ANTAG_HUD_SOULLESS 21
|
||||||
#define ANTAG_HUD_CLOCKWORK 22
|
#define ANTAG_HUD_CLOCKWORK 22
|
||||||
#define ANTAG_HUD_BROTHER 23
|
#define ANTAG_HUD_BROTHER 23
|
||||||
|
#define ANTAG_HUD_HIVE 24
|
||||||
|
|
||||||
// Notification action types
|
// Notification action types
|
||||||
#define NOTIFY_JUMP "jump"
|
#define NOTIFY_JUMP "jump"
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#define ROLE_VAMPIRE "vampire" // Yogs
|
#define ROLE_VAMPIRE "vampire" // Yogs
|
||||||
#define ROLE_BRAINWASHED "brainwashed victim"
|
#define ROLE_BRAINWASHED "brainwashed victim"
|
||||||
#define ROLE_OVERTHROW "syndicate mutineer"
|
#define ROLE_OVERTHROW "syndicate mutineer"
|
||||||
|
#define ROLE_HIVE "hivemind host"
|
||||||
#define ROLE_SENTIENCE "sentience potion spawn"
|
#define ROLE_SENTIENCE "sentience potion spawn"
|
||||||
#define ROLE_MIND_TRANSFER "mind transfer potion"
|
#define ROLE_MIND_TRANSFER "mind transfer potion"
|
||||||
#define ROLE_POSIBRAIN "posibrain"
|
#define ROLE_POSIBRAIN "posibrain"
|
||||||
@@ -61,6 +62,7 @@ GLOBAL_LIST_INIT(special_roles, list(
|
|||||||
ROLE_VAMPIRE = /datum/game_mode/vampire, // Yogs
|
ROLE_VAMPIRE = /datum/game_mode/vampire, // Yogs
|
||||||
ROLE_OVERTHROW = /datum/game_mode/overthrow,
|
ROLE_OVERTHROW = /datum/game_mode/overthrow,
|
||||||
ROLE_SHADOWLING = /datum/game_mode/shadowling, //yogs
|
ROLE_SHADOWLING = /datum/game_mode/shadowling, //yogs
|
||||||
|
ROLE_HIVE = /datum/game_mode/hivemind,
|
||||||
ROLE_INTERNAL_AFFAIRS = /datum/game_mode/traitor/internal_affairs,
|
ROLE_INTERNAL_AFFAIRS = /datum/game_mode/traitor/internal_affairs,
|
||||||
ROLE_SENTIENCE
|
ROLE_SENTIENCE
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ GLOBAL_LIST_INIT(huds, list(
|
|||||||
ANTAG_HUD_CLOCKWORK = new/datum/atom_hud/antag(),
|
ANTAG_HUD_CLOCKWORK = new/datum/atom_hud/antag(),
|
||||||
ANTAG_HUD_BROTHER = new/datum/atom_hud/antag/hidden(),
|
ANTAG_HUD_BROTHER = new/datum/atom_hud/antag/hidden(),
|
||||||
ANTAG_HUD_VAMPIRE = new/datum/atom_hud/antag/hidden(), // Yogs
|
ANTAG_HUD_VAMPIRE = new/datum/atom_hud/antag/hidden(), // Yogs
|
||||||
|
ANTAG_HUD_HIVE = new/datum/atom_hud/antag/hidden(),
|
||||||
))
|
))
|
||||||
|
|
||||||
/datum/atom_hud
|
/datum/atom_hud
|
||||||
|
|||||||
75
code/game/gamemodes/hivemind/hivemind.dm
Normal file
75
code/game/gamemodes/hivemind/hivemind.dm
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/datum/game_mode/hivemind
|
||||||
|
name = "Assimilation"
|
||||||
|
config_tag = "hivemind"
|
||||||
|
antag_flag = ROLE_HIVE
|
||||||
|
false_report_weight = 5
|
||||||
|
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain")
|
||||||
|
restricted_jobs = list("Cyborg","AI")
|
||||||
|
required_players = 25
|
||||||
|
required_enemies = 2
|
||||||
|
recommended_enemies = 3
|
||||||
|
reroll_friendly = 1
|
||||||
|
enemy_minimum_age = 0
|
||||||
|
|
||||||
|
announce_span = "danger"
|
||||||
|
announce_text = "The hosts of several psionic hiveminds have infiltrated the station and are looking to assimilate the crew!\n\
|
||||||
|
<span class='danger'>Hosts</span>: Expand your hivemind and complete your objectives at all costs!\n\
|
||||||
|
<span class='notice'>Crew</span>: Prevent the hosts from getting into your mind!"
|
||||||
|
|
||||||
|
var/list/hosts = list()
|
||||||
|
|
||||||
|
/proc/is_hivehost(mob/living/M)
|
||||||
|
return M && M.mind && M.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
|
||||||
|
/proc/is_hivemember(mob/living/M)
|
||||||
|
if(!M)
|
||||||
|
return FALSE
|
||||||
|
for(var/datum/antagonist/hivemind/H in GLOB.antagonists)
|
||||||
|
if(H.hivemembers.Find(M))
|
||||||
|
return TRUE
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
/proc/remove_hivemember(mob/living/M) //Removes somebody from all hives as opposed to the antag proc remove_from_hive()
|
||||||
|
if(!M)
|
||||||
|
return
|
||||||
|
for(var/datum/antagonist/hivemind/H in GLOB.antagonists)
|
||||||
|
if(H.hivemembers.Find(M))
|
||||||
|
H.hivemembers -= M
|
||||||
|
H.calc_size()
|
||||||
|
|
||||||
|
/datum/game_mode/hivemind/pre_setup()
|
||||||
|
|
||||||
|
if(CONFIG_GET(flag/protect_roles_from_antagonist))
|
||||||
|
restricted_jobs += protected_jobs
|
||||||
|
|
||||||
|
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
|
||||||
|
restricted_jobs += "Assistant"
|
||||||
|
|
||||||
|
var/num_hosts = max( 1 , rand(0,1) + min(5, round(num_players() / 15) ) ) //1 host for every 15 players up to 75, with a 50% chance of an extra
|
||||||
|
|
||||||
|
for(var/j = 0, j < num_hosts, j++)
|
||||||
|
if (!antag_candidates.len)
|
||||||
|
break
|
||||||
|
var/datum/mind/host = antag_pick(antag_candidates)
|
||||||
|
hosts += host
|
||||||
|
host.special_role = ROLE_HIVE
|
||||||
|
host.restricted_roles = restricted_jobs
|
||||||
|
log_game("[key_name(host)] has been selected as a hivemind host")
|
||||||
|
antag_candidates.Remove(host)
|
||||||
|
|
||||||
|
if(hosts.len < required_enemies)
|
||||||
|
setup_error = "Not enough host candidates"
|
||||||
|
return FALSE
|
||||||
|
else
|
||||||
|
return TRUE
|
||||||
|
|
||||||
|
|
||||||
|
/datum/game_mode/hivemind/post_setup()
|
||||||
|
for(var/datum/mind/i in hosts)
|
||||||
|
i.add_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/datum/game_mode/hivemind/generate_report()
|
||||||
|
return "Reports of psychic activity have been showing up in this sector, and we believe this may have to do with a containment breach on \[REDACTED\] last month \
|
||||||
|
when a sapient hive intelligence displaying paranormal powers escaped into the unknown. They present a very large risk as they can assimilate people into \
|
||||||
|
the hivemind with ease, although they appear unable to affect mindshielded personnel."
|
||||||
60
code/game/gamemodes/hivemind/objectives.dm
Normal file
60
code/game/gamemodes/hivemind/objectives.dm
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/datum/objective/hivemind
|
||||||
|
|
||||||
|
/datum/objective/hivemind/hivesize
|
||||||
|
explanation_text = "This is a bug. Error:HIVE2"
|
||||||
|
target_amount = 10
|
||||||
|
|
||||||
|
/datum/objective/hivemind/hivesize/New()
|
||||||
|
target_amount = ( max(8, round(GLOB.joined_player_list.len/3)) + rand(0,3) )
|
||||||
|
update_explanation_text()
|
||||||
|
|
||||||
|
/datum/objective/hivemind/hivesize/update_explanation_text()
|
||||||
|
explanation_text = "End the round with at least [target_amount] beings assimilated into the hive."
|
||||||
|
|
||||||
|
/datum/objective/hivemind/hivesize/check_completion()
|
||||||
|
var/datum/antagonist/hivemind/host = owner.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!host)
|
||||||
|
return FALSE
|
||||||
|
return host.hive_size >= target_amount
|
||||||
|
|
||||||
|
/datum/objective/hivemind/hiveescape
|
||||||
|
explanation_text = "This is a bug. Error:HIVE2"
|
||||||
|
target_amount = 10
|
||||||
|
|
||||||
|
/datum/objective/hivemind/hiveescape/New()
|
||||||
|
target_amount = ( max(5, round(GLOB.joined_player_list.len/6)) + rand(0,2) )
|
||||||
|
update_explanation_text()
|
||||||
|
|
||||||
|
/datum/objective/hivemind/hiveescape/update_explanation_text()
|
||||||
|
explanation_text = "Have at least [target_amount] members of the hive escape on the shuttle alive and free."
|
||||||
|
|
||||||
|
/datum/objective/hivemind/hiveescape/check_completion()
|
||||||
|
var/count = 0
|
||||||
|
var/datum/antagonist/hivemind/host = owner.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!host)
|
||||||
|
return FALSE
|
||||||
|
for(var/mob/living/L in host.hivemembers)
|
||||||
|
var/datum/mind/M = L.mind
|
||||||
|
if(M)
|
||||||
|
if(considered_escaped(M))
|
||||||
|
count++
|
||||||
|
return count >= target_amount
|
||||||
|
|
||||||
|
/datum/objective/hivemind/assimilate
|
||||||
|
explanation_text = "This is a bug. Error:HIVE3"
|
||||||
|
|
||||||
|
/datum/objective/hivemind/assimilate/update_explanation_text()
|
||||||
|
if(target)
|
||||||
|
explanation_text = "Assimilate [target.name] into the hive and ensure they survive."
|
||||||
|
else
|
||||||
|
explanation_text = "Free Objective."
|
||||||
|
|
||||||
|
/datum/objective/hivemind/assimilate/check_completion()
|
||||||
|
var/datum/antagonist/hivemind/host = owner.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!host)
|
||||||
|
return FALSE
|
||||||
|
for(var/mob/living/L in host.hivemembers)
|
||||||
|
var/datum/mind/M = L.mind
|
||||||
|
if(M == target)
|
||||||
|
return considered_alive(target)
|
||||||
|
return FALSE
|
||||||
@@ -26,6 +26,16 @@
|
|||||||
if(target.mind.has_antag_datum(/datum/antagonist/brainwashed))
|
if(target.mind.has_antag_datum(/datum/antagonist/brainwashed))
|
||||||
target.mind.remove_antag_datum(/datum/antagonist/brainwashed)
|
target.mind.remove_antag_datum(/datum/antagonist/brainwashed)
|
||||||
|
|
||||||
|
if(is_hivemember(target))
|
||||||
|
var/warning = ""
|
||||||
|
for(var/datum/antagonist/hivemind/hive in GLOB.antagonists)
|
||||||
|
if(hive.hivemembers.Find(target))
|
||||||
|
var/hive_name = hive.get_real_name()
|
||||||
|
if(hive_name)
|
||||||
|
warning += "[hive_name]. "
|
||||||
|
to_chat(target, "<span class='warning'>You hear supernatural wailing echo throughout your mind. If you listen closely you can hear... [warning]Are those... names?</span>")
|
||||||
|
remove_hivemember(target)
|
||||||
|
|
||||||
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.unconvertable)
|
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.unconvertable)
|
||||||
if(!silent)
|
if(!silent)
|
||||||
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel something interfering with your mental conditioning, but you resist it!</span>")
|
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel something interfering with your mental conditioning, but you resist it!</span>")
|
||||||
|
|||||||
187
code/modules/antagonists/hivemind/hivemind.dm
Normal file
187
code/modules/antagonists/hivemind/hivemind.dm
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
/datum/antagonist/hivemind
|
||||||
|
name = "Hivemind Host"
|
||||||
|
roundend_category = "hiveminds"
|
||||||
|
antagpanel_category = "Hivemind Host"
|
||||||
|
job_rank = ROLE_HIVE
|
||||||
|
antag_moodlet = /datum/mood_event/focused
|
||||||
|
var/special_role = ROLE_HIVE
|
||||||
|
var/list/hivemembers = list()
|
||||||
|
var/hive_size = 0
|
||||||
|
|
||||||
|
var/list/upgrade_tiers = list(
|
||||||
|
//Tier 1
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_add = 0,
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_remove = 0,
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_see = 0,
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_shock = 2,
|
||||||
|
/obj/effect/proc_holder/spell/self/hive_drain = 4,
|
||||||
|
//Tier 2
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_warp = 6,
|
||||||
|
/obj/effect/proc_holder/spell/targeted/hive_hack = 8,
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_control = 10,
|
||||||
|
/obj/effect/proc_holder/spell/targeted/induce_panic = 12,
|
||||||
|
//Tier 3
|
||||||
|
/obj/effect/proc_holder/spell/self/hive_loyal = 15,
|
||||||
|
/obj/effect/proc_holder/spell/targeted/hive_assim = 18,
|
||||||
|
/obj/effect/proc_holder/spell/targeted/forcewall/hive = 20,
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_attack = 25)
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/proc/calc_size()
|
||||||
|
listclearnulls(hivemembers)
|
||||||
|
var/old_size = hive_size
|
||||||
|
hive_size = hivemembers.len
|
||||||
|
if(hive_size != old_size)
|
||||||
|
check_powers()
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/proc/check_powers()
|
||||||
|
for(var/power in upgrade_tiers)
|
||||||
|
var/level = upgrade_tiers[power]
|
||||||
|
if(hive_size >= level && !(locate(power) in owner.spell_list))
|
||||||
|
owner.AddSpell(new power(null))
|
||||||
|
else if(hive_size < level && (locate(power) in owner.spell_list))
|
||||||
|
owner.RemoveSpell(power)
|
||||||
|
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/proc/get_real_name() //Gets the real name of the host, even if they're temporarily in another one
|
||||||
|
var/obj/effect/proc_holder/spell/target_hive/hive_control/the_spell = locate(/obj/effect/proc_holder/spell/target_hive/hive_control) in owner.spell_list
|
||||||
|
var/datum/mind/M = owner
|
||||||
|
if(M)
|
||||||
|
var/mob/living/L = owner.current
|
||||||
|
if(L)
|
||||||
|
if(the_spell && the_spell.active)
|
||||||
|
if(the_spell.original_body)
|
||||||
|
return the_spell.original_body.real_name
|
||||||
|
return L.real_name
|
||||||
|
return ""
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/proc/add_to_hive(var/mob/living/carbon/human/H)
|
||||||
|
var/warning = "<span class='userdanger'>We detect a surge of psionic energy from [H.real_name] before they disappear from the hive. An enemy host, or simply a stolen vessel?</span>"
|
||||||
|
var/user_warning = "<span class='userdanger'>We have detected an enemy hivemind using our physical form as a vessel and have begun ejecting their mind! They will be alerted of our disappearance once we succeed!</span>"
|
||||||
|
for(var/datum/antagonist/hivemind/enemy_hive in GLOB.antagonists)
|
||||||
|
if(H.mind == enemy_hive.owner)
|
||||||
|
var/eject_time = rand(1400,1600) //2.5 minutes +- 10 seconds
|
||||||
|
addtimer(CALLBACK(GLOBAL_PROC, /proc/to_chat, H, user_warning), rand(500,1300)) // If the host has assimilated an enemy hive host, alert the enemy before booting them from the hive after a short while
|
||||||
|
addtimer(CALLBACK(GLOBAL_PROC, /proc/to_chat, owner, warning), eject_time) //As well as the host who just added them as soon as they're ejected
|
||||||
|
addtimer(CALLBACK(GLOBAL_PROC, /proc/remove_hivemember, H), eject_time)
|
||||||
|
hivemembers |= H
|
||||||
|
calc_size()
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/proc/remove_from_hive(var/mob/living/carbon/human/H)
|
||||||
|
hivemembers -= H
|
||||||
|
calc_size()
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/proc/destroy_hive()
|
||||||
|
hivemembers = list()
|
||||||
|
calc_size()
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/antag_panel_data()
|
||||||
|
return "Vessels Assimilated: [hive_size]"
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/on_gain()
|
||||||
|
|
||||||
|
owner.special_role = special_role
|
||||||
|
check_powers()
|
||||||
|
forge_objectives()
|
||||||
|
..()
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/apply_innate_effects()
|
||||||
|
if(owner.assigned_role == "Clown")
|
||||||
|
var/mob/living/carbon/human/traitor_mob = owner.current
|
||||||
|
if(traitor_mob && istype(traitor_mob))
|
||||||
|
if(!silent)
|
||||||
|
to_chat(traitor_mob, "The great psionic powers of the Hive lets you overcome your clownish nature, allowing you to wield weapons with impunity.")
|
||||||
|
traitor_mob.dna.remove_mutation(CLOWNMUT)
|
||||||
|
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_HIVE]
|
||||||
|
hud.join_hud(owner.current)
|
||||||
|
set_antag_hud(owner.current, "hivemind")
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/remove_innate_effects()
|
||||||
|
if(owner.assigned_role == "Clown")
|
||||||
|
var/mob/living/carbon/human/traitor_mob = owner.current
|
||||||
|
if(traitor_mob && istype(traitor_mob))
|
||||||
|
traitor_mob.dna.add_mutation(CLOWNMUT)
|
||||||
|
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_HIVE]
|
||||||
|
hud.leave_hud(owner.current)
|
||||||
|
set_antag_hud(owner.current, null)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/on_removal()
|
||||||
|
|
||||||
|
//Remove all hive powers here
|
||||||
|
hive_size = -1
|
||||||
|
check_powers()
|
||||||
|
|
||||||
|
if(!silent && owner.current)
|
||||||
|
to_chat(owner.current,"<span class='userdanger'> Your psionic powers fade, you are no longer the hivemind's host! </span>")
|
||||||
|
owner.special_role = null
|
||||||
|
..()
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/proc/forge_objectives()
|
||||||
|
if(prob(65))
|
||||||
|
var/datum/objective/hivemind/hivesize/size_objective = new
|
||||||
|
size_objective.owner = owner
|
||||||
|
objectives += size_objective
|
||||||
|
else
|
||||||
|
var/datum/objective/hivemind/hiveescape/hive_escape_objective = new
|
||||||
|
hive_escape_objective.owner = owner
|
||||||
|
objectives += hive_escape_objective
|
||||||
|
if(prob(50))
|
||||||
|
var/datum/objective/hivemind/assimilate/assim_objective = new
|
||||||
|
assim_objective.owner = owner
|
||||||
|
if(prob(25)) //Decently high chance to have to assimilate an implanted crew member
|
||||||
|
assim_objective.find_target_by_role(pick("Captain","Head of Security","Security Officer","Detective","Warden"))
|
||||||
|
if(!assim_objective.target) //If the prob doesn't happen or there are no implanted crew, find any target
|
||||||
|
assim_objective.find_target()
|
||||||
|
assim_objective.update_explanation_text()
|
||||||
|
objectives += assim_objective
|
||||||
|
else if(prob(70))
|
||||||
|
var/datum/objective/assassinate/kill_objective = new
|
||||||
|
kill_objective.owner = owner
|
||||||
|
kill_objective.find_target()
|
||||||
|
objectives += kill_objective
|
||||||
|
else
|
||||||
|
var/datum/objective/maroon/maroon_objective = new
|
||||||
|
maroon_objective.owner = owner
|
||||||
|
maroon_objective.find_target()
|
||||||
|
objectives += maroon_objective
|
||||||
|
|
||||||
|
var/datum/objective/escape/escape_objective = new
|
||||||
|
escape_objective.owner = owner
|
||||||
|
objectives += escape_objective
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/greet()
|
||||||
|
to_chat(owner.current, "<B><font size=3 color=red>You are the host of a powerful Hivemind.</font></B>")
|
||||||
|
to_chat(owner.current, "<b>Your psionic powers will grow by assimilating the crew into your hive. Use the Assimilate Vessel spell on a stationary \
|
||||||
|
target, and after ten seconds he will be one of the hive. This is completely silent and safe to use, and failing will reset the cooldown. As \
|
||||||
|
you assimilate the crew, you will gain more powers to use. Most are silent and won't help you in a fight, but grant you great power over your \
|
||||||
|
vessels. There are other hiveminds onboard the station, collaboration is possible, but a strong enough hivemind can reap many rewards from a \
|
||||||
|
well planned betrayal.</b>")
|
||||||
|
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE)
|
||||||
|
|
||||||
|
owner.announce_objectives()
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/roundend_report()
|
||||||
|
var/list/result = list()
|
||||||
|
|
||||||
|
result += printplayer(owner)
|
||||||
|
result += "<b>Hive Size:</b> [hive_size]"
|
||||||
|
var/greentext = TRUE
|
||||||
|
if(objectives)
|
||||||
|
result += printobjectives(objectives)
|
||||||
|
for(var/datum/objective/objective in objectives)
|
||||||
|
if(!objective.check_completion())
|
||||||
|
greentext = FALSE
|
||||||
|
break
|
||||||
|
|
||||||
|
if(objectives.len == 0 || greentext)
|
||||||
|
result += "<span class='greentext big'>The [name] was successful!</span>"
|
||||||
|
else
|
||||||
|
result += "<span class='redtext big'>The [name] has failed!</span>"
|
||||||
|
|
||||||
|
return result.Join("<br>")
|
||||||
|
|
||||||
|
/datum/antagonist/hivemind/is_gamemode_hero()
|
||||||
|
return SSticker.mode.name == "hivemind"
|
||||||
@@ -40,6 +40,11 @@
|
|||||||
SSblackbox.ReportDeath(src)
|
SSblackbox.ReportDeath(src)
|
||||||
if(is_devil(src))
|
if(is_devil(src))
|
||||||
INVOKE_ASYNC(is_devil(src), /datum/antagonist/devil.proc/beginResurrectionCheck, src)
|
INVOKE_ASYNC(is_devil(src), /datum/antagonist/devil.proc/beginResurrectionCheck, src)
|
||||||
|
if(is_hivemember(src))
|
||||||
|
remove_hivemember(src)
|
||||||
|
if(is_hivehost(src))
|
||||||
|
var/datum/antagonist/hivemind/hive = mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
hive.destroy_hive()
|
||||||
|
|
||||||
/mob/living/carbon/human/proc/makeSkeleton()
|
/mob/living/carbon/human/proc/makeSkeleton()
|
||||||
add_trait(TRAIT_DISFIGURED, TRAIT_GENERIC)
|
add_trait(TRAIT_DISFIGURED, TRAIT_GENERIC)
|
||||||
|
|||||||
@@ -73,7 +73,9 @@
|
|||||||
if(changeling)
|
if(changeling)
|
||||||
stat("Chemical Storage", "[changeling.chem_charges]/[changeling.chem_storage]")
|
stat("Chemical Storage", "[changeling.chem_charges]/[changeling.chem_storage]")
|
||||||
stat("Absorbed DNA", changeling.absorbedcount)
|
stat("Absorbed DNA", changeling.absorbedcount)
|
||||||
|
var/datum/antagonist/hivemind/hivemind = mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(hivemind)
|
||||||
|
stat("Hivemind Vessels", hivemind.hive_size)
|
||||||
|
|
||||||
//NINJACODE
|
//NINJACODE
|
||||||
if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)) //Only display if actually a ninja.
|
if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)) //Only display if actually a ninja.
|
||||||
|
|||||||
@@ -120,7 +120,7 @@
|
|||||||
|
|
||||||
/datum/nanite_program/mindshield/enable_passive_effect()
|
/datum/nanite_program/mindshield/enable_passive_effect()
|
||||||
. = ..()
|
. = ..()
|
||||||
if(!host_mob.mind.has_antag_datum(/datum/antagonist/rev)) //won't work if on a rev, to avoid having implanted revs
|
if(!host_mob.mind.has_antag_datum(/datum/antagonist/rev) && !is_hivemember(host_mob)) //won't work if on a rev, to avoid having implanted revs. same applies for hivemind members.
|
||||||
host_mob.add_trait(TRAIT_MINDSHIELD, "nanites")
|
host_mob.add_trait(TRAIT_MINDSHIELD, "nanites")
|
||||||
host_mob.sec_hud_set_implants()
|
host_mob.sec_hud_set_implants()
|
||||||
|
|
||||||
|
|||||||
636
code/modules/spells/spell_types/hivemind.dm
Normal file
636
code/modules/spells/spell_types/hivemind.dm
Normal file
@@ -0,0 +1,636 @@
|
|||||||
|
/obj/effect/proc_holder/spell/target_hive
|
||||||
|
panel = "Hivemind Abilities"
|
||||||
|
invocation_type = "none"
|
||||||
|
selection_type = "range"
|
||||||
|
action_icon = 'icons/mob/actions/actions_hive.dmi'
|
||||||
|
action_background_icon_state = "bg_hive"
|
||||||
|
action_icon_state = "spell_default"
|
||||||
|
clothes_req = 0
|
||||||
|
human_req = 1
|
||||||
|
range = 0 //SNOWFLAKE, 0 is unlimited for target_external=0 spells
|
||||||
|
var/target_external = 0 //Whether or not we select targets inside or outside of the hive
|
||||||
|
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/choose_targets(mob/user = usr)
|
||||||
|
var/datum/antagonist/hivemind/hive = user.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!hive)
|
||||||
|
to_chat(user, "<span class='notice'>This is a bug. Error:HIVE1</span>")
|
||||||
|
return
|
||||||
|
var/list/possible_targets = list()
|
||||||
|
var/list/targets = list()
|
||||||
|
|
||||||
|
if(target_external)
|
||||||
|
for(var/mob/living/carbon/human/H in view_or_range(range, user, selection_type))
|
||||||
|
if(user == H)
|
||||||
|
continue
|
||||||
|
if(!can_target(H))
|
||||||
|
continue
|
||||||
|
if(!hive.hivemembers.Find(H))
|
||||||
|
possible_targets += H
|
||||||
|
else
|
||||||
|
possible_targets = hive.hivemembers.Copy()
|
||||||
|
if(range)
|
||||||
|
possible_targets &= view_or_range(range, user, selection_type)
|
||||||
|
|
||||||
|
var/mob/living/carbon/human/H = input("Choose the target for the spell.", "Targeting") as null|mob in possible_targets
|
||||||
|
if(!H)
|
||||||
|
revert_cast()
|
||||||
|
return
|
||||||
|
targets += H
|
||||||
|
perform(targets,user=user)
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_add
|
||||||
|
name = "Assimilate Vessel"
|
||||||
|
desc = "We silently add an unsuspecting target to the hive."
|
||||||
|
selection_type = "view"
|
||||||
|
action_icon_state = "add"
|
||||||
|
|
||||||
|
charge_max = 150
|
||||||
|
range = 4
|
||||||
|
target_external = 1
|
||||||
|
var/ignore_mindshield = FALSE
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_add/cast(list/targets, mob/living/user = usr)
|
||||||
|
var/mob/living/carbon/human/target = targets[1]
|
||||||
|
var/datum/antagonist/hivemind/hive = user.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
var/success = FALSE
|
||||||
|
|
||||||
|
if(target.mind && target.client && target.stat != DEAD)
|
||||||
|
if(!target.has_trait(TRAIT_MINDSHIELD) || ignore_mindshield)
|
||||||
|
if(target.has_trait(TRAIT_MINDSHIELD) && ignore_mindshield)
|
||||||
|
to_chat(user, "<span class='notice'>We bruteforce our way past the mental barriers of [target.name] and begin linking our minds!</span>")
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>We begin linking our mind with [target.name]!</span>")
|
||||||
|
if(do_mob(user,user,50))
|
||||||
|
if((target in view(range)))
|
||||||
|
to_chat(user, "<span class='notice'>[target.name] was added to the Hive!</span>")
|
||||||
|
success = TRUE
|
||||||
|
hive.add_to_hive(target)
|
||||||
|
if(ignore_mindshield)
|
||||||
|
SEND_SIGNAL(target, COMSIG_NANITE_SET_VOLUME, 0)
|
||||||
|
for(var/obj/item/implant/mindshield/M in target.implants)
|
||||||
|
qdel(M)
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>[target.name] is too far away to assimilate!</span>")
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>We fail to connect to [target.name].</span>")
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='warning'>Powerful technology protects [target.name]'s mind.</span>")
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>We detect no neural activity in this body.</span>")
|
||||||
|
if(!success)
|
||||||
|
revert_cast()
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_remove
|
||||||
|
name = "Release Vessel"
|
||||||
|
desc = "We silently remove a nearby target from the hive. We must be close to their body to do so."
|
||||||
|
selection_type = "view"
|
||||||
|
action_icon_state = "remove"
|
||||||
|
|
||||||
|
charge_max = 100
|
||||||
|
range = 4
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_remove/cast(list/targets, mob/living/user = usr)
|
||||||
|
var/mob/living/carbon/human/target = targets[1]
|
||||||
|
|
||||||
|
var/datum/antagonist/hivemind/hive = user.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!hive)
|
||||||
|
to_chat(user, "<span class='notice'>This is a bug. Error:HIVE1</span>")
|
||||||
|
return
|
||||||
|
hive.hivemembers -= target
|
||||||
|
hive.calc_size()
|
||||||
|
to_chat(user, "<span class='notice'>We remove [target.name] from the hive</span>")
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_see
|
||||||
|
name = "Hive Vision"
|
||||||
|
desc = "We use the eyes of one of our vessels. Use again to look through our own eyes once more."
|
||||||
|
action_icon_state = "see"
|
||||||
|
var/mob/vessel
|
||||||
|
var/mob/living/host //Didn't really have any other way to auto-reset the perspective if the other mob got qdeled
|
||||||
|
|
||||||
|
charge_max = 50
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_see/on_lose(mob/living/user)
|
||||||
|
user.reset_perspective()
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_see/cast(list/targets, mob/living/user = usr)
|
||||||
|
if(!active)
|
||||||
|
vessel = targets[1]
|
||||||
|
if(vessel)
|
||||||
|
user.reset_perspective(vessel)
|
||||||
|
active = TRUE
|
||||||
|
host = user
|
||||||
|
revert_cast()
|
||||||
|
else
|
||||||
|
user.reset_perspective()
|
||||||
|
active = FALSE
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_see/process()
|
||||||
|
if(active && (!vessel || !is_hivemember(vessel) || QDELETED(vessel)))
|
||||||
|
to_chat(host, "<span class='warning'>Our vessel is one of us no more!</span>")
|
||||||
|
host.reset_perspective()
|
||||||
|
active = FALSE
|
||||||
|
..()
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_see/choose_targets(mob/user = usr)
|
||||||
|
if(!active)
|
||||||
|
..()
|
||||||
|
else
|
||||||
|
perform(,user)
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_shock
|
||||||
|
name = "Neural Shock"
|
||||||
|
desc = "After a short charging time, we overload the mind of one of our vessels with psionic energy, rendering them unconscious for a short period of time. This power weakens over distance, but strengthens with hive size."
|
||||||
|
action_icon_state = "shock"
|
||||||
|
|
||||||
|
charge_max = 600
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_shock/cast(list/targets, mob/living/user = usr)
|
||||||
|
var/mob/living/carbon/human/target = targets[1]
|
||||||
|
var/datum/antagonist/hivemind/hive = user.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!hive)
|
||||||
|
to_chat(user, "<span class='notice'>This is a bug. Error:HIVE1</span>")
|
||||||
|
return
|
||||||
|
to_chat(user, "<span class='notice'>We begin increasing the psionic bandwidth between ourself and the vessel!</span>")
|
||||||
|
if(do_mob(user,user,60))
|
||||||
|
var/power = 120-get_dist(user, target)
|
||||||
|
if(!is_hivehost(target))
|
||||||
|
switch(hive.hive_size)
|
||||||
|
if(0 to 4)
|
||||||
|
if(5 to 9)
|
||||||
|
power *= 1.5
|
||||||
|
if(10 to 14)
|
||||||
|
power *= 2
|
||||||
|
if(15 to 19)
|
||||||
|
power *= 2.5
|
||||||
|
else
|
||||||
|
power *= 3
|
||||||
|
if(power > 50 && user.z == target.z)
|
||||||
|
to_chat(target, "<span class='userdanger'>You feel a sharp pain, and a foreign presence in your mind!!</span>")
|
||||||
|
to_chat(user, "<span class='notice'>We have overloaded the vessel for a short time!</span>")
|
||||||
|
target.Jitter(round(power/10))
|
||||||
|
target.Unconscious(power)
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>The vessel was too far away to be affected!</span>")
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>Our concentration has been broken!</span>")
|
||||||
|
revert_cast()
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/self/hive_drain
|
||||||
|
name = "Repair Protocol"
|
||||||
|
desc = "Our many vessels sacrifice a small portion of their mind's vitality to cure us of our physical and mental ailments."
|
||||||
|
|
||||||
|
panel = "Hivemind Abilities"
|
||||||
|
charge_max = 600
|
||||||
|
clothes_req = 0
|
||||||
|
invocation_type = "none"
|
||||||
|
action_icon = 'icons/mob/actions/actions_hive.dmi'
|
||||||
|
action_background_icon_state = "bg_hive"
|
||||||
|
action_icon_state = "drain"
|
||||||
|
human_req = 1
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/self/hive_drain/cast(mob/living/carbon/human/user)
|
||||||
|
var/datum/antagonist/hivemind/hive = user.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!hive)
|
||||||
|
return
|
||||||
|
var/iterations = 0
|
||||||
|
var/power = 5
|
||||||
|
|
||||||
|
if(!user.getBruteLoss() && !user.getFireLoss() && !user.getCloneLoss() && !user.getBrainLoss())
|
||||||
|
to_chat(user, "<span class='notice'>We cannot heal ourselves any more with this power!</span>")
|
||||||
|
revert_cast()
|
||||||
|
to_chat(user, "<span class='notice'>We begin siphoning power from our many vessels!</span>")
|
||||||
|
while(iterations < 7)
|
||||||
|
var/mob/living/carbon/human/target = pick(hive.hivemembers)
|
||||||
|
if(!target)
|
||||||
|
break
|
||||||
|
if(!do_mob(user,user,15))
|
||||||
|
to_chat(user, "<span class='warning'>Our concentration has been broken!</span>")
|
||||||
|
break
|
||||||
|
target.adjustBrainLoss(5)
|
||||||
|
power = max(5-(round(get_dist(user, target)/40)),2)
|
||||||
|
if(user.getBruteLoss() > user.getFireLoss())
|
||||||
|
user.heal_ordered_damage(power, list(CLONE, BRUTE, BURN))
|
||||||
|
else
|
||||||
|
user.heal_ordered_damage(power, list(CLONE, BURN, BRUTE))
|
||||||
|
if(!user.getBruteLoss() && !user.getFireLoss() && !user.getCloneLoss()) //If we don't have any of these, stop looping
|
||||||
|
to_chat(user, "<span class='warning'>We finish our healing</span>")
|
||||||
|
break
|
||||||
|
iterations++
|
||||||
|
user.setBrainLoss(0)
|
||||||
|
|
||||||
|
|
||||||
|
/mob/living/passenger
|
||||||
|
name = "mind control victim"
|
||||||
|
real_name = "unknown conscience"
|
||||||
|
|
||||||
|
/mob/living/passenger/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
|
||||||
|
to_chat(src, "<span class='warning'>You find yourself unable to speak, you aren't in control of your body!</span>")
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
/mob/living/passenger/emote(act, m_type = null, message = null, intentional = FALSE)
|
||||||
|
to_chat(src, "<span class='warning'>You find yourself unable to emote, you aren't in control of your body!</span>")
|
||||||
|
return
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_control
|
||||||
|
name = "Mind Control"
|
||||||
|
desc = "We assume direct control of one of our vessels, leaving our current body for up to ten seconds, although a larger hive may be able to sustain it for up to two minutes. Powers can be used via our vessel, although if it dies, the entire hivemind will come down with it."
|
||||||
|
charge_max = 600
|
||||||
|
action_icon_state = "force"
|
||||||
|
active = FALSE
|
||||||
|
var/mob/living/carbon/human/original_body //The original hivemind host
|
||||||
|
var/mob/living/carbon/human/vessel
|
||||||
|
var/mob/living/passenger/backseat //Storage for the mind controlled vessel
|
||||||
|
var/power = 100
|
||||||
|
var/time_initialized = 0
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_control/proc/release_control() //If the spell is active, force everybody into their original bodies if they exist, ghost them otherwise, delete the backseat
|
||||||
|
if(!active)
|
||||||
|
return
|
||||||
|
active = FALSE
|
||||||
|
charge_counter = max((0.5-(world.time-time_initialized)/power)*charge_max, 0) //Partially refund the power based on how long it was used, up to a max of half the charge time
|
||||||
|
|
||||||
|
if(!QDELETED(vessel))
|
||||||
|
if(vessel.mind)
|
||||||
|
if(QDELETED(original_body))
|
||||||
|
vessel.ghostize(0)
|
||||||
|
else
|
||||||
|
vessel.mind.transfer_to(original_body, 1)
|
||||||
|
|
||||||
|
if(!QDELETED(backseat) && backseat.mind)
|
||||||
|
if(QDELETED(vessel))
|
||||||
|
backseat.ghostize(0)
|
||||||
|
else
|
||||||
|
backseat.mind.transfer_to(vessel,1)
|
||||||
|
|
||||||
|
message_admins("[ADMIN_LOOKUPFLW(vessel)] is no longer being controlled by [ADMIN_LOOKUPFLW(original_body)] (Hivemind Host).")
|
||||||
|
log_game("[key_name(vessel)] was released from Mind Control by [key_name(original_body)].")
|
||||||
|
|
||||||
|
QDEL_NULL(backseat)
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_control/on_lose(mob/user)
|
||||||
|
release_control()
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_control/cast(list/targets, mob/living/user = usr)
|
||||||
|
if(!active)
|
||||||
|
vessel = targets[1]
|
||||||
|
var/datum/antagonist/hivemind/hive = user.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!hive)
|
||||||
|
to_chat(user, "<span class='notice'>This is a bug. Error:HIVE1</span>")
|
||||||
|
return
|
||||||
|
switch(hive.hive_size)
|
||||||
|
if(10 to 14)
|
||||||
|
power = 100
|
||||||
|
charge_max = 600
|
||||||
|
if(15 to 19)
|
||||||
|
power = 300
|
||||||
|
charge_max = 900
|
||||||
|
if(20 to 24)
|
||||||
|
power = 600
|
||||||
|
charge_max = 1200
|
||||||
|
else
|
||||||
|
power = 1200
|
||||||
|
charge_max = 1200
|
||||||
|
for(var/datum/antagonist/hivemind/H in GLOB.antagonists)
|
||||||
|
if(H.owner == user.mind)
|
||||||
|
continue
|
||||||
|
if(H.hivemembers.Find(vessel))
|
||||||
|
to_chat(user, "<span class='danger'>We have detected a foreign presence within this mind, it would be unwise to merge so intimately with it.</span>")
|
||||||
|
revert_cast()
|
||||||
|
return
|
||||||
|
original_body = user
|
||||||
|
vessel = targets[1]
|
||||||
|
to_chat(user, "<span class='notice'>We begin merging our mind with [vessel.name].</span>")
|
||||||
|
if(!do_mob(user,user,50))
|
||||||
|
to_chat(user, "<span class='notice'>We fail to assume control of the target.</span>")
|
||||||
|
revert_cast()
|
||||||
|
return
|
||||||
|
if(user.z != vessel.z)
|
||||||
|
to_chat(user, "<span class='notice'>Our vessel is too far away to control.</span>")
|
||||||
|
revert_cast()
|
||||||
|
return
|
||||||
|
backseat = new /mob/living/passenger()
|
||||||
|
if(vessel && vessel.mind && backseat)
|
||||||
|
var/obj/effect/proc_holder/spell/target_hive/hive_see/the_spell = locate(/obj/effect/proc_holder/spell/target_hive/hive_see) in user.mind.spell_list
|
||||||
|
if(the_spell && the_spell.active) //Uncast Hive Sight just to make things easier when casting during mind control
|
||||||
|
the_spell.perform(,user)
|
||||||
|
|
||||||
|
message_admins("[ADMIN_LOOKUPFLW(vessel)] has been temporarily taken over by [ADMIN_LOOKUPFLW(user)] (Hivemind Host).")
|
||||||
|
log_game("[key_name(vessel)] was Mind Controlled by [key_name(user)].")
|
||||||
|
|
||||||
|
original_body = user
|
||||||
|
backseat.loc = vessel
|
||||||
|
backseat.name = vessel.real_name
|
||||||
|
backseat.real_name = vessel.real_name
|
||||||
|
vessel.mind.transfer_to(backseat, 1)
|
||||||
|
user.mind.transfer_to(vessel, 1)
|
||||||
|
active = TRUE
|
||||||
|
time_initialized = world.time
|
||||||
|
revert_cast()
|
||||||
|
if(do_mob(user,user,power))
|
||||||
|
to_chat(vessel, "<span class='warning'>We cannot sustain the mind control any longer and release control!</span>")
|
||||||
|
else
|
||||||
|
to_chat(vessel, "<span class='warning'>Our body has been disturbed, interrupting the mind control!</span>")
|
||||||
|
release_control()
|
||||||
|
else
|
||||||
|
to_chat(usr, "<span class='warning'>We detect no neural activity in our vessel!</span>")
|
||||||
|
revert_cast()
|
||||||
|
else
|
||||||
|
release_control()
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_control/process()
|
||||||
|
if(active)
|
||||||
|
if(QDELETED(vessel)) //If we've been gibbed or otherwise deleted, ghost both of them and kill the original
|
||||||
|
original_body.adjustBrainLoss(200)
|
||||||
|
release_control()
|
||||||
|
else if(!is_hivemember(vessel)) //If the vessel is no longer a hive member, return to original bodies
|
||||||
|
to_chat(vessel, "<span class='warning'>Our vessel is one of us no more!</span>")
|
||||||
|
release_control()
|
||||||
|
else if(!QDELETED(original_body) && (!vessel.ckey || vessel.stat == DEAD)) //If the original body exists and the vessel is dead/ghosted, return both to body but not before killing the original
|
||||||
|
original_body.adjustBrainLoss(200)
|
||||||
|
to_chat(vessel.mind, "<span class='warning'>Our vessel is one of us no more!</span>")
|
||||||
|
release_control()
|
||||||
|
else if(!QDELETED(original_body) && original_body.z != vessel.z) //Return to original bodies
|
||||||
|
release_control()
|
||||||
|
to_chat(original_body, "<span class='warning'>Our vessel is too far away to control!</span>")
|
||||||
|
if(QDELETED(original_body) || original_body.stat == DEAD) //Return vessel to its body, either return or ghost the original
|
||||||
|
to_chat(vessel, "<span class='userdanger'>Our body has been destroyed, the hive cannot survive without its host!</span>")
|
||||||
|
release_control()
|
||||||
|
..()
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_control/choose_targets(mob/user = usr)
|
||||||
|
if(!active)
|
||||||
|
..()
|
||||||
|
else
|
||||||
|
perform(,user)
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/targeted/induce_panic
|
||||||
|
name = "Induce Panic"
|
||||||
|
desc = "We unleash a burst of psionic energy, inducing a debilitating fear in those around us and reducing their combat readiness. Mindshielded foes have a chance to resist this power."
|
||||||
|
panel = "Hivemind Abilities"
|
||||||
|
charge_max = 900
|
||||||
|
range = 7
|
||||||
|
invocation_type = "none"
|
||||||
|
clothes_req = 0
|
||||||
|
max_targets = 0
|
||||||
|
action_icon = 'icons/mob/actions/actions_hive.dmi'
|
||||||
|
action_background_icon_state = "bg_hive"
|
||||||
|
action_icon_state = "panic"
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/targeted/induce_panic/cast(list/targets, mob/living/user = usr)
|
||||||
|
var/datum/antagonist/hivemind/hive = user.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!hive)
|
||||||
|
to_chat(user, "<span class='notice'>This is a bug. Error:HIVE1</span>")
|
||||||
|
return
|
||||||
|
for(var/mob/living/carbon/human/target in targets)
|
||||||
|
if(target.has_trait(TRAIT_MINDSHIELD) && prob(50-hive.hive_size)) //Mindshielded targets resist panic pretty well
|
||||||
|
continue
|
||||||
|
if(target.stat == DEAD)
|
||||||
|
continue
|
||||||
|
target.Jitter(14)
|
||||||
|
target.adjustStaminaLoss(min(35,hive.hive_size))
|
||||||
|
if(prob(50))
|
||||||
|
var/text = pick(";HELP!","I'm losing control of the situation!!","Get me outta here!")
|
||||||
|
target.say(text, forced = "panic")
|
||||||
|
var/effect = rand(1,4)
|
||||||
|
switch(effect)
|
||||||
|
if(1)
|
||||||
|
to_chat(target, "<span class='userdanger'>You panic and drop everything to the ground!</span>")
|
||||||
|
target.drop_all_held_items()
|
||||||
|
if(2)
|
||||||
|
to_chat(target, "<span class='userdanger'>You panic and flail around!</span>")
|
||||||
|
target.click_random_mob()
|
||||||
|
addtimer(CALLBACK(target, "click_random_mob"), 5)
|
||||||
|
addtimer(CALLBACK(target, "click_random_mob"), 10)
|
||||||
|
addtimer(CALLBACK(target, "click_random_mob"), 15)
|
||||||
|
addtimer(CALLBACK(target, "click_random_mob"), 20)
|
||||||
|
target.Dizzy(2)
|
||||||
|
if(3)
|
||||||
|
to_chat(target, "<span class='userdanger'>You freeze up in fear!</span>")
|
||||||
|
target.Stun(70)
|
||||||
|
if(4)
|
||||||
|
to_chat(target, "<span class='userdanger'>You feel nauseous as dread washes over you!</span>")
|
||||||
|
target.Dizzy(15)
|
||||||
|
target.adjustStaminaLoss(45)
|
||||||
|
target.hallucination += 45
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_attack
|
||||||
|
name = "Medullary Failure"
|
||||||
|
desc = "We overload the target's medulla, inducing an immediate heart attack."
|
||||||
|
|
||||||
|
charge_max = 3000
|
||||||
|
action_icon_state = "attack"
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_attack/cast(list/targets, mob/living/user = usr)
|
||||||
|
var/mob/living/carbon/human/target = targets[1]
|
||||||
|
if(!target.undergoing_cardiac_arrest() && target.can_heartattack())
|
||||||
|
target.set_heartattack(TRUE)
|
||||||
|
to_chat(target, "<span class='userdanger'>You feel a sharp pain, and foreign presence in your mind!!</span>")
|
||||||
|
to_chat(user, "<span class='notice'>We have overloaded the vessel's medulla! Without medical attention, they will shortly die.</span>")
|
||||||
|
if(target.stat == CONSCIOUS)
|
||||||
|
target.visible_message("<span class='userdanger'>[target] clutches at [target.p_their()] chest as if [target.p_their()] heart stopped!</span>")
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='warning'>We are unable to induce a heart attack!</span>")
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_warp
|
||||||
|
name = "Distortion Field"
|
||||||
|
desc = "We warp reality surrounding a vessel, causing hallucinations in everybody around them. This power gets weaker over distance but increases in strength the larger our hive is, and will eventually weaken those caught within the field."
|
||||||
|
|
||||||
|
charge_max = 900
|
||||||
|
action_icon_state = "warp"
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/target_hive/hive_warp/cast(list/targets, mob/living/user = usr)
|
||||||
|
var/mob/living/carbon/human/target = targets[1]
|
||||||
|
var/datum/antagonist/hivemind/hive = user.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!hive)
|
||||||
|
to_chat(user, "<span class='notice'>This is a bug. Error:HIVE1</span>")
|
||||||
|
return
|
||||||
|
var/power = 50-round(get_dist(user, target)/2)
|
||||||
|
if(power < 10 || target.z != user.z)
|
||||||
|
to_chat(user, "<span class='notice'>We are too far away from [target.name] to affect them!</span>")
|
||||||
|
return
|
||||||
|
to_chat(user, "<span class='notice'>We succesfuly distort reality surrounding [target.name]!</span>")
|
||||||
|
switch(hive.hive_size)
|
||||||
|
if(5 to 9)
|
||||||
|
if(10 to 14)
|
||||||
|
power *= 1.5
|
||||||
|
if(15 to 19)
|
||||||
|
power *= 2
|
||||||
|
if(20 to 24)
|
||||||
|
power *= 2.5
|
||||||
|
else
|
||||||
|
power *= 3
|
||||||
|
for(var/mob/living/carbon/human/victim in view(7,target))
|
||||||
|
if(user == victim)
|
||||||
|
continue
|
||||||
|
victim.hallucination += power
|
||||||
|
victim.adjustStaminaLoss(max(0,power-100))
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/targeted/hive_hack
|
||||||
|
name = "Network Invasion"
|
||||||
|
desc = "We probe the mind of an adjacent target and extract valuable information on any enemy hives they may belong to. Takes longer if the target is not in our hive."
|
||||||
|
panel = "Hivemind Abilities"
|
||||||
|
charge_max = 600
|
||||||
|
range = 1
|
||||||
|
invocation_type = "none"
|
||||||
|
clothes_req = 0
|
||||||
|
max_targets = 1
|
||||||
|
action_icon = 'icons/mob/actions/actions_hive.dmi'
|
||||||
|
action_background_icon_state = "bg_hive"
|
||||||
|
action_icon_state = "hack"
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/targeted/hive_hack/cast(list/targets, mob/living/user = usr)
|
||||||
|
var/datum/antagonist/hivemind/hive = user.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!hive)
|
||||||
|
to_chat(user, "<span class='notice'>This is a bug. Error:HIVE1</span>")
|
||||||
|
return
|
||||||
|
var/mob/living/carbon/human/target = targets[1]
|
||||||
|
var/in_hive = hive.hivemembers.Find(target)
|
||||||
|
var/list/enemies = list()
|
||||||
|
var/enemy_names = ""
|
||||||
|
|
||||||
|
to_chat(user, "<span class='notice'>We begin probing [target.name]'s mind!</span>")
|
||||||
|
if(do_mob(user,target,100))
|
||||||
|
if(!in_hive)
|
||||||
|
to_chat(user, "<span class='notice'>Their mind slowly opens up to us.</span>")
|
||||||
|
if(!do_mob(user,target,200))
|
||||||
|
to_chat(user, "<span class='notice'>Our concentration has been broken!</span>")
|
||||||
|
revert_cast()
|
||||||
|
return
|
||||||
|
for(var/datum/antagonist/hivemind/enemy in GLOB.antagonists)
|
||||||
|
var/datum/mind/M = enemy.owner
|
||||||
|
if(!M || M.current == user)
|
||||||
|
continue
|
||||||
|
if(enemy.hivemembers.Find(target))
|
||||||
|
var/hive_name = enemy.get_real_name()
|
||||||
|
if(hive_name)
|
||||||
|
enemies += hive_name
|
||||||
|
enemy.remove_from_hive(target)
|
||||||
|
to_chat(M.current, "<span class='userdanger'>We detect a surge of psionic energy from [target.real_name] before they disappear from the hive. An enemy host, or simply a stolen vessel?</span>")
|
||||||
|
if(enemy.owner == target)
|
||||||
|
user.Stun(70)
|
||||||
|
user.Jitter(14)
|
||||||
|
to_chat(user, "<span class='userdanger'>A sudden surge of psionic energy rushes into your mind, only a Hive host could have such power!!</span>")
|
||||||
|
return
|
||||||
|
if(enemies.len)
|
||||||
|
enemy_names = enemies.Join(". ")
|
||||||
|
to_chat(user, "<span class='userdanger'>In a moment of clarity, we see all. Another hive. Faces. Our nemesis. [enemy_names]. They are watching us. They know we are coming.</span>")
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>We peer into the inner depths of their mind and see nothing, no enemies lurk inside this mind.</span>")
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>Our concentration has been broken!</span>")
|
||||||
|
revert_cast()
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/targeted/hive_assim
|
||||||
|
name = "Mass Assimilation"
|
||||||
|
desc = "Should we capture an enemy Hive host, we can assimilate their entire hive into ours. It is unlikely their mind will surive the ordeal."
|
||||||
|
panel = "Hivemind Abilities"
|
||||||
|
charge_max = 3000
|
||||||
|
range = 1
|
||||||
|
invocation_type = "none"
|
||||||
|
clothes_req = 0
|
||||||
|
max_targets = 1
|
||||||
|
action_icon = 'icons/mob/actions/actions_hive.dmi'
|
||||||
|
action_background_icon_state = "bg_hive"
|
||||||
|
action_icon_state = "assim"
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/targeted/hive_assim/cast(list/targets, mob/living/user = usr)
|
||||||
|
var/datum/antagonist/hivemind/hive = user.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!hive)
|
||||||
|
to_chat(user, "<span class='notice'>This is a bug. Error:HIVE1</span>")
|
||||||
|
return
|
||||||
|
var/mob/living/carbon/human/target = targets[1]
|
||||||
|
|
||||||
|
to_chat(user, "<span class='notice'>We tear into [target.name]'s mind with all our power!</span>")
|
||||||
|
to_chat(target, "<span class='userdanger'>You feel an excruciating pain in your head!</span>")
|
||||||
|
if(do_mob(user,target,150))
|
||||||
|
if(!target.mind)
|
||||||
|
to_chat(user, "<span class='notice'>This being has no mind!</span>")
|
||||||
|
revert_cast()
|
||||||
|
return
|
||||||
|
var/datum/antagonist/hivemind/enemy_hive = target.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(enemy_hive)
|
||||||
|
to_chat(user, "<span class='danger'>We begin assimilating every psionic link we can find!.</span>")
|
||||||
|
to_chat(target, "<span class='userdanger'>Our grip on our mind is slipping!</span>")
|
||||||
|
target.Jitter(14)
|
||||||
|
target.setBrainLoss(125)
|
||||||
|
if(do_mob(user,target,300))
|
||||||
|
enemy_hive = target.mind.has_antag_datum(/datum/antagonist/hivemind) //Check again incase they lost it somehow
|
||||||
|
if(enemy_hive)
|
||||||
|
to_chat(user, "<span class='userdanger'>Ours. It is ours. Our mind has never been stronger, never been larger, never been mightier. And theirs is no more.</span>")
|
||||||
|
to_chat(target, "<span class='userdanger'>Our vessels, they're! That's impossible! We can't... we can't... </span><span class ='notice'>I can't...</span>")
|
||||||
|
hive.hivemembers |= enemy_hive.hivemembers
|
||||||
|
enemy_hive.hivemembers = list()
|
||||||
|
hive.calc_size()
|
||||||
|
enemy_hive.calc_size()
|
||||||
|
target.setBrainLoss(200)
|
||||||
|
|
||||||
|
message_admins("[ADMIN_LOOKUPFLW(target)] was killed and had their hive stolen by [ADMIN_LOOKUPFLW(user)].")
|
||||||
|
log_game("[key_name(target)] was killed via Mass Assimilation by [key_name(user)].")
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>It seems we have been mistaken, this mind is not the host of a hive.</span>")
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='userdanger'>Our concentration has been broken, leaving our mind wide open for a counterattack!</span>")
|
||||||
|
to_chat(target, "<span class='userdanger'>Their concentration has been broken... leaving them wide open for a counterattack!</span>")
|
||||||
|
user.Unconscious(120)
|
||||||
|
user.adjustStaminaLoss(70)
|
||||||
|
user.Jitter(60)
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>We appear to have made a mistake... this mind is too weak to be the one we're looking for.</span>")
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>Our concentration has been broken!</span>")
|
||||||
|
revert_cast()
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/self/hive_loyal
|
||||||
|
name = "Bruteforce"
|
||||||
|
desc = "Our ability to assimilate is temporarily boosted, allowing us to crush the technology shielding the minds of Security and Command personnel and assimilate them."
|
||||||
|
panel = "Hivemind Abilities"
|
||||||
|
charge_max = 1200
|
||||||
|
invocation_type = "none"
|
||||||
|
clothes_req = 0
|
||||||
|
action_icon = 'icons/mob/actions/actions_hive.dmi'
|
||||||
|
action_background_icon_state = "bg_hive"
|
||||||
|
action_icon_state = "loyal"
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/self/hive_loyal/cast(mob/living/user = usr)
|
||||||
|
var/datum/antagonist/hivemind/hive = user.mind.has_antag_datum(/datum/antagonist/hivemind)
|
||||||
|
if(!hive)
|
||||||
|
to_chat(user, "<span class='notice'>This is a bug. Error:HIVE1</span>")
|
||||||
|
return
|
||||||
|
var/obj/effect/proc_holder/spell/target_hive/hive_add/the_spell = locate(/obj/effect/proc_holder/spell/target_hive/hive_add) in user.mind.spell_list
|
||||||
|
if(the_spell)
|
||||||
|
the_spell.ignore_mindshield = TRUE
|
||||||
|
to_chat(user, "<span class='notice'>We prepare to crush mindshielding technology!</span>")
|
||||||
|
addtimer(VARSET_CALLBACK(the_spell, ignore_mindshield, FALSE), 300)
|
||||||
|
addtimer(CALLBACK(GLOBAL_PROC, /proc/to_chat, user, "<span class='warning'>Our heightened power wears off, we are once again unable to assimilate mindshielded crew.</span>"), 300)
|
||||||
|
else
|
||||||
|
to_chat(user, "<span class='notice'>This is a bug. Error:HIVE5</span>")
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/targeted/forcewall/hive
|
||||||
|
name = "Telekinetic Field"
|
||||||
|
desc = "Our psionic powers form a barrier around us in the phsyical world that only we can pass through."
|
||||||
|
panel = "Hivemind Abilities"
|
||||||
|
charge_max = 600
|
||||||
|
clothes_req = 0
|
||||||
|
invocation_type = "none"
|
||||||
|
action_icon = 'icons/mob/actions/actions_hive.dmi'
|
||||||
|
action_background_icon_state = "bg_hive"
|
||||||
|
action_icon_state = "forcewall"
|
||||||
|
range = -1
|
||||||
|
include_user = 1
|
||||||
|
wall_type = /obj/effect/forcefield/wizard/hive
|
||||||
|
|
||||||
|
/obj/effect/proc_holder/spell/targeted/forcewall/hive/cast(list/targets,mob/user = usr)
|
||||||
|
new wall_type(get_turf(user),user)
|
||||||
|
for(var/dir in GLOB.alldirs)
|
||||||
|
new wall_type(get_step(user, dir),user)
|
||||||
|
|
||||||
|
/obj/effect/forcefield/wizard/hive
|
||||||
|
name = "Telekinetic Field"
|
||||||
|
desc = "A psychic barrier, usable by only the strongest of minds."
|
||||||
|
timeleft = 150
|
||||||
|
|
||||||
|
/obj/effect/forcefield/wizard/hive/CanPass(atom/movable/mover, turf/target)
|
||||||
|
if(mover == wizard)
|
||||||
|
return TRUE
|
||||||
|
return FALSE
|
||||||
BIN
icons/mob/actions/actions_hive.dmi
Normal file
BIN
icons/mob/actions/actions_hive.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 6.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
@@ -531,6 +531,8 @@
|
|||||||
#include "code\game\gamemodes\devil\objectives.dm"
|
#include "code\game\gamemodes\devil\objectives.dm"
|
||||||
#include "code\game\gamemodes\devil\devil agent\devil_agent.dm"
|
#include "code\game\gamemodes\devil\devil agent\devil_agent.dm"
|
||||||
#include "code\game\gamemodes\extended\extended.dm"
|
#include "code\game\gamemodes\extended\extended.dm"
|
||||||
|
#include "code\game\gamemodes\hivemind\hivemind.dm"
|
||||||
|
#include "code\game\gamemodes\hivemind\objectives.dm"
|
||||||
#include "code\game\gamemodes\meteor\meteor.dm"
|
#include "code\game\gamemodes\meteor\meteor.dm"
|
||||||
#include "code\game\gamemodes\meteor\meteors.dm"
|
#include "code\game\gamemodes\meteor\meteors.dm"
|
||||||
#include "code\game\gamemodes\monkey\monkey.dm"
|
#include "code\game\gamemodes\monkey\monkey.dm"
|
||||||
@@ -1254,6 +1256,7 @@
|
|||||||
#include "code\modules\antagonists\ert\ert.dm"
|
#include "code\modules\antagonists\ert\ert.dm"
|
||||||
#include "code\modules\antagonists\greentext\greentext.dm"
|
#include "code\modules\antagonists\greentext\greentext.dm"
|
||||||
#include "code\modules\antagonists\highlander\highlander.dm"
|
#include "code\modules\antagonists\highlander\highlander.dm"
|
||||||
|
#include "code\modules\antagonists\hivemind\hivemind.dm"
|
||||||
#include "code\modules\antagonists\magic_servant\servant.dm"
|
#include "code\modules\antagonists\magic_servant\servant.dm"
|
||||||
#include "code\modules\antagonists\monkey\monkey.dm"
|
#include "code\modules\antagonists\monkey\monkey.dm"
|
||||||
#include "code\modules\antagonists\morph\morph.dm"
|
#include "code\modules\antagonists\morph\morph.dm"
|
||||||
@@ -2576,6 +2579,7 @@
|
|||||||
#include "code\modules\spells\spell_types\forcewall.dm"
|
#include "code\modules\spells\spell_types\forcewall.dm"
|
||||||
#include "code\modules\spells\spell_types\genetic.dm"
|
#include "code\modules\spells\spell_types\genetic.dm"
|
||||||
#include "code\modules\spells\spell_types\godhand.dm"
|
#include "code\modules\spells\spell_types\godhand.dm"
|
||||||
|
#include "code\modules\spells\spell_types\hivemind.dm"
|
||||||
#include "code\modules\spells\spell_types\infinite_guns.dm"
|
#include "code\modules\spells\spell_types\infinite_guns.dm"
|
||||||
#include "code\modules\spells\spell_types\inflict_handler.dm"
|
#include "code\modules\spells\spell_types\inflict_handler.dm"
|
||||||
#include "code\modules\spells\spell_types\knock.dm"
|
#include "code\modules\spells\spell_types\knock.dm"
|
||||||
|
|||||||
@@ -540,6 +540,8 @@
|
|||||||
#include "code\game\gamemodes\devil\objectives.dm"
|
#include "code\game\gamemodes\devil\objectives.dm"
|
||||||
#include "code\game\gamemodes\devil\devil agent\devil_agent.dm"
|
#include "code\game\gamemodes\devil\devil agent\devil_agent.dm"
|
||||||
#include "code\game\gamemodes\extended\extended.dm"
|
#include "code\game\gamemodes\extended\extended.dm"
|
||||||
|
#include "code\game\gamemodes\hivemind\hivemind.dm"
|
||||||
|
#include "code\game\gamemodes\hivemind\objectives.dm"
|
||||||
#include "code\game\gamemodes\meteor\meteor.dm"
|
#include "code\game\gamemodes\meteor\meteor.dm"
|
||||||
#include "code\game\gamemodes\meteor\meteors.dm"
|
#include "code\game\gamemodes\meteor\meteors.dm"
|
||||||
#include "code\game\gamemodes\monkey\monkey.dm"
|
#include "code\game\gamemodes\monkey\monkey.dm"
|
||||||
@@ -1259,6 +1261,7 @@
|
|||||||
#include "code\modules\antagonists\ert\ert.dm"
|
#include "code\modules\antagonists\ert\ert.dm"
|
||||||
#include "code\modules\antagonists\greentext\greentext.dm"
|
#include "code\modules\antagonists\greentext\greentext.dm"
|
||||||
#include "code\modules\antagonists\highlander\highlander.dm"
|
#include "code\modules\antagonists\highlander\highlander.dm"
|
||||||
|
#include "code\modules\antagonists\hivemind\hivemind.dm"
|
||||||
#include "code\modules\antagonists\magic_servant\servant.dm"
|
#include "code\modules\antagonists\magic_servant\servant.dm"
|
||||||
#include "code\modules\antagonists\monkey\monkey.dm"
|
#include "code\modules\antagonists\monkey\monkey.dm"
|
||||||
#include "code\modules\antagonists\morph\morph.dm"
|
#include "code\modules\antagonists\morph\morph.dm"
|
||||||
@@ -2568,6 +2571,7 @@
|
|||||||
#include "code\modules\spells\spell_types\forcewall.dm"
|
#include "code\modules\spells\spell_types\forcewall.dm"
|
||||||
#include "code\modules\spells\spell_types\genetic.dm"
|
#include "code\modules\spells\spell_types\genetic.dm"
|
||||||
#include "code\modules\spells\spell_types\godhand.dm"
|
#include "code\modules\spells\spell_types\godhand.dm"
|
||||||
|
#include "code\modules\spells\spell_types\hivemind.dm"
|
||||||
#include "code\modules\spells\spell_types\infinite_guns.dm"
|
#include "code\modules\spells\spell_types\infinite_guns.dm"
|
||||||
#include "code\modules\spells\spell_types\inflict_handler.dm"
|
#include "code\modules\spells\spell_types\inflict_handler.dm"
|
||||||
#include "code\modules\spells\spell_types\knock.dm"
|
#include "code\modules\spells\spell_types\knock.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user