1 /* $Id: rules.c,v 5.14 2002/04/20 11:57:22 bertg Exp $
2  *
3  * XPilot, a multiplayer gravity war game.  Copyright (C) 1991-2001 by
4  *
5  *      Bj�rn Stabell        <bjoern@xpilot.org>
6  *      Ken Ronny Schouten   <ken@xpilot.org>
7  *      Bert Gijsbers        <bert@xpilot.org>
8  *      Dick Balaska         <dick@xpilot.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 
28 #ifdef _WINDOWS
29 # include <windows.h>
30 #endif
31 
32 #define SERVER
33 #include "version.h"
34 #include "config.h"
35 #include "serverconst.h"
36 #include "global.h"
37 #include "proto.h"
38 #include "map.h"
39 #include "rules.h"
40 #include "bit.h"
41 #include "cannon.h"
42 
43 char rules_version[] = VERSION;
44 
45 
46 #define MAX_FUEL                10000
47 #define MAX_WIDEANGLE           99
48 #define MAX_REARSHOT            99
49 #define MAX_CLOAK               99
50 #define MAX_SENSOR              99
51 #define MAX_TRANSPORTER         99
52 #define MAX_MINE                99
53 #define MAX_MISSILE             99
54 #define MAX_ECM                 99
55 #define MAX_ARMOR		99
56 #define MAX_EMERGENCY_THRUST    99
57 #define MAX_AUTOPILOT           99
58 #define MAX_EMERGENCY_SHIELD    99
59 #define MAX_DEFLECTOR           99
60 #define MAX_MIRROR		99
61 #define MAX_PHASING             99
62 #define MAX_HYPERJUMP           99
63 #define MAX_LASER		99
64 #define MAX_TRACTOR_BEAM	99
65 
66 long	KILLING_SHOTS = (OBJ_SHOT|OBJ_CANNON_SHOT|OBJ_SMART_SHOT
67 			 |OBJ_TORPEDO|OBJ_HEAT_SHOT|OBJ_PULSE);
68 long	DEF_BITS = 0;
69 long	KILL_BITS = (THRUSTING|PLAYING|KILLED|SELF_DESTRUCT|WARPING|WARPED);
70 long	DEF_HAVE =
71 	(HAS_SHIELD|HAS_COMPASS|HAS_REFUEL|HAS_REPAIR|HAS_CONNECTOR
72 	|HAS_SHOT|HAS_LASER);
73 long	DEF_USED = (HAS_SHIELD|HAS_COMPASS);
74 long	USED_KILL =
75 	(HAS_REFUEL|HAS_REPAIR|HAS_CONNECTOR|HAS_SHOT|HAS_LASER|HAS_ARMOR
76 	|HAS_TRACTOR_BEAM|HAS_CLOAKING_DEVICE|HAS_PHASING_DEVICE
77 	|HAS_DEFLECTOR|HAS_MIRROR|HAS_EMERGENCY_SHIELD|HAS_EMERGENCY_THRUST);
78 
79 
80 
81 /*
82  * Convert between probability for something to happen a given second on a
83  * given block, to chance for such an event to happen on any block this tick.
84  */
Set_item_chance(int item)85 static void Set_item_chance(int item)
86 {
87     DFLOAT	max = itemProbMult * maxItemDensity * World.x * World.y;
88     DFLOAT	sum = 0;
89     int		i, num = 0;
90 
91     if (itemProbMult * World.items[item].prob > 0) {
92 	World.items[item].chance = (int)(1.0
93 	    / (itemProbMult * World.items[item].prob * World.x * World.y * FPS));
94 	World.items[item].chance = MAX(World.items[item].chance, 1);
95     } else {
96 	World.items[item].chance = 0;
97     }
98     if (max > 0) {
99 	if (max < 1) {
100 	    World.items[item].max = 1;
101 	} else {
102 	    World.items[item].max = (int)max;
103 	}
104     } else {
105 	World.items[item].max = 0;
106     }
107     if (!BIT(CANNON_USE_ITEM, 1U << item)) {
108 	World.items[item].cannonprob = 0;
109 	return;
110     }
111     for (i = 0; i < NUM_ITEMS; i++) {
112 	if (World.items[i].prob > 0
113 	    && BIT(CANNON_USE_ITEM, 1U << i)) {
114 	    sum += World.items[i].prob;
115 	    num++;
116 	}
117     }
118     if (num) {
119 	World.items[item].cannonprob = World.items[item].prob
120 				       * (num / sum)
121 				       * (maxItemDensity / 0.00012);
122     } else {
123 	World.items[item].cannonprob = 0;
124     }
125 }
126 
127 
128 /*
129  * An item probability has been changed during game play.
130  * Update the World.items structure and test if there are more items
131  * in the world than wanted for the new item probabilities.
132  * This function is also called when itemProbMult or maxItemDensity changes.
133  */
Tune_item_probs(void)134 void Tune_item_probs(void)
135 {
136     int			i, j, excess;
137 
138     for (i = 0; i < NUM_ITEMS; i++) {
139 	Set_item_chance(i);
140 	excess = World.items[i].num - World.items[i].max;
141 	if (excess > 0) {
142 	    for (j = 0; j < NumObjs; j++) {
143 		object *obj = Obj[j];
144 		if (obj->type == OBJ_ITEM) {
145 		    if (obj->info == i) {
146 			Delete_shot(j);
147 			j--;
148 			if (--excess == 0) {
149 			    break;
150 			}
151 		    }
152 		}
153 	    }
154 	}
155     }
156 }
157 
Tune_asteroid_prob(void)158 void Tune_asteroid_prob(void)
159 {
160     DFLOAT	max = maxAsteroidDensity * World.x * World.y;
161 
162     if (World.asteroids.prob > 0) {
163 	World.asteroids.chance = (int)(1.0
164 			/ (World.asteroids.prob * World.x * World.y * FPS));
165 	World.asteroids.chance = MAX(World.asteroids.chance, 1);
166     } else {
167 	World.asteroids.chance = 0;
168     }
169     if (max > 0) {
170 	if (max < 1) {
171 	    World.asteroids.max = 1;
172 	} else {
173 	    World.asteroids.max = (int)max;
174 	}
175     } else {
176 	World.asteroids.max = 0;
177     }
178     /* superfluous asteroids are handled by Asteroid_update() */
179 
180     /* Tune asteroid concentrator parameters */
181     LIMIT(asteroidConcentratorRadius, 1, World.diagonal);
182     LIMIT(asteroidConcentratorProb, 0.0, 1.0);
183 }
184 
185 /*
186  * Postprocess a change command for the number of items per pack.
187  */
Tune_item_packs(void)188 void Tune_item_packs(void)
189 {
190     World.items[ITEM_MINE].max_per_pack = maxMinesPerPack;
191     World.items[ITEM_MISSILE].max_per_pack = maxMissilesPerPack;
192 }
193 
194 
195 /*
196  * Initializes special items.
197  * First parameter is type,
198  * second and third parameters are minimum and maximum number
199  * of elements one item gives when picked up by a ship.
200  */
Init_item(int item,int minpp,int maxpp)201 static void Init_item(int item, int minpp, int maxpp)
202 {
203     World.items[item].num = 0;
204 
205     World.items[item].min_per_pack = minpp;
206     World.items[item].max_per_pack = maxpp;
207 
208     Set_item_chance(item);
209 }
210 
211 
212 /*
213  * Give (or remove) capabilities of the ships depending upon
214  * the availability of initial items.
215  * Limit the initial resources between minimum and maximum possible values.
216  */
Set_initial_resources(void)217 void Set_initial_resources(void)
218 {
219     int			i;
220 
221     LIMIT(World.items[ITEM_FUEL].limit, 0, MAX_FUEL);
222     LIMIT(World.items[ITEM_WIDEANGLE].limit, 0, MAX_WIDEANGLE);
223     LIMIT(World.items[ITEM_REARSHOT].limit, 0, MAX_REARSHOT);
224     LIMIT(World.items[ITEM_AFTERBURNER].limit, 0, MAX_AFTERBURNER);
225     LIMIT(World.items[ITEM_CLOAK].limit, 0, MAX_CLOAK);
226     LIMIT(World.items[ITEM_SENSOR].limit, 0, MAX_SENSOR);
227     LIMIT(World.items[ITEM_TRANSPORTER].limit, 0, MAX_TRANSPORTER);
228     LIMIT(World.items[ITEM_TANK].limit, 0, MAX_TANKS);
229     LIMIT(World.items[ITEM_MINE].limit, 0, MAX_MINE);
230     LIMIT(World.items[ITEM_MISSILE].limit, 0, MAX_MISSILE);
231     LIMIT(World.items[ITEM_ECM].limit, 0, MAX_ECM);
232     LIMIT(World.items[ITEM_LASER].limit, 0, MAX_LASER);
233     LIMIT(World.items[ITEM_EMERGENCY_THRUST].limit, 0, MAX_EMERGENCY_THRUST);
234     LIMIT(World.items[ITEM_TRACTOR_BEAM].limit, 0, MAX_TRACTOR_BEAM);
235     LIMIT(World.items[ITEM_AUTOPILOT].limit, 0, MAX_AUTOPILOT);
236     LIMIT(World.items[ITEM_EMERGENCY_SHIELD].limit, 0, MAX_EMERGENCY_SHIELD);
237     LIMIT(World.items[ITEM_DEFLECTOR].limit, 0, MAX_DEFLECTOR);
238     LIMIT(World.items[ITEM_PHASING].limit, 0, MAX_PHASING);
239     LIMIT(World.items[ITEM_HYPERJUMP].limit, 0, MAX_HYPERJUMP);
240     LIMIT(World.items[ITEM_MIRROR].limit, 0, MAX_MIRROR);
241     LIMIT(World.items[ITEM_ARMOR].limit, 0, MAX_ARMOR);
242 
243     for (i = 0; i < NUM_ITEMS; i++) {
244 	LIMIT(World.items[i].initial, 0, World.items[i].limit);
245     }
246 
247     CLR_BIT(DEF_HAVE,
248 	HAS_CLOAKING_DEVICE |
249 	HAS_EMERGENCY_THRUST |
250 	HAS_EMERGENCY_SHIELD |
251 	HAS_PHASING_DEVICE |
252 	HAS_TRACTOR_BEAM |
253 	HAS_AUTOPILOT |
254 	HAS_DEFLECTOR |
255 	HAS_MIRROR |
256 	HAS_ARMOR);
257 
258     if (World.items[ITEM_CLOAK].initial > 0)
259 	SET_BIT(DEF_HAVE, HAS_CLOAKING_DEVICE);
260     if (World.items[ITEM_EMERGENCY_THRUST].initial > 0)
261 	SET_BIT(DEF_HAVE, HAS_EMERGENCY_THRUST);
262     if (World.items[ITEM_EMERGENCY_SHIELD].initial > 0)
263 	SET_BIT(DEF_HAVE, HAS_EMERGENCY_SHIELD);
264     if (World.items[ITEM_PHASING].initial > 0)
265 	SET_BIT(DEF_HAVE, HAS_PHASING_DEVICE);
266     if (World.items[ITEM_TRACTOR_BEAM].initial > 0)
267 	SET_BIT(DEF_HAVE, HAS_TRACTOR_BEAM);
268     if (World.items[ITEM_AUTOPILOT].initial > 0)
269 	SET_BIT(DEF_HAVE, HAS_AUTOPILOT);
270     if (World.items[ITEM_DEFLECTOR].initial > 0)
271 	SET_BIT(DEF_HAVE, HAS_DEFLECTOR);
272     if (World.items[ITEM_MIRROR].initial > 0)
273 	SET_BIT(DEF_HAVE, HAS_MIRROR);
274     if (World.items[ITEM_ARMOR].initial > 0)
275 	SET_BIT(DEF_HAVE, HAS_ARMOR);
276 }
277 
278 
Set_misc_item_limits(void)279 void Set_misc_item_limits(void)
280 {
281     LIMIT(dropItemOnKillProb, 0.0, 1.0);
282     LIMIT(detonateItemOnKillProb, 0.0, 1.0);
283     LIMIT(movingItemProb, 0.0, 1.0);
284     LIMIT(randomItemProb, 0.0, 1.0);
285     LIMIT(destroyItemInCollisionProb, 0.0, 1.0);
286 
287     LIMIT(itemConcentratorRadius, 1, World.diagonal);
288     LIMIT(itemConcentratorProb, 0.0, 1.0);
289 
290     LIMIT(asteroidItemProb, 0.0, 1.0);
291 
292     if (asteroidMaxItems < 0)
293 	asteroidMaxItems = 0;
294 }
295 
296 
297 /*
298  * First time initialization of all global item stuff.
299  */
Set_world_items(void)300 void Set_world_items(void)
301 {
302     Init_item(ITEM_FUEL, 0, 0);
303     Init_item(ITEM_TANK, 1, 1);
304     Init_item(ITEM_ECM, 1, 1);
305     Init_item(ITEM_ARMOR, 1, 1);
306     Init_item(ITEM_MINE, 1, maxMinesPerPack);
307     Init_item(ITEM_MISSILE, 1, maxMissilesPerPack);
308     Init_item(ITEM_CLOAK, 1, 1);
309     Init_item(ITEM_SENSOR, 1, 1);
310     Init_item(ITEM_WIDEANGLE, 1, 1);
311     Init_item(ITEM_REARSHOT, 1, 1);
312     Init_item(ITEM_AFTERBURNER, 1, 1);
313     Init_item(ITEM_TRANSPORTER, 1, 1);
314     Init_item(ITEM_MIRROR, 1, 1);
315     Init_item(ITEM_DEFLECTOR, 1, 1);
316     Init_item(ITEM_HYPERJUMP, 1, 1);
317     Init_item(ITEM_PHASING, 1, 1);
318     Init_item(ITEM_LASER, 1, 1);
319     Init_item(ITEM_EMERGENCY_THRUST, 1, 1);
320     Init_item(ITEM_EMERGENCY_SHIELD, 1, 1);
321     Init_item(ITEM_TRACTOR_BEAM, 1, 1);
322     Init_item(ITEM_AUTOPILOT, 1, 1);
323 
324     Set_misc_item_limits();
325 
326     Set_initial_resources();
327 }
328 
329 
Set_world_rules(void)330 void Set_world_rules(void)
331 {
332     static rules_t rules;
333 
334     rules.mode =
335       ((crashWithPlayer ? CRASH_WITH_PLAYER : 0)
336        | (bounceWithPlayer ? BOUNCE_WITH_PLAYER : 0)
337        | (playerKillings ? PLAYER_KILLINGS : 0)
338        | (playerShielding ? PLAYER_SHIELDING : 0)
339        | (limitedVisibility ? LIMITED_VISIBILITY : 0)
340        | (limitedLives ? LIMITED_LIVES : 0)
341        | (teamPlay ? TEAM_PLAY : 0)
342        | (allowAlliances ? ALLIANCES : 0)
343        | (timing ? TIMING : 0)
344        | (allowNukes ? ALLOW_NUKES : 0)
345        | (allowClusters ? ALLOW_CLUSTERS : 0)
346        | (allowModifiers ? ALLOW_MODIFIERS : 0)
347        | (allowLaserModifiers ? ALLOW_LASER_MODIFIERS : 0)
348        | (edgeWrap ? WRAP_PLAY : 0));
349     rules.lives = worldLives;
350     World.rules = &rules;
351 
352     if (BIT(World.rules->mode, TEAM_PLAY)) {
353 	CLR_BIT(World.rules->mode, ALLIANCES);
354     }
355 
356     if (!BIT(World.rules->mode, PLAYER_KILLINGS)) {
357 	CLR_BIT(KILLING_SHOTS,
358 		OBJ_SHOT|OBJ_CANNON_SHOT|OBJ_SMART_SHOT
359 		|OBJ_TORPEDO|OBJ_HEAT_SHOT|OBJ_PULSE);
360     }
361 
362     if (!BIT(World.rules->mode, PLAYER_SHIELDING)) {
363 	CLR_BIT(DEF_HAVE, HAS_SHIELD);
364     }
365 
366     DEF_USED &= DEF_HAVE;
367 }
368 
Set_world_asteroids(void)369 void Set_world_asteroids(void)
370 {
371     World.asteroids.num = 0;
372     Tune_asteroid_prob();
373 }
374