Reworked AI system, to make it more generic and flexible.
This commit is contained in:
parent
dfc6cc30fb
commit
a776005a65
|
@ -6,5 +6,6 @@
|
|||
"reloadTime" : 0,
|
||||
"shieldRechargeRate" : 0,
|
||||
"textureName" : "gfx/craft/civilian01.png",
|
||||
"flags" : "EF_CIVILIAN+EF_FLEEING+EF_MISSION_TARGET"
|
||||
"flags" : "EF_MISSION_TARGET+EF_FLEEING",
|
||||
"aiFlags" : "AIF_GOAL_EXTRACTION+AIF_AVOIDS_COMBAT"
|
||||
}
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
"reloadTime" : 0,
|
||||
"shieldRechargeRate" : 60,
|
||||
"textureName" : "gfx/craft/shuttle.png",
|
||||
"flags" : "EF_COLLECTS_ITEMS"
|
||||
"flags" : "EF_COLLECTS_ITEMS",
|
||||
"aiFlags" : "AIF_AVOIDS_COMBAT+AIF_COLLECTS_ITEMS+AIF_UNLIMITED_RANGE"
|
||||
}
|
||||
|
|
|
@ -13,5 +13,6 @@
|
|||
"y" : 0
|
||||
}
|
||||
],
|
||||
"flags" : "EF_HAS_ROPE"
|
||||
"flags" : "EF_HAS_ROPE",
|
||||
"aiFlags" : "AIF_AVOIDS_COMBAT"
|
||||
}
|
||||
|
|
3
makefile
3
makefile
|
@ -1,6 +1,6 @@
|
|||
PROG = tbftss
|
||||
|
||||
VERSION = 0.3
|
||||
VERSION = 0.4
|
||||
REVISION = $(shell date +"%y%m%d")
|
||||
DEBUG = 0
|
||||
|
||||
|
@ -57,6 +57,7 @@ dist:
|
|||
git log --oneline --decorate >$(PROG)-$(VERSION)/CHANGELOG.raw
|
||||
tar czf $(PROG)-$(VERSION).$(REVISION)-src.tar.gz $(PROG)-$(VERSION)
|
||||
mkdir -p dist
|
||||
$(RM) -rf dist
|
||||
mv $(PROG)-$(VERSION).$(REVISION)-src.tar.gz dist
|
||||
$(RM) -rf $(PROG)-$(VERSION)
|
||||
|
||||
|
|
182
src/battle/ai.c
182
src/battle/ai.c
|
@ -36,25 +36,53 @@ static void moveToPlayer(void);
|
|||
static int canAttack(Entity *f);
|
||||
static int selectWeapon(int type);
|
||||
static int nearExtractionPoint(void);
|
||||
static void moveToExtractionPoint(void);
|
||||
static int nearEnemies(void);
|
||||
static int nearItems(void);
|
||||
static void moveToItem(void);
|
||||
static int nearTowableCraft(void);
|
||||
static void moveToTowableCraft(void);
|
||||
static void lookForPlayer(void);
|
||||
static void fleeEnemies(void);
|
||||
static void moveToExtractionPoint(void);
|
||||
static int getActionChance(int type);
|
||||
static void flee(void);
|
||||
static void doFighterAI(void);
|
||||
static void doCivilianAI(void);
|
||||
|
||||
void doAI(void)
|
||||
{
|
||||
if (self->flags & EF_CIVILIAN)
|
||||
if ((self->aiFlags & AIF_GOAL_EXTRACTION) && nearExtractionPoint())
|
||||
{
|
||||
doCivilianAI();
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if ((self->aiFlags & AIF_AVOIDS_COMBAT) && nearEnemies())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((self->aiFlags & AIF_COLLECTS_ITEMS) && nearItems())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((self->aiFlags & AIF_TOWS) && nearTowableCraft())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(self->aiFlags & AIF_AVOIDS_COMBAT))
|
||||
{
|
||||
doFighterAI();
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->aiFlags & AIF_FOLLOWS_PLAYER)
|
||||
{
|
||||
lookForPlayer();
|
||||
return;
|
||||
}
|
||||
|
||||
/* no idea - just stay where you are */
|
||||
applyFighterBrakes();
|
||||
}
|
||||
|
||||
static void doFighterAI(void)
|
||||
|
@ -74,11 +102,11 @@ static void doFighterAI(void)
|
|||
|
||||
if (self->target == NULL)
|
||||
{
|
||||
if (player != NULL && self->side == SIDE_ALLIES)
|
||||
if (self->aiFlags & AIF_FOLLOWS_PLAYER)
|
||||
{
|
||||
moveToPlayer();
|
||||
}
|
||||
else if (!(self->flags & EF_FLEEING))
|
||||
else
|
||||
{
|
||||
applyFighterBrakes();
|
||||
}
|
||||
|
@ -119,17 +147,6 @@ static void doFighterAI(void)
|
|||
self->action = huntAndAttackTarget;
|
||||
self->aiActionTime = FPS;
|
||||
}
|
||||
|
||||
if ((player != NULL && battle.numEnemies <= 2 && self->flags & EF_FLEES) || (self->flags & EF_ALWAYS_FLEES))
|
||||
{
|
||||
self->action = flee;
|
||||
self->aiActionTime = FPS * 3;
|
||||
if (!(self->flags & EF_FLEEING) && (self->flags & EF_MISSION_TARGET) && self->side != SIDE_ALLIES)
|
||||
{
|
||||
addHudMessage(colors.cyan, "Mission target is escaping!");
|
||||
self->flags |= EF_FLEEING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int getActionChance(int type)
|
||||
|
@ -137,19 +154,19 @@ static int getActionChance(int type)
|
|||
switch (type)
|
||||
{
|
||||
case AI_DODGE:
|
||||
return 40 - (self->aggression * 3);
|
||||
return 40 - (self->aiAggression * 3);
|
||||
|
||||
case AI_BOOST:
|
||||
return 50 - (self->aggression * 4);
|
||||
return 50 - (self->aiAggression * 4);
|
||||
|
||||
case AI_SLOW:
|
||||
return 60 - (self->aggression * 5);
|
||||
return 60 - (self->aiAggression * 5);
|
||||
|
||||
case AI_STRAIGHT:
|
||||
return 70 - (self->aggression * 6);
|
||||
return 70 - (self->aiAggression * 6);
|
||||
|
||||
case AI_HUNT:
|
||||
return 80 - (self->aggression * 7);
|
||||
return 80 - (self->aiAggression * 7);
|
||||
}
|
||||
|
||||
return 100;
|
||||
|
@ -193,7 +210,7 @@ static void findTarget(void)
|
|||
Entity *e, **candidates;
|
||||
unsigned int dist, closest;
|
||||
|
||||
dist = closest = (!battle.epic) ? 2000 : MAX_TARGET_RANGE;
|
||||
dist = closest = (!battle.epic || (!(self->aiFlags & AIF_UNLIMITED_RANGE))) ? 2000 : MAX_TARGET_RANGE;
|
||||
|
||||
candidates = getAllEntsWithin(self->x - (self->w / 2) - (dist / 2), self->y - (self->h / 2) - (dist / 2), self->w + dist, self->h + dist, self);
|
||||
|
||||
|
@ -207,7 +224,7 @@ static void findTarget(void)
|
|||
|
||||
if (dist < closest)
|
||||
{
|
||||
if (self->target == NULL || ((self->target->flags & EF_CIVILIAN) == 0) || ((self->target->flags & EF_CIVILIAN) && rand() % 10) == 0)
|
||||
if (self->target == NULL || ((self->target->aiFlags & AIF_AVOIDS_COMBAT) == 0) || ((self->target->aiFlags & AIF_AVOIDS_COMBAT) && rand() % 10) == 0)
|
||||
{
|
||||
self->target = e;
|
||||
closest = dist;
|
||||
|
@ -378,15 +395,6 @@ static void nextAction(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void flee(void)
|
||||
{
|
||||
if (!nearEnemies() && battle.extractionPoint)
|
||||
{
|
||||
self->target = battle.extractionPoint;
|
||||
moveToExtractionPoint();
|
||||
}
|
||||
}
|
||||
|
||||
static int nearEnemies(void)
|
||||
{
|
||||
int i, numEnemies;
|
||||
|
@ -445,8 +453,6 @@ static void fleeEnemies(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* ====== Ally AI ======= */
|
||||
|
||||
static void moveToPlayer(void)
|
||||
{
|
||||
int dist = getDistance(self->x, self->y, player->x, player->y);
|
||||
|
@ -465,22 +471,12 @@ static void moveToPlayer(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* ====== Civilian AI ======= */
|
||||
|
||||
void doCivilianAI(void)
|
||||
{
|
||||
if (!nearExtractionPoint() && !nearEnemies())
|
||||
{
|
||||
lookForPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
static int nearExtractionPoint(void)
|
||||
{
|
||||
int i;
|
||||
Entity *e, **candidates;
|
||||
|
||||
candidates = getAllEntsWithin(self->x - (self->w / 2) - 500, self->y - (self->h / 2) - 500, 1000, 1000, self);
|
||||
candidates = getAllEntsWithin(self->x - (self->w / 2), self->y - (self->h / 2), GRID_CELL_WIDTH, GRID_CELL_HEIGHT, self);
|
||||
|
||||
self->target = NULL;
|
||||
|
||||
|
@ -507,9 +503,97 @@ static void moveToExtractionPoint(void)
|
|||
applyFighterThrust();
|
||||
}
|
||||
|
||||
static int nearItems(void)
|
||||
{
|
||||
int i;
|
||||
long closest, distance;
|
||||
Entity *e, **candidates;
|
||||
|
||||
closest = MAX_TARGET_RANGE;
|
||||
|
||||
candidates = getAllEntsWithin(self->x - (self->w / 2) - (GRID_CELL_WIDTH / 2), self->y - (self->h / 2) - (GRID_CELL_HEIGHT / 2), GRID_CELL_WIDTH, GRID_CELL_HEIGHT, self);
|
||||
|
||||
self->target = NULL;
|
||||
|
||||
for (i = 0, e = candidates[i] ; e != NULL ; e = candidates[++i])
|
||||
{
|
||||
if (e->type == ET_ITEM)
|
||||
{
|
||||
distance = getDistance(self->x, self->y, e->x, e->y);
|
||||
|
||||
if (distance < closest)
|
||||
{
|
||||
self->target = e;
|
||||
closest = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self->target != NULL)
|
||||
{
|
||||
self->action = moveToItem;
|
||||
}
|
||||
|
||||
return self->target != NULL;
|
||||
}
|
||||
|
||||
static void moveToItem(void)
|
||||
{
|
||||
if (self->target->alive == ALIVE_ALIVE)
|
||||
{
|
||||
faceTarget(self->target);
|
||||
applyFighterThrust();
|
||||
return;
|
||||
}
|
||||
|
||||
self->target = NULL;
|
||||
self->action = doAI;
|
||||
}
|
||||
|
||||
static int nearTowableCraft(void)
|
||||
{
|
||||
int i;
|
||||
long closest, distance;
|
||||
Entity *e, **candidates;
|
||||
|
||||
candidates = getAllEntsWithin(self->x - (self->w / 2) - (GRID_CELL_WIDTH / 2), self->y - (self->h / 2) - (GRID_CELL_HEIGHT / 2), GRID_CELL_WIDTH, GRID_CELL_HEIGHT, self);
|
||||
|
||||
self->target = NULL;
|
||||
|
||||
for (i = 0, e = candidates[i] ; e != NULL ; e = candidates[++i])
|
||||
{
|
||||
if (e->type == ET_FIGHTER && (e->flags & EF_DISABLED))
|
||||
{
|
||||
distance = getDistance(self->x, self->y, e->x, e->y);
|
||||
|
||||
if (distance < closest)
|
||||
{
|
||||
self->target = e;
|
||||
closest = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self->target != NULL)
|
||||
{
|
||||
self->action = moveToTowableCraft;
|
||||
}
|
||||
|
||||
return self->target != NULL;
|
||||
}
|
||||
|
||||
static void moveToTowableCraft(void)
|
||||
{
|
||||
faceTarget(self->target);
|
||||
|
||||
applyFighterThrust();
|
||||
}
|
||||
|
||||
static void lookForPlayer(void)
|
||||
{
|
||||
if (player != NULL && getDistance(self->x, self->y, player->x, player->y) < 1000)
|
||||
long range = (self->aiFlags & AIF_UNLIMITED_RANGE) ? MAX_TARGET_RANGE : 1000;
|
||||
|
||||
if (player != NULL && getDistance(self->x, self->y, player->x, player->y) < range)
|
||||
{
|
||||
moveToPlayer();
|
||||
return;
|
||||
|
|
|
@ -51,21 +51,26 @@ Entity *spawnFighter(char *name, int x, int y, int side)
|
|||
switch (side)
|
||||
{
|
||||
case SIDE_ALLIES:
|
||||
f->aggression = 2 + rand() % 3;
|
||||
f->aiAggression = 2 + rand() % 3;
|
||||
f->aiFlags |= AIF_FOLLOWS_PLAYER;
|
||||
if (!(f->aiFlags & AIF_AVOIDS_COMBAT))
|
||||
{
|
||||
f->aiFlags |= AIF_UNLIMITED_RANGE;
|
||||
}
|
||||
break;
|
||||
|
||||
case SIDE_PIRATE:
|
||||
f->aggression = rand() % 3;
|
||||
f->aiAggression = rand() % 3;
|
||||
break;
|
||||
|
||||
case SIDE_PANDORAN:
|
||||
f->aggression = 3 + rand() % 2;
|
||||
f->aiAggression = 3 + rand() % 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp(name, "ATAF") == 0)
|
||||
{
|
||||
f->aggression = 4;
|
||||
f->aiAggression = 4;
|
||||
}
|
||||
|
||||
if (strcmp(name, "Dart") == 0)
|
||||
|
@ -223,7 +228,7 @@ void doFighter(void)
|
|||
addHudMessage(colors.red, "Mission target has escaped.");
|
||||
battle.stats[STAT_ENEMIES_ESCAPED]++;
|
||||
}
|
||||
else if (self->flags & EF_CIVILIAN)
|
||||
else if (strcmp(self->defName, "Civilian") == 0)
|
||||
{
|
||||
battle.stats[STAT_CIVILIANS_RESCUED]++;
|
||||
}
|
||||
|
@ -251,7 +256,7 @@ void doFighter(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (self->flags & EF_CIVILIAN)
|
||||
if (strcmp(self->name, "Civilian") == 0)
|
||||
{
|
||||
battle.stats[STAT_CIVILIANS_KILLED]++;
|
||||
if (!battle.epic)
|
||||
|
@ -494,7 +499,7 @@ void fleeAllEnemies(void)
|
|||
{
|
||||
if (e->type == ET_FIGHTER && e->side != SIDE_ALLIES)
|
||||
{
|
||||
e->flags |= EF_ALWAYS_FLEES;
|
||||
e->aiFlags |= AIF_AVOIDS_COMBAT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -607,6 +612,11 @@ static void loadFighterDef(char *filename)
|
|||
f->flags = flagsToLong(cJSON_GetObjectItem(root, "flags")->valuestring);
|
||||
}
|
||||
|
||||
if (cJSON_GetObjectItem(root, "aiFlags"))
|
||||
{
|
||||
f->aiFlags = flagsToLong(cJSON_GetObjectItem(root, "aiFlags")->valuestring);
|
||||
}
|
||||
|
||||
f->separationRadius = MAX(f->w, f->h);
|
||||
f->separationRadius *= 2;
|
||||
|
||||
|
|
22
src/defs.h
22
src/defs.h
|
@ -67,14 +67,20 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#define EF_DISABLED (2 << 1)
|
||||
#define EF_IMMORTAL (2 << 2)
|
||||
#define EF_MISSION_TARGET (2 << 3)
|
||||
#define EF_FLEES (2 << 4)
|
||||
#define EF_FLEEING (2 << 5)
|
||||
#define EF_NO_MT_BOX (2 << 6)
|
||||
#define EF_CIVILIAN (2 << 7)
|
||||
#define EF_HAS_ROPE (2 << 8)
|
||||
#define EF_ALWAYS_FLEES (2 << 9)
|
||||
#define EF_COLLECTS_ITEMS (2 << 10)
|
||||
#define EF_MUST_DISABLE (2 << 11)
|
||||
#define EF_NO_MT_BOX (2 << 4)
|
||||
#define EF_HAS_ROPE (2 << 5)
|
||||
#define EF_COLLECTS_ITEMS (2 << 6)
|
||||
#define EF_MUST_DISABLE (2 << 7)
|
||||
#define EF_FLEEING (2 << 8)
|
||||
|
||||
#define AIF_NONE 0
|
||||
#define AIF_FOLLOWS_PLAYER (2 << 0)
|
||||
#define AIF_UNLIMITED_RANGE (2 << 1)
|
||||
#define AIF_COLLECTS_ITEMS (2 << 2)
|
||||
#define AIF_TOWS (2 << 3)
|
||||
#define AIF_RETREATS (2 << 4)
|
||||
#define AIF_GOAL_EXTRACTION (2 << 5)
|
||||
#define AIF_AVOIDS_COMBAT (2 << 6)
|
||||
|
||||
/* player abilities */
|
||||
#define BOOST_RECHARGE_TIME (FPS * 7)
|
||||
|
|
|
@ -103,11 +103,12 @@ struct Entity {
|
|||
int systemHit;
|
||||
int thinkTime;
|
||||
int aiActionTime;
|
||||
int aggression;
|
||||
int aiAggression;
|
||||
int separationRadius;
|
||||
Weapon guns[MAX_FIGHTER_GUNS];
|
||||
Weapon missiles;
|
||||
long flags;
|
||||
long aiFlags;
|
||||
SDL_Point targetLocation;
|
||||
Entity *towing;
|
||||
Entity *target;
|
||||
|
|
|
@ -38,12 +38,19 @@ void initLookups(void)
|
|||
addLookup("EF_MUST_DISABLE", EF_MUST_DISABLE);
|
||||
addLookup("EF_IMMORTAL", EF_IMMORTAL);
|
||||
addLookup("EF_MISSION_TARGET", EF_MISSION_TARGET);
|
||||
addLookup("EF_FLEES", EF_FLEES);
|
||||
addLookup("EF_FLEEING", EF_FLEEING);
|
||||
addLookup("EF_CIVILIAN", EF_CIVILIAN);
|
||||
addLookup("EF_HAS_ROPE", EF_HAS_ROPE);
|
||||
addLookup("EF_COLLECTS_ITEMS", EF_COLLECTS_ITEMS);
|
||||
|
||||
addLookup("AIF_NONE", AIF_NONE);
|
||||
addLookup("AIF_FOLLOWS_PLAYER", AIF_FOLLOWS_PLAYER);
|
||||
addLookup("AIF_UNLIMITED_RANGE", AIF_UNLIMITED_RANGE);
|
||||
addLookup("AIF_COLLECTS_ITEMS", AIF_COLLECTS_ITEMS);
|
||||
addLookup("AIF_TOWS", AIF_TOWS);
|
||||
addLookup("AIF_RETREATS", AIF_RETREATS);
|
||||
addLookup("AIF_GOAL_EXTRACTION", AIF_GOAL_EXTRACTION);
|
||||
addLookup("AIF_AVOIDS_COMBAT", AIF_AVOIDS_COMBAT);
|
||||
|
||||
addLookup("TT_DESTROY", TT_DESTROY);
|
||||
addLookup("TT_DISABLE", TT_DISABLE);
|
||||
addLookup("TT_WAYPOINT", TT_WAYPOINT);
|
||||
|
|
Loading…
Reference in New Issue