/* Unit task execution and general task functions.
Copyright (C) 1992-2000 Stanley T. Shebs.
+ Copyright (C) 2004-2005 Eric A. McDonald.
Xconq is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "conq.h"
#include "kernel.h"
+/* (The following inclusion is only temporary if we end up proceeding
+ with the extraction of all AI code from this file.) */
+#include "aiutil.h"
+
+#include "aiunit.h"
+#include "aiunit2.h"
+
/* This is the number of tasks to allocate initially. More will always be
allocated as needed, so this should be a "reasonable" value. */
#define INITMAXTASKS 100
#endif
-#define CLEAR_AGENDA 99
-
enum choicestate {
eitherway,
leftthenright,
static int test_for_buildable(int x, int y);
static int collect_test(int x, int y);
static int extractable_test(int x, int y);
-static int repair_test(int x, int y);
-static int resupply_test(int x, int y);
static TaskOutcome execute_task_aux(Unit *unit, Task *task);
+
+//! Perform a toolup subtask on behalf of a construct/build task.
+static TaskOutcome do_toolup_subtask(Unit *constructor, int uc);
+
static TaskOutcome do_approach_subtask(Unit *unit, Task *task, int tx, int ty, int *statep);
static void allocate_task_block(void);
-static int fire_can_damage(Unit *unit, Unit *unit2);
+#if (0)
static Unit *repair_here(int x, int y);
-static Unit *aux_resupply_here(Unit *unit);
+#endif
static Unit *resupply_here(int x, int y);
-static int can_auto_resupply_self(Unit *unit, int *materials, int numtypes);
/* Declare all the task functions. */
static Unit *tmpbuilder, *tmpbuildunit;
+/* Used by the resupply code. */
+
+int *lowm = NULL;
+
+int numlow = 0;
+
+static int tmpx, tmpy;
+
+static int *foundm;
+
/* Allocate an initial collection of task objects. */
void
completion for a given number of units of a given type. */
static TaskOutcome
-do_build_task(Unit *unit, Task *task)
-{
- int u = unit->type, dir, tmpdir, nx, ny, tp, rslt;
- int x = unit->x, y = unit->y, x2, y2;
- int u2 = task->args[0], run = task->args[3];
- Unit *unit2 = NULL;
- Side *us = unit->side;
-
- if (task->args[2] >= run) {
- return TASK_IS_COMPLETE;
+do_build_task(Unit *builder, Task *task)
+{
+ int rslt = A_ANY_OK;
+ int idc = -1;
+ int uc = NONUTYPE;
+ Unit *buildee = NULL;
+ Side *side = NULL;
+
+ assert_error(is_active(builder),
+ "AI: Attempted to build with an inactive unit");
+ assert_warning_return(task, "AI: Attempted to run invalid construct task",
+ TASK_IS_COMPLETE);
+ side = builder->side;
+ // Attempt to retrieve buildee unit from ID.
+ idc = task->args[0];
+ buildee = find_unit(idc);
+ if (!in_play(buildee))
+ return TASK_FAILED;
+ if (fullsized(buildee))
+ return TASK_IS_COMPLETE;
+ uc = buildee->type;
+ builder->creation_id = -1;
+ // If research or development is needed, then we should not be in this code.
+ if (!side_can_build(side, uc))
+ return TASK_FAILED;
+ // We must handle tooling, if necessary.
+ if (!has_enough_tooling(builder, uc))
+ return do_toolup_subtask(builder, uc);
+ // Try performing build action.
+ if (valid(rslt = can_build(builder, builder, buildee))) {
+ prep_build_action(builder, builder, buildee);
+ return TASK_PREPPED_ACTION;
+ }
+ // Wait for more materials or ACP, if necessary.
+ // TODO: Proactively accumulate materials, if possible.
+ if ((A_ANY_NO_MATERIAL == rslt) || (A_ANY_NO_ACP == rslt)) {
+ set_unit_reserve(builder->side, builder, TRUE, FALSE);
+ return TASK_IS_INCOMPLETE;
}
+ return TASK_FAILED;
+#if (0) // HACKING NOTE: Old devel logic. Keep around for now.
/* See if our technology needs improvement in order to build this
type. */
if (is_unit_type(u2)
&& u_tech_to_build(u2) > 0
- && us->tech[u2] < u_tech_to_build(u2)) {
+ && us->tech[u2] < u_tech_to_build(u2)) {
if (uu_acp_to_develop(u, u2) > 0) {
push_develop_task(unit, u2, u_tech_to_build(u2));
return TASK_IS_INCOMPLETE;
return TASK_FAILED;
}
}
- /* See if we need to toolup to work on this type. */
- if (is_unit_type(u2)) {
- tp = (unit->tooling ? unit->tooling[u2] : 0);
- if (tp < uu_tp_to_build(u, u2)) {
- if (uu_acp_to_toolup(u, u2) > 0) {
- if (valid(check_toolup_action(unit, unit, u2))) {
- prep_toolup_action(unit, unit, u2);
- return TASK_PREPPED_ACTION;
- } else {
- /* We get three trys to toolup before giving up. */
- return (task->execnum < 3 ? TASK_IS_INCOMPLETE : TASK_FAILED);
- }
- } else {
- /* Can't do the necessary toolup. */
- return TASK_FAILED;
- }
- }
- }
- /* Check out the unit supposedly in progress. */
- if (task->args[1] != 0) {
- unit2 = find_unit(task->args[1]);
- if (in_play(unit2) && unit2->type == u2) {
- if (fullsized(unit2)) {
- /* Clear the reference to the unit. */
- task->args[1] = 0;
- ++(task->args[2]);
- if (task->args[2] >= run) {
- return TASK_IS_COMPLETE;
- }
- }
- }
- }
- /* Find something to work on. */
- unit2 = find_unit_to_complete(unit, task);
- /* Try to pick up an optional location. */
- x2 = task->args[4]; y2 = task->args[5];
- if (!inside_area(x2, y2)) {
- x2 = x; y2 = y;
- }
- /* No incomplete unit found, so try to create one. */
- if (unit2 == NULL) {
- /* First try to create as occupant, but not if an explicit
- location has been supplied. */
- if ((x2 == x && y2 == y)
- && valid((rslt = check_create_in_action(unit, unit, u2, unit)))) {
- prep_create_in_action(unit, unit, u2, unit);
- return TASK_PREPPED_ACTION;
- } else if (rslt == A_ANY_NO_MATERIAL) {
- /* Assume that the builder is accumulating necessary materials,
- and just wait. */
- set_unit_reserve(unit->side, unit, TRUE, FALSE);
- return TASK_IS_INCOMPLETE;
- } else if (valid((rslt = check_create_at_action(unit, unit, u2,
- x2, y2, 0)))) {
- prep_create_at_action(unit, unit, u2, x2, y2, 0);
- return TASK_PREPPED_ACTION;
- } else if (rslt == A_ANY_NO_MATERIAL) {
- /* Assume that the builder is accumulating necessary materials,
- and just wait. */
- set_unit_reserve(unit->side, unit, TRUE, FALSE);
- return TASK_IS_INCOMPLETE;
- } else {
- /* As a fallback, try creating in an adjacent cell. */
- /* (should try any cell within create range) */
- for_all_directions_randomly(dir,tmpdir) {
- if (interior_point_in_dir(x, y, dir, &nx, &ny)
- && valid(check_create_at_action(unit, unit, u2,
- nx, ny, 0))) {
- prep_create_at_action(unit, unit, u2, nx, ny, 0);
- return TASK_PREPPED_ACTION;
- }
- }
- return TASK_FAILED;
- }
- } else {
- /* Record the incomplete unit's id for use the next time around. */
- task->args[1] = unit2->id;
- }
- /* We have an incomplete unit to work on, try to do a build action. */
- if (valid(check_build_action(unit, unit, unit2))) {
- prep_build_action(unit, unit, unit2);
- return TASK_PREPPED_ACTION;
- } else {
- /* Try three times before giving up. */
- return (task->execnum < 3 ? TASK_IS_INCOMPLETE : TASK_FAILED);
- }
+#endif
}
Unit *
do_hit_position_task(Unit *unit, Task *task)
{
int u = unit->type, tx, ty, dist;
-
+ UnitView *uview = NULL;
+
+ /* (Temporary hack. Ask the planner to re-evaluate continuation of
+ this task. If it wants to, then it will issue a new one. If not,
+ then move on and do something more productive.) */
+ /* (A better solution would be to add a new arg to the task that would
+ tell it the number of times to attempt execution before returning to
+ the planner for guidance.) */
+ if (task->execnum > 3)
+ return TASK_IS_COMPLETE;
/* This is to hit a given place. */
/* (ask for a number of hits?) */
tx = task->args[0]; ty = task->args[1];
dist = distance(tx, ty, unit->x, unit->y);
+ /* Make sure that we are not going to hit a friendly unit. */
+ /* (What if we have unit with vision-range -1 in cell and they aren't
+ seen by any of our own units?) */
+ for_all_view_stack_with_occs(unit->side, tx, ty, uview) {
+ if (!enemy_side(unit->side, side_n(uview->siden)))
+ return TASK_FAILED;
+ }
+ /* Try performing a fire-into action. */
if (valid(check_fire_into_action(unit, unit, tx, ty, 0, -1))) {
prep_fire_into_action(unit, unit, tx, ty, 0, -1);
return TASK_PREPPED_ACTION;
UnitView *uview;
Side *us = unit->side;
+ /* Temporary hack. Ask the planner to re-evaluate continuation of
+ this task. If it wants to, then it will issue a new one. If not,
+ then move on and do something more productive. */
+ /* A better solution would be to add a new arg to the task that would
+ tell it the number of times to attempt execution before returning to
+ the planner for guidance. */
+ if (task->execnum > 3)
+ return TASK_IS_COMPLETE;
/* This is to hit a (given type/side of) unit at a given place. */
- /* (should add spec for number of hits to attempt?) */
- tx = task->args[0]; ty = task->args[1];
- tu = task->args[2]; ts = task->args[3];
+ tx = task->args[0];
+ ty = task->args[1];
+ tu = task->args[2];
+ ts = task->args[3];
dist = distance(tx, ty, unit->x, unit->y);
- if (dist <= 1 /* direct attack range */) {
- if (can_attack(unit)) {
+ if (dist <= 1) {
+ if (can_attack_any(unit, unit)) {
for_all_view_stack(us, tx, ty, uview) {
- if (!trusted_side(us, view_side(uview))
- && (tu == NONUTYPE || tu == view_type(uview))) {
- /* (should check designated side to hit, if defined) */
+ if (ts == uview->siden && tu == uview->type) {
unit2 = view_unit(uview);
- if (unit2 != NULL
- && valid(check_attack_action(unit, unit, unit2, 100))
- && attack_can_damage_or_capture(unit, unit2)) {
+ if (unit2
+ && valid(check_attack_action(unit, unit, unit2, 100))) {
prep_attack_action(unit, unit, unit2, 100);
return TASK_PREPPED_ACTION;
}
/* Maybe we can overrun, but not if our cell is known to
be clear of enemies. */
if (dist > 0
- && valid(check_overrun_action(unit, unit, tx, ty, unit->z,
- 100))) {
+ && valid(
+ check_overrun_action(unit, unit, tx, ty, unit->z, 100))) {
prep_overrun_action(unit, unit, tx, ty, unit->z, 100);
return TASK_PREPPED_ACTION;
}
}
/* Might be able to fire at pointblank range. */
- if (can_fire(unit) && dist >= u_range_min(u)) {
+ if (can_fire_at_any(unit, unit) && dist >= u_range_min(u)) {
for_all_view_stack(us, tx, ty, uview) {
- if (!trusted_side(us, view_side(uview))
- && (tu == NONUTYPE || tu == view_type(uview))
- /* (should check designated side to hit, if defined) */
- ) {
+ if (ts == uview->siden && tu == uview->type) {
unit2 = view_unit(uview);
- if (unit2 != NULL
- && valid(check_fire_at_action(unit, unit, unit2, -1))
- /* Also count with capture when firing at
- pointblank range. */
- && fire_can_damage_or_capture(unit, unit2)) {
+ if (unit2
+ && valid(check_fire_at_action(unit, unit, unit2, -1))) {
prep_fire_at_action(unit, unit, unit2, -1);
return TASK_PREPPED_ACTION;
}
/* If we're within firing range, attempt to fire. */
if (dist <= u_range(u)) {
for_all_view_stack(us, tx, ty, uview) {
- int danger = FALSE;
-
- unit2 = view_unit(uview);
- if (unit2 == NULL)
- continue;
- /* Check if we may safely move in and attempt to capture a unit. */
-
- for_all_stack_with_occs(tx, ty, unit2) {
- int u2 = unit2->type;
-
- if (trusted_side(us, unit2->side))
- continue;
- if (!is_active(unit2))
- continue;
- if (u_acp(u2) + u_acp_to_fire(u2) <= 0)
- continue;
- if (uu_hit(u2, u) <= 0)
- continue;
- if (uu_damage(u2, u) <= 0)
- continue;
- /* Someone over there can still hit and damage us. */
- danger = TRUE;
- break;
- }
- if (!danger) {
- for_all_stack(tx, ty, unit2) {
- if (!trusted_side(us, unit2->side)
- && (tu == NONUTYPE || unit2->type == tu)
- && real_capture_chance(unit, unit2) > 0) {
- push_move_to_task(unit, tx, ty, dist);
- return TASK_IS_INCOMPLETE;
- }
- }
- }
- for_all_stack(tx, ty, unit2) {
- if (!trusted_side(us, unit2->side)
- && (tu == NONUTYPE || unit2->type == tu)
- /* (should check designated side to hit, if defined) */
- ) {
- if (valid(check_fire_at_action(unit, unit, unit2, -1))
- && fire_can_damage(unit, unit2)) {
- prep_fire_at_action(unit, unit, unit2, -1);
- return TASK_PREPPED_ACTION;
- }
+ if (ts == uview->siden && tu == uview->type) {
+ unit2 = view_unit(uview);
+ if (unit2 && valid(check_fire_at_action(unit, unit, unit2, -1))) {
+ prep_fire_at_action(unit, unit, unit2, -1);
+ return TASK_PREPPED_ACTION;
}
}
}
return TASK_FAILED;
}
-/* Return true if the unit can actually damage the other unit. */
-
-int
-attack_can_damage_or_capture(Unit *unit, Unit *unit2)
-{
- Unit *occ;
-
- if (!alive(unit) || !alive(unit2))
- return FALSE;
- if (capture_chance(unit->type, unit2->type, unit2->side) > 0)
- return TRUE;
- /* (should check for right kind of ammo) */
- if (uu_hit(unit->type, unit2->type) <= 0)
- return FALSE;
- /* Also take into account if we can damage any occs. Differs from
- attack code in that only visible occs are considered. */
- /* Return TRUE if we can damage the unit itself. */
- if (uu_damage(unit->type, unit2->type) > 0)
- return TRUE;
- /* Else check if we can hit and damage any of its occs. */
- for_all_occs_with_occs(unit2, occ) {
- int immune = FALSE;
- Unit *transport = occ->transport;
-
- /* Skip occs that we can't hit or damage. */
- if (uu_hit(unit->type, occ->type <= 0)
- || uu_damage(unit->type, occ->type <= 0))
- continue;
- /* Skip occs that we cannot see (no cheating - not even for the AI!) */
- if (!side_sees_image(unit->side, occ))
- continue;
- /* For vulnerable occs, check recursively that we also can hit
- all transports in the chain leading down to the occ. */
- while (transport) {
- if (uu_hit(unit->type, transport->type) <= 0) {
- immune = TRUE;
- break;
- }
- transport = transport->transport;
- }
- /* If we found an occ that is not immune we are done. */
- if (!immune)
- return TRUE;
- }
- /* We can't capture or hit anything. */
- return FALSE;
-}
-
-/* Return true if the unit can actually damage the other unit by
- firing at it. */
-
-int
-fire_can_damage(Unit *unit, Unit *unit2)
-{
- Unit *occ;
-
- if (!alive(unit) || !alive(unit2))
- return FALSE;
- /* (should check for right kind of ammo) */
- if (uu_hit(unit->type, unit2->type) <= 0)
- return FALSE;
- /* Also take into account if we can damage any occs. Differs from fire code
- in that only visible occs are considered. */
- /* Return TRUE if we can damage the unit itself. */
- if (uu_damage(unit->type, unit2->type) > 0)
- return TRUE;
- /* Else check if we can hit and damage any of its occs. */
- for_all_occs_with_occs(unit2, occ) {
-
- int immune = FALSE;
- Unit *transport = occ->transport;
-
- /* Skip occs that we can't hit or damage. */
- if (uu_hit(unit->type, occ->type <= 0)
- || uu_damage(unit->type, occ->type <= 0))
- continue;
- /* Skip occs that we cannot see (no cheating - not even for the AI!) */
- if (!side_sees_image(unit->side, occ))
- continue;
- /* For vulnerable occs, check recursively that we also can
- hit all transports in the chain leading down to the occ. */
- while (transport) {
- if (uu_hit(unit->type, transport->type) <= 0) {
- immune = TRUE;
- break;
- }
- transport = transport->transport;
- }
- /* If we found an occ that is not immune we are done. */
- if (!immune)
- return TRUE;
- }
- /* We can't damage anything. */
- return FALSE;
-}
-
-int
-fire_can_damage_or_capture(Unit *unit, Unit *unit2)
-{
- Unit *occ;
-
- if (!alive(unit) || !alive(unit2))
- return FALSE;
- /* Always return TRUE if we can capture the unit. */
- if (capture_chance(unit->type, unit2->type, unit2->side) > 0)
- return TRUE;
- /* If we can't hit the unit we can't hit any of its occs. */
- if (uu_hit(unit->type, unit2->type) <= 0)
- return FALSE;
- /* Return TRUE if we can damage the unit itself. */
- if (uu_damage(unit->type, unit2->type) > 0)
- return TRUE;
- /* Else check if we can hit and damage any of its occs. */
- for_all_occs_with_occs(unit2, occ) {
- int immune = FALSE;
- Unit *transport = occ->transport;
-
- /* Skip occs that we can't hit or damage. */
- if (uu_hit(unit->type, occ->type <= 0)
- || uu_damage(unit->type, occ->type <= 0))
- continue;
- /* Skip occs that we cannot see (no cheating - not even for the AI!) */
- if (!side_sees_image(unit->side, occ))
- continue;
- /* For vulnerable occs, check recursively that we also can
- hit all transports in the chain leading down to the occ. */
- while (transport) {
- if (uu_hit(unit->type, transport->type) <= 0) {
- immune = TRUE;
- break;
- }
- transport = transport->transport;
- }
- /* If we found an occ that is not immune we are done. */
- if (!immune)
- return TRUE;
- }
- /* We can't capture or hit anything. */
- return FALSE;
-}
-
-/* Probability that one real unit will capture another real unit with
- protection taken into account. Differs from real capture code in
- that only visible occupants are considered (no cheating!) */
-
-int
-real_capture_chance(Unit *unit, Unit *unit2)
-{
- int chance, prot;
- int u2 = unit2->type;
- int u = unit->type;
- Unit *unit3;
-
- chance = capture_chance(unit->type, unit2->type, unit2->side);
-
- /* Occupants can protect the unit. */
- for_all_occupants(unit2, unit3) {
- if (is_active(unit3)
- && side_sees_image(unit->side, unit3)) {
- prot = uu_protection(unit3->type, u2);
- if (prot != 100)
- chance = (chance * prot) / 100;
- }
- }
- /* And so can its neighbours. */
- for_all_stack(unit2->x, unit2->y, unit3) {
- if (unit3 != unit2
- && is_active(unit3)
- && side_sees_image(unit->side, unit3)
- && unit3->side == unit2->side) {
- prot = uu_stack_protection(unit3->type, u2);
- if (prot != 100)
- chance = (chance * prot) / 100;
- }
- }
- for_all_stack_with_occs(unit2->x, unit2->y, unit3) {
- if (is_active(unit3)
- && side_sees_image(unit->side, unit3)
- /* We also extend protection to our buddies! */
- && trusted_side(unit3->side, unit2->side)) {
- /* This is when a unit, such as triple-A, provides unique
- protection against a specific attacker, such as
- bombers, to all other units in the cell. */
- prot = uu_cellwide_protection_against(unit3->type, u);
- if (prot != 100)
- chance = (chance * prot) / 100;
- /* This is when a unit (such as a garrison) specifically
- protects a second unit, such as a fort (but not other
- nearby units), against all forms of attack. It thus
- works the same way as uu_protection and
- uu_stack_protection. */
- prot = uu_cellwide_protection_for(unit3->type, u2);
- if (prot != 100)
- chance = (chance * prot) / 100;
- }
- }
- return chance;
-}
-
int
target_visible(Unit *unit, Task *task)
{
tx = task->args[0]; ty = task->args[1];
tu = task->args[2]; ts = task->args[3];
- for_all_view_stack(us, tx, ty, uview) {
- if (!trusted_side(us, view_side(uview))
- && (tu == NONUTYPE || tu == view_type(uview)))
- /* (should test task's target side also?) */
- return TRUE;
+ for_all_view_stack_with_occs(us, tx, ty, uview) {
+ if (ts == uview->siden
+ && (tu == NONUTYPE || tu == uview->type)) {
+ return TRUE;
+ }
}
return FALSE;
}
do_move_dir_task(Unit *unit, Task *task)
{
int dir, tx, ty;
- Unit *unit2;
-
+ Unit *unit2 = NULL;
+ UnitView *uview = NULL;
+ Side *side = NULL;
+ int u = NONUTYPE;
+ int rslt = A_ANY_CANNOT_DO;
+ int curmp = 0, fullmp = 0;
+
+ side = unit->side;
+ u = unit->type;
if ((task->args[1])-- > 0) {
dir = task->args[0];
+ /* Is the next cell even valid? */
if (!point_in_dir(unit->x, unit->y, dir, &tx, &ty)) {
return TASK_FAILED;
}
- if (unit_at(tx, ty)) {
- for_all_stack(tx, ty, unit2) {
- if (unit_trusts_unit(unit, unit2) /* but a spy could enter untrusted unit... */
- && valid(check_enter_action(unit, unit, unit2))) {
+ /* Can we squeeze into the cell in any way, shape, or form? */
+ if (!side_thinks_it_can_put_type_at(side, u, tx, ty))
+ return TASK_FAILED;
+ /* Can the unit move into the cell? */
+ if (valid(check_move_action(unit, unit, tx, ty, 0))) {
+ /* (Probably need to set reserve here for low ACP and MP cases,
+ like we do for the enter action below.) */
+ prep_move_action(unit, unit, tx, ty, 0);
+ return TASK_PREPPED_ACTION;
+ }
+ /* Can the unit occupy another unit in the cell? */
+ else if (unit_view_at(side, tx, ty)) {
+ for_all_view_stack(side, tx, ty, uview) {
+ /* (A spy could enter untrusted unit...) */
+ if (!trusted_side(side, side_n(uview->siden)))
+ continue;
+ unit2 = view_unit(uview);
+ rslt = check_enter_action(unit, unit, unit2);
+ /* If not enough ACP left this turn, then wait. */
+ if ((A_ANY_NO_ACP == rslt)
+ && (can_have_enough_acp(unit,
+ uu_acp_to_enter(u, uview->type)))) {
+ set_unit_reserve(side, unit, TRUE, FALSE);
+ return TASK_IS_INCOMPLETE;
+ }
+ /* If not enough MP left this turn, then wait. */
+ else if (A_MOVE_NO_MP == rslt) {
+ if (can_be_actor(unit)) {
+ curmp = (unit->act->acp * unit_speed(unit, tx, ty))
+ / 100 + u_free_mp(u);
+ fullmp = ((total_acp_for_turn(unit) - u_acp_min(u)) *
+ unit_speed(unit, tx, ty)) / 100 +
+ u_free_mp(u);
+ }
+ else {
+ curmp = 0; fullmp = 0;
+ }
+ if (fullmp > curmp) {
+ set_unit_reserve(side, unit, TRUE, FALSE);
+ return TASK_IS_INCOMPLETE;
+ }
+ }
+ /* Else, we can enter now, then do it. */
+ else if (valid(rslt)) {
prep_enter_action(unit, unit, unit2);
return TASK_PREPPED_ACTION;
}
- }
+ } /* for_all_view_stack */
return TASK_FAILED;
- } else if (valid(check_move_action(unit, unit, tx, ty, 0))) {
- prep_move_action(unit, unit, tx, ty, 0);
- return TASK_PREPPED_ACTION;
- } else {
+ }
+ else {
return TASK_FAILED;
}
- } else {
+ } /* if ((task->args[1])-- > 0) */
+ /* Specified number of moves has already been executed. */
+ else {
return TASK_IS_COMPLETE;
}
}
+#if (0)
+/* If we are sitting in the same cell or transport as a resupply source,
+ resupply is "free" for both us and the resupply source, and the
+ resupply source has some supplies that we could stock up on, then
+ swipe the supplies before moving on. */
+int
+stock_up_materials_for_free(Unit *unit)
+{
+ Unit *supplier = NULL;
+ int u = NONUTYPE, u2 = NONUTYPE;
+ int m = NONMTYPE;
+
+ u = unit->type;
+ if (!has_full_amount_of_all_materials(unit)) {
+ for_all_stack(unit->x, unit->y, supplier) {
+ if (enemy_side(unit->side, supplier->side))
+ continue;
+ if (!(supplier->supply))
+ continue;
+ u2 = supplier->type;
+ for_all_material_types(m) {
+ int dir;
+ /* TODO: Finish implementing. */
+ }
+ }
+ }
+ return FALSE;
+}
+#endif
+
+static int
+could_directly_board_ferry(Unit * unit, Unit * transport)
+{
+ return (can_occupy(unit, transport) &&
+ valid(check_enter_action(unit, unit, transport)));
+}
+
/* The move-to task is the main way for units to get from point A to
point B. In addition to the destination, the task has a required
distance, so it will succeed if the unit is within that distance to
{
int dist, tx, ty, check;
Unit *unit2, *occ;
+ int canceltask = FALSE;
+ Task *tmptask = NULL;
/* This task is to get to a designated location somehow. */
tx = task->args[0]; ty = task->args[1];
dist = distance(tx, ty, unit->x, unit->y);
if (dist <= task->args[3]) {
+#if (0)
+ /* If unit is a mobile transport, then don't get jittery. */
+ if (unit->occupant && mobile(unit->type)) {
+ for_all_occupants(unit, occ) {
+ if (has_acp_left(occ) && unit->plan
+ && !unit->plan->reserve) {
+ /* delay_unit(unit, TRUE); */
+ return TASK_IS_INCOMPLETE;
+ }
+ }
+ }
+#endif
+ return TASK_IS_COMPLETE;
+ }
+#if (0)
+ /* Try to top off supplies if supplier around and supply transfer
+ does not cost any ACP. */
+ if (stock_up_materials_for_free(unit))
+ return TASK_IS_INCOMPLETE;
+#endif
+ /* Abort the move-to, if it is associated with a resupply task that is
+ no longer necessary. Some games will run supply lines while a unit is
+ en route to resupply, and thereby negate the need to explicitly
+ move to a resupply point and resupply. */
+ if (task->next && (TASK_RESUPPLY == task->next->type)) {
+ if (NONMTYPE == task->next->args[0]) {
+ if (has_full_amount_of_all_materials(unit))
+ canceltask = TRUE;
+ }
+ else if (has_full_amount_of_material(unit, task->next->args[0]))
+ canceltask = TRUE;
+ if (canceltask) {
+ tmptask = task->next;
+ task->next = task->next->next;
+ free_task(tmptask);
+ /* Lie, so that we can hopefully replan or retask. */
+ /* (Should have new TaskResult, TASK_CANCELLED.) */
+ return TASK_IS_COMPLETE;
+ }
+ }
+#if (0)
+ /* Abort the move-to, if it is not associated with a resupply
+ or entry task, and the unit is AI-controlled,
+ and the distance to the destination
+ is > the real operating range of the unit,
+ or unit has less fuel than its doctrine threshold,
+ and the unit is not in a transport. */
+ if ((!task->next
+ || (task->next
+ && ((task->next->type != TASK_RESUPPLY)
+ && (task->next->type != TASK_OCCUPY))))
+ && ai_controlled(unit)
+ && (((dist - task->args[3]) > real_operating_range_best(unit))
+ || past_halfway_point(unit))
+ && !unit->transport) {
+ clear_task_agenda(unit);
+ /* Lie, so that we can hopefully replan or retask. */
+ /* (Should have new TaskResult, TASK_CANCELLED.) */
return TASK_IS_COMPLETE;
}
+#endif
switch (dist) {
case 0:
- /* We're there already, nothing more to do. */
- return TASK_IS_COMPLETE;
+ /* We're there already, nothing more to do. */
+ return TASK_IS_COMPLETE;
case 1:
/* Adjacent cell, do a single move. */
/* But first, if there are units here already, prefer to
try to move. */
if (check == A_MOVE_NO_MP) {
notify(unit->side,
- "%s has insufficient ACP to move this turn; waiting until next",
+ "%s is resting until next turn.",
unit_handle(unit->side, unit));
set_unit_reserve(unit->side, unit, TRUE, FALSE);
return TASK_IS_INCOMPLETE;
/* If on mobile transport, let it handle things. */
if (unit->transport != NULL
&& mobile(unit->transport->type)
- /* and the transport is not blocked */
- && flip_coin()) {
+ /* and the transport is not stuck */
+ && probability(95)) {
set_unit_reserve(unit->side, unit, TRUE, FALSE);
return TASK_IS_INCOMPLETE;
}
- numdirs = choose_move_dirs(unit, tx, ty, TRUE,
- plausible_move_dir, sort_directions, dirs);
+ numdirs =
+ choose_move_dirs(unit, tx, ty, TRUE, plausible_move_dir,
+ sort_directions, dirs);
+ if (!numdirs)
+ numdirs =
+ choose_move_dirs(unit, tx, ty, FALSE, plausible_move_dir,
+ sort_directions, dirs);
for (i = 0; i < numdirs; ++i) {
point_in_dir(unit->x, unit->y, dirs[i], &nx, &ny);
for_all_stack(nx, ny, unit2) {
if (can_occupy(unit, unit2)) {
if (valid(check_enter_action(unit, unit, unit2))) {
prep_enter_action(unit, unit, unit2);
- /* We (probably) made forward progress, so reopen choice of dirs. */
+ /* We (probably) made forward progress,
+ so reopen choice of dirs. */
*statep = eitherway;
return TASK_PREPPED_ACTION;
} else {
/* If we're just short on mp, wait until the next turn to try to move. */
if (check == A_MOVE_NO_MP) {
if (unit->side)
- notify(unit->side, "%s has insufficient MP to move this turn; waiting until next",
+ notify(unit->side, "%s is resting until next turn.",
unit_handle(unit->side, unit));
set_unit_reserve(unit->side, unit, TRUE, FALSE);
return TASK_IS_INCOMPLETE;
do_occupy_task(Unit *unit, Task *task)
{
int dist;
- Unit *transport = find_unit(task->args[0]);
+ Unit *transport = NULL;
+ int tx = -1;
+ int ty = -1;
+
+ transport = find_unit_dead_or_alive(task->args[0]);
+ /* Transport may have left play in the interim between attempted task
+ executions.
+ */
+ if (!transport || !in_play(transport)) {
+ if (transport) {
+ DMprintf("%s attempted to enter out-of-play transport %s.\n",
+ unit_desig(unit), unit_desig(transport));
+ } else {
+ DMprintf("%s attempted to enter a bogus transport with id %d.\n",
+ unit_desig(unit), task->args[0]);
+ }
+ return TASK_FAILED;
+ }
- if (!in_play(transport))
- return TASK_FAILED;
+ tx = transport->x;
+ ty = transport->y;
/* (should also fail if we don't know where transport is anymore) */
if (unit->transport == transport) {
return TASK_IS_COMPLETE;
static TaskOutcome
do_pickup_task(Unit *unit, Task *task)
{
- Unit *occupant = find_unit(task->args[0]);
+ Unit *occupant = NULL;
- if (!in_play(occupant))
- return TASK_FAILED;
+ occupant = find_unit_dead_or_alive(task->args[0]);
+ /* Potential occupant may have left play in the interim between
+ attempted task executions. */
+ if (!occupant || !in_play(occupant)) {
+ if (occupant) {
+ DMprintf("%s attempted to pickup out-of-play unit %s.\n",
+ unit_desig(unit), unit_desig(occupant));
+ } else {
+ DMprintf("%s attempted to pickup a bogus unit with id %d.\n",
+ unit_desig(unit), task->args[0]);
+ }
+ return TASK_FAILED;
+ }
wake_unit(occupant->side, occupant, FALSE);
if (occupant->transport == unit)
return TASK_IS_COMPLETE;
}
}
}
- /* Compute how far out to look for a delivery point; be a little
- optimistic. */
- range = operating_range_best(unit->type);
+ /* Compute how far out to look for a delivery point. */
+ range = real_operating_range_best(unit);
if (search_around(unit->x, unit->y, range, collect_test,
&x2, &y2, 1)) {
/* (should find actual unit and chase it directly) */
for_all_stack(x, y, unit3) {
if (in_play(unit3)
&& unit3->side == unit->side
- && u_acp(unit3->type) == 0) {
+ && type_max_acp(unit3->type) == 0) {
if (valid(check_transfer_action(unit, unit3, m, 1, unit))) {
prep_transfer_action(unit, unit3, m, 1, unit);
return TASK_PREPPED_ACTION;
return FALSE;
}
-static TaskOutcome
-do_repair_task(Unit *unit, Task *task)
-{
- int x, y, u = unit->type, m, range = area.maxdim;
- int ux = unit->x, uy = unit->y;
- Unit *unit2;
+/* This function uses a global approach in that it checks if the unit
+and its destination are located in or are adjacent to the same region
+(sea or continent). Since the region area layer is precomputed during
+startup, this check is fast. There are certain simplistic assumptions
+built into the code, however. First, it is assumed that resupply and
+repair points for naval units can be on land (i.e. in ports) but not vice
+versa. Second, it is assumed that naval units always can enter such
+land-based resupply points, if they are adjacent to a liquid cell. */
+
+/* This code does not handle units on board of transports. A ground
+unit at sea will therefore fail to set a resupply or repair task since
+it cannot find a land path to a supply point. An air unit on board of a
+carrier will, however, be able to set a resupply task on land, since
+it can get there by itself. Ultimately, we should implement a way for
+occupants to set repair tasks and signal their transport where they
+need to go. In some games, occupants will leech supplies off their
+transport, so the latter will evetually run out of supplies and set
+its own resupply task. */
+
+/* The code does not check if a path to the destination really exists
+that our unit may follow. Posible problems: enemy units and hostile
+terrain. The fact that u_ground_mobile is set for a unit does not
+mean that it may traverse all possible ground terrain. Ultimately,
+the code should test for paths as well. */
- for_all_material_types(m) {
- if (um_consumption_per_move(u, m) > 0) {
- range = min(range, unit->supply[m] / um_consumption_per_move(u, m));
+int
+direct_access_to(int x, int y)
+{
+ int x1, y1, x2, y2;
+ int u = tmpunit->type;
+ int ux = tmpunit->x;
+ int uy = tmpunit->y;
+
+ /* Airborne units always have access everywhere. */
+ if (u_air_mobile(u)) {
+ return TRUE;
}
- }
- tmpunit = unit;
- /* (should use doctrine to decide when repairs sufficient) */
- if (unit->hp >= (u_hp(u) * 80) / 100) { /* what if unit is multi-part? */
- return TASK_IS_COMPLETE;
- } else if (u_hp_recovery(u) > 0) {
- if (0 /* too close to enemies */) {
- /* (should move away from danger) */
- } else {
- /* Just hang out and wait to get better. */
- set_unit_reserve(unit->side, unit, TRUE, FALSE);
+ /* Landborne units have access if both cells belong
+ to the same non-liquid region. */
+ if (u_ground_mobile(u)) {
+ if (!t_liquid(terrain_at(ux, uy))
+ && !t_liquid(terrain_at(x, y))) {
+ if (aref(area.landsea_regions, ux, uy)
+ == aref(area.landsea_regions, x, y)) {
+ return TRUE;
+ }
+ }
}
- return TASK_IS_INCOMPLETE;
- } else if (unit->transport != NULL /* and transport repairs */) {
- set_unit_reserve(unit->side, unit, TRUE, FALSE);
- return TASK_IS_INCOMPLETE;
- } else if ((unit2 = repair_here(ux, uy)) != NULL
- && unit2 != unit->transport) {
- prep_enter_action(unit, unit, unit2);
- return TASK_PREPPED_ACTION;
- } else if (search_around(ux, uy, range, repair_test, &x, &y, 1)) {
- /* (should collect actual unit and chase it directly) */
- push_move_to_task(unit, x, y, 0);
+ /* Seaborne units have access if both cells are adjacent
+ to the same liquid region. */
+ if (u_naval_mobile(u)) {
+ /* We are starting from a liquid cell. */
+ if (t_liquid(terrain_at(ux, uy))) {
+ /* Test if the destination belongs to the same region. */
+ if (aref(area.landsea_regions, ux, uy)
+ == aref(area.landsea_regions, x, y)) {
+ return TRUE;
+ }
+ /* Next test cells adjacent to the destination. */
+ for_all_cells_within_range(x, y, 1, x2, y2) {
+ if (aref(area.landsea_regions, ux, uy)
+ == aref(area.landsea_regions, x2, y2)) {
+ return TRUE;
+ }
+ }
+ /* We are starting from a land cell. */
+ } else {
+ /* Scan adjacent liquid cells. */
+ for_all_cells_within_range(ux, uy, 1, x1, y1) {
+ if (t_liquid(terrain_at(x1, y1))) {
+ /* Test if the destination belongs to the same region. */
+ if (aref(area.landsea_regions, x1, y1)
+ == aref(area.landsea_regions, x, y)) {
+ return TRUE;
+ }
+ /* Next test cells adjacent to the destination. */
+ for_all_cells_within_range(x, y, 1, x2, y2) {
+ if (aref(area.landsea_regions, x1, y1)
+ == aref(area.landsea_regions, x2, y2)) {
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+static TaskOutcome
+do_toolup_subtask(Unit *constructor, int uc)
+{
+ int rslt = A_ANY_OK;
+
+ assert_error(is_active(constructor),
+ "AI: Attempted to toolup an inactive unit");
+ assert_error(is_unit_type(uc),
+ "AI: Encountered and invalid unit type to toolup for");
+ // If by some strange coincidence we are inside this function,
+ // and constructor already has enough tooling,
+ // then get out.
+ if (has_enough_tooling(constructor, uc))
return TASK_IS_INCOMPLETE;
- } else {
- /* (should be able to signal interface usefully somehow) */
+ // If we cannot perform the toolup action, then the task must fail.
+ if (!valid(rslt =
+ can_toolup_for(constructor, constructor, uc))) {
+ // TODO: Should notify side.
return TASK_FAILED;
}
+ // Try to prep a toolup action.
+ if (prep_toolup_action(constructor, constructor, uc))
+ return TASK_PREPPED_ACTION;
+ return TASK_FAILED;
}
-Unit *
-repair_here(int x, int y)
-{
- Unit *unit;
-
- for_all_stack(x, y, unit) {
- if (unit != tmpunit
- && unit_trusts_unit(tmpunit, unit)
- && (uu_auto_repair(unit->type, tmpunit->type) > 0
- || 0 /* (should allow for explicit repair actions) */)) {
- return unit;
+static TaskOutcome
+do_construct_task(Unit *constructor, Task *task)
+{
+ int rslt = A_ANY_OK;
+ int uc = NONUTYPE;
+ int run = 0, x = -1, y = -1;
+ int transid = -1;
+ Unit *creation = NULL, *transport = NULL;
+ Side *side = NULL;
+ Task *tasks = NULL;
+
+ assert_error(is_active(constructor),
+ "AI: Attempted to construct with an inactive unit");
+ assert_warning_return(task, "AI: Attempted to run invalid construct task",
+ TASK_IS_COMPLETE);
+ side = constructor->side;
+ // Unpack task args.
+ uc = task->args[0];
+ run = task->args[1];
+ // If run length <= 0, then we're done.
+ if (0 >= run)
+ return TASK_IS_COMPLETE;
+ transid = task->args[2];
+ // If no transport or out-of-play transport, then try getting coords.
+ transport = find_unit(transid);
+ if (!in_play(transport)) {
+ x = task->args[3]; y = task->args[4];
+ // If coords are also invalid, then task fails.
+ if (!inside_area(x, y))
+ return TASK_FAILED;
+ }
+ // If constructor has its creation ID set,
+ // then see if it matches an incomplete unit.
+ // If so, then decrement the construction run length,
+ // clear the creation ID, and push a build task, if necessary.
+ creation = find_unit(constructor->creation_id);
+ if (in_play(creation)) {
+ constructor->creation_id = -1;
+ --task->args[1];
+ // Push or set build task, if creation is incomplete.
+ // Push, if we have more construction planned.
+ // Set, if this is last of construction run.
+ if (task->args[1])
+ push_build_task(
+ constructor, creation->id, u_cp(creation->type));
+ else
+ set_build_task(
+ constructor, creation->id, u_cp(creation->type));
+ tasks = (constructor->plan ? constructor->plan->tasks : NULL);
+ if (!tasks || (TASK_BUILD != tasks->type))
+ return TASK_FAILED;
+ tasks->args[1] = creation->id;
+ return TASK_IS_INCOMPLETE;
+ }
+ // Else, we try to proceed with creating an unit
+ // in the desired cell or transport.
+ // If research or development is needed, then we should not be in this code.
+ if (!side_can_build(side, uc))
+ return TASK_FAILED;
+ // We must handle tooling, if necessary.
+ if (!has_enough_tooling(constructor, uc))
+ return do_toolup_subtask(constructor, uc);
+ // If transport is valid, then try creating in it.
+ if (in_play(transport)) {
+ if (valid(rslt =
+ can_create_in(constructor, constructor, uc, transport))) {
+ prep_create_in_action(constructor, constructor, uc, transport);
+ return TASK_PREPPED_ACTION;
}
- /* should look at occupants in stack too */
}
- return NULL;
+ // Else, try creating in designated cell.
+ else {
+ if (valid(rslt =
+ can_create_at(constructor, constructor, uc, x, y))) {
+ prep_create_at_action(constructor, constructor, uc, x, y, 0);
+ return TASK_PREPPED_ACTION;
+ }
+ }
+ // Wait for more materials or ACP, if necessary.
+ // TODO: Proactively accumulate materials, if possible.
+ if ((A_ANY_NO_MATERIAL == rslt) || (A_ANY_NO_ACP == rslt)) {
+ set_unit_reserve(constructor->side, constructor, TRUE, FALSE);
+ return TASK_IS_INCOMPLETE;
+ }
+ return TASK_FAILED;
}
-static int
-repair_test(int x, int y)
-{
- return (repair_here(x, y) != NULL);
+static TaskOutcome
+do_repair_self_subtask(Unit *repairee, Task *task)
+{
+ Unit *unit2 = NULL, *unitbest = NULL;
+ Side *side = NULL;
+ Task *tasks2 = NULL;
+ int u = NONUTYPE, u2 = NONUTYPE;
+ int tx = -1, ty = -1, dist = -1;
+ int score = -1, scorebest = -1;
+
+ assert_error(is_active(repairee),
+ "AI: Attempted to repair with an inactive unit");
+ assert_warning_return(task, "AI: Attempted to run invalid repair task",
+ TASK_IS_COMPLETE);
+ // Useful info.
+ side = repairee->side;
+ u = repairee->type;
+ tx = repairee->x; ty = repairee->y;
+ // Try hp-recovery mechanism.
+ if (0 < u_hp_recovery(u)) {
+ set_unit_reserve(side, repairee, TRUE, FALSE);
+ return TASK_IS_INCOMPLETE;
+ }
+ // Check if anyone else is tasked with repairing us.
+ for_all_side_units(side, unit2) {
+ if (unit2 == repairee)
+ continue;
+ // Skip inactive units.
+ if (!is_active(unit2))
+ continue;
+ // Skip taskless units.
+ tasks2 = (unit2->plan ? unit2->plan->tasks : NULL);
+ if (!tasks2)
+ continue;
+ // Useful info.
+ u2 = unit2->type;
+ // If 'move-to' task, then inspect closer.
+ if ((TASK_MOVE_TO == tasks2->type)
+ && (uu_auto_repair_range(u2, u)
+ >= distance(tasks2->args[0], tasks2->args[1], tx, ty)))
+ tasks2 = tasks2->next;
+ if ((TASK_MOVE_TO == tasks2->type)
+ && (uu_repair_range(u2, u)
+ >= distance(tasks2->args[0], tasks2->args[1], tx, ty)))
+ tasks2 = tasks2->next;
+ // If 'occupy' task, then inspect closer.
+ if (TASK_OCCUPY == tasks2->type)
+ tasks2 = tasks2->next;
+ // If not 'repair' task, then skip.
+ if (TASK_REPAIR != tasks2->type)
+ continue;
+ // Skip, if repairee is not the object of the repair.
+ if (repairee->id != tasks2->args[0])
+ continue;
+ // If we've made it this far, then we know enough.
+ // Now we just sit back and let the other unit do the work.
+ set_unit_reserve(side, repairee, TRUE, FALSE);
+ return TASK_IS_INCOMPLETE;
+ } // for all side units
+ // If not auto-repair, then we report that the task failed.
+ // TODO: Find an unit to explicitly repair us, and schedule the repair.
+ if (!Xconq::any_auto_repair)
+ return TASK_FAILED;
+ // Try to find someone to auto-repair us.
+ for_all_side_units(side, unit2) {
+ if (unit2 == repairee)
+ continue;
+ // Skip inactive units.
+ if (!is_active(unit2))
+ continue;
+ // Probably not worth chasing mobile units. Let them come to us.
+ if (mobile(unit2->type))
+ continue;
+ // Useful info.
+ u2 = unit2->type;
+ dist = distance(unit2->x, unit2->y, tx, ty);
+ // Subtract auto-repair range from distance.
+ // Note: For repairs that must occur inside a transport,
+ // a penalty of 1 is effectively added.
+ dist = max(0, dist - uu_auto_repair_range(u2, u));
+ // If repairee is immobile,
+ // and potential repairer is out of range, then skip it.
+ if (!mobile(u) && (0 < dist))
+ continue;
+ // Score potential repairer.
+ score = hp_per_turn_est(unit2, u) / isqrt(dist + 1);
+ // Remember unit if it is best potential repairer.
+ if (score > scorebest) {
+ scorebest = score;
+ unitbest = unit2;
+ }
+ } // for all side units
+ if (0 < scorebest) {
+ u2 = unitbest->type;
+ // Calculate distance to be within repair range of best
+ // potential repairer.
+ dist = distance(unitbest->x, unitbest->y, repairee->x, repairee->y);
+ dist = max(0, dist - uu_auto_repair_range(u2, u));
+ // If distance to repairer > 0, then move there.
+ // Note: A potential repairer with distance > 0 should never
+ // have been selected, so this is not a concern.
+ if (dist > 0) {
+ // Move within range of potential repairer.
+ if (0 <= uu_auto_repair_range(u2, u))
+ // Move within auto-repair range of potential repairer.
+ push_move_to_task(
+ repairee, unitbest->x, unitbest->y,
+ uu_auto_repair_range(u2, u));
+ else {
+ // Prepare to occupy potential repairer.
+ // TODO: Technically, we need to search not only
+ // the potential repairer but all occs under it to
+ // find an opening that we can occupy.
+ push_occupy_task(repairee, unitbest);
+ // Move-to potential repairer before occupation of it.
+ // NOTE: Move-to may not be necessary in all cases,
+ // but it cannot hurt.
+ push_move_to_task(repairee, unitbest->x, unitbest->y, 1);
+ }
+ }
+ // Else, wait around.
+ else
+ set_unit_reserve(side, repairee, TRUE, FALSE);
+ // Return the task as being incomplete.
+ // If tasks were pushed onto the stack they will execute next.
+ return TASK_IS_INCOMPLETE;
+ } // 0 < scorebest
+ return TASK_FAILED;
}
-static int *lowm = NULL, numlow;
-
-/* Test the given unit to see if it or one of its occupants can help
- the unit looking for supplies. */
+static TaskOutcome
+do_repair_task(Unit *repairer, Task *task)
+{
+ TaskOutcome rslt = TASK_IS_INCOMPLETE;
+ Unit *repairee = NULL;
+ Side *side = NULL;
+ int id = -1;
+ int u = NONUTYPE, u2 = NONUTYPE;
+ int hpgoal = -1;
+
+ assert_error(is_active(repairer),
+ "AI: Attempted to repair with an inactive unit");
+ assert_warning_return(task, "AI: Attempted to run invalid repair task",
+ TASK_IS_COMPLETE);
+ // Get repairee.
+ id = task->args[0];
+ repairee = find_unit(id);
+ // Is repairee in play?
+ if (!in_play(repairee))
+ return TASK_FAILED;
+ // Useful info.
+ u = repairer->type;
+ u2 = repairee->type;
+ side = repairer->side;
+ hpgoal = task->args[1];
+ // If repair is finished, then indicate that the task is complete.
+ if (repairee->hp >= hpgoal)
+ return TASK_IS_COMPLETE;
+ // Set up explicit repair action, if possible.
+ if (valid(can_repair(repairer, repairer, repairee))) {
+ prep_repair_action(repairer, repairer, repairee);
+ return TASK_PREPPED_ACTION;
+ }
+ // Wait around for auto-repair, if possible.
+ if (valid(can_auto_repair(repairer, repairee))) {
+ // If repairer is repairee, then try to get repaired various ways.
+ if ((repairer == repairee)
+ && (TASK_FAILED != (rslt = do_repair_self_subtask(repairee, task))))
+ return rslt;
+ else {
+ set_unit_reserve(side, repairer, TRUE, FALSE);
+ return TASK_IS_INCOMPLETE;
+ }
+ }
+ // None of the above worked. Something is wrong.
+ Dprintf("%s repair task failed!\n", unit_desig(repairer));
+ return TASK_FAILED;
+}
-Unit *
-aux_resupply_here(Unit *unit)
+#if (0)
+int
+can_repair_from_here(int x, int y)
{
- int i, enough = TRUE;
- Unit *occ;
+ int x1, y1, u = tmpunit->type, u2, range = 0, space = FALSE;
+ Unit *unit;
- if (unit_trusts_unit(unit, tmpunit)
- && can_carry(unit, tmpunit)) {
- for (i = 0; i < numlow; ++i) {
- if (unit->supply[lowm[i]] == 0)
- enough = FALSE;
- }
- /* this should be controlled by doctrine? */
- /* shouldn't wake up, should get a new task to "wait up"
- or even approach if possible */
- /* wake_unit(unit->side, unit, FALSE); */
- if (enough)
- return unit;
- }
- for_all_occupants(unit, occ) {
- if (aux_resupply_here(occ)) {
- return occ;
+ /* First test if auto-repair of this unit type is at all possible
+ (should never get this far if not, but check anyway). */
+ if (!will_be_auto_repaired
+ || !will_be_auto_repaired[u]) {
+ return FALSE;
}
- }
- return NULL;
+ /* Then test if we can get there. */
+ if (!direct_access_to(x, y)) {
+ return FALSE;
+ }
+ /* Also check if we can sit there. */
+ if (!type_survives_in_cell(u, x, y)
+ || !type_can_occupy_cell(u, x, y)) {
+ /* If not, check if we can sit inside a unit at (x, y). */
+ for_all_stack_with_occs(x, y, unit) {
+ /* We are testing for empty type on the optimistic assumption
+ that there will be some space when we get there. */
+ if (type_can_occupy_empty_type(u, unit->type)) {
+ space = TRUE;
+ break;
+ }
+ }
+ if (!space) {
+ return FALSE;
+ }
+ }
+ tmpx = x;
+ tmpy = y;
+ /* First check this cell. */
+ if (repair_here(x, y)) {
+ return TRUE;
+ }
+ /* Compute how far out we need to search. */
+ for_all_unit_types(u2) {
+ range = max(range, uu_auto_repair_range(u2, u));
+ }
+ /* Search adjacent cells. */
+ if (search_around(
+ x, y, range, (int (*)(int, int)) repair_here, &x1, &y1, 1)) {
+ return TRUE;
+ }
+ return FALSE;
}
Unit *
-resupply_here(int x, int y)
+repair_here(int x, int y)
{
- Unit *unit, *resupplier;
+ int u = tmpunit->type, range = 0;
+ Unit *unit2;
- for_all_stack(x, y, unit) {
- resupplier = aux_resupply_here(unit);
- if (resupplier)
- return resupplier;
+ for_all_stack_with_occs(x, y, unit2) {
+ /* Skip over inactive units. */
+ if (!is_active(unit2)) {
+ continue;
+ }
+ /* Skip over untrusting units. */
+ if (!unit_trusts_unit(unit2, tmpunit)) {
+ continue;
+ }
+ /* Skip over units that cannot repair us. */
+ if (uu_auto_repair(unit2->type, u) <= 0) {
+ continue;
+ }
+ range = uu_auto_repair_range(unit2->type, u);
+ /* Test if unit2 can repair unit and is within auto repair range of
+ (x, y). Force the auto repair range to 0 if it is set to -1 to
+ signal that only occupants should be repaired. */
+ if (distance(x, y, tmpx, tmpy) <= max(range, 0)) {
+ /* Check that tmpunit can occupy unit2 if this is required.
+ Note: we optimistically test for room in empty transport
+ since things may change once we get there.
+ Should eventually add code that kicks out other occs to
+ make room for damaged units that need repair. */
+ if (range < 0
+ && !type_can_occupy_empty_type(u, unit2->type)) {
+ continue;
+ }
+ /* If the auto-repair range is zero and we cannot enter either
+ the cell or unit2, we also have a problem. */
+ if (range == 0
+ && (!type_survives_in_cell(u, x, y)
+ || !type_can_occupy_cell(u, x, y))
+ && !type_can_occupy_empty_type(u, unit2->type)) {
+ continue;
+ }
+ /* We assume that the calling code already has checked that
+ tmpunit can sit at (tmpx, tmpy). */
+ return unit2;
+ }
}
return NULL;
}
-
-static int
-resupply_test(int x, int y)
-{
- return (resupply_here(x, y) != NULL);
-}
+#endif
/* Replenish our supplies, using one of several strategies, which as
usual depends on the game, unit, terrain, etc. Strategies include
int x, y, u = unit->type, m, range;
int ux = unit->x, uy = unit->y;
Unit *unit2;
+#if (0)
+ int amtavail = 0, amtwanted = 0;
+#endif
+ int i = 0;
- /* A unit acting randomly might have gotten this task, even if the game
- has no materials or the unit no supply. Pretend that it was OK and
- get out of here. */
+ /* A unit acting randomly might have gotten this task, even if the
+ game has no materials or the unit no supply. Pretend that it was OK
+ and get out of here. */
if (nummtypes == 0 || unit->supply == NULL)
return TASK_IS_COMPLETE;
tmpside = unit->side;
lowm[numlow++] = m;
}
}
- /* We're all full up, must be OK. */
+ /* We're all filled up, must be OK. */
if (numlow == 0) {
return TASK_IS_COMPLETE;
+ /* Set up an explicit extraction action if possible. We will still
+ benefit from any auto-resupply that is available.*/
+ } else if (valid(check_extract_action(unit, unit,
+ unit->x, unit->y, m, 1))) {
+ prep_extract_action(unit, unit, unit->x, unit->y, m, 1);
+ return TASK_PREPPED_ACTION;
+ /* Should not just sit around when there may be more expedient ways to
+ get supplies that could let us get back into action the same turn. */
} else if (can_auto_resupply_self(unit, lowm, numlow)) {
set_unit_reserve(unit->side, unit, TRUE, FALSE);
- return (probability(10) ? TASK_FAILED : TASK_IS_INCOMPLETE);
- } else if (unit->transport != NULL) {
- /* (should fix this test, transport is not necessarily of any help) */
- /* (could attempt to resupply via direct action) */
+ return TASK_IS_INCOMPLETE;
+ /* Check if we are already at a valid resupply point. */
+ } else if (can_resupply_from_here(ux, uy)) {
+ /* Try to enter any provider in the same cell. Note: this
+ is to ensure that aircraft lands on a carrier. If we already
+ have a transport, we stay put. */
+ if (!unit->transport) {
+ for (i = 0; i < numlow; ++i) {
+ tmpmtype = lowm[i];
+ tmpx = ux;
+ tmpy = uy;
+ unit2 = resupply_here(ux, uy);
+ if (unit2
+ && can_occupy(unit, unit2)) {
+ prep_enter_action(unit, unit, unit2);
+ return TASK_PREPPED_ACTION;
+ }
+ }
+ }
+ /* Otherwise, just wait for the economy code to to its job. */
set_unit_reserve(unit->side, unit, TRUE, FALSE);
- return (probability(10) ? TASK_FAILED : TASK_IS_INCOMPLETE);
- } else if ((unit2 = resupply_here(ux, uy)) != NULL
- && unit2 != unit->transport) {
- prep_enter_action(unit, unit, unit2);
- return TASK_PREPPED_ACTION;
+ return TASK_IS_INCOMPLETE;
} else {
- /* Compute how far out to look for a resupply point; be a little
- optimistic. */
- range = operating_range_best(u);
- /* (should reduce range if materials are really low) */
- if (search_around(ux, uy, range, resupply_test, &x, &y, 1)) {
- /* (should collect actual unit and chase it directly) */
- /* (should only need to get within outlength of needed supplies) */
+ /* Compute how far out to look for a resupply point. */
+ range = real_operating_range_best(unit);
+ if (search_around(ux, uy, range, can_resupply_from_here, &x, &y, 1)) {
push_move_to_task(unit, x, y, 0);
+ DMprintf("Resupply task: %s is moving to (%d, %d).",
+ unit_desig(unit), x, y);
return TASK_IS_INCOMPLETE;
} else {
- /* Failure - sometimes just sit, but usually try something else. */
- if (probability(10))
- set_unit_reserve(unit->side, unit, TRUE, FALSE);
- /* (should be able to signal interface usefully somehow) */
+ /* None of the above worked. Something is wrong. */
+ Dprintf("%s resupply task failed!\n", unit_desig(unit));
return TASK_FAILED;
}
}
}
+int
+can_resupply_from_here(int x, int y)
+{
+ int i, x1, y1, u = tmpunit->type, space = FALSE;
+ Unit *unit;
+
+ /* First test if we can get there. */
+ if (!direct_access_to(x, y)) {
+ return FALSE;
+ }
+ /* Also check if we can sit there. */
+ if (!type_survives_in_cell(u, x, y)
+ || !type_can_occupy_cell(u, x, y)) {
+ /* If not, check if we can sit inside a unit at (x, y). */
+ for_all_stack_with_occs(x, y, unit) {
+ /* We are testing for empty type on the optimistic assumption
+ that there will be some space when we get there. */
+ if (type_can_occupy_empty_type(u, unit->type)) {
+ space = TRUE;
+ break;
+ }
+ }
+ if (!space) {
+ return FALSE;
+ }
+ }
+ if (foundm == NULL) {
+ foundm = (int *) xmalloc(nummtypes * sizeof(int));
+ }
+ /* Search adjacent cells within our inlength for needed supplies. */
+ for (i = 0; i < numlow; ++i) {
+ foundm[i] = FALSE;
+ tmpmtype = lowm[i];
+ tmpx = x;
+ tmpy = y;
+ /* First search this cell. */
+ if (resupply_here(x, y)) {
+ foundm[i] = TRUE;
+ continue;
+ }
+ if (search_around(x, y, um_inlength(u, lowm[i]),
+ (int (*)(int, int)) resupply_here, &x1, &y1, 1)){
+ foundm[i] = TRUE;
+ continue;
+ }
+ }
+ /* If we cannot resupply a needed material from here, we are done. */
+ for (i = 0; i < numlow; ++i) {
+ if (foundm[i] == FALSE) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+Unit *
+resupply_here(int x, int y)
+{
+ int u, tu = tmpunit->type, range = 0;
+ Unit *unit;
+
+ for_all_stack_with_occs(x, y, unit) {
+ u = unit->type;
+ /* Can't resupply from ourselves. */
+ if (unit == tmpunit)
+ continue;
+ /* Can't resupply from enemies. */
+ if (!unit_trusts_unit(unit, tmpunit))
+ continue;
+ /* The unit lacks our needed supply. */
+ if (unit->supply[tmpmtype] == 0)
+ continue;
+ /* Compute the maximal possible supply range. */
+ range = min(um_outlength(u, tmpmtype), um_inlength(tu, tmpmtype));
+ /* Resupply is impossible. */
+ if (range < 0)
+ continue;
+ /* We are out of range. */
+ if (range < distance(unit->x, unit->y, tmpx, tmpy))
+ continue;
+ /* Range zero and there is no room for us. We assume
+ that it is necessary to enter the provider. */
+ if (range == 0 && !can_occupy(tmpunit, unit))
+ continue;
+ /* If range > 0 we assume that there is always room for
+ us in a cell within range. */
+ return unit;
+ }
+ return NULL;
+}
/* Return true if our own automatic material production is *greater*
than our consumption, for the given list of materials. */
static TaskOutcome
do_sentry_task(Unit *unit, Task *task)
{
+ Task *nexttask = NULL;
+ Unit *transport = NULL;
+
+ if (task->next)
+ nexttask = task->next;
+ /* If we were waiting for a transport, and are now in it,
+ then don't wait. */
+ if (nexttask->type == TASK_OCCUPY) {
+ transport = find_unit_dead_or_alive(nexttask->args[0]);
+ if (in_play(transport) && (transport == unit->transport)) {
+ task->next = nexttask->next;
+ free_task(nexttask);
+ }
+ return TASK_IS_COMPLETE;
+ }
if (task->args[0] > 0) {
set_unit_reserve(unit->side, unit, TRUE, FALSE);
--(task->args[0]);
/* This should never happen. */
if (unit->plan == NULL)
run_error("???");
- /* If the unit is AI-run, don't do more than task execution with
+ /* If the unit is AI-run, don't do more than one task execution with
it during each run step. */
- if (unit->plan->last_task_outcome != TASK_UNKNOWN
- && unit->plan->last_task_outcome != TASK_PREPPED_ACTION
- && side_has_ai(unit->side)
+ if (ai_controlled(unit)
+ && plan->last_task_outcome != TASK_UNKNOWN
+ && plan->last_task_outcome != TASK_PREPPED_ACTION
/* unless it is acp-independent ... */
- && !acp_indep(unit)
- && !unit->plan->aicontrol)
+ && !acp_indep(unit))
return unit->plan->last_task_outcome;
task = plan->tasks;
if (task == NULL)
pop_task(plan);
DMprintf("removed it");
/* We might be buzzing, so maybe go into reserve. */
- if (probability(g_ai_badtask_reserve_chance())
- && g_units_may_go_into_reserve()) {
+ if (probability(g_ai_badtask_reserve_chance())) {
plan->reserve = TRUE;
DMprintf(" and went into reserve");
}
return (*fn)(unit, task);
}
+/* Wrapper to 'choose_move_dirs' function for providing "backward"
+ compatibility to the 'choose_move_direction' function that some
+ functions scattered around the kernel came to depend upon. */
+/* (The 'range' parameter is dead weight. Should we/can we do anything
+ meaningful with it in the new context?) */
+int
+choose_move_direction(Unit *unit, int x, int y, int range)
+{
+ int rslt = NODIR;
+ int numdirs = 0;
+ int dirs[NUMDIRS];
+ int i = 0, ix, iy;
+
+ /* Try to find a shortest path first. */
+ numdirs = choose_move_dirs(unit, x, y, TRUE, plausible_move_dir,
+ sort_directions, dirs);
+#if (1)
+ /* If that fails then try to find any path. */
+ /* (Unfortunately, this is not compatible with 'do_approach_subtask'.
+ If this piece of code is enabled and called from an UI,
+ it may say a move is OK, but a movement task that is prepped may,
+ in fact, fail in do_approach_subtask. So, for the time being, we
+ must accept that certain legitimate paths will be rejected.) */
+ if (!numdirs)
+ numdirs = choose_move_dirs(unit, x, y, FALSE, plausible_move_dir,
+ sort_directions, dirs);
+#endif
+ /* If that fails then either no path exists, or else our pathfinder
+ algorithm does not do a more thorough search such as with Astar. */
+ if (!numdirs)
+ return NODIR;
+ for (i = 0; i < numdirs; ++i) {
+ interior_point_in_dir(unit->x, unit->y, dirs[i], &ix, &iy);
+ if (!side_thinks_it_can_put_type_at(unit->side, unit->type, ix, iy))
+ continue;
+ /* (Should put other tests here to make this function emulate
+ the 'choose_move_direction' from Peter's pathfinder as much as
+ is sane.) */
+ rslt = dirs[i];
+ break;
+ }
+ return rslt;
+}
+
/* This weird-looking routine computes next directions for moving to a
given spot. The number of directions ranges from 1 to 4, depending
on whether there is a straight-line path to the dest, and whether we are
required to take a direct path or are allowed to move in dirs that don't
- the unit any closer (we never increase our distance though).
+ bring the unit any closer (we never increase our distance though).
Some trickinesses: if area wraps, must resolve ambiguity about
getting to the same place going either direction (we pick shortest). */
dist = distance(unit->x, unit->y, tx, ty);
dx = tx - unit->x; dy = ty - unit->y;
-
if (area.xwrap) {
dxa = (tx + area.width) - unit->x;
if (ABS(dx) > ABS(dxa))
if ((ut_vanishes_on(u, t) || ut_wrecks_on(u, t))
&& !can_move_via_conn(unit, nx, ny))
return FALSE;
+ speed = unit_speed(unit, nx, ny);
/* Try the easy test first. */
- /* (should reconsider - what if unit is slow due to damage?) */
- if (ut_mp_to_enter(u, t) <= u_acp(u))
+ if (ut_mp_to_enter(u, t) <= ((new_acp_for_turn(unit) * speed) / 100))
return TRUE;
u2 = (unit->transport ? unit->transport->type : NONUTYPE);
totcost = total_move_cost(u, u2, ux, uy, 0, nx, ny, 0);
- speed = unit_speed(unit, nx, ny);
/* Check if unit can possibly have enough mp for the move. */
+ /* (We need to be careful here, since this function is not
+ actually privy to all knowledge. Should use type generic
+ functions instead.) */
maxmpavail =
- (((u_acp_max(u) > 0 ? u_acp_max(u) : u_acp(u)) - u_acp_min(u)) * speed)
- / 100
+ (((u_acp_max(u) > 0 ? u_acp_max(u) : new_acp_for_turn(unit))
+ - u_acp_min(u)) * speed) / 100
+ u_free_mp(u);
if (maxmpavail >= totcost)
return TRUE;
&& connection_at(ux, uy, dir, c)
&& ((cost = ut_mp_to_traverse(u, c)) >= 0)) {
if ((ut_mp_to_enter(u, c) + cost + ut_mp_to_leave(u, c))
- <= u_acp(u))
+ <= ((new_acp_for_turn(unit) * speed) / 100))
return TRUE;
}
}
int ux = tmpunit->x, uy = tmpunit->y, u2 = NONUTYPE;
int cost0 = 0, cost1 = 0, s, ps0, ps1, surr0, surr1, rslt;
int cs0, cs1;
- extern int *any_people_surrenders;
+ extern short *any_people_surrenders;
i0 = *((int *) a0); i1 = *((int *) a1);
t0 = terrs[i0]; t1 = terrs[i1];
add_task(Unit *unit, int pos, Task *task)
{
int numcleared;
+ Task *agenda = NULL;
if (unit->plan == NULL && !completed(unit)) {
init_unit_plan(unit);
DMprintf("%s add task %s",
unit_desig(unit), task_desig(task));
if (pos == CLEAR_AGENDA) {
- numcleared = clear_task_agenda(unit->plan);
+ numcleared = clear_task_agenda(unit);
DMprintf(" (cleared %d existing tasks)", numcleared);
}
DMprintf("\n");
switch (pos) {
- case 0:
+ // Put the task on the front of the agenda.
+ case ADD_TO_AGENDA_AS_LIFO:
case CLEAR_AGENDA:
- /* Put the task on the front of the agenda. */
task->next = unit->plan->tasks;
unit->plan->tasks = task;
break;
+ // Put the task on the end of the agenda.
+ case ADD_TO_AGENDA_AS_FIFO:
+ task->next = NULL;
+ agenda = unit->plan->tasks;
+ if (!agenda)
+ unit->plan->tasks = task;
+ else {
+ for (; agenda && agenda->next; agenda = agenda->next);
+ agenda->next = task;
+ }
+ break;
default:
- run_error("can't do this yet");
+ run_error("Tried to add a task to a task agenda in a strange manner");
break;
}
- /* Shouldn't be asleep any longer. */
+ // Shouldn't be asleep any longer.
unit->plan->asleep = FALSE;
- /* We're not in reserve. */
+ // We're not in reserve.
unit->plan->reserve = FALSE;
- /* Presumably we're no longer waiting to be told what to do. */
+ // Presumably we're no longer waiting to be told what to do.
set_waiting_for_tasks(unit, FALSE);
- /* Reflect all this on displays. */
+ // Reflect all this on displays.
update_unit_display(unit->side, unit, FALSE);
}
/* This routine sets up a task to build a unit of the given type. */
Task *
-create_build_task(Unit *unit, int u2, int run, int x, int y)
+create_build_task(Unit *unit, int id, int cp)
{
- Task *task = create_task(TASK_BUILD);
+ Task *task = NULL;
- task->args[0] = u2;
- task->args[3] = run;
- task->args[4] = x; task->args[5] = y;
+ task = create_task(TASK_BUILD);
+ task->args[0] = id;
+ task->args[1] = cp;
return task;
}
void
-set_build_task(Unit *unit, int u2, int run, int x, int y)
-{
- /* First check if we are building something. */
- if (unit->plan && unit->plan->tasks
- && unit->plan->tasks->type == TASK_BUILD) {
-
- Unit *unit2 = find_unit(unit->plan->tasks->args[1]);
-
- /* Return if we already are building the desired unit. */
- if (is_unit(unit2) && !completed(unit2) && unit2->type == u2) {
- return;
- /* Maybe disband incomplete unit of wrong type. */
- } else if (is_unit(unit2) && !completed(unit2)
- && side_can_disband(unit->side, unit2)
- && g_disband_unfinished_units()) {
- /* But first salvage either its cps or materials if
- permitted to do so. */
- if (g_salvage_unfinished_cps())
- unit->cp_stash += unit2->cp;
- else if (g_salvage_unfinished_materials()) {
-
- int m, u = unit2->type;
-
- for_all_material_types(m) {
- if (um_consumption_on_creation(u, m) > 0)
- unit->supply[m] += um_consumption_on_creation(u, m);
- if (um_consumption_per_cp(u2, m) > 0)
- unit->supply[m] += (unit2->cp - uu_creation_cp(unit->type, u))
- * um_consumption_per_cp(u2, m);
- }
- }
- kill_unit(unit2, H_UNIT_DISBANDED);
- }
- }
- add_task(unit, CLEAR_AGENDA, create_build_task(unit, u2, run, x, y));
-}
-
-void
-push_build_task(Unit *unit, int u2, int run, int x, int y)
-{
- /* First check if we are building something. */
- if (unit->plan && unit->plan->tasks
- && unit->plan->tasks->type == TASK_BUILD) {
-
- Unit *unit2 = find_unit(unit->plan->tasks->args[1]);
-
- /* Return if we already are building the desired unit. */
- if (is_unit(unit2) && !completed(unit2) && unit2->type == u2) {
- return;
- /* Maybe disband incomplete unit of wrong type. */
- } else if (is_unit(unit2) && !completed(unit2)
- && side_can_disband(unit->side, unit2)
- && g_disband_unfinished_units()) {
- /* But first salvage either its cps or materials if permitted to do so. */
- if (g_salvage_unfinished_cps())
- unit->cp_stash += unit2->cp;
- else if (g_salvage_unfinished_materials()) {
-
- int m, u = unit2->type;
-
- for_all_material_types(m) {
- if (um_consumption_on_creation(u, m) > 0)
- unit->supply[m] += um_consumption_on_creation(u, m);
- if (um_consumption_per_cp(u2, m) > 0)
- unit->supply[m] += (unit2->cp - uu_creation_cp(unit->type, u))
- * um_consumption_per_cp(u2, m);
- }
- }
- kill_unit(unit2, H_UNIT_DISBANDED);
- }
- }
- add_task(unit, 0, create_build_task(unit, u2, run, x, y));
+set_build_task(Unit *unit, int id, int cp)
+{
+ add_task(unit, CLEAR_AGENDA, create_build_task(unit, id, cp));
}
void
-resume_build_task(Unit *unit, Unit *unit2, int run, int x, int y)
+push_build_task(Unit *unit, int id, int cp)
{
- set_build_task(unit, unit2->type, run, x, y);
- unit->plan->tasks->args[1] = unit2->id;
+ add_task(unit, 0, create_build_task(unit, id, cp));
}
Task *
add_task(unit, 0, create_produce_task(unit, m, n));
}
+/* Construct Task */
+
Task *
-create_repair_task(Unit *unit)
+create_construct_task(Unit *unit, int u, int run, int transid, int x, int y)
{
- return create_task(TASK_REPAIR);
+ Task *task = NULL;
+
+ task = create_task(TASK_CONSTRUCT);
+ task->args[0] = u;
+ task->args[1] = run;
+ task->args[2] = transid;
+ task->args[3] = x;
+ task->args[4] = y;
+ return task;
+}
+
+void
+set_construct_task(Unit *unit, int u, int run, int transid, int x, int y)
+{
+ add_task(
+ unit, CLEAR_AGENDA, create_construct_task(unit, u, run, transid, x, y));
+}
+
+void
+push_construct_task(Unit *unit, int u, int run, int transid, int x, int y)
+{
+ add_task(unit, 0, create_construct_task(unit, u, run, transid, x, y));
+}
+
+/* Repair Task */
+
+Task *
+create_repair_task(Unit *unit, int id, int hp)
+{
+ Task *task = NULL;
+
+ task = create_task(TASK_REPAIR);
+ task->args[0] = id;
+ task->args[1] = hp;
+ return task;
+}
+
+void
+set_repair_task(Unit *unit, int id, int hp)
+{
+ add_task(unit, CLEAR_AGENDA, create_repair_task(unit, id, hp));
}
void
-set_repair_task(Unit *unit)
+push_repair_task(Unit *unit, int id, int hp)
{
- add_task(unit, CLEAR_AGENDA, create_repair_task(unit));
+ add_task(unit, 0, create_repair_task(unit, id, hp));
}
/* This routine sets up a task to develop a unit of the given type. */
}
void
-push_develop_task(Unit *unit, int u2, int n)
+set_develop_task(Unit *unit, int u2, int techgoal)
+{
+ add_task(unit, CLEAR_AGENDA, create_develop_task(unit, u2, techgoal));
+}
+
+void
+push_develop_task(Unit *unit, int u2, int techgoal)
{
- add_task(unit, 0, create_develop_task(unit, u2, n));
+ add_task(unit, 0, create_develop_task(unit, u2, techgoal));
}
Task *
rest = get_next_arg(str, substr, &arg);
/* (should check for end of command or not?) */
}
- *taskp = create_task(tasktype);
+ *taskp = create_task((TaskType)tasktype);
for (i = 0; i < numargs; ++i) {
(*taskp)->args[i] = taskargs[i];
}
char *argtypes;
if (taskbuf == NULL)
- taskbuf = xmalloc(BUFSIZE);
+ taskbuf = (char *)xmalloc(BUFSIZE);
if (task) {
sprintf(taskbuf, "{%s", taskdefns[task->type].name);
argtypes = taskdefns[task->type].argtypes;