]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/tturrets/units/unit_walker.qc
7fddd579986f9a59b4de0192e25e0a0bfdfa64d5
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / tturrets / units / unit_walker.qc
1 #define ANIM_NO         0
2 #define ANIM_TURN       1
3 #define ANIM_WALK       2
4 #define ANIM_RUN        3
5 #define ANIM_STRAFE_L   4
6 #define ANIM_STRAFE_R   5
7 #define ANIM_JUMP       6
8 #define ANIM_LAND       7
9 #define ANIM_PAIN       8
10 #define ANIM_MEELE      9
11 #define ANIM_SWIM       10
12 #define ANIM_ROAM       11
13
14 .float animflag;
15
16 #define WALKER_MIN '-70 -70 0'
17 #define WALKER_MAX '70 70 95'
18
19 #define WALKER_PATH(s,e) pathlib_astar(s,e)
20
21 float walker_firecheck()
22 {
23     if (self.animflag == ANIM_MEELE)
24         return 0;
25
26     return turret_stdproc_firecheck();
27 }
28
29 void walker_meele_do_dmg()
30 {
31     vector where;
32     entity e;
33     
34     makevectors(self.angles);
35     where = self.origin + v_forward * 128;
36
37     e = findradius(where,32);
38     while (e)
39     {
40         if (turret_validate_target(self,e,self.target_validate_flags))
41             if (e != self && e.owner != self)
42                 Damage(e, self, self, autocvar_g_turrets_unit_walker_std_meele_dmg ,DEATH_TURRET,'0 0 0', v_forward * autocvar_g_turrets_unit_walker_std_meele_force);
43
44         e = e.chain;
45     }
46 }
47 void walker_setnoanim()
48 {
49     turrets_setframe(ANIM_NO, FALSE);
50     self.animflag = self.owner.frame;
51 }
52
53 void walker_animate()
54 {
55     vector real_angle;
56     float  vz;
57
58     real_angle = vectoangles(self.steerto) - self.angles;
59     vz         = self.velocity_z;
60
61     if (self.tur_head.frame != 0)
62         self.tur_head.frame = self.tur_head.frame + 1;
63
64     if (self.tur_head.frame > 12)
65         self.tur_head.frame = 0;
66         
67     switch (self.animflag)
68     {
69
70     case ANIM_NO:
71         turrets_setframe(0, FALSE);
72         movelib_beak_simple(autocvar_g_turrets_unit_walker_speed_stop);
73         break;
74
75     /*
76     case ANIM_REVERSE:
77         if ((self.frame < 5) || (self.frame > 25))
78             self.frame = 25;
79
80         self.frame = self.frame -1;
81         movelib_move_simple(v_forward * -1, autocvar_g_turrets_unit_walker_speed_walk, 0.6);
82
83         if (self.frame < 5)
84             self.frame = 25;
85
86         break;
87     */
88
89     case ANIM_TURN:
90         self.angles_y += bound(-15, shortangle_f(real_angle_y, self.angles_y), 15);
91         movelib_beak_simple(autocvar_g_turrets_unit_walker_speed_stop);
92         break;
93
94     case ANIM_WALK:
95         self.angles_y += bound(-10, shortangle_f(real_angle_y, self.angles_y), 10);
96         movelib_move_simple(v_forward, autocvar_g_turrets_unit_walker_speed_walk, 0.6);
97         break;
98
99     case ANIM_RUN:
100         self.angles_y += bound(-5, shortangle_f(real_angle_y, self.angles_y), 5);
101         movelib_move_simple(v_forward, autocvar_g_turrets_unit_walker_speed_run, 0.6);
102         break;
103
104     case ANIM_STRAFE_L:
105         self.angles_y += bound(-2.5, shortangle_f(real_angle_y, self.angles_y), 2.5);
106         movelib_move_simple(v_right * -1, autocvar_g_turrets_unit_walker_speed_walk, 0.8);
107         break;
108
109     case ANIM_STRAFE_R:
110         self.angles_y += bound(-2.5, shortangle_f(real_angle_y, self.angles_y), 2.5);
111         movelib_move_simple(v_right, autocvar_g_turrets_unit_walker_speed_walk, 0.8);
112         break;
113
114     case ANIM_JUMP:
115         self.velocity += '0 0 1' * autocvar_g_turrets_unit_walker_speed_jump;
116         break;
117
118     case ANIM_LAND:
119         break;
120
121     case ANIM_PAIN:
122         if(self.frame != ANIM_PAIN)
123             defer(0.25, walker_setnoanim);
124         
125         break;
126
127     case ANIM_MEELE:
128         if(self.frame != ANIM_MEELE)
129         {
130             defer(0.41, walker_setnoanim);
131             defer(0.21, walker_meele_do_dmg);
132         }
133
134         movelib_beak_simple(autocvar_g_turrets_unit_walker_speed_stop);
135         break;
136
137     case ANIM_SWIM:
138         self.angles_y += bound(-10, shortangle_f(real_angle_y, self.angles_y), 10);
139         self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10);
140         movelib_move_simple(v_forward, autocvar_g_turrets_unit_walker_speed_swim, 0.3);
141         vz = self.velocity_z + sin(time * 4) * 8;
142         break;
143
144     case ANIM_ROAM:
145         self.angles_y += bound(-5, shortangle_f(real_angle_y, self.angles_y), 5);
146         movelib_move_simple(v_forward ,autocvar_g_turrets_unit_walker_speed_roam, 0.5);
147         break;
148
149     }
150
151     self.velocity_z = vz;
152
153     if (self.flags & FL_ONGROUND)
154         movelib_groundalign4point(300, 100, 0.25);
155
156 }
157
158
159 void walker_rocket_explode()
160 {
161     vector org2;
162
163     if (self.event_damage != SUB_Null)
164     {
165         self.event_damage = SUB_Null;
166         self.think = walker_rocket_explode;
167         self.nextthink = time;
168         return;
169     }
170
171     sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
172     org2 = findbetterlocation (self.origin, 16);
173
174     pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1);
175     RadiusDamage (self, self.owner, autocvar_g_turrets_unit_walker_std_rocket_dmg, 0, autocvar_g_turrets_unit_walker_std_rocket_radius, world,autocvar_g_turrets_unit_walker_std_rocket_force, DEATH_TURRET, world);
176
177     remove (self);
178 }
179
180 void walker_rocket_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
181 {
182     self.health = self.health - damage;
183     self.velocity = self.velocity + vforce;
184     if (self.health <= 0)
185         walker_rocket_explode();
186 }
187
188 #define WALKER_ROCKET_MOVE movelib_move_simple(newdir, autocvar_g_turrets_unit_walker_std_rocket_speed, autocvar_g_turrets_unit_walker_std_rocket_turnrate); UpdateCSQCProjectile(self)
189 void walker_rocket_loop();
190 void walker_rocket_think()
191 {
192     vector newdir;
193     float edist;
194     float itime;
195     float m_speed;
196
197     self.nextthink = time;
198
199     edist = vlen(self.enemy.origin - self.origin);
200
201     // Simulate crude guidance
202     if (self.cnt < time)
203     {
204         if (edist < 1000)
205             self.tur_shotorg = randomvec() * min(edist,64);
206         else
207             self.tur_shotorg = randomvec() * min(edist,256);
208
209         self.cnt = time + 0.5;
210     }
211
212     if (edist < 256)
213         self.tur_shotorg = '0 0 0';
214
215
216     if (self.tur_health < time)
217     {
218         self.think = walker_rocket_explode;
219         self.nextthink = time;
220         return;
221     }
222
223     if (self.shot_dmg != 1337)
224         if (random() < 0.01)
225         {
226             walker_rocket_loop();
227             return;
228         }
229
230     m_speed = vlen(self.velocity);
231
232     // Enemy dead? just keep on the current heading then.
233     if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO))
234     {
235         // Make sure we dont return to tracking a respawned entity
236         self.enemy = world;
237     }
238
239     if (self.enemy)
240     {
241         itime = max(edist / m_speed,1);
242         newdir = steerlib_pull(self.enemy.origin + self.tur_shotorg);
243     }
244     else
245     {
246         newdir  = normalize(self.velocity);
247     }
248
249     WALKER_ROCKET_MOVE;
250 }
251
252 void walker_rocket_loop3()
253 {
254     vector newdir;
255     self.nextthink = time;
256
257     if (self.tur_health < time)
258     {
259         self.think = walker_rocket_explode;
260         return;
261     }
262
263     if (vlen(self.origin - self.tur_shotorg) < 100 )
264     {
265         self.think = walker_rocket_think;
266         return;
267     }
268
269     newdir = steerlib_pull(self.tur_shotorg);
270     WALKER_ROCKET_MOVE;
271
272     self.angles = vectoangles(self.velocity);
273 }
274
275 void walker_rocket_loop2()
276 {
277     vector newdir;
278
279     self.nextthink = time;
280
281     if (self.tur_health < time)
282     {
283         self.think = walker_rocket_explode;
284         return;
285     }
286
287     if (vlen(self.origin - self.tur_shotorg) < 100 )
288     {
289         self.tur_shotorg = self.origin - '0 0 200';
290         self.think = walker_rocket_loop3;
291         return;
292     }
293
294     newdir = steerlib_pull(self.tur_shotorg);
295     WALKER_ROCKET_MOVE;
296 }
297
298 void walker_rocket_loop()
299 {
300     self.nextthink = time;
301     self.tur_shotorg = self.origin + '0 0 300';
302     self.think = walker_rocket_loop2;
303     self.shot_dmg = 1337;
304 }
305
306 void walker_fire_rocket(vector org)
307 {
308
309     entity rocket;
310
311
312     //self.angles_x *= -1;
313     fixedmakevectors(self.angles);
314     //self.angles_x *= -1;
315
316     te_explosion (org);
317
318     rocket = spawn ();
319     setorigin(rocket, org);
320
321     sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", VOL_BASE, ATTN_NORM);
322     setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
323
324     rocket.classname          = "walker_rocket";
325     rocket.owner              = self;
326
327     rocket.bot_dodge          = TRUE;
328     rocket.bot_dodgerating    = 50;
329
330     rocket.takedamage         = DAMAGE_YES;
331
332     rocket.damageforcescale   = 2;
333     rocket.health             = 25;
334     rocket.tur_shotorg        = randomvec() * 512;
335     rocket.cnt                = time + 1;
336     rocket.enemy              = self.enemy;
337
338     if (random() < 0.01)
339         rocket.think          = walker_rocket_loop;
340     else
341         rocket.think          = walker_rocket_think;
342
343     rocket.event_damage       = walker_rocket_damage;
344
345     rocket.nextthink          = time;// + 0.25;
346     rocket.movetype           = MOVETYPE_FLY;
347     rocket.velocity           = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * autocvar_g_turrets_unit_walker_std_rocket_speed;
348     rocket.angles             = vectoangles(rocket.velocity);
349     rocket.touch              = walker_rocket_explode;
350     rocket.flags              = FL_PROJECTILE;
351     rocket.solid              = SOLID_BBOX;
352     rocket.tur_health         = time + 9;
353
354     CSQCProjectile(rocket, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, has fly sound
355 }
356
357 void rv_think()
358 {
359     float f;
360     vector org;
361     entity oldself;
362
363     if (self.owner.deadflag != DEAD_NO)
364     {
365         remove(self);
366         return;
367     }
368
369     self.cnt = self.cnt -1;
370
371     if (self.cnt < 0)
372     {
373         remove(self);
374         return;
375     }
376
377     if (self.cnt > 1)
378         f = gettagindex(self.owner,"tag_rocket01");
379     else
380         f = gettagindex(self.owner,"tag_rocket02");
381
382     org = gettaginfo(self.owner,f);
383
384     self.nextthink = time + 0.2;
385     oldself = self;
386     self = self.owner;
387     walker_fire_rocket(org);
388     self = oldself;
389 }
390
391 void walker_move_path()
392 {
393     // Are we close enougth to a path node to switch to the next?
394     if (vlen(self.origin  - self.pathcurrent.origin) < 64)
395         if (self.pathcurrent.path_next == world)
396         {
397             // Path endpoint reached
398             pathlib_deletepath(self.pathcurrent.owner);
399             self.pathcurrent = world;
400
401             if (self.pathgoal)
402             {
403                 if (self.pathgoal.use)
404                     self.pathgoal.use();
405
406                 if (self.pathgoal.enemy)
407                 {
408                     self.pathcurrent = WALKER_PATH(self.pathgoal.origin,self.pathgoal.enemy.origin);
409                     self.pathgoal = self.pathgoal.enemy;
410                 }
411             }
412             else
413                 self.pathgoal = world;
414         }
415         else
416             self.pathcurrent = self.pathcurrent.path_next;
417
418     switch (self.waterlevel)
419     {
420     case 0:
421         self.animflag = ANIM_WALK;
422     case 1:
423     case 2:
424         if (self.animflag == ANIM_WALK)
425             self.animflag = ANIM_WALK;
426         else
427             self.animflag = ANIM_SWIM;
428         break;
429     case 3:
430         self.animflag = ANIM_SWIM;
431     }
432
433     self.moveto = self.pathcurrent.origin;
434     self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95);
435
436 }
437
438 .vector enemy_last_loc;
439 void walker_move_enemy()
440 {
441     switch (self.waterlevel)
442     {
443     case 0:
444         if (self.tur_dist_enemy > 500)
445             self.animflag = ANIM_RUN;
446         else
447             self.animflag = ANIM_WALK;
448     case 1:
449     case 2:
450         if (self.animflag != ANIM_SWIM)
451             self.animflag = ANIM_WALK;
452         else
453             self.animflag = ANIM_SWIM;
454         break;
455     case 3:
456         self.animflag = ANIM_SWIM;
457     }
458
459     self.moveto = self.enemy.origin;
460     self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
461     self.enemy_last_loc = self.enemy.origin;
462     
463 }
464
465 void walker_move_idle_pause()
466 {
467     self.moveto   = self.origin;
468     self.steerto  = v_forward;
469     self.animflag = ANIM_NO;
470 }
471
472 void walker_move_idle_roam()
473 {
474 }
475
476 void walker_move_idle()
477 {
478 }
479
480 float walker_attack_meele()
481 {
482     vector wish_angle;
483
484     if (self.animflag == ANIM_SWIM || self.animflag == ANIM_MEELE)
485         return 0;
486
487     wish_angle = angleofs(self, self.enemy);
488
489     if (self.tur_dist_enemy > autocvar_g_turrets_unit_walker_std_meele_range)
490         return 0;
491     else
492         if (fabs(wish_angle_y) > 15)
493             return 0;
494
495     self.moveto   = self.enemy.origin;
496     self.steerto  = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
497     self.animflag = ANIM_MEELE;
498
499     return 1;
500 }
501
502 float walker_attack_rockets()
503 {
504     if (self.tur_head.attack_finished_single > time)
505         return 0;
506
507     if (self.tur_dist_enemy < autocvar_g_turrets_unit_walker_std_rockets_range_min)
508         return 0;
509
510     if (self.tur_dist_enemy > autocvar_g_turrets_unit_walker_std_rockets_range)
511         return 0;
512
513     entity rv;
514
515     rv           = spawn();
516     rv.think     = rv_think;
517     rv.nextthink = time;
518     rv.cnt       = 4;
519     rv.owner     = self;
520
521     self.tur_head.attack_finished_single = time + autocvar_g_turrets_unit_walker_std_rocket_refire;
522
523     return 1;
524 }
525
526 void walker_postthink()
527 {
528     fixedmakevectors(self.angles);
529
530     if ((self.spawnflags & TSF_NO_PATHBREAK) && self.pathcurrent)
531         walker_move_path();
532     else if (self.enemy == world)
533     {
534         if(self.pathcurrent)
535             walker_move_path();
536         else
537             walker_move_idle_pause();
538     }
539     else
540     {
541         if not (self.animflag == ANIM_MEELE)
542             if not (walker_attack_rockets())
543                 walker_attack_meele();
544
545         if not (self.animflag == ANIM_MEELE)
546             walker_move_enemy();
547     }
548
549     walker_animate();
550     
551     if(self.origin != self.oldorigin)
552         self.SendFlags |= TNSF_MOVE;
553     
554     self.oldorigin = self.origin;
555     turrets_setframe(self.animflag, FALSE);
556
557 }
558
559 void walker_attack()
560 {
561     sound (self, CHAN_WEAPON, "weapons/uzi_fire.wav", VOL_BASE, ATTN_NORM);
562     fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, self.shot_speed, 5, self.shot_dmg, 0, self.shot_force, DEATH_TURRET, 0, 1, autocvar_g_balance_uzi_bulletconstant);
563     endFireBallisticBullet();
564     pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
565     
566     self.tur_head.frame    = self.tur_head.frame + 1;
567 }
568
569
570 void walker_respawnhook()
571 {
572     entity e;
573
574     // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn.
575     if(self.movetype  != MOVETYPE_WALK)
576                 return;
577                 
578     setorigin(self, self.pos1);
579     self.angles = self.pos2;
580     
581     if (self.target != "")
582     {
583         e = find(world,targetname,self.target);
584         if (!e)
585         {
586             dprint("Warning! initital waypoint for Walker does NOT exsist!\n");
587             self.target = "";
588         }
589
590         if (e.classname != "turret_checkpoint")
591             dprint("Warning: not a turrret path\n");
592         else
593         {
594             self.pathcurrent = WALKER_PATH(self.origin,e.origin);
595             self.pathgoal = e;
596         }
597     }
598     self.colormod = randomvec() * 5;
599 }
600
601 void walker_diehook()
602 {
603     if (self.pathcurrent)
604         pathlib_deletepath(self.pathcurrent.owner);
605
606     self.pathcurrent = world;
607 }
608
609 void turret_walker_dinit()
610 {
611
612     entity e;
613
614     if (self.netname == "")      self.netname     = "Walker Turret";
615
616     self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
617     self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MOVE ;
618     self.aim_flags = TFL_AIM_LEAD;
619
620     if (autocvar_g_antilag_bullets)
621         self.turrcaps_flags |= TFL_TURRCAPS_HITSCAN;
622     else
623         self.aim_flags      |= TFL_AIM_SHOTTIMECOMPENSATE;
624
625
626     self.turret_respawnhook = walker_respawnhook;
627     self.turret_diehook = walker_diehook;
628
629     self.ticrate = 0.05;
630     if (turret_stdproc_init("walker_std", "models/turrets/walker_body.md3", "models/turrets/walker_head_minigun.md3", TID_WALKER) == 0)
631     {
632         remove(self);
633         return;
634     }
635
636     self.damage_flags |= TFL_DMG_DEATH_NOGIBS;
637     self.target_select_flags   = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
638     self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
639
640     self.iscreature = TRUE;
641     self.movetype   = MOVETYPE_WALK;
642     self.solid      = SOLID_SLIDEBOX;
643     self.takedamage = DAMAGE_AIM;
644
645
646     setsize(self, WALKER_MIN, WALKER_MAX);
647
648     setorigin(self, self.origin);
649     tracebox(self.origin + '0 0 128', self.mins,self.maxs,self.origin - '0 0 10000', MOVE_NORMAL, self);
650     setorigin(self, trace_endpos + '0 0 4');
651     self.pos1 = self.origin;
652     self.pos2 = self.angles;
653     
654     self.idle_aim = '0 0 0';
655     self.turret_firecheckfunc = walker_firecheck;
656     self.turret_firefunc      = walker_attack;
657     self.turret_postthink     = walker_postthink;
658
659     if (self.target != "")
660     {
661         e = find(world,targetname,self.target);
662         if (!e)
663         {
664             dprint("Initital waypoint for walker does NOT exsist, fix your map!\n");
665             self.target = "";
666         }
667
668         if (e.classname != "turret_checkpoint")
669             dprint("Warning: not a turrret path\n");
670         else
671         {
672             self.pathcurrent = WALKER_PATH(self.origin,e.origin);
673             self.pathgoal = e;
674         }
675     }
676 }
677
678
679 void spawnfunc_turret_walker()
680 {
681     g_turrets_common_precash();
682
683     precache_model ("models/turrets/walker_head_minigun.md3");
684     precache_model ("models/turrets/walker_body.md3");
685     precache_model ( "models/turrets/rocket.md3");
686     precache_sound ( "weapons/rocket_impact.wav" );
687
688     self.think = turret_walker_dinit;
689     self.nextthink = time + 0.5;
690 }