1 .vector portal_transform;
2 .vector portal_safe_origin;
3 .float portal_wants_to_vanish;
5 .float portal_activatetime;
7 .entity portal_in, portal_out;
9 vector fixedvectoangles(vector v)
17 vector fixedvectoangles2(vector v, vector w)
20 a = vectoangles(v, w);
25 vector Portal_Transform_Apply(vector transform, vector v)
27 makevectors(transform);
28 return v_forward * v_x
33 vector Portal_Transform_Multiply(vector t1, vector t2)
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);
42 vector Portal_Transform_Invert(vector transform)
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;
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");
67 return fixedvectoangles2(i_forward, i_up);
70 vector Portal_Transform_TurnDirection(vector transform)
73 t_angles_x = -transform_x;
74 t_angles_y = mod(transform_y + 180, 360);
75 t_angles_z = -transform_z;
79 vector Portal_Transform_Divide(vector to_transform, vector from_transform)
81 return Portal_Transform_Multiply(to_transform, Portal_Transform_Invert(from_transform));
84 void Portal_TeleportPlayer(entity teleporter, entity player)
86 vector from, to, safe, step, transform, ang;
87 from = teleporter.origin;
88 to = teleporter.enemy.origin;
89 transform = teleporter.portal_transform;
91 to = to + Portal_Transform_Apply(teleporter.portal_transform, player.origin - from);
92 // this now is INSIDE the plane... can't use that
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);
101 bprint("'safe' teleport location is not safe!\n");
102 // FAIL TODO why does this happen?
106 tracebox(safe, PL_MIN, PL_MAX, to, MOVE_WORLDONLY, player);
108 error("trace_endpos in solid!");
111 if(player.classname == "player")
113 ang = Portal_Transform_Multiply(transform, player.v_angle);
114 ang_z = player.angles_z;
118 ang = Portal_Transform_Multiply(transform, player.angles);
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");
128 float Portal_FindSafeOrigin(entity portal)
132 portal.mins = PL_MIN - '8 8 8';
133 portal.maxs = PL_MAX + '8 8 8';
134 if(!move_out_of_solid(portal))
136 print("NO SAFE ORIGIN\n");
139 portal.portal_safe_origin = portal.origin;
140 setorigin(portal, o);
146 if(other.classname == "porto")
148 if(time < self.portal_activatetime)
150 makevectors(self.angles);
151 if((other.origin - self.origin) * v_forward < 0)
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)
156 // can't teleport this
159 Portal_TeleportPlayer(self, other);
161 // reset fade counter
162 self.portal_wants_to_vanish = 0;
163 self.nextthink = time + 60;
166 void Portal_MakeBrokenPortal(entity portal)
168 portal.solid = SOLID_NOT;
169 portal.touch = SUB_Null;
171 portal.colormod = '1 1 1';
174 void Portal_MakeInPortal(entity portal)
176 portal.solid = SOLID_TRIGGER;
177 portal.touch = Portal_Touch;
178 portal.effects = EF_RED;
179 portal.colormod = '1 0 0';
182 void Portal_MakeOutPortal(entity portal)
184 portal.solid = SOLID_NOT;
185 portal.touch = SUB_Null;
186 portal.effects = EF_STARDUST;
187 portal.colormod = '0 0 1';
190 void Portal_Disconnect(entity teleporter, entity destination)
192 teleporter.enemy = world;
193 destination.enemy = world;
194 Portal_MakeBrokenPortal(teleporter);
195 Portal_MakeBrokenPortal(destination);
198 void Portal_Connect(entity teleporter, entity destination)
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;
213 self.portal_wants_to_vanish = 1;
216 if(!self.enemy.portal_wants_to_vanish)
219 SUB_SetFade(self, time + 1, 1);
222 SUB_SetFade(self.enemy, time + 1, 1);
223 Portal_Disconnect(self, self.enemy);
227 void Portal_RequestVanish(entity portal)
233 self.enemy.portal_wants_to_vanish = 1;
238 entity Portal_Spawn(entity own, vector org, vector ang)
242 portal.classname = "portal";
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");
251 if(!Portal_FindSafeOrigin(portal))
257 setsize(portal, '-64 -64 -64', '64 64 64');
262 float Portal_SpawnInPortalAtTrace(entity own, vector dir, float portal_id_val)
264 if(trace_ent.classname == "player")
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';
275 Portal_Disconnect(own.portal_in, own.portal_out);
276 Portal_RequestVanish(own.portal_out);
278 own.portal_out = own.portal_in;
279 own.portal_in = world;
280 own.portal_out.portal_id = portal_id_val;
282 else if(own.portal_out)
284 print("this DID happen, no idea why (1)\n");
285 Portal_RequestVanish(own.portal_out);
286 own.portal_out = world;
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));
294 own.portal_in.portal_id = portal_id_val;
297 Portal_Connect(own.portal_in, own.portal_out);
302 float Portal_SpawnOutPortalAtTrace(entity own, vector dir, float portal_id_val)
304 if(trace_ent.classname == "player")
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';
314 if(own.portal_in.portal_id != portal_id_val)
315 return 0; // we need MATCHING portals
319 Portal_Disconnect(own.portal_in, own.portal_out);
320 Portal_RequestVanish(own.portal_out);
321 own.portal_out = world;
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));
329 own.portal_out.portal_id = portal_id_val;
331 Portal_Connect(own.portal_in, own.portal_out);
332 print("Portal transform: ", vtos(own.portal_in.portal_transform), "\n");