Browse Source
`APlayer` and `ATrader` represented player and trader (`ShopVolume`) with a single object instance. Such design, if used for all actors, could have led to mutitute of problems rooted in need to find that single object for any given native actor: we'd need to store object-actor pairs separately and look through pairs lists, which is hardly a sane design. Now Acedia switches to a different design, where a single in-game entity (i.e. actor) can have several interfaces referring to it. All equaly valid. Refactoring `APlayer` and `ATrader` into `EPlayer` and `ETrader` is a first step in that direction.pull/8/head
Anton Tarasenko
3 years ago
32 changed files with 1226 additions and 1082 deletions
@ -1,393 +0,0 @@
|
||||
/** |
||||
* This object describes a call attempt for one of the `Command`s. |
||||
* `Command`s are meant to be be executed from user's console input, |
||||
* so this object should only be created while parsing their input. Any other |
||||
* use of this object is not guaranteed to be supported. |
||||
* 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 CommandCall extends AcediaObject |
||||
dependson(Command); |
||||
|
||||
// Once this value is set to `true`, the command call is considered fully |
||||
// described and will prevent any changes to it's internal state |
||||
// (except deallocation). |
||||
var private bool locked; |
||||
// Player who initiated the call and targeted players (if applicable) |
||||
var private APlayer callerPlayer; |
||||
var private array<APlayer> targetPlayers; |
||||
// Specified sub-command and parameters/options |
||||
var private Text subCommandName; |
||||
var private AssociativeArray commandParameters, commandOptions; |
||||
|
||||
// Errors that occurred during command call processing are described by |
||||
// error type and optional error textual name of the object |
||||
// (parameter, option, etc.) that caused it. |
||||
var private Command.ErrorType parsingError; |
||||
var private Text errorCause; |
||||
|
||||
var public const int TBAD_PARSER, TNOSUB_COMMAND, TNO_REQ_PARAM; |
||||
var public const int TNO_REQ_PARAM_FOR_OPTION, TUNKNOW_NOPTION; |
||||
var public const int TUNKNOWN_SHORT_OPTION, TREPEATED_OPTION, TUNUSED_INPUT; |
||||
var public const int TMULTIPLE_OPTIONS_WITH_PARAMS; |
||||
var public const int TINCORRECT_TARGET, TEMPTY_TARGETS; |
||||
|
||||
protected function Constructor() |
||||
{ |
||||
// We simply take ownership and record into `commandParameters` whatever |
||||
// `AssociativeArray` was passed to us, but fill (and therefore create) |
||||
// `commandOptions` ourselves. |
||||
commandOptions = _.collections.EmptyAssociativeArray(); |
||||
} |
||||
|
||||
protected function Finalizer() |
||||
{ |
||||
_.memory.Free(commandParameters); |
||||
_.memory.Free(commandOptions); |
||||
_.memory.Free(subCommandName); |
||||
_.memory.Free(errorCause); |
||||
commandParameters = none; |
||||
commandOptions = none; |
||||
subCommandName = none; |
||||
errorCause = none; |
||||
parsingError = CET_None; |
||||
targetPlayers.length = 0; |
||||
locked = false; |
||||
} |
||||
|
||||
/** |
||||
* Method for producing erroneous `CommandCall` for the needs of |
||||
* error reporting. |
||||
* |
||||
* @param type Type of error resulting `CommandCall` must have. |
||||
* @param callerPlayer Player that caused erroneous command call. |
||||
* @return `CommandCall` with specified error type and `APlayer`. |
||||
*/ |
||||
public final static function CommandCall MakeError( |
||||
Command.ErrorType type, |
||||
APlayer callerPlayer) |
||||
{ |
||||
return CommandCall(__().memory.Allocate(class'CommandCall')) |
||||
.DeclareError(type) |
||||
.SetCallerPlayer(callerPlayer); |
||||
} |
||||
|
||||
/** |
||||
* Put caller `CommandCall` into erroneous state. |
||||
* Does nothing after `CommandCall` was locked for change (see `Finish()`). |
||||
* |
||||
* @param type Type of error caller `CommandCall` must have. |
||||
* Once error (not `CET_None`) was set - calling this method with |
||||
* `CET_None` to erase error will not work. |
||||
* @param cause Textual description of offender command part to supplement |
||||
* error report (will be used when reporting error to the caller). |
||||
* @return Caller `CommandCall` to allow for method chaining. |
||||
*/ |
||||
public final function CommandCall DeclareError( |
||||
Command.ErrorType type, |
||||
optional Text cause) |
||||
{ |
||||
if (locked) return self; |
||||
if (parsingError != CET_None) return self; |
||||
|
||||
parsingError = type; |
||||
_.memory.Free(errorCause); |
||||
errorCause = none; |
||||
if (cause != none) { |
||||
errorCause = cause.Copy(); |
||||
} |
||||
return self; |
||||
} |
||||
|
||||
/** |
||||
* Checks if caller `CommandCall` is in erroneous state. |
||||
* |
||||
* @return `true` if `CommandCall` has not recorded any errors so far and |
||||
* `false` otherwise. |
||||
*/ |
||||
public final function bool IsSuccessful() |
||||
{ |
||||
return parsingError == CET_None; |
||||
} |
||||
|
||||
/** |
||||
* Returns current error type (including `CET_None` if there were no errors). |
||||
* |
||||
* @return Error type for caller `CommandCall` error. |
||||
*/ |
||||
public final function Command.ErrorType GetError() |
||||
{ |
||||
return parsingError; |
||||
} |
||||
|
||||
/** |
||||
* In case there were any errors - returns textual description of offender |
||||
* command part. Mostly used for reporting errors to players. |
||||
* |
||||
* @return Textual description of command part that caused an error. |
||||
*/ |
||||
public final function Text GetErrorCause() |
||||
{ |
||||
if (errorCause != none) { |
||||
return errorCause.Copy(); |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
/** |
||||
* After this method is called - changes to the `CommandCall` will be |
||||
* prevented. |
||||
* |
||||
* @return Caller `CommandCall` to allow for method chaining. |
||||
*/ |
||||
public final function CommandCall Finish() |
||||
{ |
||||
locked = true; |
||||
return self; |
||||
} |
||||
|
||||
/** |
||||
* Returns player, that initiated command, that produced caller `CommandCall`. |
||||
* |
||||
* @return Player, that initiated command, that produced caller `CommandCall` |
||||
*/ |
||||
public final function APlayer GetCallerPlayer() |
||||
{ |
||||
return callerPlayer; |
||||
} |
||||
|
||||
/** |
||||
* Sets player, that initiated command, that produced caller `CommandCall`. |
||||
* Does nothing after `CommandCall` was locked for change (see `Finish()`). |
||||
* |
||||
* @return Caller `CommandCall` to allow for method chaining. |
||||
*/ |
||||
public final function CommandCall SetCallerPlayer(APlayer player) |
||||
{ |
||||
callerPlayer = player; |
||||
return self; |
||||
} |
||||
|
||||
/** |
||||
* Returns players that were targeted by command that produced caller |
||||
* `CommandCall`. |
||||
* |
||||
* @return Players, targeted by caller `CommandCall`. |
||||
*/ |
||||
public final function array<APlayer> GetTargetPlayers() |
||||
{ |
||||
return targetPlayers; |
||||
} |
||||
|
||||
/** |
||||
* Sets players, targeted by command that produced caller `CommandCall`. |
||||
* Does nothing after `CommandCall` was locked for change (see `Finish()`). |
||||
* |
||||
* @return Caller `CommandCall` to allow for method chaining. |
||||
*/ |
||||
public final function CommandCall SetTargetPlayers(array<APlayer> newTargets) |
||||
{ |
||||
if (!locked) { |
||||
targetPlayers = newTargets; |
||||
} |
||||
return self; |
||||
} |
||||
|
||||
/** |
||||
* Returns picked sub-command of command that produced caller `CommandCall`. |
||||
* |
||||
* @return Sub-command of command that produced caller `CommandCall`. |
||||
* Returns stored value that will be deallocated along with |
||||
* caller `CommandCall` - do not deallocate returned `Text` manually. |
||||
*/ |
||||
public final function Text GetSubCommand() |
||||
{ |
||||
return subCommandName; |
||||
} |
||||
|
||||
/** |
||||
* Sets sub-command of command that produced caller `CommandCall`. |
||||
* Does nothing after `CommandCall` was locked for change (see `Finish()`). |
||||
* |
||||
* @param newSubCommandName New sub command name. |
||||
* Copy of passed object will be stored. |
||||
* @return Caller `CommandCall` to allow for method chaining. |
||||
*/ |
||||
public final function CommandCall SetSubCommand(Text newSubCommandName) |
||||
{ |
||||
if (!locked) |
||||
{ |
||||
_.memory.Free(subCommandName); |
||||
subCommandName = newSubCommandName.Copy(); |
||||
} |
||||
return self; |
||||
} |
||||
|
||||
/** |
||||
* Returns parameters of command that produced caller `CommandCall`. |
||||
* |
||||
* @return Parameters of command that produced caller `CommandCall`. |
||||
* Returns stored value that will be deallocated along with |
||||
* caller `CommandCall` - do not deallocate returned `AssociativeArray` |
||||
* manually. |
||||
*/ |
||||
public final function AssociativeArray GetParameters() |
||||
{ |
||||
return commandParameters; |
||||
} |
||||
|
||||
/** |
||||
* Sets parameters of command that produced caller `CommandCall`. |
||||
* Does nothing after `CommandCall` was locked for change (see `Finish()`). |
||||
* |
||||
* @param parameters New set of parameters. Passed value will be managed by |
||||
* caller `CommandCall` and should not be deallocated manually after |
||||
* calling `SetParameters()`. |
||||
* @return Caller `CommandCall` to allow for method chaining. |
||||
*/ |
||||
public final function CommandCall SetParameters(AssociativeArray parameters) |
||||
{ |
||||
if (!locked) |
||||
{ |
||||
_.memory.Free(commandParameters); |
||||
commandParameters = parameters; |
||||
} |
||||
return self; |
||||
} |
||||
|
||||
/** |
||||
* Returns options of command that produced caller `CommandCall`. |
||||
* |
||||
* If option without parameters was specified - it will be recorded as |
||||
* a key with value `none`. |
||||
* If option has parameters - `AssociativeArray` with them will be |
||||
* recorded as value instead. |
||||
* |
||||
* @return Options of command that produced caller `CommandCall`. |
||||
* Returns stored value that will be deallocated along with |
||||
* caller `CommandCall` - do not deallocate returned `AssociativeArray` |
||||
* manually. |
||||
*/ |
||||
public final function AssociativeArray GetOptions() |
||||
{ |
||||
return commandOptions; |
||||
} |
||||
|
||||
/** |
||||
* Sets option parameters of command that produced caller `CommandCall`. |
||||
* Does nothing after `CommandCall` was locked for change (see `Finish()`). |
||||
* |
||||
* For recording options without parameters simply pass `none` instead of them. |
||||
* |
||||
* @param option Option to record (along with it's possible parameters). |
||||
* @param parameters Option parameters. Passed value will be managed by |
||||
* caller `CommandCall` and should not be deallocated manually after |
||||
* calling `SetParameters()`. |
||||
* Pass `none` if option has no parameters. |
||||
* @return Caller `CommandCall` to allow for method chaining. |
||||
*/ |
||||
public final function CommandCall SetOptionParameters( |
||||
Command.Option option, |
||||
optional AssociativeArray parameters) |
||||
{ |
||||
if (locked) return self; |
||||
if (commandOptions == none) return self; |
||||
|
||||
commandOptions.SetItem(option.longName, parameters, true); |
||||
return self; |
||||
} |
||||
|
||||
/** |
||||
* Prints error message as a human-readable message that can be reported to |
||||
* the caller player. |
||||
* |
||||
* In case there was no error - empty text is returned. |
||||
* |
||||
* @return Error message in a human-readable form. |
||||
*/ |
||||
public final function Text PrintErrorMessage() |
||||
{ |
||||
local Text result; |
||||
local MutableText builder; |
||||
builder = _.text.Empty(); |
||||
switch (parsingError) |
||||
{ |
||||
case CET_BadParser: |
||||
builder.Append(T(TBAD_PARSER)); |
||||
break; |
||||
case CET_NoSubCommands: |
||||
builder.Append(T(TNOSUB_COMMAND)); |
||||
break; |
||||
case CET_NoRequiredParam: |
||||
builder.Append(T(TNO_REQ_PARAM)).Append(errorCause); |
||||
break; |
||||
case CET_NoRequiredParamForOption: |
||||
builder.Append(T(TNO_REQ_PARAM_FOR_OPTION)).Append(errorCause); |
||||
break; |
||||
case CET_UnknownOption: |
||||
builder.Append(T(TUNKNOW_NOPTION)).Append(errorCause); |
||||
break; |
||||
case CET_UnknownShortOption: |
||||
builder.Append(T(TUNKNOWN_SHORT_OPTION)); |
||||
break; |
||||
case CET_RepeatedOption: |
||||
builder.Append(T(TREPEATED_OPTION)).Append(errorCause); |
||||
break; |
||||
case CET_UnusedCommandParameters: |
||||
builder.Append(T(TUNUSED_INPUT)).Append(errorCause); |
||||
break; |
||||
case CET_MultipleOptionsWithParams: |
||||
builder.Append(T(TMULTIPLE_OPTIONS_WITH_PARAMS)).Append(errorCause); |
||||
break; |
||||
case CET_IncorrectTargetList: |
||||
builder.Append(T(TINCORRECT_TARGET)).Append(errorCause); |
||||
break; |
||||
case CET_EmptyTargetList: |
||||
builder.Append(T(TEMPTY_TARGETS)).Append(errorCause); |
||||
break; |
||||
default: |
||||
} |
||||
result = builder.Copy(); |
||||
builder.FreeSelf(); |
||||
return result; |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
TBAD_PARSER = 0 |
||||
stringConstants(0) = "Internal error occurred: invalid parser." |
||||
TNOSUB_COMMAND = 1 |
||||
stringConstants(1) = "Ill defined command: no subcommands" |
||||
TNO_REQ_PARAM = 2 |
||||
stringConstants(2) = "Missing required parameter: " |
||||
TNO_REQ_PARAM_FOR_OPTION = 3 |
||||
stringConstants(3) = "Missing required parameter for option: " |
||||
TUNKNOW_NOPTION = 4 |
||||
stringConstants(4) = "Invalid option specified: " |
||||
TUNKNOWN_SHORT_OPTION = 5 |
||||
stringConstants(5) = "Invalid short option specified." |
||||
TREPEATED_OPTION = 6 |
||||
stringConstants(6) = "Option specified several times: " |
||||
TUNUSED_INPUT = 7 |
||||
stringConstants(7) = "Part of command could not be parsed: " |
||||
TMULTIPLE_OPTIONS_WITH_PARAMS = 8 |
||||
stringConstants(8) = "Multiple short options in one declarations require parameters:" |
||||
TINCORRECT_TARGET = 9 |
||||
stringConstants(9) = "Target players are incorrectly specified." |
||||
TEMPTY_TARGETS = 10 |
||||
stringConstants(10) = "List of target players is empty." |
||||
} |
@ -0,0 +1,104 @@
|
||||
/** |
||||
* Base class for all entity interfaces. Entity interface is a reference to |
||||
* an entity inside the game world that provides a specific API for |
||||
* that entity. |
||||
* A single entity is not bound to a single `EInterface` class, |
||||
* e.g. a weapon can provide `EWeapon` and `ESellable` interfaces. |
||||
* An entity can also have several `EInterface` instances reference it at |
||||
* once (including those of the same type). Deallocating one such reference |
||||
* should not affect referred entity in any way and should be treated as simply |
||||
* getting rid of one of the references. |
||||
* 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 EInterface extends AcediaObject |
||||
abstract; |
||||
|
||||
/** |
||||
* Makes a copy of the caller interface, producing a new `EInterface` of |
||||
* the exactly the same class (`EWeapon` will produce another `EWeapon`). |
||||
* |
||||
* This should never fail. Even if referred entity is already gone. |
||||
* |
||||
* @return Copy of the caller `EInterface`, of the exactly the same class. |
||||
* Guaranteed to not be `none`. |
||||
*/ |
||||
public function EInterface Copy() |
||||
{ |
||||
return none; |
||||
} |
||||
|
||||
/** |
||||
* Provides `EInterface` reference of given class `newInterfaceClass` to |
||||
* the entity, referred to by the caller `EInterface` (if supported). |
||||
* |
||||
* Can be used to access entity's other API. |
||||
* |
||||
* @param newInterfaceClass Class of the new `EInterface` for the entity, |
||||
* referred to by the caller `EInterface`. |
||||
* @return `EInterface` of the given class `newInterfaceClass` that refers to |
||||
* the caller `EInterface`'s entity. |
||||
* Can only be `none` if either caller `EInterface`'s entity does not |
||||
* support `EInterface` of the specified class or caller `EInterface` |
||||
* no longer exists (`self.IsExistent() == false`). |
||||
*/ |
||||
public function EInterface As(class<EInterface> newInterfaceClass) |
||||
{ |
||||
return none; |
||||
} |
||||
|
||||
/** |
||||
* Checks whether caller `EInterface` refers to the entity that still exists |
||||
* in the game world. |
||||
* |
||||
* Once destroyed, same entity will not come into existence again (but can be |
||||
* replaced by its exact copy), so once `EInterface`'s `IsExistent()` call |
||||
* returns `false` it will never return `true` again. |
||||
* |
||||
* `EInterface`'s entity being gone is not the same as that `EInterface` being |
||||
* deallocated - deallocation of such `EInterface` still has to be manually |
||||
* done. |
||||
* |
||||
* @return `true` if caller `EInterface` refers to the entity that exists in |
||||
* the game world and `false` otherwise. |
||||
*/ |
||||
public function bool IsExistent() |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Checks whether caller interface refers to the same entity as |
||||
* the `other` argument. |
||||
* |
||||
* If two `EInterface`s referred to the same entity |
||||
* (`SameAs()` returned `true`), but that entity got destroyed, |
||||
* these `EInterface`s will not longer be considered "same" |
||||
* (`SameAs()` will return false). |
||||
* |
||||
* @param other `EInterface` to check for referring to the same entity. |
||||
* @return `true` if `other` refers to the same entity as the caller |
||||
* `EInterface` and `false` otherwise. |
||||
*/ |
||||
public function bool SameAs(EInterface other) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -0,0 +1,38 @@
|
||||
/** |
||||
* Signal class implementation for `PlayerAPI`'s `OnLostPlayer` signal. |
||||
* 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 PlayerAPI_OnLostPlayer_Signal extends Signal; |
||||
|
||||
public final function Emit(User identity) |
||||
{ |
||||
local Slot nextSlot; |
||||
StartIterating(); |
||||
nextSlot = GetNextSlot(); |
||||
while (nextSlot != none) |
||||
{ |
||||
PlayerAPI_OnLostPlayer_Slot(nextSlot).connect(identity); |
||||
nextSlot = GetNextSlot(); |
||||
} |
||||
CleanEmptySlots(); |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
relatedSlotClass = class'PlayerAPI_OnLostPlayer_Slot' |
||||
} |
@ -0,0 +1,38 @@
|
||||
/** |
||||
* Signal class implementation for `PlayerAPI`'s `OnNewPlayer` signal. |
||||
* 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 PlayerAPI_OnNewPlayer_Signal extends Signal; |
||||
|
||||
public final function Emit(EPlayer newPlayer) |
||||
{ |
||||
local Slot nextSlot; |
||||
StartIterating(); |
||||
nextSlot = GetNextSlot(); |
||||
while (nextSlot != none) |
||||
{ |
||||
PlayerAPI_OnNewPlayer_Slot(nextSlot).connect(EPlayer(newPlayer.Copy())); |
||||
nextSlot = GetNextSlot(); |
||||
} |
||||
CleanEmptySlots(); |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
relatedSlotClass = class'PlayerAPI_OnNewPlayer_Slot' |
||||
} |
@ -0,0 +1,40 @@
|
||||
/** |
||||
* Slot class implementation for `PlayerAPI`'s `OnNewPlayer` signal. |
||||
* 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 PlayerAPI_OnNewPlayer_Slot extends Slot; |
||||
|
||||
delegate connect(EPlayer newPlayer) |
||||
{ |
||||
DummyCall(); |
||||
} |
||||
|
||||
protected function Constructor() |
||||
{ |
||||
connect = none; |
||||
} |
||||
|
||||
protected function Finalizer() |
||||
{ |
||||
super.Finalizer(); |
||||
connect = none; |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
@ -1,183 +0,0 @@
|
||||
/** |
||||
* Service for tracking currently connected players and remembering what |
||||
* `APlayer` is connected to what `PlayerController` (`PlayerController` |
||||
* instance is an `Actor` and therefore should not be stores as `APlayer`'s |
||||
* variable, since `APlayer` is not an `Actor`). |
||||
* 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 PlayerService extends Service; |
||||
|
||||
// Used to 1-to-1 associate `APlayer` objects with `PlayerController` actors. |
||||
struct PlayerControllerPair |
||||
{ |
||||
var APlayer player; |
||||
var PlayerController controller; |
||||
}; |
||||
// Records of all known pairs |
||||
var private array<PlayerControllerPair> allPlayers; |
||||
|
||||
protected function Contructor() |
||||
{ |
||||
SetTimer(1.0, true); |
||||
} |
||||
|
||||
protected function Finalizer() |
||||
{ |
||||
SetTimer(0.0, false); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new `APlayer` instance for a given `newPlayerController` |
||||
* controller. |
||||
* |
||||
* If given controller is `none` or it's `APlayer` was already created, |
||||
* - does nothing. |
||||
* |
||||
* @param newPlayerController Controller for which we must |
||||
* create new `APlayer`. |
||||
* @return `true` if new `APlayer` was created and `false` otherwise. |
||||
*/ |
||||
public final function bool RegisterPair( |
||||
PlayerController newController, |
||||
APlayer newPlayer) |
||||
{ |
||||
local int i; |
||||
local PlayerControllerPair newPair; |
||||
if (newController == none) return false; |
||||
if (newPlayer == none) return false; |
||||
|
||||
for (i = 0; i < allPlayers.length; i += 1) |
||||
{ |
||||
if (allPlayers[i].controller == newController) { |
||||
return false; |
||||
} |
||||
if (allPlayers[i].player == newPlayer) { |
||||
return false; |
||||
} |
||||
} |
||||
// Record new pair in service's data |
||||
newPair.controller = newController; |
||||
newPair.player = newPlayer; |
||||
allPlayers[allPlayers.length] = newPair; |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Fetches current array of all players (registered `APlayer`s). |
||||
* |
||||
* @return Current array of all players (registered `APlayer`s). Guaranteed to |
||||
* not contain `none` values. |
||||
*/ |
||||
public final function array<APlayer> GetAllPlayers() |
||||
{ |
||||
local int i; |
||||
local array<APlayer> result; |
||||
for (i = 0; i < allPlayers.length; i += 1) |
||||
{ |
||||
if (allPlayers[i].controller != none) { |
||||
result[result.length] = allPlayers[i].player; |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns `APlayer` associated with a given `PlayerController`. |
||||
* |
||||
* @param controller Controller for which we want to find associated player. |
||||
* @return `APlayer` that is associated with a given `PlayerController`. |
||||
* Can return `none` if player has already "expired". |
||||
*/ |
||||
public final function APlayer GetPlayer(Controller controller) |
||||
{ |
||||
local int i; |
||||
if (controller == none) { |
||||
return none; |
||||
} |
||||
for (i = 0; i < allPlayers.length; i += 1) |
||||
{ |
||||
if (controller == allPlayers[i].controller) { |
||||
return allPlayers[i].player; |
||||
} |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
/** |
||||
* Returns `PlayerController` associated with a given `APlayer`. |
||||
* |
||||
* @param player Player for which we want to find associated `Controller`. |
||||
* @return Controller that is associated with a given player. |
||||
* Can return `none` if controller has already "expired". |
||||
*/ |
||||
public final function PlayerController GetController(APlayer player) |
||||
{ |
||||
local int i; |
||||
if (player == none) { |
||||
return none; |
||||
} |
||||
for (i = 0; i < allPlayers.length; i += 1) |
||||
{ |
||||
if (player == allPlayers[i].player) { |
||||
return allPlayers[i].controller; |
||||
} |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
/** |
||||
* Returns `Pawn` associated with a given `APlayer`. |
||||
* |
||||
* @param player Player for which we want to find associated `Pawn`. |
||||
* @return `Pawn` that is associated with a given player. |
||||
* Can return `none` if controller has already "expired". |
||||
*/ |
||||
public final function Pawn GetPawn(APlayer player) |
||||
{ |
||||
local Controller controller; |
||||
controller = GetController(player); |
||||
if (controller != none) { |
||||
return controller.pawn; |
||||
} |
||||
return none; |
||||
} |
||||
|
||||
/** |
||||
* IMPORTANT: this is a helper function that is not supposed to be |
||||
* called manually. |
||||
* |
||||
* Causes status of all players to update. |
||||
* See `APlayer.Update()` for details. |
||||
*/ |
||||
event Timer() |
||||
{ |
||||
local int i; |
||||
while (i < allPlayers.length) |
||||
{ |
||||
if (allPlayers[i].controller == none || allPlayers[i].player == none) { |
||||
allPlayers.Remove(i, 1); |
||||
} |
||||
else { |
||||
i += 1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
Loading…
Reference in new issue