/** * 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 { }