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"