1 /*
2 * XPilot NG, a multiplayer space war game.
3 *
4 * Copyright (C) 1991-2001 by
5 *
6 * Bj�rn Stabell <bjoern@xpilot.org>
7 * Ken Ronny Schouten <ken@xpilot.org>
8 * Bert Gijsbers <bert@xpilot.org>
9 * Dick Balaska <dick@xpilot.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 #include "xpserver.h"
27
28 shape_t wormhole_wire;
29
30 /*
31 * Initialization functions.
32 */
33
Wormhole_line_init(void)34 void Wormhole_line_init(void)
35 {
36 int i;
37 static clpos_t coords[MAX_SHIP_PTS];
38
39 wormhole_wire.num_points = MAX_SHIP_PTS;
40 for (i = 0; i < MAX_SHIP_PTS; i++) {
41 wormhole_wire.pts[i] = coords + i;
42 coords[i].cx = (int)(cos(i * 2 * PI / MAX_SHIP_PTS) * WORMHOLE_RADIUS);
43 coords[i].cy = (int)(sin(i * 2 * PI / MAX_SHIP_PTS) * WORMHOLE_RADIUS);
44 }
45
46 return;
47 }
48
Verify_wormhole_consistency(void)49 bool Verify_wormhole_consistency(void)
50 {
51 int i, worm_in = 0, worm_out = 0, worm_norm = 0;
52
53 /* count wormhole types */
54 for (i = 0; i < Num_wormholes(); i++) {
55 int type = Wormhole_by_index(i)->type;
56
57 if (type == WORM_NORMAL)
58 worm_norm++;
59 else if (type == WORM_IN)
60 worm_in++;
61 else if (type == WORM_OUT)
62 worm_out++;
63 }
64
65 /*
66 * Verify that the wormholes are consistent, i.e. that if
67 * we have no 'out' wormholes, make sure that we don't have
68 * any 'in' wormholes, and (less critical) if we have no 'in'
69 * wormholes, make sure that we don't have any 'out' wormholes.
70 */
71 if (worm_norm > 0) {
72 if (worm_norm + worm_out < 2) {
73 warn("Map has only one 'normal' wormhole.");
74 warn("Add at least one 'normal' or 'out' wormhole.");
75 return false;
76 }
77 } else if (worm_in > 0) {
78 if (worm_out < 1) {
79 warn("Map has %d 'in' wormholes, "
80 "but no 'normal' or 'out' wormholes.", worm_in);
81 warn("Add at least one 'normal' or 'out' wormhole.");
82 return false;
83 }
84 } else if (worm_out > 0) {
85 warn("Map has %d 'out' wormholes, but no 'normal' or 'in' wormholes.",
86 worm_out);
87 warn("Add at least one 'normal' or 'in' wormhole.");
88 return false;
89 }
90
91 return true;
92 }
93
94 /*
95 * Functions used in game.
96 */
97
Wormhole_hitmask(wormhole_t * wormhole)98 hitmask_t Wormhole_hitmask(wormhole_t *wormhole)
99 {
100 if (wormhole->type == WORM_OUT)
101 return ALL_BITS;
102 return 0;
103 }
104
Wormhole_hitfunc(group_t * gp,const move_t * move)105 bool Wormhole_hitfunc(group_t *gp, const move_t *move)
106 {
107 const object_t *obj = move->obj;
108 wormhole_t *wormhole = Wormhole_by_index(gp->mapobj_ind);
109
110 if (wormhole->type == WORM_OUT)
111 return false;
112
113 if (obj == NULL)
114 return true;
115
116 if (BIT(obj->obj_status, WARPED|WARPING))
117 return false;
118
119 return true;
120 }
121
Object_hits_wormhole(object_t * obj,int ind)122 void Object_hits_wormhole(object_t *obj, int ind)
123 {
124 SET_BIT(obj->obj_status, WARPING);
125 obj->wormHoleHit = ind;
126 }
127
128 /*
129 * Warp balls connected to warped player.
130 */
Warp_balls(player_t * pl,clpos_t dest)131 static void Warp_balls(player_t *pl, clpos_t dest)
132 {
133 /*
134 * Don't connect to balls while warping.
135 */
136 if (Player_uses_connector(pl))
137 pl->ball = NULL;
138
139 if (BIT(pl->have, HAS_BALL)) {
140 /*
141 * Warp every ball associated with player.
142 * NB. the connector can cross a wall boundary this is
143 * allowed, so long as the ball itself doesn't collide.
144 */
145 int k;
146
147 for (k = 0; k < NumObjs; k++) {
148 object_t *b = Obj[k];
149
150 if (b->type == OBJ_BALL && b->id == pl->id) {
151 clpos_t ballpos;
152 hitmask_t hitmask = BALL_BIT|HITMASK(pl->team);
153
154 ballpos.cx = b->pos.cx + dest.cx - pl->pos.cx;
155 ballpos.cy = b->pos.cy + dest.cy - pl->pos.cy;
156 ballpos = World_wrap_clpos(ballpos);
157 if (!World_contains_clpos(ballpos)
158 || (shape_is_inside(ballpos.cx, ballpos.cy, hitmask,
159 (object_t *)b, &ball_wire, 0)
160 != NO_GROUP)) {
161 b->life = 0.0;
162 continue;
163 }
164 Object_position_set_clpos(b, ballpos);
165 Object_position_remember(b);
166 Cell_add_object(b);
167 }
168 }
169 }
170 }
171
Find_wormhole_dest(int wh_hit_ind)172 static int Find_wormhole_dest(int wh_hit_ind)
173 {
174 int wh_ind;
175 wormhole_t *wh, *wh_hit = Wormhole_by_index(wh_hit_ind);
176
177 if (wh_hit->type == WORM_FIXED)
178 return wh_hit_ind;
179
180 if (wh_hit->countdown > 0)
181 return wh_hit->lastdest;
182
183 do {
184 wh_ind = (int)(rfrac() * Num_wormholes());
185 wh = Wormhole_by_index(wh_ind);
186 }
187 while (wh->type == WORM_IN
188 || wh->type == WORM_FIXED
189 || wh_hit_ind == wh_ind);
190
191 return wh_ind;
192 }
193
194 /*
195 * Move player trough wormhole.
196 */
Traverse_wormhole(player_t * pl)197 static void Traverse_wormhole(player_t *pl)
198 {
199 clpos_t dest;
200 int wh_dest;
201 wormhole_t *wh_hit = Wormhole_by_index(pl->wormHoleHit);
202
203 wh_dest = Find_wormhole_dest(pl->wormHoleHit);
204 /*assert(wh_dest != pl->wormHoleHit);*/
205 sound_play_sensors(pl->pos, WORM_HOLE_SOUND);
206 dest = Wormhole_by_index(wh_dest)->pos;
207 Warp_balls(pl, dest);
208 pl->wormHoleDest = wh_dest;
209 Object_position_init_clpos(OBJ_PTR(pl), dest);
210 pl->forceVisible += 15;
211 /*assert(pl->wormHoleHit != NO_IND);*/
212
213 if (wh_dest != pl->wormHoleHit) {
214 wh_hit->lastdest = wh_dest;
215 wh_hit->countdown = options.wormholeStableTicks;
216 }
217 /*else
218 assert(0);*/
219
220 CLR_BIT(pl->obj_status, WARPING);
221 SET_BIT(pl->obj_status, WARPED);
222
223 sound_play_sensors(pl->pos, WORM_HOLE_SOUND);
224 }
225
226 /*
227 * Returns true if warp status was achieved.
228 */
Initiate_hyperjump(player_t * pl)229 bool Initiate_hyperjump(player_t *pl)
230 {
231 if (pl->item[ITEM_HYPERJUMP] <= 0)
232 return false;
233 if (pl->fuel.sum < -ED_HYPERJUMP)
234 return false;
235 pl->item[ITEM_HYPERJUMP]--;
236 Player_add_fuel(pl, ED_HYPERJUMP);
237 SET_BIT(pl->obj_status, WARPING);
238 pl->wormHoleHit = -1;
239 return true;
240 }
241
242 /*
243 * Player has used hyperjump item.
244 */
Hyperjump(player_t * pl)245 static void Hyperjump(player_t *pl)
246 {
247 clpos_t dest;
248 int counter;
249 hitmask_t hitmask = NONBALL_BIT | HITMASK(pl->team); /* kps - ok ? */
250
251 /* Try to find empty space to hyperjump to. */
252 for (counter = 100; counter > 0; counter--) {
253 dest = World_get_random_clpos();
254 if (shape_is_inside(dest.cx, dest.cy, hitmask, OBJ_PTR(pl),
255 (shape_t *)pl->ship, pl->dir) == NO_GROUP)
256 break;
257 }
258
259 /* We can't find an empty space, hyperjump failed. */
260 if (!counter) {
261 /* need to do something else here ? */
262 Set_player_message(pl, "Could not hyperjump. [*Server notice*]");
263 CLR_BIT(pl->obj_status, WARPING);
264 sound_play_sensors(pl->pos, HYPERJUMP_SOUND);
265 return;
266 }
267
268 sound_play_sensors(pl->pos, HYPERJUMP_SOUND);
269
270 Warp_balls(pl, dest);
271
272 Object_position_init_clpos(OBJ_PTR(pl), dest);
273 pl->forceVisible += 15;
274
275 CLR_BIT(pl->obj_status, WARPING);
276 }
277
Player_warp(player_t * pl)278 void Player_warp(player_t *pl)
279 {
280 if (pl->wormHoleHit == NO_IND)
281 Hyperjump(pl);
282 else
283 Traverse_wormhole(pl);
284 }
285
Player_finish_warp(player_t * pl)286 void Player_finish_warp(player_t *pl)
287 {
288 int group;
289 hitmask_t hitmask = NONBALL_BIT | HITMASK(pl->team);
290 /*
291 * clear warped, so we can use shape_is inside,
292 * Wormhole_hitfunc check for WARPED bit.
293 */
294 CLR_BIT(pl->obj_status, WARPED);
295 group = shape_is_inside(pl->pos.cx, pl->pos.cy, hitmask,
296 OBJ_PTR(pl), (shape_t *)pl->ship,
297 pl->dir);
298 /*
299 * kps - we might possibly have entered another polygon, e.g.
300 * a wormhole ?
301 */
302 if (group != NO_GROUP)
303 SET_BIT(pl->obj_status, WARPED);
304 }
305
Object_warp(object_t * obj)306 void Object_warp(object_t *obj)
307 {
308 clpos_t dest;
309 int wh_dest;
310 wormhole_t *wh_hit = Wormhole_by_index(obj->wormHoleHit);
311
312 wh_dest = Find_wormhole_dest(obj->wormHoleHit);
313 /*assert(wh_dest != obj->wormHoleHit);*/
314 dest = Wormhole_by_index(wh_dest)->pos;
315 obj->wormHoleDest = wh_dest;
316 Object_position_init_clpos(obj, dest);
317 /*assert(obj->wormHoleHit != NO_IND);*/
318
319 if (wh_dest != obj->wormHoleHit) {
320 wh_hit->lastdest = wh_dest;
321 wh_hit->countdown = options.wormholeStableTicks;
322 }
323 /*else
324 assert(0);*/
325
326 CLR_BIT(obj->obj_status, WARPING);
327 SET_BIT(obj->obj_status, WARPED);
328 }
329
Object_finish_warp(object_t * obj)330 void Object_finish_warp(object_t *obj)
331 {
332 int group;
333 hitmask_t hitmask = NONBALL_BIT | HITMASK(obj->team);
334 /*
335 * clear warped, so we can use shape_is inside,
336 * Wormhole_hitfunc check for WARPED bit.
337 */
338 CLR_BIT(obj->obj_status, WARPED);
339 group = is_inside(obj->pos.cx, obj->pos.cy, hitmask, obj);
340
341 /*
342 * kps - we might possibly have entered another polygon, e.g.
343 * a wormhole ?
344 */
345 if (group != NO_GROUP)
346 SET_BIT(obj->obj_status, WARPED);
347 }
348