Compare commits

...

145 Commits

Author SHA1 Message Date
abcdefg30
4d368c1ec1 Merge pull request #10213 from obrakmann/fix9851_prep-edition
[prep] Fix AttackMove not working properly under certain conditions
2015-12-13 20:30:24 +01:00
RoosterDragon
c504b2a9ae Ensure LaysTerrain doesn't access cells outside the map. 2015-12-13 20:07:48 +01:00
RoosterDragon
b9f08b4607 Fix GPS refreshing for incorrect player.
GpsWatcher.RefreshGps was wrongly using the render player in simulation code.
2015-12-13 18:30:57 +01:00
RoosterDragon
5897230226 Fix GPS dot relying on frozen actor render state.
The GPS dot was not displayed if a frozen actor for it was already visible. However, this was mistakenly affecting visibility checks in the world. Now, we only avoid rendering it when the frozen actor is present, but the indicator is still regarded as visible for visilbity checks in the world.
2015-12-13 18:30:51 +01:00
RoosterDragon
f26b0f1251 Ensure frozen actors are rendered on the first tick they become visible.
The previous lazy rendering means the snapshot of the render state might be more up to date then when the frozen actor actually did become visible. Now, we take this snapshot as soon as needed. We still retain the performance of only doing this rendering when needed by avoiding extra rendering until the visibility cycles again.
2015-12-13 18:30:43 +01:00
RoosterDragon
7bc4d205fe Merge dictionaries in GpsDot.
This speeds up GpsDot.Tick as it requires only one dictionary lookup per player rather than two.
2015-12-13 18:30:35 +01:00
Oliver Brakmann
71d4e29483 Fix AttackMove not working properly under certain conditions
This fixes the issue where AttackMove (and possibly other order modes) would not work properly when people were clicking their mouse buttons at the same time. A move order (the default order mode) would be issued instead.
2015-12-13 14:17:39 +01:00
abcdefg30
0e082183e3 Let spawned soviet vehicles attack move on Survival02 2015-12-12 23:50:50 +02:00
RoosterDragon
00bce1266f When hardware cursors fail, retry next session. 2015-12-12 12:56:42 +00:00
RoosterDragon
09f0242059 When the SDL hardware cursor fails, ensure more of the error message is logged.
Capture the useful details from the inner exception into the message, as this will be logged.
2015-12-12 12:56:35 +00:00
Oliver Brakmann
aa14f1b0a2 Fix showing unread messages count on active lobby chat button
The lobby chat button text would change to include the unread messages count for a split second, even when the lobby chat was the current panel, leading to the button text sort of 'blinking' in an ugly way.
2015-12-08 20:51:32 +00:00
abcdefg30
3cf6148c03 Fix a crash when producing an actor without OccupiesSpace in combination with UpgradeActorsNear 2015-12-03 21:57:38 +01:00
Oliver Brakmann
eddda3ecca Set enemies of 'Creeps' player when adding a new player in the editor 2015-11-29 20:49:09 +02:00
abcdefg30
ec001a608e Misc (style) improvements in the upgrade rule code 2015-11-29 20:48:14 +02:00
abcdefg30
07ca72127f Try to avoid adding duplicate values through upgrade rules 2015-11-29 20:48:07 +02:00
abcdefg30
246b25f008 Let upgrade rules also account for negated traits
(this commit affects only "-SpawnViceroid:", "-LeavesHusk:", "-Plane:" and "-Helicopter:")
2015-11-29 20:47:55 +02:00
abcdefg30
0bca700bff Let the utility fix cosmetics in the chrome yamls 2015-11-29 20:47:46 +02:00
abcdefg30
0d286e12b2 Fix potential crashes in the upgrade rules 2015-11-29 20:47:38 +02:00
Oliver Brakmann
811acc5a58 Merge pull request #10144 from cjshmyr/missile-prepfix
Set missile default acceleration to 0 (prep only)
2015-11-29 13:10:05 +01:00
Curtis Shmyr
265faa3452 Set missile default acceleration to 0 2015-11-28 21:59:05 -07:00
Paul Chote
bc51d6638d Add a server log line for player joins. 2015-11-28 15:45:30 +01:00
Oliver Brakmann
60a27f9f83 Merge pull request #10130 from pchote/revert-bridge-fix
Revert "add custom selection sizes to aid the renderer" on prep-1512
2015-11-28 15:25:25 +01:00
Paul Chote
a7cfaff96c Revert "add custom selection sizes to aid the renderer"
This reverts commit c3dce785a5.
2015-11-28 09:58:44 +00:00
Paul Chote
d957a5a15e Implement suicide saboteur. 2015-11-27 14:31:20 +01:00
Paul Chote
026a27296c Fix C4Demolition trait doc punctuation. 2015-11-27 14:31:13 +01:00
Paul Chote
f7c2043e06 Add enter behaviour customisation to C4Demolition. 2015-11-27 14:31:02 +01:00
OmegaBolt
4efe8d091c D2k Deviator balance 2015-11-25 22:54:38 +02:00
Matthias Mailänder
97e810d753 simplify the hierarchy section hierarchy 2015-11-25 22:35:22 +02:00
Matthias Mailänder
7ed82fa4c7 make the "Game" component read-only 2015-11-25 22:35:15 +02:00
reaperrr
32b4402ba0 Give Missile Tank weapon a range limit and reduce Quad rocket range limit 2015-11-25 22:30:36 +02:00
DArcy Rush
897c0f01f0 Add second exit to kennel 2015-11-25 22:16:55 +02:00
Paul Chote
b21b9c9762 Fix the ConvertSpriteToPngCommand. 2015-11-25 22:08:30 +02:00
Paul Chote
5bcc83bc0c Add upgraded tag to D2K production structures. 2015-11-25 22:02:28 +02:00
Paul Chote
509662d8a6 Add SelectionDecoration flag to WithDecoration. 2015-11-25 22:02:14 +02:00
Paul Chote
fd171d842e Add new primary building tag artwork. 2015-11-25 22:02:07 +02:00
Paul Chote
9258face3c Fix D2K outpost artwork offsets and selection box. 2015-11-25 22:01:41 +02:00
Oliver Brakmann
41e5032d48 Fix worms not attacking anything anymore 2015-11-22 21:56:49 +01:00
OmegaBolt
6006dbd235 D2k, fix #10037 terrain regressions 2015-11-22 21:56:35 +01:00
OmegaBolt
b7e7ff7fc0 D2k spice no longer erases map details & terrain fixes 2015-11-22 18:22:11 +01:00
reaperrr
6d98635e94 Add invisible spice bloom spawn
When real spiceblooms explode, they spawn an invisible
spicebloom.spawnpoint actor, which only 'grows' when the cell is of
GrowthTerrainType. When 'exploding', it then displays the animation of
blowing spice up into the air, and spawns a real spicebloom.

This replicates the original behavior where spice blooms would only grow
on sand cells that had been harvested and free of spice.
2015-11-22 17:24:03 +01:00
reaperrr
46762140a5 Improve spice bloom seeding mechanism
Improve seeding target cell determination.
Draw out spice seedable cells before loop.
Default to null for SpiceBloom.Weapon.
Pull spice seeding into its own method.
Skip it completely if no seed weapon is defined.
2015-11-22 17:19:28 +01:00
reaperrr
09cdf3b257 Only allow spice blooms to grow on certain terrain types 2015-11-22 17:19:28 +01:00
RoosterDragon
033e469bea Prevent invalid cells checks in SeedsResource. 2015-11-22 08:05:10 +01:00
Paul Chote
7e680b9c02 Fix width of the MP filter checkboxes. 2015-11-22 01:36:32 +00:00
abcdefg30
a56d367f60 Add an upgrade rule for the Upgrades -> DeployedUpgrades change 2015-11-22 01:06:16 +01:00
Pavel Penev
bd2629196a Update thumpers and Mobile Sensor Arrays to the new DeployToUpgrade
Currently those are the only two usages of the trait.
2015-11-22 01:01:56 +01:00
Pavel Penev
011f566c6f Add support for deploy animations using a specific upgrade
Also ignore orders while playing a deploy/undeploy animation. Fixes 9242.

We grant a level 1 Undeployed upgrade(s) when the actor is created which enables the default behavior of the actor. When the actor receives a deploy order, the Undeployed upgrade(s) is(are) revoked, which may or may not be used to enable a trait to play a deploy animation. Once the deployment is complete, the Deployed upgrade(s) is(are) granted.
Then do the reverse when undeploying, disabling weapons and other systems first by revoking the Deployed upgrade(s), and granting the Undeployed upgrade(s) when the undeploy animation is done.
2015-11-22 01:01:40 +01:00
abcdefg30
161907b852 Fix 'oldlst's letting units enter it (after delivering them) 2015-11-21 23:23:36 +01:00
abcdefg30
1576101a2c Add IsDead checks to gdi06 2015-11-21 23:15:43 +01:00
abcdefg30
67ba3e53f2 Fix the harvester stopping when you reach the base in gdi05 2015-11-21 23:07:16 +01:00
DArcy Rush
52b061a8a8 Hide ammobox healthbar. 2015-11-21 22:49:06 +01:00
abcdefg30
5755f4048d Merge pull request #10057 from atlimit8/FinishRevertingMultilineSupportForSimpleTooltipLogic
Finish reverting multiline support for SimpleTooltipLogic
2015-11-21 22:37:36 +01:00
atlimit8
b1b210ea6d Finish reverting #9925 & #9715 2015-11-21 14:28:33 -06:00
abcdefg30
fc8f1d40c5 Fix the rushAI building spens/syrds without being able to build naval units 2015-11-21 21:02:22 +01:00
Paul Chote
5d83a5a7d6 Restore earlier spy targeting behaviour. 2015-11-21 20:58:17 +01:00
Paul Chote
8c24e87de2 Make CanTarget modifiers a ref parameter. 2015-11-21 20:58:17 +01:00
reaperrr
13c4f16272 Increase the default range of angles that missiles can launch at 2015-11-21 21:12:23 +02:00
Pavel Penev
20a33b8411 Make GPS respect shroud 2015-11-21 18:59:41 +01:00
Pavel Penev
51fcb55b22 Remove byPlayer parameter from IFogVisibilityModifier.HasFogVisibility() 2015-11-21 18:59:41 +01:00
Pavel Penev
8e7469e194 Add IFogVisibilityModifier.IsVisible(Actor) 2015-11-21 18:59:41 +01:00
Pavel Penev
7e0617075f Track GpsDot state per player
Should let us fix the issue mentioned in https://github.com/OpenRA/OpenRA/pull/10007#issuecomment-157409237
2015-11-21 18:59:41 +01:00
Pavel Penev
4ff6ba8c37 Don't attack actors that you can't see 2015-11-21 18:59:35 +01:00
atlimit8
958a72aaf8 Revert Multi-line tooltips due to lack of multi-line support.
This reverts #9925 & #9715.
2015-11-21 11:08:11 -06:00
Oliver Brakmann
e7df117892 Merge pull request #10019 from r34ch/ra-map-pool
Add a couple community maps to the pool.
2015-11-21 14:55:31 +01:00
reaperrr
57d49d0aeb Fix D2k wormspawner editor art 2015-11-19 19:36:54 +01:00
reaperrr
ee86b5f45f Fix D2k effect palette shadow index 2015-11-19 19:36:54 +01:00
reaperrr
ba0e3130af Fix mpspawn, waypoint and camera art
All mpspawn, waypoint and camera editor art uses the correct shadow and remap colors.
Additionally added camera art to CnC, D2k and TS.
2015-11-19 19:36:54 +01:00
Pavel Penev
64427dd1a6 Fix UpgradeActorsNear revoking upgrades after the actor dies
Very hard bug to reproduce: if you can power down a stealth generator just as it is dieing you can hit a race condition that leads to ActorExited() being called to revoke the upgrades after they have already been revoked once.
2015-11-19 19:00:12 +01:00
abcdefg30
7412669d03 Add production acceleration to d2k
except for starports
2015-11-17 19:33:26 +01:00
Curtis Shmyr
ec8e9f537d Increase TRUK and DTRK vision from 3 to 4 cells 2015-11-17 19:25:39 +01:00
DArcy Rush
9c71d555c1 Add a couple community maps to the pool. 2015-11-17 08:39:43 +00:00
abcdefg30
0f191484d7 Fix a crash when using the chronoshift power
if the "chronosphere-actor" doesn't have an "active" sequence
2015-11-16 22:00:30 +00:00
Pavel Penev
4be5979b90 Add a null check to CachedTransform.Update() 2015-11-16 21:52:19 +00:00
Pavel Penev
6bd092f192 Check if WithSpriteBody is available in GrantUpgradePower 2015-11-16 13:58:24 +01:00
Oliver Brakmann
bd6bcda6d0 Merge pull request #9993 from pchote/nsprocess-fix
Switch to a (hopefully) working NSProcess url.
2015-11-14 22:52:56 +01:00
Paul Chote
f12deca9ca Switch to a (hopefully) working NSProcess url. 2015-11-14 21:47:21 +00:00
Oliver Brakmann
5ea82b4e5e Increase range of flak truck against air targets
From 6 cells to 8 cells
2015-11-14 22:10:40 +01:00
DArcy Rush
2d210a97df Rebalance Tesla Tank.
Allow attacking of target regardless of facing.
2015-11-14 22:10:40 +01:00
DArcy Rush
f03a100115 Rebalance phase transport.
Passengers increased to 4, speed and LoS increased.
2015-11-14 22:10:40 +01:00
DArcy Rush
1e30684782 Reduce chronotank charge time.
Chronotank charge decreased from 20s to 12s.
2015-11-14 22:10:40 +01:00
Curtis Shmyr
abb45197b1 lower InfiltrateForPowerOutage time from 30->20sec 2015-11-14 19:34:37 +00:00
Paul Chote
a7935f6166 Check deploy location at the time of transform. 2015-11-14 16:46:18 +01:00
Oliver Brakmann
7b75a9c2f7 Fix reinforcements appearing in soviet04a/b despite killed radar dome 2015-11-14 15:01:51 +02:00
reaperrr
9b694f6044 Fix spice bloom editor visibility 2015-11-14 12:09:07 +00:00
Biofreak1987
0a75019aa0 Fix ai building in soviet04/06 missions despite killed construction yard 2015-11-13 20:53:26 +01:00
Biofreak1987
d3e41b6d50 Fix nod05 airstrike Lua crash and update map rules
- Fix airstrike Lua crash when there are no targets
- Increased harvester search range
- Hide owner row from husks and walls

Changes to make it closer to the original:
- Added e3 and sandbag production
- Made airstrikes gdi only
- Limited airstrike squadsize to 1
- Allow to attack civilian buildings
2015-11-12 22:52:38 +01:00
Oliver Brakmann
18f3e1f57b Merge pull request #9971 from pchote/tooltip-fix
Fix map editor crash
2015-11-12 19:32:02 +01:00
Paul Chote
1760f8acd6 Fix tooltip colour definition. 2015-11-11 23:15:33 +00:00
Pavel Penev
4154553318 Validate player color when changing slots in the lobby 2015-11-11 22:59:48 +00:00
Oliver Brakmann
e5e8e64ace Fix bogus owner check in Lua API production code 2015-11-11 22:24:05 +00:00
OmegaBolt
0ae58c10ad D2k thumper worm attraction balance 2015-11-11 23:01:53 +02:00
Paul Chote
89ea810cbe Truncate player name in replay browser. 2015-11-11 22:40:00 +02:00
Paul Chote
f6cf35c99c Truncate player name in observer stats and diplomacy panels. 2015-11-11 22:39:52 +02:00
Paul Chote
5fbaeabb6d Truncate player name in ingame menu stats. 2015-11-11 22:39:37 +02:00
Paul Chote
581907f53a Prevent frozen actors leaking through shroud. 2015-11-11 22:33:16 +02:00
Paul Chote
7cf4be9d78 Add copy/paste to the map editor. 2015-11-11 01:02:16 +02:00
Paul Chote
bdf9f5e201 Make editor brushes disposable. 2015-11-11 01:02:09 +02:00
Pavel Penev
b26b1f410f Enable AutoTarget to aquire targets under fog if GPS is present 2015-11-10 22:27:40 +01:00
OmegaBolt
ce69abe1f2 D2k fixed armour & AI typos 2015-11-10 22:04:09 +01:00
atlimit8
b3d2b36875 Make ActorSelector tooltips better match actor tooltips in map editor 2015-11-10 21:08:53 +01:00
atlimit8
f28b37323f Made editor actor preview tooltip easier to read 2015-11-10 21:08:53 +01:00
atlimit8
7c82ab60e8 Multiline support for SimpleTooltipLogic 2015-11-10 21:08:53 +01:00
Zimmermann Gyula
d14fbd0f84 Fix observer GUI showing disabled support powers. 2015-11-09 21:07:19 +01:00
OmegaBolt
c567f5cbdd D2k super weapon timers 2015-11-09 21:51:02 +02:00
OmegaBolt
51a9831fc2 D2k Ornithopter bomb damage increase 2015-11-09 19:58:36 +01:00
OmegaBolt
4d5777cbc9 D2k, fixed infantry moving slow on dunes and rough 2015-11-08 21:59:07 +02:00
Paul Chote
a6a835d95f Truncate long player names in lobby. 2015-11-08 17:24:24 +02:00
Paul Chote
e16be14ec7 Truncate long server names in MP browser. 2015-11-08 17:24:19 +02:00
Paul Chote
11939d3af1 Truncate long map name in replay browser. 2015-11-08 17:24:13 +02:00
Paul Chote
feb133c2eb Truncate long map name / mod version in MP panel. 2015-11-08 17:24:02 +02:00
Paul Chote
f6361b031b Truncate long map name / author in map chooser. 2015-11-08 17:23:56 +02:00
Paul Chote
36967951e6 Truncate long map name / author in lobby. 2015-11-08 17:23:39 +02:00
Paul Chote
012a9d71ba Add helpers for truncating long labels. 2015-11-08 17:23:31 +02:00
Paul Chote
cff369850c Set the default map location to the support dir. 2015-11-08 14:49:38 +02:00
DArcy Rush
136b4c0fa9 Add self to AUTHORS. 2015-11-08 11:40:37 +00:00
DArcy Rush
aae2459d0b Move blossom trees south one cell in cnc64gdi01. 2015-11-08 11:40:27 +00:00
teees
d47ba1d135 only change editor brushes on mouseup
let other uses of the right mouse button bubble up
2015-11-08 11:30:25 +00:00
teees
133cffff24 Handle mouse up event in default editor brush 2015-11-08 11:30:08 +00:00
Oliver Brakmann
52f6c993d5 Fix lint warning in gdi06 2015-11-08 11:17:22 +00:00
Biofreak1987
21eecb048d Fix soviet06 prerequisites 2015-11-08 09:48:47 +01:00
Oliver Brakmann
b97398a671 Fix typo in WeatherOverlayInfo 2015-11-08 09:14:22 +01:00
Curtis Shmyr
a285c908db Added 3 Lua player API properties 2015-11-08 08:53:15 +01:00
reaperrr
35b39162ff Fix spice debris target cell randomization 2015-11-07 18:32:21 +01:00
reaperrr
0b5b6adc7c Improve SpiceExplosion and add BloomExplosion
Fixes angle and loudness of spice debris, adds BloomExplosion to add proper animation and damage crushing actors.
2015-11-07 18:32:14 +01:00
reaperrr
5357eb6f29 Increase variation and average of spice bloom growth 2015-11-07 18:32:04 +01:00
reaperrr
e274ac5ff8 Make sure that a spice bloom spawns at least the minimum of pieces 2015-11-07 18:31:59 +01:00
reaperrr
f8e39bd142 Reduce spawned spice per piece 2015-11-07 18:31:56 +01:00
Pavel Penev
48881f4e73 Fix repair pad reference in units' Repairable trait 2015-11-07 18:31:52 +01:00
Pavel Penev
d2eb56183d Buff D2k repair pads 2015-11-07 18:10:40 +01:00
reaperrr
afd75c7f55 Fix WithSpriteRotorOverlay upgrade rule to account for @ separator 2015-11-07 16:18:29 +02:00
reaperrr
6d1551c9fd Change engineVersion for upgrade rules of changes made between 20150808 and 20150919
Those changes were merged after the feature-freeze, but predate the stable release. Changing the engine version to the day after release makes it easier for modders to upgrade without using bogus engine dates.
2015-11-07 16:18:18 +02:00
Paul Chote
a015b0990b Fetch battlefield news once per game launch. 2015-11-07 14:13:31 +02:00
Paul Chote
6dd08bbc98 Fix global chat line margins. 2015-11-07 12:43:14 +01:00
Paul Chote
343616e924 Add unread message count to lobby chat tabs. 2015-11-07 12:43:03 +01:00
Paul Chote
91242a832f Add Tick method to ChromeLogic. 2015-11-07 12:43:03 +01:00
OmegaBolt
1c53bfed11 D2k crate balance
- Fixed prerequisites.
- Added more infantry crates.
- Upped the create explosion damage, though it is still very little. Takes about a third health off a Trike.
2015-11-06 19:47:34 +01:00
RoosterDragon
e5e7d037a3 Remove event handler when disposed in MainMenuLogic.
Removes a static event handler to Game.OnRemoteDirectConnect which allow the GC to reclaim the MainMenuLogic class after it has been disposed.
2015-11-05 22:49:25 +01:00
RoosterDragon
2a301392c6 Remove event handlers to CellEntryChanged when done.
Several classes would attach event handlers to the Map which would live longer then they did. Detaching them when no longer needed allows the GC to reclaim them.
2015-11-05 22:49:25 +01:00
RoosterDragon
e35bb8ea95 Detach event handlers on dispose in TerrainSpriteLayer.
The WorldRenderer outlives the TerrainSpriteLayer and thus keeps it alive longer than expected via the event handler. We detach it to allow the GC to reclaim it.
2015-11-05 22:49:25 +01:00
Pavel Penev
7a4002ff08 Fix the C17 delivery plane doing a steep dive when trying to land 2015-11-05 19:49:04 +00:00
RoosterDragon
523fe20255 Work around a hang on shutdown caused by IRC.
Our IRC client doesn't shut down properly - but we only need to shut it down when we're about to close the game anyway, so we just don't bother since it won't hurt anybody.
2015-11-05 20:42:15 +01:00
Zimmermann Gyula
31e0c8b2e3 Fix (HealthPercentage)DamageWarheads ignoring stances/TargetTypes.
Moves the IsValidAgainst check from SpreadDamage level to Damage level
and adds a duplication to HealthPercentageDamageWarhead.
2015-11-04 22:34:48 +00:00
Pavel Penev
7c37d1cfcc Fix server filter width 2015-11-04 22:31:27 +01:00
Oliver Brakmann
8e76a109bd Fix viceroids being spawned by crushed infantry 2015-11-03 10:33:40 +00:00
Michael Rätzel
ba9b18ccb7 reset RallyPoint Location after capture. resolves #9163. 2015-10-31 21:59:03 +01:00
181 changed files with 2087 additions and 882 deletions

View File

@@ -44,6 +44,7 @@ Also thanks to:
* clem
* Cody Brittain (Generalcamo)
* D2k Sardaukar
* D'Arcy Rush (r34ch)
* Daniel Derejvanik (Harisson)
* Danny Keary (Dan9550)
* David Jiménez (Rydra)

View File

@@ -329,7 +329,6 @@ namespace OpenRA
Console.WriteLine("Error was: " + e.Message);
Cursor = new SoftwareCursor(ModData.CursorProvider);
Settings.Graphics.HardwareCursors = false;
}
}
else

View File

@@ -150,7 +150,7 @@ namespace OpenRA.Chat
}
client.Listen();
}) { Name = "IrcListenThread" }.Start();
}) { Name = "IrcListenThread", IsBackground = true }.Start();
}
void AddNotification(string text)
@@ -364,8 +364,19 @@ namespace OpenRA.Chat
public void Dispose()
{
if (client.IsConnected)
client.Disconnect();
// HACK: The IRC library we are using has terrible thread-handling code that relies on Thread.Abort.
// There is a thread reading from the network socket which is aborted, however on Windows this is inside
// native code so this abort call hangs until the network socket reads something and returns to managed
// code where it can then be aborted.
//
// This means we may hang for several seconds during shutdown (until we receive something over IRC!) before
// closing.
//
// Since our IRC client currently lives forever, the only time we call this Dispose method is during the
// shutdown of our process. Therefore, we can work around the problem by just not bothering to disconnect
// properly. Since our process is about to die anyway, it's not like anyone will care.
////if (client.IsConnected)
//// client.Disconnect();
}
}
}

View File

@@ -18,11 +18,13 @@ namespace OpenRA.Graphics
readonly TerrainSpriteLayer terrain;
readonly Theater theater;
readonly CellLayer<TerrainTile> mapTiles;
readonly CellLayer<byte> mapHeight;
public TerrainRenderer(World world, WorldRenderer wr)
{
theater = wr.Theater;
mapTiles = world.Map.MapTiles.Value;
mapHeight = world.Map.MapHeight.Value;
terrain = new TerrainSpriteLayer(world, wr, theater.Sheet, BlendMode.Alpha,
wr.Palette("terrain"), wr.World.Type != WorldType.Editor);
@@ -30,8 +32,8 @@ namespace OpenRA.Graphics
foreach (var cell in world.Map.AllCells)
UpdateCell(cell);
world.Map.MapTiles.Value.CellEntryChanged += UpdateCell;
world.Map.MapHeight.Value.CellEntryChanged += UpdateCell;
mapTiles.CellEntryChanged += UpdateCell;
mapHeight.CellEntryChanged += UpdateCell;
}
public void UpdateCell(CPos cell)
@@ -48,6 +50,8 @@ namespace OpenRA.Graphics
public void Dispose()
{
mapTiles.CellEntryChanged -= UpdateCell;
mapHeight.CellEntryChanged -= UpdateCell;
terrain.Dispose();
}
}

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Graphics
readonly WorldRenderer worldRenderer;
readonly Map map;
float paletteIndex;
readonly PaletteReference palette;
public TerrainSpriteLayer(World world, WorldRenderer wr, Sheet sheet, BlendMode blendMode, PaletteReference palette, bool restrictToBounds)
{
@@ -41,7 +41,7 @@ namespace OpenRA.Graphics
this.restrictToBounds = restrictToBounds;
Sheet = sheet;
BlendMode = blendMode;
paletteIndex = palette.TextureIndex;
this.palette = palette;
map = world.Map;
rowStride = 4 * map.MapSize.X;
@@ -50,21 +50,21 @@ namespace OpenRA.Graphics
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(vertices.Length);
emptySprite = new Sprite(sheet, Rectangle.Empty, TextureChannel.Alpha);
wr.PaletteInvalidated += () =>
wr.PaletteInvalidated += UpdatePaletteIndices;
}
void UpdatePaletteIndices()
{
// Everything in the layer uses the same palette,
// so we can fix the indices in one pass
for (var i = 0; i < vertices.Length; i++)
{
paletteIndex = palette.TextureIndex;
var v = vertices[i];
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.U, v.V, palette.TextureIndex, v.C);
}
// Everything in the layer uses the same palette,
// so we can fix the indices in one pass
for (var i = 0; i < vertices.Length; i++)
{
var v = vertices[i];
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.U, v.V, paletteIndex, v.C);
}
for (var row = 0; row < map.MapSize.Y; row++)
dirtyRows.Add(row);
};
for (var row = 0; row < map.MapSize.Y; row++)
dirtyRows.Add(row);
}
public void Update(CPos cell, Sprite sprite)
@@ -88,7 +88,7 @@ namespace OpenRA.Graphics
sprite = emptySprite;
var offset = rowStride * uv.V + 4 * uv.U;
Util.FastCreateQuad(vertices, pos, sprite, paletteIndex, offset, sprite.Size);
Util.FastCreateQuad(vertices, pos, sprite, palette.TextureIndex, offset, sprite.Size);
dirtyRows.Add(uv.V);
}
@@ -130,6 +130,7 @@ namespace OpenRA.Graphics
public void Dispose()
{
worldRenderer.PaletteInvalidated -= UpdatePaletteIndices;
vertexBuffer.Dispose();
}
}

View File

@@ -13,19 +13,19 @@ using OpenRA.Graphics;
namespace OpenRA.Orders
{
public class GenericSelectTarget : IOrderGenerator
public class GenericSelectTarget : UnitOrderGenerator
{
readonly IEnumerable<Actor> subjects;
readonly string order;
readonly string cursor;
readonly MouseButton expectedButton;
protected readonly IEnumerable<Actor> Subjects;
protected readonly string OrderName;
protected readonly string Cursor;
protected readonly MouseButton ExpectedButton;
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor, MouseButton button)
{
this.subjects = subjects;
this.order = order;
this.cursor = cursor;
expectedButton = button;
Subjects = subjects;
OrderName = order;
Cursor = cursor;
ExpectedButton = button;
}
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor)
@@ -37,26 +37,23 @@ namespace OpenRA.Orders
public GenericSelectTarget(Actor subject, string order, string cursor, MouseButton button)
: this(new Actor[] { subject }, order, cursor, button) { }
public IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
public override IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
{
if (mi.Button != expectedButton)
if (mi.Button != ExpectedButton)
world.CancelInputMode();
return OrderInner(world, xy, mi);
}
IEnumerable<Order> OrderInner(World world, CPos xy, MouseInput mi)
protected virtual IEnumerable<Order> OrderInner(World world, CPos xy, MouseInput mi)
{
if (mi.Button == expectedButton && world.Map.Contains(xy))
if (mi.Button == ExpectedButton && world.Map.Contains(xy))
{
world.CancelInputMode();
foreach (var subject in subjects)
yield return new Order(order, subject, false) { TargetLocation = xy };
foreach (var subject in Subjects)
yield return new Order(OrderName, subject, false) { TargetLocation = xy };
}
}
public virtual void Tick(World world) { }
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
public IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr, World world) { yield break; }
public string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.Contains(xy) ? cursor : "generic-blocked"; }
public override string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.Contains(xy) ? Cursor : "generic-blocked"; }
}
}

View File

@@ -15,9 +15,9 @@ using OpenRA.Traits;
namespace OpenRA.Orders
{
class UnitOrderGenerator : IOrderGenerator
public class UnitOrderGenerator : IOrderGenerator
{
public IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
public virtual IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
{
var underCursor = world.ScreenMap.ActorsAt(mi)
.Where(a => !world.FogObscures(a) && a.Info.HasTraitInfo<ITargetableInfo>())
@@ -50,11 +50,11 @@ namespace OpenRA.Orders
yield return CheckSameOrder(o.Order, o.Trait.IssueOrder(o.Actor, o.Order, o.Target, mi.Modifiers.HasModifier(Modifiers.Shift)));
}
public void Tick(World world) { }
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
public IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr, World world) { yield break; }
public virtual void Tick(World world) { }
public virtual IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
public virtual IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr, World world) { yield break; }
public string GetCursor(World world, CPos xy, MouseInput mi)
public virtual string GetCursor(World world, CPos xy, MouseInput mi)
{
var useSelect = false;
var underCursor = world.ScreenMap.ActorsAt(mi)
@@ -130,7 +130,7 @@ namespace OpenRA.Orders
modifiers |= TargetModifiers.ForceMove;
string cursor = null;
if (o.Order.CanTarget(self, target, actorsAt, modifiers, ref cursor))
if (o.Order.CanTarget(self, target, actorsAt, ref modifiers, ref cursor))
return new UnitOrderResult(self, o.Order, o.Trait, cursor, target);
}
}

View File

@@ -159,13 +159,13 @@ namespace OpenRA
public bool CanTargetActor(Actor a)
{
if (HasFogVisibility)
if (HasFogVisibility && fogVisibilities.Any(f => f.IsVisible(a)))
return true;
return CanViewActor(a);
}
public bool HasFogVisibility { get { return fogVisibilities.Any(f => f.HasFogVisibility(this)); } }
public bool HasFogVisibility { get { return fogVisibilities.Any(f => f.HasFogVisibility()); } }
#region Scripting interface

View File

@@ -372,6 +372,9 @@ namespace OpenRA.Server
SyncLobbyInfo();
Log.Write("server", "{0} ({1}) has joined the game.",
client.Name, newConn.Socket.RemoteEndPoint);
if (!LobbyInfo.IsSinglePlayer)
SendMessage("{0} has joined the game.".F(client.Name));

View File

@@ -179,7 +179,6 @@ namespace OpenRA
public bool FetchNews = true;
public string NewsUrl = "http://www.openra.net/gamenews";
public DateTime NewsFetchedDate;
}
public class KeySettings

View File

@@ -41,8 +41,12 @@ namespace OpenRA.Traits
public DamageState DamageState;
public bool Visible = true;
public bool NeedRenderables { get; private set; }
public bool IsRendering { get; private set; }
public bool Shrouded { get; private set; }
public bool NeedRenderables { get; set; }
public IRenderable[] Renderables = NoRenderables;
static readonly IRenderable[] NoRenderables = new IRenderable[0];
int flashTicks;
public FrozenActor(Actor self, PPos[] footprint, Shroud shroud, bool startsRevealed)
{
@@ -68,11 +72,6 @@ namespace OpenRA.Traits
public ActorInfo Info { get { return actor.Info; } }
public Actor Actor { get { return !actor.IsDead ? actor : null; } }
static readonly IRenderable[] NoRenderables = new IRenderable[0];
int flashTicks;
IRenderable[] renderables = NoRenderables;
public void Tick()
{
UpdateVisibility();
@@ -84,6 +83,7 @@ namespace OpenRA.Traits
void UpdateVisibility()
{
var wasVisible = Visible;
Shrouded = true;
// We are doing the following LINQ manually for performance since this is a hot path.
// Visible = !Footprint.Any(shroud.IsVisible);
@@ -93,12 +93,15 @@ namespace OpenRA.Traits
if (shroud.IsVisible(puv))
{
Visible = false;
Shrouded = false;
break;
}
if (Shrouded && shroud.IsExplored(puv))
Shrouded = false;
}
if (Visible && !wasVisible)
NeedRenderables = true;
NeedRenderables = Visible && !wasVisible;
}
public void Flash()
@@ -108,28 +111,20 @@ namespace OpenRA.Traits
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (NeedRenderables)
{
NeedRenderables = false;
if (!actor.Disposed)
{
IsRendering = true;
renderables = actor.Render(wr).ToArray();
IsRendering = false;
}
}
if (Shrouded)
return NoRenderables;
if (flashTicks > 0 && flashTicks % 2 == 0)
{
var highlight = wr.Palette("highlight");
return renderables.Concat(renderables.Where(r => !r.IsDecoration)
return Renderables.Concat(Renderables.Where(r => !r.IsDecoration)
.Select(r => r.WithPalette(highlight)));
}
return renderables;
return Renderables;
}
public bool HasRenderables { get { return renderables.Any(); } }
public bool HasRenderables { get { return !Shrouded && Renderables.Any(); } }
public bool ShouldBeRemoved(Player owner)
{
@@ -175,6 +170,8 @@ namespace OpenRA.Traits
VisibilityHash = 0;
FrozenHash = 0;
// TODO: Track shroud updates using Shroud.CellsChanged
// and then only tick FrozenActors that might have changed
foreach (var kvp in frozen)
{
var hash = (int)kvp.Key;

View File

@@ -97,7 +97,7 @@ namespace OpenRA.Traits
{
string OrderID { get; }
int OrderPriority { get; }
bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor);
bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor);
bool IsQueued { get; }
bool OverrideSelection { get; }
}
@@ -189,7 +189,12 @@ namespace OpenRA.Traits
public interface IDefaultVisibilityInfo : ITraitInfo { }
public interface IDefaultVisibility { bool IsVisible(Actor self, Player byPlayer); }
public interface IVisibilityModifier { bool IsVisible(Actor self, Player byPlayer); }
public interface IFogVisibilityModifier { bool HasFogVisibility(Player byPlayer); }
public interface IFogVisibilityModifier
{
bool IsVisible(Actor actor);
bool HasFogVisibility();
}
public interface IRadarColorModifier { Color RadarColorOverride(Actor self); }

View File

@@ -143,6 +143,7 @@ namespace OpenRA.Widgets
public class ChromeLogic : IDisposable
{
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
public virtual void Tick() { }
protected virtual void Dispose(bool disposing) { }
}
@@ -436,6 +437,10 @@ namespace OpenRA.Widgets
Tick();
foreach (var child in Children)
child.TickOuter();
if (LogicObjects != null)
foreach (var l in LogicObjects)
l.Tick();
}
}

View File

@@ -227,6 +227,22 @@ namespace OpenRA.Widgets
return text;
}
public static string TruncateText(string text, int width, SpriteFont font)
{
var trimmedWidth = font.Measure(text).X;
if (trimmedWidth <= width)
return text;
var trimmed = text;
while (trimmedWidth > width && trimmed.Length > 3)
{
trimmed = text.Substring(0, trimmed.Length - 4) + "...";
trimmedWidth = font.Measure(trimmed).X;
}
return trimmed;
}
public static Action Once(Action a) { return () => { if (a != null) { a(); a = null; } }; }
public static string ChooseInitialMap(string initialUid)
@@ -242,6 +258,32 @@ namespace OpenRA.Widgets
}
}
public class CachedTransform<T, U>
{
readonly Func<T, U> transform;
bool initialized;
T lastInput;
U lastOutput;
public CachedTransform(Func<T, U> transform)
{
this.transform = transform;
}
public U Update(T input)
{
if (initialized && ((input == null && lastInput == null) || (input != null && input.Equals(lastInput))))
return lastOutput;
lastInput = input;
lastOutput = transform(input);
initialized = true;
return lastOutput;
}
}
[Flags]
public enum PanelSides
{

View File

@@ -120,12 +120,26 @@ namespace OpenRA.Widgets
}
}
}
else if (dragStart.HasValue)
else
{
// Select actors in the dragbox
var newSelection = SelectActorsInBoxWithDeadzone(World, dragStart.Value, xy);
World.Selection.Combine(World, newSelection, mi.Modifiers.HasModifier(Modifiers.Shift), dragStart == xy);
/* The block below does three things:
// 1. Allows actor selection using a selection box regardless of input mode.
// 2. Allows actor deselection with a single click in the default input mode (UnitOrderGenerator).
// 3. Prevents units from getting deselected when exiting input modes (eg. AttackMove or Guard).
//
// We cannot check for UnitOrderGenerator here since it's the default order generator that gets activated in
// World.CancelInputMode. If we did check it, actor de-selection would not be possible by just clicking somewhere,
// only by dragging an empty selection box.
*/
if (dragStart.HasValue && (!(World.OrderGenerator is GenericSelectTarget) || hasBox))
{
// Select actors in the dragbox
var newSelection = SelectActorsInBoxWithDeadzone(World, dragStart.Value, xy);
World.Selection.Combine(World, newSelection, mi.Modifiers.HasModifier(Modifiers.Shift), dragStart == xy);
}
}
World.CancelInputMode();
}
dragStart = dragEnd = null;

View File

@@ -63,7 +63,7 @@ namespace OpenRA.Mods.Cnc.Traits
new FacingInit(64)
});
actor.QueueActivity(new Fly(actor, Target.FromCell(w, self.Location + new CVec(9, 0))));
actor.QueueActivity(new Fly(actor, Target.FromCell(w, self.Location + new CVec(12, 0))));
actor.QueueActivity(new Land(actor, Target.FromActor(self)));
actor.QueueActivity(new CallFunc(() =>
{

View File

@@ -20,6 +20,7 @@ namespace OpenRA.Mods.Common.Activities
{
readonly Actor target;
readonly IDemolishable[] demolishables;
readonly EnterBehaviour enterBehaviour;
readonly int delay;
readonly int flashes;
readonly int flashesDelay;
@@ -28,11 +29,13 @@ namespace OpenRA.Mods.Common.Activities
readonly Cloak cloak;
public Demolish(Actor self, Actor target, int delay, int flashes, int flashesDelay, int flashInterval, int flashDuration)
public Demolish(Actor self, Actor target, EnterBehaviour enterBehaviour, int delay,
int flashes, int flashesDelay, int flashInterval, int flashDuration)
: base(self, target)
{
this.target = target;
demolishables = target.TraitsImplementing<IDemolishable>().ToArray();
this.enterBehaviour = enterBehaviour;
this.delay = delay;
this.flashes = flashes;
this.flashesDelay = flashesDelay;
@@ -72,6 +75,11 @@ namespace OpenRA.Mods.Common.Activities
if (Util.ApplyPercentageModifiers(100, modifiers) > 0)
demolishables.Do(d => d.Demolish(target, self));
}));
if (enterBehaviour == EnterBehaviour.Suicide)
self.Kill(self);
else if (enterBehaviour == EnterBehaviour.Dispose)
self.Dispose();
});
}
}

View File

@@ -15,6 +15,8 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Activities
{
public enum EnterBehaviour { Exit, Suicide, Dispose }
public abstract class Enter : Activity
{
public enum ReserveStatus { None, TooFar, Pending, Ready }

View File

@@ -24,7 +24,7 @@ using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class EditorActorBrush : IEditorBrush
public sealed class EditorActorBrush : IEditorBrush
{
public readonly ActorInfo Actor;
@@ -89,8 +89,13 @@ namespace OpenRA.Mods.Common.Widgets
if (mi.Button == MouseButton.Right)
{
editorWidget.ClearBrush();
return true;
if (mi.Event == MouseInputEvent.Up)
{
editorWidget.ClearBrush();
return true;
}
return false;
}
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location);
@@ -143,5 +148,7 @@ namespace OpenRA.Mods.Common.Widgets
preview.Bounds.Width = (int)(zoom * s.X);
preview.Bounds.Height = (int)(zoom * s.Y);
}
public void Dispose() { }
}
}

View File

@@ -0,0 +1,174 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Orders;
using OpenRA.Primitives;
using OpenRA.Traits;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public sealed class EditorCopyPasteBrush : IEditorBrush
{
enum State { SelectFirst, SelectSecond, Paste }
readonly WorldRenderer worldRenderer;
readonly EditorViewportControllerWidget editorWidget;
readonly EditorSelectionLayer selectionLayer;
readonly EditorActorLayer editorLayer;
State state;
CPos start;
CPos end;
public EditorCopyPasteBrush(EditorViewportControllerWidget editorWidget, WorldRenderer wr)
{
this.editorWidget = editorWidget;
worldRenderer = wr;
selectionLayer = wr.World.WorldActor.Trait<EditorSelectionLayer>();
editorLayer = wr.World.WorldActor.Trait<EditorActorLayer>();
}
public bool HandleMouseInput(MouseInput mi)
{
// Exclusively uses left and right mouse buttons, but nothing else
if (mi.Button != MouseButton.Left && mi.Button != MouseButton.Right)
return false;
if (mi.Button == MouseButton.Right)
{
if (mi.Event == MouseInputEvent.Up)
{
editorWidget.ClearBrush();
return true;
}
return false;
}
if (mi.Button == MouseButton.Left && (mi.Event == MouseInputEvent.Up || mi.Event == MouseInputEvent.Down))
{
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location);
switch (state)
{
case State.SelectFirst:
if (mi.Event != MouseInputEvent.Down)
break;
start = cell;
selectionLayer.SetCopyRegion(start, end);
state = State.SelectSecond;
break;
case State.SelectSecond:
if (mi.Event != MouseInputEvent.Up)
break;
end = cell;
selectionLayer.SetCopyRegion(start, end);
state = State.Paste;
break;
case State.Paste:
{
var gridType = worldRenderer.World.Map.Grid.Type;
var source = CellRegion.BoundingRegion(gridType, new[] { start, end });
Copy(source, cell - end);
editorWidget.ClearBrush();
break;
}
}
return true;
}
return false;
}
void Copy(CellRegion source, CVec offset)
{
var gridType = worldRenderer.World.Map.Grid.Type;
var mapTiles = worldRenderer.World.Map.MapTiles.Value;
var mapHeight = worldRenderer.World.Map.MapHeight.Value;
var mapResources = worldRenderer.World.Map.MapResources.Value;
var dest = new CellRegion(gridType, source.TopLeft + offset, source.BottomRight + offset);
var previews = new Dictionary<string, ActorReference>();
var tiles = new Dictionary<CPos, Tuple<TerrainTile, ResourceTile, byte>>();
foreach (var cell in source)
{
if (!mapTiles.Contains(cell) || !mapTiles.Contains(cell + offset))
continue;
tiles.Add(cell + offset, Tuple.Create(mapTiles[cell], mapResources[cell], mapHeight[cell]));
foreach (var preview in editorLayer.PreviewsAt(cell))
{
if (previews.ContainsKey(preview.ID))
continue;
var copy = preview.Export();
if (copy.InitDict.Contains<LocationInit>())
{
var location = copy.InitDict.Get<LocationInit>();
copy.InitDict.Remove(location);
copy.InitDict.Add(new LocationInit(location.Value(worldRenderer.World) + offset));
}
previews.Add(preview.ID, copy);
}
}
foreach (var kv in tiles)
{
mapTiles[kv.Key] = kv.Value.Item1;
mapResources[kv.Key] = kv.Value.Item2;
mapHeight[kv.Key] = kv.Value.Item3;
}
var removeActors = dest.SelectMany(editorLayer.PreviewsAt).Distinct().ToList();
foreach (var preview in removeActors)
editorLayer.Remove(preview);
foreach (var kv in previews)
editorLayer.Add(kv.Value);
}
public void Tick()
{
var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
if (state == State.Paste)
{
selectionLayer.SetPasteRegion(cell + (start - end), cell);
return;
}
if (state == State.SelectFirst)
start = end = cell;
else if (state == State.SelectSecond)
end = cell;
selectionLayer.SetCopyRegion(start, end);
}
public void Dispose()
{
selectionLayer.Clear();
}
}
}

View File

@@ -24,13 +24,13 @@ using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public interface IEditorBrush
public interface IEditorBrush : IDisposable
{
bool HandleMouseInput(MouseInput mi);
void Tick();
}
public class EditorDefaultBrush : IEditorBrush
public sealed class EditorDefaultBrush : IEditorBrush
{
public readonly ActorInfo Actor;
@@ -55,12 +55,11 @@ namespace OpenRA.Mods.Common.Widgets
{
// Exclusively uses mouse wheel and right mouse buttons, but nothing else
// Mouse move events are important for tooltips, so we always allow these through
if (mi.Button != MouseButton.Right && mi.Event != MouseInputEvent.Move && mi.Event != MouseInputEvent.Scroll)
if ((mi.Button != MouseButton.Right && mi.Event != MouseInputEvent.Move && mi.Event != MouseInputEvent.Scroll) ||
mi.Event == MouseInputEvent.Down)
return false;
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location);
if (mi.Event == MouseInputEvent.Up)
return true;
var underCursor = editorLayer.PreviewsAt(worldRenderer.Viewport.ViewToWorldPx(mi.Location))
.FirstOrDefault();
@@ -111,5 +110,6 @@ namespace OpenRA.Mods.Common.Widgets
}
public void Tick() { }
public void Dispose() { }
}
}

View File

@@ -24,7 +24,7 @@ using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class EditorResourceBrush : IEditorBrush
public sealed class EditorResourceBrush : IEditorBrush
{
public readonly ResourceTypeInfo ResourceType;
@@ -64,8 +64,13 @@ namespace OpenRA.Mods.Common.Widgets
if (mi.Button == MouseButton.Right)
{
editorWidget.ClearBrush();
return true;
if (mi.Event == MouseInputEvent.Up)
{
editorWidget.ClearBrush();
return true;
}
return false;
}
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location);
@@ -114,5 +119,7 @@ namespace OpenRA.Mods.Common.Widgets
preview.Bounds.X = cellScreenPixel.X;
preview.Bounds.Y = cellScreenPixel.Y;
}
public void Dispose() { }
}
}

View File

@@ -24,7 +24,7 @@ using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class EditorTileBrush : IEditorBrush
public sealed class EditorTileBrush : IEditorBrush
{
public readonly ushort Template;
@@ -64,8 +64,13 @@ namespace OpenRA.Mods.Common.Widgets
if (mi.Button == MouseButton.Right)
{
editorWidget.ClearBrush();
return true;
if (mi.Event == MouseInputEvent.Up)
{
editorWidget.ClearBrush();
return true;
}
return false;
}
if (mi.Button == MouseButton.Left)
@@ -151,5 +156,7 @@ namespace OpenRA.Mods.Common.Widgets
preview.Bounds.Width = (int)(zoom * bounds.Width);
preview.Bounds.Height = (int)(zoom * bounds.Height);
}
public void Dispose() { }
}
}

View File

@@ -35,10 +35,10 @@ namespace OpenRA.Mods.Common.Effects
public readonly bool Shadow = false;
[Desc("Minimum vertical launch angle (pitch).")]
public readonly WAngle MinimumLaunchAngle = WAngle.Zero;
public readonly WAngle MinimumLaunchAngle = new WAngle(-64);
[Desc("Maximum vertical launch angle (pitch).")]
public readonly WAngle MaximumLaunchAngle = new WAngle(64);
public readonly WAngle MaximumLaunchAngle = new WAngle(128);
[Desc("Minimum launch speed in WDist / tick")]
public readonly WDist MinimumLaunchSpeed = new WDist(75);
@@ -50,7 +50,7 @@ namespace OpenRA.Mods.Common.Effects
public readonly WDist MaximumSpeed = new WDist(384);
[Desc("Projectile acceleration when propulsion activated.")]
public readonly WDist Acceleration = new WDist(5);
public readonly WDist Acceleration = WDist.Zero;
[Desc("How many ticks before this missile is armed and can explode.")]
public readonly int Arm = 0;

View File

@@ -705,6 +705,8 @@
<Compile Include="Lint\CheckChromeLogic.cs" />
<Compile Include="Lint\CheckMapMetadata.cs" />
<Compile Include="Widgets\Logic\MultiplayerLogic.cs" />
<Compile Include="EditorBrushes\EditorCopyPasteBrush.cs" />
<Compile Include="Traits\World\EditorSelectionLayer.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View File

@@ -24,7 +24,7 @@ namespace OpenRA.Mods.Common.Orders
public AircraftMoveOrderTargeter(AircraftInfo info) { this.info = info; }
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor)
{
if (target.Type != TargetType.Terrain)
return false;

View File

@@ -34,7 +34,7 @@ namespace OpenRA.Mods.Common.Orders
public int OrderPriority { get; private set; }
public bool OverrideSelection { get { return true; } }
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor)
{
if (target.Type != TargetType.Actor)
return false;

View File

@@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.Orders
public abstract bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor);
public abstract bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor);
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor)
{
var type = target.Type;
if (type != TargetType.Actor && type != TargetType.FrozenActor)

View File

@@ -34,8 +34,8 @@ namespace OpenRA.Mods.Common.Scripting
[Desc("Demolish the target actor.")]
public void Demolish(Actor target)
{
Self.QueueActivity(new Demolish(Self, target, info.C4Delay, info.Flashes,
info.FlashesDelay, info.FlashInterval, info.FlashDuration));
Self.QueueActivity(new Demolish(Self, target, info.EnterBehaviour, info.C4Delay,
info.Flashes, info.FlashesDelay, info.FlashInterval, info.FlashDuration));
}
}
}

View File

@@ -24,6 +24,9 @@ namespace OpenRA.Mods.Common.Scripting
public PlayerProperties(ScriptContext context, Player player)
: base(context, player) { }
[Desc("The player's internal name.")]
public string InternalName { get { return Player.InternalName; } }
[Desc("The player's name.")]
public string Name { get { return Player.PlayerName; } }
@@ -52,6 +55,12 @@ namespace OpenRA.Mods.Common.Scripting
[Desc("Returns true if the player is a bot.")]
public bool IsBot { get { return Player.IsBot; } }
[Desc("Returns true if the player is non combatant.")]
public bool IsNonCombatant { get { return Player.NonCombatant; } }
[Desc("Returns true if the player is the local player.")]
public bool IsLocalPlayer { get { return Player == (Player.World.RenderPlayer ?? Player.World.LocalPlayer); } }
[Desc("Returns an array of actors representing all ground attack units of this player.")]
public Actor[] GetGroundAttackers()
{

View File

@@ -112,7 +112,7 @@ namespace OpenRA.Mods.Common.Scripting
if (actionFunc != null)
{
var playerIndex = Self.Owner.ClientIndex;
var player = Self.Owner;
var squadSize = actorTypes.Length;
var squad = new List<Actor>();
var func = actionFunc.CopyReference() as LuaFunction;
@@ -120,7 +120,7 @@ namespace OpenRA.Mods.Common.Scripting
Action<Actor, Actor> productionHandler = (_, __) => { };
productionHandler = (factory, unit) =>
{
if (playerIndex != factory.Owner.ClientIndex)
if (player != factory.Owner)
{
triggers.OnProducedInternal -= productionHandler;
return;

View File

@@ -135,6 +135,10 @@ namespace OpenRA.Mods.Common.Server
client.Slot = s;
S.SyncClientToPlayerReference(client, server.MapPlayers.Players[s]);
var validatedColor = ColorValidator.ValidatePlayerColorAndGetAlternative(server, client.Color, client.Index, conn);
client.PreferredColor = client.Color = validatedColor;
server.SyncLobbyClients();
CheckAutoStart(server);

View File

@@ -89,6 +89,9 @@ namespace OpenRA.Mods.Common.Traits
if (self.IsDisabled())
return false;
if (target.Type == TargetType.Actor && !self.Owner.CanTargetActor(target.Actor))
return false;
return true;
}
@@ -279,13 +282,21 @@ namespace OpenRA.Mods.Common.Traits
public int OrderPriority { get; private set; }
public bool OverrideSelection { get { return true; } }
bool CanTargetActor(Actor self, Target target, TargetModifiers modifiers, ref string cursor)
bool CanTargetActor(Actor self, Target target, ref TargetModifiers modifiers, ref string cursor)
{
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
if (modifiers.HasModifier(TargetModifiers.ForceMove))
return false;
// Disguised actors are revealed by the attack cursor
// HACK: works around limitations in the targeting code that force the
// targeting and attacking logic (which should be logically separate)
// to use the same code
if (target.Type == TargetType.Actor && target.Actor.EffectiveOwner != null &&
target.Actor.EffectiveOwner.Disguised && self.Owner.Stances[target.Actor.Owner] == Stance.Enemy)
modifiers |= TargetModifiers.ForceAttack;
var forceAttack = modifiers.HasModifier(TargetModifiers.ForceAttack);
var a = ab.ChooseArmamentsForTarget(target, forceAttack).FirstOrDefault();
if (a == null)
@@ -326,13 +337,13 @@ namespace OpenRA.Mods.Common.Traits
return true;
}
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor)
{
switch (target.Type)
{
case TargetType.Actor:
case TargetType.FrozenActor:
return CanTargetActor(self, target, modifiers, ref cursor);
return CanTargetActor(self, target, ref modifiers, ref cursor);
case TargetType.Terrain:
return CanTargetLocation(self, self.World.Map.CellContaining(target.CenterPosition), othersAtTarget, modifiers, ref cursor);
default:

View File

@@ -190,7 +190,7 @@ namespace OpenRA.Mods.Common.Traits
&& !target.IsInRange(self.CenterPosition, arm.Weapon.MinRange))), a);
})
.Where(kv => kv.Key != null && self.Owner.CanViewActor(kv.Value))
.Where(kv => kv.Key != null && (self.Owner.HasFogVisibility || self.Owner.CanViewActor(kv.Value)))
.GroupBy(kv => kv.Key, kv => kv.Value)
.ToDictionary(kv => kv.Key, kv => kv.ClosestTo(self));

View File

@@ -38,10 +38,15 @@ namespace OpenRA.Mods.Common.Traits
public RallyPointInfo Info;
public string PaletteName { get; private set; }
public void ResetLocation(Actor self)
{
Location = self.Location + Info.Offset;
}
public RallyPoint(Actor self, RallyPointInfo info)
{
Info = info;
Location = self.Location + info.Offset;
ResetLocation(self);
PaletteName = info.IsPlayerPalette ? info.Palette + self.Owner.InternalName : info.Palette;
self.World.Add(new RallyPointIndicator(self, this));
}
@@ -50,6 +55,8 @@ namespace OpenRA.Mods.Common.Traits
{
if (Info.IsPlayerPalette)
PaletteName = Info.Palette + newOwner.InternalName;
ResetLocation(self);
}
public IEnumerable<IOrderTargeter> Orders
@@ -77,7 +84,7 @@ namespace OpenRA.Mods.Common.Traits
public int OrderPriority { get { return 0; } }
public bool OverrideSelection { get { return true; } }
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor)
{
if (target.Type != TargetType.Terrain)
return false;

View File

@@ -23,18 +23,22 @@ namespace OpenRA.Mods.Common.Traits
"Measured in game ticks. Default is 1.8 seconds.")]
public readonly int C4Delay = 45;
[Desc("Number of times to flash the target")]
[Desc("Number of times to flash the target.")]
public readonly int Flashes = 3;
[Desc("Delay before the flashing starts")]
[Desc("Delay before the flashing starts.")]
public readonly int FlashesDelay = 4;
[Desc("Interval between each flash")]
[Desc("Interval between each flash.")]
public readonly int FlashInterval = 4;
[Desc("Duration of each flash")]
[Desc("Duration of each flash.")]
public readonly int FlashDuration = 3;
[Desc("Behaviour when entering the structure.",
"Possible values are Exit, Suicide, Dispose.")]
public readonly EnterBehaviour EnterBehaviour = EnterBehaviour.Exit;
[Desc("Voice string when planting explosive charges.")]
[VoiceReference] public readonly string Voice = "Action";
@@ -83,8 +87,8 @@ namespace OpenRA.Mods.Common.Traits
self.CancelActivity();
self.SetTargetLine(target, Color.Red);
self.QueueActivity(new Demolish(self,
target.Actor, info.C4Delay, info.Flashes, info.FlashesDelay, info.FlashInterval, info.FlashDuration));
self.QueueActivity(new Demolish(self, target.Actor, info.EnterBehaviour, info.C4Delay,
info.Flashes, info.FlashesDelay, info.FlashInterval, info.FlashDuration));
}
public string VoicePhraseForOrder(Actor self, Order order)

View File

@@ -14,6 +14,7 @@ using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Activities;
using OpenRA.Orders;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
@@ -59,52 +60,40 @@ namespace OpenRA.Mods.Common.Traits
}
}
public class GuardOrderGenerator : IOrderGenerator
public class GuardOrderGenerator : GenericSelectTarget
{
readonly IEnumerable<Actor> subjects;
public GuardOrderGenerator(IEnumerable<Actor> subjects, string order, string cursor, MouseButton button)
: base(subjects, order, cursor, button) { }
public GuardOrderGenerator(IEnumerable<Actor> subjects)
protected override IEnumerable<Order> OrderInner(World world, CPos xy, MouseInput mi)
{
this.subjects = subjects;
}
public IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
{
if (mi.Button == Game.Settings.Game.MouseButtonPreference.Cancel)
{
world.CancelInputMode();
yield break;
}
var target = FriendlyGuardableUnits(world, mi).FirstOrDefault();
if (target == null || subjects.All(s => s.IsDead))
if (target == null || Subjects.All(s => s.IsDead))
yield break;
foreach (var subject in subjects)
world.CancelInputMode();
foreach (var subject in Subjects)
if (subject != target)
yield return new Order("Guard", subject, false) { TargetActor = target };
yield return new Order(OrderName, subject, false) { TargetActor = target };
}
public void Tick(World world)
public override void Tick(World world)
{
if (subjects.All(s => s.IsDead || !s.Info.HasTraitInfo<GuardInfo>()))
if (Subjects.All(s => s.IsDead || !s.Info.HasTraitInfo<GuardInfo>()))
world.CancelInputMode();
}
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
public IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr, World world) { yield break; }
public string GetCursor(World world, CPos xy, MouseInput mi)
public override string GetCursor(World world, CPos xy, MouseInput mi)
{
if (!subjects.Any())
if (!Subjects.Any())
return null;
var multiple = subjects.Count() > 1;
var multiple = Subjects.Count() > 1;
var canGuard = FriendlyGuardableUnits(world, mi)
.Any(a => multiple || a != subjects.First());
.Any(a => multiple || a != Subjects.First());
return canGuard ? "guard" : "move-blocked";
return canGuard ? Cursor : "move-blocked";
}
static IEnumerable<Actor> FriendlyGuardableUnits(World world, MouseInput mi)

View File

@@ -454,7 +454,7 @@ namespace OpenRA.Mods.Common.Traits
public bool IsQueued { get; protected set; }
public bool OverrideSelection { get { return true; } }
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor)
{
if (target.Type != TargetType.Terrain)
return false;

View File

@@ -706,7 +706,7 @@ namespace OpenRA.Mods.Common.Traits
public int OrderPriority { get { return 4; } }
public bool IsQueued { get; protected set; }
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor)
{
if (rejectMove || !target.IsValidFor(self))
return false;

View File

@@ -27,7 +27,7 @@ namespace OpenRA.Mods.Common.Traits
public object Create(ActorInitializer init) { return new FrozenUnderFog(init, this); }
}
public class FrozenUnderFog : IRenderModifier, IDefaultVisibility, ITick, ISync
public class FrozenUnderFog : IRenderModifier, IDefaultVisibility, ITick, ITickRender, ISync
{
[Sync] public int VisibilityHash;
@@ -41,6 +41,7 @@ namespace OpenRA.Mods.Common.Traits
readonly Dictionary<Player, FrozenState> stateByPlayer = new Dictionary<Player, FrozenState>();
bool initialized;
bool isRendering;
class FrozenState
{
@@ -132,11 +133,35 @@ namespace OpenRA.Mods.Common.Traits
initialized = true;
}
public void TickRender(WorldRenderer wr, Actor self)
{
if (!initialized)
return;
IRenderable[] renderables = null;
foreach (var player in self.World.Players)
{
var frozen = stateByPlayer[player].FrozenActor;
if (!frozen.NeedRenderables)
continue;
if (renderables == null)
{
isRendering = true;
renderables = self.Render(wr).ToArray();
isRendering = false;
}
frozen.NeedRenderables = false;
frozen.Renderables = renderables;
}
}
public IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r)
{
return
IsVisible(self, self.World.RenderPlayer) ||
(initialized && stateByPlayer[self.World.RenderPlayer].FrozenActor.IsRendering) ?
(initialized && isRendering) ?
r : SpriteRenderable.None;
}
}

View File

@@ -10,6 +10,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Traits;
@@ -59,19 +60,24 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Should this be visible to enemy players?")]
public readonly bool ShowToEnemies = false;
[Desc("Should this be visible only when selected?")]
public readonly bool SelectionDecoration = false;
public override object Create(ActorInitializer init) { return new WithDecoration(init.Self, this); }
}
public class WithDecoration : UpgradableTrait<WithDecorationInfo>, IRender
public class WithDecoration : UpgradableTrait<WithDecorationInfo>, IRender, IPostRenderSelection
{
readonly WithDecorationInfo info;
readonly string image;
readonly Animation anim;
readonly Actor self;
public WithDecoration(Actor self, WithDecorationInfo info)
: base(info)
{
this.info = info;
this.self = self;
image = info.Image ?? self.Info.Name;
anim = new Animation(self.World, image);
anim.Paused = () => self.World.Paused;
@@ -86,6 +92,16 @@ namespace OpenRA.Mods.Common.Traits
public virtual bool ShouldRender(Actor self) { return true; }
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
{
return !info.SelectionDecoration ? RenderInner(self, wr, self.Bounds) : Enumerable.Empty<IRenderable>();
}
public IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr)
{
return info.SelectionDecoration ? RenderInner(self, wr, self.VisualBounds) : Enumerable.Empty<IRenderable>();
}
IEnumerable<IRenderable> RenderInner(Actor self, WorldRenderer wr, Rectangle actorBounds)
{
if (IsTraitDisabled)
return Enumerable.Empty<IRenderable>();
@@ -111,7 +127,6 @@ namespace OpenRA.Mods.Common.Traits
return Enumerable.Empty<IRenderable>();
var pxPos = wr.ScreenPxPosition(self.CenterPosition);
var actorBounds = self.Bounds;
actorBounds.Offset(pxPos.X, pxPos.Y);
var img = anim.Image;

View File

@@ -65,7 +65,8 @@ namespace OpenRA.Mods.Common.Traits
{
var cell = Util.RandomWalk(self.Location, self.World.SharedRandom)
.Take(info.MaxRange)
.SkipWhile(p => resLayer.GetResource(p) == resourceType && resLayer.IsFull(p))
.SkipWhile(p => !self.World.Map.Contains(p) ||
(resLayer.GetResource(p) == resourceType && resLayer.IsFull(p)))
.Cast<CPos?>().FirstOrDefault();
if (cell != null && resLayer.CanSpawnResourceAt(resourceType, cell.Value))

View File

@@ -30,7 +30,8 @@ namespace OpenRA.Mods.Common.Traits
public readonly int Range = 1;
public readonly string GrantUpgradeSound = "ironcur9.aud";
[Desc("Sequence to play for granting actor when activated."), SequenceReference]
[SequenceReference, Desc("Sequence to play for granting actor when activated.",
"This requires the actor to have the WithSpriteBody trait or one of its derivatives.")]
public readonly string GrantUpgradeSequence = "active";
public override object Create(ActorInitializer init) { return new GrantUpgradePower(init.Self, this); }
@@ -56,7 +57,9 @@ namespace OpenRA.Mods.Common.Traits
{
base.Activate(self, order, manager);
self.Trait<WithSpriteBody>().PlayCustomAnimation(self, info.GrantUpgradeSequence);
var wsb = self.TraitOrDefault<WithSpriteBody>();
if (wsb != null && wsb.DefaultAnimation.HasSequence(info.GrantUpgradeSequence))
wsb.PlayCustomAnimation(self, info.GrantUpgradeSequence);
Game.Sound.Play(info.GrantUpgradeSound, self.World.Map.CenterOfCell(order.TargetLocation));

View File

@@ -9,6 +9,7 @@
#endregion
using System.Collections.Generic;
using OpenRA.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
using OpenRA.Traits;
@@ -95,9 +96,10 @@ namespace OpenRA.Mods.Common.Traits
public void DeployTransform(bool queued)
{
var building = self.TraitOrDefault<Building>();
if (!CanDeploy() || (building != null && !building.Lock()))
if (!queued && !CanDeploy())
{
// Only play the "Cannot deploy here" audio
// for non-queued orders
foreach (var s in info.NoTransformSounds)
Game.Sound.PlayToPlayer(self.Owner, s);
@@ -115,23 +117,31 @@ namespace OpenRA.Mods.Common.Traits
if (self.Info.HasTraitInfo<AircraftInfo>())
self.QueueActivity(new HeliLand(self, true));
foreach (var nt in self.TraitsImplementing<INotifyTransform>())
nt.BeforeTransform(self);
var transform = new Transform(self, info.IntoActor)
self.QueueActivity(new CallFunc(() =>
{
Offset = info.Offset,
Facing = info.Facing,
Sounds = info.TransformSounds,
Notification = info.TransformNotification,
Faction = faction
};
// Prevent deployment in bogus locations
var building = self.TraitOrDefault<Building>();
if (!CanDeploy() || (building != null && !building.Lock()))
return;
var makeAnimation = self.TraitOrDefault<WithMakeAnimation>();
if (makeAnimation != null)
makeAnimation.Reverse(self, transform);
else
self.QueueActivity(transform);
foreach (var nt in self.TraitsImplementing<INotifyTransform>())
nt.BeforeTransform(self);
var transform = new Transform(self, info.IntoActor)
{
Offset = info.Offset,
Facing = info.Facing,
Sounds = info.TransformSounds,
Notification = info.TransformNotification,
Faction = faction
};
var makeAnimation = self.TraitOrDefault<WithMakeAnimation>();
if (makeAnimation != null)
makeAnimation.Reverse(self, transform);
else
self.QueueActivity(transform);
}));
}
public void ResolveOrder(Actor self, Order order)

View File

@@ -10,7 +10,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
@@ -20,9 +19,13 @@ namespace OpenRA.Mods.Common.Traits
{
public class DeployToUpgradeInfo : ITraitInfo, Requires<UpgradeManagerInfo>
{
[UpgradeGrantedReference]
[Desc("The upgrades to grant while the actor is undeployed.")]
public readonly string[] UndeployedUpgrades = { };
[UpgradeGrantedReference, FieldLoader.Require]
[Desc("The upgrades to grant when deploying and revoke when undeploying.")]
public readonly string[] Upgrades = { };
[Desc("The upgrades to grant after deploying and revoke before undeploying.")]
public readonly string[] DeployedUpgrades = { };
[Desc("The terrain types that this actor can deploy on to receive these upgrades. " +
"Leave empty to allow any.")]
@@ -52,8 +55,10 @@ namespace OpenRA.Mods.Common.Traits
public object Create(ActorInitializer init) { return new DeployToUpgrade(init.Self, this); }
}
public class DeployToUpgrade : IResolveOrder, IIssueOrder
public class DeployToUpgrade : IResolveOrder, IIssueOrder, INotifyCreated
{
enum DeployState { Undeployed, Deploying, Deployed }
readonly Actor self;
readonly DeployToUpgradeInfo info;
readonly UpgradeManager manager;
@@ -61,7 +66,7 @@ namespace OpenRA.Mods.Common.Traits
readonly bool canTurn;
readonly Lazy<ISpriteBody> body;
bool isUpgraded;
DeployState deployState;
public DeployToUpgrade(Actor self, DeployToUpgradeInfo info)
{
@@ -73,6 +78,11 @@ namespace OpenRA.Mods.Common.Traits
body = Exts.Lazy(self.TraitOrDefault<ISpriteBody>);
}
public void Created(Actor self)
{
OnUndeployCompleted();
}
public IEnumerable<IOrderTargeter> Orders
{
get { yield return new DeployOrderTargeter("DeployToUpgrade", 5,
@@ -89,59 +99,24 @@ namespace OpenRA.Mods.Common.Traits
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString != "DeployToUpgrade")
if (order.OrderString != "DeployToUpgrade" || deployState == DeployState.Deploying)
return;
if (!IsOnValidTerrain())
return;
if (isUpgraded)
{
// Play undeploy animation and after that revoke the upgrades
self.QueueActivity(false, new CallFunc(() =>
{
if (!string.IsNullOrEmpty(info.UndeploySound))
Game.Sound.Play(info.UndeploySound, self.CenterPosition);
if (string.IsNullOrEmpty(info.DeployAnimation))
{
RevokeUpgrades();
return;
}
if (body.Value != null)
body.Value.PlayCustomAnimationBackwards(self, info.DeployAnimation, RevokeUpgrades);
else
RevokeUpgrades();
}));
}
else
{
if (!order.Queued)
self.CancelActivity();
// Turn
if (deployState == DeployState.Deployed)
{
self.QueueActivity(new CallFunc(Undeploy));
}
else if (deployState == DeployState.Undeployed)
{
// Turn to the required facing.
if (info.Facing != -1 && canTurn)
self.QueueActivity(new Turn(self, info.Facing));
// Grant the upgrade
self.QueueActivity(new CallFunc(GrantUpgrades));
// Play deploy sound and animation
self.QueueActivity(new CallFunc(() =>
{
if (!string.IsNullOrEmpty(info.DeploySound))
Game.Sound.Play(info.DeploySound, self.CenterPosition);
if (string.IsNullOrEmpty(info.DeployAnimation))
return;
if (body.Value != null)
body.Value.PlayCustomAnimation(self, info.DeployAnimation,
() => body.Value.PlayCustomAnimationRepeating(self, "idle"));
}));
self.QueueActivity(new CallFunc(Deploy));
}
isUpgraded = !isUpgraded;
}
bool IsOnValidTerrain()
@@ -181,16 +156,80 @@ namespace OpenRA.Mods.Common.Traits
return ramp == 0;
}
void GrantUpgrades()
/// <summary>Play deploy sound and animation.</summary>
void Deploy()
{
foreach (var up in info.Upgrades)
manager.GrantUpgrade(self, up, this);
// Something went wrong, most likely due to deploy order spam and the fact that this is a delayed action.
if (deployState != DeployState.Undeployed)
return;
if (!IsOnValidTerrain())
return;
if (!string.IsNullOrEmpty(info.DeploySound))
Game.Sound.Play(info.DeploySound, self.CenterPosition);
// Revoke upgrades that are used while undeployed.
OnDeployStarted();
// If there is no animation to play just grant the upgrades that are used while deployed.
// Alternatively, play the deploy animation and then grant the upgrades.
if (string.IsNullOrEmpty(info.DeployAnimation) || body.Value == null)
OnDeployCompleted();
else
body.Value.PlayCustomAnimation(self, info.DeployAnimation, OnDeployCompleted);
}
void RevokeUpgrades()
/// <summary>Play undeploy sound and animation and after that revoke the upgrades.</summary>
void Undeploy()
{
foreach (var up in info.Upgrades)
// Something went wrong, most likely due to deploy order spam and the fact that this is a delayed action.
if (deployState != DeployState.Deployed)
return;
if (!string.IsNullOrEmpty(info.UndeploySound))
Game.Sound.Play(info.UndeploySound, self.CenterPosition);
OnUndeployStarted();
// If there is no animation to play just grant the upgrades that are used while undeployed.
// Alternatively, play the undeploy animation and then grant the upgrades.
if (string.IsNullOrEmpty(info.DeployAnimation))
OnUndeployCompleted();
else
body.Value.PlayCustomAnimationBackwards(self, info.DeployAnimation, OnUndeployCompleted);
}
void OnDeployStarted()
{
deployState = DeployState.Deploying;
foreach (var up in info.UndeployedUpgrades)
manager.RevokeUpgrade(self, up, this);
}
void OnDeployCompleted()
{
foreach (var up in info.DeployedUpgrades)
manager.GrantUpgrade(self, up, this);
deployState = DeployState.Deployed;
}
void OnUndeployStarted()
{
foreach (var up in info.DeployedUpgrades)
manager.RevokeUpgrade(self, up, this);
deployState = DeployState.Deploying;
}
void OnUndeployCompleted()
{
deployState = DeployState.Undeployed;
foreach (var up in info.UndeployedUpgrades)
manager.GrantUpgrade(self, up, this);
}
}
}

View File

@@ -88,7 +88,7 @@ namespace OpenRA.Mods.Common.Traits
void ActorEntered(Actor a)
{
if (a.Disposed)
if (a.Disposed || self.Disposed)
return;
if (a == self && !info.AffectsParent)
@@ -106,6 +106,10 @@ namespace OpenRA.Mods.Common.Traits
public void UnitProducedByOther(Actor self, Actor producer, Actor produced)
{
// If the produced Actor doesn't occupy space, it can't be in range
if (produced.OccupiesSpace == null)
return;
// Work around for actors produced within the region not triggering until the second tick
if ((produced.CenterPosition - self.CenterPosition).HorizontalLengthSquared <= info.Range.LengthSquared)
{
@@ -123,7 +127,7 @@ namespace OpenRA.Mods.Common.Traits
void ActorExited(Actor a)
{
if (a == self || a.Disposed)
if (a == self || a.Disposed || self.Disposed)
return;
var stance = self.Owner.Stances[a.Owner];

View File

@@ -186,6 +186,10 @@ namespace OpenRA.Mods.Common.Traits
Players.Players.Add(pr.Name, pr);
worldRenderer.UpdatePalettesForPlayer(pr.Name, pr.Color, true);
}
var creeps = Players.Players.Keys.FirstOrDefault(p => p == "Creeps");
if (!string.IsNullOrEmpty(creeps))
Players.Players[creeps].Enemies = Players.Players.Keys.Where(p => !Players.Players[p].NonCombatant).ToArray();
}
void UpdateNeighbours(IReadOnlyDictionary<CPos, SubCell> footprint)

View File

@@ -70,7 +70,7 @@ namespace OpenRA.Mods.Common.Traits
}
var tooltip = Info.TraitInfoOrDefault<TooltipInfo>();
Tooltip = tooltip == null ? ID + ": " + Info.Name : ID + ": " + tooltip.Name + " (" + Info.Name + ")" + "\n" + owner.Name + " (" + owner.Faction + ")";
Tooltip = tooltip == null ? ID + ": " + Info.Name : ID + ": " + tooltip.Name + " (" + Info.Name + ")";
GeneratePreviews();
@@ -160,5 +160,10 @@ namespace OpenRA.Mods.Common.Traits
.SelectMany(rpi => rpi.RenderPreview(init))
.ToArray();
}
public ActorReference Export()
{
return new ActorReference(actor.Type, actor.Save().ToDictionary());
}
}
}

View File

@@ -207,6 +207,8 @@ namespace OpenRA.Mods.Common.Traits
foreach (var kv in spriteLayers.Values)
kv.Dispose();
Map.MapResources.Value.CellEntryChanged -= UpdateCell;
disposed = true;
}
}

View File

@@ -0,0 +1,105 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Required for the map editor to work. Attach this to the world actor.")]
public class EditorSelectionLayerInfo : ITraitInfo
{
[PaletteReference]
[Desc("Palette to use for rendering the placement sprite.")]
public readonly string Palette = "terrain";
[Desc("Sequence image where the selection overlay types are defined.")]
public readonly string Image = "editor-overlay";
[SequenceReference("Image")]
[Desc("Sequence to use for the copy overlay.")]
public readonly string CopySequence = "copy";
[SequenceReference("Image")]
[Desc("Sequence to use for the paste overlay.")]
public readonly string PasteSequence = "paste";
public virtual object Create(ActorInitializer init) { return new EditorSelectionLayer(init.Self, this); }
}
public class EditorSelectionLayer : IWorldLoaded, IPostRender
{
readonly EditorSelectionLayerInfo info;
readonly Map map;
readonly Sprite copySprite;
readonly Sprite pasteSprite;
PaletteReference palette;
public CellRegion CopyRegion { get; private set; }
public CellRegion PasteRegion { get; private set; }
public EditorSelectionLayer(Actor self, EditorSelectionLayerInfo info)
{
if (self.World.Type != WorldType.Editor)
return;
this.info = info;
map = self.World.Map;
copySprite = map.SequenceProvider.GetSequence(info.Image, info.CopySequence).GetSprite(0);
pasteSprite = map.SequenceProvider.GetSequence(info.Image, info.PasteSequence).GetSprite(0);
}
public void WorldLoaded(World w, WorldRenderer wr)
{
if (w.Type != WorldType.Editor)
return;
palette = wr.Palette(info.Palette);
}
public void SetCopyRegion(CPos start, CPos end)
{
CopyRegion = CellRegion.BoundingRegion(map.Grid.Type, new[] { start, end });
}
public void SetPasteRegion(CPos start, CPos end)
{
PasteRegion = CellRegion.BoundingRegion(map.Grid.Type, new[] { start, end });
}
public void Clear()
{
CopyRegion = PasteRegion = null;
}
public void RenderAfterWorld(WorldRenderer wr, Actor self)
{
if (wr.World.Type != WorldType.Editor)
return;
if (CopyRegion != null)
foreach (var c in CopyRegion)
new SpriteRenderable(copySprite, wr.World.Map.CenterOfCell(c),
WVec.Zero, -511, palette, 1f, true).PrepareRender(wr).Render(wr);
if (PasteRegion != null)
foreach (var c in PasteRegion)
new SpriteRenderable(pasteSprite, wr.World.Map.CenterOfCell(c),
WVec.Zero, -511, palette, 1f, true).PrepareRender(wr).Render(wr);
}
}
}

View File

@@ -294,6 +294,8 @@ namespace OpenRA.Mods.Common.Traits
foreach (var kv in spriteLayers.Values)
kv.Dispose();
RenderContent.CellEntryChanged -= UpdateSpriteLayers;
disposed = true;
}

View File

@@ -35,11 +35,11 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Hard or soft fading between the WindLevels.")]
public readonly bool InstantWindChanges = false;
[Desc("Particles are drawn in squares when enabled, else with lines.")]
[Desc("Particles are drawn in squares when enabled, otherwise with lines.")]
public readonly bool UseSquares = true;
[Desc("Works only with squares enabled. Size min. and max. value in pixels.")]
public readonly int[] PaticleSize = { 1, 3 };
public readonly int[] ParticleSize = { 1, 3 };
[Desc("Scatters falling direction on the x-axis. Scatter min. and max. value in px/tick.")]
public readonly int[] ScatterDirection = { -1, 1 };
@@ -64,7 +64,7 @@ namespace OpenRA.Mods.Common.Traits
Color.FromArgb(255, 188, 188, 188)
};
[Desc("Works only with line enabled and can get used to fade out the tail of the line like a contrail.")]
[Desc("Works only with line enabled and can be used to fade out the tail of the line like a contrail.")]
public readonly byte LineTailAlphaValue = 200;
public object Create(ActorInitializer init) { return new WeatherOverlay(init.World, this); }
@@ -130,7 +130,7 @@ namespace OpenRA.Mods.Common.Traits
{
PosX = Game.CosmeticRandom.Next(Game.Renderer.Resolution.Width),
PosY = Game.CosmeticRandom.Next(rangeY),
Size = Game.CosmeticRandom.Next(info.PaticleSize[0], info.PaticleSize[1] + 1),
Size = Game.CosmeticRandom.Next(info.ParticleSize[0], info.ParticleSize[1] + 1),
DirectionScatterX = info.ScatterDirection[0] + Game.CosmeticRandom.Next(info.ScatterDirection[1] - info.ScatterDirection[0]),
Gravity = float2.Lerp(info.Gravity[0], info.Gravity[1], Game.CosmeticRandom.NextFloat()),
SwingOffset = float2.Lerp(info.SwingOffset[0], info.SwingOffset[1], Game.CosmeticRandom.NextFloat()),

View File

@@ -31,6 +31,10 @@ namespace OpenRA.Mods.Common.UtilityCommands
"Convert a shp/tmp/R8 to a series of PNGs, optionally removing shadow")]
public void Run(ModData modData, string[] args)
{
// HACK: The engine code assumes that Game.modData is set.
Game.ModData = modData;
Game.ModData.MountFiles();
var src = args[1];
var shadowIndex = new int[] { };
if (args.Contains("--noshadow"))

View File

@@ -1611,7 +1611,15 @@ namespace OpenRA.Mods.Common.UtilityCommands
var otherNodes = nodes;
var inherits = new Func<string, bool>(traitName => node.Value.Nodes.Where(n => n.Key.StartsWith("Inherits"))
.Any(inh => otherNodes.First(n => n.Key.StartsWith(inh.Value.Value)).Value.Nodes.Any(n => n.Key.StartsWith(traitName))));
.Any(inh =>
{
var otherNode = otherNodes.FirstOrDefault(n => n.Key.StartsWith(inh.Value.Value));
if (otherNode == null)
return false;
return otherNode.Value.Nodes.Any(n => n.Key.StartsWith(traitName));
}));
// For actors that have or inherit a TargetableUnit, disable the trait while parachuting
var tu = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("TargetableUnit"));
@@ -1727,7 +1735,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
// SpawnViceroid was replaced by SpawnActorOnDeath
// And LeavesHusk was renamed to SpawnActorOnDeath
if (engineVersion < 20150809)
if (engineVersion < 20150920)
{
if (node.Key == "SpawnViceroid")
{
@@ -1774,15 +1782,21 @@ namespace OpenRA.Mods.Common.UtilityCommands
if (actor != null)
actor.Key = "Actor";
}
if (node.Key == "-SpawnViceroid")
node.Key = "-SpawnActorOnDeath";
if (node.Key == "-LeavesHusk")
node.Key = "-SpawnActorOnDeath";
}
if (engineVersion < 20150810)
if (engineVersion < 20150920)
{
if (depth == 2 && parentKey == "RallyPoint" && node.Key == "RallyPoint")
node.Key = "Offset";
}
if (engineVersion < 20150811)
if (engineVersion < 20150920)
{
if (node.Key.StartsWith("ProductionQueue"))
{
@@ -1806,7 +1820,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
}
if (engineVersion < 20150816)
if (engineVersion < 20150920)
{
// Rename RenderSprites.RaceImages
if (depth == 2 && node.Key == "RaceImages")
@@ -1820,35 +1834,40 @@ namespace OpenRA.Mods.Common.UtilityCommands
node.Key = "ValidFactions";
}
if (engineVersion < 20150823)
if (engineVersion < 20150920)
{
// Introduce QuantizeFacingsFromSequence
// This will only do roughly the right thing and probably require the modder to do some manual cleanup
if (depth == 0)
{
var inftraits = node.Value.Nodes.FirstOrDefault(n =>
n.Key.StartsWith("WithInfantryBody")
|| n.Key.StartsWith("WithDisguisingInfantryBody"));
if (inftraits != null)
// Check if the upgrade rule ran already before
var qffs = node.Value.Nodes.FirstOrDefault(n => n.Key == "QuantizeFacingsFromSequence");
if (qffs == null)
{
node.Value.Nodes.Add(new MiniYamlNode("QuantizeFacingsFromSequence", null, new List<MiniYamlNode>
var inftraits = node.Value.Nodes.FirstOrDefault(n =>
n.Key.StartsWith("WithInfantryBody")
|| n.Key.StartsWith("WithDisguisingInfantryBody"));
if (inftraits != null)
{
new MiniYamlNode("Sequence", "stand"),
}));
}
node.Value.Nodes.Add(new MiniYamlNode("QuantizeFacingsFromSequence", null, new List<MiniYamlNode>
{
new MiniYamlNode("Sequence", "stand"),
}));
}
var other = node.Value.Nodes.FirstOrDefault(x =>
x.Key.StartsWith("RenderBuilding")
|| x.Key.StartsWith("RenderSimple")
|| x.Key.StartsWith("WithCrateBody")
|| x.Key.StartsWith("WithSpriteBody")
|| x.Key.StartsWith("WithFacingSpriteBody"));
if (other != null)
node.Value.Nodes.Add(new MiniYamlNode("QuantizeFacingsFromSequence", ""));
var other = node.Value.Nodes.FirstOrDefault(x =>
x.Key.StartsWith("RenderBuilding")
|| x.Key.StartsWith("RenderSimple")
|| x.Key.StartsWith("WithCrateBody")
|| x.Key.StartsWith("WithSpriteBody")
|| x.Key.StartsWith("WithFacingSpriteBody"));
if (other != null)
node.Value.Nodes.Add(new MiniYamlNode("QuantizeFacingsFromSequence", ""));
}
}
}
if (engineVersion < 20150826)
if (engineVersion < 20150920)
{
// Replaced RenderBuildingCharge with RenderSprites + WithSpriteBody + WithChargeAnimation (+AutoSelectionSize)
if (depth == 0)
@@ -1969,7 +1988,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
}
if (engineVersion < 20150828)
if (engineVersion < 20150920)
{
// Replaced RenderBuilding with RenderSprites + WithSpriteBody (+AutoSelectionSize)
if (depth == 0)
@@ -2003,7 +2022,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
}
if (engineVersion < 20150902)
if (engineVersion < 20150920)
{
if (depth == 1)
{
@@ -2041,7 +2060,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
// Add `WhileCloakedUpgrades: underwater` to Cloak trait if `CloakTypes: Underwater`
var cloak = node.Value.Nodes.FirstOrDefault(n => (n.Key == "Cloak" || n.Key.StartsWith("Cloak@"))
&& n.Value.Nodes.Any(p => p.Key == "CloakTypes" && p.Value.Value == "Underwater"));
if (cloak != null)
if (cloak != null && !cloak.Value.Nodes.Any(n => n.Key == "WhileCloakedUpgrades"))
cloak.Value.Nodes.Add(new MiniYamlNode("WhileCloakedUpgrades", "underwater"));
// Remove split traits if TargetableSubmarine was removed
@@ -2092,14 +2111,14 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
// WaterPaletteRotation renamed to RotationPaletteEffect
if (engineVersion < 20150903)
if (engineVersion < 20150920)
{
if (depth == 1 && node.Key == "WaterPaletteRotation")
node.Key = "RotationPaletteEffect";
}
// Replace RenderSimple with RenderSprites + WithSpriteBody + AutoSelectionSize
if (engineVersion < 20150909)
if (engineVersion < 20150920)
{
if (depth == 0)
{
@@ -2125,18 +2144,19 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
// Rename D2k actors to match the original game.
if (engineVersion < 20150910 && Game.ModData.Manifest.Mod.Id == "d2k")
if (engineVersion < 20150920 && Game.ModData.Manifest.Mod.Id == "d2k")
node.Key = RenameD2kActors(node.Key);
// Make Range WDist for all traits with circular ranges.
if (engineVersion < 20150917 && depth == 2 && node.Key == "Range")
if (engineVersion < 20150920 && depth == 2 && node.Key == "Range")
{
if (parentKey == "DetectCloaked"
if ((parentKey == "DetectCloaked"
|| parentKey == "JamsMissiles"
|| parentKey == "JamsRadar"
|| parentKey == "Guardable"
|| parentKey == "BaseProvider"
|| parentKey == "ProximityCapturable")
&& !node.Value.Value.Contains("c0"))
node.Value.Value = node.Value.Value + "c0";
}
@@ -2197,15 +2217,54 @@ namespace OpenRA.Mods.Common.UtilityCommands
node.Key = "Aircraft";
node.Value.Nodes.Add(new MiniYamlNode("CanHover", "True"));
}
var mplane = node.Value.Nodes.FirstOrDefault(n => n.Key == "-Plane");
if (mplane != null)
{
// Check if a Helicopter trait was renamed to Aircraft
// In that case, we don't want to straight negate it with -Aircraft again
if (node.Value.Nodes.Any(n => n.Key == "Aircraft" || n.Key == "Helicopter"))
{
Console.WriteLine("Warning: Removed '-Plane:', this can introduce side effects with inherited 'Aircraft' definitions.");
node.Value.Nodes.Remove(mplane);
}
else
mplane.Key = "-Aircraft";
}
var mheli = node.Value.Nodes.FirstOrDefault(n => n.Key == "-Helicopter");
if (mheli != null)
{
// Check if a Plane trait was renamed to Aircraft
// In that case, we don't want to straight negate it with -Aircraft again
if (node.Value.Nodes.Any(n => n.Key == "Aircraft" || n.Key == "Plane"))
{
Console.WriteLine("Warning: Removed '-Helicopter:', this can introduce side effects with inherited 'Aircraft' definitions.");
node.Value.Nodes.Remove(mheli);
}
else
mheli.Key = "-Aircraft";
}
}
if (engineVersion < 20151004)
{
if (depth == 1 && node.Key == "WithRotor")
// Rename WithRotor to WithSpriteRotorOverlay
if (depth == 1 && node.Key.StartsWith("WithRotor"))
{
var parts = node.Key.Split('@');
node.Key = "WithSpriteRotorOverlay";
if (parts.Length > 1)
node.Key += "@" + parts[1];
}
if (depth == 1 && node.Key == "-WithRotor")
if (depth == 1 && node.Key.StartsWith("-WithRotor"))
{
var parts = node.Key.Split('@');
node.Key = "-WithSpriteRotorOverlay";
if (parts.Length > 1)
node.Key += "@" + parts[1];
}
}
if (engineVersion < 20151005)
@@ -2216,7 +2275,15 @@ namespace OpenRA.Mods.Common.UtilityCommands
{
var otherNodes = nodes;
var inherits = new Func<string, bool>(traitName => node.Value.Nodes.Where(n => n.Key.StartsWith("Inherits"))
.Any(inh => otherNodes.First(n => n.Key.StartsWith(inh.Value.Value)).Value.Nodes.Any(n => n.Key.StartsWith(traitName))));
.Any(inh =>
{
var otherNode = otherNodes.FirstOrDefault(n => n.Key.StartsWith(inh.Value.Value));
if (otherNode == null)
return false;
return otherNode.Value.Nodes.Any(n => n.Key.StartsWith(traitName));
}));
var target = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("-AutoTarget"));
if (target != null)
@@ -2272,11 +2339,29 @@ namespace OpenRA.Mods.Common.UtilityCommands
if (depth == 1 && node.Key == "AutoTarget")
{
var stance = node.Value.Nodes.FirstOrDefault(n => n.Key == "InitialStance");
if (stance != null)
var aiStance = node.Value.Nodes.FirstOrDefault(n => n.Key == "InitialStanceAI");
if (stance != null && aiStance == null)
node.Value.Nodes.Add(new MiniYamlNode("InitialStanceAI", stance.Value.Value));
}
}
if (engineVersion < 20151107 && depth == 2)
{
if (node.Key == "PaticleSize")
node.Key = "ParticleSize";
}
// Upgrades on DeployToUpgrade were renamed to DeployedUpgrades
if (engineVersion < 20151122)
{
if (node.Key == "DeployToUpgrade")
{
var u = node.Value.Nodes.FirstOrDefault(n => n.Key == "Upgrades");
if (u != null)
u.Key = "DeployedUpgrades";
}
}
UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1);
}
}
@@ -2368,8 +2453,8 @@ namespace OpenRA.Mods.Common.UtilityCommands
oldNodeAtName = "_" + curNode.Key.Split('@')[1];
// Per Cell Damage Model
if (curNode.Value.Nodes.Where(n => n.Key.Contains("DamageModel") &&
n.Value.Value.Contains("PerCell")).Any())
if (curNode.Value.Nodes.Any(n => n.Key.Contains("DamageModel") &&
n.Value.Value.Contains("PerCell")))
{
warheadCounter++;
@@ -2404,14 +2489,15 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
// HealthPercentage damage model
if (curNode.Value.Nodes.Where(n => n.Key.Contains("DamageModel") &&
n.Value.Value.Contains("HealthPercentage")).Any())
if (curNode.Value.Nodes.Any(n => n.Key.Contains("DamageModel") &&
n.Value.Value.Contains("HealthPercentage")))
{
warheadCounter++;
var newYaml = new List<MiniYamlNode>();
var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Size"); // New HealthPercentage warhead allows 2 spreads, as opposed to 1 size
// New HealthPercentage warhead allows 2 spreads, as opposed to 1 size
var temp = curNode.Value.Nodes.FirstOrDefault(n => n.Key == "Size");
if (temp != null)
{
var newValue = temp.Value.Value.Split(',').First() + "c0";
@@ -2440,7 +2526,8 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
// SpreadDamage
{ // Always occurs, since by definition all warheads were SpreadDamage warheads before
// Always occurs, since by definition all warheads were SpreadDamage warheads before
{
warheadCounter++;
var newYaml = new List<MiniYamlNode>();
@@ -2467,8 +2554,8 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
// DestroyResource
if (curNode.Value.Nodes.Where(n => n.Key.Contains("DestroyResources") ||
n.Key.Contains("Ore")).Any())
if (curNode.Value.Nodes.Any(n => n.Key.Contains("DestroyResources") ||
n.Key.Contains("Ore")))
{
warheadCounter++;
@@ -2486,7 +2573,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
// CreateResource
if (curNode.Value.Nodes.Where(n => n.Key.Contains("AddsResourceType")).Any())
if (curNode.Value.Nodes.Any(n => n.Key.Contains("AddsResourceType")))
{
warheadCounter++;
@@ -2505,7 +2592,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
// LeaveSmudge
if (curNode.Value.Nodes.Where(n => n.Key.Contains("SmudgeType")).Any())
if (curNode.Value.Nodes.Any(n => n.Key.Contains("SmudgeType")))
{
warheadCounter++;
@@ -2524,8 +2611,8 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
// CreateEffect - Explosion
if (curNode.Value.Nodes.Where(n => n.Key.Contains("Explosion") ||
n.Key.Contains("ImpactSound")).Any())
if (curNode.Value.Nodes.Any(n => n.Key.Contains("Explosion") ||
n.Key.Contains("ImpactSound")))
{
warheadCounter++;
@@ -2546,8 +2633,8 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
// CreateEffect - Water Explosion
if (curNode.Value.Nodes.Where(n => n.Key.Contains("WaterExplosion") ||
n.Key.Contains("WaterImpactSound")).Any())
if (curNode.Value.Nodes.Any(n => n.Key.Contains("WaterExplosion") ||
n.Key.Contains("WaterImpactSound")))
{
warheadCounter++;

View File

@@ -65,6 +65,9 @@ namespace OpenRA.Mods.Common.Warheads
public virtual void DoImpact(Actor victim, Actor firedBy, IEnumerable<int> damageModifiers)
{
if (!IsValidAgainst(victim, firedBy))
return;
var damage = Util.ApplyPercentageModifiers(Damage, damageModifiers.Append(DamageVersus(victim)));
victim.InflictDamage(firedBy, damage, this);
}

View File

@@ -42,6 +42,9 @@ namespace OpenRA.Mods.Common.Warheads
public override void DoImpact(Actor victim, Actor firedBy, IEnumerable<int> damageModifiers)
{
if (!IsValidAgainst(victim, firedBy))
return;
var healthInfo = victim.Info.TraitInfoOrDefault<HealthInfo>();
if (healthInfo == null)
return;

View File

@@ -62,9 +62,6 @@ namespace OpenRA.Mods.Common.Warheads
foreach (var victim in hitActors)
{
if (!IsValidAgainst(victim, firedBy))
continue;
var localModifiers = damageModifiers;
var healthInfo = victim.Info.TraitInfoOrDefault<HealthInfo>();
if (healthInfo != null)

View File

@@ -48,6 +48,9 @@ namespace OpenRA.Mods.Common.Widgets
public void ClearBrush() { SetBrush(null); }
public void SetBrush(IEditorBrush brush)
{
if (CurrentBrush != null)
CurrentBrush.Dispose();
CurrentBrush = brush ?? defaultBrush;
}

View File

@@ -21,6 +21,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
[ObjectCreator.UseCtor]
public MapEditorLogic(Widget widget, World world, WorldRenderer worldRenderer)
{
var editorViewport = widget.Get<EditorViewportControllerWidget>("MAP_EDITOR");
var gridButton = widget.GetOrNull<ButtonWidget>("GRID_BUTTON");
var terrainGeometryTrait = world.WorldActor.Trait<TerrainGeometryOverlay>();
@@ -63,6 +65,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
};
}
var copypasteButton = widget.GetOrNull<ButtonWidget>("COPYPASTE_BUTTON");
if (copypasteButton != null)
{
copypasteButton.OnClick = () => editorViewport.SetBrush(new EditorCopyPasteBrush(editorViewport, worldRenderer));
copypasteButton.IsHighlighted = () => editorViewport.CurrentBrush is EditorCopyPasteBrush;
}
var coordinateLabel = widget.GetOrNull<LabelWidget>("COORDINATE_LABEL");
if (coordinateLabel != null)
coordinateLabel.GetText = () => worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos).ToString();

View File

@@ -76,8 +76,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var mapDirectory = map.Path != null ? Platform.UnresolvePath(Path.GetDirectoryName(map.Path)) : null;
var initialDirectory = mapDirectories.Keys.FirstOrDefault(f => f == mapDirectory);
// Prioritize MapClassification.User directories over system directories
if (initialDirectory == null)
initialDirectory = mapDirectories.Keys.First();
initialDirectory = mapDirectories.OrderByDescending(kv => kv.Value).First().Key;
directoryDropdown.Text = initialDirectory;
directoryDropdown.OnClick = () =>

View File

@@ -10,7 +10,10 @@
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Network;
using OpenRA.Primitives;
using OpenRA.Traits;
using OpenRA.Widgets;
@@ -50,11 +53,20 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var client = world.LobbyInfo.ClientWithIndex(pp.ClientIndex);
var item = playerTemplate.Clone();
var nameLabel = item.Get<LabelWidget>("NAME");
var nameFont = Game.Renderer.Fonts[nameLabel.Font];
var suffixLength = new CachedTransform<string, int>(s => nameFont.Measure(s).X);
var name = new CachedTransform<Pair<string, int>, string>(c =>
WidgetUtils.TruncateText(c.First, nameLabel.Bounds.Width - c.Second, nameFont));
nameLabel.GetText = () =>
{
var suffix = pp.WinState == WinState.Undefined ? "" : " (" + pp.WinState + ")";
if (client != null && client.State == Network.Session.ClientState.Disconnected)
return pp.PlayerName + " (Gone)";
return pp.PlayerName + (pp.WinState == WinState.Undefined ? "" : " (" + pp.WinState + ")");
suffix = " (Gone)";
var sl = suffixLength.Update(suffix);
return name.Update(Pair.New(pp.PlayerName, sl)) + suffix;
};
nameLabel.GetColor = () => pp.Color.RGB;

View File

@@ -62,6 +62,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly LabelWidget chatLabel;
bool teamChat;
int lobbyChatUnreadMessages;
int globalChatLastReadMessages;
int globalChatUnreadMessages;
// Listen for connection failures
void ConnectionStateChanged(OrderManager om)
{
@@ -583,6 +587,17 @@ namespace OpenRA.Mods.Common.Widgets.Logic
globalChatInput.TakeKeyboardFocus();
};
var globalChatLabel = globalChatTab.Text;
globalChatTab.GetText = () =>
{
if (globalChatUnreadMessages == 0 || chatPanel == ChatPanelType.Global)
return globalChatLabel;
return globalChatLabel + " ({0})".F(globalChatUnreadMessages);
};
globalChatLastReadMessages = Game.GlobalChat.History.Count;
var lobbyChat = lobby.Get("LOBBYCHAT");
lobbyChat.IsVisible = () => chatPanel == ChatPanelType.Lobby;
@@ -624,6 +639,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic
chatTextField.TakeKeyboardFocus();
};
var lobbyChatLabel = lobbyChatTab.Text;
lobbyChatTab.GetText = () =>
{
if (lobbyChatUnreadMessages == 0 || chatPanel == ChatPanelType.Lobby)
return lobbyChatLabel;
return lobbyChatLabel + " ({0})".F(lobbyChatUnreadMessages);
};
lobbyChatPanel = lobby.Get<ScrollPanelWidget>("CHAT_DISPLAY");
chatTemplate = lobbyChatPanel.Get("CHAT_TEMPLATE");
lobbyChatPanel.RemoveChildren();
@@ -652,8 +676,23 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}
}
public override void Tick()
{
var newMessages = Game.GlobalChat.History.Count;
globalChatUnreadMessages += newMessages - globalChatLastReadMessages;
globalChatLastReadMessages = newMessages;
if (chatPanel == ChatPanelType.Lobby)
lobbyChatUnreadMessages = 0;
if (chatPanel == ChatPanelType.Global)
globalChatUnreadMessages = 0;
}
void AddChatLine(Color c, string from, string text)
{
lobbyChatUnreadMessages += 1;
var template = chatTemplate.Clone();
var nameLabel = template.Get<LabelWidget>("NAME");
var timeLabel = template.Get<LabelWidget>("TIME");

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System;
using OpenRA.Network;
using OpenRA.Widgets;
@@ -28,17 +29,25 @@ namespace OpenRA.Mods.Common.Widgets.Logic
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
preview.SpawnOccupants = () => LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, lobby.Map);
var title = available.GetOrNull<LabelWidget>("MAP_TITLE");
if (title != null)
title.GetText = () => lobby.Map.Title;
var titleLabel = available.GetOrNull<LabelWidget>("MAP_TITLE");
if (titleLabel != null)
{
var font = Game.Renderer.Fonts[titleLabel.Font];
var title = new CachedTransform<MapPreview, string>(m => WidgetUtils.TruncateText(m.Title, titleLabel.Bounds.Width, font));
titleLabel.GetText = () => title.Update(lobby.Map);
}
var type = available.GetOrNull<LabelWidget>("MAP_TYPE");
if (type != null)
type.GetText = () => lobby.Map.Type;
var typeLabel = available.GetOrNull<LabelWidget>("MAP_TYPE");
if (typeLabel != null)
typeLabel.GetText = () => lobby.Map.Type;
var author = available.GetOrNull<LabelWidget>("MAP_AUTHOR");
if (author != null)
author.GetText = () => "Created by {0}".F(lobby.Map.Author);
var authorLabel = available.GetOrNull<LabelWidget>("MAP_AUTHOR");
if (authorLabel != null)
{
var font = Game.Renderer.Fonts[authorLabel.Font];
var author = new CachedTransform<MapPreview, string>(m => WidgetUtils.TruncateText("Created by {0}".F(lobby.Map.Author), authorLabel.Bounds.Width, font));
authorLabel.GetText = () => author.Update(lobby.Map);
}
}
var invalid = widget.GetOrNull("MAP_INVALID");

View File

@@ -291,7 +291,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
public static void SetupNameWidget(Widget parent, Session.Slot s, Session.Client c)
{
var name = parent.Get<LabelWidget>("NAME");
name.GetText = () => c.Name;
var font = Game.Renderer.Fonts[name.Font];
var label = WidgetUtils.TruncateText(c.Name, name.Bounds.Width, font);
name.GetText = () => label;
}
public static void SetupEditableSlotWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, Ruleset rules)
@@ -472,14 +474,23 @@ namespace OpenRA.Mods.Common.Widgets.Logic
else
flag.GetImageName = () => player.Faction.InternalName;
var playerName = template.Get<LabelWidget>("PLAYER");
var client = player.World.LobbyInfo.ClientWithIndex(player.ClientIndex);
var playerName = template.Get<LabelWidget>("PLAYER");
var playerNameFont = Game.Renderer.Fonts[playerName.Font];
var suffixLength = new CachedTransform<string, int>(s => playerNameFont.Measure(s).X);
var name = new CachedTransform<Pair<string, int>, string>(c =>
WidgetUtils.TruncateText(c.First, playerName.Bounds.Width - c.Second, playerNameFont));
playerName.GetText = () =>
{
var suffix = player.WinState == WinState.Undefined ? "" : " (" + player.WinState + ")";
if (client != null && client.State == Network.Session.ClientState.Disconnected)
return player.PlayerName + " (Gone)";
return player.PlayerName + (player.WinState == WinState.Undefined ? "" : " (" + player.WinState + ")");
suffix = " (Gone)";
var sl = suffixLength.Update(suffix);
return name.Update(Pair.New(player.PlayerName, sl)) + suffix;
};
playerName.GetColor = () => player.Color.RGB;
}

View File

@@ -28,6 +28,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly Widget newsTemplate;
readonly LabelWidget newsStatus;
// Update news once per game launch
static bool fetchedNews;
[ObjectCreator.UseCtor]
public MainMenuLogic(Widget widget, World world)
{
@@ -207,27 +210,28 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (newsButton != null)
{
// Only query for news once per day.
var cacheValid = currentNews != null && DateTime.Today.ToUniversalTime() <= Game.Settings.Game.NewsFetchedDate;
if (!cacheValid)
if (!fetchedNews)
new Download(Game.Settings.Game.NewsUrl, cacheFile, e => { },
(e, c) => NewsDownloadComplete(e, cacheFile, currentNews, () => newsButton.AttachPanel(newsPanel)));
(e, c) => NewsDownloadComplete(e, cacheFile, currentNews,
() => newsButton.AttachPanel(newsPanel)));
newsButton.OnClick = () => newsButton.AttachPanel(newsPanel);
}
}
Game.OnRemoteDirectConnect += (host, port) =>
Game.OnRemoteDirectConnect += OnRemoteDirectConnect;
}
void OnRemoteDirectConnect(string host, int port)
{
menuType = MenuType.None;
Ui.OpenWindow("MULTIPLAYER_PANEL", new WidgetArgs
{
menuType = MenuType.None;
Ui.OpenWindow("MULTIPLAYER_PANEL", new WidgetArgs
{
{ "onStart", RemoveShellmapUI },
{ "onExit", () => menuType = MenuType.Main },
{ "directConnectHost", host },
{ "directConnectPort", port },
});
};
{ "onStart", RemoveShellmapUI },
{ "onExit", () => menuType = MenuType.Main },
{ "directConnectHost", host },
{ "directConnectPort", port },
});
}
void LoadMapIntoEditor(Map map)
@@ -290,6 +294,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return;
}
fetchedNews = true;
var newNews = ParseNews(cacheFile);
if (newNews == null)
return;
@@ -298,9 +303,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (oldNews == null || newNews.Any(n => !oldNews.Select(c => c.DateTime).Contains(n.DateTime)))
onNewsDownloaded();
Game.Settings.Game.NewsFetchedDate = DateTime.Today.ToUniversalTime();
Game.Settings.Save();
});
}
@@ -362,5 +364,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
OpenSkirmishLobbyPanel,
() => { Game.CloseServer(); menuType = MenuType.Main; });
}
protected override void Dispose(bool disposing)
{
if (disposing)
Game.OnRemoteDirectConnect -= OnRemoteDirectConnect;
base.Dispose(disposing);
}
}
}

View File

@@ -235,7 +235,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
item.IsVisible = () => item.RenderBounds.IntersectsWith(scrollpanels[tab].RenderBounds);
var titleLabel = item.Get<LabelWidget>("TITLE");
titleLabel.GetText = () => preview.Title;
if (titleLabel != null)
{
var font = Game.Renderer.Fonts[titleLabel.Font];
var title = WidgetUtils.TruncateText(preview.Title, titleLabel.Bounds.Width, font);
titleLabel.GetText = () => title;
}
var previewWidget = item.Get<MapPreviewWidget>("PREVIEW");
previewWidget.Preview = () => preview;
@@ -246,7 +251,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var authorWidget = item.GetOrNull<LabelWidget>("AUTHOR");
if (authorWidget != null)
authorWidget.GetText = () => "Created by {0}".F(preview.Author);
{
var font = Game.Renderer.Fonts[authorWidget.Font];
var author = WidgetUtils.TruncateText("Created by {0}".F(preview.Author), authorWidget.Bounds.Width, font);
authorWidget.GetText = () => author;
}
var sizeWidget = item.GetOrNull<LabelWidget>("SIZE");
if (sizeWidget != null)

View File

@@ -198,7 +198,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var mapTitle = widget.GetOrNull<LabelWidget>("SELECTED_MAP");
if (mapTitle != null)
mapTitle.GetText = () => currentMap != null ? currentMap.Title : "No Server Selected";
{
var font = Game.Renderer.Fonts[mapTitle.Font];
var title = new CachedTransform<MapPreview, string>(m => m == null ? "No Server Selected" :
WidgetUtils.TruncateText(m.Title, mapTitle.Bounds.Width, font));
mapTitle.GetText = () => title.Update(currentMap);
}
var ip = widget.GetOrNull<LabelWidget>("SELECTED_IP");
if (ip != null)
@@ -219,8 +224,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (modVersion != null)
{
modVersion.IsVisible = () => currentServer != null;
modVersion.GetText = () => currentServer.ModLabel;
modVersion.GetColor = () => currentServer.IsCompatible ? modVersion.TextColor : incompatibleVersionColor;
var font = Game.Renderer.Fonts[modVersion.Font];
var version = new CachedTransform<GameServer, string>(s => WidgetUtils.TruncateText(s.ModLabel, mapTitle.Bounds.Width, font));
modVersion.GetText = () => version.Update(currentServer);
}
var players = widget.GetOrNull<LabelWidget>("SELECTED_PLAYERS");
@@ -358,7 +366,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var title = item.GetOrNull<LabelWidget>("TITLE");
if (title != null)
{
title.GetText = () => game.Name;
var font = Game.Renderer.Fonts[title.Font];
var label = WidgetUtils.TruncateText(game.Name, title.Bounds.Width, font);
title.GetText = () => label;
title.GetColor = () => canJoin ? title.TextColor : incompatibleGameColor;
}

View File

@@ -71,9 +71,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic
preview.SpawnOccupants = () => selectedSpawns;
preview.Preview = () => selectedReplay != null ? selectedReplay.GameInfo.MapPreview : null;
var title = panel.GetOrNull<LabelWidget>("MAP_TITLE");
if (title != null)
title.GetText = () => selectedReplay != null ? selectedReplay.GameInfo.MapPreview.Title : null;
var titleLabel = panel.GetOrNull<LabelWidget>("MAP_TITLE");
if (titleLabel != null)
{
titleLabel.IsVisible = () => selectedReplay != null;
var font = Game.Renderer.Fonts[titleLabel.Font];
var title = new CachedTransform<MapPreview, string>(m => WidgetUtils.TruncateText(m.Title, titleLabel.Bounds.Width, font));
titleLabel.GetText = () => title.Update(selectedReplay.GameInfo.MapPreview);
}
var type = panel.GetOrNull<LabelWidget>("MAP_TYPE");
if (type != null)
@@ -633,7 +639,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var item = ScrollItemWidget.Setup(playerTemplate, () => false, () => { });
var label = item.Get<LabelWidget>("LABEL");
label.GetText = () => o.Name;
var font = Game.Renderer.Fonts[label.Font];
var name = WidgetUtils.TruncateText(o.Name, label.Bounds.Width, font);
label.GetText = () => name;
label.GetColor = () => color;
var flag = item.Get<ImageWidget>("FLAG");

View File

@@ -52,7 +52,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic
};
panel.Get<MapPreviewWidget>("MAP_PREVIEW").Preview = () => preview;
panel.Get<LabelWidget>("MAP_NAME").GetText = () => preview.Title;
var mapTitle = panel.Get<LabelWidget>("MAP_NAME");
if (mapTitle != null)
{
var font = Game.Renderer.Fonts[mapTitle.Font];
var title = new CachedTransform<MapPreview, string>(m => WidgetUtils.TruncateText(m.Title, mapTitle.Bounds.Width, font));
mapTitle.GetText = () => title.Update(preview);
}
}
var serverName = panel.Get<TextFieldWidget>("SERVER_NAME");

View File

@@ -26,13 +26,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
tooltipContainer.BeforeRender = () =>
{
labelText = getText();
var textDim = font.Measure(labelText);
if (textDim.X != cachedWidth)
var textWidth = font.Measure(labelText).X;
if (textWidth != cachedWidth)
{
label.Bounds.Width = textDim.X;
widget.Bounds.Width = 2 * label.Bounds.X + textDim.X;
label.Bounds.Height = textDim.Y;
widget.Bounds.Height = 4 * label.Bounds.Y + textDim.Y;
label.Bounds.Width = textWidth;
widget.Bounds.Width = 2 * label.Bounds.X + textWidth;
}
};

View File

@@ -68,7 +68,7 @@ namespace OpenRA.Mods.Common.Widgets
return;
var powers = player.PlayerActor.Trait<SupportPowerManager>().Powers
.Select((a, i) => new { a, i });
.Where(x => !x.Value.Disabled).Select((a, i) => new { a, i });
foreach (var power in powers)
{
if (!clocks.ContainsKey(power.a.Key))

View File

@@ -442,5 +442,12 @@ namespace OpenRA.Mods.Common.Widgets
var v = (int)((p.Y - mapRect.Y) / previewScale) + world.Map.Bounds.Top;
return new MPos(u, v).ToCPos(world.Map);
}
public override void Removed()
{
base.Removed();
world.Map.MapTiles.Value.CellEntryChanged -= UpdateTerrainCell;
world.Map.CustomTerrain.CellEntryChanged -= UpdateTerrainCell;
}
}
}

View File

@@ -184,7 +184,8 @@ namespace OpenRA.Mods.Common.Widgets
.Where(a => !a.Disposed && a.Owner == world.LocalPlayer && a.Info.HasTraitInfo<GuardInfo>());
if (actors.Any())
world.OrderGenerator = new GuardOrderGenerator(actors);
world.OrderGenerator = new GuardOrderGenerator(actors,
"Guard", "guard", Game.Settings.Game.MouseButtonPreference.Action);
return true;
}

View File

@@ -56,7 +56,7 @@ namespace OpenRA.Mods.D2k.Traits
foreach (var c in FootprintUtils.Tiles(self))
{
// Only place on allowed terrain types
if (!info.TerrainTypes.Contains(map.GetTerrainInfo(c).Type))
if (!map.Contains(c) || !info.TerrainTypes.Contains(map.GetTerrainInfo(c).Type))
continue;
// Don't place under other buildings or custom terrain

View File

@@ -14,6 +14,7 @@ using System.Linq;
using OpenRA.Effects;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
@@ -21,8 +22,11 @@ using OpenRA.Traits;
namespace OpenRA.Mods.D2k.Traits
{
[Desc("Seeds resources by explosive eruptions after accumulation times.")]
public class SpiceBloomInfo : ITraitInfo, Requires<RenderSpritesInfo>
public class SpiceBloomInfo : ITraitInfo, IRenderActorPreviewSpritesInfo, Requires<RenderSpritesInfo>
{
[ActorReference]
public readonly string SpawnActor = "spicebloom.spawnpoint";
[SequenceReference]
public readonly string[] GrowthSequences = { "grow1", "grow2", "grow3" };
@@ -30,21 +34,32 @@ namespace OpenRA.Mods.D2k.Traits
public readonly int[] RespawnDelay = { 1500, 2500 };
[Desc("The range of time (in ticks) that the spicebloom will take to grow.")]
public readonly int[] GrowthDelay = { 1000, 1500 };
public readonly int[] GrowthDelay = { 1000, 3000 };
public readonly string ResourceType = "Spice";
[Desc("Spice blooms only grow on these terrain types.")]
public readonly HashSet<string> GrowthTerrainTypes = new HashSet<string>();
[Desc("The weapon to use for spice creation.")]
[WeaponReference]
public readonly string Weapon = "SpiceExplosion";
public readonly string Weapon = null;
[Desc("The amount of spice to expel.")]
public readonly int[] Pieces = { 3, 10 };
public readonly int[] Pieces = { 2, 12 };
[Desc("The maximum distance in cells that spice may be expelled.")]
public readonly int Range = 5;
public object Create(ActorInitializer init) { return new SpiceBloom(init, this); }
public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
{
var anim = new Animation(init.World, image);
anim.PlayRepeating(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), GrowthSequences[0]));
yield return new SpriteActorPreview(anim, WVec.Zero, 0, p, rs.Scale);
}
}
public class SpiceBloom : ITick, INotifyKilled
@@ -78,6 +93,12 @@ namespace OpenRA.Mods.D2k.Traits
public void Tick(Actor self)
{
if (!self.World.Map.Contains(self.Location))
return;
if (info.GrowthTerrainTypes.Count > 0 && !info.GrowthTerrainTypes.Contains(self.World.Map.GetTerrainInfo(self.Location).Type))
return;
ticks++;
if (ticks >= growTicks)
@@ -89,32 +110,35 @@ namespace OpenRA.Mods.D2k.Traits
}
}
public void Killed(Actor self, AttackInfo e)
void SeedResources(Actor self)
{
var args = new ProjectileArgs
{
Weapon = self.World.Map.Rules.Weapons[info.Weapon.ToLowerInvariant()],
Facing = 0,
DamageModifiers = self.TraitsImplementing<IFirepowerModifier>()
.Select(a => a.GetFirepowerModifier()).ToArray(),
InaccuracyModifiers = self.TraitsImplementing<IInaccuracyModifier>()
.Select(a => a.GetInaccuracyModifier()).ToArray(),
Source = self.CenterPosition,
SourceActor = self,
};
var pieces = self.World.SharedRandom.Next(info.Pieces[0], info.Pieces[1]) * ticks / growTicks;
for (var i = 0; pieces > i; i++)
{
var cells = OpenRA.Traits.Util.RandomWalk(self.Location, self.World.SharedRandom);
var cell = cells.Take(info.Range).SkipWhile(p => resLayer.GetResource(p) == resType && resLayer.IsFull(p)).Cast<CPos?>().RandomOrDefault(self.World.SharedRandom);
if (cell == null)
cell = cells.Take(info.Range).Random(self.World.SharedRandom);
if (pieces < info.Pieces[0])
pieces = info.Pieces[0];
args.PassiveTarget = self.World.Map.CenterOfCell(cell.Value);
var cells = self.World.Map.FindTilesInAnnulus(self.Location, 1, info.Range);
for (var i = 0; i < pieces; i++)
{
var cell = cells.SkipWhile(p => resLayer.GetResource(p) == resType && resLayer.IsFull(p)).Cast<CPos?>().RandomOrDefault(self.World.SharedRandom);
if (cell == null)
cell = cells.Random(self.World.SharedRandom);
var args = new ProjectileArgs
{
Weapon = self.World.Map.Rules.Weapons[info.Weapon.ToLowerInvariant()],
Facing = 0,
DamageModifiers = self.TraitsImplementing<IFirepowerModifier>()
.Select(a => a.GetFirepowerModifier()).ToArray(),
InaccuracyModifiers = self.TraitsImplementing<IInaccuracyModifier>()
.Select(a => a.GetInaccuracyModifier()).ToArray(),
Source = self.CenterPosition,
SourceActor = self,
PassiveTarget = self.World.Map.CenterOfCell(cell.Value)
};
self.World.AddFrameEndTask(_ =>
{
@@ -129,6 +153,12 @@ namespace OpenRA.Mods.D2k.Traits
}
});
}
}
public void Killed(Actor self, AttackInfo e)
{
if (!string.IsNullOrEmpty(info.Weapon))
SeedResources(self);
self.World.AddFrameEndTask(t => t.Add(new DelayedAction(respawnTicks, () =>
{
@@ -141,7 +171,7 @@ namespace OpenRA.Mods.D2k.Traits
new FactionInit(self.Owner.Faction.InternalName),
new SkipMakeAnimsInit()
};
self.World.CreateActor(self.Info.Name, td);
self.World.CreateActor(info.SpawnActor, td);
})));
}
}

View File

@@ -93,7 +93,7 @@ namespace OpenRA.Mods.RA.Activities
if (teleporter != null && self != teleporter && !teleporter.Disposed)
{
var building = teleporter.TraitOrDefault<WithSpriteBody>();
if (building != null)
if (building != null && building.DefaultAnimation.HasSequence("active"))
building.PlayCustomAnimation(teleporter, "active");
}

View File

@@ -10,6 +10,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
@@ -36,14 +37,23 @@ namespace OpenRA.Mods.RA.Effects
readonly GpsDotInfo info;
readonly Animation anim;
Lazy<HiddenUnderFog> huf;
Lazy<FrozenUnderFog> fuf;
Lazy<Disguise> disguise;
Lazy<Cloak> cloak;
Cache<Player, GpsWatcher> watcher;
Cache<Player, FrozenActorLayer> frozen;
readonly Dictionary<Player, DotState> stateByPlayer = new Dictionary<Player, DotState>();
readonly Lazy<HiddenUnderFog> huf;
readonly Lazy<FrozenUnderFog> fuf;
readonly Lazy<Disguise> disguise;
readonly Lazy<Cloak> cloak;
readonly Cache<Player, FrozenActorLayer> frozen;
bool show = false;
class DotState
{
public readonly GpsWatcher Gps;
public bool IsTargetable;
public bool ShouldRender;
public DotState(GpsWatcher gps)
{
Gps = gps;
}
}
public GpsDot(Actor self, GpsDotInfo info)
{
@@ -59,32 +69,49 @@ namespace OpenRA.Mods.RA.Effects
disguise = Exts.Lazy(() => self.TraitOrDefault<Disguise>());
cloak = Exts.Lazy(() => self.TraitOrDefault<Cloak>());
watcher = new Cache<Player, GpsWatcher>(p => p.PlayerActor.Trait<GpsWatcher>());
frozen = new Cache<Player, FrozenActorLayer>(p => p.PlayerActor.Trait<FrozenActorLayer>());
stateByPlayer = self.World.Players.ToDictionary(p => p, p => new DotState(p.PlayerActor.Trait<GpsWatcher>()));
}
bool ShouldShowIndicator()
public bool IsDotVisible(Player toPlayer)
{
return stateByPlayer[toPlayer].IsTargetable;
}
bool IsTargetableBy(Player toPlayer, out bool shouldRenderIndicator)
{
shouldRenderIndicator = false;
if (cloak.Value != null && cloak.Value.Cloaked)
return false;
if (disguise.Value != null && disguise.Value.Disguised)
return false;
if (huf.Value != null && !huf.Value.IsVisible(self, self.World.RenderPlayer))
if (huf.Value != null && !huf.Value.IsVisible(self, toPlayer)
&& toPlayer.Shroud.IsExplored(self.CenterPosition))
{
var f1 = FrozenActorForPlayer(toPlayer);
shouldRenderIndicator = f1 == null || !f1.HasRenderables;
return true;
}
if (fuf.Value == null)
return false;
var f = frozen[self.World.RenderPlayer].FromID(self.ActorID);
if (f == null)
var f2 = FrozenActorForPlayer(toPlayer);
if (f2 == null)
return false;
if (f.HasRenderables || f.NeedRenderables)
return false;
shouldRenderIndicator = !f2.HasRenderables;
return f.Visible;
return f2.Visible && !f2.Shrouded;
}
FrozenActor FrozenActorForPlayer(Player player)
{
return frozen[player].FromID(self.ActorID);
}
public void Tick(World world)
@@ -92,17 +119,22 @@ namespace OpenRA.Mods.RA.Effects
if (self.Disposed)
world.AddFrameEndTask(w => w.Remove(this));
show = false;
if (!self.IsInWorld || self.IsDead || self.World.RenderPlayer == null)
if (!self.IsInWorld || self.IsDead)
return;
var gps = watcher[self.World.RenderPlayer];
show = (gps.Granted || gps.GrantedAllies) && ShouldShowIndicator();
foreach (var player in self.World.Players)
{
var state = stateByPlayer[player];
var shouldRender = false;
var targetable = (state.Gps.Granted || state.Gps.GrantedAllies) && IsTargetableBy(player, out shouldRender);
state.IsTargetable = targetable;
state.ShouldRender = targetable && shouldRender;
}
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (!show || self.Disposed)
if (self.World.RenderPlayer == null || !stateByPlayer[self.World.RenderPlayer].ShouldRender || self.Disposed)
return SpriteRenderable.None;
var palette = wr.Palette(info.IndicatorPalettePrefix + self.Owner.InternalName);

View File

@@ -15,7 +15,7 @@ namespace OpenRA.Mods.RA.Traits
{
class InfiltrateForPowerOutageInfo : ITraitInfo
{
public readonly int Duration = 25 * 30;
public readonly int Duration = 25 * 20;
public object Create(ActorInitializer init) { return new InfiltrateForPowerOutage(init.Self, this); }
}

View File

@@ -199,7 +199,7 @@ namespace OpenRA.Mods.RA.Traits
public int OrderPriority { get { return 5; } }
public bool OverrideSelection { get { return true; } }
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor)
{
if (target.Type != TargetType.Terrain)
return false;

View File

@@ -120,7 +120,7 @@ namespace OpenRA.Mods.RA.Traits
public bool IsQueued { get; protected set; }
public bool OverrideSelection { get { return true; } }
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor)
{
// TODO: When target modifiers are configurable this needs to be revisited
if (modifiers.HasModifier(TargetModifiers.ForceMove) || modifiers.HasModifier(TargetModifiers.ForceQueue))

View File

@@ -72,7 +72,7 @@ namespace OpenRA.Mods.RA.Traits
return;
var warhead = e.Warhead as DamageWarhead;
if (info.DeathType != null && warhead != null && !warhead.DamageTypes.Contains(info.DeathType))
if (info.DeathType != null && (warhead == null || !warhead.DamageTypes.Contains(info.DeathType)))
return;
self.World.AddFrameEndTask(w =>

View File

@@ -63,7 +63,7 @@ namespace OpenRA.Mods.RA.Traits
foreach (var i in atek.World.ActorsWithTrait<GpsWatcher>())
i.Trait.RefreshGranted();
if ((Granted || GrantedAllies) && atek.Owner.IsAlliedWith(atek.World.RenderPlayer))
if ((Granted || GrantedAllies) && atek.Owner.IsAlliedWith(Owner))
atek.Owner.Shroud.ExploreAll(atek.World);
}
@@ -76,10 +76,19 @@ namespace OpenRA.Mods.RA.Traits
Owner.Shroud.ExploreAll(Owner.World);
}
public bool HasFogVisibility(Player byPlayer)
public bool HasFogVisibility()
{
return Granted || GrantedAllies;
}
public bool IsVisible(Actor actor)
{
var gpsDot = actor.TraitOrDefault<GpsDot>();
if (gpsDot == null)
return false;
return gpsDot.IsDotVisible(Owner);
}
}
class GpsPowerInfo : SupportPowerInfo

View File

@@ -109,7 +109,7 @@ namespace OpenRA.Platforms.Default
}
catch (Exception ex)
{
throw new InvalidDataException("Failed to create hardware cursor `{0}`".F(name), ex);
throw new InvalidDataException("Failed to create hardware cursor `{0}` - {1}".F(name, ex.Message), ex);
}
}

BIN
mods/cnc/bits/camera.shp Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -6,7 +6,7 @@ Container@NEW_MAP_BG:
Height: 125
Children:
Label@TITLE:
Text:New Map
Text: New Map
Width: PARENT_RIGHT
Y: 0-25
Font: BigBold
@@ -73,7 +73,6 @@ Container@NEW_MAP_BG:
Font: Bold
Key: return
Background@SAVE_MAP_PANEL:
Logic: SaveMapLogic
X: (WINDOW_RIGHT - WIDTH)/2
@@ -361,7 +360,7 @@ Container@EDITOR_WORLD_ROOT:
Text: Actors
Font: Bold
Button@GRID_BUTTON:
X: WINDOW_RIGHT - 420
X: WINDOW_RIGHT - 500
Y: 5
Width: 100
Height: 25
@@ -372,7 +371,7 @@ Container@EDITOR_WORLD_ROOT:
TooltipText: Toggle the terrain grid
TooltipContainer: TOOLTIP_CONTAINER
Label@ZOOM_LABEL:
X: WINDOW_RIGHT - 500 - 55
X: WINDOW_RIGHT - 580 - 55
Y: 5
Width: 50
Height: 25
@@ -381,11 +380,17 @@ Container@EDITOR_WORLD_ROOT:
Font: Bold
Contrast: true
DropDownButton@ZOOM_BUTTON:
X: WINDOW_RIGHT - 500
X: WINDOW_RIGHT - 580
Y: 5
Width: 70
Height: 25
Font: Bold
Button@COPYPASTE_BUTTON:
X: WINDOW_RIGHT-390
Y: 5
Width: 96
Height: 25
Text: Copy/Paste
Label@COORDINATE_LABEL:
X: 10
Width: 50
@@ -400,3 +405,4 @@ Container@EDITOR_WORLD_ROOT:
Align: Left
Font: Bold
Contrast: true

View File

@@ -8,7 +8,7 @@ Container@SCRIPT_ERROR_PANEL:
Width: PARENT_RIGHT-30
Height: 20
Font: Bold
Align:Center
Align: Center
Text: The map script has encountered a fatal error
Label@DESCB:
X: 15
@@ -24,4 +24,5 @@ Container@SCRIPT_ERROR_PANEL:
Width: PARENT_RIGHT-30
Height: 20
Font: Regular
Text: Please send this file to the map author so that they can fix this issue.
Text: Please send this file to the map author so that they can fix this issue.

View File

@@ -202,7 +202,7 @@ Container@OBSERVER_WIDGETS:
Logic: ObserverShroudSelectorLogic
X: WINDOW_RIGHT-WIDTH-5
Y: 260
Width:256
Width: 256
Height: 25
Font: Bold
Children:
@@ -467,6 +467,7 @@ Container@PLAYER_WIDGETS:
Y: 268
Width: 194
Height: 20
Background@FMVPLAYER:
Width: WINDOW_RIGHT
Height: WINDOW_BOTTOM

View File

@@ -23,12 +23,12 @@ Container@LOBBY_GLOBALCHAT_PANEL:
Y: 19
Width: 582
Height: PARENT_BOTTOM - 19
ItemSpacing: 5
TopBottomSpacing: 4
ItemSpacing: 4
Children:
Label@HISTORY_TEMPLATE:
X: 5
Width: 530
Height: 25
WordWrap: True
TextField@CHAT_TEXTFIELD:
X: 200

View File

@@ -45,6 +45,7 @@ Background@KICK_CLIENT_DIALOG:
Height: 25
Text: Cancel
Font: Bold
Background@KICK_SPECTATORS_DIALOG:
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
@@ -78,6 +79,7 @@ Background@KICK_SPECTATORS_DIALOG:
Height: 25
Text: Cancel
Font: Bold
Background@FORCE_START_DIALOG:
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
@@ -137,3 +139,4 @@ Background@FORCE_START_DIALOG:
Height: 25
Text: Cancel
Font: Bold

View File

@@ -89,25 +89,25 @@ Container@MAPCHOOSER_PANEL:
IgnoreMouseOver: true
IgnoreMouseInput: true
Label@TITLE:
X: 2
X: 4
Y: PARENT_BOTTOM-48
Width: PARENT_RIGHT-4
Width: PARENT_RIGHT-8
Align: Center
Label@DETAILS:
Width: PARENT_RIGHT-4
X: 2
Width: PARENT_RIGHT-8
X: 4
Y: PARENT_BOTTOM-34
Align: Center
Font: Tiny
Label@AUTHOR:
Width: PARENT_RIGHT-4
X: 2
Width: PARENT_RIGHT-8
X: 4
Y: PARENT_BOTTOM-22
Align: Center
Font: Tiny
Label@SIZE:
Width: PARENT_RIGHT-4
X: 2
Width: PARENT_RIGHT-8
X: 4
Y: PARENT_BOTTOM-10
Align: Center
Font: Tiny

View File

@@ -148,6 +148,7 @@ Container@MISSIONBROWSER_PANEL:
Y: 1
Width: 640
Height: 375
Background@FULLSCREEN_PLAYER:
Width: WINDOW_RIGHT
Height: WINDOW_BOTTOM
@@ -159,3 +160,4 @@ Background@FULLSCREEN_PLAYER:
Y: 0
Width: WINDOW_RIGHT
Height: WINDOW_BOTTOM

View File

@@ -23,12 +23,12 @@ Container@GLOBALCHAT_PANEL:
Y: 19
Width: 582
Height: PARENT_BOTTOM - 49
ItemSpacing: 5
TopBottomSpacing: 4
ItemSpacing: 4
Children:
Label@HISTORY_TEMPLATE:
X: 5
Width: 530
Height: 25
WordWrap: True
TextField@CHAT_TEXTFIELD:
Y: PARENT_BOTTOM - 25

View File

@@ -73,7 +73,7 @@ ScrollPanel@MULTIPLAYER_FILTER_PANEL:
Checkbox@WAITING_FOR_PLAYERS:
X: 5
Y: 5
Width: 137
Width: PARENT_RIGHT-29
Height: 20
Text: Waiting
TextColor: 50,205,50
@@ -81,14 +81,14 @@ ScrollPanel@MULTIPLAYER_FILTER_PANEL:
Checkbox@EMPTY:
X: 5
Y: 30
Width: 137
Width: PARENT_RIGHT-29
Height: 20
Text: Empty
Font: Regular
Checkbox@PASSWORD_PROTECTED:
X: 5
Y: 55
Width: 137
Width: PARENT_RIGHT-29
Height: 20
Text: Protected
TextColor: 255,0,0
@@ -96,7 +96,7 @@ ScrollPanel@MULTIPLAYER_FILTER_PANEL:
Checkbox@ALREADY_STARTED:
X: 5
Y: 80
Width: 137
Width: PARENT_RIGHT-29
Height: 20
Text: Started
TextColor: 255,165,0
@@ -104,8 +104,8 @@ ScrollPanel@MULTIPLAYER_FILTER_PANEL:
Checkbox@INCOMPATIBLE_VERSION:
X: 5
Y: 105
Width: 137
Width: PARENT_RIGHT-29
Height: 20
Text: Incompatible
TextColor: 190,190,190
Font: Regular
Font: Regular

View File

@@ -273,7 +273,7 @@ Container@REPLAYBROWSER_PANEL:
Height: 16
Label@LABEL:
X: 40
Width: 60
Width: PARENT_RIGHT-50
Height: 25
Label@NOFLAG_LABEL:
X: 5

View File

@@ -293,7 +293,7 @@ Actors:
Location: 38,27
Owner: Neutral
Actor77: split3
Location: 31,27
Location: 31,28
Owner: Neutral
Actor78: tc01
Location: 29,27
@@ -332,13 +332,13 @@ Actors:
Location: 21,9
Owner: Neutral
Actor90: split2
Location: 19,59
Location: 19,60
Owner: Neutral
Actor91: split3
Location: 22,58
Location: 22,59
Owner: Neutral
Actor92: split3
Location: 42,56
Location: 42,57
Owner: Neutral
Actor93: tc05
Location: 44,59
@@ -428,16 +428,16 @@ Actors:
Location: 4,14
Owner: Neutral
Actor122: split3
Location: 7,28
Location: 7,29
Owner: Neutral
Actor123: split3
Location: 7,32
Location: 7,33
Owner: Neutral
Actor124: split3
Location: 3,33
Location: 3,34
Owner: Neutral
Actor125: split3
Location: 22,41
Location: 22,42
Owner: Neutral
AttackTrigger1: proc
Location: 46,42
@@ -804,6 +804,8 @@ Rules:
-WithRoof:
-Selectable:
RejectsOrders:
Cargo:
Types: disabled
airstrike.proxy:
AlwaysVisible:
AirstrikePower:

View File

@@ -488,6 +488,8 @@ Rules:
-WithRoof:
-Selectable:
RejectsOrders:
Cargo:
Types: disabled
TREX:
Health:
HP: 750

View File

@@ -535,6 +535,8 @@ Rules:
-WithRoof:
-Selectable:
RejectsOrders:
Cargo:
Types: disabled
Sequences:
oldlst:

View File

@@ -769,6 +769,8 @@ Rules:
-WithRoof:
-Selectable:
RejectsOrders:
Cargo:
Types: disabled
Sequences:
oldlst:

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