mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-20 19:44:09 +01:00
Converts giant spiders to basic mobs (#29796)
* Giant spiders initial * Ling spiders, araneous, conversion to basic * Removed some extra * Fixes cling spiders * Linters * Do_afters * Nurse AI works now * Cling spider AI * Forgot an element * Updatepaths * New Linters * AI New linters * Fixed action buttons * No longer wraps spiderlings, adds a movement delay * Fixes the sarge hatching from eggs, increases action cooldown on eggs * Improved cling spider AI, improved insect random speech --------- Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#define IDLE_AGGRESSIVE 0
|
||||
#define FOLLOW_AGGRESSIVE 1
|
||||
#define FOLLOW_RETALIATE 2
|
||||
#define FOLLOW_AGGRESSIVE 0
|
||||
#define FOLLOW_RETALIATE 1
|
||||
#define IDLE_AGGRESSIVE 2
|
||||
#define IDLE_RETALIATE 3
|
||||
|
||||
/datum/action/changeling/spiders
|
||||
@@ -30,9 +30,11 @@
|
||||
if(do_after(user, 4 SECONDS, FALSE, target = user, hidden = TRUE)) // Takes 4 seconds to spawn a spider
|
||||
spider_counter++
|
||||
user.visible_message("<span class='danger'>[user] vomits up an arachnid!</span>")
|
||||
var/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/S = new(user.loc)
|
||||
var/mob/living/basic/giant_spider/hunter/infestation_spider/S = new(user.loc)
|
||||
S.owner_UID = user.UID()
|
||||
S.faction |= list("spiders", "\ref[owner]") // Makes them friendly only to the owner & other spiders
|
||||
S.ai_controller.set_blackboard_key(BB_CURRENT_PET_TARGET, user)
|
||||
S.ai_controller.set_blackboard_key(BB_CHANGELING_SPIDER_ORDER, IDLE_AGGRESSIVE)
|
||||
S.befriend(user)
|
||||
SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("[name]"))
|
||||
is_operating = FALSE
|
||||
return TRUE
|
||||
@@ -40,26 +42,28 @@
|
||||
return FALSE
|
||||
|
||||
/// Child of giant_spider because this should do everything the spider does and more
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider
|
||||
/mob/living/basic/giant_spider/hunter/infestation_spider
|
||||
/// References to the owner changeling
|
||||
var/mob/owner_UID
|
||||
/// Handles the spider's behavior
|
||||
var/current_order = IDLE_AGGRESSIVE
|
||||
var/list/enemies = list()
|
||||
sentience_type = SENTIENCE_OTHER
|
||||
venom_per_bite = 3
|
||||
speak_chance = 0
|
||||
wander = 0
|
||||
gold_core_spawnable = NO_SPAWN
|
||||
ai_controller = /datum/ai_controller/basic_controller/giant_spider/changeling
|
||||
/// To check and gib the spider when dead, then remove only one of the counter for the changeling owner
|
||||
var/gibbed = FALSE
|
||||
|
||||
/mob/living/basic/giant_spider/hunter/infestation_spider/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/ai_retaliate)
|
||||
|
||||
//These two below are needed to both gib the spider always, and even if it was gibbed only remove 1 from the counter of spider_counter instead of death's gib calling death again and removing 2
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/gib()
|
||||
/mob/living/basic/giant_spider/hunter/infestation_spider/gib()
|
||||
gibbed = TRUE
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/death(gibbed)
|
||||
/mob/living/basic/giant_spider/hunter/infestation_spider/death(gibbed)
|
||||
var/mob/owner_mob = locateUID(owner_UID)
|
||||
if(!ismob(owner_mob))
|
||||
return ..(TRUE)
|
||||
@@ -71,7 +75,7 @@
|
||||
gib()
|
||||
return ..(TRUE)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/examine(mob/user)
|
||||
/mob/living/basic/giant_spider/hunter/infestation_spider/examine(mob/user)
|
||||
. = ..()
|
||||
if(user.UID() != owner_UID)
|
||||
return
|
||||
@@ -85,113 +89,37 @@
|
||||
if(IDLE_RETALIATE)
|
||||
. += "<span class='notice'>The giant spider will remain idle and calm, only attacking if it is attacked.</span>"
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/AltShiftClick(mob/user)
|
||||
/mob/living/basic/giant_spider/hunter/infestation_spider/AltShiftClick(mob/user)
|
||||
. = ..()
|
||||
if(user.UID() != owner_UID)
|
||||
return
|
||||
var/list/possible_orders = list("Escort", "Escort Aggressively", "Idle", "Idle Aggressively")
|
||||
var/new_order = tgui_input_list(user, "How do you want your spiders to behave?", "Spider Orders", possible_orders)
|
||||
switch(new_order)
|
||||
if("Escort")
|
||||
current_order = FOLLOW_RETALIATE
|
||||
if("Escort Aggressively")
|
||||
current_order = FOLLOW_AGGRESSIVE
|
||||
if("Idle")
|
||||
current_order = IDLE_RETALIATE
|
||||
if("Idle Aggressively")
|
||||
current_order = IDLE_AGGRESSIVE
|
||||
spider_order(user)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/proc/spider_order(mob/user)
|
||||
enemies = list()
|
||||
/mob/living/basic/giant_spider/hunter/infestation_spider/proc/spider_order(mob/user)
|
||||
switch(current_order)
|
||||
if(IDLE_AGGRESSIVE)
|
||||
if(FOLLOW_AGGRESSIVE)
|
||||
to_chat(user, "<span class='notice'>We order the giant spider to follow us but attack anyone on sight.</span>")
|
||||
current_order = FOLLOW_AGGRESSIVE
|
||||
if(FOLLOW_AGGRESSIVE)
|
||||
ai_controller.set_blackboard_key(BB_CHANGELING_SPIDER_ORDER, FOLLOW_AGGRESSIVE)
|
||||
if(FOLLOW_RETALIATE)
|
||||
to_chat(user, "<span class='notice'>We order the giant spider to follow us and to remain calm, only attacking if it is attacked.</span>")
|
||||
current_order = FOLLOW_RETALIATE
|
||||
if(FOLLOW_RETALIATE)
|
||||
ai_controller.set_blackboard_key(BB_CHANGELING_SPIDER_ORDER, FOLLOW_RETALIATE)
|
||||
if(IDLE_RETALIATE)
|
||||
to_chat(user, "<span class='notice'>We order the giant spider to remain idle and calm, only attacking if it is attacked.</span>")
|
||||
current_order = IDLE_RETALIATE
|
||||
if(IDLE_RETALIATE)
|
||||
to_chat(user, "<span class='notice'>We order the giant spider to remain idle, but ready to attack anyone on sight.</span>")
|
||||
current_order = IDLE_AGGRESSIVE
|
||||
handle_automated_movement()
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/handle_automated_movement() //Hacky and ugly.
|
||||
. = ..()
|
||||
var/list/around = view(src, vision_range)
|
||||
switch(current_order)
|
||||
ai_controller.set_blackboard_key(BB_CHANGELING_SPIDER_ORDER, IDLE_RETALIATE)
|
||||
if(IDLE_AGGRESSIVE)
|
||||
Find_Enemies(around)
|
||||
GLOB.move_manager.stop_looping(src)
|
||||
if(FOLLOW_AGGRESSIVE)
|
||||
Find_Enemies(around)
|
||||
for(var/mob/living/carbon/C in around)
|
||||
if(!faction_check_mob(C))
|
||||
continue
|
||||
if(Adjacent(C))
|
||||
return TRUE
|
||||
Goto(C, 0.5 SECONDS, 1)
|
||||
if(FOLLOW_RETALIATE)
|
||||
for(var/mob/living/carbon/C in around)
|
||||
if(!faction_check_mob(C))
|
||||
continue
|
||||
if(Adjacent(C))
|
||||
return TRUE
|
||||
Goto(C, 0.5 SECONDS, 1)
|
||||
if(IDLE_RETALIATE)
|
||||
GLOB.move_manager.stop_looping(src)
|
||||
|
||||
for(var/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/H in around)
|
||||
if(faction_check_mob(H) && !attack_same && !H.attack_same)
|
||||
H.enemies |= enemies
|
||||
|
||||
return TRUE
|
||||
|
||||
// Bellow is the way the spiders react and retaliate when in an idle/aggresive mode.
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/ListTargets()
|
||||
if(!length(enemies))
|
||||
return list()
|
||||
var/list/see = ..()
|
||||
see &= enemies // Remove all entries that aren't in enemies
|
||||
return see
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/proc/Find_Enemies(around)
|
||||
enemies = list() // Reset enemies list, only focus on the ones around you, spiders don't have grudges
|
||||
for(var/mob/living/A in around)
|
||||
if(A == src)
|
||||
continue
|
||||
if(!isliving(A))
|
||||
continue
|
||||
var/mob/living/M = A
|
||||
if(!faction_check_mob(M))
|
||||
enemies |= M
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/attack_by(obj/item/W, mob/living/user, params)
|
||||
if(..())
|
||||
return FINISH_ATTACK
|
||||
if(W.force == 0)
|
||||
return FINISH_ATTACK
|
||||
if(!faction_check_mob(user))
|
||||
enemies |= user
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/bullet_act(obj/item/projectile/P)
|
||||
. = ..()
|
||||
if(!faction_check_mob(P.firer))
|
||||
enemies |= P.firer
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/attack_alien(mob/living/carbon/alien/user)
|
||||
. = ..()
|
||||
if(user.a_intent == INTENT_HELP)
|
||||
return
|
||||
if(!faction_check_mob(user))
|
||||
enemies |= user
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/attack_animal(mob/living/simple_animal/M)
|
||||
. = ..()
|
||||
if(M.a_intent == INTENT_HELP)
|
||||
return
|
||||
if(!faction_check_mob(M))
|
||||
enemies |= M
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/attack_hand(mob/living/carbon/human/H)
|
||||
. = ..()
|
||||
if(H.a_intent == INTENT_HELP)
|
||||
return
|
||||
if(!faction_check_mob(H))
|
||||
enemies |= H
|
||||
to_chat(user, "<span class='notice'>We order the giant spider to remain idle, but ready to attack anyone on sight.</span>")
|
||||
ai_controller.set_blackboard_key(BB_CHANGELING_SPIDER_ORDER, IDLE_AGGRESSIVE)
|
||||
|
||||
#undef IDLE_AGGRESSIVE
|
||||
#undef FOLLOW_AGGRESSIVE
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
var/obj/vent = pick_n_take(vents)
|
||||
var/obj/structure/spider/spiderling/S = new(vent.loc)
|
||||
if(prob(66))
|
||||
S.grow_as = /mob/living/simple_animal/hostile/poison/giant_spider/nurse
|
||||
S.grow_as = /mob/living/basic/giant_spider/nurse
|
||||
spawncount--
|
||||
successSpawn = TRUE
|
||||
|
||||
@@ -501,7 +501,7 @@
|
||||
visible_message("<span class='warning'>The pests seem to behave oddly...</span>")
|
||||
for(var/i in 1 to 3)
|
||||
var/obj/structure/spider/spiderling/S = new(get_turf(src))
|
||||
S.grow_as = /mob/living/simple_animal/hostile/poison/giant_spider/hunter
|
||||
S.grow_as = /mob/living/basic/giant_spider/hunter
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The pests seem to behave oddly, but quickly settle down...</span>")
|
||||
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
// basic spider mob, these generally guard nests
|
||||
/mob/living/basic/giant_spider
|
||||
name = "giant spider"
|
||||
desc = "Furry and black, it makes you shudder to look at it. This one has deep red eyes."
|
||||
icon_state = "guard"
|
||||
icon_living = "guard"
|
||||
icon_dead = "guard_dead"
|
||||
mob_biotypes = MOB_ORGANIC | MOB_BUG
|
||||
speak_emote = list("chitters")
|
||||
see_in_dark = 8
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
|
||||
step_type = FOOTSTEP_MOB_CLAW
|
||||
a_intent = INTENT_HARM
|
||||
butcher_results = list(/obj/item/food/monstermeat/spidermeat = 2, /obj/item/food/monstermeat/spiderleg = 8)
|
||||
response_help_continuous = "pets"
|
||||
response_help_simple = "pets"
|
||||
response_disarm_continuous = "gently pushes aside"
|
||||
response_disarm_simple = "gently pushes aside"
|
||||
maxHealth = 200
|
||||
health = 200
|
||||
obj_damage = 60
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 20
|
||||
melee_attack_cooldown_min = 1.5 SECONDS
|
||||
melee_attack_cooldown_max = 2.5 SECONDS
|
||||
unsuitable_heat_damage = 20
|
||||
unsuitable_cold_damage = 20
|
||||
faction = list("spiders")
|
||||
pass_flags = PASSTABLE
|
||||
attack_verb_simple = "bite"
|
||||
attack_verb_continuous = "bites"
|
||||
attack_sound = 'sound/weapons/bite.ogg'
|
||||
gold_core_spawnable = HOSTILE_SPAWN
|
||||
contains_xeno_organ = TRUE
|
||||
surgery_container = /datum/xenobiology_surgery_container/spider
|
||||
ai_controller = /datum/ai_controller/basic_controller/giant_spider
|
||||
/// How much venom is injected per bite into the poor victim
|
||||
var/venom_per_bite = 0
|
||||
/// Actions to grant on Initialize
|
||||
var/list/innate_actions = list(/datum/action/innate/web_giant_spider = BB_SPIDER_WEB_ACTION)
|
||||
|
||||
/mob/living/basic/giant_spider/Initialize(mapload)
|
||||
. = ..()
|
||||
grant_actions_by_list(innate_actions)
|
||||
|
||||
/mob/living/basic/giant_spider/Destroy()
|
||||
for(var/datum/action/innate/web_giant_spider/web_action in actions)
|
||||
web_action.Remove(src)
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/giant_spider/melee_attack(atom/target, list/modifiers, ignore_cooldown)
|
||||
. = ..()
|
||||
if(. && venom_per_bite > 0 && iscarbon(target) && (!client || a_intent == INTENT_HARM))
|
||||
var/mob/living/carbon/C = target
|
||||
var/inject_target = pick("chest", "head")
|
||||
if(C.can_inject(null, FALSE, inject_target, FALSE))
|
||||
C.reagents.add_reagent("spidertoxin", venom_per_bite)
|
||||
|
||||
/mob/living/basic/giant_spider/get_spacemove_backup(movement_dir)
|
||||
. = ..()
|
||||
// If we don't find any normal thing to use, attempt to use any nearby spider structure instead.
|
||||
if(!.)
|
||||
for(var/obj/structure/spider/S in range(1, get_turf(src)))
|
||||
return S
|
||||
|
||||
/mob/living/basic/giant_spider/proc/create_web()
|
||||
var/T = loc
|
||||
visible_message("<span class='notice'>[src] begins to secrete a sticky substance.</span>")
|
||||
if(!do_after_once(src, 4 SECONDS, target = loc, attempt_cancel_message = "You stop spinning a web.", interaction_key = "spider_web_create"))
|
||||
return
|
||||
new /obj/structure/spider/stickyweb(T)
|
||||
|
||||
// Nursemaids - these create webs and eggs
|
||||
/mob/living/basic/giant_spider/nurse
|
||||
desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes."
|
||||
icon_state = "nurse"
|
||||
icon_living = "nurse"
|
||||
icon_dead = "nurse_dead"
|
||||
butcher_results = list(/obj/item/food/monstermeat/spidermeat = 2, /obj/item/food/monstermeat/spiderleg = 8, /obj/item/food/monstermeat/spidereggs = 4)
|
||||
ai_controller = /datum/ai_controller/basic_controller/giant_spider/nurse
|
||||
maxHealth = 40
|
||||
health = 40
|
||||
melee_damage_lower = 5
|
||||
melee_damage_upper = 10
|
||||
gold_core_spawnable = NO_SPAWN
|
||||
venom_per_bite = 30
|
||||
innate_actions = list(
|
||||
/datum/action/innate/web_giant_spider = BB_SPIDER_WEB_ACTION,
|
||||
/datum/action/innate/wrap_giant_spider = BB_SPIDER_WRAP_ACTION,
|
||||
/datum/action/innate/lay_eggs_giant_spider = BB_SPIDER_EGG_LAYING_ACTION
|
||||
)
|
||||
/// How much have we eaten
|
||||
var/fed = 0
|
||||
|
||||
/mob/living/basic/giant_spider/nurse/proc/wrap_target()
|
||||
var/atom/cocoon_target
|
||||
if(!client)
|
||||
cocoon_target = ai_controller.blackboard[BB_SPIDER_WRAP_TARGET]
|
||||
if(!cocoon_target && client)
|
||||
var/list/choices = list()
|
||||
for(var/mob/living/L in view(1, src))
|
||||
if(L == src)
|
||||
continue
|
||||
if(L.stat != DEAD)
|
||||
continue
|
||||
if(istype(L, /mob/living/basic/giant_spider))
|
||||
continue
|
||||
if(Adjacent(L))
|
||||
choices += L
|
||||
for(var/obj/O in get_turf(src))
|
||||
if(O.anchored)
|
||||
continue
|
||||
if(!(isitem(O) || isstructure(O) || ismachinery(O)))
|
||||
continue
|
||||
if(Adjacent(O))
|
||||
choices += O
|
||||
if(length(choices))
|
||||
cocoon_target = tgui_input_list(src, "What do you wish to cocoon?", "Cocoon Wrapping", choices)
|
||||
else
|
||||
to_chat(src, "<span class='warning'>No suitable dead prey or wrappable objects found nearby.")
|
||||
return
|
||||
|
||||
if(cocoon_target)
|
||||
visible_message("<span class='notice'>[src] begins to secrete a sticky substance around [cocoon_target].</span>")
|
||||
if(!do_after_once(src, 5 SECONDS, target = cocoon_target, attempt_cancel_message = "You stop wrapping [cocoon_target].", interaction_key = "spider_web_wrap"))
|
||||
return
|
||||
if(cocoon_target && isturf(cocoon_target.loc) && get_dist(src, cocoon_target) <= 1)
|
||||
var/obj/structure/spider/cocoon/C = new(cocoon_target.loc)
|
||||
var/large_cocoon = FALSE
|
||||
C.pixel_x = cocoon_target.pixel_x
|
||||
C.pixel_y = cocoon_target.pixel_y
|
||||
for(var/obj/item/I in C.loc)
|
||||
I.loc = C
|
||||
for(var/obj/structure/S in C.loc)
|
||||
if(!S.anchored)
|
||||
S.loc = C
|
||||
large_cocoon = TRUE
|
||||
for(var/obj/machinery/M in C.loc)
|
||||
if(!M.anchored)
|
||||
M.loc = C
|
||||
large_cocoon = TRUE
|
||||
for(var/mob/living/L in C.loc)
|
||||
if(istype(L, /mob/living/basic/giant_spider))
|
||||
continue
|
||||
if(L.stat != DEAD)
|
||||
continue
|
||||
large_cocoon = TRUE
|
||||
L.loc = C
|
||||
C.pixel_x = L.pixel_x
|
||||
C.pixel_y = L.pixel_y
|
||||
fed++
|
||||
visible_message("<span class='danger'>[src] sticks a proboscis into [L] and sucks a viscous substance out.</span>")
|
||||
|
||||
break
|
||||
if(large_cocoon)
|
||||
C.icon_state = pick("cocoon_large1", "cocoon_large2", "cocoon_large3")
|
||||
|
||||
/mob/living/basic/giant_spider/nurse/proc/lay_spider_eggs()
|
||||
var/obj/structure/spider/eggcluster/E = locate() in get_turf(src)
|
||||
if(E)
|
||||
to_chat(src, "<span class='notice'>There is already a cluster of eggs here!</span>")
|
||||
return
|
||||
if(!fed)
|
||||
to_chat(src, "<span class='warning'>You are too hungry to do this!</span>")
|
||||
return
|
||||
visible_message("<span class='notice'>[src] begins to lay a cluster of eggs.</span>")
|
||||
if(!do_after_once(src, 4 SECONDS, target = loc, attempt_cancel_message = "You stop laying eggs.", interaction_key = "spider_egg_lay"))
|
||||
return
|
||||
var/obj/structure/spider/eggcluster/C = new /obj/structure/spider/eggcluster(loc)
|
||||
C.faction = faction.Copy()
|
||||
C.master_commander = master_commander
|
||||
if(ckey)
|
||||
C.player_spiders = TRUE
|
||||
fed--
|
||||
|
||||
/mob/living/basic/giant_spider/nurse/proc/find_cocoon_target()
|
||||
// Prioritize food
|
||||
var/list/food = list()
|
||||
var/list/can_see = view(src, 10)
|
||||
for(var/mob/living/C in can_see)
|
||||
if(C.stat && !istype(C, /mob/living/basic/giant_spider) && !C.anchored)
|
||||
food += C
|
||||
if(length(food))
|
||||
return pick(food)
|
||||
var/list/objects = list()
|
||||
for(var/obj/O in can_see)
|
||||
if(O.anchored)
|
||||
continue
|
||||
|
||||
if(isitem(O) || isstructure(O) || ismachinery(O))
|
||||
objects += O
|
||||
if(length(objects))
|
||||
return pick(objects)
|
||||
return
|
||||
|
||||
// Hunters have the most poison and move the fastest, so they can find prey
|
||||
/mob/living/basic/giant_spider/hunter
|
||||
desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes."
|
||||
icon_state = "hunter"
|
||||
icon_living = "hunter"
|
||||
icon_dead = "hunter_dead"
|
||||
maxHealth = 120
|
||||
health = 120
|
||||
melee_damage_lower = 10
|
||||
venom_per_bite = 10
|
||||
|
||||
/mob/living/basic/giant_spider/araneus
|
||||
name = "Sergeant Araneus"
|
||||
real_name = "Sergeant Araneus"
|
||||
desc = "A fierce companion for any person of power, this spider has been carefully trained by Nanotrasen specialists. Its beady, staring eyes send shivers down your spine."
|
||||
faction = list("spiders")
|
||||
maxHealth = 250
|
||||
health = 250
|
||||
atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 2, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
|
||||
gender = FEMALE
|
||||
ai_controller = /datum/ai_controller/basic_controller/giant_spider/retaliate
|
||||
|
||||
/mob/living/basic/giant_spider/araneus/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/ai_retaliate)
|
||||
AddElement(/datum/element/pet_bonus, "chitters!")
|
||||
@@ -0,0 +1,26 @@
|
||||
/datum/action/innate/web_giant_spider
|
||||
name = "Lay Web"
|
||||
button_icon = 'icons/effects/effects.dmi'
|
||||
button_icon_state = "stickyweb1"
|
||||
|
||||
/datum/action/innate/web_giant_spider/Activate()
|
||||
var/mob/living/basic/giant_spider/user = owner
|
||||
user.create_web()
|
||||
|
||||
/datum/action/innate/wrap_giant_spider
|
||||
name = "Wrap"
|
||||
button_icon = 'icons/effects/effects.dmi'
|
||||
button_icon_state = "cocoon_large1"
|
||||
|
||||
/datum/action/innate/wrap_giant_spider/Activate()
|
||||
var/mob/living/basic/giant_spider/nurse/user = owner
|
||||
user.wrap_target()
|
||||
|
||||
/datum/action/innate/lay_eggs_giant_spider
|
||||
name = "Lay Eggs"
|
||||
button_icon = 'icons/effects/effects.dmi'
|
||||
button_icon_state = "eggs"
|
||||
|
||||
/datum/action/innate/lay_eggs_giant_spider/Activate()
|
||||
var/mob/living/basic/giant_spider/nurse/user = owner
|
||||
user.lay_spider_eggs()
|
||||
@@ -0,0 +1,292 @@
|
||||
/// Attacks people it can see, spins webs if it can't see anything to attack.
|
||||
/datum/ai_controller/basic_controller/giant_spider
|
||||
blackboard = list(
|
||||
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
||||
)
|
||||
ai_movement = /datum/ai_movement/basic_avoidance
|
||||
movement_delay = 1 SECONDS
|
||||
idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking
|
||||
|
||||
ai_traits = AI_FLAG_STOP_MOVING_WHEN_PULLED | AI_FLAG_PAUSE_DURING_DO_AFTER
|
||||
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/random_speech/insect,
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree,
|
||||
/datum/ai_planning_subtree/find_unwebbed_turf,
|
||||
/datum/ai_planning_subtree/spin_web,
|
||||
)
|
||||
|
||||
/datum/ai_controller/basic_controller/giant_spider/nurse
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/random_speech/insect,
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree,
|
||||
/datum/ai_planning_subtree/lay_eggs,
|
||||
/datum/ai_planning_subtree/find_unwrapped_target,
|
||||
/datum/ai_planning_subtree/find_unwebbed_turf,
|
||||
/datum/ai_planning_subtree/wrap_target,
|
||||
/datum/ai_planning_subtree/spin_web,
|
||||
)
|
||||
|
||||
/datum/ai_controller/basic_controller/giant_spider/changeling
|
||||
blackboard = list(
|
||||
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends,
|
||||
)
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/simple_find_target/cling_spider,
|
||||
/datum/ai_planning_subtree/target_retaliate,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree,
|
||||
/datum/ai_planning_subtree/cling_spider_follow,
|
||||
)
|
||||
|
||||
/// Used by Araneus, who only attacks those who attack first. He is house-trained and will not web up the HoS office.
|
||||
/datum/ai_controller/basic_controller/giant_spider/retaliate
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/random_speech/insect,
|
||||
/datum/ai_planning_subtree/target_retaliate,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree,
|
||||
)
|
||||
|
||||
/datum/ai_planning_subtree/random_speech/insect
|
||||
speech_chance = 2
|
||||
sound = list('sound/creatures/chitter.ogg')
|
||||
emote_hear = list("chitters.")
|
||||
|
||||
/// Search for a nearby location to put webs on
|
||||
/datum/ai_planning_subtree/find_unwebbed_turf
|
||||
|
||||
/datum/ai_planning_subtree/find_unwebbed_turf/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
controller.queue_behavior(/datum/ai_behavior/find_unwebbed_turf)
|
||||
|
||||
/// Find an unwebbed nearby turf and store it
|
||||
/datum/ai_behavior/find_unwebbed_turf
|
||||
action_cooldown = 5 SECONDS
|
||||
/// Where do we store the target data
|
||||
var/target_key = BB_SPIDER_WEB_TARGET
|
||||
/// How far do we look for unwebbed turfs?
|
||||
var/scan_range = 3
|
||||
|
||||
/datum/ai_behavior/find_unwebbed_turf/perform(seconds_per_tick, datum/ai_controller/controller)
|
||||
var/mob/living/spider = controller.pawn
|
||||
var/atom/current_target = controller.blackboard[target_key]
|
||||
if(current_target && !(locate(/obj/structure/spider/stickyweb) in current_target))
|
||||
// Already got a target
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
controller.clear_blackboard_key(target_key)
|
||||
var/turf/our_turf = get_turf(spider)
|
||||
if(is_valid_web_turf(our_turf, spider))
|
||||
controller.set_blackboard_key(target_key, our_turf)
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
var/list/turfs_by_range = list()
|
||||
for(var/i in 1 to scan_range)
|
||||
turfs_by_range["[i]"] = list()
|
||||
for(var/turf/turf_in_view in oview(scan_range, our_turf))
|
||||
if(!is_valid_web_turf(turf_in_view, spider))
|
||||
continue
|
||||
turfs_by_range["[get_dist(our_turf, turf_in_view)]"] += turf_in_view
|
||||
|
||||
var/list/final_turfs
|
||||
for(var/list/turf_list as anything in turfs_by_range)
|
||||
if(length(turfs_by_range[turf_list]))
|
||||
final_turfs = turfs_by_range[turf_list]
|
||||
break
|
||||
if(!length(final_turfs))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
controller.set_blackboard_key(target_key, pick(final_turfs))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/datum/ai_behavior/find_unwebbed_turf/proc/is_valid_web_turf(turf/target_turf, mob/living/spider)
|
||||
if(locate(/obj/structure/spider/stickyweb) in target_turf)
|
||||
return FALSE
|
||||
if(HAS_TRAIT(target_turf, TRAIT_SPINNING_WEB_TURF))
|
||||
return FALSE
|
||||
return !target_turf.is_blocked_turf(source_atom = spider)
|
||||
|
||||
/// Run the spin web behaviour if we have an ability to use for it
|
||||
/datum/ai_planning_subtree/spin_web
|
||||
/// Key where the web spinning action is stored
|
||||
var/action_key = BB_SPIDER_WEB_ACTION
|
||||
/// Key where the target turf is stored
|
||||
var/target_key = BB_SPIDER_WEB_TARGET
|
||||
|
||||
/datum/ai_planning_subtree/spin_web/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(controller.blackboard_key_exists(action_key) && controller.blackboard_key_exists(target_key))
|
||||
controller.queue_behavior(/datum/ai_behavior/spin_web, action_key, target_key)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/// Move to an unwebbed nearby turf and web it up
|
||||
/datum/ai_behavior/spin_web
|
||||
action_cooldown = 10 SECONDS // We don't want them doing this too quickly
|
||||
required_distance = 0
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
|
||||
/datum/ai_behavior/spin_web/setup(datum/ai_controller/controller, action_key, target_key)
|
||||
var/datum/action/innate/web_giant_spider/web_action = controller.blackboard[action_key]
|
||||
var/turf/target_turf = controller.blackboard[target_key]
|
||||
if(!web_action || !target_turf)
|
||||
return FALSE
|
||||
|
||||
set_movement_target(controller, target_turf)
|
||||
return ..()
|
||||
|
||||
/datum/ai_behavior/spin_web/perform(seconds_per_tick, datum/ai_controller/controller, action_key, target_key)
|
||||
var/datum/action/innate/web_giant_spider/web_action = controller.blackboard[action_key]
|
||||
if(web_action?.Trigger())
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
/datum/ai_behavior/spin_web/finish_action(datum/ai_controller/controller, succeeded, action_key, target_key)
|
||||
controller.clear_blackboard_key(target_key)
|
||||
return ..()
|
||||
|
||||
/// Search for a nearby location to put webs on
|
||||
/datum/ai_planning_subtree/find_unwrapped_target
|
||||
|
||||
/datum/ai_planning_subtree/find_unwrapped_target/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
controller.queue_behavior(/datum/ai_behavior/find_unwrapped_target)
|
||||
|
||||
/// Find an unwrapped target and store it
|
||||
/datum/ai_behavior/find_unwrapped_target
|
||||
action_cooldown = 5 SECONDS
|
||||
/// Where do we store the target data
|
||||
var/target_key = BB_SPIDER_WRAP_TARGET
|
||||
/// How far do we look for unwebbed items?
|
||||
var/scan_range = 3
|
||||
|
||||
/datum/ai_behavior/find_unwrapped_target/perform(seconds_per_tick, datum/ai_controller/controller)
|
||||
var/mob/living/spider = controller.pawn
|
||||
var/atom/current_target = controller.blackboard[target_key]
|
||||
if(current_target)
|
||||
// Already got a target
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
// Prioritize food
|
||||
var/list/food = list()
|
||||
var/list/can_see = view(scan_range, spider)
|
||||
for(var/mob/living/C in can_see)
|
||||
if(C.stat && !istype(C, /mob/living/basic/giant_spider) && !C.anchored)
|
||||
food += C
|
||||
if(length(food))
|
||||
controller.set_blackboard_key(target_key, pick(food))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
var/list/objects = list()
|
||||
for(var/obj/O in can_see)
|
||||
if(O.anchored)
|
||||
continue
|
||||
if(istype(O, /obj/structure/spider))
|
||||
continue // Don't web up spider structures or spiderlings
|
||||
if(isitem(O) || isstructure(O) || ismachinery(O))
|
||||
objects += O
|
||||
if(length(objects))
|
||||
controller.set_blackboard_key(target_key, pick(objects))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
/// Run the wrap behaviour if we have an ability to use for it
|
||||
/datum/ai_planning_subtree/wrap_target
|
||||
/// Key where the web spinning action is stored
|
||||
var/action_key = BB_SPIDER_WRAP_ACTION
|
||||
/// Key where the target is stored
|
||||
var/target_key = BB_SPIDER_WRAP_TARGET
|
||||
|
||||
/datum/ai_planning_subtree/wrap_target/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(controller.blackboard_key_exists(action_key) && controller.blackboard_key_exists(target_key))
|
||||
controller.queue_behavior(/datum/ai_behavior/wrap_target, action_key, target_key)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/// Move to an unwrapped item and wrap it
|
||||
/datum/ai_behavior/wrap_target
|
||||
action_cooldown = 15 SECONDS // We don't want them doing this too quickly
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
|
||||
/datum/ai_behavior/wrap_target/setup(datum/ai_controller/controller, action_key, target_key)
|
||||
var/datum/action/innate/wrap_giant_spider/wrap_action = controller.blackboard[action_key]
|
||||
var/atom/target = controller.blackboard[target_key]
|
||||
var/turf/target_turf = get_turf(target)
|
||||
if(!wrap_action || !target_turf)
|
||||
return FALSE
|
||||
|
||||
set_movement_target(controller, target_turf)
|
||||
return ..()
|
||||
|
||||
/datum/ai_behavior/wrap_target/perform(seconds_per_tick, datum/ai_controller/controller, action_key, target_key)
|
||||
var/datum/action/innate/wrap_giant_spider/wrap_action = controller.blackboard[action_key]
|
||||
if(wrap_action?.Trigger())
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
/datum/ai_behavior/wrap_target/finish_action(datum/ai_controller/controller, succeeded, action_key, target_key)
|
||||
controller.clear_blackboard_key(target_key)
|
||||
return ..()
|
||||
|
||||
/// Run the egg laying behavior
|
||||
/datum/ai_planning_subtree/lay_eggs
|
||||
/// Key where the egg laying action is stored
|
||||
var/action_key = BB_SPIDER_EGG_LAYING_ACTION
|
||||
|
||||
/datum/ai_planning_subtree/lay_eggs/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
var/mob/living/basic/giant_spider/nurse/spider = controller.pawn
|
||||
if(controller.blackboard_key_exists(action_key) && spider.fed > 0)
|
||||
controller.queue_behavior(/datum/ai_behavior/lay_eggs, action_key)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/// Attempt to lay eggs if we're fed
|
||||
/datum/ai_behavior/lay_eggs
|
||||
action_cooldown = 2 MINUTES
|
||||
behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
|
||||
/datum/ai_behavior/lay_eggs/setup(datum/ai_controller/controller, action_key)
|
||||
var/datum/action/innate/lay_eggs_giant_spider/egg_action = controller.blackboard[action_key]
|
||||
if(!egg_action)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/ai_behavior/lay_eggs/perform(seconds_per_tick, datum/ai_controller/controller, action_key)
|
||||
var/datum/action/innate/lay_eggs_giant_spider/egg_action = controller.blackboard[action_key]
|
||||
if(egg_action?.Trigger())
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
/// Spider only attacks when it has the valid order.
|
||||
/datum/ai_planning_subtree/simple_find_target/cling_spider
|
||||
|
||||
/datum/ai_planning_subtree/simple_find_target/cling_spider/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(controller.blackboard[BB_CHANGELING_SPIDER_ORDER] == 1 || controller.blackboard[BB_CHANGELING_SPIDER_ORDER] == 3)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/// Spider follows who created it
|
||||
/datum/ai_planning_subtree/cling_spider_follow
|
||||
/// Key where the owner is stored
|
||||
var/target_key = BB_CURRENT_PET_TARGET
|
||||
|
||||
/datum/ai_planning_subtree/cling_spider_follow/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(controller.blackboard_key_exists(target_key) && controller.blackboard[BB_CHANGELING_SPIDER_ORDER] < 2)
|
||||
controller.queue_behavior(/datum/ai_behavior/cling_spider_follow, target_key)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/// Attempt to follow the owner
|
||||
/datum/ai_behavior/cling_spider_follow
|
||||
required_distance = 0
|
||||
behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM
|
||||
|
||||
/datum/ai_behavior/cling_spider_follow/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/atom/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target))
|
||||
return FALSE
|
||||
set_movement_target(controller, target)
|
||||
|
||||
/datum/ai_behavior/cling_spider_follow/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
||||
var/atom/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
return AI_BEHAVIOR_DELAY
|
||||
@@ -1,283 +0,0 @@
|
||||
|
||||
//basic spider mob, these generally guard nests
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider
|
||||
name = "giant spider"
|
||||
desc = "Furry and black, it makes you shudder to look at it. This one has deep red eyes."
|
||||
icon_state = "guard"
|
||||
icon_living = "guard"
|
||||
icon_dead = "guard_dead"
|
||||
mob_biotypes = MOB_ORGANIC | MOB_BUG
|
||||
speak_emote = list("chitters")
|
||||
emote_hear = list("chitters")
|
||||
speak_chance = 5
|
||||
turns_per_move = 5
|
||||
see_in_dark = 8
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
|
||||
footstep_type = FOOTSTEP_MOB_CLAW
|
||||
butcher_results = list(/obj/item/food/monstermeat/spidermeat = 2, /obj/item/food/monstermeat/spiderleg = 8)
|
||||
response_help = "pets"
|
||||
response_disarm = "gently pushes aside"
|
||||
maxHealth = 200
|
||||
health = 200
|
||||
obj_damage = 60
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 20
|
||||
heat_damage_per_tick = 20 //amount of damage applied if animal's body temperature is higher than maxbodytemp
|
||||
cold_damage_per_tick = 20 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp
|
||||
faction = list("spiders")
|
||||
pass_flags = PASSTABLE
|
||||
move_to_delay = 6
|
||||
attacktext = "bites"
|
||||
attack_sound = 'sound/weapons/bite.ogg'
|
||||
gold_core_spawnable = HOSTILE_SPAWN
|
||||
var/venom_per_bite = 0 // While the /poison/ type path remains as-is for consistency reasons, we're really talking about venom, not poison.
|
||||
contains_xeno_organ = TRUE
|
||||
surgery_container = /datum/xenobiology_surgery_container/spider
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/Initialize(mapload)
|
||||
. = ..()
|
||||
var/datum/action/innate/web_giant_spider/web_action = new()
|
||||
web_action.Grant(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/Destroy()
|
||||
for(var/datum/action/innate/web_giant_spider/web_action in actions)
|
||||
web_action.Remove(src)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/AttackingTarget()
|
||||
// This is placed here, NOT on /poison, because the other subtypes of /poison/ already override AttackingTarget() completely, and as such it would do nothing but confuse people there.
|
||||
. = ..()
|
||||
if(. && venom_per_bite > 0 && iscarbon(target) && (!client || a_intent == INTENT_HARM))
|
||||
var/mob/living/carbon/C = target
|
||||
var/inject_target = pick("chest", "head")
|
||||
if(C.can_inject(null, FALSE, inject_target, FALSE))
|
||||
C.reagents.add_reagent("spidertoxin", venom_per_bite)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/get_spacemove_backup()
|
||||
. = ..()
|
||||
// If we don't find any normal thing to use, attempt to use any nearby spider structure instead.
|
||||
if(!.)
|
||||
for(var/obj/structure/spider/S in range(1, get_turf(src)))
|
||||
return S
|
||||
|
||||
//nursemaids - these create webs and eggs
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/nurse
|
||||
desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes."
|
||||
icon_state = "nurse"
|
||||
icon_living = "nurse"
|
||||
icon_dead = "nurse_dead"
|
||||
butcher_results = list(/obj/item/food/monstermeat/spidermeat = 2, /obj/item/food/monstermeat/spiderleg = 8, /obj/item/food/monstermeat/spidereggs = 4)
|
||||
|
||||
maxHealth = 40
|
||||
health = 40
|
||||
melee_damage_lower = 5
|
||||
melee_damage_upper = 10
|
||||
gold_core_spawnable = NO_SPAWN
|
||||
venom_per_bite = 30
|
||||
var/atom/cocoon_target
|
||||
var/fed = 0
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/nurse/Initialize(mapload)
|
||||
. = ..()
|
||||
var/datum/action/innate/wrap_giant_spider/wrap_action = new()
|
||||
wrap_action.Grant(src)
|
||||
var/datum/action/innate/lay_eggs_giant_spider/egg_action = new()
|
||||
egg_action.Grant(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/nurse/Destroy()
|
||||
for(var/datum/action/innate/wrap_giant_spider/wrap_action in actions)
|
||||
wrap_action.Remove(src)
|
||||
for(var/datum/action/innate/lay_eggs_giant_spider/egg_action in actions)
|
||||
egg_action.Remove(src)
|
||||
return ..()
|
||||
|
||||
//hunters have the most poison and move the fastest, so they can find prey
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/hunter
|
||||
desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes."
|
||||
icon_state = "hunter"
|
||||
icon_living = "hunter"
|
||||
icon_dead = "hunter_dead"
|
||||
maxHealth = 120
|
||||
health = 120
|
||||
melee_damage_lower = 10
|
||||
venom_per_bite = 10
|
||||
move_to_delay = 5
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/handle_automated_movement() //Hacky and ugly.
|
||||
. = ..()
|
||||
if(AIStatus == AI_IDLE)
|
||||
//1% chance to skitter madly away
|
||||
if(prob(1))
|
||||
stop_automated_movement = TRUE
|
||||
Goto(pick(urange(20, src, 1)), move_to_delay)
|
||||
spawn(50)
|
||||
stop_automated_movement = FALSE
|
||||
walk(src,0)
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/GiveUp(C)
|
||||
spawn(100)
|
||||
if(cocoon_target == C && get_dist(src, cocoon_target) > 1)
|
||||
cocoon_target = null
|
||||
stop_automated_movement = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/nurse/handle_automated_movement() //Hacky and ugly.
|
||||
if(..())
|
||||
var/list/can_see = view(src, 10)
|
||||
if(prob(30)) //30% chance to stop wandering and do something
|
||||
//first, check for potential food nearby to cocoon
|
||||
for(var/mob/living/C in can_see)
|
||||
if(C.stat && !istype(C, /mob/living/simple_animal/hostile/poison/giant_spider) && !C.anchored)
|
||||
cocoon_target = C
|
||||
Goto(C, move_to_delay)
|
||||
//give up if we can't reach them after 10 seconds
|
||||
GiveUp(C)
|
||||
return
|
||||
//second, spin a sticky spiderweb on this tile
|
||||
var/obj/structure/spider/stickyweb/W = locate() in get_turf(src)
|
||||
if(!W)
|
||||
create_web()
|
||||
else
|
||||
//third, lay an egg cluster there
|
||||
if(fed)
|
||||
lay_spider_eggs()
|
||||
else
|
||||
//fourthly, cocoon any nearby items so those pesky pinkskins can't use them
|
||||
for(var/obj/O in can_see)
|
||||
if(O.anchored)
|
||||
continue
|
||||
|
||||
if(isitem(O) || isstructure(O) || ismachinery(O))
|
||||
cocoon_target = O
|
||||
stop_automated_movement = TRUE
|
||||
Goto(O, move_to_delay)
|
||||
//give up if we can't reach them after 10 seconds
|
||||
GiveUp(O)
|
||||
|
||||
else if(cocoon_target)
|
||||
if(get_dist(src, cocoon_target) <= 1)
|
||||
wrap_target()
|
||||
|
||||
else
|
||||
stop_automated_movement = FALSE
|
||||
|
||||
/datum/action/innate/web_giant_spider
|
||||
name = "Lay Web"
|
||||
button_icon = 'icons/effects/effects.dmi'
|
||||
button_icon_state = "stickyweb1"
|
||||
|
||||
/datum/action/innate/web_giant_spider/Activate()
|
||||
var/mob/living/simple_animal/hostile/poison/giant_spider/user = owner
|
||||
user.create_web()
|
||||
|
||||
/datum/action/innate/wrap_giant_spider
|
||||
name = "Wrap"
|
||||
button_icon = 'icons/effects/effects.dmi'
|
||||
button_icon_state = "cocoon_large1"
|
||||
|
||||
/datum/action/innate/wrap_giant_spider/Activate()
|
||||
var/mob/living/simple_animal/hostile/poison/giant_spider/nurse/user = owner
|
||||
user.wrap_target()
|
||||
|
||||
/datum/action/innate/lay_eggs_giant_spider
|
||||
name = "Lay Eggs"
|
||||
button_icon = 'icons/effects/effects.dmi'
|
||||
button_icon_state = "eggs"
|
||||
|
||||
/datum/action/innate/lay_eggs_giant_spider/Activate()
|
||||
var/mob/living/simple_animal/hostile/poison/giant_spider/nurse/user = owner
|
||||
user.lay_spider_eggs()
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/proc/create_web()
|
||||
var/T = loc
|
||||
|
||||
visible_message("<span class='notice'>[src] begins to secrete a sticky substance.</span>")
|
||||
stop_automated_movement = TRUE
|
||||
if(!do_after_once(src, 4 SECONDS, target = loc, attempt_cancel_message = "You stop spinning a web."))
|
||||
return
|
||||
new /obj/structure/spider/stickyweb(T)
|
||||
stop_automated_movement = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/wrap_target()
|
||||
if(!cocoon_target)
|
||||
var/list/choices = list()
|
||||
for(var/mob/living/L in view(1, src))
|
||||
if(L == src)
|
||||
continue
|
||||
if(L.stat != DEAD)
|
||||
continue
|
||||
if(istype(L, /mob/living/simple_animal/hostile/poison/giant_spider))
|
||||
continue
|
||||
if(Adjacent(L))
|
||||
choices += L
|
||||
for(var/obj/O in get_turf(src))
|
||||
if(O.anchored)
|
||||
continue
|
||||
if(!(isitem(O) || isstructure(O) || ismachinery(O)))
|
||||
continue
|
||||
if(Adjacent(O))
|
||||
choices += O
|
||||
if(length(choices))
|
||||
cocoon_target = tgui_input_list(src, "What do you wish to cocoon?", "Cocoon Wrapping", choices)
|
||||
else
|
||||
to_chat(src, "<span class='warning'>No suitable dead prey or wrappable objects found nearby.")
|
||||
return
|
||||
|
||||
if(cocoon_target)
|
||||
visible_message("<span class='notice'>[src] begins to secrete a sticky substance around [cocoon_target].</span>")
|
||||
stop_automated_movement = TRUE
|
||||
walk(src, 0)
|
||||
if(!do_after_once(src, 5 SECONDS, target = cocoon_target, attempt_cancel_message = "You stop wrapping [cocoon_target]."))
|
||||
return
|
||||
if(cocoon_target && isturf(cocoon_target.loc) && get_dist(src, cocoon_target) <= 1)
|
||||
var/obj/structure/spider/cocoon/C = new(cocoon_target.loc)
|
||||
var/large_cocoon = FALSE
|
||||
C.pixel_x = cocoon_target.pixel_x
|
||||
C.pixel_y = cocoon_target.pixel_y
|
||||
for(var/obj/item/I in C.loc)
|
||||
I.loc = C
|
||||
for(var/obj/structure/S in C.loc)
|
||||
if(!S.anchored)
|
||||
S.loc = C
|
||||
large_cocoon = TRUE
|
||||
for(var/obj/machinery/M in C.loc)
|
||||
if(!M.anchored)
|
||||
M.loc = C
|
||||
large_cocoon = TRUE
|
||||
for(var/mob/living/L in C.loc)
|
||||
if(istype(L, /mob/living/simple_animal/hostile/poison/giant_spider))
|
||||
continue
|
||||
if(L.stat != DEAD)
|
||||
continue
|
||||
large_cocoon = TRUE
|
||||
L.loc = C
|
||||
C.pixel_x = L.pixel_x
|
||||
C.pixel_y = L.pixel_y
|
||||
fed++
|
||||
visible_message("<span class='danger'>[src] sticks a proboscis into [L] and sucks a viscous substance out.</span>")
|
||||
|
||||
break
|
||||
if(large_cocoon)
|
||||
C.icon_state = pick("cocoon_large1", "cocoon_large2", "cocoon_large3")
|
||||
cocoon_target = null
|
||||
stop_automated_movement = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/lay_spider_eggs()
|
||||
var/obj/structure/spider/eggcluster/E = locate() in get_turf(src)
|
||||
if(E)
|
||||
to_chat(src, "<span class='notice'>There is already a cluster of eggs here!</span>")
|
||||
return
|
||||
if(!fed)
|
||||
to_chat(src, "<span class='warning'>You are too hungry to do this!</span>")
|
||||
return
|
||||
visible_message("<span class='notice'>[src] begins to lay a cluster of eggs.</span>")
|
||||
stop_automated_movement = TRUE
|
||||
if(!do_after_once(src, 4 SECONDS, target = loc, attempt_cancel_message = "You stop laying eggs."))
|
||||
return
|
||||
var/obj/structure/spider/eggcluster/C = new /obj/structure/spider/eggcluster(loc)
|
||||
C.faction = faction.Copy()
|
||||
C.master_commander = master_commander
|
||||
if(ckey)
|
||||
C.player_spiders = TRUE
|
||||
fed--
|
||||
stop_automated_movement = FALSE
|
||||
@@ -1,21 +0,0 @@
|
||||
/mob/living/simple_animal/hostile/retaliate/araneus
|
||||
name = "Sergeant Araneus"
|
||||
real_name = "Sergeant Araneus"
|
||||
desc = "A fierce companion for any person of power, this spider has been carefully trained by Nanotrasen specialists. Its beady, staring eyes send shivers down your spine."
|
||||
faction = list("spiders")
|
||||
icon_state = "guard"
|
||||
icon_living = "guard"
|
||||
icon_dead = "guard_dead"
|
||||
icon_gib = "guard_dead"
|
||||
mob_biotypes = MOB_ORGANIC | MOB_BUG
|
||||
turns_per_move = 8
|
||||
response_help = "pets"
|
||||
emote_hear = list("chitters")
|
||||
maxHealth = 250
|
||||
health = 250
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 20
|
||||
unique_pet = TRUE
|
||||
atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 2, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
|
||||
gender = FEMALE
|
||||
|
||||
@@ -251,4 +251,7 @@
|
||||
/// Controls whether they can say some symbols.
|
||||
var/speaks_ooc = FALSE
|
||||
|
||||
/// For storing what do_after's something has, key = string, value = amount of interactions of that type happening.
|
||||
var/list/do_afters
|
||||
|
||||
new_attack_chain = TRUE
|
||||
|
||||
Reference in New Issue
Block a user