Move bomber and club attackers from FightForGidl to default AI as attack modes

alut-include-path
Sven Eberhardt 2017-02-21 20:09:15 -05:00
parent f754210a99
commit 649d7bed0a
5 changed files with 92 additions and 68 deletions

View File

@ -29,23 +29,10 @@ public func FindTarget(effect fx)
return target;
}
private func FindInventoryWeapon(effect fx)
private func CheckVehicleAmmo(effect fx, object catapult)
{
// Extra weapons
if (fx.weapon = fx.Target->FindContents(PowderKeg))
{
fx.strategy = this.ExecuteBomber;
return true;
}
if (fx.weapon = fx.Target->FindContents(Club))
{
fx.strategy = this.ExecuteClub;
return true;
}
if (inherited(fx, ...))
return true;
// no weapon :(
return false;
// Ammo is auto-refilled
return true;
}
private func ExecuteBomber(effect fx)
@ -72,55 +59,6 @@ private func ExecuteBomber(effect fx)
return true;
}
private func ExecuteClub(effect fx)
{
// Still carrying the melee weapon?
if (fx.weapon->Contained() != fx.Target)
{
fx.weapon = nil;
return false;
}
// Are we in range?
var x=fx.Target->GetX(), y=fx.Target->GetY(), tx=fx.target->GetX(), ty=fx.target->GetY();
var dx = tx-x, dy = ty-y;
if (Abs(dx) <= 10 && PathFree(x,y,tx,ty))
{
if (Abs(dy) >= 15)
{
// Clonk is above or below us - wait
if (dx<-5) fx.Target->SetComDir(COMD_Left); else if (dx>5) fx.Target->SetComDir(COMD_Right); else fx.Target->SetComDir(COMD_None);
return true;
}
if (!this->CheckHandsAction(fx)) return true;
// Stop here
fx.Target->SetCommand("None"); fx.Target->SetComDir(COMD_None);
// cooldown?
if (!fx.weapon->CanStrikeWithWeapon(fx.Target))
{
//Message("MeleeWAIT %s @ %s!!!", fx.weapon->GetName(), fx.target->GetName());
// While waiting for the cooldown, we try to evade...
ExecuteEvade(fx,dx,dy);
return true;
}
// OK, attack! Prefer upwards strike
dy -= 16;
fx.weapon->ControlUseStart(fx.Target, dx,dy);
fx.weapon->ControlUseHolding(fx.Target, dx,dy);
fx.weapon->ControlUseStop(fx.Target, dx,dy);
return true;
}
// Not in range. Walk there.
if (!fx.Target->GetCommand() || !Random(10)) fx.Target->SetCommand("MoveTo", fx.target);
//Message("Melee %s @ %s!!!", fx.weapon->GetName(), fx.target->GetName());
return true;
}
private func CheckVehicleAmmo(effect fx, object catapult)
{
// Ammo is auto-refilled
return true;
}
func Execute(effect fx, int time)
{
// Execution: No defensive AI. All enemies move towards target

View File

@ -32,7 +32,12 @@ local AttackModes = {}; // empty pre-init to force proplist ownership in base AI
local SingleWeaponAttackMode = {
Construction = func(effect fx)
{
// Contents hack: Carry heavy collection while "Walk" plays an annoying animation. Skip that by having a jump animation
// (the jump animation skipping pickup is weird anyway, but it works for now)
var is_carry_heavy_workaround = fx.attack_mode.Weapon->~IsCarryHeavy() && !fx.Target->Contained() && fx.Target->GetAction() == "Walk";
if (is_carry_heavy_workaround) fx.Target->SetAction("Jump");
var weapon = fx.Target->CreateContents(fx.attack_mode.Weapon);
if (is_carry_heavy_workaround) fx.Target->SetAction("Walk");
if (weapon)
{
if (fx.attack_mode.Ammo)
@ -130,8 +135,9 @@ private func DefinitionAttackModes(proplist def)
// Register presets for all the default weapons usable by the AI
def->RegisterAttackMode("Default", { Name = "$Default$", EditorHelp = "$DefaultHelp$", FindWeapon = AI.FindInventoryWeapon });
def->RegisterAttackMode("Sword", new SingleWeaponAttackMode { Weapon = Sword, Strategy = this.ExecuteMelee });
def->RegisterAttackMode("Club", new SingleWeaponAttackMode { Weapon = Club, Strategy = this.ExecuteMelee });
def->RegisterAttackMode("Axe", new SingleWeaponAttackMode { Weapon = Axe, Strategy = this.ExecuteMelee });
def->RegisterAttackMode("Club", new SingleWeaponAttackMode { Weapon = Club, Strategy = this.ExecuteClub });
def->RegisterAttackMode("PowderKeg", new SingleWeaponAttackMode { Weapon = PowderKeg, Strategy = this.ExecuteBomber });
def->RegisterAttackMode("BowArrow", new SingleWeaponAttackMode { Weapon = Bow, Ammo = Arrow, FindWeapon = this.FindInventoryWeaponBow });
def->RegisterAttackMode("BowFireArrow", new SingleWeaponAttackMode { Weapon = Bow, Ammo = FireArrow, FindWeapon = this.FindInventoryWeaponBow });
def->RegisterAttackMode("BowBombArrow", new SingleWeaponAttackMode { Weapon = Bow, Ammo = BombArrow, FindWeapon = this.FindInventoryWeaponBow });

View File

@ -68,3 +68,72 @@ private func ExecuteMelee(effect fx)
fx.Target->SetCommand("MoveTo", fx.target);
return true;
}
private func ExecuteClub(effect fx)
{
// Still carrying the melee weapon?
if (fx.weapon->Contained() != fx.Target)
{
fx.weapon = nil;
return false;
}
// Are we in range?
var x=fx.Target->GetX(), y=fx.Target->GetY(), tx=fx.target->GetX(), ty=fx.target->GetY();
var dx = tx-x, dy = ty-y;
if (Abs(dx) <= 10 && PathFree(x,y,tx,ty))
{
if (Abs(dy) >= 15)
{
// Clonk is above or below us - wait
if (dx<-5) fx.Target->SetComDir(COMD_Left); else if (dx>5) fx.Target->SetComDir(COMD_Right); else fx.Target->SetComDir(COMD_None);
return true;
}
if (!this->CheckHandsAction(fx)) return true;
// Stop here
fx.Target->SetCommand("None"); fx.Target->SetComDir(COMD_None);
// cooldown?
if (!fx.weapon->CanStrikeWithWeapon(fx.Target))
{
//Message("MeleeWAIT %s @ %s!!!", fx.weapon->GetName(), fx.target->GetName());
// While waiting for the cooldown, we try to evade...
this->ExecuteEvade(fx,dx,dy);
return true;
}
// OK, attack! Prefer upwards strike
dy -= 16;
fx.weapon->ControlUseStart(fx.Target, dx,dy);
fx.weapon->ControlUseHolding(fx.Target, dx,dy);
fx.weapon->ControlUseStop(fx.Target, dx,dy);
return true;
}
// Not in range. Walk there.
if (!fx.Target->GetCommand() || !Random(10)) fx.Target->SetCommand("MoveTo", fx.target);
//Message("Melee %s @ %s!!!", fx.weapon->GetName(), fx.target->GetName());
return true;
}
private 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) < 20 && !fx.weapon->OnFire())
{
// make sure it kills the AI clonk, because it would be useless after the explosion
fx.Target->DoEnergy(5 - fx.Target->GetEnergy());
// Boom!
fx.weapon.TimeToExplode = 10;
fx.weapon->Incinerate(100, fx.Target->GetController());
}
else
{
// Not in range. Walk there.
if (!fx.Target->GetCommand() || !Random(10))
fx.Target->SetCommand("MoveTo", fx.target);
}
return true;
}

View File

@ -549,11 +549,21 @@ public func FindInventoryWeapon(effect fx)
return true;
}
// Melee weapons.
if ((fx.weapon = fx.Target->FindContents(Sword)) || (fx.weapon = fx.Target->FindContents(Club)) || (fx.weapon = fx.Target->FindContents(Axe)))
if ((fx.weapon = fx.Target->FindContents(Sword)) || (fx.weapon = fx.Target->FindContents(Axe))) // Sword attacks aren't 100% correct for Axe, but work well enough
{
fx.strategy = this.ExecuteMelee;
return true;
}
if ((fx.weapon = fx.Target->FindContents(PowderKeg)))
{
fx.strategy = this.ExecuteBomber;
return true;
}
if ((fx.weapon = fx.Target->FindContents(Club)))
{
fx.strategy = this.ExecuteClub;
return true;
}
// No weapon.
return false;
}

View File

@ -8,6 +8,7 @@
#include Library_CarryHeavy
local count;
local TimeToExplode = 90; // Number of frames to pass until keg explodes
public func GetCarryTransform(clonk)
{
@ -101,7 +102,7 @@ public func Incineration(int caused_by)
public func FxFuseTimer(object target, effect, int timer)
{
CreateParticle("Fire", 0, 0, PV_Random(-10, 10), PV_Random(-20, 10), PV_Random(10, 40), Particles_Glimmer(), 6);
if (timer > 90)
if (timer > TimeToExplode)
Explode(GetExplosionStrength());
}