Compare commits

...

111 Commits

Author SHA1 Message Date
Paul Chote
9ac7765a93 Add line limit to global chat. 2017-10-14 12:30:08 +01:00
Paul Chote
fe8341a3aa Add missing StringComparison to IngameChatLogic. 2017-10-14 12:30:08 +01:00
Paul Chote
7b85e81f37 Limit chat messages to 2500 characters. 2017-10-14 13:16:18 +02:00
Paul Chote
4e28cdd040 Fix submarine autotarget behaviour. 2017-10-08 20:15:38 +02:00
Paul Chote
ccf2ac83bb Fix Defend stance auto-targeting naval structures. 2017-10-08 20:15:27 +02:00
Paul Chote
729cc1b83b Remove directory creation side effect from Platform.SupportDir. 2017-10-08 19:54:47 +02:00
Paul Chote
c4239fd16e Populate MapLocations from LoadMaps.
This ensures that the map locations won't be created by the utility
but means that LoadMaps must be called before attempting to use MapLocations.
2017-09-30 18:11:18 +02:00
Paul Chote
13e74f7836 Display (attack|assault)move-blocked cursor outside map bounds.
This makes them consistent with the regular move order.
2017-09-30 14:40:30 +02:00
Paul Chote
5179a95572 Fix crash when selection contains destroyed actors. 2017-09-30 14:40:18 +02:00
Paul Chote
e6d4f1fdc3 Clamp out of bounds attack-move orders to the edge of the map. 2017-09-30 14:40:03 +02:00
Paul Chote
3124a0024a Fix the attack move order generator activating with an empty selection. 2017-09-30 14:39:35 +02:00
Paul Chote
0f7016cb6e Fix global chat history buffer timestamps. 2017-09-28 15:14:12 +02:00
Paul Chote
dc02686a17 Don't draw mine overlay for single-cell fields. 2017-09-27 21:08:28 +02:00
Paul Chote
5a86c8fff6 Fix mine field overlay extending to map corner for the first tick. 2017-09-27 21:08:17 +02:00
Paul Chote
c67aef47aa Add voice feedback when issuing a mine deploy order. 2017-09-27 21:08:06 +02:00
Paul Chote
87e5dfad9f Add mine deployment to the command bar. 2017-09-27 21:07:51 +02:00
reaperrr
2dda81b2db Fix DepthCharge dealing no damage to surfaced subs 2017-09-23 20:07:19 +01:00
reaperrr
4243d42a63 Fix DepthCharge not showing an explosion when hitting emerged subs 2017-09-23 20:07:13 +01:00
reaperrr
4dec9258be Fix submerged sub vulnerability to most weapons 2017-09-23 20:07:06 +01:00
Paul Chote
b2e3ce8edf Fix cheats reenabling OneShot support powers. 2017-09-19 17:55:25 +02:00
Matthias Mailänder
dbd48831b7 Fix indentions of weapon documentation table of contents. 2017-09-17 16:51:14 +02:00
reaperrr
d11875b091 Fix ExtractWeaponDocsCommand
The original commit was accidentally cherry-picked from a slightly outdated branch revision.
2017-09-17 16:50:20 +02:00
RoosterDragon
838a3f83c6 Remove OpenAlStreamingSound.
Whilst the implementation appears to work most of the time, it sometimes stops playing audio mid-track. This could be an implementation issue, or bugs in the underlying streaming APIs of the OpenAL library. Either way, it is not currently reliable enough to be used.
2017-09-17 15:55:50 +02:00
RoosterDragon
8512cdb8bb Create OpenAlAsyncLoadSound.
This loads the sound inside a task, then begins playing it, avoiding blocking the UI thread. Unlike OpenAlStreamingSound it does not stream the sound from disk, rather, it loads the entire sound into memory and plays it in one go.
2017-09-17 15:55:36 +02:00
Smittytron
0b78f93178 Remove EjectOnDeath from Vehicles 2017-09-16 14:11:57 +01:00
Paul Chote
59fd3184ca Add assault move to D2K. 2017-09-11 19:17:26 +02:00
Paul Chote
ffa7db01a0 Add assault move to TD. 2017-09-11 19:16:56 +02:00
Paul Chote
0eb1e2307d Add assault move to RA. 2017-09-11 19:16:32 +02:00
Paul Chote
bbf60a3697 Add frontend code for issuing assault move orders. 2017-09-11 19:16:07 +02:00
Paul Chote
a8fa37d84b Add AttackMove support for AssaultMove orders and condition granting. 2017-09-11 19:15:45 +02:00
Paul Chote
0176c1a217 Calculate building placement offsets in screen space. 2017-09-10 18:27:45 +02:00
reaperrr
43abbbecb1 Fix RA TorpTube bridge explosions
There were two issues at work here:
1) The combination of default MaximumLaunchAngle and default CruiseAltitude made torpedoes fly a ballistic curve, which combined with BoundToTerrain type made them explode 'in the air' when hitting non-water tiles. This would result in AIr returned as target type, which is invalid for torpedoes.
2) The explosion warheads were missing the Ground target type, which is actually the (only) target type that the bridge tiles you hit with torpedoes have.
2017-09-10 18:11:20 +02:00
reaperrr
d063b28728 Fix water splash on bridge in RA
On bleed, both the explosion as well as the water splash warhead of weapons triggered on bridges, since they have both the Ground and Water target types.

Ground cannot be made invalid for water splashes without causing regressions elsewhere, so a new Bridge target type is introduced to explicitly make bridges invalid targets for water splash effect warheads.
2017-09-10 18:11:20 +02:00
Paul Chote
50c4f19523 Remove dummy d2k support bin overlay. 2017-09-10 15:27:29 +02:00
SoScared
a93435ccc4 Replace all Creep with Neutral actors on map Agenda 2017-09-10 15:22:23 +02:00
Taryn Hill
ffed6c0a4a Always use noget in fetch-thirdparty-deps-windows.sh 2017-09-10 14:56:08 +02:00
SoScared
52934818e9 RevealOnDeath with Aircraft 2017-09-02 20:19:18 +02:00
abcdefg30
8ddfcd705b Don't spawn aircraft husks when not airborne 2017-09-02 20:12:48 +02:00
abcdefg30
578313c3a5 Adjust IsAtGroundLevel fixing EjectOnDeath 2017-09-02 20:08:18 +02:00
reaperrr
01fdd67ef0 Polish RA Jeep and APC muzzle offsets
And Jeep turret offset.
2017-09-02 14:30:37 +02:00
Matthias Mailänder
ac9b641240 Bring back the MiGs to the desert shellmap. 2017-09-02 14:15:59 +02:00
rob-v
18604cf905 Change default Observer shroud option for Explored map 2017-09-02 14:03:03 +02:00
Paul Chote
dc2fca9df8 Clear editor search fields and yield focus on escape. 2017-09-02 13:00:02 +02:00
reaperrr
e8d95a1d57 Fixed no explosions showing on some RA civ structures
The combination of HitShape, but not Targetable makes the actors be considered invalid for effect warheads.
Lack of Targetable makes them invulnerable anyway, so removing HitShape and Health (and Explodes) is the most logical fix.
2017-09-02 09:27:49 +01:00
Matthias Mailänder
d6aab1bd7a Remove a bogus ore map in a water segment. 2017-09-02 09:13:41 +01:00
Paul Chote
a293e162b2 Suppress scroll events from the DropDownWidget fullscreen mask. 2017-08-31 12:43:53 +02:00
Matthias Mailänder
60752b115f Fix indentions of trait documentation table of contents. 2017-08-30 17:17:36 +02:00
SoScared
b4f02440d9 patch tent cost increase 2017-08-28 21:07:13 +02:00
reaperrr
bd951d8fb0 Fix FindActorsOnLine overscan 2017-08-26 23:48:01 +01:00
Paul Chote
f520b375e4 Add mod-specific appdata metadata. 2017-08-26 23:47:33 +01:00
Paul Chote
cadf6956a9 Use curl instead of wget in fetch-thirdparty-deps. 2017-08-26 13:54:42 +02:00
SoScared
db317eb226 Additional tweak of the Artillery weapon 2017-08-25 15:32:11 +02:00
Matthias Mailänder
b36ca98a83 Fix Dune 2000 harvester husk offset.
Closes #10087.
2017-08-24 11:36:58 +02:00
C. Helmig
1b5261e58a d2k: sonic weapon fix.
Works around buggy friend-foe discrimination (/Falloff) for sonic weapon. Damage values closer to original game. (Fixes #13850)
2017-08-23 13:04:43 +02:00
reaperrr
5654736086 Properly account for disabled Armaments in various places
These places didn't care if an Armament was disabled, which could lead to unexpected behavior.
2017-08-22 20:57:56 +01:00
reaperrr
3038f15f00 Consider disabled armaments invalid 2017-08-22 20:57:48 +01:00
SoScared
187dd5a071 fix ore spots on Dual Cold Front 2017-08-22 19:40:24 +02:00
reaperrr
740f04801b Allow skipping make anim for actors with WithMakeAnimation and GrantConditionOnDeploy 2017-08-17 22:04:47 +01:00
reaperrr
619d433e8b Allow skipping 'sell'/reversed make anim on Sell 2017-08-17 22:04:43 +01:00
reaperrr
4ca87c1e05 Allow skipping 'sell'/undeploy anim for actors that TransformOnCapture 2017-08-17 22:04:35 +01:00
Paul Chote
0b97003d83 Add x64process stat. 2017-08-17 19:48:21 +02:00
Paul Chote
6ba02da2eb Force '.' decimal point for windowscale stat. 2017-08-17 19:48:07 +02:00
SoScared
4496cffa4a probe new civilian structures 2017-08-17 15:26:58 +02:00
AoAGeneral
c3f1f617e0 TD Bridges.
Currently in both playtest and release the bridges for TD used to be 500hp with no armor. Meaning that minigunners and other unit types were able to kill them off fairly quickly. Giving them extra HP and an armor type means units such as minigunners, humvees, flamethrowers, and APCs can't kill bridges off quickly.
2017-08-13 20:21:50 +02:00
rob-v
dbab122594 Keep Attack M./Guard action while holding hotkey (revert Playtest change) 2017-08-13 17:46:41 +01:00
Allan Greis Eriksen
521236398d Audio device names is now shown correcly in the Audio Device combolist.
Ignored DA version of resource dll.
2017-08-13 18:38:29 +02:00
Oliver Brakmann
4b7f8c6c82 Fix Mobile not sanitizing target location of Move orders 2017-08-13 17:25:19 +01:00
Paul Chote
2e60e41764 Change beaconlib reference to match other deps. 2017-08-13 18:16:52 +02:00
Paul Chote
e8f552bf68 Update BeaconLib to 1.0.1. 2017-08-13 18:16:52 +02:00
abcdefg30
8743f13caa Fix monster tanks not attacking radar domes 2017-08-13 18:01:36 +02:00
abcdefg30
963a8e6ea9 Add the win and loss videos to Monster Tank Madness 2017-08-13 18:01:36 +02:00
Paul Chote
c8f1ce0d42 Update macOS launcher to 20170812 tag. 2017-08-13 17:53:58 +02:00
Paul Chote
13e0e927fe Switch to .NET 4.5 compatible Marshal.SizeOf overload. 2017-08-13 17:40:50 +02:00
Paul Chote
d3c130e8ae Fix d2k commandbar artwork glitch. 2017-08-13 14:10:10 +02:00
Paul Chote
8ff1762eac Revert TicksBeforePathing move from Mobile to Move.
This partially reverts commit 11c8cda0c3.
2017-08-13 13:41:13 +02:00
abc013
ef418403c5 Changed SAM launch offset 2017-08-13 12:55:03 +02:00
abcdefg30
74da4065fc Remove TargetWhenIdle and TargetWhenDamaged from AutoTarget 2017-08-10 19:18:57 +02:00
abcdefg30
b3e4fc807e Add TRUCK.Husk to Tiberian Dawn 2017-08-10 18:25:16 +02:00
SoScared
893ee6eb2c final RA balance patch for the next playtest/release 2017-08-10 18:17:48 +02:00
abcdefg30
a5df442499 Don't spawn new actors before all RemovedFromWorld callbacks have run 2017-08-10 18:03:47 +02:00
Paul Chote
a87df0f81e Focus the ViewportController on Standard/Inverted scroll modes. 2017-08-10 12:49:30 +02:00
Paul Chote
75130c47ff Extract ViewportEdgeScrollMargin settings entry. 2017-08-10 12:44:52 +02:00
Paul Chote
fd73556685 Revert "Have some activities count as idle activities"
This reverts commit ae111248f3.
2017-08-10 12:25:15 +02:00
rob-v
958cfcd378 Disable click sound on command bar buttons 2017-08-09 21:52:04 +01:00
rob-v
08be5d3907 Add ButtonWidget.DisableKeySound property 2017-08-09 21:51:58 +01:00
reaperrr
4adee762d9 Fix SpawnActorPower not playing launch sounds 2017-08-09 21:28:26 +01:00
abcdefg30
bf0b139398 Move the interior tile 384 from the Floor to the Wall category 2017-08-09 20:55:56 +02:00
SoScared
65fedd1eb1 Add Gap effect values to new vision range changes and ironing out misc. values 2017-08-09 20:37:06 +02:00
rob-v
117b229cb6 Fix Client tooltip crash in Lobby 2017-08-09 19:17:33 +01:00
abc013
76f52abd3d Fix ground units can stay on water (tile 409) 2017-08-08 12:33:19 +02:00
AoAGeneral
a7212d52fb TD Prerequisite changes.
Adds prerequisite labels to E1, E3, E6, and TDTRUCK.

Closes #13780
2017-08-08 12:22:19 +02:00
reaperrr
5e0a789dac Shrink shape of TD SAM 2017-08-07 14:52:29 +02:00
reaperrr
17b4158a56 Shrink shape of RA SAM
To adress balancing concerns.
Also tweaked sprite offsets to make it more centered.
2017-08-07 14:52:25 +02:00
reaperrr
09d9cf7da6 Rename tdtruk to truck 2017-08-07 11:00:48 +02:00
reaperrr
63ee481f01 Rename TD TowerMissle to TowerMissile 2017-08-07 11:00:46 +02:00
reaperrr
818fc9d173 Fix RA effect waheads
- take Trees into consideration (they don't have Ground TargetType)
- fixed the delayed effect warheads on Vulcan
2017-08-07 11:00:44 +02:00
Oliver Brakmann
fed3b5f565 Fix harv docking activities getting lost under certain circumstances 2017-08-07 10:45:57 +02:00
AoAGeneral
1d2dc1c232 TD Balance Changes. (20170802)
Engineer movement speed reduced to 48 from 56.

Helicopter crash damage increased to 100 from 40.

Rocket infantry spread damage reduced to 32 from 128.

------

Engineer movement was the same speed as minigunners and at times would enable them to escape the minigunners. This slight speed reduction helps for this.

Helicopter crash damage was lacking and a slight increase in this enables them to do some damage. With the cost of the helicopter it is still not a good idea to sacrifice aircraft just for exta damage.

Rocket infantry spread is reduced because of the damage done to infantry packs. Games turned into tanks mixed with rocket infantry. Reducing this spread enables them to single target units more closely then in larger armies.
2017-08-07 09:38:16 +02:00
Matthias Mailänder
722ea75cab Add weapon rules documentation. 2017-08-05 21:03:52 +02:00
Matthias Mailänder
e81b9e9467 Cache shouldn't be exposed at all so drop it. 2017-08-05 21:03:52 +02:00
Matthias Mailänder
509dc09aa6 Explain the color notation. 2017-08-05 21:03:52 +02:00
Matthias Mailänder
d71c377e93 Give Dictionaries a friendlier notation. 2017-08-05 21:03:52 +02:00
Matthias Mailänder
c6baf16fa7 Move friendly type names into shared helper class. 2017-08-05 21:03:52 +02:00
Lars Beckers
0529a16298 Fix RA Aftermath installer metadata on Linux 2017-08-02 20:11:23 +02:00
FrameLimiter
53adc2bec7 Fix duplicate Targetable value on ^Ammobox 2017-08-02 19:45:41 +02:00
Taryn Hill
e97617d618 Improve the exception message when a Bridge's DemolishWeapon cannot be found 2017-07-29 22:30:47 +02:00
Paul Chote
4d47dd6ad7 Add Demo Truck and MAD Tank to Deploy description. 2017-07-23 20:10:57 +02:00
Paul Chote
7ad95ed0b0 Implement IIssueDeployOrder on MADTank. 2017-07-23 20:10:57 +02:00
GSonderling
1d6732f24f Removed references to make docs 2017-07-23 15:26:45 +02:00
SoScared
85931d5f0b Revert 4 cell adjacency of Kennel/Silo back to 2. 2017-07-22 22:48:51 +01:00
abcdefg30
6f68b4fb3e Disable bots on Poland Raid (again) 2017-07-22 22:34:10 +01:00
132 changed files with 2100 additions and 670 deletions

3
.gitignore vendored
View File

@@ -62,6 +62,7 @@ OpenRA.Launcher.Mac/OpenRA.xcodeproj/*.mode1v3
# auto-generated documentation
DOCUMENTATION.md
WEAPONS.md
Lua-API.md
*.html
openra.6
@@ -80,3 +81,5 @@ StyleCopViolations.xml
# Support directory
/Support
/da/Microsoft.Build.Utilities.v3.5.resources.dll
/da

View File

@@ -14,9 +14,6 @@
# to check the official mod dlls for StyleCop violations, run:
# make check
#
# to generate documentation aimed at modders, run:
# make docs
#
# to install, run:
# make [prefix=/foo] [bindir=/bar/bin] install
#
@@ -414,7 +411,13 @@ install-linux-mime:
install-linux-appdata:
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/appdata/"
@$(INSTALL_DATA) packaging/linux/openra.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
@sed 's/{MOD}/ra/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Red Alert/g' | sed 's/{SCREENSHOT_RA}/ type="default"/g' | sed 's/{SCREENSHOT_CNC}//g' | sed 's/{SCREENSHOT_D2K}//g'> packaging/linux/openra-ra.appdata.xml
@$(INSTALL_DATA) packaging/linux/openra-ra.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
@sed 's/{MOD}/cnc/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Tiberian Dawn/g' | sed 's/{SCREENSHOT_RA}//g' | sed 's/{SCREENSHOT_CNC}/ type="default"/g' | sed 's/{SCREENSHOT_D2K}//g'> packaging/linux/openra-cnc.appdata.xml
@$(INSTALL_DATA) packaging/linux/openra-cnc.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
@sed 's/{MOD}/d2k/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Dune 2000/g' | sed 's/{SCREENSHOT_RA}//g' | sed 's/{SCREENSHOT_CNC}//g' | sed 's/{SCREENSHOT_D2K}/ type="default"/g'> packaging/linux/openra-d2k.appdata.xml
@$(INSTALL_DATA) packaging/linux/openra-d2k.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
@-$(RM) packaging/linux/openra-ra.appdata.xml packaging/linux/openra-cnc.appdata.xml packaging/linux/openra-d2k.appdata.xml
install-man-page: man-page
@$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man6/"
@@ -495,9 +498,6 @@ help:
@echo 'to check the official mods for erroneous yaml files, run:'
@echo ' make test'
@echo
@echo 'to generate documentation aimed at modders, run:'
@echo ' make docs'
@echo
@echo 'to install, run:'
@echo ' make [prefix=/foo] [bindir=/bar/bin] install'
@echo

View File

@@ -152,7 +152,6 @@ namespace OpenRA.Activities
set { NextActivity = value; }
}
public bool IsIdle { get; protected set; }
public bool IsInterruptible { get; protected set; }
public bool IsCanceled { get { return State == ActivityState.Canceled; } }

View File

@@ -53,7 +53,7 @@ namespace OpenRA
public IOccupySpace OccupiesSpace { get; private set; }
public ITargetable[] Targetables { get; private set; }
public bool IsIdle { get { return CurrentActivity == null || CurrentActivity.IsIdle; } }
public bool IsIdle { get { return CurrentActivity == null; } }
public bool IsDead { get { return Disposed || (health != null && health.IsDead); } }
public CPos Location { get { return OccupiesSpace.TopLeft; } }

View File

@@ -123,9 +123,6 @@ namespace OpenRA
foreach (var source in sources.Distinct())
{
if (!Directory.Exists(source))
continue;
var metadataPath = Path.Combine(source, "ModMetadata");
try

View File

@@ -28,6 +28,7 @@ namespace OpenRA
{
public static readonly MapPreview UnknownMap = new MapPreview(null, null, MapGridType.Rectangular, null);
public readonly IReadOnlyDictionary<IReadOnlyPackage, MapClassification> MapLocations;
readonly Dictionary<IReadOnlyPackage, MapClassification> mapLocations = new Dictionary<IReadOnlyPackage, MapClassification>();
readonly Cache<string, MapPreview> previews;
readonly ModData modData;
@@ -45,8 +46,16 @@ namespace OpenRA
previews = new Cache<string, MapPreview>(uid => new MapPreview(modData, uid, gridType.Value, this));
sheetBuilder = new SheetBuilder(SheetType.BGRA);
MapLocations = new ReadOnlyDictionary<IReadOnlyPackage, MapClassification>(mapLocations);
}
public void LoadMaps()
{
// Utility mod that does not support maps
if (!modData.Manifest.Contains<MapGrid>())
return;
// Enumerate map directories
var mapLocations = new Dictionary<IReadOnlyPackage, MapClassification>();
foreach (var kv in modData.Manifest.MapFolders)
{
var name = kv.Key;
@@ -82,15 +91,6 @@ namespace OpenRA
mapLocations.Add(package, classification);
}
MapLocations = new ReadOnlyDictionary<IReadOnlyPackage, MapClassification>(mapLocations);
}
public void LoadMaps()
{
// Utility mod that does not support maps
if (!modData.Manifest.Contains<MapGrid>())
return;
var mapGrid = modData.Manifest.Get<MapGrid>();
foreach (var kv in MapLocations)
{

View File

@@ -16,8 +16,9 @@ using OpenRA.Traits;
namespace OpenRA.Network
{
static class UnitOrders
public static class UnitOrders
{
public const int ChatMessageMaxLength = 2500;
const string ServerChatName = "Battlefield Control";
static Player FindPlayerByClient(this World world, Session.Client c)
@@ -28,7 +29,7 @@ namespace OpenRA.Network
p => (p.ClientIndex == c.Index && p.PlayerReference.Playable));
}
public static void ProcessOrder(OrderManager orderManager, World world, int clientId, Order order)
internal static void ProcessOrder(OrderManager orderManager, World world, int clientId, Order order)
{
if (world != null)
{
@@ -42,6 +43,12 @@ namespace OpenRA.Network
case "Chat":
{
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
// Cut chat messages to the hard limit to avoid exploits
var message = order.TargetString;
if (message.Length > ChatMessageMaxLength)
message = order.TargetString.Substring(0, ChatMessageMaxLength);
if (client != null)
{
var player = world != null ? world.FindPlayerByClient(client) : null;
@@ -51,10 +58,10 @@ namespace OpenRA.Network
if (orderManager.LocalClient != null && client != orderManager.LocalClient && client.Team > 0 && client.Team == orderManager.LocalClient.Team)
suffix += " (Ally)";
Game.AddChatLine(client.Color.RGB, client.Name + suffix, order.TargetString);
Game.AddChatLine(client.Color.RGB, client.Name + suffix, message);
}
else
Game.AddChatLine(Color.White, "(player {0})".F(clientId), order.TargetString);
Game.AddChatLine(Color.White, "(player {0})".F(clientId), message);
break;
}

View File

@@ -93,9 +93,6 @@ namespace OpenRA
break;
}
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
return dir + Path.DirectorySeparatorChar;
}

View File

@@ -157,6 +157,8 @@ namespace OpenRA
public string Platform = "Default";
public bool ViewportEdgeScroll = true;
public int ViewportEdgeScrollMargin = 5;
public bool LockMouseWindow = false;
public MouseScrollType MiddleMouseScroll = MouseScrollType.Standard;
public MouseScrollType RightMouseScroll = MouseScrollType.Disabled;

View File

@@ -61,7 +61,7 @@ namespace OpenRA.Mods.Cnc.Traits
}
}
class MadTank : IIssueOrder, IResolveOrder, IOrderVoice, ITick, IPreventsTeleport
class MadTank : IIssueOrder, IResolveOrder, IOrderVoice, ITick, IPreventsTeleport, IIssueDeployOrder
{
readonly Actor self;
readonly MadTankInfo info;
@@ -116,6 +116,11 @@ namespace OpenRA.Mods.Cnc.Traits
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
}
Order IIssueDeployOrder.IssueDeployOrder(Actor self)
{
return new Order("Detonate", self, false);
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return info.Voice;

View File

@@ -11,10 +11,12 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Activities;
using OpenRA.Mods.Common.Orders;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
@@ -28,18 +30,25 @@ namespace OpenRA.Mods.Cnc.Traits
public readonly WDist MinefieldDepth = new WDist(1536);
public object Create(ActorInitializer init) { return new Minelayer(init.Self); }
[Desc("Voice to use when ordered to lay a minefield.")]
[VoiceReference] public readonly string Voice = "Action";
public object Create(ActorInitializer init) { return new Minelayer(init.Self, this); }
}
public class Minelayer : IIssueOrder, IResolveOrder, IRenderAboveShroudWhenSelected, ISync
public class Minelayer : IIssueOrder, IResolveOrder, IRenderAboveShroudWhenSelected, ISync, IIssueDeployOrder, IOrderVoice
{
readonly MinelayerInfo info;
/* TODO: [Sync] when sync can cope with arrays! */
public CPos[] Minefield = null;
readonly Sprite tile;
[Sync] CPos minefieldStart;
public Minelayer(Actor self)
public Minelayer(Actor self, MinelayerInfo info)
{
this.info = info;
var tileset = self.World.Map.Tileset.ToLowerInvariant();
tile = self.World.Map.Rules.Sequences.GetSequence("overlay", "build-valid-{0}".F(tileset)).GetSprite(0);
}
@@ -72,6 +81,11 @@ namespace OpenRA.Mods.Cnc.Traits
}
}
Order IIssueDeployOrder.IssueDeployOrder(Actor self)
{
return new Order("PlaceMine", self, false) { TargetLocation = self.Location };
}
void IResolveOrder.ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "BeginMinefield")
@@ -89,15 +103,25 @@ namespace OpenRA.Mods.Cnc.Traits
{
var movement = self.Trait<IPositionable>();
Minefield = GetMinefieldCells(minefieldStart, order.TargetLocation,
self.Info.TraitInfo<MinelayerInfo>().MinefieldDepth)
Minefield = GetMinefieldCells(minefieldStart, order.TargetLocation, info.MinefieldDepth)
.Where(p => movement.CanEnterCell(p, null, false)).ToArray();
if (Minefield.Length == 1 && Minefield[0] != self.Location)
self.SetTargetLine(Target.FromCell(self.World, Minefield[0]), Color.Red);
self.CancelActivity();
self.QueueActivity(new LayMines(self));
}
}
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
{
if (order.OrderString == "PlaceMine" || order.OrderString == "PlaceMinefield")
return info.Voice;
return null;
}
static IEnumerable<CPos> GetMinefieldCells(CPos start, CPos end, WDist depth)
{
var mins = new CPos(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y));
@@ -122,6 +146,10 @@ namespace OpenRA.Mods.Cnc.Traits
if (self.Owner != self.World.LocalPlayer || Minefield == null)
yield break;
// Single-cell mine fields use a target line instead
if (Minefield.Length == 1)
yield break;
var pal = wr.Palette(TileSet.TerrainPaletteInternalName);
foreach (var c in Minefield)
yield return new SpriteRenderable(tile, self.World.Map.CenterOfCell(c),
@@ -178,7 +206,6 @@ namespace OpenRA.Mods.Cnc.Traits
world.CancelInputMode();
}
CPos lastMousePos;
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
public IEnumerable<IRenderable> RenderAboveShroud(WorldRenderer wr, World world)
{
@@ -187,6 +214,7 @@ namespace OpenRA.Mods.Cnc.Traits
yield break;
// We get the biggest depth so we cover all cells that mines could be placed on.
var lastMousePos = wr.Viewport.ViewToWorld(Viewport.LastMousePos);
var minefield = GetMinefieldCells(minefieldStart, lastMousePos,
minelayers.Max(m => m.Info.TraitInfo<MinelayerInfo>().MinefieldDepth));
@@ -202,7 +230,7 @@ namespace OpenRA.Mods.Cnc.Traits
public string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi)
{
lastMousePos = cell; return "ability"; /* TODO */
return "ability";
}
}

View File

@@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Activities
return NextActivity;
// TODO: This should fire each weapon at its maximum range
if (attackPlane != null && target.IsInRange(self.CenterPosition, attackPlane.Armaments.Select(a => a.Weapon.MinRange).Min()))
if (attackPlane != null && target.IsInRange(self.CenterPosition, attackPlane.Armaments.Where(Exts.IsTraitEnabled).Select(a => a.Weapon.MinRange).Min()))
ChildActivity = ActivityUtils.SequenceActivities(new FlyTimed(ticksUntilTurn, self), new Fly(self, target), new FlyTimed(ticksUntilTurn, self));
else
ChildActivity = ActivityUtils.SequenceActivities(new Fly(self, target), new FlyTimed(ticksUntilTurn, self));

View File

@@ -21,7 +21,6 @@ namespace OpenRA.Mods.Common.Activities
public FlyCircle(Actor self)
{
IsIdle = true;
plane = self.Trait<Aircraft>();
cruiseAltitude = plane.Info.CruiseAltitude;
}

View File

@@ -22,7 +22,6 @@ namespace OpenRA.Mods.Common.Activities
public HeliFlyCircle(Actor self)
{
helicopter = self.Trait<Aircraft>();
IsIdle = true;
}
public override Activity Tick(Actor self)

View File

@@ -23,8 +23,6 @@ namespace OpenRA.Mods.Common.Activities
{
public class Move : Activity
{
const int AverageTicksBeforePathing = 5;
const int SpreadTicksBeforePathing = 5;
static readonly List<CPos> NoPath = new List<CPos>();
readonly Mobile mobile;
@@ -41,7 +39,6 @@ namespace OpenRA.Mods.Common.Activities
int waitTicksRemaining;
// To work around queued activity issues while minimizing changes to legacy behaviour
int ticksBeforePathing;
bool evaluateNearestMovableCell;
// Scriptable move order
@@ -140,9 +137,6 @@ namespace OpenRA.Mods.Common.Activities
protected override void OnFirstRun(Actor self)
{
ticksBeforePathing = AverageTicksBeforePathing +
self.World.SharedRandom.Next(-SpreadTicksBeforePathing, SpreadTicksBeforePathing);
if (evaluateNearestMovableCell && destination.HasValue)
{
var movableDestination = mobile.NearestMoveableCell(destination.Value);
@@ -165,9 +159,9 @@ namespace OpenRA.Mods.Common.Activities
if (path == null)
{
if (ticksBeforePathing > 0)
if (mobile.TicksBeforePathing > 0)
{
--ticksBeforePathing;
--mobile.TicksBeforePathing;
return this;
}
@@ -264,9 +258,9 @@ namespace OpenRA.Mods.Common.Activities
if (--waitTicksRemaining >= 0)
return null;
if (ticksBeforePathing > 0)
if (mobile.TicksBeforePathing > 0)
{
--ticksBeforePathing;
--mobile.TicksBeforePathing;
return null;
}

View File

@@ -67,7 +67,7 @@ namespace OpenRA.Mods.Common.Activities
nt.BeforeTransform(self);
var makeAnimation = self.TraitOrDefault<WithMakeAnimation>();
if (makeAnimation != null)
if (!SkipMakeAnims && makeAnimation != null)
{
// Once the make animation starts the activity must not be stopped anymore.
IsInterruptible = false;

View File

@@ -21,9 +21,6 @@ namespace OpenRA.Mods.Common
{
public static bool IsAtGroundLevel(this Actor self)
{
if (self.IsDead)
return false;
if (self.OccupiesSpace == null)
return false;

View File

@@ -26,8 +26,7 @@ namespace OpenRA.Mods.Common.Widgets
readonly EditorActorLayer editorLayer;
readonly EditorViewportControllerWidget editorWidget;
readonly ActorPreviewWidget preview;
readonly CVec locationOffset;
readonly WVec previewOffset;
readonly WVec centerOffset;
readonly PlayerReference owner;
readonly CVec[] footprint;
@@ -49,10 +48,7 @@ namespace OpenRA.Mods.Common.Widgets
var buildingInfo = actor.TraitInfoOrDefault<BuildingInfo>();
if (buildingInfo != null)
{
locationOffset = -buildingInfo.LocationOffset();
previewOffset = buildingInfo.CenterOffset(world);
}
centerOffset = buildingInfo.CenterOffset(world);
var td = new TypeDictionary();
td.Add(new FacingInit(facing));
@@ -91,17 +87,16 @@ namespace OpenRA.Mods.Common.Widgets
return false;
}
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location);
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location - worldRenderer.ScreenPxOffset(centerOffset));
if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down)
{
// Check the actor is inside the map
if (!footprint.All(c => world.Map.Tiles.Contains(cell + locationOffset + c)))
if (!footprint.All(c => world.Map.Tiles.Contains(cell + c)))
return true;
var newActorReference = new ActorReference(Actor.Name);
newActorReference.Add(new OwnerInit(owner.Name));
cell += locationOffset;
newActorReference.Add(new LocationInit(cell));
var ios = Actor.TraitInfoOrDefault<IOccupySpaceInfo>();
@@ -128,8 +123,8 @@ namespace OpenRA.Mods.Common.Widgets
public void Tick()
{
var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
var pos = world.Map.CenterOfCell(cell + locationOffset) + previewOffset;
var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos - worldRenderer.ScreenPxOffset(centerOffset));
var pos = world.Map.CenterOfCell(cell) + centerOffset;
var origin = worldRenderer.Viewport.WorldToViewPx(worldRenderer.ScreenPxPosition(pos));

View File

@@ -41,9 +41,9 @@
<HintPath>..\thirdparty\download\FuzzyLogicLibrary.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="rix0rrr.BeaconLib, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Reference Include="rix0rrr.BeaconLib">
<HintPath>..\thirdparty\download\rix0rrr.BeaconLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@@ -549,6 +549,7 @@
<Compile Include="UtilityCommands\ExtractSettingsDocsCommand.cs" />
<Compile Include="UtilityCommands\ExtractZeroBraneStudioLuaAPI.cs" />
<Compile Include="UtilityCommands\ExtractTraitDocsCommand.cs" />
<Compile Include="UtilityCommands\ExtractWeaponDocsCommand.cs" />
<Compile Include="Widgets\Logic\Ingame\MusicControllerLogic.cs" />
<Compile Include="Widgets\UnitCommandWidget.cs" />
<Compile Include="WorldExtensions.cs" />

View File

@@ -33,14 +33,18 @@ namespace OpenRA.Mods.Common.Orders
readonly string faction;
readonly Sprite buildOk;
readonly Sprite buildBlocked;
readonly Viewport viewport;
readonly WVec centerOffset;
readonly int2 topLeftScreenOffset;
IActorPreview[] preview;
bool initialized;
public PlaceBuildingOrderGenerator(ProductionQueue queue, string name)
public PlaceBuildingOrderGenerator(ProductionQueue queue, string name, WorldRenderer worldRenderer)
{
var world = queue.Actor.World;
this.queue = queue;
viewport = worldRenderer.Viewport;
placeBuildingInfo = queue.Actor.Owner.PlayerActor.Info.TraitInfo<PlaceBuildingInfo>();
building = name;
@@ -53,6 +57,8 @@ namespace OpenRA.Mods.Common.Orders
var info = map.Rules.Actors[building];
buildingInfo = info.TraitInfo<BuildingInfo>();
centerOffset = buildingInfo.CenterOffset(world);
topLeftScreenOffset = -worldRenderer.ScreenPxOffset(centerOffset);
var buildableInfo = info.TraitInfo<BuildableInfo>();
var mostLikelyProducer = queue.MostLikelyProducer();
@@ -99,7 +105,7 @@ namespace OpenRA.Mods.Common.Orders
if (mi.Button == MouseButton.Left)
{
var orderType = "PlaceBuilding";
var topLeft = cell - buildingInfo.LocationOffset();
var topLeft = viewport.ViewToWorld(Viewport.LastMousePos + topLeftScreenOffset);
var plugInfo = world.Map.Rules.Actors[building].TraitInfoOrDefault<PlugInfo>();
if (plugInfo != null)
@@ -162,14 +168,13 @@ namespace OpenRA.Mods.Common.Orders
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
public IEnumerable<IRenderable> RenderAboveShroud(WorldRenderer wr, World world)
{
var xy = wr.Viewport.ViewToWorld(Viewport.LastMousePos);
var topLeft = xy - buildingInfo.LocationOffset();
var offset = world.Map.CenterOfCell(topLeft) + buildingInfo.CenterOffset(world);
var topLeft = viewport.ViewToWorld(Viewport.LastMousePos + topLeftScreenOffset);
var centerPosition = world.Map.CenterOfCell(topLeft) + centerOffset;
var rules = world.Map.Rules;
var actorInfo = rules.Actors[building];
foreach (var dec in actorInfo.TraitInfos<IPlaceBuildingDecorationInfo>())
foreach (var r in dec.Render(wr, world, actorInfo, offset))
foreach (var r in dec.Render(wr, world, actorInfo, centerPosition))
yield return r;
var cells = new Dictionary<CPos, CellType>();
@@ -219,7 +224,7 @@ namespace OpenRA.Mods.Common.Orders
}
var previewRenderables = preview
.SelectMany(p => p.Render(wr, offset))
.SelectMany(p => p.Render(wr, centerPosition))
.OrderBy(WorldRenderer.RenderableScreenZPositionComparisonKey);
foreach (var r in previewRenderables)

View File

@@ -210,7 +210,7 @@ namespace OpenRA.Mods.Common.Traits
foreach (var armament in Armaments)
{
var checkIsValid = checkForCenterTargetingWeapons ? armament.Weapon.TargetActorCenter : !armament.OutOfAmmo;
if (checkIsValid && armament.Weapon.IsValidAgainst(t, self.World, self))
if (checkIsValid && !armament.IsTraitDisabled && armament.Weapon.IsValidAgainst(t, self.World, self))
return true;
}

View File

@@ -151,6 +151,9 @@ namespace OpenRA.Mods.Common.Traits
foreach (var a in Armaments)
{
if (a.IsTraitDisabled)
continue;
var port = SelectFirePort(self, targetYaw);
if (port == null)
return;

View File

@@ -9,8 +9,11 @@
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Mods.Common.Activities;
using OpenRA.Orders;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
@@ -20,10 +23,18 @@ namespace OpenRA.Mods.Common.Traits
{
[VoiceReference] public readonly string Voice = "Action";
[GrantedConditionReference]
[Desc("The condition to grant to self while scanning for targets during an attack-move.")]
public readonly string AttackMoveScanCondition = null;
[GrantedConditionReference]
[Desc("The condition to grant to self while scanning for targets during an assault-move.")]
public readonly string AssaultMoveScanCondition = null;
public object Create(ActorInitializer init) { return new AttackMove(init.Self, this); }
}
class AttackMove : IResolveOrder, IOrderVoice, INotifyIdle, ISync
class AttackMove : INotifyCreated, ITick, IResolveOrder, IOrderVoice, INotifyIdle, ISync
{
[Sync] public CPos _targetLocation { get { return TargetLocation.HasValue ? TargetLocation.Value : CPos.Zero; } }
public CPos? TargetLocation = null;
@@ -31,23 +42,54 @@ namespace OpenRA.Mods.Common.Traits
readonly IMove move;
readonly AttackMoveInfo info;
ConditionManager conditionManager;
int attackMoveToken = ConditionManager.InvalidConditionToken;
int assaultMoveToken = ConditionManager.InvalidConditionToken;
bool assaultMoving = false;
public AttackMove(Actor self, AttackMoveInfo info)
{
move = self.Trait<IMove>();
this.info = info;
}
void INotifyCreated.Created(Actor self)
{
conditionManager = self.TraitOrDefault<ConditionManager>();
}
void ITick.Tick(Actor self)
{
if (conditionManager == null)
return;
var activity = self.CurrentActivity as AttackMoveActivity;
var attackActive = activity != null && !assaultMoving;
var assaultActive = activity != null && assaultMoving;
if (attackActive && attackMoveToken == ConditionManager.InvalidConditionToken && !string.IsNullOrEmpty(info.AttackMoveScanCondition))
attackMoveToken = conditionManager.GrantCondition(self, info.AttackMoveScanCondition);
else if (!attackActive && attackMoveToken != ConditionManager.InvalidConditionToken)
attackMoveToken = conditionManager.RevokeCondition(self, attackMoveToken);
if (assaultActive && assaultMoveToken == ConditionManager.InvalidConditionToken && !string.IsNullOrEmpty(info.AssaultMoveScanCondition))
assaultMoveToken = conditionManager.GrantCondition(self, info.AssaultMoveScanCondition);
else if (!assaultActive && assaultMoveToken != ConditionManager.InvalidConditionToken)
assaultMoveToken = conditionManager.RevokeCondition(self, assaultMoveToken);
}
public string VoicePhraseForOrder(Actor self, Order order)
{
if (order.OrderString == "AttackMove")
if (order.OrderString == "AttackMove" || order.OrderString == "AssaultMove")
return info.Voice;
return null;
}
void Activate(Actor self)
void Activate(Actor self, bool assaultMove)
{
self.CancelActivity();
assaultMoving = assaultMove;
self.QueueActivity(new AttackMoveActivity(self, move.MoveTo(TargetLocation.Value, 1)));
}
@@ -55,19 +97,71 @@ namespace OpenRA.Mods.Common.Traits
{
// This might cause the actor to be stuck if the target location is unreachable
if (TargetLocation.HasValue && self.Location != TargetLocation.Value)
Activate(self);
Activate(self, assaultMoving);
}
public void ResolveOrder(Actor self, Order order)
{
TargetLocation = null;
if (order.OrderString == "AttackMove")
if (order.OrderString == "AttackMove" || order.OrderString == "AssaultMove")
{
TargetLocation = move.NearestMoveableCell(order.TargetLocation);
self.SetTargetLine(Target.FromCell(self.World, TargetLocation.Value), Color.Red);
Activate(self);
Activate(self, order.OrderString == "AssaultMove");
}
}
}
public class AttackMoveOrderGenerator : UnitOrderGenerator
{
readonly TraitPair<AttackMove>[] subjects;
readonly MouseButton expectedButton;
public AttackMoveOrderGenerator(IEnumerable<Actor> subjects, MouseButton button)
{
expectedButton = button;
this.subjects = subjects.Where(a => !a.IsDead)
.SelectMany(a => a.TraitsImplementing<AttackMove>()
.Select(am => new TraitPair<AttackMove>(a, am)))
.ToArray();
}
public override IEnumerable<Order> Order(World world, CPos cell, int2 worldPixel, MouseInput mi)
{
if (mi.Button != expectedButton)
world.CancelInputMode();
return OrderInner(world, cell, mi);
}
protected virtual IEnumerable<Order> OrderInner(World world, CPos cell, MouseInput mi)
{
if (mi.Button == expectedButton)
{
world.CancelInputMode();
var queued = mi.Modifiers.HasModifier(Modifiers.Shift);
var orderName = mi.Modifiers.HasModifier(Modifiers.Ctrl) ? "AssaultMove" : "AttackMove";
// Cells outside the playable area should be clamped to the edge for consistency with move orders
cell = world.Map.Clamp(cell);
foreach (var s in subjects)
yield return new Order(orderName, s.Actor, queued) { TargetLocation = cell };
}
}
public override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi)
{
var prefix = mi.Modifiers.HasModifier(Modifiers.Ctrl) ? "assaultmove" : "attackmove";
return world.Map.Contains(cell) ? prefix : prefix + "-blocked";
}
public override bool InputOverridesSelection(World world, int2 xy, MouseInput mi)
{
// Custom order generators always override selection
return true;
}
}
}

View File

@@ -60,10 +60,6 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Ticks to wait until next AutoTarget: attempt.")]
public readonly int MaximumScanTimeInterval = 8;
public readonly bool TargetWhenIdle = true;
public readonly bool TargetWhenDamaged = true;
public override object Create(ActorInitializer init) { return new AutoTarget(init, this); }
public override void RulesetLoaded(Ruleset rules, ActorInfo info)
@@ -157,17 +153,18 @@ namespace OpenRA.Mods.Common.Traits
public void Damaged(Actor self, AttackInfo e)
{
if (IsTraitDisabled)
if (IsTraitDisabled || !self.IsIdle || Stance < UnitStance.ReturnFire)
return;
if (!self.IsIdle || !Info.TargetWhenDamaged)
// Don't retaliate against healers
if (e.Damage.Value < 0)
return;
var attacker = e.Attacker;
if (attacker.Disposed || Stance < UnitStance.ReturnFire)
if (attacker.Disposed)
return;
if (!attacker.IsInWorld && !attacker.Disposed)
if (!attacker.IsInWorld)
{
// If the aggressor is in a transport, then attack the transport instead
var passenger = attacker.TraitOrDefault<Passenger>();
@@ -175,19 +172,15 @@ namespace OpenRA.Mods.Common.Traits
attacker = passenger.Transport;
}
// not a lot we can do about things we can't hurt... although maybe we should automatically run away?
// Not a lot we can do about things we can't hurt... although maybe we should automatically run away?
var attackerAsTarget = Target.FromActor(attacker);
if (!activeAttackBases.Any(a => a.HasAnyValidWeapons(attackerAsTarget)))
return;
// don't retaliate against own units force-firing on us. It's usually not what the player wanted.
// Don't retaliate against own units force-firing on us. It's usually not what the player wanted.
if (attacker.AppearsFriendlyTo(self))
return;
// don't retaliate against healers
if (e.Damage.Value < 0)
return;
Aggressor = attacker;
bool allowMove;
@@ -197,10 +190,7 @@ namespace OpenRA.Mods.Common.Traits
public void TickIdle(Actor self)
{
if (IsTraitDisabled)
return;
if (Stance < UnitStance.Defend || !Info.TargetWhenIdle)
if (IsTraitDisabled || Stance < UnitStance.Defend)
return;
bool allowMove;

View File

@@ -47,7 +47,18 @@ namespace OpenRA.Mods.Common.Traits
public object Create(ActorInitializer init) { return new Bridge(init.Self, this); }
public void RulesetLoaded(Ruleset rules, ActorInfo ai) { DemolishWeaponInfo = rules.Weapons[DemolishWeapon.ToLowerInvariant()]; }
public void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
if (string.IsNullOrEmpty(DemolishWeapon))
throw new YamlException("A value for DemolishWeapon of a Bridge trait is missing.");
WeaponInfo weapon;
var weaponToLower = DemolishWeapon.ToLowerInvariant();
if (!rules.Weapons.TryGetValue(weaponToLower, out weapon))
throw new YamlException("Weapons Ruleset does not contain an entry '{0}'".F(weaponToLower));
DemolishWeaponInfo = weapon;
}
public IEnumerable<Pair<ushort, int>> Templates
{

View File

@@ -144,11 +144,6 @@ namespace OpenRA.Mods.Common.Traits
yield return t;
}
public CVec LocationOffset()
{
return new CVec(Dimensions.X / 2, Dimensions.Y > 1 ? (Dimensions.Y + 1) / 2 : 0);
}
public WVec CenterOffset(World w)
{
var off = (w.Map.CenterOfCell(new CPos(Dimensions.X, Dimensions.Y)) - w.Map.CenterOfCell(new CPos(1, 1))) / 2;

View File

@@ -139,12 +139,12 @@ namespace OpenRA.Mods.Common.Traits
{
if (!preventDock)
{
harv.QueueActivity(new CallFunc(() => dockedHarv = harv, false));
harv.QueueActivity(DockSequence(harv, self));
harv.QueueActivity(new CallFunc(() => dockedHarv = null, false));
dockOrder.Queue(new CallFunc(() => dockedHarv = harv, false));
dockOrder.Queue(DockSequence(harv, self));
dockOrder.Queue(new CallFunc(() => dockedHarv = null, false));
}
harv.QueueActivity(new CallFunc(() => harv.Trait<Harvester>().ContinueHarvesting(harv)));
dockOrder.Queue(new CallFunc(() => harv.Trait<Harvester>().ContinueHarvesting(harv)));
}
void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)

View File

@@ -117,6 +117,9 @@ namespace OpenRA.Mods.Common.Traits
foreach (var a in attack.Armaments)
{
if (a.IsTraitDisabled)
continue;
foreach (var b in a.Barrels)
{
var muzzle = self.CenterPosition + a.MuzzleOffset(self, b);

View File

@@ -55,6 +55,9 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Can this actor undeploy?")]
public readonly bool CanUndeploy = true;
[Desc("Skip make/deploy animation?")]
public readonly bool SkipMakeAnimation = false;
public object Create(ActorInitializer init) { return new GrantConditionOnDeploy(init, this); }
}
@@ -223,7 +226,7 @@ namespace OpenRA.Mods.Common.Traits
OnDeployCompleted();
else
foreach (var n in notify)
n.Deploy(self);
n.Deploy(self, Info.SkipMakeAnimation);
}
/// <summary>Play undeploy sound and animation and after that revoke the condition.</summary>
@@ -246,7 +249,7 @@ namespace OpenRA.Mods.Common.Traits
OnUndeployCompleted();
else
foreach (var n in notify)
n.Undeploy(self);
n.Undeploy(self, Info.SkipMakeAnimation);
}
void OnDeployStarted()

View File

@@ -392,6 +392,10 @@ namespace OpenRA.Mods.Common.Traits
public class Mobile : ConditionalTrait<MobileInfo>, INotifyCreated, IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove,
IFacing, IDeathActorInitModifier, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyBlockingMove, IActorPreviewInitModifier, INotifyBecomingIdle
{
const int AverageTicksBeforePathing = 5;
const int SpreadTicksBeforePathing = 5;
internal int TicksBeforePathing = 0;
readonly Actor self;
readonly Lazy<IEnumerable<int>> speedModifiers;
public bool IsMoving { get; set; }
@@ -629,14 +633,18 @@ namespace OpenRA.Mods.Common.Traits
{
if (order.OrderString == "Move")
{
if (!Info.MoveIntoShroud && !self.Owner.Shroud.IsExplored(order.TargetLocation))
var loc = self.World.Map.Clamp(order.TargetLocation);
if (!Info.MoveIntoShroud && !self.Owner.Shroud.IsExplored(loc))
return;
if (!order.Queued)
self.CancelActivity();
self.SetTargetLine(Target.FromCell(self.World, order.TargetLocation), Color.Green);
self.QueueActivity(order.Queued, new Move(self, order.TargetLocation, WDist.FromCells(8), null, true));
TicksBeforePathing = AverageTicksBeforePathing + self.World.SharedRandom.Next(-SpreadTicksBeforePathing, SpreadTicksBeforePathing);
self.SetTargetLine(Target.FromCell(self.World, loc), Color.Green);
self.QueueActivity(order.Queued, new Move(self, loc, WDist.FromCells(8), null, true));
}
if (order.OrderString == "Stop")

View File

@@ -116,16 +116,24 @@ namespace OpenRA.Mods.Common.Traits.Render
}
// TODO: Make this use Forward instead
void INotifyDeployTriggered.Deploy(Actor self)
void INotifyDeployTriggered.Deploy(Actor self, bool skipMakeAnim)
{
var notified = false;
var notify = self.TraitsImplementing<INotifyDeployComplete>();
if (skipMakeAnim)
{
foreach (var n in notify)
n.FinishedDeploy(self);
return;
}
foreach (var wsb in wsbs)
{
if (wsb.IsTraitDisabled)
continue;
var notify = self.TraitsImplementing<INotifyDeployComplete>();
wsb.PlayCustomAnimation(self, info.Sequence, () =>
{
if (notified)
@@ -141,16 +149,24 @@ namespace OpenRA.Mods.Common.Traits.Render
}
// TODO: Make this use Reverse instead
void INotifyDeployTriggered.Undeploy(Actor self)
void INotifyDeployTriggered.Undeploy(Actor self, bool skipMakeAnim)
{
var notified = false;
var notify = self.TraitsImplementing<INotifyDeployComplete>();
if (skipMakeAnim)
{
foreach (var n in notify)
n.FinishedUndeploy(self);
return;
}
foreach (var wsb in wsbs)
{
if (wsb.IsTraitDisabled)
continue;
var notify = self.TraitsImplementing<INotifyDeployComplete>();
wsb.PlayCustomAnimationBackwards(self, info.Sequence, () =>
{
if (notified)

View File

@@ -23,6 +23,9 @@ namespace OpenRA.Mods.Common.Traits
public readonly int RefundPercent = 50;
public readonly string[] SellSounds = { };
[Desc("Skip playing (reversed) make animation.")]
public readonly bool SkipMakeAnimation = false;
public override object Create(ActorInitializer init) { return new Sellable(init.Self, this); }
}
@@ -63,11 +66,17 @@ namespace OpenRA.Mods.Common.Traits
foreach (var ns in self.TraitsImplementing<INotifySold>())
ns.Selling(self);
var makeAnimation = self.TraitOrDefault<WithMakeAnimation>();
if (makeAnimation != null)
makeAnimation.Reverse(self, new Sell(self), false);
else
self.QueueActivity(false, new Sell(self));
if (!info.SkipMakeAnimation)
{
var makeAnimation = self.TraitOrDefault<WithMakeAnimation>();
if (makeAnimation != null)
{
makeAnimation.Reverse(self, new Sell(self), false);
return;
}
}
self.QueueActivity(false, new Sell(self));
}
public bool IsTooltipVisible(Player forPlayer)

View File

@@ -53,11 +53,13 @@ namespace OpenRA.Mods.Common.Traits
public override object Create(ActorInitializer init) { return new SpawnActorOnDeath(init, this); }
}
public class SpawnActorOnDeath : ConditionalTrait<SpawnActorOnDeathInfo>, INotifyKilled
public class SpawnActorOnDeath : ConditionalTrait<SpawnActorOnDeathInfo>, INotifyKilled, INotifyRemovedFromWorld
{
readonly string faction;
readonly bool enabled;
Player attackingPlayer;
public SpawnActorOnDeath(ActorInitializer init, SpawnActorOnDeathInfo info)
: base(info)
{
@@ -65,7 +67,7 @@ namespace OpenRA.Mods.Common.Traits
faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName;
}
public void Killed(Actor self, AttackInfo e)
void INotifyKilled.Killed(Actor self, AttackInfo e)
{
if (!enabled || IsTraitDisabled)
return;
@@ -79,39 +81,41 @@ namespace OpenRA.Mods.Common.Traits
if (Info.DeathType != null && !e.Damage.DamageTypes.Contains(Info.DeathType))
return;
self.World.AddFrameEndTask(w =>
attackingPlayer = e.Attacker.Owner;
}
// Don't add the new actor to the world before all RemovedFromWorld callbacks have run
void INotifyRemovedFromWorld.RemovedFromWorld(Actor self)
{
if (attackingPlayer == null)
return;
var td = new TypeDictionary
{
// Actor has been disposed by something else before its death (for example `Enter`).
if (self.Disposed)
return;
new ParentActorInit(self),
new LocationInit(self.Location + Info.Offset),
new CenterPositionInit(self.CenterPosition),
new FactionInit(faction)
};
var td = new TypeDictionary
{
new ParentActorInit(self),
new LocationInit(self.Location + Info.Offset),
new CenterPositionInit(self.CenterPosition),
new FactionInit(faction)
};
if (Info.OwnerType == OwnerType.Victim)
td.Add(new OwnerInit(self.Owner));
else if (Info.OwnerType == OwnerType.Killer)
td.Add(new OwnerInit(attackingPlayer));
else
td.Add(new OwnerInit(self.World.Players.First(p => p.InternalName == Info.InternalOwner)));
if (Info.OwnerType == OwnerType.Victim)
td.Add(new OwnerInit(self.Owner));
else if (Info.OwnerType == OwnerType.Killer)
td.Add(new OwnerInit(e.Attacker.Owner));
else
td.Add(new OwnerInit(self.World.Players.First(p => p.InternalName == Info.InternalOwner)));
if (Info.SkipMakeAnimations)
td.Add(new SkipMakeAnimsInit());
if (Info.SkipMakeAnimations)
td.Add(new SkipMakeAnimsInit());
foreach (var modifier in self.TraitsImplementing<IDeathActorInitModifier>())
modifier.ModifyDeathActorInit(self, td);
foreach (var modifier in self.TraitsImplementing<IDeathActorInitModifier>())
modifier.ModifyDeathActorInit(self, td);
var huskActor = self.TraitsImplementing<IHuskModifier>()
.Select(ihm => ihm.HuskActor(self))
.FirstOrDefault(a => a != null);
var huskActor = self.TraitsImplementing<IHuskModifier>()
.Select(ihm => ihm.HuskActor(self))
.FirstOrDefault(a => a != null);
w.CreateActor(huskActor ?? Info.Actor, td);
});
self.World.AddFrameEndTask(w => w.CreateActor(huskActor ?? Info.Actor, td));
}
}
}

View File

@@ -50,6 +50,7 @@ namespace OpenRA.Mods.Common.Traits
{
var location = self.World.Map.CenterOfCell(order.TargetLocation);
PlayLaunchSounds();
Game.Sound.Play(SoundType.World, info.DeploySound, location);
if (!string.IsNullOrEmpty(info.EffectSequence) && !string.IsNullOrEmpty(info.EffectPalette))

View File

@@ -160,13 +160,14 @@ namespace OpenRA.Mods.Common.Traits
public int RemainingTime;
public int TotalTime;
public bool Active { get; private set; }
public bool Disabled { get { return (!prereqsAvailable && !manager.DevMode.AllTech) || !instancesEnabled; } }
public bool Disabled { get { return (!prereqsAvailable && !manager.DevMode.AllTech) || !instancesEnabled || oneShotFired; } }
public SupportPowerInfo Info { get { return Instances.Select(i => i.Info).FirstOrDefault(); } }
public bool Ready { get { return Active && RemainingTime == 0; } }
bool instancesEnabled;
bool prereqsAvailable = true;
bool oneShotFired;
public SupportPowerInstance(string key, SupportPowerManager manager)
{
@@ -249,7 +250,10 @@ namespace OpenRA.Mods.Common.Traits
notifiedCharging = notifiedReady = false;
if (Info.OneShot)
{
PrerequisitesAvailable(false);
oneShotFired = true;
}
}
}

View File

@@ -175,8 +175,8 @@ namespace OpenRA.Mods.Common.Traits
public interface INotifyDeployTriggered
{
void Deploy(Actor self);
void Undeploy(Actor self);
void Deploy(Actor self, bool skipMakeAnim);
void Undeploy(Actor self, bool skipMakeAnim);
}
public interface IAcceptResourcesInfo : ITraitInfo { }

View File

@@ -11,8 +11,10 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Support;
using OpenRA.Traits;
@@ -189,5 +191,64 @@ namespace OpenRA.Mods.Common
return rules.Actors.Where(a => a.Value.HasTraitInfo<IBlocksProjectilesInfo>())
.SelectMany(a => a.Value.TraitInfos<HitShapeInfo>()).Max(h => h.Type.OuterRadius);
}
public static string FriendlyTypeName(Type t)
{
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(HashSet<>))
return "Set of {0}".F(t.GetGenericArguments().Select(FriendlyTypeName).ToArray());
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dictionary<,>))
return "Mapping of {0} to {1}".F(t.GetGenericArguments().Select(FriendlyTypeName).ToArray());
if (t.IsSubclassOf(typeof(Array)))
return "Collection of {0}".F(FriendlyTypeName(t.GetElementType()));
if (t.IsGenericType && t.GetGenericTypeDefinition().GetInterfaces().Any(e => e.IsGenericType && e.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
return "Collection of {0}".F(FriendlyTypeName(t.GetGenericArguments().First()));
if (t == typeof(int) || t == typeof(uint))
return "Integer";
if (t == typeof(int2))
return "2D Integer";
if (t == typeof(float) || t == typeof(decimal))
return "Real Number";
if (t == typeof(float2))
return "2D Real Number";
if (t == typeof(CPos))
return "2D Cell Position";
if (t == typeof(CVec))
return "2D Cell Vector";
if (t == typeof(WAngle))
return "1D World Angle";
if (t == typeof(WRot))
return "3D World Rotation";
if (t == typeof(WPos))
return "3D World Position";
if (t == typeof(WDist))
return "1D World Distance";
if (t == typeof(WVec))
return "3D World Vector";
if (t == typeof(HSLColor) || t == typeof(Color))
return "Color (RRGGBB[AA] notation)";
if (t == typeof(IProjectileInfo))
return "Projectile";
if (t == typeof(IWarhead))
return "Warhead";
return t.Name;
}
}
}

View File

@@ -13,7 +13,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.UtilityCommands
@@ -57,7 +56,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
var traitName = t.Name.EndsWith("Info") ? t.Name.Substring(0, t.Name.Length - 4) : t.Name;
toc.AppendLine(" * [{0}](#{1})".F(traitName, traitName.ToLowerInvariant()));
toc.AppendLine(" * [{0}](#{1})".F(traitName, traitName.ToLowerInvariant()));
var traitDescLines = t.GetCustomAttributes<DescAttribute>(false).SelectMany(d => d.Lines);
doc.AppendLine();
doc.AppendLine("### {0}".F(traitName));
@@ -92,7 +91,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
foreach (var info in infos)
{
var fieldDescLines = info.Field.GetCustomAttributes<DescAttribute>(true).SelectMany(d => d.Lines);
var fieldType = FriendlyTypeName(info.Field.FieldType);
var fieldType = Util.FriendlyTypeName(info.Field.FieldType);
var loadInfo = info.Field.GetCustomAttributes<FieldLoader.SerializeAttribute>(true).FirstOrDefault();
var defaultValue = loadInfo != null && loadInfo.Required ? "<em>(required)</em>" : FieldSaver.SaveField(liveTraitInfo, info.Field.Name).Value.Value;
doc.Append("<tr><td>{0}</td><td>{1}</td><td>{2}</td>".F(info.YamlName, defaultValue, fieldType));
@@ -118,58 +117,5 @@ namespace OpenRA.Mods.Common.UtilityCommands
.OrderBy(i => i.Name)
.ToArray();
}
static string FriendlyTypeName(Type t)
{
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(HashSet<>))
return "Set of {0}".F(t.GetGenericArguments().Select(FriendlyTypeName).ToArray());
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dictionary<,>))
return "Dictionary<{0},{1}>".F(t.GetGenericArguments().Select(FriendlyTypeName).ToArray());
if (t.IsSubclassOf(typeof(Array)))
return "Multiple {0}".F(FriendlyTypeName(t.GetElementType()));
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Primitives.Cache<,>))
return "Cached<{0},{1}>".F(t.GetGenericArguments().Select(FriendlyTypeName).ToArray());
if (t == typeof(int) || t == typeof(uint))
return "Integer";
if (t == typeof(int2))
return "2D Integer";
if (t == typeof(float) || t == typeof(decimal))
return "Real Number";
if (t == typeof(float2))
return "2D Real Number";
if (t == typeof(CPos))
return "2D Cell Position";
if (t == typeof(CVec))
return "2D Cell Vector";
if (t == typeof(WAngle))
return "1D World Angle";
if (t == typeof(WRot))
return "3D World Rotation";
if (t == typeof(WPos))
return "3D World Position";
if (t == typeof(WDist))
return "1D World Distance";
if (t == typeof(WVec))
return "3D World Vector";
if (t == typeof(HSLColor))
return "Color";
return t.Name;
}
}
}

View File

@@ -0,0 +1,106 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRA.GameRules;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.UtilityCommands
{
class ExtractWeaponDocsCommand : IUtilityCommand
{
string IUtilityCommand.Name { get { return "--weapon-docs"; } }
bool IUtilityCommand.ValidateArguments(string[] args)
{
return true;
}
[Desc("Generate weaponry documentation in MarkDown format.")]
void IUtilityCommand.Run(Utility utility, string[] args)
{
// HACK: The engine code assumes that Game.modData is set.
Game.ModData = utility.ModData;
Console.WriteLine(
"This documentation is aimed at modders. It displays a template for weapon definitions " +
"as well as its contained types (warheads and projectiles) with default values and developer commentary. " +
"Please do not edit it directly, but add new `[Desc(\"String\")]` tags to the source code. This file has been " +
"automatically generated for version {0} of OpenRA.", utility.ModData.Manifest.Metadata.Version);
Console.WriteLine();
var toc = new StringBuilder();
var doc = new StringBuilder();
var currentNamespace = "";
var objectCreator = utility.ModData.ObjectCreator;
var weaponInfo = objectCreator.GetTypesImplementing<WeaponInfo>();
var warheads = objectCreator.GetTypesImplementing<IWarhead>().OrderBy(t => t.Namespace);
var projectiles = objectCreator.GetTypesImplementing<IProjectileInfo>().OrderBy(t => t.Namespace);
var weaponTypes = Enumerable.Concat(weaponInfo, Enumerable.Concat(projectiles, warheads));
foreach (var t in weaponTypes)
{
// skip helpers like TraitInfo<T>
if (t.ContainsGenericParameters || t.IsAbstract)
continue;
if (currentNamespace != t.Namespace)
{
currentNamespace = t.Namespace;
doc.AppendLine();
doc.AppendLine("## {0}".F(currentNamespace));
toc.AppendLine("* [{0}](#{1})".F(currentNamespace, currentNamespace.Replace(".", "").ToLowerInvariant()));
}
var traitName = t.Name.EndsWith("Info") ? t.Name.Substring(0, t.Name.Length - 4) : t.Name;
toc.AppendLine(" * [{0}](#{1})".F(traitName, traitName.ToLowerInvariant()));
doc.AppendLine();
doc.AppendLine("### {0}".F(traitName));
var traitDescLines = t.GetCustomAttributes<DescAttribute>(false).SelectMany(d => d.Lines);
foreach (var line in traitDescLines)
doc.AppendLine(line);
var infos = FieldLoader.GetTypeLoadInfo(t);
if (!infos.Any())
continue;
doc.AppendLine("<table>");
doc.AppendLine("<tr><th>Property</th><th>Default Value</th><th>Type</th><th>Description</th></tr>");
var liveTraitInfo = t == typeof(WeaponInfo) ? null : objectCreator.CreateBasic(t);
foreach (var info in infos)
{
var fieldDescLines = info.Field.GetCustomAttributes<DescAttribute>(true).SelectMany(d => d.Lines);
var fieldType = Util.FriendlyTypeName(info.Field.FieldType);
var defaultValue = liveTraitInfo == null ? "" : FieldSaver.SaveField(liveTraitInfo, info.Field.Name).Value.Value;
doc.Append("<tr><td>{0}</td><td>{1}</td><td>{2}</td>".F(info.YamlName, defaultValue, fieldType));
doc.Append("<td>");
foreach (var line in fieldDescLines)
doc.Append(line + " ");
doc.AppendLine("</td></tr>");
}
doc.AppendLine("</table>");
}
Console.Write(toc.ToString());
Console.Write(doc.ToString());
}
}
}

View File

@@ -845,6 +845,82 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
}
// TargetWhenIdle and TargetWhenDamaged were removed from AutoTarget
if (engineVersion < 20170722)
{
if (node.Key.StartsWith("AutoTarget", StringComparison.Ordinal))
{
var valueNodes = node.Value.Nodes;
var targetIdle = valueNodes.FirstOrDefault(n => n.Key == "TargetWhenIdle");
var targetDamaged = valueNodes.FirstOrDefault(n => n.Key == "TargetWhenDamaged");
var hasInitialStance = valueNodes.FirstOrDefault(n => n.Key == "InitialStance") != null;
var enableStances = valueNodes.FirstOrDefault(n => n.Key == "EnableStances");
if (targetDamaged == null)
{
if (targetIdle != null)
{
if (hasInitialStance)
Console.WriteLine("'TargetWhenIdle' was removed from 'AutoTarget'. 'InitialStance' might need to be adjusted.");
else
{
valueNodes.Add(new MiniYamlNode("InitialStance", targetIdle.Value.Value.ToLower() == "true" ? "Defend" : "ReturnFire"));
if (enableStances != null)
enableStances.Value.Value = "false";
else
valueNodes.Add(new MiniYamlNode("EnableStances", "false"));
}
valueNodes.Remove(targetIdle);
}
}
else
{
if (targetIdle == null)
{
if (hasInitialStance)
Console.WriteLine("'TargetWhenDamaged' was removed from 'AutoTarget'. 'InitialStance' might need to be adjusted.");
else
{
// In this case the default for "TargetWhenIdle" (true) takes effect, i.e. use the "Defend" stance
valueNodes.Add(new MiniYamlNode("InitialStance", "Defend"));
if (enableStances != null)
enableStances.Value.Value = "false";
else
valueNodes.Add(new MiniYamlNode("EnableStances", "false"));
}
valueNodes.Remove(targetDamaged);
}
else
{
if (hasInitialStance)
Console.WriteLine("'TargetWhenDamaged' and 'TargetWhenIdle' were removed from 'AutoTarget'. 'InitialStance' might need to be adjusted.");
else
{
var idle = targetIdle.Value.Value.ToLower() == "true";
var damaged = targetDamaged.Value.Value.ToLower() == "true";
if (idle)
valueNodes.Add(new MiniYamlNode("InitialStance", "Defend"));
else
valueNodes.Add(new MiniYamlNode("InitialStance", damaged ? "ReturnFire" : "HoldFire"));
if (enableStances != null)
enableStances.Value.Value = "false";
else
valueNodes.Add(new MiniYamlNode("EnableStances", "false"));
}
valueNodes.Remove(targetIdle);
valueNodes.Remove(targetDamaged);
}
}
}
}
UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
}

View File

@@ -28,6 +28,7 @@ namespace OpenRA.Mods.Common.Widgets
}
public bool DisableKeyRepeat = false;
public bool DisableKeySound = false;
[Translate] public string Text = "";
public string Background = "button";
@@ -141,9 +142,10 @@ namespace OpenRA.Mods.Common.Widgets
if (!IsDisabled())
{
OnKeyPress(e);
Game.Sound.PlayNotification(ModRules, null, "Sounds", "ClickSound", null);
if (!DisableKeySound)
Game.Sound.PlayNotification(ModRules, null, "Sounds", "ClickSound", null);
}
else
else if (!DisableKeySound)
Game.Sound.PlayNotification(ModRules, null, "Sounds", "ClickDisabledSound", null);
return true;

View File

@@ -183,7 +183,7 @@ namespace OpenRA.Mods.Common.Widgets
public override bool HandleMouseInput(MouseInput mi)
{
if (mi.Event != MouseInputEvent.Down && mi.Event != MouseInputEvent.Up)
if (mi.Event == MouseInputEvent.Move)
return false;
if (mi.Event == MouseInputEvent.Down)

View File

@@ -165,6 +165,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
InitializeActorPreviews();
};
searchTextField.OnEscKey = () =>
{
searchTextField.Text = "";
searchTextField.YieldKeyboardFocus();
return true;
};
var actorCategorySelector = widget.Get<DropDownButtonWidget>("CATEGORIES_DROPDOWN");
actorCategorySelector.GetText = () =>
{

View File

@@ -73,6 +73,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
InitializeTilePreview();
};
searchTextField.OnEscKey = () =>
{
searchTextField.Text = "";
searchTextField.YieldKeyboardFocus();
return true;
};
Func<string, string> categoryTitle = s => s != null ? s : "Search Results";
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
{

View File

@@ -42,6 +42,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
inputBox.IsDisabled = () => Game.GlobalChat.ConnectionStatus != ChatConnectionStatus.Joined;
inputBox.OnEnterKey = EnterPressed;
// IRC protocol limits messages to 510 characters + CRLF
inputBox.MaxLength = 510;
var nickName = Game.GlobalChat.SanitizedName(Game.Settings.Player.Name);
var nicknameBox = widget.Get<TextFieldWidget>("NICKNAME_TEXTFIELD");
nicknameBox.Text = nickName;
@@ -80,7 +83,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var color = message.Type == ChatMessageType.Notification ? ChromeMetrics.Get<Color>("GlobalChatNotificationColor")
: ChromeMetrics.Get<Color>("GlobalChatPlayerNameColor");
var template = (ContainerWidget)chatTemplate.Clone();
LobbyUtils.SetupChatLine(template, color, from, message.Message);
LobbyUtils.SetupChatLine(template, color, message.Time, from, message.Message);
template.Id = message.UID;
return template;

View File

@@ -10,13 +10,10 @@
#endregion
using System;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Orders;
using OpenRA.Mods.Common.Traits;
using OpenRA.Orders;
using OpenRA.Primitives;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
@@ -55,17 +52,21 @@ namespace OpenRA.Mods.Common.Widgets
attackMoveButton.GetKey = _ => ks.AttackMoveKey;
attackMoveButton.IsDisabled = () => { UpdateStateIfNecessary(); return attackMoveDisabled; };
attackMoveButton.IsHighlighted = () => world.OrderGenerator is GenericSelectTarget
&& ((GenericSelectTarget)world.OrderGenerator).OrderName == "AttackMove";
attackMoveButton.IsHighlighted = () => world.OrderGenerator is AttackMoveOrderGenerator;
attackMoveButton.OnClick = () =>
Action<bool> toggle = allowCancel =>
{
if (attackMoveButton.IsHighlighted())
world.CancelInputMode();
{
if (allowCancel)
world.CancelInputMode();
}
else
world.OrderGenerator = new GenericSelectTarget(selectedActors,
"AttackMove", "attackmove", Game.Settings.Game.MouseButtonPreference.Action);
world.OrderGenerator = new AttackMoveOrderGenerator(selectedActors, Game.Settings.Game.MouseButtonPreference.Action);
};
attackMoveButton.OnClick = () => toggle(true);
attackMoveButton.OnKeyPress = _ => toggle(false);
}
var forceMoveButton = widget.GetOrNull<ButtonWidget>("FORCE_MOVE");
@@ -90,7 +91,9 @@ namespace OpenRA.Mods.Common.Widgets
BindButtonIcon(forceAttackButton);
forceAttackButton.IsDisabled = () => { UpdateStateIfNecessary(); return forceAttackDisabled; };
forceAttackButton.IsHighlighted = () => !forceAttackButton.IsDisabled() && IsForceModifiersActive(Modifiers.Ctrl);
forceAttackButton.IsHighlighted = () => !forceAttackButton.IsDisabled() && IsForceModifiersActive(Modifiers.Ctrl)
&& !(world.OrderGenerator is AttackMoveOrderGenerator);
forceAttackButton.OnClick = () =>
{
if (forceAttackButton.IsHighlighted())
@@ -110,14 +113,20 @@ namespace OpenRA.Mods.Common.Widgets
guardButton.IsHighlighted = () => world.OrderGenerator is GenericSelectTarget
&& ((GenericSelectTarget)world.OrderGenerator).OrderName == "Guard";
guardButton.OnClick = () =>
Action<bool> toggle = allowCancel =>
{
if (guardButton.IsHighlighted())
world.CancelInputMode();
{
if (allowCancel)
world.CancelInputMode();
}
else
world.OrderGenerator = new GuardOrderGenerator(selectedActors,
"Guard", "guard", Game.Settings.Game.MouseButtonPreference.Action);
};
guardButton.OnClick = () => toggle(true);
guardButton.OnKeyPress = _ => toggle(false);
}
var scatterButton = widget.GetOrNull<ButtonWidget>("SCATTER");
@@ -171,6 +180,23 @@ namespace OpenRA.Mods.Common.Widgets
world.OrderGenerator = new ForceModifiersOrderGenerator(Modifiers.Shift, false);
};
}
var keyOverrides = widget.GetOrNull<LogicKeyListenerWidget>("MODIFIER_OVERRIDES");
if (keyOverrides != null)
{
keyOverrides.OnKeyPress = ki =>
{
// HACK: enable attack move to be triggered if the ctrl key is pressed
var modified = new Hotkey(ki.Key, ki.Modifiers & ~Modifiers.Ctrl);
if (!attackMoveDisabled && attackMoveButton.Key == modified)
{
attackMoveButton.OnKeyPress(ki);
return true;
}
return false;
};
}
}
public override void Tick()

View File

@@ -9,6 +9,7 @@
*/
#endregion
using System;
using System.Drawing;
using System.Linq;
using OpenRA.Mods.Common.Commands;
@@ -67,12 +68,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
chatMode.IsDisabled = () => disableTeamChat;
chatText = chatChrome.Get<TextFieldWidget>("CHAT_TEXTFIELD");
chatText.MaxLength = UnitOrders.ChatMessageMaxLength;
chatText.OnEnterKey = () =>
{
var team = teamChat && !disableTeamChat;
if (chatText.Text != "")
{
if (!chatText.Text.StartsWith("/"))
if (!chatText.Text.StartsWith("/", StringComparison.Ordinal))
orderManager.IssueOrder(Order.Chat(team, chatText.Text.Trim()));
else if (chatTraits != null)
{

View File

@@ -127,7 +127,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var keyhandler = shroudSelector.Get<LogicKeyListenerWidget>("SHROUD_KEYHANDLER");
keyhandler.OnKeyPress = HandleKeyPress;
selected = limitViews ? groups.First().Value.First() : disableShroud;
selected = limitViews ? groups.First().Value.First() : world.WorldActor.Owner.Shroud.ExploreMapEnabled ? combined : disableShroud;
selected.OnClick();
}

View File

@@ -51,6 +51,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
widget.IsVisible = () => (orderManager.LobbyInfo.ClientWithIndex(clientIndex) != null);
tooltipContainer.BeforeRender = () =>
{
if (!widget.IsVisible())
return;
var latencyPrefixSize = latencyPrefix == null ? 0 : latencyPrefix.Bounds.X + latencyPrefixFont.Measure(latencyPrefix.GetText() + " ").X;
var locationWidth = locationFont.Measure(location.GetText()).X;
var adminWidth = adminFont.Measure(admin.GetText()).X;

View File

@@ -469,6 +469,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
chatLabel = lobby.Get<LabelWidget>("LABEL_CHATTYPE");
var chatTextField = lobby.Get<TextFieldWidget>("CHAT_TEXTFIELD");
chatTextField.MaxLength = UnitOrders.ChatMessageMaxLength;
chatTextField.TakeKeyboardFocus();
chatTextField.OnEnterKey = () =>
{
@@ -567,7 +569,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
lobbyChatUnreadMessages += 1;
var template = (ContainerWidget)chatTemplate.Clone();
LobbyUtils.SetupChatLine(template, c, from, text);
LobbyUtils.SetupChatLine(template, c, DateTime.Now, from, text);
var scrolledToBottom = lobbyChatPanel.ScrolledToBottom;
lobbyChatPanel.AddChild(template);

View File

@@ -542,7 +542,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return address;
}
public static void SetupChatLine(ContainerWidget template, Color c, string from, string text)
public static void SetupChatLine(ContainerWidget template, Color c, DateTime time, string from, string text)
{
var nameLabel = template.Get<LabelWidget>("NAME");
var timeLabel = template.Get<LabelWidget>("TIME");
@@ -552,7 +552,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var font = Game.Renderer.Fonts[nameLabel.Font];
var nameSize = font.Measure(from);
var time = DateTime.Now;
timeLabel.GetText = () => "{0:D2}:{1:D2}".F(time.Hour, time.Minute);
nameLabel.GetColor = () => c;

View File

@@ -13,6 +13,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
@@ -37,7 +38,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
static bool fetchedNews;
// Increment the version number when adding new stats
const int SystemInformationVersion = 2;
const int SystemInformationVersion = 3;
Dictionary<string, Pair<string, string>> GetSystemInformation()
{
var lang = System.Globalization.CultureInfo.InstalledUICulture.TwoLetterISOLanguageName;
@@ -47,10 +48,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ "platform", Pair.New("OS Type", Platform.CurrentPlatform.ToString()) },
{ "os", Pair.New("OS Version", Environment.OSVersion.ToString()) },
{ "x64", Pair.New("OS is 64 bit", Environment.Is64BitOperatingSystem.ToString()) },
{ "x64process", Pair.New("Process is 64 bit", Environment.Is64BitProcess.ToString()) },
{ "runtime", Pair.New(".NET Runtime", Platform.RuntimeVersion) },
{ "gl", Pair.New("OpenGL Version", Game.Renderer.GLVersion) },
{ "windowsize", Pair.New("Window Size", "{0}x{1}".F(Game.Renderer.Resolution.Width, Game.Renderer.Resolution.Height)) },
{ "windowscale", Pair.New("Window Scale", Game.Renderer.WindowScale.ToString("F2")) },
{ "windowscale", Pair.New("Window Scale", Game.Renderer.WindowScale.ToString("F2", CultureInfo.InvariantCulture)) },
{ "lang", Pair.New("System Language", lang) }
};
}

View File

@@ -200,7 +200,7 @@ namespace OpenRA.Mods.Common.Widgets
if (item != null && item.Done && actor.HasTraitInfo<BuildingInfo>())
{
World.OrderGenerator = new PlaceBuildingOrderGenerator(CurrentQueue, icon.Name);
World.OrderGenerator = new PlaceBuildingOrderGenerator(CurrentQueue, icon.Name, worldRenderer);
return true;
}

View File

@@ -35,8 +35,6 @@ namespace OpenRA.Mods.Common.Widgets
public FrozenActor FrozenActorTooltip { get; private set; }
public ResourceType ResourceTooltip { get; private set; }
public int EdgeScrollThreshold = 5;
int2? joystickScrollStart, joystickScrollEnd;
int2? standardScrollStart;
bool isStandardScrolling;
@@ -308,7 +306,12 @@ namespace OpenRA.Mods.Common.Widgets
if (scrollType == MouseScrollType.Standard || scrollType == MouseScrollType.Inverted)
{
if (mi.Event == MouseInputEvent.Down && !isStandardScrolling)
{
if (!TakeMouseFocus(mi))
return false;
standardScrollStart = mi.Location;
}
else if (mi.Event == MouseInputEvent.Move && (isStandardScrolling ||
(standardScrollStart.HasValue && ((standardScrollStart.Value - mi.Location).Length > Game.Settings.Game.MouseScrollDeadzone))))
{
@@ -322,6 +325,7 @@ namespace OpenRA.Mods.Common.Widgets
var wasStandardScrolling = isStandardScrolling;
isStandardScrolling = false;
standardScrollStart = null;
YieldMouseFocus(mi);
if (wasStandardScrolling)
return true;
@@ -335,6 +339,7 @@ namespace OpenRA.Mods.Common.Widgets
{
if (!TakeMouseFocus(mi))
return false;
joystickScrollStart = mi.Location;
}
@@ -474,14 +479,15 @@ namespace OpenRA.Mods.Common.Widgets
ScrollDirection CheckForDirections()
{
var margin = Game.Settings.Game.ViewportEdgeScrollMargin;
var directions = ScrollDirection.None;
if (Viewport.LastMousePos.X < EdgeScrollThreshold)
if (Viewport.LastMousePos.X < margin)
directions |= ScrollDirection.Left;
if (Viewport.LastMousePos.Y < EdgeScrollThreshold)
if (Viewport.LastMousePos.Y < margin)
directions |= ScrollDirection.Up;
if (Viewport.LastMousePos.X >= Game.Renderer.Resolution.Width - EdgeScrollThreshold)
if (Viewport.LastMousePos.X >= Game.Renderer.Resolution.Width - margin)
directions |= ScrollDirection.Right;
if (Viewport.LastMousePos.Y >= Game.Renderer.Resolution.Height - EdgeScrollThreshold)
if (Viewport.LastMousePos.Y >= Game.Renderer.Resolution.Height - margin)
directions |= ScrollDirection.Down;
return directions;

View File

@@ -32,8 +32,11 @@ namespace OpenRA.Mods.Common
// Then we iterate over this list, and find all actors for which their health radius is at least within lineWidth of the line.
// For actors without a health radius, we simply check their center point.
// The square in which we select all actors must be large enough to encompass the entire line's width.
var xDir = Math.Sign(lineEnd.X - lineStart.X);
var yDir = Math.Sign(lineEnd.Y - lineStart.Y);
// xDir and yDir must never be 0, otherwise the overscan will be 0 in the respective direction.
var xDiff = lineEnd.X - lineStart.X;
var yDiff = lineEnd.Y - lineStart.Y;
var xDir = xDiff < 0 ? -1 : 1;
var yDir = yDiff < 0 ? -1 : 1;
var dir = new WVec(xDir, yDir, 0);
var overselect = dir * (1024 + lineWidth.Length + targetExtraSearchRadius.Length);

View File

@@ -81,7 +81,7 @@ namespace OpenRA.Platforms.Default
}
// A null indicates termination of that string, so add that to our list.
devices.Add(Encoding.Default.GetString(buffer.ToArray()));
devices.Add(Encoding.UTF8.GetString(buffer.ToArray()));
buffer.Clear();
// Two successive nulls indicates the end of the list.
@@ -266,7 +266,7 @@ namespace OpenRA.Platforms.Default
slot.FrameStarted = currFrame;
slot.IsRelative = relative;
slot.SoundSource = null;
slot.Sound = new OpenAlStreamingSound(source, loop, relative, pos, volume, channels, sampleBits, sampleRate, stream);
slot.Sound = new OpenAlAsyncLoadSound(source, loop, relative, pos, volume, channels, sampleBits, sampleRate, stream);
return slot.Sound;
}
@@ -282,25 +282,33 @@ namespace OpenRA.Platforms.Default
return;
var source = ((OpenAlSound)sound).Source;
int state;
AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE, out state);
if (state == AL10.AL_PLAYING && paused)
AL10.alSourcePause(source);
else if (state == AL10.AL_PAUSED && !paused)
AL10.alSourcePlay(source);
PauseSound(source, paused);
}
public void SetAllSoundsPaused(bool paused)
{
foreach (var source in sourcePool.Keys)
PauseSound(source, paused);
}
void PauseSound(uint source, bool paused)
{
int state;
AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE, out state);
if (paused)
{
int state;
AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE, out state);
if (state == AL10.AL_PLAYING && paused)
if (state == AL10.AL_PLAYING)
AL10.alSourcePause(source);
else if (state == AL10.AL_PAUSED && !paused)
else if (state == AL10.AL_INITIAL)
{
// If a sound hasn't started yet,
// we indicate it should not play be transitioning it to the stopped state.
AL10.alSourcePlay(source);
AL10.alSourceStop(source);
}
}
else if (!paused && state != AL10.AL_PLAYING)
AL10.alSourcePlay(source);
}
public void SetSoundVolume(float volume, ISound music, ISound video)
@@ -483,149 +491,99 @@ namespace OpenRA.Platforms.Default
}
}
class OpenAlStreamingSound : OpenAlSound
class OpenAlAsyncLoadSound : OpenAlSound
{
const int BufferCount = 3;
const int BufferSizeInSecs = 1;
readonly object bufferDequeueLock = new object();
static readonly byte[] SilentData = new byte[2];
readonly CancellationTokenSource cts = new CancellationTokenSource();
readonly Task streamTask;
readonly Stack<uint> freeBuffers = new Stack<uint>(BufferCount);
int totalSamplesPlayed;
readonly Task playTask;
public OpenAlStreamingSound(uint source, bool looping, bool relative, WPos pos, float volume, int channels, int sampleBits, int sampleRate, Stream stream)
public OpenAlAsyncLoadSound(uint source, bool looping, bool relative, WPos pos, float volume, int channels, int sampleBits, int sampleRate, Stream stream)
: base(source, looping, relative, pos, volume, sampleRate)
{
streamTask = Task.Run(async () =>
// Load a silent buffer into the source. Without this,
// attempting to change the state (i.e. play/pause) the source fails on some systems.
var silentSource = new OpenAlSoundSource(SilentData, channels, sampleBits, sampleRate);
AL10.alSourcei(source, AL10.AL_BUFFER, (int)silentSource.Buffer);
playTask = Task.Run(async () =>
{
var format = OpenAlSoundEngine.MakeALFormat(channels, sampleBits);
var bytesPerSample = sampleBits / 8;
var buffers = new uint[BufferCount];
AL10.alGenBuffers(new IntPtr(buffers.Length), buffers);
try
MemoryStream memoryStream;
using (stream)
{
foreach (var buffer in buffers)
freeBuffers.Push(buffer);
var data = new byte[sampleRate * bytesPerSample * BufferSizeInSecs];
var streamEnd = false;
while (!streamEnd && !cts.IsCancellationRequested)
memoryStream = new MemoryStream();
try
{
// Fill the data array as fully as possible.
var count = await ReadFillingBuffer(stream, data);
streamEnd = count < data.Length;
await stream.CopyToAsync(memoryStream, 81920, cts.Token);
}
catch (TaskCanceledException)
{
// Sound was stopped early, cleanup the unused buffer and exit.
AL10.alSourceStop(source);
AL10.alSourcei(source, AL10.AL_BUFFER, 0);
silentSource.Dispose();
return;
}
}
// Fill a buffer and queue it for playback.
var nextBuffer = freeBuffers.Pop();
AL10.alBufferData(nextBuffer, format, data, new IntPtr(count), new IntPtr(sampleRate));
AL10.alSourceQueueBuffers(source, new IntPtr(1), ref nextBuffer);
var data = memoryStream.ToArray();
var bytesPerSample = sampleBits / 8f;
var lengthInSecs = data.Length / (channels * bytesPerSample * sampleRate);
using (var soundSource = new OpenAlSoundSource(data, channels, sampleBits, sampleRate))
{
// Need to stop the source, before attaching the real input and deleting the silent one.
AL10.alSourceStop(source);
AL10.alSourcei(source, AL10.AL_BUFFER, (int)soundSource.Buffer);
silentSource.Dispose();
lock (cts)
lock (cts)
{
if (!cts.IsCancellationRequested)
{
if (!cts.IsCancellationRequested)
// TODO: A race condition can happen between the state check and playing/rewinding if a
// user pauses/resumes at the right moment. The window of opportunity is small and the
// consequences are minor, so for now we'll ignore it.
int state;
AL10.alGetSourcei(Source, AL10.AL_SOURCE_STATE, out state);
if (state != AL10.AL_STOPPED)
AL10.alSourcePlay(source);
else
{
// Once we have at least one buffer filled, we can actually start.
// If streaming fell behind, the source will have stopped,
// we also need to play it in this case to resume audio.
int state;
AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE, out state);
if (state == AL10.AL_INITIAL || state == AL10.AL_STOPPED)
{
// If we resume playback from the stopped state, it resets to the beginning!
// To avoid replaying the same audio, we need to dequeue the processed buffers first.
if (state == AL10.AL_STOPPED)
DequeueBuffers();
AL10.alSourcePlay(source);
}
// A stopped sound indicates it was paused before we finishing loaded.
// We don't want to start playing it right away.
// We rewind the source so when it is started, it plays from the beginning.
AL10.alSourceRewind(source);
}
}
// Try and dequeue buffers as they become available to be reused.
// When the stream ends, wait for all the buffers to be processed
while (freeBuffers.Count < (streamEnd ? buffers.Length : 1))
{
await Task.Delay(TimeSpan.FromSeconds(BufferSizeInSecs), cts.Token).ConfigureAwait(false);
DequeueBuffers();
}
}
}
catch (TaskCanceledException)
{
// Streaming has been cancelled, we'll need to perform some cleanup.
}
finally
{
// If we never actually started the source, we need to start it and then stop it.
// Otherwise it is left in the initial state and never returned to the source pool.
int state;
AL10.alGetSourcei(Source, AL10.AL_SOURCE_STATE, out state);
if (state == AL10.AL_INITIAL)
AL10.alSourcePlay(Source);
// Ensure the source is stopped, which will mark all buffers as processed.
// Dequeue these, so they can then be freed.
AL10.alSourceStop(Source);
lock (bufferDequeueLock)
while (!cts.IsCancellationRequested)
{
int buffersProcessed;
AL10.alGetSourcei(source, AL10.AL_BUFFERS_PROCESSED, out buffersProcessed);
for (var i = 0; i < buffersProcessed; i++)
// Need to check seek before state. Otherwise, the music can stop after our state check at
// which point the seek will be zero, meaning we'll wait the full track length before seeing it
// has stopped.
var currentSeek = SeekPosition;
int state;
AL10.alGetSourcei(Source, AL10.AL_SOURCE_STATE, out state);
if (state == AL10.AL_STOPPED)
break;
try
{
var dequeued = 0U;
AL10.alSourceUnqueueBuffers(source, new IntPtr(1), ref dequeued);
// Wait until the track is due to complete, and at most 60 times a second to prevent a
// busy-wait.
var delaySecs = Math.Max(lengthInSecs - currentSeek, 1 / 60f);
await Task.Delay(TimeSpan.FromSeconds(delaySecs), cts.Token);
}
catch (TaskCanceledException)
{
// Sound was stopped early, allow normal cleanup to occur.
}
}
AL10.alDeleteBuffers(new IntPtr(buffers.Length), buffers);
stream.Dispose();
AL10.alSourcei(Source, AL10.AL_BUFFER, 0);
}
}).ContinueWith(task =>
{
Game.RunAfterTick(() =>
{
throw new Exception("Failed to stream a sound.", task.Exception);
});
}, TaskContinuationOptions.OnlyOnFaulted);
}
async Task<int> ReadFillingBuffer(Stream stream, byte[] buffer)
{
var offset = 0;
int count;
while (offset < buffer.Length &&
(count = await stream.ReadAsync(buffer, offset, buffer.Length - offset, cts.Token).ConfigureAwait(false)) > 0)
offset += count;
return offset;
}
void DequeueBuffers()
{
lock (bufferDequeueLock)
{
// Check for any processed buffers, and dequeue them for reuse.
int buffersProcessed;
AL10.alGetSourcei(Source, AL10.AL_BUFFERS_PROCESSED, out buffersProcessed);
for (var i = 0; i < buffersProcessed; i++)
{
// Dequeue a processed buffer, so we can reuse it.
var dequeued = 0U;
AL10.alSourceUnqueueBuffers(Source, new IntPtr(1), ref dequeued);
freeBuffers.Push(dequeued);
// When we remove a buffer, we need to account for this to calculate the overall seek position.
// The final buffer in the track may be shorter then BufferSizeInSecs, so we'll need to check how
// many bytes are actually in each buffer to avoid adding too much at the end.
int byteSize;
AL10.alGetBufferi(dequeued, AL10.AL_SIZE, out byteSize);
int bitRate;
AL10.alGetBufferi(dequeued, AL10.AL_BITS, out bitRate);
totalSamplesPlayed += byteSize / (bitRate / 8);
}
}
});
}
public override void Stop()
@@ -638,41 +596,16 @@ namespace OpenRA.Platforms.Default
try
{
streamTask.Wait();
playTask.Wait();
}
catch (AggregateException)
{
}
}
public override float SeekPosition
{
get
{
// Stop buffers being dequeued whilst we calculate the seek position.
lock (bufferDequeueLock)
{
int sampleOffset;
AL10.alGetSourcei(Source, AL11.AL_SAMPLE_OFFSET, out sampleOffset);
int state;
AL10.alGetSourcei(Source, AL10.AL_SOURCE_STATE, out state);
// If the source is not stopped, add the current offset to the total offset and return that.
if (state != AL10.AL_STOPPED)
return (sampleOffset + totalSamplesPlayed) / SampleRate;
// If the source stopped, the current offset will have been reset to 0.
// We'll need to dequeue any buffers played first and then return the total.
DequeueBuffers();
return totalSamplesPlayed / SampleRate;
}
}
}
public override bool Complete
{
get { return streamTask.IsCompleted; }
get { return playTask.IsCompleted; }
}
}
}

View File

@@ -193,6 +193,7 @@ function Docs-Command
{
./make.ps1 version
./OpenRA.Utility.exe all --docs | Out-File -Encoding "UTF8" DOCUMENTATION.md
./OpenRA.Utility.exe all --weapon-docs | Out-File -Encoding "UTF8" WEAPONS.md
./OpenRA.Utility.exe all --lua-docs | Out-File -Encoding "UTF8" Lua-API.md
}
else

Binary file not shown.

View File

@@ -264,16 +264,18 @@ Container@PLAYER_WIDGETS:
Width: 276
Height: 36
Children:
LogicKeyListener@MODIFIER_OVERRIDES:
Button@ATTACK_MOVE:
X: 1
Y: 1
Width: 34
Height: 34
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Attack Move
TooltipDesc: Selected units will move to the desired location\nand attack any enemies they encounter en route.\n\nLeft-click icon then right-click on target location.
TooltipDesc: Selected units will move to the desired location\nand attack any enemies they encounter en route.\n\nHold {(Ctrl)} while targeting to order an Assault Move\nthat attacks any units or structures encountered en route.\n\nLeft-click icon then right-click on target location.
TooltipContainer: TOOLTIP_CONTAINER
TooltipTemplate: BUTTON_WITH_DESC_HIGHLIGHT_TOOLTIP
Children:
Image@ICON:
X: 5
@@ -286,6 +288,7 @@ Container@PLAYER_WIDGETS:
Width: 34
Height: 34
Background: command-button
DisableKeySound: true
TooltipText: Force Move
TooltipDesc: Selected units will move to the desired location\n - Default activity for the target is suppressed\n - Vehicles will attempt to crush enemies at the target location\n - Units entering transports will consider nearby alternatives\n\nLeft-click icon then right-click on target.\nHold {(Alt)} to activate temporarily while commanding units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -302,6 +305,7 @@ Container@PLAYER_WIDGETS:
Width: 34
Height: 34
Background: command-button
DisableKeySound: true
TooltipText: Force Attack
TooltipDesc: Selected units will attack the targeted unit or location\nignoring their default activity for the target.\n\nLeft-click icon then right-click on target.\nHold {(Ctrl)} to activate temporarily while commanding units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -318,7 +322,7 @@ Container@PLAYER_WIDGETS:
Width: 34
Height: 34
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Guard
TooltipDesc: Selected units will follow the targeted unit.\n\nLeft-click icon then right-click on target unit.
TooltipContainer: TOOLTIP_CONTAINER
@@ -335,6 +339,7 @@ Container@PLAYER_WIDGETS:
Height: 34
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Deploy
TooltipDesc: Selected units will perform their default deploy activity\n - MCVs will unpack into a Construction Yard\n - Construction Yards will re-pack into a MCV\n - Transports will unload their passengers\n - Helicopters will return to base\n\nActs immediately on selected units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -351,6 +356,7 @@ Container@PLAYER_WIDGETS:
Height: 34
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Scatter
TooltipDesc: Selected units will stop their current activity\nand move to a nearby location.\n\nActs immediately on selected units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -367,6 +373,7 @@ Container@PLAYER_WIDGETS:
Height: 34
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Stop
TooltipDesc: Selected units will stop their current activity.\n\nActs immediately on selected units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -382,6 +389,7 @@ Container@PLAYER_WIDGETS:
Width: 34
Height: 34
Background: command-button
DisableKeySound: true
TooltipText: Waypoint Mode
TooltipDesc: Use Waypoint Mode to give multiple linking commands\nto the selected units. Units will execute the commands\nimmediately upon receiving them.\n\nLeft-click icon then give commands in the game world.\nHold {(Shift)} to activate temporarily while commanding units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -407,6 +415,7 @@ Container@PLAYER_WIDGETS:
Height: 26
Background: stance-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Attack Anything Stance
TooltipDesc: Set the selected units to Attack Anything stance:\n - Units will attack enemy units and structures on sight\n - Units will pursue attackers across the battlefield
TooltipContainer: TOOLTIP_CONTAINER
@@ -423,6 +432,7 @@ Container@PLAYER_WIDGETS:
Height: 26
Background: stance-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Defend Stance
TooltipDesc: Set the selected units to Defend stance:\n - Units will attack enemy units on sight\n - Units will not move or pursue enemies
TooltipContainer: TOOLTIP_CONTAINER
@@ -439,6 +449,7 @@ Container@PLAYER_WIDGETS:
Height: 26
Background: stance-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Return Fire Stance
TooltipDesc: Set the selected units to Return Fire stance:\n - Units will retaliate against enemies that attack them\n - Units will not move or pursue enemies
TooltipContainer: TOOLTIP_CONTAINER
@@ -455,6 +466,7 @@ Container@PLAYER_WIDGETS:
Height: 26
Background: stance-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Hold Fire Stance
TooltipDesc: Set the selected units to Hold Fire stance:\n - Units will not fire upon enemies\n - Units will not move or pursue enemies
TooltipContainer: TOOLTIP_CONTAINER

View File

@@ -155,23 +155,41 @@ Cursors:
move:
Start: 0
Length: 8
move-minimap:
Start: 8
Length: 4
move-rough:
Start: 0
Length: 8
move-minimap:
Start: 8
Length: 4
attackmove:
Start: 12
Length: 8
attackmove-minimap:
Start: 20
Length: 4
move-blocked:
assaultmove:
Start: 24
Length: 8
assaultmove-minimap:
Start: 32
Length: 4
move-blocked:
Start: 36
Length: 1
attackmove-blocked:
Start: 37
Length: 1
assaultmove-blocked:
Start: 38
Length: 1
move-blocked-minimap:
Start: 25
Start: 39
Length: 1
attackmove-blocked-minimap:
Start: 39
Length: 1
assaultmove-blocked-minimap:
Start: 40
Length: 1
mouse5.shp: cursor
guard:

View File

@@ -74,8 +74,8 @@ RMBO:
MustBeDestroyed:
RequiredForShortGame: true
AutoTarget:
TargetWhenIdle: false
TargetWhenDamaged: true
EnableStances: false
InitialStance: ReturnFire
Health:
HP: 150

View File

@@ -155,3 +155,4 @@ ORCA.IN:
-AutoTarget:
-AutoTargetPriority@DEFAULT:
-AutoTargetPriority@ATTACKANYTHING:
-AttackMove:

View File

@@ -37,6 +37,7 @@ UNITCRATE:
Units: e3
APC:
Inherits@AUTOTARGET: ^AutoTargetAll
Health:
HP: 1000
MustBeDestroyed:

View File

@@ -54,7 +54,7 @@ TRAN:
HELI:
Inherits: ^Helicopter
Inherits@EXPERIENCE: ^GainsExperience
Inherits@AUTOTARGET: ^AutoTargetAll
Inherits@AUTOTARGET: ^AutoTargetAllAssaultMove
Valued:
Cost: 1200
Tooltip:
@@ -113,7 +113,7 @@ HELI:
ORCA:
Inherits: ^Helicopter
Inherits@EXPERIENCE: ^GainsExperience
Inherits@AUTOTARGET: ^AutoTargetAll
Inherits@AUTOTARGET: ^AutoTargetAllAssaultMove
Valued:
Cost: 1200
Tooltip:

View File

@@ -50,6 +50,6 @@ airstrike.proxy:
ClockSequence: clock
CircleSequence: circles
TDTRUK:
TRUCK:
Buildable:
Prerequisites: ~disabled

View File

@@ -159,6 +159,15 @@
ValidTargets: Infantry, Vehicle, Creep, Water, Structure, Defense
InvalidTargets: NoAutoTarget
^AutoTargetGroundAssaultMove:
Inherits: ^AutoTargetGround
AutoTargetPriority@DEFAULT:
RequiresCondition: !stance-attackanything && !assault-move
AutoTargetPriority@ATTACKANYTHING:
RequiresCondition: stance-attackanything || assault-move
AttackMove:
AssaultMoveScanCondition: assault-move
^AutoTargetAir:
AutoTarget:
AutoTargetPriority@DEFAULT:
@@ -177,6 +186,15 @@
ValidTargets: Infantry, Vehicle, Creep, Water, Air, Structure, Defense
InvalidTargets: NoAutoTarget
^AutoTargetAllAssaultMove:
Inherits: ^AutoTargetAll
AutoTargetPriority@DEFAULT:
RequiresCondition: !stance-attackanything && !assault-move
AutoTargetPriority@ATTACKANYTHING:
RequiresCondition: stance-attackanything || assault-move
AttackMove:
AssaultMoveScanCondition: assault-move
^AcceptsCloakCrate:
Cloak:
InitialDelay: 15
@@ -455,7 +473,7 @@
^DINO:
Inherits@1: ^ExistsInWorld
Inherits@2: ^SpriteActor
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Huntable:
Health:
HP: 1000
@@ -516,7 +534,7 @@
^Viceroid:
Inherits@1: ^ExistsInWorld
Inherits@2: ^SpriteActor
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Huntable:
Health:
HP: 300
@@ -966,7 +984,9 @@
RequiresForceFire: yes
TargetTypes: Ground, Water
Health:
HP: 500
HP: 600
Armor:
Type: Heavy
SoundOnDamageTransition:
DamagedSounds: xplos.aud
DestroyedSounds: xplobig4.aud

View File

@@ -133,3 +133,12 @@ STNK.Husk:
IntoActor: stnk
RenderSprites:
Image: stnk.destroyed
TRUCK.Husk:
Inherits: ^Husk
Tooltip:
Name: Supply Truck (Destroyed)
TransformOnCapture:
IntoActor: truck
RenderSprites:
Image: truck.destroyed

View File

@@ -1,13 +1,14 @@
E1:
Inherits: ^Soldier
Inherits@EXPERIENCE: ^GainsExperience
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 100
Tooltip:
Name: Minigunner
Buildable:
BuildPaletteOrder: 10
Prerequisites: barracks
Queue: Infantry.GDI, Infantry.Nod
Description: General-purpose infantry.\n Strong vs Infantry\n Weak vs Vehicles
Mobile:
@@ -26,7 +27,7 @@ E1:
E2:
Inherits: ^Soldier
Inherits@EXPERIENCE: ^GainsExperience
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 160
Tooltip:
@@ -59,13 +60,14 @@ E2:
E3:
Inherits: ^Soldier
Inherits@EXPERIENCE: ^GainsExperience
Inherits@AUTOTARGET: ^AutoTargetAll
Inherits@AUTOTARGET: ^AutoTargetAllAssaultMove
Valued:
Cost: 300
Tooltip:
Name: Rocket Soldier
Buildable:
BuildPaletteOrder: 20
Prerequisites: barracks
Queue: Infantry.GDI, Infantry.Nod
Description: Anti-tank/Anti-aircraft infantry. \n Strong vs Tanks, Aircraft\n Weak vs Infantry
Mobile:
@@ -87,7 +89,7 @@ E3:
E4:
Inherits: ^Soldier
Inherits@EXPERIENCE: ^GainsExperience
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 200
Tooltip:
@@ -118,7 +120,7 @@ E4:
E5:
Inherits: ^Soldier
Inherits@EXPERIENCE: ^GainsExperience
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 300
Tooltip:
@@ -160,10 +162,11 @@ E6:
Name: Engineer
Buildable:
BuildPaletteOrder: 30
Prerequisites: barracks
Queue: Infantry.GDI, Infantry.Nod
Description: Damages and captures enemy structures.\n Unarmed
Mobile:
Speed: 56
Speed: 48
Health:
HP: 30
Passenger:
@@ -177,7 +180,7 @@ E6:
RMBO:
Inherits: ^Soldier
Inherits@EXPERIENCE: ^GainsExperience
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 2000
Tooltip:

View File

@@ -776,6 +776,10 @@ SAM:
Inherits@IDISABLE: ^DisabledOverlay
Inherits@AUTOTARGET: ^AutoTargetAir
Inherits@shape: ^2x1Shape
HitShape:
Type: Rectangle
TopLeft: -768,-512
BottomRight: 768,512
Valued:
Cost: 650
Tooltip:
@@ -928,7 +932,7 @@ ATWR:
TurnSpeed: 255
Offset: 128,128,384
Armament@PRIMARY:
Weapon: TowerMissle
Weapon: TowerMissile
LocalOffset: 256,128,0, 256,-128,0
LocalYaw: -100,100
Armament@SECONDARY:

View File

@@ -82,7 +82,7 @@ APC:
Inherits: ^Tank
Inherits@EXPERIENCE: ^GainsExperience
Inherits@CLOAK: ^AcceptsCloakCrate
Inherits@AUTOTARGET: ^AutoTargetAll
Inherits@AUTOTARGET: ^AutoTargetAllAssaultMove
Valued:
Cost: 550
Tooltip:
@@ -132,7 +132,7 @@ ARTY:
Inherits: ^Tank
Inherits@EXPERIENCE: ^GainsExperience
Inherits@CLOAK: ^AcceptsCloakCrate
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 600
Tooltip:
@@ -170,7 +170,7 @@ FTNK:
Inherits: ^Tank
Inherits@EXPERIENCE: ^GainsExperience
Inherits@CLOAK: ^AcceptsCloakCrate
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 600
Tooltip:
@@ -205,7 +205,7 @@ BGGY:
Inherits: ^Vehicle
Inherits@@EXPERIENCE: ^GainsExperience
Inherits@CLOAK: ^AcceptsCloakCrate
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 300
Tooltip:
@@ -241,7 +241,7 @@ BIKE:
Inherits: ^Vehicle
Inherits@EXPERIENCE: ^GainsExperience
Inherits@CLOAK: ^AcceptsCloakCrate
Inherits@AUTOTARGET: ^AutoTargetAll
Inherits@AUTOTARGET: ^AutoTargetAllAssaultMove
Valued:
Cost: 500
Tooltip:
@@ -279,7 +279,7 @@ JEEP:
Inherits: ^Vehicle
Inherits@EXPERIENCE: ^GainsExperience
Inherits@CLOAK: ^AcceptsCloakCrate
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 400
Tooltip:
@@ -315,7 +315,7 @@ LTNK:
Inherits: ^Tank
Inherits@EXPERIENCE: ^GainsExperience
Inherits@CLOAK: ^AcceptsCloakCrate
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 650
Tooltip:
@@ -354,7 +354,7 @@ MTNK:
Inherits: ^Tank
Inherits@EXPERIENCE: ^GainsExperience
Inherits@CLOAK: ^AcceptsCloakCrate
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 800
Tooltip:
@@ -392,7 +392,7 @@ HTNK:
Inherits: ^Tank
Inherits@EXPERIENCE: ^GainsExperience
Inherits@CLOAK: ^AcceptsCloakCrate
Inherits@AUTOTARGET: ^AutoTargetAll
Inherits@AUTOTARGET: ^AutoTargetAllAssaultMove
Valued:
Cost: 1500
Tooltip:
@@ -443,7 +443,7 @@ MSAM:
Inherits: ^Tank
Inherits@EXPERIENCE: ^GainsExperience
Inherits@CLOAK: ^AcceptsCloakCrate
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 1000
Tooltip:
@@ -525,7 +525,7 @@ MLRS:
STNK:
Inherits: ^Vehicle
Inherits@EXPERIENCE: ^GainsExperience
Inherits@AUTOTARGET: ^AutoTargetAll
Inherits@AUTOTARGET: ^AutoTargetAllAssaultMove
Valued:
Cost: 900
Tooltip:
@@ -586,12 +586,12 @@ MHQ:
Buildable:
Description: Mobile base of operations
TDTRUK:
TRUCK:
Inherits: ^Vehicle
Buildable:
Queue: Vehicle.GDI, Vehicle.Nod
BuildPaletteOrder: 35
Prerequisites: ~techlevel.low
Prerequisites: vehicleproduction
Description: Transports cash to other players.\n Unarmed
Valued:
Cost: 500
@@ -610,3 +610,5 @@ TDTRUK:
DeliversCash:
Payload: 500
PlayerExperience: 50
SpawnActorOnDeath:
Actor: TRUCK.Husk

View File

@@ -337,8 +337,14 @@ apc.destroyed:
Facings: 32
ZOffset: -512
tdtruk:
truck:
idle:
Facings: 32
UseClassicFacingFudge: True
icon: tdtrukicon
icon: truckicon
truck.destroyed:
idle: truck
Facings: 32
UseClassicFacingFudge: True
ZOffset: -512

View File

@@ -39,6 +39,8 @@ FlametankExplode:
HeliCrash:
Inherits: ^DamagingExplosion
Warhead@1Dam: SpreadDamage
Damage: 100
HeliExplode:
Warhead@1Dam: SpreadDamage

View File

@@ -20,7 +20,7 @@
Damage: 35
ValidTargets: Ground, Air
Versus:
None: 30
None: 20
Wood: 85
Light: 100
Heavy: 100
@@ -89,7 +89,7 @@ OrcaAGMissiles:
Damage: 28
ValidTargets: Ground
Versus:
None: 50
None: 30
Wood: 100
Light: 100
Heavy: 75
@@ -215,7 +215,7 @@ BoatMissile:
VictimScanRadius: 0
ValidTargets: Air
TowerMissle:
TowerMissile:
Inherits: ^MissileWeapon
ReloadDelay: 15
Range: 7c0

BIN
mods/d2k/bits/assaultmove.shp Executable file

Binary file not shown.

BIN
mods/d2k/bits/attackmove.shp Executable file

Binary file not shown.

View File

@@ -2,7 +2,6 @@ sidebar: chrome.png
background-top: 0,0,226,295
background-iconrow: 0,295,226,48
background-bottom: 0,343,226,13
background-supportoverlay: 236,212,60,48
sidebar-button: chrome.png
background: 377,0,25,25

View File

@@ -14,16 +14,6 @@ Container@PLAYER_WIDGETS:
TooltipContainer: TOOLTIP_CONTAINER
ReadyText: READY
HoldText: ON HOLD
Container@PALETTE_FOREGROUND:
Children:
Image@ICON_TEMPLATE:
X: 0 - 2
Y: 0 - 2
Width: 60
Height: 48
IgnoreMouseOver: true
ImageCollection: sidebar
ImageName: background-supportoverlay
Image@COMMAND_BAR_BACKGROUND:
X: 0
Y: WINDOW_BOTTOM - HEIGHT
@@ -38,15 +28,17 @@ Container@PLAYER_WIDGETS:
Width: 275
Height: 41
Children:
LogicKeyListener@MODIFIER_OVERRIDES:
Button@ATTACK_MOVE:
Width: 34
Height: 41
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Attack Move
TooltipDesc: Selected units will move to the desired location\nand attack any enemies they encounter en route.\n\nLeft-click icon then right-click on target location.
TooltipDesc: Selected units will move to the desired location\nand attack any enemies they encounter en route.\n\nHold {(Ctrl)} while targeting to order an Assault Move\nthat attacks any units or structures encountered en route.\n\nLeft-click icon then right-click on target location.
TooltipContainer: TOOLTIP_CONTAINER
TooltipTemplate: BUTTON_WITH_DESC_HIGHLIGHT_TOOLTIP
Children:
Image@ICON:
X: 4
@@ -59,6 +51,7 @@ Container@PLAYER_WIDGETS:
Height: 41
VisualHeight: 0
Background: command-button
DisableKeySound: true
TooltipText: Force Move
TooltipDesc: Selected units will move to the desired location\n - Default activity for the target is suppressed\n - Vehicles will attempt to crush enemies at the target location\n\nLeft-click icon then right-click on target.\nHold {(Alt)} to activate temporarily while commanding units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -75,6 +68,7 @@ Container@PLAYER_WIDGETS:
Height: 41
VisualHeight: 0
Background: command-button
DisableKeySound: true
TooltipText: Force Attack
TooltipDesc: Selected units will attack the targeted unit or location\nignoring their default activity for the target.\n\nLeft-click icon then right-click on target.\nHold {(Ctrl)} to activate temporarily while commanding units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -91,7 +85,7 @@ Container@PLAYER_WIDGETS:
Height: 41
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Guard
TooltipDesc: Selected units will follow the targeted unit.\n\nLeft-click icon then right-click on target unit.
TooltipContainer: TOOLTIP_CONTAINER
@@ -108,6 +102,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Deploy
TooltipDesc: Selected units will perform their default deploy activity\n - MCVs will unpack into a Construction Yard\n - Thumpers will start or stop attracting worms\n\nActs immediately on selected units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -124,6 +119,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Scatter
TooltipDesc: Selected units will stop their current activity\nand move to a nearby location.\n\nActs immediately on selected units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -140,6 +136,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Stop
TooltipDesc: Selected units will stop their current activity.\n\nActs immediately on selected units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -155,6 +152,7 @@ Container@PLAYER_WIDGETS:
Height: 41
VisualHeight: 0
Background: command-button
DisableKeySound: true
TooltipText: Waypoint Mode
TooltipDesc: Use Waypoint Mode to give multiple linking commands\nto the selected units. Units will execute the commands\nimmediately upon receiving them.\n\nLeft-click icon then give commands in the game world.\nHold {(Shift)} to activate temporarily while commanding units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -178,6 +176,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background:
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Attack Anything Stance
TooltipDesc: Set the selected units to Attack Anything stance:\n - Units will attack enemy units and structures on sight\n - Units will pursue attackers across the battlefield
TooltipContainer: TOOLTIP_CONTAINER
@@ -194,6 +193,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background:
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Defend Stance
TooltipDesc: Set the selected units to Defend stance:\n - Units will attack enemy units on sight\n - Units will not move or pursue enemies
TooltipContainer: TOOLTIP_CONTAINER
@@ -210,6 +210,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background:
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Return Fire Stance
TooltipDesc: Set the selected units to Return Fire stance:\n - Units will retaliate against enemies that attack them\n - Units will not move or pursue enemies
TooltipContainer: TOOLTIP_CONTAINER
@@ -226,6 +227,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background:
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Hold Fire Stance
TooltipDesc: Set the selected units to Hold Fire stance:\n - Units will not fire upon enemies\n - Units will not move or pursue enemies
TooltipContainer: TOOLTIP_CONTAINER

View File

@@ -128,16 +128,6 @@ Cursors:
Length: 8
X: 24
Y: 24
attackmove:
Start: 16
Length: 8
X: 24
Y: 24
attackmove-minimap:
Start: 16
Length: 8
X: 24
Y: 24
harvest:
Start: 16
Length: 8
@@ -305,3 +295,41 @@ Cursors:
Length: 3
X: 12
Y: 12
attackmove.shp: mouse
attackmove:
Start: 0
Length: 8
X: -2
Y: -2
attackmove-minimap:
Start: 0
Length: 8
X: -2
Y: -2
attackmove-blocked:
Start: 8
X: -2
Y: -2
attackmove-blocked-minimap:
Start: 8
X: -2
Y: -2
assaultmove.shp: mouse
assaultmove:
Start: 0
Length: 8
X: -2
Y: -2
assaultmove-minimap:
Start: 0
Length: 8
X: -2
Y: -2
assaultmove-blocked:
Start: 8
X: -2
Y: -2
assaultmove-blocked-minimap:
Start: 8
X: -2
Y: -2

View File

@@ -132,6 +132,15 @@
ValidTargets: Infantry, Vehicle, Creep, Water, Structure, Defense
InvalidTargets: NoAutoTarget
^AutoTargetGroundAssaultMove:
Inherits: ^AutoTargetGround
AutoTargetPriority@DEFAULT:
RequiresCondition: !stance-attackanything && !assault-move
AutoTargetPriority@ATTACKANYTHING:
RequiresCondition: stance-attackanything || assault-move
AttackMove:
AssaultMoveScanCondition: assault-move
^AutoTargetAll:
AutoTarget:
AttackAnythingCondition: stance-attackanything
@@ -144,6 +153,15 @@
ValidTargets: Infantry, Vehicle, Creep, Water, Air, Structure, Defense
InvalidTargets: NoAutoTarget
^AutoTargetAllAssaultMove:
Inherits: ^AutoTargetAll
AutoTargetPriority@DEFAULT:
RequiresCondition: !stance-attackanything && !assault-move
AutoTargetPriority@ATTACKANYTHING:
RequiresCondition: stance-attackanything || assault-move
AttackMove:
AssaultMoveScanCondition: assault-move
^Vehicle:
Inherits@1: ^ExistsInWorld
Inherits@2: ^GainsExperience

View File

@@ -1,6 +1,6 @@
light_inf:
Inherits: ^Infantry
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Infantry
BuildPaletteOrder: 10
@@ -50,7 +50,7 @@ engineer:
trooper:
Inherits: ^Infantry
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Infantry
BuildPaletteOrder: 20
@@ -121,7 +121,7 @@ thumper:
fremen:
Inherits: ^Infantry
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Tooltip:
Name: Fremen
Buildable:
@@ -165,7 +165,7 @@ fremen:
grenadier:
Inherits: ^Infantry
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Infantry
BuildPaletteOrder: 80
@@ -196,7 +196,7 @@ grenadier:
sardaukar:
Inherits: ^Infantry
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Infantry
BuildPaletteOrder: 80

View File

@@ -100,7 +100,7 @@ harvester:
trike:
Inherits: ^Vehicle
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Vehicle
BuildPaletteOrder: 10
@@ -140,7 +140,7 @@ trike:
quad:
Inherits: ^Vehicle
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Vehicle
Prerequisites: upgrade.light, ~techlevel.medium
@@ -175,7 +175,7 @@ quad:
siege_tank:
Inherits: ^Tank
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Armor
Prerequisites: upgrade.heavy, ~techlevel.medium
@@ -222,7 +222,7 @@ siege_tank:
missile_tank:
Inherits: ^Tank
Inherits@AUTOTARGET: ^AutoTargetAll
Inherits@AUTOTARGET: ^AutoTargetAllAssaultMove
Tooltip:
Name: Missile Tank
Buildable:
@@ -261,7 +261,7 @@ missile_tank:
sonic_tank:
Inherits: ^Vehicle
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Armor
BuildPaletteOrder: 100
@@ -296,7 +296,7 @@ sonic_tank:
devastator:
Inherits: ^Tank
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Armor
BuildPaletteOrder: 100
@@ -341,7 +341,7 @@ devastator:
raider:
Inherits: ^Vehicle
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Vehicle
BuildPaletteOrder: 10
@@ -407,7 +407,7 @@ stealth_raider:
deviator:
Inherits: ^Tank
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Valued:
Cost: 1000
Tooltip:
@@ -444,7 +444,7 @@ deviator:
^combat_tank:
Inherits: ^Tank
Inherits@AUTOTARGET: ^AutoTargetGround
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Buildable:
Queue: Armor
BuildPaletteOrder: 40

View File

@@ -36,7 +36,7 @@ harvester.husk:
idle: DATA.R8
Start: 1699
Facings: -32
ZOffset: -1023
ZOffset: -512
trike:
idle: DATA.R8

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 209 KiB

View File

@@ -15,10 +15,13 @@ Sound:
Warhead@1Dam: SpreadDamage
Range: 0, 32
Falloff: 100, 100
Damage: 150
Damage: 86
AffectsParent: false
ValidStances: Neutral, Enemy
Versus:
none: 200
light: 110
wood: 110
wall: 50
building: 60
heavy: 60
@@ -28,11 +31,14 @@ Sound:
DamageTypes: Prone50Percent, TriggerProne, SoundDeath
Warhead@2Dam: SpreadDamage
Range: 0, 32
Falloff: 50, 50 # Only does half damage to friendly units
Damage: 150
Falloff: 100, 100
Damage: 43 # Only does half damage to friendly units
AffectsParent: false
ValidStances: Ally
Versus:
none: 200
light: 110
wood: 110
wall: 50
building: 60
heavy: 60

Binary file not shown.

View File

@@ -43,16 +43,18 @@ Container@PLAYER_WIDGETS:
Width: 275
Height: 26
Children:
LogicKeyListener@MODIFIER_OVERRIDES:
Button@ATTACK_MOVE:
Logic: AddFactionSuffixLogic
Width: 34
Height: 26
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Attack Move
TooltipDesc: Selected units will move to the desired location\nand attack any enemies they encounter en route.\n\nLeft-click icon then right-click on target location.
TooltipDesc: Selected units will move to the desired location\nand attack any enemies they encounter en route.\n\nHold {(Ctrl)} while targeting to order an Assault Move\nthat attacks any units or structures encountered en route.\n\nLeft-click icon then right-click on target location.
TooltipContainer: TOOLTIP_CONTAINER
TooltipTemplate: BUTTON_WITH_DESC_HIGHLIGHT_TOOLTIP
Children:
Image@ICON:
X: 5
@@ -66,6 +68,7 @@ Container@PLAYER_WIDGETS:
Height: 26
VisualHeight: 0
Background: command-button
DisableKeySound: true
TooltipText: Force Move
TooltipDesc: Selected units will move to the desired location\n - Default activity for the target is suppressed\n - Vehicles will attempt to crush enemies at the target location\n - Units entering transports will consider nearby alternatives\n - Chrono Tanks will teleport towards the target location\n\nLeft-click icon then right-click on target.\nHold {(Alt)} to activate temporarily while commanding units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -83,6 +86,7 @@ Container@PLAYER_WIDGETS:
Height: 26
VisualHeight: 0
Background: command-button
DisableKeySound: true
TooltipText: Force Attack
TooltipDesc: Selected units will attack the targeted unit or location\nignoring their default activity for the target.\n\nLeft-click icon then right-click on target.\nHold {(Ctrl)} to activate temporarily while commanding units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -100,7 +104,7 @@ Container@PLAYER_WIDGETS:
Height: 26
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Guard
TooltipDesc: Selected units will follow the targeted unit.\n\nLeft-click icon then right-click on target unit.
TooltipContainer: TOOLTIP_CONTAINER
@@ -118,8 +122,9 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Deploy
TooltipDesc: Selected units will perform their default deploy activity\n - MCVs will unpack into a Construction Yard\n - Construction Yards will re-pack into a MCV\n - Transports will unload their passengers\n - Aircraft will return to base\n\nActs immediately on selected units.
TooltipDesc: Selected units will perform their default deploy activity\n - MCVs will unpack into a Construction Yard\n - Construction Yards will re-pack into a MCV\n - Transports will unload their passengers\n - Demolition Trucks and MAD Tanks will self-destruct\n - Minelayers will deploy a mine\n - Aircraft will return to base\n\nActs immediately on selected units.
TooltipContainer: TOOLTIP_CONTAINER
Children:
Image@ICON:
@@ -135,6 +140,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Scatter
TooltipDesc: Selected units will stop their current activity\nand move to a nearby location.\n\nActs immediately on selected units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -152,6 +158,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Stop
TooltipDesc: Selected units will stop their current activity.\n\nActs immediately on selected units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -168,6 +175,7 @@ Container@PLAYER_WIDGETS:
Height: 26
VisualHeight: 0
Background: command-button
DisableKeySound: true
TooltipText: Waypoint Mode
TooltipDesc: Use Waypoint Mode to give multiple linking commands\nto the selected units. Units will execute the commands\nimmediately upon receiving them.\n\nLeft-click icon then give commands in the game world.\nHold {(Shift)} to activate temporarily while commanding units.
TooltipContainer: TOOLTIP_CONTAINER
@@ -192,6 +200,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Attack Anything Stance
TooltipDesc: Set the selected units to Attack Anything stance:\n - Units will attack enemy units and structures on sight\n - Units will pursue attackers across the battlefield
TooltipContainer: TOOLTIP_CONTAINER
@@ -209,6 +218,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Defend Stance
TooltipDesc: Set the selected units to Defend stance:\n - Units will attack enemy units on sight\n - Units will not move or pursue enemies
TooltipContainer: TOOLTIP_CONTAINER
@@ -226,6 +236,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Return Fire Stance
TooltipDesc: Set the selected units to Return Fire stance:\n - Units will retaliate against enemies that attack them\n - Units will not move or pursue enemies
TooltipContainer: TOOLTIP_CONTAINER
@@ -243,6 +254,7 @@ Container@PLAYER_WIDGETS:
VisualHeight: 0
Background: command-button
DisableKeyRepeat: true
DisableKeySound: true
TooltipText: Hold Fire Stance
TooltipDesc: Set the selected units to Hold Fire stance:\n - Units will not fire upon enemies\n - Units will not move or pursue enemies
TooltipContainer: TOOLTIP_CONTAINER

View File

@@ -217,3 +217,17 @@ Cursors:
attackmove-minimap:
Start: 4
Length: 4
attackmove-blocked:
Start: 16
attackmove-blocked-minimap:
Start: 18
assaultmove:
Start: 8
Length: 4
assaultmove-minimap:
Start: 12
Length: 4
assaultmove-blocked:
Start: 17
assaultmove-blocked-minimap:
Start: 19

View File

@@ -118,7 +118,7 @@ aftermath-linux: Aftermath Expansion Disc (English)
readme.txt: 9902fb74c019df1b76ff5634e68f0371d790b5e0
setup/install/patch.rtp: 5bce93f834f9322ddaa7233242e5b6c7fea0bf17
Install:
extract-raw: SETUP/INSTALL/PATCH.RTP
extract-raw: setup/install/patch.rtp
^Content/ra/v2/expand/expand2.mix:
Offset: 4712984
Length: 469922

BIN
mods/ra/maps/agenda.oramap Normal file

Binary file not shown.

View File

@@ -72,7 +72,6 @@ TRUK.mission:
Prerequisites: ~disabled
WithFacingSpriteBody:
-SpawnActorOnDeath:
-EjectOnDeath:
RevealsShroud:
Range: 4c0
ValidStances: Ally, Enemy

View File

@@ -41,6 +41,9 @@ HelicopterUnitTypes = { "e1", "e1", "e1", "e1", "e3", "e3" };
ParadropWaypoints = { Paradrop1, Paradrop2, Paradrop3, Paradrop4, Paradrop5, Paradrop6, Paradrop7, Paradrop8 }
Mig1Waypoints = { Mig11, Mig12, Mig13, Mig14 }
Mig2Waypoints = { Mig21, Mig22, Mig23, Mig24 }
BindActorTriggers = function(a)
if a.HasProperty("Hunt") then
if a.Owner == allies then
@@ -76,6 +79,18 @@ SendSovietUnits = function(entryCell, unitTypes, interval)
Trigger.OnAllKilled(units, function() SendSovietUnits(entryCell, unitTypes, interval) end)
end
SendMigs = function(waypoints)
local migEntryPath = { waypoints[1].Location, waypoints[2].Location }
local migs = Reinforcements.Reinforce(soviets, { "mig" }, migEntryPath, 4)
Utils.Do(migs, function(mig)
mig.Move(waypoints[3].Location)
mig.Move(waypoints[4].Location)
mig.Destroy()
end)
Trigger.AfterDelay(DateTime.Seconds(40), function() SendMigs(waypoints) end)
end
ShipAlliedUnits = function()
local units = Reinforcements.ReinforceWithTransport(allies, "lst",
ShipUnitTypes, { LstEntry.Location, LstUnload.Location }, { LstEntry.Location })[2]
@@ -171,6 +186,9 @@ WorldLoaded = function()
Trigger.AfterDelay(DateTime.Seconds(5), ChronoshiftAlliedUnits)
Utils.Do(ProducedUnitTypes, ProduceUnits)
Trigger.AfterDelay(DateTime.Seconds(30), function() SendMigs(Mig1Waypoints) end)
Trigger.AfterDelay(DateTime.Seconds(30), function() SendMigs(Mig2Waypoints) end)
SendSovietUnits(Entry1.Location, UnitTypes, 50)
SendSovietUnits(Entry2.Location, UnitTypes, 50)
SendSovietUnits(Entry3.Location, UnitTypes, 50)

View File

@@ -1239,6 +1239,30 @@ Actors:
ChronoshiftLocation: waypoint
Location: 80,65
Owner: Neutral
Mig11: waypoint
Location: 94,1
Owner: Neutral
Mig12: waypoint
Location: 68,33
Owner: Neutral
Mig13: waypoint
Location: 41,38
Owner: Neutral
Mig14: waypoint
Location: 1,26
Owner: Neutral
Mig21: waypoint
Location: 96,3
Owner: Neutral
Mig22: waypoint
Location: 70,35
Owner: Neutral
Mig23: waypoint
Location: 41,40
Owner: Neutral
Mig24: waypoint
Location: 1,28
Owner: Neutral
Rules: rules.yaml

View File

@@ -46,6 +46,7 @@ UNITCRATE:
Percentage: 0
APC:
Inherits@AUTOTARGET: ^AutoTargetGround
Health:
HP: 1000
MustBeDestroyed:

View File

@@ -46,6 +46,7 @@ UNITCRATE:
Percentage: 0
APC:
Inherits@AUTOTARGET: ^AutoTargetGround
Health:
HP: 1000
MustBeDestroyed:

Binary file not shown.

Binary file not shown.

View File

@@ -8,6 +8,8 @@ World:
-StartGameNotification:
MissionData:
Briefing: Dr. Demitri, creator of a Soviet Super Tank, wants to defect.\n\nWe planned to extract him while the Soviets were testing their new weapon, but something has gone wrong.\n\nThe Super Tanks are out of control, and Demitri is missing -- likely hiding in the village to the far south.\n\nFind our outpost and start repairs on it, then find and evacuate Demitri.\n\nAs for the tanks, we can reprogram them. Send a spy into the Soviet radar dome in the NE, turning the tanks on their creators.\n
WinVideo: sovbatl.vqa
LossVideo: sovtstar.vqa
^Building:
AnnounceOnSeen:
@@ -161,7 +163,6 @@ PBOX:
DamageCooldown: 150
Selectable:
Bounds: 44,38,0,-4
-EjectOnDeath:
RenderSprites:
Image: 4TNK
@@ -184,7 +185,7 @@ DOME.NoInfiltrate:
Image: DOME
-InfiltrateForExploration:
Targetable:
TargetTypes: Ground, C4, DetonateAttack, MissionObjective
TargetTypes: Ground, Structure, C4, DetonateAttack, MissionObjective
SPY:
Infiltrates:
@@ -194,7 +195,6 @@ BAD3TNK:
Inherits: 3TNK
Buildable:
Prerequisites: ~disabled
-EjectOnDeath:
RenderSprites:
Image: 3TNK
@@ -202,7 +202,6 @@ BADTRUK:
Inherits: TRUK
Buildable:
Prerequisites: ~disabled
-EjectOnDeath:
RenderSprites:
Image: TRUK

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More