Removed unused ai:attack-with-force ccl function

This commit is contained in:
pludov 2003-11-20 16:48:12 +00:00
parent 6ceea59b80
commit b7c9fd452a
4 changed files with 3 additions and 895 deletions

View file

@ -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>

View file

@ -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();
}

View file

@ -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
*/

View file

@ -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);