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