- healthbars for players with attached sprite (flag carrier, key carrier, ball kicker)
[divverent/nexuiz.git] / data / qcsrc / client / waypointsprites.qc
1 float waypointsprite_initialized;
2 float waypointsprite_fadedistance;
3 float waypointsprite_normdistance;
4 float waypointsprite_minscale;
5 float waypointsprite_minalpha;
6 float waypointsprite_distancealphaexponent;
7 float waypointsprite_timealphaexponent;
8 float waypointsprite_scale;
9
10 .float rule;
11 .string netname; // primary picture
12 .string netname2; // secondary picture
13 .string netname3; // tertiary picture
14 .float team; // team that gets netname2
15 .float lifetime;
16 .float fadetime;
17 .float maxdistance;
18 .float hideflags;
19 .float spawntime;
20 .float health;
21 .float build_started;
22 .float build_starthealth;
23 .float build_finished;
24
25 vector SPRITE_SIZE = '256 32 0';
26 vector SPRITE_HOTSPOT = '128 32 0';
27 float SPRITE_HEALTHBAR_WIDTH = 96;
28 float SPRITE_HEALTHBAR_HEIGHT = 6;
29 float SPRITE_HEALTHBAR_MARGIN = 4;
30 float SPRITE_HEALTHBAR_BORDER = 1;
31 float SPRITE_HEALTHBAR_BORDERALPHA = 1;
32 float SPRITE_HEALTHBAR_HEALTHALPHA = 0.5;
33
34 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
35 {
36         vector v1, v2, v3, v4;
37
38         hotspot = -1 * hotspot;
39
40         // hotspot-relative coordinates of the corners
41         v1 = hotspot;
42         v2 = hotspot + '1 0 0' * sz_x;
43         v3 = hotspot + '1 0 0' * sz_x + '0 1 0' * sz_y;
44         v4 = hotspot                  + '0 1 0' * sz_y;
45
46         // rotate them, and make them absolute
47         rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
48         v1 = rotate(v1, rot) + org;
49         v2 = rotate(v2, rot) + org;
50         v3 = rotate(v3, rot) + org;
51         v4 = rotate(v4, rot) + org;
52
53         // draw them
54         R_BeginPolygon(pic, f);
55         R_PolygonVertex(v1, '0 0 0', rgb, a);
56         R_PolygonVertex(v2, '1 0 0', rgb, a);
57         R_PolygonVertex(v3, '1 1 0', rgb, a);
58         R_PolygonVertex(v4, '0 1 0', rgb, a);
59         R_EndPolygon();
60 }
61
62 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
63 {
64         R_BeginPolygon(pic, f);
65         R_PolygonVertex(o, '0 0 0', rgb, a);
66         R_PolygonVertex(o + ri, '1 0 0', rgb, a);
67         R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
68         R_PolygonVertex(o + up, '0 1 0', rgb, a);
69         R_EndPolygon();
70 }
71
72 void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float height, float margin, float border, vector rgb, float a, vector hrgb, float ha, float f)
73 {
74         vector o, ri, up;
75         float owidth; // outer width
76
77         hotspot = -1 * hotspot;
78
79         // hotspot-relative coordinates of the healthbar corners
80         o = hotspot;
81         ri = '1 0 0';
82         up = '0 1 0';
83         
84         rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
85         o = rotate(o, rot) + org;
86         ri = rotate(ri, rot);
87         up = rotate(up, rot);
88
89         owidth = width + 2 * border;
90         o = o - up * (margin + border + height) + ri * (sz_x - owidth) * 0.5;
91
92         drawquad(o - up * border,            ri * owidth,    up * border, "", rgb,  a,  f);
93         drawquad(o + up * height,            ri * owidth,    up * border, "", rgb,  a,  f);
94         drawquad(o,                          ri * border,    up * height, "", rgb,  a,  f);
95         drawquad(o + ri * (owidth - border), ri * border,    up * height, "", rgb,  a,  f);
96         drawquad(o + ri * border,            ri * width * h, up * height, "", hrgb, ha, f);
97 }
98
99 void Draw_WaypointSprite()
100 {
101         string spriteimage;
102         float t;
103
104         if(self.lifetime)
105                 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
106         else
107                 self.alpha = 1;
108
109         if(self.hideflags & 2)
110                 return; // radar only
111
112         if(cvar("cl_hidewaypoints") >= 2)
113                 return;
114
115         if(self.hideflags & 1)
116                 if(cvar("cl_hidewaypoints"))
117                         return; // fixed waypoint
118
119         InterpolateOrigin_Do();
120
121         t = GetPlayerColor(player_localentnum - 1) + 1;
122
123         spriteimage = "";
124
125         // choose the sprite
126         switch(self.rule)
127         {
128                 case SPRITERULE_DEFAULT:
129                         if(self.team)
130                         {
131                                 if(self.team == t)
132                                         spriteimage = self.netname;
133                                 else
134                                         spriteimage = "";
135                         }
136                         else
137                                 spriteimage = self.netname;
138                         break;
139                 case SPRITERULE_TEAMPLAY:
140                         if(t == COLOR_SPECTATOR + 1)
141                                 spriteimage = self.netname3;
142                         else if(self.team == t)
143                                 spriteimage = self.netname2;
144                         else
145                                 spriteimage = self.netname;
146                         break;
147                 default:
148                         error("Invalid waypointsprite rule!");
149                         break;
150         }
151
152         if(spriteimage == "")
153                 return;
154         
155         float dist;
156         dist = vlen(self.origin - view_origin);
157         
158         float a;
159         a = self.alpha;
160
161         if(self.maxdistance > waypointsprite_normdistance)
162                 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
163         else if(self.maxdistance > 0)
164                 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
165
166         if(a <= 0)
167                 return;
168         
169         // draw the sprite image
170         vector o;
171         float rot;
172         o = project_3d_to_2d(self.origin);
173         rot = 0;
174
175         if(o_z < 0 || o_x < 0 || o_y < 0 || o_x > vid_conwidth || o_y > vid_conheight)
176         {
177                 // scale it to be just in view
178                 vector d;
179                 float f1, f2;
180
181                 // get the waypoint angle vector
182                 /*
183                 d_x = view_right * (self.origin - view_origin) * vid_conwidth / vid_width;
184                 d_y = -view_up * (self.origin - view_origin) * vid_conheight / (vid_height * vid_pixelheight);
185                 d_z = 0;
186                 */
187                 
188                 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
189
190                 if(cvar("v_flipped"))
191                         d_x = -d_x;
192
193                 f1 = d_x / vid_conwidth;
194                 f2 = d_y / vid_conheight;
195
196                 if(max(f1, -f1) > max(f2, -f2))
197                 {
198                         if(f1 > 0)
199                         {
200                                 // RIGHT edge
201                                 d = d * (0.5 / f1);
202                                 rot = 3;
203                         }
204                         else
205                         {
206                                 // LEFT edge
207                                 d = d * (-0.5 / f1);
208                                 rot = 1;
209                         }
210                 }
211                 else
212                 {
213                         if(f2 > 0)
214                         {
215                                 // BOTTOM edge
216                                 d = d * (0.5 / f2);
217                                 rot = 0;
218                         }
219                         else
220                         {
221                                 // TOP edge
222                                 d = d * (-0.5 / f2);
223                                 rot = 2;
224                         }
225                 }
226
227                 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
228         }
229         o_z = 0;
230
231         float vidscale;
232         vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
233
234         t = stof(db_get(tempdb, strcat("/spriteframes/", spriteimage)));
235         if(t == 0)
236                 spriteimage = strcat("models/sprites/", spriteimage);
237         else
238                 spriteimage = strcat("models/sprites/", spriteimage, "_frame", ftos(mod(floor((max(0, time - self.spawntime)) * 2), t)));
239
240         drawrotpic(o, rot * 90 * DEG2RAD, spriteimage, SPRITE_SIZE * waypointsprite_scale * vidscale, SPRITE_HOTSPOT * waypointsprite_scale * vidscale, '1 1 1', a, DRAWFLAG_MIPMAP);
241
242         if(self.build_finished)
243         {
244                 if(time < self.build_finished + 0.25)
245                 {
246                         if(time < self.build_started)
247                                 self.health = self.build_starthealth;
248                         else if(time < self.build_finished)
249                                 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
250                         else
251                                 self.health = 1;
252                 }
253                 else
254                         self.health = -1;
255         }
256
257         if(self.health >= 0)
258         {
259                 drawhealthbar(o, rot * 90 * DEG2RAD, self.health, SPRITE_SIZE * waypointsprite_scale * vidscale, SPRITE_HOTSPOT * waypointsprite_scale * vidscale, SPRITE_HEALTHBAR_WIDTH, SPRITE_HEALTHBAR_HEIGHT, SPRITE_HEALTHBAR_MARGIN, SPRITE_HEALTHBAR_BORDER, self.teamradar_color, a * SPRITE_HEALTHBAR_BORDERALPHA, self.teamradar_color, a * SPRITE_HEALTHBAR_HEALTHALPHA, DRAWFLAG_NORMAL);
260         }
261 }
262
263 void Ent_RemoveWaypointSprite()
264 {
265         if(self.netname)
266                 strunzone(self.netname);
267         if(self.netname2)
268                 strunzone(self.netname2);
269         if(self.netname3)
270                 strunzone(self.netname3);
271 }
272
273 void Ent_WaypointSprite()
274 {
275         float sendflags, f, t;
276         sendflags = ReadByte();
277
278         if(!self.spawntime)
279                 self.spawntime = time;
280
281         self.draw2d = Draw_WaypointSprite;
282
283         InterpolateOrigin_Undo();
284
285         if(sendflags & 0x80)
286         {
287                 t = ReadByte();
288                 if(t < 192)
289                 {
290                         self.health = t / 191.0;
291                         self.build_finished = 0;
292                 }
293                 else
294                 {
295                         t = (t - 192) * 256 + ReadByte();
296                         self.build_started = servertime;
297                         self.build_starthealth = bound(0, self.health, 1);
298                         self.build_finished = servertime + t / 32;
299                         //print("build: ", ftos(self.build_finished - self.build_started), "\n");
300                 }
301         }
302         else
303                 self.health = -1;
304
305         if(sendflags & 64)
306         {
307                 // unfortunately, this needs to be exact (for the 3D display)
308                 self.origin_x = ReadCoord();
309                 self.origin_y = ReadCoord();
310                 self.origin_z = ReadCoord();
311         }
312
313         if(sendflags & 1)
314         {
315                 self.team = ReadByte();
316                 self.rule = ReadByte();
317         }
318
319         if(sendflags & 2)
320         {
321                 if(self.netname)
322                         strunzone(self.netname);
323                 self.netname = strzone(ReadString());
324         }
325
326         if(sendflags & 4)
327         {
328                 if(self.netname2)
329                         strunzone(self.netname2);
330                 self.netname2 = strzone(ReadString());
331         }
332
333         if(sendflags & 8)
334         {
335                 if(self.netname3)
336                         strunzone(self.netname3);
337                 self.netname3 = strzone(ReadString());
338         }
339
340         if(sendflags & 16)
341         {
342                 self.lifetime = ReadCoord();
343                 self.fadetime = ReadCoord();
344                 self.maxdistance = ReadShort();
345                 self.hideflags = ReadByte();
346         }
347
348         if(sendflags & 32)
349         {
350                 f = ReadByte();
351                 self.teamradar_icon = (f & 0x7F);
352                 if(f & 0x80)
353                 {
354                         self.(teamradar_times[self.teamradar_time_index]) = time;
355                         self.teamradar_time_index = mod(self.teamradar_time_index + 1, MAX_TEAMRADAR_TIMES);
356                 }
357                 self.teamradar_color_x = ReadByte() / 255.0;
358                 self.teamradar_color_y = ReadByte() / 255.0;
359                 self.teamradar_color_z = ReadByte() / 255.0;
360         }
361
362         InterpolateOrigin_Note();
363
364         self.entremove = Ent_RemoveWaypointSprite;
365 }
366
367 void WaypointSprite_Load()
368 {
369         waypointsprite_fadedistance = vlen(world.maxs - world.mins);
370         waypointsprite_normdistance = cvar("g_waypointsprite_normdistance");
371         waypointsprite_minscale = cvar("g_waypointsprite_minscale");
372         waypointsprite_minalpha = cvar("g_waypointsprite_minalpha");
373         waypointsprite_distancealphaexponent = cvar("g_waypointsprite_distancealphaexponent");
374         waypointsprite_timealphaexponent = cvar("g_waypointsprite_timealphaexponent");
375         waypointsprite_scale = cvar("g_waypointsprite_scale");
376         if(!waypointsprite_scale)
377                 waypointsprite_scale = 1.0;
378
379         if(!waypointsprite_initialized)
380         {
381                 float dh, n, i, o, f;
382                 string s, sname, sframes;
383                 dh = search_begin("models/sprites/*_frame*.tga", FALSE, FALSE);
384                 n = search_getsize(dh);
385                 for(i = 0; i < n; ++i)
386                 {
387                         s = search_getfilename(dh, i);
388                         if(substring(s, 0, 15) != "models/sprites/")
389                                 continue;
390                         if(substring(s, strlen(s) - 4, 4) != ".tga")
391                                 continue;
392                         s = substring(s, 15, strlen(s) - 19);
393
394                         o = strstrofs(s, "_frame", 0);
395                         sname = strcat("/spriteframes/", substring(s, 0, o));
396                         sframes = substring(s, o + 6, strlen(s) - o - 6);
397                         f = stof(sframes) + 1;
398                         db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
399                 }
400                 search_end(dh);
401         }
402         waypointsprite_initialized = 1;
403 }