]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/portals.qc
fix Port-O-Launch
[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_WORLDONLY, 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_WORLDONLY, 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         print("previous velocity: ", vtos(player.velocity), "\n");
128         print("previous origin: ", vtos(player.origin), "\n");
129         TeleportPlayer(teleporter, player, to, ang, Portal_Transform_Apply(transform, player.velocity));
130         print("new velocity: ", vtos(player.velocity), "\n");
131         print("new origin: ", vtos(player.origin), "\n");
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                 return;
156         fixedmakevectors(self.mangle);
157         if((other.origin - self.origin) * v_forward < 0)
158                 return;
159         if(other.mins_x < PL_MIN_x || other.mins_y < PL_MIN_y || other.mins_z < PL_MIN_z
160         || other.maxs_x > PL_MAX_x || other.maxs_y > PL_MAX_y || other.maxs_z > PL_MAX_z)
161         {
162                 // can't teleport this
163                 return;
164         }
165         Portal_TeleportPlayer(self, other);
166
167         // reset fade counter
168         self.portal_wants_to_vanish = 0;
169         self.nextthink = time + 60;
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.nextthink = time + 60;
212         destination.nextthink = time + 60;
213         teleporter.portal_wants_to_vanish = 0;
214         destination.portal_wants_to_vanish = 0;
215 }
216
217 void Portal_Vanish()
218 {
219         self.portal_wants_to_vanish = 1;
220
221         if(self.enemy)
222                 if(!self.enemy.portal_wants_to_vanish)
223                         return;
224
225         SUB_SetFade(self, time + 1, 1);
226         if(self.enemy)
227         {
228                 SUB_SetFade(self.enemy, time + 1, 1);
229                 Portal_Disconnect(self, self.enemy);
230         }
231 }
232
233 void Portal_RequestVanish(entity portal)
234 {
235         entity oldself;
236         oldself = self;
237         self = portal;
238         if(self.enemy)
239                 self.enemy.portal_wants_to_vanish = 1;
240         Portal_Vanish();
241         self = oldself;
242 }
243
244 float CheckWireframeBox(vector v0, vector dvx, vector dvy, vector dvz)
245 {
246         traceline(v0, v0 + dvx, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
247         traceline(v0, v0 + dvy, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
248         traceline(v0, v0 + dvz, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
249         traceline(v0 + dvx, v0 + dvx + dvy, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
250         traceline(v0 + dvx, v0 + dvx + dvz, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
251         traceline(v0 + dvy, v0 + dvy + dvx, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
252         traceline(v0 + dvy, v0 + dvy + dvz, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
253         traceline(v0 + dvz, v0 + dvz + dvx, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
254         traceline(v0 + dvz, v0 + dvz + dvy, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
255         traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
256         traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
257         traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, MOVE_NORMAL, world); if(trace_fraction < 1) return 0;
258         return 1;
259 }
260
261 entity Portal_Spawn(entity own, vector org, vector ang)
262 {
263         fixedmakevectors(ang);
264         if(!CheckWireframeBox(org - 64 * v_right - 64 * v_up, 128 * v_right, 128 * v_up, 32 * v_forward))
265                 return world;
266
267         entity portal;
268         portal = spawn();
269         portal.classname = "portal";
270         portal.owner = own;
271         portal.origin = org;
272         portal.mangle = ang;
273         ang_x = -ang_x;
274         portal.angles = ang;
275         portal.think = Portal_Vanish;
276         portal.nextthink = time + 60;
277         portal.portal_activatetime = time + 0.1;
278         setmodel(portal, "models/portal.md3");
279
280         if(!Portal_FindSafeOrigin(portal))
281         {
282                 remove(portal);
283                 return world;
284         }
285
286         setsize(portal, '-64 -64 -64', '64 64 64');
287
288         return portal;
289 }
290
291 float Portal_SpawnInPortalAtTrace(entity own, vector dir, float portal_id_val)
292 {
293         if(trace_ent.classname == "player")
294         {
295                 print("hit a player, adjusting...\n");
296                 trace_endpos = trace_ent.origin + '0 0 1' * PL_MIN_z;
297                 trace_plane_normal = '0 0 1';
298         }
299
300         if(own.portal_in)
301         {
302                 if(own.portal_out)
303                 {
304                         Portal_Disconnect(own.portal_in, own.portal_out);
305                         Portal_RequestVanish(own.portal_out);
306                 }
307                 own.portal_out = own.portal_in;
308                 own.portal_in = world;
309                 own.portal_out.portal_id = portal_id_val;
310         }
311         else if(own.portal_out)
312         {
313                 print("this DID happen, no idea why (1)\n");
314                 Portal_RequestVanish(own.portal_out);
315                 own.portal_out = world;
316         }
317                 
318         print("Plane normal: ", vtos(trace_plane_normal), "\n");
319         print("Portal angles: ", vtos(vectoangles(trace_plane_normal)), "\n");
320         own.portal_in = Portal_Spawn(own, trace_endpos + trace_plane_normal, fixedvectoangles2(trace_plane_normal, dir));
321         if(!own.portal_in)
322                 return 0;
323         own.portal_in.portal_id = portal_id_val;
324
325         if(own.portal_out)
326                 Portal_Connect(own.portal_in, own.portal_out);
327
328         return 1;
329 }
330
331 float Portal_SpawnOutPortalAtTrace(entity own, vector dir, float portal_id_val)
332 {
333         if(trace_ent.classname == "player")
334         {
335                 print("hit a player, adjusting...\n");
336                 trace_endpos = trace_ent.origin + '0 0 1' * PL_MIN_z;
337                 trace_plane_normal = '0 0 1';
338         }
339
340         if(!own.portal_in)
341                 return 0; // sorry
342
343         if(own.portal_in.portal_id != portal_id_val)
344                 return 0; // we need MATCHING portals
345
346         if(own.portal_out)
347         {
348                 Portal_Disconnect(own.portal_in, own.portal_out);
349                 Portal_RequestVanish(own.portal_out);
350                 own.portal_out = world;
351         }
352                 
353         print("Plane normal: ", vtos(trace_plane_normal), "\n");
354         print("Portal angles: ", vtos(vectoangles(trace_plane_normal)), "\n");
355         own.portal_out = Portal_Spawn(own, trace_endpos + trace_plane_normal, fixedvectoangles2(trace_plane_normal, -1 * dir));
356         if(!own.portal_out)
357                 return 0;
358         own.portal_out.portal_id = portal_id_val;
359
360         Portal_Connect(own.portal_in, own.portal_out);
361         print("Portal transform: ", vtos(own.portal_in.portal_transform), "\n");
362
363         return 1;
364 }