From e902db39cdf46412d7337a124b9856c895de9c33 Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Tue, 18 Feb 2020 23:12:30 +0700 Subject: [PATCH] Add `FixDualiesCost` feature This feature addressed two inventory issues: 1. Players carrying amount of weapons that shouldn't be allowed by the weight limit. 2. Players carrying two variants of the same gun. For example carrying both M32 and camo M32. Single and dual version of the same weapon are also considered the same gun, so you can't carry both MK23 and dual MK23 or dual handcannons and golden handcannon. --- .../FixInventoryAbuse/FixInventoryAbuse.uc | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 sources/Features/FixInventoryAbuse/FixInventoryAbuse.uc diff --git a/sources/Features/FixInventoryAbuse/FixInventoryAbuse.uc b/sources/Features/FixInventoryAbuse/FixInventoryAbuse.uc new file mode 100644 index 0000000..819b6b8 --- /dev/null +++ b/sources/Features/FixInventoryAbuse/FixInventoryAbuse.uc @@ -0,0 +1,202 @@ +/** + * This feature addressed two inventory issues: + * 1. Players carrying amount of weapons that shouldn't be allowed by the + * weight limit. + * 2. Players carrying two variants of the same gun. + * For example carrying both M32 and camo M32. + * Single and dual version of the same weapon are also considered + * the same gun, so you can't carry both MK23 and dual MK23 or + * dual handcannons and golden handcannon. + * + * It fixes them by doing repeated checks to find violations of those rules + * and destroys all droppable weapons of people that use this exploit. + * Copyright 2020 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 FixInventoryAbuse extends Feature; + +// How often (in seconds) should we do our inventory validations? +// We shouldn't really worry about performance, but there's also no need to +// do this check too often. +var private config const float checkInterval; + +struct DualiesPair +{ + var class single; + var class dual; +}; +// For this fix to properly work, this array must contain an entry for +// every dual weapon in the game (like pistols, with single and dual versions). +// It's made configurable in case of custom dual weapons. +var private config const array dualiesClasses; + +public function OnEnabled() +{ + local float actualInterval; + actualInterval = checkInterval; + if (actualInterval <= 0) + { + actualInterval = 0.25; + } + SetTimer(actualInterval, true); +} + +public function OnDisabled() +{ + SetTimer(0.0f, false); +} + +// Did player with this controller contribute to the latest dosh generation? +private final function bool IsWeightLimitViolated(KFHumanPawn playerPawn) +{ + if (playerPawn == none) return false; + return (playerPawn.currentWeight > playerPawn.maxCarryWeight); +} + +// Returns a root pickup class. +// For non-dual weapons, root class is defined as either: +// 1. the first variant (reskin), if there are variants for that weapon; +// 2. and as the class itself, if there are no variants. +// For dual weapons (all dual pistols) root class is defined as +// a root of their single version. +// This definition is useful because: +// ~ Vanilla game rules are such that player can only have two weapons +// in the inventory if they have different roots; +// ~ Root is easy to find. +private final function class GetRootPickupClass(KFWeapon weapon) +{ + local int i; + local class root; + if (weapon == none) return none; + // Start with a pickup of the given weapons + root = class(weapon.default.pickupClass); + if (root == none) return none; + + // In case it's a dual version - find corresponding single pickup class + // (it's root would be the same). + for (i = 0; i < dualiesClasses.length; i += 1) + { + if (dualiesClasses[i].dual == root) + { + root = dualiesClasses[i].single; + break; + } + } + // Take either first variant class or the class itself, - + // it's going to be root by definition. + if (root.default.variantClasses.length > 0) + { + root = class(root.default.variantClasses[0]); + } + return root; +} + +// Returns 'true' if passed pawn has two weapons that are just variants of +// each other (they have the same root, see 'GetRootPickupClass'). +private final function bool HasDuplicateGuns(KFHumanPawn playerPawn) +{ + local int i, j; + local Inventory inv; + local KFWeapon nextWeapon; + local class rootClass; + local array< class > rootList; + if (playerPawn == none) return false; + + // First find a root for every weapon in the pawn's inventory. + for (inv = playerPawn.inventory; inv != none; inv = inv.inventory) + { + nextWeapon = KFWeapon(inv); + if (nextWeapon == none) continue; + if (nextWeapon.bKFNeverThrow) continue; + rootClass = GetRootPickupClass(nextWeapon); + if (rootClass != none) + { + rootList[rootList.length] = rootClass; + } + } + // Then just check obtained roots for duplicates. + for (i = 0; i < rootList.length; i += 1) + { + for (j = i + 1; j < rootList.length; j += 1) + { + if (rootList[i] == rootList[j]) + { + return true; + } + } + } + return false; +} + +// Kill the gun devil! +private final function DestroyEverything(KFHumanPawn playerPawn) +{ + local int i; + local Inventory inv; + local KFWeapon nextWeapon; + local array weaponList; + if (playerPawn == none) return; + // Going through the linked list while removing items can be tricky, + // so just find all weapons first. + for (inv = playerPawn.inventory; inv != none; inv = inv.inventory) + { + nextWeapon = KFWeapon(inv); + if (nextWeapon == none) continue; + if (nextWeapon.bKFNeverThrow) continue; + weaponList[weaponList.length] = nextWeapon; + } + // And destroy them later. + for(i = 0; i < weaponList.length; i += 1) + { + weaponList[i].Destroy(); + } +} + +event Timer() +{ + local int i; + local KFHumanPawn nextPawn; + local ConnectionService service; + local array connections; + service = ConnectionService(class'ConnectionService'.static.GetInstance()); + if (service == none) return; + + connections = service.GetActiveConnections(); + for (i = 0; i < connections.length; i += 1) + { + nextPawn = none; + if (connections[i].controllerReference != none) + { + nextPawn = KFHumanPawn(connections[i].controllerReference.pawn); + } + if (IsWeightLimitViolated(nextPawn) || HasDuplicateGuns(nextPawn)) + { + DestroyEverything(nextPawn); + } + } +} + +defaultproperties +{ + checkInterval = 0.25 + dualiesClasses(0)=(single=class'KFMod.SinglePickup',dual=class'KFMod.DualiesPickup') + dualiesClasses(1)=(single=class'KFMod.Magnum44Pickup',dual=class'KFMod.Dual44MagnumPickup') + dualiesClasses(2)=(single=class'KFMod.MK23Pickup',dual=class'KFMod.DualMK23Pickup') + dualiesClasses(3)=(single=class'KFMod.DeaglePickup',dual=class'KFMod.DualDeaglePickup') + dualiesClasses(4)=(single=class'KFMod.GoldenDeaglePickup',dual=class'KFMod.GoldenDualDeaglePickup') + dualiesClasses(5)=(single=class'KFMod.FlareRevolverPickup',dual=class'KFMod.DualFlareRevolverPickup') +} \ No newline at end of file