forked from Mirrors/openclonk
Add catapult to enemy spawn
Also fix some catapult behavior (such as pushing it along waypoints)alut-include-path
parent
aa5f5cd654
commit
1ff3b17dad
|
@ -56,6 +56,7 @@ local SingleWeaponAttackMode = {
|
|||
{
|
||||
weapon->~SetStackCount(1); // Ensure departure is called on every object
|
||||
weapon.Departure = AI.Departure_WeaponRespawn;
|
||||
fx.has_ammo_respawn = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -92,6 +92,9 @@ public func ExecuteJump(effect fx)
|
|||
// Follow attack path or return to the AI's home if not yet there.
|
||||
public func ExecuteIdle(effect fx)
|
||||
{
|
||||
// Persist commands because constant command resets may hinder execution
|
||||
if (fx.Target->GetCommand() && Random(4)) return true;
|
||||
// Follow attack path
|
||||
if (fx.attack_path)
|
||||
{
|
||||
var next_pt = fx.attack_path[0];
|
||||
|
@ -111,30 +114,38 @@ public func ExecuteIdle(effect fx)
|
|||
fx.home_y = next_pt.Y;
|
||||
fx.home_dir = Random(2);
|
||||
}
|
||||
if (!Inside(fx.Target->GetX() - fx.home_x, -5, 5) || !Inside(fx.Target->GetY() - fx.home_y, -15, 15))
|
||||
// Check if we need to move/push to a target
|
||||
if (fx.vehicle)
|
||||
{
|
||||
return fx.Target->SetCommand("MoveTo", nil, fx.home_x, fx.home_y);
|
||||
if (!Inside(fx.vehicle->GetX() - fx.home_x, -15, 15) || !Inside(fx.vehicle->GetY() - fx.home_y, -20, 20))
|
||||
{
|
||||
return fx.Target->SetCommand("PushTo", fx.vehicle, fx.home_x, fx.home_y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Next section on path or done?
|
||||
if (fx.attack_path)
|
||||
if (!Inside(fx.Target->GetX() - fx.home_x, -5, 5) || !Inside(fx.Target->GetY() - fx.home_y, -15, 15))
|
||||
{
|
||||
if (GetLength(fx.attack_path) > 1)
|
||||
{
|
||||
fx.attack_path = fx.attack_path[1:];
|
||||
// Wait one cycle. After that, ExecuteIdle will continue the path.
|
||||
}
|
||||
else
|
||||
{
|
||||
fx.attack_path = nil;
|
||||
}
|
||||
return fx.Target->SetCommand("MoveTo", nil, fx.home_x, fx.home_y);
|
||||
}
|
||||
// Movement done (for now)
|
||||
fx.Target->SetCommand("None");
|
||||
fx.Target->SetComDir(COMD_Stop);
|
||||
fx.Target->SetDir(fx.home_dir);
|
||||
}
|
||||
// Next section on path or done?
|
||||
if (fx.attack_path)
|
||||
{
|
||||
if (GetLength(fx.attack_path) > 1)
|
||||
{
|
||||
fx.attack_path = fx.attack_path[1:];
|
||||
// Wait one cycle. After that, ExecuteIdle will continue the path.
|
||||
}
|
||||
else
|
||||
{
|
||||
fx.attack_path = nil;
|
||||
}
|
||||
}
|
||||
// Movement done (for now)
|
||||
fx.Target->SetCommand("None");
|
||||
fx.Target->SetComDir(COMD_Stop);
|
||||
fx.Target->SetDir(fx.home_dir);
|
||||
// Nothing to do.
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -218,6 +218,18 @@ public func SetAttackPath(object clonk, array new_attack_path)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Set controlled vehicle
|
||||
public func SetVehicle(object clonk, object new_vehicle)
|
||||
{
|
||||
if (GetType(this) != C4V_Def)
|
||||
Log("WARNING: SetVehicle(%v, %v) not called from definition context but from %v", clonk, new_vehicle, this);
|
||||
var fx_ai = GetAI(clonk);
|
||||
if (!fx_ai)
|
||||
return false;
|
||||
fx_ai.vehicle = new_vehicle;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*-- AI Effect --*/
|
||||
|
||||
|
@ -508,22 +520,7 @@ public func ExecuteArm(effect fx)
|
|||
{
|
||||
// Find shield.
|
||||
fx.shield = fx.Target->FindContents(Shield);
|
||||
// Find a weapon. Depends on attack mode
|
||||
if (Call(fx.attack_mode.FindWeapon, fx))
|
||||
{
|
||||
// Select unless it's e.g. a vehicle or a spell
|
||||
SelectItem(fx, fx.weapon);
|
||||
return true;
|
||||
}
|
||||
// No weapon.
|
||||
return false;
|
||||
}
|
||||
|
||||
public func FindInventoryWeapon(effect fx)
|
||||
{
|
||||
fx.ammo_check = nil;
|
||||
fx.ranged = false;
|
||||
// Find weapon in inventory, mark it as equipped and set according strategy, etc.
|
||||
// Vehicle control overrides all other weapons
|
||||
if (fx.weapon = fx.vehicle)
|
||||
{
|
||||
if (this->CheckVehicleAmmo(fx, fx.weapon))
|
||||
|
@ -537,6 +534,22 @@ public func FindInventoryWeapon(effect fx)
|
|||
else
|
||||
fx.weapon = nil;
|
||||
}
|
||||
// Find a weapon. Depends on attack mode
|
||||
if (Call(fx.attack_mode.FindWeapon, fx))
|
||||
{
|
||||
// Select unless it's e.g. a vehicle or a spell
|
||||
SelectItem(fx, fx.weapon);
|
||||
return true;
|
||||
}
|
||||
// No weapon.
|
||||
return false;
|
||||
}
|
||||
|
||||
public func FindInventoryWeapon(effect fx)
|
||||
{
|
||||
// Find weapon in inventory, mark it as equipped and set according strategy, etc.
|
||||
fx.ammo_check = nil;
|
||||
fx.ranged = false;
|
||||
if (FindInventoryWeaponGrenadeLauncher(fx)) return true;
|
||||
if (FindInventoryWeaponBlunderbuss(fx)) return true;
|
||||
if (FindInventoryWeaponBow(fx)) return true;
|
||||
|
|
|
@ -103,15 +103,20 @@ private func ExecuteCatapult(effect fx)
|
|||
// Can shoot now?
|
||||
if (fx.time >= fx.aim_time + fx.aim_wait && PathFree(x, y - 20, x + dx, y + dy - 20))
|
||||
{
|
||||
fx.aim_weapon->~DoFire(fx.Target, power, 0);
|
||||
fx.aim_weapon = nil;
|
||||
// Need to wait for ammo?
|
||||
if (fx.vehicle->ContentsCount() || fx.Target->ContentsCount())
|
||||
{
|
||||
fx.aim_weapon->~DoFire(fx.Target, power, 0);
|
||||
fx.aim_weapon = nil;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private func CheckCatapultAmmo(effect fx, object vehicle)
|
||||
{
|
||||
return vehicle->ContentsCount() > 0;
|
||||
// Must have ammo in the catapult or in the clonk (or be respawning ammo)
|
||||
return vehicle->ContentsCount() > 0 || fx.Target->ContentsCount() > 0 || fx.has_ammo_respawn;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -539,18 +539,18 @@ public func GetAIClonkEditorProps()
|
|||
return this.AIClonkEditorProps;
|
||||
}
|
||||
|
||||
public func GetAIClonkDefaultPropValues()
|
||||
public func GetAIClonkDefaultPropValues(string attack_mode)
|
||||
{
|
||||
// Default settings for AI enemy clonks
|
||||
return {
|
||||
AttackMode = { Identifier="Sword" },
|
||||
AttackMode = { Identifier=attack_mode ?? "Sword" },
|
||||
Color = 0xff0000,
|
||||
Bounty = 0,
|
||||
ScaleX = 100,
|
||||
ScaleY = 100,
|
||||
Speed = 100,
|
||||
Energy = 50,
|
||||
Backpack = true,
|
||||
Backpack = false,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -314,13 +314,46 @@ public func CatapultDismount(object clonk)
|
|||
return true;
|
||||
}
|
||||
|
||||
/*-- Properties --*/
|
||||
|
||||
/* Register enemy spawn with catapult */
|
||||
|
||||
func Definition(proplist def)
|
||||
{
|
||||
def.PictureTransformation = Trans_Mul(Trans_Translate(-1000, -4000, 0), Trans_Rotate(-20, 1, 0, 0), Trans_Rotate(35, 0, 1, 0));
|
||||
EnemySpawn->AddEnemyDef("Catapult",
|
||||
{ SpawnType=Catapult,
|
||||
SpawnFunction=def.SpawnCatapult,
|
||||
OffsetAttackPathByPos=false,
|
||||
GetInfoString=def.GetSpawnInfoString },
|
||||
EnemySpawn->GetAIClonkDefaultPropValues("Firestone"),
|
||||
EnemySpawn->GetAIClonkEditorProps());
|
||||
}
|
||||
|
||||
private func SpawnCatapult(array pos, proplist clonk_data, proplist enemy_def, array clonk_attack_path, object spawner)
|
||||
{
|
||||
// First spawn the catapult
|
||||
var catapult = CreateObjectAbove(Catapult, pos[0], pos[1], g_enemyspawn_player);
|
||||
catapult->Unstick(10);
|
||||
if (!catapult) return;
|
||||
// Next let a clonk steer the catapult
|
||||
var clonk = EnemySpawn->SpawnClonk(pos, clonk_data, enemy_def, clonk_attack_path, spawner);
|
||||
if (!clonk) return;
|
||||
clonk->SetAction("Push", catapult);
|
||||
// Set attack mode
|
||||
AI->SetVehicle(clonk, catapult);
|
||||
// Only the clonk is an actual enemy
|
||||
return clonk;
|
||||
}
|
||||
|
||||
private func GetSpawnInfoString(proplist enemy_data)
|
||||
{
|
||||
// Prepend balloon to clonk info string
|
||||
return Format("{{Catapult}}%s", EnemySpawn->GetAIClonkInfoString(enemy_data));
|
||||
}
|
||||
|
||||
|
||||
/* Properties */
|
||||
|
||||
local Name = "$Name$";
|
||||
local Description = "$Description$";
|
||||
local Touchable = 1;
|
||||
|
|
Loading…
Reference in New Issue