diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..da599f4
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 4
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..dfe0770
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..98da1c8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,110 @@
+# Created by https://www.toptal.com/developers/gitignore/api/macos,linux,symfony,windows,composer,visualstudiocode
+# Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,symfony,windows,composer,visualstudiocode
+
+### Composer ###
+composer.phar
+/vendor/
+
+# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
+# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
+composer.lock
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Symfony ###
+/var/
+
+# PHPUnit
+/phpunit.xml
+.phpunit.result.cache
+
+# Build data
+/build/
+
+# Composer PHAR
+/composer.phar
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/tasks.json
+!.vscode/launch.json
+*.code-workspace
+
+### VisualStudioCode Patch ###
+# Ignore all local history of files
+.history
+.ionide
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# dist files/folder
+*.dist
+# Not ignore
+!phpunit.xml.dist
+
+# End of https://www.toptal.com/developers/gitignore/api/macos,linux,symfony,windows,composer,visualstudiocode
diff --git a/.php_cs-fixer.php b/.php_cs-fixer.php
new file mode 100644
index 0000000..ac44c1e
--- /dev/null
+++ b/.php_cs-fixer.php
@@ -0,0 +1,110 @@
+setRiskyAllowed(true)
+ ->setRules([
+ '@DoctrineAnnotation' => true,
+ '@PHP70Migration' => true,
+ '@PHP71Migration' => true,
+ '@PSR2' => true,
+ '@Symfony' => true,
+ '@PHP73Migration' => true,
+ // Each line of multi-line DocComments must have an asterisk [PSR-5] and must be aligned with the first one.
+ 'align_multiline_comment' => true,
+ // Each element of an array must be indented exactly once.
+ 'array_indentation' => true,
+ // PHP arrays should be declared using the configured syntax.
+ 'array_syntax' => ['syntax' => 'short'],
+ // Binary operators should be surrounded by space as configured.
+ 'binary_operator_spaces' => ['default' => 'align_single_space_minimal'],
+ // An empty line feed must precede any configured statement.
+ 'blank_line_before_statement' => true,
+ // The body of each structure MUST be enclosed by braces. Braces should be properly placed. Body of braces should be properly indented.
+ 'braces' => ['position_after_anonymous_constructs' => 'next', 'position_after_control_structures' => 'next', 'position_after_functions_and_oop_constructs' => 'next'],
+ // Using `isset($var) &&` multiple times should be done in one call.
+ 'combine_consecutive_issets' => true,
+ // Calling `unset` on multiple items should be done in one call.
+ 'combine_consecutive_unsets' => true,
+ // Replace multiple nested calls of `dirname` by only one call with second `$level` parameter. Requires PHP >= 7.0.
+ 'combine_nested_dirname' => true,
+ // Remove extra spaces in a nullable typehint.
+ 'compact_nullable_typehint' => true,
+ // Equal sign in declare statement should be surrounded by spaces or not following configuration.
+ 'declare_equal_normalize' => ['space' => 'single'],
+ // Replaces `dirname(__FILE__)` expression with equivalent `__DIR__` constant.
+ 'dir_constant' => true,
+ // Replace deprecated `ereg` regular expression functions with `preg`.
+ 'ereg_to_preg' => true,
+ // Add curly braces to indirect variables to make them clear to understand. Requires PHP >= 7.0.
+ 'explicit_indirect_variable' => true,
+ // Converts implicit variables into explicit ones in double-quoted strings or heredoc syntax.
+ 'explicit_string_variable' => true,
+ // Replace core functions calls returning constants with the constants.
+ 'function_to_constant' => true,
+ // Convert `heredoc` to `nowdoc` where possible.
+ 'heredoc_to_nowdoc' => true,
+ // Ensure there is no code on the same line as the PHP open tag.
+ 'linebreak_after_opening_tag' => true,
+ // Method chaining MUST be properly indented. Method chaining with different levels of indentation is not supported.
+ 'method_chaining_indentation' => true,
+ // Replaces `intval`, `floatval`, `doubleval`, `strval` and `boolval` function calls with according type casting operator.
+ 'modernize_types_casting' => true,
+ // DocBlocks must start with two asterisks, multiline comments must start with a single asterisk, after the opening slash. Both must end with a single asterisk before the closing slash.
+ 'multiline_comment_opening_closing' => true,
+ // Forbid multi-line whitespace before the closing semicolon or move the semicolon to the new line for chained calls.
+ 'multiline_whitespace_before_semicolons' => ['strategy' => 'new_line_for_chained_calls'],
+ // Add leading `\` before function invocation to speed up resolving.
+ 'native_function_invocation' => true,
+ // Master functions shall be used instead of aliases.
+ 'no_alias_functions' => true,
+ // Replace control structure alternative syntax to use braces.
+ 'no_alternative_syntax' => true,
+ // Properties MUST not be explicitly initialized with `null` except when they have a type declaration (PHP 7.4).
+ 'no_null_property_initialization' => true,
+ // Variables must be set `null` instead of using `(unset)` casting.
+ 'no_unset_cast' => true,
+ // There should not be an empty `return` statement at the end of a function.
+ 'no_useless_return' => true,
+ // Logical NOT operators (`!`) should have leading and trailing whitespaces.
+ 'not_operator_with_space' => true,
+ // Adds or removes `?` before type declarations for parameters with a default `null` value.
+ 'nullable_type_declaration_for_default_null_value' => true,
+ // Orders the elements of classes/interfaces/traits.
+ 'ordered_class_elements' => true,
+ // Ordering `use` statements.
+ 'ordered_imports' => true,
+ // All PHPUnit test classes should be marked as internal.
+ 'php_unit_internal_class' => true,
+ // Enforce camel (or snake) case for PHPUnit test methods, following configuration.
+ 'php_unit_method_casing' => true,
+ // Adds a default `@coversNothing` annotation to PHPUnit test classes that have no `@covers*` annotation.
+ 'php_unit_test_class_requires_covers' => true,
+ // PHPDoc should contain `@param` for all params.
+ 'phpdoc_add_missing_param_annotation' => true,
+ // `@return void` and `@return null` annotations should be omitted from PHPDoc.
+ 'phpdoc_no_empty_return' => true,
+ // Annotations in PHPDoc should be ordered so that `@param` annotations come first, then `@throws` annotations, then `@return` annotations.
+ 'phpdoc_order' => true,
+ // `@var` and `@type` annotations must have type and name in the correct order.
+ 'phpdoc_var_annotation_correct_order' => true,
+ // Local, dynamic and directly referenced variables should not be assigned and directly returned by a function or method.
+ 'return_assignment' => true,
+ // Converts explicit variables in double-quoted strings and heredoc syntax from simple to complex format (`${` to `{$`).
+ 'simple_to_complex_string_variable' => true,
+ // Simplify `if` control structures that return the boolean result of their condition.
+ 'simplified_if_return' => true,
+ // A return statement wishing to return `void` should not return `null`.
+ 'simplified_null_return' => true,
+ ])
+ ->setFinder(
+ PhpCsFixer\Finder::create()
+ ->exclude('vendor')
+ ->in(__DIR__)
+ )
+;
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7c28b07
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2021, IDMarinas
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7c7011a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,28 @@
+# LoTGD Bunde Garden Party
+
+Party in the Garden of all cities.
+
+# Install
+```bash
+composer require lotgd-core/bundle-garden-party
+```
+
+# Default configuration
+```yaml
+# Note: party duration is 24 hours always.
+lotgd_garden_party:
+ # When does the part start. Valid format for DateTime object
+ start: null # Required, Example: '2015-01-20 use this format YYYY-MM-DD'
+ # How often is the party repeated? By default repeated every year
+ repeat: P1Y # Example: 'http://php.net/manual/en/dateinterval.construct.php for examples of format'
+ cake:
+ # Cost per level for cake
+ cost: 20
+ # How many slices of cake can a player buy in one day?
+ max: 3
+ drink:
+ # Cost per level for drink
+ cost: 20
+ # How many party drinks can a player buy in one day?
+ max: 3
+```
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..78f8eaf
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,71 @@
+{
+ "name": "lotgd-core/bundle-garden-party",
+ "description": "Party in the Garden of all cities.",
+ "version": "0.1.0",
+ "type": "lotgd-bundle",
+ "license": "BSD-3-Clause",
+ "authors": [
+ {
+ "name": "Iván Diaz Marinas (IDMarinas)",
+ "email": "35842929+idmarinas@users.noreply.github.com",
+ "homepage": "https://github.com/lotgd-core/bundle-garden-party",
+ "role": "Developer"
+ },
+ {
+ "name": "Eric Stevens",
+ "role": "Original Idea"
+ }
+ ],
+ "config": {
+ "optimize-autoloader": true,
+ "preferred-install": {
+ "*": "dist"
+ },
+ "process-timeout": 5000,
+ "sort-packages": true
+ },
+ "support": {
+ "issues": "https://github.com/lotgd-core/bundle-garden-party/issues"
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "require": {
+ "php": ">=7.3",
+ "idmarinas/lotgd-contracts": "^0.2.0",
+ "symfony/dependency-injection": "^4.4|^5.0",
+ "symfony/framework-bundle": "^4.4|^5.0",
+ "symfony/http-kernel": "^4.4|^5.0",
+ "symfony/yaml": "^4.4|^5.0"
+ },
+ "require-dev": {
+ "idmarinas/lotgd": "^6.0",
+ "matthiasnoback/symfony-config-test": "^4.2",
+ "matthiasnoback/symfony-dependency-injection-test": "^4.2",
+ "nyholm/symfony-bundle-test": "^1.7",
+ "phpunit/phpunit": "^8.5",
+ "symfony/phpunit-bridge": "^5.3",
+ "symfony/test-pack": "^1.0"
+ },
+ "conflict": {
+ "idmarinas/lotgd": "<6.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Lotgd\\Bundle\\GardenParty\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Lotgd\\Bundle\\GardenParty\\Tests\\": "tests/"
+ }
+ },
+ "scripts": {
+ "test": "phpunit"
+ },
+ "funding": [
+ {
+ "type": "paypal",
+ "url": "https://www.paypal.me/idmarinas"
+ }
+ ]
+}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..9eeba0b
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ./tests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ./src
+
+
+
diff --git a/src/Controller/GardenPartyController.php b/src/Controller/GardenPartyController.php
new file mode 100644
index 0000000..695df78
--- /dev/null
+++ b/src/Controller/GardenPartyController.php
@@ -0,0 +1,164 @@
+parameter = $parameter;
+ $this->translator = $translator;
+ $this->buffer = $buffer;
+ $this->settings = $settings;
+ $this->commentary = $commentary;
+ $this->response = $response;
+ $this->navigation = $navigation;
+ }
+
+ public function cake(): Response
+ {
+ global $session;
+
+ //- See if the party is currently running.
+ if ( ! $this->checkPartyRunning())
+ {
+ return $this->redirect('gardens.php');
+ }
+
+ $cakeToday = get_module_pref('cake_today', 'bundle_garden_party');
+ $cost = $this->parameter->get('lotgd_bundle.garden_party.cake.cost') * $session['user']['level'];
+
+ $cake = $this->translator->trans('consumption.cake.name', [], self::TRANSLATION_DOMAIN);
+
+ if ($session['user']['gold'] >= $cost)
+ {
+ $session['user']['gold'] -= $cost;
+
+ set_module_pref('cake_today', $cakeToday + 1, 'bundle_garden_party');
+
+ $this->buyedType($cake, 'cake');
+
+ return redirect('gardens.php');
+ }
+
+ return $this->render('@LotgdGardenParty/cake.html.twig', $this->notHaveGold($cake));
+ }
+
+ public function drink(): Response
+ {
+ global $session;
+
+ //- See if the party is currently running.
+ if ( ! $this->checkPartyRunning())
+ {
+ return $this->redirect('gardens.php');
+ }
+
+ $drinksToday = get_module_pref('drinks_today', 'bundle_garden_party');
+ $cost = $this->parameter->get('lotgd_bundle.garden_party.drink.cost') * $session['user']['level'];
+
+ $drink = $this->translator->trans('consumption.drink.name', [], self::TRANSLATION_DOMAIN);
+
+ if ($session['user']['gold'] >= $cost)
+ {
+ $session['user']['gold'] -= $cost;
+
+ set_module_pref('drinks_today', $drinksToday + 1, 'bundle_garden_party');
+
+ $this->buyedType($drink, 'drink');
+
+ return redirect('gardens.php');
+ }
+
+ return $this->render('@LotgdGardenParty/drink.html.twig', $this->notHaveGold($drink));
+ }
+
+ private function buyedType(string $name, string $type): void
+ {
+ $this->commentary->saveComment([
+ 'section' => 'gardens',
+ 'comment' => ': '.$this->translator->trans("consumption.{$type}.mote", [], self::TRANSLATION_DOMAIN),
+ ]);
+
+ $buff = [
+ 'name' => $name,
+ 'atkmod' => 1.05,
+ 'roundmsg' => $this->translator->trans("buff.msg.{$type}", ['name' => $name], self::TRANSLATION_DOMAIN),
+ 'rounds' => 20,
+ 'schema' => self::TRANSLATION_DOMAIN,
+ ];
+
+ $this->buffer->applyBuff("bundle_garden_party.{$type}", $buff);
+
+ $buff = [
+ 'name' => $this->translator->trans('buff.name.miss', [], self::TRANSLATION_DOMAIN),
+ 'minioncount' => 1,
+ 'maxbadguydamage' => 0,
+ 'minbadguydamage' => 0,
+ 'effectnodmgmsg' => $this->translator->trans('buff.msg.miss', [], self::TRANSLATION_DOMAIN),
+ 'rounds' => -1,
+ 'schema' => self::TRANSLATION_DOMAIN,
+ ];
+
+ $this->buffer->applyBuff('bundle_garden_party', $buff);
+ }
+
+ private function notHaveGold(string $name): array
+ {
+ $this->response->pageTitle('title', [
+ 'barman' => $this->settings->getSetting('barkeep', '`tCedrik`0'),
+ 'clothes' => $this->translator->trans('section.hook.gardens.party.barman.clothes', [], self::TRANSLATION_DOMAIN),
+ ], self::TRANSLATION_DOMAIN);
+
+ $this->navigation->addNav('navigation.nav.return', 'gardens.php', ['textDomain' => self::TRANSLATION_DOMAIN]);
+
+ return [
+ 'translation_domain' => self::TRANSLATION_DOMAIN,
+ 'buy_type' => $name,
+ 'barman' => $this->settings->getSetting('barkeep', '`tCedrik`0'),
+ 'party_type' => $this->translator->trans('party.type', [], self::TRANSLATION_DOMAIN),
+ ];
+ }
+}
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
new file mode 100644
index 0000000..e221a5e
--- /dev/null
+++ b/src/DependencyInjection/Configuration.php
@@ -0,0 +1,110 @@
+getRootNode()
+ ->addDefaultsIfNotSet()
+ ->info('Note: party duration is 24 hours always.')
+ ->children()
+ ->scalarNode('start')
+ ->defaultValue(null)
+ ->isRequired()
+ ->cannotBeEmpty()
+ ->validate()
+ ->ifTrue(function ($value)
+ {
+ try
+ {
+ (new DateTime($value));
+ }
+ catch (\Throwable $th)
+ {
+ return true;
+ }
+
+ return false;
+ })
+ ->thenInvalid('%s not is a valid date for DateTime object.')
+ ->end()
+ ->info('When does the part start. Valid format for DateTime object')
+ ->example('2015-01-20 use this format YYYY-MM-DD')
+ ->end()
+ ->scalarNode('repeat')
+ ->defaultValue('P1Y')
+ ->info('How often is the party repeated? By default repeated every year')
+ ->example('http://php.net/manual/en/dateinterval.construct.php for examples of format')
+ ->validate()
+ ->ifTrue(function ($value)
+ {
+ try
+ {
+ (new DateInterval($value));
+ }
+ catch (\Throwable $th)
+ {
+ return true;
+ }
+
+ return false;
+ })
+ ->thenInvalid('%s cannot be parsed as an interval.')
+ ->end()
+ ->end()
+ ->arrayNode('cake')
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->integerNode('cost')
+ ->defaultValue(20)
+ ->min(0)
+ ->info('Cost per level for cake')
+ ->end()
+ ->integerNode('max')
+ ->defaultValue(3)
+ ->min(1)
+ ->info('How many slices of cake can a player buy in one day?')
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('drink')
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->integerNode('cost')
+ ->defaultValue(20)
+ ->min(0)
+ ->info('Cost per level for drink')
+ ->end()
+ ->integerNode('max')
+ ->defaultValue(3)
+ ->min(1)
+ ->info('How many party drinks can a player buy in one day?')
+ ->end()
+ ->end()
+ ->end()
+ ->end()
+ ;
+
+ return $treeBuilder;
+ }
+}
diff --git a/src/DependencyInjection/LotgdGardenPartyExtension.php b/src/DependencyInjection/LotgdGardenPartyExtension.php
new file mode 100644
index 0000000..2dc3f37
--- /dev/null
+++ b/src/DependencyInjection/LotgdGardenPartyExtension.php
@@ -0,0 +1,38 @@
+load('services.php');
+
+ $container->setParameter('lotgd_bundle.garden_party.start', $mergedConfig['start']);
+ $container->setParameter('lotgd_bundle.garden_party.repeat', $mergedConfig['repeat']);
+
+ $container->setParameter('lotgd_bundle.garden_party.cake.cost', $mergedConfig['cake']['cost']);
+ $container->setParameter('lotgd_bundle.garden_party.cake.max', $mergedConfig['cake']['max']);
+
+ $container->setParameter('lotgd_bundle.garden_party.drink.cost', $mergedConfig['drink']['cost']);
+ $container->setParameter('lotgd_bundle.garden_party.drink.max', $mergedConfig['drink']['max']);
+ }
+}
diff --git a/src/EventSubscriber/GardenPartySubscriber.php b/src/EventSubscriber/GardenPartySubscriber.php
new file mode 100644
index 0000000..c0812d9
--- /dev/null
+++ b/src/EventSubscriber/GardenPartySubscriber.php
@@ -0,0 +1,112 @@
+parameter = $parameter;
+ $this->setting = $setting;
+ $this->navigation = $navigation;
+ $this->translator = $translator;
+ }
+
+ public function newday(): void
+ {
+ set_module_pref('cake_today', 0, 'bundle_garden_party');
+ set_module_pref('drinks_today', 0, 'bundle_garden_party');
+ }
+
+ public function garden(GenericEvent $event): void
+ {
+ global $session;
+
+ //-- See if the party is currently running.
+ if ( ! $this->checkPartyRunning())
+ {
+ return;
+ }
+
+ $this->navigation->setTextDomain(self::TRANSLATION_DOMAIN);
+
+ $params = [
+ 'translation_domain' => self::TRANSLATION_DOMAIN,
+ 'barman' => $this->setting->getSetting('barkeep', '`tCedrik`0'),
+ ];
+
+ $args['includeTemplatesPost']['@LotgdGardenParty/garden_party_info.html.twig'] = $params;
+
+ $this->navigation->addHeader('navigation.category.party');
+
+ $cakeToday = get_module_pref('cake_today', 'bundle_garden_party');
+ $drinkstoday = get_module_pref('drinks_today', 'bundle_garden_party');
+ $cakeCost = $this->parameter->get('lotgd_bundle.garden_party.cake.cost') * $session['user']['level'];
+ $drinkCost = $this->parameter->get('lotgd_bundle.garden_party.drink.cost') * $session['user']['level'];
+
+ if ($cakeToday < $this->parameter->get('lotgd_bundle.garden_party.cake.max') && $session['user']['gold'] >= $cakeCost)
+ {
+ $cake = $this->translator->trans('consumption.cake.name', [], self::TRANSLATION_DOMAIN);
+ $this->navigation->addNav('navigation.nav.consumption', $this->getModuleUrl('cake'), [
+ 'params' => ['name' => $cake, 'cost' => $cakeCost],
+ ]);
+ }
+
+ if ($drinkstoday < $this->parameter->get('lotgd_bundle.garden_party.drink.max') && $session['user']['gold'] >= $drinkCost)
+ {
+ $drink = $this->translator->trans('consumption.drink.name', [], self::TRANSLATION_DOMAIN);
+ $this->navigation->addNav('navigation.nav.consumption', $this->getModuleUrl('drink'), [
+ 'params' => ['name' => $drink, 'cost' => $drinkCost],
+ ]);
+ }
+
+ $this->navigation->setTextDomain();
+ }
+
+ /**
+ * @return array
+ */
+ public static function getSubscribedEvents(): array
+ {
+ return [
+ Core::NEWDAY => 'newday',
+ Events::PAGE_GARDEN_POST => 'garden',
+ ];
+ }
+}
diff --git a/src/LotgdGardenPartyBundle.php b/src/LotgdGardenPartyBundle.php
new file mode 100644
index 0000000..4e7a948
--- /dev/null
+++ b/src/LotgdGardenPartyBundle.php
@@ -0,0 +1,51 @@
+ 'What will display in the conversation when you order drink?|takes a big swig of Grape Soda.',
+
+ /**
+ * @inheritDoc
+ */
+ public function getLotgdVersion(): string
+ {
+ return '0.1.0';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLotgdIcon(): ?string
+ {
+ return 'party';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLotgdDescription(): string
+ {
+ return 'Party in the Garden of all cities.';
+ }
+}
diff --git a/src/Pattern/CheckPartyRunningTrait.php b/src/Pattern/CheckPartyRunningTrait.php
new file mode 100644
index 0000000..26b36f6
--- /dev/null
+++ b/src/Pattern/CheckPartyRunningTrait.php
@@ -0,0 +1,35 @@
+parameter->get('lotgd_bundle.garden_party.repeat'));
+ $start = new \DateTime($this->parameter->get('lotgd_bundle.garden_party.start'));
+ $end = new \DateTime('now');
+
+ $period = new \DatePeriod($start, $interval, $end);
+
+ $periodArray = iterator_to_array($period);
+ $lastPeriod = end($periodArray);
+
+ return (bool) (strtotime($lastPeriod->format('Y-m-d')) == strtotime($end->format('Y-m-d')));
+ }
+
+}
diff --git a/src/Pattern/ModuleUrlTrait.php b/src/Pattern/ModuleUrlTrait.php
new file mode 100644
index 0000000..34030a0
--- /dev/null
+++ b/src/Pattern/ModuleUrlTrait.php
@@ -0,0 +1,24 @@
+services()
+ //-- Controllers
+ ->set(GardenPartyController::class)
+ ->autoconfigure()
+ ->args([
+ new ReferenceConfigurator('parameter_bag'),
+ new ReferenceConfigurator('translator'),
+ new ReferenceConfigurator(Buffer::class),
+ new ReferenceConfigurator('lotgd_core.settings'),
+ new ReferenceConfigurator(Commentary::class),
+ new ReferenceConfigurator(Response::class),
+ new ReferenceConfigurator(Navigation::class)
+ ])
+ ->call('setContainer', [
+ new ReferenceConfigurator('service_container')
+ ])
+ ->tag('controller.service_arguments')
+
+ //-- Event Subscribers
+ ->set(GardenPartySubscriber::class)
+ ->args([
+ new ReferenceConfigurator('parameter_bag'),
+ new ReferenceConfigurator('lotgd_core.settings'),
+ new ReferenceConfigurator(Navigation::class),
+ new ReferenceConfigurator('translator')
+ ])
+ ->tag('kernel.event_subscriber')
+ ;
+};
diff --git a/src/Resources/translations/bundle_garden_party+intl-icu.en.yaml b/src/Resources/translations/bundle_garden_party+intl-icu.en.yaml
new file mode 100644
index 0000000..24a7df8
--- /dev/null
+++ b/src/Resources/translations/bundle_garden_party+intl-icu.en.yaml
@@ -0,0 +1,39 @@
+title: '{barman} in {clothes}'
+
+section:
+ gardens:
+ time: "There's a party going on! It's a {name}!"
+ inn: '{barman} is here, wearing (of all things) {clothes}, serving food and drinks.`n`n'
+ run:
+ paragraph: >
+ You wander over to where {barman} is standing in the gardens, and ask to buy {buy_type}, but he tells you that you don't have enough gold to buy it.
+ You think it's a little odd that you're being charged for food and drinks at {party_type}, but ignore this, eager to get back to the revelry.
+
+party:
+ type: birthday party for MightyE
+ barman.clothes: a Hawaiian shirt
+ miss:
+ item: 'a bogus item'
+ comment: 'mutters something that you cannot make out.'
+
+consumption:
+ cake:
+ name: Birthday Cake
+ mote: pigs out and takes a huge bite of Birthday Cake.
+ drink:
+ name: Grape Soda
+ mote: takes a big swig of Grape Soda.
+
+buff:
+ name.miss: 'Party Fever'
+ msg:
+ cake: '`b{name}´b increased your defense.'
+ drink: '`b{name}´b increased your attack.'
+ miss: 'In the distance you hear the sounds of "Happy Birthday" being sung.'
+
+navigation:
+ category:
+ party: 'Party Treats!'
+ nav:
+ consumption: '{name} (`^{cost,number} gold`0)'
+ return: 'Back to the party'
diff --git a/src/Resources/views/cake.html.twig b/src/Resources/views/cake.html.twig
new file mode 100644
index 0000000..7efbabc
--- /dev/null
+++ b/src/Resources/views/cake.html.twig
@@ -0,0 +1,3 @@
+{% trans_default_domain translation_domain %}
+
+{{ 'section.run.paragraph'|trans(_context)|colorize }}
diff --git a/src/Resources/views/drink.html.twig b/src/Resources/views/drink.html.twig
new file mode 100644
index 0000000..7efbabc
--- /dev/null
+++ b/src/Resources/views/drink.html.twig
@@ -0,0 +1,3 @@
+{% trans_default_domain translation_domain %}
+
+{{ 'section.run.paragraph'|trans(_context)|colorize }}
diff --git a/src/Resources/views/garden_party_info.html.twig b/src/Resources/views/garden_party_info.html.twig
new file mode 100644
index 0000000..e8df447
--- /dev/null
+++ b/src/Resources/views/garden_party_info.html.twig
@@ -0,0 +1,4 @@
+{% trans_default_domain translation_domain %}
+
+{{ 'section.gardens.time'|trans({'name': 'party.type'|trans })|colorize }}
+{{ 'section.gardens.inn'|trans({'clothes': 'party.barman.clothes'|trans })|colorize }}