Anton Tarasenko
2 years ago
27 changed files with 4342 additions and 473 deletions
@ -0,0 +1,118 @@
|
||||
[Editor.EditorEngine] |
||||
EditPackages=Core |
||||
EditPackages=Engine |
||||
EditPackages=Fire |
||||
EditPackages=Editor |
||||
EditPackages=UnrealEd |
||||
EditPackages=IpDrv |
||||
EditPackages=UWeb |
||||
EditPackages=GamePlay |
||||
EditPackages=UnrealGame |
||||
EditPackages=XGame |
||||
EditPackages=XInterface |
||||
EditPackages=XAdmin |
||||
EditPackages=XWebAdmin |
||||
EditPackages=GUI2K4 |
||||
EditPackages=xVoting |
||||
EditPackages=UTV2004c |
||||
EditPackages=UTV2004s |
||||
EditPackages=ROEffects |
||||
EditPackages=ROEngine |
||||
EditPackages=ROInterface |
||||
EditPackages=Old2k4 |
||||
EditPackages=KFMod |
||||
EditPackages=KFChar |
||||
EditPackages=KFGui |
||||
EditPackages=GoodKarma |
||||
EditPackages=KFMutators |
||||
EditPackages=KFStoryGame |
||||
EditPackages=KFStoryUI |
||||
EditPackages=SideShowScript |
||||
EditPackages=FrightScript |
||||
CutdownPackages=Core |
||||
CutdownPackages=Editor |
||||
CutdownPackages=Engine |
||||
CutdownPackages=Fire |
||||
CutdownPackages=GamePlay |
||||
CutdownPackages=GUI2K4 |
||||
CutdownPackages=IpDrv |
||||
CutdownPackages=Onslaught |
||||
CutdownPackages=UnrealEd |
||||
CutdownPackages=UnrealGame |
||||
CutdownPackages=UWeb |
||||
CutdownPackages=XAdmin |
||||
CutdownPackages=XEffects |
||||
CutdownPackages=XInterface |
||||
CutdownPackages=XPickups |
||||
CutdownPackages=XWebAdmin |
||||
CutdownPackages=XVoting |
||||
|
||||
|
||||
[Engine.Engine] |
||||
RenderDevice=D3D9Drv.D3D9RenderDevice |
||||
AudioDevice=ALAudio.ALAudioSubsystem |
||||
NetworkDevice=IpDrv.TcpNetDriver |
||||
DemoRecordingDevice=Engine.DemoRecDriver |
||||
Console=KFMod.KFConsole |
||||
GUIController=KFGUI.KFGUIController |
||||
StreamPlayer=Engine.StreamInteraction |
||||
Language=int |
||||
Product=KillingFloor |
||||
GameEngine=Engine.GameEngine |
||||
EditorEngine=Editor.EditorEngine |
||||
DefaultGame=KFMod.KFGameType |
||||
DefaultServerGame=KFMod.KFGameType |
||||
ViewportManager=WinDrv.WindowsClient |
||||
Render=Render.Render |
||||
Input=Engine.Input |
||||
Canvas=Engine.Canvas |
||||
DetectedVideoMemory=0 |
||||
ServerReadsStdin=False |
||||
|
||||
|
||||
[Core.System] |
||||
PurgeCacheDays=30 |
||||
SavePath=..\Save |
||||
CachePath=../Cache |
||||
CacheExt=.uxx |
||||
CacheRecordPath=../System/*.ucl |
||||
MusicPath=../Music |
||||
SpeechPath=../Speech |
||||
Paths=../System/*.u |
||||
Paths=../Maps/*.rom |
||||
Paths=../TestMaps/*.rom |
||||
Paths=../Textures/*.utx |
||||
Paths=../Sounds/*.uax |
||||
Paths=../Music/*.umx |
||||
Paths=../StaticMeshes/*.usx |
||||
Paths=../Animations/*.ukx |
||||
Paths=../Saves/*.uvx |
||||
Paths=../Textures/Old2k4/*.utx |
||||
Paths=../Sounds/Old2k4/*.uax../Music/Old2k4/*.umx |
||||
Paths=../StaticMeshes/Old2k4/*.usx |
||||
Paths=../Animations/Old2k4/*.ukx |
||||
Paths=../KarmaData/Old2k4/*.ka |
||||
Suppress=DevLoad |
||||
Suppress=DevSave |
||||
Suppress=DevNetTraffic |
||||
Suppress=DevGarbage |
||||
Suppress=DevKill |
||||
Suppress=DevReplace |
||||
Suppress=DevCompile |
||||
Suppress=DevBind |
||||
Suppress=DevBsp |
||||
Suppress=DevNet |
||||
Suppress=DevLIPSinc |
||||
Suppress=DevKarma |
||||
Suppress=RecordCache |
||||
Suppress=MapVoteDebug |
||||
Suppress=Init |
||||
Suppress=MapVote |
||||
Suppress=VoiceChat |
||||
Suppress=ChatManager |
||||
Suppress=Time |
||||
|
||||
|
||||
[ROFirstRun] |
||||
ROFirstRun=1094 |
||||
|
@ -0,0 +1,12 @@
|
||||
; Every single option in this config should be considered [ADVANCED] |
||||
[AcediaCore.KFDualiesTool] |
||||
; This array defines what weapons Acedia's Killing Floor frontend considers as |
||||
; pairs of single - dual versions. |
||||
; If you are adding some custom dual weapons to your server, then they should |
||||
; also be added here until a better way is implemented. |
||||
dualiesClasses=(single=class'KFMod.SinglePickup',dual=class'KFMod.DualiesPickup') |
||||
dualiesClasses=(single=class'KFMod.Magnum44Pickup',dual=class'KFMod.Dual44MagnumPickup') |
||||
dualiesClasses=(single=class'KFMod.MK23Pickup',dual=class'KFMod.DualMK23Pickup') |
||||
dualiesClasses=(single=class'KFMod.DeaglePickup',dual=class'KFMod.DualDeaglePickup') |
||||
dualiesClasses=(single=class'KFMod.GoldenDeaglePickup',dual=class'KFMod.GoldenDualDeaglePickup') |
||||
dualiesClasses=(single=class'KFMod.FlareRevolverPickup',dual=class'KFMod.DualFlareRevolverPickup') |
@ -0,0 +1,82 @@
|
||||
/** |
||||
* Subset of functionality for dealing with everything related to templates. |
||||
* Copyright 2022 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 ATemplatesComponent extends AcediaObject |
||||
abstract; |
||||
|
||||
/** |
||||
* Returns `true` if list of items named `listName` exists and |
||||
* `false` otherwise. |
||||
* |
||||
* This method is necessary, since `GetItemList()` does not allow to |
||||
* distinguish between empty and non-existing item list. |
||||
* |
||||
* @param listName Name of the list to check for whether it exists. |
||||
* @return `true` if list named `listName` exists and `false` otherwise. |
||||
* Always returns `false` if `listName` equals `none`. |
||||
*/ |
||||
public function bool ItemListExists(Text listName) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Returns array with templates of items belonging to the `listName` list. |
||||
* |
||||
* All implementations must support: |
||||
* 1. "all weapons" / "weapons" (both names |
||||
* should refer to the same list) list with templates of all weapons in |
||||
* the game; |
||||
* 2. "trading weapons" list with names of all weapons available for trade |
||||
* in one way or another (even if they are not all tradable in |
||||
* all shops / for all players). |
||||
* |
||||
* @param listName Name of the list to return templates for. |
||||
* In case a name of inexistent list is specified - method does nothing. |
||||
* @return Array of templates in the list, specified with `listName`. |
||||
* All of the `Text`s in the returned array are guaranteed to be `none`. |
||||
* When incorrect `listName` is specified - empty array is returned |
||||
* (which can also happen if specified list is empty). |
||||
*/ |
||||
public function array<Text> GetItemList(Text listName) |
||||
{ |
||||
local array<Text> emptyArray; |
||||
return emptyArray; |
||||
} |
||||
|
||||
/** |
||||
* Returns array that is listing all available lists of item templates. |
||||
* |
||||
* All implementations must include "all weapons" and "trading weapons" lists. |
||||
* |
||||
* @return Array with names of all available lists. |
||||
* All of the `Text`s in the returned array are guaranteed to be `none`. |
||||
* If a certain list has several names (like "all weapons" / "weapons"), |
||||
* only one of these names (guaranteed to always be the same between calls) |
||||
* will be included. |
||||
*/ |
||||
public function array<Text> GetAvailableLists() |
||||
{ |
||||
local array<Text> emptyArray; |
||||
return emptyArray; |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,464 @@
|
||||
/** |
||||
* Implementation of `EAmmo` 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 2022 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 EKFAmmo extends EAmmo; |
||||
|
||||
var private NativeActorRef ammunitionReference; |
||||
|
||||
protected function Finalizer() |
||||
{ |
||||
_.memory.Free(ammunitionReference); |
||||
ammunitionReference = none; |
||||
} |
||||
|
||||
/** |
||||
* Creates new `EKFAmmo` that refers to the `ammunitionInstance` ammunition. |
||||
* |
||||
* @param ammunitionInstance Native ammunition instance that new `EKFAmmo` |
||||
* will represent. |
||||
* @return New `EKFAmmo` that represents given `ammunitionInstance`. |
||||
* `none` iff `ammunitionInstance` is either `none` or |
||||
* is an unused flash light ammunition |
||||
* (has `class'KFMod.FlashlightAmmo'` class). |
||||
*/ |
||||
public final static /*unreal*/ function EKFAmmo Wrap( |
||||
Ammunition ammunitionInstance) |
||||
{ |
||||
local EKFAmmo newReference; |
||||
if (ammunitionInstance == none) return none; |
||||
// This one is not actually used for anything, so it is not real |
||||
if (ammunitionInstance.class == class'KFMod.FlashlightAmmo') return none; |
||||
|
||||
newReference = EKFAmmo(__().memory.Allocate(class'EKFAmmo')); |
||||
newReference.ammunitionReference = __().unreal.ActorRef(ammunitionInstance); |
||||
return newReference; |
||||
} |
||||
|
||||
public function EInterface Copy() |
||||
{ |
||||
local Ammunition ammunitionInstance; |
||||
ammunitionInstance = GetNativeInstance(); |
||||
return Wrap(ammunitionInstance); |
||||
} |
||||
|
||||
public function bool Supports(class<EInterface> newInterfaceClass) |
||||
{ |
||||
if (newInterfaceClass == none) return false; |
||||
if (newInterfaceClass == class'EItem') return true; |
||||
if (newInterfaceClass == class'EAmmo') return true; |
||||
if (newInterfaceClass == class'EKFAmmo') return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public function EInterface As(class<EInterface> newInterfaceClass) |
||||
{ |
||||
if (!IsExistent()) { |
||||
return none; |
||||
} |
||||
if ( newInterfaceClass == class'EItem' |
||||
|| newInterfaceClass == class'EAmmo' |
||||
|| newInterfaceClass == class'EKFAmmo') |
||||
{ |
||||
return Copy(); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function bool IsExistent() |
||||
{ |
||||
return (GetNativeInstance() != none); |
||||
} |
||||
|
||||
public function bool SameAs(EInterface other) |
||||
{ |
||||
local EKFAmmo otherAmmo; |
||||
otherAmmo = EKFAmmo(other); |
||||
if (otherAmmo == none) { |
||||
return false; |
||||
} |
||||
return (GetNativeInstance() == otherAmmo.GetNativeInstance()); |
||||
} |
||||
|
||||
/** |
||||
* Returns `Ammunition` instance represented by the caller `EKFAmmo`. |
||||
* |
||||
* @return `Ammunition` instance represented by the caller `EKFAmmo`. |
||||
*/ |
||||
public final /*unreal*/ function Ammunition GetNativeInstance() |
||||
{ |
||||
if (ammunitionReference != none) { |
||||
return Ammunition(ammunitionReference.Get()); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function array<Text> GetTags() |
||||
{ |
||||
local array<Text> tagArray; |
||||
if (ammunitionReference == none) return tagArray; |
||||
if (ammunitionReference.Get() == none) return tagArray; |
||||
|
||||
tagArray[0] = P("ammo").Copy(); |
||||
return tagArray; |
||||
} |
||||
|
||||
public function bool HasTag(Text tagToCheck) |
||||
{ |
||||
if (tagToCheck == none) return false; |
||||
if (tagToCheck.Compare(P("ammo"))) return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public function Text GetTemplate() |
||||
{ |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) { |
||||
return none; |
||||
} |
||||
return _.text.FromString(string(ammunition.class)); |
||||
} |
||||
|
||||
public function Text GetName() |
||||
{ |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) { |
||||
return none; |
||||
} |
||||
return _.text.FromString(ammunition.GetHumanReadableName()); |
||||
} |
||||
|
||||
public function bool IsRemovable() |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function bool IsSellable() |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
private function class<KFWeaponPickup> GetOwnerWeaponPickupClass() |
||||
{ |
||||
local KFWeapon ownerWeapon; |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) { |
||||
return none; |
||||
} |
||||
ownerWeapon = GetOwnerWeapon(); |
||||
if (ownerWeapon != none) { |
||||
return class<KFWeaponPickup>(ownerWeapon.pickupClass); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
// Finds a weapons that is corresponding to our ammo. |
||||
// We can limit ourselves to returning a single instance, since one weapon |
||||
// per ammo type is how Killing Floor does things. |
||||
private function KFWeapon GetOwnerWeapon() |
||||
{ |
||||
local Pawn myOwner; |
||||
local KFWeapon nextWeapon; |
||||
local Inventory nextInventory; |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) return none; |
||||
myOwner = Pawn(ammunition.owner); |
||||
if (myOwner == none) return none; |
||||
|
||||
nextInventory = myOwner.inventory; |
||||
while (nextInventory != none) |
||||
{ |
||||
nextWeapon = KFWeapon(nextInventory); |
||||
nextInventory = nextInventory.inventory; |
||||
if (_.unreal.inventory.GetAmmoClass(nextWeapon, 0) == ammunition.class) |
||||
{ |
||||
return nextWeapon; |
||||
} |
||||
else if ( _.unreal.inventory.GetAmmoClass(nextWeapon, 1) |
||||
== ammunition.class) { |
||||
return nextWeapon; |
||||
} |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
/** |
||||
* In Killing Floor ammo object itself does not actually have a price, |
||||
* instead it is defined inside weapon's `Pickup` class and, therefore, |
||||
* cannot be changed for an individual item. Only calculated. |
||||
*/ |
||||
public function bool SetPrice(int newPrice) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function int GetPrice() |
||||
{ |
||||
return GetPriceOf(GetAmount()); |
||||
} |
||||
|
||||
public function int GetTotalPrice() |
||||
{ |
||||
return GetPriceOf(GetTotalAmount()); |
||||
} |
||||
|
||||
public function int GetPriceOf(int ammoAmount) |
||||
{ |
||||
local Pawn myOwner; |
||||
local int clipSize; |
||||
local float clipPrice; |
||||
local KFWeapon ownerWeapon; |
||||
local KFPlayerReplicationInfo ownerKFPRI; |
||||
local class<KFWeaponPickup> ownerWeaponPickupClass; |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) return 0; |
||||
ownerWeapon = GetOwnerWeapon(); |
||||
if (ownerWeapon == none) return 0; |
||||
ownerWeaponPickupClass = class<KFWeaponPickup>(ownerWeapon.pickupClass); |
||||
if (ownerWeaponPickupClass == none) return 0; |
||||
|
||||
// Calculate clip price |
||||
if ( ownerWeapon.bHasSecondaryAmmo |
||||
&& ammunition.class != ownerWeapon.fireModeClass[0].default.ammoClass) |
||||
{ |
||||
// Amon Killing Floor's weapons, only M4 203 has a real secondary ammo |
||||
clipSize = 1; |
||||
} |
||||
else { |
||||
clipSize = ownerWeapon.default.magCapacity; |
||||
} |
||||
if( ownerWeapon.PickupClass == class'HuskGunPickup' ) { |
||||
clipSize = ownerWeaponPickupClass.default.buyClipSize; |
||||
} |
||||
clipPrice = ownerWeaponPickupClass.default.ammoCost; |
||||
// Calculate clip size |
||||
myOwner = Pawn(ammunition.owner); |
||||
if (myOwner != none) { |
||||
ownerKFPRI = KFPlayerReplicationInfo(myOwner.playerReplicationInfo); |
||||
} |
||||
if (ownerKFPRI != none) |
||||
{ |
||||
clipPrice *= ownerKFPRI.clientVeteranSkill.static |
||||
.GetAmmoCostScaling(ownerKFPRI, ownerWeaponPickupClass); |
||||
} |
||||
// Calculate price of total ammo |
||||
return int(ammoAmount * clipPrice / clipSize); |
||||
} |
||||
|
||||
public function bool SetWeight(int newWeight) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function int GetWeight() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
// Killing Floor weapons do not reduce ammunition when it is loaded it into |
||||
// the weapons. This is because each ammo type is only ever used by one weapon, |
||||
// so the can simply treat it as part of the weapon and only record how much |
||||
// of it is currently in the weapon's magazine. |
||||
// This method goes through inventory weapons to find how much ammo was |
||||
// already loaded into the weapons. |
||||
private function int GetLoadedAmmo() |
||||
{ |
||||
local KFWeapon ownerWeapon; |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) return 0; |
||||
ownerWeapon = GetOwnerWeapon(); |
||||
if (ownerWeapon == none) return 0; |
||||
// Husk gun does not load ammo at all |
||||
if (ownerWeapon.class == class'KFMod.HuskGun') return 0; |
||||
|
||||
// Most of the Killing Floor weapons do not have a proper separate |
||||
// secondary ammo: they either reuse primary ammo (like zed guns or |
||||
// hunting shotgun), or they use some pseudo-ammo (like medic guns). |
||||
// They only exception is M4 203 that loads itself as soon as |
||||
// it fires. Some modded weapons might also be exceptions and/or use |
||||
// secondary ammo differently, but we have no way of knowing how |
||||
// exactly they are doing it and cannot implement this interface |
||||
// for them. |
||||
// That is why we only bother with the first fire mode and count |
||||
// one loaded ammo for the secondary, just assuming it is M4 203. |
||||
// We can also quit as soon as we have found a single weapon that |
||||
// uses our ammo, since one weapon per ammo type is how Killing Floor |
||||
// does things. |
||||
if (_.unreal.inventory.GetAmmoClass(ownerWeapon, 0) == ammunition.class) { |
||||
return ownerWeapon.magAmmoRemaining; |
||||
} |
||||
else if ( _.unreal.inventory.GetAmmoClass(ownerWeapon, 1) |
||||
== ammunition.class) |
||||
{ |
||||
return 1; // M4 203 |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
public function Add(int amount, optional bool forceAddition) |
||||
{ |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) { |
||||
return; |
||||
} |
||||
if (forceAddition) { |
||||
ammunition.ammoAmount += amount; |
||||
} |
||||
else |
||||
{ |
||||
ammunition.ammoAmount = |
||||
Min(ammunition.maxAmmo, ammunition.ammoAmount + amount); |
||||
} |
||||
// Correct possible negative values |
||||
if (ammunition.ammoAmount < 0) { |
||||
ammunition.ammoAmount = 0; |
||||
} |
||||
ammunition.netUpdateTime = ammunition.level.timeSeconds - 1; |
||||
} |
||||
|
||||
public function int GetAmount() |
||||
{ |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) { |
||||
return 0; |
||||
} |
||||
return Max(0, ammunition.ammoAmount - GetLoadedAmmo()); |
||||
} |
||||
|
||||
public function int GetTotalAmount() |
||||
{ |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) { |
||||
return 0; |
||||
} |
||||
return Max(0, ammunition.ammoAmount); |
||||
} |
||||
|
||||
public function SetAmount(int amount, optional bool forceAddition) |
||||
{ |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) { |
||||
return; |
||||
} |
||||
if (forceAddition) { |
||||
ammunition.ammoAmount = amount; |
||||
} |
||||
else { |
||||
ammunition.ammoAmount = Min(ammunition.maxAmmo, amount); |
||||
} |
||||
// Correct possible negative values |
||||
if (ammunition.ammoAmount < 0) { |
||||
ammunition.ammoAmount = 0; |
||||
} |
||||
ammunition.netUpdateTime = ammunition.level.timeSeconds - 1; |
||||
} |
||||
|
||||
public function int GetMaxAmount() |
||||
{ |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) { |
||||
return 0; |
||||
} |
||||
// `Ammunition` does not really support infinite ammo, so return `0` if |
||||
// the value is messed up. |
||||
return Max(0, ammunition.maxAmmo - GetLoadedAmmo()); |
||||
} |
||||
|
||||
public function int GetMaxTotalAmount() |
||||
{ |
||||
local Ammunition ammunition; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) { |
||||
return 0; |
||||
} |
||||
// `Ammunition` does not really support infinite ammo, so return `0` if |
||||
// the value is messed up. |
||||
return Max(0, ammunition.maxAmmo); |
||||
} |
||||
|
||||
/** |
||||
* Supports any non-negative ammo value. |
||||
*/ |
||||
public function bool SetMaxAmount( |
||||
int newMaxAmmo, |
||||
optional bool leaveCurrentAmmo) |
||||
{ |
||||
local Ammunition ammunition; |
||||
// We do not support unlimited ammo values |
||||
if (newMaxAmmo < 0) return false; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) return false; |
||||
|
||||
ammunition.maxAmmo = newMaxAmmo + GetLoadedAmmo(); |
||||
if (!leaveCurrentAmmo) { |
||||
ammunition.ammoAmount = Min(ammunition.maxAmmo, ammunition.ammoAmount); |
||||
} |
||||
ammunition.netUpdateTime = ammunition.level.timeSeconds - 1; |
||||
return true; |
||||
} |
||||
|
||||
public function bool SetMaxTotalAmount( |
||||
int newTotalMaxAmmo, |
||||
optional bool leaveCurrentAmmo) |
||||
{ |
||||
local Ammunition ammunition; |
||||
// We do not support unlimited ammo values |
||||
if (newTotalMaxAmmo < 0) return false; |
||||
ammunition = GetNativeInstance(); |
||||
if (ammunition == none) return false; |
||||
|
||||
ammunition.maxAmmo = newTotalMaxAmmo; |
||||
if (!leaveCurrentAmmo) { |
||||
ammunition.ammoAmount = Min(ammunition.maxAmmo, ammunition.ammoAmount); |
||||
} |
||||
ammunition.netUpdateTime = ammunition.level.timeSeconds - 1; |
||||
return true; |
||||
} |
||||
|
||||
public function bool HasWeapon() |
||||
{ |
||||
return (GetOwnerWeapon() != none); |
||||
} |
||||
|
||||
// Killing Floor's ammo should also count ammo already loaded into the magazine |
||||
public function Fill() |
||||
{ |
||||
if (GetMaxTotalAmount() < 0) return; |
||||
if (GetAmount() >= GetMaxTotalAmount()) return; |
||||
|
||||
SetAmount(GetMaxTotalAmount()); |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,298 @@
|
||||
/** |
||||
* Implementation of `EAmmo` for Killing Floor's flashlight charge that changes |
||||
* as little as possible and only on request from another mod, otherwise not |
||||
* altering gameplay at all. |
||||
* Copyright 2022 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 EKFFlashlightAmmo extends EAmmo; |
||||
|
||||
var private NativeActorRef pawnReference; |
||||
|
||||
protected function Finalizer() |
||||
{ |
||||
_.memory.Free(pawnReference); |
||||
pawnReference = none; |
||||
} |
||||
|
||||
/** |
||||
* Creates new `EKFFlashlightAmmo` that refers to the `medicWeaponInstance`'s |
||||
* medic ammunition. |
||||
* |
||||
* @param kfHumanPawn Pawn class with flashlight ammo. |
||||
* In Killing Floor, "flashlight ammo" is basically just a variable |
||||
* inside `KFHumanPawn` instance. |
||||
* @return New `EKFFlashlightAmmo` that represents medic ammunition of given |
||||
* `kfHumanPawn`. `none` iff `kfHumanPawn` is `none`. |
||||
*/ |
||||
public final static /*unreal*/ function EKFFlashlightAmmo Wrap( |
||||
KFHumanPawn kfHumanPawn) |
||||
{ |
||||
local EKFFlashlightAmmo newReference; |
||||
if (kfHumanPawn == none) { |
||||
return none; |
||||
} |
||||
newReference = |
||||
EKFFlashlightAmmo(__().memory.Allocate(class'EKFFlashlightAmmo')); |
||||
newReference.pawnReference = __().unreal.ActorRef(kfHumanPawn); |
||||
return newReference; |
||||
} |
||||
|
||||
public function EInterface Copy() |
||||
{ |
||||
local KFHumanPawn pawnInstance; |
||||
pawnInstance = GetNativeInstance(); |
||||
return Wrap(pawnInstance); |
||||
} |
||||
|
||||
public function bool Supports(class<EInterface> newInterfaceClass) |
||||
{ |
||||
if (newInterfaceClass == none) return false; |
||||
if (newInterfaceClass == class'EItem') return true; |
||||
if (newInterfaceClass == class'EAmmo') return true; |
||||
if (newInterfaceClass == class'EKFFlashlightAmmo') return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public function EInterface As(class<EInterface> newInterfaceClass) |
||||
{ |
||||
if (!IsExistent()) { |
||||
return none; |
||||
} |
||||
if ( newInterfaceClass == class'EItem' |
||||
|| newInterfaceClass == class'EAmmo' |
||||
|| newInterfaceClass == class'EKFFlashlightAmmo') |
||||
{ |
||||
return Copy(); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function bool IsExistent() |
||||
{ |
||||
return (GetNativeInstance() != none); |
||||
} |
||||
|
||||
public function bool SameAs(EInterface other) |
||||
{ |
||||
local EKFFlashlightAmmo otherAmmo; |
||||
otherAmmo = EKFFlashlightAmmo(other); |
||||
if (otherAmmo == none) { |
||||
return false; |
||||
} |
||||
return (GetNativeInstance() == otherAmmo.GetNativeInstance()); |
||||
} |
||||
|
||||
/** |
||||
* Returns `KFAmmunition` instance represented by the caller `EKFAmmo`. |
||||
* |
||||
* @return `KFAmmunition` instance represented by the caller `EKFAmmo`. |
||||
*/ |
||||
public final /*unreal*/ function KFHumanPawn GetNativeInstance() |
||||
{ |
||||
if (pawnReference != none) { |
||||
return KFHumanPawn(pawnReference.Get()); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function array<Text> GetTags() |
||||
{ |
||||
local array<Text> tagArray; |
||||
if (pawnReference == none) return tagArray; |
||||
if (pawnReference.Get() == none) return tagArray; |
||||
|
||||
tagArray[0] = P("ammo").Copy(); |
||||
return tagArray; |
||||
} |
||||
|
||||
public function bool HasTag(Text tagToCheck) |
||||
{ |
||||
if (tagToCheck == none) return false; |
||||
if (tagToCheck.Compare(P("ammo"))) return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public function Text GetTemplate() |
||||
{ |
||||
if (IsExistent()) { |
||||
return P("flashlight:ammo").Copy(); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function Text GetName() |
||||
{ |
||||
if (IsExistent()) { |
||||
return P("Flashlight's ammo").Copy(); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function bool IsRemovable() |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function bool IsSellable() |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Medic ammo is free and does not have a price in Killing Floor. |
||||
*/ |
||||
public function bool SetPrice(int newPrice) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function int GetPrice() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function int GetTotalPrice() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function int GetPriceOf(int ammoAmount) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function bool SetWeight(int newWeight) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function int GetWeight() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function Add(int amount, optional bool forceAddition) |
||||
{ |
||||
local KFHumanPawn kfHumanPawn; |
||||
kfHumanPawn = GetNativeInstance(); |
||||
if (kfHumanPawn == none) { |
||||
return; |
||||
} |
||||
if (forceAddition) { |
||||
kfHumanPawn.torchBatteryLife += amount; |
||||
} |
||||
else |
||||
{ |
||||
kfHumanPawn.torchBatteryLife = |
||||
Min( kfHumanPawn.default.torchBatteryLife, |
||||
kfHumanPawn.torchBatteryLife + amount); |
||||
} |
||||
// Correct possible negative values |
||||
if (kfHumanPawn.torchBatteryLife < 0) { |
||||
kfHumanPawn.torchBatteryLife = 0; |
||||
} |
||||
} |
||||
|
||||
public function int GetAmount() |
||||
{ |
||||
local KFHumanPawn kfHumanPawn; |
||||
kfHumanPawn = GetNativeInstance(); |
||||
if (kfHumanPawn == none) { |
||||
return 0; |
||||
} |
||||
return Max(0, kfHumanPawn.torchBatteryLife); |
||||
} |
||||
|
||||
public function int GetTotalAmount() |
||||
{ |
||||
return GetAmount(); |
||||
} |
||||
|
||||
public function SetAmount(int amount, optional bool forceAddition) |
||||
{ |
||||
local KFHumanPawn kfHumanPawn; |
||||
kfHumanPawn = GetNativeInstance(); |
||||
if (kfHumanPawn == none) { |
||||
return; |
||||
} |
||||
if (forceAddition) { |
||||
kfHumanPawn.torchBatteryLife = amount; |
||||
} |
||||
else |
||||
{ |
||||
kfHumanPawn.torchBatteryLife = |
||||
Min(kfHumanPawn.default.torchBatteryLife, amount); |
||||
} |
||||
// Correct possible negative values |
||||
if (kfHumanPawn.torchBatteryLife < 0) { |
||||
kfHumanPawn.torchBatteryLife = 0; |
||||
} |
||||
} |
||||
|
||||
public function int GetMaxAmount() |
||||
{ |
||||
local KFHumanPawn kfHumanPawn; |
||||
kfHumanPawn = GetNativeInstance(); |
||||
if (kfHumanPawn == none) { |
||||
return 0; |
||||
} |
||||
return Max(0, kfHumanPawn.default.torchBatteryLife); |
||||
} |
||||
|
||||
public function int GetMaxTotalAmount() |
||||
{ |
||||
return GetMaxAmount(); |
||||
} |
||||
|
||||
public function bool SetMaxAmount( |
||||
int newMaxAmmo, |
||||
optional bool leaveCurrentAmmo) |
||||
{ |
||||
local KFHumanPawn kfHumanPawn; |
||||
// We do not support unlimited ammo values |
||||
if (newMaxAmmo < 0) return false; |
||||
kfHumanPawn = GetNativeInstance(); |
||||
if (kfHumanPawn == none) return false; |
||||
|
||||
kfHumanPawn.default.torchBatteryLife = newMaxAmmo; |
||||
if (!leaveCurrentAmmo) |
||||
{ |
||||
kfHumanPawn.torchBatteryLife = |
||||
Min( kfHumanPawn.default.torchBatteryLife, |
||||
kfHumanPawn.torchBatteryLife); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
public function bool SetMaxTotalAmount( |
||||
int newTotalMaxAmmo, |
||||
optional bool leaveCurrentAmmo) |
||||
{ |
||||
return SetMaxAmount(newTotalMaxAmmo, leaveCurrentAmmo); |
||||
} |
||||
|
||||
public function bool HasWeapon() |
||||
{ |
||||
return (GetNativeInstance() != none); |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,285 @@
|
||||
/** |
||||
* Implementation of `EAmmo` for Killing Floor medic weapons that changes |
||||
* as little as possible and only on request from another mod, otherwise not |
||||
* altering gameplay at all. |
||||
* Copyright 2022 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 EKFMedicAmmo extends EAmmo; |
||||
|
||||
var private NativeActorRef medicWeaponReference; |
||||
|
||||
protected function Finalizer() |
||||
{ |
||||
_.memory.Free(medicWeaponReference); |
||||
medicWeaponReference = none; |
||||
} |
||||
|
||||
/** |
||||
* Creates new `EKFMedicAmmo` that refers to the `medicWeaponInstance`'s |
||||
* medic ammunition. |
||||
* |
||||
* @param medicWeaponInstance Native medic gun, whose medic ammunition |
||||
* new `EKFMedicAmmo` will represent. |
||||
* @return New `EKFMedicAmmo` that represents medic ammunition of given |
||||
* `medicWeaponInstance`. `none` iff `medicWeaponInstance` is `none`. |
||||
*/ |
||||
public final static /*unreal*/ function EKFMedicAmmo Wrap( |
||||
KFMedicGun medicWeaponInstance) |
||||
{ |
||||
local EKFMedicAmmo newReference; |
||||
if (medicWeaponInstance == none) { |
||||
return none; |
||||
} |
||||
newReference = EKFMedicAmmo(__().memory.Allocate(class'EKFMedicAmmo')); |
||||
newReference.medicWeaponReference = |
||||
__().unreal.ActorRef(medicWeaponInstance); |
||||
return newReference; |
||||
} |
||||
|
||||
public function EInterface Copy() |
||||
{ |
||||
local KFMedicGun medicWeaponInstance; |
||||
medicWeaponInstance = GetNativeInstance(); |
||||
return Wrap(medicWeaponInstance); |
||||
} |
||||
|
||||
public function bool Supports(class<EInterface> newInterfaceClass) |
||||
{ |
||||
if (newInterfaceClass == none) return false; |
||||
if (newInterfaceClass == class'EItem') return true; |
||||
if (newInterfaceClass == class'EAmmo') return true; |
||||
if (newInterfaceClass == class'EKFMedicAmmo') return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public function EInterface As(class<EInterface> newInterfaceClass) |
||||
{ |
||||
if (!IsExistent()) { |
||||
return none; |
||||
} |
||||
if ( newInterfaceClass == class'EItem' |
||||
|| newInterfaceClass == class'EAmmo' |
||||
|| newInterfaceClass == class'EKFMedicAmmo') |
||||
{ |
||||
return Copy(); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function bool IsExistent() |
||||
{ |
||||
return (GetNativeInstance() != none); |
||||
} |
||||
|
||||
public function bool SameAs(EInterface other) |
||||
{ |
||||
local EKFMedicAmmo otherAmmo; |
||||
otherAmmo = EKFMedicAmmo(other); |
||||
if (otherAmmo == none) { |
||||
return false; |
||||
} |
||||
return (GetNativeInstance() == otherAmmo.GetNativeInstance()); |
||||
} |
||||
|
||||
/** |
||||
* Returns `KFAmmunition` instance represented by the caller `EKFAmmo`. |
||||
* |
||||
* @return `KFAmmunition` instance represented by the caller `EKFAmmo`. |
||||
*/ |
||||
public final /*unreal*/ function KFMedicGun GetNativeInstance() |
||||
{ |
||||
if (medicWeaponReference != none) { |
||||
return KFMedicGun(medicWeaponReference.Get()); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function array<Text> GetTags() |
||||
{ |
||||
local array<Text> tagArray; |
||||
if (medicWeaponReference == none) return tagArray; |
||||
if (medicWeaponReference.Get() == none) return tagArray; |
||||
|
||||
tagArray[0] = P("ammo").Copy(); |
||||
return tagArray; |
||||
} |
||||
|
||||
public function bool HasTag(Text tagToCheck) |
||||
{ |
||||
if (tagToCheck == none) return false; |
||||
if (tagToCheck.Compare(P("ammo"))) return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public function Text GetTemplate() |
||||
{ |
||||
local KFMedicGun medicWeapon; |
||||
medicWeapon = GetNativeInstance(); |
||||
if (medicWeapon == none) { |
||||
return none; |
||||
} |
||||
return _.text.FromString(string(medicWeapon.class) $ ":ammo"); |
||||
} |
||||
|
||||
public function Text GetName() |
||||
{ |
||||
local KFMedicGun medicWeapon; |
||||
medicWeapon = GetNativeInstance(); |
||||
if (medicWeapon == none) { |
||||
return none; |
||||
} |
||||
return _.text.FromString(medicWeapon.GetHumanReadableName() $ "'s ammo"); |
||||
} |
||||
|
||||
public function bool IsRemovable() |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function bool IsSellable() |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Medic ammo is free and does not have a price in Killing Floor. |
||||
*/ |
||||
public function bool SetPrice(int newPrice) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function int GetPrice() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function int GetTotalPrice() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function int GetPriceOf(int ammoAmount) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function bool SetWeight(int newWeight) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function int GetWeight() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function Add(int amount, optional bool forceAddition) |
||||
{ |
||||
local KFMedicGun medicWeapon; |
||||
medicWeapon = GetNativeInstance(); |
||||
if (medicWeapon == none) { |
||||
return; |
||||
} |
||||
if (forceAddition) { |
||||
medicWeapon.healAmmoCharge += amount; |
||||
} |
||||
else |
||||
{ |
||||
medicWeapon.healAmmoCharge = |
||||
Min(medicWeapon.maxAmmoCount, medicWeapon.healAmmoCharge + amount); |
||||
} |
||||
// Correct possible negative values |
||||
if (medicWeapon.healAmmoCharge < 0) { |
||||
medicWeapon.healAmmoCharge = 0; |
||||
} |
||||
} |
||||
|
||||
public function int GetAmount() |
||||
{ |
||||
local KFMedicGun medicWeapon; |
||||
medicWeapon = GetNativeInstance(); |
||||
if (medicWeapon == none) { |
||||
return 0; |
||||
} |
||||
return Max(0, medicWeapon.healAmmoCharge); |
||||
} |
||||
|
||||
public function int GetTotalAmount() |
||||
{ |
||||
return GetAmount(); |
||||
} |
||||
|
||||
public function SetAmount(int amount, optional bool forceAddition) |
||||
{ |
||||
local KFMedicGun medicWeapon; |
||||
medicWeapon = GetNativeInstance(); |
||||
if (medicWeapon == none) { |
||||
return; |
||||
} |
||||
if (forceAddition) { |
||||
medicWeapon.healAmmoCharge = amount; |
||||
} |
||||
else { |
||||
medicWeapon.healAmmoCharge = Min(medicWeapon.maxAmmoCount, amount); |
||||
} |
||||
// Correct possible negative values |
||||
if (medicWeapon.healAmmoCharge < 0) { |
||||
medicWeapon.healAmmoCharge = 0; |
||||
} |
||||
} |
||||
|
||||
public function int GetMaxAmount() |
||||
{ |
||||
local KFMedicGun medicWeapon; |
||||
medicWeapon = GetNativeInstance(); |
||||
if (medicWeapon == none) { |
||||
return 0; |
||||
} |
||||
return Max(0, medicWeapon.maxAmmoCount); |
||||
} |
||||
|
||||
public function int GetMaxTotalAmount() |
||||
{ |
||||
return GetMaxAmount(); |
||||
} |
||||
|
||||
public function bool SetMaxAmount( |
||||
int newMaxAmmo, |
||||
optional bool leaveCurrentAmmo) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function bool SetMaxTotalAmount( |
||||
int newTotalMaxAmmo, |
||||
optional bool leaveCurrentAmmo) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function bool HasWeapon() |
||||
{ |
||||
return (GetNativeInstance() != none); |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,288 @@
|
||||
/** |
||||
* Implementation of `EAmmo` for Killing Floor medical syringe that changes |
||||
* as little as possible and only on request from another mod, otherwise not |
||||
* altering gameplay at all. |
||||
* Copyright 2022 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 EKFSyringeAmmo extends EAmmo; |
||||
|
||||
var private NativeActorRef syringeReference; |
||||
|
||||
protected function Finalizer() |
||||
{ |
||||
_.memory.Free(syringeReference); |
||||
syringeReference = none; |
||||
} |
||||
|
||||
/** |
||||
* Creates new `EKFSyringeAmmo` that refers to the `syringeInstance`'s |
||||
* ammunition. |
||||
* |
||||
* @param syringeInstance Native syringe instance, whose ammunition |
||||
* new `EKFSyringeAmmo` will represent. |
||||
* @return New `EKFSyringeAmmo` that represents ammunition of given |
||||
* `syringeInstance`. `none` iff `syringeInstance` is `none`. |
||||
*/ |
||||
public final static /*unreal*/ function EKFSyringeAmmo Wrap( |
||||
Syringe syringeInstance) |
||||
{ |
||||
local EKFSyringeAmmo newReference; |
||||
if (syringeInstance == none) { |
||||
return none; |
||||
} |
||||
newReference = EKFSyringeAmmo(__().memory.Allocate(class'EKFSyringeAmmo')); |
||||
newReference.syringeReference = |
||||
__().unreal.ActorRef(syringeInstance); |
||||
return newReference; |
||||
} |
||||
|
||||
public function EInterface Copy() |
||||
{ |
||||
local Syringe syringeInstance; |
||||
syringeInstance = GetNativeInstance(); |
||||
return Wrap(syringeInstance); |
||||
} |
||||
|
||||
public function bool Supports(class<EInterface> newInterfaceClass) |
||||
{ |
||||
if (newInterfaceClass == none) return false; |
||||
if (newInterfaceClass == class'EItem') return true; |
||||
if (newInterfaceClass == class'EAmmo') return true; |
||||
if (newInterfaceClass == class'EKFSyringeAmmo') return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public function EInterface As(class<EInterface> newInterfaceClass) |
||||
{ |
||||
if (!IsExistent()) { |
||||
return none; |
||||
} |
||||
if ( newInterfaceClass == class'EItem' |
||||
|| newInterfaceClass == class'EAmmo' |
||||
|| newInterfaceClass == class'EKFSyringeAmmo') |
||||
{ |
||||
return Copy(); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function bool IsExistent() |
||||
{ |
||||
return (GetNativeInstance() != none); |
||||
} |
||||
|
||||
public function bool SameAs(EInterface other) |
||||
{ |
||||
local EKFSyringeAmmo otherAmmo; |
||||
otherAmmo = EKFSyringeAmmo(other); |
||||
if (otherAmmo == none) { |
||||
return false; |
||||
} |
||||
return (GetNativeInstance() == otherAmmo.GetNativeInstance()); |
||||
} |
||||
|
||||
/** |
||||
* Returns `KFAmmunition` instance represented by the caller `EKFAmmo`. |
||||
* |
||||
* @return `KFAmmunition` instance represented by the caller `EKFAmmo`. |
||||
*/ |
||||
public final /*unreal*/ function Syringe GetNativeInstance() |
||||
{ |
||||
if (syringeReference != none) { |
||||
return Syringe(syringeReference.Get()); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function array<Text> GetTags() |
||||
{ |
||||
local array<Text> tagArray; |
||||
if (syringeReference == none) return tagArray; |
||||
if (syringeReference.Get() == none) return tagArray; |
||||
|
||||
tagArray[0] = P("ammo").Copy(); |
||||
return tagArray; |
||||
} |
||||
|
||||
public function bool HasTag(Text tagToCheck) |
||||
{ |
||||
if (tagToCheck == none) return false; |
||||
if (tagToCheck.Compare(P("ammo"))) return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public function Text GetTemplate() |
||||
{ |
||||
local Syringe syringeInstance; |
||||
syringeInstance = GetNativeInstance(); |
||||
if (syringeInstance == none) { |
||||
return none; |
||||
} |
||||
return _.text.FromString("kfmod.syringe:ammo"); |
||||
} |
||||
|
||||
public function Text GetName() |
||||
{ |
||||
local Syringe syringeInstance; |
||||
syringeInstance = GetNativeInstance(); |
||||
if (syringeInstance == none) { |
||||
return none; |
||||
} |
||||
return _.text.FromString("Syringe's ammo"); |
||||
} |
||||
|
||||
public function bool IsRemovable() |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function bool IsSellable() |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Medic ammo is free and does not have a price in Killing Floor. |
||||
*/ |
||||
public function bool SetPrice(int newPrice) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function int GetPrice() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function int GetTotalPrice() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function int GetPriceOf(int ammoAmount) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function bool SetWeight(int newWeight) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function int GetWeight() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
public function Add(int amount, optional bool forceAddition) |
||||
{ |
||||
local Syringe syringeInstance; |
||||
syringeInstance = GetNativeInstance(); |
||||
if (syringeInstance == none) { |
||||
return; |
||||
} |
||||
if (forceAddition) { |
||||
syringeInstance.ammoCharge[0] += amount; |
||||
} |
||||
else |
||||
{ |
||||
syringeInstance.ammoCharge[0] = |
||||
Min(syringeInstance.maxAmmoCount, |
||||
syringeInstance.ammoCharge[0] + amount); |
||||
} |
||||
// Correct possible negative values |
||||
if (syringeInstance.ammoCharge[0] < 0) { |
||||
syringeInstance.ammoCharge[0] = 0; |
||||
} |
||||
} |
||||
|
||||
public function int GetAmount() |
||||
{ |
||||
local Syringe syringeInstance; |
||||
syringeInstance = GetNativeInstance(); |
||||
if (syringeInstance == none) { |
||||
return 0; |
||||
} |
||||
return Max(0, syringeInstance.ammoCharge[0]); |
||||
} |
||||
|
||||
public function int GetTotalAmount() |
||||
{ |
||||
return GetAmount(); |
||||
} |
||||
|
||||
public function SetAmount(int amount, optional bool forceAddition) |
||||
{ |
||||
local Syringe syringeInstance; |
||||
syringeInstance = GetNativeInstance(); |
||||
if (syringeInstance == none) { |
||||
return; |
||||
} |
||||
if (forceAddition) { |
||||
syringeInstance.ammoCharge[0] = amount; |
||||
} |
||||
else |
||||
{ |
||||
syringeInstance.ammoCharge[0] = |
||||
Min(syringeInstance.maxAmmoCount, amount); |
||||
} |
||||
// Correct possible negative values |
||||
if (syringeInstance.ammoCharge[0] < 0) { |
||||
syringeInstance.ammoCharge[0] = 0; |
||||
} |
||||
} |
||||
|
||||
public function int GetMaxAmount() |
||||
{ |
||||
local Syringe syringeInstance; |
||||
syringeInstance = GetNativeInstance(); |
||||
if (syringeInstance == none) { |
||||
return 0; |
||||
} |
||||
return Max(0, syringeInstance.maxAmmoCount); |
||||
} |
||||
|
||||
public function int GetMaxTotalAmount() |
||||
{ |
||||
return GetMaxAmount(); |
||||
} |
||||
|
||||
public function bool SetMaxAmount( |
||||
int newMaxAmmo, |
||||
optional bool leaveCurrentAmmo) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function bool SetMaxTotalAmount( |
||||
int newTotalMaxAmmo, |
||||
optional bool leaveCurrentAmmo) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function bool HasWeapon() |
||||
{ |
||||
return (GetNativeInstance() != none); |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,168 @@
|
||||
/** |
||||
* Dummy implementation for `EItem` interface that can wrap around `Inventory` |
||||
* instances that Acedia does not know about - including any non-weapons and |
||||
* non-ammo items added by any other mods. |
||||
* Copyright 2022 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 EKFUnknownItem extends EItem; |
||||
|
||||
var private NativeActorRef inventoryReference; |
||||
|
||||
protected function Finalizer() |
||||
{ |
||||
_.memory.Free(inventoryReference); |
||||
inventoryReference = none; |
||||
} |
||||
|
||||
/** |
||||
* Creates new `EKFUnknownItem` that refers to the `inventoryInstance`. |
||||
* |
||||
* @param inventoryInstance Native inventory instance that new |
||||
* `EKFUnknownItem` will represent. |
||||
* @return New `EKFUnknownItem` that represents given `inventoryInstance`. |
||||
* `none` iff `inventoryInstance` is either `none`. |
||||
*/ |
||||
public final static /*unreal*/ function EKFUnknownItem Wrap( |
||||
Inventory inventoryInstance) |
||||
{ |
||||
local EKFUnknownItem newReference; |
||||
if (inventoryInstance == none) return none; |
||||
if (Ammunition(inventoryInstance) != none) return none; |
||||
// This one is not actually used for anything, so it is not real |
||||
if (inventoryInstance.class == class'KFMod.FlashlightAmmo') return none; |
||||
|
||||
newReference = EKFUnknownItem(__().memory.Allocate(class'EKFUnknownItem')); |
||||
newReference.inventoryReference = __().unreal.ActorRef(inventoryInstance); |
||||
return newReference; |
||||
} |
||||
|
||||
/** |
||||
* Returns `Inventory` instance represented by the caller `EKFUnknownItem`. |
||||
* |
||||
* @return `Inventory` instance represented by the caller `EKFUnknownItem`. |
||||
*/ |
||||
public final /*unreal*/ function Inventory GetNativeInstance() |
||||
{ |
||||
if (inventoryReference != none) { |
||||
return Inventory(inventoryReference.Get()); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function EInterface Copy() |
||||
{ |
||||
local Inventory inventoryInstance; |
||||
inventoryInstance = GetNativeInstance(); |
||||
return Wrap(inventoryInstance); |
||||
} |
||||
|
||||
public function bool Supports(class<EInterface> newInterfaceClass) |
||||
{ |
||||
if (newInterfaceClass == none) return false; |
||||
if (newInterfaceClass == class'EItem') return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public function EInterface As(class<EInterface> newInterfaceClass) |
||||
{ |
||||
if (!IsExistent()) { |
||||
return none; |
||||
} |
||||
if (newInterfaceClass == class'EItem') { |
||||
return Copy(); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
public function bool IsExistent() |
||||
{ |
||||
return (GetNativeInstance() != none); |
||||
} |
||||
|
||||
public function bool SameAs(EInterface other) |
||||
{ |
||||
local EKFUnknownItem otherItem; |
||||
otherItem = EKFUnknownItem(other); |
||||
if (otherItem == none) { |
||||
return false; |
||||
} |
||||
return (GetNativeInstance() == otherItem.GetNativeInstance()); |
||||
} |
||||
|
||||
public function array<Text> GetTags() |
||||
{ |
||||
local array<Text> emptyArray; |
||||
return emptyArray; |
||||
} |
||||
|
||||
public function bool HasTag(Text tagToCheck) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function Text GetTemplate() |
||||
{ |
||||
local Inventory inventory; |
||||
inventory = GetNativeInstance(); |
||||
if (inventory == none) { |
||||
return none; |
||||
} |
||||
return _.text.FromString(string(inventory.class)); |
||||
} |
||||
|
||||
public function Text GetName() |
||||
{ |
||||
local Inventory inventory; |
||||
inventory = GetNativeInstance(); |
||||
if (inventory == none) { |
||||
return none; |
||||
} |
||||
return _.text.FromString(inventory.GetHumanReadableName()); |
||||
} |
||||
|
||||
public function bool IsRemovable() |
||||
{ |
||||
return true; |
||||
} |
||||
|
||||
public function bool IsSellable() |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function bool SetPrice(int newPrice) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function int GetPrice() { return 0; } |
||||
|
||||
public function bool SetWeight(int newWeight) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
public function int GetWeight() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,215 @@
|
||||
/** |
||||
* `ATemplatesComponent`'s implementation for `KF1_Frontend`. |
||||
* Lists weapons available at the trader, provides support for per-perk lists, |
||||
* derived from the `KFLevelRules`. |
||||
* Copyright 2022 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 KF1_TemplatesComponent extends ATemplatesComponent; |
||||
|
||||
var private bool listsAreReady; |
||||
// TODO: add tools |
||||
var private array<Text> availableWeaponLists; |
||||
var private array<Text> allWeaponsList; |
||||
var private array<Text> medicWeaponsList; |
||||
var private array<Text> supportWeaponsList; |
||||
var private array<Text> sharpshooterWeaponsList; |
||||
var private array<Text> commandoWeaponsList; |
||||
var private array<Text> berserkerWeaponsList; |
||||
var private array<Text> firebugWeaponsList; |
||||
var private array<Text> demolitionWeaponsList; |
||||
var private array<Text> neutralWeaponsList; |
||||
|
||||
protected function Finalizer() |
||||
{ |
||||
_.memory.FreeMany(allWeaponsList); |
||||
_.memory.FreeMany(medicWeaponsList); |
||||
_.memory.FreeMany(supportWeaponsList); |
||||
_.memory.FreeMany(sharpshooterWeaponsList); |
||||
_.memory.FreeMany(commandoWeaponsList); |
||||
_.memory.FreeMany(berserkerWeaponsList); |
||||
_.memory.FreeMany(firebugWeaponsList); |
||||
_.memory.FreeMany(demolitionWeaponsList); |
||||
_.memory.FreeMany(neutralWeaponsList); |
||||
_.memory.FreeMany(availableWeaponLists); |
||||
if (allWeaponsList.length > 0) { |
||||
allWeaponsList.length = 0; |
||||
} |
||||
if (medicWeaponsList.length > 0) { |
||||
medicWeaponsList.length = 0; |
||||
} |
||||
if (supportWeaponsList.length > 0) { |
||||
supportWeaponsList.length = 0; |
||||
} |
||||
if (sharpshooterWeaponsList.length > 0) { |
||||
sharpshooterWeaponsList.length = 0; |
||||
} |
||||
if (commandoWeaponsList.length > 0) { |
||||
commandoWeaponsList.length = 0; |
||||
} |
||||
if (berserkerWeaponsList.length > 0) { |
||||
berserkerWeaponsList.length = 0; |
||||
} |
||||
if (firebugWeaponsList.length > 0) { |
||||
firebugWeaponsList.length = 0; |
||||
} |
||||
if (demolitionWeaponsList.length > 0) { |
||||
demolitionWeaponsList.length = 0; |
||||
} |
||||
if (neutralWeaponsList.length > 0) { |
||||
neutralWeaponsList.length = 0; |
||||
} |
||||
if (availableWeaponLists.length > 0) { |
||||
availableWeaponLists.length = 0; |
||||
} |
||||
listsAreReady = false; |
||||
} |
||||
|
||||
private function BuildKFWeaponLists() |
||||
{ |
||||
local LevelInfo level; |
||||
local KFLevelRules kfLevelRules; |
||||
if (listsAreReady) return; |
||||
level = _.unreal.GetLevel(); |
||||
if (level == none) return; |
||||
foreach level.DynamicActors(class'KFMod.KFLevelRules', kfLevelRules) break; |
||||
if (kfLevelRules == none) return; |
||||
|
||||
medicWeaponsList = MakeWeaponList(kfLevelRules.mediItemForSale); |
||||
supportWeaponsList = MakeWeaponList(kfLevelRules.suppItemForSale); |
||||
sharpshooterWeaponsList = MakeWeaponList(kfLevelRules.shrpItemForSale); |
||||
commandoWeaponsList = MakeWeaponList(kfLevelRules.commItemForSale); |
||||
berserkerWeaponsList = MakeWeaponList(kfLevelRules.bersItemForSale); |
||||
firebugWeaponsList = MakeWeaponList(kfLevelRules.fireItemForSale); |
||||
demolitionWeaponsList = MakeWeaponList(kfLevelRules.demoItemForSale); |
||||
neutralWeaponsList = MakeWeaponList(kfLevelRules.neutItemForSale); |
||||
availableWeaponLists[0] = _.text.FromString("all weapons"); |
||||
availableWeaponLists[1] = _.text.FromString("trading weapons"); |
||||
availableWeaponLists[2] = _.text.FromString("medic weapons"); |
||||
availableWeaponLists[3] = _.text.FromString("support weapons"); |
||||
availableWeaponLists[4] = _.text.FromString("sharpshooter weapons"); |
||||
availableWeaponLists[5] = _.text.FromString("commando weapons"); |
||||
availableWeaponLists[6] = _.text.FromString("firebug weapons"); |
||||
availableWeaponLists[7] = _.text.FromString("demolition weapons"); |
||||
availableWeaponLists[8] = _.text.FromString("neutral weapons"); |
||||
listsAreReady = true; |
||||
} |
||||
|
||||
private function array<Text> MakeWeaponList(array< class<Pickup> > shopList) |
||||
{ |
||||
local int i; |
||||
local Text nextTemplate; |
||||
local class<Weapon> nextWeaponClass; |
||||
local array<Text> resultArray; |
||||
if (listsAreReady) { |
||||
return resultArray; |
||||
} |
||||
for (i = 0; i < shopList.length; i += 1) |
||||
{ |
||||
if (shopList[i] == none) continue; |
||||
nextWeaponClass = class<Weapon>(shopList[i].default.inventoryType); |
||||
if (nextWeaponClass == none) continue; |
||||
|
||||
nextTemplate = _.text.FromString(string(nextWeaponClass)); |
||||
resultArray[resultArray.length] = nextTemplate.Copy(); |
||||
allWeaponsList[allWeaponsList.length] = nextTemplate; |
||||
} |
||||
return resultArray; |
||||
} |
||||
|
||||
private function array<Text> CopyList(array<Text> inputList) |
||||
{ |
||||
local int i; |
||||
local array<Text> outputList; |
||||
// `inputList` is guaranteed to not contain invalid `Text` objects |
||||
for (i = 0; i < inputList.length; i += 1) { |
||||
outputList[outputList.length] = inputList[i].Copy(); |
||||
} |
||||
return outputList; |
||||
} |
||||
|
||||
public function bool ItemListExists(Text listName) |
||||
{ |
||||
local string listNameAsString; |
||||
if (listName == none) return false; |
||||
listNameAsString = listName.ToString(); |
||||
if (listNameAsString == "weapons") return true; |
||||
if (listNameAsString == "all weapons") return true; |
||||
if (listNameAsString == "trading weapons") return true; |
||||
if (listNameAsString == "medic weapons") return true; |
||||
if (listNameAsString == "support weapons") return true; |
||||
if (listNameAsString == "sharpshooter weapons") return true; |
||||
if (listNameAsString == "commando weapons") return true; |
||||
if (listNameAsString == "berserker weapons") return true; |
||||
if (listNameAsString == "firebug weapons") return true; |
||||
if (listNameAsString == "demolition weapons") return true; |
||||
if (listNameAsString == "neutral weapons") return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public function array<Text> GetItemList(Text listName) |
||||
{ |
||||
local string listNameAsString; |
||||
local array<Text> emptyArray; |
||||
if (listName == none) { |
||||
return emptyArray; |
||||
} |
||||
listNameAsString = listName.ToString(); |
||||
BuildKFWeaponLists(); |
||||
if ( listNameAsString == "weapons" |
||||
|| listNameAsString == "all weapons" |
||||
|| listNameAsString == "trading weapons") |
||||
{ |
||||
return CopyList(allWeaponsList); |
||||
} |
||||
if (listNameAsString == "medic weapons") { |
||||
return CopyList(medicWeaponsList); |
||||
} |
||||
if (listNameAsString == "support weapons") { |
||||
return CopyList(supportWeaponsList); |
||||
} |
||||
if (listNameAsString == "sharpshooter weapons") { |
||||
return CopyList(sharpshooterWeaponsList); |
||||
} |
||||
if (listNameAsString == "commando weapons") { |
||||
return CopyList(commandoWeaponsList); |
||||
} |
||||
if (listNameAsString == "berserker weapons") { |
||||
return CopyList(berserkerWeaponsList); |
||||
} |
||||
if (listNameAsString == "firebug weapons") { |
||||
return CopyList(firebugWeaponsList); |
||||
} |
||||
if (listNameAsString == "demolition weapons") { |
||||
return CopyList(demolitionWeaponsList); |
||||
} |
||||
if (listNameAsString == "neutral weapons") { |
||||
return CopyList(neutralWeaponsList); |
||||
} |
||||
return emptyArray; |
||||
} |
||||
|
||||
public function array<Text> GetAvailableLists() |
||||
{ |
||||
BuildKFWeaponLists(); |
||||
return CopyList(availableWeaponLists); |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,259 @@
|
||||
/** |
||||
* Abstract interface that represents ammunition of a certain type. |
||||
* Ammunition methods make distinction between "amount" and "total amount": |
||||
* * "Amount" is how much of this ammo is stored in this |
||||
* inventory item, "max amount" is how much it can store at once. |
||||
* These values can be affected by other items in the inventory. |
||||
* * "Total amount" is how much ammo of this type player has in his |
||||
* inventory in total. |
||||
* Neither amounts can ever be negative. |
||||
* For Killing Floor "total ammo" corresponds to the amount of ammunition |
||||
* associated with a weapon, while "ammo" would correspond to the amount of |
||||
* ammo still unleaded into the weapon. This means that "max ammo" becomes |
||||
* quite a bizarre value that depends on how full your magazine is. |
||||
* For example, if you bought lever action rifle, filled it |
||||
* with ammo (80 bullets) and then shot out 6, you will have: |
||||
* * "Ammo" == 70 - since you will have that much still unloaded; |
||||
* * "Max ammo" == 76 - since with 4 loaded bullets you can only |
||||
* have 76 unloaded ones; |
||||
* * "Total ammo" == 74 - amount of bullets you can still shoot; |
||||
* * "Max total ammo" = 80 - since that is the limit of LAR bullets you |
||||
* can carry in total. |
||||
* When one loads ammo into the weapon, its "amount" decreases, but its |
||||
* "total amount" stays the same. Unless specified otherwise, all the methods |
||||
* deal with a regular "amount". |
||||
* Copyright 2022 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 EAmmo extends EItem |
||||
abstract; |
||||
|
||||
/** |
||||
* Changes amount of ammo inside referred ammunition item by given `amount`. |
||||
* |
||||
* Negative argument values will decrease it ("adding" a negative amount). |
||||
* |
||||
* New value cannot go below zero and can only exceed maximum amount |
||||
* (@see `GetMaxAmount()` and @see `GetMaxTotalAmount()`) if `forceAddition` |
||||
* is also set to `true`. |
||||
* If resulting value is to go over the limits - it will be clamped inside |
||||
* allowed range. |
||||
* |
||||
* @param amount How much ammo to add. `0` does nothing, negative |
||||
* values decrease the total ammo count. Cannot force total ammo to go into |
||||
* negative values. |
||||
* @param forceAddition This parameter is only relevant when changing ammo |
||||
* amount by `amount` will go over maximum (total) amount that referred |
||||
* ammo item can store. Setting this parameter to `true` will allow you to |
||||
* add more ammo than caller `EAmmo` normally supports. |
||||
* Cannot force total amount below zero. |
||||
*/ |
||||
public function Add(int amount, optional bool forceAddition) {} |
||||
|
||||
/** |
||||
* Returns current price of total ammo inside inventory of the owner of |
||||
* referred ammunition item. |
||||
* |
||||
* In comparison, `EItem`'s method `GetPrice()` returns the price of only |
||||
* the ammo inside the referred item. |
||||
* @return Current price of total ammo inside inventory of the owner of |
||||
* referred ammunition item. |
||||
*/ |
||||
public function int GetTotalPrice() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* Returns how much would `ammoAmount` amount of referred ammo item would cost. |
||||
* |
||||
* @return Price of `ammoAmount` amount of referred ammo item. |
||||
*/ |
||||
public function int GetPriceOf(int ammoAmount) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* Returns current amount of ammo inside referred ammunition item. |
||||
* |
||||
* Guaranteed to not be negative, but can exceed maximum value |
||||
* (@see `GetMaxAmount()`). |
||||
* |
||||
* @return Current amount of ammo inside referred ammunition item. |
||||
*/ |
||||
public function int GetAmount() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* Returns current total amount of ammo inside inventory of the owner of |
||||
* referred ammunition item. |
||||
* |
||||
* Guaranteed to not be negative, but can exceed maximum value |
||||
* (@see `GetMaxTotalAmount()`). |
||||
* |
||||
* @return Current amount of ammo inside referred ammunition item. |
||||
*/ |
||||
public function int GetTotalAmount() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* Changes amount of ammo inside referred ammunition item. |
||||
* |
||||
* Negative values will be treated as `0`. Values that exceed |
||||
* maximum (total) amount will be automatically reduced to said maximum amount |
||||
* (@see `GetMaxAmount()` and @see `GetMaxTotalAmount()`), unless |
||||
* `forceAddition` is also set to `true`. |
||||
* If resulting value is to go over the limits - it will be clamped inside |
||||
* allowed range. |
||||
* |
||||
* @param amount How much ammo should referred ammunition item have. |
||||
* Negative values are treated like `0`. |
||||
* @param forceAddition This parameter is only relevant when `amount` is |
||||
* higher than maximum (total) amount that referred ammo item can store. |
||||
* Setting this parameter to `true` will allow you to add more ammo than |
||||
* caller `EAmmo` normally supports. Cannot force total amount below zero. |
||||
*/ |
||||
public function SetAmount(int amount, optional bool forceAddition) {} |
||||
|
||||
/** |
||||
* Returns maximum amount of ammo referred ammunition item supports. |
||||
* |
||||
* This is not a hard limit and can be bypassed by `SetAmount()` and |
||||
* `Add()` methods, meaning that it is possible that |
||||
* `GetAmount() > GetMaxAmount()`. |
||||
* Treat this value like a limit obtainable through "normal means", that |
||||
* can only be exceeded through cheats or special powerups of some kind. |
||||
* |
||||
* @return Current "soft" max ammo limit of the referred ammunition item. |
||||
* Returning negative value means that there is no upper limit. |
||||
* Zero is considered a valid value. |
||||
*/ |
||||
public function int GetMaxAmount() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* Returns maximum total amount of ammo owner of the referred ammunition item |
||||
* can hold. |
||||
* |
||||
* This is not a hard limit and can be bypassed by `SetAmount()` and |
||||
* `Add()` methods, meaning that it is possible that |
||||
* `GetTotalAmount() > GetMaxTotalAmount()`. |
||||
* Treat this value like a limit obtainable through "normal means", that |
||||
* can only be exceeded through cheats or special powerups of some kind. |
||||
* |
||||
* @return Current "soft" max total ammo limit of the referred ammunition item. |
||||
* Returning negative value means that there is no upper limit. |
||||
* Zero is considered a valid value. |
||||
*/ |
||||
public function int GetMaxTotalAmount() |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* Changes maximum amount of ammo referred ammunition item supports. |
||||
* |
||||
* This is not a hard limit and can be bypassed by `SetAmount()` and |
||||
* `Add()` methods, meaning that it is possible that |
||||
* `GetAmount() > GetMaxAmount()`. |
||||
* Treat this value like a limit obtainable through "normal means", that |
||||
* can only be exceeded through cheats or special powerups of some kind. |
||||
* |
||||
* Referred ammunition item does not have to support this method and is |
||||
* allowed to refuse changing maximum ammo value. It can also only support |
||||
* certain ranges of values. |
||||
* |
||||
* @param newMaxAmmo New maximum ammo referred ammunition |
||||
* should support. Negative values mean unlimited maximum value. |
||||
* @param leaveCurrentAmmo Default value of `false` will result in current |
||||
* ammo being updated to not exceed `newMaxAmmo`, while setting this to |
||||
* `true` will leave it unchanged. |
||||
* |
||||
* @return `true` if maximum value was changed and `false` otherwise. |
||||
*/ |
||||
public function bool SetMaxAmount( |
||||
int newMaxAmmo, |
||||
optional bool leaveCurrentAmmo) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Changes maximum total amount of ammo that owner of the referred |
||||
* ammunition item supports. |
||||
* |
||||
* This is not a hard limit and can be bypassed by `SetAmount()` and |
||||
* `Add()` methods, meaning that it is possible that |
||||
* `GetTotalAmount() > GetMaxTotalAmount()`. |
||||
* Treat this value like a limit obtainable through "normal means", that |
||||
* can only be exceeded through cheats or special powerups of some kind. |
||||
* |
||||
* Referred ammunition item does not have to support this method is allowed to |
||||
* refuse changing maximum ammo value. It can also only support certain ranges |
||||
* of values. |
||||
* |
||||
* @param newTotalMaxAmmo New maximum total ammo owner of the referred |
||||
* ammunition can have. Negative values mean unlimited maximum value. |
||||
* @param leaveCurrentAmmo Default value of `false` will result in current |
||||
* total ammo being updated to not exceed `newTotalMaxAmmo`, while setting |
||||
* this to `true` will leave it unchanged. |
||||
* |
||||
* @return `true` if maximum value was changed and `false` otherwise. |
||||
*/ |
||||
public function bool SetMaxTotalAmount( |
||||
int newTotalMaxAmmo, |
||||
optional bool leaveCurrentAmmo) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Checks whether the owner of the referred ammo item also has a weapon that |
||||
* can be loaded with that ammo. |
||||
* |
||||
* @return `true` if owner of the referred ammo has a weapon that can be |
||||
* loaded with that ammo and `false` otherwise. |
||||
*/ |
||||
public function bool HasWeapon() |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Maxes out amount of ammo of the referred ammunition item. |
||||
* |
||||
* Does nothing if current ammo is already at (or higher) than maximum value |
||||
* (@see `GetMaxAmount()`). |
||||
*/ |
||||
public function Fill() |
||||
{ |
||||
if (GetMaxAmount() < 0) return; |
||||
if (GetAmount() >= GetMaxAmount()) return; |
||||
|
||||
SetAmount(GetMaxAmount()); |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,57 @@
|
||||
/** |
||||
* Abstract interface that represents any kind of weapon. |
||||
* Copyright 2022 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 EWeapon extends EItem |
||||
abstract; |
||||
|
||||
/** |
||||
* Returns `EAmmo` for every ammo item that can be used with the caller's |
||||
* referred weapon. Method looks for that ammo in the weapon's owner's |
||||
* inventory. |
||||
* |
||||
* @return Array of `EAmmo`s that refer to ammo items suitable for use with |
||||
* referred weapon. Every item of the returned array is guaranteed to not |
||||
* be `none` and refer to an existent item. |
||||
*/ |
||||
public function array<EAmmo> GetAvailableAmmo() |
||||
{ |
||||
local array<EAmmo> emptyArray; |
||||
return emptyArray; |
||||
} |
||||
|
||||
/** |
||||
* Fills (@see `EAmmo.Fill()` method) `EAmmo` for every ammo item that can be |
||||
* used with the caller's referred weapon. Method looks for that ammo in |
||||
* the weapon's owner's inventory. |
||||
*/ |
||||
public final function FillAmmo() |
||||
{ |
||||
local int i; |
||||
local array<EAmmo> myAmmo; |
||||
myAmmo = GetAvailableAmmo(); |
||||
for (i = 0; i < myAmmo.length; i += 1) { |
||||
myAmmo[i].Fill(); |
||||
} |
||||
_.memory.FreeMany(myAmmo); |
||||
} |
||||
|
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,530 @@
|
||||
/** |
||||
* Low-level API that provides set of utility methods for working with |
||||
* unreal script inventory classes, including some Killing Floor specific |
||||
* methods that depend on how its weapons work. |
||||
* Copyright 2022 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 InventoryAPI extends AcediaObject |
||||
config(AcediaSystem); |
||||
|
||||
/** |
||||
* Describes a single-dual weapons class pair. |
||||
* For example, `single = class'MK23Pickup'` and |
||||
* `dual = class'DualMK23Pickup'`. |
||||
*/ |
||||
struct DualiesPair |
||||
{ |
||||
var class<KFWeaponPickup> single; |
||||
var class<KFWeaponPickup> dual; |
||||
}; |
||||
// All dual pairs that Acedia will recognize |
||||
var private const config array<DualiesPair> dualiesClasses; |
||||
|
||||
/** |
||||
* Describe the role of the weapon regarding a dual wielding. |
||||
* All weapons have a dual wielding role, although for most it |
||||
* is simply `DWR_None`. |
||||
*/ |
||||
enum DualWieldingRole |
||||
{ |
||||
// Not a dual weapons and cannot be dual wielded; |
||||
// Most weapons are in this category (e.g. lar, ak47, husk cannon, etc.) |
||||
DWR_None, |
||||
// Not a dual weapon, but can be dual wielded (e.g. single pistols) |
||||
DWR_Single, |
||||
// A dual weapon, consisted of two single ones (e.g. dual pistols) |
||||
DWR_Dual |
||||
}; |
||||
|
||||
/** |
||||
* Returns array of single - dual pairs (`DualiesPair`) that defines which |
||||
* single weapon class corresponds to which dual class. |
||||
* For example, `KFMod.GoldenDeaglePickup` is a single class corresponding |
||||
* to the `KFMod.GoldenDualDeaglePickup` dual class. |
||||
*/ |
||||
public function array<DualiesPair> GetDualiesPairs() |
||||
{ |
||||
return dualiesClasses; |
||||
} |
||||
|
||||
/** |
||||
* Returns dual wielding role of the given class of weapon `weaponClass`. |
||||
* See `DualWieldingRole` enum for more details. |
||||
* |
||||
* @param weaponClass Weapon class to check the role for. |
||||
* @return Dual wielding role of the weapon of given class `weaponClass`. |
||||
* `DWR_None` in case given `weaponClass` is `none`. |
||||
*/ |
||||
public function DualWieldingRole GetDualWieldingRole( |
||||
class<KFWeapon> weaponClass) |
||||
{ |
||||
local int i; |
||||
local class<KFWeaponPickup> pickupClass; |
||||
if (weaponClass == none) return DWR_None; |
||||
pickupClass = class<KFWeaponPickup>(weaponClass.default.pickupClass); |
||||
if (pickupClass == none) return DWR_None; |
||||
|
||||
for (i = 0; i < dualiesClasses.length; i += 1) |
||||
{ |
||||
if (dualiesClasses[i].single == pickupClass) { |
||||
return DWR_Single; |
||||
} |
||||
if (dualiesClasses[i].dual == pickupClass) { |
||||
return DWR_Dual; |
||||
} |
||||
} |
||||
return DWR_None; |
||||
} |
||||
|
||||
/** |
||||
* For "dual" weapons (`DWR_Dual`), corresponding of two "single" version |
||||
* returns class of corresponding single version, for any other |
||||
* (including single weapons themselves) returns `none`. |
||||
* |
||||
* @param weaponClass Weapon class for which to find matching single class. |
||||
* @return Single class that corresponds to the given `weaponClass`, if it is |
||||
* classified as `DWR_Dual`. `none` for every other class. |
||||
*/ |
||||
public function class<KFWeapon> GetSingleClass(class<KFWeapon> weapon) |
||||
{ |
||||
local int i; |
||||
local class<KFWeaponPickup> pickupClass; |
||||
local class<KFWeaponPickup> singlePickupClass; |
||||
if (weapon == none) return none; |
||||
pickupClass = class<KFWeaponPickup>(weapon.default.pickupClass); |
||||
if (pickupClass == none) return none; |
||||
|
||||
for (i = 0; i < dualiesClasses.length; i += 1) |
||||
{ |
||||
if (dualiesClasses[i].dual == pickupClass) |
||||
{ |
||||
singlePickupClass = dualiesClasses[i].single; |
||||
if (singlePickupClass != none) { |
||||
return class<KFWeapon>(singlePickupClass.default.inventoryType); |
||||
} |
||||
} |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
/** |
||||
* For "single" weapons (`DWR_Single`) that can have a "dual" version returns |
||||
* class of corresponding dual version, for any other (including dual weapons |
||||
* themselves) returns `none`. |
||||
* |
||||
* @param weaponClass Weapon class for which to find matching dual class. |
||||
* @return Dual class that corresponds to the given `weaponClass`, if it is |
||||
* classified as `DWR_Single`. `none` for every other class. |
||||
*/ |
||||
public function class<KFWeapon> GetDualClass(class<KFWeapon> weaponClass) |
||||
{ |
||||
local int i; |
||||
local class<KFWeaponPickup> pickupClass; |
||||
local class<KFWeaponPickup> dualPickupClass; |
||||
if (weaponClass == none) return none; |
||||
pickupClass = class<KFWeaponPickup>(weaponClass.default.pickupClass); |
||||
if (pickupClass == none) return none; |
||||
|
||||
for (i = 0; i < dualiesClasses.length; i += 1) |
||||
{ |
||||
if (dualiesClasses[i].single == pickupClass) |
||||
{ |
||||
dualPickupClass = dualiesClasses[i].dual; |
||||
if (dualPickupClass != none) { |
||||
return class<KFWeapon>(dualPickupClass.default.inventoryType); |
||||
} |
||||
} |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
/** |
||||
* 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 Get( |
||||
class<Inventory> 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 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<Inventory> GetAll( |
||||
class<Inventory> inventoryClass, |
||||
Inventory inventoryChain, |
||||
optional bool acceptChildClass) |
||||
{ |
||||
local bool shouldAdd; |
||||
local array<Inventory> 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; |
||||
} |
||||
|
||||
/** |
||||
* Checks whether `inventory` is contained in the inventory given by |
||||
* `inventoryChain`. |
||||
* |
||||
* @param inventory Item we are searching for. |
||||
* @param inventoryChain Inventory chain in which we should search for |
||||
* the given item. |
||||
* @return `true` if `inventoryChain` contains `inventory` and |
||||
* `false` otherwise. |
||||
*/ |
||||
public final function bool Contains( |
||||
Inventory inventory, |
||||
Inventory inventoryChain) |
||||
{ |
||||
while (inventoryChain != none) |
||||
{ |
||||
if (inventoryChain == inventory) { |
||||
return true; |
||||
} |
||||
inventoryChain = inventoryChain.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: |
||||
* 1. Vanilla game rules are such that player can only have two weapons |
||||
* in the inventory if they have different roots; |
||||
* 2. Root is easy to find. |
||||
* |
||||
* @param weaponClass Weapon class for which we must find root class. |
||||
* @return Root class for the provided `weaponClass` class. |
||||
* If `weaponClass` is `none`, method will also return `none`. |
||||
*/ |
||||
public final function class<KFWeaponPickup> GetRootPickupClass( |
||||
class<KFWeapon> weaponClass) |
||||
{ |
||||
local int i; |
||||
local class<KFWeaponPickup> root; |
||||
if (weaponClass == none) return none; |
||||
// Start with a pickup of the given weapons |
||||
root = class<KFWeaponPickup>(weaponClass.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 != none && root.default.variantClasses.length > 0) { |
||||
root = class<KFWeaponPickup>(root.default.variantClasses[0]); |
||||
} |
||||
return root; |
||||
} |
||||
|
||||
/** |
||||
* Convenience method for finding a first inventory entry with the same root as |
||||
* class `inventoryClass` in the given inventory chain `inventoryChain`. |
||||
* For information of what a "root" is, see `GetRootPickupClass()`. |
||||
* |
||||
* 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. |
||||
* @return First inventory from `inventoryChain` that has the same root as |
||||
* given `inventoryClass` class. |
||||
*/ |
||||
public function KFWeapon GetByRoot( |
||||
class<KFWeapon> inventoryClass, |
||||
Inventory inventoryChain) |
||||
{ |
||||
local class<KFWeapon> nextWeaponClass; |
||||
local class<KFWeaponPickup> itemRoot, nextRoot; |
||||
itemRoot = GetRootPickupClass(inventoryClass); |
||||
if (itemRoot == none) { |
||||
return none; |
||||
} |
||||
while (inventoryChain != none) |
||||
{ |
||||
nextWeaponClass = class<KFWeapon>(inventoryChain.class); |
||||
nextRoot = GetRootPickupClass(nextWeaponClass); |
||||
if (itemRoot == nextRoot) { |
||||
return KFWeapon(inventoryChain); |
||||
} |
||||
inventoryChain = inventoryChain.inventory; |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
/** |
||||
* Convenience method for finding all inventory entries with the same root as |
||||
* class `inventoryClass` in the given inventory chain `inventoryChain`. |
||||
* For information of what a "root" is, see `GetRootPickupClass()`. |
||||
* |
||||
* 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. |
||||
* @return Array of inventory items from `inventoryChain` that have the same |
||||
* root as given `inventoryClass` class. |
||||
*/ |
||||
public function array<KFWeapon> GetAllByRoot( |
||||
class<KFWeapon> inventoryClass, |
||||
Inventory inventoryChain) |
||||
{ |
||||
local array<KFWeapon> result; |
||||
local KFWeapon nextWeapon; |
||||
local class<KFWeapon> nextWeaponClass; |
||||
local class<KFWeaponPickup> itemRoot, nextRoot; |
||||
itemRoot = GetRootPickupClass(inventoryClass); |
||||
if (itemRoot == none) { |
||||
return result; |
||||
} |
||||
while (inventoryChain != none) |
||||
{ |
||||
nextWeaponClass = class<KFWeapon>(inventoryChain.class); |
||||
nextRoot = GetRootPickupClass(nextWeaponClass); |
||||
if (itemRoot == nextRoot) { |
||||
nextWeapon = KFWeapon(inventoryChain); |
||||
} |
||||
if (nextWeapon != none) |
||||
{ |
||||
result[result.length] = nextWeapon; |
||||
nextWeapon = none; |
||||
} |
||||
inventoryChain = inventoryChain.inventory; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns ammunition class for the given `weapon` weapon, that it uses for |
||||
* fire mode numbered `modeNumber`. |
||||
* |
||||
* @param weapon Weapon for which ammunition class should be found. |
||||
* @param modeNumber Fire mode for which ammunition class should be found. |
||||
* @return Class of ammunition used for `weapon`'s fire mode, |
||||
* numbered `modeNumber`. |
||||
* `none` if `weapon` is `none`, fire mode does not exist or |
||||
* it is not associated with inventory ammo class. |
||||
*/ |
||||
public function class<Ammunition> GetAmmoClass(Weapon weapon, int modeNumber) |
||||
{ |
||||
local WeaponFire relevantWeaponFire; |
||||
if (weapon == none) { |
||||
return none; |
||||
} |
||||
// Just use majestic rjp's hack method `GetFireMode()` to get ammo class |
||||
// through a weapon fire |
||||
relevantWeaponFire = weapon.GetFireMode(modeNumber); |
||||
if (relevantWeaponFire != none) { |
||||
return relevantWeaponFire.ammoClass; |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
/** |
||||
* Removes all ammo from the given `weapon`. Assumes weapons has no more than |
||||
* two fire modes. |
||||
* |
||||
* In case given weapon is a child class of `KFWeapon`, also clears its |
||||
* magazine counter. |
||||
* |
||||
* @param weapon Weapon to remove all ammo from. If `none`, |
||||
* method does nothing. |
||||
*/ |
||||
public final function ClearAmmo(Weapon weapon) |
||||
{ |
||||
local InventoryService service; |
||||
service = InventoryService(class'InventoryService'.static.Require()); |
||||
if (service != none) { |
||||
service.ClearAmmo(weapon); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates and adds a weapons of the given class to the `pawn` with specified |
||||
* amount of ammunition. |
||||
* |
||||
* @param pawn `Pawn` to which we should add new weapon. |
||||
* If `none` - method does nothing. |
||||
* @param weaponClassToAdd Class of the weapon we need to add. |
||||
* If `none` - method does nothing. |
||||
* @param totalAmmoPrimary Ammo to add to the primary fire. |
||||
* @param totalAmmoSecondary Ammo to add to the secondary fire. |
||||
* @param magazineAmmo Ammo to add to the new weapon's magazine count. |
||||
* Only relevant if `weaponClassToAdd` is a child class of `KFWeapon` and |
||||
* otherwise ignored. |
||||
* @param clearStarterAmmo Newly created weapons usually come with some |
||||
* default amount of ammo. Setting this flag to `true` will remove it |
||||
* before adding `totalAmmoPrimary`, `totalAmmoSecondary` and |
||||
* `magazineAmmo`. |
||||
* @return Instance of the newly created weapon. `none` in case of failure or |
||||
* if created weapon was destroyed in the process of adding it to |
||||
* the `pawn` (can happen as a result of interaction with preexisting |
||||
* weapons - e.g. pistol can merge with another one of the same type and |
||||
* produce a new weapon). |
||||
*/ |
||||
public function Weapon AddWeaponWithAmmo( |
||||
Pawn pawn, |
||||
class<Weapon> weaponClassToAdd, |
||||
optional int totalAmmoPrimary, |
||||
optional int totalAmmoSecondary, |
||||
optional int magazineAmmo, |
||||
optional bool clearStarterAmmo) |
||||
{ |
||||
local InventoryService service; |
||||
service = InventoryService(class'InventoryService'.static.Require()); |
||||
if (service == none) { |
||||
return none; |
||||
} |
||||
return service.AddWeaponWithAmmo( pawn, weaponClassToAdd, |
||||
totalAmmoPrimary, totalAmmoSecondary, |
||||
magazineAmmo, clearStarterAmmo); |
||||
} |
||||
|
||||
/** |
||||
* Auxiliary method for "merging" weapons. Basically acts as |
||||
* a `AddWeaponWithAmmo()` - creates and adds a weapons of the given class to |
||||
* the `pawn`. But instead of taking numeric parameters to specify starter |
||||
* ammunition, copies ammunition counts (adding them together) from two weapons |
||||
* (`weaponToMerge1` and `weaponToMerge2`) specified for "merging" |
||||
* |
||||
* @param pawn `Pawn` to which we should add new merged weapon. |
||||
* If `none` - method does nothing. |
||||
* @param mergedClass Class of the weapon we need to add as a result |
||||
* of "merging". If `none` - method does nothing. |
||||
* @param weaponToMerge1 First weapon from which to copy ammunition counts. |
||||
* In case it is of a child class of `KFWeapon`, also copies magazine size. |
||||
* If `none` - assumes all ammo counts to be zero. |
||||
* @param weaponToMerge2 Second weapon from which to copy ammunition counts. |
||||
* Completely interchangeable with `weaponToMerge1`. |
||||
* @param clearStarterAmmo Newly created weapons usually come with some |
||||
* default amount of ammo. Setting this flag to `true` will remove it |
||||
* before adding ammunition counts from `weaponToMerge1` and |
||||
* `weaponToMerge2`. |
||||
* @return Instance of the newly created weapon. `none` in case of failure or |
||||
* if created weapon was destroyed in the process of adding it to |
||||
* the `pawn` (can happen as a result of interaction with preexisting |
||||
* weapons - e.g. pistol can merge with another one of the same type and |
||||
* produce a new weapon). |
||||
*/ |
||||
public function Weapon MergeWeapons( |
||||
Pawn ownerPawn, |
||||
class<Weapon> mergedClass, |
||||
optional Weapon weaponToMerge1, |
||||
optional Weapon weaponToMerge2, |
||||
optional bool clearStarterAmmo) |
||||
{ |
||||
local InventoryService service; |
||||
service = InventoryService(class'InventoryService'.static.Require()); |
||||
if (service == none) { |
||||
return none; |
||||
} |
||||
return service.MergeWeapons(ownerPawn, mergedClass, |
||||
weaponToMerge1, weaponToMerge2); |
||||
} |
||||
|
||||
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') |
||||
} |
@ -0,0 +1,128 @@
|
||||
/** |
||||
* Service that simply does some of the work of `InventoryService`, since |
||||
* working with `Actor`s that can get destroyed in the process is much safer |
||||
* inside another `Actor`. |
||||
* For description of all methods see `InventoryAPI`. |
||||
* Copyright 2022 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 InventoryService extends Service; |
||||
|
||||
public function Weapon AddWeaponWithAmmo( |
||||
Pawn pawn, |
||||
class<Weapon> weaponClassToAdd, |
||||
optional int totalAmmoPrimary, |
||||
optional int totalAmmoSecondary, |
||||
optional int magazineAmmo, |
||||
optional bool clearStarterAmmo) |
||||
{ |
||||
local Weapon newWeapon; |
||||
local KFWeapon newKFWeapon; |
||||
if (pawn == none) return none; |
||||
newWeapon = Weapon(_.memory.Allocate(weaponClassToAdd)); |
||||
if (newWeapon == none) return none; |
||||
// It is possible that `newWeapon` can get destroyed somewhere here, |
||||
// so add two more checks |
||||
_.unreal.GetKFGameType().WeaponSpawned(newWeapon); |
||||
if (newWeapon == none) return none; |
||||
newWeapon.GiveTo(pawn); |
||||
if (newWeapon == none) return none; |
||||
|
||||
// Update ammo & magazine (if applicable) |
||||
if (clearStarterAmmo) { |
||||
ClearAmmo(newWeapon); |
||||
} |
||||
newKFWeapon = KFWeapon(newWeapon); |
||||
if (newKFWeapon != none) |
||||
{ |
||||
if (clearStarterAmmo) { |
||||
newKFWeapon.magAmmoRemaining = 0; |
||||
} |
||||
newKFWeapon.magAmmoRemaining += magazineAmmo; |
||||
} |
||||
if (totalAmmoPrimary > 0) { |
||||
newWeapon.AddAmmo(totalAmmoPrimary, 0); |
||||
} |
||||
if (totalAmmoSecondary > 0) { |
||||
newWeapon.AddAmmo(totalAmmoSecondary, 1); |
||||
} |
||||
return newWeapon; |
||||
} |
||||
|
||||
public function Weapon MergeWeapons( |
||||
Pawn pawn, |
||||
class<Weapon> mergedClass, |
||||
optional Weapon weaponToMerge1, |
||||
optional Weapon weaponToMerge2, |
||||
optional bool clearStarterAmmo) |
||||
{ |
||||
local int totalAmmoPrimary, totalAmmoSecondary, magazineAmmo; |
||||
local KFWeapon kfWeapon; |
||||
if (pawn == none) { |
||||
return none; |
||||
} |
||||
if (weaponToMerge1 != none) |
||||
{ |
||||
kfWeapon = KFWeapon(weaponToMerge1); |
||||
if (kfWeapon != none) { |
||||
magazineAmmo += kfWeapon.magAmmoRemaining; |
||||
} |
||||
totalAmmoPrimary += weaponToMerge1.AmmoAmount(0); |
||||
totalAmmoSecondary += weaponToMerge1.AmmoAmount(1); |
||||
weaponToMerge1.Destroyed(); |
||||
if (weaponToMerge1 != none) { |
||||
weaponToMerge1.Destroy(); |
||||
} |
||||
} |
||||
if (weaponToMerge2 != none) |
||||
{ |
||||
kfWeapon = KFWeapon(weaponToMerge2); |
||||
if (kfWeapon != none) { |
||||
magazineAmmo += kfWeapon.magAmmoRemaining; |
||||
} |
||||
totalAmmoPrimary += weaponToMerge2.AmmoAmount(0); |
||||
totalAmmoSecondary += weaponToMerge2.AmmoAmount(1); |
||||
weaponToMerge2.Destroyed(); |
||||
if (weaponToMerge2 != none) { |
||||
weaponToMerge2.Destroy(); |
||||
} |
||||
} |
||||
return AddWeaponWithAmmo( pawn, mergedClass, totalAmmoPrimary, |
||||
totalAmmoSecondary, magazineAmmo, |
||||
clearStarterAmmo); |
||||
} |
||||
|
||||
public final function ClearAmmo(Weapon weapon) |
||||
{ |
||||
local float auxiliary, currentAmmoPrimary, currentAmmoSecondary; |
||||
local KFWeapon kfWeapon; |
||||
if (weapon == none) { |
||||
return; |
||||
} |
||||
weapon.GetAmmoCount(auxiliary, currentAmmoPrimary); |
||||
//weapon.GetSecondaryAmmoCount(auxiliary, currentAmmoSecondary); |
||||
weapon.AddAmmo(-currentAmmoPrimary, 0); |
||||
weapon.AddAmmo(-currentAmmoSecondary, 1); |
||||
kfWeapon = KFWeapon(weapon); |
||||
if (kfWeapon != none) { |
||||
kfWeapon.magAmmoRemaining = 0; |
||||
} |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
Loading…
Reference in new issue