Compare commits
72 Commits
log_chat_s
...
log_chat_s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50277c59d7 | ||
|
|
2a13752203 | ||
|
|
8ce3ea5f2c | ||
|
|
b4f3d8ae02 | ||
|
|
d70c30763b | ||
|
|
27297a1ba9 | ||
|
|
2890ccf2b1 | ||
|
|
f9f1889112 | ||
|
|
d9c5821329 | ||
|
|
7fc415de19 | ||
|
|
ec01f63317 | ||
|
|
c9ac702217 | ||
|
|
1913c608a6 | ||
|
|
94ac6cc400 | ||
|
|
97d9520bf0 | ||
|
|
e7300cc430 | ||
|
|
677d7e47a4 | ||
|
|
5f287d38ab | ||
|
|
3a00d5a679 | ||
|
|
d5d43ee97c | ||
|
|
fd12ef1535 | ||
|
|
de8317d9b8 | ||
|
|
b953f4a40d | ||
|
|
b565f0903a | ||
|
|
da71a8271d | ||
|
|
54558169e1 | ||
|
|
bcb953309a | ||
|
|
a371826613 | ||
|
|
82ee3d779c | ||
|
|
4ea0e70589 | ||
|
|
3f4e4db836 | ||
|
|
85020bc2af | ||
|
|
70bb01a2c7 | ||
|
|
38d4719073 | ||
|
|
aebb97967d | ||
|
|
97f81d0aa4 | ||
|
|
2030c17a8b | ||
|
|
b6dcd8d9af | ||
|
|
9a78f22e86 | ||
|
|
19ca3d610b | ||
|
|
dd74d3572a | ||
|
|
ad7e5d62cd | ||
|
|
ece7547695 | ||
|
|
944bc7667a | ||
|
|
0ac28e1f5f | ||
|
|
ca3a57289e | ||
|
|
a4641be7e7 | ||
|
|
0651308210 | ||
|
|
cac6aebb6c | ||
|
|
0baddf46e2 | ||
|
|
e0d893b736 | ||
|
|
2ccfa425b2 | ||
|
|
52102da839 | ||
|
|
fc08f18663 | ||
|
|
cc064d0dad | ||
|
|
8b997a5656 | ||
|
|
7d8fff535d | ||
|
|
0855a9e5bf | ||
|
|
3fa2b9fe79 | ||
|
|
c561509c82 | ||
|
|
0566b6f787 | ||
|
|
e2821933a4 | ||
|
|
34c29f872a | ||
|
|
2192a6b4f9 | ||
|
|
68ad9163ff | ||
|
|
5f6eecc1f6 | ||
|
|
5edca87622 | ||
|
|
fa01c24f23 | ||
|
|
593c2735ce | ||
|
|
48e72c62f3 | ||
|
|
f316d4c757 | ||
|
|
d6dba3e545 |
@@ -336,7 +336,7 @@ dotnet_diagnostic.IDE0305.severity = silent # Requires C# 12
|
||||
#dotnet_style_readonly_field = true
|
||||
dotnet_diagnostic.IDE0044.severity = warning
|
||||
|
||||
## Language keyword vs. framework types preferences
|
||||
## Language keyword vs framework types preferences
|
||||
|
||||
# IDE0049 Use language keywords instead of framework type names for type references
|
||||
#dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||
|
||||
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -14,6 +14,9 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Remove System .NET
|
||||
run: sudo apt-get remove -y dotnet*
|
||||
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
|
||||
85
.github/workflows/itch.yml
vendored
85
.github/workflows/itch.yml
vendored
@@ -15,73 +15,56 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.repository == 'openra/openra'
|
||||
steps:
|
||||
- name: Download Packages
|
||||
- name: Download Butler
|
||||
env:
|
||||
BUTLER_API_KEY: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
run: |
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-${{ github.event.inputs.tag }}-x64.exe"
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-${{ github.event.inputs.tag }}-x64-winportable.zip" -O "OpenRA-${{ github.event.inputs.tag }}-x64-win-itch.zip"
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-${{ github.event.inputs.tag }}.dmg"
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Dune-2000-x86_64.AppImage"
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Red-Alert-x86_64.AppImage"
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Tiberian-Dawn-x86_64.AppImage"
|
||||
wget -q "https://raw.githubusercontent.com/${{ github.repository }}/${{ github.event.inputs.tag }}/packaging/.itch.toml"
|
||||
zip -u "OpenRA-${{ github.event.inputs.tag }}-x64-win-itch.zip" .itch.toml
|
||||
wget -cq -O butler-linux-amd64.zip https://broth.itch.ovh/butler/linux-amd64/LATEST/archive/default
|
||||
unzip butler-linux-amd64.zip
|
||||
rm butler-linux-amd64.zip
|
||||
chmod +x butler
|
||||
./butler -V
|
||||
./butler login
|
||||
|
||||
- name: Publish Windows Installer
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: win
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-${{ github.event.inputs.tag }}-x64.exe
|
||||
BUTLER_API_KEY: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
run: |
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-${{ github.event.inputs.tag }}-x64.exe"
|
||||
./butler push "OpenRA-${{ github.event.inputs.tag }}-x64.exe" "openra/openra:win" --userversion ${{ github.event.inputs.tag }}
|
||||
|
||||
- name: Publish Windows Itch Bundle
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: itch
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-${{ github.event.inputs.tag }}-x64-win-itch.zip
|
||||
BUTLER_API_KEY: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
run: |
|
||||
wget -q "https://raw.githubusercontent.com/${{ github.repository }}/${{ github.event.inputs.tag }}/packaging/.itch.toml"
|
||||
zip -u "OpenRA-${{ github.event.inputs.tag }}-x64-win-itch.zip" .itch.toml
|
||||
./butler push "OpenRA-${{ github.event.inputs.tag }}-x64-win-itch.zip" "openra/openra:itch" --userversion ${{ github.event.inputs.tag }}
|
||||
|
||||
- name: Publish macOS Package
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: macos
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-${{ github.event.inputs.tag }}.dmg
|
||||
BUTLER_API_KEY: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
run: |
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-${{ github.event.inputs.tag }}.dmg"
|
||||
./butler push "OpenRA-${{ github.event.inputs.tag }}.dmg" "openra/openra:macos" --userversion ${{ github.event.inputs.tag }}
|
||||
|
||||
- name: Publish RA AppImage
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: linux-ra
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-Red-Alert-x86_64.AppImage
|
||||
BUTLER_API_KEY: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
run: |
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Red-Alert-x86_64.AppImage"
|
||||
./butler push "OpenRA-Red-Alert-x86_64.AppImage" "openra/openra:linux-ra" --userversion ${{ github.event.inputs.tag }}
|
||||
|
||||
- name: Publish TD AppImage
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: linux-cnc
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-Tiberian-Dawn-x86_64.AppImage
|
||||
BUTLER_API_KEY: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
run: |
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Tiberian-Dawn-x86_64.AppImage"
|
||||
./butler push "OpenRA-Tiberian-Dawn-x86_64.AppImage" "openra/openra:linux-cnc" --userversion ${{ github.event.inputs.tag }}
|
||||
|
||||
- name: Publish D2k AppImage
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: linux-d2k
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-Dune-2000-x86_64.AppImage
|
||||
BUTLER_API_KEY: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
run: |
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Dune-2000-x86_64.AppImage"
|
||||
./butler push "OpenRA-Dune-2000-x86_64.AppImage" "openra/openra:linux-d2k" --userversion ${{ github.event.inputs.tag }}
|
||||
|
||||
36
.github/workflows/packaging.yml
vendored
36
.github/workflows/packaging.yml
vendored
@@ -57,13 +57,11 @@ jobs:
|
||||
./packaging/linux/buildpackage.sh "${GIT_TAG}" "${PWD}/build/linux"
|
||||
|
||||
- name: Upload Packages
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
file: build/linux/*
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
gh release upload ${{ github.ref_name }} build/linux/*
|
||||
|
||||
macos:
|
||||
name: macOS Disk Image
|
||||
@@ -92,13 +90,11 @@ jobs:
|
||||
./packaging/macos/buildpackage.sh "${GIT_TAG}" "${PWD}/build/macos"
|
||||
|
||||
- name: Upload Package
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
file: build/macos/*
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
gh release upload ${{ github.ref_name }} build/macos/*
|
||||
|
||||
windows:
|
||||
name: Windows Installers
|
||||
@@ -124,10 +120,8 @@ jobs:
|
||||
./packaging/windows/buildpackage.sh "${GIT_TAG}" "${PWD}/build/windows"
|
||||
|
||||
- name: Upload Packages
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
file: build/windows/*
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
gh release upload ${{ github.ref_name }} build/windows/*
|
||||
|
||||
29
Dockerfile
Normal file
29
Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0
|
||||
|
||||
RUN \
|
||||
apt-get update; \
|
||||
apt-get -y upgrade; \
|
||||
apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
wget \
|
||||
make \
|
||||
python3 \
|
||||
unzip \
|
||||
mono-complete
|
||||
|
||||
|
||||
RUN useradd -d /home/openra -m -s /sbin/nologin openra
|
||||
|
||||
WORKDIR /home/openra
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN chown -R openra:openra .
|
||||
|
||||
USER openra
|
||||
|
||||
RUN make
|
||||
|
||||
EXPOSE 1234
|
||||
|
||||
ENTRYPOINT ["./launch-dedicated.sh"]
|
||||
@@ -22,6 +22,7 @@ namespace OpenRA.Graphics
|
||||
public string Name { get; private set; }
|
||||
public bool IsDecoration { get; set; }
|
||||
|
||||
readonly Map map;
|
||||
readonly SequenceSet sequences;
|
||||
readonly Func<WAngle> facingFunc;
|
||||
readonly Func<bool> paused;
|
||||
@@ -43,6 +44,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public Animation(World world, string name, Func<WAngle> facingFunc, Func<bool> paused)
|
||||
{
|
||||
map = world.Map;
|
||||
sequences = world.Map.Sequences;
|
||||
Name = name.ToLowerInvariant();
|
||||
this.facingFunc = facingFunc;
|
||||
@@ -65,8 +67,10 @@ namespace OpenRA.Graphics
|
||||
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
|
||||
if (shadow != null)
|
||||
{
|
||||
var height = map.DistanceAboveTerrain(pos).Length;
|
||||
|
||||
var shadowRenderable = new SpriteRenderable(
|
||||
shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette,
|
||||
shadow, pos, offset - new WVec(0, 0, height), CurrentSequence.ShadowZOffset + zOffset + height, palette,
|
||||
CurrentSequence.Scale, 1f, float3.Ones, tintModifiers,
|
||||
true, rotation);
|
||||
return new IRenderable[] { shadowRenderable, imageRenderable };
|
||||
|
||||
@@ -149,6 +149,10 @@ namespace OpenRA.Graphics
|
||||
var buffer = data;
|
||||
ReleaseBuffer();
|
||||
|
||||
// We aren't commiting data to the GPU, so let's not delete our data.
|
||||
if (Game.Renderer == null)
|
||||
return false;
|
||||
|
||||
// Only transfer if the destination has no data that would be lost by overwriting.
|
||||
if (buffer != null && destination.data == null && destination.texture == null)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using OpenRA.FileSystem;
|
||||
@@ -244,39 +245,61 @@ namespace OpenRA
|
||||
{
|
||||
var client = HttpClientFactory.Create();
|
||||
var stringPool = new HashSet<string>(); // Reuse common strings in YAML
|
||||
var buffer = new StringBuilder();
|
||||
|
||||
// Limit each query to 50 maps at a time to avoid request size limits
|
||||
for (var i = 0; i < queryUids.Count; i += 50)
|
||||
{
|
||||
var batchUids = queryUids.Skip(i).Take(50).ToList();
|
||||
var url = repositoryUrl + "hash/" + string.Join(",", batchUids) + "/yaml";
|
||||
try
|
||||
|
||||
using (new PerfTimer("RemoteMapDetails"))
|
||||
{
|
||||
var httpResponseMessage = await client.GetAsync(url);
|
||||
var result = await httpResponseMessage.Content.ReadAsStreamAsync();
|
||||
|
||||
var yaml = MiniYaml.FromStream(result, url, stringPool: stringPool);
|
||||
foreach (var kv in yaml)
|
||||
previews[kv.Key].UpdateRemoteSearch(MapStatus.DownloadAvailable, kv.Value, modData.Manifest.MapCompatibility, mapDetailsReceived);
|
||||
|
||||
foreach (var uid in batchUids)
|
||||
try
|
||||
{
|
||||
var p = previews[uid];
|
||||
if (p.Status != MapStatus.DownloadAvailable)
|
||||
p.UpdateRemoteSearch(MapStatus.Unavailable, null, null);
|
||||
await using (var resultStream = await client.GetStreamAsync(url))
|
||||
{
|
||||
using (var resultReader = new StreamReader(resultStream))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var line = await resultReader.ReadLineAsync();
|
||||
if (line == null || !line.StartsWith('\t'))
|
||||
{
|
||||
var yaml = MiniYaml.FromString(buffer.ToString(), url, stringPool: stringPool);
|
||||
buffer.Clear();
|
||||
foreach (var kv in yaml)
|
||||
previews[kv.Key].UpdateRemoteSearch(MapStatus.DownloadAvailable, kv.Value, modData.Manifest.MapCompatibility, mapDetailsReceived);
|
||||
|
||||
if (line == null)
|
||||
break;
|
||||
}
|
||||
|
||||
buffer.Append(line);
|
||||
buffer.Append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var uid in batchUids)
|
||||
{
|
||||
var p = previews[uid];
|
||||
if (p.Status != MapStatus.DownloadAvailable)
|
||||
p.UpdateRemoteSearch(MapStatus.Unavailable, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("debug", "Remote map query failed with error:");
|
||||
Log.Write("debug", e);
|
||||
Log.Write("debug", $"URL was: {url}");
|
||||
|
||||
foreach (var uid in batchUids)
|
||||
catch (Exception e)
|
||||
{
|
||||
var p = previews[uid];
|
||||
p.UpdateRemoteSearch(MapStatus.Unavailable, null, null);
|
||||
mapQueryFailed?.Invoke(p);
|
||||
Log.Write("debug", "Remote map query failed with error:");
|
||||
Log.Write("debug", e);
|
||||
Log.Write("debug", $"URL was: {url}");
|
||||
|
||||
foreach (var uid in batchUids)
|
||||
{
|
||||
var p = previews[uid];
|
||||
p.UpdateRemoteSearch(MapStatus.Unavailable, null, null);
|
||||
mapQueryFailed?.Invoke(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,11 +249,26 @@ namespace OpenRA
|
||||
/// </summary>
|
||||
public string GetMessage(string key, object[] args = null)
|
||||
{
|
||||
// PERF: instead of loading mod level strings per each MapPreview, reuse the already loaded one in FluentProvider.
|
||||
if (FluentProvider.TryGetModMessage(key, out var message, args))
|
||||
if (TryGetMessage(key, out var message, args))
|
||||
return message;
|
||||
|
||||
return innerData.FluentBundle?.GetMessage(key, args) ?? key;
|
||||
return key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Functionality mirrors <see cref="FluentProvider.TryGetMessage"/>, except instead of using
|
||||
/// loaded <see cref="Map"/>'s fluent bundle as backup, we use this <see cref="MapPreview"/>'s.
|
||||
/// </summary>
|
||||
public bool TryGetMessage(string key, out string message, object[] args = null)
|
||||
{
|
||||
// PERF: instead of loading mod level strings per each MapPreview, reuse the already loaded one in FluentProvider.
|
||||
if (FluentProvider.TryGetModMessage(key, out message, args))
|
||||
return true;
|
||||
|
||||
if (innerData.FluentBundle == null)
|
||||
return false;
|
||||
|
||||
return innerData.FluentBundle.TryGetMessage(key, out message, args);
|
||||
}
|
||||
|
||||
Sprite minimap;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<RootNamespace>OpenRA</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(MSBuildRuntimeType)'!='Mono'">
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="6.0.2" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(MSBuildRuntimeType)'=='Mono'">
|
||||
|
||||
@@ -1005,9 +1005,12 @@ namespace OpenRA.Server
|
||||
}
|
||||
|
||||
case "Chat":
|
||||
{
|
||||
if (!IsMultiplayer || !playerMessageTracker.IsPlayerAtFloodLimit(conn))
|
||||
DispatchOrdersToClients(conn, 0, o.Serialize());
|
||||
{
|
||||
var client = GetClient(conn);
|
||||
Console.WriteLine($"[{DateTime.Now.ToString(Settings.TimestampFormat)}] {client.Name}: {o.TargetString}");
|
||||
|
||||
if (Type == ServerType.Local || !playerMessageTracker.IsPlayerAtFloodLimit(conn))
|
||||
DispatchOrdersToClients(conn, 0, o.Serialize());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ namespace OpenRA.Mods.Cnc.Scripting
|
||||
public ChronosphereProperties(ScriptContext context, Actor self)
|
||||
: base(context, self) { }
|
||||
|
||||
[Desc("Chronoshift a group of actors. A duration of 0 will teleport the actors permanently.")]
|
||||
[Desc("Chronoshift a group of actors. A duration of 0 will teleport the actors permanently. " +
|
||||
"If a given cell is unexplored for this power's owner, the closest valid cell will be used instead.")]
|
||||
public void Chronoshift([ScriptEmmyTypeOverride("{ [actor]: cpos }")] LuaTable unitLocationPairs, int duration = 0, bool killCargo = false)
|
||||
{
|
||||
foreach (var kv in unitLocationPairs)
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
public override object Create(ActorInitializer init) { return new ChronoVortexRenderer(init.Self); }
|
||||
}
|
||||
|
||||
public sealed class ChronoVortexRenderer : IRenderPostProcessPass
|
||||
public sealed class ChronoVortexRenderer : IRenderPostProcessPass, INotifyActorDisposing
|
||||
{
|
||||
readonly Renderer renderer;
|
||||
readonly IShader shader;
|
||||
@@ -105,5 +105,11 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
|
||||
vortices.Clear();
|
||||
}
|
||||
|
||||
void INotifyActorDisposing.Disposing(Actor self)
|
||||
{
|
||||
vortexSheet.Dispose();
|
||||
vortexBuffer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
void CancelCapture()
|
||||
{
|
||||
manager.CancelCapture(enterCaptureManager);
|
||||
manager.CancelCapture(enterActor, enterCaptureManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,8 @@ namespace OpenRA.Mods.Common.Activities
|
||||
// Prioritise search by these locations in this order: lastHarvestedCell -> lastLinkedDock -> self.
|
||||
CPos searchFromLoc;
|
||||
int searchRadius;
|
||||
WPos? dockPos = null;
|
||||
var dockPos = harv.DockClientManager?.LastReservedHost?.DockPosition;
|
||||
|
||||
if (lastHarvestedCell.HasValue)
|
||||
{
|
||||
searchRadius = harvInfo.SearchFromHarvesterRadius;
|
||||
@@ -182,12 +183,8 @@ namespace OpenRA.Mods.Common.Activities
|
||||
else
|
||||
{
|
||||
searchRadius = harvInfo.SearchFromProcRadius;
|
||||
var dock = harv.DockClientManager?.LastReservedHost;
|
||||
if (dock != null)
|
||||
{
|
||||
dockPos = dock.DockPosition;
|
||||
if (dockPos != null)
|
||||
searchFromLoc = self.World.Map.CellContaining(dockPos.Value);
|
||||
}
|
||||
else
|
||||
searchFromLoc = self.Location;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace OpenRA.Mods.Common.Activities
|
||||
readonly bool forceEnter;
|
||||
readonly bool ignoreOccupancy;
|
||||
|
||||
bool dockingCancelled;
|
||||
|
||||
public MoveToDock(Actor self, Actor dockHostActor = null, IDockHost dockHost = null,
|
||||
bool forceEnter = false, bool ignoreOccupancy = false, Color? dockLineColor = null)
|
||||
{
|
||||
@@ -44,17 +46,25 @@ namespace OpenRA.Mods.Common.Activities
|
||||
|
||||
protected override void OnFirstRun(Actor self)
|
||||
{
|
||||
if (IsCanceling || dockClient.IsTraitDisabled)
|
||||
if (dockClient.IsTraitDisabled)
|
||||
return;
|
||||
|
||||
// We were ordered to dock to an actor but host was unspecified.
|
||||
if (dockHostActor != null && dockHost == null)
|
||||
{
|
||||
if (dockHostActor.IsDead || !dockHostActor.IsInWorld)
|
||||
{
|
||||
dockingCancelled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var link = dockClient.AvailableDockHosts(dockHostActor, default, forceEnter, ignoreOccupancy)
|
||||
.ClosestDock(self, dockClient);
|
||||
|
||||
if (link.HasValue)
|
||||
dockHost = link.Value.Trait;
|
||||
else
|
||||
dockingCancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +73,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
if (IsCanceling)
|
||||
return true;
|
||||
|
||||
if (dockClient.IsTraitDisabled)
|
||||
if (dockingCancelled || dockClient.IsTraitDisabled)
|
||||
{
|
||||
Cancel(self, true);
|
||||
return true;
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.EditorBrushes;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Widgets;
|
||||
@@ -87,6 +89,12 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return ((long)pixelDistance << 32) + worldZPosition;
|
||||
}
|
||||
|
||||
public void DeleteSelection(MapBlitFilters filters)
|
||||
{
|
||||
if (Selection.Area != null)
|
||||
editorActionManager.Add(new DeleteAreaAction(world.Map, filters, Selection.Area, resourceLayer, actorLayer));
|
||||
}
|
||||
|
||||
public void ClearSelection(bool updateSelectedTab = false)
|
||||
{
|
||||
if (Selection.HasSelection)
|
||||
@@ -335,6 +343,114 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DeleteAreaAction : IEditorAction
|
||||
{
|
||||
[FluentReference("x", "y", "width", "height")]
|
||||
const string RemovedArea = "notification-removed-area";
|
||||
|
||||
public string Text { get; }
|
||||
|
||||
readonly EditorBlitSource editorBlitSource;
|
||||
readonly MapBlitFilters blitFilters;
|
||||
readonly IResourceLayer resourceLayer;
|
||||
readonly EditorActorLayer editorActorLayer;
|
||||
readonly CellRegion area;
|
||||
readonly Map map;
|
||||
|
||||
public DeleteAreaAction(Map map, MapBlitFilters blitFilters, CellRegion area, IResourceLayer resourceLayer, EditorActorLayer editorActorLayer)
|
||||
{
|
||||
this.map = map;
|
||||
this.blitFilters = blitFilters;
|
||||
this.resourceLayer = resourceLayer;
|
||||
this.editorActorLayer = editorActorLayer;
|
||||
this.area = area;
|
||||
|
||||
editorBlitSource = EditorBlit.CopyRegionContents(map, editorActorLayer, resourceLayer, area, blitFilters);
|
||||
|
||||
Text = FluentProvider.GetMessage(RemovedArea,
|
||||
"x", area.TopLeft.X,
|
||||
"y", area.TopLeft.Y,
|
||||
"width", area.BottomRight.X - area.TopLeft.X,
|
||||
"height", area.BottomRight.Y - area.TopLeft.Y);
|
||||
}
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
Do();
|
||||
}
|
||||
|
||||
public void Do()
|
||||
{
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Actors))
|
||||
{
|
||||
// Clear any existing actors in the paste cells.
|
||||
foreach (var regionActor in editorActorLayer.PreviewsInCellRegion(area.CellCoords).ToList())
|
||||
editorActorLayer.Remove(regionActor);
|
||||
}
|
||||
|
||||
foreach (var tileKeyValuePair in editorBlitSource.Tiles)
|
||||
{
|
||||
var position = tileKeyValuePair.Key;
|
||||
if (!map.Tiles.Contains(position))
|
||||
continue;
|
||||
|
||||
// Clear any existing resources.
|
||||
if (resourceLayer != null && blitFilters.HasFlag(MapBlitFilters.Resources))
|
||||
resourceLayer.ClearResources(position);
|
||||
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Terrain))
|
||||
{
|
||||
map.Tiles[position] = map.Rules.TerrainInfo.DefaultTerrainTile;
|
||||
map.Height[position] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Undo()
|
||||
{
|
||||
foreach (var tileKeyValuePair in editorBlitSource.Tiles)
|
||||
{
|
||||
var position = tileKeyValuePair.Key;
|
||||
if (!map.Tiles.Contains(position))
|
||||
continue;
|
||||
|
||||
var tile = tileKeyValuePair.Value;
|
||||
var resourceLayerContents = tile.ResourceLayerContents;
|
||||
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Terrain))
|
||||
{
|
||||
map.Tiles[position] = tile.TerrainTile;
|
||||
map.Height[position] = tile.Height;
|
||||
}
|
||||
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Resources) &&
|
||||
resourceLayerContents.HasValue &&
|
||||
!string.IsNullOrWhiteSpace(resourceLayerContents.Value.Type))
|
||||
resourceLayer.AddResource(resourceLayerContents.Value.Type, position, resourceLayerContents.Value.Density);
|
||||
}
|
||||
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Actors))
|
||||
{
|
||||
// Create copies of the original actors, update their locations, and place.
|
||||
foreach (var actorKeyValuePair in editorBlitSource.Actors)
|
||||
{
|
||||
var copy = actorKeyValuePair.Value.Export();
|
||||
var locationInit = copy.GetOrDefault<LocationInit>();
|
||||
if (locationInit != null)
|
||||
{
|
||||
if (!map.Tiles.Contains(locationInit.Value))
|
||||
continue;
|
||||
|
||||
copy.RemoveAll<LocationInit>();
|
||||
copy.Add(new LocationInit(locationInit.Value));
|
||||
}
|
||||
|
||||
editorActorLayer.Add(copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class RemoveSelectedActorAction : IEditorAction
|
||||
{
|
||||
[FluentReference("name", "id")]
|
||||
|
||||
@@ -13,23 +13,28 @@ using System.Collections.Generic;
|
||||
|
||||
namespace OpenRA.Mods.Common.FileSystem
|
||||
{
|
||||
[Desc("A file system that loads game assets installed by the user into their support directory.")]
|
||||
public class ContentInstallerFileSystemLoader : IFileSystemLoader, IFileSystemExternalContent
|
||||
{
|
||||
[FieldLoader.Require]
|
||||
[Desc("Mod to use for content installation.")]
|
||||
public readonly string ContentInstallerMod = null;
|
||||
|
||||
[FieldLoader.Require]
|
||||
public readonly Dictionary<string, string> Packages = null;
|
||||
[Desc("A list of mod-provided packages. Anything required to display the initial load screen must be listed here.")]
|
||||
public readonly Dictionary<string, string> SystemPackages = null;
|
||||
|
||||
[Desc("A list of user-installed packages. If missing (and not marked as optional), these will trigger the content installer.")]
|
||||
public readonly Dictionary<string, string> ContentPackages = null;
|
||||
|
||||
public readonly Dictionary<string, string> ContentFiles = null;
|
||||
[Desc("Files that aren't mounted as packages, but still need to trigger the content installer if missing.")]
|
||||
public readonly Dictionary<string, string> RequiredContentFiles = null;
|
||||
|
||||
bool contentAvailable = true;
|
||||
bool isContentAvailable = true;
|
||||
|
||||
public void Mount(OpenRA.FileSystem.FileSystem fileSystem, ObjectCreator objectCreator)
|
||||
{
|
||||
foreach (var kv in Packages)
|
||||
foreach (var kv in SystemPackages)
|
||||
fileSystem.Mount(kv.Key, kv.Value);
|
||||
|
||||
if (ContentPackages != null)
|
||||
@@ -42,23 +47,23 @@ namespace OpenRA.Mods.Common.FileSystem
|
||||
}
|
||||
catch
|
||||
{
|
||||
contentAvailable = false;
|
||||
isContentAvailable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ContentFiles != null)
|
||||
foreach (var kv in ContentFiles)
|
||||
if (RequiredContentFiles != null)
|
||||
foreach (var kv in RequiredContentFiles)
|
||||
if (!fileSystem.Exists(kv.Key))
|
||||
contentAvailable = false;
|
||||
isContentAvailable = false;
|
||||
}
|
||||
|
||||
bool IFileSystemExternalContent.InstallContentIfRequired(ModData modData)
|
||||
{
|
||||
if (!contentAvailable)
|
||||
if (!isContentAvailable)
|
||||
Game.InitializeMod(ContentInstallerMod, new Arguments());
|
||||
|
||||
return !contentAvailable;
|
||||
return !isContentAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">
|
||||
<Private>False</Private>
|
||||
</ProjectReference>
|
||||
<PackageReference Include="NuGet.CommandLine" Version="6.12.1" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="OpenRA-FuzzyLogicLibrary" Version="1.0.1" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
|
||||
@@ -82,7 +82,8 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnIdle, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when the actor is damaged. The callback " +
|
||||
[Desc("Call a function when the actor is damaged. " +
|
||||
"Repairs or other negative damage can activate this trigger. The callback " +
|
||||
"function will be called as func(self: actor, attacker: actor, damage: integer).")]
|
||||
public void OnDamaged(Actor actor, [ScriptEmmyTypeOverride("fun(self: actor, attacker: actor, damage: integer)")] LuaFunction func)
|
||||
{
|
||||
@@ -130,7 +131,8 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
GetScriptTriggers(a).OnKilledInternal += OnMemberKilled;
|
||||
}
|
||||
|
||||
[Desc("Call a function when one of the actors in a group is killed. The callback " +
|
||||
[Desc("Call a function when one of the actors in a group is killed. " +
|
||||
"This trigger is only called once. The callback " +
|
||||
"function will be called as func(killed: actor).")]
|
||||
public void OnAnyKilled(Actor[] actors, [ScriptEmmyTypeOverride("fun(killed: actor)")] LuaFunction func)
|
||||
{
|
||||
@@ -164,12 +166,12 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
|
||||
[Desc("Call a function when this actor produces another actor. " +
|
||||
"The callback function will be called as func(producer: actor, produced: actor).")]
|
||||
public void OnProduction(Actor actors, [ScriptEmmyTypeOverride("fun(producer: actor, produced: actor)")] LuaFunction func)
|
||||
public void OnProduction(Actor actor, [ScriptEmmyTypeOverride("fun(producer: actor, produced: actor)")] LuaFunction func)
|
||||
{
|
||||
if (actors == null)
|
||||
throw new NullReferenceException(nameof(actors));
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actors).RegisterCallback(Trigger.OnProduction, func, Context);
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnProduction, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when any actor produces another actor. The callback " +
|
||||
@@ -304,15 +306,16 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
|
||||
[Desc("Call a function when this actor is captured. The callback function " +
|
||||
"will be called as func(self: actor, captor: actor, oldOwner: player, newOwner: player).")]
|
||||
public void OnCapture(Actor actors, [ScriptEmmyTypeOverride("fun(self: actor, captor: actor, oldOwner: player, newOwner: player)")] LuaFunction func)
|
||||
public void OnCapture(Actor actor, [ScriptEmmyTypeOverride("fun(self: actor, captor: actor, oldOwner: player, newOwner: player)")] LuaFunction func)
|
||||
{
|
||||
if (actors == null)
|
||||
throw new NullReferenceException(nameof(actors));
|
||||
if (actor == null)
|
||||
throw new NullReferenceException(nameof(actor));
|
||||
|
||||
GetScriptTriggers(actors).RegisterCallback(Trigger.OnCapture, func, Context);
|
||||
GetScriptTriggers(actor).RegisterCallback(Trigger.OnCapture, func, Context);
|
||||
}
|
||||
|
||||
[Desc("Call a function when this actor is killed or captured. " +
|
||||
"This trigger is only called once. " +
|
||||
"The callback function will be called as func().")]
|
||||
public void OnKilledOrCaptured(Actor actor, [ScriptEmmyTypeOverride("fun()")] LuaFunction func)
|
||||
{
|
||||
@@ -345,6 +348,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
|
||||
[Desc("Call a function when all of the actors in a group have been killed or captured. " +
|
||||
"This trigger is only called once. " +
|
||||
"The callback function will be called as func().")]
|
||||
public void OnAllKilledOrCaptured(Actor[] actors, [ScriptEmmyTypeOverride("fun()")] LuaFunction func)
|
||||
{
|
||||
|
||||
@@ -80,7 +80,8 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
Self.World.Add(new FlashTarget(Self, color, 0.5f, count, interval, delay));
|
||||
}
|
||||
|
||||
[Desc("The effective owner of the actor.")]
|
||||
[Desc("The effective (displayed) owner of the actor. " +
|
||||
"This may differ from the true owner in some cases, such as disguised actors.")]
|
||||
public Player EffectiveOwner
|
||||
{
|
||||
get
|
||||
@@ -96,7 +97,6 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
[ScriptPropertyGroup("General")]
|
||||
public class GeneralProperties : ScriptActorProperties
|
||||
{
|
||||
readonly IFacing facing;
|
||||
readonly AutoTarget autotarget;
|
||||
readonly ScriptTags scriptTags;
|
||||
readonly Tooltip[] tooltips;
|
||||
@@ -104,30 +104,11 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
public GeneralProperties(ScriptContext context, Actor self)
|
||||
: base(context, self)
|
||||
{
|
||||
facing = self.TraitOrDefault<IFacing>();
|
||||
autotarget = self.TraitOrDefault<AutoTarget>();
|
||||
scriptTags = self.TraitOrDefault<ScriptTags>();
|
||||
tooltips = self.TraitsImplementing<Tooltip>().ToArray();
|
||||
}
|
||||
|
||||
[Desc("The actor position in cell coordinates.")]
|
||||
public CPos Location => Self.Location;
|
||||
|
||||
[Desc("The actor position in world coordinates.")]
|
||||
public WPos CenterPosition => Self.CenterPosition;
|
||||
|
||||
[Desc("The direction that the actor is facing.")]
|
||||
public WAngle Facing
|
||||
{
|
||||
get
|
||||
{
|
||||
if (facing == null)
|
||||
throw new LuaException($"Actor '{Self}' doesn't define a facing");
|
||||
|
||||
return facing.Facing;
|
||||
}
|
||||
}
|
||||
|
||||
[ScriptActorPropertyActivity]
|
||||
[Desc("Instantly moves the actor to the specified cell.")]
|
||||
public void Teleport(CPos cell)
|
||||
@@ -213,4 +194,32 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
return IsTaggable && scriptTags.HasTag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
[ScriptPropertyGroup("General")]
|
||||
public class LocationProperties : ScriptActorProperties, Requires<IOccupySpaceInfo>
|
||||
{
|
||||
public LocationProperties(ScriptContext context, Actor self)
|
||||
: base(context, self) { }
|
||||
|
||||
[Desc("The actor position in cell coordinates.")]
|
||||
public CPos Location => Self.Location;
|
||||
|
||||
[Desc("The actor position in world coordinates.")]
|
||||
public WPos CenterPosition => Self.CenterPosition;
|
||||
}
|
||||
|
||||
[ScriptPropertyGroup("General")]
|
||||
public class FacingProperties : ScriptActorProperties, Requires<IFacingInfo>
|
||||
{
|
||||
readonly IFacing facing;
|
||||
|
||||
public FacingProperties(ScriptContext context, Actor self)
|
||||
: base(context, self)
|
||||
{
|
||||
facing = self.Trait<IFacing>();
|
||||
}
|
||||
|
||||
[Desc("The direction that the actor is facing.")]
|
||||
public WAngle Facing => facing.Facing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,8 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
[ScriptActorPropertyActivity]
|
||||
[Desc("Mark an objective as failed. This needs the objective ID returned " +
|
||||
"by AddObjective as argument. Secondary objectives do not have any " +
|
||||
"influence whatsoever on the outcome of the game.")]
|
||||
"influence whatsoever on the outcome of the game. " +
|
||||
"It is possible to mark a completed objective as a failure.")]
|
||||
public void MarkFailedObjective(int id)
|
||||
{
|
||||
if (id < 0 || id >= mo.Objectives.Count)
|
||||
|
||||
@@ -87,9 +87,6 @@ namespace OpenRA.Mods.Common.Server
|
||||
[FluentReference("faction")]
|
||||
const string InvalidFactionSelected = "notification-invalid-faction-selected";
|
||||
|
||||
[FluentReference("factions")]
|
||||
const string SupportedFactions = "notification-supported-factions";
|
||||
|
||||
[FluentReference]
|
||||
const string RequiresHost = "notification-requires-host";
|
||||
|
||||
@@ -609,9 +606,8 @@ namespace OpenRA.Mods.Common.Server
|
||||
|
||||
foreach (var c in server.LobbyInfo.Clients)
|
||||
{
|
||||
c.Faction = SanitizePlayerFaction(server, c.Faction, selectableFactions);
|
||||
c.State = Session.ClientState.Invalid;
|
||||
if (!selectableFactions.Contains(c.Faction))
|
||||
c.Faction = "Random";
|
||||
}
|
||||
|
||||
// Reassign players into new slots based on their old slots:
|
||||
@@ -1055,15 +1051,13 @@ namespace OpenRA.Mods.Common.Server
|
||||
if (server.LobbyInfo.Slots[targetClient.Slot].LockFaction)
|
||||
return true;
|
||||
|
||||
var factions = server.Map.WorldActorInfo.TraitInfos<FactionInfo>()
|
||||
.Where(f => f.Selectable).Select(f => f.InternalName)
|
||||
.ToList();
|
||||
|
||||
var faction = parts[1];
|
||||
if (!factions.Contains(faction))
|
||||
var isValidFaction = server.Map.WorldActorInfo.TraitInfos<FactionInfo>()
|
||||
.Any(f => f.Selectable && f.InternalName == client.Faction);
|
||||
|
||||
if (!isValidFaction)
|
||||
{
|
||||
server.SendFluentMessageTo(conn, InvalidFactionSelected, new object[] { "faction", faction });
|
||||
server.SendFluentMessageTo(conn, SupportedFactions, new object[] { "factions", factions.JoinWith(", ") });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1438,6 +1432,11 @@ namespace OpenRA.Mods.Common.Server
|
||||
}
|
||||
}
|
||||
|
||||
public static string SanitizePlayerFaction(S server, string askedFaction, IEnumerable<string> validFactions)
|
||||
{
|
||||
return !validFactions.Contains(askedFaction) ? "Random" : askedFaction;
|
||||
}
|
||||
|
||||
static string MissionBriefingOrDefault(S server)
|
||||
{
|
||||
var missionData = server.Map.WorldActorInfo.TraitInfoOrDefault<MissionDataInfo>();
|
||||
|
||||
@@ -92,12 +92,18 @@ namespace OpenRA.Mods.Common.Server
|
||||
}
|
||||
}
|
||||
|
||||
var selectableFactions = server.Map.WorldActorInfo.TraitInfos<FactionInfo>()
|
||||
.Where(f => f.Selectable)
|
||||
.Select(f => f.InternalName)
|
||||
.ToList();
|
||||
|
||||
var playerNode = nodes.NodeWithKeyOrDefault("Player");
|
||||
if (playerNode != null)
|
||||
{
|
||||
var client = server.GetClient(conn);
|
||||
SkirmishSlot.DeserializeToClient(playerNode.Value, client);
|
||||
client.Color = LobbyCommands.SanitizePlayerColor(server, client.Color, client.Index);
|
||||
client.Faction = LobbyCommands.SanitizePlayerFaction(server, client.Faction, selectableFactions);
|
||||
}
|
||||
|
||||
var botsNode = nodes.NodeWithKeyOrDefault("Bots");
|
||||
@@ -128,6 +134,8 @@ namespace OpenRA.Mods.Common.Server
|
||||
if (client.Slot != null && !server.LobbyInfo.Slots[client.Slot].LockColor)
|
||||
client.Color = LobbyCommands.SanitizePlayerColor(server, client.Color, client.Index);
|
||||
|
||||
client.Faction = LobbyCommands.SanitizePlayerFaction(server, client.Faction, selectableFactions);
|
||||
|
||||
server.LobbyInfo.Clients.Add(client);
|
||||
S.SyncClientToPlayerReference(client, server.Map.Players.Players[client.Slot]);
|
||||
}
|
||||
|
||||
@@ -1325,9 +1325,11 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public override IEnumerable<TargetLineNode> TargetLineNodes(Actor self)
|
||||
{
|
||||
if (ChildActivity != null)
|
||||
foreach (var n in ChildActivity.TargetLineNodes(self))
|
||||
yield return n;
|
||||
var a = ChildActivity;
|
||||
for (; a != null; a = a.NextActivity)
|
||||
if (!a.IsCanceling)
|
||||
foreach (var n in a.TargetLineNodes(self))
|
||||
yield return n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
for (var i = 0; i < updateCount; i++)
|
||||
{
|
||||
var rp = rallyPoints.Pop();
|
||||
if (rp.Actor.Owner == player)
|
||||
if (rp.Actor.Owner == player && !rp.Actor.Disposed)
|
||||
SetRallyPoint(bot, rp);
|
||||
}
|
||||
}
|
||||
@@ -343,23 +343,37 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
Locomotor[] LocomotorsForProducibles(Actor producer)
|
||||
{
|
||||
var buildingInfo = producer.Info.TraitInfoOrDefault<BuildingInfo>();
|
||||
var productionInfo = producer.Info.TraitInfoOrDefault<ProductionInfo>();
|
||||
var locomotors = Array.Empty<Locomotor>();
|
||||
// Per-actor production
|
||||
var productions = producer.TraitsImplementing<Production>();
|
||||
|
||||
if (productionInfo != null && productionInfo.Produces.Length > 0)
|
||||
// Player-wide production
|
||||
if (!productions.Any())
|
||||
productions = producer.World.ActorsWithTrait<Production>().Where(x => x.Actor.Owner != producer.Owner).Select(x => x.Trait);
|
||||
|
||||
var produces = productions.SelectMany(p => p.Info.Produces).ToHashSet();
|
||||
var locomotors = Array.Empty<Locomotor>();
|
||||
if (produces.Count > 0)
|
||||
{
|
||||
var productionQueues = producer.Owner.PlayerActor.TraitsImplementing<ProductionQueue>()
|
||||
.Where(pq => productionInfo.Produces.Contains(pq.Info.Type));
|
||||
// Per-actor production
|
||||
var productionQueues = producer.TraitsImplementing<ProductionQueue>();
|
||||
|
||||
// Player-wide production
|
||||
if (!productionQueues.Any())
|
||||
productionQueues = producer.Owner.PlayerActor.TraitsImplementing<ProductionQueue>();
|
||||
|
||||
productionQueues = productionQueues.Where(pq => produces.Contains(pq.Info.Type));
|
||||
|
||||
var producibles = productionQueues.SelectMany(pq => pq.BuildableItems());
|
||||
var locomotorNames = producibles
|
||||
.Select(p => p.TraitInfoOrDefault<MobileInfo>())
|
||||
.Where(mi => mi != null)
|
||||
.Select(mi => mi.Locomotor)
|
||||
.ToHashSet();
|
||||
locomotors = world.WorldActor.TraitsImplementing<Locomotor>()
|
||||
.Where(l => locomotorNames.Contains(l.Info.Name))
|
||||
.ToArray();
|
||||
|
||||
if (locomotorNames.Count != 0)
|
||||
locomotors = world.WorldActor.TraitsImplementing<Locomotor>()
|
||||
.Where(l => locomotorNames.Contains(l.Info.Name))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
return locomotors;
|
||||
|
||||
@@ -173,7 +173,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
// Returns true if FindNextResource was called.
|
||||
bool HarvestIfAble(IBot bot, HarvesterTraitWrapper h)
|
||||
{
|
||||
if (h.Mobile == null)
|
||||
if (h.Actor.IsDead || !h.Actor.IsInWorld || h.Mobile == null)
|
||||
return false;
|
||||
|
||||
if (!h.Actor.IsIdle)
|
||||
@@ -195,7 +195,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
// Tell the idle harvester to quit slacking:
|
||||
var newSafeResourcePatch = FindNextResource(h.Actor, h);
|
||||
AIUtils.BotDebug($"AI: Harvester {h.Actor} is idle. Ordering to {newSafeResourcePatch} in search for new resources.");
|
||||
if (newSafeResourcePatch != Target.Invalid)
|
||||
if (newSafeResourcePatch.Type != TargetType.Invalid)
|
||||
bot.QueueOrder(new Order("Harvest", h.Actor, newSafeResourcePatch, false));
|
||||
else
|
||||
h.NoResourcesCooldown = Info.ScanIntervalMultiplerWhenNoResources;
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
|
||||
new("Units", FieldSaver.FormatValue(Units.Select(a => a.ActorID).ToArray()))
|
||||
};
|
||||
|
||||
if (Target != Target.Invalid)
|
||||
if (Target.Type != TargetType.Invalid)
|
||||
{
|
||||
nodes.Add(new MiniYamlNode("ActorToTarget", FieldSaver.FormatValue(TargetActor.ActorID)));
|
||||
nodes.Add(new MiniYamlNode("TargetOffset", FieldSaver.FormatValue(Target.CenterPosition - TargetActor.CenterPosition)));
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
Info.EnterCursor,
|
||||
Info.EnterBlockedCursor,
|
||||
() => Info.RequiresForceMove,
|
||||
DockingPossible,
|
||||
CanQueueDockAt,
|
||||
CanDockAt);
|
||||
}
|
||||
}
|
||||
@@ -109,9 +109,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
|
||||
{
|
||||
if (order.OrderString == "Dock" && CanDockAt(order.Target.Actor, false))
|
||||
return Info.Voice;
|
||||
else if (order.OrderString == "ForceDock" && CanDockAt(order.Target.Actor, true))
|
||||
if (order.Target.Type != TargetType.Actor || IsTraitDisabled)
|
||||
return null;
|
||||
|
||||
if (order.OrderString != "Dock" && order.OrderString != "ForceDock")
|
||||
return null;
|
||||
|
||||
if (CanQueueDockAt(order.Target.Actor, order.OrderString == "ForceDock", order.Queued))
|
||||
return Info.Voice;
|
||||
|
||||
return null;
|
||||
@@ -125,12 +129,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Clone of <see cref="DockClientManager.DockingPossible(Actor, bool)"/>.</summary>
|
||||
public bool DockingPossible(Actor target, bool forceEnter)
|
||||
{
|
||||
return !IsTraitDisabled && target.TraitsImplementing<DockHost>().Any(host => dockClients.Any(client => client.CanDock(host.GetDockType)));
|
||||
}
|
||||
|
||||
/// <summary>Clone of <see cref="DockClientManager.CanDockAt(Actor, bool, bool)"/>.</summary>
|
||||
public bool CanDockAt(Actor target, bool forceEnter)
|
||||
{
|
||||
@@ -140,5 +138,16 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return !IsTraitDisabled && target.TraitsImplementing<DockHost>().Any(
|
||||
host => dockClients.Any(client => client.CanDockAt(target, host, forceEnter, true)));
|
||||
}
|
||||
|
||||
/// <summary>Clone of <see cref="DockClientManager.CanQueueDockAt(Actor, bool, bool)"/>.</summary>
|
||||
public bool CanQueueDockAt(Actor target, bool forceEnter, bool isQueued)
|
||||
{
|
||||
if (Info.RequiresForceMove && !forceEnter)
|
||||
return false;
|
||||
|
||||
return (!IsTraitDisabled)
|
||||
&& target.TraitsImplementing<IDockHost>().Any(
|
||||
host => dockClients.Any(client => client.CanQueueDockAt(target, host, forceEnter, isQueued)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (target != currentTarget)
|
||||
{
|
||||
if (currentTargetManager != null)
|
||||
CancelCapture(currentTargetManager);
|
||||
CancelCapture(currentTarget, currentTargetManager);
|
||||
|
||||
targetManager.currentCaptors.Add(self);
|
||||
currentTarget = target;
|
||||
@@ -239,12 +239,11 @@ namespace OpenRA.Mods.Common.Traits
|
||||
/// This method revokes the capturing conditions on the captor and target
|
||||
/// and resets any capturing progress.
|
||||
/// </summary>
|
||||
public void CancelCapture(CaptureManager targetManager)
|
||||
public void CancelCapture(Actor target, CaptureManager targetManager)
|
||||
{
|
||||
if (currentTarget == null)
|
||||
return;
|
||||
|
||||
var target = targetManager.self;
|
||||
foreach (var w in progressWatchers)
|
||||
w.Update(self, self, target, 0, 0);
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Allow deploying on specified charge to grant a condition for a specified duration.")]
|
||||
public class GrantConditionOnDeployWithChargeInfo : PausableConditionalTraitInfo, IRulesetLoaded
|
||||
[Desc("Grant a condition via player orders for a specified amount of time.")]
|
||||
public class GrantChargedConditionOnToggleInfo : PausableConditionalTraitInfo, IRulesetLoaded
|
||||
{
|
||||
[FieldLoader.Require]
|
||||
[GrantedConditionReference]
|
||||
[Desc("The condition to grant after deploying.")]
|
||||
public readonly string DeployedCondition = null;
|
||||
[Desc("The condition to grant when enabled.")]
|
||||
public readonly string ActivatedCondition = null;
|
||||
|
||||
[GrantedConditionReference]
|
||||
[Desc("The condition to grant when charge is above " + nameof(ChargeThreshhold) + ".")]
|
||||
@@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Cooldown (in ticks) to reach full charge.")]
|
||||
public readonly int ChargeDuration = 500;
|
||||
|
||||
[Desc("The amount of charge that needs to be present for deploy to be issued. " +
|
||||
[Desc("The amount of charge that needs to be present to turn on the condition. " +
|
||||
"If set to -1, threshold is set to full charge. " +
|
||||
"If activated without full charge " + nameof(ConditionDuration) + " is percentally smaller.")]
|
||||
public readonly int ChargeThreshhold = -1;
|
||||
@@ -43,30 +43,39 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("How long (in ticks) should the condition stay active?")]
|
||||
public readonly int ConditionDuration = 1;
|
||||
|
||||
[Desc("Can " + nameof(DeployedCondition) + " be canceled by followup deploy order?")]
|
||||
[Desc("Can " + nameof(ActivatedCondition) + " be turned off manually?")]
|
||||
public readonly bool CanCancelCondition = false;
|
||||
|
||||
[CursorReference]
|
||||
[Desc("Cursor to display when able to (un)deploy the actor.")]
|
||||
public readonly string DeployCursor = "deploy";
|
||||
[Desc("Should we interrupt the current activity")]
|
||||
public readonly bool CancelsCurrentActivity = false;
|
||||
|
||||
[CursorReference]
|
||||
[Desc("Cursor to display when unable to (un)deploy the actor.")]
|
||||
public readonly string DeployBlockedCursor = "deploy-blocked";
|
||||
[Desc("Cursor to display when able to trigger a state change.")]
|
||||
public readonly string Cursor = "deploy";
|
||||
|
||||
[Desc("Play a randomly selected sound from this list when deploying.")]
|
||||
public readonly string[] DeploySounds = null;
|
||||
[CursorReference]
|
||||
[Desc("Cursor to display when unable to trigger a state change.")]
|
||||
public readonly string BlockedCursor = "deploy-blocked";
|
||||
|
||||
[Desc("Play a randomly selected sound from this list when undeploying.")]
|
||||
public readonly string[] UndeploySounds = null;
|
||||
[Desc("Play a randomly selected sound from this list when turning on.")]
|
||||
public readonly string[] ActivationSounds = null;
|
||||
|
||||
[Desc("Play a randomly selected sound from this list when turning off.")]
|
||||
public readonly string[] DeactivattionSounds = null;
|
||||
|
||||
[VoiceReference]
|
||||
public readonly string Voice = "Action";
|
||||
|
||||
public readonly Color ChargingColor = Color.Magenta;
|
||||
public readonly Color DeployedColor = Color.DarkMagenta;
|
||||
[Desc("Color of the charge bar when deactivated.")]
|
||||
public readonly Color DeactivatedColor = Color.Magenta;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new GrantConditionOnDeployWithCharge(this); }
|
||||
[Desc("Color of the charge bar when activated.")]
|
||||
public readonly Color ActivatedColor = Color.DarkMagenta;
|
||||
|
||||
[Desc("Should the charge bar be displayed when not charged or the trait is disabled?")]
|
||||
public readonly bool DisplayBarWhenEmpty = true;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new GrantChargedConditionOnToggle(this); }
|
||||
|
||||
public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
|
||||
{
|
||||
@@ -80,37 +89,37 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public class GrantConditionOnDeployWithCharge : PausableConditionalTrait<GrantConditionOnDeployWithChargeInfo>,
|
||||
public class GrantChargedConditionOnToggle : PausableConditionalTrait<GrantChargedConditionOnToggleInfo>,
|
||||
IIssueOrder, IResolveOrder, ITick, ISelectionBar, IOrderVoice, ISync, IIssueDeployOrder
|
||||
{
|
||||
[Sync]
|
||||
int chargeTick = 0;
|
||||
|
||||
bool deployed = false;
|
||||
bool isActive = false;
|
||||
|
||||
int deployedToken = Actor.InvalidConditionToken;
|
||||
int activatedToken = Actor.InvalidConditionToken;
|
||||
|
||||
int chargedToken = Actor.InvalidConditionToken;
|
||||
|
||||
readonly int chargeThreshold;
|
||||
readonly int deployedChargeThreshold;
|
||||
readonly int activatedChargeThreshold;
|
||||
|
||||
public GrantConditionOnDeployWithCharge(GrantConditionOnDeployWithChargeInfo info)
|
||||
public GrantChargedConditionOnToggle(GrantChargedConditionOnToggleInfo info)
|
||||
: base(info)
|
||||
{
|
||||
chargeTick = info.InitialCharge < 0 || info.InitialCharge >= info.ChargeDuration ? Info.ChargeDuration : info.InitialCharge;
|
||||
|
||||
// PERF: Cache the conversions.
|
||||
chargeThreshold = Info.ChargeThreshhold < 0 || Info.ChargeThreshhold > Info.ChargeDuration ? Info.ChargeDuration : Info.ChargeThreshhold;
|
||||
deployedChargeThreshold = chargeThreshold * Info.ConditionDuration / Info.ChargeDuration;
|
||||
activatedChargeThreshold = chargeThreshold * Info.ConditionDuration / Info.ChargeDuration;
|
||||
}
|
||||
|
||||
protected override void TraitDisabled(Actor self)
|
||||
{
|
||||
base.TraitDisabled(self);
|
||||
|
||||
if (deployed)
|
||||
Undeploy(self);
|
||||
if (isActive)
|
||||
Deactivate(self);
|
||||
|
||||
// Reset charge.
|
||||
chargeTick = Info.InitialCharge < 0 || Info.InitialCharge > Info.ChargeDuration ? Info.ChargeDuration : Info.InitialCharge;
|
||||
@@ -120,7 +129,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public string VoicePhraseForOrder(Actor self, Order order)
|
||||
{
|
||||
return order.OrderString == "DeployWithCharge" ? Info.Voice : null;
|
||||
return order.OrderString == "ActivateCondition" ? Info.Voice : null;
|
||||
}
|
||||
|
||||
public IEnumerable<IOrderTargeter> Orders
|
||||
@@ -128,14 +137,14 @@ namespace OpenRA.Mods.Common.Traits
|
||||
get
|
||||
{
|
||||
if (!IsTraitDisabled)
|
||||
yield return new DeployOrderTargeter("DeployWithCharge", 5,
|
||||
() => CanDeploy() ? Info.DeployCursor : Info.DeployBlockedCursor);
|
||||
yield return new DeployOrderTargeter("ActivateCondition", 5,
|
||||
() => CanToggle() ? Info.Cursor : Info.BlockedCursor);
|
||||
}
|
||||
}
|
||||
|
||||
public Order IssueOrder(Actor self, IOrderTargeter order, in Target target, bool queued)
|
||||
{
|
||||
if (order.OrderID == "DeployWithCharge")
|
||||
if (order.OrderID == "ActivateCondition")
|
||||
return new Order(order.OrderID, self, queued);
|
||||
|
||||
return null;
|
||||
@@ -143,55 +152,60 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
Order IIssueDeployOrder.IssueDeployOrder(Actor self, bool queued)
|
||||
{
|
||||
return new Order("DeployWithCharge", self, queued);
|
||||
return new Order("ActivateCondition", self, queued);
|
||||
}
|
||||
|
||||
bool IIssueDeployOrder.CanIssueDeployOrder(Actor self, bool queued) { return queued || CanDeploy(); }
|
||||
bool IIssueDeployOrder.CanIssueDeployOrder(Actor self, bool queued) { return queued || CanToggle(); }
|
||||
|
||||
public void ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
if (order.OrderString == "DeployWithCharge")
|
||||
self.QueueActivity(order.Queued, new DeployForGrantedConditionWithCharge(self, this));
|
||||
if (order.OrderString != "ActivateCondition")
|
||||
return;
|
||||
|
||||
if (order.Queued || Info.CancelsCurrentActivity)
|
||||
self.QueueActivity(order.Queued, new ToggleChargedCondition(self, this));
|
||||
else if (CanToggle())
|
||||
ToggleState(self);
|
||||
}
|
||||
|
||||
public bool CanDeploy() => !IsTraitDisabled && !IsTraitPaused && ((!deployed && chargeTick >= chargeThreshold) || (deployed && Info.CanCancelCondition));
|
||||
public bool CanToggle() => !IsTraitDisabled && !IsTraitPaused && ((!isActive && chargeTick >= chargeThreshold) || (isActive && Info.CanCancelCondition));
|
||||
|
||||
public void TriggerDeploy(Actor self)
|
||||
public void ToggleState(Actor self)
|
||||
{
|
||||
if (deployed)
|
||||
if (isActive)
|
||||
{
|
||||
// Keep the percentage of the unused charge.
|
||||
chargeTick = chargeTick * Info.ChargeDuration / Info.ConditionDuration;
|
||||
Undeploy(self);
|
||||
Deactivate(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If deployed without full charge, reduce the deploy duration.
|
||||
// If activated without full charge, subtract from the activated duration.
|
||||
chargeTick = chargeTick * Info.ConditionDuration / Info.ChargeDuration;
|
||||
Deploy(self);
|
||||
Activate(self);
|
||||
}
|
||||
}
|
||||
|
||||
void Deploy(Actor self)
|
||||
void Activate(Actor self)
|
||||
{
|
||||
if (Info.DeploySounds != null && Info.DeploySounds.Length > 0)
|
||||
Game.Sound.Play(SoundType.World, Info.DeploySounds, self.World, self.CenterPosition);
|
||||
if (Info.ActivationSounds != null && Info.ActivationSounds.Length > 0)
|
||||
Game.Sound.Play(SoundType.World, Info.ActivationSounds, self.World, self.CenterPosition);
|
||||
|
||||
if (deployedToken == Actor.InvalidConditionToken)
|
||||
deployedToken = self.GrantCondition(Info.DeployedCondition);
|
||||
if (activatedToken == Actor.InvalidConditionToken)
|
||||
activatedToken = self.GrantCondition(Info.ActivatedCondition);
|
||||
|
||||
deployed = true;
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
void Undeploy(Actor self)
|
||||
void Deactivate(Actor self)
|
||||
{
|
||||
if (Info.UndeploySounds != null && Info.UndeploySounds.Length > 0)
|
||||
Game.Sound.Play(SoundType.World, Info.UndeploySounds, self.World, self.CenterPosition);
|
||||
if (Info.DeactivattionSounds != null && Info.DeactivattionSounds.Length > 0)
|
||||
Game.Sound.Play(SoundType.World, Info.DeactivattionSounds, self.World, self.CenterPosition);
|
||||
|
||||
if (deployedToken != Actor.InvalidConditionToken)
|
||||
deployedToken = self.RevokeCondition(deployedToken);
|
||||
if (activatedToken != Actor.InvalidConditionToken)
|
||||
activatedToken = self.RevokeCondition(activatedToken);
|
||||
|
||||
deployed = false;
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
void ITick.Tick(Actor self)
|
||||
@@ -199,12 +213,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (IsTraitDisabled || IsTraitPaused)
|
||||
return;
|
||||
|
||||
if (deployed)
|
||||
if (isActive)
|
||||
{
|
||||
if (chargeTick > 0)
|
||||
chargeTick--;
|
||||
else
|
||||
Undeploy(self);
|
||||
Deactivate(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -214,7 +228,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
if (Info.ChargedCondition != null)
|
||||
{
|
||||
if (chargeTick < (deployed ? deployedChargeThreshold : chargeThreshold))
|
||||
if (chargeTick < (isActive ? activatedChargeThreshold : chargeThreshold))
|
||||
{
|
||||
if (chargedToken != Actor.InvalidConditionToken)
|
||||
chargedToken = self.RevokeCondition(chargedToken);
|
||||
@@ -232,28 +246,28 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (IsTraitDisabled)
|
||||
return 0f;
|
||||
|
||||
return deployed
|
||||
return isActive
|
||||
? (float)chargeTick / Info.ConditionDuration
|
||||
: (float)chargeTick / Info.ChargeDuration;
|
||||
}
|
||||
|
||||
Color ISelectionBar.GetColor() { return deployed ? Info.DeployedColor : Info.ChargingColor; }
|
||||
bool ISelectionBar.DisplayWhenEmpty => false;
|
||||
Color ISelectionBar.GetColor() { return isActive ? Info.ActivatedColor : Info.DeactivatedColor; }
|
||||
bool ISelectionBar.DisplayWhenEmpty => Info.DisplayBarWhenEmpty;
|
||||
}
|
||||
|
||||
public class DeployForGrantedConditionWithCharge : Activity
|
||||
public class ToggleChargedCondition : Activity
|
||||
{
|
||||
readonly GrantConditionOnDeployWithCharge deploy;
|
||||
readonly GrantChargedConditionOnToggle toggle;
|
||||
|
||||
public DeployForGrantedConditionWithCharge(Actor self, GrantConditionOnDeployWithCharge deploy)
|
||||
public ToggleChargedCondition(Actor self, GrantChargedConditionOnToggle toggle)
|
||||
{
|
||||
this.deploy = deploy;
|
||||
this.toggle = toggle;
|
||||
}
|
||||
|
||||
protected override void OnFirstRun(Actor self)
|
||||
{
|
||||
if (deploy.CanDeploy())
|
||||
deploy.TriggerDeploy(self);
|
||||
if (toggle.CanToggle())
|
||||
toggle.ToggleState(self);
|
||||
}
|
||||
|
||||
public override IEnumerable<TargetLineNode> TargetLineNodes(Actor self)
|
||||
@@ -37,9 +37,14 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public virtual bool CanDockAt(Actor hostActor, IDockHost host, bool forceEnter = false, bool ignoreOccupancy = false)
|
||||
{
|
||||
return (forceEnter || self.Owner.IsAlliedWith(hostActor.Owner)) &&
|
||||
CanDock(host.GetDockType, forceEnter) &&
|
||||
host.IsDockingPossible(self, this, ignoreOccupancy);
|
||||
return CanDock(host.GetDockType, forceEnter)
|
||||
&& host.IsDockingPossible(self, this, ignoreOccupancy);
|
||||
}
|
||||
|
||||
public virtual bool CanQueueDockAt(Actor hostActor, IDockHost host, bool forceEnter, bool isQueued)
|
||||
{
|
||||
return CanDock(host.GetDockType, true)
|
||||
&& host.IsDockingPossible(self, this, true);
|
||||
}
|
||||
|
||||
public virtual void OnDockStarted(Actor self, Actor hostActor, IDockHost host) { }
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
Info.EnterCursor,
|
||||
Info.EnterBlockedCursor,
|
||||
() => requireForceMove,
|
||||
DockingPossible,
|
||||
CanQueueDockAt,
|
||||
(target, forceEnter) => CanDockAt(target, forceEnter, true));
|
||||
}
|
||||
}
|
||||
@@ -194,9 +194,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (order.Target.Type != TargetType.Actor || IsTraitDisabled)
|
||||
return null;
|
||||
|
||||
if (order.OrderString == "Dock" && CanDockAt(order.Target.Actor, false, true))
|
||||
return Info.Voice;
|
||||
else if (order.OrderString == "ForceDock" && CanDockAt(order.Target.Actor, true, true))
|
||||
if (order.OrderString != "Dock" && order.OrderString != "ForceDock")
|
||||
return null;
|
||||
|
||||
if (CanQueueDockAt(order.Target.Actor, order.OrderString == "ForceDock", order.Queued))
|
||||
return Info.Voice;
|
||||
|
||||
return null;
|
||||
@@ -225,13 +226,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
/// <summary>Do we have an enabled client with matching <paramref name="type"/>.</summary>
|
||||
public bool DockingPossible(BitSet<DockType> type, bool forceEnter = false)
|
||||
public bool CanDock(BitSet<DockType> type, bool forceEnter = false)
|
||||
{
|
||||
return !IsTraitDisabled && dockClients.Any(client => client.CanDock(type, forceEnter));
|
||||
}
|
||||
|
||||
/// <summary>Does this <paramref name="target"/> contain at least one enabled <see cref="IDockHost"/> with maching <see cref="DockType"/>.</summary>
|
||||
public bool DockingPossible(Actor target, bool forceEnter = false)
|
||||
public bool CanDock(Actor target, bool forceEnter = false)
|
||||
{
|
||||
return !IsTraitDisabled &&
|
||||
target.TraitsImplementing<IDockHost>()
|
||||
@@ -252,6 +253,14 @@ namespace OpenRA.Mods.Common.Traits
|
||||
host => dockClients.Any(client => client.CanDockAt(target, host, forceEnter, ignoreOccupancy)));
|
||||
}
|
||||
|
||||
/// <summary>Can we dock to this <paramref name="target"/>.</summary>
|
||||
public bool CanQueueDockAt(Actor target, bool forceEnter, bool isQueued)
|
||||
{
|
||||
return !IsTraitDisabled
|
||||
&& target.TraitsImplementing<IDockHost>()
|
||||
.Any(host => dockClients.Any(client => client.CanQueueDockAt(target, host, forceEnter, isQueued)));
|
||||
}
|
||||
|
||||
/// <summary>Find the closest viable <see cref="IDockHost"/>.</summary>
|
||||
/// <remarks>If <paramref name="type"/> is not set, scans all clients. Does not check if <see cref="DockClientManager"/> is enabled.</remarks>
|
||||
public TraitPair<IDockHost>? ClosestDock(IDockHost ignore, BitSet<DockType> type = default, bool forceEnter = false, bool ignoreOccupancy = false)
|
||||
@@ -292,11 +301,11 @@ namespace OpenRA.Mods.Common.Traits
|
||||
readonly string enterCursor;
|
||||
readonly string enterBlockedCursor;
|
||||
readonly Func<bool> requireForceMove;
|
||||
readonly Func<Actor, bool, bool> canTarget;
|
||||
readonly Func<Actor, bool, bool, bool> canTarget;
|
||||
readonly Func<Actor, bool, bool> useEnterCursor;
|
||||
|
||||
public DockActorTargeter(int priority, string enterCursor, string enterBlockedCursor,
|
||||
Func<bool> requireForceMove, Func<Actor, bool, bool> canTarget, Func<Actor, bool, bool> useEnterCursor)
|
||||
Func<bool> requireForceMove, Func<Actor, bool, bool, bool> canTarget, Func<Actor, bool, bool> useEnterCursor)
|
||||
{
|
||||
OrderID = "Dock";
|
||||
OrderPriority = priority;
|
||||
@@ -325,10 +334,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (requireForceMove() && !forceEnter)
|
||||
return false;
|
||||
|
||||
if (!canTarget(target.Actor, forceEnter))
|
||||
if (!canTarget(target.Actor, forceEnter, IsQueued))
|
||||
return false;
|
||||
|
||||
cursor = useEnterCursor(target.Actor, forceEnter) ? enterCursor : enterBlockedCursor;
|
||||
cursor = IsQueued || useEnterCursor(target.Actor, forceEnter)
|
||||
? enterCursor
|
||||
: enterBlockedCursor;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -137,6 +137,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
&& (self.Owner == hostActor.Owner || (ignoreOccupancy && self.Owner.IsAlliedWith(hostActor.Owner)));
|
||||
}
|
||||
|
||||
public override bool CanQueueDockAt(Actor hostActor, IDockHost host, bool forceEnter, bool isQueued)
|
||||
{
|
||||
return base.CanQueueDockAt(hostActor, host, forceEnter, isQueued)
|
||||
&& self.Owner.IsAlliedWith(hostActor.Owner);
|
||||
}
|
||||
|
||||
void UpdateCondition(Actor self)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Info.EmptyCondition))
|
||||
|
||||
@@ -87,11 +87,11 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
bool IsValidActor(Actor target)
|
||||
{
|
||||
var instantlyRepairable = target.Info.TraitInfoOrDefault<InstantlyRepairableInfo>();
|
||||
if (instantlyRepairable == null)
|
||||
var instantlyRepairable = target.TraitOrDefault<InstantlyRepairable>();
|
||||
if (instantlyRepairable == null || instantlyRepairable.IsTraitDisabled)
|
||||
return false;
|
||||
|
||||
if (!instantlyRepairable.Types.IsEmpty && !instantlyRepairable.Types.Overlaps(Info.Types))
|
||||
if (!instantlyRepairable.Info.Types.IsEmpty && !instantlyRepairable.Info.Types.Overlaps(Info.Types))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1000,6 +1000,23 @@ namespace OpenRA.Mods.Common.Traits
|
||||
foreach (var cell in rallyPoint)
|
||||
QueueChild(new AttackMoveActivity(self, () => mobile.MoveTo(cell, 1, evaluateNearestMovableCell: true, targetLineColor: Color.OrangeRed)));
|
||||
}
|
||||
|
||||
public override IEnumerable<Target> GetTargets(Actor self)
|
||||
{
|
||||
if (ChildActivity != null)
|
||||
return ChildActivity.GetTargets(self);
|
||||
|
||||
return Target.None;
|
||||
}
|
||||
|
||||
public override IEnumerable<TargetLineNode> TargetLineNodes(Actor self)
|
||||
{
|
||||
var a = ChildActivity;
|
||||
for (; a != null; a = a.NextActivity)
|
||||
if (!a.IsCanceling)
|
||||
foreach (var n in a.TargetLineNodes(self))
|
||||
yield return n;
|
||||
}
|
||||
}
|
||||
|
||||
Activity ICreationActivity.GetCreationActivity()
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
protected override void PrepareRender(WorldRenderer wr, IShader shader)
|
||||
{
|
||||
shader.SetVec("Blend", blend);
|
||||
shader.SetVec("Color", (float)Info.Color.B / 255, (float)Info.Color.G / 255, (float)Info.Color.R / 255);
|
||||
shader.SetVec("Color", (float)Info.Color.R / 255, (float)Info.Color.G / 255, (float)Info.Color.B / 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,12 +174,11 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
void UpdateNetWorth(string oldResourceType, int oldDensity, string newResourceType, int newDensity)
|
||||
{
|
||||
// Density + 1 as workaround for fixing ResourceLayer.Harvest as it would be very disruptive to balancing
|
||||
if (oldResourceType != null && oldDensity > 0 && resourceValues.TryGetValue(oldResourceType, out var oldResourceValue))
|
||||
NetWorth -= (oldDensity + 1) * oldResourceValue;
|
||||
NetWorth -= oldDensity * oldResourceValue;
|
||||
|
||||
if (newResourceType != null && newDensity > 0 && resourceValues.TryGetValue(newResourceType, out var newResourceValue))
|
||||
NetWorth += (newDensity + 1) * newResourceValue;
|
||||
NetWorth += newDensity * newResourceValue;
|
||||
}
|
||||
|
||||
public int CalculateRegionValue(CellRegion sourceRegion)
|
||||
@@ -188,39 +187,44 @@ namespace OpenRA.Mods.Common.Traits
|
||||
foreach (var cell in sourceRegion.CellCoords)
|
||||
{
|
||||
var mcell = cell.ToMPos(Map);
|
||||
if (Map.Resources.Contains(mcell) && Map.Resources[mcell].Type != 0)
|
||||
{
|
||||
resourceValueInRegion++;
|
||||
var rcell = Map.Resources[mcell];
|
||||
if (ResourceTypesByIndex.TryGetValue(rcell.Type, out var resourceType) && resourceValues.TryGetValue(resourceType, out var resourceValuePerUnit))
|
||||
resourceValueInRegion += Tiles[mcell].Density * resourceValuePerUnit;
|
||||
}
|
||||
if (!Map.Resources.Contains(mcell))
|
||||
continue;
|
||||
|
||||
var resource = Map.Resources[mcell].Type;
|
||||
if (resource != 0
|
||||
&& ResourceTypesByIndex.TryGetValue(resource, out var resourceType)
|
||||
&& resourceValues.TryGetValue(resourceType, out var resourceValuePerUnit))
|
||||
resourceValueInRegion += Tiles[mcell].Density * resourceValuePerUnit;
|
||||
}
|
||||
|
||||
return resourceValueInRegion;
|
||||
}
|
||||
|
||||
protected virtual int CalculateCellDensity(ResourceLayerContents contents, CPos c)
|
||||
/// <summary>
|
||||
/// Matches the logic in <see cref="ResourceLayer"/> trait.
|
||||
/// </summary>
|
||||
protected virtual int CalculateCellDensity(ResourceLayerContents contents, CPos cell)
|
||||
{
|
||||
var resources = Map.Resources;
|
||||
if (contents.Type == null || !info.ResourceTypes.TryGetValue(contents.Type, out var resourceInfo) || resources[c].Type != resourceInfo.ResourceIndex)
|
||||
if (contents.Type == null || !info.ResourceTypes.TryGetValue(contents.Type, out var resourceInfo) || resources[cell].Type != resourceInfo.ResourceIndex)
|
||||
return 0;
|
||||
|
||||
if (!info.RecalculateResourceDensity)
|
||||
return contents.Density.Clamp(1, resourceInfo.MaxDensity);
|
||||
|
||||
// Set density based on the number of neighboring resources
|
||||
// Set density based on the number of neighboring resources.
|
||||
var adjacent = 0;
|
||||
for (var u = -1; u < 2; u++)
|
||||
var directions = CVec.Directions;
|
||||
for (var i = 0; i < directions.Length; i++)
|
||||
{
|
||||
for (var v = -1; v < 2; v++)
|
||||
{
|
||||
var cell = c + new CVec(u, v);
|
||||
if (resources.Contains(cell) && resources[cell].Type == resourceInfo.ResourceIndex)
|
||||
adjacent++;
|
||||
}
|
||||
var c = cell + directions[i];
|
||||
if (resources.Contains(c) && resources[c].Type == resourceInfo.ResourceIndex)
|
||||
++adjacent;
|
||||
}
|
||||
|
||||
// We need to have at least one resource in the cell.
|
||||
// HACK: we should not be lerping to 9, as maximum adjacent resources is 8.
|
||||
// HACK: it's too disruptive to fix.
|
||||
return Math.Max(int2.Lerp(0, resourceInfo.MaxDensity, adjacent, 9), 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public abstract class RenderPostProcessPassBase : IRenderPostProcessPass
|
||||
public abstract class RenderPostProcessPassBase : IRenderPostProcessPass, INotifyActorDisposing
|
||||
{
|
||||
readonly Renderer renderer;
|
||||
readonly IShader shader;
|
||||
@@ -52,5 +52,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
protected abstract bool Enabled { get; }
|
||||
protected abstract void PrepareRender(WorldRenderer wr, IShader shader);
|
||||
|
||||
void INotifyActorDisposing.Disposing(Actor self)
|
||||
{
|
||||
buffer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
++adjacent;
|
||||
}
|
||||
|
||||
// Adjacent includes the current cell, so is always >= 1
|
||||
// We need to have at least one resource in the cell.
|
||||
// HACK: we should not be lerping to 9, as maximum adjacent resources is 8.
|
||||
// HACK: it's too disruptive to fix.
|
||||
var density = Math.Max(int2.Lerp(0, resourceInfo.MaxDensity, adjacent, 9), 1);
|
||||
Content[cell] = new ResourceLayerContents(resource.Type, density);
|
||||
}
|
||||
|
||||
@@ -220,19 +220,26 @@ namespace OpenRA.Mods.Common.Traits
|
||||
bool OnDockTick(Actor self, Actor hostActor, IDockHost dock);
|
||||
void OnDockCompleted(Actor self, Actor hostActor, IDockHost host);
|
||||
|
||||
/// <summary>Is this client allowed to dock.</summary>
|
||||
/// <summary>Are we allowed to dock.</summary>
|
||||
/// <remarks>
|
||||
/// Does not check if <see cref="Traits.DockClientManager"/> is enabled.
|
||||
/// Function should only be called from within <see cref="IDockClient"/> or <see cref="Traits.DockClientManager"/>.
|
||||
/// </remarks>
|
||||
bool CanDock(BitSet<DockType> type, bool forceEnter = false);
|
||||
|
||||
/// <summary>Is this client allowed to dock to <paramref name="host"/>.</summary>
|
||||
/// <summary>Are we allowed to dock to this <paramref name="host"/>.</summary>
|
||||
/// <remarks>
|
||||
/// Does not check if <see cref="Traits.DockClientManager"/> is enabled.
|
||||
/// Function should only be called from within <see cref="IDockClient"/> or <see cref="Traits.DockClientManager"/>.
|
||||
/// </remarks>
|
||||
bool CanDockAt(Actor hostActor, IDockHost host, bool forceEnter = false, bool ignoreOccupancy = false);
|
||||
|
||||
/// <summary>Are we allowed to give a docking order for this <paramref name="host"/>.</summary>
|
||||
/// <remarks>
|
||||
/// Does not check if <see cref="Traits.DockClientManager"/> is enabled.
|
||||
/// Function should only be called from within <see cref="IDockClient"/> or <see cref="Traits.DockClientManager"/>.
|
||||
/// </remarks>
|
||||
bool CanQueueDockAt(Actor hostActor, IDockHost host, bool forceEnter, bool isQueued);
|
||||
}
|
||||
|
||||
public interface IDockHostInfo : ITraitInfoInterface { }
|
||||
|
||||
@@ -35,8 +35,9 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
var palette = new ImmutablePalette(args[1], new[] { 0 }, Array.Empty<int>());
|
||||
|
||||
SequenceSet sequences;
|
||||
if (modData.DefaultTerrainInfo.ContainsKey(args[2]))
|
||||
sequences = new SequenceSet(modData.ModFiles, modData, args[2], null);
|
||||
var tilesetUpper = args[2].ToUpperInvariant();
|
||||
if (modData.DefaultTerrainInfo.ContainsKey(tilesetUpper))
|
||||
sequences = new SequenceSet(modData.ModFiles, modData, tilesetUpper, null);
|
||||
else
|
||||
{
|
||||
var mapPackage = new Folder(Platform.EngineDir).OpenPackage(args[2], modData.ModFiles);
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
|
||||
foreach (var layout in chromeLayouts)
|
||||
{
|
||||
var fluentFolder = layout.Key + "|languages";
|
||||
var fluentFolder = layout.Key + "|fluent";
|
||||
var fluentPackage = modData.ModFiles.OpenPackage(fluentFolder);
|
||||
var fluentPath = Path.Combine(fluentPackage.Name, "chrome.ftl");
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
type = type.Replace("text", "");
|
||||
}
|
||||
|
||||
build += $" .{type} = {candidate.Value}\n";
|
||||
build += $" .{type} = {candidate.Value}\n";
|
||||
foreach (var node in candidate.Nodes)
|
||||
node.Value.Value = $"{key}.{type}";
|
||||
}
|
||||
@@ -262,12 +262,23 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
&& !UpdateUtils.IsAlreadyExtracted(childNode.Value.Value)
|
||||
&& childNode.Value.Value.Any(char.IsLetterOrDigit))
|
||||
{
|
||||
var value = childNode.Value.Value
|
||||
.Replace("\\n", "\n ")
|
||||
var replaced = false;
|
||||
var value = childNode.Value.Value;
|
||||
if (value.Contains("\\n"))
|
||||
{
|
||||
value = value.Replace("\\n", "\n ");
|
||||
replaced = true;
|
||||
}
|
||||
|
||||
value = value
|
||||
.Replace("{", "<")
|
||||
.Replace("}", ">")
|
||||
.Trim().Trim('\n');
|
||||
|
||||
// Preserve indentation
|
||||
if (replaced)
|
||||
value = "\n " + value;
|
||||
|
||||
validChildTypes.Add((childNode, childType.ToLowerInvariant(), value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
}
|
||||
}
|
||||
|
||||
var fluentPackage = modData.ModFiles.OpenPackage(modData.Manifest.Id + "|languages");
|
||||
var fluentPackage = modData.ModFiles.OpenPackage(modData.Manifest.Id + "|fluent");
|
||||
ExtractFromFile(Path.Combine(fluentPackage.Name, "rules.ftl"), modRules, traitInfos);
|
||||
modRules.Save();
|
||||
|
||||
@@ -214,7 +214,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
foreach (var candidate in grouping)
|
||||
{
|
||||
var type = candidate.Key;
|
||||
build += $" .{type} = {candidate.Value}\n";
|
||||
build += $" .{type} = {candidate.Value}\n";
|
||||
|
||||
foreach (var node in candidate.Nodes)
|
||||
node.Value.Value = $"{key}.{type}";
|
||||
@@ -307,9 +307,16 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
if (string.IsNullOrEmpty(propertyValue) || UpdateUtils.IsAlreadyExtracted(propertyValue) || !propertyValue.Any(char.IsLetterOrDigit))
|
||||
continue;
|
||||
|
||||
var value = propertyValue
|
||||
.Replace("\\n", "\n ")
|
||||
.Trim().Trim('\n');
|
||||
var replaced = false;
|
||||
var value = propertyValue;
|
||||
if (value.Contains("\\n"))
|
||||
{
|
||||
value = value.Replace("\\n", "\n ");
|
||||
replaced = true;
|
||||
}
|
||||
|
||||
// Preserve indentation
|
||||
value = (replaced ? "\n " : "") + value.Trim().Trim('\n');
|
||||
|
||||
var actorName = ToLowerActor(actor.Key);
|
||||
var key = traitInfo;
|
||||
|
||||
@@ -179,5 +179,11 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
OnChange();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Removed()
|
||||
{
|
||||
mixerSheet?.Dispose();
|
||||
base.Removed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
Sprite hueSprite;
|
||||
Sprite pickerSprite;
|
||||
Sheet hueSheet;
|
||||
|
||||
public HueSliderWidget() { }
|
||||
public HueSliderWidget(HueSliderWidget other)
|
||||
@@ -28,7 +29,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
base.Initialize(args);
|
||||
|
||||
var hueSheet = new Sheet(SheetType.BGRA, new Size(256, 1));
|
||||
hueSheet = new Sheet(SheetType.BGRA, new Size(256, 1));
|
||||
|
||||
var buffer = new byte[4 * 256];
|
||||
|
||||
@@ -63,5 +64,11 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
var pos = RenderOrigin + new int2(PxFromValue(Value).Clamp(0, rb.Width - 1) - (int)pickerSprite.Size.X / 2, (rb.Height - (int)pickerSprite.Size.Y) / 2);
|
||||
WidgetUtils.DrawSprite(pickerSprite, pos);
|
||||
}
|
||||
|
||||
public override void Removed()
|
||||
{
|
||||
hueSheet?.Dispose();
|
||||
base.Removed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
public LabelWidget DiagonalLabel;
|
||||
public LabelWidget ResourceCounterLabel;
|
||||
|
||||
MapBlitFilters copyFilters = MapBlitFilters.All;
|
||||
MapBlitFilters selectionFilters = MapBlitFilters.All;
|
||||
EditorBlitSource? clipboard;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
@@ -81,12 +81,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
worldRenderer,
|
||||
clipboard.Value,
|
||||
resourceLayer,
|
||||
() => copyFilters));
|
||||
() => selectionFilters));
|
||||
};
|
||||
|
||||
pasteButton.IsDisabled = () => clipboard == null;
|
||||
pasteButton.IsHighlighted = () => editor.CurrentBrush is EditorCopyPasteBrush;
|
||||
|
||||
var deleteAreaSelectionButton = areaEditPanel.Get<ButtonWidget>("SELECTION_DELETE_BUTTON");
|
||||
deleteAreaSelectionButton.OnClick = () => editor.DefaultBrush.DeleteSelection(selectionFilters);
|
||||
|
||||
var closeAreaSelectionButton = areaEditPanel.Get<ButtonWidget>("SELECTION_CANCEL_BUTTON");
|
||||
closeAreaSelectionButton.OnClick = () => editor.DefaultBrush.ClearSelection(updateSelectedTab: true);
|
||||
|
||||
@@ -102,15 +105,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
editorActorLayer,
|
||||
resourceLayer,
|
||||
editor.DefaultBrush.Selection.Area,
|
||||
copyFilters);
|
||||
selectionFilters);
|
||||
}
|
||||
|
||||
void CreateCategoryPanel(MapBlitFilters copyFilter, CheckboxWidget checkbox)
|
||||
{
|
||||
checkbox.GetText = () => copyFilter.ToString();
|
||||
checkbox.IsChecked = () => copyFilters.HasFlag(copyFilter);
|
||||
checkbox.IsChecked = () => selectionFilters.HasFlag(copyFilter);
|
||||
checkbox.IsVisible = () => true;
|
||||
checkbox.OnClick = () => copyFilters ^= copyFilter;
|
||||
checkbox.OnClick = () => selectionFilters ^= copyFilter;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
readonly LabelWidget productionTime;
|
||||
readonly Widget productionPowerIcon;
|
||||
readonly LabelWidget productionPower;
|
||||
readonly List<Sheet> sheets = new();
|
||||
|
||||
ActorInfo selectedActor;
|
||||
ScrollItemWidget firstItem;
|
||||
@@ -81,6 +82,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
defaultPortrait = new Png(modData.DefaultFileSystem.Open("encyclopedia/default.png"));
|
||||
var spriteBounds = new Rectangle(0, 0, defaultPortrait.Width, defaultPortrait.Height);
|
||||
var sheet = new Sheet(SheetType.BGRA, spriteBounds.Size.NextPowerOf2());
|
||||
sheets.Add(sheet);
|
||||
sheet.CreateBuffer();
|
||||
sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
|
||||
portraitSprite = new Sprite(sheet, spriteBounds, TextureChannel.RGBA);
|
||||
@@ -304,5 +306,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
time = time * bi.BuildDurationModifier * pbi / 10000;
|
||||
return time;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
foreach (var sheet in sheets)
|
||||
sheet.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -751,7 +751,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
}
|
||||
else
|
||||
{
|
||||
LobbyUtils.SetupNameWidget(template, client, orderManager, worldRenderer);
|
||||
LobbyUtils.SetupNameWidget(template, client, orderManager, worldRenderer, map);
|
||||
LobbyUtils.SetupTeamWidget(template, client);
|
||||
LobbyUtils.SetupHandicapWidget(template, client);
|
||||
LobbyUtils.SetupSpawnWidget(template, client);
|
||||
@@ -803,7 +803,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
LobbyUtils.SetupPlayerActionWidget(template, client, orderManager, worldRenderer,
|
||||
lobby, () => panel = PanelType.Kick, () => panel = PanelType.Players);
|
||||
else
|
||||
LobbyUtils.SetupNameWidget(template, client, orderManager, worldRenderer);
|
||||
LobbyUtils.SetupNameWidget(template, client, orderManager, worldRenderer, map);
|
||||
|
||||
if (client.IsAdmin)
|
||||
LobbyUtils.SetupReadyWidget(template, client);
|
||||
|
||||
@@ -33,6 +33,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
[FluentReference]
|
||||
const string Bots = "options-lobby-slot.bots";
|
||||
|
||||
[FluentReference]
|
||||
const string BotPlayer = "label-bot-player";
|
||||
|
||||
[FluentReference]
|
||||
const string BotsDisabled = "options-lobby-slot.bots-disabled";
|
||||
|
||||
@@ -75,7 +78,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
foreach (var b in map.PlayerActorInfo.TraitInfos<IBotInfo>())
|
||||
{
|
||||
var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
|
||||
bots.Add(new SlotDropDownOption(FluentProvider.GetMessage(b.Name),
|
||||
bots.Add(new SlotDropDownOption(map.GetMessage(b.Name),
|
||||
$"slot_bot {slot.PlayerReference} {botController.Index} {b.Type}",
|
||||
() => client != null && client.Bot == b.Type));
|
||||
}
|
||||
@@ -425,14 +428,22 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
HideChildWidget(parent, "SLOT_OPTIONS");
|
||||
}
|
||||
|
||||
public static void SetupNameWidget(Widget parent, Session.Client c, OrderManager orderManager, WorldRenderer worldRenderer)
|
||||
public static void SetupNameWidget(Widget parent, Session.Client c, OrderManager orderManager, WorldRenderer worldRenderer, MapPreview map)
|
||||
{
|
||||
var label = parent.Get<LabelWidget>("NAME");
|
||||
label.IsVisible = () => true;
|
||||
var font = Game.Renderer.Fonts[label.Font];
|
||||
var name = c.IsBot ? FluentProvider.GetMessage(c.Name) : c.Name;
|
||||
var text = WidgetUtils.TruncateText(name, label.Bounds.Width, font);
|
||||
label.GetText = () => text;
|
||||
|
||||
var clientName = new CachedTransform<MapStatus, string>(s =>
|
||||
{
|
||||
var name = c.Name;
|
||||
if (c.IsBot && !map.TryGetMessage(c.Name, out name))
|
||||
name = FluentProvider.GetMessage(BotPlayer);
|
||||
|
||||
return WidgetUtils.TruncateText(name, label.Bounds.Width, font);
|
||||
});
|
||||
|
||||
label.GetText = () => clientName.Update(map.Status);
|
||||
|
||||
SetupProfileWidget(parent, c, orderManager, worldRenderer);
|
||||
}
|
||||
@@ -450,9 +461,23 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
var closed = FluentProvider.GetMessage(Closed);
|
||||
var open = FluentProvider.GetMessage(Open);
|
||||
|
||||
var clientName = new CachedTransform<MapStatus, string>(s =>
|
||||
{
|
||||
if (c.IsBot)
|
||||
{
|
||||
if (map.TryGetMessage(c.Name, out var message))
|
||||
return message;
|
||||
else
|
||||
return FluentProvider.GetMessage(BotPlayer);
|
||||
}
|
||||
|
||||
return c.Name;
|
||||
});
|
||||
|
||||
slot.GetText = () => truncated.Update(c != null ?
|
||||
c.IsBot ? FluentProvider.GetMessage(c.Name) : c.Name
|
||||
: s.Closed ? closed : open);
|
||||
clientName.Update(map.Status)
|
||||
: s.Closed ? closed : open);
|
||||
|
||||
slot.OnMouseDown = _ => ShowSlotDropDown(slot, s, c, orderManager, map, modData);
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
protected MenuType menuType = MenuType.Main;
|
||||
readonly Widget rootMenu;
|
||||
readonly ScrollPanelWidget newsPanel;
|
||||
readonly int maxNewsHeight;
|
||||
readonly Widget newsTemplate;
|
||||
readonly LabelWidget newsStatus;
|
||||
readonly ModData modData;
|
||||
@@ -230,6 +231,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
newsPanel = Ui.LoadWidget<ScrollPanelWidget>("NEWS_PANEL", null, new WidgetArgs());
|
||||
newsTemplate = newsPanel.Get("NEWS_ITEM_TEMPLATE");
|
||||
newsPanel.RemoveChild(newsTemplate);
|
||||
maxNewsHeight = newsPanel.Bounds.Height;
|
||||
|
||||
newsStatus = newsPanel.Get<LabelWidget>("NEWS_STATUS");
|
||||
SetNewsStatus(FluentProvider.GetMessage(LoadingNews));
|
||||
@@ -416,10 +418,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
newsPanel.RemoveChildren();
|
||||
SetNewsStatus("");
|
||||
|
||||
foreach (var i in newsItems)
|
||||
foreach (var item in newsItems)
|
||||
{
|
||||
var item = i;
|
||||
|
||||
var newsItem = newsTemplate.Clone();
|
||||
|
||||
var titleLabel = newsItem.Get<LabelWidget>("TITLE");
|
||||
@@ -441,6 +441,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
newsPanel.AddChild(newsItem);
|
||||
newsPanel.Layout.AdjustChildren();
|
||||
newsPanel.Bounds.Height = Math.Min(newsPanel.ContentHeight, maxNewsHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,18 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
readonly ScrollItemWidget headerTemplate;
|
||||
readonly ScrollItemWidget template;
|
||||
|
||||
readonly Widget miniOptions;
|
||||
readonly DropDownButtonWidget difficultyButton;
|
||||
readonly DropDownButtonWidget gameSpeedButton;
|
||||
readonly string unsetDifficulty;
|
||||
readonly string defaultTooltop;
|
||||
|
||||
// For remembering options
|
||||
// TODO: this should be persistent across game sessions
|
||||
string selectedDifficulty;
|
||||
string selectedGameSpeed;
|
||||
|
||||
bool minifiedOptions = true;
|
||||
MapPreview selectedMap;
|
||||
PlayingVideo playingVideo;
|
||||
readonly Dictionary<string, string> missionOptions = new();
|
||||
@@ -125,6 +137,16 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
stopInfoVideoButton.IsVisible = () => playingVideo == PlayingVideo.Info;
|
||||
stopInfoVideoButton.OnClick = () => StopVideo(videoPlayer);
|
||||
|
||||
miniOptions = widget.GetOrNull("MISSION_MINIFIED_OPTIONS");
|
||||
if (miniOptions != null)
|
||||
{
|
||||
miniOptions.IsVisible = () => minifiedOptions;
|
||||
difficultyButton = miniOptions.GetOrNull<DropDownButtonWidget>("DIFFICULTY");
|
||||
gameSpeedButton = miniOptions.GetOrNull<DropDownButtonWidget>("GAMESPEED");
|
||||
unsetDifficulty = FluentProvider.GetMessage(difficultyButton.Text);
|
||||
defaultTooltop = FluentProvider.GetMessage(difficultyButton.TooltipText);
|
||||
}
|
||||
|
||||
var allPreviews = new List<MapPreview>();
|
||||
missionList.RemoveChildren();
|
||||
|
||||
@@ -204,16 +226,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
};
|
||||
|
||||
var tabContainer = widget.Get("MISSION_TABS");
|
||||
tabContainer.IsVisible = () => true;
|
||||
tabContainer.IsVisible = () => !minifiedOptions;
|
||||
|
||||
var optionsTab = tabContainer.Get<ButtonWidget>("OPTIONS_TAB");
|
||||
optionsTab.IsHighlighted = () => panel == PanelType.Options;
|
||||
optionsTab.IsDisabled = () => false;
|
||||
optionsTab.OnClick = () => panel = PanelType.Options;
|
||||
|
||||
var missionTab = tabContainer.Get<ButtonWidget>("MISSIONINFO_TAB");
|
||||
missionTab.IsHighlighted = () => panel == PanelType.MissionInfo;
|
||||
missionTab.IsDisabled = () => false;
|
||||
missionTab.OnClick = () => panel = PanelType.MissionInfo;
|
||||
}
|
||||
|
||||
@@ -319,6 +339,44 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
.Where(o => o.IsVisible)
|
||||
.OrderBy(o => o.DisplayOrder).ToArray();
|
||||
|
||||
minifiedOptions = allOptions.All(o => o.Id == "difficulty" || o.Id == "gamespeed");
|
||||
if (minifiedOptions)
|
||||
BuildMinifiedOptions(allOptions);
|
||||
else
|
||||
BuildOptions(allOptions);
|
||||
}
|
||||
|
||||
void SetMapDifficulty(LobbyOption option)
|
||||
{
|
||||
selectedDifficulty ??= option.DefaultValue;
|
||||
if (option.Values.ContainsKey(selectedDifficulty))
|
||||
missionOptions[option.Id] = selectedDifficulty;
|
||||
else
|
||||
missionOptions[option.Id] = option.DefaultValue;
|
||||
}
|
||||
|
||||
void SetMapSpeed(LobbyOption option)
|
||||
{
|
||||
selectedGameSpeed ??= option.DefaultValue;
|
||||
if (option.Values.ContainsKey(selectedGameSpeed))
|
||||
missionOptions[option.Id] = selectedGameSpeed;
|
||||
else
|
||||
missionOptions[option.Id] = option.DefaultValue;
|
||||
}
|
||||
|
||||
void OnOptionSelected(string optionId, string value)
|
||||
{
|
||||
// Only remember when the user manually changes the value
|
||||
if (optionId == "difficulty")
|
||||
selectedDifficulty = value;
|
||||
else if (optionId == "gamespeed")
|
||||
selectedGameSpeed = value;
|
||||
|
||||
missionOptions[optionId] = value;
|
||||
}
|
||||
|
||||
void BuildOptions(LobbyOption[] allOptions)
|
||||
{
|
||||
Widget row = null;
|
||||
var checkboxColumns = new Queue<CheckboxWidget>();
|
||||
var dropdownColumns = new Queue<DropDownButtonWidget>();
|
||||
@@ -364,7 +422,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
foreach (var option in allOptions.Where(o => o is not LobbyBooleanOption))
|
||||
{
|
||||
missionOptions[option.Id] = option.DefaultValue;
|
||||
if (option.Id == "difficulty")
|
||||
SetMapDifficulty(option);
|
||||
else if (option.Id == "gamespeed")
|
||||
SetMapSpeed(option);
|
||||
else
|
||||
missionOptions[option.Id] = option.DefaultValue;
|
||||
|
||||
if (dropdownColumns.Count == 0)
|
||||
{
|
||||
@@ -378,42 +441,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
optionsContainer.AddChild(row);
|
||||
}
|
||||
|
||||
var dropdown = dropdownColumns.Dequeue();
|
||||
var val = dropdownColumns.Dequeue();
|
||||
SetupDropdown(val, option);
|
||||
|
||||
dropdown.GetText = () =>
|
||||
{
|
||||
if (option.Values.TryGetValue(missionOptions[option.Id], out var value))
|
||||
return value;
|
||||
|
||||
return FluentProvider.GetMessage(NotAvailable);
|
||||
};
|
||||
|
||||
if (option.Description != null)
|
||||
{
|
||||
var (text, desc) = LobbyUtils.SplitOnFirstToken(option.Description);
|
||||
dropdown.GetTooltipText = () => text;
|
||||
dropdown.GetTooltipDesc = () => desc;
|
||||
}
|
||||
|
||||
dropdown.IsVisible = () => true;
|
||||
dropdown.IsDisabled = () => option.IsLocked;
|
||||
|
||||
dropdown.OnMouseDown = _ =>
|
||||
{
|
||||
ScrollItemWidget SetupItem(KeyValuePair<string, string> c, ScrollItemWidget template)
|
||||
{
|
||||
bool IsSelected() => missionOptions[option.Id] == c.Key;
|
||||
void OnClick() => missionOptions[option.Id] = c.Key;
|
||||
|
||||
var item = ScrollItemWidget.Setup(template, IsSelected, OnClick);
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => c.Value;
|
||||
return item;
|
||||
}
|
||||
|
||||
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", option.Values.Count * 30, option.Values, SetupItem);
|
||||
};
|
||||
|
||||
var label = row.GetOrNull<LabelWidget>(dropdown.Id + "_DESC");
|
||||
var label = row.GetOrNull<LabelWidget>(val.Id + "_DESC");
|
||||
if (label != null)
|
||||
{
|
||||
label.GetText = () => option.Name + ":";
|
||||
@@ -422,6 +453,76 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
}
|
||||
}
|
||||
|
||||
void BuildMinifiedOptions(LobbyOption[] allOptions)
|
||||
{
|
||||
if (difficultyButton != null)
|
||||
{
|
||||
var mapDifficulty = allOptions.FirstOrDefault(sld => sld.Id == "difficulty");
|
||||
if (mapDifficulty != null)
|
||||
{
|
||||
SetMapDifficulty(mapDifficulty);
|
||||
SetupDropdown(difficultyButton, mapDifficulty);
|
||||
}
|
||||
else
|
||||
{
|
||||
difficultyButton.IsDisabled = () => true;
|
||||
difficultyButton.GetText = () => unsetDifficulty;
|
||||
difficultyButton.GetTooltipText = () => defaultTooltop;
|
||||
}
|
||||
}
|
||||
|
||||
if (gameSpeedButton != null)
|
||||
{
|
||||
var gameSpeed = allOptions.FirstOrDefault(sld => sld.Id == "gamespeed");
|
||||
if (gameSpeed != null)
|
||||
{
|
||||
SetMapSpeed(gameSpeed);
|
||||
SetupDropdown(gameSpeedButton, gameSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
gameSpeedButton.IsDisabled = () => true;
|
||||
gameSpeedButton.GetText = () => FluentProvider.GetMessage(NotAvailable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetupDropdown(DropDownButtonWidget dropdown, LobbyOption option)
|
||||
{
|
||||
dropdown.GetText = () =>
|
||||
{
|
||||
if (option.Values.TryGetValue(missionOptions[option.Id], out var value))
|
||||
return value;
|
||||
|
||||
return FluentProvider.GetMessage(NotAvailable);
|
||||
};
|
||||
|
||||
if (option.Description != null)
|
||||
{
|
||||
var (text, desc) = LobbyUtils.SplitOnFirstToken(option.Description);
|
||||
dropdown.GetTooltipText = () => text;
|
||||
dropdown.GetTooltipDesc = () => desc;
|
||||
}
|
||||
|
||||
dropdown.IsVisible = () => true;
|
||||
dropdown.IsDisabled = () => option.IsLocked;
|
||||
|
||||
dropdown.OnMouseDown = _ =>
|
||||
{
|
||||
ScrollItemWidget SetupItem(KeyValuePair<string, string> c, ScrollItemWidget template)
|
||||
{
|
||||
bool IsSelected() => missionOptions[option.Id] == c.Key;
|
||||
void OnClick() => OnOptionSelected(option.Id, c.Key);
|
||||
|
||||
var item = ScrollItemWidget.Setup(template, IsSelected, OnClick);
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => c.Value;
|
||||
return item;
|
||||
}
|
||||
|
||||
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", option.Values.Count * 30, option.Values, SetupItem);
|
||||
};
|
||||
}
|
||||
|
||||
float cachedSoundVolume;
|
||||
float cachedMusicVolume;
|
||||
void MuteSounds()
|
||||
|
||||
@@ -14,6 +14,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BeaconLib;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Server;
|
||||
@@ -49,6 +50,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
[FluentReference("bots")]
|
||||
const string BotsLabel = "label-bots-count";
|
||||
|
||||
[FluentReference]
|
||||
const string BotPlayer = "label-bot-player";
|
||||
|
||||
[FluentReference("spectators")]
|
||||
const string SpectatorsLabel = "label-spectators-count";
|
||||
|
||||
@@ -604,14 +608,21 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
foreach (var option in kv.Value)
|
||||
{
|
||||
var o = option;
|
||||
var playerName = new CachedTransform<(MapStatus, int, SpriteFont), string>(s =>
|
||||
{
|
||||
var name = o.IsBot
|
||||
? currentMap.TryGetMessage(o.Name, out var msg) ? msg : FluentProvider.GetMessage(BotPlayer)
|
||||
: o.Name;
|
||||
|
||||
return WidgetUtils.TruncateText(name, s.Item2, s.Item3);
|
||||
});
|
||||
|
||||
var item = ScrollItemWidget.Setup(clientTemplate, () => false, () => { });
|
||||
if (!o.IsSpectator && server.Mod == modData.Manifest.Id)
|
||||
{
|
||||
var label = item.Get<LabelWidget>("LABEL");
|
||||
var font = Game.Renderer.Fonts[label.Font];
|
||||
var name = WidgetUtils.TruncateText(o.Name, label.Bounds.Width, font);
|
||||
label.GetText = () => name;
|
||||
label.GetText = () => playerName.Update((currentMap.Status, label.Bounds.Width, font));
|
||||
label.GetColor = () => o.Color;
|
||||
|
||||
var flag = item.Get<ImageWidget>("FLAG");
|
||||
@@ -623,11 +634,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
var label = item.Get<LabelWidget>("NOFLAG_LABEL");
|
||||
var font = Game.Renderer.Fonts[label.Font];
|
||||
var name = WidgetUtils.TruncateText(o.Name, label.Bounds.Width, font);
|
||||
|
||||
// Force spectator color to prevent spoofing by the server
|
||||
var color = o.IsSpectator ? Color.White : o.Color;
|
||||
label.GetText = () => name;
|
||||
label.GetText = () => playerName.Update((currentMap.Status, label.Bounds.Width, font));
|
||||
label.GetColor = () => color;
|
||||
}
|
||||
|
||||
@@ -761,14 +771,22 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
if (game.Clients.Length > 0)
|
||||
{
|
||||
var displayClients = game.Clients.Select(c => c.Name);
|
||||
if (game.Clients.Length > 10)
|
||||
displayClients = displayClients
|
||||
.Take(9)
|
||||
.Append(FluentProvider.GetMessage(OtherPlayers, "players", game.Clients.Length - 9));
|
||||
var preview = modData.MapCache[game.Map];
|
||||
var tooltip = new CachedTransform<MapStatus, string>(s =>
|
||||
{
|
||||
var displayClients = game.Clients.Select(c => c.IsBot
|
||||
? preview.TryGetMessage(c.Name, out var msg) ? msg : FluentProvider.GetMessage(BotPlayer)
|
||||
: c.Name);
|
||||
|
||||
var tooltip = displayClients.JoinWith("\n");
|
||||
players.GetTooltipText = () => tooltip;
|
||||
if (game.Clients.Length > 10)
|
||||
displayClients = displayClients
|
||||
.Take(9)
|
||||
.Append(FluentProvider.GetMessage(OtherPlayers, "players", game.Clients.Length - 9));
|
||||
|
||||
return displayClients.JoinWith("\n");
|
||||
});
|
||||
|
||||
players.GetTooltipText = () => tooltip.Update(preview.Status);
|
||||
}
|
||||
else
|
||||
players.GetTooltipText = null;
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
readonly Stopwatch playTime = new();
|
||||
int textureWidth;
|
||||
int textureHeight;
|
||||
Sheet videoSheet;
|
||||
|
||||
Action onComplete;
|
||||
|
||||
@@ -123,7 +124,8 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
textureWidth = Exts.NextPowerOf2(video.Width);
|
||||
textureHeight = Exts.NextPowerOf2(video.Height);
|
||||
var videoSheet = new Sheet(SheetType.BGRA, new Size(textureWidth, textureHeight));
|
||||
videoSheet?.Dispose();
|
||||
videoSheet = new Sheet(SheetType.BGRA, new Size(textureWidth, textureHeight));
|
||||
|
||||
videoSheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
|
||||
videoSheet.GetTexture().SetData(video.CurrentFrameData, textureWidth, textureHeight);
|
||||
@@ -301,5 +303,11 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
Stop();
|
||||
Video = null;
|
||||
}
|
||||
|
||||
public override void Removed()
|
||||
{
|
||||
videoSheet?.Dispose();
|
||||
overlaySheet?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj" />
|
||||
<PackageReference Include="OpenRA-Freetype6" Version="1.0.11" />
|
||||
<PackageReference Include="OpenRA-OpenAL-CS" Version="1.0.22" />
|
||||
<PackageReference Include="OpenRA-SDL2-CS" Version="1.0.41" />
|
||||
<PackageReference Include="OpenRA-SDL2-CS" Version="1.0.42" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="OpenRA.Platforms.Default.dll.config" Condition="'$(TargetPlatform)' != 'win-x64' And '$(TargetPlatform)' != 'win-x86'">
|
||||
|
||||
@@ -341,7 +341,15 @@ namespace OpenRA.Platforms.Default
|
||||
// Run graphics rendering on a dedicated thread.
|
||||
// The calling thread will then have more time to process other tasks, since rendering happens in parallel.
|
||||
// If the calling thread is the main game thread, this means it can run more logic and render ticks.
|
||||
Context = new ThreadedGraphicsContext(new Sdl2GraphicsContext(this), vertexBatchSize, indexBatchSize);
|
||||
// This is disabled when running in windowed mode on Windows because it breaks the ability to minimize/restore the window.
|
||||
if (Platform.CurrentPlatform == PlatformType.Windows && windowMode == WindowMode.Windowed)
|
||||
{
|
||||
var ctx = new Sdl2GraphicsContext(this);
|
||||
ctx.InitializeOpenGL();
|
||||
Context = ctx;
|
||||
}
|
||||
else
|
||||
Context = new ThreadedGraphicsContext(new Sdl2GraphicsContext(this), vertexBatchSize, indexBatchSize);
|
||||
|
||||
Context.SetVSyncEnabled(Game.Settings.Graphics.VSync);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenRA-SDL2-CS" Version="1.0.41" />
|
||||
<PackageReference Include="OpenRA-SDL2-CS" Version="1.0.42" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj" />
|
||||
|
||||
@@ -73,26 +73,26 @@ vec4 linear2srgb(vec4 c)
|
||||
return c.a * vec4(linear2srgb(c.r / c.a), linear2srgb(c.g / c.a), linear2srgb(c.b / c.a), 1.0f);
|
||||
}
|
||||
|
||||
ivec2 Size(uint samplerIndex)
|
||||
vec2 Size(uint samplerIndex)
|
||||
{
|
||||
switch (samplerIndex)
|
||||
{
|
||||
case 7u:
|
||||
return textureSize(Texture7, 0);
|
||||
return vec2(textureSize(Texture7, 0));
|
||||
case 6u:
|
||||
return textureSize(Texture6, 0);
|
||||
return vec2(textureSize(Texture6, 0));
|
||||
case 5u:
|
||||
return textureSize(Texture5, 0);
|
||||
return vec2(textureSize(Texture5, 0));
|
||||
case 4u:
|
||||
return textureSize(Texture4, 0);
|
||||
return vec2(textureSize(Texture4, 0));
|
||||
case 3u:
|
||||
return textureSize(Texture3, 0);
|
||||
return vec2(textureSize(Texture3, 0));
|
||||
case 2u:
|
||||
return textureSize(Texture2, 0);
|
||||
return vec2(textureSize(Texture2, 0));
|
||||
case 1u:
|
||||
return textureSize(Texture1, 0);
|
||||
return vec2(textureSize(Texture1, 0));
|
||||
default:
|
||||
return textureSize(Texture0, 0);
|
||||
return vec2(textureSize(Texture0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ void main()
|
||||
vec4 c;
|
||||
if (EnablePixelArtScaling)
|
||||
{
|
||||
vec2 textureSize = vec2(Size(vChannelSampler));
|
||||
vec2 textureSize = Size(vChannelSampler);
|
||||
vec2 vUv = coords.st * textureSize;
|
||||
vec2 offset = fract(vUv);
|
||||
vec2 pixelsPerTexel = vec2(1.0 / dFdx(vUv.x), 1.0 / dFdy(vUv.y));
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
Metadata:
|
||||
Title: All mods
|
||||
Version: {DEV_VERSION}
|
||||
Version: release-20250330
|
||||
Hidden: true
|
||||
|
||||
FileSystem: DefaultFileSystem
|
||||
Packages:
|
||||
Packages:
|
||||
^EngineDir
|
||||
|
||||
Cursors:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Metadata:
|
||||
Title: mod-title
|
||||
Version: {DEV_VERSION}
|
||||
Version: release-20250330
|
||||
Hidden: true
|
||||
|
||||
FileSystem: DefaultFileSystem
|
||||
|
||||
@@ -284,29 +284,27 @@ ScrollPanel@NEWS_PANEL:
|
||||
Width: 400
|
||||
Height: 265
|
||||
Background: panel-black
|
||||
TopBottomSpacing: 5
|
||||
TopBottomSpacing: 10
|
||||
ItemSpacing: 5
|
||||
Children:
|
||||
Container@NEWS_ITEM_TEMPLATE:
|
||||
X: 10
|
||||
Y: 5
|
||||
Width: PARENT_WIDTH - 40
|
||||
Height: 45
|
||||
Height: 40
|
||||
Children:
|
||||
Label@TITLE:
|
||||
Y: 1
|
||||
Width: PARENT_WIDTH
|
||||
Height: 25
|
||||
Height: 20
|
||||
Align: Center
|
||||
Font: Bold
|
||||
Label@AUTHOR_DATETIME:
|
||||
Y: 26
|
||||
Y: 21
|
||||
Width: PARENT_WIDTH
|
||||
Height: 15
|
||||
Align: Center
|
||||
Font: TinyBold
|
||||
Label@CONTENT:
|
||||
Y: 46
|
||||
Y: 40
|
||||
Width: PARENT_WIDTH
|
||||
Label@NEWS_STATUS:
|
||||
X: 80
|
||||
|
||||
@@ -439,7 +439,7 @@ Container@EDITOR_WORLD_ROOT:
|
||||
Y: 1
|
||||
Width: 55
|
||||
Height: 25
|
||||
Text: label-tools-bg-categories
|
||||
Text: label-tool-bg-category
|
||||
Align: Right
|
||||
Font: TinyBold
|
||||
DropDownButton@TOOLS_DROPDOWN:
|
||||
@@ -634,7 +634,7 @@ Container@EDITOR_WORLD_ROOT:
|
||||
Height: 25
|
||||
Font: Bold
|
||||
Align: Left
|
||||
Text: label-copy-filters
|
||||
Text: label-selection-filters
|
||||
Checkbox@COPY_FILTER_TERRAIN_CHECKBOX:
|
||||
X: 7
|
||||
Y: 70
|
||||
@@ -688,6 +688,17 @@ Container@EDITOR_WORLD_ROOT:
|
||||
Width: 55
|
||||
Height: 22
|
||||
Align: Left
|
||||
Button@SELECTION_DELETE_BUTTON:
|
||||
X: 7
|
||||
Y: 222
|
||||
Width: 75
|
||||
Height: 25
|
||||
Text: button-delete-area.label
|
||||
Font: Bold
|
||||
Key: EditorDeleteSelection
|
||||
TooltipTemplate: BUTTON_TOOLTIP
|
||||
TooltipText: button-delete-area.tooltip
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
Button@SELECTION_CANCEL_BUTTON:
|
||||
X: 208
|
||||
Y: 222
|
||||
@@ -779,8 +790,12 @@ Container@EDITOR_WORLD_ROOT:
|
||||
X: 7
|
||||
Width: 75
|
||||
Height: 25
|
||||
Text: button-container-delete
|
||||
Text: button-delete-actor.label
|
||||
Font: Bold
|
||||
Key: EditorDeleteSelection
|
||||
TooltipTemplate: BUTTON_TOOLTIP
|
||||
TooltipText: button-delete-actor.tooltip
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
Button@CANCEL_BUTTON:
|
||||
X: 127
|
||||
Width: 75
|
||||
|
||||
@@ -12,7 +12,7 @@ Container@SKIRMISH_STATS:
|
||||
Width: 80
|
||||
Height: 20
|
||||
Font: MediumBold
|
||||
Text: label-objective-mission
|
||||
Text: label-mission-objective
|
||||
Label@STATS_STATUS:
|
||||
X: 95
|
||||
Y: 17
|
||||
@@ -25,7 +25,7 @@ Container@SKIRMISH_STATS:
|
||||
Width: 482
|
||||
Height: 20
|
||||
Font: Bold
|
||||
Text: checkbox-objective-stats
|
||||
Text: checkbox-stats-objective
|
||||
Disabled: true
|
||||
TextColorDisabled: FFFFFF
|
||||
Container@STATS_HEADERS:
|
||||
|
||||
@@ -489,7 +489,7 @@ Container@OBSERVER_WIDGETS:
|
||||
Align: Right
|
||||
Shadow: True
|
||||
Label@DERRICKS_HEADER:
|
||||
X: 650
|
||||
X: 645
|
||||
Width: 80
|
||||
Height: PARENT_HEIGHT
|
||||
Font: Bold
|
||||
|
||||
@@ -16,14 +16,14 @@ Background@KICK_CLIENT_DIALOG:
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Align: Center
|
||||
Text: label-kick-client-dialog-texta
|
||||
Text: label-kick-client-dialog-text-a
|
||||
Label@TEXTB:
|
||||
Y: 86
|
||||
Width: PARENT_WIDTH
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Align: Center
|
||||
Text: label-kick-client-dialog-textb
|
||||
Text: label-kick-client-dialog-text-b
|
||||
Checkbox@PREVENT_REJOINING_CHECKBOX:
|
||||
X: (PARENT_WIDTH - WIDTH) / 2
|
||||
Y: 120
|
||||
@@ -98,14 +98,14 @@ Background@FORCE_START_DIALOG:
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Align: Center
|
||||
Text: label-force-start-dialog-texta
|
||||
Text: label-force-start-dialog-text-a
|
||||
Label@TEXTB:
|
||||
Y: 86
|
||||
Width: PARENT_WIDTH
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Align: Center
|
||||
Text: label-force-start-dialog-textb
|
||||
Text: label-force-start-dialog-text-b
|
||||
Container@KICK_WARNING:
|
||||
Width: PARENT_WIDTH
|
||||
Children:
|
||||
|
||||
@@ -256,8 +256,8 @@ Container@LOBBY_PLAYER_BIN:
|
||||
Height: 25
|
||||
Text: label-lobby-players-faction
|
||||
Label@TEAM:
|
||||
X: 435
|
||||
Width: 44
|
||||
X: 425
|
||||
Width: 45
|
||||
Height: 25
|
||||
Align: Center
|
||||
DropDownButton@TEAM_DROPDOWN:
|
||||
|
||||
@@ -68,6 +68,44 @@ Container@MISSIONBROWSER_PANEL:
|
||||
IgnoreMouseOver: True
|
||||
IgnoreMouseInput: True
|
||||
ShowSpawnPoints: False
|
||||
Container@MISSION_MINIFIED_OPTIONS:
|
||||
Width: PARENT_WIDTH
|
||||
Height: PARENT_HEIGHT
|
||||
Visible: False
|
||||
Children:
|
||||
LabelForInput@DIFFICULTY_DESC:
|
||||
Y: PARENT_HEIGHT - HEIGHT + 2
|
||||
Width: 56
|
||||
Height: 25
|
||||
Align: Right
|
||||
Text: dropdown-missionbrowser-difficulty.label
|
||||
For: DIFFICULTY
|
||||
DropDownButton@DIFFICULTY:
|
||||
X: 61
|
||||
Y: PARENT_HEIGHT - HEIGHT + 2
|
||||
Width: 120
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Text: label-missionbrowser-normal-difficulty
|
||||
TooltipText: dropdown-missionbrowser-difficulty.description
|
||||
PanelRoot: MISSION_DROPDOWN_PANEL_ROOT
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
LabelForInput@GAMESPEED_DESC:
|
||||
X: PARENT_WIDTH - WIDTH - 125
|
||||
Y: PARENT_HEIGHT - HEIGHT + 2
|
||||
Width: 120
|
||||
Height: 25
|
||||
Align: Right
|
||||
Text: dropdown-missionbrowser-gamespeed
|
||||
For: GAMESPEED
|
||||
DropDownButton@GAMESPEED:
|
||||
X: PARENT_WIDTH - WIDTH
|
||||
Y: PARENT_HEIGHT - HEIGHT + 2
|
||||
Width: 120
|
||||
Height: 25
|
||||
Font: Regular
|
||||
PanelRoot: MISSION_DROPDOWN_PANEL_ROOT
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
Container@MISSION_TABS:
|
||||
Width: PARENT_WIDTH
|
||||
Y: PARENT_HEIGHT - 31
|
||||
|
||||
@@ -90,7 +90,7 @@ Container@ADVANCED_PANEL:
|
||||
Font: Regular
|
||||
Text: checkbox-sendsysinfo-container
|
||||
Label@SENDSYSINFO_DESC:
|
||||
Y: 15
|
||||
Y: 30
|
||||
Width: PARENT_WIDTH
|
||||
Height: 30
|
||||
Font: Tiny
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## world
|
||||
dropdown-difficulty =
|
||||
.label = Difficulty
|
||||
.description = The difficulty of the mission.
|
||||
.description = The difficulty of the mission
|
||||
|
||||
options-difficulty =
|
||||
.easy = Easy
|
||||
@@ -10,7 +10,7 @@ options-difficulty =
|
||||
|
||||
## player
|
||||
bot-campaign-ai =
|
||||
.name = Campaign Player AI
|
||||
.name = Campaign Player AI
|
||||
|
||||
## campaign-maprules.yaml
|
||||
actor-moneycrate-name = Money Crate
|
||||
|
||||
@@ -55,7 +55,6 @@ label-save-map-background-directory = Directory:
|
||||
label-save-map-background-filename = Filename:
|
||||
button-save-map-panel = Save
|
||||
label-actor-edit-panel-id = ID
|
||||
button-container-delete = Delete
|
||||
button-container-ok = OK
|
||||
button-editor-world-root-options-tooltip = Menu
|
||||
label-tiles-bg-search = Search:
|
||||
@@ -65,12 +64,12 @@ label-actor-bg-owner = Owner:
|
||||
label-area-selection = Area Selection
|
||||
label-area-info = Area Info
|
||||
label-selected-area-diagonal = Diagonal:
|
||||
label-selected-area-resources= Resources:
|
||||
label-copy-filters = Copy Filters
|
||||
label-selected-area-resources = Resources:
|
||||
label-selection-filters = Filters
|
||||
label-filter-terrain = Terrain
|
||||
label-filter-resources = Resources
|
||||
label-filter-actors = Actors
|
||||
label-tools-bg-categories = Tool:
|
||||
label-tool-bg-category = Tool:
|
||||
button-marker-tiles-clear-current = Clear Current
|
||||
button-marker-tiles-clear-all = Clear All
|
||||
label-marker-layer-num-sides = Number of Sides
|
||||
@@ -85,21 +84,29 @@ button-map-editor-tab-container-actors-tooltip = Actors
|
||||
button-map-editor-tab-container-tools-tooltip = Tools
|
||||
button-map-editor-tab-container-history-tooltip = History
|
||||
|
||||
button-delete-area =
|
||||
.label = Delete
|
||||
.tooltip = Remove everything in the selected area
|
||||
|
||||
button-delete-actor =
|
||||
.label = Delete
|
||||
.tooltip = Remove the selected actor
|
||||
|
||||
button-editor-world-root-copy =
|
||||
.label = Copy
|
||||
.tooltip = Copy the selected area
|
||||
.label = Copy
|
||||
.tooltip = Copy the selected area
|
||||
|
||||
button-editor-world-root-paste =
|
||||
.label = Paste
|
||||
.tooltip = Paste the saved area
|
||||
.label = Paste
|
||||
.tooltip = Paste the saved area
|
||||
|
||||
button-editor-world-root-undo =
|
||||
.label = Undo
|
||||
.tooltip = Undo last step
|
||||
.label = Undo
|
||||
.tooltip = Undo last step
|
||||
|
||||
button-editor-world-root-redo =
|
||||
.label = Redo
|
||||
.tooltip = Redo last step
|
||||
.label = Redo
|
||||
.tooltip = Redo last step
|
||||
|
||||
dropdownbutton-editor-world-root-overlay-button = Overlays
|
||||
button-select-categories-buttons-all = All
|
||||
@@ -126,8 +133,8 @@ label-gamesave-loading-screen-desc = Press Escape to cancel loading and return t
|
||||
|
||||
## ingame-chat.yaml, ingame-infochat.yaml
|
||||
button-chat-chrome-mode =
|
||||
.label = Team
|
||||
.tooltip = Toggle chat mode
|
||||
.label = Team
|
||||
.tooltip = Toggle chat mode
|
||||
|
||||
## ingame-debug.yaml
|
||||
label-debug-panel-title = Debug Options
|
||||
@@ -166,8 +173,8 @@ label-script-error-panel-desc-b = The details of the error have been saved to lu
|
||||
label-script-error-panel-desc-c = Please send this file to the map author so that they can fix this issue.
|
||||
|
||||
## ingame-infostats.yaml
|
||||
label-objective-mission = Mission:
|
||||
checkbox-objective-stats = Destroy all opposition!
|
||||
label-mission-objective = Mission:
|
||||
checkbox-stats-objective = Destroy all opposition!
|
||||
label-stats-player = Player
|
||||
label-stats-faction = Faction
|
||||
label-stats-score = Score
|
||||
@@ -179,20 +186,20 @@ button-replay-player-pause-tooltip = Pause
|
||||
button-replay-player-play-tooltip = Play
|
||||
|
||||
button-replay-player-slow =
|
||||
.tooltip = Slow speed
|
||||
.label = 50%
|
||||
.tooltip = Slow speed
|
||||
.label = 50%
|
||||
|
||||
button-replay-player-regular =
|
||||
.tooltip = Regular speed
|
||||
.label = 100%
|
||||
.tooltip = Regular speed
|
||||
.label = 100%
|
||||
|
||||
button-replay-player-fast =
|
||||
.tooltip = Fast speed
|
||||
.label = 200%
|
||||
.tooltip = Fast speed
|
||||
.label = 200%
|
||||
|
||||
button-replay-player-maximum =
|
||||
.tooltip = Maximum speed
|
||||
.label = MAX
|
||||
.tooltip = Maximum speed
|
||||
.label = MAX
|
||||
|
||||
label-basic-stats-player-header = Player
|
||||
label-basic-stats-cash-header = Cash
|
||||
@@ -228,12 +235,13 @@ label-combat-stats-army-value-header = Army Value
|
||||
label-combat-stats-vision-header = Vision
|
||||
|
||||
supportpowers-support-powers-palette =
|
||||
.ready = Ready
|
||||
.hold = On Hold
|
||||
.ready = Ready
|
||||
.hold = On Hold
|
||||
|
||||
button-command-bar-attack-move =
|
||||
.tooltip = Attack Move
|
||||
.tooltipdesc = Selected units will move to the desired location
|
||||
.tooltip = Attack Move
|
||||
.tooltipdesc =
|
||||
Selected units will move to the desired location
|
||||
and attack any enemies they encounter en route.
|
||||
|
||||
Hold <(Ctrl)> while targeting to order an Assault Move
|
||||
@@ -242,8 +250,9 @@ button-command-bar-attack-move =
|
||||
Left-click icon then right-click on target location.
|
||||
|
||||
button-command-bar-force-move =
|
||||
.tooltip = Force Move
|
||||
.tooltipdesc = Selected units will move to the desired location
|
||||
.tooltip = Force Move
|
||||
.tooltipdesc =
|
||||
Selected units will move to the desired location
|
||||
- Default activity for the target is suppressed
|
||||
- Vehicles will attempt to crush enemies at the target location
|
||||
- Helicopters will land at the target location
|
||||
@@ -252,8 +261,9 @@ button-command-bar-force-move =
|
||||
Hold <(Alt)> to activate temporarily while commanding units.
|
||||
|
||||
button-command-bar-force-attack =
|
||||
.tooltip = Force Attack
|
||||
.tooltipdesc = Selected units will attack the targeted unit or location
|
||||
.tooltip = Force Attack
|
||||
.tooltipdesc =
|
||||
Selected units will attack the targeted unit or location
|
||||
- Default activity for the target is suppressed
|
||||
- Allows targeting of own or ally forces
|
||||
- Long-range artillery units will always target the
|
||||
@@ -263,14 +273,16 @@ button-command-bar-force-attack =
|
||||
Hold <(Ctrl)> to activate temporarily while commanding units.
|
||||
|
||||
button-command-bar-guard =
|
||||
.tooltip = Guard
|
||||
.tooltipdesc = Selected units will follow the targeted unit.
|
||||
.tooltip = Guard
|
||||
.tooltipdesc =
|
||||
Selected units will follow the targeted unit.
|
||||
|
||||
Left-click icon then right-click on target unit.
|
||||
|
||||
button-command-bar-deploy =
|
||||
.tooltip = Deploy
|
||||
.tooltipdesc = Selected units will perform their default deploy activity
|
||||
.tooltip = Deploy
|
||||
.tooltipdesc =
|
||||
Selected units will perform their default deploy activity
|
||||
- MCVs will unpack into a Construction Yard
|
||||
- Construction Yards will re-pack into a MCV
|
||||
- Transports will unload their passengers
|
||||
@@ -278,22 +290,25 @@ button-command-bar-deploy =
|
||||
Acts immediately on selected units.
|
||||
|
||||
button-command-bar-scatter =
|
||||
.tooltip = Scatter
|
||||
.tooltipdesc = Selected units will stop their current activity
|
||||
.tooltip = Scatter
|
||||
.tooltipdesc =
|
||||
Selected units will stop their current activity
|
||||
and move to a nearby location.
|
||||
|
||||
Acts immediately on selected units.
|
||||
|
||||
button-command-bar-stop =
|
||||
.tooltip = Stop
|
||||
.tooltipdesc = Selected units will stop their current activity.
|
||||
.tooltip = Stop
|
||||
.tooltipdesc =
|
||||
Selected units will stop their current activity.
|
||||
Selected buildings will reset their rally point.
|
||||
|
||||
Acts immediately on selected targets.
|
||||
|
||||
button-command-bar-queue-orders =
|
||||
.tooltip = Waypoint Mode
|
||||
.tooltipdesc = Use Waypoint Mode to give multiple linking commands
|
||||
.tooltip = Waypoint Mode
|
||||
.tooltipdesc =
|
||||
Use Waypoint Mode to give multiple linking commands
|
||||
to the selected units. Units will execute the commands
|
||||
immediately upon receiving them.
|
||||
|
||||
@@ -301,26 +316,30 @@ button-command-bar-queue-orders =
|
||||
Hold <(Shift)> to activate temporarily while commanding units.
|
||||
|
||||
button-stance-bar-attackanything =
|
||||
.tooltip = Attack Anything Stance
|
||||
.tooltipdesc = Set the selected units to Attack Anything stance:
|
||||
.tooltip = Attack Anything Stance
|
||||
.tooltipdesc =
|
||||
Set the selected units to Attack Anything stance:
|
||||
- Units will attack enemy units and structures on sight
|
||||
- Units will pursue attackers across the battlefield
|
||||
|
||||
button-stance-bar-defend =
|
||||
.tooltip = Defend Stance
|
||||
.tooltipdesc = Set the selected units to Defend stance:
|
||||
.tooltip = Defend Stance
|
||||
.tooltipdesc =
|
||||
Set the selected units to Defend stance:
|
||||
- Units will attack enemy units on sight
|
||||
- Units will not move or pursue enemies
|
||||
|
||||
button-stance-bar-returnfire =
|
||||
.tooltip = Return Fire Stance
|
||||
.tooltipdesc = Set the selected units to Return Fire stance:
|
||||
.tooltip = Return Fire Stance
|
||||
.tooltipdesc =
|
||||
Set the selected units to Return Fire stance:
|
||||
- Units will retaliate against enemies that attack them
|
||||
- Units will not move or pursue enemies
|
||||
|
||||
button-stance-bar-holdfire =
|
||||
.tooltip = Hold Fire Stance
|
||||
.tooltipdesc = Set the selected units to Hold Fire stance:
|
||||
.tooltip = Hold Fire Stance
|
||||
.tooltipdesc =
|
||||
Set the selected units to Hold Fire stance:
|
||||
- Units will not fire upon enemies
|
||||
- Units will not move or pursue enemies
|
||||
|
||||
@@ -336,19 +355,19 @@ button-production-types-vehicle-tooltip = Vehicles
|
||||
button-production-types-aircraft-tooltip = Aircraft
|
||||
|
||||
productionpalette-player-widgets-production-palette =
|
||||
.ready = Ready
|
||||
.hold = On Hold
|
||||
.ready = Ready
|
||||
.hold = On Hold
|
||||
|
||||
## lobby-kickdialogs.yaml
|
||||
label-kick-client-dialog-texta = You may also apply a temporary ban, preventing
|
||||
label-kick-client-dialog-textb = them from joining for the remainder of this game.
|
||||
label-kick-client-dialog-text-a = You may also apply a temporary ban, preventing
|
||||
label-kick-client-dialog-text-b = them from joining for the remainder of this game.
|
||||
checkbox-kick-client-dialog-prevent-rejoining = Temporarily Ban
|
||||
button-kick-client-dialog = Kick
|
||||
label-kick-spectators-dialog-title = Kick Spectators
|
||||
button-kick-spectators-dialog-ok = Ok
|
||||
label-force-start-dialog-title = Start Game?
|
||||
label-force-start-dialog-texta = One or more players are not yet ready.
|
||||
label-force-start-dialog-textb = Are you sure that you want to force start the game?
|
||||
label-force-start-dialog-text-a = One or more players are not yet ready.
|
||||
label-force-start-dialog-text-b = Are you sure that you want to force start the game?
|
||||
label-kick-warning-a = One or more clients are missing the selected
|
||||
label-kick-warning-b = map, and will be kicked from the server.
|
||||
button-force-start-dialog-start = Start
|
||||
@@ -419,8 +438,8 @@ button-multiplayer-tabs-servers-tab = Servers
|
||||
button-bg-changemap = Change Map
|
||||
|
||||
button-lobbychat-chat-mode =
|
||||
.label = Team
|
||||
.tooltip = Toggle chat mode
|
||||
.label = Team
|
||||
.tooltip = Toggle chat mode
|
||||
|
||||
button-server-lobby-disconnect = Leave Game
|
||||
button-server-lobby-start-game = Start Game
|
||||
@@ -512,6 +531,12 @@ button-missionbrowser-panel-stop-briefing-video = Stop Briefing
|
||||
button-missionbrowser-panel-start-info-video = Watch Info Video
|
||||
button-missionbrowser-panel-stop-info-video = Stop Info Video
|
||||
button-missionbrowser-panel-play = Play
|
||||
dropdown-missionbrowser-difficulty =
|
||||
.label = Difficulty
|
||||
.description = The difficulty of the mission
|
||||
|
||||
dropdown-missionbrowser-gamespeed = Speed:
|
||||
label-missionbrowser-normal-difficulty = Normal
|
||||
|
||||
## multiplayer-browser.yaml
|
||||
image-bg-password-protected-tooltip = Requires Password
|
||||
@@ -632,8 +657,8 @@ checkbox-mute-sound-container = Mute Sound
|
||||
label-sound-volume-container = Sound Volume:
|
||||
|
||||
checkbox-mute-background-music-container =
|
||||
.label = Mute Menu Music
|
||||
.tooltip = Mute background music when no specific track is playing
|
||||
.label = Mute Menu Music
|
||||
.tooltip = Mute background music when no specific track is playing
|
||||
|
||||
label-music-title-volume-container = Music Volume:
|
||||
label-audio-device-container = Audio Device:
|
||||
@@ -645,16 +670,16 @@ label-target-lines-dropdown-container = Target Lines:
|
||||
label-status-bar-dropdown-container-bars = Status Bars:
|
||||
|
||||
checkbox-player-stance-colors-container =
|
||||
.label = Player Relationship Colors
|
||||
.tooltip = Change player colors based on relationship (own, enemy, ally, neutral)
|
||||
.label = Player Relationship Colors
|
||||
.tooltip = Change player colors based on relationship (own, enemy, ally, neutral)
|
||||
|
||||
checkbox-ui-feedback-container =
|
||||
.label = Show UI Feedback Notifications
|
||||
.tooltip = Show transient text notifications for UI events
|
||||
.label = Show UI Feedback Notifications
|
||||
.tooltip = Show transient text notifications for UI events
|
||||
|
||||
checkbox-transients-container =
|
||||
.label = Show Game Event Notifications
|
||||
.tooltip = Show transient text notifications for game events
|
||||
.label = Show Game Event Notifications
|
||||
.tooltip = Show transient text notifications for game events
|
||||
|
||||
checkbox-hide-replay-chat-container = Hide Chat in Replays
|
||||
label-video-section-header = Video
|
||||
@@ -688,12 +713,12 @@ label-notices-readonly-notice = This hotkey cannot be modified
|
||||
button-hotkey-remap-dialog-override = Override
|
||||
|
||||
button-hotkey-remap-dialog-clear =
|
||||
.label = Clear
|
||||
.tooltip = Unbind the hotkey
|
||||
.label = Clear
|
||||
.tooltip = Unbind the hotkey
|
||||
|
||||
button-hotkey-remap-dialog-reset =
|
||||
.label = Reset
|
||||
.tooltip = Reset to default
|
||||
.label = Reset
|
||||
.tooltip = Reset to default
|
||||
|
||||
## settings-input.yaml
|
||||
label-zoom-modifier-container = Zoom Modifier:
|
||||
|
||||
@@ -105,7 +105,7 @@ destroy-gdi-supporter-houses = Destroy the houses of the GDI supporters
|
||||
in the village.
|
||||
|
||||
## nod06c
|
||||
infiltrate-barracks-factory-conyard = Infiltrate the barracks, weapons factory and
|
||||
capture-barracks-factory-conyard = Capture the barracks, weapons factory and
|
||||
construction yard.
|
||||
|
||||
## nod07a
|
||||
|
||||
@@ -35,7 +35,7 @@ resource-tiberium = Tiberium
|
||||
faction-random =
|
||||
.name = Any
|
||||
.description = Random Faction
|
||||
A random faction is chosen at the start of the game.
|
||||
A random faction is chosen at the start of the game
|
||||
|
||||
faction-gdi =
|
||||
.name = GDI
|
||||
@@ -60,27 +60,36 @@ notification-structure-lost = Structure lost.
|
||||
|
||||
## aircraft.yaml
|
||||
actor-tran =
|
||||
.name = Chinook Transport
|
||||
.description = Fast Infantry Transport Helicopter.
|
||||
Unarmed.
|
||||
.encyclopedia = The chinook is a flying transport capable of carrying a detachement of infantry. It is mostly used to transport commandos or engineers for backline-destruction.
|
||||
.name = Chinook Transport
|
||||
.description =
|
||||
Fast Infantry Transport Helicopter.
|
||||
Unarmed
|
||||
.encyclopedia =
|
||||
The chinook is a flying transport capable of carrying a detachement of infantry. It is mostly used to transport commandos or engineers for backline-destruction.
|
||||
|
||||
Like all aircraft, it can be handy in a pinch to crush a commando by landing!
|
||||
|
||||
actor-heli =
|
||||
.name = Apache Longbow
|
||||
.description = Helicopter Gunship with chainguns.
|
||||
Strong vs. Infantry, Light Vehicles and Aircraft.
|
||||
Weak vs. Tanks.
|
||||
.encyclopedia = Has more health than the Orca and acts as a support unit. It works well with artillery, as it can kill approaching light vehicles.
|
||||
Ammo is automatically reloaded in the air.
|
||||
.name = Apache Longbow
|
||||
.description =
|
||||
Helicopter Gunship with chainguns.
|
||||
Strong vs Infantry, Light Vehicles and Aircraft
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
Has more health than the Orca and acts as a support unit. It works well with artillery, as it can kill approaching light vehicles.
|
||||
|
||||
Ammo is automatically reloaded in the air.
|
||||
|
||||
actor-orca =
|
||||
.name = Orca
|
||||
.description = Helicopter gunship with AG missiles.
|
||||
Strong vs. Buildings and Tanks.
|
||||
Weak vs. Infantry.
|
||||
.encyclopedia = Fast but fragile, the Orca harasses enemy vehicles and aircraft with AT missiles. It is best used opportunistically, and can be kept alive longer with careful micro. Its AA missiles are effective but have a hard time hitting fast-moving air targets.
|
||||
Ammo is automatically reloaded in the air.
|
||||
.name = Orca
|
||||
.description =
|
||||
Helicopter gunship with AG missiles.
|
||||
Strong vs Buildings and Tanks
|
||||
Weak vs Infantry
|
||||
.encyclopedia =
|
||||
Fast but fragile, the Orca harasses enemy vehicles and aircraft with AT missiles. It is best used opportunistically, and can be kept alive longer with careful micro. Its AA missiles are effective but have a hard time hitting fast-moving air targets.
|
||||
|
||||
Ammo is automatically reloaded in the air.
|
||||
|
||||
actor-c17-name = Supply Aircraft
|
||||
actor-a10-name = A10 Bomber
|
||||
@@ -181,12 +190,12 @@ meta-civbuildinghusk-generic-name = Civilian Building (Destroyed)
|
||||
meta-civfield-generic-name = Field
|
||||
|
||||
meta-civhaystackorigloo =
|
||||
.winter-name = Igloo
|
||||
.summer-name = Haystack
|
||||
.winter-name = Igloo
|
||||
.summer-name = Haystack
|
||||
|
||||
meta-civhaystackorigloohusk =
|
||||
.winter-name = Igloo (Destroyed)
|
||||
.summer-name = Haystack (Destroyed)
|
||||
.winter-name = Igloo (Destroyed)
|
||||
.summer-name = Haystack (Destroyed)
|
||||
|
||||
meta-tree-name = Tree
|
||||
meta-treehusk-name = Tree (Burnt)
|
||||
@@ -197,8 +206,8 @@ meta-helicopterhusk-generic-name = Destroyed Helicopter
|
||||
meta-bridge-name = Bridge
|
||||
|
||||
meta-crate =
|
||||
.name = Crate
|
||||
.generic-name = Crate
|
||||
.name = Crate
|
||||
.generic-name = Crate
|
||||
|
||||
## husks.yaml
|
||||
actor-mcv-husk-name = Mobile Construction Vehicle (Destroyed)
|
||||
@@ -219,81 +228,113 @@ actor-truck-husk-name = Supply Truck (Destroyed)
|
||||
|
||||
## infantry.yaml
|
||||
actor-e1 =
|
||||
.name = Minigunner
|
||||
.description = General-purpose infantry.
|
||||
Strong vs. Infantry.
|
||||
Weak vs. Vehicles.
|
||||
.encyclopedia = The humble Minigunner is a highly specialized infantry unit designed to soak damage and kill other infantry.
|
||||
Like most TD units, they can see farther than they can shoot, and work well in packs to eliminate enemy infantry.
|
||||
.name = Minigunner
|
||||
.description =
|
||||
General-purpose infantry.
|
||||
Strong vs Infantry
|
||||
Weak vs Vehicles
|
||||
.encyclopedia =
|
||||
The humble Minigunner is a highly specialized infantry unit designed to soak damage and kill other infantry.
|
||||
|
||||
Like most TD units, they can see farther than they can shoot, and work well in packs to eliminate enemy infantry.
|
||||
|
||||
actor-e2 =
|
||||
.name = Grenadier
|
||||
.description = Fast infantry with grenades.
|
||||
Strong vs. Buildings, slow-moving targets.
|
||||
.encyclopedia = Fast and able to keep up with medium tanks, letting it work well with flanking armies.
|
||||
While not as powerful as the Minigunner or a Rocket soldier, its speed can be used to your advantage.
|
||||
.name = Grenadier
|
||||
.description =
|
||||
Fast infantry with grenades.
|
||||
Strong vs Buildings, slow-moving targets
|
||||
.encyclopedia =
|
||||
Fast and able to keep up with medium tanks, letting it work well with flanking armies.
|
||||
|
||||
While not as powerful as the Minigunner or a Rocket soldier, its speed can be used to your advantage.
|
||||
|
||||
actor-e3 =
|
||||
.name = Rocket Soldier
|
||||
.description = Anti-tank/Anti-aircraft infantry.
|
||||
Strong vs. Tanks and Aircraft.
|
||||
Weak vs. Infantry.
|
||||
.encyclopedia = Highly vulnerable but excelling at eliminating enemy armor and aircraft. It is the slowest unit in the game, lagging behind other infantry and even mammoth tanks.
|
||||
Its large weapons range makes up for its slow speed, making it most effective when used defensively, where it can be protected and provide vision.
|
||||
.name = Rocket Soldier
|
||||
.description =
|
||||
Anti-tank/Anti-aircraft infantry.
|
||||
Strong vs Tanks and Aircraft
|
||||
Weak vs Infantry
|
||||
.encyclopedia =
|
||||
Highly vulnerable but excelling at eliminating enemy armor and aircraft. It is the slowest unit in the game, lagging behind other infantry and even mammoth tanks.
|
||||
|
||||
Its large weapons range makes up for its slow speed, making it most effective when used defensively, where it can be protected and provide vision.
|
||||
|
||||
actor-e4 =
|
||||
.name = Flamethrower
|
||||
.description = Advanced anti-infantry unit.
|
||||
Strong vs. Infantry and Buildings.
|
||||
Weak vs. Tanks.
|
||||
.encyclopedia = Good for flanking and burning down structures. It has more health than a standard Minigunner, but its short weapons range makes it difficult to use in large armies.
|
||||
.name = Flamethrower
|
||||
.description =
|
||||
Advanced anti-infantry unit.
|
||||
Strong vs Infantry and Buildings
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
Good for flanking and burning down structures. It has more health than a standard Minigunner, but its short weapons range makes it difficult to use in large armies.
|
||||
|
||||
actor-e5 =
|
||||
.name = Chemical Warrior
|
||||
.description = Advanced general-purpose infantry.
|
||||
Strong vs. all Ground units.
|
||||
.encyclopedia = Similar stats to the Flamethrower but deals good damage vs. heavy armor. Great for stopping enemy armor from crushing your forces.
|
||||
It can walk on Tiberium without taking damage, making it particularly useful for harvester harassment on certain maps.
|
||||
.name = Chemical Warrior
|
||||
.description =
|
||||
Advanced general-purpose infantry.
|
||||
Strong vs all Ground units
|
||||
.encyclopedia =
|
||||
Similar stats to the Flamethrower but deals good damage vs heavy armor. Great for stopping enemy armor from crushing your forces.
|
||||
|
||||
It can walk on Tiberium without taking damage, making it particularly useful for harvester harassment on certain maps.
|
||||
|
||||
actor-e6 =
|
||||
.name = Engineer
|
||||
.description = Damages and captures enemy structures.
|
||||
Repairs destroyed vehicles.
|
||||
Unarmed.
|
||||
.encyclopedia = Like the rocket soldier, the Engineer is slow and requires escorts to be used effectively. It can instantly capture structures but is consumed in the process.
|
||||
Capturing a civilian structure requires only one engineer, while capturing an enemy structure typically requires two engineers, or one if the structure is heavily damaged.
|
||||
Engineers can also be used to repair friendly structures or restore husks from destroyed vehicles.
|
||||
.name = Engineer
|
||||
.description =
|
||||
Damages and captures enemy structures.
|
||||
Repairs destroyed vehicles.
|
||||
Unarmed
|
||||
.encyclopedia =
|
||||
Like the rocket soldier, the Engineer is slow and requires escorts to be used effectively. It can instantly capture structures but is consumed in the process.
|
||||
|
||||
Capturing a civilian structure requires only one engineer, while capturing an enemy structure typically requires two engineers, or one if the structure is heavily damaged.
|
||||
|
||||
Engineers can also be used to repair friendly structures or restore husks from destroyed vehicles.
|
||||
|
||||
actor-rmbo =
|
||||
.name = Commando
|
||||
.description = Elite sniper infantry unit.
|
||||
Strong vs Infantry and Buildings.
|
||||
Weak vs Vehicles.
|
||||
.encyclopedia = Equipped with a long-range sniper rifle, the Commando fires slowly but can eliminate enemy infantry from a distance when well-supported.
|
||||
It carries C4, for surprise backline structure destruction.
|
||||
.name = Commando
|
||||
.description =
|
||||
Elite sniper infantry unit.
|
||||
Strong vs Infantry and Buildings
|
||||
Weak vs Vehicles
|
||||
.encyclopedia =
|
||||
Equipped with a long-range sniper rifle, the Commando fires slowly but can eliminate enemy infantry from a distance when well-supported.
|
||||
|
||||
It carries C4, for surprise backline structure destruction.
|
||||
|
||||
actor-pvice =
|
||||
.description = Mutated abomination that spits liquid Tiberium.
|
||||
Strong vs Infantry and Buildings.
|
||||
Weak vs Aircraft.
|
||||
.encyclopedia = A mutated lifeform created from the strange properties of Tiberium, when infantry units are expose to it.
|
||||
They regenerate health quickly and have a short-range Tiberium weapon.
|
||||
.description =
|
||||
Mutated abomination that spits liquid Tiberium.
|
||||
Strong vs Infantry and Buildings
|
||||
Weak vs Aircraft
|
||||
.encyclopedia =
|
||||
A mutated lifeform created from the strange properties of Tiberium, when infantry units are expose to it.
|
||||
|
||||
They regenerate health quickly and have a short-range Tiberium weapon.
|
||||
|
||||
actor-steg =
|
||||
.name = Stegosaurus
|
||||
.description = A large, heavily built, herbivorous quadruped.
|
||||
.name = Stegosaurus
|
||||
.description =
|
||||
A large, heavily built,
|
||||
herbivorous quadruped.
|
||||
|
||||
actor-trex =
|
||||
.name = Tyrannosaurus rex
|
||||
.description = Bipedal carnivore with a massive skull.
|
||||
.name = Tyrannosaurus rex
|
||||
.description =
|
||||
Bipedal carnivore with
|
||||
a massive skull.
|
||||
|
||||
actor-tric =
|
||||
.name = Triceratops
|
||||
.description = Quadruped with large bony frill and three horns.
|
||||
.name = Triceratops
|
||||
.description =
|
||||
Quadruped with large bony
|
||||
frill and three horns.
|
||||
|
||||
actor-rapt =
|
||||
.name = Velociraptor
|
||||
.description = Bipedal with enlarged sickle-shaped claw on each hindfoot.
|
||||
.name = Velociraptor
|
||||
.description =
|
||||
Bipedal with enlarged sickle-shaped
|
||||
claw on each hindfoot.
|
||||
|
||||
## misc.yaml
|
||||
actor-wcrate-name = Wooden Crate
|
||||
@@ -326,155 +367,198 @@ notification-nuclear-weapon-launched = Nuclear weapon launched.
|
||||
notification-nuclear-warhead-approaching = Nuclear warhead approaching.
|
||||
|
||||
actor-fact =
|
||||
.name = Construction Yard
|
||||
.description = Builds structures.
|
||||
.encyclopedia = The core of any base. It produces buildings and defenses while also providing a build radius. It has a large health pool, but is difficult to replace, making it paramount to defend.
|
||||
It can be unpacked into a mobile version using the deploy key, but takes a small amount of time to do so.
|
||||
.name = Construction Yard
|
||||
.description = Builds structures.
|
||||
.encyclopedia =
|
||||
The core of any base. It produces buildings and defenses while also providing a build radius. It has a large health pool, but is difficult to replace, making it paramount to defend.
|
||||
|
||||
It can be unpacked into a mobile version using the deploy key, but takes a small amount of time to do so.
|
||||
|
||||
actor-fact-gdi-name = GDI Construction Yard
|
||||
actor-fact-nod-name = Nod Construction Yard
|
||||
|
||||
actor-nuke =
|
||||
.name = Power Plant
|
||||
.description = Generates power.
|
||||
.encyclopedia = Provides power to the structures in your base. Power output is directly related to the power plant's condition, so protect them during battles.
|
||||
.name = Power Plant
|
||||
.description = Generates power.
|
||||
.encyclopedia =
|
||||
Provides power to the structures in your base. Power output is directly related to the power plant's condition, so protect them during battles.
|
||||
|
||||
actor-nuk2 =
|
||||
.name = Advanced Power Plant
|
||||
.description = Provides more power, cheaper than the
|
||||
.name = Advanced Power Plant
|
||||
.description =
|
||||
Provides more power, cheaper than the
|
||||
standard Power Plant.
|
||||
.encyclopedia = Provides more power to the structures in your base, making it more cost-effective than the Power Plant. Power output is directly related to its condition, so protect it during battle.
|
||||
.encyclopedia =
|
||||
Provides more power to the structures in your base, making it more cost-effective than the Power Plant. Power output is directly related to its condition, so protect it during battle.
|
||||
|
||||
actor-proc =
|
||||
.name = Tiberium Refinery
|
||||
.description = Processes raw Tiberium
|
||||
into usable resources.
|
||||
.encyclopedia = Stores and processes the alien material and resource, Tiberium. Harvesters collect Tiberium in the field and deposit it at the refinery, where it is converted into credits.
|
||||
The refinery stores Tiberium and immediately deploys a harvester once constructed. 3 harvesters is the maximum it can handle for close refining.
|
||||
.name = Tiberium Refinery
|
||||
.description =
|
||||
Processes raw Tiberium
|
||||
into usable resources.
|
||||
.encyclopedia =
|
||||
Stores and processes the alien material and resource, Tiberium. Harvesters collect Tiberium in the field and deposit it at the refinery, where it is converted into credits.
|
||||
|
||||
The refinery stores Tiberium and immediately deploys a harvester once constructed. 3 harvesters is the maximum it can handle for close refining.
|
||||
|
||||
actor-silo =
|
||||
.name = Tiberium Silo
|
||||
.description = Stores processed Tiberium.
|
||||
.encyclopedia = Stores credits. If storage is full, harvesters will wait until credits are spent.
|
||||
Useful when harvesting the more valuable blue Tiberium, as a single refinery cannot store a full load.
|
||||
.name = Tiberium Silo
|
||||
.description = Stores processed Tiberium.
|
||||
.encyclopedia =
|
||||
Stores credits. If storage is full, harvesters will wait until credits are spent.
|
||||
|
||||
Useful when harvesting the more valuable blue Tiberium, as a single refinery cannot store a full load.
|
||||
|
||||
actor-pyle =
|
||||
.name = Barracks
|
||||
.description = Trains infantry
|
||||
.encyclopedia = Produces infantry for GDI. Once unlocked, advanced GDI infantry are hard-hitting and fast.
|
||||
.name = Barracks
|
||||
.description = Trains infantry.
|
||||
.encyclopedia =
|
||||
Produces infantry for GDI. Once unlocked, advanced GDI infantry are hard-hitting and fast.
|
||||
|
||||
actor-hand =
|
||||
.name = Hand of Nod
|
||||
.description = Trains infantry
|
||||
.encyclopedia = Produces infantry for Nod. Once unlocked, advanced Nod infantry are slow but have high health pools.
|
||||
.name = Hand of Nod
|
||||
.description = Trains infantry.
|
||||
.encyclopedia =
|
||||
Produces infantry for Nod. Once unlocked, advanced Nod infantry are slow but have high health pools.
|
||||
|
||||
actor-afld =
|
||||
.name = Airstrip
|
||||
.description = Provides a dropzone
|
||||
for vehicle reinforcements.
|
||||
.encyclopedia = Delivers vehicles by plane for Nod. Nod vehicles are fast but fragile. Produce harvesters at the start to jumpstart your economy.
|
||||
.name = Airstrip
|
||||
.description =
|
||||
Provides a dropzone
|
||||
for vehicle reinforcements.
|
||||
.encyclopedia =
|
||||
Delivers vehicles by plane for Nod. Nod vehicles are fast but fragile. Produce harvesters at the start to jumpstart your economy.
|
||||
|
||||
actor-weap =
|
||||
.name = Weapons Factory
|
||||
.description = Produces vehicles.
|
||||
.encyclopedia = Produces vehicles for GDI. GDI vehicles tend to be slow but hard-hitting. Produce harvesters at the start to jumpstart your economy.
|
||||
.name = Weapons Factory
|
||||
.description = Produces vehicles.
|
||||
.encyclopedia =
|
||||
Produces vehicles for GDI. GDI vehicles tend to be slow but hard-hitting. Produce harvesters at the start to jumpstart your economy.
|
||||
|
||||
actor-hpad =
|
||||
.name = Helipad
|
||||
.description = Produces and repairs helicopters.
|
||||
.encyclopedia = Produces and repairs helicopters. Requires a Communications Center to build advanced attack helicopters.
|
||||
.name = Helipad
|
||||
.description =
|
||||
Produces and repairs helicopters.
|
||||
.encyclopedia =
|
||||
Produces and repairs helicopters. Requires a Communications Center to build advanced attack helicopters.
|
||||
|
||||
actor-hq =
|
||||
.name = Communications Center
|
||||
.description = Provides radar and Air Strike support power
|
||||
.name = Communications Center
|
||||
.description =
|
||||
Provides radar and Air Strike support power.
|
||||
Unlocks higher-tech units and buildings.
|
||||
Requires power to operate.
|
||||
.airstrikepower-name = Air Strike
|
||||
.airstrikepower-description = Deploy an aerial napalm strike.
|
||||
.airstrikepower-name = Air Strike
|
||||
.airstrikepower-description = Deploy an aerial napalm strike.
|
||||
Burns buildings and infantry along a line.
|
||||
.encyclopedia = Grants the player access to the minimap (top right) when there is sufficient power. Unlocks new units while also granting access to the airstrike support power.
|
||||
.encyclopedia =
|
||||
Grants the player access to the minimap (top right) when there is sufficient power. Unlocks new units while also granting access to the airstrike support power.
|
||||
|
||||
actor-fix =
|
||||
.name = Repair Facility
|
||||
.description = Repairs vehicles.
|
||||
.encyclopedia = Repairs vehicles. The repair command can be used on units to send them to the nearest repair facility.
|
||||
.name = Repair Facility
|
||||
.description = Repairs vehicles.
|
||||
.encyclopedia =
|
||||
Repairs vehicles. The repair command can be used on units to send them to the nearest repair facility.
|
||||
|
||||
actor-eye =
|
||||
.name = Advanced Communications Center
|
||||
.description = Provides radar and Orbital Ion Cannon support power.
|
||||
.name = Advanced Communications Center
|
||||
.description =
|
||||
Provides radar and Orbital Ion Cannon support power.
|
||||
Unlocks Mammoth Tank and Commando.
|
||||
Requires power to operate.
|
||||
.ioncannonpower-name = Ion Cannon
|
||||
.ioncannonpower-description = Initiates an Ion Cannon strike.
|
||||
.ioncannonpower-name = Ion Cannon
|
||||
.ioncannonpower-description = Initiates an Ion Cannon strike.
|
||||
Applies instant damage to a small area.
|
||||
.encyclopedia = Unlocks advanced units and the ion cannon support power. If the Communications Center is lost, it restores minimap functionality and unit production.
|
||||
.encyclopedia =
|
||||
Unlocks advanced units and the ion cannon support power. If the Communications Center is lost, it restores minimap functionality and unit production.
|
||||
|
||||
actor-tmpl =
|
||||
.name = Temple of Nod
|
||||
.description = Provides Nuclear Strike support power.
|
||||
.name = Temple of Nod
|
||||
.description =
|
||||
Provides Nuclear Strike support power.
|
||||
Unlocks Stealth Tank, Chem. Warrior and Obelisk of Light.
|
||||
Requires power to operate.
|
||||
.nukepower-name = Nuclear Strike
|
||||
.nukepower-description = Launches a tactical nuclear warhead.
|
||||
.nukepower-name = Nuclear Strike
|
||||
.nukepower-description = Launches a tactical nuclear warhead.
|
||||
Applies heavy damage over a large area.
|
||||
.encyclopedia = Unlocks advanced units and the Nuke support power. Keeps unit unlocks online if the Communications Center is lost.
|
||||
.encyclopedia =
|
||||
Unlocks advanced units and the Nuke support power. Keeps unit unlocks online if the Communications Center is lost.
|
||||
|
||||
actor-gun =
|
||||
.name = Turret
|
||||
.description = Basic Anti-Tank base defense.
|
||||
Strong vs Tanks and Vehicles.
|
||||
Weak vs Infantry.
|
||||
.encyclopedia = Base defense armed with an armor-piercing cannon, it deals significant damage to vehicles in range.
|
||||
.name = Turret
|
||||
.description =
|
||||
Basic Anti-Tank base defense.
|
||||
Strong vs Tanks and Vehicles
|
||||
Weak vs Infantry
|
||||
.encyclopedia =
|
||||
Base defense armed with an armor-piercing cannon, it deals significant damage to vehicles in range.
|
||||
|
||||
actor-sam =
|
||||
.name = SAM Site
|
||||
.description = Anti-Aircraft base defense.
|
||||
Strong vs Aircraft.
|
||||
Cannot target Ground units.
|
||||
.encyclopedia = Nod anti-air base defense. It remains protected when closed and opens to engage aircraft.
|
||||
.name = SAM Site
|
||||
.description =
|
||||
Anti-Aircraft base defense.
|
||||
Strong vs Aircraft
|
||||
Cannot target Ground units
|
||||
.encyclopedia =
|
||||
Nod anti-air base defense. It remains protected when closed and opens to engage aircraft.
|
||||
|
||||
actor-obli =
|
||||
.name = Obelisk of Light
|
||||
.description = Advanced base defense.
|
||||
Requires power to operate.
|
||||
Strong vs. all Ground units.
|
||||
Cannot target Aircraft.
|
||||
.encyclopedia = Advanced defense for Nod that quickly destroys ground targets with a powerful laser.
|
||||
.name = Obelisk of Light
|
||||
.description =
|
||||
Advanced base defense.
|
||||
Requires power to operate.
|
||||
Strong vs all Ground units
|
||||
Cannot target Aircraft
|
||||
.encyclopedia =
|
||||
Advanced defense for Nod that quickly destroys ground targets with a powerful laser.
|
||||
|
||||
actor-gtwr =
|
||||
.name = Guard Tower
|
||||
.description = Basic defensive structure.
|
||||
Strong vs. Infantry.
|
||||
Weak vs. Tanks.
|
||||
.encyclopedia = Base defense armed with a high-velocity machine gun, it shreds infantry and light vehicles within range with its large area of effect.
|
||||
.name = Guard Tower
|
||||
.description =
|
||||
Basic defensive structure.
|
||||
Strong vs Infantry
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
Base defense armed with a high-velocity machine gun, it shreds infantry and light vehicles within range with its large area of effect.
|
||||
|
||||
actor-atwr =
|
||||
.name = Advanced Guard Tower
|
||||
.description = All-purpose defensive structure.
|
||||
Strong vs. Aircraft and Infantry.
|
||||
Weak vs. Tanks.
|
||||
.encyclopedia = Advanced defense for GDI that fires volleys of high explosive missiles at both ground and air targets. Effective versus everything.
|
||||
.name = Advanced Guard Tower
|
||||
.description =
|
||||
All-purpose defensive structure.
|
||||
Strong vs Aircraft and Infantry
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
Advanced defense for GDI that fires volleys of high explosive missiles at both ground and air targets. Effective versus everything.
|
||||
|
||||
actor-sbag =
|
||||
.name = Sandbag Barrier
|
||||
.description = Stops infantry and light vehicles.
|
||||
Can be crushed by tanks.
|
||||
.encyclopedia = Blocks movement of infantry and light vehicles, but can be crushed by tanks. Immune to small arms fire.
|
||||
This barrier can be built in multiple segments once an initial sandbag has been placed.
|
||||
.name = Sandbag Barrier
|
||||
.description =
|
||||
Stops infantry and light vehicles.
|
||||
Can be crushed by tanks.
|
||||
.encyclopedia =
|
||||
Blocks movement of infantry and light vehicles, but can be crushed by tanks. Immune to small arms fire.
|
||||
|
||||
This barrier can be built in multiple segments once an initial sandbag has been placed.
|
||||
|
||||
actor-cycl =
|
||||
.name = Chain Link Barrier
|
||||
.description = Stops infantry and light vehicles.
|
||||
Can be crushed by tanks.
|
||||
.encyclopedia = Blocks infantry movement and light vehicles, but can be crushed by tanks. Immune to small arms fire.
|
||||
This barrier can be built in multiple segments once an initial chain link has been placed.
|
||||
.name = Chain Link Barrier
|
||||
.description =
|
||||
Stops infantry and light vehicles.
|
||||
Can be crushed by tanks.
|
||||
.encyclopedia =
|
||||
Blocks movement of infantry and light vehicles, but can be crushed by tanks. Immune to small arms fire.
|
||||
|
||||
This barrier can be built in multiple segments once an initial chain link has been placed.
|
||||
|
||||
actor-brik =
|
||||
.name = Concrete Barrier
|
||||
.description = Stops infantry and most tanks.
|
||||
.name = Concrete Barrier
|
||||
.description =
|
||||
Stops infantry and most tanks.
|
||||
Blocks some projectiles.
|
||||
.encyclopedia = Blocks all unit movement, except for Mammoth Tanks which can crush it. Immune to small arms fire.
|
||||
This barrier can be built in multiple segments once an initial wall has been placed.
|
||||
.encyclopedia =
|
||||
Blocks all unit movement, except for Mammoth Tanks which can crush it. Immune to small arms fire.
|
||||
|
||||
This barrier can be built in multiple segments once an initial wall has been placed.
|
||||
|
||||
actor-barracks-name = Infantry Production
|
||||
actor-vehicleproduction-name = Vehicle Production
|
||||
@@ -487,28 +571,32 @@ actor-hosp-husk-name = Hospital (Destroyed)
|
||||
actor-bio-husk-name = Biological Lab (Destroyed)
|
||||
|
||||
actor-hosp =
|
||||
.name = Hospital
|
||||
.captured-desc = Provides infantry with self-healing.
|
||||
.capturable-desc = Capture to enable self-healing for infantry.
|
||||
.encyclopedia = Heals friendly infantry over time when captured.
|
||||
.name = Hospital
|
||||
.captured-desc = Provides infantry with self-healing.
|
||||
.capturable-desc = Capture to enable self-healing for infantry.
|
||||
.encyclopedia =
|
||||
Heals friendly infantry over time when captured.
|
||||
|
||||
actor-miss =
|
||||
.name = Tech Center
|
||||
.captured-desc = Provides range of vision.
|
||||
.capturable-desc = Capture to give visual range.
|
||||
.encyclopedia = Provides a wide area of vision when captured.
|
||||
.name = Tech Center
|
||||
.captured-desc = Provides range of vision.
|
||||
.capturable-desc = Capture to give visual range.
|
||||
.encyclopedia =
|
||||
Provides a wide area of vision when captured.
|
||||
|
||||
actor-bio =
|
||||
.name = Biological Lab
|
||||
.captured-desc = Provides infantry with Tiberium immunity.
|
||||
.capturable-desc = Capture to enable Tiberium immunity for infantry.
|
||||
.encyclopedia = Grants friendly infantry immunity to Tiberium when captured.
|
||||
.name = Biological Lab
|
||||
.captured-desc = Provides infantry with Tiberium immunity.
|
||||
.capturable-desc = Capture to enable Tiberium immunity for infantry.
|
||||
.encyclopedia =
|
||||
Grants friendly infantry immunity to Tiberium when captured.
|
||||
|
||||
actor-v19 =
|
||||
.name = Oil Derrick
|
||||
.captured-desc = Provides additional funds.
|
||||
.capturable-desc = Capture to receive additional funds.
|
||||
.encyclopedia = Produces a small amount of income frequently when captured. Makes less money than building a harvester, but provides consistent income.
|
||||
.name = Oil Derrick
|
||||
.captured-desc = Provides additional funds.
|
||||
.capturable-desc = Capture to receive additional funds.
|
||||
.encyclopedia =
|
||||
Produces a small amount of income frequently when captured. Makes less money than building a harvester, but provides consistent income.
|
||||
|
||||
## trees.yaml
|
||||
actor-splitblue-name = Blue Blossom Tree
|
||||
@@ -517,132 +605,174 @@ actor-t13-transformable-name = (Tree that can transform into a Blossom Tree)
|
||||
|
||||
## vehicles.yaml
|
||||
actor-mcv =
|
||||
.name = Mobile Construction Vehicle
|
||||
.description = Deploys into a Construction Yard.
|
||||
Unarmed.
|
||||
.encyclopedia = Deploying an MCV changes it into a Construction Yard. The MCV has more health in its deployed form, but undeploying can be helpful to escape infantry.
|
||||
If you have a teammate, they can “gift” you an MCV by destroying an undeployed one near your base, allowing you to reclaim it with an engineer.
|
||||
.name = Mobile Construction Vehicle
|
||||
.description =
|
||||
Deploys into a Construction Yard.
|
||||
Unarmed
|
||||
.encyclopedia =
|
||||
Deploying an MCV changes it into a Construction Yard. The MCV has more health in its deployed form, but undeploying can be helpful to escape infantry.
|
||||
|
||||
If you have a teammate, they can “gift” you an MCV by destroying an undeployed one near your base, allowing you to reclaim it with an engineer.
|
||||
|
||||
actor-harv =
|
||||
.name = Harvester
|
||||
.generic-name = Harvester
|
||||
.description = Collects Tiberium for processing.
|
||||
Unarmed.
|
||||
.encyclopedia = Harvesters slowly mine Tiberium and deposit it in your refinery, where it is converted into credits. They can also be sent to allied refineries to provide funds.
|
||||
.name = Harvester
|
||||
.generic-name = Harvester
|
||||
.description =
|
||||
Collects Tiberium for processing.
|
||||
Unarmed
|
||||
.encyclopedia =
|
||||
Harvesters slowly mine Tiberium and deposit it in your refinery, where it is converted into credits. They can also be sent to allied refineries to provide funds.
|
||||
|
||||
notification-harvester-lost = Harvester lost.
|
||||
|
||||
actor-apc =
|
||||
.name = APC
|
||||
.description = Armed infantry transport.
|
||||
.name = APC
|
||||
.description =
|
||||
Armed infantry transport.
|
||||
Can attack Aircraft.
|
||||
Strong vs. Vehicles.
|
||||
Weak vs. Infantry.
|
||||
.encyclopedia = The APC (Armored Personnel Carrier) transports infantry, making it handy for capturing civilian structures. While its flak gun does not do much damage to enemy vehicles, the APC can resist enemy shots while your Hum-vees deal the damage.
|
||||
It also functions as your primary AA unit; if you hit the stop command, it'll retarget onto any nearby enemy air units.
|
||||
Strong vs Vehicles
|
||||
Weak vs Infantry
|
||||
.encyclopedia =
|
||||
The APC (Armored Personnel Carrier) transports infantry, making it handy for capturing civilian structures. While its flak gun does not do much damage to enemy vehicles, the APC can resist enemy shots while your Hum-vees deal the damage.
|
||||
|
||||
It also functions as your primary AA unit; if you hit the stop command, it'll retarget onto any nearby enemy air units.
|
||||
|
||||
actor-arty =
|
||||
.name = Artillery
|
||||
.description = Long-range artillery.
|
||||
Strong vs. Infantry, Vehicles and Buildings.
|
||||
.encyclopedia = Nod's artillery is a glass cannon, fragile but dealing large amounts of damage at long range. Particularly strong vs structures and infantry, it can destroy stationary tanks.
|
||||
Highly vulnerable when clumped due to its explosion on death.
|
||||
.name = Artillery
|
||||
.description =
|
||||
Long-range artillery.
|
||||
Strong vs Infantry, Vehicles and Buildings
|
||||
.encyclopedia =
|
||||
Nod's artillery is a glass cannon, fragile but dealing large amounts of damage at long range. Particularly strong vs structures and infantry, it can destroy stationary tanks.
|
||||
|
||||
Highly vulnerable when clumped due to its explosion on death.
|
||||
|
||||
actor-ftnk =
|
||||
.name = Flame Tank
|
||||
.description = Heavily armored flame-throwing vehicle.
|
||||
Strong vs. Infantry, Buildings and Vehicles.
|
||||
Weak vs. Tanks.
|
||||
.encyclopedia = Roast infantry and structures alike, making them great for surprise attacks. Be wary of their splash damage on death, but be aware it can also be used to your advantage.
|
||||
.name = Flame Tank
|
||||
.description =
|
||||
Heavily armored flame-throwing vehicle.
|
||||
Strong vs Infantry, Buildings and Vehicles
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
Roast infantry and structures alike, making them great for surprise attacks. Be wary of their splash damage on death, but be aware it can also be used to your advantage.
|
||||
|
||||
actor-bggy =
|
||||
.name = Nod Buggy
|
||||
.description = Fast scout and anti-infantry vehicle.
|
||||
Strong vs. Infantry.
|
||||
Weak vs. Tanks.
|
||||
.encyclopedia = Strong versus light vehicles and infantry, Buggies are great at scouting and killing isolated infantry. Though it is a little cheaper and faster than the GDI Hum-vee, they are more fragile.
|
||||
.name = Nod Buggy
|
||||
.description =
|
||||
Fast scout and anti-infantry vehicle.
|
||||
Strong vs Infantry
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
Strong versus light vehicles and infantry, Buggies are great at scouting and killing isolated infantry. Though it is a little cheaper and faster than the GDI Hum-vee, they are more fragile.
|
||||
|
||||
actor-bike =
|
||||
.name = Recon Bike
|
||||
.description = Fast scout vehicle with rockets.
|
||||
.name = Recon Bike
|
||||
.description =
|
||||
Fast scout vehicle with rockets.
|
||||
Can attack Aircraft.
|
||||
Strong vs. Vehicles and Tanks.
|
||||
Weak vs. Infantry.
|
||||
.encyclopedia = The Nod bike is a very fast vehicle armed with armor-piercing rockets, which makes it great for hit and run and harvester harassment.
|
||||
Be wary that its rockets are slow and have a hard time hitting fast-moving targets.
|
||||
Strong vs Vehicles and Tanks
|
||||
Weak vs Infantry
|
||||
.encyclopedia =
|
||||
The Nod bike is a very fast vehicle armed with armor-piercing rockets, which makes it great for hit and run and harvester harassment.
|
||||
|
||||
Be wary that its rockets are slow and have a hard time hitting fast-moving targets.
|
||||
|
||||
actor-jeep =
|
||||
.name = Hum-vee
|
||||
.description = Fast scout and anti-infantry vehicle.
|
||||
Strong vs. Infantry.
|
||||
Weak vs. Tanks.
|
||||
.encyclopedia = Strong versus light vehicles and infantry, Hum-vees are great at scouting and eliminating isolated infantry.
|
||||
They're excellent for dealing with Nod bike spam and should be built instead of tanks in such engagements. Although a little slower than the Nod buggy, it has more health.
|
||||
.name = Hum-vee
|
||||
.description =
|
||||
Fast scout and anti-infantry vehicle.
|
||||
Strong vs Infantry
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
Strong versus light vehicles and infantry, Hum-vees are great at scouting and eliminating isolated infantry.
|
||||
|
||||
They're excellent for dealing with Nod bike spam and should be built instead of tanks in such engagements. Although a little slower than the Nod buggy, it has more health.
|
||||
|
||||
actor-ltnk =
|
||||
.name = Light Tank
|
||||
.description = Fast, light tank.
|
||||
Strong vs. Vehicles and Tanks.
|
||||
Weak vs. Infantry.
|
||||
.encyclopedia = With its great speed and high health pool, the light tank works great as a frontline unit in various compositions despite its damage output.
|
||||
It should be combined with bikes, stealth tanks, or infantry when facing medium tanks.
|
||||
.name = Light Tank
|
||||
.description =
|
||||
Fast, light tank.
|
||||
Strong vs Vehicles and Tanks
|
||||
Weak vs Infantry
|
||||
.encyclopedia =
|
||||
With its great speed and high health pool, the light tank works great as a frontline unit in various compositions despite its damage output.
|
||||
|
||||
It should be combined with bikes, stealth tanks, or infantry when facing medium tanks.
|
||||
|
||||
actor-mtnk =
|
||||
.name = Medium Tank
|
||||
.description = General-Purpose GDI Tank.
|
||||
Strong vs. Tanks and Vehicles.
|
||||
Weak vs. Infantry.
|
||||
.encyclopedia = The workhorse of GDI, offering a good balance of speed, durability and firepower. It will easily destroy Nod light tanks and can tear through structures if it sneaks into a base.
|
||||
Mix minigunners with your medium tank armies to deal with enemy infantry.
|
||||
.name = Medium Tank
|
||||
.description =
|
||||
General-purpose GDI Tank.
|
||||
Strong vs Tanks and Vehicles
|
||||
Weak vs Infantry
|
||||
.encyclopedia =
|
||||
The workhorse of GDI, offering a good balance of speed, durability and firepower. It will easily destroy Nod light tanks and can tear through structures if it sneaks into a base.
|
||||
|
||||
Mix minigunners with your medium tank armies to deal with enemy infantry.
|
||||
|
||||
actor-htnk =
|
||||
.name = Mammoth Tank
|
||||
.description = Heavily armored GDI Tank.
|
||||
.name = Mammoth Tank
|
||||
.description =
|
||||
Heavily armored GDI Tank.
|
||||
Can attack Aircraft.
|
||||
Strong vs. Everything.
|
||||
.encyclopedia = A crawling battle station, capable of self-repair and engaging any threat. It is particularly effective against enemy armor, but can also destroy small amounts of infantry or aircraft.
|
||||
When in an enemy base, put it on aggressive stance to have it carve a path of destruction as it moves.
|
||||
Strong vs Everything
|
||||
.encyclopedia =
|
||||
A crawling battle station, capable of self-repair and engaging any threat. It is particularly effective against enemy armor, but can also destroy small amounts of infantry or aircraft.
|
||||
|
||||
When in an enemy base, put it on aggressive stance to have it carve a path of destruction as it moves.
|
||||
|
||||
actor-msam =
|
||||
.name = Rocket Launcher
|
||||
.description = Long-range rocket artillery.
|
||||
Strong vs. all ground units.
|
||||
.encyclopedia = Referred to as “MLRS” by players (Multiple Launch Rocket System), this artillery platform fires volleys of rockets at distant targets. It is particularly effective against light vehicles and does moderate damage vs infantry, buildings, and heavy armor.
|
||||
A strong unit to build vs enemy artillery, its high bullet velocity and damage to light vehicles makes it the perfect counter to Nod artillery.
|
||||
.name = Rocket Launcher
|
||||
.description =
|
||||
Long-range rocket artillery.
|
||||
Strong vs all ground units
|
||||
.encyclopedia =
|
||||
Referred to as “MLRS” by players (Multiple Launch Rocket System), this artillery platform fires volleys of rockets at distant targets. It is particularly effective against light vehicles and does moderate damage vs infantry, buildings, and heavy armor.
|
||||
|
||||
A strong unit to build vs enemy artillery, its high bullet velocity and damage to light vehicles makes it the perfect counter to Nod artillery.
|
||||
|
||||
actor-mlrs =
|
||||
.name = Mobile SAM.
|
||||
.description = Powerful anti-air unit.
|
||||
Cannot attack ground units.
|
||||
.encyclopedia = Generally referred to as “MSAM” by players, this Nod vehicle is a dedicated anti-air vehicle when their bikes do not cut it. It fires slow-moving missiles with powerful splash damage.
|
||||
.name = Mobile SAM
|
||||
.description =
|
||||
Powerful anti-air unit.
|
||||
Cannot attack ground units.
|
||||
.encyclopedia =
|
||||
Generally referred to as “MSAM” by players, this Nod vehicle is a dedicated anti-air vehicle when their bikes do not cut it. It fires slow-moving missiles with powerful splash damage.
|
||||
|
||||
actor-stnk =
|
||||
.name = Stealth Tank
|
||||
.description = Long-range missile tank that can cloak.
|
||||
.name = Stealth Tank
|
||||
.description =
|
||||
Long-range missile tank that can cloak.
|
||||
Can attack Aircraft.
|
||||
Has weak armor. Can be spotted by infantry and defense structures.
|
||||
Strong vs. Vehicles and Tanks.
|
||||
Weak vs. Infantry.
|
||||
.encyclopedia = Cloaked units that become visible when damaged or firing their weapons. They can also be detected by defenses within a small radius or infantry at a one-cell range.
|
||||
Their long-range AP missiles are excellent for dealing with enemy armor or air. They can also crush infantry in a pinch.
|
||||
Has weak armor. Can be spotted by infantry and
|
||||
defense structures.
|
||||
Strong vs Vehicles and Tanks
|
||||
Weak vs Infantry
|
||||
.encyclopedia =
|
||||
Cloaked units that become visible when damaged or firing their weapons. They can also be detected by defenses within a small radius or infantry at a one-cell range.
|
||||
|
||||
Their long-range AP missiles are excellent for dealing with enemy armor or air. They can also crush infantry in a pinch.
|
||||
|
||||
actor-mhq =
|
||||
.name = Mobile HQ
|
||||
.description = Mobile base of operations.
|
||||
.name = Mobile HQ
|
||||
.description =
|
||||
Mobile base of operations.
|
||||
|
||||
actor-truck =
|
||||
.name = Supply Truck
|
||||
.description = Transports cash to other players.
|
||||
Builds quickly.
|
||||
Unarmed.
|
||||
.encyclopedia = Supply trucks are a convenient way to share cash when your ally is out of money, or you can't spend yours fast enough. They build far quicker than their cost suggests.
|
||||
.name = Supply Truck
|
||||
.description =
|
||||
Transports cash to other players.
|
||||
Builds quickly
|
||||
Unarmed
|
||||
.encyclopedia =
|
||||
Supply trucks are a convenient way to share cash when your ally is out of money, or you can't spend yours fast enough. They build far quicker than their cost suggests.
|
||||
|
||||
## ai.yaml
|
||||
bot-cabal =
|
||||
.name = Cabal
|
||||
.name = Cabal
|
||||
|
||||
bot-watson =
|
||||
.name = Watson
|
||||
.name = Watson
|
||||
|
||||
bot-hal9001 =
|
||||
.name = HAL 9001
|
||||
.name = HAL 9001
|
||||
|
||||
@@ -62,7 +62,7 @@ WorldLoaded = function()
|
||||
InitObjectives(Nod)
|
||||
|
||||
NodObjective1 = AddPrimaryObjective(Nod, "steal-nuclear-detonator")
|
||||
InfiltrateObjective = AddSecondaryObjective(Nod, "infiltrate-barracks-factory-conyard")
|
||||
CaptureObjective = AddSecondaryObjective(Nod, "capture-barracks-factory-conyard")
|
||||
|
||||
InsertNodUnits()
|
||||
|
||||
@@ -111,18 +111,18 @@ WorldLoaded = function()
|
||||
Trigger.AfterDelay(ProductionDelay, ProdTriggerFunction)
|
||||
|
||||
Trigger.OnAnyKilled(BuildingsToCapture, function()
|
||||
if not Nod.IsObjectiveCompleted(InfiltrateObjective) then
|
||||
Nod.MarkFailedObjective(InfiltrateObjective)
|
||||
if not Nod.IsObjectiveCompleted(CaptureObjective) then
|
||||
Nod.MarkFailedObjective(CaptureObjective)
|
||||
end
|
||||
end)
|
||||
|
||||
local captured = 0
|
||||
Utils.Do(BuildingsToCapture, function(building)
|
||||
local captured = 0
|
||||
Trigger.OnCapture(building, function()
|
||||
captured = captured + 1
|
||||
|
||||
if captured == 3 then
|
||||
Nod.MarkCompletedObjective(InfiltrateObjective)
|
||||
Nod.MarkCompletedObjective(CaptureObjective)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Metadata:
|
||||
Title: mod-title
|
||||
Version: {DEV_VERSION}
|
||||
Version: release-20250330
|
||||
Website: https://www.openra.net
|
||||
WebIcon32: https://www.openra.net/images/icons/cnc_32x32.png
|
||||
WindowTitle: mod-windowtitle
|
||||
@@ -8,13 +8,13 @@ Metadata:
|
||||
PackageFormats: Mix
|
||||
|
||||
FileSystem: ContentInstallerFileSystem
|
||||
Packages:
|
||||
SystemPackages:
|
||||
^EngineDir
|
||||
$cnc: cnc
|
||||
^EngineDir|mods/common: common
|
||||
~^SupportDir|Content/cnc: content
|
||||
cnc|scripts
|
||||
common|scripts
|
||||
cnc|scripts
|
||||
cnc|uibits
|
||||
ContentPackages:
|
||||
content|speech.mix
|
||||
@@ -30,6 +30,7 @@ FileSystem: ContentInstallerFileSystem
|
||||
~content|scores-covertops.mix
|
||||
~content|transit.mix
|
||||
~content|general.mix
|
||||
# Below are mod-provided (system) packages that need to be loaded after the content packages so they can override content assets.
|
||||
cnc|bits/snow.mix
|
||||
cnc|bits
|
||||
cnc|bits/jungle
|
||||
@@ -39,7 +40,7 @@ FileSystem: ContentInstallerFileSystem
|
||||
|
||||
MapFolders:
|
||||
cnc|maps: System
|
||||
~^SupportDir|maps/cnc/{DEV_VERSION}: User
|
||||
~^SupportDir|maps/cnc/release-20250330: User
|
||||
|
||||
Rules:
|
||||
cnc|rules/misc.yaml
|
||||
|
||||
@@ -69,8 +69,6 @@ tc04:
|
||||
SNOW: tc04.sno
|
||||
JUNGLE: tc04.jun
|
||||
idle:
|
||||
damaged-idle:
|
||||
Start: 1
|
||||
|
||||
tc04.husk:
|
||||
Defaults:
|
||||
@@ -80,7 +78,7 @@ tc04.husk:
|
||||
SNOW: tc04.sno
|
||||
JUNGLE: tc04.jun
|
||||
idle:
|
||||
Start: 2
|
||||
Start: 1
|
||||
|
||||
tc05:
|
||||
Defaults:
|
||||
@@ -90,8 +88,6 @@ tc05:
|
||||
SNOW: tc05.sno
|
||||
JUNGLE: tc05.jun
|
||||
idle:
|
||||
damaged-idle:
|
||||
Start: 1
|
||||
|
||||
tc05.husk:
|
||||
Defaults:
|
||||
@@ -101,7 +97,7 @@ tc05.husk:
|
||||
SNOW: tc05.sno
|
||||
JUNGLE: tc05.jun
|
||||
idle:
|
||||
Start: 2
|
||||
Start: 1
|
||||
|
||||
tc03:
|
||||
Defaults:
|
||||
|
||||
@@ -152,32 +152,30 @@ ScrollPanel@SPECTATOR_LABEL_DROPDOWN_TEMPLATE:
|
||||
ScrollPanel@NEWS_PANEL:
|
||||
Width: 400
|
||||
Height: 265
|
||||
TopBottomSpacing: 5
|
||||
TopBottomSpacing: 15
|
||||
ItemSpacing: 5
|
||||
Children:
|
||||
Container@NEWS_ITEM_TEMPLATE:
|
||||
X: 10
|
||||
Y: 5
|
||||
Width: PARENT_WIDTH - 40
|
||||
Height: 45
|
||||
Height: 40
|
||||
Children:
|
||||
Label@TITLE:
|
||||
Width: PARENT_WIDTH
|
||||
Height: 25
|
||||
Height: 15
|
||||
Align: Center
|
||||
Font: Bold
|
||||
Label@AUTHOR_DATETIME:
|
||||
Y: 25
|
||||
Y: 20
|
||||
Width: PARENT_WIDTH
|
||||
Height: 15
|
||||
Align: Center
|
||||
Font: TinyBold
|
||||
Label@CONTENT:
|
||||
Y: 45
|
||||
Y: 40
|
||||
Width: PARENT_WIDTH
|
||||
Label@NEWS_STATUS:
|
||||
X: 80
|
||||
Y: 0
|
||||
Width: PARENT_WIDTH - 80 - 80 - 24
|
||||
Height: PARENT_HEIGHT
|
||||
Align: Center
|
||||
|
||||
@@ -399,7 +399,7 @@ Container@EDITOR_WORLD_ROOT:
|
||||
Y: 12
|
||||
Width: 55
|
||||
Height: 25
|
||||
Text: label-tools-bg-categories
|
||||
Text: label-tool-bg-category
|
||||
Align: Right
|
||||
Font: TinyBold
|
||||
DropDownButton@TOOLS_DROPDOWN:
|
||||
@@ -600,7 +600,7 @@ Container@EDITOR_WORLD_ROOT:
|
||||
Height: 25
|
||||
Font: Bold
|
||||
Align: Left
|
||||
Text: label-copy-filters
|
||||
Text: label-selection-filters
|
||||
Checkbox@COPY_FILTER_TERRAIN_CHECKBOX:
|
||||
X: 5
|
||||
Y: 70
|
||||
@@ -654,6 +654,17 @@ Container@EDITOR_WORLD_ROOT:
|
||||
Width: 55
|
||||
Height: 22
|
||||
Align: Left
|
||||
Button@SELECTION_DELETE_BUTTON:
|
||||
X: 5
|
||||
Y: 222
|
||||
Width: 75
|
||||
Height: 25
|
||||
Text: button-delete-area.label
|
||||
Font: Bold
|
||||
Key: EditorDeleteSelection
|
||||
TooltipTemplate: BUTTON_TOOLTIP
|
||||
TooltipText: button-delete-area.tooltip
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
Button@SELECTION_CANCEL_BUTTON:
|
||||
X: 208
|
||||
Y: 222
|
||||
@@ -747,8 +758,12 @@ Container@EDITOR_WORLD_ROOT:
|
||||
X: 5
|
||||
Width: 75
|
||||
Height: 25
|
||||
Text: button-container-delete
|
||||
Text: button-delete-actor.label
|
||||
Font: Bold
|
||||
Key: EditorDeleteSelection
|
||||
TooltipTemplate: BUTTON_TOOLTIP
|
||||
TooltipText: button-delete-actor.tooltip
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
Button@CANCEL_BUTTON:
|
||||
X: 127
|
||||
Width: 75
|
||||
|
||||
@@ -12,7 +12,7 @@ Container@SKIRMISH_STATS:
|
||||
Width: 482
|
||||
Height: 25
|
||||
Font: MediumBold
|
||||
Text: label-objective-mission
|
||||
Text: label-mission-objective
|
||||
Label@STATS_STATUS:
|
||||
X: 100
|
||||
Y: 22
|
||||
@@ -25,7 +25,7 @@ Container@SKIRMISH_STATS:
|
||||
Width: 482
|
||||
Height: 20
|
||||
Font: Bold
|
||||
Text: checkbox-objective-stats
|
||||
Text: checkbox-stats-objective
|
||||
Disabled: true
|
||||
TextColorDisabled: FFFFFF
|
||||
Container@STATS_HEADERS:
|
||||
|
||||
@@ -16,14 +16,14 @@ Background@KICK_CLIENT_DIALOG:
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Align: Center
|
||||
Text: label-kick-client-dialog-texta
|
||||
Text: label-kick-client-dialog-text-a
|
||||
Label@TEXTB:
|
||||
Y: 86
|
||||
Width: PARENT_WIDTH
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Align: Center
|
||||
Text: label-kick-client-dialog-textb
|
||||
Text: label-kick-client-dialog-text-b
|
||||
Checkbox@PREVENT_REJOINING_CHECKBOX:
|
||||
X: (PARENT_WIDTH - WIDTH) / 2
|
||||
Y: 120
|
||||
@@ -97,14 +97,14 @@ Background@FORCE_START_DIALOG:
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Align: Center
|
||||
Text: label-force-start-dialog-texta
|
||||
Text: label-force-start-dialog-text-a
|
||||
Label@TEXTB:
|
||||
Y: 86
|
||||
Width: PARENT_WIDTH
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Align: Center
|
||||
Text: label-force-start-dialog-textb
|
||||
Text: label-force-start-dialog-text-b
|
||||
Container@KICK_WARNING:
|
||||
Width: PARENT_WIDTH
|
||||
Children:
|
||||
|
||||
@@ -252,8 +252,8 @@ Container@LOBBY_PLAYER_BIN:
|
||||
Height: 25
|
||||
Text: label-lobby-players-faction
|
||||
Label@TEAM:
|
||||
X: 420
|
||||
Width: 42
|
||||
X: 410
|
||||
Width: 43
|
||||
Height: 25
|
||||
Text: label-lobby-players-team
|
||||
Align: Center
|
||||
|
||||
@@ -76,6 +76,44 @@ Background@MISSIONBROWSER_PANEL:
|
||||
Height: 31
|
||||
Font: Bold
|
||||
Text: button-missionbrowser-panel-mission-options
|
||||
Container@MISSION_MINIFIED_OPTIONS:
|
||||
Width: PARENT_WIDTH
|
||||
Height: PARENT_HEIGHT
|
||||
Visible: False
|
||||
Children:
|
||||
LabelForInput@DIFFICULTY_DESC:
|
||||
Y: PARENT_HEIGHT - HEIGHT
|
||||
Width: 56
|
||||
Height: 25
|
||||
Align: Right
|
||||
Text: dropdown-missionbrowser-difficulty.label
|
||||
For: DIFFICULTY
|
||||
DropDownButton@DIFFICULTY:
|
||||
X: 61
|
||||
Y: PARENT_HEIGHT - HEIGHT
|
||||
Width: 135
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Text: label-missionbrowser-normal-difficulty
|
||||
TooltipText: dropdown-missionbrowser-difficulty.description
|
||||
PanelRoot: MISSION_DROPDOWN_PANEL_ROOT
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
LabelForInput@GAMESPEED_DESC:
|
||||
X: PARENT_WIDTH - WIDTH - 115
|
||||
Y: PARENT_HEIGHT - HEIGHT
|
||||
Width: 120
|
||||
Height: 25
|
||||
Align: Right
|
||||
Text: dropdown-missionbrowser-gamespeed
|
||||
For: GAMESPEED
|
||||
DropDownButton@GAMESPEED:
|
||||
X: PARENT_WIDTH - WIDTH
|
||||
Y: PARENT_HEIGHT - HEIGHT
|
||||
Width: 110
|
||||
Height: 25
|
||||
Font: Regular
|
||||
PanelRoot: MISSION_DROPDOWN_PANEL_ROOT
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
Container@MISSION_DETAIL:
|
||||
Y: 212
|
||||
Width: PARENT_WIDTH
|
||||
|
||||
@@ -90,7 +90,7 @@ Container@ADVANCED_PANEL:
|
||||
Font: Regular
|
||||
Text: checkbox-sendsysinfo-container
|
||||
Label@SENDSYSINFO_DESC:
|
||||
Y: 15
|
||||
Y: 30
|
||||
Width: PARENT_WIDTH
|
||||
Height: 30
|
||||
Font: Tiny
|
||||
|
||||
@@ -52,7 +52,6 @@ label-save-map-panel-directory = Directory:
|
||||
label-save-map-panel-filename = Filename:
|
||||
button-save-map-panel = Save
|
||||
label-actor-edit-panel-id = ID
|
||||
button-container-delete = Delete
|
||||
button-container-ok = OK
|
||||
label-tiles-bg-search = Search:
|
||||
label-bg-filter = Filter:
|
||||
@@ -61,12 +60,12 @@ label-actor-bg-owner = Owner:
|
||||
label-area-selection = Area Selection
|
||||
label-area-info = Area Info
|
||||
label-selected-area-diagonal = Diagonal:
|
||||
label-selected-area-resources= Resources:
|
||||
label-copy-filters = Copy Filters
|
||||
label-selected-area-resources = Resources:
|
||||
label-selection-filters = Filters
|
||||
label-filter-terrain = Terrain
|
||||
label-filter-resources = Resources
|
||||
label-filter-actors = Actors
|
||||
label-tools-bg-categories = Tool:
|
||||
label-tool-bg-category = Tool:
|
||||
button-marker-tiles-clear-current = Clear Current
|
||||
button-marker-tiles-clear-all = Clear All
|
||||
label-marker-layer-num-sides = Number of Sides
|
||||
@@ -81,25 +80,33 @@ button-map-editor-tab-container-actors-tooltip = Actors
|
||||
button-map-editor-tab-container-tools-tooltip = Tools
|
||||
button-map-editor-tab-container-history-tooltip = History
|
||||
|
||||
button-delete-area =
|
||||
.label = Delete
|
||||
.tooltip = Remove everything in the selected area
|
||||
|
||||
button-delete-actor =
|
||||
.label = Delete
|
||||
.tooltip = Remove the selected actor
|
||||
|
||||
button-editor-world-root-options =
|
||||
.label = Menu
|
||||
.tooltip = Menu
|
||||
.label = Menu
|
||||
.tooltip = Menu
|
||||
|
||||
button-editor-world-root-copy =
|
||||
.label = Copy
|
||||
.tooltip = Copy the selected area
|
||||
.label = Copy
|
||||
.tooltip = Copy the selected area
|
||||
|
||||
button-editor-world-root-paste =
|
||||
.label = Paste
|
||||
.tooltip = Paste the saved area
|
||||
.label = Paste
|
||||
.tooltip = Paste the saved area
|
||||
|
||||
button-editor-world-root-undo =
|
||||
.label = Undo
|
||||
.tooltip = Undo last step
|
||||
.label = Undo
|
||||
.tooltip = Undo last step
|
||||
|
||||
button-editor-world-root-redo =
|
||||
.label = Redo
|
||||
.tooltip = Redo last step
|
||||
.label = Redo
|
||||
.tooltip = Redo last step
|
||||
|
||||
dropdownbutton-editor-world-root-overlay-button = Overlays
|
||||
button-select-categories-buttons-all = All
|
||||
@@ -119,8 +126,8 @@ button-gamesave-browser-panel-save = Save
|
||||
|
||||
## ingame-chat.yaml, ingame-infochat.yaml
|
||||
button-chat-chrome-mode =
|
||||
.label = Team
|
||||
.tooltip = Toggle chat mode
|
||||
.label = Team
|
||||
.tooltip = Toggle chat mode
|
||||
|
||||
## ingame-debug-hpf.yaml
|
||||
dropdownbutton-hpf-overlay-locomotor = Select Locomotor
|
||||
@@ -156,8 +163,8 @@ label-script-error-panel-desc-b = The details of the error have been saved to lu
|
||||
label-script-error-panel-desc-c = Please send this file to the map author so that they can fix this issue.
|
||||
|
||||
## ingame-infostats.yaml
|
||||
label-objective-mission = Mission:
|
||||
checkbox-objective-stats = Destroy all opposition!
|
||||
label-mission-objective = Mission:
|
||||
checkbox-stats-objective = Destroy all opposition!
|
||||
label-stats-player = Player
|
||||
label-stats-faction = Faction
|
||||
label-stats-score = Score
|
||||
@@ -167,15 +174,15 @@ label-stats-actions = Actions
|
||||
label-menu-buttons-title = Options
|
||||
|
||||
## lobby-kickdialogs.yaml
|
||||
label-kick-client-dialog-texta = You may also apply a temporary ban, preventing
|
||||
label-kick-client-dialog-textb = them from joining for the remainder of this game.
|
||||
label-kick-client-dialog-text-a = You may also apply a temporary ban, preventing
|
||||
label-kick-client-dialog-text-b = them from joining for the remainder of this game.
|
||||
checkbox-kick-client-dialog-prevent-rejoining = Temporarily Ban
|
||||
button-kick-client-dialog = Kick
|
||||
label-kick-spectators-dialog-title = Kick Spectators
|
||||
button-kick-spectators-dialog-ok = Ok
|
||||
label-force-start-dialog-title = Start Game?
|
||||
label-force-start-dialog-texta = One or more players are not yet ready.
|
||||
label-force-start-dialog-textb = Are you sure that you want to force start the game?
|
||||
label-force-start-dialog-text-a = One or more players are not yet ready.
|
||||
label-force-start-dialog-text-b = Are you sure that you want to force start the game?
|
||||
label-kick-warning-a = One or more clients are missing the selected
|
||||
label-kick-warning-b = map, and will be kicked from the server.
|
||||
button-force-start-dialog-start = Start
|
||||
@@ -251,8 +258,8 @@ button-multiplayer-tabs-servers-tab = Servers
|
||||
button-server-lobby-changemap = Change Map
|
||||
|
||||
button-lobbychat-chat-mode =
|
||||
.label = Team
|
||||
.tooltip = Toggle chat mode
|
||||
.label = Team
|
||||
.tooltip = Toggle chat mode
|
||||
|
||||
button-server-lobby-start-game = Start Game
|
||||
button-server-lobby-disconnect = Leave Game
|
||||
@@ -343,6 +350,12 @@ button-missionbrowser-panel-stop-info-video = Stop Info Video
|
||||
button-missionbrowser-panel-play = Play
|
||||
button-missionbrowser-panel-mission-info = Mission Info
|
||||
button-missionbrowser-panel-mission-options = Options
|
||||
dropdown-missionbrowser-difficulty =
|
||||
.label = Difficulty
|
||||
.description = The difficulty of the mission
|
||||
|
||||
dropdown-missionbrowser-gamespeed = Speed:
|
||||
label-missionbrowser-normal-difficulty = Normal
|
||||
|
||||
## multiplayer-browser.yaml
|
||||
image-multiplayer-panel-password-protected-tooltip = Requires Password
|
||||
@@ -454,8 +467,8 @@ checkbox-mute-sound-container = Mute Sound
|
||||
label-sound-volume-container = Sound Volume:
|
||||
|
||||
checkbox-mute-background-music-container =
|
||||
.label = Mute Menu Music
|
||||
.tooltip = Mute background music when no specific track is playing
|
||||
.label = Mute Menu Music
|
||||
.tooltip = Mute background music when no specific track is playing
|
||||
|
||||
label-music-title-volume-container = Music Volume:
|
||||
label-audio-device-container = Audio Device:
|
||||
@@ -467,16 +480,16 @@ label-target-lines-dropdown-container = Target Lines:
|
||||
label-status-bar-dropdown-container-bars = Status Bars:
|
||||
|
||||
checkbox-player-stance-colors-container =
|
||||
.label = Player Relationship Colors
|
||||
.tooltip = Change player colors based on relationship (own, enemy, ally, neutral)
|
||||
.label = Player Relationship Colors
|
||||
.tooltip = Change player colors based on relationship (own, enemy, ally, neutral)
|
||||
|
||||
checkbox-ui-feedback-container =
|
||||
.label = Show UI Feedback Notifications
|
||||
.tooltip = Show transient text notifications for UI events
|
||||
.label = Show UI Feedback Notifications
|
||||
.tooltip = Show transient text notifications for UI events
|
||||
|
||||
checkbox-transients-container =
|
||||
.label = Show Game Event Notifications
|
||||
.tooltip = Show transient text notifications for game events
|
||||
.label = Show Game Event Notifications
|
||||
.tooltip = Show transient text notifications for game events
|
||||
|
||||
checkbox-pause-shellmap-container = Pause Menu Background
|
||||
checkbox-hide-replay-chat-container = Hide Chat in Replays
|
||||
@@ -511,12 +524,12 @@ label-notices-readonly-notice = This hotkey cannot be modified
|
||||
button-hotkey-remap-dialog-override = Override
|
||||
|
||||
button-hotkey-remap-dialog-clear =
|
||||
.label = Clear
|
||||
.tooltip = Unbind the hotkey
|
||||
.label = Clear
|
||||
.tooltip = Unbind the hotkey
|
||||
|
||||
button-hotkey-remap-dialog-reset =
|
||||
.label = Reset
|
||||
.tooltip = Reset to default
|
||||
.label = Reset
|
||||
.tooltip = Reset to default
|
||||
|
||||
## settings-input.yaml
|
||||
label-zoom-modifier-container = Zoom Modifier:
|
||||
|
||||
@@ -17,7 +17,6 @@ notification-insufficient-enabled-spawn-points = Unable to start the game until
|
||||
notification-malformed-command = Malformed { $command } command.
|
||||
notification-state-unchanged-ready = Cannot change state when marked as ready.
|
||||
notification-invalid-faction-selected = Invalid faction selected: { $faction }.
|
||||
notification-supported-factions = Supported values: { $factions }.
|
||||
notification-state-unchanged-game-started = State cannot be changed once the game has started ({ $command }).
|
||||
notification-requires-host = Only the host can do that.
|
||||
notification-invalid-bot-slot = Cannot add bots to a slot with another client.
|
||||
@@ -206,6 +205,9 @@ label-chat-availability =
|
||||
*[other] Chat available in { $seconds } seconds...
|
||||
}
|
||||
|
||||
## LobbyLogic, ServerListLogic
|
||||
label-bot-player = AI Player
|
||||
|
||||
## IngameMenuLogic
|
||||
menu-ingame =
|
||||
.leave = Leave
|
||||
@@ -804,6 +806,7 @@ notification-copied-tiles =
|
||||
|
||||
## EditorDefaultBrush
|
||||
notification-selected-area = Selected area { $x },{ $y } ({ $width },{ $height })
|
||||
notification-removed-area = Removed area { $x },{ $y } ({ $width },{ $height })
|
||||
notification-selected-actor = Selected actor { $id }
|
||||
notification-cleared-selection = Cleared selection
|
||||
notification-removed-actor = Removed { $name } ({ $id })
|
||||
|
||||
@@ -70,6 +70,7 @@ hotkey-description-editorredo = Redo
|
||||
hotkey-description-editorcopy = Copy
|
||||
hotkey-description-editorquicksave = Save Map
|
||||
hotkey-description-editorpaste = Paste
|
||||
hotkey-description-editordeleteselection = Delete Selection
|
||||
hotkey-description-editorselecttab = Select Tab
|
||||
hotkey-description-editortilestab = Tiles Tab
|
||||
hotkey-description-editoroverlaystab = Overlays Tab
|
||||
|
||||
@@ -34,6 +34,13 @@ EditorPaste: V Ctrl
|
||||
Platform:
|
||||
OSX: V Meta
|
||||
|
||||
EditorDeleteSelection: DELETE
|
||||
Description: hotkey-description-editordeleteselection
|
||||
Types: Editor
|
||||
Contexts: editor
|
||||
Platform:
|
||||
OSX: BACKSPACE
|
||||
|
||||
EditorSelectTab: E
|
||||
Description: hotkey-description-editorselecttab
|
||||
Types: Editor
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Metadata:
|
||||
Title: mod-title
|
||||
Version: {DEV_VERSION}
|
||||
Version: release-20250330
|
||||
Hidden: true
|
||||
|
||||
FileSystem: DefaultFileSystem
|
||||
|
||||
@@ -116,32 +116,30 @@ ScrollPanel@SPECTATOR_DROPDOWN_TEMPLATE:
|
||||
ScrollPanel@NEWS_PANEL:
|
||||
Width: 400
|
||||
Height: 265
|
||||
TopBottomSpacing: 5
|
||||
TopBottomSpacing: 15
|
||||
ItemSpacing: 5
|
||||
Children:
|
||||
Container@NEWS_ITEM_TEMPLATE:
|
||||
X: 10
|
||||
Y: 5
|
||||
Width: PARENT_WIDTH - 40
|
||||
Height: 45
|
||||
Height: 40
|
||||
Children:
|
||||
Label@TITLE:
|
||||
Width: PARENT_WIDTH
|
||||
Height: 25
|
||||
Height: 15
|
||||
Align: Center
|
||||
Font: Bold
|
||||
Label@AUTHOR_DATETIME:
|
||||
Y: 25
|
||||
Y: 20
|
||||
Width: PARENT_WIDTH
|
||||
Height: 15
|
||||
Align: Center
|
||||
Font: TinyBold
|
||||
Label@CONTENT:
|
||||
Y: 45
|
||||
Y: 40
|
||||
Width: PARENT_WIDTH
|
||||
Label@NEWS_STATUS:
|
||||
X: 80
|
||||
Y: 0
|
||||
Width: PARENT_WIDTH - 80 - 80 - 24
|
||||
Height: PARENT_HEIGHT
|
||||
Align: Center
|
||||
|
||||
@@ -12,7 +12,7 @@ Container@SKIRMISH_STATS:
|
||||
Width: 482
|
||||
Height: 25
|
||||
Font: MediumBold
|
||||
Text: label-objective-mission
|
||||
Text: label-mission-objective
|
||||
Label@STATS_STATUS:
|
||||
X: 100
|
||||
Y: 22
|
||||
@@ -25,7 +25,7 @@ Container@SKIRMISH_STATS:
|
||||
Width: 482
|
||||
Height: 20
|
||||
Font: Bold
|
||||
Text: checkbox-objective-stats
|
||||
Text: checkbox-stats-objective
|
||||
Disabled: true
|
||||
TextColorDisabled: FFFFFF
|
||||
Container@STATS_HEADERS:
|
||||
|
||||
@@ -252,8 +252,8 @@ Container@LOBBY_PLAYER_BIN:
|
||||
Height: 25
|
||||
Text: label-lobby-players-faction
|
||||
Label@TEAM:
|
||||
X: 420
|
||||
Width: 42
|
||||
X: 410
|
||||
Width: 43
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: label-lobby-players-team
|
||||
|
||||
@@ -144,6 +144,45 @@ Background@MISSIONBROWSER_PANEL:
|
||||
Visible: False
|
||||
PanelRoot: MISSION_DROPDOWN_PANEL_ROOT
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
Container@MISSION_MINIFIED_OPTIONS:
|
||||
X: 220
|
||||
Width: PARENT_WIDTH - 220
|
||||
Height: PARENT_HEIGHT
|
||||
Visible: False
|
||||
Children:
|
||||
LabelForInput@DIFFICULTY_DESC:
|
||||
Y: PARENT_HEIGHT - 80
|
||||
Width: 56
|
||||
Height: 25
|
||||
Align: Right
|
||||
Text: dropdown-missionbrowser-difficulty.label
|
||||
For: DIFFICULTY
|
||||
DropDownButton@DIFFICULTY:
|
||||
X: 71
|
||||
Y: PARENT_HEIGHT - 80
|
||||
Width: 140
|
||||
Height: 25
|
||||
Font: Regular
|
||||
Text: label-missionbrowser-normal-difficulty
|
||||
TooltipText: dropdown-missionbrowser-difficulty.description
|
||||
PanelRoot: MISSION_DROPDOWN_PANEL_ROOT
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
LabelForInput@GAMESPEED_DESC:
|
||||
X: PARENT_WIDTH - 140 - WIDTH - 35
|
||||
Y: PARENT_HEIGHT - 80
|
||||
Width: 56
|
||||
Height: 25
|
||||
Align: Right
|
||||
Text: dropdown-missionbrowser-gamespeed
|
||||
For: GAMESPEED
|
||||
DropDownButton@GAMESPEED:
|
||||
X: PARENT_WIDTH - 160
|
||||
Y: PARENT_HEIGHT - 80
|
||||
Width: 140
|
||||
Height: 25
|
||||
Font: Regular
|
||||
PanelRoot: MISSION_DROPDOWN_PANEL_ROOT
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
Button@START_BRIEFING_VIDEO_BUTTON:
|
||||
X: 20
|
||||
Y: PARENT_HEIGHT - 45
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## world
|
||||
dropdown-difficulty =
|
||||
.label = Difficulty
|
||||
.description = The difficulty of the mission.
|
||||
.description = The difficulty of the mission
|
||||
|
||||
options-difficulty =
|
||||
.easy = Easy
|
||||
@@ -10,7 +10,7 @@ options-difficulty =
|
||||
|
||||
## player
|
||||
bot-campaign-ai =
|
||||
.name = Campaign Player AI
|
||||
.name = Campaign Player AI
|
||||
|
||||
## campaign-tooltips.yaml
|
||||
neutral-prefix = Neutral
|
||||
|
||||
@@ -10,20 +10,20 @@ button-replay-player-pause-tooltip = Pause
|
||||
button-replay-player-play-tooltip = Play
|
||||
|
||||
button-replay-player-slow =
|
||||
.tooltip = Slow speed
|
||||
.label = 50%
|
||||
.tooltip = Slow speed
|
||||
.label = 50%
|
||||
|
||||
button-replay-player-regular =
|
||||
.tooltip = Regular speed
|
||||
.label = 100%
|
||||
.tooltip = Regular speed
|
||||
.label = 100%
|
||||
|
||||
button-replay-player-fast =
|
||||
.tooltip = Fast speed
|
||||
.label = 200%
|
||||
.tooltip = Fast speed
|
||||
.label = 200%
|
||||
|
||||
button-replay-player-maximum =
|
||||
.tooltip = Maximum speed
|
||||
.label = MAX
|
||||
.tooltip = Maximum speed
|
||||
.label = MAX
|
||||
|
||||
label-basic-stats-player-header = Player
|
||||
label-basic-stats-cash-header = Cash
|
||||
@@ -63,12 +63,13 @@ label-mute-indicator = Audio Muted
|
||||
|
||||
## ingame-player.yaml
|
||||
supportpowers-support-powers-palette =
|
||||
.ready = READY
|
||||
.hold = ON HOLD
|
||||
.ready = READY
|
||||
.hold = ON HOLD
|
||||
|
||||
button-command-bar-attack-move =
|
||||
.tooltip = Attack Move
|
||||
.tooltipdesc = Selected units will move to the desired location
|
||||
.tooltip = Attack Move
|
||||
.tooltipdesc =
|
||||
Selected units will move to the desired location
|
||||
and attack any enemies they encounter en route.
|
||||
|
||||
Hold <(Ctrl)> while targeting to order an Assault Move
|
||||
@@ -77,8 +78,9 @@ button-command-bar-attack-move =
|
||||
Left-click icon then right-click on target location.
|
||||
|
||||
button-command-bar-force-move =
|
||||
.tooltip = Force Move
|
||||
.tooltipdesc = Selected units will move to the desired location
|
||||
.tooltip = Force Move
|
||||
.tooltipdesc =
|
||||
Selected units will move to the desired location
|
||||
- Default activity for the target is suppressed
|
||||
- Vehicles will attempt to crush enemies at the target location
|
||||
- Deployed thumpers will undeploy and move to the target location
|
||||
@@ -87,8 +89,9 @@ button-command-bar-force-move =
|
||||
Hold <(Alt)> to activate temporarily while commanding units.
|
||||
|
||||
button-command-bar-force-attack =
|
||||
.tooltip = Force Attack
|
||||
.tooltipdesc = Selected units will attack the targeted unit or location
|
||||
.tooltip = Force Attack
|
||||
.tooltipdesc =
|
||||
Selected units will attack the targeted unit or location
|
||||
- Default activity for the target is suppressed
|
||||
- Allows targeting of own or ally forces
|
||||
|
||||
@@ -96,14 +99,16 @@ button-command-bar-force-attack =
|
||||
Hold <(Ctrl)> to activate temporarily while commanding units.
|
||||
|
||||
button-command-bar-guard =
|
||||
.tooltip = Guard
|
||||
.tooltipdesc = Selected units will follow the targeted unit.
|
||||
.tooltip = Guard
|
||||
.tooltipdesc =
|
||||
Selected units will follow the targeted unit.
|
||||
|
||||
Left-click icon then right-click on target unit.
|
||||
|
||||
button-command-bar-deploy =
|
||||
.tooltip = Deploy
|
||||
.tooltipdesc = Selected units will perform their default deploy activity
|
||||
.tooltip = Deploy
|
||||
.tooltipdesc =
|
||||
Selected units will perform their default deploy activity
|
||||
- MCVs will unpack into a Construction Yard
|
||||
- Thumpers will start or stop attracting worms
|
||||
- Devastators will become immobilized and explode
|
||||
@@ -111,22 +116,25 @@ button-command-bar-deploy =
|
||||
Acts immediately on selected units.
|
||||
|
||||
button-command-bar-scatter =
|
||||
.tooltip = Scatter
|
||||
.tooltipdesc = Selected units will stop their current activity
|
||||
.tooltip = Scatter
|
||||
.tooltipdesc =
|
||||
Selected units will stop their current activity
|
||||
and move to a nearby location.
|
||||
|
||||
Acts immediately on selected units.
|
||||
|
||||
button-command-bar-stop =
|
||||
.tooltip = Stop
|
||||
.tooltipdesc = Selected units will stop their current activity.
|
||||
.tooltip = Stop
|
||||
.tooltipdesc =
|
||||
Selected units will stop their current activity.
|
||||
Selected buildings will reset their rally point.
|
||||
|
||||
Acts immediately on selected targets.
|
||||
|
||||
button-command-bar-queue-orders =
|
||||
.tooltip = Waypoint Mode
|
||||
.tooltipdesc = Use Waypoint Mode to give multiple linking commands
|
||||
.tooltip = Waypoint Mode
|
||||
.tooltipdesc =
|
||||
Use Waypoint Mode to give multiple linking commands
|
||||
to the selected units. Units will execute the commands
|
||||
immediately upon receiving them.
|
||||
|
||||
@@ -134,26 +142,30 @@ button-command-bar-queue-orders =
|
||||
Hold <(Shift)> to activate temporarily while commanding units.
|
||||
|
||||
button-stance-bar-attackanything =
|
||||
.tooltip = Attack Anything Stance
|
||||
.tooltipdesc = Set the selected units to Attack Anything stance:
|
||||
.tooltip = Attack Anything Stance
|
||||
.tooltipdesc =
|
||||
Set the selected units to Attack Anything stance:
|
||||
- Units will attack enemy units and structures on sight
|
||||
- Units will pursue attackers across the battlefield
|
||||
|
||||
button-stance-bar-defend =
|
||||
.tooltip = Defend Stance
|
||||
.tooltipdesc = Set the selected units to Defend stance:
|
||||
.tooltip = Defend Stance
|
||||
.tooltipdesc =
|
||||
Set the selected units to Defend stance:
|
||||
- Units will attack enemy units on sight
|
||||
- Units will not move or pursue enemies
|
||||
|
||||
button-stance-bar-returnfire =
|
||||
.tooltip = Return Fire Stance
|
||||
.tooltipdesc = Set the selected units to Return Fire stance:
|
||||
.tooltip = Return Fire Stance
|
||||
.tooltipdesc =
|
||||
Set the selected units to Return Fire stance:
|
||||
- Units will retaliate against enemies that attack them
|
||||
- Units will not move or pursue enemies
|
||||
|
||||
button-stance-bar-holdfire =
|
||||
.tooltip = Hold Fire Stance
|
||||
.tooltipdesc = Set the selected units to Hold Fire stance:
|
||||
.tooltip = Hold Fire Stance
|
||||
.tooltipdesc =
|
||||
Set the selected units to Hold Fire stance:
|
||||
- Units will not fire upon enemies
|
||||
- Units will not move or pursue enemies
|
||||
|
||||
@@ -164,8 +176,8 @@ button-top-buttons-power-tooltip = Power Down
|
||||
button-top-buttons-options-tooltip = Options
|
||||
|
||||
productionpalette-sidebar-production-palette =
|
||||
.ready = READY
|
||||
.hold = ON HOLD
|
||||
.ready = READY
|
||||
.hold = ON HOLD
|
||||
|
||||
button-production-types-building-tooltip = Buildings
|
||||
button-production-types-infantry-tooltip = Infantry
|
||||
|
||||
@@ -17,7 +17,7 @@ not-enough-silos = We don't have enough silo space to store the required amount
|
||||
|
||||
## atreides-01ab, harkonnen-01ab, ordos01ab
|
||||
build-concrete = Build a concrete foundation before placing structures.
|
||||
build-windtrap = Build a Wind Trap to generate power.
|
||||
build-wind-trap = Build a Wind Trap to generate power.
|
||||
build-refinery = Build a Refinery to collect Spice.
|
||||
build-silo = Build a Silo to store additional Spice.
|
||||
|
||||
@@ -43,7 +43,7 @@ eliminate-ordos-units-reinforcements = Eliminate all Ordos forces and reinforcem
|
||||
destroy-harkonnen = Destroy the Harkonnen.
|
||||
|
||||
## atreides-04
|
||||
fremen-leader = Fremen Leader.
|
||||
fremen-leader = Fremen Leader
|
||||
sietch-integrity = Sietch structural integrity: { $integrity }%
|
||||
protect-fremen-sietch = Protect the Fremen Sietch.
|
||||
keep-sietch-intact = Keep the Sietch { $integrity }% intact!
|
||||
@@ -127,7 +127,7 @@ atreides-hostile = The Atreides are now hostile!
|
||||
mercenaries-annihilated = The Mercenaries have been annihilated!
|
||||
ally-mercenaries = Persuade the Mercenaries to fight alongside House Harkonnen.
|
||||
mercenary-leader-captured-allied = Leader Captured. The mercenaries have been persuaded to fight with House Harkonnen.
|
||||
can-not-stand-harkonnen-must-become-neutral = We cannot stand against the Harkonnen. We must become neutral.
|
||||
cannot-stand-harkonnen-must-become-neutral = We cannot stand against the Harkonnen. We must become neutral.
|
||||
atreides-commander = Atreides Commander
|
||||
|
||||
## harkonnen-09ab
|
||||
|
||||
@@ -34,13 +34,13 @@ options-starting-units =
|
||||
resource-spice = Spice
|
||||
|
||||
faction-random =
|
||||
.name = Any
|
||||
.description = Random House.
|
||||
A random house is chosen at the start of the game.
|
||||
.name = Any
|
||||
.description = Random House
|
||||
A random house is chosen at the start of the game
|
||||
|
||||
faction-atreides =
|
||||
.name = Atreides
|
||||
.description = House Atreides
|
||||
.name = Atreides
|
||||
.description = House Atreides
|
||||
The noble Atreides, from the water world of Caladan,
|
||||
rely on their Ornithopters to ensure air superiority.
|
||||
They have allied themselves with the Fremen, the fearsome
|
||||
@@ -58,14 +58,14 @@ faction-atreides =
|
||||
- Airstrike
|
||||
|
||||
faction-harkonnen =
|
||||
.name = Harkonnen
|
||||
.description = House Harkonnen
|
||||
.name = Harkonnen
|
||||
.description = House Harkonnen
|
||||
The evil Harkonnen will stop at nothing to gain control of the spice.
|
||||
They rely on brute force and atomic weapons to achieve their goals:
|
||||
wealth, and the destruction of House Atreides.
|
||||
|
||||
Faction Variations:
|
||||
- Combat Tanks are more durable but move at a slower speed.
|
||||
- Combat Tanks are more durable but move at a slower speed
|
||||
|
||||
Special Units:
|
||||
- Sardaukar
|
||||
@@ -75,15 +75,15 @@ faction-harkonnen =
|
||||
- Death Hand Missile
|
||||
|
||||
faction-ordos =
|
||||
.name = Ordos
|
||||
.description = House Ordos
|
||||
.name = Ordos
|
||||
.description = House Ordos
|
||||
From the icy world of Sigma Draconis IV, the insidious Ordos are known for
|
||||
their wealth, greed, and treachery. They often turn to mercenaries, sabotage,
|
||||
and forbidden Ixian technologies to gain the upper hand.
|
||||
|
||||
Faction Variations:
|
||||
- Trikes are replaced by Raider Trikes.
|
||||
- Combat Tanks are faster but less durable.
|
||||
- Trikes are replaced by Raider Trikes
|
||||
- Combat Tanks are faster but less durable
|
||||
|
||||
Special Units:
|
||||
- Raider Trike
|
||||
@@ -92,16 +92,16 @@ faction-ordos =
|
||||
- Deviator
|
||||
|
||||
faction-corrino =
|
||||
.name = Corrino
|
||||
.name = Corrino
|
||||
|
||||
faction-mercenaries =
|
||||
.name = Mercenaries
|
||||
.name = Mercenaries
|
||||
|
||||
faction-smugglers =
|
||||
.name = Smugglers
|
||||
.name = Smugglers
|
||||
|
||||
faction-fremen =
|
||||
.name = Fremen
|
||||
.name = Fremen
|
||||
|
||||
## defaults.yaml
|
||||
notification-unit-lost = Unit lost.
|
||||
@@ -111,19 +111,23 @@ notification-primary-building-selected = Primary building selected.
|
||||
|
||||
## aircraft.yaml
|
||||
actor-carryall-reinforce =
|
||||
.name = Carryall
|
||||
.description = Large winged, planet-bound ship
|
||||
Automatically lifts harvesters to and from Spice fields.
|
||||
Lifts vehicles to Repair Pads when ordered to.
|
||||
.name = Carryall
|
||||
.description =
|
||||
Large winged, planet-bound ship
|
||||
Automatically lifts harvesters to and from Spice fields.
|
||||
Lifts vehicles to Repair Pads when ordered to.
|
||||
|
||||
actor-carryall-encyclopedia = Automatically transport Harvesters between the Spice Fields and Refineries. They can also pick up units and deliver them to the Repair Pad when ordered to.
|
||||
actor-carryall-encyclopedia =
|
||||
Automatically transport Harvesters between the Spice Fields and Refineries. They can also pick up units and deliver them to the Repair Pad when ordered to.
|
||||
|
||||
The Carryall is a lightly armored transport aircraft. It is vulnerable to missiles and can only be hit by anti-aircraft weapons.
|
||||
|
||||
actor-frigate-name = Frigate
|
||||
|
||||
actor-ornithopter =
|
||||
.name = Ornithopter
|
||||
.encyclopedia = The fastest aircraft on Dune, it is lightly armored and capable of dropping 500 lb bombs. Highly effective against infantry and lightly armored targets, with the ability to damage other armor types.
|
||||
.name = Ornithopter
|
||||
.encyclopedia =
|
||||
The fastest aircraft on Dune, it is lightly armored and capable of dropping 500 lb bombs. Highly effective against infantry and lightly armored targets, with the ability to damage other armor types.
|
||||
|
||||
actor-ornithopter-husk-name = Ornithopter
|
||||
actor-carryall-husk-name = Carryall
|
||||
@@ -158,81 +162,100 @@ meta-combat-tank-husk-name = Combat Tank (Destroyed)
|
||||
|
||||
## infantry.yaml
|
||||
actor-light-inf =
|
||||
.name = Light Infantry
|
||||
.description = General-purpose infantry.
|
||||
Strong vs. Infantry.
|
||||
Weak vs. Vehicles and Artillery.
|
||||
.encyclopedia = Lightly armored foot soldiers, equipped with 9mm RP assault rifles. They are effective against infantry and lightly armored vehicles.
|
||||
.name = Light Infantry
|
||||
.description =
|
||||
General-purpose infantry.
|
||||
Strong vs Infantry
|
||||
Weak vs Vehicles and Artillery
|
||||
.encyclopedia =
|
||||
Lightly armored foot soldiers, equipped with 9mm RP assault rifles. They are effective against infantry and lightly armored vehicles.
|
||||
|
||||
Light Infantry are resistant to missiles and large-caliber guns, but are very vulnerable to high-explosives, fire and bullet weapons.
|
||||
Light Infantry are resistant to missiles and large-caliber guns, but are very vulnerable to high-explosives, fire, and small arms.
|
||||
|
||||
actor-engineer =
|
||||
.name = Engineer
|
||||
.description = Infiltrates and captures enemy structures.
|
||||
Strong vs. Buildings.
|
||||
Weak vs. Everything.
|
||||
Repairs damaged cliffs.
|
||||
.encyclopedia = Can be used to capture enemy buildings.
|
||||
.name = Engineer
|
||||
.description =
|
||||
Infiltrates and captures enemy
|
||||
structures.
|
||||
Strong vs Buildings
|
||||
Weak vs Everything
|
||||
Repairs damaged cliffs
|
||||
.encyclopedia =
|
||||
Can be used to capture enemy buildings.
|
||||
|
||||
Engineers are resistant to anti-tank weaponry but are very vulnerable to high-explosives, fire and firearms.
|
||||
Engineers are resistant to anti-tank weaponry but are very vulnerable to high-explosives, fire, and small arms.
|
||||
|
||||
actor-trooper =
|
||||
.name = Trooper
|
||||
.description = Anti-tank infantry.
|
||||
Strong vs. Tanks.
|
||||
Weak vs. Infantry and Artillery.
|
||||
.encyclopedia = Armed with wire-guided, armor-piercing missile warheads, Troopers are very effective against vehicles and buildings but struggle against infantry.
|
||||
.name = Trooper
|
||||
.description =
|
||||
Anti-tank infantry.
|
||||
Strong vs Tanks
|
||||
Weak vs Infantry and Artillery
|
||||
.encyclopedia =
|
||||
Armed with wire-guided, armor-piercing missile warheads, Troopers are very effective against vehicles and buildings but struggle against infantry.
|
||||
|
||||
Troopers are resistant to anti-tank weaponry but very vulnerable to high-explosives, fire and bullet weapons.
|
||||
|
||||
actor-thumper =
|
||||
.name = Thumper Infantry
|
||||
.description = Attracts nearby worms when deployed.
|
||||
Unarmed.
|
||||
.encyclopedia = Deploys a loud hammering device that draws Sandworms to the area.
|
||||
.name = Thumper Infantry
|
||||
.description =
|
||||
Attracts nearby worms when deployed.
|
||||
Unarmed
|
||||
.encyclopedia =
|
||||
Deploys a loud hammering device that draws Sandworms to the area.
|
||||
|
||||
actor-fremen =
|
||||
.name = Fremen
|
||||
.description = Elite infantry unit with assault rifles and rockets.
|
||||
Strong vs. Infantry and Vehicles.
|
||||
Weak vs. Artillery.
|
||||
Special Ability: Invisibility.
|
||||
.encyclopedia = The native desert warriors of Dune, armed with 10mm Assault Rifles and Rockets. Their firepower is equally effective against infantry and vehicles.
|
||||
.name = Fremen
|
||||
.description =
|
||||
Elite infantry unit with assault rifles and rockets.
|
||||
Strong vs Infantry and Vehicles
|
||||
Weak vs Artillery
|
||||
Special Ability: Invisibility
|
||||
.encyclopedia =
|
||||
The native desert warriors of Dune, armed with 10mm Assault Rifles and Rockets. Their firepower is equally effective against infantry and vehicles.
|
||||
|
||||
Fremen units are very vulnerable to high-explosive and bullet weapons.
|
||||
|
||||
actor-grenadier =
|
||||
.name = Grenadier
|
||||
.description = Infantry with grenades.
|
||||
Strong vs. Buildings and Infantry.
|
||||
Weak vs. Vehicles.
|
||||
.encyclopedia = An infantry artillery unit strong against buildings. They have a chance of exploding when killed, so should not be grouped together.
|
||||
.name = Grenadier
|
||||
.description =
|
||||
Infantry with grenades.
|
||||
Strong vs Buildings and Infantry
|
||||
Weak vs Vehicles
|
||||
.encyclopedia =
|
||||
An infantry artillery unit strong against buildings. They have a chance of exploding when killed, so should not be grouped together.
|
||||
|
||||
actor-sardaukar =
|
||||
.name = Sardaukar
|
||||
.description = Elite Corrino assault infantry.
|
||||
Strong vs. Infantry and Vehicles.
|
||||
Weak vs. Artillery.
|
||||
.encyclopedia = Powerful heavy troopers equipped with a machine gun that is effective against infantry and a rocket launcher for targeting vehicles.
|
||||
.name = Sardaukar
|
||||
.description =
|
||||
Elite Corrino assault infantry.
|
||||
Strong vs Infantry and Vehicles
|
||||
Weak vs Artillery
|
||||
.encyclopedia =
|
||||
Powerful heavy troopers equipped with a machine gun that is effective against infantry and a rocket launcher for targeting vehicles.
|
||||
|
||||
actor-mpsardaukar-description = Elite Harkonnen assault infantry.
|
||||
Strong vs. Infantry and Vehicles.
|
||||
Weak vs. Artillery.
|
||||
actor-mpsardaukar-description =
|
||||
Elite Harkonnen assault infantry.
|
||||
Strong vs Infantry and Vehicles
|
||||
Weak vs Artillery
|
||||
|
||||
actor-saboteur =
|
||||
.name = Saboteur
|
||||
.description = Sneaky infantry with explosives.
|
||||
.name = Saboteur
|
||||
.description =
|
||||
Sneaky infantry with explosives.
|
||||
Turns invisible for a limited time.
|
||||
Strong vs. Buildings.
|
||||
Weak vs. Everything.
|
||||
Special Ability: Destroys buildings.
|
||||
.encyclopedia = A specialized military unit of House Ordos, capable of demolishing enemy buildings upon entry, but dying in the resulting explosion. It can activate stealth mode to become invisible.
|
||||
Strong vs Buildings
|
||||
Weak vs Everything
|
||||
Special Ability: Destroys buildings
|
||||
.encyclopedia =
|
||||
A specialized military unit of House Ordos, capable of demolishing enemy buildings upon entry, but dying in the resulting explosion. It can activate stealth mode to become invisible.
|
||||
|
||||
The Saboteur is resistant to anti-tank weaponry, but very vulnerable to high-explosives, fire, and bullet weapons.
|
||||
|
||||
actor-nsfremen-description = Elite infantry unit with assault rifles and rockets.
|
||||
Strong vs. Infantry and Vehicles.
|
||||
Weak vs. Artillery.
|
||||
actor-nsfremen-description =
|
||||
Elite infantry unit with assault rifles and rockets.
|
||||
Strong vs Infantry and Vehicles
|
||||
Weak vs Artillery
|
||||
|
||||
## misc.yaml
|
||||
actor-crate-name = Crate
|
||||
@@ -242,41 +265,54 @@ actor-camera-name = (reveals area to owner)
|
||||
actor-wormspawner-name = (worm spawning location)
|
||||
|
||||
actor-upgrade-conyard =
|
||||
.name = Construction Yard Upgrade
|
||||
.description = Unlocks additional construction options:
|
||||
(Large Concrete Slab and Rocket Turret)
|
||||
.name = Construction Yard Upgrade
|
||||
.description =
|
||||
Unlocks additional construction options:
|
||||
- Large Concrete Slab
|
||||
- Rocket Turret
|
||||
|
||||
actor-upgrade-barracks =
|
||||
.name = Barracks Upgrade
|
||||
.description = Unlocks additional infantry:
|
||||
(Trooper, Engineer and Thumper Infantry)
|
||||
.name = Barracks Upgrade
|
||||
.description =
|
||||
Unlocks additional infantry:
|
||||
- Trooper
|
||||
- Engineer
|
||||
- Thumper Infantry
|
||||
|
||||
Required to unlock faction-specific infantry:
|
||||
(Atreides: Grenadier, Harkonnen: Sardaukar)
|
||||
- Atreides: Grenadier
|
||||
- Harkonnen: Sardaukar
|
||||
|
||||
actor-upgrade-light =
|
||||
.name = Light Factory Upgrade
|
||||
.description = Unlocks additional light units:
|
||||
(Missile Quad)
|
||||
.name = Light Factory Upgrade
|
||||
.description =
|
||||
Unlocks additional light units:
|
||||
- Missile Quad
|
||||
|
||||
Required to unlock a faction-specific light unit:
|
||||
(Ordos: Stealth Raider Trike)
|
||||
- Ordos: Stealth Raider Trike
|
||||
|
||||
actor-upgrade-heavy =
|
||||
.name = Heavy Factory Upgrade
|
||||
.description = Unlocks additional construction options:
|
||||
(Repair Pad and IX Research Center)
|
||||
.name = Heavy Factory Upgrade
|
||||
.description =
|
||||
Unlocks additional construction options:
|
||||
- Repair Pad
|
||||
- IX Research Center
|
||||
|
||||
Unlocks additional heavy units:
|
||||
(Siege Tank, Missile Tank and MCV)
|
||||
- Siege Tank
|
||||
- Missile Tank
|
||||
- MCV
|
||||
|
||||
actor-upgrade-hightech =
|
||||
.name = High Tech Factory Upgrade
|
||||
.description = Unlocks the Atreides Air Strike superweapon.
|
||||
.name = High Tech Factory Upgrade
|
||||
.description =
|
||||
Unlocks the Atreides Air Strike superweapon.
|
||||
|
||||
actor-deathhand =
|
||||
.name = Death Hand
|
||||
.encyclopedia = Armed with atomic cluster munitions, it detonates above its target, inflicting great damage over a wide area.
|
||||
.name = Death Hand
|
||||
.encyclopedia =
|
||||
Armed with atomic cluster munitions, it detonates above its target, inflicting great damage over a wide area.
|
||||
|
||||
## structures.yaml
|
||||
notification-construction-complete = Construction complete.
|
||||
@@ -294,287 +330,338 @@ notification-fremen-ready = Fremen ready.
|
||||
notification-saboteur-ready = Saboteur ready.
|
||||
|
||||
meta-concrete =
|
||||
.generic-name = Structure
|
||||
.description = Provides a strong foundation that protects
|
||||
against terrain damage.
|
||||
.generic-name = Structure
|
||||
.description =
|
||||
Provides a strong foundation that
|
||||
protects against terrain damage.
|
||||
|
||||
actor-concrete-a =
|
||||
.name = Concrete Slab
|
||||
.encyclopedia = Buildings not constructed on a Concrete Slab will sustain ongoing damage from the harsh desert environment of Dune. While repairs are possible, placing structures on concrete prevents continuous weathering.
|
||||
.name = Concrete Slab
|
||||
.encyclopedia =
|
||||
Buildings not constructed on a Concrete Slab will sustain ongoing damage from the harsh desert environment of Dune. While repairs are possible, placing structures on concrete prevents continuous weathering.
|
||||
|
||||
Concrete is vulnerable to most weapons and cannot be repaired once damaged.
|
||||
|
||||
actor-concrete-b-name = Large Concrete Slab
|
||||
|
||||
actor-construction-yard =
|
||||
.name = Construction Yard
|
||||
.description = Produces structures.
|
||||
.encyclopedia = Serving as the foundation of any base built on Arrakis, the Construction Yard produces a small amount of power and enables the construction of new structures. Protect this structure! It is critical to the success of your base.
|
||||
.name = Construction Yard
|
||||
.description = Produces structures.
|
||||
.encyclopedia =
|
||||
Serving as the foundation of any base built on Arrakis, the Construction Yard produces a small amount of power and enables the construction of new structures. Protect this structure! It is critical to the success of your base.
|
||||
|
||||
Construction yards are fairly strong, but vulnerable to all weapons to varying degrees.
|
||||
|
||||
actor-wind-trap =
|
||||
.name = Wind Trap
|
||||
.description = Supplies power to other structures.
|
||||
.encyclopedia = Produces power and water for your base. Large, above-ground ducts funnel wind currents underground into massive turbines, which drive power generators and humidity extractors.
|
||||
.name = Wind Trap
|
||||
.description =
|
||||
Supplies power to other
|
||||
structures.
|
||||
.encyclopedia =
|
||||
Produces power and water for your base. Large, above-ground ducts funnel wind currents underground into massive turbines, which drive power generators and humidity extractors.
|
||||
|
||||
Wind Traps are vulnerable to most weapons.
|
||||
|
||||
actor-barracks =
|
||||
.name = Barracks
|
||||
.description = Trains infantry.
|
||||
.encyclopedia = Required for producing and training light infantry units, it can be upgraded to train advanced infantry in later missions.
|
||||
.name = Barracks
|
||||
.description = Trains infantry.
|
||||
.encyclopedia =
|
||||
Required for producing and training light infantry units, it can be upgraded to train advanced infantry in later missions.
|
||||
|
||||
Barracks are vulnerable to most weapons.
|
||||
|
||||
actor-refinery =
|
||||
.name = Spice Refinery
|
||||
.description = Harvesters unload Spice here for processing.
|
||||
.encyclopedia = The basis of all Spice production on Dune. Harvesters transport mined Spice to the Refinery where it is converted into credits. Refined Spice is automatically distributed to Silos and Refineries for storage. Each refinery can store Spice. A Spice Harvester is delivered by Carryall once a Refinery is built.
|
||||
.name = Spice Refinery
|
||||
.description =
|
||||
Harvesters unload Spice here
|
||||
for processing.
|
||||
.encyclopedia =
|
||||
The basis of all Spice production on Dune. Harvesters transport mined Spice to the Refinery where it is converted into credits. Refined Spice is automatically distributed to Silos and Refineries for storage. Each refinery can store Spice. A Spice Harvester is delivered by Carryall once a Refinery is built.
|
||||
|
||||
Refineries are vulnerable to most weapons.
|
||||
|
||||
actor-silo =
|
||||
.name = Silo
|
||||
.description = Stores excess harvested Spice.
|
||||
.encyclopedia = Store mined Spice. Any surplus from Refineries is evenly distributed among all available Silos. If storage capacity is exceeded, excess Spice is lost. Destroyed or captured Silos redistribute their contents, provided there is sufficient space.
|
||||
.name = Silo
|
||||
.description = Stores excess harvested Spice.
|
||||
.encyclopedia =
|
||||
Store mined Spice. Any surplus from Refineries is evenly distributed among all available Silos. If storage capacity is exceeded, excess Spice is lost. Destroyed or captured Silos redistribute their contents, provided there is sufficient space.
|
||||
|
||||
The Spice Silo is vulnerable to most weapons.
|
||||
|
||||
actor-light-factory =
|
||||
.name = Light Factory
|
||||
.description = Produces light vehicles.
|
||||
.encyclopedia = Required to produce small, lightly armored combat vehicles. It can be upgraded in later missions to manufacture more advanced light vehicles.
|
||||
.name = Light Factory
|
||||
.description = Produces light vehicles.
|
||||
.encyclopedia =
|
||||
Required to produce small, lightly armored combat vehicles. It can be upgraded in later missions to manufacture more advanced light vehicles.
|
||||
|
||||
A Light Factory is vulnerable to most weapons.
|
||||
|
||||
actor-heavy-factory =
|
||||
.name = Heavy Factory
|
||||
.description = Produces heavy vehicles.
|
||||
.encyclopedia = Enables the construction of heavy vehicles such as Harvesters and Combat Tanks. With upgrades, it unlocks advanced vehicles, though some may require additional buildings.
|
||||
.name = Heavy Factory
|
||||
.description = Produces heavy vehicles.
|
||||
.encyclopedia =
|
||||
Enables the construction of heavy vehicles such as Harvesters and Combat Tanks. With upgrades, it unlocks advanced vehicles, though some may require additional buildings.
|
||||
|
||||
The Heavy Factory is vulnerable to most weapons.
|
||||
|
||||
actor-outpost =
|
||||
.name = Outpost
|
||||
.description = Provides a radar map of the battlefield.
|
||||
Requires power to operate.
|
||||
.encyclopedia = Once enough power is available, the Radar Outpost activates, providing a radar map.
|
||||
.name = Outpost
|
||||
.description =
|
||||
Provides a radar map of the battlefield.
|
||||
Requires power to operate.
|
||||
.encyclopedia =
|
||||
Once enough power is available, the Radar Outpost activates, providing a radar map.
|
||||
|
||||
The Radar Outpost is vulnerable to most weapons.
|
||||
|
||||
actor-starport =
|
||||
.name = Starport
|
||||
.description = Dropzone for quick reinforcements, at a price.
|
||||
.encyclopedia = Unlocks intergalactic trade with the CHOAM Merchants' Guild, where vehicles and airborne units can be purchased at varying rates. This facility is essential for acquiring units from the Guild.
|
||||
.name = Starport
|
||||
.description = Dropzone for quick reinforcements, at a price.
|
||||
.encyclopedia =
|
||||
Unlocks intergalactic trade with the CHOAM Merchants' Guild, where vehicles and airborne units can be purchased at varying rates. This facility is essential for acquiring units from the Guild.
|
||||
|
||||
Even with heavy armor, the Starport is vulnerable to most weapons.
|
||||
|
||||
actor-wall =
|
||||
.name = Concrete Wall
|
||||
.generic-name = Structure
|
||||
.description = Stops units and blocks enemy fire.
|
||||
.encyclopedia = The most effective defensive barriers on Dune, blocking tank fire and impeding unit movement.
|
||||
.name = Concrete Wall
|
||||
.generic-name = Structure
|
||||
.description = Stops units and blocks enemy fire.
|
||||
.encyclopedia =
|
||||
The most effective defensive barriers on Dune, blocking tank fire and impeding unit movement.
|
||||
|
||||
Walls can only be damaged by explosive weapons, missiles and shells. Similar to concrete slabs, they cannot be repaired once damaged.
|
||||
Walls can only be damaged by explosive weapons, missiles and shells. Similar to Concrete Slabs, they cannot be repaired once damaged.
|
||||
|
||||
actor-medium-gun-turret =
|
||||
.name = Gun Turret
|
||||
.description = Defensive structure.
|
||||
Strong vs. Tanks.
|
||||
Weak vs. Infantry and Aircraft.
|
||||
.encyclopedia = A medium-range weapon that is effective against all types of vehicle, particularly heavily armored ones. It automatically fires upon any enemy unit within its range and requires power to operate.
|
||||
.name = Gun Turret
|
||||
.description =
|
||||
Defensive structure.
|
||||
Strong vs Tanks
|
||||
Weak vs Infantry and Aircraft
|
||||
.encyclopedia =
|
||||
A medium-range weapon that is effective against all types of vehicle, particularly heavily armored ones. It automatically fires upon any enemy unit within its range and requires power to operate.
|
||||
|
||||
The Gun Turret is resistant to firearms and explosive weapons, but vulnerable to missiles and high-caliber guns.
|
||||
The Gun Turret is resistant to small arms and explosive weapons, but vulnerable to missiles and high-caliber guns.
|
||||
|
||||
actor-large-gun-turret =
|
||||
.name = Rocket Turret
|
||||
.description = Defensive structure.
|
||||
Strong vs. Infantry and Aircraft.
|
||||
Weak vs. Tanks.
|
||||
|
||||
Requires power to operate.
|
||||
.encyclopedia = An enhanced defensive structure with a longer range and faster rate of fire than the Gun Turret. Its advanced targeting system requires power to operate.
|
||||
.name = Rocket Turret
|
||||
.description =
|
||||
Defensive structure.
|
||||
Requires power to operate.
|
||||
Strong vs Infantry and Aircraft
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
An enhanced defensive structure with a longer range and faster rate of fire than the Gun Turret. Its advanced targeting system requires power to operate.
|
||||
|
||||
The Rocket Turret is resistant to firearms and explosive weapons, but vulnerable to missiles and high-caliber guns.
|
||||
|
||||
actor-repair-pad =
|
||||
.name = Repair Pad
|
||||
.description = Repairs vehicles.
|
||||
Allows MCVs to be built.
|
||||
.encyclopedia = Repairs units for a fraction of their production cost.
|
||||
.name = Repair Pad
|
||||
.description =
|
||||
Repairs vehicles.
|
||||
Allows MCVs to be built.
|
||||
.encyclopedia =
|
||||
Repairs units for a fraction of their production cost.
|
||||
|
||||
The Repair Pad is vulnerable to most weapons.
|
||||
|
||||
actor-high-tech-factory =
|
||||
.name = High Tech Factory
|
||||
.description = Unlocks advanced technology.
|
||||
.airstrikepower-name = Air Strike
|
||||
.airstrikepower-description = Ornithopters bomb the target.
|
||||
.encyclopedia = Produces airborne units, and is required to build Carryalls. House Atreides can upgrade this facility to build Ornithopters for air strikes in later missions.
|
||||
.name = High Tech Factory
|
||||
.description = Unlocks advanced technology.
|
||||
.airstrikepower-name = Air Strike
|
||||
.airstrikepower-description = Ornithopters bomb the target.
|
||||
.encyclopedia =
|
||||
Produces airborne units, and is required to build Carryalls. House Atreides can upgrade this facility to build Ornithopters for air strikes in later missions.
|
||||
|
||||
The High Tech Factory is vulnerable to most weapons.
|
||||
|
||||
actor-research-centre =
|
||||
.name = IX Research Center
|
||||
.description = Unlocks advanced tanks.
|
||||
.encyclopedia = Provides technology upgrades for both structures and vehicles. This facility is required to develop advanced special weapons and prototypes.
|
||||
.name = IX Research Center
|
||||
.description = Unlocks advanced tanks.
|
||||
.encyclopedia =
|
||||
Provides technology upgrades for both structures and vehicles. This facility is required to develop advanced special weapons and prototypes.
|
||||
|
||||
The IX Research Center is vulnerable to most weapons.
|
||||
|
||||
actor-palace =
|
||||
.name = Palace
|
||||
.description = Unlocks elite infantry and weapons.
|
||||
.encyclopedia = Serves as the command center once built, offering additional options and special weapons.
|
||||
.name = Palace
|
||||
.description = Unlocks elite infantry and weapons.
|
||||
.encyclopedia =
|
||||
Serves as the command center once built, offering additional options and special weapons.
|
||||
|
||||
Even with heavy armor, the Palace is vulnerable to most weapons.
|
||||
.nukepower-name = Death Hand
|
||||
.nukepower-description = Launches an atomic missile at a target location.
|
||||
.produceactorpower-fremen-name = Recruit Fremen
|
||||
.produceactorpower-fremen-description = Elite infantry unit with assault rifles and rockets.
|
||||
Strong vs. Infantry and Vehicles.
|
||||
Weak vs. Artillery.
|
||||
Special Ability: Invisibility.
|
||||
.produceactorpower-saboteur-name = Recruit Saboteur
|
||||
.produceactorpower-saboteur-description = Sneaky infantry with explosives.
|
||||
.nukepower-name = Death Hand
|
||||
.nukepower-description = Launches an atomic missile at a target location.
|
||||
.produceactorpower-fremen-name = Recruit Fremen
|
||||
.produceactorpower-fremen-description = Elite infantry unit with assault rifles and rockets.
|
||||
Strong vs Infantry and Vehicles
|
||||
Weak vs Artillery
|
||||
Special Ability: Invisibility
|
||||
.produceactorpower-saboteur-name = Recruit Saboteur
|
||||
.produceactorpower-saboteur-description = Sneaky infantry with explosives.
|
||||
Can be deployed to become invisible for a limited time.
|
||||
Strong vs Buildings.
|
||||
Weak vs Everything.
|
||||
Special Ability: Destroys buildings.
|
||||
Strong vs Buildings
|
||||
Weak vs Everything
|
||||
Special Ability: Destroys buildings
|
||||
|
||||
## vehicles.yaml
|
||||
actor-mcv =
|
||||
.name = Mobile Construction Vehicle
|
||||
.description = Deploys into a Construction Yard.
|
||||
Unarmed.
|
||||
.encyclopedia = Must be driven to an area where it can be deployed. After finding a suitable rock surface, the MCV can be transformed into a Construction Yard.
|
||||
.name = Mobile Construction Vehicle
|
||||
.description =
|
||||
Deploys into a Construction Yard.
|
||||
Unarmed
|
||||
.encyclopedia =
|
||||
Must be driven to an area where it can be deployed. After finding a suitable rock surface, the MCV can be transformed into a Construction Yard.
|
||||
|
||||
MCVs are resistant to bullets and light-explosives. They are vulnerable to missiles and high-caliber guns.
|
||||
|
||||
actor-harvester =
|
||||
.name = Spice Harvester
|
||||
.description = Collects Spice for processing.
|
||||
Unarmed.
|
||||
.encyclopedia = Resistant to bullets, and to some degree, high explosives. They are vulnerable to missiles and high-caliber guns.
|
||||
.name = Spice Harvester
|
||||
.description =
|
||||
Collects Spice for processing.
|
||||
Unarmed
|
||||
.encyclopedia =
|
||||
Resistant to bullets, and to some degree, high explosives. They are vulnerable to missiles and high-caliber guns.
|
||||
|
||||
A Harvester is included with a Refinery.
|
||||
|
||||
actor-trike =
|
||||
.name = Trike
|
||||
.description = Fast scout.
|
||||
Strong vs. Infantry.
|
||||
Weak vs. Tanks.
|
||||
.encyclopedia = Lightly armored, three-wheeled vehicles armed with heavy machine guns, effective against infantry and lightly armored vehicles.
|
||||
.name = Trike
|
||||
.description =
|
||||
Fast scout.
|
||||
Strong vs Infantry
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
Lightly armored, three-wheeled vehicles armed with heavy machine guns, effective against infantry and lightly armored vehicles.
|
||||
|
||||
Trikes are vulnerable to most weapons, high-caliber guns are slightly less effective against them.
|
||||
|
||||
actor-quad =
|
||||
.name = Missile Quad
|
||||
.description = Missile Scout.
|
||||
Strong vs. Vehicles.
|
||||
Weak vs. Infantry.
|
||||
.encyclopedia = Superior to the Trike in both armor and firepower, the Quad is a four-wheeled vehicle firing armor-piercing rockets. It is effective against most vehicles.
|
||||
.name = Missile Quad
|
||||
.description =
|
||||
Missile Scout.
|
||||
Strong vs Vehicles
|
||||
Weak vs Infantry
|
||||
.encyclopedia =
|
||||
Superior to the Trike in both armor and firepower, the Quad is a four-wheeled vehicle firing armor-piercing rockets. It is effective against most vehicles.
|
||||
|
||||
Quads are resistant to bullets and, to a lesser degree, explosives. They are vulnerable to missiles and high-caliber guns.
|
||||
|
||||
actor-siege-tank =
|
||||
.name = Siege Tank
|
||||
.description = Siege Artillery.
|
||||
Strong vs. Infantry and Buildings.
|
||||
Weak vs. Tanks.
|
||||
.encyclopedia = Incredibly effective against infantry and lightly armored vehicles, but struggles against heavily armored targets. It has a long firing range.
|
||||
.name = Siege Tank
|
||||
.description =
|
||||
Siege Artillery.
|
||||
Strong vs Infantry and Buildings
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
Incredibly effective against infantry and lightly armored vehicles, but struggles against heavily armored targets. It has a long firing range.
|
||||
|
||||
Siege Tanks are resistant to bullets, and to some degree, explosives. They are vulnerable to missiles and high-caliber guns.
|
||||
|
||||
actor-missile-tank =
|
||||
.name = Missile Tank
|
||||
.description = Rocket Artillery.
|
||||
Strong vs. Vehicles, Buildings and Aircraft.
|
||||
Weak vs. Infantry.
|
||||
.encyclopedia = Shoots down aircraft and is effective against most targets, except infantry.
|
||||
.name = Missile Tank
|
||||
.description =
|
||||
Rocket Artillery.
|
||||
Strong vs Vehicles, Buildings and Aircraft
|
||||
Weak vs Infantry
|
||||
.encyclopedia =
|
||||
Shoots down aircraft and is effective against most targets, except infantry.
|
||||
|
||||
Missile Tanks are vulnerable to most weapons, high-caliber guns are slightly less effective.
|
||||
|
||||
actor-sonic-tank =
|
||||
.name = Sonic Tank
|
||||
.description = Fires sonic shocks.
|
||||
Strong vs. Infantry and Vehicles.
|
||||
Weak vs. Artillery.
|
||||
.encyclopedia = Most effective against infantry and lightly armored vehicles, but weaker against armored targets.
|
||||
.name = Sonic Tank
|
||||
.description =
|
||||
Fires sonic shocks.
|
||||
Strong vs Infantry and Vehicles
|
||||
Weak vs Artillery
|
||||
.encyclopedia =
|
||||
Most effective against infantry and lightly armored vehicles, but weaker against armored targets.
|
||||
|
||||
Its sonic waves damage all units in their path.
|
||||
|
||||
Resistant to bullets and small-explosives, but vulnerable to missiles and high-caliber guns.
|
||||
|
||||
actor-devastator =
|
||||
.name = Devastator
|
||||
.description = Super Heavy Tank.
|
||||
Strong vs. Tanks.
|
||||
Weak vs. Artillery.
|
||||
.encyclopedia = As the most powerful tank on Dune, the Devastator is slow but highly effective against most units. It fires dual plasma charges and can self-destruct on command, damaging nearby units and structures.
|
||||
.name = Devastator
|
||||
.description =
|
||||
Super Heavy Tank.
|
||||
Strong vs Tanks
|
||||
Weak vs Artillery
|
||||
.encyclopedia =
|
||||
As the most powerful tank on Dune, the Devastator is slow but highly effective against most units. It fires dual plasma charges and can self-destruct on command, damaging nearby units and structures.
|
||||
|
||||
Resistant to bullets and high explosives, but vulnerable to missiles and high-caliber guns.
|
||||
|
||||
actor-raider =
|
||||
.name = Raider Trike
|
||||
.description = Improved Scout.
|
||||
Strong vs. Infantry and Light Vehicles.
|
||||
Weak vs. Tanks.
|
||||
.encyclopedia = Raider Trikes, upgraded by House Ordos, have enhanced firepower, speed, and armor. Equipped with dual 20mm cannons, they are strong against infantry and lightly armored vehicles.
|
||||
.name = Raider Trike
|
||||
.description =
|
||||
Improved Scout.
|
||||
Strong vs Infantry and Light Vehicles
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
Raider Trikes, upgraded by House Ordos, have enhanced firepower, speed, and armor. Equipped with dual 20mm cannons, they are strong against infantry and lightly armored vehicles.
|
||||
|
||||
Raiders are vulnerable to most weapons, though high-caliber guns are slightly less effective against them.
|
||||
|
||||
actor-stealth-raider =
|
||||
.name = Stealth Raider Trike
|
||||
.description = Invisible Raider Trike.
|
||||
Strong vs. Infantry and Light Vehicles.
|
||||
Weak vs. Tanks.
|
||||
.encyclopedia = A cloaked version of the Raider, good for stealth attacks. It uncloaks when it fires its machine guns.
|
||||
.name = Stealth Raider Trike
|
||||
.description =
|
||||
Invisible Raider Trike.
|
||||
Strong vs Infantry and Light Vehicles
|
||||
Weak vs Tanks
|
||||
.encyclopedia =
|
||||
A cloaked version of the Raider, good for stealth attacks. It uncloaks when it fires its machine guns.
|
||||
|
||||
actor-deviator =
|
||||
.name = Deviator
|
||||
.description = Fires a warhead that changes
|
||||
the allegiance of enemy vehicles.
|
||||
.encyclopedia = Fires missiles that release a silicon cloud, temporarily altering the allegiance of targeted vehicles. Personnel are only slightly affected by the cloud.
|
||||
.name = Deviator
|
||||
.description =
|
||||
Fires a warhead that changes the
|
||||
allegiance of enemy vehicles.
|
||||
.encyclopedia =
|
||||
Fires missiles that release a silicon cloud, temporarily altering the allegiance of targeted vehicles. Personnel are only slightly affected by the cloud.
|
||||
|
||||
The Deviator is vulnerable to most weapons, high-caliber guns are slightly less effective.
|
||||
|
||||
meta-combat-tank-description = Main Battle Tank.
|
||||
Strong vs. Tanks.
|
||||
Weak vs. Infantry.
|
||||
meta-combat-tank-description =
|
||||
Main Battle Tank.
|
||||
Strong vs Tanks
|
||||
Weak vs Infantry
|
||||
|
||||
actor-combat-tank-a =
|
||||
.name = Atreides Combat Tank
|
||||
.encyclopedia = Effective against most vehicles but less suited against lightly armored targets.
|
||||
.name = Atreides Combat Tank
|
||||
.encyclopedia =
|
||||
Effective against most vehicles but less suited against lightly armored targets.
|
||||
|
||||
Resistant to bullets and heavy explosives, but vulnerable to missiles and high-caliber guns.
|
||||
|
||||
actor-combat-tank-h =
|
||||
.name = Harkonnen Combat Tank
|
||||
.encyclopedia = Effective against most vehicles but less suited against lightly armored targets.
|
||||
.name = Harkonnen Combat Tank
|
||||
.encyclopedia =
|
||||
Effective against most vehicles but less suited against lightly armored targets.
|
||||
|
||||
Stronger than its counterparts, but also slower.
|
||||
|
||||
actor-combat-tank-o =
|
||||
.name = Ordos Combat Tank
|
||||
.encyclopedia = Effective against most vehicles but less suited against lightly armored targets.
|
||||
.name = Ordos Combat Tank
|
||||
.encyclopedia =
|
||||
Effective against most vehicles but less suited against lightly armored targets.
|
||||
|
||||
The fastest variant of Combat Tank, but also the weakest.
|
||||
|
||||
meta-destroyabletile =
|
||||
.generic-name = Passage (destroyable)
|
||||
.name = Passage (destroyable)
|
||||
.generic-name = Passage (destroyable)
|
||||
.name = Passage (destroyable)
|
||||
|
||||
meta-destroyedtile =
|
||||
.generic-name = Passage (repairable)
|
||||
.name = Passage (repairable)
|
||||
.generic-name = Passage (repairable)
|
||||
.name = Passage (repairable)
|
||||
|
||||
## ai.yaml
|
||||
bot-omnius =
|
||||
.name = Omnius
|
||||
.name = Omnius
|
||||
|
||||
bot-vidius =
|
||||
.name = Vidious
|
||||
.name = Vidious
|
||||
|
||||
bot-gladius =
|
||||
.name = Gladius
|
||||
.name = Gladius
|
||||
|
||||
@@ -54,7 +54,7 @@ AtreidesEntryPath = { AtreidesWaypoint.Location, AtreidesRally.Location }
|
||||
Messages =
|
||||
{
|
||||
UserInterface.GetFluentMessage("build-concrete"),
|
||||
UserInterface.GetFluentMessage("build-windtrap"),
|
||||
UserInterface.GetFluentMessage("build-wind-trap"),
|
||||
UserInterface.GetFluentMessage("build-refinery"),
|
||||
UserInterface.GetFluentMessage("build-silo")
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ AtreidesEntryPath = { AtreidesWaypoint.Location, AtreidesRally.Location }
|
||||
Messages =
|
||||
{
|
||||
UserInterface.GetFluentMessage("build-concrete"),
|
||||
UserInterface.GetFluentMessage("build-windtrap"),
|
||||
UserInterface.GetFluentMessage("build-wind-trap"),
|
||||
UserInterface.GetFluentMessage("build-refinery"),
|
||||
UserInterface.GetFluentMessage("build-silo")
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ HarkonnenEntryPath = { HarkonnenWaypoint.Location, HarkonnenRally.Location }
|
||||
Messages =
|
||||
{
|
||||
UserInterface.GetFluentMessage("build-concrete"),
|
||||
UserInterface.GetFluentMessage("build-windtrap"),
|
||||
UserInterface.GetFluentMessage("build-wind-trap"),
|
||||
UserInterface.GetFluentMessage("build-refinery"),
|
||||
UserInterface.GetFluentMessage("build-silo")
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ HarkonnenEntryPath = { HarkonnenWaypoint.Location, HarkonnenRally.Location }
|
||||
Messages =
|
||||
{
|
||||
UserInterface.GetFluentMessage("build-concrete"),
|
||||
UserInterface.GetFluentMessage("build-windtrap"),
|
||||
UserInterface.GetFluentMessage("build-wind-trap"),
|
||||
UserInterface.GetFluentMessage("build-refinery"),
|
||||
UserInterface.GetFluentMessage("build-silo")
|
||||
}
|
||||
|
||||
@@ -79,3 +79,4 @@ outpost:
|
||||
Tooltip@Modified:
|
||||
Name: actor-outpost-modified-name
|
||||
RequiresCondition: modified
|
||||
ShowOwnerRow: false
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user