]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/warpzonelib/server.qc
nex also supports warpzones now
[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_angles;
27 .vector warpzone_forward;
28
29 float WarpZone_Teleport(entity player)
30 {
31         vector o0, a0, v0, o1, a1, v1;
32
33         o0 = player.origin + player.view_ofs;
34         v0 = player.velocity;
35         a0 = player.angles;
36
37         if((o0 - self.warpzone_origin) * self.warpzone_forward >= 0) // wrong side of the portal
38                 return 2;
39         // no failure, we simply don't want to teleport yet; TODO in
40         // this situation we may want to create a temporary clone
41         // entity of the player to fix graphics glitch
42
43         o1 = WarpZone_TransformOrigin(self, o0);
44         v1 = WarpZone_TransformVelocity(self, v0);
45         if(player.classname == "player")
46                 a1 = WarpZone_TransformVAngles(self, player.v_angle);
47         else
48                 a1 = WarpZone_TransformAngles(self, a0);
49
50         // put him inside solid
51         tracebox(o1 - player.view_ofs, player.mins, player.maxs, o1 - player.view_ofs, MOVE_NOMONSTERS, player);
52         if(trace_startsolid)
53         {
54                 vector mi, ma;
55                 mi = player.mins;
56                 ma = player.maxs;
57                 setsize(player, mi - player.view_ofs, ma - player.view_ofs);
58                 setorigin(player, o1);
59                 if(WarpZoneLib_MoveOutOfSolid(player))
60                 {
61                         o1 = player.origin;
62                         setsize(player, mi, ma);
63                         setorigin(player, o0);
64                 }
65                 else
66                 {
67                         print("would have to put player in solid, won't do that\n");
68                         setsize(player, mi, ma);
69                         setorigin(player, o0 - player.view_ofs);
70                         return 0; // cannot fix
71                 }
72         }
73
74         if((o1 - self.enemy.warpzone_origin) * self.enemy.warpzone_forward <= 0) // wrong side of the portal post-teleport
75         {
76                 print("inconsistent warp zones or evil roundoff error\n");
77                 return 0;
78         }
79
80         //print(sprintf("warpzone: %f %f %f -> %f %f %f\n", o0_x, o0_y, o0_z, o1_x, o1_y, o1_z));
81
82         //o1 = trace_endpos;
83         TeleportPlayer(self, other, o1 - player.view_ofs, a1, v1, '0 0 0', '0 0 0', TELEPORT_FLAGS_WARPZONE);
84
85         return 1;
86 }
87
88 void WarpZone_Touch (void)
89 {
90         entity oldself, e;
91
92         // FIXME needs a better check to know what is safe to teleport and what not
93         if(other.movetype == MOVETYPE_NONE)
94                 return;
95
96         if(WarpZoneLib_ExactTrigger_Touch())
97                 return;
98
99         e = self.enemy;
100         if(WarpZone_Teleport(other))
101         {
102                 if(self.aiment.target)
103                 {
104                         oldself = self;
105                         activator = other;
106                         self = self.aiment;
107                         SUB_UseTargets();
108                         self = oldself;
109                 }
110         }
111         else
112         {
113                 dprint("WARPZONE FAIL AHAHAHAHAH))\n");
114         }
115 }
116
117 float WarpZone_Send(entity to, float sendflags)
118 {
119         WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE);
120
121         // we need THESE to render the warpzone (and cull properly)...
122         WriteCoord(MSG_ENTITY, self.origin_x);
123         WriteCoord(MSG_ENTITY, self.origin_y);
124         WriteCoord(MSG_ENTITY, self.origin_z);
125
126         WriteShort(MSG_ENTITY, self.modelindex);
127         WriteCoord(MSG_ENTITY, self.mins_x);
128         WriteCoord(MSG_ENTITY, self.mins_y);
129         WriteCoord(MSG_ENTITY, self.mins_z);
130         WriteCoord(MSG_ENTITY, self.maxs_x);
131         WriteCoord(MSG_ENTITY, self.maxs_y);
132         WriteCoord(MSG_ENTITY, self.maxs_z);
133
134         // we need THESE to calculate the proper transform
135         WriteCoord(MSG_ENTITY, self.warpzone_origin_x);
136         WriteCoord(MSG_ENTITY, self.warpzone_origin_y);
137         WriteCoord(MSG_ENTITY, self.warpzone_origin_z);
138         WriteCoord(MSG_ENTITY, self.warpzone_angles_x);
139         WriteCoord(MSG_ENTITY, self.warpzone_angles_y);
140         WriteCoord(MSG_ENTITY, self.warpzone_angles_z);
141         WriteCoord(MSG_ENTITY, self.enemy.warpzone_origin_x);
142         WriteCoord(MSG_ENTITY, self.enemy.warpzone_origin_y);
143         WriteCoord(MSG_ENTITY, self.enemy.warpzone_origin_z);
144         WriteCoord(MSG_ENTITY, self.enemy.warpzone_angles_x);
145         WriteCoord(MSG_ENTITY, self.enemy.warpzone_angles_y);
146         WriteCoord(MSG_ENTITY, self.enemy.warpzone_angles_z);
147
148         return TRUE;
149 }
150
151 void WarpZone_InitStep_SpawnFunc()
152 {
153         // warp zone entities must have:
154         // "killtarget" pointing to a target_position with a direction arrow
155         //              that points AWAY from the warp zone, and that is inside
156         //              the warp zone trigger
157         // "target"     pointing to an identical warp zone at another place in
158         //              the map, with another killtarget to designate its
159         //              orientation
160
161         string m;
162         m = self.model;
163         WarpZoneLib_ExactTrigger_Init();
164         setmodel(self, m);
165         self.SendEntity = WarpZone_Send;
166         self.SendFlags = 0xFFFFFF;
167         self.effects |= EF_NODEPTHTEST;
168 }
169
170 void WarpZone_InitStep_FindTarget()
171 {
172         entity e;
173
174         if(self.killtarget == "")
175         {
176                 objerror("Warp zone with no killtarget");
177                 return;
178         }
179         self.aiment = find(world, targetname, self.killtarget);
180         if(self.aiment == world)
181         {
182                 objerror("Warp zone with nonexisting killtarget");
183                 return;
184         }
185
186         // this way only one of the two ents needs to target
187         if(self.target != "")
188         {
189                 e = find(world, targetname, self.target);
190                 if(e)
191                 {
192                         self.enemy = e;
193                         self.enemy.enemy = self;
194                 }
195         }
196 }
197
198 void WarpZone_InitStep_UpdateTransform()
199 {
200         if(!self.enemy || self.enemy.enemy != self)
201         {
202                 objerror("Invalid warp zone detected. Killed.");
203                 return;
204         }
205
206         // 1. update this, and the enemy, warp zone
207         self.warpzone_origin = self.aiment.origin;
208         self.warpzone_angles = self.aiment.angles;
209         self.enemy.warpzone_origin = self.enemy.aiment.origin;
210         self.enemy.warpzone_angles = self.enemy.aiment.angles;
211
212         // 2. combine the angle transforms
213         //    current forward must be turned into previous backward
214         self.warpzone_transform = AnglesTransform_Divide(AnglesTransform_TurnDirectionFR(self.enemy.warpzone_angles), self.warpzone_angles);
215
216         // 3. store off a saved forward vector for plane hit decisions
217         fixedmakevectors(self.warpzone_angles);
218         self.warpzone_forward = v_forward;
219
220         // now enable touch
221         self.touch = WarpZone_Touch;
222
223         // our mins/maxs are set to the warpzone... so all we need:
224         self.flags |= FL_CAMERA;
225         self.view_ofs = self.enemy.warpzone_origin;
226 }