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