[MIRROR] Re-pr of #70522 "Space Dragon Update: Up Close and Personal" [MDB IGNORE] (#21553)

* Re-pr of #70522 "Space Dragon Update: Up Close and Personal"

* Update mind_linker.dm

* Update hivemind.dm

---------

Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: lessthanthree <83487515+lessthnthree@users.noreply.github.com>
This commit is contained in:
SkyratBot
2023-06-05 20:56:31 -07:00
committed by GitHub
co-authored by MrMelbert lessthanthree
parent 856e059850
commit 33adb63d55
13 changed files with 330 additions and 119 deletions
+100 -43
View File
@@ -10,10 +10,6 @@
var/network_name = "Mind Link"
/// The color of the network when talking in chat
var/chat_color
/// The message sent to someone when linked up.
var/link_message
/// The message sent to someone when unlinked.
var/unlink_message
/// A list of all signals that will call qdel() on our component if triggered. Optional.
var/list/signals_which_destroy_us
/// A callback invoked after an unlink is done. Optional.
@@ -24,25 +20,25 @@
var/speech_action_icon_state = "link_speech"
/// The icon background for the speech action handed out.
var/speech_action_background_icon_state = "bg_alien"
/// The master's linking action, which allows them to link people to the network.
var/datum/action/linker_action
/// The border icon state for the speech action handed out.
var/speech_action_overlay_state = "bg_alien_border"
/// The master's speech action. The owner of the link shouldn't lose this as long as the link remains.
var/datum/action/innate/linked_speech/master_speech
VAR_FINAL/datum/action/innate/linked_speech/master_speech
/// An assoc list of [mob/living]s to [datum/action/innate/linked_speech]s. All the mobs that are linked to our network.
var/list/mob/living/linked_mobs = list()
VAR_FINAL/list/mob/living/linked_mobs = list()
/datum/component/mind_linker/Initialize(
// Customization related settings
network_name = "Mind Link",
chat_color = "#008CA2",
linker_action_path,
link_message,
unlink_message,
signals_which_destroy_us,
datum/callback/post_unlink_callback,
speech_action_icon = 'icons/mob/actions/actions_slime.dmi',
speech_action_icon_state = "link_speech",
speech_action_background_icon_state = "bg_alien",
)
speech_action_overlay_state = "bg_alien_border",
// Optional
signals_which_destroy_us,
datum/callback/post_unlink_callback,
)
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
@@ -51,8 +47,9 @@
src.network_name = network_name
src.chat_color = chat_color
src.link_message = link_message || "You are now connected to [owner.real_name]'s [network_name]."
src.unlink_message = unlink_message || "You are no longer connected to [owner.real_name]'s [network_name]."
src.speech_action_icon = speech_action_icon
src.speech_action_icon_state = speech_action_icon_state
src.speech_action_background_icon_state = speech_action_background_icon_state
if(islist(signals_which_destroy_us))
src.signals_which_destroy_us = signals_which_destroy_us
@@ -63,12 +60,6 @@
src.speech_action_icon_state = speech_action_icon_state
src.speech_action_background_icon_state = speech_action_background_icon_state
if(ispath(linker_action_path))
linker_action = new linker_action_path(src)
linker_action.Grant(owner)
else
stack_trace("[type] was created without a valid linker_action_path. No one will be able to link to it.")
/* ORIGINAL CODE
master_speech = new(src)
master_speech.Grant(owner)
@@ -80,13 +71,10 @@
master_speech.Grant(owner)
//SKYRAT EDIT END
to_chat(owner, span_boldnotice("You establish a [network_name], allowing you to link minds to communicate telepathically."))
/datum/component/mind_linker/Destroy(force, silent)
for(var/mob/living/remaining_mob as anything in linked_mobs)
unlink_mob(remaining_mob)
linked_mobs.Cut()
QDEL_NULL(linker_action)
QDEL_NULL(master_speech)
QDEL_NULL(post_unlink_callback)
return ..()
@@ -127,20 +115,19 @@
if(to_link == owner)
return FALSE
to_chat(to_link, span_notice(link_message))
to_chat(owner, span_notice("You connect [to_link]'s mind to your [network_name]."))
for(var/mob/living/other_link as anything in linked_mobs)
to_chat(other_link, span_notice("You feel a new presence within [owner.real_name]'s [network_name]."))
var/datum/action/innate/linked_speech/new_link = new(src)
new_link.Grant(to_link)
linked_mobs[to_link] = new_link
RegisterSignals(to_link, list(COMSIG_LIVING_DEATH, COMSIG_PARENT_QDELETING, COMSIG_MINDSHIELD_IMPLANTED), PROC_REF(unlink_mob))
RegisterSignals(to_link, list(COMSIG_LIVING_DEATH, COMSIG_PARENT_QDELETING), PROC_REF(sig_unlink_mob))
return TRUE
/datum/component/mind_linker/proc/sig_unlink_mob(mob/living/to_unlink)
SIGNAL_HANDLER
unlink_mob(to_unlink)
/**
* Unlinks [to_unlink] from our network, deleting their speech action
* and cleaning up anything involved.
@@ -148,25 +135,17 @@
* Also invokes post_unlink_callback, if supplied.
*/
/datum/component/mind_linker/proc/unlink_mob(mob/living/to_unlink)
SIGNAL_HANDLER
if(!linked_mobs[to_unlink])
return
return FALSE
to_chat(to_unlink, span_warning(unlink_message))
post_unlink_callback?.Invoke(to_unlink)
UnregisterSignal(to_unlink, list(COMSIG_LIVING_DEATH, COMSIG_PARENT_QDELETING, COMSIG_MINDSHIELD_IMPLANTED))
UnregisterSignal(to_unlink, list(COMSIG_LIVING_DEATH, COMSIG_PARENT_QDELETING))
var/datum/action/innate/linked_speech/old_link = linked_mobs[to_unlink]
linked_mobs -= to_unlink
qdel(old_link)
var/mob/living/owner = parent
to_chat(owner, span_warning("You feel someone disconnect from your [network_name]."))
for(var/mob/living/other_link as anything in linked_mobs)
to_chat(other_link, span_warning("You feel a pressence disappear from [owner.real_name]'s [network_name]."))
return TRUE
/**
* Signal proc sent from any signals given to us initialize.
@@ -181,6 +160,84 @@
qdel(src)
/// Subtype of mind linker (I know) which is more active rather than passive,
/// which involves the master linking people manually rather than people being added automatically.
/datum/component/mind_linker/active_linking
/// The message sent to someone when linked up.
var/link_message
/// The message sent to someone when unlinked.
var/unlink_message
/// The master's linking action, which allows them to link people to the network.
VAR_FINAL/datum/action/linker_action
/datum/component/mind_linker/active_linking/Initialize(
// Customization related settings
network_name = "Mind Link",
chat_color = "#008CA2",
speech_action_icon = 'icons/mob/actions/actions_slime.dmi',
speech_action_icon_state = "link_speech",
speech_action_background_icon_state = "bg_alien",
speech_action_overlay_state = "bg_alien_border",
// Optional
signals_which_destroy_us,
datum/callback/post_unlink_callback,
// Optional for this subtype
link_message,
unlink_message,
// Required for this subtype
linker_action_path,
)
. = ..()
if(. == COMPONENT_INCOMPATIBLE)
return
var/mob/living/owner = parent
src.link_message = link_message || "You are now connected to [owner.real_name]'s [network_name]."
src.unlink_message = unlink_message || "You are no longer connected to [owner.real_name]'s [network_name]."
if(ispath(linker_action_path))
linker_action = new linker_action_path(src)
linker_action.Grant(owner)
else
stack_trace("[type] was created without a valid linker_action_path. No one will be able to link to it.")
to_chat(owner, span_boldnotice("You establish a [network_name], allowing you to link minds to communicate telepathically."))
/datum/component/mind_linker/active_linking/Destroy()
QDEL_NULL(linker_action)
return ..()
/datum/component/mind_linker/active_linking/link_mob(mob/living/to_link)
if(HAS_TRAIT(to_link, TRAIT_MINDSHIELD)) // Mindshield implant - no dice
return FALSE
if(to_link.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0))
return FALSE
. = ..()
if(!.)
return
RegisterSignal(to_link, COMSIG_MINDSHIELD_IMPLANTED, PROC_REF(sig_unlink_mob))
var/mob/living/owner = parent
to_chat(to_link, span_notice(link_message))
to_chat(owner, span_notice("You connect [to_link]'s mind to your [network_name]."))
for(var/mob/living/other_link as anything in linked_mobs)
to_chat(other_link, span_notice("You feel a new presence within [owner.real_name]'s [network_name]."))
/datum/component/mind_linker/active_linking/unlink_mob(mob/living/to_unlink)
. = ..()
if(!.)
return
UnregisterSignal(to_unlink, COMSIG_MINDSHIELD_IMPLANTED)
var/mob/living/owner = parent
to_chat(to_unlink, span_warning(unlink_message))
to_chat(owner, span_warning("You feel someone disconnect from your [network_name]."))
for(var/mob/living/other_link as anything in linked_mobs)
to_chat(other_link, span_warning("You feel a pressence disappear from [owner.real_name]'s [network_name]."))
// Used in mind linker to talk to everyone in the network.
/datum/action/innate/linked_speech
name = "Mind Link Speech"
desc = "Send a psychic message to everyone connected to your Link."
@@ -1,3 +1,4 @@
// Proximity monitor applies forced gravity to all turfs in range.
/datum/proximity_monitor/advanced/gravity
edge_is_a_field = TRUE
var/gravity_value = 0
@@ -10,9 +11,10 @@
/datum/proximity_monitor/advanced/gravity/setup_field_turf(turf/target)
. = ..()
if (isnull(modified_turfs[target]))
if(!isnull(modified_turfs[target]))
return
if(HAS_TRAIT(target, TRAIT_FORCED_GRAVITY))
return
target.AddElement(/datum/element/forced_gravity, gravity_value)
modified_turfs[target] = gravity_value
@@ -20,5 +22,44 @@
. = ..()
if(isnull(modified_turfs[target]))
return
target.RemoveElement(/datum/element/forced_gravity, modified_turfs[target])
var/grav_value = modified_turfs[target] || 0
target.RemoveElement(/datum/element/forced_gravity, grav_value)
modified_turfs -= target
// Subtype which pops up a balloon alert when a mob enters the field
/datum/proximity_monitor/advanced/gravity/warns_on_entrance
/// This is a list of mob refs that have recently entered the field.
/// We track it so that we don't spam a player who is stutter stepping in and out with balloon alerts.
var/list/recently_warned
/datum/proximity_monitor/advanced/gravity/warns_on_entrance/setup_field_turf(turf/target)
. = ..()
for(var/mob/living/guy in target)
warn_mob(guy, target)
/datum/proximity_monitor/advanced/gravity/warns_on_entrance/cleanup_field_turf(turf/target)
. = ..()
for(var/mob/living/guy in target)
warn_mob(guy, target)
/datum/proximity_monitor/advanced/gravity/warns_on_entrance/field_edge_crossed(atom/movable/movable, turf/location)
. = ..()
if(isliving(movable))
warn_mob(movable, location)
/datum/proximity_monitor/advanced/gravity/warns_on_entrance/field_edge_uncrossed(atom/movable/movable, turf/location)
. = ..()
if(isliving(movable))
warn_mob(movable, location)
/datum/proximity_monitor/advanced/gravity/warns_on_entrance/proc/warn_mob(mob/living/to_warn, turf/location)
var/mob_ref_key = REF(to_warn)
if(mob_ref_key in recently_warned)
return
location.balloon_alert(to_warn, "gravity [(location in modified_turfs) ? "shifts!" : "reverts..."]")
LAZYADD(recently_warned, mob_ref_key)
addtimer(CALLBACK(src, PROC_REF(clear_recent_warning), mob_ref_key), 3 SECONDS)
/datum/proximity_monitor/advanced/gravity/warns_on_entrance/proc/clear_recent_warning(mob_ref_key)
LAZYREMOVE(recently_warned, mob_ref_key)
@@ -574,8 +574,6 @@
var/mob/living/simple_animal/hostile/space_dragon/S = new (pick(spawn_locs))
player_mind.transfer_to(S)
player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon))
player_mind.special_role = ROLE_SPACE_DRAGON
player_mind.add_antag_datum(/datum/antagonist/space_dragon)
playsound(S, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
@@ -18,13 +18,13 @@
if(!dragon)
return
var/area/rift_location = get_area(owner)
if(!(rift_location.area_flags & VALID_TERRITORY))
to_chat(owner, span_warning("You can't summon a rift here! Try summoning somewhere secure within the station!"))
if(!(rift_location in dragon.chosen_rift_areas))
owner.balloon_alert(owner, "can't summon a rift here!")
return
for(var/obj/structure/carp_rift/rift as anything in dragon.rift_list)
var/area/used_location = get_area(rift)
if(used_location == rift_location)
to_chat(owner, span_warning("You've already summoned a rift in this area! You have to summon again somewhere else!"))
owner.balloon_alert(owner, "already summoned a rift here!")
return
var/turf/rift_spawn_turf = get_turf(dragon)
if(isopenspaceturf(rift_spawn_turf))
@@ -64,7 +64,7 @@
light_color = LIGHT_COLOR_PURPLE
light_range = 10
anchored = TRUE
density = FALSE
density = TRUE
plane = MASSIVE_OBJ_PLANE
/// The amount of time the rift has charged for.
var/time_charged = 0
@@ -77,11 +77,13 @@
/// Current charge state of the rift.
var/charge_state = CHARGE_ONGOING
/// The interval for adding additional space carp spawns to the rift.
var/carp_interval = 60
var/carp_interval = 45
/// The time since an extra carp was added to the ghost role spawning pool.
var/last_carp_inc = 0
/// A list of all the ckeys which have used this carp rift to spawn in as carps.
var/list/ckey_list = list()
/// Gravity aura for the rift, makes all turfs nearby forced grav.
var/datum/proximity_monitor/advanced/gravity/warns_on_entrance/gravity_aura
/datum/armor/structure_carp_rift
energy = 100
@@ -98,14 +100,25 @@
AddComponent( \
/datum/component/aura_healing, \
range = 0, \
range = 1, \
simple_heal = 5, \
limit_to_trait = TRAIT_HEALS_FROM_CARP_RIFTS, \
healing_color = COLOR_BLUE, \
)
gravity_aura = new(
/* host = */src,
/* range = */15,
/* ignore_if_not_on_turf = */TRUE,
/* gravity = */1,
)
START_PROCESSING(SSobj, src)
/obj/structure/carp_rift/Destroy()
QDEL_NULL(gravity_aura)
return ..()
// Carp rifts always take heavy explosion damage. Discourages the use of maxcaps
// and favours more weaker explosives to destroy the portal
// as they have the same effect on the portal.
@@ -183,7 +196,7 @@
if(time_charged >= max_charge)
charge_state = CHARGE_COMPLETED
var/area/A = get_area(src)
priority_announce("Spatial object has reached peak energy charge in [initial(A.name)], please stand-by.", "Central Command Wildlife Observations")
priority_announce("Spatial object has reached peak energy charge in [initial(A.name)], please stand-by.", "Central Command Wildlife Observations", has_important_message = TRUE)
atom_integrity = INFINITY
icon_state = "carp_rift_charged"
set_light_color(LIGHT_COLOR_DIM_YELLOW)
@@ -230,12 +243,13 @@
to_chat(user, span_warning("The rift already summoned enough carp!"))
return FALSE
if(!dragon)
if(isnull(dragon))
return
var/mob/living/newcarp = new dragon.minion_to_spawn(loc)
newcarp.faction = dragon.owner.current.faction
newcarp.AddElement(/datum/element/nerfed_pulling, GLOB.typecache_general_bad_things_to_easily_move)
newcarp.AddElement(/datum/element/prevent_attacking_of_types, GLOB.typecache_general_bad_hostile_attack_targets, "this tastes awful!")
dragon.wavespeak?.link_mob(newcarp)
if(!is_listed)
ckey_list += user.ckey
@@ -21,9 +21,13 @@
/// Whether or not Space Dragon has completed their objective, and thus triggered the ending sequence.
var/objective_complete = FALSE
/// What mob to spawn from ghosts using this dragon's rifts
var/minion_to_spawn = /mob/living/basic/carp
var/minion_to_spawn = /mob/living/basic/carp/advanced
/// What AI mobs to spawn from this dragon's rifts
var/ai_to_spawn = /mob/living/basic/carp
/// Wavespeak mind linker, to allow telepathy between dragon and carps
var/datum/component/mind_linker/wavespeak
/// What areas are we allowed to place rifts in?
var/list/chosen_rift_areas = list()
/datum/antagonist/space_dragon/greet()
. = ..()
@@ -33,16 +37,42 @@
Today, we will snuff out one of those lights.</b>")
to_chat(owner, span_boldwarning("You have five minutes to find a safe location to place down the first rift. If you take longer than five minutes to place a rift, you will be returned from whence you came."))
owner.announce_objectives()
SEND_SOUND(owner.current, sound('sound/magic/demon_attack1.ogg'))
owner.current.playsound_local(get_turf(owner.current), 'sound/magic/demon_attack1.ogg', 80)
/datum/antagonist/space_dragon/forge_objectives()
var/static/list/area/allowed_areas
if(!allowed_areas)
// Areas that will prove a challeng for the dragon and are provocative to the crew.
allowed_areas = typecacheof(list(
/area/station/command,
/area/station/engineering,
/area/station/science,
/area/station/security,
))
var/list/possible_areas = typecache_filter_list(get_sorted_areas(), allowed_areas)
for(var/area/possible_area as anything in possible_areas)
if(initial(possible_area.outdoors) || !(possible_area.area_flags & VALID_TERRITORY))
possible_areas -= possible_area
for(var/i in 1 to 5)
chosen_rift_areas += pick_n_take(possible_areas)
var/datum/objective/summon_carp/summon = new
summon.dragon = src
objectives += summon
summon.owner = owner
summon.update_explanation_text()
/datum/antagonist/space_dragon/on_gain()
forge_objectives()
rift_ability = new()
owner.special_role = ROLE_SPACE_DRAGON
owner.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon))
return ..()
/datum/antagonist/space_dragon/on_removal()
owner.special_role = null
owner.set_assigned_role(SSjob.GetJobType(/datum/job/unassigned))
return ..()
/datum/antagonist/space_dragon/apply_innate_effects(mob/living/mob_override)
@@ -52,6 +82,15 @@
antag.faction |= FACTION_CARP
// Give the ability over if we have one
rift_ability?.Grant(antag)
wavespeak = antag.AddComponent( \
/datum/component/mind_linker, \
network_name = "Wavespeak", \
chat_color = "#635BAF", \
signals_which_destroy_us = list(COMSIG_LIVING_DEATH), \
speech_action_icon = 'icons/mob/actions/actions_space_dragon.dmi', \
speech_action_icon_state = "wavespeak", \
)
RegisterSignal(wavespeak, COMSIG_PARENT_QDELETING, PROC_REF(clear_wavespeak))
/datum/antagonist/space_dragon/remove_innate_effects(mob/living/mob_override)
var/mob/living/antag = mob_override || owner.current
@@ -59,11 +98,14 @@
UnregisterSignal(antag, COMSIG_LIVING_DEATH)
antag.faction -= FACTION_CARP
rift_ability?.Remove(antag)
QDEL_NULL(wavespeak)
/datum/antagonist/space_dragon/Destroy()
rift_list = null
carp = null
QDEL_NULL(rift_ability)
QDEL_NULL(wavespeak)
chosen_rift_areas.Cut()
return ..()
/datum/antagonist/space_dragon/get_preview_icon()
@@ -77,6 +119,10 @@
return icon
/datum/antagonist/space_dragon/proc/clear_wavespeak()
SIGNAL_HANDLER
wavespeak = null
/**
* Checks to see if we need to do anything with the current state of the dragon's rifts.
*
@@ -132,11 +178,10 @@
objective_complete = TRUE
permanant_empower()
var/datum/objective/summon_carp/main_objective = locate() in objectives
if(main_objective)
main_objective.completed = TRUE
main_objective?.completed = TRUE
priority_announce("A large amount of lifeforms have been detected approaching [station_name()] at extreme speeds. \
Remaining crew are advised to evacuate as soon as possible.", "Central Command Wildlife Observations")
sound_to_playing_players('sound/creatures/space_dragon_roar.ogg')
Remaining crew are advised to evacuate as soon as possible.", "Central Command Wildlife Observations", has_important_message = TRUE)
sound_to_playing_players('sound/creatures/space_dragon_roar.ogg', volume = 75)
for(var/obj/structure/carp_rift/rift as anything in rift_list)
rift.carp_stored = 999999
rift.time_charged = rift.max_charge
@@ -177,8 +222,19 @@
owner.current.remove_movespeed_modifier(/datum/movespeed_modifier/dragon_rage)
/datum/objective/summon_carp
var/datum/antagonist/space_dragon/dragon
explanation_text = "Summon and protect the rifts to flood the station with carp."
explanation_text = "Summon 3 rifts in order to flood the station with carp."
/datum/objective/summon_carp/update_explanation_text()
var/datum/antagonist/space_dragon/dragon_owner = owner.has_antag_datum(/datum/antagonist/space_dragon)
if(isnull(dragon_owner))
return
var/list/converted_names = list()
for(var/area/possible_area as anything in dragon_owner.chosen_rift_areas)
converted_names += possible_area.get_original_area_name()
explanation_text = initial(explanation_text)
explanation_text += " Your possible rift locations are: [english_list(converted_names)]"
/datum/antagonist/space_dragon/roundend_report()
var/list/parts = list()
@@ -197,7 +253,17 @@
parts += "<span class='greentext big'>The [name] was successful!</span>"
else
parts += "<span class='redtext big'>The [name] has failed!</span>"
if(carp.len)
parts += "<span class='header'>The [name] was assisted by:</span>"
parts += printplayerlist(carp)
if(length(carp))
parts += "<br><span class='header'>The [name] was assisted by:</span>"
parts += "<ul class='playerlist'>"
var/list/players_to_carp_taken = list()
for(var/datum/mind/carpy as anything in carp)
players_to_carp_taken[carpy.key] += 1
var/list = ""
for(var/carp_user in players_to_carp_taken)
list += "<li><b>[carp_user]<b>, who played <b>[players_to_carp_taken[carp_user]]</b> space carps.</li>"
parts += list
parts += "</ul>"
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
@@ -33,8 +33,6 @@
var/mob/living/simple_animal/hostile/space_dragon/dragon = new (spawn_location)
dragon.key = key
dragon.mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon))
dragon.mind.special_role = ROLE_SPACE_DRAGON
dragon.mind.add_antag_datum(/datum/antagonist/space_dragon)
playsound(dragon, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(dragon)] has been made into a Space Dragon by an event.")
@@ -256,4 +256,8 @@
disk_overlay = mutable_appearance('icons/mob/simple/carp.dmi', "disk_overlay")
new_overlays += disk_overlay
/mob/living/basic/carp/advanced
health = 40
obj_damage = 15
#undef RARE_CAYENNE_CHANCE
@@ -695,10 +695,11 @@
project_action = new(src)
project_action.Grant(grant_to)
grant_to.AddComponent(/datum/component/mind_linker, \
grant_to.AddComponent( \
/datum/component/mind_linker/active_linking, \
network_name = "Slime Link", \
linker_action_path = /datum/action/innate/link_minds, \
signals_which_destroy_us = list(COMSIG_SPECIES_LOSS), \
linker_action_path = /datum/action/innate/link_minds, \
)
//Species datums don't normally implement destroy, but JELLIES SUCK ASS OUT OF A STEEL STRAW
@@ -73,14 +73,16 @@
var/on_unlink_message = "Your mind shatters as [src]'s Mansus Link leaves your mind."
AddComponent(/datum/component/mind_linker, \
AddComponent( \
/datum/component/mind_linker/active_linking, \
network_name = "Mansus Link", \
chat_color = "#568b00", \
post_unlink_callback = CALLBACK(src, PROC_REF(after_unlink)), \
speech_action_background_icon_state = "bg_heretic", \
speech_action_overlay_state = "bg_heretic_border", \
linker_action_path = /datum/action/cooldown/spell/pointed/manse_link, \
link_message = on_link_message, \
unlink_message = on_unlink_message, \
post_unlink_callback = CALLBACK(src, PROC_REF(after_unlink)), \
speech_action_background_icon_state = "bg_heretic", \
)
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet/attack_animal(mob/living/simple_animal/user, list/modifiers)
@@ -24,8 +24,8 @@
name = "Space Dragon"
desc = "A vile, leviathan-esque creature that flies in the most unnatural way. Looks slightly similar to a space carp."
gender = NEUTER
maxHealth = 320
health = 320
maxHealth = 400
health = 400
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0.5, OXY = 1)
combat_mode = TRUE
speed = 0
@@ -283,7 +283,7 @@
for(var/obj/machinery/door/D in T.contents)
if(D.density)
return
delayFire += 1.5
delayFire += 1.0
addtimer(CALLBACK(src, PROC_REF(dragon_fire_line), T), delayFire)
/**
@@ -295,25 +295,52 @@
* Arguments:
* * turf/T - The turf to trigger the effects on.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/dragon_fire_line(turf/T)
/mob/living/simple_animal/hostile/space_dragon/proc/dragon_fire_line(turf/fire_turf)
var/list/hit_list = list()
hit_list += src
new /obj/effect/hotspot(T)
T.hotspot_expose(700,50,1)
for(var/mob/living/L in T.contents)
if(L in hit_list)
new /obj/effect/hotspot(fire_turf)
fire_turf.hotspot_expose(700,50,1)
for(var/mob/living/living_target in fire_turf.contents)
if(living_target.faction_check_mob(src) && living_target != src)
hit_list += living_target
start_carp_speedboost(living_target)
if(living_target in hit_list)
continue
if(L.mind?.has_antag_datum(/datum/antagonist/space_carp))
if(living_target.mind?.has_antag_datum(/datum/antagonist/space_carp))
continue
hit_list += L
L.adjustFireLoss(30)
to_chat(L, span_userdanger("You're hit by [src]'s fire breath!"))
hit_list += living_target
living_target.adjustFireLoss(30)
to_chat(living_target, span_userdanger("You're hit by [src]'s fire breath!"))
// deals damage to mechs
for(var/obj/vehicle/sealed/mecha/M in T.contents)
if(M in hit_list)
for(var/obj/vehicle/sealed/mecha/mech_target in fire_turf.contents)
if(mech_target in hit_list)
continue
hit_list += M
M.take_damage(50, BRUTE, MELEE, 1)
hit_list += mech_target
mech_target.take_damage(50, BRUTE, MELEE, 1)
/**
* Applies the speed boost to carps when hit by space dragon's flame breath
*
* Applies the dragon rage effect to carps temporarily, giving them a glow and a speed boost.
* This lasts for 8 seconds.
* Arguments:
* * mob/living/target - The carp being affected.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/start_carp_speedboost(mob/living/target)
target.add_filter("anger_glow", 3, list("type" = "outline", "color" = "#ff330030", "size" = 2))
target.add_movespeed_modifier(/datum/movespeed_modifier/dragon_rage)
addtimer(CALLBACK(src, PROC_REF(end_carp_speedboost), target), 8 SECONDS)
/**
* Remove the speed boost from carps when hit by space dragon's flame breath
*
* Removes the dragon rage effect from carps, removing their glow and speed boost.
* Arguments:
* * mob/living/target - The carp being affected.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/end_carp_speedboost(mob/living/target)
target.remove_filter("anger_glow")
target.remove_movespeed_modifier(/datum/movespeed_modifier/dragon_rage)
/**
* Handles consuming and storing consumed things inside Space Dragon
@@ -325,7 +352,7 @@
*/
/mob/living/simple_animal/hostile/space_dragon/proc/eat(atom/movable/A)
if(A && A.loc != src)
playsound(src, 'sound/magic/demon_attack1.ogg', 100, TRUE)
playsound(src, 'sound/magic/demon_attack1.ogg', 60, TRUE)
visible_message(span_warning("[src] swallows [A] whole!"))
A.forceMove(src)
return TRUE
@@ -357,7 +384,7 @@
/mob/living/simple_animal/hostile/space_dragon/proc/useGust(timer)
if(timer != 10)
pixel_y = pixel_y + 2;
addtimer(CALLBACK(src, PROC_REF(useGust), timer + 1), 1.5)
addtimer(CALLBACK(src, PROC_REF(useGust), timer + 1), 1.2)
return
pixel_y = 0
icon_state = "spacedragon_gust_2"
@@ -366,20 +393,22 @@
overlay.appearance_flags = RESET_COLOR
add_overlay(overlay)
playsound(src, 'sound/effects/gravhit.ogg', 100, TRUE)
var/gust_locs = spiral_range_turfs(gust_distance, get_turf(src))
var/list/hit_things = list()
for(var/turf/T in gust_locs)
for(var/mob/living/L in T.contents)
if(L == src)
continue
hit_things += L
visible_message(span_boldwarning("[L] is knocked back by the gust!"))
to_chat(L, span_userdanger("You're knocked back by the gust!"))
var/dir_to_target = get_dir(get_turf(src), get_turf(L))
var/throwtarget = get_edge_target_turf(target, dir_to_target)
L.safe_throw_at(throwtarget, 10, 1, src)
L.Paralyze(50)
for (var/mob/living/candidate in view(gust_distance, src))
if(candidate == src || candidate.faction_check_mob(src))
continue
visible_message(span_boldwarning("[candidate] is knocked back by the gust!"))
to_chat(candidate, span_userdanger("You're knocked back by the gust!"))
var/dir_to_target = get_dir(get_turf(src), get_turf(candidate))
var/throwtarget = get_edge_target_turf(target, dir_to_target)
candidate.safe_throw_at(throwtarget, 10, 1, src)
candidate.Paralyze(50)
addtimer(CALLBACK(src, PROC_REF(reset_status)), 4 + ((tiredness * tiredness_mult) / 10))
tiredness = tiredness + (gust_tiredness * tiredness_mult)
/mob/living/simple_animal/hostile/space_dragon/spawn_with_antag
/mob/living/simple_animal/hostile/space_dragon/spawn_with_antag/mind_initialize()
. = ..()
mind.add_antag_datum(/datum/antagonist/space_dragon)
#undef DARKNESS_THRESHOLD
@@ -262,6 +262,7 @@
/mob/living/simple_animal/hostile/smspider,
/mob/living/simple_animal/hostile/smspider/overcharged,
/mob/living/simple_animal/hostile/space_dragon,
/mob/living/simple_animal/hostile/space_dragon/spawn_with_antag,
/mob/living/simple_animal/hostile/vatbeast,
/mob/living/simple_animal/hostile/venus_human_trap,
/mob/living/simple_animal/hostile/wizard,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 959 B

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -16,11 +16,11 @@ GLOBAL_LIST_EMPTY(hivemind_users)
ui_icon = "users"
///The network that the user is currently hosting
var/datum/component/mind_linker/nif/user_network
var/datum/component/mind_linker/active_linking/nif/user_network
///What networks are the user connected to?
var/list/network_list = list()
///What network is the user sending messages to? This is saved from the keyboard so the user doesn't have to change the channel every time.
var/datum/component/mind_linker/nif/active_network
var/datum/component/mind_linker/active_linking/nif/active_network
///The physical keyboard item being used to send messages
var/obj/item/hivemind_keyboard/linked_keyboard
///What action is being used to summon the Keyboard?
@@ -29,7 +29,7 @@ GLOBAL_LIST_EMPTY(hivemind_users)
/datum/nifsoft/hivemind/New()
. = ..()
user_network = linked_mob.AddComponent(/datum/component/mind_linker/nif, \
user_network = linked_mob.AddComponent(/datum/component/mind_linker/active_linking/nif, \
network_name = "Hivemind Link", \
linker_action_path = /datum/action/innate/hivemind_config, \
)
@@ -54,7 +54,7 @@ GLOBAL_LIST_EMPTY(hivemind_users)
linked_keyboard = null
for(var/datum/component/mind_linker/nif/hivemind as anything in network_list)
for(var/datum/component/mind_linker/active_linking/nif/hivemind as anything in network_list)
hivemind.linked_mobs -= linked_mob
var/mob/living/hivemind_owner = hivemind.parent
@@ -90,7 +90,7 @@ GLOBAL_LIST_EMPTY(hivemind_users)
/datum/action/innate/hivemind_config/Activate()
. = ..()
var/datum/component/mind_linker/nif/link = target
var/datum/component/mind_linker/active_linking/nif/link = target
var/choice = tgui_input_list(owner, "Chose your option", "Hivemind Configuration Menu", list("Link a user","Remove a user","Change Hivemind color","Change active Hivemind","Leave a Hivemind", "Toggle invites"))
if(!choice)
@@ -119,7 +119,7 @@ GLOBAL_LIST_EMPTY(hivemind_users)
var/mob/living/carbon/human/user = owner
var/datum/nifsoft/hivemind/hivemind = user.find_nifsoft(/datum/nifsoft/hivemind)
var/datum/component/mind_linker/nif/new_active_hivemind = tgui_input_list(user, "Choose a Hivemind to set as active.", "Switch Hivemind", hivemind.network_list)
var/datum/component/mind_linker/active_linking/nif/new_active_hivemind = tgui_input_list(user, "Choose a Hivemind to set as active.", "Switch Hivemind", hivemind.network_list)
if(!new_active_hivemind)
return FALSE
@@ -137,7 +137,7 @@ GLOBAL_LIST_EMPTY(hivemind_users)
var/list/network_list = hivemind.network_list
network_list -= hivemind.user_network
var/datum/component/mind_linker/nif/hivemind_to_leave = tgui_input_list(user, "Choose a Hivemind to disconnect from.", "Remove Hivemind", network_list)
var/datum/component/mind_linker/active_linking/nif/hivemind_to_leave = tgui_input_list(user, "Choose a Hivemind to disconnect from.", "Remove Hivemind", network_list)
if(!hivemind_to_leave)
return FALSE
@@ -181,21 +181,21 @@ GLOBAL_LIST_EMPTY(hivemind_users)
///Does the component check to see if the person being linked has a mindshield or anti-magic?
var/linking_protection = TRUE
/datum/component/mind_linker/nif
/datum/component/mind_linker/active_linking/nif
speech_action = FALSE
linking_protection = FALSE
///What is the name of the hivemind? This is mostly here for the TGUI selection menus.
var/name = ""
/datum/component/mind_linker/nif/New()
/datum/component/mind_linker/active_linking/nif/New()
. = ..()
var/mob/living/owner = parent
name = owner.name + "'s " + network_name
///Lets the user add someone to their Hivemind through a choice menu that shows everyone that has the Hivemind NIFSoft.
/datum/component/mind_linker/nif/proc/invite_user()
/datum/component/mind_linker/active_linking/nif/proc/invite_user()
var/list/hivemind_users = GLOB.hivemind_users.Copy()
var/mob/living/carbon/human/owner = parent
@@ -226,7 +226,7 @@ GLOBAL_LIST_EMPTY(hivemind_users)
to_chat(owner, span_abductor("[person_to_add] has now been added to your Hivemind"))
///Removes a user from the list of connected people within a hivemind
/datum/component/mind_linker/nif/proc/remove_user()
/datum/component/mind_linker/active_linking/nif/proc/remove_user()
var/mob/living/carbon/human/owner = parent
var/mob/living/carbon/human/person_to_remove = tgui_input_list(owner, "Choose a person to remove from your Hivemind.", "Remove User", linked_mobs)
@@ -244,7 +244,7 @@ GLOBAL_LIST_EMPTY(hivemind_users)
to_chat(person_to_remove, span_abductor("You have now been removed from [owner]'s Hivemind."))
to_chat(owner, span_abductor("[person_to_remove] has now been removed from your Hivemind."))
/datum/component/mind_linker/nif/proc/change_chat_color()
/datum/component/mind_linker/active_linking/nif/proc/change_chat_color()
var/mob/living/carbon/human/owner = parent
var/new_chat_color = input(owner, "", "Choose Color", COLOR_ASSEMBLY_GREEN) as color
@@ -262,7 +262,7 @@ GLOBAL_LIST_EMPTY(hivemind_users)
righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
inhand_icon_state = "electronic"
///What Hivemind are messages being sent to?
var/datum/component/mind_linker/nif/connected_network
var/datum/component/mind_linker/active_linking/nif/connected_network
//Who owns the controller?
var/datum/weakref/source_user