Compare commits

...

549 Commits

Author SHA1 Message Date
Paul Chote
d3c5aa53be Cache passenger bounty traits.
This avoids querying from potentially dead actors.
2019-02-09 19:47:14 +01:00
Paul Chote
adfb905f97 Abort activities when we don't know how close to move to a target. 2019-02-09 19:20:38 +01:00
portablestew
c2ead21bf0 Fix for #7083: Fly stops turning when target is inside the turn radius 2019-02-08 19:37:44 +01:00
Paul Chote
aaaa5c78de Defer UpdateFrozenActor until the end of the tick.
Updating the frozen actor calls Actor.GetTargetablePositions,
and so we must guarantee that Created has been called for
the ITargetablePositions traits first.
2019-02-07 19:51:11 +00:00
Oliver Brakmann
b5105e5e50 Fix unresponsive aircraft when executing orders queued during resupply 2019-02-06 18:02:20 +00:00
reaperrr
b18110012b Normalize RA tracks 2019-02-03 20:47:47 +01:00
reaperrr
38bf3eb30d Normalize TD track volumes and fix order
Some tracks from the original were falsely listed under CovOps.
2019-02-03 20:47:45 +01:00
abcdefg30
7ad4ba7adb Let the extraction helicopter of Monster Tank Madness leave 2019-02-03 19:33:11 +00:00
Paul Chote
9d624579fc Fix inconsistent FrozenActor state on capture/destruction. 2019-02-03 20:28:49 +01:00
Paul Chote
46f0978789 Remove obsolete LegacyEnter and ResolveFrozenActorOrder. 2019-02-03 20:28:25 +01:00
Paul Chote
880f635e83 Remove ResolveFrozenActorOrder from MadTank. 2019-02-03 20:23:59 +01:00
Paul Chote
3bfb43a305 Remove ResolveFrozenActorOrder from EntersTunnels. 2019-02-03 20:23:59 +01:00
Paul Chote
4ca4aebf50 Remove ResolveFrozenActorOrder from Carryall. 2019-02-03 20:23:59 +01:00
Paul Chote
8df18da419 Port EnterTransport to the new Enter activity.
This dramatically simplifies the reservation logic,
which seemed to be needlessly complicated. This may
regress unexpected edge-cases.
2019-02-03 20:23:59 +01:00
Paul Chote
98b4d6e828 Port DonateExperience to the new Enter activity. 2019-02-03 20:23:59 +01:00
Paul Chote
5f1d4a4ff1 Port DonateCash to the new Enter activity. 2019-02-03 20:23:59 +01:00
Paul Chote
dc722b0a0c Port RepairBridge to the new Enter activity. 2019-02-03 20:23:59 +01:00
Paul Chote
142e6776af Port RepairBuilding to the new Enter activity. 2019-02-03 20:23:59 +01:00
Paul Chote
baa786a4fd Port Demolish to the new Enter activity. 2019-02-03 20:23:59 +01:00
Paul Chote
75ccdfce7a Port Infiltrate to the new Enter activity. 2019-02-03 20:23:59 +01:00
Paul Chote
1672041dd4 Port CaptureActor to the new Enter activity. 2019-02-03 20:23:59 +01:00
Paul Chote
f63bd278e1 Rewrite Enter activity, accounting for frozen and hidden actors. 2019-02-03 20:23:59 +01:00
Paul Chote
0a2731fc72 Rewrite Mobile.MoveIntoTarget to support moving targets. 2019-02-03 20:23:59 +01:00
Paul Chote
c304a0682e Rename Enter to LegacyEnter. 2019-02-03 20:23:59 +01:00
Paul Chote
1372b5dd85 Pause actor movement between cells when Mobile is disabled. 2019-02-03 20:23:59 +01:00
Paul Chote
1f861ce35c Fix turn disabling mobile.IsMoving when setIsMoving is false. 2019-02-03 20:23:59 +01:00
Paul Chote
fce4afd448 Fix Positions returned by FrozenActor targets.
Also removes redundant Targetables check from actors.
2019-02-03 20:23:59 +01:00
reaperrr
210de9440c Fix D2k bots wasting cash on building repairs
D2k bots not repairing buildings when damaged due to placement
without concrete was intentional, and this was bleed's default behavior
before BuildingRepairBotModule got introduced, too.
2019-02-03 18:25:03 +01:00
Paul Chote
d3f1f39635 Disable OpportunityFire on Disruptors.
The original game defined NoMovingFire=true.
2019-02-03 18:12:04 +01:00
Paul Chote
789676919c Allow turreted actors to acquire targets while doing other activities. 2019-02-03 18:11:54 +01:00
Paul Chote
9cafc1d7b4 Implement a secondary target-of-opportunity for AttackFollow. 2019-02-03 18:11:47 +01:00
Smittytron
76497eaea6 Add e6 and thf to Monster Tank Madness 2019-02-03 17:10:17 +01:00
Paul Chote
93cef0241d Fix double-revoke crash in Demolishable. 2019-02-03 16:48:26 +01:00
Paul Chote
a641ac08f8 Prefer target to lastVisible target if it is visible. 2019-02-03 16:34:37 +01:00
matjaeck
2d0ee684b8 Reset unit stance on owner change. 2019-02-03 16:26:25 +01:00
reaperrr
f83d680197 Fix that bots don't re-use capturers
They were never removed from activeCapturers when their target becomes invalid,
preventing the bot from reusing them.
2019-02-03 12:13:09 +00:00
reaperrr
b54e45f4a4 Fix CaptureManagerBotModule crashing on multiple Capturable
By removing the now-redundant CaptureTarget class.
2019-02-03 12:13:04 +00:00
Oliver Brakmann
55b8f42ac5 Consider dead aircraft to no longer be in range 2019-02-03 11:31:55 +00:00
tovl
8f16a792cc add check for NextActivity to FlyCircle
prevent infinite loop

fix for ReturnToBase

cleanup
2019-02-03 11:24:42 +00:00
Paul Chote
66282cbd87 Add VolumeModifier support to music. 2019-02-02 22:29:15 +01:00
Paul Chote
96ce888d97 Remove AttackSuicides trait. 2019-02-01 23:19:49 +00:00
Paul Chote
0c7a1ec3bf Implement Hunter-Seeker kill behaviour. 2019-02-01 23:19:20 +00:00
Paul Chote
5c8aa7f0f7 Replace AttackSuicides with AttackFrontal and conditions in RA. 2019-02-01 23:19:17 +00:00
Paul Chote
45fa0dc41e Remove unused negativeDamage variable from AttackBase. 2019-02-01 23:19:13 +00:00
Smittytron
3cb807329a Reduce thief speed and engineer CaptureDelay 2019-02-01 22:43:39 +01:00
Paul Chote
6215e4358a Enable "campaign" bot for all scripted players in D2k missions. 2019-02-01 21:01:00 +01:00
Paul Chote
caddb39e79 Enable "campaign" bot for all scripted players in TD missions. 2019-02-01 21:00:45 +01:00
Paul Chote
fff1d0bfa9 Enable "campaign" bot for all scripted players in RA missions. 2019-02-01 21:00:28 +01:00
Paul Chote
f26a7d7a3f Amend Force Attack command bar tooltip. 2019-01-28 00:22:24 +01:00
Paul Chote
b9d1a83158 Enable ForceFireIgnoresActors on artillery-style units. 2019-01-28 00:22:11 +01:00
Paul Chote
b568023489 Add ForceFireIgnoresActors to AttackBase. 2019-01-28 00:21:59 +01:00
Paul Chote
f6bc07894b Fix gate animations. 2019-01-27 21:38:42 +01:00
Paul Chote
da451896e8 Fix FrozenUnderFog / FrozenActor visibility consistency.
This fixes cases where both objects return visible / not
when queried at the wrong time during a tick.
2019-01-27 15:27:12 +01:00
Paul Chote
7d26e78cb3 Allow attack orders to preempt move completion for turreted units. 2019-01-27 15:26:48 +01:00
Paul Chote
4c28776a93 Reduce cash tick volume in the default mods. 2019-01-27 15:09:56 +01:00
Paul Chote
6a8f4301fb Add VolumeModifier support to sound definitions. 2019-01-27 15:09:40 +01:00
Paul Chote
6ddbab929c Add AttackMove workaround for Aircraft. 2019-01-27 00:20:06 +00:00
Paul Chote
42779c72cf Fix target invalidation and reacquisition in fly activities. 2019-01-26 22:56:22 +00:00
Paul Chote
35acae244c Fix target invalidation and reacquisition in (Fly|Heli)Attack. 2019-01-26 22:56:22 +00:00
Paul Chote
965de05b0a Fix target invalidation and reacquisition in AttackFollow. 2019-01-26 22:56:22 +00:00
Paul Chote
75f5076ac3 Fix target invalidation and reacquisition in LeapAttack. 2019-01-26 22:56:22 +00:00
Paul Chote
70cddb72f2 Fix target invalidation and reacquisition in Attack. 2019-01-26 22:56:22 +00:00
Paul Chote
19da3bc4c2 Fix target invalidation and reacquisition in Follow. 2019-01-26 22:56:22 +00:00
Paul Chote
f950a61886 Fix target invalidation and reacquisition in MoveAdjacentTo. 2019-01-26 22:56:22 +00:00
Paul Chote
d3fbb83877 Split Target.Recalculate into methods with and without invalidation.
TargetExtensions is moved into its own file.
2019-01-26 22:56:22 +00:00
Paul Chote
8e85431266 Define plumbing to pass initial target positions to inner move activities. 2019-01-26 22:56:22 +00:00
Paul Chote
ecd4d996c7 Pass target line color to inner move activities. 2019-01-26 22:56:22 +00:00
Paul Chote
378fecd2db Add support for Terrain targets with multiple positions. 2019-01-26 22:56:22 +00:00
Paul Chote
67d3010d78 Make Target fields readonly. 2019-01-26 22:56:22 +00:00
Paul Chote
eee71368a8 Fix self parameter name in DrawLineToTarget. 2019-01-26 22:56:21 +00:00
Paul Chote
527b4da3f2 Remove FrameEndTask from DrawLineToTarget.
This is no longer needed and causes ordering
issues when the unit becomes idle in the same
tick that SetTargetLine is called.
2019-01-26 22:56:21 +00:00
Paul Chote
7ae2822b6e Fix source package creation. 2019-01-26 23:08:25 +01:00
Paul Chote
8fdddd238b Map Ctrl to Cmd for editor copy hotkey. 2019-01-26 21:48:53 +00:00
rob-v
8dfcc716c8 Add shortcut for Copy in Map editor 2019-01-26 21:48:47 +00:00
Paul Chote
892b60d0eb Deselect actors when their owner changes. 2019-01-26 21:40:48 +00:00
Paul Chote
987f2a26fa Cache world INotifySelection traits in Selection. 2019-01-26 21:40:43 +00:00
Paul Chote
cc2912b993 Only play queued notification if queue is empty.
This matches the behaviour of the RA2 sidebar.
2019-01-26 21:37:03 +00:00
Paul Chote
76b15d7e37 Replace broken (Disabled)TabClick and with (Disabled)ClickSound. 2019-01-26 21:36:58 +00:00
Paul Chote
1a0b975d31 Disable bot logic during replays. 2019-01-26 21:34:56 +00:00
abcdefg30
b55a1b86db Allies03b: Fix the insertion helicopter revealing shroud 2019-01-26 21:30:12 +00:00
abcdefg30
7840f92f39 Allies03: Remove hacke6 2019-01-26 21:30:08 +00:00
abcdefg30
c13a0e2c9e Allies03b: Fix heavy tank reinforcements triggering twice 2019-01-26 21:30:04 +00:00
abcdefg30
edd92c7d3b Allies01: Remove redundance (caused by inheritance) 2019-01-26 21:30:00 +00:00
abcdefg30
beb9cae9ca Allies01: Add a new line at the end of the briefing 2019-01-26 21:29:56 +00:00
abcdefg30
1ecb921364 Allies01: Fix civilian infantry being visible below fog 2019-01-26 21:29:52 +00:00
abcdefg30
ffc7314a7f Allies01: Fix the extraction helicopter revealing shroud 2019-01-26 21:29:48 +00:00
abcdefg30
a4420bad23 Allies01: Fix the extraction helicopter landing before being removed 2019-01-26 21:29:44 +00:00
abcdefg30
b8c90ef506 Fix OnAllRemovedFromWorld only triggering once 2019-01-22 23:09:36 +00:00
Paul Chote
f30b659b3f Remove Game.Debug messages from ValidateOrder. 2019-01-22 22:59:51 +00:00
rob-v
35ee5b807a No player name in replay for chat commands 2019-01-22 22:57:21 +00:00
abcdefg30
7f21a868bd Fix LeapAttack setting attack.IsAiming too early 2019-01-22 22:54:05 +00:00
abcdefg30
1eb8c395e7 Reset the client state when being moved to spectator 2019-01-22 22:48:05 +00:00
abcdefg30
d2a8a8f19a Only consider system maps in the mission browser 2019-01-19 18:48:04 +00:00
abcdefg30
b826c5e22a Use map folder names instead of paths in the mission browser 2019-01-19 18:47:58 +00:00
rob-v
65f5b763e9 Fix Warhead.IsValidAgainst (FrozenActor.Owner null) 2019-01-19 12:03:25 +00:00
abcdefg30
b167424654 Move Actor103 in Allies02 one cell to the right
He was standing on impassable terrain and therefore couldn't move.
2019-01-19 11:56:41 +00:00
abcdefg30
0777fe534d Let the remaing enemy troops in Allies02 attack the player
once the base is destroyed.
Does not include unit guarding the convoy path.
2019-01-19 11:56:36 +00:00
Smittytron
c3a8065b99 Add IsDead check to fix crash in Sarin Gas 1 2019-01-19 11:45:46 +00:00
abcdefg30
59dcab4db0 Fix a crash in Infiltration 2019-01-19 11:27:28 +00:00
Smittytron
4631cbfc2a Change tree husks from FrozenUnderFog to HiddenUnderShroud 2019-01-19 11:17:05 +00:00
Smittytron
315f1188ed Change LST turn speed to default max 2019-01-07 12:49:16 +01:00
Mustafa Alperen Seki
eae3f297f3 Hide husks under fog regardless of their owner. 2019-01-06 22:11:56 +01:00
Paul Chote
6de92de8d9 Revert macOS dark mode (again).
The updated GL surface appears to have regressed vsync behaviour.
2019-01-06 08:33:11 +00:00
reaperrr
b05d246c48 Add BotDebug message for external unit build requests
For easier bot debugging of things like MCV- and harvester replacement.
2019-01-06 08:39:45 +01:00
reaperrr
3a1656c3dd Remove unused BuildUnit overload from UnitBuilderBotModule
Unused and didn't check things like Buildable, so better just remove it.
2019-01-06 08:39:45 +01:00
reaperrr
b2649749d9 Enable harvester replacement in official mods 2019-01-06 08:39:45 +01:00
reaperrr
137d3be346 Add plumbing for bots auto-replacing harvesters
If their number drops below refinery count.
2019-01-06 08:39:45 +01:00
reaperrr
e36ef57e35 Increase default scan interval for idle bot harvesters
Every 2 seconds (at default speed) should be enough.
2019-01-06 08:39:45 +01:00
reaperrr
481e5e03d8 Make bots deploy MCV on first tick
And use a boolean instead of counting ticks.
2019-01-06 08:39:45 +01:00
Paul Chote
4d3db0d454 Fix invalid target crash if Leap target dies. 2019-01-05 23:59:25 +01:00
Paul Chote
01f5c67036 Fix spectator crash if replay does not define FinalGameTick. 2019-01-05 23:56:21 +01:00
Paul Chote
f929087d15 Fix artwork when leaping. 2019-01-05 19:54:38 +01:00
Paul Chote
ae38133c9f Use the CenterPosition as the starting position. 2019-01-05 19:54:38 +01:00
Paul Chote
0c7158efcd Calculate Leap state on first run instead of construct. 2019-01-05 19:54:38 +01:00
abcdefg30
6b9a2a3c29 Reduce the range of DogJaw from 3c0 to 2c0 2019-01-05 18:28:57 +00:00
Paul Chote
f5d788f4fc Prevent unit requests from stacking during production. 2019-01-04 21:14:20 +00:00
Ivaylo Draganov
caead311cb Add hotkey for Army value statistics panel 2019-01-04 21:12:06 +00:00
Ivaylo Draganov
f26905f5d0 Rearrange default observer hotkeys
* Move replay speed hotkeys to `F9 - F12`
* Set `F5` as default for `StatisticsGraph`
2019-01-04 21:12:06 +00:00
rob-v
a77d2f15b1 Map Editor - Copy filter 2019-01-03 20:32:10 +01:00
Paul Chote
5a8f7f1a5f Increase squad calculation intervals.
These were unnecessarily short, increasing the
AI performance overhead, and making it difficult
for units to escape concave terrain features.
2019-01-03 02:04:08 +01:00
Paul Chote
3d9e877eb2 Disable rush attacks against enemy aircraft. 2019-01-03 02:04:08 +01:00
Paul Chote
8a6a68feef Unify Squad enemy unit filtering. 2019-01-03 02:04:08 +01:00
Paul Chote
6fc291a634 Disable target recalculation for bots.
This fixes bot-controlled units freezing when they
lock on to units that aren't visible.
2019-01-03 02:04:08 +01:00
Mustafa Alperen Seki
4578ea09ba Add DamageTypes to Capture Sabotage 2019-01-02 20:04:06 +01:00
Mustafa Alperen Seki
baac3f3ee9 Remove C17 from map editor. 2019-01-02 19:57:24 +01:00
Mustafa Alperen Seki
816cb2cdc2 Remove duplicate D2k starport actors from map editor. 2019-01-02 19:57:24 +01:00
Mustafa Alperen Seki
0c540cd41e Remove actors with AttackBomber trait from map editor. 2019-01-02 19:57:24 +01:00
Mustafa Alperen Seki
6b4ba96e34 Remove aircraft husks from map editor actor list. 2019-01-02 19:57:24 +01:00
Mustafa Alperen Seki
b0188cc476 Remove Buildable traits from A10 and C17.
Leftover from upgrade rule that move description from Tooltip to
Buildable.
2019-01-02 19:57:24 +01:00
Mustafa Alperen Seki
eaa9b49793 Add Stance Support to GrantExternalConditionPower. 2019-01-01 23:05:56 +01:00
Paul Chote
d7c54d74ad Evaluate smooth scrolling per-frame instead of per-tick. 2019-01-01 21:52:13 +01:00
Clément Bœsch
bb5e0eafba Observer: display army value in a new dedicated graph tab 2019-01-01 18:25:44 +00:00
Clément Bœsch
11b064a333 Observer: display army value in stats widget 2019-01-01 18:25:44 +00:00
Clément Bœsch
501c029579 TS: set UpdatesPlayerStatistics.AddToArmyValue where appropriate 2019-01-01 18:25:44 +00:00
Clément Bœsch
7cde528969 D2K: set UpdatesPlayerStatistics.AddToArmyValue where appropriate 2019-01-01 18:25:44 +00:00
Clément Bœsch
a162cdda41 CNC: set UpdatesPlayerStatistics.AddToArmyValue where appropriate 2019-01-01 18:25:44 +00:00
Clément Bœsch
3aedeefced RA: set UpdatesPlayerStatistics.AddToArmyValue where appropriate 2019-01-01 18:25:44 +00:00
Clément Bœsch
3ec2f23109 Add Army value to player statistics
To be accounted as army, the unit needs to have
UpdatesPlayerStatistics.AddToArmyValue to true (false by default)
2019-01-01 18:25:44 +00:00
Paul Chote
4d56ecb3a8 Remove unused MaximumDefenseRadius parameter. 2019-01-01 12:43:10 +01:00
Paul Chote
e23b6f8a9d Fix screenshot pixel opacity. 2019-01-01 11:28:05 +00:00
rob-v
8c94f262b6 Map Editor - Tiles' filters multiple selection 2019-01-01 11:25:31 +00:00
rob-v
f18d874524 CommonSelectorLogic for ActorSelectorLogic and TileSelectionLogic 2019-01-01 11:25:31 +00:00
reaperrr
63f76fc277 Exclude dogs from bot squads in RA
This prevents bots from using their up-to-4 dogs in attacks.
Bots aren't good at using them effectively, so they're better as passive defense
against infantry attacks or spies/engineers sent by human players (and maybe later bots, too).
2018-12-31 16:15:03 +00:00
reaperrr
7ccfe0d2e7 Move up GrantConditionOnBotOwner in AI yamls
Right below the bot traits is better for readability than between modules.
2018-12-31 16:15:03 +00:00
Paul Chote
59f2f5669f Change UpdateRules to account for 20181215 hotfix 2018-12-31 14:22:49 +01:00
reaperrr
d179f6eaae HackyAI dissolve update rule and yaml updates 2018-12-31 10:56:01 +00:00
reaperrr
54c2894b4e Split off last bot modules
And dissolve AI namespace.
There would have been so little left in Common.AI,
that keeping it made no sense anymore.
2018-12-31 10:56:01 +00:00
Paul Chote
b74ff33039 Revert "Fix QuantizeFacing returning values >= numFacings."
This reverts commit f35ee8c303.
2018-12-31 00:01:03 +00:00
Paul Chote
cc004b3546 Fix compatibility with macOS's dark mode. 2018-12-30 23:57:06 +00:00
Smittytron
1facff6ab1 Override Bio Lab construction options in campain-rules.yaml 2018-12-30 21:54:27 +01:00
Smittytron
d751c055b5 Add Counterstrike mission Sarin Gas 1: Crackdown 2018-12-30 21:54:27 +01:00
Dylan Manitta
f5d12bfde1 Add ant mission 1 2018-12-30 21:20:56 +01:00
abcdefg30
0ff4e466ee Refactor Leap attack logic 2018-12-29 19:21:54 +01:00
abcdefg30
9c4cb9091e Change the setter of AttackBase.IsAiming to be public 2018-12-29 19:21:54 +01:00
abcdefg30
8689030f79 Add GrantConditionWhileAiming 2018-12-29 19:21:54 +01:00
dtluna
7608922ff0 Add Nod 10a mission 2018-12-27 21:09:14 +01:00
dtluna
809f57e48d Add Nod 10b mission 2018-12-27 19:22:37 +01:00
Smittytron
a0089d97e6 Fix typo in CrateSpawner 2018-12-27 04:11:54 +01:00
Mustafa Alperen Seki
00faccdecc Fix LandOnCondition causing stopping after changing altitude. 2018-12-26 17:44:45 +01:00
Paul Chote
2a2ad71db9 Add GUI checkbox for Debug.StrictActivityChecking. 2018-12-26 17:33:55 +01:00
Paul Chote
4dea39fffe Reorder and document advanced settings. 2018-12-26 17:33:55 +01:00
Paul Chote
69105180eb Hide developer-only settings behind a hidden setting. 2018-12-26 17:33:55 +01:00
Paul Chote
601990aa27 Add setting to check BotModule sync. 2018-12-26 17:33:55 +01:00
Paul Chote
83e44bee66 Rework and rename Sync.CheckSyncUnchanged 2018-12-26 17:33:55 +01:00
Paul Chote
b41c178cb9 Revert "Units that have turrets while deployed now move their turrets back to their initial positions before undeploying"
This reverts commit d34bea2935.
2018-12-26 16:35:26 +01:00
Unknown
7184f5f97e Fix QueryRemoteMapDetails multi-map status updates 2018-12-25 11:14:48 +13:00
Mustafa Alperen Seki
a2ac95d140 Add facing support to Gravity Bomb. 2018-12-24 20:58:55 +01:00
Jeremy
85a97998aa Added final game tick to replay meta data for completion percentage on replay. 2018-12-24 20:51:03 +01:00
Mustafa Alperen Seki
863091d5cc Make Concrete under buildings indestructible. 2018-12-24 13:48:19 +01:00
reaperrr
be310ab6a6 Streamline resource anim traits
- Replaces WithSiloAnimation with
  WithResourceLevelSpriteBody.

PlayFetchIndex on a With*Animation trait conflicts
with the animation concept, as it's bound to conflict
with pretty much all 'normal' animation traits and
blocks progress on the animation priority system.

We also already have multiple similar SpriteBody traits,
like WithGateSpriteBody and WithWallSpriteBody.

- Rename WithResources to WithResourceLevelOverlay

Make name more accurate and consistent with sprite body
equivalent.
Also fix TS silo yaml setup (bleed setup stems from times
before WithResources was introduced).
2018-12-24 21:29:42 +13:00
reaperrr
305d82f887 Replace WithChargeAnimation with -SpriteBody
PlayFetchIndex on a With*Animation trait conflicts with the animation
concept, as it's bound to conflict with pretty much all 'normal'
animation traits and blocks progress on the animation priority system.

We also already have multiple precedent SpriteBody traits of similar kind,
like WithGateSpriteBody and WithWallSpriteBody.
2018-12-24 21:29:42 +13:00
Chris Forbes
e292e88bff Improve RenderShroudCircle configuration. 2018-12-24 01:57:11 +01:00
Oliver Brakmann
680ffffff2 Cache some more strings in GameInfoStatsLogic 2018-12-24 00:55:09 +01:00
Oliver Brakmann
3507167e79 Fix player score not updating while game info screen is visible 2018-12-24 00:55:09 +01:00
David Wilson
bbc83c1799 Rate limits for notification sounds 2018-12-24 12:18:52 +13:00
Paul Chote
9b3ddee517 Fix QuantizeFacing returning values >= numFacings. 2018-12-24 00:00:06 +01:00
Oliver Brakmann
e5f34a99ac Fix Lint test failures in Allies08b 2018-12-24 11:06:17 +13:00
Paul Chote
3817c7b96f Change existing husks to neutral when owner loses. 2018-12-23 22:14:56 +01:00
Smittytron
65269f7950 Add Allies08b 2018-12-23 21:59:14 +01:00
reaperrr
7ed67338f3 Allow forcing sprite body Z position to ground 2018-12-23 17:16:06 +13:00
Paul Chote
5efbcf19f2 Fix cell-out-of-bounds crashes in BuildableTerrainLayer. 2018-12-22 21:15:09 +01:00
reaperrr
6db27b1839 Split off CaptureManagerBotModule
from HackyAI.

Note: This isn't used in any official mod right know,
and known to be bugged on bleed already.
2018-12-22 17:42:53 +01:00
Paul Chote
d823d38e8c Fix "game is full" error when the game is not full.
Client.IsObserver is not valid to check until the
slot has been assigned.
2018-12-22 04:55:57 +01:00
Oliver Brakmann
e71a31925f Require explicit implementation of INotifyObjectivesUpdated 2018-12-22 16:39:06 +13:00
Smittytron
d10d48c25f RA balance changes for December 2018 2018-12-22 11:07:27 +13:00
reaperrr
9914848356 BaseBuilder- and BuildingRepairBotModule update rule 2018-12-19 21:50:54 +13:00
reaperrr
d46710d6ce Fix bot module update rule setting wrong RequiresCondition
Yaml nodes are reference types, so caching this meant changes would be applied on all of them.
Additionally, only add HarvesterBotModule if at least one AI is actually using it.
2018-12-19 21:50:54 +13:00
reaperrr
04c34741c8 Extract BaseBuilderBotModule from HackyAI 2018-12-19 21:50:54 +13:00
Paul Chote
9f30e2ecb0 Add a --utility argument to the AppImages. 2018-12-17 22:34:37 +01:00
Paul Chote
224377f078 Track visibility modifiers on FrozenActors. 2018-12-17 22:19:26 +01:00
Paul Chote
5f79c31a57 Add AutoTarget support for FrozenActors. 2018-12-17 22:19:26 +01:00
Paul Chote
c34dd4b824 Allow Attack activities to target FrozenActors directly.
Removing the legacy FrozenActor to Actor workaround
fixes a number of long-standing bugs.

This also prevents units from losing their target when
it transforms into a different actor type.
2018-12-17 22:19:26 +01:00
Paul Chote
0406b89a96 Add Actor.ReplacedByActor to track transformations.
This isn't great conceptually, but has precedent
in the Generation number.
2018-12-17 22:19:26 +01:00
Paul Chote
2ac7e451b4 Remove AttackBase.IgnoresVisibility.
This was a workaround for D2K sandworms, which is
now implemented using a custom attack activity.
2018-12-17 22:19:26 +01:00
Paul Chote
8eeb6d68e7 Tweak FrozenActorLayer queries:
- FrozenActorsInRegion now filters for valid and (optionally) visible FAs
- Add new FrozenActorsInCircle to mirror World.FindActorsInCircle.

The first change means that SupportPowerDecision now correctly ignores
FrozenActors that the AI has not discovered.
2018-12-17 22:19:26 +01:00
Paul Chote
3e490e5843 Cache FrozenActorLayer on the Player object.
This avoids unnecessary trait queries.
2018-12-17 22:19:26 +01:00
Andre Mohren
f238e2c5cc Fixed filename 2018-12-17 10:28:54 +13:00
reaperrr
b048e9c77b Fix two typos in TD music.yaml
One major (filename, track wouldn't show up) and one minor (titles should be all uppercase).
2018-12-16 18:19:40 +01:00
Paul Chote
98b80d44eb Remove legacy workaround that crashes modern Mono. 2018-12-15 23:35:29 +01:00
Paul Chote
04359206ff Remove long-broken setting to ignore version mismatches. 2018-12-15 23:30:36 +01:00
Paul Chote
b7317f2202 Add auth and sync report settings to dedicated server scripts. 2018-12-15 23:30:36 +01:00
Paul Chote
cc707f0037 Disable MP sync report generation by default.
A new Server.EnableSyncReports option is provided
so that server operators can restore them remotely
in the event of a future desync bug.
2018-12-15 23:30:36 +01:00
Paul Chote
081182b60f Profile sync report generation separately. 2018-12-15 23:30:36 +01:00
Paul Chote
9cee77ed8c Add hardcoded fallback mountpoints for asset detection on Linux. 2018-12-15 22:22:54 +01:00
abcdefg30
ee221f3e0d Revert balance changes to civilian buildings for the campaign missions 2018-12-10 10:01:31 +13:00
abcdefg30
48360bad8b Use the tooltips from the original game for MISS and FCOM 2018-12-10 10:01:31 +13:00
abcdefg30
fe05dad670 Disable inaccurate tooltip descriptions in the campaign missions 2018-12-10 10:01:31 +13:00
Paul Chote
ea9f12ffbc Simplify and fix panel positioning at different zooms. 2018-12-08 22:53:13 +01:00
Paul Chote
4723e5ddb9 Expose common actor Inits in the map editor. 2018-12-08 22:53:13 +01:00
Paul Chote
f6768fe624 Remove legacy editor actor properties plumbing. 2018-12-08 22:53:13 +01:00
Paul Chote
1d98b8b8f0 Fix damagestates in the map editor. 2018-12-08 22:53:13 +01:00
Paul Chote
07fc67f58d Remove obsolete and unused PreventsTeleport method. 2018-12-08 22:34:44 +01:00
Paul Chote
38f341ac1d Allow MadTank Detonate order to be queued. 2018-12-08 22:34:44 +01:00
Ivaylo Draganov
9be7298311 Remove airstrike/paradrop beacon when the whole squad is shot down 2018-12-08 22:30:17 +01:00
Paul Chote
13f5ef50b9 Fix production tooltip padding. 2018-12-08 15:32:58 +01:00
Paul Chote
3e7caa2faa Fix Commando/Tanya build announcements. 2018-12-08 15:27:06 +01:00
Paul Chote
d9f8afdbe5 Add GrantExternalConditionToProduced trait. 2018-12-08 15:27:06 +01:00
Paul Chote
73198dc45a Fix queued EnterTransport unload glitch. 2018-12-08 11:41:20 +01:00
Paul Chote
4b6853b433 Prevent multiple Transforms from triggering in the same tick.
This leads to actor duplication.
2018-12-08 11:34:49 +01:00
Smittytron
72923b9572 Merge Hijacker into Thief 2018-12-07 17:38:24 +03:00
Inq8
e6668bbb59 Improve Hind visibility
Recoloured the hind to a lighter shade to alleviate #15401
2018-12-06 18:09:23 +13:00
reaperrr
bdbc19376a Fix bot module update rule NRE on overrides
We cannot reliably update overrides of base HackyAI definitions,
unless they (re-)define Type.
If they don't, we now instead just list their locations.
2018-12-05 09:29:13 +13:00
reaperrr
20ba45d467 Remove queueing HeliFlyCircle from AutoCarryall 2018-12-05 09:20:58 +13:00
reaperrr
ac3e601edf Use INotifyIdle on Aircraft
For now only to trigger landing or circling.
2018-12-05 09:20:58 +13:00
reaperrr
d8220b390a Add IdleTurnSpeed to Aircraft
Instead of hardcoding 1/3 of normal TurnSpeed on HeliFlyCircle.
2018-12-05 09:20:58 +13:00
reaperrr
1553a8a5cb Fix empty activity tick when becoming idle
Activities usually don't do much more than cleanup on their last, 'null' tick.
That, combined with queued activities normally only starting to tick on the next tick,
would lead to visible 1-tick 'gaps' between movement activities.
Non-movement activities would suffer from the same problem,
only with different (presumably less noticable) symptoms.

Now we start ticking any activity that was queued from OnBecomingIdle
immediately, to avoid that issue.
2018-12-05 09:20:58 +13:00
reaperrr
87fa8a77c2 Make various D2k map deco require Neutral owner
And make worm require Creeps.
2018-12-05 09:07:30 +13:00
reaperrr
eaca8b6287 Make various RA map deco require Neutral owner 2018-12-05 09:07:30 +13:00
reaperrr
7c9856ded7 Make various TD map deco require Neutral owner 2018-12-05 09:07:30 +13:00
reaperrr
7503919659 Enforce Neutral owner for TS decorations
Fortunately, all official maps already adhered to that.
2018-12-05 09:07:30 +13:00
reaperrr
eb86160021 Remove CrateEffect in favor of using updated SpriteEffect 2018-12-05 09:04:29 +13:00
reaperrr
4de0d0fcb9 Replace LaunchEffect with updated SpriteEffect 2018-12-05 09:04:29 +13:00
reaperrr
c2d6b78b18 Add dynamic position support to SpriteEffect
This was the last missing 'puzzle piece' to replace some remaining spcial-case effects.
2018-12-05 09:04:29 +13:00
Paul Chote
6ac7f887c0 Fix a VS code style suggestion. 2018-11-26 19:46:05 +01:00
Paul Chote
9e85aefca8 Make the lobby teamchat selector match the in-game selector. 2018-11-26 19:46:05 +01:00
Oliver Brakmann
7454427b13 Fix FindResources aborting to early 2018-11-25 22:41:46 +00:00
Paul Chote
8a95241fd5 Change mechanics to repair ally-owned husks.
Now uses the goldwrench cursor and keeps the
original owner.
2018-11-25 19:20:28 +01:00
Paul Chote
89161b61ec Remove neutral-owner workaround from RA husks. 2018-11-25 19:20:28 +01:00
Paul Chote
fac271245b Add InfiltrateForTransform trait. 2018-11-25 19:20:28 +01:00
Paul Chote
66464a6164 Add cursor support to Infiltrates.
Also fixes targeting vs ally-owned actors.
2018-11-25 19:20:28 +01:00
Paul Chote
26363e5811 Remove references to buildings from infiltration Descs. 2018-11-25 19:20:28 +01:00
Paul Chote
7ddcc2e958 Remove the default notification from Infiltrates. 2018-11-25 19:20:28 +01:00
reaperrr
451a38338b Convert AISupportPowerManager to module 2018-11-25 19:00:44 +01:00
netnazgul
c195699476 Implement a slider widget for volume control that returns an exponentially scaled value 2018-11-25 16:54:30 +01:00
Smittytron
9c08e430e2 Add myself to AUTHORS 2018-11-24 23:21:01 +01:00
Smittytron
32968e4f4b Add Soviet08a 2018-11-24 23:21:01 +01:00
reaperrr
67cba65800 Fix bot module plumbing
Fixes the issues pointed out after the original harvester module was merged.
Also merges the update rules as discussed on IRC.
2018-11-24 11:05:37 +00:00
David Wilson
22bece2dc9 Add a basic actor properties panel to the editor. 2018-11-24 10:14:17 +00:00
abcdefg30
9b4db3468b Fix CombatProperties not accounting for multiple AttackBase traits 2018-11-22 23:16:30 +00:00
Brenton Horne
2d4d6cdc1b Fixing several ShellCheck warnings 2018-11-22 21:30:59 +00:00
Paul Chote
807a40c209 Remove IExplodeModifier interface. 2018-11-22 22:16:56 +01:00
Paul Chote
80842fd4b8 Add GrantConditionOnPlayerResources trait. 2018-11-22 22:16:56 +01:00
Paul Chote
3be008f592 Add EmptyCondition to Harvester. 2018-11-22 22:16:56 +01:00
Paul Chote
5f2cc5981d Remove unused IExplodeModifier from Refinery. 2018-11-22 22:16:56 +01:00
reaperrr
10e51db236 Remove hardcoded mpspawn owner lint check
Use RequiresSpecificOwner to enforce the owner
that owns the world instead.

Require 'Neutral' in the official mods accordingly.
2018-11-21 22:29:55 +00:00
reaperrr
1eb573bcbc Enforce required owner in map editor
It can easily happen that mappers forget to set the
current player to Neutral before placing more trees,
for example, so we force the editor to set a valid owner.
2018-11-21 22:29:55 +00:00
reaperrr
fcb09d069b Add RequiresSpecificOwners trait
To enforce specific owners via Lint rules,
and possibly other means later.
This is for cases where accidentally setting an
unfitting owner via editor could cause issues.

Example: AI might try to attack Creeps-owned trees
and get stuck.
2018-11-21 22:29:55 +00:00
Oliver Brakmann
310b63150f Check for player trait prereqs in ProximityCapturable 2018-11-21 22:12:07 +00:00
Andre Mohren
c3f4bc484d Correctly handle Production traits disabled by condition. 2018-11-21 22:13:20 +01:00
Mustafa Alperen Seki
53032576e2 Update default mods for Heal WH full health check removal 2018-11-21 19:25:45 +01:00
Mustafa Alperen Seki
6a599e57f6 Remove check for full health for negative damage warheads 2018-11-21 19:25:45 +01:00
Andre Mohren
99de33bbe3 Added smudge chance. 2018-11-21 18:00:11 +01:00
TheChosenEvilOne
e01953afa3 Made Turreted PausableConditional 2018-11-21 17:32:53 +01:00
Paul Chote
1af9efe246 Avoid a crash if subjects is empty.
This can happen in the rare instance that the last
actor in the selection is killed in the same tick
that the OG is activated, and GetCursor is called
before the next tick cancels the OG.
2018-11-20 21:55:55 +01:00
teinarss
3b6024c086 Defer setting slot on client to completeConnection 2018-11-19 22:05:32 +00:00
reaperrr
b8d3c9f73a Fix aircraft being repaired mid-air
Repairable was originally written for ground actors,
so it's both safer and much easier to just handle this in Aircraft directly.
2018-11-19 21:40:10 +00:00
reaperrr
560d7b4ee8 Fix Repairable crash
MoveAdjacentTo is a Mobile-only activity.
2018-11-19 21:40:10 +00:00
reaperrr
7d695f0c8f Fix actors in ReturnFire stance following targets
On bleed, if AllowMovement is true actors with ReturnFire will actually follow the acquired target, unlike in Defend stance.
This is at least unintuitive, since ReturnFire is expected to be more passive than Defend.
2018-11-18 16:19:50 +01:00
teinarss
bc009634e5 Show team/spawn widget after admin transfer. 2018-11-18 14:47:10 +01:00
Paul Chote
ad4b4dc7f8 Remove unused tileset update/save code. 2018-11-18 14:11:11 +01:00
Andre Mohren
0fca984463 Implemented InfiniteBuildAfter. 2018-11-17 17:39:18 +00:00
Andre Mohren
89051d40e8 Remove utf8 BOM. 2018-11-17 17:23:22 +00:00
Andre Mohren
7323db1492 Unified copyright regions. 2018-11-17 17:23:22 +00:00
Andre Mohren
b1a44086a0 Removed unused using directives. 2018-11-17 17:23:22 +00:00
Ivaylo Draganov
db64dc82c3 Fix damaged-idle sequence of RA refinery 2018-11-16 21:33:23 +01:00
reaperrr
cd82382f68 Change CreateEffectWarhead to use World.LocalRandom 2018-11-11 19:50:16 +01:00
reaperrr
16e78b8ca8 Add LocalRandom to World
To - in the long term - reduce or remove Game.CosmeticRandom.
2018-11-11 19:50:16 +01:00
Mustafa Alperen Seki
becfc154c5 Add Creates/RevealsShroudMultiplier. 2018-11-10 12:14:14 +01:00
Mustafa Alperen Seki
4987d45f23 Fix a crash when protected properties are [Sync]ed. 2018-11-10 12:14:14 +01:00
Mustafa Alperen Seki
3224843d70 Add Demolition>(Force)TargetStances 2018-11-10 11:48:28 +01:00
TheChosenEvilOne
51ec97fb2c Check for null in Turreted.StopAiming 2018-11-09 23:58:16 +01:00
reaperrr
74fa8752c9 Revert granting condition from HackyAI
In favor of using GrantConditionOnBotOwner.
Updated update rule and shipping mods accordingly.
2018-11-09 23:45:24 +01:00
reaperrr
4c9c8bf7fc Remove unused HarvesterEnemyAvoidanceRadius
...from HackyAI. This removal either got lost during rebase,
or was simply forgotten to apply when partially rewriting HarvesterBotModule.
2018-11-07 11:01:21 +01:00
Paul Chote
2064dc7c30 Support non-int Enum types in the Lua API. 2018-11-04 18:32:33 +01:00
Chris Forbes
d653614e75 Replicate palette high bits into the low bits
Previously we didn't quite get the full range -- the most intense value we
could produce was 0xfc.
2018-11-04 15:06:50 +01:00
abcdefg30
ae0a0163cc Fix DamagesConcreteWarhead crashing when a target becomes invalid 2018-11-04 10:49:29 +00:00
Ivaylo Draganov
31f2441709 Draw target lines for allied players and observers 2018-11-04 06:34:43 +01:00
reaperrr
927b6cd561 Convert AIHarvesterManager into *Module 2018-11-04 01:11:00 +01:00
reaperrr
04c69efc30 Prepare HackyAI for module support
- Split order handling to BotOrderManager
- Make HackyAI provide a condition
- Move BotDebug to AIUtils
2018-11-04 01:11:00 +01:00
reaperrr
71fb670def Move InitialFacing to ^Helicopter default
In RA and TD mods.
2018-11-04 00:25:55 +01:00
reaperrr
92912c6c94 Add TurnToDock to Aircraft
Instead of hard-coding a turn before VTOLs
land/dock on resupplier.
2018-11-04 00:25:55 +01:00
reaperrr
e2227b9450 Make HeliReturnToBase use a landingProcedures list
Like ReturnToBase already does. Makes them easier to compare and later merge.
2018-11-04 00:25:55 +01:00
reaperrr
577fc1c409 Remove separate AircraftInfo caching from ReturnToBase
This extra info caching was overkill and most likely had zero effect on performance.
2018-11-04 00:25:55 +01:00
reaperrr
54d3656205 Move CalculateTurnRadius up in ReturnToBase
Just a slight readability improvement.
2018-11-04 00:25:55 +01:00
Mustafa Alperen Seki
a3d9822bb3 Update TD mod for Cloak>RequiresCondition to PauseOnCondition 2018-11-03 22:40:28 +00:00
Mustafa Alperen Seki
b1db79cce2 Update D2k mod for Cloak>RequiresCondition to PauseOnCondition 2018-11-03 22:40:28 +00:00
Mustafa Alperen Seki
faad2b8653 Update TS mod for Cloak>RequiresCondition to PauseOnCondition 2018-11-03 22:40:28 +00:00
Mustafa Alperen Seki
d9ecbb0351 Update RA mod for Cloak>RequiresCondition to PauseOnCondition 2018-11-03 22:40:28 +00:00
Mustafa Alperen Seki
dd39ab5b12 Add Update Rule to change Cloak>RequiresCondition to PauseOnCondition 2018-11-03 22:40:28 +00:00
Mustafa Alperen Seki
dd92ec4d02 Make Cloak PausableConditional
If disabled now it CloakDelay resets to InitialCloakDelay when
reenabled, if paused it continues with CloakDelay when resumed.
2018-11-03 22:40:28 +00:00
Ivaylo Draganov
a7279415dc Allow player to add a "priority unit" to production queue
* If production is ordered by `Ctrl + Left Click` the item is added to the top of the stack after the currently produced item
* Works with `Shift` for priority queueing of 5 items
* This modifier is not taken into account for `ParallelProductionQueue` as it doesn't make sense in that context
2018-11-03 17:54:50 +01:00
Paul Chote
81d53a4f1a Improve error message for children of removals. 2018-11-03 17:36:40 +01:00
Paul Chote
f05e3e871f Fix crash when Inherits has child nodes. 2018-11-03 17:36:40 +01:00
abcdefg30
f968b169ad Fix PrimaryBuilding changing the status of and from disabled queues 2018-11-03 17:30:05 +01:00
abcdefg30
272d9b99fd Fix ProductionFromMapEdge blocking the base Created call. 2018-11-03 17:30:05 +01:00
abcdefg30
cde18221e6 Add Lua API support for actors with multiple Production traits. 2018-11-03 17:30:05 +01:00
abcdefg30
c0ee199ad1 Support multiple Production traits in WithProductionOverlay. 2018-11-03 17:30:05 +01:00
abcdefg30
a03abe78af Ignore disabled production traits. 2018-11-03 17:30:05 +01:00
Paul Chote
4a4415c74b Remove unnecessary caching of ProductionAirdropInfo. 2018-11-03 17:30:05 +01:00
abcdefg30
394e33dcc2 Improve ClonesProducedUnits logic:
- Supports multiple Production trait instances
- Clones the correct faction variant, if defined
2018-11-03 17:30:05 +01:00
Paul Chote
ea3731a7cc Pass the original init dict to UnitProducedByOther. 2018-11-03 17:30:05 +01:00
abcdefg30
5e5183549c Overhaul ProductionBar:
- Is now a conditional trait
- Now respects multiple Production trait instances
- ProductionType is now required
2018-11-03 17:30:05 +01:00
abcdefg30
7f255a17da Revert "Add conyard.corrino"
This reverts commit a0d4a03530.
2018-11-03 17:30:05 +01:00
Smittytron
557c87eecb Remove unused waypoints from Soviet02a 2018-11-03 15:34:00 +01:00
Paul Chote
fc6ada38f4 Revoke WithMakeAnimation condition at the end of the tick.
This fixes traits becoming enabled for a tick
between the animation completing and the actor
being removed from the world.
2018-11-03 15:28:05 +01:00
Paul Chote
47a470e945 Remove legacy Building plumbing. 2018-11-03 15:09:14 +01:00
Paul Chote
e77aaa1a47 Remove (INotify)BuildComplete from Attack* 2018-11-03 15:09:14 +01:00
Paul Chote
e57087cb5b Remove building lock from Production. 2018-11-03 15:09:14 +01:00
Paul Chote
7bc53dd266 Remove building lock from ToggleConditionOnOrder. 2018-11-03 15:09:14 +01:00
Paul Chote
94088d37a6 Remove building lock from Sellable. 2018-11-03 15:09:14 +01:00
Paul Chote
492bcdd9a7 Remove building lock from Transforms. 2018-11-03 15:09:14 +01:00
Paul Chote
18b84750aa Reimplement demolition lock using conditions. 2018-11-03 15:09:14 +01:00
Paul Chote
0901a7d9de Simplify FlashTarget.
Now defined in terms of a flash count, interval, and delay.
Broken FlashDuration parameter removed from Demolition.
2018-11-03 15:09:14 +01:00
Paul Chote
1b9f23eca0 Replace AnnounceOnBuild with VoiceAnnouncement. 2018-11-03 15:09:14 +01:00
Paul Chote
78a2d9aa23 Remove INotifyBuildComplete from ConyardChronoReturn. 2018-11-03 15:09:14 +01:00
Paul Chote
ae3bfb73a1 Fix LastChildMatching ignoring the includeRemovals argument. 2018-11-03 15:09:14 +01:00
Paul Chote
3d6b170ec3 Support multiple capture traits in order targeter and script plumbing. 2018-11-03 14:47:22 +01:00
Paul Chote
346e670563 Simplify type filtering in GivesCashOnCapture/TransformOnCapture. 2018-11-03 14:47:22 +01:00
Paul Chote
7e67ce0139 Pass CaptureTypes through the INotifyCapture interface.
Also make it require explicit implementation.
2018-11-03 14:47:22 +01:00
Paul Chote
4ea3e8382d Work around a race condition between server join and auth validation. 2018-11-03 14:25:05 +01:00
reaperrr
e42094625d Merge AttackPlane and AttackHeli into AttackAircraft 2018-11-03 11:37:23 +00:00
Taryn Hill
5899636e10 Fix SubCell indexing in Map.CenterOfSubCell and MapGrid.OffsetOfSubCell 2018-11-02 22:42:51 +00:00
reaperrr
8f1d8a67cc Remove RearmBuildings from Aircraft and Minelayer
In favor of using Rearmable trait.
2018-11-02 22:28:08 +00:00
reaperrr
2485029452 Add Rearmable trait 2018-11-02 22:28:08 +00:00
reaperrr
139d5efba8 Remove RepairBuildings from Aircraft
Require them to use Repairable trait instead.
2018-11-02 22:28:08 +00:00
abcdefg30
ed7d12506d Add an update rule for CarryableHarvester 2018-11-02 22:24:56 +00:00
abcdefg30
db58b35856 Further untangle (and - hopefully - fix) the afterLandActivity mess 2018-11-02 22:24:56 +00:00
abcdefg30
7e20bdd7ea Introduce a new CarryableHarvester trait 2018-11-02 22:24:56 +00:00
Andre Mohren
89e3b62f61 Allow different color picker preview actors per faction. 2018-11-02 21:17:50 +00:00
Smittytron
8e00ddedc7 Move Moneycrate to campaign-rules.yaml 2018-11-01 19:06:51 +01:00
Paul Chote
95c5c683e3 Limit samplers to 8 in combined.frag.
The additional palette sampler wasn't accounted
for in the original PR.
2018-11-01 14:38:57 +01:00
reaperrr
a12bb54ded Add overlooked entries to bleed update path
Those were forgotten to be added in their respective PR.
Not adding them at the bottom because
  a) RenameCrateActionNotification was merged later than them
  b) this might allow some open PRs to rebase without merge conflicts
2018-11-01 09:33:09 +00:00
Paul Chote
c05527c561 Add ARM architecture support to dependency configuration. 2018-11-01 04:21:20 +01:00
Mustafa Alperen Seki
5a0c1459b2 Add MPSpawnUnits>BaseActorOffset 2018-11-01 02:16:59 +01:00
abcdefg30
d46e47d16d Fix target lines still being visible after an owner change 2018-11-01 02:09:36 +01:00
Andre Mohren
8b8a14e0b8 Proper usage of IHealthInfo. 2018-10-30 20:59:04 +00:00
Paul Chote
2ad8179672 Add PngSheetMetadata sprite metadata. 2018-10-28 20:55:40 +00:00
Paul Chote
6b7f1c6458 Use EmbeddedSpritePalette in D2k. 2018-10-28 20:55:40 +00:00
Paul Chote
eb61c45e14 Add EmbeddedSpritePalette sprite metadata. 2018-10-28 20:55:40 +00:00
Paul Chote
dee6d03626 Allow sprites to store custom metadata. 2018-10-28 20:55:40 +00:00
abcdefg30
0b89883012 Prevent ReturnToBase from causing a divide by zero crash 2018-10-28 18:54:32 +01:00
reaperrr
e05baf6ec7 Reduce RA helipad reload animation speed
Matching TD helipad.
2018-10-28 17:22:41 +00:00
Mustafa Alperen Seki
1a16ef3537 Add RenameCrateActionNotification to bleed update path 2018-10-28 11:21:49 +00:00
abcdefg30
9cbe7bc62d Fix infantry getting stuck in the last attack frame 2018-10-28 02:13:18 +01:00
Andre Mohren
cc036cfd62 Implement AnnounceOnBuildInfo.OnlyToOwner 2018-10-27 15:21:33 +02:00
Mustafa Alperen Seki
98006d3870 Make CrateAction>Sound to play for everyone 2018-10-26 22:37:00 +01:00
Mustafa Alperen Seki
2dcd377aaf Update main mods for CrateAction>Notification rename 2018-10-26 22:37:00 +01:00
Mustafa Alperen Seki
5f17f0b5b0 Add actual notification support for *CrateAction traits 2018-10-26 22:37:00 +01:00
Mustafa Alperen Seki
f066655bb7 Rename CrateAction>Notification to Sound 2018-10-26 22:37:00 +01:00
reaperrr
f18ce8cfda Make HitShape mandatory for damaging actors and refactor warheads.
* Adds support for linking Armor traits to HitShapes.
* Adds spread support to TargetDamageWarhead
* Removes ring-damage support from HealthPercentageDamage
* Removes IsValidAgainst check from DoImpact(Actor victim...) overload
  and instead lets warheads perform the check beforehand
  (to avoid HitShape look-ups on invalid targets).
* Reduces duplication and improves readability of Warhead implementations
2018-10-26 22:03:34 +02:00
reaperrr
e2050dfdc9 Fix FindActorsOnCircle summary
The summary was not entirely correct:
Since FAOC simply adds the largest existing OuterRadius to the specified circle range, it's still possible (very likely, in fact) for this helper to return actors whose HitShape is entirely outside the specified range (*without* the added largest OuterRadius).
2018-10-26 22:03:34 +02:00
Smittytron
b815e80e99 Reorganize RA missions list 2018-10-24 15:32:50 +01:00
Mustafa Alperen Seki
f3cbc5f72b Make CrateAction conditional. 2018-10-24 15:13:04 +01:00
Ectras
93977782a7 Updated Description for TerrainSpeeds
Added check for speed > 0
2018-10-18 18:45:51 +02:00
reaperrr
3e73357619 Remove IsIdle check from DrawLineToTarget.OnBecomingIdle
OnBecomingIdle is triggered when IsIdle becomes true, so this check is bogus.
2018-10-15 22:53:57 +02:00
reaperrr
422ba16ed9 Run TickIdle from inside Actor.Tick instead of World.Tick
Avoids calling ActorsWithTrait<INotifyIdle> every tick and allows caching all INotifyIdle traits at creation.
2018-10-15 22:53:57 +02:00
abcdefg30
cf4dc42c8f Fix lint warnings in RA missions 2018-10-15 19:16:27 +02:00
abcdefg30
a6716e2c2d Fix TS units not using the being-captured condition 2018-10-15 19:16:27 +02:00
abcdefg30
ed5bb1b1a1 Fix RA vehicles not using the being-captured condition 2018-10-15 19:16:27 +02:00
Paul Chote
a06cfb4004 Move TerrainRenderer to a mod-defined trait. 2018-10-13 18:16:56 +02:00
Inq8
fec9fe1ad4 Aircraft Takeoff & Landing Sounds (Fixed-Wing)
Added Takeoff & Landing sounds to planes.

Changed Aircraft Trait, TakeoffSounds & LandingSounds are now arrays & accept a list of sound files & it will randomly select one to play.

Changed/fixed take off & landing sounds to originate from the aircraft location, rather than play a global sound.
2018-10-12 14:29:53 +02:00
Smittytron
9cf8cba750 Touchup Allies08a errors 2018-10-10 01:01:06 +02:00
Mustafa Alperen Seki
9972d69c5d Change capture Production lock back to exit lock 2018-10-08 22:00:39 +01:00
Mustafa Alperen Seki
faa35946b8 Make Exit Conditional 2018-10-08 22:00:39 +01:00
Mustafa Alperen Seki
47c4be9191 Update mods for LowPowerSlowdown to LowPowerModifier 2018-10-08 21:38:30 +01:00
Mustafa Alperen Seki
9bcb222a2d Add Update rule for LPSlowdown to LPModifier 2018-10-08 21:38:30 +01:00
Mustafa Alperen Seki
5b00c12ca3 Change default value of LowPowerModifier to 100 2018-10-08 21:38:30 +01:00
Mustafa Alperen Seki
53304a0353 Change LowPowerSlowdown to LowPowerModifier 2018-10-08 21:38:30 +01:00
Smittytron
831ec0aeda Add Allies08a 2018-10-07 20:02:46 +02:00
Paul Chote
e038b86742 Hook up make animation conditions for the default mods. 2018-10-07 19:29:34 +02:00
Paul Chote
14607f55c5 Replace INotifyBuildComplete in render traits with conditions. 2018-10-07 19:29:34 +02:00
abcdefg30
26b0a06a17 Remove a wrong empty line from ra/rules/player.yaml 2018-10-07 18:28:37 +01:00
Andre Mohren
450dc70375 Refactored cursors.yaml to use palettes from rules. 2018-10-07 19:28:11 +02:00
Noam
c71f97e2c6 Update editor sidebar when a player is removed. 2018-10-07 19:01:55 +02:00
abcdefg30
52900f8112 Correct the amount of money found in crates in allies03 2018-10-07 17:59:34 +01:00
Paul Chote
1abfaa94af Remove unwanted overrides from allies-03*. 2018-10-07 18:46:21 +02:00
Paul Chote
769f9429f9 Stop vehicle movement when a hijacker is entering. 2018-10-07 18:46:21 +02:00
Paul Chote
de8fa56461 Remove obsolete building lock check from BaseProvider. 2018-10-07 18:46:21 +02:00
Paul Chote
22bd7fd90b Remove obsolete code. 2018-10-07 18:46:21 +02:00
Paul Chote
b1b35c1e1b Rework RA Engineer behaviour.
Capturing now behaves as in C&C3:KW - wait for 8 seconds
outside the structure before running inside and being disposed.

A "Reusable Engineer" lobby option is provided to restore
the previous non-disposing behaviour.
2018-10-07 18:46:21 +02:00
Paul Chote
ccad3bd185 Add MergeCaptureTraits update rule. 2018-10-07 18:46:21 +02:00
Paul Chote
a4405009c8 Add PreventsAutoTarget. 2018-10-07 18:46:21 +02:00
Paul Chote
132268bc49 Remove default building capture type. 2018-10-07 18:46:21 +02:00
Paul Chote
e50b0b193d Disable sabotaging by default. 2018-10-07 18:46:21 +02:00
Paul Chote
db1f794beb Fix captor being disposed if the capture fails. 2018-10-07 18:46:21 +02:00
Paul Chote
d97a2a5fa0 Remove legacy building lock calls.
This has been superseded by conditions.
2018-10-07 18:46:21 +02:00
Paul Chote
50423b13fb Add ConsumedByCapture flag to emulate legacy external behaviour. 2018-10-07 18:46:21 +02:00
Paul Chote
4d2f1f8942 Add capture progress bars and blinking. 2018-10-07 18:46:21 +02:00
Paul Chote
bab34252dd Add support for capture delays and conditions.
CaptureDelay is defined per-trait, and the shortest
delay will be used if multiple traits are enabled.

CapturingCondition and BeingCapturedCondition are
global, and are granted when any capture is in progress.

The capture period is defined from when the unit reaches
the cell next to the target, and either starts to wait
or enter the target through to when the capture succeeds
or fails.
2018-10-07 18:46:21 +02:00
Paul Chote
a53ef6e503 Add CaptureManager trait to fix multiple-trait interactions.
This fixes the various edge cases that occur when multiple
Captures or Capturable traits are defined on an actor and
are toggled using conditions.

The Sabotage threshold field moves from Capturable to
Captures in order to simplify the plumbing. The previous
behaviour ingame can be restored by creating a new
capturable type for each threshold level, each with their
own Captures trait.
2018-10-07 18:46:21 +02:00
Paul Chote
588a5d784f Add IMove.EstimatedMoveDuration. 2018-10-07 18:46:21 +02:00
Paul Chote
3b16938ae5 Remove legacy building.Locked and building.BuildComplete from Gate. 2018-10-07 18:23:03 +02:00
BGluth
b88b84c05a Units that have turrets while deployed now move their turrets back to their initial positions before undeploying
- Tested in TS with all deployable units and did a quick check for obvious issues in TD and RA.
2018-10-07 12:19:09 +01:00
abcdefg30
00dc161628 Add a proper exception when creating an actor with an invalid owner in Lua 2018-10-07 11:09:06 +00:00
Brenton Horne
38e8bed9c9 [INSTALL.md] Pkg lists->command; add extra distros 2018-10-06 23:48:36 +01:00
Paul Chote
eb0e2eeb9d Fix misc indentation errors. 2018-10-06 23:32:38 +02:00
Mustafa Alperen Seki
416f529cb0 Make GPS dots use a bit darker remap colors. 2018-10-06 17:25:43 +01:00
SoScared
143951a409 polish sniper shp 2018-10-06 17:20:22 +01:00
Paul Chote
cc9da63323 Fix sampler index check
The index values are round numbers. Checking agaist
the half-values improves robustness against small
floating point delta errors that occur on some GPUs.
2018-10-06 15:10:43 +01:00
abcdefg30
01d340db09 Properly end capturing during an owner change 2018-10-06 14:59:33 +01:00
Mustafa Alperen Seki
f60b2275fc Make WithBuildingPlacedAnimation not play during vortex 2018-10-06 13:31:47 +01:00
Mustafa Alperen Seki
536f5f05a8 Make WithBuildingPlacedAnimation conditional 2018-10-06 13:31:47 +01:00
reaperrr
dd3c600258 Fix WithLandingCraftAnimation compatibility with elevated terrain 2018-10-06 13:11:10 +01:00
Smittytron
a7de079a53 Remove redundant demolishable overrides from evacuation 2018-10-02 10:44:06 +02:00
teinarss
cfaf5a6467 Updated CPos struct to use a bit field for all properties. 2018-10-02 00:54:45 +01:00
abcdefg30
9f82ef999f Set AutoGenerateBindingRedirects to true in OpenRA.Game.csproj 2018-10-01 17:52:52 +01:00
teinarss
e353c8c176 Changed SubCell to byte 2018-09-30 19:48:27 +01:00
Andre Mohren
52a7d39e51 Implemented Parallel ProductionQueue style. 2018-09-30 16:58:49 +02:00
Andre Mohren
3bfcecd539 Refactored ProductionQueue to support different production styles. 2018-09-30 16:58:49 +02:00
Andre Mohren
6cd1919cca Calling PreparingAttack before calculating Muzzle. 2018-09-30 15:34:08 +02:00
Mustafa Alperen Seki
abdb1f7547 Add TargetTypes to AttackSuicides 2018-09-30 14:34:14 +02:00
reaperrr
adc03c41b1 Cache DomainIndex in PathFinder
Should save a trait look-up for each path search. Also ditch bogus null checks (this trait is a must-have anyway, so we *want* to crash if it's missing).
2018-09-30 14:34:05 +02:00
SoScared
893e20ee76 Fix yaml error with notifications.yaml 2018-09-30 06:52:41 +00:00
Andre Mohren
693b5a54af PNG spritesheet support, along with PaletteFromPng.
Cursor palette loader can now be specified via yaml.
2018-09-29 21:12:40 +02:00
Andre Mohren
48248266a8 ClickSound and ClickDisabledSound and ChatLine are optional ui sounds. 2018-09-29 20:05:53 +02:00
Andre Mohren
28623ce54c Allow usage of different palette for WithSpriteTurret 2018-09-29 19:47:47 +02:00
Andre Mohren
640078a2b1 Refactored Health usage to IHealth. 2018-09-29 18:12:40 +02:00
reaperrr
83cd7cf485 Merge two ifs in RevealOnDeath 2018-09-29 12:54:34 +01:00
reaperrr
ade85f8977 Skip DamageWarhead armor lookups if no Versus defined
- directly return 100 when no Versus values are defined (meaning the warhead would have 100% efficiency vs. all armor types anyway)
2018-09-29 12:54:34 +01:00
reaperrr
1f7edf9f0b Cache *Notify traits in Health where applicable
During heated battles, those TraitsImplementing look-ups in Health might cause bursty CPU load on warhead impacts. Caching the notify traits of the actor + owner can reduce the trait look-ups per impact by more than half.
2018-09-29 12:54:34 +01:00
reaperrr
13769d48a1 Minor Health readability style fix 2018-09-29 12:54:34 +01:00
reaperrr
43693d84d1 Migrate DamageState thresholds to integer
While avoiding divisions.

While there haven't been any desyncs to speak of recently (not in this part of the code, in any case), this still looks like an oversight from when we migrated away from using floats.
This also makes it easier to expose the thresholds to modders later.
2018-09-29 12:54:34 +01:00
reaperrr
0b0c3b7170 Changed an 'if' to 'else' in Health.Tick
Since the two 'if's were mutually exclusive, the 2nd 'if' was turned into an 'else' to potentially skip that check if the 1st succeeds.
2018-09-29 12:54:34 +01:00
Mustafa Alperen Seki
b53c13dca4 Add GrantConditionOnProduction 2018-09-29 12:10:11 +01:00
Smittytron
2bb2cd51cc Change menu cancel buttons to back 2018-09-28 23:33:17 +01:00
abcdefg30
4298584af2 Prevent units from gaining more experience than MaxLevel requires 2018-09-28 22:48:50 +01:00
Andre Mohren
a86f41cd5c Made Valued optional for traits who do not require it. 2018-09-28 22:06:56 +01:00
reaperrr
ec15acbc80 Enable LaserZap launch-effect syncing with current source barrel/muzzle facing 2018-09-28 21:32:34 +02:00
reaperrr
28c89920ac Add dynamic muzzle LaunchEffect facing plumbing 2018-09-28 21:32:34 +02:00
Mike
619457828f Soviet Soldier Volkov & Chitzkoi fully ported
Crash fixed.
2018-09-28 21:12:15 +02:00
reaperrr
8144fca5be Merge repair and rearm anim traits into WithResupplyAnimation
This is the safest approach to avoid conflicts/visual glitches when the host is responsible for both resupply types.
The new trait will simply play a looping animation as long as the actor is resupplying in any form.
2018-09-27 16:38:08 +02:00
Smittytron
9fb8f6c6f8 Add Allies07 2018-09-27 16:04:17 +02:00
reaperrr
288dfdbf03 Update UpdatePaths and move bleed rules to subfolder 2018-09-26 23:43:30 +01:00
Paul Chote
09d8aafddf Add a lint test for audio notifications.
Only traits are linted - the UI still hardcodes
too many audio references for this to be worthwhile.
2018-09-26 13:57:05 +02:00
Smittytron
096de8f5aa Remove out of bound actors from RA missions 2018-09-25 18:38:08 +02:00
Smittytron
f668e18f37 Remove out of bounds actors from TD missions 2018-09-25 18:38:08 +02:00
Andre Mohren
f342ecf18a Added UpdateRule. 2018-09-24 22:43:14 +02:00
Andre Mohren
a68ea0fe2d RepairsUnits notification now optional. 2018-09-24 22:43:14 +02:00
Andre Mohren
091f660dc7 Extracted Repairing notification to RepairableBuilding. 2018-09-24 22:43:14 +02:00
Andre Mohren
a0ad79e555 Extracted RadarUp and RadarDown notifications to RadarWidget. 2018-09-24 22:43:14 +02:00
Andre Mohren
d7f81d4a20 Extracted Win and Lose and Leave notifications MissionObjectives. 2018-09-24 22:43:14 +02:00
Andre Mohren
fac758f38e ProductionQueue notifications now optional. 2018-09-24 22:43:14 +02:00
Andre Mohren
c2b1a5f4e0 Extracted BuildingCannotPlaceAudio to PlaceBuilding. 2018-09-24 22:43:14 +02:00
Andre Mohren
8834cee13c PlaceBuilding notification now optional. 2018-09-24 22:43:14 +02:00
Andre Mohren
7057e32902 PowerManager notification now optional. 2018-09-24 22:43:14 +02:00
Andre Mohren
90c2249317 PrimaryBuilding notification now optional. 2018-09-24 22:43:14 +02:00
Andre Mohren
cf84e6f8d5 GainsExperience notification now optional. 2018-09-24 22:43:14 +02:00
Andre Mohren
e353ff326e Extracted CashTickUp and CashTickDown to PlayerResources. 2018-09-24 22:43:14 +02:00
reaperrr
bb7c19ad02 Fix spawning 1 crate too much if maximum == minimum
If Maximum == Minimum but 'crates' < Maximum, the formula would simply return min + 1 regardless of max being identical to min.
Only adding 1 more crate if Maximum is higher than Minimum fixes that.
2018-09-24 22:06:21 +02:00
reaperrr
1098804f9b Make WithSpriteBody always use Animation.PlayRepeating
... for looped sequences.

PlayCustomAnimationRepeating looping back into itself via Action instead of simply using Animation.PlayRepeating is weird, and in fact causes a slight 'desync' in animation speed with Animation.PlayRepeating in at least one downstream mod.
2018-09-24 22:06:10 +02:00
reaperrr
9bcb754836 Add OnActorDispose plumbing to Activity
This allows activities to perform necessary cleanups on actor
death/disposal, for example by running OnLastRun directly,
which would otherwise be skipped when the actor dies or is disposed
through other means.
2018-09-24 22:01:22 +02:00
Andre Mohren
e8eb4d9563 Credit removal due to request. 2018-09-24 13:11:15 +02:00
Paul Chote
7d59602c0b Revert "Update macOS launcher template to osx-launcher-20180723."
This reverts commit 904c3afc4d.
2018-09-22 18:57:21 +02:00
abcdefg30
978d447d42 Disable the restart button on dedicated servers 2018-09-22 15:44:56 +01:00
abcdefg30
92e8fbf4d0 Make the menu widget readonly 2018-09-22 15:44:56 +01:00
Ectras
d9946f63e4 Renamed EditorTilesetFilter to MapEditorData and added an update rule 2018-09-22 15:12:15 +02:00
Glen
2e3f2e079a Github Username Change 2018-09-20 11:23:06 +02:00
Matthias Mailänder
cb05d8a98d Fix CustomTerrainDebugOverlay not rendered in isometric mods 2018-09-19 20:04:41 +02:00
Noam
4e7a35b50f add NoAvailableMaps exception.
modify ChooseInitialMap to throw NoAvailalbeMaps exception if no maps were loaded.
implement Utilities.TryWithPrompt - safe execution of a provided action with ability to prompt user on error.
2018-09-19 12:52:51 +02:00
Matthias Mailänder
399e451ada Add a DamagesConcreteWarhead to remove buildable concrete. 2018-09-15 15:36:12 +02:00
Paul Chote
7438af8266 Improve status line display for unknown-size downloads. 2018-09-14 18:55:58 +02:00
Emmalyn Renato
3f4d5fa68c Fix display of progress text in Advanced Install when total file size is unknown. 2018-09-14 17:30:53 +01:00
Paul Chote
91adc61abd Revert RA Grenadier explosion DamageSource. 2018-09-13 17:11:15 +02:00
SoScared
c242333922 Fix d2k bots not excluding harvesters from squads 2018-09-13 17:01:23 +02:00
SoScared
953d76a20a Enable nuke for d2k omnius ai 2018-09-13 17:01:23 +02:00
SoScared
3f9fde8855 Tweak AI excess power and unit production 2018-09-13 17:01:23 +02:00
Dennis Snell
95558a36ab Fix: Display progress message properly when download size missing (#1)
When I downloaded the assets for Red Alert through the Quick Install I noticed the progress bar proceed and display a recognizable message: `Downloading from … 1.47/12 MB (12%)`. This was fine.

When I downloaded the assets for one of the other games, maybe Dune 2000, there was obviously no total download size available. I was an unexpected message: `Downloading from … 1.47/NaN (NaN%)`

The code handling network progress events seems to be aware of the possibility that no full download size exists but it doesn't update the message. In this path I'm proposing that we display a separate messaging indicating that we don't know how much more we have to download for these cases.

Of the alternative ways to implement this I chose to move the reassignment of `getStatusText` into the conditional structures to preserve the existing choice. The message was qualitatively different and so I felt it worthwhile to create entirely different closures vs. doing something like this…

```cs
getStatusText = () => ( Double.isNaN( dataTotal ) ? "Downloading {1} of unknown amount" : "Downloading {1}/{2}" ).F( … );
```
2018-09-13 16:55:51 +02:00
Andre Mohren
ef7c49c116 Allow mods to determine associated SuperPower from SelectGenericPowerTarget 2018-09-12 15:11:18 +02:00
Paul Chote
16ff9fbc8e Fix menu/exit fade effect for paused replays. 2018-09-11 23:29:23 +02:00
reaperrr
bbf6e38faa Always leave trails when no TerrainTypes are defined
Requiring terrain types is bogus, as it unnecessarily complicates using this trait for airborne units.
2018-09-10 19:45:02 +02:00
Paul Chote
8533aa8d26 Disable sync reports when we know we won't need them.
Generating the sync report takes ~twice as long as
a normal tick, and occurs once every 3 ticks.

These reports record of all of the synced state
(separate to the sync hash, which is still calculated)
in order to generate the syncreport.log of the game
desyncs. This perf overhead is completely unnecessary
when we know that we won't have other syncreports to
compare against (singleplayer, replays).

Disabling report generation in these cases gives
us an easy 40% average tick-time win.
2018-09-10 19:44:06 +02:00
Paul Chote
0a507f3d33 Allow deploy orders to be queued from the command bar. 2018-09-10 19:42:24 +02:00
Paul Chote
3abc85b588 Add a Desc for Buildable.BuildPaletteOrder.
Also removes the long-outdated UI-fluff comment.
2018-09-10 11:27:50 +02:00
SoScared
024722a96c Enable airfield for AI as Ukraine 2018-09-10 03:45:59 +01:00
abcdefg30
5d1c37a4c5 Remove the CodeAnalysisRuleSet property from all csproj files 2018-08-25 22:02:59 +02:00
Paul Chote
48f2519811 Make SetupLatencyWidget consistent with SetupProfileWidget. 2018-08-25 21:56:10 +02:00
Paul Chote
0c098e74f1 Don't crash when mousing over a bot as a non-admin. 2018-08-25 21:56:10 +02:00
Paul Chote
8475bd6294 Ignore malformed orders instead of crashing. 2018-08-25 18:23:35 +02:00
rtri
cdfa52d918 fix threadedRenderer context creation logic 2018-08-23 01:50:18 +02:00
Chris Forbes
67d9ad6a93 Convert cloak types to use BitSet
Fixes #15412
2018-08-20 01:11:43 +02:00
Chris Forbes
1f71377d82 Convert cloneabletypes to bitsets
Fixes #15411
2018-08-20 01:11:43 +02:00
Paul Chote
8634c001f9 Add requires-auth indicator to the server lists. 2018-08-18 16:57:28 +02:00
Paul Chote
e374c8e6c3 Sync auth information with the master server. 2018-08-18 16:57:28 +02:00
Paul Chote
77bb39304b Give server operators more control over client validation. 2018-08-18 16:57:28 +02:00
Paul Chote
9ec22e48a6 Revert "Re-active Edge-Scrolling for inverted Mouse-Scrolling"
This reverts commit c4867d4030.
2018-08-18 16:51:51 +02:00
Paul Chote
03adceb656 Rework master server ping rate-limit logic.
Pings are now delayed instead of dropped to avoid data loss.
2018-08-18 09:31:11 +01:00
Paul Chote
efccd610d3 Simplify server tick timeout handling. 2018-08-18 09:31:11 +01:00
Paul Chote
d37119655b Add Engine.SupportDir argument. 2018-08-17 21:02:36 +02:00
Andre Mohren
8c5caaf154 Fixed unnecessary crash if RallyPoint palette is not used. 2018-08-16 22:24:58 +02:00
Paul Chote
4a5525d1af Ensure that TLS 1.2 is enabled for web downloads. 2018-08-16 19:56:18 +02:00
Mustafa Alperen Seki
6df243f79b Fix GivesExperienceModifier not working 2018-08-16 19:44:08 +02:00
Paul Chote
e56cb9901b Add a hard requirement on the cert-sync utility.
This tool is required to sync the certificates
used for https queries. This also has a side-effect
of prompting the mono-complete package on Mint,
which pulls in other required but missing packages.
2018-08-15 21:21:56 +01:00
Paul Chote
20dbf76e81 Fix the Chronoshift-return cancellation bug. 2018-08-15 21:17:27 +01:00
Paul Chote
4375dc2fc1 Kill chronoshifted actors if the return-to-origin fails. 2018-08-15 21:17:27 +01:00
Paul Chote
6f864b055d Remove IPreventsTeleport interface.
MadTank is changed to use conditions instead.
This has a side-benefit of disabling the move
cursor while deployed.
2018-08-15 21:17:27 +01:00
Paul Chote
e6d552eee7 Make Chronoshiftable conditional. 2018-08-15 21:17:27 +01:00
Paul Chote
828106cf82 Fix a typo in ChronosphereProperties class name. 2018-08-15 21:17:27 +01:00
Mustafa Alperen Seki
1977e64e07 Fix Cargo>EjectOnDeath crash with multipile INotifyBlockingMove 2018-08-14 23:00:08 +02:00
reaperrr
c983dda077 Fix husks not updating targetable positions on teleport
This should have checked for IPositionableInfo to begin with.

Husk already implements IPositionable, so implementing *Info as well
makes sense, even if it only serves to exclude it from
ITargetablePositions caching for now.
2018-08-14 17:26:36 +01:00
BGluth
09b9ed3506 Rearming aircraft now reapplies rearming order if canceled on landing structure
- Implemented by making the ResupplyAircraft activity recreate a new resupply activity if cancelled and also having no other queued activities.
- Tested in TD, RA, TS.
2018-08-12 21:16:23 +02:00
Paul Chote
1bfe70e923 Revert "Add "Restart" button for multiplayer replays"
This reverts commit 3a377a572c.
2018-08-12 18:57:12 +02:00
reaperrr
ba231636f5 Minor RepairsUnits style fix 2018-08-12 14:36:59 +01:00
reaperrr
46cee82027 Move RepairsUnits out of Building folder 2018-08-12 14:36:59 +01:00
reaperrr
77d03ce1e1 Replace Airfield/Helipad references with generic Resupplier in RTB activities
More generalization to prepare for possible future activity merger.
2018-08-12 14:24:21 +02:00
reaperrr
fc79e04c49 Generalize Land activities Aircraft caching naming
To make a possible future merger (or inheritance or other code-sharing) of these activities easier.
2018-08-12 14:24:21 +02:00
reaperrr
3f9aab7e86 Generalize Fly* plane activities Aircraft caching naming
To make a possible future merger (or inheritance or other code-sharing) with other activities easier.
2018-08-12 14:24:21 +02:00
reaperrr
6810ac92ba Generalize HeliAttack activity Aircraft caching naming
To make a possible future merger (or inheritance or other code-sharing) with FlyAttack easier.
2018-08-12 14:24:21 +02:00
reaperrr
c3a0d129a3 Generalize FlyCircle activity Aircraft caching naming
To make a possible future merger (or inheritance or other code-sharing) of these activities easier.
2018-08-12 14:24:21 +02:00
reaperrr
ae92255ded Generalize Fly activity Aircraft caching naming
To make a possible future merger (or inheritance or other code-sharing) of these activities easier.
2018-08-12 14:24:21 +02:00
reaperrr
96032d1953 Generalize *ReturnToBase trait caching naming
Makes both copying changes as well as a potential future activity merger a little easier.
2018-08-12 14:24:21 +02:00
Smittytron
bd569c9ae2 Revert ranger change and dial back missile sub damage 2018-08-11 23:04:41 +02:00
Paul Chote
3711a695c5 Fix badge label padding. 2018-08-11 23:02:41 +02:00
abcdefg30
94c47fdac0 Remove TSLauncher.exe from the IDFiles of the origin ts install 2018-08-11 22:58:28 +02:00
abcdefg30
60a492803d Remove RA95Launcher.exe from the IDFiles of the origin ra install 2018-08-11 22:58:28 +02:00
abcdefg30
4473059c60 Remove CNC95Launcher.exe from the IDFiles of the origin cnc and ra installs 2018-08-11 22:58:28 +02:00
Paul Chote
1b1521812c Add EnabledByDefault check to WithInfantryBody. 2018-08-11 14:30:01 +02:00
Paul Chote
d110a21bfe Add missing EnabledByDefault checks to WithSpriteBodyInfo subclasses. 2018-08-11 14:30:01 +02:00
abcdefg30
0b53c1f139 Resolve conflicts between actor and API names (Radar) 2018-08-10 20:49:06 +02:00
Oliver Brakmann
1927b88a18 Fix issueing superfluous difficulty lobby command from mission browser 2018-08-09 20:07:06 +01:00
Smittytron
e57f1dcd8c Fix death sequences of Chan, Einstein, and Delphi 2018-08-05 12:57:11 +02:00
reaperrr
6c401f0f9a Fix AI idle harvester management
This was broken because our default mods now list `harv` under `ExcludeFromSquads`, which prevents them from being added to `activeUnits`.
2018-08-04 22:30:07 +02:00
reaperrr
35600d9291 Some HackyAI cleanups
- harvManager.Tick should run after FindNewUnits() in case new harvesters have appeared
- moved the FindNewUnits Mcv and ExcludeFromSquads checks to the foreach loop, for better readability and preparation of the idle harvester fix
2018-08-04 22:30:07 +02:00
Paul Chote
a51b916eaa Hide the Ready checkbox when a spectator transfers away Admin. 2018-08-04 22:12:43 +02:00
BGluth
5c42f55b3a Fixed aircraft in TS landing instantly to reload their ammo
- Updated calculating the landing altitude for the Land activity not taking into account the terrain height.
- Fixes 14312.
2018-08-04 21:09:59 +02:00
Andre Mohren
3f81df9c52 Fixed backwards animation playback. 2018-08-04 20:58:02 +02:00
Paul Chote
a0dcd9e106 Clear selection when a text field's contents is changed programatically. 2018-08-04 20:29:19 +02:00
Paul Chote
d6737ccfab Fix missing profile indicator for spectators. 2018-08-04 20:22:35 +02:00
Paul Chote
3661dbdfd0 Disable the threaded renderer on Windows.
A DisableWindowsRenderThread graphics setting is
added to allow players to optionally reenable it.
2018-08-04 20:10:52 +02:00
reaperrr
599e87c4aa Fix tree owners in TD (for real this time) 2018-08-04 19:06:32 +01:00
Paul Chote
567ab47765 Revert ShpTD empty space trimming.
This reverts the following commits:
 * 1faae73c08
 * a7d39fc76d
2018-08-04 19:56:24 +02:00
Unknown
c97f36793c Implemented horizontal allign support for SupportPowersWidget 2018-08-04 17:39:18 +02:00
reaperrr
e179a9529e Fix tree owner on TD maps
Needs to be Neutral instead of Creeps, to avoid confusing the AI.
2018-08-04 14:03:02 +01:00
reaperrr
606bf6d9bb Fix tree owner on several RA maps
Owner: Creeps can lead to AI trying to attack trees, so all trees must have Neutral as owner.
2018-08-04 14:03:02 +01:00
abcdefg30
e8068cf329 Remove all uses of Server.ExternalPort 2018-08-04 12:32:01 +02:00
Andre Mohren
8f15535da0 Allow to use non tileset specific build-valid. 2018-08-04 12:28:41 +02:00
Mustafa Alperen Seki
d062523700 Add GetActorsByTypes. 2018-08-04 12:20:16 +02:00
Smittytron
6e18caa096 Remove duplicate line from V19.Husk 2018-08-04 12:18:01 +02:00
Zimmermann Gyula
6027a123d4 Split off the upgrade rules of the previous release cycle. 2018-08-02 17:00:02 +02:00
abcdefg30
f9bc1113b5 Fix the die1 sequence of fremen 2018-07-31 21:00:59 +02:00
1047 changed files with 30367 additions and 9808 deletions

View File

@@ -54,6 +54,7 @@ Also thanks to:
* David Russell (DavidARussell)
* DeadlySurprise
* Dmitri Suvorov (suvjunmd)
* dtluna
* Erasmus Schroder (rasco)
* Eric Bajumpaa (SteelPhase)
* Evgeniy Sergeev (evgeniysergeev)
@@ -61,11 +62,10 @@ Also thanks to:
* Florian Wiesbauer (FiveAces)
* Frank Razenberg (zzattack)
* Gareth Needham (Ripley`)
* Glen Anderson (GlenLife)
* Glen Anderson (glen7)
* Glenn Martin Jensen (Baxxster)
* Gordon Martin (Happy0)
* Guido Lipke (LipkeGu)
* Gyula Zimmermann (Graion Dilach)
* Hervé Matysiak (Herve-M)
* Huw Pascoe
* Ian T. Jacobsen (Smilex)
@@ -89,10 +89,12 @@ Also thanks to:
* Kenny Hoxworth (hoxworth)
* Kevin Azzam (ChaoticMind)
* Krishnakanth Mallik
* Kyle Smith (Smitty)
* Kyrre Soerensen (zypres)
* Lawrence Wang
* Lesueur Benjamin (Valkirie)
* Maarten Meuris (Nyerguds)
* Manuel Geiger (Ectras)
* Mark Olson (markolson)
* Markus Hartung (hartmark)
* Matija Hustic (matija-hustic)
@@ -104,9 +106,11 @@ Also thanks to:
* Michael Rätzel
* Michael Silber (frühstück)
* Michael Sztolcman (s1w_)
* Mike Gagné (AngryBirdz)
* Muh
* Mustafa Alperen Seki (MustaphaTR)
* Neil Shivkar (havok13888)
* Nikolay Fomin (netnazgul)
* Nooze
* Nukem
* Okunev Yu Dmitry (xaionaro)

View File

@@ -18,52 +18,69 @@ You need to fetch the thirdparty dependencies and place them at the appropriate
To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with MSBuild or use the Makefile analogue command `make all` scripted in PowerShell syntax.
Run the game with `OpenRA.Game.exe Game.Mod=ra` for Red Alert or `OpenRA.Game.exe Game.Mod=cnc` for Tiberian Dawn.
Run the game with `launch-game.cmd`. It can be handed arguments that specify the exact mod one wishes to run, for example, run `launch-game.cmd Game.Mod=ra` to launch Red Alert, `launch-game.cmd Game.Mod=cnc` to start Tiberian dawn or `launch-game.cmd Game.Mod=d2k` to launch Dune 2000.
Linux
=====
Use `make dependencies` to map the native libraries to your system and fetch the remaining CLI dependencies to place them at the appropriate places.
To compile OpenRA, run `make all` from the command line.
To compile OpenRA, run `make all` from the command line. After this one can run the game with `./launch-game.sh`. It is also possible to specify the mod you wish to run from the command line, e.g. with `./launch-game.sh Game.Mod=ts` if you wish to try the experimental Tiberian Sun mod.
Run with either `launch-game.sh` or `mono --debug OpenRA.Game.exe`.
Type `sudo make install` for system-wide installation. Run `sudo make install-linux-shortcuts` to get startup scripts, icons and desktop files. You can then run the Red Alert by executing the `openra-ra` command, the Dune 2000 mod by running the `openra-d2k` command and Tiberian Dawn by the `openra-cnc` command. Alternatively, you can also run these mods by clicking on their desktop shortcuts if you ran `sudo make install-linux-shortcuts`.
Type `sudo make install` for system wide installation. Run `make install-linux-shortcuts` to get startup scripts, icons and desktop files. You can then run from the `openra` shortcut.
Arch Linux
----------
It is important to note there is an unofficial [`openra-git`](https://aur.archlinux.org/packages/openra-git) package in the Arch User Repository (AUR) of Arch Linux. If manually compiling is the way you wish to go the build and runtime dependencies can be installed with:
```
sudo pacman -S mono openal libgl freetype2 sdl2 lua51 xdg-utils zenity
```
Debian/Ubuntu
-------------
* mono-devel
* libfreetype6
* libopenal1
* liblua5.1-0
* libsdl2-2.0-0
* xdg-utils
* zenity
* curl
```
sudo apt install mono-devel libfreetype6 libopenal1 liblua5.1-0 libsdl2-2.0-0 xdg-utils zenity wget
```
openSUSE
--------
Fedora
------
```
sudo zypper in mono-devel openal-soft freetype2 SDL2 lua51 xdg-utils zenity curl
sudo dnf install "pkgconfig(mono)" SDL2 freetype "lua = 5.1" openal-soft xdg-utils zenity
```
Gentoo
------
* dev-lang/mono
* dev-dotnet/libgdiplus
* media-libs/freetype:2
* media-libs/libsdl2
* media-libs/openal
* virtual/jpeg
* virtual/opengl
* dev-lang/lua-5.1.5
* x11-misc/xdg-utils
* gnome-extra/zenity
* net-misc/curl
```
sudo emerge -av dev-lang/mono dev-dotnet/libgdiplus media-libs/freetype:2 media-libs/libsdl2 media-libs/openal virtual/jpeg virtual/opengl '=dev-lang/lua-5.1.5*' x11-misc/xdg-utils gnome-extra/zenity
```
Mageia
------
```
sudo dnf install "pkgconfig(mono)" SDL2 freetype "lib*lua5.1" "lib*freetype2" "lib*sdl2.0_0" openal-soft xdg-utils zenity
```
openSUSE
--------
```
sudo zypper in mono-devel openal-soft freetype2 SDL2 lua51 xdg-utils zenity
```
Red Hat Enterprise Linux (and rebuilds, e.g. CentOS)
----------------------------------------------------
The EPEL repository is required in order for the following command to run properly.
```
sudo yum install "pkgconfig(mono)" SDL2 freetype "lua = 5.1" openal-soft xdg-utils zenity
```
OSX
=====
@@ -72,4 +89,4 @@ Use `make dependencies` to map the native libraries to your system.
To compile OpenRA, run `make` from the command line.
Run with `mono --debug OpenRA.Game.exe`.
Run with `./launch-game.sh`.

View File

@@ -200,6 +200,24 @@ namespace OpenRA.Activities
/// </summary>
protected virtual void OnLastRun(Actor self) { }
/// <summary>
/// Runs once on Actor.Dispose() (through OnActorDisposeOuter) and can be used to perform activity clean-up on actor death/disposal,
/// for example by force-triggering OnLastRun (which would otherwise be skipped).
/// </summary>
protected virtual void OnActorDispose(Actor self) { }
/// <summary>
/// Runs once on Actor.Dispose().
/// Main purpose is to ensure ChildActivity.OnActorDispose runs as well (which isn't otherwise accessible due to protection level).
/// </summary>
internal void OnActorDisposeOuter(Actor self)
{
if (ChildActivity != null)
ChildActivity.OnActorDisposeOuter(self);
OnActorDispose(self);
}
public virtual bool Cancel(Actor self, bool keepQueue = false)
{
if (!IsInterruptible)

View File

@@ -42,11 +42,13 @@ namespace OpenRA
public Player Owner { get; internal set; }
public bool IsInWorld { get; internal set; }
public bool WillDispose { get; private set; }
public bool Disposed { get; private set; }
public Activity CurrentActivity { get; private set; }
public int Generation;
public Actor ReplacedByActor;
public IEffectiveOwner EffectiveOwner { get; private set; }
public IOccupySpace OccupiesSpace { get; private set; }
@@ -77,6 +79,8 @@ namespace OpenRA
readonly IMouseBounds[] mouseBounds;
readonly IVisibilityModifier[] visibilityModifiers;
readonly IDefaultVisibility defaultVisibility;
readonly INotifyBecomingIdle[] becomingIdles;
readonly INotifyIdle[] tickIdles;
readonly ITargetablePositions[] targetablePositions;
WPos[] staticTargetablePositions;
@@ -119,14 +123,16 @@ namespace OpenRA
mouseBounds = TraitsImplementing<IMouseBounds>().ToArray();
visibilityModifiers = TraitsImplementing<IVisibilityModifier>().ToArray();
defaultVisibility = Trait<IDefaultVisibility>();
becomingIdles = TraitsImplementing<INotifyBecomingIdle>().ToArray();
tickIdles = TraitsImplementing<INotifyIdle>().ToArray();
Targetables = TraitsImplementing<ITargetable>().ToArray();
targetablePositions = TraitsImplementing<ITargetablePositions>().ToArray();
world.AddFrameEndTask(w =>
{
// Caching this in a AddFrameEndTask, because trait construction order might cause problems if done directly at creation time.
// All actors that can move should have IMove, if not it's pretty safe to assume the actor is immobile and
// All actors that can move or teleport should have IPositionable, if not it's pretty safe to assume the actor is completely immobile and
// all targetable positions can be cached if all ITargetablePositions have no conditional requirements.
if (!Info.HasTraitInfo<IMoveInfo>() && targetablePositions.Any() && targetablePositions.All(tp => tp.AlwaysEnabled))
if (!Info.HasTraitInfo<IPositionableInfo>() && targetablePositions.Any() && targetablePositions.All(tp => tp.AlwaysEnabled))
staticTargetablePositions = targetablePositions.SelectMany(tp => tp.TargetablePositions(this)).ToArray();
});
@@ -139,8 +145,18 @@ namespace OpenRA
CurrentActivity = ActivityUtils.RunActivity(this, CurrentActivity);
if (!wasIdle && IsIdle)
foreach (var n in TraitsImplementing<INotifyBecomingIdle>())
{
foreach (var n in becomingIdles)
n.OnBecomingIdle(this);
// If IsIdle is true, it means the last CurrentActivity.Tick returned null.
// If a next activity has been queued via OnBecomingIdle, we need to start running it now,
// to avoid an 'empty' null tick where the actor will (visibly, if moving) do nothing.
CurrentActivity = ActivityUtils.RunActivity(this, CurrentActivity);
}
else if (wasIdle)
foreach (var tickIdle in tickIdles)
tickIdle.TickIdle(this);
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
@@ -265,6 +281,14 @@ namespace OpenRA
public void Dispose()
{
// If CurrentActivity isn't null, run OnActorDisposeOuter in case some cleanups are needed.
// This should be done before the FrameEndTask to avoid dependency issues.
if (CurrentActivity != null)
CurrentActivity.RootActivity.OnActorDisposeOuter(this);
// Allow traits/activities to prevent a race condition when they depend on disposing the actor (e.g. Transforms)
WillDispose = true;
World.AddFrameEndTask(w =>
{
if (Disposed)
@@ -312,6 +336,8 @@ namespace OpenRA
foreach (var t in TraitsImplementing<INotifyOwnerChanged>())
t.OnOwnerChanged(this, oldOwner, newOwner);
World.Selection.OnOwnerChanged(this, oldOwner, newOwner);
if (wasInWorld)
World.Add(this);
}

View File

@@ -18,11 +18,28 @@ namespace OpenRA
{
public struct CPos : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaEqualityBinding, ILuaTableBinding, IEquatable<CPos>
{
public readonly int X, Y;
public readonly byte Layer;
// Coordinates are packed in a 32 bit signed int
// X and Y are 12 bits (signed): -2048...2047
// Layer is an unsigned byte
// Packing is XXXX XXXX XXXX YYYY YYYY YYYY LLLL LLLL
public readonly int Bits;
// X is padded to MSB, so bit shift does the correct sign extension
public int X { get { return Bits >> 20; } }
// Align Y with a short, cast, then shift the rest of the way
// The signed short bit shift does the correct sign extension
public int Y { get { return ((short)(Bits >> 4)) >> 4; } }
public byte Layer { get { return (byte)Bits; } }
public CPos(int bits) { Bits = bits; }
public CPos(int x, int y) : this(x, y, 0) { }
public CPos(int x, int y, byte layer)
{
Bits = (x & 0xFFF) << 20 | (y & 0xFFF) << 8 | layer;
}
public CPos(int x, int y) { X = x; Y = y; Layer = 0; }
public CPos(int x, int y, byte layer) { X = x; Y = y; Layer = layer; }
public static readonly CPos Zero = new CPos(0, 0, 0);
public static explicit operator CPos(int2 a) { return new CPos(a.X, a.Y); }
@@ -32,12 +49,12 @@ namespace OpenRA
public static CPos operator -(CPos a, CVec b) { return new CPos(a.X - b.X, a.Y - b.Y, a.Layer); }
public static CVec operator -(CPos a, CPos b) { return new CVec(a.X - b.X, a.Y - b.Y); }
public static bool operator ==(CPos me, CPos other) { return me.X == other.X && me.Y == other.Y && me.Layer == other.Layer; }
public static bool operator ==(CPos me, CPos other) { return me.Bits == other.Bits; }
public static bool operator !=(CPos me, CPos other) { return !(me == other); }
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Layer.GetHashCode(); }
public override int GetHashCode() { return Bits.GetHashCode(); }
public bool Equals(CPos other) { return X == other.X && Y == other.Y && Layer == other.Layer; }
public bool Equals(CPos other) { return Bits == other.Bits; }
public override bool Equals(object obj) { return obj is CPos && Equals((CPos)obj); }
public override string ToString() { return X + "," + Y; }

View File

@@ -123,14 +123,14 @@ namespace OpenRA
// SEQUENCE -> BIT_STRING -> SEQUENCE -> INTEGER
// Modulus is padded with a zero to avoid issues with the sign bit
writer.Write((byte)0x02);
writer.Write((byte)0x02);
WriteTLVLength(writer, parameters.Modulus.Length + 1);
writer.Write((byte)0);
writer.Write(parameters.Modulus);
// SEQUENCE -> BIT_STRING -> SEQUENCE -> INTEGER
// Exponent is padded with a zero to avoid issues with the sign bit
writer.Write((byte)0x02);
writer.Write((byte)0x02);
WriteTLVLength(writer, parameters.Exponent.Length + 1);
writer.Write((byte)0);
writer.Write(parameters.Exponent);
@@ -180,7 +180,7 @@ namespace OpenRA
try
{
using (var rsa = new RSACryptoServiceProvider())
{
{
rsa.ImportParameters(parameters);
return Encoding.UTF8.GetString(rsa.Decrypt(Convert.FromBase64String(data), false));
}
@@ -203,7 +203,7 @@ namespace OpenRA
try
{
using (var rsa = new RSACryptoServiceProvider())
{
{
rsa.ImportParameters(parameters);
using (var csp = SHA1.Create())
return Convert.ToBase64String(rsa.SignHash(csp.ComputeHash(data), CryptoConfig.MapNameToOID("SHA1")));
@@ -227,7 +227,7 @@ namespace OpenRA
try
{
using (var rsa = new RSACryptoServiceProvider())
{
{
rsa.ImportParameters(parameters);
using (var csp = SHA1.Create())
return rsa.VerifyHash(csp.ComputeHash(data), CryptoConfig.MapNameToOID("SHA1"), Convert.FromBase64String(signature));

View File

@@ -43,8 +43,19 @@ namespace OpenRA
}
}
void EnableTLS12OnWindows()
{
// Enable TLS 1.2 on Windows: .NET 4.7 on Windows 10 only supports obsolete protocols by default
// SecurityProtocolType.Tls12 is not defined in the .NET 4.5 reference dlls used by mono,
// so we must use the enum's constant value directly
if (Platform.CurrentPlatform == PlatformType.Windows)
ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072;
}
public Download(string url, string path, Action<DownloadProgressChangedEventArgs> onProgress, Action<AsyncCompletedEventArgs> onComplete)
{
EnableTLS12OnWindows();
lock (syncObject)
{
wc = new WebClient { Proxy = null };
@@ -56,6 +67,8 @@ namespace OpenRA
public Download(string url, Action<DownloadProgressChangedEventArgs> onProgress, Action<DownloadDataCompletedEventArgs> onComplete)
{
EnableTLS12OnWindows();
lock (syncObject)
{
wc = new WebClient { Proxy = null };

View File

@@ -9,7 +9,6 @@
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Graphics;
using OpenRA.Traits;

View File

@@ -488,6 +488,11 @@ namespace OpenRA
return int.Parse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
}
public static byte ParseByte(string s)
{
return byte.Parse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
}
public static bool TryParseIntegerInvariant(string s, out int i)
{
return int.TryParse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out i);

View File

@@ -1,206 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2018 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
namespace OpenRA.FileFormats
{
public static class PngLoader
{
public static Bitmap Load(string filename)
{
using (var s = File.OpenRead(filename))
return Load(s);
}
public static Bitmap Load(Stream s)
{
using (var br = new BinaryReader(s))
{
var signature = new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 };
foreach (var b in signature)
if (br.ReadByte() != b)
throw new InvalidDataException("PNG Signature is bogus");
Bitmap bitmap = null;
Color[] palette = null;
var data = new List<byte>();
try
{
for (;;)
{
var length = IPAddress.NetworkToHostOrder(br.ReadInt32());
var type = Encoding.UTF8.GetString(br.ReadBytes(4));
var content = br.ReadBytes(length);
/*var crc = */br.ReadInt32();
if (bitmap == null && type != "IHDR")
throw new InvalidDataException("Invalid PNG file - header does not appear first.");
using (var ms = new MemoryStream(content))
using (var cr = new BinaryReader(ms))
switch (type)
{
case "IHDR":
{
if (bitmap != null)
throw new InvalidDataException("Invalid PNG file - duplicate header.");
var width = IPAddress.NetworkToHostOrder(cr.ReadInt32());
var height = IPAddress.NetworkToHostOrder(cr.ReadInt32());
var bitDepth = cr.ReadByte();
var colorType = (PngColorType)cr.ReadByte();
var compression = cr.ReadByte();
/*var filter = */cr.ReadByte();
var interlace = cr.ReadByte();
if (compression != 0) throw new InvalidDataException("Compression method not supported");
if (interlace != 0) throw new InvalidDataException("Interlacing not supported");
bitmap = new Bitmap(width, height, MakePixelFormat(bitDepth, colorType));
}
break;
case "PLTE":
{
palette = new Color[256];
for (var i = 0; i < length / 3; i++)
{
var r = cr.ReadByte(); var g = cr.ReadByte(); var b = cr.ReadByte();
palette[i] = Color.FromArgb(r, g, b);
}
}
break;
case "tRNS":
{
if (palette == null)
throw new InvalidDataException("Non-Palette indexed PNG are not supported.");
for (var i = 0; i < length; i++)
palette[i] = Color.FromArgb(cr.ReadByte(), palette[i]);
}
break;
case "IDAT":
{
data.AddRange(content);
}
break;
case "IEND":
{
var bits = bitmap.LockBits(bitmap.Bounds(),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
using (var ns = new MemoryStream(data.ToArray()))
{
// 'zlib' flags bytes; confuses the DeflateStream.
/*var flags = (byte)*/ns.ReadByte();
/*var moreFlags = (byte)*/ns.ReadByte();
using (var ds = new DeflateStream(ns, CompressionMode.Decompress))
using (var dr = new BinaryReader(ds))
{
var prevLine = new byte[bitmap.Width]; // all zero
for (var y = 0; y < bitmap.Height; y++)
{
var filter = (PngFilter)dr.ReadByte();
var line = dr.ReadBytes(bitmap.Width);
for (var i = 0; i < bitmap.Width; i++)
line[i] = i > 0
? UnapplyFilter(filter, line[i], line[i - 1], prevLine[i], prevLine[i - 1])
: UnapplyFilter(filter, line[i], 0, prevLine[i], 0);
Marshal.Copy(line, 0, new IntPtr(bits.Scan0.ToInt64() + y * bits.Stride), line.Length);
prevLine = line;
}
}
}
bitmap.UnlockBits(bits);
if (palette == null)
throw new InvalidDataException("Non-Palette indexed PNG are not supported.");
using (var temp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
{
var cp = temp.Palette;
for (var i = 0; i < 256; i++)
cp.Entries[i] = palette[i]; // finalize the palette.
bitmap.Palette = cp;
return bitmap;
}
}
}
}
}
catch
{
if (bitmap != null)
bitmap.Dispose();
throw;
}
}
}
static byte UnapplyFilter(PngFilter f, byte x, byte a, byte b, byte c)
{
switch (f)
{
case PngFilter.None: return x;
case PngFilter.Sub: return (byte)(x + a);
case PngFilter.Up: return (byte)(x + b);
case PngFilter.Average: return (byte)(x + (a + b) / 2);
case PngFilter.Paeth: return (byte)(x + Paeth(a, b, c));
default:
throw new InvalidOperationException("Unsupported Filter");
}
}
static byte Paeth(byte a, byte b, byte c)
{
var p = a + b - c;
var pa = Math.Abs(p - a);
var pb = Math.Abs(p - b);
var pc = Math.Abs(p - c);
return (pa <= pb && pa <= pc) ? a :
(pb <= pc) ? b : c;
}
[Flags]
enum PngColorType { Indexed = 1, Color = 2, Alpha = 4 }
enum PngFilter { None, Sub, Up, Average, Paeth }
static PixelFormat MakePixelFormat(byte bitDepth, PngColorType colorType)
{
if (bitDepth == 8 && colorType == (PngColorType.Indexed | PngColorType.Color))
return PixelFormat.Format8bppIndexed;
throw new InvalidDataException("Unknown pixel format");
}
}
}

View File

@@ -258,6 +258,10 @@ namespace OpenRA
static void Initialize(Arguments args)
{
var supportDirArg = args.GetValue("Engine.SupportDir", null);
if (supportDirArg != null)
Platform.OverrideSupportDir(supportDirArg);
Console.WriteLine("Platform is {0}", Platform.CurrentPlatform);
// Load the engine version as early as possible so it can be written to exception logs
@@ -566,7 +570,7 @@ namespace OpenRA
var integralTickTimestep = (uiTickDelta / Timestep) * Timestep;
Ui.LastTickTime += integralTickTimestep >= TimestepJankThreshold ? integralTickTimestep : Timestep;
Sync.CheckSyncUnchanged(world, Ui.Tick);
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, Ui.Tick);
Cursor.Tick();
}
@@ -584,7 +588,7 @@ namespace OpenRA
orderManager.LastTickTime += integralTickTimestep >= TimestepJankThreshold ? integralTickTimestep : worldTimestep;
Sound.Tick();
Sync.CheckSyncUnchanged(world, orderManager.TickImmediate);
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, orderManager.TickImmediate);
if (world == null)
return;
@@ -603,7 +607,7 @@ namespace OpenRA
if (isNetTick)
orderManager.Tick();
Sync.CheckSyncUnchanged(world, () =>
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, () =>
{
world.OrderGenerator.Tick(world);
world.Selection.Tick(world);
@@ -618,7 +622,7 @@ namespace OpenRA
// Wait until we have done our first world Tick before TickRendering
if (orderManager.LocalFrameNumber > 0)
Sync.CheckSyncUnchanged(world, () => world.TickRender(worldRenderer));
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, () => world.TickRender(worldRenderer));
}
}
}

View File

@@ -24,6 +24,7 @@ namespace OpenRA
public string MapUid;
public string MapTitle;
public int FinalGameTick;
/// <summary>Game start timestamp (when the recoding started).</summary>
public DateTime StartTimeUtc;

View File

@@ -147,20 +147,6 @@ namespace OpenRA
.Select(t => t.GetGenericArguments()[0]);
}
public IEnumerable<Pair<string, Type>> GetInitKeys()
{
var inits = traits.WithInterface<ITraitInfo>().SelectMany(
t => t.GetType().GetInterfaces()
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(UsesInit<>))
.Select(i => i.GetGenericArguments()[0])).ToList();
inits.Add(typeof(OwnerInit)); /* not exposed by a trait; this is used by the Actor itself */
return inits.Select(
i => Pair.New(
i.Name.Replace("Init", ""), i));
}
public bool HasTraitInfo<T>() where T : ITraitInfoInterface { return traits.Contains<T>(); }
public T TraitInfo<T>() where T : ITraitInfoInterface { return traits.Get<T>(); }
public T TraitInfoOrDefault<T>() where T : ITraitInfoInterface { return traits.GetOrDefault<T>(); }

View File

@@ -10,7 +10,6 @@
#endregion
using System.IO;
using OpenRA.FileFormats;
using OpenRA.FileSystem;
namespace OpenRA.GameRules
@@ -20,6 +19,7 @@ namespace OpenRA.GameRules
public readonly string Filename;
public readonly string Title;
public readonly bool Hidden;
public readonly float VolumeModifier = 1f;
public int Length { get; private set; } // seconds
public bool Exists { get; private set; }
@@ -32,6 +32,9 @@ namespace OpenRA.GameRules
if (nd.ContainsKey("Hidden"))
bool.TryParse(nd["Hidden"].Value, out Hidden);
if (nd.ContainsKey("VolumeModifier"))
VolumeModifier = FieldLoader.GetValue<float>("VolumeModifier", nd["VolumeModifier"].Value);
var ext = nd.ContainsKey("Extension") ? nd["Extension"].Value : "aud";
Filename = (nd.ContainsKey("Filename") ? nd["Filename"].Value : key) + "." + ext;
}

View File

@@ -33,19 +33,48 @@ namespace OpenRA.GameRules
{
FieldLoader.Load(this, y);
VoicePools = Exts.Lazy(() => Voices.ToDictionary(a => a.Key, a => new SoundPool(a.Value)));
NotificationsPools = Exts.Lazy(() => Notifications.ToDictionary(a => a.Key, a => new SoundPool(a.Value)));
VoicePools = Exts.Lazy(() => Voices.ToDictionary(a => a.Key, a => new SoundPool(0, 1f, a.Value)));
NotificationsPools = Exts.Lazy(() => ParseSoundPool(y, "Notifications"));
}
Dictionary<string, SoundPool> ParseSoundPool(MiniYaml y, string key)
{
var ret = new Dictionary<string, SoundPool>();
var classifiction = y.Nodes.First(x => x.Key == key);
foreach (var t in classifiction.Value.Nodes)
{
var rateLimit = 0;
var rateLimitNode = t.Value.Nodes.FirstOrDefault(x => x.Key == "RateLimit");
if (rateLimitNode != null)
rateLimit = FieldLoader.GetValue<int>(rateLimitNode.Key, rateLimitNode.Value.Value);
var volumeModifier = 1f;
var volumeModifierNode = t.Value.Nodes.FirstOrDefault(x => x.Key == "VolumeModifier");
if (volumeModifierNode != null)
volumeModifier = FieldLoader.GetValue<float>(volumeModifierNode.Key, volumeModifierNode.Value.Value);
var names = FieldLoader.GetValue<string[]>(t.Key, t.Value.Value);
var sp = new SoundPool(rateLimit, volumeModifier, names);
ret.Add(t.Key, sp);
}
return ret;
}
}
public class SoundPool
{
public readonly float VolumeModifier;
readonly string[] clips;
readonly int rateLimit;
readonly List<string> liveclips = new List<string>();
long lastPlayed = 0;
public SoundPool(params string[] clips)
public SoundPool(int rateLimit, float volumeModifier, params string[] clips)
{
VolumeModifier = volumeModifier;
this.clips = clips;
this.rateLimit = rateLimit;
}
public string GetNext()
@@ -53,8 +82,19 @@ namespace OpenRA.GameRules
if (liveclips.Count == 0)
liveclips.AddRange(clips);
// Avoid crashing if there's no clips at all
if (liveclips.Count == 0)
return null; /* avoid crashing if there's no clips at all */
return null;
// Perform rate limiting if necessary
if (rateLimit != 0)
{
var now = Game.RunTime;
if (lastPlayed != 0 && now < lastPlayed + rateLimit)
return null;
lastPlayed = now;
}
var i = Game.CosmeticRandom.Next(liveclips.Count);
var s = liveclips[i];

View File

@@ -25,6 +25,7 @@ namespace OpenRA.GameRules
public int[] InaccuracyModifiers;
public int[] RangeModifiers;
public int Facing;
public Func<int> CurrentMuzzleFacing;
public WPos Source;
public Func<WPos> CurrentSource;
public Actor SourceActor;

View File

@@ -10,7 +10,6 @@
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Support;
@@ -50,7 +49,7 @@ namespace OpenRA.Graphics
this.paused = paused;
}
public int CurrentFrame { get { return backwards ? CurrentSequence.Start + CurrentSequence.Length - frame - 1 : frame; } }
public int CurrentFrame { get { return backwards ? CurrentSequence.Length - frame - 1 : frame; } }
public Sprite Image { get { return CurrentSequence.GetSprite(CurrentFrame, facingFunc()); } }
public IRenderable[] Render(WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale)

View File

@@ -10,7 +10,6 @@
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
namespace OpenRA.Graphics

View File

@@ -12,6 +12,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
@@ -26,21 +27,18 @@ namespace OpenRA.Graphics
var sequenceYaml = MiniYaml.Merge(modData.Manifest.Cursors.Select(
s => MiniYaml.FromStream(fileSystem.Open(s), s)));
var shadowIndex = new int[] { };
var nodesDict = new MiniYaml(null, sequenceYaml).ToDictionary();
if (nodesDict.ContainsKey("ShadowIndex"))
{
Array.Resize(ref shadowIndex, shadowIndex.Length + 1);
Exts.TryParseIntegerInvariant(nodesDict["ShadowIndex"].Value,
out shadowIndex[shadowIndex.Length - 1]);
}
var palettes = new Dictionary<string, ImmutablePalette>();
foreach (var p in nodesDict["Palettes"].Nodes)
palettes.Add(p.Key, new ImmutablePalette(fileSystem.Open(p.Value.Value), shadowIndex));
// Overwrite previous definitions if there are duplicates
var pals = new Dictionary<string, IProvidesCursorPaletteInfo>();
foreach (var p in modData.DefaultRules.Actors["world"].TraitInfos<IProvidesCursorPaletteInfo>())
if (p.Palette != null)
pals[p.Palette] = p;
Palettes = palettes.AsReadOnly();
Palettes = nodesDict["Cursors"].Nodes.Select(n => n.Value.Value)
.Distinct()
.ToDictionary(p => p, p => pals[p].ReadPalette(modData.DefaultFileSystem))
.AsReadOnly();
var frameCache = new FrameCache(fileSystem, modData.SpriteLoaders);
var cursors = new Dictionary<string, CursorSequence>();

View File

@@ -45,11 +45,6 @@ namespace OpenRA.Graphics
for (var i = 0; i < Size; i++)
pal.Entries[i] = palette.GetColor(i);
// hack around a mono bug -- the palette flags get set wrong.
if (Platform.CurrentPlatform != PlatformType.Windows)
typeof(ColorPalette).GetField("flags",
BindingFlags.Instance | BindingFlags.NonPublic).SetValue(pal, 1);
return pal;
}
@@ -117,6 +112,12 @@ namespace OpenRA.Graphics
var r = (byte)(reader.ReadByte() << 2);
var g = (byte)(reader.ReadByte() << 2);
var b = (byte)(reader.ReadByte() << 2);
// Replicate high bits into the (currently zero) low bits.
r |= (byte)(r >> 6);
g |= (byte)(g >> 6);
b |= (byte)(b >> 6);
colors[i] = (uint)((255 << 24) | (r << 16) | (g << 8) | b);
}

View File

@@ -10,9 +10,6 @@
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace OpenRA.Graphics
{

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Graphics
{
public interface ISpriteLoader
{
bool TryParseSprite(Stream s, out ISpriteFrame[] frames);
bool TryParseSprite(Stream s, out ISpriteFrame[] frames, out TypeDictionary metadata);
}
public interface ISpriteFrame
@@ -50,6 +50,7 @@ namespace OpenRA.Graphics
readonly Dictionary<string, List<Sprite[]>> sprites = new Dictionary<string, List<Sprite[]>>();
readonly Dictionary<string, ISpriteFrame[]> unloadedFrames = new Dictionary<string, ISpriteFrame[]>();
readonly Dictionary<string, TypeDictionary> metadata = new Dictionary<string, TypeDictionary>();
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders, SheetBuilder sheetBuilder)
{
@@ -80,8 +81,10 @@ namespace OpenRA.Graphics
// the loaded cache (initially empty)
if (sprite == null)
{
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders);
TypeDictionary fileMetadata = null;
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
unloadedFrames[filename] = unloaded;
metadata[filename] = fileMetadata;
sprite = new Sprite[unloaded.Length];
allSprites.Add(sprite);
@@ -111,6 +114,22 @@ namespace OpenRA.Graphics
return sprite;
}
}
/// <summary>
/// Returns a TypeDictionary containing any metadata defined by the frame
/// or null if the frame does not define metadata.
/// </summary>
public TypeDictionary FrameMetadata(string filename)
{
TypeDictionary fileMetadata;
if (!metadata.TryGetValue(filename, out fileMetadata))
{
FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
metadata[filename] = fileMetadata;
}
return fileMetadata;
}
}
public class FrameCache
@@ -119,7 +138,8 @@ namespace OpenRA.Graphics
public FrameCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
{
frames = new Cache<string, ISpriteFrame[]>(filename => FrameLoader.GetFrames(fileSystem, filename, loaders));
TypeDictionary metadata;
frames = new Cache<string, ISpriteFrame[]>(filename => FrameLoader.GetFrames(fileSystem, filename, loaders, out metadata));
}
public ISpriteFrame[] this[string filename] { get { return frames[filename]; } }
@@ -127,11 +147,11 @@ namespace OpenRA.Graphics
public static class FrameLoader
{
public static ISpriteFrame[] GetFrames(IReadOnlyFileSystem fileSystem, string filename, ISpriteLoader[] loaders)
public static ISpriteFrame[] GetFrames(IReadOnlyFileSystem fileSystem, string filename, ISpriteLoader[] loaders, out TypeDictionary metadata)
{
using (var stream = fileSystem.Open(filename))
{
var spriteFrames = GetFrames(stream, loaders);
var spriteFrames = GetFrames(stream, loaders, out metadata);
if (spriteFrames == null)
throw new InvalidDataException(filename + " is not a valid sprite file!");
@@ -139,11 +159,13 @@ namespace OpenRA.Graphics
}
}
public static ISpriteFrame[] GetFrames(Stream stream, ISpriteLoader[] loaders)
public static ISpriteFrame[] GetFrames(Stream stream, ISpriteLoader[] loaders, out TypeDictionary metadata)
{
ISpriteFrame[] frames;
metadata = null;
foreach (var loader in loaders)
if (loader.TryParseSprite(stream, out frames))
if (loader.TryParseSprite(stream, out frames, out metadata))
return frames;
return null;

View File

@@ -11,7 +11,6 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace OpenRA.Graphics
{

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Graphics
readonly IShader shader;
readonly Vertex[] vertices;
readonly Sheet[] sheets = new Sheet[8];
readonly Sheet[] sheets = new Sheet[7];
BlendMode currentBlend = BlendMode.Alpha;
int nv = 0;

View File

@@ -34,7 +34,7 @@ namespace OpenRA.Graphics
readonly HashSet<Actor> onScreenActors = new HashSet<Actor>();
readonly HardwarePalette palette = new HardwarePalette();
readonly Dictionary<string, PaletteReference> palettes = new Dictionary<string, PaletteReference>();
readonly TerrainRenderer terrainRenderer;
readonly IRenderTerrain terrainRenderer;
readonly Lazy<DebugVisualizations> debugVis;
readonly Func<string, PaletteReference> createPaletteReference;
readonly bool enableDepthBuffer;
@@ -62,7 +62,7 @@ namespace OpenRA.Graphics
palette.Initialize();
Theater = new Theater(world.Map.Rules.TileSet);
terrainRenderer = new TerrainRenderer(world, this);
terrainRenderer = world.WorldActor.TraitOrDefault<IRenderTerrain>();
debugVis = Exts.Lazy(() => world.WorldActor.TraitOrDefault<DebugVisualizations>());
}
@@ -181,7 +181,9 @@ namespace OpenRA.Graphics
if (enableDepthBuffer)
Game.Renderer.Context.EnableDepthBuffer();
terrainRenderer.Draw(this, Viewport);
if (terrainRenderer != null)
terrainRenderer.RenderTerrain(this, Viewport);
Game.Renderer.Flush();
for (var i = 0; i < renderables.Count; i++)
@@ -330,7 +332,6 @@ namespace OpenRA.Graphics
palette.Dispose();
Theater.Dispose();
terrainRenderer.Dispose();
}
}
}

View File

@@ -37,17 +37,17 @@ namespace OpenRA
public void OnKeyInput(KeyInput input)
{
Sync.CheckSyncUnchanged(world, () => Ui.HandleKeyPress(input));
Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, () => Ui.HandleKeyPress(input));
}
public void OnTextInput(string text)
{
Sync.CheckSyncUnchanged(world, () => Ui.HandleTextInput(text));
Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, () => Ui.HandleTextInput(text));
}
public void OnMouseInput(MouseInput input)
{
Sync.CheckSyncUnchanged(world, () => Ui.HandleInput(input));
Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, () => Ui.HandleInput(input));
}
}

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information
#region Copyright & License Information
/*
* Copyright 2007-2018 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
@@ -16,7 +16,6 @@ using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using OpenRA.Network;
namespace OpenRA
{
@@ -168,7 +167,11 @@ namespace OpenRA
public string Sign(params string[] data)
{
if (State != LinkState.Linked)
// If we don't have any keys, or we know for sure that they haven't been linked to the forum
// then we can't do much here. If we have keys but don't yet know if they have been linked to the
// forum (LinkState.CheckingLink or ConnectionFailed) then we sign to avoid blocking the main thread
// but accept that - if the cert is invalid - the server will reject the result.
if (State <= LinkState.Unlinked)
return null;
return CryptoUtil.Sign(parameters, data.Where(x => !string.IsNullOrEmpty(x)).JoinWith(string.Empty));
@@ -176,7 +179,7 @@ namespace OpenRA
public string DecryptString(string data)
{
if (State != LinkState.Linked)
if (State <= LinkState.Unlinked)
return null;
return CryptoUtil.DecryptString(parameters, data);

View File

@@ -18,7 +18,6 @@ using System.Linq;
using System.Reflection;
using System.Text;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Support;
using OpenRA.Traits;
@@ -786,7 +785,7 @@ namespace OpenRA
public WPos CenterOfSubCell(CPos cell, SubCell subCell)
{
var index = (int)subCell;
if (index >= 0 && index <= Grid.SubCellOffsets.Length)
if (index >= 0 && index < Grid.SubCellOffsets.Length)
return CenterOfCell(cell) + Grid.SubCellOffsets[index];
return CenterOfCell(cell);
}

View File

@@ -205,6 +205,10 @@ namespace OpenRA
var yaml = MiniYaml.FromString(data);
foreach (var kv in yaml)
maps[kv.Key].UpdateRemoteSearch(MapStatus.DownloadAvailable, kv.Value, mapDetailsReceived);
foreach (var map in maps)
if (map.Value.Status != MapStatus.DownloadAvailable)
map.Value.UpdateRemoteSearch(MapStatus.Unavailable, null);
}
catch (Exception e)
{
@@ -332,8 +336,8 @@ namespace OpenRA
if (string.IsNullOrEmpty(initialUid) || previews[initialUid].Status != MapStatus.Available)
{
var selected = previews.Values.Where(IsSuitableInitialMap).RandomOrDefault(random) ??
previews.Values.First(m => m.Status == MapStatus.Available && m.Visibility.HasFlag(MapVisibility.Lobby));
return selected.Uid;
previews.Values.FirstOrDefault(m => m.Status == MapStatus.Available && m.Visibility.HasFlag(MapVisibility.Lobby));
return selected == null ? string.Empty : selected.Uid;
}
return initialUid;

View File

@@ -168,7 +168,11 @@ namespace OpenRA
if (subCell == SubCell.Invalid || subCell == SubCell.Any)
return WVec.Zero;
return SubCellOffsets[(int)subCell];
var index = (int)subCell;
if (index >= 0 && index < SubCellOffsets.Length)
return SubCellOffsets[index];
return WVec.Zero;
}
}
}

View File

@@ -11,7 +11,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.IO;
@@ -56,7 +55,7 @@ namespace OpenRA
public readonly string[] categories;
public readonly int players;
public readonly Rectangle bounds;
public readonly int[] spawnpoints = { };
public readonly short[] spawnpoints = { };
public readonly MapGridType map_grid_type;
public readonly string minimap;
public readonly bool downloading;

View File

@@ -30,36 +30,10 @@ namespace OpenRA
public readonly float ZOffset = 0.0f;
public readonly float ZRamp = 1.0f;
public MiniYaml Save(TileSet tileSet)
{
var root = new List<MiniYamlNode>();
if (Height != 0)
root.Add(FieldSaver.SaveField(this, "Height"));
if (RampType != 0)
root.Add(FieldSaver.SaveField(this, "RampType"));
if (LeftColor != tileSet.TerrainInfo[TerrainType].Color)
root.Add(FieldSaver.SaveField(this, "LeftColor"));
if (RightColor != tileSet.TerrainInfo[TerrainType].Color)
root.Add(FieldSaver.SaveField(this, "RightColor"));
if (ZOffset != 0.0f)
root.Add(FieldSaver.SaveField(this, "ZOffset"));
if (ZRamp != 1.0f)
root.Add(FieldSaver.SaveField(this, "ZRamp"));
return new MiniYaml(tileSet.TerrainInfo[TerrainType].Type, root);
}
}
public class TerrainTypeInfo
{
static readonly TerrainTypeInfo Default = new TerrainTypeInfo();
public readonly string Type;
public readonly BitSet<TargetableType> TargetTypes;
public readonly HashSet<string> AcceptsSmudgeType = new HashSet<string>();
@@ -67,18 +41,11 @@ namespace OpenRA
public readonly bool RestrictPlayerColor = false;
public readonly string CustomCursor;
// Private default ctor for serialization comparison
TerrainTypeInfo() { }
public TerrainTypeInfo(MiniYaml my) { FieldLoader.Load(this, my); }
public MiniYaml Save() { return FieldSaver.SaveDifferences(this, Default); }
}
public class TerrainTemplateInfo
{
static readonly TerrainTemplateInfo Default = new TerrainTemplateInfo(0, new string[] { null }, int2.Zero, null);
public readonly ushort Id;
public readonly string[] Images;
public readonly int[] Frames;
@@ -160,21 +127,6 @@ namespace OpenRA
{
get { return tileInfo.Length; }
}
public MiniYaml Save(TileSet tileSet)
{
var root = FieldSaver.SaveDifferences(this, Default);
var tileYaml = tileInfo
.Select((ti, i) => Pair.New(i.ToString(), ti))
.Where(t => t.Second != null)
.Select(t => new MiniYamlNode(t.First, t.Second.Save(tileSet)))
.ToList();
root.Nodes.Add(new MiniYamlNode("Tiles", null, tileYaml));
return root;
}
}
public class TileSet
@@ -197,9 +149,6 @@ namespace OpenRA
readonly Dictionary<string, byte> terrainIndexByType = new Dictionary<string, byte>();
readonly byte defaultWalkableTerrainIndex;
// Private default ctor for serialization comparison
TileSet() { }
public TileSet(IReadOnlyFileSystem fileSystem, string filepath)
{
var yaml = MiniYaml.FromStream(fileSystem.Open(filepath), filepath)
@@ -298,18 +247,5 @@ namespace OpenRA
return tpl.Contains(r.Index) ? tpl[r.Index] : null;
}
public void Save(string filepath)
{
var root = new List<MiniYamlNode>();
root.Add(new MiniYamlNode("General", FieldSaver.SaveDifferences(this, new TileSet())));
root.Add(new MiniYamlNode("Terrain", null,
TerrainInfo.Select(t => new MiniYamlNode("TerrainType@{0}".F(t.Type), t.Save())).ToList()));
root.Add(new MiniYamlNode("Templates", null,
Templates.Select(t => new MiniYamlNode("Template@{0}".F(t.Value.Id), t.Value.Save(this))).ToList()));
root.WriteToFile(filepath);
}
}
}

View File

@@ -20,6 +20,7 @@ namespace OpenRA.Network
public class GameClient
{
public readonly string Name;
public readonly string Fingerprint;
public readonly HSLColor Color;
public readonly string Faction;
public readonly int Team;
@@ -33,6 +34,7 @@ namespace OpenRA.Network
public GameClient(Session.Client c)
{
Name = c.Name;
Fingerprint = c.Fingerprint;
Color = c.Color;
Faction = c.Faction;
Team = c.Team;
@@ -54,7 +56,7 @@ namespace OpenRA.Network
"Mod", "Version", "ModTitle", "ModWebsite", "ModIcon32",
// Current server state
"Map", "State", "MaxPlayers", "Protected"
"Map", "State", "MaxPlayers", "Protected", "Authentication"
};
public const int ProtocolVersion = 2;
@@ -98,6 +100,9 @@ namespace OpenRA.Network
/// <summary>Password protected</summary>
public readonly bool Protected = false;
/// <summary>Players must be authenticated with the OpenRA forum</summary>
public readonly bool Authentication = false;
/// <summary>UTC datetime string when the game changed to the Playing state</summary>
public readonly string Started = null;
@@ -222,6 +227,7 @@ namespace OpenRA.Network
ModWebsite = manifest.Metadata.Website;
ModIcon32 = manifest.Metadata.WebIcon32;
Protected = !string.IsNullOrEmpty(server.Settings.Password);
Authentication = server.Settings.RequireAuthentication || server.Settings.ProfileIDWhitelist.Any();
Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToArray();
}

View File

@@ -11,7 +11,6 @@
using System;
using System.IO;
using System.Linq;
using OpenRA.Network;
using OpenRA.Traits;
@@ -76,10 +75,12 @@ namespace OpenRA
public static Order Deserialize(World world, BinaryReader r)
{
var magic = r.ReadByte();
switch (magic)
try
{
case 0xFF:
var magic = r.ReadByte();
switch (magic)
{
case 0xFF:
{
var order = r.ReadString();
var subjectId = r.ReadUInt32();
@@ -111,11 +112,10 @@ namespace OpenRA
if (world == null || !TryGetActorFromUInt(world, playerActorID, out playerActor))
break;
var frozenLayer = playerActor.TraitOrDefault<FrozenActorLayer>();
if (frozenLayer == null)
if (playerActor.Owner.FrozenActorLayer == null)
break;
var frozen = frozenLayer.FromID(frozenActorID);
var frozen = playerActor.Owner.FrozenActorLayer.FromID(frozenActorID);
if (frozen != null)
target = Target.FromFrozenActor(frozen);
@@ -126,8 +126,8 @@ namespace OpenRA
{
if (flags.HasField(OrderFields.TargetIsCell))
{
var cell = new CPos(r.ReadInt32(), r.ReadInt32(), r.ReadByte());
var subCell = (SubCell)r.ReadInt32();
var cell = new CPos(r.ReadInt32());
var subCell = (SubCell)r.ReadByte();
if (world != null)
target = Target.FromCell(world, cell, subCell);
}
@@ -144,7 +144,7 @@ namespace OpenRA
var targetString = flags.HasField(OrderFields.TargetString) ? r.ReadString() : null;
var queued = flags.HasField(OrderFields.Queued);
var extraLocation = flags.HasField(OrderFields.ExtraLocation) ? new CPos(r.ReadInt32(), r.ReadInt32(), r.ReadByte()) : CPos.Zero;
var extraLocation = flags.HasField(OrderFields.ExtraLocation) ? new CPos(r.ReadInt32()) : CPos.Zero;
var extraData = flags.HasField(OrderFields.ExtraData) ? r.ReadUInt32() : 0;
if (world == null)
@@ -156,7 +156,7 @@ namespace OpenRA
return new Order(order, subject, target, targetString, queued, extraLocation, extraData);
}
case 0xfe:
case 0xfe:
{
var name = r.ReadString();
var data = r.ReadString();
@@ -164,11 +164,23 @@ namespace OpenRA
return new Order(name, null, false) { IsImmediate = true, TargetString = data };
}
default:
default:
{
Log.Write("debug", "Received unknown order with magic {0}", magic);
return null;
}
}
}
catch (Exception e)
{
Log.Write("debug", "Caught exception while processing order");
Log.Write("debug", e.ToString());
// HACK: this can hopefully go away in the future
Game.Debug("Ignoring malformed order that would have crashed the game");
Game.Debug("Please file a bug report and include the replay from this match");
return null;
}
}
@@ -217,9 +229,9 @@ namespace OpenRA
return new Order("Command", null, false) { IsImmediate = true, TargetString = text };
}
public static Order StartProduction(Actor subject, string item, int count)
public static Order StartProduction(Actor subject, string item, int count, bool queued = true)
{
return new Order("StartProduction", subject, false) { ExtraData = (uint)count, TargetString = item };
return new Order("StartProduction", subject, queued) { ExtraData = (uint)count, TargetString = item };
}
public static Order PauseProduction(Actor subject, string item, bool pause)
@@ -297,7 +309,7 @@ namespace OpenRA
if (fields.HasField(OrderFields.TargetIsCell))
{
w.Write(Target.SerializableCell.Value);
w.Write((int)Target.SerializableSubCell);
w.Write((byte)Target.SerializableSubCell);
}
else
w.Write(Target.SerializablePos);

View File

@@ -58,9 +58,7 @@ namespace OpenRA.Network
public static void Write(this BinaryWriter w, CPos cell)
{
w.Write(cell.X);
w.Write(cell.Y);
w.Write(cell.Layer);
w.Write(cell.Bits);
}
public static void Write(this BinaryWriter w, WPos pos)

View File

@@ -14,6 +14,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA.Network
{
@@ -52,6 +53,7 @@ namespace OpenRA.Network
public readonly ReadOnlyList<ChatLine> ChatCache;
bool disposed;
bool generateSyncReport = false;
void OutOfSync(int frame)
{
@@ -61,7 +63,12 @@ namespace OpenRA.Network
public void StartGame()
{
if (GameStarted) return;
if (GameStarted)
return;
// Generating sync reports is expensive, so only do it if we have
// other players to compare against if a desync did occur
generateSyncReport = !(Connection is ReplayConnection) && LobbyInfo.GlobalSettings.EnableSyncReports;
NetFrameNumber = 1;
for (var i = NetFrameNumber; i <= FramesAhead; i++)
@@ -180,7 +187,9 @@ namespace OpenRA.Network
Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(World.SyncHash()));
syncReport.UpdateSyncReport();
if (generateSyncReport)
using (new PerfSample("sync_report"))
syncReport.UpdateSyncReport();
++NetFrameNumber;
}

View File

@@ -33,6 +33,7 @@ namespace OpenRA.Network
public int LocalClientId { get { return 0; } }
public ConnectionState ConnectionState { get { return ConnectionState.Connected; } }
public readonly int TickCount;
public readonly int FinalGameTick;
public readonly bool IsValid;
public readonly Session LobbyInfo;
public readonly string Filename;
@@ -40,6 +41,7 @@ namespace OpenRA.Network
public ReplayConnection(string replayFilename)
{
Filename = replayFilename;
FinalGameTick = ReplayMetadata.Read(replayFilename).GameInfo.FinalGameTick;
// Parse replay data into a struct that can be fed to the game in chunks
// to avoid issues with all immediate orders being resolved on the first tick.

View File

@@ -198,9 +198,9 @@ namespace OpenRA.Network
public int OrderLatency = 3; // net tick frames (x 120 = ms)
public int RandomSeed = 0;
public bool AllowSpectators = true;
public bool AllowVersionMismatch;
public string GameUid;
public bool EnableSingleplayer;
public bool EnableSyncReports;
public bool Dedicated;
[FieldLoader.Ignore]

View File

@@ -10,7 +10,6 @@
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Threading;

View File

@@ -9,12 +9,9 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using OpenRA.Traits;
namespace OpenRA.Network

View File

@@ -31,6 +31,7 @@
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
@@ -38,7 +39,6 @@
<OutputPath>..</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<StartArguments>Game.Mod=ra</StartArguments>
@@ -53,7 +53,6 @@
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<Optimize>true</Optimize>
<StartArguments>Game.Mod=ra</StartArguments>
<Commandlineparameters>Game.Mod=ra</Commandlineparameters>
@@ -135,7 +134,6 @@
<Compile Include="Graphics\SpriteFont.cs" />
<Compile Include="Graphics\SpriteLoader.cs" />
<Compile Include="Graphics\SpriteRenderer.cs" />
<Compile Include="Graphics\TerrainRenderer.cs" />
<Compile Include="Graphics\Util.cs" />
<Compile Include="Graphics\Viewport.cs" />
<Compile Include="Graphics\WorldRenderer.cs" />
@@ -275,7 +273,6 @@
<Compile Include="FieldSaver.cs" />
<Compile Include="Manifest.cs" />
<Compile Include="Graphics\Vertex.cs" />
<Compile Include="FileFormats\PngLoader.cs" />
<Compile Include="Primitives\ActionQueue.cs" />
<Compile Include="Primitives\BitSet.cs" />
<Compile Include="Primitives\Cache.cs" />

View File

@@ -12,7 +12,6 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Orders

View File

@@ -12,7 +12,6 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
namespace OpenRA
@@ -71,9 +70,30 @@ namespace OpenRA
/// </summary>
public static string SupportDir { get { return supportDir.Value; } }
static Lazy<string> supportDir = Exts.Lazy(GetSupportDir);
static string supportDirOverride;
/// <summary>
/// Specify a custom support directory that already exists on the filesystem.
/// MUST be called before Platform.SupportDir is first accessed.
/// </summary>
public static void OverrideSupportDir(string path)
{
if (!Directory.Exists(path))
throw new DirectoryNotFoundException(path);
if (!path.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) &&
!path.EndsWith(Path.AltDirectorySeparatorChar.ToString(), StringComparison.Ordinal))
path += Path.DirectorySeparatorChar;
supportDirOverride = path;
}
static string GetSupportDir()
{
// Use the custom override if it has been defined
if (supportDirOverride != null)
return supportDirOverride;
// Use a local directory in the game root if it exists (shared with the system support dir)
var localSupportDir = Path.Combine(GameDir, "Support");
if (Directory.Exists(localSupportDir))

View File

@@ -56,6 +56,8 @@ namespace OpenRA
public readonly PlayerReference PlayerReference;
public readonly bool IsBot;
public readonly string BotType;
public readonly Shroud Shroud;
public readonly FrozenActorLayer FrozenActorLayer;
/// <summary>The faction (including Random, etc) that was selected in the lobby.</summary>
public readonly FactionInfo DisplayFaction;
@@ -65,7 +67,6 @@ namespace OpenRA
public bool HasObjectives = false;
public bool Spectating;
public Shroud Shroud;
public World World { get; private set; }
readonly bool inMissionMap;
@@ -156,6 +157,7 @@ namespace OpenRA
var playerActorType = world.Type == WorldType.Editor ? "EditorPlayer" : "Player";
PlayerActor = world.CreateActor(playerActorType, new TypeDictionary { new OwnerInit(this) });
Shroud = PlayerActor.Trait<Shroud>();
FrozenActorLayer = PlayerActor.TraitOrDefault<FrozenActorLayer>();
// Enable the bot logic on the host
IsBot = BotType != null;

View File

@@ -9,9 +9,7 @@
*/
#endregion
using System;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Runtime.InteropServices;
namespace OpenRA

View File

@@ -79,6 +79,18 @@ namespace OpenRA.Scripting
return true;
}
if (value is LuaNumber && t.IsAssignableFrom(typeof(short)))
{
clrObject = (short)value.ToNumber().Value;
return true;
}
if (value is LuaNumber && t.IsAssignableFrom(typeof(byte)))
{
clrObject = (byte)value.ToNumber().Value;
return true;
}
if (value is LuaString && t.IsAssignableFrom(typeof(string)))
{
clrObject = value.ToString();

View File

@@ -13,8 +13,6 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Traits
{

View File

@@ -21,7 +21,14 @@ namespace OpenRA
{
public int Hash { get; private set; }
public IEnumerable<Actor> Actors { get { return actors; } }
readonly HashSet<Actor> actors = new HashSet<Actor>();
readonly INotifySelection[] worldNotifySelection;
internal Selection(IEnumerable<INotifySelection> worldNotifySelection)
{
this.worldNotifySelection = worldNotifySelection.ToArray();
}
void UpdateHash()
{
@@ -31,17 +38,41 @@ namespace OpenRA
Hash += 1;
}
public void Add(World w, Actor a)
public void Add(Actor a)
{
actors.Add(a);
UpdateHash();
foreach (var sel in a.TraitsImplementing<INotifySelected>())
sel.Selected(a);
foreach (var ns in w.WorldActor.TraitsImplementing<INotifySelection>())
foreach (var ns in worldNotifySelection)
ns.SelectionChanged();
}
public void Remove(Actor a)
{
if (actors.Remove(a))
{
UpdateHash();
foreach (var ns in worldNotifySelection)
ns.SelectionChanged();
}
}
internal void OnOwnerChanged(Actor a, Player oldOwner, Player newOwner)
{
if (!actors.Contains(a))
return;
// Remove the actor from the original owners selection
// Call UpdateHash directly for everyone else so watchers can account for the owner change if needed
if (oldOwner == a.World.LocalPlayer)
Remove(a);
else
UpdateHash();
}
public bool Contains(Actor a)
{
return actors.Contains(a);
@@ -77,7 +108,7 @@ namespace OpenRA
foreach (var sel in a.TraitsImplementing<INotifySelected>())
sel.Selected(a);
foreach (var ns in world.WorldActor.TraitsImplementing<INotifySelection>())
foreach (var ns in worldNotifySelection)
ns.SelectionChanged();
if (world.IsGameOver)

View File

@@ -157,6 +157,7 @@ namespace OpenRA.Server
Map = settings.Map,
ServerName = settings.Name,
EnableSingleplayer = settings.EnableSingleplayer || !dedicated,
EnableSyncReports = settings.EnableSyncReports,
GameUid = Guid.NewGuid().ToString(),
Dedicated = dedicated
}
@@ -170,7 +171,6 @@ namespace OpenRA.Server
Log.Write("server", "Initial mod: {0}", ModData.Manifest.Id);
Log.Write("server", "Initial map: {0}", LobbyInfo.GlobalSettings.Map);
var timeout = serverTraits.WithInterface<ITick>().Min(t => t.TickTimeout);
for (;;)
{
var checkRead = new List<Socket>();
@@ -180,7 +180,9 @@ namespace OpenRA.Server
checkRead.AddRange(Conns.Select(c => c.Socket));
checkRead.AddRange(PreConns.Select(c => c.Socket));
var localTimeout = waitingForAuthenticationCallback > 0 ? 100000 : timeout;
// Block for at most 1 second in order to guarantee a minimum tick rate for ServerTraits
// Decrease this to 100ms to improve responsiveness if we are waiting for an authentication query
var localTimeout = waitingForAuthenticationCallback > 0 ? 100000 : 1000000;
if (checkRead.Count > 0)
Socket.Select(checkRead, null, null, localTimeout);
@@ -323,7 +325,6 @@ namespace OpenRA.Server
Name = OpenRA.Settings.SanitizedPlayerName(handshake.Client.Name),
IpAddress = ((IPEndPoint)newConn.Socket.RemoteEndPoint).Address.ToString(),
Index = newConn.PlayerIndex,
Slot = LobbyInfo.FirstEmptySlot(),
PreferredColor = handshake.Client.PreferredColor,
Color = handshake.Client.Color,
Faction = "Random",
@@ -333,18 +334,6 @@ namespace OpenRA.Server
IsAdmin = !LobbyInfo.Clients.Any(c1 => c1.IsAdmin)
};
if (client.IsObserver && !LobbyInfo.GlobalSettings.AllowSpectators)
{
SendOrderTo(newConn, "ServerError", "The game is full");
DropClient(newConn);
return;
}
if (client.Slot != null)
SyncClientToPlayerReference(client, Map.Players.Players[client.Slot]);
else
client.Color = HSLColor.FromRGB(255, 255, 255);
if (ModData.Manifest.Id != handshake.Mod)
{
Log.Write("server", "Rejected connection from {0}; mods do not match.",
@@ -355,7 +344,7 @@ namespace OpenRA.Server
return;
}
if (ModData.Manifest.Metadata.Version != handshake.Version && !LobbyInfo.GlobalSettings.AllowVersionMismatch)
if (ModData.Manifest.Metadata.Version != handshake.Version)
{
Log.Write("server", "Rejected connection from {0}; Not running the same version.",
newConn.Socket.RemoteEndPoint);
@@ -377,6 +366,20 @@ namespace OpenRA.Server
Action completeConnection = () =>
{
client.Slot = LobbyInfo.FirstEmptySlot();
if (client.IsObserver && !LobbyInfo.GlobalSettings.AllowSpectators)
{
SendOrderTo(newConn, "ServerError", "The game is full");
DropClient(newConn);
return;
}
if (client.Slot != null)
SyncClientToPlayerReference(client, Map.Players.Players[client.Slot]);
else
client.Color = HSLColor.FromRGB(255, 255, 255);
// Promote connection to a valid client
PreConns.Remove(newConn);
Conns.Add(newConn);
@@ -475,11 +478,25 @@ namespace OpenRA.Server
delayedActions.Add(() =>
{
if (Dedicated && Settings.RequireAuthIDs.Any() &&
(profile == null || !Settings.RequireAuthIDs.Contains(profile.ProfileID)))
var notAuthenticated = Dedicated && profile == null && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Any());
var blacklisted = Dedicated && profile != null && Settings.ProfileIDBlacklist.Contains(profile.ProfileID);
var notWhitelisted = Dedicated && Settings.ProfileIDWhitelist.Any() &&
(profile == null || !Settings.ProfileIDWhitelist.Contains(profile.ProfileID));
if (notAuthenticated)
{
Log.Write("server", "Rejected connection from {0}; Not in server whitelist.", newConn.Socket.RemoteEndPoint);
SendOrderTo(newConn, "ServerError", "You are not authenticated for this server");
Log.Write("server", "Rejected connection from {0}; Not authenticated.", newConn.Socket.RemoteEndPoint);
SendOrderTo(newConn, "ServerError", "Server requires players to have an OpenRA forum account");
DropClient(newConn);
}
else if (blacklisted || notWhitelisted)
{
if (blacklisted)
Log.Write("server", "Rejected connection from {0}; In server blacklist.", newConn.Socket.RemoteEndPoint);
else
Log.Write("server", "Rejected connection from {0}; Not in server whitelist.", newConn.Socket.RemoteEndPoint);
SendOrderTo(newConn, "ServerError", "You do not have permission to join this server");
DropClient(newConn);
}
else
@@ -493,10 +510,10 @@ namespace OpenRA.Server
}
else
{
if (Dedicated && Settings.RequireAuthIDs.Any())
if (Dedicated && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Any()))
{
Log.Write("server", "Rejected connection from {0}; Not authenticated and whitelist is set.", newConn.Socket.RemoteEndPoint);
SendOrderTo(newConn, "ServerError", "You are not authenticated for this server");
Log.Write("server", "Rejected connection from {0}; Not authenticated.", newConn.Socket.RemoteEndPoint);
SendOrderTo(newConn, "ServerError", "Server requires players to have an OpenRA forum account");
DropClient(newConn);
}
else

View File

@@ -23,11 +23,7 @@ namespace OpenRA.Server
public interface IStartGame { void GameStarted(Server server); }
public interface IClientJoined { void ClientJoined(Server server, Connection conn); }
public interface IEndGame { void GameEnded(Server server); }
public interface ITick
{
void Tick(Server server);
int TickTimeout { get; }
}
public interface ITick { void Tick(Server server); }
public abstract class ServerTrait { }

View File

@@ -59,8 +59,14 @@ namespace OpenRA
[Desc("Takes a comma separated list of IP addresses that are not allowed to join.")]
public string[] Ban = { };
[Desc("If non-empty, only allow authenticated players with these user IDs to join.")]
public int[] RequireAuthIDs = { };
[Desc("For dedicated servers only, allow anonymous clients to join.")]
public bool RequireAuthentication = false;
[Desc("For dedicated servers only, if non-empty, only allow authenticated players with these profile IDs to join.")]
public int[] ProfileIDWhitelist = { };
[Desc("For dedicated servers only, if non-empty, always reject players with these user IDs from joining.")]
public int[] ProfileIDBlacklist = { };
[Desc("For dedicated servers only, controls whether a game can be started with just one human player in the lobby.")]
public bool EnableSingleplayer = false;
@@ -68,6 +74,9 @@ namespace OpenRA
[Desc("Query map information from the Resource Center if they are not available locally.")]
public bool QueryMapRepository = true;
[Desc("Enable client-side report generation to help debug desync errors.")]
public bool EnableSyncReports = false;
public string TimestampFormat = "s";
public ServerSettings Clone()
@@ -78,31 +87,50 @@ namespace OpenRA
public class DebugSettings
{
public bool BotDebug = false;
public bool LuaDebug = false;
[Desc("Display average FPS and tick/render times")]
public bool PerfText = false;
[Desc("Display a graph with various profiling traces")]
public bool PerfGraph = false;
[Desc("Amount of time required for triggering perf.log output.")]
public float LongTickThresholdMs = 1;
public bool SanityCheckUnsyncedCode = false;
[Desc("Numer of samples to average over when calculating tick and render times.")]
public int Samples = 25;
[Desc("Show incompatible games in server browser.")]
public bool IgnoreVersionMismatch = false;
public bool StrictActivityChecking = false;
[Desc("Check whether a newer version is available online.")]
public bool CheckVersion = true;
[Desc("Allow the collection of anonymous data such as Operating System, .NET runtime, OpenGL version and language settings.")]
public bool SendSystemInformation = true;
[Desc("Version of sysinfo that the player last opted in or out of.")]
public int SystemInformationVersionPrompt = 0;
public string UUID = System.Guid.NewGuid().ToString();
[Desc("Sysinfo anonymous user identifier.")]
public string UUID = Guid.NewGuid().ToString();
[Desc("Enable hidden developer settings in the Advanced settings tab.")]
public bool DisplayDeveloperSettings = false;
[Desc("Display bot debug messages in the game chat.")]
public bool BotDebug = false;
[Desc("Display Lua debug messages in the game chat.")]
public bool LuaDebug = false;
[Desc("Enable the chat field during replays to allow use of console commands.")]
public bool EnableDebugCommandsInReplays = false;
[Desc("Amount of time required for triggering perf.log output.")]
public float LongTickThresholdMs = 1;
[Desc("Throw an exception if the world sync hash changes while evaluating user input.")]
public bool SyncCheckUnsyncedCode = false;
[Desc("Throw an exception if the world sync hash changes while evaluating BotModules.")]
public bool SyncCheckBotModuleCode = false;
[Desc("Throw an exception if an actor activity is ticked after it has been marked as completed.")]
public bool StrictActivityChecking = false;
}
public class GraphicSettings
@@ -130,6 +158,9 @@ namespace OpenRA
[Desc("Disable high resolution DPI scaling on Windows operating systems.")]
public bool DisableWindowsDPIScaling = true;
[Desc("Disable separate OpenGL render thread on Windows operating systems.")]
public bool DisableWindowsRenderThread = true;
public int BatchSize = 8192;
public int SheetSize = 2048;

View File

@@ -214,7 +214,7 @@ namespace OpenRA
Func<ISoundFormat, ISound> stream = soundFormat => soundEngine.Play2DStream(
soundFormat.GetPCMInputStream(), soundFormat.Channels, soundFormat.SampleBits, soundFormat.SampleRate,
false, true, WPos.Zero, MusicVolume);
false, true, WPos.Zero, MusicVolume * m.VolumeModifier);
music = LoadSound(m.Filename, stream);
if (music == null)
@@ -352,7 +352,7 @@ namespace OpenRA
var id = voicedActor != null ? voicedActor.ActorID : 0;
string clip;
SoundPool pool;
var suffix = rules.DefaultVariant;
var prefix = rules.DefaultPrefix;
@@ -361,16 +361,17 @@ namespace OpenRA
if (!rules.VoicePools.Value.ContainsKey(definition))
throw new InvalidOperationException("Can't find {0} in voice pool.".F(definition));
clip = rules.VoicePools.Value[definition].GetNext();
pool = rules.VoicePools.Value[definition];
}
else
{
if (!rules.NotificationsPools.Value.ContainsKey(definition))
throw new InvalidOperationException("Can't find {0} in notification pool.".F(definition));
clip = rules.NotificationsPools.Value[definition].GetNext();
pool = rules.NotificationsPools.Value[definition];
}
var clip = pool.GetNext();
if (string.IsNullOrEmpty(clip))
return false;
@@ -388,7 +389,7 @@ namespace OpenRA
{
var sound = soundEngine.Play2D(sounds[name],
false, relative, pos,
InternalSoundVolume * volumeModifier, attenuateVolume);
InternalSoundVolume * volumeModifier * pool.VolumeModifier, attenuateVolume);
if (id != 0)
{
if (currentSounds.ContainsKey(id))

View File

@@ -168,8 +168,8 @@ namespace OpenRA.Support
public readonly Grouping Closes;
public TokenTypeInfo(string symbol, Precedence precedence, Sides operandSides = Sides.None,
Associativity associativity = Associativity.Left,
Grouping opens = Grouping.None, Grouping closes = Grouping.None)
Associativity associativity = Associativity.Left,
Grouping opens = Grouping.None, Grouping closes = Grouping.None)
{
Symbol = symbol;
Precedence = precedence;
@@ -181,9 +181,9 @@ namespace OpenRA.Support
}
public TokenTypeInfo(string symbol, Precedence precedence, Sides operandSides,
Sides whitespaceSides,
Associativity associativity = Associativity.Left,
Grouping opens = Grouping.None, Grouping closes = Grouping.None)
Sides whitespaceSides,
Associativity associativity = Associativity.Left,
Grouping opens = Grouping.None, Grouping closes = Grouping.None)
{
Symbol = symbol;
Precedence = precedence;
@@ -195,15 +195,14 @@ namespace OpenRA.Support
}
public TokenTypeInfo(string symbol, Precedence precedence, Grouping opens, Grouping closes = Grouping.None,
Associativity associativity = Associativity.Left)
Associativity associativity = Associativity.Left)
{
Symbol = symbol;
Precedence = precedence;
WhitespaceSides = Sides.None;
OperandSides = opens == Grouping.None ?
(closes == Grouping.None ? Sides.None : Sides.Left)
:
(closes == Grouping.None ? Sides.Right : Sides.Both);
(closes == Grouping.None ? Sides.None : Sides.Left) :
(closes == Grouping.None ? Sides.Right : Sides.Both);
Associativity = associativity;
Opens = opens;
Closes = closes;

View File

@@ -96,7 +96,7 @@ namespace OpenRA
foreach (var prop in t.GetProperties(Binding).Where(x => x.HasAttribute<SyncAttribute>()))
{
il.Emit(OpCodes.Ldloc, this_);
il.EmitCall(OpCodes.Call, prop.GetGetMethod(), null);
il.EmitCall(OpCodes.Call, prop.GetGetMethod(true), null);
EmitSyncOpcodes(prop.PropertyType, il);
}
@@ -161,20 +161,19 @@ namespace OpenRA
return t.GetHashCode();
}
public static void CheckSyncUnchanged(World world, Action fn)
public static void RunUnsynced(bool checkSyncHash, World world, Action fn)
{
CheckSyncUnchanged(world, () => { fn(); return true; });
RunUnsynced(checkSyncHash, world, () => { fn(); return true; });
}
static bool inUnsyncedCode = false;
public static T CheckSyncUnchanged<T>(World world, Func<T> fn)
public static T RunUnsynced<T>(bool checkSyncHash, World world, Func<T> fn)
{
if (world == null)
return fn();
var shouldCheckSync = Game.Settings.Debug.SanityCheckUnsyncedCode;
var sync = shouldCheckSync ? world.SyncHash() : 0;
var sync = checkSyncHash ? world.SyncHash() : 0;
var prevInUnsyncedCode = inUnsyncedCode;
inUnsyncedCode = true;
@@ -185,8 +184,8 @@ namespace OpenRA
finally
{
inUnsyncedCode = prevInUnsyncedCode;
if (shouldCheckSync && sync != world.SyncHash())
throw new InvalidOperationException("CheckSyncUnchanged: sync-changing code may not run here");
if (checkSyncHash && sync != world.SyncHash())
throw new InvalidOperationException("RunUnsynced: sync-changing code may not run here");
}
}

View File

@@ -12,7 +12,6 @@
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Traits
{

View File

@@ -37,6 +37,19 @@ namespace OpenRA.Traits
[AttributeUsage(AttributeTargets.Field)]
public sealed class LocomotorReferenceAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Field)]
public sealed class NotificationReferenceAttribute : Attribute
{
public readonly string NotificationTypeFieldName = null;
public readonly string NotificationType = null;
public NotificationReferenceAttribute(string type = null, string typeFromField = null)
{
NotificationType = type;
NotificationTypeFieldName = typeFromField;
}
}
[AttributeUsage(AttributeTargets.Field)]
public sealed class SequenceReferenceAttribute : Attribute
{

View File

@@ -18,6 +18,11 @@ using OpenRA.Primitives;
namespace OpenRA.Traits
{
public interface ICreatesFrozenActors
{
void OnVisibilityChanged(FrozenActor frozen);
}
[Desc("Required for FrozenUnderFog to work. Attach this to the player actor.")]
public class FrozenActorLayerInfo : Requires<ShroudInfo>, ITraitInfo
{
@@ -32,11 +37,13 @@ namespace OpenRA.Traits
public readonly PPos[] Footprint;
public readonly WPos CenterPosition;
readonly Actor actor;
readonly ICreatesFrozenActors frozenTrait;
readonly Player viewer;
readonly Shroud shroud;
public Player Owner { get; private set; }
public BitSet<TargetableType> TargetTypes { get; private set; }
public WPos[] TargetablePositions { get; private set; }
public ITooltipInfo TooltipInfo { get; private set; }
public Player TooltipOwner { get; private set; }
@@ -46,7 +53,17 @@ namespace OpenRA.Traits
public DamageState DamageState { get; private set; }
readonly IHealth health;
// The Visible flag is tied directly to the actor visibility under the fog.
// If Visible is true, the actor is made invisible (via FrozenUnderFog/IDefaultVisibility)
// and this FrozenActor is rendered instead.
// The Hidden flag covers the edge case that occurs when the backing actor was last "seen"
// to be cloaked or otherwise not CanBeViewedByPlayer()ed. Setting Visible to true when
// the actor is hidden under the fog would leak the actors position via the tooltips and
// AutoTargetability, and keeping Visible as false would cause the actor to be rendered
// under the fog.
public bool Visible = true;
public bool Hidden = false;
public bool Shrouded { get; private set; }
public bool NeedRenderables { get; set; }
public IRenderable[] Renderables = NoRenderables;
@@ -60,9 +77,10 @@ namespace OpenRA.Traits
int flashTicks;
public FrozenActor(Actor actor, PPos[] footprint, Player viewer, bool startsRevealed)
public FrozenActor(Actor actor, ICreatesFrozenActors frozenTrait, PPos[] footprint, Player viewer, bool startsRevealed)
{
this.actor = actor;
this.frozenTrait = frozenTrait;
this.viewer = viewer;
shroud = viewer.Shroud;
NeedRenderables = startsRevealed;
@@ -101,6 +119,8 @@ namespace OpenRA.Traits
{
Owner = actor.Owner;
TargetTypes = actor.GetEnabledTargetTypes();
TargetablePositions = actor.GetTargetablePositions().ToArray();
Hidden = !actor.CanBeViewedByPlayer(viewer);
if (health != null)
{
@@ -142,9 +162,19 @@ namespace OpenRA.Traits
Shrouded = false;
}
// Force the backing trait to update so other actors can't
// query inconsistent state (both hidden or both visible)
if (Visible != wasVisible)
frozenTrait.OnVisibilityChanged(this);
NeedRenderables |= Visible && !wasVisible;
}
public void Invalidate()
{
Owner = null;
}
public void Flash()
{
flashTicks = 5;
@@ -323,11 +353,28 @@ namespace OpenRA.Traits
return fa;
}
public IEnumerable<FrozenActor> FrozenActorsInRegion(CellRegion region)
public IEnumerable<FrozenActor> FrozenActorsInRegion(CellRegion region, bool onlyVisible = true)
{
var tl = region.TopLeft;
var br = region.BottomRight;
return partitionedFrozenActorIds.InBox(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y)).Select(FromID);
return partitionedFrozenActorIds.InBox(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y))
.Select(FromID)
.Where(fa => fa.IsValid && (!onlyVisible || fa.Visible));
}
public IEnumerable<FrozenActor> FrozenActorsInCircle(World world, WPos origin, WDist r, bool onlyVisible = true)
{
var centerCell = world.Map.CellContaining(origin);
var cellRange = (r.Length + 1023) / 1024;
var tl = centerCell - new CVec(cellRange, cellRange);
var br = centerCell + new CVec(cellRange, cellRange);
// Target ranges are calculated in 2D, so ignore height differences
return partitionedFrozenActorIds.InBox(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y))
.Select(FromID)
.Where(fa => fa.IsValid &&
(!onlyVisible || fa.Visible) &&
(fa.CenterPosition - origin).HorizontalLengthSquared <= r.LengthSquared);
}
}
}

View File

@@ -9,10 +9,6 @@
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using OpenRA.Graphics;
namespace OpenRA.Traits
{
[Desc("This actor is selectable. Defines bounds of selectable area, selection class and selection priority.")]

View File

@@ -19,42 +19,74 @@ namespace OpenRA.Traits
public struct Target
{
public static readonly Target[] None = { };
public static readonly Target Invalid = new Target { type = TargetType.Invalid };
public static readonly Target Invalid = new Target();
TargetType type;
Actor actor;
FrozenActor frozen;
WPos pos;
CPos? cell;
SubCell? subCell;
int generation;
readonly TargetType type;
readonly Actor actor;
readonly FrozenActor frozen;
readonly WPos terrainCenterPosition;
readonly WPos[] terrainPositions;
readonly CPos? cell;
readonly SubCell? subCell;
readonly int generation;
public static Target FromPos(WPos p) { return new Target { pos = p, type = TargetType.Terrain }; }
public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell)
Target(WPos terrainCenterPosition, WPos[] terrainPositions = null)
{
return new Target
{
pos = w.Map.CenterOfSubCell(c, subCell),
cell = c,
subCell = subCell,
type = TargetType.Terrain
};
type = TargetType.Terrain;
this.terrainCenterPosition = terrainCenterPosition;
this.terrainPositions = terrainPositions ?? new[] { terrainCenterPosition };
actor = null;
frozen = null;
cell = null;
subCell = null;
generation = 0;
}
public static Target FromActor(Actor a)
Target(World w, CPos c, SubCell subCell)
{
if (a == null)
return Invalid;
type = TargetType.Terrain;
terrainCenterPosition = w.Map.CenterOfSubCell(c, subCell);
terrainPositions = new[] { terrainCenterPosition };
cell = c;
this.subCell = subCell;
return new Target
{
actor = a,
type = TargetType.Actor,
generation = a.Generation,
};
actor = null;
frozen = null;
generation = 0;
}
public static Target FromFrozenActor(FrozenActor a) { return new Target { frozen = a, type = TargetType.FrozenActor }; }
Target(Actor a)
{
type = TargetType.Actor;
actor = a;
generation = a.Generation;
terrainCenterPosition = WPos.Zero;
terrainPositions = null;
frozen = null;
cell = null;
subCell = null;
}
Target(FrozenActor fa)
{
type = TargetType.FrozenActor;
frozen = fa;
terrainCenterPosition = WPos.Zero;
terrainPositions = null;
actor = null;
cell = null;
subCell = null;
generation = 0;
}
public static Target FromPos(WPos p) { return new Target(p); }
public static Target FromTargetPositions(Target t) { return new Target(t.CenterPosition, t.Positions.ToArray()); }
public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell) { return new Target(w, c, subCell); }
public static Target FromActor(Actor a) { return a != null ? new Target(a) : Invalid; }
public static Target FromFrozenActor(FrozenActor fa) { return new Target(fa); }
public Actor Actor { get { return actor; } }
public FrozenActor FrozenActor { get { return frozen; } }
@@ -80,13 +112,21 @@ namespace OpenRA.Traits
public bool IsValidFor(Actor targeter)
{
if (targeter == null || Type == TargetType.Invalid)
if (targeter == null)
return false;
if (actor != null && !actor.IsTargetableBy(targeter))
return false;
return true;
switch (Type)
{
case TargetType.Actor:
return actor.IsTargetableBy(targeter);
case TargetType.FrozenActor:
return frozen.IsValid && frozen.Visible && !frozen.Hidden;
case TargetType.Invalid:
return false;
default:
case TargetType.Terrain:
return true;
}
}
// Currently all or nothing.
@@ -126,7 +166,7 @@ namespace OpenRA.Traits
case TargetType.FrozenActor:
return frozen.CenterPosition;
case TargetType.Terrain:
return pos;
return terrainCenterPosition;
default:
case TargetType.Invalid:
throw new InvalidOperationException("Attempting to query the position of an invalid Target");
@@ -143,14 +183,11 @@ namespace OpenRA.Traits
switch (Type)
{
case TargetType.Actor:
if (!actor.Targetables.Any(Exts.IsTraitEnabled))
return new[] { actor.CenterPosition };
return actor.GetTargetablePositions();
case TargetType.FrozenActor:
return new[] { frozen.CenterPosition };
return frozen.TargetablePositions;
case TargetType.Terrain:
return new[] { pos };
return terrainPositions;
default:
case TargetType.Invalid:
return NoPositions;
@@ -178,7 +215,7 @@ namespace OpenRA.Traits
return frozen.ToString();
case TargetType.Terrain:
return pos.ToString();
return terrainCenterPosition.ToString();
default:
case TargetType.Invalid:
@@ -191,6 +228,6 @@ namespace OpenRA.Traits
internal Actor SerializableActor { get { return actor; } }
internal CPos? SerializableCell { get { return cell; } }
internal SubCell? SerializableSubCell { get { return subCell; } }
internal WPos SerializablePos { get { return pos; } }
internal WPos SerializablePos { get { return terrainCenterPosition; } }
}
}

View File

@@ -13,7 +13,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using OpenRA.Activities;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Primitives;
@@ -38,6 +38,11 @@ namespace OpenRA.Traits
/// </summary>
public sealed class DamageType { DamageType() { } }
public interface IHealthInfo : ITraitInfo
{
int MaxHP { get; }
}
public interface IHealth
{
DamageState DamageState { get; }
@@ -266,6 +271,13 @@ namespace OpenRA.Traits
IEnumerable<Rectangle> ModifyScreenBounds(Actor self, WorldRenderer wr, IEnumerable<Rectangle> r);
}
[RequireExplicitImplementation]
public interface IProvidesCursorPaletteInfo : ITraitInfoInterface
{
string Palette { get; }
ImmutablePalette ReadPalette(IReadOnlyFileSystem fileSystem);
}
public interface ILoadsPalettes { void LoadPalettes(WorldRenderer wr); }
public interface ILoadsPlayerPalettes { void LoadPlayerPalettes(WorldRenderer wr, string playerName, HSLColor playerColor, bool replaceExisting); }
public interface IPaletteModifier { void AdjustPalette(IReadOnlyDictionary<string, MutablePalette> b); }
@@ -287,7 +299,7 @@ namespace OpenRA.Traits
Pair<CPos, SubCell>[] OccupiedCells();
}
public enum SubCell { Invalid = int.MinValue, Any = int.MinValue / 2, FullCell = 0, First = 1 }
public enum SubCell : byte { Invalid = byte.MaxValue, Any = byte.MaxValue - 1, FullCell = 0, First = 1 }
public interface IPositionableInfo : IOccupySpaceInfo
{
@@ -331,8 +343,6 @@ namespace OpenRA.Traits
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:InterfaceNamesMustBeginWithI", Justification = "Not a real interface, but more like a tag.")]
public interface Requires<T> where T : class, ITraitInfoInterface { }
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:InterfaceNamesMustBeginWithI", Justification = "Not a real interface, but more like a tag.")]
public interface UsesInit<T> : ITraitInfo where T : IActorInit { }
[RequireExplicitImplementation]
public interface INotifySelected { void Selected(Actor self); }
@@ -353,7 +363,9 @@ namespace OpenRA.Traits
public interface IBot
{
void Activate(Player p);
void QueueOrder(Order order);
IBotInfo Info { get; }
Player Player { get; }
}
[RequireExplicitImplementation]
@@ -361,12 +373,16 @@ namespace OpenRA.Traits
[RequireExplicitImplementation]
public interface INotifyBecomingIdle { void OnBecomingIdle(Actor self); }
[RequireExplicitImplementation]
public interface INotifyIdle { void TickIdle(Actor self); }
public interface IRenderAboveWorld { void RenderAboveWorld(Actor self, WorldRenderer wr); }
public interface IRenderShroud { void RenderShroud(Shroud shroud, WorldRenderer wr); }
[RequireExplicitImplementation]
public interface IRenderTerrain { void RenderTerrain(WorldRenderer wr, Viewport viewport); }
public interface IRenderAboveShroud
{
IEnumerable<IRenderable> RenderAboveShroud(Actor self, WorldRenderer wr);

View File

@@ -41,6 +41,7 @@ namespace OpenRA
public Session LobbyInfo { get { return OrderManager.LobbyInfo; } }
public readonly MersenneTwister SharedRandom;
public readonly MersenneTwister LocalRandom;
public readonly IModelCache ModelCache;
public Player[] Players = new Player[0];
@@ -65,7 +66,7 @@ namespace OpenRA
foreach (var t in WorldActor.TraitsImplementing<IGameOver>())
t.GameOver(this);
gameInfo.FinalGameTick = WorldTick;
GameOver();
}
}
@@ -141,7 +142,7 @@ namespace OpenRA
}
}
public readonly Selection Selection = new Selection();
public readonly Selection Selection;
public void CancelInputMode() { OrderGenerator = new UnitOrderGenerator(); }
@@ -169,6 +170,7 @@ namespace OpenRA
Map = map;
Timestep = orderManager.LobbyInfo.GlobalSettings.Timestep;
SharedRandom = new MersenneTwister(orderManager.LobbyInfo.GlobalSettings.RandomSeed);
LocalRandom = new MersenneTwister();
ModelCache = modData.ModelSequenceLoader.CacheModels(map, modData, map.Rules.ModelSequences);
@@ -177,6 +179,8 @@ namespace OpenRA
ActorMap = WorldActor.Trait<IActorMap>();
ScreenMap = WorldActor.Trait<ScreenMap>();
Selection = new Selection(WorldActor.TraitsImplementing<INotifySelection>());
// Add players
foreach (var cmp in WorldActor.TraitsImplementing<ICreatePlayers>())
cmp.CreatePlayers(this);
@@ -355,12 +359,7 @@ namespace OpenRA
{
WorldTick++;
using (new PerfSample("tick_idle"))
foreach (var ni in ActorsWithTrait<INotifyIdle>())
if (ni.Actor.IsIdle)
ni.Trait.TickIdle(ni.Actor);
using (new PerfSample("tick_activities"))
using (new PerfSample("tick_actors"))
foreach (var a in actors.Values)
a.Tick();

View File

@@ -9,8 +9,9 @@
*/
#endregion
using System;
using System.Drawing;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Cnc.Traits;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
@@ -20,32 +21,49 @@ namespace OpenRA.Mods.Cnc.Activities
{
class Infiltrate : Enter
{
readonly Actor target;
readonly Infiltrates infiltrates;
readonly INotifyInfiltration[] notifiers;
Actor enterActor;
public Infiltrate(Actor self, Actor target, Infiltrates infiltrate)
: base(self, target, infiltrate.Info.EnterBehaviour)
public Infiltrate(Actor self, Target target, Infiltrates infiltrates)
: base(self, target, Color.Red)
{
this.target = target;
infiltrates = infiltrate;
this.infiltrates = infiltrates;
notifiers = self.TraitsImplementing<INotifyInfiltration>().ToArray();
}
protected override void OnInside(Actor self)
protected override void TickInner(Actor self, Target target, bool targetIsDeadOrHiddenActor)
{
if (target.IsDead)
return;
if (infiltrates.IsTraitDisabled)
Cancel(self, true);
}
var stance = self.Owner.Stances[target.Owner];
if (!infiltrates.Info.ValidStances.HasStance(stance))
protected override bool TryStartEnter(Actor self, Actor targetActor)
{
// Make sure we can still demolish the target before entering
// (but not before, because this may stop the actor in the middle of nowhere)
if (!infiltrates.CanInfiltrateTarget(self, Target.FromActor(targetActor)))
{
Cancel(self, true);
return false;
}
enterActor = targetActor;
return true;
}
protected override void OnEnterComplete(Actor self, Actor targetActor)
{
// Make sure the target hasn't changed while entering
// OnEnterComplete is only called if targetActor is alive
if (targetActor != enterActor || !infiltrates.CanInfiltrateTarget(self, Target.FromActor(targetActor)))
return;
foreach (var ini in notifiers)
ini.Infiltrating(self);
foreach (var t in target.TraitsImplementing<INotifyInfiltrated>())
t.Infiltrated(target, self, infiltrates.Info.Types);
foreach (var t in targetActor.TraitsImplementing<INotifyInfiltrated>())
t.Infiltrated(targetActor, self, infiltrates.Info.Types);
var exp = self.Owner.PlayerActor.TraitOrDefault<PlayerExperience>();
if (exp != null)
@@ -54,14 +72,11 @@ namespace OpenRA.Mods.Cnc.Activities
if (!string.IsNullOrEmpty(infiltrates.Info.Notification))
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech",
infiltrates.Info.Notification, self.Owner.Faction.InternalName);
}
public override Activity Tick(Actor self)
{
if (infiltrates.IsTraitDisabled)
Cancel(self);
return base.Tick(self);
if (infiltrates.Info.EnterBehaviour == EnterBehaviour.Dispose)
self.Dispose();
else if (infiltrates.Info.EnterBehaviour == EnterBehaviour.Suicide)
self.Kill(self);
}
}
}

View File

@@ -9,7 +9,6 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Cnc.Traits;
@@ -27,7 +26,7 @@ namespace OpenRA.Mods.Cnc.Activities
readonly MinelayerInfo info;
readonly AmmoPool[] ammoPools;
readonly IMove movement;
readonly HashSet<string> rearmBuildings;
readonly RearmableInfo rearmableInfo;
public LayMines(Actor self)
{
@@ -35,7 +34,7 @@ namespace OpenRA.Mods.Cnc.Activities
info = self.Info.TraitInfo<MinelayerInfo>();
ammoPools = self.TraitsImplementing<AmmoPool>().ToArray();
movement = self.Trait<IMove>();
rearmBuildings = info.RearmBuildings;
rearmableInfo = self.Info.TraitInfoOrDefault<RearmableInfo>();
}
public override Activity Tick(Actor self)
@@ -43,21 +42,21 @@ namespace OpenRA.Mods.Cnc.Activities
if (IsCanceled)
return NextActivity;
if (ammoPools != null && ammoPools.Any(p => p.Info.Name == info.AmmoPoolName && !p.HasAmmo()))
if (rearmableInfo != null && ammoPools.Any(p => p.Info.Name == info.AmmoPoolName && !p.HasAmmo()))
{
// Rearm (and possibly repair) at rearm building, then back out here to refill the minefield some more
var rearmTarget = self.World.Actors.Where(a => self.Owner.Stances[a.Owner] == Stance.Ally
&& rearmBuildings.Contains(a.Info.Name))
&& rearmableInfo.RearmActors.Contains(a.Info.Name))
.ClosestTo(self);
if (rearmTarget == null)
return new Wait(20);
// Add a CloseEnough range of 512 to the Repair activity in order to ensure that we're at the host actor
// Add a CloseEnough range of 512 to the Rearm/Repair activities in order to ensure that we're at the host actor
return ActivityUtils.SequenceActivities(
new MoveAdjacentTo(self, Target.FromActor(rearmTarget)),
movement.MoveTo(self.World.Map.CellContaining(rearmTarget.CenterPosition), rearmTarget),
new Rearm(self),
new Rearm(self, rearmTarget, new WDist(512)),
new Repair(self, rearmTarget, new WDist(512)),
this);
}

View File

@@ -13,70 +13,126 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.GameRules;
using OpenRA.Mods.Cnc.Traits;
using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.Common.Traits.Render;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Activities
{
class Leap : Activity
public class Leap : Activity
{
readonly Mobile mobile;
readonly WeaponInfo weapon;
readonly int length;
readonly Mobile targetMobile;
readonly int speed;
readonly AttackLeap attack;
readonly EdibleByLeap edible;
readonly Target target;
WPos from;
WPos to;
int ticks;
WAngle angle;
BitSet<DamageType> damageTypes;
CPos destinationCell;
SubCell destinationSubCell = SubCell.Any;
WPos destination, origin;
int length;
bool canceled = false;
bool jumpComplete = false;
int ticks = 0;
WPos targetPosition;
public Leap(Actor self, Actor target, Armament a, WDist speed, WAngle angle, BitSet<DamageType> damageTypes)
public Leap(Actor self, Target target, Mobile mobile, Mobile targetMobile, int speed, AttackLeap attack, EdibleByLeap edible)
{
var targetMobile = target.TraitOrDefault<Mobile>();
if (targetMobile == null)
throw new InvalidOperationException("Leap requires a target actor with the Mobile trait");
this.mobile = mobile;
this.targetMobile = targetMobile;
this.attack = attack;
this.target = target;
this.edible = edible;
this.speed = speed;
}
this.weapon = a.Weapon;
this.angle = angle;
this.damageTypes = damageTypes;
mobile = self.Trait<Mobile>();
mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, targetMobile.FromCell, targetMobile.FromSubCell);
mobile.IsMoving = true;
protected override void OnFirstRun(Actor self)
{
destinationCell = target.Actor.Location;
if (targetMobile != null)
destinationSubCell = targetMobile.ToSubCell;
from = self.CenterPosition;
to = self.World.Map.CenterOfSubCell(targetMobile.FromCell, targetMobile.FromSubCell);
length = Math.Max((to - from).Length / speed.Length, 1);
origin = self.CenterPosition;
destination = self.World.Map.CenterOfSubCell(destinationCell, destinationSubCell);
length = Math.Max((origin - destination).Length / speed, 1);
// HACK: why isn't this using the interface?
self.Trait<WithInfantryBody>().Attacking(self, Target.FromActor(target), a);
// First check if we are still allowed to leap
// We need an extra boolean as using Cancel() in OnFirstRun doesn't work
canceled = !edible.GetLeapAtBy(self) || target.Type != TargetType.Actor;
if (weapon.Report != null && weapon.Report.Any())
Game.Sound.Play(SoundType.World, weapon.Report.Random(self.World.SharedRandom), self.CenterPosition);
IsInterruptible = false;
if (canceled)
return;
targetPosition = target.CenterPosition;
attack.GrantLeapCondition(self);
}
public override Activity Tick(Actor self)
{
if (ticks == 0 && IsCanceled)
if (canceled)
return NextActivity;
mobile.SetVisualPosition(self, WPos.LerpQuadratic(from, to, angle, ++ticks, length));
if (ticks >= length)
// Correct the visual position after we jumped
if (jumpComplete)
{
if (ChildActivity == null)
return NextActivity;
ChildActivity = ActivityUtils.RunActivity(self, ChildActivity);
return this;
}
if (target.Type != TargetType.Invalid)
targetPosition = target.CenterPosition;
var position = length > 1 ? WPos.Lerp(origin, targetPosition, ticks, length - 1) : targetPosition;
mobile.SetVisualPosition(self, position);
// We are at the destination
if (++ticks >= length)
{
mobile.SetLocation(mobile.ToCell, mobile.ToSubCell, mobile.ToCell, mobile.ToSubCell);
mobile.FinishedMoving(self);
mobile.IsMoving = false;
self.World.ActorMap.GetActorsAt(mobile.ToCell, mobile.ToSubCell)
.Except(new[] { self }).Where(t => weapon.IsValidAgainst(t, self))
.Do(t => t.Kill(self, damageTypes));
// Revoke the run condition
attack.IsAiming = false;
return NextActivity;
// Move to the correct subcells, if our target actor uses subcells
// (This does not update the visual position!)
mobile.SetLocation(destinationCell, destinationSubCell, destinationCell, destinationSubCell);
// Revoke the condition before attacking, as it is usually used to pause the attack trait
attack.RevokeLeapCondition(self);
attack.DoAttack(self, target);
jumpComplete = true;
QueueChild(mobile.VisualMove(self, position, self.World.Map.CenterOfSubCell(destinationCell, destinationSubCell)));
return this;
}
mobile.IsMoving = true;
return this;
}
protected override void OnLastRun(Actor self)
{
attack.RevokeLeapCondition(self);
base.OnLastRun(self);
}
protected override void OnActorDispose(Actor self)
{
attack.RevokeLeapCondition(self);
base.OnActorDispose(self);
}
public override IEnumerable<Target> GetTargets(Actor self)
{
yield return Target.FromPos(ticks < length / 2 ? origin : destination);
}
}
}

View File

@@ -0,0 +1,146 @@
#region Copyright & License Information
/*
* Copyright 2007-2018 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Drawing;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Cnc.Traits;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Activities
{
public class LeapAttack : Activity
{
readonly AttackLeapInfo info;
readonly AttackLeap attack;
readonly Mobile mobile;
readonly bool allowMovement;
Target target;
Target lastVisibleTarget;
bool useLastVisibleTarget;
WDist lastVisibleMinRange;
WDist lastVisibleMaxRange;
public LeapAttack(Actor self, Target target, bool allowMovement, AttackLeap attack, AttackLeapInfo info)
{
this.target = target;
this.info = info;
this.attack = attack;
this.allowMovement = allowMovement;
mobile = self.Trait<Mobile>();
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
// Moving to any position (even if quite stale) is still better than immediately giving up
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
{
lastVisibleTarget = Target.FromPos(target.CenterPosition);
lastVisibleMinRange = attack.GetMinimumRangeVersusTarget(target);
lastVisibleMaxRange = attack.GetMaximumRangeVersusTarget(target);
}
}
protected override void OnFirstRun(Actor self)
{
attack.IsAiming = true;
}
public override Activity Tick(Actor self)
{
// Run this even if the target became invalid to avoid visual glitches
if (ChildActivity != null)
{
ChildActivity = ActivityUtils.RunActivity(self, ChildActivity);
return this;
}
if (IsCanceled)
return NextActivity;
bool targetIsHiddenActor;
target = target.Recalculate(self.Owner, out targetIsHiddenActor);
if (!targetIsHiddenActor && target.Type == TargetType.Actor)
{
lastVisibleTarget = Target.FromTargetPositions(target);
lastVisibleMinRange = attack.GetMinimumRangeVersusTarget(target);
lastVisibleMaxRange = attack.GetMaximumRangeVersusTarget(target);
}
var oldUseLastVisibleTarget = useLastVisibleTarget;
useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);
// Update target lines if required
if (useLastVisibleTarget != oldUseLastVisibleTarget)
self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, Color.Red, false);
// Target is hidden or dead, and we don't have a fallback position to move towards
if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self))
return NextActivity;
var pos = self.CenterPosition;
var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target;
if (!checkTarget.IsInRange(pos, lastVisibleMaxRange) || checkTarget.IsInRange(pos, lastVisibleMinRange))
{
if (!allowMovement || lastVisibleMaxRange == WDist.Zero || lastVisibleMaxRange < lastVisibleMinRange)
return NextActivity;
QueueChild(mobile.MoveWithinRange(target, lastVisibleMinRange, lastVisibleMaxRange, checkTarget.CenterPosition, Color.Red));
return this;
}
// Ready to leap, but target isn't visible
if (targetIsHiddenActor || target.Type != TargetType.Actor)
return NextActivity;
// Target is not valid
if (!target.IsValidFor(self) || !attack.HasAnyValidWeapons(target))
return NextActivity;
var edible = target.Actor.TraitOrDefault<EdibleByLeap>();
if (edible == null || !edible.CanLeap(self))
return NextActivity;
// Can't leap yet
if (attack.Armaments.All(a => a.IsReloading))
return this;
// Use CenterOfSubCell with ToSubCell instead of target.Centerposition
// to avoid continuous facing adjustments as the target moves
var targetMobile = target.Actor.TraitOrDefault<Mobile>();
var targetSubcell = targetMobile != null ? targetMobile.ToSubCell : SubCell.Any;
var destination = self.World.Map.CenterOfSubCell(target.Actor.Location, targetSubcell);
var origin = self.World.Map.CenterOfSubCell(self.Location, mobile.FromSubCell);
var desiredFacing = (destination - origin).Yaw.Facing;
if (mobile.Facing != desiredFacing)
{
QueueChild(new Turn(self, desiredFacing));
return this;
}
QueueChild(new Leap(self, target, mobile, targetMobile, info.Speed.Length, attack, edible));
// Re-queue the child activities to kill the target if it didn't die in one go
return this;
}
protected override void OnLastRun(Actor self)
{
attack.IsAiming = false;
}
}
}

View File

@@ -15,22 +15,25 @@ using OpenRA.Activities;
using OpenRA.Mods.Cnc.Traits;
using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.Common.Traits.Render;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Activities
{
public interface IPreventsTeleport { bool PreventsTeleport(Actor self); }
public class Teleport : Activity
{
readonly Actor teleporter;
readonly int? maximumDistance;
readonly bool killOnFailure;
readonly BitSet<DamageType> killDamageTypes;
CPos destination;
bool killCargo;
bool screenFlash;
string sound;
public Teleport(Actor teleporter, CPos destination, int? maximumDistance, bool killCargo, bool screenFlash, string sound)
public Teleport(Actor teleporter, CPos destination, int? maximumDistance,
bool killCargo, bool screenFlash, string sound, bool interruptable = true,
bool killOnFailure = false, BitSet<DamageType> killDamageTypes = default(BitSet<DamageType>))
{
var max = teleporter.World.Map.Grid.MaximumTileSearchRange;
if (maximumDistance > max)
@@ -42,21 +45,32 @@ namespace OpenRA.Mods.Cnc.Activities
this.killCargo = killCargo;
this.screenFlash = screenFlash;
this.sound = sound;
this.killOnFailure = killOnFailure;
this.killDamageTypes = killDamageTypes;
if (!interruptable)
IsInterruptible = false;
}
public override Activity Tick(Actor self)
{
var pc = self.TraitOrDefault<PortableChrono>();
if (teleporter == self && pc != null && !pc.CanTeleport)
return NextActivity;
{
if (killOnFailure)
self.Kill(teleporter, killDamageTypes);
foreach (var condition in self.TraitsImplementing<IPreventsTeleport>())
if (condition.PreventsTeleport(self))
return NextActivity;
return NextActivity;
}
var bestCell = ChooseBestDestinationCell(self, destination);
if (bestCell == null)
{
if (killOnFailure)
self.Kill(teleporter, killDamageTypes);
return NextActivity;
}
destination = bestCell.Value;

View File

@@ -12,7 +12,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using OpenRA.Graphics;
using OpenRA.Mods.Common.LoadScreens;
using OpenRA.Widgets;

View File

@@ -9,12 +9,9 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Scripting;
namespace OpenRA.Mods.Cnc.Effects
{

View File

@@ -53,7 +53,7 @@ namespace OpenRA.Mods.Cnc.Effects
visibilityModifiers = actor.TraitsImplementing<IVisibilityModifier>().ToArray();
dotStates = new PlayerDictionary<DotState>(actor.World,
p => new DotState(actor, p.PlayerActor.Trait<GpsWatcher>(), p.PlayerActor.TraitOrDefault<FrozenActorLayer>()));
p => new DotState(actor, p.PlayerActor.Trait<GpsWatcher>(), p.FrozenActorLayer));
}
bool ShouldRender(DotState state, Player toPlayer)

View File

@@ -7,7 +7,6 @@
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;

View File

@@ -7,7 +7,6 @@
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;

View File

@@ -13,7 +13,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.FileSystem;
using OpenRA.Mods.Cnc.FileFormats;
using OpenRA.Primitives;

View File

@@ -39,7 +39,6 @@
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<OutputPath>bin\Debug\</OutputPath>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>5</LangVersion>
</PropertyGroup>
@@ -51,7 +50,6 @@
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
@@ -64,9 +62,12 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Activities\LeapAttack.cs" />
<Compile Include="CncLoadScreen.cs" />
<Compile Include="Traits\EdibleByLeap.cs" />
<Compile Include="Traits\Attack\AttackPopupTurreted.cs" />
<Compile Include="Traits\Buildings\ProductionAirdrop.cs" />
<Compile Include="Traits\Infiltration\InfiltrateForTransform.cs" />
<Compile Include="Traits\Render\WithGunboatBody.cs" />
<Compile Include="Traits\Render\WithEmbeddedTurretSpriteBody.cs" />
<Compile Include="Traits\Render\WithCargo.cs" />

View File

@@ -19,7 +19,10 @@ namespace OpenRA.Mods.Cnc.Traits
public class PlaceSimpleBeaconInfo : ITraitInfo
{
public readonly int Duration = 30 * 25;
public readonly string NotificationType = "Sounds";
[NotificationReference(typeFromField: "NotificationType")]
public readonly string Notification = "Beacon";
public readonly bool IsPlayerPalette = false;

View File

@@ -11,7 +11,6 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Traits;

View File

@@ -10,7 +10,6 @@
#endregion
using System.Collections.Generic;
using OpenRA.Effects;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Graphics;

View File

@@ -17,9 +17,9 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Scripting
{
[ScriptPropertyGroup("Support Powers")]
public class ChronsphereProperties : ScriptActorProperties, Requires<ChronoshiftPowerInfo>
public class ChronosphereProperties : ScriptActorProperties, Requires<ChronoshiftPowerInfo>
{
public ChronsphereProperties(ScriptContext context, Actor self)
public ChronosphereProperties(ScriptContext context, Actor self)
: base(context, self) { }
[Desc("Chronoshift a group of actors. A duration of 0 will teleport the actors permanently.")]
@@ -37,7 +37,9 @@ namespace OpenRA.Mods.Cnc.Scripting
kv.Key.WrappedClrType().Name, kv.Value.WrappedClrType().Name));
}
var cs = actor.TraitOrDefault<Chronoshiftable>();
var cs = actor.TraitsImplementing<Chronoshiftable>()
.FirstEnabledTraitOrDefault();
if (cs != null && cs.CanChronoshiftTo(actor, cell))
cs.Teleport(actor, cell, duration, killCargo, Self);
}

View File

@@ -9,7 +9,6 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using Eluant;
using OpenRA.Mods.Cnc.Activities;
@@ -38,7 +37,7 @@ namespace OpenRA.Mods.Cnc.Scripting
if (infiltrates == null)
throw new LuaException("{0} tried to infiltrate invalid target {1}!".F(Self, target));
Self.QueueActivity(new Infiltrate(Self, target, infiltrates));
Self.QueueActivity(new Infiltrate(Self, Target.FromActor(target), infiltrates));
}
}
}

View File

@@ -5,15 +5,12 @@
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* Information, see COPYING.
* information, see COPYING.
*/
#endregion
using System.Linq;
using OpenRA.Mods.Cnc.Effects;
using OpenRA.Mods.Cnc.Traits;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Scripting;
using OpenRA.Traits;

View File

@@ -12,6 +12,7 @@
using System.Drawing;
using System.IO;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Mods.Cnc.SpriteLoaders
{
@@ -182,8 +183,9 @@ namespace OpenRA.Mods.Cnc.SpriteLoaders
return tiles;
}
public bool TryParseSprite(Stream s, out ISpriteFrame[] frames)
public bool TryParseSprite(Stream s, out ISpriteFrame[] frames, out TypeDictionary metadata)
{
metadata = null;
if (!IsTmpTS(s))
{
frames = null;

View File

@@ -9,53 +9,71 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Cnc.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
[Desc("Dogs use this attack model.")]
class AttackLeapInfo : AttackFrontalInfo
[Desc("Move onto the target then execute the attack.")]
public class AttackLeapInfo : AttackFrontalInfo, Requires<MobileInfo>
{
[Desc("Leap speed (in units/tick).")]
[Desc("Leap speed (in WDist units/tick).")]
public readonly WDist Speed = new WDist(426);
public readonly WAngle Angle = WAngle.FromDegrees(20);
[Desc("Types of damage that this trait causes. Leave empty for no damage types.")]
public readonly BitSet<DamageType> DamageTypes = default(BitSet<DamageType>);
[Desc("Conditions that last from start of the leap until the attack.")]
[GrantedConditionReference]
public readonly string LeapCondition = "attacking";
public override object Create(ActorInitializer init) { return new AttackLeap(init.Self, this); }
}
class AttackLeap : AttackFrontal
public class AttackLeap : AttackFrontal
{
readonly AttackLeapInfo info;
ConditionManager conditionManager;
int leapToken = ConditionManager.InvalidConditionToken;
public AttackLeap(Actor self, AttackLeapInfo info)
: base(self, info)
{
this.info = info;
}
public override void DoAttack(Actor self, Target target, IEnumerable<Armament> armaments = null)
protected override void Created(Actor self)
{
if (target.Type != TargetType.Actor || !CanAttack(self, target))
return;
conditionManager = self.TraitOrDefault<ConditionManager>();
base.Created(self);
}
var a = ChooseArmamentsForTarget(target, true).FirstOrDefault();
if (a == null)
return;
protected override bool CanAttack(Actor self, Target target)
{
if (target.Type != TargetType.Actor)
return false;
if (!target.IsInRange(self.CenterPosition, a.MaxRange()))
return;
if (self.Location == target.Actor.Location && HasAnyValidWeapons(target))
return true;
self.CancelActivity();
self.QueueActivity(new Leap(self, target.Actor, a, info.Speed, info.Angle, info.DamageTypes));
return base.CanAttack(self, target);
}
public void GrantLeapCondition(Actor self)
{
if (conditionManager != null && !string.IsNullOrEmpty(info.LeapCondition))
leapToken = conditionManager.GrantCondition(self, info.LeapCondition);
}
public void RevokeLeapCondition(Actor self)
{
if (leapToken != ConditionManager.InvalidConditionToken)
leapToken = conditionManager.RevokeCondition(self, leapToken);
}
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove, bool forceAttack)
{
return new LeapAttack(self, newTarget, allowMove, this, info);
}
}
}

View File

@@ -43,7 +43,7 @@ namespace OpenRA.Mods.Cnc.Traits
public override object Create(ActorInitializer init) { return new AttackPopupTurreted(init, this); }
}
class AttackPopupTurreted : AttackTurreted, INotifyBuildComplete, INotifyIdle, IDamageModifier
class AttackPopupTurreted : AttackTurreted, INotifyIdle, IDamageModifier
{
enum PopupState { Open, Rotating, Transitioning, Closed }
@@ -64,9 +64,22 @@ namespace OpenRA.Mods.Cnc.Traits
skippedMakeAnimation = init.Contains<SkipMakeAnimsInit>();
}
protected override void Created(Actor self)
{
base.Created(self);
// Map placed actors are created in the closed state
if (skippedMakeAnimation)
{
state = PopupState.Closed;
wsb.PlayCustomAnimationRepeating(self, info.ClosedIdleSequence);
turret.DesiredFacing = null;
}
}
protected override bool CanAttack(Actor self, Target target)
{
if (state == PopupState.Transitioning || !building.BuildComplete)
if (state == PopupState.Transitioning)
return false;
if (!base.CanAttack(self, target))
@@ -106,16 +119,6 @@ namespace OpenRA.Mods.Cnc.Traits
}
}
void INotifyBuildComplete.BuildingComplete(Actor self)
{
if (skippedMakeAnimation)
{
state = PopupState.Closed;
wsb.PlayCustomAnimationRepeating(self, info.ClosedIdleSequence);
turret.DesiredFacing = null;
}
}
int IDamageModifier.GetDamageModifier(Actor attacker, Damage damage)
{
return state == PopupState.Closed ? info.ClosedDamageMultiplier : 100;

View File

@@ -59,10 +59,10 @@ namespace OpenRA.Mods.Cnc.Traits
{
// Check that AttackTDGunboatTurreted hasn't cancelled the target by modifying attack.Target
// Having both this and AttackTDGunboatTurreted modify that field is a horrible hack.
if (hasTicked && attack.Target.Type == TargetType.Invalid)
if (hasTicked && attack.requestedTarget.Type == TargetType.Invalid)
return NextActivity;
attack.Target = target;
attack.requestedTarget = target;
hasTicked = true;
}

View File

@@ -9,7 +9,7 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
@@ -22,7 +22,7 @@ namespace OpenRA.Mods.Cnc.Traits
{
[FieldLoader.Require]
[Desc("Uses the \"Cloneable\" trait to determine whether or not we should clone a produced unit.")]
public readonly HashSet<string> CloneableTypes = new HashSet<string>();
public readonly BitSet<CloneableType> CloneableTypes = default(BitSet<CloneableType>);
public object Create(ActorInitializer init) { return new ClonesProducedUnits(init, this); }
}
@@ -30,17 +30,15 @@ namespace OpenRA.Mods.Cnc.Traits
public class ClonesProducedUnits : INotifyOtherProduction
{
readonly ClonesProducedUnitsInfo info;
readonly Production production;
readonly string faction;
readonly Production[] productionTraits;
public ClonesProducedUnits(ActorInitializer init, ClonesProducedUnitsInfo info)
{
this.info = info;
production = init.Self.Trait<Production>();
faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName;
productionTraits = init.Self.TraitsImplementing<Production>().ToArray();
}
public void UnitProducedByOther(Actor self, Actor producer, Actor produced, string productionType)
public void UnitProducedByOther(Actor self, Actor producer, Actor produced, string productionType, TypeDictionary init)
{
// No recursive cloning!
if (producer.Owner != self.Owner || producer.Info.HasTraitInfo<ClonesProducedUnitsInfo>())
@@ -50,13 +48,23 @@ namespace OpenRA.Mods.Cnc.Traits
if (ci == null || !info.CloneableTypes.Overlaps(ci.Types))
return;
var inits = new TypeDictionary
{
new OwnerInit(self.Owner),
new FactionInit(BuildableInfo.GetInitialFaction(produced.Info, faction))
};
var factionInit = init.GetOrDefault<FactionInit>();
production.Produce(self, produced.Info, productionType, inits);
// Stop as soon as one production trait successfully produced
foreach (var p in productionTraits)
{
if (!string.IsNullOrEmpty(productionType) && !p.Info.Produces.Contains(productionType))
continue;
var inits = new TypeDictionary
{
new OwnerInit(self.Owner),
factionInit ?? new FactionInit(BuildableInfo.GetInitialFaction(produced.Info, p.Faction))
};
if (p.Produce(self, produced.Info, productionType, inits))
return;
}
}
}
}

View File

@@ -9,7 +9,6 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common;
@@ -23,7 +22,9 @@ namespace OpenRA.Mods.Cnc.Traits
[Desc("Deliver the unit in production via skylift.")]
public class ProductionAirdropInfo : ProductionInfo
{
[NotificationReference("Speech")]
public readonly string ReadyAudio = "Reinforce";
[Desc("Cargo aircraft used for delivery. Must have the `Aircraft` trait.")]
[ActorReference(typeof(AircraftInfo))] public readonly string ActorType = "c17";
@@ -32,18 +33,15 @@ namespace OpenRA.Mods.Cnc.Traits
class ProductionAirdrop : Production
{
readonly ProductionAirdropInfo info;
public ProductionAirdrop(ActorInitializer init, ProductionAirdropInfo info)
: base(init, info)
{
this.info = info;
}
: base(init, info) { }
public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)
{
if (IsTraitDisabled || IsTraitPaused)
return false;
var info = (ProductionAirdropInfo)Info;
var owner = self.Owner;
var aircraftInfo = self.World.Map.Rules.Actors[info.ActorType].TraitInfo<AircraftInfo>();

View File

@@ -11,6 +11,7 @@
using System.Drawing;
using OpenRA.Mods.Cnc.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
@@ -18,12 +19,13 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
[Desc("Can be teleported via Chronoshift power.")]
public class ChronoshiftableInfo : ITraitInfo
public class ChronoshiftableInfo : ConditionalTraitInfo
{
[Desc("Should the actor die instead of being teleported?")]
public readonly bool ExplodeInstead = false;
[Desc("Types of damage that this trait causes to self when 'ExplodeInstead' is true. Leave empty for no damage types.")]
[Desc("Types of damage that this trait causes to self when 'ExplodeInstead' is true",
"or the return-to-origin is blocked. Leave empty for no damage types.")]
public readonly BitSet<DamageType> DamageTypes = default(BitSet<DamageType>);
public readonly string ChronoshiftSound = "chrono2.aud";
@@ -34,12 +36,12 @@ namespace OpenRA.Mods.Cnc.Traits
[Desc("The color the bar of the 'return-to-origin' logic has.")]
public readonly Color TimeBarColor = Color.White;
public object Create(ActorInitializer init) { return new Chronoshiftable(init, this); }
public override object Create(ActorInitializer init) { return new Chronoshiftable(init, this); }
}
public class Chronoshiftable : ITick, ISync, ISelectionBar, IDeathActorInitModifier, ITransformActorInitModifier, INotifyCreated
public class Chronoshiftable : ConditionalTrait<ChronoshiftableInfo>, ITick, ISync, ISelectionBar,
IDeathActorInitModifier, ITransformActorInitModifier, INotifyCreated
{
readonly ChronoshiftableInfo info;
readonly Actor self;
Actor chronosphere;
bool killCargo;
@@ -51,8 +53,8 @@ namespace OpenRA.Mods.Cnc.Traits
[Sync] public int ReturnTicks = 0;
public Chronoshiftable(ActorInitializer init, ChronoshiftableInfo info)
: base(info)
{
this.info = info;
self = init.Self;
if (init.Contains<ChronoshiftReturnInit>())
@@ -70,14 +72,29 @@ namespace OpenRA.Mods.Cnc.Traits
void ITick.Tick(Actor self)
{
if (!info.ReturnToOrigin || ReturnTicks <= 0)
if (IsTraitDisabled || !Info.ReturnToOrigin || ReturnTicks <= 0)
return;
// Return to original location
if (--ReturnTicks == 0)
{
self.CancelActivity();
self.QueueActivity(new Teleport(chronosphere, Origin, null, killCargo, true, info.ChronoshiftSound));
// The Move activity is not immediately cancelled, which, combined
// with Activity.Cancel discarding NextActivity without checking the
// IsInterruptable flag, means that a well timed order can cancel the
// Teleport activity queued below - an exploit / cheat of the return mechanic.
// The Teleport activity queued below is guaranteed to either complete
// (force-resetting the actor to the middle of the target cell) or kill
// the actor. It is therefore safe to force-erase the Move activity to
// work around the cancellation bug.
// HACK: this is manipulating private internal actor state
if (self.CurrentActivity is Move)
typeof(Actor).GetProperty("CurrentActivity").SetValue(self, null);
// The actor is killed using Info.DamageTypes if the teleport fails
self.QueueActivity(new Teleport(chronosphere, Origin, null, true, killCargo, Info.ChronoshiftSound,
false, true, Info.DamageTypes));
}
}
@@ -90,19 +107,22 @@ namespace OpenRA.Mods.Cnc.Traits
public virtual bool CanChronoshiftTo(Actor self, CPos targetLocation)
{
// TODO: Allow enemy units to be chronoshifted into bad terrain to kill them
return iPositionable != null && iPositionable.CanEnterCell(targetLocation);
return !IsTraitDisabled && iPositionable != null && iPositionable.CanEnterCell(targetLocation);
}
public virtual bool Teleport(Actor self, CPos targetLocation, int duration, bool killCargo, Actor chronosphere)
{
if (IsTraitDisabled)
return false;
// Some things appear chronoshiftable, but instead they just die.
if (info.ExplodeInstead)
if (Info.ExplodeInstead)
{
self.World.AddFrameEndTask(w =>
{
// Damage is inflicted by the chronosphere
if (!self.Disposed)
self.Kill(chronosphere, info.DamageTypes);
self.Kill(chronosphere, Info.DamageTypes);
});
return true;
}
@@ -122,7 +142,7 @@ namespace OpenRA.Mods.Cnc.Traits
// Set up the teleport
self.CancelActivity();
self.QueueActivity(new Teleport(chronosphere, targetLocation, null, killCargo, true, info.ChronoshiftSound));
self.QueueActivity(new Teleport(chronosphere, targetLocation, null, killCargo, true, Info.ChronoshiftSound));
return true;
}
@@ -130,7 +150,7 @@ namespace OpenRA.Mods.Cnc.Traits
// Show the remaining time as a bar
float ISelectionBar.GetValue()
{
if (!info.ReturnToOrigin)
if (IsTraitDisabled || !Info.ReturnToOrigin)
return 0f;
// Otherwise an empty bar is rendered all the time
@@ -140,12 +160,12 @@ namespace OpenRA.Mods.Cnc.Traits
return (float)ReturnTicks / duration;
}
Color ISelectionBar.GetColor() { return info.TimeBarColor; }
Color ISelectionBar.GetColor() { return Info.TimeBarColor; }
bool ISelectionBar.DisplayWhenEmpty { get { return false; } }
void ModifyActorInit(TypeDictionary init)
{
if (!info.ReturnToOrigin || ReturnTicks <= 0)
if (IsTraitDisabled || !Info.ReturnToOrigin || ReturnTicks <= 0)
return;
init.Add(new ChronoshiftOriginInit(Origin));

View File

@@ -9,17 +9,20 @@
*/
#endregion
using System.Collections.Generic;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
// Type tag for CloneableTypes
public class CloneableType { }
[Desc("Actors with the \"ClonesProducedUnits\" trait will produce a free duplicate of me.")]
public class CloneableInfo : TraitInfo<Cloneable>
{
[FieldLoader.Require]
[Desc("This unit's cloneable type is:")]
public readonly HashSet<string> Types = new HashSet<string>();
public readonly BitSet<CloneableType> Types = default(BitSet<CloneableType>);
}
public class Cloneable { }

View File

@@ -12,19 +12,19 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.GameRules;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.Common.Traits.Render;
using OpenRA.Primitives;
using OpenRA.Support;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
[Desc("Implements the special case handling for the Chronoshiftable return on a construction yard.",
"Actors that are actively (un)deploying will be returned to the origin as the original actor.",
"If ReturnOriginalActorOnCondition evaluates true and the actor is not being sold then OriginalActor will be returned to the origin.",
"Otherwise, a vortex animation is played and damage is dealt each tick, ignoring modifiers.")]
public class ConyardChronoReturnInfo : ITraitInfo, Requires<HealthInfo>, Requires<WithSpriteBodyInfo>
public class ConyardChronoReturnInfo : IObservesVariablesInfo, Requires<HealthInfo>, Requires<WithSpriteBodyInfo>
{
[Desc("Sequence name with the baked-in vortex animation"), SequenceReference]
public readonly string Sequence = "pdox";
@@ -42,7 +42,12 @@ namespace OpenRA.Mods.Cnc.Traits
[Desc("Apply the damage using these damagetypes.")]
public readonly BitSet<DamageType> DamageTypes = default(BitSet<DamageType>);
[Desc("Actor to transform into when the timer expires during (un)deploy."), ActorReference]
[ConsumedConditionReference]
[Desc("Boolean expression defining the condition under which to teleport a replacement actor instead of triggering the vortex.")]
public readonly BooleanExpression ReturnOriginalActorOnCondition = null;
[ActorReference(typeof(MobileInfo))]
[Desc("Replacement actor to create when ReturnOriginalActorOnCondition evaluates true.")]
public readonly string OriginalActor = "mcv";
[Desc("Facing of the returned actor.")]
@@ -56,8 +61,8 @@ namespace OpenRA.Mods.Cnc.Traits
public object Create(ActorInitializer init) { return new ConyardChronoReturn(init, this); }
}
public class ConyardChronoReturn : INotifyCreated, ITick, ISync, ISelectionBar, IDeathActorInitModifier,
ITransformActorInitModifier, INotifyBuildComplete, INotifySold, INotifyTransform
public class ConyardChronoReturn : INotifyCreated, ITick, ISync, IObservesVariables, ISelectionBar, INotifySold,
IDeathActorInitModifier, ITransformActorInitModifier
{
readonly ConyardChronoReturnInfo info;
readonly WithSpriteBody wsb;
@@ -70,7 +75,7 @@ namespace OpenRA.Mods.Cnc.Traits
Actor chronosphere;
int duration;
bool buildComplete;
bool returnOriginal;
bool selling;
[Sync]
@@ -110,6 +115,17 @@ namespace OpenRA.Mods.Cnc.Traits
conditionManager = self.TraitOrDefault<ConditionManager>();
}
IEnumerable<VariableObserver> IObservesVariables.GetVariableObservers()
{
if (info.ReturnOriginalActorOnCondition != null)
yield return new VariableObserver(ReplacementConditionChanged, info.ReturnOriginalActorOnCondition.Variables);
}
void ReplacementConditionChanged(Actor self, IReadOnlyDictionary<string, int> conditions)
{
returnOriginal = info.ReturnOriginalActorOnCondition.Evaluate(conditions);
}
void TriggerVortex()
{
if (conditionManager != null && !string.IsNullOrEmpty(info.Condition) && conditionToken == ConditionManager.InvalidConditionToken)
@@ -177,7 +193,7 @@ namespace OpenRA.Mods.Cnc.Traits
nt.AfterTransform(a);
if (selected)
self.World.Selection.Add(self.World, a);
self.World.Selection.Add(a);
if (controlgroup.HasValue)
self.World.Selection.AddToControlGroup(a, controlgroup.Value);
@@ -188,13 +204,16 @@ namespace OpenRA.Mods.Cnc.Traits
void ITick.Tick(Actor self)
{
if (self.WillDispose)
return;
if (triggered)
health.InflictDamage(self, chronosphere, new Damage(info.Damage, info.DamageTypes), true);
if (returnTicks <= 0 || --returnTicks > 0)
return;
if (!buildComplete && !selling)
if (returnOriginal && !selling)
ReturnToOrigin();
else
TriggerVortex();
@@ -228,26 +247,12 @@ namespace OpenRA.Mods.Cnc.Traits
void IDeathActorInitModifier.ModifyDeathActorInit(Actor self, TypeDictionary init) { ModifyActorInit(init); }
void ITransformActorInitModifier.ModifyTransformActorInit(Actor self, TypeDictionary init) { ModifyActorInit(init); }
void INotifyBuildComplete.BuildingComplete(Actor self)
{
buildComplete = true;
}
void INotifySold.Sold(Actor self) { }
void INotifySold.Selling(Actor self)
{
buildComplete = false;
selling = true;
}
void INotifyTransform.BeforeTransform(Actor self)
{
buildComplete = false;
}
void INotifyTransform.OnTransform(Actor self) { }
void INotifyTransform.AfterTransform(Actor self) { }
// Show the remaining time as a bar
float ISelectionBar.GetValue()
{

View File

@@ -15,7 +15,6 @@ using System.Drawing;
using System.Linq;
using OpenRA.Mods.Common.Orders;
using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.Common.Traits.Render;
using OpenRA.Primitives;
using OpenRA.Traits;

View File

@@ -0,0 +1,37 @@
#region Copyright & License Information
/*
* Copyright 2007-2018 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
[Desc("Allows this actor to be the target of an attack leap.")]
public class EdibleByLeapInfo : TraitInfo<EdibleByLeap> { }
public class EdibleByLeap
{
Actor leaper;
public bool CanLeap(Actor targeter)
{
return leaper == null || leaper.IsDead || leaper == targeter;
}
public bool GetLeapAtBy(Actor targeter)
{
if (leaper != null && !leaper.IsDead && leaper != targeter)
return false;
leaper = targeter;
return true;
}
}
}

View File

@@ -3,8 +3,9 @@
* Copyright 2007-2018 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.
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion

View File

@@ -39,6 +39,7 @@ namespace OpenRA.Mods.Cnc.Traits
static readonly FrozenActorAction Remove = (fufubg, fal, gps, fa) =>
{
// Removes the frozen actor. Once done, we no longer need to track GPS updates.
fa.Invalidate();
fal.Remove(fa);
gps.UnregisterForOnGpsRefreshed(fufubg.self, fufubg);
};
@@ -49,7 +50,7 @@ namespace OpenRA.Mods.Cnc.Traits
public readonly GpsWatcher GpsWatcher;
public Traits(Player player, FrozenUnderFogUpdatedByGps frozenUnderFogUpdatedByGps)
{
FrozenActorLayer = player.PlayerActor.TraitOrDefault<FrozenActorLayer>();
FrozenActorLayer = player.FrozenActorLayer;
GpsWatcher = player.PlayerActor.TraitOrDefault<GpsWatcher>();
GpsWatcher.RegisterForOnGpsRefreshed(frozenUnderFogUpdatedByGps.self, frozenUnderFogUpdatedByGps);
}

View File

@@ -9,7 +9,6 @@
*/
#endregion
using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Effects;
using OpenRA.Traits;

View File

@@ -10,7 +10,6 @@
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Mods.Common.Effects;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
@@ -18,7 +17,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
[Desc("This structure can be infiltrated causing funds to be stolen.")]
[Desc("Funds are transferred from the owner to the infiltrator.")]
class InfiltrateForCashInfo : ITraitInfo
{
public readonly BitSet<TargetableType> Types;
@@ -33,6 +32,7 @@ namespace OpenRA.Mods.Cnc.Traits
[Desc("Maximum amount of funds which will be stolen.")]
public readonly int Maximum = int.MaxValue;
[NotificationReference("Speech")]
[Desc("Sound the victim will hear when they get robbed.")]
public readonly string Notification = null;

View File

@@ -9,7 +9,6 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;

View File

@@ -9,7 +9,6 @@
*/
#endregion
using System.Collections.Generic;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;

View File

@@ -9,7 +9,6 @@
*/
#endregion
using System.Collections.Generic;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;

View File

@@ -0,0 +1,67 @@
#region Copyright & License Information
/*
* Copyright 2007-2018 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
[Desc("Transform into a different actor type.")]
class InfiltrateForTransformInfo : ITraitInfo
{
[ActorReference, FieldLoader.Require]
public readonly string IntoActor = null;
public readonly int ForceHealthPercentage = 0;
public readonly bool SkipMakeAnims = true;
public readonly BitSet<TargetableType> Types = default(BitSet<TargetableType>);
public object Create(ActorInitializer init) { return new InfiltrateForTransform(init, this); }
}
class InfiltrateForTransform : INotifyInfiltrated
{
readonly InfiltrateForTransformInfo info;
readonly string faction;
public InfiltrateForTransform(ActorInitializer init, InfiltrateForTransformInfo info)
{
this.info = info;
faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName;
}
void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, BitSet<TargetableType> types)
{
if (!info.Types.Overlaps(types))
return;
var transform = new Transform(self, info.IntoActor)
{
ForceHealthPercentage = info.ForceHealthPercentage,
Faction = faction,
SkipMakeAnims = info.SkipMakeAnims
};
var facing = self.TraitOrDefault<IFacing>();
if (facing != null)
transform.Facing = facing.Facing;
self.CancelActivity();
self.QueueActivity(transform);
}
}
}

View File

@@ -11,7 +11,6 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Mods.Cnc.Activities;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Activities;
@@ -24,23 +23,27 @@ namespace OpenRA.Mods.Cnc.Traits
{
public class InfiltratesInfo : ConditionalTraitInfo
{
public readonly BitSet<TargetableType> Types;
public readonly BitSet<TargetableType> Types = default(BitSet<TargetableType>);
[VoiceReference] public readonly string Voice = "Action";
[VoiceReference]
public readonly string Voice = "Action";
[Desc("What diplomatic stances can be infiltrated by this actor.")]
public readonly Stance ValidStances = Stance.Neutral | Stance.Enemy;
[Desc("Behaviour when entering the structure.",
[Desc("Behaviour when entering the target.",
"Possible values are Exit, Suicide, Dispose.")]
public readonly EnterBehaviour EnterBehaviour = EnterBehaviour.Dispose;
[Desc("Notification to play when a building is infiltrated.")]
public readonly string Notification = "BuildingInfiltrated";
[NotificationReference("Speech")]
[Desc("Notification to play when a target is infiltrated.")]
public readonly string Notification = null;
[Desc("Experience to grant to the infiltrating player.")]
public readonly int PlayerExperience = 0;
public readonly string EnterCursor = "enter";
public override object Create(ActorInitializer init) { return new Infiltrates(this); }
}
@@ -89,21 +92,34 @@ namespace OpenRA.Mods.Cnc.Traits
? Info.Voice : null;
}
public bool CanInfiltrateTarget(Actor self, Target target)
{
switch (target.Type)
{
case TargetType.Actor:
return Info.Types.Overlaps(target.Actor.GetEnabledTargetTypes()) &&
Info.ValidStances.HasStance(self.Owner.Stances[target.Actor.Owner]);
case TargetType.FrozenActor:
return target.FrozenActor.IsValid && Info.Types.Overlaps(target.FrozenActor.TargetTypes) &&
Info.ValidStances.HasStance(self.Owner.Stances[target.FrozenActor.Owner]);
default:
return false;
}
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString != "Infiltrate" || !IsValidOrder(self, order) || IsTraitDisabled)
return;
var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (target.Type != TargetType.Actor
|| !Info.Types.Overlaps(target.Actor.GetAllTargetTypes()))
if (!CanInfiltrateTarget(self, order.Target))
return;
if (!order.Queued)
self.CancelActivity();
self.SetTargetLine(target, Color.Red);
self.QueueActivity(new Infiltrate(self, target.Actor, this));
self.SetTargetLine(order.Target, Color.Red);
self.QueueActivity(new Infiltrate(self, order.Target, this));
}
}
@@ -112,7 +128,7 @@ namespace OpenRA.Mods.Cnc.Traits
readonly InfiltratesInfo info;
public InfiltrationOrderTargeter(InfiltratesInfo info)
: base("Infiltrate", 7, "enter", true, false)
: base("Infiltrate", 7, info.EnterCursor, true, true)
{
this.info = info;
}

View File

@@ -14,7 +14,6 @@ using System.Drawing;
using System.Linq;
using OpenRA.Activities;
using OpenRA.GameRules;
using OpenRA.Mods.Cnc.Activities;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
@@ -50,6 +49,10 @@ namespace OpenRA.Mods.Cnc.Traits
[VoiceReference] public readonly string Voice = "Action";
[GrantedConditionReference]
[Desc("The condition to grant to self while deployed.")]
public readonly string DeployedCondition = null;
public WeaponInfo ThumpDamageWeaponInfo { get; private set; }
public WeaponInfo DetonationWeaponInfo { get; private set; }
@@ -75,12 +78,13 @@ namespace OpenRA.Mods.Cnc.Traits
}
}
class MadTank : IIssueOrder, IResolveOrder, IOrderVoice, ITick, IPreventsTeleport, IIssueDeployOrder
class MadTank : INotifyCreated, IIssueOrder, IResolveOrder, IOrderVoice, ITick, IIssueDeployOrder
{
readonly Actor self;
readonly MadTankInfo info;
readonly WithFacingSpriteBody wfsb;
readonly ScreenShaker screenShaker;
ConditionManager conditionManager;
bool deployed;
int tick;
@@ -92,6 +96,11 @@ namespace OpenRA.Mods.Cnc.Traits
screenShaker = self.World.WorldActor.Trait<ScreenShaker>();
}
void INotifyCreated.Created(Actor self)
{
conditionManager = self.TraitOrDefault<ConditionManager>();
}
void ITick.Tick(Actor self)
{
if (!deployed)
@@ -127,9 +136,9 @@ namespace OpenRA.Mods.Cnc.Traits
return new Order(order.OrderID, self, target, queued);
}
Order IIssueDeployOrder.IssueDeployOrder(Actor self)
Order IIssueDeployOrder.IssueDeployOrder(Actor self, bool queued)
{
return new Order("Detonate", self, false);
return new Order("Detonate", self, queued);
}
bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return true; }
@@ -165,13 +174,14 @@ namespace OpenRA.Mods.Cnc.Traits
driverMobile.Nudge(driver, driver, true);
}
public bool PreventsTeleport(Actor self) { return deployed; }
void StartDetonationSequence()
{
if (deployed)
return;
if (conditionManager != null && !string.IsNullOrEmpty(info.DeployedCondition))
conditionManager.GrantCondition(self, info.DeployedCondition);
self.World.AddFrameEndTask(w => EjectDriver());
if (info.ThumpSequence != null)
wfsb.PlayCustomAnimationRepeating(self, info.ThumpSequence);
@@ -187,20 +197,18 @@ namespace OpenRA.Mods.Cnc.Traits
{
if (order.OrderString == "DetonateAttack")
{
var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (target.Type != TargetType.Actor)
return;
if (!order.Queued)
self.CancelActivity();
self.SetTargetLine(target, Color.Red);
self.QueueActivity(new MoveAdjacentTo(self, target));
self.SetTargetLine(order.Target, Color.Red);
self.QueueActivity(new MoveAdjacentTo(self, order.Target, targetLineColor: Color.Red));
self.QueueActivity(new CallFunc(StartDetonationSequence));
}
else if (order.OrderString == "Detonate")
{
self.CancelActivity();
if (!order.Queued)
self.CancelActivity();
self.QueueActivity(new CallFunc(StartDetonationSequence));
}
}

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