diff --git a/code/modules/critters/critter.dm b/code/modules/critters/critter.dm
index bd46ecaf04..854765817c 100644
--- a/code/modules/critters/critter.dm
+++ b/code/modules/critters/critter.dm
@@ -23,6 +23,8 @@
wanderer = 1
//Will open doors it bumps ignoring access
opensdoors = 0
+ //Will randomly travel through vents
+ ventcrawl = 0
//Internal tracking ignore
frustration = 0
@@ -34,6 +36,10 @@
target = null
oldtarget_name = null
target_lastloc = null
+
+ thinkspeed = 15
+ chasespeed = 4
+ wanderspeed = 10
//The last guy who attacked it
attacker = null
//Will not attack this thing
@@ -49,6 +55,11 @@
atksame = 0
atkmech = 0
+ //Attacks syndies/traitors (distinguishes via mind)
+ atksynd = 1
+ //Attacks things NOT in its obj/req_access list
+ atkreq = 0
+
//Damage multipliers
brutevuln = 1
firevuln = 1
@@ -61,7 +72,10 @@
//Basic attack message when they move to attack and attack
angertext = "charges at"
attacktext = "attacks"
+ deathtext = "dies!"
+ chasestate = null // the icon state to use when attacking or chasing a target
+ attackflick = null // the icon state to flick when it attacks
attack_sound = null // the sound it makes when it attacks!
attack_speed = 25 // delay of attack
@@ -121,4 +135,4 @@
-*/
+*/
\ No newline at end of file
diff --git a/code/modules/critters/critter_AI.dm b/code/modules/critters/critter_AI.dm
index 169ce48974..ae11c6e6d6 100644
--- a/code/modules/critters/critter_AI.dm
+++ b/code/modules/critters/critter_AI.dm
@@ -13,7 +13,7 @@
if("thinking")
src.attack = 0
src.target = null
- sleep(15)
+ sleep(thinkspeed)
walk_to(src,0)
if (src.aggressive) seek_target()
if (src.wanderer && !src.target) src.task = "wandering"
@@ -29,11 +29,13 @@
var/mob/living/carbon/M = src.target
ChaseAttack()
src.task = "attacking"
+ if(chasestate)
+ icon_state = chasestate
src.anchored = 1
src.target_lastloc = M.loc
else
var/turf/olddist = get_dist(src, src.target)
- walk_to(src, src.target,1,speed)
+ walk_to(src, src.target,1,chasespeed)
if ((get_dist(src, src.target)) >= (olddist))
src.frustration++
else
@@ -45,6 +47,8 @@
if ((get_dist(src, src.target) > 1) || ((src.target:loc != src.target_lastloc)))
src.anchored = 0
src.task = "chasing"
+ if(chasestate)
+ icon_state = chasestate
else
if (get_dist(src, src.target) <= 1)
var/mob/living/carbon/M = src.target
@@ -58,20 +62,25 @@
src.attacking = 0
else
if(M!=null)
- if(M.health < 0)
- src.task = "thinking"
- src.target = null
- src.anchored = 0
- src.last_found = world.time
- src.frustration = 0
- src.attacking = 0
+ if(ismob(src.target))
+ if(M.health < 0)
+ src.task = "thinking"
+ src.target = null
+ src.anchored = 0
+ src.last_found = world.time
+ src.frustration = 0
+ src.attacking = 0
else
src.anchored = 0
src.attacking = 0
src.task = "chasing"
+ if(chasestate)
+ icon_state = chasestate
if("wandering")
+ if(chasestate)
+ icon_state = initial(icon_state)
patrol_step()
- sleep(10)
+ sleep(wanderspeed)
spawn(8)
process()
return
@@ -114,29 +123,57 @@
if (src.target)
src.task = "chasing"
break
+
+ // Ignore syndicates and traitors if specified
+ if(!atksynd && C.mind)
+ var/datum/mind/synd_mind = C.mind
+ if( synd_mind.special_role == "Syndicate" || synd_mind.special_role == "traitor" )
+ continue
if((C.name == src.oldtarget_name) && (world.time < src.last_found + 100)) continue
if(istype(C, /mob/living/carbon/) && !src.atkcarbon) continue
if(istype(C, /mob/living/silicon/) && !src.atksilicon) continue
+ if(atkreq)
+ if(src.allowed(C)) continue
if(C.health < 0) continue
if(istype(C, /mob/living/carbon/) && src.atkcarbon) src.attack = 1
if(istype(C, /mob/living/silicon/) && src.atksilicon) src.attack = 1
+ if(atkreq)
+ if(!src.allowed(C)) src.attack = 1
if(src.attack)
T = C
break
if(!src.attack)
for(var/obj/effect/critter/C in view(src.seekrange,src))
- if(istype(C, /obj/effect/critter) && !src.atkcritter) continue
- if(istype(C, /obj/mecha) && !src.atkmech) continue
+ if(!src.atkcritter) continue
if(C.health <= 0) continue
- if(istype(C, /obj/effect/critter) && src.atkcritter)
+ if(src.atkcritter)
if((istype(C, src.type) && !src.atksame) || (C == src)) continue
src.attack = 1
- if(istype(C, /obj/mecha) && src.atkmech) src.attack = 1
if(src.attack)
T = C
break
+ if(!src.attack)
+ for(var/obj/mecha/C in view(src.seekrange,src))
+ if(!C.occupant) continue
+
+ if(atkreq && C.occupant)
+ if(src.allowed(C.occupant)) continue
+
+ if(!atksynd && C.occupant)
+ if(C.occupant.mind)
+ var/datum/mind/synd_mind = C.occupant.mind
+ if( synd_mind.special_role == "Syndicate" || synd_mind.special_role == "traitor" )
+ continue
+
+ if(!src.atkmech) continue
+ if(C.health <= 0) continue
+ if(src.atkmech) src.attack = 1
+ if(src.attack)
+ T = C
+ break
+
if(src.attack)
src.target = T
src.oldtarget_name = T:name
@@ -152,10 +189,11 @@
RunAttack()
src.attacking = 1
- for(var/mob/O in viewers(src, null))
- O.show_message("\red [src] [src.attacktext] [src.target]!", 1)
if(ismob(src.target))
+ for(var/mob/O in viewers(src, null))
+ O.show_message("\red [src] [src.attacktext] [src.target]!", 1)
+
var/damage = rand(melee_damage_lower, melee_damage_upper)
if(istype(target, /mob/living/carbon/human))
@@ -174,7 +212,8 @@
if(isobj(src.target))
if(istype(target, /obj/mecha))
- src.target:take_damage(rand(melee_damage_lower,melee_damage_upper))
+ //src.target:take_damage(rand(melee_damage_lower,melee_damage_upper))
+ src.target:attack_critter(src)
else
src.target:TakeDamage(rand(melee_damage_lower,melee_damage_upper))
spawn(attack_speed)
@@ -200,4 +239,4 @@
smoke.set_up(10, 0, src.loc)
smoke.start()
src.task = "thinking"
-*/
+*/
\ No newline at end of file
diff --git a/code/modules/critters/critter_defenses.dm b/code/modules/critters/critter_defenses.dm
index fc50e9f02a..fde45ab5d3 100644
--- a/code/modules/critters/critter_defenses.dm
+++ b/code/modules/critters/critter_defenses.dm
@@ -13,7 +13,7 @@ Contains the procs that control attacking critters
if("fire") damage = W.force * firevuln
if("brute") damage = W.force * brutevuln
TakeDamage(damage)
- if(src.defensive) Target_Attacker(user)
+ if(src.defensive && alive) Target_Attacker(user)
return
@@ -51,9 +51,10 @@ Contains the procs that control attacking critters
if(!target) return
src.target = target
src.oldtarget_name = target:name
- if(task != "chasing" || task != "attacking")
- for(var/mob/O in viewers(src, null))
- O.show_message("\red [src] [src.angertext] at [target:name]!", 1)
+ if(task != "chasing" && task != "attacking")
+ if(angertext && angertext != "")
+ for(var/mob/O in viewers(src, null))
+ O.show_message("\red [src] [src.angertext] at [target:name]!", 1)
src.task = "chasing"
return
@@ -75,7 +76,7 @@ Contains the procs that control attacking critters
src.anchored = 0
src.density = 0
walk_to(src,0)
- src.visible_message("[src] dies!")
+ src.visible_message("[src] [deathtext]")
Harvest(var/obj/item/weapon/W, var/mob/living/user)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index e3d151d2ba..bf3df67ac3 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -252,6 +252,7 @@ proc/slur(phrase)
p++//for each letter p is increased to find where the next letter will be.
return copytext(sanitize(t),1,MAX_MESSAGE_LEN)
+
proc/Gibberish(t, p)
/* Turn text into complete gibberish! */
var/returntext = ""
@@ -269,6 +270,7 @@ proc/Gibberish(t, p)
return returntext
+
/proc/ninjaspeak(n)
/*
The difference with stutter is that this proc can stutter more than 1 letter
diff --git a/icons/mob/critter.dmi b/icons/mob/critter.dmi
index 0febb340d1..2b5a718991 100644
Binary files a/icons/mob/critter.dmi and b/icons/mob/critter.dmi differ