Browse Source

Add prototype interfaces for inventory management

pull/8/head
Anton Tarasenko 3 years ago
parent
commit
f18180a8f8
  1. 14
      sources/Gameplay/BaseClasses/KillingFloor/KFFrontend.uc
  2. 417
      sources/Gameplay/KF1Frontend/BaseImplementation/EKFInventory.uc
  3. 81
      sources/Gameplay/KF1Frontend/BaseImplementation/EKFItemTemplateInfo.uc
  4. 150
      sources/Gameplay/KF1Frontend/BaseImplementation/EKFWeapon.uc
  5. 10
      sources/Gameplay/KF1Frontend/KF1_Frontend.uc
  6. 14
      sources/Players/APlayer.uc
  7. 291
      sources/Players/Inventory/EInventory.uc
  8. 179
      sources/Players/Inventory/EItem.uc
  9. 67
      sources/Players/Inventory/EItemTemplateInfo.uc
  10. 19
      sources/Players/PlayerService.uc

14
sources/Gameplay/BaseClasses/KillingFloor/KFFrontend.uc

@ -36,6 +36,20 @@ protected function Finalizer()
trading = none; trading = none;
} }
/**
* Returns an instance of information about item template with a given name
* `templateName`.
*
* @param templateName Name of the template to return info for.
* @return Template info for item template named `templateName`.
* `none` if item template with given name does not exist or passed
* `templateName` is equal to `none`.
*/
public function EItemTemplateInfo GetItemTemplateInfo(Text templateName)
{
return none;
}
defaultproperties defaultproperties
{ {
tradingClass = none tradingClass = none

417
sources/Gameplay/KF1Frontend/BaseImplementation/EKFInventory.uc

@ -0,0 +1,417 @@
/**
* Player's inventory implementation for classic Killing Floor that changes
* as little as possible and only on request from another mod, otherwise not
* altering gameplay at all.
* 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 <https://www.gnu.org/licenses/>.
*/
class EKFInventory extends EInventory
config(AcediaSystem_KF1Frontend);
struct DualiesPair
{
var class<KFWeaponPickup> single;
var class<KFWeaponPickup> dual;
};
var private APlayer inventoryOwner;
var private config array<DualiesPair> dualiesClasses;
protected function Finalizer()
{
inventoryOwner = none;
}
public function Initialize(APlayer player)
{
if (inventoryOwner != none) {
return;
}
inventoryOwner = player;
}
private function Pawn GetOwnerPawn()
{
local PlayerService service;
service = PlayerService(class'PlayerService'.static.Require());
if (service == none) {
return none;
}
return service.GetPawn(inventoryOwner);
}
public function bool Add(EItem newItem, optional bool forceAddition)
{
local Pawn pawn;
local EKFWeapon kfWeaponItem;
local KFWeapon kfWeapon;
if (!CanAdd(newItem, forceAddition)) return false;
kfWeaponItem = EKFWeapon(newItem);
if (kfWeaponItem == none) return false;
pawn = GetOwnerPawn();
if (pawn == none) return false;
kfWeapon = kfWeaponItem.GetNativeInstance();
if (kfWeapon == none) return false;
kfWeapon.GiveTo(pawn);
return true;
}
public function bool AddTemplate(
Text newItemTemplate,
optional bool forceAddition)
{
local Pawn pawn;
local KFWeapon newWeapon;
if (newItemTemplate == none) return false;
if (!CanAddTemplate(newItemTemplate, forceAddition)) return false;
pawn = GetOwnerPawn();
if (pawn == none) return false;
newWeapon = KFWeapon(_.memory.AllocateByReference(newItemTemplate));
if (newWeapon != none)
{
_.unreal.GetKFGameType().WeaponSpawned(newWeapon);
newWeapon.GiveTo(pawn);
return true;
}
return false;
}
public function bool CanAdd(EItem itemToCheck, optional bool forceAddition)
{
local EKFWeapon kfWeaponItem;
local KFWeapon kfWeapon;
kfWeaponItem = EKFWeapon(itemToCheck);
if (kfWeaponItem == none) return false; // can only add weapons
kfWeapon = kfWeaponItem.GetNativeInstance();
if (kfWeapon == none) return false; // dead `EKFWeapon` object
return CanAddWeaponClass(kfWeapon.class, forceAddition);
}
public function bool CanAddTemplate(
Text itemTemplateToCheck,
optional bool forceAddition)
{
local class<KFWeapon> kfWeaponClass;
// Can only add weapons for now
kfWeaponClass = class<KFWeapon>(_.memory.LoadClass(itemTemplateToCheck));
return CanAddWeaponClass(kfWeaponClass, forceAddition);
}
public function bool CanAddWeaponClass(
class<KFWeapon> kfWeaponClass,
optional bool forceAddition)
{
local KFPawn kfPawn;
if (kfWeaponClass == none) return false;
kfPawn = KFPawn(GetOwnerPawn());
if (kfPawn == none) return false;
if (!forceAddition && !kfPawn.CanCarry(kfWeaponClass.default.weight)) {
return false;
}
if (kfPawn.FindInventoryType(kfWeaponClass) != none) {
return false;
}
if (!forceAddition && HasSameTypeWeapons(kfWeaponClass, kfPawn)) {
return false;
}
return true;
}
private function bool HasSameTypeWeapons(
class<KFWeapon> kfWeaponClass,
Pawn pawn)
{
local Inventory nextInventory;
local class<KFWeaponPickup> itemRoot, nextRoot;
nextInventory = pawn.inventory;
itemRoot = GetRootPickupClass(kfWeaponClass);
while (nextInventory != none)
{
nextRoot = GetRootPickupClass(class<KFWeapon>(nextInventory.class));
if (itemRoot == nextRoot) {
return true;
}
nextInventory = nextInventory.inventory;
}
return false;
}
// 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<KFWeaponPickup> GetRootPickupClass(
class<KFWeapon> weapon)
{
local int i;
local class<KFWeaponPickup> root;
if (weapon == none) return none;
// Start with a pickup of the given weapons
root = class<KFWeaponPickup>(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<KFWeaponPickup>(root.default.variantClasses[0]);
}
return root;
}
public function bool Remove(
EItem itemToRemove,
optional bool keepItem,
optional bool forceRemoval)
{
local bool removedItem;
local float passedTime;
local Pawn pawn;
local Inventory nextInventory;
local EKFWeapon kfWeaponItem;
local KFWeapon kfWeapon;
kfWeaponItem = EKFWeapon(itemToRemove);
if (kfWeaponItem == none) return false;
pawn = GetOwnerPawn();
if (pawn == none) return false;
if (pawn.inventory == none) return false;
kfWeapon = kfWeaponItem.GetNativeInstance();
if (kfWeapon == none) return false;
if (!forceRemoval && kfWeapon.bKFNeverThrow) return false;
passedTime = _.unreal.GetLevel().timeSeconds - 1;
nextInventory = pawn.inventory;
while (nextInventory.inventory != none)
{
if (nextInventory.inventory == kfWeapon)
{
nextInventory.inventory = kfWeapon.inventory;
kfWeapon.inventory = none;
nextInventory.netUpdateTime = passedTime;
kfWeapon.netUpdateTime = passedTime;
kfWeapon.Destroy();
removedItem = true;
}
else {
nextInventory = nextInventory.inventory;
}
}
return removedItem;
}
public function bool RemoveTemplate(
Text itemTemplateToRemove,
optional bool keepItem,
optional bool forceRemoval,
optional bool removeAll)
{
local bool canRemoveInventory;
local bool removedItem;
local float passedTime;
local Pawn pawn;
local Inventory nextInventory;
local KFWeapon nextKFWeapon;
local class<KFWeapon> kfWeaponClass;
pawn = GetOwnerPawn();
if (pawn == none) return false;
if (pawn.inventory == none) return false;
kfWeaponClass = class<KFWeapon>(_.memory.LoadClass(itemTemplateToRemove));
if (kfWeaponClass == none) return false;
if (!forceRemoval && kfWeaponClass.default.bKFNeverThrow) return false;
passedTime = _.unreal.GetLevel().timeSeconds - 1;
nextInventory = pawn.inventory;
while (nextInventory.inventory != none)
{
canRemoveInventory = true;
if (!forceRemoval)
{
nextKFWeapon = KFWeapon(nextInventory.inventory);
if (nextKFWeapon != none && nextKFWeapon.bKFNeverThrow) {
canRemoveInventory = false;
}
}
if ( canRemoveInventory
&& nextInventory.inventory.class == kfWeaponClass)
{
nextInventory.inventory = nextKFWeapon.inventory;
nextKFWeapon.inventory = none;
nextInventory.netUpdateTime = passedTime;
nextKFWeapon.netUpdateTime = passedTime;
nextKFWeapon.Destroy();
removedItem = true;
if (!removeAll) {
return true;
}
}
else {
nextInventory = nextInventory.inventory;
}
}
return removedItem;
}
public function bool RemoveAll(
optional bool keepItems,
optional bool forceRemoval)
{
local int i;
local Pawn pawn;
local KFWeapon kfWeapon;
local Inventory nextInventory;
local class<Weapon> destroyedClass;
local array<Inventory> inventoryToRemove;
pawn = GetOwnerPawn();
if (pawn == none) return false;
if (pawn.inventory == none) return false;
nextInventory = pawn.inventory;
while (nextInventory != none)
{
kfWeapon = KFWeapon(nextInventory);
if (kfWeapon == none)
{
nextInventory = nextInventory.inventory;
continue; // TODO: handle non-weapons differently
}
if (forceRemoval || !kfWeapon.bKFNeverThrow) {
inventoryToRemove[inventoryToRemove.length] = nextInventory;
}
nextInventory = nextInventory.inventory;
}
for(i = 0; i < inventoryToRemove.length; i += 1)
{
if (inventoryToRemove[i] == none) {
continue;
}
destroyedClass = class<Weapon>(inventoryToRemove[i].class);
inventoryToRemove[i].Destroyed();
inventoryToRemove[i].Destroy();
_.unreal.GetKFGameType().WeaponDestroyed(destroyedClass);
}
return (inventoryToRemove.length > 0);
}
/**
* Checks whether caller `EInventory` contains given `itemToCheck`.
*
* @param itemToCheck `EItem` we want to check for belonging to the caller
* `EInventory`.
* @result `true` if item does belong to the inventory and `false` otherwise.
*/
public function bool Contains(EItem itemToCheck)
{
return false;
}
/**
* Returns array with all `EItem`s contained inside the caller `EInventory`.
*
* @return Array with all `EItem`s contained inside the caller `EInventory`.
*/
public function array<EItem> GetAllItems()
{
local array<EItem> emptyArray;
return emptyArray;
}
/**
* Returns array with all `EItem`s contained inside the caller `EInventory`
* that has specified tag `tag`.
*
* @param tag Tag, which items we want to get.
* @return Array with all `EItem`s contained inside the caller `EInventory`
* that has specified tag `tag`.
*/
public function array<EItem> GetTagItems(Text tag)
{
local array<EItem> emptyArray;
return emptyArray;
}
/**
* Returns `EItem` contained inside the caller `EInventory` that has specified
* tag `tag`.
*
* If several `EItem`s inside caller `EInventory` have specified tag,
* inventory system can pick one arbitrarily (can be based on simple
* convenience of implementation). Returned value does not have to
* be stable (the same after repeated calls).
*
* @param tag Tag, which item we want to get.
* @return `EItem` contained inside the caller `EInventory` that belongs to
* the specified tag `tag`.
*/
public function EItem GetTagItem(Text tag) { return none; }
/**
* Returns array with all `EItem`s contained inside the caller `EInventory`
* that originated from the specified template `template`.
*
* @param template Template, that items we want to get originated from.
* @return Array with all `EItem`s contained inside the caller `EInventory`
* that originated from the specified template `template`.
*/
public function array<EItem> GetTemplateItems(Text template)
{
local array<EItem> emptyArray;
return emptyArray;
}
/**
* Returns array with all `EItem`s contained inside the caller `EInventory`
* that originated from the specified template `template`.
*
* If several `EItem`s inside caller `EInventory` originated from
* that template, inventory system can pick one arbitrarily (can be based on
* simple convenience of implementation). Returned value does not have to
* be stable (the same after repeated calls).
*
* @param template Template, that item we want to get originated from.
* @return `EItem`s contained inside the caller `EInventory` that originated
* from the specified template `template`.
*/
public function EItem GetTemplateItem(Text template) { return none; }
defaultproperties
{
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')
}

81
sources/Gameplay/KF1Frontend/BaseImplementation/EKFItemTemplateInfo.uc

@ -0,0 +1,81 @@
/**
* Implementation of `EKFItemTemplateInfo` for classic Killing Floor items that
* changes as little as possible and only on request from another mod,
* otherwise not altering gameplay at all.
* 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 <https://www.gnu.org/licenses/>.
*/
class EKFItemTemplateInfo extends EItemTemplateInfo;
var private class<Inventory> classReference;
/**
* Creates new `EKFItemTemplateInfo` that refers to the `newClass` inventory
* class.
*
* @param newClass Native inventory class that new `EKFItemTemplateInfo`
* will represent.
* @return New `EKFItemTemplateInfo` that represents given `newClass`.
*/
public final static function EKFItemTemplateInfo Wrap(
class<Inventory> newInventoryClass)
{
local EKFItemTemplateInfo newTemplateReference;
if (newInventoryClass == none) {
return none;
}
newTemplateReference =
EKFItemTemplateInfo(__().memory.Allocate(class'EKFItemTemplateInfo'));
newTemplateReference.classReference = newInventoryClass;
return newTemplateReference;
}
public function array<Text> GetTags()
{
local array<Text> tagArray;
if (class<Weapon>(classReference) != none) {
tagArray[0] = P("weapon").Copy();
}
return tagArray;
}
public function Text GetTemplateName()
{
if (classReference == none) {
return none;
}
return _.text.FromString(Locs(string(classReference)));
}
public function Text GetName()
{
local class<KFWeaponPickup> pickupClass;
if (classReference == none) {
return none;
}
// `KFWeaponPickup` names are usually longer and an overall better fit for
// being displayed
pickupClass = class<KFWeaponPickup>(classReference.default.pickupClass);
if (pickupClass != none && pickupClass.default.itemName != "") {
return _.text.FromString(pickupClass.default.itemName);
}
return _.text.FromString(classReference.default.itemName);
}
defaultproperties
{
}

150
sources/Gameplay/KF1Frontend/BaseImplementation/EKFWeapon.uc

@ -0,0 +1,150 @@
/**
* Implementation of `EItem` for classic Killing Floor weapons that changes
* as little as possible and only on request from another mod, otherwise not
* altering gameplay at all.
* 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 <https://www.gnu.org/licenses/>.
*/
class EKFWeapon extends EItem
abstract;
var private NativeActorRef weaponReference;
protected function Finalizer()
{
_.memory.Free(weaponReference);
weaponReference = none;
}
/**
* Creates new `EKFWeapon` that refers to the `weaponInstance` weapon.
*
* @param weaponInstance Native weapon class that new `EKFWeapon` will
* represent.
* @return New `EKFWeapon` that represents given `weaponInstance`.
*/
public final static function EKFWeapon Make(KFWeapon weaponInstance)
{
local EKFWeapon newReference;
newReference = EKFWeapon(__().memory.Allocate(class'EKFWeapon'));
newReference.weaponReference = __().unreal.ActorRef(weaponInstance);
return newReference;
}
/**
* Returns `KFWeapon` instance represented by the caller `EKFWeapon`.
*
* @return `KFWeapon` instance represented by the caller `EKFWeapon`.
*/
public final function KFWeapon GetNativeInstance()
{
if (weaponReference != none) {
return KFWeapon(weaponReference.Get());
}
return none;
}
public function array<Text> GetTags()
{
local array<Text> tagArray;
if (weaponReference == none) return tagArray;
if (weaponReference.Get() == none) return tagArray;
tagArray[0] = P("weapon").Copy();
return tagArray;
}
public function Text GetTemplate()
{
local Weapon weapon;
if (weaponReference == none) return none;
weapon = Weapon(weaponReference.Get());
if (weapon == none) return none;
return _.text.FromString(Locs(string(weapon.class)));
}
public function Text GetName()
{
local Weapon weapon;
if (weaponReference == none) return none;
weapon = Weapon(weaponReference.Get());
if (weapon == none) return none;
return _.text.FromString(Locs(weapon.itemName));
}
public function bool IsRemovable()
{
local KFWeapon kfWeapon; // Check is only meaningful for `KFWeapon`s
if (weaponReference == none) return false;
kfWeapon = KFWeapon(weaponReference.Get());
if (kfWeapon == none) return false;
return !kfWeapon.bKFNeverThrow;
}
public function bool IsSellable()
{
return IsRemovable();
}
public function bool SetPrice(int newPrice)
{
local KFWeapon kfWeapon; // Price is only meaningful for `KFWeapon`s
if (weaponReference == none) return false;
kfWeapon = KFWeapon(weaponReference.Get());
if (kfWeapon == none) return false;
kfWeapon.sellValue = newPrice;
return true;
}
public function int GetPrice()
{
local KFWeapon kfWeapon; // Price is only meaningful for `KFWeapon`s
if (weaponReference == none) return 0;
kfWeapon = KFWeapon(weaponReference.Get());
if (kfWeapon == none) return 0;
return kfWeapon.sellValue;
}
public function bool SetWeight(int newWeight)
{
local KFWeapon kfWeapon; // Weight is only meaningful for `KFWeapon`s
if (weaponReference == none) return false;
kfWeapon = KFWeapon(weaponReference.Get());
if (kfWeapon == none) return false;
kfWeapon.weight = newWeight;
return true;
}
public function int GetWeight()
{
local KFWeapon kfWeapon; // Weight is only meaningful for `KFWeapon`s
if (weaponReference == none) return 0;
kfWeapon = KFWeapon(weaponReference.Get());
if (kfWeapon == none) return 0;
return int(kfWeapon.weight);
}
defaultproperties
{
}

10
sources/Gameplay/KF1Frontend/KF1_Frontend.uc

@ -21,6 +21,16 @@
*/ */
class KF1_Frontend extends KFFrontend; class KF1_Frontend extends KFFrontend;
public function EItemTemplateInfo GetItemTemplateInfo(Text templateName)
{
local class<Inventory> inventoryClass;
inventoryClass = class<Inventory>(_.memory.LoadClass(templateName));
if (inventoryClass == none) {
return none;
}
return class'EKFItemTemplateInfo'.static.Wrap(inventoryClass);
}
defaultproperties defaultproperties
{ {
tradingClass = class'KF1_TradingComponent' tradingClass = class'KF1_TradingComponent'

14
sources/Players/APlayer.uc

@ -216,6 +216,20 @@ public final function SetName(Text newPlayerName)
myReplicationInfo.playerName = hashedName; myReplicationInfo.playerName = hashedName;
} }
// TODO: replace this, it has no place here
// ^ works as a temporary solution before we add pawn wrappers
public final function EInventory GetInventory()
{
local EKFInventory inventory;
if (controller != none && controller.Get() != none)
{
inventory = EKFInventory(_.memory.Allocate(class'EKFInventory'));
inventory.Initialize(self);
return inventory;
}
return none;
}
/** /**
* Returns admin status of the caller player. * Returns admin status of the caller player.
* Disconnected players are never admins. * Disconnected players are never admins.

291
sources/Players/Inventory/EInventory.uc

@ -0,0 +1,291 @@
/**
* Abstract interface that represents inventory system. Inventory system is
* supposed to represent a way to handle items in players inventory -
* in Killing Floor it is a simple set of items with total weight limitations.
* But any other kind of inventory can be implemented as long as it follows
* limitations of this interface.
* 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 <https://www.gnu.org/licenses/>.
*/
class EInventory extends AcediaObject
abstract;
/**
* Initializes `EInventory` for a given `player`.
*
* This method should not be called manually, unless you implement your own
* game interface.
*
* Cannot fail for any connected player and can assume it will not be called
* for not connected ones.
*
* @param player `APlayer` for which to initialize this inventory.
*/
public function Initialize(APlayer player) {}
/**
* Adds passed `EItem` to the caller inventory system.
*
* If adding `newItem` is not currently possible for the caller
* inventory system - it can refuse it.
*
* @param newItem New item to add to the caller inventory system.
* @param forceAddition This parameter is only relevant when `newItem`
* cannot be added in the caller inventory system. If it cannot be added
* because of the conflict with other items - setting this flag to `true`
* allows caller inventory system to get rid of such items to make room for
* `newItem`. Removing items is only allowed if it will actually let us add
* `newItem`. How removal will be done is up to the implementation.
* @return `true` if `newItem` was added and `false` otherwise.
*/
public function bool Add(EItem newItem, optional bool forceAddition)
{
return false;
}
/**
* Adds new `EItem` of template `newItemTemplate` to the caller
* inventory system.
*
* If adding new item is not currently possible for the caller
* inventory system - it can refuse it.
*
* @param newItemTemplate Template of the new item to add to
* the caller inventory system.
* @param forceAddition This parameter is only relevant when new item
* cannot be added in the caller inventory system. If it cannot be added
* because of the conflict with other items - setting this flag to `true`
* allows caller inventory system to get rid of such items to make room for
* new item. Removing items is only allowed if it will actually let us add
* new item. How removal will be done is up to the implementation.
* @return `true` if new item was added and `false` otherwise.
*/
public function bool AddTemplate(
Text newItemTemplate,
optional bool forceAddition)
{
return false;
}
/**
* Checks whether given item `itemToCheck` can be added to the caller
* inventory system.
*
* @param itemToCheck Item to check for whether we can add it to
* the caller `EInventory`.
* @param forceAddition New items can be added with or without
* `forceAddition` flag. This parameter allows you to check whether we
* test for addition with or without it.
* @return `true` if given `itemToCheck` can be added to the caller
* inventory system with given flag `forceAddition` and `false` otherwise.
*/
public function bool CanAdd(EItem itemToCheck, optional bool forceAddition)
{
return false;
}
/**
* Checks whether item with given template `itemToCheck` can be added to
* the caller inventory system.
*
* @param itemTemplateToCheck Template of the item to check for whether we can
* add it to the caller `EInventory`.
* @param forceAddition New items can be added with or without
* `forceAddition` flag. This parameter allows you to check whether we
* test for addition with or without it.
* @return `true` if item with given template `itemTemplateToCheck` can be
* added to the caller inventory system with given flag `forceAddition` and
* `false` otherwise.
*/
public function bool CanAddTemplate(
Text itemTemplateToCheck,
optional bool forceAddition)
{
return false;
}
/**
* Removes given item `itemToRemove` from the caller `EInventory`.
*
* Based on gameplay considerations, inventory system can refuse removing
* `EItem`s for which `IsRemovable()` returns `false`. But removal of any item
* can be enforced with optional third parameter.
*
* @param itemToRemove Item that needs to be removed.
* @param keepItem By default removed item is destroyed.
* Setting this flag to `true` will make caller `EInventory` try to
* preserve it in some way. For Killing Floor it means dropping the item.
* @param forceRemoval Set this to `true` if item must be removed
* no matter what. Otherwise inventory system can refuse removal of items,
* whose `IsRemovable()` returns `false`.
* @return `true` if `EItem` was removed and `false` otherwise
* (including the case where `EItem` was not kept in the caller
* `EInventory` in the first place).
*/
public function bool Remove(
EItem itemToRemove,
optional bool keepItem,
optional bool forceRemoval)
{
return false;
}
/**
* Removes item of type `itemTemplateToRemove` from the caller `EInventory`.
*
* By default removes one arbitrary (can be based on simple convenience of
* implementation) item, but optional parameter can make it remove all items
* of that type.
*
* Based on gameplay considerations, inventory system can refuse removing any
* `EItem` if `IsRemovable()` returns `false` for all stored items of
* given type `itemTemplateToRemove`. But removal of any item can be enforced
* with optional third parameter.
*
* @param itemTemplateToRemove Type of item that needs to be removed.
* @param keepItem By default removed item is destroyed.
* Setting this flag to `true` will make caller `EInventory` try to
* preserve it in some way. For Killing Floor it means dropping the item.
* @param forceRemoval Set this to `true` if item must be removed
* no matter what. Otherwise inventory system can refuse removal if
* `IsRemovable()` returns `false` for all items of given type.
* @param removeAll Set this to `true` if all items of the given type
* must be removed from the caller `EInventory` and keep `false` to remove
* only one. With `forceRemoval == false` it is possible that only items
* that return `false` for `IsRemovable()` will be removed, while others
* will be retained.
* @return `true` if any `EItem`s was removed and `false` otherwise
* (including the case where `EItem` of given type were not kept in the
* caller `EInventory` in the first place).
*/
public function bool RemoveTemplate(
Text itemTemplateToRemove,
optional bool keepItem,
optional bool forceRemoval,
optional bool removeAll)
{
return false;
}
/**
* Removes all items from the caller `EInventory`.
*
* Based on gameplay considerations, inventory system can refuse removing
* `EItem`s for which `IsRemovable()` returns `false`. But removal of any item
* can be enforced with optional second parameter.
*
* @param keepItem By default removed item is destroyed.
* Setting this flag to `true` will make caller `EInventory` try to
* preserve it in some way. For Killing Floor it means dropping the item.
* @param forceRemoval Set this to `true` if item must be removed
* no matter what. Otherwise inventory system can refuse removal of items,
* whose `IsRemovable()` returns `false`.
* @return `true` if any `EItem` was removed and `false` otherwise
* (including the case where no `EItem`s were kept in the caller
* `EInventory` in the first place).
*/
public function bool RemoveAll(
optional bool keepItems,
optional bool forceRemoval)
{
return false;
}
/**
* Checks whether caller `EInventory` contains given `itemToCheck`.
*
* @param itemToCheck `EItem` we want to check for belonging to the caller
* `EInventory`.
* @result `true` if item does belong to the inventory and `false` otherwise.
*/
public function bool Contains(EItem itemToCheck)
{
return false;
}
/**
* Returns array with all `EItem`s contained inside the caller `EInventory`.
*
* @return Array with all `EItem`s contained inside the caller `EInventory`.
*/
public function array<EItem> GetAllItems()
{
local array<EItem> emptyArray;
return emptyArray;
}
/**
* Returns array with all `EItem`s contained inside the caller `EInventory`
* that has specified tag `tag`.
*
* @param tag Tag, which items we want to get.
* @return Array with all `EItem`s contained inside the caller `EInventory`
* that has specified tag `tag`.
*/
public function array<EItem> GetTagItems(Text tag)
{
local array<EItem> emptyArray;
return emptyArray;
}
/**
* Returns `EItem` contained inside the caller `EInventory` that has specified
* tag `tag`.
*
* If several `EItem`s inside caller `EInventory` have specified tag,
* inventory system can pick one arbitrarily (can be based on simple
* convenience of implementation). Returned value does not have to
* be stable (the same after repeated calls).
*
* @param tag Tag, which item we want to get.
* @return `EItem` contained inside the caller `EInventory` that belongs to
* the specified tag `tag`.
*/
public function EItem GetTagItem(Text tag) { return none; }
/**
* Returns array with all `EItem`s contained inside the caller `EInventory`
* that originated from the specified template `template`.
*
* @param template Template, that items we want to get originated from.
* @return Array with all `EItem`s contained inside the caller `EInventory`
* that originated from the specified template `template`.
*/
public function array<EItem> GetTemplateItems(Text template)
{
local array<EItem> emptyArray;
return emptyArray;
}
/**
* Returns array with all `EItem`s contained inside the caller `EInventory`
* that originated from the specified template `template`.
*
* If several `EItem`s inside caller `EInventory` originated from
* that template, inventory system can pick one arbitrarily (can be based on
* simple convenience of implementation). Returned value does not have to
* be stable (the same after repeated calls).
*
* @param template Template, that item we want to get originated from.
* @return `EItem`s contained inside the caller `EInventory` that originated
* from the specified template `template`.
*/
public function EItem GetTemplateItem(Text template) { return none; }
defaultproperties
{
}

179
sources/Players/Inventory/EItem.uc

@ -0,0 +1,179 @@
/**
* Abstract interface that represents some kind of item inside player's
* inventory.
* At most basic, abstract item has "template", "group" and "name":
* 1. "Template" refers to some sort of preset from which new instances of
* `EItem` can be created. "Template" might be implemented in any way,
* but the requirement is that "templates" can be referred to by
* case-insensitive, human-readable text value. In Killing Floor
* "templates" correspond to classes: for example,
* `KFMod.M79GrenadeLauncher` for M79 or `KFMod.M14EBRBattleRifle`
* for EBR.
* 2. "Tag" refers to the one of the groups inventory belongs to.
* For example all weapons would belong to group "weapons", while ammo
* or objective items can have their own group. But weapons can have
* several different tags further separating them, like "primary",
* "secondary" or "ultimate".
* 3. "Name" is simply a human-readable name of an item that is also fit
* to be output in UI. Like "M79 Grenade Launcher" instead of just
* "KFMod.M79GrenadeLauncher".
* We can also specify whether a certain item is allowed to be removed from
* inventory by player's own volition for any item. `IsRemovable()` method must
* return `true` if it can be.
*
* However, while `EItem` is meant to be abstract generalization for any
* kind of item, to help simplify access to common item data we have also
* added several additional groups of parameters:
* 1. Shop-related parameters. Price (accessed by `SetPrice()` and
* `GetPrice()`) and whether it is sellable (`IsSellable()`). Price
* should only be used if an item is sellable.
* 2. Weight. Accessed by `SetWeight()` / `GetWeight()`.
* All of these parameters can be ignored if they are not applicable to
* a certain type of item.
* 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 <https://www.gnu.org/licenses/>.
*/
class EItem extends AcediaObject
abstract;
/**
* Returns arrays of tags for caller `EItem`.
*
* @return Tags for the caller `EItem`. Returned `Text` values are not allowed
* to be empty or `none`. There can be no duplicates (in case-insensitive
* sense). But returned array can be empty.
*/
public function array<Text> GetTags()
{
local array<Text> emptyArray;
return emptyArray;
}
/**
* Returns template caller `EItem` was created from.
*
* @return Template caller `EItem` belongs to, even if it was modified to be
* something else entirely. `none` for dead `EItem`s.
*/
public function Text GetTemplate()
{
return none;
}
/**
* Returns UI-usable name of the caller `EItem`.
*
* @return UI-usable name of the caller `EItem`. Allowed to be empty,
* not allowed to be `none`. `none` for dead `EItem`s.
*/
public function Text GetName()
{
return none;
}
/**
* Checks whether this item can be removed as a result of player's action.
*
* We will not enforce items to be completely unremovable through the API, so
* this only marks an item as one unintended to be removed from inventory.
* Enforcing of this rule is up to the implementation.
* 9mm pistol is a good example for Killing Floor.
*
* Note that item being removable does not mean game must always (or ever)
* provide players with a way to remove that item, just that it can.
*
* @return `true` if caller `EItem` is removable by player and
* `false` otherwise.
*/
public function bool IsRemovable()
{
return false;
}
/**
* Can caller `EItem` be sold?
*
* @return `true` if caller `EItem` can be sold and `false` otherwise.
*/
public function bool IsSellable()
{
return false;
}
/**
* Changes price of the caller `EItem`.
* Only applicable it item is sellable (`IsSellable() == true`).
*
* Price is allowed to have any integer value.
*
* Caller `EItem` is allowed to refuse this call and keep the old price.
* Setting new price, different from both old value and `newPrice` is
* forbidden.
*
* @return `true` if price was successfully changed and `false` otherwise.
*/
public function bool SetPrice(int newPrice)
{
return false;
}
/**
* Returns current price of the caller `EItem`.
* Only applicable it item is sellable (`IsSellable() == true`).
*
* Price is allowed to have any integer value.
*
* @return Current price of the caller `EItem`.
*/
public function int GetPrice() { return 0; }
/**
* Returns "weight" of the caller `EItem`.
* A parameter widely used in Killing Floor.
*
* Weight is allowed to have any integer value.
*
* Caller `EItem` is allowed to refuse this call and keep the old weight.
* Setting new weight, different from both old value and `newWeight` is
* forbidden.
*
* @return `true` if weight was successfully changed and `false` otherwise.
*/
public function bool SetWeight(int newWeight)
{
return false;
}
/**
* Returns current weight of the caller `EItem`.
*
* Weight is allowed to have any integer value.
*
* If concept of weight is not applicable, this method should return `0`.
* However inverse does not hold - returning `0` does not mean that weight
* is not applicable for the caller `EItem`.
*
* @return Current weight of the caller `EItem`.
*/
public function int GetWeight()
{
return 0;
}
defaultproperties
{
}

67
sources/Players/Inventory/EItemTemplateInfo.uc

@ -0,0 +1,67 @@
/**
* Abstract interface that represents information about some kind of item.
* It refers to some sort of preset from which new instances of `EItem`
* can be created. "Template" might be implemented in any way, but
* the requirement is that "templates" can be referred to by case-insensitive,
* human-readable text value. In Killing Floor "templates" correspond to
* classes: for example, `KFMod.M79GrenadeLauncher` for M79 or
* `KFMod.M14EBRBattleRifle` for EBR. However Acedia adds its own parameters
* (such as tags) on top.
* 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 <https://www.gnu.org/licenses/>.
*/
class EItemTemplateInfo extends AcediaObject
abstract;
/**
* Returns arrays of tags for caller `EItem`.
*
* @return Tags for the caller `EItem`. Returned `Text` values are not allowed
* to be empty or `none`. There can be no duplicates (in case-insensitive
* sense). But returned array can be empty.
*/
public function array<Text> GetTags()
{
local array<Text> emptyArray;
return emptyArray;
}
/**
* Returns template caller `EItem` was created from.
*
* @return Template caller `EItem` belongs to, even if it was modified to be
* something else entirely. `none` for dead `EItem`s.
*/
public function Text GetTemplateName()
{
return none;
}
/**
* Returns UI-usable name of the caller `EItem`.
*
* @return UI-usable name of the caller `EItem`. Allowed to be empty,
* not allowed to be `none`. `none` for dead `EItem`s.
*/
public function Text GetName()
{
return none;
}
defaultproperties
{
}

19
sources/Players/PlayerService.uc

@ -121,7 +121,7 @@ public final function APlayer GetPlayer(Controller controller)
/** /**
* Returns `PlayerController` associated with a given `APlayer`. * Returns `PlayerController` associated with a given `APlayer`.
* *
* @param player Player for which we want to find associated controller. * @param player Player for which we want to find associated `Controller`.
* @return Controller that is associated with a given player. * @return Controller that is associated with a given player.
* Can return `none` if controller has already "expired". * Can return `none` if controller has already "expired".
*/ */
@ -140,6 +140,23 @@ public final function PlayerController GetController(APlayer player)
return none; return none;
} }
/**
* Returns `Pawn` associated with a given `APlayer`.
*
* @param player Player for which we want to find associated `Pawn`.
* @return `Pawn` that is associated with a given player.
* Can return `none` if controller has already "expired".
*/
public final function Pawn GetPawn(APlayer player)
{
local Controller controller;
controller = GetController(player);
if (controller != none) {
return controller.pawn;
}
return none;
}
/** /**
* IMPORTANT: this is a helper function that is not supposed to be * IMPORTANT: this is a helper function that is not supposed to be
* called manually. * called manually.

Loading…
Cancel
Save