Merge pull request #1178 from TheDZD/mobs

simple_animal and simple_animal/hostile Refactor
This commit is contained in:
ZomgPonies
2015-05-27 19:38:50 -04:00
7 changed files with 177 additions and 118 deletions
+7 -1
View File
@@ -50,8 +50,14 @@
#define GRAB_UPGRADING 4
#define GRAB_KILL 5
//Hostile Mob Stances
#define HOSTILE_STANCE_IDLE 1
#define HOSTILE_STANCE_ALERT 2
#define HOSTILE_STANCE_ATTACK 3
#define HOSTILE_STANCE_ATTACKING 4
#define HOSTILE_STANCE_TIRED 5
#define HOSTILE_STANCE_TIRED 5
//Hostile Mob AI Status
#define AI_ON 1
#define AI_SLEEP 2
#define AI_OFF 3
+1
View File
@@ -603,6 +603,7 @@
var/mob/living/simple_animal/hostile/H = M
if(malfunctioning)
H.faction |= list("lazarus", "\ref[user]")
H.robust_searching = 1
H.friends += user
H.attack_same = 1
log_game("[user] has revived hostile mob [target] with a malfunctioning lazarus injector")
@@ -23,6 +23,8 @@
health = 60
melee_damage_lower = 20
melee_damage_upper = 30
attacktext = "mauls"
attack_sound = 'sound/weapons/genhit3.ogg'
//Space bears aren't affected by atmos.
min_oxy = 0
@@ -75,7 +77,7 @@
stop_automated_movement = 1
var/found_mob = 0
if(target && target in ListTargets())
if(!(SA_attackable(target)))
if(CanAttack(target))
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
@@ -5,7 +5,6 @@
environment_smash = 1 //Set to 1 to break closets,tables,racks, etc; 2 for walls; 3 for rwalls
var/stance = HOSTILE_STANCE_IDLE //Used to determine behavior
var/atom/target
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
var/ranged = 0
var/rapid = 0
var/projectiletype
@@ -13,20 +12,24 @@
var/casingtype
var/move_to_delay = 3 //delay for the automated movement.
var/list/friends = list()
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/ranged_message = "fires" //Fluff text for ranged mobs
var/ranged_cooldown = 0 //What the starting cooldown is on ranged attacks
var/ranged_cooldown_cap = 3 //What ranged attacks, after being used are set to, to go back on cooldown, defaults to 3 life() ticks
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/AIenabled = 1 //Badminnery and general AI toggle.
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
var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_SLEEP (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever)
/mob/living/simple_animal/hostile/Life()
@@ -34,7 +37,11 @@
if(!.)
walk(src, 0)
return 0
if(client || !AIenabled)
if(ranged)
ranged_cooldown--
if(client)
return 0
if(!AICanContinue())
return 0
if(!stat)
switch(stance)
@@ -50,30 +57,24 @@
AttackTarget()
DestroySurroundings()
if(ranged)
ranged_cooldown--
if(AIShouldSleep())
AIStatus = AI_SLEEP
//////////////HOSTILE MOB TARGETTING AND AGGRESSION////////////
/mob/living/simple_animal/hostile/proc/ListTargets()//Step 1, find out what we can see
var/list/L = list()
if(search_objects < 2)
var/list/Mobs = hearers(src, vision_range)
for(var/mob/living/G in Mobs)
L.Add(G)
L.Remove(src)//So we don't suicide because we listed ourselves as a target!
if(search_objects)
var/list/Objects = oview(vision_range, src)
for(var/obj/O in Objects)
L.Add(O)
else
if(!search_objects)
var/list/Mobs = hearers(vision_range, src) - src //Remove self, so we don't suicide
L += Mobs
for(var/obj/mecha/M in mechas_list)
if(get_dist(M, src) <= vision_range && can_see(src, M, vision_range))
L += M
for(var/obj/spacepod/S in spacepods_list)
if(get_dist(S, src) <= vision_range && can_see(src, S, vision_range))
L += S
else
var/list/Objects = oview(vision_range, src)
L += Objects
return L
/mob/living/simple_animal/hostile/proc/FindTarget()//Step 2, filter down possible targets to things we actually care about
@@ -82,16 +83,14 @@
for(var/atom/A in ListTargets())
if(Found(A))//Just in case people want to override targetting
var/list/FoundTarget = list()
FoundTarget.Add(A)
FoundTarget += A
Targets = FoundTarget
break
if(CanAttack(A))//Can we attack it?
if(istype(src, /mob/living/simple_animal/hostile/scarybat))
if(A == src:owner)
continue
Targets += A
continue
Target = PickTarget(Targets)
GiveTarget(Target)
return Target //We now have a target
/mob/living/simple_animal/hostile/proc/Found(var/atom/A)//This is here as a potential override to pick a specific target if available
@@ -112,32 +111,35 @@
/mob/living/simple_animal/hostile/CanAttack(var/atom/the_target)//Can we actually attack a possible target?
if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
return 0
if(isobj(the_target) && search_objects)
if(search_objects < 2)
if(istype(the_target, /obj/mecha))
var/obj/mecha/M = the_target
if(M.occupant)//Just so we don't attack empty mechs
if(CanAttack(M.occupant))
return 1
if(isliving(the_target))
var/mob/living/L = the_target
var/faction_check = 0
for(var/F in faction)
if(F in L.faction)
faction_check = 1
break
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
if(isobj(the_target))
if(the_target.type in wanted_objects)
return 1
if(isliving(the_target) && search_objects < 2)
var/mob/living/L = the_target
if(L.stat > stat_attack || L.stat != stat_attack && stat_exclusive == 1)
return 0
var/faction_check = 0
for(var/F in faction)
if(F in L.faction)
faction_check = 1
break
if(faction_check && !attack_same || !faction_check && attack_same == 2)
return 0
return 0
if(L in friends)
return 0
return 1
if(istype(the_target, /obj/mecha))
var/obj/mecha/M = the_target
if(M.occupant)//Just so we don't attack empty mechs
return 1
if(istype(the_target, /obj/spacepod))
var/obj/spacepod/S = the_target
if(S.occupant || S.occupant2)//Just so we don't attack empty mechs
return 1
return 0
/mob/living/simple_animal/hostile/proc/GiveTarget(var/new_target)//Step 4, give us our selected target
@@ -149,15 +151,16 @@
/mob/living/simple_animal/hostile/proc/MoveToTarget()//Step 5, handle movement between us and our target
stop_automated_movement = 1
if(!target || SA_attackable(target))
if(!target || !CanAttack(target))
LoseTarget()
return
if(target in ListTargets())
var/TargetDistance = get_dist(src,target)
var/target_distance = get_dist(src,target)
if(ranged)//We ranged? Shoot at em
if(TargetDistance >= 2 && ranged_cooldown <= 0)//But make sure they're a tile away at least, and our range attack is off cooldown
if(target_distance >= 2 && ranged_cooldown <= 0)//But make sure they're a tile away at least, and our range attack is off cooldown
OpenFire(target)
if(retreat_distance != null)//If we have a retreat distance, check if we need to run from our target
if(TargetDistance <= retreat_distance)//If target's closer than our retreat distance, run
if(target_distance <= retreat_distance)//If target's closer than our retreat distance, run
walk_away(src,target,retreat_distance,move_to_delay)
else
Goto(target,move_to_delay,minimum_distance)//Otherwise, get to our minimum distance so we chase them
@@ -166,6 +169,15 @@
if(isturf(loc) && target.Adjacent(src)) //If they're next to us, attack
AttackingTarget()
return
if(environment_smash)
if(target.loc != null && get_dist(src, target.loc) <= vision_range)//We can't see our target, but he's in our vision range still
if(environment_smash >= 2)//If we're capable of smashing through walls, forget about vision completely after finding our target
Goto(target,move_to_delay,minimum_distance)
FindHidden()
return
else
if(FindHidden())
return
LostTarget()
/mob/living/simple_animal/hostile/proc/Goto(var/target, var/delay, var/minimum_distance)
@@ -189,7 +201,7 @@
/mob/living/simple_animal/hostile/proc/AttackTarget()
stop_automated_movement = 1
if(!target || SA_attackable(target))
if(!target || !CanAttack(target))
LoseTarget()
return 0
if(!(target in ListTargets()))
@@ -229,8 +241,9 @@
walk(src, 0)
/mob/living/simple_animal/hostile/proc/OpenFire(var/the_target)
var/target = the_target
visible_message("\red <b>[src]</b> [ranged_message] at [target]!", 1)
visible_message("<span class='danger'><b>[src]</b> [ranged_message] at [target]!</span>")
var/tturf = get_turf(target)
if(rapid)
@@ -250,9 +263,6 @@
Shoot(tturf, src.loc, src)
if(casingtype)
new casingtype
stance = HOSTILE_STANCE_IDLE
target = null
ranged_cooldown = ranged_cooldown_cap
return
@@ -265,9 +275,10 @@
if(!A) return
if (!istype(target, /turf))
del(A)
qdel(A)
return
A.current = target
A.firer = src
A.yo = target:y - start:y
A.xo = target:x - start:x
spawn( 0 )
@@ -276,18 +287,66 @@
/mob/living/simple_animal/hostile/proc/DestroySurroundings()
if(environment_smash)
if(buckled)//Beds and chairs are no longer hostile mob kryptonite
buckled.attack_animal(src)
var/list/directions = cardinal.Copy()
for(var/dir in directions)
EscapeConfinement()
for(var/dir in cardinal)
var/turf/T = get_step(src, dir)
if(istype(T, /turf/simulated/wall) && T.Adjacent(src))
T.attack_animal(src)
if(istype(T, /turf/simulated/wall) || istype(T, /turf/simulated/mineral))
if(T.Adjacent(src))
T.attack_animal(src)
for(var/atom/A in T)
if(!A.Adjacent(src))
continue
if(istype(A, /obj/structure/window) || istype(A, /obj/structure/closet) || \
istype(A, /obj/structure/table) || istype(A, /obj/structure/grille) || \
istype(A, /obj/structure/rack) || istype(A, /obj/machinery/door/window))
if(istype(A, /obj/structure/window) || istype(A, /obj/structure/closet) || istype(A, /obj/structure/table) || istype(A, /obj/structure/grille) || istype(A, /obj/structure/rack))
A.attack_animal(src)
return
/mob/living/simple_animal/hostile/proc/EscapeConfinement()
if(buckled)
buckled.attack_animal(src)
if(!isturf(src.loc) && src.loc != null)//Did someone put us in something?
var/atom/A = src.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))
var/atom/A = target.loc
Goto(A,move_to_delay,minimum_distance)
if(A.Adjacent(src))
A.attack_animal(src)
return 1
/mob/living/simple_animal/hostile/RangedAttack(var/atom/A, var/params) //Player firing
if(ranged && ranged_cooldown <= 0)
target = A
OpenFire(A)
..()
////// AI Status ///////
/mob/living/simple_animal/hostile/proc/AICanContinue()
switch(AIStatus)
if(AI_ON)
. = 1
if(AI_SLEEP)
if(AIShouldWake())
. = 1
AIStatus = AI_ON //Wake up for more than one Life() cycle.
else
. = 0
if(AI_OFF)
. = 0
//Returns 1 if the AI should wake up
//Returns 0 if the AI should remain asleep
/mob/living/simple_animal/hostile/proc/AIShouldWake()
. = 0
if(FindTarget())
. = 1
//Convenience
/mob/living/simple_animal/hostile/proc/AIShouldSleep()
. = !(AIShouldWake())
@@ -20,6 +20,8 @@
a_intent = "harm"
var/throw_message = "bounces off of"
var/icon_aggro = null // for swapping to when we get aggressive
see_in_dark = 8
see_invisible = SEE_INVISIBLE_MINIMUM
/mob/living/simple_animal/hostile/asteroid/Aggro()
..()
@@ -138,6 +140,7 @@
melee_damage_lower = 0
melee_damage_upper = 0
attacktext = "barrels into"
attack_sound = 'sound/weapons/punch1.ogg'
a_intent = "help"
throw_message = "sinks in slowly, before being pushed out of "
status_flags = CANPUSH
@@ -238,6 +241,7 @@
melee_damage_lower = 0
melee_damage_upper = 0
attacktext = "lashes out at"
speak_emote = list("telepathically cries")
throw_message = "falls right through the strange body of the"
ranged_cooldown = 0
ranged_cooldown_cap = 0
@@ -288,7 +292,7 @@
user << "<span class='notice'>You chomp into [src], barely managing to hold it down, but feel amazingly refreshed in mere moments.</span>"
playsound(src.loc,'sound/items/eatfood.ogg', rand(10,50), 1)
H.revive()
del(src)
qdel(src)
..()
/mob/living/simple_animal/hostile/asteroid/hivelordbrood
@@ -311,6 +315,7 @@
melee_damage_lower = 2
melee_damage_upper = 2
attacktext = "slashes"
speak_emote = list("telepathically cries")
throw_message = "falls right through the strange body of the"
environment_smash = 0
pass_flags = PASSTABLE
@@ -318,10 +323,10 @@
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/New()
..()
spawn(100)
del(src)
qdel(src)
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/Die()
del(src)
qdel(src)
/mob/living/simple_animal/hostile/asteroid/goliath
name = "goliath"
@@ -339,6 +344,7 @@
ranged_cooldown = 2 //By default, start the Goliath with his cooldown off so that people can run away quickly on first sight
ranged_cooldown_cap = 8
friendly = "wails at"
speak_emote = list("bellows")
vision_range = 4
speed = 3
maxHealth = 300
@@ -347,6 +353,7 @@
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
@@ -398,6 +405,7 @@
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)
@@ -425,15 +433,15 @@
/obj/effect/goliath_tentacle/proc/Trip()
for(var/mob/living/M in src.loc)
M.Weaken(5)
visible_message("<span class='warning'>The [src.name] knocks [M.name] down!</span>")
del(src)
/obj/effect/goliath_tentacle/Crossed(AM as mob|obj)
if(isliving(AM))
Trip()
return
..()
M.Stun(5)
M.adjustBruteLoss(rand(10,15))
latched = 1
visible_message("<span class='danger'>The [src.name] grabs hold of [M.name]!</span>")
if(!latched)
qdel(src)
else
spawn(50)
qdel(src)
/mob/living/simple_animal/hostile/asteroid/goliath/Die()
var/obj/item/asteroid/goliath_hide/G = new /obj/item/asteroid/goliath_hide(src.loc)
@@ -457,15 +465,18 @@
if(current_armor.["melee"] < 80)
current_armor.["melee"] = min(current_armor.["melee"] + 10, 80)
user << "<span class='info'>You strengthen [target], improving its resistance against melee attacks.</span>"
del(src)
qdel(src)
else
user << "<span class='info'>You can't improve [C] any further.</span>"
return
if(istype(target, /obj/mecha/working/ripley))
var/obj/mecha/D = target
var/list/damage_absorption = D.damage_absorption
if(damage_absorption.["brute"] > 0.3)
damage_absorption.["brute"] = max(damage_absorption.["brute"] - 0.1, 0.3)
if(damage_absorption["brute"] > 0.3)
damage_absorption["brute"] = max(damage_absorption["brute"] - 0.1, 0.3)
damage_absorption["bullet"] = damage_absorption["bullet"] - 0.05
damage_absorption["fire"] = damage_absorption["fire"] - 0.05
damage_absorption["laser"] = damage_absorption["laser"] - 0.025
user << "<span class='info'>You strengthen [target], improving its resistance against melee attacks.</span>"
qdel(src)
if(D.icon_state == "ripley-open")
@@ -478,7 +489,7 @@
D.overlays += image("icon"="mecha.dmi", "icon_state"="ripley-g-full-open")
D.desc = "Autonomous Power Loader Unit. It's wearing a fearsome carapace entirely composed of goliath hide plates - the pilot must be an experienced monster hunter."
else
user << "<span class='info'>You can't add armour onto the mech while someone is inside!</span>"
user << "<span class='warning'>You can't add armour onto the mech while someone is inside!</span>"
else
user << "<span class='info'>You can't improve [D] any further.</span>"
user << "<span class='warning'>You can't improve [D] any further!</span>"
return
@@ -36,7 +36,7 @@
speed = -1
AIenabled = 0 //The body isn't conscious
AIStatus = AI_OFF
anchored = 1 //otherwise people can literally fucking pull spaceworms apart
@@ -74,7 +74,7 @@
animate_movement = SLIDE_STEPS
AIenabled = 1//The head is conscious
AIStatus = AI_ON//The head is conscious
stop_automated_movement = 0 //Ditto ^
faction = list("spaceworms") //head and body both have this faction JIC
@@ -474,19 +474,17 @@
if(health < 1)
Die()
/mob/living/simple_animal/proc/SA_attackable(target)
if (isliving(target))
var/mob/living/L = target
if(!L.stat)
/mob/living/simple_animal/proc/CanAttack(var/atom/the_target)
if(see_invisible < the_target.invisibility)
return 0
if (isliving(the_target))
var/mob/living/L = the_target
if(L.stat != CONSCIOUS)
return 0
if (istype(target,/obj/mecha))
var/obj/mecha/M = target
if (istype(the_target, /obj/mecha))
var/obj/mecha/M = the_target
if (M.occupant)
return 0
if (istype(target,/obj/spacepod))
var/obj/spacepod/S = target
if (S.occupant || S.occupant2)
return 0
return 1
/mob/living/simple_animal/update_fire()
@@ -529,24 +527,6 @@
gib()
return
/mob/living/simple_animal/proc/CanAttack(var/atom/the_target)
if(see_invisible < the_target.invisibility)
return 0
if (isliving(the_target))
var/mob/living/L = the_target
if(L.stat != CONSCIOUS)
return 0
if (istype(the_target, /obj/mecha))
var/obj/mecha/M = the_target
if (M.occupant)
return 0
if (istype(the_target, /obj/spacepod))
var/obj/spacepod/S = the_target
if (S.occupant || S.occupant2)
return 0
return 1
/mob/living/simple_animal/say(var/message)
if(stat)
return