Merge pull request #12157 from Fox-McCloud/simple-mob-update

TG Simple Mob Update
This commit is contained in:
variableundefined
2019-09-03 21:50:24 -04:00
committed by GitHub
86 changed files with 972 additions and 998 deletions
+4
View File
@@ -155,6 +155,10 @@
// /mob/living/carbon signals
#define COMSIG_CARBON_SOUNDBANG "carbon_soundbang" //from base of mob/living/carbon/soundbang_act(): (list(intensity))
// /mob/living/simple_animal/hostile signals
#define COMSIG_HOSTILE_ATTACKINGTARGET "hostile_attackingtarget"
#define COMPONENT_HOSTILE_NO_ATTACK 1
// /obj signals
#define COMSIG_OBJ_DECONSTRUCT "obj_deconstruct" //from base of obj/deconstruct(): (disassembled)
#define COMSIG_OBJ_SETANCHORED "obj_setanchored" //called in /obj/structure/setAnchored(): (value)
+1
View File
@@ -13,6 +13,7 @@
#define CONDUCT 32 // conducts electricity (metal etc.)
#define ABSTRACT 64 // for all things that are technically items but used for various different stuff, made it 128 because it could conflict with other flags other way
#define ON_BORDER 128 // item has priority to check when entering or leaving
#define PREVENT_CLICK_UNDER 256
#define EARBANGPROTECT 1024
+2
View File
@@ -18,6 +18,8 @@
#define ismecha(A) (istype(A, /obj/mecha))
#define isspacepod(A) (istype(A, /obj/spacepod))
#define iseffect(A) (istype(A, /obj/effect))
#define is_cleanable(A) (istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/rune)) //if something is cleanable
+2
View File
@@ -219,3 +219,5 @@
#define hasorgans(A) (ishuman(A))
#define is_admin(user) (check_rights(R_ADMIN, 0, (user)) != 0)
#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return;
+27 -4
View File
@@ -107,6 +107,9 @@
if(next_move > world.time) // in the year 2000...
return
if(!modifiers["catcher"] && A.IsObscured())
return
if(istype(loc,/obj/mecha))
if(!locate(/turf) in list(A,A.loc)) // Prevents inventory from being drilled
return
@@ -172,6 +175,24 @@
return
//Is the atom obscured by a PREVENT_CLICK_UNDER_1 object above it
/atom/proc/IsObscured()
if(!isturf(loc)) //This only makes sense for things directly on turfs for now
return FALSE
var/turf/T = get_turf_pixel(src)
if(!T)
return FALSE
for(var/atom/movable/AM in T)
if(AM.flags & PREVENT_CLICK_UNDER && AM.density && AM.layer > layer)
return TRUE
return FALSE
/turf/IsObscured()
for(var/atom/movable/AM in src)
if(AM.flags & PREVENT_CLICK_UNDER && AM.density)
return TRUE
return FALSE
// Default behavior: ignore double clicks, consider them normal clicks instead
/mob/proc/DblClickOn(var/atom/A, var/params)
return
@@ -399,8 +420,8 @@
dir = direction
/obj/screen/click_catcher
icon = 'icons/mob/screen_full.dmi'
icon_state = "passage0"
icon = 'icons/mob/screen_gen.dmi'
icon_state = "catcher"
plane = CLICKCATCHER_PLANE
mouse_opacity = MOUSE_OPACITY_OPAQUE
screen_loc = "CENTER"
@@ -430,5 +451,7 @@
C.swap_hand()
else
var/turf/T = params2turf(modifiers["screen-loc"], get_turf(usr))
T.Click(location, control, params)
return 1
params += "&catcher=1"
if(T)
T.Click(location, control, params)
. = 1
@@ -158,9 +158,9 @@
////CTRL CLICK FOR SWARMERS AND SWARMER_ACT()'S////
/mob/living/simple_animal/hostile/swarmer/AttackingTarget()
if(!isliving(target))
target.swarmer_act(src)
return target.swarmer_act(src)
else
..()
return ..()
/mob/living/simple_animal/hostile/swarmer/CtrlClickOn(atom/A)
face_atom(A)
@@ -352,11 +352,11 @@
/obj/structure/lattice/catwalk/swarmer_catwalk/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>We have created these for our own benefit. Aborting.</span>")
return FALSE
return FALSE
/obj/structure/shuttle/engine/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
to_chat(S, "<span class='warning'>This shuttle may be important to us later. Aborting.</span>")
return FALSE
return FALSE
////END CTRL CLICK FOR SWARMERS////
@@ -123,7 +123,7 @@
med_hud_set_health()
med_hud_set_status()
/mob/living/simple_animal/hostile/guardian/adjustHealth(amount) //The spirit is invincible, but passes on damage to the summoner
/mob/living/simple_animal/hostile/guardian/adjustHealth(amount, updating_health = TRUE) //The spirit is invincible, but passes on damage to the summoner
var/damage = amount * damage_transfer
if(summoner)
if(loc == summoner)
@@ -26,11 +26,12 @@
stat(null, "Stealth Cooldown Remaining: [max(round((stealthcooldown - world.time)*0.1, 0.1), 0)] seconds")
/mob/living/simple_animal/hostile/guardian/assassin/AttackingTarget()
..()
if(toggle && (isliving(target) || istype(target, /obj/structure/window) || istype(target, /obj/structure/grille)))
ToggleMode(1)
. = ..()
if(.)
if(toggle && (isliving(target) || istype(target, /obj/structure/window) || istype(target, /obj/structure/grille)))
ToggleMode(1)
/mob/living/simple_animal/hostile/guardian/assassin/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
/mob/living/simple_animal/hostile/guardian/assassin/adjustHealth(amount, updating_health = TRUE)
. = ..()
if(. > 0 && toggle)
ToggleMode(1)
@@ -28,9 +28,9 @@
toggle = TRUE
/mob/living/simple_animal/hostile/guardian/fire/AttackingTarget()
..()
. = ..()
if(toggle)
if(ishuman(target) && !summoner)
if(. && ishuman(target) && !summoner)
spawn(0)
new /obj/effect/hallucination/delusion(target.loc, target, force_kind = "custom", duration = 200, skip_nearby = 0, custom_icon = icon_state, custom_icon_file = icon)
else
@@ -43,7 +43,7 @@
stat(null, "Bluespace Beacon Cooldown Remaining: [max(round((beacon_cooldown - world.time)*0.1, 0.1), 0)] seconds")
/mob/living/simple_animal/hostile/guardian/healer/AttackingTarget()
..()
. = ..()
if(toggle == TRUE)
if(loc == summoner)
to_chat(src, "<span class='danger'>You must be manifested to heal!</span>")
@@ -19,8 +19,8 @@
var/successfulshocks = 0
/mob/living/simple_animal/hostile/guardian/beam/AttackingTarget()
..()
if(isliving(target) && target != src && target != summoner)
. = ..()
if(. && isliving(target) && target != src && target != summoner)
cleardeletedchains()
for(var/chain in enemychains)
var/datum/beam/B = chain
@@ -11,10 +11,9 @@
melee_damage_upper = 10
damage_transfer = 0.9
projectiletype = /obj/item/projectile/guardian
ranged_cooldown_time = 10
ranged_cooldown_time = 1 //fast!
projectilesound = 'sound/effects/hit_on_shattered_glass.ogg'
ranged = 1
rapid = 1
range = 13
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
see_in_dark = 8
@@ -19,7 +19,7 @@
battlecry = input
/mob/living/simple_animal/hostile/guardian/punch/AttackingTarget()
..()
. = ..()
if(iscarbon(target) && target != summoner)
if(length(battlecry) > 11)//no more then 11 letters in a battle cry.
visible_message("<span class='danger'>[src] punches [target]!</span>")
@@ -28,7 +28,7 @@
melee_damage_upper = 20
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
idle_vision_range = 1 // Only attack when target is close
vision_range = 1 // Only attack when target is close
wander = 0
attacktext = "glomps"
attack_sound = 'sound/effects/blobattack.ogg'
@@ -54,7 +54,7 @@
. = ..()
AddSpell(new /obj/effect/proc_holder/spell/targeted/smoke)
AddSpell(new /obj/effect/proc_holder/spell/targeted/forcewall)
/mob/living/simple_animal/hostile/morph/examine(mob/user)
if(morphed)
if(form)
@@ -153,7 +153,7 @@
restore()
/mob/living/simple_animal/hostile/morph/LoseAggro()
vision_range = idle_vision_range
vision_range = initial(vision_range)
/mob/living/simple_animal/hostile/morph/AIShouldSleep(var/list/possible_targets)
. = ..()
@@ -179,4 +179,4 @@
if(do_after(src, 20, target = I))
eat(I)
return
target.attack_animal(src)
return ..()
@@ -85,7 +85,7 @@
/mob/living/simple_animal/revenant/narsie_act()
return //most humans will now be either bones or harvesters, but we're still un-alive.
/mob/living/simple_animal/revenant/adjustHealth(amount)
/mob/living/simple_animal/revenant/adjustHealth(amount, updating_health = TRUE)
if(!revealed)
return
essence = max(0, essence-amount)
+1
View File
@@ -10,6 +10,7 @@
power_channel = ENVIRON
max_integrity = 350
armor = list(melee = 30, bullet = 30, laser = 20, energy = 20, bomb = 10, bio = 100, rad = 100)
flags = PREVENT_CLICK_UNDER
var/closingLayer = CLOSED_DOOR_LAYER
var/visible = 1
var/operating = FALSE
@@ -140,6 +140,15 @@
..()
animate(src, alpha = 0, time = duration)
/obj/effect/temp_visual/decoy/fading/threesecond
duration = 40
/obj/effect/temp_visual/decoy/fading/fivesecond
duration = 50
/obj/effect/temp_visual/decoy/fading/halfsecond
duration = 5
/obj/effect/temp_visual/fire
icon = 'icons/goonstation/effects/fire.dmi'
icon_state = "3"
+3
View File
@@ -579,6 +579,7 @@ var/global/wcCommon = pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", "#8f
dir = FULLTILE_WINDOW_DIR
level = 3
fulltile = TRUE
flags = PREVENT_CLICK_UNDER
/obj/structure/window/full/basic
desc = "It looks thin and flimsy. A few knocks with... anything, really should shatter it."
@@ -675,6 +676,7 @@ obj/structure/window/full/reinforced/ice
dir = FULLTILE_WINDOW_DIR
max_integrity = 200
fulltile = TRUE
flags = PREVENT_CLICK_UNDER
reinf = TRUE
heat_resistance = 1600
armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 100, "rad" = 100)
@@ -734,6 +736,7 @@ obj/structure/window/full/reinforced/ice
smooth = SMOOTH_TRUE
canSmoothWith = null
fulltile = TRUE
flags = PREVENT_CLICK_UNDER
dir = FULLTILE_WINDOW_DIR
max_integrity = 120
level = 3
@@ -77,6 +77,7 @@
dir = FULLTILE_WINDOW_DIR
max_integrity = 100
fulltile = TRUE
flags = PREVENT_CLICK_UNDER
reinf = TRUE
heat_resistance = 1600
armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 100, "rad" = 100)
@@ -296,7 +297,7 @@
name = "air flow blocker"
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF
unacidable = TRUE
invisibility = INVISIBILITY_ABSTRACT
invisibility = INVISIBILITY_ABSTRACT
//Signs
/obj/structure/sign/mining
@@ -380,7 +380,7 @@
medsensor.add_hud_to(src)
/mob/living/simple_animal/hostile/lightgeist/AttackingTarget()
..()
. = ..()
if(isliving(target) && target != src)
var/mob/living/L = target
if(L.stat < DEAD)
+3 -4
View File
@@ -15,7 +15,6 @@
a_intent = INTENT_HARM
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
idle_vision_range = 5
move_to_delay = 10
health = 125
maxHealth = 125
@@ -145,7 +144,7 @@
/mob/living/simple_animal/hostile/mining_drone/proc/SetCollectBehavior()
mode = MINEDRONE_COLLECT
idle_vision_range = 9
vision_range = 9
search_objects = 2
wander = TRUE
ranged = FALSE
@@ -156,7 +155,7 @@
/mob/living/simple_animal/hostile/mining_drone/proc/SetOffenseBehavior()
mode = MINEDRONE_ATTACK
idle_vision_range = 7
vision_range = 7
search_objects = 0
wander = FALSE
ranged = TRUE
@@ -192,7 +191,7 @@
for(var/obj/item/stack/ore/O in contents)
O.forceMove(drop_location())
/mob/living/simple_animal/hostile/mining_drone/adjustHealth(amount)
/mob/living/simple_animal/hostile/mining_drone/adjustHealth(amount, updating_health = TRUE)
if(mode != MINEDRONE_ATTACK && amount > 0)
SetOffenseBehavior()
. = ..()
@@ -34,9 +34,6 @@
handcrafting = new()
var/mob/M = src
faction |= "\ref[M]" //what
// Set up DNA.
if(dna)
dna.ready_dna(src)
@@ -65,8 +65,6 @@
var/ventcrawler = 0 //Determines if the mob can go through the vents.
var/has_fine_manipulation = 1 // Can use small items.
var/mob/living/list/ignored_by = list() // list of mobs that will ignore this species
var/list/allowed_consumed_mobs = list() //If a species can consume mobs, put the type of mobs it can consume here.
var/list/species_traits = list()
@@ -8,8 +8,6 @@
unarmed_type = /datum/unarmed_attack/claws
ignored_by = list(/mob/living/simple_animal/hostile/faithless)
blood_color = "#CCCCCC"
flesh_color = "#AAAAAA"
has_organ = list(
@@ -51,12 +49,14 @@
if(grant_vision_toggle)
vision_toggle = new
vision_toggle.Grant(H)
H.faction |= "faithless"
/datum/species/shadow/on_species_loss(mob/living/carbon/human/H)
..()
if(grant_vision_toggle && vision_toggle)
H.vision_type = null
vision_toggle.Remove(H)
H.faction -= "faithless"
/datum/species/shadow/handle_life(mob/living/carbon/human/H)
var/light_amount = 0
+3 -2
View File
@@ -2,6 +2,7 @@
. = ..()
var/datum/atom_hud/data/human/medical/advanced/medhud = huds[DATA_HUD_MEDICAL_ADVANCED]
medhud.add_to_hud(src)
faction += "\ref[src]"
/mob/living/prepare_huds()
..()
@@ -256,10 +257,10 @@
death()
to_chat(src, "<span class='notice'>You have given up life and succumbed to death.</span>")
/mob/living/proc/InCritical()
return (health < HEALTH_THRESHOLD_CRIT && health > HEALTH_THRESHOLD_DEAD && stat == UNCONSCIOUS)
/mob/living/ex_act(severity)
..()
@@ -1,10 +1,26 @@
/mob/living/simple_animal/attackby(obj/item/O, mob/living/user)
if(can_collar && !collar && istype(O, /obj/item/clothing/accessory/petcollar))
var/obj/item/clothing/accessory/petcollar/C = O
if(user.drop_item())
C.forceMove(src)
collar = C
collar.equipped(src)
regenerate_icons()
to_chat(usr, "<span class='notice'>You put \the [C] around \the [src]'s neck.</span>")
if(C.tagname)
name = C.tagname
real_name = C.tagname
return
else
return ..()
/mob/living/simple_animal/attack_hand(mob/living/carbon/human/M)
..()
switch(M.a_intent)
if(INTENT_HELP)
if(health > 0)
visible_message("<span class='notice'>[M] [response_help] [src].</span>")
visible_message("<span class='notice'>[M] [response_help] [src].</span>", "<span class='notice'>[M] [response_help] you.</span>")
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
if(INTENT_GRAB)
@@ -12,12 +28,12 @@
if(INTENT_HARM, INTENT_DISARM)
M.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
visible_message("<span class='danger'>[M] [response_harm] [src]!</span>")
visible_message("<span class='danger'>[M] [response_harm] [src]!</span>", "<span class='userdanger'>[M] [response_harm] you!</span>")
playsound(loc, attacked_sound, 25, 1, -1)
attack_threshold_check(harm_intent_damage)
add_attack_logs(M, src, "Melee attacked with fists")
updatehealth()
return 1
return TRUE
/mob/living/simple_animal/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
if(user.a_intent == INTENT_HARM)
@@ -39,10 +55,11 @@
/mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L)
if(..()) //successful larva bite
var/damage = rand(5, 10)
if(stat != DEAD)
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
attack_threshold_check(damage)
var/damage = rand(5, 10)
. = attack_threshold_check(damage)
if(.)
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
/mob/living/simple_animal/attack_animal(mob/living/simple_animal/M)
if(..())
@@ -51,21 +68,57 @@
/mob/living/simple_animal/attack_slime(mob/living/carbon/slime/M)
..()
var/damage = rand(1, 3)
var/damage = rand(15, 25)
if(M.is_adult)
damage = rand(20, 40)
else
damage = rand(5, 35)
damage = rand(20, 35)
attack_threshold_check(damage)
return
/mob/living/simple_animal/proc/attack_threshold_check(damage, damagetype = BRUTE)
if(damage <= force_threshold || !damage_coeff[damagetype])
visible_message("<span class='warning'>[src] looks unharmed.</span>")
/mob/living/simple_animal/proc/attack_threshold_check(damage, damagetype = BRUTE, armorcheck = "melee")
var/temp_damage = damage
if(!damage_coeff[damagetype])
temp_damage = 0
else
apply_damage(damage, damagetype)
temp_damage *= damage_coeff[damagetype]
if(temp_damage >= 0 && temp_damage <= force_threshold)
visible_message("<span class='warning'>[src] looks unharmed.</span>")
return FALSE
else
apply_damage(damage, damagetype, null, getarmor(null, armorcheck))
return TRUE
/mob/living/simple_animal/bullet_act(obj/item/projectile/Proj)
if(!Proj)
return
apply_damage(Proj.damage, Proj.damage_type)
Proj.on_hit(src, 0)
return FALSE
/mob/living/simple_animal/ex_act(severity, origin)
if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src))
return
..()
var/bomb_armor = getarmor(null, "bomb")
switch(severity)
if(1)
if(prob(bomb_armor))
adjustBruteLoss(500)
else
gib()
return
if(2)
var/bloss = 60
if(prob(bomb_armor))
bloss = bloss / 1.5
adjustBruteLoss(bloss)
if(3)
var/bloss = 30
if(prob(bomb_armor))
bloss = bloss / 1.5
adjustBruteLoss(bloss)
/mob/living/simple_animal/blob_act(obj/structure/blob/B)
adjustBruteLoss(20)
/mob/living/simple_animal/do_attack_animation(atom/A, visual_effect_icon, used_item, no_effect, end_pixel_y)
if(!no_effect && !visual_effect_icon && melee_damage_upper)
@@ -241,10 +241,10 @@
else
to_chat(user, "[src] is in pristine condition.")
/mob/living/simple_animal/bot/adjustHealth(amount)
/mob/living/simple_animal/bot/adjustHealth(amount, updating_health = TRUE)
if(amount > 0 && prob(10))
new /obj/effect/decal/cleanable/blood/oil(loc)
return ..(amount)
. = ..()
/mob/living/simple_animal/bot/updatehealth(reason = "none given")
..(reason)
@@ -217,7 +217,7 @@
/mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here?
if(istype(A, /mob/living/simple_animal/hostile/construct)) //is it a construct?
if(isconstruct(A)) //is it a construct?
var/mob/living/simple_animal/hostile/construct/C = A
if(C.health < C.maxHealth) //is it hurt? let's go heal it if it is
return 1
@@ -236,7 +236,7 @@
..()
if(isliving(target))
var/mob/living/L = target
if(istype(L, /mob/living/simple_animal/hostile/construct) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it
if(isconstruct(L) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it
LoseTarget()
return 0
if(L.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you
@@ -245,7 +245,7 @@
/mob/living/simple_animal/hostile/construct/builder/Aggro()
..()
if(istype(target, /mob/living/simple_animal/hostile/construct)) //oh the target is a construct no need to flee
if(isconstruct(target)) //oh the target is a construct no need to flee
retreat_distance = null
minimum_distance = 1
@@ -256,7 +256,7 @@
/mob/living/simple_animal/hostile/construct/builder/hostile //actually hostile, will move around, hit things, heal other constructs
AIStatus = AI_ON
environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP
environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP
/////////////////////////////Behemoth/////////////////////////
@@ -0,0 +1,37 @@
/mob/living/simple_animal/proc/adjustHealth(amount, updating_health = TRUE)
if(status_flags & GODMODE)
return FALSE
var/oldbruteloss = bruteloss
bruteloss = Clamp(bruteloss + amount, 0, maxHealth)
if(oldbruteloss == bruteloss)
updating_health = FALSE
. = STATUS_UPDATE_NONE
else
. = STATUS_UPDATE_HEALTH
if(updating_health)
updatehealth()
/mob/living/simple_animal/adjustBruteLoss(amount, updating_health = TRUE)
if(damage_coeff[BRUTE])
return adjustHealth(amount * damage_coeff[BRUTE], updating_health)
/mob/living/simple_animal/adjustFireLoss(amount, updating_health = TRUE)
if(damage_coeff[BURN])
return adjustHealth(amount * damage_coeff[BURN], updating_health)
/mob/living/simple_animal/adjustOxyLoss(amount, updating_health = TRUE)
if(damage_coeff[OXY])
return adjustHealth(amount * damage_coeff[OXY], updating_health)
/mob/living/simple_animal/adjustToxLoss(amount, updating_health = TRUE)
if(damage_coeff[TOX])
return adjustHealth(amount * damage_coeff[TOX], updating_health)
/mob/living/simple_animal/adjustCloneLoss(amount, updating_health = TRUE)
if(damage_coeff[CLONE])
return adjustHealth(amount * damage_coeff[CLONE], updating_health)
/mob/living/simple_animal/adjustStaminaLoss(amount, updating_health = TRUE)
if(damage_coeff[STAMINA])
return ..(amount*damage_coeff[STAMINA], updating_health)
@@ -94,26 +94,27 @@
new cat_type(loc)
/mob/living/simple_animal/pet/cat/handle_automated_action()
/mob/living/simple_animal/pet/cat/Life()
..()
if(prob(1))
custom_emote(1, pick("stretches out for a belly rub.", "wags its tail.", "lies down."))
icon_state = "[icon_living]_rest"
resting = 1
update_canmove()
else if (prob(1))
custom_emote(1, pick("sits down.", "crouches on its hind legs.", "looks alert."))
icon_state = "[icon_living]_sit"
resting = 1
update_canmove()
else if (prob(1))
if (resting)
custom_emote(1, pick("gets up and meows.", "walks around.", "stops resting."))
icon_state = "[icon_living]"
resting = 0
make_babies()
/mob/living/simple_animal/pet/cat/handle_automated_action()
if(!stat && !buckled)
if(prob(1))
custom_emote(1, pick("stretches out for a belly rub.", "wags its tail.", "lies down."))
StartResting()
else if(prob(1))
custom_emote(1, pick("sits down.", "crouches on its hind legs.", "looks alert."))
icon_state = "[icon_living]_sit"
resting = TRUE
update_canmove()
else
custom_emote(1, pick("grooms its fur.", "twitches its whiskers.", "shakes out its coat."))
else if(prob(1))
if(resting)
custom_emote(1, pick("gets up and meows.", "walks around.", "stops resting."))
StopResting()
else
custom_emote(1, pick("grooms its fur.", "twitches its whiskers.", "shakes out its coat."))
//MICE!
if(eats_mice && isturf(loc) && !incapacitated())
@@ -128,10 +129,9 @@
if(T.cooldown < (world.time - 400))
custom_emote(1, "bats \the [T] around with its paw!")
T.cooldown = world.time
make_babies()
/mob/living/simple_animal/pet/cat/handle_automated_movement()
..()
. = ..()
if(!stat && !resting && !buckled)
turns_since_scan++
if(turns_since_scan > 5)
@@ -451,9 +451,8 @@
response_harm = "kicks"
gold_core_spawnable = CHEM_MOB_SPAWN_INVALID
/mob/living/simple_animal/pet/corgi/Ian/Life()
..()
/mob/living/simple_animal/pet/corgi/Ian/handle_automated_movement()
. = ..()
//Feeding, chasing food, FOOOOODDDD
if(!resting && !buckled)
turns_since_scan++
@@ -580,9 +579,10 @@
/mob/living/simple_animal/pet/corgi/Lisa/Life()
..()
make_babies()
/mob/living/simple_animal/pet/corgi/Lisa/handle_automated_movement()
. = ..()
if(!resting && !buckled)
if(prob(1))
custom_emote(1, pick("dances around.","chases her tail."))
@@ -651,15 +651,16 @@
/mob/living/simple_animal/pet/corgi/Ian/borgi/Life(seconds, times_fired)
..()
if(emagged && prob(25))
var/mob/living/carbon/target = locate() in view(10,src)
if(target)
shootAt(target)
//spark for no reason
if(prob(5))
do_sparks(3, 1, src)
/mob/living/simple_animal/pet/corgi/Ian/borgi/handle_automated_action()
if(emagged && prob(25))
var/mob/living/carbon/target = locate() in view(10, src)
if(target)
shootAt(target)
/mob/living/simple_animal/pet/corgi/Ian/borgi/death(gibbed)
// Only execute the below if we successfully died
. = ..(gibbed)
@@ -17,8 +17,6 @@
stop_automated_movement = 1
friendly = "pinches"
ventcrawler = 2
var/obj/item/inventory_head
var/obj/item/inventory_mask
can_hide = 1
can_collar = 1
gold_core_spawnable = CHEM_MOB_SPAWN_FRIENDLY
@@ -32,11 +30,6 @@
var/east_vs_west = pick(4, 8)
if(Process_Spacemove(east_vs_west))
Move(get_step(src, east_vs_west), east_vs_west)
turns_since_move = 0
/mob/living/simple_animal/crab/Life(seconds, times_fired)
. = ..()
regenerate_icons()
//COFFEE! SQUEEEEEEEEE!
/mob/living/simple_animal/crab/Coffee
@@ -34,12 +34,11 @@
. = ..()
/mob/living/simple_animal/hostile/retaliate/goat/Destroy()
qdel(udder)
udder = null
QDEL_NULL(udder)
return ..()
/mob/living/simple_animal/hostile/retaliate/goat/handle_automated_movement()
..()
. = ..()
//chance to go crazy and start wacking stuff
if(!enemies.len && prob(1))
Retaliate()
@@ -49,14 +48,13 @@
LoseTarget()
visible_message("<span class='notice'>[src] calms down.</span>")
if(stat == CONSCIOUS)
eat_plants()
if(!pulledby)
for(var/direction in shuffle(list(1,2,4,8,5,6,9,10)))
var/step = get_step(src, direction)
if(step)
if(locate(/obj/structure/spacevine) in step || locate(/obj/structure/glowshroom) in step)
Move(step, get_dir(src, step))
eat_plants()
if(!pulledby)
for(var/direction in shuffle(list(1, 2, 4, 8, 5, 6, 9, 10)))
var/step = get_step(src, direction)
if(step)
if(locate(/obj/structure/spacevine) in step || locate(/obj/structure/glowshroom) in step)
Move(step, get_dir(src, step))
/mob/living/simple_animal/hostile/retaliate/goat/Life(seconds, times_fired)
. = ..()
@@ -94,8 +92,8 @@
say("Nom")
/mob/living/simple_animal/hostile/retaliate/goat/AttackingTarget()
..()
if(isdiona(target))
. = ..()
if(. && isdiona(target))
var/mob/living/carbon/human/H = target
var/obj/item/organ/external/NB = pick(H.bodyparts)
H.visible_message("<span class='warning'>[src] takes a big chomp out of [H]!</span>", "<span class='userdanger'>[src] takes a big chomp out of your [NB.name]!</span>")
@@ -56,28 +56,19 @@
visible_message("<span class='warning'>[src] chews through [C].</span>")
/mob/living/simple_animal/mouse/handle_automated_speech()
..()
if(prob(speak_chance))
for(var/mob/M in view())
M << squeak_sound
/mob/living/simple_animal/mouse/Life(seconds, times_fired)
. = ..()
if(stat == UNCONSCIOUS)
if(ckey || prob(1))
stat = CONSCIOUS
icon_state = "mouse_[mouse_color]"
wander = 1
else if(prob(5))
emote("snuffles")
/mob/living/simple_animal/mouse/Life()
..()
if(prob(0.5) && !ckey)
stat = UNCONSCIOUS
icon_state = "mouse_[mouse_color]_sleep"
wander = 0
speak_chance = 0
if(prob(speak_chance) && !incapacitated())
playsound(src, squeak_sound, 100, 1)
/mob/living/simple_animal/mouse/handle_automated_movement()
. = ..()
if(resting)
if(prob(1))
StopResting()
else if(prob(5))
custom_emote(2, "snuffles")
else if(prob(0.5))
StartResting()
/mob/living/simple_animal/mouse/New()
..()
@@ -119,7 +110,7 @@
desc = "It's toast."
death()
/mob/living/simple_animal/mouse/death(gibbed)
/mob/living/simple_animal/mouse/death(gibbed)
// Only execute the below if we successfully died
playsound(src, squeak_sound, 40, 1)
. = ..(gibbed)
@@ -19,13 +19,12 @@
see_in_dark = 5
gold_core_spawnable = CHEM_MOB_SPAWN_FRIENDLY
/mob/living/simple_animal/pet/pug/Life()
..()
/mob/living/simple_animal/pet/pug/handle_automated_movement()
. = ..()
if(!resting && !buckled)
if(prob(1))
custom_emote(1, pick("chases its tail."))
spawn(0)
for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
for(var/i in list(1, 2, 4, 8, 4, 2, 1, 2, 4, 8, 4, 2, 1, 2, 4, 8, 4, 2))
dir = i
sleep(1)
sleep(1)
@@ -158,7 +158,8 @@
if(istype(target, /obj/effect/decal/cleanable))
visible_message("<span class='notice'>\The [src] cleans up \the [target].</span>")
qdel(target)
return
return TRUE
var/atom/movable/M = target
M.clean_blood()
visible_message("<span class='notice'>\The [src] polishes \the [target].</span>")
return TRUE
@@ -22,6 +22,9 @@
attacktext = "bites"
attack_sound = 'sound/weapons/bite.ogg'
emote_taunt = list("flutters")
taunt_chance = 20
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
@@ -39,11 +42,6 @@
/mob/living/simple_animal/hostile/scarybat/Process_Spacemove(var/check_drift = 0)
return ..() //No drifting in space for space carp! //original comments do not steal
/mob/living/simple_animal/hostile/scarybat/FindTarget()
. = ..()
if(.)
custom_emote(1, "flutters towards [.]")
/mob/living/simple_animal/hostile/scarybat/Found(var/atom/A)//This is here as a potential override to pick a specific target if available
if(istype(A) && A == owner)
return 0
@@ -137,12 +137,13 @@
if(target == beehome)
var/obj/structure/beebox/BB = target
forceMove(BB)
toggle_ai(AI_IDLE)
target = null
wanted_objects -= beehometypecache //so we don't attack beeboxes when not going home
return //no don't attack the goddamm box
else
..()
if(isliving(target) && (!client || a_intent == INTENT_HARM))
. = ..()
if(. && isliving(target) && (!client || a_intent == INTENT_HARM))
var/mob/living/L = target
if(L.reagents)
if(beegent)
@@ -222,8 +223,8 @@
//leave pollination for the peasent bees
/mob/living/simple_animal/hostile/poison/bees/queen/AttackingTarget()
..()
if(beegent && isliving(target))
. = ..()
if(. && beegent && isliving(target))
var/mob/living/L = target
beegent.reaction_mob(L, TOUCH)
L.reagents.add_reagent(beegent.id, rand(1, 5))
@@ -283,6 +284,14 @@
QDEL_NULL(queen)
return ..()
/mob/living/simple_animal/hostile/poison/bees/consider_wakeup()
if(beehome && loc == beehome) // If bees are chilling in their nest, they're not actively looking for targets
idle = min(100, ++idle)
if(idle >= BEE_IDLE_ROAMING && prob(BEE_PROB_GOROAM))
toggle_ai(AI_ON)
forceMove(beehome.drop_location())
else
..()
//Syndicate Bees
/mob/living/simple_animal/hostile/poison/bees/syndi
@@ -327,8 +336,8 @@
return TRUE
/mob/living/simple_animal/hostile/poison/bees/syndi/AttackingTarget()
..()
if(target && isliving(target))
. = ..()
if(. && target && isliving(target))
var/mob/living/L = target
if(L.stat)
LoseTarget()
@@ -10,6 +10,8 @@
response_help = "pets the"
response_disarm = "gently pushes aside the"
response_harm = "hits the"
emote_taunt = list("gnashes")
taunt_chance = 30
speed = 0
maxHealth = 25
health = 25
@@ -50,14 +52,9 @@
/mob/living/simple_animal/hostile/carp/Process_Spacemove(var/movement_dir = 0)
return 1 //No drifting in space for space carp! //original comments do not steal
/mob/living/simple_animal/hostile/carp/FindTarget()
. = ..()
if(.)
custom_emote(1, "gnashes at [.]!")
/mob/living/simple_animal/hostile/carp/AttackingTarget()
..()
if(ishuman(target))
. = ..()
if(. && ishuman(target))
var/mob/living/carbon/human/H = target
H.adjustStaminaLoss(8)
@@ -19,6 +19,8 @@
attacktext = "grips"
attack_sound = 'sound/hallucinations/growl1.ogg'
speak_emote = list("growls")
emote_taunt = list("wails")
taunt_chance = 25
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
@@ -29,14 +31,9 @@
/mob/living/simple_animal/hostile/faithless/Process_Spacemove(var/movement_dir = 0)
return 1
/mob/living/simple_animal/hostile/faithless/FindTarget()
. = ..()
if(.)
custom_emote(1, "wails at [.]!")
/mob/living/simple_animal/hostile/faithless/AttackingTarget()
..()
if(iscarbon(target))
. = ..()
if(. && iscarbon(target))
var/mob/living/carbon/C = target
if(prob(12))
C.Weaken(3)
@@ -40,8 +40,8 @@
/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))
. = ..()
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, 0, inject_target, 0))
@@ -76,20 +76,19 @@
venom_per_bite = 10
move_to_delay = 5
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/handle_automated_action()
if(!..()) //AIStatus is off
return
/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(!busy && prob(1))
stop_automated_movement = 1
Goto(pick(orange(20, src)), move_to_delay)
Goto(pick(urange(20, src, 1)), move_to_delay)
spawn(50)
stop_automated_movement = 0
walk(src,0)
return 1
/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/GiveUp(var/C)
/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/GiveUp(C)
spawn(100)
if(busy == MOVING_TO_TARGET)
if(cocoon_target == C && get_dist(src,cocoon_target) > 1)
@@ -97,13 +96,13 @@
busy = 0
stop_automated_movement = 0
/mob/living/simple_animal/hostile/poison/giant_spider/nurse/handle_automated_action()
/mob/living/simple_animal/hostile/poison/giant_spider/nurse/handle_automated_movement() //Hacky and ugly.
if(..())
var/list/can_see = view(src, 10)
if(!busy && prob(30)) //30% chance to stop wandering and do something
//first, check for potential food nearby to cocoon
//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))
if(C.stat && !istype(C, /mob/living/simple_animal/hostile/poison/giant_spider) && !C.anchored)
cocoon_target = C
busy = MOVING_TO_TARGET
Goto(C, move_to_delay)
@@ -123,13 +122,14 @@
for(var/obj/O in can_see)
if(O.anchored)
continue
if(istype(O, /obj/item) || istype(O, /obj/structure) || istype(O, /obj/machinery))
cocoon_target = O
busy = MOVING_TO_TARGET
stop_automated_movement = 1
Goto(O, move_to_delay)
//give up if we can't reach them after 10 seconds
GiveUp(O)
if(isitem(O) || isstructure(O) || ismachinery(O))
cocoon_target = O
busy = MOVING_TO_TARGET
stop_automated_movement = 1
Goto(O, move_to_delay)
//give up if we can't reach them after 10 seconds
GiveUp(O)
else if(busy == MOVING_TO_TARGET && cocoon_target)
if(get_dist(src, cocoon_target) <= 1)
@@ -36,11 +36,11 @@
for(var/mob/living/L in T)
if(L == src || L == A)
continue
if(faction_check(L) && !attack_same)
if(faction_check_mob(L) && !attack_same)
return
visible_message("<span class='danger'><b>[src]</b> [ranged_message] at [A]!</span>")
throw_at(A, jumpdistance, jumpspeed, spin = FALSE, diagonals_first = TRUE)
ranged_cooldown = world.time + ranged_cooldown_time
ranged_cooldown = world.time + ranged_cooldown_time
/mob/living/simple_animal/hostile/headcrab/proc/Zombify(mob/living/carbon/human/H)
if(!H.check_death_method())
@@ -36,16 +36,14 @@
else if(mind) // Let's make this a feature
egg.origin = mind
for(var/obj/item/organ/internal/I in src)
I.loc = egg
I.forceMove(egg)
visible_message("<span class='warning'>[src] plants something in [victim]'s flesh!</span>", \
"<span class='danger'>We inject our egg into [victim]'s body!</span>")
egg_lain = 1
/mob/living/simple_animal/hostile/headslug/AttackingTarget()
if(egg_lain)
target.attack_animal(src)
return
if(iscarbon(target) && !issmall(target))
. = ..()
if(. && !egg_lain && iscarbon(target) && !issmall(target))
// Changeling egg can survive in aliens!
var/mob/living/carbon/C = target
if(C.stat == DEAD)
@@ -54,10 +52,7 @@
return
Infect(target)
to_chat(src, "<span class='userdanger'>With our egg laid, our death approaches rapidly...</span>")
spawn(100)
death()
return
target.attack_animal(src)
addtimer(CALLBACK(src, .proc/death), 100)
/obj/item/organ/internal/body_egg/changeling_egg
name = "changeling egg"
@@ -42,19 +42,16 @@
whisper_action.Grant(src)
/mob/living/simple_animal/hostile/hellhound/handle_automated_action()
. = ..()
if(!..())
return
if(resting)
if(!wants_to_rest())
custom_emote(1, "growls, and gets up.")
playsound(get_turf(src), 'sound/hallucinations/growl2.ogg', 50, 1)
icon_state = "[icon_living]"
resting = 0
update_canmove()
StopResting()
else if(wants_to_rest())
custom_emote(1, "lays down, and starts to lick their wounds.")
icon_state = "[icon_resting]"
resting = 1
update_canmove()
StartResting()
/mob/living/simple_animal/hostile/hellhound/examine(mob/user)
. = ..()
@@ -97,7 +94,7 @@
/mob/living/simple_animal/hostile/hellhound/AttackingTarget()
. = ..()
if(ishuman(target) && (!client || a_intent == INTENT_HARM))
if(. && ishuman(target) && (!client || a_intent == INTENT_HARM))
special_aoe()
/mob/living/simple_animal/hostile/hellhound/attackby(obj/item/C, mob/user, params)
@@ -36,7 +36,7 @@
/mob/living/simple_animal/hostile/hivebot/rapid
ranged = 1
rapid = 1
rapid = 3
retreat_distance = 5
minimum_distance = 5
@@ -2,60 +2,60 @@
faction = list("hostile")
stop_automated_movement_when_pulled = 0
obj_damage = 40
environment_smash = 1 //Set to 1 to break closets,tables,racks, etc; 2 for walls; 3 for rwalls
environment_smash = ENVIRONMENT_SMASH_STRUCTURES //Bitflags. Set to ENVIRONMENT_SMASH_STRUCTURES to break closets,tables,racks, etc; ENVIRONMENT_SMASH_WALLS for walls; ENVIRONMENT_SMASH_RWALLS for rwalls
var/atom/target
var/ranged = 0
var/rapid = 0
var/projectiletype
var/ranged = FALSE
var/rapid = 0 //How many shots per volley.
var/rapid_fire_delay = 2 //Time between rapid fire shots
var/dodging = FALSE
var/approaching_target = FALSE //We should dodge now
var/in_melee = FALSE //We should sidestep now
var/dodge_prob = 30
var/sidestep_per_cycle = 1 //How many sidesteps per npcpool cycle when in melee
var/projectiletype //set ONLY it and NULLIFY casingtype var, if we have ONLY projectile
var/projectilesound
var/casingtype
var/casingtype //set ONLY it and NULLIFY projectiletype, if we have projectile IN CASING
var/move_to_delay = 3 //delay for the automated movement.
var/list/friends = list()
var/list/emote_taunt = list()
var/taunt_chance = 0
var/rapid_melee = 1 //Number of melee attacks between each npc pool tick. Spread evenly.
var/melee_queue_distance = 4 //If target is close enough start preparing to hit them if we have rapid_melee enabled
var/ranged_message = "fires" //Fluff text for ranged mobs
var/ranged_cooldown = 0 //What the starting cooldown is on ranged attacks
var/ranged_cooldown = 0 //What the current cooldown on ranged attacks is, generally world.time + ranged_cooldown_time
var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is
var/ranged_ignores_vision = FALSE //if it'll fire ranged attacks even if it lacks vision on its target, only works with environment smash
var/check_friendly_fire = 0 // Should the ranged mob check for friendlies when shooting
var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat.
var/minimum_distance = 1 //Minimum approach distance, so ranged mobs chase targets down, but still keep their distance set in tiles to the target, set higher to make mobs keep distance
//These vars are related to how mobs locate and target
var/robust_searching = 0 //By default, mobs have a simple searching method, set this to 1 for the more scrutinous searching (stat_attack, stat_exclusive, etc), should be disabled on most mobs
var/vision_range = 9 //How big of an area to search for targets in, a vision of 9 attempts to find targets as soon as they walk into screen view
var/aggro_vision_range = 9 //If a mob is aggro, we search in this radius. Defaults to 9 to keep in line with original simple mob aggro radius
var/idle_vision_range = 9 //If a mob is just idling around, it's vision range is limited to this. Defaults to 9 to keep in line with original simple mob aggro radius
var/search_objects = 0 //If we want to consider objects when searching around, set this to 1. If you want to search for objects while also ignoring mobs until hurt, set it to 2. To completely ignore mobs, even when attacked, set it to 3
var/list/wanted_objects = list() //A list of objects that will be checked against to attack, should we have search_objects enabled
var/stat_attack = 0 //Mobs with stat_attack to 1 will attempt to attack things that are unconscious, Mobs with stat_attack set to 2 will attempt to attack the dead.
var/stat_exclusive = 0 //Mobs with this set to 1 will exclusively attack things defined by stat_attack, stat_attack 2 means they will only attack corpses
var/attack_same = 0 //Set us to 1 to allow us to attack our own faction, or 2, to only ever attack our own faction
//typecache of things this mob will attack in DestroySurroundings() if it has environment_smash
var/list/environment_target_typecache = list(
/obj/machinery/door/window,
/obj/structure/window,
/obj/structure/closet,
/obj/structure/table,
/obj/structure/grille,
/obj/structure/girder,
/obj/structure/rack,
/obj/structure/computerframe,
/obj/machinery/constructable_frame,
/obj/structure/barricade) //turned into a typecache in New()
var/search_objects_timer_id //Timer for regaining our old search_objects value after being attacked
var/search_objects_regain_time = 30 //the delay between being attacked and gaining our old search_objects value back
var/list/wanted_objects = list() //A typecache of objects types that will be checked against to attack, should we have search_objects enabled
var/stat_attack = CONSCIOUS //Mobs with stat_attack to UNCONSCIOUS will attempt to attack things that are unconscious, Mobs with stat_attack set to DEAD will attempt to attack the dead.
var/stat_exclusive = FALSE //Mobs with this set to TRUE will exclusively attack things defined by stat_attack, stat_attack DEAD means they will only attack corpses
var/attack_same = 0 //Set us to 1 to allow us to attack our own faction
var/atom/targets_from = null //all range/attack/etc. calculations should be done from this atom, defaults to the mob itself, useful for Vehicles and such
var/list/emote_taunt = list()
var/taunt_chance = 0
var/attack_all_objects = FALSE //if true, equivalent to having a wanted_objects list containing ALL objects.
var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach
var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever
var/attack_all_objects = FALSE //if true, equivalent to having a wanted_objects list containing ALL objects.
/mob/living/simple_animal/hostile/Initialize(mapload)
. = ..()
/mob/living/simple_animal/hostile/New()
..()
if(!targets_from)
targets_from = src
environment_target_typecache = typecacheof(environment_target_typecache)
wanted_objects = typecacheof(wanted_objects)
/mob/living/simple_animal/hostile/Destroy()
@@ -73,16 +73,46 @@
if(AIStatus == AI_OFF)
return 0
var/list/possible_targets = ListTargets() //we look around for potential targets and make it a list for later use.
if(environment_smash)
EscapeConfinement()
if(AICanContinue(possible_targets))
DestroySurroundings()
if(!QDELETED(target) && !targets_from.Adjacent(target))
DestroyPathToTarget()
if(!MoveToTarget(possible_targets)) //if we lose our target
if(AIShouldSleep(possible_targets)) // we try to acquire a new one
toggle_ai(AI_IDLE) // otherwise we go idle
return 1
/mob/living/simple_animal/hostile/handle_automated_movement()
. = ..()
if(dodging && target && in_melee && isturf(loc) && isturf(target.loc))
var/datum/cb = CALLBACK(src,.proc/sidestep)
if(sidestep_per_cycle > 1) //For more than one just spread them equally - this could changed to some sensible distribution later
var/sidestep_delay = SSnpcpool.wait / sidestep_per_cycle
for(var/i in 1 to sidestep_per_cycle)
addtimer(cb, (i - 1) * sidestep_delay)
else //Otherwise randomize it to make the players guessing.
addtimer(cb,rand(1, SSnpcpool.wait))
/mob/living/simple_animal/hostile/proc/sidestep()
if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD)
return
var/target_dir = get_dir(src, target)
var/static/list/cardinal_sidestep_directions = list(-90, -45, 0, 45, 90)
var/static/list/diagonal_sidestep_directions = list(-45, 0, 45)
var/chosen_dir = 0
if (target_dir & (target_dir - 1))
chosen_dir = pick(diagonal_sidestep_directions)
else
chosen_dir = pick(cardinal_sidestep_directions)
if(chosen_dir)
chosen_dir = turn(target_dir, chosen_dir)
Move(get_step(src, chosen_dir))
face_atom(target) //Looks better if they keep looking at you when dodging
/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user)
if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user)
FindTarget(list(user), 1)
@@ -97,7 +127,6 @@
//////////////HOSTILE MOB TARGETTING AND AGGRESSION////////////
/mob/living/simple_animal/hostile/proc/ListTargets()//Step 1, find out what we can see
if(!search_objects)
. = hearers(vision_range, targets_from) - src //Remove self, so we don't suicide
@@ -108,7 +137,11 @@
if(can_see(targets_from, HM, vision_range))
. += HM
else
. = oview(vision_range, targets_from)
. = list() // The following code is only very slightly slower than just returning oview(vision_range, targets_from), but it saves us much more work down the line, particularly when bees are involved
for(var/obj/A in oview(vision_range, targets_from))
. += A
for(var/mob/A in oview(vision_range, targets_from))
. += A
/mob/living/simple_animal/hostile/proc/FindTarget(var/list/possible_targets, var/HasTargetsList = 0)//Step 2, filter down possible targets to things we actually care about
. = list()
@@ -153,59 +186,61 @@
var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random
return chosen_target
/mob/living/simple_animal/hostile/CanAttack(var/atom/the_target)//Can we actually attack a possible target?
if(!the_target)
return 0
if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
return 0
// Please do not add one-off mob AIs here, but override this function for your mob
/mob/living/simple_animal/hostile/CanAttack(atom/the_target)//Can we actually attack a possible target?
if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids
return FALSE
if(ismob(the_target)) //Target is in godmode, ignore it.
var/mob/M = the_target
if(M.status_flags & GODMODE)
return FALSE
if(see_invisible < the_target.invisibility) //Target's invisible to us, forget it
return FALSE
if(search_objects < 2)
if(isliving(the_target))
var/mob/living/L = the_target
var/faction_check = faction_check(L)
var/faction_check = faction_check_mob(L)
if(robust_searching)
if(L.stat > stat_attack || L.stat != stat_attack && stat_exclusive == 1)
return 0
if(faction_check && !attack_same || !faction_check && attack_same == 2)
return 0
if(L in friends)
return 0
else
if(L.stat)
return 0
if(faction_check && !attack_same)
return 0
return 1
return FALSE
if(L.stat > stat_attack)
return FALSE
if(L in friends)
return FALSE
else
if((faction_check && !attack_same) || L.stat)
return FALSE
return TRUE
if(ishuman(the_target))
var/mob/living/carbon/human/H = the_target
if(is_type_in_list(src, H.dna.species.ignored_by))
return 0
if(istype(the_target, /obj/mecha))
if(ismecha(the_target))
var/obj/mecha/M = the_target
if(M.occupant)//Just so we don't attack empty mechs
if(CanAttack(M.occupant))
return 1
return TRUE
if(istype(the_target, /obj/spacepod))
if(isspacepod(the_target))
var/obj/spacepod/S = the_target
if(S.pilot) //Just so we don't attack empty pods
if(S.pilot)//Just so we don't attack empty pods
if(CanAttack(S.pilot))
return 1
return TRUE
if(istype(the_target, /obj/machinery/porta_turret))
var/obj/machinery/porta_turret/P = the_target
if(P.faction in faction)
return 0
return FALSE
if(!P.raised) //Don't attack invincible turrets
return 0
return FALSE
if(P.stat & BROKEN) //Or turrets that are already broken
return 0
return 1
return FALSE
return TRUE
if(isobj(the_target))
if(attack_all_objects || is_type_in_list(the_target, wanted_objects))
return 1
return 0
if(attack_all_objects || is_type_in_typecache(the_target, wanted_objects))
return TRUE
return FALSE
/mob/living/simple_animal/hostile/proc/GiveTarget(new_target)//Step 4, give us our selected target
target = new_target
@@ -215,19 +250,39 @@
Aggro()
return 1
/mob/living/simple_animal/hostile/proc/MoveToTarget(var/list/possible_targets)//Step 5, handle movement between us and our target
//What we do after closing in
/mob/living/simple_animal/hostile/proc/MeleeAction(patience = TRUE)
if(rapid_melee > 1)
var/datum/callback/cb = CALLBACK(src, .proc/CheckAndAttack)
var/delay = SSnpcpool.wait / rapid_melee
for(var/i in 1 to rapid_melee)
addtimer(cb, (i - 1)*delay)
else
AttackingTarget()
if(patience)
GainPatience()
/mob/living/simple_animal/hostile/proc/CheckAndAttack()
if(target && targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && !incapacitated())
AttackingTarget()
/mob/living/simple_animal/hostile/proc/MoveToTarget(list/possible_targets)//Step 5, handle movement between us and our target
stop_automated_movement = 1
if(!target || !CanAttack(target))
LoseTarget()
return 0
if(target in possible_targets)
if(target.z != z)
var/turf/T = get_turf(src)
if(target.z != T.z)
LoseTarget()
return 0
var/target_distance = get_dist(targets_from,target)
if(ranged) //We ranged? Shoot at em
if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown
OpenFire(target)
if(!Process_Spacemove()) //Drifting
walk(src,0)
return 1
if(retreat_distance != null) //If we have a retreat distance, check if we need to run from our target
if(target_distance <= retreat_distance) //If target's closer than our retreat distance, run
walk_away(src,target,retreat_distance,move_to_delay)
@@ -237,8 +292,11 @@
Goto(target,move_to_delay,minimum_distance)
if(target)
if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack
AttackingTarget()
GainPatience()
MeleeAction()
else
if(rapid_melee > 1 && target_distance <= melee_queue_distance)
MeleeAction(FALSE)
in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode
return 1
return 0
if(environment_smash)
@@ -256,14 +314,18 @@
return 0
/mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance)
if(target == src.target)
approaching_target = TRUE
else
approaching_target = FALSE
walk_to(src, target, minimum_distance, delay)
/mob/living/simple_animal/hostile/adjustHealth(damage)
/mob/living/simple_animal/hostile/adjustHealth(damage, updating_health = TRUE)
. = ..()
if(!ckey && !stat && search_objects < 3 && damage > 0)//Not unconscious, and we don't ignore mobs
if(search_objects)//Turn off item searching and ignore whatever item we were looking at, we're more concerned with fight or flight
search_objects = 0
target = null
LoseSearchObjects()
if(AIStatus != AI_ON && AIStatus != AI_OFF)
toggle_ai(AI_ON)
FindTarget()
@@ -271,7 +333,9 @@
FindTarget()
/mob/living/simple_animal/hostile/proc/AttackingTarget()
target.attack_animal(src)
SEND_SIGNAL(src, COMSIG_HOSTILE_ATTACKINGTARGET, target)
in_melee = TRUE
return target.attack_animal(src)
/mob/living/simple_animal/hostile/proc/Aggro()
vision_range = aggro_vision_range
@@ -281,11 +345,13 @@
/mob/living/simple_animal/hostile/proc/LoseAggro()
stop_automated_movement = 0
vision_range = idle_vision_range
vision_range = initial(vision_range)
taunt_chance = initial(taunt_chance)
/mob/living/simple_animal/hostile/proc/LoseTarget()
target = null
approaching_target = FALSE
in_melee = FALSE
walk(src, 0)
LoseAggro()
@@ -302,8 +368,7 @@
do_alert_animation(src)
playsound(loc, 'sound/machines/chime.ogg', 50, 1, -1)
for(var/mob/living/simple_animal/hostile/M in oview(distance, targets_from))
var/list/L = M.faction&faction
if(L.len)
if(faction_check_mob(M, TRUE))
if(M.AIStatus == AI_OFF)
return
else
@@ -323,21 +388,18 @@
return
visible_message("<span class='danger'><b>[src]</b> [ranged_message] at [A]!</span>")
if(rapid)
spawn(1)
Shoot(A)
spawn(4)
Shoot(A)
spawn(6)
Shoot(A)
if(rapid > 1)
var/datum/callback/cb = CALLBACK(src, .proc/Shoot, A)
for(var/i in 1 to rapid)
addtimer(cb, (i - 1)*rapid_fire_delay)
else
Shoot(A)
ranged_cooldown = world.time + ranged_cooldown_time
/mob/living/simple_animal/hostile/proc/Shoot(atom/targeted_atom)
if(targeted_atom == targets_from.loc || targeted_atom == targets_from)
if( QDELETED(targeted_atom) || targeted_atom == targets_from.loc || targeted_atom == targets_from )
return
var/turf/startloc = get_turf(targets_from)
if(casingtype)
var/obj/item/ammo_casing/casing = new casingtype(startloc)
@@ -354,32 +416,70 @@
if(AIStatus != AI_ON)//Don't want mindless mobs to have their movement screwed up firing in space
newtonian_move(get_dir(targeted_atom, targets_from))
P.original = targeted_atom
P.preparePixelProjectile(targeted_atom, get_turf(targeted_atom), src)
P.fire()
return P
/mob/living/simple_animal/hostile/proc/DestroySurroundings()
/mob/living/simple_animal/hostile/proc/CanSmashTurfs(turf/T)
return iswallturf(T) || ismineralturf(T)
/mob/living/simple_animal/hostile/Move(atom/newloc, dir , step_x , step_y)
if(dodging && approaching_target && prob(dodge_prob) && moving_diagonally == 0 && isturf(loc) && isturf(newloc))
return dodge(newloc, dir)
else
return ..()
/mob/living/simple_animal/hostile/proc/dodge(moving_to,move_direction)
//Assuming we move towards the target we want to swerve toward them to get closer
var/cdir = turn(move_direction, 45)
var/ccdir = turn(move_direction, -45)
dodging = FALSE
. = Move(get_step(loc,pick(cdir,ccdir)))
if(!.)//Can't dodge there so we just carry on
. = Move(moving_to,move_direction)
dodging = TRUE
/mob/living/simple_animal/hostile/proc/DestroyObjectsInDirection(direction)
var/turf/T = get_step(targets_from, direction)
if(QDELETED(T))
return
if(T.Adjacent(targets_from))
if(CanSmashTurfs(T))
T.attack_animal(src)
return
for(var/obj/O in T.contents)
if(!O.Adjacent(targets_from))
continue
if((ismachinery(O) || isstructure(O)) && O.density && environment_smash >= ENVIRONMENT_SMASH_STRUCTURES && !O.IsObscured())
O.attack_animal(src)
return
/mob/living/simple_animal/hostile/proc/DestroyPathToTarget()
if(environment_smash)
EscapeConfinement()
var/dir_to_target = get_dir(targets_from, target)
var/dir_list = list()
if(dir_to_target in diagonals) //it's diagonal, so we need two directions to hit
for(var/direction in cardinal)
if(direction & dir_to_target)
dir_list += direction
else
dir_list += dir_to_target
for(var/direction in dir_list) //now we hit all of the directions we got in this fashion, since it's the only directions we should actually need
DestroyObjectsInDirection(direction)
/mob/living/simple_animal/hostile/proc/DestroySurroundings() // for use with megafauna destroying everything around them
if(environment_smash)
EscapeConfinement()
for(var/dir in cardinal)
var/turf/T = get_step(targets_from, dir)
if(iswallturf(T) || ismineralturf(T))
if(T.Adjacent(targets_from))
T.attack_animal(src)
for(var/a in T)
var/atom/A = a
if(!A.Adjacent(targets_from))
continue
if(is_type_in_typecache(A, environment_target_typecache))
A.attack_animal(src)
return
DestroyObjectsInDirection(dir)
/mob/living/simple_animal/hostile/proc/EscapeConfinement()
if(buckled)
buckled.attack_animal(src)
if(!isturf(targets_from.loc) && targets_from.loc != null)//Did someone put us in something?
var/atom/A = get_turf(targets_from)
var/atom/A = targets_from.loc
A.attack_animal(src)//Bang on it till we get out
return
/mob/living/simple_animal/hostile/proc/FindHidden()
if(istype(target.loc, /obj/structure/closet) || istype(target.loc, /obj/machinery/disposal) || istype(target.loc, /obj/machinery/sleeper) || istype(target.loc, /obj/machinery/bodyscanner) || istype(target.loc, /obj/machinery/recharge_station))
@@ -420,9 +520,23 @@
LosePatience()
lose_patience_timer_id = addtimer(CALLBACK(src, .proc/LoseTarget), lose_patience_timeout, TIMER_STOPPABLE)
/mob/living/simple_animal/hostile/proc/LosePatience()
deltimer(lose_patience_timer_id)
//These two procs handle losing and regaining search_objects when attacked by a mob
/mob/living/simple_animal/hostile/proc/LoseSearchObjects()
search_objects = 0
deltimer(search_objects_timer_id)
search_objects_timer_id = addtimer(CALLBACK(src, .proc/RegainSearchObjects), search_objects_regain_time, TIMER_STOPPABLE)
/mob/living/simple_animal/hostile/proc/RegainSearchObjects(value)
if(!value)
value = initial(search_objects)
search_objects = value
/mob/living/simple_animal/hostile/consider_wakeup()
..()
var/list/tlist
@@ -455,4 +569,4 @@
if(isturf(M.loc))
. += M
else if(M.loc.type in hostile_machines)
. += M.loc
. += M.loc
@@ -46,8 +46,8 @@
/mob/living/simple_animal/hostile/illusion/AttackingTarget()
..()
if(istype(target, /mob/living) && prob(multiply_chance))
. = ..()
if(. && isliving(target) && prob(multiply_chance))
var/mob/living/L = target
if(L.stat == DEAD)
return
@@ -23,6 +23,9 @@
pixel_x = -16
see_in_dark = 8
emote_taunt = list("nashes")
taunt_chance = 20
harm_intent_damage = 8
melee_damage_lower = 15
melee_damage_upper = 15
@@ -33,18 +36,13 @@
var/stalk_tick_delay = 3
gold_core_spawnable = CHEM_MOB_SPAWN_HOSTILE
/mob/living/simple_animal/hostile/panther/FindTarget(var/list/possible_targets)
/mob/living/simple_animal/hostile/panther/AttackingTarget()
. = ..()
if(.)
emote("nashes at [.]")
/mob/living/simple_animal/hostile/panther/AttackingTarget()
. =..()
var/mob/living/L = .
if(istype(L))
if(prob(15))
L.Weaken(3)
L.visible_message("<span class='danger'>\the [src] knocks down \the [L]!</span>")
if(prob(15) && iscarbon(target))
var/mob/living/carbon/C = target
C.Weaken(3)
C.visible_message("<span class='danger'>\the [src] knocks down \the [C]!</span>")
//*******//
// Snake //
@@ -67,6 +65,9 @@
maxHealth = 25
health = 25
emote_taunt = list("hisses wickedly")
taunt_chance = 20
harm_intent_damage = 2
melee_damage_lower = 3
melee_damage_upper = 10
@@ -77,13 +78,9 @@
var/stalk_tick_delay = 3
gold_core_spawnable = CHEM_MOB_SPAWN_HOSTILE
/mob/living/simple_animal/hostile/snake/FindTarget()
. = ..()
if(.)
emote("hisses wickedly")
/mob/living/simple_animal/hostile/snake/AttackingTarget()
. =..()
var/mob/living/L = .
if(istype(L))
L.apply_damage(rand(3,12), TOX)
if(.)
if(iscarbon(target))
var/mob/living/carbon/C = target
C.apply_damage(rand(3, 12), TOX)
@@ -34,7 +34,7 @@ Difficulty: Medium
move_to_delay = 3
projectiletype = /obj/item/projectile/kinetic/miner
projectilesound = 'sound/weapons/kenetic_accel.ogg'
ranged = 1
ranged = TRUE
ranged_cooldown_time = 16
pixel_x = -16
crusher_loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator, /obj/item/crusher_trophy/miner_eye)
@@ -47,8 +47,12 @@ Difficulty: Medium
var/dashing = FALSE
var/dash_cooldown = 15
var/guidance = FALSE
var/transform_stop_attack = FALSE // stops the blood drunk miner from attacking after transforming his weapon until the next attack chain
deathmessage = "falls to the ground, decaying into glowing particles."
death_sound = "bodyfall"
attack_action_types = list(/datum/action/innate/megafauna_attack/dash,
/datum/action/innate/megafauna_attack/kinetic_accelerator,
/datum/action/innate/megafauna_attack/transform_weapon)
/obj/item/gps/internal/miner
icon_state = null
@@ -56,13 +60,52 @@ Difficulty: Medium
desc = "The sweet blood, oh, it sings to me."
invisibility = 100
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/guidance
guidance = TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/hunter/AttackingTarget()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Initialize(mapload)
. = ..()
if(. && prob(12))
INVOKE_ASYNC(src, .proc/dash)
miner_saw = new(src)
internal_gps = new/obj/item/gps/internal/miner(src)
// Add a zone selection UI; otherwise the mob can't melee attack properly.
zone_sel = new /obj/screen/zone_sel()
/datum/action/innate/megafauna_attack/dash
name = "Dash To Target"
icon_icon = 'icons/mob/actions/actions.dmi'
button_icon_state = "sniper_zoom"
chosen_message = "<span class='colossus'>You are now dashing to your target.</span>"
chosen_attack_num = 1
/datum/action/innate/megafauna_attack/kinetic_accelerator
name = "Fire Kinetic Accelerator"
icon_icon = 'icons/obj/guns/energy.dmi'
button_icon_state = "kineticgun"
chosen_message = "<span class='colossus'>You are now shooting your kinetic accelerator.</span>"
chosen_attack_num = 2
/datum/action/innate/megafauna_attack/transform_weapon
name = "Transform Weapon"
icon_icon = 'icons/obj/lavaland/artefacts.dmi'
button_icon_state = "cleaving_saw"
chosen_message = "<span class='colossus'>You are now transforming your weapon.</span>"
chosen_attack_num = 3
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/OpenFire()
if(client)
switch(chosen_attack)
if(1)
dash(target)
if(2)
shoot_ka()
if(3)
transform_weapon()
return
Goto(target, move_to_delay, minimum_distance)
if(get_dist(src, target) > MINER_DASH_RANGE && dash_cooldown <= world.time)
dash_attack()
else
shoot_ka()
transform_weapon()
/obj/item/melee/energy/cleaving_saw/miner //nerfed saw because it is very murdery
force = 6
@@ -79,15 +122,7 @@ Difficulty: Medium
icon_state = "ka_tracer"
range = MINER_DASH_RANGE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Initialize()
. = ..()
miner_saw = new(src)
internal_gps = new/obj/item/gps/internal/miner(src)
// Add a zone selection UI; otherwise the mob can't melee attack properly.
zone_sel = new /obj/screen/zone_sel()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/adjustHealth(amount, updating_health = TRUE)
var/adjustment_amount = amount * 0.1
if(world.time + adjustment_amount > next_move)
changeNext_move(adjustment_amount) //attacking it interrupts it attacking, but only briefly
@@ -104,16 +139,19 @@ Difficulty: Medium
return FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/ex_act(severity, target)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/ex_act(severity)
if(dash())
return
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/MeleeAction(patience = TRUE)
transform_stop_attack = FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/AttackingTarget()
if(QDELETED(target))
return
if(next_move > world.time || !Adjacent(target)) //some cheating
INVOKE_ASYNC(src, .proc/quick_attack_loop)
if(client)
transform_stop_attack = FALSE
if(QDELETED(target) || transform_stop_attack)
return
face_atom(target)
if(isliving(target))
@@ -132,8 +170,6 @@ Difficulty: Medium
miner_saw.melee_attack_chain(src, target)
if(guidance)
adjustHealth(-2)
transform_weapon()
INVOKE_ASYNC(src, .proc/quick_attack_loop)
return TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
@@ -146,16 +182,10 @@ Difficulty: Medium
. = ..()
if(. && target && !targets_the_same)
wander = TRUE
transform_weapon()
INVOKE_ASYNC(src, .proc/quick_attack_loop)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/OpenFire()
Goto(target, move_to_delay, minimum_distance)
if(get_dist(src, target) > MINER_DASH_RANGE && dash_cooldown <= world.time)
INVOKE_ASYNC(src, .proc/dash, target)
else
shoot_ka()
transform_weapon()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/dash_attack()
INVOKE_ASYNC(src, .proc/dash, target)
shoot_ka()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/shoot_ka()
if(ranged_cooldown <= world.time && get_dist(src, target) <= MINER_DASH_RANGE && !Adjacent(target))
@@ -166,21 +196,6 @@ Difficulty: Medium
Shoot(target)
changeNext_move(CLICK_CD_RANGE)
//I'm still of the belief that this entire proc needs to be wiped from existence.
// do not take my touching of it to be endorsement of it. ~mso
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/quick_attack_loop()
while(!QDELETED(target) && next_move <= world.time) //this is done this way because next_move can change to be sooner while we sleep.
stoplag(1)
sleep((next_move - world.time) * 1.5) //but don't ask me what the fuck this is about
if(QDELETED(target))
return
if(dashing || next_move > world.time || !Adjacent(target))
if(dashing && next_move <= world.time)
next_move = world.time + 1
INVOKE_ASYNC(src, .proc/quick_attack_loop) //lets try that again.
return
AttackingTarget()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/dash(atom/dash_target)
if(world.time < dash_cooldown)
return
@@ -189,7 +204,9 @@ Difficulty: Medium
var/turf/own_turf = get_turf(src)
if(!QDELETED(dash_target))
self_dist_to_target += get_dist(dash_target, own_turf)
for(var/turf/simulated/O in RANGE_TURFS(MINER_DASH_RANGE, own_turf))
for(var/turf/O in RANGE_TURFS(MINER_DASH_RANGE, own_turf))
if(O.density)
continue
var/turf_dist_to_target = 0
if(!QDELETED(dash_target))
turf_dist_to_target += get_dist(dash_target, O)
@@ -218,26 +235,29 @@ Difficulty: Medium
var/turf/step_forward_turf = get_step(own_turf, get_cardinal_dir(own_turf, target_turf))
new /obj/effect/temp_visual/small_smoke/halfsecond(step_back_turf)
new /obj/effect/temp_visual/small_smoke/halfsecond(step_forward_turf)
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc, src)
animate(D, alpha = 0, time = 5)
var/obj/effect/temp_visual/decoy/fading/halfsecond/D = new (own_turf, src)
forceMove(step_back_turf)
playsound(own_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1)
dashing = TRUE
alpha = 0
animate(src, alpha = 255, time = 5)
sleep(2)
SLEEP_CHECK_DEATH(2)
D.forceMove(step_forward_turf)
forceMove(target_turf)
playsound(target_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1)
sleep(1)
SLEEP_CHECK_DEATH(1)
dashing = FALSE
shoot_ka()
return TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/transform_weapon()
if(time_until_next_transform <= world.time)
miner_saw.transform_cooldown = 0
miner_saw.transform_weapon(src, TRUE)
if(!miner_saw.active)
rapid_melee = 5 // 4 deci cooldown before changes, npcpool subsystem wait is 20, 20/4 = 5
else
rapid_melee = 3 // same thing but halved (slightly rounded up)
transform_stop_attack = TRUE
icon_state = "miner[miner_saw.active ? "_transformed":""]"
icon_living = "miner[miner_saw.active ? "_transformed":""]"
time_until_next_transform = world.time + rand(50, 100)
@@ -263,4 +283,12 @@ Difficulty: Medium
sleep(4)
animate(src, alpha = 0, time = 6, easing = EASE_OUT, flags = ANIMATION_PARALLEL)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/guidance
guidance = TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/hunter/AttackingTarget()
. = ..()
if(. && prob(12))
INVOKE_ASYNC(src, .proc/dash)
#undef MINER_DASH_RANGE
@@ -122,12 +122,16 @@ Difficulty: Hard
/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget()
if(!charging)
..()
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Goto(target, delay, minimum_distance)
if(!charging)
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/MoveToTarget(list/possible_targets)
if(!charging)
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Move()
if(charging)
new /obj/effect/temp_visual/decoy/fading(loc, src)
@@ -195,7 +199,7 @@ Difficulty: Hard
. = list()
for(var/mob/living/L in targets)
var/list/bloodpool = get_pools(get_turf(L), 0)
if(bloodpool.len && (!faction_check(L) || L.stat == DEAD))
if(bloodpool.len && (!faction_check_mob(L) || L.stat == DEAD))
. += L
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/try_bloodattack()
@@ -246,7 +250,7 @@ Difficulty: Hard
new /obj/effect/temp_visual/bubblegum_hands/leftsmack(T)
sleep(2.5)
for(var/mob/living/L in T)
if(!faction_check(L))
if(!faction_check_mob(L))
to_chat(L, "<span class='userdanger'>[src] rends you!</span>")
playsound(T, attack_sound, 100, 1, -1)
var/limb_to_hit = pick("head", "chest", "r_arm", "l_arm", "r_leg", "l_leg")
@@ -262,7 +266,7 @@ Difficulty: Hard
new /obj/effect/temp_visual/bubblegum_hands/leftthumb(T)
sleep(6)
for(var/mob/living/L in T)
if(!faction_check(L))
if(!faction_check_mob(L))
to_chat(L, "<span class='userdanger'>[src] drags you through the blood!</span>")
playsound(T, 'sound/misc/enter_blood.ogg', 100, 1, -1)
var/turf/targetturf = get_step(src, dir)
@@ -62,14 +62,14 @@ Difficulty: Medium
return
..()
/mob/living/simple_animal/hostile/megafauna/dragon/adjustHealth(amount)
/mob/living/simple_animal/hostile/megafauna/dragon/adjustHealth(amount, updating_health = TRUE)
if(swooping)
return 0
return FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/dragon/AttackingTarget()
if(!swooping)
..()
return ..()
/mob/living/simple_animal/hostile/megafauna/dragon/DestroySurroundings()
if(!swooping)
@@ -123,13 +123,18 @@ Difficulty: Hard
adjustHealth(-L.maxHealth*0.5)
L.dust()
/mob/living/simple_animal/hostile/megafauna/hierophant/CanAttack(atom/the_target)
. = ..()
if(istype(the_target, /mob/living/simple_animal/hostile/asteroid/hivelordbrood)) //ignore temporary targets in favor of more permanent targets
return FALSE
/*/mob/living/simple_animal/hostile/megafauna/hierophant/GiveTarget(new_target)
var/targets_the_same = (new_target == target)
. = ..()
if(. && target && !targets_the_same)
visible_message("<span class='hierophant'>\"[pick(target_phrases)]\"</span>")*/
/mob/living/simple_animal/hostile/megafauna/hierophant/adjustHealth(amount)
/mob/living/simple_animal/hostile/megafauna/hierophant/adjustHealth(amount, updating_health = TRUE)
. = ..()
if(src && amount > 0 && !blinking)
wander = TRUE
@@ -140,7 +145,7 @@ Difficulty: Hard
if(target && isliving(target))
spawn(0)
melee_blast(get_turf(target)) //melee attacks on living mobs produce a 3x3 blast
..()
return ..()
/mob/living/simple_animal/hostile/megafauna/hierophant/DestroySurroundings()
if(!blinking)
@@ -536,7 +541,7 @@ Difficulty: Hard
/obj/effect/temp_visual/hierophant/blast/proc/do_damage(turf/T)
for(var/mob/living/L in T.contents - hit_things) //find and damage mobs...
hit_things += L
if((friendly_fire_check && caster && caster.faction_check(L)) || L.stat == DEAD)
if((friendly_fire_check && caster && caster.faction_check_mob(L)) || L.stat == DEAD)
continue
if(L.client)
flash_color(L.client, "#660099", 1)
@@ -551,7 +556,7 @@ Difficulty: Hard
for(var/obj/mecha/M in T.contents - hit_things) //and mechs.
hit_things += M
if(M.occupant)
if(friendly_fire_check && caster && caster.faction_check(M.occupant))
if(friendly_fire_check && caster && caster.faction_check_mob(M.occupant))
continue
to_chat(M.occupant, "<span class='userdanger'>Your [M.name] is struck by a [name]!</span>")
playsound(M,'sound/weapons/sear.ogg', 50, 1, -4)
@@ -46,7 +46,6 @@ Difficulty: Medium
loot = list(/obj/item/stack/sheet/bone = 3)
vision_range = 13
elimination = 1
idle_vision_range = 13
appearance_flags = 0
mouse_opacity = MOUSE_OPACITY_ICON
stat_attack = 1 // Overriden from /tg/ - otherwise Legion starts chasing its minions
@@ -56,8 +55,8 @@ Difficulty: Medium
internal_gps = new/obj/item/gps/internal/legion(src)
/mob/living/simple_animal/hostile/megafauna/legion/AttackingTarget()
..()
if(ishuman(target))
. = ..()
if(. && ishuman(target))
var/mob/living/L = target
if(L.stat == UNCONSCIOUS)
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc)
@@ -18,19 +18,8 @@
damage_coeff = list(BRUTE = 1, BURN = 0.5, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
minbodytemp = 0
maxbodytemp = INFINITY
vision_range = 5
aggro_vision_range = 18
idle_vision_range = 5
environment_target_typecache = list(
/obj/machinery/door/window,
/obj/structure/window,
/obj/structure/closet,
/obj/structure/table,
/obj/structure/grille,
/obj/structure/girder,
/obj/structure/rack,
/obj/structure/barricade,
/obj/machinery/field,
/obj/machinery/power/emitter)
var/list/crusher_loot
var/medal_type
var/score_type = BOSS_SCORE
@@ -43,10 +32,15 @@
mob_size = MOB_SIZE_LARGE
layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise
mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway
var/chosen_attack = 1 // chosen attack num
var/list/attack_action_types = list()
/mob/living/simple_animal/hostile/megafauna/New()
..()
apply_status_effect(STATUS_EFFECT_CRUSHERDAMAGETRACKING)
for(var/action_type in attack_action_types)
var/datum/action/innate/megafauna_attack/attack_action = new action_type()
attack_action.Grant(src)
/mob/living/simple_animal/hostile/megafauna/Destroy()
QDEL_NULL(internal_gps)
@@ -70,8 +64,8 @@
loot = crusher_loot
/mob/living/simple_animal/hostile/megafauna/AttackingTarget()
..()
if(isliving(target))
. = ..()
if(. && isliving(target))
var/mob/living/L = target
if(L.stat != DEAD)
if(!client && ranged && ranged_cooldown <= world.time)
@@ -124,3 +118,21 @@
SSmedals.SetScore(BOSS_SCORE, C, 1)
SSmedals.SetScore(score_type, C, 1)
return TRUE
/datum/action/innate/megafauna_attack
name = "Megafauna Attack"
icon_icon = 'icons/mob/actions/actions_animal.dmi'
button_icon_state = ""
var/mob/living/simple_animal/hostile/megafauna/M
var/chosen_message
var/chosen_attack_num = 0
/datum/action/innate/megafauna_attack/Grant(mob/living/L)
if(istype(L, /mob/living/simple_animal/hostile/megafauna))
M = L
return ..()
return FALSE
/datum/action/innate/megafauna_attack/Activate()
M.chosen_attack = chosen_attack_num
to_chat(M, chosen_message)
@@ -81,7 +81,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
new createtype(loc)
/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/adjustHealth(amount, updating_health = TRUE)
. = ..()
if(. > 0 && world.time > call_help_cooldown)
call_help_cooldown = world.time + call_help_cooldown_amt
@@ -17,7 +17,9 @@
melee_damage_upper = 12
attacktext = "attacks"
attack_sound = 'sound/weapons/bite.ogg'
emote_taunt = list("growls")
speak_emote = list("creaks")
taunt_chance = 30
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
@@ -29,11 +31,6 @@
gold_core_spawnable = CHEM_MOB_SPAWN_HOSTILE
del_on_death = 1
/mob/living/simple_animal/hostile/mimic/FindTarget()
. = ..()
if(.)
custom_emote(1, "growls at [.]")
/mob/living/simple_animal/hostile/mimic/emp_act(severity)
if(is_electronic)
switch(severity)
@@ -56,7 +53,7 @@
for(var/obj/item/I in loc)
I.loc = src
/mob/living/simple_animal/hostile/mimic/crate/DestroySurroundings()
/mob/living/simple_animal/hostile/mimic/crate/DestroyPathToTarget()
..()
if(prob(90))
icon_state = "[initial(icon_state)]open"
@@ -77,15 +74,19 @@
. = ..()
if(.)
icon_state = initial(icon_state)
if(prob(15) && iscarbon(target))
var/mob/living/carbon/C = target
C.Weaken(2)
C.visible_message("<span class='danger'>\The [src] knocks down \the [C]!</span>", "<span class='userdanger'>\The [src] knocks you down!</span>")
/mob/living/simple_animal/hostile/mimic/crate/proc/trigger()
if(!attempt_open)
visible_message("<b>[src]</b> starts to move!")
attempt_open = 1
/mob/living/simple_animal/hostile/mimic/crate/adjustHealth(damage)
/mob/living/simple_animal/hostile/mimic/crate/adjustHealth(amount, updating_health = TRUE)
trigger()
..(damage)
. = ..()
/mob/living/simple_animal/hostile/mimic/crate/LoseTarget()
..()
@@ -96,18 +97,10 @@
var/obj/structure/closet/crate/C = new(get_turf(src))
// Put loot in crate
for(var/obj/O in src)
O.loc = C
O.forceMove(C)
// due to `del_on_death`
return ..()
/mob/living/simple_animal/hostile/mimic/crate/AttackingTarget()
. =..()
var/mob/living/L = .
if(istype(L))
if(prob(15))
L.Weaken(2)
L.visible_message("<span class='danger'>\the [src] knocks down \the [L]!</span>")
var/global/list/protected_objects = list(/obj/structure/table, /obj/structure/cable, /obj/structure/window)
/mob/living/simple_animal/hostile/mimic/copy
@@ -191,14 +184,11 @@ var/global/list/protected_objects = list(/obj/structure/table, /obj/structure/ca
..()
/mob/living/simple_animal/hostile/mimic/copy/AttackingTarget()
..()
if(knockdown_people)
if(iscarbon(target))
var/mob/living/carbon/C = target
if(prob(15))
C.Weaken(2)
C.visible_message("<span class='danger'>\The [src] knocks down \the [C]!</span>", \
"<span class='userdanger'>\The [src] knocks you down!</span>")
. = ..()
if(knockdown_people && . && prob(15) && iscarbon(target))
var/mob/living/carbon/C = target
C.Weaken(2)
C.visible_message("<span class='danger'>\The [src] knocks down \the [C]!</span>", "<span class='userdanger'>\The [src] knocks you down!</span>")
/mob/living/simple_animal/hostile/mimic/copy/Aggro()
..()
@@ -26,8 +26,8 @@
a_intent = INTENT_HARM
speak_emote = list("chitters")
attack_sound = 'sound/weapons/bladeslice.ogg'
vision_range = 2
aggro_vision_range = 9
idle_vision_range = 2
turns_per_move = 5
loot = list(/obj/item/stack/ore/diamond{layer = 4.1},
/obj/item/stack/ore/diamond{layer = 4.1})
@@ -41,13 +41,10 @@
flag = "energy"
temperature = 50
/mob/living/simple_animal/hostile/asteroid/basilisk/GiveTarget(var/new_target)
/mob/living/simple_animal/hostile/asteroid/basilisk/GiveTarget(new_target)
if(..()) //we have a target
if(isliving(target))
var/mob/living/L = target
if(L.bodytemperature > 261)
L.bodytemperature = 261
visible_message("<span class='danger'>The [src.name]'s stare chills [L.name] to the bone!</span>")
if(isliving(target) && !target.Adjacent(targets_from) && ranged_cooldown <= world.time)//No more being shot at point blank or spammed with RNG beams
OpenFire(target)
/mob/living/simple_animal/hostile/asteroid/basilisk/ex_act(severity)
switch(severity)
@@ -8,8 +8,8 @@
icon_dead = "Goldgrub_dead"
icon_gib = "syndicate_gib"
vision_range = 2
vision_range = 2
aggro_vision_range = 9
idle_vision_range = 2
move_to_delay = 5
friendly = "harmlessly rolls into"
maxHealth = 45
@@ -48,14 +48,13 @@
retreat_distance = 10
minimum_distance = 10
if(will_burrow)
spawn(chase_time)
Burrow()
addtimer(CALLBACK(src, .proc/Burrow), chase_time)
/mob/living/simple_animal/hostile/asteroid/goldgrub/AttackingTarget()
if(istype(target, /obj/item/stack/ore))
EatOre(target)
return
..()
return ..()
/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/EatOre(var/atom/targeted_ore)
for(var/obj/item/stack/ore/O in get_turf(targeted_ore))
@@ -69,13 +68,13 @@
/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/Burrow()//Begin the chase to kill the goldgrub in time
if(!stat)
visible_message("<span class='danger'>The [src.name] buries into the ground, vanishing from sight!</span>")
visible_message("<span class='danger'>The [name] buries into the ground, vanishing from sight!</span>")
qdel(src)
/mob/living/simple_animal/hostile/asteroid/goldgrub/bullet_act(obj/item/projectile/P)
visible_message("<span class='danger'>The [P.name] was repelled by [src.name]'s girth!</span>")
return
/mob/living/simple_animal/hostile/asteroid/goldgrub/adjustHealth(damage)
idle_vision_range = 9
/mob/living/simple_animal/hostile/asteroid/goldgrub/adjustHealth(amount, updating_health = TRUE)
vision_range = 9
. = ..()
@@ -11,7 +11,6 @@
mouse_opacity = MOUSE_OPACITY_OPAQUE
move_to_delay = 40
ranged = TRUE
ranged_cooldown = 2 //By default, start the Goliath with his cooldown off so that people can run away quickly on first sight
ranged_cooldown_time = 120
friendly = "wails at"
speak_emote = list("bellows")
@@ -26,8 +25,8 @@
attacktext = "pulverizes"
attack_sound = 'sound/weapons/punch1.ogg'
throw_message = "does nothing to the rocky hide of the"
vision_range = 5
aggro_vision_range = 9
idle_vision_range = 5
move_force = MOVE_FORCE_VERY_STRONG
move_resist = MOVE_FORCE_VERY_STRONG
pull_force = MOVE_FORCE_VERY_STRONG
@@ -54,6 +53,8 @@
/mob/living/simple_animal/hostile/asteroid/goliath/OpenFire()
var/tturf = get_turf(target)
if(!isturf(tturf))
return
if(get_dist(src, target) <= 7)//Screen range check, so you can't get tentacle'd offscreen
visible_message("<span class='warning'>The [src.name] digs its tentacles under [target.name]!</span>")
new /obj/effect/temp_visual/goliath_tentacle/original(tturf, src)
@@ -61,10 +62,10 @@
icon_state = icon_aggro
pre_attack = FALSE
/mob/living/simple_animal/hostile/asteroid/goliath/adjustHealth(damage)
ranged_cooldown--
/mob/living/simple_animal/hostile/asteroid/goliath/adjustHealth(amount, updating_health = TRUE)
ranged_cooldown -= 10
handle_preattack()
..()
. = ..()
/mob/living/simple_animal/hostile/asteroid/goliath/Aggro()
vision_range = aggro_vision_range
@@ -188,4 +189,4 @@
/obj/effect/temp_visual/goliath_tentacle/proc/retract()
icon_state = "Goliath_tentacle_retract"
deltimer(timerid)
timerid = QDEL_IN(src, 5)
timerid = QDEL_IN(src, 7)
@@ -30,7 +30,7 @@
stop_automated_movement_when_pulled = TRUE
stat_exclusive = TRUE
robust_searching = TRUE
search_objects = TRUE
search_objects = 3
del_on_death = TRUE
loot = list(/obj/effect/decal/cleanable/blood/gibs)
deathmessage = "is pulped into bugmash."
@@ -38,7 +38,7 @@
animal_species = /mob/living/simple_animal/hostile/asteroid/gutlunch
childtype = list(/mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck = 45, /mob/living/simple_animal/hostile/asteroid/gutlunch/guthen = 55)
wanted_objects = list(/obj/effect/decal/cleanable/blood/gibs)
wanted_objects = list(/obj/effect/decal/cleanable/blood/gibs, /obj/item/organ/internal)
var/obj/item/udder/gutlunch/udder = null
/mob/living/simple_animal/hostile/asteroid/gutlunch/New()
@@ -61,16 +61,37 @@
udder.milkAnimal(O, user)
regenerate_icons()
else
..()
return ..()
/mob/living/simple_animal/hostile/asteroid/gutlunch/CanAttack(atom/the_target) // Gutlunch-specific version of CanAttack to handle stupid stat_exclusive = true crap so we don't have to do it for literally every single simple_animal/hostile except the two that spawn in lavaland
if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids
return FALSE
if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
return FALSE
if(isliving(the_target))
var/mob/living/L = the_target
if(faction_check_mob(L) && !attack_same)
return FALSE
if(L.stat > stat_attack || L.stat != stat_attack && stat_exclusive)
return FALSE
return TRUE
if(isobj(the_target) && is_type_in_typecache(the_target, wanted_objects))
return TRUE
return FALSE
/mob/living/simple_animal/hostile/asteroid/gutlunch/AttackingTarget()
if(is_type_in_typecache(target, wanted_objects)) //we eats
if(is_type_in_typecache(target,wanted_objects)) //we eats
udder.generateMilk()
regenerate_icons()
visible_message("<span class='notice'>[src] slurps up [target].</span>")
qdel(target)
return
..()
return ..()
/obj/item/udder/gutlunch
name = "nutrient sac"
@@ -11,8 +11,8 @@
move_to_delay = 14
ranged = 1
vision_range = 5
vision_range = 5
aggro_vision_range = 9
idle_vision_range = 5
speed = 3
maxHealth = 75
health = 75
@@ -44,6 +44,7 @@
/mob/living/simple_animal/hostile/asteroid/hivelord/AttackingTarget()
OpenFire()
return TRUE
/mob/living/simple_animal/hostile/asteroid/hivelord/spawn_crusher_loot()
loot += crusher_loot //we don't butcher
@@ -28,12 +28,14 @@
/mob/living/simple_animal/hostile/asteroid/Aggro()
..()
icon_state = icon_aggro
if(vision_range != aggro_vision_range)
icon_state = icon_aggro
/mob/living/simple_animal/hostile/asteroid/LoseAggro()
..()
if(stat == CONSCIOUS)
icon_state = icon_living
if(stat == DEAD)
return
icon_state = icon_living
/mob/living/simple_animal/hostile/asteroid/bullet_act(var/obj/item/projectile/P)//Reduces damage from most projectiles to curb off-screen kills
if(!stat)
@@ -1,225 +0,0 @@
/mob/living/simple_animal/hostile/asteroid/
vision_range = 2
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 15
faction = list("mining")
weather_immunities = list("lava","ash")
obj_damage = 30
environment_smash = 2
minbodytemp = 0
heat_damage_per_tick = 20
response_help = "pokes"
response_disarm = "shoves"
response_harm = "strikes"
status_flags = 0
a_intent = INTENT_HARM
var/throw_message = "bounces off of"
var/icon_aggro = null // for swapping to when we get aggressive
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
mob_size = MOB_SIZE_LARGE
/mob/living/simple_animal/hostile/asteroid/Aggro()
..()
icon_state = icon_aggro
/mob/living/simple_animal/hostile/asteroid/LoseAggro()
..()
if(stat == CONSCIOUS)
icon_state = icon_living
/mob/living/simple_animal/hostile/asteroid/bullet_act(var/obj/item/projectile/P)//Reduces damage from most projectiles to curb off-screen kills
if(!stat)
Aggro()
if(P.damage < 30 && P.damage_type != BRUTE)
P.damage = (P.damage / 3)
visible_message("<span class='danger'>The [P] has a reduced effect on [src]!</span>")
..()
/mob/living/simple_animal/hostile/asteroid/hitby(atom/movable/AM)//No floor tiling them to death, wiseguy
if(istype(AM, /obj/item))
var/obj/item/T = AM
if(!stat)
Aggro()
if(T.throwforce <= 20)
visible_message("<span class='notice'>The [T.name] [src.throw_message] [src.name]!</span>")
return
..()
/mob/living/simple_animal/hostile/asteroid/basilisk
name = "basilisk"
desc = "A territorial beast, covered in a thick shell that absorbs energy. Its stare causes victims to freeze from the inside."
icon = 'icons/mob/animal.dmi'
icon_state = "Basilisk"
icon_living = "Basilisk"
icon_aggro = "Basilisk_alert"
icon_dead = "Basilisk_dead"
icon_gib = "syndicate_gib"
move_to_delay = 20
projectiletype = /obj/item/projectile/temp/basilisk
projectilesound = 'sound/weapons/pierce.ogg'
ranged = 1
ranged_message = "stares"
ranged_cooldown_time = 30
throw_message = "does nothing against the hard shell of"
vision_range = 2
speed = 3
maxHealth = 200
health = 200
harm_intent_damage = 5
obj_damage = 60
melee_damage_lower = 12
melee_damage_upper = 12
attacktext = "bites into"
a_intent = INTENT_HARM
speak_emote = list("chitters")
attack_sound = 'sound/weapons/bladeslice.ogg'
aggro_vision_range = 9
idle_vision_range = 2
turns_per_move = 5
loot = list(/obj/item/stack/ore/diamond{layer = 4.1},
/obj/item/stack/ore/diamond{layer = 4.1})
/obj/item/projectile/temp/basilisk
name = "freezing blast"
icon_state = "ice_2"
damage = 0
damage_type = BURN
nodamage = 1
flag = "energy"
temperature = 50
/mob/living/simple_animal/hostile/asteroid/basilisk/GiveTarget(var/new_target)
if(..()) //we have a target
if(isliving(target))
var/mob/living/L = target
if(L.bodytemperature > 261)
L.bodytemperature = 261
visible_message("<span class='danger'>The [src.name]'s stare chills [L.name] to the bone!</span>")
/mob/living/simple_animal/hostile/asteroid/basilisk/ex_act(severity)
switch(severity)
if(1.0)
gib()
if(2.0)
adjustBruteLoss(140)
if(3.0)
adjustBruteLoss(110)
/mob/living/simple_animal/hostile/asteroid/goliath
name = "goliath"
desc = "A massive beast that uses long tentacles to ensare its prey, threatening them is not advised under any conditions."
icon = 'icons/mob/animal.dmi'
icon_state = "Goliath"
icon_living = "Goliath"
icon_aggro = "Goliath_alert"
icon_dead = "Goliath_dead"
icon_gib = "syndicate_gib"
attack_sound = 'sound/weapons/punch4.ogg'
mouse_opacity = MOUSE_OPACITY_OPAQUE
move_to_delay = 40
ranged = 1
ranged_cooldown = 2 //By default, start the Goliath with his cooldown off so that people can run away quickly on first sight
ranged_cooldown_time = 120
friendly = "wails at"
speak_emote = list("bellows")
vision_range = 4
speed = 3
maxHealth = 300
health = 300
harm_intent_damage = 1 //Only the manliest of men can kill a Goliath with only their fists.
obj_damage = 100
melee_damage_lower = 25
melee_damage_upper = 25
attacktext = "pulverizes"
attack_sound = 'sound/weapons/punch1.ogg'
throw_message = "does nothing to the rocky hide of the"
aggro_vision_range = 9
idle_vision_range = 5
move_force = MOVE_FORCE_VERY_STRONG
move_resist = MOVE_FORCE_VERY_STRONG
pull_force = MOVE_FORCE_VERY_STRONG
var/pre_attack = 0
loot = list(/obj/item/stack/sheet/animalhide/goliath_hide{layer = ABOVE_MOB_LAYER})
/mob/living/simple_animal/hostile/asteroid/goliath/Life()
..()
handle_preattack()
/mob/living/simple_animal/hostile/asteroid/goliath/proc/handle_preattack()
if(ranged_cooldown <= world.time + ranged_cooldown_time * 0.25 && !pre_attack)
pre_attack++
if(!pre_attack || stat || AIStatus == AI_IDLE)
return
icon_state = "Goliath_preattack"
/mob/living/simple_animal/hostile/asteroid/goliath/revive()
move_force = MOVE_FORCE_VERY_STRONG
move_resist = MOVE_FORCE_VERY_STRONG
pull_force = MOVE_FORCE_VERY_STRONG
..()
/mob/living/simple_animal/hostile/asteroid/goliath/OpenFire()
var/tturf = get_turf(target)
if(get_dist(src, target) <= 7)//Screen range check, so you can't get tentacle'd offscreen
visible_message("<span class='warning'>The [src.name] digs its tentacles under [target.name]!</span>")
new /obj/effect/goliath_tentacle/original(tturf)
ranged_cooldown = world.time + ranged_cooldown_time
icon_state = icon_aggro
pre_attack = 0
return
/mob/living/simple_animal/hostile/asteroid/goliath/adjustHealth(damage)
ranged_cooldown--
handle_preattack()
..()
/mob/living/simple_animal/hostile/asteroid/goliath/Aggro()
vision_range = aggro_vision_range
handle_preattack()
if(icon_state != icon_aggro)
icon_state = icon_aggro
return
/obj/effect/goliath_tentacle/
name = "Goliath tentacle"
icon = 'icons/mob/animal.dmi'
icon_state = "Goliath_tentacle"
var/latched = 0
/obj/effect/goliath_tentacle/New()
var/turftype = get_turf(src)
if(istype(turftype, /turf/simulated/mineral))
var/turf/simulated/mineral/M = turftype
M.gets_drilled()
spawn(20)
Trip()
/obj/effect/goliath_tentacle/original
/obj/effect/goliath_tentacle/original/New()
for(var/obj/effect/goliath_tentacle/original/O in loc)//No more GG NO RE from 2+ goliaths simultaneously tentacling you
if(O != src)
qdel(src)
var/list/directions = cardinal.Copy()
var/counter
for(counter = 1, counter <= 3, counter++)
var/spawndir = pick(directions)
directions -= spawndir
var/turf/T = get_step(src,spawndir)
new /obj/effect/goliath_tentacle(T)
..()
/obj/effect/goliath_tentacle/proc/Trip()
for(var/mob/living/M in src.loc)
M.Stun(5)
M.adjustBruteLoss(rand(10,15))
latched = 1
if(src && M)
visible_message("<span class='danger'>The [src.name] grabs hold of [M.name]!</span>")
if(!latched)
qdel(src)
else
spawn(50)
qdel(src)
@@ -61,21 +61,42 @@
health = maxHealth
..()
/mob/living/simple_animal/hostile/mushroom/adjustHealth(damage)//Possibility to flee from a fight just to make it more visually interesting
/mob/living/simple_animal/hostile/mushroom/CanAttack(atom/the_target) // Mushroom-specific version of CanAttack to handle stupid attack_same = 2 crap so we don't have to do it for literally every single simple_animal/hostile because this shit never gets spawned
if(!the_target || isturf(the_target) || istype(the_target, /atom/movable/lighting_object))
return FALSE
if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
return FALSE
if(isliving(the_target))
var/mob/living/L = the_target
if (!faction_check_mob(L) && attack_same == 2)
return FALSE
if(L.stat > stat_attack)
return FALSE
return TRUE
return FALSE
/mob/living/simple_animal/hostile/mushroom/adjustHealth(amount, updating_health = TRUE)//Possibility to flee from a fight just to make it more visually interesting
if(!retreat_distance && prob(33))
retreat_distance = 5
spawn(30)
retreat_distance = null
..()
addtimer(CALLBACK(src, .proc/stop_retreat), 30)
. = ..()
/mob/living/simple_animal/hostile/mushroom/attack_animal(var/mob/living/L)
/mob/living/simple_animal/hostile/mushroom/proc/stop_retreat()
retreat_distance = null
/mob/living/simple_animal/hostile/mushroom/attack_animal(mob/living/L)
if(istype(L, /mob/living/simple_animal/hostile/mushroom) && stat == DEAD)
var/mob/living/simple_animal/hostile/mushroom/M = L
if(faint_ticker < 2)
M.visible_message("<span class='notice'>[M] chews a bit on [src].</span>")
M.visible_message("[M] chews a bit on [src].")
faint_ticker++
return
M.visible_message("<span class='notice'>[M] devours [src]!</span>")
return TRUE
M.visible_message("<span class='warning'>[M] devours [src]!</span>")
var/level_gain = (powerlevel - M.powerlevel)
if(level_gain >= -1 && !bruised && !M.ckey)//Player shrooms can't level up to become robust gods.
if(level_gain < 1)//So we still gain a level if two mushrooms were the same level
@@ -83,7 +104,8 @@
M.LevelUp(level_gain)
M.adjustBruteLoss(-M.maxHealth)
qdel(src)
..()
return TRUE
return ..()
/mob/living/simple_animal/hostile/mushroom/revive()
..()
@@ -36,7 +36,7 @@
icon_dead = "piratemelee_dead"
projectilesound = 'sound/weapons/laser.ogg'
ranged = 1
rapid = 1
rapid = 2
retreat_distance = 5
minimum_distance = 5
projectiletype = /obj/item/projectile/beam
@@ -7,7 +7,7 @@
icon_living = "drone3"
icon_dead = "drone_dead"
ranged = 1
rapid = 1
rapid = 3
speak_chance = 5
turns_per_move = 3
response_help = "pokes the"
@@ -1,20 +1,20 @@
/mob/living/simple_animal/hostile/retaliate
var/list/enemies = list()
/mob/living/simple_animal/hostile/retaliate/Found(var/atom/A)
/mob/living/simple_animal/hostile/retaliate/Found(atom/A)
if(isliving(A))
var/mob/living/L = A
if(!L.stat)
return L
else
enemies -= L
else if(istype(A, /obj/mecha))
else if(ismecha(A))
var/obj/mecha/M = A
if(M.occupant)
return A
else if(istype(A, /obj/spacepod))
var/obj/spacepod/M = A
if(M.pilot)
else if(isspacepod(A))
var/obj/spacepod/S = A
if(S.pilot)
return A
/mob/living/simple_animal/hostile/retaliate/ListTargets()
@@ -25,48 +25,32 @@
return see
/mob/living/simple_animal/hostile/retaliate/proc/Retaliate()
..()
var/list/around = view(src, 7)
var/list/around = view(src, vision_range)
for(var/atom/movable/A in around)
if(A == src)
continue
if(isliving(A))
var/mob/living/M = A
var/faction_check = 0
for(var/F in faction)
if(F in M.faction)
faction_check = 1
break
if(faction_check && attack_same || !faction_check)
if(faction_check_mob(M) && attack_same || !faction_check_mob(M))
enemies |= M
else if(istype(A, /obj/mecha))
else if(ismecha(A))
var/obj/mecha/M = A
if(M.occupant)
enemies |= M
enemies |= M.occupant
else if(istype(A, /obj/spacepod))
var/obj/spacepod/M = A
if(M.pilot)
enemies |= M
enemies |= M.pilot
else if(isspacepod(A))
var/obj/spacepod/S = A
if(S.pilot)
enemies |= S
enemies |= S.pilot
for(var/mob/living/simple_animal/hostile/retaliate/H in around)
var/retaliate_faction_check = 0
for(var/F in faction)
if(F in H.faction)
retaliate_faction_check = 1
break
if(retaliate_faction_check && !attack_same && !H.attack_same)
if(faction_check_mob(H) && !attack_same && !H.attack_same)
H.enemies |= enemies
return 0
/mob/living/simple_animal/hostile/retaliate/adjustHealth(damage)
..(damage)
Retaliate()
/mob/living/simple_animal/hostile/retaliate/DestroySurroundings()
for(var/dir in cardinal) // North, South, East, West
var/obj/structure/obstacle = locate(/obj/structure, get_step(src, dir))
if(istype(obstacle, /obj/structure/closet) || istype(obstacle, /obj/structure/table))
obstacle.attack_animal(src)
/mob/living/simple_animal/hostile/retaliate/adjustHealth(amount, updating_health = TRUE)
. = ..()
if(amount > 0 && stat == CONSCIOUS)
Retaliate()
@@ -35,6 +35,9 @@
maxHealth = 20
health = 20
emote_taunt = list("wails")
taunt_chance = 20
harm_intent_damage = 10
melee_damage_lower = 2
melee_damage_upper = 3
@@ -51,11 +54,6 @@
/mob/living/simple_animal/hostile/retaliate/ghost/Process_Spacemove(var/check_drift = 0)
return 1
/mob/living/simple_animal/hostile/retaliate/ghost/FindTarget()
. = ..()
if(.)
custom_emote(1, "wails at [.]")
/mob/living/simple_animal/hostile/retaliate/ghost/Life(seconds, times_fired)
if(target)
invisibility = pick(0,0,60,invisibility)
@@ -33,6 +33,7 @@
ranged = 1
retreat_distance = 5
minimum_distance = 5
projectilesound = 'sound/weapons/gunshots/gunshot.ogg'
casingtype = /obj/item/ammo_casing/a357
loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, /obj/item/gun/projectile/revolver/mateba)
@@ -118,8 +118,9 @@
//Try to move onto target's turf and eat them
/mob/living/simple_animal/hostile/spaceWorm/wormHead/AttackingTarget()
..()
attemptToEat(target)
. = ..()
if(.)
attemptToEat(target)
//Attempt to eat things we bump into, Mobs, Walls, Clowns
/mob/living/simple_animal/hostile/spaceWorm/wormHead/Bump(atom/obstacle)
@@ -36,7 +36,6 @@
see_in_dark = 8
vision_range = 12
aggro_vision_range = 12
idle_vision_range = 12
search_objects = 1 // So that it can see through walls
@@ -87,11 +86,11 @@
if(can_be_seen(get_turf(loc)))
if(client)
to_chat(src, "<span class='warning'>You cannot attack, there are eyes on you!</span>")
return
return FALSE
else
..()
return ..()
/mob/living/simple_animal/hostile/statue/DestroySurroundings()
/mob/living/simple_animal/hostile/statue/DestroyPathToTarget()
if(!can_be_seen(get_turf(loc)))
..()
@@ -135,6 +135,8 @@
LoseTarget()
/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/handle_automated_action()
if(!..())
return
if(seen_enemy)
aggro_cycles++
if(alert_on_timeout && !raised_alert && aggro_cycles >= 60)
@@ -159,14 +161,13 @@
continue
if(depotarea.list_includes(body, depotarea.dead_list))
continue
if(faction_check(body))
if(faction_check_mob(body))
continue
say("Target [body]... terminated.")
depotarea.list_add(body, depotarea.dead_list)
pointed(body)
else
scan_cycles++
..()
/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/proc/raise_alert(var/reason)
if(istype(depotarea) && (!raised_alert || seen_revived_enemy) && !depotarea.used_self_destruct)
@@ -190,7 +191,7 @@
/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/CanPass(atom/movable/mover, turf/target, height=0)
if(isliving(mover))
var/mob/living/blocker = mover
if(faction_check(blocker))
if(faction_check_mob(blocker))
return 1
return ..(mover, target, height)
@@ -256,11 +257,12 @@
/mob/living/simple_animal/hostile/syndicate/ranged
ranged = 1
rapid = 1
rapid = 2
retreat_distance = 5
minimum_distance = 5
icon_state = "syndicateranged"
icon_living = "syndicateranged"
projectilesound = 'sound/weapons/gunshots/gunshot.ogg'
casingtype = /obj/item/ammo_casing/c45
loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/gun/projectile/automatic/c20r)
@@ -20,7 +20,7 @@
idle_ventcrawl_chance = 0
ai_playercontrol_allowtype = 0
ai_type = TS_AI_AGGRESSIVE
rapid = 1
rapid = 3
canlay = 1000
spider_tier = TS_TIER_5
projectiletype = /obj/item/projectile/terrorqueenspit/empress
@@ -23,7 +23,7 @@
regen_points_per_hp = 2 // 50% higher regen speed
stat_attack = 1 // ensures they will target people in crit, too!
wander = 0 // wandering defeats the purpose of stealth
idle_vision_range = 3 // very low idle vision range
vision_range = 3 // very low idle vision range
delay_web = 20 // double speed
web_type = /obj/structure/spider/terrorweb/gray
@@ -58,7 +58,7 @@
if(invisibility > 0)
GrayDeCloak()
else
..()
return ..()
/mob/living/simple_animal/hostile/poison/terror_spider/proc/GrayCloak()
visible_message("<span class='notice'>[src] hides in the vent.</span>")
@@ -67,7 +67,6 @@
icon_living = "terror_gray_cloaked"
if(!ckey)
vision_range = 3
idle_vision_range = 3
// Bugged, does not work yet. Also spams webs. Also doesn't look great. But... planned.
move_to_delay = 15 // while invisible, slow.
@@ -76,7 +75,6 @@
icon_state = "terror_gray"
icon_living = "terror_gray"
vision_range = 9
idle_vision_range = 9
move_to_delay = 5
prob_ai_hides_in_vents = 10
@@ -58,7 +58,7 @@
visible_message("<span class='notice'>[src] retracts its fangs a little.</span>")
melee_damage_lower = melee_damage_lower_rage1
melee_damage_upper = melee_damage_upper_rage1
..()
return ..()
/obj/structure/spider/terrorweb/red
@@ -11,7 +11,6 @@
layer = 2.75
health = 3
var/stillborn = FALSE
faction = list("terrorspiders")
var/spider_myqueen = null
var/spider_mymother = null
var/goto_mother = FALSE
@@ -169,10 +168,8 @@
if(!grow_as)
grow_as = pick(/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)
var/mob/living/simple_animal/hostile/poison/terror_spider/S = new grow_as(loc)
S.faction = faction
S.spider_myqueen = spider_myqueen
S.spider_mymother = spider_mymother
S.master_commander = master_commander
S.enemies = enemies
qdel(src)
@@ -186,10 +183,8 @@
var/obj/structure/spider/eggcluster/terror_eggcluster/C = new /obj/structure/spider/eggcluster/terror_eggcluster(get_turf(src))
C.spiderling_type = lay_type
C.spiderling_number = lay_number
C.faction = faction
C.spider_myqueen = spider_myqueen
C.spider_mymother = src
C.master_commander = master_commander
C.enemies = enemies
if(spider_growinstantly)
C.amount_grown = 250
@@ -202,7 +197,6 @@
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
faction = list("terrorspiders")
var/spider_myqueen = null
var/spider_mymother = null
var/spiderling_type = null
@@ -244,10 +238,8 @@
var/obj/structure/spider/spiderling/terror_spiderling/S = new /obj/structure/spider/spiderling/terror_spiderling(get_turf(src))
if(spiderling_type)
S.grow_as = spiderling_type
S.faction = faction
S.spider_myqueen = spider_myqueen
S.spider_mymother = spider_mymother
S.master_commander = master_commander
S.enemies = enemies
if(spider_growinstantly)
S.amount_grown = 250
@@ -92,7 +92,7 @@ var/global/list/ts_spiderling_list = list()
var/degenerate = 0 // if 1, they slowly degen until they all die off. Used by high-level abilities only.
// Vision
idle_vision_range = 10
vision_range = 10
aggro_vision_range = 10
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
@@ -364,7 +364,7 @@ var/global/list/ts_spiderling_list = list()
to_chat(T, "<span class='terrorspider'>TerrorSense: [msgtext]</span>")
/mob/living/simple_animal/hostile/poison/terror_spider/proc/CheckFaction()
if(faction.len != 1 || (!("terrorspiders" in faction)) || master_commander != null)
if(faction.len != 2 || (!("terrorspiders" in faction)) || master_commander != null)
to_chat(src, "<span class='userdanger'>Your connection to the hive mind has been severed!</span>")
log_runtime(EXCEPTION("Terror spider with incorrect faction list at: [atom_loc_line(src)]"))
gib()
@@ -24,6 +24,8 @@
attacktext = "bites"
attack_sound = 'sound/weapons/bite.ogg'
speak_emote = list("pines")
emote_taunt = list("growls")
taunt_chance = 20
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
@@ -34,15 +36,10 @@
deathmessage = "is hacked into pieces!"
del_on_death = 1
/mob/living/simple_animal/hostile/tree/FindTarget()
. = ..()
if(.)
custom_emote(1, "growls at [.]")
/mob/living/simple_animal/hostile/tree/AttackingTarget()
. =..()
var/mob/living/L = .
if(istype(L))
. = ..()
if(. && iscarbon(target))
var/mob/living/carbon/C = target
if(prob(15))
L.Weaken(3)
L.visible_message("<span class='danger'>\the [src] knocks down \the [L]!</span>")
C.Weaken(3)
C.visible_message("<span class='danger'>\the [src] knocks down \the [C]!</span>")
@@ -90,7 +90,7 @@
if(grasping.len < max_grasps)
grasping:
for(var/mob/living/L in view(grasp_range, src))
if(L == src || faction_check(L) || (L in grasping) || L == target)
if(L == src || faction_check_mob(L) || (L in grasping) || L == target)
continue
for(var/t in getline(src,L))
for(var/a in t)
@@ -105,6 +105,12 @@
/mob/living/simple_animal/hostile/venus_human_trap/OpenFire(atom/the_target)
for(var/turf/T in getline(src,target))
if (T.density)
return
for(var/obj/O in T)
if(O.density)
return
var/dist = get_dist(src,the_target)
Beam(the_target, "vine", time=dist*2, maxdistance=dist+2, beam_type=/obj/effect/ebeam/vine)
the_target.attack_animal(src)
@@ -117,7 +117,7 @@
maxHealth = 250
health = 250
ranged = 1
rapid = 1
rapid = 3
speed = 0 //he's lost some weight from the fighting
projectiletype = /obj/item/projectile/ornament
retreat_distance = 3
@@ -3,15 +3,17 @@
icon = 'icons/mob/animal.dmi'
health = 20
maxHealth = 20
gender = PLURAL //placeholder
universal_understand = 1
universal_speak = 0
status_flags = CANPUSH
var/icon_living = ""
var/icon_dead = ""
var/icon_resting = ""
var/icon_gib = null //We only try to show a gibbing animation if this exists.
var/healable = 1
var/flip_on_death = FALSE //Flip the sprite upside down on death. Mostly here for things lacking custom dead sprites.
var/list/speak = list()
var/speak_chance = 0
@@ -20,7 +22,6 @@
var/turns_per_move = 1
var/turns_since_move = 0
universal_speak = 0
var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals.
var/wander = 1 // Does the mob wander around when idle?
var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it.
@@ -38,11 +39,13 @@
var/heat_damage_per_tick = 3 //amount of damage applied if animal's body temperature is higher than maxbodytemp
var/cold_damage_per_tick = 2 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp
//Healable by medical stacks? Defaults to yes.
var/healable = 1
//Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage
var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum
var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above
//LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly
var/melee_damage_lower = 0
var/melee_damage_upper = 0
@@ -53,7 +56,7 @@
var/attacktext = "attacks"
var/attack_sound = null
var/friendly = "nuzzles" //If the mob does no damage with it's attack
var/environment_smash = 0 //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls
var/environment_smash = ENVIRONMENT_SMASH_NONE //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls
var/speed = 1 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster
var/can_hide = 0
@@ -66,6 +69,7 @@
var/next_scan_time = 0
var/animal_species //Sorry, no spider+corgi buttbabies.
var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against
var/gold_core_spawnable = CHEM_MOB_SPAWN_INVALID //if CHEM_MOB_SPAWN_HOSTILE can be spawned by plasma with gold core, CHEM_MOB_SPAWN_FRIENDLY are 'friendlies' spawned with blood
var/mob/living/carbon/human/master_commander = null //holding var for determining who own/controls a sentient simple animal (for sentience potions).
@@ -73,20 +77,35 @@
var/mob/living/simple_animal/hostile/spawner/nest
var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions
var/list/loot = list() //list of things spawned at mob's loc when it dies
var/del_on_death = 0 //causes mob to be deleted on death, useful for mobs that spawn lootable corpses
var/attacked_sound = "punch"
var/deathmessage = ""
var/death_sound = null //The sound played on death
var/allow_movement_on_non_turfs = FALSE
var/attacked_sound = "punch"
var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever)
var/can_have_ai = TRUE //once we have become sentient, we can never go back
var/shouldwakeup = FALSE //convenience var for forcibly waking up an idling AI on next check.
//domestication
var/tame = 0
/mob/living/simple_animal/Initialize()
..()
var/my_z // I don't want to confuse this with client registered_z
/mob/living/simple_animal/Initialize(mapload)
. = ..()
GLOB.simple_animals[AIStatus] += src
if(gender == PLURAL)
gender = pick(MALE, FEMALE)
if(!real_name)
real_name = name
if(!loc)
stack_trace("Simple animal being instantiated in nullspace")
verbs -= /mob/verb/observe
if(!can_hide)
verbs -= /mob/living/simple_animal/verb/hide
@@ -114,27 +133,25 @@
return ..()
/mob/living/simple_animal/Login()
if(src && src.client)
src.client.screen = list()
client.screen += client.void
..()
/mob/living/simple_animal/examine(mob/user)
. = ..()
if(stat == DEAD)
to_chat(user, "<span class='deadsay'>Upon closer examination, [p_they()] appear[p_s()] to be dead.</span>")
/mob/living/simple_animal/updatehealth(reason = "none given")
..(reason)
health = Clamp(health, 0, maxHealth)
med_hud_set_status()
/mob/living/simple_animal/lay_down()
/mob/living/simple_animal/StartResting(updating = 1)
..()
handle_resting_state_icons()
if(icon_resting && stat != DEAD)
icon_state = icon_resting
/mob/living/simple_animal/proc/handle_resting_state_icons()
if(icon_resting)
if(resting && stat != DEAD)
icon_state = icon_resting
else if(stat != DEAD)
icon_state = icon_living
/mob/living/simple_animal/StopResting(updating = 1)
..()
if(icon_resting && stat != DEAD)
icon_state = icon_living
/mob/living/simple_animal/update_stat(reason = "none given")
if(status_flags & GODMODE)
@@ -147,11 +164,13 @@
create_debug_log("died of damage, trigger reason: [reason]")
/mob/living/simple_animal/proc/handle_automated_action()
set waitfor = FALSE
return
/mob/living/simple_animal/proc/handle_automated_movement()
set waitfor = FALSE
if(!stop_automated_movement && wander)
if(isturf(src.loc) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc.
if((isturf(loc) || allow_movement_on_non_turfs) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc.
turns_since_move++
if(turns_since_move >= turns_per_move)
if(!(stop_automated_movement_when_pulled && pulledby)) //Soma animals don't move when pulled
@@ -159,11 +178,12 @@
if(Process_Spacemove(anydir))
Move(get_step(src,anydir), anydir)
turns_since_move = 0
return
return 1
/mob/living/simple_animal/proc/handle_automated_speech()
/mob/living/simple_animal/proc/handle_automated_speech(override)
set waitfor = FALSE
if(speak_chance)
if(rand(0,200) < speak_chance)
if(prob(speak_chance) || override)
if(speak && speak.len)
if((emote_hear && emote_hear.len) || (emote_see && emote_see.len))
var/length = speak.len
@@ -201,7 +221,7 @@
var/areatemp = get_temperature(environment)
if(abs(areatemp - bodytemperature) > 40 && !(BREATHLESS in mutations))
if(abs(areatemp - bodytemperature) > 5 && !(BREATHLESS in mutations))
var/diff = areatemp - bodytemperature
diff = diff / 5
bodytemperature += diff
@@ -242,30 +262,24 @@
atmos_suitable = 0
if(!atmos_suitable)
adjustBruteLoss(unsuitable_atmos_damage)
adjustHealth(unsuitable_atmos_damage)
handle_temperature_damage()
/mob/living/simple_animal/proc/handle_temperature_damage()
if(bodytemperature < minbodytemp)
adjustBruteLoss(cold_damage_per_tick)
else if(bodytemperature > maxbodytemp)
adjustBruteLoss(heat_damage_per_tick)
if((bodytemperature < minbodytemp) || (bodytemperature > maxbodytemp))
adjustHealth(unsuitable_atmos_damage)
/mob/living/simple_animal/gib()
if(icon_gib)
flick(icon_gib, src)
if(butcher_results)
var/atom/Tsec = drop_location()
for(var/path in butcher_results)
for(var/i = 1; i <= butcher_results[path];i++)
new path(src.loc)
for(var/i in 1 to butcher_results[path])
new path(Tsec)
..()
/mob/living/simple_animal/blob_act()
adjustBruteLoss(20)
return
/mob/living/simple_animal/emote(act, m_type = 1, message = null, force)
if(stat)
return
@@ -279,29 +293,13 @@
..()
/mob/living/simple_animal/bullet_act(var/obj/item/projectile/Proj)
if(!Proj)
return
if((Proj.damage_type != STAMINA))
adjustBruteLoss(Proj.damage)
Proj.on_hit(src, 0)
return FALSE
/mob/living/simple_animal/say_quote(message)
var/verb = "says"
/mob/living/simple_animal/attackby(obj/item/O, mob/living/user)
if(can_collar && !collar && istype(O, /obj/item/clothing/accessory/petcollar))
var/obj/item/clothing/accessory/petcollar/C = O
user.drop_item()
C.forceMove(src)
collar = C
collar.equipped(src)
regenerate_icons()
to_chat(usr, "<span class='notice'>You put \the [C] around \the [src]'s neck.</span>")
if(C.tagname)
name = C.tagname
real_name = C.tagname
return
else
..()
if(speak_emote.len)
verb = pick(speak_emote)
return verb
/mob/living/simple_animal/movement_delay()
. = ..()
@@ -312,21 +310,25 @@
/mob/living/simple_animal/Stat()
..()
if(statpanel("Status"))
stat(null, "Health: [round((health / maxHealth) * 100)]%")
return TRUE
statpanel("Status")
stat(null, "Health: [round((health / maxHealth) * 100)]%")
/mob/living/simple_animal/proc/drop_loot()
if(loot.len)
for(var/i in loot)
new i(loc)
/mob/living/simple_animal/death(gibbed)
// Only execute the below if we successfully died
. = ..()
if(!.)
return FALSE
flying = FALSE
if(nest)
nest.spawned_mobs -= src
nest = null
if(loot.len)
for(var/i in loot)
new i(loc)
drop_loot()
if(!gibbed)
if(death_sound)
playsound(get_turf(src),death_sound, 200, 1)
@@ -335,87 +337,41 @@
else if(!del_on_death)
visible_message("<span class='danger'>\The [src] stops moving...</span>")
if(del_on_death)
//Prevent infinite loops if the mob Destroy() is overridden in such
//a manner as to cause a call to death() again
del_on_death = FALSE
ghostize()
qdel(src)
else
health = 0
icon_state = icon_dead
if(flip_on_death)
transform = transform.Turn(180)
density = 0
lying = 1
/mob/living/simple_animal/ex_act(severity)
..()
switch(severity)
if(1.0)
gib()
return
if(2.0)
adjustBruteLoss(60)
if(3.0)
adjustBruteLoss(30)
/mob/living/simple_animal/proc/adjustHealth(amount, updating_health = TRUE)
if(status_flags & GODMODE)
return FALSE
var/oldbruteloss = bruteloss
bruteloss = Clamp(bruteloss + amount, 0, maxHealth)
if(oldbruteloss == bruteloss)
updating_health = FALSE
. = STATUS_UPDATE_NONE
else
. = STATUS_UPDATE_HEALTH
if(updating_health)
updatehealth()
/mob/living/simple_animal/adjustBruteLoss(amount, updating_health = TRUE)
if(damage_coeff[BRUTE])
return adjustHealth(amount * damage_coeff[BRUTE], updating_health)
/mob/living/simple_animal/adjustFireLoss(amount, updating_health = TRUE)
if(damage_coeff[BURN])
return adjustHealth(amount * damage_coeff[BURN], updating_health)
/mob/living/simple_animal/adjustOxyLoss(amount, updating_health = TRUE)
if(damage_coeff[OXY])
return adjustHealth(amount * damage_coeff[OXY], updating_health)
/mob/living/simple_animal/adjustToxLoss(amount, updating_health = TRUE)
if(damage_coeff[TOX])
return adjustHealth(amount * damage_coeff[TOX], updating_health)
/mob/living/simple_animal/adjustCloneLoss(amount, updating_health = TRUE)
if(damage_coeff[CLONE])
return adjustHealth(amount * damage_coeff[CLONE], updating_health)
/mob/living/simple_animal/adjustStaminaLoss(amount, updating_health = TRUE)
if(damage_coeff[STAMINA])
return ..(amount*damage_coeff[STAMINA], updating_health)
/mob/living/simple_animal/proc/CanAttack(var/atom/the_target)
/mob/living/simple_animal/proc/CanAttack(atom/the_target)
if(see_invisible < the_target.invisibility)
return FALSE
if(ismob(the_target))
var/mob/M = the_target
if(M.status_flags & GODMODE)
return FALSE
if(isliving(the_target))
var/mob/living/L = the_target
if(L.stat != CONSCIOUS)
return FALSE
if(istype(the_target, /obj/mecha))
if(ismecha(the_target))
var/obj/mecha/M = the_target
if(M.occupant)
return FALSE
if(istype(the_target,/obj/spacepod))
if(isspacepod(the_target))
var/obj/spacepod/S = the_target
if(S.pilot)
return FALSE
return TRUE
/mob/living/simple_animal/handle_fire()
return
/mob/living/simple_animal/update_fire()
return
return TRUE
/mob/living/simple_animal/IgniteMob()
return FALSE
@@ -423,32 +379,20 @@
/mob/living/simple_animal/ExtinguishMob()
return
/mob/living/simple_animal/update_transform()
var/matrix/ntransform = matrix(transform) //aka transform.Copy()
var/changed = 0
if(resize != RESIZE_DEFAULT_SIZE)
changed++
ntransform.Scale(resize)
resize = RESIZE_DEFAULT_SIZE
if(changed)
animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT)
/mob/living/simple_animal/revive()
..()
health = maxHealth
icon = initial(icon)
icon_state = icon_living
density = initial(density)
update_canmove()
flying = initial(flying)
/mob/living/simple_animal/proc/make_babies() // <3 <3 <3
if(gender != FEMALE || stat || next_scan_time > world.time || !childtype || !animal_species || !SSticker.IsRoundInProgress())
return FALSE
next_scan_time = world.time + 400
var/alone = TRUE
var/mob/living/simple_animal/partner
var/children = 0
@@ -472,42 +416,6 @@
if(target)
return new childspawn(target)
/mob/living/simple_animal/say_quote(var/message)
var/verb = "says"
if(speak_emote.len)
verb = pick(speak_emote)
return verb
/mob/living/simple_animal/update_canmove(delay_action_updates = 0)
if(paralysis || stunned || weakened || stat || resting)
drop_r_hand()
drop_l_hand()
canmove = 0
else if(buckled)
canmove = 0
else
canmove = 1
update_transform()
if(!delay_action_updates)
update_action_buttons_icon()
return canmove
/mob/living/simple_animal/update_transform()
var/matrix/ntransform = matrix(transform) //aka transform.Copy()
var/changed = 0
if(resize != RESIZE_DEFAULT_SIZE)
changed++
ntransform.Scale(resize)
resize = RESIZE_DEFAULT_SIZE
if(changed)
animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT)
/* Inventory */
/mob/living/simple_animal/show_inv(mob/user as mob)
if(!can_collar)
return
@@ -572,50 +480,39 @@
if(collar)
. |= collar.GetAccess()
/* End Inventory */
/mob/living/simple_animal/update_canmove(delay_action_updates = 0)
if(paralysis || stunned || weakened || stat || resting)
drop_r_hand()
drop_l_hand()
canmove = 0
else if(buckled)
canmove = 0
else
canmove = 1
if(!canmove)
walk(src, 0) //stop mid walk
update_transform()
if(!delay_action_updates)
update_action_buttons_icon()
return canmove
/mob/living/simple_animal/update_transform()
var/matrix/ntransform = matrix(transform) //aka transform.Copy()
var/changed = FALSE
if(resize != RESIZE_DEFAULT_SIZE)
changed = TRUE
ntransform.Scale(resize)
resize = RESIZE_DEFAULT_SIZE
if(changed)
animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT)
/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion
toggle_ai(AI_OFF)
can_have_ai = FALSE
return
/mob/living/simple_animal/can_hear()
. = TRUE
/mob/living/simple_animal/proc/consider_wakeup()
if(pulledby || shouldwakeup)
toggle_ai(AI_ON)
/mob/living/simple_animal/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
. = ..()
if(!ckey && !stat)//Not unconscious
if(AIStatus == AI_IDLE)
toggle_ai(AI_ON)
/mob/living/simple_animal/proc/toggle_ai(togglestatus)
if(!can_have_ai && (togglestatus != AI_OFF))
return
if(AIStatus != togglestatus)
if(togglestatus > 0 && togglestatus < 5)
if(togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF)
var/turf/T = get_turf(src)
if(AIStatus == AI_Z_OFF)
SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src
else
SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src
GLOB.simple_animals[AIStatus] -= src
GLOB.simple_animals[togglestatus] += src
AIStatus = togglestatus
else
stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]")
/mob/living/simple_animal/onTransitZ(old_z, new_z)
..()
if(AIStatus == AI_Z_OFF)
SSidlenpcpool.idle_mobs_by_zlevel[old_z] -= src
toggle_ai(initial(AIStatus))
// Simple animals will not be given night vision upon death, as that would result in issues when they are revived.
/mob/living/simple_animal/grant_death_vision()
sight |= SEE_TURFS
sight |= SEE_MOBS
@@ -642,4 +539,37 @@
return
SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT)
sync_lighting_plane_alpha()
sync_lighting_plane_alpha()
/mob/living/simple_animal/proc/toggle_ai(togglestatus)
if(!can_have_ai && (togglestatus != AI_OFF))
return
if(AIStatus != togglestatus)
if(togglestatus > 0 && togglestatus < 5)
if(togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF)
var/turf/T = get_turf(src)
if(AIStatus == AI_Z_OFF)
SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src
else
SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src
GLOB.simple_animals[AIStatus] -= src
GLOB.simple_animals[togglestatus] += src
AIStatus = togglestatus
else
stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]")
/mob/living/simple_animal/proc/consider_wakeup()
if(pulledby || shouldwakeup)
toggle_ai(AI_ON)
/mob/living/simple_animal/adjustHealth(amount, updating_health = TRUE)
. = ..()
if(!ckey && !stat)//Not unconscious
if(AIStatus == AI_IDLE)
toggle_ai(AI_ON)
/mob/living/simple_animal/onTransitZ(old_z, new_z)
..()
if(AIStatus == AI_Z_OFF)
SSidlenpcpool.idle_mobs_by_zlevel[old_z] -= src
toggle_ai(initial(AIStatus))
-3
View File
@@ -56,9 +56,6 @@
if(abilities)
client.verbs |= abilities
if(ishuman(src))
client.screen += client.void
//HUD updates (antag hud, etc)
//readd this mob's HUDs (antag, med, etc)
reload_huds()
-6
View File
@@ -1234,12 +1234,6 @@ var/list/slot_equipment_priority = list( \
/mob/proc/get_access()
return list() //must return list or IGNORE_ACCESS
/mob/proc/faction_check(mob/target)
for(var/F in faction)
if(F in target.faction)
return 1
return 0
/mob/proc/create_attack_log(text, collapse = TRUE)
LAZYINITLIST(attack_log)
create_log_in_list(attack_log, text, collapse, last_log)
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 138 KiB

+1
View File
@@ -1904,6 +1904,7 @@
#include "code\modules\mob\living\simple_animal\animal_defense.dm"
#include "code\modules\mob\living\simple_animal\constructs.dm"
#include "code\modules\mob\living\simple_animal\corpse.dm"
#include "code\modules\mob\living\simple_animal\damage_procs.dm"
#include "code\modules\mob\living\simple_animal\parrot.dm"
#include "code\modules\mob\living\simple_animal\posessed_object.dm"
#include "code\modules\mob\living\simple_animal\powers.dm"