1 #include "movement.h"
2
3 #include "building/building.h"
4 #include "building/destruction.h"
5 #include "building/roadblock.h"
6 #include "core/calc.h"
7 #include "core/config.h"
8 #include "figure/combat.h"
9 #include "figure/route.h"
10 #include "figure/service.h"
11 #include "game/time.h"
12 #include "map/bridge.h"
13 #include "map/building.h"
14 #include "map/figure.h"
15 #include "map/grid.h"
16 #include "map/property.h"
17 #include "map/random.h"
18 #include "map/road_access.h"
19 #include "map/routing_terrain.h"
20 #include "map/terrain.h"
21
22 #define PALISADE_HP 60
23 #define BUILDING_HP 10
24 #define WALL_HP 200
25 #define GATEHOUSE_HP 150
26
advance_tick(figure * f)27 static void advance_tick(figure *f)
28 {
29 switch (f->direction) {
30 case DIR_0_TOP:
31 f->cross_country_y--;
32 break;
33 case DIR_1_TOP_RIGHT:
34 f->cross_country_x++;
35 f->cross_country_y--;
36 break;
37 case DIR_2_RIGHT:
38 f->cross_country_x++;
39 break;
40 case DIR_3_BOTTOM_RIGHT:
41 f->cross_country_x++;
42 f->cross_country_y++;
43 break;
44 case DIR_4_BOTTOM:
45 f->cross_country_y++;
46 break;
47 case DIR_5_BOTTOM_LEFT:
48 f->cross_country_x--;
49 f->cross_country_y++;
50 break;
51 case DIR_6_LEFT:
52 f->cross_country_x--;
53 break;
54 case DIR_7_TOP_LEFT:
55 f->cross_country_x--;
56 f->cross_country_y--;
57 break;
58 default:
59 break;
60 }
61 if (f->height_adjusted_ticks) {
62 f->height_adjusted_ticks--;
63 if (f->height_adjusted_ticks > 0) {
64 f->is_ghost = 1;
65 if (f->current_height < f->target_height) {
66 f->current_height++;
67 }
68 if (f->current_height > f->target_height) {
69 f->current_height--;
70 }
71 } else {
72 f->is_ghost = 0;
73 }
74 } else {
75 if (f->current_height) {
76 f->current_height--;
77 }
78 }
79 }
80
set_target_height_bridge(figure * f)81 static void set_target_height_bridge(figure *f)
82 {
83 f->height_adjusted_ticks = 18;
84 f->target_height = map_bridge_height(f->grid_offset);
85 }
86
get_permission_for_figure_type(figure * f)87 static roadblock_permission get_permission_for_figure_type(figure* f)
88 {
89 switch (f->type) {
90 case FIGURE_ENGINEER:
91 case FIGURE_PREFECT:
92 return PERMISSION_MAINTENANCE;
93 break;
94 case FIGURE_PRIEST:
95 return PERMISSION_PRIEST;
96 break;
97 case FIGURE_MARKET_TRADER:
98 return PERMISSION_MARKET;
99 break;
100 case FIGURE_GLADIATOR:
101 case FIGURE_CHARIOTEER:
102 case FIGURE_ACTOR:
103 case FIGURE_LION_TAMER:
104 case FIGURE_BARKEEP:
105 return PERMISSION_ENTERTAINER;
106 break;
107 case FIGURE_SURGEON:
108 case FIGURE_DOCTOR:
109 case FIGURE_BARBER:
110 case FIGURE_BATHHOUSE_WORKER:
111 return PERMISSION_MEDICINE;
112 case FIGURE_SCHOOL_CHILD:
113 case FIGURE_TEACHER:
114 case FIGURE_LIBRARIAN:
115 return PERMISSION_EDUCATION;
116 case FIGURE_TAX_COLLECTOR:
117 return PERMISSION_TAX_COLLECTOR;
118 case FIGURE_LABOR_SEEKER:
119 return PERMISSION_LABOR_SEEKER;
120 case FIGURE_MISSIONARY:
121 return PERMISSION_MISSIONARY;
122 case FIGURE_WATCHMAN:
123 return PERMISSION_WATCHMAN;
124 default:
125 return PERMISSION_NONE;
126 break;
127 }
128 }
129
move_to_next_tile(figure * f)130 static void move_to_next_tile(figure *f)
131 {
132 int old_x = f->x;
133 int old_y = f->y;
134 map_figure_delete(f);
135 switch (f->direction) {
136 default:
137 return;
138 case DIR_0_TOP:
139 f->y--;
140 break;
141 case DIR_1_TOP_RIGHT:
142 f->x++; f->y--;
143 break;
144 case DIR_2_RIGHT:
145 f->x++;
146 break;
147 case DIR_3_BOTTOM_RIGHT:
148 f->x++; f->y++;
149 break;
150 case DIR_4_BOTTOM:
151 f->y++;
152 break;
153 case DIR_5_BOTTOM_LEFT:
154 f->x--; f->y++;
155 break;
156 case DIR_6_LEFT:
157 f->x--;
158 break;
159 case DIR_7_TOP_LEFT:
160 f->x--; f->y--;
161 break;
162 }
163 f->grid_offset += map_grid_direction_delta(f->direction);
164 map_figure_add(f);
165 if (map_terrain_is(f->grid_offset, TERRAIN_ROAD)) {
166 f->is_on_road = 1;
167 if (map_terrain_is(f->grid_offset, TERRAIN_WATER)) { // bridge
168 set_target_height_bridge(f);
169 }
170 } else {
171 f->is_on_road = 0;
172 }
173 figure_combat_attack_figure_at(f, f->grid_offset);
174 f->previous_tile_x = old_x;
175 f->previous_tile_y = old_y;
176 }
177
set_next_route_tile_direction(figure * f)178 static void set_next_route_tile_direction(figure *f)
179 {
180 if (f->routing_path_id > 0) {
181 if (f->routing_path_current_tile < f->routing_path_length) {
182 f->direction = figure_route_get_direction(f->routing_path_id, f->routing_path_current_tile);
183 } else {
184 figure_route_remove(f);
185 f->direction = DIR_FIGURE_AT_DESTINATION;
186 }
187 } else { // should be at destination
188 f->direction = calc_general_direction(f->x, f->y, f->destination_x, f->destination_y);
189 if (f->direction != DIR_FIGURE_AT_DESTINATION) {
190 f->direction = DIR_FIGURE_LOST;
191 }
192 }
193 }
194
advance_route_tile(figure * f,int roaming_enabled)195 static void advance_route_tile(figure *f, int roaming_enabled)
196 {
197 if (f->direction >= 8) {
198 return;
199 }
200 int target_grid_offset = f->grid_offset + map_grid_direction_delta(f->direction);
201 if (f->is_boat) {
202 if (!map_terrain_is(target_grid_offset, TERRAIN_WATER)) {
203 f->direction = DIR_FIGURE_REROUTE;
204 }
205 } else if (f->terrain_usage == TERRAIN_USAGE_ENEMY) {
206 if (!map_routing_noncitizen_is_passable(target_grid_offset)) {
207 f->direction = DIR_FIGURE_REROUTE;
208 } else if (map_routing_is_destroyable(target_grid_offset)) {
209 int cause_damage = 1;
210 int max_damage = 0;
211 switch (map_routing_get_destroyable(target_grid_offset)) {
212 case DESTROYABLE_BUILDING:
213 {
214 building *b = building_get(map_building_at(target_grid_offset));
215 switch (b->type) {
216 case BUILDING_PALISADE:
217 max_damage = PALISADE_HP;
218 break;
219 default:
220 max_damage = BUILDING_HP;
221 break;
222 }
223 break;
224 }
225 case DESTROYABLE_AQUEDUCT_GARDEN:
226 if (map_terrain_is(target_grid_offset, TERRAIN_GARDEN | TERRAIN_ACCESS_RAMP | TERRAIN_RUBBLE)) {
227 cause_damage = 0;
228 } else {
229 max_damage = BUILDING_HP;
230 }
231 break;
232 case DESTROYABLE_WALL:
233 max_damage = WALL_HP;
234 break;
235 case DESTROYABLE_GATEHOUSE:
236 max_damage = GATEHOUSE_HP;
237 break;
238 }
239 if (cause_damage) {
240 f->attack_direction = f->direction;
241 f->direction = DIR_FIGURE_ATTACK;
242 if (!(game_time_tick() & 3)) {
243 building_destroy_increase_enemy_damage(target_grid_offset, max_damage);
244 }
245 }
246 }
247 } else if (f->terrain_usage == TERRAIN_USAGE_WALLS) {
248 if (!map_routing_is_wall_passable(target_grid_offset)) {
249 f->direction = DIR_FIGURE_REROUTE;
250 }
251 } else if (map_terrain_is(target_grid_offset, TERRAIN_ROAD | TERRAIN_ACCESS_RAMP)) {
252 if (roaming_enabled && map_terrain_is(target_grid_offset, TERRAIN_BUILDING)) {
253 building* b = building_get(map_building_at(target_grid_offset));
254 if (b->type == BUILDING_GATEHOUSE) {
255 // do not allow roaming through gatehouse
256 f->direction = DIR_FIGURE_REROUTE;
257 }
258 if (building_type_is_roadblock(b->type)) {
259 // do not allow roaming through roadblock
260 int permission = get_permission_for_figure_type(f);
261 if (!building_roadblock_get_permission(permission, b)) {
262 f->direction = DIR_FIGURE_REROUTE;
263 }
264 }
265 }
266 } else if (map_terrain_is(target_grid_offset, TERRAIN_BUILDING)) {
267 int type = building_get(map_building_at(target_grid_offset))->type;
268 switch (type) {
269 case BUILDING_WAREHOUSE:
270 case BUILDING_GRANARY:
271 case BUILDING_TRIUMPHAL_ARCH:
272 case BUILDING_FORT_GROUND:
273 break; // OK to walk
274 default:
275 f->direction = DIR_FIGURE_REROUTE;
276 }
277 } else if (map_terrain_is(target_grid_offset, TERRAIN_IMPASSABLE)) {
278 f->direction = DIR_FIGURE_REROUTE;
279 }
280 }
281
282
walk_ticks(figure * f,int num_ticks,int roaming_enabled)283 static void walk_ticks(figure *f, int num_ticks, int roaming_enabled)
284 {
285 while (num_ticks > 0) {
286 num_ticks--;
287 f->progress_on_tile++;
288 if (f->progress_on_tile < 15) {
289 advance_tick(f);
290 } else {
291 figure_service_provide_coverage(f);
292 f->progress_on_tile = 15;
293 if (f->routing_path_id <= 0) {
294 figure_route_add(f);
295 }
296 set_next_route_tile_direction(f);
297 advance_route_tile(f, roaming_enabled);
298 if (f->direction >= 8) {
299 break;
300 }
301 f->routing_path_current_tile++;
302 f->previous_tile_direction = f->direction;
303 f->progress_on_tile = 0;
304 move_to_next_tile(f);
305 advance_tick(f);
306 }
307 }
308 }
309
figure_movement_init_roaming(figure * f)310 void figure_movement_init_roaming(figure *f)
311 {
312 building *b = building_get(f->building_id);
313 f->progress_on_tile = 15;
314 f->roam_choose_destination = 0;
315 f->roam_ticks_until_next_turn = -1;
316 f->roam_turn_direction = 2;
317 if (config_get(CONFIG_GP_CH_ROAMERS_DONT_SKIP_CORNERS)) {
318 f->disallow_diagonal = 1;
319 }
320 int roam_dir = b->figure_roam_direction;
321 b->figure_roam_direction += 2;
322 if (b->figure_roam_direction > 6) {
323 b->figure_roam_direction = 0;
324 }
325 int x = b->x;
326 int y = b->y;
327 switch (roam_dir) {
328 case DIR_0_TOP: y -= 8; break;
329 case DIR_2_RIGHT: x += 8; break;
330 case DIR_4_BOTTOM: y += 8; break;
331 case DIR_6_LEFT: x -= 8; break;
332 }
333 map_grid_bound(&x, &y);
334 int x_road, y_road;
335 if (map_closest_road_within_radius(x, y, 1, 6, &x_road, &y_road)) {
336 f->destination_x = x_road;
337 f->destination_y = y_road;
338 } else {
339 f->roam_choose_destination = 1;
340 }
341 }
342
roam_set_direction(figure * f)343 static void roam_set_direction(figure *f)
344 {
345 int grid_offset = map_grid_offset(f->x, f->y);
346 int direction = calc_general_direction(f->x, f->y, f->destination_x, f->destination_y);
347 if (direction >= 8) {
348 direction = 0;
349 }
350 int road_offset_dir1 = 0;
351 int road_dir1 = 0;
352 for (int i = 0, dir = direction; i < 8; i++) {
353 if (dir % 2 == 0 && map_terrain_is(grid_offset + map_grid_direction_delta(dir), TERRAIN_ROAD)) {
354 road_dir1 = dir;
355 break;
356 }
357 dir++;
358 if (dir > 7) {
359 dir = 0;
360 }
361 road_offset_dir1++;
362 }
363 int road_offset_dir2 = 0;
364 int road_dir2 = 0;
365 for (int i = 0, dir = direction; i < 8; i++) {
366 if (dir % 2 == 0 && map_terrain_is(grid_offset + map_grid_direction_delta(dir), TERRAIN_ROAD)) {
367 road_dir2 = dir;
368 break;
369 }
370 dir--;
371 if (dir < 0) {
372 dir = 7;
373 }
374 road_offset_dir2++;
375 }
376 if (road_offset_dir1 <= road_offset_dir2) {
377 f->direction = road_dir1;
378 f->roam_turn_direction = 2;
379 } else {
380 f->direction = road_dir2;
381 f->roam_turn_direction = -2;
382 }
383 f->roam_ticks_until_next_turn = 5;
384 }
385
figure_movement_move_ticks(figure * f,int num_ticks)386 void figure_movement_move_ticks(figure *f, int num_ticks)
387 {
388 walk_ticks(f, num_ticks, 0);
389 }
390
figure_movement_move_ticks_with_percentage(figure * f,int num_ticks,int tick_percentage)391 void figure_movement_move_ticks_with_percentage(figure* f, int num_ticks, int tick_percentage)
392 {
393 int progress = f->progress_to_next_tick + tick_percentage;
394
395 if (progress >= 100) {
396 progress -= 100;
397 num_ticks++;
398 } else if (progress <= -100) {
399 progress += 100;
400 num_ticks--;
401 }
402 f->progress_to_next_tick = (char) progress;
403
404 walk_ticks(f, num_ticks, 0);
405 }
406
figure_movement_move_ticks_tower_sentry(figure * f,int num_ticks)407 void figure_movement_move_ticks_tower_sentry(figure *f, int num_ticks)
408 {
409 while (num_ticks > 0) {
410 num_ticks--;
411 f->progress_on_tile++;
412 if (f->progress_on_tile < 15) {
413 advance_tick(f);
414 } else {
415 f->progress_on_tile = 15;
416 }
417 }
418 }
419
figure_movement_follow_ticks(figure * f,int num_ticks)420 void figure_movement_follow_ticks(figure *f, int num_ticks)
421 {
422 const figure *leader = figure_get(f->leading_figure_id);
423 if (f->x == f->source_x && f->y == f->source_y) {
424 f->is_ghost = 1;
425 }
426 while (num_ticks > 0) {
427 num_ticks--;
428 f->progress_on_tile++;
429 if (f->progress_on_tile < 15) {
430 advance_tick(f);
431 } else {
432 f->progress_on_tile = 15;
433 f->direction = calc_general_direction(f->x, f->y,
434 leader->previous_tile_x, leader->previous_tile_y);
435 if (f->direction >= 8) {
436 break;
437 }
438 f->previous_tile_direction = f->direction;
439 f->progress_on_tile = 0;
440 move_to_next_tile(f);
441 advance_tick(f);
442 }
443 }
444 }
445
figure_movement_follow_ticks_with_percentage(figure * f,int num_ticks,int tick_percentage)446 void figure_movement_follow_ticks_with_percentage(figure* f, int num_ticks, int tick_percentage)
447 {
448 int progress = f->progress_to_next_tick + tick_percentage;
449
450 if (progress >= 100) {
451 progress -= 100;
452 num_ticks++;
453 } else if (progress <= -100) {
454 progress += 100;
455 num_ticks--;
456 }
457 f->progress_to_next_tick = (char) progress;
458
459 const figure* leader = figure_get(f->leading_figure_id);
460 if (f->x == f->source_x && f->y == f->source_y) {
461 f->is_ghost = 1;
462 }
463 while (num_ticks > 0) {
464 num_ticks--;
465 f->progress_on_tile++;
466 if (f->progress_on_tile < 15) {
467 advance_tick(f);
468 }
469 else {
470 f->progress_on_tile = 15;
471 f->direction = calc_general_direction(f->x, f->y,
472 leader->previous_tile_x, leader->previous_tile_y);
473 if (f->direction >= 8) {
474 break;
475 }
476 f->previous_tile_direction = f->direction;
477 f->progress_on_tile = 0;
478 move_to_next_tile(f);
479 advance_tick(f);
480 }
481 }
482 }
483
figure_movement_roam_ticks(figure * f,int num_ticks)484 void figure_movement_roam_ticks(figure *f, int num_ticks)
485 {
486 if (f->roam_choose_destination == 0) {
487 walk_ticks(f, num_ticks, 1);
488 if (f->direction == DIR_FIGURE_AT_DESTINATION) {
489 f->roam_choose_destination = 1;
490 f->roam_length = 0;
491 } else if (f->direction == DIR_FIGURE_REROUTE || f->direction == DIR_FIGURE_LOST) {
492 f->roam_choose_destination = 1;
493 }
494 if (f->roam_choose_destination) {
495 f->roam_ticks_until_next_turn = 100;
496 f->direction = f->previous_tile_direction;
497 } else {
498 return;
499 }
500 }
501 // no destination: walk to end of tile and pick a direction
502 while (num_ticks > 0) {
503 num_ticks--;
504 f->progress_on_tile++;
505 if (f->progress_on_tile < 15) {
506 advance_tick(f);
507 } else {
508 f->progress_on_tile = 15;
509 f->roam_random_counter++;
510 int came_from_direction = (f->previous_tile_direction + 4) % 8;
511 if (figure_service_provide_coverage(f)) {
512 return;
513 }
514 int road_tiles[8];
515 int permission = get_permission_for_figure_type(f);
516 int adjacent_road_tiles = map_get_adjacent_road_tiles_for_roaming(f->grid_offset, road_tiles, permission);
517 if (adjacent_road_tiles == 3 && map_get_diagonal_road_tiles_for_roaming(f->grid_offset, road_tiles) >= 5) {
518 // go in the straight direction of a double-wide road
519 adjacent_road_tiles = 2;
520 if (came_from_direction == DIR_0_TOP || came_from_direction == DIR_4_BOTTOM) {
521 if (road_tiles[0] && road_tiles[4]) {
522 road_tiles[2] = road_tiles[6] = 0;
523 } else {
524 road_tiles[0] = road_tiles[4] = 0;
525 }
526 } else {
527 if (road_tiles[2] && road_tiles[6]) {
528 road_tiles[0] = road_tiles[4] = 0;
529 } else {
530 road_tiles[2] = road_tiles[6] = 0;
531 }
532 }
533 }
534 if (adjacent_road_tiles == 4 && map_get_diagonal_road_tiles_for_roaming(f->grid_offset, road_tiles) >= 8) {
535 // go straight on when all surrounding tiles are road
536 adjacent_road_tiles = 2;
537 if (came_from_direction == DIR_0_TOP || came_from_direction == DIR_4_BOTTOM) {
538 road_tiles[2] = road_tiles[6] = 0;
539 } else {
540 road_tiles[0] = road_tiles[4] = 0;
541 }
542 }
543 if (adjacent_road_tiles <= 0) {
544 f->roam_length = f->max_roam_length; // end roaming walk
545 return;
546 }
547 if (adjacent_road_tiles == 1) {
548 int dir = 0;
549 do {
550 f->direction = 2 * dir;
551 } while (!road_tiles[f->direction] && dir++ < 4);
552 } else if (adjacent_road_tiles == 2) {
553 if (f->roam_ticks_until_next_turn == -1) {
554 roam_set_direction(f);
555 came_from_direction = -1;
556 }
557 // 1. continue in the same direction
558 // 2. turn in the direction given by roam_turn_direction
559 int dir = 0;
560 do {
561 if (road_tiles[f->direction] && f->direction != came_from_direction) {
562 break;
563 }
564 f->direction += f->roam_turn_direction;
565 if (f->direction > 6) {
566 f->direction = 0;
567 } else if (f->direction < 0) {
568 f->direction = 6;
569 }
570 } while (dir++ < 4);
571 } else { // > 2 road tiles
572 f->direction = (f->roam_random_counter + map_random_get(f->grid_offset)) & 6;
573 if (!road_tiles[f->direction] || f->direction == came_from_direction) {
574 f->roam_ticks_until_next_turn--;
575 if (f->roam_ticks_until_next_turn <= 0) {
576 roam_set_direction(f);
577 came_from_direction = -1;
578 }
579 int dir = 0;
580 do {
581 if (road_tiles[f->direction] && f->direction != came_from_direction) {
582 break;
583 }
584 f->direction += f->roam_turn_direction;
585 if (f->direction > 6) {
586 f->direction = 0;
587 } else if (f->direction < 0) {
588 f->direction = 6;
589 }
590 } while (dir++ < 4);
591 }
592 }
593 f->routing_path_current_tile++;
594 f->previous_tile_direction = f->direction;
595 f->progress_on_tile = 0;
596 move_to_next_tile(f);
597 advance_tick(f);
598 }
599 }
600 }
601
figure_movement_advance_attack(figure * f)602 void figure_movement_advance_attack(figure *f)
603 {
604 if (f->progress_on_tile <= 5) {
605 f->progress_on_tile++;
606 advance_tick(f);
607 }
608 }
609
figure_movement_set_cross_country_direction(figure * f,int x_src,int y_src,int x_dst,int y_dst,int is_missile)610 void figure_movement_set_cross_country_direction(figure *f, int x_src, int y_src, int x_dst, int y_dst, int is_missile)
611 {
612 // all x/y are in 1/15th of a tile
613 f->cc_destination_x = x_dst;
614 f->cc_destination_y = y_dst;
615 f->cc_delta_x = (x_src > x_dst) ? (x_src - x_dst) : (x_dst - x_src);
616 f->cc_delta_y = (y_src > y_dst) ? (y_src - y_dst) : (y_dst - y_src);
617 if (f->cc_delta_x < f->cc_delta_y) {
618 f->cc_delta_xy = 2 * f->cc_delta_x - f->cc_delta_y;
619 } else if (f->cc_delta_y < f->cc_delta_x) {
620 f->cc_delta_xy = 2 * f->cc_delta_y - f->cc_delta_x;
621 } else { // equal
622 f->cc_delta_xy = 0;
623 }
624 if (is_missile) {
625 f->direction = calc_missile_direction(x_src, y_src, x_dst, y_dst);
626 } else {
627 f->direction = calc_general_direction(x_src, y_src, x_dst, y_dst);
628 if (f->cc_delta_y > 2 * f->cc_delta_x) {
629 switch (f->direction) {
630 case DIR_1_TOP_RIGHT: case DIR_7_TOP_LEFT: f->direction = DIR_0_TOP; break;
631 case DIR_3_BOTTOM_RIGHT: case DIR_5_BOTTOM_LEFT: f->direction = DIR_4_BOTTOM; break;
632 }
633 }
634 if (f->cc_delta_x > 2 * f->cc_delta_y) {
635 switch (f->direction) {
636 case DIR_1_TOP_RIGHT: case DIR_3_BOTTOM_RIGHT: f->direction = DIR_2_RIGHT; break;
637 case DIR_5_BOTTOM_LEFT: case DIR_7_TOP_LEFT: f->direction = DIR_6_LEFT; break;
638 }
639 }
640 }
641 if (f->cc_delta_x >= f->cc_delta_y) {
642 f->cc_direction = 1;
643 } else {
644 f->cc_direction = 2;
645 }
646 }
647
figure_movement_set_cross_country_destination(figure * f,int x_dst,int y_dst)648 void figure_movement_set_cross_country_destination(figure *f, int x_dst, int y_dst)
649 {
650 f->destination_x = x_dst;
651 f->destination_y = y_dst;
652 figure_movement_set_cross_country_direction(
653 f, f->cross_country_x, f->cross_country_y,
654 15 * x_dst, 15 * y_dst, 0);
655 }
656
cross_country_update_delta(figure * f)657 static void cross_country_update_delta(figure *f)
658 {
659 if (f->cc_direction == 1) { // x
660 if (f->cc_delta_xy >= 0) {
661 f->cc_delta_xy += 2 * (f->cc_delta_y - f->cc_delta_x);
662 } else {
663 f->cc_delta_xy += 2 * f->cc_delta_y;
664 }
665 f->cc_delta_x--;
666 } else { // y
667 if (f->cc_delta_xy >= 0) {
668 f->cc_delta_xy += 2 * (f->cc_delta_x - f->cc_delta_y);
669 } else {
670 f->cc_delta_xy += 2 * f->cc_delta_x;
671 }
672 f->cc_delta_y--;
673 }
674 }
675
cross_country_advance_x(figure * f)676 static void cross_country_advance_x(figure *f)
677 {
678 if (f->cross_country_x < f->cc_destination_x) {
679 f->cross_country_x++;
680 } else if (f->cross_country_x > f->cc_destination_x) {
681 f->cross_country_x--;
682 }
683 }
684
cross_country_advance_y(figure * f)685 static void cross_country_advance_y(figure *f)
686 {
687 if (f->cross_country_y < f->cc_destination_y) {
688 f->cross_country_y++;
689 } else if (f->cross_country_y > f->cc_destination_y) {
690 f->cross_country_y--;
691 }
692 }
693
cross_country_advance(figure * f)694 static void cross_country_advance(figure *f)
695 {
696 cross_country_update_delta(f);
697 if (f->cc_direction == 2) { // y
698 cross_country_advance_y(f);
699 if (f->cc_delta_xy >= 0) {
700 f->cc_delta_x--;
701 cross_country_advance_x(f);
702 }
703 } else {
704 cross_country_advance_x(f);
705 if (f->cc_delta_xy >= 0) {
706 f->cc_delta_y--;
707 cross_country_advance_y(f);
708 }
709 }
710 }
711
figure_movement_move_ticks_cross_country(figure * f,int num_ticks)712 int figure_movement_move_ticks_cross_country(figure *f, int num_ticks)
713 {
714 map_figure_delete(f);
715 int is_at_destination = 0;
716 while (num_ticks > 0) {
717 num_ticks--;
718 if (f->missile_damage > 0) {
719 f->missile_damage--;
720 } else {
721 f->missile_damage = 0;
722 }
723 if (f->cc_delta_x + f->cc_delta_y <= 0) {
724 is_at_destination = 1;
725 break;
726 }
727 cross_country_advance(f);
728 }
729 f->x = f->cross_country_x / 15;
730 f->y = f->cross_country_y / 15;
731 f->grid_offset = map_grid_offset(f->x, f->y);
732 if (map_terrain_is(f->grid_offset, TERRAIN_BUILDING)) {
733 f->in_building_wait_ticks = 8;
734 } else if (f->in_building_wait_ticks) {
735 f->in_building_wait_ticks--;
736 }
737 map_figure_add(f);
738 return is_at_destination;
739 }
740
figure_movement_can_launch_cross_country_missile(int x_src,int y_src,int x_dst,int y_dst)741 int figure_movement_can_launch_cross_country_missile(int x_src, int y_src, int x_dst, int y_dst)
742 {
743 int height = 0;
744 figure *f = figure_get(0); // abuse unused figure 0 as scratch
745 f->cross_country_x = 15 * x_src;
746 f->cross_country_y = 15 * y_src;
747 if (map_terrain_is(map_grid_offset(x_src, y_src), TERRAIN_WALL_OR_GATEHOUSE) || building_get(map_building_at(map_grid_offset(x_src, y_src)))->type == BUILDING_WATCHTOWER) {
748 height = 6;
749 }
750 figure_movement_set_cross_country_direction(f, 15 * x_src, 15 * y_src, 15 * x_dst, 15 * y_dst, 0);
751
752 for (int guard = 0; guard < 1000; guard++) {
753 for (int i = 0; i < 8; i++) {
754 if (f->cc_delta_x + f->cc_delta_y <= 0) {
755 return 1;
756 }
757 cross_country_advance(f);
758 }
759 f->x = f->cross_country_x / 15;
760 f->y = f->cross_country_y / 15;
761 if (height) {
762 height--;
763 } else {
764 int grid_offset = map_grid_offset(f->x, f->y);
765 if (map_terrain_is(grid_offset, TERRAIN_WALL | TERRAIN_GATEHOUSE | TERRAIN_TREE)) {
766 break;
767 }
768 if (map_terrain_is(grid_offset, TERRAIN_BUILDING) && map_property_multi_tile_size(grid_offset) > 1) {
769 break;
770 }
771 }
772 }
773 return 0;
774 }
775