]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/attic/monsters/monster_zombie.qc
Merge remote-tracking branch 'origin/master' into samual/update_effects_tab
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / attic / monsters / monster_zombie.qc
1 //#define MONSTES_ENABLED
2 #ifdef MONSTES_ENABLED
3
4 float autocvar_g_monster_zombie_attack_run_damage;
5 float autocvar_g_monster_zombie_attack_run_delay;
6 float autocvar_g_monster_zombie_attack_run_force;
7 float autocvar_g_monster_zombie_attack_run_hitrange;
8 float autocvar_g_monster_zombie_attack_run_range;
9 float autocvar_g_monster_zombie_attack_stand_damage;
10 float autocvar_g_monster_zombie_attack_stand_delay;
11 float autocvar_g_monster_zombie_attack_stand_force;
12 float autocvar_g_monster_zombie_attack_stand_range;
13 float autocvar_g_monster_zombie_health;
14 float autocvar_g_monster_zombie_idle_timer_max;
15 float autocvar_g_monster_zombie_idle_timer_min;
16 float autocvar_g_monster_zombie_movespeed;
17 float autocvar_g_monster_zombie_respawntime;
18 float autocvar_g_monster_zombie_stopspeed;
19 float autocvar_g_monster_zombie_targetrange;
20 float autocvar_g_monster_zombie_turnspeed;
21 float autocvar_g_monsters;
22
23
24 #define zombie_anim_attackleap         0
25 #define zombie_anim_attackrun1         1
26 #define zombie_anim_attackrun2         2
27 #define zombie_anim_attackrun3         3
28 #define zombie_anim_attackstanding1    4
29 #define zombie_anim_attackstanding2    5
30 #define zombie_anim_attackstanding3    6
31 #define zombie_anim_blockend           7
32 #define zombie_anim_blockstart         8
33 #define zombie_anim_deathback1         9
34 #define zombie_anim_deathback2         10
35 #define zombie_anim_deathback3         11
36 #define zombie_anim_deathfront1        12
37 #define zombie_anim_deathfront2        13
38 #define zombie_anim_deathfront3        14
39 #define zombie_anim_deathleft1         15
40 #define zombie_anim_deathleft2         16
41 #define zombie_anim_deathright1        17
42 #define zombie_anim_deathright2        18
43 #define zombie_anim_idle               19
44 #define zombie_anim_painback1          20
45 #define zombie_anim_painback2          21
46 #define zombie_anim_painfront1         22
47 #define zombie_anim_painfront2         23
48 #define zombie_anim_runbackwards       24
49 #define zombie_anim_runbackwardsleft   25
50 #define zombie_anim_runbackwardsright  26
51 #define zombie_anim_runforward         27
52 #define zombie_anim_runforwardleft     28
53 #define zombie_anim_runforwardright    29
54 #define zombie_anim_spawn              30
55
56 #define ZOMBIE_MIN                                       '-18 -18 -25'
57 #define ZOMBIE_MAX                                       '18 18 47'
58
59 #define ZV_IDLE     10
60
61 #define ZV_PATH     100
62 #define ZV_HUNT     200
63
64 #define ZV_ATTACK_FIND  10
65 #define ZV_ATTACK_RUN   20
66 #define ZV_ATTACK_STAND 30
67
68 #define ZV_PATH2 10000
69
70 //.entity verbs_idle;
71 //.entity verbs_attack;
72 //.entity verbs_move;
73
74 //.float  state_timeout;
75 //.void() monster_state;
76 #define MONSTERFLAG_NORESPAWN 2
77
78 void zombie_spawn();
79
80 float zombie_scoretarget(entity trg)
81 {
82     float  tmp;
83     vector ang1;
84
85     if (trg.takedamage == DAMAGE_AIM)
86     if not (trg.flags & FL_NOTARGET)
87     if (trg.deadflag == DEAD_NO)
88     if (trg.team != self.team)
89     {
90         if((self.origin_z - trg.origin_z) < 128)
91         {
92             ang1 = normalize(self.origin - trg.origin);
93             tmp = vlen(ang1 - v_forward);
94             if(tmp > 1.5)
95             {
96                 traceline(self.origin + '0 0 47',trg.origin + '0 0 32',MOVE_NORMAL,self);
97                 if(trace_ent != trg)
98                     return 0;
99
100                 return (autocvar_g_monster_zombie_targetrange - vlen(self.origin - trg.origin)) * tmp;
101             }
102             else if(self.enemy == trg)
103                 return (autocvar_g_monster_zombie_targetrange - vlen(self.origin - trg.origin)) * tmp;
104         }
105     }
106
107     return 0;
108 }
109
110 void zombie_corpse_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
111 {
112     //dprint("zombie_corpse_damage\n");
113     Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker);
114
115     self.health -= damage;
116
117     if(self.health < 0)
118     {
119         Violence_GibSplash(self, 1, 1, attacker);
120         remove(self);
121     }
122 }
123
124 void zombie_die(vector dir)
125 {
126     vector v;
127     float f;
128
129     entity dummy;
130
131     dummy = spawn();
132     setmodel(dummy,"models/monsters/zombie.dpm");
133     setorigin(dummy, self.origin);
134     dummy.velocity  = self.velocity;
135     dummy.movetype  = MOVETYPE_BOUNCE;
136     dummy.think     = SUB_Remove;
137     dummy.nextthink = time + 3;
138     dummy.health    = 50;
139     dummy.takedamage = DAMAGE_YES;
140     dummy.event_damage = zombie_corpse_damage;
141     dummy.solid      = SOLID_CORPSE;
142     setsize(dummy,self.mins,self.maxs);
143
144     SUB_SetFade(dummy,time + 5,2);
145
146
147     v = normalize(self.origin - dir);
148     f = vlen(v_forward - v) - 1;
149     if(f > 0.5)
150         dummy.frame = zombie_anim_deathfront1 + rint(random() * 2);
151     else if(f < 0.5)
152         dummy.frame = zombie_anim_deathback1 + rint(random() * 2);
153     else
154     {
155         f = vlen(v_right - v) - 1;
156         if(f > 0.5)
157             dummy.frame = zombie_anim_deathright1 + rint(random() * 2);
158         else if(f < 0.5)
159             dummy.frame = zombie_anim_deathleft1 + rint(random() * 2);
160     }
161
162
163     if(self.spawnflags & MONSTERFLAG_NORESPAWN)
164     {
165         self.think = SUB_Remove;
166         self.nextthink = time;
167         return;
168     }
169
170     setmodel(self,"");
171     self.solid          = SOLID_NOT;
172     self.takedamage     = DAMAGE_NO;
173     self.event_damage   = func_null;
174     self.enemy          = world;
175     self.think          = zombie_spawn;
176     self.nextthink      = time + autocvar_g_monster_zombie_respawntime;
177     self.pain_finished  = self.nextthink;
178 }
179
180 void zombie_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
181 {
182
183     vector v;
184     float f;
185
186     v = normalize(self.origin - hitloc);
187     f = vlen(v_forward - v) - 1;
188
189
190     self.health -= damage;
191     self.velocity = self.velocity + force;
192     if(self.health <= 0)
193     {
194         zombie_die(hitloc);
195         return;
196     }
197
198     Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker);
199
200         if (damage > 50)
201                 Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, self, attacker);
202         if (damage > 100)
203                 Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, self, attacker);
204
205     if (time > self.pain_finished)
206     {
207         if(f < 0.5)
208         {
209             if(random() < 0.5)
210                 self.frame = zombie_anim_painback1;
211             else
212                 self.frame = zombie_anim_painback2;
213         }
214         else
215         {
216             if(random() < 0.5)
217                 self.frame = zombie_anim_painfront1;
218             else
219                 self.frame = zombie_anim_painfront2;
220         }
221
222         self.pain_finished = time + 0.36;
223     }
224 }
225
226 .vector bvec;
227 .float bvec_time;
228
229 void zombie_move()
230 {
231     vector real_angle;
232     float vz, tdiff, tspeed;
233
234     tdiff = time - self.zoomstate;
235     tspeed = tdiff * autocvar_g_monster_zombie_turnspeed;
236     vz = self.velocity_z;
237     self.zoomstate = time;
238
239     if(self.bvec_time < time)
240     {
241         self.bvec_time = time + 0.2;
242         self.bvec = steerlib_beamsteer(steerlib_attract2(self.moveto,0.5,500,0.95),512,32,34,64);
243     }
244
245     if(self.enemy)
246         self.moveto = self.enemy.origin;
247     else
248         self.moveto = self.origin + v_forward;
249
250     self.steerto = normalize(steerlib_attract2(self.moveto,0.5,500,0.95) + self.bvec);
251
252     self.angles_y = safeangle(self.angles_y);
253     real_angle = vectoangles(self.steerto) - self.angles;
254     self.angles_y += bound(-10, real_angle_y, 10);
255
256     if(vlen(self.origin - self.moveto) > 64)
257     {
258         movelib_move_simple(v_forward ,autocvar_g_monster_zombie_movespeed,0.6);
259         if(time > self.pain_finished)
260             if(self.attack_finished_single < time)
261                 self.frame = zombie_anim_runforward;
262     }
263     else
264     {
265         movelib_beak_simple(autocvar_g_monster_zombie_stopspeed);
266         if(time > self.pain_finished)
267             if(self.attack_finished_single < time)
268                 self.frame = zombie_anim_idle;
269     }
270
271     self.velocity_z = vz;
272     self.steerto = self.origin;
273 }
274
275 float zombie_verb_idle_roam(float eval)
276 {
277     switch (eval)
278     {
279     case VCM_EVAL:
280
281         if(self.enemy)
282             return VS_CALL_NO;
283
284         return verb.verb_static_value;
285
286     case VCM_DO:
287
288         self.moveto = v_forward * 128;
289         self.steerto = v_forward; //steerlib_beamsteer(v_forward,512,32,34,64);
290
291         return VS_CALL_YES_DOING;
292     }
293
294     return VS_CALL_YES_DONE;
295 }
296
297 float zombie_verb_idle_stand(float eval)
298 {
299     switch (eval)
300     {
301     case VCM_EVAL:
302
303         if(self.enemy)
304             return VS_CALL_NO;
305
306         return verb.verb_static_value;
307
308     case VCM_DO:
309
310         self.moveto   = self.origin;
311         self.frame    = zombie_anim_idle;
312         self.velocity = '0 0 0';
313
314         return VS_CALL_YES_DOING;
315     }
316
317     return VS_CALL_YES_DONE;
318 }
319
320 float zombie_verb_idle(float eval)
321 {
322     switch (eval)
323     {
324     case VCM_EVAL:
325
326         if(self.enemy)
327             return VS_CALL_NO;
328
329         return verb.verb_static_value;
330
331     case VCM_DO:
332         float t;
333
334         t = autocvar_g_monster_zombie_idle_timer_max -  autocvar_g_monster_zombie_idle_timer_min;
335         t = autocvar_g_monster_zombie_idle_timer_min + (random() * t);
336
337         if(random() < 0.5)
338             verbstack_push(self.verbs_idle, zombie_verb_idle_roam,  ZV_IDLE + 1, t, self);
339         else
340             verbstack_push(self.verbs_idle, zombie_verb_idle_stand, ZV_IDLE + 1, 0.1, self);
341
342         return VS_CALL_YES_DOING;
343     }
344
345     return VS_CALL_YES_DONE;
346 }
347
348 float zombie_verb_attack_findtarget(float eval)
349 {
350     switch (eval)
351     {
352     case VCM_EVAL:
353         if(self.enemy)
354             return VS_CALL_NO;
355
356         return verb.verb_static_value;
357
358     case VCM_DO:
359
360         entity trg, best_trg;
361         float trg_score, best_trg_score;
362
363         trg = findradius(self.origin,autocvar_g_monster_zombie_targetrange);
364         while(trg)
365         {
366             trg_score = zombie_scoretarget(trg);
367             if(trg_score > best_trg_score)
368             {
369                 best_trg = trg;
370                 best_trg_score = trg_score;
371             }
372
373             trg = trg.chain;
374         }
375
376         if(best_trg)
377         {
378             self.enemy = best_trg;
379             dprint("Selected: ",best_trg.netname, " as target.\n");
380         }
381
382         return VS_CALL_YES_DOING;
383     }
384
385     return VS_CALL_YES_DONE;
386 }
387
388 void zombie_runattack_damage()
389 {
390     entity oldself;
391     oldself = self;
392     self = self.owner;
393
394     if(vlen(self.origin - self.enemy.origin) > autocvar_g_monster_zombie_attack_run_hitrange)
395         return;
396
397     if(vlen(normalize(self.origin - self.enemy.origin) - v_forward) < 1.6)
398         return;
399
400     Damage(self.enemy, self, self, autocvar_g_monster_zombie_attack_run_damage, DEATH_TURRET, self.enemy.origin, normalize(self.enemy.origin - self.origin)  * autocvar_g_monster_zombie_attack_run_force);
401
402     self = oldself;
403     self.think = SUB_Remove;
404     self.nextthink = time;
405 }
406
407 float zombie_verb_attack_run(float eval)
408 {
409     switch (eval)
410     {
411     case VCM_EVAL:
412         if not (self.enemy)
413             return VS_CALL_NO;
414
415         if(self.attack_finished_single > time)
416             return VS_CALL_NO;
417
418         if(vlen(self.origin - self.enemy.origin) > autocvar_g_monster_zombie_attack_run_range)
419             return VS_CALL_NO;
420
421         if(vlen(normalize(self.origin - self.enemy.origin) - v_forward) < 1.6)
422             return VS_CALL_NO;
423
424         return verb.verb_static_value;
425
426     case VCM_DO:
427         entity pain;
428         pain = spawn();
429         pain.owner = self;
430         pain.think = zombie_runattack_damage;
431         pain.nextthink = time + autocvar_g_monster_zombie_attack_run_delay;
432
433         self.attack_finished_single = time + 0.7;
434         self.frame = zombie_anim_attackrun1 + rint(random() * 2);
435
436         return VS_CALL_YES_DOING;
437     }
438
439     return VS_CALL_YES_DONE;
440 }
441
442 void zombie_standattack_damage()
443 {
444     //entity oldself;
445     //oldself = self;
446     //self = self.owner;
447
448     setorigin(self,self.owner.origin + v_forward * 32);
449     RadiusDamage(self, self.owner, autocvar_g_monster_zombie_attack_stand_damage,autocvar_g_monster_zombie_attack_stand_damage,16,self, autocvar_g_monster_zombie_attack_stand_force,DEATH_TURRET,world);
450     //float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity ignore, float forceintensity, float deathtype, entity directhitentity)
451
452
453     //self = oldself;
454     self.think = SUB_Remove;
455     self.nextthink = time;
456 }
457
458 float zombie_verb_attack_stand(float eval)
459 {
460     switch (eval)
461     {
462     case VCM_EVAL:
463         if not (self.enemy)
464             return VS_CALL_NO;
465
466         if(self.attack_finished_single > time)
467             return VS_CALL_NO;
468
469         if(vlen(self.origin - self.enemy.origin) > autocvar_g_monster_zombie_attack_stand_range)
470             return VS_CALL_NO;
471
472         if(vlen(normalize(self.origin - self.enemy.origin) - v_forward) < 1.8)
473             return VS_CALL_NO;
474
475         return verb.verb_static_value;
476
477     case VCM_DO:
478         entity pain;
479         pain = spawn();
480         pain.owner = self;
481         pain.think = zombie_runattack_damage;
482         pain.nextthink = time + autocvar_g_monster_zombie_attack_stand_delay;
483
484         self.attack_finished_single = time + 0.7;
485         self.frame = zombie_anim_attackstanding1 + rint(random() * 1);
486         dprint("frame:",ftos(self.frame),"\n");
487
488         return VS_CALL_YES_DOING;
489     }
490
491     return VS_CALL_YES_DONE;
492 }
493
494 void zombie_think()
495 {
496     self.angles_x *= -1;
497     makevectors(self.angles);
498     self.angles_x *= -1;
499
500     if (zombie_scoretarget(self.enemy) == 0)
501         self.enemy = world;
502
503     verbstack_pop(self.verbs_attack);
504     //verbstack_pop(self.verbs_move);
505
506     if not (self.enemy)
507         verbstack_pop(self.verbs_idle);
508
509     zombie_move();
510
511     if(self.enemy)
512         self.nextthink = time;
513     else
514         self.nextthink = time + 0.2;
515 }
516
517 void zombie_spawn()
518 {
519     setmodel(self,"models/monsters/zombie.dpm");
520
521     self.solid          = SOLID_BBOX;
522     self.takedamage     = DAMAGE_AIM;
523     self.event_damage   = zombie_damage;
524     self.enemy          = world;
525     self.frame          = zombie_anim_spawn;
526     self.think          = zombie_think;
527     self.nextthink      = time + 2.1;
528     self.pain_finished  = self.nextthink;
529     self.movetype       = MOVETYPE_WALK;
530     self.health         = autocvar_g_monster_zombie_health;
531     self.velocity       = '0 0 0';
532     self.angles         = self.pos2;
533     self.moveto         = self.origin;
534     self.flags          = FL_MONSTER;
535
536     setorigin(self,self.pos1);
537     setsize(self,ZOMBIE_MIN,ZOMBIE_MAX);
538 }
539
540
541 void spawnfunc_monster_zombie()
542 {
543     if not(autocvar_g_monsters)
544     {
545         remove(self);
546         return;
547     }
548
549     precache_model("models/monsters/zombie.dpm");
550
551
552     self.verbs_idle   = spawn();
553     self.verbs_attack = spawn();
554
555     self.verbs_idle.owner = self;
556     self.verbs_attack.owner = self;
557
558     self.think      = zombie_spawn;
559     self.nextthink  = time + 2;
560
561     traceline(self.origin + '0 0 10', self.origin - '0 0 32', MOVE_WORLDONLY, self);
562
563     self.pos1 = trace_endpos;
564     self.pos2 = self.angles;
565     self.team = MAX_SHOT_DISTANCE -1;
566
567     verbstack_push(self.verbs_idle, zombie_verb_idle, ZV_IDLE,0 , self);
568
569     verbstack_push(self.verbs_attack, zombie_verb_attack_findtarget, ZV_ATTACK_FIND,0 , self);
570     verbstack_push(self.verbs_attack, zombie_verb_attack_run, ZV_ATTACK_RUN,0 , self);
571     verbstack_push(self.verbs_attack, zombie_verb_attack_stand, ZV_ATTACK_STAND,0 , self);
572
573 }
574
575 #endif // MONSTES_ENABLED