ai: add bomber and improve defense ai

alut-include-path
Maikel de Vries 2017-02-02 21:36:40 +01:00
parent 2807f36319
commit caca643c83
7 changed files with 110 additions and 17 deletions

View File

@ -68,7 +68,7 @@ public func GetAttackWave(int nr)
if (nr == 1) return new DefenseWave.Break { Duration = 120 };
// Attack positions.
var pos_land = {X = LandscapeWidth(), Y = 740, Exact = true};
var pos_land = {X = LandscapeWidth(), Y = 760, Exact = true};
var pos_sky = {X = LandscapeWidth() - 100, Y = 0};
var pos_above = {X = 200, Y = 0};
@ -83,27 +83,32 @@ public func GetAttackWave(int nr)
Enemies = []
};
// Add enemy ground troups: swordsman, archer or spearman.
// Add enemy ground troups: swordsman, archer, spearman, grenadier, bomber.
PushBack(wave.Enemies, new DefenseEnemy.Swordsman {
Amount = BoundBy((nr + 2) / 4, 0, 20),
Amount = BoundBy((nr + 2) / 5, 0, 20),
Energy = BoundBy(20 + nr, 30, 100),
Position = pos_land
});
PushBack(wave.Enemies, new DefenseEnemy.Archer {
Amount = BoundBy((nr + 1) / 4, 0, 20),
Amount = BoundBy((nr + 1) / 5, 0, 20),
Energy = BoundBy(10 + nr, 20, 50),
Position = pos_land
});
PushBack(wave.Enemies, new DefenseEnemy.Spearman {
Amount = BoundBy(nr / 4, 0, 20),
Amount = BoundBy(nr / 5, 0, 20),
Energy = BoundBy(10 + nr, 20, 50),
Position = pos_land
});
PushBack(wave.Enemies, new DefenseEnemy.Grenadier {
Amount = BoundBy((nr - 1) / 4, 0, 20),
Amount = BoundBy((nr - 1) / 5, 0, 20),
Energy = BoundBy(25 + nr, 30, 80),
Position = pos_land
});
PushBack(wave.Enemies, new DefenseEnemy.Bomber {
Amount = BoundBy((nr - 2) / 5, 0, 20),
Energy = BoundBy(10 + nr, 20, 50),
Position = pos_land
});
// Add enemy: boom attack.
PushBack(wave.Enemies, new DefenseEnemy.BoomAttack {
Amount = BoundBy(nr / 2 + 1, 1, 20),

View File

@ -12,19 +12,42 @@
local AltTargetDistance = 400; // Use the scenario given target if normal AI target is further away than this distance.
private func FindTarget(effect fx)
// Alternative target finding for defense scenarios: partially controlled by the scenario script.
public func FindTarget(effect fx)
{
var target = _inherited(fx, ...);
// Focus on defense target if normal target is too far away.
if (!target || ObjectDistance(target, fx.Target) > fx.control.AltTargetDistance)
target = GetRandomAttackTarget(fx.Target);
{
if (fx.is_siege)
target = GetRandomSiegeTarget(fx.Target);
else
target = GetRandomAttackTarget(fx.Target);
}
// If target can't be attacked just take normal target again.
if (!this->HasWeaponForTarget(fx, target))
target = _inherited(fx, ...);
return target;
}
private func FindInventoryWeapon(effect fx)
// Move to a target if idle.
public func ExecuteIdle(effect fx)
{
if (!fx.target)
fx.target = this->FindEmergencyTarget(fx);
if (!Random(5) && fx.target)
{
var tx = fx.target->GetX();
if (Abs(tx - fx.Target->GetX())>30)
{
fx.Target->SetCommand("MoveTo", nil, BoundBy(fx.Target->GetX(), tx - 30, tx + 30), fx.target->GetY());
return true;
}
}
return true;
}
public func FindInventoryWeapon(effect fx)
{
// Alternative behavior when riding the boom attack.
if (fx.vehicle && fx.vehicle->GetID() == DefenseBoomAttack)
@ -37,5 +60,40 @@ private func FindInventoryWeapon(effect fx)
fx.ranged = true;
return true;
}
// Make a bomber out of those who carry the powderkeg.
if (fx.weapon = fx.Target->FindContents(PowderKeg))
{
fx.is_siege = true;
fx.strategy = this.ExecuteBomber;
return true;
}
return _inherited(fx, ...);
}
/*-- Bomber --*/
public func ExecuteBomber(effect fx)
{
// Still carrying the bomb?
if (fx.weapon->Contained() != fx.Target)
{
fx.weapon = nil;
return false;
}
// Are we in range?
if (fx.Target->ObjectDistance(fx.target) < 16 || Distance(fx.Target->GetX(), fx.Target->GetY(), fx.target->GetX(), fx.target->GetY() + fx.target->GetBottom()) < 16)
{
// Suicide!
fx.weapon->Explode(fx.weapon->GetExplosionStrength());
fx.Target->Kill();
}
else
{
// Not in range. Walk there.
if (!fx.Target->GetCommand() || !Random(10))
fx.Target->SetCommand("MoveTo", fx.target);
}
return true;
}

View File

@ -296,6 +296,17 @@ local Rocketeer = new DefaultEnemy
Vehicle = DefenseBoomAttack
};
// An archer riding a boom attack.
local Bomber = new DefaultEnemy
{
Name = "$EnemyBomber$",
Inventory = PowderKeg,
Energy = 50,
Bounty = 10,
Color = 0xff55aaff,
Skin = CSKIN_Default
};
// Commander of the airship.
local AirshipPilot = new DefaultEnemy
{

View File

@ -6,5 +6,6 @@ EnemyBoomAttack=Rakete
EnemyRapidBoomAttack=Rasche Rakete
EnemyBallooner=Fallschirmspringer
EnemyRocketeer=Raketenreiter
EnemyBomber=Bomber
EnemyAirshipPilot=Pilot
EnemyAirshipCrew=Luftmatrose

View File

@ -6,5 +6,6 @@ EnemyBoomAttack=Rocket
EnemyRapidBoomAttack=Rapid Rocket
EnemyBallooner=Parachutist
EnemyRocketeer=Rocketeer
EnemyBomber=Bomber
EnemyAirshipPilot=Pilot
EnemyAirshipCrew=Airsailor

View File

@ -4,11 +4,28 @@ global func GetRandomAttackTarget(object attacker)
{
// First let the scenario tell what to do by a gamecall.
var target = GameCall("GiveRandomAttackTarget", attacker);
if (!target)
{
// Attack structures owned by the enemy of the attacker.
var controller = attacker->GetController();
var target = FindObject(Find_Category(C4D_Structure), Find_Hostile(controller), Sort_Random());
}
return target;
if (target)
return target;
// Attack structures owned by the enemy of the attacker.
var controller = attacker->GetController();
for (var target in FindObjects(Find_Category(C4D_Structure), Find_Hostile(controller), Sort_Distance()))
if (target && PathFree(attacker->GetX(), attacker->GetY(), target->GetX(), target->GetY()))
return target;
// Otherwise return random enemy structure.
return FindObject(Find_Category(C4D_Structure), Find_Hostile(controller), Sort_Random());
}
global func GetRandomSiegeTarget(object attacker)
{
// First let the scenario tell what to do by a gamecall.
var target = GameCall("GiveRandomSiegeTarget", attacker);
if (target)
return target;
// Attack structures owned by the enemy of the attacker.
var controller = attacker->GetController();
for (var target in FindObjects(Find_Category(C4D_Structure), Find_Hostile(controller), Sort_Distance()))
if (target && PathFree(attacker->GetX(), attacker->GetY(), target->GetX(), target->GetY()))
return target;
// Otherwise return random enemy structure.
return FindObject(Find_Category(C4D_Structure), Find_Hostile(controller), Sort_Random());
}

View File

@ -390,7 +390,7 @@ public func Execute(effect fx, int time)
this->ExecuteAppearance(fx);
// Attack it!
if (!this->IsWeaponForTarget(fx))
this->LogAI(fx, Format("weapon of type %i is not fit to attack %v.", fx.weapon->GetID(), fx.target));
this->LogAI(fx, Format("weapon of type %i is not fit to attack %v (type: %i).", fx.weapon->GetID(), fx.target, fx.target->GetID()));
return this->Call(fx.strategy, fx);
}