Stop sharing requirement_unit_state_ereq().
[freeciv.git] / common / movement.c
blob116500078d297e67074e6247919f6d2cbbb92933
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 (utype_can_do_act_when_ustate(utype, ACTION_ATTACK,
177 USP_NATIVE_TILE, FALSE)
178 || utype_can_do_act_when_ustate(utype, ACTION_CONQUER_CITY,
179 USP_LIVABLE_TILE, FALSE));
182 /****************************************************************************
183 Check for a city channel.
184 ****************************************************************************/
185 bool is_city_channel_tile(const struct unit_class *punitclass,
186 const struct tile *ptile,
187 const struct tile *pexclude)
189 struct dbv tile_processed;
190 struct tile_list *process_queue = tile_list_new();
191 bool found = FALSE;
193 dbv_init(&tile_processed, map_num_tiles());
194 for (;;) {
195 dbv_set(&tile_processed, tile_index(ptile));
196 adjc_iterate(ptile, piter) {
197 if (dbv_isset(&tile_processed, tile_index(piter))) {
198 continue;
199 } else if (piter != pexclude
200 && is_native_to_class(punitclass, tile_terrain(piter),
201 tile_extras(piter))) {
202 found = TRUE;
203 break;
204 } else if (piter != pexclude
205 && NULL != tile_city(piter)) {
206 tile_list_append(process_queue, piter);
207 } else {
208 dbv_set(&tile_processed, tile_index(piter));
210 } adjc_iterate_end;
212 if (found || 0 == tile_list_size(process_queue)) {
213 break; /* No more tile to process. */
214 } else {
215 ptile = tile_list_front(process_queue);
216 tile_list_pop_front(process_queue);
220 dbv_free(&tile_processed);
221 tile_list_destroy(process_queue);
222 return found;
225 /****************************************************************************
226 Return TRUE iff a unit of the given unit type can "exist" at this location.
227 This means it can physically be present on the tile (without the use of a
228 transporter). See also can_unit_survive_at_tile.
229 ****************************************************************************/
230 bool can_exist_at_tile(const struct unit_type *utype,
231 const struct tile *ptile)
233 /* Cities are safe havens except for units in the middle of non-native
234 * terrain. This can happen if adjacent terrain is changed after unit
235 * arrived to city. */
236 if (NULL != tile_city(ptile)
237 && (uclass_has_flag(utype_class(utype), UCF_BUILD_ANYWHERE)
238 || is_native_near_tile(utype_class(utype), ptile)
239 || (1 == game.info.citymindist
240 && is_city_channel_tile(utype_class(utype), ptile, NULL)))) {
241 return TRUE;
244 /* UTYF_COAST_STRICT unit cannot exist in an ocean tile without access to land. */
245 if (utype_has_flag(utype, UTYF_COAST_STRICT) && !is_safe_ocean(ptile)) {
246 return FALSE;
249 return is_native_tile(utype, ptile);
252 /****************************************************************************
253 Return TRUE iff the unit can "exist" at this location. This means it can
254 physically be present on the tile (without the use of a transporter). See
255 also can_unit_survive_at_tile.
256 ****************************************************************************/
257 bool can_unit_exist_at_tile(const struct unit *punit,
258 const struct tile *ptile)
260 return can_exist_at_tile(unit_type_get(punit), ptile);
263 /****************************************************************************
264 This tile is native to unit.
266 See is_native_to_class()
267 ****************************************************************************/
268 bool is_native_tile(const struct unit_type *punittype,
269 const struct tile *ptile)
271 return is_native_to_class(utype_class(punittype), tile_terrain(ptile),
272 tile_extras(ptile));
275 /****************************************************************************
276 This terrain is native to unit class. Units that require fuel dont survive
277 even on native terrain.
278 ****************************************************************************/
279 bool is_native_to_class(const struct unit_class *punitclass,
280 const struct terrain *pterrain,
281 const bv_extras *extras)
283 if (!pterrain) {
284 /* Unknown is considered native terrain */
285 return TRUE;
288 if (BV_ISSET(pterrain->native_to, uclass_index(punitclass))) {
289 return TRUE;
292 if (extras != NULL) {
293 extra_type_list_iterate(punitclass->cache.native_tile_extras, pextra) {
294 if (BV_ISSET(*extras, extra_index(pextra))) {
295 return TRUE;
297 } extra_type_list_iterate_end;
300 return FALSE;
304 /****************************************************************************
305 Is the move under consideration a native move?
306 Note that this function does not check for possible moves, only native
307 moves, so that callers are responsible for checking for other sources of
308 legal moves (e.g. cities, transports, etc.).
309 ****************************************************************************/
310 bool is_native_move(const struct unit_class *punitclass,
311 const struct tile *src_tile,
312 const struct tile *dst_tile)
314 const struct road_type *proad;
316 if (is_native_to_class(punitclass, tile_terrain(dst_tile), NULL)) {
317 /* We aren't using extras to make the destination native. */
318 return TRUE;
319 } else if (!is_native_tile_to_class(punitclass, src_tile)) {
320 /* Disembarking or leaving port, so ignore road connectivity. */
321 return TRUE;
322 } else if (is_native_to_class(punitclass, tile_terrain(src_tile), NULL)) {
323 /* Native source terrain depends entirely on destination tile nativity. */
324 return is_native_tile_to_class(punitclass, dst_tile);
327 /* Check for non-road native extras on the source tile. */
328 extra_type_list_iterate(punitclass->cache.native_tile_extras, pextra) {
329 if (tile_has_extra(src_tile, pextra)
330 && !is_extra_caused_by(pextra, EC_ROAD)
331 && is_native_tile_to_class(punitclass, dst_tile)) {
332 /* If there is one, and the destination is native, the move is native. */
333 return TRUE;
335 } extra_type_list_iterate_end;
337 extra_type_list_iterate(punitclass->cache.native_tile_extras, pextra) {
338 if (!tile_has_extra(dst_tile, pextra)) {
339 continue;
340 } else if (!is_extra_caused_by(pextra, EC_ROAD)) {
341 /* The destination is native because of a non-road extra. */
342 return TRUE;
345 proad = extra_road_get(pextra);
347 if (road_has_flag(proad, RF_JUMP_TO)) {
348 extra_type_list_iterate(punitclass->cache.native_tile_extras, jextra) {
349 if (pextra != jextra
350 && is_extra_caused_by(jextra, EC_ROAD)
351 && tile_has_extra(src_tile, jextra)
352 && road_has_flag(jextra->data.road, RF_JUMP_FROM)) {
353 return TRUE;
355 } extra_type_list_iterate_end;
358 extra_type_list_iterate(proad->integrators, iextra) {
359 if (!tile_has_extra(src_tile, iextra)) {
360 continue;
362 if (ALL_DIRECTIONS_CARDINAL()) {
363 /* move_mode does not matter as all of them accept cardinal move */
364 return TRUE;
366 switch (extra_road_get(iextra)->move_mode) {
367 case RMM_FAST_ALWAYS:
368 /* Road connects source and destination, so we're fine. */
369 return TRUE;
370 case RMM_CARDINAL:
371 /* Road connects source and destination if cardinal move. */
372 if (is_move_cardinal(src_tile, dst_tile)) {
373 return TRUE;
375 break;
376 case RMM_RELAXED:
377 if (is_move_cardinal(src_tile, dst_tile)) {
378 /* Cardinal moves have no between tiles, so connected. */
379 return TRUE;
381 cardinal_between_iterate(src_tile, dst_tile, between) {
382 if (tile_has_extra(between, iextra)
383 || (pextra != iextra && tile_has_extra(between, pextra))) {
384 /* We have a link for the connection.
385 * 'pextra != iextra' is there just to avoid tile_has_extra()
386 * in by far more common case that 'pextra == iextra' */
387 return TRUE;
389 } cardinal_between_iterate_end;
390 break;
392 } extra_type_list_iterate_end;
393 } extra_type_list_iterate_end;
395 return FALSE;
398 /****************************************************************************
399 Is there native tile adjacent to given tile
400 ****************************************************************************/
401 bool is_native_near_tile(const struct unit_class *uclass, const struct tile *ptile)
403 if (is_native_tile_to_class(uclass, ptile)) {
404 return TRUE;
407 adjc_iterate(ptile, ptile2) {
408 if (is_native_tile_to_class(uclass, ptile2)) {
409 return TRUE;
411 } adjc_iterate_end;
413 return FALSE;
416 /****************************************************************************
417 Return TRUE iff the unit can "survive" at this location. This means it can
418 not only be physically present at the tile but will be able to survive
419 indefinitely on its own (without a transporter). Units that require fuel
420 or have a danger of drowning are examples of non-survivable units. See
421 also can_unit_exist_at_tile.
423 (This function could be renamed as unit_wants_transporter.)
424 ****************************************************************************/
425 bool can_unit_survive_at_tile(const struct unit *punit,
426 const struct tile *ptile)
428 if (!can_unit_exist_at_tile(punit, ptile)) {
429 return FALSE;
432 if (tile_city(ptile)) {
433 return TRUE;
436 if (tile_has_refuel_extra(ptile, unit_type_get(punit))) {
437 /* Unit can always survive at refueling base */
438 return TRUE;
441 if (utype_fuel(unit_type_get(punit))) {
442 /* Unit requires fuel and this is not refueling tile */
443 return FALSE;
446 if (is_losing_hp(punit)) {
447 /* Unit is losing HP over time in this tile (no city or native base) */
448 return FALSE;
451 return TRUE;
455 /****************************************************************************
456 Returns whether the unit is allowed (by ZOC) to move from src_tile
457 to dest_tile (assumed adjacent).
459 You CAN move if:
460 1. You have units there already
461 2. Your unit isn't a ground unit
462 3. Your unit ignores ZOC (diplomat, freight, etc.)
463 4. You're moving from or to a city
464 5. You're moving from an ocean square (from a boat)
465 6. The spot you're moving from or to is in your ZOC
466 ****************************************************************************/
467 bool can_step_taken_wrt_to_zoc(const struct unit_type *punittype,
468 const struct player *unit_owner,
469 const struct tile *src_tile,
470 const struct tile *dst_tile)
472 if (unit_type_really_ignores_zoc(punittype)) {
473 return TRUE;
475 if (is_allied_unit_tile(dst_tile, unit_owner)) {
476 return TRUE;
478 if (tile_city(src_tile) || tile_city(dst_tile)) {
479 return TRUE;
481 if (terrain_has_flag(tile_terrain(src_tile), TER_NO_ZOC)
482 || terrain_has_flag(tile_terrain(dst_tile), TER_NO_ZOC)) {
483 return TRUE;
486 return (is_my_zoc(unit_owner, src_tile)
487 || is_my_zoc(unit_owner, dst_tile));
491 /****************************************************************************
492 See can_step_take_wrt_to_zoc(). This function is exactly the same but
493 it takes a unit instead of a unittype and player.
494 ****************************************************************************/
495 static bool zoc_ok_move_gen(const struct unit *punit,
496 const struct tile *src_tile,
497 const struct tile *dst_tile)
499 return can_step_taken_wrt_to_zoc(unit_type_get(punit), unit_owner(punit),
500 src_tile, dst_tile);
504 /****************************************************************************
505 Returns whether the unit can safely move from its current position to
506 the adjacent dst_tile. This function checks only ZOC.
508 See can_step_taken_wrt_to_zoc().
509 ****************************************************************************/
510 bool zoc_ok_move(const struct unit *punit, const struct tile *dst_tile)
512 return zoc_ok_move_gen(punit, unit_tile(punit), dst_tile);
516 /****************************************************************************
517 Returns whether the unit can move from its current tile to the destination
518 tile.
520 See unit_move_to_tile_test().
521 ****************************************************************************/
522 bool unit_can_move_to_tile(const struct unit *punit,
523 const struct tile *dst_tile,
524 bool igzoc,
525 bool enter_enemy_city)
527 return (MR_OK == unit_move_to_tile_test(punit,
528 punit->activity, unit_tile(punit),
529 dst_tile, igzoc, NULL,
530 enter_enemy_city));
533 /**************************************************************************
534 Returns whether the unit can move from its current tile to the
535 destination tile. An enumerated value is returned indication the error
536 or success status.
538 The unit can move if:
539 1) The unit is idle or on server goto.
540 2) The target location is next to the unit.
541 3) There are no non-allied units on the target tile.
542 4) Animals cannot move out from home terrains
543 5) Unit can move to a tile where it can't survive on its own if there
544 is free transport capacity.
545 6) There are no peaceful but non allied units on the target tile.
546 7) There is not a non allied city on the target tile when
547 enter_enemy_city is false. When enter_enemy_city is true a non
548 peaceful city is also accepted.
549 8) There is no non-allied unit blocking (zoc) [or igzoc is true].
550 9) Triremes cannot move out of sight from land.
551 10) It is not the territory of a player we are at peace with.
552 11) The unit is unable to disembark from current transporter.
553 12) The unit is making a non-native move (e.g. lack of road)
554 **************************************************************************/
555 enum unit_move_result
556 unit_move_to_tile_test(const struct unit *punit,
557 enum unit_activity activity,
558 const struct tile *src_tile,
559 const struct tile *dst_tile, bool igzoc,
560 struct unit *embark_to,
561 bool enter_enemy_city)
563 bool zoc;
564 struct city *pcity;
565 const struct unit_type *punittype = unit_type_get(punit);
566 const struct player *puowner = unit_owner(punit);
568 /* 1) */
569 if (activity != ACTIVITY_IDLE
570 && activity != ACTIVITY_GOTO) {
571 /* For other activities the unit must be stationary. */
572 return MR_BAD_ACTIVITY;
575 /* 2) */
576 if (!is_tiles_adjacent(src_tile, dst_tile)) {
577 /* Of course you can only move to adjacent positions. */
578 return MR_BAD_DESTINATION;
581 /* 3) */
582 if (is_non_allied_unit_tile(dst_tile, puowner)) {
583 /* You can't move onto a tile with non-allied units on it (try
584 * attacking instead). */
585 return MR_DESTINATION_OCCUPIED_BY_NON_ALLIED_UNIT;
588 /* 4) */
589 if (puowner->ai_common.barbarian_type == ANIMAL_BARBARIAN
590 && dst_tile->terrain->animal != punittype) {
591 return MR_ANIMAL_DISALLOWED;
594 /* 5) */
595 if (embark_to != NULL) {
596 if (!could_unit_load(punit, embark_to)) {
597 return MR_NO_TRANSPORTER_CAPACITY;
599 } else if (!(can_exist_at_tile(punittype, dst_tile)
600 || unit_could_load_at(punit, dst_tile))) {
601 return MR_NO_TRANSPORTER_CAPACITY;
604 /* 6) */
605 if (is_non_attack_unit_tile(dst_tile, puowner)) {
606 /* You can't move into a non-allied tile.
608 * FIXME: this should never happen since it should be caught by check
609 * #3. */
610 return MR_NO_WAR;
613 /* 7) */
614 if ((pcity = tile_city(dst_tile))) {
615 if (enter_enemy_city) {
616 if (pplayers_non_attack(city_owner(pcity), puowner)) {
617 /* You can't move into an empty city of a civilization you're at
618 * peace with - you must first either declare war or make an
619 * alliance. */
620 return MR_NO_WAR;
622 } else {
623 if (!pplayers_allied(city_owner(pcity), puowner)) {
624 /* You can't move into an empty city of a civilization you're not
625 * allied to. Assume that the player tried to attack. */
626 return MR_NO_WAR;
631 /* 8) */
632 zoc = igzoc
633 || can_step_taken_wrt_to_zoc(punittype, puowner, src_tile, dst_tile);
634 if (!zoc) {
635 /* The move is illegal because of zones of control. */
636 return MR_ZOC;
639 /* 9) */
640 if (utype_has_flag(punittype, UTYF_COAST_STRICT) && !is_safe_ocean(dst_tile)) {
641 return MR_TRIREME;
644 /* 10) */
645 if (!utype_has_flag(punittype, UTYF_CIVILIAN)
646 && !player_can_invade_tile(puowner, dst_tile)) {
647 return MR_PEACE;
650 /* 11) */
651 if (unit_transported(punit)
652 && !can_unit_unload(punit, unit_transport_get(punit))) {
653 return MR_CANNOT_DISEMBARK;
656 /* 12) */
657 if (!(is_native_move(utype_class(punittype), src_tile, dst_tile)
658 /* Allow non-native moves into cities or boarding transport. */
659 || pcity
660 || unit_could_load_at(punit, dst_tile))) {
661 return MR_NON_NATIVE_MOVE;
664 return MR_OK;
667 /**************************************************************************
668 Return true iff transporter has ability to transport transported.
669 **************************************************************************/
670 bool can_unit_transport(const struct unit *transporter,
671 const struct unit *transported)
673 fc_assert_ret_val(transporter != NULL, FALSE);
674 fc_assert_ret_val(transported != NULL, FALSE);
676 return can_unit_type_transport(unit_type_get(transporter),
677 unit_class_get(transported));
680 /**************************************************************************
681 Return TRUE iff transporter type has ability to transport transported class.
682 **************************************************************************/
683 bool can_unit_type_transport(const struct unit_type *transporter,
684 const struct unit_class *transported)
686 if (transporter->transport_capacity <= 0) {
687 return FALSE;
690 return BV_ISSET(transporter->cargo, uclass_index(transported));
693 /****************************************************************************
694 Return whether we can find a suitable transporter for given unit at
695 'ptile'. It needs to have free space. To find the best transporter, see
696 transporter_for_unit().
697 ****************************************************************************/
698 bool unit_can_load(const struct unit *punit)
700 unit_list_iterate(unit_tile(punit)->units, ptransport) {
701 if (can_unit_load(punit, ptransport)) {
702 return TRUE;
704 } unit_list_iterate_end;
706 return FALSE;
709 /****************************************************************************
710 Return whether we could find a suitable transporter for given unit at
711 'ptile'. It needs to have free space. To find the best transporter, see
712 transporter_for_unit_at().
713 ****************************************************************************/
714 bool unit_could_load_at(const struct unit *punit, const struct tile *ptile)
716 unit_list_iterate(ptile->units, ptransport) {
717 if (could_unit_load(punit, ptransport)) {
718 return TRUE;
720 } unit_list_iterate_end;
722 return FALSE;
725 static int move_points_denomlen = 0;
727 /****************************************************************************
728 Call whenever terrain_control.move_fragments / SINGLE_MOVE changes.
729 ****************************************************************************/
730 void init_move_fragments(void)
732 char denomstr[10];
733 /* String length of maximum denominator for fractional representation of
734 * movement points, for padding of text representation */
735 fc_snprintf(denomstr, sizeof(denomstr), "%d", SINGLE_MOVE);
736 move_points_denomlen = strlen(denomstr);
739 /****************************************************************************
740 Render positive movement points as text, including fractional movement
741 points, scaled by SINGLE_MOVE. Returns a pointer to a static buffer.
742 'reduce' is whether fractional movement points should be reduced to
743 lowest terms (this might be confusing in some cases).
744 'prefix' is a string put in front of all numeric output.
745 'none' is the string to display in place of the integer part if no
746 movement points (or NULL to just say 0).
747 'align' controls whether this is for a fixed-width table, in which case
748 padding spaces will be included to make all such strings line up when
749 right-aligned.
750 ****************************************************************************/
751 const char *move_points_text_full(int mp, bool reduce, const char *prefix,
752 const char *none, bool align)
754 static struct astring str = ASTRING_INIT;
755 int pad1, pad2;
757 if (align && SINGLE_MOVE > 1) {
758 /* Align to worst-case denominator even if we might be reducing to
759 * lowest terms, as other entries in a table might not reduce */
760 pad1 = move_points_denomlen; /* numerator or denominator */
761 pad2 = move_points_denomlen*2+2; /* everything right of integer part */
762 } else {
763 /* If no possible fractional part, alignment unneeded even if requested */
764 pad1 = pad2 = 0;
766 if (!prefix) {
767 prefix = "";
769 astr_clear(&str);
770 if ((mp == 0 && none) || SINGLE_MOVE == 0) {
771 /* No movement points, and we have a special representation to use */
772 /* (Also used when SINGLE_MOVE == 0, to avoid dividing by zero, which is
773 * important for client before ruleset has been received. Doesn't much
774 * matter what we print in this case.) */
775 astr_add(&str, "%s%*s", none ? none : "", pad2, "");
776 } else if ((mp % SINGLE_MOVE) == 0) {
777 /* Integer move points */
778 astr_add(&str, "%s%d%*s", prefix, mp / SINGLE_MOVE, pad2, "");
779 } else {
780 /* Fractional part */
781 int cancel;
783 fc_assert(SINGLE_MOVE > 1);
784 if (reduce) {
785 /* Reduce to lowest terms */
786 int gcd = mp;
787 /* Calculate greatest common divisor with Euclid's algorithm */
788 int b = SINGLE_MOVE;
790 while (b != 0) {
791 int t = b;
792 b = gcd % b;
793 gcd = t;
795 cancel = gcd;
796 } else {
797 /* No cancellation */
798 cancel = 1;
800 if (mp < SINGLE_MOVE) {
801 /* Fractional move points */
802 astr_add(&str, "%s%*d/%*d", prefix,
803 pad1, (mp % SINGLE_MOVE) / cancel, pad1, SINGLE_MOVE / cancel);
804 } else {
805 /* Integer + fractional move points */
806 astr_add(&str,
807 "%s%d %*d/%*d", prefix, mp / SINGLE_MOVE,
808 pad1, (mp % SINGLE_MOVE) / cancel, pad1, SINGLE_MOVE / cancel);
811 return astr_str(&str);
814 /****************************************************************************
815 Simple version of move_points_text_full() -- render positive movement
816 points as text without any prefix or alignment.
817 ****************************************************************************/
818 const char *move_points_text(int mp, bool reduce)
820 return move_points_text_full(mp, reduce, NULL, NULL, FALSE);