Add missing docblocks

This commit is contained in:
Austen McDonald
2016-07-31 21:54:54 +00:00
parent d30bab4ec8
commit 7ae281992f
30 changed files with 650 additions and 347 deletions
+35 -28
View File
@@ -9,7 +9,8 @@ use Symfony\Component\Yaml\Yaml;
use LotGD\Core\ComposerManager;
/**
* BootConfiguration
* Represents the configuration of a LotGD package (core, crate or module),
* with its configuration parameters.
* @author sauterb
*/
class BootConfiguration
@@ -25,15 +26,21 @@ class BootConfiguration
private $models;
private $daenerysCommands;
private $cwd;
/**
* Construct a configuration.
* @param ComposerManager $composerManager
* @param PackageInterface $package
* @param string $cwd
*/
public function __construct(ComposerManager $composerManager, PackageInterface $package, string $cwd)
{
$this->composerManager = $composerManager;
$this->package = $package;
$this->cwd = $cwd;
$installationManager = $composerManager->getComposer()->getInstallationManager();
// only lotgd-modules are installed in the vendor directory
if ($package->getType() === "lotgd-module") {
$confFile = $installationManager->getInstallPath($package) . DIRECTORY_SEPARATOR . "lotgd.yml";
@@ -41,7 +48,7 @@ class BootConfiguration
else {
$confFile = $cwd . DIRECTORY_SEPARATOR . "lotgd.yml";
}
$this->rootNamespace = $this->findRootNamespace($package);
if (file_exists($confFile)) {
$this->rawConfig = Yaml::parse(file_get_contents($confFile));
@@ -51,14 +58,14 @@ class BootConfiguration
$type = $package->getType();
throw new \Exception("Package {$name} of type {$type} does not have a lotgd.yml in it's root ($confFile).");
}
$this->findEntityDirectory();
$this->findDaenerysCommands();
}
/**
* Searches for a root namespace
*
*
* This function searches the package's configuration to find it's root namespace.
* For this, it uses the following order:
* - check psr-4 autoload configuration. If used, it takes the first element
@@ -73,15 +80,15 @@ class BootConfiguration
if (isset($autoload["psr-4"]) && count($autoload["psr-4"]) > 0) {
return key($autoload["psr-4"]);
}
if (isset($autoload["psr-0"]) && count($autoload["psr-0"]) > 0) {
return key($autoload["psr-0"]);
}
$name = $package->getName();
throw new \Exception("{$name} has no valid namespace.");
}
/**
* Returns a subkey if it exists or null.
* @param array $arguments
@@ -90,7 +97,7 @@ class BootConfiguration
protected function getSubKeyIfItExists(array $arguments)
{
$parent = $this->rawConfig;
foreach ($arguments as $argument){
if (isset($parent[$argument])) {
$parent = $parent[$argument];
@@ -99,10 +106,10 @@ class BootConfiguration
return null;
}
}
return $parent;
}
/**
* Tries to iterate an array element given by the arguments
* @param scalar $argument1,... array keys, by increasing depth
@@ -110,14 +117,14 @@ class BootConfiguration
protected function iterateKey(...$arguments)
{
$result = $this->getSubKeyIfItExists($arguments);
if (is_array($result)) {
foreach ($result as $key => $val) {
yield $key => $val;
}
}
}
/**
* Returns a subkey of an array if it exists or null
* @param scalar $argument1,... array keys, by increasing depth
@@ -128,28 +135,28 @@ class BootConfiguration
$result = $this->getSubKeyIfItExists($arguments);
return $result;
}
/**
* internal function. Adds models to the boot configuration.
*/
protected function findEntityDirectory()
{
$this->entityDirectory = null;
$entityNamespace = $this->getConfig("bootstrap", "entityNamespace");
$entityNamespace = $this->rootNamespace . $entityNamespace;
if (is_null($entityNamespace) === false) {
$entityDirectory = $this->composerManager->translateNamespaceToPath($entityNamespace, $this->cwd);
if (is_dir($entityDirectory) === false) {
throw new \Exception("{$entityDirectory}, generated from {$entityNamespace}, is not a valid directory.");
}
$this->entityDirectory = $entityDirectory;
}
}
/**
* Returns true if there are any models to add.
* @return type
@@ -158,7 +165,7 @@ class BootConfiguration
{
return $this->entityDirectory === null ? false : true;
}
/**
* Returns a list of fqcn for all models added by packages.
* @return array<string>
@@ -167,7 +174,7 @@ class BootConfiguration
{
return $this->entityDirectory;
}
/**
* Searches the config file for daenerys commands and, if found, adds the class name to a list
* @return type
@@ -176,12 +183,12 @@ class BootConfiguration
{
$list = $this->iterateKey("bootstrap", "daenerysCommands");
$this->daenerysCommands = [];
foreach ($list as $command) {
$this->daenerysCommands = $this->rootNamespace . $command;
}
}
/**
* Returns true if this configuration has daenerys commands
* @return bool
@@ -189,8 +196,8 @@ class BootConfiguration
public function hasDaenerysCommands(): bool
{
return count($this->daenerysCommands) > 0 ? true : false;
}
}
/**
* Returns a list of daenerys commands
*/
+18 -10
View File
@@ -6,27 +6,35 @@ use Symfony\Component\Console\Application;
use LotGD\Core\ComposerManager;
/**
* Handle the boot configurations for the installed core, crate and modules.
*/
class BootConfigurationManager
{
private $cwd;
/** @var array<BootConfiguration> */
private $configurations = null;
/**
* Construct a manager.
* @param ComposerManager $composerManager
* @param string $cwd
*/
public function __construct(ComposerManager $composerManager, string $cwd)
{
$this->cwd = $cwd;
$packages = $composerManager->getPackages();
$packages = $composerManager->getPackages();
$this->configurations = [];
foreach($packages as $package)
{
{
if ($package->getType() === "lotgd-crate" || $package->getType() === "lotgd-module") {
$this->configurations[] = new BootConfiguration($composerManager, $package, $cwd);
}
}
}
/**
* Returns a list of all entity directories from lotgd packages
* @return array
@@ -34,16 +42,16 @@ class BootConfigurationManager
public function getEntityDirectories(): array
{
$entityDirectories = [];
foreach ($this->configurations as $config) {
if ($config->hasEntityDirectory()) {
$entityDirectories[] = $config->getEntityDirectory();
}
}
return $entityDirectories;
}
/**
* Adds commands from packages to daenerys
* @param \LotGD\Core\Game $game
@@ -58,4 +66,4 @@ class BootConfigurationManager
}
}
}
}
}
+29 -26
View File
@@ -23,13 +23,16 @@ use LotGD\Core\ {
Exceptions\InvalidConfigurationException
};
/**
* The entry point for constructing a properly configured LotGD Game object.
*/
class Bootstrap
{
private $rootDir;
private $game;
private $bootConfigurationManager = [];
private $annotationDirectories = [];
/**
* Create a new Game object, with all the necessary configuration.
* @param string $rootDir The root directory if it is different from getcwd()
@@ -40,7 +43,7 @@ class Bootstrap
$game = new self();
return $game->getGame($rootDir);
}
/**
* Starts the game kernel with the most important classes and returns the object
* @param string $rootDir The root directory if it is different from getcwd()
@@ -49,23 +52,23 @@ class Bootstrap
public function getGame(string $rootDir = null): Game
{
$this->rootDir = $rootDir ?? getcwd();
$composer = $this->createComposerManager();
$this->bootConfigurationManager = $this->createBootConfigurationManager($composer, $this->rootDir);
$config = $this->createConfiguration();
$logger = $this->createLogger($config, "lotgd");
$pdo = $this->connectToDatabase($config);
$entityManager = $this->createEntityManager($pdo);
$eventManager = $this->createEventManager($entityManager);
$this->game = new Game($config, $logger, $entityManager, $eventManager);
return $this->game;
}
/**
* Creates the boot configuration manager
* @param ComposerManager $composerManager
@@ -73,12 +76,12 @@ class Bootstrap
* @return \LotGD\Core\BootConfigurationManager
*/
protected function createBootConfigurationManager(
ComposerManager $composerManager,
ComposerManager $composerManager,
string $cwd
): BootConfigurationManager {
return new BootConfigurationManager($composerManager, $cwd);
}
/**
* Connects to a database using pdo
* @param \LotGD\Core\Configuration $config
@@ -88,7 +91,7 @@ class Bootstrap
{
return new \PDO($config->getDatabaseDSN(), $config->getDatabaseUser(), $config->getDatabasePassword());
}
/**
* Creates and returns an instance of ComposerManager
* @param Logger $logger
@@ -96,10 +99,10 @@ class Bootstrap
*/
protected function createComposerManager(): ComposerManager
{
$composer = new ComposerManager();
$composer = new ComposerManager();
return $composer;
}
/**
* Returns a configuration object reading from the file located at the path stored in LOTGD_CONFIG.
* @return \LotGD\Core\Configuration
@@ -108,22 +111,22 @@ class Bootstrap
protected function createConfiguration(): Configuration
{
$configFilePath = getenv('LOTGD_CONFIG');
if (empty($configFilePath)) {
$configFilePath = implode(DIRECTORY_SEPARATOR, [$this->rootDir, "config", "lotgd.yml"]);
}
else {
$configFilePath = $this->rootDir . $configFilePath;
}
if ($configFilePath === false || strlen($configFilePath) == 0 || is_file($configFilePath) === false) {
throw new InvalidConfigurationException("Invalid or missing configuration file: {$configFilePath}.");
}
$config = new Configuration($configFilePath, $this->rootDir);
return $config;
}
/**
* Returns a logger instance
* @param type $name
@@ -137,10 +140,10 @@ class Bootstrap
$v = Game::getVersion();
$logger->info("Bootstrap constructing game (Daenerys 🐲{$v}).");
return $logger;
}
/**
* Creates and returns an instance of the EventManager
* @param EntityManagerInterface $entityManager
@@ -150,7 +153,7 @@ class Bootstrap
{
return new EventManager($entityManager);
}
/**
* Creates the EntityManager using the pdo connection given in it's argument
* @param \PDO $pdo
@@ -171,7 +174,7 @@ class Bootstrap
$metaData = $entityManager->getMetadataFactory()->getAllMetadata();
$schemaTool = new SchemaTool($entityManager);
$schemaTool->updateSchema($metaData);
return $entityManager;
}
@@ -184,13 +187,13 @@ class Bootstrap
{
// Read db annotations from our own model files.
$directories = [__DIR__ . DIRECTORY_SEPARATOR . 'Models'];
// Get additional annotation directories from bootstrap classes
$packageDirectories = $this->bootConfigurationManager->getEntityDirectories();
return array_merge($directories, $packageDirectories);
}
/**
* Return all directories used for reading annotations.
* @return array<string>
@@ -199,7 +202,7 @@ class Bootstrap
{
return $this->annotationDirectories;
}
/**
* Adds console commands to a given console application from bootstrapping packages.
* @param Application $application
+70 -67
View File
@@ -34,7 +34,7 @@ class BuffList
protected $activeBuffs = [];
/** @var Doctrine\Common\Collections\ArrayCollection */
protected $usedBuffs;
/** @var boolean True of the modifiers have already been calculated */
protected $modifiersCalculated = false;
/** @var boolean True if the badguy is invulnurable */
@@ -53,10 +53,10 @@ class BuffList
protected $goodguyAttackModifier = 1.;
/** @var float */
protected $goodguyDefenseModifier = 1.;
protected $events;
protected $loaded = false;
/**
* Initiates some variables
* @param Collection $buffs
@@ -67,7 +67,7 @@ class BuffList
$this->events = new ArrayCollection();
$this->usedBuffs = new ArrayCollection();
}
/**
* Loads all buffs (since it's a lazy correlation)
*/
@@ -79,7 +79,7 @@ class BuffList
}
}
}
/**
* Returns true if the given buff has already been used this round.
* @param Buff $buff
@@ -93,10 +93,10 @@ class BuffList
else {
$used = false;
}
return $used;
}
/**
* Marks the given buff as used
* @param Buff $buff
@@ -105,7 +105,7 @@ class BuffList
{
$this->usedBuffs->add($buff);
}
/**
* Returns the buff's start or round message
* @param Buff $buff
@@ -122,10 +122,10 @@ class BuffList
elseif($used === false) {
$return = $buff->getRoundMessage();
}
return $return;
}
/**
* Resets the buff usage for a new round
*/
@@ -135,12 +135,15 @@ class BuffList
$this->usedBuffs = new ArrayCollection();
$this->modifiersCalculated = false;
}
/**
* Returns whether any buffs are in use.
*/
public function hasBuffsInUse(): bool
{
return count($this->usedBuffs) > 0 ? true : false;
}
/**
* Activates all buffs that activate upon the given activation parameter.
* @param int $activation
@@ -153,37 +156,37 @@ class BuffList
if ($activation%2 !== 0 && $activation !== 1) {
throw new ArgumentException("You can only activate one activation type at a time.");
}
if (!empty($this->activeBuffs[$activation])) {
throw new BuffListAlreadyActivatedException("You can activate the buff list for the given activation step only once.");
}
$this->activeBuffs[$activation] = new ArrayCollection();
$activationEvents = new ArrayCollection();
foreach ($this->iterateBuffList() as $buff) {
// Continue to next buff if the activation is not in this round.
if ($buff->getsActivatedAt($activation) === false) {
continue;
}
$this->activeBuffs[$activation]->add($buff);
// Returns start or roundMessage if the buff has not been used yet.
$buffMessage = $this->getBuffMessage($buff);
if ($buffMessage !== "") {
$activationEvents->add(new BuffMessageEvent($buffMessage));
}
// Needs to come at the end
if ($this->hasBuffBeenUsed($buff) === false) {
$this->useBuff($buff);
}
}
return $activationEvents;
}
/**
* Decreases the rounds left on all used buffs
* @return Collection A Collection containing expire messages (if there are any)
@@ -192,27 +195,27 @@ class BuffList
{
/* @var $endEvents Collection */
$endEvents = new ArrayCollection();
foreach($this->usedBuffs as $buff) {
/* @var $roundsLeft int */
$roundsLeft = $buff->getRounds() - 1;
$buff->setRounds($roundsLeft);
if ($roundsLeft === 0) {
/* @var $endMessage string */
$endMessage = $buff->getEndMessage();
if ($endMessage !== "") {
$endEvents->add(new BuffMessageEvent($endMessage));
}
$this->remove($buff);
}
}
return $endEvents;
}
/**
* Removes a buff from the buff list.
* @param Buff $buff
@@ -223,7 +226,7 @@ class BuffList
$this->buffs->removeElement($buff);
$this->usedBuffs->removeElement($buff);
}
/**
* Adds a buff to the buff list, occupying the slot.
* @param Buff $buff
@@ -233,15 +236,15 @@ class BuffList
{
$this->loadBuffs();
$slot = $buff->getSlot();
if (isset($this->buffsBySlot[$buff->getSlot()])) {
throw new BuffSlotOccupiedException("The slot {$slot} is already occupied.");
}
$this->buffs->add($buff);
$this->buffsBySlot[$buff->getSlot()] = $buff;
}
/**
* Renews a buff.
* @param Buff $buff
@@ -250,15 +253,15 @@ class BuffList
{
$this->loadBuffs();
$slot = $buff->getSlot();
if (isset($this->buffsBySlot[$buff->getSlot()])) {
$this->buffs->removeElement($buff);
}
$this->buffs->add($buff);
$this->buffsBySlot[$buff->getSlot()] = $buff;
}
/**
* Calculates all total modifiers
* @return type
@@ -268,7 +271,7 @@ class BuffList
if ($this->modifiersCalculated === true) {
return;
}
$this->badguyAttackModifier = 1.;
$this->badguyDamageModifier = 1.;
$this->badguyDefenseModifier = 1.;
@@ -277,7 +280,7 @@ class BuffList
$this->goodguyDamageModifier = 1.;
$this->goodguyDefenseModifier = 1.;
$this->goodguyInvulnurable = false;
/* @var $buff \LotGD\Core\Model\Buff */
foreach ($this->iterateBuffList() as $buff) {
$this->badguyAttackModifier *= $buff->getBadguyAttackModifier();
@@ -290,7 +293,7 @@ class BuffList
$this->goodguyInvulnurable = $this->goodguyInvulnurable || $buff->goodguyIsInvulnurable();
}
}
/**
* Iterates over every buff that gets activated at one point during a round.
* @return Generator|\LotGD\Core\Model\Buff[]
@@ -307,7 +310,7 @@ class BuffList
}
}
}
/**
* Returns the badguy attack modifier calculated over the whole bufflist
* @return float
@@ -317,7 +320,7 @@ class BuffList
$this->calculateModifiers();
return $this->badguyAttackModifier;
}
/**
* Returns the badguy defense modifier calculated over the whole bufflist
* @return float
@@ -327,7 +330,7 @@ class BuffList
$this->calculateModifiers();
return $this->badguyDefenseModifier;
}
/**
* Returns the badguy damage modifier calculated over the whole bufflist
* @return float
@@ -337,7 +340,7 @@ class BuffList
$this->calculateModifiers();
return $this->badguyDamageModifier;
}
/**
* Returns true if the badguy is invulnurable
* @return bool
@@ -347,7 +350,7 @@ class BuffList
$this->calculateModifiers();
return $this->badguyInvulnurable;
}
/**
* Returns the badguy attack modifier calculated over the whole bufflist
* @return float
@@ -357,7 +360,7 @@ class BuffList
$this->calculateModifiers();
return $this->goodguyAttackModifier;
}
/**
* Returns the badguy defense modifier calculated over the whole bufflist
* @return float
@@ -367,7 +370,7 @@ class BuffList
$this->calculateModifiers();
return $this->goodguyDefenseModifier;
}
/**
* Returns the badguy damage modifier calculated over the whole bufflist
* @return float
@@ -377,7 +380,7 @@ class BuffList
$this->calculateModifiers();
return $this->goodguyDamageModifier;
}
/**
* Returns true if the goodguy is invulnurable
* @return bool
@@ -387,7 +390,7 @@ class BuffList
$this->calculateModifiers();
return $this->goodguyInvulnurable;
}
/**
* Processes buffs that do direct damage or regeneration
* @param int $activation
@@ -403,7 +406,7 @@ class BuffList
FighterInterface $badguy
): Collection {
$events = [];
foreach ($this->activeBuffs[$activation] as $buff) {
// Add good guy regeneration
if ($buff->getGoodguyRegeneration() !== 0) {
@@ -414,7 +417,7 @@ class BuffList
$buff->getNoEffectMessage()
);
}
// Add bad guy regeneration
if ($buff->getBadguyRegeneration() !== 0) {
$events[] = new RegenerationBuffEvent(
@@ -424,23 +427,23 @@ class BuffList
$buff->getNoEffectMessage()
);
}
// Minion buff
if ($buff->getNumberOfMinions() > 0) {
/* @var $n int */
$n = $buff->getNumberOfMinions();
/* @var $attacksOne bool */
$attacksOne = ($buff->getMinionMinGoodguyDamage() || $buff->getMinionMaxGoodguyDamage() !== 0)
$attacksOne = ($buff->getMinionMinGoodguyDamage() || $buff->getMinionMaxGoodguyDamage() !== 0)
|| ($buff->getMinionMinBadguyDamage() || $buff->getMinionMaxBadguyDamage() !== 0);
/* @var $attacksBoth bool */
$attacksBoth = ($buff->getMinionMinGoodguyDamage() || $buff->getMinionMaxGoodguyDamage() !== 0)
$attacksBoth = ($buff->getMinionMinGoodguyDamage() || $buff->getMinionMaxGoodguyDamage() !== 0)
&& ($buff->getMinionMinBadguyDamage() || $buff->getMinionMaxBadguyDamage() !== 0);
// Faulty buff - if minions attack no one, it's better to have no minions at all. Or they will just do... nothing.
if ($attacksOne === false) {
$n = 0;
}
// Add a minion event for every single minion
for ($i = 0; $i < $n; $i++) {
// If the buff is setup to attack both good and badguy, we throw a dice to decide who the minion attacks
@@ -459,7 +462,7 @@ class BuffList
else {
$who = -1;
}
if ($who === 1) {
// Minion does damage to the goodguy
$damage = $game->getDiceBag()->normal($buff->getMinionMinGoodguyDamage(), $buff->getMinionMaxGoodguyDamage());
@@ -470,7 +473,7 @@ class BuffList
$damage = $game->getDiceBag()->normal($buff->getMinionMinBadguyDamage(), $buff->getMinionMaxBadguyDamage());
$target = $badguy;
}
if ($damage < 0) {
$message = $buff->getEffectFailsMessage();
}
@@ -489,12 +492,12 @@ class BuffList
}
}
}
return new ArrayCollection(
$events
);
}
/**
* Processes buffs that are dependant on the damage done in one round
* @param int $activation
@@ -512,7 +515,7 @@ class BuffList
FighterInterface $badguy
): Collection {
$events = [];
foreach($this->activeBuffs[$activation] as $buff) {
if ($buff->getGoodguyDamageReflection() !== 0.) {
if ($damage > 0) {
@@ -532,14 +535,14 @@ class BuffList
$message = $buff->getEffectSucceedsMessage();
}
}
$events[] = new DamageReflectionEvent(
$badguy,
$badguy,
$reflectedDamage,
$message
);
}
if ($buff->getBadguyDamageReflection() !== 0.) {
if ($damage > 0) {
// Damage is > 0, so badguy takes damage, we can normally reflect
@@ -558,14 +561,14 @@ class BuffList
$reflectedDamage = 0;
$message = $buff->getEffectFailsMessage();
}
$events[] = new DamageReflectionEvent(
$goodguy,
$goodguy,
$reflectedDamage,
$message
);
}
if ($buff->getGoodguyLifetap() !== 0.) {
if ($damage > 0) {
// Damage is > 0, badguy takes damage. Goodguy lifetap works only upon damage to the goodguy.
@@ -586,14 +589,14 @@ class BuffList
$healAmount = 0;
$message = $buff->getNoEffectMessage();
}
$events[] = new DamageLifetapEvent(
$badguy,
$healAmount,
$message
);
}
if ($buff->getBadguyLifetap() !== 0.) {
if ($damage > 0) {
// Damage is > 0, badguy takes damage. We act upon this to heal goodguy.
@@ -614,7 +617,7 @@ class BuffList
$healAmount = 0;
$message = $buff->getNoEffectMessage();
}
$events[] = new DamageLifetapEvent(
$goodguy,
$healAmount,
@@ -622,7 +625,7 @@ class BuffList
);
}
}
return new ArrayCollection($events);
}
}
+4
View File
@@ -173,6 +173,10 @@ class Configuration
return $this->gameDaysPerDay;
}
/**
* Generate a textual representation of the configuration, for debugging
* purposes.
*/
public function __toString(): string
{
$s = "";
+9 -2
View File
@@ -7,13 +7,20 @@ use Symfony\Component\Console\Command\Command;
use LotGD\Core\Game;
/**
* Parent class for daenerys tool commands.
*/
abstract class BaseCommand extends Command
{
protected $game;
/**
* Construct the command, using the provided Game.
* @param Game $game
*/
public function __construct(Game $game)
{
parent::__construct();
$this->game = $game;
}
}
}
+9
View File
@@ -6,14 +6,23 @@ namespace LotGD\Core\Console\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Daenerys command to start a PHP REPL with a basic game set up.
*/
class ConsoleCommand extends BaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('console')
->setDescription('Start a shell to interact with the game.');
}
/**
* @inheritDoc
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
print("Daenerys console, the dragon prompt. lotgd/core " . \LotGD\Core\Game::getVersion() . ".\n");
+10 -1
View File
@@ -10,14 +10,23 @@ use Symfony\Component\Console\Output\OutputInterface;
use LotGD\Core\Console\Main;
use LotGD\Core\Game;
/**
* Danerys command to initiate the database with default values.
*/
class DatabaseInitCommand extends BaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('database:init')
->setDescription('Initiates database with default values.');
}
/**
* @inheritDoc
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->game->getEntityManager()->flush();
+10 -1
View File
@@ -9,14 +9,23 @@ use Symfony\Component\Console\Output\OutputInterface;
use LotGD\Core\Exceptions\ClassNotFoundException;
use LotGD\Core\Exceptions\ModuleAlreadyExistsException;
/**
* Danerys command to register and initiate any newly installed modules.
*/
class ModuleRegisterCommand extends BaseCommand
{
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('module:register')
->setDescription('Register and initialize any newly installed modules');
}
/**
* @inheritDoc
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$modules = $this->game->getComposerManager()->getModulePackages();
@@ -6,14 +6,23 @@ namespace LotGD\Core\Console\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Danerys command to validate installed modules.
*/
class ModuleValidateCommand extends BaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('module:validate')
->setDescription('Validate installed modules');
}
/**
* @inheritDoc
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$results = $this->game->getModuleManager()->validate();
+18 -6
View File
@@ -14,11 +14,17 @@ use LotGD\Core\Console\Command\{
ConsoleCommand
};
/**
* Main execution class for the daenerys tool.
*/
class Main {
private $application;
private $bootstrap;
private $game;
/**
* Construct a new daenerys tool instance.
*/
public function __construct()
{
$this->application = new Application();
@@ -26,27 +32,33 @@ class Main {
$this->application->setName("daenerys 🐲 ");
$this->application->setVersion("0.0.1 (lotgd/core version " . \LotGD\Core\Game::getVersion() . ")");
}
/**
* Add supported commands, including those configured from lotgd.yml files.
*/
protected function addCommands()
{
$this->application->add(new ModuleValidateCommand($this->game));
$this->application->add(new ModuleRegisterCommand($this->game));
$this->application->add(new DatabaseInitCommand($this->game));
$this->application->add(new ConsoleCommand($this->game));
// Add additional ones
$this->bootstrap->addDaenerysCommands($this->application);
}
/**
* Run the danerys tool.
*/
public function run()
{
// Bootstrap application
$this->bootstrap = new Bootstrap();
$this->game = $this->bootstrap->getGame();
// Add commands
$this->addCommands();
// Run
$this->application->run();
}
+21 -4
View File
@@ -4,8 +4,15 @@ declare (strict_types = 1);
namespace LotGD\Core;
/**
* A collection of random number generators, with various distributions.
*/
class DiceBag
{
/**
* Returns true $p percent of the time, where $p is between 0 and 1.
* @param float $p
*/
public function chance(float $p): bool
{
$r = $this->uniform(0., 1.);
@@ -13,11 +20,21 @@ class DiceBag
return $r < $p;
}
/**
* Generates a uniformly randomly number between $min and $max.
* @param float $min
* @param float $max
*/
public function uniform(float $min, float $max): float
{
return (mt_rand(0, 100) / 100.0) * ($max - $min) + $min;
}
/**
* Generates a normally distributed random number between $min and $max.
* @param float $min
* @param float $max
*/
public function normal(float $min, float $max): float
{
if ($min > $max) {
@@ -38,7 +55,7 @@ class DiceBag
return $r;
}
/**
* This function has uniform distribution except for the extreme values, which are
* half as likely to happen.
@@ -53,14 +70,14 @@ class DiceBag
if (is_null($min)) {
return mt_rand();
}
$min *= 1000;
if (is_null($max)) {
return (int)round(mt_rand($min)/1000, 0);
}
$max *= 1000;
if ($min === $max) {
return (int)round($min/1000, 0);
}
+7 -1
View File
@@ -8,6 +8,9 @@ use Monolog\Logger;
use LotGD\Core\Models\Character;
/**
* The main game class.
*/
class Game
{
private $entityManager;
@@ -16,7 +19,10 @@ class Game
private $moduleManager;
private $logger;
private $configuration;
/**
* Construct a game. You probably want to use Bootstrap to do this.
*/
public function __construct(
Configuration $configuration,
Logger $logger,
+6 -6
View File
@@ -6,12 +6,12 @@ namespace LotGD\Core\Models\BattleEvents;
use LotGD\Core\Exceptions\BattleEventException;
/**
* BattleEvent
* A representation of something that happened in battle.
*/
class BattleEvent
{
{
private $applied = false;
/**
* Applies the event.
* @throws BattleEventException
@@ -21,10 +21,10 @@ class BattleEvent
if ($this->applied === true) {
throw new BattleEventException("Cannot apply an event more than once.");
}
$this->applied = true;
}
/**
* Returns a string describing the event.
* @param \LotGD\Core\Models\BattleEvents\Game $game
@@ -36,7 +36,7 @@ class BattleEvent
if ($this->applied === false) {
throw new BattleEventException("Buff needs to get applied before decoration.");
}
return "";
}
}
+16 -5
View File
@@ -6,21 +6,32 @@ namespace LotGD\Core\Models\BattleEvents;
use LotGD\Core\Exceptions\BattleEventException;
/**
* BattleEvent
* A battle event representing a message generated by a buff.
*/
class BuffMessageEvent extends BattleEvent
{
{
private $message = "";
/**
* Create a new BuffMessageEvent.
* @param string $message The message from the buff.
*/
public function __construct(string $message) {
$this->message = $message;
}
/**
* Return the message.
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
/**
* @inheritDoc
*/
public function decorate(Game $game): string
{
return $message;
+12 -4
View File
@@ -6,7 +6,7 @@ namespace LotGD\Core\Models\BattleEvents;
use LotGD\Core\Models\FighterInterface;
/**
* Description of CriticalHitEvent
* Battle event representing a stronger than average attack.
*/
class CriticalHitEvent extends BattleEvent
{
@@ -14,17 +14,25 @@ class CriticalHitEvent extends BattleEvent
protected $attacker;
/** @var int */
protected $criticalAttackValue;
/**
* Construct a CriticalHitEvent with attacker $attacker.
* @param FighterInterface $attacker
* @param int $criticalAttackValue
*/
public function __construct(FighterInterface $attacker, int $criticalAttackValue)
{
$this->attacker = $attacker;
$this->criticalAttackValue = $criticalAttackValue;
}
/**
* @inheritDoc
*/
public function decorate(Game $game): string
{
$pureAttackersAttack = $this->attacker->getAttack($game, true);
if ($this->criticalAttackValue > $pureAttackersAttack * 4) {
return "You execute a MEGA power move!!!";
} elseif ($this->criticalAttackValue > $pureAttackersAttack * 3) {
+17 -13
View File
@@ -9,24 +9,30 @@ use LotGD\Core\Models\FighterInterface;
* BattleEvent
*/
class DamageEvent extends BattleEvent
{
{
/** @var FighterInstance */
protected $attacker;
/** @var FighterInstance */
protected $defender;
/** @var int Damage applied */
protected $damage;
/**
* Construct a new DamageEvent of $attacker attacking $defender.
* @param FighterInterface $attacker
* @param FighterInterface $defender
* @param int $damage
*/
public function __construct(FighterInterface $attacker, FighterInterface $defender, int $damage)
{
$this->attacker = $attacker;
$this->defender = $defender;
$this->damage = $damage;
}
/**
* Returns the damage that is applied in this fight.
*
*
* If the damage is > 0, the damage is applied to the defender. If it's < 0, it's applied to the attacker.
* @return int
*/
@@ -34,33 +40,31 @@ class DamageEvent extends BattleEvent
{
return $this->damage;
}
/**
* Applies the damage.
* @inheritDoc
*/
public function apply()
{
parent::apply();
if ($this->damage !== 0) {
// Only damage the victim if there is an actual effect
$victim = $this->damage > 0 ? $this->defender : $this->attacker;
$victim->damage(abs($this->damage));
}
}
/**
* Returns a string describing the event.
* @param \LotGD\Core\Models\BattleEvents\Game $game
* @return string
* @inheritDoc
*/
public function decorate(Game $game): string
{
parent::decorate($game);
$attackersName = $this->attacker->getDisplayName();
$defendersName = $this->defender->getDisplayName();
if ($this->damage === 0) {
if ($this->attacker === $game->getCharacter()) {
return "You try to hit {$defendersName} but MISS!";
+28 -9
View File
@@ -6,33 +6,49 @@ namespace LotGD\Core\Models\BattleEvents;
use LotGD\Core\Models\FighterInterface;
/**
* BattleEvent
* Damage event where damage is the result of a life tap.
*/
class DamageLifetapEvent extends BattleEvent
{
{
/** @var \LotGD\Core\Models\FighterInterface */
protected $target;
/** @var int */
protected $healAmount;
/** @var string */
protected $message;
/**
* Construct a new DamageLifetapEvent where healing amount is $healAmount and
* target is $target.
* $message can contain '{target}' and '{damage}'
* which will be replaced by the name of the target and the damage, respectively.
* @param FighterInterface $target
* @param int $healAmount
* @param string $message
*/
public function __construct(FighterInterface $target, int $healAmount, string $message)
{
$this->target = $target;
$this->healAmount = $healAmount;
$this->message = $message;
}
/**
* Return the heal amount.
* @return int
*/
public function getHealAmount(): int
{
return $this->healAmount;
}
/**
* @inheritDoc
*/
public function apply()
{
parent::apply();
if ($this->healAmount === 0) {
return;
} elseif ($this->healAmount > 0) {
@@ -41,14 +57,17 @@ class DamageLifetapEvent extends BattleEvent
$this->target->setHealth($this->target->getHealth() + $this->healAmount);
}
}
/**
* @inheritDoc
*/
public function decorate(Game $game): string
{
parent::decorate($game);
return str_replace(
[
"{target}",
"{target}",
"{damage}"
],
[
@@ -6,24 +6,33 @@ namespace LotGD\Core\Models\BattleEvents;
use LotGD\Core\Models\FighterInterface;
/**
* BattleEvent
* A battle event representing damage being reflected back on the attacker.
*/
class DamageReflectionEvent extends BattleEvent
{
{
/** @var \LotGD\Core\Models\FighterInterface */
protected $target;
/** @var int */
protected $damage;
/** @var string */
protected $message;
/**
* Construct a DamageReflectionEvent with the target $target, damage amount
* $damage and the message $message.
* $message can contain '{target}' and '{damage}'
* which will be replaced by the name of the target and the damage, respectively.
* @param FighterInterface $target
* @param int $damage
* @param string $message
*/
public function __construct(FighterInterface $target, int $damage, string $message)
{
$this->target = $target;
$this->damage = $damage;
$this->message = $message;
}
/**
* Returns the damage
* @return int
@@ -32,14 +41,14 @@ class DamageReflectionEvent extends BattleEvent
{
return $this->damage;
}
/*
* Applies the damage
/**
* @inheritDoc
*/
public function apply()
{
parent::apply();
if ($this->damage === 0) {
return;
} elseif ($this->damage > 0) {
@@ -48,19 +57,17 @@ class DamageReflectionEvent extends BattleEvent
$this->target->setHealth($this->target->getHealth() - $this->damage);
}
}
/**
* Returns a string describing the event
* @param \LotGD\Core\Models\BattleEvents\Game $game
* @return string
* @inheritDoc
*/
public function decorate(Game $game): string
{
parent::decorate($game);
return str_replace(
[
"{target}",
"{target}",
"{damage}"
],
[
+16 -6
View File
@@ -6,22 +6,32 @@ namespace LotGD\Core\Models\BattleEvents;
use LotGD\Core\Models\FighterInterface;
/**
* BattleEvent
* BattleEvent representing a fighter's death.
*/
class DeathEvent extends BattleEvent
{
{
protected $victim;
/**
* Construct a DeathEvent for victim $victim.
* @param FighterInterface $victim
*/
public function __construct(FighterInterface $victim)
{
$this->victim = $victim;
}
/**
* @inheritDoc
*/
public function apply()
{
}
/**
* @inheritDoc
*/
public function decorate(Game $game): string
{
return "";
+20 -5
View File
@@ -7,14 +7,23 @@ use LotGD\Core\Exceptions\BattleEventException;
use LotGD\Core\Models\FighterInterface;
/**
* BattleEvent
* Battle event that represents damage to a minion.
*/
class MinionDamageEvent extends BattleEvent
{
protected $target;
protected $damage;
protected $message;
/**
* Construct a MinionDamageEvent against $target, with damage $damage
* and message $message.
* $message can contain '{target}' and '{amount}'
* which will be replaced by the name of the target and the damage, respectively.
* @param FighterInterface $target
* @param int $damage
* @param string $message
*/
public function __construct(
FighterInterface $target,
int $damage,
@@ -24,11 +33,14 @@ class MinionDamageEvent extends BattleEvent
$this->damage = $damage;
$this->message = $message;
}
/**
* @inheritDoc
*/
public function decorate(Game $game): string
{
parent::decorate();
return str_replace(
[
"{target}",
@@ -41,7 +53,10 @@ class MinionDamageEvent extends BattleEvent
$this->message
);
}
/**
* @inheritDoc
*/
public function apply()
{
parent::apply();
@@ -7,7 +7,7 @@ use LotGD\Core\Exceptions\BattleEventException;
use LotGD\Core\Models\FighterInterface;
/**
* BattleEvent
* Battle event that represents regenerating health.
*/
class RegenerationBuffEvent extends BattleEvent
{
@@ -15,7 +15,18 @@ class RegenerationBuffEvent extends BattleEvent
protected $regeneration;
protected $effectMessage;
protected $noEffectMessage;
/**
* Construct a RegenerationBuffEvent against $target, with regenerating value
* $regeneration. $effectMessage is shown if there is an effect of
* regeneration, and $noEffectMessage is shown if the $regeneation is 0.
* $effectMessage and $noEffectMessage can contain '{target}' and '{amount}'
* which will be replaced by the name of the target and the damage, respectively.
* @param FighterInterface $target
* @param int $regeneration
* @param string $effectMessage
* @param string $noEffectMessage
*/
public function __construct(
FighterInterface $target,
int $regeneration,
@@ -27,11 +38,14 @@ class RegenerationBuffEvent extends BattleEvent
$this->effectMessage = $effectMessage;
$this->noEffectMessage = $noEffectMessage;
}
/**
* @inheritDoc
*/
public function decorate(Game $game): string
{
parent::decorate();
if ($this->regeneration === 0) {
return str_replace(
"{target}",
@@ -53,14 +67,17 @@ class RegenerationBuffEvent extends BattleEvent
);
}
}
/**
* @inheritDoc
*/
public function apply()
{
parent::apply();
$healthLacking = $this->target->getMaxHealth() - $this->target->getHealth();
$healthLeft = $this->target->getHealth();
if ($this->regeneration > 0) {
// Healing
if ($healthLacking === 0) {
@@ -79,7 +96,7 @@ class RegenerationBuffEvent extends BattleEvent
$this->regeneration = - $healthLeft;
}
}
$this->target->setHealth($this->target->getHealth() + $this->regeneration);
}
}
+31 -28
View File
@@ -32,7 +32,7 @@ class Character implements CharacterInterface, CreateableInterface
use Creator;
use SoftDeletable;
use PropertyManager;
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
/** @Column(type="string", length=50); */
@@ -49,7 +49,7 @@ class Character implements CharacterInterface, CreateableInterface
private $properties;
/** @OneToMany(targetEntity="CharacterViewpoint", mappedBy="owner", cascade={"persist"}) */
private $characterViewpoint;
/**
/**
* @ManyToMany(targetEntity="MessageThread", inversedBy="participants", cascade={"persist"})
* @JoinTable(
* name="message_threads_x_characters",
@@ -66,14 +66,14 @@ class Character implements CharacterInterface, CreateableInterface
private $buffs;
/** @var BuffList */
private $buffList;
/** @var array */
private static $fillable = [
"name",
"maxHealth",
"level",
];
/**
* Creates a character at full health
*/
@@ -83,7 +83,10 @@ class Character implements CharacterInterface, CreateableInterface
$newCharacter->setHealth($newCharacter->getMaxHealth());
return $newCharacter;
}
/**
* Construct an empty character.
*/
public function __construct()
{
$this->properties = new ArrayCollection();
@@ -91,7 +94,7 @@ class Character implements CharacterInterface, CreateableInterface
$this->buffs = new ArrayCollection();
$this->messageThreads = new ArrayCollection();
}
/**
* Returns the entity's id
* @return int The id
@@ -100,7 +103,7 @@ class Character implements CharacterInterface, CreateableInterface
{
return $this->id;
}
/**
* Sets the character's name and generates the display name
* @param string $name The name to set
@@ -110,7 +113,7 @@ class Character implements CharacterInterface, CreateableInterface
$this->name = $name;
$this->generateDisplayName();
}
/**
* Returns the character's name
* @return string The name
@@ -119,7 +122,7 @@ class Character implements CharacterInterface, CreateableInterface
{
return $this->name;
}
/**
* Generates the display name which is a composition of title and name.
*/
@@ -127,7 +130,7 @@ class Character implements CharacterInterface, CreateableInterface
{
$this->displayName = $this->name;
}
/**
* Returns displayName, a combination of title, name and suffix, mixed with colour codes
* @return string The displayName
@@ -136,7 +139,7 @@ class Character implements CharacterInterface, CreateableInterface
{
return $this->displayName;
}
/**
* Sets the maximum health of a character to a given value. It also sets the
* health if none has been set yet.
@@ -146,7 +149,7 @@ class Character implements CharacterInterface, CreateableInterface
{
$this->maxHealth = $maxHealth;
}
/**
* Returns the maximum health
* @return int
@@ -155,7 +158,7 @@ class Character implements CharacterInterface, CreateableInterface
{
return $this->maxHealth;
}
/**
* Sets current health
* @param int $health
@@ -164,7 +167,7 @@ class Character implements CharacterInterface, CreateableInterface
{
$this->health = $health;
}
/**
* Returns current health
* @return int
@@ -173,7 +176,7 @@ class Character implements CharacterInterface, CreateableInterface
{
return $this->health;
}
/**
* Does damage to the entity.
* @param int $damage
@@ -181,12 +184,12 @@ class Character implements CharacterInterface, CreateableInterface
public function damage(int $damage)
{
$this->health -= $damage;
if ($this->health < 0) {
$this->health = 0;
}
}
/**
* Heals the enemy
* @param int $heal
@@ -195,12 +198,12 @@ class Character implements CharacterInterface, CreateableInterface
public function heal(int $heal, bool $overheal = false)
{
$this->health += $heal;
if ($this->health > $this->getMaxHealth() && $overheal === false) {
$this->health = $this->getMaxHealth();
}
}
/**
* Returns true if the character is alive.
* @return bool
@@ -209,7 +212,7 @@ class Character implements CharacterInterface, CreateableInterface
{
return $this->getHealth() > 0;
}
/**
* Returns the character's level
* @return int
@@ -218,7 +221,7 @@ class Character implements CharacterInterface, CreateableInterface
{
return $this->level;
}
/**
* Returns the character's virtual attribute "attack"
*/
@@ -226,7 +229,7 @@ class Character implements CharacterInterface, CreateableInterface
{
return $this->level * 2;
}
/**
* Returns the character's virtual attribute "defense"
*/
@@ -234,7 +237,7 @@ class Character implements CharacterInterface, CreateableInterface
{
return $this->level * 2;
}
/**
* Sets the character's level
* @param int $level
@@ -243,7 +246,7 @@ class Character implements CharacterInterface, CreateableInterface
{
$this->level = $level;
}
/**
* Returns the current character scene and creates one if it is non-existant
* @return \LotGD\Core\Models\CharacterViewpoint
@@ -254,10 +257,10 @@ class Character implements CharacterInterface, CreateableInterface
$characterScene = CharacterViewpoint::Create(["owner" => $this]);
$this->characterViewpoint->add($characterScene);
}
return $this->characterViewpoint->first();
}
/**
* Returns a list of buffs
*/
@@ -266,7 +269,7 @@ class Character implements CharacterInterface, CreateableInterface
$this->buffList = $this->buffList ?? new BuffList($this->buffs);
return $this->buffList;
}
/**
* Adds a buff to the buffList
*/
@@ -278,7 +281,7 @@ class Character implements CharacterInterface, CreateableInterface
$this->getBuffs()->renew($buff);
}
}
/**
* Returns a list of message threads this user has created.
* @return Collection
+13
View File
@@ -47,6 +47,11 @@ class EventSubscription implements CreateableInterface
return $this->pattern;
}
/**
* Set the pattern used to match against event names.
* Format is PHP regular expressions.
* @param string $pattern
*/
public function setPattern(string $pattern)
{
$this->pattern = $pattern;
@@ -61,6 +66,10 @@ class EventSubscription implements CreateableInterface
return $this->class;
}
/**
* Sets the class name subscribed to this event.
* @param string $class
*/
public function setClass(string $class)
{
$this->class = $class;
@@ -75,6 +84,10 @@ class EventSubscription implements CreateableInterface
return $this->library;
}
/**
* Sets the library that this subscription is part of, in vendor/module format.
* @param string $library
*/
public function setLibrary(string $library)
{
$this->library = $library;
+3
View File
@@ -27,6 +27,9 @@ class Module implements SaveableInterface
/** @Column(type="datetime") */
private $createdAt;
/**
* Construct a new module entry.
*/
public function __construct(string $library)
{
$this->createdAt = new \DateTime();
+24 -15
View File
@@ -11,17 +11,20 @@ use Doctrine\ORM\QueryBuilder;
use LotGD\Core\Models\Character;
/**
* Description of CharacterRepository
* Convenience methods to query for characters.
*/
class CharacterRepository extends EntityRepository
{
const SKIP_SOFTDELETED = 0;
const INCLUDE_SOFTDELETED = 1;
const ONLY_SOFTDELETED = 2;
protected function modifyQuery(QueryBuilder $queryBuilder, int $level)
/**
* Change the provided query to handle the specified deletion mode.
*/
protected function modifyQuery(QueryBuilder $queryBuilder, int $deletes)
{
switch ($level) {
switch ($deletes) {
case self::SKIP_SOFTDELETED:
$queryBuilder->andWhere(
$queryBuilder->expr()->orX(
@@ -30,7 +33,7 @@ class CharacterRepository extends EntityRepository
)
);
break;
case self::ONLY_SOFTDELETED:
$queryBuilder->andWhere(
$queryBuilder->expr()->lte('c.deletedAt', 'CURRENT_TIMESTAMP()')
@@ -38,32 +41,38 @@ class CharacterRepository extends EntityRepository
break;
}
}
public function find($id, int $level = self::SKIP_SOFTDELETED)
/**
* Find a character by ID.
*/
public function find($id, int $deletes = self::SKIP_SOFTDELETED)
{
$queryBuilder = $this->getEntityManager()->createQueryBuilder();
$queryBuilder->select("c")
->from(Character::class, "c")
->where($queryBuilder->expr()->eq("c.id", ":id"))
->setParameter("id", $id);
$this->modifyQuery($queryBuilder, $level);
$this->modifyQuery($queryBuilder, $deletes);
try {
return $queryBuilder->getQuery()->getSingleResult();
} catch (NoResultException $e) {
return null;
}
}
public function findAll(int $level = self::SKIP_SOFTDELETED)
/**
* Return all characters.
*/
public function findAll(int $deletes = self::SKIP_SOFTDELETED)
{
$queryBuilder = $this->getEntityManager()->createQueryBuilder();
$queryBuilder->select("c")
->from(Character::class, "c");
$this->modifyQuery($queryBuilder, $level);
$this->modifyQuery($queryBuilder, $deletes);
return $queryBuilder->getQuery()->getResult();
}
}
+23 -10
View File
@@ -7,35 +7,48 @@ namespace LotGD\Core\Models;
use LotGD\Core\Tools\Model\MockCharacter;
/**
* Provides a basic system character to provide system information.
*
* Whenever a message should be sent by the System instead of a standard character,
* Provides a basic system character to serve as an anonymous user.
*
* Whenever a message should be sent by the System instead of a standard character,
* this class is returned by the entity containing the message instead of a standard
* character instance.
*/
class SystemCharacter implements CharacterInterface
{
use MockCharacter;
static $instance = null;
static $characterName = "System";
/**
* Return an instance of SystemCharacter.
* @return SystemCharacter
*/
public static function getInstance()
{
self::$instance = self::$instance ?? new self();
return self::$instance;
}
/**
* Private constructor. Use the static method getInstance().
*/
private function __construct()
{
{
}
/**
* @inheritDoc
*/
public function getDisplayName(): string
{
return self::$characterName;
}
/**
* @inheritDoc
*/
public function getName(): string
{
return self::$characterName;
+4
View File
@@ -19,6 +19,7 @@ class ModuleManager
private $g;
/**
* Construct a module manager.
* @param Game $g The game.
*/
public function __construct(Game $g)
@@ -26,6 +27,9 @@ class ModuleManager
$this->g = $g;
}
/**
* Return the package's event subscriptions as an array of regex patterns.
*/
private static function getPackageSubscriptions(PackageInterface $package): array
{
$name = $package->getName();
+31
View File
@@ -5,6 +5,9 @@ namespace LotGD\Core;
use DateTime;
/**
* Configurable way to convert back and forth between real time and game time.
*/
class TimeKeeper
{
private $adjustedEpoch;
@@ -19,6 +22,12 @@ class TimeKeeper
private $secondsPerGameMinute;
private $secondsPerGameSecond;
/**
* Construct a TimeKeeper with required configuration.
* @param DateTime $gameEpoch When in real time is game day 0.
* @param int $gameOffsetSeconds How many seconds from midnight on the epoch should the first game day start.
* @param int $gameDaysPerDay How many game days are in one real day.
*/
public function __construct(DateTime $gameEpoch, int $gameOffsetSeconds, int $gameDaysPerDay)
{
$gameEpochCopy = clone($gameEpoch);
@@ -34,6 +43,11 @@ class TimeKeeper
$this->secondsPerGameSecond = $this->secondsPerGameMinute / 60;
}
/**
* Returns whether a user who is interating with the game now and last
* interacted at $lastInteractionTime should experience a New Day event.
* @param DateTime $lastInteractionTime
*/
public function isNewDay(DateTime $lastInteractionTime): bool
{
if ($lastInteractionTime == null) {
@@ -47,11 +61,20 @@ class TimeKeeper
return $d1 != $d2;
}
/**
* Return the current game time.
* @return DateTime
*/
public function gameTime(): DateTime
{
return $this->convertToGameTime(new DateTime());
}
/**
* Given a game time, convert it to a real time.
* @param DateTime $time Game time to convert.
* @return DateTime Real time corresponding to game time $time.
*/
public function convertFromGameTime(
DateTime $time
): DateTime {
@@ -68,6 +91,11 @@ class TimeKeeper
return $ret->add($this->interval(0, 0, 0, 0, (int) $seconds));
}
/**
* Given a real time, convert it to a game time.
* @param DateTime $time Real time to convert.
* @return DateTime Game time corresponding to real time $time.
*/
public function convertToGameTime(
DateTime $time
): DateTime {
@@ -97,6 +125,9 @@ class TimeKeeper
);
}
/**
* Convenience method to generate a DateInterval from an exploded date.
*/
private function interval(
int $years,
int $days,
+111 -78
View File
@@ -11,6 +11,9 @@ use LotGD\Core\Exceptions\KeyNotFoundException;
use LotGD\Core\Exceptions\NotImplementedException;
use LotGD\Core\Exceptions\WrongTypeException;
/**
* A one-to-many relation between two entities.
*/
class OneToManyCollection implements Collection
{
/** @var string */
@@ -21,9 +24,9 @@ class OneToManyCollection implements Collection
private $collection;
/** @var int */
private $numberOfRows;
/**
* Constructor
* Constructor for a one to many colelction of type $typeClass.
* @param EntityManagerInterface $entityManager
* @param string $typeClass
* @throws ClassNotFoundException
@@ -33,26 +36,25 @@ class OneToManyCollection implements Collection
if(class_exists($typeClass) === false) {
throw new ClassNotFoundException(sprintf("The class %s has not been found.", $typeClass));
}
$this->entityManager = $entityManager;
$this->typeClass = $typeClass;
// Load eagerly everything.
$this->collection = $this->entityManager->getRepository($this->typeClass)->findAll();
}
/**
* returns the class this collection consists of.
* Returns the class this collection consists of.
* @return string
*/
public function getTypeClass(): ClassMetadata
{
return $this->entityManager->getClassMetadata($this->typeClass);
}
/**
* Counts the number of settings stored
* @return int
* @inheritDoc
*/
public function count(): int
{
@@ -64,7 +66,7 @@ class OneToManyCollection implements Collection
->getQuery()
->getSingleScalarResult();
}
if ($this->collection === null) {
return $this->numberOfRows;
}
@@ -74,7 +76,7 @@ class OneToManyCollection implements Collection
}
/**
* Checks if the element matches the typeClass of this collection
* Checks if the element matches the type of this collection.
* @param mixed $element
* @throws WrongTypeException
*/
@@ -84,25 +86,24 @@ class OneToManyCollection implements Collection
throw new WrongTypeException(sprintf('$element needs to be of type %s', $this->typeClass));
}
}
/**
* Adds an element to the collection
* @param mixed $element
* @inheritDoc
*/
public function add($element)
{
$this->checkElementType($element);
if ($this->collection === null) {
$this->collection = [];
}
$this->collection[] = $element;
$this->entityManager->persist($element);
}
/**
* Clears the collection
* @inheritDoc
*/
public function clear()
{
@@ -112,11 +113,9 @@ class OneToManyCollection implements Collection
->execute();
$this->collection = [];
}
/**
* Returns true if a item is contained in this collection
* @param type $element
* @return bool
* @inheritDoc
*/
public function contains($element): bool
{
@@ -126,22 +125,20 @@ class OneToManyCollection implements Collection
catch (WrongTypeException $e) {
return false;
}
return in_array($element, $this->collection);
}
/**
* Checks if this the collection is empty
* @return bool
* @inheritDoc
*/
public function isEmpty(): bool
{
return empty($this->collection);
}
/**
* Removes an element from this collection by the given key
* @param int|string $key
* @inheritDoc
*/
public function remove($key)
{
@@ -150,10 +147,9 @@ class OneToManyCollection implements Collection
$this->removeElement($element);
}
}
/**
* Removes an element from this collection
* @param type $element
* @inheritDoc
*/
public function removeElement($element)
{
@@ -163,18 +159,17 @@ class OneToManyCollection implements Collection
unset($this->collection[$key]);
}
}
/**
* Checks if this collection contains a certain key
* @param int|string $key
* @inheritDoc
*/
public function containsKey($key)
{
return isset($this->collection[$key]);
}
/**
* Returns the element saved at the given position
* Returns the element saved with the given key.
* @param int|string $key
* @return type
* @throws KeyNotFoundException
@@ -188,136 +183,174 @@ class OneToManyCollection implements Collection
throw new KeyNotFoundException(sprintf("The key %s has not been found within the collection", $key));
}
}
/**
* Returns all collection keys
* @return array
* @inheritDoc
*/
public function getKeys(): array
{
return array_keys($this->collection);
}
/**
* Returns all collection values
* @return array
* @inheritDoc
*/
public function getValues(): array
{
return array_values($this->collection);
}
/**
* Sets the element at position $key to $value.
* @param int|string $key
* @param mixed $value
* @inheritDoc
*/
public function set($key, $element)
public function set($key, $value)
{
$this->checkElementType($element);
$this->checkElementType($value);
$this->remove($key);
$this->collection[$key] = $element;
$this->entityManager->persist($element);
$this->collection[$key] = $value;
$this->entityManager->persist($value);
}
/**
* Returns an array representation of this collection
* @return array
* @inheritDoc
*/
public function toArray(): array
{
return $this->collection;
}
/**
* @inheritDoc
*/
public function first()
{
return first($this->collection);
}
/**
* @inheritDoc
*/
public function last()
{
return last($this->collection);
}
/**
* @inheritDoc
*/
public function key()
{
return key($this->collection);
}
/**
* @inheritDoc
*/
public function next()
{
return next($this->collection);
}
/**
* @inheritDoc
*/
public function current()
{
return current($this->collection);
}
/**
* @inheritDoc
*/
public function exists(\Closure $p): bool
{
throw new NotImplementedException();
}
/**
* @inheritDoc
*/
public function filter(\Closure $p)
{
throw new NotImplementedException();
}
/**
* @inheritDoc
*/
public function forAll(\Closure $p)
{
throw new NotImplementedException();
}
/**
* @inheritDoc
*/
public function map(\Closure $p)
{
throw new NotImplementedException();
}
/**
* @inheritDoc
*/
public function partition(\Closure $p)
{
throw new NotImplementedException();
}
/**
* Returns the index of a specific element
* @param mixed $element
* @return int|string
* @inheritDoc
*/
public function indexOf($element)
{
$this->checkElementType($element);
return array_search($element, $this->collection);
}
/**
* @inheritDoc
*/
public function slice($offset, $length = null)
{
throw new NotImplementedException();
}
/**
* Gets a Iterator over this collection
* Gets a Iterator over this collection.
* @return \ArrayIterator
*/
public function getIterator()
{
return new \ArrayIterator($this->collection);
}
/**
* @inheritDoc
*/
public function offsetGet($key) {
return $this->get($key);
}
/**
* @inheritDoc
*/
public function offsetSet($key, $element) {
$this->set($key, $element);
$this->set($key, $element);
}
/**
* @inheritDoc
*/
public function offsetUnset($key) {
$this->remove($key);
}
/**
* @inheritDoc
*/
public function offsetExists($key) {
return isset($this->collection[$key]);
}
}
}