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