Core: Added basic support for Spectators
TODO: Someone modify the files for cnc (chrome / rules)
This commit is contained in:
@@ -56,7 +56,7 @@ namespace OpenRA.Network
|
|||||||
public string Bot; // trait name of the bot to initialize in this slot, or null otherwise.
|
public string Bot; // trait name of the bot to initialize in this slot, or null otherwise.
|
||||||
public bool Closed; // host has explicitly closed this slot.
|
public bool Closed; // host has explicitly closed this slot.
|
||||||
public string MapPlayer; // playerReference to bind against.
|
public string MapPlayer; // playerReference to bind against.
|
||||||
|
public bool Spectator = false; // Spectating or not
|
||||||
// todo: more stuff?
|
// todo: more stuff?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ namespace OpenRA
|
|||||||
PlayerName = client.Name;
|
PlayerName = client.Name;
|
||||||
InternalName = pr.Name;
|
InternalName = pr.Name;
|
||||||
Country = world.GetCountries()
|
Country = world.GetCountries()
|
||||||
.FirstOrDefault(c => client != null && client.Country == c.Race)
|
.FirstOrDefault(c => client != null && client.Country == c.Race )
|
||||||
?? world.GetCountries().Random(world.SharedRandom);
|
?? world.GetCountries().Random(world.SharedRandom);
|
||||||
|
|
||||||
ClientIndex = client.Index;
|
ClientIndex = client.Index;
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ namespace OpenRA.Server
|
|||||||
|
|
||||||
const int MasterPingInterval = 60 * 3; // 3 minutes. server has a 5 minute TTL for games, so give ourselves a bit
|
const int MasterPingInterval = 60 * 3; // 3 minutes. server has a 5 minute TTL for games, so give ourselves a bit
|
||||||
// of leeway.
|
// of leeway.
|
||||||
|
|
||||||
|
public static int MaxSpectators = 4; // How many spectators to allow // @todo Expose this as an option
|
||||||
|
|
||||||
static int lastPing = 0;
|
static int lastPing = 0;
|
||||||
static bool isInternetServer;
|
static bool isInternetServer;
|
||||||
static string masterServerUrl;
|
static string masterServerUrl;
|
||||||
@@ -135,6 +138,12 @@ namespace OpenRA.Server
|
|||||||
.Where(s => s != null)
|
.Where(s => s != null)
|
||||||
.Select((s, i) => { s.Index = i; return s; })
|
.Select((s, i) => { s.Index = i; return s; })
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
// Generate slots for spectators
|
||||||
|
for (int i = 0; i < MaxSpectators; i++)
|
||||||
|
{
|
||||||
|
lobbyInfo.Slots.Add(new Session.Slot { Spectator = true, Index = lobbyInfo.Slots.Count(), MapPlayer = null, Bot = null});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* lobby rework todo:
|
/* lobby rework todo:
|
||||||
@@ -283,6 +292,8 @@ namespace OpenRA.Server
|
|||||||
|
|
||||||
static void SyncClientToPlayerReference(Session.Client c, PlayerReference pr)
|
static void SyncClientToPlayerReference(Session.Client c, PlayerReference pr)
|
||||||
{
|
{
|
||||||
|
if (pr == null)
|
||||||
|
return;
|
||||||
if (pr.LockColor)
|
if (pr.LockColor)
|
||||||
{
|
{
|
||||||
c.Color1 = pr.Color;
|
c.Color1 = pr.Color;
|
||||||
@@ -358,6 +369,13 @@ namespace OpenRA.Server
|
|||||||
SyncLobbyInfo();
|
SyncLobbyInfo();
|
||||||
return true;
|
return true;
|
||||||
}},
|
}},
|
||||||
|
{ "spectator",
|
||||||
|
s =>
|
||||||
|
{
|
||||||
|
// GetClient(conn).Slot = -1; /* observer */
|
||||||
|
SyncLobbyInfo();
|
||||||
|
return true;
|
||||||
|
}},
|
||||||
{ "team",
|
{ "team",
|
||||||
s =>
|
s =>
|
||||||
{
|
{
|
||||||
@@ -411,7 +429,7 @@ namespace OpenRA.Server
|
|||||||
var cl = GetClient(conn);
|
var cl = GetClient(conn);
|
||||||
cl.Slot = slot;
|
cl.Slot = slot;
|
||||||
|
|
||||||
SyncClientToPlayerReference(cl, Map.Players[slotData.MapPlayer]);
|
SyncClientToPlayerReference(cl, slotData.MapPlayer != null ? Map.Players[slotData.MapPlayer] : null);
|
||||||
|
|
||||||
SyncLobbyInfo();
|
SyncLobbyInfo();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
void DrawUnitPath(Actor self)
|
void DrawUnitPath(Actor self)
|
||||||
{
|
{
|
||||||
if (!self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug) return;
|
if (self.World.LocalPlayer == null ||!self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug) return;
|
||||||
|
|
||||||
var activity = self.GetCurrentActivity();
|
var activity = self.GetCurrentActivity();
|
||||||
var mobile = self.TraitOrDefault<IMove>();
|
var mobile = self.TraitOrDefault<IMove>();
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace OpenRA.Widgets.Delegates
|
|||||||
{
|
{
|
||||||
public class LobbyDelegate : IWidgetDelegate
|
public class LobbyDelegate : IWidgetDelegate
|
||||||
{
|
{
|
||||||
Widget Players, LocalPlayerTemplate, RemotePlayerTemplate, EmptySlotTemplate, EmptySlotTemplateHost;
|
Widget Players, LocalPlayerTemplate, RemotePlayerTemplate, EmptySlotTemplate, EmptySlotTemplateHost, EmptySpectatorSlotTemplateHost;
|
||||||
|
|
||||||
Dictionary<string, string> CountryNames;
|
Dictionary<string, string> CountryNames;
|
||||||
string MapUid;
|
string MapUid;
|
||||||
@@ -44,6 +44,7 @@ namespace OpenRA.Widgets.Delegates
|
|||||||
RemotePlayerTemplate = Players.GetWidget("TEMPLATE_REMOTE");
|
RemotePlayerTemplate = Players.GetWidget("TEMPLATE_REMOTE");
|
||||||
EmptySlotTemplate = Players.GetWidget("TEMPLATE_EMPTY");
|
EmptySlotTemplate = Players.GetWidget("TEMPLATE_EMPTY");
|
||||||
EmptySlotTemplateHost = Players.GetWidget("TEMPLATE_EMPTY_HOST");
|
EmptySlotTemplateHost = Players.GetWidget("TEMPLATE_EMPTY_HOST");
|
||||||
|
EmptySpectatorSlotTemplateHost = Players.GetWidget("TEMPLATE_EMPTY_SPECTATOR");
|
||||||
|
|
||||||
var mapPreview = lobby.GetWidget<MapPreviewWidget>("LOBBY_MAP_PREVIEW");
|
var mapPreview = lobby.GetWidget<MapPreviewWidget>("LOBBY_MAP_PREVIEW");
|
||||||
mapPreview.Map = () => Map;
|
mapPreview.Map = () => Map;
|
||||||
@@ -71,6 +72,7 @@ namespace OpenRA.Widgets.Delegates
|
|||||||
};
|
};
|
||||||
|
|
||||||
CountryNames = Rules.Info["world"].Traits.WithInterface<OpenRA.Traits.CountryInfo>().ToDictionary(a => a.Race, a => a.Name);
|
CountryNames = Rules.Info["world"].Traits.WithInterface<OpenRA.Traits.CountryInfo>().ToDictionary(a => a.Race, a => a.Name);
|
||||||
|
|
||||||
CountryNames.Add("random", "Random");
|
CountryNames.Add("random", "Random");
|
||||||
|
|
||||||
var mapButton = lobby.GetWidget("CHANGEMAP_BUTTON");
|
var mapButton = lobby.GetWidget("CHANGEMAP_BUTTON");
|
||||||
@@ -232,25 +234,48 @@ namespace OpenRA.Widgets.Delegates
|
|||||||
{
|
{
|
||||||
if (Game.IsHost)
|
if (Game.IsHost)
|
||||||
{
|
{
|
||||||
template = EmptySlotTemplateHost.Clone();
|
if (slot.Spectator)
|
||||||
var name = template.GetWidget<ButtonWidget>("NAME");
|
|
||||||
name.GetText = () => s.Closed ? "Closed" : (s.Bot == null)? "Open" : "Bot: " + s.Bot;
|
|
||||||
name.OnMouseUp = _ =>
|
|
||||||
{
|
{
|
||||||
if (s.Closed)
|
template = EmptySlotTemplateHost.Clone();
|
||||||
|
var btn = template.GetWidget<ButtonWidget>("JOIN");
|
||||||
|
var name = template.GetWidget<ButtonWidget>("NAME");
|
||||||
|
btn.GetText = () => "Spectate in this slot";
|
||||||
|
name.GetText = () => s.Closed ? "Closed" : "Open";
|
||||||
|
name.OnMouseUp = _ =>
|
||||||
{
|
{
|
||||||
s.Bot = null;
|
if (s.Closed)
|
||||||
orderManager.IssueOrder(Order.Command("slot_open " + s.Index));
|
{
|
||||||
}
|
orderManager.IssueOrder(Order.Command("slot_open " + s.Index));
|
||||||
else
|
}
|
||||||
{
|
|
||||||
if (s.Bot == null && Map.Players[s.MapPlayer].AllowBots)
|
|
||||||
orderManager.IssueOrder(Order.Command("slot_bot {0} HackyAI".F(s.Index)));
|
|
||||||
else
|
else
|
||||||
|
{
|
||||||
orderManager.IssueOrder(Order.Command("slot_close " + s.Index));
|
orderManager.IssueOrder(Order.Command("slot_close " + s.Index));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
template = EmptySlotTemplateHost.Clone();
|
||||||
|
var name = template.GetWidget<ButtonWidget>("NAME");
|
||||||
|
name.GetText = () => s.Closed ? "Closed" : (s.Bot == null) ? "Open" : "Bot: " + s.Bot;
|
||||||
|
name.OnMouseUp = _ =>
|
||||||
|
{
|
||||||
|
if (s.Closed)
|
||||||
|
{
|
||||||
|
s.Bot = null;
|
||||||
|
orderManager.IssueOrder(Order.Command("slot_open " + s.Index));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s.Bot == null && Map.Players[s.MapPlayer].AllowBots)
|
||||||
|
orderManager.IssueOrder(Order.Command("slot_bot {0} HackyAI".F(s.Index)));
|
||||||
|
else
|
||||||
|
orderManager.IssueOrder(Order.Command("slot_close " + s.Index));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -330,6 +355,14 @@ namespace OpenRA.Widgets.Delegates
|
|||||||
var status = template.GetWidget<CheckboxWidget>("STATUS");
|
var status = template.GetWidget<CheckboxWidget>("STATUS");
|
||||||
status.Checked = () => c.State == Session.ClientState.Ready;
|
status.Checked = () => c.State == Session.ClientState.Ready;
|
||||||
status.OnMouseDown = CycleReady;
|
status.OnMouseDown = CycleReady;
|
||||||
|
|
||||||
|
Session.Slot slot1 = slot;
|
||||||
|
color.IsVisible = () => !slot1.Spectator;
|
||||||
|
colorBlock.IsVisible = () => !slot1.Spectator;
|
||||||
|
faction.IsVisible = () => !slot1.Spectator;
|
||||||
|
factionname.IsVisible = () => !slot1.Spectator;
|
||||||
|
factionflag.IsVisible = () => !slot1.Spectator;
|
||||||
|
team.IsVisible = () => !slot1.Spectator;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace OpenRA
|
|||||||
|
|
||||||
public void AddPlayer(Player p) { players[p.Index] = p; }
|
public void AddPlayer(Player p) { players[p.Index] = p; }
|
||||||
|
|
||||||
int localPlayerIndex;
|
int localPlayerIndex = -999;
|
||||||
public Player LocalPlayer
|
public Player LocalPlayer
|
||||||
{
|
{
|
||||||
get { return players.ContainsKey(localPlayerIndex) ? players[localPlayerIndex] : null; }
|
get { return players.ContainsKey(localPlayerIndex) ? players[localPlayerIndex] : null; }
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ namespace OpenRA
|
|||||||
|
|
||||||
public static bool IsVisible(this Actor a, Player byPlayer) /* must never be relied on in synced code! */
|
public static bool IsVisible(this Actor a, Player byPlayer) /* must never be relied on in synced code! */
|
||||||
{
|
{
|
||||||
|
if (byPlayer == null) return true; // Observer
|
||||||
if (a.World.LocalPlayer != null && a.World.LocalPlayer.Shroud.Disabled)
|
if (a.World.LocalPlayer != null && a.World.LocalPlayer.Shroud.Disabled)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA
|
|||||||
// create the players which are bound through slots.
|
// create the players which are bound through slots.
|
||||||
foreach (var slot in w.LobbyInfo.Slots)
|
foreach (var slot in w.LobbyInfo.Slots)
|
||||||
{
|
{
|
||||||
var client = w.LobbyInfo.Clients.FirstOrDefault(c => c.Slot == slot.Index);
|
var client = w.LobbyInfo.Clients.FirstOrDefault(c => c.Slot == slot.Index && slot.MapPlayer != null);
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
/* spawn a real player in this slot. */
|
/* spawn a real player in this slot. */
|
||||||
@@ -45,7 +45,7 @@ namespace OpenRA.Mods.RA
|
|||||||
if (client.Index == Game.LocalClientId)
|
if (client.Index == Game.LocalClientId)
|
||||||
w.SetLocalPlayer(player.Index); // bind this one to the local player.
|
w.SetLocalPlayer(player.Index); // bind this one to the local player.
|
||||||
}
|
}
|
||||||
else if (slot.Bot != null)
|
else if (slot.Bot != null && slot.MapPlayer != null)
|
||||||
{
|
{
|
||||||
/* spawn a bot in this slot, "owned" by the host */
|
/* spawn a bot in this slot, "owned" by the host */
|
||||||
|
|
||||||
@@ -80,6 +80,14 @@ namespace OpenRA.Mods.RA
|
|||||||
static Stance ChooseInitialStance(Player p, Player q)
|
static Stance ChooseInitialStance(Player p, Player q)
|
||||||
{
|
{
|
||||||
if (p == q) return Stance.Ally;
|
if (p == q) return Stance.Ally;
|
||||||
|
var pc = GetClientForPlayer(p);
|
||||||
|
var qc = GetClientForPlayer(q);
|
||||||
|
|
||||||
|
if (p.World.LobbyInfo.Slots.Count > 0)
|
||||||
|
{
|
||||||
|
if (p.World.LobbyInfo.Slots[pc.Slot].Spectator) return Stance.Ally;
|
||||||
|
if (p.World.LobbyInfo.Slots[qc.Slot].Spectator) return Stance.Ally;
|
||||||
|
}
|
||||||
|
|
||||||
if (p.PlayerRef.Allies.Contains(q.InternalName))
|
if (p.PlayerRef.Allies.Contains(q.InternalName))
|
||||||
return Stance.Ally;
|
return Stance.Ally;
|
||||||
@@ -92,8 +100,6 @@ namespace OpenRA.Mods.RA
|
|||||||
if (p.IsBot ^ q.IsBot)
|
if (p.IsBot ^ q.IsBot)
|
||||||
return Stance.Enemy; // bots and humans hate each other
|
return Stance.Enemy; // bots and humans hate each other
|
||||||
|
|
||||||
var pc = GetClientForPlayer(p);
|
|
||||||
var qc = GetClientForPlayer(q);
|
|
||||||
|
|
||||||
return pc.Team != 0 && pc.Team == qc.Team
|
return pc.Team != 0 && pc.Team == qc.Team
|
||||||
? Stance.Ally : Stance.Enemy;
|
? Stance.Ally : Stance.Enemy;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.Network;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
using XRandom = OpenRA.Thirdparty.Random;
|
using XRandom = OpenRA.Thirdparty.Random;
|
||||||
using OpenRA.FileFormats;
|
using OpenRA.FileFormats;
|
||||||
@@ -232,6 +233,10 @@ namespace OpenRA.Mods.RA
|
|||||||
return !hackyAI.enabled;
|
return !hackyAI.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasHumanPlayers()
|
||||||
|
{
|
||||||
|
return p.World.players.Any(a => !a.Value.IsBot && !a.Value.NonCombatant);
|
||||||
|
}
|
||||||
int2? ChooseEnemyTarget()
|
int2? ChooseEnemyTarget()
|
||||||
{
|
{
|
||||||
// Criteria for picking an enemy:
|
// Criteria for picking an enemy:
|
||||||
@@ -240,7 +245,7 @@ namespace OpenRA.Mods.RA
|
|||||||
// 3. not dead.
|
// 3. not dead.
|
||||||
|
|
||||||
var possibleTargets = world.WorldActor.Trait<MPStartLocations>().Start
|
var possibleTargets = world.WorldActor.Trait<MPStartLocations>().Start
|
||||||
.Where(kv => kv.Key != p && IsHumanPlayer(kv.Key)
|
.Where(kv => kv.Key != p && (!HasHumanPlayers()|| IsHumanPlayer(kv.Key))
|
||||||
&& p.WinState == WinState.Undefined)
|
&& p.WinState == WinState.Undefined)
|
||||||
.Select(kv => kv.Value);
|
.Select(kv => kv.Value);
|
||||||
|
|
||||||
|
|||||||
@@ -28,13 +28,15 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public void WorldLoaded(World world)
|
public void WorldLoaded(World world)
|
||||||
{
|
{
|
||||||
var taken = world.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0)
|
var taken = world.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0 && c.Slot != -1)
|
||||||
.Select(c => world.Map.SpawnPoints.ElementAt(c.SpawnPoint - 1)).ToList();
|
.Select(c => world.Map.SpawnPoints.ElementAt(c.SpawnPoint - 1)).ToList();
|
||||||
var available = world.Map.SpawnPoints.Except(taken).ToList();
|
var available = world.Map.SpawnPoints.Except(taken).ToList();
|
||||||
|
|
||||||
// Set spawn
|
// Set spawn
|
||||||
foreach (var slot in world.LobbyInfo.Slots)
|
foreach (var slot in world.LobbyInfo.Slots)
|
||||||
{
|
{
|
||||||
|
if (slot.Spectator)
|
||||||
|
continue; // Skip spectator slots
|
||||||
var client = world.LobbyInfo.Clients.FirstOrDefault(c => c.Slot == slot.Index);
|
var client = world.LobbyInfo.Clients.FirstOrDefault(c => c.Slot == slot.Index);
|
||||||
var player = FindPlayerInSlot(world, slot);
|
var player = FindPlayerInSlot(world, slot);
|
||||||
|
|
||||||
@@ -49,7 +51,7 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
// Explore allied shroud
|
// Explore allied shroud
|
||||||
foreach (var p in Start)
|
foreach (var p in Start)
|
||||||
if (p.Key == world.LocalPlayer || p.Key.Stances[world.LocalPlayer] == Stance.Ally)
|
if ((world.LocalPlayer != null ) &&(p.Key == world.LocalPlayer || p.Key.Stances[world.LocalPlayer] == Stance.Ally))
|
||||||
world.WorldActor.Trait<Shroud>().Explore(world, p.Value,
|
world.WorldActor.Trait<Shroud>().Explore(world, p.Value,
|
||||||
world.WorldActor.Info.Traits.Get<MPStartLocationsInfo>().InitialExploreRange);
|
world.WorldActor.Info.Traits.Get<MPStartLocationsInfo>().InitialExploreRange);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProductVersion>9.0.21022</ProductVersion>
|
<ProductVersion>9.0.30729</ProductVersion>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
<ProjectGuid>{4A8A43B5-A9EF-4ED0-99DD-4BAB10A0DB6E}</ProjectGuid>
|
<ProjectGuid>{4A8A43B5-A9EF-4ED0-99DD-4BAB10A0DB6E}</ProjectGuid>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@@ -230,6 +230,7 @@
|
|||||||
<Compile Include="WaterPaletteRotation.cs" />
|
<Compile Include="WaterPaletteRotation.cs" />
|
||||||
<Compile Include="Weapon.cs" />
|
<Compile Include="Weapon.cs" />
|
||||||
<Compile Include="Widgets\BuildPaletteWidget.cs" />
|
<Compile Include="Widgets\BuildPaletteWidget.cs" />
|
||||||
|
<Compile Include="Widgets\Delegates\IngameObserverChromeDelegate.cs" />
|
||||||
<Compile Include="Widgets\Delegates\IngameChromeDelegate.cs" />
|
<Compile Include="Widgets\Delegates\IngameChromeDelegate.cs" />
|
||||||
<Compile Include="Widgets\MoneyBinWidget.cs" />
|
<Compile Include="Widgets\MoneyBinWidget.cs" />
|
||||||
<Compile Include="Widgets\OrderButtonWidget.cs" />
|
<Compile Include="Widgets\OrderButtonWidget.cs" />
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ namespace OpenRA.Mods.RA
|
|||||||
{
|
{
|
||||||
public class OpenWidgetAtGameStartInfo : ITraitInfo
|
public class OpenWidgetAtGameStartInfo : ITraitInfo
|
||||||
{
|
{
|
||||||
public readonly string Widget = "INGAME_ROOT";
|
public readonly string Widget = "INGAME_ROOT";
|
||||||
|
public readonly string ObserverWidget = "";
|
||||||
|
|
||||||
public object Create(ActorInitializer init) { return new OpenWidgetAtGameStart(this); }
|
public object Create(ActorInitializer init) { return new OpenWidgetAtGameStart(this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,9 +30,10 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public void WorldLoaded(World world)
|
public void WorldLoaded(World world)
|
||||||
{
|
{
|
||||||
// Todo: custom observer ui?
|
|
||||||
if (world.LocalPlayer != null)
|
if (world.LocalPlayer != null)
|
||||||
world.OpenWindow(Info.Widget);
|
world.OpenWindow(Info.Widget);
|
||||||
|
else if (Info.ObserverWidget != null)
|
||||||
|
world.OpenWindow(Info.ObserverWidget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ namespace OpenRA.Mods.RA
|
|||||||
void SpawnUnitsForPlayer(Player p, int2 sp)
|
void SpawnUnitsForPlayer(Player p, int2 sp)
|
||||||
{
|
{
|
||||||
if (!p.PlayerRef.DefaultStartingUnits)
|
if (!p.PlayerRef.DefaultStartingUnits)
|
||||||
return; /* they don't want an mcv, the map provides something else for them. */
|
return; /* they don't want an mcv, the map provides something else for them OR it is a spectator. */
|
||||||
|
|
||||||
p.World.CreateActor("mcv", new TypeDictionary
|
p.World.CreateActor("mcv", new TypeDictionary
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ Player:
|
|||||||
|
|
||||||
World:
|
World:
|
||||||
OpenWidgetAtGameStart:
|
OpenWidgetAtGameStart:
|
||||||
|
Widget: INGAME_ROOT
|
||||||
|
# ObserverWidget:
|
||||||
ScreenShaker:
|
ScreenShaker:
|
||||||
NukePaletteEffect:
|
NukePaletteEffect:
|
||||||
WaterPaletteRotation:
|
WaterPaletteRotation:
|
||||||
|
|||||||
@@ -193,6 +193,7 @@
|
|||||||
<image name="allies" x="30" y="84" width="30" height="15" />
|
<image name="allies" x="30" y="84" width="30" height="15" />
|
||||||
<image name="soviet" x="0" y="84" width="30" height="15" />
|
<image name="soviet" x="0" y="84" width="30" height="15" />
|
||||||
<image name="random" x="60" y="84" width="30" height="15" />
|
<image name="random" x="60" y="84" width="30" height="15" />
|
||||||
|
<image name="spectator" x="60" y="84" width="30" height="15" />
|
||||||
</collection>
|
</collection>
|
||||||
<collection name="music" src="musicplayer.png">
|
<collection name="music" src="musicplayer.png">
|
||||||
<image name="pause" x="0" y="0" width="25" height="25" />
|
<image name="pause" x="0" y="0" width="25" height="25" />
|
||||||
|
|||||||
@@ -29,175 +29,204 @@ Background@SERVER_LOBBY:
|
|||||||
Y:4
|
Y:4
|
||||||
Width:244
|
Width:244
|
||||||
Height:244
|
Height:244
|
||||||
Container@PLAYERS:
|
ListBox@PLAYERSX:
|
||||||
Id:PLAYERS
|
Id:PLAYERSX
|
||||||
X:20
|
X:20
|
||||||
Y:75
|
Y:75
|
||||||
Width:500
|
Width:500
|
||||||
Height:200
|
Height:227
|
||||||
Children:
|
Children:
|
||||||
Container@TEMPLATE_LOCAL:
|
Container@PLAYERS:
|
||||||
Id:TEMPLATE_LOCAL
|
Id:PLAYERS
|
||||||
X:0
|
X:2
|
||||||
Y:0
|
Y:1
|
||||||
Width:500
|
Width:500
|
||||||
Height:30
|
Height:2270
|
||||||
Visible:false
|
|
||||||
Children:
|
Children:
|
||||||
TextField@NAME:
|
Container@TEMPLATE_LOCAL:
|
||||||
Id:NAME
|
Id:TEMPLATE_LOCAL
|
||||||
Text:Name
|
|
||||||
Width:139
|
|
||||||
Height:25
|
|
||||||
X:0
|
X:0
|
||||||
Y:0
|
Y:0
|
||||||
MaxLength:16
|
Width:400
|
||||||
Button@COLOR:
|
Height:30
|
||||||
Id:COLOR
|
Visible:false
|
||||||
Width:65
|
|
||||||
Height:25
|
|
||||||
X:159
|
|
||||||
Y:0
|
|
||||||
Children:
|
Children:
|
||||||
ColorBlock@COLORBLOCK:
|
TextField@NAME:
|
||||||
Id:COLORBLOCK
|
Id:NAME
|
||||||
X:5
|
Text:Name
|
||||||
|
Width:139
|
||||||
|
Height:25
|
||||||
|
X:0
|
||||||
|
Y:0
|
||||||
|
MaxLength:16
|
||||||
|
Button@COLOR:
|
||||||
|
Id:COLOR
|
||||||
|
Width:65
|
||||||
|
Height:25
|
||||||
|
X:159
|
||||||
|
Y:0
|
||||||
|
Children:
|
||||||
|
ColorBlock@COLORBLOCK:
|
||||||
|
Id:COLORBLOCK
|
||||||
|
X:5
|
||||||
|
Y:7
|
||||||
|
Width:PARENT_RIGHT-10
|
||||||
|
Height:PARENT_BOTTOM-12
|
||||||
|
Button@FACTION:
|
||||||
|
Id:FACTION
|
||||||
|
Width:110
|
||||||
|
Height:25
|
||||||
|
X:244
|
||||||
|
Y:0
|
||||||
|
Children:
|
||||||
|
Image@FACTIONFLAG:
|
||||||
|
Id:FACTIONFLAG
|
||||||
|
Width:30
|
||||||
|
Height:15
|
||||||
|
X:5
|
||||||
|
Y:5
|
||||||
|
Label@FACTIONNAME:
|
||||||
|
Id:FACTIONNAME
|
||||||
|
Text:Faction
|
||||||
|
Width:60
|
||||||
|
Height:25
|
||||||
|
X:40
|
||||||
|
Y:0
|
||||||
|
Button@TEAM:
|
||||||
|
Id:TEAM
|
||||||
|
Text:Team
|
||||||
|
Width:25
|
||||||
|
Height:25
|
||||||
|
X:374
|
||||||
|
Y:0
|
||||||
|
Checkbox@STATUS:
|
||||||
|
Id:STATUS
|
||||||
|
X:450
|
||||||
|
Y:2
|
||||||
|
Width:20
|
||||||
|
Height:20
|
||||||
|
Container@TEMPLATE_REMOTE:
|
||||||
|
Id:TEMPLATE_REMOTE
|
||||||
|
X:0
|
||||||
|
Y:0
|
||||||
|
Width:400
|
||||||
|
Height:30
|
||||||
|
Visible:false
|
||||||
|
Children:
|
||||||
|
Label@NAME:
|
||||||
|
Id:NAME
|
||||||
|
Text:Name
|
||||||
|
Width:139
|
||||||
|
Height:25
|
||||||
|
X:0
|
||||||
|
Y:0
|
||||||
|
ColorBlock@COLOR:
|
||||||
|
Id:COLOR
|
||||||
|
X:164
|
||||||
Y:7
|
Y:7
|
||||||
Width:PARENT_RIGHT-10
|
Width:55
|
||||||
Height:PARENT_BOTTOM-12
|
Height:13
|
||||||
Button@FACTION:
|
Label@FACTION:
|
||||||
Id:FACTION
|
Id:FACTION
|
||||||
Width:110
|
Width:110
|
||||||
Height:25
|
|
||||||
X:244
|
|
||||||
Y:0
|
|
||||||
Children:
|
|
||||||
Image@FACTIONFLAG:
|
|
||||||
Id:FACTIONFLAG
|
|
||||||
Width:30
|
|
||||||
Height:15
|
|
||||||
X:5
|
|
||||||
Y:5
|
|
||||||
Label@FACTIONNAME:
|
|
||||||
Id:FACTIONNAME
|
|
||||||
Text:Faction
|
|
||||||
Width:60
|
|
||||||
Height:25
|
Height:25
|
||||||
X:40
|
X:244
|
||||||
Y:0
|
Y:0
|
||||||
Button@TEAM:
|
Children:
|
||||||
Id:TEAM
|
Image@FACTIONFLAG:
|
||||||
Text:Team
|
Id:FACTIONFLAG
|
||||||
Width:25
|
Width:30
|
||||||
Height:25
|
Height:15
|
||||||
X:374
|
X:5
|
||||||
Y:0
|
Y:5
|
||||||
Checkbox@STATUS:
|
Label@FACTIONNAME:
|
||||||
Id:STATUS
|
Id:FACTIONNAME
|
||||||
X:455
|
Text:Faction
|
||||||
Y:2
|
Width:60
|
||||||
Width:20
|
Height:25
|
||||||
Height:20
|
X:40
|
||||||
Container@TEMPLATE_REMOTE:
|
Y:0
|
||||||
Id:TEMPLATE_REMOTE
|
Label@TEAM:
|
||||||
X:0
|
Id:TEAM
|
||||||
Y:0
|
Text:Team
|
||||||
Width:500
|
Width:70
|
||||||
Height:30
|
|
||||||
Visible:false
|
|
||||||
Children:
|
|
||||||
Label@NAME:
|
|
||||||
Id:NAME
|
|
||||||
Text:Name
|
|
||||||
Width:139
|
|
||||||
Height:25
|
|
||||||
X:0
|
|
||||||
Y:0
|
|
||||||
ColorBlock@COLOR:
|
|
||||||
Id:COLOR
|
|
||||||
X:164
|
|
||||||
Y:7
|
|
||||||
Width:55
|
|
||||||
Height:13
|
|
||||||
Label@FACTION:
|
|
||||||
Id:FACTION
|
|
||||||
Width:110
|
|
||||||
Height:25
|
|
||||||
X:244
|
|
||||||
Y:0
|
|
||||||
Children:
|
|
||||||
Image@FACTIONFLAG:
|
|
||||||
Id:FACTIONFLAG
|
|
||||||
Width:30
|
|
||||||
Height:15
|
|
||||||
X:5
|
|
||||||
Y:5
|
|
||||||
Label@FACTIONNAME:
|
|
||||||
Id:FACTIONNAME
|
|
||||||
Text:Faction
|
|
||||||
Width:60
|
|
||||||
Height:25
|
Height:25
|
||||||
X:40
|
X:351
|
||||||
Y:0
|
Y:0
|
||||||
Label@TEAM:
|
Align:Center
|
||||||
Id:TEAM
|
Bold: false
|
||||||
Text:Team
|
Checkbox@STATUS:
|
||||||
Width:70
|
Id:STATUS
|
||||||
Height:25
|
X:455
|
||||||
X:351
|
Y:2
|
||||||
Y:0
|
Width:20
|
||||||
Align:Center
|
Height:20
|
||||||
Bold: false
|
Container@TEMPLATE_EMPTY_SPECTATOR:
|
||||||
Checkbox@STATUS:
|
Id:TEMPLATE_EMPTY_SPECTATOR
|
||||||
Id:STATUS
|
|
||||||
X:455
|
|
||||||
Y:2
|
|
||||||
Width:20
|
|
||||||
Height:20
|
|
||||||
Container@TEMPLATE_EMPTY:
|
|
||||||
Id:TEMPLATE_EMPTY
|
|
||||||
X:0
|
|
||||||
Y:0
|
|
||||||
Width:500
|
|
||||||
Height:30
|
|
||||||
Visible:false
|
|
||||||
Children:
|
|
||||||
Label@NAME:
|
|
||||||
Id:NAME
|
|
||||||
Text:Name
|
|
||||||
Width:139
|
|
||||||
Height:25
|
|
||||||
X:0
|
X:0
|
||||||
Y:0
|
Y:0
|
||||||
Button@JOIN:
|
Width:400
|
||||||
Id:JOIN
|
Height:30
|
||||||
Text:Play in this slot
|
Visible:false
|
||||||
Width:PARENT_RIGHT - 160
|
Children:
|
||||||
Height:25
|
Button@NAME:
|
||||||
X:160
|
Id:NAME
|
||||||
Y:0
|
Text:Name
|
||||||
Container@TEMPLATE_EMPTY_HOST:
|
Width:139
|
||||||
Id:TEMPLATE_EMPTY_HOST
|
Height:25
|
||||||
X:0
|
X:0
|
||||||
Y:0
|
Y:0
|
||||||
Width:500
|
Button@JOIN:
|
||||||
Height:30
|
Id:JOIN
|
||||||
Visible:false
|
Text:Spectate this match
|
||||||
Children:
|
Width:PARENT_RIGHT - 90
|
||||||
Button@NAME: -- TODO: replace with dropdown
|
Height:25
|
||||||
Id:NAME
|
X:160
|
||||||
Text:Name
|
Y:0
|
||||||
Width:155
|
Container@TEMPLATE_EMPTY:
|
||||||
Height:25
|
Id:TEMPLATE_EMPTY
|
||||||
X:0
|
X:0
|
||||||
Y:0
|
Y:0
|
||||||
Button@JOIN:
|
Width:400
|
||||||
Id:JOIN
|
Height:30
|
||||||
Text:Play in this slot
|
Visible:false
|
||||||
Width:PARENT_RIGHT - 160
|
Children:
|
||||||
Height:25
|
Label@NAME:
|
||||||
X:160
|
Id:NAME
|
||||||
|
Text:Name
|
||||||
|
Width:139
|
||||||
|
Height:25
|
||||||
|
X:0
|
||||||
|
Y:0
|
||||||
|
Button@JOIN:
|
||||||
|
Id:JOIN
|
||||||
|
Text:Play in this slot
|
||||||
|
Width:PARENT_RIGHT - 90
|
||||||
|
Height:25
|
||||||
|
X:160
|
||||||
|
Y:0
|
||||||
|
Container@TEMPLATE_EMPTY_HOST:
|
||||||
|
Id:TEMPLATE_EMPTY_HOST
|
||||||
|
X:0
|
||||||
Y:0
|
Y:0
|
||||||
|
Width:400
|
||||||
|
Height:30
|
||||||
|
Visible:false
|
||||||
|
Children:
|
||||||
|
Button@NAME: -- TODO: replace with dropdown
|
||||||
|
Id:NAME
|
||||||
|
Text:Name
|
||||||
|
Width:155
|
||||||
|
Height:25
|
||||||
|
X:0
|
||||||
|
Y:0
|
||||||
|
Button@JOIN:
|
||||||
|
Id:JOIN
|
||||||
|
Text:Play in this slot
|
||||||
|
Width:PARENT_RIGHT - 90
|
||||||
|
Height:25
|
||||||
|
X:160
|
||||||
|
Y:0
|
||||||
Container@LABEL_CONTAINER:
|
Container@LABEL_CONTAINER:
|
||||||
X:30
|
X:30
|
||||||
Y:45
|
Y:45
|
||||||
@@ -240,7 +269,7 @@ Background@SERVER_LOBBY:
|
|||||||
Bold:True
|
Bold:True
|
||||||
Label@LABEL_LOBBY_STATUS:
|
Label@LABEL_LOBBY_STATUS:
|
||||||
Id:LABEL_LOBBY_STATUS
|
Id:LABEL_LOBBY_STATUS
|
||||||
X:432
|
X:420
|
||||||
Y:0
|
Y:0
|
||||||
Width:70
|
Width:70
|
||||||
Height:25
|
Height:25
|
||||||
|
|||||||
@@ -284,3 +284,133 @@ Container@INGAME_ROOT:
|
|||||||
Width:200
|
Width:200
|
||||||
Height:20
|
Height:20
|
||||||
Text: Give Exploration
|
Text: Give Exploration
|
||||||
|
Container@OBSERVER_ROOT:
|
||||||
|
Id:OBSERVER_ROOT
|
||||||
|
Visible:true
|
||||||
|
Delegate:IngameObserverChromeDelegate
|
||||||
|
Children:
|
||||||
|
WorldInteractionController:
|
||||||
|
X:0
|
||||||
|
Y:0
|
||||||
|
Width:WINDOW_RIGHT
|
||||||
|
Height:WINDOW_BOTTOM
|
||||||
|
ViewportScrollController:
|
||||||
|
X:0
|
||||||
|
Y:0
|
||||||
|
Width:WINDOW_RIGHT
|
||||||
|
Height:WINDOW_BOTTOM
|
||||||
|
Timer@GAME_TIMER:
|
||||||
|
Id:GAME_TIMER
|
||||||
|
X: WINDOW_RIGHT/2
|
||||||
|
Y: 10
|
||||||
|
Background@POSTGAME_BG:
|
||||||
|
Id:POSTGAME_BG
|
||||||
|
X:(WINDOW_RIGHT - WIDTH)/2
|
||||||
|
Y:(WINDOW_BOTTOM - HEIGHT)/2
|
||||||
|
Width:400
|
||||||
|
Height:100
|
||||||
|
Background:dialog4
|
||||||
|
Visible:false
|
||||||
|
Children:
|
||||||
|
Label@TEXT:
|
||||||
|
Id:TEXT
|
||||||
|
X:(PARENT_RIGHT - WIDTH)/2
|
||||||
|
Y:(PARENT_BOTTOM - HEIGHT)/2
|
||||||
|
Width:200
|
||||||
|
Height:40
|
||||||
|
Align:Center
|
||||||
|
Bold:True
|
||||||
|
SpecialPowerBin@INGAME_POWERS_BIN:
|
||||||
|
Id:INGAME_POWERS_BIN
|
||||||
|
X:0
|
||||||
|
Y:25
|
||||||
|
Button@INGAME_OPTIONS_BUTTON:
|
||||||
|
Id:INGAME_OPTIONS_BUTTON
|
||||||
|
X:0
|
||||||
|
Y:0
|
||||||
|
Width:160
|
||||||
|
Height:25
|
||||||
|
Text:Options
|
||||||
|
Bold:True
|
||||||
|
WorldTooltip:
|
||||||
|
Background@INGAME_OPTIONS_BG:
|
||||||
|
Id:INGAME_OPTIONS_BG
|
||||||
|
X:(WINDOW_RIGHT - WIDTH)/2
|
||||||
|
Y:(WINDOW_BOTTOM - HEIGHT)/2
|
||||||
|
Width:300
|
||||||
|
Height:320
|
||||||
|
Visible:false
|
||||||
|
Children:
|
||||||
|
Label@LABEL_TITLE:
|
||||||
|
Id:LABEL_TITLE
|
||||||
|
X:(PARENT_RIGHT - WIDTH)/2
|
||||||
|
Y:20
|
||||||
|
Width:250
|
||||||
|
Height:25
|
||||||
|
Text:Options
|
||||||
|
Align:Center
|
||||||
|
Bold:True
|
||||||
|
Button@RESUME:
|
||||||
|
Id:RESUME
|
||||||
|
X:(PARENT_RIGHT - WIDTH)/2
|
||||||
|
Y:60
|
||||||
|
Width:160
|
||||||
|
Height:25
|
||||||
|
Text:Resume
|
||||||
|
Bold:True
|
||||||
|
Button@SETTINGS:
|
||||||
|
Id:SETTINGS
|
||||||
|
X:(PARENT_RIGHT - WIDTH)/2
|
||||||
|
Y:100
|
||||||
|
Width:160
|
||||||
|
Height:25
|
||||||
|
Text:Settings
|
||||||
|
Bold:True
|
||||||
|
Button@MUSIC:
|
||||||
|
Id:MUSIC
|
||||||
|
X:(PARENT_RIGHT - WIDTH)/2
|
||||||
|
Y:140
|
||||||
|
Width:160
|
||||||
|
Height:25
|
||||||
|
Text:Music
|
||||||
|
Bold:True
|
||||||
|
Button@SURRENDER:
|
||||||
|
Id:SURRENDER
|
||||||
|
X:(PARENT_RIGHT - WIDTH)/2
|
||||||
|
Y:180
|
||||||
|
Width:160
|
||||||
|
Height:25
|
||||||
|
Text:Surrender
|
||||||
|
Bold:True
|
||||||
|
Button@DISCONNECT:
|
||||||
|
Id:DISCONNECT
|
||||||
|
X:(PARENT_RIGHT - WIDTH)/2
|
||||||
|
Y:220
|
||||||
|
Width:160
|
||||||
|
Height:25
|
||||||
|
Text:Disconnect
|
||||||
|
Bold:True
|
||||||
|
Button@QUIT:
|
||||||
|
Id:QUIT
|
||||||
|
X:(PARENT_RIGHT - WIDTH)/2
|
||||||
|
Y:260
|
||||||
|
Width:160
|
||||||
|
Height:25
|
||||||
|
Text:Quit
|
||||||
|
Bold:True
|
||||||
|
ChatDisplay@CHAT_DISPLAY:
|
||||||
|
Id:CHAT_DISPLAY
|
||||||
|
X:250
|
||||||
|
Y:WINDOW_BOTTOM - HEIGHT - 30
|
||||||
|
Width: 760
|
||||||
|
Height: 200
|
||||||
|
ClickThrough: True
|
||||||
|
DrawBackground: False
|
||||||
|
RemoveTime:250
|
||||||
|
ChatEntry@CHAT_ENTRY:
|
||||||
|
Id:CHAT_ENTRY
|
||||||
|
X:250
|
||||||
|
Y:WINDOW_BOTTOM - HEIGHT
|
||||||
|
Width: 760
|
||||||
|
Height: 30
|
||||||
|
ClickThrough: True
|
||||||
|
|||||||
@@ -125,8 +125,10 @@ Player:
|
|||||||
PlayerColorPalette:
|
PlayerColorPalette:
|
||||||
BasePalette: terrain
|
BasePalette: terrain
|
||||||
|
|
||||||
World:
|
World:
|
||||||
OpenWidgetAtGameStart:
|
OpenWidgetAtGameStart:
|
||||||
|
Widget: INGAME_ROOT
|
||||||
|
ObserverWidget: OBSERVER_ROOT
|
||||||
ScreenShaker:
|
ScreenShaker:
|
||||||
WaterPaletteRotation:
|
WaterPaletteRotation:
|
||||||
ChronoshiftPaletteEffect:
|
ChronoshiftPaletteEffect:
|
||||||
|
|||||||
Reference in New Issue
Block a user