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