From 8ee01d27b390f3e83ff2ad625243dc130dfe3ec7 Mon Sep 17 00:00:00 2001 From: Jacquerel Date: Tue, 7 Feb 2023 03:15:09 +0000 Subject: [PATCH] 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 :cl: 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. /:cl: --- code/__DEFINES/do_afters.dm | 1 + code/datums/mutations/_mutations.dm | 6 +- code/datums/mutations/webbing.dm | 42 +-- code/game/objects/effects/spiderwebs.dm | 7 +- .../giant_spider/spider_abilities/hivemind.dm | 70 ++++ .../giant_spider/spider_abilities/lay_eggs.dm | 82 ++++ .../giant_spider/spider_abilities/web.dm | 85 +++++ .../giant_spider/spider_abilities/wrap.dm | 110 ++++++ .../simple_animal/hostile/giant_spider.dm | 353 +----------------- .../mob_spawn/ghost_roles/spider_roles.dm | 9 +- tgstation.dme | 4 + 11 files changed, 380 insertions(+), 389 deletions(-) create mode 100644 code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/hivemind.dm create mode 100644 code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/lay_eggs.dm create mode 100644 code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/web.dm create mode 100644 code/modules/mob/living/basic/space_fauna/giant_spider/spider_abilities/wrap.dm 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 2fd033c71d9..5dcf534e31b 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 @@ -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" 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 925fcb53462..ba40570b8a2 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -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"