Compare commits

...

117 Commits

Author SHA1 Message Date
Chris Forbes
e3d71acb05 fix crash in CrateEffect 2010-10-09 13:40:57 +13:00
Chris Forbes
580f1cfe97 bot and humans hate each other, even if the host is on a team. 2010-10-09 13:40:55 +13:00
Andrew Riedi
3d7434f42e Fixed under VS 2010. 2010-10-09 13:40:52 +13:00
Chris Forbes
ef96604f9e #231 scroll jumping fixed 2010-10-09 13:40:49 +13:00
Chris Forbes
465f5d295b force master server url update, if old. 2010-10-09 13:40:47 +13:00
Chris Forbes
1feb377d43 fix bogus target lines being drawn to 0,0 2010-10-09 13:40:33 +13:00
Caleb Anderson
a065fb464e bandage to fix dogs not killing infantry in certain cases 2010-10-09 13:40:21 +13:00
Chris Forbes
e39917ca19 trim bbox for ftrk 2010-10-08 19:09:18 +13:00
Chris Forbes
318f496bf9 #202 fixed 2010-10-08 19:03:01 +13:00
Chris Forbes
ed8a155249 if c17 gets confused, just fly away 2010-10-08 18:57:14 +13:00
Chris Forbes
40ac5f7b6e FallsToEarth for cnc helis too 2010-10-08 18:45:52 +13:00
Chris Forbes
517754027e 8Inch and SubMissile minimum ranges temporarily disabled, until combat bugs are fixed 2010-10-08 18:33:38 +13:00
Caleb Anderson
ef4f478e10 Strip newlines from scrolling text. Frame-friendly update of scrolling text 2010-10-08 18:29:20 +13:00
Chris Forbes
92c30b89f8 FACT hp 1000 -> 1500 2010-10-08 18:26:25 +13:00
Chris Forbes
7331a9c4fb prevent aircraft shooting while landed 2010-10-08 18:25:26 +13:00
Chris Forbes
d2b9e150f1 add FallsToEarth for helicopters dying 2010-10-08 18:24:08 +13:00
Chris Forbes
8ba329f1be reduce cost of CA 3200 -> 2400 2010-10-08 18:03:34 +13:00
Chris Forbes
8868eab247 nerf hind chaingun from [x2] 40@3 to 20@10 2010-10-08 18:03:13 +13:00
Caleb Anderson
f12889e684 Sensible scroll cursor positions for cnc. RA seems to be totally different, didn't touch atm 2010-10-08 17:39:00 +13:00
Caleb Anderson
5c095bc174 more politic default motd text for cnc 2010-10-08 17:38:55 +13:00
Caleb Anderson
0c24a08436 sane default scroll speed 2010-10-08 17:38:48 +13:00
Chris Forbes
9762b540b0 fix bogus reverse-enter-transport (in ResolveOrder this time) 2010-10-08 17:38:07 +13:00
Paul Chote
fd34f2ba99 Fix #225 and some other uses of a.IsInWorld / a.IsDead() 2010-10-08 10:56:50 +13:00
Paul Chote
ad6481c8e8 Change master server url. Won't work until dns is set correctly. 2010-10-07 22:46:44 +13:00
Paul Chote
7426d47cd5 Fix some compile warnings 2010-10-07 22:46:44 +13:00
pdovy
761f62292f missing files from previous commit 2010-10-07 22:46:44 +13:00
unknown
63b8555bc9 Fix bug that prevented ground units from attacking landed aircraft. 2010-10-07 22:46:44 +13:00
Bob
3209da4a4a fixed PlaceBuilding and Chronosphere ordergenerators 2010-10-07 22:07:13 +13:00
Bob
aebef4f1c8 rename IIssueOrder2 -> IIssueOrder 2010-10-07 22:07:13 +13:00
Bob
d3244184c1 implement order targeter for everything else 2010-10-07 22:07:13 +13:00
Bob
39e62354a8 implement order targeter for passenger 2010-10-07 22:07:12 +13:00
Bob
f525c3808e implement order targeter for cargo 2010-10-07 22:07:12 +13:00
Bob
87a0b52ce5 more order targeters 2010-10-07 22:07:12 +13:00
Bob
4bc9e01516 use new orders system in various traits 2010-10-07 22:07:12 +13:00
Bob
711d05da98 use IIssueOrder2 in AttackBase 2010-10-07 22:07:12 +13:00
Bob
3d805ff40d added IIssueOrder2. most orders are broken, but Minelayer is fixed 2010-10-07 22:07:12 +13:00
Bob
0cd140849b fix some support powers 2010-10-07 22:07:12 +13:00
Bob
d6110b9ef0 add Sync.AssertUnsynced. use it in OrderGenerator.set 2010-10-07 22:07:12 +13:00
Bob
26d1db778e push the check-synchash-doesn't-change pattern into a utility fn. furthur reduce the number of uses on Game.world 2010-10-07 22:07:11 +13:00
Bob
f41aa474aa remove more uses of Game.world 2010-10-07 22:07:11 +13:00
Paul Chote
0002e80a19 Use new deps package, built on mono 2.8. Enable new sgen garbage collector. 2010-10-07 22:05:43 +13:00
Chris Forbes
759a52d86e fix build failure from bad merge in prev 2010-10-07 18:24:40 +13:00
max621
44fe0396bb Added shift+right click on build menu cancels 5 orders. Added ctrl+shift+right click on build menu cancels all orders 2010-10-07 18:23:26 +13:00
rasco
dd6d8d916e hackyAI 2010-10-07 07:42:40 +13:00
Chris Forbes
5af8f5e2d9 bots choose random colors 2010-10-07 07:41:14 +13:00
Caleb Anderson
c85503811c Clamp, scroll, scrollspeed, sliders
Reduced clamp duplication
Fixed scrolling speed issue
Modified scrollspeed slider to use a range
Fixed scrollspeed, volume, and sound sliders not showing current setting.
2010-10-06 20:53:56 +13:00
Chris Forbes
ab431fe9ee fix crash on warhead=null 2010-10-06 20:17:03 +13:00
Chris Forbes
7ec9958d47 hackyai should only attack humans 2010-10-06 20:04:36 +13:00
rasco
cfc74a6dee HackyAI: builds defense now. rally points are rechosen so units are more scattered in the base. builds only e1-3 and 1-3tnks. 2010-10-06 19:14:29 +13:00
Chris Forbes
cecdd73e08 destroy ore in all smudged cells 2010-10-06 18:37:32 +13:00
Chris Forbes
381e080b11 tweak nuke timing back 2010-10-06 18:32:33 +13:00
Chris Forbes
c6a047cb1a fix silent nukes 2010-10-06 18:29:05 +13:00
Chris Forbes
4c51733e04 actually add the cratenuke warhead 2010-10-06 18:25:07 +13:00
Chris Forbes
8f613b80e8 ban cloak crate on infantry 2010-10-06 18:16:09 +13:00
Chris Forbes
10de282daa add mechanism to exclude particular actor types from picking up any crate 2010-10-06 18:13:29 +13:00
Chris Forbes
915e123956 add minimum ranges for mig/yak/ca/msub/hind/v2rl/arty 2010-10-06 18:05:31 +13:00
Chris Forbes
8cb7a7b8ce add support for WeaponInfo.MinRange 2010-10-06 17:41:52 +13:00
Chris Forbes
ce5cf93077 prevent infantry going prone due to tib damage 2010-10-06 17:37:23 +13:00
Chris Forbes
1390bb7428 ra: reduce parabombs charge to 1min 2010-10-06 17:32:04 +13:00
Chris Forbes
3c5b136216 ra: reduce nuke charge time 13min -> 9 min 2010-10-06 17:29:45 +13:00
Chris Forbes
66785e606a ra: brik hp 1500 => 1000 2010-10-06 17:23:12 +13:00
Chris Forbes
e2239fa50c #215 obli misses moving units fixed 2010-10-06 17:22:11 +13:00
Chris Forbes
7f7712aa64 #203 dogs eating walls fixed 2010-10-06 17:20:20 +13:00
Chris Forbes
b1605e115a #206 badr/badr.bomber appear in tooltips 2010-10-06 17:15:52 +13:00
Chris Forbes
69b2da86be RA: +20% 4tnk HP 2010-10-06 17:13:39 +13:00
Chris Forbes
18e965e5aa fix desync on destroying ore storage buildings 2010-10-06 10:59:46 +13:00
Chris Forbes
24f0c28f56 fix massive player/client confusion after people drop 2010-10-06 10:59:45 +13:00
Paul Chote
25af51b4ac Prevent a race condition 2010-10-05 19:00:36 +13:00
Paul Chote
ecd7064cc3 Add an example mod that adds a soviet supply truck 2010-10-05 18:48:00 +13:00
Chris Forbes
477a21e782 fix 'minv/minp' showing up in tooltips 2010-10-05 17:58:30 +13:00
Chris Forbes
899d9af62b CNC: +25% HP on FACT/MCV 2010-10-05 17:58:29 +13:00
Paul Chote
17eca983ef Fix wall sell exploit 2010-10-05 17:56:30 +13:00
max621
2fc219ecd5 Fixed auto attack not working properly due to code expecting 'Idle' activity but most units use 'IdleAnimation' in RA mod 2010-10-05 17:45:29 +13:00
Chris Forbes
49a645cd2d fix ubuntu-64 support (Tao.FreeType was quite bogus.) 2010-10-05 17:42:13 +13:00
Matthew
7cb8da411d Updated git url to point to new upstream location. 2010-10-05 17:42:13 +13:00
Matthew
4bcdf3cf11 Netcode discussion from Etherpad. 2010-10-05 17:36:03 +13:00
Matthew
1b525ff809 Export FTP server and added username/password to VERSION file upload. 2010-10-05 17:36:03 +13:00
Caleb Anderson
cdec3fce26 Don't tell all players about my yaks 2010-10-05 17:25:30 +13:00
Caleb Anderson
7bdf6a953f New slider Range parameter. Palette modifications. Potential crash fix. Clamp function.
Range parameter added to slider. Supports returning a range of values
rather than just 0-1. Allows you to not have to post process the offset.
Modified palette selector to not have full range, which was causing
blown out units.
Introduced exension method Clamp<T>(min, max)
Fixed crash deserializing out of bound color value using above
extension.
2010-10-05 17:25:25 +13:00
Chris Forbes
06b20c8ba5 fix blatant wrongness in parallel builds 2010-10-02 20:55:43 +13:00
Paul Chote
9b484b53ec Show mod info in server browser 2010-10-02 20:37:25 +13:00
Paul Chote
9620b4ed46 Add mod metadata, and filter valid mods on startup. 2010-10-02 20:37:22 +13:00
Caleb Anderson
d8908c44d0 Nicer default ticker text 2010-10-02 01:13:55 -05:00
Caleb Anderson
cfe705531a Async motd grab. Client and server version in MasterServerQuery 2010-10-02 01:13:53 -05:00
Caleb Anderson
9a2fd38ab6 MOTD ticker. ScrollingText Widget 2010-10-02 01:13:51 -05:00
Chris Forbes
911e7f62de fix retardedly putting everyone in slot 0. 2010-10-02 18:33:29 +13:00
Chris Forbes
403b81bdc9 apply the balance stick to BIKE 2010-10-02 15:49:42 +13:00
Chris Forbes
a0714b00b3 a bit of cleanup 2010-10-02 15:49:42 +13:00
Chris Forbes
d5239ee77a add tib and spawns to new map 2010-10-02 15:49:41 +13:00
Chris Forbes
03ec97f302 unfinished 2v2 cnc map 2010-10-02 15:49:41 +13:00
Chris Forbes
3255f95ee9 ffs, more paths 2010-10-02 15:35:48 +13:00
Chris Forbes
4c8fd5e73d oops 2010-10-02 15:30:16 +13:00
Chris Forbes
7179ef0f45 fix packaging script 2010-10-02 15:19:14 +13:00
Alli
6d5918b11d Remove compiler warnings 2010-10-02 14:30:54 +13:00
Matthew Bowra-Dean
81c484d1c9 Fixed debian package prereqs 2010-10-02 13:28:22 +13:00
Chris Forbes
de1044c24c hack around imagelist deserialization being completely busted across platforms 2010-10-02 13:22:54 +13:00
Paul Chote
e4c31939d9 Save all settings to settings.yaml, not just non-defaults. 2010-10-02 12:15:15 +13:00
Paul Chote
7f48d6796e Fix scroll ticks 2010-10-02 12:01:11 +13:00
Matthew Bowra-Dean
4fd77aec8e Only add 'Latest:' text if query succeeds. 2010-10-02 11:39:56 +13:00
Matthew Bowra-Dean
92fece01de Added latest version information underneath current version in main menu. 2010-10-02 11:39:54 +13:00
geckosoft
d8d987f844 Fixed Settings menu issue (overlapping text) 2010-10-02 11:38:42 +13:00
unknown
de429a4c62 Added new setting : Scroll Speed (added to cnc & ra) 2010-10-02 11:38:39 +13:00
Chris Forbes
c0ca35a4ff fix AI jam in cnc 2010-10-02 11:33:50 +13:00
Chris Forbes
1bff8559fb pull HasAdequatePower out into a function 2010-10-02 11:33:47 +13:00
Chris Forbes
de98274165 fix #184 crashes while placing minefield 2010-10-02 11:33:04 +13:00
Chris Forbes
59e2228b2a shader fallback path for cg-2.1, which misinterprets 'latest' 2010-10-02 11:32:14 +13:00
geckosoft
96d1408d45 Fixed crash bug #197 2010-10-02 11:31:55 +13:00
Paul Chote
ff45ae2d16 Fix direct connect in cnc 2010-10-02 11:31:19 +13:00
Chris Forbes
47950c9113 Revert "remove setters on Mobile.{from,to}Cell. use SetLocation instead"
This reverts commit 911db3feb1.
2010-09-28 07:45:14 +13:00
Chris Forbes
f402ec7898 Revert "add IHasLocation"
This reverts commit 699b4b1154.
2010-09-28 07:43:49 +13:00
Chris Forbes
a3c0448e15 fix broken mac packaging script 2010-09-27 21:44:23 +13:00
Bob
699b4b1154 add IHasLocation 2010-09-26 18:17:23 +12:00
Bob
911db3feb1 remove setters on Mobile.{from,to}Cell. use SetLocation instead 2010-09-26 16:46:22 +12:00
Paul Chote
c790db8e84 Fix and bulletproof osx packaging script; cleanup some obsolete .gitignore entries 2010-09-26 10:18:22 +13:00
Bob
d6dd392028 use Invariant culture for float parsing (workaround a mono bug) 2010-09-26 09:11:26 +12:00
Bob
636b2a8ea7 fix Direct Connect window 2010-09-25 19:26:02 +12:00
Chris Forbes
959d3f8bd7 fix crash in giving a harv DeliverOre order after its proc has died, but it hasnt noticed yet 2010-09-25 07:34:51 +12:00
161 changed files with 2995 additions and 1500 deletions

11
.gitignore vendored
View File

@@ -12,20 +12,11 @@ mods/*/*.dll
# Red Alert binary files
mods/*/packages/*.[mM][iI][xX]
# Crap generated by OpenRa
sheet-*.png
log.txt
*.rep
#binary stuff
/*.dll
*.pdb
*.mdb
*.exe
OpenRA
OpenRA.app
*.vqa
# backup files by various editors
*~
@@ -33,10 +24,8 @@ OpenRA.app
# dependency DLLs (different for every platform!)
cg.dll
cgGL.dll
glfw.dll
/OpenRa.Gl.dll
settings.ini
#monodevelop
*.pidb

View File

@@ -6,4 +6,5 @@ The OpenRA developers are:
* Matthew Bowra-Dean
* Paul Chote
* Alli Witheford
* Joakim Lindberg
* Joakim Lindberg
* Andrew Riedi

View File

@@ -17,6 +17,7 @@ using OpenRA;
using OpenRA.FileFormats;
using OpenRA.Traits;
using System.Drawing;
using System.Globalization;
namespace OpenRA.Editor
{
@@ -401,7 +402,7 @@ namespace OpenRA.Editor
{
new LocationInit(new int2(loc % MapSize, loc / MapSize)),
new OwnerInit(parts[0]),
new HealthInit(float.Parse(parts[2])/256),
new HealthInit(float.Parse(parts[2], NumberFormatInfo.InvariantInfo)/256),
new FacingInit((section == "INFANTRY") ? int.Parse(parts[6]) : int.Parse(parts[4])),
new ActorStanceInit(stance),
};

View File

@@ -52,11 +52,13 @@
this.txtTitle = new System.Windows.Forms.TextBox();
this.lblMapName = new System.Windows.Forms.Label();
this.lblMinimap = new System.Windows.Forms.Label();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)(this.pbMinimap)).BeginInit();
this.pnlBottom.SuspendLayout();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// MapList
@@ -85,9 +87,9 @@
//
// MapIconsList
//
this.MapIconsList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("MapIconsList.ImageStream")));
this.MapIconsList.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
this.MapIconsList.ImageSize = new System.Drawing.Size(24, 24);
this.MapIconsList.TransparentColor = System.Drawing.Color.Transparent;
this.MapIconsList.Images.SetKeyName(0, "mapicon");
//
// btnCancel
//
@@ -141,6 +143,7 @@
//
// pnlBottom
//
this.pnlBottom.Controls.Add(this.pictureBox1);
this.pnlBottom.Controls.Add(this.txtPathOut);
this.pnlBottom.Controls.Add(this.lblPathOut);
this.pnlBottom.Controls.Add(this.lblPath);
@@ -301,6 +304,16 @@
this.lblMinimap.TabIndex = 6;
this.lblMinimap.Text = "Map preview:";
//
// pictureBox1
//
this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
this.pictureBox1.Location = new System.Drawing.Point(336, -9);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(54, 35);
this.pictureBox1.TabIndex = 7;
this.pictureBox1.TabStop = false;
this.pictureBox1.Visible = false;
//
// MapSelect
//
this.AcceptButton = this.btnOk;
@@ -327,6 +340,7 @@
this.splitContainer1.Panel2.ResumeLayout(false);
this.splitContainer1.Panel2.PerformLayout();
this.splitContainer1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
}
@@ -356,5 +370,6 @@
public System.Windows.Forms.Label lblPathOut;
public System.Windows.Forms.Label lblPath;
public System.Windows.Forms.TextBox txtPathOut;
private System.Windows.Forms.PictureBox pictureBox1;
}
}

View File

@@ -1,13 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using OpenRA.FileFormats;
using System.Windows.Forms;
using System.IO;
using System.Windows.Forms;
using OpenRA.FileFormats;
using OpenRA.Graphics;
namespace OpenRA.Editor
@@ -19,11 +13,11 @@ namespace OpenRA.Editor
public MapSelect()
{
InitializeComponent();
MapIconsList.Images.Add(pictureBox1.Image);
}
private void MapSelect_Load(object sender, EventArgs e)
void MapSelect_Load(object sender, EventArgs e)
{
DirectoryInfo directory = new DirectoryInfo(MapFolderPath);
DirectoryInfo[] directories = directory.GetDirectories();
MapList.Items.Clear();
@@ -33,20 +27,14 @@ namespace OpenRA.Editor
ListViewItem map1 = new ListViewItem(subDirectory.Name);
map1.ImageIndex = 0;
MapList.Items.Add(map1);
}
if (txtNew.Text == "unnamed")
{
//dumb indian code
}
else
{
// hack
if (txtNew.Text != "unnamed")
MapList.Items[0].Selected = true;
}
}
private void MapList_SelectedIndexChanged(object sender, EventArgs e)
void MapList_SelectedIndexChanged(object sender, EventArgs e)
{
if (MapList.SelectedItems.Count == 1)
{
@@ -69,7 +57,7 @@ namespace OpenRA.Editor
}
}
private void txtPathOut_TextChanged(object sender, EventArgs e)
void txtPathOut_TextChanged(object sender, EventArgs e)
{
MapFolderPath = txtPathOut.Text;
}

View File

@@ -120,73 +120,58 @@
<metadata name="MapIconsList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<data name="MapIconsList.ImageStream" mimetype="application/x-microsoft.net.object.binary.base64">
<assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABW
DgAAAk1TRnQBSQFMAwEBAAEoAQABKAEAARgBAAEYAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABYAMA
ARgDAAEBAQABIAYAASQhAAEBAx8BLAM/AW8DSQGJAzYBWQMYASIDAQECDAADHQEqA0gBhQNCAXYDDwEU
AwEBAgMNARIDMwFSA0MBdwNRAacDDwEU/wAwAAEBAwMBBAM0BFQBwgFlAVMBUgH0AV4CWAHYA0oBjQMW
AR4DAgEDAwABAQMCAQMDGgElA00BlAEIAQkBJgH9A1ABpAM1AVcDQQFzA1MBugJDAVMB4wJTAVQBsgMA
AQH/ADAAAQEDDAEQA0IBdAFVAVQBUQHYAZkBiAGBAf8BoAGMAYUB/wF8AXABZQH4AzoBYQMKAQ4DCgEN
Ax0BKgM/AW0DVAG7ARkCGgH5AQABBQG/Af8BFwEbAUcB+QE5AUYBSwHrAQABBQFEAf8CCAFOAf0DDQER
/wAxAAMDAQQDHQEpA1ABpAFdAVMBUQHtAZsBiwGDAf8BnAGMAYQB/wGWAYQBdwH/AVsBWgFYAdgDMwFT
AzQBVANPAZ0CUwFSAdMCTAFOAfMBMAGeAbsB/wEUAXMBqwH/ARQBdQG/Af8BEAFmAbYB/wEXAY0BwgH/
AUgBYQFpAeEDAQEC/wAwAAEBAwkBDAMwAUwBVgJVAcMBewFqAWAB+gGoAZkBjwH/AZMBhAF3Af8BjAF3
AXAB/wGHAXEBbAH/A1YBvgNTAbwBTAFIAUYB5QF2AVsBUgH6AYMBbQFlAf8BUQFIAUcB/wEOAUsBpAH/
AQMBGgG4Af8BDQFRAZUB/wEZAZUBwAH/AUABagGBAesDMwFSAw0BEgMCAQP/ACUAAwEBAgMYASEDSwGP
A1EB3AGbAY4BhQH9AaEBkQGJAf8BhwFzAWsB/wGBAW4BZQH/AXUBZwFgAf8BUwFKAUMB/QFHAT4BOwH8
AZYBhgF5Af8BowGSAYoB/wFTAUoBUwH/AQMBGAHzAf8BEgFgAaYB/wEVAXYBlQH/AS8B/QH+Af8BBwEy
AZQB/wEVAXoByAH/AgABVAH/A00BlAMZASP/ACUAAwYBCAMtAUYDUwG8AWoBZQFiAfQBqgGbAZEB/wGJ
AXQBbQH/AYwBdwFuAf8BmgGJAYEB/wGLAXcBbwH/AXUBZQFeAf8BhgFxAWkB/wGVAYYBegH/AUEBPAFK
Af8BEwEfAYgB/wEKAREBrgH/ARwBegGVAf8BKgHfAeIB/wEOAVMBdwH/AQkBOQG8Af8BGQGBAaoB/wEK
ARIBqQH/ASABJQFSAfcDUQGg/wAlAAMQARUDQQFyA08B1gGTAYQBcgH9AZoBiQGDAf8BjwGAAXMB/wGW
AYYBeAH/AZ8BjQGEAf8BlgGFAXkB/wGUAYQBdwH/AZQBgwF3Af8BlwGHAYAB/wFqAV8BXAH/AWMBVwFW
Af8BYAFVAVMB/wFTAVcBWAH/ASIBYwGaAf8BDQFLAZ0B/wEgAY4BsAH/AUwBnAGvAf8BaAGaAa4B/wE7
AUsBWgH+A1ABn/8AJQADGAEiA00BlQF8AXUBbwH8AagBmAGOAf8BkQGBAXUB/wGNAXcBcAH/AZoBiQGB
Af8BkwGDAXYB/wGZAYgBgQH/AZwBiwGDAf8BlQGGAXgB/wGWAYYBeQH/AaABjwGHAf8BlgGFAXgB/wF2
AW8BbAH/AUwBbQGBAf8BHgE7AV0B/wEdAZ4BhAH/AVMBlwGpAf8BhgHFAd8B/wGIAb4B1wH/AUUBXwFi
AfsDQQFz/wAlAAMYASIDTQGVAZ0BjwGGAf8BmwGLAYMB/wGGAXEBagH/AYUBbwFoAf8BiAFzAWwB/wGJ
AXQBbAH/AZABdwFvAf8BlQGEAXcB/wGWAYUBeAH/AZ8BjwGGAf8BmQGLAYQB/wFtAYABhgH/AVYBkwGw
Af8BWgGmAccB/wFMAYMBpAH/AQUBFgGgAf8BYwGaAbAB/wGCAb8B2QH/AXEBrQHIAf8BXQFpAXIB3gMq
AUD/ACEAAwMBBAMmATgDUAGjAZoBjAGEAf8BpwGWAY4B/wGXAYYBegH/AZQBggF2Af8BlAGDAXcB/wGW
AYUBeAH/AZIBeQFyAf8BjwF5AXIB/wGVAYQBeAH/AY8BdgFrAf8BcAGAAYcB/wFaAZcBswH/AU8BmgHC
Af8BZQG1AdQB/wFlAaQBvgH/AR4BKgE1Af8BeAG6AdMB/wGAAbsB1QH/AYEBvwHaAf8BVQFWAVkBtQMP
ART/ACEAAwoBDQNBAXMDUgHPAasBmgGRAf8BmgGJAYIB/wGOAXgBcQH/AZEBgQF1Af8BlQGEAXcB/wGY
AYgBgAH/AYsBdQFuAf8BkQF6AXMB/wF4AW4BagH/AW8BrwHIAf8BWQGoAc0B/wFZAasBzAH/AVUBpgHJ
Af8BdQHDAeAB/wF6AcIB3wH/AYMBwAHbAf8BggHBAd0B/wFvAa4ByAH/AYsBvwHbAf8DSAGGAwUBB/8A
IQADHgErA1EBoAFSAVABTQHtAYMBbwFoAf8BgwFuAWcB/wGBAWwBZQH/AYEBbQFmAf8BiAFzAWsB/wGH
AXIBawH/AZQBhAF4Af8BhQF2AXMB/wFUAWwBgwH/AU0BhAGmAf8BZgGwAdAB/wFdAaoBywH/AWEBsQHR
Af8BdgG/Ad0B/wGFAcgB4wH/AYEBxAHfAf8BiAHHAeIB/wF3AbYB0QH/AW0BngG2Af8DPgFrAwIBA/8A
IQADOAFcA1UByAFrAlwB+AGRAYABdAH/AZgBiAGAAf8BlQGEAXgB/wGNAXcBcAH/AZUBhAF4Af8BmAGH
AYAB/wGTAYMBdwH/AWgBgAGLAf8BVgGQAbEB/wE3AUoBagH/AWMBqQHKAf8BYAGuAc8B/wFhAawBzAH/
AXYBvwHcAf8BiQHMAecB/wGDAcYB4gH/AYUBxQHgAf8BhwHDAd4B/wFIAYMBmwH/AzwBaAMEAQX/ACEA
A0kBiQFXAVYBVQHiAaQBkwGKAf8BrAGcAZMB/wGlAZUBjAH/AZYBhgF5Af8BkAF5AXQB/wGZAYgBgQH/
AaIBkQGJAf8BbgFdAVUB/wFkAZUBqwH/AWgBtQHVAf8BcgHBAd0B/wF0AbsB2wH/AV4BrAHOAf8BXwGt
AcwB/wF4AcMB4AH/AYgBywHmAf8BhQHGAeQB/wGDAcYB4gH/AYcBvwHdAf8BQwFVAWkB7gMbASb/ACUA
A08BpQFiAlgB7wGjAZMBigH/AZgBhwGAAf8BnwGOAYYB/wGdAYwBhAH/AZABeQFzAf8BiAFyAWsB/wGI
AXYBbwH/AWoBhQGPAf8BaAGwAdAB/wFvAbwB2gH/AWYBtgHWAf8BXwGxAdAB/wFdAa8B0AH/AXEBvQHa
Af8BcAG7AdoB/wGAAcQB3wH/AY8BzQHpAf8BigHJAeUB/wFbAZcBrAH9A0QBeQMAAQH/ACUAA1MBsQFs
Al8B8wGjAZIBigH/AZUBgwF4Af8BlAGCAXcB/wGUAYUBdwH/AYQBiwGNAf8BWwFzAYYB/wFcAYcBnQH/
AWEBrwHSAf8BbwG7AdoB/wF3AcEB3gH/AWwBuQHXAf8BYQGzAdIB/wFpAbkB1gH/AYABxQHhAf8BeAHC
Ad8B/wGAAcUB4QH/AYUBxAHhAf8BiQHGAeIB/wFSAXMBhAHzAzEBTv8AKQADUQHHAXsBcQFnAfgBqwGb
AZIB/wGQAYABdQH/AYQBbwFmAf8BcgGaAasB/wFiAasBzwH/AWQBsgHTAf8BbQG8AdoB/wFrAbgB1QH/
AXEBvgHbAf8BdgG/Ad0B/wFwAb4B2wH/AWgBuQHWAf8BgAHHAeEB/wGJAcsB5AH/AYoBzAHnAf8BjQHR
AewB/wGFAckB5gH/AW0BswHUAf8DTgGYAzABTAMAAQEDAAEB/wAhAAM+AWwBYgFfAV4ByQGJAYIBfAH1
AaQBkwGLAf8BhgGeAakB/wFnAa8B0gH/AXgBxAHgAf8BdgHCAd4B/wF2AcIB3gH/AXEBvwHbAf8BcwG8
AdoB/wFyAbsB2AH/AXQBuwHZAf8BdAG/Ad0B/wFhAZ0BwwH/AUEBYwGUAf8BbQGnAcgB/wGHAcQB4AH/
AYgBzQHpAf8BXwFxAXkB4AMsAUQDBwEJAwQBBgMAAQH/ACEAAwQBBQMZASMDNAFUA0oBiQFYAlsBwQFh
AXIBeQHiAW4BlwGoAfUBfQG0Ac8B/gGAAcUB4gH/AXIBvAHaAf8BcQG8AdoB/wFwAbsB2AH/AXUBvAHZ
Af8BjgHPAesB/wFtAa8B0gH/AU8BdQGkAf8BbAGrAc0B/wFuAa8BzwH/AY8BygHnAf8BWAFjAWYB0QMi
ATH/AD0AAwEBAgMJAQwDGgElA0IBdgFSAVwBZQHqAYEBxAHgAf4BigHHAeQB/wF5AcAB3QH/AYoBxgHi
Af8BlgHUAe4B/wF5AbgB1wH/AWEBoQHIAf8BaQGmAcwB/wFZAZIBvwH/AX8BwwHdAf4CRgFHAYEDAwEE
/wBJAAMCAQMDEAEWAzIBUANKAY0DUAGdA1IBoQFmAXUBewHgAXIBrwHFAf0BYAGXAb4B/wFgAZQBwAH/
AW8BqAHOAf8BQAF5AaoB/wM1AVYDAAEB/wBVAAMCAQMDBgEIAwoBDQMoAT0DQAFxAU8CUQGcAVsBYgFo
AdYBZQF/AZMB8gFKAX0BoQH8AyABL/8AcAABAQMIAQsDJAE2A0YBgQMDAQT/ADEAAUIBTQE+BwABPgMA
ASgDAAFgAwABGAMAAQEBAAEBBQABIAEBFgAD/wEAAeABOAEBCQABwAEAAQEJAAHAAQABAwkAAcABAAED
CQABgAsAAYALAAGACwABgAsAAYALAAGAPQABAQsAAQELAAEDIwABBwkAAfABAAEHCQAB/gEAAQcJAAH/
AcABDwkAAf8B/gEPCQAL
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAArjSURBVFhHrVcJUFRXFmVpkE0Wg0ZEMoIBl8RodGSMmVES
R0NiChVxCyIIAmFfhe6maeh9AZodZHFpUFQWcQmKisiuLKLEKFFEcIuljtZMYmI5Uc/c14EZYyapmpq8
qlP/9/+/373v3HPu+19P7/cf+jSlEWHCOEuzZfNn2Yvo3Pj3D/PTjCwYAwtgyeFwFk163Tos2tvtWJZw
zeUjmYFP6vL9fhw31vRPI8/9LnmMrtCWZhtPcJ7hbC/W8DwfnS0LR0t5NKoFazHcqsaprE3YGbMQLrYW
W/7fBAxogjGEd5wcXuPK4jxa60qD75VrfIbKMrYMD7VlPX/Ul49rp8RoyPbFIaEnhpuU6NRGoVa2HKuX
zEj/X8vAVmlIsDY2Nna2sjBd+ee5b7Yq4tfgfm8a7pxNw60OFa6e4uFmhxr/uFSIexfz0VsZh7PaIJzb
F42u8hAcV2/ANz3KF8Hub52kucb+Fv+jAccbm5quWOfxYX5y6Mcnq/IDh1oreX/vOcx9drdbjTudUgye
Tsb1piT0H4/DACUw0JiEh33ZePhlFi7UxOCLtI1o2x6IW2cUqM/egLtdediV9MnjSTYm60bE+Ys8rMzN
zd1c3/vLgVRV3lN1XjkqyzNoNaEUjIvrLXwMtQsw1JqEy/UxuHg0DJcp+KXj8bh0IobOucSGDA8vpqE6
+VNINixBU0kQbp5JJ8jxDSXdeyAE5arlAxTZ/BfRra2tS8Tqwh9L9tQhq6gSabm7odLkoP8oF90V/rjR
LsRgExc32oS40ZFCiQgw2Mgj+gW42pBI4OPSsUh6Jgn1mX4ojXJH865IXDzCx50uDe6fV+P8oQQ4Tx6r
pOATmVteKgdjXu/Mhk1h328rO4TMwn3gp2YhmquEX7gAfYfjcb4qGH0HQ3D5WAiunU7ApfpofH0ymhIS
UwkSqQRb6VoYvm6IZvXG7W4VBVbjwQU1/tanIQZkGDqtwOmSjzHXxXaLsZnZpy85QpdAnfunax/mllRD
ma2FQJyHxORMJAqzwBVpUJgtRLF8E7bxlqKjzA991QEYaIijgFwMt6VggJK60hCFr46F4sqprbjbLaf6
S0icIh1rwy1i3OvNQff+GEgj3r87/c1JLIGfjX1LP/F6KJDlIy1vN4SyQohVRdAU7IVIXQxuSiZ8/OOw
JYyP8EiqfV04Lh2JoHIwMSbiwoFgtO1ah97aTSTMKGIjDl/WhONWl5jKlYrbZ9OJGRWVIg/+axf8SK5a
8WoC5fPm//l2qqIIOaVVECm3QZG5C7L0HZCklYJLTCQKNUiWFYAnykJYRDRaywNxrTGRyhKNKyciiIEY
dFd743z1ZioT6eJkHK41kXDbRFSKbHzTpcJwsxoHc71fTLGzyBix+L/zUFlaWX+VTcFZ4NzSamQXV1Lw
EggkeeBRAsnSfKgyy6DKKcNnxEahIgRfHg5B/7EoDDYn4ma7FD0VAThfG4Cemi3EDI90IKWVq3GbHHKz
nUpCbrh6QokEr5mPTEw4773MQij9aPZY5fNYrC6hEhRAnrkTjJEkcS5ieWokS/IhUZdCrtlJbGQiIjQQ
/YeD0VMVhoTVbmgs8iVBctG11xtdlUHkDGpQpIN7velUAhmG2+W43spclI6K9JWY6TyxlGKOGxWjG53s
5HBMBpZ7+iI6UY44vhoiVTH4ohxCLoTyArqu1OlBll6CZHE20mI9EOjuis3L5uFCbTh6D4Wgu8offYeC
qSRccgcPD/qyMEiBb55VYahFgSuNckhjlr2YaGMqH2ntOiKmEAQGBgbnNviGPg2LFSM4UoiAUD78ghLo
yEVojBhRiQqEx0nAI5sKiBH/z+OgVfuic7c/Lh+NwZndfughDfQdCtL1hKFmEW51ymn1EjpSAh1ytFCL
Pl+nhKebY8XLTYn16DWEgzY2ttdzyI6yjB06+uP4acRANokvhzRRjBR5oe4aN4WupeYgNCIWZ2vj0b17
I/UKf3Tt34jOis9otTyyILmkORUDTRKyqxj9DSI07Y0k26Yhes38Roo3YVQH7OVhGiGR0K7O0epEqCRB
shKwRIQkQrZqMelAIM6ncihII9uQSCXZEi2HSMjDXvVqXKSGdTLbg6z6OVkwGcfLQvHB4jkYbJMS/UL0
1aeSM6So1Hg/oljTR93AupEVYSXhGE+soZa8X+cIlgALzFhg5/HJ6YhIzACXVs9EKiHRMmckSfMQGx0G
rXIldU4/dJSvJx3EoSDZC7OszHByTxTOfZGEq00y9B3lo70iElamhp4Ujy1eN8wIrEOd0BRW6DpiWEwq
giKSdYiIl5IOUskRKh39AmKECZNP7Mg0O6Ci59Pzd4O/NQK7FBvQULyGOmMUpBsX4KqjGTL5qzHQIqJy
iNG6Px5dtYlY7jqhmeKxvUE32C7FGKiP58uf5W6v0ekgPkmNrYIMor4YqcoinQBFdGQrl2ZsB4/cwBJR
Zml/+k0sxcQnQZzgDWHIIni9NQHSHU5IS15EPSEN11ok6G9UQs1dh/lvv/GdkZHR3NEETOhkIUE7zvb1
wRRFwYv0vD3UBTORJCEdSHMhydgFCXVHVnuWEGvZrDewjqmzKbHDNjHmFl5KBlasWokiFwtop5nhyL6p
2JO/CN2HBdSgMpHJ88GH82c8s7b4T1vmUPA3CFxCp19QzBPdtpxdRqon2kkDKRSYXeOJcyBUFMI7IJ70
kYsEgRLTXGbgrzNnwmWqC2Y4OsLZZjx43El4ggXI1zohy24MAFd4u9tj8YLpmD71DTiMtwGHY1A7ygB7
52N29CLULV7qeT+HnFCoPairu87/RC9rTjE8JYJoY/p4lR8lVQAp7Rc+/pHY7OKE61NN8a21MbQpfwC+
m4fbQ7MQNdcCfWMMsTPWDi2yyRgyNkDhOGPYGhpeo97jP5oAcwJjYQ4hk3DOyzvwn9v3HtUJjHXAzSFc
RHHlCAznIyAsCZa2k56u3xT2gtHPmGHCdXtnHh47mKBqjS1K5Q7ITrCjlf8RqXPM0bDUCr3bp2C9nj70
DDinKMYSwmujCbAjY4G9ZrN3tyMLP/D4IT1vP/J21CBeoKaa55MTUhAeK8J7i91v0DMdxqZmgw7Oc74N
iFQ8T5YX6ZzisWI9Uiebo8rKCOcsjLAv1Z72BydUxE5ElcTuKf2P9RunEeGzmD8bTIzsI0I7wc7xdgyf
NiayWVoueZ1qz7zPT8l5McnBqZieYZsYO/a4zF7yaKWP4HlASMKLj9xX4WrXWzhdMxUFlkaI1tPDAZE9
Htc4wWuhOas5Y/oXgUezYDdZdmK2N6zzCfuB1V1GFkulNswT5cFllms73f+QMIWwjCDjGJnUW1rbfu2z
wvXJk85pUH5gieNJdugvd8QV7RSc0TjgiMwevkvH3np1xa/+ZplZE9jecNh14UcPmO8VmnLyfzGr/feU
mB/dY7VjybItdSZhrb6+fvEYY6PuMWOMn7ZbGeILfQNYmnN+mO1o/OBdCw74HAMsMjR8NjL/r+Yx+p3H
XhjKZs93uxMZr4Z/uOb54qWf3bGf4pw3EpC1UPYsS3g0EU9K4uBsI86zYHPOY3MOp4zuRRLyDfT1O941
Mrzvb86Bob4h+0r6zcEmdiFICA2vvT55kLFBYOJhnYt9or06TOmCJ7HTbGxgADpndmaJMU29QxAQKvTo
vhnHkN1nX12/OlgCNoT3CZsIvgRWc9ao2IT/bTD3+BID7N1/zisBWCKOBB/CagKf8PboJP8CoUmu3yhA
ga8AAAAASUVORK5CYII=
</value>
</data>
</root>

View File

@@ -439,10 +439,6 @@ namespace OpenRA.Editor
float OffsetY = t.Centered ? t.Bitmap.Height / 2 - TileSet.TileSize / 2 : 0;
float DrawY = TileSet.TileSize * p.Y * Zoom + Offset.Y - OffsetY;
float width = t.Bitmap.Width * Zoom;
float height = t.Bitmap.Height * Zoom;
RectangleF sourceRect = new RectangleF(0, 0, t.Bitmap.Width, t.Bitmap.Height);
RectangleF destRect = new RectangleF(DrawX, DrawY, width, height);
g.DrawRectangle(CordonPen,
DrawX, DrawY,
t.Bitmap.Width * Zoom, t.Bitmap.Height * Zoom);

10
OpenRA.FileFormats/Exts.cs Normal file → Executable file
View File

@@ -85,5 +85,15 @@ namespace OpenRA
{
return (T[])mi.GetCustomAttributes( typeof( T ), true );
}
public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
{
if (val.CompareTo(min) < 0)
return min;
else if (val.CompareTo(max) > 0)
return max;
else
return val;
}
}
}

12
OpenRA.FileFormats/FieldLoader.cs Normal file → Executable file
View File

@@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Globalization;
namespace OpenRA.FileFormats
{
@@ -99,7 +100,7 @@ namespace OpenRA.FileFormats
else if (fieldType == typeof(float))
{
float res;
if (float.TryParse(x.Replace("%",""), out res))
if (float.TryParse(x.Replace("%",""), System.Globalization.NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
return res * (x.Contains( '%' ) ? 0.01f : 1f);
return InvalidValueAction(x,fieldType, field);
}
@@ -111,9 +112,9 @@ namespace OpenRA.FileFormats
{
var parts = x.Split(',');
if (parts.Length == 3)
return Color.FromArgb(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]));
return Color.FromArgb(int.Parse(parts[0]).Clamp(0, 255), int.Parse(parts[1]).Clamp(0, 255), int.Parse(parts[2]).Clamp(0, 255));
if (parts.Length == 4)
return Color.FromArgb(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]));
return Color.FromArgb(int.Parse(parts[0]).Clamp(0, 255), int.Parse(parts[1]).Clamp(0, 255), int.Parse(parts[2]).Clamp(0, 255), int.Parse(parts[3]).Clamp(0, 255));
return InvalidValueAction(x,fieldType, field);
}
@@ -263,7 +264,10 @@ namespace OpenRA.FileFormats
if (f.FieldType == typeof(Color))
{
var c = (Color)v;
return "{0},{1},{2},{3}".F(c.A,c.R,c.G,c.B);
return "{0},{1},{2},{3}".F(((int)c.A).Clamp(0, 255),
((int)c.R).Clamp(0, 255),
((int)c.G).Clamp(0, 255),
((int)c.B).Clamp(0, 255));
}
return f.FieldType.IsArray

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}</ProjectGuid>
<OutputType>Library</OutputType>

View File

@@ -110,23 +110,6 @@ namespace OpenRA
return mods.Aggregate(sprites, (m, p) => p.ModifyRender(this, m));
}
public Order Order( int2 xy, MouseInput mi, Actor underCursor )
{
if (Owner != World.LocalPlayer)
return null;
if (!World.Map.IsInMap(xy.X, xy.Y))
return null;
if (Destroyed)
return null;
return TraitsImplementing<IIssueOrder>()
.OrderByDescending( x => x.OrderPriority( this, xy, mi, underCursor ) )
.Select( x => x.IssueOrder( this, xy, mi, underCursor ) )
.FirstOrDefault( x => x != null );
}
public RectangleF GetBounds(bool useAltitude)
{
var si = Info.Traits.GetOrDefault<SelectableInfo>();

20
OpenRA.Game/Exts.cs Normal file → Executable file
View File

@@ -34,17 +34,17 @@ namespace OpenRA
return xs.Aggregate(1f, (a, x) => a * x);
}
public static V GetOrAdd<K, V>( this Dictionary<K, V> d, K k )
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k)
where V : new()
{
return d.GetOrAdd( k, _ => new V() );
return d.GetOrAdd(k, _ => new V());
}
public static V GetOrAdd<K, V>( this Dictionary<K, V> d, K k, Func<K, V> createFn )
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, Func<K, V> createFn)
{
V ret;
if( !d.TryGetValue( k, out ret ) )
d.Add( k, ret = createFn( k ) );
if (!d.TryGetValue(k, out ret))
d.Add(k, ret = createFn(k));
return ret;
}
@@ -54,18 +54,18 @@ namespace OpenRA
return xs[r.Next(xs.Length)];
}
public static void DoTimed<T>( this IEnumerable<T> e, Action<T> a, string text, double time )
public static void DoTimed<T>(this IEnumerable<T> e, Action<T> a, string text, double time)
{
var sw = new Stopwatch();
e.Do( x =>
e.Do(x =>
{
var t = sw.ElapsedTime();
a( x );
a(x);
var dt = sw.ElapsedTime() - t;
if( dt > time )
if (dt > time)
Log.Write("perf", text, x, dt * 1000, Game.LocalTick);
} );
});
}
}
}

View File

@@ -47,7 +47,6 @@ namespace OpenRA
var map = modData.PrepareMap(uid);
viewport = new Viewport(new float2(Renderer.Resolution), map.TopLeft, map.BottomRight, Renderer);
world = null; // trying to access the old world will NRE, rather than silently doing it wrong.
world = new World(modData.Manifest, map);
}
@@ -101,7 +100,7 @@ namespace OpenRA
static ConnectionState lastConnectionState = ConnectionState.PreConnecting;
public static int LocalClientId { get { return orderManager.Connection.LocalClientId; } }
static void Tick()
static void Tick( World world, OrderManager orderManager, Viewport viewPort )
{
if (orderManager.Connection.ConnectionState != lastConnectionState)
{
@@ -159,7 +158,7 @@ namespace OpenRA
internal static event Action LobbyInfoChanged = () => { };
internal static void SyncLobbyInfo(string data)
internal static void SyncLobbyInfo( World world, string data)
{
LobbyInfo = Session.Deserialize(data);
@@ -179,13 +178,14 @@ namespace OpenRA
LobbyInfoChanged();
}
public static void IssueOrder(Order o) { orderManager.IssueOrder(o); } /* avoid exposing the OM to mod code */
public static void IssueOrder( Order o ) { orderManager.IssueOrder( o ); } /* avoid exposing the OM to mod code */
public static event Action AfterGameStart = () => {};
public static event Action BeforeGameStart = () => {};
internal static void StartGame(string map)
{
world = null;
BeforeGameStart();
LoadMap(map);
if (orderManager.GameStarted) return;
@@ -200,23 +200,20 @@ namespace OpenRA
public static void DispatchMouseInput(MouseInputEvent ev, MouseEventArgs e, Modifiers modifierKeys)
{
if (world == null)
return;
int sync = world.SyncHash();
var initialWorld = world;
var world = Game.world;
if (world == null) return;
var mi = new MouseInput
Sync.CheckSyncUnchanged( world, () =>
{
Button = (MouseButton)(int)e.Button,
Event = ev,
Location = new int2(e.Location),
Modifiers = modifierKeys,
};
Widget.HandleInput(world, mi);
if (sync != world.SyncHash() && world == initialWorld)
throw new InvalidOperationException("Desync in DispatchMouseInput");
var mi = new MouseInput
{
Button = (MouseButton)(int)e.Button,
Event = ev,
Location = new int2( e.Location ),
Modifiers = modifierKeys,
};
Widget.HandleInput( world, mi );
} );
}
public static bool IsHost
@@ -231,16 +228,13 @@ namespace OpenRA
public static void HandleKeyEvent(KeyInput e)
{
if (world == null)
return;
int sync = world.SyncHash();
if (Widget.HandleKeyPress(e))
return;
var world = Game.world;
if( world == null ) return;
if (sync != Game.world.SyncHash())
throw new InvalidOperationException("Desync in OnKeyPress");
Sync.CheckSyncUnchanged( world, () =>
{
Widget.HandleKeyPress( e );
} );
}
static Modifiers modifiers;
@@ -257,6 +251,12 @@ namespace OpenRA
SupportDir = args.GetValue("SupportDir", defaultSupport);
Settings = new Settings(SupportDir + "settings.yaml", args);
// force master server upgrade -- remove once everyone is switched over.
if (Settings.Server.MasterServer == "http://open-ra.org/master/")
Settings.Server.MasterServer = "http://master.open-ra.org/";
Settings.Save();
Log.LogPath = SupportDir + "Logs" + Path.DirectorySeparatorChar;
Log.AddChannel("perf", "perf.log");
Log.AddChannel("debug", "debug.log");
@@ -267,7 +267,14 @@ namespace OpenRA
Renderer.SheetSize = Settings.Game.SheetSize;
Renderer = new Renderer();
LobbyInfo.GlobalSettings.Mods = Settings.Game.Mods;
Console.WriteLine("Available mods:");
foreach(var mod in ModData.AllMods)
Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Title, mod.Value.Version);
// Discard any invalid mods
LobbyInfo.GlobalSettings.Mods = Settings.Game.Mods.Where(m => ModData.AllMods.ContainsKey(m)).ToArray();
Console.WriteLine("Loading mods: {0}",string.Join(",",LobbyInfo.GlobalSettings.Mods));
modData = new ModData( LobbyInfo.GlobalSettings.Mods );
Sound.Initialize();
@@ -317,7 +324,7 @@ namespace OpenRA
internal static void Run()
{
while (!quit)
{
{
Tick( world, orderManager, viewport );
Application.DoEvents();
}

View File

@@ -24,7 +24,7 @@ namespace OpenRA.GameRules
public int ListenPort = 1234;
public int ExternalPort = 1234;
public bool AdvertiseOnline = true;
public string MasterServer = "http://open-ra.org/master/";
public string MasterServer = "http://master.open-ra.org/";
public bool AllowCheats = false;
}
@@ -69,6 +69,7 @@ namespace OpenRA.GameRules
// Behaviour settings
public bool ViewportEdgeScroll = true;
public bool InverseDragScroll = false;
public float ViewportEdgeScrollStep = 10f;
// Internal game settings
public int Timestep = 40;
@@ -106,12 +107,12 @@ namespace OpenRA.GameRules
FieldLoader.UnknownFieldAction = (s,f) =>
{
System.Console.WriteLine( "Ignoring unknown field `{0}` on `{1}`".F( s, f.Name ) );
Console.WriteLine( "Ignoring unknown field `{0}` on `{1}`".F( s, f.Name ) );
};
if (File.Exists(SettingsFile))
{
System.Console.WriteLine("Loading settings file {0}",SettingsFile);
Console.WriteLine("Loading settings file {0}",SettingsFile);
var yaml = MiniYaml.DictFromFile(SettingsFile);
foreach (var kv in Sections)
@@ -123,7 +124,7 @@ namespace OpenRA.GameRules
foreach (var kv in Sections)
foreach (var f in kv.Value.GetType().GetFields())
if (args.Contains(kv.Key+"."+f.Name))
OpenRA.FileFormats.FieldLoader.LoadField( kv.Value, f.Name, args.GetValue(kv.Key+"."+f.Name, "") );
FieldLoader.LoadField( kv.Value, f.Name, args.GetValue(kv.Key+"."+f.Name, "") );
FieldLoader.UnknownFieldAction = err1;
FieldLoader.InvalidValueAction = err2;
@@ -133,16 +134,11 @@ namespace OpenRA.GameRules
{
var root = new List<MiniYamlNode>();
foreach( var kv in Sections )
root.Add( new MiniYamlNode( kv.Key, SectionYaml( kv.Value ) ) );
root.Add( new MiniYamlNode( kv.Key, FieldSaver.Save(kv.Value) ) );
root.WriteToFile(SettingsFile);
}
MiniYaml SectionYaml(object section)
{
return FieldSaver.SaveDifferences(section, Activator.CreateInstance(section.GetType()));
}
void LoadSectionYaml(MiniYaml yaml, object section)
{
object defaults = Activator.CreateInstance(section.GetType());

View File

@@ -31,7 +31,7 @@ namespace OpenRA.GameRules
: new Dictionary<string, string[]>();
}
public readonly Lazy<Dictionary<string, VoicePool>> Pools;
public readonly OpenRA.FileFormats.Lazy<Dictionary<string, VoicePool>> Pools;
public VoiceInfo( MiniYaml y )
{

View File

@@ -32,6 +32,7 @@ namespace OpenRA.GameRules
[FieldLoader.Load] public readonly int Damage = 0; // how much (raw) damage to deal
[FieldLoader.Load] public readonly int Delay = 0; // delay in ticks before dealing the damage. 0=instant (old model)
[FieldLoader.Load] public readonly DamageModel DamageModel = DamageModel.Normal; // which damage model to use
[FieldLoader.Load] public readonly bool PreventProne = false; // whether we should prevent prone response in infantry.
public float EffectivenessAgainst(Actor self)
{
@@ -92,6 +93,7 @@ namespace OpenRA.GameRules
[FieldLoader.Load] public readonly bool Underwater = false;
[FieldLoader.Load] public readonly string[] ValidTargets = { "Ground" };
[FieldLoader.Load] public readonly int BurstDelay = 5;
[FieldLoader.Load] public readonly float MinRange = 0;
[FieldLoader.LoadUsing( "LoadProjectile" )] public IProjectileInfo Projectile;
[FieldLoader.LoadUsing( "LoadWarheads" )] public List<WarheadInfo> Warheads;

View File

@@ -80,17 +80,20 @@ namespace OpenRA.Graphics
GlyphInfo CreateGlyph(Pair<char,Color> c)
{
var index = FT.FT_Get_Char_Index(face, (uint)c.First);
FT.FT_Load_Glyph(face, index, FT.FT_LOAD_RENDER);
if (0 != FT.FT_Load_Glyph(face, index, FT.FT_LOAD_RENDER))
throw new InvalidOperationException( "FT_Load_Glyph failed." );
var _face = (FT_FaceRec)Marshal.PtrToStructure(face, typeof(FT_FaceRec));
var _glyph = (FT_GlyphSlotRec)Marshal.PtrToStructure(_face.glyph, typeof(FT_GlyphSlotRec));
var s = builder.Allocate(new Size(_glyph.metrics.width >> 6, _glyph.metrics.height >> 6));
var s = builder.Allocate(
new Size(_glyph.metrics.width.ToInt32() >> 6,
_glyph.metrics.height.ToInt32() >> 6));
var g = new GlyphInfo
{
Sprite = s,
Advance = _glyph.metrics.horiAdvance / 64f,
Advance = _glyph.metrics.horiAdvance.ToInt32() / 64f,
Offset = { X = _glyph.bitmap_left, Y = -_glyph.bitmap_top }
};

View File

@@ -97,8 +97,8 @@ namespace OpenRA.Graphics
if (world.OrderGenerator != null)
world.OrderGenerator.RenderBeforeWorld(world);
foreach (var image in worldSprites)
image.Sprite.DrawAt(image.Pos, image.Palette);
foreach( var image in worldSprites )
image.Sprite.DrawAt( image.Pos, this.GetPaletteIndex( image.Palette ) );
uiOverlay.Draw(world);
if (world.OrderGenerator != null)

View File

@@ -19,6 +19,35 @@ namespace OpenRA
{
public class ModData
{
public static readonly Dictionary<string,Mod> AllMods = ValidateMods(Directory.GetDirectories("mods").Select(x => x.Substring(5)).ToArray());
public static Dictionary<string,Mod> ValidateMods(string[] mods)
{
var ret = new Dictionary<string,Mod>();
foreach (var m in mods)
{
if (!File.Exists("mods" + Path.DirectorySeparatorChar + m + Path.DirectorySeparatorChar + "mod.yaml"))
continue;
var yaml = new MiniYaml( null, MiniYaml.FromFile("mods" + Path.DirectorySeparatorChar + m + Path.DirectorySeparatorChar + "mod.yaml"));
if (!yaml.NodesDict.ContainsKey("Metadata"))
{
System.Console.WriteLine("Invalid mod: "+m);
continue;
}
ret.Add(m,FieldLoader.Load<Mod>(yaml.NodesDict["Metadata"]));
}
return ret;
}
public class Mod
{
public string Title;
public string Description;
public string Version;
}
public readonly Manifest Manifest;
public readonly ObjectCreator ObjectCreator;
public readonly SheetBuilder SheetBuilder;
@@ -28,7 +57,7 @@ namespace OpenRA
public ILoadScreen LoadScreen = null;
public ModData( params string[] mods )
{
{
Manifest = new Manifest( mods );
ObjectCreator = new ObjectCreator( Manifest );
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen);

View File

@@ -187,9 +187,9 @@ namespace OpenRA
return new Order("PauseProduction", subject, new int2( pause ? 1 : 0, 0 ), item);
}
public static Order CancelProduction(Actor subject, string item)
public static Order CancelProduction(Actor subject, string item, int count)
{
return new Order("CancelProduction", subject, item);
return new Order("CancelProduction", subject, new int2( count, 0 ), item);
}
}
}

View File

@@ -21,10 +21,10 @@ namespace OpenRA.Network
return Game.LobbyInfo.Clients.FirstOrDefault(c => c.Index == id);
}
static Player FindPlayerByClientId(int id)
static Player FindPlayerByClientId( this World world, int id)
{
/* todo: find the interactive player. */
return Game.world.players.Values.FirstOrDefault(p => p.ClientIndex == id);
return world.players.Values.FirstOrDefault(p => p.ClientIndex == id);
}
public static void ProcessOrder( World world, int clientId, Order order )
@@ -43,7 +43,7 @@ namespace OpenRA.Network
var client = FindClientById(clientId);
if (client != null)
{
var player = FindPlayerByClientId(clientId);
var player = world.FindPlayerByClientId(clientId);
if (player != null && player.WinState == WinState.Lost)
Game.AddChatLine(client.Color1, client.Name + " (Dead)", order.TargetString);
else
@@ -56,7 +56,7 @@ namespace OpenRA.Network
var client = FindClientById(clientId);
if (client != null)
{
var player = FindPlayerByClientId(clientId);
var player = world.FindPlayerByClientId(clientId);
var display = (world.GameHasStarted) ?
player != null && (world.LocalPlayer != null && player.Stances[world.LocalPlayer] == Stance.Ally
|| player.WinState == WinState.Lost) :
@@ -78,7 +78,7 @@ namespace OpenRA.Network
}
case "SyncInfo":
{
Game.SyncLobbyInfo(order.TargetString);
Game.SyncLobbyInfo( world, order.TargetString);
break;
}
case "SetStance":

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{0DFB103F-2962-400F-8C6D-E2C28CCBA633}</ProjectGuid>
<OutputType>WinExe</OutputType>
@@ -230,6 +230,7 @@
<Compile Include="Traits\Valued.cs" />
<Compile Include="Traits\World\BibLayer.cs" />
<Compile Include="Widgets\Delegates\DeveloperModeDelegate.cs" />
<Compile Include="Widgets\ScrollingTextWidget.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Framework.2.0">

View File

@@ -35,8 +35,11 @@ namespace OpenRA.Orders
IEnumerable<Order> OrderInner(World world, int2 xy, MouseInput mi)
{
if (mi.Button == MouseButton.Left && world.Map.IsInMap(xy))
yield return new Order(order, subject, xy);
if( mi.Button == MouseButton.Left && world.Map.IsInMap( xy ) )
{
world.CancelInputMode();
yield return new Order( order, subject, xy );
}
}
public virtual void Tick(World world) { }

View File

@@ -11,7 +11,8 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Traits;
using OpenRA.Traits;
using OpenRA.FileFormats;
namespace OpenRA.Orders
{
@@ -25,17 +26,17 @@ namespace OpenRA.Orders
.FirstOrDefault();
var orders = world.Selection.Actors
.Select(a => a.Order(xy, mi, underCursor))
.Select(a => OrderForUnit(a, xy, mi, underCursor))
.Where(o => o != null)
.ToArray();
var actorsInvolved = orders.Select(o => o.Subject).Distinct();
var actorsInvolved = orders.Select(o => o.self).Distinct();
if (actorsInvolved.Any())
yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor,
string.Join(",", actorsInvolved.Select(a => a.ActorID.ToString()).ToArray()));
foreach (var o in orders)
yield return o;
string.Join(",", actorsInvolved.Select(a => a.ActorID.ToString()).ToArray()));
foreach( var o in orders )
yield return CheckSameOrder( o.iot, o.trait.IssueOrder( o.self, o.iot, o.target ) );
}
public void Tick( World world ) {}
@@ -45,39 +46,107 @@ namespace OpenRA.Orders
foreach (var a in world.Selection.Actors)
if (!a.Destroyed)
foreach (var t in a.TraitsImplementing<IPreRenderSelection>())
t.RenderBeforeWorld(a);
t.RenderBeforeWorld(a);
Game.Renderer.Flush();
}
public void RenderAfterWorld( World world )
{
foreach (var a in world.Selection.Actors)
foreach (var a in world.Selection.Actors)
if (!a.Destroyed)
foreach (var t in a.TraitsImplementing<IPostRenderSelection>())
t.RenderAfterWorld(a);
t.RenderAfterWorld(a);
Game.Renderer.Flush();
}
public string GetCursor( World world, int2 xy, MouseInput mi )
{
if (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())
{
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.Info.Traits.Contains<SelectableInfo>())
.Any();
if (underCursor)
return "select";
}
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.Info.Traits.Contains<TargetableInfo>())
.OrderByDescending(a => a.Info.Traits.Contains<SelectableInfo>() ? a.Info.Traits.Get<SelectableInfo>().Priority : int.MinValue)
.FirstOrDefault();
var c = Order(world, xy, mi)
.Select(o => o.Subject.TraitsImplementing<IOrderCursor>()
.Select(pc => pc.CursorForOrder(o.Subject, o)).FirstOrDefault(a => a != null))
.FirstOrDefault(a => a != null);
return c ?? "default";
if( mi.Modifiers.HasModifier( Modifiers.Shift ) || !world.Selection.Actors.Any() )
if( underCursor != null )
return "select";
var orders = world.Selection.Actors
.Select(a => OrderForUnit(a, xy, mi, underCursor))
.Where(o => o != null)
.ToArray();
if( orders.Length == 0 ) return "default";
return orders[ 0 ].cursor ?? "default";
}
static UnitOrderResult OrderForUnit( Actor self, int2 xy, MouseInput mi, Actor underCursor )
{
if (self.Owner != self.World.LocalPlayer)
return null;
if (!self.World.Map.IsInMap(xy.X, xy.Y))
return null;
if (self.Destroyed)
return null;
//var old = self.TraitsImplementing<IIssueOrder>()
// .OrderByDescending( x => x.OrderPriority( self, xy, mi, underCursor ) )
// .Select( x => x.IssueOrder( self, xy, mi, underCursor ) )
// .FirstOrDefault( x => x != null );
//if( old != null )
// return old;
if( mi.Button == MouseButton.Right )
{
var uim = self.World.WorldActor.Trait<UnitInfluence>();
foreach( var o in self.TraitsImplementing<IIssueOrder>()
.SelectMany( trait => trait.Orders
.Select( x => new { Trait = trait, Order = x } ) )
.OrderByDescending( x => x.Order.OrderPriority ) )
{
var actorsAt = uim.GetUnitsAt( xy ).ToList();
string cursor = null;
if( underCursor != null )
if( o.Order.CanTargetUnit( self, underCursor, mi.Modifiers.HasModifier( Modifiers.Ctrl ), mi.Modifiers.HasModifier( Modifiers.Alt ), ref cursor ) )
return new UnitOrderResult( self, o.Order, o.Trait, cursor, Target.FromActor( underCursor ) );
if( o.Order.CanTargetLocation( self, xy, actorsAt, mi.Modifiers.HasModifier( Modifiers.Ctrl ), mi.Modifiers.HasModifier( Modifiers.Alt ), ref cursor ) )
return new UnitOrderResult( self, o.Order, o.Trait, cursor, Target.FromCell( xy ) );
}
}
return null;
}
static Order CheckSameOrder( IOrderTargeter iot, Order order )
{
if( order == null && iot.OrderID != null )
Game.Debug( "BUG: in order targeter - decided on {0} but then didn't order", iot.OrderID );
else if( iot.OrderID != order.OrderString )
Game.Debug( "BUG: in order targeter - decided on {0} but ordered {1}", iot.OrderID, order.OrderString );
return order;
}
class UnitOrderResult
{
public readonly Actor self;
public readonly IOrderTargeter iot;
public readonly IIssueOrder trait;
public readonly string cursor;
public readonly Target target;
public UnitOrderResult( Actor self, IOrderTargeter iot, IIssueOrder trait, string cursor, Target target )
{
this.self = self;
this.iot = iot;
this.trait = trait;
this.cursor = cursor;
this.target = target;
}
}
}
}

View File

@@ -37,7 +37,8 @@ namespace OpenRA
public readonly int Index;
public readonly bool NonCombatant = false;
public readonly int ClientIndex;
public readonly PlayerReference PlayerRef;
public readonly PlayerReference PlayerRef;
public bool IsBot;
public ShroudRenderer Shroud;
public World World { get; private set; }
@@ -49,6 +50,7 @@ namespace OpenRA
Index = index;
Palette = "player"+index;
Color = pr.Color;
Color2 = pr.Color2;
ClientIndex = 0; /* it's a map player, "owned" by host */

View File

@@ -42,7 +42,7 @@ namespace OpenRA
else
actors = (isCombine ? oldSelection.Union(newSelection) : newSelection).ToList();
var voicedUnit = actors.FirstOrDefault(a => a.Owner == world.LocalPlayer && a.HasVoice());
var voicedUnit = actors.FirstOrDefault(a => a.Owner == world.LocalPlayer && a.IsInWorld && a.HasVoice());
if (voicedUnit != null)
Sound.PlayVoice("Select", voicedUnit, voicedUnit.Owner.Country.Race);
@@ -56,6 +56,10 @@ namespace OpenRA
public void Tick(World world)
{
actors.RemoveAll(a => !a.IsInWorld);
foreach (var cg in controlGroups.Values)
cg.RemoveAll(a => a.Destroyed); // note: NOT `!a.IsInWorld`, since that would remove things
// that are in transports.
}
Cache<int, List<Actor>> controlGroups = new Cache<int, List<Actor>>(_ => new List<Actor>());

1
OpenRA.Game/Server/Exts.cs Normal file → Executable file
View File

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

60
OpenRA.Game/Server/MasterServerQuery.cs Normal file → Executable file
View File

@@ -14,15 +14,20 @@ using System.Net;
using System.Text;
using System.Threading;
using OpenRA.FileFormats;
using OpenRA.Widgets;
namespace OpenRA.Server
{
static class MasterServerQuery
{
public static event Action<GameServer[]> OnComplete = _ => { };
public static event Action<string> OnVersion = _ => { };
static GameServer[] Games = { };
public static string ClientVersion = "";
public static string ServerVersion = "";
static AutoResetEvent ev = new AutoResetEvent(false);
static AutoResetEvent ev2 = new AutoResetEvent(false);
public static void Refresh(string masterServerUrl)
{
@@ -30,9 +35,7 @@ namespace OpenRA.Server
{
try
{
var wc = new WebClient();
var data = wc.DownloadData(new Uri(masterServerUrl + "list.php"));
var str = Encoding.UTF8.GetString(data);
var str = GetData(new Uri(masterServerUrl + "list.php"));
var yaml = MiniYaml.FromString(str);
@@ -48,10 +51,61 @@ namespace OpenRA.Server
}).Start();
}
public static void GetMOTD(string masterServerUrl)
{
var motd = Widget.RootWidget.GetWidget<ScrollingTextWidget>("MOTD_SCROLLER");
// Runs in a separate thread to prevent dns lookup hitches
new Thread(() =>
{
if (motd != null)
{
try
{
motd.SetText(GetData(new Uri(masterServerUrl + "motd.php?v=" + ClientVersion)));
motd.ResetScroll();
}
catch
{
motd.SetText("Welcome to OpenRA. MOTD unable to be loaded from server.");
motd.ResetScroll();
}
}
ev.Set();
}).Start();
}
public static void Tick()
{
if (ev.WaitOne(TimeSpan.FromMilliseconds(0)))
OnComplete(Games);
if (ev2.WaitOne(TimeSpan.FromMilliseconds(0)))
OnVersion(ServerVersion);
}
static string GetData(Uri uri)
{
var wc = new WebClient();
var data = wc.DownloadData(uri);
return Encoding.UTF8.GetString(data);
}
public static void GetCurrentVersion(string masterServerUrl)
{
new Thread(() =>
{
try
{
ServerVersion = GetData(new Uri(masterServerUrl + "VERSION"));
}
catch
{
ServerVersion = "";
}
ev2.Set();
}).Start();
}
}

View File

@@ -164,7 +164,8 @@ namespace OpenRA.Server
static int ChooseFreeSlot()
{
return lobbyInfo.Slots.First(s => !s.Closed && s.Bot == null).Index;
return lobbyInfo.Slots.First(s => !s.Closed && s.Bot == null
&& !lobbyInfo.Clients.Any( c => c.Slot == s.Index )).Index;
}
static void AcceptConnection()
@@ -588,8 +589,9 @@ namespace OpenRA.Server
static void SyncLobbyInfo()
{
DispatchOrders(null, 0,
new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize());
if (!GameStarted) /* don't do this while the game is running, it breaks things. */
DispatchOrders(null, 0,
new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize());
PingMasterServer();
}

View File

@@ -129,5 +129,34 @@ namespace OpenRA
return p.Index * 0x567;
return 0;
}
public static void CheckSyncUnchanged( World world, Action fn )
{
CheckSyncUnchanged( world, () => { fn(); return true; } );
}
static bool inUnsyncedCode = false;
public static T CheckSyncUnchanged<T>( World world, Func<T> fn )
{
int sync = world.SyncHash();
inUnsyncedCode = true;
try
{
return fn();
}
finally
{
inUnsyncedCode = false;
if( sync != world.SyncHash() )
throw new InvalidOperationException( "Desync in DispatchMouseInput" );
}
}
public static void AssertUnsynced( string message )
{
if( !inUnsyncedCode )
throw new InvalidOperationException( message );
}
}
}

View File

@@ -10,7 +10,7 @@
namespace OpenRA.Traits.Activities
{
class Idle : CancelableActivity
public class Idle : CancelableActivity
{
public override IActivity Tick(Actor self) { return NextActivity; }
}

View File

@@ -53,6 +53,9 @@ namespace OpenRA.Traits
if ((lifetime <= 0 || --lifetime <= 0) && !force)
return;
if (!target.IsValid)
return;
var p = target.CenterLocation;
Game.Renderer.LineRenderer.DrawLine(self.CenterLocation, p, c, c);

6
OpenRA.Game/Traits/Health.cs Normal file → Executable file
View File

@@ -140,6 +140,8 @@ namespace OpenRA.Traits
{
public static bool IsDead(this Actor self)
{
if (self.Destroyed) return true;
var health = self.TraitOrDefault<Health>();
return (health == null) ? true : health.IsDead;
}
@@ -161,7 +163,9 @@ namespace OpenRA.Traits
{
var health = self.TraitOrDefault<Health>();
if (health == null) return;
health.InflictDamage(self, attacker, health.HP, null);
/* hack. Fix for proper */
health.InflictDamage(self, attacker, int.MaxValue, null);
}
}
}

View File

@@ -16,6 +16,7 @@ using OpenRA.Effects;
using OpenRA.Traits.Activities;
using OpenRA.FileFormats;
using System.Diagnostics;
using OpenRA.Orders;
namespace OpenRA.Traits
{
@@ -53,7 +54,7 @@ namespace OpenRA.Traits
}
}
public class Mobile : IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice, IOccupySpace, IMove, IFacing, INudge
public class Mobile : IIssueOrder, IResolveOrder, IOrderVoice, IOccupySpace, IMove, IFacing, INudge
{
public readonly Actor self;
public readonly MobileInfo Info;
@@ -131,21 +132,17 @@ namespace OpenRA.Traits
self.CenterLocation = Util.CenterOfCell(fromCell);
}
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
// Force move takes precedence
return mi.Modifiers.HasModifier(Modifiers.Alt) ? int.MaxValue : 0;
}
// Note: Returns a valid order even if the unit can't move to the target
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (Info.OnRails) return null;
if (mi.Button == MouseButton.Left) return null;
public IEnumerable<IOrderTargeter> Orders { get { yield return new MoveOrderTargeter( Info ); } }
var type = (!self.World.LocalPlayer.Shroud.IsVisible(xy) || CanEnterCell(xy)) ? "Move" : "Move-Blocked";
return new Order(type, self, xy, mi.Modifiers.HasModifier(Modifiers.Shift));
// Note: Returns a valid order even if the unit can't move to the target
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if( order is MoveOrderTargeter )
{
if( Info.OnRails ) return null;
return new Order( "Move", self, Util.CellContaining( target.CenterLocation ), false );
}
return null;
}
public int2 NearestMoveableCell(int2 target)
@@ -170,7 +167,7 @@ namespace OpenRA.Traits
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Move" || order.OrderString == "Move-Blocked")
if (order.OrderString == "Move")
{
int2 currentLocation = NearestMoveableCell(order.TargetLocation);
if (!CanEnterCell(currentLocation))
@@ -190,20 +187,9 @@ namespace OpenRA.Traits
}
}
public string CursorForOrder(Actor self, Order order)
{
if (order.OrderString == "Move")
return "move";
if (order.OrderString == "Move-Blocked")
return "move-blocked";
return null;
}
public string VoicePhraseForOrder(Actor self, Order order)
{
if (order.OrderString == "Move" || order.OrderString == "Move-Blocked")
if (order.OrderString == "Move")
return "Move";
return null;
}
@@ -369,5 +355,31 @@ namespace OpenRA.Traits
Log.Write("debug", "OnNudge #{0} refuses at {1}",
self.ActorID, self.Location);
}
class MoveOrderTargeter : IOrderTargeter
{
readonly MobileInfo unitType;
public MoveOrderTargeter( MobileInfo unitType )
{
this.unitType = unitType;
}
public string OrderID { get { return "Move"; } }
public int OrderPriority { get { return 4; } }
public bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
{
return false;
}
public bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor )
{
cursor = "move";
if( self.World.LocalPlayer.Shroud.IsVisible( location ) && !self.Trait<Mobile>().CanEnterCell( location ) )
cursor = "move-blocked";
return true;
}
}
}
}

View File

@@ -67,10 +67,10 @@ namespace OpenRA.Traits
break;
}
case "DevShroud":
{
{
DisableShroud ^= true;
if (self.World.LocalPlayer == self.Owner)
Game.world.LocalPlayer.Shroud.Disabled = DisableShroud;
self.World.LocalPlayer.Shroud.Disabled = DisableShroud;
break;
}
case "DevPathDebug":
@@ -83,17 +83,17 @@ namespace OpenRA.Traits
if (self.World.LocalPlayer == self.Owner)
Game.Settings.Debug.ShowCollisions ^= true;
break;
}
case "DevGiveExploration":
{
if (self.World.LocalPlayer == self.Owner)
self.World.WorldActor.Trait<Shroud>().ExploreAll(self.World);
break;
}
default:
case "DevGiveExploration":
{
if (self.World.LocalPlayer == self.Owner)
self.World.WorldActor.Trait<Shroud>().ExploreAll(self.World);
break;
}
default:
return;
}
}
Game.Debug("Cheat used: {0} by {1}"
.F(order.OrderString, self.Owner.PlayerName));
}

View File

@@ -61,9 +61,10 @@ namespace OpenRA.Traits
void DrawHealthBar(Actor self, float2 xy, float2 Xy)
{
if (!self.IsInWorld) return;
var health = self.TraitOrDefault<Health>();
if (self.IsDead() || health == null)
return;
if (health == null || health.IsDead) return;
var c = Color.Gray;
Game.Renderer.LineRenderer.DrawLine(xy + new float2(0, -2), xy + new float2(0, -4), c, c);
@@ -158,7 +159,7 @@ namespace OpenRA.Traits
void DrawUnitPath(Actor self)
{
if (!Game.world.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug) return;
if (!self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug) return;
var activity = self.GetCurrentActivity();
var mobile = self.TraitOrDefault<IMove>();

View File

@@ -17,7 +17,7 @@ namespace OpenRA.Traits
public class TargetableInfo : ITraitInfo
{
public readonly string[] TargetTypes = {};
public object Create( ActorInitializer init ) { return new Targetable(this); }
public virtual object Create( ActorInitializer init ) { return new Targetable(this); }
}
public class Targetable : ITargetable

View File

@@ -35,8 +35,15 @@ namespace OpenRA.Traits
public interface IRender { IEnumerable<Renderable> Render(Actor self); }
public interface IIssueOrder
{
Order IssueOrder( Actor self, int2 xy, MouseInput mi, Actor underCursor );
int OrderPriority( Actor self, int2 xy, MouseInput mi, Actor underCursor );
IEnumerable<IOrderTargeter> Orders { get; }
Order IssueOrder( Actor self, IOrderTargeter order, Target target );
}
public interface IOrderTargeter
{
string OrderID { get; }
int OrderPriority { get; }
bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor );
bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor );
}
public interface IResolveOrder { void ResolveOrder(Actor self, Order order); }
public interface IOrderCursor { string CursorForOrder(Actor self, Order order); }

202
OpenRA.Game/Widgets/Delegates/LobbyDelegate.cs Normal file → Executable file
View File

@@ -12,7 +12,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Network;
using OpenRA.Network;
namespace OpenRA.Widgets.Delegates
{
@@ -22,7 +22,7 @@ namespace OpenRA.Widgets.Delegates
Dictionary<string, string> CountryNames;
string MapUid;
MapStub Map;
MapStub Map;
public static Color CurrentColorPreview1;
public static Color CurrentColorPreview2;
@@ -133,74 +133,74 @@ namespace OpenRA.Widgets.Delegates
teamChat ^= true;
chatLabel.Text = (teamChat) ? "Team:" : "Chat:";
return true;
};
var colorChooser = lobby.GetWidget("COLOR_CHOOSER");
var hueSlider = colorChooser.GetWidget<SliderWidget>("HUE_SLIDER");
var satSlider = colorChooser.GetWidget<SliderWidget>("SAT_SLIDER");
var lumSlider = colorChooser.GetWidget<SliderWidget>("LUM_SLIDER");
var rangeSlider = colorChooser.GetWidget<SliderWidget>("RANGE_SLIDER");
hueSlider.OnChange += _ => UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
satSlider.OnChange += _ => UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
lumSlider.OnChange += _ => UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
rangeSlider.OnChange += _ => UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
colorChooser.GetWidget<ButtonWidget>("BUTTON_OK").OnMouseUp = mi =>
{
colorChooser.IsVisible = () => false;
UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
UpdatePlayerColor(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
return true;
};
}
void UpdatePlayerColor(float hf, float sf, float lf, float r)
{
var c1 = ColorFromHSL(hf, sf, lf);
var c2 = ColorFromHSL(hf, sf, r*lf);
Game.Settings.Player.Color1 = c1;
Game.Settings.Player.Color2 = c2;
Game.Settings.Save();
Game.IssueOrder(Order.Command("color {0},{1},{2},{3},{4},{5}".F(c1.R,c1.G,c1.B,c2.R,c2.G,c2.B)));
}
void UpdateColorPreview(float hf, float sf, float lf, float r)
{
CurrentColorPreview1 = ColorFromHSL(hf, sf, lf);
CurrentColorPreview2 = ColorFromHSL(hf, sf, r*lf);
Game.viewport.RefreshPalette();
}
var colorChooser = lobby.GetWidget("COLOR_CHOOSER");
var hueSlider = colorChooser.GetWidget<SliderWidget>("HUE_SLIDER");
var satSlider = colorChooser.GetWidget<SliderWidget>("SAT_SLIDER");
var lumSlider = colorChooser.GetWidget<SliderWidget>("LUM_SLIDER");
var rangeSlider = colorChooser.GetWidget<SliderWidget>("RANGE_SLIDER");
hueSlider.OnChange += _ => UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
satSlider.OnChange += _ => UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
lumSlider.OnChange += _ => UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
rangeSlider.OnChange += _ => UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
colorChooser.GetWidget<ButtonWidget>("BUTTON_OK").OnMouseUp = mi =>
{
colorChooser.IsVisible = () => false;
UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
UpdatePlayerColor(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
return true;
};
}
// hk is hue in the range [0,1] instead of [0,360]
Color ColorFromHSL(float hk, float s, float l)
{
// Convert from HSL to RGB
var q = (l < 0.5f) ? l * (1 + s) : l + s - (l * s);
var p = 2 * l - q;
float[] trgb = { hk + 1 / 3.0f,
hk,
hk - 1/3.0f };
float[] rgb = { 0, 0, 0 };
for (int k = 0; k < 3; k++)
{
while (trgb[k] < 0) trgb[k] += 1.0f;
while (trgb[k] > 1) trgb[k] -= 1.0f;
}
for (int k = 0; k < 3; k++)
{
if (trgb[k] < 1 / 6.0f) { rgb[k] = (p + ((q - p) * 6 * trgb[k])); }
else if (trgb[k] >= 1 / 6.0f && trgb[k] < 0.5) { rgb[k] = q; }
else if (trgb[k] >= 0.5f && trgb[k] < 2.0f / 3) { rgb[k] = (p + ((q - p) * 6 * (2.0f / 3 - trgb[k]))); }
else { rgb[k] = p; }
}
return Color.FromArgb((int)(rgb[0] * 255), (int)(rgb[1] * 255), (int)(rgb[2] * 255));
}
void UpdatePlayerColor(float hf, float sf, float lf, float r)
{
var c1 = ColorFromHSL(hf, sf, lf);
var c2 = ColorFromHSL(hf, sf, r*lf);
Game.Settings.Player.Color1 = c1;
Game.Settings.Player.Color2 = c2;
Game.Settings.Save();
Game.IssueOrder(Order.Command("color {0},{1},{2},{3},{4},{5}".F(c1.R,c1.G,c1.B,c2.R,c2.G,c2.B)));
}
void UpdateColorPreview(float hf, float sf, float lf, float r)
{
CurrentColorPreview1 = ColorFromHSL(hf, sf, lf);
CurrentColorPreview2 = ColorFromHSL(hf, sf, r*lf);
Game.viewport.RefreshPalette();
}
// hk is hue in the range [0,1] instead of [0,360]
public static Color ColorFromHSL(float hk, float s, float l)
{
// Convert from HSL to RGB
var q = (l < 0.5f) ? l * (1 + s) : l + s - (l * s);
var p = 2 * l - q;
float[] trgb = { hk + 1 / 3.0f,
hk,
hk - 1/3.0f };
float[] rgb = { 0, 0, 0 };
for (int k = 0; k < 3; k++)
{
while (trgb[k] < 0) trgb[k] += 1.0f;
while (trgb[k] > 1) trgb[k] -= 1.0f;
}
for (int k = 0; k < 3; k++)
{
if (trgb[k] < 1 / 6.0f) { rgb[k] = (p + ((q - p) * 6 * trgb[k])); }
else if (trgb[k] >= 1 / 6.0f && trgb[k] < 0.5) { rgb[k] = q; }
else if (trgb[k] >= 0.5f && trgb[k] < 2.0f / 3) { rgb[k] = (p + ((q - p) * 6 * (2.0f / 3 - trgb[k]))); }
else { rgb[k] = p; }
}
return Color.FromArgb((int)(rgb[0] * 255), (int)(rgb[1] * 255), (int)(rgb[2] * 255));
}
void UpdateCurrentMap()
@@ -209,23 +209,23 @@ namespace OpenRA.Widgets.Delegates
MapUid = Game.LobbyInfo.GlobalSettings.Map;
Map = Game.modData.AvailableMaps[MapUid];
}
bool hasJoined = false;
void JoinedServer()
{
if (hasJoined)
return;
hasJoined = true;
if (Game.LocalClient.Name != Game.Settings.Player.Name)
Game.IssueOrder(Order.Command("name " + Game.Settings.Player.Name));
bool hasJoined = false;
void JoinedServer()
{
if (hasJoined)
return;
hasJoined = true;
if (Game.LocalClient.Name != Game.Settings.Player.Name)
Game.IssueOrder(Order.Command("name " + Game.Settings.Player.Name));
var c1 = Game.Settings.Player.Color1;
var c2 = Game.Settings.Player.Color2;
if (Game.LocalClient.Color1 != c1 || Game.LocalClient.Color2 != c2)
Game.IssueOrder(Order.Command("color {0},{1},{2},{3},{4},{5}".F(c1.R,c1.G,c1.B,c2.R,c2.G,c2.B)));
if (Game.LocalClient.Color1 != c1 || Game.LocalClient.Color2 != c2)
Game.IssueOrder(Order.Command("color {0},{1},{2},{3},{4},{5}".F(c1.R,c1.G,c1.B,c2.R,c2.G,c2.B)));
}
void ResetConnectionState()
@@ -313,24 +313,24 @@ namespace OpenRA.Widgets.Delegates
name.OnLoseFocus = () => name.OnEnterKey();
var color = template.GetWidget<ButtonWidget>("COLOR");
color.OnMouseUp = mi =>
{
var colorChooser = Widget.RootWidget.GetWidget("SERVER_LOBBY").GetWidget("COLOR_CHOOSER");
var hueSlider = colorChooser.GetWidget<SliderWidget>("HUE_SLIDER");
hueSlider.Offset = Game.LocalClient.Color1.GetHue()/360f;
var satSlider = colorChooser.GetWidget<SliderWidget>("SAT_SLIDER");
satSlider.Offset = Game.LocalClient.Color1.GetSaturation();
var lumSlider = colorChooser.GetWidget<SliderWidget>("LUM_SLIDER");
lumSlider.Offset = Game.LocalClient.Color1.GetBrightness();
var rangeSlider = colorChooser.GetWidget<SliderWidget>("RANGE_SLIDER");
rangeSlider.Offset = Game.LocalClient.Color1.GetBrightness() == 0 ? 0 : Game.LocalClient.Color2.GetBrightness()/Game.LocalClient.Color1.GetBrightness();
UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
colorChooser.IsVisible = () => true;
return true;
color.OnMouseUp = mi =>
{
var colorChooser = Widget.RootWidget.GetWidget("SERVER_LOBBY").GetWidget("COLOR_CHOOSER");
var hueSlider = colorChooser.GetWidget<SliderWidget>("HUE_SLIDER");
hueSlider.SetOffset(Game.LocalClient.Color1.GetHue()/360f);
var satSlider = colorChooser.GetWidget<SliderWidget>("SAT_SLIDER");
satSlider.SetOffset(Game.LocalClient.Color1.GetSaturation());
var lumSlider = colorChooser.GetWidget<SliderWidget>("LUM_SLIDER");
lumSlider.SetOffset(Game.LocalClient.Color1.GetBrightness());
var rangeSlider = colorChooser.GetWidget<SliderWidget>("RANGE_SLIDER");
rangeSlider.SetOffset(Game.LocalClient.Color1.GetBrightness() == 0 ? 0 : Game.LocalClient.Color2.GetBrightness()/Game.LocalClient.Color1.GetBrightness());
UpdateColorPreview(hueSlider.GetOffset(), satSlider.GetOffset(), lumSlider.GetOffset(), rangeSlider.GetOffset());
colorChooser.IsVisible = () => true;
return true;
};
var colorBlock = color.GetWidget<ColorBlockWidget>("COLORBLOCK");
@@ -422,4 +422,4 @@ namespace OpenRA.Widgets.Delegates
return true;
}
}
}
}

View File

@@ -9,6 +9,9 @@
#endregion
using OpenRA.FileFormats;
using OpenRA.Server;
using System.Net;
using System.IO;
namespace OpenRA.Widgets.Delegates
{
@@ -31,7 +34,16 @@ namespace OpenRA.Widgets.Delegates
var s = FileSystem.Open("VERSION");
version.Text = s.ReadAllText();
s.Close();
MasterServerQuery.OnVersion += v => { if (!string.IsNullOrEmpty(v)) version.Text += "\nLatest: " + v; };
MasterServerQuery.GetCurrentVersion(Game.Settings.Server.MasterServer);
}
else
{
version.Text = "Dev Build";
}
MasterServerQuery.ClientVersion = version.Text;
MasterServerQuery.GetMOTD(Game.Settings.Server.MasterServer);
}
}
}

View File

@@ -30,8 +30,8 @@ namespace OpenRA.Widgets.Delegates
MasterServerQuery.OnComplete += games => RefreshServerList(games);
widget.GetWidget("JOINSERVER_PROGRESS_TITLE").Visible = true;
widget.GetWidget<LabelWidget>("JOINSERVER_PROGRESS_TITLE").Text = "Fetching game list...";
bg.GetWidget("JOINSERVER_PROGRESS_TITLE").Visible = true;
bg.GetWidget<LabelWidget>("JOINSERVER_PROGRESS_TITLE").Text = "Fetching game list...";
bg.Children.RemoveAll(a => GameButtons.Contains(a));
GameButtons.Clear();
@@ -44,7 +44,7 @@ namespace OpenRA.Widgets.Delegates
preview.IsVisible = () => CurrentMap() != null;
bg.GetWidget<LabelWidget>("SERVER_IP").GetText = () => currentServer.Address;
bg.GetWidget<LabelWidget>("SERVER_MODS").GetText = () => string.Join(",", currentServer.Mods);
bg.GetWidget<LabelWidget>("SERVER_MODS").GetText = () => GenerateModsLabel();
bg.GetWidget<LabelWidget>("MAP_TITLE").GetText = () => (CurrentMap() != null) ? CurrentMap().Title : "Unknown";
bg.GetWidget<LabelWidget>("MAP_PLAYERS").GetText = () =>
{
@@ -62,8 +62,8 @@ namespace OpenRA.Widgets.Delegates
bg.GetWidget("REFRESH_BUTTON").OnMouseUp = mi =>
{
widget.GetWidget("JOINSERVER_PROGRESS_TITLE").Visible = true;
widget.GetWidget<LabelWidget>("JOINSERVER_PROGRESS_TITLE").Text = "Fetching game list...";
bg.GetWidget("JOINSERVER_PROGRESS_TITLE").Visible = true;
bg.GetWidget<LabelWidget>("JOINSERVER_PROGRESS_TITLE").Text = "Fetching game list...";
bg.Children.RemoveAll(a => GameButtons.Contains(a));
GameButtons.Clear();
@@ -120,11 +120,22 @@ namespace OpenRA.Widgets.Delegates
return (currentServer == null || !Game.modData.AvailableMaps.ContainsKey(currentServer.Map))
? null : Game.modData.AvailableMaps[currentServer.Map];
}
string GenerateModsLabel()
{
return string.Join("\n", currentServer.Mods.Select(m =>
ModData.AllMods.ContainsKey(m) ? string.Format("{0} ({1})", ModData.AllMods[m].Title, ModData.AllMods[m].Version)
: string.Format("Unknown Mod: {0}",m)).ToArray());
}
void RefreshServerList(IEnumerable<GameServer> games)
{
var r = Widget.RootWidget;
var bg = r.GetWidget("JOINSERVER_BG");
if (bg == null) // We got a MasterServer reply AFTER the browser is gone, just return to prevent crash - Gecko
return;
var sl = bg.GetWidget<ListBoxWidget>("SERVER_LIST");
sl.Children.Clear();
@@ -184,7 +195,6 @@ namespace OpenRA.Widgets.Delegates
dc.GetWidget("JOIN_BUTTON").OnMouseUp = mi =>
{
var address = dc.GetWidget<TextFieldWidget>("SERVER_ADDRESS").Text;
var cpts = address.Split(':').ToArray();
if (cpts.Length != 2)
@@ -201,7 +211,8 @@ namespace OpenRA.Widgets.Delegates
dc.GetWidget("CANCEL_BUTTON").OnMouseUp = mi =>
{
Widget.CloseWindow();
return widget.GetWidget("MAINMENU_BUTTON_JOIN").OnMouseUp(mi);
Widget.OpenWindow("MAINMENU_BG");
return true;
};
}
}

View File

@@ -53,6 +53,15 @@ namespace OpenRA.Widgets.Delegates
Game.Settings.Game.ViewportEdgeScroll ^= true;
return true;
};
// Added scroll sensitivity - Gecko
var edgeScrollSlider = general.GetWidget<SliderWidget>("EDGE_SCROLL_AMOUNT");
if (edgeScrollSlider != null) // Backwards compatible - Gecko
{
edgeScrollSlider.SetOffset(Game.Settings.Game.ViewportEdgeScrollStep);
edgeScrollSlider.OnChange += _ => { Game.Settings.Game.ViewportEdgeScrollStep = edgeScrollSlider.GetOffset(); };
Game.Settings.Game.ViewportEdgeScrollStep = edgeScrollSlider.GetOffset();
}
var inverseScroll = general.GetWidget<CheckboxWidget>("INVERSE_SCROLL");
inverseScroll.Checked = () => Game.Settings.Game.InverseDragScroll;
@@ -68,10 +77,12 @@ namespace OpenRA.Widgets.Delegates
var soundslider = audio.GetWidget<SliderWidget>("SOUND_VOLUME");
soundslider.OnChange += x => { Sound.SoundVolume = x; };
soundslider.GetOffset = () => { return Sound.SoundVolume; };
soundslider.SetOffset(Sound.SoundVolume);
var musicslider = audio.GetWidget<SliderWidget>("MUSIC_VOLUME");
musicslider.OnChange += x => { Sound.MusicVolume = x; };
musicslider.GetOffset = () => { return Sound.MusicVolume; };
musicslider.SetOffset(Sound.MusicVolume);
// Display

View File

@@ -16,10 +16,12 @@ namespace OpenRA.Widgets
public class LabelWidget : Widget
{
public enum TextAlign { Left, Center, Right }
public enum TextVAlign { Top, Middle, Bottom }
public string Text = null;
public string Background = null;
public TextAlign Align = TextAlign.Left;
public TextVAlign VAlign = TextVAlign.Middle;
public bool Bold = false;
public Func<string> GetText;
public Func<string> GetBackground;
@@ -54,8 +56,14 @@ namespace OpenRA.Widgets
return;
int2 textSize = font.Measure(text);
int2 position = RenderOrigin + new int2(0, (Bounds.Height - textSize.Y)/2);
int2 position = RenderOrigin;
if (VAlign == TextVAlign.Middle)
position += new int2(0, (Bounds.Height - textSize.Y)/2);
if (VAlign == TextVAlign.Bottom)
position += new int2(0, Bounds.Height - textSize.Y);
if (Align == TextAlign.Center)
position += new int2((Bounds.Width - textSize.X)/2, 0);

View File

@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace OpenRA.Widgets
{
class ScrollingTextWidget : Widget
{
public string Text = "";
private string ScrollingText = "";
public string Background = null;
public bool Bold = false;
public int ScrollLength = 200;
// ticks per single letter scroll
public int ScrollRate = 4;
private string ScrollBuffer = "";
private int ScrollLocation = 0;
private int ScrollTick = 0;
public Func<string> GetText;
public Func<string> GetBackground;
public ScrollingTextWidget()
: base()
{
GetText = () => Text;
GetBackground = () => Background;
}
protected ScrollingTextWidget(ScrollingTextWidget other)
: base(other)
{
Text = other.Text;
GetText = other.GetText;
Bold = other.Bold;
GetBackground = other.GetBackground;
}
public override void Tick(World world)
{
if (Text != "")
{
ScrollingText = Text;
Text = "";
}
UpdateScrollBuffer();
}
public void ResetScroll()
{
ScrollLocation = 0;
ScrollTick = 0;
}
private void UpdateScrollBuffer()
{
ScrollTick++;
if (ScrollTick < ScrollRate)
{
return;
}
ScrollTick = 0;
ScrollBuffer = "";
if (ScrollingText.Substring(ScrollingText.Length - 4, 3) != " ")
{
ScrollingText += " ";
}
int tempScrollLocation = ScrollLocation;
for (int i = 0; i < ScrollLength; ++i)
{
ScrollBuffer += ScrollingText.Substring(tempScrollLocation, 1);
tempScrollLocation = (tempScrollLocation + 1) % ScrollingText.Length;
}
ScrollLocation = (ScrollLocation + 1) % ScrollingText.Length;
}
public void SetText(string newText)
{
Text = newText.Replace("\n", " ");
Text = Text.Replace("\r", "");
}
public override void DrawInner(World world)
{
var bg = GetBackground();
if (bg != null)
WidgetUtils.DrawPanel(bg, RenderBounds);
var font = (Bold) ? Game.Renderer.BoldFont : Game.Renderer.RegularFont;
var text = GetText();
if (text == null)
return;
int2 textSize = font.Measure(text);
int2 position = RenderOrigin + new int2(0, (Bounds.Height - textSize.Y) / 2);
Game.Renderer.EnableScissor(position.X, position.Y, Bounds.Width, Bounds.Height);
font.DrawText(ScrollBuffer, position, Color.White);
Game.Renderer.DisableScissor();
}
public override Widget Clone() { return new ScrollingTextWidget(this); }
}
}

45
OpenRA.Game/Widgets/SliderWidget.cs Normal file → Executable file
View File

@@ -6,9 +6,9 @@
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System;
#endregion
using System;
using System.Drawing;
namespace OpenRA.Widgets
@@ -17,9 +17,11 @@ namespace OpenRA.Widgets
{
public event Action<float> OnChange;
public Func<float> GetOffset;
public float Offset = 0;
public int Ticks = 0;
public int TrackHeight = 5;
public float2 Range = new float2(0f, 1f);
private float Offset = 0;
int2 lastMouseLocation;
bool isMoving = false;
@@ -27,8 +29,15 @@ namespace OpenRA.Widgets
public SliderWidget()
: base()
{
GetOffset = () => Offset;
OnChange = x => Offset = x;
GetOffset = () =>
{
var Big = Math.Max(Range.X, Range.Y);
var Little = Math.Min(Range.X, Range.Y);
var Spread = Big - Little;
return Spread * Offset + Little;
};
OnChange = x => Offset = x.Clamp(0f, 1f);
}
public SliderWidget(SliderWidget other)
@@ -36,13 +45,23 @@ namespace OpenRA.Widgets
{
OnChange = other.OnChange;
GetOffset = other.GetOffset;
Offset = other.Offset;
Ticks = other.Ticks;
Range = other.Range;
Offset = GetOffset();
TrackHeight = other.TrackHeight;
lastMouseLocation = other.lastMouseLocation;
isMoving = other.isMoving;
}
public void SetOffset(float newOffset)
{
var Big = Math.Max(Range.X, Range.Y);
var Little = Math.Min(Range.X, Range.Y);
var Spread = Big - Little;
Offset = ((newOffset - Little) / Spread).Clamp(0f, 1f);
}
public override bool HandleInputInner(MouseInput mi)
{
if (mi.Event == MouseInputEvent.Down && !TakeFocus(mi))
@@ -72,7 +91,7 @@ namespace OpenRA.Widgets
}
else if (Ticks != 0)
{
var pos = GetOffset();
var pos = Offset;
// Offset slightly the direction we want to move so we don't get stuck on a tick
var delta = 0.001;
@@ -92,7 +111,7 @@ namespace OpenRA.Widgets
var thumb = thumbRect;
var center = thumb.X + thumb.Width / 2;
var newOffset = OffsetBy((mi.Location.X - center) * 1f / (RenderBounds.Width - thumb.Width));
if (newOffset != GetOffset())
if (newOffset != Offset)
{
OnChange(newOffset);
@@ -127,7 +146,7 @@ namespace OpenRA.Widgets
float OffsetBy(float amount)
{
var centerPos = GetOffset() + amount;
var centerPos = Offset + amount;
if (centerPos < 0) centerPos = 0;
if (centerPos > 1) centerPos = 1;
return centerPos;
@@ -141,7 +160,7 @@ namespace OpenRA.Widgets
{
var width = RenderBounds.Height;
var height = RenderBounds.Height;
var origin = (int)((RenderBounds.X + width / 2) + GetOffset() * (RenderBounds.Width - width) - width / 2f);
var origin = (int)((RenderBounds.X + width / 2) + Offset * (RenderBounds.Width - width) - width / 2f);
return new Rectangle(origin, RenderBounds.Y, width, height);
}
}
@@ -169,5 +188,5 @@ namespace OpenRA.Widgets
WidgetUtils.DrawPanel("dialog2", thumbRect);
}
}
}
}

View File

@@ -1,172 +1,178 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System;
using OpenRA.Graphics;
namespace OpenRA.Widgets
{
[Flags]
public enum ScrollDirection
{
None = 0,
Up = 1,
Left = 2,
Down = 4,
Right = 8
}
class ViewportScrollControllerWidget : Widget
{
public int EdgeScrollThreshold = 15;
ScrollDirection Keyboard;
namespace OpenRA.Widgets
{
[Flags]
public enum ScrollDirection
{
None = 0,
Up = 1,
Left = 2,
Down = 4,
Right = 8
}
class ViewportScrollControllerWidget : Widget
{
public int EdgeScrollThreshold = 15;
ScrollDirection Keyboard;
ScrollDirection Edge;
public ViewportScrollControllerWidget() : base() { }
protected ViewportScrollControllerWidget(ViewportScrollControllerWidget widget) : base(widget) {}
public override void DrawInner( World world ) {}
public override bool HandleInputInner(MouseInput mi)
{
if (mi.Event == MouseInputEvent.Move &&
(mi.Button == MouseButton.Middle || mi.Button == (MouseButton.Left | MouseButton.Right)))
public ViewportScrollControllerWidget() : base() { }
protected ViewportScrollControllerWidget(ViewportScrollControllerWidget widget) : base(widget) {}
public override void DrawInner( World world ) {}
public override bool HandleInputInner(MouseInput mi)
{
if (mi.Event == MouseInputEvent.Move &&
(mi.Button == MouseButton.Middle || mi.Button == (MouseButton.Left | MouseButton.Right)))
{
int InverseScroll = Game.Settings.Game.InverseDragScroll ? -1 : 1;
Game.viewport.Scroll((Viewport.LastMousePos - mi.Location) * InverseScroll);
return true;
}
return false;
}
public override string GetCursor(int2 pos)
{
if (!Game.Settings.Game.ViewportEdgeScroll)
return null;
if (Edge.Includes(ScrollDirection.Up) && Edge.Includes(ScrollDirection.Left)){
ScrollDirection BlockedDirections = Game.viewport.GetBlockedDirections();
if(BlockedDirections.Includes(ScrollDirection.Up) && BlockedDirections.Includes(ScrollDirection.Left))
return "scroll-tl-blocked";
else
return "scroll-tl";
}
if (Edge.Includes(ScrollDirection.Up) && Edge.Includes(ScrollDirection.Right)){
ScrollDirection BlockedDirections = Game.viewport.GetBlockedDirections();
if (BlockedDirections.Includes(ScrollDirection.Up) && BlockedDirections.Includes(ScrollDirection.Right))
return "scroll-tr-blocked";
else
return "scroll-tr";
}
if (Edge.Includes(ScrollDirection.Down) && Edge.Includes(ScrollDirection.Left)){
ScrollDirection BlockedDirections = Game.viewport.GetBlockedDirections();
if (BlockedDirections.Includes(ScrollDirection.Down) && BlockedDirections.Includes(ScrollDirection.Left))
return "scroll-bl-blocked";
else
return "scroll-bl";
}
if (Edge.Includes(ScrollDirection.Down) && Edge.Includes(ScrollDirection.Right)){
ScrollDirection BlockedDirections = Game.viewport.GetBlockedDirections();
if (BlockedDirections.Includes(ScrollDirection.Down) && BlockedDirections.Includes(ScrollDirection.Right))
return "scroll-br-blocked";
else
return "scroll-br";
}
if (Edge.Includes(ScrollDirection.Up))
if (Game.viewport.GetBlockedDirections().Includes(ScrollDirection.Up))
return "scroll-t-blocked";
else
return "scroll-t";
if (Edge.Includes(ScrollDirection.Down))
if (Game.viewport.GetBlockedDirections().Includes(ScrollDirection.Down))
return "scroll-b-blocked";
else
return "scroll-b";
if (Edge.Includes(ScrollDirection.Left))
if (Game.viewport.GetBlockedDirections().Includes(ScrollDirection.Left))
return "scroll-l-blocked";
else
return "scroll-l";
if (Edge.Includes(ScrollDirection.Right))
if (Game.viewport.GetBlockedDirections().Includes(ScrollDirection.Right))
return "scroll-r-blocked";
else
return "scroll-r";
return null;
}
public override bool LoseFocus (MouseInput mi)
{
Keyboard = ScrollDirection.None;
return base.LoseFocus(mi);
}
public override bool HandleKeyPressInner(KeyInput e)
{
switch (e.KeyName)
{
case "up": Keyboard = Keyboard.Set(ScrollDirection.Up, (e.Event == KeyInputEvent.Down)); return true;
case "down": Keyboard = Keyboard.Set(ScrollDirection.Down, (e.Event == KeyInputEvent.Down)); return true;
case "left": Keyboard = Keyboard.Set(ScrollDirection.Left, (e.Event == KeyInputEvent.Down)); return true;
case "right": Keyboard = Keyboard.Set(ScrollDirection.Right, (e.Event == KeyInputEvent.Down)); return true;
}
return false;
}
public override void Tick(World world)
{
Edge = ScrollDirection.None;
if (Game.Settings.Game.ViewportEdgeScroll && Game.HasInputFocus)
{
// Check for edge-scroll
if (Viewport.LastMousePos.X < EdgeScrollThreshold)
Edge = Edge.Set(ScrollDirection.Left, true);
if (Viewport.LastMousePos.Y < EdgeScrollThreshold)
Edge = Edge.Set(ScrollDirection.Up, true);
if (Viewport.LastMousePos.X >= Game.viewport.Width - EdgeScrollThreshold)
Edge = Edge.Set(ScrollDirection.Right, true);
if (Viewport.LastMousePos.Y >= Game.viewport.Height - EdgeScrollThreshold)
Edge = Edge.Set(ScrollDirection.Down, true);
}
if(Keyboard != ScrollDirection.None || Edge != ScrollDirection.None)
{
var scroll = new float2(0,0);
if (Keyboard.Includes(ScrollDirection.Up) || Edge.Includes(ScrollDirection.Up))
scroll += new float2(0, -10);
if (Keyboard.Includes(ScrollDirection.Right) || Edge.Includes(ScrollDirection.Right))
scroll += new float2(10, 0);
if (Keyboard.Includes(ScrollDirection.Down) || Edge.Includes(ScrollDirection.Down))
scroll += new float2(0, 10);
if (Keyboard.Includes(ScrollDirection.Left) || Edge.Includes(ScrollDirection.Left))
scroll += new float2(-10, 0);
Game.viewport.Scroll(scroll);
}
}
public override Widget Clone() { return new ViewportScrollControllerWidget(this); }
}
public static class ViewportExts
{
public static bool Includes(this ScrollDirection d, ScrollDirection s)
{
return (d & s) == s;
}
public static ScrollDirection Set(this ScrollDirection d, ScrollDirection s, bool val)
{
return (d.Includes(s) != val) ? d ^ s : d;
}
}
}
Game.viewport.Scroll((Viewport.LastMousePos - mi.Location) * InverseScroll);
return true;
}
return false;
}
public override string GetCursor(int2 pos)
{
if (!Game.Settings.Game.ViewportEdgeScroll)
return null;
if (Edge.Includes(ScrollDirection.Up) && Edge.Includes(ScrollDirection.Left)){
ScrollDirection BlockedDirections = Game.viewport.GetBlockedDirections();
if(BlockedDirections.Includes(ScrollDirection.Up) && BlockedDirections.Includes(ScrollDirection.Left))
return "scroll-tl-blocked";
else
return "scroll-tl";
}
if (Edge.Includes(ScrollDirection.Up) && Edge.Includes(ScrollDirection.Right)){
ScrollDirection BlockedDirections = Game.viewport.GetBlockedDirections();
if (BlockedDirections.Includes(ScrollDirection.Up) && BlockedDirections.Includes(ScrollDirection.Right))
return "scroll-tr-blocked";
else
return "scroll-tr";
}
if (Edge.Includes(ScrollDirection.Down) && Edge.Includes(ScrollDirection.Left)){
ScrollDirection BlockedDirections = Game.viewport.GetBlockedDirections();
if (BlockedDirections.Includes(ScrollDirection.Down) && BlockedDirections.Includes(ScrollDirection.Left))
return "scroll-bl-blocked";
else
return "scroll-bl";
}
if (Edge.Includes(ScrollDirection.Down) && Edge.Includes(ScrollDirection.Right)){
ScrollDirection BlockedDirections = Game.viewport.GetBlockedDirections();
if (BlockedDirections.Includes(ScrollDirection.Down) && BlockedDirections.Includes(ScrollDirection.Right))
return "scroll-br-blocked";
else
return "scroll-br";
}
if (Edge.Includes(ScrollDirection.Up))
if (Game.viewport.GetBlockedDirections().Includes(ScrollDirection.Up))
return "scroll-t-blocked";
else
return "scroll-t";
if (Edge.Includes(ScrollDirection.Down))
if (Game.viewport.GetBlockedDirections().Includes(ScrollDirection.Down))
return "scroll-b-blocked";
else
return "scroll-b";
if (Edge.Includes(ScrollDirection.Left))
if (Game.viewport.GetBlockedDirections().Includes(ScrollDirection.Left))
return "scroll-l-blocked";
else
return "scroll-l";
if (Edge.Includes(ScrollDirection.Right))
if (Game.viewport.GetBlockedDirections().Includes(ScrollDirection.Right))
return "scroll-r-blocked";
else
return "scroll-r";
return null;
}
public override bool LoseFocus (MouseInput mi)
{
Keyboard = ScrollDirection.None;
return base.LoseFocus(mi);
}
public override bool HandleKeyPressInner(KeyInput e)
{
switch (e.KeyName)
{
case "up": Keyboard = Keyboard.Set(ScrollDirection.Up, (e.Event == KeyInputEvent.Down)); return true;
case "down": Keyboard = Keyboard.Set(ScrollDirection.Down, (e.Event == KeyInputEvent.Down)); return true;
case "left": Keyboard = Keyboard.Set(ScrollDirection.Left, (e.Event == KeyInputEvent.Down)); return true;
case "right": Keyboard = Keyboard.Set(ScrollDirection.Right, (e.Event == KeyInputEvent.Down)); return true;
}
return false;
}
public override void Tick(World world)
{
Edge = ScrollDirection.None;
if (Game.Settings.Game.ViewportEdgeScroll && Game.HasInputFocus)
{
// Check for edge-scroll
if (Viewport.LastMousePos.X < EdgeScrollThreshold)
Edge = Edge.Set(ScrollDirection.Left, true);
if (Viewport.LastMousePos.Y < EdgeScrollThreshold)
Edge = Edge.Set(ScrollDirection.Up, true);
if (Viewport.LastMousePos.X >= Game.viewport.Width - EdgeScrollThreshold)
Edge = Edge.Set(ScrollDirection.Right, true);
if (Viewport.LastMousePos.Y >= Game.viewport.Height - EdgeScrollThreshold)
Edge = Edge.Set(ScrollDirection.Down, true);
}
if(Keyboard != ScrollDirection.None || Edge != ScrollDirection.None)
{
var scroll = new float2(0, 0);
// Modified to use the ViewportEdgeScrollStep setting - Gecko
if (Keyboard.Includes(ScrollDirection.Up) || Edge.Includes(ScrollDirection.Up))
scroll += new float2(0, -1);
if (Keyboard.Includes(ScrollDirection.Right) || Edge.Includes(ScrollDirection.Right))
scroll += new float2(1, 0);
if (Keyboard.Includes(ScrollDirection.Down) || Edge.Includes(ScrollDirection.Down))
scroll += new float2(0, 1);
if (Keyboard.Includes(ScrollDirection.Left) || Edge.Includes(ScrollDirection.Left))
scroll += new float2(-1, 0);
float length = Math.Max(1, scroll.Length);
scroll.X = (scroll.X / length) * Game.Settings.Game.ViewportEdgeScrollStep;
scroll.Y = (scroll.Y / length) * Game.Settings.Game.ViewportEdgeScrollStep;
Game.viewport.Scroll(scroll);
}
}
public override Widget Clone() { return new ViewportScrollControllerWidget(this); }
}
public static class ViewportExts
{
public static bool Includes(this ScrollDirection d, ScrollDirection s)
{
return (d & s) == s;
}
public static ScrollDirection Set(this ScrollDirection d, ScrollDirection s, bool val)
{
return (d.Includes(s) != val) ? d ^ s : d;
}
}
}

View File

@@ -6,14 +6,14 @@
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Orders;
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Orders;
using OpenRA.Traits;
namespace OpenRA.Widgets
@@ -109,9 +109,8 @@ namespace OpenRA.Widgets
public override string GetCursor(int2 pos)
{
var world = Game.world;
int sync = world.SyncHash();
try
{
return Sync.CheckSyncUnchanged( world, () =>
{
if (!world.GameHasStarted)
return "default";
@@ -122,29 +121,24 @@ namespace OpenRA.Widgets
Modifiers = Game.GetModifierKeys()
};
return Game.world.OrderGenerator.GetCursor( world, Game.viewport.ViewToWorld(mi).ToInt2(), mi );
}
finally
{
if( sync != world.SyncHash() )
throw new InvalidOperationException( "Desync in InputControllerWidget.GetCursor" );
}
return world.OrderGenerator.GetCursor( world, Game.viewport.ViewToWorld(mi).ToInt2(), mi );
} );
}
public override bool HandleKeyPressInner(KeyInput e)
{
if (e.Event == KeyInputEvent.Down)
{
if (e.KeyName.Length == 1 && char.IsDigit(e.KeyName[0]))
{
Game.world.Selection.DoControlGroup(Game.world, e.KeyName[0] - '0', e.Modifiers);
return true;
}
if (e.KeyChar == '\b')
{
GotoNextBase();
return true;
{
if (e.KeyName.Length == 1 && char.IsDigit(e.KeyName[0]))
{
Game.world.Selection.DoControlGroup(Game.world, e.KeyName[0] - '0', e.Modifiers);
return true;
}
if (e.KeyChar == '\b')
{
GotoNextBase();
return true;
}
}
return false;

View File

@@ -54,8 +54,18 @@ namespace OpenRA
public readonly TileSet TileSet;
public readonly WorldRenderer WorldRenderer;
public IOrderGenerator OrderGenerator = new UnitOrderGenerator();
IOrderGenerator orderGenerator_;
public IOrderGenerator OrderGenerator
{
get { return orderGenerator_; }
set
{
Sync.AssertUnsynced( "The current order generator may not be changed from synced code" );
orderGenerator_ = value;
}
}
public Selection Selection = new Selection();
public void CancelInputMode() { OrderGenerator = new UnitOrderGenerator(); }
@@ -76,6 +86,7 @@ namespace OpenRA
public World(Manifest manifest, Map map)
{
orderGenerator_ = new UnitOrderGenerator();
Map = map;
TileSet = Rules.TileSets[Map.Tileset];
@@ -194,7 +205,6 @@ namespace OpenRA
readonly World world;
public readonly Cache<Player, OwnedByCachedView> OwnedBy;
readonly TypeDictionary hasTrait = new TypeDictionary();
public AllQueries( World world )
{

View File

@@ -34,9 +34,6 @@ namespace OpenRA.Mods.Cnc
w.Add(new IonCannon(self, w, order.TargetLocation));
});
if (Owner == Owner.World.LocalPlayer)
self.World.CancelInputMode();
FinishActivate();
}
}

View File

@@ -126,7 +126,7 @@ namespace OpenRA.Mods.RA
}
// GoodGuy win conditions
// BadGuy is dead
int badcount = self.World.Queries.OwnedBy[Players["BadGuy"]].Count(a => a.IsInWorld && !a.IsDead());
int badcount = self.World.Queries.OwnedBy[Players["BadGuy"]].Count(a => !a.IsDead());
if (badcount != lastBadCount)
{
Game.Debug("{0} badguys remain".F(badcount));
@@ -137,7 +137,7 @@ namespace OpenRA.Mods.RA
}
//GoodGuy lose conditions
if (self.World.Queries.OwnedBy[Players["GoodGuy"]].Count( a => a.IsInWorld && !a.IsDead()) == 0)
if (self.World.Queries.OwnedBy[Players["GoodGuy"]].Count( a => !a.IsDead()) == 0)
OnLose(self.World);
// GoodGuy reinforcements

View File

@@ -12,9 +12,9 @@ using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Mods.RA;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Render;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
using OpenRA.Mods.RA.Render;
namespace OpenRA.Mods.Cnc
{
@@ -33,8 +33,7 @@ namespace OpenRA.Mods.Cnc
// Start and end beyond the edge of the map, to give a finite delay, and ability to land when AFLD is on map edge
var startPos = new int2(owner.World.Map.XOffset + owner.World.Map.Width+5, self.Location.Y);
var endPos = new int2(owner.World.Map.XOffset-5, self.Location.Y);
var endPos = new int2(owner.World.Map.XOffset-5, self.Location.Y);
// Assume a single exit point for simplicity
var exit = self.Info.Traits.WithInterface<ExitInfo>().First();
@@ -55,8 +54,9 @@ namespace OpenRA.Mods.Cnc
a.QueueActivity(new Land(Target.FromActor(self)));
a.QueueActivity(new CallFunc(() =>
{
if (self.IsDead())
if (!self.IsInWorld || self.IsDead())
return;
rb.PlayCustomAnimRepeating(self, "idle");
self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit));
}));

View File

@@ -11,10 +11,11 @@
using System.Collections.Generic;
using OpenRA.Mods.RA.Render;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
namespace OpenRA.Mods.RA.Activities
{
public class IdleAnimation : IActivity
{
public class IdleAnimation : Idle
{
string sequence;
int delay;
@@ -25,10 +26,8 @@ namespace OpenRA.Mods.RA.Activities
this.delay = delay;
}
IActivity NextActivity { get; set; }
bool active = true;
public IActivity Tick(Actor self)
public override IActivity Tick(Actor self)
{
if (!active) return NextActivity;
@@ -38,23 +37,11 @@ namespace OpenRA.Mods.RA.Activities
return this;
}
public void Cancel(Actor self)
protected override bool OnCancel()
{
active = false;
NextActivity = null;
active = false;
return true;
}
public void Queue( IActivity activity )
{
if( NextActivity != null )
NextActivity.Queue( activity );
else
NextActivity = activity;
}
public IEnumerable<float2> GetCurrentPath()
{
yield break;
}
}
}

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Mods.RA.Activities
public override IActivity Tick(Actor self)
{
if (IsCanceled) return NextActivity;
if (target == null || target.IsDead()) return NextActivity;
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
if (target.Owner == self.Owner) return NextActivity;
foreach (var t in target.TraitsImplementing<IAcceptSpy>())

View File

@@ -9,9 +9,9 @@
#endregion
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
using OpenRA.FileFormats;
namespace OpenRA.Mods.RA.Activities
{

View File

@@ -16,7 +16,7 @@ namespace OpenRA.Mods.RA
{
public class AircraftInfo : ITraitInfo
{
public readonly int CruiseAltitude = 20;
public readonly int CruiseAltitude = 30;
[ActorReference]
public readonly string[] RepairBuildings = { "fix" };
[ActorReference]
@@ -30,6 +30,7 @@ namespace OpenRA.Mods.RA
public class Aircraft : IMove, IFacing, IOccupySpace
{
protected readonly Actor self;
[Sync]
public int2 Location;
[Sync]
@@ -41,6 +42,7 @@ namespace OpenRA.Mods.RA
public Aircraft( ActorInitializer init , AircraftInfo info)
{
this.self = init.self;
if (init.Contains<LocationInit>())
this.Location = init.Get<LocationInit,int2>();
@@ -64,8 +66,9 @@ namespace OpenRA.Mods.RA
self.CenterLocation = Util.CenterOfCell(cell);
}
public bool AircraftCanEnter(Actor self, Actor a)
public bool AircraftCanEnter(Actor a)
{
if( self.Owner != a.Owner ) return false;
return Info.RearmBuildings.Contains( a.Info.Name )
|| Info.RepairBuildings.Contains( a.Info.Name );
}

View File

@@ -39,7 +39,7 @@ namespace OpenRA.Mods.RA
public virtual object Create(ActorInitializer init) { return new AttackBase(init.self); }
}
public class AttackBase : IIssueOrder, IResolveOrder, ITick, IExplodeModifier, IOrderCursor, IOrderVoice
public class AttackBase : IIssueOrder, IResolveOrder, ITick, IExplodeModifier, IOrderVoice
{
public bool IsAttacking { get; internal set; }
public Target target;
@@ -123,6 +123,9 @@ namespace OpenRA.Mods.RA
if (w.Info.Range * w.Info.Range * Game.CellSize * Game.CellSize
< (target.CenterLocation - self.CenterLocation).LengthSquared) return false;
if (w.Info.MinRange * w.Info.MinRange * Game.CellSize * Game.CellSize >
(target.CenterLocation - self.CenterLocation).LengthSquared) return false;
if (!w.IsValidAgainst(target)) return false;
@@ -176,48 +179,23 @@ namespace OpenRA.Mods.RA
return info.FireDelay;
}
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
bool IsHeal { get { return Weapons[ 0 ].Info.Warheads[ 0 ].Damage < 0; } }
public IEnumerable<IOrderTargeter> Orders
{
return mi.Modifiers.HasModifier(Modifiers.Ctrl) ? 1000 : 1;
get { yield return new AttackOrderTargeter( "Attack", 6, IsHeal ); }
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if (mi.Button == MouseButton.Left) return null;
if (self == underCursor) return null;
var target = underCursor == null ? Target.FromCell(xy) : Target.FromActor(underCursor);
var isHeal = Weapons[0].Info.Warheads[0].Damage < 0;
var forceFire = mi.Modifiers.HasModifier(Modifiers.Ctrl);
if (isHeal)
if( order is AttackOrderTargeter )
{
// we can never "heal ground"; that makes no sense.
if (!target.IsActor) return null;
// unless forced, only heal allies.
if (self.Owner.Stances[underCursor.Owner] != Stance.Ally && !forceFire) return null;
// don't allow healing of fully-healed stuff!
if (underCursor.GetDamageState() == DamageState.Undamaged) return null;
if( target.IsActor )
return new Order( IsHeal ? "Heal" : "Attack", self, target.Actor );
else
return new Order( IsHeal ? "Heal" : "Attack", self, Util.CellContaining( target.CenterLocation ) );
}
else
{
if (!target.IsActor)
{
if (!forceFire) return null;
if (!self.Info.Traits.Get<AttackBaseInfo>().CanAttackGround) return null;
return new Order("Attack", self, xy);
}
if ((self.Owner.Stances[underCursor.Owner] != Stance.Enemy) && !forceFire)
return null;
}
if (!HasAnyValidWeapons(target)) return null;
return new Order(isHeal ? "Heal" : "Attack", self, underCursor);
return null;
}
public void ResolveOrder(Actor self, Order order)
@@ -248,16 +226,6 @@ namespace OpenRA.Mods.RA
self.Trait<Turreted>().desiredFacing = null;
}
}
public string CursorForOrder(Actor self, Order order)
{
switch (order.OrderString)
{
case "Attack": return "attack";
case "Heal": return "heal";
default: return null;
}
}
public string VoicePhraseForOrder(Actor self, Order order)
{
@@ -279,5 +247,48 @@ namespace OpenRA.Mods.RA
public float GetMaximumRange() { return Weapons.Max(w => w.Info.Range); }
public Weapon ChooseWeaponForTarget(Target t) { return Weapons.FirstOrDefault(w => w.IsValidAgainst(t)); }
class AttackOrderTargeter : IOrderTargeter
{
readonly bool isHeal;
public AttackOrderTargeter( string order, int priority, bool isHeal )
{
this.OrderID = order;
this.OrderPriority = priority;
this.isHeal = isHeal;
}
public string OrderID { get; private set; }
public int OrderPriority { get; private set; }
public bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
{
cursor = isHeal ? "heal" : "attack";
if( self == target ) return false;
if( !self.Trait<AttackBase>().HasAnyValidWeapons( Target.FromActor( target ) ) ) return false;
var playerRelationship = self.Owner.Stances[ target.Owner ];
if( isHeal )
return playerRelationship == Stance.Ally || forceAttack;
else
return playerRelationship == Stance.Enemy || forceAttack;
}
public bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor )
{
cursor = isHeal ? "heal" : "attack";
if( isHeal ) return false;
if( !self.Trait<AttackBase>().HasAnyValidWeapons( Target.FromCell( location ) ) ) return false;
if( forceAttack )
if( self.Info.Traits.Get<AttackBaseInfo>().CanAttackGround )
return true;
return false;
}
}
}
}

View File

@@ -24,6 +24,9 @@ namespace OpenRA.Mods.RA
protected override void QueueAttack(Actor self, Order order)
{
if (self.Trait<Aircraft>().Altitude == 0)
return; // dont fire while landed
target = Target.FromOrder(order);
self.QueueActivity(new FlyAttack(target));
}

View File

@@ -61,7 +61,7 @@ namespace OpenRA.Mods.RA
return inRange
.Where(a => a != self && self.Owner.Stances[a.Owner] == Stance.Ally)
.Where(a => !a.IsDead())
.Where(a => a.IsInWorld && !a.IsDead())
.Where(a => a.HasTrait<Health>() && a.GetDamageState() > DamageState.Undamaged)
.Where(a => attack.HasAnyValidWeapons(Target.FromActor(a)))
.OrderBy(a => (a.Location - self.Location).LengthSquared)

View File

@@ -139,7 +139,7 @@ namespace OpenRA.Mods.RA
bool IsIntact(Bridge b)
{
return b != null && b.self.IsInWorld && !b.self.IsDead();
return b != null && !b.self.IsDead();
}
void KillUnitsOnBridge()

View File

@@ -8,11 +8,14 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using OpenRA.Effects;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Orders;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
using System.Drawing;
namespace OpenRA.Mods.RA
{
@@ -21,21 +24,19 @@ namespace OpenRA.Mods.RA
public readonly float C4Delay = 0;
}
class C4Demolition : IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice
class C4Demolition : IIssueOrder, IResolveOrder, IOrderVoice
{
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
return mi.Modifiers.HasModifier(Modifiers.Ctrl) ? 1001 : 1;
get { yield return new UnitTraitOrderTargeter<Building>( "C4", 6, "c4", true, false ); }
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button != MouseButton.Right) return null;
if (underCursor == null) return null;
if (!underCursor.HasTrait<Building>()) return null;
if (self.Owner.Stances[underCursor.Owner] != Stance.Enemy && !mi.Modifiers.HasModifier(Modifiers.Ctrl)) return null;
return new Order("C4", self, underCursor);
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if( order.OrderID == "C4" )
return new Order( "C4", self, target.Actor );
return null;
}
public void ResolveOrder(Actor self, Order order)
@@ -58,11 +59,6 @@ namespace OpenRA.Mods.RA
}
}
public string CursorForOrder(Actor self, Order order)
{
return (order.OrderString == "C4") ? "c4" : null;
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "C4") ? "Attack" : null;

View File

@@ -12,38 +12,46 @@ using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.RA.Activities;
using OpenRA.Traits;
using OpenRA.Mods.RA.Orders;
namespace OpenRA.Mods.RA
{
public class CargoInfo : TraitInfo<Cargo>
public class CargoInfo : ITraitInfo
{
public readonly int Passengers = 0;
public readonly string[] Types = { };
public readonly int UnloadFacing = 0;
public object Create( ActorInitializer init ) { return new Cargo( init.self ); }
}
public class Cargo : IPips, IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice, INotifyDamage
public class Cargo : IPips, IIssueOrder, IResolveOrder, IOrderVoice, INotifyDamage
{
readonly Actor self;
List<Actor> cargo = new List<Actor>();
public IEnumerable<Actor> Passengers { get { return cargo; } }
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public Cargo( Actor self )
{
return 5;
this.self = self;
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button == MouseButton.Right && underCursor == self)
return new Order("Unload", self);
if (mi.Button == MouseButton.Right && underCursor != null && underCursor.Owner == self.Owner)
public IEnumerable<IOrderTargeter> Orders
{
get
{
var pi = underCursor.Info.Traits.GetOrDefault<PassengerInfo>();
var ci = self.Info.Traits.Get<CargoInfo>();
if (pi != null && ci.Types.Contains(pi.CargoType))
return new Order("EnterTransport", underCursor, self);
yield return new DeployOrderTargeter( "Unload", 10, () => CanUnload( self ) );
yield return new UnitTraitOrderTargeter<Passenger>( "ReverseEnterTransport", 6, null, false, true );
}
}
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if( order.OrderID == "Unload" )
return new Order( order.OrderID, self );
if( order.OrderID == "ReverseEnterTransport" )
return new Order( order.OrderID, self, target.Actor );
return null;
}
@@ -58,6 +66,15 @@ namespace OpenRA.Mods.RA
self.CancelActivity();
self.QueueActivity(new UnloadCargo());
}
if( order.OrderString == "ReverseEnterTransport" )
{
if( order.TargetActor != null && order.Subject.Owner == order.TargetActor.Owner )
{
var passenger = order.TargetActor.Trait<Passenger>();
passenger.ResolveOrder( order.TargetActor, new Order( "EnterTransport", order.TargetActor, self ) );
}
}
}
bool CanUnload(Actor self)
@@ -74,7 +91,6 @@ namespace OpenRA.Mods.RA
return true;
}
public string CursorForOrder(Actor self, Order order)
{
if (order.OrderString != "Unload") return null;

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA
public readonly int ChargeTime = 120; // Seconds
}
class ChronoshiftDeploy : IIssueOrder, IResolveOrder, ITick, IPips, IOrderCursor, IOrderVoice
class ChronoshiftDeploy : IIssueOrder, IResolveOrder, ITick, IPips, IOrderVoice
{
// Recharge logic
[Sync]
@@ -33,29 +33,22 @@ namespace OpenRA.Mods.RA
chargeTick--;
}
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
return 5;
get { yield return new DeployOrderTargeter( "ChronoshiftDeploy", 5, () => chargeTick <= 0 ); }
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if (mi.Button == MouseButton.Right && xy == self.Location)
return new Order("ChronoshiftDeploy", self);
if( order.OrderID == "ChronoshiftDeploy" )
if (chargeTick <= 0)
self.World.OrderGenerator = new SetChronoTankDestination( self );
return null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "ChronoshiftDeploy")
{
if (chargeTick <= 0)
if (self.Owner == self.World.LocalPlayer)
self.World.OrderGenerator = new SetChronoTankDestination(self);
return;
}
var movement = self.TraitOrDefault<IMove>();
if (order.OrderString == "ChronoshiftSelf" && movement.CanEnterCell(order.TargetLocation))
{
@@ -76,14 +69,6 @@ namespace OpenRA.Mods.RA
}
}
public string CursorForOrder(Actor self, Order order)
{
if (order.OrderString != "ChronoshiftDeploy")
return null;
return (chargeTick <= 0) ? "deploy" : "deploy-blocked";
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "ChronoshiftDeploy" && chargeTick <= 0) ? "Move" : null;

View File

@@ -62,7 +62,11 @@ namespace OpenRA.Mods.RA
smudgeCells = smudgeCells.Except(world.FindTilesInCircle(targetTile, warhead.Size[1])) ;
foreach (var sc in smudgeCells)
{
smudgeLayer.AddSmudge(sc);
if (warhead.Ore)
world.WorldActor.Trait<ResourceLayer>().Destroy(sc);
}
}
else
smudgeLayer.AddSmudge(targetTile);

View File

@@ -58,7 +58,7 @@ namespace OpenRA.Mods.RA
public void OnCrush(Actor crusher)
{
var shares = self.TraitsImplementing<CrateAction>().Select(
a => Pair.New(a, a.GetSelectionShares(crusher)));
a => Pair.New(a, a.GetSelectionSharesOuter(crusher)));
var totalShares = shares.Sum(a => a.Second);
var n = self.World.SharedRandom.Next(totalShares);

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System.Linq;
using OpenRA.Mods.RA.Effects;
using OpenRA.Traits;
@@ -18,6 +19,7 @@ namespace OpenRA.Mods.RA
public int SelectionShares = 10;
public string Effect = null;
public string Notification = null;
public string[] ExcludedActorTypes = { };
public virtual object Create(ActorInitializer init) { return new CrateAction(init.self, this); }
}
@@ -32,6 +34,14 @@ namespace OpenRA.Mods.RA
this.self = self;
this.info = info;
}
public int GetSelectionSharesOuter(Actor collector)
{
if (info.ExcludedActorTypes.Contains(collector.Info.Name))
return 0;
return GetSelectionShares(collector);
}
public virtual int GetSelectionShares(Actor collector)
{

View File

@@ -10,7 +10,8 @@
using System.Linq;
using OpenRA.Network;
using OpenRA.Traits;
using OpenRA.Traits;
using OpenRA.Widgets.Delegates;
namespace OpenRA.Mods.RA
{
@@ -47,6 +48,14 @@ namespace OpenRA.Mods.RA
else if (slot.Bot != null)
{
/* spawn a bot in this slot, "owned" by the host */
/* pick a random color for the bot */
var hue = (float)w.SharedRandom.NextDouble();
w.Map.Players[slot.MapPlayer].Color = LobbyDelegate.ColorFromHSL(hue, 1.0f, 0.7f);
w.Map.Players[slot.MapPlayer].Color2 = LobbyDelegate.ColorFromHSL(hue, 1.0f, 0.2f);
/* todo: pick a random name from the pool */
var player = new Player(w, w.Map.Players[slot.MapPlayer], playerIndex++);
w.AddPlayer(player);
@@ -54,6 +63,9 @@ namespace OpenRA.Mods.RA
if (Game.IsHost)
foreach (var bot in player.PlayerActor.TraitsImplementing<IBot>())
bot.Activate(player);
/* a bit of a hack */
player.IsBot = true;
}
}
@@ -77,6 +89,9 @@ namespace OpenRA.Mods.RA
// Hack: All map players are neutral wrt everyone else
if (p.Index < 0 || q.Index < 0) return Stance.Neutral;
if (p.IsBot ^ q.IsBot)
return Stance.Enemy; // bots and humans hate each other
var pc = GetClientForPlayer(p);
var qc = GetClientForPlayer(q);

View File

@@ -41,8 +41,9 @@ namespace OpenRA.Mods.RA.Effects
public IEnumerable<Renderable> Render()
{
yield return new Renderable(anim.Image,
a.CenterLocation - .5f * anim.Image.size + offset, "effect", (int)a.CenterLocation.Y);
if (a.IsInWorld)
yield return new Renderable(anim.Image,
a.CenterLocation - .5f * anim.Image.size + offset, "effect", (int)a.CenterLocation.Y);
}
}
}

View File

@@ -52,6 +52,9 @@ namespace OpenRA.Mods.RA.Effects
if (!doneDamage)
{
if (args.target.IsValid)
args.dest = args.target.CenterLocation.ToInt2();
Combat.DoImpacts(args);
doneDamage = true;
}

View File

@@ -13,32 +13,31 @@ using OpenRA.Effects;
using OpenRA.Mods.RA.Activities;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
using OpenRA.Mods.RA.Orders;
using System.Collections.Generic;
namespace OpenRA.Mods.RA
{
class EngineerCaptureInfo : TraitInfo<EngineerCapture> {}
class EngineerCapture : IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice
class EngineerCapture : IIssueOrder, IResolveOrder, IOrderVoice
{
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
return 5;
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button != MouseButton.Right) return null;
if (underCursor == null) return null;
if (self.Owner.Stances[underCursor.Owner] == Stance.Ally) return null;
if (!underCursor.HasTrait<Building>() || !underCursor.Info.Traits.Get<BuildingInfo>().Capturable) return null;
return new Order("CaptureBuilding", self, underCursor);
get
{
yield return new EnterOrderTargeter<Building>( "CaptureBuilding", 5, true, false,
_ => true, target => target.Info.Traits.Get<BuildingInfo>().Capturable );
}
}
public string CursorForOrder(Actor self, Order order)
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
return (order.OrderString == "CaptureBuilding") ? "capture" : null;
if( order.OrderID == "CaptureBuilding" )
return new Order( order.OrderID, self, target.Actor );
return null;
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "CaptureBuilding") ? "Attack" : null;

View File

@@ -6,47 +6,35 @@
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System.Drawing;
using OpenRA.Effects;
using OpenRA.Mods.RA.Activities;
using OpenRA.Traits;
#endregion
using System.Collections.Generic;
using System.Drawing;
using OpenRA.Effects;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Orders;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
namespace OpenRA.Mods.RA
{
class EngineerRepairInfo : TraitInfo<EngineerRepair> {}
class EngineerRepair : IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice
class EngineerRepair : IIssueOrder, IResolveOrder, IOrderVoice
{
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
return 5;
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button != MouseButton.Right) return null;
if (underCursor == null) return null;
if (!CanRepair(self, underCursor)) return null;
return new Order("EngineerRepair", self, underCursor);
}
bool CanRepair(Actor self, Actor a)
{
if (!a.HasTrait<Building>()) return false;
return (self.Owner.Stances[a.Owner] == Stance.Ally);
}
public string CursorForOrder(Actor self, Order order)
{
if (order.OrderString != "EngineerRepair") return null;
if (order.TargetActor == null) return null;
return (order.TargetActor.GetDamageState() == DamageState.Undamaged) ? "goldwrench-blocked" : "goldwrench";
}
public IEnumerable<IOrderTargeter> Orders
{
get { yield return new EngineerRepairOrderTargeter(); }
}
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if( order.OrderID == "EngineerRepair" )
return new Order( order.OrderID, self, target.Actor );
return null;
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "EngineerRepair"
@@ -71,6 +59,23 @@ namespace OpenRA.Mods.RA
self.QueueActivity(new Move(order.TargetActor.Location, order.TargetActor));
self.QueueActivity(new RepairBuilding(order.TargetActor));
}
}
}
class EngineerRepairOrderTargeter : UnitTraitOrderTargeter<Building>
{
public EngineerRepairOrderTargeter()
: base( "EngineerRepair", 6, "goldwrench", false, true )
{
}
public override bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
{
if( !base.CanTargetUnit( self, target, forceAttack, forceMove, ref cursor ) ) return false;
if( target.GetDamageState() > DamageState.Undamaged )
cursor = "goldwrench-blocked";
return true;
}
}
}
}

View File

@@ -0,0 +1,57 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
class FallsToEarthInfo : TraitInfo<FallsToEarth> { }
class FallsToEarth : INotifyDamage
{
public void Damaged(Actor self, AttackInfo e)
{
if (self.IsDead())
{
self.Trait<Health>().RemoveOnDeath = false;
self.CancelActivity();
self.QueueActivity(new FallToEarth());
}
}
}
class FallToEarth : CancelableActivity
{
int acceleration = 0;
int spin = 0;
public override IActivity Tick(Actor self)
{
if (acceleration == 0)
acceleration = self.World.SharedRandom.Next(2) * 2 - 1;
var aircraft = self.Trait<Aircraft>();
if (aircraft.Altitude <= 0)
{
self.Destroy();
return null;
}
spin += acceleration;
aircraft.Facing = (aircraft.Facing + spin) % 256;
aircraft.Altitude--;
return this;
}
protected override bool OnCancel() { return false; }
}
}

View File

@@ -1,8 +1,17 @@
using System.Linq;
using OpenRA.Traits;
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
using XRandom = OpenRA.Thirdparty.Random;
@@ -36,18 +45,37 @@ namespace OpenRA.Mods.RA
int2 baseCenter;
XRandom random = new XRandom(); //we do not use the synced random number generator.
World world { get { return p.PlayerActor.World; } }
Dictionary<string, float> unitsToBuild = new Dictionary<string, float>
{
{"e1", .0f},
{"e2", .0f},
{"e3", .0f},
{"1tnk", .0f},
{"2tnk", .0f},
{"3tnk", .0f}
};
Dictionary<string, float> buildingFractions = new Dictionary<string, float>
{
{ "proc", .2f },
{ "barr", .05f },
{ "tent", .05f },
{ "weap", .05f },
{ "pbox", .05f },
{ "hbox", .05f },
{ "gun", .05f },
{ "tsla", .05f },
{ "ftur", .05f },
{ "agun", .01f },
{ "sam", .01f },
{ "atek", .01f },
{ "stek", .01f },
{ "silo", .05f },
{ "fix", .01f },
{ "hpad", .01f },
{ "afld", .01f },
//{ "hpad", .01f },
//{ "afld", .01f },
{ "dome", .01f },
};
@@ -62,7 +90,8 @@ namespace OpenRA.Mods.RA
const int MaxBaseDistance = 15;
BuildState state = BuildState.WaitForFeedback;
BuildState bstate = BuildState.WaitForFeedback;
BuildState dstate = BuildState.WaitForFeedback;
/* called by the host's player creation code */
public void Activate(Player p)
@@ -86,11 +115,18 @@ namespace OpenRA.Mods.RA
return buildableThings.ElementAtOrDefault(random.Next(buildableThings.Count()));
}
bool HasAdequatePower()
{
/* note: CNC `fact` provides a small amount of power. don't get jammed because of that. */
return playerPower.PowerProvided > 50 &&
playerPower.PowerProvided > playerPower.PowerDrained * 1.2;
}
ActorInfo ChooseBuildingToBuild(ProductionQueue queue)
{
var buildableThings = queue.BuildableItems();
if (playerPower.PowerProvided <= playerPower.PowerDrained * 1.2) /* try to maintain 20% excess power */
if (!HasAdequatePower()) /* try to maintain 20% excess power */
{
/* find the best thing we can build which produces power */
var best = buildableThings.Where(a => GetPowerProvidedBy(a) > 0)
@@ -107,7 +143,8 @@ namespace OpenRA.Mods.RA
var myBuildings = p.World.Queries.OwnedBy[p].WithTrait<Building>()
.Select( a => a.Actor.Info.Name ).ToArray();
foreach (var frac in buildingFractions)
if (buildableThings.Any(b => b.Name == frac.Key))
if (myBuildings.Count(a => a == frac.Key) < frac.Value * myBuildings.Length)
@@ -116,14 +153,29 @@ namespace OpenRA.Mods.RA
return null;
}
ActorInfo ChooseDefenseToBuild(ProductionQueue queue)
{
var buildableThings = queue.BuildableItems();
var myBuildings = p.World.Queries.OwnedBy[p].WithTrait<Building>()
.Select(a => a.Actor.Info.Name).ToArray();
foreach (var frac in buildingFractions)
if (buildableThings.Any(b => b.Name == frac.Key))
if (myBuildings.Count(a => a == frac.Key) < frac.Value * myBuildings.Length)
return Rules.Info[frac.Key];
return null;
}
int2? ChooseBuildLocation(ProductionItem item)
{
var bi = Rules.Info[item.Item].Traits.Get<BuildingInfo>();
for (var k = 0; k < MaxBaseDistance; k++)
foreach (var t in Game.world.FindTilesInCircle(baseCenter, k))
if (Game.world.CanPlaceBuilding(item.Item, bi, t, null))
if (Game.world.IsCloseEnoughToBase(p, item.Item, bi, t))
foreach (var t in world.FindTilesInCircle(baseCenter, k))
if (world.CanPlaceBuilding(item.Item, bi, t, null))
if (world.IsCloseEnoughToBase(p, item.Item, bi, t))
return t;
return null; // i don't know where to put it.
@@ -158,7 +210,7 @@ namespace OpenRA.Mods.RA
BuildBuildings();
//build Defense
BuildDefense();
//build Ship
}
@@ -173,7 +225,29 @@ namespace OpenRA.Mods.RA
//This is purely to identify production buildings that don't have a rally point set.
List<Actor> activeProductionBuildings = new List<Actor>();
private void AssignRolesToIdleUnits(Actor self)
bool IsHumanPlayer(Player p)
{
/* hack hack: this actually detects 'is not HackyAI' -- possibly actually a good thing. */
var hackyAI = p.PlayerActor.Trait<HackyAI>();
return !hackyAI.enabled;
}
int2? ChooseEnemyTarget()
{
// Criteria for picking an enemy:
// 1. not ourself.
// 2. human.
// 3. not dead.
var possibleTargets = world.WorldActor.Trait<MPStartLocations>().Start
.Where(kv => kv.Key != p && IsHumanPlayer(kv.Key)
&& p.WinState == WinState.Undefined)
.Select(kv => kv.Value);
return possibleTargets.Any() ? possibleTargets.Random(random) : (int2?) null;
}
void AssignRolesToIdleUnits(Actor self)
{
//HACK: trim these lists -- we really shouldn't be hanging onto all this state
//when it's invalidated so easily, but that's Matthew/Alli's problem.
@@ -196,17 +270,18 @@ namespace OpenRA.Mods.RA
/* Create an attack force when we have enough units around our base. */
// (don't bother leaving any behind for defense.)
if (unitsHangingAroundTheBase.Count > 5)
if (unitsHangingAroundTheBase.Count > 8)
{
Game.Debug("Launch an attack.");
int2 attackTarget = Game.world.WorldActor.Trait<MPStartLocations>().Start
.Where(kv => kv.Key != p)
.Select(kv => kv.Value)
.Random(random);
foreach (var a in unitsHangingAroundTheBase)
if (TryToMove(a, attackTarget))
var attackTarget = ChooseEnemyTarget();
if (attackTarget == null)
return;
foreach (var a in unitsHangingAroundTheBase)
if (TryToMove(a, attackTarget.Value))
attackForce.Add(a);
unitsHangingAroundTheBase.Clear();
}
}
@@ -215,12 +290,15 @@ namespace OpenRA.Mods.RA
{
var newProdBuildings = self.World.Queries.OwnedBy[p]
.Where(a => (a.TraitOrDefault<RallyPoint>() != null
&& !activeProductionBuildings.Contains(a))).ToArray();
&& !activeProductionBuildings.Contains(a)
)).ToArray();
foreach (var a in newProdBuildings)
{
activeProductionBuildings.Add(a);
int2 newRallyPoint = ChooseRallyLocationNear(a.Location);
newRallyPoint.X += 4;
newRallyPoint.Y += 4;
Game.IssueOrder(new Order("SetRallyPoint", a, newRallyPoint));
}
}
@@ -228,10 +306,11 @@ namespace OpenRA.Mods.RA
//won't work for shipyards...
private int2 ChooseRallyLocationNear(int2 startPos)
{
foreach (var t in Game.world.FindTilesInCircle(startPos, 6))
if (Game.world.IsCellBuildable(t, false) && t != startPos)
Random r = new Random();
foreach (var t in world.FindTilesInCircle(startPos, 8))
if (world.IsCellBuildable(t, false) && t != startPos && r.Next(64) == 0)
return t;
return startPos; // i don't know where to put it.
}
@@ -276,7 +355,7 @@ namespace OpenRA.Mods.RA
private void BuildRandom(string category)
{
// Pick a free queue
var queue = Game.world.Queries.WithTraitMultiple<ProductionQueue>()
var queue = world.Queries.WithTraitMultiple<ProductionQueue>()
.Where(a => a.Actor.Owner == p &&
a.Trait.Info.Type == category &&
a.Trait.CurrentItem() == null)
@@ -287,17 +366,30 @@ namespace OpenRA.Mods.RA
return;
var unit = ChooseRandomUnitToBuild(queue);
if (unit != null)
{
Game.IssueOrder(Order.StartProduction(queue.self, unit.Name, 1));
}
Boolean found = false;
if (unit != null)
{
foreach (var un in unitsToBuild)
{
if (un.Key == unit.Name)
{
found = true;
break;
}
}
if (found == true)
{
Game.IssueOrder(Order.StartProduction(queue.self, unit.Name, 1));
}
}
}
private void BuildBuildings()
{
// Pick a free queue
var queue = Game.world.Queries.WithTraitMultiple<ProductionQueue>()
.Where(a => a.Actor.Owner == p && a.Trait.Info.Type == "Building")
var queue = world.Queries.WithTraitMultiple<ProductionQueue>()
.Where(a => a.Actor.Owner == p && a.Trait.Info.Type == "Building")
.Select(a => a.Trait)
.FirstOrDefault();
@@ -305,19 +397,20 @@ namespace OpenRA.Mods.RA
return;
var currentBuilding = queue.CurrentItem();
switch (state)
switch (bstate)
{
case BuildState.ChooseItem:
{
var item = ChooseBuildingToBuild(queue);
if (item == null)
{
state = BuildState.WaitForFeedback;
bstate = BuildState.WaitForFeedback;
lastThinkTick = ticks;
}
else
{
state = BuildState.WaitForProduction;
Game.Debug("AI: Starting production of {0}".F(item.Name));
bstate = BuildState.WaitForProduction;
Game.IssueOrder(Order.StartProduction(queue.self, item.Name, 1));
}
}
@@ -330,7 +423,7 @@ namespace OpenRA.Mods.RA
Game.IssueOrder(Order.PauseProduction(queue.self, currentBuilding.Item, false));
else if (currentBuilding.Done)
{
state = BuildState.WaitForFeedback;
bstate = BuildState.WaitForFeedback;
lastThinkTick = ticks;
/* place the building */
@@ -338,7 +431,7 @@ namespace OpenRA.Mods.RA
if (location == null)
{
Game.Debug("AI: Nowhere to place {0}".F(currentBuilding.Item));
Game.IssueOrder(Order.CancelProduction(queue.self, currentBuilding.Item));
Game.IssueOrder(Order.CancelProduction(queue.self, currentBuilding.Item, 1));
}
else
{
@@ -349,7 +442,69 @@ namespace OpenRA.Mods.RA
case BuildState.WaitForFeedback:
if (ticks - lastThinkTick > feedbackTime)
state = BuildState.ChooseItem;
bstate = BuildState.ChooseItem;
break;
}
}
private void BuildDefense()
{
// Pick a free queue
var queue = world.Queries.WithTraitMultiple<ProductionQueue>()
.Where(a => a.Actor.Owner == p && a.Trait.Info.Type == "Defense")
.Select(a => a.Trait)
.FirstOrDefault();
if (queue == null)
return;
var currentBuilding = queue.CurrentItem();
switch (dstate)
{
case BuildState.ChooseItem:
{
var item = ChooseDefenseToBuild(queue);
if (item == null)
{
dstate = BuildState.WaitForFeedback;
lastThinkTick = ticks;
}
else
{
Game.Debug("AI: Starting production of {0}".F(item.Name));
dstate = BuildState.WaitForProduction;
Game.IssueOrder(Order.StartProduction(queue.self, item.Name, 1));
}
}
break;
case BuildState.WaitForProduction:
if (currentBuilding == null) return; /* let it happen.. */
else if (currentBuilding.Paused)
Game.IssueOrder(Order.PauseProduction(queue.self, currentBuilding.Item, false));
else if (currentBuilding.Done)
{
dstate = BuildState.WaitForFeedback;
lastThinkTick = ticks;
/* place the building */
var location = ChooseBuildLocation(currentBuilding);
if (location == null)
{
Game.Debug("AI: Nowhere to place {0}".F(currentBuilding.Item));
Game.IssueOrder(Order.CancelProduction(queue.self, currentBuilding.Item, 1));
}
else
{
Game.IssueOrder(new Order("PlaceBuilding", p.PlayerActor, location.Value, currentBuilding.Item));
}
}
break;
case BuildState.WaitForFeedback:
if (ticks - lastThinkTick > feedbackTime)
dstate = BuildState.ChooseItem;
break;
}
}

View File

@@ -14,7 +14,8 @@ using System.Linq;
using OpenRA.Effects;
using OpenRA.Mods.RA.Activities;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
using OpenRA.Traits.Activities;
using OpenRA.Mods.RA.Orders;
namespace OpenRA.Mods.RA
{
@@ -30,7 +31,7 @@ namespace OpenRA.Mods.RA
}
public class Harvester : IIssueOrder, IResolveOrder, INotifyDamage, IPips,
IRenderModifier, IExplodeModifier, IOrderCursor, IOrderVoice,
IRenderModifier, IExplodeModifier, IOrderVoice,
ISpeedModifier
{
Dictionary<ResourceTypeInfo, int> contents = new Dictionary<ResourceTypeInfo, int>();
@@ -93,40 +94,26 @@ namespace OpenRA.Mods.RA
contents.Clear();
}
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
return 5;
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button == MouseButton.Left) return null;
// Don't leak info about resources under the shroud
if (!self.World.LocalPlayer.Shroud.IsExplored(xy)) return null;
if (underCursor != null
&& self.Owner.Stances[ underCursor.Owner ] == Stance.Ally
&& underCursor.HasTrait<IAcceptOre>())
{
return new Order("Deliver", self, underCursor);
}
var res = self.World.WorldActor.Trait<ResourceLayer>().GetResource(xy);
var info = self.Info.Traits.Get<HarvesterInfo>();
if (underCursor == null && res != null && info.Resources.Contains(res.info.Name) && !IsFull)
return new Order("Harvest", self, xy);
return null;
}
public string CursorForOrder(Actor self, Order order)
{
if (order.OrderString == "Harvest") return "attackmove";
if (order.OrderString == "Deliver") return IsEmpty ? "enter-blocked" : "enter";
return null;
get
{
yield return new EnterOrderTargeter<IAcceptOre>( "Deliver", 5, false, true, _ => true, _ => !IsEmpty );
yield return new HarvestOrderTargeter();
}
}
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if( order.OrderID == "Deliver" )
return new Order( order.OrderID, self, target.Actor );
if( order.OrderID == "Harvest" )
return new Order( order.OrderID, self, Util.CellContaining( target.CenterLocation ) );
return null;
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "Harvest" || (order.OrderString == "Deliver" && !IsEmpty)) ? "Move" : null;
@@ -153,7 +140,7 @@ namespace OpenRA.Mods.RA
{
if (order.TargetActor != LinkedProc)
{
if (LinkedProc != null)
if (LinkedProc != null && LinkedProc.IsInWorld)
LinkedProc.TraitsImplementing<IAcceptOre>().FirstOrDefault().UnlinkHarvester(LinkedProc,self);
LinkedProc = order.TargetActor;
LinkedProc.TraitsImplementing<IAcceptOre>().FirstOrDefault().LinkHarvester(LinkedProc,self);
@@ -222,5 +209,30 @@ namespace OpenRA.Mods.RA
return float2.Lerp(1f, Info.FullyLoadedSpeed,
contents.Values.Sum() / (float)Info.Capacity);
}
class HarvestOrderTargeter : IOrderTargeter
{
public string OrderID { get { return "Harvest";}}
public int OrderPriority { get { return 10; } }
public bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
{
return false;
}
public bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor )
{
// Don't leak info about resources under the shroud
if( !self.World.LocalPlayer.Shroud.IsExplored( location ) ) return false;
var res = self.World.WorldActor.Trait<ResourceLayer>().GetResource( location );
var info = self.Info.Traits.Get<HarvesterInfo>();
if( res == null ) return false;
if( !info.Resources.Contains( res.info.Name ) ) return false;
cursor = "attackmove";
return true;
}
}
}
}

View File

@@ -16,6 +16,7 @@ using OpenRA.Mods.RA.Activities;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
using System.Drawing;
using OpenRA.Mods.RA.Orders;
namespace OpenRA.Mods.RA
{
@@ -29,7 +30,7 @@ namespace OpenRA.Mods.RA
public override object Create( ActorInitializer init ) { return new Helicopter( init, this); }
}
class Helicopter : Aircraft, ITick, IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice
class Helicopter : Aircraft, ITick, IIssueOrder, IResolveOrder, IOrderVoice
{
public IDisposable reservation;
HelicopterInfo Info;
@@ -39,32 +40,25 @@ namespace OpenRA.Mods.RA
Info = info;
}
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
// Force move takes precidence
return mi.Modifiers.HasModifier(Modifiers.Alt) ? int.MaxValue : 0;
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button == MouseButton.Left) return null;
get
{
yield return new EnterOrderTargeter<Building>( "Enter", 5, false, true,
target => AircraftCanEnter( target ), target => !Reservable.IsReserved( target ) );
if (underCursor != null && AircraftCanEnter(self, underCursor)
&& underCursor.Owner == self.Owner)
return new Order("Enter", self, underCursor);
if (self.TraitOrDefault<IMove>().CanEnterCell(xy))
return new Order("Move", self, xy);
return null;
yield return new AircraftMoveOrderTargeter();
}
}
public string CursorForOrder(Actor self, Order order)
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if (order.OrderString == "Move") return "move";
if (order.OrderString == "Enter")
return Reservable.IsReserved(order.TargetActor) ? "enter-blocked" : "enter";
if( order.OrderID == "Enter" )
return new Order( order.OrderID, self, target.Actor );
if( order.OrderID == "Move" )
return new Order( order.OrderID, self, Util.CellContaining( target.CenterLocation ) );
return null;
}

View File

@@ -26,44 +26,35 @@ namespace OpenRA.Mods.RA
public readonly string[] RearmBuildings = { "fix" };
}
class Minelayer : IIssueOrder, IResolveOrder, IOrderCursor, IPostRenderSelection
class Minelayer : IIssueOrder, IResolveOrder, IPostRenderSelection
{
/* [Sync] when sync can cope with arrays! */
public int2[] minefield = null;
[Sync] int2 minefieldStart;
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
return 5;
get { yield return new BeginMinefieldOrderTargeter(); }
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button == MouseButton.Right && underCursor == null && mi.Modifiers.HasModifier(Modifiers.Ctrl))
return new Order("BeginMinefield", self, xy);
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if( order is BeginMinefieldOrderTargeter )
{
var start = Util.CellContaining( target.CenterLocation );
self.World.OrderGenerator = new MinefieldOrderGenerator( self, start );
return new Order( "BeginMinefield", self, start );
}
return null;
}
public string CursorForOrder(Actor self, Order order)
{
return (order.OrderString == "BeginMinefield") ? "ability" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "BeginMinefield")
{
if( order.OrderString == "BeginMinefield" )
minefieldStart = order.TargetLocation;
if (self.Owner == self.World.LocalPlayer)
self.World.OrderGenerator = new MinefieldOrderGenerator(self);
}
if (order.OrderString == "PlaceMinefield")
{
if (self.Owner == self.World.LocalPlayer)
self.World.CancelInputMode();
var movement = self.Trait<IMove>();
minefield = GetMinefieldCells(minefieldStart, order.TargetLocation,
@@ -97,9 +88,10 @@ namespace OpenRA.Mods.RA
class MinefieldOrderGenerator : IOrderGenerator
{
Actor minelayer;
readonly Actor minelayer;
readonly int2 minefieldStart;
public MinefieldOrderGenerator(Actor self) { minelayer = self; }
public MinefieldOrderGenerator(Actor self, int2 xy ) { minelayer = self; minefieldStart = xy; }
public IEnumerable<Order> Order(World world, int2 xy, MouseInput mi)
{
@@ -110,26 +102,31 @@ namespace OpenRA.Mods.RA
}
var underCursor = world.FindUnitsAtMouse(mi.Location)
//.Where(a => a.Info.Traits.Contains<SelectableInfo>())
.OrderByDescending(a => a.Info.Traits.Contains<SelectableInfo>() ? a.Info.Traits.Get<SelectableInfo>().Priority : int.MinValue)
.OrderByDescending(a => a.Info.Traits.Contains<SelectableInfo>()
? a.Info.Traits.Get<SelectableInfo>().Priority : int.MinValue)
.FirstOrDefault();
if (mi.Button == MouseButton.Right && underCursor == null)
yield return new Order("PlaceMinefield", minelayer, xy);
if( mi.Button == MouseButton.Right && underCursor == null )
{
minelayer.World.CancelInputMode();
yield return new Order( "PlaceMinefield", minelayer, xy );
}
}
public void Tick(World world)
{
if (minelayer.IsDead() || !minelayer.IsInWorld)
if (!minelayer.IsInWorld || minelayer.IsDead())
world.CancelInputMode();
}
int2 lastMousePos;
public void RenderAfterWorld(World world)
{
var ml = minelayer.Trait<Minelayer>();
if (!minelayer.IsInWorld)
return;
var movement = minelayer.Trait<IMove>();
var minefield = GetMinefieldCells(ml.minefieldStart, lastMousePos, minelayer.Info.Traits.Get<MinelayerInfo>().MinefieldDepth)
var minefield = GetMinefieldCells(minefieldStart, lastMousePos, minelayer.Info.Traits.Get<MinelayerInfo>().MinefieldDepth)
.Where(p => movement.CanEnterCell(p)).ToArray();
world.WorldRenderer.DrawLocus(Color.Cyan, minefield);
@@ -148,5 +145,22 @@ namespace OpenRA.Mods.RA
if (minefield != null)
self.World.WorldRenderer.DrawLocus(Color.Cyan, minefield);
}
class BeginMinefieldOrderTargeter : IOrderTargeter
{
public string OrderID { get { return "BeginMinefield"; } }
public int OrderPriority { get { return 5; } }
public bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
{
return false;
}
public bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor )
{
cursor = "ability";
return ( actorsAtLocation.Count == 0 && forceAttack );
}
}
}
}

View File

@@ -91,9 +91,13 @@
<Compile Include="Effects\RepairIndicator.cs" />
<Compile Include="Effects\Smoke.cs" />
<Compile Include="Effects\TeslaZap.cs" />
<Compile Include="FallsToEarth.cs" />
<Compile Include="LimitedAmmo.cs" />
<Compile Include="MPStartLocations.cs" />
<Compile Include="Orders\DeployOrderTargeter.cs" />
<Compile Include="Orders\EnterBuildingOrderTargeter.cs" />
<Compile Include="Orders\PlaceBuildingOrderGenerator.cs" />
<Compile Include="Orders\UnitOrderTargeter.cs" />
<Compile Include="Player\ActorGroupProxy.cs" />
<Compile Include="Aircraft.cs" />
<Compile Include="Player\ClassicProductionQueue.cs" />
@@ -203,6 +207,7 @@
<Compile Include="SupportPowers\NukePower.cs" />
<Compile Include="SupportPowers\SupportPower.cs" />
<Compile Include="TakeCover.cs" />
<Compile Include="TargetableAircraft.cs" />
<Compile Include="TeslaInstantKills.cs" />
<Compile Include="Crates\ResetRadarCrateAction.cs" />
<Compile Include="ThrowsParticles.cs" />

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Orders
{
class DeployOrderTargeter : IOrderTargeter
{
readonly Func<bool> useDeployCursor;
public DeployOrderTargeter( string order, int priority )
: this( order, priority, () => true )
{
}
public DeployOrderTargeter( string order, int priority, Func<bool> useDeployCursor )
{
this.OrderID = order;
this.OrderPriority = priority;
this.useDeployCursor = useDeployCursor;
}
public string OrderID { get; private set; }
public int OrderPriority { get; private set; }
public bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
{
cursor = useDeployCursor() ? "deploy" : "deploy-blocked";
return self == target;
}
public bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor )
{
return false;
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Orders
{
class EnterOrderTargeter<T> : UnitTraitOrderTargeter<T>
{
readonly Func<Actor, bool> canTarget;
readonly Func<Actor, bool> useEnterCursor;
public EnterOrderTargeter( string order, int priority, bool targetEnemy, bool targetAlly,
Func<Actor, bool> canTarget, Func<Actor, bool> useEnterCursor )
: base( order, priority, "enter", targetEnemy, targetAlly )
{
this.canTarget = canTarget;
this.useEnterCursor = useEnterCursor;
}
public override bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
{
if( !base.CanTargetUnit( self, target, forceAttack, forceMove, ref cursor ) ) return false;
if( !canTarget( target ) ) return false;
cursor = useEnterCursor( target ) ? "enter" : "enter-blocked";
return true;
}
}
}

View File

@@ -32,7 +32,13 @@ namespace OpenRA.Mods.RA.Orders
if (mi.Button == MouseButton.Right)
world.CancelInputMode();
return InnerOrder(world, xy, mi);
var ret = InnerOrder( world, xy, mi ).ToList();
foreach( var order in ret )
{
world.CancelInputMode();
break;
}
return ret;
}
IEnumerable<Order> InnerOrder(World world, int2 xy, MouseInput mi)
@@ -58,14 +64,11 @@ namespace OpenRA.Mods.RA.Orders
public void Tick( World world )
{
// Find the queue with the target actor
var queue = Producer.TraitsImplementing<ProductionQueue>()
.Where(p => p.CurrentItem() != null &&
p.CurrentItem().Item == Building &&
p.CurrentItem().RemainingTime == 0)
.FirstOrDefault();
if (queue == null)
world.CancelInputMode();
//var queue = Producer.TraitsImplementing<ProductionQueue>()
// .Where(p => p.CurrentItem() != null &&
// p.CurrentItem().Item == Building &&
// p.CurrentItem().RemainingTime == 0)
// .FirstOrDefault();
}
public void RenderAfterWorld( World world ) {}

View File

@@ -0,0 +1,70 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Orders
{
class UnitOrderTargeter : IOrderTargeter
{
readonly string cursor;
readonly bool targetEnemyUnits, targetAllyUnits;
public UnitOrderTargeter( string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits )
{
this.OrderID = order;
this.OrderPriority = priority;
this.cursor = cursor;
this.targetEnemyUnits = targetEnemyUnits;
this.targetAllyUnits = targetAllyUnits;
}
public string OrderID { get; private set; }
public int OrderPriority { get; private set; }
public virtual bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
{
if( self == null ) throw new ArgumentNullException( "self" );
if( target == null ) throw new ArgumentNullException( "target" );
cursor = this.cursor;
var playerRelationship = self.Owner.Stances[ target.Owner ];
if( !forceAttack && playerRelationship == Stance.Ally && !targetAllyUnits ) return false;
if( !forceAttack && playerRelationship == Stance.Enemy && !targetEnemyUnits ) return false;
return true;
}
public virtual bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor )
{
return false;
}
}
class UnitTraitOrderTargeter<T> : UnitOrderTargeter
{
public UnitTraitOrderTargeter( string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits )
: base( order, priority, cursor, targetEnemyUnits, targetAllyUnits )
{
}
public override bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
{
if( !base.CanTargetUnit( self, target, forceAttack, forceMove, ref cursor ) ) return false;
if( !target.HasTrait<T>() ) return false;
return true;
}
}
}

View File

@@ -8,64 +8,64 @@
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Orders;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
using System.Linq;
namespace OpenRA.Mods.RA
{
class PassengerInfo : TraitInfo<Passenger>
class PassengerInfo : ITraitInfo
{
public readonly string CargoType = null;
public readonly PipType PipType = PipType.Green;
public object Create( ActorInitializer init ) { return new Passenger( init.self ); }
}
class Passenger : IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice
class Passenger : IIssueOrder, IResolveOrder, IOrderVoice
{
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
readonly Actor self;
public Passenger( Actor self ) { this.self = self; }
public IEnumerable<IOrderTargeter> Orders
{
return 5;
get
{
yield return new EnterOrderTargeter<Cargo>( "EnterTransport", 6, false, true,
target => IsCorrectCargoType( target ), target => CanEnter( target ) );
}
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button != MouseButton.Right)
return null;
if (underCursor == null || underCursor.Owner != self.Owner)
return null;
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if( order.OrderID == "EnterTransport" )
return new Order( order.OrderID, self, target.Actor );
var cargo = underCursor.TraitOrDefault<Cargo>();
if (cargo == null)
return null;
return null;
}
bool IsCorrectCargoType( Actor target )
{
var pi = self.Info.Traits.Get<PassengerInfo>();
var ci = underCursor.Info.Traits.Get<CargoInfo>();
if (!ci.Types.Contains(pi.CargoType))
return null;
return new Order("EnterTransport", self, underCursor);
}
bool CanEnter(Actor self, Actor a)
{
var cargo = a.TraitOrDefault<Cargo>();
return (cargo != null && !cargo.IsFull(a));
}
public string CursorForOrder(Actor self, Order order)
{
if (order.OrderString != "EnterTransport") return null;
return CanEnter(self, order.TargetActor) ? "enter" : "enter-blocked";
var ci = target.Info.Traits.Get<CargoInfo>();
return ci.Types.Contains( pi.CargoType );
}
bool CanEnter( Actor target )
{
var cargo = target.TraitOrDefault<Cargo>();
return (cargo != null && !cargo.IsFull(target));
}
public string VoicePhraseForOrder(Actor self, Order order)
{
if (order.OrderString != "EnterTransport" ||
!CanEnter(self, order.TargetActor)) return null;
!CanEnter(order.TargetActor)) return null;
return "Move";
}
@@ -73,7 +73,9 @@ namespace OpenRA.Mods.RA
{
if (order.OrderString == "EnterTransport")
{
if (!CanEnter(self, order.TargetActor)) return;
if (order.TargetActor == null) return;
if (!CanEnter(order.TargetActor)) return;
if (!IsCorrectCargoType(order.TargetActor)) return;
if (self.Owner == self.World.LocalPlayer)
self.World.AddFrameEndTask(w =>

View File

@@ -9,11 +9,13 @@
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Mods.RA.Activities;
using OpenRA.Traits;
using OpenRA.Mods.RA.Orders;
namespace OpenRA.Mods.RA
{
@@ -22,7 +24,7 @@ namespace OpenRA.Mods.RA
public override object Create( ActorInitializer init ) { return new Plane( init, this ); }
}
public class Plane : Aircraft, IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice, ITick
public class Plane : Aircraft, IIssueOrder, IResolveOrder, IOrderVoice, ITick
{
public IDisposable reservation;
@@ -46,31 +48,28 @@ namespace OpenRA.Mods.RA
}
}
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
// Force move takes precidence
return mi.Modifiers.HasModifier(Modifiers.Alt) ? int.MaxValue : 0;
get
{
yield return new EnterOrderTargeter<Building>( "Enter", 5, false, true,
target => AircraftCanEnter( target ), target => !Reservable.IsReserved( target ) );
yield return new AircraftMoveOrderTargeter();
}
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if (mi.Button == MouseButton.Left) return null;
if (underCursor != null && AircraftCanEnter(self, underCursor)
&& underCursor.Owner == self.Owner)
return new Order("Enter", self, underCursor);
return new Order("Move", self, xy);
}
public string CursorForOrder(Actor self, Order order)
{
if (order.OrderString == "Move") return "move";
if (order.OrderString == "Enter")
return Reservable.IsReserved(order.TargetActor) ? "enter-blocked" : "enter";
if( order.OrderID == "Enter" )
return new Order( order.OrderID, self, target.Actor );
if( order.OrderID == "Move" )
return new Order( order.OrderID, self, Util.CellContaining( target.CenterLocation ) );
return null;
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "Move" || order.OrderString == "Enter") ? "Move" : null;
@@ -134,4 +133,21 @@ namespace OpenRA.Mods.RA
}
}
}
class AircraftMoveOrderTargeter : IOrderTargeter
{
public string OrderID { get { return "Move"; } }
public int OrderPriority { get { return 4; } }
public bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
{
return false;
}
public bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor )
{
cursor = "move";
return true;
}
}
}

View File

@@ -55,7 +55,7 @@ namespace OpenRA.Mods.RA
if (producers.Count() == 0)
{
CancelProduction(name);
CancelProduction(name,1);
return;
}

View File

@@ -173,7 +173,7 @@ namespace OpenRA.Mods.RA
}
case "CancelProduction":
{
CancelProduction(order.TargetString);
CancelProduction(order.TargetString,order.TargetLocation.X);
break;
}
}
@@ -194,15 +194,26 @@ namespace OpenRA.Mods.RA
return (int) time;
}
protected void CancelProduction( string itemName )
{
protected void CancelProduction(string itemName, int numberToCancel)
{
if (Queue.Count == 0)
return; // Nothing to do here
var lastIndex = Queue.FindLastIndex( a => a.Item == itemName );
if (lastIndex > 0)
var lastIndex = Queue.FindLastIndex(a => a.Item == itemName);
while (lastIndex > 0)
{
Queue.RemoveAt(lastIndex);
else if( lastIndex == 0 )
lastIndex = Queue.FindLastIndex(a => a.Item == itemName);
if (numberToCancel > 0)
--numberToCancel;
if (numberToCancel == 0)
break;
//else negative, continue deleting all orders
}
if (lastIndex == 0)
{
var item = Queue[0];
self.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(item.TotalCost - item.RemainingCost); // refund what's been paid so far.
@@ -226,9 +237,16 @@ namespace OpenRA.Mods.RA
return a.TraitsImplementing<IDisable>().Any(d => d.Disabled);
}
// Builds a unit from the actor that holds this queue (1 queue per building)
protected virtual void BuildUnit( string name )
{
// queue lives on actor; is produced at same actor
// Cannot produce if i'm dead
if (!self.IsInWorld || self.IsDead())
{
CancelProduction(name, 1);
return;
}
var sp = self.TraitsImplementing<Production>().Where(p => p.Info.Produces.Contains(Info.Type)).FirstOrDefault();
if (sp != null && !IsDisabledBuilding(self) && sp.Produce(self, Rules.Info[ name ]))
FinishProduction();

View File

@@ -11,12 +11,13 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
using OpenRA.Mods.RA.Orders;
namespace OpenRA.Mods.RA
{
class PrimaryBuildingInfo : TraitInfo<PrimaryBuilding> { }
class PrimaryBuilding : IIssueOrder, IResolveOrder, IOrderCursor, ITags
class PrimaryBuilding : IIssueOrder, IResolveOrder, ITags
{
bool isPrimary = false;
public bool IsPrimary { get { return isPrimary; } }
@@ -26,23 +27,19 @@ namespace OpenRA.Mods.RA
yield return (isPrimary) ? TagType.Primary : TagType.None;
}
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
return 0;
get { yield return new DeployOrderTargeter( "PrimaryProducer", 1 ); }
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if (mi.Button == MouseButton.Right && underCursor == self)
return new Order("PrimaryProducer", self);
if( order.OrderID == "PrimaryProducer" )
return new Order( order.OrderID, self );
return null;
}
public string CursorForOrder(Actor self, Order order)
{
return (order.OrderString == "PrimaryProducer") ? "deploy" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "PrimaryProducer")

View File

@@ -43,15 +43,17 @@ namespace OpenRA.Mods.RA
anim.Image, Traits.Util.CenterOfCell(rallyPoint));
}
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
return 0;
get { yield return new RallyPointOrderTargeter(); }
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if (mi.Button == MouseButton.Left || underCursor != null) return null;
return new Order("SetRallyPoint", self, xy);
if( order.OrderID == "SetRallyPoint" )
return new Order( order.OrderID, self, Traits.Util.CellContaining( target.CenterLocation ) );
return null;
}
public void ResolveOrder( Actor self, Order order )
@@ -61,5 +63,21 @@ namespace OpenRA.Mods.RA
}
public void Tick(Actor self) { anim.Tick(); }
class RallyPointOrderTargeter : IOrderTargeter
{
public string OrderID { get { return "SetRallyPoint"; } }
public int OrderPriority { get { return 0; } }
public bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
{
return false;
}
public bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor )
{
return true;
}
}
}
}

View File

@@ -77,7 +77,6 @@ namespace OpenRA.Mods.RA.Render
public void PlayCustomAnimBackwards(Actor self, string name, Action a)
{
var hasSequence = anim.HasSequence(NormalizeSequence(self, name));
anim.PlayBackwardsThen(NormalizeSequence(self, name),
() => { anim.PlayRepeating(NormalizeSequence(self, "idle")); a(); });
}

View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
using OpenRA.Mods.RA.Orders;
namespace OpenRA.Mods.RA.Render
{
@@ -19,7 +20,7 @@ namespace OpenRA.Mods.RA.Render
public override object Create(ActorInitializer init) { return new RenderSpy(init.self); }
}
class RenderSpy : RenderInfantry, IRenderModifier, IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice
class RenderSpy : RenderInfantry, IRenderModifier, IIssueOrder, IResolveOrder, IOrderVoice
{
Player disguisedAsPlayer;
string disguisedAsSprite;
@@ -55,25 +56,20 @@ namespace OpenRA.Mods.RA.Render
}
}
}
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
return 5;
get { yield return new UnitTraitOrderTargeter<RenderInfantry>( "Disguise", 5, "ability", true, true ); }
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if (underCursor != null && underCursor.HasTrait<RenderInfantry>())
return new Order("Disguise", self, underCursor);
if( order.OrderID == "Disguise" )
return new Order( order.OrderID, self, target.Actor );
return null;
}
public string CursorForOrder(Actor self, Order order)
{
return order.OrderString == "Disguise" ? "ability" : null;
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return order.OrderString == "Disguise" ? "Attack" : null;

View File

@@ -51,8 +51,8 @@ namespace OpenRA.Mods.RA.Render
public override void Tick(Actor self)
{
base.Tick(self);
var isFlying = self.Trait<IMove>().Altitude > 0;
var isFlying = self.Trait<IMove>().Altitude > 0 && !self.IsDead();
if (isFlying ^ (rotorAnim.CurrentSequence.Name != "rotor"))
return;

View File

@@ -14,6 +14,8 @@ using OpenRA.Effects;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
using System.Drawing;
using System.Collections.Generic;
using OpenRA.Mods.RA.Orders;
namespace OpenRA.Mods.RA
{
@@ -23,53 +25,51 @@ namespace OpenRA.Mods.RA
public virtual object Create(ActorInitializer init) { return new Repairable(init.self); }
}
class Repairable : IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice
class Repairable : IIssueOrder, IResolveOrder, IOrderVoice
{
Health Health;
readonly Actor self;
readonly Health Health;
public Repairable(Actor self)
{
this.self = self;
Health = self.Trait<Health>();
}
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
return 5;
get { yield return new EnterOrderTargeter<Building>( "Repair", 5, false, true, target => CanRepairAt( target ), _ => CanRepair() ); }
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button != MouseButton.Right) return null;
if (underCursor == null) return null;
if (self.Info.Traits.Get<RepairableInfo>().RepairBuildings.Contains(underCursor.Info.Name)
&& underCursor.Owner == self.Owner)
return new Order("Repair", self, underCursor);
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if( order.OrderID == "Repair" )
return new Order( order.OrderID, self, target.Actor );
return null;
}
bool CanRepair(Actor self)
bool CanRepairAt( Actor target )
{
return self.Info.Traits.Get<RepairableInfo>().RepairBuildings.Contains( target.Info.Name );
}
bool CanRepair()
{
var li = self.TraitOrDefault<LimitedAmmo>();
return (Health.DamageState > DamageState.Undamaged || (li != null && !li.FullAmmo()) );
}
public string CursorForOrder(Actor self, Order order)
{
if (order.OrderString != "Repair") return null;
return CanRepair(self) ? "enter" : "enter-blocked";
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "Repair" && CanRepair(self)) ? "Move" : null;
return (order.OrderString == "Repair" && CanRepair()) ? "Move" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Repair")
{
if (!CanRepair(self))
if( !CanRepairAt( order.TargetActor ) || !CanRepair() )
return;
var rp = order.TargetActor.TraitOrDefault<RallyPoint>();

View File

@@ -13,43 +13,55 @@ using OpenRA.Mods.RA.Activities;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
using System.Drawing;
using System.Collections.Generic;
using OpenRA.Mods.RA.Orders;
namespace OpenRA.Mods.RA
{
class RepairableNearInfo : TraitInfo<RepairableNear>, ITraitPrerequisite<HealthInfo>
class RepairableNearInfo : ITraitInfo, ITraitPrerequisite<HealthInfo>
{
[ActorReference]
public readonly string[] Buildings = { "spen", "syrd" };
public object Create( ActorInitializer init ) { return new RepairableNear( init.self ); }
}
class RepairableNear : IIssueOrder, IResolveOrder, IOrderCursor
class RepairableNear : IIssueOrder, IResolveOrder
{
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
readonly Actor self;
public RepairableNear( Actor self ) { this.self = self; }
public IEnumerable<IOrderTargeter> Orders
{
return 5;
get
{
yield return new EnterOrderTargeter<Building>( "RepairNear", 5, false, true,
target => CanRepairAt( target ), _ => ShouldRepair() );
}
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
if (mi.Button != MouseButton.Right) return null;
if (underCursor == null) return null;
if (underCursor.Owner == self.Owner &&
self.Info.Traits.Get<RepairableNearInfo>().Buildings.Contains( underCursor.Info.Name ) &&
self.GetDamageState() > DamageState.Undamaged)
return new Order("Enter", self, underCursor);
if( order.OrderID == "RepairNear" )
return new Order( order.OrderID, self, target.Actor );
return null;
}
public string CursorForOrder(Actor self, Order order)
bool CanRepairAt( Actor target )
{
return (order.OrderString == "Enter") ? "enter" : null;
return self.Info.Traits.Get<RepairableNearInfo>().Buildings.Contains( target.Info.Name );
}
bool ShouldRepair()
{
return self.GetDamageState() > DamageState.Undamaged;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Enter")
if (order.OrderString == "RepairNear" && CanRepairAt(order.TargetActor) && ShouldRepair())
{
self.CancelActivity();
self.QueueActivity(new Move(order.TargetActor, 1));

23
OpenRA.Mods.RA/Reservable.cs Normal file → Executable file
View File

@@ -13,17 +13,14 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
class ReservableInfo : ITraitInfo
{
public object Create(ActorInitializer init) { return new Reservable(init.self); }
}
class ReservableInfo : TraitInfo<Reservable> {}
public class Reservable : ITick
{
Actor reservedFor;
Actor self;
//Actor self;
public Reservable(Actor self) { this.self = self; }
//public Reservable(Actor self) { this.self = self; }
public void Tick(Actor self)
{
@@ -36,18 +33,18 @@ namespace OpenRA.Mods.RA
public IDisposable Reserve(Actor forActor)
{
if (reservedFor != null)
Game.Debug("BUG: #{0} {1} was already reserved (by #{2} {3})".F(
self.ActorID, self.Info.Name, reservedFor.ActorID, reservedFor.Info.Name));
//if (reservedFor != null)
// Game.Debug("BUG: #{0} {1} was already reserved (by #{2} {3})".F(
// self.ActorID, self.Info.Name, reservedFor.ActorID, reservedFor.Info.Name));
reservedFor = forActor;
Game.Debug("#{0} {1} reserved by #{2} {3}".F(
self.ActorID, self.Info.Name, forActor.ActorID, forActor.Info.Name));
//Game.Debug("#{0} {1} reserved by #{2} {3}".F(
// self.ActorID, self.Info.Name, forActor.ActorID, forActor.Info.Name));
return new DisposableAction(() =>
{
Game.Debug("#{0} {1} unreserved".F(
self.ActorID, self.Info.Name));
//Game.Debug("#{0} {1} unreserved".F(
// self.ActorID, self.Info.Name));
reservedFor = null;
});
}

12
OpenRA.Mods.RA/ReturnOnIdle.cs Normal file → Executable file
View File

@@ -32,9 +32,19 @@ namespace OpenRA.Mods.RA
}
else
{
Game.Debug("Plane has nowhere to land; flying away");
//Game.Debug("Plane has nowhere to land; flying away");
self.QueueActivity(new FlyOffMap());
}
}
}
class FlyAwayOnIdleInfo : TraitInfo<FlyAwayOnIdle> { }
class FlyAwayOnIdle : INotifyIdle
{
public void Idle(Actor self)
{
self.QueueActivity(new FlyOffMap());
}
}
}

View File

@@ -8,40 +8,35 @@
*/
#endregion
using System.Drawing;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Orders;
using OpenRA.Traits;
using OpenRA.Traits.Activities;
using System.Drawing;
using System.Collections.Generic;
namespace OpenRA.Mods.RA
{
class SpyInfo : TraitInfo<Spy> { }
class Spy : IIssueOrder, IResolveOrder, IOrderCursor
class Spy : IIssueOrder, IResolveOrder
{
public int OrderPriority(Actor self, int2 xy, MouseInput mi, Actor underCursor)
public IEnumerable<IOrderTargeter> Orders
{
return 5;
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button != MouseButton.Right) return null;
if (underCursor == null) return null;
if (underCursor.Owner == self.Owner) return null;
if (!underCursor.HasTrait<IAcceptSpy>()) return null;
return new Order("Infiltrate", self, underCursor);
get { yield return new UnitTraitOrderTargeter<IAcceptSpy>( "SpyInfiltrate", 5, "enter", true, false ); }
}
public string CursorForOrder(Actor self, Order order)
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
{
return (order.OrderString == "Infiltrate") ? "enter" : null;
if( order.OrderID == "SpyInfiltrate" )
return new Order( order.OrderID, self, target.Actor );
return null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Infiltrate")
if (order.OrderString == "SpyInfiltrate")
{
self.CancelActivity();
self.QueueActivity(new Move(order.TargetActor, 1));

View File

@@ -44,13 +44,13 @@ namespace OpenRA.Mods.RA
int Stored(Actor self)
{
return (int)(Player.GetSiloFullness() * Info.Capacity);
return Info.Capacity * Player.Ore / Player.OreCapacity;
}
public void Damaged(Actor self, AttackInfo e)
{
if (self.IsDead() && Player.GetSiloFullness() > 0)
Player.TakeOre(Stored(self)); // Lose the stored ore
if (self.IsDead())
Player.TakeOre(Stored(self)); // Lose the stored ore
}
public IEnumerable<PipType> GetPips(Actor self)
@@ -60,6 +60,6 @@ namespace OpenRA.Mods.RA
? Info.PipColor : PipType.Transparent );
}
public bool ShouldExplode(Actor self) { return Player.GetSiloFullness() > 0; }
public bool ShouldExplode(Actor self) { return Stored(self) > 0; }
}
}

View File

@@ -71,9 +71,6 @@ namespace OpenRA.Mods.RA
a.QueueActivity(new RemoveSelf());
});
if (Owner == Owner.World.LocalPlayer)
self.World.CancelInputMode();
FinishActivate();
}
}

View File

@@ -32,14 +32,11 @@ namespace OpenRA.Mods.RA
if (order.OrderString == "ChronosphereSelect" && self.Owner == self.World.LocalPlayer)
{
self.World.OrderGenerator = new SelectDestination(order.TargetActor);
//self.World.OrderGenerator = new SelectDestination(order.TargetActor);
}
if (order.OrderString == "ChronosphereActivate")
{
if (self.Owner == self.World.LocalPlayer)
self.World.CancelInputMode();
// Ensure the target cell is valid for the unit
var movement = order.TargetActor.TraitOrDefault<IMove>();
if (!movement.CanEnterCell(order.TargetLocation))
@@ -63,7 +60,13 @@ namespace OpenRA.Mods.RA
if (mi.Button == MouseButton.Right)
world.CancelInputMode();
return OrderInner(world, xy, mi);
var ret = OrderInner( world, xy, mi ).ToList();
foreach( var order in ret )
{
world.OrderGenerator = new SelectDestination(order.TargetActor);
break;
}
return ret;
}
IEnumerable<Order> OrderInner(World world, int2 xy, MouseInput mi)
@@ -112,11 +115,19 @@ namespace OpenRA.Mods.RA
public IEnumerable<Order> Order(World world, int2 xy, MouseInput mi)
{
if (mi.Button == MouseButton.Right)
world.CancelInputMode();
var ret = OrderInner( world, xy, mi ).ToList();
foreach( var order in ret )
{
world.CancelInputMode();
yield break;
break;
}
return ret;
}
IEnumerable<Order> OrderInner(World world, int2 xy, MouseInput mi)
{
// Cannot chronoshift into unexplored location
if (world.LocalPlayer.Shroud.IsExplored(xy))
yield return new Order("ChronosphereActivate", world.LocalPlayer.PlayerActor, self, xy);

View File

@@ -39,9 +39,6 @@ namespace OpenRA.Mods.RA
if (order.OrderString == "IronCurtain")
{
if (self.Owner == self.World.LocalPlayer)
self.World.CancelInputMode();
var curtain = self.World.Queries.WithTrait<IronCurtain>()
.Where(a => a.Actor.Owner != null)
.FirstOrDefault().Actor;
@@ -78,8 +75,11 @@ namespace OpenRA.Mods.RA
&& a.HasTrait<IronCurtainable>()
&& a.HasTrait<Selectable>()).FirstOrDefault();
if (underCursor != null)
yield return new Order("IronCurtain", underCursor.Owner.PlayerActor, underCursor);
if( underCursor != null )
{
world.CancelInputMode();
yield return new Order( "IronCurtain", underCursor.Owner.PlayerActor, underCursor );
}
}
yield break;

View File

@@ -49,7 +49,6 @@ namespace OpenRA.Mods.RA
silo.Trait<NukeSilo>().Attack(order.TargetLocation);
self.World.CancelInputMode();
FinishActivate();
}
}

View File

@@ -43,9 +43,6 @@ namespace OpenRA.Mods.RA
if (order.OrderString == "ParatroopersActivate")
{
if (self.Owner == self.World.LocalPlayer)
self.World.CancelInputMode();
DoParadrop(Owner, order.TargetLocation,
self.Info.Traits.Get<ParatroopersPowerInfo>().DropItems);

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