]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/portals.qc
some weapon system fixes; allow infinite portals to work by anticipating the collision
[divverent/nexuiz.git] / data / qcsrc / server / portals.qc
1 .vector portal_transform;
2 .vector portal_safe_origin;
3 .float portal_wants_to_vanish;
4 .float portal_id;
5 .float portal_activatetime;
6
7 .entity portal_in, portal_out;
8
9 vector fixedvectoangles(vector v)
10 {
11         vector a;
12         a = vectoangles(v);
13         a_x = -a_x;
14         return a;
15 }
16
17 vector fixedvectoangles2(vector v, vector w)
18 {
19         vector a;
20         a = vectoangles2(v, w);
21         a_x = -a_x;
22         return a;
23 }
24
25 void fixedmakevectors(vector a)
26 {
27         //a_x = -a_x;
28         makevectors(a);
29 }
30
31 vector Portal_Transform_Apply(vector transform, vector v)
32 {
33         fixedmakevectors(transform);
34         return v_forward * v_x
35              + v_right   * (-v_y)
36                  + v_up      * v_z;
37 }
38
39 vector Portal_Transform_Multiply(vector t1, vector t2)
40 {
41         vector m_forward, m_up;
42         fixedmakevectors(t2); m_forward = v_forward; m_up = v_up;
43         m_forward = Portal_Transform_Apply(t1, m_forward);
44         m_up = Portal_Transform_Apply(t1, m_up);
45         return fixedvectoangles2(m_forward, m_up);
46 }
47
48 vector Portal_Transform_Invert(vector transform)
49 {
50         vector i_forward, i_up;
51         fixedmakevectors(transform);
52         // we want angles that turn v_forward into '1 0 0', v_right into '0 1 0' and v_up into '0 0 1'
53         // but these are orthogonal unit vectors!
54         // so to invert, we can simply vectoangles the TRANSPOSED matrix
55         // TODO is this always -transform?
56         i_forward_x = v_forward_x;
57         i_forward_y = -v_right_x;
58         i_forward_z = v_up_x;
59         i_up_x = v_forward_z;
60         i_up_y = -v_right_z;
61         i_up_z = v_up_z;
62 #ifdef DEBUG
63         vector v;
64         v = fixedvectoangles2(i_forward, i_up);
65         print("Transform: ", vtos(transform), "\n");
66         print("Inverted: ", vtos(v), "\n");
67         print("Verify: ", vtos(Portal_Transform_Multiply(v, transform)), "\n");
68         fixedmakevectors(Portal_Transform_Multiply(v, transform));
69         print("Verify: ", vtos(v_forward), "\n");
70         print("Verify: ", vtos(v_right), "\n");
71         print("Verify: ", vtos(v_up), "\n");
72 #endif
73         return fixedvectoangles2(i_forward, i_up);
74 }
75
76 vector Portal_Transform_TurnDirection(vector transform)
77 {
78         vector t_angles;
79         t_angles_x = -transform_x;
80         t_angles_y = mod(transform_y + 180, 360);
81         t_angles_z = -transform_z;
82         return t_angles;
83 }
84
85 vector Portal_Transform_Divide(vector to_transform, vector from_transform)
86 {
87         return Portal_Transform_Multiply(to_transform, Portal_Transform_Invert(from_transform));
88 }
89
90 void Portal_TeleportPlayer(entity teleporter, entity player)
91 {
92         vector from, to, safe, step, transform, ang;
93         from = teleporter.origin;
94         to = teleporter.enemy.origin;
95         transform = teleporter.portal_transform;
96
97         to = to + Portal_Transform_Apply(teleporter.portal_transform, player.origin - from);
98         // this now is INSIDE the plane... can't use that
99
100         // shift it out
101         fixedmakevectors(teleporter.enemy.mangle);
102         safe = teleporter.enemy.portal_safe_origin; // a valid player origin
103         step = to + ((safe - to) * v_forward) * v_forward;
104         tracebox(safe, PL_MIN, PL_MAX, step, MOVE_NOMONSTERS, player);
105         if(trace_startsolid)
106         {
107                 bprint("'safe' teleport location is not safe!\n");
108                 // FAIL TODO why does this happen?
109                 return;
110         }
111         safe = trace_endpos;
112         tracebox(safe, PL_MIN, PL_MAX, to, MOVE_NOMONSTERS, player);
113         if(trace_startsolid)
114                 error("trace_endpos in solid!");
115         to = trace_endpos;
116
117         if(player.classname == "player")
118         {
119                 ang = Portal_Transform_Multiply(transform, player.v_angle);
120                 ang_z = player.angles_z;
121         }
122         else
123         {
124                 ang = Portal_Transform_Multiply(transform, player.mangle);
125         }
126
127         TeleportPlayer(teleporter, player, to, ang, Portal_Transform_Apply(transform, player.velocity), teleporter.enemy.absmin, teleporter.enemy.absmax);
128
129         // reset fade counter
130         teleporter.portal_wants_to_vanish = 0;
131         teleporter.fade_time = time + 10;
132 }
133
134 float Portal_FindSafeOrigin(entity portal)
135 {
136         vector o;
137         o = portal.origin;
138         portal.mins = PL_MIN - '8 8 8';
139         portal.maxs = PL_MAX + '8 8 8';
140         if(!move_out_of_solid(portal))
141         {
142                 print("NO SAFE ORIGIN\n");
143                 return 0;
144         }
145         portal.portal_safe_origin = portal.origin;
146         setorigin(portal, o);
147         return 1;
148 }
149
150 void Portal_Touch()
151 {
152         if(other.classname == "porto")
153                 return;
154         if(time < self.portal_activatetime)
155                 if(other == self.owner)
156                 {
157                         self.portal_activatetime = time + 0.1;
158                         return;
159                 }
160         fixedmakevectors(self.mangle);
161         if((other.origin - self.origin) * v_forward < 0)
162                 return;
163         if(other.mins_x < PL_MIN_x || other.mins_y < PL_MIN_y || other.mins_z < PL_MIN_z
164         || other.maxs_x > PL_MAX_x || other.maxs_y > PL_MAX_y || other.maxs_z > PL_MAX_z)
165         {
166                 // can't teleport this
167                 return;
168         }
169         Portal_TeleportPlayer(self, other);
170 }
171
172 void Portal_MakeBrokenPortal(entity portal)
173 {
174         portal.solid = SOLID_NOT;
175         portal.touch = SUB_Null;
176         portal.effects = 0;
177         portal.colormod = '1 1 1';
178 }
179
180 void Portal_MakeInPortal(entity portal)
181 {
182         portal.solid = SOLID_TRIGGER;
183         portal.touch = Portal_Touch;
184         portal.effects = EF_RED;
185         portal.colormod = '1 0 0';
186 }
187
188 void Portal_MakeOutPortal(entity portal)
189 {
190         portal.solid = SOLID_NOT;
191         portal.touch = SUB_Null;
192         portal.effects = EF_STARDUST;
193         portal.colormod = '0 0 1';
194 }
195
196 void Portal_Disconnect(entity teleporter, entity destination)
197 {
198         teleporter.enemy = world;
199         destination.enemy = world;
200         Portal_MakeBrokenPortal(teleporter);
201         Portal_MakeBrokenPortal(destination);
202 }
203
204 void Portal_Connect(entity teleporter, entity destination)
205 {
206         teleporter.portal_transform = Portal_Transform_Divide(Portal_Transform_TurnDirection(destination.mangle), teleporter.mangle);
207         teleporter.enemy = destination;
208         destination.enemy = teleporter;
209         Portal_MakeInPortal(teleporter);
210         Portal_MakeOutPortal(destination);
211         teleporter.fade_time = time + 10;
212         destination.fade_time = time + 10;
213         teleporter.portal_wants_to_vanish = 0;
214         destination.portal_wants_to_vanish = 0;
215 }
216
217 void Portal_Think()
218 {
219         entity e, o;
220         float m;
221
222         if(self.solid == SOLID_TRIGGER)
223         {
224                 m = self.modelindex;
225                 o = self.owner;
226                 self.solid = SOLID_BBOX;
227                 self.owner = world;
228                 self.modelindex = 0;
229                 FOR_EACH_PLAYER(e)
230                 {
231                         if(time < self.portal_activatetime)
232                                 if(e == o)
233                                         continue;
234                         // if e would hit the portal in a frame...
235                         // already teleport him
236                         tracebox(e.origin, e.mins, e.maxs, e.origin + e.velocity * 3 * frametime, MOVE_NORMAL, e);
237                         if(trace_ent == self)
238                                 Portal_TeleportPlayer(self, e);
239                 }
240                 self.solid = SOLID_TRIGGER;
241                 self.owner = o;
242                 self.modelindex = m;
243         }
244
245         self.nextthink = time;
246
247         if(time < self.fade_time)
248                 return;
249
250         self.portal_wants_to_vanish = 1;
251
252         if(self.enemy)
253                 if(!self.enemy.portal_wants_to_vanish)
254                         return;
255
256         SUB_SetFade(self, time + 1, 1);
257         if(self.enemy)
258         {
259                 SUB_SetFade(self.enemy, time + 1, 1);
260                 Portal_Disconnect(self, self.enemy);
261         }
262 }
263
264 void Portal_RequestVanish(entity portal)
265 {
266         entity oldself;
267         oldself = self;
268         self = portal;
269         if(self.enemy)
270                 self.enemy.portal_wants_to_vanish = 1;
271         Portal_Think();
272         self = oldself;
273 }
274
275 entity Portal_Spawn(entity own, vector org, vector ang)
276 {
277         fixedmakevectors(ang);
278         if(!CheckWireframeBox(org - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 16 * v_forward))
279                 return world;
280
281         entity portal;
282         portal = spawn();
283         portal.classname = "portal";
284         portal.owner = own;
285         portal.origin = org;
286         portal.mangle = ang;
287         ang_x = -ang_x;
288         portal.angles = ang;
289         portal.think = Portal_Think;
290         portal.nextthink = time;
291         portal.fade_time = time + 10;
292         portal.portal_activatetime = time + 0.1;
293         setmodel(portal, "models/portal.md3");
294
295         if(!Portal_FindSafeOrigin(portal))
296         {
297                 remove(portal);
298                 return world;
299         }
300
301         setsize(portal, '-48 -48 -48', '48 48 48');
302
303         return portal;
304 }
305
306 float Portal_SpawnInPortalAtTrace(entity own, vector dir, float portal_id_val)
307 {
308         if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
309                 return 0;
310
311         if(trace_ent.classname == "player")
312         {
313                 print("hit a player, adjusting...\n");
314                 trace_endpos = trace_ent.origin + '0 0 1' * PL_MIN_z;
315                 trace_plane_normal = '0 0 1';
316         }
317
318         if(own.portal_in)
319         {
320                 if(own.portal_out)
321                 {
322                         Portal_Disconnect(own.portal_in, own.portal_out);
323                         Portal_RequestVanish(own.portal_out);
324                 }
325                 own.portal_out = own.portal_in;
326                 own.portal_in = world;
327                 own.portal_out.portal_id = portal_id_val;
328         }
329         else if(own.portal_out)
330         {
331                 print("this DID happen, no idea why (1)\n");
332                 Portal_RequestVanish(own.portal_out);
333                 own.portal_out = world;
334         }
335                 
336         own.portal_in = Portal_Spawn(own, trace_endpos + trace_plane_normal, fixedvectoangles2(trace_plane_normal, dir));
337         if(!own.portal_in)
338                 return 0;
339         own.portal_in.portal_id = portal_id_val;
340
341         if(own.portal_out)
342                 Portal_Connect(own.portal_in, own.portal_out);
343
344         return 1;
345 }
346
347 float Portal_SpawnOutPortalAtTrace(entity own, vector dir, float portal_id_val)
348 {
349         if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
350                 return 0;
351
352         if(trace_ent.classname == "player")
353         {
354                 print("hit a player, adjusting...\n");
355                 trace_endpos = trace_ent.origin + '0 0 1' * PL_MIN_z;
356                 trace_plane_normal = '0 0 1';
357         }
358
359         if(!own.portal_in)
360                 return 0; // sorry
361
362         if(own.portal_in.portal_id != portal_id_val)
363                 return 0; // we need MATCHING portals
364
365         if(own.portal_out)
366         {
367                 Portal_Disconnect(own.portal_in, own.portal_out);
368                 Portal_RequestVanish(own.portal_out);
369                 own.portal_out = world;
370         }
371                 
372         own.portal_out = Portal_Spawn(own, trace_endpos + trace_plane_normal, fixedvectoangles2(trace_plane_normal, -1 * dir));
373         if(!own.portal_out)
374                 return 0;
375         own.portal_out.portal_id = portal_id_val;
376
377         Portal_Connect(own.portal_in, own.portal_out);
378
379         return 1;
380 }