Basic Mob Spiders I: Ability refactor (#73201)

## About The Pull Request

This morning a man presented me with a mission

![image](https://user-images.githubusercontent.com/7483112/216712023-788f2f35-feaa-4152-acee-a2a58e1e6d2c.png)
I realised that I can achieve this in merely _three_ pull requests.
...because I'm leaving one that adds new AI until after feature freeze
february.

This one pulls all of the ability code out of the giant spider mob file
and spruces it up a bit in preparation for making them into basic mobs.
It removes an ancient "todo" about merging the spider and genetics web
abilities by, doing that.
Spider abilities no longer extend from a shared and reasonably pointless
"innate" root ability and also don't require their owners to be spiders
to work properly any more.

Also while I was in there I fixed a bug where the "set directive"
ability did not actually pass that into the antag datum of a new spider
unless they logged out and back in.

## Why It's Good For The Game

Code should smell nicer now.
Removes an essentially duplicated ability.
Clears up some chat log spam which didn't need to be in there.
You can actually set an objective for spiders you create as a spider
brood mother.
I guess you can make spiders-man as an admin by giving spider abilities
to other mobs.

## Changelog

🆑
refactor: Tidied up spider ability code, they should all work the same,
but please report if they do not.
refactor: Spiders and Geneticists now use the same ability to make webs
rather than two virtually-identical ones, which should also perform just
as it did before this change.
fix: Setting a directive as a Spider Midwife should properly pass and
announce itself to new spiders.
/🆑
This commit is contained in:
Jacquerel
2023-02-06 20:15:09 -07:00
committed by GitHub
parent 7d4f34f057
commit 8ee01d27b3
11 changed files with 380 additions and 389 deletions
+1
View File
@@ -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"
+3 -3
View File
@@ -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
@@ -176,7 +176,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
@@ -213,7 +213,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"
+4 -38
View File
@@ -5,20 +5,20 @@
quality = POSITIVE
text_gain_indication = "<span class='notice'>Your skin feels webby.</span>"
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 ..()
+4 -3
View File
@@ -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)
@@ -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("<b>Command from [user]:</b> [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")
@@ -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 ..()
@@ -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))
@@ -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")
@@ -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("<b>Command from [user]:</b> [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
@@ -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"
+4
View File
@@ -3765,6 +3765,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\space_fauna\netherworld\blankbody.dm"
#include "code\modules\mob\living\basic\space_fauna\netherworld\creature.dm"
#include "code\modules\mob\living\basic\space_fauna\netherworld\migo.dm"