Add airship spawn and attack path movement to EnemySpawn

alut-include-path
Sven Eberhardt 2017-03-04 17:16:47 -05:00
parent 3ebedd0c5f
commit d2e57dd318
5 changed files with 227 additions and 43 deletions

View File

@ -95,8 +95,23 @@ 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 (this->ExecuteAttackPath(fx)) return true;
// Movement done (for now)
fx.Target->SetCommand("None");
fx.Target->SetComDir(COMD_Stop);
fx.Target->SetDir(fx.home_dir);
if (fx.vehicle) fx.vehicle->SetCommand();
// Nothing to do.
return false;
}
public func ExecuteAttackPath(effect fx)
{
// Attack path is to follow the commander.
if (fx.commander) return true;
if (fx.attack_path)
{
// Follow 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;
@ -119,7 +134,19 @@ public func ExecuteIdle(effect fx)
{
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);
if (fx.vehicle->IsAirship())
{
if (!fx.vehicle->GetCommand())
{
fx.vehicle->SetCommand("MoveTo", nil, fx.home_x, fx.home_y);
}
}
else
{
// Default vehicle movement
return fx.Target->SetCommand("PushTo", fx.vehicle, fx.home_x, fx.home_y);
}
return true;
}
}
else
@ -130,23 +157,25 @@ public func ExecuteIdle(effect fx)
}
}
// Next section on path or done?
this->AdvanceAttackPath(fx);
return false;
}
public func AdvanceAttackPath(effect fx)
{
// Pick next element in attack path if an attack path is set. Return whether a path remains.
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.
return true;
}
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;
}

View File

@ -10,7 +10,7 @@
// AI Settings.
local AirshipBoardDistance = 100; // How near must an airship be to the target to dispatch its troops.
local AirshipLostDistance = 50; // How far the pilot must be away from an airship for it to find a new pilot.
local AirshipOccludedTargetMaxDistance = 250; // IF a target is further than this and occluded, search for a new target
/*-- General Vehicle --*/
@ -62,6 +62,8 @@ private func ExecuteCatapult(effect fx)
fx.Target->SetCommand("Grab", fx.vehicle);
return true;
}
// No target? Revert to default idle proc
if (!fx.target) return false;
// Target still in guard range?
if (!this->CheckTargetInGuardRange(fx))
return false;
@ -137,39 +139,66 @@ public func ExecuteAirship(effect fx)
return true;
}
if (!fx.Target->GetCommand())
{
fx.Target->SetCommand("Grab", fx.vehicle);
}
return true;
}
// Move the airship to the target. Check if no command or is making contact, this means a new control needs to be issued.
// Move the airship to the target or along path. Check if no command or is making contact, this means a new control needs to be issued.
if (!fx.vehicle->GetCommand() || fx.vehicle->GetContact(-1))
{
// If close enough (also in y-coordinates, must be above target) release the crew.
if (fx.vehicle->ObjectDistance(fx.target) < fx.control.AirshipBoardDistance && Inside(fx.vehicle->GetY() - fx.target->GetY(), -fx.control.AirshipBoardDistance / 2, 0))
if (!fx.attack_path)
{
// Unboard the crew and let go of airship.
for (var clonk in this->GetCommanderCrew(fx))
var tx, ty;
if (fx.target)
{
var clonk_ai = clonk->GetAI();
clonk_ai.commander = nil;
if (clonk->GetProcedure() == "PUSH")
clonk->SetAction("Walk");
clonk_ai.target = fx.target;
tx = fx.target->GetX();
ty = fx.target->GetY();
}
fx.vehicle = nil;
fx.weapon = nil;
fx.Target->SetCommand("UnGrab");
return true;
}
// Find a boarding point for the target.
var boarding_point = this->GetAirshipBoardingPoint(fx);
if (boarding_point)
else
{
tx = fx.home_x;
ty = fx.home_y;
}
if (Distance(fx.vehicle->GetX(), fx.vehicle->GetY(), tx, ty) < fx.control.AirshipBoardDistance
&& Inside(fx.vehicle->GetY() - ty, -fx.control.AirshipBoardDistance / 2, 0))
{
// Unboard the crew and let go of airship.
for (var clonk in this->GetCommanderCrew(fx))
{
var clonk_ai = clonk->GetAI();
clonk_ai.commander = nil;
if (clonk->GetProcedure() == "PUSH")
clonk->SetAction("Walk");
clonk_ai.target = fx.target;
}
fx.vehicle = nil;
fx.weapon = nil;
fx.Target->SetCommand("UnGrab");
return true;
}
// Find a boarding point for the target.
var boarding_point = this->GetAirshipBoardingPoint(fx);
if (boarding_point)
{
fx.vehicle->SetCommand("MoveTo", nil, boarding_point[0], boarding_point[1]);
return true;
}
}
else
{
fx.vehicle->SetCommand("MoveTo", nil, boarding_point[0], boarding_point[1]);
return true;
// Always follow attack path
fx.vehicle->SetCommand("MoveTo", nil, fx.home_x, fx.home_y);
return true;
}
}
return false;
else if (fx.attack_path)
{
this->ExecuteAttackPath(fx);
}
// Nothing to do. But return handled because we need to keep pushing.
return true;
}
// Finds a location where to board the airship close to the target.
@ -179,16 +208,25 @@ public func GetAirshipBoardingPoint(effect fx)
return nil;
var vx = fx.vehicle->GetX();
var vy = fx.vehicle->GetY();
var tx = fx.target->GetX();
var ty = fx.target->GetY();
// Look for a new target if the current path is not free and too far away.
if (fx.vehicle->ObjectDistance(fx.target) > 250 && !PathFree(vx - 30, vy, tx, ty) && !PathFree(vx + 30, vy, tx, ty))
var tx, ty;
if (fx.target)
{
fx.target = this->FindTarget(fx);
if (!fx.target)
return nil;
tx = fx.target->GetX();
ty = fx.target->GetY();
// Look for a new target if the current path is not free and too far away.
if (fx.auto_search_target && fx.vehicle->ObjectDistance(fx.target) > AirshipOccludedTargetMaxDistance && !PathFree(vx - 30, vy, tx, ty) && !PathFree(vx + 30, vy, tx, ty))
{
fx.target = this->FindTarget(fx);
if (!fx.target)
return nil;
tx = fx.target->GetX();
ty = fx.target->GetY();
}
}
else
{
tx = fx.home_x;
ty = fx.home_y;
}
// Approach from above or the side if possible, so move airship up if below target.
if (vy > ty)
@ -233,6 +271,7 @@ public func PromoteNewAirshipCaptain(effect fx)
fx_ai.weapon = fx.vehicle;
fx_ai.vehicle = fx.vehicle;
fx_ai.strategy = this.ExecuteVehicle;
fx_ai.attack_path = fx.attack_path;
// Set new commander for remaining crew members.
for (var crew in crew_members)
if (crew != new_pilot)

View File

@ -142,9 +142,9 @@ public func FxIntAirshipMovementTimer(object target, proplist effect, int time)
}
//Rise in water
if (GBackLiquid(0,25))
//if (GBackLiquid(0,25))
//effect.SpeedY = -10;
if (GBackLiquid(0,25) && !GBackLiquid(0,24) && effect.SpeedY > 1)
//if (GBackLiquid(0,25) && !GBackLiquid(0,24) && effect.SpeedY > 1)
//effect.SpeedY = 0;
// Turn the airship around if needed
@ -160,14 +160,14 @@ public func FxIntAirshipMovementTimer(object target, proplist effect, int time)
return 1;
}
func TurnAirship(int to_dir)
func TurnAirship(int to_dir, bool instant)
{
// Default direction is left
var animName = "TurnLeft";
if (to_dir == DIR_Right)
animName = "TurnRight";
turnanim = PlayAnimation(animName, 10, Anim_Linear(0, 0, GetAnimationLength(animName), 36, ANIM_Hold));
turnanim = PlayAnimation(animName, 10, Anim_Linear(0, GetAnimationLength(animName) * !!instant, GetAnimationLength(animName), 36, ANIM_Hold));
SetAnimDir(to_dir);
@ -362,11 +362,107 @@ local ActMap = {
},
};
func Definition(def)
/* Register enemy spawn with catapult */
func Definition(proplist def)
{
SetProperty("PictureTransformation",Trans_Mul(Trans_Rotate(-25,1,0,0),Trans_Rotate(40,0,1,0)),def);
def.PictureTransformation = Trans_Mul(Trans_Rotate(-25,1,0,0),Trans_Rotate(40,0,1,0));
if (def == Airship)
{
var clonk_editor_props = { Type="enum", ValueKey="Properties", OptionKey="Type", Options=[
{ Name="$None$", EditorHelp="$NoPilotHelp$" },
{ Name=Clonk->GetName(), EditorHelp="$ClonkPilotHelp$", Value={ Type="Clonk", Properties=EnemySpawn->GetAIClonkDefaultPropValues() }, Delegate=EnemySpawn->GetAIClonkEditorProps() }
] };
var spawn_editor_props = { Type="proplist", Name=def->GetName(), EditorProps= {
Pilot = new clonk_editor_props { Name="$Pilot$", EditorHelp="$PilotHelp$" },
FlySpeed = { Name="$FlySpeed$", EditorHelp="$FlySpeedHelp$", Type="int", Min=5, Max=10000 },
Crew = { Name="$Crew$", EditorHelp="$CrewHelp$", Type="array", Elements=clonk_editor_props }
} };
var spawn_default_values = {
Pilot = { Type="Clonk", Properties=EnemySpawn->GetAIClonkDefaultPropValues() },
FlySpeed = def.FlySpeed,
Crew = [ { Type="Clonk", Properties=EnemySpawn->GetAIClonkDefaultPropValues("BowArrow", true) } ],
};
EnemySpawn->AddEnemyDef("Airship",
{ SpawnType=Airship,
SpawnFunction=def.SpawnAirship,
OffsetAttackPathByPos=true,
GetInfoString=def.GetSpawnInfoString },
spawn_default_values, spawn_editor_props);
}
}
private func SpawnAirship(array pos, proplist enemy_data, proplist enemy_def, array attack_path, object spawner)
{
// Spawn the boomattack
var airship = CreateObjectAbove(Airship, pos[0], pos[1]+15, g_enemyspawn_player);
var rval = [airship], n=1;
if (!airship) return;
airship->TurnAirship(attack_path[0].X > pos[0], true);
// Boomattack settings
airship.FlySpeed = enemy_data.FlySpeed;
// Pilot
var clonk, pilot;
if (enemy_data.Pilot && enemy_data.Pilot.Type == "Clonk")
{
// Target the rider AI to the final position of the attack path (in case it gets shot down)
airship.pilot = pilot = EnemySpawn->SpawnClonk(pos, enemy_data.Pilot.Properties, enemy_def, attack_path, spawner);
if (pilot)
{
pilot->SetAction("Push", airship);
rval[n++] = pilot;
// Set attack mode
AI->SetVehicle(pilot, airship);
}
}
// Crew
if (enemy_data.Crew)
{
var idx = 0;
for (var crew_data in enemy_data.Crew)
{
if (crew_data && crew_data.Type == "Clonk")
{
var xpos = pos[0] - 15 + 30 * idx / Max(1, GetLength(enemy_data.Crew)-1);
clonk = EnemySpawn->SpawnClonk([xpos, pos[1]], crew_data.Properties, enemy_def, attack_path, spawner);
if (clonk)
{
rval[n++] = clonk;
clonk.commander = pilot;
var ai = clonk->~GetAI();
if (ai) ai.commander = pilot;
}
}
++idx;
}
}
// Return airship and all created enemies
return rval;
}
private func GetSpawnInfoString(proplist enemy_data)
{
var s = "{{Airship}}";
if (enemy_data.Pilot && enemy_data.Pilot.Type == "Clonk")
{
s = Format("%s%s", s, EnemySpawn->GetAIClonkInfoString(enemy_data.Pilot.Properties));
}
if (enemy_data.Crew)
{
for (var crew_data in enemy_data.Crew)
{
if (crew_data && crew_data.Type == "Clonk")
{
s = Format("%s%s", s, EnemySpawn->GetAIClonkInfoString(crew_data.Properties));
}
}
}
return s;
}
/* Properties */
local Name = "$Name$";
local Description = "$Description$";
local Touchable = 2;
@ -375,3 +471,5 @@ local SolidMaskPlane = 275;
local BorderBound = C4D_Border_Sides | C4D_Border_Top | C4D_Border_Bottom;
local HitPoints = 30;
local Components = {Metal = 4, Wood = 4, Cloth = 2};
public func IsAirship() { return true; }

View File

@ -1,2 +1,11 @@
Name=Luftschiff
Description=Aerostatisches Reise- und Transportfahrzeug. Warnung: Enthält brennbare Gase. Von Zündquellen fernhalten - nicht rauchen. Vor Beschädigungen schützen. Darf nicht in die Hände von Kindern gelangen.
None=Keiner
NoPilotHelp=Das Luftschiff steht ohne Pilot in der Luft.
ClonkPilotHelp=Ein Clonk steuert das Luftschiff.
Pilot=Pilot
PilotHelp=Clonktyp, der das Luftschiff steuert.
FlySpeed=Fluggeschwindigkeit
FlySpeedHelp=Wie schnell das Luftschiff fliegen kann.
Crew=Crew
CrewHelp=Weitere Clonks auf dem Luftschiff.

View File

@ -1,2 +1,11 @@
Name=Airship
Description=Lighter-than-air travel and transport vehicle. Caution: the balloon contains volatile gas. Do not puncture.
Description=Lighter-than-air travel and transport vehicle. Caution: the balloon contains volatile gas. Do not puncture.
None=None
NoPilotHelp=The airship will stand in the air without a pilot.
ClonkPilotHelp=A clonk controls the airship.
Pilot=Pilot
PilotHelp=Typ eof clonk to steer the airship.
FlySpeed=Flight speed
FlySpeedHelp=How fast the airship can fly.
Crew=Crew
CrewHelp=Additional clonks to be placed on each airship.