Use linear-gradient instead of gtk css extension in gtk3.22-client theme
[freeciv.git] / common / movement.c
blob0916bbdf6a221fc48f026c066be63f3b7da6eb6d
1 /****************************************************************************
2 Freeciv - Copyright (C) 2004 - The Freeciv Team
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)
6 any later version.
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 ****************************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 /* utility */
19 #include "bitvector.h"
20 #include "fcintl.h"
21 #include "log.h"
22 #include "shared.h"
23 #include "support.h"
25 /* common */
26 #include "astring.h"
27 #include "base.h"
28 #include "effects.h"
29 #include "fc_types.h"
30 #include "game.h"
31 #include "map.h"
32 #include "road.h"
33 #include "unit.h"
34 #include "unitlist.h"
35 #include "unittype.h"
36 #include "terrain.h"
38 #include "movement.h"
40 /****************************************************************************
41 This function calculates the move rate of the unit, taking into account
42 the penalty for reduced hitpoints, the active effects, and any veteran
43 bonuses.
45 'utype' and 'pplayer' must be set. 'ptile' can be NULL.
46 ****************************************************************************/
47 int utype_move_rate(const struct unit_type *utype, const struct tile *ptile,
48 const struct player *pplayer, int veteran_level,
49 int hitpoints)
51 const struct unit_class *uclass;
52 const struct veteran_level *vlevel;
53 int base_move_rate, move_rate;
55 fc_assert_ret_val(NULL != utype, 0);
56 fc_assert_ret_val(NULL != pplayer, 0);
57 vlevel = utype_veteran_level(utype, veteran_level);
58 fc_assert_ret_val(NULL != vlevel, 0);
59 uclass = utype_class(utype);
61 base_move_rate = utype->move_rate + vlevel->move_bonus;
62 move_rate = base_move_rate;
64 if (uclass_has_flag(uclass, UCF_DAMAGE_SLOWS)) {
65 /* Scale the MP based on how many HP the unit has. */
66 move_rate = (move_rate * hitpoints) / utype->hp;
69 /* Add on effects bonus (Magellan's Expedition, Lighthouse,
70 * Nuclear Power). */
71 move_rate += (get_unittype_bonus(pplayer, ptile, utype, EFT_MOVE_BONUS)
72 * SINGLE_MOVE);
74 /* Don't let the move_rate be less than min_speed unless the base_move_rate is
75 * also less than min_speed. */
76 if (move_rate < uclass->min_speed) {
77 move_rate = MIN(uclass->min_speed, base_move_rate);
80 return move_rate;
83 /****************************************************************************
84 This function calculates the move rate of the unit. See utype_move_rate()
85 for further details.
86 ****************************************************************************/
87 int unit_move_rate(const struct unit *punit)
89 fc_assert_ret_val(NULL != punit, 0);
91 return utype_move_rate(unit_type_get(punit), unit_tile(punit),
92 unit_owner(punit), punit->veteran, punit->hp);
95 /****************************************************************************
96 This function calculates the movement cost to unknown tiles. The base
97 value is equal to the highest movement cost the unit can encounter. If
98 the unit cannot enter all terrains, a malus is applied.
99 The returned value is usually cached into utype->unknown_move_cost and
100 used in the path-finding module.
101 ****************************************************************************/
102 int utype_unknown_move_cost(const struct unit_type *utype)
104 const struct unit_class *uclass = utype_class(utype);
105 int move_cost;
107 if (!uclass_has_flag(uclass, UCF_TERRAIN_SPEED)) {
108 /* Unit is not subject to terrain movement costs. */
109 move_cost = SINGLE_MOVE;
110 } else if (utype_has_flag(utype, UTYF_IGTER)) {
111 /* All terrain movement costs are equal! */
112 move_cost = MOVE_COST_IGTER;
113 } else {
114 /* Unit is subject to terrain movement costs. */
115 move_cost = 1; /* Arbitrary minimum. */
116 terrain_type_iterate(pterrain) {
117 if (is_native_to_class(uclass, pterrain, NULL)
118 && pterrain->movement_cost > move_cost) {
119 /* Exact movement cost matters only if we can enter
120 * the tile. */
121 move_cost = pterrain->movement_cost;
123 } terrain_type_iterate_end;
124 move_cost *= SINGLE_MOVE; /* Real value. */
127 /* Let's see if we can cross over all terrain types, else apply a malus.
128 * We want units that may encounter unsuitable terrain explore less.
129 * N.B.: We don't take in account terrain no unit can enter here. */
130 terrain_type_iterate(pterrain) {
131 if (BV_ISSET_ANY(pterrain->native_to)
132 && !is_native_to_class(uclass, pterrain, NULL)) {
133 /* Units that may encounter unsuitable terrain explore less. */
134 move_cost *= 2;
135 break;
137 } terrain_type_iterate_end;
139 return move_cost;
143 /****************************************************************************
144 Return TRUE iff the unit can be a defender at its current location. This
145 should be checked when looking for a defender - not all units on the
146 tile are valid defenders.
147 ****************************************************************************/
148 bool unit_can_defend_here(const struct unit *punit)
150 struct unit *ptrans = unit_transport_get(punit);
152 /* Do not just check if unit is transported.
153 * Even transported units may step out from transport to fight,
154 * if this is their native terrain. */
155 return (can_unit_exist_at_tile(punit, unit_tile(punit))
156 && (ptrans == NULL || can_unit_unload(punit, ptrans)));
159 /****************************************************************************
160 This unit can attack non-native tiles (eg. Ships ability to
161 shore bombardment)
162 ****************************************************************************/
163 bool can_attack_non_native(const struct unit_type *utype)
165 return uclass_has_flag(utype_class(utype), UCF_ATTACK_NON_NATIVE)
166 && utype->attack_strength > 0
167 && !utype_has_flag(utype, UTYF_ONLY_NATIVE_ATTACK);
170 /****************************************************************************
171 This unit can attack from non-native tiles (Marines can attack from
172 transport, ships from harbour cities)
173 ****************************************************************************/
174 bool can_attack_from_non_native(const struct unit_type *utype)
176 return uclass_has_flag(utype_class(utype), UCF_ATT_FROM_NON_NATIVE)
177 || utype_has_flag(utype, UTYF_MARINES);
180 /****************************************************************************
181 Check for a city channel.
182 ****************************************************************************/
183 bool is_city_channel_tile(const struct unit_class *punitclass,
184 const struct tile *ptile,
185 const struct tile *pexclude)
187 struct dbv tile_processed;
188 struct tile_list *process_queue = tile_list_new();
189 bool found = FALSE;
191 dbv_init(&tile_processed, map_num_tiles());
192 for (;;) {
193 dbv_set(&tile_processed, tile_index(ptile));
194 adjc_iterate(ptile, piter) {
195 if (dbv_isset(&tile_processed, tile_index(piter))) {
196 continue;
197 } else if (piter != pexclude
198 && is_native_to_class(punitclass, tile_terrain(piter),
199 tile_extras(piter))) {
200 found = TRUE;
201 break;
202 } else if (piter != pexclude
203 && NULL != tile_city(piter)) {
204 tile_list_append(process_queue, piter);
205 } else {
206 dbv_set(&tile_processed, tile_index(piter));
208 } adjc_iterate_end;
210 if (found || 0 == tile_list_size(process_queue)) {
211 break; /* No more tile to process. */
212 } else {
213 ptile = tile_list_front(process_queue);
214 tile_list_pop_front(process_queue);
218 dbv_free(&tile_processed);
219 tile_list_destroy(process_queue);
220 return found;
223 /****************************************************************************
224 Return TRUE iff a unit of the given unit type can "exist" at this location.
225 This means it can physically be present on the tile (without the use of a
226 transporter). See also can_unit_survive_at_tile.
227 ****************************************************************************/
228 bool can_exist_at_tile(const struct unit_type *utype,
229 const struct tile *ptile)
231 /* Cities are safe havens except for units in the middle of non-native
232 * terrain. This can happen if adjacent terrain is changed after unit
233 * arrived to city. */
234 if (NULL != tile_city(ptile)
235 && (uclass_has_flag(utype_class(utype), UCF_BUILD_ANYWHERE)
236 || is_native_near_tile(utype_class(utype), ptile)
237 || (1 == game.info.citymindist
238 && is_city_channel_tile(utype_class(utype), ptile, NULL)))) {
239 return TRUE;
242 /* A trireme unit cannot exist in an ocean tile without access to land. */
243 if (utype_has_flag(utype, UTYF_TRIREME) && !is_safe_ocean(ptile)) {
244 return FALSE;
247 return is_native_tile(utype, ptile);
250 /****************************************************************************
251 Return TRUE iff the unit can "exist" at this location. This means it can
252 physically be present on the tile (without the use of a transporter). See
253 also can_unit_survive_at_tile.
254 ****************************************************************************/
255 bool can_unit_exist_at_tile(const struct unit *punit,
256 const struct tile *ptile)
258 return can_exist_at_tile(unit_type_get(punit), ptile);
261 /****************************************************************************
262 This tile is native to unit.
264 See is_native_to_class()
265 ****************************************************************************/
266 bool is_native_tile(const struct unit_type *punittype,
267 const struct tile *ptile)
269 return is_native_to_class(utype_class(punittype), tile_terrain(ptile),
270 tile_extras(ptile));
273 /****************************************************************************
274 This terrain is native to unit class. Units that require fuel dont survive
275 even on native terrain.
276 ****************************************************************************/
277 bool is_native_to_class(const struct unit_class *punitclass,
278 const struct terrain *pterrain,
279 const bv_extras *extras)
281 if (!pterrain) {
282 /* Unknown is considered native terrain */
283 return TRUE;
286 if (BV_ISSET(pterrain->native_to, uclass_index(punitclass))) {
287 return TRUE;
290 if (extras != NULL) {
291 extra_type_list_iterate(punitclass->cache.native_tile_extras, pextra) {
292 if (BV_ISSET(*extras, extra_index(pextra))) {
293 return TRUE;
295 } extra_type_list_iterate_end;
298 return FALSE;
302 /****************************************************************************
303 Is the move under consideration a native move?
304 Note that this function does not check for possible moves, only native
305 moves, so that callers are responsible for checking for other sources of
306 legal moves (e.g. cities, transports, etc.).
307 ****************************************************************************/
308 bool is_native_move(const struct unit_class *punitclass,
309 const struct tile *src_tile,
310 const struct tile *dst_tile)
312 const struct road_type *proad;
314 if (is_native_to_class(punitclass, tile_terrain(dst_tile), NULL)) {
315 /* We aren't using extras to make the destination native. */
316 return TRUE;
317 } else if (!is_native_tile_to_class(punitclass, src_tile)) {
318 /* Disembarking or leaving port, so ignore road connectivity. */
319 return TRUE;
320 } else if (is_native_to_class(punitclass, tile_terrain(src_tile), NULL)) {
321 /* Native source terrain depends entirely on destination tile nativity. */
322 return is_native_tile_to_class(punitclass, dst_tile);
325 /* Check for non-road native extras on the source tile. */
326 extra_type_list_iterate(punitclass->cache.native_tile_extras, pextra) {
327 if (tile_has_extra(src_tile, pextra)
328 && !is_extra_caused_by(pextra, EC_ROAD)
329 && is_native_tile_to_class(punitclass, dst_tile)) {
330 /* If there is one, and the destination is native, the move is native. */
331 return TRUE;
333 } extra_type_list_iterate_end;
335 extra_type_list_iterate(punitclass->cache.native_tile_extras, pextra) {
336 if (!tile_has_extra(dst_tile, pextra)) {
337 continue;
338 } else if (!is_extra_caused_by(pextra, EC_ROAD)) {
339 /* The destination is native because of a non-road extra. */
340 return TRUE;
343 proad = extra_road_get(pextra);
345 if (road_has_flag(proad, RF_JUMP_TO)) {
346 extra_type_list_iterate(punitclass->cache.native_tile_extras, jextra) {
347 if (pextra != jextra
348 && is_extra_caused_by(jextra, EC_ROAD)
349 && tile_has_extra(src_tile, jextra)
350 && road_has_flag(jextra->data.road, RF_JUMP_FROM)) {
351 return TRUE;
353 } extra_type_list_iterate_end;
356 extra_type_list_iterate(proad->integrators, iextra) {
357 if (!tile_has_extra(src_tile, iextra)) {
358 continue;
360 if (ALL_DIRECTIONS_CARDINAL()) {
361 /* move_mode does not matter as all of them accept cardinal move */
362 return TRUE;
364 switch (extra_road_get(iextra)->move_mode) {
365 case RMM_FAST_ALWAYS:
366 /* Road connects source and destination, so we're fine. */
367 return TRUE;
368 case RMM_CARDINAL:
369 /* Road connects source and destination if cardinal move. */
370 if (is_move_cardinal(src_tile, dst_tile)) {
371 return TRUE;
373 break;
374 case RMM_RELAXED:
375 if (is_move_cardinal(src_tile, dst_tile)) {
376 /* Cardinal moves have no between tiles, so connected. */
377 return TRUE;
379 cardinal_between_iterate(src_tile, dst_tile, between) {
380 if (tile_has_extra(between, iextra)
381 || (pextra != iextra && tile_has_extra(between, pextra))) {
382 /* We have a link for the connection.
383 * 'pextra != iextra' is there just to avoid tile_has_extra()
384 * in by far more common case that 'pextra == iextra' */
385 return TRUE;
387 } cardinal_between_iterate_end;
388 break;
390 } extra_type_list_iterate_end;
391 } extra_type_list_iterate_end;
393 return FALSE;
396 /****************************************************************************
397 Is there native tile adjacent to given tile
398 ****************************************************************************/
399 bool is_native_near_tile(const struct unit_class *uclass, const struct tile *ptile)
401 if (is_native_tile_to_class(uclass, ptile)) {
402 return TRUE;
405 adjc_iterate(ptile, ptile2) {
406 if (is_native_tile_to_class(uclass, ptile2)) {
407 return TRUE;
409 } adjc_iterate_end;
411 return FALSE;
414 /****************************************************************************
415 Return TRUE iff the unit can "survive" at this location. This means it can
416 not only be physically present at the tile but will be able to survive
417 indefinitely on its own (without a transporter). Units that require fuel
418 or have a danger of drowning are examples of non-survivable units. See
419 also can_unit_exist_at_tile.
421 (This function could be renamed as unit_wants_transporter.)
422 ****************************************************************************/
423 bool can_unit_survive_at_tile(const struct unit *punit,
424 const struct tile *ptile)
426 if (!can_unit_exist_at_tile(punit, ptile)) {
427 return FALSE;
430 if (tile_city(ptile)) {
431 return TRUE;
434 if (tile_has_refuel_extra(ptile, unit_type_get(punit))) {
435 /* Unit can always survive at refueling base */
436 return TRUE;
439 if (utype_fuel(unit_type_get(punit))) {
440 /* Unit requires fuel and this is not refueling tile */
441 return FALSE;
444 if (is_losing_hp(punit)) {
445 /* Unit is losing HP over time in this tile (no city or native base) */
446 return FALSE;
449 return TRUE;
453 /****************************************************************************
454 Returns whether the unit is allowed (by ZOC) to move from src_tile
455 to dest_tile (assumed adjacent).
457 You CAN move if:
458 1. You have units there already
459 2. Your unit isn't a ground unit
460 3. Your unit ignores ZOC (diplomat, freight, etc.)
461 4. You're moving from or to a city
462 5. You're moving from an ocean square (from a boat)
463 6. The spot you're moving from or to is in your ZOC
464 ****************************************************************************/
465 bool can_step_taken_wrt_to_zoc(const struct unit_type *punittype,
466 const struct player *unit_owner,
467 const struct tile *src_tile,
468 const struct tile *dst_tile)
470 if (unit_type_really_ignores_zoc(punittype)) {
471 return TRUE;
473 if (is_allied_unit_tile(dst_tile, unit_owner)) {
474 return TRUE;
476 if (tile_city(src_tile) || tile_city(dst_tile)) {
477 return TRUE;
479 if (terrain_has_flag(tile_terrain(src_tile), TER_NO_ZOC)
480 || terrain_has_flag(tile_terrain(dst_tile), TER_NO_ZOC)) {
481 return TRUE;
484 return (is_my_zoc(unit_owner, src_tile)
485 || is_my_zoc(unit_owner, dst_tile));
489 /****************************************************************************
490 See can_step_take_wrt_to_zoc(). This function is exactly the same but
491 it takes a unit instead of a unittype and player.
492 ****************************************************************************/
493 static bool zoc_ok_move_gen(const struct unit *punit,
494 const struct tile *src_tile,
495 const struct tile *dst_tile)
497 return can_step_taken_wrt_to_zoc(unit_type_get(punit), unit_owner(punit),
498 src_tile, dst_tile);
502 /****************************************************************************
503 Returns whether the unit can safely move from its current position to
504 the adjacent dst_tile. This function checks only ZOC.
506 See can_step_taken_wrt_to_zoc().
507 ****************************************************************************/
508 bool zoc_ok_move(const struct unit *punit, const struct tile *dst_tile)
510 return zoc_ok_move_gen(punit, unit_tile(punit), dst_tile);
514 /****************************************************************************
515 Returns whether the unit can move from its current tile to the destination
516 tile.
518 See unit_move_to_tile_test().
519 ****************************************************************************/
520 bool unit_can_move_to_tile(const struct unit *punit,
521 const struct tile *dst_tile,
522 bool igzoc)
524 return (MR_OK == unit_move_to_tile_test(punit,
525 punit->activity, unit_tile(punit),
526 dst_tile, igzoc, NULL));
529 /**************************************************************************
530 Returns whether the unit can move from its current tile to the
531 destination tile. An enumerated value is returned indication the error
532 or success status.
534 The unit can move if:
535 1) The unit is idle or on server goto.
536 2) The target location is next to the unit.
537 3) There are no non-allied units on the target tile.
538 4) Animals cannot move out from home terrains
539 5) Unit can move to a tile where it can't survive on its own if there
540 is free transport capacity.
541 6) Some units cannot take over a city.
542 7) Only units permitted to attack from non-native tiles may do so.
543 8) There are no peaceful but non allied units on the target tile.
544 9) There is not a peaceful but non allied city on the target tile.
545 10) There is no non-allied unit blocking (zoc) [or igzoc is true].
546 11) Triremes cannot move out of sight from land.
547 12) It is not the territory of a player we are at peace with.
548 13) The unit is unable to disembark from current transporter.
549 14) The unit is making a non-native move (e.g. lack of road)
550 **************************************************************************/
551 enum unit_move_result
552 unit_move_to_tile_test(const struct unit *punit,
553 enum unit_activity activity,
554 const struct tile *src_tile,
555 const struct tile *dst_tile, bool igzoc,
556 struct unit *embark_to)
558 bool zoc;
559 struct city *pcity;
560 const struct unit_type *punittype = unit_type_get(punit);
561 const struct player *puowner = unit_owner(punit);
563 /* 1) */
564 if (activity != ACTIVITY_IDLE
565 && activity != ACTIVITY_GOTO) {
566 /* For other activities the unit must be stationary. */
567 return MR_BAD_ACTIVITY;
570 /* 2) */
571 if (!is_tiles_adjacent(src_tile, dst_tile)) {
572 /* Of course you can only move to adjacent positions. */
573 return MR_BAD_DESTINATION;
576 /* 3) */
577 if (is_non_allied_unit_tile(dst_tile, puowner)) {
578 /* You can't move onto a tile with non-allied units on it (try
579 * attacking instead). */
580 return MR_DESTINATION_OCCUPIED_BY_NON_ALLIED_UNIT;
583 /* 4) */
584 if (puowner->ai_common.barbarian_type == ANIMAL_BARBARIAN
585 && dst_tile->terrain->animal != punittype) {
586 return MR_ANIMAL_DISALLOWED;
589 /* 5) */
590 if (embark_to != NULL) {
591 if (!could_unit_load(punit, embark_to)) {
592 return MR_NO_TRANSPORTER_CAPACITY;
594 } else if (!(can_exist_at_tile(punittype, dst_tile)
595 || unit_could_load_at(punit, dst_tile))) {
596 return MR_NO_TRANSPORTER_CAPACITY;
599 pcity = is_enemy_city_tile(dst_tile, puowner);
600 if (NULL != pcity) {
601 /* 6) */
602 if (!unit_can_take_over(punit)) {
603 return MR_BAD_TYPE_FOR_CITY_TAKE_OVER;
604 } else {
605 /* No point checking for being able to take over from non-native
606 * for units that can't take over a city anyway. */
608 /* 7) */
609 if (!can_exist_at_tile(punittype, src_tile)
610 && !can_attack_from_non_native(punittype)) {
611 /* Don't use is_native_tile() because any unit in an
612 * adjacent city may conquer, regardless of flags. */
613 return MR_BAD_TYPE_FOR_CITY_TAKE_OVER_FROM_NON_NATIVE;
618 /* 8) */
619 if (is_non_attack_unit_tile(dst_tile, puowner)) {
620 /* You can't move into a non-allied tile.
622 * FIXME: this should never happen since it should be caught by check
623 * #3. */
624 return MR_NO_WAR;
627 /* 9) */
628 pcity = tile_city(dst_tile);
629 if (pcity && pplayers_non_attack(city_owner(pcity), puowner)) {
630 /* You can't move into an empty city of a civilization you're at
631 * peace with - you must first either declare war or make alliance. */
632 return MR_NO_WAR;
635 /* 10) */
636 zoc = igzoc
637 || can_step_taken_wrt_to_zoc(punittype, puowner, src_tile, dst_tile);
638 if (!zoc) {
639 /* The move is illegal because of zones of control. */
640 return MR_ZOC;
643 /* 11) */
644 if (utype_has_flag(punittype, UTYF_TRIREME) && !is_safe_ocean(dst_tile)) {
645 return MR_TRIREME;
648 /* 12) */
649 if (!utype_has_flag(punittype, UTYF_CIVILIAN)
650 && !player_can_invade_tile(puowner, dst_tile)) {
651 return MR_PEACE;
654 /* 13) */
655 if (unit_transported(punit)
656 && !can_unit_unload(punit, unit_transport_get(punit))) {
657 return MR_CANNOT_DISEMBARK;
660 /* 14) */
661 if (!(is_native_move(utype_class(punittype), src_tile, dst_tile)
662 /* Allow non-native moves into cities or boarding transport. */
663 || pcity
664 || unit_could_load_at(punit, dst_tile))) {
665 return MR_NON_NATIVE_MOVE;
668 return MR_OK;
671 /**************************************************************************
672 Return true iff transporter has ability to transport transported.
673 **************************************************************************/
674 bool can_unit_transport(const struct unit *transporter,
675 const struct unit *transported)
677 fc_assert_ret_val(transporter != NULL, FALSE);
678 fc_assert_ret_val(transported != NULL, FALSE);
680 return can_unit_type_transport(unit_type_get(transporter),
681 unit_class_get(transported));
684 /**************************************************************************
685 Return TRUE iff transporter type has ability to transport transported class.
686 **************************************************************************/
687 bool can_unit_type_transport(const struct unit_type *transporter,
688 const struct unit_class *transported)
690 if (transporter->transport_capacity <= 0) {
691 return FALSE;
694 return BV_ISSET(transporter->cargo, uclass_index(transported));
697 /****************************************************************************
698 Return whether we can find a suitable transporter for given unit at
699 'ptile'. It needs to have free space. To find the best transporter, see
700 transporter_for_unit().
701 ****************************************************************************/
702 bool unit_can_load(const struct unit *punit)
704 unit_list_iterate(unit_tile(punit)->units, ptransport) {
705 if (can_unit_load(punit, ptransport)) {
706 return TRUE;
708 } unit_list_iterate_end;
710 return FALSE;
713 /****************************************************************************
714 Return whether we could find a suitable transporter for given unit at
715 'ptile'. It needs to have free space. To find the best transporter, see
716 transporter_for_unit_at().
717 ****************************************************************************/
718 bool unit_could_load_at(const struct unit *punit, const struct tile *ptile)
720 unit_list_iterate(ptile->units, ptransport) {
721 if (could_unit_load(punit, ptransport)) {
722 return TRUE;
724 } unit_list_iterate_end;
726 return FALSE;
729 static int move_points_denomlen = 0;
731 /****************************************************************************
732 Call whenever terrain_control.move_fragments / SINGLE_MOVE changes.
733 ****************************************************************************/
734 void init_move_fragments(void)
736 char denomstr[10];
737 /* String length of maximum denominator for fractional representation of
738 * movement points, for padding of text representation */
739 fc_snprintf(denomstr, sizeof(denomstr), "%d", SINGLE_MOVE);
740 move_points_denomlen = strlen(denomstr);
743 /****************************************************************************
744 Render positive movement points as text, including fractional movement
745 points, scaled by SINGLE_MOVE. Returns a pointer to a static buffer.
746 'reduce' is whether fractional movement points should be reduced to
747 lowest terms (this might be confusing in some cases).
748 'prefix' is a string put in front of all numeric output.
749 'none' is the string to display in place of the integer part if no
750 movement points (or NULL to just say 0).
751 'align' controls whether this is for a fixed-width table, in which case
752 padding spaces will be included to make all such strings line up when
753 right-aligned.
754 ****************************************************************************/
755 const char *move_points_text_full(int mp, bool reduce, const char *prefix,
756 const char *none, bool align)
758 static struct astring str = ASTRING_INIT;
759 int pad1, pad2;
761 if (align && SINGLE_MOVE > 1) {
762 /* Align to worst-case denominator even if we might be reducing to
763 * lowest terms, as other entries in a table might not reduce */
764 pad1 = move_points_denomlen; /* numerator or denominator */
765 pad2 = move_points_denomlen*2+2; /* everything right of integer part */
766 } else {
767 /* If no possible fractional part, alignment unneeded even if requested */
768 pad1 = pad2 = 0;
770 if (!prefix) {
771 prefix = "";
773 astr_clear(&str);
774 if ((mp == 0 && none) || SINGLE_MOVE == 0) {
775 /* No movement points, and we have a special representation to use */
776 /* (Also used when SINGLE_MOVE==0, to avoid dividing by zero, which is
777 * important for client before ruleset has been received. Doesn't much
778 * matter what we print in this case.) */
779 astr_add(&str, "%s%*s", none ? none : "", pad2, "");
780 } else if ((mp % SINGLE_MOVE) == 0) {
781 /* Integer move points */
782 astr_add(&str, "%s%d%*s", prefix, mp / SINGLE_MOVE, pad2, "");
783 } else {
784 /* Fractional part */
785 int cancel;
787 fc_assert(SINGLE_MOVE > 1);
788 if (reduce) {
789 /* Reduce to lowest terms */
790 int gcd = mp;
791 /* Calculate greatest common divisor with Euclid's algorithm */
792 int b = SINGLE_MOVE;
794 while (b != 0) {
795 int t = b;
796 b = gcd % b;
797 gcd = t;
799 cancel = gcd;
800 } else {
801 /* No cancellation */
802 cancel = 1;
804 if (mp < SINGLE_MOVE) {
805 /* Fractional move points */
806 astr_add(&str, "%s%*d/%*d", prefix,
807 pad1, (mp % SINGLE_MOVE) / cancel, pad1, SINGLE_MOVE / cancel);
808 } else {
809 /* Integer + fractional move points */
810 astr_add(&str,
811 "%s%d %*d/%*d", prefix, mp / SINGLE_MOVE,
812 pad1, (mp % SINGLE_MOVE) / cancel, pad1, SINGLE_MOVE / cancel);
815 return astr_str(&str);
818 /****************************************************************************
819 Simple version of move_points_text_full() -- render positive movement
820 points as text without any prefix or alignment.
821 ****************************************************************************/
822 const char *move_points_text(int mp, bool reduce)
824 return move_points_text_full(mp, reduce, NULL, NULL, FALSE);