diff --git a/OpenRA.Mods.Common/AI/DummyAI.cs b/OpenRA.Mods.Common/AI/DummyAI.cs
new file mode 100644
index 0000000000..93799b348e
--- /dev/null
+++ b/OpenRA.Mods.Common/AI/DummyAI.cs
@@ -0,0 +1,43 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
+ * This file is part of OpenRA, which is free software. It is made
+ * available to you under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version. For more
+ * information, see COPYING.
+ */
+#endregion
+
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.AI
+{
+ public sealed class DummyAIInfo : ITraitInfo, IBotInfo
+ {
+ [Desc("Ingame name this bot uses.")]
+ public readonly string Name = "Unnamed Bot";
+
+ string IBotInfo.Name { get { return Name; } }
+
+ public object Create(ActorInitializer init) { return new DummyAI(this); }
+ }
+
+ public sealed class DummyAI : IBot
+ {
+ readonly DummyAIInfo info;
+ public bool Enabled { get; private set; }
+
+ public DummyAI(DummyAIInfo info)
+ {
+ this.info = info;
+ }
+
+ void IBot.Activate(Player p)
+ {
+ Enabled = true;
+ }
+
+ IBotInfo IBot.Info { get { return info; } }
+ }
+}
diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index dd5c9ba35c..f5e309e05f 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -739,6 +739,7 @@
+
diff --git a/OpenRA.sln b/OpenRA.sln
index a1e627ecd7..62649d2044 100644
--- a/OpenRA.sln
+++ b/OpenRA.sln
@@ -82,6 +82,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Red Alert Lua scripts", "Re
mods\ra\maps\soviet-07\soviet07.lua = mods\ra\maps\soviet-07\soviet07.lua
mods\ra\maps\survival01\survival01.lua = mods\ra\maps\survival01\survival01.lua
mods\ra\maps\survival02\survival02.lua = mods\ra\maps\survival02\survival02.lua
+ mods\ra\maps\fort-lonestar\fort-lonestar-AI.lua = mods\ra\maps\fort-lonestar\fort-lonestar-AI.lua
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dune 2000 Lua scripts", "Dune 2000 Lua scripts", "{06B1AE07-DDB0-4287-8700-A8CD9A0E652E}"
diff --git a/mods/ra/maps/fort-lonestar/fort-lonestar-AI.lua b/mods/ra/maps/fort-lonestar/fort-lonestar-AI.lua
new file mode 100644
index 0000000000..ce39e1623f
--- /dev/null
+++ b/mods/ra/maps/fort-lonestar/fort-lonestar-AI.lua
@@ -0,0 +1,180 @@
+
+AIPlayers = { }
+AIBarracks = { }
+AIDerricks = { }
+AIBaseLocation = { }
+IdlingAIActors = { }
+IdlingAISupportActors = { }
+AIOnDefense = { }
+
+-- It's good to start with 10 rifle man, one medic and 5 rocket soldiers
+InitialUnitsToBuild = { "e1", "e1", "e1", "e1", "e1", "medi", "e1", "e1", "e1", "e1", "e1", "e3", "e3", "e3", "e3", "e3" }
+UnitsToBuild = { "e1", "e1", "e1", "e1", "e1", "e3", "e3", "e3", "medi" }
+
+ActivateAI = function(player, id)
+ AIPlayers[id] = player
+
+ Trigger.AfterDelay(0, function()
+ local barracks = player.GetActorsByType("tent")
+ if #barracks > 0 then
+ AIBarracks[id] = barracks[1]
+ AIBaseLocation[id] = barracks[1].Location + CVec.New(2, 1)
+ IdlingAIActors[id] = { }
+ IdlingAISupportActors[id] = { }
+ InitialInfantryProduction(id, InitialUnitsToBuild)
+ DefendActor(id, barracks[1])
+ RepairBarracks(id)
+ SellWalls(id)
+
+ Trigger.AfterDelay(DateTime.Seconds(10), function() LookOutForCrates(id) end)
+ end
+
+ local derricks = player.GetActorsByType("oilb")
+ if #derricks > 0 then
+ AIDerricks[id] = derricks[1]
+ DefendActor(id, derricks[1])
+ end
+ end)
+end
+
+InitialInfantryProduction = function(id, units)
+ local productionComplete = AIPlayers[id].Build(units, function(actors)
+ InfantryProduction(id)
+ end)
+
+ Trigger.OnProduction(AIBarracks[id], function(producer, produced)
+ BuildComplete(id, produced)
+ end)
+end
+
+InfantryProduction = function(id)
+ local productionComplete = AIPlayers[id].Build({ Utils.Random(UnitsToBuild) }, function(actors)
+ Trigger.AfterDelay(0, function() InfantryProduction(id) end)
+ end)
+
+ if not productionComplete then
+ Trigger.AfterDelay(0, function() InfantryProduction(id) end)
+ end
+end
+
+BuildComplete = function(id, actor)
+ if actor.Type == "medi" then
+ local number = #IdlingAISupportActors[id] + 1
+ IdlingAISupportActors[id][number] = actor
+
+ Trigger.OnKilled(actor, function()
+ table.remove(IdlingAISupportActors[id], number)
+ end)
+ else
+ local number = #IdlingAIActors[id] + 1
+ IdlingAIActors[id][number] = actor
+
+ Trigger.OnKilled(actor, function()
+ table.remove(IdlingAIActors[id], number)
+ end)
+ end
+
+ Trigger.AfterDelay(0, function() DefendActor(id, actor) end)
+end
+
+AttackGroupSize = 5
+SetupAttackGroup = function(id)
+ local units = { }
+
+ for i = 0, AttackGroupSize, 1 do
+ if (#IdlingAIActors[id] == 0) then
+ return units
+ end
+
+ local number = Utils.RandomInteger(0, #IdlingAIActors[id]) + 1
+ units[#units + 1] = IdlingAIActors[id][number]
+ table.remove(IdlingAIActors[id], number)
+ end
+
+ return units
+end
+
+DefendActor = function(id, actorToDefend)
+ if not actorToDefend or actorToDefend.IsDead or not actorToDefend.IsInWorld then
+ return
+ end
+
+ Trigger.OnDamaged(actorToDefend, function(self, attacker)
+ if AIOnDefense[id] or not attacker or attacker.IsDead then
+ return
+ end
+
+ -- Don't try to kill something you can't kill
+ if attacker.Type == "sniper.soviets" then
+ return
+ end
+
+ AIOnDefense[id] = true
+ local attackLoc = attacker.Location
+
+ local defenders = SetupAttackGroup(id)
+ if not defenders or #defenders == 0 then
+ Trigger.AfterDelay(DateTime.Seconds(30), function() AIOnDefense[id] = false end)
+ return
+ end
+
+ Utils.Do(defenders, function(unit)
+ if unit.IsDead then
+ return
+ end
+
+ unit.AttackMove(attackLoc)
+
+ local home = AIBaseLocation[id]
+ Trigger.OnIdle(unit, function()
+ if unit.Location == home then
+ IdlingAIActors[id][#IdlingAIActors[id] + 1] = unit
+ Trigger.Clear(unit, "OnIdle")
+ AIOnDefense[id] = false
+ else
+ unit.AttackMove(home)
+ end
+ end)
+ end)
+ end)
+end
+
+RepairBarracks = function(id)
+ Trigger.OnDamaged(AIBarracks[id], function(self, attacker)
+ self.StartBuildingRepairs(AIPlayers[id])
+ end)
+end
+
+SellWalls = function(id)
+ Media.DisplayMessage("Lonestar AI " .. id .. " sold its walls for better combat experience.")
+
+ local walls = AIPlayers[id].GetActorsByType("brik")
+ Utils.Do(walls, function(wall)
+ wall.Destroy()
+ end)
+end
+
+LookOutForCrates = function(id)
+ Trigger.OnEnteredProximityTrigger(AIBarracks[id].CenterPosition, WDist.New(12 * 1024), function(actor)
+ if actor.Type ~= "fortcrate" or #IdlingAIActors[id] == 0 then
+ return
+ end
+
+ local unit = Utils.Random(IdlingAIActors[id])
+ local home = AIBaseLocation[id]
+ local aim = actor.Location
+ if unit.IsDead then
+ return
+ end
+
+ unit.AttackMove(aim)
+ Trigger.OnIdle(unit, function()
+ if unit.Location == aim or not actor.IsInWorld then
+ unit.AttackMove(home)
+ Trigger.Clear(unit, "OnIdle")
+ else
+ unit.AttackMove(aim)
+ end
+ end)
+ end)
+end
diff --git a/mods/ra/maps/fort-lonestar/fort-lonestar.lua b/mods/ra/maps/fort-lonestar/fort-lonestar.lua
index 0717c3d8e0..6c0ba9bfa0 100644
--- a/mods/ra/maps/fort-lonestar/fort-lonestar.lua
+++ b/mods/ra/maps/fort-lonestar/fort-lonestar.lua
@@ -197,9 +197,13 @@ end
WorldLoaded = function()
soviets = Player.GetPlayer("Soviets")
players = { }
- for i = 0, 4, 1 do
+ for i = 0, 4 do
local player = Player.GetPlayer("Multi" ..i)
players[i] = player
+
+ if players[i] and players[i].IsBot then
+ ActivateAI(players[i], i)
+ end
end
Media.DisplayMessage("Defend Fort Lonestar at all costs!")
diff --git a/mods/ra/maps/fort-lonestar/map.yaml b/mods/ra/maps/fort-lonestar/map.yaml
index 442dca4c1b..fcdb8fa595 100644
--- a/mods/ra/maps/fort-lonestar/map.yaml
+++ b/mods/ra/maps/fort-lonestar/map.yaml
@@ -25,7 +25,6 @@ Players:
PlayerReference@Multi0:
Name: Multi0
Playable: True
- AllowBots: False
LockTeam: True
Team: 1
LockFaction: True
@@ -35,7 +34,6 @@ Players:
PlayerReference@Multi1:
Name: Multi1
Playable: True
- AllowBots: False
LockTeam: True
Team: 1
LockFaction: True
@@ -45,7 +43,6 @@ Players:
PlayerReference@Multi2:
Name: Multi2
Playable: True
- AllowBots: False
LockTeam: True
Team: 1
LockFaction: True
@@ -55,7 +52,6 @@ Players:
PlayerReference@Multi3:
Name: Multi3
Playable: True
- AllowBots: False
LockTeam: True
Team: 1
LockFaction: True
diff --git a/mods/ra/maps/fort-lonestar/rules.yaml b/mods/ra/maps/fort-lonestar/rules.yaml
index c2717eb3ef..bc13d43edf 100644
--- a/mods/ra/maps/fort-lonestar/rules.yaml
+++ b/mods/ra/maps/fort-lonestar/rules.yaml
@@ -27,7 +27,7 @@ World:
FlashPaletteEffect@LIGHTNINGSTRIKE:
Type: LightningStrike
LuaScript:
- Scripts: fort-lonestar.lua
+ Scripts: fort-lonestar.lua, fort-lonestar-AI.lua
ScriptUpgradesCache:
Upgrades: invulnerability
MapBuildRadius:
@@ -86,6 +86,12 @@ Player:
PlayerResources:
DefaultCashLocked: True
DefaultCash: 50
+ -HackyAI@RushAI:
+ -HackyAI@NormalAI:
+ -HackyAI@NavalAI:
+ -HackyAI@TurtleAI:
+ DummyAI@LonestarAI:
+ Name: Lonestar AI
^Infantry:
Inherits@IC: ^IronCurtainable