1 /* Unit task execution and general task functions.
2 Copyright (C) 1992-2000 Stanley T. Shebs.
3 Copyright (C) 2004-2005 Eric A. McDonald.
4
5 Xconq is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version. See the file COPYING. */
9
10 #include "conq.h"
11 #include "kernel.h"
12
13 /* (The following inclusion is only temporary if we end up proceeding
14 with the extraction of all AI code from this file.) */
15 #include "aiutil.h"
16
17 #include "aiunit.h"
18 #include "aiunit2.h"
19
20 /* This is the number of tasks to allocate initially. More will always be
21 allocated as needed, so this should be a "reasonable" value. */
22
23 #ifndef INITMAXTASKS
24 #define INITMAXTASKS 100
25 #endif
26
27 enum choicestate {
28 eitherway,
29 leftthenright,
30 rightthenleft,
31 leftonly,
32 rightonly
33 };
34
35 static int compare_directions(const void *a0, const void *a1);
36 static int test_for_buildable(int x, int y);
37 static int collect_test(int x, int y);
38 static int extractable_test(int x, int y);
39 static TaskOutcome execute_task_aux(Unit *unit, Task *task);
40
41 //! Perform a toolup subtask on behalf of a construct/build task.
42 static TaskOutcome do_toolup_subtask(Unit *constructor, int uc);
43
44 static TaskOutcome do_approach_subtask(Unit *unit, Task *task, int tx, int ty, int *statep);
45 static void allocate_task_block(void);
46 #if (0)
47 static Unit *repair_here(int x, int y);
48 #endif
49 static Unit *resupply_here(int x, int y);
50
51
52 /* Declare all the task functions. */
53
54 #undef DEF_TASK
55 #define DEF_TASK(name,dname,code,argtypes,DOFN,createfn,setfn,netsetfn,pushfn,netpushfn,argdecl) \
56 static TaskOutcome DOFN(Unit *unit, Task *task);
57
58 #include "task.def"
59
60 /* Array of descriptions of task types. */
61
62 TaskDefn taskdefns[] = {
63
64 #undef DEF_TASK
65 #define DEF_TASK(NAME,DNAME,code,ARGTYPES,DOFN,createfn,setfn,netsetfn,pushfn,netpushfn,argdecl) \
66 { NAME, DNAME, ARGTYPES, DOFN },
67
68 #include "task.def"
69
70 { NULL, NULL, NULL }
71 };
72
73 /* The list of available task objects. */
74
75 Task *freetasks;
76
77 /* Pointer to a buffer that task debug info goes into. */
78
79 char *taskbuf;
80
81 static int tmpbuildutype;
82
83 static Unit *tmpbuilder, *tmpbuildunit;
84
85 /* Used by the resupply code. */
86
87 int *lowm = NULL;
88
89 int numlow = 0;
90
91 static int tmpx, tmpy;
92
93 static int *foundm;
94
95 /* Allocate an initial collection of task objects. */
96
97 void
init_tasks(void)98 init_tasks(void)
99 {
100 allocate_task_block();
101 }
102
103 /* Allocate a new block of tasks. */
104
105 void
allocate_task_block(void)106 allocate_task_block(void)
107 {
108 int i;
109
110 freetasks = (Task *) xmalloc(INITMAXTASKS * sizeof(Task));
111 /* Chain the tasks together. */
112 for (i = 0; i < INITMAXTASKS; ++i) {
113 freetasks[i].next = &freetasks[i+1];
114 }
115 freetasks[INITMAXTASKS-1].next = NULL;
116 }
117
118 /* Create and return a new task. */
119
120 Task *
create_task(TaskType type)121 create_task(TaskType type)
122 {
123 int i;
124 Task *task;
125
126 /* Scarf up some more memory if we need it. */
127 if (freetasks == NULL) {
128 allocate_task_block();
129 }
130 /* Peel off a task from the free list. */
131 task = freetasks;
132 freetasks = task->next;
133 /* Reset its slots. */
134 task->type = type;
135 task->execnum = 0;
136 task->retrynum = 0;
137 for (i = 0; i < MAXTASKARGS; ++i)
138 task->args[i] = 0;
139 task->next = NULL;
140 return task;
141 }
142
143 /* Make a copy of the given task. */
144
145 Task *
clone_task(Task * oldtask)146 clone_task(Task *oldtask)
147 {
148 int i;
149 Task *newtask;
150
151 newtask = create_task(oldtask->type);
152 /* Probably not a good idea to copy exec/retry counts, skip them. */
153 for (i = 0; i < MAXTASKARGS; ++i)
154 newtask->args[i] = oldtask->args[i];
155 return newtask;
156 }
157
158 /* The empty task always succeeds immediately. */
159
160 static TaskOutcome
do_none_task(Unit * unit,Task * task)161 do_none_task(Unit *unit, Task *task)
162 {
163 return TASK_IS_COMPLETE;
164 }
165
166 static int
test_for_buildable(int x,int y)167 test_for_buildable(int x, int y)
168 {
169 Unit *unit;
170
171 for_all_stack(x, y, unit) {
172 if (in_play(unit)
173 && !fullsized(unit)
174 && unit->type == tmpbuildutype
175 && unit->side == tmpbuilder->side) {
176 tmpbuildunit = unit;
177 return TRUE;
178 }
179 }
180 return FALSE;
181 }
182
183 /* The build task handles the development, tooling up, creation, and
184 completion for a given number of units of a given type. */
185
186 static TaskOutcome
do_build_task(Unit * builder,Task * task)187 do_build_task(Unit *builder, Task *task)
188 {
189 int rslt = A_ANY_OK;
190 int idc = -1;
191 int uc = NONUTYPE;
192 Unit *buildee = NULL;
193 Side *side = NULL;
194
195 assert_error(is_active(builder),
196 "AI: Attempted to build with an inactive unit");
197 assert_warning_return(task, "AI: Attempted to run invalid construct task",
198 TASK_IS_COMPLETE);
199 side = builder->side;
200 // Attempt to retrieve buildee unit from ID.
201 idc = task->args[0];
202 buildee = find_unit(idc);
203 if (!in_play(buildee))
204 return TASK_FAILED;
205 if (fullsized(buildee))
206 return TASK_IS_COMPLETE;
207 uc = buildee->type;
208 builder->creation_id = -1;
209 // If research or development is needed, then we should not be in this code.
210 if (!side_can_build(side, uc))
211 return TASK_FAILED;
212 // We must handle tooling, if necessary.
213 if (!has_enough_tooling(builder, uc))
214 return do_toolup_subtask(builder, uc);
215 // Try performing build action.
216 if (valid(rslt = can_build(builder, builder, buildee))) {
217 prep_build_action(builder, builder, buildee);
218 return TASK_PREPPED_ACTION;
219 }
220 // Wait for more materials or ACP, if necessary.
221 // TODO: Proactively accumulate materials, if possible.
222 if ((A_ANY_NO_MATERIAL == rslt) || (A_ANY_NO_ACP == rslt)) {
223 set_unit_reserve(builder->side, builder, TRUE, FALSE);
224 return TASK_IS_INCOMPLETE;
225 }
226 return TASK_FAILED;
227 #if (0) // HACKING NOTE: Old devel logic. Keep around for now.
228 /* See if our technology needs improvement in order to build this
229 type. */
230 if (is_unit_type(u2)
231 && u_tech_to_build(u2) > 0
232 && us->tech[u2] < u_tech_to_build(u2)) {
233 if (uu_acp_to_develop(u, u2) > 0) {
234 push_develop_task(unit, u2, u_tech_to_build(u2));
235 return TASK_IS_INCOMPLETE;
236 } else {
237 /* Can't do the necessary development. */
238 /* (should filter out when asking to build?) */
239 notify(unit->side,
240 "You need tech of %d to build %s, but are only at %d.",
241 u_tech_to_build(u2), u_type_name(u2), us->tech[u2]);
242 notify(unit->side,
243 "%s cannot develop %s, so build task failed.",
244 unit_handle(unit->side, unit), u_type_name(u2));
245 return TASK_FAILED;
246 }
247 }
248 #endif
249 }
250
251 Unit *
find_unit_to_complete(Unit * unit,Task * task)252 find_unit_to_complete(Unit *unit, Task *task)
253 {
254 Unit *occ;
255 int u = unit->type, nx, ny, range;
256 int u2 = task->args[0];
257 int x = unit->x, y = unit->y;
258
259 /* Check out the unit supposedly in progress. */
260 if (task->args[1] != 0) {
261 occ = find_unit(task->args[1]);
262 if (in_play(occ) && occ->type == u2 && !fullsized(occ))
263 return occ;
264 }
265 /* Maybe search for any appropriate incomplete occupants. */
266 for_all_occupants(unit, occ) {
267 if (in_play(occ)
268 && !fullsized(occ)
269 && occ->type == u2
270 && occ->side == unit->side)
271 return occ;
272 }
273 /* Or else search for any appropriate incomplete units in this cell. */
274 for_all_stack(x, y, occ) {
275 if (in_play(occ)
276 && !fullsized(occ)
277 && occ->type == u2
278 && occ->side == unit->side) {
279 return occ;
280 }
281 }
282 /* Or else search nearby area. */
283 if (is_unit_type(u2)) {
284 range = uu_build_range(u, u2);
285 if (range > 0) {
286 tmpbuilder = unit;
287 tmpbuildutype = u2;
288 if (search_around(x, y, range, test_for_buildable, &nx, &ny, 1)) {
289 return tmpbuildunit;
290 }
291 }
292 }
293 /* nothing found */
294 return NULL;
295 }
296
297 /* This is a "pure development" task, with the sole objective of
298 increasing technology. */
299
300 static TaskOutcome
do_develop_task(Unit * unit,Task * task)301 do_develop_task(Unit *unit, Task *task)
302 {
303 int u = unit->type;
304 int u2 = task->args[0], lev = task->args[1];
305 Side *us = unit->side;
306
307 /* Independents can never ever do development. */
308 if (us == NULL)
309 return TASK_FAILED;
310 if (us->tech[u2] > u_tech_max(u2))
311 run_error("s%d tech for u%d is %d", side_number(us), u2, us->tech[u2]);
312 if (us->tech[u2] >= lev)
313 return TASK_IS_COMPLETE;
314 if (uu_acp_to_develop(u, u2) <= 0)
315 return TASK_FAILED;
316 if (valid(check_develop_action(unit, unit, u2))) {
317 prep_develop_action(unit, unit, u2);
318 return TASK_PREPPED_ACTION;
319 } else {
320 /* We get three tries to develop before giving up. */
321 return (task->execnum < 3 ? TASK_IS_INCOMPLETE : TASK_FAILED);
322 }
323 }
324
325 /* This is to capture a given type/side of unit at a given place. */
326
327 static TaskOutcome
do_capture_task(Unit * unit,Task * task)328 do_capture_task(Unit *unit, Task *task)
329 {
330 int u = unit->type, tx, ty, tu2, ts2, dist;
331 Unit *unit2;
332 Side *us = unit->side;
333
334 /* (should be able to say how hard to try) */
335 tx = task->args[0]; ty = task->args[1];
336 tu2 = task->args[2];
337 ts2 = task->args[3];
338 dist = distance(tx, ty, unit->x, unit->y);
339 switch (dist) {
340 case 0:
341 case 1:
342 for_all_stack(tx, ty, unit2) {
343 if ((ts2 >= 0 ?
344 (unit2->side->id == ts2) :
345 enemy_side(us, unit2->side))
346 && (tu2 == NONUTYPE || tu2 == unit2->type)) {
347 if (valid(check_capture_action(unit, unit, unit2))) {
348 prep_capture_action(unit, unit, unit2);
349 return TASK_PREPPED_ACTION;
350 } else if (valid(check_attack_action(unit, unit, unit2, 100))) {
351 prep_attack_action(unit, unit, unit2, 100);
352 return TASK_PREPPED_ACTION;
353 } else {
354 /* We get several tries to capture before giving up. */
355 set_unit_reserve(unit->side, unit, TRUE, FALSE);
356 return (task->execnum < 5 ? TASK_IS_INCOMPLETE : TASK_FAILED);
357 }
358 }
359 }
360 /* Nothing was here to capture. */
361 return TASK_IS_COMPLETE;
362 default:
363 /* If on mobile transport, let it handle things. */
364 if (unit->transport != NULL
365 && mobile(unit->transport->type)
366 /* and the transport is not blocked */
367 && flip_coin()) {
368 return TASK_IS_INCOMPLETE;
369 }
370 /* If out of range and can move, push a task to get closer
371 (usually). */
372 if (mobile(u) && probability(90)) {
373 push_move_to_task(unit, tx, ty, 1);
374 return TASK_IS_INCOMPLETE;
375 }
376 return TASK_FAILED;
377 }
378 }
379
380 /* The disband task's purpose is to make the unit disappear, so just
381 keep doing actions; when the unit goes away, so will the task. */
382
383 static TaskOutcome
do_disband_task(Unit * unit,Task * task)384 do_disband_task(Unit *unit, Task *task)
385 {
386 if (valid(check_disband_action(unit, unit))) {
387 prep_disband_action(unit, unit);
388 return TASK_PREPPED_ACTION;
389 } else {
390 /* (should try to find a nearby unit to do it?) */
391 return (task->execnum < 5 ? TASK_IS_INCOMPLETE : TASK_FAILED);
392 }
393 }
394
395 static TaskOutcome
do_hit_position_task(Unit * unit,Task * task)396 do_hit_position_task(Unit *unit, Task *task)
397 {
398 int u = unit->type, tx, ty, dist;
399 UnitView *uview = NULL;
400
401 /* (Temporary hack. Ask the planner to re-evaluate continuation of
402 this task. If it wants to, then it will issue a new one. If not,
403 then move on and do something more productive.) */
404 /* (A better solution would be to add a new arg to the task that would
405 tell it the number of times to attempt execution before returning to
406 the planner for guidance.) */
407 if (task->execnum > 3)
408 return TASK_IS_COMPLETE;
409 /* This is to hit a given place. */
410 /* (ask for a number of hits?) */
411 tx = task->args[0]; ty = task->args[1];
412 dist = distance(tx, ty, unit->x, unit->y);
413 /* Make sure that we are not going to hit a friendly unit. */
414 /* (What if we have unit with vision-range -1 in cell and they aren't
415 seen by any of our own units?) */
416 for_all_view_stack_with_occs(unit->side, tx, ty, uview) {
417 if (!enemy_side(unit->side, side_n(uview->siden)))
418 return TASK_FAILED;
419 }
420 /* Try performing a fire-into action. */
421 if (valid(check_fire_into_action(unit, unit, tx, ty, 0, -1))) {
422 prep_fire_into_action(unit, unit, tx, ty, 0, -1);
423 return TASK_PREPPED_ACTION;
424 } else if (mobile(u) && flip_coin()) {
425 /* We're too far away to shoot directly, add a move-to task. */
426 push_move_to_task(unit, tx, ty, max(1 /* attack range */, u_range(u)));
427 return TASK_IS_INCOMPLETE;
428 }
429 return TASK_FAILED;
430 }
431
432 static TaskOutcome
do_hit_unit_task(Unit * unit,Task * task)433 do_hit_unit_task(Unit *unit, Task *task)
434 {
435 int u = unit->type, tx, ty, dist, movedist, tu, ts;
436 Unit *unit2;
437 UnitView *uview;
438 Side *us = unit->side;
439
440 /* Temporary hack. Ask the planner to re-evaluate continuation of
441 this task. If it wants to, then it will issue a new one. If not,
442 then move on and do something more productive. */
443 /* A better solution would be to add a new arg to the task that would
444 tell it the number of times to attempt execution before returning to
445 the planner for guidance. */
446 if (task->execnum > 3)
447 return TASK_IS_COMPLETE;
448 /* This is to hit a (given type/side of) unit at a given place. */
449 tx = task->args[0];
450 ty = task->args[1];
451 tu = task->args[2];
452 ts = task->args[3];
453 dist = distance(tx, ty, unit->x, unit->y);
454 if (dist <= 1) {
455 if (can_attack_any(unit, unit)) {
456 for_all_view_stack(us, tx, ty, uview) {
457 if (ts == uview->siden && tu == uview->type) {
458 unit2 = view_unit(uview);
459 if (unit2
460 && valid(check_attack_action(unit, unit, unit2, 100))) {
461 prep_attack_action(unit, unit, unit2, 100);
462 return TASK_PREPPED_ACTION;
463 }
464 }
465 }
466 /* Maybe we can overrun, but not if our cell is known to
467 be clear of enemies. */
468 if (dist > 0
469 && valid(
470 check_overrun_action(unit, unit, tx, ty, unit->z, 100))) {
471 prep_overrun_action(unit, unit, tx, ty, unit->z, 100);
472 return TASK_PREPPED_ACTION;
473 }
474 }
475 /* Might be able to fire at pointblank range. */
476 if (can_fire_at_any(unit, unit) && dist >= u_range_min(u)) {
477 for_all_view_stack(us, tx, ty, uview) {
478 if (ts == uview->siden && tu == uview->type) {
479 unit2 = view_unit(uview);
480 if (unit2
481 && valid(check_fire_at_action(unit, unit, unit2, -1))) {
482 prep_fire_at_action(unit, unit, unit2, -1);
483 return TASK_PREPPED_ACTION;
484 }
485 }
486 }
487 }
488 return TASK_FAILED;
489 }
490 if (dist < u_range_min(u)) {
491 /* should move further away */
492 return TASK_FAILED;
493 }
494 /* If we're within firing range, attempt to fire. */
495 if (dist <= u_range(u)) {
496 for_all_view_stack(us, tx, ty, uview) {
497 if (ts == uview->siden && tu == uview->type) {
498 unit2 = view_unit(uview);
499 if (unit2 && valid(check_fire_at_action(unit, unit, unit2, -1))) {
500 prep_fire_at_action(unit, unit, unit2, -1);
501 return TASK_PREPPED_ACTION;
502 }
503 }
504 }
505 return TASK_FAILED;
506 }
507 if (!target_visible(unit, task))
508 return TASK_FAILED;
509 /* If on mobile transport, let it handle things. */
510 if (unit->transport != NULL
511 && mobile(unit->transport->type)
512 /* and the transport is not blocked */
513 && flip_coin()) {
514 return TASK_IS_INCOMPLETE;
515 }
516 /* If out of range and can move, push a task to get closer (maybe). */
517 if (mobile(u) && flip_coin()) {
518 movedist = max(1 /* attack range */, u_range(u));
519 if (dist > movedist + u_acp(u) /* or dist that could be covered in 1-2 turns */) {
520 movedist = max(movedist, (dist - movedist) / 4);
521 }
522 push_move_to_task(unit, tx, ty, movedist);
523 return TASK_IS_INCOMPLETE;
524 }
525 return TASK_FAILED;
526 }
527
528 int
target_visible(Unit * unit,Task * task)529 target_visible(Unit *unit, Task *task)
530 {
531 int tx, ty, tu, ts;
532 Side *us = unit->side;
533 UnitView *uview;
534
535 tx = task->args[0]; ty = task->args[1];
536 tu = task->args[2]; ts = task->args[3];
537
538 for_all_view_stack_with_occs(us, tx, ty, uview) {
539 if (ts == uview->siden
540 && (tu == NONUTYPE || tu == uview->type)) {
541 return TRUE;
542 }
543 }
544 return FALSE;
545 }
546
547 /* Move in a straight line, go through things or stop rather than
548 going around. */
549
550 static TaskOutcome
do_move_dir_task(Unit * unit,Task * task)551 do_move_dir_task(Unit *unit, Task *task)
552 {
553 int dir, tx, ty;
554 Unit *unit2 = NULL;
555 UnitView *uview = NULL;
556 Side *side = NULL;
557 int u = NONUTYPE;
558 int rslt = A_ANY_CANNOT_DO;
559 int curmp = 0, fullmp = 0;
560
561 side = unit->side;
562 u = unit->type;
563 if ((task->args[1])-- > 0) {
564 dir = task->args[0];
565 /* Is the next cell even valid? */
566 if (!point_in_dir(unit->x, unit->y, dir, &tx, &ty)) {
567 return TASK_FAILED;
568 }
569 /* Can we squeeze into the cell in any way, shape, or form? */
570 if (!side_thinks_it_can_put_type_at(side, u, tx, ty))
571 return TASK_FAILED;
572 /* Can the unit move into the cell? */
573 if (valid(check_move_action(unit, unit, tx, ty, 0))) {
574 /* (Probably need to set reserve here for low ACP and MP cases,
575 like we do for the enter action below.) */
576 prep_move_action(unit, unit, tx, ty, 0);
577 return TASK_PREPPED_ACTION;
578 }
579 /* Can the unit occupy another unit in the cell? */
580 else if (unit_view_at(side, tx, ty)) {
581 for_all_view_stack(side, tx, ty, uview) {
582 /* (A spy could enter untrusted unit...) */
583 if (!trusted_side(side, side_n(uview->siden)))
584 continue;
585 unit2 = view_unit(uview);
586 rslt = check_enter_action(unit, unit, unit2);
587 /* If not enough ACP left this turn, then wait. */
588 if ((A_ANY_NO_ACP == rslt)
589 && (can_have_enough_acp(unit,
590 uu_acp_to_enter(u, uview->type)))) {
591 set_unit_reserve(side, unit, TRUE, FALSE);
592 return TASK_IS_INCOMPLETE;
593 }
594 /* If not enough MP left this turn, then wait. */
595 else if (A_MOVE_NO_MP == rslt) {
596 if (can_be_actor(unit)) {
597 curmp = (unit->act->acp * unit_speed(unit, tx, ty))
598 / 100 + u_free_mp(u);
599 fullmp = ((total_acp_for_turn(unit) - u_acp_min(u)) *
600 unit_speed(unit, tx, ty)) / 100 +
601 u_free_mp(u);
602 }
603 else {
604 curmp = 0; fullmp = 0;
605 }
606 if (fullmp > curmp) {
607 set_unit_reserve(side, unit, TRUE, FALSE);
608 return TASK_IS_INCOMPLETE;
609 }
610 }
611 /* Else, we can enter now, then do it. */
612 else if (valid(rslt)) {
613 prep_enter_action(unit, unit, unit2);
614 return TASK_PREPPED_ACTION;
615 }
616 } /* for_all_view_stack */
617 return TASK_FAILED;
618 }
619 else {
620 return TASK_FAILED;
621 }
622 } /* if ((task->args[1])-- > 0) */
623 /* Specified number of moves has already been executed. */
624 else {
625 return TASK_IS_COMPLETE;
626 }
627 }
628
629 #if (0)
630 /* If we are sitting in the same cell or transport as a resupply source,
631 resupply is "free" for both us and the resupply source, and the
632 resupply source has some supplies that we could stock up on, then
633 swipe the supplies before moving on. */
634 int
stock_up_materials_for_free(Unit * unit)635 stock_up_materials_for_free(Unit *unit)
636 {
637 Unit *supplier = NULL;
638 int u = NONUTYPE, u2 = NONUTYPE;
639 int m = NONMTYPE;
640
641 u = unit->type;
642 if (!has_full_amount_of_all_materials(unit)) {
643 for_all_stack(unit->x, unit->y, supplier) {
644 if (enemy_side(unit->side, supplier->side))
645 continue;
646 if (!(supplier->supply))
647 continue;
648 u2 = supplier->type;
649 for_all_material_types(m) {
650 int dir;
651 /* TODO: Finish implementing. */
652 }
653 }
654 }
655 return FALSE;
656 }
657 #endif
658
659 static int
could_directly_board_ferry(Unit * unit,Unit * transport)660 could_directly_board_ferry(Unit * unit, Unit * transport)
661 {
662 return (can_occupy(unit, transport) &&
663 valid(check_enter_action(unit, unit, transport)));
664 }
665
666 /* The move-to task is the main way for units to get from point A to
667 point B. In addition to the destination, the task has a required
668 distance, so it will succeed if the unit is within that distance to
669 the nominal destination. */
670
671 static TaskOutcome
do_move_to_task(Unit * unit,Task * task)672 do_move_to_task(Unit *unit, Task *task)
673 {
674 int dist, tx, ty, check;
675 Unit *unit2, *occ;
676 int canceltask = FALSE;
677 Task *tmptask = NULL;
678
679 /* This task is to get to a designated location somehow. */
680 tx = task->args[0]; ty = task->args[1];
681 dist = distance(tx, ty, unit->x, unit->y);
682 if (dist <= task->args[3]) {
683 #if (0)
684 /* If unit is a mobile transport, then don't get jittery. */
685 if (unit->occupant && mobile(unit->type)) {
686 for_all_occupants(unit, occ) {
687 if (has_acp_left(occ) && unit->plan
688 && !unit->plan->reserve) {
689 /* delay_unit(unit, TRUE); */
690 return TASK_IS_INCOMPLETE;
691 }
692 }
693 }
694 #endif
695 return TASK_IS_COMPLETE;
696 }
697 #if (0)
698 /* Try to top off supplies if supplier around and supply transfer
699 does not cost any ACP. */
700 if (stock_up_materials_for_free(unit))
701 return TASK_IS_INCOMPLETE;
702 #endif
703 /* Abort the move-to, if it is associated with a resupply task that is
704 no longer necessary. Some games will run supply lines while a unit is
705 en route to resupply, and thereby negate the need to explicitly
706 move to a resupply point and resupply. */
707 if (task->next && (TASK_RESUPPLY == task->next->type)) {
708 if (NONMTYPE == task->next->args[0]) {
709 if (has_full_amount_of_all_materials(unit))
710 canceltask = TRUE;
711 }
712 else if (has_full_amount_of_material(unit, task->next->args[0]))
713 canceltask = TRUE;
714 if (canceltask) {
715 tmptask = task->next;
716 task->next = task->next->next;
717 free_task(tmptask);
718 /* Lie, so that we can hopefully replan or retask. */
719 /* (Should have new TaskResult, TASK_CANCELLED.) */
720 return TASK_IS_COMPLETE;
721 }
722 }
723 #if (0)
724 /* Abort the move-to, if it is not associated with a resupply
725 or entry task, and the unit is AI-controlled,
726 and the distance to the destination
727 is > the real operating range of the unit,
728 or unit has less fuel than its doctrine threshold,
729 and the unit is not in a transport. */
730 if ((!task->next
731 || (task->next
732 && ((task->next->type != TASK_RESUPPLY)
733 && (task->next->type != TASK_OCCUPY))))
734 && ai_controlled(unit)
735 && (((dist - task->args[3]) > real_operating_range_best(unit))
736 || past_halfway_point(unit))
737 && !unit->transport) {
738 clear_task_agenda(unit);
739 /* Lie, so that we can hopefully replan or retask. */
740 /* (Should have new TaskResult, TASK_CANCELLED.) */
741 return TASK_IS_COMPLETE;
742 }
743 #endif
744 switch (dist) {
745 case 0:
746 /* We're there already, nothing more to do. */
747 return TASK_IS_COMPLETE;
748 case 1:
749 /* Adjacent cell, do a single move. */
750 /* But first, if there are units here already, prefer to
751 interact with them. */
752 for_all_stack(tx, ty, unit2) {
753 /* If there's somebody that we can enter, prefer to do that. */
754 if (can_occupy(unit, unit2)
755 && valid(check_enter_action(unit, unit, unit2))) {
756 prep_enter_action(unit, unit, unit2);
757 return TASK_PREPPED_ACTION;
758 }
759 /* Perhaps an occupant... */
760 /* (We could use the recursive occupant test, but if
761 things are that complicated, the player should probably
762 exercise manual control here.) */
763 for_all_occupants(unit2, occ) {
764 if (can_occupy(unit, occ)
765 && valid(check_enter_action(unit, unit, occ))) {
766 prep_enter_action(unit, unit, occ);
767 return TASK_PREPPED_ACTION;
768 }
769 }
770 }
771 #if 0 /* auto-attack on move should be player-controlled... */
772 if (!trusted_side(unit->side, unit2->side)) {
773 /* This is probably not a good idea, combat odds not
774 taken into account. */
775 if (valid(check_attack_action(unit, unit, unit2, 100))) {
776 prep_attack_action(unit, unit, unit2, 100);
777 return TASK_PREPPED_ACTION;
778 } else {
779 continue;
780 }
781 }
782 #endif
783 /* Now try a basic move action. */
784 if (valid(check = check_move_action(unit, unit, tx, ty, unit->z))) {
785 /* Moving into an empty cell. */
786 prep_move_action(unit, unit, tx, ty, unit->z);
787 return TASK_PREPPED_ACTION;
788 } else {
789 /* If we're just short on mp, wait until the next turn to
790 try to move. */
791 if (check == A_MOVE_NO_MP) {
792 notify(unit->side,
793 "%s is resting until next turn.",
794 unit_handle(unit->side, unit));
795 set_unit_reserve(unit->side, unit, TRUE, FALSE);
796 return TASK_IS_INCOMPLETE;
797 }
798 Dprintf("%s move action fails check, result is %s\n",
799 unit_desig(unit), hevtdefns[check].name);
800 return TASK_FAILED;
801 }
802 break;
803 default:
804 if (dist <= u_move_range(unit->type)
805 && valid(check_move_action(unit, unit, tx, ty, unit->z))) {
806 prep_move_action(unit, unit, tx, ty, unit->z);
807 return TASK_PREPPED_ACTION;
808 } else if (dist == 2
809 && valid(check_move_action(unit, unit, tx, ty, unit->z))) {
810 /* Border slide check. */
811 prep_move_action(unit, unit, tx, ty, unit->z);
812 return TASK_PREPPED_ACTION;
813 } else {
814 /* Still some distance away, pick a way to go. */
815 return do_approach_subtask(unit, task, tx, ty, &(task->args[4]));
816 }
817 }
818 return TASK_FAILED;
819 }
820
821 static TaskOutcome
do_approach_subtask(Unit * unit,Task * task,int tx,int ty,int * statep)822 do_approach_subtask(Unit *unit, Task *task, int tx, int ty, int *statep)
823 {
824 int nx, ny, dirs[NUMDIRS], numdirs, i, numdirs2, check;
825 Unit *unit2;
826
827 /* If on mobile transport, let it handle things. */
828 if (unit->transport != NULL
829 && mobile(unit->transport->type)
830 /* and the transport is not stuck */
831 && probability(95)) {
832 set_unit_reserve(unit->side, unit, TRUE, FALSE);
833 return TASK_IS_INCOMPLETE;
834 }
835 numdirs =
836 choose_move_dirs(unit, tx, ty, TRUE, plausible_move_dir,
837 sort_directions, dirs);
838 if (!numdirs)
839 numdirs =
840 choose_move_dirs(unit, tx, ty, FALSE, plausible_move_dir,
841 sort_directions, dirs);
842 for (i = 0; i < numdirs; ++i) {
843 point_in_dir(unit->x, unit->y, dirs[i], &nx, &ny);
844 for_all_stack(nx, ny, unit2) {
845 if (can_occupy(unit, unit2)) {
846 if (valid(check_enter_action(unit, unit, unit2))) {
847 prep_enter_action(unit, unit, unit2);
848 /* We (probably) made forward progress,
849 so reopen choice of dirs. */
850 *statep = eitherway;
851 return TASK_PREPPED_ACTION;
852 } else {
853 continue;
854 }
855 } else if (!trusted_side(unit->side, unit2->side)) {
856 if (unit->occupant) {
857 /* More important to find a way through. */
858 continue;
859 } else {
860 /* This will encourage some re-evaluation. */
861 return TASK_FAILED;
862 }
863 #if 0 /* the following is rarely a good idea */
864 if (valid(check_attack_action(unit, unit, unit2, 100))) {
865 prep_attack_action(unit, unit, unit2, 100);
866 /* We (probably) made forward progress, so reopen choice of dirs. */
867 *statep = eitherway;
868 return TASK_PREPPED_ACTION;
869 } else {
870 continue;
871 }
872 #endif
873 }
874 }
875 if (valid(check_move_action(unit, unit, nx, ny, unit->z))) {
876 prep_move_action(unit, unit, nx, ny, unit->z);
877 /* We (probably) made forward progress, so reopen choice of dirs. */
878 *statep = eitherway;
879 return TASK_PREPPED_ACTION;
880 }
881 }
882 /* Get both right and left non-decreasing dirs. */
883 numdirs = choose_move_dirs(unit, tx, ty, TRUE, NULL, NULL, dirs);
884 numdirs2 = choose_move_dirs(unit, tx, ty, FALSE, NULL, NULL, dirs);
885 for (i = numdirs; i < numdirs2; ++i) {
886 if (plausible_move_dir(unit, dirs[i])) {
887 switch (*statep) {
888 case eitherway:
889 if (i == numdirs)
890 *statep = leftonly /* leftthenright */;
891 if (i == numdirs+1)
892 *statep = rightonly /* rightthenleft */;
893 break;
894 #if 0
895 case leftthenright:
896 if (i == numdirs)
897 *statep = rightonly;
898 if (i == numdirs+1)
899 *statep = rightonly;
900 continue;
901 break;
902 case rightthenleft:
903 if (i == numdirs+1)
904 *statep = leftonly;
905 continue;
906 break;
907 #endif
908 case leftonly:
909 if (i == numdirs+1)
910 continue;
911 break;
912 case rightonly:
913 if (i == numdirs)
914 continue;
915 break;
916 default:
917 run_warning("Weird right/left state %d", *statep);
918 *statep = leftonly;
919 break;
920 }
921 } else {
922 switch (*statep) {
923 case eitherway:
924 if (i == numdirs)
925 *statep = rightonly;
926 if (i == numdirs+1)
927 *statep = leftonly;
928 continue;
929 break;
930 #if 0
931 case leftthenright:
932 if (i == numdirs)
933 *statep = rightonly;
934 if (i == numdirs+1)
935 *statep = rightonly;
936 continue;
937 break;
938 case rightthenleft:
939 if (i == numdirs+1)
940 *statep = leftonly;
941 continue;
942 break;
943 #endif
944 case leftonly:
945 if (i == numdirs)
946 return TASK_FAILED;
947 if (i == numdirs+1)
948 continue;
949 break;
950 case rightonly:
951 if (i == numdirs)
952 continue;
953 if (i == numdirs+1)
954 return TASK_FAILED;
955 break;
956 default:
957 run_warning("Weird right/left state %d", *statep);
958 *statep = leftonly;
959 break;
960 }
961 }
962 point_in_dir(unit->x, unit->y, dirs[i], &nx, &ny);
963 for_all_stack(nx, ny, unit2) {
964 if (can_occupy(unit, unit2)) {
965 if (valid(check_enter_action(unit, unit, unit2))) {
966 prep_enter_action(unit, unit, unit2);
967 return TASK_PREPPED_ACTION;
968 } else {
969 continue;
970 }
971 } else if (!trusted_side(unit->side, unit2->side)) {
972 if (unit->occupant) {
973 /* More important to find a way through. */
974 continue;
975 } else {
976 /* This will encourage some re-evaluation. */
977 return TASK_FAILED;
978 }
979 #if 0 /* the following is rarely a good idea */
980 if (valid(check_attack_action(unit, unit, unit2, 100))) {
981 prep_attack_action(unit, unit, unit2, 100);
982 return TASK_PREPPED_ACTION;
983 } else {
984 continue;
985 }
986 #endif
987 }
988 }
989 if (valid(check = check_move_action(unit, unit, nx, ny, unit->z))) {
990 prep_move_action(unit, unit, nx, ny, unit->z);
991 return TASK_PREPPED_ACTION;
992 }
993 /* If we're just short on mp, wait until the next turn to try to move. */
994 if (check == A_MOVE_NO_MP) {
995 if (unit->side)
996 notify(unit->side, "%s is resting until next turn.",
997 unit_handle(unit->side, unit));
998 set_unit_reserve(unit->side, unit, TRUE, FALSE);
999 return TASK_IS_INCOMPLETE;
1000 }
1001 }
1002 return TASK_FAILED;
1003 }
1004
1005 static TaskOutcome
do_occupy_task(Unit * unit,Task * task)1006 do_occupy_task(Unit *unit, Task *task)
1007 {
1008 int dist;
1009 Unit *transport = NULL;
1010 int tx = -1;
1011 int ty = -1;
1012
1013 transport = find_unit_dead_or_alive(task->args[0]);
1014 /* Transport may have left play in the interim between attempted task
1015 executions.
1016 */
1017 if (!transport || !in_play(transport)) {
1018 if (transport) {
1019 DMprintf("%s attempted to enter out-of-play transport %s.\n",
1020 unit_desig(unit), unit_desig(transport));
1021 } else {
1022 DMprintf("%s attempted to enter a bogus transport with id %d.\n",
1023 unit_desig(unit), task->args[0]);
1024 }
1025 return TASK_FAILED;
1026 }
1027
1028 tx = transport->x;
1029 ty = transport->y;
1030 /* (should also fail if we don't know where transport is anymore) */
1031 if (unit->transport == transport) {
1032 return TASK_IS_COMPLETE;
1033 }
1034 dist = distance(unit->x, unit->y, transport->x, transport->y);
1035 if (dist <= 1) {
1036 if (valid(check_enter_action(unit, unit, transport))) {
1037 prep_enter_action(unit, unit, transport);
1038 return TASK_PREPPED_ACTION;
1039 } else {
1040 /* Try a couple times, then fail if not working. */
1041 return (task->execnum < 3 ? TASK_IS_INCOMPLETE : TASK_FAILED);
1042 }
1043 } else {
1044 /* Still some distance away, pick a way to go. */
1045 return do_approach_subtask(unit, task, transport->x, transport->y,
1046 &(task->args[1]));
1047 }
1048 }
1049
1050 /* Wait around for a particular unit. Give up if the unit is not
1051 forthcoming. */
1052
1053 static TaskOutcome
do_pickup_task(Unit * unit,Task * task)1054 do_pickup_task(Unit *unit, Task *task)
1055 {
1056 Unit *occupant = NULL;
1057
1058 occupant = find_unit_dead_or_alive(task->args[0]);
1059 /* Potential occupant may have left play in the interim between
1060 attempted task executions. */
1061 if (!occupant || !in_play(occupant)) {
1062 if (occupant) {
1063 DMprintf("%s attempted to pickup out-of-play unit %s.\n",
1064 unit_desig(unit), unit_desig(occupant));
1065 } else {
1066 DMprintf("%s attempted to pickup a bogus unit with id %d.\n",
1067 unit_desig(unit), task->args[0]);
1068 }
1069 return TASK_FAILED;
1070 }
1071 wake_unit(occupant->side, occupant, FALSE);
1072 if (occupant->transport == unit)
1073 return TASK_IS_COMPLETE;
1074 if (distance(unit->x, unit->y, occupant->x, occupant->y) > 1)
1075 return do_approach_subtask(unit, task, occupant->x, occupant->y,
1076 &(task->args[1]));
1077 if (task->execnum > 10) {
1078 /* Waiting around isn't working for us, give up. If the
1079 prospective occupant still needs us, we'll get another
1080 call. */
1081 return TASK_FAILED;
1082 } else {
1083 if (valid(check_enter_action(occupant, occupant, unit))) {
1084 prep_enter_action(occupant, occupant, unit);
1085 return TASK_PREPPED_ACTION;
1086 } else if (valid(check_enter_action(unit, occupant, unit))) {
1087 prep_enter_action(unit, occupant, unit);
1088 return TASK_PREPPED_ACTION;
1089 } else {
1090 return (task->execnum < 5 ? TASK_IS_INCOMPLETE : TASK_FAILED);
1091 }
1092 }
1093 }
1094
1095 static TaskOutcome
do_produce_task(Unit * unit,Task * task)1096 do_produce_task(Unit *unit, Task *task)
1097 {
1098 int m, tot, sofar, amt;
1099
1100 m = task->args[0];
1101 if (!is_material_type(m)) {
1102 /* This may be an indication of code bogosity; warn? */
1103 return TASK_FAILED;
1104 }
1105 tot = task->args[1];
1106 sofar = task->args[2];
1107 if (sofar >= tot)
1108 return TASK_IS_COMPLETE;
1109 amt = um_material_per_production(unit->type, m);
1110 if (valid(check_produce_action(unit, unit, m, amt))) {
1111 task->args[2] += amt;
1112 prep_produce_action(unit, unit, m, amt);
1113 return TASK_PREPPED_ACTION;
1114 }
1115 return TASK_FAILED;
1116 }
1117
1118 /* The collection task runs a series of extraction actions,
1119 transferring the collected material to another, given, unit. */
1120
1121 static int tmpm;
1122
1123 static Unit *collect_here(int x, int y);
1124 static Unit *aux_collect_here(Unit *unit);
1125
1126 static TaskOutcome
do_collect_task(Unit * unit,Task * task)1127 do_collect_task(Unit *unit, Task *task)
1128 {
1129 int m, x, y, dir, x1, y1, x2, y2, range;
1130 Unit *collector, *unit3;
1131
1132 m = task->args[0];
1133 x = task->args[1]; y = task->args[2];
1134 /* Set up tmp globals early, all steps use these. */
1135 tmpunit = unit;
1136 tmpm = m;
1137 if (!is_material_type(m)) {
1138 /* This may be an indication of code bogosity; warn? */
1139 return TASK_FAILED;
1140 }
1141 /* If the unit is full, then arrange to dump the supply somewhere. */
1142 if (unit->supply[m] == um_storage_x(unit->type, m)) {
1143 for_all_directions(dir) {
1144 if (interior_point_in_dir(unit->x, unit->y, dir, &x1, &y1)) {
1145 if ((collector = collect_here(x1, y1)) != NULL) {
1146 transfer_supply(unit, collector, m, unit->supply[m]);
1147 if (unit->supply[m] == um_storage_x(unit->type, m))
1148 return TASK_FAILED;
1149 push_move_to_task(unit, task->args[1], task->args[2], 0);
1150 return TASK_IS_INCOMPLETE;
1151 }
1152 }
1153 }
1154 /* Compute how far out to look for a delivery point. */
1155 range = real_operating_range_best(unit);
1156 if (search_around(unit->x, unit->y, range, collect_test,
1157 &x2, &y2, 1)) {
1158 /* (should find actual unit and chase it directly) */
1159 push_move_to_task(unit, x2, y2, 1);
1160 return TASK_IS_INCOMPLETE;
1161 } else {
1162 /* Failure - sometimes just sit, but usually try something else. */
1163 if (probability(10))
1164 set_unit_reserve(unit->side, unit, TRUE, FALSE);
1165 /* (should be able to signal interface usefully somehow) */
1166 return TASK_FAILED;
1167 }
1168 }
1169 if (distance(unit->x, unit->y, x, y) > 1) {
1170 push_move_to_task(unit, x, y, 1);
1171 return TASK_IS_INCOMPLETE;
1172 }
1173 if (valid(check_extract_action(unit, unit, x, y, m, 1))) {
1174 prep_extract_action(unit, unit, x, y, m, 1);
1175 return TASK_PREPPED_ACTION;
1176 }
1177 for_all_stack(x, y, unit3) {
1178 if (in_play(unit3)
1179 && unit3->side == unit->side
1180 && type_max_acp(unit3->type) == 0) {
1181 if (valid(check_transfer_action(unit, unit3, m, 1, unit))) {
1182 prep_transfer_action(unit, unit3, m, 1, unit);
1183 return TASK_PREPPED_ACTION;
1184 }
1185 }
1186 }
1187 /* We've run out of extractables in the immediate vicinity; look around
1188 for more. */
1189 tmpunit = unit;
1190 tmpm = m;
1191 if (search_around(x, y, 3, extractable_test, &x1, &y1, 1)) {
1192 push_move_to_task(unit, x1, y1, 1);
1193 task->args[1] = x1; task->args[2] = y1;
1194 return TASK_IS_INCOMPLETE;
1195 }
1196 return TASK_FAILED;
1197 }
1198
1199 static int
collect_test(int x,int y)1200 collect_test(int x, int y)
1201 {
1202 return (collect_here(x, y) != NULL);
1203 }
1204
1205 static Unit *
collect_here(int x,int y)1206 collect_here(int x, int y)
1207 {
1208 Unit *unit, *collector;
1209
1210 for_all_stack(x, y, unit) {
1211 collector = aux_collect_here(unit);
1212 if (collector)
1213 return collector;
1214 }
1215 return NULL;
1216 }
1217
1218 static Unit *
aux_collect_here(Unit * unit)1219 aux_collect_here(Unit *unit)
1220 {
1221 Unit *occ;
1222
1223 /* what about allies? */
1224 if (unit != tmpunit && unit_trusts_unit(unit, tmpunit)) {
1225 if ((unit->supply[tmpm] < um_storage_x(unit->type, tmpm)
1226 || um_gives_to_treasury(unit->type, tmpm))
1227 /* this is a hack to prevent foragers from trying to
1228 deliver to each other */
1229 && !mobile(unit->type))
1230 return unit;
1231 /* (should also test for ability to transfer to side's storage) */
1232 }
1233 for_all_occupants(unit, occ) {
1234 if (aux_collect_here(occ)) {
1235 return occ;
1236 }
1237 }
1238 return NULL;
1239 }
1240
1241 static int
extractable_test(int x,int y)1242 extractable_test(int x, int y)
1243 {
1244 Unit *unit;
1245
1246 /* (should only look at already-seen cells) */
1247 /* (should test if unit can extract from these places) */
1248 if (any_cell_materials_defined()
1249 && cell_material_defined(tmpm)
1250 && material_at(x, y, tmpm) > 0)
1251 return TRUE;
1252 for_all_stack(x, y, unit) {
1253 if (in_play(unit)
1254 && indep(unit)
1255 && unit->supply[tmpm] > 0)
1256 return TRUE;
1257 }
1258 return FALSE;
1259 }
1260
1261 /* This function uses a global approach in that it checks if the unit
1262 and its destination are located in or are adjacent to the same region
1263 (sea or continent). Since the region area layer is precomputed during
1264 startup, this check is fast. There are certain simplistic assumptions
1265 built into the code, however. First, it is assumed that resupply and
1266 repair points for naval units can be on land (i.e. in ports) but not vice
1267 versa. Second, it is assumed that naval units always can enter such
1268 land-based resupply points, if they are adjacent to a liquid cell. */
1269
1270 /* This code does not handle units on board of transports. A ground
1271 unit at sea will therefore fail to set a resupply or repair task since
1272 it cannot find a land path to a supply point. An air unit on board of a
1273 carrier will, however, be able to set a resupply task on land, since
1274 it can get there by itself. Ultimately, we should implement a way for
1275 occupants to set repair tasks and signal their transport where they
1276 need to go. In some games, occupants will leech supplies off their
1277 transport, so the latter will evetually run out of supplies and set
1278 its own resupply task. */
1279
1280 /* The code does not check if a path to the destination really exists
1281 that our unit may follow. Posible problems: enemy units and hostile
1282 terrain. The fact that u_ground_mobile is set for a unit does not
1283 mean that it may traverse all possible ground terrain. Ultimately,
1284 the code should test for paths as well. */
1285
1286 int
direct_access_to(int x,int y)1287 direct_access_to(int x, int y)
1288 {
1289 int x1, y1, x2, y2;
1290 int u = tmpunit->type;
1291 int ux = tmpunit->x;
1292 int uy = tmpunit->y;
1293
1294 /* Airborne units always have access everywhere. */
1295 if (u_air_mobile(u)) {
1296 return TRUE;
1297 }
1298 /* Landborne units have access if both cells belong
1299 to the same non-liquid region. */
1300 if (u_ground_mobile(u)) {
1301 if (!t_liquid(terrain_at(ux, uy))
1302 && !t_liquid(terrain_at(x, y))) {
1303 if (aref(area.landsea_regions, ux, uy)
1304 == aref(area.landsea_regions, x, y)) {
1305 return TRUE;
1306 }
1307 }
1308 }
1309 /* Seaborne units have access if both cells are adjacent
1310 to the same liquid region. */
1311 if (u_naval_mobile(u)) {
1312 /* We are starting from a liquid cell. */
1313 if (t_liquid(terrain_at(ux, uy))) {
1314 /* Test if the destination belongs to the same region. */
1315 if (aref(area.landsea_regions, ux, uy)
1316 == aref(area.landsea_regions, x, y)) {
1317 return TRUE;
1318 }
1319 /* Next test cells adjacent to the destination. */
1320 for_all_cells_within_range(x, y, 1, x2, y2) {
1321 if (aref(area.landsea_regions, ux, uy)
1322 == aref(area.landsea_regions, x2, y2)) {
1323 return TRUE;
1324 }
1325 }
1326 /* We are starting from a land cell. */
1327 } else {
1328 /* Scan adjacent liquid cells. */
1329 for_all_cells_within_range(ux, uy, 1, x1, y1) {
1330 if (t_liquid(terrain_at(x1, y1))) {
1331 /* Test if the destination belongs to the same region. */
1332 if (aref(area.landsea_regions, x1, y1)
1333 == aref(area.landsea_regions, x, y)) {
1334 return TRUE;
1335 }
1336 /* Next test cells adjacent to the destination. */
1337 for_all_cells_within_range(x, y, 1, x2, y2) {
1338 if (aref(area.landsea_regions, x1, y1)
1339 == aref(area.landsea_regions, x2, y2)) {
1340 return TRUE;
1341 }
1342 }
1343 }
1344 }
1345 }
1346 }
1347 return FALSE;
1348 }
1349
1350 static TaskOutcome
do_toolup_subtask(Unit * constructor,int uc)1351 do_toolup_subtask(Unit *constructor, int uc)
1352 {
1353 int rslt = A_ANY_OK;
1354
1355 assert_error(is_active(constructor),
1356 "AI: Attempted to toolup an inactive unit");
1357 assert_error(is_unit_type(uc),
1358 "AI: Encountered and invalid unit type to toolup for");
1359 // If by some strange coincidence we are inside this function,
1360 // and constructor already has enough tooling,
1361 // then get out.
1362 if (has_enough_tooling(constructor, uc))
1363 return TASK_IS_INCOMPLETE;
1364 // If we cannot perform the toolup action, then the task must fail.
1365 if (!valid(rslt =
1366 can_toolup_for(constructor, constructor, uc))) {
1367 // TODO: Should notify side.
1368 return TASK_FAILED;
1369 }
1370 // Try to prep a toolup action.
1371 if (prep_toolup_action(constructor, constructor, uc))
1372 return TASK_PREPPED_ACTION;
1373 return TASK_FAILED;
1374 }
1375
1376 static TaskOutcome
do_construct_task(Unit * constructor,Task * task)1377 do_construct_task(Unit *constructor, Task *task)
1378 {
1379 int rslt = A_ANY_OK;
1380 int uc = NONUTYPE;
1381 int run = 0, x = -1, y = -1;
1382 int transid = -1;
1383 Unit *creation = NULL, *transport = NULL;
1384 Side *side = NULL;
1385 Task *tasks = NULL;
1386
1387 assert_error(is_active(constructor),
1388 "AI: Attempted to construct with an inactive unit");
1389 assert_warning_return(task, "AI: Attempted to run invalid construct task",
1390 TASK_IS_COMPLETE);
1391 side = constructor->side;
1392 // Unpack task args.
1393 uc = task->args[0];
1394 run = task->args[1];
1395 // If run length <= 0, then we're done.
1396 if (0 >= run)
1397 return TASK_IS_COMPLETE;
1398 transid = task->args[2];
1399 // If no transport or out-of-play transport, then try getting coords.
1400 transport = find_unit(transid);
1401 if (!in_play(transport)) {
1402 x = task->args[3]; y = task->args[4];
1403 // If coords are also invalid, then task fails.
1404 if (!inside_area(x, y))
1405 return TASK_FAILED;
1406 }
1407 // If constructor has its creation ID set,
1408 // then see if it matches an incomplete unit.
1409 // If so, then decrement the construction run length,
1410 // clear the creation ID, and push a build task, if necessary.
1411 creation = find_unit(constructor->creation_id);
1412 if (in_play(creation)) {
1413 constructor->creation_id = -1;
1414 --task->args[1];
1415 // Push or set build task, if creation is incomplete.
1416 // Push, if we have more construction planned.
1417 // Set, if this is last of construction run.
1418 if (task->args[1])
1419 push_build_task(
1420 constructor, creation->id, u_cp(creation->type));
1421 else
1422 set_build_task(
1423 constructor, creation->id, u_cp(creation->type));
1424 tasks = (constructor->plan ? constructor->plan->tasks : NULL);
1425 if (!tasks || (TASK_BUILD != tasks->type))
1426 return TASK_FAILED;
1427 tasks->args[1] = creation->id;
1428 return TASK_IS_INCOMPLETE;
1429 }
1430 // Else, we try to proceed with creating an unit
1431 // in the desired cell or transport.
1432 // If research or development is needed, then we should not be in this code.
1433 if (!side_can_build(side, uc))
1434 return TASK_FAILED;
1435 // We must handle tooling, if necessary.
1436 if (!has_enough_tooling(constructor, uc))
1437 return do_toolup_subtask(constructor, uc);
1438 // If transport is valid, then try creating in it.
1439 if (in_play(transport)) {
1440 if (valid(rslt =
1441 can_create_in(constructor, constructor, uc, transport))) {
1442 prep_create_in_action(constructor, constructor, uc, transport);
1443 return TASK_PREPPED_ACTION;
1444 }
1445 }
1446 // Else, try creating in designated cell.
1447 else {
1448 if (valid(rslt =
1449 can_create_at(constructor, constructor, uc, x, y))) {
1450 prep_create_at_action(constructor, constructor, uc, x, y, 0);
1451 return TASK_PREPPED_ACTION;
1452 }
1453 }
1454 // Wait for more materials or ACP, if necessary.
1455 // TODO: Proactively accumulate materials, if possible.
1456 if ((A_ANY_NO_MATERIAL == rslt) || (A_ANY_NO_ACP == rslt)) {
1457 set_unit_reserve(constructor->side, constructor, TRUE, FALSE);
1458 return TASK_IS_INCOMPLETE;
1459 }
1460 return TASK_FAILED;
1461 }
1462
1463 static TaskOutcome
do_repair_self_subtask(Unit * repairee,Task * task)1464 do_repair_self_subtask(Unit *repairee, Task *task)
1465 {
1466 Unit *unit2 = NULL, *unitbest = NULL;
1467 Side *side = NULL;
1468 Task *tasks2 = NULL;
1469 int u = NONUTYPE, u2 = NONUTYPE;
1470 int tx = -1, ty = -1, dist = -1;
1471 int score = -1, scorebest = -1;
1472
1473 assert_error(is_active(repairee),
1474 "AI: Attempted to repair with an inactive unit");
1475 assert_warning_return(task, "AI: Attempted to run invalid repair task",
1476 TASK_IS_COMPLETE);
1477 // Useful info.
1478 side = repairee->side;
1479 u = repairee->type;
1480 tx = repairee->x; ty = repairee->y;
1481 // Try hp-recovery mechanism.
1482 if (0 < u_hp_recovery(u)) {
1483 set_unit_reserve(side, repairee, TRUE, FALSE);
1484 return TASK_IS_INCOMPLETE;
1485 }
1486 // Check if anyone else is tasked with repairing us.
1487 for_all_side_units(side, unit2) {
1488 if (unit2 == repairee)
1489 continue;
1490 // Skip inactive units.
1491 if (!is_active(unit2))
1492 continue;
1493 // Skip taskless units.
1494 tasks2 = (unit2->plan ? unit2->plan->tasks : NULL);
1495 if (!tasks2)
1496 continue;
1497 // Useful info.
1498 u2 = unit2->type;
1499 // If 'move-to' task, then inspect closer.
1500 if ((TASK_MOVE_TO == tasks2->type)
1501 && (uu_auto_repair_range(u2, u)
1502 >= distance(tasks2->args[0], tasks2->args[1], tx, ty)))
1503 tasks2 = tasks2->next;
1504 if ((TASK_MOVE_TO == tasks2->type)
1505 && (uu_repair_range(u2, u)
1506 >= distance(tasks2->args[0], tasks2->args[1], tx, ty)))
1507 tasks2 = tasks2->next;
1508 // If 'occupy' task, then inspect closer.
1509 if (TASK_OCCUPY == tasks2->type)
1510 tasks2 = tasks2->next;
1511 // If not 'repair' task, then skip.
1512 if (TASK_REPAIR != tasks2->type)
1513 continue;
1514 // Skip, if repairee is not the object of the repair.
1515 if (repairee->id != tasks2->args[0])
1516 continue;
1517 // If we've made it this far, then we know enough.
1518 // Now we just sit back and let the other unit do the work.
1519 set_unit_reserve(side, repairee, TRUE, FALSE);
1520 return TASK_IS_INCOMPLETE;
1521 } // for all side units
1522 // If not auto-repair, then we report that the task failed.
1523 // TODO: Find an unit to explicitly repair us, and schedule the repair.
1524 if (!Xconq::any_auto_repair)
1525 return TASK_FAILED;
1526 // Try to find someone to auto-repair us.
1527 for_all_side_units(side, unit2) {
1528 if (unit2 == repairee)
1529 continue;
1530 // Skip inactive units.
1531 if (!is_active(unit2))
1532 continue;
1533 // Probably not worth chasing mobile units. Let them come to us.
1534 if (mobile(unit2->type))
1535 continue;
1536 // Useful info.
1537 u2 = unit2->type;
1538 dist = distance(unit2->x, unit2->y, tx, ty);
1539 // Subtract auto-repair range from distance.
1540 // Note: For repairs that must occur inside a transport,
1541 // a penalty of 1 is effectively added.
1542 dist = max(0, dist - uu_auto_repair_range(u2, u));
1543 // If repairee is immobile,
1544 // and potential repairer is out of range, then skip it.
1545 if (!mobile(u) && (0 < dist))
1546 continue;
1547 // Score potential repairer.
1548 score = hp_per_turn_est(unit2, u) / isqrt(dist + 1);
1549 // Remember unit if it is best potential repairer.
1550 if (score > scorebest) {
1551 scorebest = score;
1552 unitbest = unit2;
1553 }
1554 } // for all side units
1555 if (0 < scorebest) {
1556 u2 = unitbest->type;
1557 // Calculate distance to be within repair range of best
1558 // potential repairer.
1559 dist = distance(unitbest->x, unitbest->y, repairee->x, repairee->y);
1560 dist = max(0, dist - uu_auto_repair_range(u2, u));
1561 // If distance to repairer > 0, then move there.
1562 // Note: A potential repairer with distance > 0 should never
1563 // have been selected, so this is not a concern.
1564 if (dist > 0) {
1565 // Move within range of potential repairer.
1566 if (0 <= uu_auto_repair_range(u2, u))
1567 // Move within auto-repair range of potential repairer.
1568 push_move_to_task(
1569 repairee, unitbest->x, unitbest->y,
1570 uu_auto_repair_range(u2, u));
1571 else {
1572 // Prepare to occupy potential repairer.
1573 // TODO: Technically, we need to search not only
1574 // the potential repairer but all occs under it to
1575 // find an opening that we can occupy.
1576 push_occupy_task(repairee, unitbest);
1577 // Move-to potential repairer before occupation of it.
1578 // NOTE: Move-to may not be necessary in all cases,
1579 // but it cannot hurt.
1580 push_move_to_task(repairee, unitbest->x, unitbest->y, 1);
1581 }
1582 }
1583 // Else, wait around.
1584 else
1585 set_unit_reserve(side, repairee, TRUE, FALSE);
1586 // Return the task as being incomplete.
1587 // If tasks were pushed onto the stack they will execute next.
1588 return TASK_IS_INCOMPLETE;
1589 } // 0 < scorebest
1590 return TASK_FAILED;
1591 }
1592
1593 static TaskOutcome
do_repair_task(Unit * repairer,Task * task)1594 do_repair_task(Unit *repairer, Task *task)
1595 {
1596 TaskOutcome rslt = TASK_IS_INCOMPLETE;
1597 Unit *repairee = NULL;
1598 Side *side = NULL;
1599 int id = -1;
1600 int u = NONUTYPE, u2 = NONUTYPE;
1601 int hpgoal = -1;
1602
1603 assert_error(is_active(repairer),
1604 "AI: Attempted to repair with an inactive unit");
1605 assert_warning_return(task, "AI: Attempted to run invalid repair task",
1606 TASK_IS_COMPLETE);
1607 // Get repairee.
1608 id = task->args[0];
1609 repairee = find_unit(id);
1610 // Is repairee in play?
1611 if (!in_play(repairee))
1612 return TASK_FAILED;
1613 // Useful info.
1614 u = repairer->type;
1615 u2 = repairee->type;
1616 side = repairer->side;
1617 hpgoal = task->args[1];
1618 // If repair is finished, then indicate that the task is complete.
1619 if (repairee->hp >= hpgoal)
1620 return TASK_IS_COMPLETE;
1621 // Set up explicit repair action, if possible.
1622 if (valid(can_repair(repairer, repairer, repairee))) {
1623 prep_repair_action(repairer, repairer, repairee);
1624 return TASK_PREPPED_ACTION;
1625 }
1626 // Wait around for auto-repair, if possible.
1627 if (valid(can_auto_repair(repairer, repairee))) {
1628 // If repairer is repairee, then try to get repaired various ways.
1629 if ((repairer == repairee)
1630 && (TASK_FAILED != (rslt = do_repair_self_subtask(repairee, task))))
1631 return rslt;
1632 else {
1633 set_unit_reserve(side, repairer, TRUE, FALSE);
1634 return TASK_IS_INCOMPLETE;
1635 }
1636 }
1637 // None of the above worked. Something is wrong.
1638 Dprintf("%s repair task failed!\n", unit_desig(repairer));
1639 return TASK_FAILED;
1640 }
1641
1642 #if (0)
1643 int
can_repair_from_here(int x,int y)1644 can_repair_from_here(int x, int y)
1645 {
1646 int x1, y1, u = tmpunit->type, u2, range = 0, space = FALSE;
1647 Unit *unit;
1648
1649 /* First test if auto-repair of this unit type is at all possible
1650 (should never get this far if not, but check anyway). */
1651 if (!will_be_auto_repaired
1652 || !will_be_auto_repaired[u]) {
1653 return FALSE;
1654 }
1655 /* Then test if we can get there. */
1656 if (!direct_access_to(x, y)) {
1657 return FALSE;
1658 }
1659 /* Also check if we can sit there. */
1660 if (!type_survives_in_cell(u, x, y)
1661 || !type_can_occupy_cell(u, x, y)) {
1662 /* If not, check if we can sit inside a unit at (x, y). */
1663 for_all_stack_with_occs(x, y, unit) {
1664 /* We are testing for empty type on the optimistic assumption
1665 that there will be some space when we get there. */
1666 if (type_can_occupy_empty_type(u, unit->type)) {
1667 space = TRUE;
1668 break;
1669 }
1670 }
1671 if (!space) {
1672 return FALSE;
1673 }
1674 }
1675 tmpx = x;
1676 tmpy = y;
1677 /* First check this cell. */
1678 if (repair_here(x, y)) {
1679 return TRUE;
1680 }
1681 /* Compute how far out we need to search. */
1682 for_all_unit_types(u2) {
1683 range = max(range, uu_auto_repair_range(u2, u));
1684 }
1685 /* Search adjacent cells. */
1686 if (search_around(
1687 x, y, range, (int (*)(int, int)) repair_here, &x1, &y1, 1)) {
1688 return TRUE;
1689 }
1690 return FALSE;
1691 }
1692
1693 Unit *
repair_here(int x,int y)1694 repair_here(int x, int y)
1695 {
1696 int u = tmpunit->type, range = 0;
1697 Unit *unit2;
1698
1699 for_all_stack_with_occs(x, y, unit2) {
1700 /* Skip over inactive units. */
1701 if (!is_active(unit2)) {
1702 continue;
1703 }
1704 /* Skip over untrusting units. */
1705 if (!unit_trusts_unit(unit2, tmpunit)) {
1706 continue;
1707 }
1708 /* Skip over units that cannot repair us. */
1709 if (uu_auto_repair(unit2->type, u) <= 0) {
1710 continue;
1711 }
1712 range = uu_auto_repair_range(unit2->type, u);
1713 /* Test if unit2 can repair unit and is within auto repair range of
1714 (x, y). Force the auto repair range to 0 if it is set to -1 to
1715 signal that only occupants should be repaired. */
1716 if (distance(x, y, tmpx, tmpy) <= max(range, 0)) {
1717 /* Check that tmpunit can occupy unit2 if this is required.
1718 Note: we optimistically test for room in empty transport
1719 since things may change once we get there.
1720 Should eventually add code that kicks out other occs to
1721 make room for damaged units that need repair. */
1722 if (range < 0
1723 && !type_can_occupy_empty_type(u, unit2->type)) {
1724 continue;
1725 }
1726 /* If the auto-repair range is zero and we cannot enter either
1727 the cell or unit2, we also have a problem. */
1728 if (range == 0
1729 && (!type_survives_in_cell(u, x, y)
1730 || !type_can_occupy_cell(u, x, y))
1731 && !type_can_occupy_empty_type(u, unit2->type)) {
1732 continue;
1733 }
1734 /* We assume that the calling code already has checked that
1735 tmpunit can sit at (tmpx, tmpy). */
1736 return unit2;
1737 }
1738 }
1739 return NULL;
1740 }
1741 #endif
1742
1743 /* Replenish our supplies, using one of several strategies, which as
1744 usual depends on the game, unit, terrain, etc. Strategies include
1745 1) wait for supply line or own production to replenish, 2) move to
1746 productive terrain and then wait, 3) move within range of a
1747 supplier, and 4) request a supplier to approach. */
1748
1749 /* (should see if production actions would resupply, prep those actions) */
1750
1751 static TaskOutcome
do_resupply_task(Unit * unit,Task * task)1752 do_resupply_task(Unit *unit, Task *task)
1753 {
1754 int x, y, u = unit->type, m, range;
1755 int ux = unit->x, uy = unit->y;
1756 Unit *unit2;
1757 #if (0)
1758 int amtavail = 0, amtwanted = 0;
1759 #endif
1760 int i = 0;
1761
1762 /* A unit acting randomly might have gotten this task, even if the
1763 game has no materials or the unit no supply. Pretend that it was OK
1764 and get out of here. */
1765 if (nummtypes == 0 || unit->supply == NULL)
1766 return TASK_IS_COMPLETE;
1767 tmpside = unit->side;
1768 tmpunit = unit;
1769 if (lowm == NULL)
1770 lowm = (int *) xmalloc(nummtypes * sizeof(int));
1771 numlow = 0;
1772 if (task->args[0] == NONMTYPE) {
1773 for_all_material_types(m) {
1774 if (unit->supply[m] < um_storage_x(u, m)) {
1775 lowm[numlow++] = m;
1776 }
1777 }
1778 } else {
1779 m = task->args[0];
1780 if (unit->supply[m] < um_storage_x(u, m)) {
1781 lowm[numlow++] = m;
1782 }
1783 }
1784 /* We're all filled up, must be OK. */
1785 if (numlow == 0) {
1786 return TASK_IS_COMPLETE;
1787 /* Set up an explicit extraction action if possible. We will still
1788 benefit from any auto-resupply that is available.*/
1789 } else if (valid(check_extract_action(unit, unit,
1790 unit->x, unit->y, m, 1))) {
1791 prep_extract_action(unit, unit, unit->x, unit->y, m, 1);
1792 return TASK_PREPPED_ACTION;
1793 /* Should not just sit around when there may be more expedient ways to
1794 get supplies that could let us get back into action the same turn. */
1795 } else if (can_auto_resupply_self(unit, lowm, numlow)) {
1796 set_unit_reserve(unit->side, unit, TRUE, FALSE);
1797 return TASK_IS_INCOMPLETE;
1798 /* Check if we are already at a valid resupply point. */
1799 } else if (can_resupply_from_here(ux, uy)) {
1800 /* Try to enter any provider in the same cell. Note: this
1801 is to ensure that aircraft lands on a carrier. If we already
1802 have a transport, we stay put. */
1803 if (!unit->transport) {
1804 for (i = 0; i < numlow; ++i) {
1805 tmpmtype = lowm[i];
1806 tmpx = ux;
1807 tmpy = uy;
1808 unit2 = resupply_here(ux, uy);
1809 if (unit2
1810 && can_occupy(unit, unit2)) {
1811 prep_enter_action(unit, unit, unit2);
1812 return TASK_PREPPED_ACTION;
1813 }
1814 }
1815 }
1816 /* Otherwise, just wait for the economy code to to its job. */
1817 set_unit_reserve(unit->side, unit, TRUE, FALSE);
1818 return TASK_IS_INCOMPLETE;
1819 } else {
1820 /* Compute how far out to look for a resupply point. */
1821 range = real_operating_range_best(unit);
1822 if (search_around(ux, uy, range, can_resupply_from_here, &x, &y, 1)) {
1823 push_move_to_task(unit, x, y, 0);
1824 DMprintf("Resupply task: %s is moving to (%d, %d).",
1825 unit_desig(unit), x, y);
1826 return TASK_IS_INCOMPLETE;
1827 } else {
1828 /* None of the above worked. Something is wrong. */
1829 Dprintf("%s resupply task failed!\n", unit_desig(unit));
1830 return TASK_FAILED;
1831 }
1832 }
1833 }
1834
1835 int
can_resupply_from_here(int x,int y)1836 can_resupply_from_here(int x, int y)
1837 {
1838 int i, x1, y1, u = tmpunit->type, space = FALSE;
1839 Unit *unit;
1840
1841 /* First test if we can get there. */
1842 if (!direct_access_to(x, y)) {
1843 return FALSE;
1844 }
1845 /* Also check if we can sit there. */
1846 if (!type_survives_in_cell(u, x, y)
1847 || !type_can_occupy_cell(u, x, y)) {
1848 /* If not, check if we can sit inside a unit at (x, y). */
1849 for_all_stack_with_occs(x, y, unit) {
1850 /* We are testing for empty type on the optimistic assumption
1851 that there will be some space when we get there. */
1852 if (type_can_occupy_empty_type(u, unit->type)) {
1853 space = TRUE;
1854 break;
1855 }
1856 }
1857 if (!space) {
1858 return FALSE;
1859 }
1860 }
1861 if (foundm == NULL) {
1862 foundm = (int *) xmalloc(nummtypes * sizeof(int));
1863 }
1864 /* Search adjacent cells within our inlength for needed supplies. */
1865 for (i = 0; i < numlow; ++i) {
1866 foundm[i] = FALSE;
1867 tmpmtype = lowm[i];
1868 tmpx = x;
1869 tmpy = y;
1870 /* First search this cell. */
1871 if (resupply_here(x, y)) {
1872 foundm[i] = TRUE;
1873 continue;
1874 }
1875 if (search_around(x, y, um_inlength(u, lowm[i]),
1876 (int (*)(int, int)) resupply_here, &x1, &y1, 1)){
1877 foundm[i] = TRUE;
1878 continue;
1879 }
1880 }
1881 /* If we cannot resupply a needed material from here, we are done. */
1882 for (i = 0; i < numlow; ++i) {
1883 if (foundm[i] == FALSE) {
1884 return FALSE;
1885 }
1886 }
1887 return TRUE;
1888 }
1889
1890 Unit *
resupply_here(int x,int y)1891 resupply_here(int x, int y)
1892 {
1893 int u, tu = tmpunit->type, range = 0;
1894 Unit *unit;
1895
1896 for_all_stack_with_occs(x, y, unit) {
1897 u = unit->type;
1898 /* Can't resupply from ourselves. */
1899 if (unit == tmpunit)
1900 continue;
1901 /* Can't resupply from enemies. */
1902 if (!unit_trusts_unit(unit, tmpunit))
1903 continue;
1904 /* The unit lacks our needed supply. */
1905 if (unit->supply[tmpmtype] == 0)
1906 continue;
1907 /* Compute the maximal possible supply range. */
1908 range = min(um_outlength(u, tmpmtype), um_inlength(tu, tmpmtype));
1909 /* Resupply is impossible. */
1910 if (range < 0)
1911 continue;
1912 /* We are out of range. */
1913 if (range < distance(unit->x, unit->y, tmpx, tmpy))
1914 continue;
1915 /* Range zero and there is no room for us. We assume
1916 that it is necessary to enter the provider. */
1917 if (range == 0 && !can_occupy(tmpunit, unit))
1918 continue;
1919 /* If range > 0 we assume that there is always room for
1920 us in a cell within range. */
1921 return unit;
1922 }
1923 return NULL;
1924 }
1925
1926 /* Return true if our own automatic material production is *greater*
1927 than our consumption, for the given list of materials. */
1928
1929 int
can_auto_resupply_self(Unit * unit,int * materials,int numtypes)1930 can_auto_resupply_self(Unit *unit, int *materials, int numtypes)
1931 {
1932 int u = unit->type, i, m, rslt = TRUE, t = terrain_at(unit->x, unit->y);
1933
1934 for (i = 0; i < numtypes; ++i) {
1935 m = materials[i];
1936 if ((um_base_production(u, m) * ut_productivity(u, t))
1937 <= um_base_consumption(u, m))
1938 rslt = FALSE;
1939 }
1940 return rslt;
1941 }
1942
1943 /* The sentry task puts the unit into reserve each turn for a given
1944 number of turns. */
1945
1946 static TaskOutcome
do_sentry_task(Unit * unit,Task * task)1947 do_sentry_task(Unit *unit, Task *task)
1948 {
1949 Task *nexttask = NULL;
1950 Unit *transport = NULL;
1951
1952 if (task->next)
1953 nexttask = task->next;
1954 /* If we were waiting for a transport, and are now in it,
1955 then don't wait. */
1956 if (nexttask->type == TASK_OCCUPY) {
1957 transport = find_unit_dead_or_alive(nexttask->args[0]);
1958 if (in_play(transport) && (transport == unit->transport)) {
1959 task->next = nexttask->next;
1960 free_task(nexttask);
1961 }
1962 return TASK_IS_COMPLETE;
1963 }
1964 if (task->args[0] > 0) {
1965 set_unit_reserve(unit->side, unit, TRUE, FALSE);
1966 --(task->args[0]);
1967 return TASK_IS_INCOMPLETE;
1968 } else {
1969 /* Unit won't necessarily wake up, may just replan and
1970 continue sleeping. */
1971 return TASK_IS_COMPLETE;
1972 }
1973 }
1974
1975 /* This is the main routine for a unit to execute a task. It
1976 basically consists of a dispatch to the execution code for each
1977 task type, and handling for a task's several possible outcomes.
1978 Note that a task does *not* directly invoke any actions; instead it
1979 will schedule ("prep") an action, which will be executed later by
1980 execute_action. Therefore, it is possible for a task to succeed
1981 but the action to fail, although each task type's code tries to
1982 reduce the chances of this happening (not possible to prevent
1983 entirely - unit may become damaged and unable to do perform an
1984 action after the task had decided on that action). */
1985
1986 TaskOutcome
execute_task(Unit * unit)1987 execute_task(Unit *unit)
1988 {
1989 Plan *plan = unit->plan;
1990 TaskOutcome rslt;
1991 Task *task;
1992 extern int taskexecs;
1993
1994 /* This should never happen. */
1995 if (unit->plan == NULL)
1996 run_error("???");
1997 /* If the unit is AI-run, don't do more than one task execution with
1998 it during each run step. */
1999 if (ai_controlled(unit)
2000 && plan->last_task_outcome != TASK_UNKNOWN
2001 && plan->last_task_outcome != TASK_PREPPED_ACTION
2002 /* unless it is acp-independent ... */
2003 && !acp_indep(unit))
2004 return unit->plan->last_task_outcome;
2005 task = plan->tasks;
2006 if (task == NULL)
2007 return TASK_UNKNOWN;
2008 ++taskexecs;
2009 rslt = execute_task_aux(unit, task);
2010 DMprintf("%s did task %s, ", unit_desig(unit), task_desig(task));
2011 /* Record it. */
2012 memcpy(&(unit->plan->last_task), task, sizeof(Task));
2013 unit->plan->last_task_outcome = rslt;
2014 /* Now look at what happened with task execution. */
2015 switch (rslt) {
2016 case TASK_UNKNOWN:
2017 DMprintf("???unknown outcome???");
2018 break;
2019 case TASK_FAILED:
2020 ++task->retrynum;
2021 DMprintf("failed try %d, ", task->retrynum);
2022 /* If a task fails, it might be because the task cannot be
2023 completed, or just because conditions are temporarily
2024 unfavorable, such as a passing unit blocking the way while
2025 moving through. So we need to retry a couple times at
2026 least; the variables here control how hard to keep
2027 trying. */
2028 /* (should be doctrine, since these affect human-run units too) */
2029 if (probability(g_ai_badtask_remove_chance())
2030 || task->retrynum >= g_ai_badtask_max_retries()) {
2031 pop_task(plan);
2032 DMprintf("removed it");
2033 /* We might be buzzing, so maybe go into reserve. */
2034 if (probability(g_ai_badtask_reserve_chance())) {
2035 plan->reserve = TRUE;
2036 DMprintf(" and went into reserve");
2037 }
2038 } else {
2039 DMprintf("will retry");
2040 }
2041 break;
2042 case TASK_IS_INCOMPLETE:
2043 /* Leave the task alone. */
2044 DMprintf("incomplete");
2045 break;
2046 case TASK_PREPPED_ACTION:
2047 /* Mention the action that was prepared to execute. */
2048 DMprintf("prepped action %s", action_desig(&(unit->act->nextaction)));
2049 break;
2050 case TASK_IS_COMPLETE:
2051 /* Task completed successfully, get rid of it. */
2052 DMprintf("completed after %d executions", task->execnum);
2053 pop_task(plan);
2054 break;
2055 default:
2056 break;
2057 }
2058 DMprintf("\n");
2059 return rslt;
2060 }
2061
2062 /* Perform a single given task. */
2063
2064 static TaskOutcome
execute_task_aux(Unit * unit,Task * task)2065 execute_task_aux(Unit *unit, Task *task)
2066 {
2067 TaskOutcome (*fn)(Unit *unit, Task *task);
2068
2069 if (!alive(unit) || task == NULL)
2070 return TASK_UNKNOWN;
2071 DMprintf("%s doing task %s\n", unit_desig(unit), task_desig(task));
2072 if (task->type < 0 || task->type >= NUMTASKTYPES) {
2073 run_warning("Unknown task type %d", task->type);
2074 return TASK_FAILED;
2075 }
2076 /* Count this execution. */
2077 ++task->execnum;
2078 /* Do it. */
2079 fn = taskdefns[task->type].exec;
2080 return (*fn)(unit, task);
2081 }
2082
2083 /* Wrapper to 'choose_move_dirs' function for providing "backward"
2084 compatibility to the 'choose_move_direction' function that some
2085 functions scattered around the kernel came to depend upon. */
2086 /* (The 'range' parameter is dead weight. Should we/can we do anything
2087 meaningful with it in the new context?) */
2088 int
choose_move_direction(Unit * unit,int x,int y,int range)2089 choose_move_direction(Unit *unit, int x, int y, int range)
2090 {
2091 int rslt = NODIR;
2092 int numdirs = 0;
2093 int dirs[NUMDIRS];
2094 int i = 0, ix, iy;
2095
2096 /* Try to find a shortest path first. */
2097 numdirs = choose_move_dirs(unit, x, y, TRUE, plausible_move_dir,
2098 sort_directions, dirs);
2099 #if (1)
2100 /* If that fails then try to find any path. */
2101 /* (Unfortunately, this is not compatible with 'do_approach_subtask'.
2102 If this piece of code is enabled and called from an UI,
2103 it may say a move is OK, but a movement task that is prepped may,
2104 in fact, fail in do_approach_subtask. So, for the time being, we
2105 must accept that certain legitimate paths will be rejected.) */
2106 if (!numdirs)
2107 numdirs = choose_move_dirs(unit, x, y, FALSE, plausible_move_dir,
2108 sort_directions, dirs);
2109 #endif
2110 /* If that fails then either no path exists, or else our pathfinder
2111 algorithm does not do a more thorough search such as with Astar. */
2112 if (!numdirs)
2113 return NODIR;
2114 for (i = 0; i < numdirs; ++i) {
2115 interior_point_in_dir(unit->x, unit->y, dirs[i], &ix, &iy);
2116 if (!side_thinks_it_can_put_type_at(unit->side, unit->type, ix, iy))
2117 continue;
2118 /* (Should put other tests here to make this function emulate
2119 the 'choose_move_direction' from Peter's pathfinder as much as
2120 is sane.) */
2121 rslt = dirs[i];
2122 break;
2123 }
2124 return rslt;
2125 }
2126
2127 /* This weird-looking routine computes next directions for moving to a
2128 given spot. The number of directions ranges from 1 to 4, depending
2129 on whether there is a straight-line path to the dest, and whether we are
2130 required to take a direct path or are allowed to move in dirs that don't
2131 bring the unit any closer (we never increase our distance though).
2132 Some trickinesses: if area wraps, must resolve ambiguity about
2133 getting to the same place going either direction (we pick shortest). */
2134
2135 int
choose_move_dirs(Unit * unit,int tx,int ty,int shortest,int (* dirtest)(Unit *,int),void (* dirsort)(Unit *,int *,int),int * dirs)2136 choose_move_dirs(Unit *unit, int tx, int ty, int shortest,
2137 int (*dirtest)(Unit *, int),
2138 void (*dirsort)(Unit *, int *, int),
2139 int *dirs)
2140 {
2141 int dx, dxa, dy, dist, d1, d2, d3, d4, axis = -1, hextant = -1;
2142 int numdirs = 0, shortestnumdirs;
2143
2144 dist = distance(unit->x, unit->y, tx, ty);
2145 dx = tx - unit->x; dy = ty - unit->y;
2146 if (area.xwrap) {
2147 dxa = (tx + area.width) - unit->x;
2148 if (ABS(dx) > ABS(dxa))
2149 dx = dxa;
2150 dxa = (tx - area.width) - unit->x;
2151 if (ABS(dx) > ABS(dxa))
2152 dx = dxa;
2153 }
2154 if (dx == 0 && dy == 0) {
2155 return -1;
2156 }
2157 axis = hextant = -1;
2158 if (dx == 0) {
2159 axis = (dy > 0 ? NORTHEAST : SOUTHWEST);
2160 } else if (dy == 0) {
2161 axis = (dx > 0 ? EAST : WEST);
2162 } else if (dx == (0 - dy)) {
2163 axis = (dy > 0 ? NORTHWEST : SOUTHEAST);
2164 } else if (dx > 0) {
2165 hextant = (dy > 0 ? EAST :
2166 (ABS(dx) > ABS(dy) ? SOUTHEAST : SOUTHWEST));
2167 } else {
2168 hextant = (dy < 0 ? WEST :
2169 (ABS(dx) > ABS(dy) ? NORTHWEST : NORTHEAST));
2170 }
2171 if (axis >= 0) {
2172 d1 = d2 = axis;
2173 if (dirtest == NULL || (*dirtest)(unit, d1)) {
2174 dirs[numdirs++] = d1;
2175 }
2176 }
2177 if (hextant >= 0) {
2178 d1 = left_dir(hextant);
2179 d2 = hextant;
2180 if (dirtest == NULL || (*dirtest)(unit, d1)) {
2181 dirs[numdirs++] = d1;
2182 }
2183 if (dirtest == NULL || (*dirtest)(unit, d2)) {
2184 dirs[numdirs++] = d2;
2185 }
2186 }
2187 /* Check on other properties of the two choices. */
2188 if (numdirs > 1 && dirsort != NULL) {
2189 (*dirsort)(unit, dirs, numdirs);
2190 }
2191 if (dist > 1 && !shortest) {
2192 shortestnumdirs = numdirs;
2193 d3 = left_dir(d1);
2194 d4 = right_dir(d2);
2195 if (dirtest == NULL || (*dirtest)(unit, d3)) {
2196 dirs[numdirs++] = d3;
2197 }
2198 if (dirtest == NULL || (*dirtest)(unit, d4)) {
2199 dirs[numdirs++] = d4;
2200 }
2201 if (numdirs > shortestnumdirs + 1 && dirsort != NULL) {
2202 (*dirsort)(unit, dirs + shortestnumdirs, numdirs - shortestnumdirs);
2203 }
2204 }
2205 return numdirs;
2206 }
2207
2208 /* A heuristic test for whether the given direction is a good one
2209 to move in. */
2210
2211 int
plausible_move_dir(Unit * unit,int dir)2212 plausible_move_dir(Unit *unit, int dir)
2213 {
2214 int u = unit->type, ux = unit->x, uy = unit->y, u2, nx, ny, t, c, cost;
2215 int totcost, speed, maxmpavail;
2216
2217 point_in_dir(ux, uy, dir, &nx, &ny);
2218 if (unit_at(nx, ny))
2219 return TRUE;
2220 t = terrain_at(nx, ny);
2221 /* Deadly terrain with no bridges is always implausible. */
2222 if ((ut_vanishes_on(u, t) || ut_wrecks_on(u, t))
2223 && !can_move_via_conn(unit, nx, ny))
2224 return FALSE;
2225 speed = unit_speed(unit, nx, ny);
2226 /* Try the easy test first. */
2227 if (ut_mp_to_enter(u, t) <= ((new_acp_for_turn(unit) * speed) / 100))
2228 return TRUE;
2229 u2 = (unit->transport ? unit->transport->type : NONUTYPE);
2230 totcost = total_move_cost(u, u2, ux, uy, 0, nx, ny, 0);
2231 /* Check if unit can possibly have enough mp for the move. */
2232 /* (We need to be careful here, since this function is not
2233 actually privy to all knowledge. Should use type generic
2234 functions instead.) */
2235 maxmpavail =
2236 (((u_acp_max(u) > 0 ? u_acp_max(u) : new_acp_for_turn(unit))
2237 - u_acp_min(u)) * speed) / 100
2238 + u_free_mp(u);
2239 if (maxmpavail >= totcost)
2240 return TRUE;
2241 /* Cross-country movement is not possible; try each connection
2242 type to see if it provides an alternative. */
2243 for_all_connection_types(c) {
2244 if (aux_terrain_defined(c)
2245 && connection_at(ux, uy, dir, c)
2246 && ((cost = ut_mp_to_traverse(u, c)) >= 0)) {
2247 if ((ut_mp_to_enter(u, c) + cost + ut_mp_to_leave(u, c))
2248 <= ((new_acp_for_turn(unit) * speed) / 100))
2249 return TRUE;
2250 }
2251 }
2252 return FALSE;
2253 }
2254
2255 /* This compares the desirability of two different directions. This is
2256 somewhat tricky, because it should return < 0 if i0 designates a BETTER
2257 direction than i1. */
2258
2259 int xs[NUMDIRS];
2260 int ys[NUMDIRS];
2261 int terrs[NUMDIRS];
2262
2263 static int
compare_directions(CONST void * a0,CONST void * a1)2264 compare_directions(CONST void *a0, CONST void *a1)
2265 {
2266 int i0, i1;
2267 int u = tmputype, t0, t1;
2268 int ux = tmpunit->x, uy = tmpunit->y, u2 = NONUTYPE;
2269 int cost0 = 0, cost1 = 0, s, ps0, ps1, surr0, surr1, rslt;
2270 int cs0, cs1;
2271 extern short *any_people_surrenders;
2272
2273 i0 = *((int *) a0); i1 = *((int *) a1);
2274 t0 = terrs[i0]; t1 = terrs[i1];
2275 if (tmpunit->transport)
2276 u2 = tmpunit->transport->type;
2277 /* Check the overall movement cost of each direction. */
2278 cost0 = total_move_cost(u, u2, ux, uy, 0, xs[i0], ys[i0], 0);
2279 cost1 = total_move_cost(u, u2, ux, uy, 0, xs[i1], ys[i1], 0);
2280 if (cost0 != cost1) {
2281 return cost0 - cost1;
2282 }
2283 if (1 /* not in supply */) {
2284 if ((rslt = ut_productivity(u, t1) - ut_productivity(u, t0)) != 0) {
2285 return rslt;
2286 }
2287 }
2288 if ((rslt = ut_mp_to_leave(u, t1) - ut_mp_to_leave(u, t0)) != 0) {
2289 return rslt;
2290 }
2291 /* Chooser the safer terrain. */
2292 if ((rslt = ut_accident_hit(u, t1) - ut_accident_hit(u, t0)) != 0) {
2293 return rslt;
2294 }
2295 /* Choose the better-concealing terrain. */
2296 /* (should only do if limited visibility) */
2297 if ((rslt = ut_visibility(u, t1) - ut_visibility(u, t0)) != 0) {
2298 return rslt;
2299 }
2300 /* Prefer to go over cells that we can change to our side. */
2301 /* (should control via doctrine, this will reveal movements
2302 more often) */
2303 if (any_people_surrenders != NULL
2304 && any_people_surrenders[u]
2305 && people_sides_defined()) {
2306 s = side_number(tmpunit->side);
2307 ps0 = people_side_at(xs[i0], ys[i0]);
2308 ps1 = people_side_at(xs[i1], ys[i1]);
2309 surr0 = ut_people_surrender(u, t0)
2310 * ((ps0 != NOBODY && s != ps0) ? 1 : 0);
2311 surr1 = ut_people_surrender(u, t1)
2312 * ((ps1 != NOBODY && s != ps1) ? 1 : 0);
2313 if (surr0 != surr1) {
2314 return surr1 - surr0;
2315 }
2316 }
2317 if (control_sides_defined()) {
2318 s = side_number(tmpunit->side);
2319 cs0 = control_side_at(xs[i0], ys[i0]);
2320 cs1 = control_side_at(xs[i1], ys[i1]);
2321 /* (should test trusted side) */
2322 surr0 = ((ps0 != NOBODY && s != ps0) ? 1 : 0);
2323 surr1 = ((ps1 != NOBODY && s != ps1) ? 1 : 0);
2324 if (surr0 != surr1) {
2325 return surr1 - surr0;
2326 }
2327 }
2328 return 0;
2329 }
2330
2331 void
sort_directions(Unit * unit,int * dirs,int numdirs)2332 sort_directions(Unit *unit, int *dirs, int numdirs)
2333 {
2334 int i, tmp, i0 = 0, i1 = 1, compar;
2335
2336 for (i = 0; i < numdirs; ++i) {
2337 point_in_dir(unit->x, unit->y, dirs[i], &(xs[i]), &(ys[i]));
2338 terrs[i] = terrain_at(xs[i], ys[i]);
2339 }
2340 tmpunit = unit;
2341 tmputype = unit->type;
2342 if (numdirs == 2) {
2343 compar = compare_directions(&i0, &i1);
2344 if (compar > 0 || (compar == 0 && flip_coin())) {
2345 tmp = dirs[0]; dirs[0] = dirs[1]; dirs[1] = tmp;
2346 }
2347 } else if (numdirs > 2) {
2348 qsort(dirs, numdirs, sizeof(int), compare_directions);
2349 if (compare_directions(&i0, &i1) == 0 && flip_coin()) {
2350 tmp = dirs[0]; dirs[0] = dirs[1]; dirs[1] = tmp;
2351 }
2352 }
2353 }
2354
2355 /* Put the given task back onto the list of free tasks. */
2356
2357 void
free_task(Task * task)2358 free_task(Task *task)
2359 {
2360 task->next = freetasks;
2361 freetasks = task;
2362 }
2363
2364 void
add_task(Unit * unit,int pos,Task * task)2365 add_task(Unit *unit, int pos, Task *task)
2366 {
2367 int numcleared;
2368 Task *agenda = NULL;
2369
2370 if (unit->plan == NULL && !completed(unit)) {
2371 init_unit_plan(unit);
2372 }
2373 if (!in_play(unit) || unit->plan == NULL) {
2374 run_warning("Trying to do %s task with bad %s",
2375 task_desig(task), unit_desig(unit));
2376 return;
2377 }
2378 DMprintf("%s add task %s",
2379 unit_desig(unit), task_desig(task));
2380 if (pos == CLEAR_AGENDA) {
2381 numcleared = clear_task_agenda(unit);
2382 DMprintf(" (cleared %d existing tasks)", numcleared);
2383 }
2384 DMprintf("\n");
2385 switch (pos) {
2386 // Put the task on the front of the agenda.
2387 case ADD_TO_AGENDA_AS_LIFO:
2388 case CLEAR_AGENDA:
2389 task->next = unit->plan->tasks;
2390 unit->plan->tasks = task;
2391 break;
2392 // Put the task on the end of the agenda.
2393 case ADD_TO_AGENDA_AS_FIFO:
2394 task->next = NULL;
2395 agenda = unit->plan->tasks;
2396 if (!agenda)
2397 unit->plan->tasks = task;
2398 else {
2399 for (; agenda && agenda->next; agenda = agenda->next);
2400 agenda->next = task;
2401 }
2402 break;
2403 default:
2404 run_error("Tried to add a task to a task agenda in a strange manner");
2405 break;
2406 }
2407 // Shouldn't be asleep any longer.
2408 unit->plan->asleep = FALSE;
2409 // We're not in reserve.
2410 unit->plan->reserve = FALSE;
2411 // Presumably we're no longer waiting to be told what to do.
2412 set_waiting_for_tasks(unit, FALSE);
2413 // Reflect all this on displays.
2414 update_unit_display(unit->side, unit, FALSE);
2415 }
2416
2417 /* This routine sets up a task to build a unit of the given type. */
2418
2419 Task *
create_build_task(Unit * unit,int id,int cp)2420 create_build_task(Unit *unit, int id, int cp)
2421 {
2422 Task *task = NULL;
2423
2424 task = create_task(TASK_BUILD);
2425 task->args[0] = id;
2426 task->args[1] = cp;
2427 return task;
2428 }
2429
2430 void
set_build_task(Unit * unit,int id,int cp)2431 set_build_task(Unit *unit, int id, int cp)
2432 {
2433 add_task(unit, CLEAR_AGENDA, create_build_task(unit, id, cp));
2434 }
2435
2436 void
push_build_task(Unit * unit,int id,int cp)2437 push_build_task(Unit *unit, int id, int cp)
2438 {
2439 add_task(unit, 0, create_build_task(unit, id, cp));
2440 }
2441
2442 Task *
create_capture_task(Unit * unit,int x,int y,int u,int s)2443 create_capture_task(Unit *unit, int x, int y, int u, int s)
2444 {
2445 Task *task = create_task(TASK_CAPTURE);
2446
2447 task->args[0] = x; task->args[1] = y;
2448 task->args[2] = u;
2449 task->args[3] = s;
2450 return task;
2451 }
2452
2453 void
set_capture_task(Unit * unit,int x,int y,int u,int s)2454 set_capture_task(Unit *unit, int x, int y, int u, int s)
2455 {
2456 add_task(unit, CLEAR_AGENDA, create_capture_task(unit, x, y, u, s));
2457 }
2458
2459 void
push_capture_task(Unit * unit,int x,int y,int u,int s)2460 push_capture_task(Unit *unit, int x, int y, int u, int s)
2461 {
2462 add_task(unit, 0, create_capture_task(unit, x, y, u, s));
2463 }
2464
2465 Task *
create_collect_task(Unit * unit,int m,int x,int y)2466 create_collect_task(Unit *unit, int m, int x, int y)
2467 {
2468 Task *task = create_task(TASK_COLLECT);
2469
2470 task->args[0] = m;
2471 task->args[1] = x; task->args[2] = y;
2472 return task;
2473 }
2474
2475 void
set_collect_task(Unit * unit,int m,int x,int y)2476 set_collect_task(Unit *unit, int m, int x, int y)
2477 {
2478 add_task(unit, 0, create_collect_task(unit, m, x, y));
2479 }
2480
2481 Task *
create_disband_task(Unit * unit)2482 create_disband_task(Unit *unit)
2483 {
2484 return create_task(TASK_DISBAND);
2485 }
2486
2487 void
set_disband_task(Unit * unit)2488 set_disband_task(Unit *unit)
2489 {
2490 add_task(unit, CLEAR_AGENDA, create_disband_task(unit));
2491 }
2492
2493 Task *
create_hit_unit_task(Unit * unit,int x,int y,int u,int s)2494 create_hit_unit_task(Unit *unit, int x, int y, int u, int s)
2495 {
2496 Task *task = create_task(TASK_HIT_UNIT);
2497
2498 task->args[0] = x; task->args[1] = y;
2499 task->args[2] = u;
2500 task->args[3] = s;
2501 return task;
2502 }
2503
2504 void
set_hit_unit_task(Unit * unit,int x,int y,int u,int s)2505 set_hit_unit_task(Unit *unit, int x, int y, int u, int s)
2506 {
2507 add_task(unit, CLEAR_AGENDA, create_hit_unit_task(unit, x, y, u, s));
2508 }
2509
2510 void
push_hit_unit_task(Unit * unit,int x,int y,int u,int s)2511 push_hit_unit_task(Unit *unit, int x, int y, int u, int s)
2512 {
2513 add_task(unit, 0, create_hit_unit_task(unit, x, y, u, s));
2514 }
2515
2516 /* Create a task to move in a given direction for a given distance. */
2517
2518 Task *
create_move_dir_task(Unit * unit,int dir,int n)2519 create_move_dir_task(Unit *unit, int dir, int n)
2520 {
2521 Task *task = create_task(TASK_MOVE_DIR);
2522
2523 task->args[0] = dir;
2524 task->args[1] = n;
2525 return task;
2526 }
2527
2528 /* Fill in the given unit with direction-moving orders. */
2529
2530 void
set_move_dir_task(Unit * unit,int dir,int n)2531 set_move_dir_task(Unit *unit, int dir, int n)
2532 {
2533 add_task(unit, CLEAR_AGENDA, create_move_dir_task(unit, dir, n));
2534 }
2535
2536 Task *
create_move_to_task(Unit * unit,int x,int y,int dist)2537 create_move_to_task(Unit *unit, int x, int y, int dist)
2538 {
2539 Task *task = create_task(TASK_MOVE_TO);
2540
2541 /* Other (time-critical AI) code expects that our data is clean,
2542 so filter it. */
2543 if (!in_area(x, y)) {
2544 run_warning("move-to task with bad dest %d,%d, replacing", x, y);
2545 /* Replace with a location guaranteed to be valid. */
2546 x = area.width / 2; y = area.height / 2;
2547 }
2548 task->args[0] = x; task->args[1] = y; task->args[2] = 0;
2549 task->args[3] = dist;
2550 task->args[4] = eitherway;
2551 return task;
2552 }
2553
2554 void
set_move_to_task(Unit * unit,int x,int y,int dist)2555 set_move_to_task(Unit *unit, int x, int y, int dist)
2556 {
2557 add_task(unit, CLEAR_AGENDA, create_move_to_task(unit, x, y, dist));
2558 }
2559
2560 void
push_move_to_task(Unit * unit,int x,int y,int dist)2561 push_move_to_task(Unit *unit, int x, int y, int dist)
2562 {
2563 add_task(unit, 0, create_move_to_task(unit, x, y, dist));
2564 }
2565
2566 Task *
create_occupy_task(Unit * unit,Unit * transport)2567 create_occupy_task(Unit *unit, Unit *transport)
2568 {
2569 Task *task = create_task(TASK_OCCUPY);
2570
2571 task->args[0] = transport->id;
2572 task->args[1] = eitherway;
2573 /* add a waiting period also? */
2574 return task;
2575 }
2576
2577 void
set_occupy_task(Unit * unit,Unit * transport)2578 set_occupy_task(Unit *unit, Unit *transport)
2579 {
2580 add_task(unit, CLEAR_AGENDA, create_occupy_task(unit, transport));
2581 }
2582
2583 void
push_occupy_task(Unit * unit,Unit * transport)2584 push_occupy_task(Unit *unit, Unit *transport)
2585 {
2586 add_task(unit, 0, create_occupy_task(unit, transport));
2587 }
2588
2589 Task *
create_pickup_task(Unit * unit,Unit * occ)2590 create_pickup_task(Unit *unit, Unit *occ)
2591 {
2592 Task *task = create_task(TASK_PICKUP);
2593
2594 task->args[0] = occ->id;
2595 /* add a waiting period also? */
2596 return task;
2597 }
2598
2599 void
push_pickup_task(Unit * unit,Unit * occ)2600 push_pickup_task(Unit *unit, Unit *occ)
2601 {
2602 add_task(unit, 0, create_pickup_task(unit, occ));
2603 }
2604
2605 Task *
create_produce_task(Unit * unit,int m,int n)2606 create_produce_task(Unit *unit, int m, int n)
2607 {
2608 Task *task = create_task(TASK_PRODUCE);
2609
2610 task->args[0] = m;
2611 task->args[1] = n;
2612 /* Third arg is amount produced, which starts at 0. */
2613 return task;
2614 }
2615
2616 void
push_produce_task(Unit * unit,int m,int n)2617 push_produce_task(Unit *unit, int m, int n)
2618 {
2619 add_task(unit, 0, create_produce_task(unit, m, n));
2620 }
2621
2622 /* Construct Task */
2623
2624 Task *
create_construct_task(Unit * unit,int u,int run,int transid,int x,int y)2625 create_construct_task(Unit *unit, int u, int run, int transid, int x, int y)
2626 {
2627 Task *task = NULL;
2628
2629 task = create_task(TASK_CONSTRUCT);
2630 task->args[0] = u;
2631 task->args[1] = run;
2632 task->args[2] = transid;
2633 task->args[3] = x;
2634 task->args[4] = y;
2635 return task;
2636 }
2637
2638 void
set_construct_task(Unit * unit,int u,int run,int transid,int x,int y)2639 set_construct_task(Unit *unit, int u, int run, int transid, int x, int y)
2640 {
2641 add_task(
2642 unit, CLEAR_AGENDA, create_construct_task(unit, u, run, transid, x, y));
2643 }
2644
2645 void
push_construct_task(Unit * unit,int u,int run,int transid,int x,int y)2646 push_construct_task(Unit *unit, int u, int run, int transid, int x, int y)
2647 {
2648 add_task(unit, 0, create_construct_task(unit, u, run, transid, x, y));
2649 }
2650
2651 /* Repair Task */
2652
2653 Task *
create_repair_task(Unit * unit,int id,int hp)2654 create_repair_task(Unit *unit, int id, int hp)
2655 {
2656 Task *task = NULL;
2657
2658 task = create_task(TASK_REPAIR);
2659 task->args[0] = id;
2660 task->args[1] = hp;
2661 return task;
2662 }
2663
2664 void
set_repair_task(Unit * unit,int id,int hp)2665 set_repair_task(Unit *unit, int id, int hp)
2666 {
2667 add_task(unit, CLEAR_AGENDA, create_repair_task(unit, id, hp));
2668 }
2669
2670 void
push_repair_task(Unit * unit,int id,int hp)2671 push_repair_task(Unit *unit, int id, int hp)
2672 {
2673 add_task(unit, 0, create_repair_task(unit, id, hp));
2674 }
2675
2676 /* This routine sets up a task to develop a unit of the given type. */
2677
2678 Task *
create_develop_task(Unit * unit,int u2,int n)2679 create_develop_task(Unit *unit, int u2, int n)
2680 {
2681 Task *task = create_task(TASK_DEVELOP);
2682
2683 task->args[0] = u2;
2684 task->args[1] = n;
2685 return task;
2686 }
2687
2688 void
set_develop_task(Unit * unit,int u2,int techgoal)2689 set_develop_task(Unit *unit, int u2, int techgoal)
2690 {
2691 add_task(unit, CLEAR_AGENDA, create_develop_task(unit, u2, techgoal));
2692 }
2693
2694 void
push_develop_task(Unit * unit,int u2,int techgoal)2695 push_develop_task(Unit *unit, int u2, int techgoal)
2696 {
2697 add_task(unit, 0, create_develop_task(unit, u2, techgoal));
2698 }
2699
2700 Task *
create_resupply_task(Unit * unit,int m)2701 create_resupply_task(Unit *unit, int m)
2702 {
2703 Task *task = create_task(TASK_RESUPPLY);
2704
2705 task->args[0] = m;
2706 return task;
2707 }
2708
2709 void
set_resupply_task(Unit * unit,int m)2710 set_resupply_task(Unit *unit, int m)
2711 {
2712 add_task(unit, CLEAR_AGENDA, create_resupply_task(unit, m));
2713 }
2714
2715 Task *
create_sentry_task(Unit * unit,int n)2716 create_sentry_task(Unit *unit, int n)
2717 {
2718 Task *task = create_task(TASK_SENTRY);
2719
2720 task->args[0] = n;
2721 return task;
2722 }
2723
2724 void
set_sentry_task(Unit * unit,int n)2725 set_sentry_task(Unit *unit, int n)
2726 {
2727 add_task(unit, CLEAR_AGENDA, create_sentry_task(unit, n));
2728 }
2729
2730 void
push_sentry_task(Unit * unit,int n)2731 push_sentry_task(Unit *unit, int n)
2732 {
2733 add_task(unit, 0, create_sentry_task(unit, n));
2734 }
2735
2736 extern int parse_location(Side *side, char *arg, int *xp, int *yp);
2737
2738 extern Unit *parse_unit(Side *side, char *arg);
2739
2740 /* Find a unit with the given name, either alive or dead. */
2741
2742 Unit *
parse_unit(Side * side,char * nm)2743 parse_unit(Side *side, char *nm)
2744 {
2745 Unit *unit;
2746
2747 if (empty_string(nm))
2748 return NULL;
2749 for_all_side_units(side, unit) {
2750 if (alive(unit) && unit->name != NULL && strcmp(unit->name, nm) == 0)
2751 return unit;
2752 }
2753 /* Under some circumstances, we can refer to other sides' units by name. */
2754 for_all_units(unit) {
2755 if (alive(unit)
2756 && unit->side != side
2757 && unit->name != NULL
2758 && strcmp(unit->name, nm) == 0
2759 && side_sees_image(side, unit))
2760 return unit;
2761 }
2762 return NULL;
2763 }
2764
2765 /* Given a textual description of a location, compute an x,y for it
2766 if possible. */
2767
2768 int
parse_location(Side * side,char * arg,int * xp,int * yp)2769 parse_location(Side *side, char *arg, int *xp, int *yp)
2770 {
2771 char *arg2;
2772 Unit *unit;
2773
2774 *xp = strtol(arg, &arg2, 10);
2775 if (arg != arg2 && *arg2 == ',') {
2776 *yp = strtol(arg2 + 1, &arg, 10);
2777 if (arg2 + 1 != arg)
2778 return TRUE;
2779 } else if ((unit = parse_unit(side, arg)) != NULL) {
2780 *xp = unit->x; *yp = unit->y;
2781 return TRUE;
2782 }
2783 notify(side, "location \"%s\" not recognized", arg);
2784 return FALSE;
2785 }
2786
2787 /* Given a string describing a task that has been entered in
2788 by a player, generate a task object and return the rest
2789 of the string, if NULL if failure. */
2790
2791 char *
parse_task(Side * side,char * str,Task ** taskp)2792 parse_task(Side *side, char *str, Task **taskp)
2793 {
2794 int tasktype, i, x, y, n, dir, u, taskargs[MAXTASKARGS], numargs;
2795 char *arg, *arg2, substr[BUFSIZE], *rest, *argtypes;
2796 Unit *unit;
2797
2798 *taskp = NULL;
2799 rest = get_next_arg(str, substr, &arg);
2800 /* Recognize special cases of task types first. */
2801 if (strcmp(arg, "nil") == 0 || strcmp(arg, "nothing") == 0) {
2802 /* NULL task with non-NULL return indicates order cancellation. */
2803 *taskp = NULL;
2804 return rest;
2805 } else if (strcmp(arg, "move-near") == 0) {
2806 rest = get_next_arg(rest, substr, &arg);
2807 if (parse_location(side, arg, &x, &y)) {
2808 rest = get_next_arg(rest, substr, &arg);
2809 n = strtol(arg, &arg2, 10);
2810 *taskp = create_move_to_task(NULL, x, y, n);
2811 return rest;
2812 }
2813 }
2814 tasktype = lookup_task_type(arg);
2815 if (tasktype < 0) {
2816 notify(side, "task type \"%s\" not recognized", arg);
2817 return NULL;
2818 }
2819 switch (tasktype) {
2820 case TASK_MOVE_TO:
2821 rest = get_next_arg(rest, substr, &arg);
2822 if (parse_location(side, arg, &x, &y)) {
2823 *taskp = create_move_to_task(NULL, x, y, 0);
2824 return rest;
2825 } else {
2826 return NULL;
2827 }
2828 break;
2829 default:
2830 argtypes = taskdefns[tasktype].argtypes;
2831 numargs = strlen(argtypes);
2832 for (i = 0; i < numargs; ++i)
2833 taskargs[i] = 0;
2834 rest = get_next_arg(rest, substr, &arg);
2835 for (i = 0; i < numargs; ++i) {
2836 if (argtypes[i] == 'x' && argtypes[i+1] == 'y') {
2837 /* If there are two arguments that are together a position,
2838 interpret both together. */
2839 if (parse_location(side, arg, &x, &y)) {
2840 taskargs[i] = x; taskargs[i + 1] = y;
2841 ++i;
2842 } else {
2843 return NULL;
2844 }
2845 } else if (argtypes[i] == 'd') {
2846 char *mydirchars = "ulnbhy"; /* (a local copy of ui.c thing) */
2847 /* Match on names or chars for directions. */
2848 for_all_directions(dir) {
2849 if (strcmp(arg, dirnames[dir]) == 0) {
2850 taskargs[i] = dir;
2851 goto nextarg;
2852 }
2853 if (strlen(arg) == 1 && arg[0] == mydirchars[dir]) {
2854 taskargs[i] = dir;
2855 goto nextarg;
2856 }
2857 }
2858 notify(side, "direction \"%s\" not recognized", arg);
2859 } else if (argtypes[i] == 'u') {
2860 u = utype_from_name(arg);
2861 if (u != NONUTYPE) {
2862 taskargs[i] = u;
2863 } else {
2864 notify(side, "unit type \"%s\" not recognized", arg);
2865 }
2866 } else if (argtypes[i] == 'U') {
2867 unit = parse_unit(side, arg);
2868 if (unit != NULL) {
2869 taskargs[i] = unit->id;
2870 } else {
2871 notify(side, "unit called \"%s\" not recognized", arg);
2872 }
2873 } else {
2874 /* Just collect an integer and stuff it. */
2875 taskargs[i] = strtol(arg, &arg2, 10);
2876 if (arg == arg2) {
2877 notify(side, "argument \"%s\" not recognized", arg);
2878 }
2879 }
2880 nextarg:
2881 rest = get_next_arg(str, substr, &arg);
2882 /* (should check for end of command or not?) */
2883 }
2884 *taskp = create_task((TaskType)tasktype);
2885 for (i = 0; i < numargs; ++i) {
2886 (*taskp)->args[i] = taskargs[i];
2887 }
2888 return rest;
2889 }
2890 }
2891
2892 /* Describe a task succinctly - use for debugging only. */
2893
2894 char *
task_desig(Task * task)2895 task_desig(Task *task)
2896 {
2897 int i, slen;
2898 char *argtypes;
2899
2900 if (taskbuf == NULL)
2901 taskbuf = (char *)xmalloc(BUFSIZE);
2902 if (task) {
2903 sprintf(taskbuf, "{%s", taskdefns[task->type].name);
2904 argtypes = taskdefns[task->type].argtypes;
2905 slen = strlen(argtypes);
2906 for (i = 0; i < slen; ++i) {
2907 tprintf(taskbuf, "%c%d", (i == 0 ? ' ' : ','), task->args[i]);
2908 }
2909 tprintf(taskbuf, " x %d", task->execnum);
2910 if (task->retrynum > 0) {
2911 tprintf(taskbuf, " fail %d", task->retrynum);
2912 }
2913 strcat(taskbuf, "}");
2914 } else {
2915 sprintf(taskbuf, "no task");
2916 }
2917 return taskbuf;
2918 }
2919
2920