1 //#define MONSTES_ENABLED
4 #define zombie_anim_attackleap 0
5 #define zombie_anim_attackrun1 1
6 #define zombie_anim_attackrun2 2
7 #define zombie_anim_attackrun3 3
8 #define zombie_anim_attackstanding1 4
9 #define zombie_anim_attackstanding2 5
10 #define zombie_anim_attackstanding3 6
11 #define zombie_anim_blockend 7
12 #define zombie_anim_blockstart 8
13 #define zombie_anim_deathback1 9
14 #define zombie_anim_deathback2 10
15 #define zombie_anim_deathback3 11
16 #define zombie_anim_deathfront1 12
17 #define zombie_anim_deathfront2 13
18 #define zombie_anim_deathfront3 14
19 #define zombie_anim_deathleft1 15
20 #define zombie_anim_deathleft2 16
21 #define zombie_anim_deathright1 17
22 #define zombie_anim_deathright2 18
23 #define zombie_anim_idle 19
24 #define zombie_anim_painback1 20
25 #define zombie_anim_painback2 21
26 #define zombie_anim_painfront1 22
27 #define zombie_anim_painfront2 23
28 #define zombie_anim_runbackwards 24
29 #define zombie_anim_runbackwardsleft 25
30 #define zombie_anim_runbackwardsright 26
31 #define zombie_anim_runforward 27
32 #define zombie_anim_runforwardleft 28
33 #define zombie_anim_runforwardright 29
34 #define zombie_anim_spawn 30
36 #define ZOMBIE_MIN '-18 -18 -25'
37 #define ZOMBIE_MAX '18 18 47'
44 #define ZV_ATTACK_FIND 10
45 #define ZV_ATTACK_RUN 20
46 #define ZV_ATTACK_STAND 30
48 #define ZV_PATH2 10000
51 //.entity verbs_attack;
54 //.float state_timeout;
55 //.void() monster_state;
56 #define MONSTERFLAG_NORESPAWN 2
60 float zombie_scoretarget(entity trg)
65 if (trg.takedamage == DAMAGE_AIM)
66 if not (trg.flags & FL_NOTARGET)
67 if (trg.deadflag == DEAD_NO)
68 if (trg.team != self.team)
70 if((self.origin_z - trg.origin_z) < 128)
72 ang1 = normalize(self.origin - trg.origin);
73 tmp = vlen(ang1 - v_forward);
76 traceline(self.origin + '0 0 47',trg.origin + '0 0 32',MOVE_NORMAL,self);
80 return (cvar("g_monster_zombie_targetrange") - vlen(self.origin - trg.origin)) * tmp;
82 else if(self.enemy == trg)
83 return (cvar("g_monster_zombie_targetrange") - vlen(self.origin - trg.origin)) * tmp;
90 void zombie_corpse_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
92 //dprint("zombie_corpse_damage\n");
93 Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker);
95 self.health -= damage;
99 Violence_GibSplash(self, 1, 1, attacker);
104 void zombie_die(vector dir)
112 setmodel(dummy,"models/monsters/zombie.dpm");
113 setorigin(dummy, self.origin);
114 dummy.velocity = self.velocity;
115 dummy.movetype = MOVETYPE_BOUNCE;
116 dummy.think = SUB_Remove;
117 dummy.nextthink = time + 3;
119 dummy.takedamage = DAMAGE_YES;
120 dummy.event_damage = zombie_corpse_damage;
121 dummy.solid = SOLID_CORPSE;
122 setsize(dummy,self.mins,self.maxs);
124 SUB_SetFade(dummy,time + 5,2);
127 v = normalize(self.origin - dir);
128 f = vlen(v_forward - v) - 1;
130 dummy.frame = zombie_anim_deathfront1 + rint(random() * 2);
132 dummy.frame = zombie_anim_deathback1 + rint(random() * 2);
135 f = vlen(v_right - v) - 1;
137 dummy.frame = zombie_anim_deathright1 + rint(random() * 2);
139 dummy.frame = zombie_anim_deathleft1 + rint(random() * 2);
143 if(self.spawnflags & MONSTERFLAG_NORESPAWN)
145 self.think = SUB_Remove;
146 self.nextthink = time;
151 self.solid = SOLID_NOT;
152 self.takedamage = DAMAGE_NO;
153 self.event_damage = SUB_Null;
155 self.think = zombie_spawn;
156 self.nextthink = time + cvar("g_monster_zombie_respawntime");
157 self.pain_finished = self.nextthink;
160 void zombie_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
166 v = normalize(self.origin - hitloc);
167 f = vlen(v_forward - v) - 1;
170 self.health -= damage;
171 self.velocity = self.velocity + force;
178 Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker);
181 Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, self, attacker);
183 Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, self, attacker);
185 if (time > self.pain_finished)
190 self.frame = zombie_anim_painback1;
192 self.frame = zombie_anim_painback2;
197 self.frame = zombie_anim_painfront1;
199 self.frame = zombie_anim_painfront2;
202 self.pain_finished = time + 0.36;
212 float vz, tdiff, tspeed;
214 tdiff = time - self.zoomstate;
215 tspeed = tdiff * cvar("g_monster_zombie_turnspeed");
216 vz = self.velocity_z;
217 self.zoomstate = time;
219 if(self.bvec_time < time)
221 self.bvec_time = time + 0.2;
222 self.bvec = steerlib_beamsteer(steerlib_attract2(self.moveto,0.5,500,0.95),512,32,34,64);
226 self.moveto = self.enemy.origin;
228 self.moveto = self.origin + v_forward;
230 self.steerto = normalize(steerlib_attract2(self.moveto,0.5,500,0.95) + self.bvec);
232 self.angles_y = safeangle(self.angles_y);
233 real_angle = vectoangles(self.steerto) - self.angles;
234 self.angles_y += bound(-10, real_angle_y, 10);
236 if(vlen(self.origin - self.moveto) > 64)
238 movelib_move_simple(v_forward ,cvar("g_monster_zombie_movespeed"),0.6);
239 if(time > self.pain_finished)
240 if(self.attack_finished_single < time)
241 self.frame = zombie_anim_runforward;
245 movelib_beak_simple(cvar("g_monster_zombie_stopspeed"));
246 if(time > self.pain_finished)
247 if(self.attack_finished_single < time)
248 self.frame = zombie_anim_idle;
251 self.velocity_z = vz;
252 self.steerto = self.origin;
255 float zombie_verb_idle_roam(float eval)
264 return verb.verb_static_value;
268 self.moveto = v_forward * 128;
269 self.steerto = v_forward; //steerlib_beamsteer(v_forward,512,32,34,64);
271 return VS_CALL_YES_DOING;
274 return VS_CALL_YES_DONE;
277 float zombie_verb_idle_stand(float eval)
286 return verb.verb_static_value;
290 self.moveto = self.origin;
291 self.frame = zombie_anim_idle;
292 self.velocity = '0 0 0';
294 return VS_CALL_YES_DOING;
297 return VS_CALL_YES_DONE;
300 float zombie_verb_idle(float eval)
309 return verb.verb_static_value;
314 t = cvar("g_monster_zombie_idle_timer_max") - cvar("g_monster_zombie_idle_timer_min");
315 t = cvar("g_monster_zombie_idle_timer_min") + (random() * t);
318 verbstack_push(self.verbs_idle, zombie_verb_idle_roam, ZV_IDLE + 1, t, self);
320 verbstack_push(self.verbs_idle, zombie_verb_idle_stand, ZV_IDLE + 1, 0.1, self);
322 return VS_CALL_YES_DOING;
325 return VS_CALL_YES_DONE;
328 float zombie_verb_attack_findtarget(float eval)
336 return verb.verb_static_value;
340 entity trg, best_trg;
341 float trg_score, best_trg_score;
343 trg = findradius(self.origin,cvar("g_monster_zombie_targetrange"));
346 trg_score = zombie_scoretarget(trg);
347 if(trg_score > best_trg_score)
350 best_trg_score = trg_score;
358 self.enemy = best_trg;
359 dprint("Selected: ",best_trg.netname, " as target.\n");
362 return VS_CALL_YES_DOING;
365 return VS_CALL_YES_DONE;
368 void zombie_runattack_damage()
374 if(vlen(self.origin - self.enemy.origin) > cvar("g_monster_zombie_attack_run_hitrange"))
377 if(vlen(normalize(self.origin - self.enemy.origin) - v_forward) < 1.6)
380 Damage(self.enemy, self, self, cvar("g_monster_zombie_attack_run_damage"), DEATH_TURRET, self.enemy.origin, normalize(self.enemy.origin - self.origin) * cvar("g_monster_zombie_attack_run_force"));
383 self.think = SUB_Remove;
384 self.nextthink = time;
387 float zombie_verb_attack_run(float eval)
395 if(self.attack_finished_single > time)
398 if(vlen(self.origin - self.enemy.origin) > cvar("g_monster_zombie_attack_run_range"))
401 if(vlen(normalize(self.origin - self.enemy.origin) - v_forward) < 1.6)
404 return verb.verb_static_value;
410 pain.think = zombie_runattack_damage;
411 pain.nextthink = time + cvar("g_monster_zombie_attack_run_delay");
413 self.attack_finished_single = time + 0.7;
414 self.frame = zombie_anim_attackrun1 + rint(random() * 2);
416 return VS_CALL_YES_DOING;
419 return VS_CALL_YES_DONE;
422 void zombie_standattack_damage()
428 setorigin(self,self.owner.origin + v_forward * 32);
429 RadiusDamage(self, self.owner, cvar("g_monster_zombie_attack_stand_damage"),cvar("g_monster_zombie_attack_stand_damage"),16,self, cvar("g_monster_zombie_attack_stand_force"),DEATH_TURRET,world);
430 //float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity ignore, float forceintensity, float deathtype, entity directhitentity)
434 self.think = SUB_Remove;
435 self.nextthink = time;
438 float zombie_verb_attack_stand(float eval)
446 if(self.attack_finished_single > time)
449 if(vlen(self.origin - self.enemy.origin) > cvar("g_monster_zombie_attack_stand_range"))
452 if(vlen(normalize(self.origin - self.enemy.origin) - v_forward) < 1.8)
455 return verb.verb_static_value;
461 pain.think = zombie_runattack_damage;
462 pain.nextthink = time + cvar("g_monster_zombie_attack_stand_delay");
464 self.attack_finished_single = time + 0.7;
465 self.frame = zombie_anim_attackstanding1 + rint(random() * 1);
466 dprint("frame:",ftos(self.frame),"\n");
468 return VS_CALL_YES_DOING;
471 return VS_CALL_YES_DONE;
477 makevectors(self.angles);
480 if (zombie_scoretarget(self.enemy) == 0)
483 verbstack_pop(self.verbs_attack);
484 //verbstack_pop(self.verbs_move);
487 verbstack_pop(self.verbs_idle);
492 self.nextthink = time;
494 self.nextthink = time + 0.2;
499 setmodel(self,"models/monsters/zombie.dpm");
501 self.solid = SOLID_BBOX;
502 self.takedamage = DAMAGE_AIM;
503 self.event_damage = zombie_damage;
505 self.frame = zombie_anim_spawn;
506 self.think = zombie_think;
507 self.nextthink = time + 2.1;
508 self.pain_finished = self.nextthink;
509 self.movetype = MOVETYPE_WALK;
510 self.health = cvar("g_monster_zombie_health");
511 self.velocity = '0 0 0';
512 self.angles = self.pos2;
513 self.moveto = self.origin;
514 self.flags = FL_MONSTER;
516 setorigin(self,self.pos1);
517 setsize(self,ZOMBIE_MIN,ZOMBIE_MAX);
521 void spawnfunc_monster_zombie()
523 if not(cvar("g_monsters"))
529 precache_model("models/monsters/zombie.dpm");
532 self.verbs_idle = spawn();
533 self.verbs_attack = spawn();
535 self.verbs_idle.owner = self;
536 self.verbs_attack.owner = self;
538 self.think = zombie_spawn;
539 self.nextthink = time + 2;
541 traceline(self.origin + '0 0 10', self.origin - '0 0 32', MOVE_WORLDONLY, self);
543 self.pos1 = trace_endpos;
544 self.pos2 = self.angles;
545 self.team = MAX_SHOT_DISTANCE -1;
547 verbstack_push(self.verbs_idle, zombie_verb_idle, ZV_IDLE,0 , self);
549 verbstack_push(self.verbs_attack, zombie_verb_attack_findtarget, ZV_ATTACK_FIND,0 , self);
550 verbstack_push(self.verbs_attack, zombie_verb_attack_run, ZV_ATTACK_RUN,0 , self);
551 verbstack_push(self.verbs_attack, zombie_verb_attack_stand, ZV_ATTACK_STAND,0 , self);
555 #endif // MONSTES_ENABLED