set lowprecision whereever possible
[divverent/nexuiz.git] / data / qcsrc / server / waypointsprites.qc
1 float waypointsprite_normdistance;
2 float waypointsprite_minscale;
3 float waypointsprite_fadedistance;
4 float waypointsprite_minalpha;
5 float waypointsprite_distancealphaexponent;
6 float waypointsprite_timealphaexponent;
7 float waypointsprite_deployed_lifetime;
8 float waypointsprite_deadlifetime;
9 float waypointsprite_limitedrange;
10
11 ..entity owned_by_field;
12
13 void WaypointSprite_Init()
14 {
15         waypointsprite_fadedistance = vlen(world.maxs - world.mins);
16         waypointsprite_normdistance = cvar("g_waypointsprite_normdistance");
17         waypointsprite_minscale = cvar("g_waypointsprite_minscale");
18         waypointsprite_minalpha = cvar("g_waypointsprite_minalpha");
19         waypointsprite_distancealphaexponent = cvar("g_waypointsprite_distancealphaexponent");
20         waypointsprite_timealphaexponent = cvar("g_waypointsprite_timealphaexponent");
21         waypointsprite_deployed_lifetime = cvar("g_waypointsprite_deployed_lifetime");
22         waypointsprite_deadlifetime = cvar("g_waypointsprite_deadlifetime");
23         waypointsprite_limitedrange = cvar("g_waypointsprite_limitedrange");
24
25         precache_model("models/sprites/bluebase.sp2");
26         precache_model("models/sprites/flagcarrier.sp2");
27         precache_model("models/sprites/here.sp2");
28         precache_model("models/sprites/helpme.sp2");
29         precache_model("models/sprites/redbase.sp2");
30         precache_model("models/sprites/waypoint.sp2");
31         precache_model("models/sprites/danger.sp2");
32 }
33
34 void WaypointSprite_InitClient(entity e)
35 {
36         stuffcmd(e, "\nalias \"g_waypointsprite_personal\" \"impulse 30\"\n alias \"g_waypointsprite_personal_p\" \"impulse 31\"\n alias \"g_waypointsprite_personal_d\" \"impulse 32\"\n alias \"g_waypointsprite_team_helpme\" \"impulse 33\"\n alias \"g_waypointsprite_team_here\" \"impulse 34\"\n alias \"g_waypointsprite_team_here_p\" \"impulse 35\"\n alias \"g_waypointsprite_team_here_d\" \"impulse 36\"\n alias \"g_waypointsprite_team_danger\" \"impulse 37\"\n alias \"g_waypointsprite_team_danger_p\" \"impulse 38\"\n alias \"g_waypointsprite_team_danger_d\" \"impulse 39\"\n alias \"g_waypointsprite_clear_personal\" \"impulse 47\"\n alias \"g_waypointsprite_clear\" \"impulse 48\"\n alias \"g_waypointsprite_toggle\" \"impulse 49\"\n");
37 }
38
39 void WaypointSprite_Kill(entity wp)
40 {
41         if(!wp)
42                 return;
43         if(wp.owner)
44                 wp.owner.(wp.owned_by_field) = world;
45         remove(wp);
46 }
47
48 void WaypointSprite_Disown(entity wp, float fadetime)
49 {
50         if(!wp)
51                 return;
52         if(wp.owner)
53         {
54                 if(wp.exteriormodeltoclient == wp.owner)
55                 {
56                         setattachment(wp, world, "");
57 #ifdef ATTACHMENT_WORKS_WITH_EF_NODEPTHTEST
58                         setorigin(wp, wp.origin + wp.exteriormodeltoclient.origin);
59 #else
60                         setorigin(wp, wp.view_ofs + wp.exteriormodeltoclient.origin);
61 #endif
62                         wp.exteriormodeltoclient = world;
63                 }
64                 wp.owner.(wp.owned_by_field) = world;
65                 wp.owner = world;
66
67                 if(!wp.health)
68                 {
69                         wp.health = fadetime;
70                         wp.teleport_time = time + fadetime;
71                 }
72                 else if(fadetime < (wp.teleport_time - time))
73                 {
74                         // accelerate the waypoint's dying
75                         // ensure:
76                         //   (wp.teleport_time - time) / wp.health stays
77                         //   wp.teleport_time = time + fadetime
78                         float current_fadetime;
79                         current_fadetime = wp.teleport_time - time;
80                         wp.teleport_time = time + fadetime;
81                         wp.health = wp.health * fadetime / current_fadetime;
82                 }
83         }
84 }
85
86 void WaypointSprite_Think()
87 {
88         float doremove;
89
90         doremove = FALSE;
91
92         if(self.health)
93                 if(time >= self.teleport_time)
94                         doremove = TRUE;
95
96         if(doremove)
97                 WaypointSprite_Kill(self);
98         else
99                 self.nextthink = time;
100 }
101
102 float WaypointSprite_CustomizeEntityForClient()
103 {
104         vector realorigin, porigin;
105         float distancealpha, timealpha;
106         float distance;
107
108         if(self.health)
109         {
110                 timealpha = bound(0, (self.teleport_time - time) / self.health, 1);
111                 if(timealpha == 0)
112                         return FALSE;
113                 timealpha = pow(timealpha, waypointsprite_timealphaexponent);
114         }
115         else
116                 timealpha = 1;
117
118         if(self.enemy)
119                 if(self.enemy != other)
120                         return FALSE;
121         if(self.team)
122         {
123                 if(self.team != other.team)
124                         return FALSE;
125                 if(other.classname != "player")
126                         return FALSE;
127         }
128
129         if(self.currentammo) // hidable?
130                 if(other.cvar_cl_hidewaypoints) // wants to hide;
131                         return FALSE;
132
133         porigin = other.origin + other.view_ofs_z * '0 0 1';
134
135 #ifdef ATTACHMENT_WORKS_WITH_EF_NODEPTHTEST
136         realorigin = self.exteriormodeltoclient.origin + self.origin;
137 #else
138         if(self.exteriormodeltoclient)
139         {
140                 if(self.exteriormodeltoclient == other)
141                 {
142                         setattachment(self, other, "");
143                         setorigin(self, self.view_ofs);
144                         realorigin = other.origin + self.origin;
145                 }
146                 else
147                 {
148                         setattachment(self, world, "");
149                         setorigin(self, self.exteriormodeltoclient.origin + self.view_ofs);
150                         realorigin = self.origin;
151                 }
152         }
153         else
154                 realorigin = self.origin;
155 #endif
156
157         distance = vlen(realorigin - porigin);
158
159         if(self.max_health)
160                 if(distance >= self.max_health)
161                         return FALSE;
162
163         self.scale = max(1, distance / waypointsprite_normdistance) * waypointsprite_minscale;
164
165         if(self.max_health > waypointsprite_normdistance)
166         {
167                 // alpha 1 at normdistance, alpha 0 at maxdistance
168                 distancealpha = bound(0, (self.max_health - distance) / (self.max_health - waypointsprite_normdistance), 1);
169                 distancealpha = pow(distancealpha, waypointsprite_distancealphaexponent);
170         }
171         else if(self.max_health)
172         {
173                 // alpha 1 if visible
174                 distancealpha = 1;
175         }
176         else
177         {
178                 // alpha 1 at normdistance, alpha minalpha at fadedistance
179                 distancealpha = bound(0, (waypointsprite_fadedistance - distance) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1);
180                 distancealpha = pow(distancealpha, waypointsprite_distancealphaexponent);
181                 distancealpha = distancealpha * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
182         }
183
184         self.alpha = timealpha * distancealpha;
185
186         return TRUE;
187 }
188
189 entity WaypointSprite_Spawn(
190         string spr, // sprite
191         float lifetime, float maxdistance, // lifetime, max distance
192         entity ref, vector ofs, // position
193         entity showto, float t, // show to whom? Use a flag to indicate a team
194         entity own, .entity ownfield, // remove when own gets killed
195         float hideable // true when it should be controlled by cl_hidewaypoints
196 )
197 {
198         entity wp;
199         wp = spawn();
200         wp.classname = "sprite_waypoint";
201         wp.teleport_time = time + lifetime;
202         wp.health = lifetime;
203         wp.exteriormodeltoclient = ref;
204         if(ref)
205         {
206 #ifdef ATTACHMENT_WORKS_WITH_EF_NODEPTHTEST
207                 setattachment(wp, ref, "");
208                 setorigin(wp, ofs);
209 #else
210                 wp.view_ofs = ofs;
211 #endif
212         }
213         else
214                 setorigin(wp, ofs);
215         wp.enemy = showto;
216         wp.team = t;
217         wp.owner = own;
218         wp.currentammo = hideable;
219         if(own)
220         {
221                 if(own.ownfield)
222                         remove(own.ownfield);
223                 own.ownfield = wp;
224                 wp.owned_by_field = ownfield;
225         }
226         wp.max_health = maxdistance;
227         wp.customizeentityforclient = WaypointSprite_CustomizeEntityForClient;
228         wp.think = WaypointSprite_Think;
229         wp.nextthink = time;
230         wp.effects = EF_NODEPTHTEST | EF_LOWPRECISION;
231         setmodel(wp, strcat("models/sprites/", spr, ".sp2")); // precision set above
232         return wp;
233 }
234
235 entity WaypointSprite_SpawnFixed(
236         string spr,
237         vector ofs
238 )
239 {
240         return WaypointSprite_Spawn(spr, 0, 0, world, ofs, world, 0, world, enemy, TRUE);
241 }
242
243 .entity waypointsprite_deployed_fixed;
244 entity WaypointSprite_DeployFixed(
245         string spr,
246         float limited_range,
247         vector ofs
248 )
249 {
250         float t, maxdistance;
251         if(teams_matter)
252                 t = self.team;
253         else
254                 t = 0;
255         if(limited_range)
256                 maxdistance = waypointsprite_limitedrange;
257         else
258                 maxdistance = 0;
259         return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, world, ofs, world, t, self, waypointsprite_deployed_fixed, FALSE);
260 }
261
262 .entity waypointsprite_deployed_personal;
263 entity WaypointSprite_DeployPersonal(
264         string spr,
265         vector ofs
266 )
267 {
268         return WaypointSprite_Spawn(spr, 0, 0, world, ofs, self, 0, self, waypointsprite_deployed_personal, FALSE);
269 }
270
271 .entity waypointsprite_attached;
272 .entity waypointsprite_attachedforcarrier;
273 entity WaypointSprite_Attach(
274         string spr,
275         float limited_range
276 )
277 {
278         float t, maxdistance;
279         if(self.waypointsprite_attachedforcarrier)
280                 return world; // can't attach to FC
281         if(teams_matter)
282                 t = self.team;
283         else
284                 t = 0;
285         if(limited_range)
286                 maxdistance = waypointsprite_limitedrange;
287         else
288                 maxdistance = 0;
289         return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, self, '0 0 64', world, t, self, waypointsprite_attached, FALSE);
290 }
291
292 entity WaypointSprite_AttachCarrier(
293         string spr,
294         entity carrier
295 )
296 {
297         WaypointSprite_Kill(carrier.waypointsprite_attached); // FC overrides attached
298         return WaypointSprite_Spawn(spr, 0, 0, carrier, '0 0 64', world, carrier.team, carrier, waypointsprite_attachedforcarrier, FALSE);
299 }
300
301 void WaypointSprite_DetachCarrier(entity carrier)
302 {
303         WaypointSprite_Disown(carrier.waypointsprite_attachedforcarrier, waypointsprite_deadlifetime);
304 }
305
306 void WaypointSprite_ClearPersonal()
307 {
308         WaypointSprite_Kill(self.waypointsprite_deployed_personal);
309 }
310
311 void WaypointSprite_ClearOwned()
312 {
313         WaypointSprite_Kill(self.waypointsprite_deployed_fixed);
314         WaypointSprite_Kill(self.waypointsprite_deployed_personal);
315         WaypointSprite_Kill(self.waypointsprite_attached);
316 }
317
318 void WaypointSprite_PlayerDead()
319 {
320         WaypointSprite_Disown(self.waypointsprite_attached, waypointsprite_deadlifetime);
321         WaypointSprite_DetachCarrier(self);
322 }
323
324 void WaypointSprite_PlayerGone()
325 {
326         WaypointSprite_Disown(self.waypointsprite_deployed_fixed, waypointsprite_deadlifetime);
327         WaypointSprite_Kill(self.waypointsprite_deployed_personal);
328         WaypointSprite_Disown(self.waypointsprite_attached, waypointsprite_deadlifetime);
329         WaypointSprite_DetachCarrier(self);
330 }