Compare commits

...

443 Commits

Author SHA1 Message Date
Matthias Mailänder
046d6be402 Merge pull request #3490 from pchote/changelog
Updated changelog for playtest-201306XX.
2013-06-29 04:53:23 -07:00
Paul Chote
cb6b4c3b93 Updated changelog with key changes from release-20130514...04ae39. 2013-06-29 23:49:06 +12:00
Paul Chote
04ae39caa5 Merge pull request #3467 from Mailaender/autotarget-bots
AutoTarget unarmed buildings again to help AI crush it's enemy
2013-06-29 04:16:34 -07:00
Matthias Mailänder
63a1377aca Merge pull request #3489 from ScottNZ/balance
More RA balance work
2013-06-29 03:12:36 -07:00
Matthias Mailänder
450fbded3f Merge pull request #3487 from pchote/server-client-fix
Prevent handshake spoofing of Client data.
2013-06-29 01:26:37 -07:00
Matthias Mailänder
e805dff16f Merge pull request #3486 from pchote/misc-ui-polish
Misc UI polish.
2013-06-29 01:22:12 -07:00
ScottNZ
8f087878be Merge pull request #3488 from cjshmyr/maps
RA map - doughnut - made trees for bottom teams more even towards middil...
2013-06-29 00:02:04 -07:00
ScottNZ
716b470911 Increase repair of mechanic from 13 to 20. Increase light armour multiplier percentage from 40% to 100%. 2013-06-29 15:41:29 +12:00
Paul Chote
05030e6b98 Polish d2k/ra map chooser. 2013-06-29 15:14:04 +12:00
Paul Chote
e6eac5a6c0 Remove duplication between ra and d2k lobby yaml. 2013-06-29 13:29:39 +12:00
Paul Chote
ec7bbd6c18 Add Ui.LoadWidget<T> overload. 2013-06-29 13:29:39 +12:00
Paul Chote
528198977a Move color picker remap indices into a chrome metric. 2013-06-29 13:29:39 +12:00
Paul Chote
fd660c7fa0 Make ChromeMetrics accessible from mod dlls. 2013-06-29 12:58:48 +12:00
Paul Chote
fe6c6a3017 Add Developer Menu toggle. Fixes #3296. 2013-06-29 12:53:09 +12:00
Paul Chote
f4d37857aa Remove now-obsolete desync cheat. 2013-06-29 12:53:09 +12:00
Paul Chote
3e2f7b41d8 Use a trylock to simplify ColorMixerWidget. Fixes #3374. 2013-06-29 12:53:09 +12:00
Paul Chote
d7a580584d Remove renderer type setting.
Multiple people have needed support after breaking
their game by switching to an unsupported renderer.
2013-06-29 12:53:09 +12:00
Paul Chote
f7f8740745 Simplify some setting descriptions. 2013-06-29 12:53:09 +12:00
Paul Chote
7eb579cb6c Remove settings UI for IgnoreVersionMismatch. 2013-06-29 12:53:08 +12:00
Paul Chote
8307014de9 Improve kick button label for C&C. 2013-06-29 12:53:08 +12:00
Paul Chote
daf94e61e0 Add some noise to the C&C dialog textures. 2013-06-29 12:53:08 +12:00
ScottNZ
07689efc6e MRJ missile deflection range increased from 3 to 4. 2013-06-29 12:49:42 +12:00
ScottNZ
14a952169b Iron Curtain duration increased from 15 to 20. Chronosphere duration decreased from 30 to 20. 2013-06-29 12:49:42 +12:00
ScottNZ
646f7c1362 RA Sniper HP reduced from 200 to 80. 2013-06-29 12:49:41 +12:00
Paul Chote
faa745e566 Merge pull request #3456 from ScottNZ/lobby
Add temp banning to servers
2013-06-28 16:33:23 -07:00
Curtis Shmyr
3485a5e855 RA map - doughnut - made trees for bottom teams more even towards middile. Added missing fences top. 2013-06-28 17:16:06 -06:00
Paul Chote
79779d69ba Prevent handshake spoofing of Client data. 2013-06-29 11:12:41 +12:00
ScottNZ
a6e5a0b53f Add temp banning to servers 2013-06-29 10:58:37 +12:00
Matthias Mailänder
6cd69f5c05 Merge pull request #3479 from pchote/conyard-fix
Fix zombie MCV bug.
2013-06-27 14:14:38 -07:00
Matthias Mailänder
251c316120 Merge pull request #3480 from pchote/server-fix
Handle failure cases of Socket.Send.
2013-06-27 13:47:43 -07:00
Paul Chote
8856a1444c Handle failure cases of Socket.Send. Fixes #3455. 2013-06-27 18:43:10 +12:00
Paul Chote
791cdeba4d Cancel make animation if the building dies. Fixes #3464. 2013-06-27 18:07:51 +12:00
Curtis Shmyr
39e97ee767 Merge pull request #3477 from ScottNZ/bleed
Reduce RA oil derrick capture bonus from $500 to $100
2013-06-26 21:51:57 -07:00
ScottNZ
7e7d48444b Reduce RA oil derrick capture bonus from $500 to $100 2013-06-27 16:31:44 +12:00
Matthias Mailänder
bd7e8da81d Merge pull request #3475 from pchote/startgame-fix
Startgame fix and observer tweaks.
2013-06-26 11:21:39 -07:00
Paul Chote
1fce900801 Set spectator color to white & remove selector. 2013-06-26 22:32:27 +12:00
Paul Chote
859af00f1c Remove unused spectator ready checkboxes. 2013-06-26 22:21:13 +12:00
Paul Chote
6e4cf37e44 Fixes #3336. 2013-06-26 21:42:56 +12:00
Chris Forbes
1cd3adef5e Merge pull request #3472 from pchote/renderer-regressions
Fix renderer regressions
2013-06-26 02:00:35 -07:00
Paul Chote
557492cc72 Merge pull request #3474 from chrisforbes/movement-equiv-classes
movement equivalence classes
2013-06-26 01:56:25 -07:00
Chris Forbes
c0e0efd0ef movement equivalence classes
These can be used as a basis for a bunch of pathing optimizations.

- Feasability of movement can be precomputed for each class, avoiding
  the worst-case pathfinding behavior

- A path could potentially be shared between all members of a class.
  This isnt necessarily the best path for any single unit, as it
  doesn't care about efficiency of movement across various terrain --
  but it would be a "reasonable" path that the whole group could take
  together.

- General pathing checks can be converted from intersection of sets of
  strings to a simple AND.

- Other, wilder things.

V2: be paranoid about too-long bit vectors.
2013-06-26 20:50:51 +12:00
Paul Chote
185e9b3f08 Fix and rewrite contrails. Closes #3457. 2013-06-25 19:38:48 +12:00
Paul Chote
c886253738 Fix white lines at the bottom of units. 2013-06-25 18:40:11 +12:00
Paul Chote
f52a1c1521 Round CashTick pos to the nearest int. 2013-06-24 19:31:15 +12:00
Chris Forbes
fc6a38182d Merge pull request #3461 from Mailaender/default-buffer-size
More Debug Logging for the Server/Client connection
2013-06-23 14:37:05 -07:00
Chris Forbes
5dca3742f1 Merge pull request #3470 from ScottNZ/production
Favour newer buildings over older buildings when a unit can appear from ...
2013-06-23 14:33:33 -07:00
ScottNZ
9f1d9e153a Favour newer buildings over older buildings when a unit can appear from multiple buildings after being built 2013-06-24 09:16:35 +12:00
Chris Forbes
836a0ffe4b Merge pull request #3465 from Mailaender/legacy-capture-fixes
Various fixes towards LegacyCapture
2013-06-23 13:48:19 -07:00
ScottNZ
c7ccdfde2d Merge pull request #3469 from Mailaender/unarm-observers
Removed SupportPowerBin from Observer Widget
2013-06-23 04:56:12 -07:00
Matthias Mailänder
d18289d5bc remove SupportPowerBin from observer widget
fixes #3444
2013-06-23 13:45:33 +02:00
Matthias Mailänder
fcf987a4dd also revert AutoTargetIgnore: for all buildings in d2k 2013-06-23 13:15:52 +02:00
Matthias Mailänder
52069c6788 also revert AutoTargetIgnore: for all buildings in cnc 2013-06-23 13:15:47 +02:00
Matthias Mailänder
c20df5874d Revert "don't auto-target unarmed buildings in ra mod"
This reverts commit c74fa90305
2013-06-23 13:10:03 +02:00
Matthias Mailänder
e6bdacce9c various fixes towards LegacyCapture
- sniper using Captures, but churches using LegacyCapturable
- unable to parse CaptureThreshold/SabotageHPRemoval from yaml
- not firing up INotifyCapture and breaking:
  * sniper transforming the church on capture in ra
  * proc not giving away it's contents in cnc
2013-06-23 12:43:24 +02:00
Matthias Mailänder
2b84dbfccf Merge pull request #3463 from pchote/harv-force-move
Allow force-move to disable the harvest activity.
2013-06-23 02:26:25 -07:00
Paul Chote
efc494b859 Allow force-move to disable the harvest activity. Fixes #3459. 2013-06-23 14:22:37 +12:00
Matthias Mailänder
6a50c760ae record socket buffer sizes to server.log
for investigating #3455
2013-06-22 15:49:52 +02:00
Matthias Mailänder
c18b5e424e at least write the acception we swallow here to the server.log 2013-06-22 15:46:26 +02:00
Matthias Mailänder
8ddc840370 fix some plenks
no logic changes
2013-06-22 15:17:10 +02:00
Matthias Mailänder
dab3d99890 write another socket error catch into server.log 2013-06-22 15:16:32 +02:00
Matthias Mailänder
b93e9a5945 Merge pull request #3458 from pchote/harvester-animations
Fix the harvest animations for TS and D2k.
2013-06-22 01:21:34 -07:00
Matthias Mailänder
c9e46ee0f6 Merge pull request #3454 from pchote/cargo-antivis
Fix shroud glitches once and for all
2013-06-22 01:01:29 -07:00
Paul Chote
c94e80d682 Fix D2k harvester dock animation. 2013-06-22 19:31:18 +12:00
Paul Chote
2e2e95ef57 Add D2k harvest animation.
harvest2.shp is movingsand2.shp with a corrected frame order.
2013-06-22 19:25:10 +12:00
Paul Chote
0029cb8aec Allow Harvester to define the number of harvestable facings. 2013-06-22 19:25:10 +12:00
Paul Chote
0424b56af1 Add WithHarvestAnimation for TS and D2K harvester animations. 2013-06-22 19:25:10 +12:00
Paul Chote
80c3cf479d Remove harvester dependency on RenderUnit. 2013-06-22 16:58:40 +12:00
Paul Chote
6d24eb14d7 Merge pull request #3371 from aperkins81/bleed
Add Bots button in Lobby
2013-06-21 18:52:17 -07:00
Paul Chote
76efea72d9 Merge pull request #3441 from Mailaender/display-server-error
Display ServerError in CONNECTIONFAILED_PANEL
2013-06-21 18:45:24 -07:00
Paul Chote
37bc3766a5 Merge pull request #3420 from Mailaender/sound-info
Display the current sound engine in the console
2013-06-21 18:42:06 -07:00
Paul Chote
9adfb56f65 Only update vis for actors in the world. Fixes #2726. 2013-06-22 13:15:38 +12:00
Paul Chote
b95cd0c91f Add an extra sanity check. 2013-06-22 13:15:38 +12:00
Paul Chote
7bce2017c3 Simplify and increase robustness of Shroud et al. Fixes #3440. 2013-06-22 13:15:27 +12:00
Andy Perkins
23d9d611c1 Lobby, Add Bots button 2013-06-22 07:56:06 +10:00
Matthias Mailänder
13fbcc56ee inform the user which sound engine is used 2013-06-21 18:13:13 +02:00
Matthias Mailänder
e9b2bcc579 fix some plenks
no logic changes
2013-06-21 18:05:30 +02:00
Matthias Mailänder
65088ea2d5 display the last ServerError in the connection failed dialog
closes #2727
2013-06-21 18:04:40 +02:00
Paul Chote
897cb13626 Merge pull request #3452 from Mailaender/center-range
Don't discriminate bottom right directions when checking whether a unit is in range.
2013-06-21 05:39:47 -07:00
Paul Chote
574ab7281b Merge pull request #3429 from Mailaender/dont-crash-on-drop
Don't crash when the player to drop can't be found.
2013-06-21 02:05:10 -07:00
Paul Chote
a13ce8cd40 Merge pull request #3439 from Mailaender/kill-cargo
Kill Cargo properly
2013-06-21 00:43:07 -07:00
Matthias Mailänder
ed8f6d9691 Attack the center of the cell instead of the top left corner
to not discriminate bottom right directions. Fixes #2205
2013-06-20 20:37:04 +02:00
Matthias Mailänder
29c93182f1 remove some plenks
no logic changes
2013-06-20 20:12:55 +02:00
Matthias Mailänder
c5a8af9885 Merge pull request #3450 from pchote/offset-sprites
Allow sequences to define sprite offsets
2013-06-20 09:46:39 -07:00
Paul Chote
c40fd80ae1 Remove obsolete SpriteRenderable ctor. 2013-06-20 22:35:01 +12:00
Paul Chote
90d7f230ed Move RallyPoint offset into sequences. 2013-06-20 22:35:01 +12:00
Paul Chote
88fd5ba094 Move SatelliteLaunch offsets into sequences. 2013-06-20 22:28:57 +12:00
Paul Chote
3095c6ea5f Move WithFire, Burns offsets into sequences. 2013-06-20 22:01:59 +12:00
Paul Chote
fc33c56d61 Use NormalizeSequence in RenderBuildingWall. 2013-06-20 21:53:31 +12:00
Paul Chote
5084b30f3f Simplify RenderGunboat. 2013-06-20 21:53:31 +12:00
Paul Chote
5ee9b5cab4 Support additional render damage states for buildings. 2013-06-20 21:53:31 +12:00
Paul Chote
1a3a4bd7ba Remove unnecessary parameter from RenderSprites ctor. 2013-06-20 21:39:12 +12:00
Paul Chote
ac0094176a Reduce C&C silo pip count to a reasonable number. 2013-06-20 18:39:27 +12:00
Paul Chote
5734131310 Remove unnecessary fields from RenderBuilding*. 2013-06-20 18:29:20 +12:00
Paul Chote
e99d0a0b43 RenderBuildingRefinery -> WithResources. 2013-06-20 18:28:33 +12:00
Paul Chote
f376eb9b2a Move proc light offset into sequences. Fixes #3391. 2013-06-20 17:59:27 +12:00
Paul Chote
98039abf1b Allow sequences to define a pixel offset. Fixes #3287. 2013-06-20 17:58:32 +12:00
Paul Chote
9a6b5e21dc Account for sprite offsets in RenderDebugGeometry. 2013-06-20 17:57:23 +12:00
Chris Forbes
979ac4f91f Merge pull request #3407 from pchote/voxel-fbo
Voxel refactoring
2013-06-19 14:57:17 -07:00
Matthias Mailänder
425aa46476 kill the cargo instead of just destroying it
fixes #2983 and the player statistics
2013-06-19 19:09:40 +02:00
Chris Forbes
5a0bd24a4e Merge pull request #3411 from Mailaender/save-the-gnomes
Made ore mines indestructible
2013-06-18 19:57:24 -07:00
Chris Forbes
d60b33b159 Merge pull request #3442 from pchote/credits-menu
Ingame credits menu
2013-06-18 19:19:51 -07:00
Paul Chote
74ddb432aa Update AUTHORS for display in the credits panel. 2013-06-19 13:29:41 +12:00
Paul Chote
a0e224ec71 Add a Credits menu. Fixes #2864. 2013-06-19 13:21:29 +12:00
Matthias Mailänder
b7f1711497 remove some plenks
no logic changes
2013-06-18 20:11:27 +02:00
Paul Chote
66383a57d1 Merge pull request #3435 from chrisforbes/revert-sniper-hotkey
fix sniper hotkey conflict AGAIN
2013-06-17 22:16:43 -07:00
Chris Forbes
d7bafdf0c8 Merge pull request #3428 from ScottNZ/maps
Remove some maps
2013-06-17 22:12:43 -07:00
Chris Forbes
698efb4995 fix sniper hotkey conflict AGAIN
You can't use `s`, it's a combat hotkey.
2013-06-18 11:32:22 +12:00
Matthias Mailänder
321ed4ea3d Merge pull request #3432 from chrisforbes/remove-BuiltAt
closes #2323
2013-06-17 14:08:16 -07:00
Chris Forbes
6c1d4efb07 remove useless Buildable.BuiltAt
V2: don't leave the broken [ActorReference] attached to Owner.
2013-06-18 09:02:12 +12:00
Matthias Mailänder
708e91145c log exception thrown when the handshake fails 2013-06-17 22:50:47 +02:00
Matthias Mailänder
6b261534d8 remove some plenks
no logic changes
2013-06-17 22:50:39 +02:00
Chris Forbes
627a892097 Merge pull request #3430 from Mailaender/csc-target-x86
corflags OpenRA.Utility.exe /32BITREQ+
2013-06-17 13:37:45 -07:00
Chris Forbes
c862ba6e03 Merge pull request #3431 from Mailaender/chronoshift-shellmap
Let the cruisers jump in again on the temperat shellmap
2013-06-17 13:36:44 -07:00
Matthias Mailänder
7e39b2546b corflags OpenRA.Utility.exe /32BITREQ+
fixes #2812
2013-06-17 22:04:16 +02:00
Matthias Mailänder
659600db23 let the cruiser jump in earlier and nearer to friendly harbor
fixes #3126
2013-06-17 21:50:05 +02:00
Matthias Mailänder
875ac468d3 log the catched exception when a client is dropped
because order dispatching failed for debugging
2013-06-17 20:46:02 +02:00
Matthias Mailänder
b01d55f1f6 Don't crash when the player to drop can't be found.
fixes #3224
2013-06-17 19:42:56 +02:00
Matthias Mailänder
734f12bbc6 Merge pull request #3426 from chrisforbes/ra_perf_removal
ra_perf removal
2013-06-17 09:34:02 -07:00
ScottNZ
6f3cd5dff2 Remove hotzone, mjolnir-2, seaside 2013-06-17 20:55:42 +12:00
Chris Forbes
787fde31ed Merge pull request #3405 from Mailaender/fort-bugstar
Fixed the worsed problems in Fort Lonestar
2013-06-16 21:37:07 -07:00
Chris Forbes
c273a02b94 remove ra_perf mod 2013-06-17 13:23:32 +12:00
Chris Forbes
6735fd593b Merge pull request #3425 from wuschel/bleed
Added wuschel to contributors
2013-06-16 17:18:53 -07:00
wuschel
154cdc8709 Added wuschel to contributors
Added wuschel to contributors.
2013-06-17 01:41:56 +02:00
Chris Forbes
cec62bd356 Merge pull request #3424 from wuschel/bleed
Updating 7 large scale wuschel maps
2013-06-16 15:53:49 -07:00
wuschel
b34a31b5be Updating 7 wuschel maps
updated: RA map POSEIDON - facelift, small tactical changes on middle
island.
updated: RA map PLUTO - corrected oil derrick that was not capturable.
updated: RA map ARTEMIS - some cosmetic changes on main island.
updated: RA map ARES - minor yaml changes to MISS.
updated: RA map APOLLO - minor yaml changes to MISS.
updated: RA map DIONYSUS - cosmetic changes on main island, minor yaml
changes to MISS.
added: RA map ZEUS, 256x320 (lo and behold, the father of all mapz!)

Note: ZEUS, ARTEMIS and APOLLO need
https://github.com/OpenRA/OpenRA/pull/3375 to be implemented in order to
avoid lags. The first map is lagging badly.
2013-06-16 23:48:50 +02:00
Chris Forbes
5d733b8e45 Merge pull request #3414 from Mailaender/sound.rs
Added native support for Dune 2000 SOUND.RS packages
2013-06-16 14:00:41 -07:00
Chris Forbes
2deef04095 Merge pull request #3418 from Mailaender/no-husk-reservation
Don't reserve airfields/helipads for husks that just fall down
2013-06-16 13:56:19 -07:00
Chris Forbes
e03734f2b4 Merge pull request #3423 from Mailaender/imperial-basin
Added Imperial Basin
2013-06-16 13:53:53 -07:00
Matthias Mailänder
ce01eca4d7 clean up d2k map folder
- removed Forgotten Path as it lacks quality
- replaced small originals with larger/modded versions
2013-06-16 22:39:42 +02:00
Matthias Mailänder
ae8c556187 added Imperial Basin
http://content.open-ra.org/index.php?p=detail&table=maps&id=1028
2013-06-16 22:34:03 +02:00
Matthias Mailänder
11755798cc Don't reserve airfields/helipads for husks that just fall down
fixes #3376
2013-06-16 12:43:41 +02:00
Paul Chote
e229194b62 Change extension check to EXT_fbo. 2013-06-16 19:50:52 +12:00
Paul Chote
82059dca6d Add BeamRenderable for lasers. 2013-06-16 19:37:54 +12:00
Paul Chote
1eb04a70a5 Add TextRenderable for CashTick. Make ticks consistent. 2013-06-16 19:10:38 +12:00
Matthias Mailänder
886099a948 Merge pull request #3415 from pchote/remove-dead-external-connect
Remove Bitrotted JoinExternalGame.
2013-06-15 10:44:30 -07:00
Paul Chote
71616201ff Ignore unknown wav chunks instead of throwing. 2013-06-15 19:41:07 +02:00
Paul Chote
2eeabfe668 Fixup: Use ReadASCIIZ in D2kSoundResources. 2013-06-15 19:41:07 +02:00
Paul Chote
0c4df26ed0 Add Stream.ReadASCIIZ for d2k. 2013-06-15 19:41:07 +02:00
Matthias Mailänder
cdeea80037 replaced Dune 2000 AUD with original WAV
removed *.aud hard-coding everywhere
2013-06-15 19:41:07 +02:00
Matthias Mailänder
9fb98f04d4 implement the fact chunk for WAVE files 2013-06-15 19:41:07 +02:00
Matthias Mailänder
85da51ca09 added a Dune 2000 SOUND.RS parser
again @pchote via IRC
2013-06-15 19:41:07 +02:00
Matthias Mailänder
d98a766362 Merge pull request #3403 from wuschel/bleed
added new RA multiplayer (desert) map: Pluto
2013-06-15 10:40:02 -07:00
Paul Chote
659e56f0fa Remove Bitrotted JoinExternalGame. Fixes #3406. Closes #2153. 2013-06-16 04:19:06 +12:00
Matthias Mailänder
924aba49ea Merge pull request #3413 from pchote/install-modchooser
Add "Change Mod" buttons to RA and D2K. Fixes #3401.
2013-06-15 02:34:54 -07:00
Paul Chote
0e21107694 Add "Change Mod" buttons to RA and D2K. Fixes #3401. 2013-06-15 21:24:49 +12:00
Matthias Mailänder
f918d1d0b2 removed spurious tabs 2013-06-15 11:02:46 +02:00
Matthias Mailänder
52de4e1796 made ore mines indestructible
don't inherit tree to avoid future regressions
2013-06-15 10:50:25 +02:00
Paul Chote
52335a37bf Allow/require renderers to enable alpha blending when needed. Fixes voxel shadow issues. 2013-06-15 19:31:52 +12:00
Paul Chote
4c22193446 Remove obsolete Stencil Buffer functions. 2013-06-15 19:31:52 +12:00
Paul Chote
c5337cdcf3 Reimplement voxel rendering with a FBO. 2013-06-15 19:31:52 +12:00
Paul Chote
2215f74959 Support rendering sprites into non-rectangular quads. 2013-06-15 19:16:09 +12:00
Paul Chote
7a71f87d9f Introduce Renderable.BeforeRender().
WorldRenderer.Draw() has been slightly reorganized
to ensure that BeforeRender is called before any
drawing render state (e.g. scissor) has been set.
2013-06-15 19:16:08 +12:00
Paul Chote
18311be3ae Remove nearest-int position rounding from SpriteRenderer.
The things that want to be rounded already do this
much earlier (wr.ScreenPxPosition, etc).
2013-06-15 19:16:08 +12:00
Paul Chote
7d09e78655 Add WorldRgbaSpriteRenderer. 2013-06-15 19:16:08 +12:00
Paul Chote
7beef85a64 Use PaletteReferences everywhere. 2013-06-15 19:16:08 +12:00
Paul Chote
e5bcb88b0e Support sprites with an internal offset. 2013-06-15 19:16:08 +12:00
Paul Chote
9b576d3fdd Add a visualization layer for renderable geometry. 2013-06-15 19:16:08 +12:00
Paul Chote
4152f61999 Move MakeFloatMatrix to Graphics.Util. 2013-06-15 19:16:08 +12:00
Paul Chote
5f0ab1f62d Add functions for calculating voxel bounding boxes. 2013-06-15 19:16:08 +12:00
Paul Chote
37770a4e47 Extract voxel transform matrix into a function. 2013-06-15 19:16:08 +12:00
Paul Chote
013ad0617e Allow sheets to wrap an ITexture directly. 2013-06-15 19:16:07 +12:00
Paul Chote
22e6966c8e Texture changes:
* The GL texture id is now readonly.
* Added Size property.
* Added GetData() for reading data back from the GPU.
* Added SetEmpty() for creating an empty texture of a given size.
2013-06-15 19:16:07 +12:00
Paul Chote
4c8c010506 Expose FBOs to engine code. 2013-06-15 19:16:07 +12:00
Paul Chote
0e1c12131a Generalize SheetBuilder overflow behavior. 2013-06-15 19:16:07 +12:00
Paul Chote
cd268c11ee Move IsPowerOf2 to Exts. 2013-06-15 19:16:07 +12:00
Paul Chote
da8202a15e Clean up LineRenderer whitespace (no code changes). 2013-06-15 19:09:20 +12:00
Paul Chote
ef95faa9b9 Fix LaserZap LineRenderer flushing. 2013-06-15 19:09:20 +12:00
Matthias Mailänder
b2201a0463 fixed conflicting sniper hotkey and too much damage vs walls
closes #3381
2013-06-15 08:35:29 +02:00
Matthias Mailänder
ebe0b0323e make the snipers fire at targets in Fort Lonestar 2013-06-15 08:35:29 +02:00
Matthias Mailänder
2b8319090e fixed Mig bombers crashing to earth when entering Fort Lonestar 2013-06-15 08:35:29 +02:00
Matthias Mailänder
bad3bd5fbb added the parabox animation when parabomb cates are collected 2013-06-15 08:35:21 +02:00
ScottNZ
06b797fd14 Rename FortScript.cs to match the name of the map it's for 2013-06-14 20:40:40 +12:00
ScottNZ
5d37438561 Unbreak Fort Lonestar 2013-06-14 20:33:33 +12:00
ScottNZ
1593c94d90 Add some debugging information to LoadYamlRules 2013-06-14 20:33:26 +12:00
ScottNZ
c2e450986f Add license notice to FortScript.cs 2013-06-14 20:20:55 +12:00
ScottNZ
6b44d232e7 Add Nukem's maps to the csproj 2013-06-14 20:20:33 +12:00
Matthias Mailänder
01a625691a Merge pull request #3389 from ScottNZ/infil
Infiltration code cleanup
2013-06-14 00:08:24 -07:00
ScottNZ
5334589922 Infiltration code cleanup 2013-06-14 18:58:39 +12:00
Chris Forbes
17a3c0773b Merge pull request #3404 from cjshmyr/prodqueue
Fix BuildTimeSpeedReduction being one step (building) too early with bui...
2013-06-13 17:37:57 -07:00
Curtis Shmyr
06375fe1a3 Fix BuildTimeSpeedReduction being one step (building) too early with build speed reduction 2013-06-13 18:31:30 -06:00
wuschel
e3bde3ccfe added new RA multiplayer (desert) map: Pluto
Added Pluto,  a new RA multiplayer map that focuses on Naval engagements
and oil derrick control.
2013-06-13 19:21:14 +02:00
Chris Forbes
6a5f1b0f63 Merge pull request #3400 from Mailaender/xbuild-warning
Fixed CS1701 xbuild/md-tool warning
2013-06-12 21:29:55 -07:00
Matthias Mailänder
10e5558812 fix an xbuild/md-tool warning
Assuming assembly reference.
You may need to supply runtime policy (CS1701)
2013-06-13 06:18:01 +02:00
Chris Forbes
2301c11309 Merge pull request #3383 from Mailaender/wave
Added support for WAVE sound files
2013-06-12 17:01:20 -07:00
Curtis Shmyr
db0829e6a0 Merge pull request #3397 from chrisforbes/target-generations
Target generations
2013-06-12 17:01:08 -07:00
Chris Forbes
c5f491dfc4 Merge pull request #3385 from cjshmyr/prodqueue
Production queue - change BuildTimeSpeedUpDivisor
2013-06-12 17:00:20 -07:00
Curtis Shmyr
3513e6538a Build Acceleration - Change BuildTimeSpeedReduction to be more configurable 2013-06-12 17:45:43 -06:00
Chris Forbes
6ca4f0797a Merge pull request #3399 from wuschel/bleed
Renamed Ares: National Park to Dionysus
2013-06-12 16:11:42 -07:00
wuschel
18a64cb0e5 Renamed Ares: National Park to Dionysus
Renamed map upon chrisf's request.
2013-06-13 00:10:15 +02:00
Curtis Shmyr
ec9345b051 Merge pull request #3398 from chrisforbes/panic-heal
Fix #3393: Prevent ScaredyCat from reacting to healing
2013-06-12 15:06:27 -07:00
Chris Forbes
0a8e47c357 Fix #3393: Prevent ScaredyCat from reacting to healing 2013-06-13 10:03:08 +12:00
Chris Forbes
59d10cfc5d Invalidate targets when chronoshifted
This replaces OpenRA/OpenRA#2807, without the massive cost.
2013-06-13 09:47:42 +12:00
Chris Forbes
ae809ce39f Use actor/target generations to invalidate targets on ownership change 2013-06-13 09:44:50 +12:00
Chris Forbes
5bc47f4834 Add generation counting to Actor and Target
This allows us to invalidate targets based on arbitrary conditions,
just by bumping the actor's generation number.

The next patches will use this.
2013-06-13 09:44:13 +12:00
Chris Forbes
a9b6a94ade Merge pull request #3396 from wuschel/bleed
wuschel's OpenRA map update
2013-06-12 14:30:24 -07:00
Chris Forbes
1258a6186c Merge pull request #3379 from NukemBro/bleed
Mission Changes
2013-06-12 14:30:11 -07:00
Chris Forbes
b91c8f0b6a Merge pull request #3384 from cjshmyr/engi2
Capture changes - Engis now capture from outside. Added classic (legacy)...
2013-06-12 14:29:42 -07:00
wuschel
1b4521fd51 Added new OpenRA map: Artemis
Added new large OpenRA map based on western part of Zeus map. NOTE: The
file on the content.openra website is out of date.
2013-06-12 22:38:28 +02:00
Paul Chote
f871998a35 Merge pull request #3380 from Mailaender/setup.z
Added an InstallShield extraction frontend for Dune 2000
2013-06-12 11:18:22 -07:00
Matthias Mailänder
0554ef35b7 added support for WAVE sound files
closes #2174
2013-06-12 20:18:07 +02:00
Paul Chote
1ea322ff9b Merge pull request #3294 from Mailaender/single-player-orderlag
Set the OrderLatency to 1 in single player games
2013-06-12 11:02:53 -07:00
wuschel
5ea2598ea9 Updated my maps: Apollo, Ares, Ares National Park, Poseidon
Removed the oil derrick and mine layer modd in the yaml files, since
they are no longer necessary.
2013-06-12 19:56:49 +02:00
Curtis Shmyr
6a1b37b5b7 Capture changes - Engis now capture from outside. Added classic (legacy) capturable traits. 2013-06-11 20:44:06 -06:00
Matthias Mailänder
801f293948 also extract GAMESFX from Dune 2000 setup.z 2013-06-09 17:31:14 +02:00
Matthias Mailänder
c9466f995b extracting d2k setup.z works now, adapting the UI 2013-06-09 16:54:13 +02:00
Chris Forbes
d898899de7 Revert "sound and explosion changes"
This reverts commit 7e3f90ee1d.
2013-06-09 13:17:15 +12:00
Grant H.
54439f8af8 Mission changes
Mission changes
2013-06-08 18:46:32 -05:00
Grant H.
eb159afb64 Fixed Survival02
Few Changes
2013-06-08 18:39:59 -05:00
Chris Forbes
e7fcb758e3 Merge pull request #3361 from pchote/voxels
TS/RA2 Voxel support
2013-06-06 17:25:39 -07:00
Matthias Mailänder
22a8af34ea Merge pull request #3373 from ScottNZ/husk
Rework aircraft destruction to use husks instead of the original actor. Fixes #3324 and closes #3216.
2013-06-06 10:20:17 -07:00
ScottNZ
5184cee3ca Rework aircraft destruction to use husks instead of the original actor 2013-06-07 05:04:47 +12:00
Matthias Mailänder
fd3f48b359 Merge pull request #3372 from ScottNZ/missions
Mission objectives refactoring
2013-06-05 09:37:44 -07:00
ScottNZ
d040153017 Sort/remove usings 2013-06-06 03:10:21 +12:00
ScottNZ
77f66fa8b8 Add dynamic heights for things in the objectives panel. 2013-06-06 03:09:02 +12:00
ScottNZ
20058b137e Adjust CountdownTimerWidget's red-white time threshold to 60s 2013-06-06 02:16:45 +12:00
ScottNZ
11d8b4e747 Change some Destroyed checks to IsDead checks 2013-06-06 02:08:29 +12:00
ScottNZ
82128be9e2 Refactor Survival 01 objectives code 2013-06-06 02:07:07 +12:00
ScottNZ
51fcb8d679 Refactor Soviet 01 Classic objectives code 2013-06-06 02:04:24 +12:00
ScottNZ
0acfb77be9 Refactor Monster Tank Madness objectives code 2013-06-06 01:59:32 +12:00
ScottNZ
e4cd7220cc Refactor Allies 04 objectives code 2013-06-06 01:54:40 +12:00
ScottNZ
cdb74fd547 Refactor Allies 03 objectives code 2013-06-06 01:51:15 +12:00
ScottNZ
aecc3624ba Refactor Allies 02 objectives code 2013-06-06 01:47:38 +12:00
ScottNZ
fdfd2eacea Refactor Allies 01 objectives code 2013-06-06 01:40:42 +12:00
Chris Forbes
9383e0572d Merge pull request #3370 from pchote/server-admin-fix
Simplify admin promotion check in ValidateClient.
2013-06-05 03:40:27 -07:00
Paul Chote
18f1683968 Simplify admin promotion check in ValidateClient.
This also fixes a crash if we want to allow
multiple admins in a server.
2013-06-05 22:35:30 +12:00
Grant H.
980c44bf62 Added Sahara
Sahara 4v4 desert map.
2013-06-05 02:13:41 -05:00
Grant H.
1043f58cc4 Finished Fort LoneStar
Finished Fort LoneStar + Added 2 new Desert maps:
-Great Sahara
-Suffrage
2013-06-05 01:57:08 -05:00
Paul Chote
7fb643f962 Add render trait for Mammoth Mk II. 2013-06-04 20:40:25 +12:00
Paul Chote
5cfde95358 Add a basic set of voxel render traits. 2013-06-04 20:40:24 +12:00
Paul Chote
235042ea65 Add VoxelRenderable to integrate voxel drawing with render traits. 2013-06-04 20:40:24 +12:00
Paul Chote
82faf7e929 Allow mods and maps to define voxel sequences. 2013-06-04 20:40:24 +12:00
Paul Chote
a00696ec3b Add core voxel rendering code. 2013-06-04 20:40:24 +12:00
Paul Chote
6cef4290df Store the voxel normals table in a palette. 2013-06-04 20:40:24 +12:00
Paul Chote
af6791d942 Implement hva parser. 2013-06-04 20:25:21 +12:00
Paul Chote
992db08f71 Implement vxl parser. 2013-06-04 20:25:21 +12:00
Paul Chote
656a529249 Support Vertex objects with custom z coordinate. 2013-06-04 20:25:21 +12:00
Paul Chote
f6264eeba4 Expose stencil buffer to render code.
The intention is to provide a layer for tracking shadow
rendering, so a single bit is sufficient for now.
2013-06-04 20:25:21 +12:00
Paul Chote
1b34c7d6b9 Expose depth buffer to render code. 2013-06-04 20:25:21 +12:00
Paul Chote
9566385aac Add renderer support for additional vec* uniforms. 2013-06-04 20:25:20 +12:00
Paul Chote
064938378f Add renderer support for matrix uniforms. 2013-06-04 20:25:20 +12:00
Paul Chote
fb3e776cb9 Add matrix helpers to Graphics.Util. 2013-06-04 20:25:20 +12:00
Paul Chote
eff46cf40d Add a Name field to Armament for other traits to reference. 2013-06-04 20:25:20 +12:00
Grant H.
9ed7d05ca7 Added Sahara Map
Added Sahara 4v4 map
Good balanced ore and symmetrical
2013-06-02 20:23:12 -05:00
Grant H.
1456cd6254 Fort LoneStar Fixes/Changes
Added:
-Game-play Balance changes
-Greater Difficulty
-Added Boss(es)
2013-06-02 18:54:53 -05:00
Chris Forbes
8c9fb382e3 Merge pull request #3366 from pchote/bridge-fix
Fixes #3329
2013-06-02 00:44:28 -07:00
Paul Chote
06d11c4fa9 Make missing tile transparent to avoid visual glitch with bridge1h in maps. 2013-06-02 19:27:23 +12:00
Paul Chote
50dff05675 Return a synthesized tile for missing indices. Fixes #3329. 2013-06-02 19:26:55 +12:00
Paul Chote
5692b95ad2 Use stream extensions to read binary map data. 2013-06-02 17:29:31 +12:00
Grant H.
130300074e Added New Missions
Added New Single-player Survival Map - Survival02
Added New 4 Player Co-op Survival Minigame - Fort LoneStar
2013-06-01 19:09:27 -05:00
ScottNZ
0cccfa4f8c Adjust some Tournament Island rock positions 2013-06-02 02:52:51 +12:00
Matthias Mailänder
ee058e72a9 Merge pull request #3356 from pchote/consistent-stream-reading
Fix BinaryReader / stream inconsistencies.
2013-06-01 03:51:16 -07:00
Matthias Mailänder
10acb115da Merge pull request #3358 from ScottNZ/maps
Add Tournament Island map to RA
2013-06-01 03:34:57 -07:00
Paul Chote
61c5b99dc5 Remove BinaryReader from R8Reader. 2013-06-01 15:09:36 +12:00
Paul Chote
edf604e080 Remove BinaryReader from VqaReader. 2013-06-01 15:09:36 +12:00
Paul Chote
6d8df80664 Remove BinaryReader from Dune2ShpReader. 2013-06-01 15:09:36 +12:00
Paul Chote
dd23e9598a Remove BinaryReader from ShpTSReader. 2013-06-01 15:09:36 +12:00
Paul Chote
6edde6c4ac Remove BinaryReader from MixFile. 2013-06-01 15:09:36 +12:00
Paul Chote
93b606da2c Add stream extensions for reading basic types. 2013-06-01 15:09:35 +12:00
Paul Chote
a3729a11c7 Extract stream extensions to their own file. 2013-06-01 15:09:35 +12:00
Chris Forbes
646b94e7cb Merge pull request #3348 from pchote/rendersprites
Renderer refactoring - Traits
2013-05-31 20:05:02 -07:00
Chris Forbes
bd72179cb2 Merge pull request #3355 from ScottNZ/follow
Guard fixes
2013-05-31 18:38:53 -07:00
Chris Forbes
4552bbf19f Merge pull request #3345 from reaperrr/minorfixes01
Some minor fixes
2013-05-31 18:38:14 -07:00
Paul Chote
8123a383b6 Fixes #3334. 2013-06-01 12:53:31 +12:00
ScottNZ
81991ffae0 Add Tournament Island map to RA 2013-06-01 12:00:05 +12:00
ScottNZ
02a8b05aff Remove ability to have a single unit guard itself 2013-05-31 18:12:30 +12:00
ScottNZ
5f3563d83e Fix #3354: Guard queries trait from destroyed object 2013-05-31 18:10:56 +12:00
Chris Forbes
733f29d184 Merge pull request #3351 from reaperrr/patch-2
fixes d2k mod Sound weapon
2013-05-29 14:22:27 -07:00
reaperrr
92d72cc725 fixing d2k mod Sound weapon
I hadn't noticed that the D2K mod uses LaserZap as well. Added HitAnim and replaced BeamRadius with BeamWidth.
2013-05-29 22:21:44 +03:00
Matthias Mailänder
cc9a45daff set the order lag to 1 in single player games 2013-05-28 19:36:23 +02:00
reaperrr
ec36279ecb changes temp shellmap allied color to same as singleplayer/desert shellmap. 2013-05-28 17:55:59 +02:00
reaperrr
403f3745d9 undo change to desert shellmap allied color 2013-05-28 17:54:47 +02:00
Paul Chote
bf3d337913 Support alternate sequence ordering for ts and d2k. Fixes #3333. 2013-05-28 22:37:07 +12:00
Paul Chote
c149898592 Separate IBodyOrientation from render traits. 2013-05-28 22:37:06 +12:00
Paul Chote
53aa698491 Rename LocalCoordinatesModel -> BodyOrientation. 2013-05-28 22:34:59 +12:00
Paul Chote
e7aa6ce998 Change traits to use RenderSprites directly. 2013-05-28 22:34:58 +12:00
Paul Chote
07f3c0171d Allow turret recoil to be disabled. 2013-05-28 22:34:58 +12:00
Paul Chote
1d5f67cb6a Allow turrets to have a different facing count to the actor body. 2013-05-28 22:34:58 +12:00
Paul Chote
13f8d944d2 Split RenderSprites out of RenderSimple.
RenderSprites handles sprite drawing independently
from the extra bits needed by actors that only use
sprites.
2013-05-28 22:34:57 +12:00
Paul Chote
e029347355 Merge pull request #3344 from reaperrr/sequences
Splits sequences.yaml into multiple files.
2013-05-28 03:25:50 -07:00
Paul Chote
0d6f02abcc Merge pull request #3182 from Mailaender/d2k-font
Added a new d2k title font
2013-05-28 03:14:24 -07:00
Paul Chote
cae9bef807 Merge pull request #3310 from psydev/bleed
new cnc map: deterring democracy (expansion of east vs. west)
2013-05-28 03:12:08 -07:00
Paul Chote
1a307b45e4 Merge pull request #3323 from psydev/cnc
cnc balance - orca tweaked (improved), sam fix, viceroid
2013-05-28 03:01:30 -07:00
psydev
e885e428ae removed debugretaliate and debugautotarget entries 2013-05-27 15:22:22 -07:00
reaperrr
f22216aeb5 bring temp shellmap allied color in line with desert shellmap/original RA. 2013-05-27 22:02:34 +02:00
reaperrr
7a4d06efb7 make allied color on desert shellmap slightly more vibrant. 2013-05-27 22:01:50 +02:00
reaperrr
d4b21ce206 removing tech center sequence duplicate.
there already is a more 'sophisticated' entry in map.yaml.
2013-05-27 21:23:51 +02:00
reaperrr
592c76eb6e sequences.yaml split-up
Implementing cjshmyr's suggestions.
2013-05-27 21:03:12 +02:00
reaperrr
5ef3a49485 Splits sequences.yaml into multiple files.
The sequences.yaml is a behemoth, next to impossible to navigate other than through the search function. Splitting it into multiple files named after their category makes navigation easier for everyone.
2013-05-27 19:35:22 +02:00
psydev
f31912aa42 reduce orca reload time, similar to earlier levels
Orca was over-nerfed with respect to reload time. It was not effective at finishing off vehicles and buildings if it ran out of ammo, because it took way too long to reload.

Its reload time has been set to the old one, but now it reloads 2 pips at a time, so it is slightly slower than originally, but not by much. It's a bit OP when it loads one at a time, it seems.
2013-05-26 18:42:58 -07:00
psydev
84ab4ec854 gave SAM missile a spread, to match MSAM.
To increase power vs. large groups of helis
2013-05-26 18:42:57 -07:00
psydev
beb73befa3 return orca range to 5
Problem: orcas in small groups will sometimes get in each other's way and be stuck outside of range. The old range was 5, but this was to nerf large groups.

Orcas have been nerfed in several ways. One of the ways was to reduce their range from 5 to 4. However, when the range reduction is coupled with all the other nerfs, it seems a bit too much. Orcas are fairly vulnerable now that air defense has improved.
2013-05-26 18:42:56 -07:00
psydev
a0fa99037b increase viceroid sight & scan range
Viceroids are kind of passive. Would like them to be a bit more aggressive when units are nearby.
2013-05-26 18:42:56 -07:00
Chris Forbes
1c6a98ba93 Merge pull request #3337 from Mailaender/auto-target-buildings
Don't auto-target unarmed buildings in RA mod
2013-05-26 13:24:07 -07:00
Chris Forbes
2958c80e51 Merge pull request #3342 from baxtor/setup-z-fix
Fixes #2216 InstallShield .Z package decompression
2013-05-26 13:23:51 -07:00
Chris Forbes
33376b52bd Merge pull request #3312 from ScottNZ/forcemove
Add force-move
2013-05-26 13:23:14 -07:00
Chris Forbes
5af277d20e Merge pull request #3338 from Mailaender/gps-dots
Added GPS dot shadows again and removed pixelation
2013-05-26 13:21:47 -07:00
Chris Forbes
a863f556a6 Merge pull request #3339 from reaperrr/laserzap
LaserZap improvements rev.2
2013-05-26 13:20:15 -07:00
baxtor
629fe95ebd Fixes issue #2216 InstallShield .Z package decompression 2013-05-26 21:51:37 +02:00
reaperrr
547e64724f Changes name of LaserZap Explosion trait to HitAnim.
Additionally sets default HitAnim to null instead of "laserfire", to avoid crashes when neither a custom HitAnim nor laserfire anim are present.
2013-05-26 16:47:23 +02:00
reaperrr
afb67f8595 Replaces BeamRadius with BeamWidth.
Allows for more fine-grained control over laserbeam width.
2013-05-26 16:45:16 +02:00
Matthias Mailänder
413b476c01 this is not the repository for the open-ra.org website 2013-05-25 19:13:03 +02:00
Matthias Mailänder
113968aaa7 PNGs are not artsrc 2013-05-25 19:12:39 +02:00
Matthias Mailänder
770477f7a5 removed hard-coded GPS dot upscaling
please create a bigger sprite instead
2013-05-25 19:12:22 +02:00
Matthias Mailänder
90dc755ad4 fixed GPS dot shadow remapping, closes #3319 2013-05-25 19:11:12 +02:00
Matthias Mailänder
c74fa90305 don't auto-target unarmed buildings in ra mod 2013-05-25 15:39:04 +02:00
Chris Forbes
93c89a6ef5 Merge pull request #3286 from pchote/renderable-refactoring
Renderer refactoring - Renderables
2013-05-24 20:26:29 -07:00
Chris Forbes
9d074c6721 Merge pull request #3305 from pchote/mixdatabase
Parse XCC mix databases
2013-05-24 20:25:38 -07:00
Chris Forbes
835dfd0780 Merge pull request #3320 from reaperrr/improved_visuals_v2
several visual improvements
2013-05-24 20:24:18 -07:00
Chris Forbes
bd360dab54 Merge pull request #3330 from ScottNZ/balance
Oil derrick buff
2013-05-24 20:23:22 -07:00
Scott_NZ
81563ee5d6 Add capture bonus to OILB 2013-05-25 03:16:58 +12:00
Paul Chote
0dd8d7f7b6 Distinguish between classic and crc file hashes. Fixes #3306, #3328. 2013-05-24 21:10:41 +12:00
Paul Chote
a11e403084 Draw turrets, spinners, rotors at ZOffset +1 relative to the body. 2013-05-24 18:18:47 +12:00
Paul Chote
d048d083c4 Convert px z offsets to world coords. 2013-05-24 17:41:55 +12:00
Paul Chote
437de8e884 Fix FTRK turret position. 2013-05-23 21:39:57 +12:00
Paul Chote
25dd8d9fa7 Fix aircraft shadow render order. 2013-05-23 21:25:11 +12:00
psydev
debfdfbad2 fix bialystok map--removed viceroids. && renamed. 2013-05-20 20:14:52 -07:00
psydev
923df5eafe dd: changed to 1vs1. dd+: added more land 2013-05-20 19:58:37 -07:00
psydev
38fcfe9a83 fixed map: the_hourglass. Creeps & biolab problems. 2013-05-20 19:20:31 -07:00
reaperrr
f85fce2ed1 adding myself to AUTHORS 2013-05-20 21:34:00 +02:00
reaperrr
e1669ecd70 construction yard with shadow
It always annoyed me that the CY had no shadow at all. This adds some at the edges.
2013-05-20 15:48:59 +02:00
reaperrr
3cc3a7dd65 crates with shadow and wood crate palette fix
- westwood never bothered converting the wood crate to RA's palette. I did.
- shadows added to both wood crate and silver crate.
2013-05-20 02:14:02 +02:00
reaperrr
2abb6421fe gap gen & mgg with shadows 2013-05-20 02:03:53 +02:00
reaperrr
579bafd439 Yak shp fix
Originally, the 2nd frame of yak.shp used greyscale for the tail fins instead of remap.
2013-05-20 02:01:09 +02:00
reaperrr
0719b89cdf adjusting explosion position
- originally, frag1 was not centric and appears to the right of the impact/exploding vehicle.
- fball1 appeared quite a bit below the impact point. This moves it up a little.
2013-05-20 01:58:53 +02:00
reaperrr
e3391d1535 changes light tank bullet to 50cal.shp
a better match for the low damage per shot, imo.
2013-05-20 01:54:54 +02:00
reaperrr
04fd2e6d0f changes civ pistol explosion to single piff
multiple piffs from a civilian pistol shot? Looks a little strange ;)
2013-05-20 01:53:07 +02:00
reaperrr
fbe06df9e6 silo gfx improvements
- fixed some minor errors on the undamaged frames.
- added shadow.
2013-05-20 01:49:28 +02:00
reaperrr
4e7f04da5f airfield gfx improvements
- added separate idle shp to allow using all 8 frames while keeping idle/active separate.
- replaced some empty/background pixels inside tower with shadow.
2013-05-20 01:47:42 +02:00
psydev
4e3da1c081 updated & renamed rock_canyon. && Fewer players. 2013-05-19 00:43:27 -07:00
Paul Chote
d9fab238d5 Show individual packages in the asset browser list. 2013-05-19 19:16:15 +12:00
Paul Chote
de3d4da000 Allow mounted IFolders to be queried. 2013-05-19 19:14:20 +12:00
psydev
9067c2c842 updated 2 maps to allow for fewer players 2013-05-18 23:39:21 -07:00
psydev
69ae57b246 removed old version of east vs. west 2013-05-18 23:13:48 -07:00
psydev
6d3cf83052 update & rename east vs. west; made expanded ver. 2013-05-18 23:13:48 -07:00
psydev
e6745f80ce new map: expansion of east vs. west redux 2013-05-18 23:13:47 -07:00
Paul Chote
f7aca32e0e Fix unit turret vs War Factory roof rendering. 2013-05-19 11:10:04 +12:00
Scott_NZ
ac430bd3bc Add force-move 2013-05-18 17:52:36 +12:00
Matthias Mailänder
be3b18057a replace PackageContent yamls with XCC database 2013-05-18 07:34:49 +02:00
Curtis Shmyr
9772f9b770 Merge pull request #3311 from chrisforbes/placebuilding-resolve
validate building placement in ResolveOrder as well
2013-05-17 21:14:53 -07:00
Chris Forbes
4d85605cca fix validation of startgame order 2013-05-18 15:58:16 +12:00
Chris Forbes
96ecb7c232 validate building placement in ResolveOrder as well 2013-05-18 15:47:11 +12:00
Paul Chote
112034a41d Allow hashes to be accepted as valid mix filenames. 2013-05-18 03:59:46 +12:00
Paul Chote
9dc3f4bf2d Add IFolder.AllFileNames() for listing the filenames in a package. 2013-05-17 23:52:02 +12:00
Paul Chote
974e9b3325 Write XCC compatible mix files. 2013-05-17 23:46:29 +12:00
Paul Chote
47f078ec3a Parse XCC global mix database. 2013-05-17 23:46:24 +12:00
Paul Chote
8817fd2cc5 Parse XCC local mix database. 2013-05-17 23:46:24 +12:00
Paul Chote
50b87b580a Remove 12 character limit from mix content names (XCC compatibility). 2013-05-17 23:46:19 +12:00
Paul Chote
170e14546a Convert NukeLaunch to world coordinates. 2013-05-17 18:12:29 +12:00
Paul Chote
4b4c1b71df Convert GpsSatellite to world coordinates. 2013-05-17 18:12:29 +12:00
Paul Chote
e122797a68 Convert CrateEffect to world coordinates. 2013-05-17 18:12:29 +12:00
Paul Chote
4d8dd2db7d Convert Corpse to world coordinates. 2013-05-17 18:12:29 +12:00
Paul Chote
3e1c1096c2 Convert Smoke to world coordinates. 2013-05-17 18:12:29 +12:00
Paul Chote
c6e6977bf6 Convert RallyPoint to world coordinates. 2013-05-17 18:12:28 +12:00
Paul Chote
85056e1c6c Convert bridges to world coordinates. 2013-05-17 18:12:28 +12:00
Paul Chote
57142fbb8d Convert indicator overlays to world coordinates. 2013-05-17 18:12:28 +12:00
Paul Chote
e382dc9b71 Remove obsolete functions from WorldRenderer. 2013-05-17 18:12:28 +12:00
Paul Chote
e1290dca47 Round PxPosition to the nearest pixel. 2013-05-17 18:12:28 +12:00
Paul Chote
ca5f2c3785 Split Renderable into an IRenderable interface and SpriteRenderable implementation. 2013-05-17 18:12:28 +12:00
Paul Chote
9b7aaebcbc Rewrite RenderCargo -> WithCargo.
Now uses world coordinates and properly displays
cargo at all facings.
2013-05-17 18:12:27 +12:00
Paul Chote
462478afdf Convert building rendering to world coordinates. 2013-05-17 18:12:27 +12:00
Paul Chote
d7e6125dd7 Convert ChronoshiftPower to world coords. 2013-05-17 18:12:27 +12:00
Paul Chote
79cbb7f729 Convert Parachute to world coords. 2013-05-17 18:12:27 +12:00
Paul Chote
fe716e76a7 Convert AnimationWithOffset to world coords.
Animations (via Actor.CenterPosition) now
understand Altitude, so there is potential for
mis-positioned animations if any existing altitude
hacks were missed.
2013-05-17 18:12:27 +12:00
Paul Chote
fb17654ea0 Convert Renderable to World Coordinates internally. 2013-05-17 18:12:26 +12:00
Paul Chote
d878c96343 Change Renderable to take centered positions. 2013-05-17 18:12:26 +12:00
Paul Chote
d103a187f6 Make the guts of Renderable private. 2013-05-17 18:12:26 +12:00
Paul Chote
7719ad1f2d Move Renderable into its own file. 2013-05-17 18:12:26 +12:00
Chris Forbes
cfcccb590d Merge pull request #3295 from reaperrr/patch-3
adding ImpactSounds to many weapons + other changes
2013-05-16 15:46:30 -07:00
Chris Forbes
8b4814e399 Merge pull request #3233 from ScottNZ/follow
Add unit following/guarding
2013-05-16 14:02:32 -07:00
Chris Forbes
3f1805c1ea Merge pull request #3192 from Mailaender/asset-browser
Created an Asset Browser to view and convert Sprites
2013-05-16 12:57:15 -07:00
Chris Forbes
56067e4d4f Merge pull request #3281 from Mailaender/sdl-openal-mirrors
Added OpenAL and SDL Windows dependency DLLs to mirrors
2013-05-16 12:55:31 -07:00
reaperrr
7e3f90ee1d sound and explosion changes
Explosion sounds have been noticeably absent from the RA mod, artillery weapons only used the weak kaboom12, most other weapons had no ImpactSound at all, making even larger battles sound 'empty'.

Now, explosive artillery weapons (155m, 8Inch, SubMissile) use art-exp1 as animation and kaboom22 as impact sound. Furthermore, most cannons and rockets now use kaboom12 as ImpactSound. In one or two cases kaboom15 is used instead. Furthermore, for consistency Trail: smokey was disabled on Dragon and ChronoTusk.
2013-05-16 21:10:54 +03:00
reaperrr
c71bdf32e1 adds artillery_explosion
Technically unnecessary, but using self_destruct on regular weapons in weapons.yaml might be a little confusing for modders. artillery_explosion is a more straight-forward label for art-exp1.
2013-05-16 20:52:19 +03:00
Scott_NZ
6e5d58379f Add unit guarding 2013-05-16 21:23:58 +12:00
Chris Forbes
a2a3def685 Merge pull request #3291 from reaperrr/patch-2
Adjusting infantry run animations to speed
2013-05-15 13:50:25 -07:00
reaperrr
01e6c14025 Adjusting infantry run animations to speed
I noticed that by default the run and prone-run animations are way too fast for the infantry movement speed. I used Tick values to fix this, taking into account individual speed. I used Tick: 80 for Speed: 5, 100 for speed 4 and 120 for speed 3. Some fine-tuning may be needed, but it already looks much better than before.
2013-05-15 23:43:20 +03:00
Matthias Mailänder
2d1bc7bac8 adapt asset browser for R8 files 2013-05-15 18:13:40 +02:00
Matthias Mailänder
6262aa846f wire up the import from PNG to SHP button
store everything in user directory to not junk up the game
and for file permissions on Mac/Linux when installed
2013-05-15 18:13:39 +02:00
Matthias Mailänder
8f822f6cad game asset browser UI polishing
- display file extension
- show total frame count
- dark background for preview
2013-05-15 18:13:39 +02:00
Matthias Mailänder
f14441d2f4 added MinimumThumbSize to ScrollPanelWidget
closes #2865
2013-05-15 18:13:39 +02:00
Matthias Mailänder
2762c245ee fix Makefile
OpenRA.Mods.RA.dll now depends on the Utility.exe
2013-05-15 18:13:39 +02:00
Matthias Mailänder
0c1b6f21b9 add a dump everything to PNG options for @xanax 2013-05-15 18:13:39 +02:00
Matthias Mailänder
7f40f59d85 added button to extract the selected SHP and convert it to PNG 2013-05-15 18:13:39 +02:00
Matthias Mailänder
6a6776754b added .mix file support for game asset browser 2013-05-15 18:13:39 +02:00
Matthias Mailänder
4a1ebb69c3 remove duplicate ra-desert bridge tiles 2013-05-15 18:13:39 +02:00
Matthias Mailänder
3ae61c5f8c added the game asset viewer to d2k mod 2013-05-15 18:13:38 +02:00
Matthias Mailänder
881fcf1191 added in-game SHP viewer with frame slider 2013-05-15 18:13:33 +02:00
Chris Forbes
ce021a4f62 Merge pull request #3283 from pchote/turret-rendering
Unify turret rendering traits
2013-05-15 03:30:17 -07:00
Chris Forbes
2207c391d6 Merge pull request #3265 from Mailaender/building-building-building
Avoid notification spam when the build palette is left-clicked
2013-05-15 03:27:54 -07:00
Chris Forbes
e97638ce86 Merge pull request #3282 from pchote/renderer-refactoring
Renderer refactoring - SheetBuilder
2013-05-15 03:26:16 -07:00
Paul Chote
f5d59fab22 Fix #2106.
The current tilesets only use only ~3% of the
available sheet space, so a future patch may want
to look into decreasing the terrain sheet size to
save some GPU memory.
2013-05-15 20:36:30 +12:00
Paul Chote
4ebe547a05 Allow SheetBuilder to generate 1/2/4 channel sheets.
This makes the SpriteFont RBGA sprite hack explicit,
and adds a DualIndexed option to be used by the voxel
renderer.
2013-05-15 20:18:41 +12:00
Paul Chote
28d4df355d Tidy Sprite. 2013-05-15 20:18:41 +12:00
Paul Chote
891158ca44 Allow SheetBuilder to throw an exception on sheet overflow.
This removes unnecessary checks from TerrainRenderer
and will be used in the voxel renderer.
2013-05-15 20:18:41 +12:00
Paul Chote
c13c989fe8 Allow sheet contents to be exported to bitmap. 2013-05-15 19:24:19 +12:00
Paul Chote
7d543abb92 Always store Sheet data as bytes. 2013-05-15 19:24:19 +12:00
Paul Chote
6dd62d7806 Fix Animation formatting (no code changes). 2013-05-15 19:24:18 +12:00
Paul Chote
d70a5aff6a Tidy TileSet formatting (no code changes). 2013-05-15 19:24:18 +12:00
Paul Chote
ccbfacec62 Tidy TerrainRenderer formatting (no code changes). 2013-05-15 19:24:18 +12:00
Paul Chote
cca784752b Make WithSpinner consistent with WithTurret. 2013-05-15 19:17:33 +12:00
Paul Chote
c91f0dfd2f Merge the 3 turret rendering traits into WithTurret. 2013-05-15 19:17:32 +12:00
Paul Chote
b7ea36b858 Merge pull request #3176 from Mailaender/travis-xbuild
Added xbuild to Travis configuration
2013-05-15 00:10:54 -07:00
Paul Chote
484b726373 Merge pull request #3253 from Mailaender/super-oil-derricks
Cleaned up the Red Alert map directory
2013-05-15 00:06:42 -07:00
Paul Chote
44336b1ea5 Merge pull request #3256 from Mailaender/debug-trait-removal
Removed all Debug traits
2013-05-15 00:05:44 -07:00
Paul Chote
63c71bf3f5 Merge pull request #3234 from Mailaender/ts-mix-filehashes
Added support for Tiberian Sun MIX filename hashes
2013-05-15 00:02:29 -07:00
Paul Chote
3500f4b3e3 Merge pull request #3193 from Mailaender/shp-ts
Added support for Tiberian Sun/Red Alert 2 SHP format
2013-05-14 22:29:16 -07:00
Chris Forbes
942946f1b1 Merge pull request #3266 from psydev/bleed
D2K - added forgotten_path map by Holloweye
2013-05-13 23:01:15 -07:00
Chris Forbes
e8db3c2e77 Merge pull request #3263 from Mailaender/alliance-checks
Removed inconstencies in alliance checks
2013-05-13 22:50:17 -07:00
Matthias Mailänder
c14dbb29c9 avoid notification spam when the build palette is left-clicked
fixes #2361
2013-05-13 23:43:37 +02:00
Matthias Mailänder
f05760b986 Don't apply palette modifiers on effects in C&C.
fixes #2778 unit veterancy symbol cloaking together with unit
2013-05-13 23:42:50 +02:00
Matthias Mailänder
0576937fd6 render powerdown indicator for observers 2013-05-13 23:42:50 +02:00
Matthias Mailänder
2c250ed700 fixes #3181 GPS dots not visble in spectator mode 2013-05-13 23:42:49 +02:00
Matthias Mailänder
95ec993134 Don't render unit ranks you are not supposed to see. 2013-05-13 23:42:49 +02:00
Matthias Mailänder
f4a4afbb69 make cash ticks consistent with per-player shroud and observers 2013-05-13 23:42:49 +02:00
Matthias Mailänder
05d187d327 consistent spy disguise radar color override in spectator mode 2013-05-13 23:42:49 +02:00
Matthias Mailänder
904c6b76b5 fixes #3178 invisible superpower charge bars in spectator mode 2013-05-13 23:42:49 +02:00
Matthias Mailänder
a4c69f0a74 try pulling OpenAL and SDL Windows libraries from mirrors 2013-05-13 23:06:47 +02:00
Chris Forbes
b301bc1d79 Merge pull request #3267 from Mailaender/ubuntu-travis
Tried to fix Travis CI problems
2013-05-13 04:20:02 -07:00
Chris Forbes
c9255a2873 Merge pull request #3276 from pchote/release-changelog
Update CHANGELOG for release-20130514.
2013-05-13 03:40:56 -07:00
Paul Chote
ca4e99993d Update CHANGELOG for release-20130514.
Thanks to @Mailaender and @psydev for collating
the changes, and to eyen from IRC for copy editing.
2013-05-13 22:31:19 +12:00
Scott_NZ
37fe6009ee Increase the health of OILB to 1000 and the amount of cash its CashTrickler brings in to 100 2013-05-13 00:18:09 +12:00
Matthias Mailänder
37069652c0 fix dependency installation for Travis CI
looks like the Ubuntu repositories don't update automatically
2013-05-12 07:37:39 +02:00
psydev
0c12dcb3c3 added forgotten_path map by Holloweye 2013-05-11 21:30:13 -07:00
Matthias Mailänder
a606ea4a80 Merge pull request #3264 from ScottNZ/sequences
Remove duplicate gnrl sequences definition
2013-05-11 05:37:45 -07:00
Scott_NZ
792d61ad8c Remove duplicate gnrl sequences definition 2013-05-12 00:35:53 +12:00
Matthias Mailänder
84d595bcde Merge pull request #3258 from pchote/drop-desync-fix
Revert "Players and bots surrender on disconnect."
2013-05-10 23:56:03 -07:00
Paul Chote
426467a38f Revert "Players and bots surrender on disconnect."
This reverts commit 500c24fbaf.
2013-05-11 18:34:16 +12:00
Matthias Mailänder
6aae5d2ad6 removed now redundant debug player-resource traits 2013-05-10 22:34:43 +02:00
Matthias Mailänder
e38aea276c removed now redundant debug aircraft traits 2013-05-10 22:32:05 +02:00
Matthias Mailänder
99d4d07cd5 removed now redundant debug auto-target traits 2013-05-10 22:31:54 +02:00
Matthias Mailänder
c75f758f8d deflated room-convergence because of the custom anti-rush rules 2013-05-10 19:23:02 +02:00
Matthias Mailänder
21775df1b8 deflated KOTH maps because of custom yaml rules
removed the mission building 3/4th scaling as it looked strange
2013-05-10 19:23:02 +02:00
Matthias Mailänder
0a49153dbf deflated ice woods because of custom creep building yaml rules 2013-05-10 19:23:01 +02:00
Matthias Mailänder
e10a5e591e deflated doughtnut-hole and stripped the abomb changes to diff 2013-05-10 19:23:01 +02:00
Matthias Mailänder
022aeec41a deflated center of attention redux 2
as it has custom oil derrick rules (although not busted)
2013-05-10 19:23:01 +02:00
Matthias Mailänder
a396d1e170 moved boomer test map into the performance benchmark mod 2013-05-10 19:22:42 +02:00
Matthias Mailänder
f7668f3b9d Made oil derricks remap to player colors on capture everywhere
by removing bogus custom yaml rules from some island maps
and enforcing the policy to extract all those oramap files.

fixes #3022
2013-05-10 18:49:28 +02:00
Matthias Mailänder
37f5b471fc removed strange "\"\\\"''\\\"\"" junk from black-gold map 2013-05-10 18:42:25 +02:00
Matthias Mailänder
956d68a84b removed custom yaml rules from bad-neighbors
They are redundant to the sniper/church stuff we already have.
Weapon rules had broken indentions which made this not work.
2013-05-10 18:32:21 +02:00
Matthias Mailänder
7fb85e7abb added support for Tiberian Sun MIX filename hashes
closes #3227
2013-05-08 21:08:24 +02:00
Matthias Mailänder
2d685ab07d added SHP(TS) support by @katzsmile 2013-05-05 19:19:16 +02:00
Matthias Mailänder
52797e8b79 added a new d2k title font
from http://www.d2kplus.com/font.php
by http://forum.dune2k.com/user/2251-d2k-sardaukar/
2013-04-28 08:07:39 +02:00
Matthias Mailänder
3c4a68bfbc add xbuild to travis configuration
to check if we can still compile with MonoDevelop/Visual Studio
2013-04-27 15:51:07 +02:00
540 changed files with 35884 additions and 7992 deletions

View File

@@ -6,17 +6,18 @@ language: c
# Make sure build dependencies are installed.
install:
- sudo apt-get install mono-gmcs cli-common-dev libgl1-mesa-dev libsdl1.2-dev libopenal-dev
- sudo apt-get update && sudo apt-get install mono-gmcs cli-common-dev libgl1-mesa-dev libsdl1.2-dev libopenal-dev
# Run the build script which will automatically call RALint.
# Run the build script which will automatically call RALint and ensure that the IDE project files are still valid.
script:
- make all
- xbuild
# Only watch the development branch.
branches:
only:
- bleed
# Notify developers when needed.
# Notify developers when build passed/failed.
notifications:
irc: "irc.freenode.net#openra"

118
AUTHORS
View File

@@ -1,55 +1,73 @@
AUTHORS
OpenRA wouldn't be where it is today without the
hard work of many contributors.
The OpenRA developers are:
* Alli Witheford (alzeih)
* Caleb Anderson (RobotCaleb)
* Chris Forbes (chrisf)
* Curtis Shmyr (hamb)
* Daniel Hernandez (Mancano)
* Matthew Bowra-Dean (beedee)
* Mike Bundy (kehaar)
* Chris Forbes (chrisf)
* Curtis Shmyr (hamb)
* Paul Chote (pchote)
* Matthias Mailänder (Mailaender)
* ScottNZ
Previous developers included:
* Alli Witheford (alzeih)
* Caleb Anderson (RobotCaleb)
* Daniel Hernandez (Mancano)
* Megan Bowra-Dean (beedee)
* Mike Bundy (kehaar)
* Robert Pepperell (ytinasni)
Also thanks to:
* Akseli Virtanen (RAGEQUIT)
* Andrew Riedi
* Barnaby Smith (mvi)
* Bellator
* Bugra Cuhadaroglu (BugraC)
* Christer Ulfsparre (Holloweye)
* Cody Brittain (Generalcamo)
* Daniel Derejvanik (Harisson)
* Danny (Dan9550)
* Erasmus Schroder (rasco)
* Igor Popov (ihptru)
* Iran
* James Dunne (jsd)
* Jeff Harris (jeff_1amstudios)
* Jes (-Jes-)
* Joakim Lindberg (booom3)
* JOo
* Kenny Hoxworth (hoxworth)
* Krishnakanth Mallik
* Kyrre Soerensen (zypres)
* Lawrence Wang
* Lesueur Benjamin (Valkirie)
* Mark Olson (markolson)
* Matthew Gatland (mgatland)
* Matthias Mailänder (Mailaender)
* Maarten Meuris (Nyerguds)
* Max Ugrumov (katzsmile)
* Max621
* Nukem
* Paolo Chiodi (paolochiodi)
* Paul Dovydaitis (pdovy)
* Psydev
* Raymond Martineau (mart0258)
* Riderr3
* Sascha Biedermann (bidifx)
* Tim Mylemans (gecko)
* Tirili
* Tristan Keating (Kilkakon)
* Vladimir Komarov (VrKomarov)
* Akseli Virtanen (RAGEQUIT)
* Andrew Perkins
* Andrew Riedi
* Andreas Beck (baxtor)
* Barnaby Smith (mvi)
* Bellator
* Bugra Cuhadaroglu (BugraC)
* Christer Ulfsparre (Holloweye)
* Cody Brittain (Generalcamo)
* D2k Sardaukar
* Daniel Derejvanik (Harisson)
* Danny (Dan9550)
* Erasmus Schroder (rasco)
* Frank Razenberg (zzattack)
* Igor Popov (ihptru)
* Iran
* James Dunne (jsd)
* Jeff Harris (jeff_1amstudios)
* Jes
* Joakim Lindberg (booom3)
* JOo
* Kenny Hoxworth (hoxworth)
* Krishnakanth Mallik
* Kyrre Soerensen (zypres)
* Lawrence Wang
* Lesueur Benjamin (Valkirie)
* Mark Olson (markolson)
* Matthew Gatland (mgatland)
* Maarten Meuris (Nyerguds)
* Max Ugrumov (katzsmile)
* Max621
* Nukem
* Olaf van der Spek
* Paolo Chiodi (paolochiodi)
* Paul Dovydaitis (pdovy)
* Psydev
* Raymond Martineau (mart0258)
* Reaperrr
* Riderr3
* Sascha Biedermann (bidifx)
* Tim Mylemans (gecko)
* Tirili
* Tristan Keating (Kilkakon)
* Vladimir Komarov (VrKomarov)
* Wuschel
Finally, special thanks goes to the original teams
at Westwood Studios and EA for creating the classic
games that inspired the creation of OpenRA.
Red Alert, Command and Conquer, and related
trademarks belong to Electronic Arts Inc. and are
used without permission.
Past developers included:
* Paul Chote (pchote)
* Robert Pepperell (ytinasni)

576
CHANGELOG
View File

@@ -1,312 +1,274 @@
NEW:
Engine:
Changed OpenRA now uses Mono.Nat 1.1.0 instead of it's own limited UPnP router detection and port forwarding code (should support more devices now)
Changed Detect NAT devices only once on startup (removes lag when creating a new game). Disable the Automatic portforwarding checkbox if no UPnP enabled devices are discovered
Added more UPnP user settings: boolean Server.VerboseNatDiscovery for additional debug messages in server.log, integer Server.NatDiscoveryTimeout in miliseconds
Now using SharpFont instead of a custom patched Tao.FreeType in close collaboration with upstream
Dedicated servers:
Misc dedicated server improvements
Fixed AI orders misinterpreted as an exploit on dedicated servers
Added option to disable bots on servers (including dedicated)
Added a dedicated server launch script
Fix bots being able to become admin status on dedicated servers
Allow MOTD to be fetched from file for dedicated servers
Fixed issue with pinging the master-server when dedicated machines did not show up until players connected
Allow classic left-click mouse orders (optional)
New hotkeys:
Hotkeys are now user-configurable via settings.yaml and the GUI
Move viewport to selection (default: home or double tap group number)
Switch cursor to sell / repair / powerdown
Added per player shrouds (units can't shoot enemies covered in unexplored terrain)
General game lobby improvements:
Add "Assign Teams" lobby drop down button for admins to randomize teams
Removed mouse capture while composing text in chat
Asynchronous loading of minimaps in the map chooser dialog
Map chooser polish - added map type and author to lobby
Spawnpoint map tooltips now display players name in lobby
Player colors are now reverted after changing to a map which does not lock colors
Fixed word wrap when chatting ingame
Added a tooltip that shows provided and drained power
Changed scroll velocity in map chooser to be better suited for mouse scrolling
Fixed grounded airplane not landing properly when ordered to another airstrip or service depot
Don't hardcode READY/HOLD and custom prerequisites anymore for easier translation
Fixed (removed) sound attentuation for PlayNotification sounds - fixes the Extreme cashtick sounds
Replaced admin indicator icon with bold formatted player name
Connecting to a different mod will show an appropriate error message (was unknown map before)
Fix problems with multi tap detection
Fixed floating point inconsistencies to reduce desyncs
Civilians now panic when in combat
Units with turrets are now displayed correctly when parachuting
Print the total number of values extracted from random number generator stream to sync report for debugging purposes
Allow strides for sequence definitions (if you don't want to use the full SHP animation length, optional)
Fixed a crash when multiple clients on the same machine attempted to write replay files at once
Refactored palette remapping rendering code as a preparation for upcoming Voxel support
Fixed crashes when attempting to watch replays in non-RA mods
Added support for randomized weapon reports
Added pathfinder Debug overlay visualization A* search cost per cell in player color tints
Attempt to improve A* algorithm
Added hidden settings to the GUI:
Cap Framerate checkbox and FPS limit
Autoplay music after map load
Sound engine backend drop down selection box
Performance graph text update rate
Show bot debug message checkbox
Slow trait report threshold slider
We now use Travis Continous Integration and enforce a strict no compiler warnings policy
Adapted to dpkg's "--instdir"-like options to allow installations of different openra next to each other on Debian GNU/Linux
Better AI Bots:
Removed the AI attack bottleneck where the whole squad had to be killed completely before a new one was started
Units will attack/rush/retreat based on own and opponents health/speed using fuzzy logic while staying in squads
Protect harvesters and base buildings when they are attacked
Place defense building in the direction of the enemy
Place refineries in vicinity to the nearest ressource fields
Support for aircraft and ships
Try to kill off unprotected enemy targets with aircraft
Don't ignore percentage to build for units in yaml configuration
Will maintain adequate numbers of refineries and silos
Will now try to use super weapons
Added a cap for maximum building count
Give the users some hints in exception.log when crashing due to desyncs
Log projectile destination (innaccuracy calculation) and nextScanTime (auto-target) to debug.log to investigate desyncs
Added Shroud, CreatesShroud and RevealsShroud as well as AutoTarget and ScaredyCat (panicking infantry) to syncreport.log
Refactoring: cleaned up code duplication to ease the addition of new renderers
Added a new reset exploration developer cheat (like hide-map crate, opposite of give exploration)
Support for classic RA and C&C mods (not yet packaged):
Speedup production queues if multiple of the same building exist
Engineer capture rules which require/do damage at first
Fixed CustomBuildTimeValue related desync
Added a trait to turn off fog of war
Separated spy disguise and infiltration for thief
Fixed tooltips for fake buildings
Fixed a desync with regards to auto-target and Cloak.IsVisible
Fixed crash when performing keyboard orders on destroyed actors
Added additional server lobby buttons
Added burning trees support for RA and C&C
Fixed unit ranks not showing in replay viewer
Fixed crash caused by capturing a dead actor
AI support power activiation needs to go through order manager
Ship a proper SDK for modders
Added documentation within .cs files for modders
Documentation set to push itself to github wiki for new playtests or releases
Added a DrawRangeCircleWithContrast method to go along with DrawRangeCircle
Added cancel chat with escape
Added double click to join servers in browser
Desura compatiblity stuff
Added sync to carpet bombing
Added announcer voice upon game start
Added engine support to automatically download a missing map from content.open-ra.org when joining a lobby
Added work towards an ingame IRC client
Engine changes in preparation for Voxel support - removed Turret and PVecFloat cruft (code refactoring)
Changed ThrowsParticle now uses world coordinates and implements a simpler model using a cubic lerp
Fixed a crash that may occur in lobbies
Made the version mismatch mechanic configurable in the options
Fixed color selector/team selector closing when another player in the lobby made a similar change
Fixed LaserZap and TeslaZap to account for altitude
Send players and bot counts to master server as different fields
Sort servers by players in the server browser
Fixed cloak units being invis to spectators
Fixed paratroopers revealing shroud after death
Added a Force Desync cheat for the upcoming automatic crash reporter
Changed throw specific exception on missing sound definitions
Fixed MCV create not being given if normal SelectionShares was zero
Changed split multiple-production-queue widget logic into its own file
Added moving average to contrails to smooth them when aircraft are circling
Added attack traits into the syncreport.log
Added a --docs flag for OpenRA.Utility
Removed the tileset builder again from packages
Editor:
Map editor now saves and loads from OpenRA user folder
Fixed map editor failing to import legacy maps
Template categories are now ordered by what is specified in the tileset yaml
Added terrain category types to RA Snow tileset
Added terrain category types to RA Desert tileset
Fixed terrain categories for two river tiles in RA Temperate tileset
Fixed wrong palette remapping for neutral buildings
Show full name + version of the loaded mod in the toolbar
Added a help menu with useful tips
Added icons to the menu with tooltips
Added a toolbar to the editor
Added total cash count to editor status bar
Added a ruler to the editor
Added an eraser tool
All mods:
Added an ingame credits menu.
Added descriptive error messages when connecting to a server fails.
Added guard ability to combat units ('d').
Added force-move ability (hold alt to prioritize move/crush over attack/harvest).
Added change-mod button to the asset download screen.
Added kick-ban to the server lobby.
Added a button to fill empty lobby slots with bots.
Added a renderer geometry visualization to the debug/cheats menu.
Fixed exploit allowing arbitrary building placement.
Fixed exploit allowing a malicious client to steal admin rights.
Fixed glitches relating to passenger-carrying units being killed.
Fixed a crash related to bridges on certain maps.
Fixed a crash related to the color picker.
Fixed a glitch where aircraft circle in the air after being killed.
Fixed a glitch where an uncontrollable MCV is left after a construction yard is killed.
Fixed civilians panicking when healed.
Fixed visibility of additional spectator information (progress bars, spy ownership, etc).
Fixed excessive "Building" notifications when rapidly pressing icons in the sidebar.
Fixed units continuing to attack a building after it was captured.
Fixed units chasing a chronoshifted unit across the map.
Fixed units not being able to attack buildings from some angles.
Removed some developer-specific options from the settings menu.
Red Alert:
Automatic pausing when in single player missions
Added Allies03 mission
Added Allies04 mission (has sunset weather)
Added Soviet01Classic mission
Added MonsterTankMadness mission
Added Survival01 mission
Added Desert tileset - original and new tiles ported from Tiberium Dawn by Harrison, code by Matt
Fixed empty camo pillboxes in shellmap
Fixed graphics for craters for Snow maps (art by MrFibble)
Ice Floe actors work in maps now (sequences were missing)
Brought back music track "Mud" into the playlist
Balance:
Base defenses now do less damage to main base structures
Pillbox armor changed from wood to heavy
Construction Yard armor changed from heavy to wood with a health boost
Construction Yard sell ability removed to combat MCV crate exploit
Superweapons now have more health
Tanya no longer fires her pistols at main base structures
Tanya no longer has a C4 demolition cursor for barrels
Added missing line of sight for Chronotank
Chronotank damage upped from 20 to 30 per rocket and damage improved vs heavy armor
Demo truck cost from 1500 to 2500
Demo truck health from 110 to 50
Demo truck speed from 8 to 6
Disguised spies are now invisible under enemy GPS
Heroes (Tanya, Volkov) and missile silos are now limited to one per player
Volkov got a new armor type: Cybernetic, for individual balancing
More health points for Volkov to make him the 4tnk equivalent of infantry
Medics can't heal Volkov, send him to repair pad or mechanic instead
Dogs can't eat Volkov and tanks need two attempts to crush him
Added Mechanic to Allies
Added Mobile Gap Generator to Allies
Oil derricks are now repairable by engineers
Placed mines (AP andAT) now use the Cloak trait, and minelayers will detect mines
Added duration indicators for units under Chronosphere and Iron Curtain effects
Nukes can destroy ore wells
Reworked Volkov death sounds/animations (Cyborg version from German RA)
Replaced supply truck graphics with aftermath SHP which has a shadow (via Arda)
Added hotkeys for aircraft, vehicles, ships, infantry
Modified the hotkeys
Fixed visual glitch where war factory closed its door prematurely
Fixed broken shadows on GPS dot
Fixed Chronotank teleport causing a desync
Fixed replay crash when a Chronotank tries to teleport
Fixed Chronotank death causing a crash before teleporting
Fixed crash caused by Demo Truck destruction
Added in-game player statistics for observers
Added support for map difficulties
Renamed airstrip to airfield in tooltip.
Don't remap civilian buildings/fields in-game.
Added Random Map button to the lobby
Added Crates on/off checkbox to the lobby
Gems are now rendered slate blue on the radar minimap
Shroud creators now temporarily create shroud
Added support for Nyerguds music upgrade pack
Added unused actors for legacy map import (thief, chan, general, mobile radar jammer)
You can now define the LoadScreenImage in mod.yaml and easily replace it with your own
Made conyard buildable with Build Everything cheat
Brought back the diplomacy menu, however diplomacy can only be changed in free for all games (Fragile Alliances option)
Added a shroud selector when in spectate mode
Added an improved ingame chat
Fixed maps Chaos Canyon and Bomber John
Added map: Room Convergence (Sunny Sproket)
Added map: Ghost Town (hamb)
Added map: Bad Neighbors (Nukem)
Updated map: Bombardment Islands (czech army)
Added external capturing for engineers (like C&C Generals).
Added collection animation for parabomb crate.
Fixed artwork glitches in several units and buildings.
Fixed incorrect player color on captured oil derricks on some maps.
Fixed support powers being available after winning a game.
Fixed sniper damage vs walls.
Fixed destroyable ore mines.
Tweaked some weapon explosions.
Sniper health reduced.
Iron curtain duration increased to 20 seconds.
Chronoshift duration decreased to 20 seconds.
Mobile Radar Jammer range increased.
Mechanic repair amount increased.
Decrease the infantry run animation speed.
Increased oil derrick health and cash rate. Added initial capture bonus.
Added maps: Artemis, Fort Lonestar, Pluto, Sahara, Survival02, Tournament Island, Zeus.
Updated maps: Ares, Apollo, Dionysus, Doughnut, Poseidon.
Renamed maps: Ares: National Park -> Dionysus.
Removed maps: Hotzone, Mjolnir-2, Seaside.
C&C:
Construction yard now has a build placement range around it where structures must be placed within, and must be within 5 tiles of another structure
Removed custom build time for tiberium refinery to avoid desync crashes
Explosions given names instead of just numbers. Added some explosions that came with the game but were unused.
Corrected music.yaml track titles
Fix wrong civilian field remapping in-game
Slowed down structure building animations
Changed sounds for cloaking and building destruction
Tanks UnitExplodeSmall now by default
Blue tiberium now poisons infantry
Levelup crate removed
Crate lifetime increased
Added Hard AI
Temporarily disabled AI building of HPAD and FIX since they can't use them
AI modified to cope with construction yard build radius
AI to build maximum of 1 silos to build to save space
Hospital fan spins slower
Slowed down *make animations to 80-tick
Fixed infantry running anim
Added explosions for flametank and helis
Added prison and tech center icon/build-up animations
Added @Nyerguds TD Deluxe Cameo Pack
Tweaked harvester capacity & unload time
Fixed visceroid doesn't show control group number when selected
Fixed options menu fade effect
Polished map chooser layout
Added game type filter to map chooser
Developer Cheats menu got renamed to Debug Options and moved to in-game settings
Fixed tooltip flickering (increased default tooltip delay to 200 ms)
Fixed a bug with shift queueing production on extra production tabs
Implemented observer shroud selector
Shortened the names of some units in tooltips
Balance:
Tier 2 infantry and vehicles can now still be built as long as you have the GDI advanced comm center or temple of NOD
Added blue tiberium trees
Tiberium blossom trees are indestructible
Blue tiberium blossom trees grows new resources 50% the speed of the regular green one
Crates no longer provide MCVs if you already got one
Odds to get a MCV crate without base are now 80% to prevent gambling for it in early game
Husks burn 10 seconds now instead of 40
Infantry detect cloaked units in adjacent tile
Infantry run faster on clear terrain (90% -> 100% speed)
Vehicle speeds adjusted, now 80% on clear, like tanks
Airstrike cooldown time reduced to 3 minutes (from 4)
Construction Yard HP 2000 -> 1500
Communications Center HP reduced so it can be nuked
Airfield HP reduced so it can be destroyed by simultaneous airstrike + ion cannon
Reduced Helipad price reduced to $1000 (was $1500)
WEAP/AFLD HP reduced
Guard Tower range 6 -> 7, gun damage slightly increased
Advantaced Tower range 8 -> 9 and HP 800 -> 600
Obelisk Range + 1
Obelisk now AutoTargeted properly by units
Scaled back tank firing rate a bit (was 2x, now 1.5x)
Reduced Artillery damage vs. wood from 75% to 50%.
Rotation speeds increased for: bike, buggy, jeep, APC, stnk, ftnk, ltnk
Bike speed increased on clear, reduced on road
Slight buff to recon bike damage
Increased shroud for Buggy, Bike, LTNK and APC.
NOD buggy hitpoints reduced 140 -> 120
Flamethrower range 3 -> 2.5
Halved Chinook price from $1500 to $750
Orca/Apache now require any tech building instead of the specific GDI/NOD one
Apache ammo 10 -> 6 range 8 -> 10
Orca ammo 10 -> 5 range 8 -> 10, ROF decreased 10 -> 15
A10 range 8 -> 12
Mammoth tank speed 3 -> 4, HP 600 -> 800, turret rotation 2 -> 3
Mammoth missile spread increased
Apache shoots at air units again
STNK range - 1
STNK cloak delay slightly increased
Slight tweak to Stealth Tank damage vs. heavy armor (100% -> 90%)
GUN/GTWR range + 1
MCV now slightly slower
MCV no longer requires FIX
MASM given to Nod - speed and HP slightly lowered - uses patriot missile now
APC damage slightly increased, ROF decreased
APC speed reduced 10 -> 9
APC gun recoil and muzzle flash made single shot instead of double
MLRS speed increased 6 -> 7
Increased speed and burst delay of TowerMissile
Increased nuke damage vs heavy armor
Increased ion damage vs heavy armor
Fixed stealth tank not holding fire by default
Added map: Rock Canyon (psydev)
Added map: No Escapism (psydev)
Added map: Skull Valley (psydev)
Added map: Slippery Slopes (psydev)
Added map: Bifurcation (psydev)
Added map: Dead in Motion redux (psydev)
Added map: East vs West redux (psydev)
Removed map: Delta Dunes
Restored "classic" multi-engineer behavior. Buildings above 50% health will be damaged by 50%; buildings below this will be captured.
Added some texture to the UI background.
Fixed veterancy chevrons on cloaked units.
Increased aggressiveness of Viceroids.
Reduced the pip-count on silos.
Orca range increased, reload time decreased.
Sam Site missile spread increased.
Added maps: Deterring Democracy, Deterring Democracy +
Removed maps: East vs West Redux.
Updated maps: Skull Valley, Slippery Slopes, The Hourglass.
Renamed maps: Rock Canyon -> Manufacturing Consent, Bialystok -> Lessons from Kosovo.
Dune 2000:
Merged the Rounded Edges Mod by Jes and Bellator:
Bibs have been removed
Building offset and selection boxes adapted
Units and turrets leave husks
Siege tank barrel won't rotate anymore
AI capable of building all units using all queues
Bullet traces
Added Windtrap and IX Research building animations
New unit balance (mix of Dune II, Dune 2000 and Red Alert)
Repair pad animation
No more obsessive "silos needed"
Defense building queue → heavy armor queue
Medics (using thumper graphics)
Aircraft are no longer buildable
Harvesting has been slowed down
Replace blank shellmap with random multiplayer stills
Starport requires energy to operate
Corrino palace made unbuildable
Added new title font.
Added maps: Imperial Basin.
Fixed harvester docking animation.
Added harvesting animation.
Uses original (better quality) audio.
Updated maps: Black Mesa, Brimstone, Death Depths, Dune Boogie.
Engine:
Fixed a crash with server hosting under Windows.
Reduced order lag for singleplayer games
Added support for TS/RA2 mix files.
Added support for TS/RA2 SHP images.
Added support for TS/RA2 voxel models.
Added support for D2K RS archives.
Added support for WAV audio files.
Added support for XCC mix databases.
Added support for the D2K InstallShield archive.
Added support for classic production-acceleration behavior.
Added support for additional damage states on buildings.
Added additional debug information when yaml merging fails.
Added additional customization options for laser weapons.
Added additional logging for server errors.
Additional refactoring working towards TS/RA2 terrain support.
Build system and packages:
Host windows OpenAL and SDL dependencies on our own servers to avoid excessive downtime.
Map Editor and Tools:
Added an ingame asset viewer / converter to D2K and RA (enable via the Debug tab in the settings menu).
20130514:
All mods:
Destroyed bridges can now be repaired with an engineer.
Significantly improved AI.
Added (beta) support for left-click mouse orders via the settings menu.
Added "move viewport to group" hotkey (home key or double tap group number).
Exposed several new and/or hidden preferences in the settings menu.
Games can now be paused with a hotkey (F9 by default) or by opening the menu in a single player game.
New/improved UI for spectators and defeated players.
Improved server list and lobby UI.
Fixed units attacking enemy units hidden by fog/shroud.
Fixed several other visibility-related bugs.
Improved range circle contrast against terrain.
Fixed UI notification sound issues.
Added "Reset Exploration" developer option (same effect as the hide-map crate).
Fixed the visibility of peripheral effects for spectators/replays (ranks, production bars, etc).
Improved aircraft contrail rendering.
Fixed issue with harvesters not undocking when a refinery is destroyed.
Fixed civilian building rendering.
Added in-game player statistics for spectators (RA and D2K only).
Added "Fragile Alliances" option, allowing alliances to be changed during free-for-all games (RA and D2K only).
Added a tooltip that shows provided and drained power (RA and D2K - C&C already had this)
Improved ingame chat dialog (RA and D2K only).
Red Alert:
Added Mobile Gap Generator.
Added Mobile Radar Jammer (jams radar and deflects enemy missiles).
Added desert theater (ported from C&C plus additional custom tiles by Harrison).
Removed Volkov.
Added announcer voice upon game start.
Civilians now panic when attacked.
Added a new desert-themed shellmap inspired by C&C Generals.
Added ice floe actors for use on snow maps.
Several improvements to the original shellmap.
New missions: Allies03, Allies04, Soviet01Classic, MonsterTankMadness, Survival01.
New maps: Room Convergence, Ghost Town, Bad Neighbors.
Improved maps: Chaos Canyon, Bomber John, Bombardment Islands.
Fixed Mechanic repair cursor.
Fixed graphics for craters for snow maps.
Re-enabled "Mud" music track.
Fixed supply track shadow.
Added unit production hotkeys.
Fixed Airfield tooltip.
Changed minimap color of gems to blue.
Gap Generator shroud now disappears on low-power/death.
Added support for Nyerguds music upgrade pack.
Fixed target line/flash for Demo Truck targets.
Added building death frames for Construction Yard, Power Plants and Ore Refinery.
Improved Ore Silo artwork (more fill states).
Improved Weapon Factory door animation and prevented the door closing before units exited.
Changed building placement color to black on snow maps for increased contrast.
Fixed Attack Dog sound.
Added duration indicators for units under Chronosphere and Iron Curtain effects.
Fixed Tanya shooting buildings and using C4 on barrels.
Fixed Chrono Tank being unable to crush infantry/sandbags or trigger mines.
Balance changes:
Base defense damage reduced against structures.
Pillbox armor increased from wood to heavy.
Construction Yard armor reduced/health increased.
Construction Yard is now unsellable to prevent Mobile Construction Vehicle crate exploit.
Superweapon health increased.
Chrono Tank damage increased/health increased/Chrono-shift range limit added/Chronosphere prerequisite added.
Demo Truck cost increased/health reduced/speed reduced.
Disguised Spies are now hidden from enemy GPS.
Added build limit to Missile Silo and Tanya.
Oil derricks are now repairable by engineers.
Minelayers can now detect mines, allowing other units to target and destroy them.
Nukes can destroy ore wells and trees.
Tesla Tank health increased.
Medium Tank damage increased.
Mammoth Tank turret rotation speed increased/damage increased.
Badger bomber health increased.
Cruiser speed increased.
Missile Sub accuracy increased.
C&C:
Building range and a short construction delay added to Construction Yards to prevent base walking.
Mobile SAM launcher given to Nod as dedicated mobile AA.
Improved explosions.
Corrected music.yaml track titles.
Tweaked building construction animation speed.
Improved cloak and building destruction sounds.
Added blue tiberium trees.
Fixed blue tiberium not poisoning infantry.
Increased crate lifetime and removed level-up crate effect.
Fixed infantry running animations.
Fixed Viceroid unit decorations (veterancy/group number).
Fixed tooltip flickering when moving the mouse.
Fixed mouse-interaction bugs in the production palette.
Improved tooltip names.
Added maps: The Sentinel, Bialystok, No Escapism, Rock Canyon, Slippery Slopes, Skull Valley, Dead in Motion Redux, East vs West redux, Drop Zone, The Hourglass.
Added EVA notification for nuclear missile launches.
Husk lifetime reduced to 10 seconds.
Obelisk is now automatically targeted by units.
Balance:
Harvester capacity increased.
Advanced Comm-Center and Temple of Nod act as a substitute prerequisite of the Comm-Center.
Reduced MCV crate probability to 80% to solve Construction Yard sell exploit.
MCV can be constructed without a service depot.
Construction Yard health reduced.
Infantry detect nearby cloaked units.
Infantry and vehicle speed on clear terrain increased.
Airstrike regen reduced to three minutes.
Comm-Center health reduced.
Airfield health reduced.
Helipad price reduced.
Advanced Guard Tower range increased/health reduced/power consumption increased.
Advanced Guard Tower missiles improved vs aircraft and vehicles/reduced vs infantry
Obelisk range increased.
Tank firing rate increased/damage vs light armor increased.
Light Tank speed increased/turn rate increased/ROF increased/damage reduced.
Turn speeds increased for most units.
Recon Bike damage reduced/sight increased/speed increased on clear, reduced on road.
Buggy health reduced/sight increased.
Flamethrower range reduced/damage increased vs wood.
Flame Tank significantly buffed.
Chinook price reduced.
Helicopter prerequisites changed to Comm-Center/Advanced Tech building.
Apache sight increased.
Orca ROF reduced/ammo increased/damage reduced/sight increased.
Stealth Tank initial stance changed to "hold-fire."
Increased sight range for Light Tank and APC.
A10 sight range increased.
Mammoth Tank speed increased/health increased/turret rotation increased/missile spread increased.
Stealth Tank range reduced/cloak delay increased/damage vs heavy armor reduced.
Turret and Guard Tower range increased.
APC damage increased/speed reduced.
Artillery range reduced/damage vs wood reduced.
Rocket Infantry damage increased vs armor.
Increased nuke and Ion Cannon damage vs heavy armor.
Grenadier's grenade speed increased.
SAM site pop-up speed increased.
Humvee / Buggy damage increased.
MLRS range reduced/ROF reduced/damage reduced.
Recon Bike damage increased vs heavy armor/burst delay added/range reduced.
Dune 2000:
Removed shroud (uses fog-of-war instead). Buildings are hidden under the fog until they are first seen.
Added Medic.
Added Stealth Raider.
Removed Deviator.
Aircraft are now unbuildable.
Fixed building positioning and selection boxes.
Added husks for units and turrets.
Added trails to shells and missiles.
Fixed animations for Windtrap, Repair Pad, IX Research.
Reduced "silos needed" warnings.
Husk lifetime reduced to 20 seconds.
Added large building radius to construction yard.
Improved explosions.
Crate probabilities adjusted.
New UI button artwork.
Added maps: Black Mesa, Black Mesa (large), Dune Boogie, Dune Boogie (large), Brimstone, Death Depths - Modded, Tuck's Sietch.
Complete rebalancing from the ground up.
Engine:
Fixed several desync crashes.
Fixed a crash involving keyboard shortcuts and dead units.
Fixed a crash involving replays and multiple game sessions on a single machine.
Fixed a rare loadscreen crash.
Improved logging on game desync.
Improved UPnP support via Mono.Nat.
Improved freetype support via SharpFont (replaces custom patched Tao.Freetype).
Improved dedicated server support.
Improved mix file decryption (fixes a crash when playing the "Mud" audio track).
Introduced a new coordinate model to simplify eventual TS/RA2 support.
Initial work to port existing code to new coordinates model.
Added a muzzle positioning debug visualization.
Added a pathfinder debug visualization.
Removed player-configurable range behind the scenes of the color picker. Fixes "radioactive" color exploit.
Show an improved error message when connecting to a server with incompatible mods.
Added automatic map downloads from content.open-ra.org when joining a server with an unknown map.
Added support for randomized weapon reports.
Improved loading times by removing unnecessary map indexing.
Scripted maps can now require human players in specified slots.
Allow mods to independently enable/disable shroud and fog of war.
Minor pathfinding improvements.
Other misc refactoring and code cleanup.
Build system and packages:
Added a `version` rule to the makefile for setting mod version strings on local development builds.
Added a `docs` rule for generating trait documentation.
Fixed permission errors in the .deb package.
Support `--instdir` options for parallel installation of .deb packages.
Added Desura compatibility for Linux.
Map Editor and Tools:
Added a toolstrip with new and improved tools.
Added support for terrain categories.
Fixed wrong palette remapping for neutral buildings.
Maps are now saved/loaded from the custom maps directory.
Fixed legacy map importer.
Added a --docs flag for OpenRA.Utility for generating trait documentation.
20121019:
Engine:
@@ -1254,11 +1216,11 @@ NEW:
Added scroll speed control (thanks, Gecko)
20100922 through 20100922-4
Allow queueing of buildings when a building is ready to place
Allow queuing of buildings when a building is ready to place
Change the pathfinder to use integers instead of floating point (fixes desync)
Create ui widgets on demand instead of at gamestart
Begin refactoring activity queuing for cleaner code
Reenable crates
Re-enable crates
Remove desync debug logging
Fix editor crash
Fix crash when a building being repaired is killed

BIN
Dune2k.ttf Normal file

Binary file not shown.

View File

@@ -93,8 +93,8 @@ STD_MOD_DEPS = $(STD_MOD_LIBS) $(ralint_TARGET)
mod_ra_SRCS := $(shell find OpenRA.Mods.RA/ -iname '*.cs')
mod_ra_TARGET = mods/ra/OpenRA.Mods.RA.dll
mod_ra_KIND = library
mod_ra_DEPS = $(STD_MOD_DEPS)
mod_ra_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS)
mod_ra_DEPS = $(STD_MOD_DEPS) $(utility_TARGET)
mod_ra_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(utility_TARGET)
mod_ra_EXTRA_CMDS = mono --debug RALint.exe ra
PROGRAMS += mod_ra
mod_ra: $(mod_ra_TARGET)

View File

@@ -45,7 +45,7 @@ namespace OpenRA.Editor
public static ActorTemplate RenderActor(ActorInfo info, TileSet tileset, Palette p)
{
var image = RenderSimple.GetImage(info);
var image = RenderSprites.GetImage(info);
using (var s = FileSystem.OpenWithExts(image, tileset.Extensions))
{

View File

@@ -41,41 +41,6 @@ namespace OpenRA
return a.GetTypes().Select(t => t.Namespace).Distinct().Where(n => n != null);
}
public static string ReadAllText(this Stream s)
{
using (s)
using (var sr = new StreamReader(s))
return sr.ReadToEnd();
}
public static byte[] ReadAllBytes(this Stream s)
{
using (s)
{
var data = new byte[s.Length - s.Position];
s.Read(data, 0, data.Length);
return data;
}
}
public static void Write(this Stream s, byte[] data)
{
s.Write(data, 0, data.Length);
}
public static IEnumerable<string> ReadAllLines(this Stream s)
{
using (var sr = new StreamReader(s))
for (; ; )
{
var line = sr.ReadLine();
if (line == null)
yield break;
else
yield return line;
}
}
public static bool HasAttribute<T>(this MemberInfo mi)
{
return mi.GetCustomAttributes(typeof(T), true).Length != 0;
@@ -172,6 +137,11 @@ namespace OpenRA
return v;
}
public static bool IsPowerOf2(int v)
{
return (v & (v - 1)) == 0;
}
public static Size NextPowerOf2(this Size s) { return new Size(NextPowerOf2(s.Width), NextPowerOf2(s.Height)); }
public static string JoinWith<T>(this IEnumerable<T> ts, string j)
@@ -255,6 +225,20 @@ namespace OpenRA
}
public static Rectangle Bounds(this Bitmap b) { return new Rectangle(0, 0, b.Width, b.Height); }
public static int ToBits(this IEnumerable<bool> bits)
{
var i = 0;
var result = 0;
foreach (var b in bits)
if (b)
result |= (1 << i++);
else
i++;
if (i > 33)
throw new InvalidOperationException("ToBits only accepts up to 32 values.");
return result;
}
}
public static class Enum<T>

View File

@@ -0,0 +1,125 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
namespace OpenRA.FileFormats
{
/// <summary>
/// Static class that uses a lookup table to calculates CRC32
/// checksums of input strings.
/// </summary>
public static class CRC32
{
/// <summary>
/// The CRC32 lookup table
/// </summary>
static uint[] lookUp = new uint[256]
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
/// <summary>
/// A fast (native) CRC32 implementation that can be used on a regular byte arrays.
/// </summary>
/// <param name="data">The data from which to calculate the checksum.</param>
/// <param name="polynomal">The polynomal.</param>
/// <returns>
/// The calculated checksum.
/// </returns>
public static uint Calculate(byte[] data, uint polynomal = 0xFFFFFFFF)
{
uint crc = polynomal;
for (int i = 0; i < data.Length; i++)
crc = (crc >> 8) ^ lookUp[(crc & 0xFF) ^ data[i]];
crc ^= polynomal;
return crc;
}
/// <summary>
/// A fast (native) CRC32 implementation that can be used on a pinned byte array using
/// default polynomal.
/// </summary>
/// <param name="data"> [in,out] If non-null, the.</param>
/// <param name="len"> The length of the data data.</param>
/// <param name="polynomal">The polynomal to xor with.</param>
/// <returns>The calculated checksum.</returns>
public static unsafe uint Calculate(byte* data, uint len, uint polynomal = 0xFFFFFFFF)
{
uint crc = polynomal;
for (int i = 0; i < len; i++)
crc = (crc >> 8) ^ lookUp[(crc & 0xFF) ^ *data++];
crc ^= polynomal;
return crc;
}
}
}

View File

@@ -0,0 +1,75 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.IO;
using System.Text;
namespace OpenRA.FileFormats
{
public class WavLoader
{
public readonly int FileSize;
public readonly string Format;
public readonly int FmtChunkSize;
public readonly int AudioFormat;
public readonly int Channels;
public readonly int SampleRate;
public readonly int ByteRate;
public readonly int BlockAlign;
public readonly int BitsPerSample;
public readonly int DataSize;
public readonly byte[] RawOutput;
public WavLoader(Stream s)
{
while (s.Position < s.Length)
{
if ((s.Position & 1) == 1)
s.ReadByte(); // Alignment
var type = s.ReadASCII(4);
switch (type)
{
case "RIFF":
FileSize = s.ReadInt32();
Format = s.ReadASCII(4);
if (Format != "WAVE")
throw new NotSupportedException("Not a canonical WAVE file.");
break;
case "fmt ":
FmtChunkSize = s.ReadInt32();
if (FmtChunkSize != 16)
throw new NotSupportedException("{0} fmt chunk size is not a supported encoding scheme.".F(FmtChunkSize));
AudioFormat = s.ReadInt16();
if (AudioFormat != 1)
throw new NotSupportedException("Non-PCM compression is not supported.");
Channels = s.ReadInt16();
SampleRate = s.ReadInt32();
ByteRate = s.ReadInt32();
BlockAlign = s.ReadInt16();
BitsPerSample = s.ReadInt16();
break;
case "data":
DataSize = s.ReadInt32();
RawOutput = s.ReadBytes(DataSize);
break;
default:
// Ignore unknown chunks
var chunkSize = s.ReadInt32();
s.ReadBytes(chunkSize);
break;
}
}
}
}
}

View File

@@ -0,0 +1,44 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.IO;
namespace OpenRA.FileFormats
{
public class XccGlobalDatabase
{
public readonly string[] Entries;
public XccGlobalDatabase(Stream s)
{
var entries = new List<string>();
var reader = new BinaryReader(s);
while (reader.PeekChar() > -1)
{
var count = reader.ReadInt32();
for (var i = 0; i < count; i++)
{
var chars = new List<char>();
char c;
// Read filename
while ((c = reader.ReadChar()) != 0)
chars.Add(c);
entries.Add(new string(chars.ToArray()));
// Skip comment
while ((c = reader.ReadChar()) != 0);
}
}
Entries = entries.ToArray();
}
}
}

View File

@@ -0,0 +1,67 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace OpenRA.FileFormats
{
public class XccLocalDatabase
{
public readonly string[] Entries;
public XccLocalDatabase(Stream s)
{
// Skip unnecessary header data
s.Seek(48, SeekOrigin.Begin);
var reader = new BinaryReader(s);
var count = reader.ReadInt32();
Entries = new string[count];
for (var i = 0; i < count; i++)
{
var chars = new List<char>();
char c;
while ((c = reader.ReadChar()) != 0)
chars.Add(c);
Entries[i] = new string(chars.ToArray());
}
}
public XccLocalDatabase(IEnumerable<string> filenames)
{
Entries = filenames.ToArray();
}
public byte[] Data()
{
var data = new MemoryStream();
using (var writer = new BinaryWriter(data))
{
writer.Write(Encoding.ASCII.GetBytes("XCC by Olaf van der Spek"));
writer.Write(new byte[] {0x1A,0x04,0x17,0x27,0x10,0x19,0x80,0x00});
writer.Write((int)(Entries.Aggregate(Entries.Length, (a,b) => a + b.Length) + 52)); // Size
writer.Write((int)0); // Type
writer.Write((int)0); // Version
writer.Write((int)0); // Game/Format (0 == TD)
writer.Write((int)Entries.Length); // Entries
foreach (var e in Entries)
{
writer.Write(Encoding.ASCII.GetBytes(e));
writer.Write((byte)0);
}
}
return data.ToArray();
}
}
}

View File

@@ -0,0 +1,102 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
namespace OpenRA.FileFormats
{
public class D2kSoundResources : IFolder
{
readonly Stream s;
readonly string filename;
readonly List<string> filenames;
readonly int priority;
readonly Dictionary<uint, PackageEntry> index = new Dictionary<uint, PackageEntry>();
public D2kSoundResources(string filename, int priority)
{
this.filename = filename;
this.priority = priority;
s = FileSystem.Open(filename);
s.Seek(0, SeekOrigin.Begin);
filenames = new List<string>();
var headerLength = s.ReadUInt32();
while (s.Position < headerLength + 4)
{
var name = s.ReadASCIIZ();
var offset = s.ReadUInt32();
var length = s.ReadUInt32();
var hash = PackageEntry.HashFilename(name, PackageHashType.Classic);
if (!index.ContainsKey(hash))
index.Add(hash, new PackageEntry(hash, offset, length));
filenames.Add(name);
}
}
public Stream GetContent(uint hash)
{
PackageEntry e;
if (!index.TryGetValue(hash, out e))
return null;
s.Seek(e.Offset, SeekOrigin.Begin);
var data = new byte[e.Length];
s.Read(data, 0, (int)e.Length);
return new MemoryStream(data);
}
public Stream GetContent(string filename)
{
return GetContent(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public bool Exists(string filename)
{
return index.ContainsKey(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public IEnumerable<string> AllFileNames()
{
return filenames;
}
public string Name { get { return filename; } }
public int Priority { get { return 1000 + priority; }}
public IEnumerable<uint> ClassicHashes()
{
return index.Keys;
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public void Write(Dictionary<string, byte[]> contents)
{
throw new NotImplementedException("Cannot save Dune 2000 Sound Resources.");
}
}
}

View File

@@ -18,29 +18,33 @@ namespace OpenRA.FileFormats
{
public static class FileSystem
{
static List<IFolder> mountedFolders = new List<IFolder>();
public static List<IFolder> MountedFolders = new List<IFolder>();
static Cache<uint, List<IFolder>> classicHashIndex = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
static Cache<uint, List<IFolder>> crcHashIndex = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
static Cache<uint, List<IFolder>> allFiles = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
public static List<string> FolderPaths = new List<string>();
static void MountInner(IFolder folder)
{
mountedFolders.Add(folder);
MountedFolders.Add(folder);
foreach( var hash in folder.AllFileHashes() )
foreach (var hash in folder.ClassicHashes())
{
var l = allFiles[hash];
if( !l.Contains( folder ) )
l.Add( folder );
var l = classicHashIndex[hash];
if (!l.Contains(folder))
l.Add(folder);
}
foreach (var hash in folder.CrcHashes())
{
var l = crcHashIndex[hash];
if (!l.Contains(folder))
l.Add(folder);
}
}
static int order = 0;
static IFolder OpenPackage(string filename)
{
return OpenPackage(filename, order++);
}
public static IFolder CreatePackage(string filename, int order, Dictionary<string, byte[]> content)
{
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
@@ -49,20 +53,28 @@ namespace OpenRA.FileFormats
return new ZipFile(filename, order, content);
else if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(filename, order, content);
else if (filename.EndsWith(".RS", StringComparison.InvariantCultureIgnoreCase))
throw new NotImplementedException("Creating .RS archives is unsupported");
else if (filename.EndsWith(".Z", StringComparison.InvariantCultureIgnoreCase))
throw new NotImplementedException("Creating .Z archives is unsupported");
else
return new Folder(filename, order, content);
}
public static IFolder OpenPackage(string filename, int order)
public static IFolder OpenPackage(string filename, string annotation, int order)
{
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
return new MixFile(filename, order);
{
var type = string.IsNullOrEmpty(annotation) ? PackageHashType.Classic :
FieldLoader.GetValue<PackageHashType>("(value)", annotation);
return new MixFile(filename, type, order);
}
else if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(filename, order);
else if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(filename, order);
else if (filename.EndsWith(".RS", StringComparison.InvariantCultureIgnoreCase))
return new D2kSoundResources(filename, order);
else if (filename.EndsWith(".Z", StringComparison.InvariantCultureIgnoreCase))
return new InstallShieldPackage(filename, order);
else
@@ -70,6 +82,11 @@ namespace OpenRA.FileFormats
}
public static void Mount(string name)
{
Mount(name, null);
}
public static void Mount(string name, string annotation)
{
var optional = name.StartsWith("~");
if (optional) name = name.Substring(1);
@@ -78,7 +95,8 @@ namespace OpenRA.FileFormats
if (name.StartsWith("^"))
name = Platform.SupportDir+name.Substring(1);
var a = (Action)(() => FileSystem.MountInner(OpenPackage(name)));
FolderPaths.Add(name);
Action a = () => FileSystem.MountInner(OpenPackage(name, annotation, order++));
if (optional)
try { a(); }
@@ -89,30 +107,36 @@ namespace OpenRA.FileFormats
public static void UnmountAll()
{
mountedFolders.Clear();
allFiles = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
MountedFolders.Clear();
FolderPaths.Clear();
classicHashIndex = new Cache<uint, List<IFolder>>(_ => new List<IFolder>());
crcHashIndex = new Cache<uint, List<IFolder>>(_ => new List<IFolder>());
}
public static bool Unmount(IFolder mount)
{
return (mountedFolders.RemoveAll(f => f == mount) > 0);
return (MountedFolders.RemoveAll(f => f == mount) > 0);
}
public static void Mount(IFolder mount)
{
if (!mountedFolders.Contains(mount)) mountedFolders.Add(mount);
if (!MountedFolders.Contains(mount)) MountedFolders.Add(mount);
}
public static void LoadFromManifest( Manifest manifest )
public static void LoadFromManifest(Manifest manifest)
{
UnmountAll();
foreach (var dir in manifest.Folders) Mount(dir);
foreach (var pkg in manifest.Packages) Mount(pkg);
foreach (var dir in manifest.Folders)
Mount(dir);
foreach (var pkg in manifest.Packages)
Mount(pkg.Key, pkg.Value);
}
static Stream GetFromCache( Cache<uint, List<IFolder>> index, string filename )
static Stream GetFromCache(PackageHashType type, string filename)
{
var folder = index[PackageEntry.HashFilename(filename)]
var index = type == PackageHashType.CRC32 ? crcHashIndex : classicHashIndex;
var folder = index[PackageEntry.HashFilename(filename, type)]
.Where(x => x.Exists(filename))
.OrderBy(x => x.Priority)
.FirstOrDefault();
@@ -125,21 +149,25 @@ namespace OpenRA.FileFormats
public static Stream Open(string filename) { return OpenWithExts(filename, ""); }
public static Stream OpenWithExts( string filename, params string[] exts )
public static Stream OpenWithExts(string filename, params string[] exts)
{
if( filename.IndexOfAny( new char[] { '/', '\\' } ) == -1 )
if (filename.IndexOfAny(new char[] { '/', '\\' } ) == -1)
{
foreach( var ext in exts )
foreach (var ext in exts)
{
var s = GetFromCache( allFiles, filename + ext );
if( s != null )
var s = GetFromCache(PackageHashType.Classic, filename + ext);
if (s != null)
return s;
s = GetFromCache(PackageHashType.CRC32, filename + ext);
if (s != null)
return s;
}
}
foreach( var ext in exts )
foreach (var ext in exts)
{
var folder = mountedFolders
var folder = MountedFolders
.Where(x => x.Exists(filename + ext))
.OrderByDescending(x => x.Priority)
.FirstOrDefault();
@@ -151,7 +179,7 @@ namespace OpenRA.FileFormats
throw new FileNotFoundException("File not found: {0}".F(filename), filename);
}
public static bool Exists(string filename) { return mountedFolders.Any(f => f.Exists(filename)); }
public static bool Exists(string filename) { return MountedFolders.Any(f => f.Exists(filename)); }
static Dictionary<string, Assembly> assemblyCache = new Dictionary<string, Assembly>();

View File

@@ -17,8 +17,7 @@ namespace OpenRA.FileFormats
public class Folder : IFolder
{
readonly string path;
int priority;
readonly int priority;
// Create a new folder package
public Folder(string path, int priority, Dictionary<string, byte[]> contents)
@@ -45,22 +44,30 @@ namespace OpenRA.FileFormats
catch { return null; }
}
public IEnumerable<uint> AllFileHashes()
public IEnumerable<uint> ClassicHashes()
{
foreach( var filename in Directory.GetFiles( path, "*", SearchOption.TopDirectoryOnly ) )
yield return PackageEntry.HashFilename( Path.GetFileName(filename) );
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
yield return PackageEntry.HashFilename(Path.GetFileName(filename), PackageHashType.Classic);
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
yield return Path.GetFileName(filename);
}
public bool Exists(string filename)
{
return File.Exists(Path.Combine(path,filename));
return File.Exists(Path.Combine(path, filename));
}
public int Priority
{
get { return priority; }
}
public int Priority { get { return priority; } }
public string Name { get { return path; } }
public void Write(Dictionary<string, byte[]> contents)
{

View File

@@ -18,13 +18,17 @@ namespace OpenRA.FileFormats
public class InstallShieldPackage : IFolder
{
readonly Dictionary<uint, PackageEntry> index = new Dictionary<uint, PackageEntry>();
readonly List<string> filenames;
readonly Stream s;
readonly long dataStart = 255;
int priority;
readonly int priority;
readonly string filename;
public InstallShieldPackage(string filename, int priority)
{
this.filename = filename;
this.priority = priority;
filenames = new List<string>();
s = FileSystem.Open(filename);
// Parse package header
@@ -45,11 +49,20 @@ namespace OpenRA.FileFormats
// Parse the directory list
s.Seek(TOCAddress, SeekOrigin.Begin);
BinaryReader TOCreader = new BinaryReader(s);
var fileCountInDirs = new List<uint>();
// Parse directories
for (var i = 0; i < DirCount; i++)
ParseDirectory(TOCreader);
fileCountInDirs.Add(ParseDirectory(TOCreader));
// Parse files
foreach (var fileCount in fileCountInDirs)
for (var i = 0; i < fileCount; i++)
ParseFile(reader);
}
void ParseDirectory(BinaryReader reader)
uint ParseDirectory(BinaryReader reader)
{
// Parse directory header
var FileCount = reader.ReadUInt16();
@@ -59,10 +72,7 @@ namespace OpenRA.FileFormats
// Skip to the end of the chunk
reader.ReadBytes(ChunkSize - NameLength - 6);
// Parse files
for (var i = 0; i < FileCount; i++)
ParseFile(reader);
return FileCount;
}
uint AccumulatedData = 0;
@@ -76,8 +86,10 @@ namespace OpenRA.FileFormats
var NameLength = reader.ReadByte();
var FileName = new String(reader.ReadChars(NameLength));
var hash = PackageEntry.HashFilename(FileName);
index.Add(hash, new PackageEntry(hash,AccumulatedData, CompressedSize));
var hash = PackageEntry.HashFilename(FileName, PackageHashType.Classic);
if (!index.ContainsKey(hash))
index.Add(hash, new PackageEntry(hash,AccumulatedData, CompressedSize));
filenames.Add(FileName);
AccumulatedData += CompressedSize;
// Skip to the end of the chunk
@@ -90,33 +102,40 @@ namespace OpenRA.FileFormats
if (!index.TryGetValue(hash, out e))
return null;
s.Seek( dataStart + e.Offset, SeekOrigin.Begin );
byte[] data = new byte[ e.Length ];
s.Read( data, 0, (int)e.Length );
s.Seek(dataStart + e.Offset, SeekOrigin.Begin);
var data = new byte[e.Length];
s.Read(data, 0, (int)e.Length);
return new MemoryStream(Blast.Decompress(data));
}
public Stream GetContent(string filename)
{
return GetContent(PackageEntry.HashFilename(filename));
return GetContent(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public IEnumerable<uint> AllFileHashes()
public IEnumerable<uint> ClassicHashes()
{
return index.Keys;
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
return filenames;
}
public bool Exists(string filename)
{
return index.ContainsKey(PackageEntry.HashFilename(filename));
return index.ContainsKey(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public int Priority
{
get { return 2000 + priority; }
}
public int Priority { get { return 2000 + priority; }}
public string Name { get { return filename; } }
public void Write(Dictionary<string, byte[]> contents)
{

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2013 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,
@@ -10,6 +10,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -19,9 +20,12 @@ namespace OpenRA.FileFormats
{
Stream GetContent(string filename);
bool Exists(string filename);
IEnumerable<uint> AllFileHashes();
IEnumerable<uint> ClassicHashes();
IEnumerable<uint> CrcHashes();
IEnumerable<string> AllFileNames();
void Write(Dictionary<string, byte[]> contents);
int Priority { get; }
string Name { get; }
}
public class MixFile : IFolder
@@ -29,35 +33,41 @@ namespace OpenRA.FileFormats
readonly Dictionary<uint, PackageEntry> index;
readonly long dataStart;
readonly Stream s;
int priority;
readonly int priority;
readonly string filename;
readonly PackageHashType type;
// Save a mix to disk with the given contents
public MixFile(string filename, int priority, Dictionary<string, byte[]> contents)
{
this.filename = filename;
this.priority = priority;
this.type = PackageHashType.Classic;
if (File.Exists(filename))
File.Delete(filename);
s = File.Create(filename);
// TODO: Add a local mix database.dat for compatibility with XCC Mixer
index = new Dictionary<uint, PackageEntry>();
contents.Add("local mix database.dat", new XccLocalDatabase(contents.Keys.Append("local mix database.dat")).Data());
Write(contents);
}
public MixFile(string filename, int priority)
public MixFile(string filename, PackageHashType type, int priority)
{
this.filename = filename;
this.priority = priority;
this.type = type;
s = FileSystem.Open(filename);
// Detect format type
s.Seek(0, SeekOrigin.Begin);
var reader = new BinaryReader(s);
var isCncMix = reader.ReadUInt16() != 0;
var isCncMix = s.ReadUInt16() != 0;
// The C&C mix format doesn't contain any flags or encryption
var isEncrypted = false;
if (!isCncMix)
isEncrypted = (reader.ReadUInt16() & 0x2) != 0;
isEncrypted = (s.ReadUInt16() & 0x2) != 0;
List<PackageEntry> entries;
if (isEncrypted)
@@ -77,13 +87,12 @@ namespace OpenRA.FileFormats
List<PackageEntry> ParseHeader(Stream s, long offset, out long headerEnd)
{
s.Seek(offset, SeekOrigin.Begin);
var reader = new BinaryReader(s);
var numFiles = reader.ReadUInt16();
/*uint dataSize = */reader.ReadUInt32();
var numFiles = s.ReadUInt16();
/*uint dataSize = */s.ReadUInt32();
var items = new List<PackageEntry>();
for (var i = 0; i < numFiles; i++)
items.Add(new PackageEntry(reader));
items.Add(new PackageEntry(s));
headerEnd = offset + 6 + numFiles*PackageEntry.Size;
return items;
@@ -92,16 +101,15 @@ namespace OpenRA.FileFormats
MemoryStream DecryptHeader(Stream s, long offset, out long headerEnd)
{
s.Seek(offset, SeekOrigin.Begin);
var reader = new BinaryReader(s);
// Decrypt blowfish key
var keyblock = reader.ReadBytes(80);
var keyblock = s.ReadBytes(80);
var blowfishKey = new BlowfishKeyProvider().DecryptKey(keyblock);
var fish = new Blowfish(blowfishKey);
// Decrypt first block to work out the header length
var ms = Decrypt(ReadBlocks(s, offset + 80, 1), fish);
var numFiles = new BinaryReader(ms).ReadUInt16();
var numFiles = ms.ReadUInt16();
// Decrypt the full header - round bytes up to a full block
var blockCount = (13 + numFiles*PackageEntry.Size)/8;
@@ -127,16 +135,32 @@ namespace OpenRA.FileFormats
uint[] ReadBlocks(Stream s, long offset, int count)
{
s.Seek(offset, SeekOrigin.Begin);
var r = new BinaryReader(s);
// A block is a single encryption unit (represented as two 32-bit integers)
var ret = new uint[2*count];
for (var i = 0; i < ret.Length; i++)
ret[i] = r.ReadUInt32();
ret[i] = s.ReadUInt32();
return ret;
}
uint? FindMatchingHash(string filename)
{
var hash = PackageEntry.HashFilename(filename, type);
if (index.ContainsKey(hash))
return hash;
// Maybe we were given a raw hash?
uint raw;
if (!uint.TryParse(filename, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out raw))
return null;
if ("{0:X}".F(raw) == filename && index.ContainsKey(raw))
return raw;
return null;
}
public Stream GetContent(uint hash)
{
PackageEntry e;
@@ -151,25 +175,63 @@ namespace OpenRA.FileFormats
public Stream GetContent(string filename)
{
return GetContent(PackageEntry.HashFilename(filename));
var hash = FindMatchingHash(filename);
return hash.HasValue ? GetContent(hash.Value) : null;
}
public IEnumerable<uint> AllFileHashes()
static readonly uint[] Nothing = {};
public IEnumerable<uint> ClassicHashes()
{
return index.Keys;
if (type == PackageHashType.Classic)
return index.Keys;
return Nothing;
}
public IEnumerable<uint> CrcHashes()
{
if (type == PackageHashType.CRC32)
return index.Keys;
return Nothing;
}
public IEnumerable<string> AllFileNames()
{
var lookup = new Dictionary<uint, string>();
if (Exists("local mix database.dat"))
{
var db = new XccLocalDatabase(GetContent("local mix database.dat"));
foreach (var e in db.Entries)
{
var hash = PackageEntry.HashFilename(e, type);
if (!lookup.ContainsKey(hash))
lookup.Add(hash, e);
}
}
if (FileSystem.Exists("global mix database.dat"))
{
var db = new XccGlobalDatabase(FileSystem.Open("global mix database.dat"));
foreach (var e in db.Entries)
{
var hash = PackageEntry.HashFilename(e, type);
if (!lookup.ContainsKey(hash))
lookup.Add(hash, e);
}
}
return index.Keys.Select(k => lookup.ContainsKey(k) ? lookup[k] : "{0:X}".F(k));
}
public bool Exists(string filename)
{
return index.ContainsKey(PackageEntry.HashFilename(filename));
}
public int Priority
{
get { return 1000 + priority; }
return FindMatchingHash(filename).HasValue;
}
public int Priority { get { return 1000 + priority; } }
public string Name { get { return filename; } }
public void Write(Dictionary<string, byte[]> contents)
{
// Cannot modify existing mixfile - rename existing file and
@@ -186,7 +248,7 @@ namespace OpenRA.FileFormats
foreach (var kv in contents)
{
var length = (uint)kv.Value.Length;
var hash = PackageEntry.HashFilename(Path.GetFileName(kv.Key));
var hash = PackageEntry.HashFilename(Path.GetFileName(kv.Key), type);
items.Add(new PackageEntry(hash, dataSize, length));
dataSize += length;
}

View File

@@ -51,7 +51,6 @@ namespace OpenRA.FileFormats
public Stream GetContent(string filename)
{
using (var z = pkg.GetInputStream(pkg.GetEntry(filename)))
{
var ms = new MemoryStream();
@@ -65,10 +64,21 @@ namespace OpenRA.FileFormats
}
}
public IEnumerable<uint> AllFileHashes()
public IEnumerable<uint> ClassicHashes()
{
foreach(ZipEntry entry in pkg)
yield return PackageEntry.HashFilename(entry.Name);
yield return PackageEntry.HashFilename(entry.Name, PackageHashType.Classic);
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
foreach(ZipEntry entry in pkg)
yield return entry.Name;
}
public bool Exists(string filename)
@@ -76,10 +86,8 @@ namespace OpenRA.FileFormats
return pkg.GetEntry(filename) != null;
}
public int Priority
{
get { return 500 + priority; }
}
public int Priority { get { return 500 + priority; } }
public string Name { get { return filename; } }
public void Write(Dictionary<string, byte[]> contents)
{

View File

@@ -36,23 +36,23 @@ namespace OpenRA.FileFormats
public readonly byte[] LookupTable;
public byte[] Image;
public Dune2ImageHeader(BinaryReader reader)
public Dune2ImageHeader(Stream s)
{
Flags = (Dune2ImageFlags)reader.ReadUInt16();
Slices = reader.ReadByte();
Width = reader.ReadUInt16();
Height = reader.ReadByte();
FileSize = reader.ReadUInt16();
DataSize = reader.ReadUInt16();
Flags = (Dune2ImageFlags)s.ReadUInt16();
Slices = s.ReadUInt8();
Width = s.ReadUInt16();
Height = s.ReadUInt8();
FileSize = s.ReadUInt16();
DataSize = s.ReadUInt16();
if (Flags == Dune2ImageFlags.L16_F80_F2_1 ||
Flags == Dune2ImageFlags.L16_F80_F2_2 ||
Flags == Dune2ImageFlags.Ln_F80_F2)
{
int n = Flags == Dune2ImageFlags.Ln_F80_F2 ? reader.ReadByte() : (byte)16;
int n = Flags == Dune2ImageFlags.Ln_F80_F2 ? s.ReadUInt8() : (byte)16;
LookupTable = new byte[n];
for (int i = 0; i < n; i++)
LookupTable[i] = reader.ReadByte();
LookupTable[i] = s.ReadUInt8();
}
else
{
@@ -78,16 +78,14 @@ namespace OpenRA.FileFormats
List<Dune2ImageHeader> headers = new List<Dune2ImageHeader>();
public Dune2ShpReader(Stream stream)
public Dune2ShpReader(Stream s)
{
BinaryReader reader = new BinaryReader(stream);
ImageCount = reader.ReadUInt16();
ImageCount = s.ReadUInt16();
//Last offset is pointer to end of file.
uint[] offsets = new uint[ImageCount + 1];
uint temp = reader.ReadUInt32();
uint temp = s.ReadUInt32();
//If fourth byte in file is non-zero, the offsets are two bytes each.
bool twoByteOffsets = (temp & 0xFF0000) > 0;
@@ -100,13 +98,13 @@ namespace OpenRA.FileFormats
offsets[0] = temp + 2;
for (int i = twoByteOffsets ? 2 : 1; i < ImageCount + 1; i++)
offsets[i] = (twoByteOffsets ? reader.ReadUInt16() : reader.ReadUInt32()) + 2;
offsets[i] = (twoByteOffsets ? s.ReadUInt16() : s.ReadUInt32()) + 2;
for (int i = 0; i < ImageCount; i++)
{
reader.BaseStream.Seek(offsets[i], SeekOrigin.Begin);
Dune2ImageHeader header = new Dune2ImageHeader(reader);
byte[] imgData = reader.ReadBytes(header.FileSize);
s.Seek(offsets[i], SeekOrigin.Begin);
Dune2ImageHeader header = new Dune2ImageHeader(s);
byte[] imgData = s.ReadBytes(header.FileSize);
header.Image = new byte[header.Height * header.Width];
//Decode image data

View File

@@ -0,0 +1,57 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.IO;
using System.Linq;
namespace OpenRA.FileFormats
{
public class HvaReader
{
public readonly uint FrameCount;
public readonly uint LimbCount;
public readonly float[] Transforms;
public HvaReader(Stream s)
{
// Index swaps for transposing a matrix
var ids = new byte[]{0,4,8,12,1,5,9,13,2,6,10,14};
s.Seek(16, SeekOrigin.Begin);
FrameCount = s.ReadUInt32();
LimbCount = s.ReadUInt32();
// Skip limb names
s.Seek(16*LimbCount, SeekOrigin.Current);
Transforms = new float[16*FrameCount*LimbCount];
for (var j = 0; j < FrameCount; j++)
for (var i = 0; i < LimbCount; i++)
{
// Convert to column-major matrices and add the final matrix row
var c = 16*(LimbCount*j + i);
Transforms[c + 3] = 0;
Transforms[c + 7] = 0;
Transforms[c + 11] = 0;
Transforms[c + 15] = 1;
for (var k = 0; k < 12; k++)
Transforms[c + ids[k]] = s.ReadFloat();
}
}
public static HvaReader Load(string filename)
{
using (var s = File.OpenRead(filename))
return new HvaReader(s);
}
}
}

View File

@@ -37,6 +37,7 @@ namespace OpenRA.FileFormats.Graphics
IVertexBuffer<Vertex> CreateVertexBuffer( int length );
ITexture CreateTexture( Bitmap bitmap );
ITexture CreateTexture();
IFrameBuffer CreateFrameBuffer(Size s);
IShader CreateShader( string name );
Size WindowSize { get; }
@@ -50,6 +51,12 @@ namespace OpenRA.FileFormats.Graphics
void SetLineWidth( float width );
void EnableScissor( int left, int top, int width, int height );
void DisableScissor();
void EnableDepthBuffer();
void DisableDepthBuffer();
void EnableAlphaBlending();
void DisableAlphaBlending();
}
public interface IVertexBuffer<T>
@@ -60,8 +67,11 @@ namespace OpenRA.FileFormats.Graphics
public interface IShader
{
void SetVec(string name, float x);
void SetVec(string name, float x, float y);
void SetVec(string name, float[] vec, int length);
void SetTexture(string param, ITexture texture);
void SetMatrix(string param, float[] mtx);
void Render(Action a);
}
@@ -70,6 +80,15 @@ namespace OpenRA.FileFormats.Graphics
void SetData(Bitmap bitmap);
void SetData(uint[,] colors);
void SetData(byte[] colors, int width, int height);
byte[] GetData();
Size Size { get; }
}
public interface IFrameBuffer
{
void Bind();
void Unbind();
ITexture Texture { get; }
}
public enum PrimitiveType

View File

@@ -62,9 +62,9 @@ namespace OpenRA.FileFormats
int recurseDepth = 0;
public ShpReader( Stream stream )
public ShpReader(Stream stream)
{
using( var reader = new BinaryReader( stream ) )
using (var reader = new BinaryReader(stream))
{
ImageCount = reader.ReadUInt16();
reader.ReadUInt16();
@@ -73,60 +73,60 @@ namespace OpenRA.FileFormats
Height = reader.ReadUInt16();
reader.ReadUInt32();
for( int i = 0 ; i < ImageCount ; i++ )
headers.Add( new ImageHeader( reader ) );
for (int i = 0 ; i < ImageCount ; i++)
headers.Add(new ImageHeader(reader));
new ImageHeader( reader ); // end-of-file header
new ImageHeader( reader ); // all-zeroes header
new ImageHeader(reader); // end-of-file header
new ImageHeader(reader); // all-zeroes header
var offsets = headers.ToDictionary(h => h.Offset, h =>h);
for( int i = 0 ; i < ImageCount ; i++ )
for (int i = 0 ; i < ImageCount ; i++)
{
var h = headers[ i ];
if( h.Format == Format.Format20 )
h.RefImage = headers[ i - 1 ];
if (h.Format == Format.Format20)
h.RefImage = headers[i - 1];
else if( h.Format == Format.Format40 )
if( !offsets.TryGetValue( h.RefOffset, out h.RefImage ) )
throw new InvalidDataException( "Reference doesnt point to image data {0}->{1}".F(h.Offset, h.RefOffset) );
else if (h.Format == Format.Format40)
if (!offsets.TryGetValue(h.RefOffset, out h.RefImage))
throw new InvalidDataException("Reference doesnt point to image data {0}->{1}".F(h.Offset, h.RefOffset));
}
foreach( ImageHeader h in headers )
Decompress( stream, h );
foreach (ImageHeader h in headers)
Decompress(stream, h);
}
}
public ImageHeader this[ int index ]
public ImageHeader this[int index]
{
get { return headers[ index ]; }
get { return headers[index]; }
}
void Decompress( Stream stream, ImageHeader h )
void Decompress(Stream stream, ImageHeader h)
{
if( recurseDepth > ImageCount )
throw new InvalidDataException( "Format20/40 headers contain infinite loop" );
if (recurseDepth > ImageCount)
throw new InvalidDataException("Format20/40 headers contain infinite loop");
switch( h.Format )
switch(h.Format)
{
case Format.Format20:
case Format.Format40:
{
if( h.RefImage.Image == null )
if (h.RefImage.Image == null)
{
++recurseDepth;
Decompress( stream, h.RefImage );
Decompress(stream, h.RefImage);
--recurseDepth;
}
h.Image = CopyImageData( h.RefImage.Image );
h.Image = CopyImageData(h.RefImage.Image);
Format40.DecodeInto(ReadCompressedData(stream, h), h.Image);
break;
}
case Format.Format80:
{
var imageBytes = new byte[ Width * Height ];
Format80.DecodeInto( ReadCompressedData( stream, h ), imageBytes );
var imageBytes = new byte[Width * Height];
Format80.DecodeInto(ReadCompressedData(stream, h), imageBytes);
h.Image = imageBytes;
break;
}
@@ -135,11 +135,11 @@ namespace OpenRA.FileFormats
}
}
static byte[] ReadCompressedData( Stream stream, ImageHeader h )
static byte[] ReadCompressedData(Stream stream, ImageHeader h)
{
stream.Position = h.Offset;
// Actually, far too big. There's no length field with the correct length though :(
var compressedLength = (int)( stream.Length - stream.Position );
// TODO: Actually, far too big. There's no length field with the correct length though :(
var compressedLength = (int)(stream.Length - stream.Position);
var compressedBytes = new byte[ compressedLength ];
stream.Read( compressedBytes, 0, compressedLength );
@@ -147,11 +147,11 @@ namespace OpenRA.FileFormats
return compressedBytes;
}
byte[] CopyImageData( byte[] baseImage )
byte[] CopyImageData(byte[] baseImage)
{
var imageData = new byte[ Width * Height ];
for( int i = 0 ; i < Width * Height ; i++ )
imageData[ i ] = baseImage[ i ];
var imageData = new byte[Width * Height];
for (int i = 0 ; i < Width * Height ; i++)
imageData[i] = baseImage[i];
return imageData;
}

View File

@@ -0,0 +1,615 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 LICENSE.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
namespace OpenRA.FileFormats
{
public struct Header
{
public ushort A;
// Unknown
// Width and Height of the images
public ushort Width;
public ushort Height;
public ushort NumImages;
}
public class HeaderImage
{
public ushort x;
public ushort y;
public ushort cx;
public ushort cy;
// cx and cy are width n height of stored image
public byte compression;
public byte[] align;
public byte[] transparent;
public int zero;
public int offset;
public byte[] Image;
}
public struct SHPData
{
public HeaderImage HeaderImage;
public byte[] Databuffer;
public byte[] FrameImage;
}
public struct SHP
{
public Header Header;
public SHPData[] Data;
}
public class ShpTSReader : IEnumerable<HeaderImage>
{
public readonly int ImageCount;
public readonly ushort Width;
public readonly ushort Height;
public readonly ushort Width2;
public readonly ushort Height2;
public int arroff = 0;
public int erri = 0;
public int errj = 0;
public int errk = 0;
public int errl = 0;
public static int FindNextOffsetFrom(SHP SHP, int Init, int Last)
{
int result;
result = 0;
Last++;
while ((result == 0) && (Init < Last))
{
result = SHP.Data[Init].HeaderImage.offset;
Init++;
}
return result;
}
private readonly List<HeaderImage> headers = new List<HeaderImage>();
public ShpTSReader(Stream s)
{
SHP SHP = new SHP();
int FileSize;
int x;
int k = 0;
int l = 0;
int ImageSize;
int NextOffset;
byte[] FData;
byte cp;
byte[] Databuffer;
FileSize = (int)s.Length;
// Get Header
SHP.Header.A = s.ReadUInt16();
SHP.Header.Width = s.ReadUInt16();
SHP.Header.Height = s.ReadUInt16();
SHP.Header.NumImages = s.ReadUInt16();
SHP.Data = new SHPData[SHP.Header.NumImages + 1];
ImageCount = SHP.Header.NumImages;
for (x = 1; x <= SHP.Header.NumImages; x++)
{
SHP.Data[x].HeaderImage = new HeaderImage();
SHP.Data[x].HeaderImage.x = s.ReadUInt16();
SHP.Data[x].HeaderImage.y = s.ReadUInt16();
SHP.Data[x].HeaderImage.cx = s.ReadUInt16();
SHP.Data[x].HeaderImage.cy = s.ReadUInt16();
SHP.Data[x].HeaderImage.compression = s.ReadUInt8();
SHP.Data[x].HeaderImage.align = s.ReadBytes(3);
s.ReadInt32();
SHP.Data[x].HeaderImage.zero = s.ReadUInt8();
SHP.Data[x].HeaderImage.transparent = s.ReadBytes(3);
SHP.Data[x].HeaderImage.offset = s.ReadInt32();
}
Width = SHP.Header.Width;
Height = SHP.Header.Height;
for (int i = 0; i < ImageCount; i++)
{
headers.Add(SHP.Data[i+1].HeaderImage);
}
// Read and decode each image from the file
for (x = 1; x <= SHP.Header.NumImages; x++)
{
headers[x - 1].Image = new byte[(Width * Height)];
for (int i = 0; i < headers[x - 1].Image.Length; i++)
headers[x - 1].Image[i] = 0;
FData = new byte[(Width * Height)];
// Does it really reads the frame?
if (SHP.Data[x].HeaderImage.offset != 0)
{
try
{
// Now it checks the compression:
if ((SHP.Data[x].HeaderImage.compression == 3))
{
// decode it
// Compression 3
NextOffset = FindNextOffsetFrom(SHP, x + 1, SHP.Header.NumImages);
if (NextOffset != 0)
{
ImageSize = NextOffset - SHP.Data[x].HeaderImage.offset;
Databuffer = new byte[ImageSize];
for (int i = 0; i < ImageSize; i++)
{
s.Seek(SHP.Data[x].HeaderImage.offset + i, SeekOrigin.Begin);
Databuffer[i] = s.ReadUInt8();
}
SHP.Data[x].Databuffer = new byte[(SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy)];
Decode3(Databuffer, ref SHP.Data[x].Databuffer, SHP.Data[x].HeaderImage.cx, SHP.Data[x].HeaderImage.cy, ref FileSize);
k = 0;
l = 0;
for (int i = 0; i < Height; i++)
{
erri = i;
for (int j = SHP.Data[x].HeaderImage.x; j < Width; j++)
{
errj = j;
errl = l;
errk = k;
arroff = i + j + l;
if (((j + 1) > (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x)) || ((i + 1) > (SHP.Data[x].HeaderImage.cy)))
cp = 0;
else
cp = SHP.Data[x].Databuffer[i + (j - SHP.Data[x].HeaderImage.x) + l];
FData[i + j + k] = cp;
if (j == (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x - 1))
l = l + (SHP.Data[x].HeaderImage.cx - 1);
if (j == (Width - 1))
k = k + (Width - 1);
}
}
//FData = headers[x - 1].Image;
k = 0;
for (int i = 0; i < (Height - SHP.Data[x].HeaderImage.y); i++)
{
for (int j = 0; j < Width; j++)
{
headers[x - 1].Image[i + j + k + (Width * SHP.Data[x].HeaderImage.y)] = FData[i + j + k];
if (j == (Width - 1))
{
k = k + (Width - 1);
}
}
}
}
else
{
ImageSize = 0;
ImageSize = FileSize - SHP.Data[x].HeaderImage.offset;
Databuffer = new byte[ImageSize];
for (int i = 0; i < ImageSize; i++)
{
s.Seek(SHP.Data[x].HeaderImage.offset + i, SeekOrigin.Begin);
Databuffer[i] = s.ReadUInt8();
}
SHP.Data[x].Databuffer = new byte[((SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy))];
Decode3(Databuffer, ref SHP.Data[x].Databuffer, SHP.Data[x].HeaderImage.cx, SHP.Data[x].HeaderImage.cy, ref ImageSize);
k = 0;
l = 0;
for (int i = 0; i < Height; i++)
{
erri = i;
for (int j = SHP.Data[x].HeaderImage.x; j < Width; j++)
{
errj = j;
errl = l;
errk = k;
arroff = i + j + l;
if (((j + 1) > (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x)) || ((i + 1) > (SHP.Data[x].HeaderImage.cy)))
cp = 0;
else
cp = SHP.Data[x].Databuffer[i + (j - SHP.Data[x].HeaderImage.x) + l];
FData[i + j + k] = cp;
if (j == (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x - 1))
l = l + (SHP.Data[x].HeaderImage.cx - 1);
if (j == (Width - 1))
k = k + (Width - 1);
}
}
//FData = headers[x - 1].Image;
k = 0;
for (int i = 0; i < (Height - SHP.Data[x].HeaderImage.y); i++)
{
for (int j = 0; j < Width; j++)
{
headers[x - 1].Image[i + j + k + (Width * SHP.Data[x].HeaderImage.y)] = FData[i + j + k];
if (j == (Width - 1))
{
k = k + (Width - 1);
}
}
}
}
}
else if ((SHP.Data[x].HeaderImage.compression == 2))
{
NextOffset = FindNextOffsetFrom(SHP, x + 1, SHP.Header.NumImages);
if (NextOffset != 0)
{
ImageSize = NextOffset - SHP.Data[x].HeaderImage.offset;
SHP.Data[x].Databuffer = new byte[(SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy)];
Databuffer = new byte[ImageSize];
for (int i = 0; i < ImageSize; i++)
{
s.Seek(SHP.Data[x].HeaderImage.offset + i, SeekOrigin.Begin);
Databuffer[i] = s.ReadUInt8();
}
Decode2(Databuffer, ref SHP.Data[x].Databuffer, SHP.Data[x].HeaderImage.cx, SHP.Data[x].HeaderImage.cy, ref ImageSize);
k = 0;
l = 0;
for (int i = 0; i < Height; i++)
{
erri = i;
for (int j = SHP.Data[x].HeaderImage.x; j < Width; j++)
{
errj = j;
errl = l;
errk = k;
arroff = i + j + l;
if (((j + 1) > (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x)) || ((i + 1) > (SHP.Data[x].HeaderImage.cy)))
cp = 0;
else
cp = SHP.Data[x].Databuffer[i + (j - SHP.Data[x].HeaderImage.x) + l];
FData[i + j + k] = cp;
if (j == (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x - 1))
l = l + (SHP.Data[x].HeaderImage.cx - 1);
if (j == (Width - 1))
k = k + (Width - 1);
}
}
//FData = headers[x - 1].Image;
k = 0;
for (int i = 0; i < (Height - SHP.Data[x].HeaderImage.y); i++)
{
for (int j = 0; j < Width; j++)
{
headers[x - 1].Image[i + j + k + (Width * SHP.Data[x].HeaderImage.y)] = FData[i + j + k];
if (j == (Width - 1))
{
k = k + (Width - 1);
}
}
}
// Compression 2
}
else
{
ImageSize = 0;
ImageSize = FileSize - SHP.Data[x].HeaderImage.offset;
Databuffer = new byte[ImageSize];
for (int i = 0; i < ImageSize; i++)
{
s.Seek(SHP.Data[x].HeaderImage.offset + i, SeekOrigin.Begin);
Databuffer[i] = s.ReadUInt8();
}
SHP.Data[x].Databuffer = new byte[((SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy))];
Decode2(Databuffer, ref SHP.Data[x].Databuffer, SHP.Data[x].HeaderImage.cx, SHP.Data[x].HeaderImage.cy, ref ImageSize);
k = 0;
l = 0;
for (int i = 0; i < Height; i++)
{
erri = i;
for (int j = SHP.Data[x].HeaderImage.x; j < Width; j++)
{
errj = j;
errl = l;
errk = k;
arroff = i + j + l;
if (((j + 1) > (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x)) || ((i + 1) > (SHP.Data[x].HeaderImage.cy)))
cp = 0;
else
cp = SHP.Data[x].Databuffer[i + (j - SHP.Data[x].HeaderImage.x) + l];
FData[i + j + k] = cp;
if (j == (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x - 1))
l = l + (SHP.Data[x].HeaderImage.cx - 1);
if (j == (Width - 1))
k = k + (Width - 1);
}
}
//FData = headers[x - 1].Image;
k = 0;
for (int i = 0; i < (Height - SHP.Data[x].HeaderImage.y); i++)
{
for (int j = 0; j < Width; j++)
{
headers[x - 1].Image[i + j + k + (Width * SHP.Data[x].HeaderImage.y)] = FData[i + j + k];
if (j == (Width - 1))
{
k = k + (Width - 1);
}
}
}
// Compression 2
}
}
else
{
// Compression 1
ImageSize = (int)(SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy);
Databuffer = new byte[ImageSize];
for (int i = 0; i < ImageSize; i++)
{
s.Seek(SHP.Data[x].HeaderImage.offset + i, SeekOrigin.Begin);
Databuffer[i] = s.ReadUInt8();
}
SHP.Data[x].Databuffer = new byte[(SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy)];
SHP.Data[x].Databuffer = Databuffer;
k = 0;
l = 0;
for (int i = 0; i < Height; i++)
{
erri = i;
for (int j = SHP.Data[x].HeaderImage.x; j < Width; j++)
{
errj = j;
errl = l;
errk = k;
arroff = i + j + l;
if (((j + 1) > (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x)) || ((i + 1) > (SHP.Data[x].HeaderImage.cy)))
cp = 0;
else
cp = SHP.Data[x].Databuffer[i + (j - SHP.Data[x].HeaderImage.x) + l];
FData[i + j + k] = cp;
if (j == (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x - 1))
l = l + (SHP.Data[x].HeaderImage.cx - 1);
if (j == (Width - 1))
k = k + (Width - 1);
}
}
//FData = headers[x - 1].Image;
k = 0;
for (int i = 0; i < (Height - SHP.Data[x].HeaderImage.y); i++)
{
for (int j = 0; j < Width; j++)
{
headers[x - 1].Image[i + j + k + (Width * SHP.Data[x].HeaderImage.y)] = FData[i + j + k];
if (j == (Width - 1))
{
k = k + (Width - 1);
}
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
// Set the shp's databuffer to the result after decompression
}
//Width = Width2;
//Height = Height2;
}
public HeaderImage this[int index]
{
get { return headers[index]; }
}
public static void ReInterpretWordFromBytes(byte Byte1, byte Byte2, ref ushort FullValue)
{
FullValue = (ushort)((Byte2 * 256) + Byte1);
}
public static void ReInterpretWordFromBytes(byte Byte1, byte Byte2, ref uint FullValue)
{
FullValue = (uint)((Byte2 * 256) + Byte1);
}
// Compression 3:
public static void Decode3(byte[] Source, ref byte[] Dest, int cx, int cy, ref int max)
{
int SP;
int DP;
int x;
int y;
int Count;
int v;
int maxdp;
ushort Pos;
maxdp = cx * cy;
SP = 0;
DP = 0;
Pos = 0;
try
{
for (y = 1; y <= cy; y++)
{
ReInterpretWordFromBytes(Source[SP], Source[SP + 1], ref Pos);
Count = Pos - 2;
SP = SP + 2;
x = 0;
while (Count > 0)
{
Count = Count - 1;
if ((SP > max) || (DP > maxdp))
{
break;
}
else
{
// SP has reached max value, exit
v = Source[SP];
SP++;
if (v != 0)
{
if ((SP > max) || (DP > maxdp))
{
break;
}
else
{
x++;
Dest[DP] += (byte)v;
}
DP++;
}
else
{
Count -= 1;
v = Source[SP];
SP++;
if ((x + v) > cx)
{
v = cx - x;
}
x = x + v;
while (v > 0)
{
if ((SP > max) || (DP > maxdp))
{
break;
}
else
{
v -= 1;
Dest[DP] = 0;
}
DP++;
// SP has reached max value, exit
}
}
}
}
if ((SP >= max) || (DP >= maxdp))
{
return;
}
// SP has reached max value, exit
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public static void Decode2(byte[] Source, ref byte[] Dest, int cx, int cy, ref int max)
{
int SP;
int DP;
int y;
int Count;
int maxdp;
ushort Pos;
maxdp = cx * cy;
SP = 0;
DP = 0;
Pos = 0;
try
{
for (y = 1; y <= cy; y++)
{
ReInterpretWordFromBytes(Source[SP], Source[SP + 1], ref Pos);
Count = Pos - 2;
SP += 2;
while (Count > 0)
{
Count -= 1;
if ((SP > max) || (DP > maxdp))
{
return;
}
// SP has reached max value, exit
Dest[DP] = Source[SP];
SP++;
DP++;
}
if ((SP >= max) || (DP >= maxdp))
{
return;
}
// SP has reached max value, exit
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public IEnumerator<HeaderImage> GetEnumerator()
{
return headers.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public Size Size { get { return new Size(Width, Height); } }
}
}

View File

@@ -24,5 +24,12 @@ namespace OpenRA.FileFormats.Graphics
this.u = uv.X; this.v = uv.Y;
this.p = pc.X; this.c = pc.Y;
}
public Vertex(float[] xyz, float2 uv, float2 pc)
{
this.x = xyz[0]; this.y = xyz[1]; this.z = xyz[2];
this.u = uv.X; this.v = uv.Y;
this.p = pc.X; this.c = pc.Y;
}
}
}

View File

@@ -46,67 +46,66 @@ namespace OpenRA.FileFormats
public byte[] AudioData { get { return audioData; } }
public int CurrentFrame { get { return currentFrame; } }
public VqaReader( Stream stream )
public VqaReader(Stream stream)
{
this.stream = stream;
BinaryReader reader = new BinaryReader( stream );
// Decode FORM chunk
if (new String(reader.ReadChars(4)) != "FORM")
if (stream.ReadASCII(4) != "FORM")
throw new InvalidDataException("Invalid vqa (invalid FORM section)");
/*var length = */ reader.ReadUInt32();
/*var length = */ stream.ReadUInt32();
if (new String(reader.ReadChars(8)) != "WVQAVQHD")
if (stream.ReadASCII(8) != "WVQAVQHD")
throw new InvalidDataException("Invalid vqa (not WVQAVQHD)");
/* var length = */reader.ReadUInt32();
/* var length = */stream.ReadUInt32();
/*var version = */reader.ReadUInt16();
/*var flags = */reader.ReadUInt16();
Frames = reader.ReadUInt16();
Width = reader.ReadUInt16();
Height = reader.ReadUInt16();
/*var version = */stream.ReadUInt16();
/*var flags = */stream.ReadUInt16();
Frames = stream.ReadUInt16();
Width = stream.ReadUInt16();
Height = stream.ReadUInt16();
blockWidth = reader.ReadByte();
blockHeight = reader.ReadByte();
Framerate = reader.ReadByte();
cbParts = reader.ReadByte();
blockWidth = stream.ReadUInt8();
blockHeight = stream.ReadUInt8();
Framerate = stream.ReadUInt8();
cbParts = stream.ReadUInt8();
blocks = new int2(Width / blockWidth, Height / blockHeight);
numColors = reader.ReadUInt16();
/*var maxBlocks = */reader.ReadUInt16();
/*var unknown1 = */reader.ReadUInt16();
/*var unknown2 = */reader.ReadUInt32();
numColors = stream.ReadUInt16();
/*var maxBlocks = */stream.ReadUInt16();
/*var unknown1 = */stream.ReadUInt16();
/*var unknown2 = */stream.ReadUInt32();
// Audio
/*var freq = */reader.ReadUInt16();
/*var channels = */reader.ReadByte();
/*var bits = */reader.ReadByte();
/*var unknown3 = */reader.ReadChars(14);
/*var freq = */stream.ReadUInt16();
/*var channels = */stream.ReadByte();
/*var bits = */stream.ReadByte();
/*var unknown3 = */stream.ReadBytes(14);
var frameSize = Exts.NextPowerOf2(Math.Max(Width,Height));
var frameSize = Exts.NextPowerOf2(Math.Max(Width, Height));
cbf = new byte[Width*Height];
cbp = new byte[Width*Height];
palette = new uint[numColors];
origData = new byte[2*blocks.X*blocks.Y];
frameData = new uint[frameSize,frameSize];
frameData = new uint[frameSize, frameSize];
var type = new String(reader.ReadChars(4));
var type = stream.ReadASCII(4);
if (type != "FINF")
{
reader.ReadBytes(27);
type = new String(reader.ReadChars(4));
stream.Seek(27, SeekOrigin.Current);
type = stream.ReadASCII(4);
}
/*var length = */reader.ReadUInt16();
/*var unknown4 = */reader.ReadUInt16();
/*var length = */stream.ReadUInt16();
/*var unknown4 = */stream.ReadUInt16();
// Frame offsets
offsets = new UInt32[Frames];
for (int i = 0; i < Frames; i++)
{
offsets[i] = reader.ReadUInt32();
if (offsets[i] > 0x40000000) offsets[i] -= 0x40000000;
offsets[i] = stream.ReadUInt32();
if (offsets[i] > 0x40000000)
offsets[i] -= 0x40000000;
offsets[i] <<= 1;
}
@@ -130,28 +129,28 @@ namespace OpenRA.FileFormats
for (var i = 0; i < Frames; i++)
{
stream.Seek(offsets[i], SeekOrigin.Begin);
BinaryReader reader = new BinaryReader(stream);
var end = (i < Frames - 1) ? offsets[i + 1] : stream.Length;
while (reader.BaseStream.Position < end)
while (stream.Position < end)
{
var type = new String(reader.ReadChars(4));
var length = int2.Swap(reader.ReadUInt32());
var type = stream.ReadASCII(4);
var length = int2.Swap(stream.ReadUInt32());
switch (type)
{
case "SND0":
case "SND2":
var rawAudio = reader.ReadBytes((int)length);
var rawAudio = stream.ReadBytes((int)length);
ms.Write(rawAudio);
compressed = (type == "SND2");
break;
default:
reader.ReadBytes((int)length);
stream.ReadBytes((int)length);
break;
}
if (reader.PeekChar() == 0) reader.ReadByte();
// Chunks are aligned on even bytes; advance by a byte if the next one is null
if (stream.Peek() == 0) stream.ReadByte();
}
}
@@ -171,48 +170,47 @@ namespace OpenRA.FileFormats
// Seek to the start of the frame
stream.Seek(offsets[currentFrame], SeekOrigin.Begin);
BinaryReader reader = new BinaryReader(stream);
var end = (currentFrame < Frames - 1) ? offsets[currentFrame+1] : stream.Length;
while(reader.BaseStream.Position < end)
while (stream.Position < end)
{
var type = new String(reader.ReadChars(4));
var length = int2.Swap(reader.ReadUInt32());
var type = stream.ReadASCII(4);
var length = int2.Swap(stream.ReadUInt32());
switch(type)
{
case "VQFR":
DecodeVQFR(reader);
DecodeVQFR(stream);
break;
default:
// Don't parse sound here.
reader.ReadBytes((int)length);
stream.ReadBytes((int)length);
break;
}
// Chunks are aligned on even bytes; advance by a byte if the next one is null
if (reader.PeekChar() == 0) reader.ReadByte();
if (stream.Peek() == 0) stream.ReadByte();
}
}
// VQA Frame
public void DecodeVQFR(BinaryReader reader)
public void DecodeVQFR(Stream s)
{
while(true)
while (true)
{
// Chunks are aligned on even bytes; may be padded with a single null
if (reader.PeekChar() == 0) reader.ReadByte();
var type = new String(reader.ReadChars(4));
int subchunkLength = (int)int2.Swap(reader.ReadUInt32());
if (s.Peek() == 0) s.ReadByte();
var type = s.ReadASCII(4);
int subchunkLength = (int)int2.Swap(s.ReadUInt32());
switch(type)
{
// Full frame-modifier
case "CBFZ":
Format80.DecodeInto( reader.ReadBytes(subchunkLength), cbf );
Format80.DecodeInto(s.ReadBytes(subchunkLength), cbf);
break;
case "CBF0":
cbf = reader.ReadBytes(subchunkLength);
cbf = s.ReadBytes(subchunkLength);
break;
// frame-modifier chunk
@@ -224,12 +222,12 @@ namespace OpenRA.FileFormats
if (type == "CBP0")
cbf = (byte[])cbp.Clone();
else
Format80.DecodeInto( cbp, cbf );
Format80.DecodeInto(cbp, cbf);
cbOffset = cbChunk = 0;
}
var bytes = reader.ReadBytes(subchunkLength);
var bytes = s.ReadBytes(subchunkLength);
bytes.CopyTo(cbp,cbOffset);
cbOffset += subchunkLength;
cbChunk++;
@@ -239,16 +237,16 @@ namespace OpenRA.FileFormats
case "CPL0":
for (int i = 0; i < numColors; i++)
{
byte r = (byte)(reader.ReadByte() << 2);
byte g = (byte)(reader.ReadByte() << 2);
byte b = (byte)(reader.ReadByte() << 2);
byte r = (byte)(s.ReadUInt8() << 2);
byte g = (byte)(s.ReadUInt8() << 2);
byte b = (byte)(s.ReadUInt8() << 2);
palette[i] = (uint)((255 << 24) | (r << 16) | (g << 8) | b);
}
break;
// Frame data
case "VPTZ":
Format80.DecodeInto( reader.ReadBytes(subchunkLength), origData );
Format80.DecodeInto(s.ReadBytes(subchunkLength), origData);
// This is the last subchunk
return;
default:

View File

@@ -0,0 +1,158 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
namespace OpenRA.FileFormats
{
public enum NormalType { TiberianSun = 2, RedAlert2 = 4 }
public class VxlElement
{
public byte Color;
public byte Normal;
}
public class VxlLimb
{
public string Name;
public float Scale;
public float[] Bounds;
public byte[] Size;
public NormalType Type;
public uint VoxelCount;
public Dictionary<byte, VxlElement>[,] VoxelMap;
}
public class VxlReader
{
public readonly uint LimbCount;
public VxlLimb[] Limbs;
uint BodySize;
void ReadVoxelData(Stream s, VxlLimb l)
{
var baseSize = l.Size[0]*l.Size[1];
var colStart = new int[baseSize];
for (var i = 0; i < baseSize; i++)
colStart[i] = s.ReadInt32();
s.Seek(4*baseSize, SeekOrigin.Current);
var dataStart = s.Position;
// Count the voxels in this limb
l.VoxelCount = 0;
for (var i = 0; i < baseSize; i++)
{
// Empty column
if (colStart[i] == -1)
continue;
s.Seek(dataStart + colStart[i], SeekOrigin.Begin);
var z = 0;
do
{
z += s.ReadUInt8();
var count = s.ReadUInt8();
z += count;
l.VoxelCount += count;
s.Seek(2*count + 1, SeekOrigin.Current);
} while (z < l.Size[2]);
}
// Read the data
l.VoxelMap = new Dictionary<byte, VxlElement>[l.Size[0],l.Size[1]];
for (var i = 0; i < baseSize; i++)
{
// Empty column
if (colStart[i] == -1)
continue;
s.Seek(dataStart + colStart[i], SeekOrigin.Begin);
byte x = (byte)(i % l.Size[0]);
byte y = (byte)(i / l.Size[0]);
byte z = 0;
l.VoxelMap[x,y] = new Dictionary<byte, VxlElement>();
do
{
z += s.ReadUInt8();
var count = s.ReadUInt8();
for (var j = 0; j < count; j++)
{
var v = new VxlElement();
v.Color = s.ReadUInt8();
v.Normal = s.ReadUInt8();
l.VoxelMap[x,y].Add(z, v);
z++;
}
// Skip duplicate count
s.ReadUInt8();
} while (z < l.Size[2]);
}
}
public VxlReader(Stream s)
{
if (!s.ReadASCII(16).StartsWith("Voxel Animation"))
throw new InvalidDataException("Invalid vxl header");
s.ReadUInt32();
LimbCount = s.ReadUInt32();
s.ReadUInt32();
BodySize = s.ReadUInt32();
s.Seek(770, SeekOrigin.Current);
// Read Limb headers
Limbs = new VxlLimb[LimbCount];
for (var i = 0; i < LimbCount; i++)
{
Limbs[i] = new VxlLimb();
Limbs[i].Name = s.ReadASCII(16);
s.Seek(12, SeekOrigin.Current);
}
// Skip to the Limb footers
s.Seek(802 + 28*LimbCount + BodySize, SeekOrigin.Begin);
var LimbDataOffset = new uint[LimbCount];
for (var i = 0; i < LimbCount; i++)
{
LimbDataOffset[i] = s.ReadUInt32();
s.Seek(8, SeekOrigin.Current);
Limbs[i].Scale = s.ReadFloat();
s.Seek(48, SeekOrigin.Current);
Limbs[i].Bounds = new float[6];
for (var j = 0; j < 6; j++)
Limbs[i].Bounds[j] = s.ReadFloat();
Limbs[i].Size = s.ReadBytes(3);
Limbs[i].Type = (NormalType)s.ReadByte();
}
for (var i = 0; i < LimbCount; i++)
{
s.Seek(802 + 28*LimbCount + LimbDataOffset[i], SeekOrigin.Begin);
ReadVoxelData(s, Limbs[i]);
}
}
public static VxlReader Load(string filename)
{
using (var s = File.OpenRead(filename))
return new VxlReader(s);
}
}
}

View File

@@ -9,6 +9,7 @@
#endregion
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace OpenRA.FileFormats
@@ -18,9 +19,12 @@ namespace OpenRA.FileFormats
public class Manifest
{
public readonly string[]
Mods, Folders, Packages, Rules, ServerTraits,
Sequences, Cursors, Chrome, Assemblies, ChromeLayout,
Weapons, Voices, Notifications, Music, Movies, TileSets, ChromeMetrics;
Mods, Folders, Rules, ServerTraits,
Sequences, VoxelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
Weapons, Voices, Notifications, Music, Movies, TileSets,
ChromeMetrics, PackageContents;
public readonly Dictionary<string, string> Packages;
public readonly MiniYaml LoadScreen;
public readonly Dictionary<string, Pair<string,int>> Fonts;
public readonly int TileSize = 24;
@@ -29,15 +33,16 @@ namespace OpenRA.FileFormats
{
Mods = mods;
var yaml = new MiniYaml(null, mods
.Select(m => MiniYaml.FromFile("mods/" + m + "/mod.yaml"))
.Select(m => MiniYaml.FromFile("mods{0}{1}{0}mod.yaml".F(Path.DirectorySeparatorChar, m)))
.Aggregate(MiniYaml.MergeLiberal)).NodesDict;
// TODO: Use fieldloader
Folders = YamlList(yaml, "Folders");
Packages = YamlList(yaml, "Packages");
Packages = yaml["Packages"].NodesDict.ToDictionary(x => x.Key, x => x.Value.Value);
Rules = YamlList(yaml, "Rules");
ServerTraits = YamlList(yaml, "ServerTraits");
Sequences = YamlList(yaml, "Sequences");
VoxelSequences = YamlList(yaml, "VoxelSequences");
Cursors = YamlList(yaml, "Cursors");
Chrome = YamlList(yaml, "Chrome");
Assemblies = YamlList(yaml, "Assemblies");
@@ -49,6 +54,7 @@ namespace OpenRA.FileFormats
Movies = YamlList(yaml, "Movies");
TileSets = YamlList(yaml, "TileSets");
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
PackageContents = YamlList(yaml, "PackageContents");
LoadScreen = yaml["LoadScreen"];
Fonts = yaml["Fonts"].NodesDict.ToDictionary(x => x.Key,

View File

@@ -39,17 +39,17 @@ namespace OpenRA.FileFormats
public bool PickAny;
public string Category;
[FieldLoader.LoadUsing( "LoadTiles" )]
[FieldLoader.LoadUsing("LoadTiles")]
public Dictionary<byte, string> Tiles = new Dictionary<byte, string>();
public TileTemplate() {}
public TileTemplate(MiniYaml my) { FieldLoader.Load( this, my ); }
public TileTemplate(MiniYaml my) { FieldLoader.Load(this, my); }
static object LoadTiles( MiniYaml y )
static object LoadTiles(MiniYaml y)
{
return y.NodesDict["Tiles"].NodesDict.ToDictionary(
t => byte.Parse(t.Key),
t => t.Value.Value );
t => t.Value.Value);
}
static readonly string[] Fields = { "Id", "Image", "Size", "PickAny" };
@@ -60,12 +60,14 @@ namespace OpenRA.FileFormats
foreach (var field in Fields)
{
FieldInfo f = this.GetType().GetField(field);
if (f.GetValue(this) == null) continue;
root.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) );
if (f.GetValue(this) == null)
continue;
root.Add(new MiniYamlNode(field, FieldSaver.FormatValue(this, f)));
}
root.Add( new MiniYamlNode( "Tiles", null,
Tiles.Select( x => new MiniYamlNode( x.Key.ToString(), x.Value ) ).ToList() ) );
root.Add(new MiniYamlNode("Tiles", null,
Tiles.Select(x => new MiniYamlNode(x.Key.ToString(), x.Value)).ToList()));
return new MiniYaml(null, root);
}
@@ -90,9 +92,9 @@ namespace OpenRA.FileFormats
public TileSet() {}
public TileSet( string filepath )
public TileSet(string filepath)
{
var yaml = MiniYaml.DictFromFile( filepath );
var yaml = MiniYaml.DictFromFile(filepath);
// General info
FieldLoader.Load(this, yaml["General"]);
@@ -110,7 +112,7 @@ namespace OpenRA.FileFormats
{
foreach (var t in Templates)
if (t.Value.Data == null)
using( var s = FileSystem.OpenWithExts(t.Value.Image, Extensions) )
using (var s = FileSystem.OpenWithExts(t.Value.Image, Extensions))
t.Value.Data = new Terrain(s, TileSize);
}
@@ -122,33 +124,39 @@ namespace OpenRA.FileFormats
foreach (var field in fields)
{
FieldInfo f = this.GetType().GetField(field);
if (f.GetValue(this) == null) continue;
gen.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) );
if (f.GetValue(this) == null)
continue;
gen.Add(new MiniYamlNode(field, FieldSaver.FormatValue(this, f)));
}
root.Add( new MiniYamlNode( "General", null, gen ) );
root.Add(new MiniYamlNode("General", null, gen));
root.Add( new MiniYamlNode( "Terrain", null,
Terrain.Select( t => new MiniYamlNode(
"TerrainType@{0}".F( t.Value.Type ),
t.Value.Save() ) ).ToList() ) );
root.Add(new MiniYamlNode( "Terrain", null,
Terrain.Select(t => new MiniYamlNode(
"TerrainType@{0}".F(t.Value.Type),
t.Value.Save())).ToList()));
root.Add( new MiniYamlNode( "Templates", null,
Templates.Select( t => new MiniYamlNode(
"Template@{0}".F( t.Value.Id ),
t.Value.Save() ) ).ToList() ) );
root.Add(new MiniYamlNode("Templates", null,
Templates.Select(t => new MiniYamlNode(
"Template@{0}".F(t.Value.Id),
t.Value.Save())).ToList()));
root.WriteToFile(filepath);
}
public byte[] GetBytes(TileReference<ushort,byte> r)
{
TileTemplate tile;
if( Templates.TryGetValue( r.type, out tile ) )
return tile.Data.TileBitmapBytes[ r.index ];
if (Templates.TryGetValue(r.type, out tile))
{
var data = tile.Data.TileBitmapBytes[r.index];
if (data != null)
return data;
}
byte[] missingTile = new byte[ TileSize * TileSize ];
for( int i = 0 ; i < missingTile.Length ; i++ )
missingTile[ i ] = 0x36;
byte[] missingTile = new byte[TileSize*TileSize];
for (var i = 0; i < missingTile.Length; i++)
missingTile[i] = 0x00;
return missingTile;
}
@@ -159,6 +167,7 @@ namespace OpenRA.FileFormats
string ret;
if (!tt.TryGetValue(r.index, out ret))
return "Clear"; // Default walkable
return ret;
}

View File

@@ -84,6 +84,7 @@
<Compile Include="FileFormats\Blast.cs" />
<Compile Include="FileFormats\Blowfish.cs" />
<Compile Include="FileFormats\BlowfishKeyProvider.cs" />
<Compile Include="FileFormats\CRC32.cs" />
<Compile Include="FileFormats\Format2.cs" />
<Compile Include="FileFormats\Format40.cs" />
<Compile Include="FileFormats\Format80.cs" />
@@ -137,6 +138,14 @@
<Compile Include="WRot.cs" />
<Compile Include="WRange.cs" />
<Compile Include="HSLColor.cs" />
<Compile Include="Graphics\ShpTSReader.cs" />
<Compile Include="FileFormats\XccLocalDatabase.cs" />
<Compile Include="FileFormats\XccGlobalDatabase.cs" />
<Compile Include="Graphics\VxlReader.cs" />
<Compile Include="Graphics\HvaReader.cs" />
<Compile Include="StreamExts.cs" />
<Compile Include="FileFormats\WavLoader.cs" />
<Compile Include="Filesystem\D2kSoundResources.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2013 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,
@@ -8,19 +8,21 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace OpenRA.FileFormats
{
public enum PackageHashType { Classic, CRC32 }
public class PackageEntry
{
public readonly uint Hash;
public readonly uint Offset;
public readonly uint Length;
public PackageEntry(uint hash, uint offset, uint length)
{
Hash = hash;
@@ -28,11 +30,11 @@ namespace OpenRA.FileFormats
Length = length;
}
public PackageEntry(BinaryReader r)
public PackageEntry(Stream s)
{
Hash = r.ReadUInt32();
Offset = r.ReadUInt32();
Length = r.ReadUInt32();
Hash = s.ReadUInt32();
Offset = s.ReadUInt32();
Length = s.ReadUInt32();
}
public void Write(BinaryWriter w)
@@ -51,33 +53,55 @@ namespace OpenRA.FileFormats
return "0x{0:x8} - offset 0x{1:x8} - length 0x{2:x8}".F(Hash, Offset, Length);
}
public static uint HashFilename(string name)
public static uint HashFilename(string name, PackageHashType type)
{
if (name.Length > 12)
name = name.Substring(0, 12);
switch(type)
{
case PackageHashType.Classic:
{
name = name.ToUpperInvariant();
if (name.Length % 4 != 0)
name = name.PadRight(name.Length + (4 - name.Length % 4), '\0');
name = name.ToUpperInvariant();
if (name.Length % 4 != 0)
name = name.PadRight(name.Length + (4 - name.Length % 4), '\0');
MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(name));
BinaryReader reader = new BinaryReader(ms);
MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(name));
BinaryReader reader = new BinaryReader(ms);
int len = name.Length >> 2;
uint result = 0;
int len = name.Length >> 2;
uint result = 0;
while (len-- != 0)
result = ((result << 1) | (result >> 31)) + reader.ReadUInt32();
while (len-- != 0)
result = ((result << 1) | (result >> 31)) + reader.ReadUInt32();
return result;
}
return result;
case PackageHashType.CRC32:
{
name = name.ToUpperInvariant();
var l = name.Length;
int a = l >> 2;
if ((l & 3) != 0)
{
name += (char)(l - (a << 2));
int i = 3 - (l & 3);
while (i-- != 0)
name += name[a << 2];
}
return CRC32.Calculate(Encoding.ASCII.GetBytes(name));
}
default: throw new NotImplementedException("Unknown hash type `{0}`".F(type));
}
}
static Dictionary<uint, string> Names = new Dictionary<uint,string>();
public static void AddStandardName(string s)
{
uint hash = HashFilename(s);
uint hash = HashFilename(s, PackageHashType.Classic); // RA1 and TD
Names.Add(hash, s);
uint crcHash = HashFilename(s, PackageHashType.CRC32); // TS
Names.Add(crcHash, s);
}
public const int Size = 12;

138
OpenRA.FileFormats/StreamExts.cs Executable file
View File

@@ -0,0 +1,138 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace OpenRA
{
public static class StreamExts
{
public static byte[] ReadBytes(this Stream s, int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException("count", "Non-negative number required.");
var buf = new byte[count];
if (s.Read(buf, 0, count) < count)
throw new EndOfStreamException();
return buf;
}
public static int Peek(this Stream s)
{
var buf = new byte[1];
if (s.Read(buf, 0, 1) == 0)
return -1;
s.Seek(s.Position - 1, SeekOrigin.Begin);
return buf[0];
}
public static byte ReadUInt8(this Stream s)
{
return s.ReadBytes(1)[0];
}
public static ushort ReadUInt16(this Stream s)
{
return BitConverter.ToUInt16(s.ReadBytes(2), 0);
}
public static short ReadInt16(this Stream s)
{
return BitConverter.ToInt16(s.ReadBytes(2), 0);
}
public static uint ReadUInt32(this Stream s)
{
return BitConverter.ToUInt32(s.ReadBytes(4), 0);
}
public static int ReadInt32(this Stream s)
{
return BitConverter.ToInt32(s.ReadBytes(4), 0);
}
public static float ReadFloat(this Stream s)
{
return BitConverter.ToSingle(s.ReadBytes(4), 0);
}
public static double ReadDouble(this Stream s)
{
return BitConverter.ToDouble(s.ReadBytes(8), 0);
}
public static string ReadASCII(this Stream s, int length)
{
return new string(Encoding.ASCII.GetChars(s.ReadBytes(length)));
}
public static string ReadASCIIZ(this Stream s)
{
var bytes = new List<byte>();
var buf = new byte[1];
for (;;)
{
if (s.Read(buf, 0, 1) < 1)
throw new EndOfStreamException();
if (buf[0] == 0)
break;
bytes.Add(buf[0]);
}
return new string(Encoding.ASCII.GetChars(bytes.ToArray()));
}
public static string ReadAllText(this Stream s)
{
using (s)
using (var sr = new StreamReader(s))
return sr.ReadToEnd();
}
public static byte[] ReadAllBytes(this Stream s)
{
using (s)
{
var data = new byte[s.Length - s.Position];
s.Read(data, 0, data.Length);
return data;
}
}
public static void Write(this Stream s, byte[] data)
{
s.Write(data, 0, data.Length);
}
public static IEnumerable<string> ReadAllLines(this Stream s)
{
using (var sr = new StreamReader(s))
{
for (;;)
{
var line = sr.ReadLine();
if (line == null)
yield break;
else
yield return line;
}
}
}
}
}

View File

@@ -66,12 +66,11 @@ namespace OpenRA
}
}
public Shroud.ActorVisibility Sight;
[Sync] public Player Owner;
Activity currentActivity;
public Group Group;
public int Generation;
internal Actor(World world, string name, TypeDictionary initDict )
{
@@ -106,15 +105,6 @@ namespace OpenRA
return TraitsImplementing<IAutoSelectionSize>().Select(x => x.SelectionSize(this)).FirstOrDefault();
});
if (this.HasTrait<RevealsShroud>())
{
Sight = new Shroud.ActorVisibility
{
range = this.Trait<RevealsShroud>().RevealRange,
vis = Shroud.GetVisOrigins(this).ToArray()
};
}
ApplyIRender = (x, wr) => x.Render(this, wr);
ApplyRenderModifier = (m, p, wr) => p.ModifyRender(this, wr, m);
@@ -127,12 +117,7 @@ namespace OpenRA
Bounds.Invalidate();
ExtendedBounds.Invalidate();
currentActivity = Traits.Util.RunActivity( this, currentActivity );
}
public void UpdateSight()
{
Sight.vis = Shroud.GetVisOrigins(this).ToArray();
currentActivity = Traits.Util.RunActivity(this, currentActivity);
}
public bool IsIdle
@@ -143,9 +128,9 @@ namespace OpenRA
OpenRA.FileFormats.Lazy<int2> Size;
// note: these delegates are cached to avoid massive allocation.
Func<IRender, WorldRenderer, IEnumerable<Renderable>> ApplyIRender;
Func<IEnumerable<Renderable>, IRenderModifier, WorldRenderer, IEnumerable<Renderable>> ApplyRenderModifier;
public IEnumerable<Renderable> Render(WorldRenderer wr)
Func<IRender, WorldRenderer, IEnumerable<IRenderable>> ApplyIRender;
Func<IEnumerable<IRenderable>, IRenderModifier, WorldRenderer, IEnumerable<IRenderable>> ApplyRenderModifier;
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
var mods = TraitsImplementing<IRenderModifier>();
var sprites = TraitsImplementing<IRender>().SelectMany(x => ApplyIRender(x, wr));
@@ -272,6 +257,7 @@ namespace OpenRA
// momentarily remove from world so the ownership queries don't get confused
w.Remove(this);
Owner = newOwner;
Generation++;
w.Add(this);
foreach (var t in this.TraitsImplementing<INotifyOwnerChanged>())

View File

@@ -43,6 +43,8 @@ namespace OpenRA
public int2 ToInt2() { return new int2(X, Y); }
public PPos ToPPos() { return new PPos(Game.CellSize * X, Game.CellSize * Y); }
public WPos CenterPosition { get { return new WPos(1024*X + 512, 1024*Y + 512, 0); } }
public CPos Clamp(Rectangle r)
{
return new CPos(Math.Min(r.Right, Math.Max(X, r.Left)),

View File

@@ -51,6 +51,7 @@ namespace OpenRA
public float2 ToFloat2() { return new float2(X, Y); }
public int2 ToInt2() { return new int2(X, Y); }
public WVec ToWVec() { return new WVec(X*1024, Y*1024, 0); }
public CVec Clamp(Rectangle r)
{

View File

@@ -32,6 +32,6 @@ namespace OpenRA.Effects
world.AddFrameEndTask(w => { w.Remove(this); a(); });
}
public IEnumerable<Renderable> Render(WorldRenderer wr) { yield break; }
public IEnumerable<IRenderable> Render(WorldRenderer wr) { yield break; }
}
}

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Effects
world.AddFrameEndTask(w => w.Remove(this));
}
public IEnumerable<Renderable> Render(WorldRenderer wr)
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (!target.IsInWorld)
yield break;

View File

@@ -17,6 +17,6 @@ namespace OpenRA.Effects
public interface IEffect
{
void Tick(World world);
IEnumerable<Renderable> Render(WorldRenderer r);
IEnumerable<IRenderable> Render(WorldRenderer r);
}
}

View File

@@ -122,17 +122,17 @@ namespace OpenRA
public static void RunAfterTick(Action a) { delayedActions.Add(a); }
public static void RunAfterDelay(int delay, Action a) { delayedActions.Add(a, delay); }
static void Tick( OrderManager orderManager, Viewport viewPort )
static void Tick(OrderManager orderManager, Viewport viewPort)
{
if (orderManager.Connection.ConnectionState != lastConnectionState)
{
lastConnectionState = orderManager.Connection.ConnectionState;
ConnectionStateChanged( orderManager );
ConnectionStateChanged(orderManager);
}
Tick( orderManager );
if( worldRenderer != null && orderManager.world != worldRenderer.world )
Tick( worldRenderer.world.orderManager );
Tick(orderManager);
if (worldRenderer != null && orderManager.world != worldRenderer.world)
Tick(worldRenderer.world.orderManager);
using (new PerfSample("render"))
{
@@ -149,12 +149,12 @@ namespace OpenRA
delayedActions.PerformActions();
}
static void Tick( OrderManager orderManager )
static void Tick(OrderManager orderManager)
{
int t = Environment.TickCount;
int dt = t - orderManager.LastTickTime;
if (dt >= Settings.Game.Timestep)
using( new PerfSample( "tick_time" ) )
using (new PerfSample("tick_time"))
{
orderManager.LastTickTime += Settings.Game.Timestep;
Ui.Tick();
@@ -162,7 +162,7 @@ namespace OpenRA
if (orderManager.GameStarted)
++Viewport.TicksSinceLastMove;
Sound.Tick();
Sync.CheckSyncUnchanged( world, () => { orderManager.TickImmediate(); } );
Sync.CheckSyncUnchanged(world, orderManager.TickImmediate);
if (world != null)
{
@@ -230,7 +230,7 @@ namespace OpenRA
{
get
{
var client= orderManager.LobbyInfo.ClientWithIndex (
var client= orderManager.LobbyInfo.ClientWithIndex(
orderManager.Connection.LocalClientId);
return ((client!=null) && client.IsAdmin);
}
@@ -238,7 +238,7 @@ namespace OpenRA
public static Dictionary<String, Mod> CurrentMods
{
get { return Mod.AllMods.Where( k => modData.Manifest.Mods.Contains( k.Key )).ToDictionary( k => k.Key, k => k.Value ); }
get { return Mod.AllMods.Where(k => modData.Manifest.Mods.Contains(k.Key)).ToDictionary(k => k.Key, k => k.Value); }
}
static Modifiers modifiers;
@@ -311,10 +311,9 @@ namespace OpenRA
Sound.StopVideo();
Sound.Initialize();
modData = new ModData( mm );
modData = new ModData(mm);
Renderer.InitializeFonts(modData.Manifest);
modData.LoadInitialAssets(true);
modData.InitializeLoaders();
PerfHistory.items["render"].hasNormalTick = false;
PerfHistory.items["batches"].hasNormalTick = false;
@@ -331,11 +330,11 @@ namespace OpenRA
Game.Settings.Server.Map = WidgetUtils.ChooseInitialMap(Game.Settings.Server.Map);
Game.Settings.Save();
Game.CreateServer(new ServerSettings(Game.Settings.Server));
while(true)
while (true)
{
System.Threading.Thread.Sleep(100);
if((server.State == Server.ServerState.GameStarted)
if ((server.State == Server.ServerState.GameStarted)
&& (server.conns.Count<=1))
{
Console.WriteLine("No one is playing, shutting down...");
@@ -426,7 +425,7 @@ namespace OpenRA
public static T CreateObject<T>( string name )
{
return modData.ObjectCreator.CreateObject<T>( name );
return modData.ObjectCreator.CreateObject<T>(name);
}
public static void CreateServer(ServerSettings settings)
@@ -459,23 +458,6 @@ namespace OpenRA
return orderManager != null && orderManager.world == world;
}
public static void JoinExternalGame()
{
var addressParts = Game.Settings.Game.ConnectTo.Split(
new [] { ':' }, StringSplitOptions.RemoveEmptyEntries);
if (addressParts.Length < 1 || addressParts.Length > 2)
return;
var host = addressParts[0];
var port = Exts.WithDefault(1234, () => int.Parse(addressParts[1]));
Game.Settings.Game.ConnectTo = "";
Game.Settings.Save();
Game.JoinServer(host, port);
}
public static bool DownloadMap(string mapHash)
{
try

View File

@@ -46,9 +46,9 @@ namespace OpenRA
static Dictionary<string, T> LoadYamlRules<T>(string[] files, List<MiniYamlNode> dict, Func<MiniYamlNode, Dictionary<string, MiniYaml>, T> f)
{
var y = files.Select(a => MiniYaml.FromFile(a)).Aggregate(dict,MiniYaml.MergeLiberal);
var yy = y.ToDictionary( x => x.Key, x => x.Value );
return y.ToDictionary(kv => kv.Key.ToLowerInvariant(), kv => f(kv, yy));
var y = files.Select(MiniYaml.FromFile).Aggregate(dict, MiniYaml.MergeLiberal);
var yy = y.ToDictionary(x => x.Key, x => x.Value);
return y.ToDictionaryWithConflictLog(kv => kv.Key.ToLowerInvariant(), kv => f(kv, yy), "LoadYamlRules", null, null);
}
public static IEnumerable<KeyValuePair<string,MusicInfo>> InstalledMusic { get { return Music.Where( m => m.Value.Exists ); } }

View File

@@ -36,7 +36,7 @@ namespace OpenRA.GameRules
public bool VerboseNatDiscovery = false; // print very detailed logs for debugging
public bool AllowCheats = false;
public string Map = null;
public string[] Ban = null;
public string[] Ban = { };
public int TimeOut = 0;
public bool Dedicated = false;
public bool DedicatedLoop = true;
@@ -77,6 +77,7 @@ namespace OpenRA.GameRules
public bool SanityCheckUnsyncedCode = false;
public int Samples = 25;
public bool IgnoreVersionMismatch = false;
public bool DeveloperMenu = false;
}
public class GraphicSettings
@@ -130,7 +131,6 @@ namespace OpenRA.GameRules
// Internal game settings
public int Timestep = 40;
public string ConnectTo = "";
public bool AllowDownloading = true;
public string MapRepository = "http://content.open-ra.org/map/";
}
@@ -151,6 +151,7 @@ namespace OpenRA.GameRules
public string ScatterKey = "x";
public string DeployKey = "f";
public string StanceCycleKey = "z";
public string GuardKey = "d";
public string CycleTabsKey = "tab";
}

View File

@@ -24,15 +24,13 @@ namespace OpenRA.Graphics
public string Name { get { return name; } }
public Animation( string name )
: this( name, () => 0 )
{
}
public Animation(string name)
: this(name, () => 0) {}
public Animation( string name, Func<int> facingFunc )
public Animation(string name, Func<int> facingFunc)
{
this.name = name.ToLowerInvariant();
this.tickFunc = () => { };
this.tickFunc = () => {};
this.facingFunc = facingFunc;
}
@@ -46,12 +44,12 @@ namespace OpenRA.Graphics
}
}
public void Play( string sequenceName )
public void Play(string sequenceName)
{
PlayThen(sequenceName, null);
}
public void PlayRepeating( string sequenceName )
public void PlayRepeating(string sequenceName)
{
backwards = false;
tickAlways = false;
@@ -75,16 +73,16 @@ namespace OpenRA.Graphics
return true;
}
public void PlayThen( string sequenceName, Action after )
public void PlayThen(string sequenceName, Action after)
{
backwards = false;
tickAlways = false;
CurrentSequence = SequenceProvider.GetSequence( name, sequenceName );
CurrentSequence = SequenceProvider.GetSequence(name, sequenceName);
frame = 0;
tickFunc = () =>
{
++frame;
if( frame >= CurrentSequence.Length )
if (frame >= CurrentSequence.Length)
{
frame = CurrentSequence.Length - 1;
tickFunc = () => { };
@@ -99,11 +97,11 @@ namespace OpenRA.Graphics
backwards = true;
}
public void PlayFetchIndex( string sequenceName, Func<int> func )
public void PlayFetchIndex(string sequenceName, Func<int> func)
{
backwards = false;
tickAlways = true;
CurrentSequence = SequenceProvider.GetSequence( name, sequenceName );
CurrentSequence = SequenceProvider.GetSequence(name, sequenceName);
frame = func();
tickFunc = () => frame = func();
}
@@ -113,19 +111,19 @@ namespace OpenRA.Graphics
public void Tick()
{
Tick( 40 ); // tick one frame
Tick(40); // tick one frame
}
public bool HasSequence(string seq) { return SequenceProvider.HasSequence( name, seq ); }
public bool HasSequence(string seq) { return SequenceProvider.HasSequence(name, seq); }
public void Tick( int t )
public void Tick(int t)
{
if( tickAlways )
if (tickAlways)
tickFunc();
else
{
timeUntilNextFrame -= t;
while( timeUntilNextFrame <= 0 )
while (timeUntilNextFrame <= 0)
{
tickFunc();
timeUntilNextFrame += CurrentSequence != null ? CurrentSequence.Tick : 40; // 25 fps == 40 ms
@@ -145,9 +143,9 @@ namespace OpenRA.Graphics
}
}
public Sequence GetSequence( string sequenceName )
public Sequence GetSequence(string sequenceName)
{
return SequenceProvider.GetSequence( name, sequenceName );
return SequenceProvider.GetSequence(name, sequenceName);
}
}
}

View File

@@ -15,36 +15,43 @@ namespace OpenRA.Graphics
{
public class AnimationWithOffset
{
public Animation Animation;
public Func<WorldRenderer, float2> OffsetFunc;
public Func<bool> DisableFunc;
public int ZOffset;
public readonly Animation Animation;
public readonly Func<WVec> OffsetFunc;
public readonly Func<bool> DisableFunc;
public readonly Func<WPos, int> ZOffset;
public AnimationWithOffset(Animation a)
: this(a, null, null)
{
}
public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable)
: this(a, offset, disable, null) { }
public AnimationWithOffset(Animation a, Func<WorldRenderer, float2> o, Func<bool> d)
public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable, int zOffset)
: this(a, offset, disable, _ => zOffset) { }
public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable, Func<WPos, int> zOffset)
{
this.Animation = a;
this.OffsetFunc = o;
this.DisableFunc = d;
this.OffsetFunc = offset;
this.DisableFunc = disable;
this.ZOffset = zOffset;
}
public Renderable Image(Actor self, WorldRenderer wr, PaletteReference pal)
public IRenderable Image(Actor self, WorldRenderer wr, PaletteReference pal)
{
var p = self.CenterLocation;
var loc = p.ToFloat2() - 0.5f * Animation.Image.size
+ (OffsetFunc != null ? OffsetFunc(wr) : float2.Zero);
var r = new Renderable(Animation.Image, loc, pal, p.Y);
return Image(self, wr, pal, 1f);
}
return ZOffset != 0 ? r.WithZOffset(ZOffset) : r;
public IRenderable Image(Actor self, WorldRenderer wr, PaletteReference pal, float scale)
{
var p = self.CenterPosition;
if (OffsetFunc != null)
p += OffsetFunc();
var z = (ZOffset != null) ? ZOffset(p) : 0;
return new SpriteRenderable(Animation.Image, p, z, pal, scale);
}
public static implicit operator AnimationWithOffset(Animation a)
{
return new AnimationWithOffset(a);
return new AnimationWithOffset(a, null, null, null);
}
}
}

View File

@@ -0,0 +1,68 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
namespace OpenRA.Graphics
{
public struct BeamRenderable : IRenderable
{
readonly WPos pos;
readonly int zOffset;
readonly WVec length;
readonly Color color;
readonly float width;
public BeamRenderable(WPos pos, int zOffset, WVec length, float width, Color color)
{
this.pos = pos;
this.zOffset = zOffset;
this.length = length;
this.color = color;
this.width = width;
}
public WPos Pos { get { return pos; } }
public float Scale { get { return 1f; } }
public PaletteReference Palette { get { return null; } }
public int ZOffset { get { return zOffset; } }
public IRenderable WithScale(float newScale) { return new BeamRenderable(pos, zOffset, length, width, color); }
public IRenderable WithPalette(PaletteReference newPalette) { return new BeamRenderable(pos, zOffset, length, width, color); }
public IRenderable WithZOffset(int newOffset) { return new BeamRenderable(pos, zOffset, length, width, color); }
public IRenderable WithPos(WPos pos) { return new BeamRenderable(pos, zOffset, length, width, color); }
public void BeforeRender(WorldRenderer wr) {}
public void Render(WorldRenderer wr)
{
var wlr = Game.Renderer.WorldLineRenderer;
var src = wr.ScreenPosition(pos);
var dest = wr.ScreenPosition(pos + length);
var lineWidth = wlr.LineWidth;
if (lineWidth != width)
{
wlr.Flush();
wlr.LineWidth = width;
}
wlr.DrawLine(src, dest, color, color);
if (lineWidth != width)
{
wlr.Flush();
wlr.LineWidth = lineWidth;
}
}
public void RenderDebugGeometry(WorldRenderer wr) {}
}
}

View File

@@ -0,0 +1,105 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
namespace OpenRA.Graphics
{
public struct ContrailRenderable : IRenderable
{
readonly World world;
// Store trail positions in a circular buffer
readonly WPos[] trail;
int next;
int length;
int skip;
readonly Color color;
readonly int zOffset;
public ContrailRenderable(World world, Color color, int length, int skip, int zOffset)
: this(world, new WPos[length], 0, 0, skip, color, zOffset) {}
ContrailRenderable(World world, WPos[] trail, int next, int length, int skip, Color color, int zOffset)
{
this.world = world;
this.trail = trail;
this.next = next;
this.length = length;
this.skip = skip;
this.color = color;
this.zOffset = zOffset;
}
public WPos Pos { get { return trail[idx(next-1)]; } }
public float Scale { get { return 1f; } }
public PaletteReference Palette { get { return null; } }
public int ZOffset { get { return zOffset; } }
public IRenderable WithScale(float newScale) { return new ContrailRenderable(world, (WPos[])trail.Clone(), next, length, skip, color, zOffset); }
public IRenderable WithPalette(PaletteReference newPalette) { return new ContrailRenderable(world, (WPos[])trail.Clone(), next, length, skip, color, zOffset); }
public IRenderable WithZOffset(int newOffset) { return new ContrailRenderable(world, (WPos[])trail.Clone(), next, length, skip, color, newOffset); }
public IRenderable WithPos(WPos pos) { return new ContrailRenderable(world, (WPos[])trail.Clone(), next, length, skip, color, zOffset); }
public void BeforeRender(WorldRenderer wr) {}
public void Render(WorldRenderer wr)
{
// Need at least 4 points to smooth the contrail over
if (length - skip < 4 )
return;
// Start of the first line segment is the tail of the list - don't smooth it.
var curPos = trail[idx(next - skip - 1)];
var curCell = new CPos(curPos);
var curColor = color;
for (var i = 0; i < length - skip - 4; i++)
{
var j = next - skip - i - 2;
var nextPos = WPos.Average(trail[idx(j)], trail[idx(j-1)], trail[idx(j-2)], trail[idx(j-3)]);
var nextCell = new CPos(nextPos);
var nextColor = Exts.ColorLerp(i * 1f / (length - 4), color, Color.Transparent);
if (!world.FogObscures(curCell) && !world.FogObscures(nextCell))
Game.Renderer.WorldLineRenderer.DrawLine(wr.ScreenPosition(curPos), wr.ScreenPosition(nextPos), curColor, nextColor);
curPos = nextPos;
curCell = nextCell;
curColor = nextColor;
}
}
public void RenderDebugGeometry(WorldRenderer wr) {}
// Array index modulo length
int idx(int i)
{
var j = i % trail.Length;
return j < 0 ? j + trail.Length : j;
}
public void Update(WPos pos)
{
trail[next] = pos;
next = idx(next+1);
if (length < trail.Length)
length++;
}
public static Color ChooseColor(Actor self)
{
var ownerColor = Color.FromArgb(255, self.Owner.Color.RGB);
return Exts.ColorLerp(0.5f, ownerColor, Color.White);
}
}
}

View File

@@ -20,12 +20,23 @@ namespace OpenRA.Graphics
{
public static class CursorProvider
{
static HardwarePalette Palette;
static HardwarePalette palette;
static Dictionary<string, CursorSequence> cursors;
static Cache<string, PaletteReference> palettes;
static PaletteReference CreatePaletteReference(string name)
{
var pal = palette.GetPalette(name);
if (pal == null)
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
return new PaletteReference(name, palette.GetPaletteIndex(name), pal);
}
public static void Initialize(string[] sequenceFiles)
{
cursors = new Dictionary<string, CursorSequence>();
palettes = new Cache<string, PaletteReference>(CreatePaletteReference);
var sequences = new MiniYaml(null, sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal));
int[] ShadowIndex = { };
@@ -35,14 +46,14 @@ namespace OpenRA.Graphics
int.TryParse(sequences.NodesDict["ShadowIndex"].Value, out ShadowIndex[ShadowIndex.Length - 1]);
}
Palette = new HardwarePalette();
palette = new HardwarePalette();
foreach (var p in sequences.NodesDict["Palettes"].Nodes)
Palette.AddPalette(p.Key, new Palette(FileSystem.Open(p.Value.Value), ShadowIndex), false);
palette.AddPalette(p.Key, new Palette(FileSystem.Open(p.Value.Value), ShadowIndex), false);
foreach (var s in sequences.NodesDict["Cursors"].Nodes)
LoadSequencesForCursor(s.Key, s.Value);
Palette.Initialize();
palette.Initialize();
}
static void LoadSequencesForCursor(string cursorSrc, MiniYaml cursor)
@@ -63,10 +74,10 @@ namespace OpenRA.Graphics
var cursorSequence = GetCursorSequence(cursorName);
var cursorSprite = cursorSequence.GetSprite(cursorFrame);
renderer.SetPalette(Palette);
renderer.SetPalette(palette);
renderer.SpriteRenderer.DrawSprite(cursorSprite,
lastMousePos - cursorSequence.Hotspot,
Palette.GetPaletteIndex(cursorSequence.Palette),
palettes[cursorSequence.Palette],
cursorSprite.size);
}

View File

@@ -21,10 +21,10 @@ namespace OpenRA.Graphics
Renderer renderer;
IShader shader;
Vertex[] vertices = new Vertex[ Renderer.TempBufferSize ];
Vertex[] vertices = new Vertex[Renderer.TempBufferSize];
int nv = 0;
public LineRenderer( Renderer renderer, IShader shader )
public LineRenderer(Renderer renderer, IShader shader)
{
this.renderer = renderer;
this.shader = shader;
@@ -32,49 +32,50 @@ namespace OpenRA.Graphics
public void Flush()
{
if( nv > 0 )
if (nv > 0)
{
shader.Render( () =>
renderer.Device.EnableAlphaBlending();
shader.Render(() =>
{
var vb = renderer.GetTempVertexBuffer();
vb.SetData( vertices, nv );
vb.SetData(vertices, nv);
renderer.SetLineWidth(LineWidth * Game.viewport.Zoom);
renderer.DrawBatch( vb, 0, nv, PrimitiveType.LineList );
} );
renderer.DrawBatch(vb, 0, nv, PrimitiveType.LineList);
});
renderer.Device.DisableAlphaBlending();
nv = 0;
}
}
public void DrawRect( float2 tl, float2 br, Color c )
public void DrawRect(float2 tl, float2 br, Color c)
{
var tr = new float2( br.X, tl.Y );
var bl = new float2( tl.X, br.Y );
DrawLine( tl, tr, c, c );
DrawLine( tl, bl, c, c );
DrawLine( tr, br, c, c );
DrawLine( bl, br, c, c );
var tr = new float2(br.X, tl.Y);
var bl = new float2(tl.X, br.Y);
DrawLine(tl, tr, c, c);
DrawLine(tl, bl, c, c);
DrawLine(tr, br, c, c);
DrawLine(bl, br, c, c);
}
public void DrawLine( float2 start, float2 end, Color startColor, Color endColor )
public void DrawLine(float2 start, float2 end, Color startColor, Color endColor)
{
Renderer.CurrentBatchRenderer = this;
if( nv + 2 > Renderer.TempBufferSize )
if (nv + 2 > Renderer.TempBufferSize)
Flush();
vertices[ nv++ ] = new Vertex( start + offset,
new float2( startColor.R / 255.0f, startColor.G / 255.0f ),
new float2( startColor.B / 255.0f, startColor.A / 255.0f ) );
vertices[nv++] = new Vertex(start + offset,
new float2(startColor.R / 255.0f, startColor.G / 255.0f),
new float2(startColor.B / 255.0f, startColor.A / 255.0f));
vertices[ nv++ ] = new Vertex( end + offset,
new float2( endColor.R / 255.0f, endColor.G / 255.0f ),
new float2( endColor.B / 255.0f, endColor.A / 255.0f ) );
vertices[nv++] = new Vertex(end + offset,
new float2(endColor.R / 255.0f, endColor.G / 255.0f),
new float2(endColor.B / 255.0f, endColor.A / 255.0f));
}
public void FillRect( RectangleF r, Color color )
public void FillRect(RectangleF r, Color color)
{
for (float y = r.Top; y < r.Bottom; y++)
for (var y = r.Top; y < r.Bottom; y++)
DrawLine(new float2(r.Left, y), new float2(r.Right, y), color, color);
}

View File

@@ -31,12 +31,14 @@ namespace OpenRA.Graphics
{
if (nv > 0)
{
renderer.Device.EnableAlphaBlending();
shader.Render(() =>
{
var vb = renderer.GetTempVertexBuffer();
vb.SetData(vertices, nv);
renderer.DrawBatch(vb, 0, nv, PrimitiveType.QuadList);
});
renderer.Device.DisableAlphaBlending();
nv = 0;
}

View File

@@ -0,0 +1,91 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
namespace OpenRA.Graphics
{
public class RenderableComparer : IComparer<IRenderable>
{
WorldRenderer wr;
public RenderableComparer(WorldRenderer wr)
{
this.wr = wr;
}
public int Compare(IRenderable x, IRenderable y)
{
var xOrder = wr.ScreenZPosition(x.Pos, x.ZOffset);
var yOrder = wr.ScreenZPosition(y.Pos, y.ZOffset);
return xOrder.CompareTo(yOrder);
}
}
public interface IRenderable
{
WPos Pos { get; }
float Scale { get; }
PaletteReference Palette { get; }
int ZOffset { get; }
IRenderable WithScale(float newScale);
IRenderable WithPalette(PaletteReference newPalette);
IRenderable WithZOffset(int newOffset);
IRenderable WithPos(WPos pos);
void BeforeRender(WorldRenderer wr);
void Render(WorldRenderer wr);
void RenderDebugGeometry(WorldRenderer wr);
}
public struct SpriteRenderable : IRenderable
{
readonly Sprite sprite;
readonly WPos pos;
readonly int zOffset;
readonly PaletteReference palette;
readonly float scale;
public SpriteRenderable(Sprite sprite, WPos pos, int zOffset, PaletteReference palette, float scale)
{
this.sprite = sprite;
this.pos = pos;
this.zOffset = zOffset;
this.palette = palette;
this.scale = scale;
}
// Provided for legacy support only - Don't use for new things!
public SpriteRenderable(Sprite sprite, float2 pos, PaletteReference palette, int z)
: this(sprite, new PPos((int)pos.X, (int)pos.Y).ToWPos(0), z, palette, 1f) { }
public WPos Pos { get { return pos; } }
public float Scale { get { return scale; } }
public PaletteReference Palette { get { return palette; } }
public int ZOffset { get { return zOffset; } }
public IRenderable WithScale(float newScale) { return new SpriteRenderable(sprite, pos, zOffset, palette, newScale); }
public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, zOffset, newPalette, scale); }
public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, newOffset, palette, scale); }
public IRenderable WithPos(WPos pos) { return new SpriteRenderable(sprite, pos, zOffset, palette, scale); }
public void BeforeRender(WorldRenderer wr) {}
public void Render(WorldRenderer wr)
{
sprite.DrawAt(wr.ScreenPxPosition(pos) - (0.5f*scale*sprite.size).ToInt2(), palette, scale);
}
public void RenderDebugGeometry(WorldRenderer wr)
{
var offset = wr.ScreenPxPosition(pos) - 0.5f*scale*sprite.size + sprite.offset;
Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + sprite.size, Color.Red);
}
}
}

View File

@@ -28,8 +28,10 @@ namespace OpenRA.Graphics
internal static int TempBufferCount;
public SpriteRenderer WorldSpriteRenderer { get; private set; }
public SpriteRenderer WorldRgbaSpriteRenderer { get; private set; }
public QuadRenderer WorldQuadRenderer { get; private set; }
public LineRenderer WorldLineRenderer { get; private set; }
public VoxelRenderer WorldVoxelRenderer { get; private set; }
public LineRenderer LineRenderer { get; private set; }
public SpriteRenderer RgbaSpriteRenderer { get; private set; }
public SpriteRenderer SpriteRenderer { get; private set; }
@@ -45,7 +47,9 @@ namespace OpenRA.Graphics
SheetSize = Game.Settings.Graphics.SheetSize;
WorldSpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp"));
WorldRgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba"));
WorldLineRenderer = new LineRenderer(this, device.CreateShader("line"));
WorldVoxelRenderer = new VoxelRenderer(this, device.CreateShader("vxl"));
LineRenderer = new LineRenderer(this, device.CreateShader("line"));
WorldQuadRenderer = new QuadRenderer(this, device.CreateShader("line"));
RgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba"));
@@ -66,11 +70,13 @@ namespace OpenRA.Graphics
{
device.Clear();
WorldSpriteRenderer.SetViewportParams(Resolution, zoom, scroll);
WorldRgbaSpriteRenderer.SetViewportParams(Resolution, zoom, scroll);
SpriteRenderer.SetViewportParams(Resolution, 1f, float2.Zero);
RgbaSpriteRenderer.SetViewportParams(Resolution, 1f, float2.Zero);
WorldLineRenderer.SetViewportParams(Resolution, zoom, scroll);
WorldQuadRenderer.SetViewportParams(Resolution, zoom, scroll);
LineRenderer.SetViewportParams(Resolution, 1f, float2.Zero);
WorldVoxelRenderer.SetViewportParams(Resolution, zoom, scroll);
}
ITexture currentPaletteTexture;
@@ -85,6 +91,8 @@ namespace OpenRA.Graphics
RgbaSpriteRenderer.SetPalette(currentPaletteTexture);
SpriteRenderer.SetPalette(currentPaletteTexture);
WorldSpriteRenderer.SetPalette(currentPaletteTexture);
WorldRgbaSpriteRenderer.SetPalette(currentPaletteTexture);
WorldVoxelRenderer.SetPalette(currentPaletteTexture);
}
public void EndFrame(IInputHandler inputHandler)
@@ -186,5 +194,17 @@ namespace OpenRA.Graphics
Flush();
Device.DisableScissor();
}
public void EnableDepthBuffer()
{
Flush();
Device.EnableDepthBuffer();
}
public void DisableDepthBuffer()
{
Flush();
Device.DisableDepthBuffer();
}
}
}

View File

@@ -11,6 +11,7 @@
using System;
using System.Xml;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
namespace OpenRA.Graphics
@@ -19,6 +20,7 @@ namespace OpenRA.Graphics
{
readonly Sprite[] sprites;
readonly int start, length, stride, facings, tick;
readonly bool reverseFacings, transpose;
public readonly string Name;
public int Start { get { return start; } }
@@ -33,10 +35,18 @@ namespace OpenRA.Graphics
var srcOverride = info.Value;
Name = name;
var d = info.NodesDict;
var offset = float2.Zero;
sprites = Game.modData.SpriteLoader.LoadAllSprites(srcOverride ?? unit);
start = int.Parse(d["Start"].Value);
if (d.ContainsKey("Offset"))
offset = FieldLoader.GetValue<float2>("Offset", d["Offset"].Value);
// Apply offset to each sprite in the sequence
// Different sequences may apply different offsets to the same frame
sprites = Game.modData.SpriteLoader.LoadAllSprites(srcOverride ?? unit).Select(
s => new Sprite(s.sheet, s.bounds, s.offset + offset, s.channel)).ToArray();
if (!d.ContainsKey("Length"))
length = 1;
else if (d["Length"].Value == "*")
@@ -49,16 +59,23 @@ namespace OpenRA.Graphics
else
stride = length;
if(d.ContainsKey("Facings"))
facings = int.Parse(d["Facings"].Value);
if (d.ContainsKey("Facings"))
{
var f = int.Parse(d["Facings"].Value);
facings = Math.Abs(f);
reverseFacings = f < 0;
}
else
facings = 1;
if(d.ContainsKey("Tick"))
if (d.ContainsKey("Tick"))
tick = int.Parse(d["Tick"].Value);
else
tick = 40;
if (d.ContainsKey("Transpose"))
transpose = bool.Parse(d["Transpose"].Value);
if (length > stride)
throw new InvalidOperationException(
"{0}: Sequence {1}.{2}: Length must be <= stride"
@@ -71,15 +88,22 @@ namespace OpenRA.Graphics
info.Nodes[0].Location));
}
public Sprite GetSprite( int frame )
public Sprite GetSprite(int frame)
{
return GetSprite( frame, 0 );
return GetSprite(frame, 0);
}
public Sprite GetSprite(int frame, int facing)
{
var f = Traits.Util.QuantizeFacing( facing, facings );
return sprites[ (f * stride) + ( frame % length ) + start ];
var f = Traits.Util.QuantizeFacing(facing, facings);
if (reverseFacings)
f = (facings - f) % facings;
int i = transpose ? (frame % length) * facings + f :
(f * stride) + (frame % length);
return sprites[start + i];
}
}
}

View File

@@ -8,7 +8,9 @@
*/
#endregion
using System;
using System.Drawing;
using System.Drawing.Imaging;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
@@ -16,21 +18,52 @@ namespace OpenRA.Graphics
{
public class Sheet
{
Bitmap bitmap;
ITexture texture;
bool dirty;
byte[] data;
public readonly Size Size;
public byte[] Data { get { return data ?? texture.GetData(); } }
public Sheet(Size size)
{
Size = size;
data = new byte[4*Size.Width*Size.Height];
}
public Sheet(ITexture texture)
{
this.texture = texture;
Size = texture.Size;
}
public Sheet(string filename)
{
bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename));
var bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename));
Size = bitmap.Size;
data = new byte[4*Size.Width*Size.Height];
var b = bitmap.LockBits(bitmap.Bounds(),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
unsafe
{
int* c = (int*)b.Scan0;
for (var x = 0; x < Size.Width; x++)
for (var y = 0; y < Size.Height; y++)
{
var i = 4*Size.Width*y + 4*x;
// Convert argb to bgra
var argb = *(c + (y * b.Stride >> 2) + x);
data[i++] = (byte)(argb >> 0);
data[i++] = (byte)(argb >> 8);
data[i++] = (byte)(argb >> 16);
data[i++] = (byte)(argb >> 24);
}
}
bitmap.UnlockBits(b);
}
public ITexture Texture
@@ -45,23 +78,69 @@ namespace OpenRA.Graphics
if (dirty)
{
if (data != null)
{
texture.SetData(data, Size.Width, Size.Height);
dirty = false;
}
else if (bitmap != null)
{
texture.SetData(bitmap);
dirty = false;
}
texture.SetData(data, Size.Width, Size.Height);
dirty = false;
}
return texture;
}
}
public byte[] Data { get { if (data == null) data = new byte[4 * Size.Width * Size.Height]; return data; } }
public void MakeDirty() { dirty = true; }
public Bitmap AsBitmap()
{
var d = Data;
var b = new Bitmap(Size.Width, Size.Height);
var output = b.LockBits(new Rectangle(0, 0, Size.Width, Size.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
unsafe
{
int* c = (int*)output.Scan0;
for (var x = 0; x < Size.Width; x++)
for (var y = 0; y < Size.Height; y++)
{
var i = 4*Size.Width*y + 4*x;
// Convert bgra to argb
var argb = (d[i+3] << 24) | (d[i+2] << 16) | (d[i+1] << 8) | d[i];
*(c + (y * output.Stride >> 2) + x) = argb;
}
}
b.UnlockBits(output);
return b;
}
public Bitmap AsBitmap(TextureChannel channel, Palette pal)
{
var d = Data;
var b = new Bitmap(Size.Width, Size.Height);
var output = b.LockBits(new Rectangle(0, 0, Size.Width, Size.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
unsafe
{
int* c = (int*)output.Scan0;
for (var x = 0; x < Size.Width; x++)
for (var y = 0; y < Size.Height; y++)
{
var index = d[4*Size.Width*y + 4*x + (int)channel];
*(c + (y * output.Stride >> 2) + x) = pal.GetColor(index).ToArgb();
}
}
b.UnlockBits(output);
return b;
}
public void CommitData()
{
if (data == null)
throw new InvalidOperationException("Texture-wrappers are read-only");
dirty = true;
}
}
}

View File

@@ -8,68 +8,78 @@
*/
#endregion
using System;
using System.Drawing;
namespace OpenRA.Graphics
{
public class SheetOverflowException : Exception
{
public SheetOverflowException(string message)
: base(message) {}
}
public enum SheetType
{
Indexed = 1,
DualIndexed = 2,
BGRA = 4,
}
public class SheetBuilder
{
internal SheetBuilder(TextureChannel ch)
Sheet current;
TextureChannel channel;
SheetType type;
int rowHeight = 0;
Point p;
Func<Sheet> allocateSheet;
public static Sheet AllocateSheet()
{
current = null;
rowHeight = 0;
channel = null;
initialChannel = ch;
return new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize));;
}
internal SheetBuilder(SheetType t)
: this(t, AllocateSheet) {}
internal SheetBuilder(SheetType t, Func<Sheet> allocateSheet)
{
channel = TextureChannel.Red;
type = t;
current = allocateSheet();
this.allocateSheet = allocateSheet;
}
public Sprite Add(byte[] src, Size size)
{
Sprite rect = Allocate(size);
var rect = Allocate(size);
Util.FastCopyIntoChannel(rect, src);
current.CommitData();
return rect;
}
public Sprite Add(Size size, byte paletteIndex)
{
byte[] data = new byte[size.Width * size.Height];
for (int i = 0; i < data.Length; i++)
var data = new byte[size.Width * size.Height];
for (var i = 0; i < data.Length; i++)
data[i] = paletteIndex;
return Add(data, size);
}
Sheet NewSheet() { return new Sheet(new Size( Renderer.SheetSize, Renderer.SheetSize ) ); }
Sheet current = null;
int rowHeight = 0;
Point p;
TextureChannel? channel = null;
TextureChannel initialChannel;
TextureChannel? NextChannel(TextureChannel? t)
TextureChannel? NextChannel(TextureChannel t)
{
if (t == null)
return initialChannel;
var nextChannel = (int)t + (int)type;
if (nextChannel > (int)TextureChannel.Alpha)
return null;
switch (t.Value)
{
case TextureChannel.Red: return TextureChannel.Green;
case TextureChannel.Green: return TextureChannel.Blue;
case TextureChannel.Blue: return TextureChannel.Alpha;
case TextureChannel.Alpha: return null;
default: return null;
}
return (TextureChannel)nextChannel;
}
public Sprite Allocate(Size imageSize)
public Sprite Allocate(Size imageSize) { return Allocate(imageSize, float2.Zero); }
public Sprite Allocate(Size imageSize, float2 spriteOffset)
{
if (current == null)
{
current = NewSheet();
channel = NextChannel(null);
}
if (imageSize.Width + p.X > current.Size.Width)
{
p = new Point(0, p.Y + rowHeight);
@@ -81,22 +91,25 @@ namespace OpenRA.Graphics
if (p.Y + imageSize.Height > current.Size.Height)
{
if (null == (channel = NextChannel(channel)))
var next = NextChannel(channel);
if (next == null)
{
current = NewSheet();
channel = NextChannel(channel);
current = allocateSheet();
channel = TextureChannel.Red;
}
else
channel = next.Value;
rowHeight = imageSize.Height;
p = new Point(0,0);
}
Sprite rect = new Sprite(current, new Rectangle(p, imageSize), channel.Value);
current.MakeDirty();
var rect = new Sprite(current, new Rectangle(p, imageSize), spriteOffset, channel);
p.X += imageSize.Width;
return rect;
}
public Sheet Current { get { return current; } }
}
}

View File

@@ -185,14 +185,14 @@ namespace OpenRA.Graphics
{
s[starti, j].DrawAt(
Game.CellSize * new float2(starti, j),
pal.Index,
pal,
new float2(Game.CellSize * (i - starti), Game.CellSize));
starti = i + 1;
}
s[i, j].DrawAt(
Game.CellSize * new float2(i, j),
pal.Index);
pal);
starti = i + 1;
last = s[i, j];
}
@@ -200,7 +200,7 @@ namespace OpenRA.Graphics
if (starti < clip.Right)
s[starti, j].DrawAt(
Game.CellSize * new float2(starti, j),
pal.Index,
pal,
new float2(Game.CellSize * (clip.Right - starti), Game.CellSize));
}
}

View File

@@ -17,57 +17,52 @@ namespace OpenRA.Graphics
public readonly Rectangle bounds;
public readonly Sheet sheet;
public readonly TextureChannel channel;
public readonly RectangleF uv;
public readonly float2 size;
readonly float2[] uvhax;
public readonly float2 offset;
readonly float2[] textureCoords;
public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel)
: this(sheet, bounds, float2.Zero, channel) {}
public Sprite(Sheet sheet, Rectangle bounds, float2 offset, TextureChannel channel)
{
this.bounds = bounds;
this.sheet = sheet;
this.bounds = bounds;
this.offset = offset;
this.channel = channel;
uv = new RectangleF(
(float)(bounds.Left) / sheet.Size.Width,
(float)(bounds.Top) / sheet.Size.Height,
(float)(bounds.Width) / sheet.Size.Width,
(float)(bounds.Height) / sheet.Size.Height);
uvhax = new float2[]
{
new float2( uv.Left, uv.Top ),
new float2( uv.Right, uv.Top ),
new float2( uv.Left, uv.Bottom ),
new float2( uv.Right, uv.Bottom ),
};
this.size = new float2(bounds.Size);
var left = (float)(bounds.Left) / sheet.Size.Width;
var top = (float)(bounds.Top) / sheet.Size.Height;
var right = (float)(bounds.Right) / sheet.Size.Width;
var bottom = (float)(bounds.Bottom) / sheet.Size.Height;
textureCoords = new float2[]
{
new float2(left, top),
new float2(right, top),
new float2(left, bottom),
new float2(right, bottom),
};
}
public float2 FastMapTextureCoords( int k )
public float2 FastMapTextureCoords(int k)
{
return uvhax[ k ];
return textureCoords[k];
}
public void DrawAt( WorldRenderer wr, float2 location, string palette )
public void DrawAt(float2 location, PaletteReference pal)
{
Game.Renderer.WorldSpriteRenderer.DrawSprite( this, location, wr, palette, this.size );
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, pal, size);
}
public void DrawAt( float2 location, int paletteIndex )
public void DrawAt(float2 location, PaletteReference pal, float scale)
{
Game.Renderer.WorldSpriteRenderer.DrawSprite( this, location, paletteIndex, this.size );
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, pal, size*scale);
}
public void DrawAt(float2 location, int paletteIndex, float scale)
public void DrawAt(float2 location, PaletteReference pal, float2 size)
{
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, paletteIndex, this.size * scale);
}
public void DrawAt( float2 location, int paletteIndex, float2 size )
{
Game.Renderer.WorldSpriteRenderer.DrawSprite( this, location, paletteIndex, size );
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, pal, size);
}
}

View File

@@ -31,8 +31,10 @@ namespace OpenRA.Graphics
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph,
Pair<char,Color>.EqualityComparer);
// setup a 1-channel SheetBuilder for our private use
if (builder == null) builder = new SheetBuilder(TextureChannel.Alpha);
// setup a SheetBuilder for our private use
// TODO: SheetBuilder state is leaked between mod switches
if (builder == null)
builder = new SheetBuilder(SheetType.BGRA);
PrecacheColor(Color.White);
PrecacheColor(Color.Red);
@@ -46,7 +48,7 @@ namespace OpenRA.Graphics
throw new InvalidOperationException();
}
public void DrawText (string text, float2 location, Color c)
public void DrawText(string text, float2 location, Color c)
{
location.Y += size; // baseline vs top
@@ -84,7 +86,7 @@ namespace OpenRA.Graphics
public int2 Measure(string text)
{
return new int2((int)text.Split( '\n' ).Max( s => s.Sum(a => glyphs[Pair.New(a, Color.White)].Advance)), text.Split('\n').Count()*size);
return new int2((int)text.Split('\n').Max(s => s.Sum(a => glyphs[Pair.New(a, Color.White)].Advance)), text.Split('\n').Count()*size);
}
Cache<Pair<char,Color>, GlyphInfo> glyphs;
@@ -96,9 +98,8 @@ namespace OpenRA.Graphics
face.LoadGlyph(index, LoadFlags.Default, LoadTarget.Normal);
face.Glyph.RenderGlyph(RenderMode.Normal);
var s = builder.Allocate(
new Size((int)face.Glyph.Metrics.Width >> 6,
(int)face.Glyph.Metrics.Height >> 6));
var size = new Size((int)face.Glyph.Metrics.Width >> 6, (int)face.Glyph.Metrics.Height >> 6);
var s = builder.Allocate(size);
var g = new GlyphInfo
{
@@ -128,6 +129,8 @@ namespace OpenRA.Graphics
p += face.Glyph.Bitmap.Pitch;
}
}
s.sheet.CommitData();
return g;
}

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System.IO;
using System.Linq;
using OpenRA.FileFormats;
@@ -15,11 +16,11 @@ namespace OpenRA.Graphics
{
public class SpriteLoader
{
public SpriteLoader( string[] exts, SheetBuilder sheetBuilder )
public SpriteLoader(string[] exts, SheetBuilder sheetBuilder)
{
SheetBuilder = sheetBuilder;
this.exts = exts;
sprites = new Cache<string, Sprite[]>( LoadSprites );
sprites = new Cache<string, Sprite[]>(LoadSprites);
}
readonly SheetBuilder SheetBuilder;
@@ -28,8 +29,19 @@ namespace OpenRA.Graphics
Sprite[] LoadSprites(string filename)
{
var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts));
return shp.Frames.Select(a => SheetBuilder.Add(a.Image, shp.Size)).ToArray();
BinaryReader reader = new BinaryReader(FileSystem.OpenWithExts(filename, exts));
var ImageCount = reader.ReadUInt16();
if (ImageCount == 0)
{
var shp = new ShpTSReader(FileSystem.OpenWithExts(filename, exts));
return shp.Select(a => SheetBuilder.Add(a.Image, shp.Size)).ToArray();
}
else
{
var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts));
return shp.Frames.Select(a => SheetBuilder.Add(a.Image, shp.Size)).ToArray();
}
}
public Sprite[] LoadAllSprites(string filename) { return sprites[filename]; }

View File

@@ -33,40 +33,42 @@ namespace OpenRA.Graphics
if (nv > 0)
{
shader.SetTexture("DiffuseTexture", currentSheet.Texture);
renderer.Device.EnableAlphaBlending();
shader.Render(() =>
{
var vb = renderer.GetTempVertexBuffer();
vb.SetData(vertices, nv);
renderer.DrawBatch(vb, 0, nv, PrimitiveType.QuadList);
});
renderer.Device.DisableAlphaBlending();
nv = 0;
currentSheet = null;
}
}
public void DrawSprite(Sprite s, float2 location, WorldRenderer wr, string palette)
public void DrawSprite(Sprite s, float2 location, PaletteReference pal)
{
DrawSprite(s, location, wr.Palette(palette).Index, s.size);
DrawSprite(s, location, pal.Index, s.size);
}
public void DrawSprite(Sprite s, float2 location, WorldRenderer wr, string palette, float2 size)
public void DrawSprite(Sprite s, float2 location, PaletteReference pal, float2 size)
{
DrawSprite(s, location, wr.Palette(palette).Index, size);
DrawSprite(s, location, pal.Index, size);
}
public void DrawSprite(Sprite s, float2 location, int paletteIndex, float2 size)
void DrawSprite(Sprite s, float2 location, int paletteIndex, float2 size)
{
Renderer.CurrentBatchRenderer = this;
if (s.sheet != currentSheet)
Flush();
if( nv + 4 > Renderer.TempBufferSize )
if (nv + 4 > Renderer.TempBufferSize)
Flush();
currentSheet = s.sheet;
Util.FastCreateQuad(vertices, location.ToInt2(), s, paletteIndex, nv, size);
Util.FastCreateQuad(vertices, location + s.offset, s, paletteIndex, nv, size);
nv += 4;
}
@@ -81,10 +83,27 @@ namespace OpenRA.Graphics
DrawSprite(s, location, 0, size);
}
public void DrawSprite(Sprite s, float2 a, float2 b, float2 c, float2 d)
{
Renderer.CurrentBatchRenderer = this;
if (s.sheet != currentSheet)
Flush();
if (nv + 4 > Renderer.TempBufferSize)
Flush();
currentSheet = s.sheet;
Util.FastCreateQuad(vertices, a, b, c, d, s, 0, nv);
nv += 4;
}
public void DrawVertexBuffer(IVertexBuffer<Vertex> buffer, int start, int length, PrimitiveType type, Sheet sheet)
{
shader.SetTexture("DiffuseTexture", sheet.Texture);
renderer.Device.EnableAlphaBlending();
shader.Render(() => renderer.DrawBatch(buffer, start, length, type));
renderer.Device.DisableAlphaBlending();
}
public void SetPalette(ITexture palette)

View File

@@ -18,8 +18,8 @@ namespace OpenRA.Graphics
{
class TerrainRenderer
{
SheetBuilder sheetBuilder;
IVertexBuffer<Vertex> vertexBuffer;
Sheet terrainSheet;
World world;
Map map;
@@ -29,37 +29,42 @@ namespace OpenRA.Graphics
this.world = world;
this.map = world.Map;
var tileSize = new Size( Game.CellSize, Game.CellSize );
var allocated = false;
Func<Sheet> allocate = () =>
{
if (allocated)
throw new SheetOverflowException("Terrain sheet overflow");
allocated = true;
// TODO: Use a fixed sheet size specified in the tileset yaml
return SheetBuilder.AllocateSheet();
};
sheetBuilder = new SheetBuilder(SheetType.Indexed, allocate);
var tileSize = new Size(Game.CellSize, Game.CellSize);
var tileMapping = new Cache<TileReference<ushort,byte>, Sprite>(
x => Game.modData.SheetBuilder.Add(world.TileSet.GetBytes(x), tileSize));
var vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width];
terrainSheet = tileMapping[map.MapTiles.Value[map.Bounds.Left, map.Bounds.Top]].sheet;
int nv = 0;
x => sheetBuilder.Add(world.TileSet.GetBytes(x), tileSize));
var terrainPalette = wr.Palette("terrain").Index;
var vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width];
int nv = 0;
for( int j = map.Bounds.Top; j < map.Bounds.Bottom; j++ )
for( int i = map.Bounds.Left; i < map.Bounds.Right; i++ )
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
{
var tile = tileMapping[map.MapTiles.Value[i, j]];
// TODO: move GetPaletteIndex out of the inner loop.
Util.FastCreateQuad(vertices, Game.CellSize * new float2(i, j), tile, terrainPalette, nv, tile.size);
nv += 4;
if (tileMapping[map.MapTiles.Value[i, j]].sheet != terrainSheet)
throw new InvalidOperationException("Terrain sprites span multiple sheets. Try increasing Game.Settings.Graphics.SheetSize.");
}
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer( vertices.Length );
vertexBuffer.SetData( vertices, nv );
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(vertices.Length);
vertexBuffer.SetData(vertices, nv);
}
public void Draw( WorldRenderer wr, Viewport viewport )
public void Draw(WorldRenderer wr, Viewport viewport)
{
int verticesPerRow = map.Bounds.Width * 4;
int verticesPerRow = 4*map.Bounds.Width;
int visibleRows = (int)(viewport.Height * 1f / Game.CellSize / viewport.Zoom + 2);
@@ -79,17 +84,22 @@ namespace OpenRA.Graphics
firstRow = r.Bottom - map.Bounds.Top;
}
if (firstRow < 0) firstRow = 0;
if (lastRow > map.Bounds.Height) lastRow = map.Bounds.Height;
// Sanity checking
if (firstRow < 0)
firstRow = 0;
if( lastRow < firstRow ) lastRow = firstRow;
if (lastRow > map.Bounds.Height)
lastRow = map.Bounds.Height;
if (lastRow < firstRow)
lastRow = firstRow;
Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer(
vertexBuffer, verticesPerRow * firstRow, verticesPerRow * (lastRow - firstRow),
PrimitiveType.QuadList, terrainSheet);
PrimitiveType.QuadList, sheetBuilder.Current);
foreach (var r in world.WorldActor.TraitsImplementing<IRenderOverlay>())
r.Render( wr );
r.Render(wr);
}
}
}

View File

@@ -0,0 +1,59 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
namespace OpenRA.Graphics
{
public struct TextRenderable : IRenderable
{
readonly SpriteFont font;
readonly WPos pos;
readonly int zOffset;
readonly Color color;
readonly string text;
public TextRenderable(SpriteFont font, WPos pos, int zOffset, Color color, string text)
{
this.font = font;
this.pos = pos;
this.zOffset = zOffset;
this.color = color;
this.text = text;
}
public WPos Pos { get { return pos; } }
public float Scale { get { return 1f; } }
public PaletteReference Palette { get { return null; } }
public int ZOffset { get { return zOffset; } }
public IRenderable WithScale(float newScale) { return new TextRenderable(font, pos, zOffset, color, text); }
public IRenderable WithPalette(PaletteReference newPalette) { return new TextRenderable(font, pos, zOffset, color, text); }
public IRenderable WithZOffset(int newOffset) { return new TextRenderable(font, pos, zOffset, color, text); }
public IRenderable WithPos(WPos pos) { return new TextRenderable(font, pos, zOffset, color, text); }
public void BeforeRender(WorldRenderer wr) {}
public void Render(WorldRenderer wr)
{
var screenPos = Game.viewport.Zoom*(wr.ScreenPosition(pos) - Game.viewport.Location) - 0.5f*font.Measure(text).ToFloat2();
var screenPxPos = new float2((float)Math.Round(screenPos.X), (float)Math.Round(screenPos.Y));
font.DrawTextWithContrast(text, screenPxPos, color, Color.Black, 1);
}
public void RenderDebugGeometry(WorldRenderer wr)
{
var size = font.Measure(text).ToFloat2();
var offset = wr.ScreenPxPosition(pos) - 0.5f*size;
Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + size, Color.Red);
}
}
}

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System;
using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
@@ -17,27 +18,32 @@ namespace OpenRA.Graphics
static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f };
public static void FastCreateQuad(Vertex[] vertices, float2 o, Sprite r, int palette, int nv, float2 size)
{
var b = new float2(o.X + size.X, o.Y);
var c = new float2(o.X + size.X, o.Y + size.Y);
var d = new float2(o.X, o.Y + size.Y);
FastCreateQuad(vertices, o, b, c, d, r, palette, nv);
}
public static void FastCreateQuad(Vertex[] vertices, float2 a, float2 b, float2 c, float2 d, Sprite r, int palette, int nv)
{
var attrib = new float2(palette / (float)HardwarePalette.MaxPalettes, channelSelect[(int)r.channel]);
vertices[nv] = new Vertex(o,
r.FastMapTextureCoords(0), attrib);
vertices[nv + 1] = new Vertex(new float2(o.X + size.X, o.Y),
r.FastMapTextureCoords(1), attrib);
vertices[nv + 2] = new Vertex(new float2(o.X + size.X, o.Y + size.Y),
r.FastMapTextureCoords(3), attrib);
vertices[nv + 3] = new Vertex(new float2(o.X, o.Y + size.Y),
r.FastMapTextureCoords(2), attrib);
vertices[nv] = new Vertex(a, r.FastMapTextureCoords(0), attrib);
vertices[nv + 1] = new Vertex(b, r.FastMapTextureCoords(1), attrib);
vertices[nv + 2] = new Vertex(c, r.FastMapTextureCoords(3), attrib);
vertices[nv + 3] = new Vertex(d, r.FastMapTextureCoords(2), attrib);
}
static readonly int[] channelMasks = { 2, 1, 0, 3 }; // yes, our channel order is nuts.
public static void FastCopyIntoChannel(Sprite dest, byte[] src)
public static void FastCopyIntoChannel(Sprite dest, byte[] src) { FastCopyIntoChannel(dest, 0, src); }
public static void FastCopyIntoChannel(Sprite dest, int channelOffset, byte[] src)
{
var data = dest.sheet.Data;
var srcStride = dest.bounds.Width;
var destStride = dest.sheet.Size.Width * 4;
var destOffset = destStride * dest.bounds.Top + dest.bounds.Left * 4 + channelMasks[(int)dest.channel];
var destOffset = destStride * dest.bounds.Top + dest.bounds.Left * 4 + channelMasks[(int)dest.channel + channelOffset];
var destSkip = destStride - 4 * srcStride;
var height = dest.bounds.Height;
@@ -52,5 +58,217 @@ namespace OpenRA.Graphics
destOffset += destSkip;
}
}
public static float[] IdentityMatrix()
{
return Exts.MakeArray(16, j => (j % 5 == 0) ? 1.0f : 0);
}
public static float[] ScaleMatrix(float sx, float sy, float sz)
{
var mtx = IdentityMatrix();
mtx[0] = sx;
mtx[5] = sy;
mtx[10] = sz;
return mtx;
}
public static float[] TranslationMatrix(float x, float y, float z)
{
var mtx = IdentityMatrix();
mtx[12] = x;
mtx[13] = y;
mtx[14] = z;
return mtx;
}
public static float[] MatrixMultiply(float[] lhs, float[] rhs)
{
var mtx = new float[16];
for (var i = 0; i < 4; i++)
for (var j = 0; j < 4; j++)
{
mtx[4*i + j] = 0;
for (var k = 0; k < 4; k++)
mtx[4*i + j] += lhs[4*k + j]*rhs[4*i + k];
}
return mtx;
}
public static float[] MatrixVectorMultiply(float[] mtx, float[] vec)
{
var ret = new float[4];
for (var j = 0; j < 4; j++)
{
ret[j] = 0;
for (var k = 0; k < 4; k++)
ret[j] += mtx[4*k + j]*vec[k];
}
return ret;
}
public static float[] MatrixInverse(float[] m)
{
var mtx = new float[16];
mtx[0] = m[5]*m[10]*m[15] -
m[5]*m[11]*m[14] -
m[9]*m[6]*m[15] +
m[9]*m[7]*m[14] +
m[13]*m[6]*m[11] -
m[13]*m[7]*m[10];
mtx[4] = -m[4]*m[10]*m[15] +
m[4]*m[11]*m[14] +
m[8]*m[6]*m[15] -
m[8]*m[7]*m[14] -
m[12]*m[6]*m[11] +
m[12]*m[7]*m[10];
mtx[8] = m[4]*m[9]*m[15] -
m[4]*m[11]*m[13] -
m[8]*m[5]*m[15] +
m[8]*m[7]*m[13] +
m[12]*m[5]*m[11] -
m[12]*m[7]*m[9];
mtx[12] = -m[4]*m[9]*m[14] +
m[4]*m[10]*m[13] +
m[8]*m[5]*m[14] -
m[8]*m[6]*m[13] -
m[12]*m[5]*m[10] +
m[12]*m[6]*m[9];
mtx[1] = -m[1]*m[10]*m[15] +
m[1]*m[11]*m[14] +
m[9]*m[2]*m[15] -
m[9]*m[3]*m[14] -
m[13]*m[2]*m[11] +
m[13]*m[3]*m[10];
mtx[5] = m[0]*m[10]*m[15] -
m[0]*m[11]*m[14] -
m[8]*m[2]*m[15] +
m[8]*m[3]*m[14] +
m[12]*m[2]*m[11] -
m[12]*m[3]*m[10];
mtx[9] = -m[0]*m[9]*m[15] +
m[0]*m[11]*m[13] +
m[8]*m[1]*m[15] -
m[8]*m[3]*m[13] -
m[12]*m[1]*m[11] +
m[12]*m[3]*m[9];
mtx[13] = m[0]*m[9]*m[14] -
m[0]*m[10]*m[13] -
m[8]*m[1]*m[14] +
m[8]*m[2]*m[13] +
m[12]*m[1]*m[10] -
m[12]*m[2]*m[9];
mtx[2] = m[1]*m[6]*m[15] -
m[1]*m[7]*m[14] -
m[5]*m[2]*m[15] +
m[5]*m[3]*m[14] +
m[13]*m[2]*m[7] -
m[13]*m[3]*m[6];
mtx[6] = -m[0]*m[6]*m[15] +
m[0]*m[7]*m[14] +
m[4]*m[2]*m[15] -
m[4]*m[3]*m[14] -
m[12]*m[2]*m[7] +
m[12]*m[3]*m[6];
mtx[10] = m[0]*m[5]*m[15] -
m[0]*m[7]*m[13] -
m[4]*m[1]*m[15] +
m[4]*m[3]*m[13] +
m[12]*m[1]*m[7] -
m[12]*m[3]*m[5];
mtx[14] = -m[0]*m[5]*m[14] +
m[0]*m[6]*m[13] +
m[4]*m[1]*m[14] -
m[4]*m[2]*m[13] -
m[12]*m[1]*m[6] +
m[12]*m[2]*m[5];
mtx[3] = -m[1]*m[6]*m[11] +
m[1]*m[7]*m[10] +
m[5]*m[2]*m[11] -
m[5]*m[3]*m[10] -
m[9]*m[2]*m[7] +
m[9]*m[3]*m[6];
mtx[7] = m[0]*m[6]*m[11] -
m[0]*m[7]*m[10] -
m[4]*m[2]*m[11] +
m[4]*m[3]*m[10] +
m[8]*m[2]*m[7] -
m[8]*m[3]*m[6];
mtx[11] = -m[0]*m[5]*m[11] +
m[0]*m[7]*m[9] +
m[4]*m[1]*m[11] -
m[4]*m[3]*m[9] -
m[8]*m[1]*m[7] +
m[8]*m[3]*m[5];
mtx[15] = m[0]*m[5]*m[10] -
m[0]*m[6]*m[9] -
m[4]*m[1]*m[10] +
m[4]*m[2]*m[9] +
m[8]*m[1]*m[6] -
m[8]*m[2]*m[5];
var det = m[0]*mtx[0] + m[1]*mtx[4] + m[2]*mtx[8] + m[3]*mtx[12];
if (det == 0)
return null;
for (var i = 0; i < 16; i++)
mtx[i] *= 1/det;
return mtx;
}
public static float[] MakeFloatMatrix(int[] imtx)
{
var fmtx = new float[16];
for (var i = 0; i < 16; i++)
fmtx[i] = imtx[i]*1f / imtx[15];
return fmtx;
}
public static float[] MatrixAABBMultiply(float[] mtx, float[] bounds)
{
// Corner offsets
var ix = new uint[] {0,0,0,0,3,3,3,3};
var iy = new uint[] {1,1,4,4,1,1,4,4};
var iz = new uint[] {2,5,2,5,2,5,2,5};
// Vectors to opposing corner
var ret = new float[] {float.MaxValue, float.MaxValue, float.MaxValue,
float.MinValue, float.MinValue, float.MinValue};
// Transform vectors and find new bounding box
for (var i = 0; i < 8; i++)
{
var vec = new float[] {bounds[ix[i]], bounds[iy[i]], bounds[iz[i]], 1};
var tvec = Util.MatrixVectorMultiply(mtx, vec);
ret[0] = Math.Min(ret[0], tvec[0]/tvec[3]);
ret[1] = Math.Min(ret[1], tvec[1]/tvec[3]);
ret[2] = Math.Min(ret[2], tvec[2]/tvec[3]);
ret[3] = Math.Max(ret[3], tvec[0]/tvec[3]);
ret[4] = Math.Max(ret[4], tvec[1]/tvec[3]);
ret[5] = Math.Max(ret[5], tvec[2]/tvec[3]);
}
return ret;
}
}
}

View File

@@ -0,0 +1,128 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
struct Limb
{
public float Scale;
public float[] Bounds;
public byte[] Size;
public VoxelRenderData RenderData;
}
public class Voxel
{
Limb[] limbData;
float[] transforms;
public readonly uint Frames;
public readonly uint Limbs;
public Voxel(VoxelLoader loader, VxlReader vxl, HvaReader hva)
{
if (vxl.LimbCount != hva.LimbCount)
throw new InvalidOperationException("Voxel and hva limb counts don't match");
transforms = hva.Transforms;
Frames = hva.FrameCount;
Limbs = hva.LimbCount;
limbData = new Limb[vxl.LimbCount];
for (var i = 0; i < vxl.LimbCount; i++)
{
var vl = vxl.Limbs[i];
var l = new Limb();
l.Scale = vl.Scale;
l.Bounds = (float[])vl.Bounds.Clone();
l.Size = (byte[])vl.Size.Clone();
l.RenderData = loader.GenerateRenderData(vxl.Limbs[i]);
limbData[i] = l;
}
}
public float[] TransformationMatrix(uint limb, uint frame)
{
if (frame >= Frames)
throw new ArgumentOutOfRangeException("frame", "Only {0} frames exist.".F(Frames));
if (limb >= Limbs)
throw new ArgumentOutOfRangeException("limb", "Only {1} limbs exist.".F(Limbs));
var l = limbData[limb];
var t = new float[16];
Array.Copy(transforms, 16*(Limbs*frame + limb), t, 0, 16);
// Fix limb position
t[12] *= l.Scale*(l.Bounds[3] - l.Bounds[0]) / l.Size[0];
t[13] *= l.Scale*(l.Bounds[4] - l.Bounds[1]) / l.Size[1];
t[14] *= l.Scale*(l.Bounds[5] - l.Bounds[2]) / l.Size[2];
// Center, flip and scale
t = Util.MatrixMultiply(t, Util.TranslationMatrix(l.Bounds[0], l.Bounds[1], l.Bounds[2]));
t = Util.MatrixMultiply(Util.ScaleMatrix(l.Scale, -l.Scale, l.Scale), t);
return t;
}
public VoxelRenderData RenderData(uint limb)
{
return limbData[limb].RenderData;
}
public float[] Size
{
get
{
return limbData.Select(a => a.Size.Select(b => a.Scale*b).ToArray())
.Aggregate((a,b) => new float[]
{
Math.Max(a[0], b[0]),
Math.Max(a[1], b[1]),
Math.Max(a[2], b[2])
});
}
}
public float[] Bounds(uint frame)
{
var ret = new float[] {float.MaxValue,float.MaxValue,float.MaxValue,
float.MinValue,float.MinValue,float.MinValue};
for (uint j = 0; j < Limbs; j++)
{
var l = limbData[j];
var b = new float[]
{
0, 0, 0,
(l.Bounds[3] - l.Bounds[0]),
(l.Bounds[4] - l.Bounds[1]),
(l.Bounds[5] - l.Bounds[2])
};
// Calculate limb bounding box
var bb = Util.MatrixAABBMultiply(TransformationMatrix(j, frame), b);
for (var i = 0; i < 3; i++)
{
ret[i] = Math.Min(ret[i], bb[i]);
ret[i+3] = Math.Max(ret[i+3], bb[i+3]);
}
}
return ret;
}
}
}

View File

@@ -0,0 +1,35 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
public struct VoxelAnimation
{
public readonly Voxel Voxel;
public readonly Func<WVec> OffsetFunc;
public readonly Func<IEnumerable<WRot>> RotationFunc;
public readonly Func<bool> DisableFunc;
public readonly Func<uint> FrameFunc;
public VoxelAnimation(Voxel voxel, Func<WVec> offset, Func<IEnumerable<WRot>> rotation, Func<bool> disable, Func<uint> frame)
{
Voxel = voxel;
OffsetFunc = offset;
RotationFunc = rotation;
DisableFunc = disable;
FrameFunc = frame;
}
}
}

View File

@@ -0,0 +1,222 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
public struct VoxelRenderData
{
public readonly int Start;
public readonly int Count;
public readonly Sheet Sheet;
public VoxelRenderData(int start, int count, Sheet sheet)
{
Start = start;
Count = count;
Sheet = sheet;
}
}
public class VoxelLoader
{
SheetBuilder sheetBuilder;
Cache<Pair<string,string>, Voxel> voxels;
IVertexBuffer<Vertex> vertexBuffer;
List<Vertex[]> vertices;
int totalVertexCount;
int cachedVertexCount;
SheetBuilder CreateSheetBuilder()
{
var allocated = false;
Func<Sheet> allocate = () =>
{
if (allocated)
throw new SheetOverflowException("");
allocated = true;
return SheetBuilder.AllocateSheet();
};
return new SheetBuilder(SheetType.DualIndexed, allocate);
}
public VoxelLoader()
{
voxels = new Cache<Pair<string,string>, Voxel>(LoadFile);
vertices = new List<Vertex[]>();
totalVertexCount = 0;
cachedVertexCount = 0;
sheetBuilder = CreateSheetBuilder();
}
static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f };
Vertex[] GenerateSlicePlane(int su, int sv, Func<int,int,VxlElement> first, Func<int,int,VxlElement> second, Func<int, int, float[]> coord)
{
var colors = new byte[su*sv];
var normals = new byte[su*sv];
var c = 0;
for (var v = 0; v < sv; v++)
for (var u = 0; u < su; u++)
{
var voxel = first(u,v) ?? second(u,v);
colors[c] = voxel == null ? (byte)0 : voxel.Color;
normals[c] = voxel == null ? (byte)0 : voxel.Normal;
c++;
}
Sprite s = sheetBuilder.Allocate(new Size(su, sv));
Util.FastCopyIntoChannel(s, 0, colors);
Util.FastCopyIntoChannel(s, 1, normals);
s.sheet.CommitData();
var channels = new float2(channelSelect[(int)s.channel], channelSelect[(int)s.channel + 1]);
return new Vertex[4]
{
new Vertex(coord(0, 0), s.FastMapTextureCoords(0), channels),
new Vertex(coord(su, 0), s.FastMapTextureCoords(1), channels),
new Vertex(coord(su, sv), s.FastMapTextureCoords(3), channels),
new Vertex(coord(0, sv), s.FastMapTextureCoords(2), channels)
};
}
IEnumerable<Vertex[]> GenerateSlicePlanes(VxlLimb l)
{
Func<int,int,int,VxlElement> get = (x,y,z) =>
{
if (x < 0 || y < 0 || z < 0)
return null;
if (x >= l.Size[0] || y >= l.Size[1] || z >= l.Size[2])
return null;
var v = l.VoxelMap[(byte)x,(byte)y];
if (v == null || !v.ContainsKey((byte)z))
return null;
return l.VoxelMap[(byte)x,(byte)y][(byte)z];
};
// Cull slices without any visible faces
var xPlanes = new bool[l.Size[0]+1];
var yPlanes = new bool[l.Size[1]+1];
var zPlanes = new bool[l.Size[2]+1];
for (var x = 0; x < l.Size[0]; x++)
{
for (var y = 0; y < l.Size[1]; y++)
{
for (var z = 0; z < l.Size[2]; z++)
{
if (get(x,y,z) == null)
continue;
// Only generate a plane if it is actually visible
if (!xPlanes[x] && get(x-1,y,z) == null)
xPlanes[x] = true;
if (!xPlanes[x+1] && get(x+1,y,z) == null)
xPlanes[x+1] = true;
if (!yPlanes[y] && get(x,y-1,z) == null)
yPlanes[y] = true;
if (!yPlanes[y+1] && get(x,y+1,z) == null)
yPlanes[y+1] = true;
if (!zPlanes[z] && get(x,y,z-1) == null)
zPlanes[z] = true;
if (!zPlanes[z+1] && get(x,y,z+1) == null)
zPlanes[z+1] = true;
}
}
}
for (var x = 0; x <= l.Size[0]; x++)
if (xPlanes[x])
yield return GenerateSlicePlane(l.Size[1], l.Size[2],
(u,v) => get(x, u, v),
(u,v) => get(x - 1, u, v),
(u,v) => new float[] {x, u, v});
for (var y = 0; y <= l.Size[1]; y++)
if (yPlanes[y])
yield return GenerateSlicePlane(l.Size[0], l.Size[2],
(u,v) => get(u, y, v),
(u,v) => get(u, y - 1, v),
(u,v) => new float[] {u, y, v});
for (var z = 0; z <= l.Size[2]; z++)
if (zPlanes[z])
yield return GenerateSlicePlane(l.Size[0], l.Size[1],
(u,v) => get(u, v, z),
(u,v) => get(u, v, z - 1),
(u,v) => new float[] {u, v, z});
}
public VoxelRenderData GenerateRenderData(VxlLimb l)
{
Vertex[] v;
try
{
v = GenerateSlicePlanes(l).SelectMany(x => x).ToArray();
}
catch (SheetOverflowException)
{
// Sheet overflow - allocate a new sheet and try once more
Log.Write("debug", "Voxel sheet overflow! Generating new sheet");
sheetBuilder = CreateSheetBuilder();
v = GenerateSlicePlanes(l).SelectMany(x => x).ToArray();
}
vertices.Add(v);
var start = totalVertexCount;
var count = v.Length;
totalVertexCount += count;
return new VoxelRenderData(start, count, sheetBuilder.Current);
}
public void RefreshBuffer()
{
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(totalVertexCount);
vertexBuffer.SetData(vertices.SelectMany(v => v).ToArray(), totalVertexCount);
cachedVertexCount = totalVertexCount;
}
public IVertexBuffer<Vertex> VertexBuffer
{
get
{
if (cachedVertexCount != totalVertexCount)
RefreshBuffer();
return vertexBuffer;
}
}
Voxel LoadFile(Pair<string,string> files)
{
var vxl = new VxlReader(FileSystem.OpenWithExts(files.First, ".vxl"));
var hva = new HvaReader(FileSystem.OpenWithExts(files.Second, ".hva"));
return new Voxel(this, vxl, hva);
}
public Voxel Load(string vxl, string hva)
{
return voxels[Pair.New(vxl, hva)];
}
}
}

View File

@@ -0,0 +1,88 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileFormats;
namespace OpenRA.Graphics
{
public static class VoxelProvider
{
static Dictionary<string, Dictionary<string, Voxel>> units;
public static void Initialize(string[] voxelFiles, List<MiniYamlNode> voxelNodes)
{
units = new Dictionary<string, Dictionary<string, Voxel>>();
var sequences = voxelFiles
.Select(s => MiniYaml.FromFile(s))
.Aggregate(voxelNodes, MiniYaml.MergeLiberal);
foreach (var s in sequences)
LoadVoxelsForUnit(s.Key, s.Value);
Game.modData.VoxelLoader.RefreshBuffer();
}
static Voxel LoadVoxel(string unit, string name, MiniYaml info)
{
var vxl = unit;
var hva = unit;
if (info.Value != null)
{
var fields = info.Value.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
if (fields.Length >= 1)
vxl = hva = fields[0].Trim();
if (fields.Length >= 2)
hva = fields[1].Trim();
}
return Game.modData.VoxelLoader.Load(vxl, hva);
}
static void LoadVoxelsForUnit(string unit, MiniYaml sequences)
{
Game.modData.LoadScreen.Display();
try
{
var seq = sequences.NodesDict.ToDictionary(x => x.Key, x => LoadVoxel(unit,x.Key,x.Value));
units.Add(unit, seq);
}
catch (FileNotFoundException) {} // Do nothing; we can crash later if we actually wanted art
}
public static Voxel GetVoxel(string unitName, string voxelName)
{
try { return units[unitName][voxelName]; }
catch (KeyNotFoundException)
{
if (units.ContainsKey(unitName))
throw new InvalidOperationException(
"Unit `{0}` does not have a voxel `{1}`".F(unitName, voxelName));
else
throw new InvalidOperationException(
"Unit `{0}` does not have any voxels defined.".F(unitName));
}
}
public static bool HasVoxel(string unit, string seq)
{
if (!units.ContainsKey(unit))
throw new InvalidOperationException(
"Unit `{0}` does not have any voxels defined.".F(unit));
return units[unit].ContainsKey(seq);
}
}
}

View File

@@ -0,0 +1,176 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace OpenRA.Graphics
{
public struct VoxelRenderable : IRenderable
{
readonly IEnumerable<VoxelAnimation> voxels;
readonly WPos pos;
readonly int zOffset;
readonly WRot camera;
readonly WRot lightSource;
readonly float[] lightAmbientColor;
readonly float[] lightDiffuseColor;
readonly PaletteReference palette;
readonly PaletteReference normalsPalette;
readonly PaletteReference shadowPalette;
readonly float scale;
// Generated at render-time
VoxelRenderProxy renderProxy;
public VoxelRenderable(IEnumerable<VoxelAnimation> voxels, WPos pos, int zOffset, WRot camera, float scale,
WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadow)
{
this.voxels = voxels;
this.pos = pos;
this.zOffset = zOffset;
this.scale = scale;
this.camera = camera;
this.lightSource = lightSource;
this.lightAmbientColor = lightAmbientColor;
this.lightDiffuseColor = lightDiffuseColor;
this.palette = color;
this.normalsPalette = normals;
this.shadowPalette = shadow;
this.renderProxy = null;
}
public WPos Pos { get { return pos; } }
public float Scale { get { return scale; } }
public PaletteReference Palette { get { return palette; } }
public int ZOffset { get { return zOffset; } }
public IRenderable WithScale(float newScale)
{
return new VoxelRenderable(voxels, pos, zOffset, camera, newScale,
lightSource, lightAmbientColor, lightDiffuseColor,
palette, normalsPalette, shadowPalette);
}
public IRenderable WithPalette(PaletteReference newPalette)
{
return new VoxelRenderable(voxels, pos, zOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
newPalette, normalsPalette, shadowPalette);
}
public IRenderable WithZOffset(int newOffset)
{
return new VoxelRenderable(voxels, pos, newOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
palette, normalsPalette, shadowPalette);
}
public IRenderable WithPos(WPos newPos)
{
return new VoxelRenderable(voxels, newPos, zOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
palette, normalsPalette, shadowPalette);
}
// This will need generalizing once we support TS/RA2 terrain
static readonly float[] groundNormal = new float[] {0,0,1,1};
public void BeforeRender(WorldRenderer wr)
{
renderProxy = Game.Renderer.WorldVoxelRenderer.RenderAsync(
wr, voxels, camera, scale, groundNormal, lightSource,
lightAmbientColor, lightDiffuseColor,
palette, normalsPalette, shadowPalette);
}
public void Render(WorldRenderer wr)
{
var pxOrigin = wr.ScreenPosition(pos);
var groundZ = 0.5f*(pxOrigin.Y - wr.ScreenZPosition(pos, 0));
var shadowOrigin = pxOrigin - groundZ*(new float2(renderProxy.ShadowDirection, 1));
var psb = renderProxy.ProjectedShadowBounds;
var sa = shadowOrigin + psb[0];
var sb = shadowOrigin + psb[2];
var sc = shadowOrigin + psb[1];
var sd = shadowOrigin + psb[3];
Game.Renderer.WorldRgbaSpriteRenderer.DrawSprite(renderProxy.ShadowSprite, sa, sb, sc, sd);
Game.Renderer.WorldRgbaSpriteRenderer.DrawSprite(renderProxy.Sprite, pxOrigin - 0.5f*renderProxy.Sprite.size);
}
public void RenderDebugGeometry(WorldRenderer wr)
{
var pxOrigin = wr.ScreenPosition(pos);
var groundZ = 0.5f*(pxOrigin.Y - wr.ScreenZPosition(pos, 0));
var shadowOrigin = pxOrigin - groundZ*(new float2(renderProxy.ShadowDirection, 1));
// Draw sprite rect
var offset = pxOrigin + renderProxy.Sprite.offset - 0.5f*renderProxy.Sprite.size;
Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + renderProxy.Sprite.size, Color.Red);
// Draw transformed shadow sprite rect
var c = Color.Purple;
var psb = renderProxy.ProjectedShadowBounds;
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[1], shadowOrigin + psb[3], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[3], shadowOrigin + psb[0], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[0], shadowOrigin + psb[2], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[2], shadowOrigin + psb[1], c, c);
// Draw voxel bounding box
var draw = voxels.Where(v => v.DisableFunc == null || !v.DisableFunc());
var scaleTransform = Util.ScaleMatrix(scale, scale, scale);
var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix());
foreach (var v in draw)
{
var bounds = v.Voxel.Bounds(v.FrameFunc());
var worldTransform = v.RotationFunc().Reverse().Aggregate(scaleTransform,
(x,y) => Util.MatrixMultiply(x, Util.MakeFloatMatrix(y.AsMatrix())));
var pxOffset = wr.ScreenVector(v.OffsetFunc());
var pxPos = pxOrigin + new float2(pxOffset[0], pxOffset[1]);
var screenTransform = Util.MatrixMultiply(cameraTransform, worldTransform);
DrawBoundsBox(pxPos, screenTransform, bounds, Color.Yellow);
}
}
static readonly uint[] ix = new uint[] {0,0,0,0,3,3,3,3};
static readonly uint[] iy = new uint[] {1,1,4,4,1,1,4,4};
static readonly uint[] iz = new uint[] {2,5,2,5,2,5,2,5};
static void DrawBoundsBox(float2 pxPos, float[] transform, float[] bounds, Color c)
{
var corners = new float2[8];
for (var i = 0; i < 8; i++)
{
var vec = new float[] {bounds[ix[i]], bounds[iy[i]], bounds[iz[i]], 1};
var screen = Util.MatrixVectorMultiply(transform, vec);
corners[i] = pxPos + new float2(screen[0], screen[1]);
}
Game.Renderer.WorldLineRenderer.DrawLine(corners[0], corners[1], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(corners[1], corners[3], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(corners[3], corners[2], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(corners[2], corners[0], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(corners[4], corners[5], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(corners[5], corners[7], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(corners[7], corners[6], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(corners[6], corners[4], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(corners[0], corners[4], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(corners[1], corners[5], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(corners[2], corners[6], c, c);
Game.Renderer.WorldLineRenderer.DrawLine(corners[3], corners[7], c, c);
}
}
}

View File

@@ -0,0 +1,342 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
public class VoxelRenderProxy
{
public readonly Sprite Sprite;
public readonly Sprite ShadowSprite;
public readonly float ShadowDirection;
public readonly float2[] ProjectedShadowBounds;
public VoxelRenderProxy(Sprite sprite, Sprite shadowSprite, float2[] projectedShadowBounds, float shadowDirection)
{
Sprite = sprite;
ShadowSprite = shadowSprite;
ProjectedShadowBounds = projectedShadowBounds;
ShadowDirection = shadowDirection;
}
}
public class VoxelRenderer
{
Renderer renderer;
IShader shader;
SheetBuilder sheetBuilder;
Dictionary<Sheet, IFrameBuffer> mappedBuffers;
Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers;
List<Pair<Sheet, Action>> doRender;
// Static constants
static readonly float[] shadowDiffuse = new float[] {0,0,0};
static readonly float[] shadowAmbient = new float[] {1,1,1};
static readonly float2 spritePadding = new float2(2, 2);
static readonly float[] zeroVector = new float[] {0,0,0,1};
static readonly float[] zVector = new float[] {0,0,1,1};
static readonly float[] flipMtx = Util.ScaleMatrix(1, -1, 1);
static readonly float[] shadowScaleFlipMtx = Util.ScaleMatrix(2, -2, 2);
public VoxelRenderer(Renderer renderer, IShader shader)
{
this.renderer = renderer;
this.shader = shader;
mappedBuffers = new Dictionary<Sheet, IFrameBuffer>();
unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
doRender = new List<Pair<Sheet, Action>>();
}
public void SetPalette(ITexture palette)
{
shader.SetTexture("Palette", palette);
}
public void SetViewportParams(Size screen, float zoom, float2 scroll)
{
var a = 2f / Renderer.SheetSize;
var view = new float[]
{
a, 0, 0, 0,
0, -a, 0, 0,
0, 0, -2*a, 0,
-1, 1, 0, 1
};
shader.SetMatrix("View", view);
}
public VoxelRenderProxy RenderAsync(WorldRenderer wr, IEnumerable<VoxelAnimation> voxels, WRot camera, float scale,
float[] groundNormal, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadowPalette)
{
// Correct for inverted y-axis
var scaleTransform = Util.ScaleMatrix(scale, scale, scale);
// Correct for bogus light source definition
var lightYaw = Util.MakeFloatMatrix(new WRot(WAngle.Zero, WAngle.Zero, -lightSource.Yaw).AsMatrix());
var lightPitch = Util.MakeFloatMatrix(new WRot(WAngle.Zero, -lightSource.Pitch, WAngle.Zero).AsMatrix());
var shadowTransform = Util.MatrixMultiply(lightPitch, lightYaw);
var invShadowTransform = Util.MatrixInverse(shadowTransform);
var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix());
var invCameraTransform = Util.MatrixInverse(cameraTransform);
// Sprite rectangle
var tl = new float2(float.MaxValue, float.MaxValue);
var br = new float2(float.MinValue, float.MinValue);
// Shadow sprite rectangle
var stl = new float2(float.MaxValue, float.MaxValue);
var sbr = new float2(float.MinValue, float.MinValue);
foreach (var v in voxels)
{
// Convert screen offset back to world coords
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(v.OffsetFunc()));
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
var worldTransform = v.RotationFunc().Aggregate(Util.IdentityMatrix(),
(x,y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
worldTransform = Util.MatrixMultiply(scaleTransform, worldTransform);
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
var bounds = v.Voxel.Bounds(v.FrameFunc());
var worldBounds = Util.MatrixAABBMultiply(worldTransform, bounds);
var screenBounds = Util.MatrixAABBMultiply(cameraTransform, worldBounds);
var shadowBounds = Util.MatrixAABBMultiply(shadowTransform, worldBounds);
// Aggregate bounds rects
tl = float2.Min(tl, new float2(screenBounds[0], screenBounds[1]));
br = float2.Max(br, new float2(screenBounds[3], screenBounds[4]));
stl = float2.Min(stl, new float2(shadowBounds[0], shadowBounds[1]));
sbr = float2.Max(sbr, new float2(shadowBounds[3], shadowBounds[4]));
}
// Inflate rects to ensure rendering is within bounds
tl -= spritePadding;
br += spritePadding;
stl -= spritePadding;
sbr += spritePadding;
// Corners of the shadow quad, in shadow-space
var corners = new float[][]
{
new float[] {stl.X, stl.Y, 0, 1},
new float[] {sbr.X, sbr.Y, 0, 1},
new float[] {sbr.X, stl.Y, 0, 1},
new float[] {stl.X, sbr.Y, 0, 1}
};
var shadowScreenTransform = Util.MatrixMultiply(cameraTransform, invShadowTransform);
var shadowGroundNormal = Util.MatrixVectorMultiply(shadowTransform, groundNormal);
var screenCorners = new float2[4];
for (var j = 0; j < 4; j++)
{
// Project to ground plane
corners[j][2] = -(corners[j][1]*shadowGroundNormal[1]/shadowGroundNormal[2] +
corners[j][0]*shadowGroundNormal[0]/shadowGroundNormal[2]);
// Rotate to camera-space
corners[j] = Util.MatrixVectorMultiply(shadowScreenTransform, corners[j]);
screenCorners[j] = new float2(corners[j][0], corners[j][1]);
}
// Shadows are rendered at twice the resolution to reduce artefacts
Size spriteSize, shadowSpriteSize;
int2 spriteOffset, shadowSpriteOffset;
CalculateSpriteGeometry(tl, br, 1, out spriteSize, out spriteOffset);
CalculateSpriteGeometry(stl, sbr, 2, out shadowSpriteSize, out shadowSpriteOffset);
var sprite = sheetBuilder.Allocate(spriteSize, spriteOffset);
var shadowSprite = sheetBuilder.Allocate(shadowSpriteSize, shadowSpriteOffset);
var sb = sprite.bounds;
var ssb = shadowSprite.bounds;
var spriteCenter = new float2(sb.Left + sb.Width / 2, sb.Top + sb.Height / 2);
var shadowCenter = new float2(ssb.Left + ssb.Width / 2, ssb.Top + ssb.Height / 2);
var translateMtx = Util.TranslationMatrix(spriteCenter.X - spriteOffset.X, Renderer.SheetSize - (spriteCenter.Y - spriteOffset.Y), 0);
var shadowTranslateMtx = Util.TranslationMatrix(shadowCenter.X - shadowSpriteOffset.X, Renderer.SheetSize - (shadowCenter.Y - shadowSpriteOffset.Y), 0);
var correctionTransform = Util.MatrixMultiply(translateMtx, flipMtx);
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, shadowScaleFlipMtx);
doRender.Add(Pair.New<Sheet, Action>(sprite.sheet, () =>
{
foreach (var v in voxels)
{
// Convert screen offset to world offset
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(v.OffsetFunc()));
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
var rotations = v.RotationFunc().Aggregate(Util.IdentityMatrix(),
(x,y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
var worldTransform = Util.MatrixMultiply(scaleTransform, rotations);
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
var transform = Util.MatrixMultiply(cameraTransform, worldTransform);
transform = Util.MatrixMultiply(correctionTransform, transform);
var shadow = Util.MatrixMultiply(shadowTransform, worldTransform);
shadow = Util.MatrixMultiply(shadowCorrectionTransform, shadow);
var lightTransform = Util.MatrixMultiply(Util.MatrixInverse(rotations), invShadowTransform);
var frame = v.FrameFunc();
for (uint i = 0; i < v.Voxel.Limbs; i++)
{
var rd = v.Voxel.RenderData(i);
var t = v.Voxel.TransformationMatrix(i, frame);
// Transform light vector from shadow -> world -> limb coords
var lightDirection = ExtractRotationVector(Util.MatrixMultiply(Util.MatrixInverse(t), lightTransform));
Render(rd, Util.MatrixMultiply(transform, t), lightDirection,
lightAmbientColor, lightDiffuseColor, color.Index, normals.Index);
// Disable shadow normals by forcing zero diffuse and identity ambient light
Render(rd, Util.MatrixMultiply(shadow, t), lightDirection,
shadowAmbient, shadowDiffuse, shadowPalette.Index, normals.Index);
}
}
}));
var screenLightVector = Util.MatrixVectorMultiply(invShadowTransform, zVector);
screenLightVector = Util.MatrixVectorMultiply(cameraTransform, screenLightVector);
return new VoxelRenderProxy(sprite, shadowSprite, screenCorners, -screenLightVector[2]/screenLightVector[1]);
}
static void CalculateSpriteGeometry(float2 tl, float2 br, float scale, out Size size, out int2 offset)
{
var width = (int)(scale*(br.X - tl.X));
var height = (int)(scale*(br.Y - tl.Y));
offset = (0.5f*scale*(br + tl)).ToInt2();
// Width and height must be even to avoid rendering glitches
if ((width & 1) == 1)
width += 1;
if ((height & 1) == 1)
height += 1;
size = new Size(width, height);
}
static float[] ExtractRotationVector(float[] mtx)
{
var tVec = Util.MatrixVectorMultiply(mtx, zVector);
var tOrigin = Util.MatrixVectorMultiply(mtx, zeroVector);
tVec[0] -= tOrigin[0]*tVec[3]/tOrigin[3];
tVec[1] -= tOrigin[1]*tVec[3]/tOrigin[3];
tVec[2] -= tOrigin[2]*tVec[3]/tOrigin[3];
// Renormalize
var w = (float)Math.Sqrt(tVec[0]*tVec[0] + tVec[1]*tVec[1] + tVec[2]*tVec[2]);
tVec[0] /= w;
tVec[1] /= w;
tVec[2] /= w;
tVec[3] = 1f;
return tVec;
}
void Render(VoxelRenderData renderData,
float[] t, float[] lightDirection,
float[] ambientLight, float[] diffuseLight,
int colorPalette, int normalsPalette)
{
shader.SetTexture("DiffuseTexture", renderData.Sheet.Texture);
shader.SetVec("PaletteRows", (colorPalette + 0.5f) / HardwarePalette.MaxPalettes,
(normalsPalette + 0.5f) / HardwarePalette.MaxPalettes);
shader.SetMatrix("TransformMatrix", t);
shader.SetVec("LightDirection", lightDirection, 4);
shader.SetVec("AmbientLight", ambientLight, 3);
shader.SetVec("DiffuseLight", diffuseLight, 3);
shader.Render(() => renderer.DrawBatch(Game.modData.VoxelLoader.VertexBuffer, renderData.Start, renderData.Count, PrimitiveType.QuadList));
}
public void BeginFrame()
{
foreach (var kv in mappedBuffers)
unmappedBuffers.Push(kv);
mappedBuffers.Clear();
sheetBuilder = new SheetBuilder(SheetType.BGRA, AllocateSheet);
doRender.Clear();
}
IFrameBuffer EnableFrameBuffer(Sheet s)
{
var fbo = mappedBuffers[s];
Game.Renderer.Flush();
fbo.Bind();
Game.Renderer.Device.EnableDepthBuffer();
return fbo;
}
void DisableFrameBuffer(IFrameBuffer fbo)
{
Game.Renderer.Flush();
Game.Renderer.Device.DisableDepthBuffer();
fbo.Unbind();
}
public void EndFrame()
{
if (doRender.Count == 0)
return;
Sheet currentSheet = null;
IFrameBuffer fbo = null;
foreach (var v in doRender)
{
// Change sheet
if (v.First != currentSheet)
{
if (fbo != null)
DisableFrameBuffer(fbo);
currentSheet = v.First;
fbo = EnableFrameBuffer(currentSheet);
}
v.Second();
}
DisableFrameBuffer(fbo);
}
public Sheet AllocateSheet()
{
// Reuse cached fbo
if (unmappedBuffers.Count > 0)
{
var kv = unmappedBuffers.Pop();
mappedBuffers.Add(kv.Key, kv.Value);
return kv.Key;
}
var size = new Size(Renderer.SheetSize, Renderer.SheetSize);
var framebuffer = renderer.Device.CreateFrameBuffer(size);
var sheet = new Sheet(framebuffer.Texture);
mappedBuffers.Add(sheet, framebuffer);
return sheet;
}
}
}

View File

@@ -37,6 +37,7 @@ namespace OpenRA.Graphics
internal readonly ShroudRenderer shroudRenderer;
internal readonly HardwarePalette palette;
internal Cache<string, PaletteReference> palettes;
Lazy<DeveloperMode> devTrait;
internal WorldRenderer(World world)
{
@@ -51,6 +52,8 @@ namespace OpenRA.Graphics
terrainRenderer = new TerrainRenderer(world, this);
shroudRenderer = new ShroudRenderer(world);
devTrait = Lazy.New(() => world.LocalPlayer != null ? world.LocalPlayer.PlayerActor.Trait<DeveloperMode>() : null);
}
PaletteReference CreatePaletteReference(string name)
@@ -65,30 +68,32 @@ namespace OpenRA.Graphics
public PaletteReference Palette(string name) { return palettes[name]; }
public void AddPalette(string name, Palette pal, bool allowModifiers) { palette.AddPalette(name, pal, allowModifiers); }
class SpriteComparer : IComparer<Renderable>
{
public int Compare(Renderable x, Renderable y)
{
return (x.Z + x.ZOffset).CompareTo(y.Z + y.ZOffset);
}
}
IEnumerable<Renderable> SpritesToRender()
List<IRenderable> GenerateRenderables()
{
var bounds = Game.viewport.WorldBounds(world);
var comparer = new SpriteComparer();
var comparer = new RenderableComparer(this);
var actors = world.FindUnits(
bounds.TopLeftAsCPos().ToPPos(),
bounds.BottomRightAsCPos().ToPPos()
);
bounds.BottomRightAsCPos().ToPPos());
var renderables = actors.SelectMany(a => a.Render(this))
var worldRenderables = actors.SelectMany(a => a.Render(this))
.OrderBy(r => r, comparer);
var effects = world.Effects.SelectMany(e => e.Render(this));
// Effects are drawn on top of all actors
// TODO: Allow effects to be interleaved with actors
var effectRenderables = world.Effects
.SelectMany(e => e.Render(this));
return renderables.Concat(effects);
// Iterating via foreach() copies the structs, so enumerate by index
var renderables = worldRenderables.Concat(effectRenderables).ToList();
Game.Renderer.WorldVoxelRenderer.BeginFrame();
for (var i = 0; i < renderables.Count; i++)
renderables[i].BeforeRender(this);
Game.Renderer.WorldVoxelRenderer.EndFrame();
return renderables;
}
public void Draw()
@@ -98,13 +103,14 @@ namespace OpenRA.Graphics
if (world.IsShellmap && !Game.Settings.Game.ShowShellmap)
return;
var renderables = GenerateRenderables();
var bounds = Game.viewport.ViewBounds(world);
Game.Renderer.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
terrainRenderer.Draw(this, Game.viewport);
foreach (var a in world.traitDict.ActorsWithTraitMultiple<IRenderAsTerrain>(world))
foreach (var r in a.Trait.RenderAsTerrain(this, a.Actor))
r.Sprite.DrawAt(r.Pos, r.Palette.Index, r.Scale);
r.Render(this);
foreach (var a in world.Selection.Actors)
if (!a.Destroyed)
@@ -116,8 +122,8 @@ namespace OpenRA.Graphics
if (world.OrderGenerator != null)
world.OrderGenerator.RenderBeforeWorld(this, world);
foreach (var image in SpritesToRender())
image.Sprite.DrawAt(image.Pos, image.Palette.Index, image.Scale);
for (var i = 0; i < renderables.Count; i++)
renderables[i].Render(this);
// added for contrails
foreach (var a in world.ActorsWithTrait<IPostRender>())
@@ -129,6 +135,11 @@ namespace OpenRA.Graphics
var renderShroud = world.RenderPlayer != null ? world.RenderPlayer.Shroud : null;
shroudRenderer.Draw(this, renderShroud);
if (devTrait.Value != null && devTrait.Value.ShowDebugGeometry)
for (var i = 0; i < renderables.Count; i++)
renderables[i].RenderDebugGeometry(this);
Game.Renderer.DisableScissor();
foreach (var g in world.Selection.Actors.Where(a => !a.Destroyed)
@@ -222,19 +233,18 @@ namespace OpenRA.Graphics
public int2 ScreenPxPosition(WPos pos)
{
return new int2(Game.CellSize*pos.X/1024, Game.CellSize*(pos.Y - pos.Z)/1024);
}
public float ScreenZOffset(WPos pos) { return pos.Z*Game.CellSize/1024f; }
public int2 ScreenPxOffset(WVec vec)
{
return new int2(Game.CellSize*vec.X/1024, Game.CellSize*(vec.Y - vec.Z)/1024);
// Round to nearest pixel
var px = ScreenPosition(pos);
return new int2((int)Math.Round(px.X), (int)Math.Round(px.Y));
}
public float[] ScreenOffset(WVec vec)
// For scaling vectors to pixel sizes in the voxel renderer
public float[] ScreenVector(WVec vec)
{
var c = Game.CellSize/1024f;
return new float[] {c*vec.X, c*vec.Y, c*vec.Z};
return new float[] {c*vec.X, c*vec.Y, c*vec.Z, 1};
}
public float ScreenZPosition(WPos pos, int zOffset) { return (pos.Y + pos.Z + zOffset)*Game.CellSize/1024f; }
}
}

View File

@@ -59,6 +59,7 @@ namespace OpenRA
[FieldLoader.Ignore] public List<MiniYamlNode> Rules = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Sequences = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> VoxelSequences = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Weapons = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Voices = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Notifications = new List<MiniYamlNode>();
@@ -104,7 +105,7 @@ namespace OpenRA
public Map(string path)
{
Path = path;
Container = FileSystem.OpenPackage(path, int.MaxValue);
Container = FileSystem.OpenPackage(path, null, int.MaxValue);
AssertExists("map.yaml");
AssertExists("map.bin");
@@ -150,6 +151,7 @@ namespace OpenRA
Rules = NodesOrEmpty(yaml, "Rules");
Sequences = NodesOrEmpty(yaml, "Sequences");
VoxelSequences = NodesOrEmpty(yaml, "VoxelSequences");
Weapons = NodesOrEmpty(yaml, "Weapons");
Voices = NodesOrEmpty(yaml, "Voices");
Notifications = NodesOrEmpty(yaml, "Notifications");
@@ -206,6 +208,7 @@ namespace OpenRA
root.Add(new MiniYamlNode("Smudges", MiniYaml.FromList<SmudgeReference>( Smudges.Value )));
root.Add(new MiniYamlNode("Rules", null, Rules));
root.Add(new MiniYamlNode("Sequences", null, Sequences));
root.Add(new MiniYamlNode("VoxelSequences", null, VoxelSequences));
root.Add(new MiniYamlNode("Weapons", null, Weapons));
root.Add(new MiniYamlNode("Voices", null, Voices));
root.Add(new MiniYamlNode("Notifications", null, Notifications));
@@ -229,44 +232,27 @@ namespace OpenRA
Container.Write(entries);
}
static byte ReadByte(Stream s)
{
int ret = s.ReadByte();
if (ret == -1)
throw new NotImplementedException();
return (byte)ret;
}
static ushort ReadWord(Stream s)
{
ushort ret = ReadByte(s);
ret |= (ushort)(ReadByte(s) << 8);
return ret;
}
public TileReference<ushort, byte>[,] LoadMapTiles()
{
var tiles = new TileReference<ushort, byte>[MapSize.X, MapSize.Y];
using (var dataStream = Container.GetContent("map.bin"))
{
if (ReadByte(dataStream) != 1)
if (dataStream.ReadUInt8() != 1)
throw new InvalidDataException("Unknown binary map format");
// Load header info
var width = ReadWord(dataStream);
var height = ReadWord(dataStream);
var width = dataStream.ReadUInt16();
var height = dataStream.ReadUInt16();
if (width != MapSize.X || height != MapSize.Y)
throw new InvalidDataException("Invalid tile data");
// Load tile data
for (int i = 0; i < MapSize.X; i++)
for (int j = 0; j < MapSize.Y; j++)
{
ushort tile = ReadWord(dataStream);
byte index = ReadByte(dataStream);
var tile = dataStream.ReadUInt16();
var index = dataStream.ReadUInt8();
if (index == byte.MaxValue)
index = (byte)(i % 4 + (j % 4) * 4);
@@ -282,26 +268,25 @@ namespace OpenRA
using (var dataStream = Container.GetContent("map.bin"))
{
if (ReadByte(dataStream) != 1)
if (dataStream.ReadUInt8() != 1)
throw new InvalidDataException("Unknown binary map format");
// Load header info
var width = ReadWord(dataStream);
var height = ReadWord(dataStream);
var width = dataStream.ReadUInt16();
var height = dataStream.ReadUInt16();
if (width != MapSize.X || height != MapSize.Y)
throw new InvalidDataException("Invalid tile data");
// Skip past tile data
for (var i = 0; i < 3*MapSize.X*MapSize.Y; i++)
ReadByte(dataStream);
dataStream.Seek(3*MapSize.X*MapSize.Y, SeekOrigin.Current);
// Load resource data
for (var i = 0; i < MapSize.X; i++)
for (var j = 0; j < MapSize.Y; j++)
{
byte type = ReadByte(dataStream);
byte index = ReadByte(dataStream);
var type = dataStream.ReadUInt8();
var index = dataStream.ReadUInt8();
resources[i, j] = new TileReference<byte, byte>(type, index);
}
}

View File

@@ -28,33 +28,35 @@ namespace OpenRA
public ILoadScreen LoadScreen = null;
public SheetBuilder SheetBuilder;
public SpriteLoader SpriteLoader;
public VoxelLoader VoxelLoader;
public ModData( params string[] mods )
public ModData(params string[] mods)
{
Manifest = new Manifest( mods );
ObjectCreator = new ObjectCreator( Manifest );
Manifest = new Manifest(mods);
ObjectCreator = new ObjectCreator(Manifest);
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen.Value);
LoadScreen.Init(Manifest.LoadScreen.NodesDict.ToDictionary(x => x.Key, x => x.Value.Value));
LoadScreen.Display();
WidgetLoader = new WidgetLoader( this );
}
WidgetLoader = new WidgetLoader(this);
public void LoadInitialAssets(bool enumMaps)
{
// all this manipulation of static crap here is nasty and breaks
// horribly when you use ModData in unexpected ways.
AvailableMaps = FindMaps(Manifest.Mods);
// HACK: Mount only local folders so we have a half-working environment for the asset installer
FileSystem.UnmountAll();
foreach (var dir in Manifest.Folders)
FileSystem.Mount(dir);
if (enumMaps)
AvailableMaps = FindMaps(Manifest.Mods);
}
public void InitializeLoaders()
{
// all this manipulation of static crap here is nasty and breaks
// horribly when you use ModData in unexpected ways.
ChromeMetrics.Initialize(Manifest.ChromeMetrics);
ChromeProvider.Initialize(Manifest.Chrome);
SheetBuilder = new SheetBuilder(TextureChannel.Red);
SheetBuilder = new SheetBuilder(SheetType.Indexed);
SpriteLoader = new SpriteLoader(new string[] { ".shp" }, SheetBuilder);
VoxelLoader = new VoxelLoader();
CursorProvider.Initialize(Manifest.Cursors);
}
@@ -66,18 +68,17 @@ namespace OpenRA
var map = new Map(AvailableMaps[uid].Path);
// Reinit all our assets
LoadInitialAssets(false);
foreach (var pkg in Manifest.Packages)
FileSystem.Mount(pkg);
InitializeLoaders();
FileSystem.LoadFromManifest(Manifest);
// Mount map package so custom assets can be used. TODO: check priority.
FileSystem.Mount(FileSystem.OpenPackage(map.Path, int.MaxValue));
FileSystem.Mount(FileSystem.OpenPackage(map.Path, null, int.MaxValue));
Rules.LoadRules(Manifest, map);
SpriteLoader = new SpriteLoader(Rules.TileSets[map.Tileset].Extensions, SheetBuilder);
// TODO: Don't load the sequences for assets that are not used in this tileset. Maybe use the existing EditorTilesetFilters.
SequenceProvider.Initialize(Manifest.Sequences, map.Sequences);
VoxelProvider.Initialize(Manifest.VoxelSequences, map.VoxelSequences);
return map;
}

View File

@@ -30,10 +30,10 @@ namespace OpenRA.Network
{
int LocalClientId { get; }
ConnectionState ConnectionState { get; }
void Send( int frame, List<byte[]> orders );
void SendImmediate( List<byte[]> orders );
void SendSync( int frame, byte[] syncData );
void Receive( Action<int, byte[]> packetFn );
void Send(int frame, List<byte[]> orders);
void SendImmediate(List<byte[]> orders);
void SendSync(int frame, byte[] syncData);
void Receive(Action<int, byte[]> packetFn);
}
class EchoConnection : IConnection
@@ -55,51 +55,51 @@ namespace OpenRA.Network
get { return ConnectionState.PreConnecting; }
}
public virtual void Send( int frame, List<byte[]> orders )
public virtual void Send(int frame, List<byte[]> orders)
{
var ms = new MemoryStream();
ms.Write( BitConverter.GetBytes( frame ) );
foreach( var o in orders )
ms.Write( o );
Send( ms.ToArray() );
ms.Write(BitConverter.GetBytes(frame));
foreach (var o in orders)
ms.Write(o);
Send(ms.ToArray());
}
public virtual void SendImmediate( List<byte[]> orders )
public virtual void SendImmediate(List<byte[]> orders)
{
var ms = new MemoryStream();
ms.Write( BitConverter.GetBytes( (int)0 ) );
foreach( var o in orders )
ms.Write( o );
Send( ms.ToArray() );
ms.Write(BitConverter.GetBytes((int)0));
foreach (var o in orders)
ms.Write(o);
Send(ms.ToArray());
}
public virtual void SendSync( int frame, byte[] syncData )
public virtual void SendSync(int frame, byte[] syncData)
{
var ms = new MemoryStream();
ms.Write( BitConverter.GetBytes( frame ) );
ms.Write( syncData );
Send( ms.ToArray() );
ms.Write(BitConverter.GetBytes(frame));
ms.Write(syncData);
Send(ms.ToArray());
}
protected virtual void Send( byte[] packet )
protected virtual void Send(byte[] packet)
{
if( packet.Length == 0 )
if (packet.Length == 0)
throw new NotImplementedException();
lock( this )
receivedPackets.Add( new ReceivedPacket { FromClient = LocalClientId, Data = packet } );
lock (this)
receivedPackets.Add(new ReceivedPacket { FromClient = LocalClientId, Data = packet } );
}
public virtual void Receive( Action<int, byte[]> packetFn )
public virtual void Receive(Action<int, byte[]> packetFn)
{
List<ReceivedPacket> packets;
lock( this )
lock (this)
{
packets = receivedPackets;
receivedPackets = new List<ReceivedPacket>();
}
foreach( var p in packets )
packetFn( p.FromClient, p.Data );
foreach (var p in packets)
packetFn(p.FromClient, p.Data);
}
public virtual void Dispose() { }
@@ -112,15 +112,15 @@ namespace OpenRA.Network
ConnectionState connectionState = ConnectionState.Connecting;
Thread t;
public NetworkConnection( string host, int port )
public NetworkConnection(string host, int port)
{
t = new Thread( _ =>
{
try
{
socket = new TcpClient( host, port );
socket = new TcpClient(host, port);
socket.NoDelay = true;
var reader = new BinaryReader( socket.GetStream() );
var reader = new BinaryReader(socket.GetStream());
var serverProtocol = reader.ReadInt32();
if (ProtocolVersion.Version != serverProtocol)
@@ -131,22 +131,22 @@ namespace OpenRA.Network
clientId = reader.ReadInt32();
connectionState = ConnectionState.Connected;
for( ; ; )
for (;;)
{
var len = reader.ReadInt32();
var client = reader.ReadInt32();
var buf = reader.ReadBytes( len );
if( len == 0 )
if (len == 0)
throw new NotImplementedException();
lock( this )
receivedPackets.Add( new ReceivedPacket { FromClient = client, Data = buf } );
lock (this)
receivedPackets.Add(new ReceivedPacket { FromClient = client, Data = buf } );
}
}
catch { }
finally
{
connectionState = ConnectionState.NotConnected;
if( socket != null )
if (socket != null)
socket.Close();
}
}
@@ -159,28 +159,28 @@ namespace OpenRA.Network
List<byte[]> queuedSyncPackets = new List<byte[]>();
public override void SendSync( int frame, byte[] syncData )
public override void SendSync(int frame, byte[] syncData)
{
var ms = new MemoryStream();
ms.Write( BitConverter.GetBytes( frame ) );
ms.Write( syncData );
queuedSyncPackets.Add( ms.ToArray() );
ms.Write(BitConverter.GetBytes(frame));
ms.Write(syncData);
queuedSyncPackets.Add(ms.ToArray());
}
protected override void Send( byte[] packet )
protected override void Send(byte[] packet)
{
base.Send( packet );
base.Send(packet);
try
{
var ms = new MemoryStream();
ms.Write(BitConverter.GetBytes((int)packet.Length));
ms.Write(packet);
foreach( var q in queuedSyncPackets )
foreach (var q in queuedSyncPackets)
{
ms.Write( BitConverter.GetBytes( (int)q.Length ) );
ms.Write( q );
base.Send( q );
ms.Write(BitConverter.GetBytes((int)q.Length));
ms.Write(q);
base.Send(q);
}
queuedSyncPackets.Clear();
ms.WriteTo(socket.GetStream());
@@ -192,16 +192,16 @@ namespace OpenRA.Network
bool disposed = false;
public override void Dispose ()
public override void Dispose()
{
if (disposed) return;
disposed = true;
GC.SuppressFinalize( this );
GC.SuppressFinalize(this);
t.Abort();
if (socket != null)
socket.Client.Close();
using( new PerfSample( "Thread.Join" ))
using (new PerfSample("Thread.Join"))
{
if (!t.Join(1000))
return;

View File

@@ -168,16 +168,16 @@ namespace OpenRA
return a.ActorID;
}
static bool TryGetActorFromUInt(World world, uint aID, out Actor ret )
static bool TryGetActorFromUInt(World world, uint aID, out Actor ret)
{
if( aID == 0xFFFFFFFF )
if (aID == 0xFFFFFFFF)
{
ret = null;
return true;
}
else
{
foreach( var a in world.Actors.Where( x => x.ActorID == aID ) )
foreach (var a in world.Actors.Where(x => x.ActorID == aID))
{
ret = a;
return true;

View File

@@ -21,13 +21,14 @@ namespace OpenRA.Network
readonly SyncReport syncReport;
readonly FrameData frameData = new FrameData();
public Session LobbyInfo = new Session( Game.Settings.Game.Mods );
public Session.Client LocalClient { get { return LobbyInfo.ClientWithIndex( Connection.LocalClientId ); } }
public Session LobbyInfo = new Session(Game.Settings.Game.Mods);
public Session.Client LocalClient { get { return LobbyInfo.ClientWithIndex(Connection.LocalClientId); } }
public World world;
public readonly string Host;
public readonly int Port;
public string ServerError;
public string ServerError = "Server is not responding.";
public int NetFrameNumber { get; private set; }
public int LocalFrameNumber;
@@ -47,27 +48,27 @@ namespace OpenRA.Network
if (GameStarted) return;
NetFrameNumber = 1;
for( int i = NetFrameNumber ; i <= FramesAhead ; i++ )
Connection.Send( i, new List<byte[]>() );
for (var i = NetFrameNumber ; i <= FramesAhead ; i++)
Connection.Send(i, new List<byte[]>());
}
public OrderManager( string host, int port, IConnection conn )
public OrderManager(string host, int port, IConnection conn)
{
this.Host = host;
this.Port = port;
Connection = conn;
syncReport = new SyncReport( this );
syncReport = new SyncReport(this);
}
public void IssueOrders( Order[] orders )
public void IssueOrders(Order[] orders)
{
foreach( var order in orders )
IssueOrder( order );
foreach (var order in orders)
IssueOrder(order);
}
public void IssueOrder( Order order )
public void IssueOrder(Order order)
{
localOrders.Add( order );
localOrders.Add(order);
}
public void TickImmediate()
@@ -82,25 +83,25 @@ namespace OpenRA.Network
Connection.Receive(
( clientId, packet ) =>
{
var frame = BitConverter.ToInt32( packet, 0 );
if( packet.Length == 5 && packet[ 4 ] == 0xBF )
frameData.ClientQuit( clientId, frame );
else if( packet.Length >= 5 && packet[ 4 ] == 0x65 )
CheckSync( packet );
else if( frame == 0 )
immediatePackets.Add( Pair.New( clientId, packet ) );
var frame = BitConverter.ToInt32(packet, 0);
if (packet.Length == 5 && packet[4] == 0xBF)
frameData.ClientQuit(clientId, frame);
else if (packet.Length >= 5 && packet[4] == 0x65)
CheckSync(packet);
else if (frame == 0)
immediatePackets.Add(Pair.New(clientId, packet));
else
frameData.AddFrameOrders( clientId, frame, packet );
frameData.AddFrameOrders(clientId, frame, packet);
} );
foreach( var p in immediatePackets )
foreach( var o in p.Second.ToOrderList( world ) )
UnitOrders.ProcessOrder( this, world, p.First, o );
foreach (var p in immediatePackets)
foreach (var o in p.Second.ToOrderList(world))
UnitOrders.ProcessOrder(this, world, p.First, o);
}
Dictionary<int, byte[]> syncForFrame = new Dictionary<int, byte[]>();
void CheckSync( byte[] packet )
void CheckSync(byte[] packet)
{
var frame = BitConverter.ToInt32(packet, 0);
byte[] existingSync;
@@ -133,7 +134,7 @@ namespace OpenRA.Network
void OutOfSync(int frame, int index)
{
var orders = frameData.OrdersForFrame( world, frame );
var orders = frameData.OrdersForFrame(world, frame);
// Invalid index
if (index >= orders.Count())
@@ -154,7 +155,7 @@ namespace OpenRA.Network
public bool IsReadyForNextFrame
{
get { return NetFrameNumber >= 1 && frameData.IsReadyForFrame( NetFrameNumber ); }
get { return NetFrameNumber >= 1 && frameData.IsReadyForFrame(NetFrameNumber); }
}
static readonly IEnumerable<Session.Client> NoClients = new Session.Client[] {};
@@ -171,23 +172,23 @@ namespace OpenRA.Network
public void Tick()
{
if( !IsReadyForNextFrame )
if (!IsReadyForNextFrame)
throw new InvalidOperationException();
Connection.Send( NetFrameNumber + FramesAhead, localOrders.Select( o => o.Serialize() ).ToList() );
Connection.Send(NetFrameNumber + FramesAhead, localOrders.Select(o => o.Serialize()).ToList());
localOrders.Clear();
var sync = new List<int>();
sync.Add( world.SyncHash() );
sync.Add(world.SyncHash());
foreach( var order in frameData.OrdersForFrame( world, NetFrameNumber) )
foreach (var order in frameData.OrdersForFrame(world, NetFrameNumber))
{
UnitOrders.ProcessOrder( this, world, order.Client, order.Order );
sync.Add( world.SyncHash() );
UnitOrders.ProcessOrder(this, world, order.Client, order.Order);
sync.Add(world.SyncHash());
}
var ss = sync.SerializeSync();
Connection.SendSync( NetFrameNumber, ss );
Connection.SendSync(NetFrameNumber, ss);
syncReport.UpdateSyncReport();

View File

@@ -84,7 +84,6 @@ namespace OpenRA.Network
{
public string ServerName;
public string Map;
public string[] Ban;
public string[] Mods = { "ra" }; // mod names
public int OrderLatency = 3; // net tick frames (x 120 = ms)
public int RandomSeed = 0;

View File

@@ -154,8 +154,10 @@ namespace OpenRA.Network
}
case "ServerError":
orderManager.ServerError = order.TargetString;
break;
{
orderManager.ServerError = order.TargetString;
break;
}
case "SyncInfo":
{
@@ -200,13 +202,13 @@ namespace OpenRA.Network
}
default:
{
if( !order.IsImmediate )
if (!order.IsImmediate)
{
var self = order.Subject;
var health = self.TraitOrDefault<Health>();
if( health == null || !health.IsDead )
foreach( var t in self.TraitsImplementing<IResolveOrder>() )
t.ResolveOrder( self, order );
if (health == null || !health.IsDead)
foreach (var t in self.TraitsImplementing<IResolveOrder>())
t.ResolveOrder(self, order);
}
break;
}

View File

@@ -224,6 +224,18 @@
<Compile Include="Traits\DebugPauseState.cs" />
<Compile Include="Network\UPnP.cs" />
<Compile Include="Widgets\ClientTooltipRegionWidget.cs" />
<Compile Include="Graphics\Renderable.cs" />
<Compile Include="Traits\Render\RenderSprites.cs" />
<Compile Include="Graphics\Voxel.cs" />
<Compile Include="Graphics\VoxelRenderer.cs" />
<Compile Include="Graphics\VoxelLoader.cs" />
<Compile Include="Graphics\VoxelProvider.cs" />
<Compile Include="Traits\BodyOrientation.cs" />
<Compile Include="Graphics\VoxelAnimation.cs" />
<Compile Include="Graphics\VoxelRenderable.cs" />
<Compile Include="Graphics\TextRenderable.cs" />
<Compile Include="Graphics\BeamRenderable.cs" />
<Compile Include="Graphics\ContrailRenderable.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -87,13 +87,19 @@ namespace OpenRA.Orders
{
var actorsAt = self.World.ActorMap.GetUnitsAt(xy).ToList();
var forceAttack = mi.Modifiers.HasModifier(Modifiers.Ctrl);
var forceQueue = mi.Modifiers.HasModifier(Modifiers.Shift);
var modifiers = TargetModifiers.None;
if (mi.Modifiers.HasModifier(Modifiers.Ctrl))
modifiers |= TargetModifiers.ForceAttack;
if (mi.Modifiers.HasModifier(Modifiers.Shift))
modifiers |= TargetModifiers.ForceQueue;
if (mi.Modifiers.HasModifier(Modifiers.Alt))
modifiers |= TargetModifiers.ForceMove;
string cursor = null;
if (underCursor != null)
if (o.Order.CanTargetActor(self, underCursor, forceAttack, forceQueue, ref cursor))
if (o.Order.CanTargetActor(self, underCursor, modifiers, ref cursor))
return new UnitOrderResult(self, o.Order, o.Trait, cursor, Target.FromActor(underCursor));
if (o.Order.CanTargetLocation(self, xy, actorsAt, forceAttack, forceQueue, ref cursor))
if (o.Order.CanTargetLocation(self, xy, actorsAt, modifiers, ref cursor))
return new UnitOrderResult(self, o.Order, o.Trait, cursor, Target.FromCell(xy));
}
}

View File

@@ -36,7 +36,7 @@ namespace OpenRA.Server
return result.ToArray();
}
bool ReadDataInner( Server server )
bool ReadDataInner(Server server)
{
var rx = new byte[1024];
var len = 0;
@@ -65,6 +65,7 @@ namespace OpenRA.Server
if (e.SocketErrorCode == SocketError.WouldBlock) break;
server.DropClient(this);
Log.Write("server", "Dropping client {0} because reading the data failed: {1}", this.PlayerIndex.ToString(), e);
return false;
}
}
@@ -72,7 +73,7 @@ namespace OpenRA.Server
return true;
}
public void ReadData( Server server )
public void ReadData(Server server)
{
if (ReadDataInner(server))
while (data.Count >= ExpectLength)

View File

@@ -66,6 +66,8 @@ namespace OpenRA.Server
protected set { pState = value; }
}
public List<string> TempBans = new List<string>();
public void Shutdown()
{
State = ServerState.ShuttingDown;
@@ -97,20 +99,19 @@ namespace OpenRA.Server
UPnP.ForwardPort();
foreach (var trait in modData.Manifest.ServerTraits)
ServerTraits.Add( modData.ObjectCreator.CreateObject<ServerTrait>(trait) );
ServerTraits.Add(modData.ObjectCreator.CreateObject<ServerTrait>(trait));
lobbyInfo = new Session( mods );
lobbyInfo = new Session(mods);
lobbyInfo.GlobalSettings.RandomSeed = randomSeed;
lobbyInfo.GlobalSettings.Map = settings.Map;
lobbyInfo.GlobalSettings.ServerName = settings.Name;
lobbyInfo.GlobalSettings.Ban = settings.Ban;
lobbyInfo.GlobalSettings.Dedicated = settings.Dedicated;
foreach (var t in ServerTraits.WithInterface<INotifyServerStart>())
t.ServerStarted(this);
Log.Write("server", "Initial mods: ");
foreach( var m in lobbyInfo.GlobalSettings.Mods )
foreach (var m in lobbyInfo.GlobalSettings.Mods)
Log.Write("server","- {0}", m);
Log.Write("server", "Initial map: {0}",lobbyInfo.GlobalSettings.Map);
@@ -118,31 +119,31 @@ namespace OpenRA.Server
new Thread( _ =>
{
var timeout = ServerTraits.WithInterface<ITick>().Min(t => t.TickTimeout);
for( ; ; )
for (;;)
{
var checkRead = new List<Socket>();
checkRead.Add( listener.Server );
foreach( var c in conns ) checkRead.Add( c.socket );
foreach( var c in preConns ) checkRead.Add( c.socket );
checkRead.Add(listener.Server);
foreach (var c in conns) checkRead.Add(c.socket);
foreach (var c in preConns) checkRead.Add(c.socket);
Socket.Select( checkRead, null, null, timeout );
Socket.Select(checkRead, null, null, timeout);
if (State == ServerState.ShuttingDown)
{
EndGame();
break;
}
foreach( var s in checkRead )
if( s == listener.Server ) AcceptConnection();
foreach (var s in checkRead)
if (s == listener.Server) AcceptConnection();
else if (preConns.Count > 0)
{
var p = preConns.SingleOrDefault( c => c.socket == s );
if (p != null) p.ReadData( this );
var p = preConns.SingleOrDefault(c => c.socket == s);
if (p != null) p.ReadData(this);
}
else if (conns.Count > 0)
{
var conn = conns.SingleOrDefault( c => c.socket == s );
if (conn != null) conn.ReadData( this );
var conn = conns.SingleOrDefault(c => c.socket == s);
if (conn != null) conn.ReadData(this);
}
foreach (var t in ServerTraits.WithInterface<ITick>())
@@ -151,8 +152,7 @@ namespace OpenRA.Server
if (State == ServerState.ShuttingDown)
{
EndGame();
if (Settings.AllowPortForward)
UPnP.RemovePortforward();
if (Settings.AllowPortForward) UPnP.RemovePortforward();
break;
}
}
@@ -187,10 +187,11 @@ namespace OpenRA.Server
if (!listener.Server.IsBound) return;
newSocket = listener.AcceptSocket();
}
catch
catch (Exception e)
{
/* could have an exception here when listener 'goes away' when calling AcceptConnection! */
/* alternative would be to use locking but the listener doesnt go away without a reason */
/* TODO: Could have an exception here when listener 'goes away' when calling AcceptConnection! */
/* Alternative would be to use locking but the listener doesnt go away without a reason. */
Log.Write("server", "Accepting the connection failed.", e);
return;
}
@@ -202,19 +203,23 @@ namespace OpenRA.Server
// assign the player number.
newConn.PlayerIndex = ChooseFreePlayerIndex();
newConn.socket.Send(BitConverter.GetBytes(ProtocolVersion.Version));
newConn.socket.Send(BitConverter.GetBytes(newConn.PlayerIndex));
SendData(newConn.socket, BitConverter.GetBytes(ProtocolVersion.Version));
SendData(newConn.socket, BitConverter.GetBytes(newConn.PlayerIndex));
preConns.Add(newConn);
// Dispatch a handshake order
var request = new HandshakeRequest()
{
Map = lobbyInfo.GlobalSettings.Map,
Mods = lobbyInfo.GlobalSettings.Mods.Select(m => "{0}@{1}".F(m,Mod.AllMods[m].Version)).ToArray()
Mods = lobbyInfo.GlobalSettings.Mods.Select(m => "{0}@{1}".F(m, Mod.AllMods[m].Version)).ToArray()
};
DispatchOrdersToClient(newConn, 0, 0, new ServerOrder("HandshakeRequest", request.Serialize()).Serialize());
}
catch (Exception) { DropClient(newConn); }
catch (Exception e)
{
DropClient(newConn);
Log.Write("server", "Dropping client {0} because handshake failed: {1}", newConn.PlayerIndex.ToString(), e);
}
}
void ValidateClient(Connection newConn, string data)
@@ -232,7 +237,25 @@ namespace OpenRA.Server
}
var handshake = HandshakeResponse.Deserialize(data);
var client = handshake.Client;
var client = new Session.Client()
{
Name = handshake.Client.Name,
IpAddress = ((IPEndPoint)newConn.socket.RemoteEndPoint).Address.ToString(),
Index = newConn.PlayerIndex,
Slot = lobbyInfo.FirstEmptySlot(),
PreferredColor = handshake.Client.Color,
Color = handshake.Client.Color,
Country = "random",
SpawnPoint = 0,
Team = 0,
State = Session.ClientState.NotReady,
IsAdmin = !lobbyInfo.Clients.Any(c1 => c1.IsAdmin)
};
if (client.Slot != null)
SyncClientToPlayerReference(client, Map.Players[client.Slot]);
var mods = handshake.Mods;
// Check that the client has compatible mods
@@ -263,42 +286,21 @@ namespace OpenRA.Server
return;
}
client.IpAddress = ((IPEndPoint)newConn.socket.RemoteEndPoint).Address.ToString();
// Check if IP is banned
if (lobbyInfo.GlobalSettings.Ban != null)
var bans = Settings.Ban.Union(TempBans);
if (bans.Contains(client.IpAddress))
{
if (lobbyInfo.GlobalSettings.Ban.Contains(client.IpAddress))
{
Console.WriteLine("Rejected connection from "+client.Name+"("+newConn.socket.RemoteEndPoint+"); Banned.");
Log.Write("server", "Rejected connection from {0}; Banned.",
newConn.socket.RemoteEndPoint);
SendOrderTo(newConn, "ServerError", "You are banned from the server!");
DropClient(newConn);
return;
}
Log.Write("server", "Rejected connection from {0}; Banned.", newConn.socket.RemoteEndPoint);
SendOrderTo(newConn, "ServerError", "You are {0} from the server.".F(Settings.Ban.Contains(client.IpAddress) ? "banned" : "temporarily banned"));
DropClient(newConn);
return;
}
// Promote connection to a valid client
preConns.Remove(newConn);
conns.Add(newConn);
// Enforce correct PlayerIndex and Slot
client.Index = newConn.PlayerIndex;
client.Slot = lobbyInfo.FirstEmptySlot();
if (client.Slot != null)
SyncClientToPlayerReference(client, Map.Players[client.Slot]);
lobbyInfo.Clients.Add(client);
// Assume that first validated client is server admin
if (lobbyInfo.Clients.Where(c1 => c1.Bot == null).Count() == 1)
client.IsAdmin=true;
OpenRA.Network.Session.Client clientAdmin = lobbyInfo.Clients.Where(c1 => c1.IsAdmin).Single();
Log.Write("server", "Client {0}: Accepted connection from {1}.",
newConn.PlayerIndex, newConn.socket.RemoteEndPoint);
@@ -317,19 +319,25 @@ namespace OpenRA.Server
SendOrderTo(newConn, "Message", motd);
}
if (lobbyInfo.GlobalSettings.Dedicated)
{
var message = client.IsAdmin ? "You are the server admin." : "{0} is the server admin.".F(clientAdmin.Name);
SendOrderTo(newConn, "Message", message);
}
if (mods.Any(m => m.Contains("{DEV_VERSION}")))
SendMessage("{0} is running an unversioned development build, ".F(client.Name) +
"and may desynchronize the game state if they have incompatible rules.");
SetOrderLag();
}
catch (Exception) { DropClient(newConn); }
}
void SetOrderLag()
{
if (lobbyInfo.IsSinglePlayer)
lobbyInfo.GlobalSettings.OrderLatency = 1;
else
lobbyInfo.GlobalSettings.OrderLatency = 3;
SyncLobbyInfo();
}
public static void SyncClientToPlayerReference(Session.Client c, PlayerReference pr)
{
if (pr == null)
@@ -364,14 +372,16 @@ namespace OpenRA.Server
{
try
{
var ms = new MemoryStream();
ms.Write( BitConverter.GetBytes( data.Length + 4 ) );
ms.Write( BitConverter.GetBytes( client ) );
ms.Write( BitConverter.GetBytes( frame ) );
ms.Write( data );
c.socket.Send( ms.ToArray() );
SendData(c.socket, BitConverter.GetBytes(data.Length + 4));
SendData(c.socket, BitConverter.GetBytes(client));
SendData(c.socket, BitConverter.GetBytes(frame));
SendData(c.socket, data);
}
catch (Exception e)
{
DropClient(c);
Log.Write("server", "Dropping client {0} because dispatching orders failed: {1}", client.ToString(), e);
}
catch (Exception) { DropClient(c); }
}
public void DispatchOrdersToClients(Connection conn, int frame, byte[] data)
@@ -485,7 +495,9 @@ namespace OpenRA.Server
{
conns.Remove(toDrop);
OpenRA.Network.Session.Client dropClient = lobbyInfo.Clients.Where(c1 => c1.Index == toDrop.PlayerIndex).Single();
var dropClient = lobbyInfo.Clients.FirstOrDefault(c1 => c1.Index == toDrop.PlayerIndex);
if (dropClient == null)
return;
// Send disconnected order, even if still in the lobby
SendMessage("{0} has disconnected.".F(dropClient.Name));
@@ -500,7 +512,7 @@ namespace OpenRA.Server
// Remove any bots controlled by the admin
lobbyInfo.Clients.RemoveAll(c => c.Bot != null && c.BotControllerClientIndex == toDrop.PlayerIndex);
OpenRA.Network.Session.Client nextAdmin = lobbyInfo.Clients.Where(c1 => c1.Bot == null)
var nextAdmin = lobbyInfo.Clients.Where(c1 => c1.Bot == null)
.OrderBy(c => c.Index).FirstOrDefault();
if (nextAdmin != null)
@@ -512,7 +524,10 @@ namespace OpenRA.Server
DispatchOrders(toDrop, toDrop.MostRecentFrame, new byte[] {0xbf});
if (conns.Count != 0 || lobbyInfo.GlobalSettings.Dedicated)
if (!conns.Any())
TempBans.Clear();
if (conns.Any() || lobbyInfo.GlobalSettings.Dedicated)
SyncLobbyInfo();
if (!lobbyInfo.GlobalSettings.Dedicated && dropClient.IsAdmin)
@@ -524,6 +539,8 @@ namespace OpenRA.Server
toDrop.socket.Disconnect(false);
}
catch { }
SetOrderLag();
}
public void SyncLobbyInfo()
@@ -543,9 +560,9 @@ namespace OpenRA.Server
Console.WriteLine("Game started");
foreach( var c in conns )
foreach( var d in conns )
DispatchOrdersToClient( c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF } );
foreach (var c in conns)
foreach (var d in conns)
DispatchOrdersToClient(c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF });
// Drop any unvalidated clients
foreach (var c in preConns.ToArray())
@@ -569,5 +586,29 @@ namespace OpenRA.Server
gameTimeout.Enabled = true;
}
}
void SendData(Socket s, byte[] data)
{
var start = 0;
var length = data.Length;
SocketError error;
// Non-blocking sends are free to send only part of the data
while (start < length)
{
var sent = s.Send(data, start, length - start, SocketFlags.None, out error);
if (error == SocketError.WouldBlock)
{
Log.Write("server", "Non-blocking send of {0} bytes failed. Falling back to blocking send.", length - start);
s.Blocking = true;
sent = s.Send(data, start, length - start, SocketFlags.None);
s.Blocking = false;
}
else if (error != SocketError.Success)
throw new SocketException((int)error);
start += sent;
}
}
}
}

View File

@@ -35,7 +35,15 @@ namespace OpenRA
return null;
}
return LoadSoundRaw(AudLoader.LoadSound(FileSystem.Open(filename)));
if (filename.ToLowerInvariant().EndsWith("wav"))
return LoadWave(new WavLoader(FileSystem.Open(filename)));
else
return LoadSoundRaw(AudLoader.LoadSound(FileSystem.Open(filename)));
}
static ISoundSource LoadWave(WavLoader wave)
{
return soundEngine.AddSoundSourceFromMemory(wave.RawOutput, wave.Channels, wave.BitsPerSample, wave.SampleRate);
}
static ISoundSource LoadSoundRaw(byte[] rawData)
@@ -45,7 +53,7 @@ namespace OpenRA
static ISoundEngine CreateEngine(string engine)
{
engine = Game.Settings.Server.Dedicated?"Null":engine;
engine = Game.Settings.Server.Dedicated ? "Null" : engine;
switch (engine)
{ /* TODO: if someone cares about pluggable crap here, ship this out */
case "AL": return new OpenAlSoundEngine();
@@ -366,6 +374,7 @@ namespace OpenRA
public OpenAlSoundEngine()
{
Console.WriteLine("Using OpenAL sound engine");
//var str = Alc.alcGetString(IntPtr.Zero, Alc.ALC_DEFAULT_DEVICE_SPECIFIER);
var dev = Alc.alcOpenDevice(null);
if (dev == IntPtr.Zero)
@@ -637,6 +646,11 @@ namespace OpenRA
class NullSoundEngine : ISoundEngine
{
public NullSoundEngine()
{
Console.WriteLine("Using Null sound engine which disables SFX completely");
}
public ISoundSource AddSoundSourceFromMemory(byte[] data, int channels, int sampleBits, int sampleRate)
{
return new NullSoundSource();

View File

@@ -0,0 +1,58 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.FileFormats;
namespace OpenRA.Traits
{
public class BodyOrientationInfo : ITraitInfo, IBodyOrientationInfo
{
[Desc("Camera pitch for rotation calculations")]
public readonly WAngle CameraPitch = WAngle.FromDegrees(40);
public object Create(ActorInitializer init) { return new BodyOrientation(init.self, this); }
}
public class BodyOrientation : IBodyOrientation
{
[Sync] public int QuantizedFacings { get; set; }
BodyOrientationInfo Info;
public BodyOrientation(Actor self, BodyOrientationInfo info)
{
Info = info;
}
public WAngle CameraPitch { get { return Info.CameraPitch; } }
public WVec LocalToWorld(WVec vec)
{
// RA's 2d perspective doesn't correspond to an orthonormal 3D
// coordinate system, so fudge the y axis to make things look good
return new WVec(vec.Y, -Info.CameraPitch.Sin()*vec.X/1024, vec.Z);
}
public WRot QuantizeOrientation(Actor self, WRot orientation)
{
// Quantization disabled
if (QuantizedFacings == 0)
return orientation;
// Map yaw to the closest facing
var facing = Util.QuantizeFacing(orientation.Yaw.Angle / 4, QuantizedFacings) * (256 / QuantizedFacings);
// Roll and pitch are always zero if yaw is quantized
return new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facing));
}
}
}

View File

@@ -21,8 +21,8 @@ namespace OpenRA.Traits
public class CreatesShroud : ITick, ISync
{
CreatesShroudInfo Info;
[Sync] CPos previousLocation;
Shroud.ActorVisibility v;
[Sync] CPos cachedLocation;
[Sync] bool cachedDisabled;
public CreatesShroud(CreatesShroudInfo info)
{
@@ -31,29 +31,18 @@ namespace OpenRA.Traits
public void Tick(Actor self)
{
// TODO: don't tick all the time.
if (self.Owner == null)
return;
var shrouds = self.World.ActorsWithTrait<Shroud>().Select(s => s.Actor.Owner.Shroud);
if (previousLocation != self.Location && v != null)
var disabled = self.TraitsImplementing<IDisable>().Any(d => d.Disabled);
if (cachedLocation != self.Location || cachedDisabled != disabled)
{
previousLocation = self.Location;
cachedLocation = self.Location;
cachedDisabled = disabled;
foreach (var shroud in shrouds)
shroud.UnhideActor(self, v, Info.Range);
var shroud = self.World.Players.Select(p => p.Shroud);
foreach (var s in shroud)
s.UpdateShroudGeneration(self);
}
if (!self.TraitsImplementing<IDisable>().Any(d => d.Disabled))
foreach (var shroud in shrouds)
shroud.HideActor(self, Info.Range);
else
foreach (var shroud in shrouds)
shroud.UnhideActor(self, v, Info.Range);
v = new Shroud.ActorVisibility {
vis = Shroud.GetVisOrigins(self).ToArray()
};
}
public int Range { get { return cachedDisabled ? 0 : Info.Range; } }
}
}

View File

@@ -144,7 +144,7 @@ namespace OpenRA.Traits
.Concat(self.Owner.PlayerActor.TraitsImplementing<INotifyKilled>()))
nd.Killed(self, ai);
if( RemoveOnDeath )
if (RemoveOnDeath)
self.Destroy();
Log.Write("debug", "{0} #{1} killed by {2} #{3}", self.Info.Name, self.ActorID, attacker.Info.Name, attacker.ActorID);

View File

@@ -22,6 +22,7 @@ namespace OpenRA.Traits
public bool UnlimitedPower;
public bool BuildAnywhere;
public bool ShowMuzzles;
public bool ShowDebugGeometry;
public object Create (ActorInitializer init) { return new DeveloperMode(this); }
}
@@ -39,6 +40,7 @@ namespace OpenRA.Traits
// Client size only
public bool ShowMuzzles;
public bool ShowDebugGeometry;
public DeveloperMode(DeveloperModeInfo info)
{
@@ -50,6 +52,7 @@ namespace OpenRA.Traits
UnlimitedPower = info.UnlimitedPower;
BuildAnywhere = info.BuildAnywhere;
ShowMuzzles = info.ShowMuzzles;
ShowDebugGeometry = info.ShowDebugGeometry;
}
public void ResolveOrder (Actor self, Order order)

View File

@@ -23,42 +23,6 @@ namespace OpenRA.Traits
public object Create(ActorInitializer init) { return new PlayerResources(init.self, this); }
}
public class DebugResourceCashInfo : ITraitInfo, Requires<PlayerResourcesInfo>
{
public object Create(ActorInitializer init) { return new DebugResourceCash(init.self); }
}
public class DebugResourceCash : ISync
{
readonly PlayerResources pr;
public DebugResourceCash(Actor self) { pr = self.Trait<PlayerResources>(); }
[Sync] public int foo { get { return pr.Cash; } }
}
public class DebugResourceOreInfo : ITraitInfo, Requires<PlayerResourcesInfo>
{
public object Create(ActorInitializer init) { return new DebugResourceOre(init.self); }
}
public class DebugResourceOre : ISync
{
readonly PlayerResources pr;
public DebugResourceOre(Actor self) { pr = self.Trait<PlayerResources>(); }
[Sync] public int foo { get { return pr.Ore; } }
}
public class DebugResourceOreCapacityInfo : ITraitInfo
{
public object Create(ActorInitializer init) { return new DebugResourceOreCapacity(init.self); }
}
public class DebugResourceOreCapacity : ISync
{
readonly PlayerResources pr;
public DebugResourceOreCapacity(Actor self) { pr = self.Trait<PlayerResources>(); }
[Sync] public int foo { get { return pr.OreCapacity; } }
}
public class PlayerResources : ITick, ISync
{
readonly Player Owner;

View File

@@ -16,103 +16,35 @@ using OpenRA.FileFormats;
namespace OpenRA.Traits
{
public class RenderSimpleInfo : ITraitInfo, LocalCoordinatesModelInfo
public class RenderSimpleInfo : RenderSpritesInfo, Requires<IBodyOrientationInfo>
{
[Desc("Defaults to the actor name.")]
public readonly string Image = null;
[Desc("custom palette name")]
public readonly string Palette = null;
[Desc("custom PlayerColorPalette: BaseName")]
public readonly string PlayerPalette = "player";
[Desc("Change the sprite image size.")]
public readonly float Scale = 1f;
public override object Create(ActorInitializer init) { return new RenderSimple(init.self); }
[Desc("Number of facings for gameplay calculations. -1 indiciates auto-detection from sequence")]
public readonly int QuantizedFacings = -1;
public readonly WAngle CameraPitch = WAngle.FromDegrees(40);
public virtual object Create(ActorInitializer init) { return new RenderSimple(init.self); }
public virtual IEnumerable<Renderable> RenderPreview(ActorInfo building, PaletteReference pr)
public virtual IEnumerable<IRenderable> RenderPreview(ActorInfo ai, PaletteReference pr)
{
var anim = new Animation(RenderSimple.GetImage(building), () => 0);
var anim = new Animation(RenderSimple.GetImage(ai), () => 0);
anim.PlayRepeating("idle");
yield return new Renderable(anim.Image, 0.5f * anim.Image.size * (1 - Scale), pr, 0, Scale);
yield return new SpriteRenderable(anim.Image, WPos.Zero, 0, pr, 1f);
}
}
public class RenderSimple : IRender, ILocalCoordinatesModel, IAutoSelectionSize, ITick, INotifyOwnerChanged
public class RenderSimple : RenderSprites, IAutoSelectionSize
{
public Dictionary<string, AnimationWithOffset> anims = new Dictionary<string, AnimationWithOffset>();
public static Func<int> MakeFacingFunc(Actor self)
{
var facing = self.TraitOrDefault<IFacing>();
if (facing == null) return () => 0;
return () => facing.Facing;
}
public Animation anim
{
get { return anims[""].Animation; }
protected set { anims[""].Animation = value; }
}
public static string GetImage(ActorInfo actor)
{
var Info = actor.Traits.Get<RenderSimpleInfo>();
return Info.Image ?? actor.Name;
}
public string GetImage(Actor self)
{
if (cachedImage != null)
return cachedImage;
return cachedImage = GetImage(self.Info);
}
RenderSimpleInfo Info;
string cachedImage = null;
bool initializePalette = true;
protected PaletteReference palette;
public RenderSimple(Actor self, Func<int> baseFacing)
: base(self)
{
anims.Add("", new Animation(GetImage(self), baseFacing));
Info = self.Info.Traits.Get<RenderSimpleInfo>();
}
public RenderSimple(Actor self) : this( self, MakeFacingFunc(self) )
public RenderSimple(Actor self)
: this(self, MakeFacingFunc(self))
{
anim.PlayRepeating("idle");
}
protected virtual string PaletteName(Actor self)
{
return Info.Palette ?? Info.PlayerPalette + self.Owner.InternalName;
}
protected void UpdatePalette() { initializePalette = true; }
public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { UpdatePalette(); }
public virtual IEnumerable<Renderable> Render(Actor self, WorldRenderer wr)
{
if (initializePalette)
{
palette = wr.Palette(PaletteName(self));
initializePalette = false;
}
foreach (var a in anims.Values)
if (a.DisableFunc == null || !a.DisableFunc())
{
Renderable ret = a.Image(self, wr, palette);
if (Info.Scale != 1f)
ret = ret.WithScale(Info.Scale).WithPos(ret.Pos + 0.5f * ret.Sprite.size * (1 - Info.Scale));
yield return ret;
}
self.Trait<IBodyOrientation>().QuantizedFacings = anim.CurrentSequence.Facings;
}
public int2 SelectionSize(Actor self)
@@ -123,19 +55,9 @@ namespace OpenRA.Traits
.FirstOrDefault();
}
public virtual void Tick(Actor self)
public string NormalizeSequence(Actor self, string baseSequence)
{
foreach (var a in anims.Values)
a.Animation.Tick();
}
protected virtual string NormalizeSequence(Actor self, string baseSequence)
{
string damageState = self.GetDamageState() >= DamageState.Heavy ? "damaged-" : "";
if (anim.HasSequence(damageState + baseSequence))
return damageState + baseSequence;
else
return baseSequence;
return NormalizeSequence(anim, self.GetDamageState(), baseSequence);
}
public void PlayCustomAnim(Actor self, string name)
@@ -144,22 +66,5 @@ namespace OpenRA.Traits
anim.PlayThen(NormalizeSequence(self, name),
() => anim.PlayRepeating(NormalizeSequence(self, "idle")));
}
public WVec LocalToWorld(WVec vec)
{
// RA's 2d perspective doesn't correspond to an orthonormal 3D
// coordinate system, so fudge the y axis to make things look good
return new WVec(vec.Y, -Info.CameraPitch.Sin()*vec.X/1024, vec.Z);
}
public WRot QuantizeOrientation(Actor self, WRot orientation)
{
// Map yaw to the closest facing
var numDirs = Info.QuantizedFacings == -1 ? anim.CurrentSequence.Facings : Info.QuantizedFacings;
var facing = Util.QuantizeFacing(orientation.Yaw.Angle / 4, numDirs) * (256 / numDirs);
// Roll and pitch are always zero
return new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facing));
}
}
}

View File

@@ -0,0 +1,120 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.FileFormats;
namespace OpenRA.Traits
{
public class RenderSpritesInfo : ITraitInfo
{
[Desc("Defaults to the actor name.")]
public readonly string Image = null;
[Desc("Custom palette name")]
public readonly string Palette = null;
[Desc("Custom PlayerColorPalette: BaseName")]
public readonly string PlayerPalette = "player";
[Desc("Change the sprite image size.")]
public readonly float Scale = 1f;
public virtual object Create(ActorInitializer init) { return new RenderSprites(init.self); }
}
public class RenderSprites : IRender, ITick, INotifyOwnerChanged
{
public Dictionary<string, AnimationWithOffset> anims = new Dictionary<string, AnimationWithOffset>();
public static Func<int> MakeFacingFunc(Actor self)
{
var facing = self.TraitOrDefault<IFacing>();
if (facing == null) return () => 0;
return () => facing.Facing;
}
public Animation anim
{
get { return anims[""].Animation; }
protected set { anims[""] = new AnimationWithOffset(value,
anims[""].OffsetFunc, anims[""].DisableFunc, anims[""].ZOffset); }
}
RenderSpritesInfo Info;
string cachedImage = null;
bool initializePalette = true;
protected PaletteReference palette;
public RenderSprites(Actor self)
{
Info = self.Info.Traits.Get<RenderSpritesInfo>();
}
public static string GetImage(ActorInfo actor)
{
var Info = actor.Traits.Get<RenderSpritesInfo>();
return Info.Image ?? actor.Name;
}
public string GetImage(Actor self)
{
if (cachedImage != null)
return cachedImage;
return cachedImage = GetImage(self.Info);
}
protected virtual string PaletteName(Actor self)
{
return Info.Palette ?? Info.PlayerPalette + self.Owner.InternalName;
}
protected void UpdatePalette() { initializePalette = true; }
public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { UpdatePalette(); }
public virtual IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
{
if (initializePalette)
{
palette = wr.Palette(PaletteName(self));
initializePalette = false;
}
foreach (var a in anims.Values)
if (a.DisableFunc == null || !a.DisableFunc())
yield return a.Image(self, wr, palette, Info.Scale);
}
public virtual void Tick(Actor self)
{
foreach (var a in anims.Values)
a.Animation.Tick();
}
public static string NormalizeSequence(Animation anim, DamageState state, string baseSequence)
{
var states = new Pair<DamageState, string>[]
{
Pair.New(DamageState.Critical, "critical-"),
Pair.New(DamageState.Heavy, "damaged-"),
Pair.New(DamageState.Medium, "scratched-"),
Pair.New(DamageState.Light, "scuffed-")
};
foreach (var s in states)
if (state >= s.First && anim.HasSequence(s.Second+baseSequence))
return s.Second+baseSequence;
return baseSequence;
}
}
}

View File

@@ -8,6 +8,8 @@
*/
#endregion
using System.Linq;
namespace OpenRA.Traits
{
public class RevealsShroudInfo : ITraitInfo
@@ -19,7 +21,7 @@ namespace OpenRA.Traits
public class RevealsShroud : ITick, ISync
{
RevealsShroudInfo Info;
[Sync] CPos previousLocation;
[Sync] CPos cachedLocation;
public RevealsShroud(RevealsShroudInfo info)
{
@@ -28,26 +30,15 @@ namespace OpenRA.Traits
public void Tick(Actor self)
{
// TODO: don't tick all the time.
World w = self.World;
if(self.Owner == null) return;
if (previousLocation != self.Location)
if (cachedLocation != self.Location)
{
previousLocation = self.Location;
var actors = w.ActorsWithTrait<Shroud>();
cachedLocation = self.Location;
foreach( var s in actors )
s.Actor.Owner.Shroud.RemoveActor(self);
self.UpdateSight();
foreach( var s in actors )
s.Actor.Owner.Shroud.AddActor(self);
foreach (var s in self.World.Players.Select(p => p.Shroud))
s.UpdateVisibility(self);
}
}
public int RevealRange { get { return Info.Range; } }
public int Range { get { return Info.Range; } }
}
}

View File

@@ -51,7 +51,7 @@ namespace OpenRA.Traits
var pipImages = new Animation("pips");
pipImages.PlayFetchIndex("groups", () => (int)group);
pipImages.Tick();
pipImages.Image.DrawAt(wr, basePosition + new float2(-8, 1), "chrome");
pipImages.Image.DrawAt(basePosition + new float2(-8, 1), wr.Palette("chrome"));
}
void DrawPips(WorldRenderer wr, Actor self, float2 basePosition)
@@ -69,6 +69,7 @@ namespace OpenRA.Traits
var pipSize = pipImages.Image.size;
var pipxyBase = basePosition + new float2(1, -pipSize.Y);
var pipxyOffset = new float2(0, 0); // Correct for offset due to multiple columns/rows
var pal = wr.Palette("chrome");
foreach (var pips in pipSources)
{
@@ -86,7 +87,7 @@ namespace OpenRA.Traits
pipxyOffset.Y -= pipSize.Y;
}
pipImages.PlayRepeating(pipStrings[(int)pip]);
pipImages.Image.DrawAt(wr, pipxyBase + pipxyOffset, "chrome");
pipImages.Image.DrawAt(pipxyBase + pipxyOffset, pal);
pipxyOffset += new float2(pipSize.X, 0);
}
@@ -104,7 +105,7 @@ namespace OpenRA.Traits
// If a mod wants to implement a unit with multiple tags, then they are placed on multiple rows
var tagxyBase = basePosition + new float2(-16, 2); // Correct for the offset in the shp file
var tagxyOffset = new float2(0, 0); // Correct for offset due to multiple rows
var pal = wr.Palette("chrome");
foreach (var tags in self.TraitsImplementing<ITags>())
{
foreach (var tag in tags.GetTags())
@@ -114,7 +115,7 @@ namespace OpenRA.Traits
var tagImages = new Animation("pips");
tagImages.PlayRepeating(tagStrings[(int)tag]);
tagImages.Image.DrawAt(wr, tagxyBase + tagxyOffset, "chrome");
tagImages.Image.DrawAt(tagxyBase + tagxyOffset, pal);
// Increment row
tagxyOffset.Y += 8;

View File

@@ -15,9 +15,9 @@ namespace OpenRA.Traits
public static Target[] NoTargets = {};
Actor actor;
Player owner;
PPos pos;
bool valid;
int generation;
public static Target FromActor(Actor a)
{
@@ -25,7 +25,7 @@ namespace OpenRA.Traits
{
actor = a,
valid = (a != null),
owner = (a != null) ? a.Owner : null
generation = a.Generation,
};
}
public static Target FromPos(PPos p) { return new Target { pos = p, valid = true }; }
@@ -39,7 +39,7 @@ namespace OpenRA.Traits
public static readonly Target None = new Target();
public bool IsValid { get { return valid && (actor == null || (actor.IsInWorld && !actor.IsDead() && actor.Owner == owner)); } }
public bool IsValid { get { return valid && (actor == null || (actor.IsInWorld && !actor.IsDead() && actor.Generation == generation)); } }
public PPos PxPosition { get { return IsActor ? actor.Trait<IHasLocation>().PxPosition : pos; } }
public PPos CenterLocation { get { return PxPosition; } }

View File

@@ -35,7 +35,7 @@ namespace OpenRA.Traits
public interface ITick { void Tick(Actor self); }
public interface ITickRender { void TickRender(WorldRenderer wr, Actor self); }
public interface IRender { IEnumerable<Renderable> Render(Actor self, WorldRenderer wr); }
public interface IRender { IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr); }
public interface IAutoSelectionSize { int2 SelectionSize(Actor self); }
public interface IIssueOrder
@@ -44,12 +44,19 @@ namespace OpenRA.Traits
Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued);
}
[Flags] public enum TargetModifiers { None = 0, ForceAttack = 1, ForceQueue = 2, ForceMove = 4 };
public static class TargetModifiersExts
{
public static bool HasModifier(this TargetModifiers self, TargetModifiers m) { return (self & m) == m; }
}
public interface IOrderTargeter
{
string OrderID { get; }
int OrderPriority { get; }
bool CanTargetActor(Actor self, Actor target, bool forceAttack, bool forceQueue, ref string cursor);
bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, bool forceAttack, bool forceQueue, ref string cursor);
bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor);
bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor);
bool IsQueued { get; }
}
@@ -67,6 +74,8 @@ namespace OpenRA.Traits
public interface INotifyOwnerChanged { void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner); }
public interface INotifyCapture { void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner); }
public interface INotifyOtherCaptured { void OnActorCaptured(Actor self, Actor captured, Actor captor, Player oldOwner, Player newOwner); }
public interface INotifyHarvest { void Harvested(Actor self, ResourceType resource); }
public interface IAcceptInfiltrator { void OnInfiltrate(Actor self, Actor infiltrator); }
public interface IStoreOre { int Capacity { get; } }
public interface IToolTip
@@ -116,7 +125,7 @@ namespace OpenRA.Traits
}
public interface INotifyAttack { void Attacking(Actor self, Target target); }
public interface IRenderModifier { IEnumerable<Renderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<Renderable> r); }
public interface IRenderModifier { IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r); }
public interface IDamageModifier { float GetDamageModifier(Actor attacker, WarheadInfo warhead); }
public interface ISpeedModifier { decimal GetSpeedModifier(); }
public interface IFirepowerModifier { float GetFirepowerModifier(); }
@@ -152,37 +161,6 @@ namespace OpenRA.Traits
bool CrushableBy(string[] crushClasses, Player owner);
}
public struct Renderable
{
public readonly Sprite Sprite;
public readonly float2 Pos;
public readonly PaletteReference Palette;
public readonly int Z;
public readonly int ZOffset;
public float Scale;
public Renderable(Sprite sprite, float2 pos, PaletteReference palette, int z, int zOffset, float scale)
{
Sprite = sprite;
Pos = pos;
Palette = palette;
Z = z;
ZOffset = zOffset;
Scale = scale; /* default */
}
public Renderable(Sprite sprite, float2 pos, PaletteReference palette, int z)
: this(sprite, pos, palette, z, 0, 1f) { }
public Renderable(Sprite sprite, float2 pos, PaletteReference palette, int z, float scale)
: this(sprite, pos, palette, z, 0, scale) { }
public Renderable WithScale(float newScale) { return new Renderable(Sprite, Pos, Palette, Z, ZOffset, newScale); }
public Renderable WithPalette(PaletteReference newPalette) { return new Renderable(Sprite, Pos, newPalette, Z, ZOffset, Scale); }
public Renderable WithZOffset(int newOffset) { return new Renderable(Sprite, Pos, Palette, Z, newOffset, Scale); }
public Renderable WithPos(float2 newPos) { return new Renderable(Sprite, newPos, Palette, Z, ZOffset, Scale); }
}
public interface ITraitInfo { object Create(ActorInitializer init); }
public class TraitInfo<T> : ITraitInfo where T : new() { public virtual object Create(ActorInitializer init) { return new T(); } }
@@ -210,13 +188,15 @@ namespace OpenRA.Traits
public interface IPostRenderSelection { void RenderAfterWorld(WorldRenderer wr); }
public interface IPreRenderSelection { void RenderBeforeWorld(WorldRenderer wr, Actor self); }
public interface IRenderAsTerrain { IEnumerable<Renderable> RenderAsTerrain(WorldRenderer wr, Actor self); }
public interface ILocalCoordinatesModel
public interface IRenderAsTerrain { IEnumerable<IRenderable> RenderAsTerrain(WorldRenderer wr, Actor self); }
public interface IBodyOrientation
{
WAngle CameraPitch { get; }
int QuantizedFacings { get; set; }
WVec LocalToWorld(WVec vec);
WRot QuantizeOrientation(Actor self, WRot orientation);
}
public interface LocalCoordinatesModelInfo {}
public interface IBodyOrientationInfo {}
public interface ITargetable
{

View File

@@ -110,19 +110,19 @@ namespace OpenRA.Traits
(next, a) => { a.Queue( next ); return a; });
}
public static Activity RunActivity( Actor self, Activity act )
public static Activity RunActivity(Actor self, Activity act)
{
while( act != null )
while (act != null)
{
var prev = act;
var sw = new Stopwatch();
act = act.Tick( self );
act = act.Tick(self);
var dt = sw.ElapsedTime();
if(dt > Game.Settings.Debug.LongTickThreshold)
if (dt > Game.Settings.Debug.LongTickThreshold)
Log.Write("perf", "[{2}] Activity: {0} ({1:0.000} ms)", prev, dt * 1000, Game.LocalTick);
if( prev == act )
if (prev == act)
break;
}
return act;

View File

@@ -47,7 +47,7 @@ namespace OpenRA.Traits
if (c.image != null)
c.image[c.density].DrawAt(
new CPos(x, y).ToPPos().ToFloat2(),
c.type.info.PaletteRef.Index);
c.type.info.PaletteRef);
}
}

View File

@@ -29,9 +29,14 @@ namespace OpenRA.Traits
Actor self;
Map map;
int[,] visibleCells;
bool[,] exploredCells;
bool[,] foggedCells;
int[,] visibleCount;
int[,] generatedShroudCount;
bool[,] explored;
// Cache of visibility that was added, so no matter what crazy trait code does, it
// can't make us invalid.
Dictionary<Actor, CPos[]> visibility = new Dictionary<Actor, CPos[]>();
Dictionary<Actor, CPos[]> generation = new Dictionary<Actor, CPos[]>();
public Rectangle ExploredBounds { get; private set; }
@@ -43,11 +48,15 @@ namespace OpenRA.Traits
this.self = self;
map = self.World.Map;
visibleCells = new int[map.MapSize.X, map.MapSize.Y];
exploredCells = new bool[map.MapSize.X, map.MapSize.Y];
foggedCells = new bool[map.MapSize.X, map.MapSize.Y];
self.World.ActorAdded += AddActor;
self.World.ActorRemoved += RemoveActor;
visibleCount = new int[map.MapSize.X, map.MapSize.Y];
generatedShroudCount = new int[map.MapSize.X, map.MapSize.Y];
explored = new bool[map.MapSize.X, map.MapSize.Y];
self.World.ActorAdded += AddVisibility;
self.World.ActorRemoved += RemoveVisibility;
self.World.ActorAdded += AddShroudGeneration;
self.World.ActorRemoved += RemoveShroudGeneration;
if (!info.Shroud)
ExploredBounds = map.Bounds;
@@ -58,16 +67,6 @@ namespace OpenRA.Traits
Hash = Sync.hash_player(self.Owner) + self.World.FrameNumber * 3;
}
// cache of positions that were added, so no matter what crazy trait code does, it
// can't make us invalid.
public class ActorVisibility
{
[Sync] public int range;
[Sync] public CPos[] vis;
}
public Dictionary<Actor, ActorVisibility> vis = new Dictionary<Actor, ActorVisibility>();
static IEnumerable<CPos> FindVisibleTiles(World world, CPos a, int r)
{
var min = a - new CVec(r, r);
@@ -86,77 +85,99 @@ namespace OpenRA.Traits
for (var j = min.Y; j <= max.Y; j++)
for (var i = min.X; i <= max.X; i++)
if (r * r >= (new CPos(i, j) - a).LengthSquared)
if (r*r >= (new CPos(i, j) - a).LengthSquared)
yield return new CPos(i, j);
}
public void AddActor(Actor a)
void AddVisibility(Actor a)
{
if (!a.HasTrait<RevealsShroud>() || !a.Owner.IsAlliedWith(self.Owner))
var rs = a.TraitOrDefault<RevealsShroud>();
if (rs == null || !a.Owner.IsAlliedWith(self.Owner) || rs.Range == 0)
return;
ActorVisibility v = a.Sight;
if (v.range == 0)
return;
var origins = GetVisOrigins(a);
var visible = origins.SelectMany(o => FindVisibleTiles(a.World, o, rs.Range))
.Distinct().ToArray();
foreach (var p in v.vis)
// Update bounding rect
foreach (var o in origins)
{
foreach (var q in FindVisibleTiles(a.World, p, v.range))
{
++visibleCells[q.X, q.Y];
exploredCells[q.X, q.Y] = true;
foggedCells[q.X, q.Y] = true;
}
var box = new Rectangle(p.X - v.range, p.Y - v.range, 2 * v.range + 1, 2 * v.range + 1);
var box = new Rectangle(o.X - rs.Range, o.Y - rs.Range, 2*rs.Range + 1, 2*rs.Range + 1);
ExploredBounds = Rectangle.Union(ExploredBounds, box);
}
Invalidate();
}
public void HideActor(Actor a, int range)
{
if (a.Owner.IsAlliedWith(self.Owner))
return;
var v = new ActorVisibility
// Update visibility
foreach (var c in visible)
{
vis = GetVisOrigins(a).ToArray()
};
foreach (var p in v.vis)
foreach (var q in FindVisibleTiles(a.World, p, range))
foggedCells[q.X, q.Y] = visibleCells[q.X, q.Y] > 0;
Invalidate();
}
public void UnhideActor(Actor a, ActorVisibility v, int range)
{
if (a.Owner.IsAlliedWith(self.Owner) || v == null)
return;
foreach (var p in v.vis)
foreach (var q in FindVisibleTiles(a.World, p, range))
foggedCells[q.X, q.Y] = exploredCells[q.X, q.Y];
Invalidate();
}
public void MergeShroud(Shroud s)
{
for (int i = map.Bounds.Left; i < map.Bounds.Right; i++)
{
for (int j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
{
if (s.exploredCells[i,j] == true)
exploredCells[i, j] = true;
if (s.foggedCells[i,j] == true)
foggedCells[i, j] = true;
}
ExploredBounds = Rectangle.Union(ExploredBounds, s.ExploredBounds);
visibleCount[c.X, c.Y]++;
explored[c.X, c.Y] = true;
}
if (visibility.ContainsKey(a))
throw new InvalidOperationException("Attempting to add duplicate actor visibility");
visibility[a] = visible;
Invalidate();
}
void RemoveVisibility(Actor a)
{
CPos[] visible;
if (!visibility.TryGetValue(a, out visible))
return;
foreach (var c in visible)
visibleCount[c.X, c.Y]--;
visibility.Remove(a);
Invalidate();
}
public void UpdateVisibility(Actor a)
{
// Actors outside the world don't have any vis
if (!a.IsInWorld)
return;
RemoveVisibility(a);
AddVisibility(a);
}
void AddShroudGeneration(Actor a)
{
var cs = a.TraitOrDefault<CreatesShroud>();
if (cs == null || a.Owner.IsAlliedWith(self.Owner) || cs.Range == 0)
return;
var shrouded = GetVisOrigins(a).SelectMany(o => FindVisibleTiles(a.World, o, cs.Range))
.Distinct().ToArray();
foreach (var c in shrouded)
generatedShroudCount[c.X, c.Y]++;
if (generation.ContainsKey(a))
throw new InvalidOperationException("Attempting to add duplicate shroud generation");
generation[a] = shrouded;
Invalidate();
}
void RemoveShroudGeneration(Actor a)
{
CPos[] shrouded;
if (!generation.TryGetValue(a, out shrouded))
return;
foreach (var c in shrouded)
generatedShroudCount[c.X, c.Y]--;
generation.Remove(a);
Invalidate();
}
public void UpdateShroudGeneration(Actor a)
{
RemoveShroudGeneration(a);
AddShroudGeneration(a);
}
public void UpdatePlayerStance(World w, Player player, Stance oldStance, Stance newStance)
@@ -164,29 +185,11 @@ namespace OpenRA.Traits
if (oldStance == newStance)
return;
// No longer our ally; remove unit vis
if (oldStance == Stance.Ally)
foreach (var a in w.Actors.Where(a => a.Owner == player))
{
var toRemove = w.Actors.Where(a => a.Owner == player).ToList();
foreach (var a in toRemove)
RemoveActor(a);
UpdateVisibility(a);
UpdateShroudGeneration(a);
}
// Is now our ally; add unit vis
if (newStance == Stance.Ally)
foreach (var a in w.Actors.Where( a => a.Owner == player ))
AddActor(a);
}
public int Explored()
{
int seen = 0;
for (int i = map.Bounds.Left; i < map.Bounds.Right; i++)
for (int j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
if (foggedCells[i, j])
seen++;
return seen;
}
public static IEnumerable<CPos> GetVisOrigins(Actor a)
@@ -195,64 +198,40 @@ namespace OpenRA.Traits
if (ios != null)
{
var cells = ios.OccupiedCells();
if (cells.Any()) return cells.Select(c => c.First);
if (cells.Any())
return cells.Select(c => c.First);
}
return new[] { a.CenterLocation.ToCPos() };
}
public void RemoveActor(Actor a)
{
ActorVisibility v = a.Sight;
if (!a.Owner.IsAlliedWith(self.Owner))
{
if (a.HasTrait<CreatesShroud>())
foreach (var p in v.vis)
foreach (var q in FindVisibleTiles(a.World, p, v.range))
foggedCells[q.X, q.Y] = exploredCells[q.X, q.Y];
return;
}
if (!a.HasTrait<RevealsShroud>())
return;
foreach (var p in v.vis)
foreach (var q in FindVisibleTiles(a.World, p, v.range))
--visibleCells[q.X, q.Y];
Invalidate();
}
public void UpdateActor(Actor a)
{
if (!a.Owner.IsAlliedWith(self.Owner))
return;
RemoveActor(a);
AddActor(a);
}
public void Explore(World world, CPos center, int range)
{
foreach (var q in FindVisibleTiles(world, center, range)) {
exploredCells[q.X, q.Y] = true;
foggedCells[q.X, q.Y] = true;
}
foreach (var q in FindVisibleTiles(world, center, range))
explored[q.X, q.Y] = true;
var box = new Rectangle(center.X - range, center.Y - range, 2 * range + 1, 2 * range + 1);
var box = new Rectangle(center.X - range, center.Y - range, 2*range + 1, 2*range + 1);
ExploredBounds = Rectangle.Union(ExploredBounds, box);
Invalidate();
}
public void Explore(Shroud s)
{
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
if (s.explored[i,j] == true)
explored[i, j] = true;
ExploredBounds = Rectangle.Union(ExploredBounds, s.ExploredBounds);
}
public void ExploreAll(World world)
{
for (int i = map.Bounds.Left; i < map.Bounds.Right; i++) {
for (int j = map.Bounds.Top; j < map.Bounds.Bottom; j++) {
exploredCells[i, j] = true;
foggedCells[i, j] = true;
}
}
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
explored[i, j] = true;
ExploredBounds = world.Map.Bounds;
Invalidate();
@@ -260,13 +239,9 @@ namespace OpenRA.Traits
public void ResetExploration()
{
for (var j = 0; j <= exploredCells.GetUpperBound(1); j++)
for (var i = 0; i <= exploredCells.GetUpperBound(0); i++)
exploredCells[i, j] = visibleCells[i, j] > 0;
for (var j = 0; j <= foggedCells.GetUpperBound(1); j++)
for (var i = 0; i <= foggedCells.GetUpperBound(0); i++)
foggedCells[i, j] = visibleCells[i, j] > 0;
for (var i = map.Bounds.Left; i < map.Bounds.Right; i++)
for (var j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
explored[i, j] = visibleCount[i, j] > 0;
Invalidate();
}
@@ -280,7 +255,7 @@ namespace OpenRA.Traits
if (!Info.Shroud)
return true;
return foggedCells[x,y];
return explored[x, y] && (generatedShroudCount[x, y] == 0 || visibleCount[x, y] > 0);
}
public bool IsExplored(Actor a)
@@ -299,7 +274,7 @@ namespace OpenRA.Traits
if (!Info.Fog)
return true;
return visibleCells[x,y] != 0;
return visibleCount[x, y] > 0;
}
// Actors are hidden under shroud, but not under fog by default
@@ -311,7 +286,8 @@ namespace OpenRA.Traits
return a.Owner.IsAlliedWith(self.Owner) || IsExplored(a);
}
public bool IsTargetable(Actor a) {
public bool IsTargetable(Actor a)
{
if (a.TraitsImplementing<IVisibilityModifier>().Any(t => !t.IsVisible(a, self.Owner)))
return false;

View File

@@ -15,7 +15,7 @@ using OpenRA.FileFormats;
namespace OpenRA.Widgets
{
static class ChromeMetrics
public static class ChromeMetrics
{
static Dictionary<string, string> data = new Dictionary<string, string>();
@@ -32,7 +32,7 @@ namespace OpenRA.Widgets
public static T Get<T>(string key)
{
return FieldLoader.GetValue<T>( key, data[key] );
return FieldLoader.GetValue<T>(key, data[key]);
}
}
}

View File

@@ -26,6 +26,7 @@ namespace OpenRA.Widgets
public string Background = "scrollpanel-bg";
public int ContentHeight = 0;
public ILayout Layout;
public int MinimumThumbSize = 10;
protected float ListOffset = 0;
protected bool UpPressed = false;
protected bool DownPressed = false;
@@ -79,7 +80,7 @@ namespace OpenRA.Widgets
var ScrollbarHeight = rb.Height - 2 * ScrollbarWidth;
var thumbHeight = ContentHeight == 0 ? 0 : (int)(ScrollbarHeight*Math.Min(rb.Height*1f/ContentHeight, 1f));
var thumbHeight = ContentHeight == 0 ? 0 : Math.Max(MinimumThumbSize, (int)(ScrollbarHeight*Math.Min(rb.Height*1f/ContentHeight, 1f)));
var thumbOrigin = rb.Y + ScrollbarWidth + (int)((ScrollbarHeight - thumbHeight)*(-1f*ListOffset/(ContentHeight - rb.Height)));
if (thumbHeight == ScrollbarHeight)
thumbHeight = 0;
@@ -206,7 +207,7 @@ namespace OpenRA.Widgets
{
var rb = RenderBounds;
var ScrollbarHeight = rb.Height - 2 * ScrollbarWidth;
var thumbHeight = ContentHeight == 0 ? 0 : (int)(ScrollbarHeight*Math.Min(rb.Height*1f/ContentHeight, 1f));
var thumbHeight = ContentHeight == 0 ? 0 : Math.Max(MinimumThumbSize, (int)(ScrollbarHeight*Math.Min(rb.Height*1f/ContentHeight, 1f)));
var oldOffset = ListOffset;
ListOffset += (int)((lastMouseLocation.Y - mi.Location.Y)*(ContentHeight - rb.Height)*1f/(ScrollbarHeight - thumbHeight));
ListOffset = Math.Min(0,Math.Max(rb.Height - ContentHeight, ListOffset));

View File

@@ -18,6 +18,7 @@ namespace OpenRA.Widgets
public string Image = "";
public int Frame = 0;
public string Palette = "chrome";
public bool LoopAnimation = false;
public Func<string> GetImage;
public Func<int> GetFrame;
@@ -26,12 +27,13 @@ namespace OpenRA.Widgets
readonly WorldRenderer worldRenderer;
[ObjectCreator.UseCtor]
public ShpImageWidget( WorldRenderer worldRenderer)
public ShpImageWidget(WorldRenderer worldRenderer)
: base()
{
GetImage = () => { return Image; };
GetFrame = () => { return Frame; };
GetPalette = () => { return Palette; };
this.worldRenderer = worldRenderer;
}
@@ -41,9 +43,12 @@ namespace OpenRA.Widgets
Image = other.Image;
Frame = other.Frame;
Palette = other.Palette;
LoopAnimation = other.LoopAnimation;
GetImage = other.GetImage;
GetFrame = other.GetFrame;
GetPalette = other.GetPalette;
worldRenderer = other.worldRenderer;
}
@@ -66,7 +71,34 @@ namespace OpenRA.Widgets
cachedFrame = frame;
}
Game.Renderer.SpriteRenderer.DrawSprite(sprite, RenderOrigin, worldRenderer, palette);
Game.Renderer.SpriteRenderer.DrawSprite(sprite, RenderOrigin, worldRenderer.Palette(palette));
}
public int FrameCount
{
get { return Game.modData.SpriteLoader.LoadAllSprites(Image).Length-1; }
}
public void RenderNextFrame()
{
if (Frame < FrameCount)
Frame++;
else
Frame = 0;
}
public void RenderPreviousFrame()
{
if (Frame > 0)
Frame--;
else
Frame = FrameCount;
}
public override void Tick()
{
if (LoopAnimation)
RenderNextFrame();
}
}
}

View File

@@ -24,10 +24,15 @@ namespace OpenRA.Widgets
public float MinimumValue = 0;
public float MaximumValue = 1;
public float Value = 0;
public Func<float> GetValue;
protected bool isMoving = false;
public SliderWidget() : base() {}
public SliderWidget()
: base()
{
GetValue = () => Value;
}
public SliderWidget(SliderWidget other)
: base(other)
@@ -38,6 +43,7 @@ namespace OpenRA.Widgets
MaximumValue = other.MaximumValue;
Value = other.Value;
TrackHeight = other.TrackHeight;
GetValue = other.GetValue;
}
void UpdateValue(float newValue)
@@ -53,7 +59,7 @@ namespace OpenRA.Widgets
if (mi.Event == MouseInputEvent.Down && !TakeFocus(mi)) return false;
if (!Focused) return false;
switch( mi.Event )
switch(mi.Event)
{
case MouseInputEvent.Up:
isMoving = false;
@@ -99,6 +105,8 @@ namespace OpenRA.Widgets
if (!IsVisible())
return;
Value = GetValue();
var tr = ThumbRect;
var rb = RenderBounds;
var trackWidth = rb.Width;

View File

@@ -48,6 +48,15 @@ namespace OpenRA.Widgets
return window;
}
public static T LoadWidget<T>(string id, Widget parent, WidgetArgs args) where T : Widget
{
var widget = LoadWidget(id, parent, args) as T;
if (widget == null)
throw new InvalidOperationException(
"Widget {0} is not of type {1}".F(id, typeof(T).Name));
return widget;
}
public static Widget LoadWidget(string id, Widget parent, WidgetArgs args)
{
return Game.modData.WidgetLoader.LoadWidget(args, parent, id);

View File

@@ -31,12 +31,12 @@ namespace OpenRA.Widgets
public static void DrawSHP(Sprite s, float2 pos, WorldRenderer wr)
{
Game.Renderer.SpriteRenderer.DrawSprite(s,pos, wr, "chrome");
Game.Renderer.SpriteRenderer.DrawSprite(s, pos, wr.Palette("chrome"));
}
public static void DrawSHP(Sprite s, float2 pos, WorldRenderer wr, float2 size)
{
Game.Renderer.SpriteRenderer.DrawSprite(s, pos, wr, "chrome", size);
Game.Renderer.SpriteRenderer.DrawSprite(s, pos, wr.Palette("chrome"), size);
}
public static void DrawPanel(string collection, Rectangle Bounds)
@@ -227,6 +227,12 @@ namespace OpenRA.Widgets
return Mod.AllMods[mod].Title;
}
public static string ActiveModId()
{
var mod = Game.modData.Manifest.Mods[0];
return Mod.AllMods[mod].Id;
}
public static string ChooseInitialMap(string map)
{
var availableMaps = Game.modData.AvailableMaps;

View File

@@ -120,7 +120,6 @@ namespace OpenRA.Mods.Cnc
public void StartGame()
{
TestAndContinue();
Game.JoinExternalGame();
}
void TestAndContinue()

View File

@@ -32,10 +32,10 @@ namespace OpenRA.Mods.Cnc.Effects
public void Tick(World world) { anim.Tick(); }
public IEnumerable<Renderable> Render(WorldRenderer wr)
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
yield return new Renderable(anim.Image,
target.CenterLocation.ToFloat2() - new float2(.5f * anim.Image.size.X, anim.Image.size.Y - Game.CellSize),
yield return new SpriteRenderable(anim.Image,
target.CenterLocation.ToFloat2() - new float2(0, 0.5f*anim.Image.size.Y - Game.CellSize),
wr.Palette("effect"), (int)target.CenterLocation.Y);
}

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