#include "kernel.h"
#include "kpublic.h"
-/* This is for storing basic stuff about the most recently executed
- action. */
+/* This is for storing the most recently executed action. */
-typedef struct a_action_record {
- Unit *actor; /* Pointer to the actor if it still exists. */
- short x, y, z; /* Actor's location at the time of action. */
- ActionType type; /* Type of action. */
- HistEventType result; /* Its result. */
-} ActionRecord;
+Action *latest_action = NULL;
-static ActionRecord *latest_action = NULL;
+/* The last known position of the latest actor. */
-int latest_action_x, latest_action_y;
-
-/* Functions in run2.c, only called from this file. */
-
-extern void run_people(void);
-extern void run_advanced_units(void);
-extern void run_turn_start(void);
-extern void run_restored_turn_start(void);
-extern void run_turn_end(void);
+int latest_action_x;
+int latest_action_y;
static void compose_actionvectors(void);
static void init_movement(void);
-static void clear_task_outcomes(void);
+static void clear_aicontrolled_outcomes(void);
static int move_some_units(int lim);
static int side_move_some_units(Side *side, int lim);
static int unit_still_acting(Unit *unit);
/* Advanced unit support. */
+extern void allocate_used_cells(Unit *unit); /* Used in move.c */
+
static void run_side_research(void);
static void run_population(Unit *unit);
-static void run_construction(Unit *unit);
static void run_research(Unit *unit);
-static void allocate_used_cells(Unit *unit);
static int auto_pick_new_build_task(Unit *unit);
+static void take_all_from_treasury(Unit *unit);
+static void give_all_to_treasury(Unit *unit);
+
/* Priority of sides that are now moving. */
int curpriority;
need_ai_planning = FALSE;
/* Make sure the action record is allocated. */
if (latest_action == NULL)
- latest_action = (ActionRecord *) xmalloc(sizeof(ActionRecord));
+ latest_action = (Action *) xmalloc(sizeof(Action));
if (beforestart) {
/* If we haven't started yet, see if it's time. */
test_for_game_start();
cur_unit_priority = highest_unit_priority;
compose_actionvectors();
update_all_progress_displays("", -1);
+ /* Make sure the research window is popped up for sides
+ without a research task. */
+ for_all_sides(side) {
+ if (side_has_display(side)) {
+ update_research_display(side);
+ }
+ }
/* Game might have been ended by new turn init. */
test_for_game_end();
if (endofgame) {
one advance each turn. */
run_side_research();
if (last_taskexecs > 0)
- clear_task_outcomes();
+ clear_aicontrolled_outcomes();
/* Move some units around. */
numacted += move_some_units(maxactions);
if (cur_unit_priority < lowest_unit_priority) {
problems. This generates massive amounts of saved data very
quickly, be careful using it! */
-static int save_run_states = 1;
+static int save_run_states = 0;
void
save_run_state(char *suffix)
int
units_still_acting(Side *side)
{
- int curactor;
+ int curactor, a;
Unit *unit;
UnitVector *av = side->actionvector;
/* Try not to let a side not be researching something. */
if (numatypes > 0
&& g_side_can_research()
- && side->research_topic == NOADVANCE
- /* (should also test that any advances are available) */
- )
- return TRUE;
+ && side->research_topic == NOADVANCE) {
+ /* Check that the side can research something. */
+ for_all_advance_types(a) {
+ if (side_can_research(side, a)) {
+ /* Popup the research dialog if the side has a
+ display. */
+ if (side_has_display(side)) {
+ update_research_display(side);
+ }
+ return TRUE;
+ }
+ }
+ }
return FALSE;
}
clear_unit_vector(side->actionvector);
for_all_side_units(side, unit) {
if (unit->act
- && (unit->act->initacp > 0
- || acp_indep(unit))) {
+ && ((unit->act->initacp > 0)
+ || acp_indep(unit))) {
priority = unit_priority(unit);
if (priority == cur_unit_priority) {
side->actionvector =
/* Clear any delay flags. */
if (unit->plan)
unit->plan->delayed = FALSE;
+ /* Clear any busy flags. */
+ if (unit->busy) {
+ /* The busy flag should have been cleared when the unit
+ moved. If it is still set, it could mean that a
+ broadcasted action was never received by the host
+ and rebroadcasted. */
+ Dprintf(
+"Busy flag still set for %s at start of turn.\n",
+ unit_desig(unit));
+ unit->busy = FALSE;
+ }
}
}
if (unit->plan) {
}
static void
-clear_task_outcomes(void)
+clear_aicontrolled_outcomes(void)
{
Unit *unit;
ai-controlled units. */
for_all_units(unit) {
if (ai_controlled(unit))
- unit->plan->last_task_outcome = TASK_UNKNOWN;
+ clear_task_outcome(unit);
}
}
static int
side_move_some_units(Side *side, int lim)
{
- int num, foundanytomove, curactor0, curactor, numdelayed;
+ int num, foundanytomove, curactor0, curactor, numcouldact = 0, numfollowing;
+ int numdelayed = 0;
Unit *unit;
UnitVector *av = side->actionvector;
curactor0 = 0;
try_again:
foundanytomove = FALSE;
+ numcouldact = 0;
numdelayed = 0;
+ numfollowing = 0;
/* Iterate through all units in the action vector, starting with
the "current" (most recently moved) unit. */
/* Note that the action vector may get resized/reallocated as
- units act (such as when a unit is capture), so we can't cache
+ units act (such as when a unit is captured), so we can't cache
it into a local var here. */
for (curactor = curactor0; curactor < side->actionvector->numunits;
++curactor) {
Dprintf("Considering moving %s with plan %s\n",
unit_desig(unit), plan_desig(unit->plan));
#endif
+ /* If the unit cannot act anymore, then skip it forthwith. */
+ if (!has_acp_left(unit))
+ continue;
/* Count and skip over deliberately delayed units. */
if (unit->plan && unit->plan->delayed) {
+ ++numcouldact;
++numdelayed;
continue;
}
+ /* Count the units which are in formations and can still act. */
+ if (unit->plan && unit->plan->formation && unit_still_acting(unit))
+ ++numfollowing;
/* Default AIs to the slow play rate. */
current_play_rate = slow_play_rate;
/* AIs should play as fast as possible if turns are sequential,
&& unit->side->ingame
&& !unit->side->finishedturn
&& has_acp_left(unit)
- && (unit->plan && unit->plan->formation)) {
+ && (unit->plan && unit->plan->formation
+ && !is_in_formation(unit))) {
num += move_one_unit_multiple(unit, lim - num);
foundanytomove = TRUE;
}
num += move_one_unit_multiple(unit, lim - num);
foundanytomove = TRUE;
}
+ /* Execute any tasks or pending actions that the unit has. */
if (unit_still_acting(unit)
&& (unit->plan && !unit->plan->waitingfortasks)) {
num += move_one_unit_multiple(unit, lim - num);
foundanytomove = TRUE;
} else if (unit_still_acting_no_plan(unit)) {
- num += move_one_unit_multiple(unit, lim - num);
- foundanytomove = TRUE;
- }
- if (unit_still_acting(unit)) {
- foundanytomove = TRUE;
+ if (has_pending_action(unit) || (unit->plan && unit->plan->tasks)) {
+ num += move_one_unit_multiple(unit, lim - num);
+ foundanytomove = TRUE;
+ }
}
/* See if we exceeded the limit on the number of moves that
we're allowed to do this time. If so, memorize the unit
}
return num;
}
+ /* Increment the count of units that could act. */
+ ++numcouldact;
}
/* If started in middle of list, and didn't find anything to do,
rescan from beginning. */
curactor0 = 0;
goto try_again;
}
- /* Now clear all the delay flags and rescan the action vector. */
- if (!foundanytomove && numdelayed > 0) {
+ /* See if any at all could act, and let the 'run_game' logic deal
+ with it. */
+ if (!numcouldact)
+ return 0;
+ /* Clear all the delay flags and rescan the action vector, if no other
+ non-delayed actors left. */
+ if (!foundanytomove && (0 >= (numcouldact - numdelayed))) {
av = side->actionvector;
for (curactor = 0; curactor < av->numunits; ++curactor) {
unit = unit_in_vector(av, curactor);
if (!foundanytomove && 0 /* not at max priority */) {
/* (should recompose action vector for new unit priority?) */
}
+ /* Set waiting-for-tasks to true for any units in a formation, which
+ still are acting (have ACP, are not skipped, etc...). */
+ if (numfollowing > 0) {
+ for (curactor = 0; curactor < av->numunits; ++curactor) {
+ unit = unit_in_vector(av, curactor);
+ if (unit->plan && unit->plan->formation
+ && unit_still_acting(unit) && !(unit->plan->tasks))
+ set_waiting_for_tasks(unit, TRUE);
+ }
+ }
side->curactor_pos = 0;
side->curactor = NULL;
side->curactor_id = 0;
return FALSE;
if (!has_acp_left(unit))
return FALSE;
- if (unit->plan
- && (unit->plan->asleep
- || unit->plan->reserve))
- return FALSE;
- /* Conditions that require further action provided
- that it was not ruled out above. */
- if (acp_indep(unit)
- && can_build(unit)
- && !unit->buildingdone)
- return TRUE;
- if (has_pending_action(unit))
- return TRUE;
- if (unit->plan)
+ if (unit->plan
+ && (unit->plan->asleep
+ || unit->plan->reserve))
+ return FALSE;
+ /* Conditions that require further action provided
+ that it was not ruled out above. */
+ // HACK: We need to check action allowance flags rather than doing this.
+ if (acp_indep(unit))
+ return TRUE;
+ if (has_pending_action(unit))
+ return TRUE;
+ if (unit->plan)
return TRUE;
return FALSE;
}
return FALSE;
/* Conditions that require further action provided that it was not
ruled out above. */
- if (acp_indep(unit)
- && can_build(unit)
- && !unit->buildingdone)
+ // HACK: We need to check action allowance flags rather than doing this.
+ if (acp_indep(unit))
return TRUE;
if (has_pending_action(unit))
return TRUE;
static int
move_one_unit_multiple(Unit *unit, int lim)
{
- int num = 0, buzz = 0, acp1;
+ int num = 0, buzz = 0, acp1, i;
int rslt;
/* If unit is incapable of acting right now, get out of here. */
&& !acp_indep(unit)))
return 0;
acp1 = unit->act->acp;
- while (is_active(unit)
- && has_acp_left(unit)
- && ((unit->plan
- && !unit->plan->asleep
- && !unit->plan->reserve
- && !unit->plan->delayed
- )
+ while (is_active(unit) && has_acp_left(unit)
+ && ((unit->plan && !unit->plan->asleep
+ && !unit->plan->reserve && !unit->plan->delayed)
|| has_pending_action(unit))
&& num < lim
&& buzz < lim) {
-#if 1 /* enable for more intense debugging */
+#if (1) /* enable for more intense debugging */
Dprintf(" Moving %s (%d/%d, buzz %d) with plan %s\n",
unit_desig(unit), num, lim, buzz, plan_desig(unit->plan));
#endif
+ /* Unit may be dead, and thus cannot complete the rest of its actions.
+ Although kill_unit removes the unit from action vector, we may
+ not have had a chance to go back up to side_move_some_units to
+ discover this.
+ Tell side_move_some_units to go find another unit to move.
+ (Note that execute_action does check whether the unit is alive
+ or not, but why check in each called function when we can
+ nip things in the bud right here, right now? None of the
+ called functions can reasonably do anything with a dead unit.)
+ */
+ if (!alive(unit)) {
+ Dprintf("Tried to act with dead unit, %s!\n", unit_desig(unit));
+ break;
+ }
+#if (0)
/* Skip over AI-run units that have executed a task during
this run step, so that the AI gets a chance to react to the
result. */
- if (unit->plan
+ if (ai_controlled(unit)
&& unit->plan->last_task_outcome != TASK_UNKNOWN
- && unit->plan->last_task_outcome != TASK_PREPPED_ACTION
- && side_has_ai(unit->side)
- && !unit->plan->aicontrol)
+ && unit->plan->last_task_outcome != TASK_PREPPED_ACTION)
break;
- /* Do acp-independent construction. */
- if (acp_indep(unit)) {
- if (!unit->buildingdone) {
- run_construction(unit);
- gamestatesafe = FALSE;
- }
- /* Sides without ai never proceed beyond the first unit in the
- actionvector if we return 1 here. Why? */
- return 0;
- } else if (has_pending_action(unit)) {
-
- /* Save this info now in case the actor dies etc. */
+#endif
+ if (has_pending_action(unit)) {
+ /* Save the action now in case the actor dies or wrecks. */
latest_action->type = unit->act->nextaction.type;
- latest_action->actor = unit;
- latest_action->x = latest_action_x = unit->x;
- latest_action->y = latest_action_y = unit->y;
- latest_action->z = unit->z;
+ for (i = 0; i < MAXACTIONARGS; ++i) {
+ latest_action->args[i] = unit->act->nextaction.args[i];
+ }
+ latest_action->actee = unit->act->nextaction.actee;
+ latest_action->next = unit->act->nextaction.next;
+ /* Also save the latest known position of the actor. */
+ latest_action_x = unit->x;
+ latest_action_y = unit->y;
/* Execute the action directly. */
rslt = execute_action(unit, &(unit->act->nextaction));
-
- /* Save the result. */
- latest_action->result = rslt;
-
/* Trigger ai reaction for certain actions. */
- need_ai_action_reaction = action_reaction_needed(latest_action->type, rslt);
-
+ need_ai_action_reaction =
+ action_reaction_needed(latest_action->type, rslt);
/* Clear the action. Note that the unit might have changed
to a non-acting type, so we have to check for act struct. */
if (unit->act)
unit->act->nextaction.type = ACTION_NONE;
+ /* Also clear the busy flag. */
+ unit->busy = FALSE;
/* In any case, the game state is irrevocably altered. */
gamestatesafe = FALSE;
++num;
gamestatesafe = FALSE;
++buzz;
}
+#if 0
+ /* This replanning caused buzzing. It is sufficient to call
+ ai_decide_plan at the start of each turn and after
+ force_replan. Possibly, we should test for an empty task queue
+ here, and replan in that case only. However, the AI works fine
+ without any replanning. */
+
/* Flag so that we run AI code in the near future (after
run_game exits). */
if (side_has_ai(unit->side))
need_ai_planning = TRUE;
+#endif
/* Get out of here if unit is set not to do anything on
its own. */
if (unit->plan->waitingfortasks
nextpriority = 9999;
for_all_sides(side2) {
if (!side2->finishedturn
-/* && side2->priority > curpriority */
+ /* && side2->priority > curpriority */
&& side2->priority < nextpriority) {
nextpriority = side2->priority;
}
if (!side2->finishedturn && side2->priority < curpriority)
- run_warning("%s not finished, but priority is %d, less than current %d",
+ run_warning(
+"%s not finished, but priority is %d, less than current %d",
side_desig(side2), side2->priority, curpriority);
}
if (nextpriority > curpriority)
update_turn_display(side, TRUE);
/* (should update side's view of all sides?) */
update_side_display(side, side, TRUE);
+ /* Update legends and their positions as new terrain comes into view. */
+ place_legends(side);
+ /* Redraw legends in new positions. */
+ update_area_display(side);
}
}
dump_statistics();
void
run_side_research(void)
{
- int a, m, consump, maxrp, rp, amt;
+ int a, m, consump, amt, totalweight = 0;
+ long maxrp, rp;
Side *side;
+ Obj *choice = NULL, *nextgoal = NULL;
/* No research if there are no advances. */
if (numatypes == 0)
/* Our wise men are waiting for something to do. */
if (side->research_topic == NOADVANCE) {
if (side_has_ai(side)) {
- ;
+ /* Do nothing. ai_plan_research will pick a new advance. */
} else if (side->autoresearch) {
auto_pick_side_research(side);
}
a = side->research_topic;
/* Compute the maximum number of rps that our material limits
allow us to add. */
- maxrp = 999999;
+ maxrp = VARHI;
for_all_material_types(m) {
consump = am_consumption_per_rp(a, m);
if (consump > 0) {
maxrp = min(maxrp, rp);
}
}
- /* Add 1 rp per turn by default (Why?). */
- if (maxrp == 999999)
- maxrp = 1;
+ /* If research is not limited by materials something is wrong. */
+ if (maxrp == VARHI) {
+ run_error("research is not limited by materials");
+ }
/* Don't spend more than needed to complete the advance! */
maxrp = min(maxrp, a_rp(a) - side->advance[a]);
- side->advance[a] += maxrp;
- /* Make sure the research window is updated. */
- if (side_has_display(side)) {
- update_side_display(side, side, TRUE);
- }
- if (side->advance[a] >= a_rp(a)) {
- side->advance[a] = DONE;
- /* Update research and build vectors. */
- update_canresearch_vector(side);
- update_canbuild_vector(side);
- notify(side, "Your wise men discover %s!", a_type_name(a));
- side->research_topic = NOADVANCE;
- if (side_has_ai(side)) {
- ;
- } else if (side->autoresearch) {
- auto_pick_side_research(side);
+ /* Only proceed if we are still doing some research. */
+ if (maxrp > 0) {
+ side->advance[a] += maxrp;
+ /* Make sure the research window is updated. */
+ if (side_has_display(side)) {
+ update_research_display(side);
}
- }
- /* Consume the materials. */
- for_all_material_types(m) {
- consump = am_consumption_per_rp(a, m);
- if (consump > 0) {
- amt = maxrp * consump;
- side->treasury[m] -= amt;
+ if (side->advance[a] >= a_rp(a)) {
+ side->advance[a] = DONE;
+ /* Update research and build vectors. */
+ update_canresearch_vector(side);
+ update_canbuild_vector(side);
+ /* (TODO: Replace "Your wise men discover" with a custom
+ string.) */
+ notify(side, "Your wise men discover %s!", a_type_name(a));
+ side->research_topic = NOADVANCE;
+ nextgoal = lispnil;
+ choice = lispnil;
+ if (a == side->research_goal) {
+ side->research_goal = NONATYPE;
+ nextgoal = a_ai_next_goal(a);
+ }
+ if (nextgoal != lispnil) {
+ if (consp(nextgoal)) {
+ choice = choose_side_research_goal_from_weighted_list(
+ nextgoal, &totalweight, side);
+ }
+ else {
+ choice = eval(nextgoal);
+ }
+ }
+ if (choice != lispnil) {
+ if (symbolp(choice))
+ choice = eval_symbol(choice);
+ if (atypep(choice)) {
+ if (is_advance_type(c_number(choice)))
+ side->research_goal = c_number(choice);
+ else
+ run_warning(
+"Invalid research goal provided by advance, %s, upon reserach completion",
+ a_type_name(a));
+ }
+ else
+ run_warning(
+"Invalid research goal provided by advance, %s, upon reserach completion",
+ a_type_name(a));
+ }
+ if (side_has_ai(side)) {
+ /* Do nothing. ai_plan_research will pick a new advance. */
+ } else if (side->autoresearch) {
+ auto_pick_side_research(side);
+ }
+ /* Make sure the research window is updated. */
+ if (side_has_display(side)) {
+ update_research_display(side);
+ }
+ }
+ /* Consume the materials. */
+ for_all_material_types(m) {
+ consump = am_consumption_per_rp(a, m);
+ if (consump > 0) {
+ amt = maxrp * consump;
+ side->treasury[m] -= amt;
+ }
}
}
}
}
/* Advanced unit and advance code for Xconq.
- Copyright (C) 1998 Hans Ronne. */
+ Copyright (C) 1998 Hans Ronne.
+ Copyright (C) 2004 Eric A. McDonald.
+*/
void
update_canresearch_vector(Side *side)
{
int a, a2;
+ update_side_research_goal_availability(side);
for_all_advance_types(a) {
/* We can't research advances that are already done. */
if (has_advance(side, a)) {
side->canresearch[a] = FALSE;
break;
}
+ if (aa_precludes(a2, a) && has_advance(side, a2)) {
+ side->canresearch[a] = FALSE;
+ break;
+ }
}
}
}
-/* Call this whenever the list of buildable types changes. */
+void
+update_side_research_goal_availability(Side *side)
+{
+ int updated = TRUE;
+ int a = NONATYPE, a2 = NONATYPE;
+
+ assert_error(side, "Attempted to access a NULL side");
+ while (updated) {
+ updated = FALSE;
+ for_all_advance_types(a) {
+ if (side->research_precluded[a])
+ continue;
+ for_all_advance_types(a2) {
+ if ((aa_precludes(a2, a) && has_advance(side, a2))
+ || (side->research_precluded[a2]
+ && aa_needed_to_research(a, a2))) {
+ side->research_precluded[a] = TRUE;
+ updated = TRUE;
+ break;
+ }
+ } /* a2 */
+ if (updated)
+ break;
+ } /* a */
+ } /* while updated */
+}
+
+/* Call this whenever the list of buildable/developable types changes. */
void
update_canbuild_vector(Side *side)
/* Start out optimistic, then following code may disallow some
types. */
side->canbuild[u] = TRUE;
+ side->candevelop[u] = TRUE;
/* Units with zero cp are unbuildable by definition. */
if (u_cp(u) == 0) {
side->canbuild[u] = FALSE;
+ side->candevelop[u] = FALSE;
continue;
}
/* First test if this unit type is at all allowed on our side. */
if (!type_allowed_on_side(u, side)) {
side->canbuild[u] = FALSE;
+ side->candevelop[u] = FALSE;
continue;
}
/* Then test if we have the required tech to both build and own
this type. */
if (u_tech_to_build(u) > 0) {
+ /* Don't "continue" in this group, because we need to set
+ both build and develop cases correctly. */
if (side->tech[u] < u_tech_to_own(u)
- || side->tech[u] < u_tech_to_build(u)) {
- side->canbuild[u] = FALSE;
- continue;
- }
+ || side->tech[u] < u_tech_to_build(u))
+ side->canbuild[u] = FALSE;
+ if (side->tech[u] >= u_tech_max(u))
+ side->candevelop[u] = FALSE;
}
+ /* Consider the effect of advances on ability to build/develop. */
if (numatypes > 0) {
/* Test for an obsoleting advance. */
a = u_obsolete(u);
if (a != NONATYPE && has_advance(side, a)) {
side->canbuild[u] = FALSE;
+ side->candevelop[u] = FALSE;
continue;
}
/* Then test for required advances. */
for_all_advance_types(a) {
if (ua_needed_to_build(u, a) && !has_advance(side, a)) {
side->canbuild[u] = FALSE;
+ side->candevelop[u] = FALSE;
break;
}
}
}
}
-void take_all_from_treasury(Unit *unit);
-
void
+update_cancarry_vector(Side *side)
+{
+ Unit *unit;
+ int u;
+
+ for_all_unit_types(u) {
+ side->cancarry[u] = FALSE;
+ for_all_side_units(side, unit) {
+ if (mobile(unit->type)
+ && could_carry(unit->type, u)) {
+ side->cancarry[u] = TRUE;
+ break;
+ }
+ }
+ }
+}
+
+static void
take_all_from_treasury(Unit *unit)
{
- int m, amount;
+ int m, amount;
- /* Borrow materials from treasury if it exists. */
- for_all_material_types(m) {
- if (side_has_treasury(unit->side, m)
- && um_takes_from_treasury(unit->type, m)) {
- amount= min(unit->side->treasury[m],
- /* Don't exceed the unit's storage capacity. */
- um_storage_x(unit->type, m) - unit->supply[m]);
- unit->supply[m] += amount;
- unit->side->treasury[m] -= amount;
- }
+ /* Borrow materials from treasury if it exists. */
+ for_all_material_types(m) {
+ if (side_has_treasury(unit->side, m)
+ && um_takes_from_treasury(unit->type, m)) {
+ /* Don't exceed the unit's storage capacity unless there is no
+ capacity, in which case take the lot. */
+ if (um_storage_x(unit->type, m) <= 0)
+ amount = unit->side->treasury[m];
+ else
+ amount = min(unit->side->treasury[m],
+ um_storage_x(unit->type, m) - unit->supply[m]);
+ unit->supply[m] += amount;
+ unit->side->treasury[m] -= amount;
}
+ }
}
-void give_all_to_treasury(Unit *unit);
-
-void
+static void
give_all_to_treasury(Unit *unit)
{
- int m;
+ int m, amount;
- /* Return all unused materials to treasury. */
- for_all_material_types(m) {
- if (side_has_treasury(unit->side, m)
- && um_gives_to_treasury(unit->type, m)) {
- unit->side->treasury[m] += unit->supply[m];
- unit->supply[m] = 0;
- }
+ /* Return all unused materials to treasury. */
+ for_all_material_types(m) {
+ if (side_has_treasury(unit->side, m)
+ && um_gives_to_treasury(unit->type, m)) {
+ amount = min(unit->supply[m],
+ g_treasury_size() - unit->side->treasury[m]);
+ unit->side->treasury[m] += amount;
+ unit->supply[m] -= amount;
}
+ }
}
/* Called from run_turn_start. */
/* Main city emulating routine. Called by run_turn_start in run2.c. */
+static int *rau_incrs;
+
void
run_advanced_units()
{
- Unit *unit;
+ Unit *unit, *unit2;
for_all_units(unit) {
Side *side;
if (!u_advanced(unit->type))
continue;
+ /* Skip if this is an indep which cannot produce or consume materials. */
+ if (indep(unit) &! g_indepside_has_economy())
+ continue;
+
/* Units still under construction or off-area can't do anything. */
if (!completed(unit) || !in_play(unit))
continue;
if (!nummtypes)
continue;
- if (!user_defined())
- allocate_area_users();
-
/* Make sure landuse is optimize before collecting materials. */
allocate_used_cells(unit);
/* Collect materials from cells controlled by the city. */
for_all_cells_within_reach(unit, x, y) {
int m;
+
if (!inside_area(x, y))
continue;
if (user_at(x, y) == unit->id)
each turn. */
for_all_material_types(m) {
- Unit *unit2;
-
+
/* First compute additive effect of each occupant/facility. */
- for_all_occupants(unit, unit2)
- unit->production[m] += um_occ_add_production(unit2->type, m);
+ for_all_occupants(unit, unit2) {
+ if (is_active(unit2)) {
+ unit->production[m] += um_occ_add_production(unit2->type, m);
+ }
+ }
/* Then compute additive effect of each completed advance. */
if (unit->side) {
for_all_advance_types(a)
if (unit->side->advance[a] == DONE)
unit->production[m] += am_adv_add_production(a, m);
}
+
/* Then factor in multiplicative effect (in %) of each
occupant/facility. */
- for_all_occupants(unit, unit2)
- unit->production[m] *= (float) um_occ_mult_production(unit2->type, m) / 100;
+ for_all_occupants(unit, unit2) {
+ if (is_active(unit2)) {
+ unit->production[m] =
+ (unit->production[m] * um_occ_mult_production(unit2->type, m)) / 100;
+ }
+ }
/* Then factor in multiplicative effect (in %) of each
completed advance. */
if (unit->side) {
for_all_advance_types(a)
if (unit->side->advance[a] == DONE)
- unit->production[m] *= (float) am_adv_mult_production(a, m) / 100;
+ unit->production[m] =
+ (unit->production[m] * am_adv_mult_production(a, m))
+ / 100;
}
+
{
- int m2, anyconversion = FALSE, incrs[MAXMTYPES], tot = 0;
+ int m2, anyconversion = FALSE, tot = 0;
for_all_material_types(m2) {
- incrs[m2] = 0;
+ if (rau_incrs == NULL)
+ rau_incrs = (int *) xmalloc(nummtypes * sizeof(int));
+ rau_incrs[m2] = 0;
if (mm_conversion(m, m2) > 0
&& unit->side->c_rates != NULL
&& unit->side->c_rates[m2] > 0) {
if (mm_conversion(m, m2) > 0
&& unit->side->c_rates != NULL
&& unit->side->c_rates[m2] > 0) {
- incrs[m2] += unit->side->c_rates[m2];
+ rau_incrs[m2] += unit->side->c_rates[m2];
}
}
for_all_material_types(m2) {
- if (incrs[m2] >= tot && unit->production[m] > 0) {
- incrs[m2] -= tot;
- unit->supply[m2] += 1;
+ if (rau_incrs[m2] >= tot
+ && unit->production[m] > 0) {
+ rau_incrs[m2] -= tot;
+ unit->production[m2] += 1;
unit->production[m] -= 1;
}
}
}
}
- /* Finally add production to unit supply. */
- unit->supply[m] += unit->production[m];
+ }
+ /* Finally add production to unit supply. Check that we have room and
+ transfer any excess materials to the treasury if possible. */
+ for_all_material_types(m) {
+ int space, amount;
+
+ space = um_storage_x(unit->type, m) - unit->supply[m];
+ if (unit->production[m] <= space) {
+ unit->supply[m] += unit->production[m];
+ } else {
+ unit->supply[m] = um_storage_x(unit->type, m);
+ amount = unit->production[m] - space;
+ if (side_has_treasury(unit->side, m)
+ && um_gives_to_treasury(unit->type, m)) {
+ amount = min(amount, g_treasury_size() - unit->side->treasury[m]);
+ unit->side->treasury[m] += amount;
+ }
+ }
}
/* Growth or starvation. */
run_population(unit);
allocate_used_cells(unit);
/* Also reflect any changes of the city size text. */
for_all_sides(side) {
- if (side->see_all
- || side_sees_unit(side, unit)
- || side_tracking_unit(side, unit)) {
- update_cell_display(side, unit->x, unit->y, UPDATE_ALWAYS);
+ if (side->see_all) {
+ update_cell_display(side, unit->x, unit->y,
+ UPDATE_ALWAYS);
+ } else if (side_sees_unit(side, unit)
+ || side_tracking_unit(side, unit)
+ || cover(side, unit->x, unit->y) > 0) {
+ see_cell(side, unit->x, unit->y);
}
}
}
-
-#if 0 /* This is now done from the normal run code. */
-
- /* Run acp-independent construction. */
- if (acp_indep(unit))
- run_construction(unit);
- /* Do unit-based research. */
- if (u_can_research(unit->type))
- run_research(unit);
-
-#endif
-
}
}
int
production_at(int x, int y, int m)
{
- int prod, c;
-
- prod = tm_production(terrain_at(x, y), m);
- /* Add bonuses for connection types. */
- for_all_connection_types(c) {
- if (aux_terrain_defined(c)
- && any_connections_at(x, y, c)
- && tm_production(c, m) > 0)
- prod += tm_production(c, m);
+ int prod = 0, t2 = NONTTYPE;
+
+ prod = tm_prod_from_terrain(terrain_at(x, y), m);
+ /* Add bonuses for all aux terrain types. */
+ for_all_aux_terrain_types(t2) {
+ if (aux_terrain_defined(t2)
+ && aux_terrain_at(x, y, t2)
+ && (tm_prod_from_terrain(t2, m) > 0)) {
+ prod += tm_prod_from_terrain(t2, m);
+ }
}
return prod;
}
int a, m, u2;
Side *side = unit->side;
- /* First check if we have the required advances for the next size. */
+ /* If we have no people we are done. */
+ if (!g_people())
+ return;
+ /* Loot the treasury. */
+ take_all_from_treasury(unit);
+ /* Consume necessary materials (food) in proportion to unit size. */
+ for_all_material_types(m) {
+ unit->supply[m] -= unit->size * um_consumption_per_size(u, m);
+ if (unit->supply[m] < 0) {
+ /* Don't allow negative supply. */
+ unit->supply[m] = 0;
+ /* Trigger starvation. */
+ starved = TRUE;
+ }
+ }
+ /* Return remaining materials to treasury. */
+ give_all_to_treasury(unit);
+ /* Starvation! (Size Loss) */
+ if (starved && (unit->size > u_size_min(u))) {
+ /* Shrink size by one. */
+ unit->size -= 1;
+ /* Run warning. */
+ if (unit->size > 0) {
+ notify(side, "The people in %s are starving!",
+ unit_handle(side, unit));
+ } else {
+ notify(side, "%s is no more. The people starved to death.",
+ unit_handle(side, unit));
+ kill_unit(unit, H_UNIT_STARVED);
+ }
+ /* Done with handling starvation. */
+ return;
+ }
+ /* Growth (Size Gain) */
+ /* First check if we have reached our max size. */
+ if (unit->size >= u_size_max(u))
+ return;
+ /* Then check if we have the required advances for the next size. */
for_all_advance_types(a) {
if (side->advance[a] != DONE
&& ua_size_limit_without_adv(u, a) <= unit->size) {
Unit *unit2;
for_all_occupants(unit, unit2) {
- if (unit2->type == u2) {
+ if (is_active(unit2) && unit2->type == u2) {
hasocc = TRUE;
break;
}
return;
}
}
- }
-
- /* Loot the treasury. */
+ }
+ /* Loot the treasury again, in case storage space was limiting before
+ consumption. */
take_all_from_treasury(unit);
-
- /* Consume necessary materials (food) in proportion to unit size. */
- for_all_material_types(m) {
- unit->supply[m] -= unit->size * um_consumption_per_size(u, m);
- if (unit->supply[m] < 0) {
- /* Don't allow negative supply. */
- unit->supply[m] = 0;
- /* Trigger starvation. */
- starved = TRUE;
- }
- }
- /* Starvation! */
- if (starved) {
- /* Shrink size by one. */
- unit->size -= 1;
- /* Run warning. */
- if (unit->size > 0) {
- notify(side, "The people in %s are starving!",
- unit_handle(side, unit));
- } else {
- /* Kill the unit, but first return borrowed material to
- treasury. */
- give_all_to_treasury(unit);
- notify(side, "%s is no more. The people starved to death.",
- unit_handle(side, unit));
- kill_unit(unit, H_UNIT_STARVED);
- }
- /* Done with handling starvation. */
- return;
- }
-
/* Now check if we have enough supplies left to grow in size. */
- /* (Should check for granary effect). */
+ /*! \todo Consider occupant effects on growth consumption. */
for_all_material_types(m) {
if (unit->supply[m] < unit->size * um_consumption_to_grow(u, m)) {
- /* Return borrowed stuff to treasury. */
- give_all_to_treasury(unit);
- return;
+ /* Return borrowed stuff to treasury. */
+ give_all_to_treasury(unit);
+ return;
}
}
/* If we got this far we are ready for a size increase! */
for_all_material_types(m) {
- int consump;
- Unit *occ;
-
- consump = unit->size * um_consumption_to_grow(u, m);
- for_all_occupants(unit, occ) {
- /* (Should use utype property instead). */
- if (strcmp(u_type_name(occ->type), "Granary") == 0)
- consump /= 2;
- }
- /* Use up more supplies as required to prepare growth. */
- unit->supply[m] -= consump;
+ int consump = unit->size * um_consumption_to_grow(u, m);
+ /*! \todo Consider occupant effects on growth consumption. */
+ /* Use up more supplies as required to prepare growth. */
+ unit->supply[m] -= consump;
}
/* Then increase the size. */
unit->size += 1;
- /* And bragg about it. */
+ /* And brag about it. */
notify(side, "%s prospers and reaches size %d.",
unit_handle(side, unit), unit->size);
-
/* Return borrowed stuff to treasury. */
give_all_to_treasury(unit);
}
+#if (0) // A few leftover snippets worth considering.
void
run_construction(Unit *unit)
{
- int builds = unit->size;
/* Skip if this is an indep which cannot build. */
- if (indep(unit) &! g_indepside_can_build())
+ if (indep(unit) && !g_indepside_can_build())
return;
- /* Replan for passive units under ai control. */
- if ((!side_has_display(unit->side)
- || ai_controlled(unit)
- || unit->autoplan)
- && (unit->plan->type == PLAN_NONE
- || unit->plan->type == PLAN_PASSIVE)) {
- auto_pick_new_plan(unit);
- }
-
- /* Loot the treasury. */
- take_all_from_treasury(unit);
-
- while (builds) {
- Unit *unit2 = NULL;
- int u2 = NONUTYPE;
- int found;
- int limited = FALSE;
- int actionresult;
- TaskOutcome taskresult;
- int m;
-
- if (unit->plan
- && unit->plan->tasks) {
- unit2 = find_unit(unit->plan->tasks->args[1]);
- u2 = unit->plan->tasks->args[0];
- }
- /* Check if we lack a build task or just completed one. */
- if (unit->plan->tasks == NULL
- || unit->plan->tasks->type != TASK_BUILD
- || (unit->plan->tasks->type == TASK_BUILD
- && unit->plan->tasks->args[2] >= unit->plan->tasks->args[3])) {
- /* Find any incomplete occupant that is hanging around.
- This is also how we pick up old tasks after saving a
- game. */
- found = FALSE;
- for_all_occupants(unit, unit2) {
- if (in_play(unit2) && !fullsized(unit2)) {
- found = TRUE;
- break;
- }
- }
- /* Resume building of incomplete occupant. */
- if (found){
- resume_build_task(unit, unit2, 1, 0, 0);
- u2 = unit2->type;
- } else {
- /* Else select a new unit type to build. */
- if (!side_has_display(unit->side)
- || ai_controlled(unit)
- || unit->autobuild) {
- /* ai control. */
- u2 = auto_pick_new_build_task(unit);
- } else {
- /* Human control. */
- clear_task_agenda(unit->plan);
- set_waiting_for_tasks(unit, TRUE);
- unit->buildingdone = TRUE;
- goto end;
- }
- /* Emergency exit. Happens if "Idle" is manually
- selected or if the city is full. */
- if (!is_unit_type(u2))
- goto end;
- }
- }
- /* unit2 has either not been created or is complete. */
- if (unit2 == NULL || fullsized(unit2)) {
- /* First check if there is enough supplies for creation. */
- for_all_material_types(m) {
- if (unit->supply[m] < um_consumption_on_creation(u2, m)) {
- unit->buildingdone = TRUE;
- goto end;
- }
- }
- /* Then consume materials as required. */
- for_all_material_types(m) {
- unit->supply[m] -= um_consumption_on_creation(u2, m);
- }
- /* unit2 is still in progress. */
- } else {
- /* Check if any material limits its construction. */
- for_all_material_types(m) {
- if (um_consumption_per_cp(u2, m) > 0) {
- limited = TRUE;
- break;
- }
- }
- if (limited) {
- /* First check if there is enough supplies for one build. */
- for_all_material_types(m) {
- if (unit->supply[m] < um_consumption_per_cp(u2, m)) {
- unit->buildingdone = TRUE;
- goto end;
- }
- }
- /* Then consume materials as required. */
- for_all_material_types(m) {
- unit->supply[m] -= um_consumption_per_cp(u2, m);
- }
- /* Else limit total builds to unit size as fallback. */
- } else {
- --builds;
- if (!builds)
- unit->buildingdone = TRUE;
- }
- }
-
- /* Prepare the build action. */
- taskresult = execute_task(unit);
- /* Does happen when the city is full. */
- if (taskresult == TASK_FAILED || taskresult == TASK_UNKNOWN) {
- Dprintf("Task %s failed for %s!\n",
- task_desig(unit->plan->tasks), unit_desig(unit));
- /* Prevents infinite loop. */
- unit->buildingdone = TRUE;
- goto end;
- }
- /* Do not execute pending action if we lack one or need to
- pick a new task! The latter is important since we will
- continue to build on the old unit and get an error if a new
- build task has not been assigned. */
- if (has_pending_action(unit) && taskresult != TASK_IS_COMPLETE) {
- actionresult = execute_action(unit, &unit->act->nextaction);
- /* Happens if create unit failed due to design limits on numbers. */
- if (actionresult == A_ANY_ERROR) {
- Dprintf("Action %s failed for %s. Clearing the task agenda.\n",
- action_desig(&unit->act->nextaction),
- unit_desig(unit));
- /* Clear tasks so that a new build task is picked. */
- clear_task_agenda(unit->plan);
- /* Alert the human player to the fact. */
- if (side_has_display(unit->side)
- && !ai_controlled(unit)
- && !unit->autobuild) {
- set_waiting_for_tasks(unit, TRUE);
- }
- unit->buildingdone = TRUE;
- goto end;
- }
- }
- }
-
-end:
- /* Return any borrowed material to treasury. */
- give_all_to_treasury(unit);
update_unit_acp_display(unit->side, unit, TRUE);
-
/* Things have changed. */
gamestatesafe = FALSE;
}
+#endif
/* Do research until supply of its limiting material is gone. */
int m, a, lim = PROPHI;
/* Check if independent units are allowed to do research. */
- if (indep(unit) &! g_indepside_can_research())
- return;
+ if (indep(unit) && !g_indepside_can_research())
+ return;
/* First test if we are already done, and select new research in
that case. */
if (has_advance(unit->side, unit->curadvance)
unit->curadvance = NOADVANCE;
/* Pick new research manually for human players. */
if (side_has_display(unit->side)
- && !side_has_ai(unit->side)
- && !unit->autoresearch)
+ && !side_has_ai(unit->side))
unit_research_dialog(unit);
/* Pick new research automatically for AIs. */
else
auto_pick_unit_research(unit);
}
-
/* Only proceed if we now have a new advance. */
a = unit->curadvance;
if (a == DONE)
- return;
+ return;
/* Loot the treasury. */
take_all_from_treasury(unit);
-
/* Loop until break. */
while (1) {
-
/* Find the material whose supply limits current research. */
for_all_material_types(m) {
- if (am_consumption_per_rp(a, m) > 0) {
- lim = min(lim, (float) unit->supply[m] / am_consumption_per_rp(a, m));
- }
+ if (am_consumption_per_rp(a, m) > 0) {
+ lim = min(lim, (int)((float) unit->supply[m] /
+ am_consumption_per_rp(a, m)));
+ }
}
- /* Don't do more research than needed to complete the advance, though. */
+ /* Don't do more research than needed to complete the advance though. */
lim = min(lim, a_rp(a) - unit->side->advance[a]);
-
/* Then do research to the limit. */
unit->side->advance[a] += lim;
-
/* Reduce all supplies accordingly. */
for_all_material_types(m) {
if (am_consumption_per_rp(a, m) > 0) {
update_canresearch_vector(unit->side);
update_canbuild_vector(unit->side);
/* Notify the units side about it. */
+ /* (TODO: Change "wise men in" to be a customizable string.) */
+ /* (TODO: Change "discover" to be a customizable string.) */
notify(unit->side, "Your wise men in %s discover %s!",
- unit->name, a_type_name(a));
+ unit->name, a_type_name(a));
/* Pick new research for all involved units. */
if (side_has_display(unit->side)
&& !side_has_ai(unit->side)) {
for_all_side_units(unit->side, unit2) {
- if (!u_advanced(unit2->type))
- continue;
- /* Reallocate units on auto that took part in this
- advance. */
- if (unit2->curadvance == a && unit2->autoresearch) {
- auto_pick_unit_research(unit2);
- /* Detect idle units on non-auto. */
- } else if (unit2->curadvance == a) {
- unit2->curadvance = 0;
- unit_research_dialog(unit2);
- }
+ if (!u_advanced(unit2->type))
+ continue;
+ /* Reallocate units on auto that took part in this
+ advance. */
+ if (unit2->curadvance == a) {
+ auto_pick_unit_research(unit2);
+ /* Detect idle units on non-auto. */
+ } else if (unit2->curadvance == a) {
+ unit2->curadvance = 0;
+ unit_research_dialog(unit2);
+ }
}
} else {
for_all_side_units(unit->side, unit2) {
}
/* Research ended because we ran out of materials. */
} else {
- unit->researchdone = TRUE;
- break;
+ unit->researchdone = TRUE;
+ break;
}
}
/* Return all materials to treasury. */
void
allocate_used_cells(Unit *unit)
{
- int x, y, a, i, m, mlim = 0, lim = 0;
- int u = unit->type;
- Unit *unit2;
-
- /* Set max used cells to unit size. */
- unit->maxcells = unit->size;
- if (u_use_own_cell(u))
- ++(unit->maxcells);
- /* First compute additive effect of each occupant/facility. */
- for_all_occupants(unit, unit2)
- unit->maxcells += uu_occ_add_maxcells(u, unit2->type);
- /* Then compute additive effect of completed advances. */
- for_all_advance_types(a)
- unit->maxcells += ua_adv_add_maxcells(u, a);
- /* Then factor in multiplicative effect (in %) of each
- occupant/facility. */
- for_all_occupants(unit, unit2)
- unit->maxcells *= (float) uu_occ_mult_maxcells(u, unit2->type) / 100;
- /* Then factor in multiplicative effect (in %) of completed advances. */
- for_all_advance_types(a)
- unit->maxcells *= (float) ua_adv_mult_maxcells(u, a) / 100;
-
- /* Check the number of used cells. */
- unit->usedcells = 0;
- for_all_cells_within_reach(unit, x, y) {
- if (!inside_area(x, y))
- continue;
- if (user_at(x, y) == unit->id)
- unit->usedcells += 1;
- }
- /* Find the limiting material for population maintenance. */
- for_all_material_types(m) {
- if (um_consumption_per_size(u, m) > lim) {
- lim = um_consumption_per_size(u, m);
- mlim = m;
+ int x, y, a, i, m, mlim = 0, lim = 0;
+ int u = unit->type;
+ Unit *unit2;
+
+ /* Set max used cells to unit size. */
+ unit->maxcells = unit->size;
+ if (u_use_own_cell(u))
+ ++(unit->maxcells);
+ /* First compute additive effect of each occupant/facility. */
+ for_all_occupants(unit, unit2) {
+ if (is_active(unit2)) {
+ unit->maxcells += uu_occ_add_maxcells(u, unit2->type);
+ }
}
- }
-
- /* Free all of this unit's doubtful cells within reach. */
- for_all_cells_within_reach(unit, x, y) {
- /* Skip if not inside area */
- if (!inside_area(x, y))
- continue;
- /* Free cells used by the unit itself if it is independent or
- played by an AI. */
- if (user_at(x, y) == unit->id
- && (indep(unit) || side_has_ai(unit->side))) {
- set_user_at(x, y, NOUSER);
- unit->usedcells -= 1;
- } else if (user_at(x, y) != NOUSER && unit_at(x, y) != NULL) {
- /* Also free cells used by enemy side if we have a unit in
- the cell. */
- for_all_stack(x, y, unit2) {
- if (unit2->side && unit2->side == unit->side) {
- kick_out_enemy_users(unit->side, x, y);
- break;
+ /* Then compute additive effect of completed advances. */
+ for_all_advance_types(a) {
+ if (has_advance(unit->side, a)) {
+ unit->maxcells += ua_adv_add_maxcells(u, a);
+ }
+ }
+ /* Then factor in multiplicative effect (in %) of each
+ occupant/facility. */
+ for_all_occupants(unit, unit2) {
+ if (is_active(unit2)) {
+ unit->maxcells = (unit->maxcells * uu_occ_mult_maxcells(u, unit2->type)) / 100;
+ }
+ }
+ /* Then factor in multiplicative effect (in %) of completed advances. */
+ for_all_advance_types(a) {
+ if (has_advance(unit->side, a)) {
+ unit->maxcells = (unit->maxcells * ua_adv_mult_maxcells(u, a)) / 100;
+ }
+ }
+ /* Check the number of used cells. */
+ unit->usedcells = 0;
+ for_all_cells_within_reach(unit, x, y) {
+ if (!inside_area(x, y))
+ continue;
+ if (user_at(x, y) == unit->id)
+ unit->usedcells += 1;
+ }
+ /* Find the limiting material for population maintenance. */
+ for_all_material_types(m) {
+ if (um_consumption_per_size(u, m) > lim) {
+ lim = um_consumption_per_size(u, m);
+ mlim = m;
}
- }
}
- }
- /* May want to use own cell automatically. */
- if (u_use_own_cell(u) && user_at(unit->x, unit->y) == NOUSER) {
- set_user_at(unit->x, unit->y, unit->id);
- unit->usedcells += 1;
- }
- /* Then select new cells up to unit size */
- for (i = unit->usedcells + 1; i <= unit->maxcells; i++) {
- int prod, tmpprod = 0, tmpx = unit->x, tmpy = unit->y;
+ /* Free all of this unit's doubtful cells within reach. */
for_all_cells_within_reach(unit, x, y) {
- /* Skip if not inside area */
- if (!inside_area(x, y))
- continue;
- /* Skip if cell already is in use */
- if (user_at(x, y) != NOUSER)
- continue;
- /* Skip if cell is invisible to unit */
- if (!terrain_visible(unit->side, x, y))
- continue;
- /* Skip if untrusted side has a unit in the cell */
- if (unit_at(x, y) != NULL) {
- int enemy = FALSE;
- for_all_stack(x, y, unit2) {
- if (!trusted_side(unit->side, unit2->side)) {
- enemy = TRUE;
- break;
- }
+ /* Skip if not inside area */
+ if (!inside_area(x, y))
+ continue;
+ /* Free cells used by the unit itself if it is independent or
+ played by an AI. */
+ if (user_at(x, y) == unit->id
+ && (indep(unit) || side_has_ai(unit->side))) {
+ set_user_at(x, y, NOUSER);
+ unit->usedcells -= 1;
+ } else if (user_at(x, y) != NOUSER && unit_at(x, y) != NULL) {
+ /* Also free cells used by enemy side if we have a unit in
+ the cell. */
+ for_all_stack(x, y, unit2) {
+ if (unit2->side && unit2->side == unit->side) {
+ kick_out_enemy_users(unit->side, x, y);
+ break;
+ }
+ }
}
- if (enemy)
- continue;
- }
- /* Make this tmp used cell if it produces more limiting
- material than previous tmp used cell or if the previous
- tmp cell is already in use. */
- prod = production_at(x, y, mlim);
- if ((prod > tmpprod) || (user_at(tmpx, tmpy) != NOUSER)) {
- tmpprod = prod;
- tmpx = x; tmpy = y;
- }
}
- /* Make tmp used cell permanently used if it is unused. */
- if (user_at(tmpx, tmpy) == NOUSER) {
- set_user_at(tmpx, tmpy, unit->id);
- unit->usedcells += 1;
+ /* May want to use own cell automatically. */
+ if (u_use_own_cell(u) && user_at(unit->x, unit->y) == NOUSER) {
+ set_user_at(unit->x, unit->y, unit->id);
+ unit->usedcells += 1;
+ }
+ /* Then select new cells up to unit size */
+ for (i = unit->usedcells + 1; i <= unit->maxcells; i++) {
+ int prod, tmpprod = 0, tmpx = unit->x, tmpy = unit->y;
+
+ for_all_cells_within_reach(unit, x, y) {
+ /* Skip if not inside area */
+ if (!inside_area(x, y))
+ continue;
+ /* Skip if cell already is in use */
+ if (user_at(x, y) != NOUSER)
+ continue;
+ /* Skip if cell is invisible to unit */
+ if (!terrain_visible(unit->side, x, y))
+ continue;
+ /* Skip if untrusted side has a unit in the cell */
+ if (unit_at(x, y) != NULL) {
+ int enemy = FALSE;
+ for_all_stack(x, y, unit2) {
+ if (!trusted_side(unit->side, unit2->side)) {
+ enemy = TRUE;
+ break;
+ }
+ }
+ if (enemy)
+ continue;
+ }
+ /* Make this tmp used cell if it produces more limiting
+ material than previous tmp used cell or if the previous
+ tmp cell is already in use. */
+ prod = production_at(x, y, mlim);
+ if ((prod > tmpprod) || (user_at(tmpx, tmpy) != NOUSER)) {
+ tmpprod = prod;
+ tmpx = x; tmpy = y;
+ }
+ }
+ /* Make tmp used cell permanently used if it is unused. */
+ if (user_at(tmpx, tmpy) == NOUSER) {
+ set_user_at(tmpx, tmpy, unit->id);
+ unit->usedcells += 1;
+ }
}
- }
}
/* Can't use a cell occupied by an enemy unit. */
/* Now picks new advance at random rather than by number. */
+static int *apr_type;
+
void
auto_pick_unit_research(Unit *unit)
{
int a;
- int type[MAXATYPES];
int numtypes = 0;
+ if (apr_type == NULL)
+ apr_type = (int *) xmalloc(numutypes * sizeof(int));
+
/* Get researchable advance types. */
for_all_advance_types(a) {
if (side_can_research(unit->side, a)) {
/* Add to list of researchable advances. */
- type[numtypes++] = a;
+ apr_type[numtypes++] = a;
}
}
if (numtypes)
/* Pick one of them at random. */
- unit->curadvance = type[xrandom(numtypes)];
+ unit->curadvance = apr_type[xrandom(numtypes)];
else
unit->curadvance = NOADVANCE;
}
-/* Now picks new advance at random rather than by number. */
+/* Try to pick a good advance to research. */
void
auto_pick_side_research(Side *side)
{
- int a;
- int type[MAXATYPES];
- int numtypes = 0;
-
- /* Get researchable advance types. */
- for_all_advance_types(a) {
- if (side_can_research(side, a)) {
- /* Add to list of researchable advances. */
- type[numtypes++] = a;
- }
- }
- if (numtypes > 0)
- /* Pick one of them at random. */
- side->research_topic = type[xrandom(numtypes)];
- else
- side->research_topic = NONATYPE;
+ ai_pick_side_research(side);
}
+#if (0)
+static int *apnbt_types;
+
int
auto_pick_new_build_task(Unit *unit)
{
- int prefs[MAXUTYPES] = {0};
int enemy_city_near = FALSE;
int x, y, x1, y1, u;
int city_has_shore = FALSE;
int numtypes = 0;
+ Side *side = unit->side;
Unit *unit2;
-
- /* Reevaluate the current plan every time. */
- auto_pick_new_plan(unit);
+ if (apnbt_types == NULL)
+ apnbt_types = (int *) xmalloc(numutypes * sizeof(int));
+ /* Zero the apnbt vector! */
+ for_all_unit_types(u) {
+ apnbt_types[u] = 0;
+ }
+ /* Reevaluate the current plan every time. */
+ auto_pick_new_plan(unit);
/* Check if the unit borders on a water cell. */
for_all_cells_within_range(unit->x, unit->y, 1, x, y) {
- if (t_liquid(terrain_at(wrapx(x), y))) {
+ if (!inside_area(x, y))
+ continue;
+ if (t_liquid(terrain_at(x, y))) {
/* Also check if that water cell borders on another water cell.
(we don't want to build battleships in the city pond) */
for_all_cells_within_range(x, y, 1, x1, y1) {
- if (t_liquid(terrain_at(wrapx(x1), y1))
+ if (!inside_area(x1, y1))
+ continue;
+ if (t_liquid(terrain_at(x1, y1))
/* Don't count the first water cell. */
&& (x != x1 || y != y1)) {
city_has_shore = TRUE;
break;
}
}
-
/* Check if we have an enemy city within our tactical range. */
for_all_units(unit2) {
if (u_advanced(unit2->type)
&& distance(unit->x, unit->y, unit2->x, unit2->y)
- >= u_ai_tactical_range(unit->type)
- && enemy_side(unit->side, unit2->side)
+ <= u_ai_enemy_alert_range(unit->type)
+ && enemy_side(side, unit2->side)
/* Only count cities that we can actually see. */
- && side_sees_image(unit->side, unit2)) {
+ && side_sees_image(side, unit2)) {
enemy_city_near = TRUE;
break;
}
}
-
/* Calculate unit values. */
for_all_unit_types(u) {
- if (side_can_build(unit->side, u)
- && could_create(unit->type, u)
+ if (side_can_build(side, u)
+ && could_create(unit->type, u)) {
/* Don't build immobile units for which there is no room. */
- && (mobile(u) || type_can_occupy(u, unit))
- /* Don't build ships if we don't have a shore. */
- && (!u_naval_mobile(u) || city_has_shore)) {
-
+ if (!mobile(u)
+ && !type_can_occupy(u, unit)) {
+ continue;
+ }
+ /* Don't build ships (naval-only movers)
+ if we don't have a shore. */
+ if (u_naval_mobile(u)
+ && !city_has_shore
+ && !u_air_mobile(u)
+ && !u_ground_mobile(u)) {
+ continue;
+ }
switch (unit->plan->type) {
- case PLAN_COLONIZING:
- prefs[u] = u_colonizer_worth(u);
- break;
case PLAN_IMPROVING:
- prefs[u] = u_facility_worth(u);
+ apnbt_types[u] = u_facility_worth(u);
break;
case PLAN_EXPLORATORY:
- prefs[u] = u_explorer_worth(u);
+ apnbt_types[u] = u_ai_explorer_worth(u);
break;
case PLAN_OFFENSIVE:
if (enemy_city_near) {
- /* Build siege units. */
- prefs[u] = u_siege_worth(u);
+ /* Build 50% siege units. */
+ if (probability(50))
+ apnbt_types[u] = u_siege_worth(u);
+ else
+ apnbt_types[u] = u_siege_worth(u);
} else {
- /* Build attack units. */
- prefs[u] = u_offensive_worth(u);
+ /* Build only attack units. */
+ apnbt_types[u] = u_offensive_worth(u);
}
break;
case PLAN_DEFENSIVE:
- prefs[u] = occ_can_defend_transport(u, unit->type)
+ apnbt_types[u] = occ_can_defend_transport(u, unit->type)
* u_defensive_worth(u);
break;
- case PLAN_RANDOM:
- prefs[u] = u_random_worth(u);
- break;
case PLAN_NONE:
case PLAN_PASSIVE:
case NUMPLANTYPES:
- prefs[u] = 0;
+ apnbt_types[u] = 0;
break;
}
}
- if (prefs[u] <= 0)
- prefs[u] = 0;
+ if (apnbt_types[u] <= 0)
+ apnbt_types[u] = 0;
else ++numtypes;
}
-
/* Try again with random choice if we found nothing to build. */
if (numtypes == 0) {
for_all_unit_types(u) {
- if (side_can_build(unit->side, u)
- && could_create(unit->type, u)
- /* Don't build immobile units for which there is no room. */
- && (mobile(u) || type_can_occupy(u, unit))
- /* Don't build ships if we don't have a shore. */
- && (!u_naval_mobile(u) || city_has_shore)) {
-
- prefs[u] = u_random_worth(u);
- if (prefs[u] <= 0)
- prefs[u] = 0;
+ if (side_can_build(side, u)
+ && could_create(unit->type, u)) {
+ /* Don't build immobile units for which there is no room. */
+ if (!mobile(u)
+ && !type_can_occupy(u, unit)) {
+ continue;
+ }
+ /* Don't build ships (naval-only movers)
+ if we don't have a shore. */
+ if (u_naval_mobile(u)
+ && !city_has_shore
+ && !u_air_mobile(u)
+ && !u_ground_mobile(u)) {
+ continue;
+ }
+ apnbt_types[u] = u_random_worth(u);
+ if (apnbt_types[u] <= 0)
+ apnbt_types[u] = 0;
else ++numtypes;
}
}
}
-
if (numtypes > 0) {
/* Use prefs weight to pick unit type to build. */
- u = select_by_weight(prefs, numutypes);
+ u = select_by_weight(apnbt_types, numutypes);
if (is_unit_type(u))
- set_build_task(unit, u, 1, 0, 0);
+ set_construct_task(unit, u, 1, -1, unit->x, unit->y);
else
u = NONUTYPE;
} else
u = NONUTYPE;
+ if (u == NONUTYPE) {
+ set_unit_reserve(side, unit, TRUE, FALSE);
+ notify(side, "%s unable to pick build task, waiting one turn.",
+ unit_handle(side, unit));
+ }
return u;
}
/* Init plan if necessary. */
if (!unit->plan)
init_unit_plan(unit);
-
/* First count our facilities, defenders and mobile types. */
for_all_occs_with_occs(unit, unit2) {
if (u_facility(unit2->type))
set_unit_plan_type(unit->side, unit, PLAN_DEFENSIVE);
return;
}
-
/* Check out the tactical neighbourhood. */
for_all_cells_within_range(unit->x, unit->y,
u_ai_tactical_range(unit->type), x, y) {
}
}
}
-
if (enemy_total > 0
&& can_build_attackers(unit->side, unit->type)) {
/* Always build attackers if any enemy unit is near. */
&& can_build_explorers(unit->side, unit->type)) {
/* Then build explorers if we have any invisible cells nearby. */
set_unit_plan_type(unit->side, unit, PLAN_EXPLORATORY);
- } else if (my_advanced + my_colonizers < 7
- && can_build_colonizers(unit->side, unit->type)) {
- /* Build colonizers if we have < 7 colonizers or places nearby. */
- set_unit_plan_type(unit->side, unit, PLAN_COLONIZING);
} else if (flip_coin() && facilities < u_facility_total_max(u)
&& can_build_facilities(unit->side, unit->type)) {
/* Build 50% facilities if there is room left. */
} else if (can_build_attackers(unit->side, unit->type)) {
/* Else build offensive units. */
set_unit_plan_type(unit->side, unit, PLAN_OFFENSIVE);
- } else
- /* Fallback - causes building of any unit that can be built. */
- set_unit_plan_type(unit->side, unit, PLAN_RANDOM);
-
- clear_task_agenda(unit->plan);
+ } else {
+ set_unit_plan_type(unit->side, unit, PLAN_PASSIVE);
+ set_unit_reserve(unit->side, unit, TRUE, FALSE);
+ }
}
+#endif