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