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