1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
15 #include <fc_config.h>
18 #include <stdlib.h> /* exit */
26 #include "mem.h" /* free */
27 #include "shared.h" /* ARRAY_SIZE */
28 #include "string_vector.h"
38 struct advance_req_iter
{
41 const struct advance
*array
[A_LAST
];
42 const struct advance
**current
, **end
;
44 #define ADVANCE_REQ_ITER(it) ((struct advance_req_iter *) it)
46 /* the advances array is now setup in:
47 * server/ruleset.c (for the server)
48 * client/packhand.c (for the client)
50 struct advance advances
[A_ARRAY_SIZE
];
52 struct tech_class tech_classes
[MAX_NUM_TECH_CLASSES
];
54 static struct user_flag user_tech_flags
[MAX_NUM_USER_TECH_FLAGS
];
56 /**************************************************************************
57 Return the last item of advances/technologies.
58 **************************************************************************/
59 const struct advance
*advance_array_last(void)
61 if (game
.control
.num_tech_types
> 0) {
62 return &advances
[game
.control
.num_tech_types
- 1];
67 /**************************************************************************
68 Return the number of advances/technologies.
69 **************************************************************************/
70 Tech_type_id
advance_count(void)
72 return game
.control
.num_tech_types
;
75 /**************************************************************************
76 Return the advance index.
78 Currently same as advance_number(), paired with advance_count()
79 indicates use as an array index.
80 **************************************************************************/
81 Tech_type_id
advance_index(const struct advance
*padvance
)
83 fc_assert_ret_val(NULL
!= padvance
, -1);
84 return padvance
- advances
;
87 /**************************************************************************
88 Return the advance index.
89 **************************************************************************/
90 Tech_type_id
advance_number(const struct advance
*padvance
)
92 fc_assert_ret_val(NULL
!= padvance
, -1);
93 return padvance
->item_number
;
96 /**************************************************************************
97 Return the advance for the given advance index.
98 **************************************************************************/
99 struct advance
*advance_by_number(const Tech_type_id atype
)
101 if (atype
!= A_FUTURE
102 && (atype
< 0 || atype
>= game
.control
.num_tech_types
)) {
103 /* This isn't an error; some callers depend on it. */
107 return &advances
[atype
];
110 /**************************************************************************
111 Accessor for requirements.
112 **************************************************************************/
113 Tech_type_id
advance_required(const Tech_type_id tech
,
114 enum tech_req require
)
116 fc_assert_ret_val(require
>= 0 && require
< AR_SIZE
, -1);
117 fc_assert_ret_val(tech
>= A_NONE
&& tech
< A_LAST
, -1);
118 if (A_NEVER
== advances
[tech
].require
[require
]) {
122 return advance_number(advances
[tech
].require
[require
]);
125 /**************************************************************************
126 Accessor for requirements.
127 **************************************************************************/
128 struct advance
*advance_requires(const struct advance
*padvance
,
129 enum tech_req require
)
131 fc_assert_ret_val(require
>= 0 && require
< AR_SIZE
, NULL
);
132 fc_assert_ret_val(NULL
!= padvance
, NULL
);
133 return padvance
->require
[require
];
136 /**************************************************************************
137 Returns pointer when the advance "exists" in this game, returns NULL
140 A tech doesn't exist if it has been flagged as removed by setting its
141 require values to A_NEVER. Note that this function returns NULL if either
142 of req values is A_NEVER, rather than both, to be on the safe side.
143 **************************************************************************/
144 struct advance
*valid_advance(struct advance
*padvance
)
147 || A_NEVER
== padvance
->require
[AR_ONE
]
148 || A_NEVER
== padvance
->require
[AR_TWO
]) {
155 /**************************************************************************
156 Returns pointer when the advance "exists" in this game,
157 returns NULL otherwise.
159 In addition to valid_advance(), tests for id is out of range.
160 **************************************************************************/
161 struct advance
*valid_advance_by_number(const Tech_type_id id
)
163 return valid_advance(advance_by_number(id
));
166 /**************************************************************************
167 Does a linear search of advances[].name.translated
168 Returns NULL when none match.
169 **************************************************************************/
170 struct advance
*advance_by_translated_name(const char *name
)
172 advance_iterate(A_NONE
, padvance
) {
173 if (0 == strcmp(advance_name_translation(padvance
), name
)) {
176 } advance_iterate_end
;
181 /**************************************************************************
182 Does a linear search of advances[].name.vernacular
183 Returns NULL when none match.
184 **************************************************************************/
185 struct advance
*advance_by_rule_name(const char *name
)
187 const char *qname
= Qn_(name
);
189 advance_iterate(A_NONE
, padvance
) {
190 if (0 == fc_strcasecmp(advance_rule_name(padvance
), qname
)) {
193 } advance_iterate_end
;
198 /**************************************************************************
199 Return TRUE if the tech has this flag otherwise FALSE
200 **************************************************************************/
201 bool advance_has_flag(Tech_type_id tech
, enum tech_flag_id flag
)
203 fc_assert_ret_val(tech_flag_id_is_valid(flag
), FALSE
);
204 return BV_ISSET(advance_by_number(tech
)->flags
, flag
);
207 /****************************************************************************
208 Function to precalculate needed data for technologies.
209 ****************************************************************************/
210 void techs_precalc_data(void)
212 fc_assert_msg(tech_cost_style_is_valid(game
.info
.tech_cost_style
),
213 "Invalid tech_cost_style %d", game
.info
.tech_cost_style
);
215 advance_iterate(A_FIRST
, padvance
) {
219 advance_req_iterate(padvance
, preq
) {
220 (void) preq
; /* Compiler wants us to do something with 'preq'. */
222 } advance_req_iterate_end
;
223 padvance
->num_reqs
= num_reqs
;
225 switch (game
.info
.tech_cost_style
) {
226 case TECH_COST_CIV1CIV2
:
227 padvance
->cost
= game
.info
.base_tech_cost
* num_reqs
;
229 case TECH_COST_CLASSIC_PRESET
:
230 if (-1 != padvance
->cost
) {
235 case TECH_COST_CLASSIC
:
236 padvance
->cost
= game
.info
.base_tech_cost
* (1.0 + num_reqs
)
237 * sqrt(1.0 + num_reqs
) / 2;
239 case TECH_COST_EXPERIMENTAL_PRESET
:
240 if (-1 != padvance
->cost
) {
245 case TECH_COST_EXPERIMENTAL
:
246 padvance
->cost
= game
.info
.base_tech_cost
* ((num_reqs
) * (num_reqs
)
247 / (1 + sqrt(sqrt(num_reqs
+ 1))) - 0.5);
251 if (min_req
&& padvance
->cost
< game
.info
.base_tech_cost
) {
252 padvance
->cost
= game
.info
.base_tech_cost
;
256 if (padvance
->tclass
!= NULL
) {
257 padvance
->cost
= padvance
->cost
* padvance
->tclass
->cost_pct
/ 100;
259 } advance_iterate_end
;
262 /**************************************************************************
263 Is the given tech a future tech.
264 **************************************************************************/
265 bool is_future_tech(Tech_type_id tech
)
267 return tech
== A_FUTURE
;
270 /**************************************************************************
271 Return the (translated) name of the given advance/technology.
272 You don't have to free the return pointer.
273 **************************************************************************/
274 const char *advance_name_translation(const struct advance
*padvance
)
276 return name_translation_get(&padvance
->name
);
279 /****************************************************************************
280 Return the (untranslated) rule name of the advance/technology.
281 You don't have to free the return pointer.
282 ****************************************************************************/
283 const char *advance_rule_name(const struct advance
*padvance
)
285 return rule_name_get(&padvance
->name
);
288 /**************************************************************************
289 Initialize tech classes
290 **************************************************************************/
291 void tech_classes_init(void)
295 for (i
= 0; i
< MAX_NUM_TECH_CLASSES
; i
++) {
296 tech_classes
[i
].idx
= i
;
297 tech_classes
[i
].disabled
= FALSE
;
301 /**************************************************************************
302 Return the tech_class for the given index.
303 **************************************************************************/
304 struct tech_class
*tech_class_by_number(const int idx
)
306 if (idx
< 0 || idx
>= game
.control
.num_tech_classes
) {
310 return &tech_classes
[idx
];
313 /**************************************************************************
314 Return the (translated) name of the given tech_class
315 You must not free the return pointer.
316 **************************************************************************/
317 const char *tech_class_name_translation(const struct tech_class
*ptclass
)
319 return name_translation_get(&ptclass
->name
);
322 /****************************************************************************
323 Return the (untranslated) rule name of tech_class
324 You must not free the return pointer.
325 ****************************************************************************/
326 const char *tech_class_rule_name(const struct tech_class
*ptclass
)
328 return rule_name_get(&ptclass
->name
);
331 /**************************************************************************
332 Does a linear search of tech_classes[].name.vernacular
333 Returns NULL when none match.
334 **************************************************************************/
335 struct tech_class
*tech_class_by_rule_name(const char *name
)
337 const char *qname
= Qn_(name
);
340 for (i
= 0; i
< game
.control
.num_tech_classes
; i
++) {
341 struct tech_class
*ptclass
= tech_class_by_number(i
);
343 if (!fc_strcasecmp(tech_class_rule_name(ptclass
), qname
)) {
351 /**************************************************************************
352 Initialize user tech flags.
353 **************************************************************************/
354 void user_tech_flags_init(void)
358 for (i
= 0; i
< MAX_NUM_USER_TECH_FLAGS
; i
++) {
359 user_flag_init(&user_tech_flags
[i
]);
363 /***************************************************************
364 Frees the memory associated with all user tech flags
365 ***************************************************************/
366 void user_tech_flags_free(void)
370 for (i
= 0; i
< MAX_NUM_USER_TECH_FLAGS
; i
++) {
371 user_flag_free(&user_tech_flags
[i
]);
375 /**************************************************************************
376 Sets user defined name for tech flag.
377 **************************************************************************/
378 void set_user_tech_flag_name(enum tech_flag_id id
, const char *name
,
381 int tfid
= id
- TECH_USER_1
;
383 fc_assert_ret(id
>= TECH_USER_1
&& id
<= TECH_USER_LAST
);
385 if (user_tech_flags
[tfid
].name
!= NULL
) {
386 FC_FREE(user_tech_flags
[tfid
].name
);
387 user_tech_flags
[tfid
].name
= NULL
;
390 if (name
&& name
[0] != '\0') {
391 user_tech_flags
[tfid
].name
= fc_strdup(name
);
394 if (user_tech_flags
[tfid
].helptxt
!= NULL
) {
395 FC_FREE(user_tech_flags
[tfid
].helptxt
);
396 user_tech_flags
[tfid
].helptxt
= NULL
;
399 if (helptxt
&& helptxt
[0] != '\0') {
400 user_tech_flags
[tfid
].helptxt
= fc_strdup(helptxt
);
404 /**************************************************************************
405 Tech flag name callback, called from specenum code.
406 **************************************************************************/
407 const char *tech_flag_id_name_cb(enum tech_flag_id flag
)
409 if (flag
< TECH_USER_1
|| flag
> TECH_USER_LAST
) {
413 return user_tech_flags
[flag
-TECH_USER_1
].name
;
416 /**************************************************************************
417 Return the (untranslated) helptxt of the user tech flag.
418 **************************************************************************/
419 const char *tech_flag_helptxt(enum tech_flag_id id
)
421 fc_assert(id
>= TECH_USER_1
&& id
<= TECH_USER_LAST
);
423 return user_tech_flags
[id
- TECH_USER_1
].helptxt
;
426 /**************************************************************************
427 Returns true if the costs for the given technology will stay constant
428 during the game. False otherwise.
430 Checking every tech_cost_style with fixed costs seems a waste of system
431 resources, when we can check that it is not the one style without fixed
433 **************************************************************************/
434 bool techs_have_fixed_costs()
436 return (game
.info
.tech_leakage
== TECH_LEAKAGE_NONE
437 && game
.info
.tech_cost_style
!= TECH_COST_CIV1CIV2
);
440 /****************************************************************************
441 Initialize tech structures.
442 ****************************************************************************/
443 void techs_init(void)
445 struct advance
*a_none
= &advances
[A_NONE
];
446 struct advance
*a_future
= &advances
[A_FUTURE
];
449 memset(advances
, 0, sizeof(advances
));
450 for (i
= 0; i
< ARRAY_SIZE(advances
); i
++) {
451 advances
[i
].item_number
= i
;
452 advances
[i
].cost
= -1;
453 advances
[i
].tclass
= 0;
455 requirement_vector_init(&(advances
[i
].research_reqs
));
458 /* Initialize dummy tech A_NONE */
459 /* TRANS: "None" tech */
460 name_set(&a_none
->name
, NULL
, N_("?tech:None"));
461 a_none
->require
[AR_ONE
] = a_none
;
462 a_none
->require
[AR_TWO
] = a_none
;
463 a_none
->require
[AR_ROOT
] = A_NEVER
;
465 name_set(&a_future
->name
, NULL
, "Future");
466 a_future
->require
[AR_ONE
] = A_NEVER
;
467 a_future
->require
[AR_TWO
] = A_NEVER
;
468 a_future
->require
[AR_ROOT
] = A_NEVER
;
471 /***************************************************************
472 De-allocate resources associated with the given tech.
473 ***************************************************************/
474 static void tech_free(Tech_type_id tech
)
476 struct advance
*p
= &advances
[tech
];
478 if (NULL
!= p
->helptext
) {
479 strvec_destroy(p
->helptext
);
483 if (p
->bonus_message
) {
484 free(p
->bonus_message
);
485 p
->bonus_message
= NULL
;
489 /***************************************************************
490 De-allocate resources of all techs.
491 ***************************************************************/
492 void techs_free(void)
496 advance_index_iterate(A_FIRST
, adv_idx
) {
498 } advance_index_iterate_end
;
500 for (i
= 0; i
< ARRAY_SIZE(advances
); i
++) {
501 requirement_vector_free(&(advances
[i
].research_reqs
));
505 /****************************************************************************
506 Return the size of the advance requirements iterator.
507 ****************************************************************************/
508 size_t advance_req_iter_sizeof(void)
510 return sizeof(struct advance_req_iter
);
513 /****************************************************************************
514 Return the current advance.
515 ****************************************************************************/
516 static void *advance_req_iter_get(const struct iterator
*it
)
518 return (void *) *ADVANCE_REQ_ITER(it
)->current
;
521 /****************************************************************************
522 Jump to next advance requirement.
523 ****************************************************************************/
524 static void advance_req_iter_next(struct iterator
*it
)
526 struct advance_req_iter
*iter
= ADVANCE_REQ_ITER(it
);
527 const struct advance
*padvance
= *iter
->current
, *preq
;
531 for (req
= AR_ONE
; req
< AR_SIZE
; req
++) {
532 preq
= valid_advance(advance_requires(padvance
, req
));
534 && A_NONE
!= advance_number(preq
)
535 && !BV_ISSET(iter
->done
, advance_number(preq
))) {
536 BV_SET(iter
->done
, advance_number(preq
));
540 *iter
->current
= preq
;
551 /****************************************************************************
552 Return whether we finished to iterate or not.
553 ****************************************************************************/
554 static bool advance_req_iter_valid(const struct iterator
*it
)
556 const struct advance_req_iter
*iter
= ADVANCE_REQ_ITER(it
);
558 return iter
->current
< iter
->end
;
561 /****************************************************************************
562 Initialize an advance requirements iterator.
563 ****************************************************************************/
564 struct iterator
*advance_req_iter_init(struct advance_req_iter
*it
,
565 const struct advance
*goal
)
567 struct iterator
*base
= ITERATOR(it
);
569 base
->get
= advance_req_iter_get
;
570 base
->next
= advance_req_iter_next
;
571 base
->valid
= advance_req_iter_valid
;
573 BV_CLR_ALL(it
->done
);
574 it
->current
= it
->array
;
576 it
->end
= it
->current
+ 1;