Skip to content

Commit 16178c2

Browse files
Merge pull request #10 from source-engine-discord/feature/danger-zone-support
Feature/danger zone support
2 parents fae1918 + 70b633c commit 16178c2

File tree

23 files changed

+1145
-603
lines changed

23 files changed

+1145
-603
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<Company>Source Engine Discord</Company>
1616
<Copyright>© 2014 EHVAG, 2020 Source Engine Discord and contributors</Copyright>
1717
<Product>SourceEngine.Demo</Product>
18-
<Version>2.4.0</Version>
18+
<Version>2.5.0</Version>
1919
</PropertyGroup>
2020

2121
<PropertyGroup Label="Package Metadata">

README.md

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ Outputs game statistics from CS:GO demos as JSON. The solution has three project
1414

1515
- Defuse
1616
- Hostage
17-
- Wingman
17+
- Wingman Defuse
18+
- Wingman Hostage
19+
- Danger Zone
1820

1921
## Running IDemO
2022

@@ -29,23 +31,27 @@ Latest release:
2931
### Usage
3032

3133
```
32-
-config [path] Path to config file.
33-
-folders [paths (space seperated)] Processes all demo files in each folder specified.
34-
-demos [paths (space seperated)] Processess a list of single demo files at paths.
35-
-recursive Switch for recursive demo search.
36-
-steaminfo Takes steam names from steam.
37-
-clear Clears the data folder.
38-
-nochickens Disables checks for number of chickens killed when parsing.
39-
-noplayerpositions Disables checks for player positions when parsing.
40-
-samefilename Uses the demo's filename as the output filename.
41-
-samefolderstructure Uses the demo's folder structure inside the root folder for the output json file.
42-
-lowoutputmode Does not print out the progress bar and round completed messages to console.
34+
-config [path] Path to config file.
35+
-folders [paths (space seperated)] Processes all demo files in each folder specified.
36+
-demos [paths (space seperated)] Processess a list of single demo files at paths.
37+
-gamemodeoverride [string] Defines the gamemode for the match instead of having the parser attempt to figure it out. -> (defuse / hostage / wingmandefuse / wingmanhostage / dangerzone)
38+
-testtype [string] Defines the test type for the match. Otherwise it attempts to grab it from the filename in SE Discord's filename formatting. Only matters for defuse and hostage. -> (competitive / casual)
39+
-testdateoverride [string] Defines the test date of the match. Otherwise it attempts to grab it from the filename. -> (dd/mm/yyyy)
40+
-hostagerescuezonecountoverride [int] Defines the number of hostage rescue zones in the map. Without this, the parser assumes hostage has 1 and danger zone has 2 -> (0-2)
41+
-recursive Switch for recursive demo search.
42+
-steaminfo Takes steam names from steam.
43+
-clear Clears the data folder.
44+
-nochickens Disables checks for number of chickens killed when parsing.
45+
-noplayerpositions Disables checks for player positions when parsing.
46+
-samefilename Uses the demo's filename as the output filename.
47+
-samefolderstructure Uses the demo's folder structure inside the root folder for the output json file.
48+
-lowoutputmode Does not print out the progress bar and round completed messages to console.
4349
```
4450

4551
Example:
4652

4753
```
48-
IDemO -folders "demos" -output "parsed" -recursive -nochickens -noplayerpositions -samefilename -samefolderstructure
54+
IDemO -folders "demos" -output "parsed" -recursive -nochickens -noplayerpositions -samefilename -samefolderstructure -gamemodeoverride "defuse" -testtype "competitive" -testdateoverride "28/02/2021"
4955
```
5056

5157
## Development
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Collections.Generic;
2+
3+
namespace SourceEngine.Demo.Parser.Constants
4+
{
5+
public static class Gamemodes
6+
{
7+
public const string Defuse = "defuse";
8+
public const string Hostage = "hostage";
9+
public const string WingmanDefuse = "wingmandefuse";
10+
public const string WingmanHostage = "wingmanhostage";
11+
public const string DangerZone = "dangerzone";
12+
13+
public const string Unknown = "unknown";
14+
15+
public static List<string> GetAll()
16+
{
17+
return new List<string>() { Defuse, Hostage, WingmanDefuse, WingmanHostage, DangerZone };
18+
}
19+
20+
public static List<string> HaveBombsites()
21+
{
22+
return new List<string>() { Defuse, WingmanDefuse, Unknown };
23+
}
24+
25+
public static List<string> HaveHostages()
26+
{
27+
return new List<string>() { Hostage, WingmanHostage, DangerZone, Unknown };
28+
}
29+
}
30+
}

src/SourceEngine.Demo.Parser/DP/Handler/GameEventHandler.cs

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -512,26 +512,43 @@ public static void Apply(GameEvent rawEvent, DemoParser parser, bool parseChicke
512512

513513
int site = (int)data["site"];
514514

515-
//works out which bombsite the bomb was planted at
516-
if (site == parser.bombsiteAIndex) {
517-
bombEventArgs.Site = 'A';
518-
} else if (site == parser.bombsiteBIndex) {
519-
bombEventArgs.Site = 'B';
520-
} else {
521-
var relevantTrigger = parser.triggers.Single(a => a.Index == site);
522-
if (relevantTrigger.Contains(parser.bombsiteACenter)) {
523-
//planted at A.
524-
bombEventArgs.Site = 'A';
525-
parser.bombsiteAIndex = site;
526-
} else if (relevantTrigger.Contains(parser.bombsiteBCenter)) {
527-
//planted at B.
528-
bombEventArgs.Site = 'B';
529-
parser.bombsiteBIndex = site;
530-
} else {
531-
//where have they planted???
532-
bombEventArgs.Site = '?';
533-
}
534-
}
515+
//works out which bombsite the bomb was at
516+
if (site <= 0)
517+
{
518+
bombEventArgs.Site = null; // bomb at no bombsite, likely danger zone
519+
}
520+
else
521+
{
522+
if (site == parser.bombsiteAIndex)
523+
{
524+
bombEventArgs.Site = 'A';
525+
}
526+
else if (site == parser.bombsiteBIndex)
527+
{
528+
bombEventArgs.Site = 'B';
529+
}
530+
else
531+
{
532+
var relevantTrigger = parser.triggers.Single(a => a.Index == site);
533+
if (relevantTrigger.Contains(parser.bombsiteACenter))
534+
{
535+
//planted at A.
536+
bombEventArgs.Site = 'A';
537+
parser.bombsiteAIndex = site;
538+
}
539+
else if (relevantTrigger.Contains(parser.bombsiteBCenter))
540+
{
541+
//planted at B.
542+
bombEventArgs.Site = 'B';
543+
parser.bombsiteBIndex = site;
544+
}
545+
else
546+
{
547+
//where have they planted since 'site' was not 0 ???
548+
bombEventArgs.Site = '?';
549+
}
550+
}
551+
}
535552

536553
bombEventArgs.TimeInRound = parser.CurrentTime - timestampFreezetimeEnded;
537554

src/SourceEngine.Demo.Parser/DemoParser.cs

Lines changed: 98 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using System.Linq;
55
using System.Text;
66
using System.Threading;
7-
7+
using SourceEngine.Demo.Parser.Constants;
88
using SourceEngine.Demo.Parser.DP;
99
using SourceEngine.Demo.Parser.DT;
1010
using SourceEngine.Demo.Parser.ST;
@@ -29,6 +29,11 @@ public class DemoParser : IDisposable
2929
public bool stopParsingDemo = false;
3030
bool parseChickens = true;
3131
bool parsePlayerPositions = true;
32+
string gamemode = string.Empty;
33+
int numOfHostageRescueZonesLookingFor = 0; // this MAY work up to 4 (since it uses 000, 001, 002 & 003)
34+
35+
public List<BoundingBoxInformation> triggers = new List<BoundingBoxInformation>();
36+
internal Dictionary<int, Player> InfernoOwners = new Dictionary<int, Player>();
3237

3338

3439
#region Events
@@ -387,7 +392,7 @@ public IEnumerable<Player> PlayingParticipants {
387392
public int hostageAIndex { get; internal set; } = -1;
388393
public int hostageBIndex { get; internal set; } = -1;
389394
public int rescueZoneIndex { get; internal set; } = -1;
390-
public Vector rescueZoneCenter { get; internal set; } = new Vector();
395+
public Dictionary<int, Vector> rescueZoneCenters { get; internal set; } = new Dictionary<int, Vector>();
391396

392397
/// <summary>
393398
/// The ID of the CT-Team
@@ -542,7 +547,7 @@ public float ParsingProgess {
542547
/// Hint: ParseHeader() is propably what you want to look into next.
543548
/// </summary>
544549
/// <param name="input">An input-stream.</param>
545-
public DemoParser(Stream input, bool parseChickens = true, bool parsePlayerPositions = true)
550+
public DemoParser(Stream input, bool parseChickens = true, bool parsePlayerPositions = true, string gamemode = "", int hostagerescuezonecountoverride = 0)
546551
{
547552
BitStream = BitStreamUtil.Create(input);
548553

@@ -552,6 +557,25 @@ public DemoParser(Stream input, bool parseChickens = true, bool parsePlayerPosit
552557

553558
this.parseChickens = parseChickens;
554559
this.parsePlayerPositions = parsePlayerPositions;
560+
this.gamemode = gamemode;
561+
562+
// automatically decides rescue zone amounts unless overridden with a provided parameter
563+
if (hostagerescuezonecountoverride > 0)
564+
{
565+
this.numOfHostageRescueZonesLookingFor = hostagerescuezonecountoverride;
566+
}
567+
else if (gamemode == Gamemodes.DangerZone)
568+
{
569+
this.numOfHostageRescueZonesLookingFor = 2;
570+
}
571+
else if (gamemode == Gamemodes.Hostage)
572+
{
573+
this.numOfHostageRescueZonesLookingFor = 1;
574+
}
575+
else
576+
{
577+
this.numOfHostageRescueZonesLookingFor = 0;
578+
}
555579
}
556580

557581

@@ -1090,13 +1114,14 @@ private void MapEquipment()
10901114
}
10911115
}
10921116

1093-
// Check the name iff the above switch matches nothing.
1117+
// Check the name iff the above switch matches nothing. (usually only things that the player can hold that are neither a weapon nor a grenade (?))
10941118
switch (sc.Name) {
10951119
case "CC4":
10961120
// Bomb is neither "ratatata" nor "boom", its "booooooom".
10971121
equipmentMapping.Add(sc, EquipmentElement.Bomb);
10981122
break;
10991123
case "CKnife":
1124+
case "CKnifeGG":
11001125
// tsching weapon
11011126
equipmentMapping.Add(sc, EquipmentElement.Knife);
11021127
break;
@@ -1112,6 +1137,18 @@ private void MapEquipment()
11121137
case "CItem_Healthshot":
11131138
equipmentMapping.Add(sc, EquipmentElement.HealthShot);
11141139
break;
1140+
case "CFists":
1141+
equipmentMapping.Add(sc, EquipmentElement.Fists);
1142+
break;
1143+
case "CMelee":
1144+
equipmentMapping.Add(sc, EquipmentElement.Melee);
1145+
break;
1146+
case "CTablet":
1147+
equipmentMapping.Add(sc, EquipmentElement.Tablet);
1148+
break;
1149+
case "CBumpMine":
1150+
equipmentMapping.Add(sc, EquipmentElement.BumpMine);
1151+
break;
11151152
}
11161153
}
11171154
}
@@ -1217,9 +1254,12 @@ void HandleWeapon (object sender, EntityCreatedEventArgs e)
12171254
}
12181255
}
12191256

1220-
public List<BoundingBoxInformation> triggers = new List<BoundingBoxInformation>();
1257+
12211258
public void HandleBombSitesAndRescueZones()
12221259
{
1260+
List<int> rescueZoneIdsDoneAtLeastOnceMin = new List<int>(), rescueZoneIdsDoneAtLeastOnceMax = new List<int>();
1261+
1262+
12231263
SendTableParser.FindByName("CCSPlayerResource").OnNewEntity += (s1, newResource) => {
12241264
// defuse
12251265
newResource.Entity.FindProperty("m_bombsiteCenterA").VectorRecived += (s2, center) => {
@@ -1229,28 +1269,48 @@ public void HandleBombSitesAndRescueZones()
12291269
bombsiteBCenter = center.Value;
12301270
};
12311271

1232-
// hostage (for multiple hostage rescue zones, use 001, 002 and 003)
1233-
newResource.Entity.FindProperty("m_hostageRescueX.000").DataRecivedDontUse += (s4, center) =>
1272+
1273+
// hostage (for multiple hostage rescue zones it uses 000, 001, 002 & 003 (how many of them depends on value of numOfHostageRescueZones))
1274+
int numOfSortedRescueZonesX = 0, numOfSortedZonesRescueY = 0, numOfSortedZonesRescueZ = 0;
1275+
for (int i = 0; i < numOfHostageRescueZonesLookingFor; i++)
12341276
{
1235-
if (!(rescueZoneCenter.X != 0 && Convert.ToSingle(center.Value) == 0))
1277+
newResource.Entity.FindProperty("m_hostageRescueX.00" + i).DataRecivedDontUse += (s4, center) =>
12361278
{
1237-
rescueZoneCenter.X = Convert.ToSingle(center.Value);
1238-
}
1239-
};
1240-
newResource.Entity.FindProperty("m_hostageRescueY.000").DataRecivedDontUse += (s5, center) =>
1241-
{
1242-
if (!(rescueZoneCenter.Y != 0 && Convert.ToSingle(center.Value) == 0))
1279+
if (!rescueZoneCenters.Keys.Contains(numOfSortedRescueZonesX))
1280+
rescueZoneCenters.Add(numOfSortedRescueZonesX, new Vector());
1281+
1282+
// make sure that there are values before saying it is sorted, as it will often run through with 0 values first
1283+
if (!(rescueZoneCenters[numOfSortedRescueZonesX].X == 0 && Convert.ToSingle(center.Value) == 0))
1284+
{
1285+
rescueZoneCenters[numOfSortedRescueZonesX].X = Convert.ToSingle(center.Value);
1286+
numOfSortedRescueZonesX++;
1287+
}
1288+
};
1289+
newResource.Entity.FindProperty("m_hostageRescueY.00" + i).DataRecivedDontUse += (s5, center) =>
12431290
{
1244-
rescueZoneCenter.Y = Convert.ToSingle(center.Value);
1245-
}
1246-
};
1247-
newResource.Entity.FindProperty("m_hostageRescueZ.000").DataRecivedDontUse += (s6, center) =>
1248-
{
1249-
if (!(rescueZoneCenter.Z != 0 && Convert.ToSingle(center.Value) == 0))
1291+
if (!rescueZoneCenters.Keys.Contains(numOfSortedZonesRescueY))
1292+
rescueZoneCenters.Add(numOfSortedZonesRescueY, new Vector());
1293+
1294+
// make sure that there are values before saying it is sorted, as it will often run through with 0 values first
1295+
if (!(rescueZoneCenters[numOfSortedZonesRescueY].Y == 0 && Convert.ToSingle(center.Value) == 0))
1296+
{
1297+
rescueZoneCenters[numOfSortedZonesRescueY].Y = Convert.ToSingle(center.Value);
1298+
numOfSortedZonesRescueY++;
1299+
}
1300+
};
1301+
newResource.Entity.FindProperty("m_hostageRescueZ.00" + i).DataRecivedDontUse += (s6, center) =>
12501302
{
1251-
rescueZoneCenter.Z = Convert.ToSingle(center.Value);
1252-
}
1253-
};
1303+
if (!rescueZoneCenters.Keys.Contains(numOfSortedZonesRescueZ))
1304+
rescueZoneCenters.Add(numOfSortedZonesRescueZ, new Vector());
1305+
1306+
// make sure that there are values before saying it is sorted, as it will often run through with 0 values first
1307+
if (!(rescueZoneCenters[numOfSortedZonesRescueZ].Z == 0 && Convert.ToSingle(center.Value) == 0))
1308+
{
1309+
rescueZoneCenters[numOfSortedZonesRescueZ].Z = Convert.ToSingle(center.Value);
1310+
numOfSortedZonesRescueZ++;
1311+
}
1312+
};
1313+
}
12541314
};
12551315

12561316
SendTableParser.FindByName("CBaseTrigger").OnNewEntity += (s1, newResource) => {
@@ -1261,13 +1321,16 @@ public void HandleBombSitesAndRescueZones()
12611321
// if bombsites, it gets x,y,z values from the world origin (0,0,0)
12621322
// if hostage rescue zones, it gets x,y,z values relative to the entity's origin
12631323
newResource.Entity.FindProperty("m_Collision.m_vecMins").VectorRecived += (s2, vector) => {
1264-
if (bombsiteACenter.Absolute == 0 && bombsiteBCenter.Absolute == 0) // is hostage
1324+
if (bombsiteACenter.Absolute == 0 && bombsiteBCenter.Absolute == 0) // is hostage or danger zone
12651325
{
1326+
if (!rescueZoneIdsDoneAtLeastOnceMin.Any(x => x == trigger.Index))
1327+
rescueZoneIdsDoneAtLeastOnceMin.Add(trigger.Index);
1328+
12661329
trigger.Min = new Vector()
12671330
{
1268-
X = rescueZoneCenter.X + vector.Value.X,
1269-
Y = rescueZoneCenter.Y + vector.Value.Y,
1270-
Z = rescueZoneCenter.Z + vector.Value.Z,
1331+
X = rescueZoneCenters[rescueZoneIdsDoneAtLeastOnceMin.Count() - 1].X + vector.Value.X,
1332+
Y = rescueZoneCenters[rescueZoneIdsDoneAtLeastOnceMin.Count() - 1].Y + vector.Value.Y,
1333+
Z = rescueZoneCenters[rescueZoneIdsDoneAtLeastOnceMin.Count() - 1].Z + vector.Value.Z,
12711334
};
12721335
}
12731336
else // is defuse
@@ -1277,13 +1340,16 @@ public void HandleBombSitesAndRescueZones()
12771340
};
12781341

12791342
newResource.Entity.FindProperty("m_Collision.m_vecMaxs").VectorRecived += (s3, vector) => {
1280-
if (bombsiteACenter.Absolute == 0 && bombsiteBCenter.Absolute == 0) // is hostage
1343+
if (bombsiteACenter.Absolute == 0 && bombsiteBCenter.Absolute == 0) // is hostage or danger zone
12811344
{
1345+
if (!rescueZoneIdsDoneAtLeastOnceMax.Any(x => x == trigger.Index))
1346+
rescueZoneIdsDoneAtLeastOnceMax.Add(trigger.Index);
1347+
12821348
trigger.Max = new Vector()
12831349
{
1284-
X = rescueZoneCenter.X + vector.Value.X,
1285-
Y = rescueZoneCenter.Y + vector.Value.Y,
1286-
Z = rescueZoneCenter.Z + vector.Value.Z,
1350+
X = rescueZoneCenters[rescueZoneIdsDoneAtLeastOnceMax.Count() - 1].X + vector.Value.X,
1351+
Y = rescueZoneCenters[rescueZoneIdsDoneAtLeastOnceMax.Count() - 1].Y + vector.Value.Y,
1352+
Z = rescueZoneCenters[rescueZoneIdsDoneAtLeastOnceMax.Count() - 1].Z + vector.Value.Z,
12871353
};
12881354
}
12891355
else // is defuse
@@ -1295,7 +1361,7 @@ public void HandleBombSitesAndRescueZones()
12951361

12961362
}
12971363

1298-
internal Dictionary<int, Player> InfernoOwners = new Dictionary<int, Player>();
1364+
12991365
private void HandleInfernos()
13001366
{
13011367
var inferno = SendTableParser.FindByName("CInferno");

0 commit comments

Comments
 (0)