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