Merge branch 'master' into Mario/turrets
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / turrets / cl_turrets.qc
1 void turret_remove()
2 {
3         remove(self.tur_head);
4         //remove(self.enemy);
5         self.tur_head = world;
6 }
7
8 .vector glowmod;
9 void turret_changeteam()
10 {
11         switch(self.team - 1)
12         {
13                 case NUM_TEAM_1: // Red
14                         self.glowmod = '2 0 0';
15                         self.teamradar_color = '1 0 0';
16                         break;
17
18                 case NUM_TEAM_2: // Blue
19                         self.glowmod = '0 0 2';
20                         self.teamradar_color = '0 0 1';
21                         break;
22
23                 case NUM_TEAM_3: // Yellow
24                         self.glowmod = '1 1 0';
25                         self.teamradar_color = '1 1 0';
26                         break;
27
28                 case NUM_TEAM_4: // Pink
29                         self.glowmod = '1 0 1';
30                         self.teamradar_color = '1 0 1';
31                         break;
32         }
33
34         if(self.team)
35                 self.colormap = 1024 + (self.team - 1) * 17;
36
37         self.tur_head.colormap = self.colormap;
38         self.tur_head.glowmod = self.glowmod;
39
40 }
41
42 void turret_head_draw()
43 {
44         self.drawmask = MASK_NORMAL;
45 }
46
47 void turret_draw()
48 {
49         float dt;
50
51         dt = time - self.move_time;
52         self.move_time = time;
53         if(dt <= 0)
54                 return;
55
56         self.tur_head.angles += dt * self.tur_head.move_avelocity;
57
58         if (self.health < 127)
59         {
60                 dt = random();
61
62                 if(dt < 0.03)
63                         te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16);
64         }
65
66         if(self.health < 85)
67         if(dt < 0.01)
68                 pointparticles(particleeffectnum("smoke_large"), (self.origin + (randomvec() * 80)), '0 0 0', 1);
69
70         if(self.health < 32)
71         if(dt < 0.015)
72                 pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1);
73
74 }
75
76 void turret_draw2d()
77 {
78         if(self.netname == "")
79                 return;
80
81         if(!autocvar_g_waypointsprite_turrets)
82                 return;
83
84         if(autocvar_cl_hidewaypoints)
85                 return;
86
87         float dist = vlen(self.origin - view_origin);
88         float t = (GetPlayerColor(player_localnum) + 1);
89
90         vector o;
91         string txt;
92
93         if(autocvar_cl_vehicles_hud_tactical)
94         if(dist < 10240 && t != self.team)
95         {
96                 // TODO: Vehicle tactical hud
97                 o = project_3d_to_2d(self.origin + '0 0 32');
98                 if(o_z < 0
99                 || o_x < (vid_conwidth * waypointsprite_edgeoffset_left)
100                 || o_y < (vid_conheight * waypointsprite_edgeoffset_top)
101                 || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
102                 || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
103                         return; // Dont draw wp's for turrets out of view
104                 o_z = 0;
105                 if(hud != HUD_NORMAL)
106                 {
107                         if((get_turretinfo(self.turretid)).spawnflags & TUR_FLAG_MOVE)
108                                 txt = "gfx/vehicles/vth-mover.tga";
109                         else
110                                 txt = "gfx/vehicles/vth-stationary.tga";
111
112                         vector pz = drawgetimagesize(txt) * 0.25;
113                         drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.75, DRAWFLAG_NORMAL);
114                 }
115         }
116
117         if(dist > self.maxdistance)
118                 return;
119
120         string spriteimage = self.netname;
121         float a = self.alpha * autocvar_hud_panel_fg_alpha;
122         vector rgb = spritelookupcolor(spriteimage, self.teamradar_color);
123
124
125         if(self.maxdistance > waypointsprite_normdistance)
126                 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
127         else if(self.maxdistance > 0)
128                 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
129
130         if(rgb == '0 0 0')
131         {
132                 self.teamradar_color = '1 0 1';
133                 printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage);
134         }
135
136         txt = self.netname;
137         if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam)
138                 txt = _("Spam");
139         else
140                 txt = spritelookuptext(spriteimage);
141
142         if(time - floor(time) > 0.5 && t == self.team)
143         {
144                 if(self.helpme && time < self.helpme)
145                 {
146                         a *= SPRITE_HELPME_BLINK;
147                         txt = sprintf(_("%s under attack!"), txt);
148                 }
149                 else
150                         a *= spritelookupblinkvalue(spriteimage);
151         }
152
153         if(autocvar_g_waypointsprite_uppercase)
154                 txt = strtoupper(txt);
155
156         if(a > 1)
157         {
158                 rgb *= a;
159                 a = 1;
160         }
161
162         if(a <= 0)
163                 return;
164
165         rgb = fixrgbexcess(rgb);
166
167         o = project_3d_to_2d(self.origin + '0 0 64');
168         if(o_z < 0
169         || o_x < (vid_conwidth * waypointsprite_edgeoffset_left)
170         || o_y < (vid_conheight * waypointsprite_edgeoffset_top)
171         || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right))
172         || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)))
173                 return; // Dont draw wp's for turrets out of view
174
175         o_z = 0;
176
177         float edgedistance_min, crosshairdistance;
178                 edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)),
179         (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)),
180         (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x,
181         (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y);
182
183         float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
184
185         crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
186
187         t = waypointsprite_scale * vidscale;
188         a *= waypointsprite_alpha;
189
190         {
191                 a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
192                 t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1)));
193         }
194         if (edgedistance_min < waypointsprite_edgefadedistance) {
195                 a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
196                 t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1)));
197         }
198         if(crosshairdistance < waypointsprite_crosshairfadedistance) {
199                 a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
200                 t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1)));
201         }
202
203         o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t);
204         o = drawspritetext(o, M_PI, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
205         drawhealthbar(
206                         o,
207                         0,
208                         self.health / 255,
209                         '0 0 0',
210                         '0 0 0',
211                         0.5 * SPRITE_HEALTHBAR_WIDTH * t,
212                         0.5 * SPRITE_HEALTHBAR_HEIGHT * t,
213                         SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize,
214                         SPRITE_HEALTHBAR_BORDER * t,
215                         0,
216                         rgb,
217                         a * SPRITE_HEALTHBAR_BORDERALPHA,
218                         rgb,
219                         a * SPRITE_HEALTHBAR_HEALTHALPHA,
220                         DRAWFLAG_NORMAL
221                         );
222 }
223
224 void(entity e, entity tagentity, string tagname) setattachment = #443;
225 void turret_construct()
226 {
227         entity tur = get_turretinfo(self.turretid);
228
229         if(self.tur_head == world)
230                 self.tur_head = spawn();
231
232         self.netname = TUR_NAME(self.turretid);
233
234         setorigin(self, self.origin);
235         setmodel(self, tur.model);
236         setmodel(self.tur_head, tur.head_model);
237         setsize(self, tur.mins, tur.maxs);
238         setsize(self.tur_head, '0 0 0', '0 0 0');
239
240         if(self.turretid == TUR_EWHEEL)
241                 setattachment(self.tur_head, self, "");
242         else
243                 setattachment(self.tur_head, self, "tag_head");
244
245         self.tur_head.classname                 = "turret_head";
246         self.tur_head.owner                             = self;
247         self.tur_head.move_movetype             = MOVETYPE_NOCLIP;
248         self.move_movetype                              = MOVETYPE_NOCLIP;
249         self.tur_head.angles                    = self.angles;
250         self.health                                             = 255;
251         self.solid                                              = SOLID_BBOX;
252         self.tur_head.solid                             = SOLID_NOT;
253         self.movetype                                   = MOVETYPE_NOCLIP;
254         self.tur_head.movetype                  = MOVETYPE_NOCLIP;
255         self.draw                                               = turret_draw;
256         self.entremove                                  = turret_remove;
257         self.drawmask                                   = MASK_NORMAL;
258         self.tur_head.drawmask                  = MASK_NORMAL;
259         self.anim_start_time                    = 0;
260         self.draw2d = turret_draw2d;
261         self.maxdistance = autocvar_g_waypointsprite_turrets_maxdist;
262         self.teamradar_color = '1 0 0';
263         self.alpha = 1;
264         
265         TUR_ACTION(self.turretid, TR_SETUP);
266 }
267
268 entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode);
269 void turret_gibboom();
270 void turret_gib_draw()
271 {
272         Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
273
274         self.drawmask = MASK_NORMAL;
275
276         if(self.cnt)
277         {
278                 if(time >= self.nextthink)
279                 {
280                         turret_gibboom();
281                         remove(self);
282                 }
283         }
284         else
285         {
286                 self.alpha = bound(0, self.nextthink - time, 1);
287                 if(self.alpha < ALPHA_MIN_VISIBLE)
288                         remove(self);
289         }
290 }
291
292 void turret_gibboom()
293 {
294         float i;
295
296         sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
297         pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
298
299         for (i = 1; i < 5; i = i + 1)
300                 turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', FALSE);
301 }
302
303 entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode)
304 {
305         entity gib;
306
307         traceline(_from, _to, MOVE_NOMONSTERS, world);
308         if(trace_startsolid)
309                 return world;
310
311         gib = spawn();
312         setorigin(gib, _from);
313         setmodel(gib, _model);
314         gib.colormod    = _cmod;
315         gib.solid          = SOLID_CORPSE;
316         gib.draw                = turret_gib_draw;
317         gib.cnt          = _explode;
318         setsize(gib, '-1 -1 -1', '1 1 1');
319         if(_explode)
320         {
321                 gib.nextthink = time + 0.2 * (autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15));
322                 gib.effects = EF_FLAME;
323         }
324         else
325                 gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15);
326
327         gib.gravity              = 1;
328         gib.move_movetype   = MOVETYPE_BOUNCE;
329         gib.move_origin  = _from;
330         setorigin(gib,          _from);
331         gib.move_velocity   = _to;
332         gib.move_avelocity  = prandomvec() * 32;
333         gib.move_time      = time;
334         gib.damageforcescale = 1;
335         gib.classname = "turret_gib";
336
337         return gib;
338 }
339
340 void turret_die()
341 {
342         sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
343         pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
344         if (!autocvar_cl_nogibs)
345         {
346                 // Base
347                 if(self.turretid == TUR_EWHEEL)
348                         turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', TRUE);
349                 else if (self.turretid == TUR_WALKER)
350                         turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', TRUE);
351                 else if (self.turretid == TUR_TESLA)
352                         turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', FALSE);
353                 else
354                 {
355                         if (random() > 0.5)
356                         {
357                                 turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE);
358                                 turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE);
359                                 turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE);
360                         }
361                         else
362                                 turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', TRUE);
363
364                         entity headgib = turret_gibtoss((get_turretinfo(self.turretid)).head_model, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', TRUE);
365                         if(headgib)
366                         {
367                                 headgib.angles = headgib.move_angles = self.tur_head.angles;
368                                 headgib.avelocity = headgib.move_avelocity = self.tur_head.move_avelocity + randomvec() * 45;
369                                 headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity_y * 5;
370                                 headgib.gravity = 0.5;
371                         }
372                 }
373         }
374
375         setmodel(self, "null");
376         setmodel(self.tur_head, "null");
377 }
378
379 void ent_turret()
380 {
381         float sf;
382         sf = ReadByte();
383
384         if(sf & TNSF_SETUP)
385         {
386                 self.turretid = ReadByte();
387
388                 self.origin_x = ReadCoord();
389                 self.origin_y = ReadCoord();
390                 self.origin_z = ReadCoord();
391                 setorigin(self, self.origin);
392
393                 self.angles_x = ReadAngle();
394                 self.angles_y = ReadAngle();
395                 
396                 turret_construct();
397                 self.colormap = 1024;
398                 self.glowmod = '0 1 1';
399                 self.tur_head.colormap = self.colormap;
400                 self.tur_head.glowmod = self.glowmod;
401         }
402
403         if(sf & TNSF_ANG)
404         {
405                 if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great.
406                         self.tur_head = spawn();
407
408                 self.tur_head.move_angles_x = ReadShort();
409                 self.tur_head.move_angles_y = ReadShort();
410                 //self.tur_head.angles = self.angles + self.tur_head.move_angles;
411                 self.tur_head.angles = self.tur_head.move_angles;
412         }
413
414         if(sf & TNSF_AVEL)
415         {
416                 if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great.
417                         self.tur_head = spawn();
418
419                 self.tur_head.move_avelocity_x = ReadShort();
420                 self.tur_head.move_avelocity_y = ReadShort();
421         }
422
423         if(sf & TNSF_MOVE)
424         {
425                 self.origin_x = ReadShort();
426                 self.origin_y = ReadShort();
427                 self.origin_z = ReadShort();
428                 setorigin(self, self.origin);
429
430                 self.velocity_x = ReadShort();
431                 self.velocity_y = ReadShort();
432                 self.velocity_z = ReadShort();
433
434                 self.move_angles_y = ReadShort();
435
436                 self.move_time   = time;
437                 self.move_velocity = self.velocity;
438                 self.move_origin   = self.origin;
439         }
440
441         if(sf & TNSF_ANIM)
442         {
443                 self.frame1time = ReadCoord();
444                 self.frame        = ReadByte();
445         }
446
447         if(sf & TNSF_STATUS)
448         {
449                 float _tmp;
450                 _tmp = ReadByte();
451                 if(_tmp != self.team)
452                 {
453                         self.team = _tmp;
454                         turret_changeteam();
455                 }
456
457                 _tmp = ReadByte();
458                 if(_tmp == 0 && self.health != 0)
459                         turret_die();
460                 else if(self.health && self.health != _tmp)
461                         self.helpme = servertime + 10;
462
463                 self.health = _tmp;
464         }
465         //self.enemy.health = self.health / 255;
466 }