Removed unused ai:attack-with-force ccl function
This commit is contained in:
parent
6ceea59b80
commit
b7c9fd452a
4 changed files with 3 additions and 895 deletions
|
@ -54,7 +54,6 @@
|
|||
<a href="#define-ai-player">define-ai-player</a>
|
||||
<a href="pud.html#define-ai-wc-names">define-ai-wc-names</a>
|
||||
<a href="#ai:adhoc-force">ai:adhoc-force</a>
|
||||
<a href="#ai:attack-with-force">ai:attack-with-force</a>
|
||||
<a href="#ai:check-force">ai:check-force</a>
|
||||
<a href="#ai:clear-force">ai:clear-force</a>
|
||||
<a href="#ai:compute-gauges">ai:compute-gauges</a>
|
||||
|
@ -113,9 +112,8 @@ There are two kinds of scripts :
|
|||
Scripts can arrange and control units using forces : <br>
|
||||
A script can ask for some type of unit in a force (using <a href="#ai:force">ai:force</a>),
|
||||
and then wait for them to be ready (using <a href="#ai:wait-force">ai:wait-force</a>).<br>
|
||||
Then it can send them to attack ( <a href="#ai:attack-with-force">ai:attack-with-force</a>,
|
||||
<a href="#ai:hotspot-attack-with-force">ai:hotspot-attack-with-force</a> ) or back home
|
||||
( <a href="#ai:force-go-home">ai:force-go-home</a> ).
|
||||
Then it can send them to attack ( <a href="#ai:hotspot-attack-with-force">ai:hotspot-attack-with-force</a> )
|
||||
or back home ( <a href="#ai:force-go-home">ai:force-go-home</a> ).
|
||||
<br>
|
||||
Each action/reaction script has a specific force which keeps all its units. ( see <a href="#ai:own-force">ai:own-force</a> )<br>
|
||||
<br>
|
||||
|
@ -436,45 +434,6 @@ its representation in the resulting force.
|
|||
(writes nil "Can't defend with grunts and trolls!"))
|
||||
</pre>
|
||||
|
||||
<h4>Used</h4>
|
||||
|
||||
<a href="../../data/ccl/ai.ccl"> $LIBARYPATH/ccl/ai.ccl </a>
|
||||
|
||||
<a name="ai:attack-with-force"></a>
|
||||
<h3>(ai:attack-with-force force)</h3>
|
||||
|
||||
Attack the opponent with a force. The place is choosen by the AI. If there
|
||||
are flyers, ships and land units in the force they could attack different
|
||||
goals.
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>force</dt>
|
||||
<dd>Number of the force to which the units should belong. 0 - 9 is currently
|
||||
supported.22
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<i>The force isn't moved as unit, faster units attacks first, than later the
|
||||
slower units will attack.</i>
|
||||
|
||||
<h4>Example</h4>
|
||||
|
||||
<pre>
|
||||
;; Force 0 is built with one footman. The script continues processing, if the
|
||||
;; footman is ready trained. Now attack the opponent with force 0.
|
||||
(ai:force 0 'unit-footman 1)
|
||||
(ai:wait-force 0)
|
||||
(ai:attack-with-force 0)
|
||||
</pre>
|
||||
|
||||
<h4>See also</h4>
|
||||
|
||||
<a href="#ai:hotspot-attack-with-force">ai:hotspot-attack-with-force</a>
|
||||
|
||||
<a href="#ai:force-go-home">ai:force-go-home</a>
|
||||
|
||||
|
||||
<h4>Used</h4>
|
||||
|
||||
<a href="../../data/ccl/ai.ccl"> $LIBARYPATH/ccl/ai.ccl </a>
|
||||
|
|
|
@ -838,62 +838,6 @@ global void AiAttackWithForceAt(int force, int x, int y)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Attack opponent with force.
|
||||
**
|
||||
** @param force Force number to attack with.
|
||||
*/
|
||||
global void AiAttackWithForce(int force)
|
||||
{
|
||||
const AiUnit *aiunit;
|
||||
const Unit *enemy;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
AiCleanForce(force);
|
||||
|
||||
AiPlayer->Force[force].Attacking = 0;
|
||||
if ((aiunit = AiPlayer->Force[force].Units)) {
|
||||
AiPlayer->Force[force].Attacking = 1;
|
||||
DebugLevel3Fn("FORCE %d started ( AiAttackWithForce )\n" _C_ force);
|
||||
|
||||
enemy = NoUnitP;
|
||||
while (aiunit && !enemy) { // Use an unit that can attack
|
||||
if (aiunit->Unit->Type->CanAttack) {
|
||||
enemy = AttackUnitsInDistance(aiunit->Unit, MaxMapWidth);
|
||||
}
|
||||
aiunit = aiunit->Next;
|
||||
}
|
||||
|
||||
if (!enemy) {
|
||||
DebugLevel0Fn("Need to plan an attack with transporter\n");
|
||||
if (!AiPlayer->Force[force].State && !AiPlanAttack(&AiPlayer->Force[force])) {
|
||||
DebugLevel0Fn("Can't transport, look for walls\n");
|
||||
if (!AiFindWall(&AiPlayer->Force[force])) {
|
||||
AiPlayer->Force[force].Attacking = 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
AiPlayer->Force[force].State = 0;
|
||||
x = enemy->X;
|
||||
y = enemy->Y;
|
||||
|
||||
//
|
||||
// Send all units in the force to enemy.
|
||||
//
|
||||
aiunit = AiPlayer->Force[force].Units;
|
||||
while (aiunit) {
|
||||
if (aiunit->Unit->Type->CanAttack) {
|
||||
CommandAttack(aiunit->Unit, x, y, NULL, FlushCommands);
|
||||
} else {
|
||||
CommandMove(aiunit->Unit, x, y, FlushCommands);
|
||||
}
|
||||
aiunit = aiunit->Next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Try to group units in a force. Units are grouped arround the closest units of the hotspot.
|
||||
**
|
||||
|
@ -995,189 +939,6 @@ global void AiSendForceHome(int force)
|
|||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Handle attack of force with transporter.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
** Step 1)
|
||||
** Load force on transporters.
|
||||
**
|
||||
** @param force Force pointer.
|
||||
**
|
||||
** @todo If an unit can't reach the transporter the code hangs.
|
||||
** We must the transporter land on a new position.
|
||||
** Or the board action will be better written.
|
||||
*/
|
||||
local void AiLoadForce(AiForce * force)
|
||||
{
|
||||
AiUnit *aiunit;
|
||||
Unit *table[UnitMax];
|
||||
int n;
|
||||
int i;
|
||||
int o;
|
||||
int f;
|
||||
|
||||
//
|
||||
// Find all transporters.
|
||||
//
|
||||
n = 0;
|
||||
aiunit = force->Units;
|
||||
while (aiunit) {
|
||||
if (aiunit->Unit->Type->Transporter) {
|
||||
table[n++] = aiunit->Unit;
|
||||
}
|
||||
aiunit = aiunit->Next;
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
DebugLevel0Fn("No transporter, lost or error in code?\n");
|
||||
force->MustTransport = 0;
|
||||
force->State = 0;
|
||||
return;
|
||||
}
|
||||
//
|
||||
// Load all on transporter.
|
||||
//
|
||||
f = o = i = 0;
|
||||
aiunit = force->Units;
|
||||
while (aiunit) {
|
||||
Unit *unit;
|
||||
|
||||
unit = aiunit->Unit;
|
||||
if (!unit->Type->Transporter && unit->Type->UnitType == UnitTypeLand) {
|
||||
if (!unit->Removed) {
|
||||
f = 1;
|
||||
if (unit->Orders[0].Action != UnitActionBoard) {
|
||||
if (UnitIdle(table[i])) {
|
||||
DebugLevel0Fn("Send transporter %d\n" _C_ i);
|
||||
CommandFollow(table[i], unit, FlushCommands);
|
||||
}
|
||||
CommandBoard(unit, table[i], FlushCommands);
|
||||
++o;
|
||||
// FIXME
|
||||
if (o == table[i]->Type->MaxOnBoard) {
|
||||
DebugLevel0Fn("FIXME: next transporter for AI boarding\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
aiunit = aiunit->Next;
|
||||
}
|
||||
|
||||
if (!f) {
|
||||
DebugLevel0Fn("All are loaded\n");
|
||||
++force->State;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Step 2)
|
||||
** Send force awaay in transporters, to unload at target position.
|
||||
**
|
||||
** @param force Force pointer.
|
||||
**
|
||||
** @todo The transporter should avoid enemy contact and should land
|
||||
** at an unfortified coast. If we send more transporters they
|
||||
** should land on different positions.
|
||||
*/
|
||||
local void AiSendTransporter(AiForce * force)
|
||||
{
|
||||
AiUnit *aiunit;
|
||||
|
||||
//
|
||||
// Find all transporters.
|
||||
//
|
||||
aiunit = force->Units;
|
||||
while (aiunit) {
|
||||
// Transporter to unload units
|
||||
if (aiunit->Unit->Type->Transporter) {
|
||||
CommandUnload(aiunit->Unit, force->GoalX, force->GoalY, NoUnitP,
|
||||
FlushCommands);
|
||||
// Ships to defend transporter
|
||||
} else if (aiunit->Unit->Type->UnitType == UnitTypeNaval) {
|
||||
CommandAttack(aiunit->Unit, force->GoalX, force->GoalY, NoUnitP,
|
||||
FlushCommands);
|
||||
}
|
||||
aiunit = aiunit->Next;
|
||||
}
|
||||
++force->State;
|
||||
}
|
||||
|
||||
/**
|
||||
** Step 3)
|
||||
** Wait for transporters landed.
|
||||
**
|
||||
** @param force Force pointer.
|
||||
**
|
||||
*/
|
||||
local void AiWaitLanded(AiForce * force)
|
||||
{
|
||||
AiUnit *aiunit;
|
||||
int i;
|
||||
|
||||
DebugLevel0Fn("Waiting\n");
|
||||
//
|
||||
// Find all transporters.
|
||||
//
|
||||
i = 1;
|
||||
aiunit = force->Units;
|
||||
while (aiunit) {
|
||||
if (aiunit->Unit->Type->Transporter) {
|
||||
if (UnitIdle(aiunit->Unit)) {
|
||||
DebugLevel0Fn("Unloading\n");
|
||||
// Don't tell empty transporters to unload.
|
||||
if (aiunit->Unit->InsideCount) {
|
||||
CommandUnload(aiunit->Unit, force->GoalX, force->GoalY,
|
||||
NoUnitP, FlushCommands);
|
||||
i = 0;
|
||||
}
|
||||
} else {
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
aiunit = aiunit->Next;
|
||||
}
|
||||
if (i) {
|
||||
++force->State; // all unloaded
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Step 4)
|
||||
** Force on attack ride. We attack until there is no unit or enemy left.
|
||||
**
|
||||
** @param force Force pointer.
|
||||
*/
|
||||
local void AiForceAttacks(AiForce * force)
|
||||
{
|
||||
const AiUnit *aiunit;
|
||||
|
||||
if ((aiunit = force->Units)) {
|
||||
while (aiunit) {
|
||||
// Still some action
|
||||
if (!UnitIdle(aiunit->Unit)) {
|
||||
break;
|
||||
}
|
||||
aiunit = aiunit->Next;
|
||||
}
|
||||
// Must mark the attack as terminated
|
||||
if (!aiunit) {
|
||||
DebugLevel3Fn("FORCE stopped ( AiForceAttacks, unitactionstill )\n");
|
||||
DebugLevel3Fn("force target was %d %d\n" _C_ force->GoalX _C_ force->GoalY);
|
||||
DebugLevel3Fn("unit pos was %d %d\n" _C_ force->Units->Unit->X _C_ force->
|
||||
Units->Unit->Y);
|
||||
|
||||
force->Attacking = 0;
|
||||
// AiAttackWithForce(force-AiPlayer->Force);
|
||||
}
|
||||
} else {
|
||||
DebugLevel3Fn("FORCE stopped ( AiAttackWithForce, no unit )\n");
|
||||
force->Attacking = 0;
|
||||
}
|
||||
}
|
||||
|
||||
global void AiForceHelpMe(int force, const Unit * attacker, Unit * defender)
|
||||
{
|
||||
AiForce *aiForce;
|
||||
|
@ -1223,58 +984,12 @@ global void AiForceHelpMe(int force, const Unit * attacker, Unit * defender)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Handle an attack force.
|
||||
**
|
||||
** @param force Force pointer.
|
||||
*/
|
||||
local void AiGuideAttackForce(AiForce * force)
|
||||
{
|
||||
enum { StartState = 1, TransporterLoaded, WaitLanded, AttackNow };
|
||||
|
||||
switch (force->State) {
|
||||
//
|
||||
// Load units on transporters.
|
||||
//
|
||||
case StartState:
|
||||
AiLoadForce(force);
|
||||
break;
|
||||
case TransporterLoaded:
|
||||
AiSendTransporter(force);
|
||||
break;
|
||||
case WaitLanded:
|
||||
AiWaitLanded(force);
|
||||
break;
|
||||
case AttackNow:
|
||||
force->State = 0;
|
||||
AiAttackWithForce(force - AiPlayer->Force);
|
||||
break;
|
||||
|
||||
//
|
||||
// Attacking!
|
||||
//
|
||||
case 0:
|
||||
AiForceAttacks(force);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** Entry point of force manager, perodic called.
|
||||
*/
|
||||
global void AiForceManager(void)
|
||||
{
|
||||
int force;
|
||||
|
||||
//
|
||||
// Look if our defenders still have enemies in range.
|
||||
//
|
||||
for (force = 0; force < AI_MAX_FORCES; ++force) {
|
||||
if (AiPlayer->Force[force].Attacking) {
|
||||
AiCleanForce(force);
|
||||
AiGuideAttackForce(&AiPlayer->Force[force]);
|
||||
}
|
||||
}
|
||||
// FIXME : is this really needed anymore
|
||||
AiAssignFreeUnitsToForce();
|
||||
}
|
||||
|
||||
|
|
|
@ -50,552 +50,6 @@
|
|||
-- Variables
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Functions
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
** Choose enemy on map tile.
|
||||
**
|
||||
** @param source Unit which want to attack.
|
||||
** @param tx X position on map, tile-based.
|
||||
** @param ty Y position on map, tile-based.
|
||||
**
|
||||
** @return Returns ideal target on map tile.
|
||||
*/
|
||||
local Unit *EnemyOnMapTile(const Unit * source, int tx, int ty)
|
||||
{
|
||||
Unit *table[UnitMax];
|
||||
Unit *unit;
|
||||
Unit *best;
|
||||
const UnitType *type;
|
||||
int n;
|
||||
int i;
|
||||
|
||||
n = SelectUnitsOnTile(tx, ty, table);
|
||||
best = NoUnitP;
|
||||
for (i = 0; i < n; ++i) {
|
||||
unit = table[i];
|
||||
// unusable unit ?
|
||||
// if( UnitUnusable(unit) ) can't attack constructions
|
||||
// FIXME: did SelectUnitsOnTile already filter this?
|
||||
// Invisible and not Visible
|
||||
if (unit->Removed || unit->Invisible || !unit->HP
|
||||
|| !(unit->Visible & (1 << source->Player->Player))
|
||||
|| unit->Orders[0].Action == UnitActionDie) {
|
||||
continue;
|
||||
}
|
||||
type = unit->Type;
|
||||
if (tx < unit->X || tx >= unit->X + type->TileWidth
|
||||
|| ty < unit->Y || ty >= unit->Y + type->TileHeight) {
|
||||
continue;
|
||||
}
|
||||
if (!CanTarget(source->Type, unit->Type)) {
|
||||
continue;
|
||||
}
|
||||
if (!IsEnemy(source->Player, unit)) { // a friend or neutral
|
||||
continue;
|
||||
}
|
||||
//
|
||||
// Choose the best target.
|
||||
//
|
||||
if (!best || best->Type->Priority < unit->Type->Priority) {
|
||||
best = unit;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/**
|
||||
** Mark all by transporter reachable water tiles.
|
||||
**
|
||||
** @param unit Transporter
|
||||
** @param matrix Water matrix.
|
||||
**
|
||||
** @note only works for water transporters!
|
||||
*/
|
||||
local void AiMarkWaterTransporter(const Unit * unit, unsigned char *matrix)
|
||||
{
|
||||
static const int xoffset[] = { 0, -1, +1, 0, -1, +1, -1, +1 };
|
||||
static const int yoffset[] = { -1, 0, 0, +1, -1, -1, +1, +1 };
|
||||
struct {
|
||||
unsigned short X;
|
||||
unsigned short Y;
|
||||
} *points;
|
||||
int size;
|
||||
int x;
|
||||
int y;
|
||||
int rx;
|
||||
int ry;
|
||||
int mask;
|
||||
int wp;
|
||||
int rp;
|
||||
int ep;
|
||||
int i;
|
||||
int w;
|
||||
unsigned char *m;
|
||||
|
||||
x = unit->X;
|
||||
y = unit->Y;
|
||||
w = TheMap.Width + 2;
|
||||
matrix += w + w + 2;
|
||||
if (matrix[x + y * w]) { // already marked
|
||||
DebugLevel0("Done\n");
|
||||
return;
|
||||
}
|
||||
|
||||
points = malloc(TheMap.Width * TheMap.Height);
|
||||
size = TheMap.Width * TheMap.Height / sizeof (*points);
|
||||
|
||||
//
|
||||
// Make movement matrix.
|
||||
//
|
||||
mask = UnitMovementMask(unit);
|
||||
// Ignore all possible mobile units.
|
||||
mask &= ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit);
|
||||
|
||||
points[0].X = x;
|
||||
points[0].Y = y;
|
||||
rp = 0;
|
||||
matrix[x + y * w] = 66; // mark start point
|
||||
ep = wp = 1; // start with one point
|
||||
|
||||
//
|
||||
// Pop a point from stack, push all neightbors which could be entered.
|
||||
//
|
||||
for (;;) {
|
||||
while (rp != ep) {
|
||||
rx = points[rp].X;
|
||||
ry = points[rp].Y;
|
||||
for (i = 0; i < 8; ++i) { // mark all neighbors
|
||||
x = rx + xoffset[i];
|
||||
y = ry + yoffset[i];
|
||||
m = matrix + x + y * w;
|
||||
if (*m) { // already checked
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CanMoveToMask(x, y, mask)) { // reachable
|
||||
|
||||
*m = 66;
|
||||
points[wp].X = x; // push the point
|
||||
points[wp].Y = y;
|
||||
if (++wp >= size) { // round about
|
||||
wp = 0;
|
||||
}
|
||||
/* Must be checked multiple
|
||||
} else { // unreachable
|
||||
*m=99;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
if (++rp >= size) { // round about
|
||||
rp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Continue with next frame.
|
||||
//
|
||||
if (rp == wp) { // unreachable, no more points available
|
||||
break;
|
||||
}
|
||||
ep = wp;
|
||||
}
|
||||
|
||||
free(points);
|
||||
}
|
||||
|
||||
/**
|
||||
** Find possible targets.
|
||||
**
|
||||
** @param unit Attack.
|
||||
** @param matrix Water matrix.
|
||||
** @param dx Attack point X.
|
||||
** @param dy Attack point Y.
|
||||
** @param ds Attack state.
|
||||
**
|
||||
** @return True if target found.
|
||||
*/
|
||||
local int AiFindTarget(const Unit * unit, unsigned char *matrix, int *dx, int *dy,
|
||||
int *ds)
|
||||
{
|
||||
static const int xoffset[] = { 0, -1, +1, 0, -1, +1, -1, +1 };
|
||||
static const int yoffset[] = { -1, 0, 0, +1, -1, -1, +1, +1 };
|
||||
struct {
|
||||
unsigned short X;
|
||||
unsigned short Y;
|
||||
unsigned char State;
|
||||
} *points;
|
||||
int size;
|
||||
int x;
|
||||
int y;
|
||||
int rx;
|
||||
int ry;
|
||||
int mask;
|
||||
int wp;
|
||||
int rp;
|
||||
int ep;
|
||||
int i;
|
||||
int w;
|
||||
enum { OnWater, OnLand, OnIsle } state;
|
||||
unsigned char *m;
|
||||
|
||||
size = TheMap.Width * TheMap.Height / 2;
|
||||
points = malloc(size * sizeof (*points));
|
||||
|
||||
x = unit->X;
|
||||
y = unit->Y;
|
||||
|
||||
w = TheMap.Width + 2;
|
||||
mask = UnitMovementMask(unit);
|
||||
// Ignore all possible mobile units.
|
||||
mask &= ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit);
|
||||
|
||||
points[0].X = x;
|
||||
points[0].Y = y;
|
||||
points[0].State = OnLand;
|
||||
matrix += w + w + 2;
|
||||
rp = 0;
|
||||
matrix[x + y * w] = 1; // mark start point
|
||||
ep = wp = 1; // start with one point
|
||||
|
||||
//
|
||||
// Pop a point from stack, push all neightbors which could be entered.
|
||||
//
|
||||
for (;;) {
|
||||
while (rp != ep) {
|
||||
rx = points[rp].X;
|
||||
ry = points[rp].Y;
|
||||
state = points[rp].State;
|
||||
for (i = 0; i < 8; ++i) { // mark all neighbors
|
||||
x = rx + xoffset[i];
|
||||
y = ry + yoffset[i];
|
||||
m = matrix + x + y * w;
|
||||
|
||||
if (state != OnWater) {
|
||||
if (*m) { // already checked
|
||||
if (state == OnLand && *m == 66) { // tansporter?
|
||||
DebugLevel0Fn("->Water\n");
|
||||
*m = 6;
|
||||
points[wp].X = x; // push the point
|
||||
points[wp].Y = y;
|
||||
points[wp].State = OnWater;
|
||||
if (++wp >= size) { // round about
|
||||
wp = 0;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Check targets on tile?
|
||||
// FIXME: the move code didn't likes a shore building as
|
||||
// target
|
||||
if (EnemyOnMapTile(unit, x, y)) {
|
||||
DebugLevel0Fn("Target found %d,%d-%d\n" _C_ x _C_ y _C_ state);
|
||||
*dx = x;
|
||||
*dy = y;
|
||||
*ds = state;
|
||||
free(points);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (CanMoveToMask(x, y, mask)) { // reachable
|
||||
|
||||
*m = 1;
|
||||
points[wp].X = x; // push the point
|
||||
points[wp].Y = y;
|
||||
points[wp].State = state;
|
||||
if (++wp >= size) { // round about
|
||||
wp = 0;
|
||||
}
|
||||
} else { // unreachable
|
||||
*m = 99;
|
||||
}
|
||||
} else { // On water
|
||||
if (*m) { // already checked
|
||||
if (*m == 66) { // tansporter?
|
||||
*m = 6;
|
||||
points[wp].X = x; // push the point
|
||||
points[wp].Y = y;
|
||||
points[wp].State = OnWater;
|
||||
if (++wp >= size) { // round about
|
||||
wp = 0;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (CanMoveToMask(x, y, mask)) { // reachable
|
||||
DebugLevel0Fn("->Land\n");
|
||||
*m = 1;
|
||||
points[wp].X = x; // push the point
|
||||
points[wp].Y = y;
|
||||
points[wp].State = OnIsle;
|
||||
if (++wp >= size) { // round about
|
||||
wp = 0;
|
||||
}
|
||||
} else { // unreachable
|
||||
*m = 99;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (++rp >= size) { // round about
|
||||
rp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Continue with next frame.
|
||||
//
|
||||
if (rp == wp) { // unreachable, no more points available
|
||||
break;
|
||||
}
|
||||
ep = wp;
|
||||
}
|
||||
free(points);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
** Find possible walls to target.
|
||||
**
|
||||
** @param force Attack force.
|
||||
**
|
||||
** @return True if wall found.
|
||||
*/
|
||||
global int AiFindWall(AiForce * force)
|
||||
{
|
||||
static const int xoffset[] = { 0, -1, +1, 0, -1, +1, -1, +1 };
|
||||
static const int yoffset[] = { -1, 0, 0, +1, -1, -1, +1, +1 };
|
||||
struct {
|
||||
unsigned short X;
|
||||
unsigned short Y;
|
||||
} *points;
|
||||
int size;
|
||||
int x;
|
||||
int y;
|
||||
int rx;
|
||||
int ry;
|
||||
int mask;
|
||||
int wp;
|
||||
int rp;
|
||||
int ep;
|
||||
int i;
|
||||
int w;
|
||||
unsigned char *m;
|
||||
unsigned char *matrix;
|
||||
int destx;
|
||||
int desty;
|
||||
AiUnit *aiunit;
|
||||
Unit *unit;
|
||||
|
||||
// Find a unit to use. Best choice is a land unit with range 1.
|
||||
// Next best choice is any land unit. Otherwise just use the first.
|
||||
aiunit = force->Units;
|
||||
unit = aiunit->Unit;
|
||||
while (aiunit) {
|
||||
if (aiunit->Unit->Type->UnitType == UnitTypeLand) {
|
||||
unit = aiunit->Unit;
|
||||
if (aiunit->Unit->Type->Missile.Missile->Range == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
aiunit = aiunit->Next;
|
||||
}
|
||||
|
||||
x = unit->X;
|
||||
y = unit->Y;
|
||||
size = TheMap.Width * TheMap.Height / 4;
|
||||
points = malloc(size * sizeof (*points));
|
||||
|
||||
destx = -1;
|
||||
desty = -1;
|
||||
|
||||
matrix = CreateMatrix();
|
||||
w = TheMap.Width + 2;
|
||||
matrix += w + w + 2;
|
||||
|
||||
points[0].X = x;
|
||||
points[0].Y = y;
|
||||
rp = 0;
|
||||
matrix[x + y * w] = 1; // mark start point
|
||||
ep = wp = 1; // start with one point
|
||||
|
||||
mask = UnitMovementMask(unit);
|
||||
|
||||
//
|
||||
// Pop a point from stack, push all neighbors which could be entered.
|
||||
//
|
||||
for (; destx == -1;) {
|
||||
while (rp != ep && destx == -1) {
|
||||
rx = points[rp].X;
|
||||
ry = points[rp].Y;
|
||||
for (i = 0; i < 8; ++i) { // mark all neighbors
|
||||
x = rx + xoffset[i];
|
||||
y = ry + yoffset[i];
|
||||
m = matrix + x + y * w;
|
||||
if (*m) {
|
||||
continue;
|
||||
}
|
||||
//
|
||||
// Check for a wall
|
||||
//
|
||||
if (WallOnMap(x, y)) {
|
||||
DebugLevel0Fn("Wall found %d,%d\n" _C_ x _C_ y);
|
||||
destx = x;
|
||||
desty = y;
|
||||
break;
|
||||
}
|
||||
|
||||
if (CanMoveToMask(x, y, mask)) { // reachable
|
||||
*m = 1;
|
||||
points[wp].X = x; // push the point
|
||||
points[wp].Y = y;
|
||||
if (++wp >= size) { // round about
|
||||
wp = 0;
|
||||
}
|
||||
} else { // unreachable
|
||||
*m = 99;
|
||||
}
|
||||
}
|
||||
if (++rp >= size) { // round about
|
||||
rp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Continue with next frame.
|
||||
//
|
||||
if (rp == wp) { // unreachable, no more points available
|
||||
break;
|
||||
}
|
||||
ep = wp;
|
||||
}
|
||||
free(points);
|
||||
|
||||
if (destx != -1) {
|
||||
force->State = 0;
|
||||
aiunit = force->Units;
|
||||
while (aiunit) {
|
||||
if (aiunit->Unit->Type->CanAttack) {
|
||||
CommandAttack(aiunit->Unit, destx, desty, NULL, FlushCommands);
|
||||
} else {
|
||||
CommandMove(aiunit->Unit, destx, desty, FlushCommands);
|
||||
}
|
||||
aiunit = aiunit->Next;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
** Plan an attack with a force.
|
||||
** We know, that we must use a transporter.
|
||||
**
|
||||
** @param force Pointer on the force.
|
||||
**
|
||||
** @return True if target found, false otherwise.
|
||||
**
|
||||
** @todo Perfect planning.
|
||||
** Only works for water transporter!
|
||||
*/
|
||||
global int AiPlanAttack(AiForce * force)
|
||||
{
|
||||
char *watermatrix;
|
||||
const AiUnit *aiunit;
|
||||
int x;
|
||||
int y;
|
||||
int i;
|
||||
int state;
|
||||
Unit *transporter;
|
||||
|
||||
DebugLevel0Fn("Planning for force #%d of player #%d\n"
|
||||
_C_ force - AiPlayer->Force _C_ AiPlayer->Player->Player);
|
||||
|
||||
watermatrix = CreateMatrix();
|
||||
|
||||
//
|
||||
// Transporter must be already assigned to the force.
|
||||
// NOTE: finding free transportes was too much work for me.
|
||||
//
|
||||
aiunit = force->Units;
|
||||
state = 1;
|
||||
while (aiunit) {
|
||||
if (aiunit->Unit->Type->Transporter) {
|
||||
DebugLevel0Fn("Transporter #%d\n" _C_ UnitNumber(aiunit->Unit));
|
||||
AiMarkWaterTransporter(aiunit->Unit, watermatrix);
|
||||
state = 0;
|
||||
}
|
||||
aiunit = aiunit->Next;
|
||||
}
|
||||
|
||||
//
|
||||
// No transport that belongs to the force.
|
||||
//
|
||||
transporter = NULL;
|
||||
if (state) {
|
||||
for (i = 0; i < AiPlayer->Player->TotalNumUnits; ++i) {
|
||||
Unit *unit;
|
||||
|
||||
unit = AiPlayer->Player->Units[i];
|
||||
if (unit->Type->Transporter && UnitIdle(unit)) {
|
||||
DebugLevel0Fn("Assign any transporter\n");
|
||||
AiMarkWaterTransporter(unit, watermatrix);
|
||||
// FIXME: can be the wrong transporter.
|
||||
transporter = unit;
|
||||
state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state) { // Absolute no transporter
|
||||
DebugLevel0Fn("No transporter available\n");
|
||||
// FIXME: should tell the resource manager we need a transporter!
|
||||
return 0;
|
||||
}
|
||||
//
|
||||
// Find a land unit of the force.
|
||||
// FIXME: if force is split over different places -> broken
|
||||
//
|
||||
aiunit = force->Units;
|
||||
while (aiunit) {
|
||||
if (aiunit->Unit->Type->UnitType == UnitTypeLand) {
|
||||
DebugLevel0Fn("Landunit %d\n" _C_ UnitNumber(aiunit->Unit));
|
||||
break;
|
||||
}
|
||||
aiunit = aiunit->Next;
|
||||
}
|
||||
|
||||
if (!aiunit) {
|
||||
DebugLevel0Fn("No land unit in force\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (AiFindTarget(aiunit->Unit, watermatrix, &x, &y, &state)) {
|
||||
AiUnit *aiunit;
|
||||
|
||||
if (transporter) {
|
||||
aiunit = malloc(sizeof (*aiunit));
|
||||
aiunit->Next = force->Units;
|
||||
force->Units = aiunit;
|
||||
aiunit->Unit = transporter;
|
||||
RefsIncrease(transporter);
|
||||
}
|
||||
|
||||
DebugLevel0Fn("Can attack\n");
|
||||
force->GoalX = x;
|
||||
force->GoalY = y;
|
||||
force->MustTransport = state == 2;
|
||||
|
||||
force->State = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
** Respond to ExplorationRequests
|
||||
*/
|
||||
|
|
|
@ -1528,25 +1528,6 @@ local SCM CclAiWaitForce(SCM value)
|
|||
return SCM_BOOL_T;
|
||||
}
|
||||
|
||||
/**
|
||||
** Attack with force.
|
||||
**
|
||||
** @param value Force number.
|
||||
*/
|
||||
local SCM CclAiAttackWithForce(SCM value)
|
||||
{
|
||||
int force;
|
||||
|
||||
force = gh_scm2int(value);
|
||||
if (force < 0 || force >= AI_MAX_FORCES) {
|
||||
errl("Force out of range", value);
|
||||
}
|
||||
|
||||
AiAttackWithForce(force);
|
||||
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
/**
|
||||
** Attack with force, on the current script hotspot.
|
||||
**
|
||||
|
@ -2110,7 +2091,6 @@ global void AiCclRegister(void)
|
|||
|
||||
gh_new_procedure0_0("ai:idle", CclAiIdle);
|
||||
gh_new_procedure2_0("ai:timed-wait-force", CclAiTimedWaitForce);
|
||||
gh_new_procedure1_0("ai:attack-with-force", CclAiAttackWithForce);
|
||||
gh_new_procedure1_0("ai:hotspot-attack-with-force", CclAiHotSpotAttackWithForce);
|
||||
gh_new_procedure1_0("ai:force-go-home", CclAiForceHome);
|
||||
gh_new_procedure1_0("ai:sleep", CclAiSleep);
|
||||
|
|
Loading…
Add table
Reference in a new issue