1 /*
2  * XPilot NG, a multiplayer space war game.
3  *
4  * Copyright (C) 2000-2004 by
5  *
6  *      Uoti Urpala          <uau@users.sourceforge.net>
7  *      Kristian S�derblom   <kps@users.sourceforge.net>
8  *
9  * Copyright (C) 1991-2001 by
10  *
11  *      Bj�rn Stabell        <bjoern@xpilot.org>
12  *      Ken Ronny Schouten   <ken@xpilot.org>
13  *      Bert Gijsbers        <bert@xpilot.org>
14  *      Dick Balaska         <dick@xpilot.org>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29  */
30 
31 #include "xpserver.h"
32 
33 /*
34  * Globals.
35  */
36 world_t World, *world;
37 bool is_polygon_map = false;
38 
39 static void Find_base_direction(void);
40 
41 
Check_map_object_counters(void)42 static void Check_map_object_counters(void)
43 {
44     int i;
45 
46     /*assert(world->NumCannons == 0);*/
47 
48     /*assert(world->NumGravs == 0);*/
49     /*assert(world->NumWormholes == 0);*/
50     /*assert(world->NumTreasures == 0);*/
51     /*assert(world->NumTargets == 0);*/
52     /*assert(world->NumBases == 0);*/
53     /*assert(world->NumItemConcs == 0);
54       assert(world->NumAsteroidConcs == 0);
55       assert(world->NumFrictionAreas == 0);*/
56     /*assert(world->NumEcms == 0);*/
57     /*assert(world->NumTransporters == 0);*/
58 
59     for (i = 0; i < MAX_TEAMS; i++) {
60 	assert(world->teams[i].NumMembers == 0);
61 	assert(world->teams[i].NumRobots == 0);
62 	assert(world->teams[i].NumBases == 0);
63 	assert(world->teams[i].NumTreasures == 0);
64 	assert(world->teams[i].NumEmptyTreasures == 0);
65 	assert(world->teams[i].TreasuresDestroyed == 0);
66 	assert(world->teams[i].TreasuresLeft == 0);
67 	assert(world->teams[i].SwapperId == NO_ID);
68     }
69 }
70 
shrink(void ** pp,size_t size)71 static void shrink(void **pp, size_t size)
72 {
73     void *p;
74 
75     p = realloc(*pp, size);
76     if (!p) {
77 	warn("Realloc failed!");
78 	exit(1);
79     }
80     *pp = p;
81 }
82 
83 #define SHRINK(T,P,N,M) { \
84 if ((M) > (N)) { \
85   shrink((void **)&(P), (N) * sizeof(T)); \
86   M = (N); \
87 } } \
88 
89 
Realloc_map_objects(void)90 static void Realloc_map_objects(void)
91 {
92     /*SHRINK(cannon_t, world->cannons, world->NumCannons, world->MaxCannons);*/
93     /*SHRINK(fuel_t, world->fuels, world->NumFuels, world->MaxFuels);*/
94     /*SHRINK(grav_t, world->gravs, world->NumGravs, world->MaxGravs);*/
95     /*SHRINK(wormhole_t, world->wormholes,
96       world->NumWormholes, world->MaxWormholes);*/
97     /*SHRINK(treasure_t, world->treasures,
98       world->NumTreasures, world->MaxTreasures);*/
99     /*SHRINK(target_t, world->targets, world->NumTargets, world->MaxTargets);*/
100     /*SHRINK(base_t, world->bases, world->NumBases, world->MaxBases);*/
101     /*SHRINK(item_concentrator_t, world->itemConcs,
102       world->NumItemConcs, world->MaxItemConcs);
103       SHRINK(asteroid_concentrator_t, world->asteroidConcs,
104       world->NumAsteroidConcs, world->MaxAsteroidConcs);
105       SHRINK(friction_area_t, world->frictionAreas,
106       world->NumFrictionAreas, world->MaxFrictionAreas);*/
107 }
108 
World_place_cannon(clpos_t pos,int dir,int team)109 int World_place_cannon(clpos_t pos, int dir, int team)
110 {
111     cannon_t t, *cannon;
112     int ind = Num_cannons(), i;
113 
114     t.pos = pos;
115     t.dir = dir;
116     t.team = team;
117     t.dead_ticks = 0;
118     t.conn_mask = ~0;
119     t.group = NO_GROUP;
120     t.score = CANNON_SCORE;
121     t.id = ind + MIN_CANNON_ID;
122     assert(Is_cannon_id(t.id));
123     if (t.id > MAX_CANNON_ID) {
124 	warn("The server supports only %d cannons per map.", NUM_CANNON_IDS);
125 	exit(1);
126     }
127     for (i = 0; i < NUM_ITEMS; i++)
128 	t.initial_items[i] = -1;
129     t.shot_speed = -1;
130     t.smartness = -1;
131     Arraylist_add(world->cannons, &t);
132     cannon = Cannon_by_index(ind);
133     assert(Cannon_by_id(t.id) == cannon);
134 
135     return ind;
136 }
137 
World_place_fuel(clpos_t pos,int team)138 int World_place_fuel(clpos_t pos, int team)
139 {
140     fuel_t t;
141     int ind = Num_fuels();
142 
143     t.pos = pos;
144     t.fuel = START_STATION_FUEL;
145     t.conn_mask = ~0;
146     t.last_change = frame_loops;
147     t.team = team;
148     Arraylist_add(world->fuels, &t);
149 
150     return ind;
151 }
152 
World_place_base(clpos_t pos,int dir,int team,int order)153 int World_place_base(clpos_t pos, int dir, int team, int order)
154 {
155     base_t t;
156     int ind = Num_bases(), i;
157 
158     t.pos = pos;
159     t.order = order;
160     /*
161      * The direction of the base should be so that it points
162      * up with respect to the gravity in the region.  This
163      * is fixed in Find_base_direction() when the gravity has
164      * been computed.
165      */
166     if (dir < 0 || dir >= RES) {
167 	warn("Base with direction %d in map.", dir);
168 	warn("Valid base directions are from 0 to %d.", RES-1);
169 	while (dir < 0)
170 	    dir += RES;
171 	while (dir >= RES)
172 	    dir -= RES;
173 	warn("Using direction %d for this base.", dir);
174     }
175 
176     t.dir = dir;
177     if (BIT(world->rules->mode, TEAM_PLAY)) {
178 	if (team < 0 || team >= MAX_TEAMS)
179 	    team = 0;
180 	t.team = team;
181 	world->teams[team].NumBases++;
182 	if (world->teams[team].NumBases == 1)
183 	    world->NumTeamBases++;
184     } else
185 	t.team = TEAM_NOT_SET;
186     t.ind = Num_bases();
187 
188     for (i = 0; i < NUM_ITEMS; i++)
189 	t.initial_items[i] = -1;
190     Arraylist_add(world->bases, &t);
191 
192     return ind;
193 }
194 
World_place_treasure(clpos_t pos,int team,bool empty,int ball_style)195 int World_place_treasure(clpos_t pos, int team, bool empty,
196 			 int ball_style)
197 {
198     treasure_t t;
199     int ind = Num_treasures();
200 
201     t.pos = pos;
202     t.have = false;
203     t.destroyed = 0;
204     t.team = team;
205     t.empty = empty;
206     t.ball_style = ball_style;
207     if (team != TEAM_NOT_SET) {
208 	world->teams[team].NumTreasures++;
209 	world->teams[team].TreasuresLeft++;
210     }
211     Arraylist_add(world->treasures, &t);
212 
213     return ind;
214 }
215 
World_place_target(clpos_t pos,int team)216 int World_place_target(clpos_t pos, int team)
217 {
218     target_t t;
219     int ind = Num_targets();
220 
221     t.pos = pos;
222     /*
223      * If we have a block based map, the team is determined in
224      * in Xpmap_find_map_object_teams().
225      */
226     t.team = team;
227     t.dead_ticks = 0;
228     t.damage = TARGET_DAMAGE;
229     t.conn_mask = ~0;
230     t.update_mask = 0;
231     t.last_change = frame_loops;
232     t.group = NO_GROUP;
233     Arraylist_add(world->targets, &t);
234 
235     return ind;
236 }
237 
World_place_wormhole(clpos_t pos,wormtype_t type)238 int World_place_wormhole(clpos_t pos, wormtype_t type)
239 {
240     wormhole_t t;
241     int ind = Num_wormholes();
242 
243     t.pos = pos;
244     t.countdown = 0;
245     t.lastdest = NO_IND;
246     t.type = type;
247     t.lastblock = SPACE;
248     t.lastID = NO_ID;
249     t.group = NO_GROUP;
250     Arraylist_add(world->wormholes, &t);
251 
252     return ind;
253 }
254 
255 
256 /*
257  * Allocate checkpoints for an xp map.
258  */
alloc_old_checks(void)259 static void alloc_old_checks(void)
260 {
261     int i;
262     check_t t;
263     clpos_t pos = { -1, -1 };
264 
265     t.pos = pos;
266 
267     for (i = 0; i < OLD_MAX_CHECKS; i++)
268 	STORE(check_t, world->checks, world->NumChecks, world->MaxChecks, t);
269 
270     SHRINK(check_t, world->checks, world->NumChecks, world->MaxChecks);
271     world->NumChecks = 0;
272 }
273 
World_place_check(clpos_t pos,int ind)274 int World_place_check(clpos_t pos, int ind)
275 {
276     check_t t;
277 
278     if (!BIT(world->rules->mode, TIMING)) {
279 	warn("Checkpoint on map with no timing.");
280 	return NO_IND;
281     }
282 
283     /* kps - need to do this for other map object types ? */
284     if (!World_contains_clpos(pos)) {
285 	warn("Checkpoint outside world, ignoring.");
286 	return NO_IND;
287     }
288 
289     /*
290      * On xp maps we can have only 26 checkpoints.
291      */
292     if (ind >= 0 && ind < OLD_MAX_CHECKS) {
293 	check_t *check;
294 
295 	if (world->NumChecks == 0)
296 	    alloc_old_checks();
297 
298 	/*
299 	 * kps hack - we can't use Check_by_index because it might return
300 	 * NULL since ind can here be >= world->NumChecks.
301 	 */
302 	check = &world->checks[ind];
303 	if (World_contains_clpos(check->pos)) {
304 	    warn("Map contains too many '%c' checkpoints.", 'A' + ind);
305 	    return NO_IND;
306 	}
307 
308 	check->pos = pos;
309 	world->NumChecks++;
310 	return ind;
311     }
312 
313     ind = world->NumChecks;
314     t.pos = pos;
315     STORE(check_t, world->checks, world->NumChecks, world->MaxChecks, t);
316     return ind;
317 }
318 
World_place_item_concentrator(clpos_t pos)319 int World_place_item_concentrator(clpos_t pos)
320 {
321     item_concentrator_t t;
322     int ind = Num_itemConcs();
323 
324     t.pos = pos;
325     Arraylist_add(world->itemConcs, &t);
326 
327     return ind;
328 }
329 
World_place_asteroid_concentrator(clpos_t pos)330 int World_place_asteroid_concentrator(clpos_t pos)
331 {
332     asteroid_concentrator_t t;
333     int ind = Num_asteroidConcs();
334 
335     t.pos = pos;
336     Arraylist_add(world->asteroidConcs, &t);
337 
338     return ind;
339 }
340 
World_place_grav(clpos_t pos,double force,int type)341 int World_place_grav(clpos_t pos, double force, int type)
342 {
343     grav_t t;
344     int ind = Num_gravs();
345 
346     t.pos = pos;
347     t.force = force;
348     t.type = type;
349     Arraylist_add(world->gravs, &t);
350 
351     return ind;
352 }
353 
World_place_friction_area(clpos_t pos,double fric)354 int World_place_friction_area(clpos_t pos, double fric)
355 {
356     friction_area_t t;
357     int ind = Num_frictionAreas();
358 
359     t.pos = pos;
360     t.friction_setting = fric;
361     /*t.friction = ... ; handled in timing setup */
362     Arraylist_add(world->frictionAreas, &t);
363 
364     return ind;
365 }
366 
367 shape_t filled_wire;
368 clpos_t filled_coords[4];
369 
Filled_wire_init(void)370 static void Filled_wire_init(void)
371 {
372     int i, h;
373 
374     filled_wire.num_points = 4;
375 
376     for (i = 0; i < 4; i++)
377 	filled_wire.pts[i] = &filled_coords[i];
378 
379     h = BLOCK_CLICKS / 2;
380 
381     /* whole (filled) block */
382     filled_coords[0].cx = -h;
383     filled_coords[0].cy = -h;
384     filled_coords[1].cx = h - 1;
385     filled_coords[1].cy = -h;
386     filled_coords[2].cx = h - 1;
387     filled_coords[2].cy = h - 1;
388     filled_coords[3].cx = -h;
389     filled_coords[3].cy = h - 1;
390 }
391 
World_init(void)392 int World_init(void)
393 {
394     int i;
395 
396     memset(world, 0, sizeof(world_t));
397 
398     if ((world->asteroidConcs
399 	 = Arraylist_alloc(sizeof(asteroid_concentrator_t))) == NULL)
400 	return -1;
401     if ((world->bases = Arraylist_alloc(sizeof(base_t))) == NULL)
402 	return -1;
403     if ((world->cannons = Arraylist_alloc(sizeof(cannon_t))) == NULL)
404 	return -1;
405     if ((world->ecms = Arraylist_alloc(sizeof(ecm_t))) == NULL)
406 	return -1;
407     if ((world->frictionAreas
408 	 = Arraylist_alloc(sizeof(friction_area_t))) == NULL)
409 	return -1;
410     if ((world->fuels = Arraylist_alloc(sizeof(fuel_t))) == NULL)
411 	return -1;
412     if ((world->itemConcs
413 	 = Arraylist_alloc(sizeof(item_concentrator_t))) == NULL)
414 	return -1;
415     if ((world->gravs = Arraylist_alloc(sizeof(grav_t))) == NULL)
416 	return -1;
417     if ((world->targets = Arraylist_alloc(sizeof(target_t))) == NULL)
418 	return -1;
419     if ((world->treasures = Arraylist_alloc(sizeof(treasure_t))) == NULL)
420 	return -1;
421     if ((world->transporters
422 	 = Arraylist_alloc(sizeof(transporter_t))) == NULL)
423 	return -1;
424     if ((world->wormholes = Arraylist_alloc(sizeof(wormhole_t))) == NULL)
425 	return -1;
426 
427     for (i = 0; i < MAX_TEAMS; i++)
428 	Team_by_index(i)->SwapperId = NO_ID;
429 
430     Filled_wire_init();
431 
432     return 0;
433 }
434 
World_free(void)435 void World_free(void)
436 {
437     XFREE(world->block);
438     XFREE(world->gravity);
439     /*XFREE(world->gravs);*/
440     /*XFREE(world->bases);*/
441     /*XFREE(world->cannons);*/
442     XFREE(world->checks);
443     /*XFREE(world->fuels);*/
444     /*XFREE(world->wormholes);*/
445     /*XFREE(world->itemConcs);
446     XFREE(world->asteroidConcs);
447     XFREE(world->frictionAreas);*/
448 }
449 
World_alloc(void)450 static bool World_alloc(void)
451 {
452     int x;
453     unsigned char *map_line;
454     unsigned char **map_pointer;
455     vector_t *grav_line;
456     vector_t **grav_pointer;
457 
458     assert(world->block == NULL);
459     assert(world->gravity == NULL);
460 
461     world->block = (unsigned char **)
462 	malloc(sizeof(unsigned char *) * world->x
463 	       + world->x * sizeof(unsigned char) * world->y);
464     world->gravity = (vector_t **)
465 	malloc(sizeof(vector_t *) * world->x
466 	       + world->x * sizeof(vector_t) * world->y);
467 
468     /*assert(world->gravs == NULL);*/
469     /*assert(world->bases == NULL);*/
470     /*assert(world->fuels == NULL);*/
471     /*assert(world->cannons == NULL);*/
472     assert(world->checks == NULL);
473     /*assert(world->wormholes == NULL);*/
474     /*assert(world->itemConcs == NULL);*/
475     /*assert(world->asteroidConcs == NULL);*/
476 
477     if (world->block == NULL || world->gravity == NULL) {
478 	World_free();
479 	error("Couldn't allocate memory for map");
480 	return false;
481     }
482 
483     map_pointer = world->block;
484     map_line = (unsigned char*) ((unsigned char**)map_pointer + world->x);
485 
486     grav_pointer = world->gravity;
487     grav_line = (vector_t*) ((vector_t**)grav_pointer + world->x);
488 
489     for (x = 0; x < world->x; x++) {
490 	*map_pointer = map_line;
491 	map_pointer += 1;
492 	map_line += world->y;
493 	*grav_pointer = grav_line;
494 	grav_pointer += 1;
495 	grav_line += world->y;
496     }
497 
498     return true;
499 }
500 
501 /*
502  * This function can be called after the map options have been read.
503  */
Grok_map_size(void)504 static bool Grok_map_size(void)
505 {
506     bool bad = false;
507     int w = options.mapWidth, h = options.mapHeight;
508 
509     if (!is_polygon_map) {
510 	w *= BLOCK_SZ;
511 	h *= BLOCK_SZ;
512     }
513 
514     if (w < MIN_MAP_SIZE) {
515 	warn("mapWidth too small, minimum is %d pixels (%d blocks).\n",
516 	     MIN_MAP_SIZE, MIN_MAP_SIZE / BLOCK_SZ + 1);
517 	bad = true;
518     }
519     if (w > MAX_MAP_SIZE) {
520 	warn("mapWidth too big, maximum is %d pixels (%d blocks).\n",
521 	     MAX_MAP_SIZE, MAX_MAP_SIZE / BLOCK_SZ);
522 	bad = true;
523     }
524     if (h < MIN_MAP_SIZE) {
525 	warn("mapHeight too small, minimum is %d pixels (%d blocks).\n",
526 	     MIN_MAP_SIZE, MIN_MAP_SIZE / BLOCK_SZ + 1);
527 	bad = true;
528     }
529     if (h > MAX_MAP_SIZE) {
530 	warn("mapWidth too big, maximum is %d pixels (%d blocks).\n",
531 	     MAX_MAP_SIZE, MAX_MAP_SIZE / BLOCK_SZ);
532 	bad = true;
533     }
534 
535     if (bad)
536 	return false;
537 
538     /* pixel sizes */
539     world->width = w;
540     world->height = h;
541     if (!is_polygon_map && options.extraBorder) {
542 	world->width += 2 * BLOCK_SZ;
543 	world->height += 2 * BLOCK_SZ;
544     }
545     world->hypotenuse = LENGTH(world->width, world->height);
546 
547     /* click sizes */
548     world->cwidth = world->width * CLICK;
549     world->cheight = world->height * CLICK;
550 
551     /* block sizes */
552     world->x = (world->width - 1) / BLOCK_SZ + 1; /* !@# */
553     world->y = (world->height - 1) / BLOCK_SZ + 1;
554     world->diagonal = LENGTH(world->x, world->y);
555     world->bwidth_floor = world->width / BLOCK_SZ;
556     world->bheight_floor = world->height / BLOCK_SZ;
557 
558     return true;
559 }
560 
Grok_map_options(void)561 bool Grok_map_options(void)
562 {
563     if (world->have_options)
564 	return true;
565 
566     Check_map_object_counters();
567 
568     if (!Grok_map_size())
569 	return false;
570 
571     strlcpy(world->name, options.mapName, sizeof(world->name));
572     strlcpy(world->author, options.mapAuthor, sizeof(world->author));
573     strlcpy(world->dataURL, options.dataURL, sizeof(world->dataURL));
574 
575     if (!World_alloc())
576 	return false;
577 
578     Set_world_rules();
579     Set_world_items();
580     Set_world_asteroids();
581 
582     if (BIT(world->rules->mode, TEAM_PLAY|TIMING) == (TEAM_PLAY|TIMING)) {
583 	warn("Cannot teamplay while in race mode -- ignoring teamplay");
584 	CLR_BIT(world->rules->mode, TEAM_PLAY);
585     }
586 
587     world->have_options = true;
588 
589     return true;
590 }
591 
Grok_map(void)592 bool Grok_map(void)
593 {
594     if (!Grok_map_options())
595 	return false;
596 
597     if (!is_polygon_map) {
598 	Xpmap_grok_map_data();
599 	Xpmap_tags_to_internal_data();
600 	Xpmap_find_map_object_teams();
601     }
602 
603     if (!Verify_wormhole_consistency())
604 	return false;
605 
606     if (BIT(world->rules->mode, TIMING) && world->NumChecks == 0) {
607 	warn("No checkpoints found while race mode (timing) was set.");
608 	warn("Turning off race mode.");
609 	CLR_BIT(world->rules->mode, TIMING);
610     }
611 
612     /* kps - what are these doing here ? */
613     if (options.maxRobots == -1)
614 	options.maxRobots = Num_bases();
615 
616     if (options.minRobots == -1)
617 	options.minRobots = options.maxRobots;
618 
619     Realloc_map_objects();
620 
621     if (Num_bases() <= 0)
622 	fatal("Map has no bases!");
623 
624     xpprintf("World....: %s\nBases....: %d\nMapsize..: %dx%d pixels\n"
625 	     "Team play: %s\n",
626 	     world->name, Num_bases(), world->width, world->height,
627 	     BIT(world->rules->mode, TEAM_PLAY) ? "on" : "off");
628 
629     if (!is_polygon_map)
630 	Xpmap_blocks_to_polygons();
631 
632     Compute_gravity();
633     Find_base_direction();
634 
635     return true;
636 }
637 
638 /*
639  * Return the team that is closest to this click position.
640  */
Find_closest_team(clpos_t pos)641 int Find_closest_team(clpos_t pos)
642 {
643     int team = TEAM_NOT_SET, i;
644     double closest = FLT_MAX, l;
645 
646     for (i = 0; i < Num_bases(); i++) {
647 	base_t *base = Base_by_index(i);
648 
649 	if (base->team == TEAM_NOT_SET)
650 	    continue;
651 
652 	l = Wrap_length(pos.cx - base->pos.cx, pos.cy - base->pos.cy);
653 	if (l < closest) {
654 	    team = base->team;
655 	    closest = l;
656 	}
657     }
658 
659     return team;
660 }
661 
662 
Find_base_direction(void)663 static void Find_base_direction(void)
664 {
665     /* kps - this might go wrong if we run in -options.polygonMode ? */
666     if (!is_polygon_map)
667 	Xpmap_find_base_direction();
668 }
669 
Wrap_findDir(double dx,double dy)670 double Wrap_findDir(double dx, double dy)
671 {
672     dx = WRAP_DX(dx);
673     dy = WRAP_DY(dy);
674     return findDir(dx, dy);
675 }
676 
Wrap_cfindDir(int dcx,int dcy)677 double Wrap_cfindDir(int dcx, int dcy)
678 {
679     dcx = WRAP_DCX(dcx);
680     dcy = WRAP_DCY(dcy);
681     return findDir((double)dcx, (double)dcy);
682 }
683 
Wrap_length(int dcx,int dcy)684 double Wrap_length(int dcx, int dcy)
685 {
686     dcx = WRAP_DCX(dcx);
687     dcy = WRAP_DCY(dcy);
688     return LENGTH(dcx, dcy);
689 }
690