]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/warpzonelib/server.qc
MinstaNex now can shoot through warpzones... using warpzone-aware tracebox and trailp...
[divverent/nexuiz.git] / data / qcsrc / warpzonelib / server.qc
1 void WarpZone_TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags)
2 {
3         vector from;
4
5         makevectors (to_angles);
6
7         from = player.origin;
8         setorigin (player, to);
9         player.oldorigin = to; // for DP's unsticking
10         player.angles = to_angles;
11         player.fixangle = TRUE;
12         player.velocity = to_velocity;
13
14         if(player.effects & EF_TELEPORT_BIT)
15                 player.effects &~= EF_TELEPORT_BIT;
16         else
17                 player.effects |= EF_TELEPORT_BIT;
18
19         if(player.classname == "player")
20                 player.flags &~= FL_ONGROUND;
21
22         WarpZone_PostTeleportPlayer(player);
23 }
24
25 // the transform
26 .vector warpzone_origin, warpzone_angles;
27 .vector warpzone_forward;
28 .vector warpzone_transform;
29
30 float WarpZone_Teleport(entity player)
31 {
32         vector o0, a0, v0, o1, a1, v1;
33
34         o0 = player.origin + player.view_ofs;
35         v0 = player.velocity;
36         a0 = player.angles;
37
38         if((o0 - self.warpzone_origin) * self.warpzone_forward >= 0) // wrong side of the portal
39                 return 2;
40         // no failure, we simply don't want to teleport yet; TODO in
41         // this situation we may want to create a temporary clone
42         // entity of the player to fix graphics glitch
43
44         o1 = AnglesTransform_Apply(self.warpzone_transform, o0 - self.warpzone_origin) + self.enemy.warpzone_origin;
45         v1 = AnglesTransform_Apply(self.warpzone_transform, v0);
46         if(player.classname == "player")
47                 a1 = WarpZone_TransformVAngles(self.warpzone_transform, player.v_angle);
48         else
49                 a1 = AnglesTransform_ApplyToAngles(self.warpzone_transform, a0);
50
51         // put him inside solid
52         tracebox(o1 - player.view_ofs, player.mins, player.maxs, o1 - player.view_ofs, MOVE_NOMONSTERS, player);
53         if(trace_startsolid)
54         {
55                 vector mi, ma;
56                 mi = player.mins;
57                 ma = player.maxs;
58                 setsize(player, mi - player.view_ofs, ma - player.view_ofs);
59                 setorigin(player, o1);
60                 if(WarpZoneLib_MoveOutOfSolid(player))
61                 {
62                         o1 = player.origin;
63                         setsize(player, mi, ma);
64                         setorigin(player, o0);
65                 }
66                 else
67                 {
68                         print("would have to put player in solid, won't do that\n");
69                         setsize(player, mi, ma);
70                         setorigin(player, o0 - player.view_ofs);
71                         return 0; // cannot fix
72                 }
73         }
74
75         if((o1 - self.enemy.warpzone_origin) * self.enemy.warpzone_forward <= 0) // wrong side of the portal post-teleport
76         {
77                 print("inconsistent warp zones or evil roundoff error\n");
78                 return 0;
79         }
80
81         //print(sprintf("warpzone: %f %f %f -> %f %f %f\n", o0_x, o0_y, o0_z, o1_x, o1_y, o1_z));
82
83         //o1 = trace_endpos;
84         TeleportPlayer(self, other, o1 - player.view_ofs, a1, v1, '0 0 0', '0 0 0', TELEPORT_FLAGS_WARPZONE);
85
86         return 1;
87 }
88
89 void WarpZone_Touch (void)
90 {
91         entity oldself, e;
92
93         // FIXME needs a better check to know what is safe to teleport and what not
94         if(other.movetype == MOVETYPE_NONE)
95                 return;
96
97         if(WarpZoneLib_ExactTrigger_Touch())
98                 return;
99
100         e = self.enemy;
101         if(WarpZone_Teleport(other))
102         {
103                 if(self.aiment.target)
104                 {
105                         oldself = self;
106                         activator = other;
107                         self = self.aiment;
108                         SUB_UseTargets();
109                         self = oldself;
110                 }
111         }
112         else
113         {
114                 dprint("WARPZONE FAIL AHAHAHAHAH))\n");
115         }
116 }
117
118 float WarpZone_Send(entity to, float sendflags)
119 {
120         WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE);
121
122         // we need THESE to render the warpzone (and cull properly)...
123         WriteCoord(MSG_ENTITY, self.origin_x);
124         WriteCoord(MSG_ENTITY, self.origin_y);
125         WriteCoord(MSG_ENTITY, self.origin_z);
126
127         WriteShort(MSG_ENTITY, self.modelindex);
128         WriteCoord(MSG_ENTITY, self.mins_x);
129         WriteCoord(MSG_ENTITY, self.mins_y);
130         WriteCoord(MSG_ENTITY, self.mins_z);
131         WriteCoord(MSG_ENTITY, self.maxs_x);
132         WriteCoord(MSG_ENTITY, self.maxs_y);
133         WriteCoord(MSG_ENTITY, self.maxs_z);
134
135         // we need THESE to calculate the proper transform
136         WriteCoord(MSG_ENTITY, self.warpzone_origin_x);
137         WriteCoord(MSG_ENTITY, self.warpzone_origin_y);
138         WriteCoord(MSG_ENTITY, self.warpzone_origin_z);
139         WriteCoord(MSG_ENTITY, self.warpzone_angles_x);
140         WriteCoord(MSG_ENTITY, self.warpzone_angles_y);
141         WriteCoord(MSG_ENTITY, self.warpzone_angles_z);
142         WriteCoord(MSG_ENTITY, self.enemy.warpzone_origin_x);
143         WriteCoord(MSG_ENTITY, self.enemy.warpzone_origin_y);
144         WriteCoord(MSG_ENTITY, self.enemy.warpzone_origin_z);
145         WriteCoord(MSG_ENTITY, self.enemy.warpzone_angles_x);
146         WriteCoord(MSG_ENTITY, self.enemy.warpzone_angles_y);
147         WriteCoord(MSG_ENTITY, self.enemy.warpzone_angles_z);
148
149         return TRUE;
150 }
151
152 void WarpZone_InitStep_SpawnFunc()
153 {
154         // warp zone entities must have:
155         // "killtarget" pointing to a target_position with a direction arrow
156         //              that points AWAY from the warp zone, and that is inside
157         //              the warp zone trigger
158         // "target"     pointing to an identical warp zone at another place in
159         //              the map, with another killtarget to designate its
160         //              orientation
161
162         string m;
163         m = self.model;
164         WarpZoneLib_ExactTrigger_Init();
165         setmodel(self, m);
166         self.SendEntity = WarpZone_Send;
167         self.SendFlags = 0xFFFFFF;
168         self.effects |= EF_NODEPTHTEST;
169 }
170
171 void WarpZone_InitStep_FindTarget()
172 {
173         entity e;
174
175         if(self.killtarget == "")
176         {
177                 objerror("Warp zone with no killtarget");
178                 return;
179         }
180         self.aiment = find(world, targetname, self.killtarget);
181         if(self.aiment == world)
182         {
183                 objerror("Warp zone with nonexisting killtarget");
184                 return;
185         }
186
187         // this way only one of the two ents needs to target
188         if(self.target != "")
189         {
190                 e = find(world, targetname, self.target);
191                 if(e)
192                 {
193                         self.enemy = e;
194                         self.enemy.enemy = self;
195                 }
196         }
197 }
198
199 void WarpZone_InitStep_UpdateTransform()
200 {
201         if(!self.enemy || self.enemy.enemy != self)
202         {
203                 objerror("Invalid warp zone detected. Killed.");
204                 return;
205         }
206
207         // 1. update this, and the enemy, warp zone
208         self.warpzone_origin = self.aiment.origin;
209         self.warpzone_angles = self.aiment.angles;
210         self.enemy.warpzone_origin = self.enemy.aiment.origin;
211         self.enemy.warpzone_angles = self.enemy.aiment.angles;
212
213         // 2. combine the angle transforms
214         //    current forward must be turned into previous backward
215         self.warpzone_transform = AnglesTransform_Divide(AnglesTransform_TurnDirectionFR(self.enemy.warpzone_angles), self.warpzone_angles);
216
217         // 3. store off a saved forward vector for plane hit decisions
218         fixedmakevectors(self.warpzone_angles);
219         self.warpzone_forward = v_forward;
220
221         // now enable touch
222         self.touch = WarpZone_Touch;
223
224         // our mins/maxs are set to the warpzone... so all we need:
225         self.flags |= FL_CAMERA;
226         self.view_ofs = self.enemy.warpzone_origin;
227 }
228
229 void WarpZone_TraceBox(vector org, vector mi, vector ma, vector end, float nomonsters, entity forent)
230 {
231         float frac, sol;
232         vector o0, e0;
233         o0 = org;
234         e0 = end;
235         WarpZone_MakeAllSolid();
236         sol = -1;
237         frac = 0;
238         for(;;)
239         {
240                 tracebox(org, mi, ma, end, nomonsters, forent);
241                 if(sol < 0)
242                         sol = trace_startsolid;
243                 if(trace_fraction >= 1)
244                         break;
245                 frac = trace_fraction = frac + (1 - frac) * trace_fraction;
246                 if(trace_ent.classname != "trigger_warpzone")
247                         break;
248                 // we hit a warpzone... so, let's perform the trace after the warp again
249                 org = AnglesTransform_Apply(trace_ent.warpzone_transform, trace_endpos - trace_ent.warpzone_origin) + trace_ent.enemy.warpzone_origin;
250                 end = AnglesTransform_Apply(trace_ent.warpzone_transform, end - trace_ent.warpzone_origin) + trace_ent.enemy.warpzone_origin;
251         }
252         WarpZone_MakeAllOther();
253         WarpZone_trace_endpos = o0 + (e0 - o0) * trace_fraction;
254 }
255
256 void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
257 {
258         WarpZone_TraceBox(org, '0 0 0', '0 0 0', end, nomonsters, forent);
259 }
260
261 void WarpZone_TrailParticles(entity own, float eff, vector org, vector end)
262 {
263         float frac, sol;
264         WarpZone_MakeAllSolid();
265         sol = -1;
266         frac = 0;
267         for(;;)
268         {
269                 traceline(org, end, MOVE_NOMONSTERS, world);
270                 if(sol < 0)
271                         sol = trace_startsolid;
272                 //print(vtos(org), " to ", vtos(trace_endpos), "\n");
273                 trailparticles(own, eff, org, trace_endpos);
274                 if(trace_fraction >= 1)
275                         break;
276                 if(trace_ent.classname != "trigger_warpzone")
277                         break;
278                 // we hit a warpzone... so, let's perform the trace after the warp again
279                 org = AnglesTransform_Apply(trace_ent.warpzone_transform, trace_endpos - trace_ent.warpzone_origin) + trace_ent.enemy.warpzone_origin;
280                 end = AnglesTransform_Apply(trace_ent.warpzone_transform, end - trace_ent.warpzone_origin) + trace_ent.enemy.warpzone_origin;
281         }
282         WarpZone_MakeAllOther();
283 }