diff --git a/sources/Global.uc b/sources/Global.uc
index da1c9e1..c7b6767 100644
--- a/sources/Global.uc
+++ b/sources/Global.uc
@@ -29,7 +29,7 @@ var public RefAPI ref;
var public BoxAPI box;
var public LoggerAPI logger;
var public CollectionsAPI collections;
-//var public JSONAPI json;
+var public UnrealAPI unreal;
var public AliasesAPI alias;
var public TextAPI text;
var public MemoryAPI memory;
@@ -59,6 +59,7 @@ protected function Initialize()
box = BoxAPI(memory.Allocate(class'BoxAPI'));
text = TextAPI(memory.Allocate(class'TextAPI'));
collections = CollectionsAPI(memory.Allocate(class'CollectionsAPI'));
+ unreal = UnrealAPI(memory.Allocate(class'UnrealAPI'));
logger = LoggerAPI(memory.Allocate(class'LoggerAPI'));
alias = AliasesAPI(memory.Allocate(class'AliasesAPI'));
console = ConsoleAPI(memory.Allocate(class'ConsoleAPI'));
diff --git a/sources/Manifest.uc b/sources/Manifest.uc
index 921527a..d0fdf86 100644
--- a/sources/Manifest.uc
+++ b/sources/Manifest.uc
@@ -34,19 +34,20 @@ defaultproperties
testCases(0) = class'TEST_Base'
testCases(1) = class'TEST_Boxes'
testCases(2) = class'TEST_Refs'
- testCases(3) = class'TEST_Aliases'
- testCases(4) = class'TEST_ColorAPI'
- testCases(5) = class'TEST_Text'
- testCases(6) = class'TEST_TextAPI'
- testCases(7) = class'TEST_Parser'
- testCases(8) = class'TEST_JSON'
- testCases(9) = class'TEST_TextCache'
- testCases(10) = class'TEST_User'
- testCases(11) = class'TEST_Memory'
- testCases(12) = class'TEST_DynamicArray'
- testCases(13) = class'TEST_AssociativeArray'
- testCases(14) = class'TEST_Iterator'
- testCases(15) = class'TEST_Command'
- testCases(16) = class'TEST_CommandDataBuilder'
- testCases(17) = class'TEST_LogMessage'
+ testCases(3) = class'TEST_UnrealAPI'
+ testCases(4) = class'TEST_Aliases'
+ testCases(5) = class'TEST_ColorAPI'
+ testCases(6) = class'TEST_Text'
+ testCases(7) = class'TEST_TextAPI'
+ testCases(8) = class'TEST_Parser'
+ testCases(9) = class'TEST_JSON'
+ testCases(10) = class'TEST_TextCache'
+ testCases(11) = class'TEST_User'
+ testCases(12) = class'TEST_Memory'
+ testCases(13) = class'TEST_DynamicArray'
+ testCases(14) = class'TEST_AssociativeArray'
+ testCases(15) = class'TEST_Iterator'
+ testCases(16) = class'TEST_Command'
+ testCases(17) = class'TEST_CommandDataBuilder'
+ testCases(18) = class'TEST_LogMessage'
}
\ No newline at end of file
diff --git a/sources/Unreal/Tests/MockGameRulesA.uc b/sources/Unreal/Tests/MockGameRulesA.uc
new file mode 100644
index 0000000..8ce9444
--- /dev/null
+++ b/sources/Unreal/Tests/MockGameRulesA.uc
@@ -0,0 +1,25 @@
+/**
+ * Mock `GameRules` class for testing `UnrealAPI` and it' methods for
+ * adding / removing `GameRules`.
+ * Copyright 2021 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Acedia. If not, see .
+ */
+class MockGameRulesA extends GameRules;
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Unreal/Tests/MockGameRulesB.uc b/sources/Unreal/Tests/MockGameRulesB.uc
new file mode 100644
index 0000000..b90c33f
--- /dev/null
+++ b/sources/Unreal/Tests/MockGameRulesB.uc
@@ -0,0 +1,25 @@
+/**
+ * Mock `GameRules` class for testing `UnrealAPI` and it's methods for
+ * adding / removing `GameRules`.
+ * Copyright 2021 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Acedia. If not, see .
+ */
+class MockGameRulesB extends GameRules;
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Unreal/Tests/MockInventoryA.uc b/sources/Unreal/Tests/MockInventoryA.uc
new file mode 100644
index 0000000..1f0c5f2
--- /dev/null
+++ b/sources/Unreal/Tests/MockInventoryA.uc
@@ -0,0 +1,25 @@
+/**
+ * Mock inventory class for testing `UnrealAPI` and it's methods for
+ * adding / removing `GameRules`.
+ * Copyright 2021 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Acedia. If not, see .
+ */
+class MockInventoryA extends Inventory;
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Unreal/Tests/MockInventoryAChild.uc b/sources/Unreal/Tests/MockInventoryAChild.uc
new file mode 100644
index 0000000..9ae493b
--- /dev/null
+++ b/sources/Unreal/Tests/MockInventoryAChild.uc
@@ -0,0 +1,25 @@
+/**
+ * Mock inventory class for testing `UnrealAPI` and it's methods for
+ * adding / removing `GameRules`.
+ * Copyright 2021 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Acedia. If not, see .
+ */
+class MockInventoryAChild extends MockInventoryA;
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Unreal/Tests/MockInventoryB.uc b/sources/Unreal/Tests/MockInventoryB.uc
new file mode 100644
index 0000000..49ba579
--- /dev/null
+++ b/sources/Unreal/Tests/MockInventoryB.uc
@@ -0,0 +1,25 @@
+/**
+ * Mock inventory class for testing `UnrealAPI` and it's methods for
+ * adding / removing `GameRules`.
+ * Copyright 2021 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Acedia. If not, see .
+ */
+class MockInventoryB extends Inventory;
+
+defaultproperties
+{
+}
\ No newline at end of file
diff --git a/sources/Unreal/Tests/TEST_UnrealAPI.uc b/sources/Unreal/Tests/TEST_UnrealAPI.uc
new file mode 100644
index 0000000..a3fa8b5
--- /dev/null
+++ b/sources/Unreal/Tests/TEST_UnrealAPI.uc
@@ -0,0 +1,230 @@
+/**
+ * Set of tests for `UnrealAPI` class.
+ * Copyright 2021 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Acedia. If not, see .
+ */
+class TEST_UnrealAPI extends TestCase;
+
+protected static function int CountRulesAmount(class gameRulesClass)
+{
+ local int counter;
+ local GameRules rulesIter;
+ if (gameRulesClass == none) {
+ return 0;
+ }
+ rulesIter = __().unreal.GetGameType().gameRulesModifiers;
+ while (rulesIter != none)
+ {
+ if (rulesIter.class == gameRulesClass) {
+ counter += 1;
+ }
+ rulesIter = rulesIter.nextGameRules;
+ }
+ return counter;
+}
+
+protected static function TESTS()
+{
+ Test_GameType();
+ Test_GameRules();
+ Test_InventoryChainFetching();
+}
+
+protected static function Test_GameType()
+{
+ Context("Testing methods for returning `GameType` class.");
+ Issue("`GetGameType()` returns `none`.");
+ TEST_ExpectNotNone(__().unreal.GetGameType());
+ Issue("`GetKFGameType()` returns `none`.");
+ TEST_ExpectNotNone(__().unreal.GetKFGameType());
+ Issue("`GetGameType()` and `GetKFGameType()` return different values.");
+ TEST_ExpectTrue(__().unreal.GetGameType() == __().unreal.GetKFGameType());
+}
+
+protected static function Test_GameRules()
+{
+ Context("Testing methods for working with `GameRules`.");
+ SubTest_AddRemoveGameRules();
+ SubTest_CheckGameRules();
+}
+
+protected static function SubTest_AddRemoveGameRules()
+{
+ Issue("`AddGameRules()` does not add game rules.");
+ __().unreal.AddGameRules(class'MockGameRulesA');
+ TEST_ExpectTrue(CountRulesAmount(class'MockGameRulesA') == 1);
+
+ __().unreal.AddGameRules(class'MockGameRulesA');
+ Issue("Calling `AddGameRules()` twice leads to rule duplication.");
+ TEST_ExpectFalse(CountRulesAmount(class'MockGameRulesA') > 1);
+ Issue("Calling `AddGameRules()` leads to rule not being added.");
+ TEST_ExpectFalse(CountRulesAmount(class'MockGameRulesA') == 0);
+
+ Issue("Adding new rules with `AddGameRules()` does not work properly.");
+ __().unreal.AddGameRules(class'MockGameRulesB');
+ TEST_ExpectTrue(CountRulesAmount(class'MockGameRulesA') == 1);
+ TEST_ExpectTrue(CountRulesAmount(class'MockGameRulesB') == 1);
+
+ Issue("Adding/removing rules with `RemoveGameRules()` leads to" @
+ "unexpected results.");
+ __().unreal.RemoveGameRules(class'MockGameRulesB');
+ TEST_ExpectTrue(CountRulesAmount(class'MockGameRulesA') == 1);
+ TEST_ExpectTrue(CountRulesAmount(class'MockGameRulesB') == 0);
+ __().unreal.AddGameRules(class'MockGameRulesB');
+ __().unreal.RemoveGameRules(class'MockGameRulesA');
+ TEST_ExpectTrue(CountRulesAmount(class'MockGameRulesA') == 0);
+ TEST_ExpectTrue(CountRulesAmount(class'MockGameRulesB') == 1);
+ __().unreal.RemoveGameRules(class'MockGameRulesB');
+ TEST_ExpectTrue(CountRulesAmount(class'MockGameRulesA') == 0);
+ TEST_ExpectTrue(CountRulesAmount(class'MockGameRulesB') == 0);
+}
+
+protected static function SubTest_CheckGameRules()
+{
+ local string issueForAdded, issueForNotAdded;
+ issueForAdded = "`AreGameRulesAdded()` returns `false` for rules that are" @
+ "currently added.";
+ issueForNotAdded = "`AreGameRulesAdded()` returns `true` for rules that" @
+ "are not currently added.";
+ __().unreal.RemoveGameRules(class'MockGameRulesA');
+ __().unreal.RemoveGameRules(class'MockGameRulesB');
+ Issue(issueForNotAdded);
+ TEST_ExpectFalse(__().unreal.AreGameRulesAdded(class'MockGameRulesA'));
+ TEST_ExpectFalse(__().unreal.AreGameRulesAdded(class'MockGameRulesB'));
+
+ __().unreal.AddGameRules(class'MockGameRulesB');
+ Issue(issueForNotAdded);
+ TEST_ExpectFalse(__().unreal.AreGameRulesAdded(class'MockGameRulesA'));
+ Issue(issueForAdded);
+ TEST_ExpectTrue(__().unreal.AreGameRulesAdded(class'MockGameRulesB'));
+
+ __().unreal.AddGameRules(class'MockGameRulesA');
+ Issue(issueForAdded);
+ TEST_ExpectTrue(__().unreal.AreGameRulesAdded(class'MockGameRulesA'));
+ TEST_ExpectTrue(__().unreal.AreGameRulesAdded(class'MockGameRulesB'));
+
+ __().unreal.RemoveGameRules(class'MockGameRulesB');
+ Issue(issueForAdded);
+ TEST_ExpectTrue(__().unreal.AreGameRulesAdded(class'MockGameRulesA'));
+ Issue(issueForNotAdded);
+ TEST_ExpectFalse(__().unreal.AreGameRulesAdded(class'MockGameRulesB'));
+}
+
+protected static function Test_InventoryChainFetching()
+{
+ local Inventory chainStart, chainEnd;
+ // a - B - A - a - B - a - A,
+ // where A = `MockInventoryA`
+ // a = `MockInventoryAChild`
+ // B = `MockInventoryB`
+ chainStart = Inventory(__().memory.Allocate(class'MockInventoryAChild'));
+ chainEnd = chainStart;
+ chainEnd.inventory = Inventory(__().memory.Allocate(class'MockInventoryB'));
+ chainEnd = chainEnd.inventory;
+ chainEnd.inventory = Inventory(__().memory.Allocate(class'MockInventoryA'));
+ chainEnd = chainEnd.inventory;
+ chainEnd.inventory = Inventory(__().memory.Allocate(class'MockInventoryAChild'));
+ chainEnd = chainEnd.inventory;
+ chainEnd.inventory = Inventory(__().memory.Allocate(class'MockInventoryB'));
+ chainEnd = chainEnd.inventory;
+ chainEnd.inventory =
+ Inventory(__().memory.Allocate(class'MockInventoryAChild'));
+ chainEnd = chainEnd.inventory;
+ chainEnd.inventory = Inventory(__().memory.Allocate(class'MockInventoryA'));
+ chainEnd = chainEnd.inventory;
+ Context("Testing auxiliary methods for working with inventory chains.");
+ SubTest_InventoryChainFetchingSingle(chainStart);
+ SubTest_InventoryChainFetchingMany(chainStart);
+}
+
+protected static function SubTest_InventoryChainFetchingSingle(Inventory chain)
+{
+ Issue("Does not find correct first entry inside the inventory chain.");
+ TEST_ExpectTrue(
+ __().unreal.GetInventoryFrom(class'MockInventoryA', chain)
+ == chain.inventory.inventory);
+ TEST_ExpectTrue(
+ __().unreal.GetInventoryFrom(class'MockInventoryB', chain)
+ == chain.inventory);
+ TEST_ExpectTrue(
+ __().unreal.GetInventoryFrom(class'MockInventoryAChild', chain)
+ == chain);
+
+ Issue("Incorrectly finds missing inventory entries.");
+ TEST_ExpectNone(__().unreal.GetInventoryFrom(none, chain));
+ TEST_ExpectNone(__().unreal.GetInventoryFrom(class'Winchester', chain));
+
+ Issue("Does not find correct first entry inside the inventory chain when" @
+ "allowing for child classes.");
+ TEST_ExpectTrue(
+ __().unreal.GetInventoryFrom(class'MockInventoryA', chain, true)
+ == chain);
+ TEST_ExpectTrue(
+ __().unreal.GetInventoryFrom(class'MockInventoryB', chain, true)
+ == chain.inventory);
+ TEST_ExpectTrue(
+ __().unreal.GetInventoryFrom(class'MockInventoryAChild', chain, true)
+ == chain);
+
+ Issue("Incorrectly finds missing inventory entries when allowing for" @
+ "child classes.");
+ TEST_ExpectNone(__().unreal.GetInventoryFrom(none, chain, true));
+ TEST_ExpectNone(__().unreal.GetInventoryFrom( class'Winchester', chain,
+ true));
+}
+
+protected static function SubTest_InventoryChainFetchingMany(Inventory chain)
+{
+ local array result;
+ Issue("Does not find correct entries inside the inventory chain.");
+ result = __().unreal.GetAllInventoryFrom(class'MockInventoryB', chain);
+ TEST_ExpectTrue(result.length == 2);
+ TEST_ExpectTrue(result[0] == chain.inventory);
+ TEST_ExpectTrue(result[1] == chain.inventory.inventory.inventory.inventory);
+
+ Issue("Does not find correct entries inside the inventory chain when" @
+ "allowing for child classes.");
+ result =
+ __().unreal.GetAllInventoryFrom(class'MockInventoryB', chain, true);
+ TEST_ExpectTrue(result.length == 2);
+ TEST_ExpectTrue(result[0] == chain.inventory);
+ TEST_ExpectTrue(result[1] == chain.inventory.inventory.inventory.inventory);
+ result =
+ __().unreal.GetAllInventoryFrom(class'MockInventoryA', chain, true);
+ TEST_ExpectTrue(result.length == 5);
+ TEST_ExpectTrue(result[0] == chain);
+ TEST_ExpectTrue(result[1] == chain.inventory.inventory);
+ TEST_ExpectTrue(result[2] == chain.inventory.inventory.inventory);
+ TEST_ExpectTrue(
+ result[3]
+ == chain.inventory.inventory.inventory.inventory.inventory);
+ TEST_ExpectTrue(
+ result[4]
+ == chain.inventory.inventory.inventory.inventory.inventory.inventory);
+
+ Issue("Does not return empty array for non-existing inventory class.");
+ result = __().unreal.GetAllInventoryFrom(class'Winchester', chain);
+ TEST_ExpectTrue(result.length == 0);
+ result = __().unreal.GetAllInventoryFrom(class'Winchester', chain, true);
+ TEST_ExpectTrue(result.length == 0);
+}
+
+defaultproperties
+{
+ caseName = "UnrealAPI"
+ caseGroup = "Unreal"
+}
\ No newline at end of file
diff --git a/sources/Unreal/UnrealAPI.uc b/sources/Unreal/UnrealAPI.uc
new file mode 100644
index 0000000..63f75dc
--- /dev/null
+++ b/sources/Unreal/UnrealAPI.uc
@@ -0,0 +1,246 @@
+/**
+ * Low-level API that provides set of utility methods for working with
+ * unreal script classes.
+ * Copyright 2021 Anton Tarasenko
+ *------------------------------------------------------------------------------
+ * This file is part of Acedia.
+ *
+ * Acedia is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Acedia is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Acedia. If not, see .
+ */
+class UnrealAPI extends AcediaObject;
+
+/**
+ * Returns current game's `LevelInfo`. Useful because `level` variable
+ * is not defined inside objects.
+ *
+ * @return `LevelInfo` instance for the current game. Guaranteed to
+ * not be `none`.
+ */
+public final function LevelInfo GetLevel()
+{
+ return class'CoreService'.static.GetInstance().level;
+}
+
+/**
+ * Returns current game's `GameInfo`. Useful because `level.game` is not
+ * accessible inside objects.
+ *
+ * @return `GameInfo` instance for the current game. Guaranteed to
+ * not be `none`.
+ */
+public final function GameInfo GetGameType()
+{
+ return class'CoreService'.static.GetInstance().level.game;
+}
+
+/**
+ * Returns current game's `GameInfo` as `KFGameType`. Useful because
+ * `level.game` is not accessible inside objects and because it auto converts
+ * game type to `KFGameType`, which virtually all mods for killing floor use
+ * (by itself or as a base class).
+ *
+ * @return `KFGameType` instance for the current game. Can be `none` only if
+ * game was modded to run a `GameInfo` not derived from `KFGameType`.
+ */
+public final function KFGameType GetKFGameType()
+{
+ return KFGameType(GetGameType());
+}
+
+/**
+ * Returns current local player's `Controller`. Useful because `level`
+ * is not accessible inside objects.
+ *
+ * @return `PlayerController` instance for the local player. `none` iff run on
+ * dedicated servers.
+ */
+public final function PlayerController GetLocalPlayer()
+{
+ return class'CoreService'.static.GetInstance().level
+ .GetLocalPlayerController();
+}
+
+/**
+ * Checks if given class of `GameRules` is currently active in `GameInfo`.
+ *
+ * @param rulesClassToCheck Class of rules to check for.
+ * @return `true` if `GameRules` are active and `false` otherwise.
+ */
+public final function bool AreGameRulesAdded(
+ class rulesClassToCheck)
+{
+ local GameRules rulesIter;
+ if (rulesClassToCheck == none) {
+ return false;
+ }
+ rulesIter = GetGameType().gameRulesModifiers;
+ while (rulesIter != none)
+ {
+ if (rulesIter.class == rulesClassToCheck) {
+ return true;
+ }
+ rulesIter = rulesIter.nextGameRules;
+ }
+ return false;
+}
+
+/**
+ * Adds new `GameRules` class to the current `GameInfo`.
+ * Does nothing if give `GameRules` class was already added before.
+ *
+ * @param newRulesClass Class of rules to add.
+ * @return `true` if `GameRules` were added and `false` otherwise
+ * (because they were already active.)
+ */
+public final function bool AddGameRules(class newRulesClass)
+{
+ if (AreGameRulesAdded(newRulesClass)) {
+ return false;
+ }
+ GetGameType().AddGameModifier(GameRules(_.memory.Allocate(newRulesClass)));
+ return true;
+}
+
+/**
+ * Removes given `GameRules` class from the current `GameInfo`,
+ * if they are active. Does nothing otherwise.
+ *
+ * @param rulesClassToRemove Class of rules to try and remove.
+ * @return `true` if `GameRules` were removed and `false` otherwise
+ * (if they were not active in the first place).
+ */
+public final function bool RemoveGameRules(class rulesClassToRemove)
+{
+ local GameInfo game;
+ local GameRules rulesIter;
+ local GameRules rulesToDestroy;
+ if (rulesClassToRemove == none) return false;
+ game = GetGameType();
+ if (game.gameRulesModifiers == none) return false;
+
+ // Check root rules
+ rulesToDestroy = game.gameRulesModifiers;
+ if (rulesToDestroy.class == rulesClassToRemove)
+ {
+ game.gameRulesModifiers = rulesToDestroy.nextGameRules;
+ rulesToDestroy.Destroy();
+ return true;
+ }
+ // Check rest of the rules
+ rulesIter = game.gameRulesModifiers;
+ while (rulesIter != none)
+ {
+ rulesToDestroy = rulesIter.nextGameRules;
+ if ( rulesToDestroy != none
+ && rulesToDestroy.class == rulesClassToRemove)
+ {
+ rulesIter.nextGameRules = rulesToDestroy.nextGameRules;
+ rulesToDestroy.Destroy();
+ return true;
+ }
+ rulesIter = rulesIter.nextGameRules;
+ }
+ return false;
+}
+
+/**
+ * Convenience method for finding a first inventory entry of the given
+ * class `inventoryClass` in the given inventory chain `inventoryChain`.
+ *
+ * Inventory is stored as a linked list, where next inventory item is available
+ * through the `inventory` reference. This method follows this list, starting
+ * from `inventoryChain` until it finds `Inventory` of the appropriate class
+ * or reaches the end of the list.
+ *
+ * @param inventoryClass Class of the inventory we are interested in.
+ * @param inventoryChain Inventory chain in which we should search for
+ * the given class.
+ * @param acceptChildClass `true` if method should also return any
+ * `Inventory` of class derived from `inventoryClass` and `false` if
+ * we want given class specifically (default).
+ * @return First inventory from `inventoryChain` that matches given
+ * `inventoryClass` class (whether exactly or as a child class,
+ * in case `acceptChildClass == true`).
+ */
+public final function Inventory GetInventoryFrom(
+ class inventoryClass,
+ Inventory inventoryChain,
+ optional bool acceptChildClass)
+{
+ if (inventoryClass == none) {
+ return none;
+ }
+ while (inventoryChain != none)
+ {
+ if (inventoryChain.class == inventoryClass) {
+ return inventoryChain;
+ }
+ if ( acceptChildClass
+ && ClassIsChildOf(inventoryChain.class, inventoryClass))
+ {
+ return inventoryChain;
+ }
+ inventoryChain = inventoryChain.inventory;
+ }
+ return none;
+}
+
+/**
+ * Convenience method for finding a all inventory entries of the given
+ * class `inventoryClass` in the given inventory chain `inventoryChain`.
+ *
+ * Inventory is stored as a linked list, where next inventory item is available
+ * through the `inventory` reference. This method follows this list, starting
+ * from `inventoryChain` until the end of the list.
+ *
+ * @param inventoryClass Class of the inventory we are interested in.
+ * @param inventoryChain Inventory chain in which we should search for
+ * the given class.
+ * @param acceptChildClass `true` if method should also return any
+ * `Inventory` of class derived from `inventoryClass` and `false` if
+ * we want given class specifically (default).
+ * @return Array of inventory items from `inventoryChain` that match given
+ * `inventoryClass` class (whether exactly or as a child class,
+ * in case `acceptChildClass == true`).
+ */
+public final function array GetAllInventoryFrom(
+ class inventoryClass,
+ Inventory inventoryChain,
+ optional bool acceptChildClass)
+{
+ local bool shouldAdd;
+ local array result;
+ if (inventoryClass == none) {
+ return result;
+ }
+ while (inventoryChain != none)
+ {
+ shouldAdd = false;
+ if (inventoryChain.class == inventoryClass) {
+ shouldAdd = true;
+ }
+ else if (acceptChildClass) {
+ shouldAdd = ClassIsChildOf(inventoryChain.class, inventoryClass);
+ }
+ if (shouldAdd) {
+ result[result.length] = inventoryChain;
+ }
+ inventoryChain = inventoryChain.inventory;
+ }
+ return result;
+}
+
+defaultproperties
+{
+}
\ No newline at end of file