Compare commits
218 Commits
playtest-2
...
playtest-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d3a932453 | ||
|
|
622650785c | ||
|
|
362dc8128e | ||
|
|
f8e73afbee | ||
|
|
dbbe026b88 | ||
|
|
4b36a9bf7f | ||
|
|
70a5333b40 | ||
|
|
e8359b08c6 | ||
|
|
cebc0b66a5 | ||
|
|
6ac86cbc1a | ||
|
|
60ceb800e1 | ||
|
|
5f47b7b7fc | ||
|
|
7e2866714c | ||
|
|
7e8313b808 | ||
|
|
bfd28abb70 | ||
|
|
1afc0cbfce | ||
|
|
82bcd19d2e | ||
|
|
49049a715b | ||
|
|
3114a5b85e | ||
|
|
222f17a586 | ||
|
|
91027e9961 | ||
|
|
a06a7a8ccb | ||
|
|
a8bae22dd3 | ||
|
|
daa89f52b6 | ||
|
|
22c1059904 | ||
|
|
c0138966e7 | ||
|
|
0f14263b82 | ||
|
|
dfc6304efa | ||
|
|
632d717af8 | ||
|
|
a74a3aa3b5 | ||
|
|
c90c351692 | ||
|
|
5612a57842 | ||
|
|
6e3c8c9745 | ||
|
|
0920c67017 | ||
|
|
1ed0d80348 | ||
|
|
c41fcb15b0 | ||
|
|
6c79b454bf | ||
|
|
610e40b8d6 | ||
|
|
5038018aa5 | ||
|
|
cce12e781f | ||
|
|
245de3abcb | ||
|
|
11f9fa1942 | ||
|
|
85fa7eb8c4 | ||
|
|
62d7168d41 | ||
|
|
2a053daa2e | ||
|
|
21a903fe2d | ||
|
|
83fc546fbe | ||
|
|
f648223c39 | ||
|
|
583c3f9a64 | ||
|
|
0d9c78670e | ||
|
|
32a37d8c0f | ||
|
|
168ff6dc82 | ||
|
|
03f904155e | ||
|
|
f479f9a32d | ||
|
|
bdbb9d2a9f | ||
|
|
e3ae0ceae3 | ||
|
|
34ea82e267 | ||
|
|
fe7e016e33 | ||
|
|
f901355bef | ||
|
|
60fb45c909 | ||
|
|
9ccfb2c322 | ||
|
|
0c9f39df78 | ||
|
|
7fffb71ac4 | ||
|
|
bf0cee9057 | ||
|
|
55b17f70a8 | ||
|
|
c9318cd2dd | ||
|
|
ffd2588f50 | ||
|
|
c3b9b37122 | ||
|
|
a30d82b333 | ||
|
|
81e2c1d2d7 | ||
|
|
5f2ffc0508 | ||
|
|
00297b607b | ||
|
|
1c702048a1 | ||
|
|
fdd356c2e7 | ||
|
|
1e64a63fa4 | ||
|
|
05f079758a | ||
|
|
b220bbeda2 | ||
|
|
0a358a8846 | ||
|
|
ed43c1b482 | ||
|
|
c505954b07 | ||
|
|
d5279d123f | ||
|
|
9be501fc3d | ||
|
|
1721d7e025 | ||
|
|
881484d06a | ||
|
|
537cfb8fd3 | ||
|
|
41f1ea36fe | ||
|
|
486d340028 | ||
|
|
e2bbd22144 | ||
|
|
43d0363a55 | ||
|
|
b253de508b | ||
|
|
1da82aeba9 | ||
|
|
db4d6aaf22 | ||
|
|
5ea86873c6 | ||
|
|
0f126fd630 | ||
|
|
ecc2a7241f | ||
|
|
fbf68db0c0 | ||
|
|
9ce8c90d25 | ||
|
|
affa9ca336 | ||
|
|
3a1c4d1e6f | ||
|
|
45fbecbcd0 | ||
|
|
02f28f54c7 | ||
|
|
81f035904e | ||
|
|
8e2d712688 | ||
|
|
29b63bc3c0 | ||
|
|
d98cdd5331 | ||
|
|
94905afa3c | ||
|
|
a659892227 | ||
|
|
82eddfdf98 | ||
|
|
063191b09e | ||
|
|
3fc6f62aea | ||
|
|
48f934f188 | ||
|
|
192af5d60f | ||
|
|
53a7b0987b | ||
|
|
a3897dbc15 | ||
|
|
55651738a7 | ||
|
|
22bddf9645 | ||
|
|
2a37e6233e | ||
|
|
c1722e1270 | ||
|
|
95c27ef943 | ||
|
|
3aafde7e18 | ||
|
|
85678de9f0 | ||
|
|
bda4bfa4f3 | ||
|
|
481624e3f0 | ||
|
|
d1f12ff801 | ||
|
|
eae482d413 | ||
|
|
abd774d017 | ||
|
|
f2c630731e | ||
|
|
3e3977776d | ||
|
|
36fbddbb5c | ||
|
|
e33d988301 | ||
|
|
a55167c9ac | ||
|
|
d0a4555a1f | ||
|
|
4724ac6b00 | ||
|
|
d998367d35 | ||
|
|
0e9e7d0a9d | ||
|
|
5e1e5903d6 | ||
|
|
25fae5d109 | ||
|
|
44b8630c71 | ||
|
|
6204bfcabf | ||
|
|
1355a9f837 | ||
|
|
db8f22cdbf | ||
|
|
860ec9d85f | ||
|
|
5f2f25b758 | ||
|
|
3728685c67 | ||
|
|
5e6b8deec1 | ||
|
|
21f2b0df43 | ||
|
|
2100484598 | ||
|
|
ba1a36f26e | ||
|
|
9c9a16d80e | ||
|
|
3d8e215598 | ||
|
|
ce806fdbe6 | ||
|
|
208a3a47fe | ||
|
|
f0708ecfb2 | ||
|
|
653d72086c | ||
|
|
9258ba3ec7 | ||
|
|
d851c5646e | ||
|
|
ac0d3779f1 | ||
|
|
9a0d62dc85 | ||
|
|
59e1703b41 | ||
|
|
e49a439c79 | ||
|
|
b2e9085371 | ||
|
|
8cea309ec6 | ||
|
|
a59dfa555c | ||
|
|
e22126ca02 | ||
|
|
867f112dfe | ||
|
|
24e6e23000 | ||
|
|
498adc86a9 | ||
|
|
a20d1c95c8 | ||
|
|
4a101cf1b1 | ||
|
|
583b11a2b2 | ||
|
|
b3c5137add | ||
|
|
3dc39b2c9d | ||
|
|
5897a44fc5 | ||
|
|
8b0f15f80b | ||
|
|
1262cf0f29 | ||
|
|
b74e63f42d | ||
|
|
e89ee2c131 | ||
|
|
c2321e3eea | ||
|
|
22a49ba7c6 | ||
|
|
4d614e9122 | ||
|
|
e0069fd62a | ||
|
|
c75c72b525 | ||
|
|
007b2c5434 | ||
|
|
b8a932a084 | ||
|
|
61c3932340 | ||
|
|
c2029e9d5c | ||
|
|
d0548d6766 | ||
|
|
5a40c5df03 | ||
|
|
f5ba36a735 | ||
|
|
efe2f2e043 | ||
|
|
6681382819 | ||
|
|
7a578a0679 | ||
|
|
5fee165692 | ||
|
|
0a4669b925 | ||
|
|
109546d20f | ||
|
|
f5c606266d | ||
|
|
facf958bae | ||
|
|
480db8be42 | ||
|
|
7c4e32bd94 | ||
|
|
854a9c4eb7 | ||
|
|
cbb0b17c31 | ||
|
|
f2ab8a8541 | ||
|
|
63d21eac8c | ||
|
|
579ff04f68 | ||
|
|
18d75feb15 | ||
|
|
11da96fe22 | ||
|
|
9201b1cced | ||
|
|
e02397da3e | ||
|
|
6014f648f4 | ||
|
|
2c55a008f1 | ||
|
|
b9be918b6c | ||
|
|
781cbc00d3 | ||
|
|
b9365a149f | ||
|
|
1d4f1c657f | ||
|
|
9a66d7b530 | ||
|
|
3a87b934a6 | ||
|
|
9845d0e035 | ||
|
|
3754e791e5 |
2
Makefile
2
Makefile
@@ -101,7 +101,7 @@ mod_cnc: $(mod_cnc_TARGET)
|
||||
mod_d2k_SRCS := $(shell find OpenRA.Mods.D2k/ -iname '*.cs')
|
||||
mod_d2k_TARGET = mods/d2k/OpenRA.Mods.D2k.dll
|
||||
mod_d2k_KIND = library
|
||||
mod_d2k_DEPS = $(STD_MOD_DEPS) $(mod_ra_TARGET) $(utility_TARGET)
|
||||
mod_d2k_DEPS = $(STD_MOD_DEPS) $(mod_ra_TARGET) $(mod_cnc_TARGET) $(utility_TARGET)
|
||||
mod_d2k_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(mod_ra_TARGET) $(utility_TARGET)
|
||||
mod_d2k_EXTRA_CMDS = mono --debug RALint.exe d2k
|
||||
PROGRAMS += mod_d2k
|
||||
|
||||
30
OpenRA.Editor/Form1.Designer.cs
generated
30
OpenRA.Editor/Form1.Designer.cs
generated
@@ -82,6 +82,9 @@ namespace OpenRA.Editor
|
||||
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
|
||||
this.toolStripStatusLabelFiller = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.toolStripStatusLabelMousePosition = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.copySelectionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.splitContainer1.Panel1.SuspendLayout();
|
||||
this.splitContainer1.Panel2.SuspendLayout();
|
||||
this.splitContainer1.SuspendLayout();
|
||||
@@ -417,8 +420,11 @@ namespace OpenRA.Editor
|
||||
this.resizeToolStripMenuItem,
|
||||
this.showActorNamesToolStripMenuItem,
|
||||
this.showGridToolStripMenuItem,
|
||||
this.toolStripSeparator5,
|
||||
this.fixOpenAreasToolStripMenuItem,
|
||||
this.setupDefaultPlayersMenuItem});
|
||||
this.setupDefaultPlayersMenuItem,
|
||||
this.toolStripSeparator4,
|
||||
this.copySelectionToolStripMenuItem});
|
||||
this.mapToolStripMenuItem.Name = "mapToolStripMenuItem";
|
||||
this.mapToolStripMenuItem.Size = new System.Drawing.Size(43, 23);
|
||||
this.mapToolStripMenuItem.Text = "&Map";
|
||||
@@ -507,6 +513,23 @@ namespace OpenRA.Editor
|
||||
this.toolStripStatusLabelMousePosition.Size = new System.Drawing.Size(22, 17);
|
||||
this.toolStripStatusLabelMousePosition.Text = "0,0";
|
||||
//
|
||||
// copySelectionToolStripMenuItem
|
||||
//
|
||||
this.copySelectionToolStripMenuItem.Name = "copySelectionToolStripMenuItem";
|
||||
this.copySelectionToolStripMenuItem.Size = new System.Drawing.Size(185, 22);
|
||||
this.copySelectionToolStripMenuItem.Text = "Copy Selection";
|
||||
this.copySelectionToolStripMenuItem.Click += new System.EventHandler(this.copySelectionToolStripMenuItem_Click);
|
||||
//
|
||||
// toolStripSeparator4
|
||||
//
|
||||
this.toolStripSeparator4.Name = "toolStripSeparator4";
|
||||
this.toolStripSeparator4.Size = new System.Drawing.Size(182, 6);
|
||||
//
|
||||
// toolStripSeparator5
|
||||
//
|
||||
this.toolStripSeparator5.Name = "toolStripSeparator5";
|
||||
this.toolStripSeparator5.Size = new System.Drawing.Size(182, 6);
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
@@ -520,9 +543,9 @@ namespace OpenRA.Editor
|
||||
this.Name = "Form1";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "OpenRA Editor";
|
||||
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyUp);
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OnFormClosing);
|
||||
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
|
||||
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyUp);
|
||||
this.splitContainer1.Panel1.ResumeLayout(false);
|
||||
this.splitContainer1.Panel2.ResumeLayout(false);
|
||||
this.splitContainer1.ResumeLayout(false);
|
||||
@@ -592,6 +615,9 @@ namespace OpenRA.Editor
|
||||
private System.Windows.Forms.Panel panel1;
|
||||
private System.Windows.Forms.FlowLayoutPanel actorPalette;
|
||||
private System.Windows.Forms.ComboBox actorOwnerChooser;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator5;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
|
||||
private System.Windows.Forms.ToolStripMenuItem copySelectionToolStripMenuItem;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,8 @@ namespace OpenRA.Editor
|
||||
Rules.LoadRules(manifest, map);
|
||||
tileset = Rules.TileSets[map.Tileset];
|
||||
tileset.LoadTiles();
|
||||
var palette = new Palette(FileSystem.Open(tileset.Palette), true);
|
||||
int[] ShadowIndex = { 3, 4 };
|
||||
var palette = new Palette(FileSystem.Open(tileset.Palette), ShadowIndex);
|
||||
|
||||
surface1.Bind(map, tileset, palette);
|
||||
// construct the palette of tiles
|
||||
@@ -545,5 +546,10 @@ namespace OpenRA.Editor
|
||||
var player = actorOwnerChooser.SelectedItem as PlayerReference;
|
||||
surface1.NewActorOwner = player.Name;
|
||||
}
|
||||
|
||||
private void copySelectionToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
surface1.CopySelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,13 +120,45 @@
|
||||
<metadata name="tt.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="saveFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>76, 17</value>
|
||||
</metadata>
|
||||
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>198, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="propertiesToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
||||
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAACCklE
|
||||
QVQ4T6WT30tTUQDHz39QD0UQSpAPIkgPyR60AiUiyoGBL4qZjOyt0tZP9tBDk1AsXIhj93ILdd27DPPH
|
||||
3VwrUrdK2ioJHAgVOdoPaQsarBg43L6ec+akuRsEPnw5D4fP5/vlXg4BQHaSf8LjjwdqaTA2cg+y1Ith
|
||||
qxmi5Tas/SYWk+QRoPgmtNsLcPq7GYX8+XoTqaUL+KR2cAmDxeBgqeBv2NhWzwUFOBk4g5/zRxB3V8Es
|
||||
38AtbzuIw/0RLMrMeyhTs2CzGdRtaOTn1dajvLkAR9VKLNvLcHmoExddzSDKzAfkcjkkEtEiOLxoQpdB
|
||||
z+Er7ScQUg/DL5TD2bsfl5oPoKWnCZ3jehDZGeAC1hwL3uXNs55Jep7Gis/AJWxyTeVe1FXvhq5qH6oP
|
||||
7nlw/NoxGByNIKPT77CezSEeD6Pn+jlEIiGk6X8N+F9zyfLYIZzUVUCwT0J36jy9yX83KjDq7zSADE8t
|
||||
cGB7kpksFt76YDzbgMWVFLzBX0WCTcku8mjiTSm8lkUslcHnH2kK/+aw6k+UCJiESE+9XCApz2Gzu5Gk
|
||||
sEVy4r6got82vRV5blVbIDyZ21rAZrPmL7w5P5s1KxQWXRFtgU15lV8ge2AbdXPY8pAuEIsXWJ6FtAVW
|
||||
+SWSmXWssuZ4vnl+s5nNFl1hMLjP8U1bMGR/wS/+N9tf7o6eMpNtADko6xybtEXLAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="resizeToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
||||
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAADLklE
|
||||
QVQ4T3WS60/ScRjFf39CL3rR1ovurduWbbU2t7ZqdlkXt7Lrym6O1VoXcdYqQ54wKlGxABE1CzWF0nIm
|
||||
lZcoTbyUNJFIzDJSJK+E0qy8wInvz+i2erazfd+cz3m+zw4nTS+FLOeZNim3Vp6UU5sqvf5MSZqnyjhV
|
||||
pfKUvEx54kqp8rCkWHVQVKSJPHtbs+tUgSYiOicVAMfEqfR1dVFCGSqrX94cHQeN/aXhbyD3Zz91uf3U
|
||||
9tFHTQ4fzVtH9BOQVdKcW2F6/eKkWA1zcxu6vUAQMmCzk/3AErLtDyFrrY035z16T3PXin8B5LfqZE6P
|
||||
HyZzKw5FX+Yh4XtjwZKZGV3lNPIkhhojplON3Uc3S9/Q7LDfADKtKYklMsgTkwV7joixKHQr2Nq2fSET
|
||||
5uQwqtsyjcot45R510ozV8f/2iAhsyolCNh9WIyFoRGYHbIGfYN+SVd7Nznzk8ll0FLzKxdZO3xkaOjG
|
||||
GYUxcIIfRxSlPU4N/rn1fS8JJVlU1WCT3npggVLfAIWuHjJtNRIyjIhTPEKM7D6OSop4RcXpwZ2Wl11l
|
||||
gHanmze/c36ioc8jyRlFjSzljxl2daI9Pwuv1Cnw9PRjZeQ1cCcSS68Fza6+IXL2fCG7w0ssmc3o2IR8
|
||||
gbc1PQU9NUp48sNRcUyAkPBL4AIlUbLk3gEvS6YXrW6p0dx9UZ5n4gHjzBmYAAPPYwRwazeiMXYWqjcs
|
||||
wLSVInAHRYWq0TEf9Q+NUEsg+fr9t2nF1U71leyqn+u3vPuIl2298HZ2wnxkO5q2LYOtxoKpK86D23tW
|
||||
r+7zfCVzywAVGT/QVV2LvNDo0Eo0j3mAtbUTiRnFOJ94Axp9LfIKK5Ctq0RDmw9TQuPAsW7bHYNUYLCQ
|
||||
JL2GRAqjtMDYwV+8wzUAWeY9eIfHYW0fxNodxyGWZeOSPBvzl27C5OXnwG0T5irmr79wgdWTNYyVJFL8
|
||||
kAeo8wyBA/rx2jGErBIH0nXPsWmPEHcM9YgURGPS0jMTZfiXGKDfM4Y0fVOgA/UgRQWECTpExaqxUyDF
|
||||
wuUbJjb4H0AQfxtrDqiweLMUc8LEmLEqnv9zUMzM9B1/cM83lepxTAAAAABJRU5ErkJggg==
|
||||
</value>
|
||||
</data>
|
||||
<data name="newToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
@@ -260,41 +292,6 @@
|
||||
z2ki+Wo1CWklROkMCiT8wEm0kXEsCTmrAiTbDtcEpTVdZOS1oDfWk5xZ6RPeQZeUR8zxK0Qe1BO65xjr
|
||||
t0YotXhjAEAeQ7It8ZSESUjkznIq2bYsTGYW29JZxIs2nFEdLOSdfwFwpvLxRKIY2AAAAABJRU5ErkJg
|
||||
gg==
|
||||
</value>
|
||||
</data>
|
||||
<data name="propertiesToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
||||
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAACCklE
|
||||
QVQ4T6WT30tTUQDHz39QD0UQSpAPIkgPyR60AiUiyoGBL4qZjOyt0tZP9tBDk1AsXIhj93ILdd27DPPH
|
||||
3VwrUrdK2ioJHAgVOdoPaQsarBg43L6ec+akuRsEPnw5D4fP5/vlXg4BQHaSf8LjjwdqaTA2cg+y1Ith
|
||||
qxmi5Tas/SYWk+QRoPgmtNsLcPq7GYX8+XoTqaUL+KR2cAmDxeBgqeBv2NhWzwUFOBk4g5/zRxB3V8Es
|
||||
38AtbzuIw/0RLMrMeyhTs2CzGdRtaOTn1dajvLkAR9VKLNvLcHmoExddzSDKzAfkcjkkEtEiOLxoQpdB
|
||||
z+Er7ScQUg/DL5TD2bsfl5oPoKWnCZ3jehDZGeAC1hwL3uXNs55Jep7Gis/AJWxyTeVe1FXvhq5qH6oP
|
||||
7nlw/NoxGByNIKPT77CezSEeD6Pn+jlEIiGk6X8N+F9zyfLYIZzUVUCwT0J36jy9yX83KjDq7zSADE8t
|
||||
cGB7kpksFt76YDzbgMWVFLzBX0WCTcku8mjiTSm8lkUslcHnH2kK/+aw6k+UCJiESE+9XCApz2Gzu5Gk
|
||||
sEVy4r6got82vRV5blVbIDyZ21rAZrPmL7w5P5s1KxQWXRFtgU15lV8ge2AbdXPY8pAuEIsXWJ6FtAVW
|
||||
+SWSmXWssuZ4vnl+s5nNFl1hMLjP8U1bMGR/wS/+N9tf7o6eMpNtADko6xybtEXLAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="resizeToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
||||
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAADLklE
|
||||
QVQ4T3WS60/ScRjFf39CL3rR1ovurduWbbU2t7ZqdlkXt7Lrym6O1VoXcdYqQ54wKlGxABE1CzWF0nIm
|
||||
lZcoTbyUNJFIzDJSJK+E0qy8wInvz+i2erazfd+cz3m+zw4nTS+FLOeZNim3Vp6UU5sqvf5MSZqnyjhV
|
||||
pfKUvEx54kqp8rCkWHVQVKSJPHtbs+tUgSYiOicVAMfEqfR1dVFCGSqrX94cHQeN/aXhbyD3Zz91uf3U
|
||||
9tFHTQ4fzVtH9BOQVdKcW2F6/eKkWA1zcxu6vUAQMmCzk/3AErLtDyFrrY035z16T3PXin8B5LfqZE6P
|
||||
HyZzKw5FX+Yh4XtjwZKZGV3lNPIkhhojplON3Uc3S9/Q7LDfADKtKYklMsgTkwV7joixKHQr2Nq2fSET
|
||||
5uQwqtsyjcot45R510ozV8f/2iAhsyolCNh9WIyFoRGYHbIGfYN+SVd7Nznzk8ll0FLzKxdZO3xkaOjG
|
||||
GYUxcIIfRxSlPU4N/rn1fS8JJVlU1WCT3npggVLfAIWuHjJtNRIyjIhTPEKM7D6OSop4RcXpwZ2Wl11l
|
||||
gHanmze/c36ioc8jyRlFjSzljxl2daI9Pwuv1Cnw9PRjZeQ1cCcSS68Fza6+IXL2fCG7w0ssmc3o2IR8
|
||||
gbc1PQU9NUp48sNRcUyAkPBL4AIlUbLk3gEvS6YXrW6p0dx9UZ5n4gHjzBmYAAPPYwRwazeiMXYWqjcs
|
||||
wLSVInAHRYWq0TEf9Q+NUEsg+fr9t2nF1U71leyqn+u3vPuIl2298HZ2wnxkO5q2LYOtxoKpK86D23tW
|
||||
r+7zfCVzywAVGT/QVV2LvNDo0Eo0j3mAtbUTiRnFOJ94Axp9LfIKK5Ctq0RDmw9TQuPAsW7bHYNUYLCQ
|
||||
JL2GRAqjtMDYwV+8wzUAWeY9eIfHYW0fxNodxyGWZeOSPBvzl27C5OXnwG0T5irmr79wgdWTNYyVJFL8
|
||||
kAeo8wyBA/rx2jGErBIH0nXPsWmPEHcM9YgURGPS0jMTZfiXGKDfM4Y0fVOgA/UgRQWECTpExaqxUyDF
|
||||
wuUbJjb4H0AQfxtrDqiweLMUc8LEmLEqnv9zUMzM9B1/cM83lepxTAAAAABJRU5ErkJggg==
|
||||
</value>
|
||||
</data>
|
||||
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
|
||||
@@ -38,6 +38,12 @@ namespace OpenRA.Editor
|
||||
public bool ShowActorNames;
|
||||
public bool ShowGrid;
|
||||
|
||||
public bool IsPaste { get { return TileSelection != null && ResourceSelection != null; } }
|
||||
public TileReference<ushort, byte>[,] TileSelection;
|
||||
public TileReference<byte, byte>[,] ResourceSelection;
|
||||
public CPos SelectionStart;
|
||||
public CPos SelectionEnd;
|
||||
|
||||
public string NewActorOwner;
|
||||
|
||||
public event Action AfterChange = () => { };
|
||||
@@ -59,7 +65,7 @@ namespace OpenRA.Editor
|
||||
Tool = null;
|
||||
}
|
||||
|
||||
public void SetTool(ITool tool) { Tool = tool; }
|
||||
public void SetTool(ITool tool) { Tool = tool; ClearSelection(); }
|
||||
|
||||
public void BindActorTemplates(IEnumerable<ActorTemplate> templates)
|
||||
{
|
||||
@@ -83,6 +89,8 @@ namespace OpenRA.Editor
|
||||
UpdateStyles();
|
||||
}
|
||||
|
||||
static readonly Pen SelectionPen = new Pen(Color.Blue);
|
||||
static readonly Pen PastePen = new Pen(Color.Green);
|
||||
static readonly Pen CordonPen = new Pen(Color.Red);
|
||||
int2 MousePos;
|
||||
|
||||
@@ -182,12 +190,20 @@ namespace OpenRA.Editor
|
||||
}
|
||||
|
||||
AfterChange();
|
||||
ClearSelection();
|
||||
}
|
||||
|
||||
void Draw()
|
||||
{
|
||||
if (Tool != null) Tool.Apply(this);
|
||||
AfterChange();
|
||||
if (Tool != null)
|
||||
{
|
||||
Tool.Apply(this);
|
||||
AfterChange();
|
||||
}
|
||||
else if (IsPaste)
|
||||
PasteSelection();
|
||||
else
|
||||
SelectionEnd = GetBrushLocationBR();
|
||||
}
|
||||
|
||||
protected override void OnMouseDown(MouseEventArgs e)
|
||||
@@ -199,7 +215,12 @@ namespace OpenRA.Editor
|
||||
if (!IsPanning)
|
||||
{
|
||||
if (e.Button == MouseButtons.Right) Erase();
|
||||
if (e.Button == MouseButtons.Left) Draw();
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
Draw();
|
||||
if (!IsPaste)
|
||||
SelectionStart = SelectionEnd = GetBrushLocation();
|
||||
}
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
@@ -274,6 +295,14 @@ namespace OpenRA.Editor
|
||||
return new CPos(vX / TileSet.TileSize, vY / TileSet.TileSize);
|
||||
}
|
||||
|
||||
public CPos GetBrushLocationBR()
|
||||
{
|
||||
var vX = (int)Math.Floor((MousePos.X - Offset.X) / Zoom);
|
||||
var vY = (int)Math.Floor((MousePos.Y - Offset.Y) / Zoom);
|
||||
return new CPos((vX + TileSet.TileSize - 1) / TileSet.TileSize,
|
||||
(vY + TileSet.TileSize - 1) / TileSet.TileSize);
|
||||
}
|
||||
|
||||
public void DrawActor(SGraphics g, CPos p, ActorTemplate t, ColorPalette cp)
|
||||
{
|
||||
var centered = t.Appearance == null || !t.Appearance.RelativeToTopLeft;
|
||||
@@ -367,6 +396,25 @@ namespace OpenRA.Editor
|
||||
Map.Bounds.Width * TileSet.TileSize * Zoom,
|
||||
Map.Bounds.Height * TileSet.TileSize * Zoom);
|
||||
|
||||
e.Graphics.DrawRectangle(SelectionPen,
|
||||
(SelectionStart.X * TileSet.TileSize * Zoom) + Offset.X,
|
||||
(SelectionStart.Y * TileSet.TileSize * Zoom) + Offset.Y,
|
||||
(SelectionEnd - SelectionStart).X * TileSet.TileSize * Zoom,
|
||||
(SelectionEnd - SelectionStart).Y * TileSet.TileSize * Zoom);
|
||||
|
||||
if (IsPaste)
|
||||
{
|
||||
var loc = GetBrushLocation();
|
||||
var width = Math.Abs((SelectionStart - SelectionEnd).X);
|
||||
var height = Math.Abs((SelectionStart - SelectionEnd).Y);
|
||||
|
||||
e.Graphics.DrawRectangle(PastePen,
|
||||
(loc.X * TileSet.TileSize * Zoom) + Offset.X,
|
||||
(loc.Y * TileSet.TileSize * Zoom) + Offset.Y,
|
||||
width * (TileSet.TileSize * Zoom),
|
||||
height * (TileSet.TileSize * Zoom));
|
||||
}
|
||||
|
||||
foreach (var ar in Map.Actors.Value)
|
||||
{
|
||||
if (ActorTemplates.ContainsKey(ar.Value.Type))
|
||||
@@ -395,6 +443,67 @@ namespace OpenRA.Editor
|
||||
DrawActorBorder(e.Graphics, x.Value.Location(), ActorTemplates[x.Value.Type]);
|
||||
}
|
||||
}
|
||||
|
||||
public void CopySelection()
|
||||
{
|
||||
// Grab tiles and resources within selection (doesn't do actors)
|
||||
var start = SelectionStart;
|
||||
var end = SelectionEnd;
|
||||
|
||||
if (start == end) return;
|
||||
|
||||
int width = Math.Abs((start - end).X);
|
||||
int height = Math.Abs((start - end).Y);
|
||||
|
||||
TileSelection = new TileReference<ushort, byte>[width, height];
|
||||
ResourceSelection = new TileReference<byte, byte>[width, height];
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
//todo: crash prevention
|
||||
TileSelection[x, y] = Map.MapTiles.Value[start.X + x, start.Y + y];
|
||||
ResourceSelection[x, y] = Map.MapResources.Value[start.X + x, start.Y + y];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PasteSelection()
|
||||
{
|
||||
var loc = GetBrushLocation();
|
||||
var width = Math.Abs((SelectionStart - SelectionEnd).X);
|
||||
var height = Math.Abs((SelectionStart - SelectionEnd).Y);
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
var mapX = loc.X + x;
|
||||
var mapY = loc.Y + y;
|
||||
|
||||
//todo: crash prevention for outside of bounds
|
||||
Map.MapTiles.Value[mapX, mapY] = TileSelection[x, y];
|
||||
Map.MapResources.Value[mapX, mapY] = ResourceSelection[x, y];
|
||||
|
||||
var ch = new int2(mapX / ChunkSize, mapY / ChunkSize);
|
||||
if (Chunks.ContainsKey(ch))
|
||||
{
|
||||
Chunks[ch].Dispose();
|
||||
Chunks.Remove(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
AfterChange();
|
||||
}
|
||||
|
||||
void ClearSelection()
|
||||
{
|
||||
SelectionStart = CPos.Zero;
|
||||
SelectionEnd = CPos.Zero;
|
||||
TileSelection = null;
|
||||
ResourceSelection = null;
|
||||
}
|
||||
}
|
||||
|
||||
static class ActorReferenceExts
|
||||
|
||||
@@ -184,6 +184,58 @@ namespace OpenRA
|
||||
return ts.Concat(moreTs);
|
||||
}
|
||||
|
||||
public static Dictionary<TKey, TSource> ToDictionaryWithConflictLog<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, string debugName, Func<TKey, string> logKey, Func<TSource, string> logValue)
|
||||
{
|
||||
return ToDictionaryWithConflictLog(source, keySelector, x => x, debugName, logKey, logValue);
|
||||
}
|
||||
|
||||
public static Dictionary<TKey, TElement> ToDictionaryWithConflictLog<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, string debugName, Func<TKey, string> logKey, Func<TElement, string> logValue)
|
||||
{
|
||||
// Fall back on ToString() if null functions are provided:
|
||||
logKey = logKey ?? (s => s.ToString());
|
||||
logValue = logValue ?? (s => s.ToString());
|
||||
|
||||
// Try to build a dictionary and log all duplicates found (if any):
|
||||
var dupKeys = new Dictionary<TKey, List<string>>();
|
||||
var d = new Dictionary<TKey, TElement>();
|
||||
foreach (var item in source)
|
||||
{
|
||||
TKey key = keySelector(item);
|
||||
TElement element = elementSelector(item);
|
||||
|
||||
// Check for a key conflict:
|
||||
if (d.ContainsKey(key))
|
||||
{
|
||||
List<string> dupKeyMessages;
|
||||
if (!dupKeys.TryGetValue(key, out dupKeyMessages))
|
||||
{
|
||||
// Log the initial conflicting value already inserted:
|
||||
dupKeyMessages = new List<string>();
|
||||
dupKeyMessages.Add(logValue(d[key]));
|
||||
dupKeys.Add(key, dupKeyMessages);
|
||||
}
|
||||
|
||||
// Log this conflicting value:
|
||||
dupKeyMessages.Add(logValue(element));
|
||||
continue;
|
||||
}
|
||||
|
||||
d.Add(key, element);
|
||||
}
|
||||
|
||||
// If any duplicates were found, log it and throw a descriptive error
|
||||
if (dupKeys.Count > 0)
|
||||
{
|
||||
string badKeysFormatted = String.Join(", ", dupKeys.Select(p => "{0}: [{1}]".F(logKey(p.Key), String.Join(",", p.Value.ToArray()))).ToArray());
|
||||
string msg = "{0}, duplicate values found for the following keys: {1}".F(debugName, badKeysFormatted);
|
||||
Log.Write("debug", msg);
|
||||
throw new ArgumentException(msg);
|
||||
}
|
||||
|
||||
// Return the dictionary we built:
|
||||
return d;
|
||||
}
|
||||
|
||||
public static Color ColorLerp(float t, Color c1, Color c2)
|
||||
{
|
||||
return Color.FromArgb(
|
||||
|
||||
@@ -58,7 +58,9 @@ namespace OpenRA.FileFormats
|
||||
isEncrypted = 0 != (signature & (uint)MixFileFlags.Encrypted);
|
||||
if( isEncrypted )
|
||||
{
|
||||
index = ParseRaHeader( s, out dataStart ).ToDictionary(x => x.Hash);
|
||||
index = ParseRaHeader(s, out dataStart).ToDictionaryWithConflictLog(x => x.Hash,
|
||||
"MixFile.RaHeader", null, x => "(offs={0}, len={1})".F(x.Offset, x.Length)
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +68,9 @@ namespace OpenRA.FileFormats
|
||||
s.Seek( 0, SeekOrigin.Begin );
|
||||
|
||||
isEncrypted = false;
|
||||
index = ParseTdHeader(s, out dataStart).ToDictionary(x => x.Hash);
|
||||
index = ParseTdHeader(s, out dataStart).ToDictionaryWithConflictLog(x => x.Hash,
|
||||
"MixFile.TdHeader", null, x => "(offs={0}, len={1})".F(x.Offset, x.Length)
|
||||
);
|
||||
}
|
||||
|
||||
const long headerStart = 84;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace OpenRA.FileFormats
|
||||
public readonly string[]
|
||||
Mods, Folders, Packages, Rules, ServerTraits,
|
||||
Sequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
||||
Weapons, Voices, Music, Movies, TileSets, ChromeMetrics;
|
||||
Weapons, Voices, Notifications, Music, Movies, TileSets, ChromeMetrics;
|
||||
public readonly MiniYaml LoadScreen;
|
||||
public readonly Dictionary<string, Pair<string,int>> Fonts;
|
||||
public readonly int TileSize = 24;
|
||||
@@ -44,6 +44,7 @@ namespace OpenRA.FileFormats
|
||||
ChromeLayout = YamlList(yaml, "ChromeLayout");
|
||||
Weapons = YamlList(yaml, "Weapons");
|
||||
Voices = YamlList(yaml, "Voices");
|
||||
Notifications = YamlList(yaml, "Notifications");
|
||||
Music = YamlList(yaml, "Music");
|
||||
Movies = YamlList(yaml, "Movies");
|
||||
TileSets = YamlList(yaml, "TileSets");
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace OpenRA.FileFormats
|
||||
public string[] AcceptsSmudgeType = { };
|
||||
public bool IsWater = false;
|
||||
public Color Color;
|
||||
public string CustomCursor;
|
||||
|
||||
public TerrainTypeInfo() {}
|
||||
public TerrainTypeInfo(MiniYaml my) { FieldLoader.Load(this, my); }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2012 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,
|
||||
@@ -39,7 +39,7 @@ namespace OpenRA.FileFormats
|
||||
get { return colors; }
|
||||
}
|
||||
|
||||
public Palette(Stream s, bool remapTransparent)
|
||||
public Palette(Stream s, int[] remapShadow)
|
||||
{
|
||||
colors = new uint[256];
|
||||
|
||||
@@ -54,13 +54,9 @@ namespace OpenRA.FileFormats
|
||||
}
|
||||
}
|
||||
|
||||
colors[0] = 0;
|
||||
if (remapTransparent)
|
||||
{
|
||||
colors[1] = 178u << 24; // Hack for d2k; may have side effects
|
||||
colors[3] = 178u << 24;
|
||||
colors[4] = 140u << 24;
|
||||
}
|
||||
colors[0] = 0; //convert black background to transparency
|
||||
foreach (int i in remapShadow)
|
||||
colors[i] = 140u << 24;
|
||||
}
|
||||
|
||||
public Palette(Palette p, IPaletteRemap r)
|
||||
@@ -92,12 +88,12 @@ namespace OpenRA.FileFormats
|
||||
return pal;
|
||||
}
|
||||
|
||||
public static Palette Load( string filename, bool remap )
|
||||
public static Palette Load(string filename, int[] remap)
|
||||
{
|
||||
using(var s = File.OpenRead(filename))
|
||||
return new Palette(s, remap);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IPaletteRemap { Color GetRemappedColor(Color original, int index); }
|
||||
public interface IPaletteRemap { Color GetRemappedColor(Color original, int index); }
|
||||
}
|
||||
|
||||
@@ -19,12 +19,6 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
Dictionary<int, Color> remapColors;
|
||||
|
||||
static int[] GetRemapRamp(int[] Ramp)
|
||||
{
|
||||
var RemapRamp = Ramp.Select(r => r - Ramp[0]).ToArray();
|
||||
return RemapRamp;
|
||||
}
|
||||
|
||||
public static int GetRemapIndex(int[] Ramp, int i)
|
||||
{
|
||||
return Ramp[i];
|
||||
@@ -33,11 +27,18 @@ namespace OpenRA.FileFormats
|
||||
public PlayerColorRemap(int[] Ramp, ColorRamp c)
|
||||
{
|
||||
var c1 = c.GetColor(0);
|
||||
var c2 = c.GetColor(1); /* temptemp: this can be expressed better */
|
||||
var c2 = c.GetColor(1); // temptemp: this can be expressed better
|
||||
|
||||
var baseIndex = Ramp[0];
|
||||
var RemapRamp = GetRemapRamp(Ramp);
|
||||
var baseIndex = Ramp[0];
|
||||
var RemapRamp = Ramp.Select(r => r - Ramp[0]).ToArray();
|
||||
|
||||
if (Ramp[0] > Ramp[15]) // reversed remapping
|
||||
{
|
||||
baseIndex = Ramp[15];
|
||||
for (int i=15; i>0; i--)
|
||||
RemapRamp = Ramp.Select(r => r - Ramp[15]).ToArray();
|
||||
}
|
||||
|
||||
remapColors = RemapRamp.Select((x, i) => Pair.New(baseIndex + i, Exts.ColorLerp(x / 16f, c1, c2)))
|
||||
.ToDictionary(u => u.First, u => u.Second);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace OpenRA
|
||||
{
|
||||
public static Dictionary<string, ActorInfo> Info;
|
||||
public static Dictionary<string, WeaponInfo> Weapons;
|
||||
public static Dictionary<string, VoiceInfo> Voices;
|
||||
public static Dictionary<string, SoundInfo> Voices;
|
||||
public static Dictionary<string, SoundInfo> Notifications;
|
||||
public static Dictionary<string, MusicInfo> Music;
|
||||
public static Dictionary<string, string> Movies;
|
||||
public static Dictionary<string, TileSet> TileSets;
|
||||
@@ -30,7 +31,8 @@ namespace OpenRA
|
||||
// Added support to extend the list of rules (add it to m.LocalRules)
|
||||
Info = LoadYamlRules(m.Rules, map.Rules, (k, y) => new ActorInfo(k.Key.ToLowerInvariant(), k.Value, y));
|
||||
Weapons = LoadYamlRules(m.Weapons, map.Weapons, (k, _) => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
|
||||
Voices = LoadYamlRules(m.Voices, map.Voices, (k, _) => new VoiceInfo(k.Value));
|
||||
Voices = LoadYamlRules(m.Voices, map.Voices, (k, _) => new SoundInfo(k.Value));
|
||||
Notifications = LoadYamlRules(m.Notifications, map.Notifications, (k, _) => new SoundInfo(k.Value));
|
||||
Music = LoadYamlRules(m.Music, new List<MiniYamlNode>(), (k, _) => new MusicInfo(k.Key, k.Value));
|
||||
Movies = LoadYamlRules(m.Movies, new List<MiniYamlNode>(), (k, v) => k.Value.Value);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace OpenRA.GameRules
|
||||
public int ExternalPort = 1234;
|
||||
public bool AdvertiseOnline = true;
|
||||
public string MasterServer = "http://master.open-ra.org/";
|
||||
public bool AllowUPnP = true;
|
||||
public bool AllowUPnP = false;
|
||||
public bool AllowCheats = false;
|
||||
public string Map = null;
|
||||
public string[] Ban = null;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#region Copyright & License Information
|
||||
#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
|
||||
@@ -15,11 +15,12 @@ using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
public class VoiceInfo
|
||||
public class SoundInfo
|
||||
{
|
||||
[FieldLoader.Ignore] public readonly Dictionary<string,string[]> Variants;
|
||||
[FieldLoader.Ignore] public readonly Dictionary<string,string[]> Prefixes;
|
||||
[FieldLoader.Ignore] public readonly Dictionary<string,string[]> Voices;
|
||||
[FieldLoader.Ignore] public readonly Dictionary<string,string[]> Notifications;
|
||||
public readonly string DefaultVariant = ".aud" ;
|
||||
public readonly string DefaultPrefix = "" ;
|
||||
public readonly string[] DisableVariants = { };
|
||||
@@ -34,31 +35,28 @@ namespace OpenRA.GameRules
|
||||
: new Dictionary<string, string[]>();
|
||||
}
|
||||
|
||||
public readonly OpenRA.FileFormats.Lazy<Dictionary<string, VoicePool>> Pools;
|
||||
public readonly OpenRA.FileFormats.Lazy<Dictionary<string, SoundPool>> VoicePools;
|
||||
public readonly OpenRA.FileFormats.Lazy<Dictionary<string, SoundPool>> NotificationsPools;
|
||||
|
||||
public VoiceInfo( MiniYaml y )
|
||||
public SoundInfo( MiniYaml y )
|
||||
{
|
||||
FieldLoader.Load( this, y );
|
||||
Variants = Load(y, "Variants");
|
||||
Prefixes = Load(y, "Prefixes");
|
||||
Voices = Load(y, "Voices");
|
||||
Notifications = Load(y, "Notifications");
|
||||
|
||||
if (!Voices.ContainsKey("Attack"))
|
||||
Voices.Add("Attack", Voices["Move"]);
|
||||
|
||||
if (!Voices.ContainsKey("AttackMove"))
|
||||
Voices.Add("AttackMove", Voices["Move"]);
|
||||
|
||||
Pools = Lazy.New(() => Voices.ToDictionary( a => a.Key, a => new VoicePool(a.Value) ));
|
||||
VoicePools = Lazy.New(() => Voices.ToDictionary( a => a.Key, a => new SoundPool(a.Value) ));
|
||||
NotificationsPools = Lazy.New(() => Notifications.ToDictionary( a => a.Key, a => new SoundPool(a.Value) ));
|
||||
}
|
||||
}
|
||||
|
||||
public class VoicePool
|
||||
public class SoundPool
|
||||
{
|
||||
readonly string[] clips;
|
||||
readonly List<string> liveclips = new List<string>();
|
||||
|
||||
public VoicePool(params string[] clips)
|
||||
public SoundPool(params string[] clips)
|
||||
{
|
||||
this.clips = clips;
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace OpenRA.GameRules
|
||||
public readonly string WaterExplosion = null; // explosion effect on hitting water (usually a splash)
|
||||
public readonly string[] SmudgeType = { }; // type of smudge to apply
|
||||
public readonly int[] Size = { 0, 0 }; // size of the explosion. provide 2 values for a ring effect (outer/inner)
|
||||
public readonly int InfDeath = 0; // infantry death animation to use
|
||||
public readonly int InfDeath = 1; // infantry death animation to use
|
||||
public readonly string ImpactSound = null; // sound to play on impact
|
||||
public readonly string WaterImpactSound = null; // sound to play on impact with water
|
||||
public readonly int Damage = 0; // how much (raw) damage to deal
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2012 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,
|
||||
@@ -25,13 +25,16 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
cursors = new Dictionary<string, CursorSequence>();
|
||||
var sequences = new MiniYaml(null, sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal));
|
||||
var transparent = false;
|
||||
int[] ShadowIndex = { };
|
||||
|
||||
if (sequences.NodesDict.ContainsKey("Transparent"))
|
||||
transparent = true;
|
||||
if (sequences.NodesDict.ContainsKey("ShadowIndex"))
|
||||
{
|
||||
Array.Resize(ref ShadowIndex, ShadowIndex.Length + 1);
|
||||
ShadowIndex[ShadowIndex.Length - 1] = Convert.ToInt32(sequences.NodesDict["ShadowIndex"].Value);
|
||||
}
|
||||
|
||||
foreach (var s in sequences.NodesDict["Palettes"].Nodes)
|
||||
Game.modData.Palette.AddPalette(s.Key, new Palette(FileSystem.Open(s.Value.Value), transparent));
|
||||
Game.modData.Palette.AddPalette(s.Key, new Palette(FileSystem.Open(s.Value.Value), ShadowIndex));
|
||||
|
||||
foreach (var s in sequences.NodesDict["Cursors"].Nodes)
|
||||
LoadSequencesForCursor(s.Key, s.Value);
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace OpenRA
|
||||
[FieldLoader.Ignore] public List<MiniYamlNode> Sequences = 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>();
|
||||
|
||||
// Binary map data
|
||||
[FieldLoader.Ignore] public byte TileFormat = 1;
|
||||
@@ -150,6 +151,7 @@ namespace OpenRA
|
||||
Sequences = NodesOrEmpty(yaml, "Sequences");
|
||||
Weapons = NodesOrEmpty(yaml, "Weapons");
|
||||
Voices = NodesOrEmpty(yaml, "Voices");
|
||||
Notifications = NodesOrEmpty(yaml, "Notifications");
|
||||
|
||||
CustomTerrain = new string[MapSize.X, MapSize.Y];
|
||||
|
||||
@@ -204,6 +206,7 @@ namespace OpenRA
|
||||
root.Add(new MiniYamlNode("Sequences", null, Sequences));
|
||||
root.Add(new MiniYamlNode("Weapons", null, Weapons));
|
||||
root.Add(new MiniYamlNode("Voices", null, Voices));
|
||||
root.Add(new MiniYamlNode("Notifications", null, Notifications));
|
||||
|
||||
var entries = new Dictionary<string, byte[]>();
|
||||
entries.Add("map.bin", SaveBinaryData());
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
<Compile Include="GameRules\MusicInfo.cs" />
|
||||
<Compile Include="GameRules\Rules.cs" />
|
||||
<Compile Include="GameRules\Settings.cs" />
|
||||
<Compile Include="GameRules\VoiceInfo.cs" />
|
||||
<Compile Include="GameRules\SoundInfo.cs" />
|
||||
<Compile Include="GameRules\WeaponInfo.cs" />
|
||||
<Compile Include="Graphics\Animation.cs" />
|
||||
<Compile Include="Graphics\AnimationWithOffset.cs" />
|
||||
@@ -167,7 +167,6 @@
|
||||
<Compile Include="Traits\Health.cs" />
|
||||
<Compile Include="Traits\LintAttributes.cs" />
|
||||
<Compile Include="Traits\Player\DeveloperMode.cs" />
|
||||
<Compile Include="Traits\Player\EvaAlerts.cs" />
|
||||
<Compile Include="Traits\Player\PlayerResources.cs" />
|
||||
<Compile Include="Traits\Render\RenderSimple.cs" />
|
||||
<Compile Include="Traits\RevealsShroud.cs" />
|
||||
@@ -261,4 +260,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -90,11 +90,6 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
public void GiveAdvice(string advice)
|
||||
{
|
||||
Sound.PlayToPlayer(this, advice);
|
||||
}
|
||||
|
||||
public Dictionary<Player, Stance> Stances = new Dictionary<Player, Stance>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,10 @@ namespace OpenRA.Server
|
||||
XTimer gameTimeout;
|
||||
|
||||
volatile bool shutdown = false;
|
||||
public void Shutdown() { shutdown = true; }
|
||||
public void Shutdown()
|
||||
{
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
public Server(IPEndPoint endpoint, string[] mods, ServerSettings settings, ModData modData)
|
||||
{
|
||||
@@ -69,8 +72,43 @@ namespace OpenRA.Server
|
||||
|
||||
randomSeed = (int)DateTime.Now.ToBinary();
|
||||
|
||||
if (settings.AllowUPnP)
|
||||
PortForward();
|
||||
if (Settings.AllowUPnP)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (UPnP.NAT.Discover())
|
||||
{
|
||||
Log.Write("server", "UPnP-enabled router discovered.");
|
||||
Log.Write("server", "Your IP is: {0}", UPnP.NAT.GetExternalIP() );
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Write("server", "No UPnP-enabled router detected.");
|
||||
Settings.AllowUPnP = false;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
OpenRA.Log.Write("server", "Can't discover UPnP-enabled routers: {0}", e);
|
||||
Settings.AllowUPnP = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.AllowUPnP)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (UPnP.NAT.ForwardPort(Port, ProtocolType.Tcp, "OpenRA"))
|
||||
Log.Write("server", "Port {0} (TCP) has been forwarded.", Port);
|
||||
else
|
||||
Settings.AllowUPnP = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
OpenRA.Log.Write("server", "Can not forward ports via UPnP: {0}", e);
|
||||
Settings.AllowUPnP = false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var trait in modData.Manifest.ServerTraits)
|
||||
ServerTraits.Add( modData.ObjectCreator.CreateObject<ServerTrait>(trait) );
|
||||
@@ -102,7 +140,11 @@ namespace OpenRA.Server
|
||||
|
||||
Socket.Select( checkRead, null, null, timeout );
|
||||
if (shutdown)
|
||||
{
|
||||
if (Settings.AllowUPnP)
|
||||
RemovePortforward();
|
||||
break;
|
||||
}
|
||||
|
||||
foreach( Socket s in checkRead )
|
||||
if( s == listener.Server ) AcceptConnection();
|
||||
@@ -117,7 +159,11 @@ namespace OpenRA.Server
|
||||
t.Tick(this);
|
||||
|
||||
if (shutdown)
|
||||
{
|
||||
if (Settings.AllowUPnP)
|
||||
RemovePortforward();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GameStarted = false;
|
||||
@@ -129,20 +175,20 @@ namespace OpenRA.Server
|
||||
try { listener.Stop(); }
|
||||
catch { }
|
||||
} ) { IsBackground = true }.Start();
|
||||
|
||||
}
|
||||
|
||||
void PortForward()
|
||||
void RemovePortforward()
|
||||
{
|
||||
if (UPnP.NAT.Discover())
|
||||
try
|
||||
{
|
||||
Log.Write("server", "UPnP-enabled router discovered.");
|
||||
UPnP.NAT.ForwardPort(Port, ProtocolType.Tcp, "OpenRA"); //might timeout after second try
|
||||
Log.Write("server", "Port {0} (TCP) has been forwarded.", Port);
|
||||
Log.Write("server", "Your IP is: {0}", UPnP.NAT.GetExternalIP() );
|
||||
if (UPnP.NAT.DeleteForwardingRule(Port, ProtocolType.Tcp))
|
||||
Log.Write("server", "Port {0} (TCP) forwarding rules has been removed.", Port);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
OpenRA.Log.Write("server", "Can not remove UPnP portforwarding rules: {0}", e);
|
||||
}
|
||||
else
|
||||
Log.Write("server", "No UPnP-enabled router detected.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* lobby rework todo:
|
||||
|
||||
@@ -19,13 +19,13 @@ namespace UPnP
|
||||
{
|
||||
public class NAT
|
||||
{
|
||||
public static TimeSpan _timeout = new TimeSpan(0, 0, 0, 3);
|
||||
static string _serviceUrl;
|
||||
|
||||
public static bool Discover()
|
||||
{
|
||||
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
|
||||
s.ReceiveTimeout = 3000; //3 seconds
|
||||
string req = "M-SEARCH * HTTP/1.1\r\n" +
|
||||
"HOST: 239.255.255.250:1900\r\n" +
|
||||
"ST:upnp:rootdevice\r\n" +
|
||||
@@ -35,12 +35,9 @@ namespace UPnP
|
||||
IPEndPoint ipe = new IPEndPoint(IPAddress.Broadcast, 1900);
|
||||
byte[] buffer = new byte[0x1000];
|
||||
|
||||
DateTime start = DateTime.Now;
|
||||
|
||||
do
|
||||
try
|
||||
{
|
||||
s.SendTo(data, ipe);
|
||||
|
||||
int length = 0;
|
||||
do
|
||||
{
|
||||
@@ -52,31 +49,48 @@ namespace UPnP
|
||||
resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9);
|
||||
resp = resp.Substring(0, resp.IndexOf("\r")).Trim();
|
||||
if (!string.IsNullOrEmpty(_serviceUrl = GetServiceUrl(resp)))
|
||||
{
|
||||
s.Close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while (length > 0);
|
||||
} while ((start - DateTime.Now) < _timeout);
|
||||
return false;
|
||||
s.Close();
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
s.Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String GetServiceUrl(string resp)
|
||||
{
|
||||
XmlDocument desc = new XmlDocument();
|
||||
desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream());
|
||||
XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable);
|
||||
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
|
||||
XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr);
|
||||
if (!typen.Value.Contains("InternetGatewayDevice"))
|
||||
return null;
|
||||
XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr);
|
||||
if (node == null)
|
||||
return null;
|
||||
Uri respUri = new Uri(resp);
|
||||
Uri combinedUri = new Uri(respUri, node.Value);
|
||||
return combinedUri.AbsoluteUri;
|
||||
HttpWebRequest r = (HttpWebRequest)WebRequest.Create(resp);
|
||||
r.KeepAlive = false;
|
||||
using (WebResponse wres = r.GetResponse())
|
||||
{
|
||||
using (Stream ress = wres.GetResponseStream())
|
||||
{
|
||||
desc.Load(ress);
|
||||
XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable);
|
||||
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
|
||||
XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr);
|
||||
if (!typen.Value.Contains("InternetGatewayDevice"))
|
||||
return null;
|
||||
XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr);
|
||||
if (node == null)
|
||||
return null;
|
||||
Uri respUri = new Uri(resp);
|
||||
Uri combinedUri = new Uri(respUri, node.Value);
|
||||
return combinedUri.AbsoluteUri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ForwardPort(int port, ProtocolType protocol, string description)
|
||||
public static bool ForwardPort(int port, ProtocolType protocol, string description)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_serviceUrl))
|
||||
throw new Exception("No UPnP service available or Discover() has not been called");
|
||||
@@ -87,17 +101,23 @@ namespace UPnP
|
||||
"<NewPortMappingDescription>{3}</NewPortMappingDescription>"+
|
||||
"<NewLeaseDuration>0</NewLeaseDuration></u:AddPortMapping>",
|
||||
port, protocol.ToString().ToUpper(),Dns.GetHostAddresses(Dns.GetHostName())[0], description);
|
||||
SOAPRequest(_serviceUrl, body, "AddPortMapping");
|
||||
if (SOAPRequest(_serviceUrl, body, "AddPortMapping") != null)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void DeleteForwardingRule(int port, ProtocolType protocol)
|
||||
public static bool DeleteForwardingRule(int port, ProtocolType protocol)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_serviceUrl))
|
||||
throw new Exception("No UPnP service available or Discover() has not been called");
|
||||
string body = String.Format("<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
|
||||
"<NewRemoteHost></NewRemoteHost><NewExternalPort>{0}</NewExternalPort>"+
|
||||
"<NewProtocol>{1}</NewProtocol></u:DeletePortMapping>", port, protocol.ToString().ToUpper() );
|
||||
SOAPRequest(_serviceUrl, body, "DeletePortMapping");
|
||||
if (SOAPRequest(_serviceUrl, body, "DeletePortMapping") != null)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public static IPAddress GetExternalIP()
|
||||
@@ -114,24 +134,30 @@ namespace UPnP
|
||||
|
||||
private static XmlDocument SOAPRequest(string url, string soap, string function)
|
||||
{
|
||||
string req = "<?xml version=\"1.0\"?>" +
|
||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
|
||||
"<s:Body>" +
|
||||
soap +
|
||||
"</s:Body>" +
|
||||
"</s:Envelope>";
|
||||
WebRequest r = HttpWebRequest.Create(url);
|
||||
string body = "<?xml version=\"1.0\"?>" +
|
||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
|
||||
"<s:Body>" +
|
||||
soap +
|
||||
"</s:Body>" +
|
||||
"</s:Envelope>";
|
||||
HttpWebRequest r = (HttpWebRequest)WebRequest.Create(url);
|
||||
r.KeepAlive = false;
|
||||
r.Method = "POST";
|
||||
byte[] b = Encoding.UTF8.GetBytes(req);
|
||||
byte[] b = Encoding.UTF8.GetBytes(body);
|
||||
r.Headers.Add("SOAPACTION", "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\"");
|
||||
r.ContentType = "text/xml; charset=\"utf-8\"";
|
||||
r.ContentLength = b.Length;
|
||||
r.GetRequestStream().Write(b, 0, b.Length);
|
||||
Stream newStream = r.GetRequestStream();
|
||||
newStream.Write(b, 0, b.Length);
|
||||
XmlDocument resp = new XmlDocument();
|
||||
WebResponse wres = r.GetResponse();
|
||||
Stream ress = wres.GetResponseStream();
|
||||
resp.Load(ress);
|
||||
return resp;
|
||||
using (WebResponse wres = r.GetResponse())
|
||||
{
|
||||
using (Stream ress = wres.GetResponseStream())
|
||||
{
|
||||
resp.Load(ress);
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,12 @@ namespace OpenRA
|
||||
|
||||
static ISoundSource LoadSound(string filename)
|
||||
{
|
||||
if (!FileSystem.Exists(filename))
|
||||
{
|
||||
Log.Write("debug", "LoadSound, file does not exist: {0}", filename);
|
||||
return null;
|
||||
}
|
||||
|
||||
return LoadSoundRaw(AudLoader.LoadSound(FileSystem.Open(filename)));
|
||||
}
|
||||
|
||||
@@ -66,14 +72,14 @@ namespace OpenRA
|
||||
|
||||
static ISound Play(Player player, string name, bool headRelative, PPos pos, float volumeModifier)
|
||||
{
|
||||
if (player != null && player != player.World.LocalPlayer)
|
||||
if (String.IsNullOrEmpty(name))
|
||||
return null;
|
||||
if (name == "" || name == null)
|
||||
if (player != null && player != player.World.LocalPlayer)
|
||||
return null;
|
||||
|
||||
return soundEngine.Play2D(sounds[name],
|
||||
false, headRelative, pos.ToFloat2(),
|
||||
InternalSoundVolume * volumeModifier);
|
||||
InternalSoundVolume * volumeModifier, true);
|
||||
}
|
||||
|
||||
public static ISound Play(string name) { return Play(null, name, true, PPos.Zero, 1); }
|
||||
@@ -86,7 +92,7 @@ namespace OpenRA
|
||||
public static void PlayVideo(byte[] raw)
|
||||
{
|
||||
rawSource = LoadSoundRaw(raw);
|
||||
video = soundEngine.Play2D(rawSource, false, true, float2.Zero, InternalSoundVolume);
|
||||
video = soundEngine.Play2D(rawSource, false, true, float2.Zero, InternalSoundVolume, false);
|
||||
}
|
||||
|
||||
public static void PlayVideo()
|
||||
@@ -140,10 +146,12 @@ namespace OpenRA
|
||||
}
|
||||
StopMusic();
|
||||
|
||||
var sound = sounds[m.Filename];
|
||||
if (sound == null) return;
|
||||
|
||||
music = soundEngine.Play2D(sound, false, true, float2.Zero, MusicVolume, false);
|
||||
currentMusic = m;
|
||||
MusicPlaying = true;
|
||||
var sound = sounds[m.Filename];
|
||||
music = soundEngine.Play2D(sound, false, true, float2.Zero, MusicVolume);
|
||||
}
|
||||
|
||||
public static void PlayMusic()
|
||||
@@ -238,7 +246,48 @@ namespace OpenRA
|
||||
get { return (video != null) ? video.SeekPosition : 0; }
|
||||
}
|
||||
|
||||
// Returns true if it played a phrase
|
||||
// Returns true if played successfully
|
||||
public static bool PlayPredefined(Player p, Actor voicedUnit, string type, string definition, string variant)
|
||||
{
|
||||
if (definition == null) return false;
|
||||
|
||||
if (Rules.Voices == null) return false;
|
||||
if (Rules.Notifications == null) return false;
|
||||
var rules = (voicedUnit != null) ? Rules.Voices[type] : Rules.Notifications[type];
|
||||
if (rules == null) return false;
|
||||
|
||||
var ID = (voicedUnit != null) ? voicedUnit.ActorID : 0;
|
||||
|
||||
var clip = (voicedUnit != null) ? rules.VoicePools.Value[definition].GetNext() : rules.NotificationsPools.Value[definition].GetNext();
|
||||
if (clip == null) return false;
|
||||
|
||||
var suffix = rules.DefaultVariant;
|
||||
var prefix = rules.DefaultPrefix;
|
||||
|
||||
if (voicedUnit != null)
|
||||
{
|
||||
if (!rules.VoicePools.Value.ContainsKey("Attack"))
|
||||
rules.VoicePools.Value.Add("Attack", rules.VoicePools.Value["Move"]);
|
||||
|
||||
if (!rules.VoicePools.Value.ContainsKey("AttackMove"))
|
||||
rules.VoicePools.Value.Add("AttackMove", rules.VoicePools.Value["Move"]);
|
||||
}
|
||||
|
||||
if (variant != null)
|
||||
{
|
||||
if (rules.Variants.ContainsKey(variant) && !rules.DisableVariants.Contains(definition))
|
||||
suffix = rules.Variants[variant][ID % rules.Variants[variant].Length];
|
||||
if (rules.Prefixes.ContainsKey(variant) && !rules.DisablePrefixes.Contains(definition))
|
||||
prefix = rules.Prefixes[variant][ID % rules.Prefixes[variant].Length];
|
||||
}
|
||||
|
||||
if (p == null)
|
||||
Play(prefix + clip + suffix);
|
||||
else
|
||||
PlayToPlayer(p, prefix + clip + suffix);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool PlayVoice(string phrase, Actor voicedUnit, string variant)
|
||||
{
|
||||
if (voicedUnit == null) return false;
|
||||
@@ -248,28 +297,24 @@ namespace OpenRA
|
||||
if (mi == null) return false;
|
||||
if (mi.Voice == null) return false;
|
||||
|
||||
var vi = Rules.Voices[mi.Voice.ToLowerInvariant()];
|
||||
var type = mi.Voice.ToLowerInvariant();
|
||||
|
||||
if (!vi.Pools.Value.ContainsKey(phrase))
|
||||
return false;
|
||||
return PlayPredefined(null, voicedUnit, type, phrase, variant);
|
||||
}
|
||||
|
||||
var clip = vi.Pools.Value[phrase].GetNext();
|
||||
if (clip == null)
|
||||
return false;
|
||||
public static bool PlayNotification(Player player, string type, string notification, string variant)
|
||||
{
|
||||
if (type == null) return false;
|
||||
if (notification == null) return false;
|
||||
|
||||
var variantExt = (vi.Variants.ContainsKey(variant) && !vi.DisableVariants.Contains(phrase)) ?
|
||||
vi.Variants[variant][voicedUnit.ActorID % vi.Variants[variant].Length] : vi.DefaultVariant;
|
||||
var prefix = (vi.Prefixes.ContainsKey(variant) && !vi.DisablePrefixes.Contains(phrase)) ?
|
||||
vi.Prefixes[variant][voicedUnit.ActorID % vi.Prefixes[variant].Length] : vi.DefaultPrefix;
|
||||
Play(prefix + clip + variantExt);
|
||||
return true;
|
||||
return PlayPredefined(player, null, type.ToLowerInvariant(), notification, variant);
|
||||
}
|
||||
}
|
||||
|
||||
interface ISoundEngine
|
||||
{
|
||||
ISoundSource AddSoundSourceFromMemory(byte[] data, int channels, int sampleBits, int sampleRate);
|
||||
ISound Play2D(ISoundSource sound, bool loop, bool relative, float2 pos, float volume);
|
||||
ISound Play2D(ISoundSource sound, bool loop, bool relative, float2 pos, float volume, bool attenuateVolume);
|
||||
float Volume { get; set; }
|
||||
void PauseSound(ISound sound, bool paused);
|
||||
void StopSound(ISound sound);
|
||||
@@ -290,8 +335,17 @@ namespace OpenRA
|
||||
|
||||
class OpenAlSoundEngine : ISoundEngine
|
||||
{
|
||||
class PoolSlot
|
||||
{
|
||||
public bool isActive;
|
||||
public int frameStarted;
|
||||
public float2 pos;
|
||||
public bool isRelative;
|
||||
public ISoundSource sound;
|
||||
}
|
||||
|
||||
float volume = 1f;
|
||||
Dictionary<int, bool> sourcePool = new Dictionary<int, bool>();
|
||||
Dictionary<int, PoolSlot> sourcePool = new Dictionary<int, PoolSlot>();
|
||||
const int POOL_SIZE = 32;
|
||||
|
||||
public OpenAlSoundEngine()
|
||||
@@ -315,7 +369,7 @@ namespace OpenRA
|
||||
return;
|
||||
}
|
||||
|
||||
sourcePool.Add(source, false);
|
||||
sourcePool.Add(source, new PoolSlot() { isActive = false });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,9 +377,9 @@ namespace OpenRA
|
||||
{
|
||||
foreach (var kvp in sourcePool)
|
||||
{
|
||||
if (!kvp.Value)
|
||||
if (!kvp.Value.isActive)
|
||||
{
|
||||
sourcePool[kvp.Key] = true;
|
||||
sourcePool[kvp.Key].isActive = true;
|
||||
return kvp.Key;
|
||||
}
|
||||
}
|
||||
@@ -343,9 +397,9 @@ namespace OpenRA
|
||||
return -1;
|
||||
|
||||
foreach (int i in freeSources)
|
||||
sourcePool[i] = false;
|
||||
sourcePool[i].isActive = false;
|
||||
|
||||
sourcePool[freeSources[0]] = true;
|
||||
sourcePool[freeSources[0]].isActive = true;
|
||||
|
||||
return freeSources[0];
|
||||
}
|
||||
@@ -355,10 +409,62 @@ namespace OpenRA
|
||||
return new OpenAlSoundSource(data, channels, sampleBits, sampleRate);
|
||||
}
|
||||
|
||||
public ISound Play2D(ISoundSource sound, bool loop, bool relative, float2 pos, float volume)
|
||||
const int maxInstancesPerFrame = 3;
|
||||
const int groupDistance = 64;
|
||||
const int groupDistanceSqr = groupDistance * groupDistance;
|
||||
|
||||
public ISound Play2D(ISoundSource sound, bool loop, bool relative, float2 pos, float volume, bool attenuateVolume)
|
||||
{
|
||||
if (sound == null)
|
||||
{
|
||||
Log.Write("debug", "Attempt to Play2D a null `ISoundSource`");
|
||||
return null;
|
||||
}
|
||||
|
||||
var world = Game.orderManager.world;
|
||||
int currFrame = world != null ? world.FrameNumber : 0;
|
||||
float atten = 1f;
|
||||
|
||||
// Check if max # of instances-per-location reached:
|
||||
if (attenuateVolume)
|
||||
{
|
||||
int instances = 0, activeCount = 0;
|
||||
foreach (var s in sourcePool.Values)
|
||||
{
|
||||
if (!s.isActive)
|
||||
continue;
|
||||
if (s.isRelative != relative)
|
||||
continue;
|
||||
|
||||
++activeCount;
|
||||
if (s.sound != sound)
|
||||
continue;
|
||||
if (currFrame - s.frameStarted >= 5)
|
||||
continue;
|
||||
|
||||
// Too far away to count?
|
||||
var lensqr = (s.pos - pos).LengthSquared;
|
||||
if (lensqr >= groupDistanceSqr)
|
||||
continue;
|
||||
|
||||
// If we are starting too many instances of the same sound within a short time then stop this one:
|
||||
if (++instances == maxInstancesPerFrame)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Attenuate a little bit based on number of active sounds:
|
||||
atten = 0.66f * ((POOL_SIZE - activeCount * 0.5f) / POOL_SIZE);
|
||||
}
|
||||
|
||||
int source = GetSourceFromPool();
|
||||
return new OpenAlSound(source, (sound as OpenAlSoundSource).buffer, loop, relative, pos, volume);
|
||||
if (source == -1) return null;
|
||||
|
||||
var slot = sourcePool[source];
|
||||
slot.pos = pos;
|
||||
slot.frameStarted = currFrame;
|
||||
slot.sound = sound;
|
||||
slot.isRelative = relative;
|
||||
return new OpenAlSound(source, (sound as OpenAlSoundSource).buffer, loop, relative, pos, volume * atten);
|
||||
}
|
||||
|
||||
public float Volume
|
||||
@@ -369,6 +475,8 @@ namespace OpenRA
|
||||
|
||||
public void PauseSound(ISound sound, bool paused)
|
||||
{
|
||||
if (sound == null) return;
|
||||
|
||||
int key = ((OpenAlSound)sound).source;
|
||||
int state;
|
||||
Al.alGetSourcei(key, Al.AL_SOURCE_STATE, out state);
|
||||
@@ -410,6 +518,8 @@ namespace OpenRA
|
||||
|
||||
public void StopSound(ISound sound)
|
||||
{
|
||||
if (sound == null) return;
|
||||
|
||||
int key = ((OpenAlSound)sound).source;
|
||||
int state;
|
||||
Al.alGetSourcei(key, Al.AL_SOURCE_STATE, out state);
|
||||
@@ -467,15 +577,14 @@ namespace OpenRA
|
||||
if (source == -1) return;
|
||||
this.source = source;
|
||||
Al.alSourcef(source, Al.AL_PITCH, 1f);
|
||||
Al.alSourcef(source, Al.AL_GAIN, 1f);
|
||||
Volume = volume;
|
||||
Al.alSource3f(source, Al.AL_POSITION, pos.X, pos.Y, 0f);
|
||||
Al.alSource3f(source, Al.AL_VELOCITY, 0f, 0f, 0f);
|
||||
Al.alSourcei(source, Al.AL_BUFFER, buffer);
|
||||
Al.alSourcei(source, Al.AL_LOOPING, looping ? Al.AL_TRUE : Al.AL_FALSE);
|
||||
Al.alSourcei(source, Al.AL_SOURCE_RELATIVE, relative ? 1 : 0);
|
||||
Al.alSourcef(source, Al.AL_REFERENCE_DISTANCE, 200);
|
||||
Al.alSourcef(source, Al.AL_MAX_DISTANCE, 1500);
|
||||
Volume = volume;
|
||||
Al.alSourcef(source, Al.AL_REFERENCE_DISTANCE, 160);
|
||||
Al.alSourcef(source, Al.AL_MAX_DISTANCE, 3200 / Game.viewport.Zoom);
|
||||
Al.alSourcePlay(source);
|
||||
}
|
||||
|
||||
@@ -517,7 +626,7 @@ namespace OpenRA
|
||||
return new NullSoundSource();
|
||||
}
|
||||
|
||||
public ISound Play2D(ISoundSource sound, bool loop, bool relative, float2 pos, float volume)
|
||||
public ISound Play2D(ISoundSource sound, bool loop, bool relative, float2 pos, float volume, bool attenuateVolume)
|
||||
{
|
||||
return new NullSound();
|
||||
}
|
||||
|
||||
@@ -13,13 +13,14 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
[STAThread]
|
||||
static void Main( string[] args )
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// brutal hack
|
||||
Application.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
@@ -32,19 +33,60 @@ namespace OpenRA
|
||||
|
||||
try
|
||||
{
|
||||
Run( args );
|
||||
Run(args);
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.AddChannel("exception", "exception.log");
|
||||
Log.Write("exception", "{0}", e.ToString());
|
||||
throw;
|
||||
var rpt = BuildExceptionReport(e).ToString();
|
||||
Log.Write("exception", "{0}", rpt);
|
||||
Console.Error.WriteLine(rpt);
|
||||
}
|
||||
}
|
||||
|
||||
static void Run( string[] args )
|
||||
static StringBuilder BuildExceptionReport(Exception e)
|
||||
{
|
||||
Game.Initialize( new Arguments(args) );
|
||||
return BuildExceptionReport(e, new StringBuilder(), 0);
|
||||
}
|
||||
|
||||
static void Indent(StringBuilder sb, int d)
|
||||
{
|
||||
sb.Append(new string(' ', d * 2));
|
||||
}
|
||||
|
||||
static StringBuilder BuildExceptionReport(Exception e, StringBuilder sb, int d)
|
||||
{
|
||||
if (e == null) return sb;
|
||||
|
||||
sb.AppendFormat("Exception of type `{0}`: {1}", e.GetType().FullName, e.Message);
|
||||
|
||||
if (e is TypeLoadException)
|
||||
{
|
||||
var tle = (TypeLoadException)e;
|
||||
sb.AppendLine();
|
||||
Indent(sb, d);
|
||||
sb.AppendFormat("TypeName=`{0}`", tle.TypeName);
|
||||
}
|
||||
else // TODO: more exception types
|
||||
{
|
||||
}
|
||||
|
||||
if (e.InnerException != null)
|
||||
{
|
||||
sb.AppendLine();
|
||||
Indent(sb, d); sb.Append("Inner ");
|
||||
BuildExceptionReport(e.InnerException, sb, d + 1);
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
Indent(sb, d); sb.Append(e.StackTrace);
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
static void Run(string[] args)
|
||||
{
|
||||
Game.Initialize(new Arguments(args));
|
||||
GC.Collect();
|
||||
Game.Run();
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
#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
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class EvaAlertsInfo : TraitInfo<EvaAlerts>
|
||||
{
|
||||
// Sound effects
|
||||
public readonly string RadarUp = "radaron2.aud";
|
||||
public readonly string RadarDown = "radardn1.aud";
|
||||
|
||||
public readonly string CashTickUp = "cashup1.aud";
|
||||
public readonly string CashTickDown = "cashdn1.aud";
|
||||
|
||||
// Build Palette
|
||||
public readonly string BuildingCannotPlaceAudio = "nodeply1.aud";
|
||||
public readonly string NewOptions = "newopt1.aud";
|
||||
|
||||
// For manual powerup/down in ra-ng
|
||||
public readonly string DisablePower = "bleep11.aud";
|
||||
public readonly string EnablePower = "bleep12.aud";
|
||||
|
||||
// Eva speech
|
||||
public readonly string Repairing = "repair1.aud";
|
||||
public readonly string LowPower = "lopower1.aud";
|
||||
public readonly string SilosNeeded = "silond1.aud";
|
||||
public readonly string PrimaryBuildingSelected = "pribldg1.aud";
|
||||
|
||||
// Special powers
|
||||
public readonly string AbilityInsufficientPower = "nopowr1.aud";
|
||||
|
||||
public readonly string LevelUp = "hydrod1.aud";
|
||||
}
|
||||
|
||||
public class EvaAlerts {}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2012 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,
|
||||
@@ -136,8 +136,6 @@ namespace OpenRA.Traits
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
var eva = self.World.WorldActor.Info.Traits.Get<EvaAlertsInfo>();
|
||||
|
||||
if(cashtickallowed > 0) {
|
||||
cashtickallowed = cashtickallowed - 1;
|
||||
}
|
||||
@@ -152,7 +150,7 @@ namespace OpenRA.Traits
|
||||
if (--nextSiloAdviceTime <= 0)
|
||||
{
|
||||
if (Ore > 0.8*OreCapacity)
|
||||
Owner.GiveAdvice(eva.SilosNeeded);
|
||||
Sound.PlayNotification(Owner, "Speech", "SilosNeeded", Owner.Country.Race);
|
||||
|
||||
nextSiloAdviceTime = AdviceInterval;
|
||||
}
|
||||
@@ -192,21 +190,19 @@ namespace OpenRA.Traits
|
||||
|
||||
public void playCashTickUp(Actor self)
|
||||
{
|
||||
var eva = self.World.WorldActor.Info.Traits.Get<EvaAlertsInfo>();
|
||||
if (Game.Settings.Sound.SoundCashTickType != SoundCashTicks.Disabled)
|
||||
{
|
||||
Sound.PlayToPlayer(self.Owner, eva.CashTickUp);
|
||||
Sound.PlayNotification(self.Owner, "Sounds", "CashTickUp", self.Owner.Country.Race);
|
||||
}
|
||||
}
|
||||
|
||||
public void playCashTickDown(Actor self)
|
||||
{
|
||||
var eva = self.World.WorldActor.Info.Traits.Get<EvaAlertsInfo>();
|
||||
if (
|
||||
Game.Settings.Sound.SoundCashTickType == SoundCashTicks.Extreme ||
|
||||
(Game.Settings.Sound.SoundCashTickType == SoundCashTicks.Normal && cashtickallowed == 0)
|
||||
) {
|
||||
Sound.PlayToPlayer(self.Owner, eva.CashTickDown);
|
||||
Sound.PlayNotification(self.Owner, "Sounds", "CashTickDown", self.Owner.Country.Race);
|
||||
cashtickallowed = 3;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace OpenRA.Traits
|
||||
public interface IResolveOrder { void ResolveOrder(Actor self, Order order); }
|
||||
public interface IValidateOrder { bool OrderValidation(OrderManager orderManager, World world, int clientId, Order order); }
|
||||
public interface IOrderVoice { string VoicePhraseForOrder(Actor self, Order order); }
|
||||
public interface INotify { void Play(Player p, string notification); }
|
||||
public interface INotifySold { void Selling(Actor self); void Sold(Actor self); }
|
||||
public interface INotifyDamage { void Damaged(Actor self, AttackInfo e); }
|
||||
public interface INotifyDamageStateChanged { void DamageStateChanged(Actor self, AttackInfo e); }
|
||||
|
||||
@@ -22,8 +22,6 @@ namespace OpenRA.Widgets
|
||||
public bool Depressed = false;
|
||||
public int VisualHeight = ChromeMetrics.Get<int>("ButtonDepth");
|
||||
public string Font = ChromeMetrics.Get<string>("ButtonFont");
|
||||
public string ClickSound = null;
|
||||
public string ClickDisabledSound = null;
|
||||
public bool Disabled = false;
|
||||
public Func<string> GetText;
|
||||
public Func<bool> IsDisabled;
|
||||
@@ -73,10 +71,10 @@ namespace OpenRA.Widgets
|
||||
if (!IsDisabled())
|
||||
{
|
||||
OnKeyPress(e);
|
||||
Sound.Play(ClickSound);
|
||||
Sound.PlayNotification(null, "Sounds", "ClickSound", null);
|
||||
}
|
||||
else
|
||||
Sound.Play(ClickDisabledSound);
|
||||
Sound.PlayNotification(null, "Sounds", "ClickDisabledSound", null);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -105,12 +103,12 @@ namespace OpenRA.Widgets
|
||||
{
|
||||
OnMouseDown(mi);
|
||||
Depressed = true;
|
||||
Sound.Play(ClickSound);
|
||||
Sound.PlayNotification(null, "Sounds", "ClickSound", null);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoseFocus(mi);
|
||||
Sound.Play(ClickDisabledSound);
|
||||
Sound.PlayNotification(null, "Sounds", "ClickDisabledSound", null);
|
||||
}
|
||||
}
|
||||
else if (mi.Event == MouseInputEvent.Move && Focused)
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace OpenRA
|
||||
return selectable != null && selectable.Voice != null;
|
||||
}
|
||||
|
||||
public static VoiceInfo GetVoice(this Actor a)
|
||||
public static SoundInfo GetVoice(this Actor a)
|
||||
{
|
||||
var selectable = a.Info.Traits.GetOrDefault<SelectableInfo>();
|
||||
if (selectable == null) return null;
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
var mobile = self.Trait<Mobile>();
|
||||
self.QueueActivity(mobile.ScriptedMove(left));
|
||||
self.QueueActivity(new Teleport(right));
|
||||
self.QueueActivity(new SimpleTeleport(right));
|
||||
self.QueueActivity(new CallFunc(() => LoopTrack(self,left,right)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Cnc
|
||||
{
|
||||
public class ProductionAirdropInfo : ProductionInfo
|
||||
{
|
||||
public readonly string ReadyAudio = "reinfor1.aud";
|
||||
public readonly string ReadyAudio = "Reinforce";
|
||||
[ActorReference] public readonly string ActorType = "c17";
|
||||
|
||||
public override object Create(ActorInitializer init) { return new ProductionAirdrop(this); }
|
||||
@@ -66,7 +66,7 @@ namespace OpenRA.Mods.Cnc
|
||||
|
||||
rb.PlayCustomAnimRepeating(self, "idle");
|
||||
self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit));
|
||||
Sound.PlayToPlayer(self.Owner, (Info as ProductionAirdropInfo).ReadyAudio);
|
||||
Sound.PlayNotification(self.Owner, "Speech", (Info as ProductionAirdropInfo).ReadyAudio, self.Owner.Country.Race);
|
||||
}));
|
||||
a.QueueActivity(Fly.ToCell(endPos));
|
||||
a.QueueActivity(new RemoveSelf());
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace OpenRA.Mods.Cnc
|
||||
{
|
||||
/* altitude of the cargo, relative to us. -ve is underneath us */
|
||||
public readonly int RelativeAltitude = 0;
|
||||
public readonly string[] PassengerTypes;
|
||||
|
||||
public object Create(ActorInitializer init) { return new RenderCargo(init.self, this); }
|
||||
}
|
||||
@@ -49,7 +50,12 @@ namespace OpenRA.Mods.Cnc
|
||||
cargoFacing.Facing = facing.Facing;
|
||||
}
|
||||
|
||||
return r.Concat(cargo.Passengers.SelectMany(a => a.Render())
|
||||
var visiblePassengers = (Info.PassengerTypes != null && Info.PassengerTypes.Length > 0)
|
||||
? cargo.Passengers.Where(p =>
|
||||
Info.PassengerTypes.Contains(p.Trait<Passenger>().info.CargoType))
|
||||
: cargo.Passengers;
|
||||
|
||||
return r.Concat(visiblePassengers.SelectMany(a => a.Render())
|
||||
.Select(a => a.WithPos(a.Pos - new float2(0, Info.RelativeAltitude))
|
||||
.WithZOffset(a.ZOffset + Info.RelativeAltitude)));
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Cnc
|
||||
[ActorReference] public readonly string ViceroidActor = "vice";
|
||||
public readonly int Probability = 10;
|
||||
public readonly string Owner = "Creeps";
|
||||
public readonly int InfDeath = 5;
|
||||
public readonly int InfDeath = 6;
|
||||
|
||||
public object Create(ActorInitializer init) { return new SpawnViceroid(this); }
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
|
||||
// TODO: Create a mechanism to do things like this cleaner. Also needed for scripted missions
|
||||
Action onQuit = () =>
|
||||
{
|
||||
Sound.Play("batlcon1.aud");
|
||||
Sound.PlayNotification(null, "Speech", "Leave", null);
|
||||
resumeDisabled = true;
|
||||
Game.RunAfterDelay(1200, () => mpe.Fade(CncMenuPaletteEffect.EffectType.Black));
|
||||
Game.RunAfterDelay(1200 + 40 * mpe.Info.FadeLength, () =>
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace OpenRA.Mods.Cnc.Widgets
|
||||
else if (CurrentQueue.BuildableItems().Any(a => a.Name == clicked.Name))
|
||||
{
|
||||
Sound.Play(TabClick);
|
||||
Sound.Play(CurrentQueue.Info.QueuedAudio);
|
||||
Sound.PlayNotification(world.LocalPlayer, "Speech", CurrentQueue.Info.QueuedAudio, world.LocalPlayer.Country.Race);
|
||||
world.IssueOrder(Order.StartProduction(CurrentQueue.self, clicked.Name,
|
||||
Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1));
|
||||
}
|
||||
@@ -164,13 +164,13 @@ namespace OpenRA.Mods.Cnc.Widgets
|
||||
// instant cancel of things we havent started yet and things that are finished
|
||||
if (first.Paused || first.Done || first.TotalCost == first.RemainingCost)
|
||||
{
|
||||
Sound.Play(CurrentQueue.Info.CancelledAudio);
|
||||
Sound.PlayNotification(world.LocalPlayer, "Speech", CurrentQueue.Info.CancelledAudio, world.LocalPlayer.Country.Race);
|
||||
world.IssueOrder(Order.CancelProduction(CurrentQueue.self, clicked.Name,
|
||||
Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
Sound.Play(CurrentQueue.Info.OnHoldAudio);
|
||||
Sound.PlayNotification(world.LocalPlayer, "Speech", CurrentQueue.Info.OnHoldAudio, world.LocalPlayer.Country.Race);
|
||||
world.IssueOrder(Order.PauseProduction(CurrentQueue.self, clicked.Name, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,6 @@ namespace OpenRA.Mods.Cnc.Widgets
|
||||
class ProductionTabsWidget : Widget
|
||||
{
|
||||
public readonly string PaletteWidget = null;
|
||||
public readonly string ClickSound = null;
|
||||
public readonly string DisabledClickSound = null;
|
||||
public readonly float ScrollVelocity = 4f;
|
||||
public readonly int TabWidth = 30;
|
||||
public readonly int ArrowWidth = 20;
|
||||
@@ -248,9 +246,9 @@ namespace OpenRA.Mods.Cnc.Widgets
|
||||
if (leftPressed || rightPressed)
|
||||
{
|
||||
if ((leftPressed && !leftDisabled) || (rightPressed && !rightDisabled))
|
||||
Sound.Play(ClickSound);
|
||||
Sound.PlayNotification(null, "Sounds", "ClickSound", null);
|
||||
else
|
||||
Sound.Play(DisabledClickSound);
|
||||
Sound.PlayNotification(null, "Sounds", "ClickDisabledSound", null);
|
||||
}
|
||||
|
||||
// Check production tabs
|
||||
@@ -258,7 +256,7 @@ namespace OpenRA.Mods.Cnc.Widgets
|
||||
if (offsetloc.X > 0 && offsetloc.X < ContentWidth)
|
||||
{
|
||||
CurrentQueue = Groups[queueGroup].Tabs[offsetloc.X/(TabWidth - 1)].Queue;
|
||||
Sound.Play(ClickSound);
|
||||
Sound.PlayNotification(null, "Sounds", "ClickSound", null);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -270,7 +268,7 @@ namespace OpenRA.Mods.Cnc.Widgets
|
||||
if (e.Event != KeyInputEvent.Down) return false;
|
||||
if (e.KeyName == "tab")
|
||||
{
|
||||
Sound.Play(ClickSound);
|
||||
Sound.PlayNotification(null, "Sounds", "ClickSound", null);
|
||||
SelectNextTab(e.Modifiers.HasModifier(Modifiers.Shift));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -102,9 +102,9 @@ namespace OpenRA.Mods.D2k.Widgets.Logic
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2083", "2114", Path.Combine(PathToSHPs, "devast"), "--vehicle"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2115", "2146", Path.Combine(PathToSHPs, "combathturret"), "--vehicle"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2147", "2148", Path.Combine(PathToSHPs, "deathhandmissile")},
|
||||
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2245", "2284", Path.Combine(PathToSHPs, "saboteur"), "--infantry"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2325", "2388", Path.Combine(PathToSHPs, "saboteurdeath"), "--infantrydeath"},
|
||||
//rifleinfantry repetitions?
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2389", "2420", Path.Combine(PathToSHPs, "deviatortank"), "--vehicle"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2421", "2452", Path.Combine(PathToSHPs, "raider"), "--vehicle"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2453", "2484", Path.Combine(PathToSHPs, "combato"), "--vehicle"},
|
||||
@@ -164,7 +164,14 @@ namespace OpenRA.Mods.D2k.Widgets.Logic
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2991", "2992", Path.Combine(PathToSHPs, "starporto"), "--building"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2993", "2995", Path.Combine(PathToSHPs, "lighto"), "--building"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2996", "2997", Path.Combine(PathToSHPs, "palaceo"), "--building"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "3370", "3380", Path.Combine(PathToSHPs, "unload"), "--vehicle"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2998", "2998", Path.Combine(PathToSHPs, "sietch"), "--building"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "2999", "3000", Path.Combine(PathToSHPs, "starportc"), "--building"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "3001", "3003", Path.Combine(PathToSHPs, "heavyc"), "--building"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "3004", "3005", Path.Combine(PathToSHPs, "palacec"), "--building"},
|
||||
//conyardc repetition
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "3008", "3013", Path.Combine(PathToSHPs, "plates")},
|
||||
//projectiles
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "3370", "3380", Path.Combine(PathToSHPs, "unload"), "--projectile"},
|
||||
//explosions
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "3549", "3564", Path.Combine(PathToSHPs, "wormjaw")},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "3565", "3585", Path.Combine(PathToSHPs, "wormdust")},
|
||||
@@ -267,7 +274,7 @@ namespace OpenRA.Mods.D2k.Widgets.Logic
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "4463", "4477", Path.Combine(PathToSHPs, "craneo"), "--building"},
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "4760", "4819", Path.Combine(PathToSHPs, "windtrap_anim"), "--building"}, //?
|
||||
new string[] {"--r8", PathToDataR8, PathToPalette, "4820", "4840", Path.Combine(PathToSHPs, "missile_launch"), "--building"},
|
||||
new string[] {"--r8", Path.Combine(Platform.SupportDir, "Content/d2k/MOUSE.R8"), PathToPalette, "0", "264", Path.Combine(PathToSHPs, "mouse"), "--transparent"},
|
||||
new string[] {"--r8", Path.Combine(Platform.SupportDir, "Content/d2k/MOUSE.R8"), PathToPalette, "0", "264", Path.Combine(PathToSHPs, "mouse")},
|
||||
new string[] {"--r8", Path.Combine(Platform.SupportDir, "Content/d2k/BLOXBASE.R8"), PathToPalette, "0", "799", Path.Combine(PathToTilesets, "BASE"), "--tileset"},
|
||||
new string[] {"--r8", Path.Combine(Platform.SupportDir, "Content/d2k/BLOXBASE.R8"), PathToPalette, "748", "749", Path.Combine(PathToSHPs, "spice0")},
|
||||
new string[] {"--r8", Path.Combine(Platform.SupportDir, "Content/d2k/BLOXBAT.R8"), PathToPalette, "0", "799", Path.Combine(PathToTilesets, "BAT"), "--tileset"},
|
||||
@@ -474,6 +481,11 @@ namespace OpenRA.Mods.D2k.Widgets.Logic
|
||||
new string[] {"--shp", Path.Combine(PathToSHPs, "missile_launch.png"), "96"},
|
||||
new string[] {"--shp", Path.Combine(PathToSHPs, "mouse.png"), "48"},
|
||||
new string[] {"--shp", Path.Combine(PathToSHPs, "spice0.png"), "32"},
|
||||
new string[] {"--shp", Path.Combine(PathToSHPs, "sietch.png"), "64"},
|
||||
new string[] {"--shp", Path.Combine(PathToSHPs, "starportc.png"), "96"},
|
||||
new string[] {"--shp", Path.Combine(PathToSHPs, "heavyc.png"), "96"},
|
||||
new string[] {"--shp", Path.Combine(PathToSHPs, "palacec.png"), "96"},
|
||||
new string[] {"--shp", Path.Combine(PathToSHPs, "plates.png"), "32"},
|
||||
};
|
||||
|
||||
var SHPsToTranspose = new string[][]
|
||||
|
||||
@@ -13,6 +13,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Mods.RA.Buildings;
|
||||
using OpenRA.Mods.RA.Move;
|
||||
using OpenRA.Traits;
|
||||
using XRandom = OpenRA.Thirdparty.Random;
|
||||
|
||||
@@ -442,7 +443,11 @@ namespace OpenRA.Mods.RA.AI
|
||||
if (mcv != null)
|
||||
{
|
||||
baseCenter = mcv.Location;
|
||||
world.IssueOrder(new Order("DeployTransform", mcv, false));
|
||||
//Don't transform the mcv if it is a fact
|
||||
if (mcv.HasTrait<Mobile>())
|
||||
{
|
||||
world.IssueOrder(new Order("DeployTransform", mcv, false));
|
||||
}
|
||||
}
|
||||
else
|
||||
BotDebug("AI: Can't find BaseBuildUnit.");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2012 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,
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.RA.Move;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA.Activities
|
||||
@@ -25,8 +26,11 @@ namespace OpenRA.Mods.RA.Activities
|
||||
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
|
||||
if (target.Owner == self.Owner) return NextActivity;
|
||||
|
||||
if( !target.OccupiesSpace.OccupiedCells().Any( x => x.First == self.Location ) )
|
||||
return NextActivity;
|
||||
// Need to be next to building, TODO: stop capture when going away
|
||||
var mobile = self.Trait<Mobile>();
|
||||
var nearest = target.OccupiesSpace.NearestCellTo(mobile.toCell);
|
||||
if ((nearest - mobile.toCell).LengthSquared > 2)
|
||||
return Util.SequenceActivities(new MoveAdjacentTo(Target.FromActor(target)), this);
|
||||
|
||||
var capturable = target.TraitOrDefault<Capturable>();
|
||||
if (capturable != null && capturable.CaptureInProgress && capturable.Captor.Owner.Stances[self.Owner] == Stance.Ally)
|
||||
@@ -37,7 +41,10 @@ namespace OpenRA.Mods.RA.Activities
|
||||
return NextActivity;
|
||||
|
||||
target.Trait<Capturable>().BeginCapture(target, self);
|
||||
self.World.AddFrameEndTask(w => self.Destroy());
|
||||
|
||||
var capturesInfo = self.Info.Traits.Get<CapturesInfo>();
|
||||
if (capturesInfo != null && capturesInfo.WastedAfterwards)
|
||||
self.World.AddFrameEndTask(w => self.Destroy());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
|
||||
// Find harvestable resources nearby:
|
||||
var path = self.World.WorldActor.Trait<PathFinder>().FindPath(
|
||||
PathSearch.Search(self.World, mobileInfo, self.Owner, true)
|
||||
PathSearch.Search(self.World, mobileInfo, self, true)
|
||||
.WithCustomCost(loc =>
|
||||
{
|
||||
// Avoid enemy territory:
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
|
||||
var mobile = self.Trait<Mobile>();
|
||||
|
||||
var ps1 = new PathSearch( self.World, mobile.Info, self.Owner )
|
||||
var ps1 = new PathSearch( self.World, mobile.Info, self )
|
||||
{
|
||||
checkForBlocked = true,
|
||||
heuristic = location => 0,
|
||||
@@ -40,7 +40,7 @@ namespace OpenRA.Mods.RA.Activities
|
||||
|
||||
ps1.heuristic = PathSearch.DefaultEstimator( mobile.toCell );
|
||||
|
||||
var ps2 = PathSearch.FromPoint( self.World, mobile.Info, self.Owner, mobile.toCell, target.CenterLocation.ToCPos(), true );
|
||||
var ps2 = PathSearch.FromPoint( self.World, mobile.Info, self, mobile.toCell, target.CenterLocation.ToCPos(), true );
|
||||
var ret = self.World.WorldActor.Trait<PathFinder>().FindBidiPath( ps1, ps2 );
|
||||
|
||||
return Util.SequenceActivities( mobile.MoveTo( () => ret ), this );
|
||||
|
||||
@@ -21,13 +21,15 @@ namespace OpenRA.Mods.RA
|
||||
enum State { Wait, Turn, Dock, Loop, Undock, Complete };
|
||||
|
||||
readonly Actor proc;
|
||||
readonly int angle;
|
||||
readonly Harvester harv;
|
||||
readonly RenderUnit ru;
|
||||
State state;
|
||||
|
||||
public RAHarvesterDockSequence(Actor self, Actor proc)
|
||||
public RAHarvesterDockSequence(Actor self, Actor proc, int angle)
|
||||
{
|
||||
this.proc = proc;
|
||||
this.angle = angle;
|
||||
state = State.Turn;
|
||||
harv = self.Trait<Harvester>();
|
||||
ru = self.Trait<RenderUnit>();
|
||||
@@ -41,7 +43,7 @@ namespace OpenRA.Mods.RA
|
||||
return this;
|
||||
case State.Turn:
|
||||
state = State.Dock;
|
||||
return Util.SequenceActivities(new Turn(64), this);
|
||||
return Util.SequenceActivities(new Turn(angle), this);
|
||||
case State.Dock:
|
||||
ru.PlayCustomAnimation(self, "dock", () => {ru.PlayCustomAnimRepeating(self, "dock-loop"); state = State.Loop;});
|
||||
state = State.Wait;
|
||||
|
||||
@@ -10,18 +10,59 @@
|
||||
|
||||
using System.Linq;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Mods.RA.Render;
|
||||
|
||||
namespace OpenRA.Mods.RA.Activities
|
||||
{
|
||||
public class Teleport : Activity
|
||||
{
|
||||
CPos destination;
|
||||
bool killCargo;
|
||||
Actor chronosphere;
|
||||
|
||||
public Teleport(CPos destination)
|
||||
public Teleport(Actor chronosphere, CPos destination, bool killCargo)
|
||||
{
|
||||
this.chronosphere = chronosphere;
|
||||
this.destination = destination;
|
||||
this.killCargo = killCargo;
|
||||
}
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
Sound.Play("chrono2.aud", self.Location.ToPPos());
|
||||
Sound.Play("chrono2.aud", destination.ToPPos());
|
||||
|
||||
self.Trait<ITeleportable>().SetPosition(self, destination);
|
||||
|
||||
if (killCargo && self.HasTrait<Cargo>())
|
||||
{
|
||||
var cargo = self.Trait<Cargo>();
|
||||
while (!cargo.IsEmpty(self))
|
||||
{
|
||||
if (chronosphere != null)
|
||||
chronosphere.Owner.Kills++;
|
||||
var a = cargo.Unload(self);
|
||||
a.Owner.Deaths++;
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger screen desaturate effect
|
||||
foreach (var a in self.World.ActorsWithTrait<ChronoshiftPaletteEffect>())
|
||||
a.Trait.Enable();
|
||||
|
||||
if (chronosphere != null && !chronosphere.Destroyed && chronosphere.HasTrait<RenderBuilding>())
|
||||
chronosphere.Trait<RenderBuilding>().PlayCustomAnim(chronosphere, "active");
|
||||
|
||||
return NextActivity;
|
||||
}
|
||||
}
|
||||
|
||||
public class SimpleTeleport : Activity
|
||||
{
|
||||
CPos destination;
|
||||
|
||||
public SimpleTeleport(CPos destination) { this.destination = destination; }
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
self.Trait<ITeleportable>().SetPosition(self, destination);
|
||||
|
||||
@@ -52,11 +52,11 @@ namespace OpenRA.Mods.RA.Activities
|
||||
var health = self.TraitOrDefault<Health>();
|
||||
if (health != null)
|
||||
{
|
||||
// TODO: Fix bogus health init
|
||||
if (ForceHealthPercentage > 0)
|
||||
init.Add( new HealthInit( ForceHealthPercentage * 1f / 100 ));
|
||||
else
|
||||
init.Add( new HealthInit( (float)health.HP / health.MaxHP ));
|
||||
var newHP = (ForceHealthPercentage > 0)
|
||||
? ForceHealthPercentage / 100f
|
||||
: (float)health.HP / health.MaxHP;
|
||||
|
||||
init.Add( new HealthInit(newHP) );
|
||||
}
|
||||
|
||||
var cargo = self.TraitOrDefault<Cargo>();
|
||||
|
||||
@@ -18,6 +18,10 @@ namespace OpenRA.Mods.RA.Activities
|
||||
{
|
||||
public class UnloadCargo : Activity
|
||||
{
|
||||
bool unloadAll;
|
||||
|
||||
public UnloadCargo(bool unloadAll) { this.unloadAll = unloadAll; }
|
||||
|
||||
CPos? ChooseExitTile(Actor self, Actor cargo)
|
||||
{
|
||||
// is anyone still hogging this tile?
|
||||
@@ -35,6 +39,19 @@ namespace OpenRA.Mods.RA.Activities
|
||||
return null;
|
||||
}
|
||||
|
||||
CPos? ChooseRallyPoint(Actor self)
|
||||
{
|
||||
var mobile = self.Trait<Mobile>();
|
||||
|
||||
for (var i = -1; i < 2; i++)
|
||||
for (var j = -1; j < 2; j++)
|
||||
if ((i != 0 || j != 0) &&
|
||||
mobile.CanEnterCell(self.Location + new CVec(i, j)))
|
||||
return self.Location + new CVec(i, j);
|
||||
|
||||
return self.Location;
|
||||
}
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
if (IsCanceled) return NextActivity;
|
||||
@@ -80,10 +97,13 @@ namespace OpenRA.Mods.RA.Activities
|
||||
actor.CancelActivity();
|
||||
actor.QueueActivity(new Drag(currentPx, exitPx, length));
|
||||
actor.QueueActivity(mobile.MoveTo(exitTile.Value, 0));
|
||||
actor.SetTargetLine(Target.FromCell(exitTile.Value), Color.Green, false);
|
||||
|
||||
var rallyPoint = ChooseRallyPoint(actor).Value;
|
||||
actor.QueueActivity(mobile.MoveTo(rallyPoint, 0));
|
||||
actor.SetTargetLine(Target.FromCell(rallyPoint), Color.Green, false);
|
||||
});
|
||||
|
||||
return this;
|
||||
return unloadAll ? this : NextActivity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,15 +14,20 @@ namespace OpenRA.Mods.RA.Air
|
||||
{
|
||||
class HeliLand : Activity
|
||||
{
|
||||
public HeliLand(bool requireSpace) { this.requireSpace = requireSpace; }
|
||||
public HeliLand(bool requireSpace, int minimalAltitude)
|
||||
{
|
||||
this.requireSpace = requireSpace;
|
||||
this.minimalAltitude = minimalAltitude;
|
||||
}
|
||||
|
||||
bool requireSpace;
|
||||
int minimalAltitude = 0;
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
if (IsCanceled) return NextActivity;
|
||||
var aircraft = self.Trait<Aircraft>();
|
||||
if (aircraft.Altitude == 0)
|
||||
if (aircraft.Altitude == minimalAltitude)
|
||||
return NextActivity;
|
||||
|
||||
if (requireSpace && !aircraft.CanLand(self.Location))
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
.ClosestTo(self.CenterLocation);
|
||||
|
||||
if (nearestHpad == null)
|
||||
return Util.SequenceActivities(new Turn(initialFacing), new HeliLand(true), NextActivity);
|
||||
return Util.SequenceActivities(new Turn(initialFacing), new HeliLand(true, 0), NextActivity);
|
||||
else
|
||||
return Util.SequenceActivities(new HeliFly(Util.CenterOfCell(nearestHpad.Location)));
|
||||
}
|
||||
@@ -56,7 +56,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
return Util.SequenceActivities(
|
||||
new HeliFly(dest.Trait<IHasLocation>().PxPosition + offset),
|
||||
new Turn(initialFacing),
|
||||
new HeliLand(false),
|
||||
new HeliLand(false, 0),
|
||||
new Rearm(self),
|
||||
NextActivity);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
{
|
||||
public readonly int IdealSeparation = 40;
|
||||
public readonly bool LandWhenIdle = true;
|
||||
public readonly int MinimalLandAltitude = 0;
|
||||
|
||||
public override object Create( ActorInitializer init ) { return new Helicopter( init, this); }
|
||||
}
|
||||
@@ -52,7 +53,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
if (Info.LandWhenIdle)
|
||||
{
|
||||
self.QueueActivity(new Turn(Info.InitialFacing));
|
||||
self.QueueActivity(new HeliLand(true));
|
||||
self.QueueActivity(new HeliLand(true, Info.MinimalLandAltitude));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(new HeliFly(order.TargetActor.Trait<IHasLocation>().PxPosition + offset));
|
||||
self.QueueActivity(new Turn(Info.InitialFacing));
|
||||
self.QueueActivity(new HeliLand(false));
|
||||
self.QueueActivity(new HeliLand(false, Info.MinimalLandAltitude));
|
||||
self.QueueActivity(new ResupplyAircraft());
|
||||
}
|
||||
}
|
||||
@@ -95,7 +96,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
if (Info.LandWhenIdle)
|
||||
{
|
||||
self.QueueActivity(new Turn(Info.InitialFacing));
|
||||
self.QueueActivity(new HeliLand(true));
|
||||
self.QueueActivity(new HeliLand(true, Info.MinimalLandAltitude));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
OpenRA.Mods.RA/Attack/AttackLoyalty.cs
Normal file
43
OpenRA.Mods.RA/Attack/AttackLoyalty.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
#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 OpenRA.Traits;
|
||||
using OpenRA.Mods.RA.Activities;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class AttackLoyaltyInfo : AttackFrontalInfo
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new AttackLoyalty(init.self, this); }
|
||||
}
|
||||
|
||||
public class AttackLoyalty : AttackFrontal
|
||||
{
|
||||
public AttackLoyalty(Actor self, AttackLoyaltyInfo info)
|
||||
: base( self, info ) {}
|
||||
|
||||
public override void DoAttack(Actor self, Target target)
|
||||
{
|
||||
if (!CanAttack (self, target)) return;
|
||||
|
||||
var weapon = Weapons[0].Info;
|
||||
if (!Combat.IsInRange(self.CenterLocation, weapon.Range, target)) return;
|
||||
|
||||
var move = self.TraitOrDefault<IMove>();
|
||||
var facing = self.TraitOrDefault<IFacing>();
|
||||
foreach (var w in Weapons)
|
||||
w.CheckFire(self, this, move, facing, target);
|
||||
|
||||
if (target.Actor != null)
|
||||
target.Actor.ChangeOwner(self.Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,9 +37,7 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
if (order.OrderString == "PowerDown")
|
||||
{
|
||||
disabled = !disabled;
|
||||
var eva = self.World.WorldActor.Info.Traits.Get<EvaAlertsInfo>();
|
||||
Sound.PlayToPlayer(self.Owner, disabled ? eva.EnablePower : eva.DisablePower);
|
||||
|
||||
Sound.PlayNotification(self.Owner, "Sounds", (disabled ? "EnablePower" : "DisablePower"), self.Owner.Country.Race);
|
||||
PowerManager.UpdateActor(self, disabled ? 0 : normalPower);
|
||||
|
||||
if (disabled)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2012 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,
|
||||
@@ -108,8 +108,7 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
if (--nextPowerAdviceTime <= 0)
|
||||
{
|
||||
if (lowPower)
|
||||
Player.GiveAdvice(Rules.Info["world"].Traits.Get<EvaAlertsInfo>().LowPower);
|
||||
|
||||
Sound.PlayNotification(self.Owner, "Speech", "LowPower", self.Owner.Country.Race);
|
||||
nextPowerAdviceTime = Info.AdviceInterval;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace OpenRA.Mods.RA.Buildings
|
||||
else
|
||||
{
|
||||
Repairer = p;
|
||||
Sound.PlayToPlayer(Repairer, p.World.WorldActor.Info.Traits.Get<EvaAlertsInfo>().Repairing);
|
||||
Sound.PlayNotification(Repairer, "Speech", "Repairing", self.Owner.Country.Race);
|
||||
|
||||
self.World.AddFrameEndTask(
|
||||
w => w.Add(new RepairIndicator(self, p)));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2012 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,
|
||||
@@ -22,6 +22,7 @@ namespace OpenRA.Mods.RA
|
||||
class CapturesInfo : ITraitInfo
|
||||
{
|
||||
public string[] CaptureTypes = {"building"};
|
||||
public bool WastedAfterwards = true;
|
||||
public object Create(ActorInitializer init) { return new Captures(init.self, this); }
|
||||
}
|
||||
|
||||
@@ -40,7 +41,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new CaptureOrderTargeter(Info.CaptureTypes, target => CanEnter(target));
|
||||
yield return new CaptureOrderTargeter(Info.CaptureTypes, target => CanCapture(target));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,24 +56,23 @@ namespace OpenRA.Mods.RA
|
||||
public string VoicePhraseForOrder(Actor self, Order order)
|
||||
{
|
||||
return (order.OrderString == "CaptureActor"
|
||||
&& CanEnter(order.TargetActor)) ? "Attack" : null;
|
||||
&& CanCapture(order.TargetActor)) ? "Attack" : null;
|
||||
}
|
||||
|
||||
public void ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
if (order.OrderString == "CaptureActor")
|
||||
{
|
||||
if (!CanEnter(order.TargetActor)) return;
|
||||
if (!CanCapture(order.TargetActor)) return;
|
||||
|
||||
self.SetTargetLine(Target.FromOrder(order), Color.Red);
|
||||
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(new Enter(order.TargetActor));
|
||||
self.QueueActivity(new CaptureActor(order.TargetActor));
|
||||
}
|
||||
}
|
||||
|
||||
bool CanEnter(Actor target)
|
||||
bool CanCapture(Actor target)
|
||||
{
|
||||
var c = target.TraitOrDefault<Capturable>();
|
||||
return c != null && ( !c.CaptureInProgress || c.Captor.Owner.Stances[self.Owner] != Stance.Ally );
|
||||
@@ -104,9 +104,11 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
IsQueued = forceQueued;
|
||||
|
||||
var Info = self.Info.Traits.Get<CapturesInfo>();
|
||||
|
||||
if (captureTypes.Contains(ci.Type))
|
||||
{
|
||||
cursor = useEnterCursor(target) ? "enter" : "enter-blocked";
|
||||
cursor = (Info.WastedAfterwards) ? (useEnterCursor(target) ? "enter" : "enter-blocked") : "attack";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace OpenRA.Mods.RA
|
||||
public readonly string[] Types = { };
|
||||
public readonly int UnloadFacing = 0;
|
||||
public readonly string[] InitialUnits = { };
|
||||
public readonly int minimalUnloadAltitude = 0;
|
||||
|
||||
public object Create( ActorInitializer init ) { return new Cargo( init, this ); }
|
||||
}
|
||||
@@ -81,7 +82,7 @@ namespace OpenRA.Mods.RA
|
||||
return;
|
||||
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(new UnloadCargo());
|
||||
self.QueueActivity(new UnloadCargo(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +93,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
// Cannot unload mid-air
|
||||
var move = self.TraitOrDefault<IMove>();
|
||||
if (move != null && move.Altitude > 0)
|
||||
if (move != null && move.Altitude > info.minimalUnloadAltitude)
|
||||
return false;
|
||||
|
||||
// Todo: Check if there is a free tile to unload to
|
||||
@@ -106,7 +107,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
// Cannot load mid-air
|
||||
var move = self.TraitOrDefault<IMove>();
|
||||
return move == null || move.Altitude == 0;
|
||||
return move == null || move.Altitude == info.minimalUnloadAltitude;
|
||||
}
|
||||
|
||||
public string CursorForOrder(Actor self, Order order)
|
||||
|
||||
@@ -52,18 +52,11 @@ namespace OpenRA.Mods.RA
|
||||
if (order.OrderString == "ChronoshiftSelf" && movement.CanEnterCell(order.TargetLocation))
|
||||
{
|
||||
if (self.Owner == self.World.LocalPlayer)
|
||||
{
|
||||
self.World.CancelInputMode();
|
||||
}
|
||||
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(new Teleport(order.TargetLocation));
|
||||
Sound.Play("chrotnk1.aud", self.CenterLocation);
|
||||
Sound.Play("chrotnk1.aud", order.TargetLocation.ToPPos());
|
||||
self.QueueActivity(new Teleport(null, order.TargetLocation, true));
|
||||
chargeTick = 25 * self.Info.Traits.Get<ChronoshiftDeployInfo>().ChargeTime;
|
||||
|
||||
foreach (var a in self.World.ActorsWithTrait<ChronoshiftPaletteEffect>())
|
||||
a.Trait.Enable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace OpenRA.Mods.RA
|
||||
// Return-to-sender logic
|
||||
[Sync] CPos chronoshiftOrigin;
|
||||
[Sync] int chronoshiftReturnTicks = 0;
|
||||
Actor chronosphere;
|
||||
bool killCargo;
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
@@ -34,7 +36,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
self.CancelActivity();
|
||||
// Todo: need a new Teleport method that will move to the closest available cell
|
||||
self.QueueActivity(new Teleport(chronoshiftOrigin));
|
||||
self.QueueActivity(new Teleport(chronosphere, chronoshiftOrigin, killCargo));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,22 +54,12 @@ namespace OpenRA.Mods.RA
|
||||
/// Set up return-to-sender info
|
||||
chronoshiftOrigin = self.Location;
|
||||
chronoshiftReturnTicks = duration;
|
||||
|
||||
// Kill cargo
|
||||
if (killCargo && self.HasTrait<Cargo>())
|
||||
{
|
||||
var cargo = self.Trait<Cargo>();
|
||||
while (!cargo.IsEmpty(self))
|
||||
{
|
||||
chronosphere.Owner.Kills++;
|
||||
var a = cargo.Unload(self);
|
||||
a.Owner.Deaths++;
|
||||
}
|
||||
}
|
||||
this.chronosphere = chronosphere;
|
||||
this.killCargo = killCargo;
|
||||
|
||||
// Set up the teleport
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(new Teleport(targetLocation));
|
||||
self.QueueActivity(new Teleport(chronosphere, targetLocation, killCargo));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class ConquestVictoryConditionsInfo : ITraitInfo
|
||||
{
|
||||
public string WinNotification = null;
|
||||
public string LoseNotification = null;
|
||||
public int NotificationDelay = 1500; // Milliseconds
|
||||
public readonly string Race = null;
|
||||
|
||||
public object Create(ActorInitializer init) { return new ConquestVictoryConditions(this); }
|
||||
}
|
||||
@@ -55,7 +52,6 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public void Lose(Actor self)
|
||||
{
|
||||
if (Info.Race != null && Info.Race != self.Owner.Country.Race) return;
|
||||
if (self.Owner.WinState == WinState.Lost) return;
|
||||
self.Owner.WinState = WinState.Lost;
|
||||
|
||||
@@ -70,14 +66,13 @@ namespace OpenRA.Mods.RA
|
||||
Game.RunAfterDelay(Info.NotificationDelay, () =>
|
||||
{
|
||||
if (Game.IsCurrentWorld(self.World))
|
||||
Sound.Play(Info.LoseNotification);
|
||||
Sound.PlayNotification(self.Owner, "Speech", "Lose", self.Owner.Country.Race);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Win(Actor self)
|
||||
{
|
||||
if (Info.Race != null && Info.Race != self.Owner.Country.Race) return;
|
||||
if (self.Owner.WinState == WinState.Won) return;
|
||||
self.Owner.WinState = WinState.Won;
|
||||
|
||||
@@ -85,7 +80,7 @@ namespace OpenRA.Mods.RA
|
||||
if (self.Owner == self.World.LocalPlayer)
|
||||
{
|
||||
self.World.LocalShroud.Disabled = true;
|
||||
Game.RunAfterDelay(Info.NotificationDelay, () => Sound.Play(Info.WinNotification));
|
||||
Game.RunAfterDelay(Info.NotificationDelay, () => Sound.PlayNotification(self.Owner, "Speech", "Win", self.Owner.Country.Race));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,17 +14,6 @@ using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Mods.RA.Buildings;
|
||||
|
||||
/*
|
||||
* Crates left to implement:
|
||||
HealBase=1,INVUN ; all buildings to full strength
|
||||
ICBM=1,MISSILE2 ; nuke missile one time shot
|
||||
Sonar=3,SONARBOX ; one time sonar pulse
|
||||
Squad=20,NONE ; squad of random infantry
|
||||
Unit=20,NONE ; vehicle
|
||||
Invulnerability=3,INVULBOX,1.0 ; invulnerability (duration in minutes)
|
||||
TimeQuake=3,TQUAKE ; time quake
|
||||
*/
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
class CrateInfo : ITraitInfo, Requires<RenderSimpleInfo>
|
||||
@@ -35,12 +24,13 @@ namespace OpenRA.Mods.RA
|
||||
}
|
||||
|
||||
// ITeleportable is required for paradrop
|
||||
class Crate : ITick, IOccupySpace, ITeleportable, ICrushable, ISync
|
||||
class Crate : ITick, IOccupySpace, ITeleportable, ICrushable, ISync, INotifyParachuteLanded
|
||||
{
|
||||
readonly Actor self;
|
||||
[Sync] int ticks;
|
||||
[Sync] public CPos Location;
|
||||
CrateInfo Info;
|
||||
bool collected;
|
||||
|
||||
public Crate(ActorInitializer init, CrateInfo info)
|
||||
{
|
||||
@@ -57,12 +47,16 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public void OnCrush(Actor crusher)
|
||||
{
|
||||
if (collected) return;
|
||||
|
||||
var shares = self.TraitsImplementing<CrateAction>().Select(
|
||||
a => Pair.New(a, a.GetSelectionSharesOuter(crusher)));
|
||||
var totalShares = shares.Sum(a => a.Second);
|
||||
var n = self.World.SharedRandom.Next(totalShares);
|
||||
|
||||
self.Destroy();
|
||||
collected = true;
|
||||
|
||||
foreach (var s in shares)
|
||||
if (n < s.Second)
|
||||
{
|
||||
@@ -73,6 +67,15 @@ namespace OpenRA.Mods.RA
|
||||
n -= s.Second;
|
||||
}
|
||||
|
||||
public void OnLanded()
|
||||
{
|
||||
var landedOn = self.World.ActorMap.GetUnitsAt(self.Location)
|
||||
.FirstOrDefault(a => a != self);
|
||||
|
||||
if (landedOn != null)
|
||||
OnCrush(landedOn);
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if( ++ticks >= Info.Lifetime * 25 )
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace OpenRA.Mods.RA.Crates
|
||||
|
||||
for (var i = -1; i < 2; i++)
|
||||
for (var j = -1; j < 2; j++)
|
||||
if (mi.CanEnterCell(self.World, self.Owner, near + new CVec(i, j), null, true))
|
||||
if (mi.CanEnterCell(self.World, self, near + new CVec(i, j), null, true, true))
|
||||
yield return near + new CVec(i, j);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,9 @@ namespace OpenRA.Mods.RA.Effects
|
||||
cargo.CancelActivity();
|
||||
cargo.Trait<ITeleportable>().SetPosition(cargo, loc);
|
||||
w.Add(cargo);
|
||||
|
||||
foreach( var npl in cargo.TraitsImplementing<INotifyParachuteLanded>() )
|
||||
npl.OnLanded();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -77,8 +77,7 @@ namespace OpenRA.Mods.RA
|
||||
while (Level < MaxLevel && Experience >= Levels[Level])
|
||||
{
|
||||
Level++;
|
||||
var eva = self.World.WorldActor.Info.Traits.Get<EvaAlertsInfo>();
|
||||
Sound.PlayToPlayer(self.Owner, eva.LevelUp, self.CenterLocation);
|
||||
Sound.PlayNotification(self.Owner, "Sounds", "LevelUp", self.Owner.Country.Race);
|
||||
self.World.AddFrameEndTask(w => w.Add(new CrateEffect(self, "levelup", new int2(0,-24))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace OpenRA.Mods.RA
|
||||
public void SetProcLines(Actor proc)
|
||||
{
|
||||
if (proc == null) return;
|
||||
if (proc.Destroyed) return;
|
||||
|
||||
var linkedHarvs = proc.World.ActorsWithTrait<Harvester>()
|
||||
.Where(a => a.Trait.LinkedProc == proc)
|
||||
@@ -111,7 +112,7 @@ namespace OpenRA.Mods.RA
|
||||
// Start a search from each refinery's delivery location:
|
||||
var mi = self.Info.Traits.Get<MobileInfo>();
|
||||
var path = self.World.WorldActor.Trait<PathFinder>().FindPath(
|
||||
PathSearch.FromPoints(self.World, mi, self.Owner, refs.Values.Select(r => r.Location), self.Location, false)
|
||||
PathSearch.FromPoints(self.World, mi, self, refs.Values.Select(r => r.Location), self.Location, false)
|
||||
.WithCustomCost((loc) =>
|
||||
{
|
||||
if (!refs.ContainsKey(loc)) return 0;
|
||||
@@ -148,7 +149,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
// Check that we're not in a critical location and being useless (refinery drop-off):
|
||||
var lastproc = LastLinkedProc ?? LinkedProc;
|
||||
if (lastproc != null)
|
||||
if (lastproc != null && !lastproc.Destroyed)
|
||||
{
|
||||
var deliveryLoc = lastproc.Location + lastproc.Trait<IAcceptOre>().DeliverOffset;
|
||||
if (self.Location == deliveryLoc)
|
||||
@@ -268,8 +269,9 @@ namespace OpenRA.Mods.RA
|
||||
LinkProc(self, OwnerLinkedProc = null);
|
||||
idleSmart = true;
|
||||
|
||||
var mobile = self.Trait<Mobile>();
|
||||
self.CancelActivity();
|
||||
|
||||
var mobile = self.Trait<Mobile>();
|
||||
if (order.TargetLocation != CPos.Zero)
|
||||
{
|
||||
var loc = order.TargetLocation;
|
||||
@@ -295,7 +297,7 @@ namespace OpenRA.Mods.RA
|
||||
else
|
||||
{
|
||||
// A bot order gives us a CPos.Zero TargetLocation, so find some good resources for him:
|
||||
CPos? loc = FindNextResourceForBot(self);
|
||||
var loc = FindNextResourceForBot(self);
|
||||
// No more resources? Oh well.
|
||||
if (!loc.HasValue)
|
||||
return;
|
||||
@@ -306,6 +308,8 @@ namespace OpenRA.Mods.RA
|
||||
LastOrderLocation = loc;
|
||||
}
|
||||
|
||||
// This prevents harvesters returning to an empty patch when the player orders them to a new patch:
|
||||
LastHarvestedCell = LastOrderLocation;
|
||||
self.QueueActivity(new FindResources());
|
||||
}
|
||||
else if (order.OrderString == "Deliver")
|
||||
@@ -345,7 +349,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
// Find any harvestable resources:
|
||||
var path = self.World.WorldActor.Trait<PathFinder>().FindPath(
|
||||
PathSearch.Search(self.World, mobileInfo, self.Owner, true)
|
||||
PathSearch.Search(self.World, mobileInfo, self, true)
|
||||
.WithHeuristic(loc =>
|
||||
{
|
||||
// Get the resource at this location:
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
var self = init.self;
|
||||
location = init.Get<LocationInit, CPos>();
|
||||
PxPosition = init.Get<CenterLocationInit, PPos>();
|
||||
PxPosition = init.Contains<CenterLocationInit>() ? init.Get<CenterLocationInit, PPos>() : Util.CenterOfCell(location);
|
||||
Facing = init.Contains<FacingInit>() ? init.Get<FacingInit,int>() : 128;
|
||||
|
||||
var speed = init.Contains<HuskSpeedInit>() ? init.Get<HuskSpeedInit,int>() : 0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2012 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,
|
||||
@@ -15,261 +15,325 @@ using OpenRA.FileFormats;
|
||||
using OpenRA.Mods.RA.Activities;
|
||||
using OpenRA.Mods.RA.Air;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA.Missions
|
||||
{
|
||||
public class Allies01ScriptInfo : TraitInfo<Allies01Script>, Requires<SpawnMapActorsInfo> { }
|
||||
class Allies01ScriptInfo : TraitInfo<Allies01Script>, Requires<SpawnMapActorsInfo> { }
|
||||
|
||||
public class Allies01Script : IWorldLoaded, ITick
|
||||
{
|
||||
private static readonly string[] objectives =
|
||||
{
|
||||
"Find Einstein.",
|
||||
"Wait for the helicopter and extract Einstein."
|
||||
};
|
||||
class Allies01Script : IWorldLoaded, ITick
|
||||
{
|
||||
static readonly string[] Objectives =
|
||||
{
|
||||
"Find Einstein. Tanya and Einstein must survive.",
|
||||
"Wait for the helicopter and extract Einstein. Tanya and Einstein must survive."
|
||||
};
|
||||
|
||||
private int currentObjective;
|
||||
int currentObjective;
|
||||
|
||||
private Player allies;
|
||||
private Player soviets;
|
||||
Player allies;
|
||||
Player soviets;
|
||||
|
||||
private ISound music;
|
||||
Actor insertionLZ;
|
||||
Actor extractionLZ;
|
||||
Actor lab;
|
||||
Actor insertionLZEntryPoint;
|
||||
Actor extractionLZEntryPoint;
|
||||
Actor chinookExitPoint;
|
||||
Actor shipSpawnPoint;
|
||||
Actor shipMovePoint;
|
||||
Actor einstein;
|
||||
Actor einsteinChinook;
|
||||
Actor tanya;
|
||||
Actor attackEntryPoint1;
|
||||
Actor attackEntryPoint2;
|
||||
|
||||
private Actor insertionLZ;
|
||||
private Actor extractionLZ;
|
||||
private Actor lab;
|
||||
private Actor insertionLZEntryPoint;
|
||||
private Actor extractionLZEntryPoint;
|
||||
private Actor chinookExitPoint;
|
||||
private Actor shipSpawnPoint;
|
||||
private Actor shipMovePoint;
|
||||
private Actor einstein;
|
||||
private Actor einsteinChinook;
|
||||
private Actor tanya;
|
||||
private Actor attackEntryPoint1;
|
||||
private Actor attackEntryPoint2;
|
||||
World world;
|
||||
|
||||
private static readonly string[] taunts = { "laugh1.aud", "lefty1.aud", "cmon1.aud", "gotit1.aud" };
|
||||
static readonly string[] Taunts = { "laugh1.aud", "lefty1.aud", "cmon1.aud", "gotit1.aud" };
|
||||
|
||||
private static readonly string[] ships = { "ca", "ca", "ca", "ca" };
|
||||
static readonly string[] Ships = { "ca", "ca", "ca", "ca" };
|
||||
static readonly string[] Patrol = { "e1", "dog", "e1" };
|
||||
|
||||
private static readonly string[] attackWave = { "e1", "e1", "e1", "e1", "e2", "e2", "e2", "e2", "dog" };
|
||||
private static readonly string[] lastAttackWaveAddition = { "3tnk", "e1", "e1", "e1", "e1", "e2", "e2", "e2", "e2" };
|
||||
private int currentAttackWaveFrameNumber;
|
||||
private int currentAttackWave;
|
||||
private const int einsteinChinookArrivesAtAttackWave = 5;
|
||||
static readonly string[] AttackWave = { "e1", "e1", "e1", "e1", "e2", "e2", "e2", "e2", "dog" };
|
||||
static readonly string[] LastAttackWaveAddition = { "3tnk", "e1", "e1", "e1", "e1", "e2", "e2", "e2", "e2" };
|
||||
int currentAttackWaveFrameNumber;
|
||||
int currentAttackWave;
|
||||
const int EinsteinChinookAttackWave = 5;
|
||||
|
||||
private const int labRange = 5;
|
||||
private const string einsteinName = "einstein";
|
||||
private const string tanyaName = "e7";
|
||||
private const string chinookName = "tran";
|
||||
private const string signalFlareName = "flare";
|
||||
const int LabClearRange = 5;
|
||||
const string EinsteinName = "einstein";
|
||||
const string TanyaName = "e7";
|
||||
const string ChinookName = "tran";
|
||||
const string SignalFlareName = "flare";
|
||||
|
||||
private void NextObjective()
|
||||
{
|
||||
currentObjective++;
|
||||
}
|
||||
void DisplayObjective()
|
||||
{
|
||||
Game.AddChatLine(Color.LimeGreen, "Objective", Objectives[currentObjective]);
|
||||
Sound.Play("bleep6.aud");
|
||||
}
|
||||
|
||||
private void DisplayObjective()
|
||||
{
|
||||
Game.AddChatLine(Color.LimeGreen, "Objective", objectives[currentObjective]);
|
||||
Sound.Play("bleep6.aud", 5);
|
||||
}
|
||||
void MissionFailed(string text)
|
||||
{
|
||||
if (allies.WinState != WinState.Undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
allies.WinState = WinState.Lost;
|
||||
foreach (var actor in world.Actors.Where(a => a.IsInWorld && a.Owner == allies && !a.IsDead()))
|
||||
{
|
||||
actor.Kill(actor);
|
||||
}
|
||||
Game.AddChatLine(Color.Red, "Mission failed", text);
|
||||
Sound.Play("misnlst1.aud");
|
||||
}
|
||||
|
||||
private void MissionFailed(Actor self, string text)
|
||||
{
|
||||
if (allies.WinState != WinState.Undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
allies.WinState = WinState.Lost;
|
||||
Game.AddChatLine(Color.Red, "Mission failed", text);
|
||||
self.World.LocalShroud.Disabled = true;
|
||||
Sound.Play("misnlst1.aud", 5);
|
||||
}
|
||||
void MissionAccomplished(string text)
|
||||
{
|
||||
if (allies.WinState != WinState.Undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
allies.WinState = WinState.Won;
|
||||
Game.AddChatLine(Color.Blue, "Mission accomplished", text);
|
||||
Sound.Play("misnwon1.aud");
|
||||
}
|
||||
|
||||
private void MissionAccomplished(Actor self, string text)
|
||||
{
|
||||
if (allies.WinState != WinState.Undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
allies.WinState = WinState.Won;
|
||||
Game.AddChatLine(Color.Blue, "Mission accomplished", text);
|
||||
self.World.LocalShroud.Disabled = true;
|
||||
Sound.Play("misnwon1.aud", 5);
|
||||
}
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (allies.WinState != WinState.Undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// display current objective every so often
|
||||
if (world.FrameNumber % 1500 == 1)
|
||||
{
|
||||
DisplayObjective();
|
||||
}
|
||||
// taunt every so often
|
||||
if (world.FrameNumber % 1000 == 0)
|
||||
{
|
||||
Sound.Play(Taunts[world.SharedRandom.Next(Taunts.Length)]);
|
||||
}
|
||||
// objectives
|
||||
if (currentObjective == 0)
|
||||
{
|
||||
if (AlliesControlLab())
|
||||
{
|
||||
SpawnSignalFlare();
|
||||
Sound.Play("flaren1.aud");
|
||||
SpawnEinsteinAtLab();
|
||||
SendShips();
|
||||
currentObjective++;
|
||||
DisplayObjective();
|
||||
currentAttackWaveFrameNumber = world.FrameNumber;
|
||||
}
|
||||
if (lab.Destroyed)
|
||||
{
|
||||
MissionFailed("Einstein was killed.");
|
||||
}
|
||||
}
|
||||
else if (currentObjective == 1)
|
||||
{
|
||||
if (world.FrameNumber >= currentAttackWaveFrameNumber + 600)
|
||||
{
|
||||
Sound.Play("enmyapp1.aud");
|
||||
SendAttackWave(AttackWave);
|
||||
currentAttackWave++;
|
||||
currentAttackWaveFrameNumber = world.FrameNumber;
|
||||
if (currentAttackWave >= EinsteinChinookAttackWave)
|
||||
{
|
||||
SendAttackWave(LastAttackWaveAddition);
|
||||
}
|
||||
if (currentAttackWave == EinsteinChinookAttackWave)
|
||||
{
|
||||
FlyEinsteinFromExtractionLZ();
|
||||
}
|
||||
}
|
||||
if (einsteinChinook != null)
|
||||
{
|
||||
if (einsteinChinook.Destroyed)
|
||||
{
|
||||
MissionFailed("The extraction helicopter was destroyed.");
|
||||
}
|
||||
else if (!world.Map.IsInMap(einsteinChinook.Location) && einsteinChinook.Trait<Cargo>().Passengers.Contains(einstein))
|
||||
{
|
||||
MissionAccomplished("Einstein was rescued.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tanya != null && tanya.Destroyed)
|
||||
{
|
||||
MissionFailed("Tanya was killed.");
|
||||
}
|
||||
else if (einstein != null && einstein.Destroyed)
|
||||
{
|
||||
MissionFailed("Einstein was killed.");
|
||||
}
|
||||
ManageSovietOre();
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (allies.WinState != WinState.Undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// display current objective every so often
|
||||
if (self.World.FrameNumber % 1500 == 1)
|
||||
{
|
||||
DisplayObjective();
|
||||
}
|
||||
// taunt every so often
|
||||
if (self.World.FrameNumber % 1000 == 0)
|
||||
{
|
||||
Sound.Play(taunts[self.World.SharedRandom.Next(taunts.Length)]);
|
||||
}
|
||||
// take Tanya to the LZ
|
||||
if (self.World.FrameNumber == 1)
|
||||
{
|
||||
FlyTanyaToInsertionLZ(self);
|
||||
}
|
||||
// objectives
|
||||
if (currentObjective == 0)
|
||||
{
|
||||
if (AlliesControlLab(self))
|
||||
{
|
||||
SpawnSignalFlare(self);
|
||||
Sound.Play("flaren1.aud", 5);
|
||||
SpawnEinsteinAtLab(self); // spawn Einstein once the area is clear
|
||||
Sound.Play("einok1.aud"); // "Incredible!" - Einstein
|
||||
SendShips(self);
|
||||
NextObjective();
|
||||
DisplayObjective();
|
||||
currentAttackWaveFrameNumber = self.World.FrameNumber;
|
||||
}
|
||||
if (lab.Destroyed)
|
||||
{
|
||||
MissionFailed(self, "Einstein was killed.");
|
||||
}
|
||||
}
|
||||
else if (currentObjective == 1)
|
||||
{
|
||||
if (self.World.FrameNumber >= currentAttackWaveFrameNumber + 600)
|
||||
{
|
||||
Sound.Play("enmyapp1.aud", 5);
|
||||
SendAttackWave(self, attackWave);
|
||||
currentAttackWave++;
|
||||
currentAttackWaveFrameNumber = self.World.FrameNumber;
|
||||
if (currentAttackWave >= einsteinChinookArrivesAtAttackWave)
|
||||
{
|
||||
SendAttackWave(self, lastAttackWaveAddition);
|
||||
}
|
||||
if (currentAttackWave == einsteinChinookArrivesAtAttackWave)
|
||||
{
|
||||
FlyEinsteinFromExtractionLZ(self);
|
||||
}
|
||||
}
|
||||
if (einsteinChinook != null && !self.World.Map.IsInMap(einsteinChinook.Location) && !einstein.IsInWorld)
|
||||
{
|
||||
MissionAccomplished(self, "Einstein was rescued.");
|
||||
}
|
||||
if (einstein.Destroyed)
|
||||
{
|
||||
MissionFailed(self, "Einstein was killed.");
|
||||
}
|
||||
}
|
||||
if (tanya.Destroyed)
|
||||
{
|
||||
MissionFailed(self, "Tanya was killed.");
|
||||
}
|
||||
}
|
||||
void ManageSovietOre()
|
||||
{
|
||||
var res = soviets.PlayerActor.Trait<PlayerResources>();
|
||||
res.TakeOre(res.Ore);
|
||||
res.TakeCash(res.Cash);
|
||||
}
|
||||
|
||||
private void SpawnSignalFlare(Actor self)
|
||||
{
|
||||
self.World.CreateActor(signalFlareName, new TypeDictionary { new OwnerInit(allies), new LocationInit(extractionLZ.Location) });
|
||||
}
|
||||
void SpawnSignalFlare()
|
||||
{
|
||||
world.CreateActor(SignalFlareName, new TypeDictionary { new OwnerInit(allies), new LocationInit(extractionLZ.Location) });
|
||||
}
|
||||
|
||||
private void SendAttackWave(Actor self, IEnumerable<string> wave)
|
||||
{
|
||||
foreach (var unit in wave)
|
||||
{
|
||||
var spawnActor = self.World.SharedRandom.Next(2) == 0 ? attackEntryPoint1 : attackEntryPoint2;
|
||||
var actor = self.World.CreateActor(unit, new TypeDictionary { new OwnerInit(soviets), new LocationInit(spawnActor.Location) });
|
||||
actor.QueueActivity(new AttackMove.AttackMoveActivity(actor, new Attack(Target.FromActor(einstein), 3))); // better way of doing this?
|
||||
}
|
||||
}
|
||||
void SendAttackWave(IEnumerable<string> wave)
|
||||
{
|
||||
foreach (var unit in wave)
|
||||
{
|
||||
var spawnActor = world.SharedRandom.Next(2) == 0 ? attackEntryPoint1 : attackEntryPoint2;
|
||||
var actor = world.CreateActor(unit, new TypeDictionary { new OwnerInit(soviets), new LocationInit(spawnActor.Location) });
|
||||
Activity innerActivity;
|
||||
if (einstein != null && einstein.IsInWorld)
|
||||
{
|
||||
innerActivity = new Attack(Target.FromActor(einstein), 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
innerActivity = new Move.Move(extractionLZ.Location, 3);
|
||||
}
|
||||
actor.QueueActivity(new AttackMove.AttackMoveActivity(actor, innerActivity));
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<Actor> UnitsNearActor(Actor self, Actor actor, int range)
|
||||
{
|
||||
return self.World.FindUnitsInCircle(actor.CenterLocation, Game.CellSize * range)
|
||||
.Where(a => a.IsInWorld && a != self.World.WorldActor && !a.Destroyed && a.HasTrait<IMove>() && !a.Owner.NonCombatant);
|
||||
}
|
||||
void SendPatrol()
|
||||
{
|
||||
for (int i = 0; i < Patrol.Length; i++)
|
||||
{
|
||||
var actor = world.CreateActor(Patrol[i], new TypeDictionary { new OwnerInit(soviets), new LocationInit(insertionLZ.Location + new CVec(-1 + i, 10 + i * 2)) });
|
||||
actor.QueueActivity(new Move.Move(insertionLZ.Location));
|
||||
}
|
||||
}
|
||||
|
||||
private bool AlliesControlLab(Actor self)
|
||||
{
|
||||
var units = UnitsNearActor(self, lab, labRange);
|
||||
return units.Any() && units.All(a => a.Owner == allies);
|
||||
}
|
||||
IEnumerable<Actor> UnitsNearActor(Actor actor, int range)
|
||||
{
|
||||
return world.FindUnitsInCircle(actor.CenterLocation, Game.CellSize * range)
|
||||
.Where(a => a.IsInWorld && a != world.WorldActor && !a.Destroyed && a.HasTrait<IMove>() && !a.Owner.NonCombatant);
|
||||
}
|
||||
|
||||
private void SpawnEinsteinAtLab(Actor self)
|
||||
{
|
||||
einstein = self.World.CreateActor(einsteinName, new TypeDictionary { new OwnerInit(allies), new LocationInit(lab.Location) });
|
||||
}
|
||||
bool AlliesControlLab()
|
||||
{
|
||||
var units = UnitsNearActor(lab, LabClearRange);
|
||||
return units.Any() && units.All(a => a.Owner == allies);
|
||||
}
|
||||
|
||||
private void SendShips(Actor self)
|
||||
{
|
||||
for (int i = 0; i < ships.Length; i++)
|
||||
{
|
||||
var actor = self.World.CreateActor(ships[i],
|
||||
new TypeDictionary { new OwnerInit(allies), new LocationInit(shipSpawnPoint.Location + new CVec(i * 2, 0)) });
|
||||
actor.QueueActivity(new Move.Move(shipMovePoint.Location + new CVec(i * 4, 0)));
|
||||
}
|
||||
}
|
||||
void SpawnEinsteinAtLab()
|
||||
{
|
||||
einstein = world.CreateActor(EinsteinName, new TypeDictionary { new OwnerInit(allies), new LocationInit(lab.Location) });
|
||||
einstein.QueueActivity(new Move.Move(lab.Location - new CVec(0, 2)));
|
||||
}
|
||||
|
||||
private void FlyEinsteinFromExtractionLZ(Actor self)
|
||||
{
|
||||
einsteinChinook = self.World.CreateActor(chinookName, new TypeDictionary { new OwnerInit(allies), new LocationInit(extractionLZEntryPoint.Location) });
|
||||
einsteinChinook.QueueActivity(new HeliFly(extractionLZ.CenterLocation));
|
||||
einsteinChinook.QueueActivity(new Turn(0));
|
||||
einsteinChinook.QueueActivity(new HeliLand(true));
|
||||
einsteinChinook.QueueActivity(new WaitFor(() => einsteinChinook.Trait<Cargo>().Passengers.Contains(einstein)));
|
||||
einsteinChinook.QueueActivity(new Wait(150));
|
||||
einsteinChinook.QueueActivity(new HeliFly(chinookExitPoint.CenterLocation));
|
||||
einsteinChinook.QueueActivity(new RemoveSelf());
|
||||
}
|
||||
void SendShips()
|
||||
{
|
||||
for (int i = 0; i < Ships.Length; i++)
|
||||
{
|
||||
var actor = world.CreateActor(Ships[i],
|
||||
new TypeDictionary { new OwnerInit(allies), new LocationInit(shipSpawnPoint.Location + new CVec(i * 2, 0)) });
|
||||
actor.QueueActivity(new Move.Move(shipMovePoint.Location + new CVec(i * 4, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
private void FlyTanyaToInsertionLZ(Actor self)
|
||||
{
|
||||
tanya = self.World.CreateActor(false, tanyaName, new TypeDictionary { new OwnerInit(allies) });
|
||||
var chinook = self.World.CreateActor(chinookName, new TypeDictionary { new OwnerInit(allies), new LocationInit(insertionLZEntryPoint.Location) });
|
||||
chinook.Trait<Cargo>().Load(chinook, tanya);
|
||||
// use CenterLocation for HeliFly, Location for Move
|
||||
chinook.QueueActivity(new HeliFly(insertionLZ.CenterLocation));
|
||||
chinook.QueueActivity(new Turn(0));
|
||||
chinook.QueueActivity(new HeliLand(true));
|
||||
chinook.QueueActivity(new UnloadCargo());
|
||||
chinook.QueueActivity(new CallFunc(() => Sound.Play("laugh1.aud")));
|
||||
chinook.QueueActivity(new Wait(150));
|
||||
chinook.QueueActivity(new HeliFly(chinookExitPoint.CenterLocation));
|
||||
chinook.QueueActivity(new RemoveSelf());
|
||||
}
|
||||
void FlyEinsteinFromExtractionLZ()
|
||||
{
|
||||
einsteinChinook = world.CreateActor(ChinookName, new TypeDictionary { new OwnerInit(allies), new LocationInit(extractionLZEntryPoint.Location) });
|
||||
einsteinChinook.QueueActivity(new HeliFly(extractionLZ.CenterLocation));
|
||||
einsteinChinook.QueueActivity(new Turn(0));
|
||||
einsteinChinook.QueueActivity(new HeliLand(true, 0));
|
||||
einsteinChinook.QueueActivity(new WaitFor(() => einsteinChinook.Trait<Cargo>().Passengers.Contains(einstein)));
|
||||
einsteinChinook.QueueActivity(new Wait(150));
|
||||
einsteinChinook.QueueActivity(new HeliFly(chinookExitPoint.CenterLocation));
|
||||
einsteinChinook.QueueActivity(new RemoveSelf());
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w)
|
||||
{
|
||||
allies = w.Players.Single(p => p.InternalName == "Allies");
|
||||
soviets = w.Players.Single(p => p.InternalName == "Soviets");
|
||||
var actors = w.WorldActor.Trait<SpawnMapActors>().Actors;
|
||||
insertionLZ = actors["InsertionLZ"];
|
||||
extractionLZ = actors["ExtractionLZ"];
|
||||
lab = actors["Lab"];
|
||||
insertionLZEntryPoint = actors["InsertionLZEntryPoint"];
|
||||
chinookExitPoint = actors["ChinookExitPoint"];
|
||||
extractionLZEntryPoint = actors["ExtractionLZEntryPoint"];
|
||||
shipSpawnPoint = actors["ShipSpawnPoint"];
|
||||
shipMovePoint = actors["ShipMovePoint"];
|
||||
attackEntryPoint1 = actors["SovietAttackEntryPoint1"];
|
||||
attackEntryPoint2 = actors["SovietAttackEntryPoint2"];
|
||||
Game.MoveViewport(insertionLZ.Location.ToFloat2());
|
||||
music = Sound.Play("hell226m.aud"); // Hell March
|
||||
Game.ConnectionStateChanged += StopMusic;
|
||||
}
|
||||
void FlyTanyaToInsertionLZ()
|
||||
{
|
||||
tanya = world.CreateActor(false, TanyaName, new TypeDictionary { new OwnerInit(allies) });
|
||||
var chinook = world.CreateActor(ChinookName, new TypeDictionary { new OwnerInit(allies), new LocationInit(insertionLZEntryPoint.Location) });
|
||||
chinook.Trait<Cargo>().Load(chinook, tanya);
|
||||
chinook.QueueActivity(new HeliFly(insertionLZ.CenterLocation));
|
||||
chinook.QueueActivity(new Turn(0));
|
||||
chinook.QueueActivity(new HeliLand(true, 0));
|
||||
chinook.QueueActivity(new UnloadCargo(true));
|
||||
chinook.QueueActivity(new CallFunc(() => Sound.Play("laugh1.aud")));
|
||||
chinook.QueueActivity(new CallFunc(() => tanya.QueueActivity(new Move.Move(insertionLZ.Location - new CVec(1, 0)))));
|
||||
chinook.QueueActivity(new Wait(150));
|
||||
chinook.QueueActivity(new HeliFly(chinookExitPoint.CenterLocation));
|
||||
chinook.QueueActivity(new RemoveSelf());
|
||||
}
|
||||
|
||||
private void StopMusic(OrderManager orderManager)
|
||||
{
|
||||
if (!orderManager.GameStarted)
|
||||
{
|
||||
Sound.StopSound(music);
|
||||
Game.ConnectionStateChanged -= StopMusic;
|
||||
}
|
||||
}
|
||||
}
|
||||
void SetAlliedUnitsToDefensiveStance()
|
||||
{
|
||||
foreach (var actor in world.Actors.Where(a => a.IsInWorld && a.Owner == allies && !a.IsDead()))
|
||||
{
|
||||
var at = actor.TraitOrDefault<AutoTarget>();
|
||||
if (at != null)
|
||||
{
|
||||
at.predictedStance = UnitStance.Defend;
|
||||
}
|
||||
var order = new Order("SetUnitStance", actor, false) { TargetLocation = new CPos((int)UnitStance.Defend, 0) };
|
||||
if (Game.IsHost)
|
||||
{
|
||||
world.IssueOrder(order);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w)
|
||||
{
|
||||
world = w;
|
||||
allies = w.Players.Single(p => p.InternalName == "Allies");
|
||||
soviets = w.Players.Single(p => p.InternalName == "Soviets");
|
||||
var actors = w.WorldActor.Trait<SpawnMapActors>().Actors;
|
||||
insertionLZ = actors["InsertionLZ"];
|
||||
extractionLZ = actors["ExtractionLZ"];
|
||||
lab = actors["Lab"];
|
||||
insertionLZEntryPoint = actors["InsertionLZEntryPoint"];
|
||||
chinookExitPoint = actors["ChinookExitPoint"];
|
||||
extractionLZEntryPoint = actors["ExtractionLZEntryPoint"];
|
||||
shipSpawnPoint = actors["ShipSpawnPoint"];
|
||||
shipMovePoint = actors["ShipMovePoint"];
|
||||
attackEntryPoint1 = actors["SovietAttackEntryPoint1"];
|
||||
attackEntryPoint2 = actors["SovietAttackEntryPoint2"];
|
||||
SetAlliedUnitsToDefensiveStance();
|
||||
Game.MoveViewport(insertionLZ.Location.ToFloat2());
|
||||
Game.ConnectionStateChanged += StopMusic;
|
||||
Media.PlayFMVFullscreen(w, "ally1.vqa", () =>
|
||||
{
|
||||
Media.PlayFMVFullscreen(w, "landing.vqa", () =>
|
||||
{
|
||||
FlyTanyaToInsertionLZ();
|
||||
SendPatrol();
|
||||
PlayMusic();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void PlayMusic()
|
||||
{
|
||||
if (!Rules.InstalledMusic.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var track = Rules.InstalledMusic.Random(Game.CosmeticRandom);
|
||||
Sound.PlayMusicThen(track.Value, PlayMusic);
|
||||
}
|
||||
|
||||
void StopMusic(OrderManager orderManager)
|
||||
{
|
||||
if (!orderManager.GameStarted)
|
||||
{
|
||||
Sound.StopMusic();
|
||||
Game.ConnectionStateChanged -= StopMusic;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
540
OpenRA.Mods.RA/Missions/Allies02Script.cs
Normal file
540
OpenRA.Mods.RA/Missions/Allies02Script.cs
Normal file
@@ -0,0 +1,540 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2012 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.Mods.RA.Activities;
|
||||
using OpenRA.Mods.RA.Air;
|
||||
using OpenRA.Mods.RA.Buildings;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.RA.Missions
|
||||
{
|
||||
class Allies02ScriptInfo : TraitInfo<Allies02Script>, Requires<SpawnMapActorsInfo> { }
|
||||
|
||||
class Allies02Script : IWorldLoaded, ITick
|
||||
{
|
||||
static readonly string[] Objectives =
|
||||
{
|
||||
"Hold off the Soviet forces and destroy the SAM sites. Tanya and Einstein must survive.",
|
||||
"Wait for the helicopter and extract Einstein. Tanya and Einstein must survive."
|
||||
};
|
||||
|
||||
int currentObjective;
|
||||
|
||||
Actor sam1;
|
||||
Actor sam2;
|
||||
Actor sam3;
|
||||
Actor sam4;
|
||||
Actor tanya;
|
||||
Actor einstein;
|
||||
Actor engineer;
|
||||
|
||||
Actor engineerMiss;
|
||||
|
||||
Actor chinookHusk;
|
||||
Actor allies2BasePoint;
|
||||
Actor reinforcementsEntryPoint;
|
||||
Actor extractionLZEntryPoint;
|
||||
Actor extractionLZ;
|
||||
Actor badgerEntryPoint;
|
||||
Actor badgerDropPoint;
|
||||
Actor sovietRallyPoint;
|
||||
Actor flamersEntryPoint;
|
||||
|
||||
Actor einsteinChinook;
|
||||
|
||||
World world;
|
||||
Player allies1;
|
||||
Player allies2;
|
||||
Player soviets;
|
||||
|
||||
Actor sovietBarracks;
|
||||
Actor sovietWarFactory;
|
||||
|
||||
CountdownTimer reinforcementsTimer;
|
||||
CountdownTimerWidget reinforcementsTimerWidget;
|
||||
|
||||
const string InfantryQueueName = "Infantry";
|
||||
const string VehicleQueueName = "Vehicle";
|
||||
readonly List<string> sovietInfantry = new List<string> { "e1", "e2", "e3" };
|
||||
readonly List<string> sovietVehicles = new List<string> { "3tnk" };
|
||||
static readonly string[] SovietVehicleAdditions = { "v2rl" };
|
||||
const int SovietGroupSize = 5;
|
||||
const int SovietVehicleAdditionsTicks = 1500 * 4;
|
||||
|
||||
const int ReinforcementsTicks = 1500 * 12;
|
||||
static readonly string[] Reinforcements = { "2tnk", "2tnk", "2tnk", "2tnk", "2tnk", "2tnk", "1tnk", "1tnk", "jeep", "e1", "e1", "e1", "e1", "e3", "e3", "mcv" };
|
||||
const int ReinforcementsCash = 2000;
|
||||
|
||||
const int ParatroopersTicks = 1500 * 10;
|
||||
static readonly string[] Paratroopers = { "e1", "e1", "e1", "e2", "3tnk" };
|
||||
const string BadgerName = "badr";
|
||||
|
||||
const int FlamersTicks = 1500 * 7;
|
||||
static readonly string[] Flamers = { "e4", "e4", "e4", "e4", "e4" };
|
||||
const string ApcName = "apc";
|
||||
|
||||
const string ChinookName = "tran";
|
||||
const string SignalFlareName = "flare";
|
||||
const string EngineerName = "e6";
|
||||
const int EngineerMissClearRange = 5;
|
||||
|
||||
void DisplayObjective()
|
||||
{
|
||||
Game.AddChatLine(Color.LimeGreen, "Objective", Objectives[currentObjective]);
|
||||
Sound.Play("bleep6.aud");
|
||||
}
|
||||
|
||||
void MissionFailed(string text)
|
||||
{
|
||||
if (allies1.WinState != WinState.Undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
allies1.WinState = allies2.WinState = WinState.Lost;
|
||||
if (reinforcementsTimer != null)
|
||||
{
|
||||
reinforcementsTimerWidget.Visible = false;
|
||||
}
|
||||
foreach (var actor in world.Actors.Where(a => a.IsInWorld && (a.Owner == allies1 || a.Owner == allies2) && !a.IsDead()))
|
||||
{
|
||||
actor.Kill(actor);
|
||||
}
|
||||
Game.AddChatLine(Color.Red, "Mission failed", text);
|
||||
Sound.Play("misnlst1.aud");
|
||||
}
|
||||
|
||||
void MissionAccomplished(string text)
|
||||
{
|
||||
if (allies1.WinState != WinState.Undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
allies1.WinState = allies2.WinState = WinState.Won;
|
||||
if (reinforcementsTimer != null)
|
||||
{
|
||||
reinforcementsTimerWidget.Visible = false;
|
||||
}
|
||||
Game.AddChatLine(Color.Blue, "Mission accomplished", text);
|
||||
Sound.Play("misnwon1.aud");
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (allies1.WinState != WinState.Undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (world.FrameNumber % 3500 == 1)
|
||||
{
|
||||
DisplayObjective();
|
||||
}
|
||||
if (world.FrameNumber == 1)
|
||||
{
|
||||
InitializeSovietFactories();
|
||||
StartReinforcementsTimer();
|
||||
}
|
||||
reinforcementsTimer.Tick();
|
||||
if (world.FrameNumber == ParatroopersTicks)
|
||||
{
|
||||
ParadropSovietUnits();
|
||||
}
|
||||
if (world.FrameNumber == FlamersTicks)
|
||||
{
|
||||
RushSovietFlamers();
|
||||
}
|
||||
if (world.FrameNumber == SovietVehicleAdditionsTicks)
|
||||
{
|
||||
sovietVehicles.AddRange(SovietVehicleAdditions);
|
||||
}
|
||||
if (world.FrameNumber % 25 == 0)
|
||||
{
|
||||
AddSovietCashIfRequired();
|
||||
BuildSovietUnits();
|
||||
ManageSovietUnits();
|
||||
}
|
||||
if (!engineerMiss.Destroyed && engineer == null && AlliesControlMiss())
|
||||
{
|
||||
SpawnEngineerAtMiss();
|
||||
engineerMiss.QueueActivity(new Demolish(engineerMiss, 0));
|
||||
}
|
||||
if (currentObjective == 0)
|
||||
{
|
||||
if (sam1.Destroyed && sam2.Destroyed && sam3.Destroyed && sam4.Destroyed)
|
||||
{
|
||||
currentObjective++;
|
||||
DisplayObjective();
|
||||
SpawnSignalFlare();
|
||||
Sound.Play("flaren1.aud");
|
||||
SendChinook();
|
||||
}
|
||||
}
|
||||
else if (currentObjective == 1 && einsteinChinook != null)
|
||||
{
|
||||
if (einsteinChinook.Destroyed)
|
||||
{
|
||||
MissionFailed("The extraction helicopter was destroyed.");
|
||||
}
|
||||
else if (!world.Map.IsInMap(einsteinChinook.Location) && einsteinChinook.Trait<Cargo>().Passengers.Contains(einstein))
|
||||
{
|
||||
MissionAccomplished("Einstein was rescued.");
|
||||
}
|
||||
}
|
||||
if (tanya.Destroyed)
|
||||
{
|
||||
MissionFailed("Tanya was killed.");
|
||||
}
|
||||
else if (einstein.Destroyed)
|
||||
{
|
||||
MissionFailed("Einstein was killed.");
|
||||
}
|
||||
else if (!world.Actors.Any(a => a.IsInWorld && a.HasTrait<Building>() && !a.HasTrait<Wall>() && a.Owner == allies2))
|
||||
{
|
||||
MissionFailed("The Allied reinforcements have been defeated.");
|
||||
}
|
||||
}
|
||||
|
||||
void AddSovietCashIfRequired()
|
||||
{
|
||||
var resources = soviets.PlayerActor.Trait<PlayerResources>();
|
||||
if (resources.Cash < ReinforcementsCash)
|
||||
{
|
||||
resources.GiveCash(ReinforcementsCash);
|
||||
}
|
||||
}
|
||||
|
||||
void BuildSovietUnits()
|
||||
{
|
||||
var powerManager = soviets.PlayerActor.Trait<PowerManager>();
|
||||
if (powerManager.ExcessPower < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!sovietBarracks.Destroyed)
|
||||
{
|
||||
BuildSovietUnit(InfantryQueueName, sovietInfantry.Random(world.SharedRandom));
|
||||
}
|
||||
if (!sovietWarFactory.Destroyed)
|
||||
{
|
||||
BuildSovietUnit(VehicleQueueName, sovietVehicles.Random(world.SharedRandom));
|
||||
}
|
||||
}
|
||||
|
||||
void ManageSovietUnits()
|
||||
{
|
||||
var idleSovietUnits = ForcesNearActor(allies2BasePoint, 10).Where(a => a.Owner == soviets && a.IsIdle);
|
||||
var idleSovietUnitsAtRP = ForcesNearActor(sovietRallyPoint, 5).Where(a => a.Owner == soviets && a.IsIdle);
|
||||
if (idleSovietUnitsAtRP.Count() >= SovietGroupSize)
|
||||
{
|
||||
idleSovietUnits = idleSovietUnits.Union(idleSovietUnitsAtRP);
|
||||
}
|
||||
foreach (var unit in idleSovietUnits)
|
||||
{
|
||||
var closestAlliedBuilding = ClosestAlliedBuilding(unit, 20);
|
||||
if (closestAlliedBuilding != null)
|
||||
{
|
||||
unit.QueueActivity(new AttackMove.AttackMoveActivity(unit, new Move.Move(closestAlliedBuilding.Location, 3)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Actor ClosestAlliedBuilding(Actor actor, int range)
|
||||
{
|
||||
return BuildingsNearActor(actor, range)
|
||||
.Where(a => a.Owner == allies2)
|
||||
.OrderBy(a => (actor.Location - a.Location).LengthSquared)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
void InitializeSovietFactories()
|
||||
{
|
||||
sovietBarracks.Trait<RallyPoint>().rallyPoint = sovietRallyPoint.Location;
|
||||
sovietWarFactory.Trait<RallyPoint>().rallyPoint = sovietRallyPoint.Location;
|
||||
sovietBarracks.Trait<PrimaryBuilding>().SetPrimaryProducer(sovietBarracks, true);
|
||||
sovietWarFactory.Trait<PrimaryBuilding>().SetPrimaryProducer(sovietWarFactory, true);
|
||||
}
|
||||
|
||||
IEnumerable<ProductionQueue> FindQueues(Player player, string category)
|
||||
{
|
||||
return world.ActorsWithTrait<ProductionQueue>()
|
||||
.Where(a => a.Actor.Owner == player && a.Trait.Info.Type == category)
|
||||
.Select(a => a.Trait);
|
||||
}
|
||||
|
||||
void BuildSovietUnit(string category, string unit)
|
||||
{
|
||||
var queue = FindQueues(soviets, category).FirstOrDefault(q => q.CurrentItem() == null);
|
||||
if (queue == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var order = Order.StartProduction(queue.self, unit, 1);
|
||||
if (Game.IsHost)
|
||||
{
|
||||
world.IssueOrder(order);
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnSignalFlare()
|
||||
{
|
||||
world.CreateActor(SignalFlareName, new TypeDictionary { new OwnerInit(allies1), new LocationInit(extractionLZ.Location) });
|
||||
}
|
||||
|
||||
void StartReinforcementsTimer()
|
||||
{
|
||||
Sound.Play("timergo1.aud");
|
||||
reinforcementsTimer = new CountdownTimer(ReinforcementsTicks, ReinforcementsTimerExpired);
|
||||
reinforcementsTimerWidget = new CountdownTimerWidget(reinforcementsTimer, "Reinforcements arrive in", new float2(128, 96));
|
||||
Ui.Root.AddChild(reinforcementsTimerWidget);
|
||||
}
|
||||
|
||||
void ParadropSovietUnits()
|
||||
{
|
||||
var badger = world.CreateActor(BadgerName, new TypeDictionary
|
||||
{
|
||||
new LocationInit(badgerEntryPoint.Location),
|
||||
new OwnerInit(soviets),
|
||||
new FacingInit(Util.GetFacing(badgerDropPoint.Location - badgerEntryPoint.Location, 0)),
|
||||
new AltitudeInit(Rules.Info[BadgerName].Traits.Get<PlaneInfo>().CruiseAltitude),
|
||||
});
|
||||
badger.QueueActivity(new FlyAttack(Target.FromCell(badgerDropPoint.Location)));
|
||||
badger.Trait<ParaDrop>().SetLZ(badgerDropPoint.Location);
|
||||
var cargo = badger.Trait<Cargo>();
|
||||
foreach (var unit in Paratroopers)
|
||||
{
|
||||
cargo.Load(badger, world.CreateActor(false, unit, new TypeDictionary { new OwnerInit(soviets) }));
|
||||
}
|
||||
}
|
||||
|
||||
void RushSovietFlamers()
|
||||
{
|
||||
var closestAlliedBuilding = ClosestAlliedBuilding(badgerDropPoint, 10);
|
||||
if (closestAlliedBuilding == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var apc = world.CreateActor(ApcName, new TypeDictionary { new OwnerInit(soviets), new LocationInit(flamersEntryPoint.Location) });
|
||||
foreach (var flamer in Flamers)
|
||||
{
|
||||
var unit = world.CreateActor(false, flamer, new TypeDictionary { new OwnerInit(soviets) });
|
||||
apc.Trait<Cargo>().Load(apc, unit);
|
||||
}
|
||||
apc.QueueActivity(new MoveAdjacentTo(Target.FromActor(closestAlliedBuilding)));
|
||||
apc.QueueActivity(new UnloadCargo(true));
|
||||
}
|
||||
|
||||
void ReinforcementsTimerExpired(CountdownTimer countdownTimer)
|
||||
{
|
||||
reinforcementsTimerWidget.Visible = false;
|
||||
SendReinforcements();
|
||||
}
|
||||
|
||||
void SendReinforcements()
|
||||
{
|
||||
Sound.Play("reinfor1.aud");
|
||||
var resources = allies2.PlayerActor.Trait<PlayerResources>();
|
||||
resources.GiveCash(2000);
|
||||
foreach (var unit in Reinforcements)
|
||||
{
|
||||
var actor = world.CreateActor(unit, new TypeDictionary
|
||||
{
|
||||
new LocationInit(reinforcementsEntryPoint.Location),
|
||||
new FacingInit(0),
|
||||
new OwnerInit(allies2)
|
||||
});
|
||||
actor.QueueActivity(new Move.Move(allies2BasePoint.Location));
|
||||
}
|
||||
}
|
||||
|
||||
void SendChinook()
|
||||
{
|
||||
einsteinChinook = world.CreateActor(ChinookName, new TypeDictionary { new OwnerInit(allies1), new LocationInit(extractionLZEntryPoint.Location) });
|
||||
einsteinChinook.QueueActivity(new HeliFly(extractionLZ.CenterLocation));
|
||||
einsteinChinook.QueueActivity(new Turn(0));
|
||||
einsteinChinook.QueueActivity(new HeliLand(true, 0));
|
||||
einsteinChinook.QueueActivity(new WaitFor(() => einsteinChinook.Trait<Cargo>().Passengers.Contains(einstein)));
|
||||
einsteinChinook.QueueActivity(new Wait(150));
|
||||
einsteinChinook.QueueActivity(new HeliFly(extractionLZEntryPoint.CenterLocation));
|
||||
einsteinChinook.QueueActivity(new RemoveSelf());
|
||||
}
|
||||
|
||||
IEnumerable<Actor> UnitsNearActor(Actor actor, int range)
|
||||
{
|
||||
return world.FindUnitsInCircle(actor.CenterLocation, Game.CellSize * range)
|
||||
.Where(a => a.IsInWorld && a != world.WorldActor && !a.Destroyed && !a.Owner.NonCombatant);
|
||||
}
|
||||
|
||||
IEnumerable<Actor> BuildingsNearActor(Actor actor, int range)
|
||||
{
|
||||
return UnitsNearActor(actor, range).Where(a => a.HasTrait<Building>() && !a.HasTrait<Wall>());
|
||||
}
|
||||
|
||||
IEnumerable<Actor> ForcesNearActor(Actor actor, int range)
|
||||
{
|
||||
return UnitsNearActor(actor, range).Where(a => a.HasTrait<IMove>());
|
||||
}
|
||||
|
||||
bool AlliesControlMiss()
|
||||
{
|
||||
var units = ForcesNearActor(engineerMiss, EngineerMissClearRange);
|
||||
return units.Any() && units.All(a => a.Owner == allies1);
|
||||
}
|
||||
|
||||
void SpawnEngineerAtMiss()
|
||||
{
|
||||
engineer = world.CreateActor(EngineerName, new TypeDictionary { new OwnerInit(allies1), new LocationInit(engineerMiss.Location) });
|
||||
engineer.QueueActivity(new Move.Move(engineerMiss.Location + new CVec(5, 0)));
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w)
|
||||
{
|
||||
world = w;
|
||||
allies1 = w.Players.Single(p => p.InternalName == "Allies1");
|
||||
allies2 = w.Players.Single(p => p.InternalName == "Allies2");
|
||||
soviets = w.Players.Single(p => p.InternalName == "Soviets");
|
||||
var actors = w.WorldActor.Trait<SpawnMapActors>().Actors;
|
||||
sam1 = actors["SAM1"];
|
||||
sam2 = actors["SAM2"];
|
||||
sam3 = actors["SAM3"];
|
||||
sam4 = actors["SAM4"];
|
||||
tanya = actors["Tanya"];
|
||||
einstein = actors["Einstein"];
|
||||
chinookHusk = actors["ChinookHusk"];
|
||||
allies2BasePoint = actors["Allies2BasePoint"];
|
||||
reinforcementsEntryPoint = actors["ReinforcementsEntryPoint"];
|
||||
extractionLZ = actors["ExtractionLZ"];
|
||||
extractionLZEntryPoint = actors["ExtractionLZEntryPoint"];
|
||||
badgerEntryPoint = actors["BadgerEntryPoint"];
|
||||
badgerDropPoint = actors["BadgerDropPoint"];
|
||||
engineerMiss = actors["EngineerMiss"];
|
||||
sovietBarracks = actors["SovietBarracks"];
|
||||
sovietWarFactory = actors["SovietWarFactory"];
|
||||
sovietRallyPoint = actors["SovietRallyPoint"];
|
||||
flamersEntryPoint = actors["FlamersEntryPoint"];
|
||||
var shroud = w.WorldActor.Trait<Shroud>();
|
||||
shroud.Explore(w, sam1.Location, 2);
|
||||
shroud.Explore(w, sam2.Location, 2);
|
||||
shroud.Explore(w, sam3.Location, 2);
|
||||
shroud.Explore(w, sam4.Location, 2);
|
||||
if (w.LocalPlayer == null || w.LocalPlayer == allies1)
|
||||
{
|
||||
Game.MoveViewport(chinookHusk.Location.ToFloat2());
|
||||
}
|
||||
else
|
||||
{
|
||||
Game.MoveViewport(allies2BasePoint.Location.ToFloat2());
|
||||
}
|
||||
PlayMusic();
|
||||
Game.ConnectionStateChanged += StopMusic;
|
||||
}
|
||||
|
||||
void PlayMusic()
|
||||
{
|
||||
if (!Rules.InstalledMusic.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var track = Rules.InstalledMusic.Random(Game.CosmeticRandom);
|
||||
Sound.PlayMusicThen(track.Value, PlayMusic);
|
||||
}
|
||||
|
||||
void StopMusic(OrderManager orderManager)
|
||||
{
|
||||
if (!orderManager.GameStarted)
|
||||
{
|
||||
Sound.StopMusic();
|
||||
Game.ConnectionStateChanged -= StopMusic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CountdownTimer
|
||||
{
|
||||
public int TicksLeft { get; set; }
|
||||
|
||||
public Action<CountdownTimer> OnExpired { get; set; }
|
||||
public Action<CountdownTimer> OnOneMinuteRemaining { get; set; }
|
||||
public Action<CountdownTimer> OnTwoMinutesRemaining { get; set; }
|
||||
public Action<CountdownTimer> OnThreeMinutesRemaining { get; set; }
|
||||
public Action<CountdownTimer> OnFourMinutesRemaining { get; set; }
|
||||
public Action<CountdownTimer> OnFiveMinutesRemaining { get; set; }
|
||||
public Action<CountdownTimer> OnTenMinutesRemaining { get; set; }
|
||||
public Action<CountdownTimer> OnTwentyMinutesRemaining { get; set; }
|
||||
public Action<CountdownTimer> OnThirtyMinutesRemaining { get; set; }
|
||||
public Action<CountdownTimer> OnFortyMinutesRemaining { get; set; }
|
||||
|
||||
public CountdownTimer(int ticksLeft, Action<CountdownTimer> onExpired)
|
||||
{
|
||||
TicksLeft = ticksLeft;
|
||||
OnExpired = onExpired;
|
||||
OnOneMinuteRemaining = t => Sound.Play("1minr.aud");
|
||||
OnTwoMinutesRemaining = t => Sound.Play("2minr.aud");
|
||||
OnThreeMinutesRemaining = t => Sound.Play("3minr.aud");
|
||||
OnFourMinutesRemaining = t => Sound.Play("4minr.aud");
|
||||
OnFiveMinutesRemaining = t => Sound.Play("5minr.aud");
|
||||
OnTenMinutesRemaining = t => Sound.Play("10minr.aud");
|
||||
OnTwentyMinutesRemaining = t => Sound.Play("20minr.aud");
|
||||
OnThirtyMinutesRemaining = t => Sound.Play("30minr.aud");
|
||||
OnFortyMinutesRemaining = t => Sound.Play("40minr.aud");
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (TicksLeft > 0)
|
||||
{
|
||||
TicksLeft--;
|
||||
switch (TicksLeft)
|
||||
{
|
||||
case 1500 * 00: OnExpired(this); break;
|
||||
case 1500 * 01: OnOneMinuteRemaining(this); break;
|
||||
case 1500 * 02: OnTwoMinutesRemaining(this); break;
|
||||
case 1500 * 03: OnThreeMinutesRemaining(this); break;
|
||||
case 1500 * 04: OnFourMinutesRemaining(this); break;
|
||||
case 1500 * 05: OnFiveMinutesRemaining(this); break;
|
||||
case 1500 * 10: OnTenMinutesRemaining(this); break;
|
||||
case 1500 * 20: OnTwentyMinutesRemaining(this); break;
|
||||
case 1500 * 30: OnThirtyMinutesRemaining(this); break;
|
||||
case 1500 * 40: OnFortyMinutesRemaining(this); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CountdownTimerWidget : Widget
|
||||
{
|
||||
public CountdownTimer CountdownTimer { get; set; }
|
||||
public string Header { get; set; }
|
||||
public float2 Position { get; set; }
|
||||
|
||||
public CountdownTimerWidget(CountdownTimer countdownTimer, string header, float2 position)
|
||||
{
|
||||
CountdownTimer = countdownTimer;
|
||||
Header = header;
|
||||
Position = position;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
if (!IsVisible())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var font = Game.Renderer.Fonts["Bold"];
|
||||
var text = "{0}: {1}".F(Header, WidgetUtils.FormatTime(CountdownTimer.TicksLeft));
|
||||
font.DrawTextWithContrast(text, Position, CountdownTimer.TicksLeft == 0 && Game.LocalTick % 60 >= 30 ? Color.Red : Color.White, Color.Black, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,25 @@ namespace OpenRA.Mods.RA.Move
|
||||
{SubCell.FullCell, new PVecInt(0,0)},
|
||||
};
|
||||
|
||||
public bool CanEnterCell(World world, Player owner, CPos cell, Actor ignoreActor, bool checkTransientActors)
|
||||
static bool IsMovingInMyDirection(Actor self, Actor other)
|
||||
{
|
||||
if (!other.IsMoving()) return false;
|
||||
if (self == null) return true;
|
||||
|
||||
var selfMobile = self.TraitOrDefault<Mobile>();
|
||||
if (selfMobile == null) return false;
|
||||
|
||||
var otherMobile = other.TraitOrDefault<Mobile>();
|
||||
if (otherMobile == null) return false;
|
||||
|
||||
// Sign of dot-product indicates (roughly) if vectors are facing in same or opposite directions:
|
||||
var dp = CVec.Dot((selfMobile.toCell - self.Location), (otherMobile.toCell - other.Location));
|
||||
if (dp <= 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor, bool checkTransientActors, bool blockedByMovers)
|
||||
{
|
||||
if (MovementCostForCell(world, cell) == int.MaxValue)
|
||||
return false;
|
||||
@@ -85,7 +103,12 @@ namespace OpenRA.Mods.RA.Move
|
||||
if (SharesCell && world.ActorMap.HasFreeSubCell(cell))
|
||||
return true;
|
||||
|
||||
var blockingActors = world.ActorMap.GetUnitsAt(cell).Where(x => x != ignoreActor).ToList();
|
||||
var blockingActors = world.ActorMap.GetUnitsAt(cell)
|
||||
.Where(x => x != ignoreActor)
|
||||
// Neutral/enemy units are blockers. Allied units that are moving are not blockers.
|
||||
.Where(x => blockedByMovers || ((self.Owner.Stances[x.Owner] != Stance.Ally) || !IsMovingInMyDirection(self, x)))
|
||||
.ToList();
|
||||
|
||||
if (checkTransientActors && blockingActors.Count > 0)
|
||||
{
|
||||
// Non-sharable unit can enter a cell with shareable units only if it can crush all of them
|
||||
@@ -93,7 +116,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
return false;
|
||||
|
||||
if (blockingActors.Any(a => !(a.HasTrait<ICrushable>() &&
|
||||
a.TraitsImplementing<ICrushable>().Any(b => b.CrushableBy(Crushes, owner)))))
|
||||
a.TraitsImplementing<ICrushable>().Any(b => b.CrushableBy(Crushes, self.Owner)))))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -342,7 +365,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
|
||||
public bool CanEnterCell(CPos cell, Actor ignoreActor, bool checkTransientActors)
|
||||
{
|
||||
return Info.CanEnterCell(self.World, self.Owner, cell, ignoreActor, checkTransientActors);
|
||||
return Info.CanEnterCell(self.World, self, cell, ignoreActor, checkTransientActors, true);
|
||||
}
|
||||
|
||||
public void EnteringCell(Actor self)
|
||||
@@ -456,6 +479,10 @@ namespace OpenRA.Mods.RA.Move
|
||||
{
|
||||
IsQueued = forceQueued;
|
||||
cursor = "move";
|
||||
|
||||
if (self.World.LocalPlayer.Shroud.IsExplored(location))
|
||||
cursor = self.World.GetTerrainInfo(location).CustomCursor ?? cursor;
|
||||
|
||||
if (!self.World.Map.IsInMap(location) || (self.World.LocalPlayer.Shroud.IsExplored(location) &&
|
||||
unitType.MovementCostForCell(self.World, location) == int.MaxValue))
|
||||
cursor = "move-blocked";
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
{
|
||||
this.getPath = (self,mobile) =>
|
||||
self.World.WorldActor.Trait<PathFinder>().FindPath(
|
||||
PathSearch.FromPoint( self.World, mobile.Info, self.Owner, mobile.toCell, destination, false )
|
||||
PathSearch.FromPoint( self.World, mobile.Info, self, mobile.toCell, destination, false )
|
||||
.WithoutLaneBias());
|
||||
this.destination = destination;
|
||||
this.nearEnough = 0;
|
||||
@@ -49,7 +49,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
{
|
||||
this.getPath = (self,mobile) =>
|
||||
self.World.WorldActor.Trait<PathFinder>().FindPath(
|
||||
PathSearch.FromPoint( self.World, mobile.Info, self.Owner, mobile.toCell, destination, false )
|
||||
PathSearch.FromPoint( self.World, mobile.Info, self, mobile.toCell, destination, false )
|
||||
.WithIgnoredBuilding( ignoreBuilding ));
|
||||
|
||||
this.destination = destination;
|
||||
@@ -388,4 +388,23 @@ namespace OpenRA.Mods.RA.Move
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ActorExtensionsForMove
|
||||
{
|
||||
public static bool IsMoving(this Actor self)
|
||||
{
|
||||
if (self.IsIdle) return false;
|
||||
|
||||
Activity a = self.GetCurrentActivity();
|
||||
Debug.Assert(a != null);
|
||||
|
||||
// Dirty, but it suffices until we do something better:
|
||||
if (a.GetType() == typeof(Move)) return true;
|
||||
if (a.GetType() == typeof(MoveAdjacentTo)) return true;
|
||||
if (a.GetType() == typeof(AttackMove)) return true;
|
||||
|
||||
// Not a move:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ namespace OpenRA.Mods.RA.Move
|
||||
var mi = self.Info.Traits.Get<MobileInfo>();
|
||||
|
||||
var pb = FindBidiPath(
|
||||
PathSearch.FromPoint(world, mi, self.Owner, target, from, true),
|
||||
PathSearch.FromPoint(world, mi, self.Owner, from, target, true).InReverse()
|
||||
PathSearch.FromPoint(world, mi, self, target, from, true),
|
||||
PathSearch.FromPoint(world, mi, self, from, target, true).InReverse()
|
||||
);
|
||||
|
||||
CheckSanePath2(pb, from, target);
|
||||
@@ -73,11 +73,11 @@ namespace OpenRA.Mods.RA.Move
|
||||
{
|
||||
var mi = self.Info.Traits.Get<MobileInfo>();
|
||||
var tilesInRange = world.FindTilesInCircle(target, range)
|
||||
.Where(t => mi.CanEnterCell(self.World, self.Owner, t, null, true));
|
||||
.Where(t => mi.CanEnterCell(self.World, self, t, null, true, true));
|
||||
|
||||
var path = FindBidiPath(
|
||||
PathSearch.FromPoints(world, mi, self.Owner, tilesInRange, src, true),
|
||||
PathSearch.FromPoint(world, mi, self.Owner, src, target, true).InReverse()
|
||||
PathSearch.FromPoints(world, mi, self, tilesInRange, src, true),
|
||||
PathSearch.FromPoint(world, mi, self, src, target, true).InReverse()
|
||||
);
|
||||
|
||||
return path;
|
||||
|
||||
@@ -28,14 +28,15 @@ namespace OpenRA.Mods.RA.Move
|
||||
public bool inReverse;
|
||||
|
||||
MobileInfo mobileInfo;
|
||||
Player owner;
|
||||
Actor self;
|
||||
public Player owner { get { return self.Owner; } }
|
||||
|
||||
public PathSearch(World world, MobileInfo mobileInfo, Player owner)
|
||||
public PathSearch(World world, MobileInfo mobileInfo, Actor self)
|
||||
{
|
||||
this.world = world;
|
||||
cellInfo = InitCellInfo();
|
||||
this.mobileInfo = mobileInfo;
|
||||
this.owner = owner;
|
||||
this.self = self;
|
||||
customCost = null;
|
||||
queue = new PriorityQueue<PathDistance>();
|
||||
}
|
||||
@@ -120,7 +121,7 @@ namespace OpenRA.Mods.RA.Move
|
||||
if (costHere == int.MaxValue)
|
||||
continue;
|
||||
|
||||
if (!mobileInfo.CanEnterCell(world, owner, newHere, ignoreBuilding, checkForBlocked))
|
||||
if (!mobileInfo.CanEnterCell(world, self, newHere, ignoreBuilding, checkForBlocked, false))
|
||||
continue;
|
||||
|
||||
if (customBlock != null && customBlock(newHere))
|
||||
@@ -182,18 +183,18 @@ namespace OpenRA.Mods.RA.Move
|
||||
queue.Add(new PathDistance(heuristic(location), location));
|
||||
}
|
||||
|
||||
public static PathSearch Search(World world, MobileInfo mi, Player owner, bool checkForBlocked)
|
||||
public static PathSearch Search(World world, MobileInfo mi, Actor self, bool checkForBlocked)
|
||||
{
|
||||
var search = new PathSearch(world, mi, owner)
|
||||
var search = new PathSearch(world, mi, self)
|
||||
{
|
||||
checkForBlocked = checkForBlocked
|
||||
};
|
||||
return search;
|
||||
}
|
||||
|
||||
public static PathSearch FromPoint(World world, MobileInfo mi, Player owner, CPos from, CPos target, bool checkForBlocked)
|
||||
public static PathSearch FromPoint(World world, MobileInfo mi, Actor self, CPos from, CPos target, bool checkForBlocked)
|
||||
{
|
||||
var search = new PathSearch(world, mi, owner)
|
||||
var search = new PathSearch(world, mi, self)
|
||||
{
|
||||
heuristic = DefaultEstimator(target),
|
||||
checkForBlocked = checkForBlocked
|
||||
@@ -203,9 +204,9 @@ namespace OpenRA.Mods.RA.Move
|
||||
return search;
|
||||
}
|
||||
|
||||
public static PathSearch FromPoints(World world, MobileInfo mi, Player owner, IEnumerable<CPos> froms, CPos target, bool checkForBlocked)
|
||||
public static PathSearch FromPoints(World world, MobileInfo mi, Actor self, IEnumerable<CPos> froms, CPos target, bool checkForBlocked)
|
||||
{
|
||||
var search = new PathSearch(world, mi, owner)
|
||||
var search = new PathSearch(world, mi, self)
|
||||
{
|
||||
heuristic = DefaultEstimator(target),
|
||||
checkForBlocked = checkForBlocked
|
||||
|
||||
@@ -127,6 +127,7 @@
|
||||
<Compile Include="Attack\AttackBase.cs" />
|
||||
<Compile Include="Attack\AttackFrontal.cs" />
|
||||
<Compile Include="Attack\AttackLeap.cs" />
|
||||
<Compile Include="Attack\AttackLoyalty.cs" />
|
||||
<Compile Include="Attack\AttackMedic.cs" />
|
||||
<Compile Include="Attack\AttackOmni.cs" />
|
||||
<Compile Include="Attack\AttackPopupTurreted.cs" />
|
||||
@@ -236,6 +237,7 @@
|
||||
<Compile Include="Mine.cs" />
|
||||
<Compile Include="Minelayer.cs" />
|
||||
<Compile Include="Missions\Allies01Script.cs" />
|
||||
<Compile Include="Missions\Allies02Script.cs" />
|
||||
<Compile Include="Missions\DefaultShellmapScript.cs" />
|
||||
<Compile Include="Modifiers\FrozenUnderFog.cs" />
|
||||
<Compile Include="Modifiers\HiddenUnderFog.cs" />
|
||||
@@ -384,6 +386,7 @@
|
||||
<Compile Include="World\PlayMusicOnMapLoad.cs" />
|
||||
<Compile Include="World\SmudgeLayer.cs" />
|
||||
<Compile Include="Player\BaseAttackNotifier.cs" />
|
||||
<Compile Include="Player\HarvesterAttackNotifier.cs" />
|
||||
<Compile Include="InfiltrateForExploration.cs" />
|
||||
<Compile Include="InfiltrateForCash.cs" />
|
||||
<Compile Include="RenderShroudCircle.cs" />
|
||||
|
||||
@@ -58,8 +58,7 @@ namespace OpenRA.Mods.RA.Orders
|
||||
if (!world.CanPlaceBuilding( Building, BuildingInfo, topLeft, null)
|
||||
|| !BuildingInfo.IsCloseEnoughToBase(world, Producer.Owner, Building, topLeft))
|
||||
{
|
||||
var eva = world.WorldActor.Info.Traits.Get<EvaAlertsInfo>();
|
||||
Sound.Play(eva.BuildingCannotPlaceAudio);
|
||||
Sound.PlayNotification(Producer.Owner, "Speech", "BuildingCannotPlaceAudio", Producer.Owner.Country.Race);
|
||||
yield break;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace OpenRA.Mods.RA
|
||||
public readonly int TickLifetime = 30;
|
||||
public readonly int TickVelocity = 2;
|
||||
public readonly int TickRate = 10;
|
||||
public readonly int DockAngle = 64;
|
||||
|
||||
public virtual object Create(ActorInitializer init) { return new OreRefinery(init.self, this); }
|
||||
}
|
||||
@@ -45,7 +46,7 @@ namespace OpenRA.Mods.RA
|
||||
public bool AllowDocking { get { return !preventDock; } }
|
||||
public CVec DeliverOffset { get { return (CVec)Info.DockOffset; } }
|
||||
|
||||
public virtual Activity DockSequence(Actor harv, Actor self) { return new RAHarvesterDockSequence(harv, self); }
|
||||
public virtual Activity DockSequence(Actor harv, Actor self) { return new RAHarvesterDockSequence(harv, self, Info.DockAngle); }
|
||||
|
||||
public OreRefinery(Actor self, OreRefineryInfo info)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2012 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,
|
||||
@@ -16,7 +16,7 @@ namespace OpenRA.Mods.RA
|
||||
class PaletteFromCurrentTilesetInfo : ITraitInfo
|
||||
{
|
||||
public readonly string Name = null;
|
||||
public readonly bool Transparent = true;
|
||||
public readonly int[] ShadowIndex = { };
|
||||
|
||||
public object Create(ActorInitializer init) { return new PaletteFromCurrentTileset(init.world, this); }
|
||||
}
|
||||
@@ -34,7 +34,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public void InitPalette( OpenRA.Graphics.WorldRenderer wr )
|
||||
{
|
||||
wr.AddPalette( info.Name, new Palette( FileSystem.Open( world.TileSet.Palette ), info.Transparent ) );
|
||||
wr.AddPalette( info.Name, new Palette( FileSystem.Open( world.TileSet.Palette ), info.ShadowIndex ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2012 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,
|
||||
@@ -19,7 +19,7 @@ namespace OpenRA.Mods.RA
|
||||
public readonly string Name = null;
|
||||
public readonly string Tileset = null;
|
||||
public readonly string Filename = null;
|
||||
public readonly bool Transparent = true;
|
||||
public readonly int[] ShadowIndex = { };
|
||||
|
||||
public object Create(ActorInitializer init) { return new PaletteFromFile(init.world, this); }
|
||||
}
|
||||
@@ -37,7 +37,7 @@ namespace OpenRA.Mods.RA
|
||||
public void InitPalette( WorldRenderer wr )
|
||||
{
|
||||
if( info.Tileset == null || info.Tileset.ToLowerInvariant() == world.Map.Tileset.ToLowerInvariant() )
|
||||
wr.AddPalette( info.Name, new Palette( FileSystem.Open( info.Filename ), info.Transparent ) );
|
||||
wr.AddPalette( info.Name, new Palette( FileSystem.Open( info.Filename ), info.ShadowIndex ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public class Passenger : IIssueOrder, IResolveOrder, IOrderVoice
|
||||
{
|
||||
readonly PassengerInfo info;
|
||||
public readonly PassengerInfo info;
|
||||
public Passenger( PassengerInfo info ) { this.info = info; }
|
||||
|
||||
public IEnumerable<IOrderTargeter> Orders
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2012 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,
|
||||
@@ -17,9 +17,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class BaseAttackNotifierInfo : ITraitInfo
|
||||
{
|
||||
public readonly int NotifyInterval = 30; /* seconds */
|
||||
public readonly string Audio = "baseatk1.aud";
|
||||
public readonly string Race = null;
|
||||
public readonly int NotifyInterval = 30; // seconds
|
||||
|
||||
public object Create(ActorInitializer init) { return new BaseAttackNotifier(this); }
|
||||
}
|
||||
@@ -35,17 +33,16 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public void Damaged(Actor self, AttackInfo e)
|
||||
{
|
||||
if (info.Race != null && info.Race != self.Owner.Country.Race) return;
|
||||
/* only track last hit against our base */
|
||||
// only track last hit against our base
|
||||
if (!self.HasTrait<Building>())
|
||||
return;
|
||||
|
||||
/* don't track self-damage */
|
||||
// don't track self-damage
|
||||
if (e.Attacker != null && e.Attacker.Owner == self.Owner)
|
||||
return;
|
||||
|
||||
if (self.World.FrameNumber - lastAttackTime > info.NotifyInterval * 25)
|
||||
Sound.PlayToPlayer(self.Owner, info.Audio);
|
||||
Sound.PlayNotification(self.Owner, "Speech", "BaseAttack", self.Owner.Country.Race);
|
||||
|
||||
lastAttackLocation = self.CenterLocation.ToCPos();
|
||||
lastAttackTime = self.World.FrameNumber;
|
||||
|
||||
52
OpenRA.Mods.RA/Player/HarvesterAttackNotifier.cs
Normal file
52
OpenRA.Mods.RA/Player/HarvesterAttackNotifier.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2012 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 OpenRA.Mods.RA.Buildings;
|
||||
using OpenRA.Mods.RA.Effects;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class HarvesterAttackNotifierInfo : ITraitInfo
|
||||
{
|
||||
public readonly int NotifyInterval = 30; // seconds
|
||||
|
||||
public object Create(ActorInitializer init) { return new HarvesterAttackNotifier(this); }
|
||||
}
|
||||
|
||||
public class HarvesterAttackNotifier : INotifyDamage
|
||||
{
|
||||
HarvesterAttackNotifierInfo info;
|
||||
|
||||
public int lastAttackTime = -1;
|
||||
public CPos lastAttackLocation;
|
||||
|
||||
public HarvesterAttackNotifier(HarvesterAttackNotifierInfo info) { this.info = info; }
|
||||
|
||||
public void Damaged(Actor self, AttackInfo e)
|
||||
{
|
||||
// only track last hit against our base
|
||||
if (!self.HasTrait<Harvester>())
|
||||
return;
|
||||
|
||||
// don't track self-damage
|
||||
if (e.Attacker != null && e.Attacker.Owner == self.Owner)
|
||||
return;
|
||||
|
||||
if (self.World.FrameNumber - lastAttackTime > info.NotifyInterval * 25)
|
||||
Sound.PlayNotification(self.Owner, "Speech", "HarvesterAttack", self.Owner.Country.Race);
|
||||
|
||||
lastAttackLocation = self.CenterLocation.ToCPos();
|
||||
lastAttackTime = self.World.FrameNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +82,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
if (GetNumBuildables(self.Owner) > prevItems)
|
||||
w.Add(new DelayedAction(10,
|
||||
() => Sound.PlayToPlayer(order.Player,
|
||||
w.WorldActor.Info.Traits.Get<EvaAlertsInfo>().NewOptions)));
|
||||
() => Sound.PlayNotification(order.Player, "Speech", "NewOptions", order.Player.Country.Race)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ namespace OpenRA.Mods.RA
|
||||
public float BuildSpeed = 0.4f;
|
||||
public readonly int LowPowerSlowdown = 3;
|
||||
|
||||
public readonly string ReadyAudio = "unitrdy1.aud";
|
||||
public readonly string BlockedAudio = "nobuild1.aud";
|
||||
public readonly string QueuedAudio = "train1.aud";
|
||||
public readonly string OnHoldAudio = "onhold1.aud";
|
||||
public readonly string CancelledAudio = "cancld1.aud";
|
||||
public readonly string ReadyAudio = "UnitReady";
|
||||
public readonly string BlockedAudio = "NoBuild";
|
||||
public readonly string QueuedAudio = "Training";
|
||||
public readonly string OnHoldAudio = "OnHold";
|
||||
public readonly string CancelledAudio = "Cancelled";
|
||||
|
||||
public virtual object Create(ActorInitializer init) { return new ProductionQueue(init.self, init.self.Owner.PlayerActor, this); }
|
||||
}
|
||||
@@ -211,17 +211,15 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
if (isBuilding && !hasPlayedSound)
|
||||
{
|
||||
Sound.PlayToPlayer(order.Player, Info.ReadyAudio);
|
||||
hasPlayedSound = true;
|
||||
hasPlayedSound = Sound.PlayNotification(self.Owner, "Speech", Info.ReadyAudio, self.Owner.Country.Race);
|
||||
}
|
||||
else if (!isBuilding)
|
||||
{
|
||||
if (BuildUnit(order.TargetString))
|
||||
Sound.PlayToPlayer(order.Player, Info.ReadyAudio);
|
||||
Sound.PlayNotification(self.Owner, "Speech", Info.ReadyAudio, self.Owner.Country.Race);
|
||||
else if (!hasPlayedSound && time > 0)
|
||||
{
|
||||
Sound.PlayToPlayer(order.Player, Info.BlockedAudio);
|
||||
hasPlayedSound = true;
|
||||
hasPlayedSound = Sound.PlayNotification(self.Owner, "Speech", Info.BlockedAudio, self.Owner.Country.Race);
|
||||
}
|
||||
}
|
||||
})));
|
||||
|
||||
@@ -66,8 +66,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
isPrimary = true;
|
||||
|
||||
var eva = self.World.WorldActor.Info.Traits.Get<EvaAlertsInfo>();
|
||||
Sound.PlayToPlayer(self.Owner, eva.PrimaryBuildingSelected);
|
||||
Sound.PlayNotification(self.Owner, "Speech", "PrimaryBuildingSelected", self.Owner.Country.Race);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ namespace OpenRA.Mods.RA
|
||||
var mobileInfo = producee.Traits.GetOrDefault<MobileInfo>();
|
||||
|
||||
return mobileInfo == null ||
|
||||
mobileInfo.CanEnterCell(self.World, self.Owner, self.Location + s.ExitCellVector, self, true);
|
||||
mobileInfo.CanEnterCell(self.World, self, self.Location + s.ExitCellVector, self, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,24 +14,27 @@ namespace OpenRA.Mods.RA.Render
|
||||
{
|
||||
class RenderBuildingSiloInfo : RenderBuildingInfo
|
||||
{
|
||||
public readonly int FillSteps = 49;
|
||||
public override object Create(ActorInitializer init) { return new RenderBuildingSilo(init, this); }
|
||||
}
|
||||
|
||||
class RenderBuildingSilo : RenderBuilding, INotifyBuildComplete, INotifyCapture
|
||||
{
|
||||
PlayerResources playerResources;
|
||||
readonly RenderBuildingSiloInfo Info;
|
||||
|
||||
public RenderBuildingSilo( ActorInitializer init, RenderBuildingInfo info )
|
||||
public RenderBuildingSilo( ActorInitializer init, RenderBuildingSiloInfo info )
|
||||
: base(init, info)
|
||||
{
|
||||
playerResources = init.self.Owner.PlayerActor.Trait<PlayerResources>();
|
||||
Info = info;
|
||||
}
|
||||
|
||||
public void BuildingComplete( Actor self )
|
||||
{
|
||||
anim.PlayFetchIndex("idle",
|
||||
() => playerResources.OreCapacity != 0
|
||||
? (49 * playerResources.Ore) / (10 * playerResources.OreCapacity)
|
||||
? (Info.FillSteps * playerResources.Ore) / (10 * playerResources.OreCapacity)
|
||||
: 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace OpenRA.Mods.RA.Render
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
if (!self.Destroyed)
|
||||
w.Add(new Corpse(self, "die{0}".F(e.Warhead.InfDeath + 1)));
|
||||
w.Add(new Corpse(self, "die{0}".F(e.Warhead.InfDeath)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Scripting
|
||||
@@ -19,8 +20,19 @@ namespace OpenRA.Scripting
|
||||
{
|
||||
var playerRoot = Game.OpenWindow(w, "FMVPLAYER");
|
||||
var player = playerRoot.Get<VqaPlayerWidget>("PLAYER");
|
||||
|
||||
try
|
||||
{
|
||||
player.Load(movie);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
Ui.CloseWindow();
|
||||
onComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
w.EnableTick = false;
|
||||
player.Load(movie);
|
||||
|
||||
// Mute world sounds
|
||||
var oldModifier = Sound.SoundVolumeModifier;
|
||||
|
||||
@@ -20,15 +20,6 @@ namespace OpenRA.Scripting
|
||||
{
|
||||
public static void Chronoshift(World world, List<Pair<Actor, CPos>> units, Actor chronosphere, int duration, bool killCargo)
|
||||
{
|
||||
if (chronosphere != null)
|
||||
chronosphere.Trait<RenderBuilding>().PlayCustomAnim(chronosphere, "active");
|
||||
|
||||
// Trigger screen desaturate effect
|
||||
foreach (var a in world.ActorsWithTrait<ChronoshiftPaletteEffect>())
|
||||
a.Trait.Enable();
|
||||
|
||||
Sound.Play("chrono2.aud", units.First().First.CenterLocation);
|
||||
|
||||
foreach (var kv in units)
|
||||
{
|
||||
var target = kv.First;
|
||||
@@ -37,8 +28,6 @@ namespace OpenRA.Scripting
|
||||
if (cs.CanChronoshiftTo(target, targetCell, true))
|
||||
cs.Teleport(target, targetCell, duration, killCargo,chronosphere);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,14 +39,6 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public override void Activate(Actor self, Order order)
|
||||
{
|
||||
self.Trait<RenderBuilding>().PlayCustomAnim(self, "active");
|
||||
|
||||
// Trigger screen desaturate effect
|
||||
foreach (var a in self.World.ActorsWithTrait<ChronoshiftPaletteEffect>())
|
||||
a.Trait.Enable();
|
||||
|
||||
Sound.Play("chrono2.aud", order.TargetLocation.ToPPos());
|
||||
Sound.Play("chrono2.aud", order.ExtraLocation.ToPPos());
|
||||
foreach (var target in UnitsInRange(order.ExtraLocation))
|
||||
{
|
||||
var cs = target.Trait<Chronoshiftable>();
|
||||
|
||||
@@ -40,7 +40,9 @@ namespace OpenRA.Mods.RA
|
||||
self.Trait<RenderBuilding>().PlayCustomAnim(self, "active");
|
||||
|
||||
Sound.Play("ironcur9.aud", order.TargetLocation.ToPPos());
|
||||
foreach (var target in UnitsInRange(order.TargetLocation))
|
||||
|
||||
foreach (var target in UnitsInRange(order.TargetLocation)
|
||||
.Where(a => a.Owner.Stances[self.Owner] == Stance.Ally))
|
||||
target.Trait<IronCurtainable>().Activate(target, (Info as IronCurtainPowerInfo).Duration * 25);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
public float GetDamageModifier(Actor attacker, WarheadInfo warhead )
|
||||
{
|
||||
if( warhead != null && warhead.InfDeath == 5 )
|
||||
if( warhead != null && warhead.InfDeath == 6 )
|
||||
return 1000f;
|
||||
return 1f;
|
||||
}
|
||||
|
||||
@@ -42,4 +42,6 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
void OnNotifyResourceClaimLost(Actor self, ResourceClaim claim, Actor claimer);
|
||||
}
|
||||
|
||||
public interface INotifyParachuteLanded { void OnLanded(); }
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
[ActorReference] public readonly string IntoActor = null;
|
||||
public readonly int ForceHealthPercentage = 0;
|
||||
public readonly bool SkipMakeAnims = true;
|
||||
|
||||
public virtual object Create(ActorInitializer init) { return new TransformOnCapture(this); }
|
||||
}
|
||||
@@ -29,10 +30,12 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner)
|
||||
{
|
||||
self.QueueActivity(new Transform(self, Info.IntoActor) {
|
||||
ForceHealthPercentage = Info.ForceHealthPercentage,
|
||||
Facing = self.Trait<IFacing>().Facing
|
||||
});
|
||||
var facing = self.TraitOrDefault<IFacing>();
|
||||
var transform = new Transform(self, Info.IntoActor) { ForceHealthPercentage = Info.ForceHealthPercentage };
|
||||
if (facing != null) transform.Facing = facing.Facing;
|
||||
transform.SkipMakeAnims = Info.SkipMakeAnims;
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace OpenRA.Mods.RA
|
||||
[ActorReference] public readonly string OnEnter = null;
|
||||
[ActorReference] public readonly string OnExit = null;
|
||||
public readonly bool SkipMakeAnims = false;
|
||||
public readonly bool BecomeNeutral = false;
|
||||
|
||||
public object Create(ActorInitializer init) { return new TransformOnPassenger(this); }
|
||||
}
|
||||
@@ -43,6 +44,7 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(transform);
|
||||
if (info.BecomeNeutral) self.ChangeOwner(self.World.WorldActor.Owner);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,6 @@ namespace OpenRA.Mods.RA.Widgets
|
||||
List<Pair<Rectangle, Action<MouseInput>>> tabs = new List<Pair<Rectangle, Action<MouseInput>>>();
|
||||
Animation cantBuild;
|
||||
Animation clock;
|
||||
public readonly string BuildPaletteOpen = "bleep13.aud";
|
||||
public readonly string BuildPaletteClose = "bleep13.aud";
|
||||
public readonly string TabClick = "ramenu1.aud";
|
||||
|
||||
readonly WorldRenderer worldRenderer;
|
||||
readonly World world;
|
||||
@@ -118,11 +115,11 @@ namespace OpenRA.Mods.RA.Widgets
|
||||
|
||||
// Play palette-open sound at the start of the activate anim (open)
|
||||
if (paletteAnimationFrame == 1 && paletteOpen)
|
||||
Sound.Play(BuildPaletteOpen);
|
||||
Sound.PlayNotification(null, "Sounds", "BuildPaletteOpen", null);
|
||||
|
||||
// Play palette-close sound at the start of the activate anim (close)
|
||||
if (paletteAnimationFrame == paletteAnimationLength + -1 && !paletteOpen)
|
||||
Sound.Play(BuildPaletteClose);
|
||||
Sound.PlayNotification(null, "Sounds", "BuildPaletteClose", null);
|
||||
|
||||
// Animation is complete
|
||||
if ((paletteAnimationFrame == 0 && !paletteOpen)
|
||||
@@ -158,6 +155,17 @@ namespace OpenRA.Mods.RA.Widgets
|
||||
if (mi.Event != MouseInputEvent.Down)
|
||||
return false;
|
||||
|
||||
if (mi.Button == MouseButton.WheelDown)
|
||||
{
|
||||
TabChange(false);
|
||||
return true;
|
||||
}
|
||||
if (mi.Button == MouseButton.WheelUp)
|
||||
{
|
||||
TabChange(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
var action = tabs.Where(a => a.First.Contains(mi.Location))
|
||||
.Select(a => a.Second).FirstOrDefault();
|
||||
if (action == null && paletteOpen)
|
||||
@@ -291,7 +299,7 @@ namespace OpenRA.Mods.RA.Widgets
|
||||
Action<MouseInput> HandleClick(string name, World world)
|
||||
{
|
||||
return mi => {
|
||||
Sound.Play(TabClick);
|
||||
Sound.PlayNotification(null, "Sounds", "TabClick", null);
|
||||
|
||||
if (name != null)
|
||||
HandleBuildPalette(world, name, (mi.Button == MouseButton.Left));
|
||||
@@ -304,7 +312,7 @@ namespace OpenRA.Mods.RA.Widgets
|
||||
if (mi.Button != MouseButton.Left)
|
||||
return;
|
||||
|
||||
Sound.Play(TabClick);
|
||||
Sound.PlayNotification(null, "Sounds", "TabClick", null);
|
||||
var wasOpen = paletteOpen;
|
||||
paletteOpen = (CurrentQueue == queue && wasOpen) ? false : true;
|
||||
CurrentQueue = queue;
|
||||
@@ -366,15 +374,15 @@ namespace OpenRA.Mods.RA.Widgets
|
||||
{
|
||||
// instant cancel of things we havent really started yet, and things that are finished
|
||||
if (producing.Paused || producing.Done || producing.TotalCost == producing.RemainingCost)
|
||||
{
|
||||
Sound.Play(CurrentQueue.Info.CancelledAudio);
|
||||
{
|
||||
Sound.PlayNotification(world.LocalPlayer, "Speech", CurrentQueue.Info.CancelledAudio, world.LocalPlayer.Country.Race);
|
||||
int numberToCancel = Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1;
|
||||
|
||||
world.IssueOrder(Order.CancelProduction(CurrentQueue.self, item, numberToCancel));
|
||||
}
|
||||
else
|
||||
{
|
||||
Sound.Play(CurrentQueue.Info.OnHoldAudio);
|
||||
Sound.PlayNotification(world.LocalPlayer, "Speech", CurrentQueue.Info.OnHoldAudio, world.LocalPlayer.Country.Race);
|
||||
world.IssueOrder(Order.PauseProduction(CurrentQueue.self, item, true));
|
||||
}
|
||||
}
|
||||
@@ -383,7 +391,8 @@ namespace OpenRA.Mods.RA.Widgets
|
||||
|
||||
void StartProduction( World world, string item )
|
||||
{
|
||||
Sound.Play(CurrentQueue.Info.QueuedAudio);
|
||||
|
||||
Sound.PlayNotification(world.LocalPlayer, "Speech", CurrentQueue.Info.QueuedAudio, world.LocalPlayer.Country.Race);
|
||||
world.IssueOrder(Order.StartProduction(CurrentQueue.self, item,
|
||||
Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1));
|
||||
}
|
||||
@@ -496,7 +505,7 @@ namespace OpenRA.Mods.RA.Widgets
|
||||
|
||||
if ( toBuild != null )
|
||||
{
|
||||
Sound.Play(TabClick);
|
||||
Sound.PlayNotification(null, "Sounds", "TabClick", null);
|
||||
HandleBuildPalette(world, toBuild.Name, true);
|
||||
return true;
|
||||
}
|
||||
@@ -507,7 +516,7 @@ namespace OpenRA.Mods.RA.Widgets
|
||||
void TabChange(bool shift)
|
||||
{
|
||||
var queues = VisibleQueues.Concat(VisibleQueues);
|
||||
if (shift) queues.Reverse();
|
||||
if (shift) queues = queues.Reverse();
|
||||
var nextQueue = queues.SkipWhile( q => q != CurrentQueue )
|
||||
.ElementAtOrDefault(1);
|
||||
if (nextQueue != null)
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
};
|
||||
cheatsButton.IsVisible = () => world.LocalPlayer != null && world.LobbyInfo.GlobalSettings.AllowCheats;
|
||||
|
||||
optionsBG.Get<ButtonWidget>("DISCONNECT").OnClick = () => LeaveGame(optionsBG);
|
||||
optionsBG.Get<ButtonWidget>("DISCONNECT").OnClick = () => LeaveGame(optionsBG, world);
|
||||
|
||||
optionsBG.Get<ButtonWidget>("SETTINGS").OnClick = () => Ui.OpenWindow("SETTINGS_MENU");
|
||||
optionsBG.Get<ButtonWidget>("MUSIC").OnClick = () => Ui.OpenWindow("MUSIC_MENU");
|
||||
@@ -57,7 +57,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
var postGameObserve = postgameBG.Get<ButtonWidget>("POSTGAME_OBSERVE");
|
||||
|
||||
var postgameQuit = postgameBG.Get<ButtonWidget>("POSTGAME_QUIT");
|
||||
postgameQuit.OnClick = () => LeaveGame(postgameQuit);
|
||||
postgameQuit.OnClick = () => LeaveGame(postgameQuit, world);
|
||||
|
||||
postGameObserve.OnClick = () => postgameQuit.Visible = false;
|
||||
postGameObserve.IsVisible = () => world.LocalPlayer.WinState != WinState.Won;
|
||||
@@ -76,8 +76,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
};
|
||||
}
|
||||
|
||||
void LeaveGame(Widget pane)
|
||||
void LeaveGame(Widget pane, World world)
|
||||
{
|
||||
Sound.PlayNotification(null, "Speech", "Leave", world.LocalPlayer.Country.Race);
|
||||
pane.Visible = false;
|
||||
Game.Disconnect();
|
||||
Game.LoadShellMap();
|
||||
|
||||
@@ -237,7 +237,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
|
||||
chatPanel.AddChild(template);
|
||||
chatPanel.ScrollToBottom();
|
||||
Sound.Play("scold1.aud");
|
||||
Sound.PlayNotification(null, "Sounds", "ChatLine", null);
|
||||
}
|
||||
|
||||
void UpdateCurrentMap()
|
||||
|
||||
@@ -175,8 +175,7 @@ namespace OpenRA.Mods.RA.Widgets
|
||||
if (hasRadarNew != hasRadar)
|
||||
{
|
||||
radarAnimating = true;
|
||||
var eva = Rules.Info["world"].Traits.Get<EvaAlertsInfo>();
|
||||
Sound.Play(hasRadarNew ? eva.RadarUp : eva.RadarDown);
|
||||
Sound.PlayNotification(null, "Sounds", (hasRadarNew ? "RadarUp" : "RadarDown"), null);
|
||||
}
|
||||
|
||||
hasRadar = hasRadarNew;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
@@ -15,8 +16,12 @@ namespace OpenRA.TilesetBuilder
|
||||
public string InputMode;
|
||||
public Bitmap[] icon;
|
||||
public int TileSize;
|
||||
public int TilesPerRow;
|
||||
//private System.ComponentModel.IContainer components;
|
||||
|
||||
|
||||
public event Action<int, int, int> UpdateMouseTilePosition = (x, y, t) => { };
|
||||
|
||||
Template CurrentTemplate;
|
||||
|
||||
public bool ShowTerrainTypes
|
||||
@@ -67,19 +72,21 @@ namespace OpenRA.TilesetBuilder
|
||||
/* draw template outlines */
|
||||
foreach (var t in Templates)
|
||||
{
|
||||
System.Drawing.Pen pen = Pens.White;
|
||||
|
||||
foreach (var c in t.Cells.Keys)
|
||||
{
|
||||
if (CurrentTemplate == t)
|
||||
e.Graphics.FillRectangle(currentBrush, TileSize * c.X, TileSize * c.Y, TileSize, TileSize);
|
||||
|
||||
if (!t.Cells.ContainsKey(c + new int2(-1, 0)))
|
||||
e.Graphics.DrawLine(Pens.Red, (TileSize * c).ToPoint(), (TileSize * (c + new int2(0, 1))).ToPoint());
|
||||
e.Graphics.DrawLine(pen, (TileSize * c).ToPoint(), (TileSize * (c + new int2(0, 1))).ToPoint());
|
||||
if (!t.Cells.ContainsKey(c + new int2(+1, 0)))
|
||||
e.Graphics.DrawLine(Pens.Red, (TileSize * (c + new int2(1, 0))).ToPoint(), (TileSize * (c + new int2(1, 1))).ToPoint());
|
||||
e.Graphics.DrawLine(pen, (TileSize * (c + new int2(1, 0))).ToPoint(), (TileSize * (c + new int2(1, 1))).ToPoint());
|
||||
if (!t.Cells.ContainsKey(c + new int2(0, +1)))
|
||||
e.Graphics.DrawLine(Pens.Red, (TileSize * (c + new int2(0, 1))).ToPoint(), (TileSize * (c + new int2(1, 1))).ToPoint());
|
||||
e.Graphics.DrawLine(pen, (TileSize * (c + new int2(0, 1))).ToPoint(), (TileSize * (c + new int2(1, 1))).ToPoint());
|
||||
if (!t.Cells.ContainsKey(c + new int2(0, -1)))
|
||||
e.Graphics.DrawLine(Pens.Red, (TileSize * c).ToPoint(), (TileSize * (c + new int2(1, 0))).ToPoint());
|
||||
e.Graphics.DrawLine(pen, (TileSize * c).ToPoint(), (TileSize * (c + new int2(1, 0))).ToPoint());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,6 +135,8 @@ namespace OpenRA.TilesetBuilder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateMouseTilePosition(pos.X, pos.Y, (pos.Y * TilesPerRow) + pos.X);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
|
||||
23
OpenRA.TilesetBuilder/frmBuilder.Designer.cs
generated
23
OpenRA.TilesetBuilder/frmBuilder.Designer.cs
generated
@@ -52,6 +52,7 @@
|
||||
this.toolStripButton15 = new System.Windows.Forms.ToolStripButton();
|
||||
this.toolStripButton2 = new System.Windows.Forms.ToolStripButton();
|
||||
this.toolStripButton14 = new System.Windows.Forms.ToolStripButton();
|
||||
this.toolStripButton16 = new System.Windows.Forms.ToolStripButton();
|
||||
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.toolStripButton3 = new System.Windows.Forms.ToolStripButton();
|
||||
this.surface1 = new OpenRA.TilesetBuilder.Surface();
|
||||
@@ -227,6 +228,7 @@
|
||||
this.toolStripButton1.Name = "toolStripButton1";
|
||||
this.toolStripButton1.Size = new System.Drawing.Size(118, 20);
|
||||
this.toolStripButton1.Text = "Template Tool";
|
||||
this.toolStripButton1.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
this.toolStripButton1.Click += new System.EventHandler(this.TerrainTypeSelectorClicked);
|
||||
//
|
||||
// toolStrip1
|
||||
@@ -236,6 +238,7 @@
|
||||
this.toolStripButton15,
|
||||
this.toolStripButton2,
|
||||
this.toolStripButton14,
|
||||
this.toolStripButton16,
|
||||
this.toolStripSeparator1,
|
||||
this.toolStripButton3});
|
||||
this.toolStrip1.Location = new System.Drawing.Point(3, 0);
|
||||
@@ -250,6 +253,7 @@
|
||||
this.toolStripButton15.Name = "toolStripButton15";
|
||||
this.toolStripButton15.Size = new System.Drawing.Size(48, 22);
|
||||
this.toolStripButton15.Text = "New";
|
||||
this.toolStripButton15.ToolTipText = "Create new tileset";
|
||||
this.toolStripButton15.Click += new System.EventHandler(this.toolStripButton15_Click);
|
||||
//
|
||||
// toolStripButton2
|
||||
@@ -259,6 +263,7 @@
|
||||
this.toolStripButton2.Name = "toolStripButton2";
|
||||
this.toolStripButton2.Size = new System.Drawing.Size(51, 22);
|
||||
this.toolStripButton2.Text = "Save";
|
||||
this.toolStripButton2.ToolTipText = "Save Template definitions to file (*.tsx)";
|
||||
this.toolStripButton2.Click += new System.EventHandler(this.SaveClicked);
|
||||
//
|
||||
// toolStripButton14
|
||||
@@ -268,8 +273,19 @@
|
||||
this.toolStripButton14.Name = "toolStripButton14";
|
||||
this.toolStripButton14.Size = new System.Drawing.Size(59, 22);
|
||||
this.toolStripButton14.Text = "Export";
|
||||
this.toolStripButton14.ToolTipText = "Export defined Templates to files";
|
||||
this.toolStripButton14.Click += new System.EventHandler(this.ExportClicked);
|
||||
//
|
||||
// toolStripButton16
|
||||
//
|
||||
this.toolStripButton16.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton14.Image")));
|
||||
this.toolStripButton16.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||
this.toolStripButton16.Name = "toolStripButton16";
|
||||
this.toolStripButton16.Size = new System.Drawing.Size(65, 22);
|
||||
this.toolStripButton16.Text = "Dump";
|
||||
this.toolStripButton16.ToolTipText = "Dump Template ID to tile number mapping to console";
|
||||
this.toolStripButton16.Click += new System.EventHandler(this.Export2Clicked);
|
||||
//
|
||||
// toolStripSeparator1
|
||||
//
|
||||
this.toolStripSeparator1.Name = "toolStripSeparator1";
|
||||
@@ -282,6 +298,8 @@
|
||||
this.toolStripButton3.Name = "toolStripButton3";
|
||||
this.toolStripButton3.Size = new System.Drawing.Size(70, 22);
|
||||
this.toolStripButton3.Text = "Overlays";
|
||||
this.toolStripButton3.ToolTipText = "Show/hide terrain type overlays";
|
||||
this.toolStripButton3.CheckOnClick = true;
|
||||
this.toolStripButton3.Click += new System.EventHandler(this.ShowOverlaysClicked);
|
||||
//
|
||||
// surface1
|
||||
@@ -290,7 +308,7 @@
|
||||
this.surface1.ImagesList = this.imageList;
|
||||
this.surface1.Location = new System.Drawing.Point(0, 0);
|
||||
this.surface1.Name = "surface1";
|
||||
this.surface1.ShowTerrainTypes = true;
|
||||
this.surface1.ShowTerrainTypes = this.toolStripButton3.Checked;
|
||||
this.surface1.Size = new System.Drawing.Size(653, 454);
|
||||
this.surface1.TabIndex = 2;
|
||||
this.surface1.Text = "surface1";
|
||||
@@ -325,13 +343,14 @@
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
|
||||
private System.Windows.Forms.ToolStripButton toolStripButton3;
|
||||
private System.Windows.Forms.ToolStripButton toolStripButton14;
|
||||
private System.Windows.Forms.ToolStripButton toolStripButton15;
|
||||
private System.Windows.Forms.ToolStripButton toolStripButton16;
|
||||
public System.Windows.Forms.ImageList imageList;
|
||||
private System.Windows.Forms.ToolStrip tsTerrainTypes;
|
||||
private System.Windows.Forms.Panel panel1;
|
||||
private Surface surface1;
|
||||
private System.Windows.Forms.ToolStripLabel toolStripLabel2;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
|
||||
private System.Windows.Forms.ToolStripButton toolStripButton15;
|
||||
private System.Windows.Forms.ToolStripButton toolStripButton1;
|
||||
private System.Windows.Forms.ToolStripLabel toolStripLabel3;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
|
||||
|
||||
@@ -56,13 +56,16 @@ namespace OpenRA.TilesetBuilder
|
||||
Bitmap rbitmap = fbitmap.Clone(new Rectangle(0, 0, fbitmap.Width, fbitmap.Height),
|
||||
fbitmap.PixelFormat);
|
||||
|
||||
int[] ShadowIndex = { };
|
||||
|
||||
if (!PaletteFromImage)
|
||||
{
|
||||
tpal = Palette.Load(PaletteFile, false);
|
||||
tpal = Palette.Load(PaletteFile, ShadowIndex);
|
||||
rbitmap.Palette = tpal.AsSystemPalette();
|
||||
}
|
||||
|
||||
surface1.Image = (Bitmap)rbitmap;
|
||||
surface1.TilesPerRow = surface1.Image.Size.Width / surface1.TileSize;
|
||||
surface1.Image.SetResolution(96, 96); // people keep being noobs about DPI, and GDI+ cares.
|
||||
surface1.TerrainTypes = new int[surface1.Image.Width / size, surface1.Image.Height / size]; /* all passable by default */
|
||||
surface1.Templates = new List<Template>();
|
||||
@@ -84,6 +87,13 @@ namespace OpenRA.TilesetBuilder
|
||||
int i = 0;
|
||||
surface1.icon = new Bitmap[DefTerrain.Keys.Count];
|
||||
TerrainType = new TerrainTypeInfo[DefTerrain.Keys.Count];
|
||||
|
||||
var title = this.Text;
|
||||
surface1.UpdateMouseTilePosition += (x, y, tileNr) =>
|
||||
{
|
||||
this.Text = "{0} - {1} ({2,3}, {3,3}) tileNr: {4,3}".F(title, txtTilesetName.Text, x, y, tileNr);
|
||||
};
|
||||
|
||||
surface1.Enabled = false;
|
||||
foreach (var deftype in DefTerrain)
|
||||
{
|
||||
@@ -105,6 +115,7 @@ namespace OpenRA.TilesetBuilder
|
||||
TerrainTypeButton.ToolTipText = deftype.Key;
|
||||
TerrainTypeButton.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
|
||||
TerrainTypeButton.Tag = i.ToString();
|
||||
TerrainTypeButton.ImageAlign = ContentAlignment.MiddleLeft;
|
||||
i++;
|
||||
tsTerrainTypes.Items.Add(TerrainTypeButton);
|
||||
}
|
||||
@@ -115,6 +126,7 @@ namespace OpenRA.TilesetBuilder
|
||||
this.size = size;
|
||||
surface1.TileSize = size;
|
||||
surface1.Image = (Bitmap)Image.FromFile(src);
|
||||
surface1.TilesPerRow = surface1.Image.Size.Width / surface1.TileSize;
|
||||
surface1.Image.SetResolution(96, 96); // people keep being noobs about DPI, and GDI+ cares.
|
||||
surface1.TerrainTypes = new int[surface1.Image.Width / size, surface1.Image.Height / size]; /* all passable by default */
|
||||
surface1.Templates = new List<Template>();
|
||||
@@ -217,16 +229,8 @@ namespace OpenRA.TilesetBuilder
|
||||
void SaveClicked(object sender, EventArgs e) { Save(); }
|
||||
void ShowOverlaysClicked(object sender, EventArgs e)
|
||||
{
|
||||
if (surface1.ShowTerrainTypes == false)
|
||||
{
|
||||
surface1.ShowTerrainTypes = true;
|
||||
this.Refresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
surface1.ShowTerrainTypes = false;
|
||||
this.Refresh();
|
||||
}
|
||||
surface1.ShowTerrainTypes = (sender as ToolStripButton).Checked;
|
||||
surface1.Invalidate();
|
||||
}
|
||||
|
||||
void ExportClicked(object sender, EventArgs e)
|
||||
@@ -234,6 +238,11 @@ namespace OpenRA.TilesetBuilder
|
||||
Export("Tilesets");
|
||||
}
|
||||
|
||||
void Export2Clicked(object sender, EventArgs e)
|
||||
{
|
||||
ExportTemplateToTileNumberMapping();
|
||||
}
|
||||
|
||||
string ExportPalette(List<Color> p, string file)
|
||||
{
|
||||
while (p.Count < 256) p.Add(Color.Black); // pad the palette out with extra blacks
|
||||
@@ -399,7 +408,8 @@ namespace OpenRA.TilesetBuilder
|
||||
{
|
||||
string ttype = "Clear";
|
||||
ttype = TerrainType[surface1.TerrainTypes[t.Key.X, t.Key.Y]].Type;
|
||||
template.Tiles.Add((byte)((t.Key.X - tp.Left) + tp.Width * (t.Key.Y - tp.Top)), ttype);
|
||||
var idx = ((t.Key.X - tp.Left) + tp.Width * (t.Key.Y - tp.Top));
|
||||
template.Tiles.Add((byte)idx, ttype);
|
||||
}
|
||||
|
||||
tileset.Templates.Add(cur, template);
|
||||
@@ -415,8 +425,38 @@ namespace OpenRA.TilesetBuilder
|
||||
//File.Delete(file);
|
||||
|
||||
Console.WriteLine("Finished export");
|
||||
}
|
||||
|
||||
public void ExportTemplateToTileNumberMapping()
|
||||
{
|
||||
Console.WriteLine("# start");
|
||||
Console.WriteLine("# TemplateID CellID tilenr TemplateW TemplateH XinTilesPNG YinTilesPNG");
|
||||
|
||||
ushort cur = 0;
|
||||
foreach (var tp in surface1.Templates)
|
||||
{
|
||||
foreach (var t in tp.Cells)
|
||||
{
|
||||
var idx = ((t.Key.X - tp.Left) + tp.Width * (t.Key.Y - tp.Top));
|
||||
|
||||
// TemplateID CellID tilenr TemplateW TemplateH XinTilesPNG YinTilesPNG
|
||||
Console.WriteLine("{0} {1} {2} {3} {4} {5} {6}",
|
||||
cur,
|
||||
idx,
|
||||
((t.Key.Y * surface1.TilesPerRow) + t.Key.X).ToString(),
|
||||
tp.Width,
|
||||
tp.Height,
|
||||
t.Key.X,
|
||||
t.Key.Y
|
||||
);
|
||||
}
|
||||
|
||||
cur++;
|
||||
}
|
||||
|
||||
Console.WriteLine("# end\n");
|
||||
}
|
||||
|
||||
private void toolStripContainer1_TopToolStripPanel_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
@@ -450,6 +490,5 @@ namespace OpenRA.TilesetBuilder
|
||||
{
|
||||
CreateNewTileset();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -81,8 +81,15 @@ namespace OpenRA.Utility
|
||||
var dest = Path.ChangeExtension(src, ".png");
|
||||
|
||||
var srcImage = ShpReader.Load(src);
|
||||
var shouldRemap = args.Contains( "--transparent" );
|
||||
var palette = Palette.Load(args[2], shouldRemap);
|
||||
int[] ShadowIndex = { };
|
||||
if (args.Contains("--noshadow"))
|
||||
{
|
||||
Array.Resize(ref ShadowIndex, ShadowIndex.Length + 3);
|
||||
ShadowIndex[ShadowIndex.Length - 1] = 1;
|
||||
ShadowIndex[ShadowIndex.Length - 2] = 3;
|
||||
ShadowIndex[ShadowIndex.Length - 1] = 4;
|
||||
}
|
||||
var palette = Palette.Load(args[2], ShadowIndex);
|
||||
|
||||
using (var bitmap = new Bitmap(srcImage.ImageCount * srcImage.Width, srcImage.Height, PixelFormat.Format8bppIndexed))
|
||||
{
|
||||
@@ -110,8 +117,13 @@ namespace OpenRA.Utility
|
||||
public static void ConvertR8ToPng(string[] args)
|
||||
{
|
||||
var srcImage = new R8Reader(File.OpenRead(args[1]));
|
||||
var shouldRemap = args.Contains("--transparent");
|
||||
var palette = Palette.Load(args[2], shouldRemap);
|
||||
int[] ShadowIndex = { };
|
||||
if (args.Contains("--noshadow"))
|
||||
{
|
||||
Array.Resize(ref ShadowIndex, ShadowIndex.Length + 1);
|
||||
ShadowIndex[ShadowIndex.Length - 1] = 3;
|
||||
}
|
||||
var palette = Palette.Load(args[2], ShadowIndex);
|
||||
var startFrame = int.Parse(args[3]);
|
||||
var endFrame = int.Parse(args[4]) + 1;
|
||||
var filename = args[5];
|
||||
@@ -319,6 +331,7 @@ namespace OpenRA.Utility
|
||||
var mods = args[1].Split(',');
|
||||
var theater = args[2];
|
||||
var templateNames = args.Skip(3);
|
||||
int[] ShadowIndex = { 3, 4 };
|
||||
|
||||
var manifest = new Manifest(mods);
|
||||
FileSystem.LoadFromManifest(manifest);
|
||||
@@ -330,7 +343,7 @@ namespace OpenRA.Utility
|
||||
throw new InvalidOperationException("No theater named '{0}'".F(theater));
|
||||
|
||||
tileset.LoadTiles();
|
||||
var palette = new Palette(FileSystem.Open(tileset.Palette), true);
|
||||
var palette = new Palette(FileSystem.Open(tileset.Palette), ShadowIndex);
|
||||
|
||||
foreach( var templateName in templateNames )
|
||||
{
|
||||
@@ -413,14 +426,15 @@ namespace OpenRA.Utility
|
||||
var destPaletteInfo = Rules.Info["player"].Traits.Get<PlayerColorPaletteInfo>();
|
||||
int[] destRemapIndex = destPaletteInfo.RemapIndex;
|
||||
|
||||
int[] ShadowIndex = { };
|
||||
// the remap range is always 16 entries, but their location and order changes
|
||||
for (var i = 0; i < 16; i++)
|
||||
remap[PlayerColorRemap.GetRemapIndex(srcRemapIndex, i)]
|
||||
= PlayerColorRemap.GetRemapIndex(destRemapIndex, i);
|
||||
|
||||
// map everything else to the best match based on channel-wise distance
|
||||
var srcPalette = Palette.Load(args[1].Split(':')[1], false);
|
||||
var destPalette = Palette.Load(args[2].Split(':')[1], false);
|
||||
var srcPalette = Palette.Load(args[1].Split(':')[1], ShadowIndex);
|
||||
var destPalette = Palette.Load(args[2].Split(':')[1], ShadowIndex);
|
||||
|
||||
var fullIndexRange = Exts.MakeArray<int>(256, x => x);
|
||||
|
||||
|
||||
@@ -57,11 +57,11 @@ namespace OpenRA.Utility
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(" --settings-value KEY Get value of KEY from settings.yaml");
|
||||
Console.WriteLine(" --shp PNGFILE FRAMEWIDTH Convert a single PNG with multiple frames appended after another to a SHP");
|
||||
Console.WriteLine(" --png SHPFILE PALETTE [--transparent] Convert a SHP to a PNG containing all of its frames, optionally setting up transparency");
|
||||
Console.WriteLine(" --png SHPFILE PALETTE [--noshadow] Convert a SHP to a PNG containing all of its frames, optionally removing the shadow");
|
||||
Console.WriteLine(" --extract MOD[,MOD]* FILES Extract files from mod packages");
|
||||
Console.WriteLine(" --tmp-png MOD[,MOD]* THEATER FILES Extract terrain tiles to PNG");
|
||||
Console.WriteLine(" --remap SRCMOD:PAL DESTMOD:PAL SRCSHP DESTSHP Remap SHPs to another palette");
|
||||
Console.WriteLine(" --r8 R8FILE PALETTE START END FILENAME [--transparent] [--infrantry] [--vehicle] [--projectile] [--building] [--wall] [--tileset] Convert Dune 2000 DATA.R8 to PNGs choosing start- and endframe as well as type for correct offset to append multiple frames to one PNG named by filename optionally setting up transparency.");
|
||||
Console.WriteLine(" --r8 R8FILE PALETTE START END FILENAME [--noshadow] [--infrantry] [--vehicle] [--projectile] [--building] [--wall] [--tileset] Convert Dune 2000 DATA.R8 to PNGs choosing start- and endframe as well as type for correct offset to append multiple frames to one PNG named by filename optionally removing the shadow.");
|
||||
Console.WriteLine(" --transpose SRCSHP DESTSHP START N M [START N M ...] Transpose the N*M block of frames starting at START.");
|
||||
}
|
||||
|
||||
|
||||
BIN
artsrc/d2k/OrthodoxHerbertarian.ttf
Normal file
BIN
artsrc/d2k/OrthodoxHerbertarian.ttf
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user