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