diff --git a/code/__DEFINES/do_afters.dm b/code/__DEFINES/do_afters.dm
index 5b35e69d0fc..51baef1d04b 100644
--- a/code/__DEFINES/do_afters.dm
+++ b/code/__DEFINES/do_afters.dm
@@ -3,3 +3,4 @@
#define DOAFTER_SOURCE_SURVIVALPEN "doafter_survivalpen"
#define DOAFTER_SOURCE_GETTING_UP "doafter_gettingup"
#define DOAFTER_SOURCE_CLIMBING_LADDER "doafter_climbingladder"
+#define DOAFTER_SOURCE_SPIDER "doafter_spider"
diff --git a/code/datums/mutations/_mutations.dm b/code/datums/mutations/_mutations.dm
index 6277e62e991..1e2e683ef0e 100644
--- a/code/datums/mutations/_mutations.dm
+++ b/code/datums/mutations/_mutations.dm
@@ -17,7 +17,7 @@
/// Visual indicators upon the character of the owner of this mutation
var/static/list/visual_indicators = list()
/// The path of action we grant to our user on mutation gain
- var/datum/action/cooldown/spell/power_path
+ var/datum/action/cooldown/power_path
/// Which mutation layer to use
var/layer_used = MUTATIONS_LAYER
/// To restrict mutation to only certain species
@@ -180,7 +180,7 @@
/datum/mutation/human/proc/modify()
if(modified || !power_path || !owner)
return
- var/datum/action/cooldown/spell/modified_power = locate(power_path) in owner.actions
+ var/datum/action/cooldown/modified_power = locate(power_path) in owner.actions
if(!modified_power)
CRASH("Genetic mutation [type] called modify(), but could not find a action to modify!")
modified_power.cooldown_time *= GET_MUTATION_ENERGY(src) // Doesn't do anything for mutations with energy_coeff unset
@@ -217,7 +217,7 @@
if(!ispath(power_path) || !owner)
return FALSE
- var/datum/action/cooldown/spell/new_power = new power_path(src)
+ var/datum/action/cooldown/new_power = new power_path(src)
new_power.background_icon_state = "bg_tech_blue"
new_power.base_background_icon_state = new_power.background_icon_state
new_power.active_background_icon_state = "[new_power.base_background_icon_state]_active"
diff --git a/code/datums/mutations/webbing.dm b/code/datums/mutations/webbing.dm
index cf7b62aa9f2..4fce0e29da0 100644
--- a/code/datums/mutations/webbing.dm
+++ b/code/datums/mutations/webbing.dm
@@ -5,20 +5,20 @@
quality = POSITIVE
text_gain_indication = "Your skin feels webby."
instability = 15
- power_path = /datum/action/cooldown/spell/lay_genetic_web
+ power_path = /datum/action/cooldown/lay_web/genetic
energy_coeff = 1
/datum/mutation/human/webbing/modify()
. = ..()
- var/datum/action/cooldown/spell/lay_genetic_web/to_modify =.
+ var/datum/action/cooldown/lay_web/genetic/to_modify = .
if(!istype(to_modify)) // null or invalid
return
- if(GET_MUTATION_ENERGY(src) == 1) //energetic chromosome outputs a value less than 1 when present, 1 by default
+ if(GET_MUTATION_ENERGY(src) == 1) // Energetic chromosome outputs a value less than 1 when present, 1 by default
to_modify.webbing_time = initial(to_modify.webbing_time)
return
- to_modify.webbing_time = 2 SECONDS
+ to_modify.webbing_time = 2 SECONDS // Spin webs faster but not more often
/datum/mutation/human/webbing/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -29,37 +29,3 @@
if(..())
return
REMOVE_TRAIT(owner, TRAIT_WEB_WEAVER, GENETIC_MUTATION)
-
-// In the future this could be unified with the spider's web action
-/datum/action/cooldown/spell/lay_genetic_web
- name = "Lay Web"
- desc = "Drops a web. Only you will be able to traverse your web easily, making it pretty good for keeping you safe."
- button_icon = 'icons/mob/actions/actions_genetic.dmi'
- button_icon_state = "lay_web"
-
- cooldown_time = 4 SECONDS //the same time to lay a web
- spell_requirements = NONE
-
- /// How long it takes to lay a web
- var/webbing_time = 4 SECONDS
- /// The path of web that we create
- var/web_path = /obj/structure/spider/stickyweb/genetic
-
-/datum/action/cooldown/spell/lay_genetic_web/cast(atom/cast_on)
- var/turf/web_spot = cast_on.loc
- if(!isturf(web_spot) || (locate(web_path) in web_spot))
- to_chat(cast_on, span_warning("You can't lay webs here!"))
- reset_spell_cooldown()
- return FALSE
-
- cast_on.visible_message(
- span_notice("[cast_on] begins to secrete a sticky substance."),
- span_notice("You begin to lay a web."),
- )
-
- if(!do_after(cast_on, webbing_time, target = web_spot))
- to_chat(cast_on, span_warning("Your web spinning was interrupted!"))
- return
-
- new web_path(web_spot, cast_on)
- return ..()
diff --git a/code/game/objects/effects/spiderwebs.dm b/code/game/objects/effects/spiderwebs.dm
index b47d552ecee..eb7d5fc7b91 100644
--- a/code/game/objects/effects/spiderwebs.dm
+++ b/code/game/objects/effects/spiderwebs.dm
@@ -43,8 +43,9 @@
return
if(!HAS_TRAIT(user,TRAIT_WEB_WEAVER))
return
- user.visible_message(span_notice("[user] begins weaving [src] into cloth."), span_notice("You begin weaving [src] into cloth."))
+ user.balloon_alert_to_viewers("weaving...")
if(!do_after(user, 2 SECONDS))
+ user.balloon_alert(user, "interrupted!")
return
qdel(src)
var/obj/item/stack/sheet/cloth/woven_cloth = new /obj/item/stack/sheet/cloth
@@ -67,7 +68,7 @@
if(istype(mover.pulledby, /mob/living/simple_animal/hostile/giant_spider))
return TRUE
if(prob(50))
- to_chat(mover, span_danger("You get stuck in \the [src] for a moment."))
+ balloon_alert(mover, "stuck in web!")
return FALSE
else if(isprojectile(mover))
return prob(30)
@@ -95,7 +96,7 @@
if(mover.pulledby == allowed_mob)
return TRUE
if(prob(50))
- to_chat(mover, span_danger("You get stuck in \the [src] for a moment."))
+ balloon_alert(mover, "stuck in web!")
return FALSE
else if(isprojectile(mover))
return prob(30)
diff --git a/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/hivemind.dm b/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/hivemind.dm
new file mode 100644
index 00000000000..96f29f99171
--- /dev/null
+++ b/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/hivemind.dm
@@ -0,0 +1,70 @@
+/**
+ * Sets a directive to be given to all future spiders created by the user.
+ * This will be overwritten if used again.
+ */
+/datum/action/set_spider_directive
+ name = "Set Directive"
+ desc = "Set a directive for your future children to follow."
+ button_icon = 'icons/mob/actions/actions_animal.dmi'
+ button_icon_state = "directive"
+ background_icon_state = "bg_alien"
+ overlay_icon_state = "bg_alien_border"
+ check_flags = AB_CHECK_CONSCIOUS
+ /// Current directive to apply
+ var/current_directive = ""
+
+/datum/action/set_spider_directive/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ var/new_directive = tgui_input_text(owner, "Enter the new directive", "Create directive", "[current_directive]")
+ if(isnull(new_directive) || QDELETED(src) || QDELETED(owner) || !IsAvailable(feedback = TRUE))
+ return FALSE
+
+ current_directive = new_directive
+ message_admins("[ADMIN_LOOKUPFLW(owner)] set its directive to: '[current_directive]'.")
+ owner.log_message("set its directive to: '[current_directive]'.", LOG_GAME)
+ return TRUE
+
+/**
+ * Sends a message to all currently living spiders.
+ */
+/datum/action/command_spiders
+ name = "Command"
+ desc = "Send a command to all living spiders."
+ button_icon = 'icons/mob/actions/actions_animal.dmi'
+ button_icon_state = "command"
+ background_icon_state = "bg_alien"
+ overlay_icon_state = "bg_alien_border"
+ check_flags = AB_CHECK_CONSCIOUS
+
+/datum/action/command_spiders/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+
+ var/input = tgui_input_text(owner, "Input a command for your legions to follow.", "Command")
+ if(!input || QDELETED(src) || QDELETED(owner) || !IsAvailable(feedback = TRUE))
+ return FALSE
+
+ spider_command(owner, input)
+ return TRUE
+
+/**
+ * Sends a message to all spiders from the target.
+ *
+ * Allows the user to send a message to all spiders that exist. Ghosts will also see the message.
+ * Arguments:
+ * * user - The spider sending the message
+ * * message - The message to be sent
+ */
+/datum/action/command_spiders/proc/spider_command(mob/living/user, message)
+ if(!message)
+ return
+ var/my_message = span_spider("Command from [user]: [message]")
+ for(var/mob/living/simple_animal/hostile/giant_spider/spider as anything in GLOB.spidermobs)
+ to_chat(spider, my_message)
+ for(var/ghost in GLOB.dead_mob_list)
+ var/link = FOLLOW_LINK(ghost, user)
+ to_chat(ghost, "[link] [my_message]")
+ user.log_talk(message, LOG_SAY, tag = "spider command")
diff --git a/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/lay_eggs.dm b/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/lay_eggs.dm
new file mode 100644
index 00000000000..518ac036bca
--- /dev/null
+++ b/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/lay_eggs.dm
@@ -0,0 +1,82 @@
+/datum/action/lay_eggs
+ name = "Lay Eggs"
+ desc = "Lay a cluster of eggs, which will soon grow into a normal spider."
+ button_icon = 'icons/mob/actions/actions_animal.dmi'
+ button_icon_state = "lay_eggs"
+ background_icon_state = "bg_alien"
+ overlay_icon_state = "bg_alien_border"
+ check_flags = AB_CHECK_CONSCIOUS
+ ///How long it takes for a broodmother to lay eggs.
+ var/egg_lay_time = 12 SECONDS
+ ///The type of egg we create
+ var/egg_type = /obj/effect/mob_spawn/ghost_role/spider
+
+/datum/action/lay_eggs/Grant(mob/grant_to)
+ . = ..()
+ if (!owner)
+ return
+ RegisterSignals(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED), PROC_REF(update_status_on_signal))
+
+/datum/action/lay_eggs/Remove(mob/removed_from)
+ . = ..()
+ UnregisterSignal(removed_from, list(COMSIG_MOVABLE_MOVED, COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED))
+
+/datum/action/lay_eggs/IsAvailable(feedback = FALSE)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(DOING_INTERACTION(owner, DOAFTER_SOURCE_SPIDER))
+ if (feedback)
+ owner.balloon_alert(owner, "busy!")
+ return FALSE
+ var/obj/structure/spider/eggcluster/eggs = locate() in get_turf(owner)
+ if(eggs)
+ if (feedback)
+ owner.balloon_alert(owner, "already eggs here!")
+ return FALSE
+ return TRUE
+
+/datum/action/lay_eggs/Trigger(trigger_flags)
+ . = ..()
+ if (!.)
+ return
+
+ owner.balloon_alert_to_viewers("laying eggs...")
+ if(do_after(owner, egg_lay_time, target = get_turf(owner), interaction_key = DOAFTER_SOURCE_SPIDER))
+ var/obj/structure/spider/eggcluster/eggs = locate() in get_turf(owner)
+ if(eggs)
+ owner.balloon_alert(owner, "already eggs here!")
+ else
+ lay_egg()
+ else
+ owner.balloon_alert(owner, "interrupted!")
+ build_all_button_icons(UPDATE_BUTTON_STATUS)
+
+/datum/action/lay_eggs/proc/lay_egg()
+ var/obj/effect/mob_spawn/ghost_role/spider/new_eggs = new egg_type(get_turf(owner))
+ new_eggs.faction = owner.faction
+ var/datum/action/set_spider_directive/spider_directive = locate() in owner.actions
+ if (spider_directive)
+ new_eggs.directive = spider_directive.current_directive
+
+/datum/action/lay_eggs/enriched
+ name = "Lay Enriched Eggs"
+ desc = "Lay a cluster of eggs, which will soon grow into a greater spider. Requires you drain a human per cluster of these eggs."
+ button_icon_state = "lay_enriched_eggs"
+ egg_type = /obj/effect/mob_spawn/ghost_role/spider/enriched
+ /// How many charges we have to make eggs
+ var/charges = 0
+
+/datum/action/lay_eggs/enriched/IsAvailable(feedback = FALSE)
+ . = ..()
+ if (!.)
+ return FALSE
+ if (charges <= 0)
+ if (feedback)
+ owner.balloon_alert(owner, "must feed first!")
+ return FALSE
+ return TRUE
+
+/datum/action/lay_eggs/enriched/lay_egg()
+ charges--
+ return ..()
diff --git a/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/web.dm b/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/web.dm
new file mode 100644
index 00000000000..1c70131ffd7
--- /dev/null
+++ b/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/web.dm
@@ -0,0 +1,85 @@
+/// Make a sticky web under yourself for area fortification
+/datum/action/cooldown/lay_web
+ name = "Spin Web"
+ desc = "Spin a web to slow down potential prey."
+ button_icon = 'icons/mob/actions/actions_animal.dmi'
+ button_icon_state = "lay_web"
+ background_icon_state = "bg_alien"
+ overlay_icon_state = "bg_alien_border"
+ check_flags = AB_CHECK_CONSCIOUS
+ cooldown_time = 0 SECONDS
+ /// How long it takes to lay a web
+ var/webbing_time = 4 SECONDS
+
+/datum/action/cooldown/lay_web/Grant(mob/grant_to)
+ . = ..()
+ if (!owner)
+ return
+ RegisterSignals(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED), PROC_REF(update_status_on_signal))
+
+/datum/action/cooldown/lay_web/Remove(mob/removed_from)
+ . = ..()
+ UnregisterSignal(removed_from, list(COMSIG_MOVABLE_MOVED, COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED))
+
+/datum/action/cooldown/lay_web/IsAvailable(feedback = FALSE)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(DOING_INTERACTION(owner, DOAFTER_SOURCE_SPIDER))
+ if (feedback)
+ owner.balloon_alert(owner, "busy!")
+ return FALSE
+ if(!isturf(owner.loc))
+ if (feedback)
+ owner.balloon_alert(owner, "invalid location!")
+ return FALSE
+ if(obstructed_by_other_web())
+ if (feedback)
+ owner.balloon_alert(owner, "already webbed!")
+ return FALSE
+ return TRUE
+
+/// Returns true if there's a web we can't put stuff on in our turf
+/datum/action/cooldown/lay_web/proc/obstructed_by_other_web()
+ return !!(locate(/obj/structure/spider/stickyweb) in get_turf(owner))
+
+/datum/action/cooldown/lay_web/Activate()
+ . = ..()
+ var/turf/spider_turf = get_turf(owner)
+ var/obj/structure/spider/stickyweb/web = locate() in spider_turf
+ if(web)
+ owner.balloon_alert_to_viewers("sealing web...")
+ else
+ owner.balloon_alert_to_viewers("spinning web...")
+
+ if(do_after(owner, webbing_time, target = spider_turf, interaction_key = DOAFTER_SOURCE_SPIDER) && owner.loc == spider_turf)
+ plant_web(spider_turf, web)
+ else
+ owner.balloon_alert(owner, "interrupted!")
+ build_all_button_icons()
+
+/// Creates a web in the current turf
+/datum/action/cooldown/lay_web/proc/plant_web(turf/target_turf, obj/structure/spider/stickyweb/existing_web)
+ new /obj/structure/spider/stickyweb(target_turf)
+
+/// Variant for genetics, created webs only allow the creator passage
+/datum/action/cooldown/lay_web/genetic
+ desc = "Spin a web. Only you will be able to traverse your web easily."
+ cooldown_time = 4 SECONDS //the same time to lay a web
+
+/datum/action/cooldown/lay_web/genetic/plant_web(turf/target_turf, obj/structure/spider/stickyweb/existing_web)
+ new /obj/structure/spider/stickyweb/genetic(target_turf, owner)
+
+/// Variant which allows webs to be stacked into walls
+/datum/action/cooldown/lay_web/sealer
+ desc = "Spin a web to slow down potential prey. Webs can be stacked to make solid structures."
+
+/datum/action/cooldown/lay_web/sealer/plant_web(turf/target_turf, obj/structure/spider/stickyweb/existing_web)
+ if (existing_web)
+ qdel(existing_web)
+ new /obj/structure/spider/stickyweb/sealed(target_turf)
+ return
+ new /obj/structure/spider/stickyweb(target_turf)
+
+/datum/action/cooldown/lay_web/sealer/obstructed_by_other_web()
+ return !!(locate(/obj/structure/spider/stickyweb/sealed) in get_turf(owner))
diff --git a/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/wrap.dm b/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/wrap.dm
new file mode 100644
index 00000000000..66653c7529e
--- /dev/null
+++ b/code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/wrap.dm
@@ -0,0 +1,110 @@
+/datum/action/cooldown/wrap
+ name = "Wrap"
+ desc = "Wrap something or someone in a cocoon. \
+ If it's a human or similar species, you'll also consume them. \
+ Consuming a wrapped victim can empower your egg-laying abilities. \
+ Activate this ability and then click on an adjacent target to begin wrapping them."
+ background_icon_state = "bg_alien"
+ overlay_icon_state = "bg_alien_border"
+ button_icon = 'icons/mob/actions/actions_animal.dmi'
+ button_icon_state = "wrap_0"
+ check_flags = AB_CHECK_CONSCIOUS
+ click_to_activate = TRUE
+ ranged_mousepointer = 'icons/effects/mouse_pointers/wrap_target.dmi'
+ /// The time it takes to wrap something.
+ var/wrap_time = 5 SECONDS
+
+/datum/action/cooldown/wrap/Grant(mob/grant_to)
+ . = ..()
+ if (!owner)
+ return
+ RegisterSignals(owner, list(COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED), PROC_REF(update_status_on_signal))
+
+/datum/action/cooldown/wrap/Remove(mob/removed_from)
+ . = ..()
+ UnregisterSignal(removed_from, list(COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED))
+
+/datum/action/cooldown/wrap/IsAvailable(feedback = FALSE)
+ . = ..()
+ if(!. || owner.incapacitated())
+ return FALSE
+ if(DOING_INTERACTION(owner, DOAFTER_SOURCE_SPIDER))
+ if (feedback)
+ owner.balloon_alert(owner, "busy!")
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/wrap/set_click_ability(mob/on_who)
+ . = ..()
+ if(!.)
+ return
+
+ on_who.balloon_alert(on_who, "prepared to wrap")
+ button_icon_state = "wrap_1"
+ build_all_button_icons()
+
+/datum/action/cooldown/wrap/unset_click_ability(mob/on_who, refund_cooldown = TRUE)
+ . = ..()
+ if(!.)
+ return
+
+ if (refund_cooldown)
+ on_who.balloon_alert(on_who, "wrap cancelled")
+ button_icon_state = "wrap_0"
+ build_all_button_icons()
+
+/datum/action/cooldown/wrap/Activate(atom/to_wrap)
+ if(!owner.Adjacent(to_wrap))
+ owner.balloon_alert(owner, "must be closer!")
+ return FALSE
+
+ if(!ismovable(to_wrap) || to_wrap == owner)
+ return FALSE
+
+ if(isspider(to_wrap))
+ owner.balloon_alert(owner, "can't wrap spiders!")
+ return FALSE
+
+ var/atom/movable/target_movable = to_wrap
+ if(target_movable.anchored)
+ return FALSE
+
+ StartCooldown(wrap_time)
+ INVOKE_ASYNC(src, PROC_REF(cocoon), to_wrap)
+ return TRUE
+
+/datum/action/cooldown/wrap/proc/cocoon(atom/movable/to_wrap)
+ owner.visible_message(
+ span_notice("[owner] begins to secrete a sticky substance around [to_wrap]."),
+ span_notice("You begin wrapping [to_wrap] into a cocoon."),
+ )
+ if(do_after(owner, wrap_time, target = to_wrap, interaction_key = DOAFTER_SOURCE_SPIDER))
+ wrap_target(to_wrap)
+ else
+ owner.balloon_alert(owner, "interrupted!")
+
+/datum/action/cooldown/wrap/proc/wrap_target(atom/movable/to_wrap)
+ var/obj/structure/spider/cocoon/casing = new(to_wrap.loc)
+ if(isliving(to_wrap))
+ var/mob/living/living_wrapped = to_wrap
+ // You get a point every time you consume a living player, even if they've been consumed before.
+ // You only get a point for any individual corpse once, so you can't keep breaking it out and eating it again.
+ if(ishuman(living_wrapped) && (living_wrapped.stat != DEAD || !HAS_TRAIT(living_wrapped, TRAIT_SPIDER_CONSUMED)))
+ var/datum/action/lay_eggs/enriched/egg_power = locate() in owner.actions
+ if(egg_power)
+ egg_power.charges++
+ egg_power.build_all_button_icons()
+ owner.visible_message(
+ span_danger("[owner] sticks a proboscis into [living_wrapped] and sucks a viscous substance out."),
+ span_notice("You suck the nutriment out of [living_wrapped], feeding you enough to lay a cluster of enriched eggs."),
+ )
+ ADD_TRAIT(living_wrapped, TRAIT_SPIDER_CONSUMED, TRAIT_GENERIC)
+ living_wrapped.investigate_log("has been killed by being wrapped in a cocoon.", INVESTIGATE_DEATHS)
+ living_wrapped.death() //you just ate them, they're dead.
+ log_combat(owner, living_wrapped, "spider cocooned")
+ else
+ to_chat(owner, span_warning("[living_wrapped] is not edible!"))
+
+ to_wrap.forceMove(casing)
+ if(to_wrap.density || ismob(to_wrap))
+ casing.icon_state = pick("cocoon_large1", "cocoon_large2", "cocoon_large3")
diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
index 7766da05152..499b2a0ee78 100644
--- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
+++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
@@ -1,5 +1,3 @@
-#define INTERACTION_SPIDER_KEY "spider_key"
-
/**
* # Giant Spider
*
@@ -55,8 +53,8 @@
var/poison_type = /datum/reagent/toxin/hunterspider
///How quickly the spider can place down webbing. One is base speed, larger numbers are slower.
var/web_speed = 1
- ///Whether or not the spider can create sealed webs.
- var/web_sealer = FALSE
+ ///What action is used to lay webs, some spiders have a version which can turn webs into walls.
+ var/web_type = /datum/action/cooldown/lay_web
///The message that the mother spider left for this spider when the egg was layed.
var/directive = ""
/// Short description of what this mob is capable of, for radial menu uses
@@ -64,7 +62,8 @@
/mob/living/simple_animal/hostile/giant_spider/Initialize(mapload)
. = ..()
- var/datum/action/innate/spider/lay_web/webbing = new(src)
+ var/datum/action/cooldown/lay_web/webbing = new web_type(src)
+ webbing.webbing_time *= web_speed
webbing.Grant(src)
if(poison_per_bite)
@@ -137,7 +136,7 @@
melee_damage_lower = 5
melee_damage_upper = 10
web_speed = 0.25
- web_sealer = TRUE
+ web_type = /datum/action/cooldown/lay_web/sealer
menu_description = "Support spider variant specializing in healing their brethren and placing webbings very swiftly, but has very low amount of health and deals low damage."
///The health HUD applied to the mob.
var/health_hud = DATA_HUD_MEDICAL_ADVANCED
@@ -148,7 +147,7 @@
datahud.show_to(src)
/mob/living/simple_animal/hostile/giant_spider/nurse/AttackingTarget()
- if(DOING_INTERACTION(src, INTERACTION_SPIDER_KEY))
+ if(DOING_INTERACTION(src, DOAFTER_SOURCE_SPIDER))
return
if(!isspider(target))
return ..()
@@ -167,7 +166,7 @@
span_notice("You begin wrapping the wounds of [hurt_spider]."),
)
- if(!do_after(src, 2 SECONDS, target = hurt_spider, interaction_key = INTERACTION_SPIDER_KEY))
+ if(!do_after(src, 2 SECONDS, target = hurt_spider, interaction_key = DOAFTER_SOURCE_SPIDER))
return
hurt_spider.heal_overall_damage(20, 20)
@@ -279,7 +278,7 @@
melee_damage_upper = 15
gold_core_spawnable = NO_SPAWN
web_speed = 0.5
- web_sealer = TRUE
+ web_type = /datum/action/cooldown/lay_web/sealer
menu_description = "Royal spider variant specializing in reproduction and leadership, but has very low amount of health and deals low damage."
/mob/living/simple_animal/hostile/giant_spider/midwife/Initialize(mapload)
@@ -287,340 +286,18 @@
var/datum/action/cooldown/wrap/wrapping = new(src)
wrapping.Grant(src)
- var/datum/action/innate/spider/lay_eggs/make_eggs = new(src)
+ var/datum/action/lay_eggs/make_eggs = new(src)
make_eggs.Grant(src)
- var/datum/action/innate/spider/lay_eggs/enriched/make_better_eggs = new(src)
+ var/datum/action/lay_eggs/enriched/make_better_eggs = new(src)
make_better_eggs.Grant(src)
- var/datum/action/innate/spider/set_directive/give_orders = new(src)
+ var/datum/action/set_spider_directive/give_orders = new(src)
give_orders.Grant(src)
- var/datum/action/innate/spider/comm/not_hivemind_talk = new(src)
+ var/datum/action/command_spiders/not_hivemind_talk = new(src)
not_hivemind_talk.Grant(src)
-/datum/action/innate/spider
- button_icon = 'icons/mob/actions/actions_animal.dmi'
- background_icon_state = "bg_alien"
- overlay_icon_state = "bg_alien_border"
-
-/datum/action/innate/spider/lay_web // Todo: Unify this with the genetics power
- name = "Spin Web"
- desc = "Spin a web to slow down potential prey."
- check_flags = AB_CHECK_CONSCIOUS
- button_icon_state = "lay_web"
-
-/datum/action/innate/spider/lay_web/Grant(mob/grant_to)
- . = ..()
- if (!owner)
- return
- RegisterSignals(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED), PROC_REF(update_status_on_signal))
-
-/datum/action/innate/spider/lay_web/Remove(mob/removed_from)
- . = ..()
- UnregisterSignal(removed_from, list(COMSIG_MOVABLE_MOVED, COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED))
-
-/datum/action/innate/spider/lay_web/IsAvailable(feedback = FALSE)
- . = ..()
- if(!.)
- return FALSE
- if(!isspider(owner))
- return FALSE
- if(DOING_INTERACTION(owner, INTERACTION_SPIDER_KEY))
- return FALSE
- if(!isturf(owner.loc))
- return FALSE
- var/mob/living/simple_animal/hostile/giant_spider/spider = owner
- var/obj/structure/spider/stickyweb/web = locate() in get_turf(spider)
- if(web && (!spider.web_sealer || istype(web, /obj/structure/spider/stickyweb/sealed)))
- if (feedback)
- owner.balloon_alert(owner, "already webbed!")
- return FALSE
- return TRUE
-
-/datum/action/innate/spider/lay_web/Activate()
- var/turf/spider_turf = get_turf(owner)
- var/mob/living/simple_animal/hostile/giant_spider/spider = owner
- var/obj/structure/spider/stickyweb/web = locate() in spider_turf
- if(web)
- spider.visible_message(
- span_notice("[spider] begins to pack more webbing onto the web."),
- span_notice("You begin to seal the web."),
- )
- else
- spider.visible_message(
- span_notice("[spider] begins to secrete a sticky substance."),
- span_notice("You begin to lay a web."),
- )
-
- spider.stop_automated_movement = TRUE
-
- if(do_after(spider, 4 SECONDS * spider.web_speed, target = spider_turf, interaction_key = INTERACTION_SPIDER_KEY))
- if(spider.loc == spider_turf)
- if(web)
- qdel(web)
- new /obj/structure/spider/stickyweb/sealed(spider_turf)
- new /obj/structure/spider/stickyweb(spider_turf)
- build_all_button_icons()
-
- spider.stop_automated_movement = FALSE
-
-/datum/action/cooldown/wrap
- name = "Wrap"
- desc = "Wrap something or someone in a cocoon. If it's a human or similar species, \
- you'll also consume them, allowing you to lay enriched eggs. \
- Activate this ability and then click on an adjacent target to begin wrapping them."
- background_icon_state = "bg_alien"
- overlay_icon_state = "bg_alien_border"
- button_icon = 'icons/mob/actions/actions_animal.dmi'
- button_icon_state = "wrap_0"
- check_flags = AB_CHECK_CONSCIOUS
- click_to_activate = TRUE
- ranged_mousepointer = 'icons/effects/mouse_pointers/wrap_target.dmi'
- /// The time it takes to wrap something.
- var/wrap_time = 5 SECONDS
-
-/datum/action/cooldown/wrap/Grant(mob/grant_to)
- . = ..()
- if (!owner)
- return
- RegisterSignals(owner, list(COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED), PROC_REF(update_status_on_signal))
-
-/datum/action/cooldown/wrap/Remove(mob/removed_from)
- . = ..()
- UnregisterSignal(removed_from, list(COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED))
-
-/datum/action/cooldown/wrap/IsAvailable(feedback = FALSE)
- . = ..()
- if(!.)
- return FALSE
- if(owner.incapacitated())
- return FALSE
- if(DOING_INTERACTION(owner, INTERACTION_SPIDER_KEY))
- return FALSE
- return TRUE
-
-/datum/action/cooldown/wrap/set_click_ability(mob/on_who)
- . = ..()
- if(!.)
- return
-
- on_who.balloon_alert(on_who, "prepared to wrap")
- button_icon_state = "wrap_1"
- build_all_button_icons()
-
-/datum/action/cooldown/wrap/unset_click_ability(mob/on_who, refund_cooldown = TRUE)
- . = ..()
- if(!.)
- return
-
- if (refund_cooldown)
- on_who.balloon_alert(on_who, "wrap cancelled")
- button_icon_state = "wrap_0"
- build_all_button_icons()
-
-/datum/action/cooldown/wrap/Activate(atom/to_wrap)
- if(!owner.Adjacent(to_wrap))
- owner.balloon_alert(owner, "must be closer!")
- return FALSE
-
- if(!ismob(to_wrap) && !isobj(to_wrap))
- return FALSE
-
- if(to_wrap == owner)
- return FALSE
-
- if(isspider(to_wrap))
- owner.balloon_alert(owner, "can't wrap spiders!")
- return FALSE
-
- var/atom/movable/target_movable = to_wrap
- if(target_movable.anchored)
- return FALSE
-
- StartCooldown(wrap_time)
- INVOKE_ASYNC(src, PROC_REF(cocoon), to_wrap)
- return TRUE
-
-/datum/action/cooldown/wrap/proc/cocoon(atom/movable/to_wrap)
- owner.visible_message(
- span_notice("[owner] begins to secrete a sticky substance around [to_wrap]."),
- span_notice("You begin wrapping [to_wrap] into a cocoon."),
- )
-
- var/mob/living/simple_animal/animal_owner = owner
- if(istype(animal_owner))
- animal_owner.stop_automated_movement = TRUE
-
- if(do_after(owner, wrap_time, target = to_wrap, interaction_key = INTERACTION_SPIDER_KEY))
- wrap_target(to_wrap)
- if(istype(animal_owner))
- animal_owner.stop_automated_movement = FALSE
-
-/datum/action/cooldown/wrap/proc/wrap_target(atom/movable/to_wrap)
- var/obj/structure/spider/cocoon/casing = new(to_wrap.loc)
- if(isliving(to_wrap))
- var/mob/living/living_wrapped = to_wrap
- // You get a point every time you consume a living player, even if they've been consumed before.
- // You only get a point for any individual corpse once, so you can't keep breaking it out and eating it again.
- if(ishuman(living_wrapped) && (living_wrapped.stat != DEAD || !HAS_TRAIT(living_wrapped, TRAIT_SPIDER_CONSUMED)))
- var/datum/action/innate/spider/lay_eggs/enriched/egg_power = locate() in owner.actions
- if(egg_power)
- egg_power.charges++
- egg_power.build_all_button_icons()
- owner.visible_message(
- span_danger("[owner] sticks a proboscis into [living_wrapped] and sucks a viscous substance out."),
- span_notice("You suck the nutriment out of [living_wrapped], feeding you enough to lay a cluster of enriched eggs."),
- )
- ADD_TRAIT(living_wrapped, TRAIT_SPIDER_CONSUMED, TRAIT_GENERIC)
- living_wrapped.investigate_log("has been killed by being wrapped in a cocoon.", INVESTIGATE_DEATHS)
- living_wrapped.death() //you just ate them, they're dead.
- log_combat(owner, living_wrapped, "spider cocooned")
- else
- to_chat(owner, span_warning("[living_wrapped] is not edible!"))
-
- to_wrap.forceMove(casing)
- if(to_wrap.density || ismob(to_wrap))
- casing.icon_state = pick("cocoon_large1", "cocoon_large2", "cocoon_large3")
-
-/datum/action/innate/spider/lay_eggs
- name = "Lay Eggs"
- desc = "Lay a cluster of eggs, which will soon grow into a normal spider."
- check_flags = AB_CHECK_CONSCIOUS
- button_icon_state = "lay_eggs"
- ///How long it takes for a broodmother to lay eggs.
- var/egg_lay_time = 12 SECONDS
- ///The type of egg we create
- var/egg_type = /obj/effect/mob_spawn/ghost_role/spider
-
-/datum/action/innate/spider/lay_eggs/Grant(mob/grant_to)
- . = ..()
- if (!owner)
- return
- RegisterSignals(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED), PROC_REF(update_status_on_signal))
-
-/datum/action/innate/spider/lay_eggs/Remove(mob/removed_from)
- . = ..()
- UnregisterSignal(removed_from, list(COMSIG_MOVABLE_MOVED, COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED))
-
-/datum/action/innate/spider/lay_eggs/IsAvailable(feedback = FALSE)
- . = ..()
- if(!.)
- return FALSE
- if(!isspider(owner))
- return FALSE
- if(DOING_INTERACTION(owner, INTERACTION_SPIDER_KEY))
- return FALSE
- var/obj/structure/spider/eggcluster/eggs = locate() in get_turf(owner)
- if(eggs)
- if (feedback)
- owner.balloon_alert(owner, "already eggs here!")
- return FALSE
- return TRUE
-
-/datum/action/innate/spider/lay_eggs/Activate()
- owner.visible_message(
- span_notice("[owner] begins to lay a cluster of eggs."),
- span_notice("You begin to lay a cluster of eggs."),
- )
-
- var/mob/living/simple_animal/hostile/giant_spider/spider = owner
- spider.stop_automated_movement = TRUE
-
- if(do_after(owner, egg_lay_time, target = get_turf(owner), interaction_key = INTERACTION_SPIDER_KEY))
- var/obj/structure/spider/eggcluster/eggs = locate() in get_turf(owner)
- if(eggs)
- owner.balloon_alert(owner, "already eggs here!")
- else
- lay_egg()
- build_all_button_icons(UPDATE_BUTTON_STATUS)
- spider.stop_automated_movement = FALSE
-
-/datum/action/innate/spider/lay_eggs/proc/lay_egg()
- var/mob/living/simple_animal/hostile/giant_spider/spider = owner
- var/obj/effect/mob_spawn/ghost_role/spider/new_eggs = new egg_type(get_turf(owner))
- new_eggs.directive = spider.directive
- new_eggs.faction = spider.faction
-
-/datum/action/innate/spider/lay_eggs/enriched
- name = "Lay Enriched Eggs"
- desc = "Lay a cluster of eggs, which will soon grow into a greater spider. Requires you drain a human per cluster of these eggs."
- button_icon_state = "lay_enriched_eggs"
- egg_type = /obj/effect/mob_spawn/ghost_role/spider/enriched
- /// How many charges we have to make eggs
- var/charges = 0
-
-/datum/action/innate/spider/lay_eggs/enriched/IsAvailable(feedback = FALSE)
- . = ..()
- if (!.)
- return FALSE
- if (charges <= 0)
- if (feedback)
- owner.balloon_alert(owner, "must feed first!")
- return FALSE
- return TRUE
-
-/datum/action/innate/spider/lay_eggs/enriched/lay_egg()
- charges--
- return ..()
-
-/datum/action/innate/spider/set_directive
- name = "Set Directive"
- desc = "Set a directive for your children to follow."
- check_flags = AB_CHECK_CONSCIOUS
- button_icon_state = "directive"
-
-/datum/action/innate/spider/set_directive/IsAvailable(feedback = FALSE)
- return ..() && isspider(owner)
-
-/datum/action/innate/spider/set_directive/Activate()
- var/mob/living/simple_animal/hostile/giant_spider/spider = owner
-
- spider.directive = tgui_input_text(spider, "Enter the new directive", "Create directive", "[spider.directive]")
- if(isnull(spider.directive) || QDELETED(src) || QDELETED(owner) || !IsAvailable(feedback = TRUE))
- return FALSE
-
- message_admins("[ADMIN_LOOKUPFLW(owner)] set its directive to: '[spider.directive]'.")
- owner.log_message("set its directive to: '[spider.directive]'.", LOG_GAME)
- return TRUE
-
-/datum/action/innate/spider/comm
- name = "Command"
- desc = "Send a command to all living spiders."
- check_flags = AB_CHECK_CONSCIOUS
- button_icon_state = "command"
-
-/datum/action/innate/spider/comm/IsAvailable(feedback = FALSE)
- return ..() && istype(owner, /mob/living/simple_animal/hostile/giant_spider/midwife)
-
-/datum/action/innate/spider/comm/Trigger(trigger_flags)
- var/input = tgui_input_text(owner, "Input a command for your legions to follow.", "Command")
- if(!input || QDELETED(src) || QDELETED(owner) || !IsAvailable(feedback = TRUE))
- return FALSE
-
- spider_command(owner, input)
- return TRUE
-
-/**
- * Sends a message to all spiders from the target.
- *
- * Allows the user to send a message to all spiders that exist. Ghosts will also see the message.
- * Arguments:
- * * user - The spider sending the message
- * * message - The message to be sent
- */
-/datum/action/innate/spider/comm/proc/spider_command(mob/living/user, message)
- if(!message)
- return
- var/my_message
- my_message = span_spider("Command from [user]: [message]")
- for(var/mob/living/simple_animal/hostile/giant_spider/spider as anything in GLOB.spidermobs)
- to_chat(spider, my_message)
- for(var/ghost in GLOB.dead_mob_list)
- var/link = FOLLOW_LINK(ghost, user)
- to_chat(ghost, "[link] [my_message]")
- user.log_talk(message, LOG_SAY, tag = "spider command")
-
/**
* # Giant Ice Spider
*
@@ -744,7 +421,7 @@
blood_spawn_chance = 5)
/mob/living/simple_animal/hostile/giant_spider/hunter/flesh/AttackingTarget()
- if(DOING_INTERACTION(src, INTERACTION_SPIDER_KEY))
+ if(DOING_INTERACTION(src, DOAFTER_SOURCE_SPIDER))
return
if(src == target)
if(on_fire)
@@ -754,7 +431,7 @@
to_chat(src, span_warning("You're not injured, there's no reason to heal."))
return
visible_message(span_notice("[src] begins mending themselves..."),span_notice("You begin mending your wounds..."))
- if(do_after(src, 2 SECONDS, target = src, interaction_key = INTERACTION_SPIDER_KEY))
+ if(do_after(src, 2 SECONDS, target = src, interaction_key = DOAFTER_SOURCE_SPIDER))
heal_overall_damage(maxHealth * 0.5, maxHealth * 0.5)
new /obj/effect/temp_visual/heal(get_turf(src), "#80F5FF")
visible_message(span_notice("[src]'s wounds mend together."),span_notice("You mend your wounds together."))
@@ -776,5 +453,3 @@
/mob/living/simple_animal/hostile/giant_spider/viper/wizard/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
-
-#undef INTERACTION_SPIDER_KEY
diff --git a/code/modules/mob_spawn/ghost_roles/spider_roles.dm b/code/modules/mob_spawn/ghost_roles/spider_roles.dm
index 2badd7dd9c9..13415939035 100644
--- a/code/modules/mob_spawn/ghost_roles/spider_roles.dm
+++ b/code/modules/mob_spawn/ghost_roles/spider_roles.dm
@@ -102,14 +102,11 @@
to_chat(user, span_warning("\The [src] is not ready to hatch yet!"))
return FALSE
-/obj/effect/mob_spawn/ghost_role/spider/equip(mob/living/simple_animal/hostile/giant_spider/spawned_spider)
- if(spawned_spider)
- spawned_spider.directive = directive
-
-/obj/effect/mob_spawn/ghost_role/spider/special(mob/user)
- . = ..()
+/obj/effect/mob_spawn/ghost_role/spider/special(mob/living/simple_animal/hostile/giant_spider/spawned_mob, mob/mob_possessor)
+ spawned_mob.directive = directive
egg.spawner = null
QDEL_NULL(egg)
+ return ..()
/obj/effect/mob_spawn/ghost_role/spider/enriched
name = "enriched egg cluster"
diff --git a/tgstation.dme b/tgstation.dme
index c233d4a0782..fead6e563e4 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3833,6 +3833,10 @@
#include "code\modules\mob\living\basic\space_fauna\carp\magicarp.dm"
#include "code\modules\mob\living\basic\space_fauna\carp\megacarp.dm"
#include "code\modules\mob\living\basic\space_fauna\faithless\faithless.dm"
+#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_abilities\hivemind.dm"
+#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_abilities\lay_eggs.dm"
+#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_abilities\web.dm"
+#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_abilities\wrap.dm"
#include "code\modules\mob\living\basic\syndicate\syndicate.dm"
#include "code\modules\mob\living\basic\syndicate\syndicate_ai.dm"
#include "code\modules\mob\living\basic\vermin\axolotl.dm"