Merge pull request #5262 from Mailaender/parse-invariant-culture

Added checks for NumberFormatInfo.InvariantInfo everywhere
This commit is contained in:
Paul Chote
2014-05-14 00:20:35 +12:00
25 changed files with 150 additions and 123 deletions

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -251,6 +252,16 @@ namespace OpenRA
throw new InvalidOperationException("ToBits only accepts up to 32 values."); throw new InvalidOperationException("ToBits only accepts up to 32 values.");
return result; return result;
} }
public static int ParseIntegerInvariant(string s)
{
return int.Parse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
}
public static bool TryParseIntegerInvariant(string s, out int i)
{
return int.TryParse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out i);
}
} }
public static class Enum<T> public static class Enum<T>

61
OpenRA.Game/FieldLoader.cs Executable file → Normal file
View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -116,7 +116,7 @@ namespace OpenRA
if (fieldType == typeof(int)) if (fieldType == typeof(int))
{ {
int res; int res;
if (int.TryParse(value, out res)) if (Exts.TryParseIntegerInvariant(value, out res))
return res; return res;
return InvalidValueAction(value, fieldType, fieldName); return InvalidValueAction(value, fieldType, fieldName);
} }
@@ -124,7 +124,7 @@ namespace OpenRA
else if (fieldType == typeof(ushort)) else if (fieldType == typeof(ushort))
{ {
ushort res; ushort res;
if (ushort.TryParse(value, out res)) if (ushort.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out res))
return res; return res;
return InvalidValueAction(value, fieldType, fieldName); return InvalidValueAction(value, fieldType, fieldName);
} }
@@ -132,7 +132,7 @@ namespace OpenRA
if (fieldType == typeof(long)) if (fieldType == typeof(long))
{ {
long res; long res;
if (long.TryParse(value, out res)) if (long.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out res))
return res; return res;
return InvalidValueAction(value, fieldType, fieldName); return InvalidValueAction(value, fieldType, fieldName);
} }
@@ -140,7 +140,7 @@ namespace OpenRA
else if (fieldType == typeof(float)) else if (fieldType == typeof(float))
{ {
float res; float res;
if (float.TryParse(value.Replace("%", ""), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res)) if (float.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
return res * (value.Contains('%') ? 0.01f : 1f); return res * (value.Contains('%') ? 0.01f : 1f);
return InvalidValueAction(value, fieldType, fieldName); return InvalidValueAction(value, fieldType, fieldName);
} }
@@ -148,7 +148,7 @@ namespace OpenRA
else if (fieldType == typeof(decimal)) else if (fieldType == typeof(decimal))
{ {
decimal res; decimal res;
if (decimal.TryParse(value.Replace("%", ""), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res)) if (decimal.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
return res * (value.Contains('%') ? 0.01m : 1m); return res * (value.Contains('%') ? 0.01m : 1m);
return InvalidValueAction(value, fieldType, fieldName); return InvalidValueAction(value, fieldType, fieldName);
} }
@@ -164,9 +164,16 @@ namespace OpenRA
{ {
var parts = value.Split(','); var parts = value.Split(',');
if (parts.Length == 3) if (parts.Length == 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)); return Color.FromArgb(
Exts.ParseIntegerInvariant(parts[0]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[1]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[2]).Clamp(0, 255));
if (parts.Length == 4) if (parts.Length == 4)
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 Color.FromArgb(
Exts.ParseIntegerInvariant(parts[0]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[1]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[2]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[3]).Clamp(0, 255));
return InvalidValueAction(value, fieldType, fieldName); return InvalidValueAction(value, fieldType, fieldName);
} }
@@ -177,9 +184,9 @@ namespace OpenRA
// Allow old ColorRamp format to be parsed as HSLColor // Allow old ColorRamp format to be parsed as HSLColor
if (parts.Length == 3 || parts.Length == 4) if (parts.Length == 3 || parts.Length == 4)
return new HSLColor( return new HSLColor(
(byte)int.Parse(parts[0]).Clamp(0, 255), (byte)Exts.ParseIntegerInvariant(parts[0]).Clamp(0, 255),
(byte)int.Parse(parts[1]).Clamp(0, 255), (byte)Exts.ParseIntegerInvariant(parts[1]).Clamp(0, 255),
(byte)int.Parse(parts[2]).Clamp(0, 255)); (byte)Exts.ParseIntegerInvariant(parts[2]).Clamp(0, 255));
return InvalidValueAction(value, fieldType, fieldName); return InvalidValueAction(value, fieldType, fieldName);
} }
@@ -231,7 +238,7 @@ namespace OpenRA
else if (fieldType == typeof(WAngle)) else if (fieldType == typeof(WAngle))
{ {
int res; int res;
if (int.TryParse(value, out res)) if (Exts.TryParseIntegerInvariant(value, out res))
return new WAngle(res); return new WAngle(res);
return InvalidValueAction(value, fieldType, fieldName); return InvalidValueAction(value, fieldType, fieldName);
} }
@@ -242,7 +249,9 @@ namespace OpenRA
if (parts.Length == 3) if (parts.Length == 3)
{ {
int rr, rp, ry; int rr, rp, ry;
if (int.TryParse(value, out rr) && int.TryParse(value, out rp) && int.TryParse(value, out ry)) if (Exts.TryParseIntegerInvariant(value, out rr)
&& Exts.TryParseIntegerInvariant(value, out rp)
&& Exts.TryParseIntegerInvariant(value, out ry))
return new WRot(new WAngle(rr), new WAngle(rp), new WAngle(ry)); return new WRot(new WAngle(rr), new WAngle(rp), new WAngle(ry));
} }
@@ -252,13 +261,17 @@ namespace OpenRA
else if (fieldType == typeof(CPos)) else if (fieldType == typeof(CPos))
{ {
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
return new CPos(int.Parse(parts[0]), int.Parse(parts[1])); return new CPos(
Exts.ParseIntegerInvariant(parts[0]),
Exts.ParseIntegerInvariant(parts[1]));
} }
else if (fieldType == typeof(CVec)) else if (fieldType == typeof(CVec))
{ {
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
return new CVec(int.Parse(parts[0]), int.Parse(parts[1])); return new CVec(
Exts.ParseIntegerInvariant(parts[0]),
Exts.ParseIntegerInvariant(parts[1]));
} }
else if (fieldType.IsEnum) else if (fieldType.IsEnum)
@@ -292,13 +305,17 @@ namespace OpenRA
else if (fieldType == typeof(Size)) else if (fieldType == typeof(Size))
{ {
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
return new Size(int.Parse(parts[0]), int.Parse(parts[1])); return new Size(
Exts.ParseIntegerInvariant(parts[0]),
Exts.ParseIntegerInvariant(parts[1]));
} }
else if (fieldType == typeof(int2)) else if (fieldType == typeof(int2))
{ {
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
return new int2(int.Parse(parts[0]), int.Parse(parts[1])); return new int2(
Exts.ParseIntegerInvariant(parts[0]),
Exts.ParseIntegerInvariant(parts[1]));
} }
else if (fieldType == typeof(float2)) else if (fieldType == typeof(float2))
@@ -307,9 +324,9 @@ namespace OpenRA
float xx = 0; float xx = 0;
float yy = 0; float yy = 0;
float res; float res;
if (float.TryParse(parts[0].Replace("%", ""), out res)) if (float.TryParse(parts[0].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
xx = res * (parts[0].Contains('%') ? 0.01f : 1f); xx = res * (parts[0].Contains('%') ? 0.01f : 1f);
if (float.TryParse(parts[1].Replace("%", ""), out res)) if (float.TryParse(parts[1].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
yy = res * (parts[1].Contains('%') ? 0.01f : 1f); yy = res * (parts[1].Contains('%') ? 0.01f : 1f);
return new float2(xx, yy); return new float2(xx, yy);
} }
@@ -317,7 +334,11 @@ namespace OpenRA
else if (fieldType == typeof(Rectangle)) else if (fieldType == typeof(Rectangle))
{ {
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
return new Rectangle(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3])); return new Rectangle(
Exts.ParseIntegerInvariant(parts[0]),
Exts.ParseIntegerInvariant(parts[1]),
Exts.ParseIntegerInvariant(parts[2]),
Exts.ParseIntegerInvariant(parts[3]));
} }
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Bits<>)) else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Bits<>))

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -41,7 +41,8 @@ namespace OpenRA.Graphics
if (sequences.NodesDict.ContainsKey("ShadowIndex")) if (sequences.NodesDict.ContainsKey("ShadowIndex"))
{ {
Array.Resize(ref shadowIndex, shadowIndex.Length + 1); Array.Resize(ref shadowIndex, shadowIndex.Length + 1);
int.TryParse(sequences.NodesDict["ShadowIndex"].Value, out shadowIndex[shadowIndex.Length - 1]); Exts.TryParseIntegerInvariant(sequences.NodesDict["ShadowIndex"].Value,
out shadowIndex[shadowIndex.Length - 1]);
} }
palette = new HardwarePalette(); palette = new HardwarePalette();

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -30,22 +30,22 @@ namespace OpenRA.Graphics
sprites = Game.modData.SpriteLoader.LoadAllSprites(cursorSrc); sprites = Game.modData.SpriteLoader.LoadAllSprites(cursorSrc);
var d = info.NodesDict; var d = info.NodesDict;
start = int.Parse(d["start"].Value); start = Exts.ParseIntegerInvariant(d["start"].Value);
this.palette = palette; this.palette = palette;
if ((d.ContainsKey("length") && d["length"].Value == "*") || (d.ContainsKey("end") && d["end"].Value == "*")) if ((d.ContainsKey("length") && d["length"].Value == "*") || (d.ContainsKey("end") && d["end"].Value == "*"))
length = sprites.Length - start; length = sprites.Length - start;
else if (d.ContainsKey("length")) else if (d.ContainsKey("length"))
length = int.Parse(d["length"].Value); length = Exts.ParseIntegerInvariant(d["length"].Value);
else if (d.ContainsKey("end")) else if (d.ContainsKey("end"))
length = int.Parse(d["end"].Value) - start; length = Exts.ParseIntegerInvariant(d["end"].Value) - start;
else else
length = 1; length = 1;
if (d.ContainsKey("x")) if (d.ContainsKey("x"))
int.TryParse(d["x"].Value, out Hotspot.X); Exts.TryParseIntegerInvariant(d["x"].Value, out Hotspot.X);
if (d.ContainsKey("y")) if (d.ContainsKey("y"))
int.TryParse(d["y"].Value, out Hotspot.Y); Exts.TryParseIntegerInvariant(d["y"].Value, out Hotspot.Y);
} }
public Sprite GetSprite(int frame) public Sprite GetSprite(int frame)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -40,7 +40,7 @@ namespace OpenRA.Graphics
try try
{ {
if (d.ContainsKey("Start")) if (d.ContainsKey("Start"))
Start = int.Parse(d["Start"].Value); Start = Exts.ParseIntegerInvariant(d["Start"].Value);
if (d.ContainsKey("Offset")) if (d.ContainsKey("Offset"))
offset = FieldLoader.GetValue<float2>("Offset", d["Offset"].Value); offset = FieldLoader.GetValue<float2>("Offset", d["Offset"].Value);
@@ -58,16 +58,16 @@ namespace OpenRA.Graphics
else if (d["Length"].Value == "*") else if (d["Length"].Value == "*")
Length = sprites.Length - Start; Length = sprites.Length - Start;
else else
Length = int.Parse(d["Length"].Value); Length = Exts.ParseIntegerInvariant(d["Length"].Value);
if (d.ContainsKey("Stride")) if (d.ContainsKey("Stride"))
Stride = int.Parse(d["Stride"].Value); Stride = Exts.ParseIntegerInvariant(d["Stride"].Value);
else else
Stride = Length; Stride = Length;
if (d.ContainsKey("Facings")) if (d.ContainsKey("Facings"))
{ {
var f = int.Parse(d["Facings"].Value); var f = Exts.ParseIntegerInvariant(d["Facings"].Value);
Facings = Math.Abs(f); Facings = Math.Abs(f);
reverseFacings = f < 0; reverseFacings = f < 0;
} }
@@ -75,7 +75,7 @@ namespace OpenRA.Graphics
Facings = 1; Facings = 1;
if (d.ContainsKey("Tick")) if (d.ContainsKey("Tick"))
Tick = int.Parse(d["Tick"].Value); Tick = Exts.ParseIntegerInvariant(d["Tick"].Value);
else else
Tick = 40; Tick = 40;
@@ -83,10 +83,10 @@ namespace OpenRA.Graphics
transpose = bool.Parse(d["Transpose"].Value); transpose = bool.Parse(d["Transpose"].Value);
if (d.ContainsKey("Frames")) if (d.ContainsKey("Frames"))
Frames = Array.ConvertAll<string, int>(d["Frames"].Value.Split(','), int.Parse); Frames = Array.ConvertAll<string, int>(d["Frames"].Value.Split(','), Exts.ParseIntegerInvariant);
if (d.ContainsKey("ShadowStart")) if (d.ContainsKey("ShadowStart"))
ShadowStart = int.Parse(d["ShadowStart"].Value); ShadowStart = Exts.ParseIntegerInvariant(d["ShadowStart"].Value);
else else
ShadowStart = -1; ShadowStart = -1;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -68,7 +68,7 @@ namespace OpenRA
LobbyDefaults = yaml["LobbyDefaults"]; LobbyDefaults = yaml["LobbyDefaults"];
Fonts = yaml["Fonts"].NodesDict.ToDictionary(x => x.Key, Fonts = yaml["Fonts"].NodesDict.ToDictionary(x => x.Key,
x => Pair.New(x.Value.NodesDict["Font"].Value, x => Pair.New(x.Value.NodesDict["Font"].Value,
int.Parse(x.Value.NodesDict["Size"].Value))); Exts.ParseIntegerInvariant(x.Value.NodesDict["Size"].Value)));
if (yaml.ContainsKey("TileSize")) if (yaml.ContainsKey("TileSize"))
TileSize = FieldLoader.GetValue<Size>("TileSize", yaml["TileSize"].Value); TileSize = FieldLoader.GetValue<Size>("TileSize", yaml["TileSize"].Value);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -201,7 +201,10 @@ namespace OpenRA
{ {
var vals = kv.Key.Split(' '); var vals = kv.Key.Split(' ');
var loc = vals[1].Split(','); var loc = vals[1].Split(',');
ret.Add(new SmudgeReference(vals[0], new int2(int.Parse(loc[0]), int.Parse(loc[1])), int.Parse(vals[2]))); ret.Add(new SmudgeReference(vals[0], new int2(
Exts.ParseIntegerInvariant(loc[0]),
Exts.ParseIntegerInvariant(loc[1])),
Exts.ParseIntegerInvariant(vals[2])));
} }
return ret; return ret;

View File

@@ -29,7 +29,7 @@ namespace OpenRA
public static int2 operator -(int2 a) { return new int2(-a.X, -a.Y); } public static int2 operator -(int2 a) { return new int2(-a.X, -a.Y); }
public static bool operator ==(int2 me, int2 other) { return (me.X == other.X && me.Y == other.Y); } public static bool operator ==(int2 me, int2 other) { return me.X == other.X && me.Y == other.Y; }
public static bool operator !=(int2 me, int2 other) { return !(me == other); } public static bool operator !=(int2 me, int2 other) { return !(me == other); }
public int2 Sign() { return new int2(Math.Sign(X), Math.Sign(Y)); } public int2 Sign() { return new int2(Math.Sign(X), Math.Sign(Y)); }
@@ -77,6 +77,5 @@ namespace OpenRA
} }
public static int Dot(int2 a, int2 b) { return a.X * b.X + a.Y * b.Y; } public static int Dot(int2 a, int2 b) { return a.X * b.X + a.Y * b.Y; }
} }
} }

View File

@@ -16,18 +16,6 @@ namespace OpenRA.Server
{ {
static class Exts static class Exts
{ {
public static void Write( this Stream s, byte[] data )
{
s.Write( data, 0, data.Length );
}
public static byte[] Read( this Stream s, int len )
{
var data = new byte[ len ];
s.Read( data, 0, len );
return data;
}
public static IEnumerable<T> Except<T>(this IEnumerable<T> ts, T t) public static IEnumerable<T> Except<T>(this IEnumerable<T> ts, T t)
{ {
return ts.Except(new[] { t }); return ts.Except(new[] { t });

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2012 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -466,7 +466,7 @@ namespace OpenRA.Server
case "Pong": case "Pong":
{ {
int pingSent; int pingSent;
if (!int.TryParse(so.Data, out pingSent)) if (!OpenRA.Exts.TryParseIntegerInvariant(so.Data, out pingSent))
{ {
Log.Write("server", "Invalid order pong payload: {0}", so.Data); Log.Write("server", "Invalid order pong payload: {0}", so.Data);
break; break;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -36,7 +36,7 @@ namespace OpenRA.Support
case '-': ApplyBinop(s, (x, y) => y - x); break; case '-': ApplyBinop(s, (x, y) => y - x); break;
case '*': ApplyBinop(s, (x, y) => y * x); break; case '*': ApplyBinop(s, (x, y) => y * x); break;
case '/': ApplyBinop(s, (x, y) => y / x); break; case '/': ApplyBinop(s, (x, y) => y / x); break;
default: s.Push(int.Parse(t)); break; default: s.Push(Exts.ParseIntegerInvariant(t)); break;
} }
} }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -56,12 +56,12 @@ namespace OpenRA
switch (components.Length) switch (components.Length)
{ {
case 2: case 2:
if (!int.TryParse(components[0], out cell) || if (!Exts.TryParseIntegerInvariant(components[0], out cell) ||
!int.TryParse(components[1], out subcell)) !Exts.TryParseIntegerInvariant(components[1], out subcell))
return false; return false;
break; break;
case 1: case 1:
if (!int.TryParse(components[0], out subcell)) if (!Exts.TryParseIntegerInvariant(components[0], out subcell))
return false; return false;
break; break;
default: return false; default: return false;

View File

@@ -280,7 +280,7 @@ namespace OpenRA.Irc
OnLineRead(l); OnLineRead(l);
int numeric; int numeric;
if (int.TryParse(l.Command, out numeric)) if (Exts.TryParseIntegerInvariant(l.Command, out numeric))
{ {
var nl = new NumericLine(l, numeric); var nl = new NumericLine(l, numeric);
LocalUser.OnNumeric(nl); LocalUser.OnNumeric(nl);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -47,7 +47,7 @@ namespace OpenRA.Irc
{ {
var topic = line.GetChannel().Topic; var topic = line.GetChannel().Topic;
topic.Author = new User(line[4]); topic.Author = new User(line[4]);
topic.Time = IrcUtils.DateTimeFromUnixTime(int.Parse(line[5])); topic.Time = IrcUtils.DateTimeFromUnixTime(Exts.ParseIntegerInvariant(line[5]));
} }
break; break;
case NumericCommand.ERR_NICKNAMEINUSE: case NumericCommand.ERR_NICKNAMEINUSE:

View File

@@ -9,6 +9,7 @@
#endregion #endregion
using System.Linq; using System.Linq;
using System.Globalization;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.RA namespace OpenRA.Mods.RA
@@ -23,7 +24,7 @@ namespace OpenRA.Mods.RA
{ {
/* create a group */ /* create a group */
var actors = order.TargetString.Split(',') var actors = order.TargetString.Split(',')
.Select(id => uint.Parse(id)) .Select(id => uint.Parse(id, NumberStyles.Any, NumberFormatInfo.InvariantInfo))
.Select(id => self.World.Actors.FirstOrDefault(a => a.ActorID == id)) .Select(id => self.World.Actors.FirstOrDefault(a => a.ActorID == id))
.Where(a => a != null); .Where(a => a != null);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -225,7 +225,7 @@ namespace OpenRA.Mods.RA.Server
var slot = server.LobbyInfo.Slots[parts[0]]; var slot = server.LobbyInfo.Slots[parts[0]];
var bot = server.LobbyInfo.ClientInSlot(parts[0]); var bot = server.LobbyInfo.ClientInSlot(parts[0]);
int controllerClientIndex; int controllerClientIndex;
if (!int.TryParse(parts[1], out controllerClientIndex)) if (!Exts.TryParseIntegerInvariant(parts[1], out controllerClientIndex))
{ {
Log.Write("server", "Invalid bot controller client index: {0}", parts[1]); Log.Write("server", "Invalid bot controller client index: {0}", parts[1]);
return false; return false;
@@ -413,7 +413,7 @@ namespace OpenRA.Mods.RA.Server
} }
int teamCount; int teamCount;
if (!int.TryParse(s, out teamCount)) if (!Exts.TryParseIntegerInvariant(s, out teamCount))
{ {
server.SendOrderTo(conn, "Message", "Number of teams could not be parsed: {0}".F(s)); server.SendOrderTo(conn, "Message", "Number of teams could not be parsed: {0}".F(s));
return true; return true;
@@ -536,7 +536,7 @@ namespace OpenRA.Mods.RA.Server
return true; return true;
} }
server.LobbyInfo.GlobalSettings.StartingCash = int.Parse(s); server.LobbyInfo.GlobalSettings.StartingCash = Exts.ParseIntegerInvariant(s);
server.SyncLobbyInfo(); server.SyncLobbyInfo();
return true; return true;
}}, }},
@@ -557,7 +557,7 @@ namespace OpenRA.Mods.RA.Server
} }
int kickClientID; int kickClientID;
int.TryParse(split[0], out kickClientID); Exts.TryParseIntegerInvariant(split[0], out kickClientID);
var kickConn = server.Conns.SingleOrDefault(c => server.GetClient(c) != null && server.GetClient(c).Index == kickClientID); var kickConn = server.Conns.SingleOrDefault(c => server.GetClient(c) != null && server.GetClient(c).Index == kickClientID);
if (kickConn == null) if (kickConn == null)
@@ -596,7 +596,7 @@ namespace OpenRA.Mods.RA.Server
s => s =>
{ {
var parts = s.Split(' '); var parts = s.Split(' ');
var targetClient = server.LobbyInfo.ClientWithIndex(int.Parse(parts[0])); var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0]));
// Only the host can change other client's info // Only the host can change other client's info
if (targetClient.Index != client.Index && !client.IsAdmin) if (targetClient.Index != client.Index && !client.IsAdmin)
@@ -614,7 +614,7 @@ namespace OpenRA.Mods.RA.Server
s => s =>
{ {
var parts = s.Split(' '); var parts = s.Split(' ');
var targetClient = server.LobbyInfo.ClientWithIndex(int.Parse(parts[0])); var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0]));
// Only the host can change other client's info // Only the host can change other client's info
if (targetClient.Index != client.Index && !client.IsAdmin) if (targetClient.Index != client.Index && !client.IsAdmin)
@@ -625,7 +625,7 @@ namespace OpenRA.Mods.RA.Server
return true; return true;
int team; int team;
if (!int.TryParse(parts[1], out team)) if (!Exts.TryParseIntegerInvariant(parts[1], out team))
{ {
Log.Write("server", "Invalid team: {0}", s ); Log.Write("server", "Invalid team: {0}", s );
return false; return false;
@@ -639,7 +639,7 @@ namespace OpenRA.Mods.RA.Server
s => s =>
{ {
var parts = s.Split(' '); var parts = s.Split(' ');
var targetClient = server.LobbyInfo.ClientWithIndex(int.Parse(parts[0])); var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0]));
// Only the host can change other client's info // Only the host can change other client's info
if (targetClient.Index != client.Index && !client.IsAdmin) if (targetClient.Index != client.Index && !client.IsAdmin)
@@ -654,7 +654,8 @@ namespace OpenRA.Mods.RA.Server
return true; return true;
int spawnPoint; int spawnPoint;
if (!int.TryParse(parts[1], out spawnPoint) || spawnPoint < 0 || spawnPoint > server.Map.GetSpawnPoints().Length) if (!Exts.TryParseIntegerInvariant(parts[1], out spawnPoint)
|| spawnPoint < 0 || spawnPoint > server.Map.GetSpawnPoints().Length)
{ {
Log.Write("server", "Invalid spawn point: {0}", parts[1]); Log.Write("server", "Invalid spawn point: {0}", parts[1]);
return true; return true;
@@ -674,7 +675,7 @@ namespace OpenRA.Mods.RA.Server
s => s =>
{ {
var parts = s.Split(' '); var parts = s.Split(' ');
var targetClient = server.LobbyInfo.ClientWithIndex(int.Parse(parts[0])); var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0]));
// Only the host can change other client's info // Only the host can change other client's info
if (targetClient.Index != client.Index && !client.IsAdmin) if (targetClient.Index != client.Index && !client.IsAdmin)
@@ -684,7 +685,7 @@ namespace OpenRA.Mods.RA.Server
if (targetClient.Slot == null || server.LobbyInfo.Slots[targetClient.Slot].LockColor) if (targetClient.Slot == null || server.LobbyInfo.Slots[targetClient.Slot].LockColor)
return true; return true;
var ci = parts[1].Split(',').Select(cc => int.Parse(cc)).ToArray(); var ci = parts[1].Split(',').Select(cc => Exts.ParseIntegerInvariant(cc)).ToArray();
targetClient.Color = targetClient.PreferredColor = new HSLColor((byte)ci[0], (byte)ci[1], (byte)ci[2]); targetClient.Color = targetClient.PreferredColor = new HSLColor((byte)ci[0], (byte)ci[1], (byte)ci[2]);
server.SyncLobbyInfo(); server.SyncLobbyInfo();
return true; return true;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -28,7 +28,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
panel.Get<ButtonWidget>("JOIN_BUTTON").OnClick = () => panel.Get<ButtonWidget>("JOIN_BUTTON").OnClick = () =>
{ {
var port = Exts.WithDefault(1234, () => int.Parse(portField.Text)); var port = Exts.WithDefault(1234, () => Exts.ParseIntegerInvariant(portField.Text));
Game.Settings.Player.LastServer = "{0}:{1}".F(ipField.Text, port); Game.Settings.Player.LastServer = "{0}:{1}".F(ipField.Text, port);
Game.Settings.Save(); Game.Settings.Save();

View File

@@ -284,7 +284,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
return; return;
var host = server.Address.Split(':')[0]; var host = server.Address.Split(':')[0];
var port = int.Parse(server.Address.Split(':')[1]); var port = Exts.ParseIntegerInvariant(server.Address.Split(':')[1]);
ConnectionLogic.Connect(host, port, "", OpenLobby, DoNothing); ConnectionLogic.Connect(host, port, "", OpenLobby, DoNothing);
} }

View File

@@ -81,10 +81,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{ {
var name = panel.Get<TextFieldWidget>("SERVER_NAME").Text; var name = panel.Get<TextFieldWidget>("SERVER_NAME").Text;
int listenPort, externalPort; int listenPort, externalPort;
if (!int.TryParse(panel.Get<TextFieldWidget>("LISTEN_PORT").Text, out listenPort)) if (!Exts.TryParseIntegerInvariant(panel.Get<TextFieldWidget>("LISTEN_PORT").Text, out listenPort))
listenPort = 1234; listenPort = 1234;
if (!int.TryParse(panel.Get<TextFieldWidget>("EXTERNAL_PORT").Text, out externalPort)) if (!Exts.TryParseIntegerInvariant(panel.Get<TextFieldWidget>("EXTERNAL_PORT").Text, out externalPort))
externalPort = 1234; externalPort = 1234;
var passwordField = panel.GetOrNull<PasswordFieldWidget>("PASSWORD"); var passwordField = panel.GetOrNull<PasswordFieldWidget>("PASSWORD");

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -151,7 +151,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
frameLimitTextfield.OnLoseFocus = () => frameLimitTextfield.OnLoseFocus = () =>
{ {
int fps; int fps;
int.TryParse(frameLimitTextfield.Text, out fps); Exts.TryParseIntegerInvariant(frameLimitTextfield.Text, out fps);
ds.MaxFramerate = fps.Clamp(20, 200); ds.MaxFramerate = fps.Clamp(20, 200);
frameLimitTextfield.Text = ds.MaxFramerate.ToString(); frameLimitTextfield.Text = ds.MaxFramerate.ToString();
Game.SetIdealFrameTime(ds.MaxFramerate); Game.SetIdealFrameTime(ds.MaxFramerate);
@@ -162,8 +162,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
return () => return () =>
{ {
int x, y; int x, y;
int.TryParse(windowWidth.Text, out x); Exts.TryParseIntegerInvariant(windowWidth.Text, out x);
int.TryParse(windowHeight.Text, out y); Exts.TryParseIntegerInvariant(windowHeight.Text, out y);
ds.WindowedSize = new int2(x, y); ds.WindowedSize = new int2(x, y);
frameLimitTextfield.YieldKeyboardFocus(); frameLimitTextfield.YieldKeyboardFocus();
}; };

View File

@@ -235,9 +235,9 @@ namespace OpenRA.Utility
for (var z = 3; z < args.Length - 2; z += 3) for (var z = 3; z < args.Length - 2; z += 3)
{ {
var start = int.Parse(args[z]); var start = Exts.ParseIntegerInvariant(args[z]);
var m = int.Parse(args[z + 1]); var m = Exts.ParseIntegerInvariant(args[z + 1]);
var n = int.Parse(args[z + 2]); var n = Exts.ParseIntegerInvariant(args[z + 2]);
for (var i = 0; i < m; i++) for (var i = 0; i < m; i++)
for (var j = 0; j < n; j++) for (var j = 0; j < n; j++)

View File

@@ -129,11 +129,11 @@ namespace OpenRA.Utility
var file = new IniFile(GlobalFileSystem.Open(iniFile)); var file = new IniFile(GlobalFileSystem.Open(iniFile));
var basic = file.GetSection("Basic"); var basic = file.GetSection("Basic");
var mapSection = file.GetSection("Map"); var mapSection = file.GetSection("Map");
var legacyMapFormat = (IniMapFormat)int.Parse(basic.GetValue("NewINIFormat", "0")); var legacyMapFormat = (IniMapFormat)Exts.ParseIntegerInvariant(basic.GetValue("NewINIFormat", "0"));
var offsetX = int.Parse(mapSection.GetValue("X", "0")); var offsetX = Exts.ParseIntegerInvariant(mapSection.GetValue("X", "0"));
var offsetY = int.Parse(mapSection.GetValue("Y", "0")); var offsetY = Exts.ParseIntegerInvariant(mapSection.GetValue("Y", "0"));
var width = int.Parse(mapSection.GetValue("Width", "0")); var width = Exts.ParseIntegerInvariant(mapSection.GetValue("Width", "0"));
var height = int.Parse(mapSection.GetValue("Height", "0")); var height = Exts.ParseIntegerInvariant(mapSection.GetValue("Height", "0"));
mapSize = (legacyMapFormat == IniMapFormat.RedAlert) ? 128 : 64; mapSize = (legacyMapFormat == IniMapFormat.RedAlert) ? 128 : 64;
map.Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(iniFile)); map.Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(iniFile));
@@ -174,9 +174,9 @@ namespace OpenRA.Utility
LoadPlayer(file, p, legacyMapFormat == IniMapFormat.RedAlert); LoadPlayer(file, p, legacyMapFormat == IniMapFormat.RedAlert);
var wps = file.GetSection("Waypoints") var wps = file.GetSection("Waypoints")
.Where(kv => int.Parse(kv.Value) > 0) .Where(kv => Exts.ParseIntegerInvariant(kv.Value) > 0)
.Select(kv => Pair.New(int.Parse(kv.Key), .Select(kv => Pair.New(Exts.ParseIntegerInvariant(kv.Key),
LocationFromMapOffset(int.Parse(kv.Value), mapSize))) LocationFromMapOffset(Exts.ParseIntegerInvariant(kv.Value), mapSize)))
.ToArray(); .ToArray();
// Add waypoint actors // Add waypoint actors
@@ -298,7 +298,7 @@ namespace OpenRA.Utility
foreach (KeyValuePair<string, string> kv in terrain) foreach (KeyValuePair<string, string> kv in terrain)
{ {
var loc = int.Parse(kv.Key); var loc = Exts.ParseIntegerInvariant(kv.Key);
map.Actors.Value.Add("Actor" + actorCount++, map.Actors.Value.Add("Actor" + actorCount++,
new ActorReference(kv.Value.ToLowerInvariant()) new ActorReference(kv.Value.ToLowerInvariant())
{ {
@@ -332,7 +332,7 @@ namespace OpenRA.Utility
foreach (KeyValuePair<string, string> kv in overlay) foreach (KeyValuePair<string, string> kv in overlay)
{ {
var loc = int.Parse(kv.Key); var loc = Exts.ParseIntegerInvariant(kv.Key);
var cell = new CPos(loc % mapSize, loc / mapSize); var cell = new CPos(loc % mapSize, loc / mapSize);
var res = Pair.New((byte)0, (byte)0); var res = Pair.New((byte)0, (byte)0);
@@ -359,7 +359,7 @@ namespace OpenRA.Utility
foreach (KeyValuePair<string, string> kv in terrain) foreach (KeyValuePair<string, string> kv in terrain)
{ {
var loc = int.Parse(kv.Key); var loc = Exts.ParseIntegerInvariant(kv.Key);
map.Actors.Value.Add("Actor" + actorCount++, map.Actors.Value.Add("Actor" + actorCount++,
new ActorReference(kv.Value.Split(',')[0].ToLowerInvariant()) new ActorReference(kv.Value.Split(',')[0].ToLowerInvariant())
{ {
@@ -379,7 +379,7 @@ namespace OpenRA.Utility
try try
{ {
var parts = s.Value.Split(','); var parts = s.Value.Split(',');
var loc = int.Parse(parts[3]); var loc = Exts.ParseIntegerInvariant(parts[3]);
if (parts[0] == "") if (parts[0] == "")
parts[0] = "Neutral"; parts[0] = "Neutral";
@@ -391,11 +391,13 @@ namespace OpenRA.Utility
new LocationInit(new CPos(loc % mapSize, loc / mapSize)), new LocationInit(new CPos(loc % mapSize, loc / mapSize)),
new OwnerInit(parts[0]), new OwnerInit(parts[0]),
new HealthInit(float.Parse(parts[2], NumberFormatInfo.InvariantInfo) / 256), new HealthInit(float.Parse(parts[2], NumberFormatInfo.InvariantInfo) / 256),
new FacingInit((section == "INFANTRY") ? int.Parse(parts[6]) : int.Parse(parts[4])), new FacingInit((section == "INFANTRY")
? Exts.ParseIntegerInvariant(parts[6])
: Exts.ParseIntegerInvariant(parts[4])),
}; };
if (section == "INFANTRY") if (section == "INFANTRY")
actor.Add(new SubCellInit(int.Parse(parts[4]))); actor.Add(new SubCellInit(Exts.ParseIntegerInvariant(parts[4])));
if (!Rules.Info.ContainsKey(parts[1].ToLowerInvariant())) if (!Rules.Info.ContainsKey(parts[1].ToLowerInvariant()))
errorHandler("Ignoring unknown actor type: `{0}`".F(parts[1].ToLowerInvariant())); errorHandler("Ignoring unknown actor type: `{0}`".F(parts[1].ToLowerInvariant()));
@@ -415,8 +417,8 @@ namespace OpenRA.Utility
{ {
// loc=type,loc,depth // loc=type,loc,depth
var parts = s.Value.Split(','); var parts = s.Value.Split(',');
var loc = int.Parse(parts[1]); var loc = Exts.ParseIntegerInvariant(parts[1]);
map.Smudges.Value.Add(new SmudgeReference(parts[0].ToLowerInvariant(), new int2(loc % mapSize, loc / mapSize), int.Parse(parts[2]))); map.Smudges.Value.Add(new SmudgeReference(parts[0].ToLowerInvariant(), new int2(loc % mapSize, loc / mapSize), Exts.ParseIntegerInvariant(parts[2])));
} }
} }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -42,7 +42,7 @@ namespace OpenRA.Utility
static void ConvertPxToRange(ref string input, int scaleMult, int scaleDiv) static void ConvertPxToRange(ref string input, int scaleMult, int scaleDiv)
{ {
var value = int.Parse(input); var value = Exts.ParseIntegerInvariant(input);
var ts = Game.modData.Manifest.TileSize; var ts = Game.modData.Manifest.TileSize;
var world = value * 1024 * scaleMult / (scaleDiv * ts.Height); var world = value * 1024 * scaleMult / (scaleDiv * ts.Height);
var cells = world / 1024; var cells = world / 1024;
@@ -337,7 +337,7 @@ namespace OpenRA.Utility
public static void UpgradeMap(string[] args) public static void UpgradeMap(string[] args)
{ {
var map = new Map(args[1]); var map = new Map(args[1]);
var engineDate = int.Parse(args[2]); var engineDate = Exts.ParseIntegerInvariant(args[2]);
Game.modData = new ModData(map.RequiresMod); Game.modData = new ModData(map.RequiresMod);
UpgradeWeaponRules(engineDate, ref map.Weapons, null, 0); UpgradeWeaponRules(engineDate, ref map.Weapons, null, 0);
@@ -349,7 +349,7 @@ namespace OpenRA.Utility
public static void UpgradeMod(string[] args) public static void UpgradeMod(string[] args)
{ {
var mod = args[1]; var mod = args[1];
var engineDate = int.Parse(args[2]); var engineDate = Exts.ParseIntegerInvariant(args[2]);
Game.modData = new ModData(mod); Game.modData = new ModData(mod);
Game.modData.MapCache.LoadMaps(); Game.modData.MapCache.LoadMaps();

View File

@@ -343,7 +343,7 @@ Weapons:
Warhead: Warhead:
Spread: 426 Spread: 426
Versus: Versus:
None: 40%, None: 40%
Light: 30% Light: 30%
Heavy: 30% Heavy: 30%
Explosion: large_explosion Explosion: large_explosion