Move ShroudPalette, ShroudRenderer and StartGameNotification to Common
Move World into Traits folder
This commit is contained in:
97
OpenRA.Mods.Common/Traits/World/CreateMPPlayers.cs
Normal file
97
OpenRA.Mods.Common/Traits/World/CreateMPPlayers.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
#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.Network;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
[Desc("Attach this to the world actor.")]
|
||||
public class CreateMPPlayersInfo : TraitInfo<CreateMPPlayers> { }
|
||||
|
||||
public class CreateMPPlayers : ICreatePlayers
|
||||
{
|
||||
public void CreatePlayers(World w)
|
||||
{
|
||||
// create the unplayable map players -- neutral, shellmap, scripted, etc.
|
||||
foreach (var kv in w.Map.Players.Where(p => !p.Value.Playable))
|
||||
{
|
||||
var player = new Player(w, null, null, kv.Value);
|
||||
w.AddPlayer(player);
|
||||
if (kv.Value.OwnsWorld)
|
||||
w.WorldActor.Owner = player;
|
||||
}
|
||||
|
||||
// create the players which are bound through slots.
|
||||
foreach (var kv in w.LobbyInfo.Slots)
|
||||
{
|
||||
var client = w.LobbyInfo.ClientInSlot(kv.Key);
|
||||
if (client == null)
|
||||
continue;
|
||||
|
||||
var player = new Player(w, client, kv.Value, w.Map.Players[kv.Value.PlayerReference]);
|
||||
w.AddPlayer(player);
|
||||
|
||||
if (client.Index == Game.LocalClientId)
|
||||
w.SetLocalPlayer(player.InternalName);
|
||||
}
|
||||
|
||||
// create a player that is allied with everyone for shared observer shroud
|
||||
w.AddPlayer(new Player(w, null, null, new PlayerReference
|
||||
{
|
||||
Name = "Everyone",
|
||||
NonCombatant = true,
|
||||
Spectating = true,
|
||||
Allies = w.Players.Where(p => !p.NonCombatant && p.Playable).Select(p => p.InternalName).ToArray()
|
||||
}));
|
||||
|
||||
foreach (var p in w.Players)
|
||||
foreach (var q in w.Players)
|
||||
if (!p.Stances.ContainsKey(q))
|
||||
p.Stances[q] = ChooseInitialStance(p, q);
|
||||
}
|
||||
|
||||
static Stance ChooseInitialStance(Player p, Player q)
|
||||
{
|
||||
if (p == q)
|
||||
return Stance.Ally;
|
||||
|
||||
if (q.Spectating && !p.NonCombatant && p.Playable)
|
||||
return Stance.Ally;
|
||||
|
||||
// Stances set via PlayerReference
|
||||
if (p.PlayerReference.Allies.Contains(q.InternalName))
|
||||
return Stance.Ally;
|
||||
if (p.PlayerReference.Enemies.Contains(q.InternalName))
|
||||
return Stance.Enemy;
|
||||
|
||||
// HACK: Map players share a ClientID with the host, so would
|
||||
// otherwise take the host's team stance instead of being neutral
|
||||
if (p.PlayerReference.Playable && q.PlayerReference.Playable)
|
||||
{
|
||||
// Stances set via lobby teams
|
||||
var pc = GetClientForPlayer(p);
|
||||
var qc = GetClientForPlayer(q);
|
||||
if (pc != null && qc != null)
|
||||
return pc.Team != 0 && pc.Team == qc.Team
|
||||
? Stance.Ally : Stance.Enemy;
|
||||
}
|
||||
|
||||
// Otherwise, default to neutral
|
||||
return Stance.Neutral;
|
||||
}
|
||||
|
||||
static Session.Client GetClientForPlayer(Player p)
|
||||
{
|
||||
return p.World.LobbyInfo.ClientWithIndex(p.ClientIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
89
OpenRA.Mods.Common/Traits/World/MPStartLocations.cs
Normal file
89
OpenRA.Mods.Common/Traits/World/MPStartLocations.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
#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.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
public class MPStartLocationsInfo : TraitInfo<MPStartLocations>
|
||||
{
|
||||
public readonly WRange InitialExploreRange = WRange.FromCells(5);
|
||||
}
|
||||
|
||||
public class MPStartLocations : IWorldLoaded
|
||||
{
|
||||
public Dictionary<Player, CPos> Start = new Dictionary<Player, CPos>();
|
||||
|
||||
public void WorldLoaded(World world, WorldRenderer wr)
|
||||
{
|
||||
var spawns = world.Map.GetSpawnPoints().ToList();
|
||||
var taken = world.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0 && c.Slot != null)
|
||||
.Select(c => spawns[c.SpawnPoint-1]).ToList();
|
||||
var available = spawns.Except(taken).ToList();
|
||||
|
||||
// Set spawn
|
||||
foreach (var kv in world.LobbyInfo.Slots)
|
||||
{
|
||||
var player = FindPlayerInSlot(world, kv.Key);
|
||||
if (player == null) continue;
|
||||
|
||||
var client = world.LobbyInfo.ClientInSlot(kv.Key);
|
||||
var spid = (client == null || client.SpawnPoint == 0)
|
||||
? ChooseSpawnPoint(world, available, taken)
|
||||
: spawns[client.SpawnPoint-1];
|
||||
|
||||
Start.Add(player, spid);
|
||||
|
||||
player.SpawnPoint = (client == null || client.SpawnPoint == 0)
|
||||
? spawns.IndexOf(spid) + 1
|
||||
: client.SpawnPoint;
|
||||
}
|
||||
|
||||
// Explore allied shroud
|
||||
var explore = world.WorldActor.Info.Traits.Get<MPStartLocationsInfo>().InitialExploreRange;
|
||||
foreach (var p in Start.Keys)
|
||||
foreach (var q in world.Players)
|
||||
if (p.IsAlliedWith(q))
|
||||
q.Shroud.Explore(world, Start[p], explore);
|
||||
|
||||
// Set viewport
|
||||
if (world.LocalPlayer != null && Start.ContainsKey(world.LocalPlayer))
|
||||
wr.Viewport.Center(world.Map.CenterOfCell(Start[world.LocalPlayer]));
|
||||
}
|
||||
|
||||
static Player FindPlayerInSlot(World world, string pr)
|
||||
{
|
||||
return world.Players.FirstOrDefault(p => p.PlayerReference.Name == pr);
|
||||
}
|
||||
|
||||
static CPos ChooseSpawnPoint(World world, List<CPos> available, List<CPos> taken)
|
||||
{
|
||||
if (available.Count == 0)
|
||||
throw new InvalidOperationException("No free spawnpoint.");
|
||||
|
||||
var n = taken.Count == 0
|
||||
? world.SharedRandom.Next(available.Count)
|
||||
: available // pick the most distant spawnpoint from everyone else
|
||||
.Select((k, i) => Pair.New(k, i))
|
||||
.MaxBy(a => taken.Sum(t => (t - a.First).LengthSquared)).Second;
|
||||
|
||||
var sp = available[n];
|
||||
available.RemoveAt(n);
|
||||
taken.Add(sp);
|
||||
return sp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
OpenRA.Mods.Common/Traits/World/MPStartUnits.cs
Normal file
41
OpenRA.Mods.Common/Traits/World/MPStartUnits.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
#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
|
||||
{
|
||||
[Desc("Used by SpawnMPUnits. Attach these to the world actor. You can have multiple variants by adding @suffixes.")]
|
||||
public class MPStartUnitsInfo : TraitInfo<MPStartUnits>
|
||||
{
|
||||
[Desc("Internal class ID.")]
|
||||
public readonly string Class = "none";
|
||||
|
||||
[Desc("Exposed via the UI to the player.")]
|
||||
public readonly string ClassName = "Unlabeled";
|
||||
|
||||
[Desc("Only available when selecting this faction.", "Leave empty for no restrictions.")]
|
||||
public readonly string[] Races = { };
|
||||
|
||||
[Desc("The mobile construction vehicle.")]
|
||||
public readonly string BaseActor = null;
|
||||
|
||||
[Desc("A group of units ready to defend or scout.")]
|
||||
public readonly string[] SupportActors = { };
|
||||
|
||||
[Desc("Inner radius for spawning support actors")]
|
||||
public readonly int InnerSupportRadius = 2;
|
||||
|
||||
[Desc("Outer radius for spawning support actors")]
|
||||
public readonly int OuterSupportRadius = 4;
|
||||
}
|
||||
|
||||
public class MPStartUnits { }
|
||||
}
|
||||
51
OpenRA.Mods.Common/Traits/World/PlayMusicOnMapLoad.cs
Normal file
51
OpenRA.Mods.Common/Traits/World/PlayMusicOnMapLoad.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
#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 OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
class PlayMusicOnMapLoadInfo : ITraitInfo
|
||||
{
|
||||
public readonly string Music = null;
|
||||
public readonly bool Loop = false;
|
||||
|
||||
public object Create(ActorInitializer init) { return new PlayMusicOnMapLoad(init.world, this); }
|
||||
}
|
||||
|
||||
class PlayMusicOnMapLoad : IWorldLoaded
|
||||
{
|
||||
readonly PlayMusicOnMapLoadInfo info;
|
||||
readonly World world;
|
||||
|
||||
public PlayMusicOnMapLoad(World world, PlayMusicOnMapLoadInfo info)
|
||||
{
|
||||
this.world = world;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public void WorldLoaded(World world, WorldRenderer wr)
|
||||
{
|
||||
PlayMusic();
|
||||
}
|
||||
|
||||
void PlayMusic()
|
||||
{
|
||||
var onComplete = info.Loop ? (Action)PlayMusic : () => {};
|
||||
|
||||
if (Game.Settings.Sound.MapMusic &&
|
||||
world.Map.Rules.Music.ContainsKey(info.Music))
|
||||
Sound.PlayMusicThen(world.Map.Rules.Music[info.Music], onComplete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
113
OpenRA.Mods.Common/Traits/World/RadarPings.cs
Normal file
113
OpenRA.Mods.Common/Traits/World/RadarPings.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
#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.Drawing;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
public class RadarPingsInfo : ITraitInfo
|
||||
{
|
||||
public readonly int FromRadius = 200;
|
||||
public readonly int ToRadius = 15;
|
||||
public readonly int ShrinkSpeed = 4;
|
||||
public readonly float RotationSpeed = 0.12f;
|
||||
|
||||
public object Create(ActorInitializer init) { return new RadarPings(this); }
|
||||
}
|
||||
|
||||
public class RadarPings : ITick
|
||||
{
|
||||
public readonly List<RadarPing> Pings = new List<RadarPing>();
|
||||
readonly RadarPingsInfo info;
|
||||
|
||||
public WPos? LastPingPosition;
|
||||
|
||||
public RadarPings(RadarPingsInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
foreach (var ping in Pings.ToArray())
|
||||
if (!ping.Tick())
|
||||
Pings.Remove(ping);
|
||||
}
|
||||
|
||||
public RadarPing Add(Func<bool> isVisible, WPos position, Color color, int duration)
|
||||
{
|
||||
var ping = new RadarPing(isVisible, position, color, duration,
|
||||
info.FromRadius, info.ToRadius, info.ShrinkSpeed, info.RotationSpeed);
|
||||
|
||||
if (ping.IsVisible())
|
||||
LastPingPosition = ping.Position;
|
||||
|
||||
Pings.Add(ping);
|
||||
|
||||
return ping;
|
||||
}
|
||||
|
||||
public void Remove(RadarPing ping)
|
||||
{
|
||||
Pings.Remove(ping);
|
||||
}
|
||||
}
|
||||
|
||||
public class RadarPing
|
||||
{
|
||||
public Func<bool> IsVisible;
|
||||
public WPos Position;
|
||||
public Color Color;
|
||||
public int Duration;
|
||||
public int FromRadius;
|
||||
public int ToRadius;
|
||||
public int ShrinkSpeed;
|
||||
public float RotationSpeed;
|
||||
|
||||
int radius;
|
||||
float angle;
|
||||
int tick;
|
||||
|
||||
public RadarPing(Func<bool> isVisible, WPos position, Color color, int duration,
|
||||
int fromRadius, int toRadius, int shrinkSpeed, float rotationSpeed)
|
||||
{
|
||||
IsVisible = isVisible;
|
||||
Position = position;
|
||||
Color = color;
|
||||
Duration = duration;
|
||||
FromRadius = fromRadius;
|
||||
ToRadius = toRadius;
|
||||
ShrinkSpeed = shrinkSpeed;
|
||||
RotationSpeed = rotationSpeed;
|
||||
|
||||
radius = fromRadius;
|
||||
}
|
||||
|
||||
public bool Tick()
|
||||
{
|
||||
if (++tick == Duration)
|
||||
return false;
|
||||
|
||||
radius = Math.Max(radius - ShrinkSpeed, ToRadius);
|
||||
angle -= RotationSpeed;
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerable<float2> Points(float2 center)
|
||||
{
|
||||
yield return center + radius * float2.FromAngle(angle);
|
||||
yield return center + radius * float2.FromAngle((float)(angle + 2 * Math.PI / 3));
|
||||
yield return center + radius * float2.FromAngle((float)(angle + 4 * Math.PI / 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
20
OpenRA.Mods.Common/Traits/World/ResourceClaim.cs
Normal file
20
OpenRA.Mods.Common/Traits/World/ResourceClaim.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
#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
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
public sealed class ResourceClaim
|
||||
{
|
||||
public readonly Actor Claimer;
|
||||
public CPos Cell;
|
||||
|
||||
public ResourceClaim(Actor claimer, CPos cell) { Claimer = claimer; Cell = cell; }
|
||||
}
|
||||
}
|
||||
128
OpenRA.Mods.Common/Traits/World/ResourceClaimLayer.cs
Normal file
128
OpenRA.Mods.Common/Traits/World/ResourceClaimLayer.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
#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 OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
[Desc("Allows harvesters to coordinate their operations. Attach this to the world actor.")]
|
||||
public sealed class ResourceClaimLayerInfo : TraitInfo<ResourceClaimLayer> { }
|
||||
|
||||
public sealed class ResourceClaimLayer : IWorldLoaded
|
||||
{
|
||||
Dictionary<CPos, ResourceClaim> claimByCell;
|
||||
Dictionary<Actor, ResourceClaim> claimByActor;
|
||||
|
||||
private void MakeClaim(Actor claimer, CPos cell)
|
||||
{
|
||||
UnclaimByActor(claimer);
|
||||
UnclaimByCell(cell, claimer);
|
||||
claimByActor[claimer] = claimByCell[cell] = new ResourceClaim(claimer, cell);
|
||||
}
|
||||
|
||||
private void Unclaim(ResourceClaim claim, Actor claimer)
|
||||
{
|
||||
if (claimByActor.Remove(claim.Claimer) & claimByCell.Remove(claim.Cell))
|
||||
{
|
||||
if (claim.Claimer.Destroyed) return;
|
||||
if (!claim.Claimer.IsInWorld) return;
|
||||
if (claim.Claimer.IsDead()) return;
|
||||
|
||||
claim.Claimer.Trait<INotifyResourceClaimLost>().OnNotifyResourceClaimLost(claim.Claimer, claim, claimer);
|
||||
}
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w, WorldRenderer wr)
|
||||
{
|
||||
// NOTE(jsd): 32 seems a sane default initial capacity for the total # of harvesters in a game. Purely a guesstimate.
|
||||
claimByCell = new Dictionary<CPos, ResourceClaim>(32);
|
||||
claimByActor = new Dictionary<Actor, ResourceClaim>(32);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to claim the resource at the cell for the given actor.
|
||||
/// </summary>
|
||||
/// <param name="claimer"></param>
|
||||
/// <param name="cell"></param>
|
||||
/// <returns></returns>
|
||||
public bool ClaimResource(Actor claimer, CPos cell)
|
||||
{
|
||||
// Has anyone else claimed this point?
|
||||
ResourceClaim claim;
|
||||
if (claimByCell.TryGetValue(cell, out claim))
|
||||
{
|
||||
// Same claimer:
|
||||
if (claim.Claimer == claimer) return true;
|
||||
|
||||
// This is to prevent in-fighting amongst friendly harvesters:
|
||||
if (claimer.Owner == claim.Claimer.Owner) return false;
|
||||
if (claimer.Owner.Stances[claim.Claimer.Owner] == Stance.Ally) return false;
|
||||
|
||||
// If an enemy/neutral claimed this, don't respect that claim:
|
||||
}
|
||||
|
||||
// Either nobody else claims this point or an enemy/neutral claims it:
|
||||
MakeClaim(claimer, cell);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release the last resource claim made on this cell.
|
||||
/// </summary>
|
||||
/// <param name="cell"></param>
|
||||
public void UnclaimByCell(CPos cell, Actor claimer)
|
||||
{
|
||||
ResourceClaim claim;
|
||||
if (claimByCell.TryGetValue(cell, out claim))
|
||||
Unclaim(claim, claimer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release the last resource claim made by this actor.
|
||||
/// </summary>
|
||||
/// <param name="claimer"></param>
|
||||
public void UnclaimByActor(Actor claimer)
|
||||
{
|
||||
ResourceClaim claim;
|
||||
if (claimByActor.TryGetValue(claimer, out claim))
|
||||
Unclaim(claim, claimer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the cell location <paramref name="cell"/> claimed for harvesting by any other actor?
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
/// <param name="cell"></param>
|
||||
/// <returns>true if already claimed by an ally that isn't <paramref name="self"/>; false otherwise.</returns>
|
||||
public bool IsClaimedByAnyoneElse(Actor self, CPos cell, out ResourceClaim claim)
|
||||
{
|
||||
if (claimByCell.TryGetValue(cell, out claim))
|
||||
{
|
||||
// Same claimer:
|
||||
if (claim.Claimer == self) return false;
|
||||
|
||||
// This is to prevent in-fighting amongst friendly harvesters:
|
||||
if (self.Owner == claim.Claimer.Owner) return true;
|
||||
if (self.Owner.Stances[claim.Claimer.Owner] == Stance.Ally) return true;
|
||||
|
||||
// If an enemy/neutral claimed this, don't respect that claim and fall through:
|
||||
}
|
||||
else
|
||||
{
|
||||
// No claim.
|
||||
claim = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
OpenRA.Mods.Common/Traits/World/ShroudPalette.cs
Normal file
60
OpenRA.Mods.Common/Traits/World/ShroudPalette.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
#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.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
[Desc("Adds the hard-coded shroud palette to the game")]
|
||||
class ShroudPaletteInfo : ITraitInfo
|
||||
{
|
||||
[Desc("Internal palette name")]
|
||||
public readonly string Name = "shroud";
|
||||
|
||||
[Desc("Palette type")]
|
||||
public readonly bool Fog = false;
|
||||
|
||||
public object Create(ActorInitializer init) { return new ShroudPalette(this); }
|
||||
}
|
||||
|
||||
class ShroudPalette : ILoadsPalettes
|
||||
{
|
||||
readonly ShroudPaletteInfo info;
|
||||
|
||||
public ShroudPalette(ShroudPaletteInfo info) { this.info = info; }
|
||||
|
||||
public void LoadPalettes(WorldRenderer wr)
|
||||
{
|
||||
var c = info.Fog ? Fog : Shroud;
|
||||
wr.AddPalette(info.Name, new ImmutablePalette(Enumerable.Range(0, Palette.Size).Select(i => (uint)c[i % 8].ToArgb())));
|
||||
}
|
||||
|
||||
static Color[] Fog = new[] {
|
||||
Color.Transparent, Color.Green,
|
||||
Color.Blue, Color.Yellow,
|
||||
Color.FromArgb(128,0,0,0),
|
||||
Color.FromArgb(96,0,0,0),
|
||||
Color.FromArgb(64,0,0,0),
|
||||
Color.FromArgb(32,0,0,0)
|
||||
};
|
||||
|
||||
static Color[] Shroud = new[] {
|
||||
Color.Transparent, Color.Green,
|
||||
Color.Blue, Color.Yellow,
|
||||
Color.Black,
|
||||
Color.FromArgb(160,0,0,0),
|
||||
Color.FromArgb(128,0,0,0),
|
||||
Color.FromArgb(64,0,0,0)
|
||||
};
|
||||
}
|
||||
}
|
||||
302
OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs
Normal file
302
OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs
Normal file
@@ -0,0 +1,302 @@
|
||||
#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 OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
public class ShroudRendererInfo : ITraitInfo
|
||||
{
|
||||
public readonly string Sequence = "shroud";
|
||||
public readonly string[] ShroudVariants = new[] { "shroud" };
|
||||
public readonly string[] FogVariants = new[] { "fog" };
|
||||
|
||||
public readonly string ShroudPalette = "shroud";
|
||||
public readonly string FogPalette = "fog";
|
||||
|
||||
[Desc("Bitfield of shroud directions for each frame. Lower four bits are",
|
||||
"corners clockwise from TL; upper four are edges clockwise from top")]
|
||||
public readonly int[] Index = new[] { 12, 9, 8, 3, 1, 6, 4, 2, 13, 11, 7, 14 };
|
||||
|
||||
[Desc("Use the upper four bits when calculating frame")]
|
||||
public readonly bool UseExtendedIndex = false;
|
||||
|
||||
[Desc("Override for source art that doesn't define a fully shrouded tile")]
|
||||
public readonly string OverrideFullShroud = null;
|
||||
public readonly int OverrideShroudIndex = 15;
|
||||
|
||||
[Desc("Override for source art that doesn't define a fully fogged tile")]
|
||||
public readonly string OverrideFullFog = null;
|
||||
public readonly int OverrideFogIndex = 15;
|
||||
|
||||
public readonly BlendMode ShroudBlend = BlendMode.Alpha;
|
||||
public object Create(ActorInitializer init) { return new ShroudRenderer(init.world, this); }
|
||||
}
|
||||
|
||||
public class ShroudRenderer : IRenderShroud, IWorldLoaded
|
||||
{
|
||||
[Flags]
|
||||
enum Edges : byte
|
||||
{
|
||||
None = 0,
|
||||
TopLeft = 0x01,
|
||||
TopRight = 0x02,
|
||||
BottomRight = 0x04,
|
||||
BottomLeft = 0x08,
|
||||
AllCorners = TopLeft | TopRight | BottomRight | BottomLeft,
|
||||
TopSide = 0x10,
|
||||
RightSide = 0x20,
|
||||
BottomSide = 0x40,
|
||||
LeftSide = 0x80,
|
||||
AllSides = TopSide | RightSide | BottomSide | LeftSide,
|
||||
Top = TopSide | TopLeft | TopRight,
|
||||
Right = RightSide | TopRight | BottomRight,
|
||||
Bottom = BottomSide | BottomRight | BottomLeft,
|
||||
Left = LeftSide | TopLeft | BottomLeft,
|
||||
All = Top | Right | Bottom | Left
|
||||
}
|
||||
|
||||
struct ShroudTile
|
||||
{
|
||||
public readonly float2 ScreenPosition;
|
||||
public readonly byte Variant;
|
||||
|
||||
public Sprite Fog;
|
||||
public Sprite Shroud;
|
||||
|
||||
public ShroudTile(float2 screenPosition, byte variant)
|
||||
{
|
||||
ScreenPosition = screenPosition;
|
||||
Variant = variant;
|
||||
|
||||
Fog = null;
|
||||
Shroud = null;
|
||||
}
|
||||
}
|
||||
|
||||
readonly ShroudRendererInfo info;
|
||||
readonly Sprite[] shroudSprites, fogSprites;
|
||||
readonly byte[] spriteMap;
|
||||
readonly CellLayer<ShroudTile> tiles;
|
||||
readonly byte variantStride;
|
||||
readonly Map map;
|
||||
readonly Edges notVisibleEdges;
|
||||
|
||||
bool clearedForNullShroud;
|
||||
int lastShroudHash;
|
||||
CellRegion updatedRegion;
|
||||
|
||||
PaletteReference fogPalette, shroudPalette;
|
||||
|
||||
public ShroudRenderer(World world, ShroudRendererInfo info)
|
||||
{
|
||||
if (info.ShroudVariants.Length != info.FogVariants.Length)
|
||||
throw new ArgumentException("ShroudRenderer must define the same number of shroud and fog variants!", "info");
|
||||
|
||||
if ((info.OverrideFullFog == null) ^ (info.OverrideFullShroud == null))
|
||||
throw new ArgumentException("ShroudRenderer cannot define overrides for only one of shroud or fog!", "info");
|
||||
|
||||
if (info.ShroudVariants.Length > byte.MaxValue)
|
||||
throw new ArgumentException("ShroudRenderer cannot define this many shroud and fog variants.", "info");
|
||||
|
||||
if (info.Index.Length >= byte.MaxValue)
|
||||
throw new ArgumentException("ShroudRenderer cannot define this many indexes for shroud directions.", "info");
|
||||
|
||||
this.info = info;
|
||||
map = world.Map;
|
||||
|
||||
tiles = new CellLayer<ShroudTile>(map);
|
||||
|
||||
// Load sprite variants
|
||||
var variantCount = info.ShroudVariants.Length;
|
||||
variantStride = (byte)(info.Index.Length + (info.OverrideFullShroud != null ? 1 : 0));
|
||||
shroudSprites = new Sprite[variantCount * variantStride];
|
||||
fogSprites = new Sprite[variantCount * variantStride];
|
||||
|
||||
for (var j = 0; j < variantCount; j++)
|
||||
{
|
||||
var shroud = map.SequenceProvider.GetSequence(info.Sequence, info.ShroudVariants[j]);
|
||||
var fog = map.SequenceProvider.GetSequence(info.Sequence, info.FogVariants[j]);
|
||||
for (var i = 0; i < info.Index.Length; i++)
|
||||
{
|
||||
shroudSprites[j * variantStride + i] = shroud.GetSprite(i);
|
||||
fogSprites[j * variantStride + i] = fog.GetSprite(i);
|
||||
}
|
||||
|
||||
if (info.OverrideFullShroud != null)
|
||||
{
|
||||
var i = (j + 1) * variantStride - 1;
|
||||
shroudSprites[i] = map.SequenceProvider.GetSequence(info.Sequence, info.OverrideFullShroud).GetSprite(0);
|
||||
fogSprites[i] = map.SequenceProvider.GetSequence(info.Sequence, info.OverrideFullFog).GetSprite(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Mapping of shrouded directions -> sprite index
|
||||
spriteMap = new byte[(byte)(info.UseExtendedIndex ? Edges.All : Edges.AllCorners) + 1];
|
||||
for (var i = 0; i < info.Index.Length; i++)
|
||||
spriteMap[info.Index[i]] = (byte)i;
|
||||
|
||||
if (info.OverrideFullShroud != null)
|
||||
spriteMap[info.OverrideShroudIndex] = (byte)(variantStride - 1);
|
||||
|
||||
notVisibleEdges = info.UseExtendedIndex ? Edges.AllSides : Edges.AllCorners;
|
||||
}
|
||||
|
||||
Edges GetEdges(CPos p, Func<CPos, bool> isVisible)
|
||||
{
|
||||
if (!isVisible(p))
|
||||
return notVisibleEdges;
|
||||
|
||||
// If a side is shrouded then we also count the corners
|
||||
var u = Edges.None;
|
||||
if (!isVisible(p + new CVec(0, -1))) u |= Edges.Top;
|
||||
if (!isVisible(p + new CVec(1, 0))) u |= Edges.Right;
|
||||
if (!isVisible(p + new CVec(0, 1))) u |= Edges.Bottom;
|
||||
if (!isVisible(p + new CVec(-1, 0))) u |= Edges.Left;
|
||||
|
||||
var ucorner = u & Edges.AllCorners;
|
||||
if (!isVisible(p + new CVec(-1, -1))) u |= Edges.TopLeft;
|
||||
if (!isVisible(p + new CVec(1, -1))) u |= Edges.TopRight;
|
||||
if (!isVisible(p + new CVec(1, 1))) u |= Edges.BottomRight;
|
||||
if (!isVisible(p + new CVec(-1, 1))) u |= Edges.BottomLeft;
|
||||
|
||||
// RA provides a set of frames for tiles with shrouded
|
||||
// corners but unshrouded edges. We want to detect this
|
||||
// situation without breaking the edge -> corner enabling
|
||||
// in other combinations. The XOR turns off the corner
|
||||
// bits that are enabled twice, which gives the behavior
|
||||
// we want here.
|
||||
return info.UseExtendedIndex ? u ^ ucorner : u & Edges.AllCorners;
|
||||
}
|
||||
|
||||
Edges GetObserverEdges(CPos p)
|
||||
{
|
||||
var u = Edges.None;
|
||||
if (!map.Contains(p + new CVec(0, -1))) u |= Edges.Top;
|
||||
if (!map.Contains(p + new CVec(1, 0))) u |= Edges.Right;
|
||||
if (!map.Contains(p + new CVec(0, 1))) u |= Edges.Bottom;
|
||||
if (!map.Contains(p + new CVec(-1, 0))) u |= Edges.Left;
|
||||
|
||||
var ucorner = u & Edges.AllCorners;
|
||||
if (!map.Contains(p + new CVec(-1, -1))) u |= Edges.TopLeft;
|
||||
if (!map.Contains(p + new CVec(1, -1))) u |= Edges.TopRight;
|
||||
if (!map.Contains(p + new CVec(1, 1))) u |= Edges.BottomRight;
|
||||
if (!map.Contains(p + new CVec(-1, 1))) u |= Edges.BottomLeft;
|
||||
|
||||
return info.UseExtendedIndex ? u ^ ucorner : u & Edges.AllCorners;
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w, WorldRenderer wr)
|
||||
{
|
||||
// Initialize tile cache
|
||||
// Adds a 1-cell border around the border to cover any sprites peeking outside the map
|
||||
foreach (var cell in CellRegion.Expand(w.Map.Cells, 1))
|
||||
{
|
||||
var screen = wr.ScreenPosition(w.Map.CenterOfCell(cell));
|
||||
var variant = (byte)Game.CosmeticRandom.Next(info.ShroudVariants.Length);
|
||||
tiles[cell] = new ShroudTile(screen, variant);
|
||||
|
||||
// Set the cells outside the border so they don't need to be touched again
|
||||
if (!map.Contains(cell))
|
||||
{
|
||||
var shroudTile = tiles[cell];
|
||||
shroudTile.Shroud = GetTile(shroudSprites, notVisibleEdges, variant);
|
||||
tiles[cell] = shroudTile;
|
||||
}
|
||||
}
|
||||
|
||||
fogPalette = wr.Palette(info.FogPalette);
|
||||
shroudPalette = wr.Palette(info.ShroudPalette);
|
||||
}
|
||||
|
||||
Sprite GetTile(Sprite[] sprites, Edges edges, int variant)
|
||||
{
|
||||
if (edges == Edges.None)
|
||||
return null;
|
||||
|
||||
return sprites[variant * variantStride + spriteMap[(byte)edges]];
|
||||
}
|
||||
|
||||
void Update(Shroud shroud, CellRegion region)
|
||||
{
|
||||
if (shroud != null)
|
||||
{
|
||||
// If the current shroud hasn't changed and we have already updated the specified area, we don't need to do anything.
|
||||
if (lastShroudHash == shroud.Hash && !clearedForNullShroud && updatedRegion != null && updatedRegion.Contains(region))
|
||||
return;
|
||||
|
||||
lastShroudHash = shroud.Hash;
|
||||
clearedForNullShroud = false;
|
||||
updatedRegion = region;
|
||||
UpdateShroud(shroud);
|
||||
}
|
||||
else if (!clearedForNullShroud)
|
||||
{
|
||||
// We need to clear any applied shroud.
|
||||
clearedForNullShroud = true;
|
||||
updatedRegion = new CellRegion(map.TileShape, new CPos(0, 0), new CPos(-1, -1));
|
||||
UpdateNullShroud();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateShroud(Shroud shroud)
|
||||
{
|
||||
var visibleUnderShroud = shroud.IsExploredTest(updatedRegion);
|
||||
var visibleUnderFog = shroud.IsVisibleTest(updatedRegion);
|
||||
foreach (var cell in updatedRegion)
|
||||
{
|
||||
var shrouded = GetEdges(cell, visibleUnderShroud);
|
||||
var fogged = GetEdges(cell, visibleUnderFog);
|
||||
var shroudTile = tiles[cell];
|
||||
var variant = shroudTile.Variant;
|
||||
shroudTile.Shroud = GetTile(shroudSprites, shrouded, variant);
|
||||
shroudTile.Fog = GetTile(fogSprites, fogged, variant);
|
||||
tiles[cell] = shroudTile;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateNullShroud()
|
||||
{
|
||||
foreach (var cell in map.Cells)
|
||||
{
|
||||
var edges = GetObserverEdges(cell);
|
||||
var shroudTile = tiles[cell];
|
||||
var variant = shroudTile.Variant;
|
||||
shroudTile.Shroud = GetTile(shroudSprites, edges, variant);
|
||||
shroudTile.Fog = GetTile(fogSprites, edges, variant);
|
||||
tiles[cell] = shroudTile;
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderShroud(WorldRenderer wr, Shroud shroud)
|
||||
{
|
||||
Update(shroud, wr.Viewport.VisibleCells);
|
||||
|
||||
foreach (var cell in CellRegion.Expand(wr.Viewport.VisibleCells, 1))
|
||||
{
|
||||
var t = tiles[cell];
|
||||
|
||||
if (t.Shroud != null)
|
||||
{
|
||||
var pos = t.ScreenPosition - 0.5f * t.Shroud.size;
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(t.Shroud, pos, shroudPalette);
|
||||
}
|
||||
|
||||
if (t.Fog != null)
|
||||
{
|
||||
var pos = t.ScreenPosition - 0.5f * t.Fog.size;
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(t.Fog, pos, fogPalette);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
145
OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs
Normal file
145
OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
#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.Graphics;
|
||||
using OpenRA.Mods.Common.Effects;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
[Desc("Attach this to the world actor.", "Order of the layers defines the Z sorting.")]
|
||||
public class SmudgeLayerInfo : ITraitInfo
|
||||
{
|
||||
public readonly string Type = "Scorch";
|
||||
|
||||
[Desc("Sprite sequence name")]
|
||||
public readonly string Sequence = "scorch";
|
||||
|
||||
public readonly int SmokePercentage = 25;
|
||||
|
||||
[Desc("Sprite sequence name")]
|
||||
public readonly string SmokeType = "smoke_m";
|
||||
|
||||
public readonly string Palette = "terrain";
|
||||
|
||||
public object Create(ActorInitializer init) { return new SmudgeLayer(this); }
|
||||
}
|
||||
|
||||
public class SmudgeLayer : IRenderOverlay, IWorldLoaded, ITickRender
|
||||
{
|
||||
struct Smudge
|
||||
{
|
||||
public string Type;
|
||||
public int Depth;
|
||||
public Sprite Sprite;
|
||||
}
|
||||
|
||||
public SmudgeLayerInfo Info;
|
||||
Dictionary<CPos, Smudge> tiles;
|
||||
Dictionary<CPos, Smudge> dirty;
|
||||
Dictionary<string, Sprite[]> smudges;
|
||||
World world;
|
||||
|
||||
public SmudgeLayer(SmudgeLayerInfo info)
|
||||
{
|
||||
this.Info = info;
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w, WorldRenderer wr)
|
||||
{
|
||||
world = w;
|
||||
tiles = new Dictionary<CPos, Smudge>();
|
||||
dirty = new Dictionary<CPos, Smudge>();
|
||||
smudges = new Dictionary<string, Sprite[]>();
|
||||
|
||||
var types = world.Map.SequenceProvider.Sequences(Info.Sequence);
|
||||
foreach (var t in types)
|
||||
{
|
||||
var seq = world.Map.SequenceProvider.GetSequence(Info.Sequence, t);
|
||||
var sprites = Exts.MakeArray(seq.Length, x => seq.GetSprite(x));
|
||||
smudges.Add(t, sprites);
|
||||
}
|
||||
|
||||
// Add map smudges
|
||||
foreach (var s in w.Map.Smudges.Value.Where(s => smudges.Keys.Contains(s.Type)))
|
||||
{
|
||||
var smudge = new Smudge
|
||||
{
|
||||
Type = s.Type,
|
||||
Depth = s.Depth,
|
||||
Sprite = smudges[s.Type][s.Depth]
|
||||
};
|
||||
|
||||
tiles.Add((CPos)s.Location, smudge);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddSmudge(CPos loc)
|
||||
{
|
||||
if (Game.CosmeticRandom.Next(0, 100) <= Info.SmokePercentage)
|
||||
world.AddFrameEndTask(w => w.Add(new Smoke(w, world.Map.CenterOfCell(loc), Info.SmokeType)));
|
||||
|
||||
if (!dirty.ContainsKey(loc) && !tiles.ContainsKey(loc))
|
||||
{
|
||||
// No smudge; create a new one
|
||||
var st = smudges.Keys.Random(world.SharedRandom);
|
||||
dirty[loc] = new Smudge { Type = st, Depth = 0, Sprite = smudges[st][0] };
|
||||
}
|
||||
else
|
||||
{
|
||||
// Existing smudge; make it deeper
|
||||
var tile = dirty.ContainsKey(loc) ? dirty[loc] : tiles[loc];
|
||||
var maxDepth = smudges[tile.Type].Length;
|
||||
if (tile.Depth < maxDepth - 1)
|
||||
{
|
||||
tile.Depth++;
|
||||
tile.Sprite = smudges[tile.Type][tile.Depth];
|
||||
}
|
||||
|
||||
dirty[loc] = tile;
|
||||
}
|
||||
}
|
||||
|
||||
public void TickRender(WorldRenderer wr, Actor self)
|
||||
{
|
||||
var remove = new List<CPos>();
|
||||
foreach (var kv in dirty)
|
||||
{
|
||||
if (!self.World.FogObscures(kv.Key))
|
||||
{
|
||||
tiles[kv.Key] = kv.Value;
|
||||
remove.Add(kv.Key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var r in remove)
|
||||
dirty.Remove(r);
|
||||
}
|
||||
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
var pal = wr.Palette(Info.Palette);
|
||||
|
||||
foreach (var kv in tiles)
|
||||
{
|
||||
if (!wr.Viewport.VisibleCells.Contains(kv.Key))
|
||||
continue;
|
||||
|
||||
if (world.ShroudObscures(kv.Key))
|
||||
continue;
|
||||
|
||||
new SpriteRenderable(kv.Value.Sprite, world.Map.CenterOfCell(kv.Key),
|
||||
WVec.Zero, -511, pal, 1f, true).Render(wr); // TODO ZOffset is ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
OpenRA.Mods.Common/Traits/World/SpawnMapActors.cs
Normal file
44
OpenRA.Mods.Common/Traits/World/SpawnMapActors.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
#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.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
public class SpawnMapActorsInfo : TraitInfo<SpawnMapActors> { }
|
||||
|
||||
public class SpawnMapActors : IWorldLoaded
|
||||
{
|
||||
public Dictionary<string, Actor> Actors = new Dictionary<string, Actor>();
|
||||
public uint LastMapActorID { get; private set; }
|
||||
|
||||
public void WorldLoaded(World world, WorldRenderer wr)
|
||||
{
|
||||
foreach (var actorReference in world.Map.Actors.Value)
|
||||
{
|
||||
// if there is no real player associated, dont spawn it.
|
||||
var ownerName = actorReference.Value.InitDict.Get<OwnerInit>().PlayerName;
|
||||
if (!world.Players.Any(p => p.InternalName == ownerName))
|
||||
continue;
|
||||
|
||||
var initDict = actorReference.Value.InitDict;
|
||||
initDict.Add(new SkipMakeAnimsInit());
|
||||
var actor = world.CreateActor(actorReference.Value.Type, initDict);
|
||||
Actors[actorReference.Key] = actor;
|
||||
LastMapActorID = actor.ActorID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SkipMakeAnimsInit : IActorInit {}
|
||||
}
|
||||
36
OpenRA.Mods.Common/Traits/World/StartGameNotification.cs
Normal file
36
OpenRA.Mods.Common/Traits/World/StartGameNotification.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
#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.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
class StartGameNotificationInfo : ITraitInfo
|
||||
{
|
||||
public readonly string Notification = "StartGame";
|
||||
|
||||
public object Create(ActorInitializer init) { return new StartGameNotification(this); }
|
||||
}
|
||||
|
||||
class StartGameNotification : IWorldLoaded
|
||||
{
|
||||
StartGameNotificationInfo info;
|
||||
public StartGameNotification(StartGameNotificationInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public void WorldLoaded(World world, WorldRenderer wr)
|
||||
{
|
||||
Sound.PlayNotification(world.Map.Rules, null, "Speech", info.Notification, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
127
OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs
Normal file
127
OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
#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.Drawing;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Graphics;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
[Desc("Renders a debug overlay showing the terrain cells. Attach this to the world actor.")]
|
||||
public class TerrainGeometryOverlayInfo : ITraitInfo
|
||||
{
|
||||
public object Create(ActorInitializer init) { return new TerrainGeometryOverlay(init.self); }
|
||||
}
|
||||
|
||||
public class TerrainGeometryOverlay : IRenderOverlay
|
||||
{
|
||||
readonly int[][] vertices = new int[][]
|
||||
{
|
||||
// Flat
|
||||
new[] { 0, 0, 0, 0 },
|
||||
|
||||
// Slopes (two corners high)
|
||||
new[] { 0, 0, 1, 1 },
|
||||
new[] { 1, 0, 0, 1 },
|
||||
new[] { 1, 1, 0, 0 },
|
||||
new[] { 0, 1, 1, 0 },
|
||||
|
||||
// Slopes (one corner high)
|
||||
new[] { 0, 0, 0, 1 },
|
||||
new[] { 1, 0, 0, 0 },
|
||||
new[] { 0, 1, 0, 0 },
|
||||
new[] { 0, 0, 1, 0 },
|
||||
|
||||
// Slopes (three corners high)
|
||||
new[] { 1, 0, 1, 1 },
|
||||
new[] { 1, 1, 0, 1 },
|
||||
new[] { 1, 1, 1, 0 },
|
||||
new[] { 0, 1, 1, 1 },
|
||||
|
||||
// Slopes (two corners high, one corner double high)
|
||||
new[] { 1, 0, 1, 2 },
|
||||
new[] { 2, 1, 0, 1 },
|
||||
new[] { 1, 2, 1, 0 },
|
||||
new[] { 0, 1, 2, 1 },
|
||||
|
||||
// Slopes (two corners high, alternating)
|
||||
new[] { 1, 0, 1, 0 },
|
||||
new[] { 0, 1, 0, 1 },
|
||||
new[] { 1, 0, 1, 0 },
|
||||
new[] { 0, 1, 0, 1 }
|
||||
};
|
||||
|
||||
readonly Lazy<DeveloperMode> devMode;
|
||||
|
||||
public TerrainGeometryOverlay(Actor self)
|
||||
{
|
||||
devMode = Exts.Lazy(() => self.World.LocalPlayer != null ? self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>() : null);
|
||||
}
|
||||
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
if (devMode.Value == null || !devMode.Value.ShowTerrainGeometry)
|
||||
return;
|
||||
|
||||
var ts = wr.world.Map.TileShape;
|
||||
var colors = wr.world.TileSet.HeightDebugColors;
|
||||
|
||||
var leftDelta = ts == TileShape.Diamond ? new WVec(-512, 0, 0) : new WVec(-512, -512, 0);
|
||||
var topDelta = ts == TileShape.Diamond ? new WVec(0, -512, 0) : new WVec(512, -512, 0);
|
||||
var rightDelta = ts == TileShape.Diamond ? new WVec(512, 0, 0) : new WVec(512, 512, 0);
|
||||
var bottomDelta = ts == TileShape.Diamond ? new WVec(0, 512, 0) : new WVec(-512, 512, 0);
|
||||
|
||||
foreach (var cell in wr.Viewport.VisibleCells)
|
||||
{
|
||||
var lr = Game.Renderer.WorldLineRenderer;
|
||||
var pos = wr.world.Map.CenterOfCell(cell);
|
||||
|
||||
var height = (int)wr.world.Map.MapHeight.Value[cell];
|
||||
var tile = wr.world.Map.MapTiles.Value[cell];
|
||||
|
||||
TerrainTileInfo tileInfo = null;
|
||||
|
||||
// TODO: This is a temporary workaround for our sloppy tileset definitions
|
||||
// (ra/td templates omit Clear tiles from templates)
|
||||
try
|
||||
{
|
||||
tileInfo = wr.world.TileSet.Templates[tile.Type][tile.Index];
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
if (tileInfo == null)
|
||||
continue;
|
||||
|
||||
var leftHeight = vertices[tileInfo.RampType][0];
|
||||
var topHeight = vertices[tileInfo.RampType][1];
|
||||
var rightHeight = vertices[tileInfo.RampType][2];
|
||||
var bottomHeight = vertices[tileInfo.RampType][3];
|
||||
|
||||
var leftColor = colors[height + leftHeight];
|
||||
var topColor = colors[height + topHeight];
|
||||
var rightColor = colors[height + rightHeight];
|
||||
var bottomColor = colors[height + bottomHeight];
|
||||
|
||||
var left = wr.ScreenPxPosition(pos + leftDelta + new WVec(0, 0, 512 * leftHeight)).ToFloat2();
|
||||
var top = wr.ScreenPxPosition(pos + topDelta + new WVec(0, 0, 512 * topHeight)).ToFloat2();
|
||||
var right = wr.ScreenPxPosition(pos + rightDelta + new WVec(0, 0, 512 * rightHeight)).ToFloat2();
|
||||
var bottom = wr.ScreenPxPosition(pos + bottomDelta + new WVec(0, 0, 512 * bottomHeight)).ToFloat2();
|
||||
|
||||
lr.DrawLine(left, top, leftColor, topColor);
|
||||
lr.DrawLine(top, right, topColor, rightColor);
|
||||
lr.DrawLine(right, bottom, rightColor, bottomColor);
|
||||
lr.DrawLine(bottom, left, bottomColor, leftColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user