Browse Source

Add first draft of API for the game mechanics

pull/8/head
Anton Tarasenko 4 years ago
parent
commit
45151891bc
  1. 26
      sources/Gameplay/BaseClasses/BaseBackend.uc
  2. 26
      sources/Gameplay/BaseClasses/BaseFrontend.uc
  3. 41
      sources/Gameplay/BaseClasses/KillingFloor/KFFrontend.uc
  4. 196
      sources/Gameplay/BaseClasses/KillingFloor/Trading/ATrader.uc
  5. 204
      sources/Gameplay/BaseClasses/KillingFloor/Trading/ATradingComponent.uc
  6. 39
      sources/Gameplay/BaseClasses/KillingFloor/Trading/Events/Trading_OnSelect_Signal.uc
  7. 41
      sources/Gameplay/BaseClasses/KillingFloor/Trading/Events/Trading_OnSelect_Slot.uc
  8. 27
      sources/Gameplay/KF1Frontend/KF1_Frontend.uc
  9. 248
      sources/Gameplay/KF1Frontend/Trading/KF1_Trader.uc
  10. 213
      sources/Gameplay/KF1Frontend/Trading/KF1_TradingComponent.uc
  11. 3
      sources/Global.uc

26
sources/Gameplay/BaseClasses/BaseBackend.uc

@ -0,0 +1,26 @@
/**
* Base class for all backends. Does not define anything meaningful, which
* also means it does not put any limitations on it's implementation.
* Copyright 2021 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* Acedia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class BaseBackend extends AcediaObject
abstract;
defaultproperties
{
}

26
sources/Gameplay/BaseClasses/BaseFrontend.uc

@ -0,0 +1,26 @@
/**
* Base class for all frontends. Does not define anything meaningful, which
* also means it does not put any limitations on it's implementation.
* Copyright 2021 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* Acedia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class BaseFrontend extends AcediaObject
abstract;
defaultproperties
{
}

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

@ -0,0 +1,41 @@
/**
* Frontend skeleton for basic killing floor game mode.
* Copyright 2021 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* Acedia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class KFFrontend extends BaseBackend
abstract;
var private config class<ATradingComponent> tradingClass;
var public ATradingComponent trading;
protected function Constructor()
{
if (tradingClass != none) {
trading = ATradingComponent(_.memory.Allocate(tradingClass));
}
}
protected function Finalizer()
{
_.memory.Free(trading);
}
defaultproperties
{
tradingClass = none
}

196
sources/Gameplay/BaseClasses/KillingFloor/Trading/ATrader.uc

@ -0,0 +1,196 @@
/**
* Class, objects of which are expected to represent traders located on
* the map. In classic KF game mode it would represent areas behind closed
* doors that open during trader time and allow to purchase weapons and ammo.
* Copyright 2021 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* Acedia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class ATrader extends AcediaObject
abstract;
/**
* Returns location of the trader.
*
* Trader is usually associated with an area where players can trade and
* not just one point. Value returned by this method is merely expected to
* return position that "makes sense" for the trader.
* It can be used to calculate distance and/or path to the trader.
*
* @return Location of the caller trader.
*/
public function Vector GetLocation();
/**
* Returns name of the trader.
*
* Trader name can be any non-empty `Text`.
* The only requirement is that after map's initialization every trader
* should have a unique name. It is not forbidden to break this invariant later
* by `SetName()` method.
* If `none` or empty name is passed, this method should do nothing.
*
* This is not the hard requirement, but explanation of purpose.
* Name does not have to be player-friendly, but it must be human-readable:
* it is not expected to be seen by regular players, but admins might use it
* to tweak their server.
*
* @return Current name of the trader.
*/
public function Text GetName();
/**
* Changes name of the trader.
*
* @see `GetName()` for more details.
*
* @param newName New name of the caller trader.
* @return `true` if trader is currently enabled and `false` otherwise.
*/
public function ATrader SetName(Text newName);
/**
* Checks if caller trader is currently enabled.
*
* Trader being enabled means that it can be opened and used for trading.
* Trader being disabled means that it cannot open for trading.
*
* This should override opened and auto-opened status.
*
* Marking disabled trader as selected is discouraged, especially for classic
* KF game mode, but should be allowed.
*
* @return `true` if trader is currently enabled and `false` otherwise.
*/
public function bool IsEnabled();
/**
* Sets whether caller `ATrader`'s is currently enabled.
*
* Disabling the trader should automatically "boot" players out
* (see `BootPlayers()`).
*
* @see `IsEnabled()` for more info.
*
* @param doEnable `true` if trader is currently enabled and
* `false` otherwise.
* @return Caller `ATrader` to allow for method chaining.
*/
public function ATrader SetEnabled(bool doEnable);
/**
* Checks whether caller `ATrader` will auto-open when trading gets activated.
*
* This setting must be ignored if trader is disabled, but disabling `ATrader`
* should not reset it.
*
* @return `true` if trader is marked to always auto-open upon activating
* trading (unless it is also disabled) and `false` otherwise.
*/
public function bool IsAutoOpen();
/**
* Checks whether caller `ATrader` will auto-open when trading gets activated.
*
* @see `IsAutoOpen()` for more info.
*
* @param doAutoOpen `true` if trader should be marked to always auto-open
* upon activating trading and `false` otherwise.
* @return Caller `ATrader` to allow for method chaining.
*/
public function ATrader SetAutoOpen(bool doAutoOpen);
/**
* Checks whether caller `ATrader` is currently open.
*
* `ATrader` being open means that players can "enter" (whatever that means for
* an implementation) and use `ATrader` to buy/sell equipment.
*
* @return `true` if it is open and `false` otherwise.
*/
public function bool IsOpen();
/**
* Changes whether caller `ATrader` is open.
*
* Closing the trader should not automatically "boot" players out
* (see `BootPlayers()`).
*
* @see `IsOpen()` for more details.
*
* @param doOpen `true` if it is open and `false` otherwise.
* @return Caller `ATrader` to allow for method chaining.
*/
public function ATrader SetOpen(bool doOpen);
/**
* Checks whether caller `ATrader` is currently marked as selected.
*
* @see `ATradingComponent.GetSelectedTrader()` for more details.
*
* @return `true` if caller `ATrader` is selected and `false` otherwise.
*/
public function bool IsSelected();
/**
* Marks caller `ATrader` as a selected trader.
*
* @see `ATradingComponent.GetSelectedTrader()` for more details.
*
* @return Caller `ATrader` to allow for method chaining.
*/
public function ATrader Select();
/**
* Removes players from the trader's place.
*
* In classic KF game mode it teleported them right outside the doors.
*
* This method's goal is to make sure players are not stuck in trader's place
* after it is closed. If that is impossible (for traders resembling
* KF2's one), then this method should do nothing.
*
* @return Caller `ATrader` to allow for method chaining.
*/
public function ATrader BootPlayers();
/**
* Shortcut method to open the caller trader, guaranteed to be equivalent to
* `SetOpen(true)`. Provided for better interface.
*
* @return Caller `ATrader` to allow for method chaining.
*/
public final function ATrader Open()
{
SetOpen(true);
return self;
}
/**
* Shortcut method to close the caller trader, guaranteed to be equivalent to
* `SetOpen(false)`. Provided for better interface.
*
* @return Caller `ATrader` to allow for method chaining.
*/
public final function ATrader Close()
{
SetOpen(false);
return self;
}
defaultproperties
{
}

204
sources/Gameplay/BaseClasses/KillingFloor/Trading/ATradingComponent.uc

@ -0,0 +1,204 @@
/**
* Subset of functionality for dealing with everything related to traders.
* Copyright 2021 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* Acedia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class ATradingComponent extends AcediaObject
abstract;
var protected SimpleSignal onStartSignal;
var protected SimpleSignal onEndSignal;
var protected Trading_OnSelect_Signal onTraderSelectSignal;
protected function Constructor()
{
onStartSignal = SimpleSignal(_.memory.Allocate(class'SimpleSignal'));
onEndSignal = SimpleSignal(_.memory.Allocate(class'SimpleSignal'));
onTraderSelectSignal = Trading_OnSelect_Signal(
_.memory.Allocate(class'Trading_OnSelect_Signal'));
}
protected function Finalizer()
{
_.memory.Free(onStartSignal);
_.memory.Free(onEndSignal);
_.memory.Free(onTraderSelectSignal);
}
/**
* Signal that will be emitted whenever trading time starts.
*
* [Signature]
* void <slot>()
*/
/* SIGNAL */
public final function SimpleSlot OnStart(AcediaObject receiver)
{
return SimpleSlot(onStartSignal.NewSlot(receiver));
}
/**
* Signal that will be emitted whenever trading time ends.
*
* [Signature]
* void <slot>(ATrader oldTrader, ATrader newTrader)
*
* @param oldTrader Trader that was selected before this event.
* @param newTrader Trader that will be selected after this event.
*/
/* SIGNAL */
public final function SimpleSlot OnEnd(AcediaObject receiver)
{
return SimpleSlot(onEndSignal.NewSlot(receiver));
}
/**
* Signal that will be emitted whenever a new trader is selected.
*
* [Signature]
* void <slot>()
*/
/* SIGNAL */
public final function Trading_OnSelect_Slot OnTraderSelected(
AcediaObject receiver)
{
return Trading_OnSelect_Slot(onTraderSelectSignal.NewSlot(receiver));
}
/**
* Returns array with all existing traders (including disabled once) on
* the level.
*
* @return Array of existing traders on the level. Guaranteed to not contain
* `none`-references. None of them should be deallocated,
* otherwise Acedia's behavior is undefined.
*/
public function array<ATrader> GetTraders();
/**
* Checks whether trading is currently active.
*
* For classic KF game mode it means that it is trader time and one
* (or several) traders are open.
* This interface does not impose such limitation on trading: it is
* allowed to be active at any time, independent of anything else. However
* trading should only be permitted while trading is active.
*
* @return `true` if trading is active and `false` otherwise.
*/
public function bool IsTradingActive();
/**
* Changes current status of trading.
*
* @see `IsTradingActive()` for more details.
*/
public function SetTradingStatus(bool makeActive);
/**
* Returns the amount of time (in seconds) trading period will last for.
*
* For classic KF game mode it refers to how long trader time is
* (`60` seconds by default).
*
* @return Amount of time (in seconds) trading period will last for.
*/
public function int GetTradingInterval();
/**
* Changes the amount of time (in seconds) trading period will last for.
*
* Changing this setting only affect current round (until the end of the map).
*
* For classic KF game mode it refers to how long trader time is
* (`60` seconds by default).
*
* @param newTradingInterval New length of the trading period.
*/
public function SetTradingInterval(int newTradingInterval);
/**
* Return amount of time remaining in the current trading period.
*
* For classic KF game mode this refers to remaining trading time.
*
* @return Amount of time remaining in the current trading period.
* `0` if trading is currently inactive.
*/
public function int GetCountdown();
/**
* Changes amount of time remaining in the current trading period.
*
* For classic KF game mode this refers to remaining trading time.
*
* @param newTradingInterval New amount of time that should remain in the
* current trading period. Values `<= 0` will lead to trading time ending
* immediately.
*/
public function SetCountdown(int newTradingInterval);
/**
* Checks whether trading countdown was paused.
*
* Pause only affects current trading period and will be reset after
* the next starts.
*
* @return `true` if trading countdown was paused and `false` otherwise.
* If trading is inactive - returns `false`.
*/
public function bool IsCountDownPaused();
/**
* Changes whether trading countdown should be paused.
*
* Pause set by this method only affects current trading period and will be
* reset after the next starts.
*
* @return doPause `true` to pause trading countdown and `false` to resume.
* If trading time is currently inactive - does nothing.
*/
public function SetCountDownPause(bool doPause);
/**
* Returns currently selected trader.
*
* For classing KF game mode selected trader means the trader currently
* pointed at by the arrow in the top left corner on HUD and by the red wisp
* during trading time.
* This interface allows to generalize the concept of select trader to any
* specially marked trader or even not make use of it at all.
* Changing a selected trader in any way should always be followed
* by emitting `OnTraderSelected()` signal.
* After `SelectTrader()` call `GetSelectedTrader()` should return
* specified `ATrader`. If selected trader changes in some other way, it should
* first result in emitted `OnTraderSelected()` signal.
*
* @return Currently selected trader.
*/
public function ATrader GetSelectedTrader();
/**
* Changes currently selected trader.
*
* @see `GetSelectedTrader()` for more details.
*/
public function SelectTrader(ATrader newSelection);
defaultproperties
{
}

39
sources/Gameplay/BaseClasses/KillingFloor/Trading/Events/Trading_OnSelect_Signal.uc

@ -0,0 +1,39 @@
/**
* Signal class implementation for `ATradingComponent`, for detecting when
* another trader is selected.
* Copyright 2021 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* Acedia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class Trading_OnSelect_Signal extends Signal;
public final function Emit(ATrader oldTrader, ATrader newTrader)
{
local Slot nextSlot;
StartIterating();
nextSlot = GetNextSlot();
while (nextSlot != none)
{
Trading_OnSelect_Slot(nextSlot).connect(oldTrader, newTrader);
nextSlot = GetNextSlot();
}
CleanEmptySlots();
}
defaultproperties
{
relatedSlotClass = class'Trading_OnSelect_Slot'
}

41
sources/Gameplay/BaseClasses/KillingFloor/Trading/Events/Trading_OnSelect_Slot.uc

@ -0,0 +1,41 @@
/**
* Slot class implementation for `ATradingComponent`'s signal for
* detecting when another trader is selected.
* Copyright 2021 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* Acedia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class Trading_OnSelect_Slot extends Slot;
delegate connect(ATrader oldTrader, ATrader newTrader)
{
DummyCall();
}
protected function Constructor()
{
connect = none;
}
protected function Finalizer()
{
super.Finalizer();
connect = none;
}
defaultproperties
{
}

27
sources/Gameplay/KF1Frontend/KF1_Frontend.uc

@ -0,0 +1,27 @@
/**
* Frontend implementation for classic `KFGameType` that changes as little as
* possible and only on request from another mod, otherwise not altering
* gameplay at all.
* Copyright 2021 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* Acedia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class KF1_Frontend extends KFFrontend;
defaultproperties
{
tradingClass = class'KF1_TradingComponent'
}

248
sources/Gameplay/KF1Frontend/Trading/KF1_Trader.uc

@ -0,0 +1,248 @@
/**
* `ATrader`'s implementation for `KF1_Frontend`.
* Wrapper for KF1's `ShopVolume`s.
* Copyright 2021 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* Acedia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class KF1_Trader extends ATrader;
// We do not use any vanilla value as a name, instead storing and tracking it
// entirely as our own value.
var protected Text myName;
// Reference to `ShopVolume` actor that this `KF1_Trader` represents.
var protected NativeActorRef myShopVolume;
protected function Finalizer()
{
_.memory.Free(myShopVolume);
myShopVolume = none;
}
/**
* Detect all existing traders on the level and created a `KF1_Trader` for
* each of them.
*
* @return Array of created `KF1_Trader`s. All of them are guaranteed to not
* be `none`.
*/
public static function array<KF1_Trader> WrapVanillaShops()
{
local int shopCounter;
local MutableText textBuilder;
local LevelInfo level;
local KFGameType kfGame;
local KF1_Trader nextTrader;
local array<KF1_Trader> allTraders;
local ShopVolume nextShopVolume;
level = __().unreal.GetLevel();
kfGame = __().unreal.GetKFGameType();
textBuilder = __().text.Empty();
foreach level.AllActors(class'ShopVolume', nextShopVolume)
{
if (nextShopVolume == none) continue;
if (!nextShopVolume.bObjectiveModeOnly || kfGame.bUsingObjectiveMode)
{
nextTrader = KF1_Trader(__().memory.Allocate(class'KF1_Trader'));
nextTrader.myShopVolume = __().unreal.ActorRef(nextShopVolume);
textBuilder.Clear().AppendPlainString("trader" $ shopCounter);
nextTrader.myName = textBuilder.Copy();
allTraders[allTraders.length] = nextTrader;
shopCounter += 1;
}
}
textBuilder.FreeSelf();
return allTraders;
}
public function Text GetName()
{
if (myName == none) {
return _.text.Empty();
}
return myName.Copy();
}
public function ATrader SetName(Text newName)
{
if (newName == none) return self;
if (newName.IsEmpty()) return self;
myName.FreeSelf();
newName = newName.Copy();
return self;
}
public function Vector GetLocation()
{
local ShopVolume vanillaShopVolume;
vanillaShopVolume = ShopVolume(myShopVolume.Get());
if (vanillaShopVolume != none) {
return vanillaShopVolume.location;
}
return Vect(0, 0, 0);
}
public function bool IsEnabled()
{
local ShopVolume vanillaShopVolume;
vanillaShopVolume = ShopVolume(myShopVolume.Get());
if (vanillaShopVolume != none) {
return !vanillaShopVolume.bAlwaysClosed;
}
return false;
}
public function ATrader SetEnabled(bool doEnable)
{
local ShopVolume vanillaShopVolume;
vanillaShopVolume = ShopVolume(myShopVolume.Get());
if (vanillaShopVolume == none) {
return self;
}
if (doEnable) {
vanillaShopVolume.bAlwaysClosed = false;
}
else
{
vanillaShopVolume.bAlwaysClosed = true;
Close();
BootPlayers();
}
UpdateShopList();
return self;
}
/**
* This method re-fills `KFGameType.shopList` to contain only currently
* enabled traders.
*/
protected function UpdateShopList()
{
local int i;
local ShopVolume nextShopVolume;
local KF1_Trader nextTrader;
local array<ShopVolume> shopVolumes;
local array<ATrader> availableTraders;
availableTraders = _.kf.trading.GetTraders();
for (i = 0; i < availableTraders.length; i += 1)
{
nextTrader = KF1_Trader(availableTraders[i]);
if (nextTrader == none) continue;
if (!nextTrader.IsEnabled()) continue;
nextShopVolume = ShopVolume(nextTrader.myShopVolume.Get());
if (nextShopVolume == none) continue;
shopVolumes[shopVolumes.length] = nextShopVolume;
}
_.unreal.GetKFGameType().shopList = shopVolumes;
}
public function bool IsAutoOpen()
{
local ShopVolume vanillaShopVolume;
vanillaShopVolume = ShopVolume(myShopVolume.Get());
if (vanillaShopVolume != none) {
return vanillaShopVolume.bAlwaysEnabled;
}
return false;
}
public function ATrader SetAutoOpen(bool doAutoOpen)
{
local ShopVolume vanillaShopVolume;
vanillaShopVolume = ShopVolume(myShopVolume.Get());
if (vanillaShopVolume == none) {
return self;
}
if (doAutoOpen) {
vanillaShopVolume.bAlwaysEnabled = true;
}
else {
vanillaShopVolume.bAlwaysEnabled = false;
}
return self;
}
public function bool IsOpen()
{
local ShopVolume vanillaShopVolume;
vanillaShopVolume = ShopVolume(myShopVolume.Get());
if (vanillaShopVolume != none) {
return vanillaShopVolume.bCurrentlyOpen;
}
return false;
}
public function ATrader SetOpen(bool doOpen)
{
local ShopVolume vanillaShopVolume;
if (doOpen && !IsEnabled()) return self;
vanillaShopVolume = ShopVolume(myShopVolume.Get());
if (vanillaShopVolume == none) return self;
if (doOpen) {
vanillaShopVolume.OpenShop();
}
else {
vanillaShopVolume.CloseShop();
}
return self;
}
public function bool IsSelected()
{
local ShopVolume vanillaShopVolume;
local KFGameReplicationInfo kfGameRI;
vanillaShopVolume = ShopVolume(myShopVolume.Get());
if (vanillaShopVolume == none) {
return false;
}
kfGameRI = _.unreal.GetKFGameRI();
if (kfGameRI != none) {
return (kfGameRI.currentShop == vanillaShopVolume);
}
return false;
}
public function ATrader Select()
{
local ShopVolume vanillaShopVolume;
local KFGameReplicationInfo kfGameRI;
vanillaShopVolume = ShopVolume(myShopVolume.Get());
if (vanillaShopVolume == none) {
return self;
}
kfGameRI = _.unreal.GetKFGameRI();
if (kfGameRI != none) {
kfGameRI.currentShop = vanillaShopVolume;
}
return self;
}
public function ATrader BootPlayers()
{
local ShopVolume vanillaShopVolume;
vanillaShopVolume = ShopVolume(myShopVolume.Get());
if (vanillaShopVolume != none) {
vanillaShopVolume.BootPlayers();
}
return self;
}
defaultproperties
{
}

213
sources/Gameplay/KF1Frontend/Trading/KF1_TradingComponent.uc

@ -0,0 +1,213 @@
/**
* `ATradingComponent`'s implementation for `KF1_Frontend`.
* Only supports `KF1_Trader` as a possible trader class.
* Copyright 2021 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* Acedia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class KF1_TradingComponent extends ATradingComponent;
// Variables for enforcing a trader time pause by repeatedly setting
// `waveCountDown`'s value to `pausedCountDownValue`
var protected bool tradingCountDownPaused;
var protected int pausedCountDownValue;
// For detecting events of trading becoming active/inactive and selecting
// a different trader, to account for these changing through non-Acedia means
var protected bool wasActiveLastCheck;
var protected Atrader lastSelectedTrader;
// All known traders on map
var protected array<ATrader> registeredTraders;
protected function Constructor()
{
super.Constructor();
_.unreal.OnTick(self).connect = Tick;
registeredTraders = class'KF1_Trader'.static.WrapVanillaShops();
lastSelectedTrader = GetSelectedTrader();
wasActiveLastCheck = IsTradingActive();
}
protected function Finalizer()
{
super.Finalizer();
_.unreal.OnTick(self).Disconnect();
_.memory.FreeMany(registeredTraders);
registeredTraders.length = 0;
}
public function array<ATrader> GetTraders()
{
return registeredTraders;
}
public function bool IsTradingActive()
{
local KFGameType kfGame;
kfGame = _.unreal.GetKFGameType();
return kfGame.IsInState('MatchInProgress') && kfGame.bTradingDoorsOpen;
}
public function SetTradingStatus(bool makeActive)
{
local bool isCurrentlyActive;
local KFGameType kfGame;
local KFGameReplicationInfo kfGameRI;
local KFMonster nextZed;
isCurrentlyActive = IsTradingActive();
if (isCurrentlyActive == makeActive) {
return;
}
if (!makeActive && isCurrentlyActive)
{
SetCountDown(0);
return;
}
kfGame = _.unreal.GetKFGameType();
kfGameRI = _.unreal.GetKFGameRI();
foreach kfGame.DynamicActors(class'KFMonster', nextZed)
{
if (nextZed == none) continue;
if (nextZed.health <= 0) continue;
nextZed.Suicide();
}
kfGame.totalMaxMonsters = 0;
kfGameRI.maxMonsters = 0;
}
public function ATrader GetSelectedTrader()
{
local int i;
for (i = 0; i < registeredTraders.length; i += 1)
{
if (registeredTraders[i].IsSelected()) {
return registeredTraders[i];
}
}
return none;
}
public function SelectTrader(ATrader newSelection)
{
local ATrader oldSelection;
local KFGameReplicationInfo kfGameRI;
if (newSelection != none) {
newSelection.Select();
}
else
{
kfGameRI = _.unreal.GetKFGameRI();
if (kfGameRI != none) {
kfGameRI.currentShop = none;
}
}
// Emit signal, but first record new trader inside `lastSelectedTrader`
// in case someone decides it would be a grand idea to call `SelectTrader`
// during `onTraderSelectSignal` signal.
oldSelection = lastSelectedTrader;
lastSelectedTrader = newSelection;
if (lastSelectedTrader != newSelection) {
onTraderSelectSignal.Emit(oldSelection, newSelection);
}
}
public function int GetTradingInterval()
{
return _.unreal.GetKFGameType().timeBetweenWaves;
}
public function SetTradingInterval(int newTradingInterval)
{
if (newTradingInterval > 0) {
_.unreal.GetKFGameType().timeBetweenWaves = Max(newTradingInterval, 1);
}
}
public function int GetCountDown()
{
if (!IsTradingActive()) {
return 0;
}
return _.unreal.GetKFGameType().waveCountDown;
}
public function SetCountDown(int newCountDownValue)
{
local KFGameType kfGame;
if (!IsTradingActive()) {
return;
}
kfGame = _.unreal.GetKFGameType();
if (kfGame.waveCountDown >= 5 && newCountDownValue < 5) {
_.unreal.GetKFGameRI().waveNumber = kfGame.waveNum;
}
kfGame.waveCountDown = Max(newCountDownValue, 1);
pausedCountDownValue = newCountDownValue;
}
public function bool IsCountDownPaused()
{
if (!IsTradingActive()) {
return false;
}
return tradingCountDownPaused;
}
public function SetCountDownPause(bool doPause)
{
tradingCountDownPaused = doPause;
if (doPause) {
pausedCountDownValue = _.unreal.GetKFGameType().waveCountDown;
}
}
protected function Tick(float delta, float timeScaleCoefficient)
{
local bool isActiveNow;
local ATrader newSelectedTrader;
// Enforce pause
if (tradingCountDownPaused) {
_.unreal.GetKFGameType().waveCountDown = pausedCountDownValue;
}
// Selected trader check
newSelectedTrader = GetSelectedTrader();
if (lastSelectedTrader != newSelectedTrader)
{
onTraderSelectSignal.Emit(lastSelectedTrader, newSelectedTrader);
lastSelectedTrader = newSelectedTrader;
}
// Active status check
isActiveNow = IsTradingActive();
if (wasActiveLastCheck != isActiveNow)
{
wasActiveLastCheck = isActiveNow;
if (isActiveNow)
{
onStartSignal.Emit();
}
else
{
onEndSignal.Emit();
// Reset pause after trading time has ended
tradingCountDownPaused = false;
}
}
}
defaultproperties
{
}

3
sources/Global.uc

@ -40,6 +40,8 @@ var public UserAPI users;
var public PlayersAPI players; var public PlayersAPI players;
var public JSONAPI json; var public JSONAPI json;
var public KFFrontend kf;
public final static function Global GetInstance() public final static function Global GetInstance()
{ {
if (default.myself == none) { if (default.myself == none) {
@ -70,5 +72,6 @@ protected function Initialize()
users = UserAPI(memory.Allocate(class'UserAPI')); users = UserAPI(memory.Allocate(class'UserAPI'));
players = PlayersAPI(memory.Allocate(class'PlayersAPI')); players = PlayersAPI(memory.Allocate(class'PlayersAPI'));
json = JSONAPI(memory.Allocate(class'JSONAPI')); json = JSONAPI(memory.Allocate(class'JSONAPI'));
kf = KFFrontend(memory.Allocate(class'KF1_Frontend'));
json.StaticConstructor(); json.StaticConstructor();
} }
Loading…
Cancel
Save