no setorigin in waypoint sprites (havoc);
[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                         setorigin(wp, wp.origin + wp.exteriormodeltoclient.origin);
58                         wp.exteriormodeltoclient = world;
59                 }
60                 wp.owner.(wp.owned_by_field) = world;
61                 wp.owner = world;
62
63                 if(!wp.health)
64                 {
65                         wp.health = fadetime;
66                         wp.teleport_time = time + fadetime;
67                 }
68                 else if(fadetime < (wp.teleport_time - time))
69                 {
70                         // accelerate the waypoint's dying
71                         // ensure:
72                         //   (wp.teleport_time - time) / wp.health stays
73                         //   wp.teleport_time = time + fadetime
74                         float current_fadetime;
75                         current_fadetime = wp.teleport_time - time;
76                         wp.teleport_time = time + fadetime;
77                         wp.health = wp.health * fadetime / current_fadetime;
78                 }
79         }
80 }
81
82 void WaypointSprite_Think()
83 {
84         float doremove;
85
86         doremove = FALSE;
87
88         if(self.health)
89                 if(time >= self.teleport_time)
90                         doremove = TRUE;
91
92         if(doremove)
93                 WaypointSprite_Kill(self);
94         else
95                 self.nextthink = time;
96 }
97
98 float WaypointSprite_CustomizeEntityForClient()
99 {
100         vector realorigin, porigin;
101         float distancealpha, timealpha;
102         float distance;
103
104         if(self.health)
105         {
106                 timealpha = bound(0, (self.teleport_time - time) / self.health, 1);
107                 if(timealpha == 0)
108                         return FALSE;
109                 timealpha = pow(timealpha, waypointsprite_timealphaexponent);
110         }
111         else
112                 timealpha = 1;
113
114         if(self.enemy)
115                 if(self.enemy != other)
116                         return FALSE;
117         if(self.team)
118         {
119                 if(self.team != other.team)
120                         return FALSE;
121                 if(other.classname != "player")
122                         return FALSE;
123         }
124
125         if(self.currentammo) // hidable?
126                 if(other.cvar_cl_hidewaypoints) // wants to hide;
127                         return FALSE;
128
129         porigin = other.origin + other.view_ofs_z * '0 0 1';
130         realorigin = self.exteriormodeltoclient.origin + self.origin;
131
132         distance = vlen(realorigin - porigin);
133
134         if(self.max_health)
135                 if(distance >= self.max_health)
136                         return FALSE;
137
138         self.scale = max(1, distance / waypointsprite_normdistance) * waypointsprite_minscale;
139
140         if(self.max_health > waypointsprite_normdistance)
141         {
142                 // alpha 1 at normdistance, alpha 0 at maxdistance
143                 distancealpha = bound(0, (self.max_health - distance) / (self.max_health - waypointsprite_normdistance), 1);
144                 distancealpha = pow(distancealpha, waypointsprite_distancealphaexponent);
145         }
146         else if(self.max_health)
147         {
148                 // alpha 1 if visible
149                 distancealpha = 1;
150         }
151         else
152         {
153                 // alpha 1 at normdistance, alpha minalpha at fadedistance
154                 distancealpha = bound(0, (waypointsprite_fadedistance - distance) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1);
155                 distancealpha = pow(distancealpha, waypointsprite_distancealphaexponent);
156                 distancealpha = distancealpha * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
157         }
158
159         self.alpha = timealpha * distancealpha;
160
161         return TRUE;
162 }
163
164 entity WaypointSprite_Spawn(
165         string spr, // sprite
166         float lifetime, float maxdistance, // lifetime, max distance
167         entity ref, vector ofs, // position
168         entity showto, float t, // show to whom? Use a flag to indicate a team
169         entity own, .entity ownfield, // remove when own gets killed
170         float hideable // true when it should be controlled by cl_hidewaypoints
171 )
172 {
173         entity wp;
174         wp = spawn();
175         wp.classname = "sprite_waypoint";
176         wp.teleport_time = time + lifetime;
177         wp.health = lifetime;
178         wp.exteriormodeltoclient = ref;
179         if(ref)
180         {
181                 setattachment(wp, ref, "");
182                 setorigin(wp, ofs);
183                 wp.origin = ofs;
184         }
185         else
186                 setorigin(wp, ofs);
187         wp.enemy = showto;
188         wp.team = t;
189         wp.owner = own;
190         wp.currentammo = hideable;
191         if(own)
192         {
193                 if(own.ownfield)
194                         remove(own.ownfield);
195                 own.ownfield = wp;
196                 wp.owned_by_field = ownfield;
197         }
198         wp.max_health = maxdistance;
199         wp.customizeentityforclient = WaypointSprite_CustomizeEntityForClient;
200         wp.think = WaypointSprite_Think;
201         wp.nextthink = time;
202         wp.effects = EF_NODEPTHTEST;
203         setmodel(wp, strcat("models/sprites/", spr, ".sp2"));
204         return wp;
205 }
206
207 entity WaypointSprite_SpawnFixed(
208         string spr,
209         vector ofs
210 )
211 {
212         return WaypointSprite_Spawn(spr, 0, 0, world, ofs, world, 0, world, enemy, TRUE);
213 }
214
215 .entity waypointsprite_deployed_fixed;
216 entity WaypointSprite_DeployFixed(
217         string spr,
218         float limited_range,
219         vector ofs
220 )
221 {
222         float t, maxdistance;
223         if(teams_matter)
224                 t = self.team;
225         else
226                 t = 0;
227         if(limited_range)
228                 maxdistance = waypointsprite_limitedrange;
229         else
230                 maxdistance = 0;
231         return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, world, ofs, world, t, self, waypointsprite_deployed_fixed, FALSE);
232 }
233
234 .entity waypointsprite_deployed_personal;
235 entity WaypointSprite_DeployPersonal(
236         string spr,
237         vector ofs
238 )
239 {
240         return WaypointSprite_Spawn(spr, 0, 0, world, ofs, self, 0, self, waypointsprite_deployed_personal, FALSE);
241 }
242
243 .entity waypointsprite_attached;
244 .entity waypointsprite_attachedforcarrier;
245 entity WaypointSprite_Attach(
246         string spr,
247         float limited_range
248 )
249 {
250         float t, maxdistance;
251         if(self.waypointsprite_attachedforcarrier)
252                 return world; // can't attach to FC
253         if(teams_matter)
254                 t = self.team;
255         else
256                 t = 0;
257         if(limited_range)
258                 maxdistance = waypointsprite_limitedrange;
259         else
260                 maxdistance = 0;
261         return WaypointSprite_Spawn(spr, waypointsprite_deployed_lifetime, maxdistance, self, '0 0 64', world, t, self, waypointsprite_attached, FALSE);
262 }
263
264 entity WaypointSprite_AttachCarrier(
265         string spr,
266         entity carrier
267 )
268 {
269         WaypointSprite_Kill(carrier.waypointsprite_attached); // FC overrides attached
270         return WaypointSprite_Spawn(spr, 0, 0, carrier, '0 0 64', world, carrier.team, carrier, waypointsprite_attachedforcarrier, FALSE);
271 }
272
273 void WaypointSprite_DetachCarrier(entity carrier)
274 {
275         WaypointSprite_Disown(carrier.waypointsprite_attachedforcarrier, waypointsprite_deadlifetime);
276 }
277
278 void WaypointSprite_ClearPersonal()
279 {
280         WaypointSprite_Kill(self.waypointsprite_deployed_personal);
281 }
282
283 void WaypointSprite_ClearOwned()
284 {
285         WaypointSprite_Kill(self.waypointsprite_deployed_fixed);
286         WaypointSprite_Kill(self.waypointsprite_deployed_personal);
287         WaypointSprite_Kill(self.waypointsprite_attached);
288 }
289
290 void WaypointSprite_PlayerDead()
291 {
292         WaypointSprite_Disown(self.waypointsprite_attached, waypointsprite_deadlifetime);
293         WaypointSprite_DetachCarrier(self);
294 }
295
296 void WaypointSprite_PlayerGone()
297 {
298         WaypointSprite_Disown(self.waypointsprite_deployed_fixed, waypointsprite_deadlifetime);
299         WaypointSprite_Kill(self.waypointsprite_deployed_personal);
300         WaypointSprite_Disown(self.waypointsprite_attached, waypointsprite_deadlifetime);
301         WaypointSprite_DetachCarrier(self);
302 }