From e135c2326b0beba3e86ca514b6e19872abf219ed Mon Sep 17 00:00:00 2001 From: Kyep <16434066+Kyep@users.noreply.github.com> Date: Thu, 17 Sep 2020 22:29:32 +0000 Subject: [PATCH] Terror Princess Rework (#13657) * rework queen eggs, princess caste * add trailing newlines for travis * refactor for code standards * Remove old/incorrect comment from queen.dm * refactor purple.dm, add notification for which area queen is in * princesses no longer lose ventcrawl or gain wallsmash when nesting, deletes MassFlicker() * add status tab info for purples about where their queen is * Simplified princess code: removed NestMode(), NestMode2(). * makes terrors use 'bite' animation when attacking something * status tab spider regeneration helptext * converts queen spit from TOX to BURN, empress single-fire * fixes terror gender, double-examine text. add terrors closing airlocks, remove queen wrap * newline for travis * alter terror spider event balance depending on server pop * white infection sequence: gray, red, brown, green, green * moves #define isterrorspider(A) to __DEFINES\mobs.dm * remove typoed comment * fix: prevent exploit with spam-clicking egglay action button * fix: prevent AI trying to move spiders after a player takes control of them * fix bug where terrors bumping an airlock repeatedly could make it both open and close * clarify comment on event odds * increase queen spit burn damage from 30 to 40 * add: queen webs are now airtight, instead of dealing damage/hallucination * remove spider alert for already-controlled spiders * queen spit naming fix * travis newline * encourage princesses to remain outside vents * fixes calls to CountSpidersDetailed not using list() * makes it easier to debug away mission AI * code quality: more typeless loops * add: killing a queen, also kills most of her offspring * remove walk fix, for splitting into another PR * remove events\spider_terror.dm changes, move to another PR * makes it compile - will still require merge conflict resolving later. * fix: purple webs cause purples to lose sign of queen and die * visible-> near for purples * fix wrong gender on spiders * queen spit now damages structures (like mechs) and other spiders * ghost spacing * show queen living time on examine * Fixes #12969 - terrors getting stuck in web * revert all changes to purple.dm * farie requests 1 * farie refactoring II * var/list/spider_array * var/list/spider_totals * more farie * queen examine laycount, initial eggcount * terror heat vulnerability, refactoring 1/0 vars Co-authored-by: Kyep --- code/__DEFINES/mobs.dm | 1 + code/game/objects/effects/spiders.dm | 2 +- code/modules/events/spider_terror.dm | 4 +- .../hostile/terror_spiders/actions.dm | 41 +-- .../hostile/terror_spiders/chem.dm | 2 +- .../hostile/terror_spiders/empress.dm | 17 +- .../hostile/terror_spiders/green.dm | 7 +- .../hostile/terror_spiders/hive.dm | 65 ++++- .../hostile/terror_spiders/prince.dm | 1 + .../hostile/terror_spiders/princess.dm | 134 ++++++--- .../hostile/terror_spiders/queen.dm | 266 +++++++++--------- .../hostile/terror_spiders/reproduction.dm | 4 +- .../hostile/terror_spiders/terror_ai.dm | 2 +- .../hostile/terror_spiders/terror_spiders.dm | 117 +++++--- code/modules/mob/mob_helpers.dm | 1 - code/modules/surgery/organs/parasites.dm | 18 +- 16 files changed, 395 insertions(+), 287 deletions(-) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 719274857ec..7cd4cc7a918 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -208,6 +208,7 @@ #define isguardian(A) (istype((A), /mob/living/simple_animal/hostile/guardian)) #define isnymph(A) (istype((A), /mob/living/simple_animal/diona)) #define ishostile(A) (istype(A, /mob/living/simple_animal/hostile)) +#define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider)) #define issilicon(A) (istype((A), /mob/living/silicon)) #define isAI(A) (istype((A), /mob/living/silicon/ai)) diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 639687a45e9..a1538b5fe92 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -42,7 +42,7 @@ /obj/structure/spider/stickyweb/CanPass(atom/movable/mover, turf/target, height=0) if(height == 0) return TRUE - if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider)) + if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider) || isterrorspider(mover)) return TRUE else if(istype(mover, /mob/living)) if(prob(50)) diff --git a/code/modules/events/spider_terror.dm b/code/modules/events/spider_terror.dm index 79acc49fe73..ae10f427506 100644 --- a/code/modules/events/spider_terror.dm +++ b/code/modules/events/spider_terror.dm @@ -43,8 +43,8 @@ spawncount = 2 if(4) // Pretty strong. - spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/princess - spawncount = 2 + spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/queen/princess + spawncount = 3 if(5) // Strongest, only used during highpop. spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/queen diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm index 2b15e584922..218e01af6a2 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm @@ -42,27 +42,6 @@ var/mob/living/simple_animal/hostile/poison/terror_spider/user = owner user.DoVentSmash() -// ---------- PRINCESS ACTIONS - -/datum/action/innate/terrorspider/evolvequeen - name = "Evolve Queen" - icon_icon = 'icons/mob/terrorspider.dmi' - button_icon_state = "terror_queen" - -/datum/action/innate/terrorspider/evolvequeen/Activate() - var/mob/living/simple_animal/hostile/poison/terror_spider/princess/user = owner - if(!istype(user)) - to_chat(user, "ERROR: attempt to use evolve queen ability on a non-princess") - return - var/feedings_left = user.feedings_to_evolve - user.fed - if(feedings_left > 0) - to_chat(user, "You must wrap [feedings_left] more humanoid prey before you can do this!") - return - for(var/mob/living/simple_animal/hostile/poison/terror_spider/queen/Q in GLOB.ts_spiderlist) - if(Q.spider_awaymission == user.spider_awaymission) - to_chat(user, "The presence of another Queen in the area is preventing you from maturing.") - return - user.evolve_to_queen() // ---------- QUEEN ACTIONS @@ -93,19 +72,11 @@ var/mob/living/simple_animal/hostile/poison/terror_spider/queen/user = owner user.LayQueenEggs() -/datum/action/innate/terrorspider/queen/queenfakelings - name = "Fake Spiderlings" - icon_icon = 'icons/effects/effects.dmi' - button_icon_state = "spiderling" - -/datum/action/innate/terrorspider/queen/queenfakelings/Activate() - var/mob/living/simple_animal/hostile/poison/terror_spider/queen/user = owner - user.QueenFakeLings() // ---------- EMPRESS /datum/action/innate/terrorspider/queen/empress/empresserase - name = "Erase Brood" + name = "Empress Erase Brood" icon_icon = 'icons/effects/blood.dmi' button_icon_state = "mgibbl1" @@ -113,6 +84,16 @@ var/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/user = owner user.EraseBrood() +/datum/action/innate/terrorspider/queen/empress/empresslings + name = "Empresss Spiderlings" + icon_icon = 'icons/effects/effects.dmi' + button_icon_state = "spiderling" + +/datum/action/innate/terrorspider/queen/empress/empresslings/Activate() + var/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/user = owner + user.EmpressLings() + + // ---------- WEB /mob/living/simple_animal/hostile/poison/terror_spider/proc/Web() diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/chem.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/chem.dm index 60e11de670a..b685fdd1959 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/chem.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/chem.dm @@ -22,7 +22,7 @@ else if(volume < 90) // bitten thrice, die quickly, severe muscle cramps make movement very difficult. Even calling for help probably won't save you. // total damage: 4, human health 150 until crit, = 37.5 ticks, = 75s = 1m15s until death - update_flags |= M.adjustToxLoss(4, FALSE) // a bit worse than coiine + update_flags |= M.adjustToxLoss(4, FALSE) update_flags |= M.EyeBlurry(6, FALSE) M.Confused(6) else diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm index 137d6739466..f011becf6b8 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm @@ -19,7 +19,6 @@ ventcrawler = 1 idle_ventcrawl_chance = 0 ai_playercontrol_allowtype = 0 - rapid = 3 canlay = 1000 spider_tier = TS_TIER_5 projectiletype = /obj/item/projectile/terrorqueenspit/empress @@ -35,6 +34,8 @@ /mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/New() ..() + empresslings_action = new() + empresslings_action.Grant(src) empresserase_action = new() empresserase_action.Grant(src) @@ -44,7 +45,6 @@ /mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/NestMode() ..() queeneggs_action.button.name = "Empress Eggs" - queenfakelings_action.button.name = "Empress Lings" /mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/LayQueenEggs() var/eggtype = input("What kind of eggs?") as null|anything in list(TS_DESC_QUEEN, TS_DESC_MOTHER, TS_DESC_PRINCE, TS_DESC_PRINCESS, TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN, TS_DESC_BLACK, TS_DESC_PURPLE, TS_DESC_WHITE, TS_DESC_BROWN) @@ -70,7 +70,7 @@ if(TS_DESC_PRINCE) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/prince, numlings) if(TS_DESC_PRINCESS) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/princess, numlings) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess, numlings) if(TS_DESC_MOTHER) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/mother, numlings) if(TS_DESC_QUEEN) @@ -78,7 +78,7 @@ else to_chat(src, "Unrecognized egg type.") -/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/QueenFakeLings() +/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/proc/EmpressLings() var/numlings = input("How many?") as null|anything in list(10, 20, 30, 40, 50) var/sbpc = input("%chance to be stillborn?") as null|anything in list(0, 25, 50, 75, 100) for(var/i=0, iThrough the hivemind, the raw power of [src] floods into your body, burning it from the inside out!") @@ -106,8 +107,6 @@ qdel(T) to_chat(src, "All Terror Spiders, except yourself, will die off shortly.") - /obj/item/projectile/terrorqueenspit/empress - damage_type = BURN - damage = 30 - bonus_tox = 0 + damage = 90 + diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm index 1946f1a32ee..b07f34ec368 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm @@ -39,11 +39,10 @@ to_chat(src, "You must wrap more humanoid prey before you can do this!") return var/list/eggtypes = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN) - var/num_brown = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/brown) - if(num_brown < 2) + var/list/spider_array = CountSpidersDetailed(FALSE) + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/brown] < 2) eggtypes += TS_DESC_BROWN - var/num_black = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/black) - if(num_black < 2) + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/black] < 2) eggtypes += TS_DESC_BLACK var/eggtype = pick(eggtypes) if(client) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm index 245ae95c5e9..407e6f3174e 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm @@ -4,7 +4,8 @@ /mob/living/simple_animal/hostile/poison/terror_spider/proc/DoHiveSense() var/hsline = "" to_chat(src, "Your Brood: ") - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing if(T.spider_awaymission != spider_awaymission) continue hsline = "* [T] in [get_area(T)], " @@ -20,21 +21,55 @@ /mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpiders() var/numspiders = 0 - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing if(T.stat != DEAD && !T.spider_placed && spider_awaymission == T.spider_awaymission) numspiders += 1 return numspiders -/mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpidersType(specific_type) - var/numspiders = 0 - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) - if(T.stat != DEAD && !T.spider_placed && spider_awaymission == T.spider_awaymission) - if(T.type == specific_type) - numspiders += 1 - for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in GLOB.ts_egg_list) - if(E.spiderling_type == specific_type && E.z == z) - numspiders += E.spiderling_number - for(var/obj/structure/spider/spiderling/terror_spiderling/L in GLOB.ts_spiderling_list) - if(!L.stillborn && L.grow_as == specific_type && L.z == z) - numspiders += 1 - return numspiders +/mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpidersDetailed(check_mine = FALSE, list/mytypes = list()) + var/list/spider_totals = list("all" = 0) + var/check_list = length(mytypes) > 0 + for(var/thistype in mytypes) + spider_totals[thistype] = 0 + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing + if(T.stat == DEAD || T.spider_placed || spider_awaymission != T.spider_awaymission) + continue + if(check_mine && T.spider_myqueen != src) + continue + if(check_list && !(T.type in mytypes)) + continue + if(T == src) + continue + if(spider_totals[T.type]) + spider_totals[T.type]++ + else + spider_totals[T.type] = 1 + spider_totals["all"]++ + for(var/thing in GLOB.ts_egg_list) + var/obj/structure/spider/eggcluster/terror_eggcluster/E = thing + if(check_mine && E.spider_myqueen != src) + continue + if(check_list && E.spiderling_type && !(E.spiderling_type in mytypes)) + continue + if(spider_totals[E.spiderling_type]) + spider_totals[E.spiderling_type] += E.spiderling_number + else + spider_totals[E.spiderling_type] = E.spiderling_number + spider_totals["all"] += E.spiderling_number + for(var/thing in GLOB.ts_spiderling_list) + var/obj/structure/spider/spiderling/terror_spiderling/L = thing + if(L.stillborn) + continue + if(check_mine && L.spider_myqueen != src) + continue + if(check_list && L.grow_as && !(L.grow_as in mytypes)) + continue + if(spider_totals[L.grow_as]) + spider_totals[L.grow_as]++ + else + spider_totals[L.grow_as] = 1 + spider_totals["all"]++ + return spider_totals + diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm index e486872abbb..7b900d8a881 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm @@ -29,6 +29,7 @@ spider_opens_doors = 2 web_type = /obj/structure/spider/terrorweb/purple ai_spins_webs = FALSE + gender = MALE /mob/living/simple_animal/hostile/poison/terror_spider/prince/death(gibbed) if(can_die() && !hasdied && spider_uo71) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm index 6f33c51b246..7a840405cd0 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm @@ -2,58 +2,76 @@ // -------------------------------------------------------------------------------- // ----------------- TERROR SPIDERS: T3 PRINCESS OF TERROR -------------------------- // -------------------------------------------------------------------------------- -// -------------: ROLE: cutesy -// -------------: AI: as green, but will evolve to queen when it can -// -------------: SPECIAL: can evolve into a queen, if fed enough -// -------------: TO FIGHT IT: kill it before it evolves +// -------------: ROLE: mini-queen, maintains a smaller nest, but also more expendable +// -------------: AI: maintains a small group of spiders. Small fraction of a queen's nest. +// -------------: SPECIAL: lays eggs over time, like a queen +// -------------: TO FIGHT IT: hunt it before it lays eggs // -------------: SPRITES FROM: FoS, https://www.paradisestation.org/forum/profile/335-fos -/mob/living/simple_animal/hostile/poison/terror_spider/princess +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess name = "Princess of Terror spider" desc = "An enormous spider. It looks strangely cute and fluffy." - spider_role_summary = "Future Queen" + spider_role_summary = "Mini-Queen" ai_target_method = TS_DAMAGE_SIMPLE icon_state = "terror_princess1" icon_living = "terror_princess1" icon_dead = "terror_princess1_dead" maxHealth = 150 health = 150 - regen_points_per_hp = 1 // always regens very fast - force_threshold = 18 // outright immune to anything of force under 18, same as queen - melee_damage_lower = 10 - melee_damage_upper = 20 - idle_ventcrawl_chance = 5 spider_tier = TS_TIER_3 - spider_opens_doors = 2 - web_type = /obj/structure/spider/terrorweb/queen - var/feedings_to_evolve = 3 - var/datum/action/innate/terrorspider/ventsmash/ventsmash_action - var/datum/action/innate/terrorspider/evolvequeen/evolvequeen_action -/mob/living/simple_animal/hostile/poison/terror_spider/princess/New() - ..() - ventsmash_action = new() - ventsmash_action.Grant(src) - evolvequeen_action = new() - evolvequeen_action.Grant(src) + // Unlike queens, no ranged attack. + ranged = 0 + retreat_distance = 0 + minimum_distance = 0 + projectilesound = null + projectiletype = null -/mob/living/simple_animal/hostile/poison/terror_spider/princess/proc/evolve_to_queen() - var/mob/living/simple_animal/hostile/poison/terror_spider/queen/Q = new(loc) - if(mind) - mind.transfer_to(Q) - // Calling `transfer_to()` removes our new body (the Queen's) ability to see the med hud, so we have to re-add the queen here. - var/datum/atom_hud/U = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] - U.add_hud_to(Q) - qdel(src) + canlay = 0 + hasnested = TRUE + spider_spawnfrequency = 300 // 30 seconds + var/grant_prob = 25 // 25% chance every spider_spawnfrequency seconds to gain 1 egg + var/spider_max_children = 8 -/mob/living/simple_animal/hostile/poison/terror_spider/princess/DoWrap() - . = ..() - if(fed == 0) + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/grant_queen_subtype_abilities() + // Queens start in movement mode, where they can ventcrawl but not lay eggs. Then they move to NestMode() where they can wallsmash and egglay, but not ventcrawl. + // Princesses are simpler, and can always lay eggs, always vent crawl, but never smash walls. Unlike queens, they don't have a "nesting" transformation. + queeneggs_action = new() + queeneggs_action.Grant(src) + queensense_action = new() + queensense_action.Grant(src) + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/ListAvailableEggTypes() + var/list/valid_types = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN) + + // Each princess can also have ONE black/purple/brown. If it dies, they can pick a new spider from the 3 advanced types to lay. + var/list/spider_array = CountSpidersDetailed(TRUE, list(/mob/living/simple_animal/hostile/poison/terror_spider/black, /mob/living/simple_animal/hostile/poison/terror_spider/purple, /mob/living/simple_animal/hostile/poison/terror_spider/brown)) + if(spider_array["all"] < 1) + valid_types |= TS_DESC_BLACK + valid_types |= TS_DESC_PURPLE + valid_types |= TS_DESC_BROWN + + return valid_types + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/grant_eggs() + spider_lastspawn = world.time + + if(!prob(grant_prob)) + return + + var/list/spider_array = CountSpidersDetailed(TRUE) + var/brood_count = spider_array["all"] + + // Color shifts depending on how much of their brood capacity they have used. + if(brood_count == 0) icon_state = "terror_princess1" icon_living = "terror_princess1" icon_dead = "terror_princess1_dead" desc = "An enormous spider. It looks strangely cute and fluffy, with soft pink fur covering most of its body." - else if(fed == 1) + else if(brood_count < (spider_max_children /2)) icon_state = "terror_princess2" icon_living = "terror_princess2" icon_dead = "terror_princess2_dead" @@ -62,13 +80,45 @@ icon_state = "terror_princess3" icon_living = "terror_princess3" icon_dead = "terror_princess3_dead" - desc = "An enormous spider. Its entire body has turned an ominous blood red color, with actual blood dripping from its jaws. It stares around, hungrily." + desc = "An enormous spider. Its entire body looks to be the color of dried blood." -/mob/living/simple_animal/hostile/poison/terror_spider/princess/spider_special_action() - if(cocoon_target) - handle_cocoon_target() - else if(fed >= feedings_to_evolve) - evolve_to_queen() - else if(world.time > (last_cocoon_object + freq_cocoon_object)) - seek_cocoon_target() + if(!isturf(loc)) + to_chat(src, "You cannot generate eggs while hiding in [loc].") + return + + if((brood_count + canlay) >= spider_max_children) + return + canlay++ + if(canlay == 1) + to_chat(src, "You have an egg available to lay.") + else + to_chat(src, "You have [canlay] eggs available to lay.") + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/NestMode() + // Princesses don't nest. However, we still need to override this in case an AI princess calls it. + return + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/spider_special_action() + // Princess AI routine. GREATLY simplified version of queen routine. + if(!stat && !ckey) + // Utilize normal queen AI for finding a nest site (neststep=0), and activating NestMode() (neststep=1) + if(neststep != 2) + return ..() + // After that, simply lay an egg once per nestfrequency, until we have the max. + if(world.time < (lastnestsetup + nestfrequency)) + return + lastnestsetup = world.time + if(ai_nest_is_full()) + return + spider_lastspawn = world.time + DoLayTerrorEggs(pick(spider_types_standard), 1) + // Yes, this means NPC princesses won't create T2 spiders. + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/ai_nest_is_full() + var/list/spider_array = CountSpidersDetailed(TRUE) + if(spider_array["all"] >= spider_max_children) + return TRUE + return FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm index 737b6eba22a..af475b1a59a 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm @@ -5,7 +5,7 @@ // -------------: ROLE: gamma-level threat to the whole station, like a blob // -------------: AI: builds a nest, lays many eggs, attempts to take over the station // -------------: SPECIAL: spins webs, breaks lights, breaks cameras, webs objects, lays eggs, commands other spiders... -// -------------: TO FIGHT IT: bring an army, and take no prisoners. Mechs and/or decloner guns are a very good idea. +// -------------: TO FIGHT IT: bring an army, and take no prisoners. Mechs are a very good idea. // -------------: SPRITES FROM: IK3I /mob/living/simple_animal/hostile/poison/terror_spider/queen @@ -43,69 +43,94 @@ var/neststep = 0 var/hasnested = FALSE var/spider_max_per_nest = 35 // above this, AI queens become stable - var/canlay = 4 // main counter for egg-laying ability! # = num uses, incremented at intervals + var/canlay = 5 // main counter for egg-laying ability! # = num uses, incremented at intervals var/eggslaid = 0 - var/spider_can_fakelings = 3 // spawns defective spiderlings that don't grow up, used to freak out crew, atmosphere var/list/spider_types_standard = list(/mob/living/simple_animal/hostile/poison/terror_spider/red, /mob/living/simple_animal/hostile/poison/terror_spider/gray, /mob/living/simple_animal/hostile/poison/terror_spider/green, /mob/living/simple_animal/hostile/poison/terror_spider/black) var/datum/action/innate/terrorspider/queen/queennest/queennest_action var/datum/action/innate/terrorspider/queen/queensense/queensense_action var/datum/action/innate/terrorspider/queen/queeneggs/queeneggs_action - var/datum/action/innate/terrorspider/queen/queenfakelings/queenfakelings_action var/datum/action/innate/terrorspider/ventsmash/ventsmash_action + /mob/living/simple_animal/hostile/poison/terror_spider/queen/New() ..() - queennest_action = new() - queennest_action.Grant(src) ventsmash_action = new() ventsmash_action.Grant(src) + grant_queen_subtype_abilities() spider_myqueen = src if(spider_awaymission) - spider_growinstantly = 1 + spider_growinstantly = TRUE spider_spawnfrequency = 150 + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/grant_queen_subtype_abilities() + queennest_action = new() + queennest_action.Grant(src) + /mob/living/simple_animal/hostile/poison/terror_spider/queen/Life(seconds, times_fired) . = ..() if(stat != DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive. - if(ckey && canlay < 12 && hasnested) // max 12 eggs worth stored at any one time, realistically that's tons. + if(ckey && hasnested) if(world.time > (spider_lastspawn + spider_spawnfrequency)) - if(eggslaid >= 20) - canlay += 3 - else if(eggslaid >= 10) - canlay += 2 - else - canlay++ - spider_lastspawn = world.time - if(canlay == 1) - to_chat(src, "You have an egg available to lay.") - else if(canlay == 12) - to_chat(src, "You have [canlay] eggs available to lay. You won't grow any more eggs until you lay some of your existing ones.") - else - to_chat(src, "You have [canlay] eggs available to lay.") + grant_eggs() + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/grant_eggs() + spider_lastspawn = world.time + canlay += getSpiderLevel() + if(canlay == 1) + to_chat(src, "You have an egg available to lay.") + else if(canlay > 1) + to_chat(src, "You have [canlay] eggs available to lay.") + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/getSpiderLevel() + return 1 + round(MinutesAlive() / 10) + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/MinutesAlive() + return round((world.time - spider_creation_time) / 600) + /mob/living/simple_animal/hostile/poison/terror_spider/queen/death(gibbed) if(can_die() && !hasdied) if(spider_uo71) UnlockBlastDoors("UO71_Caves") - // When a queen dies, so do her player-controlled purple-type guardians. Intended as a motivator for purples to ensure they guard her. - for(var/mob/living/simple_animal/hostile/poison/terror_spider/purple/P in GLOB.ts_spiderlist) - if(ckey) - P.visible_message("\The [src] writhes in pain!") - to_chat(P,"\The [src] has died. Without her hivemind link, purple terrors like yourself cannot survive more than a few minutes!") - P.degenerate = 1 + // When a queen (or subtype!) dies, so do all of her spiderlings, and half of all her fully grown offspring + // This feature is intended to provide a way for crew to still win even if the queen has overwhelming numbers - by sniping the queen. + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing + if(!T.spider_myqueen) + continue + if(T.spider_myqueen != src) + continue + if(prob(50) || T.spider_tier >= spider_tier) + to_chat(T, "\The psychic backlash from the death of [src] crashes into your mind! Somehow... you find a way to keep going!") + continue + T.visible_message("[T] writhes in pain!") + to_chat(T, "\The psychic backlash from the death of [src] overwhelms you! You feel the life start to drain out of you...") + T.degenerate = TRUE + for(var/thing in GLOB.ts_spiderling_list) + var/obj/structure/spider/spiderling/terror_spiderling/T = thing + if(T.spider_myqueen && T.spider_myqueen == src) + qdel(T) return ..() + /mob/living/simple_animal/hostile/poison/terror_spider/queen/Retaliate() ..() - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing T.enemies |= enemies + /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/ai_nest_is_full() var/numspiders = CountSpiders() if(numspiders >= spider_max_per_nest) return TRUE return FALSE + /mob/living/simple_animal/hostile/poison/terror_spider/queen/spider_special_action() if(!stat && !ckey) switch(neststep) @@ -152,11 +177,13 @@ neststep = 2 NestMode() if(2) - // Create initial four purple nest guards. + // Create initial T2 spiders. if(world.time > (lastnestsetup + nestfrequency)) lastnestsetup = world.time spider_lastspawn = world.time - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 4) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 2) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/white, 2) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, 2) neststep = 3 if(3) // Create spiders (random types) until nest is full. @@ -183,28 +210,26 @@ neststep = 4 else spider_lastspawn = world.time - var/num_purple = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/purple) - var/num_white = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/white) - var/num_brown = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/brown) - if(num_purple < 4) + var/list/spider_array = CountSpidersDetailed(FALSE) + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/purple] < 4) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 2) - else if(num_white < 2) + else if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/white] < 2) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/white, 2) - else if(num_brown < 4) + else if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/brown] < 4) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, 4) else DoLayTerrorEggs(pick(spider_types_standard), 5) + /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/NestPrompt() var/confirm = alert(src, "Are you sure you want to nest? You will be able to lay eggs, and smash walls, but not ventcrawl.","Nest?","Yes","No") if(confirm == "Yes") NestMode() + /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/NestMode() queeneggs_action = new() queeneggs_action.Grant(src) - queenfakelings_action = new() - queenfakelings_action.Grant(src) queensense_action = new() queensense_action.Grant(src) queennest_action.Remove(src) @@ -213,21 +238,8 @@ ai_ventcrawls = FALSE environment_smash = ENVIRONMENT_SMASH_RWALLS DoQueenScreech(8, 100, 8, 100) - MassFlicker() to_chat(src, "You have matured to your egglaying stage. You can now smash through walls, and lay eggs, but can no longer ventcrawl.") -/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/MassFlicker() - var/list/target_lights = list() - for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.z != z) - continue - if(H.stat == DEAD) - continue - for(var/obj/machinery/light/L in orange(7, H)) - if(L.on && prob(25)) - target_lights += L - for(var/obj/machinery/light/I in target_lights) - I.flicker() /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/LayQueenEggs() if(stat == DEAD) @@ -242,42 +254,21 @@ else to_chat(src, "Too soon to attempt that again. Wait just a few more seconds...") return - var/list/eggtypes = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN, TS_DESC_BLACK, TS_DESC_PURPLE) - if(canlay >= 4) - eggtypes |= TS_DESC_BROWN - if(canlay >= 12) - eggtypes |= TS_DESC_MOTHER - eggtypes |= TS_DESC_PRINCE - var/num_purples = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/purple) - if(num_purples >= 2) - eggtypes -= TS_DESC_PURPLE - var/num_blacks = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/black) - if(num_blacks >= 2) - eggtypes -= TS_DESC_BLACK + var/list/eggtypes = ListAvailableEggTypes() + var/list/eggtypes_uncapped = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN) + var/eggtype = input("What kind of eggs?") as null|anything in eggtypes + if(canlay < 1) + // this was checked before input() but we have to check again to prevent them spam-clicking the popup. + to_chat(src, "Too soon to lay another egg.") + return if(!(eggtype in eggtypes)) to_chat(src, "Unrecognized egg type.") return 0 - if(eggtype == TS_DESC_MOTHER || eggtype == TS_DESC_PRINCE) - if(canlay < 12) - to_chat(src, "Insufficient strength. It takes as much effort to lay one of those as it does to lay 12 normal eggs.") - else - if(eggtype == TS_DESC_MOTHER) - canlay -= 12 - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/mother, 1) - else if(eggtype == TS_DESC_PRINCE) - canlay -= 12 - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/prince, 1) - return - else if(eggtype == TS_DESC_BROWN) - if(canlay < 4) - to_chat(src, "Insufficient strength. It takes as much effort to lay one of those as it does to lay 4 normal eggs.") - else - canlay -= 4 - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, 1) - return + + // Multiple of eggtypes_uncapped can be laid at once. Other types must be laid one at a time (to prevent exploits) var/numlings = 1 - if(eggtype != TS_DESC_PURPLE) + if(eggtype in eggtypes_uncapped) if(canlay >= 5) numlings = input("How many in the batch?") as null|anything in list(1, 2, 3, 4, 5) else if(canlay >= 3) @@ -287,27 +278,55 @@ if(eggtype == null || numlings == null) to_chat(src, "Cancelled.") return + // Actually lay the eggs. if(canlay < numlings) // We have to check this again after the popups, to account for people spam-clicking the button, then doing all the popups at once. to_chat(src, "Too soon to do this again!") return canlay -= numlings eggslaid += numlings - if(eggtype == TS_DESC_RED) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, numlings) - else if(eggtype == TS_DESC_GRAY) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/gray, numlings) - else if(eggtype == TS_DESC_GREEN) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/green, numlings) - else if(eggtype == TS_DESC_BLACK) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/black, numlings) - else if(eggtype == TS_DESC_PURPLE) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, numlings) - else - to_chat(src, "Unrecognized egg type.") + switch(eggtype) + if(TS_DESC_RED) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, numlings) + if(TS_DESC_GRAY) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/gray, numlings) + if(TS_DESC_GREEN) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/green, numlings) + if(TS_DESC_BLACK) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/black, numlings) + if(TS_DESC_PURPLE) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, numlings) + if(TS_DESC_BROWN) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, numlings) + if(TS_DESC_MOTHER) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/mother, numlings) + if(TS_DESC_PRINCE) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/prince, numlings) + if(TS_DESC_PRINCESS) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess, numlings) + else + to_chat(src, "Unrecognized egg type.") + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/ListAvailableEggTypes() + if(MinutesAlive() >= 20) + var/list/spider_array = CountSpidersDetailed(TRUE, list(/mob/living/simple_animal/hostile/poison/terror_spider/mother, /mob/living/simple_animal/hostile/poison/terror_spider/prince, /mob/living/simple_animal/hostile/poison/terror_spider/queen/princess)) + if(spider_array["all"] == 0) + return list(TS_DESC_PRINCE, TS_DESC_PRINCESS) // Mother will be added to this list.... AFTER mothers are reworked. + + var/list/valid_types = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN) + var/list/spider_array = CountSpidersDetailed(FALSE, list(/mob/living/simple_animal/hostile/poison/terror_spider/brown, /mob/living/simple_animal/hostile/poison/terror_spider/purple, /mob/living/simple_animal/hostile/poison/terror_spider/black)) + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/brown] < 2) + valid_types += TS_DESC_BROWN + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/purple] < 2) + valid_types += TS_DESC_PURPLE + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/black] < 2) + valid_types += TS_DESC_BLACK + return valid_types + /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/DoQueenScreech(light_range, light_chance, camera_range, camera_chance) - visible_message("\The [src] emits a bone-chilling shriek!") + visible_message("[src] emits a bone-chilling shriek!") for(var/obj/machinery/light/L in orange(light_range, src)) if(L.on && prob(light_chance)) L.break_light_tube() @@ -315,46 +334,33 @@ if(C.status && prob(camera_chance)) C.toggle_cam(src, 0) -/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/QueenFakeLings() - if(eggslaid < 10) - to_chat(src, "You must lay at least 10 eggs before doing this.") + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/examine(mob/user) + . = ..() + if(!key || stat == DEAD) return - if(spider_can_fakelings) - spider_can_fakelings-- - var/numlings = 25 - for(var/i in 1 to numlings) - var/obj/structure/spider/spiderling/terror_spiderling/S = new /obj/structure/spider/spiderling/terror_spiderling(get_turf(src)) - S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/red - S.stillborn = 1 - S.spider_mymother = src - if(!spider_can_fakelings) - queenfakelings_action.Remove(src) - else - to_chat(src, "You have run out of uses of this ability.") + if(!isobserver(user) && !isterrorspider(user)) + return + . += "[p_they(TRUE)] has laid [eggslaid] egg[eggslaid != 1 ? "s" : ""]." + . += "[p_they(TRUE)] has lived for [MinutesAlive()] minutes." + /obj/item/projectile/terrorqueenspit - name = "poisonous spit" - damage = 0 + name = "acid spit" + damage = 40 icon_state = "toxin" - damage_type = TOX - var/bonus_tox = 30 + damage_type = BURN -/obj/item/projectile/terrorqueenspit/on_hit(mob/living/carbon/target, blocked = 0, hit_zone) - if(ismob(target) && blocked < 100) - var/mob/living/L = target - if(L.reagents) - if(L.can_inject(null, FALSE, "chest", FALSE)) - L.Hallucinate(400) - if(!isterrorspider(L)) - L.adjustToxLoss(bonus_tox) /obj/structure/spider/terrorweb/queen - name = "shimmering web" - desc = "This web seems to shimmer all different colors in the light." + name = "airtight web" + desc = "This multi-layered web seems to be able to resist air pressure." + + +/obj/structure/spider/terrorweb/queen/New() + . = ..() + air_update_turf(TRUE) + +/obj/structure/spider/terrorweb/queen/CanAtmosPass(turf/T) + return FALSE -/obj/structure/spider/terrorweb/queen/web_special_ability(mob/living/carbon/C) - if(istype(C)) - var/inject_target = pick("chest","head") - if(C.can_inject(null, FALSE, inject_target, FALSE)) - C.Hallucinate(400) - C.adjustToxLoss(30) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm index a889bc6d328..8961fcdddb5 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm @@ -188,7 +188,7 @@ C.enemies = enemies if(spider_growinstantly) C.amount_grown = 250 - C.spider_growinstantly = 1 + C.spider_growinstantly = TRUE spawn(10) stop_automated_movement = 0 @@ -196,7 +196,7 @@ name = "terror egg cluster" desc = "A cluster of tiny spider eggs. They pulse with a strong inner life, and appear to have sharp thorns on the sides." icon_state = "eggs" - var/spider_growinstantly = 0 + var/spider_growinstantly = FALSE var/spider_myqueen = null var/spider_mymother = null var/spiderling_type = null diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm index 11e4cac3e4a..c3e0c9a0511 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm @@ -126,7 +126,7 @@ spider_steps_taken++ CreatePath(entry_vent) step_to(src,entry_vent) - if(spider_debug > 0) + if(spider_debug) visible_message("[src] moves towards the vent [entry_vent].") else path_to_vent = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index ab36c6ca313..6c943b26f53 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -17,6 +17,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) // Name / Description name = "terror spider" desc = "The generic parent of all other terror spider types. If you see this in-game, it is a bug." + gender = FEMALE // Icons icon = 'icons/mob/terrorspider.dmi' @@ -141,29 +142,33 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) var/mylocation = null var/chasecycles = 0 var/web_infects = 0 + var/spider_creation_time = 0 var/datum/action/innate/terrorspider/web/web_action var/web_type = /obj/structure/spider/terrorweb var/datum/action/innate/terrorspider/wrap/wrap_action - // Breathing - require some oxygen, and no toxins, but take little damage from this requirement not being met (they can hold their breath) + // Breathing - require some oxygen, and no toxins atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - unsuitable_atmos_damage = 1 - // Temperature - can freeze in space and cook in plasma, but it takes extreme temperatures to do this. - minbodytemp = 100 - maxbodytemp = 500 - heat_damage_per_tick = 3 + // Temperature + heat_damage_per_tick = 5 // Takes 250% normal damage from being in a hot environment ("kill it with fire!") // DEBUG OPTIONS & COMMANDS - var/spider_growinstantly = 0 // DEBUG OPTION, DO NOT ENABLE THIS ON LIVE. IT IS USED TO TEST NEST GROWTH/SETUP AI. - var/spider_debug = 0 + var/spider_growinstantly = FALSE // DEBUG OPTION, DO NOT ENABLE THIS ON LIVE. IT IS USED TO TEST NEST GROWTH/SETUP AI. + var/spider_debug = FALSE // -------------------------------------------------------------------------------- // --------------------- TERROR SPIDERS: SHARED ATTACK CODE ----------------------- // -------------------------------------------------------------------------------- +/mob/living/simple_animal/hostile/poison/terror_spider/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect) + // Forces terrors to use the 'bite' graphic when attacking something. Same as code/modules/mob/living/carbon/alien/larva/larva_defense.dm#L34 + if(!no_effect && !visual_effect_icon) + visual_effect_icon = ATTACK_EFFECT_BITE + ..() + /mob/living/simple_animal/hostile/poison/terror_spider/AttackingTarget() if(isterrorspider(target)) if(target in enemies) @@ -187,14 +192,13 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) if(F.welded) to_chat(src, "The fire door is welded shut.") else - visible_message("\The [src] pries open the firedoor!") + visible_message("[src] pries open the firedoor!") F.open() else to_chat(src, "Closing fire doors does not help.") else if(istype(target, /obj/machinery/door/airlock)) var/obj/machinery/door/airlock/A = target - if(A.density) - try_open_airlock(A) + try_open_airlock(A) else if(isliving(target) && (!client || a_intent == INTENT_HARM)) var/mob/living/G = target if(issilicon(G)) @@ -221,27 +225,23 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) /mob/living/simple_animal/hostile/poison/terror_spider/examine(mob/user) . = ..() - var/list/msgs = list() - if(stat == DEAD) - msgs += "It appears to be dead.\n" - else + if(stat != DEAD) if(key) - msgs += "Its eyes regard you with a curious intelligence." + . += "[p_they(TRUE)] regards [p_their()] surroundings with a curious intelligence." if(health > (maxHealth*0.95)) - msgs += "It is in excellent health." + . += "[p_they(TRUE)] is in excellent health." else if(health > (maxHealth*0.75)) - msgs += "It has a few injuries." + . += "[p_they(TRUE)] has a few injuries." else if(health > (maxHealth*0.55)) - msgs += "It has many injuries." + . += "[p_they(TRUE)] has many injuries." else if(health > (maxHealth*0.25)) - msgs += "It is barely clinging on to life!" + . += "[p_they(TRUE)] is barely clinging on to life!" if(degenerate) - msgs += "It appears to be dying." + . += "[p_they(TRUE)] appears to be dying." else if(health < maxHealth && regen_points > regen_points_per_kill) - msgs += "It appears to be regenerating quickly." + . += "[p_they(TRUE)] appears to be regenerating quickly." if(killcount >= 1) - msgs += "It has blood dribbling from its mouth." - . += msgs.Join("
") + . += "[p_they(TRUE)] has blood dribbling from [p_their()] mouth." /mob/living/simple_animal/hostile/poison/terror_spider/New() ..() @@ -254,9 +254,10 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) if(web_type) web_action = new() web_action.Grant(src) - wrap_action = new() - wrap_action.Grant(src) - + if(regen_points_per_tick < regen_points_per_hp) + // Only grant the Wrap action button to spiders who need to use it to regenerate their health + wrap_action = new() + wrap_action.Grant(src) name += " ([rand(1, 1000)])" real_name = name msg_terrorspiders("[src] has grown in [get_area(src)].") @@ -278,6 +279,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) addtimer(CALLBACK(src, .proc/announcetoghosts), 30) var/datum/atom_hud/U = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] U.add_hud_to(src) + spider_creation_time = world.time /mob/living/simple_animal/hostile/poison/terror_spider/proc/announcetoghosts() if(spider_awaymission) @@ -285,8 +287,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) if(stat == DEAD) return if(ckey) - var/image/alert_overlay = image('icons/mob/terrorspider.dmi', icon_state) - notify_ghosts("[src] has appeared in [get_area(src)]. (already player-controlled)", source = src, alert_overlay = alert_overlay) + notify_ghosts("[src] (player controlled) has appeared in [get_area(src)].") else if(ai_playercontrol_allowtype) var/image/alert_overlay = image('icons/mob/terrorspider.dmi', icon_state) notify_ghosts("[src] has appeared in [get_area(src)].", enter_link = "(Click to control)", source = src, alert_overlay = alert_overlay, action = NOTIFY_ATTACK) @@ -342,7 +343,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) /mob/living/simple_animal/hostile/poison/terror_spider/ObjBump(obj/O) if(istype(O, /obj/machinery/door/airlock)) var/obj/machinery/door/airlock/L = O - if(L.density) + if(L.density) // must check density here, to avoid rapid bumping of an airlock that is in the process of opening, instantly forcing it closed return try_open_airlock(L) if(istype(O, /obj/machinery/door/firedoor)) var/obj/machinery/door/firedoor/F = O @@ -352,7 +353,8 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) . = ..() /mob/living/simple_animal/hostile/poison/terror_spider/proc/msg_terrorspiders(msgtext) - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing if(T.stat != DEAD) to_chat(T, "TerrorSense: [msgtext]") @@ -365,21 +367,54 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) /mob/living/simple_animal/hostile/poison/terror_spider/proc/try_open_airlock(obj/machinery/door/airlock/D) if(D.operating) return - if(!D.density) - to_chat(src, "Closing doors does not help us.") - else if(D.welded) - to_chat(src, "The door is welded shut.") + if(D.welded) + to_chat(src, "The door is welded.") else if(D.locked) - to_chat(src, "The door is bolted shut.") + to_chat(src, "The door is bolted.") else if(D.allowed(src)) - D.open(1) - return 1 + if(D.density) + D.open(TRUE) + else + D.close(TRUE) + return TRUE else if(D.arePowerSystemsOn() && (spider_opens_doors != 2)) to_chat(src, "The door's motors resist your efforts to force it.") else if(!spider_opens_doors) to_chat(src, "Your type of spider is not strong enough to force open doors.") else - visible_message("[src] pries open the door!") + visible_message("[src] forces the door!") playsound(src.loc, "sparks", 100, 1) - D.open(1) - return 1 + if(D.density) + D.open(TRUE) + else + D.close(TRUE) + return TRUE + + +/mob/living/simple_animal/hostile/poison/terror_spider/Stat() + ..() + // Determines what shows in the "Status" tab for player-controlled spiders. Used to help players understand spider health regeneration mechanics. + // Uses because the status panel does NOT accept . + if(statpanel("Status") && ckey && stat == CONSCIOUS) + if(degenerate) + stat(null, "Hivemind Connection Severed! Dying...") // color=red + return + if(health != maxHealth) + var/hp_points_per_second = 0 + var/ltext = "FAST" + var/lcolor = "#fcba03" // orange + var/secs_per_tick = (SSmobs.wait / 10) // This uses SSmobs.wait because it must use the same frequency as mobs are processed + if(regen_points < (regen_points_per_hp * 2)) + // Slow regen speed: using regen_points as we get them. Figure out regen_points/sec, then convert that to hp/sec. + var/regen_points_per_second = (regen_points_per_tick / secs_per_tick) + hp_points_per_second = (regen_points_per_second / regen_points_per_hp) + ltext = "SLOW (HUNGRY!)" + lcolor = "#eb4034" // red + else + // Fast regen speed: healing at full 1 hp / tick rate. Just divide 1hp/tick by seconds/tick to get healing/sec. + hp_points_per_second = 1 / secs_per_tick + if(hp_points_per_second > 0) + var/pc_of_max_per_second = round(((hp_points_per_second / maxHealth) * 100), 0.1) + stat(null, "Regeneration: [ltext]: [num2text(pc_of_max_per_second)]% of health per second") + + diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index ae8cbd2d7ca..6acfb92a515 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -675,4 +675,3 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM return FALSE //This is the only case someone should actually be completely blocked from antag rolling as well return TRUE -#define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider)) diff --git a/code/modules/surgery/organs/parasites.dm b/code/modules/surgery/organs/parasites.dm index b61a7c429bc..b9afa20b7d2 100644 --- a/code/modules/surgery/organs/parasites.dm +++ b/code/modules/surgery/organs/parasites.dm @@ -54,8 +54,6 @@ var/eggs_hatched = 0 // num of hatch events completed var/awaymission_checked = FALSE var/awaymission_infection = FALSE // TRUE if infection occurred inside gateway - var/list/types_basic = list(/mob/living/simple_animal/hostile/poison/terror_spider/red, /mob/living/simple_animal/hostile/poison/terror_spider/gray) - var/list/types_adv = list(/mob/living/simple_animal/hostile/poison/terror_spider/red, /mob/living/simple_animal/hostile/poison/terror_spider/gray, /mob/living/simple_animal/hostile/poison/terror_spider/green) /obj/item/organ/internal/body_egg/terror_eggs/on_life() @@ -104,12 +102,16 @@ var/infection_completed = FALSE var/obj/structure/spider/spiderling/terror_spiderling/S = new(get_turf(owner)) switch(eggs_hatched) - if(0) // First spiderling - S.grow_as = pick(types_basic) - if(1) // Second - S.grow_as = pick(types_adv) - if(2) // Last - S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/princess + if(0) // 1st spiderling + S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/gray + if(1) // 2nd + S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/red + if(2) // 3rd + S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/brown + if(3) // 4th + S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/green + if(4) // 5th + S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/green infection_completed = TRUE S.immediate_ventcrawl = TRUE eggs_hatched++