]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/waypointsprites.qc
Less blurry crosshair ring
[voretournament/voretournament.git] / data / qcsrc / server / waypointsprites.qc
1 ..entity owned_by_field;\r
2 .float rule;\r
3 .string model1;\r
4 .string model2;\r
5 .string model3;\r
6 \r
7 .float(entity) waypointsprite_visible_for_player;\r
8 \r
9 void WaypointSprite_UpdateSprites(entity e, string m1, string m2, string m3)\r
10 {\r
11         if(m1 != e.model1)\r
12         {\r
13                 e.model1 = m1;\r
14                 e.SendFlags |= 2;\r
15         }\r
16         if(m2 != e.model2)\r
17         {\r
18                 e.model2 = m2;\r
19                 e.SendFlags |= 4;\r
20         }\r
21         if(m3 != e.model3)\r
22         {\r
23                 e.model3 = m3;\r
24                 e.SendFlags |= 8;\r
25         }\r
26 }\r
27 \r
28 void WaypointSprite_UpdateHealth(entity e, float f)\r
29 {\r
30         f = bound(0, f, e.max_health);\r
31         if(f != e.health || e.pain_finished)\r
32         {\r
33                 e.health = f;\r
34                 e.pain_finished = 0;\r
35                 e.SendFlags |= 0x80;\r
36         }\r
37 }\r
38 \r
39 void WaypointSprite_UpdateMaxHealth(entity e, float f)\r
40 {\r
41         if(f != e.max_health || e.pain_finished)\r
42         {\r
43                 e.max_health = f;\r
44                 e.pain_finished = 0;\r
45                 e.SendFlags |= 0x80;\r
46         }\r
47 }\r
48 \r
49 void WaypointSprite_UpdateBuildFinished(entity e, float f)\r
50 {\r
51         if(f != e.pain_finished || e.max_health)\r
52         {\r
53                 e.max_health = 0;\r
54                 e.pain_finished = f;\r
55                 e.SendFlags |= 0x80;\r
56         }\r
57 }\r
58 \r
59 void WaypointSprite_UpdateOrigin(entity e, vector o)\r
60 {\r
61         if(o != e.origin)\r
62         {\r
63                 setorigin(e, o);\r
64                 e.SendFlags |= 64;\r
65         }\r
66 }\r
67 \r
68 void WaypointSprite_UpdateRule(entity e, float t, float r)\r
69 {\r
70         // no check, as this is never called without doing an actual change (usually only once)\r
71         e.rule = r;\r
72         e.team = t;\r
73         e.SendFlags |= 1;\r
74 }\r
75 \r
76 void WaypointSprite_UpdateTeamRadar(entity e, float icon, vector col)\r
77 {\r
78         // no check, as this is never called without doing an actual change (usually only once)\r
79         e.cnt = (icon & 0x7F) | (e.cnt & 0x80);\r
80         e.colormod = col;\r
81         e.SendFlags |= 32;\r
82 }\r
83 \r
84 void WaypointSprite_Ping(entity e)\r
85 {\r
86         // ALWAYS sends (this causes a radar circle), thus no check\r
87         e.cnt |= 0x80;\r
88         e.SendFlags |= 32;\r
89 }\r
90 \r
91 void WaypointSprite_FadeOutIn(entity e, float t)\r
92 {\r
93         if(!e.fade_time)\r
94         {\r
95                 e.fade_time = t;\r
96                 e.teleport_time = time + t;\r
97         }\r
98         else if(t < (e.teleport_time - time))\r
99         {\r
100                 // accelerate the waypoint's dying\r
101                 // ensure:\r
102                 //   (e.teleport_time - time) / wp.fade_time stays\r
103                 //   e.teleport_time = time + fadetime\r
104                 float current_fadetime;\r
105                 current_fadetime = e.teleport_time - time;\r
106                 e.teleport_time = time + t;\r
107                 e.fade_time = e.fade_time * t / current_fadetime;\r
108         }\r
109 \r
110         e.SendFlags |= 16;\r
111 }\r
112 \r
113 float waypointsprite_limitedrange, waypointsprite_deployed_lifetime, waypointsprite_deadlifetime;\r
114 void WaypointSprite_Init()\r
115 {\r
116         waypointsprite_limitedrange = cvar("g_waypointsprite_limitedrange");\r
117         waypointsprite_deployed_lifetime = cvar("g_waypointsprite_deployed_lifetime");\r
118         waypointsprite_deadlifetime = cvar("g_waypointsprite_deadlifetime");\r
119 }\r
120 void WaypointSprite_InitClient(entity e)\r
121 {\r
122 }\r
123 \r
124 void WaypointSprite_Kill(entity wp)\r
125 {\r
126         if(!wp)\r
127                 return;\r
128         if(wp.owner)\r
129                 wp.owner.(wp.owned_by_field) = world;\r
130         remove(wp);\r
131 }\r
132 \r
133 void WaypointSprite_Disown(entity wp, float fadetime)\r
134 {\r
135         if(!wp)\r
136                 return;\r
137         if(wp.classname != "sprite_waypoint")\r
138         {\r
139                 backtrace("Trying to disown a non-waypointsprite");\r
140                 return;\r
141         }\r
142         if(wp.owner)\r
143         {\r
144                 if(wp.exteriormodeltoclient == wp.owner)\r
145                         wp.exteriormodeltoclient = world;\r
146                 wp.owner.(wp.owned_by_field) = world;\r
147                 wp.owner = world;\r
148 \r
149                 WaypointSprite_FadeOutIn(wp, fadetime);\r
150         }\r
151 }\r
152 \r
153 void WaypointSprite_Think()\r
154 {\r
155         float doremove;\r
156 \r
157         doremove = FALSE;\r
158 \r
159         if(self.fade_time)\r
160         {\r
161                 if(time >= self.teleport_time)\r
162                         doremove = TRUE;\r
163         }\r
164 \r
165         if(self.exteriormodeltoclient)\r
166                 WaypointSprite_UpdateOrigin(self, self.exteriormodeltoclient.origin + self.view_ofs);\r
167 \r
168         if(doremove)\r
169                 WaypointSprite_Kill(self);\r
170         else\r
171                 self.nextthink = time; // WHY?!?\r
172 }\r
173 \r
174 float WaypointSprite_visible_for_player(entity e)\r
175 {\r
176         // personal waypoints\r
177         if(self.enemy)\r
178                 if(self.enemy != other)\r
179                         return FALSE;\r
180 \r
181         // team waypoints\r
182         if(self.team && self.rule == SPRITERULE_DEFAULT)\r
183         {\r
184                 if(self.team != other.team)\r
185                         return FALSE;\r
186                 if(other.classname != "player")\r
187                         return FALSE;\r
188         }\r
189 \r
190         return TRUE;\r
191 }\r
192 \r
193 float WaypointSprite_Customize()\r
194 {\r
195         // this is not in SendEntity because it shall run every frame, not just every update\r
196 \r
197         // make spectators see what the player would see\r
198         entity e;\r
199         e = other;\r
200         if(e.classname == "spectator")\r
201                 e = e.enemy;\r
202 \r
203         return self.waypointsprite_visible_for_player(e);\r
204 }\r
205 \r
206 float WaypointSprite_SendEntity(entity to, float sendflags)\r
207 {\r
208         float dt;\r
209 \r
210         WriteByte(MSG_ENTITY, ENT_CLIENT_WAYPOINT);\r
211 \r
212         sendflags = sendflags & 0x7F;\r
213 \r
214         if(self.max_health || (self.pain_finished && (time < self.pain_finished + 0.25)))\r
215                 sendflags |= 0x80;\r
216 \r
217         WriteByte(MSG_ENTITY, sendflags);\r
218 \r
219         if(sendflags & 0x80)\r
220         {\r
221                 if(self.max_health)\r
222                 {\r
223                         WriteByte(MSG_ENTITY, (self.health / self.max_health) * 191.0);\r
224                 }\r
225                 else\r
226                 {\r
227                         dt = self.pain_finished - time;\r
228                         dt = bound(0, dt * 32, 16383);\r
229                         WriteByte(MSG_ENTITY, (dt & 0xFF00) / 256 + 192);\r
230                         WriteByte(MSG_ENTITY, (dt & 0x00FF));\r
231                 }\r
232         }\r
233 \r
234         if(sendflags & 64)\r
235         {\r
236                 WriteCoord(MSG_ENTITY, self.origin_x);\r
237                 WriteCoord(MSG_ENTITY, self.origin_y);\r
238                 WriteCoord(MSG_ENTITY, self.origin_z);\r
239         }\r
240 \r
241         if(sendflags & 1)\r
242         {\r
243                 WriteByte(MSG_ENTITY, self.team);\r
244                 WriteByte(MSG_ENTITY, self.rule);\r
245         }\r
246 \r
247         if(sendflags & 2)\r
248                 WriteString(MSG_ENTITY, self.model1);\r
249 \r
250         if(sendflags & 4)\r
251                 WriteString(MSG_ENTITY, self.model2);\r
252 \r
253         if(sendflags & 8)\r
254                 WriteString(MSG_ENTITY, self.model3);\r
255 \r
256         if(sendflags & 16)\r
257         {\r
258                 WriteCoord(MSG_ENTITY, self.fade_time);\r
259                 WriteCoord(MSG_ENTITY, self.teleport_time);\r
260                 WriteShort(MSG_ENTITY, self.fade_rate); // maxdist\r
261                 float f;\r
262                 f = 0;\r
263                 if(self.exteriormodeltoclient == to)\r
264                         f |= 2; // my own\r
265                 WriteByte(MSG_ENTITY, f);\r
266         }\r
267 \r
268         if(sendflags & 32)\r
269         {\r
270                 WriteByte(MSG_ENTITY, self.cnt); // icon on radar\r
271                 WriteByte(MSG_ENTITY, self.colormod_x * 255.0);\r
272                 WriteByte(MSG_ENTITY, self.colormod_y * 255.0);\r
273                 WriteByte(MSG_ENTITY, self.colormod_z * 255.0);\r
274         }\r
275 \r
276         return TRUE;\r
277 }\r
278 \r
279 void WaypointSprite_Reset()\r
280 {\r
281         // if a WP wants to time out, let it time out immediately; other WPs ought to be reset/killed by their owners\r
282 \r
283         if(self.fade_time) // there was there before: || g_keyhunt, do we really need this?\r
284                 WaypointSprite_Kill(self);\r
285 }\r
286 \r
287 entity WaypointSprite_Spawn(\r
288         string spr, // sprite\r
289         float lifetime, float maxdistance, // lifetime, max distance\r
290         entity ref, vector ofs, // position\r
291         entity showto, float t, // show to whom? Use a flag to indicate a team\r
292         entity own, .entity ownfield, // remove when own gets killed\r
293         float hideable // true when it should be controlled by cl_hidewaypoints\r
294 )\r
295 {\r
296         entity wp;\r
297         wp = spawn();\r
298         wp.classname = "sprite_waypoint";\r
299         wp.teleport_time = time + lifetime;\r
300         wp.fade_time = lifetime;\r
301         wp.exteriormodeltoclient = ref;\r
302         if(ref)\r
303         {\r
304                 wp.view_ofs = ofs;\r
305                 setorigin(wp, ref.origin + ofs);\r
306         }\r
307         else\r
308                 setorigin(wp, ofs);\r
309         wp.enemy = showto;\r
310         wp.team = t;\r
311         wp.owner = own;\r
312         if(own)\r
313         {\r
314                 if(own.ownfield)\r
315                         remove(own.ownfield);\r
316                 own.ownfield = wp;\r
317                 wp.owned_by_field = ownfield;\r
318         }\r
319         wp.fade_rate = maxdistance;\r
320         wp.think = WaypointSprite_Think;\r
321         wp.nextthink = time;\r
322         wp.model1 = spr;\r
323         wp.customizeentityforclient = WaypointSprite_Customize;\r
324         wp.waypointsprite_visible_for_player = WaypointSprite_visible_for_player;\r
325         wp.reset2 = WaypointSprite_Reset;\r
326         Net_LinkEntity(wp, FALSE, 0, WaypointSprite_SendEntity);\r
327         return wp;\r
328 }\r
329 \r
330 entity WaypointSprite_SpawnFixed(\r
331         string spr,\r
332         vector ofs,\r
333         entity own,\r
334         .entity ownfield\r
335 )\r
336 {\r
337         return WaypointSprite_Spawn(spr, 0, 0, world, ofs, world, 0, own, ownfield, TRUE);\r
338 }\r
339 \r
340 .entity waypointsprite_deployed_fixed;\r
341 entity WaypointSprite_DeployFixed(\r
342         string spr,\r
343         float limited_range,\r
344         vector ofs\r
345 )\r
346 {\r
347         float t, maxdistance;\r
348         if(teams_matter)\r
349                 t = self.team;\r
350         else\r
351                 t = 0;\r
352         if(limited_range)\r
353                 maxdistance = waypointsprite_limitedrange;\r
354         else\r
355                 maxdistance = 0;\r
356         return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, world, ofs, world, t, self, waypointsprite_deployed_fixed, FALSE);\r
357 }\r
358 \r
359 .entity waypointsprite_deployed_personal;\r
360 entity WaypointSprite_DeployPersonal(\r
361         string spr,\r
362         vector ofs\r
363 )\r
364 {\r
365         return WaypointSprite_Spawn(spr, 0, 0, world, ofs, world, 0, self, waypointsprite_deployed_personal, FALSE);\r
366 }\r
367 \r
368 .entity waypointsprite_attached;\r
369 .entity waypointsprite_attachedforcarrier;\r
370 entity WaypointSprite_Attach(\r
371         string spr,\r
372         float limited_range\r
373 )\r
374 {\r
375         float t, maxdistance;\r
376         if(self.waypointsprite_attachedforcarrier)\r
377                 return world; // can't attach to FC\r
378         if(teams_matter)\r
379                 t = self.team;\r
380         else\r
381                 t = 0;\r
382         if(limited_range)\r
383                 maxdistance = waypointsprite_limitedrange;\r
384         else\r
385                 maxdistance = 0;\r
386         return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, self, '0 0 64', world, t, self, waypointsprite_attached, FALSE);\r
387 }\r
388 \r
389 entity WaypointSprite_AttachCarrier(\r
390         string spr,\r
391         entity carrier\r
392 )\r
393 {\r
394         entity e;\r
395         WaypointSprite_Kill(carrier.waypointsprite_attached); // FC overrides attached\r
396         e = WaypointSprite_Spawn(spr, 0, 0, carrier, '0 0 64', world, carrier.team, carrier, waypointsprite_attachedforcarrier, FALSE);\r
397         if(e)\r
398         {\r
399                 WaypointSprite_UpdateMaxHealth(e, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, cvar("g_balance_armor_blockpercent")) * 2);\r
400                 WaypointSprite_UpdateHealth(e, '1 0 0' * healtharmor_maxdamage(carrier.health, carrier.armorvalue, cvar("g_balance_armor_blockpercent")));\r
401         }\r
402         return e;\r
403 }\r
404 \r
405 void WaypointSprite_DetachCarrier(entity carrier)\r
406 {\r
407         WaypointSprite_Disown(carrier.waypointsprite_attachedforcarrier, waypointsprite_deadlifetime);\r
408 }\r
409 \r
410 void WaypointSprite_ClearPersonal()\r
411 {\r
412         WaypointSprite_Kill(self.waypointsprite_deployed_personal);\r
413 }\r
414 \r
415 void WaypointSprite_ClearOwned()\r
416 {\r
417         WaypointSprite_Kill(self.waypointsprite_deployed_fixed);\r
418         WaypointSprite_Kill(self.waypointsprite_deployed_personal);\r
419         WaypointSprite_Kill(self.waypointsprite_attached);\r
420 }\r
421 \r
422 void WaypointSprite_PlayerDead()\r
423 {\r
424         WaypointSprite_Disown(self.waypointsprite_attached, waypointsprite_deadlifetime);\r
425         WaypointSprite_DetachCarrier(self);\r
426 }\r
427 \r
428 void WaypointSprite_PlayerGone()\r
429 {\r
430         WaypointSprite_Disown(self.waypointsprite_deployed_fixed, waypointsprite_deadlifetime);\r
431         WaypointSprite_Kill(self.waypointsprite_deployed_personal);\r
432         WaypointSprite_Disown(self.waypointsprite_attached, waypointsprite_deadlifetime);\r
433         WaypointSprite_DetachCarrier(self);\r
434 }\r