File: improvement.c

package info (click to toggle)
freeciv 2.1.5-2
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 95,924 kB
  • ctags: 20,829
  • sloc: ansic: 231,256; sh: 4,872; makefile: 2,867; python: 1,259; yacc: 318
file content (486 lines) | stat: -rw-r--r-- 16,110 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
/********************************************************************** 
 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
***********************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <assert.h>

#include "fcintl.h"
#include "game.h"
#include "log.h"
#include "map.h"
#include "mem.h"
#include "shared.h" /* ARRAY_SIZE */
#include "support.h"
#include "tech.h"

#include "improvement.h"

static const char *genus_names[IG_LAST] = {
  "GreatWonder", "SmallWonder", "Improvement", "Special"
};

static const char *flag_names[] = {
  "VisibleByOthers", "SaveSmallWonder", "Gold"
};
/* Note that these strings must correspond with the enums in impr_flag_id,
   in common/improvement.h */

/**************************************************************************
All the city improvements:
Use improvement_by_number(id) to access the array.
The improvement_types array is now setup in:
   server/ruleset.c (for the server)
   client/packhand.c (for the client)
**************************************************************************/
static struct impr_type improvement_types[B_LAST];

/**************************************************************************
  Convert impr genus names to enum; case insensitive;
  returns IG_LAST if can't match.
**************************************************************************/
enum impr_genus_id find_genus_by_rule_name(const char *s)
{
  enum impr_genus_id i;

  for(i = 0; i < ARRAY_SIZE(genus_names); i++) {
    if (mystrcasecmp(genus_names[i], s) == 0) {
      break;
    }
  }
  return i;
}

/****************************************************************************
  Inialize building structures.
****************************************************************************/
void improvements_init(void)
{
  int i;

  /* Can't use impr_type_iterate or improvement_by_number here because
   * num_impr_types isn't known yet. */
  for (i = 0; i < ARRAY_SIZE(improvement_types); i++) {
    struct impr_type *p = &improvement_types[i];

    p->index = i;
    requirement_vector_init(&p->reqs);
  }
}

/**************************************************************************
  Frees the memory associated with this improvement.
**************************************************************************/
static void improvement_free(Impr_type_id id)
{
  struct impr_type *p = improvement_by_number(id);

  free(p->helptext);
  p->helptext = NULL;

  requirement_vector_free(&p->reqs);
}

/***************************************************************
 Frees the memory associated with all improvements.
***************************************************************/
void improvements_free()
{
  impr_type_iterate(impr) {
    improvement_free(impr);
  } impr_type_iterate_end;
}

/**************************************************************************
Returns 1 if the improvement_type "exists" in this game, 0 otherwise.
An improvement_type doesn't exist if one of:
- id is out of range;
- the improvement_type has been flagged as removed by setting its
  tech_req to A_LAST;
- it is a space part, and the spacerace is not enabled.
Arguably this should be called improvement_type_exists, but that's too long.
**************************************************************************/
bool improvement_exists(Impr_type_id id)
{
  if (id < 0 || id >= B_LAST || id>= game.control.num_impr_types)
    return FALSE;

  if (!game.info.spacerace
      && (building_has_effect(id, EFT_SS_STRUCTURAL)
	  || building_has_effect(id, EFT_SS_COMPONENT)
	  || building_has_effect(id, EFT_SS_MODULE))) {
    /* This assumes that space parts don't have any other effects. */
    return FALSE;
  }

  return TRUE;
}

/**************************************************************************
  Returns the improvement type for the given index/ID.  Returns NULL for
  an out-of-range index.
**************************************************************************/
struct impr_type *improvement_by_number(const Impr_type_id id)
{
  if (id < 0 || id >= game.control.num_impr_types) {
    return NULL;
  }
  return &improvement_types[id];
}

/**************************************************************************
  Return the (translated) name of the given improvement. 
  You don't have to free the return pointer.
**************************************************************************/
const char *improvement_name_translation(Impr_type_id id)
{
  struct impr_type *itp = improvement_by_number(id);

  if (NULL == itp->name.translated) {
    /* delayed (unified) translation */
    itp->name.translated = ('\0' == itp->name.vernacular[0])
			   ? itp->name.vernacular
			   : Q_(itp->name.vernacular);
  }
  return itp->name.translated;
}

/****************************************************************************
  Return the (untranslated) rule name of the improvement.
  You don't have to free the return pointer.
****************************************************************************/
const char *improvement_rule_name(Impr_type_id id)
{
  return Qn_(improvement_by_number(id)->name.vernacular); 
}

/****************************************************************************
  Returns the number of shields it takes to build this improvement.
****************************************************************************/
int impr_build_shield_cost(Impr_type_id id)
{
  int base = improvement_by_number(id)->build_cost;

  return MAX(base * game.info.shieldbox / 100, 1);
}

/****************************************************************************
  Returns the amount of gold it takes to rush this improvement.
****************************************************************************/
int impr_buy_gold_cost(Impr_type_id id, int shields_in_stock)
{
  int cost = 0;
  const int missing = impr_build_shield_cost(id) - shields_in_stock;

  if (improvement_has_flag(id, IF_GOLD)) {
    /* Can't buy capitalization. */
    return 0;
  }

  if (missing > 0) {
    cost = 2 * missing;
  }

  if (is_wonder(id)) {
    cost *= 2;
  }
  if (shields_in_stock == 0) {
    cost *= 2;
  }
  return cost;
}

/****************************************************************************
  Returns the amount of gold received when this improvement is sold.
****************************************************************************/
int impr_sell_gold(Impr_type_id id)
{
  return impr_build_shield_cost(id);
}

/**************************************************************************
...
**************************************************************************/
bool is_wonder(Impr_type_id id)
{
  return (is_great_wonder(id) || is_small_wonder(id));
}

/**************************************************************************
  Does a linear search of improvement_types[].name.translated
  Returns B_LAST if none match.
**************************************************************************/
Impr_type_id find_improvement_by_translated_name(const char *s)
{
  impr_type_iterate(i) {
    if (0 == strcmp(improvement_name_translation(i), s)) {
      return i;
    }
  } impr_type_iterate_end;

  return B_LAST;
}

/****************************************************************************
  Does a linear search of improvement_types[].name.vernacular
  Returns B_LAST if none match.
****************************************************************************/
Impr_type_id find_improvement_by_rule_name(const char *s)
{
  const char *qs = Qn_(s);

  impr_type_iterate(i) {
    if (0 == mystrcasecmp(improvement_rule_name(i), qs)) {
      return i;
    }
  } impr_type_iterate_end;

  return B_LAST;
}

/**************************************************************************
 Return TRUE if the impr has this flag otherwise FALSE
**************************************************************************/
bool improvement_has_flag(Impr_type_id id, enum impr_flag_id flag)
{
  assert(flag >= 0 && flag < IF_LAST);
  return TEST_BIT(improvement_by_number(id)->flags, flag);
}

/**************************************************************************
 Convert flag names to enum; case insensitive;
 returns IF_LAST if can't match.
**************************************************************************/
enum impr_flag_id find_improvement_flag_by_rule_name(const char *s)
{
  enum impr_flag_id i;

  assert(ARRAY_SIZE(flag_names) == IF_LAST);
  
  for(i = 0; i < IF_LAST; i++) {
    if (mystrcasecmp(flag_names[i], s) == 0) {
      return i;
    }
  }
  return IF_LAST;
}

/**************************************************************************
 Return TRUE if the improvement should be visible to others without spying
**************************************************************************/
bool is_improvement_visible(Impr_type_id id)
{
  return (is_wonder(id) || improvement_has_flag(id, IF_VISIBLE_BY_OTHERS));
}

/**************************************************************************
 Returns 1 if the improvement is obsolete, now also works for wonders
**************************************************************************/
bool improvement_obsolete(const struct player *pplayer, Impr_type_id id) 
{
  struct impr_type *impr = improvement_by_number(id);

  if (!tech_exists(impr->obsolete_by)) {
    return FALSE;
  }

  if (is_great_wonder(id)) {
    /* a great wonder is obsolete, as soon as *any* player researched the
       obsolete tech */
   return game.info.global_advances[impr->obsolete_by];
  }

  return (get_invention(pplayer, impr->obsolete_by) == TECH_KNOWN);
}

/**************************************************************************
   Whether player can build given building somewhere, ignoring whether it
   is obsolete.
**************************************************************************/
bool can_player_build_improvement_direct(const struct player *p,
					 Impr_type_id id)
{
  struct impr_type *impr;
  bool space_part = FALSE;

  /* This also checks if tech req is Never */
  if (!improvement_exists(id)) {
    return FALSE;
  }

  impr = improvement_by_number(id);

  requirement_vector_iterate(&impr->reqs, preq) {
    if (preq->range >= REQ_RANGE_PLAYER
        && !is_req_active(p, NULL, NULL, NULL, NULL, NULL, NULL, preq,
                          RPT_CERTAIN)) {
      return FALSE;
    }
  } requirement_vector_iterate_end;

  /* Check for space part construction.  This assumes that space parts have
   * no other effects. */
  if (building_has_effect(id, EFT_SS_STRUCTURAL)) {
    space_part = TRUE;
    if (p->spaceship.structurals >= NUM_SS_STRUCTURALS) {
      return FALSE;
    }
  }
  if (building_has_effect(id, EFT_SS_COMPONENT)) {
    space_part = TRUE;
    if (p->spaceship.components >= NUM_SS_COMPONENTS) {
      return FALSE;
    }
  }
  if (building_has_effect(id, EFT_SS_MODULE)) {
    space_part = TRUE;
    if (p->spaceship.modules >= NUM_SS_MODULES) {
      return FALSE;
    }
  }
  if (space_part &&
      (!get_player_bonus(p, EFT_ENABLE_SPACE) > 0
       || p->spaceship.state >= SSHIP_LAUNCHED)) {
    return FALSE;
  }

  if (is_great_wonder(id)) {
    /* Can't build wonder if already built */
    if (great_wonder_was_built(id)) {
      return FALSE;
    }
  }

  return TRUE;
}

/**************************************************************************
  Whether player can _eventually_ build given building somewhere -- i.e.,
  returns TRUE if building is available with current tech OR will be
  available with future tech.  Returns FALSE if building is obsolete.
**************************************************************************/
bool can_player_build_improvement(const struct player *p, Impr_type_id id)
{
  if (!can_player_build_improvement_direct(p, id)) {
    return FALSE;
  }
  if (improvement_obsolete(p, id)) {
    return FALSE;
  }
  return TRUE;
}

/**************************************************************************
  Whether player can _eventually_ build given building somewhere -- i.e.,
  returns TRUE if building is available with current tech OR will be
  available with future tech.  Returns FALSE if building is obsolete.
**************************************************************************/
bool can_player_eventually_build_improvement(const struct player *p,
					     Impr_type_id id)
{
  struct impr_type *building;

  if (!improvement_exists(id)) {
    return FALSE;
  }
  if (improvement_obsolete(p, id)) {
    return FALSE;
  }

  /* Check for requirements that aren't met and that are unchanging (so
   * they can never be met). */
  building = improvement_by_number(id);
  requirement_vector_iterate(&building->reqs, preq) {
    if (preq->range >= REQ_RANGE_PLAYER
	&& is_req_unchanging(preq)
        && !is_req_active(p, NULL, NULL, NULL, NULL, NULL, NULL, preq,
                          RPT_POSSIBLE)) {
      return FALSE;
    }
  } requirement_vector_iterate_end;
  /* FIXME: should check some "unchanging" reqs here - like if there's
   * a nation requirement, we can go ahead and check it now. */

  return TRUE;
}

/**************************************************************************
  Is this building a great wonder?
**************************************************************************/
bool is_great_wonder(Impr_type_id id)
{
  return (improvement_by_number(id)->genus == IG_GREAT_WONDER);
}

/**************************************************************************
  Is this building a small wonder?
**************************************************************************/
bool is_small_wonder(Impr_type_id id)
{
  return (improvement_by_number(id)->genus == IG_SMALL_WONDER);
}

/**************************************************************************
  Is this building a regular improvement?
**************************************************************************/
bool is_improvement(Impr_type_id id)
{
  return (improvement_by_number(id)->genus == IG_IMPROVEMENT);
}

/**************************************************************************
  Get the world city with this great wonder.
**************************************************************************/
struct city *find_city_from_great_wonder(Impr_type_id id)
{
  return (game_find_city_by_number(game.info.great_wonders[id]));
}

/**************************************************************************
  Get the player city with this small wonder.
**************************************************************************/
struct city *find_city_from_small_wonder(const struct player *pplayer,
					 Impr_type_id id)
{
  if (!pplayer) {
    return NULL; /* Used in some places in the client. */
  }
  return (player_find_city_by_id(pplayer, pplayer->small_wonders[id]));
}

/**************************************************************************
  Was this great wonder built?
**************************************************************************/
bool great_wonder_was_built(Impr_type_id id)
{
  return (game.info.great_wonders[id] != 0);
}

/**************************************************************************
  Return TRUE iff the improvement can be sold.
**************************************************************************/
bool can_sell_building(Impr_type_id id)
{
  return is_improvement(id);
}

/****************************************************************************
  Return TRUE iff the city can sell the given improvement.
****************************************************************************/
bool can_city_sell_building(struct city *pcity, Impr_type_id id)
{
  return (city_got_building(pcity, id) ? can_sell_building(id) : FALSE);
}