forked from Mirrors/openclonk
improve AI for defense rounds (boom attack path finding)
parent
3acd4a22f2
commit
2b4e40e589
|
@ -100,7 +100,7 @@ public func GetAttackWave(int nr)
|
|||
Score = 50,
|
||||
Enemies = []
|
||||
};
|
||||
|
||||
|
||||
// Add enemy ground troups: swordsman, archer, spearman, grenadier, bomber.
|
||||
PushBack(wave.Enemies, new DefenseEnemy.Swordsman {
|
||||
Amount = BoundBy((nr + 2) / 5, 0, 20),
|
||||
|
@ -154,35 +154,69 @@ public func GetAttackWave(int nr)
|
|||
// The attackers should go for flagpoles, then crewmembers, and then hostile structures.
|
||||
public func GiveRandomAttackTarget(object attacker)
|
||||
{
|
||||
var target = FindObject(Find_Category(C4D_Structure), Find_Func("IsFlagpole"), Find_Hostile(attacker->GetController()), Sort_Random());
|
||||
var controller = attacker->GetController();
|
||||
var target = FindObject(Find_ID(Flagpole), Find_Hostile(controller), Sort_Random());
|
||||
if (target)
|
||||
return target;
|
||||
target = FindObject(Find_OCF(OCF_CrewMember), Find_Hostile(attacker->GetController()), Sort_Distance());
|
||||
target = FindObject(Find_OCF(OCF_CrewMember), Find_Hostile(controller), Sort_Distance());
|
||||
if (target)
|
||||
return target;
|
||||
var target = FindObject(Find_Category(C4D_Structure), Find_Hostile(attacker->GetController()), Sort_Random());
|
||||
var target = FindObject(Find_Category(C4D_Structure), Find_Hostile(controller), Sort_Random());
|
||||
if (target)
|
||||
return target;
|
||||
return;
|
||||
}
|
||||
|
||||
// Returns what boom attacks should go for while on their respective paths.
|
||||
public func GiveAttackTargetOnWaypointPath(object boomattack)
|
||||
{
|
||||
var controller = boomattack->GetController();
|
||||
var target = boomattack->FindObject(Find_ID(Flagpole), Find_Hostile(controller), Find_Distance(100), boomattack->Find_PathFree(), Sort_Distance());
|
||||
if (target)
|
||||
return target;
|
||||
target = boomattack->FindObject(Find_OCF(OCF_CrewMember), Find_Hostile(controller), Find_Distance(100), boomattack->Find_PathFree(), Sort_Distance());
|
||||
if (target)
|
||||
return target;
|
||||
var target = boomattack->FindObject(Find_Category(C4D_Structure), Find_Hostile(controller), Find_Distance(100), boomattack->Find_PathFree(), Sort_Distance());
|
||||
if (target)
|
||||
return target;
|
||||
return;
|
||||
}
|
||||
|
||||
// Give some of the boom attacks a certain path to ensure the inside of the hill is attacked.
|
||||
public func GetBoomAttackWaypoints(object boompack)
|
||||
{
|
||||
// Construct three different paths which all pass through the inside of the hill and cover a large area.
|
||||
// Upper path through the upper entrance and then straight down and finally into the ruby mine.
|
||||
if (!Random(3))
|
||||
{
|
||||
// Choose a path through the rock or on the side of the main flagpole.
|
||||
if (!Random(2))
|
||||
return [
|
||||
{X = 450 + Random(10), Y = 680 + Random(10)},
|
||||
{X = 440 + Random(10), Y = 700 + Random(10)},
|
||||
{X = 100 + Random(200), Y = 500 + Random(200)}
|
||||
];
|
||||
return [
|
||||
{X = 70 + Random(30), Y = 460 + Random(10)},
|
||||
{X = 70 + Random(30), Y = 490 + Random(10)},
|
||||
{X = 100 + Random(200), Y = 500 + Random(200)}
|
||||
{X = 360 + Random(40), Y = 80 + Random(280)},
|
||||
{X = 75 + Random(20), Y = 440 + Random(20)},
|
||||
{X = 60 + Random(100), Y = 720 + Random(120)},
|
||||
{X = 395 + Random(5), Y = 845 + Random(5)},
|
||||
{X = 465 + Random(5), Y = 885 + Random(5)},
|
||||
{X = 500 + Random(180), Y = 850 + Random(100)}
|
||||
];
|
||||
}
|
||||
return nil;
|
||||
// Middle path through the upper entrance and then diagonally down into the ruby mine.
|
||||
if (!Random(2))
|
||||
return [
|
||||
{X = 500 + Random(100), Y = 350 + Random(100)},
|
||||
{X = 200 + Random(50), Y = 400 + Random(20)},
|
||||
{X = 75 + Random(20), Y = 440 + Random(20)},
|
||||
{X = 280 + Random(40), Y = 680 + Random(60)},
|
||||
{X = 395 + Random(5), Y = 845 + Random(5)},
|
||||
{X = 465 + Random(5), Y = 885 + Random(5)},
|
||||
{X = 500 + Random(180), Y = 850 + Random(100)}
|
||||
];
|
||||
// Lower path throught the lower entrance and then into the ruby mine.
|
||||
return [
|
||||
{X = 660 + Random(240), Y = 580 + Random(80)},
|
||||
{X = 480 + Random(10), Y = 650 + Random(10)},
|
||||
{X = 455 + Random(5), Y = 680 + Random(10)},
|
||||
{X = 440 + Random(10), Y = 700 + Random(10)},
|
||||
{X = 150 + Random(100), Y = 640 + Random(120)},
|
||||
{X = 395 + Random(5), Y = 845 + Random(5)},
|
||||
{X = 465 + Random(5), Y = 885 + Random(5)},
|
||||
{X = 500 + Random(180), Y = 850 + Random(100)}
|
||||
];
|
||||
}
|
||||
|
|
|
@ -49,10 +49,15 @@ local FxFlight = new Effect
|
|||
{
|
||||
Construction = func()
|
||||
{
|
||||
// Add AI for logging purposes.
|
||||
this.fx_ai = DefenseAI->AddAI(Target);
|
||||
this.fx_ai->SetActive(false);
|
||||
// Get a target.
|
||||
this.target = GetRandomAttackTarget(Target);
|
||||
// Get the boom attack waypoints from the scenario or make an array.
|
||||
this.waypoints = GameCall("GetBoomAttackWaypoints", Target) ?? [];
|
||||
this.current_waypoint = nil;
|
||||
this.attack_on_way_point_flight = false;
|
||||
if (this.target)
|
||||
{
|
||||
var dx = this.target->GetX() - Target->GetX();
|
||||
|
@ -73,6 +78,7 @@ local FxFlight = new Effect
|
|||
if (!this.target)
|
||||
{
|
||||
this.target = GetRandomAttackTarget(Target);
|
||||
DefenseAI->LogAI_Info(this.fx_ai, Format("BoomAttack lost target and updated it to %v.", this.target));
|
||||
if (!this.target && !this.current_waypoint && GetLength(this.waypoints) == 0)
|
||||
{
|
||||
Target->DoFireworks(NO_OWNER);
|
||||
|
@ -81,9 +87,11 @@ local FxFlight = new Effect
|
|||
}
|
||||
|
||||
// Check if reached current waypoint.
|
||||
if (this.current_waypoint)
|
||||
if (Distance(this.current_waypoint.X, this.current_waypoint.Y, Target->GetX(), Target->GetY()) < 8)
|
||||
this.current_waypoint = nil;
|
||||
if (this.current_waypoint && Distance(this.current_waypoint.X, this.current_waypoint.Y, Target->GetX(), Target->GetY()) < 8)
|
||||
{
|
||||
DefenseAI->LogAI_Info(this.fx_ai, Format("BoomAttack reached waypoint (%d, %d).", this.current_waypoint.X, this.current_waypoint.Y));
|
||||
this.current_waypoint = nil;
|
||||
}
|
||||
|
||||
// Get relative coordinates to target.
|
||||
var dx, dy;
|
||||
|
@ -103,10 +111,22 @@ local FxFlight = new Effect
|
|||
// Explode if close enough to target.
|
||||
if (ObjectDistance(Target, this.target) < 12)
|
||||
{
|
||||
Target->DoFireworks(NO_OWNER);
|
||||
DefenseAI->LogAI_Info(this.fx_ai, Format("BoomAttack is in reach of enemy %v and explodes now.", this.target));
|
||||
Target->DoFireworks(NO_OWNER);
|
||||
return FX_Execute_Kill;
|
||||
}
|
||||
|
||||
// Move to a nearby target if path is free and attacking is allowed.
|
||||
var target_on_path = GetAttackTargetOnWaypointPath();
|
||||
if (target_on_path && this.current_waypoint)
|
||||
{
|
||||
// Give up current and future waypoints and set to new target.
|
||||
this.current_waypoint = nil;
|
||||
this.waypoints = [];
|
||||
this.target = target_on_path;
|
||||
DefenseAI->LogAI_Info(this.fx_ai, Format("BoomAttack found new target %v on waypoint path.", target_on_path));
|
||||
}
|
||||
|
||||
// Get relative coordinates to target.
|
||||
if (!this.current_waypoint)
|
||||
{
|
||||
|
@ -127,7 +147,8 @@ local FxFlight = new Effect
|
|||
if (!PathFree(Target->GetX(), Target->GetY(), way_x, way_y) || !PathFree(this.target->GetX(), this.target->GetY(), way_x, way_y))
|
||||
continue;
|
||||
if (!Inside(way_x, 0, LandscapeWidth()) || !Inside(way_y, 0, LandscapeHeight()))
|
||||
continue;
|
||||
continue;
|
||||
DefenseAI->LogAI_Info(this.fx_ai, Format("BoomAttack at (%d, %d) is aiming for %v at (%d, %d) takes a new route through (%d, %d).", Target->GetX(), Target->GetY(), this.target, this.target->GetX(), this.target->GetY(), way_x, way_y));
|
||||
this.current_waypoint = {X = way_x, Y = way_y};
|
||||
break;
|
||||
}
|
||||
|
@ -147,7 +168,7 @@ local FxFlight = new Effect
|
|||
angle_rocket += 360;
|
||||
// Gradually update the angle.
|
||||
var angle_delta = angle_rocket - angle_to_target;
|
||||
var angle_step = BoundBy(Target.FlySpeed / 25, 4, 8);
|
||||
var angle_step = BoundBy(Target.FlySpeed / 25, 4, 10);
|
||||
if (Inside(angle_delta, 0, 180) || Inside(angle_delta, -360, -180))
|
||||
Target->SetR(Target->GetR() - Min(angle_step, Abs(angle_delta)));
|
||||
else if (Inside(angle_delta, -180, 0) || Inside(angle_delta, 180, 360))
|
||||
|
@ -179,6 +200,21 @@ local FxFlight = new Effect
|
|||
SetTarget = func(object target)
|
||||
{
|
||||
this.target = target;
|
||||
},
|
||||
|
||||
SetAttackOnWaypointPath = func(bool on)
|
||||
{
|
||||
this.attack_on_way_point_path = on;
|
||||
},
|
||||
|
||||
GetAttackTargetOnWaypointPath = func()
|
||||
{
|
||||
var attack_target = GameCall("GiveAttackTargetOnWaypointPath", Target);
|
||||
if (attack_target && PathFree(Target->GetX(), Target->GetY(), attack_target->GetX(), attack_target->GetY()))
|
||||
return attack_target;
|
||||
if (!this.attack_on_way_point_path)
|
||||
return nil;
|
||||
return Target->FindObject(Find_Category(C4D_Structure | C4D_Living | C4D_Vehicle), Find_Hostile(Target->GetController()), Find_Distance(100), Target->Find_PathFree(), Sort_Distance());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -177,6 +177,16 @@ local FxTrackObservation = new Effect
|
|||
|
||||
public func IsFulfilled() { return is_fulfilled; }
|
||||
|
||||
// Can be called by a scenario if some other survival condition has failed.
|
||||
// For example if some object that needed to be defended has been destroyed.
|
||||
public func NotifyFailedSurvival()
|
||||
{
|
||||
// Relaunch all players so that the goal is correctly fulfilled.
|
||||
for (var plr in GetPlayers(C4PT_User))
|
||||
RelaunchPlayer(plr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*-- Score --*/
|
||||
|
||||
|
|
|
@ -358,7 +358,7 @@ public func ExecuteAirplaneCarpetBomber(effect fx)
|
|||
return false;
|
||||
// Calculate where the bomb would land and check if it would hit any enemies.
|
||||
var bomb_flight = fx.vehicle->SimFlight(0, 12);
|
||||
var bomb_target = FindObject(Find_Hostile(fx.Target->GetController()), Find_Distance(20, bomb_flight[0], bomb_flight[1]));
|
||||
var bomb_target = FindObject(Find_Hostile(fx.Target->GetController()), Find_Or(Find_AtPoint(bomb_flight[0], bomb_flight[1]), Find_Distance(20, bomb_flight[0], bomb_flight[1])));
|
||||
if (bomb_target && this->IsAirplaneTarget(fx, bomb_target, nil))
|
||||
{
|
||||
this->LogAI_Info(fx, Format("ExecuteAirplaneCarpetBomber for %v at (%d, %d) found bomb target %v at (%d, %d).", fx.vehicle, fx.vehicle->GetX(), fx.vehicle->GetY(), bomb_target, bomb_flight[0], bomb_flight[1]));
|
||||
|
|
|
@ -314,7 +314,7 @@ public func OnActivateAI(proplist fx_ai)
|
|||
}
|
||||
|
||||
// Callback when the AI is deactivated by a trigger
|
||||
public func OnDectivateAI(proplist fx_ai)
|
||||
public func OnDeactivateAI(proplist fx_ai)
|
||||
{
|
||||
_inherited(fx_ai);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue