mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-23 21:12:24 +01:00
Bear Stuff (#877)
bugfix: "Space bears can now control their movement in space." rscadd: "Bears are now stronger in space or low pressure, weaker in pressurised environments" soundadd: "Rawr!" rscadd: "Added more bears!" rscadd: "Added a new severe event."
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
/datum/event/bear_attack
|
||||
startWhen = 2
|
||||
announceWhen = 20
|
||||
|
||||
var/list/possible_turfs = list()
|
||||
var/spawn_type
|
||||
var/spawn_number
|
||||
|
||||
/datum/event/bear_attack/setup()
|
||||
for(var/areapath in typesof(/area/maintenance))
|
||||
var/area/A = locate(areapath)
|
||||
for(var/turf/simulated/floor/F in A.contents)
|
||||
if(turf_clear(F))
|
||||
possible_turfs |= F
|
||||
|
||||
if (severity <= EVENT_LEVEL_MODERATE)//Moderate spacebear event disabled by head developer veto
|
||||
spawn_type = /mob/living/simple_animal/hostile/bear
|
||||
spawn_number = rand(8,16)
|
||||
else
|
||||
spawn_type = /mob/living/simple_animal/hostile/bear/spatial
|
||||
spawn_number = rand(4,6)
|
||||
|
||||
|
||||
/datum/event/bear_attack/start()
|
||||
var/i
|
||||
for (i = 0,i < spawn_number, i++)
|
||||
new spawn_type(pick(possible_turfs))
|
||||
|
||||
/datum/event/bear_attack/announce()
|
||||
if (severity <= EVENT_LEVEL_MODERATE)
|
||||
command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg')
|
||||
else
|
||||
command_announcement.Announce("Highly dangerous bioweapons have escaped from a nearby research facility and boarded [station_name()]. Station security is advised to be on high alert.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg')
|
||||
@@ -165,6 +165,7 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
|
||||
new /datum/event_meta(EVENT_LEVEL_MODERATE, "Spider Infestation", /datum/event/spider_infestation, 50, list(ASSIGNMENT_SECURITY = 25)),
|
||||
new /datum/event_meta(EVENT_LEVEL_MODERATE, "Viral Infection", /datum/event/viral_infection, 0, list(ASSIGNMENT_MEDICAL = 12), 1),
|
||||
new /datum/event_meta(EVENT_LEVEL_MODERATE, "Major Vermin Infestation", /datum/event/infestation, 60, list(ASSIGNMENT_JANITOR = 15, ASSIGNMENT_SECURITY = 15)),
|
||||
|
||||
)
|
||||
|
||||
/datum/event_container/major
|
||||
@@ -176,6 +177,8 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
|
||||
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Meteor Wave", /datum/event/meteor_wave, 40, list(ASSIGNMENT_ENGINEER = 10),1),
|
||||
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Space Vines", /datum/event/spacevine, 50, list(ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_GARDENER = 20), 1),
|
||||
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Viral Infection", /datum/event/viral_infection, 20, list(ASSIGNMENT_MEDICAL = 13), 1),
|
||||
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Bluespace Bears", /datum/event/bear_attack, 60, list(ASSIGNMENT_SECURITY = 10), 1),
|
||||
|
||||
)
|
||||
|
||||
//NOTE: Re added nothing option, but with fairly low weight
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
var/list/antibodies = list()
|
||||
var/last_eating = 0 //Not sure what this does... I found it hidden in food.dm
|
||||
|
||||
var/life_tick = 0 // The amount of life ticks that have processed on this mob.
|
||||
|
||||
var/analgesic = 0 // when this is set, the mob isn't affected by shock or pain
|
||||
// life should decrease this by 1 every tick
|
||||
// total amount of wounds on mob, used to spread out healing and the like over all wounds
|
||||
|
||||
@@ -189,14 +189,14 @@
|
||||
// End BS12 momentum-transfer code.
|
||||
|
||||
/mob/living/attack_generic(var/mob/user, var/damage, var/attack_message)
|
||||
|
||||
if(!damage)
|
||||
return
|
||||
|
||||
adjustBruteLoss(damage)
|
||||
user.attack_log += text("\[[time_stamp()]\] <font color='red'>attacked [src.name] ([src.ckey])</font>")
|
||||
src.attack_log += text("\[[time_stamp()]\] <font color='orange'>was attacked by [user.name] ([user.ckey])</font>")
|
||||
src.visible_message("<span class='danger'>[user] has [attack_message] [src]!</span>")
|
||||
if (attack_message)
|
||||
src.visible_message("<span class='danger'>[user] has [attack_message] [src]!</span>")
|
||||
user.do_attack_animation(src)
|
||||
spawn(1) updatehealth()
|
||||
return 1
|
||||
|
||||
@@ -46,4 +46,5 @@
|
||||
var/mouth_size = 2//how large of a creature it can swallow at once, and how big of a bite it can take out of larger things
|
||||
var/eat_types = 0//This is a bitfield which must be initialised in New(). The valid values for it are in devour.dm
|
||||
var/datum/reagents/metabolism/ingested = null
|
||||
var/underdoor //Used for mobs that can walk through maintenance hatches - drones, pais, and spiderbots
|
||||
var/underdoor //Used for mobs that can walk through maintenance hatches - drones, pais, and spiderbots
|
||||
var/life_tick = 0 // The amount of life ticks that have processed on this mob.
|
||||
@@ -1,7 +1,10 @@
|
||||
#define BEARMODE_INDOORS 1
|
||||
#define BEARMODE_SPACE 2
|
||||
|
||||
//Space bears!
|
||||
/mob/living/simple_animal/hostile/bear
|
||||
name = "space bear"
|
||||
desc = "RawrRawr!!"
|
||||
desc = "You should walk away, quickly!"
|
||||
icon_state = "bear"
|
||||
icon_living = "bear"
|
||||
icon_dead = "bear_dead"
|
||||
@@ -10,19 +13,42 @@
|
||||
speak_emote = list("growls", "roars")
|
||||
emote_hear = list("rawrs","grumbles","grawls")
|
||||
emote_see = list("stares ferociously", "stomps")
|
||||
speak_chance = 1
|
||||
turns_per_move = 5
|
||||
speak_chance = 10
|
||||
turns_per_move = 7
|
||||
see_in_dark = 6
|
||||
meat_type = /obj/item/weapon/reagent_containers/food/snacks/bearmeat
|
||||
response_help = "pets"
|
||||
response_disarm = "gently pushes aside"
|
||||
response_harm = "pokes"
|
||||
stop_automated_movement_when_pulled = 0
|
||||
maxHealth = 60
|
||||
health = 60
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 30
|
||||
maxHealth = 80
|
||||
melee_damage_lower = 16
|
||||
melee_damage_upper = 24
|
||||
break_stuff_probability = 80
|
||||
mob_size = 17
|
||||
var/turns_since_hit = 0//If the bear chases someone too long without hitting them, it will try to change to another nearby target instead
|
||||
|
||||
attacktext = null//This allows custom attacking emotes
|
||||
|
||||
var/quiet_sounds = list('sound/effects/creatures/bear_quiet_1.ogg',
|
||||
'sound/effects/creatures/bear_quiet_2.ogg',
|
||||
'sound/effects/creatures/bear_quiet_3.ogg',
|
||||
'sound/effects/creatures/bear_quiet_4.ogg',
|
||||
'sound/effects/creatures/bear_quiet_5.ogg',
|
||||
'sound/effects/creatures/bear_quiet_6.ogg')
|
||||
var/loud_sounds = list('sound/effects/creatures/bear_loud_1.ogg',
|
||||
'sound/effects/creatures/bear_loud_2.ogg',
|
||||
'sound/effects/creatures/bear_loud_3.ogg',
|
||||
'sound/effects/creatures/bear_loud_4.ogg')
|
||||
var/bearmode
|
||||
//Bears in space, or other low-pressure environments are stronger. Higher health, faster movement, longer aggression before being tired
|
||||
|
||||
var/anger
|
||||
//An angry bear will immediately attack anyone it sees without warning
|
||||
//Anger decreases at 1 point per minute
|
||||
//Any amount of anger causes instant aggro, quantity of it is only a duration
|
||||
|
||||
var/health_last_tick = 0
|
||||
|
||||
//Space bears aren't affected by atmos.
|
||||
min_oxy = 0
|
||||
@@ -38,6 +64,9 @@
|
||||
|
||||
faction = "russian"
|
||||
|
||||
|
||||
|
||||
|
||||
//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!!
|
||||
/mob/living/simple_animal/hostile/bear/Hudson
|
||||
name = "Hudson"
|
||||
@@ -46,128 +75,466 @@
|
||||
response_disarm = "gently pushes aside"
|
||||
response_harm = "pokes"
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/New()
|
||||
..()
|
||||
update_bearmode()
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/proc/set_stance(var/input)
|
||||
var/previous = stance
|
||||
stance = input
|
||||
if (stance != previous)
|
||||
stance_step = 0
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/Life()
|
||||
. =..()
|
||||
if(!.)
|
||||
if (stat != CONSCIOUS)
|
||||
return
|
||||
|
||||
if(loc && istype(loc,/turf/space))
|
||||
icon_state = "bear"
|
||||
else
|
||||
icon_state = "bearfloor"
|
||||
if (life_tick % 5 == 0)
|
||||
update_bearmode()
|
||||
|
||||
|
||||
//This covers cases where the bear is damaged by things it can't detect.
|
||||
//Most notably, security bots, beepsky kept murdering them without resistance
|
||||
if (health < health_last_tick && stance != HOSTILE_STANCE_TIRED)
|
||||
anger++
|
||||
instant_aggro()
|
||||
|
||||
if (life_tick % 30 == 0)//A point of anger wears off per minute
|
||||
anger = max(0, anger-1)
|
||||
|
||||
switch(stance)
|
||||
|
||||
if(HOSTILE_STANCE_TIRED)
|
||||
stop_automated_movement = 1
|
||||
stance_step++
|
||||
if(stance_step >= 10) //rests for 10 ticks
|
||||
if(stance_step >= 15) //rests for 10 ticks
|
||||
if(target_mob && target_mob in ListTargets(10))
|
||||
stance = HOSTILE_STANCE_ATTACK //If the mob he was chasing is still nearby, resume the attack, otherwise go idle.
|
||||
set_stance(HOSTILE_STANCE_ATTACK) //If the mob he was chasing is still nearby, resume the attack, otherwise go idle.
|
||||
else
|
||||
stance = HOSTILE_STANCE_IDLE
|
||||
set_stance(HOSTILE_STANCE_IDLE)
|
||||
|
||||
if(HOSTILE_STANCE_ALERT)
|
||||
stop_automated_movement = 1
|
||||
var/found_mob = 0
|
||||
if(target_mob && target_mob in ListTargets(10))
|
||||
if(!(SA_attackable(target_mob)))
|
||||
stance_step = max(0, stance_step) //If we have not seen a mob in a while, the stance_step will be negative, we need to reset it to 0 as soon as we see a mob again.
|
||||
stance_step++
|
||||
found_mob = 1
|
||||
src.set_dir(get_dir(src,target_mob)) //Keep staring at the mob
|
||||
if(target_mob && (target_mob in ListTargets(10)) && !(SA_attackable(target_mob)))
|
||||
found_mob = 1
|
||||
else
|
||||
LoseTarget()
|
||||
|
||||
if(stance_step in list(1,4,7)) //every 3 ticks
|
||||
var/action = pick( list( "growls at [target_mob]", "stares angrily at [target_mob]", "prepares to attack [target_mob]", "closely watches [target_mob]" ) )
|
||||
if(action)
|
||||
custom_emote(1,action)
|
||||
if(!found_mob)
|
||||
if(target_mob)
|
||||
found_mob = 1
|
||||
|
||||
|
||||
if (found_mob)
|
||||
stance_step = max(0, stance_step) //If we have not seen a mob in a while, the stance_step will be negative, we need to reset it to 0 as soon as we see a mob again.
|
||||
stance_step++
|
||||
set_dir(get_dir(src,target_mob)) //Keep staring at the mob
|
||||
if(stance_step in list(1,4,7)) //every 3 ticks
|
||||
var/action = pick( list( "growls at [target_mob]", "stares angrily at [target_mob]", "prepares to attack [target_mob]", "closely watches [target_mob]" ) )
|
||||
if(action)
|
||||
custom_emote(1,action)
|
||||
speak_audio()
|
||||
else
|
||||
stance_step--
|
||||
|
||||
if(stance_step <= -20) //If we have not found a mob for 20-ish ticks, revert to idle mode
|
||||
stance = HOSTILE_STANCE_IDLE
|
||||
if(stance_step >= 7) //If we have been staring at a mob for 7 ticks,
|
||||
stance = HOSTILE_STANCE_ATTACK
|
||||
|
||||
|
||||
|
||||
if (anger && found_mob)
|
||||
instant_aggro()//If we're angry and someone is nearby, skip waiting and charge them
|
||||
|
||||
if(stance_step <= -20*bearmode) //If we have not found a mob for 20-ish ticks, revert to idle mode
|
||||
set_stance(HOSTILE_STANCE_IDLE)
|
||||
stop_automated_movement = 0
|
||||
if(stance_step >= 12) //If this mob just refuses to get out of our territory, then we attack
|
||||
anger += 3
|
||||
set_stance(HOSTILE_STANCE_ATTACK)
|
||||
growl_loud()
|
||||
|
||||
if(HOSTILE_STANCE_ATTACKING)
|
||||
if(stance_step >= 20) //attacks for 20 ticks, then it gets tired and needs to rest
|
||||
custom_emote(1, "is worn out and needs to rest." )
|
||||
stance = HOSTILE_STANCE_TIRED
|
||||
stance_step = 0
|
||||
walk(src, 0) //This stops the bear's walking
|
||||
turns_since_hit++
|
||||
stance_step++
|
||||
if(stance_step >= (16+(anger*2))*bearmode) //attacks a while, then it gets tired and needs to rest. An angry bear will chase longer
|
||||
tire_out()
|
||||
return
|
||||
|
||||
|
||||
//If we're having no luck attacking our current target
|
||||
if (target_mob && !Adjacent(target_mob) && turns_since_hit > 3)
|
||||
LoseTarget()
|
||||
else
|
||||
set_dir(get_dir(src,target_mob))
|
||||
|
||||
health_last_tick = health
|
||||
|
||||
|
||||
//Causes the bear to find and start attacking the nearest target.
|
||||
//This will overwrite any existing target if a different one is closer
|
||||
//If there are no other suitable targets, targeting will not be changed
|
||||
/mob/living/simple_animal/hostile/bear/FindTarget()
|
||||
|
||||
var/turf/here = get_turf(src)
|
||||
var/mob/nearest_target = null
|
||||
var/nearest_dist = 99999
|
||||
var/mob/nearest_downed_target = null
|
||||
var/nearest_downed_dist = 99999
|
||||
|
||||
for(var/atom/A in ListTargets(10))
|
||||
|
||||
if(A == src || A == target_mob)//We're only interested in alternatives to our current target
|
||||
continue
|
||||
|
||||
|
||||
var/dist = get_dist(here, get_turf(A)) < nearest_dist
|
||||
if(isliving(A))
|
||||
var/mob/living/L = A
|
||||
if(L.faction == src.faction && !attack_same)
|
||||
continue
|
||||
else if(L in friends)
|
||||
continue
|
||||
else
|
||||
|
||||
if(!L.stat == CONSCIOUS)
|
||||
if (dist < nearest_dist)
|
||||
nearest_target = L
|
||||
nearest_dist = dist
|
||||
|
||||
else if (L.stat != DEAD)//Unconscious people are lower priority, only targeted if nobody nearby is standing
|
||||
if (dist < nearest_downed_dist)
|
||||
nearest_downed_target = L
|
||||
nearest_downed_dist = dist
|
||||
|
||||
else if(istype(A, /obj/mecha))
|
||||
var/obj/mecha/M = A
|
||||
if (M.occupant)
|
||||
if (dist < nearest_dist)
|
||||
nearest_target = M
|
||||
nearest_dist = dist
|
||||
|
||||
if(istype(A, /obj/machinery/bot))
|
||||
var/obj/machinery/bot/B = A
|
||||
if (B.health > 0)
|
||||
if (dist < nearest_dist)
|
||||
nearest_target = B
|
||||
nearest_dist = dist
|
||||
|
||||
if (nearest_target)
|
||||
target_mob = nearest_target
|
||||
FoundTarget()
|
||||
return nearest_target
|
||||
else if (nearest_downed_target)
|
||||
target_mob = nearest_downed_target
|
||||
FoundTarget()
|
||||
return nearest_downed_target
|
||||
|
||||
return target_mob
|
||||
//If neither of the above is true, dont change the target
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/proc/tire_out()
|
||||
custom_emote(1, "is worn out and needs to rest." )
|
||||
set_stance(HOSTILE_STANCE_TIRED)
|
||||
speak_audio()
|
||||
stance_step = 0
|
||||
walk(src, 0) //This stops the bear's walking
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/spatial/tire_out()
|
||||
..()
|
||||
spawn(5)
|
||||
teleport()//Bluespace bears teleport away to rest
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/attackby(var/obj/item/O as obj, var/mob/user as mob)
|
||||
if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING)
|
||||
stance = HOSTILE_STANCE_ALERT
|
||||
stance_step = 6
|
||||
target_mob = user
|
||||
var/healthbefore = health
|
||||
..()
|
||||
spawn(1)
|
||||
if (health < healthbefore)//Hurting the bear makes it mad
|
||||
if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING)
|
||||
target_mob = user
|
||||
anger++
|
||||
instant_aggro()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/attack_hand(mob/living/carbon/human/M as mob)
|
||||
if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING)
|
||||
stance = HOSTILE_STANCE_ALERT
|
||||
stance_step = 6
|
||||
target_mob = M
|
||||
var/healthbefore = health
|
||||
..()
|
||||
spawn(1)
|
||||
if (health < healthbefore)//Hurting the bear makes it mad
|
||||
if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING)
|
||||
target_mob = M
|
||||
anger++
|
||||
instant_aggro()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/bullet_act(obj/item/projectile/P, def_zone)//Teleport around when shot, so its harder to burst it down with a carbine
|
||||
var/healthbefore = health
|
||||
..()
|
||||
spawn(1)
|
||||
if (health < healthbefore)
|
||||
instant_aggro()
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/ex_act()
|
||||
var/healthbefore = health
|
||||
..()
|
||||
spawn(1)
|
||||
if (health < healthbefore)
|
||||
instant_aggro()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/Process_Spacemove(var/check_drift = 0)
|
||||
return //No drifting in space for space bears!
|
||||
inertia_dir = 0
|
||||
return 1 //No drifting in space for space bears!
|
||||
//Fixed this, it wasnt working
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/FindTarget()
|
||||
. = ..()
|
||||
if(.)
|
||||
custom_emote(1,"stares alertly at [.]")
|
||||
stance = HOSTILE_STANCE_ALERT
|
||||
/mob/living/simple_animal/hostile/bear/FoundTarget()
|
||||
if(target_mob)
|
||||
turns_since_hit = 0
|
||||
custom_emote(1,"stares alertly at [target_mob]")
|
||||
speak_audio()
|
||||
|
||||
//If we're idle, move up to alert.
|
||||
//But don't drop down to alert if we're already in combat
|
||||
if (stance == HOSTILE_STANCE_IDLE)
|
||||
set_stance(HOSTILE_STANCE_ALERT)
|
||||
else if (stance == HOSTILE_STANCE_ATTACKING)
|
||||
MoveToTarget()
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/LoseTarget()
|
||||
..(5)
|
||||
//If we're still angry, try to find someone else to attack
|
||||
if (anger)
|
||||
FindTarget()
|
||||
|
||||
if (!anger || !target_mob)
|
||||
//If the anger has subsided, then give up the chase and let them go
|
||||
//But we'll get mad again soon if they don't go.
|
||||
if (stance > HOSTILE_STANCE_ALERT)//If we're currently above alert
|
||||
set_stance(HOSTILE_STANCE_ALERT)//Drop to alert and cease attacking
|
||||
target_mob = null
|
||||
walk(src, 0)
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/AttackingTarget()
|
||||
if(!Adjacent(target_mob))
|
||||
var/targetname = target_mob.name
|
||||
if(..())
|
||||
turns_since_hit = 0
|
||||
custom_emote(1, pick( list("crushes [targetname] in its arms","slashes at [targetname]", "bites [targetname]", "mauls [targetname]", "tears into [targetname]", "rends [targetname]") ) )
|
||||
if (prob(15))
|
||||
growl_loud()
|
||||
else if (prob(10))
|
||||
growl_soft()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/proc/update_bearmode()
|
||||
turns_per_move = initial(turns_per_move)
|
||||
if (anger >= 3)
|
||||
turns_per_move -= 1
|
||||
|
||||
|
||||
var/former = bearmode
|
||||
bearmode = BEARMODE_INDOORS
|
||||
if(loc && istype(loc,/turf/space))
|
||||
bearmode = BEARMODE_SPACE
|
||||
else
|
||||
if(istype(loc,/turf))
|
||||
var/turf/T = loc
|
||||
var/datum/gas_mixture/environment = T.return_air()
|
||||
if (environment.return_pressure() <= 80)
|
||||
bearmode = BEARMODE_SPACE
|
||||
|
||||
if (bearmode != former)
|
||||
var/healthpercent
|
||||
if (bearmode == BEARMODE_SPACE)
|
||||
custom_emote(1, "looks bright, energised and aggressive!" )
|
||||
healthpercent = health / maxHealth
|
||||
maxHealth = initial(maxHealth) * 1.5
|
||||
health = maxHealth * healthpercent
|
||||
melee_damage_lower = initial(melee_damage_lower)*1.2
|
||||
melee_damage_upper = initial(melee_damage_upper)*1.2
|
||||
turns_per_move -= 2
|
||||
growl_loud()
|
||||
else
|
||||
custom_emote(1, "looks darker and more subdued." )
|
||||
healthpercent = health / maxHealth
|
||||
maxHealth = initial(maxHealth)
|
||||
health = maxHealth * healthpercent
|
||||
melee_damage_lower = initial(melee_damage_lower)
|
||||
melee_damage_upper = initial(melee_damage_upper)
|
||||
growl_soft()
|
||||
|
||||
update_icons()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/update_icons()
|
||||
if (stat == DEAD)
|
||||
icon_state = "bear_dead"
|
||||
else if (bearmode == BEARMODE_INDOORS)
|
||||
icon_state = "bearfloor"
|
||||
else
|
||||
icon_state = "bear"
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/speak_audio()
|
||||
if (anger > 5 || (anger && prob(25)))
|
||||
growl_loud()
|
||||
else
|
||||
growl_soft()
|
||||
|
||||
|
||||
//Plays a random selection of six sounds, at a low volume
|
||||
//This is triggered randomly periodically by the bear
|
||||
/mob/living/simple_animal/hostile/bear/proc/growl_soft()
|
||||
var/sound = pick(quiet_sounds)
|
||||
playsound(src, sound, 50, 1,3, usepressure = 0)
|
||||
|
||||
|
||||
|
||||
//Plays a loud sound from a selection of four
|
||||
//Played when bear is attacking or dies
|
||||
/mob/living/simple_animal/hostile/bear/proc/growl_loud()
|
||||
var/sound = pick(loud_sounds)
|
||||
playsound(src, sound, 85, 1, 5, usepressure = 0)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//A special bear subclass which is more powerful and has the ability to teleport around to seek out prey.
|
||||
//It dislikes other bears and refuses to cooperate with them. If two of them see each other, one or both will teleport away
|
||||
//Therefore the crew never has to fight more than one at a time
|
||||
/mob/living/simple_animal/hostile/bear/spatial
|
||||
name = "bluespace bear"
|
||||
desc = "*bzzt*..Rawr!!"
|
||||
maxHealth = 130
|
||||
turns_per_move = 7
|
||||
break_stuff_probability = 100//Constantly smashing everything nearby
|
||||
speak_chance = 15
|
||||
var/idletime
|
||||
var/focus_time//How long we've focused on this target
|
||||
var/teleport_delay = 60
|
||||
var/tactical_delay = 3//Procs between shortrange teleports
|
||||
|
||||
|
||||
|
||||
//Called when we want to bypass ticks and attack immediately. For example in response to being shot
|
||||
//This calls several procs and some duplicated code from the parent class to immediately put us in an assault state and lash out
|
||||
/mob/living/simple_animal/hostile/bear/proc/instant_aggro()
|
||||
if (stance < HOSTILE_STANCE_ATTACK)
|
||||
growl_loud()
|
||||
if(!target_mob)
|
||||
FindTarget()
|
||||
|
||||
if(target_mob)
|
||||
growl_loud()
|
||||
if(destroy_surroundings)
|
||||
DestroySurroundings()
|
||||
MoveToTarget()
|
||||
AttackTarget()//When first aggroed, this causes the bear to instantly lash out and counter any melee attacker nearby
|
||||
else
|
||||
set_stance(HOSTILE_STANCE_ALERT)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//These bears are always in spacemode
|
||||
/mob/living/simple_animal/hostile/bear/spatial/update_bearmode()
|
||||
turns_per_move = initial(turns_per_move)
|
||||
if (anger >= 3)
|
||||
turns_per_move -= 1
|
||||
|
||||
|
||||
bearmode = BEARMODE_SPACE
|
||||
|
||||
var/healthpercent = health / maxHealth
|
||||
maxHealth = initial(maxHealth) * 1.5
|
||||
health = maxHealth * healthpercent
|
||||
melee_damage_lower = initial(melee_damage_lower)*1.2
|
||||
melee_damage_upper = initial(melee_damage_upper)*1.2
|
||||
turns_per_move -= 2
|
||||
|
||||
update_icons()
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/spatial/Life()
|
||||
..()
|
||||
if (stat != CONSCIOUS)
|
||||
return
|
||||
custom_emote(1, pick( list("slashes at [target_mob]", "bites [target_mob]") ) )
|
||||
|
||||
var/damage = rand(20,30)
|
||||
|
||||
if(ishuman(target_mob))
|
||||
var/mob/living/carbon/human/H = target_mob
|
||||
var/dam_zone = pick("chest", "l_hand", "r_hand", "l_leg", "r_leg")
|
||||
var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone))
|
||||
H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp=1, edge=1)
|
||||
return H
|
||||
else if(isliving(target_mob))
|
||||
var/mob/living/L = target_mob
|
||||
L.adjustBruteLoss(damage)
|
||||
return L
|
||||
//else if(istype(target_mob,/obj/mecha))
|
||||
//var/obj/mecha/M = target_mob
|
||||
//M.attack_animal(src)
|
||||
//return M
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var/time_to_go = 0
|
||||
|
||||
//If they've sat around too long without finding a mob to target, then they'll teleport elsewhere to seek prey
|
||||
if (stance == HOSTILE_STANCE_IDLE)
|
||||
idletime++
|
||||
if (idletime >= teleport_delay)
|
||||
time_to_go = 1
|
||||
else
|
||||
idletime = 0
|
||||
|
||||
//Randomly make shortrange teleports in the vicinity of the target
|
||||
if ((stance == HOSTILE_STANCE_ATTACKING)&& target_mob)
|
||||
focus_time++
|
||||
if (focus_time % tactical_delay == 0)
|
||||
teleport_tactical(target_mob)
|
||||
|
||||
|
||||
//Teleport away from other bears
|
||||
for (var/mob/living/simple_animal/hostile/bear/bear in view(world.view, get_turf(src)))
|
||||
if (bear != src && bear.stat != DEAD)
|
||||
time_to_go = 1
|
||||
|
||||
if (time_to_go)
|
||||
idletime = 0
|
||||
stance = HOSTILE_STANCE_IDLE
|
||||
teleport()
|
||||
growl_soft()
|
||||
|
||||
|
||||
|
||||
|
||||
//Used to move to a new part of the station when it sees another bear, or it hasnt found any prey
|
||||
/mob/living/simple_animal/hostile/bear/spatial/proc/teleport()
|
||||
if (stat == CONSCIOUS)
|
||||
var/area/A = random_station_area()
|
||||
var/turf/target = A.random_space()
|
||||
|
||||
sparks()
|
||||
forceMove(target)
|
||||
sparks()
|
||||
|
||||
|
||||
//Used, with some luck, to reposition near the target. Hiding behind glass is a bad idea
|
||||
//Picks a random tile in the target's area and teleports there. Might be closer, might be farther away
|
||||
//Who knows, it's unpredictable. But definitely dangerous.
|
||||
//This allows the target to escape as often as it allows the bear to attack
|
||||
/mob/living/simple_animal/hostile/bear/spatial/proc/teleport_tactical()
|
||||
if (target_mob && stat == CONSCIOUS)
|
||||
var/area/A = get_area(target_mob)
|
||||
if (A)
|
||||
var/turf/target = A.random_space()
|
||||
sparks()
|
||||
forceMove(target)
|
||||
sparks()
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/spatial/bullet_act(obj/item/projectile/P, def_zone)//Teleport around when shot, so its harder to burst it down with a carbine
|
||||
..(P, def_zone)
|
||||
if (prob(P.damage*1.5))//Bear has a good chance of teleporting when shot, making it harder to burst down
|
||||
teleport_tactical()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/spatial/proc/sparks()
|
||||
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
||||
s.set_up(10, 1, get_turf(src))
|
||||
s.start()
|
||||
spawn(2)
|
||||
qdel(s)
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/spatial/FoundTarget()
|
||||
..()
|
||||
focus_time = 0
|
||||
|
||||
|
||||
#undef BEARMODE_INDOORS
|
||||
#undef BEARMODE_SPACE
|
||||
@@ -56,16 +56,24 @@
|
||||
stance = HOSTILE_STANCE_ATTACK
|
||||
T = B
|
||||
break
|
||||
|
||||
if (T != target_mob)
|
||||
target_mob = T
|
||||
FoundTarget()
|
||||
return T
|
||||
|
||||
|
||||
//This proc is called after a target is acquired
|
||||
/mob/living/simple_animal/hostile/proc/FoundTarget()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/Found(var/atom/A)
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/MoveToTarget()
|
||||
stop_automated_movement = 1
|
||||
if(!target_mob || SA_attackable(target_mob))
|
||||
stance = HOSTILE_STANCE_IDLE
|
||||
LoseTarget()
|
||||
if(target_mob in ListTargets(10))
|
||||
if(ranged)
|
||||
if(get_dist(src, target_mob) <= 6)
|
||||
@@ -83,7 +91,7 @@
|
||||
LoseTarget()
|
||||
return 0
|
||||
if(!(target_mob in ListTargets(10)))
|
||||
LostTarget()
|
||||
LoseTarget()
|
||||
return 0
|
||||
if(get_dist(src, target_mob) <= 1) //Attacking
|
||||
AttackingTarget()
|
||||
@@ -103,15 +111,16 @@
|
||||
if(istype(target_mob,/obj/machinery/bot))
|
||||
var/obj/machinery/bot/B = target_mob
|
||||
B.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext)
|
||||
return B
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/LoseTarget()
|
||||
stance = HOSTILE_STANCE_IDLE
|
||||
target_mob = null
|
||||
walk(src, 0)
|
||||
LostTarget()
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/LostTarget()
|
||||
stance = HOSTILE_STANCE_IDLE
|
||||
walk(src, 0)
|
||||
return
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/ListTargets(var/dist = 7)
|
||||
@@ -147,9 +156,9 @@
|
||||
MoveToTarget()
|
||||
|
||||
if(HOSTILE_STANCE_ATTACKING)
|
||||
if(destroy_surroundings)
|
||||
if(!AttackTarget() && destroy_surroundings)//hit a window OR a mob, not both at once
|
||||
DestroySurroundings()
|
||||
AttackTarget()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/OpenFire(target_mob)
|
||||
var/target = target_mob
|
||||
@@ -197,10 +206,12 @@
|
||||
for(var/obj/structure/window/obstacle in get_step(src, dir))
|
||||
if(obstacle.dir == reverse_dir[dir]) // So that windows get smashed in the right order
|
||||
obstacle.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext)
|
||||
return
|
||||
return 1
|
||||
var/obj/structure/obstacle = locate(/obj/structure, get_step(src, dir))
|
||||
if(istype(obstacle, /obj/structure/window) || istype(obstacle, /obj/structure/closet) || istype(obstacle, /obj/structure/table) || istype(obstacle, /obj/structure/grille))
|
||||
obstacle.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/proc/check_horde()
|
||||
@@ -234,7 +245,7 @@
|
||||
else if(istype(A, /obj/structure/window) || istype(A, /obj/structure/closet) || istype(A, /obj/structure/table) || istype(A, /obj/structure/grille))
|
||||
A.attack_generic(src, rand(melee_damage_lower, melee_damage_upper))
|
||||
Move(T)
|
||||
FindTarget()
|
||||
target_mob = FindTarget()
|
||||
if(!target_mob || enroute)
|
||||
spawn(10)
|
||||
if(!src.stat)
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
..()
|
||||
seek_move_delay = (1 / seek_speed) / (world.tick_lag / 10)//number of ticks between moves
|
||||
turns_since_scan = rand(min_scan_interval, max_scan_interval)//Randomise this at the start so animals don't sync up
|
||||
health = maxHealth
|
||||
verbs -= /mob/verb/observe
|
||||
health = maxHealth
|
||||
if (mob_size)
|
||||
@@ -144,7 +145,7 @@
|
||||
|
||||
/mob/living/simple_animal/Life()
|
||||
..()
|
||||
|
||||
life_tick++
|
||||
//Health
|
||||
updatehealth()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user