forked from Mirrors/openclonk
Add attack path to AI and EnemySpawn
parent
9abb0bc200
commit
65db84d07e
|
@ -59,6 +59,11 @@ local SingleWeaponAttackMode = {
|
|||
{
|
||||
if (!(fx.weapon = fx.Target->FindContents(fx.attack_mode.Weapon))) return false;
|
||||
fx.strategy = fx.attack_mode.Strategy;
|
||||
if (fx.weapon->~HasExplosionOnImpact())
|
||||
{
|
||||
fx.can_attack_structures = true;
|
||||
fx.can_attack_structures_after_weapon_respawn = fx.attack_mode.Respawn; // allow structure attack even during respawn time
|
||||
}
|
||||
return true;
|
||||
},
|
||||
GetName = func()
|
||||
|
|
|
@ -89,15 +89,48 @@ public func ExecuteJump(effect fx)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Return to the AI's home if not yet there.
|
||||
// Follow attack path or return to the AI's home if not yet there.
|
||||
public func ExecuteIdle(effect fx)
|
||||
{
|
||||
if (fx.attack_path)
|
||||
{
|
||||
var next_pt = fx.attack_path[0];
|
||||
// Check for structure to kill on path. Only if the structure is alive or of the clonk can attack structures with the current weapon.
|
||||
var alive_check;
|
||||
if (!fx.can_attack_structures && !fx.can_attack_structures_after_weapon_respawn)
|
||||
{
|
||||
alive_check = Find_OCF(OCF_Alive);
|
||||
}
|
||||
if ((fx.target = FindObject(Find_AtPoint(next_pt.X, next_pt.Y), Find_Func("IsStructure"), alive_check)))
|
||||
{
|
||||
// Do not advance on path unless target(s) destroyed.
|
||||
return true;
|
||||
}
|
||||
// Follow path
|
||||
fx.home_x = next_pt.X;
|
||||
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))
|
||||
{
|
||||
return fx.Target->SetCommand("MoveTo", nil, fx.home_x, fx.home_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
|
|
|
@ -206,6 +206,18 @@ public func SetEncounterCB(object clonk, string cb_fn)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Set attack path
|
||||
public func SetAttackPath(object clonk, array new_attack_path)
|
||||
{
|
||||
if (GetType(this) != C4V_Def)
|
||||
Log("WARNING: SetAttackPath(%v, %v) not called from definition context but from %v", clonk, new_attack_path, this);
|
||||
var fx_ai = GetAI(clonk);
|
||||
if (!fx_ai)
|
||||
return false;
|
||||
fx_ai.attack_path = new_attack_path;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*-- AI Effect --*/
|
||||
|
||||
|
@ -287,16 +299,27 @@ local FxAI = new Effect
|
|||
},
|
||||
SetAttackMode = func(proplist attack_mode)
|
||||
{
|
||||
// Called by editor delegate when attack mdoe is changed.
|
||||
// Called by editor delegate when attack mode is changed.
|
||||
// For now, attack mode parameter delegates are not supported. Just set by name.
|
||||
return this.control->SetAttackMode(this.Target, attack_mode.Identifier);
|
||||
},
|
||||
SetAttackPath = func(array attack_path)
|
||||
{
|
||||
// Called by editor delegate when attack path is changed.
|
||||
return this.control->SetAttackPath(this.Target, attack_path);
|
||||
},
|
||||
EditorProps = {
|
||||
guard_range = { Name = "$GuardRange$", Type = "rect", Storage = "proplist", Color = 0xff00, Relative = false },
|
||||
ignore_allies = { Name = "$IgnoreAllies$", Type = "bool" },
|
||||
max_aggro_distance = { Name = "$MaxAggroDistance$", Type = "circle", Color = 0x808080 },
|
||||
active = { Name = "$Active$", EditorHelp = "$ActiveHelp$", Type = "bool", Priority = 50, AsyncGet = "GetActive", Set = "SetActive" },
|
||||
auto_search_target = { Name = "$AutoSearchTarget$", EditorHelp = "$AutoSearchTargetHelp$", Type = "bool" }
|
||||
auto_search_target = { Name = "$AutoSearchTarget$", EditorHelp = "$AutoSearchTargetHelp$", Type = "bool" },
|
||||
attack_path = { Name = "$AttackPath$", EditorHelp = "$AttackPathHelp$", Type = "enum", Set = "SetAttackPath", Options = [
|
||||
{ Name="$None$" },
|
||||
{ Name="$AttackPath$", Type=C4V_Array, Value = [{X = 0, Y = 0}], Delegate =
|
||||
{ Name="$AttackPath$", EditorHelp="$AttackPathHelp$", Type="polyline", StartFromObject=true, DrawArrows=true, Color=0xdf0000, Relative=false }
|
||||
}
|
||||
] }
|
||||
},
|
||||
// Save this effect and the AI for scenarios.
|
||||
SaveScen = func(proplist props)
|
||||
|
@ -308,6 +331,8 @@ local FxAI = new Effect
|
|||
props->AddCall("AI", this.control, "SetActive", this.Target, false);
|
||||
if (this.attack_mode.Identifier != "Default")
|
||||
props->AddCall("AI", this.control, "SetAttackMode", this.Target, Format("%v", this.attack_mode.Identifier));
|
||||
if (this.attack_path)
|
||||
props->AddCall("AI", this.control, "SetAttackPath", this.Target, this.attack_path);
|
||||
if (this.home_x != this.Target->GetX() || this.home_y != this.Target->GetY() || this.home_dir != this.Target->GetDir())
|
||||
props->AddCall("AI", this.control, "SetHome", this.Target, this.home_x, this.home_y, GetConstantNameByValueSafe(this.home_dir, "DIR_"));
|
||||
props->AddCall("AI", this.control, "SetGuardRange", this.Target, this.guard_range.x, this.guard_range.y, this.guard_range.wdt, this.guard_range.hgt);
|
||||
|
@ -344,6 +369,7 @@ public func Execute(effect fx, int time)
|
|||
// Find something to fight with.
|
||||
if (!fx.weapon)
|
||||
{
|
||||
fx.can_attack_structures = false;
|
||||
this->CancelAiming(fx);
|
||||
if (!this->ExecuteArm(fx))
|
||||
return this->ExecuteIdle(fx);
|
||||
|
@ -518,6 +544,7 @@ public func FindInventoryWeapon(effect fx)
|
|||
// Throwing weapons.
|
||||
if ((fx.weapon = fx.Target->FindContents(Firestone)) || (fx.weapon = fx.Target->FindContents(Rock)) || (fx.weapon = fx.Target->FindContents(Lantern)))
|
||||
{
|
||||
fx.can_attack_structures = fx.weapon->~HasExplosionOnImpact();
|
||||
fx.strategy = this.ExecuteThrow;
|
||||
return true;
|
||||
}
|
||||
|
@ -542,6 +569,7 @@ private func FindInventoryWeaponGrenadeLauncher(effect fx)
|
|||
fx.aim_wait = 85;
|
||||
fx.ammo_check = this.HasBombs;
|
||||
fx.ranged = true;
|
||||
fx.can_attack_structures = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -579,6 +607,8 @@ private func FindInventoryWeaponBow(effect fx)
|
|||
fx.aim_wait = 0;
|
||||
fx.ammo_check = this.HasArrows;
|
||||
fx.ranged = true;
|
||||
var arrow = fx.weapon->Contents(0) ?? FindObject(Find_Container(fx.Target), Find_Func("IsArrow"));
|
||||
fx.can_attack_structures = arrow && arrow->~IsExplosive();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -22,3 +22,6 @@ NewHomeDirHelp=In welche Richtung der KI-Clonk schauen soll, wenn kein Gegner in
|
|||
Unchanged=Ungeändert
|
||||
Left=Links
|
||||
Right=Rechts
|
||||
AttackPath=Angriffspfad
|
||||
AttackPathHelp=Pfad, entlang dessen sich der KI-Gegner berwegt. Befindet sich ein Gebaeude auf einem Eckpunkt des Pfades, greift der Clonk das Gebaeude an.
|
||||
None=Keiner
|
||||
|
|
|
@ -22,3 +22,6 @@ NewHomeDirHelp=In which direction the AI clonk will look if no enemy is nearby.
|
|||
Unchanged=Unchanged
|
||||
Left=Left
|
||||
Right=Right
|
||||
AttackPath=Attack path
|
||||
AttackPathHelp=Path along which the AI clonk moves. If a vertex of the attack path is placed on a structure (e.g. a gate), the clonk will attack the structure.
|
||||
None=None
|
||||
|
|
|
@ -48,6 +48,17 @@ public func SetAutoActivate(bool new_auto_activate) { auto_activate = new_auto_a
|
|||
|
||||
public func Initialize()
|
||||
{
|
||||
// Default attack path
|
||||
var tx=0, ty=0;
|
||||
if (GetY() < 100 && LandscapeHeight() > 400)
|
||||
{
|
||||
ty = 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetX() < LandscapeWidth()/2) tx = Min(100, LandscapeWidth()-GetX()-8); else tx = Max(-100, -GetX()+8);
|
||||
}
|
||||
attack_path = [{X=tx, Y=ty}];
|
||||
spawned_enemies = [];
|
||||
// Make sure there's an enemy script player
|
||||
if (!GetType(g_enemyspawn_player))
|
||||
|
@ -325,14 +336,28 @@ public func SpawnClonk(array pos, proplist clonk_data, proplist enemy_def, array
|
|||
// AI
|
||||
AI->AddAI(clonk);
|
||||
AI->SetMaxAggroDistance(clonk, Max(LandscapeWidth(), LandscapeHeight()));
|
||||
var last_pos = clonk_attack_path[-1];
|
||||
AI->SetHome(clonk, last_pos.X, last_pos.Y, Random(2));
|
||||
var guard_range = clonk_data.GuardRange ?? { x=last_pos.X-300, y=last_pos.Y-150, wdt=600, hgt=300 };
|
||||
var guard_range = clonk_data.GuardRange;
|
||||
if (!guard_range)
|
||||
{
|
||||
// Automatic guard range around attack path
|
||||
var guard_min_x = spawner->GetX();
|
||||
var guard_min_y = spawner->GetY();
|
||||
var guard_max_x = guard_min_x, guard_max_y = guard_min_y;
|
||||
for (var attack_pos in clonk_attack_path)
|
||||
{
|
||||
guard_min_x = Min(guard_min_x, attack_pos.X);
|
||||
guard_min_y = Min(guard_min_y, attack_pos.Y);
|
||||
guard_max_x = Max(guard_max_x, attack_pos.X);
|
||||
guard_max_y = Max(guard_max_y, attack_pos.Y);
|
||||
}
|
||||
guard_range = { x=guard_min_x-300, y=guard_min_y-150, wdt=guard_max_x-guard_min_x+600, hgt=guard_max_y-guard_min_y+300 };
|
||||
}
|
||||
AI->SetGuardRange(clonk, guard_range.x,guard_range.y,guard_range.wdt,guard_range.hgt);
|
||||
if (clonk_data.AttackMode)
|
||||
{
|
||||
AI->SetAttackMode(clonk, clonk_data.AttackMode.Identifier);
|
||||
}
|
||||
AI->SetAttackPath(clonk, clonk_attack_path);
|
||||
// Return clonk to be added to spawned enemy list
|
||||
return clonk;
|
||||
}
|
||||
|
@ -401,7 +426,7 @@ public func Definition(def)
|
|||
] };
|
||||
def.EditorProps.spawn_delay = { Name="$SpawnDelay$", EditorHelp="$SpawnDelayHelp$", Type="int", Min=0, Set="SetSpawnDelay", Save="SpawnDelay" };
|
||||
def.EditorProps.spawn_interval = { Name="$SpawnInterval$", EditorHelp="$SpawnIntervalHelp$", Type="int", Min=0, Set="SetSpawnInterval", Save="SpawnInterval" };
|
||||
//def.EditorProps.attack_path
|
||||
def.EditorProps.attack_path = { Name="$AttackPath$", EditorHelp="$AttackPathHelp$", Type="polyline", StartFromObject=true, DrawArrows=true, Color=0xdf0000, Relative=true, Save="AttackPath" }; // always saved
|
||||
def.EditorProps.auto_activate = { Name="$AutoActivate$", EditorHelp="$AutoActivateHelp$", Type="bool", Set="SetAutoActivate", Save="AutoActivate" };
|
||||
AddEnemyDef("Clonk", { SpawnType=Clonk, SpawnFunction=def.SpawnClonk }, def->GetAIClonkDefaultPropValues(), def->GetAIClonkEditorProps() );
|
||||
}
|
||||
|
|
|
@ -46,3 +46,5 @@ Speed=Geschwindigkeit
|
|||
SpeedHelp=Geschwindigkeit der Clonks in Prozent der Standardgeschwindigkeit
|
||||
Energy=Energie
|
||||
EnergyHelp=Lebenspunkte
|
||||
AttackPath=Angriffspfad
|
||||
AttackPathHelp=Pfad, entlang dessen sich die KI-Gegner bewegen. Flugrichtung fuer Raketen und Laufrichtung fuer Clonks. Flugrichtungen werden automatisch um den Spawn-Offset verschoben. Befindet sich ein Gebaeude auf einem Eckpunkt des Pfades, greifen Clonks das Gebaeude an.
|
|
@ -46,3 +46,5 @@ Speed=Speed
|
|||
SpeedHelp=Movement speed of the clonk in percent of the default.
|
||||
Energy=Energy
|
||||
EnergyHelp=Life points of the spwned clonk.
|
||||
AttackPath=Attack path
|
||||
AttackPathHelp=Path along which AI clonks move. Flight path for rockets and walk path for clonks. Complete flight is offset by spawn position if a spawn range is given. If a vertex of the attack path is placed on a structure (e.g. a gate), clonks will attack the structure.
|
Loading…
Reference in New Issue