Move mission objectives and victory conditions to Mods.Common
This commit is contained in:
@@ -85,6 +85,7 @@
|
||||
<Compile Include="Orders\DeployOrderTargeter.cs" />
|
||||
<Compile Include="Orders\EnterAlliedActorTargeter.cs" />
|
||||
<Compile Include="Orders\UnitOrderTargeter.cs" />
|
||||
<Compile Include="PlayerExtensions.cs" />
|
||||
<Compile Include="ServerTraits\ColorValidator.cs" />
|
||||
<Compile Include="ServerTraits\LobbyCommands.cs" />
|
||||
<Compile Include="ServerTraits\LobbySettingsNotification.cs" />
|
||||
@@ -128,16 +129,20 @@
|
||||
<Compile Include="Traits\Modifiers\DisabledOverlay.cs" />
|
||||
<Compile Include="Traits\Modifiers\HiddenUnderFog.cs" />
|
||||
<Compile Include="Traits\Modifiers\UpgradeOverlay.cs" />
|
||||
<Compile Include="Traits\MustBeDestroyed.cs" />
|
||||
<Compile Include="Traits\PaletteEffects\CloakPaletteEffect.cs" />
|
||||
<Compile Include="Traits\PaletteEffects\LightPaletteRotator.cs" />
|
||||
<Compile Include="Traits\PaletteEffects\MenuPaletteEffect.cs" />
|
||||
<Compile Include="Traits\PaletteEffects\NukePaletteEffect.cs" />
|
||||
<Compile Include="Traits\PaletteEffects\WaterPaletteRotation.cs" />
|
||||
<Compile Include="Traits\Player\ActorGroupProxy.cs" />
|
||||
<Compile Include="Traits\Player\ConquestVictoryConditions.cs" />
|
||||
<Compile Include="Traits\Player\GlobalUpgradeManager.cs" />
|
||||
<Compile Include="Traits\Player\MissionObjectives.cs" />
|
||||
<Compile Include="Traits\Player\PlaceBeacon.cs" />
|
||||
<Compile Include="Traits\Player\ProvidesCustomPrerequisite.cs" />
|
||||
<Compile Include="Traits\Player\ProvidesTechPrerequisite.cs" />
|
||||
<Compile Include="Traits\Player\StrategicVictoryConditions.cs" />
|
||||
<Compile Include="Traits\Player\TechTree.cs" />
|
||||
<Compile Include="Traits\Power\AffectedByPowerOutage.cs" />
|
||||
<Compile Include="Traits\Power\CanPowerDown.cs" />
|
||||
@@ -206,16 +211,16 @@
|
||||
<Compile Include="Warheads\HealthPercentageDamageWarhead.cs" />
|
||||
<Compile Include="Warheads\LeaveSmudgeWarhead.cs" />
|
||||
<Compile Include="Warheads\SpreadDamageWarhead.cs" />
|
||||
<Compile Include="Widgets\ColorMixerWidget.cs" />
|
||||
<Compile Include="Widgets\ColorPreviewManagerWidget.cs" />
|
||||
<Compile Include="Widgets\ConfirmationDialogs.cs" />
|
||||
<Compile Include="Widgets\HueSliderWidget.cs" />
|
||||
<Compile Include="Widgets\LabelWithTooltipWidget.cs" />
|
||||
<Compile Include="Widgets\LogicKeyListenerWidget.cs" />
|
||||
<Compile Include="Widgets\Logic\AssetBrowserLogic.cs" />
|
||||
<Compile Include="Widgets\ColorMixerWidget.cs" />
|
||||
<Compile Include="Widgets\LogicTickerWidget.cs" />
|
||||
<Compile Include="Widgets\Logic\AssetBrowserLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\ButtonTooltipLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\ColorPickerLogic.cs" />
|
||||
<Compile Include="Widgets\ColorPreviewManagerWidget.cs" />
|
||||
<Compile Include="Widgets\Logic\DisconnectWatcherLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Ingame\IngameRadarDisplayLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Ingame\LoadIngamePlayerOrObserverUILogic.cs" />
|
||||
@@ -223,6 +228,7 @@
|
||||
<Compile Include="Widgets\MenuButtonWidget.cs" />
|
||||
<Compile Include="Widgets\RadarWidget.cs" />
|
||||
<Compile Include="Widgets\ResourceBarWidget.cs" />
|
||||
<Compile Include="Widgets\StrategicProgressWidget.cs" />
|
||||
<Compile Include="SpriteLoaders\ShpTDLoader.cs" />
|
||||
<Compile Include="SpriteLoaders\ShpTSLoader.cs" />
|
||||
<Compile Include="SpriteLoaders\TmpRALoader.cs" />
|
||||
@@ -246,4 +252,4 @@ cd "$(SolutionDir)"</PostBuildEvent>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
23
OpenRA.Mods.Common/PlayerExtensions.cs
Normal file
23
OpenRA.Mods.Common/PlayerExtensions.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
public static class PlayerExtensions
|
||||
{
|
||||
public static bool HasNoRequiredUnits(this Player player)
|
||||
{
|
||||
return player.World.ActorsWithTrait<MustBeDestroyed>().All(p => p.Actor.Owner != player);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
OpenRA.Mods.Common/Traits/MustBeDestroyed.cs
Normal file
18
OpenRA.Mods.Common/Traits/MustBeDestroyed.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Tag trait for things that must be destroyed for a short game to end.")]
|
||||
public class MustBeDestroyedInfo : TraitInfo<MustBeDestroyed> { }
|
||||
public class MustBeDestroyed { }
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public class ConquestVictoryConditionsInfo : ITraitInfo, Requires<MissionObjectivesInfo>
|
||||
{
|
||||
[Desc("Delay for the end game notification in milliseconds.")]
|
||||
public int NotificationDelay = 1500;
|
||||
|
||||
public object Create(ActorInitializer init) { return new ConquestVictoryConditions(init.self, this); }
|
||||
}
|
||||
|
||||
public class ConquestVictoryConditions : ITick, INotifyObjectivesUpdated
|
||||
{
|
||||
readonly ConquestVictoryConditionsInfo info;
|
||||
readonly MissionObjectives mo;
|
||||
int objectiveID = -1;
|
||||
|
||||
public ConquestVictoryConditions(Actor self, ConquestVictoryConditionsInfo cvcInfo)
|
||||
{
|
||||
info = cvcInfo;
|
||||
mo = self.Trait<MissionObjectives>();
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (self.Owner.WinState != WinState.Undefined || self.Owner.NonCombatant) return;
|
||||
|
||||
if (objectiveID < 0)
|
||||
objectiveID = mo.Add(self.Owner, "Destroy all opposition!");
|
||||
|
||||
if (!self.Owner.NonCombatant && self.Owner.HasNoRequiredUnits())
|
||||
mo.MarkFailed(self.Owner, objectiveID);
|
||||
|
||||
var others = self.World.Players.Where(p => !p.NonCombatant
|
||||
&& !p.IsAlliedWith(self.Owner));
|
||||
|
||||
if (!others.Any()) return;
|
||||
|
||||
if (others.All(p => p.WinState == WinState.Lost))
|
||||
mo.MarkCompleted(self.Owner, objectiveID);
|
||||
}
|
||||
|
||||
public void OnPlayerLost(Player player)
|
||||
{
|
||||
Game.Debug("{0} is defeated.", player.PlayerName);
|
||||
|
||||
foreach (var a in player.World.Actors.Where(a => a.Owner == player))
|
||||
a.Kill(a);
|
||||
|
||||
if (player == player.World.LocalPlayer)
|
||||
{
|
||||
Game.RunAfterDelay(info.NotificationDelay, () =>
|
||||
{
|
||||
if (Game.IsCurrentWorld(player.World))
|
||||
Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Lose", player.Country.Race);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPlayerWon(Player player)
|
||||
{
|
||||
Game.Debug("{0} is victorious.", player.PlayerName);
|
||||
|
||||
if (player == player.World.LocalPlayer)
|
||||
Game.RunAfterDelay(info.NotificationDelay, () => Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Win", player.Country.Race));
|
||||
}
|
||||
|
||||
public void OnObjectiveAdded(Player player, int id) { }
|
||||
public void OnObjectiveCompleted(Player player, int id) { }
|
||||
public void OnObjectiveFailed(Player player, int id) { }
|
||||
}
|
||||
}
|
||||
262
OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs
Normal file
262
OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public enum ObjectiveType { Primary, Secondary }
|
||||
public enum ObjectiveState { Incomplete, Completed, Failed }
|
||||
|
||||
public class MissionObjective
|
||||
{
|
||||
public readonly ObjectiveType Type;
|
||||
public readonly string Description;
|
||||
public ObjectiveState State;
|
||||
|
||||
public MissionObjective(ObjectiveType type, string description)
|
||||
{
|
||||
Type = type;
|
||||
Description = description;
|
||||
State = ObjectiveState.Incomplete;
|
||||
}
|
||||
}
|
||||
|
||||
public class MissionObjectivesInfo : ITraitInfo
|
||||
{
|
||||
[Desc("Set this to true if multiple cooperative players have a distinct set of " +
|
||||
"objectives that each of them has to complete to win the game. This is mainly " +
|
||||
"useful for multiplayer coop missions. Do not use this for skirmish team games.")]
|
||||
public readonly bool Cooperative = false;
|
||||
|
||||
[Desc("If set to true, this setting causes the game to end immediately once the first " +
|
||||
"player (or team of cooperative players) fails or completes his objectives. If " +
|
||||
"set to false, players that fail their objectives will stick around and become observers.")]
|
||||
public readonly bool EarlyGameOver = false;
|
||||
|
||||
[Desc("Delay between the game over condition being met, and the game actually ending, in milliseconds.")]
|
||||
public readonly int GameOverDelay = 1500;
|
||||
|
||||
public object Create(ActorInitializer init) { return new MissionObjectives(init.world, this); }
|
||||
}
|
||||
|
||||
public class MissionObjectives : INotifyObjectivesUpdated, ISync, IResolveOrder
|
||||
{
|
||||
readonly MissionObjectivesInfo info;
|
||||
readonly List<MissionObjective> objectives = new List<MissionObjective>();
|
||||
public ReadOnlyList<MissionObjective> Objectives;
|
||||
|
||||
[Sync]
|
||||
public int ObjectivesHash { get { return Objectives.Aggregate(0, (code, objective) => code ^ Sync.hash(objective.State)); } }
|
||||
|
||||
// This property is used as a flag in 'Cooperative' games to mark that the player has completed all his objectives.
|
||||
// The player's WinState is only updated when his allies have all completed their objective as well.
|
||||
public WinState WinStateCooperative { get; private set; }
|
||||
|
||||
public MissionObjectives(World world, MissionObjectivesInfo moInfo)
|
||||
{
|
||||
info = moInfo;
|
||||
Objectives = new ReadOnlyList<MissionObjective>(objectives);
|
||||
|
||||
world.ObserveAfterWinOrLose = !info.EarlyGameOver;
|
||||
}
|
||||
|
||||
public int Add(Player player, string description, ObjectiveType type = ObjectiveType.Primary)
|
||||
{
|
||||
var newID = objectives.Count;
|
||||
|
||||
objectives.Insert(newID, new MissionObjective(type, description));
|
||||
|
||||
ObjectiveAdded(player);
|
||||
foreach (var inou in player.PlayerActor.TraitsImplementing<INotifyObjectivesUpdated>())
|
||||
inou.OnObjectiveAdded(player, newID);
|
||||
|
||||
return newID;
|
||||
}
|
||||
|
||||
public void MarkCompleted(Player player, int objectiveID)
|
||||
{
|
||||
if (objectiveID >= objectives.Count || objectives[objectiveID].State != ObjectiveState.Incomplete)
|
||||
return;
|
||||
|
||||
var inous = player.PlayerActor.TraitsImplementing<INotifyObjectivesUpdated>();
|
||||
|
||||
objectives[objectiveID].State = ObjectiveState.Completed;
|
||||
foreach (var inou in inous)
|
||||
inou.OnObjectiveCompleted(player, objectiveID);
|
||||
|
||||
if (objectives[objectiveID].Type == ObjectiveType.Primary)
|
||||
{
|
||||
var playerWon = objectives.Where(o => o.Type == ObjectiveType.Primary).All(o => o.State == ObjectiveState.Completed);
|
||||
|
||||
if (playerWon)
|
||||
{
|
||||
foreach (var inou in inous)
|
||||
inou.OnPlayerWon(player);
|
||||
|
||||
CheckIfGameIsOver(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkFailed(Player player, int objectiveID)
|
||||
{
|
||||
if (objectiveID >= objectives.Count || objectives[objectiveID].State == ObjectiveState.Failed)
|
||||
return;
|
||||
|
||||
var inous = player.PlayerActor.TraitsImplementing<INotifyObjectivesUpdated>();
|
||||
|
||||
objectives[objectiveID].State = ObjectiveState.Failed;
|
||||
foreach (var inou in inous)
|
||||
inou.OnObjectiveFailed(player, objectiveID);
|
||||
|
||||
if (objectives[objectiveID].Type == ObjectiveType.Primary)
|
||||
{
|
||||
var playerLost = objectives.Where(o => o.Type == ObjectiveType.Primary).Any(o => o.State == ObjectiveState.Failed);
|
||||
|
||||
if (playerLost)
|
||||
{
|
||||
foreach (var inou in inous)
|
||||
inou.OnPlayerLost(player);
|
||||
|
||||
CheckIfGameIsOver(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckIfGameIsOver(Player player)
|
||||
{
|
||||
var players = player.World.Players.Where(p => !p.NonCombatant);
|
||||
|
||||
var gameOver = players.All(p => p.WinState != WinState.Undefined || !p.HasObjectives);
|
||||
if (gameOver)
|
||||
Game.RunAfterDelay(info.GameOverDelay, () =>
|
||||
{
|
||||
player.World.EndGame();
|
||||
player.World.SetPauseState(true);
|
||||
player.World.PauseStateLocked = true;
|
||||
});
|
||||
}
|
||||
|
||||
public void OnPlayerWon(Player player)
|
||||
{
|
||||
var players = player.World.Players.Where(p => !p.NonCombatant);
|
||||
var enemies = players.Where(p => !p.IsAlliedWith(player));
|
||||
|
||||
if (info.Cooperative)
|
||||
{
|
||||
WinStateCooperative = WinState.Won;
|
||||
var allies = players.Where(p => p.IsAlliedWith(player));
|
||||
|
||||
if (allies.All(p => p.PlayerActor.Trait<MissionObjectives>().WinStateCooperative == WinState.Won))
|
||||
{
|
||||
foreach (var p in allies)
|
||||
{
|
||||
p.WinState = WinState.Won;
|
||||
p.World.OnPlayerWinStateChanged(p);
|
||||
}
|
||||
|
||||
if (info.EarlyGameOver)
|
||||
foreach (var p in enemies)
|
||||
p.PlayerActor.Trait<MissionObjectives>().ForceDefeat(p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
player.WinState = WinState.Won;
|
||||
player.World.OnPlayerWinStateChanged(player);
|
||||
|
||||
if (info.EarlyGameOver)
|
||||
foreach (var p in enemies)
|
||||
p.PlayerActor.Trait<MissionObjectives>().ForceDefeat(p);
|
||||
}
|
||||
|
||||
CheckIfGameIsOver(player);
|
||||
}
|
||||
|
||||
public void OnPlayerLost(Player player)
|
||||
{
|
||||
var players = player.World.Players.Where(p => !p.NonCombatant);
|
||||
var enemies = players.Where(p => !p.IsAlliedWith(player));
|
||||
|
||||
if (info.Cooperative)
|
||||
{
|
||||
WinStateCooperative = WinState.Lost;
|
||||
var allies = players.Where(p => p.IsAlliedWith(player));
|
||||
|
||||
if (allies.Any(p => p.PlayerActor.Trait<MissionObjectives>().WinStateCooperative == WinState.Lost))
|
||||
{
|
||||
foreach (var p in allies)
|
||||
{
|
||||
p.WinState = WinState.Lost;
|
||||
p.World.OnPlayerWinStateChanged(p);
|
||||
}
|
||||
|
||||
if (info.EarlyGameOver)
|
||||
foreach (var p in enemies)
|
||||
p.PlayerActor.Trait<MissionObjectives>().ForceDefeat(p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
player.WinState = WinState.Lost;
|
||||
player.World.OnPlayerWinStateChanged(player);
|
||||
|
||||
if (info.EarlyGameOver)
|
||||
foreach (var p in enemies)
|
||||
{
|
||||
p.WinState = WinState.Won;
|
||||
p.World.OnPlayerWinStateChanged(p);
|
||||
}
|
||||
}
|
||||
|
||||
CheckIfGameIsOver(player);
|
||||
}
|
||||
|
||||
public void ForceDefeat(Player player)
|
||||
{
|
||||
for (var id = 0; id < Objectives.Count; id++)
|
||||
if (Objectives[id].State == ObjectiveState.Incomplete)
|
||||
MarkFailed(player, id);
|
||||
}
|
||||
|
||||
public event Action<Player> ObjectiveAdded = player => { player.HasObjectives = true; };
|
||||
|
||||
public void OnObjectiveAdded(Player player, int id) { }
|
||||
public void OnObjectiveCompleted(Player player, int id) { }
|
||||
public void OnObjectiveFailed(Player player, int id) { }
|
||||
|
||||
public void ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
if (order.OrderString == "Surrender")
|
||||
ForceDefeat(self.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
[Desc("Provides game mode progress information for players.",
|
||||
"Goes on WorldActor - observers don't have a player it can live on.",
|
||||
"Current options for PanelName are 'SKIRMISH_STATS' and 'MISSION_OBJECTIVES'.")]
|
||||
public class ObjectivesPanelInfo : ITraitInfo
|
||||
{
|
||||
public string PanelName = null;
|
||||
public object Create(ActorInitializer init) { return new ObjectivesPanel(this); }
|
||||
}
|
||||
|
||||
public class ObjectivesPanel : IObjectivesPanel
|
||||
{
|
||||
ObjectivesPanelInfo info;
|
||||
public ObjectivesPanel(ObjectivesPanelInfo info) { this.info = info; }
|
||||
public string PanelName { get { return info.PanelName; } }
|
||||
}
|
||||
}
|
||||
129
OpenRA.Mods.Common/Traits/Player/StrategicVictoryConditions.cs
Normal file
129
OpenRA.Mods.Common/Traits/Player/StrategicVictoryConditions.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Used to mark a place that needs to be in possession for StrategicVictoryConditions.")]
|
||||
public class StrategicPointInfo : TraitInfo<StrategicPoint> { }
|
||||
public class StrategicPoint { }
|
||||
|
||||
[Desc("Allows King of the Hill (KotH) style gameplay.")]
|
||||
public class StrategicVictoryConditionsInfo : ITraitInfo, Requires<MissionObjectivesInfo>
|
||||
{
|
||||
[Desc("Amount of time (in game ticks) that the player has to hold all the strategic points.", "Defaults to 5 minutes.")]
|
||||
public readonly int TicksToHold = 25 * 60 * 5;
|
||||
|
||||
[Desc("Should the timer reset when the player loses hold of a strategic point.")]
|
||||
public readonly bool ResetOnHoldLost = true;
|
||||
|
||||
[Desc("Percentage of all strategic points the player has to hold to win.")]
|
||||
public readonly float RatioRequired = 0.5f;
|
||||
|
||||
[Desc("Delay for the end game notification in milliseconds.")]
|
||||
public int NotificationDelay = 1500;
|
||||
|
||||
public object Create(ActorInitializer init) { return new StrategicVictoryConditions(init.self, this); }
|
||||
}
|
||||
|
||||
public class StrategicVictoryConditions : ITick, ISync, INotifyObjectivesUpdated
|
||||
{
|
||||
readonly StrategicVictoryConditionsInfo info;
|
||||
|
||||
[Sync] public int TicksLeft;
|
||||
readonly Player player;
|
||||
readonly MissionObjectives mo;
|
||||
int objectiveID = -1;
|
||||
|
||||
public StrategicVictoryConditions(Actor self, StrategicVictoryConditionsInfo svcInfo)
|
||||
{
|
||||
info = svcInfo;
|
||||
TicksLeft = info.TicksToHold;
|
||||
player = self.Owner;
|
||||
mo = self.Trait<MissionObjectives>();
|
||||
}
|
||||
|
||||
public IEnumerable<TraitPair<StrategicPoint>> AllPoints
|
||||
{
|
||||
get { return player.World.ActorsWithTrait<StrategicPoint>(); }
|
||||
}
|
||||
|
||||
public int Total { get { return AllPoints.Count(); } }
|
||||
int Owned { get { return AllPoints.Count(a => WorldUtils.AreMutualAllies(player, a.Actor.Owner)); } }
|
||||
|
||||
public bool Holding { get { return Owned >= info.RatioRequired * Total; } }
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (player.WinState != WinState.Undefined || player.NonCombatant) return;
|
||||
|
||||
if (objectiveID < 0)
|
||||
objectiveID = mo.Add(player, "Hold all the strategic positions for a specified time!");
|
||||
|
||||
if (!self.Owner.NonCombatant && self.Owner.HasNoRequiredUnits())
|
||||
mo.MarkFailed(self.Owner, objectiveID);
|
||||
|
||||
var others = self.World.Players.Where(p => !p.NonCombatant
|
||||
&& !p.IsAlliedWith(self.Owner));
|
||||
|
||||
if (others.All(p => p.WinState == WinState.Lost))
|
||||
mo.MarkCompleted(player, objectiveID);
|
||||
|
||||
if (others.Any(p => p.WinState == WinState.Won))
|
||||
mo.MarkFailed(player, objectiveID);
|
||||
|
||||
// See if any of the conditions are met to increase the count
|
||||
if (Total > 0)
|
||||
{
|
||||
if (Holding)
|
||||
{
|
||||
// Hah! We met ths critical owned condition
|
||||
if (--TicksLeft == 0)
|
||||
mo.MarkCompleted(player, objectiveID);
|
||||
}
|
||||
else if (TicksLeft != 0)
|
||||
if (info.ResetOnHoldLost)
|
||||
TicksLeft = info.TicksToHold; // Reset the time hold
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPlayerLost(Player player)
|
||||
{
|
||||
Game.Debug("{0} is defeated.", player.PlayerName);
|
||||
|
||||
foreach (var a in player.World.Actors.Where(a => a.Owner == player))
|
||||
a.Kill(a);
|
||||
|
||||
if (player == player.World.LocalPlayer)
|
||||
{
|
||||
Game.RunAfterDelay(info.NotificationDelay, () =>
|
||||
{
|
||||
if (Game.IsCurrentWorld(player.World))
|
||||
Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Lose", player.Country.Race);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPlayerWon(Player player)
|
||||
{
|
||||
Game.Debug("{0} is victorious.", player.PlayerName);
|
||||
|
||||
if (player == player.World.LocalPlayer)
|
||||
Game.RunAfterDelay(info.NotificationDelay, () => Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Win", player.Country.Race));
|
||||
}
|
||||
|
||||
public void OnObjectiveAdded(Player player, int id) { }
|
||||
public void OnObjectiveCompleted(Player player, int id) { }
|
||||
public void OnObjectiveFailed(Player player, int id) { }
|
||||
}
|
||||
}
|
||||
106
OpenRA.Mods.Common/Widgets/StrategicProgressWidget.cs
Normal file
106
OpenRA.Mods.Common/Widgets/StrategicProgressWidget.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class StrategicProgressWidget : Widget
|
||||
{
|
||||
readonly World world;
|
||||
bool initialised = false;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public StrategicProgressWidget(World world)
|
||||
{
|
||||
IsVisible = () => true;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
if (!initialised)
|
||||
Init();
|
||||
|
||||
if (!IsVisible()) return;
|
||||
|
||||
var rb = RenderBounds;
|
||||
var offset = int2.Zero;
|
||||
|
||||
var svc = world.Players.Select(p => p.PlayerActor.TraitOrDefault<StrategicVictoryConditions>()).FirstOrDefault();
|
||||
|
||||
var totalWidth = svc.Total * 32;
|
||||
var curX = -totalWidth / 2;
|
||||
|
||||
foreach (var a in svc.AllPoints)
|
||||
{
|
||||
WidgetUtils.DrawRGBA(ChromeProvider.GetImage("strategic", "critical_unowned"), offset + new float2(rb.Left + curX, rb.Top));
|
||||
|
||||
if (world.LocalPlayer != null && WorldUtils.AreMutualAllies(a.Actor.Owner, world.LocalPlayer))
|
||||
WidgetUtils.DrawRGBA(ChromeProvider.GetImage("strategic", "player_owned"), offset + new float2(rb.Left + curX, rb.Top));
|
||||
else if (!a.Actor.Owner.NonCombatant)
|
||||
WidgetUtils.DrawRGBA(ChromeProvider.GetImage("strategic", "enemy_owned"), offset + new float2(rb.Left + curX, rb.Top));
|
||||
|
||||
curX += 32;
|
||||
}
|
||||
|
||||
offset += new int2(0, 32);
|
||||
|
||||
if (world.LocalPlayer == null) return;
|
||||
var pendingWinner = FindFirstWinningPlayer(world);
|
||||
if (pendingWinner == null) return;
|
||||
var winnerSvc = pendingWinner.PlayerActor.Trait<StrategicVictoryConditions>();
|
||||
|
||||
var isVictory = pendingWinner == world.LocalPlayer || !WorldUtils.AreMutualAllies(pendingWinner, world.LocalPlayer);
|
||||
var tc = "Strategic {0} in {1}".F(
|
||||
isVictory ? "victory" : "defeat",
|
||||
WidgetUtils.FormatTime(winnerSvc.TicksLeft));
|
||||
|
||||
var font = Game.Renderer.Fonts["Bold"];
|
||||
|
||||
var size = font.Measure(tc);
|
||||
font.DrawTextWithContrast(tc, offset + new float2(rb.Left - size.X / 2 + 1, rb.Top + 1), Color.White, Color.Black, 1);
|
||||
offset += new int2(0, size.Y + 1);
|
||||
}
|
||||
|
||||
public Player FindFirstWinningPlayer(World world)
|
||||
{
|
||||
// loop through all players, see who is 'winning' and get the one with the shortest 'time to win'
|
||||
var shortest = int.MaxValue;
|
||||
Player shortestPlayer = null;
|
||||
|
||||
foreach (var p in world.Players.Where(p => !p.NonCombatant))
|
||||
{
|
||||
var svc = p.PlayerActor.Trait<StrategicVictoryConditions>();
|
||||
|
||||
if (svc.Holding && svc.TicksLeft > 0 && svc.TicksLeft < shortest)
|
||||
{
|
||||
shortest = svc.TicksLeft;
|
||||
shortestPlayer = p;
|
||||
}
|
||||
}
|
||||
|
||||
return shortestPlayer;
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
var visible = world.ActorsWithTrait<StrategicVictoryConditions>().Any() &&
|
||||
world.ActorsWithTrait<StrategicPoint>().Any();
|
||||
|
||||
IsVisible = () => visible;
|
||||
initialised = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user