2 * Special purpose fields:
3 * .delay - time at which to check if zombie's enemy is still in range
4 * .enemy - enemy of this zombie
5 * .state - state of the zombie, see ZOMBIE_STATE_*
9 float autocvar_g_monster_zombie;
10 float autocvar_g_monster_zombie_stopspeed;
11 float autocvar_g_monster_zombie_attack_leap_damage;
12 float autocvar_g_monster_zombie_attack_leap_delay;
13 float autocvar_g_monster_zombie_attack_leap_force;
14 float autocvar_g_monster_zombie_attack_leap_range;
15 float autocvar_g_monster_zombie_attack_leap_speed;
16 float autocvar_g_monster_zombie_attack_stand_damage;
17 float autocvar_g_monster_zombie_attack_stand_delay;
18 float autocvar_g_monster_zombie_attack_stand_range;
19 float autocvar_g_monster_zombie_health;
20 float autocvar_g_monster_zombie_idle_timer;
21 float autocvar_g_monster_zombie_speed_walk;
22 float autocvar_g_monster_zombie_speed_run;
23 float autocvar_g_monster_zombie_target_recheck_delay;
24 float autocvar_g_monster_zombie_target_range;
27 #define zombie_anim_attackleap 0
28 #define zombie_anim_attackrun1 1
29 #define zombie_anim_attackrun2 2
30 #define zombie_anim_attackrun3 3
31 #define zombie_anim_attackstanding1 4
32 #define zombie_anim_attackstanding2 5
33 #define zombie_anim_attackstanding3 6
34 #define zombie_anim_blockend 7
35 #define zombie_anim_blockstart 8
36 #define zombie_anim_deathback1 9
37 #define zombie_anim_deathback2 10
38 #define zombie_anim_deathback3 11
39 #define zombie_anim_deathfront1 12
40 #define zombie_anim_deathfront2 13
41 #define zombie_anim_deathfront3 14
42 #define zombie_anim_deathleft1 15
43 #define zombie_anim_deathleft2 16
44 #define zombie_anim_deathright1 17
45 #define zombie_anim_deathright2 18
46 #define zombie_anim_idle 19
47 #define zombie_anim_painback1 20
48 #define zombie_anim_painback2 21
49 #define zombie_anim_painfront1 22
50 #define zombie_anim_painfront2 23
51 #define zombie_anim_runbackwards 24
52 #define zombie_anim_runbackwardsleft 25
53 #define zombie_anim_runbackwardsright 26
54 #define zombie_anim_runforward 27
55 #define zombie_anim_runforwardleft 28
56 #define zombie_anim_runforwardright 29
57 #define zombie_anim_spawn 30
59 const vector ZOMBIE_MIN = '-18 -18 -25';
60 const vector ZOMBIE_MAX = '18 18 47';
62 #define ZOMBIE_STATE_SPAWNING 0
63 #define ZOMBIE_STATE_IDLE 1
64 #define ZOMBIE_STATE_ANGRY 2
65 #define ZOMBIE_STATE_ATTACK_LEAP 3
68 void spawnfunc_monster_zombie();
73 Monster_CheckDropCvars ("zombie");
75 self.solid = SOLID_NOT;
76 self.takedamage = DAMAGE_NO;
77 self.event_damage = func_null;
79 self.movetype = MOVETYPE_TOSS;
80 self.think = Monster_Fade;
81 self.nextthink = time + 2.1;
82 self.pain_finished = self.nextthink;
85 self.frame = zombie_anim_deathback1;
87 self.frame = zombie_anim_deathfront1;
89 monster_hook_death(); // for post-death mods
92 void zombie_attack_standing()
94 float rand = random(), dot = 0, bigdmg = 0;
99 if(self.monster_owner == self.enemy)
105 bigdmg = autocvar_g_monster_zombie_attack_stand_damage * self.scale;
107 //print("zombie attacks!\n");
108 makevectors (self.angles);
109 dot = normalize (self.enemy.origin - self.origin) * v_forward;
112 Damage(self.enemy, self, self, bigdmg * monster_skill, DEATH_MONSTER_MELEE, self.origin, '0 0 0');
115 if (!monster_isvalidtarget(self.enemy, self, FALSE))
119 self.frame = zombie_anim_attackstanding1;
120 else if (rand < 0.66)
121 self.frame = zombie_anim_attackstanding2;
123 self.frame = zombie_anim_attackstanding3;
125 self.nextthink = time + autocvar_g_monster_zombie_attack_stand_delay;
128 void zombie_attack_leap_touch()
130 vector angles_face = '0 0 0';
131 float bigdmg = autocvar_g_monster_zombie_attack_leap_damage * self.scale;
133 if (other.deadflag != DEAD_NO)
136 if (self.monster_owner == other)
139 if (other.takedamage == DAMAGE_NO)
142 //void Damage (entity targ, entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
143 traceline(self.origin, other.origin, FALSE, self);
145 angles_face = vectoangles(self.moveto - self.origin);
146 angles_face = normalize(angles_face) * autocvar_g_monster_zombie_attack_leap_force;
147 Damage(other, self, self, bigdmg * monster_skill, DEATH_MONSTER_MELEE, trace_endpos, angles_face);
149 // make this guy zombie's priority if it wasn't already
150 if (other.deadflag == DEAD_NO)
151 if (self.enemy != other)
154 self.touch = MonsterTouch;
157 void zombie_attack_leap()
159 vector angles_face = '0 0 0', vel = '0 0 0';
162 self.state = ZOMBIE_STATE_ATTACK_LEAP;
163 self.frame = zombie_anim_attackleap;
164 angles_face = vectoangles(self.enemy.origin - self.origin);
165 self.angles_y = angles_face_y ;
166 self.nextthink = time + autocvar_g_monster_zombie_attack_leap_delay;
167 self.touch = zombie_attack_leap_touch;
168 makevectors(self.angles);
169 vel = normalize(v_forward);
170 self.velocity = vel * autocvar_g_monster_zombie_attack_leap_speed;
175 float finished = FALSE, enemyDistance = 0, mySpeed = 0;
177 self.think = zombie_think;
179 if (self.state == ZOMBIE_STATE_ATTACK_LEAP) {
181 self.state = ZOMBIE_STATE_ANGRY;
182 self.touch = func_null;
185 if (self.state == ZOMBIE_STATE_SPAWNING) {
186 // become idle when zombie spawned
187 self.frame = zombie_anim_idle;
188 self.state = ZOMBIE_STATE_IDLE;
191 if(self.enemy && !monster_isvalidtarget(self.enemy, self, FALSE))
195 if (self.enemy.team == self.team || self.monster_owner == self.enemy)
198 if(teamplay && autocvar_g_monsters_teams && self.monster_owner.team != self.team)
199 self.monster_owner = world;
201 // remove enemy that ran away
203 if (self.delay <= time) // check if we can do the rescan now
204 if (vlen(self.origin - self.enemy.origin) > autocvar_g_monster_zombie_target_range * self.scale)
206 //print("removing enemy, he is too far: ", ftos(vlen(self.origin - self.enemy.origin)), "\n");
207 //print("delay was ", ftos(self.delay), "\n");
211 self.delay = time + autocvar_g_monster_zombie_target_recheck_delay;
213 // find an enemy if no enemy available
216 self.enemy = FindTarget(self);
218 self.delay = time + autocvar_g_monster_zombie_target_recheck_delay;
223 // make sure zombie is angry
224 self.state = ZOMBIE_STATE_ANGRY;
227 // this zombie has an enemy, attack if close enough, go to it if not!
228 traceline(self.origin, self.enemy.origin, FALSE, self);
229 enemyDistance = vlen(trace_endpos - self.origin);
230 mySpeed = vlen(self.velocity);
232 //print("speed ", ftos(mySpeed), "\n");
234 if (trace_ent == self.enemy)
235 if (self.enemy.deadflag == DEAD_NO)
237 if (enemyDistance <= autocvar_g_monster_zombie_attack_stand_range * self.scale)
239 //RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity ignore, float forceintensity, float deathtype, entity directhitentity)
240 zombie_attack_standing();
243 else if (enemyDistance <= autocvar_g_monster_zombie_attack_leap_range * self.scale)
245 // do attackleap (set yaw, velocity, and check do damage on the first player entity it touches)
246 zombie_attack_leap();
252 self.nextthink = time + 1;
256 monster_move(autocvar_g_monster_zombie_speed_run, autocvar_g_monster_zombie_speed_walk, autocvar_g_monster_zombie_stopspeed, zombie_anim_runforward, zombie_anim_runforward, zombie_anim_idle);
258 if (self.enemy || self.monster_owner)
260 self.nextthink = time + 0.1;
265 if not(self.enemy || self.monster_owner || self.goalentity)
268 //print("zombie is idling while waiting for some fresh meat...\n");
269 self.frame = ((mySpeed <= 20) ? zombie_anim_idle : zombie_anim_runforward);
270 self.nextthink = time + autocvar_g_monster_zombie_idle_timer * random();
277 self.health = autocvar_g_monster_zombie_health * self.scale;
279 self.classname = "monster_zombie";
280 self.nextthink = time + 2.1;
281 self.pain_finished = self.nextthink;
282 self.state = ZOMBIE_STATE_SPAWNING;
283 self.frame = zombie_anim_spawn;
284 self.think = zombie_think;
285 self.sprite_height = 50 * self.scale;
286 self.skin = rint(random() * 4);
288 monster_hook_spawn(); // for post-spawn mods
291 /*QUAKED monster_zombie (1 0 0) (-18 -18 -25) (18 18 47)
292 Zombie, 60 health points.
293 -------- KEYS --------
294 -------- SPAWNFLAGS --------
295 MONSTERFLAG_APPEAR: monster will spawn when triggered.
296 ---------NOTES----------
297 Original Quake 1 zombie entity used a smaller box ('-16 -16 -24', '16 16 32').
298 -------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY --------
299 modeldisabled="models/monsters/zombie.dpm"
301 void spawnfunc_monster_zombie()
303 if not(autocvar_g_monster_zombie)
309 self.monster_spawnfunc = spawnfunc_monster_zombie;
311 if(self.spawnflags & MONSTERFLAG_APPEAR)
313 self.think = func_null;
315 self.use = Monster_Appear;
319 if not (monster_initialize(
321 "models/monsters/zombie.dpm",
322 ZOMBIE_MIN, ZOMBIE_MAX,
324 zombie_die, zombie_spawn))