Enemy spawn: Add rocket spawns

alut-include-path
Sven Eberhardt 2017-02-25 20:24:46 -05:00
parent 6af82f193a
commit 6f8368da93
5 changed files with 120 additions and 42 deletions

View File

@ -59,7 +59,13 @@ local FxFlight = new Effect
var dy = this.target->GetY() + this.target->GetBottom() - Target->GetY();
Target->SetR(Angle(0, 0, dx, dy));
}
this->Timer(0);
// Immediately start flying unless we don't have a target set yet
// If there's no target, it may be set directly after creation so let the rocket survive for one frame
// by not calling the timer yet.
if (this.target || GetLength(this.waypoints))
{
this->Timer(0);
}
},
Timer = func(int time)
{
@ -92,45 +98,48 @@ local FxFlight = new Effect
dy = this.current_waypoint.Y - Target->GetY();
}
// Explode if close enough to target.
if (ObjectDistance(Target, this.target) < 12)
if (this.target)
{
Target->DoFireworks(NO_OWNER);
return FX_Execute_Kill;
}
// Get relative coordinates to target.
if (this.target && !this.current_waypoint)
{
dx = this.target->GetX() - Target->GetX();
dy = this.target->GetY() + this.target->GetBottom() - Target->GetY();
// Check if path is free to target, if not try to find a way around using waypoints.
if (!PathFree(this.target->GetX(), this.target->GetY(), Target->GetX(), Target->GetY())/* && !Target->GBackSolid(dx, dy)*/)
// Explode if close enough to target.
if (ObjectDistance(Target, this.target) < 12)
{
// Try to set a waypoint half way on a line orthogonal to the current direction.
for (var attempts = 0; attempts < 40; attempts++)
Target->DoFireworks(NO_OWNER);
return FX_Execute_Kill;
}
// Get relative coordinates to target.
if (!this.current_waypoint)
{
dx = this.target->GetX() - Target->GetX();
dy = this.target->GetY() + this.target->GetBottom() - Target->GetY();
// Check if path is free to target, if not try to find a way around using waypoints.
if (!PathFree(this.target->GetX(), this.target->GetY(), Target->GetX(), Target->GetY())/* && !Target->GBackSolid(dx, dy)*/)
{
var d = Sqrt(dx**2 + dy**2);
var try_dist = Max(20 + 2 * attempts, d * attempts / 80) + RandomX(-10, 10);
var line_dist = (2 * Random(2) - 1) * try_dist;
var way_x = Target->GetX() + dx / 2 + dy * line_dist / d;
var way_y = Target->GetY() + dy / 2 - dx * line_dist / d;
// Path to new waypoint must be free and inside the landscape borders.
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;
this.current_waypoint = {X = way_x, Y = way_y};
break;
// Try to set a waypoint half way on a line orthogonal to the current direction.
for (var attempts = 0; attempts < 40; attempts++)
{
var d = Sqrt(dx**2 + dy**2);
var try_dist = Max(20 + 2 * attempts, d * attempts / 80) + RandomX(-10, 10);
var line_dist = (2 * Random(2) - 1) * try_dist;
var way_x = Target->GetX() + dx / 2 + dy * line_dist / d;
var way_y = Target->GetY() + dy / 2 - dx * line_dist / d;
// Path to new waypoint must be free and inside the landscape borders.
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;
this.current_waypoint = {X = way_x, Y = way_y};
break;
}
}
}
}
// At this distance, fly horizontally. When getting closer, gradually turn to direct flight into target.
if (this.target && !this.current_waypoint)
{
var aim_dist = 600;
dy = dy * (aim_dist - Abs(dx)) / aim_dist;
// At this distance, fly horizontally. When getting closer, gradually turn to direct flight into target.
if (!this.current_waypoint)
{
var aim_dist = 600;
dy = dy * (aim_dist - Abs(dx)) / aim_dist;
}
}
var angle_to_target = Angle(0, 0, dx, dy);
var angle_rocket = Target->GetR();
@ -241,9 +250,7 @@ private func DoFireworks(int killed_by)
SetRider(nil);
}
// Notify defense goal for reward and score.
var defense = FindObject(Find_ID(Goal_Defense));
if (defense)
defense->~OnClonkDeath(this, killed_by);
GameCallEx("OnRocketDeath", this, killed_by);
Fireworks();
Explode(40);
return;
@ -258,6 +265,53 @@ public func Destruction()
public func HasNoNeedForAI() { return true; }
/* Enemy spawn registration */
public func Definition(def)
{
if (def == DefenseBoomAttack)
{
var spawn_editor_props = { Type="proplist", Name=def->GetName(), EditorProps= {
Rider = { Name="$Rider$", EditorHelp="$RiderHelp$", Type="enum", ValueKey="Properties", OptionKey="Type", Options=[
{ Name="$None$", EditorHelp="$NoRiderHelp$" },
{ Name=Clonk->GetName(), EditorHelp="$ClonkRiderHelp$", Value={ Type="Clonk", Properties=EnemySpawn->GetAIClonkDefaultPropValues() }, Delegate=EnemySpawn->GetAIClonkEditorProps() }
] },
FlySpeed = { Name="$FlySpeed$", EditorHelp="$FlySpeedHelp$", Type="int", Min=5, Max=10000 }
} };
var spawn_default_values = {
Rider = nil,
FlySpeed = def.FlySpeed
};
EnemySpawn->AddEnemyDef("BoomAttack", { SpawnType=DefenseBoomAttack, SpawnFunction=def.SpawnBoomAttack, OffsetAttackPathByPos=true }, spawn_default_values, spawn_editor_props);
}
}
private func SpawnBoomAttack(array pos, proplist enemy_data, proplist enemy_def, array attack_path, object spawner)
{
// Spawn the boomattack
var boom = CreateObject(DefenseBoomAttack, pos[0], pos[1], g_enemyspawn_player);
if (!boom) return;
// Boomattack settings
boom.FlySpeed = enemy_data.FlySpeed;
var wp0 = attack_path[0];
boom->SetR(Angle(0, 0, wp0.X - pos[0], wp0.Y - pos[1]) + Random(11)-5);
boom->SetWaypoints(attack_path);
// Rider?
if (enemy_data.Rider && enemy_data.Rider.Type == "Clonk")
{
// Target the rider AI to the final position of the attack path (in case it gets shot down)
var clonk = EnemySpawn->SpawnClonk(pos, enemy_data.Rider.Properties, enemy_def.Rider, [attack_path[-1]], spawner);
if (clonk)
{
clonk->SetAction("Ride", boom);
return [boom, clonk];
}
}
// Return rider-less boom attack
return boom;
}
/*-- Properties --*/
local ActMap = {

View File

@ -1,2 +1,9 @@
Name=Boom-Angriff
Description=Böse Rakete die einen angreift! Könnte auch von Gegnern geritten werden.
Description=Böse Rakete die einen angreift! Könnte auch von Gegnern geritten werden.
FlySpeed=Fluggeschwindigkeit
FlySpeedHelp=Wie schnell die Rakete fliegt.
Rider=Reiter
RiderHelp=Clonk, der auf der Rakete reitet und den Spieler angreift.
None=Keiner
NoRiderHelp=Die Rakete fliegt ohne Clonk.
ClonkRiderHelp=Die Rakete traegt einen angreifenden Clonk.

View File

@ -1,2 +1,9 @@
Name=Boom Attack
Description=Angry rocket that attacks you! Might be ridden by enemies as well.
Description=Angry rocket that attacks you! Might be ridden by enemies as well.
FlySpeed=Flight speed
FlySpeedHelp=How fast the rocket flies.
Rider=Rider
RiderHelp=Clonk which rides on the rocket and attacks the player.
None=None
NoRiderHelp=The rocket is launched without a clonk.
ClonkRiderHelp=The rocket is launched with a clonk riding on top.

View File

@ -331,6 +331,11 @@ public func OnWaveCompleted(int wave_nr)
/*-- Reward --*/
public func OnRocketDeath(object rocket, int killed_by)
{
return OnClonkDeath(rocket, killed_by);
}
public func OnClonkDeath(object clonk, int killed_by)
{
var plrid = GetPlayerID(killed_by);

View File

@ -227,7 +227,12 @@ public func TrackSpawnedEnemy(object enemy)
}
}
public func OnClonkDeath(object clonk)
public func OnRocketDeath(object rocket, int killed_by)
{
return OnClonkDeath(rocket, killed_by);
}
public func OnClonkDeath(object clonk, int killed_by)
{
if (clonk.EnemySpawn_source == this)
{
@ -522,7 +527,7 @@ public func GetAIClonkEditorProps()
props.Speed = { Name="$Speed$", EditorHelp="$SpeedHelp$", Type="int", Min=5 };
props.Energy = { Name="$Energy$", EditorHelp="$EnergyHelp$", Type="int", Min=1, Max=100000 };
props.Backpack = { Name="$Backpack$", EditorHelp="$BackpackHelp$", Type="bool" };
this.AIClonkEditorProps = { Type="proplist", Name=Clonk->GetName(), EditorProps=props };
this.AIClonkEditorProps = { Type="proplist", Name=Clonk->GetName(), EditorProps=props, Priority=100 };
}
return this.AIClonkEditorProps;
}