+ {% for ship_queue in dock2ShipQueues %}
+ {% set realSizeQueue = realSizeQueue + 1 %}
+ {% set totalTimeShips = ship_queue|ship_queue_time %}
+ {% set remainingTime = ship_queue|queue_duration %}
+
+ {% if realSizeQueue > 1 %}
+
+
+ {% set realSizeQueue = 0 %}
+ {% set remainingTotalTime = 0 %}
+ {% set totalTimeTechno = 0 %}
+
+
+ {% endif %}
+
+ {% if orbitalBase.levelSpatioport > 0 %}
+ {% set percent = commercialRoutesData.total|percent(commercialRoutesData.max) %}
+
+ {% endif %}
+
+
diff --git a/templates/components/Organisms/Fleet/CommanderSquadrons.html.twig b/templates/components/Organisms/Fleet/CommanderSquadrons.html.twig
new file mode 100644
index 000000000..5161c82bd
--- /dev/null
+++ b/templates/components/Organisms/Fleet/CommanderSquadrons.html.twig
@@ -0,0 +1,29 @@
+{% set line_coord = commander.formatLineCoord %}
+
+
diff --git a/templates/components/Organisms/Fleet/FleetSpyReport.html.twig b/templates/components/Organisms/Fleet/FleetSpyReport.html.twig
new file mode 100644
index 000000000..185ef0660
--- /dev/null
+++ b/templates/components/Organisms/Fleet/FleetSpyReport.html.twig
@@ -0,0 +1,55 @@
+{% set affected = constant('App\\Modules\\Ares\\Model\\Commander::AFFECTED') %}
+
+
diff --git a/templates/components/Organisms/Fleet/ShipTransfer.html.twig b/templates/components/Organisms/Fleet/ShipTransfer.html.twig
new file mode 100644
index 000000000..9a82b4b53
--- /dev/null
+++ b/templates/components/Organisms/Fleet/ShipTransfer.html.twig
@@ -0,0 +1,20 @@
+
diff --git a/templates/components/Organisms/Layout/BaseSelector.html.twig b/templates/components/Organisms/Layout/BaseSelector.html.twig
new file mode 100644
index 000000000..1f8745b41
--- /dev/null
+++ b/templates/components/Organisms/Layout/BaseSelector.html.twig
@@ -0,0 +1,32 @@
+
diff --git a/templates/components/Organisms/Layout/ContentContainer.html.twig b/templates/components/Organisms/Layout/ContentContainer.html.twig
new file mode 100644
index 000000000..e0e66fb78
--- /dev/null
+++ b/templates/components/Organisms/Layout/ContentContainer.html.twig
@@ -0,0 +1,6 @@
+
diff --git a/templates/components/Organisms/Layout/ResourceStorage.html.twig b/templates/components/Organisms/Layout/ResourceStorage.html.twig
new file mode 100644
index 000000000..2a4ba2317
--- /dev/null
+++ b/templates/components/Organisms/Layout/ResourceStorage.html.twig
@@ -0,0 +1,31 @@
+
diff --git a/templates/components/Organisms/Layout/Sidebar.html.twig b/templates/components/Organisms/Layout/Sidebar.html.twig
new file mode 100644
index 000000000..10abe4f28
--- /dev/null
+++ b/templates/components/Organisms/Layout/Sidebar.html.twig
@@ -0,0 +1,5 @@
+
diff --git a/templates/components/Organisms/Map/Loader.html.twig b/templates/components/Organisms/Map/Loader.html.twig
new file mode 100644
index 000000000..022e5eacf
--- /dev/null
+++ b/templates/components/Organisms/Map/Loader.html.twig
@@ -0,0 +1,38 @@
+
diff --git a/templates/components/Organisms/Map/PlaceActionPanel.html.twig b/templates/components/Organisms/Map/PlaceActionPanel.html.twig
new file mode 100644
index 000000000..5ae03c3a8
--- /dev/null
+++ b/templates/components/Organisms/Map/PlaceActionPanel.html.twig
@@ -0,0 +1,5 @@
+
diff --git a/templates/components/Organisms/Map/PlaceSummary.html.twig b/templates/components/Organisms/Map/PlaceSummary.html.twig
new file mode 100644
index 000000000..42489e7d0
--- /dev/null
+++ b/templates/components/Organisms/Map/PlaceSummary.html.twig
@@ -0,0 +1,109 @@
+{% set playerFactionIdentifier = place.player.faction.identifier ?? 0 %}
+
+
diff --git a/templates/components/Organisms/Player/Character.html.twig b/templates/components/Organisms/Player/Character.html.twig
new file mode 100644
index 000000000..7813b6a15
--- /dev/null
+++ b/templates/components/Organisms/Player/Character.html.twig
@@ -0,0 +1,28 @@
+
+
diff --git a/templates/components/Organisms/Portal/Header.html.twig b/templates/components/Organisms/Portal/Header.html.twig
new file mode 100644
index 000000000..3395c7b39
--- /dev/null
+++ b/templates/components/Organisms/Portal/Header.html.twig
@@ -0,0 +1,69 @@
+
diff --git a/templates/components/Organisms/Queues.html.twig b/templates/components/Organisms/Queues.html.twig
new file mode 100644
index 000000000..a3b16c53f
--- /dev/null
+++ b/templates/components/Organisms/Queues.html.twig
@@ -0,0 +1,25 @@
+
diff --git a/templates/components/Organisms/Trade/CommanderSalesForm.html.twig b/templates/components/Organisms/Trade/CommanderSalesForm.html.twig
new file mode 100644
index 000000000..7082325b3
--- /dev/null
+++ b/templates/components/Organisms/Trade/CommanderSalesForm.html.twig
@@ -0,0 +1,60 @@
+
diff --git a/templates/components/Organisms/Trade/ResourceSalesForm.html.twig b/templates/components/Organisms/Trade/ResourceSalesForm.html.twig
new file mode 100644
index 000000000..b30dd0ed7
--- /dev/null
+++ b/templates/components/Organisms/Trade/ResourceSalesForm.html.twig
@@ -0,0 +1,61 @@
+
diff --git a/templates/components/Organisms/Trade/ShipsSalesForm.html.twig b/templates/components/Organisms/Trade/ShipsSalesForm.html.twig
new file mode 100644
index 000000000..65fb1012f
--- /dev/null
+++ b/templates/components/Organisms/Trade/ShipsSalesForm.html.twig
@@ -0,0 +1,59 @@
+
diff --git a/templates/components/base/fast_view.html.twig b/templates/components/base/fast_view.html.twig
new file mode 100644
index 000000000..ee9870045
--- /dev/null
+++ b/templates/components/base/fast_view.html.twig
@@ -0,0 +1,231 @@
+
+ {% if fast_view_profile %}
+
+
+
{{ orbital_base.name }}
+
+ {{ orbital_base|base_type }} — {{ orbital_base.points}} points
+
+
+ {% else %}
+
+
Vue de situation
+
+ {% endif %}
+
+
+ {# pending commercial routes display #}
+ {% if orbital_base.levelSpatioport > 0 and commercial_routes_data.waiting_for_me is defined and commercial_routes_data.waiting_for_me != 0 %}
+
+ {% set plural = commercial_routes_data.waiting_for_me|plural %}
+ Vous avez {{ commercial_routes_data.waiting_for_me }} proposition{{ plural }} commerciale{{ plural }}
+
+ {% endif %}
+
+ {# resources display #}
+
+
Ressources en stock
+
+ {{ orbital_base.resourcesStorage|number }}
+
+
+
+
+
+
+
+ →
+
+
+
+
Générateur
+
+ {% set nextTime = 0 %}
+ {% set nextTotalTime = 0 %}
+ {% set realSizeQueue = 0 %}
+
+
+ {% for building_queue in building_queues %}
+ {% set realSizeQueue = realSizeQueue + 1 %}
+ {% set nextTime = building_queue|queue_duration %}
+ {% set nextTotalTime = nextTotalTime + building_queue|building_queue_time %}
+
+
+
×
+
+
+ {{ building_queue|queue_name }}
+ niv. {{ building_queue.targetLevel }}
+
+
+ {{ nextTime|lite_seconds }}
+
+ {% if realSizeQueue > 1 %}
+
+ {% else %}
+
+
+
+ {% endif %}
+
+ {% endfor %}
+ {% if orbital_base|base_max_building_queues > building_queues|length %}
+
+ Construire un bâtiment
+
+ {% endif %}
+
+
+ {% if orbital_base.levelDock1 > 0 %}
+
Chantier Alpha
+
+ {% set realSizeQueue = 0 %}
+
+
+ {% for ship_queue in dock1_ship_queues %}
+ {% set realSizeQueue = realSizeQueue + 1 %}
+ {% set totalTimeShips = ship_queue|ship_queue_time %}
+ {% set remainingTime = ship_queue|queue_duration %}
+
+ {% if realSizeQueue > 1 %}
+
+ {% else %}
+
+ {% endif %}
+
+
+
+ {{ ship_queue.quantity }} {{ ship_queue|ship_queue_name }}{{ ship_queue.quantity|plural }}
+
+
+ {% if realSizeQueue > 1 %}
+
+ {% else %}
+
+ {{ remainingTime|lite_seconds }}
+
+
+
+
+
+ {% endif %}
+
+ {% endfor %}
+ {% if orbital_base|base_max_dock1_ship_queues > dock1_ship_queues|length %}
+
+ Lancer la production
+
+ {% endif %}
+
+ {% endif %}
+
+ {% if orbital_base.levelDock2 > 0 %}
+
Chantier de Ligne
+
+ {% set realSizeQueue = 0 %}
+
+
+ {% for ship_queue in dock2_ship_queues %}
+ {% set realSizeQueue = realSizeQueue + 1 %}
+ {% set totalTimeShips = ship_queue|ship_queue_time %}
+ {% set remainingTime = ship_queue|queue_duration %}
+
+ {% if realSizeQueue > 1 %}
+
+ {% else %}
+
+ {% endif %}
+
+
+
+ {{ ship_queue.quantity }} {{ ship_queue|ship_queue_name }}{{ ship_queue.quantity|plural }}
+
+
+ {% if realSizeQueue > 1 %}
+
+ {% else %}
+
+ {{ remainingTime|lite_seconds }}
+
+
+
+
+
+ {% endif %}
+
+ {% endfor %}
+ {% if orbital_base|base_max_dock2_ship_queues > dock2_ship_queues|length %}
+
+ Lancer la production
+
+ {% endif %}
+
+ {% endif %}
+
+ {% if orbital_base.levelTechnosphere > 0 %}
+
Technosphère
+
+ {% set realSizeQueue = 0 %}
+ {% set remainingTotalTime = 0 %}
+ {% set totalTimeTechno = 0 %}
+
+
+ {% for technology_queue in technology_queues %}
+ {% set realSizeQueue = realSizeQueue + 1 %}
+ {% set totalTimeTechno = totalTimeTechno + technology_queue|technology_queue_time %}
+ {% set remainingTotalTime = technology_queue|queue_duration %}
+
+
+
+
+ {{ get_technology_info(technology_queue.technology, 'name') }}
+ {% if technology_queue|is_unblocking_technology %}
+ niv. {{ technology_queue.targetLevel }}
+ {% endif %}
+
+
+
+
+ {{ remainingTotalTime|lite_seconds }}
+
+
+ {% if realSizeQueue > 1 %}
+
+ {% else %}
+
+
+
+
+ {% endif %}
+
+ {% endfor %}
+ {% if orbital_base|base_max_technology_queues > technology_queues|length %}
+
+ Développer une technologie
+
+ {% endif %}
+
+ {% endif %}
+
+ {% if orbital_base.levelSpatioport > 0 %}
+
Spatioport
+
+
+
Routes commerciales
+
+ {{ commercial_routes_data.total }} / {{ commercial_routes_data.max }}
+
+
+ {% set percent = commercial_routes_data.total|percent(commercial_routes_data.max) %}
+
+
+
+
+
+ →
+
+
+ {% endif %}
+
+
+
diff --git a/templates/components/base/leave_base.html.twig b/templates/components/base/leave_base.html.twig
new file mode 100644
index 000000000..3d2182088
--- /dev/null
+++ b/templates/components/base/leave_base.html.twig
@@ -0,0 +1,28 @@
+{% set are_commanders_in_mission = commanders|reduce((carry, c) => carry or c.isMoving, false) %}
+{% set cooldown = get_cooldown_before_leaving_base() %}
+
+
+
+
+ Abandonner cette planète
+
+
+
+
+ Pour abandonner une planète, aucun de vos commandants ne doit être en mission. De plus, vous devez posséder cette planète depuis plus de {{ cooldown }} relèves.
+ Une planète abandonnée peut être conquise par n\'importe qui. Les commandants en orbite autour de celle-ci continuent à la défendre. Les vaisseaux dans le hangar sont conservés. Cependant, les routes commerciales ainsi que les offres de ventes sont supprimées. De plus, toutes les constructions programmées sont annulées.
+
+
+
+ {% if are_commanders_in_mission %}
+ Action impossible
+ Certains de vos commandants sont en mission.
+ {% elseif not can_leave_orbital_base(current_base) %}
+ Action impossible
+ Vous possédez la planète depuis moins de {{ cooldown }} relèves.
+ Il reste {{ get_time_until_cooldown_end(current_base)|large_seconds }} avant que vous puissiez abandonner la planète.
+ {% else %}
+ Abandonner la planète
+ {% endif %}
+
+
diff --git a/templates/components/base/situation.html.twig b/templates/components/base/situation.html.twig
new file mode 100644
index 000000000..5115b4336
--- /dev/null
+++ b/templates/components/base/situation.html.twig
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+ {{ current_base|base_type }}
+ {{ current_base.name }}
+ {{ current_base.points|number}} points
+
+
+ coordonnées
+ {{ current_base|base_coords }}
+
+
+
+
+ {{ (current_base.place.population * 1000000)|number }} habitants
+ {{ current_base.place.coefResources }} % coeff. ressource
+ {{ science_coeff }} % de bonus scientifique
+
+
+
+
+ I
+
+
+ II
+
+
+ {% set current_vanguard_position = 0 %}
+ {% set current_rear_position = 0 %}
+
+ {% for commander in commanders %}
+
+
+ {% if commander.line == 1 %}
+ {% set current_vanguard_position = current_vanguard_position + 1 %}
+ {% else %}
+ {% set current_rear_position = current_rear_position + 1 %}
+ {% endif %}
+ {% endfor %}
+
+ {% if current_vanguard_position < vanguard_positions_count %}
+ {% for position in range(current_vanguard_position, vanguard_positions_count - 1) %}
+
+ {# #}
+
+ Affecter un officier
+
+
+ {% endfor %}
+ {% endif %}
+
+ {% if current_rear_position < rear_positions_count %}
+ {% for position in range(current_rear_position, rear_positions_count - 1) %}
+
+ {# #}
+
+ Affecter un officier
+
+
+ {% endfor %}
+ {% endif %}
+
+
+
+
diff --git a/templates/components/base/trade/commercial_shippings.html.twig b/templates/components/base/trade/commercial_shippings.html.twig
new file mode 100644
index 000000000..1308efc99
--- /dev/null
+++ b/templates/components/base/trade/commercial_shippings.html.twig
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ Convoi en route
+
+
+ {% for commercial_shipping in commercial_shippings['outgoing'][constant('App\\Modules\\Athena\\Model\\CommercialShipping::ST_GOING')] %}
+ {{ include('molecules/base/trade/commercial_shipping.html.twig', { commercial_shipping }) }}
+ {% endfor %}
+
+
+
+
+ Retour de convoi
+
+
+ {% for commercial_shipping in commercial_shippings['outgoing'][constant('App\\Modules\\Athena\\Model\\CommercialShipping::ST_MOVING_BACK')] %}
+ {{ include('molecules/base/trade/commercial_shipping.html.twig', { commercial_shipping }) }}
+ {% endfor %}
+
+
+
+
+ Convoi à quai
+
+
+ {% for commercial_shipping in commercial_shippings['outgoing'][constant('App\\Modules\\Athena\\Model\\CommercialShipping::ST_WAITING')] %}
+ {{ include('molecules/base/trade/commercial_shipping.html.twig', { commercial_shipping }) }}
+ {% endfor %}
+
+
+
+
diff --git a/templates/components/base/trade/market.html.twig b/templates/components/base/trade/market.html.twig
new file mode 100644
index 000000000..20957accd
--- /dev/null
+++ b/templates/components/base/trade/market.html.twig
@@ -0,0 +1,100 @@
+{% set has_incoming = commercial_shippings['incoming'][constant('App\\Modules\\Athena\\Model\\CommercialShipping::ST_GOING')]|length > 0 %}
+
+
+ {% if has_incoming %}
+
+
+
+
+ Convoi en approche
+ {% for commercial_shipping in commercial_shippings['incoming'][constant('App\\Modules\\Athena\\Model\\CommercialShipping::ST_GOING')] %}
+ {{ include('molecules/base/trade/commercial_shipping.html.twig', { commercial_shipping }) }}
+ {% endfor %}
+
+
+ {% endif %}
+
+
+
+ {# #}
+ Ressources
+ cours actuel | 1:{{ resources_current_rate|number(3) }}
+
+
+
+ {##}
+
+
+ {% for transaction in resource_transactions %}
+ {% if current_player.id != transaction.player.id %}
+ {{ include('components/base/trade/transaction.html.twig', { transaction, 'current_rate': resources_current_rate }) }}
+ {% endif %}
+ {% endfor %}
+
+
+
+
+
+
+
+ {# #}
+ Commandants
+ cours actuel | 1:{{ commander_current_rate|number(3) }}
+
+
+
+ {##}
+
+
+ {% for transaction in commander_transactions %}
+ {% if current_player.id != transaction.player.id %}
+ {{ include('components/base/trade/transaction.html.twig', { transaction, 'current_rate': commander_current_rate }) }}
+ {% endif %}
+ {% endfor %}
+
+
+
+
+
+
+
+ {# #}
+ Vaisseaux
+ cours actuel | 1:{{ ship_current_rate|number(3) }}
+
+
+
+
+ {#
#}
+
+
+ {% for transaction in ship_transactions %}
+ {% if current_player.id != transaction.player.id %}
+ {{ include('components/base/trade/transaction.html.twig', { transaction, 'current_rate': ship_current_rate }) }}
+ {% endif %}
+ {% endfor %}
+
+
+
+
+
diff --git a/templates/components/base/trade/resource.html.twig b/templates/components/base/trade/resource.html.twig
new file mode 100644
index 000000000..02b82024b
--- /dev/null
+++ b/templates/components/base/trade/resource.html.twig
@@ -0,0 +1,117 @@
+
+
+
+ {# #}
+ Envoi de ressources
+ à une autre base
+
+
+
+
+
+
+
+ {# #}
+ Envoi de vaisseaux
+ à une autre base
+
+
+
+
+
diff --git a/templates/components/base/trade/sell.html.twig b/templates/components/base/trade/sell.html.twig
new file mode 100644
index 000000000..de449132b
--- /dev/null
+++ b/templates/components/base/trade/sell.html.twig
@@ -0,0 +1,22 @@
+
diff --git a/templates/components/base/trade/transaction.html.twig b/templates/components/base/trade/transaction.html.twig
new file mode 100644
index 000000000..672f57399
--- /dev/null
+++ b/templates/components/base/trade/transaction.html.twig
@@ -0,0 +1,110 @@
+{% set transaction_data = get_transaction_data(transaction, current_base, current_rate) %}
+
+
+
+
+
+
+
+
+ Voir le détail du prix
+
+
+
+ {{ transaction.price|number }}
+
+
+
+ +
+ taxe ({{ transaction_data.export_tax }}%)
+ {{ transaction_data.export_price|number }}
+
+
+
+ +
+ taxe ({{ transaction_data.import_tax }}%)
+ {{ transaction_data.import_price|number }}
+
+
+
+ {{ transaction_data.total_price|number }}
+
+
+
+
+
+
+
+
diff --git a/templates/components/base/trade/turbo/broadcast/new_transaction.stream.html.twig b/templates/components/base/trade/turbo/broadcast/new_transaction.stream.html.twig
new file mode 100644
index 000000000..42c12d4be
--- /dev/null
+++ b/templates/components/base/trade/turbo/broadcast/new_transaction.stream.html.twig
@@ -0,0 +1,5 @@
+
+
+ {{ include('components/base/trade/transaction.html.twig', { transaction, 'current_rate': transaction.currentRate }) }}
+
+
diff --git a/templates/components/base/trade/turbo/broadcast/remove_transaction.stream.html.twig b/templates/components/base/trade/turbo/broadcast/remove_transaction.stream.html.twig
new file mode 100644
index 000000000..5a8ad28ac
--- /dev/null
+++ b/templates/components/base/trade/turbo/broadcast/remove_transaction.stream.html.twig
@@ -0,0 +1,4 @@
+
+
+
diff --git a/templates/components/base/trade/turbo/new_transaction.stream.html.twig b/templates/components/base/trade/turbo/new_transaction.stream.html.twig
new file mode 100644
index 000000000..fe6561ed5
--- /dev/null
+++ b/templates/components/base/trade/turbo/new_transaction.stream.html.twig
@@ -0,0 +1,30 @@
+{% set transaction = commercial_shipping.transaction %}
+
+{% if transaction.hasResources %}
+
+
+
+
+
+{% elseif transaction.hasCommander %}
+
+
+{% elseif transaction.hasShips %}
+
+
+
+
+
+{% endif %}
+
+
+
+ {{ include('molecules/base/trade/commercial_shipping.html.twig', { commercial_shipping }) }}
+
+
+
+
+
+
+
+
diff --git a/templates/components/base/trade/turbo/remove_transaction.stream.html.twig b/templates/components/base/trade/turbo/remove_transaction.stream.html.twig
new file mode 100644
index 000000000..c3d966c4e
--- /dev/null
+++ b/templates/components/base/trade/turbo/remove_transaction.stream.html.twig
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/templates/components/base/type.html.twig b/templates/components/base/type.html.twig
new file mode 100644
index 000000000..5fb15cc59
--- /dev/null
+++ b/templates/components/base/type.html.twig
@@ -0,0 +1,160 @@
+{% set type_neutral = constant('App\\Modules\\Athena\\Model\\OrbitalBase::TYP_NEUTRAL') %}
+{% set type_commercial = constant('App\\Modules\\Athena\\Model\\OrbitalBase::TYP_COMMERCIAL') %}
+{% set type_military = constant('App\\Modules\\Athena\\Model\\OrbitalBase::TYP_MILITARY') %}
+{% set type_capital = constant('App\\Modules\\Athena\\Model\\OrbitalBase::TYP_CAPITAL') %}
+
+{# @TODO FACTORIZE THIIIIIS #}
+
+
+
+
+ {# #}
+ {{ current_base.name }}
+ {{ current_base|base_type }}
+
+
+
+ {{ get_base_type_info(current_base.typeOfBase, 'desc') }}
+
+
+
+
+
+ {# @TODO move that logic to a Specification class #}
+ {% if current_base.typeOfBase == type_neutral and current_player.credit >= get_base_type_info(type_commercial, 'price') and current_base.levelGenerator >= minimal_change_level %}
+
+
+ Evoluer en {{ get_base_type_info(type_commercial, 'name') }}
+ {{ get_base_type_info(type_commercial, 'price')|number }}
+ {# #}
+
+
+ {% elseif current_base.typeOfBase == type_military and current_player.credit >= get_base_type_info(type_commercial, 'price') %}
+
+
+ Transformer en {{ get_base_type_info(type_commercial, 'name') }}
+ {{ get_base_type_info(type_commercial, 'price')|number }}
+ {# #}
+
+
+ {% elseif current_base.typeOfBase == type_commercial or current_base.typeOfBase == type_capital %}
+ {# do nothing #}
+ {% else %}
+
+ Evoluer en {{ get_base_type_info(type_commercial, 'name') }}
+ {{ get_base_type_info(type_commercial, 'price')|number }}
+ {# #}
+
+ {% endif %}
+
+ Avantages & Inconvénients
+
+
+ Technologies
+ Orienté commerce et production
+
+
+ Bâtiments
+ Plateforme Commerciale et Spatioport au niveau maximum
+
+
+
+ Nécessite
+ Générateur niveau {{ minimal_change_level }}
+
+
+
+
+ {% if current_base.typeOfBase == type_neutral and current_player.credit >= get_base_type_info(type_military, 'price') and current_base.levelGenerator >= minimal_change_level %}
+
+
+ Evoluer en {{ get_base_type_info(type_military, 'name') }}
+ {{- get_base_type_info(type_military, 'price')|number -}}
+ {# #}
+
+
+ {% elseif current_base.typeOfBase == type_commercial and current_player.credit >= get_base_type_info(type_military, 'price') %}
+
+
+ Evoluer en {{ get_base_type_info(type_military, 'name') }}
+ {{- get_base_type_info(type_military, 'price')|number -}}
+ {# #}
+
+
+ {% elseif current_base.typeOfBase == type_military or current_base.typeOfBase == type_capital %}
+ {# do nothing #}
+ {% else %}
+
+ Evoluer en {{ get_base_type_info(type_military, 'name') }}
+ {{- get_base_type_info(type_military, 'price')|number -}}
+ {# #}
+
+ {% endif %}
+ Avantages & Inconvénients
+
+ Technologies
+ Orienté militaire
+
+
+ Bâtiments
+ Centre de Recyclage et Chantier de Ligne au niveau maximum
+
+
+
+ Nécessite
+ Générateur niveau {{ minimal_change_level }}
+
+
+
+
+ {% set total_price = get_base_type_info(type_capital, 'price') %}
+ {% if current_base.typeOfBase in [type_commercial, type_military] and current_player.credit >= total_price and current_base.levelGenerator >= capital_change_level %}
+ {% if capitals_count == 0 %}
+
+
+ Evoluer en {{ get_base_type_info(type_capital, 'name') }}
+ {{- total_price|number -}}
+ {# #}
+
+
+ {% else %}
+
+ Vous avez déjà une {{ get_base_type_info(type_capital, 'name') }}
+
+ {% endif %}
+ {% elseif current_base.typeOfBase == type_capital %}
+ {# do nothing #}
+ {% else %}
+ {% if capitals_count == 0 %}
+
+ Evoluer en {{ get_base_type_info(type_capital, 'name') }}
+ {{- total_price|number -}}
+ {# #}
+
+ {% else %}
+
+ Vous avez déjà une {{ get_base_type_info(type_capital, 'name') }}
+
+ {% endif %}
+ {% endif %}
+
+ Avantages & Inconvénients
+
+ Vous ne pouvez avoir qu'une capitale
+
+ Technologies
+ Toutes disponibles
+
+
+ Bâtiments
+ Tous au niveau maximum
+
+
+
+ Nécessite
+ Générateur niveau {{ capital_change_level }}
+
+
+
+
+
diff --git a/templates/components/common/alerts.html.twig b/templates/components/common/alerts.html.twig
new file mode 100644
index 000000000..310af8e96
--- /dev/null
+++ b/templates/components/common/alerts.html.twig
@@ -0,0 +1,22 @@
+
+ {% set flashMessages = app.session.flashBag.all() %}
+ {% for type in flashMessages|keys %}
+ {% for message in flashMessages[type] %}
+
+
+
+
+
+
+
{{ type }}
+
+
+
+
+ {{ message }}
+
+
+
+ {% endfor %}
+ {% endfor %}
+
diff --git a/templates/components/common/card.html.twig b/templates/components/common/card.html.twig
new file mode 100644
index 000000000..f51fdced3
--- /dev/null
+++ b/templates/components/common/card.html.twig
@@ -0,0 +1,21 @@
+
+
+ {% block picto %}{% endblock %}
+
+
+
+ {% block title %}{% endblock title %}
+ {% block content %}{% endblock content %}
+ {% block footer %}{% endblock footer %}
+
+
+
diff --git a/templates/components/common/navbar.html.twig b/templates/components/common/navbar.html.twig
new file mode 100644
index 000000000..88140b6d0
--- /dev/null
+++ b/templates/components/common/navbar.html.twig
@@ -0,0 +1,534 @@
+{% set building_queues_count = current_building_queues|length %}
+{% set technology_queues_count = current_technology_queues|length %}
+{% set tutorial_step = current_player.stepTutorial %}
+
+{% apply spaceless %}
+
+
+{% endapply %}
+
diff --git a/templates/components/common/toolbar.html.twig b/templates/components/common/toolbar.html.twig
new file mode 100644
index 000000000..492c3c9e2
--- /dev/null
+++ b/templates/components/common/toolbar.html.twig
@@ -0,0 +1,331 @@
+{% set building_queues_count = current_building_queues|length %}
+{% set technology_queues_count = current_technology_queues|length %}
+
+{% apply spaceless %}
+
+{% endapply %}
diff --git a/templates/components/data_collector/applied_bonuses.html.twig b/templates/components/data_collector/applied_bonuses.html.twig
new file mode 100644
index 000000000..097beed80
--- /dev/null
+++ b/templates/components/data_collector/applied_bonuses.html.twig
@@ -0,0 +1,191 @@
+{# templates/data_collector/template.html.twig #}
+{% extends '@WebProfiler/Profiler/layout.html.twig' %}
+
+{% block toolbar %}
+ {% apply spaceless %}
+ {% set icon %}
+ {# this is the content displayed as a panel in the toolbar #}
+ {{ include('components/data_collector/modifiers.svg') }}
+
{{ collector.appliedBonuses|length }}
+ {% endset %}
+
+ {% set text %}
+ {# this is the content displayed when hovering the mouse over
+ the toolbar panel #}
+ {% for modifier in collector.appliedBonuses %}
+ {% set is_positive = modifier.modified_value > 0 %}
+
+ {{ ('app.modifier.' ~ modifier.modifier_id)|trans }}
+
+
+ {{ modifier.initial_value }}
+
+
+ {{ is_positive ? '+' : '-' }} {{ modifier.modified_value|abs }}
+
+
+
+ {% endfor %}
+
+ {% if collector.appliedBonuses == [] %}
+
+ No modifier applied
+
+ {% endif %}
+ {% endset %}
+
+ {# the 'link' value set to 'false' means that this panel doesn't
+ show a section in the web profiler #}
+ {{ include('@WebProfiler/Profiler/toolbar_item.html.twig') }}
+ {% endapply %}
+{% endblock %}
+
+{% block menu %}
+ {# This left-hand menu appears when using the full-screen profiler. #}
+
+ {{ include('components/data_collector/modifiers.svg') }}
+ Modifiers
+
+{% endblock %}
+
+{% block panel %}
+
+
+
Applied Modifiers
+
+
+
Modifier Metrics
+
+
+
+
+ {{ collector.appliedBonuses|length }}
+
+
+ Total Applied Modifiers
+
+
+
+
+ {{ collector.appliedBonuses|filter(m => m.modified_value != 0)|length }}
+
+
+ Effective Modifiers
+
+
+
+
+
Applied Modifiers
+ {% if collector.appliedBonuses == [] %}
+
+ {% else %}
+
+
+
+ ID
+
+
+ Label
+
+
+ Coeff
+
+
+ Initial Value
+
+
+ Bonus/Malus
+
+
+
+ {% for modifier in collector.appliedBonuses %}
+ {% set is_positive = modifier.modified_value > 0 %}
+
+ {{ modifier.modifier_id }}
+ {{ ('app.modifier.' ~ modifier.modifier_id)|trans }}
+
+ {% if modifier.modified_value > 0 %}
+
+ +{{ modifier.modifier_value|abs }}%
+
+ {% elseif modifier.modifier_value == 0 %}
+ 0%
+ {% else %}
+
+ -{{ modifier.modifier_value|abs }}%
+
+ {% endif %}
+
+ {{ modifier.initial_value }}
+
+ {% if modifier.modified_value > 0 %}
+
+ +{{ modifier.modified_value|abs }}
+
+ {% elseif modifier.modified_value == 0 %}
+ 0
+ {% else %}
+
+ -{{ modifier.modified_value|abs }}
+
+ {% endif %}
+
+
+ {% endfor %}
+
+
+ {% endif %}
+
+
+
+
+
Player Bonuses
+
+
+
Player Bonuses
+
+
+
+
+ Bonus
+
+
+ Value
+
+
+
+ {% for bonus_id in collector.playerBonuses|keys %}
+ {% set value = collector.playerBonuses[bonus_id] %}
+ {% set status_class -%}
+ {%- if value > 0 -%}
+ status-success
+ {%- elseif value < 0 -%}
+ status-error
+ {%- endif -%}
+ {%- endset %}
+
+
+
+ {{ ('app.modifier.' ~ bonus_id)|trans }}
+
+
+
+ {{ value }}%
+
+
+
+ {% endfor %}
+
+
+
+
+
+
+ {% if collector.appliedBonuses == [] %}
+
+
No modifier has been applied
+
+ {% endif %}
+{% endblock %}
diff --git a/templates/components/data_collector/modifiers.svg b/templates/components/data_collector/modifiers.svg
new file mode 100644
index 000000000..a536ff623
--- /dev/null
+++ b/templates/components/data_collector/modifiers.svg
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/components/financial/reporting/base_investments.html.twig b/templates/components/financial/reporting/base_investments.html.twig
new file mode 100644
index 000000000..3ae27a919
--- /dev/null
+++ b/templates/components/financial/reporting/base_investments.html.twig
@@ -0,0 +1,79 @@
+{% apply spaceless %}
+
+
+
+
+
+ {% for base in player_bases %}
+ {% set base_invest = base.iSchool + base.iAntiSpy %}
+
+
+
+
+
+ ↓
+ {{ base.name }}
+
+
+ {{- base_invest|number -}}
+
+
+
+
+
+
+ école de cmd.
+
+ {{- base.iSchool|number -}}
+
+
+
+
+
+
+
+ contre-espionnage
+
+ {{- base.iAntiSpy|number -}}
+
+
+
+
+
+
+
+
+
+ {% endfor %}
+
+
+ total des investissements
+
+ {{- investments_data.totalInvest|number -}}
+
+
+
+
+
+{% endapply %}
diff --git a/templates/components/financial/reporting/commander_fees.html.twig b/templates/components/financial/reporting/commander_fees.html.twig
new file mode 100644
index 000000000..d19e72a32
--- /dev/null
+++ b/templates/components/financial/reporting/commander_fees.html.twig
@@ -0,0 +1,63 @@
+{% apply spaceless %}
+
+
+
+
+
+ {% for base_id in commanders_by_base|keys %}
+ {% set commanders = commanders_by_base[base_id] %}
+ {% set commander_fees = commanders|reduce(
+ (carry, c) => carry + (c.level * constant('App\\Modules\\Ares\\Model\\Commander::LVLINCOMECOMMANDER'))
+ ) %}
+
+
+
+
+ {% for base in player_bases|filter(b => b.id == base_id) %}
+
+ {{ base.name }}
+ [{{ commanders|length }} officier{{ commanders|length|plural }}]
+
+ {% endfor %}
+
+ {{ commander_fees|number }}
+
+
+
+
+
+ {% for commander in commanders %}
+
+ {{ commander|commander_rank }} {{ commander.name }}
+
+ {{- (commander.level * constant('App\\Modules\\Ares\\Model\\Commander::LVLINCOMECOMMANDER')) -}}
+
+
+ {% endfor %}
+
+
+
+
+ {% endfor %}
+
+
+ total des salaires
+
+ {{- investments_data.totalFleetFees -}}
+
+
+
+
+
+{% endapply %}
diff --git a/templates/components/financial/reporting/commercial_routes.html.twig b/templates/components/financial/reporting/commercial_routes.html.twig
new file mode 100644
index 000000000..43b04345d
--- /dev/null
+++ b/templates/components/financial/reporting/commercial_routes.html.twig
@@ -0,0 +1,81 @@
+{% apply spaceless %}
+
+
+
+
+
+ {% for base in player_bases %}
+ {% set base_data = bases_data[base.id.toRfc4122()] %}
+
+
+
+
+ {% if base_data['routes_count'] > 0 %}
+
+ ↓
+
+ {% endif %}
+
+ {{ base.name }}
+
+ [{{ base_data['routes_count'] }}
+ route{{ base_data['routes_count']|plural }}]
+
+
+
+ {{ base_data['routes_income'] }}
+ {% if base_data['routes_income_bonus'] > 0 %}
+ +{{ base_data['routes_income_bonus']|number }}
+ {% endif %}
+
+
+
+
+
+ {% if base_data['routes_count'] > 0 %}
+
+ {% for route in base_data['routes'] %}
+ {% if route.isActive %}
+
+
+ {{- route.originBase.name == base.name ? route.destinationBase.name : route.originBase.name -}}
+
+
+ {{- route.income|number -}}
+
+
+ {% endif %}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+ {% endfor %}
+
+
+ total des routes commerciales
+
+ {{- investments_data.totalRouteIncome|number -}}
+ {% if investments_data.totalRouteIncomeBonus > 0 %}
+ +{{- investments_data.totalRouteIncomeBonus|number -}}
+ {% endif %}
+
+
+
+
+
+
+{% endapply %}
diff --git a/templates/components/financial/reporting/general.html.twig b/templates/components/financial/reporting/general.html.twig
new file mode 100644
index 000000000..2f4a447f0
--- /dev/null
+++ b/templates/components/financial/reporting/general.html.twig
@@ -0,0 +1,93 @@
+{% apply spaceless %}
+
+
+
+
recettes
+
+
+
+ {{- investments_data.totalTaxIn|number -}}
+
+
+
+ {{- investments_data.totalRouteIncome|number -}}
+
+
+
+ {{- investments_data.total_income|number -}}
+
+
+ {% if investments_data.gains < 0 %}
+
+ {{- investments_data.gains|abs|number -}}
+
+ {% endif %}
+
+
+
+
+
dépenses
+
+
+ {{- investments_data.totalInvest|number -}}
+
+
+
+
+
+ ↓ {{ investments_data.totalInvestUni|number }}
+
+
+
+
+
+
+
+ {{- investments_data.totalFleetFees|number -}}
+
+
+ {{- investments_data.totalShipsFees|number -}}
+
+
+ {{- investments_data.totalTaxOut|number -}}
+
+
+ {{- investments_data.total_expenses|number -}}
+
+ {% if investments_data.gains >= 0 %}
+
+ {{- investments_data.gains|number -}}
+
+ {% endif %}
+
+
+
+
+ Ces deux colonnes sont un résumé de vos recettes et de vos dépenses pendant la durée d’une relève. Dans la
+ colonne des recettes vous pouvez voir tous les éléments qui vous rapportent des crédits dans Asylamba. Dans la colonne des dépenses,
+ c’est l’inverse, vous voyez les éléments qui vous coûtent sur vos bases. La différence entre ces deux colonnes vous donne soit une
+ perte (sous la colonne des recettes) soit un bénéfice (sous la colonne des dépenses).
+
+
+
+
+ évolution des finances
+
+
+ {{- current_player.credit|number -}}
+
+
+ {{- investments_data.gains|number -}}
+
+
+ {{- investments_data.remains|number -}}
+
+
+
+
+{% endapply %}
diff --git a/templates/components/financial/reporting/imposition.html.twig b/templates/components/financial/reporting/imposition.html.twig
new file mode 100644
index 000000000..0a0dfa35a
--- /dev/null
+++ b/templates/components/financial/reporting/imposition.html.twig
@@ -0,0 +1,50 @@
+{% apply spaceless %}
+
+
+
+
+
+ {% for base in player_bases %}
+ {% set base_data = bases_data[base.id.toRfc4122()] %}
+ {% set base_tax_income = base_data.tax_income %}
+ {% set base_tax_income_bonus = base_data.tax_income_bonus %}
+
+
+ {{ base.name }}
+ [{{ base.place.population|number }} Mio hab.]
+
+
+ {{- base_tax_income|number -}}
+ {% if base_tax_income_bonus > 0 %}
+ +{{ base_tax_income_bonus|number }}
+ {% endif %}
+
+
+
+ {% endfor %}
+
+
+ total de l'imposition
+
+ {{- investments_data.totalTaxIn|number -}}
+ {% if investments_data.totalTaxInBonus > 0 %}
+ +{{ investments_data.totalTaxInBonus|number }}
+ {% endif %}
+
+
+
+
+
+
+{% endapply %}
diff --git a/templates/components/financial/reporting/ship_fees.html.twig b/templates/components/financial/reporting/ship_fees.html.twig
new file mode 100644
index 000000000..bf3cf9fa8
--- /dev/null
+++ b/templates/components/financial/reporting/ship_fees.html.twig
@@ -0,0 +1,76 @@
+{% apply spaceless %}
+
+
+
+
+
+ {% for base in player_bases %}
+ {% set base_fleet_cost = get_base_fleet_cost(base) %}
+ {% set base_commanders = commanders_by_base[base.id.toRfc4122()] is defined ? commanders_by_base[base.id.toRfc4122()] : [] %}
+ {% set base_transaction = transactions|filter(t => t.base.id == base.id)|reduce((carry, t) => carry + get_ship_transaction_cost(t))|default(0) %}
+
+ {% set total_base = base_fleet_cost + base_transaction + base_commanders|reduce(
+ (carry, commander) => carry + get_fleet_cost(commander)
+ ) %}
+
+
+
+
+ ↓
+ {{ base.name }}
+
+
+ {{- total_base|number -}}
+
+
+
+
+
+
+ Dans le hangar
+
+ {{- base_fleet_cost|number -}}
+
+
+
+
+ En vente
+
+ {{- base_transaction|number -}}
+
+
+
+ {% for commander in base_commanders %}
+
+ {{ commander|commander_rank }} {{ commander.name }}
+
+ {{- get_fleet_cost(commander)|number -}}
+
+
+ {% endfor %}
+
+
+
+ {% endfor %}
+
+
+ total des coûts
+
+ {{- investments_data.totalShipsFees|number -}}
+
+
+
+
+
+
+{% endapply %}
diff --git a/templates/components/financial/reporting/taxes.html.twig b/templates/components/financial/reporting/taxes.html.twig
new file mode 100644
index 000000000..683173930
--- /dev/null
+++ b/templates/components/financial/reporting/taxes.html.twig
@@ -0,0 +1,45 @@
+{% apply spaceless %}
+
+
+
+
+
+ {% for base in player_bases %}
+ {% set base_tax = get_base_tax(base, tax_coeff) %}
+ {# @TODO move that logic somewhere legit #}
+ {% set base_tax_out = base_tax.total * base.place.system.sector.tax / 100 %}
+
+
+
+ {{ base.name }}
+ [{{ base.place.system.sector.tax }}% de taxe]
+
+
+ {{- base_tax_out|number -}}
+
+
+
+ {% endfor %}
+
+
+ total de la redevance
+
+ {{- investments_data.totalTaxOut|number -}}
+
+
+
+
+
+
+{% endapply %}
diff --git a/templates/components/financial/transfers/faction_sending.html.twig b/templates/components/financial/transfers/faction_sending.html.twig
new file mode 100644
index 000000000..760a518a9
--- /dev/null
+++ b/templates/components/financial/transfers/faction_sending.html.twig
@@ -0,0 +1,23 @@
+
+
+
+ Envoyer à votre faction
+
+
+
+
+
diff --git a/templates/components/financial/transfers/player_sending.html.twig b/templates/components/financial/transfers/player_sending.html.twig
new file mode 100644
index 000000000..9d5bce74d
--- /dev/null
+++ b/templates/components/financial/transfers/player_sending.html.twig
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/templates/components/financial/transfers/receivings_history.html.twig b/templates/components/financial/transfers/receivings_history.html.twig
new file mode 100644
index 000000000..28c9db86e
--- /dev/null
+++ b/templates/components/financial/transfers/receivings_history.html.twig
@@ -0,0 +1,24 @@
+
+
+
+ {% for transaction in receivings %}
+
+
+
+
+
+
{{ transaction.senderStatus }}
+
{{ transaction.senderName }}
+
{{ transaction.amount|number }} crédit{{ transaction.amount|plural }}
+
+ {% endfor %}
+
+ {% if receivings|length == 0 %}
+ Vous n'avez encore reçu aucun crédit.
+ {% endif %}
+
+
diff --git a/templates/components/financial/transfers/sendings_history.html.twig b/templates/components/financial/transfers/sendings_history.html.twig
new file mode 100644
index 000000000..cc9ebc095
--- /dev/null
+++ b/templates/components/financial/transfers/sendings_history.html.twig
@@ -0,0 +1,24 @@
+
+
+
+ {% for transaction in sendings %}
+
+
+
+
+
+
{{ transaction.receiverStatus }}
+
{{ transaction.receiverName }}
+
{{ transaction.amount|number }} crédit{{ transaction.amount|plural }}
+
+ {% endfor %}
+
+ {% if sendings|length == 0 %}
+ Vous n'avez fait aucun envoi de crédit.
+ {% endif %}
+
+
diff --git a/templates/components/fleet/combat_report.html.twig b/templates/components/fleet/combat_report.html.twig
new file mode 100644
index 000000000..2d8e430fc
--- /dev/null
+++ b/templates/components/fleet/combat_report.html.twig
@@ -0,0 +1,183 @@
+
+
+
+
Attaquant
+
{{ report_attacker.name }}
+
+
+
+ {% if report.attacker.id == (report.winner.id ?? null) %}
+
+ Victoire
+
+
+ {% if report.attacker.id == current_player.id %}
+
+
+
+
+ {% set level = get_commander_level_up_from_report(report.attackerLevel, report.attackerCommanderExperience + report.earnedExperience) %}
+
+ {{ get_commander_rank(report.attackerLevel) }}
+ {% if level > 0 %}
+ a passé {{ level }} grade{{ level|plural }}
+ {% endif %}
+
+
{{ report.attacker.name }}
+
expérience : {{ report.attackerCommanderExperience|number }}
+ + {{ report.earnedExperience|number }}
+
+
victoires : {{ report.attackerPalmares }} + 1
+
+ {{- report.resources|number -}}
+ ressource{{ report.resources|plural }} gagnée{{ report.resources|plural }}
+
+
+ {% else %}
+
+
+
{{ get_commander_rank(report.attackerLevel) }}
+
{{ report.attackerCommander.name }}
+
expérience : ---
+
victoire : ---
+
+
+ {% endif %}
+ {% else %}
+
+ Défaite
+
+
+
+
+
{{ get_commander_rank(report.attackerLevel) }}
+
{{ report.attackerCommander.name }}
+ {% if report.attacker.id == current_player.id %}
+
expérience : {{ report.attackerCommanderExperience|number }}
+
victoire : {{ report.attackerPalmares }}
+
+ {% else %}
+
expérience : ---
+
victoire : ---
+
+ {% endif %}
+
+ {% endif %}
+
+ {{ include('molecules/fleet/combat_report_table.html.twig', {
+ 'total_in_begin': report.attackerTotalInBegin,
+ 'difference': report.attackerDifference,
+ 'total_at_end': report.attackerTotalAtEnd,
+ }) }}
+
+
+
+
+
+
+ {% if report_defender is null %}
+
+
Défenseur
+
Rebelle
+ {% else %}
+
+
Défenseur
+
{{ report_defender.name }}
+ {% endif %}
+
+
+
+ {% if defender_report.defender.id ?? null == report.winner.id ?? null %}
+
+ Victoire
+
+
+ {% if report.defender.id ?? null == current_player.id %}
+
+
+ {% set level = get_commander_level_up_from_report(report.defenderLevel, report.defenderCommanderExperience + report.earnedExperience) %}
+
{{ get_commander_rank(report.defenderLevel) }}
+ {% if level > 0 %}
+ a passé {{ level }} grade
+ {% endif %}
+
+
{{ report.defenderCommander.name ?? 'Rebelle' }}
+
+ expérience : {{- report.defenderCommanderExperience|number -}}
+ + {{ report.earnedExperience|number }}
+
+
victoire : {{ report.defenderPalmares }} + 1
+
+
+ {% else %}
+
+
{{ get_commander_rank(report.defenderLevel) }}
+ {% if report.defender is null %}
+
+
Rebelle
+ {% else %}
+
+
{{ report.defenderCommander.name }}
+ {% endif %}
+
expérience : ---
+
victoire : ---
+
+
+ {% endif %}
+ {% else %}
+
+ Défaite
+
+
+ {% if report.defender.id ?? null == current_player.id %}
+
+
+
{{ get_commander_rank(report.defenderLevel) }}
+
{{ report.defenderCommander.name }}
+
expérience : {{ report.defenderCommanderExperience|number }}
+
victoire : {{ report.defenderPalmares }}
+
+
+ {% else %}
+
+
+
{{ get_commander_rank(report.defenderLevel) }}
+ {% if report.defender is null %}
+
Rebelle
+ {% else %}
+
{{ report.defenderCommander.name }}
+ {% endif %}
+
expérience : ---
+
victoire : ---
+
+
+ {% endif %}
+ {% endif %}
+
+ {# @TODO check if this block is useful #}
+ {# if ($report->type == Commander::LOOT) {
+
+
+
+ {% if report.rPlayerAttacker != report.rPlayerWinner %}
+ {% if report.resources == 0 %}
+ Aucune ressource perdue
+ {% else %}
+ {{ report.resources|number }} ressources perdues
+ {% endif %}
+ {% else %}
+
+ {% endif %}
+
+
+
+ }#}
+
+ {{ include('molecules/fleet/combat_report_table.html.twig', {
+ 'total_in_begin': report.defenderTotalInBegin,
+ 'difference': report.defenderDifference,
+ 'total_at_end': report.defenderTotalAtEnd,
+ }) }}
+
+
+
diff --git a/templates/components/fleet/combat_reports.html.twig b/templates/components/fleet/combat_reports.html.twig
new file mode 100644
index 000000000..f9fb3d0de
--- /dev/null
+++ b/templates/components/fleet/combat_reports.html.twig
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+ {% if combat_reports|length > 0 %}
+
+
+ {% for report in combat_reports %}
+ {% set report_data = get_report_data(report) %}
+
+
+ {% endfor %}
+
+ {% else %}
+
Il n'y a aucun rapport de combat dans vos archives militaires.
+ {% endif %}
+
+ {% if app.request.query.get('mode') == 'archived' %}
+
Voir tous les rapports
+ {% else %}
+
Voir les archives des rapports
+ {% endif %}
+
+
+
diff --git a/templates/components/fleet/commander_details.html.twig b/templates/components/fleet/commander_details.html.twig
new file mode 100644
index 000000000..9c1672daf
--- /dev/null
+++ b/templates/components/fleet/commander_details.html.twig
@@ -0,0 +1,111 @@
+
+
+
+
+ {% if commander.isAffected %}
+
+ {% endif %}
+
+
+ État de l'officier
+ {% if commander.isInSchool %}
+ À l'école
+ {% elseif commander.isAffected %}
+ À quai
+ {% elseif commander.isMoving %}
+ En mission
+ {% else %}
+ Tombé au combat
+ {% endif %}
+
+
+ {% if commander.isMoving %}
+
+ Mission
+ {% if commander.isTransferring %}
+ Déplacement
+
+
+ Vers
+ {{ commander.destinationPlace.base.name ?? 'colonie rebelle' }}
+ {% elseif commander.isLooting %}
+ Pillage
+
+
+ Cible
+ {{ commander.destinationPlace.base.name ?? 'colonie rebelle' }}
+ {% elseif commander.isInvading %}
+ Colonisation
+
+
+ Cible
+ {{ commander.destinationPlace.base.name ?? 'colonie rebelle' }}
+ {% elseif commander.isComingBack %}
+ Retour victorieux
+
+
+ Ressources transportées
+ {{ commander.resources|number }}
+ {% endif %}
+
+ {% endif %}
+
+
+
+
+ Nom
+ {{ commander.name }}
+
+
+ Victoire{{ commander.palmares|plural }}
+ {{ commander.palmares }}
+
+
+ Grade
+ {{ commander|commander_rank }}
+
+
+ {% if commander.isAffected or commander.isMoving or commander.isInSchool %}
+
+ Expérience
+ {% set missing_experience = get_commander_missing_experience(commander) %}
+ {% set percent = (commander.experience - missing_experience / 2)|percent(missing_experience - missing_experience / 2) %}
+ {{ commander.experience|number }} / {{ missing_experience|number }}
+
+
+
+
+ {% endif %}
+
+
+
+
+
Salaire de l'officier
+
+ {{- (commander.level * constant('App\\Modules\\Ares\\Model\\Commander::LVLINCOMECOMMANDER'))|number -}}
+
+
+
+
+
+
Frais d'entretien des vaisseaux
+
+ {{- get_fleet_cost(commander)|number -}}
+
+
+
+
+
diff --git a/templates/components/fleet/commander_fleet.html.twig b/templates/components/fleet/commander_fleet.html.twig
new file mode 100644
index 000000000..a5cf84eda
--- /dev/null
+++ b/templates/components/fleet/commander_fleet.html.twig
@@ -0,0 +1,24 @@
+{% apply spaceless %}
+
+
+
+ Ceci représente la composition de votre flotte.
+ Chaque carré montre une escadrille.
+ Vous pouvez remplir cette escadrille en cliquant dessus puis en cliquant sur un type de vaisseau dans les colonnes à droite.
+
+
+ La première colonne représente votre escadrille et la seconde votre hangar.
+
+
+ En pressant CTRL + clic (CMD + clic), vous pouvez transférer le maximum de vaisseaux possible.
+
+
+
+ {% if commander.isAffected %}
+
+ {% endif %}
+
+{% endapply %}
diff --git a/templates/components/fleet/list.html.twig b/templates/components/fleet/list.html.twig
new file mode 100644
index 000000000..8881fd790
--- /dev/null
+++ b/templates/components/fleet/list.html.twig
@@ -0,0 +1,133 @@
+
+
+ {% set list_all_fleets_param = constant('App\\Classes\\Container\\Params::LIST_ALL_FLEET') %}
+
+
+ {{- app.request.cookies.get('p' ~ list_all_fleets_param, default_parameters[list_all_fleets_param]) ? 'Afficher uniquement la base courante' : 'Afficher toutes les bases' -}}
+
+
+
+ {% for base in obsets %}
+
+
+
+
+
+
+
+
+ {% for commander in base.fleets %}
+ {% set reversed = commander.player.id != current_player.id or commander.isComingBack %}
+
+
+
+ {% if commander.player.id != current_player.id %}
+
+ {% elseif commander.isAffected %}
+
+ {% elseif commander.isMoving %}
+ {% if commander.isTransferring %}
+
+ {% elseif commander.isLooting %}
+
+ {% elseif commander.isInvading %}
+
+ {% elseif commander.isComingBack %}
+ <
+ {% endif %}
+ {% endif %}
+
+ {{ commander|commander_rank }} {{ commander.name }}
+
+ {% if commander.player.id != current_player.id %}
+ {% if commander.isLooting %}
+ tente de vous piller
+ {% elseif commander.isInvading %}
+ tente de vous conquérir
+ {% else %}
+ erreur
+ {% endif %}
+ {% elseif commander.isAffected %}
+ à quai
+ {% elseif commander.isMoving %}
+ {% if commander.isTransferring %}
+ se déplace
+ {% elseif commander.isLooting %}
+ tente un pillage
+ {% elseif commander.isInvading %}
+ tente une colonisation
+ {% elseif commander.isComingBack %}
+ revient avec {{ commander.resources }} ressources
+ {% endif %}
+ {% endif %}
+
+
+ {% if commander.player.id == current_player.id %}
+ {{ commander|pev|number ~ ' pev' }}
+ {% endif %}
+
+ {% if commander.player.id == current_player.id and commander.isMoving and not commander.isComingBack %}
+ {{ ' '|raw }}annuler la mission
+ {% endif %}
+
+
+
+
+ {% if commander.isMoving %}
+ {% set spent_time = commander|travel_spent_time(reversed) %}
+ {% set remaining_time = commander|travel_remaining_time %}
+ {% set total_time = commander|travel_total_time %}
+
+
+
+
+
{{ remaining_time|lite_seconds }}
+
+
+ {% else %}
+
+ {% endif %}
+
+
+ {% if commander.isMoving %}
+
+ {% endif %}
+
+ {% if commander.player.id == current_player.id %}
+
+ >
+
+ {% endif %}
+
+ {% endfor %}
+
+
+ {% if base.fleets|length == 0 %}
+
Aucun commandant affecté autour de cette planète
+ {% endif %}
+
+
+ {% endfor %}
+
+
diff --git a/templates/components/fleet/manage_combat_report.html.twig b/templates/components/fleet/manage_combat_report.html.twig
new file mode 100644
index 000000000..df521751e
--- /dev/null
+++ b/templates/components/fleet/manage_combat_report.html.twig
@@ -0,0 +1,10 @@
+
diff --git a/templates/components/fleet/overview.html.twig b/templates/components/fleet/overview.html.twig
new file mode 100644
index 000000000..16df432ea
--- /dev/null
+++ b/templates/components/fleet/overview.html.twig
@@ -0,0 +1,118 @@
+
+
+
Aperçu des armées
+
+
+
+
+
+
+
+ {% for i in range(0, 11) %}
+ {{ get_ship_info(i, 'codeName') }}
+ {% endfor %}
+ PEV
+
+
+ {% set total_ships = range(0, 11)|map(v => 0) %}
+
+ {% for base in obsets %}
+ {% set total_ships_base = range(0, 11)|map(v => 0) %}
+
+
+
+
+ {{ get_base_type_info(base.info.type, 'name') }}
+ {{ base.info.name }}
+
+
+
+
+
+ Vaisseaux dans les hangars
+
+
+ {% set line_pev = 0 %}
+
+ {% for i in range(0, total_ships_base|length - 1) %}
+ {% set dock_storage = base.dock[i] ?? 0 %}
+
+
+ {{- dock_storage -}}
+
+
+
+ {% set line_pev = line_pev + get_ship_info(i, 'pev') * dock_storage %}
+
+ {% set total_ships = {(i): total_ships[i] + dock_storage} + total_ships %}
+ {% set total_ships_base = {(i): (total_ships_base[i] + dock_storage)} + total_ships_base %}
+ {% endfor %}
+ {{ line_pev }}
+
+
+ {% for commander in base.fleets %}
+ {% set commander_ships = commander.nbrShipByType %}
+ {% set line_pev = 0 %}
+
+
+
+
+ {{ commander|commander_rank }} {{ commander.name }}
+
+
+ {% for i in range(0, total_ships_base|length - 1) %}
+
+
+ {{- commander_ships[i] -}}
+
+
+
+ {% set line_pev = line_pev + get_ship_info(i, 'pev') * commander_ships[i] %}
+
+ {% set total_ships = {(i): total_ships[i] + commander_ships[i]} + total_ships %}
+ {% set total_ships_base = {(i): total_ships_base[i] + commander_ships[i]} + total_ships_base %}
+ {% endfor %}
+ {{ line_pev }}
+
+ {% endfor %}
+
+
+ Total sur la planète
+ {% set line_pev = 0 %}
+
+ {% for i in range(0, total_ships_base|length - 1) %}
+ {% set ships_count = total_ships_base[i] ?? 0 %}
+
+
+ {{- ships_count -}}
+
+
+
+ {% set line_pev = line_pev + get_ship_info(i, 'pev') * ships_count %}
+ {% endfor %}
+ {{ line_pev }}
+
+ {% endfor %}
+
+
+
+ Total général
+
+ {% set line_pev = 0 %}
+
+ {% for i in range(0, total_ships|length - 1) %}
+ {% set ships_count = total_ships[i] ?? 0 %}
+
+
+ {{- ships_count -}}
+
+
+
+ {% set line_pev = line_pev + get_ship_info(i, 'pev') * ships_count %}
+ {% endfor %}
+ {{ line_pev }}
+
+
+
+
+
diff --git a/templates/components/fleet/spy_report.html.twig b/templates/components/fleet/spy_report.html.twig
new file mode 100644
index 000000000..bc46d16ad
--- /dev/null
+++ b/templates/components/fleet/spy_report.html.twig
@@ -0,0 +1,137 @@
+
+
+
+
+ Informations
+
+
+
+
+ {{ get_base_type_info(spy_report.place.typeOfPlace, 'name') }}
+ {% if spy_report.hasSpottedPoints %}
+ {{ spy_report.points|number }}
+ {% else %}
+ ???
+ {% endif %}
+ points
+
+
+
+
+
+
+
+
+ Ressources dans les entrepôts
+
+ {% if spy_report.hasSpottedResourcesStorage %}
+ {{- spy_report.resources -}}
+ {% else %}
+ ???
+ {% endif %}
+
+
+
+
+
+ Investissement dans le contre-espionnage
+
+ {% if spy_report.hasSpottedAntiSpy %}
+ {{- spy_report.antiSpyInvest -}}
+ {% else %}
+ ???
+ {% endif %}
+
+
+
+
+
+ Revenus des routes commerciales
+
+ {% if spy_report.hasSpottedCommercialRoutesIncome %}
+ {{- spy_report.commercialRouteIncome -}}
+ {% else %}
+ ???
+ {% endif %}
+
+
+
+
+
+
+ {{ (place.population * 1000000)|number }} habitants
+ {{ place.coefResources }} % coeff. ressource
+ {{ get_place_technosphere_improvement_coeff(place) }} % de bonus scientifique
+
+
+
+
+
+
+
+
+ {% set orbital_base_type = spy_report.place.base.type ?? constant('App\\Modules\\Athena\\Model\\OrbitalBase::TYP_NEUTRAL') %}
+
+ {% set commanders = spy_report.commanders %}
+
+ {% if spy_report.hasSpottedFleets and commanders|length > 0 %}
+
Commandants en orbite
+ {% endif %}
+
+ {% if spy_report.hasSpottedFleets %}
+ {% for commander in commanders %}
+
+ {% endfor %}
+ {% endif %}
+
+ {% if spy_report.place.typeOfPlace is not constant('App\\Modules\\Gaia\\Model\\Place::TYP_EMPTY') %}
+
+ {% endif %}
+
+
+
+
+
diff --git a/templates/components/fleet/spy_reports.html.twig b/templates/components/fleet/spy_reports.html.twig
new file mode 100644
index 000000000..053bcdd9d
--- /dev/null
+++ b/templates/components/fleet/spy_reports.html.twig
@@ -0,0 +1,26 @@
+
+
+ Archives d'espionnage
+
+
+
+
+ {% if spy_reports|length > 0 %}
+
+ {% for spy_report in spy_reports %}
+
+ {% endfor %}
+
+ {% else %}
+
Il n'y a aucun rapport d'espionnage dans vos archives.
+ {% endif %}
+
+
+
diff --git a/templates/components/map/sectors.html.twig b/templates/components/map/sectors.html.twig
new file mode 100644
index 000000000..a46662291
--- /dev/null
+++ b/templates/components/map/sectors.html.twig
@@ -0,0 +1,51 @@
+
+
+ {% for sector in sectors %}
+
+ {% endfor %}
+
+
+
+
+ {% for sector in sectors %}
+ {% set sectorCenterX = sector.xBarycentric * galaxy_configuration.scale %}
+ {% set sectorCenterY = sector.yBarycentric * galaxy_configuration.scale %}
+
+
+ {{ sector.name }}
+
+ +
+ {% if sector.faction is not null %}
+ Revendiqué par {{ get_faction_info(sector.faction.identifier ?? 0, 'popularName') }}
+ | {{ sector.tax }}% de taxe
+ {% else %}
+ Non revendiqué
+ | Aucune taxe
+ {% endif %}
+ | rapporte {{ sector.points }} point{{ sector.points|plural }}
+
+
+
+ {{ sector.identifier }}
+
+
+ {% endfor %}
+
diff --git a/templates/components/map/system_details.html.twig b/templates/components/map/system_details.html.twig
new file mode 100644
index 000000000..d40ca21b4
--- /dev/null
+++ b/templates/components/map/system_details.html.twig
@@ -0,0 +1,92 @@
+{% apply spaceless %}
+ {% set systemFactionIdentifier = system.faction.identifier ?? 0 %}
+
+
+
+ Système #{{ system.id }}
+ Cordonnées {{ system|coords }}
+
+ {% if systemFactionIdentifier == 0 %}
+ Non revendiqué
+ {% else %}
+ Revendiqué par {{ get_faction_info(systemFactionIdentifier, 'popularName') }}
+ {% endif %}
+
+
+ {{ get_system_info(system.typeOfSystem, 'frenchName') }}
+
+
+
+ ×
+
+
+
+
+
+
+
+
+
+
+ {% for place in places %}
+ {% set playerFactionIdentifier = place.player.faction.identifier ?? 0 %}
+ {% set position = place.position - 1 %}
+
+
+
+
+ {% if place.player is not null %}
+
+ {% endif %}
+
+
+
+ {% if place.typeOfPlace == 1 %}
+
+ {% else %}
+ {% if place.resources > 10000000 %}
+ {% set size = 3 %}
+ {% elseif place.resources > 5000000 %}
+ {% set size = 2 %}
+ {% else %}
+ {% set size = 1 %}
+ {% endif %}
+
+ {% endif %}
+
+
+
+ {{ position }}
+
+
+
+ {# noAjax #}
+ {# This is the place panel deployed when the place is selected #}
+
+
+
+
+ {{ include('molecules/map/place_actions.html.twig', {
+ technologies,
+ moving_commanders,
+ colonization_cost,
+ conquest_cost,
+ recycling_missions,
+ }) }}
+
+
+
+ {% endfor %}
+
+
+{% endapply %}
diff --git a/templates/components/modifiers/mathematical-basic-signs-of-plus-and-minus-with-a-slash-svgrepo-com.svg b/templates/components/modifiers/mathematical-basic-signs-of-plus-and-minus-with-a-slash-svgrepo-com.svg
new file mode 100644
index 000000000..3f6c3f903
--- /dev/null
+++ b/templates/components/modifiers/mathematical-basic-signs-of-plus-and-minus-with-a-slash-svgrepo-com.svg
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/components/notifications/last.html.twig b/templates/components/notifications/last.html.twig
new file mode 100644
index 000000000..25b70d148
--- /dev/null
+++ b/templates/components/notifications/last.html.twig
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+ Les notifications sont les messages que vous recevez du gouvernement de votre ou vos planètes. Ces messages vous avertissent de
+ toutes les actions qui prennent fin dans le jeu, comme les attaques et les développements technologiques par exemple.
+ Elles vous permettent d’avoir un compte rendu de toutes vos activités sur Asylamba. Au bout d\'un certain temps, elles seront automatiquement supprimées, sauf si vous les archivez.
+
+
+ {% if notifications|length > 0 %}
+
+ {% for notification in notifications %}
+
+
+ {{ notification.title }}
+
+
+
+
{{ notification.content|raw }}
+
+
archiver ou
+
supprimer
+ — {{ notification.sentAt|game_date|raw }}
+
+
+
+ {% endfor %}
+
+ {% else %}
+ Il n\'y a aucune notification dans votre boîte de réception.
+ {% endif %}
+
+
diff --git a/templates/components/rankings/faction/best_faction.html.twig b/templates/components/rankings/faction/best_faction.html.twig
new file mode 100644
index 000000000..9b2a4e7a6
--- /dev/null
+++ b/templates/components/rankings/faction/best_faction.html.twig
@@ -0,0 +1,18 @@
+
+
+
+
+ {{ get_faction_info(best_faction.faction.identifier, 'popularName') }}
+
+
+
+
+
+
+
+ "{{ get_faction_info(best_faction.faction.identifier, 'devise') }}"
+
+
+
diff --git a/templates/components/rankings/faction/general_rankings.html.twig b/templates/components/rankings/faction/general_rankings.html.twig
new file mode 100644
index 000000000..ad538c911
--- /dev/null
+++ b/templates/components/rankings/faction/general_rankings.html.twig
@@ -0,0 +1,18 @@
+
+
+
+ Général
+ Somme des points des joueurs de la faction
+
+
+
+ {% for faction_ranking in general_rankings %}
+ {{ include('molecules/rankings/faction_ranking_card.html.twig', {
+ faction_ranking,
+ 'value': faction_ranking.general|number(-1) ~ ' points',
+ 'position': faction_ranking.generalPosition,
+ 'variation': faction_ranking.generalVariation,
+ }) }}
+ {% endfor %}
+
+
diff --git a/templates/components/rankings/faction/points_rankings.html.twig b/templates/components/rankings/faction/points_rankings.html.twig
new file mode 100644
index 000000000..0a494ed5d
--- /dev/null
+++ b/templates/components/rankings/faction/points_rankings.html.twig
@@ -0,0 +1,22 @@
+
+
+
+ Classement de victoire
+ Classement cumulatif. La victoire est remportée à {{ points_to_win }} points.
+
+
+ {% for faction_ranking in points_rankings %}
+ {% set value = faction_ranking.points|number(-1) ~ ' points' %}
+ {% if faction_ranking.newPoints > 0 %}
+ {% set value = value ~ ' (+' ~ faction_ranking.newPoints|number(-1) ~ ' points)' %}
+ {% endif %}
+
+ {{ include('molecules/rankings/faction_ranking_card.html.twig', {
+ faction_ranking,
+ value,
+ 'position': faction_ranking.pointsPosition,
+ 'variation': faction_ranking.pointsVariation,
+ }) }}
+ {% endfor %}
+
+
diff --git a/templates/components/rankings/faction/territorial_rankings.html.twig b/templates/components/rankings/faction/territorial_rankings.html.twig
new file mode 100644
index 000000000..26220c80b
--- /dev/null
+++ b/templates/components/rankings/faction/territorial_rankings.html.twig
@@ -0,0 +1,18 @@
+
+
+
+ Territorial
+ Nombre de points des secteurs controlés
+
+
+
+ {% for faction_ranking in territorial_rankings %}
+ {{ include('molecules/rankings/faction_ranking_card.html.twig', {
+ faction_ranking,
+ 'value': faction_ranking.territorial|number(-1) ~ ' points',
+ 'position': faction_ranking.territorialPosition,
+ 'variation': faction_ranking.territorialVariation,
+ }) }}
+ {% endfor %}
+
+
diff --git a/templates/components/rankings/faction/victory_info.html.twig b/templates/components/rankings/faction/victory_info.html.twig
new file mode 100644
index 000000000..af33cb47d
--- /dev/null
+++ b/templates/components/rankings/faction/victory_info.html.twig
@@ -0,0 +1,61 @@
+
+
+ Informations sur les conditions de victoire
+
+
+
+
+ Les différents classements (général, richesse et territorial) rapportent des points à la faction chaque jour en fonction de la position de cette dernière dans les classements.
+ Ces points sont additionnés et font monter une jauge. Lorsqu'une faction atteint le nombre de point requis, le serveur se termine et la victoire est revendiquée.
+
+
+
+ {{ points_to_win|number }}
+
+
+
+ Les différents classements n'ont pas tous le même poids.
+ Les gain journaliers en fonction du classement et de la position dans ce dernier sont disponibles dans le tableau ci-dessous.
+
+
+
+
+
+ Position
+ 1er
+ 2ème
+ 3ème
+ 4ème
+ 5ème
+
+
+
+
+ Général
+ +4
+ +3
+ +2
+ +1
+ +0
+
+
+ Richesse
+ +16
+ +12
+ +8
+ +4
+ +0
+
+
+ Territorial
+ +20
+ +15
+ +10
+ +5
+ +0
+
+
+
+
+
+
diff --git a/templates/components/rankings/faction/wealth_rankings.html.twig b/templates/components/rankings/faction/wealth_rankings.html.twig
new file mode 100644
index 000000000..a6202e9c9
--- /dev/null
+++ b/templates/components/rankings/faction/wealth_rankings.html.twig
@@ -0,0 +1,18 @@
+
+
+
+ Richesse
+ Revenu des routes commerciales de la faction
+
+
+
+ {% for faction_ranking in wealth_rankings %}
+ {{ include('molecules/rankings/faction_ranking_card.html.twig', {
+ faction_ranking,
+ 'value': faction_ranking.wealth|number(-1) ~ ' crédits',
+ 'position': faction_ranking.wealthPosition,
+ 'variation': faction_ranking.wealthVariation,
+ }) }}
+ {% endfor %}
+
+
diff --git a/templates/components/rankings/player/armies.html.twig b/templates/components/rankings/player/armies.html.twig
new file mode 100644
index 000000000..6613f3196
--- /dev/null
+++ b/templates/components/rankings/player/armies.html.twig
@@ -0,0 +1,29 @@
+
+
+
+ Armée
+ Total des PEV
+
+
+ {% for player_ranking in armies_rankings %}
+ {{ include('atoms/rankings/top_mover.html.twig', {
+ 'type': 'armies',
+ 'current_position': player_ranking.armiesPosition,
+ loop,
+ }) }}
+
+ {{include('molecules/rankings/player_ranking_card.html.twig', {
+ player_ranking,
+ 'position': player_ranking.armiesPosition,
+ 'variation': player_ranking.armiesVariation,
+ 'value': '',
+ })}}
+
+ {{ include('atoms/rankings/bottom_mover.html.twig', {
+ 'type': 'armies',
+ 'current_position': player_ranking.armiesPosition,
+ loop,
+ }) }}
+ {% endfor %}
+
+
diff --git a/templates/components/rankings/player/best_player.html.twig b/templates/components/rankings/player/best_player.html.twig
new file mode 100644
index 000000000..9f013fcb5
--- /dev/null
+++ b/templates/components/rankings/player/best_player.html.twig
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ {{ get_faction_info(best_player.faction.identifier, 'status')[best_player.status - 1] }} de {{ get_faction_info(best_player.faction.identifier, 'popularName') }}
+
+ {{ best_player.name }}
+
+
+
+
+
+
+
diff --git a/templates/components/rankings/player/butcher.html.twig b/templates/components/rankings/player/butcher.html.twig
new file mode 100644
index 000000000..7d60f92d6
--- /dev/null
+++ b/templates/components/rankings/player/butcher.html.twig
@@ -0,0 +1,30 @@
+
+
+
+ Boucher
+ PEV détruits moins PEV perdus
+
+
+
+ {% for player_ranking in butcher_rankings %}
+ {{ include('atoms/rankings/top_mover.html.twig', {
+ 'type': 'butcher',
+ 'current_position': player_ranking.butcherPosition,
+ loop,
+ }) }}
+
+ {{ include('molecules/rankings/player_ranking_card.html.twig', {
+ player_ranking,
+ 'position': player_ranking.butcherPosition,
+ 'variation': player_ranking.butcherVariation,
+ 'value': player_ranking.butcher|number ~ ' point' ~ player_ranking.butcher|plural,
+ })}}
+
+ {{ include('atoms/rankings/bottom_mover.html.twig', {
+ 'type': 'butcher',
+ 'current_position': player_ranking.butcherPosition,
+ loop,
+ }) }}
+ {% endfor %}
+
+
diff --git a/templates/components/rankings/player/experience.html.twig b/templates/components/rankings/player/experience.html.twig
new file mode 100644
index 000000000..0d25a00be
--- /dev/null
+++ b/templates/components/rankings/player/experience.html.twig
@@ -0,0 +1,29 @@
+
+
+
+ Expérience
+ Total accumulé
+
+
+ {% for player_ranking in experience_rankings %}
+ {{ include('atoms/rankings/top_mover.html.twig', {
+ 'type': 'xp',
+ 'current_position': player_ranking.experiencePosition,
+ loop,
+ }) }}
+
+ {{ include('molecules/rankings/player_ranking_card.html.twig', {
+ player_ranking,
+ 'position': player_ranking.experiencePosition,
+ 'variation': player_ranking.experienceVariation,
+ 'value': player_ranking.experience ~ ' xp',
+ }) }}
+
+ {{ include('atoms/rankings/bottom_mover.html.twig', {
+ 'type': 'xp',
+ 'current_position': player_ranking.experiencePosition,
+ loop,
+ }) }}
+ {% endfor %}
+
+
diff --git a/templates/components/rankings/player/fight.html.twig b/templates/components/rankings/player/fight.html.twig
new file mode 100644
index 000000000..1d8ce31d9
--- /dev/null
+++ b/templates/components/rankings/player/fight.html.twig
@@ -0,0 +1,29 @@
+
+
+
+ Combattant
+ Total des victoires moins les défaites
+
+
+ {% for player_ranking in fight_rankings %}
+ {{ include('atoms/rankings/top_mover.html.twig', {
+ 'type': 'armies',
+ 'current_position': player_ranking.fightPosition,
+ loop,
+ }) }}
+
+ {{include('molecules/rankings/player_ranking_card.html.twig', {
+ player_ranking,
+ 'position': player_ranking.fightPosition,
+ 'variation': player_ranking.fightVariation,
+ 'value': player_ranking.fight|number ~ ' point' ~ player_ranking.fight|plural,
+ })}}
+
+ {{ include('atoms/rankings/bottom_mover.html.twig', {
+ 'type': 'armies',
+ 'current_position': player_ranking.fightPosition,
+ loop,
+ }) }}
+ {% endfor %}
+
+
diff --git a/templates/components/rankings/player/general.html.twig b/templates/components/rankings/player/general.html.twig
new file mode 100644
index 000000000..0c0805032
--- /dev/null
+++ b/templates/components/rankings/player/general.html.twig
@@ -0,0 +1,30 @@
+
+
+
+ Général
+ Total de vos possessions
+
+
+
+ {% for player_ranking in general_rankings %}
+ {{ include('atoms/rankings/top_mover.html.twig', {
+ 'type': 'general',
+ 'current_position': player_ranking.generalPosition,
+ loop,
+ }) }}
+
+ {{ include('molecules/rankings/player_ranking_card.html.twig', {
+ player_ranking,
+ 'position': player_ranking.generalPosition,
+ 'variation': player_ranking.generalVariation,
+ 'value': player_ranking.general|number ~ ' point' ~ player_ranking.general|plural,
+ })}}
+
+ {{ include('atoms/rankings/bottom_mover.html.twig', {
+ 'type': 'general',
+ 'current_position': player_ranking.generalPosition,
+ loop,
+ }) }}
+ {% endfor %}
+
+
diff --git a/templates/components/rankings/player/rankings_part.html.twig b/templates/components/rankings/player/rankings_part.html.twig
new file mode 100644
index 000000000..c1854076c
--- /dev/null
+++ b/templates/components/rankings/player/rankings_part.html.twig
@@ -0,0 +1,26 @@
+{% for player_ranking in player_rankings %}
+ {% set data = get_player_ranking_data_by_type(player_ranking, type) %}
+
+ {% if 'next' == direction and bot > 1 %}
+ {{ include('atoms/rankings/top_mover.html.twig', {
+ type,
+ 'current_position': data.position,
+ loop,
+ }) }}
+ {% endif %}
+
+ {{ include('molecules/rankings/player_ranking_card.html.twig', {
+ player_ranking,
+ 'position': data.position,
+ 'variation': data.variation,
+ 'value': data.value|number ~ ' point' ~ data.value|plural,
+ })}}
+
+ {% if 'prev' == direction and constant('App\\Modules\\Atlas\\Model\\PlayerRanking::PAGE') == player_rankings|length %}
+ {{ include('atoms/rankings/bottom_mover.html.twig', {
+ type,
+ 'current_position': data.position,
+ loop,
+ }) }}
+ {% endif %}
+{% endfor %}
diff --git a/templates/components/rankings/player/resources.html.twig b/templates/components/rankings/player/resources.html.twig
new file mode 100644
index 000000000..f7a521792
--- /dev/null
+++ b/templates/components/rankings/player/resources.html.twig
@@ -0,0 +1,29 @@
+
+
+
+ Mineur
+ Production totale par relève
+
+
+ {% for player_ranking in resources_rankings %}
+ {{ include('atoms/rankings/top_mover.html.twig', {
+ 'type': 'resources',
+ 'current_position': player_ranking.resourcesPosition,
+ loop,
+ }) }}
+
+ {{ include('molecules/rankings/player_ranking_card.html.twig', {
+ player_ranking,
+ 'position': player_ranking.resourcesPosition,
+ 'variation': player_ranking.resourcesVariation,
+ 'value': player_ranking.resources|number ~ ' ',
+ })}}
+
+ {{ include('atoms/rankings/bottom_mover.html.twig', {
+ 'type': 'resources',
+ 'current_position': player_ranking.resourcesPosition,
+ loop,
+ }) }}
+ {% endfor %}
+
+
diff --git a/templates/components/rankings/player/stats.html.twig b/templates/components/rankings/player/stats.html.twig
new file mode 100644
index 000000000..bbc728718
--- /dev/null
+++ b/templates/components/rankings/player/stats.html.twig
@@ -0,0 +1,25 @@
+
+
+
+
+ {{ active_players_count|number }}
+ {# ? #}
+
+
+
+ {{ all_players_count|number }}
+
+
+ Le classement est mis à jour tous les jours à 3h (UTC+1) du matin.
+
+
+
+
+
+ Voir le haut du classement
+
+
+
+
diff --git a/templates/components/rankings/player/trader.html.twig b/templates/components/rankings/player/trader.html.twig
new file mode 100644
index 000000000..31e76633a
--- /dev/null
+++ b/templates/components/rankings/player/trader.html.twig
@@ -0,0 +1,29 @@
+
+
+
+ Trader
+ Revenu de toutes les routes commerciales par relève
+
+
+ {% for player_ranking in trader_rankings %}
+ {{ include('atoms/rankings/top_mover.html.twig', {
+ 'type': 'trader',
+ 'current_position': player_ranking.traderPosition,
+ loop,
+ }) }}
+
+ {{ include('molecules/rankings/player_ranking_card.html.twig', {
+ player_ranking,
+ 'position': player_ranking.traderPosition,
+ 'variation': player_ranking.traderVariation,
+ 'value': player_ranking.trader|number ~ ' ',
+ })}}
+
+ {{ include('atoms/rankings/bottom_mover.html.twig', {
+ 'type': 'trader',
+ 'current_position': player_ranking.traderPosition,
+ loop,
+ }) }}
+ {% endfor %}
+
+
diff --git a/templates/layouts/ares/fleet.html.twig b/templates/layouts/ares/fleet.html.twig
new file mode 100644
index 000000000..11f40f6d0
--- /dev/null
+++ b/templates/layouts/ares/fleet.html.twig
@@ -0,0 +1,17 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block left_sidebar %}
+
+
+
+
+
+
+
+{% endblock left_sidebar %}
+
+{% block body %}
+
+ {% block content %}{% endblock content %}
+
+{% endblock body %}
diff --git a/templates/layouts/athena/current_base.html.twig b/templates/layouts/athena/current_base.html.twig
new file mode 100644
index 000000000..23ea09602
--- /dev/null
+++ b/templates/layouts/athena/current_base.html.twig
@@ -0,0 +1,100 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block left_sidebar %}
+
+
+ {% if current_base.levelGenerator > 0 %}
+
+ {% endif %}
+ {% if current_base.levelDock1 > 0 %}
+
+ {% endif %}
+
+ {% if current_base.levelDock2 > 0 %}
+
+ {% endif %}
+
+ {% if current_base.levelTechnosphere > 0 %}
+
+ {% endif %}
+
+ {% if current_base.levelCommercialPlateforme > 0 %}
+
+ {% endif %}
+
+ {% if current_base.levelSpatioport > 0 %}
+
+ {% endif %}
+
+ {% if current_base.levelRecycling > 0 %}
+
+ {% endif %}
+
+ {% if current_base.levelRefinery > 0 %}
+
+ {% endif %}
+
+ {% if current_base.levelStorage > 0 %}
+
+ {% endif %}
+
+
+
+{% endblock left_sidebar %}
+
+{% block body %}
+
+ {{ block(outerBlocks.content) }}
+
+{% endblock body %}
diff --git a/templates/layouts/atlas/ranking.html.twig b/templates/layouts/atlas/ranking.html.twig
new file mode 100644
index 000000000..b2878b94f
--- /dev/null
+++ b/templates/layouts/atlas/ranking.html.twig
@@ -0,0 +1,18 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block left_sidebar %}
+
+
+
+
+{% endblock left_sidebar %}
+
+{% block body %}
+
+ {{ block(outerBlocks.page_title) }}
+
+
+
+ {{ block(outerBlocks.content) }}
+
+{% endblock body %}
diff --git a/templates/layouts/base.html.twig b/templates/layouts/base.html.twig
new file mode 100644
index 000000000..8fa5d58ba
--- /dev/null
+++ b/templates/layouts/base.html.twig
@@ -0,0 +1,33 @@
+{% set faction_id = (app.session.get('inscription') is not null and app.session.get('inscription').has('ally'))
+ ? app.session.get('inscription').get('ally')
+ : current_player_faction_id is defined ? current_player_faction_id : 0
+%}
+
+
+
+
+
+ {%- block title -%}
+ Kalaxia
+ {%- endblock title -%}
+
+
+
+
+
+
+
+
+ {#
#}
+ {% block stylesheets %}
+
+ {% endblock %}
+
+ {% block javascripts %}
+ {% block importmap %}{{ importmap('app') }}{% endblock %}
+ {% endblock %}
+
+
+ {% block body %}{% endblock body %}
+
+
diff --git a/templates/layouts/demeter/faction.html.twig b/templates/layouts/demeter/faction.html.twig
new file mode 100644
index 000000000..b7bf2d837
--- /dev/null
+++ b/templates/layouts/demeter/faction.html.twig
@@ -0,0 +1,34 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block left_sidebar %}
+
+
+
+ {% if faction.hasOngoingElectionCampaign %}
+
+ {% endif %}
+
+
+
+ {% if current_player.isGovernmentMember %}
+
+ {% endif %}
+
+ {% if current_player.isParliamentMember %}
+
+ {% endif %}
+
+
+
+
+{% endblock left_sidebar %}
+
+{% block body %}
+ {% if block('page_title') is defined and block('page_title') is not empty %}
+
{{ block(outerBlocks.page_title) }}
+ {% endif %}
+
+
+ {{ block(outerBlocks.content) }}
+
+{% endblock body %}
diff --git a/templates/layouts/game.html.twig b/templates/layouts/game.html.twig
new file mode 100644
index 000000000..48e90547f
--- /dev/null
+++ b/templates/layouts/game.html.twig
@@ -0,0 +1,45 @@
+
+
+
+
+ {%- block title -%}
+ Kalaxia
+ {%- endblock title -%}
+
+
+
+
+
+
+
+
+ {% block stylesheets %}
+
+
+ {% endblock %}
+
+ {#
#}
+
+ {% block javascripts %}
+ {% block importmap %}{{ importmap('app') }}{% endblock %}
+ {% endblock %}
+
+
+
+
+
+ {{ include('components/common/navbar.html.twig') }}
+ {{ include('components/common/toolbar.html.twig') }}
+
+ {% block body %}{% endblock body %}
+
+
+ {% block left_sidebar %}{% endblock left_sidebar %}
+
+
+ {% block right_sidebar %}{% endblock right_sidebar %}
+
+ {{ include('components/common/alerts.html.twig') }}
+
+
+
diff --git a/templates/layouts/map.html.twig b/templates/layouts/map.html.twig
new file mode 100644
index 000000000..c9028ca4d
--- /dev/null
+++ b/templates/layouts/map.html.twig
@@ -0,0 +1,43 @@
+
+
+
+
+ {%- block title -%}
+ Kalaxia
+ {%- endblock title -%}
+
+
+
+
+
+
+
+ {% block stylesheets %}
+
+
+ {% endblock %}
+
+ {#
#}
+
+ {% block javascripts %}
+ {% block importmap %}{{ importmap('app') }}{% endblock %}
+ {% endblock %}
+
+
+ {{ include('components/common/navbar.html.twig', { absolute: true }) }}
+
+ {{ include('components/common/toolbar.html.twig') }}
+
+
+ {% block body %}{% endblock body %}
+
+
+ {% block left_sidebar %}{% endblock left_sidebar %}
+
+
+ {% block right_sidebar %}{% endblock right_sidebar %}
+
+ {{ include('components/common/alerts.html.twig') }}
+
+
+
diff --git a/templates/layouts/zeus/registration.html.twig b/templates/layouts/zeus/registration.html.twig
new file mode 100644
index 000000000..04c49b3a0
--- /dev/null
+++ b/templates/layouts/zeus/registration.html.twig
@@ -0,0 +1,42 @@
+{% extends 'layouts/base.html.twig' %}
+
+{% block body %}
+
+
+ {% if app.request.get('_route') == 'registration_choose_faction' %}
+ {% set factionChoiceStatus = 'current' %}
+ {% set profileStatus = 'next' %}
+ {% set placeChoiceStatus = 'next' %}
+ {% elseif app.request.get('_route') == 'registration_create_character' %}
+ {% set factionChoiceStatus = 'done' %}
+ {% set profileStatus = 'current' %}
+ {% set placeChoiceStatus = 'next' %}
+ {% elseif app.request.get('_route') == 'registration_choose_place' %}
+ {% set factionChoiceStatus = 'done' %}
+ {% set profileStatus = 'done' %}
+ {% set placeChoiceStatus = 'current' %}
+ {% endif %}
+
+ {{ component('FormStep', {
+ status: factionChoiceStatus,
+ label: 'Choisissez votre faction',
+ number: 1,
+ }) }}
+ {{ component('FormStep', {
+ status: profileStatus,
+ label: 'Créez votre personnage',
+ number: 2,
+ }) }}
+ {{ component('FormStep', {
+ status: placeChoiceStatus,
+ label: 'Choisissez votre secteur de départ',
+ number: 3,
+ isLast: true,
+ }) }}
+
+
+
+
+ {% block content %}{% endblock content %}
+
+{% endblock body %}
diff --git a/templates/molecules/base/production/storage_gauge.html.twig b/templates/molecules/base/production/storage_gauge.html.twig
new file mode 100644
index 000000000..d360e3a6d
--- /dev/null
+++ b/templates/molecules/base/production/storage_gauge.html.twig
@@ -0,0 +1,3 @@
+
+ {{ stored_resources|number }}
+
diff --git a/templates/molecules/base/trade/commercial_route_base.html.twig b/templates/molecules/base/trade/commercial_route_base.html.twig
new file mode 100644
index 000000000..1b31fde6e
--- /dev/null
+++ b/templates/molecules/base/trade/commercial_route_base.html.twig
@@ -0,0 +1,12 @@
+
diff --git a/templates/molecules/base/trade/commercial_shipping.html.twig b/templates/molecules/base/trade/commercial_shipping.html.twig
new file mode 100644
index 000000000..8995b76a6
--- /dev/null
+++ b/templates/molecules/base/trade/commercial_shipping.html.twig
@@ -0,0 +1,88 @@
+
+ {% if not commercial_shipping.isMovingBack %}
+
+
+ {% if commercial_shipping.hasResources %}
+ {#
#}
+
+ {# @TODO seems that resources selling isn't sure about the used property #}
+ {# resourceTransported is used when giving resources, quantity is used when selling. @TODO use only one field #}
+ {{ (commercial_shipping.resourceTransported == 0 ? commercial_shipping.transaction.quantity : commercial_shipping.resourceTransported)|number }}
+
+
+ {% elseif commercial_shipping.hasCommander %}
+ {#
#}
+
+ {{ commercial_shipping.transaction.commander|commander_rank }} {{ commercial_shipping.transaction.commander.name }}
+ {{ commercial_shipping.transaction.commander.experience|number }} xp | '{{ commercial_shipping.transaction.commander.palmares }} victoire{{ commercial_shipping.transaction.commander.palmares|plural }}
+
+ {% elseif commercial_shipping.hasShips %}
+ {#
#}
+
+
+ {{ commercial_shipping.transaction.quantity }}
+ {{ get_ship_info(commercial_shipping.transaction.identifier, 'codeName') }}{{ commercial_shipping.transaction.quantity|plural }}
+
+ {{ get_ship_info(commercial_shipping.transaction.identifier, 'name') }} / {{ get_ship_info(commercial_shipping.transaction.identifier, 'pev') }} pev
+
+ {% endif %}
+
+ {% if commercial_shipping.resourceTransported is null %}
+ {# transaction #}
+
+ pour
+
+
+ {{ commercial_shipping.transaction.price|number }}
+
+
+ {% elseif commercial_shipping.resourceTransported == 0 %}
+ {# ships sending #}
+
+
+ envoi de vaisseaux
+
+ {% else %}
+ {# resources sending #}
+
+
+ envoi de ressources
+
+ {% endif %}
+
+
+ {% if commercial_shipping.isWaiting %}
+
+ {% endif %}
+
+ {% endif %}
+
+ {% if not commercial_shipping.isWaiting %}
+
+
+
+
+ {% else %}
+
+ {% endif %}
+
+
+ {{ commercial_shipping.shipQuantity }}
+ {#
#}
+
+
+ {% if commercial_shipping.isWaiting %}
+
à quai
+ {% else %}
+
+ {% endif %}
+
+
diff --git a/templates/molecules/common/advertising.html.twig b/templates/molecules/common/advertising.html.twig
new file mode 100644
index 000000000..c39d020b8
--- /dev/null
+++ b/templates/molecules/common/advertising.html.twig
@@ -0,0 +1,5 @@
+
diff --git a/templates/molecules/common/default.html.twig b/templates/molecules/common/default.html.twig
new file mode 100644
index 000000000..6e36f2696
--- /dev/null
+++ b/templates/molecules/common/default.html.twig
@@ -0,0 +1,6 @@
+
diff --git a/templates/molecules/common/movers.html.twig b/templates/molecules/common/movers.html.twig
new file mode 100644
index 000000000..d97522311
--- /dev/null
+++ b/templates/molecules/common/movers.html.twig
@@ -0,0 +1,4 @@
+
diff --git a/templates/molecules/common/subnav.html.twig b/templates/molecules/common/subnav.html.twig
new file mode 100644
index 000000000..0426d557a
--- /dev/null
+++ b/templates/molecules/common/subnav.html.twig
@@ -0,0 +1,4 @@
+
diff --git a/templates/molecules/embassy/faction_flag.html.twig b/templates/molecules/embassy/faction_flag.html.twig
new file mode 100644
index 000000000..ecf7f345a
--- /dev/null
+++ b/templates/molecules/embassy/faction_flag.html.twig
@@ -0,0 +1,17 @@
+
+
+ {{ get_faction_info(faction.identifier, 'officialName') }}
+
+
+
+ "{{ get_faction_info(faction.identifier, 'devise')|raw }}"
+
+
+
+
+ {{ get_faction_info(faction.identifier, 'desc1')|raw }}
+ {{ get_faction_info(faction.identifier, 'desc2')|raw }}
+ {{ get_faction_info(faction.identifier, 'desc3')|raw }}
+ {{ get_faction_info(faction.identifier, 'desc4')|raw }}
+
+
diff --git a/templates/molecules/embassy/faction_government.html.twig b/templates/molecules/embassy/faction_government.html.twig
new file mode 100644
index 000000000..32f0b96b6
--- /dev/null
+++ b/templates/molecules/embassy/faction_government.html.twig
@@ -0,0 +1,47 @@
+{% set statuses = get_faction_info(faction.identifier, 'status') %}
+
+
+
+
+
+
+ {% for minister in government_members %}
+
+
+
+
+
+ {{ statuses[minister.status - 1] }}
+ {{ minister.name }}
+
+
+ {% endfor %}
+
+ {% if government_members|length == 0 %}
+
Aucun gouvernement formé
+ {% endif %}
+
+
Informations
+
+
+ {{ faction.points|number }}
+
+
+
+ {{ faction.credits|number }}
+
+
+
+ {{ sectors_count }}
+
+
+
+ {{ active_players_count }}
+
+
+
+
diff --git a/templates/molecules/embassy/faction_infos.html.twig b/templates/molecules/embassy/faction_infos.html.twig
new file mode 100644
index 000000000..25ad6941b
--- /dev/null
+++ b/templates/molecules/embassy/faction_infos.html.twig
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/templates/molecules/embassy/faction_nav.html.twig b/templates/molecules/embassy/faction_nav.html.twig
new file mode 100644
index 000000000..587f7e528
--- /dev/null
+++ b/templates/molecules/embassy/faction_nav.html.twig
@@ -0,0 +1,19 @@
+
+
+
diff --git a/templates/molecules/embassy/player_bases.html.twig b/templates/molecules/embassy/player_bases.html.twig
new file mode 100644
index 000000000..7821f039b
--- /dev/null
+++ b/templates/molecules/embassy/player_bases.html.twig
@@ -0,0 +1,21 @@
+
+
+
+ {% for base in player_bases %}
+
+
+ {{ base.name }}
+ secteur {{ base.place.system.sector.identifier }}
+
+
+
+
+
+
+
+
+ {% endfor %}
+
+
diff --git a/templates/molecules/embassy/player_search.html.twig b/templates/molecules/embassy/player_search.html.twig
new file mode 100644
index 000000000..a485e9eeb
--- /dev/null
+++ b/templates/molecules/embassy/player_search.html.twig
@@ -0,0 +1,32 @@
+{% set faction_statuses = get_faction_info(player.faction.identifier, 'status') %}
+
+
+
+
+
+
+
+
+
+
+
+
{{ player.name }}
+
{{ faction_statuses[player.status - 1] }} de {{ get_faction_info(player.faction.identifier, 'popularName') }}
+
niveau {{ player.level }}
+
+ {% if player.id != current_player.id %}
+
+
Envoyer un message
+ {% endif %}
+
+
+
+
diff --git a/templates/molecules/faction/government/law.html.twig b/templates/molecules/faction/government/law.html.twig
new file mode 100644
index 000000000..db9d30b88
--- /dev/null
+++ b/templates/molecules/faction/government/law.html.twig
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
{{ get_law_info(law_id, 'name') }}
+
+
+
{{ get_law_info(law_id, 'shortDescription')|raw }}
+
+
+
+
+
{{ get_law_info(law_id, 'longDescription')|raw }}
+
+
+
diff --git a/templates/molecules/faction/senate/law.html.twig b/templates/molecules/faction/senate/law.html.twig
new file mode 100644
index 000000000..6419970a6
--- /dev/null
+++ b/templates/molecules/faction/senate/law.html.twig
@@ -0,0 +1,72 @@
+{% set law_duration = get_law_duration(law) %}
+
+
+
+
+
+
Object de la votation
+
+
+
+
+
{{ get_law_info(law.type, 'name') }}
+
+
+
{{ get_law_info(law.type, 'shortDescription')|raw }}
+
+
+ {% if has_voted_law(law, current_player) %}
+
+ Vous avez déjà voté
+
+ {% elseif current_player.isSenator %}
+
+ Pour
+
+
+
+ Contre
+
+ {% else %}
+
+ Seuls les sénateurs peuvent voter
+
+ {% endif %}
+
+
+
Modalités d'application
+
+
+
+ Coût
+
+ {% if get_law_info(law.type, 'bonusLaw') %}
+ {{ (get_law_info(law.type, 'price') * active_players_count * law_duration)|number }}
+ {% else %}
+ {{ get_law_info(law.type, 'price')|number }}
+ {% endif %}
+
+
+
+ {% if get_law_info(law.type, 'bonusLaw') %}
+
+ Durée d'application
+ {{ law_duration|number }} relève{{ law_duration|plural }}
+
+ {% elseif law.options.display is defined %}
+ {% for label in law.options.display|keys %}
+ {% set value = law.options.display[label] %}
+
+
+ {{ label }}
+ {{ value }}
+
+ {% endfor %}
+ {% endif %}
+
+
+
Date application
+
Mise en application {{ law.voteEndedAt|game_date|raw }}
+
+
+
diff --git a/templates/molecules/financial/subnav.html.twig b/templates/molecules/financial/subnav.html.twig
new file mode 100644
index 000000000..8398da3bd
--- /dev/null
+++ b/templates/molecules/financial/subnav.html.twig
@@ -0,0 +1,17 @@
+
diff --git a/templates/molecules/fleet/combat_report_table.html.twig b/templates/molecules/fleet/combat_report_table.html.twig
new file mode 100644
index 000000000..a222bb218
--- /dev/null
+++ b/templates/molecules/fleet/combat_report_table.html.twig
@@ -0,0 +1,51 @@
+{% set pev_begin = 0 %}
+{% set pev_difference = 0 %}
+{% set pev_end = 0 %}
+
+{% for i in range(0, 11) %}
+ {% set pev_begin = pev_begin + total_in_begin[i] * get_ship_info(i, 'pev') %}
+ {% set pev_difference = pev_difference + difference[i] * get_ship_info(i, 'pev') %}
+ {% set pev_end = pev_end + total_at_end[i] * get_ship_info(i, 'pev') %}
+{% endfor %}
+
+
+
+
+ Evolution des PEV
+ {{ pev_begin == 0 ? '—' : pev_begin }}
+ {{ pev_difference == 0 ? '—' : pev_difference }}
+ {{ pev_end == 0 ? '—' : pev_end }}
+
+
+
+
+
+
+ {% for i in range(0, 11) %}
+
+ {{ get_ship_info(i, 'name') }}
+
+ {%- if total_in_begin[i] > 0 -%}
+ {{ total_in_begin[i] }}
+ {%- else -%}
+ —
+ {%- endif -%}
+
+
+ {%- if difference[i] > 0 -%}
+ {{ difference[i] }}
+ {%- else -%}
+ —
+ {%- endif -%}
+
+
+ {%- if total_at_end[i] > 0 -%}
+ {{ total_at_end[i] }}
+ {%- else -%}
+ —
+ {%- endif -%}
+
+
+ {% endfor %}
+
+
diff --git a/templates/molecules/fleet/memorial_description.html.twig b/templates/molecules/fleet/memorial_description.html.twig
new file mode 100644
index 000000000..3365386fe
--- /dev/null
+++ b/templates/molecules/fleet/memorial_description.html.twig
@@ -0,0 +1,13 @@
+
+
+
Mémorial
+
+
+
+
En l'honneur de ceux qui ont servi notre faction.
+ En l'honneur de ceux qui sont morts dignement.
+ En l'honneur de ceux qui ont souffert pour nous libérer de l'oppression.
+ Nous leur devons le respect.
+
+
+
diff --git a/templates/molecules/map/attacks.html.twig b/templates/molecules/map/attacks.html.twig
new file mode 100644
index 000000000..d49dabb72
--- /dev/null
+++ b/templates/molecules/map/attacks.html.twig
@@ -0,0 +1,26 @@
+{% set fleet_param = constant('App\\Classes\\Container\\Params::SHOW_MAP_FLEETIN') %}
+
+
+
+ {% for commander in attacking_commanders|filter(c => c.travelType != constant('App\\Modules\\Ares\\Model\\Commander::BACK')) %}
+ {% set start_place = commander.startPlace %}
+ {% set destination_place = commander.destinationPlace %}
+ {% set x1 = start_place.system.xPosition * galaxy_configuration.scale %}
+ {% set x2 = destination_place.system.xPosition * galaxy_configuration.scale %}
+ {% set y1 = start_place.system.yPosition * galaxy_configuration.scale %}
+ {% set y2 = destination_place.system.yPosition * galaxy_configuration.scale %}
+ {% set commander_position = get_commander_position(commander, x1, x2, y1, y2) %}
+ {% set x3 = commander_position[0] %}
+ {% set y3 = commander_position[1] %}
+ {% set remaining_time = commander|travel_remaining_time %}
+
+
+
+
+
+
+ {% endfor %}
+
+
diff --git a/templates/molecules/map/commanders.html.twig b/templates/molecules/map/commanders.html.twig
new file mode 100644
index 000000000..372b22aad
--- /dev/null
+++ b/templates/molecules/map/commanders.html.twig
@@ -0,0 +1,56 @@
+{% set break = false %}
+
+
+ {% for commander in local_commanders %}
+
+
+
+ {{ commander.level }}
+
+
+
+ {{ commander|commander_rank }} {{ commander.name }}
+ {{ commander|pev|number }} pev
+
+ {% if commander.isMoving %}
+ {# @TODO Implement translator to replace the switch #}
+ {% if commander.isTransferring %}
+ Déplacement
+ {% elseif commander.isLooting %}
+ Pillage
+ {% elseif commander.isInvading %}
+ Colonisation
+ {% elseif commander.isComingBack %}
+ Retour
+ {% endif %}
+ {% else %}
+ A quai
+ {% endif %}
+
+
+
+ {% set ships_by_type = commander.nbrShipByType %}
+ {% for type in ships_by_type|keys %}
+ {% set quantity = ships_by_type[type] %}
+
+ {{ quantity }}
+
+ {% endfor %}
+
+
+
+
+ {% endfor %}
+
diff --git a/templates/molecules/map/commercial_routes.html.twig b/templates/molecules/map/commercial_routes.html.twig
new file mode 100644
index 000000000..3bce11502
--- /dev/null
+++ b/templates/molecules/map/commercial_routes.html.twig
@@ -0,0 +1,18 @@
+{% set commercial_routes_param = constant('App\\Classes\\Container\\Params::SHOW_MAP_RC') %}
+
+{# @TODO use a correct DTO for this view #}
+
+
+ {% for route in commercial_routes %}
+ {% set class = route.statement == constant('App\\Modules\\Athena\\Model\\CommercialRoute::ACTIVE') ? 'active' : 'standby' %}
+
+
+ {% endfor %}
+
+
diff --git a/templates/molecules/map/current_base.html.twig b/templates/molecules/map/current_base.html.twig
new file mode 100644
index 000000000..adad9988c
--- /dev/null
+++ b/templates/molecules/map/current_base.html.twig
@@ -0,0 +1,26 @@
+
+
+ {% for base in player_bases %}
+ {% set base_system = base.place.system %}
+ {% set centerX = base_system.xPosition * galaxy_configuration.scale %}
+ {% set centerY = base_system.yPosition * galaxy_configuration.scale %}
+
+
+
+ {% if base.id == current_base.id %}
+
+
+
+
+
+
+ {% else %}
+
+ {% endif %}
+ {% endfor %}
+
+
diff --git a/templates/molecules/map/fleet_movements.html.twig b/templates/molecules/map/fleet_movements.html.twig
new file mode 100644
index 000000000..6e81f8047
--- /dev/null
+++ b/templates/molecules/map/fleet_movements.html.twig
@@ -0,0 +1,26 @@
+{% set fleet_param = constant('App\\Classes\\Container\\Params::SHOW_MAP_FLEETOUT') %}
+
+
+
+ {% for commander in moving_commanders|filter(c => c.destinationPlace is not null) %}
+ {% set start_place = commander.startPlace %}
+ {% set destination_place = commander.destinationPlace %}
+ {% set x1 = start_place.system.xPosition * galaxy_configuration.scale %}
+ {% set x2 = destination_place.system.xPosition * galaxy_configuration.scale %}
+ {% set y1 = start_place.system.yPosition * galaxy_configuration.scale %}
+ {% set y2 = destination_place.system.yPosition * galaxy_configuration.scale %}
+ {% set commander_position = get_commander_position(commander, x1, x2, y1, y2) %}
+ {% set x3 = commander_position[0] %}
+ {% set y3 = commander_position[1] %}
+ {% set remaining_time = commander|travel_remaining_time %}
+
+
+
+
+
+
+ {% endfor %}
+
+
diff --git a/templates/molecules/map/legend.html.twig b/templates/molecules/map/legend.html.twig
new file mode 100644
index 000000000..f6ea985a3
--- /dev/null
+++ b/templates/molecules/map/legend.html.twig
@@ -0,0 +1,40 @@
+
+
Légende
+
+
Type de système
+
+
+ Cimetière Spatial
+
+
+
+ Nébuleuse
+
+
+
+ Géante Bleue
+
+
+
+ Naine Jaune
+
+
+
+ Naine Rouge
+
+
+
+
+
Revendication
+
+
+ Non revendiquée
+
+
+
+ {% for faction_id in range(1, 12) %}
+ {{ get_faction_info(faction_id, 'officialName') }}
+
+ {% endfor %}
+
+
diff --git a/templates/molecules/map/minimap/filters.html.twig b/templates/molecules/map/minimap/filters.html.twig
new file mode 100644
index 000000000..e0c1dfb64
--- /dev/null
+++ b/templates/molecules/map/minimap/filters.html.twig
@@ -0,0 +1,43 @@
+
+ {% set filters_data = [
+ {
+ 'picto': 'build/media/map/option/minimap.png',
+ 'param': constant('App\\Classes\\Container\\Params::SHOW_MAP_MINIMAP'),
+ 'tooltip': 'afficher/cacher la petite carte'
+ },
+ {
+ 'picto': 'build/media/orbitalbase/commercialplateforme.png',
+ 'param': constant('App\\Classes\\Container\\Params::SHOW_MAP_RC'),
+ 'tooltip': 'afficher/cacher vos routes commerciales'
+ },
+ {
+ 'picto': 'build/media/orbitalbase/antispy.png',
+ 'param': constant('App\\Classes\\Container\\Params::SHOW_MAP_ANTISPY'),
+ 'tooltip': 'afficher/cacher vos cercles de contre-espionnage'
+ },
+ {
+ 'picto': 'build/media/fleet/general-quarter.png',
+ 'param': constant('App\\Classes\\Container\\Params::SHOW_MAP_FLEETOUT'),
+ 'tooltip': 'afficher/cacher vos mouvements de flotte'
+ },
+ {
+ 'picto': 'build/media/fleet/movement.png',
+ 'param': constant('App\\Classes\\Container\\Params::SHOW_MAP_FLEETIN'),
+ 'tooltip': 'afficher/cacher les attaques entrantes'
+ },
+ ] %}
+ {% for filter_data in filters_data %}
+ {{ include('atoms/map/minimap/filter.html.twig', filter_data) }}
+ {% endfor %}
+
+
+
+
+
diff --git a/templates/molecules/map/minimap/minimap.html.twig b/templates/molecules/map/minimap/minimap.html.twig
new file mode 100644
index 000000000..d151b7660
--- /dev/null
+++ b/templates/molecules/map/minimap/minimap.html.twig
@@ -0,0 +1,36 @@
+{% set rate = 400 / galaxy_configuration.galaxy.size %}
+{% set minimap_param = constant('App\\Classes\\Container\\Params::SHOW_MAP_MINIMAP') %}
+
+
+
+
+ {% for sector in sectors %}
+
+ {% endfor %}
+
+
+ {% for sector in sectors %}
+ {% set i = sector.identifier - 1 %}
+ {% set display_params = galaxy_configuration.sectors[i].display %}
+
+ {{ sector.identifier }}
+
+ {% endfor %}
+
+
+ {% for base in player_bases %}
+ {% set base_system = base.place.system %}
+
+ {% endfor %}
+
+
+
+
diff --git a/templates/molecules/map/place_actions.html.twig b/templates/molecules/map/place_actions.html.twig
new file mode 100644
index 000000000..97d6d9117
--- /dev/null
+++ b/templates/molecules/map/place_actions.html.twig
@@ -0,0 +1,293 @@
+{% set travel_time = get_travel_time(current_base, place)|lite_seconds %}
+
+{% apply spaceless %}
+
+
+ {% if place.typeOfPlace == 1 %}
+ {% if place.player is null and place.typeOfPlace != 1 %}
+ {% set tooltip = 'Vous ne pouvez pas attaquer une planète non-habitable' %}
+ {% elseif place.typeOfPlace == 1 and place.player is not null and place.player.faction.id == current_player.faction.id %}
+ {% set tooltip = 'Vous ne pouvez pas attaquer un joueur de votre faction' %}
+ {% elseif place.typeOfPlace == 1 and place.player is not null and place.player.level == 1 and place.player is not null %}
+ {% set tooltip = 'Ce joueur est sous protection débutant' %}
+ {% else %}
+ {% set tooltip = '' %}
+ {% endif %}
+
+
+ {% set max_bases = technologies.getTechnology(constant('App\\Modules\\Promethee\\Model\\TechnologyId::BASE_QUANTITY')) + 1 %}
+ {% set total_bases = get_player_bases_count(moving_commanders) %}
+
+ {% if place.player is null and place.typeOfPlace != 1 %}
+ {% set tooltip = 'Vous ne pouvez pas coloniser une planète non-habitable.' %}
+ {% elseif place.typeOfPlace == 1 and place.player is not null and place.player.faction.id == current_player.faction.id %}
+ {% set tooltip = 'Vous ne pouvez pas conquérir un joueur de votre faction.' %}
+ {% elseif place.typeOfPlace == 1 and place.player is not null and place.player.level <= 3 and place.player.level > 0 %}
+ {% set tooltip = 'Vous ne pouvez pas conquérir un joueur de niveau 3 ou inférieur.' %}
+ {% elseif place.player is null and technologies.getTechnology(constant('App\\Modules\\Promethee\\Model\\TechnologyId::COLONIZATION')) == 0 %}
+ {% set tooltip = 'Vous devez développer la technologie colonisation.' %}
+ {% elseif place.player is not null and technologies.getTechnology(constant('App\\Modules\\Promethee\\Model\\TechnologyId::COLONIZATION')) == 0 %}
+ {% set tooltip = 'Vous devez développer la technologie conquête.' %}
+ {% elseif total_bases >= max_bases %}
+ {% set tooltip = 'Vous devez améliorer le niveau de la technologie administration étendue pour disposer de planète supplémentaire.' %}
+ {% else %}
+ {% set tooltip = '' %}
+ {% endif %}
+
+
+
+
+ {% if place.id == current_base.id %}
+ {% set tooltip = 'Vous ne pouvez pas déplacer une flotte sur votre planète de départ' %}
+ {% elseif place.player is not null and place.player.faction.id != current_player.faction.id %}
+ {% set tooltip = 'Vous ne pouvez donner une de vos flotte qu\'a un membre de votre faction' %}
+ {% else %}
+ {% set tooltip = '' %}
+ {% endif %}
+
+
+
+ {% if place.player is null %}
+ {% set tooltip = 'Vous ne pouvez proposer une route commerciale qu\'à des joueurs.' %}
+ {% elseif place.id == current_base.place.id %}
+ {% set tooltip = 'Vous ne pouvez pas proposer une route commerciale sur votre propre base.' %}
+ {% elseif current_base.levelSpatioport == 0 %}
+ {% set tooltip = 'Il vous faut un spatioport pour proposer une route commerciale.' %}
+ {% elseif place.base.levelSpatioport == 0 %}
+ {% set tooltip = 'Le joueur ne dispose pas d\'un spatioport.' %}
+ {% else %}
+ {% set tooltip = '' %}
+ {% endif %}
+
+
+
+ {% if place.player is not null and place.player.faction.id == current_player.faction.id %}
+ {% set tooltip = 'Vous ne pouvez pas espionner un joueur de votre faction' %}
+ {% elseif place.player is null and place.typeOfPlace != 1 %}
+ {% set tooltip = 'Vous ne pouvez pas espionner une planète non-habitable' %}
+ {% else %}
+ {% set tooltip = '' %}
+ {% endif %}
+
+ {% else %}
+ {% if place.system.sector.faction is not null and place.system.sector.faction.id != current_player.faction.id %}
+ {% set tooltip = 'Vous ne pouvez envoyer des recycleurs que dans des secteurs non-revendiqués ou contrôlés par votre faction.' %}
+ {% elseif place.typeOfPlace == constant('App\\Modules\\Gaia\\Model\\Place::EMPTYZONE') %}
+ {% set tooltip = 'Cet endroit regorgait autrefois de ressources ou de gaz mais de nombreux recycleurs sont déjà passés par là et n\'ont laissé que le vide de l\'espace.' %}
+ {% elseif current_base.levelRecycling == 0 %}
+ {% set tooltip = 'Vous devez disposer d\'un centre de recyclage.' %}
+ {% else %}
+ {% set tooltip = '' %}
+ {% endif %}
+
+
+ {% endif %}
+
+
+
+ {% if place.typeOfPlace == 1 %}
+
+
+
+ {{ parent() }}
+
+
+
+
+
+
+ {% if place.player is null %}
+ {% set cost = colonization_cost|number %}
+ {% set action_url = path('colonize', {'id': '{id}', 'placeId': place.id}) %}
+ {% set label = 'Lancer la colonisation' %}
+ {% else %}
+ {% set cost = conquest_cost|number %}
+ {% set action_url = path('conquer', {'id': '{id}', 'placeId': place.id}) %}
+ {% set label = 'Lancer la conquête' %}
+ {% endif %}
+
+
+
+ {{ parent() }}
+
+
+
+
+
+
+
+
+
+ {{ parent() }}
+
+ {% if place.player is not null and place.player.id != current_player.id %}
+
+ Attention, vous perdrez votre flotte !
+
+ {% endif %}
+
+
+
+ {{ parent() }}
+
+
+
+
+ {% if place.base is not null %}
+
+
+ {% set commercial_route_data = get_commercial_route_data(current_base, place) %}
+ {% set distance = get_place_distance(current_base, place) %}
+
+ {% set price = get_route_price(distance) %}
+ {% set income = get_route_income(current_base, place.base) %}
+
+
+ {#
#}
+
+
+
+ {{ income|number }}
+
+
+
+
+ {{ price|number }}
+
+
+
+
+ {% if commercial_route_data.proposed == true %}
+
+
+ Annuler la proposition
+
+
+ {% elseif commercial_route_data.not_accepted == true %}
+
+
+ Accepter la proposition
+
+
+ {% elseif commercial_route_data.stand_by == true %}
+
+ C'est la guerre.
+
+ {% else %}
+ {% if price > current_player.credit %}
+
+ Vous n'avez pas assez de crédits.
+
+ {% elseif commercial_route_data.slots < get_building_info(constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::SPATIOPORT'), 'level', current_base.levelSpatioport, 'nbRoutesMax') %}
+
+
+ Proposer une route commerciale
+
+
+ {% else %}
+
+ Pas assez de slots.
+
+ {% endif %}
+ {% endif %}
+
+
+
+ {% endif %}
+
+
+
+ {% set prices = {
+ 'Impact faible': 1000,
+ 'Impact moyen': 2500,
+ 'Grand impact': 5000
+ } %}
+
+ {% for label in prices|keys %}
+ {% set price = prices[label] %}
+
+ {{ label }}
+
+ {{ price|number }}
+
+
+
+ {% endfor %}
+
+
+
+
+ {% else %}
+
+
+ {% set total_ships = get_building_info(
+ constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::RECYCLING'),
+ 'level',
+ current_base.levelRecycling,
+ 'nbRecyclers',
+ ) %}
+ {# @TODO move this logic to a dedicated service #}
+ {% set active_ships = recycling_missions|reduce((carry, rm) => carry + rm.recyclerQuantity + rm.addToNextMission) %}
+ {% set available_recyclers = total_ships - active_ships %}
+
+
+ {{ available_recyclers|number }}
+
+
+
+ {{ get_recycling_mission_time(current_base, place)|lite_seconds }}
+
+
+
+
+
+ {% endif %}
+
+
+{% endapply %}
diff --git a/templates/molecules/map/spying_circles.html.twig b/templates/molecules/map/spying_circles.html.twig
new file mode 100644
index 000000000..2c5d36b08
--- /dev/null
+++ b/templates/molecules/map/spying_circles.html.twig
@@ -0,0 +1,16 @@
+{% set antispy_param = constant('App\\Classes\\Container\\Params::SHOW_MAP_ANTISPY') %}
+
+
+
+ {% for base in player_bases %}
+ {% set base_system = base.place.system %}
+ {% set spy_radius = get_base_antispy_radius(base) %}
+ {% set centerX = base_system.xPosition * galaxy_configuration.scale %}
+ {% set centerY = base_system.yPosition * galaxy_configuration.scale %}
+
+
+
+
+ {% endfor %}
+
+
diff --git a/templates/molecules/map/systems.html.twig b/templates/molecules/map/systems.html.twig
new file mode 100644
index 000000000..7fd5cf794
--- /dev/null
+++ b/templates/molecules/map/systems.html.twig
@@ -0,0 +1,29 @@
+
+ {% set current_player_bases_sector_ids = player_bases|map(b => b.place.system) %}
+
+ {% for system in systems %}
+ {% set has_bases_in = system.id in current_player_bases_sector_ids %}
+
+ {#
#}
+
+
+ {% endfor %}
+
diff --git a/templates/molecules/rankings/faction_ranking_card.html.twig b/templates/molecules/rankings/faction_ranking_card.html.twig
new file mode 100644
index 000000000..162740f71
--- /dev/null
+++ b/templates/molecules/rankings/faction_ranking_card.html.twig
@@ -0,0 +1,28 @@
+{% extends 'components/Molecules/Ranking/RankingCard.html.twig' %}
+
+{% block picture %}
+
+
+
+{% endblock picture %}
+
+{% block name %}
+
+ {{ get_faction_info(faction_ranking.faction.identifier, 'government') }}
+
+
+ {{ get_faction_info(faction_ranking.faction.identifier, 'popularName') }}
+
+{% endblock name %}
+
+{% block value %}
+ {{ value|raw }}
+{% endblock value %}
+
+{% block extra_classes current_player.faction.id == faction_ranking.faction.id ? 'active' : '' %}
+
+{% block position_class current_player.faction.id == faction_ranking.faction.id ? 'bg-gray-200 text-gray-900' : 'bg-gray-800' %}
+
+{% block variation %}
+ {{ include('atoms/rankings/faction_position.html.twig', { position, variation })}}
+{% endblock variation %}
diff --git a/templates/molecules/rankings/player_ranking_card.html.twig b/templates/molecules/rankings/player_ranking_card.html.twig
new file mode 100644
index 000000000..5fd8ff2ac
--- /dev/null
+++ b/templates/molecules/rankings/player_ranking_card.html.twig
@@ -0,0 +1,24 @@
+{% extends 'components/Molecules/Ranking/RankingCard.html.twig' %}
+
+{% set statuses = get_faction_info(player_ranking.player.faction.identifier, 'status') %}
+
+{% block picture %}
+
+
+
+{% endblock picture %}
+
+{% block name %}
+
{{ statuses[player_ranking.player.status - 1] }}
+
{{ player_ranking.player.name }}
+{% endblock name %}
+
+{% block value value|raw %}
+
+{% block extra_classes 'border-l-8 my-2 border-medium' ~ player_ranking.player.faction.identifier ~ current_player.id == player_ranking.player.id ? ' active' : '' %}
+
+{% block position_class current_player.id == player_ranking.player.id ? 'bg-gray-200 text-gray-900' : 'bg-gray-800' %}
+
+{% block variation %}
+ {{ include('atoms/rankings/faction_position.html.twig', { position, variation })}}
+{% endblock variation %}
diff --git a/templates/molecules/registration/invisible.html.twig b/templates/molecules/registration/invisible.html.twig
new file mode 100644
index 000000000..8fa7fa70a
--- /dev/null
+++ b/templates/molecules/registration/invisible.html.twig
@@ -0,0 +1,6 @@
+{# rename this file #}
+
diff --git a/templates/molecules/registration/subnav.html.twig b/templates/molecules/registration/subnav.html.twig
new file mode 100644
index 000000000..467facb9a
--- /dev/null
+++ b/templates/molecules/registration/subnav.html.twig
@@ -0,0 +1,20 @@
+
+
+
+
+ {% if app.session.get('inscription').exist('pseudo') %}
+ {{- app.session.get('inscription').get('pseudo') -}}
+ {% endif %}
+
+ {% if app.session.get('inscription').exist('avatar') %}
+
+ {% else %}
+
+ {% endif %}
+
1
+
+
+
+
+
+
diff --git a/templates/molecules/technology/technology_card.html.twig b/templates/molecules/technology/technology_card.html.twig
new file mode 100644
index 000000000..3a3bf35eb
--- /dev/null
+++ b/templates/molecules/technology/technology_card.html.twig
@@ -0,0 +1,87 @@
+
+
+ +
+ {# #}
+
+ {{ get_technology_info(data.identifier, 'name') }}
+ {% if not data.is_unblocking_technology and data.technology_level != 0 %}
+ [{{ data.technology_level }}]
+ {% endif %}
+
+ {% if data.is_over %}
+ développement terminé
+ {% endif %}
+
+
+ {#
#}
+
+
+
+ {% if not data.technosphere_requirements %}
+
+ il vous faut augmenter votre technosphère au niveau {{ get_technology_info(data.identifier, 'requiredTechnosphere') }}
+
+ {% elseif not data.research_requirements is same as (true) %}
+
+ nécessite
+ {% for requirement in data.research_requirements.all %}
+ {{ requirement.get('techno') }} {{ requirement.get('level') }}
+ {% if not loop.last %},{% endif %}
+ {% endfor %}
+
+ {% else %}
+ {% if data.in_queue %}
+
+ technologie en cours de recherche
+ {% if data.is_unblocking_technology %}
+ vers le niveau {{ data.next_level }}
+ {% endif %}
+ {% if not data.in_local_queue %}
+ sur une autre de vos bases
+ {% endif %}
+
+ {% elseif data.is_over %}
+ {% elseif not data.is_unblocking_technology and not data.max_level_requirements %}
+
+ niveau maximum atteint
+
+ {% else %}
+ {% set card_content %}
+ {{ get_technology_info(data.identifier, 'resource', data.next_level)|number }}
+ ,
+ {{ get_technology_info(data.identifier, 'credit', data.next_level)|number }}
+ et
+ {{ data.time_to_build|lite_seconds }}
+ {# #}
+ {% endset %}
+ {% if not data.queue_requirements %}
+
+ file de recherche pleine
+ {{ card_content }}
+
+ {% elseif not data.credit_requirements %}
+
+ pas assez de crédits
+ {{ card_content }}
+
+ {% elseif not data.resource_requirements %}
+
+ pas assez de ressources
+ {{ card_content }}
+
+ {% else %}
+
+
+ {% if data.is_unblocking_technology %}
+ rechercher la technologie
+ {% else %}
+ rechercher le niveau {{ data.next_level }}
+ {% endif %}
+ {{ card_content }}
+
+
+ {% endif %}
+ {% endif %}
+ {% endif %}
+
+
diff --git a/templates/pages/ares/fleet/archives.html.twig b/templates/pages/ares/fleet/archives.html.twig
new file mode 100644
index 000000000..9f8be277d
--- /dev/null
+++ b/templates/pages/ares/fleet/archives.html.twig
@@ -0,0 +1,12 @@
+{% extends 'layouts/ares/fleet.html.twig' %}
+
+{% block content %}
+
+ {{ include('components/fleet/combat_reports.html.twig') }}
+
+ {% if report is not null %}
+ {{ include('components/fleet/combat_report.html.twig', { report, report_attacker, report_defender }) }}
+ {{ include('components/fleet/manage_combat_report.html.twig', { report }) }}
+ {% endif %}
+
+{% endblock content %}
diff --git a/templates/pages/ares/fleet/headquarters.html.twig b/templates/pages/ares/fleet/headquarters.html.twig
new file mode 100644
index 000000000..e55e51081
--- /dev/null
+++ b/templates/pages/ares/fleet/headquarters.html.twig
@@ -0,0 +1,17 @@
+{% extends 'layouts/ares/fleet.html.twig' %}
+
+{% block content %}
+
+ Centre des opérations
+
+
+
+ {{ include('components/fleet/list.html.twig') }}
+
+ {% if commander is defined and commander is not null %}
+ {{ include('components/fleet/commander_details.html.twig') }}
+ {{ include('components/fleet/commander_fleet.html.twig') }}
+ {% endif %}
+
+
+{% endblock content %}
diff --git a/templates/pages/ares/fleet/memorial.html.twig b/templates/pages/ares/fleet/memorial.html.twig
new file mode 100644
index 000000000..bdf3d8775
--- /dev/null
+++ b/templates/pages/ares/fleet/memorial.html.twig
@@ -0,0 +1,27 @@
+{% extends 'layouts/ares/fleet.html.twig' %}
+
+{% block content %}
+ {{ include('molecules/fleet/memorial_description.html.twig') }}
+
+ {% for commander in commanders %}
+ {% if loop.index < 6 %}
+ {{ include('components/fleet/commander_details.html.twig', { commander }) }}
+ {% else %}
+ {{ include('molecules/common/default.html.twig') }}
+ {% endif %}
+ {% endfor %}
+
+ {#
+ if (isset($commander_commanderDetail) && count($commander_commanderDetail) > 0) {
+ } else {
+ include $componentPath . 'default.php
+ include $componentPath . 'default.php
+ }
+
+ if (isset($commander_shortMemorial) && count($commander_shortMemorial) > 0) {
+ }
+ } else {
+ $response->redirect('404');
+ }
+ #}
+{% endblock %}
diff --git a/templates/pages/ares/fleet/overview.html.twig b/templates/pages/ares/fleet/overview.html.twig
new file mode 100644
index 000000000..4c941f598
--- /dev/null
+++ b/templates/pages/ares/fleet/overview.html.twig
@@ -0,0 +1,5 @@
+{% extends 'layouts/ares/fleet.html.twig' %}
+
+{% block content %}
+ {{ include('components/fleet/overview.html.twig') }}
+{% endblock content %}
diff --git a/templates/pages/ares/fleet/spy_reports.html.twig b/templates/pages/ares/fleet/spy_reports.html.twig
new file mode 100644
index 000000000..64f008f73
--- /dev/null
+++ b/templates/pages/ares/fleet/spy_reports.html.twig
@@ -0,0 +1,16 @@
+{% extends 'layouts/ares/fleet.html.twig' %}
+
+{% block content %}
+
+ {{ include('components/fleet/spy_reports.html.twig') }}
+ {% if focused_spy_report is not null %}
+ {{ include('components/fleet/spy_report.html.twig', {
+ 'spy_report': focused_spy_report,
+ 'place': focused_spy_report.place
+ }) }}
+ {% else %}
+ {{ include('molecules/common/default.html.twig') }}
+ {{ include('molecules/common/default.html.twig') }}
+ {% endif %}
+
+{% endblock content %}
diff --git a/templates/pages/athena/base/building/recycling.html.twig b/templates/pages/athena/base/building/recycling.html.twig
new file mode 100644
index 000000000..7cf36f189
--- /dev/null
+++ b/templates/pages/athena/base/building/recycling.html.twig
@@ -0,0 +1,176 @@
+{% extends 'layouts/athena/current_base.html.twig' %}
+
+{% block content %}
+ {% set building_number = constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::RECYCLING') %}
+ {% set recycler_capacity = constant('App\\Modules\\Athena\\Model\\RecyclingMission::RECYCLER_CAPACTIY') %}
+
+
+
+
+ {# #}
+ {{ get_building_info(building_number, 'frenchName') }}
+ Niveau {{ current_base.levelRecycling }}
+
+
+
+
+ {{ busy_recyclers }} / {{ total_recyclers }}
+
+
+
+ {{ free_recyclers }}
+
+
+
+
+ capacité de transport d'un recycleur
+
+
+ {{ recycler_capacity|number }}
+ {# #}
+
+
+ {{ (recycler_capacity * 10)|number }}
+ {# #}
+
+
+
+
+
+ {{ mission_quantity }}
+
+
+
+
+ {% for mission_data in base_missions %}
+ {% set mission = mission_data.mission %}
+
+
+ {% if loop.first %}
+ Mission{{ mission_quantity|plural }} en cours
+ {% endif %}
+
+
+
+
+ {#
#}
+
Mission {{ mission_data.mission_id }}
+
+
+
+ La mission recycle la
+ {{ get_place_type(mission.target.typeOfPlace) }}
+ située aux coordonnées
+
+ {{ mission_data.coords }}
+ .
+ Il reste {{ (mission.target.resources * mission.target.coefResources / 100)|number }} ressources,
+ {{ (mission.target.resources * mission.target.coefHistory / 100)|number }} débris et
+ {{ (mission.target.resources * mission.target.population / 100)|number }} gaz nobles.
+
+
+
Retour {{ mission.endedAt|game_date|raw }}
+
+
+
+
+
+
+ {% if mission.isBeingDeleted %}
+
Cette mission a été annulée, les recycleurs terminent la mission puis deviennent disponibles.
+ {% else %}
+
+
+ Annuler la mission
+
+
+ {% endif %}
+
+
+
+
+
+
+ {% if mission.addToNextMission == 0 %}
+ {{ mission.recyclerQuantity|number }}
+ {% else %}
+ {{ mission.recyclerQuantity|number }} + {{ mission.addToNextMission|number }}
+ {% endif %}
+
+
+ {% if mission.isActive and free_recyclers > 0 %}
+ +
+
+ {% endif %}
+
+
+
+ {{ (mission.recyclerQuantity * recycler_capacity)|number }}
+ {# #}
+
+
+
+
+ {{ mission.cycleTime|short_seconds }}
+
+
+
+
+ {% if mission_logs|length > 0 %}
+ Dernières livraisons
+ {% endif %}
+
+ {% for log in mission_logs|filter(log => log.mission.id == mission.id) %}
+ {% set wedge = {
+ 'ressource': log.resources,
+ 'crédit': log.credits,
+ 'pégase': log.ship0,
+ 'satyre': log.ship1,
+ 'chimère': log.ship2,
+ 'sirène': log.ship3,
+ 'dryade': log.ship4,
+ 'méduse': log.ship5,
+ 'griffon': log.ship6,
+ 'cyclope': log.ship7,
+ 'minotaure': log.ship8,
+ 'hydre': log.ship9,
+ 'cerbère': log.ship10,
+ 'phénix': log.ship11,
+ }|filter(v => v > 0) %}
+
+
+ La mission a ramené
+ {% for type in wedge|keys %}
+ {% set number = wedge[type] %}
+ {{ number|number }}
+ {{ type ~ number|plural }}
+ {% if not loop.last %}
+ et
+ {% else -%}
+ ,
+ {%- endif %}
+ {% endfor %}
+ {{ log.createdAt|game_date|raw }}
+
+ {% endfor %}
+
+
+ {% endfor %}
+
+
+
+
+ {{ get_building_info(building_number, 'description')|raw }}
+
+
+
+{% endblock content %}
diff --git a/templates/pages/athena/base/building/technosphere.html.twig b/templates/pages/athena/base/building/technosphere.html.twig
new file mode 100644
index 000000000..db934f56d
--- /dev/null
+++ b/templates/pages/athena/base/building/technosphere.html.twig
@@ -0,0 +1,171 @@
+{% extends 'layouts/athena/current_base.html.twig' %}
+
+{% block content %}
+ {% set technosphere_building_number = constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::TECHNOSPHERE') %}
+
+
+
+
+ {# #}
+ {{ get_building_info(technosphere_building_number, 'frenchName') }}
+ niveau {{ current_base.levelTechnosphere }}
+
+
+
+
+ {{ total_bonus }} %
+
+
+
File de construction
+
+
+ {% set total_time_techno = 0 %}
+ {% set base_queues_count = base_queues|length %}
+
+ {% for queue in base_queues %}
+ {% set total_time_techno = total_time_techno + queue|technology_queue_time %}
+ {% set remaining_total_time = queue|queue_duration %}
+
+
+
×
+ {#
#}
+
{{ queue|queue_name }}
+ {% if queue|is_unblocking_technology %}
+ niv. {{ queue.targetLevel }}
+ {% endif %}
+
+
+
{{ remaining_total_time|lite_seconds }}
+
+ {% if base_queues_count > 1 %}
+
+ {% else %}
+
+ {% endif %}
+
+ {% endfor %}
+
+ {% if base_queues_count < available_queues %}
+ {% for i in range(base_queues_count, available_queues - 1) %}
+
+
+ Emplacement libre
+
+
+ {% endfor %}
+ {% endif %}
+
+
+
+
+
+ {% if has_financial_technologies %}
+
+
+ Améliorations industrielles I
+
+
+
+ {% for data in technologies_data[4] %}
+ {{ include('molecules/technology/technology_card.html.twig', { data }) }}
+ {% endfor %}
+
+
+
+
+
+ Améliorations industrielles II
+
+
+
+ {% for data in technologies_data[5] %}
+ {{ include('molecules/technology/technology_card.html.twig', { data }) }}
+ {% endfor %}
+
+
+ {% endif %}
+
+ {% if has_military_technologies %}
+
+
+ Améliorations militaires I
+
+
+
+ {% for data in technologies_data[6] %}
+ {{ include('molecules/technology/technology_card.html.twig', { data }) }}
+ {% endfor %}
+
+
+
+
+
+ Améliorations militaires II
+
+
+
+ {% for data in technologies_data[7] %}
+ {{ include('molecules/technology/technology_card.html.twig', { data }) }}
+ {% endfor %}
+
+
+ {% endif %}
+
+
+
+ Nouvelles technologies
+
+
+ {% for data in technologies_data[1]|filter(v => v.is_over) %}
+ {{ include('molecules/technology/technology_card.html.twig', { data }) }}
+ {% endfor %}
+ {% for data in technologies_data[1]|filter(v => not v.is_over) %}
+ {{ include('molecules/technology/technology_card.html.twig', { data }) }}
+ {% endfor %}
+
+
+
+
+
+
+
+ {% for data in technologies_data[2]|filter(v => v.is_over) %}
+ {{ include('molecules/technology/technology_card.html.twig', { data }) }}
+ {% endfor %}
+ {% for data in technologies_data[2]|filter(v => not v.is_over) %}
+ {{ include('molecules/technology/technology_card.html.twig', { data }) }}
+ {% endfor %}
+
+
+
+
+
+
+ {% for data in technologies_data[3]|filter(v => v.is_over) %}
+ {{ include('molecules/technology/technology_card.html.twig', { data }) }}
+ {% endfor %}
+ {% for data in technologies_data[3]|filter(v => not v.is_over) %}
+ {{ include('molecules/technology/technology_card.html.twig', { data }) }}
+ {% endfor %}
+
+
+
+
+
+
+ {{ get_building_info(technosphere_building_number, 'description')|raw }}
+
+
+
+{% endblock content %}
diff --git a/templates/pages/athena/docks.html.twig b/templates/pages/athena/docks.html.twig
new file mode 100644
index 000000000..22b47ab0a
--- /dev/null
+++ b/templates/pages/athena/docks.html.twig
@@ -0,0 +1,113 @@
+{% extends 'layouts/athena/current_base.html.twig' %}
+
+{% block content %}
+
+ {{ get_building_info(building_number, 'description')|raw }}
+
+
+
+
+
+
+
+
+ Hangar
+
+
+
+ {{ in_storage }} / {{ total_space}}
+
+ {% set percent = in_storage|percent(total_space) %}
+
+
+
+
+
+
+ {# TODO check the original destination of this link #}
+
+ intégrer à vos armées
+
+
+ ?
+
+
+
+ {# TODO enable this dialog with Stimulus #}
+
+ Le hangar de votre chantier vous sert à stocker des vaisseaux. Les vaisseaux
+ sont stockés directement après leur construction. Si vous ne disposez pas suffisamment de place, vous ne pourrez plus en construire. Faites
+ donc bien attention de vider votre hangar régulièrement.
+ Chaque vaisseau prend une place différente dans votre hangar. Cette place correspond au point équivalent vaisseau (PEV). Le hangar dispose
+ d’un certain nombre de PEV qui augmente en fonction du niveau de votre chantier.
+ Le bouton intégration vous renverra à l’amirauté, ce qui vous permettra de vider votre hangar et de répartir vos vaisseaux dans vos flottes
+ en orbite autour de la planète sur laquelle vous avez construit votre chantier.
+
+
+
+ {% for i in ships_range %}
+ {% set ships_count = current_base.shipStorage[i] ?? 0 %}
+
+
+
+ {{ ships_count }}
+ {{ get_ship_info(i, 'codeName') ~ ships_count|plural }}
+
+ {{ ships_count * get_ship_info(i, 'pev') }} PEV
+
+
+
+ {# TODO Enable this dialog #}
+ {% if ships_count != 0 %}
+
+ {% endif %}
+ {% endfor %}
+
+
+
+
+
+
+
+ {% for ship_identifier in ships_range %}
+
+ {% endfor %}
+
+
+{% endblock content %}
diff --git a/templates/pages/athena/financial/investments.html.twig b/templates/pages/athena/financial/investments.html.twig
new file mode 100644
index 000000000..91bcd2cc1
--- /dev/null
+++ b/templates/pages/athena/financial/investments.html.twig
@@ -0,0 +1,21 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block left_sidebar %}
+ {{ include('molecules/financial/subnav.html.twig') }}
+{% endblock left_sidebar %}
+
+{% block body %}
+
+ Finances
+
+
+
+ {{ include('components/financial/reporting/general.html.twig') }}
+ {{ include('components/financial/reporting/imposition.html.twig') }}
+ {{ include('components/financial/reporting/commercial_routes.html.twig') }}
+ {{ include('components/financial/reporting/base_investments.html.twig') }}
+ {{ include('components/financial/reporting/taxes.html.twig') }}
+ {{ include('components/financial/reporting/ship_fees.html.twig', { commanders_by_base }) }}
+ {{ include('components/financial/reporting/commander_fees.html.twig') }}
+
+{% endblock body %}
diff --git a/templates/pages/athena/financial/reports.html.twig b/templates/pages/athena/financial/reports.html.twig
new file mode 100644
index 000000000..b5e7e5be4
--- /dev/null
+++ b/templates/pages/athena/financial/reports.html.twig
@@ -0,0 +1,83 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block body %}
+
+
+ {{ include('molecules/financial/subnav.html.twig') }}
+ {{ include('molecules/common/movers.html.twig') }}
+
+
+ {{ include ('molecules/common/advertising.html.twig') }}
+
+
+
+
Rapports généraux
+
+
+
+
+
+
+
+
Commerce
+
Routes et échanges commerciaux
+
+
+
+
+
+
+
+
Gestion
+
Investissements et revenus des planètes sous votre contrôle
+
+
+
+
+
+
+
+
Transferts
+
Transferts de fond entre dirigeant-es et factions
+
+
+
+
+
+{% endblock body %}
+
+
+{% block javascripts %}
+ {{ parent() }}
+
+
+
+ {{ encore_entry_script_tags('financial_reports') }}
+{% endblock javascripts %}
diff --git a/templates/pages/athena/financial/transfers.html.twig b/templates/pages/athena/financial/transfers.html.twig
new file mode 100644
index 000000000..91b071fbc
--- /dev/null
+++ b/templates/pages/athena/financial/transfers.html.twig
@@ -0,0 +1,18 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block left_sidebar %}
+ {{ include('molecules/financial/subnav.html.twig') }}
+{% endblock left_sidebar %}
+
+{% block body %}
+
+ Transferts de crédits
+
+
+
+ {{ include('components/financial/transfers/player_sending.html.twig') }}
+ {{ include('components/financial/transfers/faction_sending.html.twig') }}
+ {{ include('components/financial/transfers/sendings_history.html.twig') }}
+ {{ include('components/financial/transfers/receivings_history.html.twig') }}
+
+{% endblock body %}
diff --git a/templates/pages/athena/generator.html.twig b/templates/pages/athena/generator.html.twig
new file mode 100644
index 000000000..d8d9ae361
--- /dev/null
+++ b/templates/pages/athena/generator.html.twig
@@ -0,0 +1,31 @@
+{% extends 'layouts/athena/current_base.html.twig' %}
+
+{% set generator_building_number = constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::GENERATOR') %}
+
+{% block content %}
+
+ {{ get_building_info(generator_building_number, 'description')|raw }}
+
+
+
+
+
+
+ {% for building_identifier in constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::BUILDINGS') %}
+
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/templates/pages/athena/overview.html.twig b/templates/pages/athena/overview.html.twig
new file mode 100644
index 000000000..4eda47c22
--- /dev/null
+++ b/templates/pages/athena/overview.html.twig
@@ -0,0 +1,15 @@
+{% extends 'layouts/athena/current_base.html.twig' %}
+
+{% block content %}
+
+
+
+ {{ include('components/base/situation.html.twig') }}
+
+ {{ include('components/base/type.html.twig') }}
+
+ {% if current_player_bases|length > 1 %}
+ {{ include('components/base/leave_base.html.twig') }}
+ {% endif %}
+
+{% endblock %}
diff --git a/templates/pages/athena/refinery.html.twig b/templates/pages/athena/refinery.html.twig
new file mode 100644
index 000000000..b3d832619
--- /dev/null
+++ b/templates/pages/athena/refinery.html.twig
@@ -0,0 +1,65 @@
+{% extends 'layouts/athena/current_base.html.twig' %}
+
+{% block content %}
+
+
+
+ {# #}
+
+ {{ get_building_info(constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::REFINERY'), 'frenchName') }}
+
+ Niveau {{ current_base.levelRefinery }}
+
+
+ {# @TODO factorize with toolbar.html.twig #}
+ {% set production = get_base_production(current_base) %}
+
+ {{ production|number }}
+ {# #}
+
+
+
+
+
+ {{ current_base.place.coefResources }} %
+
+
+
+ {{ refining_bonus }} %
+
+
+
+
+
+
+ Contrôle du raffinage
+
+
+
+ Production par relève
+
+ {% set refinery_level = current_base.levelRefinery %}
+
+ {% for level in get_building_level_range(refinery_level) %}
+ {% set production = get_base_production(current_base, level) %}
+
+ {{ production|number }}
+ {# #}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+ {{ get_building_info(constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::REFINERY'), 'description')|raw }}
+
+
+
+
+{% endblock content %}
diff --git a/templates/pages/athena/school.html.twig b/templates/pages/athena/school.html.twig
new file mode 100644
index 000000000..1a1f4bbc4
--- /dev/null
+++ b/templates/pages/athena/school.html.twig
@@ -0,0 +1,167 @@
+{% extends 'layouts/athena/current_base.html.twig' %}
+
+{# TODO de-duplicate code here #}
+
+{% block content %}
+
+
+
+ {# #}
+
+ Ecole de Commandement
+
+
+ Formation des officiers
+
+
+
+
+
+ {{ current_base.iSchool|number }}
+ {# #}
+
+
+ →
+
+
+
+
+ {{ commander_invest_bonus }} %
+
+
+
+
+
+
+
+
+
+
+
+
+ {% for commander in commanders %}
+ {% set missing_experience = get_commander_missing_experience(commander) %}
+
+
+ {# #}
+
+ {{ commander|commander_rank }} {{ commander.name }}
+ {{ commander.experience|number }} points d'expérience
+ ~ {{ earned_experience|number }}xp/relève
+
+
+
+ ↑
+ →
+
+
+
+
+ {% endfor %}
+
+ {% if commanders|length < max_commanders_in_school %}
+ {% for i in range(commanders|length, max_commanders_in_school - 1) %}
+
+
+
+
+
+ {% endfor %}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+ {% for commander in reserve_commanders %}
+ {% set missing_experience = get_commander_missing_experience(commander) %}
+
+
+ {# #}
+
+ {{ commander|commander_rank }} {{ commander.name }}
+ {{ commander.experience|number }} points d'expérience
+ ~ {{ earned_experience|number }}xp/relève
+
+
+
+ ↑
+ ←
+
+
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ L’Ecole de commandement est le centre de formation de vos officiers. Il suffit pour cela d’engager de jeunes recrues sans expérience et d’investir un peu d’argent dans l’école pour que de brillants stratèges issus de toute la galaxie viennent enseigner leur savoir aux jeunes commandants de votre école.
+ Au fil du temps ils gagneront de l’expérience et des niveaux . En gradant, ils acquerront la capacité de diriger une escadrille supplémentaire, ce qui augmentera la taille maximale de la flotte qu’ils dirigeront. L’investissement dans l’école de commandement s’applique à tout les commandants en formation.
+ Le nombre de place de formation est limité. Il est cepandant plus élevé sur les bases militaires et les capitales . Le mess des officiers permet de mettre vos officiers au repos.
+
+
+
+
+{% endblock content %}
diff --git a/templates/pages/athena/spatioport.html.twig b/templates/pages/athena/spatioport.html.twig
new file mode 100644
index 000000000..1cb862b9b
--- /dev/null
+++ b/templates/pages/athena/spatioport.html.twig
@@ -0,0 +1,312 @@
+{% extends 'layouts/athena/current_base.html.twig' %}
+
+{% block content %}
+
+
+
+ {# #}
+
+ {{- get_building_info(constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::SPATIOPORT'), 'frenchName') -}}
+
+ Niveau {{ current_base.levelSpatioport }}
+
+
+
+
+
+ {% if mode == 'search' %}
+
+
+
+
+
+
+ {% if search_results is not null %}
+
+ {% endif %}
+ {% else %}
+ {% for route in routes %}
+ {% if route.isProposed and route.destinationBase.player.id == current_player.id %}
+ {% set route_distance = get_route_distance(route) %}
+ {% set origin_base = include('molecules/base/trade/commercial_route_base.html.twig', {
+ 'base': route.originBase,
+ }) %}
+ {% set destination_base = include('molecules/base/trade/commercial_route_base.html.twig', {
+ 'base': route.destinationBase,
+ }) %}
+
+
+ {% if loop.index == 1 %}
+ Propositions
+ {% endif %}
+
+
+
+
+ {% set price = get_route_price(route_distance) %}
+ {% if price > current_player.credit %}
+
+ pas assez de crédits pour accepter
+
+ {% elseif routes_data.total >= routes_data.max %}
+
+ pas d'emplacement libre pour accepter
+
+ {% else %}
+ {# @TODO move this calculation in a dedicated service #}
+ {% if is_player_from_negora %}
+ {% set price = price - (price * negora_commercial_bonus / 100)|round %}
+ {% endif %}
+
+
+ accepter pour {{ price|number }} crédits
+
+
+ {% endif %}
+
+
+ x
+
+
+
+
+
+ En attente
+
+
+
+ {% if route.originBase.player.id == current_player.id %}
+ {{ destination_base|raw }}
+ {% else %}
+ {{ origin_base|raw }}
+ {% endif %}
+
+
+
+ {{ route_distance }} al.
+
+
+
+ {{ price|number }}
+
+
+
+
+ {{ route.income|number }}
+
+
+
+
+ {{ (route.originBase.place.population + route.destinationBase.place.population)|number }} millions
+
+
+ {% if not route.isProposed %}
+
+ {{ route.acceptedAt|game_date|raw }}
+
+ {% endif %}
+
+
+ {% if route.originBase.player.id == current_player.id %}
+ {{ origin_base|raw }}
+ {% else %}
+ {{ destination_base|raw }}
+ {% endif %}
+
+
+
+ {% endif %}
+ {% endfor %}
+
+ {% for route in routes %}
+ {% if not route.isProposed or route.destinationBase.player.id != current_player.id %}
+ {% set route_distance = get_route_distance(route) %}
+ {% set origin_base = include('molecules/base/trade/commercial_route_base.html.twig', {
+ 'base': route.originBase,
+ }) %}
+ {% set destination_base = include('molecules/base/trade/commercial_route_base.html.twig', {
+ 'base': route.destinationBase,
+ }) %}
+
+
+ {% if loop.index == 1 %}
+ Routes commerciales
+ {% endif %}
+
+
+
+
+
+
+ {%- if route.isProposed -%}
+ En attente
+ {%- elseif route.isActive -%}
+ En activité
+ {%- elseif route.isInStandBy -%}
+ Gelée
+ {%- endif -%}
+
+
+
+ {% if route.originBase.player.id == current_player.id %}
+ {{ destination_base|raw }}
+ {% else %}
+ {{ origin_base|raw }}
+ {% endif %}
+
+
+
+ {{ route_distance }} al.
+
+
+
+ {{ route.income|number }}
+
+
+
+
+ {{ (route.originBase.place.population + route.destinationBase.place.population)|number }} millions
+
+
+ {% if not route.isProposed %}
+
+ {{ route.acceptedAt|game_date|raw }}
+
+ {% endif %}
+
+ {% if route.originBase.player.id == current_player.id %}
+ {{ origin_base|raw }}
+ {% else %}
+ {{ destination_base|raw }}
+ {% endif %}
+
+
+
+ {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
+
+ {{ get_building_info(constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::SPATIOPORT'), 'description')|raw }}
+
+
+
+{% endblock content %}
diff --git a/templates/pages/athena/storage.html.twig b/templates/pages/athena/storage.html.twig
new file mode 100644
index 000000000..a16ee8933
--- /dev/null
+++ b/templates/pages/athena/storage.html.twig
@@ -0,0 +1,71 @@
+{% extends 'layouts/athena/current_base.html.twig' %}
+
+{% block content %}
+
+
+
+ {# #}
+
+ {{ get_building_info(constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::STORAGE'), 'frenchName') }}
+
+ Niveau {{ current_base.levelStorage }}
+
+
+
+ {{ include('molecules/base/production/storage_gauge.html.twig', {
+ 'stored_resources': current_base.resourcesStorage,
+ 'percent': current_base|base_storage_percent,
+ }) }}
+
+
+
+
+ {{ storage_bonus }} %
+
+
+
+
+
+
+
+ Stockage maximal de la raffinerie
+
+ {% set storage_level = current_base.levelStorage %}
+
+ {% for level in get_building_level_range(storage_level) %}
+
+ niveau {{ level }}
+
+ {% set storage_space = get_building_info(
+ constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::STORAGE'),
+ 'level',
+ level,
+ 'storageSpace',
+ ) %}
+ {{ storage_space|number }}
+ {% if storage_bonus > 0 %}
+ +{{ (storage_space * storage_bonus / 100)|number }}
+ {% endif %}
+ {# #}
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+ {{ get_building_info(constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::STORAGE'), 'description')|raw }}
+
+
+
+
+{% endblock content %}
diff --git a/templates/pages/athena/trade_market.html.twig b/templates/pages/athena/trade_market.html.twig
new file mode 100644
index 000000000..3cf6b9c0e
--- /dev/null
+++ b/templates/pages/athena/trade_market.html.twig
@@ -0,0 +1,50 @@
+{% extends 'layouts/athena/current_base.html.twig' %}
+
+{% block content %}
+
+
+
+ {# #}
+
+ {{ get_building_info(constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::COMMERCIAL_PLATEFORME'), 'frenchName') }}
+
+ niveau {{ current_base.levelCommercialPlateforme }}
+
+
+
+
+
+ {{ include('components/base/trade/commercial_shippings.html.twig') }}
+
+ {{ include('components/base/trade/' ~ mode ~ '.html.twig', { resources_current_rate }) }}
+
+
+
+
+
+ {{ get_building_info(constant('App\\Modules\\Athena\\Resource\\OrbitalBaseResource::COMMERCIAL_PLATEFORME'), 'description')|raw }}
+
+
+
+
+{% endblock content %}
diff --git a/templates/pages/atlas/faction_rankings.html.twig b/templates/pages/atlas/faction_rankings.html.twig
new file mode 100644
index 000000000..11972f034
--- /dev/null
+++ b/templates/pages/atlas/faction_rankings.html.twig
@@ -0,0 +1,14 @@
+{% extends 'layouts/atlas/ranking.html.twig' %}
+
+{% block page_title 'Classement faction' %}
+
+{% block content %}
+ {% if has_ranking %}
+ {{ include('components/rankings/faction/best_faction.html.twig', { best_faction }) }}
+ {{ include('components/rankings/faction/points_rankings.html.twig', { points_rankings, points_to_win }) }}
+ {{ include('components/rankings/faction/general_rankings.html.twig', { general_rankings }) }}
+ {{ include('components/rankings/faction/wealth_rankings.html.twig', { wealth_rankings }) }}
+ {{ include('components/rankings/faction/territorial_rankings.html.twig', { territorial_rankings }) }}
+ {{ include('components/rankings/faction/victory_info.html.twig') }}
+ {% endif %}
+{% endblock content %}
diff --git a/templates/pages/atlas/map.html.twig b/templates/pages/atlas/map.html.twig
new file mode 100644
index 000000000..3fc470ccd
--- /dev/null
+++ b/templates/pages/atlas/map.html.twig
@@ -0,0 +1,67 @@
+{% extends 'layouts/map.html.twig' %}
+
+{% set map_size = galaxy_configuration.scale * galaxy_configuration.galaxy.size %}
+
+{% block left_sidebar %}
+ {{ include('molecules/map/commanders.html.twig', { local_commanders }) }}
+{% endblock left_sidebar %}
+
+{% block body %}
+
+
+ {# map sytems #}
+
+
+
+
+
+
+
+
+ {% if default_position.system is not null %}
+ {% set no_ajax = true %}
+
+ {{ include('components/map/system_details.html.twig', {
+ places,
+ no_ajax,
+ moving_commanders,
+ recycling_missions,
+ technologies,
+ colonization_cost,
+ conquest_cost,
+ }) }}
+ {% endif %}
+
+{% endblock body %}
diff --git a/templates/pages/atlas/player_rankings.html.twig b/templates/pages/atlas/player_rankings.html.twig
new file mode 100644
index 000000000..176b34ad0
--- /dev/null
+++ b/templates/pages/atlas/player_rankings.html.twig
@@ -0,0 +1,17 @@
+{% extends 'layouts/atlas/ranking.html.twig' %}
+
+{% block page_title 'Classement joueur' %}
+
+{% block content %}
+ {% if has_ranking %}
+ {{ include('components/rankings/player/best_player.html.twig', { best_player }) }}
+ {{ include('components/rankings/player/general.html.twig', { general_rankings }) }}
+ {{ include('components/rankings/player/experience.html.twig', { experience_rankings }) }}
+ {{ include('components/rankings/player/fight.html.twig', { fight_rankings }) }}
+ {{ include('components/rankings/player/resources.html.twig', { resources_rankings }) }}
+ {{ include('components/rankings/player/armies.html.twig', { armies_rankings }) }}
+ {{ include('components/rankings/player/butcher.html.twig', { butcher_rankings }) }}
+ {{ include('components/rankings/player/trader.html.twig', { trader_rankings }) }}
+ {{ include('components/rankings/player/stats.html.twig', { all_players_count, active_players_count }) }}
+ {% endif %}
+{% endblock content %}
diff --git a/templates/pages/demeter/embassy.html.twig b/templates/pages/demeter/embassy.html.twig
new file mode 100644
index 000000000..423a4a0ec
--- /dev/null
+++ b/templates/pages/demeter/embassy.html.twig
@@ -0,0 +1,26 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block left_sidebar %}
+
+
+
+
+{% endblock left_sidebar %}
+
+{% block body %}
+
+ Ambassades
+
+
+
+ {% if player is defined %}
+ {{ include('molecules/embassy/player_search.html.twig') }}
+ {{ include('molecules/embassy/player_bases.html.twig') }}
+ {% else %}
+ {{ include('molecules/embassy/faction_nav.html.twig') }}
+ {{ include('molecules/embassy/faction_flag.html.twig') }}
+ {{ include('molecules/embassy/faction_government.html.twig') }}
+ {{ include('components/Faction/data/diplomacy/relations.html.twig') }}
+ {% endif %}
+
+{% endblock body %}
diff --git a/templates/pages/demeter/faction/data.html.twig b/templates/pages/demeter/faction/data.html.twig
new file mode 100644
index 000000000..f13970751
--- /dev/null
+++ b/templates/pages/demeter/faction/data.html.twig
@@ -0,0 +1,38 @@
+{% extends 'layouts/demeter/faction.html.twig' %}
+
+{% block page_title 'Registres' %}
+
+{% block content %}
+ {% set mode = app.request.query.get('mode', 'financial') %}
+
+
+ {{ include('components/Faction/data/nav.html.twig') }}
+
+ {% if 'financial' == mode %}
+ {{ include('components/Faction/data/financial/stats.html.twig') }}
+ {{ include('components/Faction/data/financial/sector_taxes.html.twig') }}
+ {{ include('components/Faction/data/financial/donations.html.twig') }}
+ {% elseif 'trade' == mode %}
+ {{ include('components/Faction/data/trade/rc_stats.html.twig') }}
+ {{ include('components/Faction/data/trade/taxes.html.twig', {'label': "Taxes à l'achat", 'taxes': import_taxes}) }}
+ {{ include('components/Faction/data/trade/taxes.html.twig', {'label': 'Taxes à la vente', 'taxes': export_taxes}) }}
+ {% elseif 'war' == mode %}
+ {{ include('components/Faction/data/war/stats.html.twig') }}
+ {{ include('components/Faction/data/war/reports.html.twig', {'is_attack': true, 'reports': attack_reports}) }}
+ {{ include('components/Faction/data/war/reports.html.twig', {'is_attack': false, 'reports': defense_reports}) }}
+ {{ include('components/Faction/data/war/levels.html.twig') }}
+ {% elseif 'tactical' == mode %}
+ {{ include('components/Faction/data/tactical/map.html.twig') }}
+ {{ include('components/Faction/data/tactical/sectors.html.twig') }}
+ {{ include('components/Faction/data/tactical/targets.html.twig') }}
+ {% elseif 'diplomacy' == mode %}
+ {{ include('components/Faction/data/diplomacy/relations.html.twig') }}
+ {{ include('components/Faction/data/diplomacy/about.html.twig') }}
+ {% elseif 'law' == mode %}
+ {{ include('components/Faction/data/law/list.html.twig', {'law_status': 6}) }}
+ {{ include('components/Faction/data/law/list.html.twig', {'law_status': 3}) }}
+ {{ include('components/Faction/data/law/list.html.twig', {'law_status': 4}) }}
+ {{ include('components/Faction/data/law/list.html.twig', {'law_status': 5}) }}
+ {% endif %}
+
+{% endblock %}
diff --git a/templates/pages/demeter/faction/election.html.twig b/templates/pages/demeter/faction/election.html.twig
new file mode 100644
index 000000000..7d06e8093
--- /dev/null
+++ b/templates/pages/demeter/faction/election.html.twig
@@ -0,0 +1,43 @@
+{% extends 'layouts/demeter/faction.html.twig' %}
+
+{% block page_title %}
+ {% if faction.isInCampaign %}
+ Campagne
+ {% endif %}
+{% endblock page_title %}
+
+{% block content %}
+
+ {% if faction.isInCampaign %}
+ {{ include('components/Faction/election/campaign.html.twig') }}
+ {{ include('components/Faction/election/list.html.twig') }}
+
+ {% if app.request.query.get('action') == 'postulate' %}
+ {{ include('components/Faction/election/postulate.html.twig') }}
+ {% elseif candidate is not null %}
+ {{ include('components/Faction/election/candidate.html.twig') }}
+ {% else %}
+ {{ include('molecules/common/default.html.twig') }}
+ {% endif %}
+ {% elseif faction.isInElection %}
+ {% if faction.isDemocratic %}
+ {{ include('components/Faction/election/election.html.twig') }}
+ {{ include('components/Faction/election/list.html.twig') }}
+
+ {% if candidate is not null %}
+ {{ include('components/Faction/election/candidate.html.twig') }}
+ {% if topic is defined %}
+ {# {{ include('components/Faction/forum/topic.html.twig') }} #}
+ {% endif %}
+ {% else %}
+ {{ include('molecules/common/default.html.twig') }}
+ {% endif %}
+ {% elseif faction.isRoyalistic %}
+ {{ include('components/Faction/election/putsch.html.twig') }}
+ {{ include('components/Faction/election/candidate.html.twig') }}
+ {% else %}
+ {{ include('molecules/common/default.html.twig') }}
+ {% endif %}
+ {% endif %}
+
+{% endblock %}
diff --git a/templates/pages/demeter/faction/forum.html.twig b/templates/pages/demeter/faction/forum.html.twig
new file mode 100644
index 000000000..4337ce5ff
--- /dev/null
+++ b/templates/pages/demeter/faction/forum.html.twig
@@ -0,0 +1,19 @@
+{% extends 'layouts/demeter/faction.html.twig' %}
+
+{% block page_title 'Forum' %}
+
+{% block content %}
+
+ {% if show_home is same as(false) %}
+ {% include 'components/Faction/forum/navbar.html.twig' %}
+ {% endif %}
+
+ {% for forumId, topics_with_last_views in topics_by_forum %}
+ {% include 'components/Faction/forum/topics.html.twig' with { forumId, topics_with_last_views } %}
+ {% endfor %}
+
+ {% if 'create' == app.request.query.get('mode') %}
+ {% include 'components/Faction/forum/new_topic.html.twig' with { forumId: selected_forum_id } %}
+ {% endif %}
+
+{% endblock content %}
diff --git a/templates/pages/demeter/faction/government.html.twig b/templates/pages/demeter/faction/government.html.twig
new file mode 100644
index 000000000..09e0d1fbe
--- /dev/null
+++ b/templates/pages/demeter/faction/government.html.twig
@@ -0,0 +1,46 @@
+{% extends 'layouts/demeter/faction.html.twig' %}
+
+{% block page_title 'Gouvernement' %}
+
+{% block content %}
+
+ {{ include('components/Faction/government/nav.html.twig') }}
+
+ {% set mode = app.request.query.get('mode', 'law') %}
+
+ {% if 'law' == mode %}
+ {% set laws_count = 0 %}
+
+ {# @TODO Implement more proper way to loop though the laws #}
+ {% for i in 1..total_laws_count %}
+ {% if get_law_info(i, 'department') == current_player.status and get_law_info(i, 'isImplemented') %}
+ {% set laws_count = laws_count + 1 %}
+ {{ include('molecules/faction/government/law.html.twig', {'law_id': i}) }}
+ {% endif %}
+ {% endfor %}
+
+ {% if laws_count < 2 %}
+ {% for i in laws_count..1 %}
+ {{ include('molecules/common/default.html.twig') }}
+ {% endfor %}
+ {% endif %}
+ {% elseif 'news' == mode %}
+ {{ include('components/Faction/news/list.html.twig') }}
+
+ {% if faction_news_to_edit is not null %}
+ {{ include('components/Faction/news/edit.html.twig', { news: faction_news_to_edit }) }}
+ {% else %}
+ {{ include('components/Faction/news/create.html.twig') }}
+ {% endif %}
+ {% elseif 'message' == mode %}
+ {{ include('components/Faction/government/message.html.twig') }}
+ {% elseif 'description' == mode %}
+ {{ include('components/Faction/government/description.html.twig') }}
+ {% elseif 'credit' == mode %}
+ {{ include('components/Faction/government/credit.html.twig') }}
+ {% elseif 'manage' == mode %}
+ {{ include('components/Faction/government/manage/main.html.twig') }}
+ {{ include('molecules/common/default.html.twig') }}
+ {% endif %}
+
+{% endblock content %}
diff --git a/templates/pages/demeter/faction/members.html.twig b/templates/pages/demeter/faction/members.html.twig
new file mode 100644
index 000000000..915cd9488
--- /dev/null
+++ b/templates/pages/demeter/faction/members.html.twig
@@ -0,0 +1,10 @@
+{% extends 'layouts/demeter/faction.html.twig' %}
+
+{% block page_title 'Membres' %}
+
+{% block content %}
+
+ {% include('components/Faction/members/stats.html.twig') %}
+ {% include('components/Faction/members/list.html.twig') %}
+
+{% endblock content %}
diff --git a/templates/pages/demeter/faction/overview.html.twig b/templates/pages/demeter/faction/overview.html.twig
new file mode 100644
index 000000000..d27c965f6
--- /dev/null
+++ b/templates/pages/demeter/faction/overview.html.twig
@@ -0,0 +1,9 @@
+{% extends 'layouts/demeter/faction.html.twig' %}
+
+{% block content %}
+
+ {{ include('components/Faction/news.html.twig') }}
+ {{ include('components/Faction/stat.html.twig') }}
+ {{ include('components/Faction/laws.html.twig') }}
+
+{% endblock content %}
diff --git a/templates/pages/demeter/faction/senate.html.twig b/templates/pages/demeter/faction/senate.html.twig
new file mode 100644
index 000000000..96dee9b30
--- /dev/null
+++ b/templates/pages/demeter/faction/senate.html.twig
@@ -0,0 +1,14 @@
+{% extends 'layouts/demeter/faction.html.twig' %}
+
+{% block page_title 'Sénat' %}
+
+{% block content %}
+
+ {{ include('components/Faction/senate/stats.html.twig') }}
+ {% for law in voting_laws %}
+ {{ include('molecules/faction/senate/law.html.twig', { law }) }}
+ {% endfor %}
+
+ {{ include('components/Faction/senate/historic.html.twig') }}
+
+{% endblock %}
diff --git a/templates/pages/demeter/faction/topic.html.twig b/templates/pages/demeter/faction/topic.html.twig
new file mode 100644
index 000000000..cecf25805
--- /dev/null
+++ b/templates/pages/demeter/faction/topic.html.twig
@@ -0,0 +1,84 @@
+{% extends 'layouts/demeter/faction.html.twig' %}
+
+{% block content %}
+
+ {% include 'components/Faction/forum/navbar.html.twig' %}
+
+ {% include 'components/Faction/forum/topics.html.twig' with { forumId: topic.forum, topics_with_last_views, display_standard_topics: true, is_archived: false } %}
+
+
+
+
+
+
+
+
+
+
+
+ {% if topic.isClosed %}
+
+ {% else %}
+
+ {% endif %}
+
+
+
+ {% for message in messages %}
+ {% if message.player.faction.identifier > 0 %}
+ {% set statuses = get_faction_info(message.player.faction.identifier, 'status') %}
+ {% set status = statuses[message.player.status - 1] %}
+ {% else %}
+ {% set status = 'Rebelle' %}
+ {% endif %}
+
+ {% set can_edit = current_player.id == message.player.id or current_player.isGovernmentMember is same as(true) and topic.forum != 20 %}
+
+
+
+
+
+
+
+ ≡ {{ status }} {{ message.player.name }}
+ {{ message.pContent|raw }}
+
+
+ {% if can_edit is same as (true) %}
+
+ {% endif %}
+
+
+
+
+ {% endfor %}
+
+
+
+{% endblock content %}
diff --git a/templates/pages/gaia/map_sandbox.html.twig b/templates/pages/gaia/map_sandbox.html.twig
new file mode 100644
index 000000000..cef003a8d
--- /dev/null
+++ b/templates/pages/gaia/map_sandbox.html.twig
@@ -0,0 +1,101 @@
+{% extends 'layouts/map.html.twig' %}
+
+{% block body %}
+
+
+ {% set scale = 10 %}
+ {% set map_size = 100 * scale %}
+
+
+
+ {# map systems #}
+
+
+{% endblock body %}
diff --git a/templates/pages/hermes/communication_center.html.twig b/templates/pages/hermes/communication_center.html.twig
new file mode 100644
index 000000000..b962c8cf2
--- /dev/null
+++ b/templates/pages/hermes/communication_center.html.twig
@@ -0,0 +1,212 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block left_sidebar %}
+ {{ include('molecules/common/subnav.html.twig') }}
+{% endblock left_sidebar %}
+
+{% block body %}
+
+ Centre de communication
+
+
+
+
+
+
+
+
+ {% for conv in conversations %}
+ {% set conv_name = [] %}
+ {% set counter = 0 %}
+ {% set remaining_players = 0 %}
+ {% set own_last_view = null %}
+
+ {% if conv.isGroupConversation %}
+ {% set conversation_avatar = 'multi' %}
+ {% set conversation_faction_identifier = 0 %}
+ {% else %}
+ {% set conversation_avatar = conv.initiator.avatar %}
+ {% set conversation_faction_identifier = conv.initiator.faction.identifier ?? 0 %}
+ {% endif %}
+
+ {% for conv_participant in conv.players %}
+ {% if conv_participant.player.id != current_player.id %}
+ {% if counter < 5 %}
+ {% set conv_name = conv_name|merge(['' ~ conv_participant.player.name ~ ' ']) %}
+ {% else %}
+ {% set remaining_players = remaining_players + 1 %}
+ {% endif %}
+
+ {% if conv.type == constant('App\\Modules\\Hermes\\Model\\Conversation::TY_SYSTEM') %}
+ {% if conv_participant.playerStatus == constant('App\\Modules\\Hermes\\Model\\ConversationUser::US_ADMIN') %}
+ {% set conversation_avatar = conv_participant.player.avatar %}
+ {% set conversation_faction_identifier = conv_participant.player.faction.identifier ?? 0 %}
+ {% endif %}
+ {% endif %}
+
+ {% set counter = counter + 1 %}
+ {% else %}
+ {% set own_last_view = conv_participant.lastViewedAt %}
+ {% endif %}
+ {% endfor %}
+
+
+
+
+ {{ conv.messagesCount }}
+ {% if own_last_view.timestamp ?? null < conv.lastMessageAt.timestamp %}
+
+ {% endif %}
+
+
+
+ {{ conv.lastMessageAt|game_date|raw }}
+
+ {% if conv.title is not empty %}
+ {{ conv.title }}
+ {% else %}
+ {{ conv_name|join(',')|raw }}
+ {% endif %}
+
+
+ {% endfor %}
+
+ {# TODO pagination is not done right here #}
+ {% if constant('App\\Modules\\Hermes\\Model\\Conversation::CONVERSATION_BY_PAGE') == conversations|length %}
+
+ Afficher plus de conversations
+
+ {% endif %}
+
+ {% if constant('App\\Modules\\Hermes\\Model\\ConversationUser::CS_ARCHIVED') == mode %}
+ Retour aux conversations
+ {% else %}
+ Voir les conversations archivées
+ {% endif %}
+
+
+
+ {% if start_new_conversation %}
+
+
+ Démarrer une nouvelle conversation
+
+
+
+ Destinataire
+
+
+
+
+
+
+
+ Message
+
+
+
+
+ {{ get_parser_toolbar()|raw }}
+
+
+
+
+
+ Démarrer la conversation
+
+
+
+
+
+ {% elseif conversation is not null %}
+
+
+ {% if conversation.title is not null %}
+
{{ conversation.title }}
+ {% endif %}
+
+
+
+ {% if constant('App\\Modules\\Hermes\\Model\\Conversation::TY_SYSTEM') != conversation.type %}
+
+
+
+
+
+ {{ get_parser_toolbar()|raw }}
+
+
+
+ Répondre
+
+
+
+ {% endif %}
+
+ {% for message in messages %}
+ {% set player_faction_statuses = get_faction_info(message.player.faction.identifier, 'status') %}
+ {% set player_status = player_faction_statuses[message.player.status - 1] %}
+
+ {% if 0 < loop.index0 and messages[loop.index0 - 1].createdAt > player_last_viewed_at and message.createdAt <= player_last_viewed_at %}
+
+ Dernier message lu
+
+ {% endif %}
+
+ {% if constant('App\\Modules\\Hermes\\Model\\ConversationMessage::TY_STD') == message.type %}
+
+
+
+
+
+
+ {{ message.content|raw }}
+
+
+
+
+ {% else %}
+
+ {{ message.content|raw }}
+
+ {% endif %}
+ {% endfor %}
+
+ {% if constant('App\\Modules\\Hermes\\Model\\ConversationMessage::MESSAGE_BY_PAGE') == messages|length %}
+
+ Afficher les messages suivants
+
+ {% endif %}
+
+
+
+ {% endif %}
+
+ {% include 'components/notifications/last.html.twig' with { notifications } %}
+
+ {% if archived_notifications|length > 0 %}
+
+ {% endif %}
+
+{% endblock body %}
diff --git a/templates/pages/hermes/conversation/start.html.twig b/templates/pages/hermes/conversation/start.html.twig
new file mode 100644
index 000000000..1f8d7b94e
--- /dev/null
+++ b/templates/pages/hermes/conversation/start.html.twig
@@ -0,0 +1,34 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block body %}
+
+
+
Démarrer une nouvelle conversation
+
+
+
+
+ Destinataire
+
+
+
+
+
+
+
+
+ Message
+
+
+
+ {{ get_parser_toolbar()|raw }}
+
+
+
+
+ Démarrer la conversation
+
+
+
+
+{% endblock body %}
diff --git a/templates/pages/hermes/conversation/view.html.twig b/templates/pages/hermes/conversation/view.html.twig
new file mode 100644
index 000000000..bed78dee2
--- /dev/null
+++ b/templates/pages/hermes/conversation/view.html.twig
@@ -0,0 +1,6 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block body %}
+
+
+{% endblock body %}
diff --git a/templates/pages/portal/homepage.html.twig b/templates/pages/portal/homepage.html.twig
new file mode 100644
index 000000000..21eac53ad
--- /dev/null
+++ b/templates/pages/portal/homepage.html.twig
@@ -0,0 +1,278 @@
+{% extends 'layouts/base.html.twig' %}
+
+{% block body %}
+
+
+
+
+
+
+
+
+
+
+
+
Jeu de stratégie par factions
+
Créez votre personnage et rejoignez une des factions s'affrontant par les armes, la culture ou la richesse !
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Partez à la conquête de la galaxie
+
+ Comment vaincrez-vous ?
+
+
+ Découvrez les différents axes de gameplay proposés par Kalaxia dans votre quête pour la victoire !
+
+
+
+
+
+ {#
#}
+
+
+
+
+
+
+
Combattez vos adversaires
+
+
+
+ Constituez vos flottes :
+ Fabriquez vos vaisseaux, composez vos escadrilles et envoyez votre armada en mission aux quatre coins de la galaxie.
+
+
+ Menez la bataille :
+ Combattez les flottes ennemies pour protéger vos planètes, piller ou conquérir les bases adverses.
+ Battez-vous en temps réel aux côtés de vos alliés dans de gigantesques affrontements spatiaux.
+
+
+ Partez à la conquête de la galaxie :
+ Disputez la possession des systèmes stellaires et des secteurs de la galaxie aux autres factions
+
+
+
+
+
Bâtissez votre empire
+
+
+
+ Développez votre base :
+ Construction de bâtiments, production de ressources
+
+
+ Découvrez de nouvelles technologies :
+ Grâce à la recherche, conservez l'avantage et boostez votre économie ou les capacités de vos flottes.
+
+
+
+
+
Prenez la tête de votre faction
+
+
+
+ Conquérez le pouvoir :
+ Par l'élection démocratique, le choix des dieux ou bien le coup d'état, prenez le contrôle de votre faction pour la mener vers la victoire.
+
+
+ Prenez les décisions :
+ A l'aide des lois que vous et votre gouvernement ferez voter, aidez votre faction dans sa progression vers la suprématie.
+
+
+
+
+
+
+
+
+ {#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Gravida quam mi erat tortor neque molestie. Auctor aliquet at porttitor a enim nunc suscipit tincidunt nunc. Et non lorem tortor posuere. Nunc eu scelerisque interdum eget tellus non nibh scelerisque bibendum.
+
+
+ Judith Black
+ CEO of Tuple
+
+
+
+
+
#}
+
+
+
+
+
Questions/réponses
+
+
+
+
+ Le jeu propose-t-il du contenu payant ?
+
+
+
+
+ Non, Kalaxia est un jeu entièrement gratuit et ne propose pas de contenu payant.
+ Il n'y aura également pas de publicité en jeu, afin de proposer l'expérience la plus immersive possible.
+ Des publicités peuvent être présentes sur le portail de connexion, avant l'accès aux parties.
+
+
+
+
+
+
+
+ Faut-il installer le jeu ?
+
+
+
+
+ Kalaxia est un jeu navigateur, vous n'avez donc pas besoin de réaliser une installation pour jouer au jeu.
+ Créez simplement votre compte avec votre adresse e-mail, votre identifiant et votre mot de passe, puis vous pourrez directement accéder aux parties en cours !
+
+
+
+
+
+
+
+
+
+
+ {#
+
+
+
+
+
+
+ #}
+{% endblock body %}
diff --git a/templates/pages/portal/login.html.twig b/templates/pages/portal/login.html.twig
new file mode 100644
index 000000000..9edc9cd93
--- /dev/null
+++ b/templates/pages/portal/login.html.twig
@@ -0,0 +1,105 @@
+{% extends 'layouts/base.html.twig' %}
+
+{% block body %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% if error is not null %}
+
+ {{ error.messageKey }}
+
+ {% endif %}
+
+
+
+
+ Sign in
+
+
+
+
+
+
+
+
+ Or continue with
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock body %}
diff --git a/templates/pages/portal/player_choice.html.twig b/templates/pages/portal/player_choice.html.twig
new file mode 100644
index 000000000..3e451408f
--- /dev/null
+++ b/templates/pages/portal/player_choice.html.twig
@@ -0,0 +1,53 @@
+{% extends 'layouts/base.html.twig' %}
+
+{% block bodyClasses 'overflow-y-auto relative pt-[7rem]' %}
+
+{% block body %}
+
+
+
+
+{% endblock %}
diff --git a/templates/pages/portal/registration.html.twig b/templates/pages/portal/registration.html.twig
new file mode 100644
index 000000000..04ac5c98c
--- /dev/null
+++ b/templates/pages/portal/registration.html.twig
@@ -0,0 +1,123 @@
+{% extends 'layouts/base.html.twig' %}
+
+{% block body %}
+
+
+
+
+
+
+
+
Créez votre compte
+
+ Vous avez déjà un compte ?
+ Connectez-vous
+
+
+
+
+
+ {{ form_start(registrationForm, {'attr': {'class': 'space-y-6'}}) }}
+
+
+ {{ form_label(registrationForm.username) }}
+
+
+ {{ form_widget(registrationForm.username, { 'attr': { 'class': 'block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6' }}) }}
+
+
+
+
+
+ {{ form_label(registrationForm.email) }}
+
+
+ {{ form_widget(registrationForm.email, { 'attr': { 'class': 'block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6' }}) }}
+
+
+
+
+
+ {{ form_label(registrationForm.password.first) }}
+
+
+ {{ form_widget(registrationForm.password.first, { 'attr': { 'class': 'block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6' }}) }}
+
+
+
+
+
+ {{ form_label(registrationForm.password.second) }}
+
+
+ {{ form_widget(registrationForm.password.second, { 'attr': { 'class': 'block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6' }}) }}
+
+
+
+
+
+
+ {{ form_errors(registrationForm) }}
+
+
+
+ S'inscrire
+
+ {{ form_end(registrationForm) }}
+
+
+
+
+
+
+ Ou continuer avec
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock body %}
diff --git a/templates/pages/promethee/tech_info.html.twig b/templates/pages/promethee/tech_info.html.twig
new file mode 100644
index 000000000..2df2d7049
--- /dev/null
+++ b/templates/pages/promethee/tech_info.html.twig
@@ -0,0 +1,47 @@
+
+
+
+
+
' . $name . '
+
x
+
+
' . $shortDescription . '
+
+
+
+
+ coût en ressource
+ ' . Format::numberFormat($resource) . '
+
+
+ coût en crédit
+ ' . Format::numberFormat($credit) . '
+
+
+ temps
+ ' . Chronos::secondToFormat($time, 'lite') . '
+
+
+ points
+ ' . Format::numberFormat($points) . '
+
+
+
+
+ # description
+
Prérequis pour le prochain niveau
+
+ Technosphère, niv. ' . $technosphere . '
+ foreach ($researchList as $research) {
+ echo $research[0] . ', niv. ' . $research[1];
+ echo $research[2] ? ' ' : NULL;
+
+ }
+
+
+ # description
+
Description
+
' . $description . '
+
+
+
diff --git a/templates/pages/promethee/university.html.twig b/templates/pages/promethee/university.html.twig
new file mode 100644
index 000000000..c8dbd84e6
--- /dev/null
+++ b/templates/pages/promethee/university.html.twig
@@ -0,0 +1,144 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block left_sidebar %}
+ {{ include('molecules/common/subnav.html.twig') }}
+{% endblock left_sidebar %}
+
+{% block body %}
+
+ Université
+
+
+
+
+
+
+
+ {{ current_player.iUniversity|number }}
+
+
+ →
+
+
+
+
+ {{ university_investment_bonus }} %
+
+
+
+
+
+ {{ current_player.partNaturalSciences }} %
+
+
+ -
+ +
+
+
+
+
+ {{ current_player.partInformaticEngineering }} %
+
+
+ -
+ +
+
+
+
+
+ {{ current_player.partLifeSciences }} %
+
+
+ -
+ +
+
+
+
+
+ {{ current_player.partSocialPoliticalSciences }} %
+
+
+ -
+ +
+
+
+
+ {% set rest = 100 - [
+ current_player.partLifeSciences,
+ current_player.partNaturalSciences,
+ current_player.partInformaticEngineering,
+ current_player.partSocialPoliticalSciences,
+ ]|reduce((sum, v) => sum + v, 0) %}
+
+
+ {{ rest }} %
+
+
+ Augmente ou baisse tes investissements par 10% avec "CTRL + clic" ou "CMD + clic".
+
+
+
+
+
+
+ {{ (current_player.iUniversity * current_player.partNaturalSciences / 100)|round|number }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ (current_player.iUniversity * current_player.partLifeSciences / 100)|round|number }}
+
+
+
+
+
+
+
+
+
+
+ {{ (current_player.iUniversity * current_player.partInformaticEngineering / 100)|round|number }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ (current_player.iUniversity * current_player.partSocialPoliticalSciences / 100)|round|number }}
+
+
+
+
+
+
+
+
+
+
+
+ L’Université est une résidence qui assemble en son sein la recherche, la conservation et la transmission
+ de différents domaines de la connaissance. Ce bâtiment vous permettra d’attribuer des crédits dans différents domaines de recherche.
+ Ces différents domaines de recherche évolueront en fonction du nombre de crédits investis dans ceux-ci.
+ Cette résidence est la plus importante de votre planète, car en investissant des crédits elle permettra de débloquer, de donner des
+ bonus non seulement aux bâtiments de votre planète, mais également à ceux de votre base orbitale et à chacun de vos vaisseaux.
+ Chaque fois qu’un type de technologie est découvert dans votre université, une nouvelle technologie sera alors disponible dans la Technosphère.
+ Pensez à toujours allouer des crédits dans votre université pour optimiser au maximum vos flottes et vos infrastructures.
+
+
+
+
+{% endblock body %}
diff --git a/templates/pages/zeus/profile.html.twig b/templates/pages/zeus/profile.html.twig
new file mode 100644
index 000000000..42e05d84c
--- /dev/null
+++ b/templates/pages/zeus/profile.html.twig
@@ -0,0 +1,24 @@
+{% extends 'layouts/game.html.twig' %}
+
+{% block body %}
+ {% if has_splash_mode %}
+
+
+
+
Bienvenue sur Asylamba
+
Afin de bien commencer le jeu, cliquez sur l\'icône du tutoriel. Celui-ci vous guidera dans les premières étapes du jeu.
+
+
+ {% endif %}
+
+ {{ include('molecules/common/movers.html.twig') }}
+
+
+
+
+ {# obFastView component #}
+ {% for orbital_base in player_bases %}
+
+ {% endfor %}
+
+{% endblock body %}
diff --git a/templates/pages/zeus/registration/faction_choice.html.twig b/templates/pages/zeus/registration/faction_choice.html.twig
new file mode 100644
index 000000000..1924e4098
--- /dev/null
+++ b/templates/pages/zeus/registration/faction_choice.html.twig
@@ -0,0 +1,11 @@
+{% extends 'layouts/zeus/registration.html.twig' %}
+
+{% block content %}
+
+
+ {% for faction_data in sorted_factions|filter(data => data.faction.identifier != 0) %}
+
+ {% endfor %}
+
+
+{% endblock content %}
diff --git a/templates/pages/zeus/registration/place_choice.html.twig b/templates/pages/zeus/registration/place_choice.html.twig
new file mode 100644
index 000000000..a623733fc
--- /dev/null
+++ b/templates/pages/zeus/registration/place_choice.html.twig
@@ -0,0 +1,75 @@
+{% extends 'layouts/zeus/registration.html.twig' %}
+
+{% block content %}
+
+
+
+
+
+ Choisissez l'emplacement dans la galaxie
+
+
+
+ {% set rate = 750 / galaxy_configuration.galaxy.size %}
+
+
+
+
+ {% for sector in sectors %}
+ {% set faction_identifier = sector.faction|default({identifier: 0}).identifier %}
+ {% set coords = galaxy_configuration.getSectorCentroid(sector, rate) %}
+
+
+
+
+ {{- sector.identifier -}}
+
+
+
+ {% endfor %}
+
+
+
+
+
+{% endblock content %}
diff --git a/templates/pages/zeus/registration/profile.html.twig b/templates/pages/zeus/registration/profile.html.twig
new file mode 100644
index 000000000..3a0f0264b
--- /dev/null
+++ b/templates/pages/zeus/registration/profile.html.twig
@@ -0,0 +1,70 @@
+{% extends 'layouts/zeus/registration.html.twig' %}
+
+{% block content %}
+
+
+
+ Choisissez votre nom dans le jeu
+
+
+ {% if app.session.get('inscription').has('pseudo') %}
+ {% set value = app.session.get('inscription').get('pseudo') %}
+ {% elseif app.session.get('inscription').get('portalPseudo') %}
+ {% set value = app.session.get('inscription').get('portalPseudo') %}
+ {% else %}
+ {% set value = '' %}
+ {% endif %}
+
+
+
+ Comment vous ferez-vous appeler ?
+
+
+
+
+ {% for label, messages in app.flashes %}
+ {% if label == 'error' %}
+
+ {% for message in messages %}
+ {{ message }}
+ {% endfor %}
+
+ {% endif %}
+ {% endfor %}
+
+ Nous déconseillons les noms moins roleplay, essayez de coller avec l'histoire et les moeurs de votre faction.
+ Ce nom ne pourra pas être changé plus tard.
+
+
+
+ Choisissez un avatar
+
+
+ {% for avatar in avatars %}
+
+
+
+
+
+
+ {% endfor %}
+
+
+
+
+ Définir son profil
+ et passer à l'étape suivante
+
+
+
+{% endblock content %}
diff --git a/tests/Classes/Library/DateTimeConverterTest.php b/tests/Classes/Library/DateTimeConverterTest.php
index 9d26df99d..e0ec4b357 100644
--- a/tests/Classes/Library/DateTimeConverterTest.php
+++ b/tests/Classes/Library/DateTimeConverterTest.php
@@ -1,8 +1,8 @@
assertEquals($expectedMs, DateTimeConverter::to_ms($dateTime));
+ $this->assertEquals($expectedMs, DateTimeConverter::to_ms(new \DateTimeImmutable($dateTime)));
}
- public function provideMsData(): \Generator
+ public static function provideMsData(): \Generator
{
yield [
'+1 minute',
diff --git a/tests/Modules/Ares/Application/Handler/CommanderArmyHandlerTest.php b/tests/Modules/Ares/Application/Handler/CommanderArmyHandlerTest.php
new file mode 100644
index 000000000..bf4bb206a
--- /dev/null
+++ b/tests/Modules/Ares/Application/Handler/CommanderArmyHandlerTest.php
@@ -0,0 +1,18 @@
+get(VirtualCommanderHandler::class);
+ $armyHandler = static::getContainer()->get(CommanderArmyHandler::class);
+
+ $commander = $handler->createVirtualCommander($place);
+
+ static::assertSame('rebelle', $commander->name);
+ static::assertTrue($commander->isAffected());
+ static::assertSame($expectedSquadronsCount, $commander->level);
+ static::assertCount($expectedSquadronsCount, $commander->squadronsIds);
+ static::assertCount($expectedSquadronsCount, $commander->army);
+ static::assertSame($expectedPev, $armyHandler->getPev($commander));
+ }
+
+ public static function provideData(): \Generator
+ {
+ yield [
+ static::getPlaceMock(
+ danger: 0,
+ maxDanger: 10,
+ population: 147,
+ position: 1,
+ historyCoeff: 20,
+ resourcesCoeff: 60,
+ ),
+ 3,
+ 18,
+ ];
+ yield [
+ static::getPlaceMock(
+ danger: 3,
+ maxDanger: 10,
+ population: 147,
+ position: 1,
+ historyCoeff: 20,
+ resourcesCoeff: 60,
+ ),
+ 3,
+ 36,
+ ];
+ yield [
+ static::getPlaceMock(
+ danger: 0,
+ maxDanger: 7,
+ population: 100,
+ position: 1,
+ historyCoeff: 10,
+ resourcesCoeff: 40,
+ ),
+ 3,
+ 18,
+ ];
+ yield [
+ static::getPlaceMock(
+ danger: 3,
+ maxDanger: 7,
+ population: 100,
+ position: 3,
+ historyCoeff: 70,
+ resourcesCoeff: 10,
+ ),
+ 3,
+ 36,
+ ];
+ }
+
+ private static function getPlaceMock(
+ int $danger,
+ int $maxDanger,
+ int $population,
+ int $position,
+ int $historyCoeff,
+ int $resourcesCoeff,
+ ): Place {
+ return new Place(
+ id: Uuid::v4(),
+ player: null,
+ base: null,
+ system: new System(
+ id: Uuid::v4(),
+ sector: new Sector(
+ id: Uuid::v4(),
+ identifier: 1,
+ faction: null,
+ xPosition: 10,
+ yPosition: 10,
+ xBarycentric: 0,
+ yBarycentric: 0,
+ tax: 5,
+ name: null,
+ points: 5,
+ population: 0,
+ lifePlanet: 10,
+ prime: 1,
+ ),
+ faction: null,
+ xPosition: 10,
+ yPosition: 10,
+ typeOfSystem: 0,
+ ),
+ typeOfPlace: Place::TERRESTRIAL,
+ position: $position,
+ population: $population,
+ coefResources: $resourcesCoeff,
+ coefHistory: $historyCoeff,
+ resources: 0,
+ danger: $danger,
+ maxDanger: $maxDanger,
+ updatedAt: new \DateTimeImmutable(),
+ );
+ }
+}
diff --git a/tests/Modules/Ares/AresModuleTest.php b/tests/Modules/Ares/AresModuleTest.php
deleted file mode 100644
index a36712d0d..000000000
--- a/tests/Modules/Ares/AresModuleTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-module = new AresModule($this->getApplicationMock());
- }
-
- public function testGetName()
- {
- $this->assertEquals('Ares', $this->module->getName());
- }
-
- public function getApplicationMock()
- {
- $applicationMock = $this
- ->getMockBuilder(Application::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $applicationMock
- ->expects($this->any())
- ->method('getContainer')
- ->willReturnCallback([$this, 'getContainerMock'])
- ;
- return $applicationMock;
- }
-
- public function getContainerMock()
- {
- $containerMock = $this
- ->getMockBuilder(Container::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $containerMock
- ->expects($this->any())
- ->method('getParameter')
- ->willReturn(realpath('.'))
- ;
- return $containerMock;
- }
-}
diff --git a/tests/Modules/Ares/Manager/CommanderManagerTest.php b/tests/Modules/Ares/Manager/CommanderManagerTest.php
deleted file mode 100644
index 4ffdc8b98..000000000
--- a/tests/Modules/Ares/Manager/CommanderManagerTest.php
+++ /dev/null
@@ -1,488 +0,0 @@
-manager = new CommanderManager(
- $this->getEntityManagerMock(),
- $this->getFightManagerMock(),
- $this->getReportManagerMock(),
- $this->getOrbitalBaseManagerMock(),
- $this->getPlayerManagerMock(),
- $this->getPlayerBonusManagerMock(),
- $this->getPlaceManagerMock(),
- $this->getColorManagerMock(),
- $this->getNotificationManagerMock(),
- $this->getSchedulerMock(),
- $this->getEventDispatcherMock(),
- 100
- );
- }
-
- public function testGet()
- {
- $commander = $this->manager->get(1);
-
- $this->assertInstanceOf(Commander::class, $commander);
- $this->assertEquals(1, $commander->getId());
- $this->assertEquals('Avla', $commander->getName());
- }
-
- public function testGetBaseCommanders()
- {
- $commanders = $this->manager->getBaseCommanders(1);
-
- $this->assertCount(3, $commanders);
- $this->assertEquals(1, $commanders[0]->getRBase());
- $this->assertEquals(2, $commanders[1]->getId());
- $this->assertEquals('Nievra', $commanders[2]->getName());
- }
-
- /**
- * @dataProvider providePlaces
- * @param Place $place
- * @param array $expected
- */
- public function testCreateVirtualCommander(Place $place, $expected)
- {
- $commander = $this->manager->createVirtualCommander($place);
-
- $this->assertEquals('Null', $commander->getId());
- $this->assertEquals('rebelle', $commander->getName());
- $this->assertEquals(ID_GAIA, $commander->getRPlayer());
- $this->assertEquals($expected['level'], $commander->getLevel());
- $this->assertEquals($expected['pev_in_begin'], $commander->getPevInBegin());
- $this->assertEquals($expected['pev'], $commander->getPev());
- $this->assertCount($expected['nb_squadrons'], $commander->squadronsIds);
- }
-
- /**
- * @return array
- */
- public function providePlaces()
- {
- return [
- [
- (new Place())
- ->setPopulation(25)
- ->setCoefHistory(32)
- ->setCoefResources(60)
- ->setPosition(3443)
- ->setDanger(4)
- ->setMaxDanger(15)
- ,
- [
- 'level' => 3,
- 'pev_in_begin' => 18,
- 'pev' => 18,
- 'nb_squadrons' => 3
- ]
- ],
- [
- (new Place())
- ->setPopulation(60)
- ->setCoefHistory(14)
- ->setCoefResources(70)
- ->setPosition(34565)
- ->setDanger(3)
- ->setMaxDanger(15)
- ,
- [
- 'level' => 4,
- 'pev_in_begin' => 23,
- 'pev' => 23,
- 'nb_squadrons' => 4
- ]
- ],
- [
- (new Place())
- ->setPopulation(214)
- ->setCoefHistory(14)
- ->setCoefResources(84)
- ->setPosition(2789)
- ->setDanger(12)
- ->setMaxDanger(15)
- ,
- [
- 'level' => 5,
- 'pev_in_begin' => 200,
- 'pev' => 200,
- 'nb_squadrons' => 5
- ]
- ],
- [
- (new Place())
- ->setPopulation(50)
- ->setCoefHistory(20)
- ->setCoefResources(85)
- ->setPosition(26553)
- ->setDanger(15)
- ->setMaxDanger(15)
- ,
- [
- 'level' => 4,
- 'pev_in_begin' => 92,
- 'pev' => 92,
- 'nb_squadrons' => 4
- ]
- ],
- ];
- }
-
- /**
- * @dataProvider provideMovements
- * @param array $data
- * @param array $expected
- */
- public function testMove($data, $expected)
- {
- $commander = $data['commander'];
-
- $this->manager->move(
- $commander,
- $data['destination_place_id'],
- $data['start_place_id'],
- $data['type'],
- $data['length'],
- $data['duration']
- );
-
- $this->assertEquals(Commander::MOVING, $commander->getStatement());
- $this->assertEquals($data['destination_place_id'], $commander->getRPlaceDestination());
- $this->assertEquals($data['start_place_id'], $commander->getStartPlaceId());
- $this->assertEquals($data['type'], $commander->getTravelType());
- $this->assertEquals($data['length'], $commander->getTravelLength());
-
- $date = new \DateTime($commander->getStartedAt());
- $date->modify('+' . $data['duration'] . 'second');
- $this->assertEquals($commander->getArrivalDate(), $date->format('Y-m-d H:i:s'));
- }
-
- public function provideMovements()
- {
- return [
- [
- [
- 'commander' => $this->getCommanderMock(1),
- 'start_place_id' => 12,
- 'destination_place_id' => 156,
- 'type' => Commander::LOOT,
- 'length' => 18,
- 'duration' => 1800
- ],
- [
-
- ]
- ],
- [
- [
- 'commander' => $this->getCommanderMock(2),
- 'start_place_id' => 132,
- 'destination_place_id' => 546,
- 'type' => Commander::LOOT,
- 'length' => 24,
- 'duration' => 2400
- ],
- [
-
- ]
- ],
- [
- [
- 'commander' => $this->getCommanderMock(64),
- 'start_place_id' => 16,
- 'destination_place_id' => 653,
- 'type' => Commander::BACK,
- 'length' => 30,
- 'duration' => 3800
- ],
- [
-
- ]
- ],
- [
- [
- 'commander' => $this->getCommanderMock(12),
- 'start_place_id' => 145,
- 'destination_place_id' => 236,
- 'type' => Commander::COLO,
- 'length' => 30,
- 'duration' => 3600
- ],
- [
-
- ]
- ],
- ];
- }
-
- public function testEndTravel()
- {
- $commander = $this->getCommanderMock(1);
-
- $this->manager->endTravel($commander, Commander::AFFECTED);
-
- $this->assertEquals(Commander::AFFECTED, $commander->getStatement());
- $this->assertNull($commander->getStartPlaceId());
- $this->assertNull($commander->getRPlaceDestination());
- $this->assertNull($commander->getStartedAt());
- $this->assertNull($commander->getArrivalDate());
- $this->assertNull($commander->getTravelType());
- $this->assertNull($commander->getTravelLength());
- }
-
- public function testEndTravelToReserve()
- {
- $commander = $this->getCommanderMock(1);
-
- $this->manager->endTravel($commander, Commander::RESERVE);
-
- $this->assertEquals(Commander::RESERVE, $commander->getStatement());
- $this->assertNull($commander->getStartPlaceId());
- $this->assertNull($commander->getRPlaceDestination());
- $this->assertNull($commander->getStartedAt());
- $this->assertNull($commander->getArrivalDate());
- $this->assertNull($commander->getTravelType());
- $this->assertNull($commander->getTravelLength());
- }
-
- public function getEntityManagerMock()
- {
- $entityManagerMock = $this
- ->getMockBuilder('\Asylamba\Classes\Entity\EntityManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $entityManagerMock
- ->expects($this->any())
- ->method('persist')
- ->willReturn(true)
- ;
- $entityManagerMock
- ->expects($this->any())
- ->method('remove')
- ->willReturn(true)
- ;
- $entityManagerMock
- ->expects($this->any())
- ->method('flush')
- ->willReturn(true)
- ;
- $entityManagerMock
- ->expects($this->any())
- ->method('getRepository')
- ->willReturnCallback([$this, 'getRepositoryMock'])
- ;
- return $entityManagerMock;
- }
-
- public function getRepositoryMock($repository)
- {
- return $this->{[
- 'Asylamba\Modules\Ares\Model\Commander' => 'getCommanderRepositoryMock'
- ][$repository]}();
- }
-
- public function getCommanderRepositoryMock()
- {
- $repositoryMock = $this
- ->getMockBuilder('\Asylamba\Modules\Ares\Repository\CommanderRepository')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $repositoryMock
- ->expects($this->any())
- ->method('get')
- ->willReturnCallback([$this, 'getCommanderMock'])
- ;
- $repositoryMock
- ->expects($this->any())
- ->method('getBaseCommanders')
- ->willReturnCallback([$this, 'getCommandersMock'])
- ;
- return $repositoryMock;
- }
-
- public function getCommanderMock($id)
- {
- return
- (new Commander())
- ->setId($id)
- ->setName('Avla')
- ->setRPlaceDestination(15)
- ->setStartPlaceId(16)
- ->setTravelLength(18)
- ->setTravelType(Commander::LOOT)
- ->setArrivalDate((new \DateTime('+2 hours'))->format('Y-m-d H:i:s'))
- ;
- }
-
- public function getCommandersMock()
- {
- return [
- (new Commander())
- ->setId(1)
- ->setName('Anvla')
- ->setRBase(1),
- (new Commander())
- ->setId(2)
- ->setName('Zania')
- ->setRBase(1),
- (new Commander())
- ->setId(3)
- ->setName('Nievra')
- ->setRBase(1),
- ];
- }
-
- public function getFightManagerMock()
- {
- $fightManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Ares\Manager\FightManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $fightManagerMock;
- }
-
- public function getReportManagerMock()
- {
- $reportManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Ares\Manager\ReportManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $reportManagerMock;
- }
-
- public function getOrbitalBaseManagerMock()
- {
- $orbitalBaseManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Athena\Manager\OrbitalBaseManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $orbitalBaseManagerMock;
- }
-
- public function getPlayerManagerMock()
- {
- $playerManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Zeus\Manager\PlayerManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $playerManagerMock
- ->expects($this->any())
- ->method('get')
- ->willReturnCallback([$this, 'getPlayerMock'])
- ;
- return $playerManagerMock;
- }
-
- public function getPlayerMock()
- {
- return
- (new Player())
- ;
- }
-
- public function getPlayerBonusManagerMock()
- {
- $playerBonusManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Zeus\Manager\PlayerBonusManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $playerBonusManagerMock;
- }
-
- public function getPlaceManagerMock()
- {
- $placeManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Gaia\Manager\PlaceManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $placeManagerMock
- ->expects($this->any())
- ->method('get')
- ->willReturnCallback([$this, 'getPlaceMock'])
- ;
- return $placeManagerMock;
- }
-
- public function getPlaceMock()
- {
- return
- (new Place())
- ;
- }
-
- public function getColorManagerMock()
- {
- $colorManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Demeter\Manager\ColorManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $colorManagerMock;
- }
-
- public function getNotificationManagerMock()
- {
- $notificationManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Hermes\Manager\NotificationManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $notificationManagerMock;
- }
-
- public function getSessionMock()
- {
- $sessionMock = $this
- ->getMockBuilder('Asylamba\Classes\Library\Session\SessionWrapper')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $sessionMock;
- }
-
- public function getSchedulerMock()
- {
- $schedulerMock = $this
- ->getMockBuilder('\Asylamba\Classes\Scheduler\RealTimeActionScheduler')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $schedulerMock
- ->expects($this->any())
- ->method('schedule')
- ->willReturn(true)
- ;
- return $schedulerMock;
- }
-
- public function getEventDispatcherMock()
- {
- $eventDispatcherMock = $this
- ->getMockBuilder('Asylamba\Classes\Worker\EventDispatcher')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $eventDispatcherMock;
- }
-}
diff --git a/tests/Modules/Ares/Manager/FightManagerTest.php b/tests/Modules/Ares/Manager/FightManagerTest.php
new file mode 100644
index 000000000..48e98e120
--- /dev/null
+++ b/tests/Modules/Ares/Manager/FightManagerTest.php
@@ -0,0 +1,120 @@
+get(FightManager::class);
+
+ $commanderA = $this->getCommanderMock('attacker', 1);
+ $commanderD = $this->getCommanderMock('defender', 1);
+
+ $fightManager->startFight(
+ $commanderA,
+ $commanderD,
+ );
+
+ LiveReport::$isLegal = true;
+ LiveReport::$dFight = new \DateTimeImmutable();
+
+ $report = Report::fromLiveReport($this->getPlaceMock());
+
+ dump($report);
+ }
+
+ private function getCommanderMock(string $name, int $squadronsCount): Commander
+ {
+ $player = new Player();
+ $player->victory = 5;
+ $player->defeat = 1;
+
+ $commander = new Commander(
+ id: Uuid::v4(),
+ name: $name,
+ avatar: 't3-c4',
+ player: $player,
+ base: null,
+ enlistedAt: new \DateTimeImmutable(),
+ level: 1,
+ updatedAt: new \DateTimeImmutable(),
+ isVirtual: true,
+ );
+
+ for ($i = 0; $i < $squadronsCount; $i++) {
+ $commander->squadrons->add($this->getSquadronMock($commander));
+ }
+
+ return $commander;
+ }
+
+ private function getSquadronMock(Commander $commander): Squadron
+ {
+ return new Squadron(
+ id: Uuid::v4(),
+ commander: $commander,
+ createdAt: new \DateTimeImmutable(),
+ updatedAt: new \DateTimeImmutable(),
+ ship0: 4,
+ ship1: 1,
+ );
+ }
+
+ private function getPlaceMock(): Place
+ {
+ return new Place(
+ id: Uuid::v4(),
+ player: null,
+ base: null,
+ system: new System(
+ id: Uuid::v4(),
+ sector: new Sector(
+ id: Uuid::v4(),
+ identifier: 1,
+ faction: null,
+ xPosition: 10,
+ yPosition: 10,
+ xBarycentric: 0,
+ yBarycentric: 0,
+ tax: 5,
+ name: null,
+ points: 5,
+ population: 0,
+ lifePlanet: 10,
+ prime: 1,
+ ),
+ faction: null,
+ xPosition: 10,
+ yPosition: 10,
+ typeOfSystem: 0,
+ ),
+ typeOfPlace: Place::TERRESTRIAL,
+ position: 1,
+ population: 100,
+ coefResources: 60,
+ coefHistory: 20,
+ resources: 0,
+ danger: 5,
+ maxDanger: 10,
+ updatedAt: new \DateTimeImmutable(),
+ );
+ }
+}
diff --git a/tests/Modules/Ares/Model/CommanderTest.php b/tests/Modules/Ares/Model/CommanderTest.php
deleted file mode 100644
index 048ee19ba..000000000
--- a/tests/Modules/Ares/Model/CommanderTest.php
+++ /dev/null
@@ -1,143 +0,0 @@
-setId(1)
- ->setName('Theodorus')
- ->setAge(40)
- ->setSexe(1)
- ->setAvatar('comm-12-1.png')
- ->setLevel(2)
- ->setExperience(200)
- ->setComment('On my way !')
- ->setDCreation($createdAt)
- ->setDAffectation($affectedAt)
- ->setDDeath(null)
- ->setArrivalDate($arrivedAt)
- ->setTravelType(Commander::LOOT)
- ->setLengthTravel(15)
- ->setRPlayer(1)
- ->setRBase(18)
- ->setBaseName('Zambia')
- ->setRPlaceDestination(23)
- ->setDestinationPlaceName('Planète rebelle')
- ->setIsAttacker(true)
- ->setPalmares(1)
- ->setStatement(Commander::MOVING)
- ->setUpdatedAt($updatedAt)
- ;
- $this->assertEquals(1, $commander->getId());
- $this->assertEquals('Theodorus', $commander->getName());
- $this->assertEquals(40, $commander->getAge());
- $this->assertEquals(1, $commander->getSexe());
- $this->assertEquals('comm-12-1.png', $commander->getAvatar());
- $this->assertEquals(2, $commander->getLevel());
- $this->assertEquals(200, $commander->getExperience());
- $this->assertEquals('On my way !', $commander->getComment());
- $this->assertEquals($createdAt, $commander->getDCreation());
- $this->assertEquals($affectedAt, $commander->getDAffectation());
- $this->assertNull($commander->getDDeath());
- $this->assertEquals($arrivedAt, $commander->getArrivalDate());
- $this->assertEquals(Commander::LOOT, $commander->getTravelType());
- $this->assertEquals(15, $commander->getLengthTravel());
- $this->assertEquals(1, $commander->getRPlayer());
- $this->assertEquals(18, $commander->getRBase());
- $this->assertEquals('Zambia', $commander->getBaseName());
- $this->assertEquals(23, $commander->getRPlaceDestination());
- $this->assertEquals('Planète rebelle', $commander->getDestinationPlaceName());
- $this->assertTrue($commander->getIsAttacker());
- $this->assertEquals(1, $commander->getPalmares());
- $this->assertEquals(Commander::MOVING, $commander->getStatement());
- $this->assertEquals($updatedAt, $commander->getUpdatedAt());
- }
-
- public function testArmy()
- {
- $commander = new Commander();
- foreach ($this->getSquadronsMock() as $squadron) {
- $commander->addSquadronId($squadron['id']);
- $commander->addArmyInBegin($squadron['data']);
- }
- $commander->setPevInBegin();
-
- $this->assertCount(3, $commander->getArmyInBegin());
- $this->assertEquals(286, $commander->getPevInBegin());
- $this->assertEquals(3, $commander->getSizeArmy());
- $commander->setArmy();
- $this->assertCount(3, $commander->getArmy());
- }
-
- public function getSquadronsMock()
- {
- return [
- [
- 'id' => 1,
- 'data' => [
- 2,
- 0,
- 17,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- '2017-05-16 20:00:00',
- '2017-05-16 20:00:00'
- ]
- ], [
- 'id' => 2,
- 'data' => [
- 2,
- 0,
- 17,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- '2017-05-16 20:00:00',
- '2017-05-16 20:00:00'
- ]
- ], [
- 'id' => 3,
- 'data' => [
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- '2017-05-16 20:00:00',
- '2017-05-16 20:00:00'
- ]
- ]
- ];
- }
-}
\ No newline at end of file
diff --git a/tests/Modules/Ares/Model/ShipTest.php b/tests/Modules/Ares/Model/ShipTest.php
deleted file mode 100644
index e394294bc..000000000
--- a/tests/Modules/Ares/Model/ShipTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-setId(1);
-
- $this->assertEquals(1, $ship->getId());
- $this->assertEquals('Destroyer', $ship->getName());
- $this->assertEquals('Minotaure', $ship->getCodeName());
- $this->assertEquals(8, $ship->getNbrName());
- $this->assertEquals(1200, $ship->getLife());
- $this->assertEquals(88, $ship->getSpeed());
- $this->assertEquals(array(35, 35, 35, 35, 25, 10, 10), $ship->getAttack());
- $this->assertEquals(120, $ship->getDefense());
- $this->assertEquals(75, $ship->getPev());
-
- }
-
- public function testFight()
- {
- /*$ship = new ship();
- $ship->engage($this->getSquadronMock());*/
- }
-
- public function testBonus()
- {
-
- }
-
- protected function chooseEnemyMock($enemySquadron) {
- return 0;
- }
-
- public function getSquadronMock()
- {
- return [
- 'id' => 1,
- 'data' => [
- 2,
- 0,
- 17,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- '2017-05-16 20:00:00',
- '2017-05-16 20:00:00'
- ]
- ];
- }
-}
diff --git a/tests/Modules/Artemis/ArtemisModuleTest.php b/tests/Modules/Artemis/ArtemisModuleTest.php
deleted file mode 100644
index f01525f08..000000000
--- a/tests/Modules/Artemis/ArtemisModuleTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-module = new ArtemisModule($this->getApplicationMock());
- }
-
- public function testGetName()
- {
- $this->assertEquals('Artemis', $this->module->getName());
- }
-
- public function getApplicationMock()
- {
- $applicationMock = $this
- ->getMockBuilder(Application::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $applicationMock
- ->expects($this->any())
- ->method('getContainer')
- ->willReturnCallback([$this, 'getContainerMock'])
- ;
- return $applicationMock;
- }
-
- public function getContainerMock()
- {
- $containerMock = $this
- ->getMockBuilder(Container::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $containerMock
- ->expects($this->any())
- ->method('getParameter')
- ->willReturn(realpath('.'))
- ;
- return $containerMock;
- }
-}
diff --git a/tests/Modules/Athena/Application/Handler/Building/BuildingLevelHandlerTest.php b/tests/Modules/Athena/Application/Handler/Building/BuildingLevelHandlerTest.php
new file mode 100644
index 000000000..4e1d072a3
--- /dev/null
+++ b/tests/Modules/Athena/Application/Handler/Building/BuildingLevelHandlerTest.php
@@ -0,0 +1,246 @@
+buildingLevelHandler = new BuildingLevelHandler();
+ }
+
+ public function testIncreaseBuildingLevel(): void
+ {
+ $orbitalBase = static::generateOrbitalBase();
+
+ $this->buildingLevelHandler->increaseBuildingLevel($orbitalBase, OrbitalBaseResource::GENERATOR);
+ $this->buildingLevelHandler->increaseBuildingLevel($orbitalBase, OrbitalBaseResource::SPATIOPORT);
+ $this->buildingLevelHandler->increaseBuildingLevel($orbitalBase, OrbitalBaseResource::REFINERY);
+
+ static::assertEquals(4, $orbitalBase->levelGenerator);
+ static::assertEquals(1, $orbitalBase->levelSpatioport);
+ static::assertEquals(3, $orbitalBase->levelRefinery);
+
+ $this->buildingLevelHandler->increaseBuildingLevel($orbitalBase, OrbitalBaseResource::GENERATOR);
+
+ static::assertEquals(5, $orbitalBase->levelGenerator);
+ }
+
+ public function testDecreaseBuildingLevel(): void
+ {
+ $orbitalBase = static::generateOrbitalBase();
+
+ $this->buildingLevelHandler->decreaseBuildingLevel($orbitalBase, OrbitalBaseResource::GENERATOR);
+ $this->buildingLevelHandler->decreaseBuildingLevel($orbitalBase, OrbitalBaseResource::SPATIOPORT);
+ $this->buildingLevelHandler->decreaseBuildingLevel($orbitalBase, OrbitalBaseResource::REFINERY);
+
+ static::assertEquals(2, $orbitalBase->levelGenerator);
+ static::assertEquals(0, $orbitalBase->levelSpatioport);
+ static::assertEquals(1, $orbitalBase->levelRefinery);
+
+ $this->buildingLevelHandler->decreaseBuildingLevel($orbitalBase, OrbitalBaseResource::GENERATOR);
+
+ static::assertEquals(1, $orbitalBase->levelGenerator);
+ }
+
+ public function testGetBuildingLevel(): void
+ {
+ $orbitalBase = $this->generateOrbitalBase();
+
+ static::assertEquals(
+ $orbitalBase->levelGenerator,
+ $this->buildingLevelHandler->getBuildingLevel($orbitalBase, OrbitalBaseResource::GENERATOR),
+ );
+
+ static::assertEquals(
+ $orbitalBase->levelRefinery,
+ $this->buildingLevelHandler->getBuildingLevel($orbitalBase, OrbitalBaseResource::REFINERY),
+ );
+
+ static::assertEquals(
+ $orbitalBase->levelStorage,
+ $this->buildingLevelHandler->getBuildingLevel($orbitalBase, OrbitalBaseResource::STORAGE),
+ );
+
+ static::assertEquals(
+ $orbitalBase->levelDock1,
+ $this->buildingLevelHandler->getBuildingLevel($orbitalBase, OrbitalBaseResource::DOCK1),
+ );
+
+ static::assertEquals(
+ $orbitalBase->levelCommercialPlateforme,
+ $this->buildingLevelHandler->getBuildingLevel($orbitalBase, OrbitalBaseResource::COMMERCIAL_PLATEFORME),
+ );
+ }
+
+ /**
+ * @param list
$buildingQueues
+ *
+ * @dataProvider provideData
+ */
+ public function testGetBuildingRealLevel(
+ OrbitalBase $orbitalBase,
+ array $buildingQueues,
+ int $buildingIdentifier,
+ int $expectedRealLevel,
+ ): void {
+ $realLevel = $this->buildingLevelHandler->getBuildingRealLevel(
+ $orbitalBase,
+ $buildingIdentifier,
+ $buildingQueues,
+ );
+
+ static::assertEquals($realLevel, $expectedRealLevel);
+ }
+
+ public function testGetInvalidBuildingLevel(): void
+ {
+ $orbitalBase = static::generateOrbitalBase();
+
+ static::expectException(\LogicException::class);
+ static::expectExceptionMessage('Building identifier 99 is not valid');
+
+ $this->buildingLevelHandler->getBuildingLevel($orbitalBase, 99);
+ }
+
+ public function testGetInvalidBuildingRealLevel(): void
+ {
+ $orbitalBase = static::generateOrbitalBase();
+ $buildingQueues = static::generateBuildingQueues($orbitalBase);
+
+ static::expectException(\LogicException::class);
+ static::expectExceptionMessage('Building identifier 99 is not valid');
+
+ $this->buildingLevelHandler->getBuildingRealLevel($orbitalBase, 99, $buildingQueues);
+ }
+
+ /**
+ * @return \Generator{0: OrbitalBase, 1: list, 2: int, 3: int}
+ */
+ public static function provideData(): \Generator
+ {
+ $orbitalBase = static::generateOrbitalBase();
+ $buildingQueues = static::generateBuildingQueues($orbitalBase);
+
+ yield [
+ $orbitalBase,
+ $buildingQueues,
+ OrbitalBaseResource::GENERATOR,
+ 7,
+ ];
+
+ yield [
+ $orbitalBase,
+ $buildingQueues,
+ OrbitalBaseResource::REFINERY,
+ 4,
+ ];
+
+ yield [
+ $orbitalBase,
+ $buildingQueues,
+ OrbitalBaseResource::COMMERCIAL_PLATEFORME,
+ 1,
+ ];
+
+ yield [
+ $orbitalBase,
+ $buildingQueues,
+ OrbitalBaseResource::DOCK1,
+ 2,
+ ];
+
+ yield [
+ $orbitalBase,
+ $buildingQueues,
+ OrbitalBaseResource::STORAGE,
+ 5,
+ ];
+
+ yield [
+ $orbitalBase,
+ $buildingQueues,
+ OrbitalBaseResource::DOCK2,
+ 0,
+ ];
+ }
+
+ private static function generateOrbitalBase(): OrbitalBase
+ {
+ return new OrbitalBase(
+ id: Uuid::v4(),
+ place: new Place(
+ id: Uuid::v4(),
+ player: new Player(),
+ base: null,
+ system: new System(
+ id: Uuid::v4(),
+ sector: null,
+ faction: null,
+ xPosition: 10,
+ yPosition: 20,
+ typeOfSystem: 0,
+ ),
+ typeOfPlace: Place::TERRESTRIAL,
+ position: 1,
+ population: 100,
+ coefResources: 60,
+ coefHistory: 20,
+ resources: 20000,
+ danger: 40,
+ maxDanger: 60,
+ updatedAt: new \DateTimeImmutable(),
+ ),
+ player: new Player(),
+ name: 'My wonderful base',
+ levelGenerator: 3,
+ levelRefinery: 2,
+ levelCommercialPlateforme: 0,
+ levelStorage: 5,
+ );
+ }
+
+ /**
+ * @return list
+ */
+ private static function generateBuildingQueues(OrbitalBase $orbitalBase): array
+ {
+ return [
+ static::generateBuildingQueue($orbitalBase, OrbitalBaseResource::GENERATOR, 6),
+ static::generateBuildingQueue($orbitalBase, OrbitalBaseResource::GENERATOR, 5),
+ static::generateBuildingQueue($orbitalBase, OrbitalBaseResource::REFINERY, 4),
+ static::generateBuildingQueue($orbitalBase, OrbitalBaseResource::GENERATOR, 7),
+ static::generateBuildingQueue($orbitalBase, OrbitalBaseResource::REFINERY, 3),
+ static::generateBuildingQueue($orbitalBase, OrbitalBaseResource::COMMERCIAL_PLATEFORME, 1),
+ static::generateBuildingQueue($orbitalBase, OrbitalBaseResource::DOCK1, 1),
+ static::generateBuildingQueue($orbitalBase, OrbitalBaseResource::DOCK1, 2),
+ ];
+ }
+
+ private static function generateBuildingQueue(
+ OrbitalBase $orbitalBase,
+ int $buildingIdentifier,
+ int $targetLevel,
+ ): BuildingQueue {
+ return new BuildingQueue(
+ id: Uuid::v4(),
+ base: $orbitalBase,
+ buildingNumber: $buildingIdentifier,
+ targetLevel: $targetLevel,
+ startedAt: new \DateTimeImmutable(),
+ endedAt: new \DateTimeImmutable(),
+ );
+ }
+}
diff --git a/tests/Modules/Athena/AthenaModuleTest.php b/tests/Modules/Athena/AthenaModuleTest.php
deleted file mode 100644
index 421063048..000000000
--- a/tests/Modules/Athena/AthenaModuleTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-module = new AthenaModule($this->getApplicationMock());
- }
-
- public function testGetName()
- {
- $this->assertEquals('Athena', $this->module->getName());
- }
-
- public function getApplicationMock()
- {
- $applicationMock = $this
- ->getMockBuilder(Application::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $applicationMock
- ->expects($this->any())
- ->method('getContainer')
- ->willReturnCallback([$this, 'getContainerMock'])
- ;
- return $applicationMock;
- }
-
- public function getContainerMock()
- {
- $containerMock = $this
- ->getMockBuilder(Container::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $containerMock
- ->expects($this->any())
- ->method('getParameter')
- ->willReturn(realpath('.'))
- ;
- return $containerMock;
- }
-}
diff --git a/tests/Modules/Athena/Domain/Service/Base/Building/BuildingDataHandlerTest.php b/tests/Modules/Athena/Domain/Service/Base/Building/BuildingDataHandlerTest.php
new file mode 100644
index 000000000..e32affb43
--- /dev/null
+++ b/tests/Modules/Athena/Domain/Service/Base/Building/BuildingDataHandlerTest.php
@@ -0,0 +1,65 @@
+get(BuildingDataHandler::class);
+
+ static::assertSame($handler->getBuildingTimeCost($identifier, $level), $costs['time']);
+ }
+
+ #[DataProvider('provideTestDataForBuildingCost')]
+ public function testGetBuildingResourceCost(int $identifier, int $level, array $costs): void
+ {
+ static::bootKernel();
+ /** @var BuildingDataHandler $handler */
+ $handler = static::getContainer()->get(BuildingDataHandler::class);
+
+ static::assertSame($handler->getBuildingResourceCost($identifier, $level), $costs['resources']);
+ }
+
+ /**
+ * @return Generator
+ */
+ public static function provideTestDataForBuildingCost(): Generator
+ {
+ yield [
+ OrbitalBaseResource::GENERATOR,
+ 2,
+ [
+ 'time' => 28,
+ 'resources' => 137,
+ ],
+ ];
+
+ yield [
+ OrbitalBaseResource::REFINERY,
+ 14,
+ [
+ 'time' => 800,
+ 'resources' => 4130,
+ ],
+ ];
+
+ yield [
+ OrbitalBaseResource::SPATIOPORT,
+ 10,
+ [
+ 'time' => 2066,
+ 'resources' => 27000,
+ ],
+ ];
+ }
+}
diff --git a/tests/Modules/Athena/Infrastructure/Validator/CanMakeBuildingValidatorTest.php b/tests/Modules/Athena/Infrastructure/Validator/CanMakeBuildingValidatorTest.php
new file mode 100644
index 000000000..de66e69d6
--- /dev/null
+++ b/tests/Modules/Athena/Infrastructure/Validator/CanMakeBuildingValidatorTest.php
@@ -0,0 +1,113 @@
+get(ValidatorInterface::class);
+ SmallMapStory::load();
+
+ $base = OrbitalBaseFactory::createOne([
+ 'levelGenerator' => 9,
+ 'levelSpatioport' => 0,
+ ])->object();
+ $targetLevel = 1;
+ $technology = TechnologyFactory::createOne()->object();
+ $buildingIdentifier = OrbitalBaseResource::SPATIOPORT;
+ $buildingQueuesCount = 0;
+ $violationList = [];
+
+ $buildingConstructionOrder = new BuildingConstructionOrder(
+ orbitalBase: $base,
+ technology: $technology,
+ buildingIdentifier: $buildingIdentifier,
+ targetLevel: $targetLevel,
+ );
+
+ $violations = $validator->validate($buildingConstructionOrder, new CanMakeBuilding($buildingQueuesCount));
+
+ static::assertCount(count($violationList), $violations);
+
+ foreach ($violations as $violation) {
+ static::assertInstanceOf(HasUnlockedBuilding::class, $violation->getConstraint());
+ }
+ }
+
+ public function testValidatorManually(): void
+ {
+ static::markTestSkipped('Must fix usage of Foundry stories');
+
+ static::bootKernel();
+ /** @var ValidatorInterface $validator */
+ $validator = static::getContainer()->get(ValidatorInterface::class);
+
+ SmallMapStory::load();
+
+ $base = OrbitalBaseFactory::createOne([
+ 'levelGenerator' => 9,
+ 'levelSpatioport' => 0,
+ ])->object();
+ $technology = TechnologyFactory::createOne()->object();
+ $buildingIdentifier = OrbitalBaseResource::SPATIOPORT;
+ $targetLevel = 1;
+ $violationList = [
+
+ ];
+
+ $buildingConstructionOrder = new BuildingConstructionOrder(
+ orbitalBase: $base,
+ technology: $technology,
+ buildingIdentifier: $buildingIdentifier,
+ targetLevel: $targetLevel,
+ );
+
+ $violations = $validator->validate($buildingConstructionOrder, new CanMakeBuilding(0));
+
+ static::assertCount(count($violationList), $violations);
+
+ foreach ($violations as $violation) {
+ static::assertInstanceOf(HasUnlockedBuilding::class, $violation->getConstraint());
+ }
+ }
+
+ private function provideData(): \Generator
+ {
+ yield [
+ OrbitalBaseFactory::new()->withoutPersisting()->create([
+ 'levelGenerator' => 9,
+ 'levelSpatioport' => 0,
+ ]),
+ TechnologyFactory::new()->withoutPersisting()->create(),
+ OrbitalBaseResource::SPATIOPORT,
+ 1,
+ [
+
+ ]
+ ];
+ }
+}
diff --git a/tests/Modules/Atlas/AtlasModuleTest.php b/tests/Modules/Atlas/AtlasModuleTest.php
deleted file mode 100644
index 8af13ddb4..000000000
--- a/tests/Modules/Atlas/AtlasModuleTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-module = new AtlasModule($this->getApplicationMock());
- }
-
- public function testGetName()
- {
- $this->assertEquals('Atlas', $this->module->getName());
- }
-
- public function getApplicationMock()
- {
- $applicationMock = $this
- ->getMockBuilder(Application::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $applicationMock
- ->expects($this->any())
- ->method('getContainer')
- ->willReturnCallback([$this, 'getContainerMock'])
- ;
- return $applicationMock;
- }
-
- public function getContainerMock()
- {
- $containerMock = $this
- ->getMockBuilder(Container::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $containerMock
- ->expects($this->any())
- ->method('getParameter')
- ->willReturn(realpath('.'))
- ;
- return $containerMock;
- }
-}
diff --git a/tests/Modules/Atlas/Routine/FactionRoutineTest.php b/tests/Modules/Atlas/Routine/FactionRoutineTest.php
index 3334a0091..ae1121870 100644
--- a/tests/Modules/Atlas/Routine/FactionRoutineTest.php
+++ b/tests/Modules/Atlas/Routine/FactionRoutineTest.php
@@ -1,45 +1,53 @@
140,
2 => 142,
4 => 130,
5 => 135,
];
-
+
public function setUp(): void
{
- $this->routine = new FactionRoutine();
+ static::bootKernel();
+
+ $this->routine = static::getContainer()->get(FactionRoutineHandler::class);
$this->rankings = [];
}
-
- public function testExecute()
+
+ public function testExecute(): never
{
+ static::markTestSkipped('Must migrate to the new domain model classes');
+
$faction = $this->getFactionMock(1);
-
- $this->routine->execute($faction, $this->getPlayerRankingsMock(1), $this->getRoutesIncomeMock(1), $this->getSectorsMock());
-
+
+ $this->routine->execute(
+ $faction,
+ $this->getPlayerRankingsMock(1),
+ $this->getRoutesIncomeMock(1),
+ $this->getSectorsMock());
+
$results = $this->routine->getResults();
-
+
$this->assertCount(1, $results);
$this->assertArrayHasKey(1, $results);
$this->assertEquals(11634, $results[1]['general']);
@@ -47,22 +55,44 @@ public function testExecute()
$this->assertEquals(5, $results[1]['territorial']);
$this->assertEquals(140, $results[1]['points']);
}
-
- public function testProcessResults()
+
+ public function testProcessResults(): never
{
+ static::markTestSkipped('Must migrate to the new domain model classes');
+
$factions = [
$this->getFactionMock(1),
$this->getFactionMock(2),
$this->getFactionMock(4),
- $this->getFactionMock(5)
+ $this->getFactionMock(5),
];
- $this->routine->execute($factions[0], $this->getPlayerRankingsMock(1), $this->getRoutesIncomeMock(1), $this->getSectorsMock());
- $this->routine->execute($factions[1], $this->getPlayerRankingsMock(2), $this->getRoutesIncomeMock(3), $this->getSectorsMock());
- $this->routine->execute($factions[2], $this->getPlayerRankingsMock(4), $this->getRoutesIncomeMock(2), $this->getSectorsMock());
- $this->routine->execute($factions[3], $this->getPlayerRankingsMock(5), $this->getRoutesIncomeMock(4), $this->getSectorsMock());
-
- $this->routine->processResults($this->getRankingMock(), $factions, $this->getFactionRankingManagerMock());
-
+ $this->routine->execute(
+ $factions[0],
+ $this->getPlayerRankingsMock(1),
+ $this->getRoutesIncomeMock(1),
+ $this->getSectorsMock(),
+ );
+ $this->routine->execute(
+ $factions[1],
+ $this->getPlayerRankingsMock(2),
+ $this->getRoutesIncomeMock(3),
+ $this->getSectorsMock(),
+ );
+ $this->routine->execute(
+ $factions[2],
+ $this->getPlayerRankingsMock(4),
+ $this->getRoutesIncomeMock(2),
+ $this->getSectorsMock(),
+ );
+ $this->routine->execute(
+ $factions[3],
+ $this->getPlayerRankingsMock(5),
+ $this->getRoutesIncomeMock(4),
+ $this->getSectorsMock(),
+ );
+
+ $this->routine->processResults($this->getRankingMock(), $factions);
+
$this->assertCount(4, $this->rankings);
$this->assertEquals(1, $this->rankings[0]->getFactionId());
$this->assertEquals(165, $this->rankings[0]->getPoints());
@@ -78,7 +108,7 @@ public function testProcessResults()
$this->assertEquals(5, $this->rankings[0]->getTerritorial());
$this->assertEquals(1, $this->rankings[0]->getTerritorialPosition());
$this->assertEquals(3, $this->rankings[0]->getTerritorialVariation());
-
+
$this->assertEquals(2, $this->rankings[1]->getFactionId());
$this->assertEquals(171, $this->rankings[1]->getPoints());
$this->assertEquals(1, $this->rankings[1]->getPointsPosition());
@@ -93,7 +123,7 @@ public function testProcessResults()
$this->assertEquals(1, $this->rankings[1]->getTerritorial());
$this->assertEquals(2, $this->rankings[1]->getTerritorialPosition());
$this->assertEquals(0, $this->rankings[1]->getTerritorialVariation());
-
+
$this->assertEquals(4, $this->rankings[2]->getFactionId());
$this->assertEquals(156, $this->rankings[2]->getPoints());
$this->assertEquals(4, $this->rankings[2]->getPointsPosition());
@@ -108,7 +138,7 @@ public function testProcessResults()
$this->assertEquals(1, $this->rankings[2]->getTerritorial());
$this->assertEquals(2, $this->rankings[2]->getTerritorialPosition());
$this->assertEquals(1, $this->rankings[2]->getTerritorialVariation());
-
+
$this->assertEquals(5, $this->rankings[3]->getFactionId());
$this->assertEquals(170, $this->rankings[3]->getPoints());
$this->assertEquals(2, $this->rankings[3]->getPointsPosition());
@@ -124,59 +154,28 @@ public function testProcessResults()
$this->assertEquals(2, $this->rankings[3]->getTerritorialPosition());
$this->assertEquals(-1, $this->rankings[3]->getTerritorialVariation());
}
-
- public function getFactionMock($id)
- {
- return
- (new Color())
- ->setId($id)
- ->setOfficialName(ColorResource::getInfo($id, 'officialName'))
- ->setPopularName(ColorResource::getInfo($id, 'popularName'))
- ->setDevise(ColorResource::getInfo($id, 'devise'))
- ->setPlayers(11)
- ->setActivePlayers(8)
- ->setChiefId(3)
- ->setCredits(15000)
- ->setIsInGame(true)
- ->setIsClosed(false)
- ->setIsWinner(false)
- ->setRankingPoints($this->rankingPoints[$id])
- ;
- }
-
- public function getRankingMock()
+
+ public function getFactionMock(int $identifier): Color
{
- return
- (new Ranking())
- ->setId(1)
- ;
+ return new Color(
+ id: Uuid::v4(),
+ identifier: $identifier,
+ isWinner: false,
+ credits: 15000,
+ rankingPoints: $this->rankingPoints[$identifier],
+ isClosed: false,
+ isInGame: true,
+ );
}
-
- public function getFactionRankingManagerMock()
+
+ public function getRankingMock(): Ranking
{
- $factionRankingManager = $this
- ->getMockBuilder('Asylamba\Modules\Atlas\Manager\FactionRankingManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $factionRankingManager
- ->expects($this->any())
- ->method('size')
- ->willReturn(4)
- ;
- $factionRankingManager
- ->expects($this->any())
- ->method('get')
- ->willReturnCallback([$this, 'getFactionRankingMock'])
- ;
- $factionRankingManager
- ->expects($this->any())
- ->method('add')
- ->willReturnCallback([$this, 'storeFactionRanking'])
- ;
- return $factionRankingManager;
+ return new Ranking(
+ id: 1,
+ createdAt: new \DateTimeImmutable(),
+ );
}
-
+
public function getFactionRankingMock($id)
{
return [
@@ -210,12 +209,12 @@ public function getFactionRankingMock($id)
->setTerritorialPosition(1),
][$id];
}
-
+
public function storeFactionRanking(FactionRanking $factionRanking)
{
$this->rankings[] = $factionRanking;
}
-
+
public function getPlayerRankingsMock($factionId)
{
return [
@@ -266,18 +265,18 @@ public function getPlayerRankingsMock($factionId)
(new Player())
->setRColor($factionId)
)
- ->setGeneral(533)
+ ->setGeneral(533),
];
}
-
+
public function getRoutesIncomeMock($coeff)
{
return [
'nb' => 6,
- 'income' => 22560 * $coeff
+ 'income' => 22560 * $coeff,
];
}
-
+
public function getSectorsMock()
{
return [
diff --git a/tests/Modules/Demeter/Application/Election/NextElectionDateCalculatorTest.php b/tests/Modules/Demeter/Application/Election/NextElectionDateCalculatorTest.php
new file mode 100644
index 000000000..f63b354be
--- /dev/null
+++ b/tests/Modules/Demeter/Application/Election/NextElectionDateCalculatorTest.php
@@ -0,0 +1,148 @@
+value;
+ $nextElectionDateCalculator = static::getContainer()->get(NextElectionDateCalculator::class);
+
+ $faction = FactionFactory::createOne([
+ 'identifier' => $data['identifier'],
+ 'regime' => $data['regime'],
+ 'electionStatement' => $data['electionStatement'],
+ 'lastElectionHeldAt' => $data['lastElectionHeldAt'],
+ ])->object();
+ ElectionFactory::createOne([
+ 'faction' => $faction,
+ 'dElection' => $data['lastElectionHeldAt'],
+ ])->object();
+
+ $startDate = $nextElectionDateCalculator->getCampaignStartDate($faction);
+
+ static::assertEquals($expected['campaign_start_date'], $startDate);
+ }
+
+ #[DataProvider('provideData')]
+ public function testGetPutschEndDate(array $data, array $expected, TimeMode $timeMode): void
+ {
+ $_ENV['SERVER_TIME_MODE'] = $timeMode->value;
+ $nextElectionDateCalculator = static::getContainer()->get(NextElectionDateCalculator::class);
+
+ $faction = FactionFactory::createOne([
+ 'identifier' => $data['identifier'],
+ 'regime' => $data['regime'],
+ 'electionStatement' => $data['electionStatement'],
+ 'lastElectionHeldAt' => $data['lastElectionHeldAt'],
+ ])->object();
+ ElectionFactory::createOne([
+ 'faction' => $faction,
+ 'dElection' => $data['lastElectionHeldAt'],
+ ])->object();
+
+ $campaignEndDate = $nextElectionDateCalculator->getCampaignEndDate($faction);
+
+ static::assertEquals($expected['putsch_end_date'], $campaignEndDate);
+ }
+
+ public function testGetEndDate(): void
+ {
+ self::markTestIncomplete('Not Implemented');
+ }
+
+ public function testGetStartDate(): void
+ {
+ self::markTestIncomplete('Not Implemented');
+ }
+
+ public function testGetBallotDate(): void
+ {
+ self::markTestIncomplete('Not Implemented');
+ }
+
+ public function testGetSenateUpdateMessage(): void
+ {
+ self::markTestIncomplete('Not Implemented');
+ }
+
+ public function testGetCampaignEndDate(): void
+ {
+ self::markTestIncomplete('Not Implemented');
+ }
+
+ /**
+ * @return Generator>>
+ */
+ public static function provideData(): Generator
+ {
+ Clock::set(new MockClock('2023-06-05 10:00:00'));
+
+ yield 'Standard mode' => [
+ [
+ 'identifier' => ColorResource::EMPIRE,
+ 'regime' => Color::REGIME_DEMOCRATIC,
+ 'electionStatement' => Color::MANDATE,
+ 'lastElectionHeldAt' => new DatePoint('2023-06-01 17:00:00'),
+ ],
+ [
+ 'campaign_start_date' => new DatePoint('2023-06-08 17:00:00'),
+ 'putsch_end_date' => new DatePoint('2023-06-12 17:00:00'),
+ ],
+ TimeMode::Standard,
+ ];
+
+ yield 'Standard mode with late elections' => [
+ [
+ 'identifier' => ColorResource::EMPIRE,
+ 'regime' => Color::REGIME_DEMOCRATIC,
+ 'electionStatement' => Color::MANDATE,
+ 'lastElectionHeldAt' => new DatePoint('2023-03-28 17:00:00'),
+ ],
+ [
+ 'campaign_start_date' => new DatePoint('2023-06-06 17:00:00'),
+ 'putsch_end_date' => new DatePoint('2023-06-13 17:00:00'),
+ ],
+ TimeMode::Standard,
+ ];
+
+ yield 'Fast mode with late elections' => [
+ [
+ 'identifier' => ColorResource::EMPIRE,
+ 'regime' => Color::REGIME_DEMOCRATIC,
+ 'electionStatement' => Color::MANDATE,
+ 'lastElectionHeldAt' => new DatePoint('2023-06-04 17:00:00'),
+ ],
+ [
+ 'campaign_start_date' => new DatePoint('2023-06-05 10:38:24'),
+ 'putsch_end_date' => new DatePoint('2023-06-05 10:32:36'),
+ ],
+ TimeMode::Fast,
+ ];
+ }
+}
diff --git a/tests/Modules/Demeter/DemeterModuleTest.php b/tests/Modules/Demeter/DemeterModuleTest.php
deleted file mode 100644
index 7a19d0216..000000000
--- a/tests/Modules/Demeter/DemeterModuleTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-module = new DemeterModule($this->getApplicationMock());
- }
-
- public function testGetName()
- {
- $this->assertEquals('Demeter', $this->module->getName());
- }
-
- public function getApplicationMock()
- {
- $applicationMock = $this
- ->getMockBuilder(Application::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $applicationMock
- ->expects($this->any())
- ->method('getContainer')
- ->willReturnCallback([$this, 'getContainerMock'])
- ;
- return $applicationMock;
- }
-
- public function getContainerMock()
- {
- $containerMock = $this
- ->getMockBuilder(Container::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $containerMock
- ->expects($this->any())
- ->method('getParameter')
- ->willReturn(realpath('.'))
- ;
- return $containerMock;
- }
-}
diff --git a/tests/Modules/Demeter/Domain/Service/Law/GetApplicationDurationTest.php b/tests/Modules/Demeter/Domain/Service/Law/GetApplicationDurationTest.php
new file mode 100644
index 000000000..52a2433d3
--- /dev/null
+++ b/tests/Modules/Demeter/Domain/Service/Law/GetApplicationDurationTest.php
@@ -0,0 +1,103 @@
+value;
+ static::bootKernel();
+
+ Clock::set(new MockClock('2024-01-01 10:00:00'));
+
+ if ($expectedException) {
+ static::expectException(\InvalidArgumentException::class);
+ }
+
+ /** @var GetApplicationDuration $getApplicationDuration */
+ $getApplicationDuration = static::getContainer()->get(GetApplicationDuration::class);
+
+ $duration = $getApplicationDuration($lawType, $cycles);
+
+ static::assertEquals(new DatePoint($expectedEndDate), $duration);
+ }
+
+ public static function provideData(): \Generator
+ {
+ yield 'Standard instantaneous law' => [
+ Law::COMTAXEXPORT,
+ null,
+ '2024-01-01 10:00:00',
+ ];
+
+ yield 'Standard instaneous law with forbidden cycles argument' => [
+ Law::COMTAXEXPORT,
+ 3,
+ '2024-01-01 10:00:00',
+ true,
+ ];
+
+ yield 'Standard duration law with missing cycles argument' => [
+ Law::MILITARYSUBVENTION,
+ null,
+ '2024-01-01 10:00:00',
+ true,
+ ];
+
+ yield 'Standard duration law' => [
+ Law::MILITARYSUBVENTION,
+ 5,
+ '2024-01-01 15:00:00',
+ ];
+
+ yield 'Fast duration law' => [
+ Law::MILITARYSUBVENTION,
+ 6,
+ '2024-01-01 11:00:00',
+ false,
+ TimeMode::Fast,
+ ];
+
+ yield 'Standard duration law with minimum duration' => [
+ Law::MILITARYSUBVENTION,
+ -4,
+ '2024-01-01 11:00:00',
+ false,
+ TimeMode::Fast,
+ ];
+
+ yield 'Fast duration law with minimum duration' => [
+ Law::MILITARYSUBVENTION,
+ -4,
+ '2024-01-01 10:10:00',
+ false,
+ TimeMode::Fast,
+ ];
+
+ yield 'Standard duration law with exceeding duration' => [
+ Law::MILITARYSUBVENTION,
+ 3000,
+ '+100 days',
+ ];
+
+ yield 'Fast duration law with exceeding duration' => [
+ Law::MILITARYSUBVENTION,
+ 100000,
+ '+100 days',
+ false,
+ TimeMode::Fast,
+ ];
+ }
+}
diff --git a/tests/Modules/Demeter/Manager/ColorManagerTest.php b/tests/Modules/Demeter/Manager/ColorManagerTest.php
deleted file mode 100644
index 9a0081dfd..000000000
--- a/tests/Modules/Demeter/Manager/ColorManagerTest.php
+++ /dev/null
@@ -1,546 +0,0 @@
-manager = new ColorManager(
- $this->getEntityManagerMock(),
- $this->getPlayerManagerMock(),
- $this->getVoteManagerMock(),
- $this->getConversationManagerMock(),
- $this->getCandidateManagerMock(),
- $this->getElectionManagerMock(),
- $this->getLawManagerMock(),
- $this->getNotificationManagerMock(),
- $this->getConversationMessageManagerMock(),
- $this->getCommercialTaxManagerMock(),
- $this->getSectorManagerMock(),
- $this->getCommercialRouteManagerMock(),
- $this->getParserMock(),
- $this->getCTCMock(),
- $this->getSchedulerMock()
- );
- }
-
- public function testGet()
- {
- $faction = $this->manager->get(1);
-
- $this->assertEquals(1, $faction->getId());
- $this->assertEquals('Pokemon', $faction->getPopularName());
- $this->assertEquals('La Ligue', $faction->getOfficialName());
- $this->assertFalse($faction->getIsWinner());
- $this->assertFalse($faction->getIsClosed());
- $this->assertTrue($faction->getIsInGame());
- }
-
- public function testUpdateInfos()
- {
- $faction = $this->getFactionMock(1);
-
- $this->manager->updateInfos($faction);
-
- $this->assertEquals(18, $faction->getPlayers());
- $this->assertEquals(15, $faction->getActivePlayers());
- }
-
- public function testSendSenateNotif()
- {
- $this->assertNull($this->manager->sendSenateNotif($this->getFactionMock(1)));
- $this->assertNull($this->manager->sendSenateNotif($this->getFactionMock(2), true));
- }
-
- public function testUpdateSenate()
- {
- $this->assertNull($this->manager->updateSenate(1));
- }
-
- public function testUpdateStatus()
- {
- $players = $this->getFactionPlayersByRankingMock(1);
-
- $this->manager->updateStatus($this->getFactionMock(1), $players);
-
- $this->assertEquals(Player::PARLIAMENT, $players[0]->getStatus());
- $this->assertEquals(Player::PARLIAMENT, $players[1]->getStatus());
- $this->assertEquals(Player::PARLIAMENT, $players[5]->getStatus());
- $this->assertEquals(Player::PARLIAMENT, $players[9]->getStatus());
- $this->assertEquals(Player::STANDARD, $players[10]->getStatus());
- $this->assertEquals(Player::STANDARD, $players[11]->getStatus());
- }
-
- public function getEntityManagerMock()
- {
- $entityManagerMock = $this
- ->getMockBuilder('Asylamba\Classes\Entity\EntityManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $entityManagerMock
- ->expects($this->any())
- ->method('getRepository')
- ->willReturnCallback([$this, 'getRepositoryMock'])
- ;
- $entityManagerMock
- ->expects($this->any())
- ->method('persist')
- ->willReturn(true)
- ;
- $entityManagerMock
- ->expects($this->any())
- ->method('remove')
- ->willReturn(true)
- ;
- $entityManagerMock
- ->expects($this->any())
- ->method('flush')
- ->willReturn(true)
- ;
- return $entityManagerMock;
- }
-
- public function getRepositoryMock()
- {
- $repositoryMock = $this
- ->getMockBuilder('Asylamba\Modules\Demeter\Repository\ColorRepository')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $repositoryMock
- ->expects($this->any())
- ->method('get')
- ->willReturnCallback([$this, 'getFactionMock'])
- ;
- $repositoryMock
- ->expects($this->any())
- ->method('getAll')
- ->willReturnCallback([$this, 'getAllFactionsMock'])
- ;
- $repositoryMock
- ->expects($this->any())
- ->method('getOpenFactions')
- ->willReturnCallback([$this, 'getOpenFactionsMock'])
- ;
- $repositoryMock
- ->expects($this->any())
- ->method('getInGameFactions')
- ->willReturnCallback([$this, 'getInGameFactionsMock'])
- ;
- $repositoryMock
- ->expects($this->any())
- ->method('getAllByActivePlayersNumber')
- ->willReturnCallback([$this, 'getAllByActivePlayersNumberMock'])
- ;
- $repositoryMock
- ->expects($this->any())
- ->method('getByRegimeAndElectionStatement')
- ->willReturnCallback([$this, 'getByRegimeAndElectionStatementMock'])
- ;
- return $repositoryMock;
- }
-
- public function getFactionMock($id)
- {
- return
- (new Color())
- ->setId($id)
- ->setChiefId(1)
- ->setCredits(10000)
- ->setDevise('Ensemble pour la victoire !')
- ->setElectionStatement(Color::MANDATE)
- ->setMandateDuration(1800)
- ->setPopularName('Pokemon')
- ->setOfficialName('La Ligue')
- ->setRegime(Color::ROYALISTIC)
- ->setIsInGame(true)
- ->setIsWinner(false)
- ->setIsClosed(false)
- ->setAlive(true)
- ;
- }
-
- public function getAllFactionsMock()
- {
- return [];
- }
-
- public function getOpenFactionsMock()
- {
- return [];
- }
-
- public function getInGameFactionsMock()
- {
- return [];
- }
-
- public function getAllByActivePlayersNumberMock()
- {
- return [];
- }
-
- public function getByRegimeAndElectionStatus()
- {
- return [];
- }
-
- public function getPlayerManagerMock()
- {
- $playerManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Zeus\Manager\PlayerManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $playerManagerMock
- ->expects($this->any())
- ->method('get')
- ->willReturnCallback([$this, 'getPlayerMock'])
- ;
- $playerManagerMock
- ->expects($this->any())
- ->method('countByFactionAndStatements')
- ->willReturnCallback([$this, 'getFactionPlayersCountMock'])
- ;
- $playerManagerMock
- ->expects($this->any())
- ->method('getFactionPlayersByRanking')
- ->willReturnCallback([$this, 'getFactionPlayersByRankingMock'])
- ;
- $playerManagerMock
- ->expects($this->any())
- ->method('getParliamentMembers')
- ->willReturnCallback([$this, 'getParliamentMembersMock'])
- ;
- return $playerManagerMock;
- }
-
- public function getPlayerMock($id)
- {
- return
- (new Player())
- ->setId($id)
- ->setName('Pokahontas')
- ->setBind('8zg4zef6ef')
- ->setCredit(5000000)
- ->setAvatar('1-5.png')
- ->setLevel(5)
- ->setDefeat(10)
- ->setVictory(15)
- ->setStatement(Player::ACTIVE)
- ->setStatus(Player::PARLIAMENT)
- ;
- }
-
- public function getFactionPlayersByRankingMock($factionId)
- {
- return [
- (new Player())
- ->setId(156)
- ->setStatus(Player::PARLIAMENT)
- ->setFactionPoints(2500),
- (new Player())
- ->setId(2)
- ->setStatus(Player::PARLIAMENT)
- ->setFactionPoints(2450),
- (new Player())
- ->setId(25)
- ->setStatus(Player::STANDARD)
- ->setFactionPoints(2300),
- (new Player())
- ->setId(45)
- ->setStatus(Player::STANDARD)
- ->setFactionPoints(2220),
- (new Player())
- ->setId(5)
- ->setStatus(Player::PARLIAMENT)
- ->setFactionPoints(1987),
- (new Player())
- ->setId(62)
- ->setStatus(Player::PARLIAMENT)
- ->setFactionPoints(1658),
- (new Player())
- ->setId(70)
- ->setStatus(Player::PARLIAMENT)
- ->setFactionPoints(1559),
- (new Player())
- ->setId(8)
- ->setStatus(Player::PARLIAMENT)
- ->setFactionPoints(1550),
- (new Player())
- ->setId(92)
- ->setStatus(Player::STANDARD)
- ->setFactionPoints(1450),
- (new Player())
- ->setId(10)
- ->setStatus(Player::STANDARD)
- ->setFactionPoints(1400),
- (new Player())
- ->setId(11)
- ->setStatus(Player::PARLIAMENT)
- ->setFactionPoints(1398),
- (new Player())
- ->setId(12)
- ->setStatus(Player::PARLIAMENT)
- ->setFactionPoints(1364)
- ];
- }
-
- public function getParliamentMembersMock()
- {
- return [
- (new Player())->setId(1),
- (new Player())->setId(2),
- (new Player())->setId(3),
- (new Player())->setId(4),
- (new Player())->setId(5),
- (new Player())->setId(6),
- (new Player())->setId(7),
- (new Player())->setId(8),
- (new Player())->setId(9),
- (new Player())->setId(10),
- ];
- }
-
- /**
- * @param int $factionId
- * @param array $statements
- * @return int
- */
- public function getFactionPlayersCountMock($factionId, $statements)
- {
- return
- (count($statements) > 1)
- ? 18
- : 15
- ;
- }
-
- public function getVoteManagerMock()
- {
- $voteManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Demeter\Manager\Election\VoteManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $voteManagerMock;
- }
-
- public function getConversationManagerMock()
- {
- $conversationManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Hermes\Manager\ConversationManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $conversationManagerMock;
- }
-
- public function getCandidateManagerMock()
- {
- $candidateManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Demeter\Manager\Election\CandidateManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $candidateManagerMock;
- }
-
- public function getElectionManagerMock()
- {
- $electionManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Demeter\Manager\Election\ElectionManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $electionManagerMock;
- }
-
- public function getLawManagerMock()
- {
- $lawManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Demeter\Manager\Law\LawManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $lawManagerMock
- ->expects($this->any())
- ->method('getByFactionAndStatements')
- ->willReturnCallback([$this, 'getLawsMock'])
- ;
- $lawManagerMock
- ->expects($this->any())
- ->method('ballot')
- ->willReturn(true)
- ;
- return $lawManagerMock;
- }
-
- public function getLawsMock($factionId)
- {
- return [
- (new Law())
- ->setId(1)
- ->setFactionId($factionId)
- ->setStatement(Law::VOTATION)
- ->setType(Law::PEACEPACT)
- ->setOptions(['rColor' => 2])
- ->setCreatedAt((new \DateTime('-2 days'))->format('Y-m-d H:i:s'))
- ->setVotedAt((new \DateTime('-2 hours'))->format('Y-m-d H:i:s')),
- (new Law())
- ->setId(2)
- ->setFactionId($factionId)
- ->setStatement(Law::VOTATION)
- ->setType(Law::SECTORNAME)
- ->setOptions(['rSector' => 1])
- ->setCreatedAt((new \DateTime('-2 days'))->format('Y-m-d H:i:s'))
- ->setVotedAt((new \DateTime('+2 hours'))->format('Y-m-d H:i:s')),
- (new Law())
- ->setId(3)
- ->setFactionId($factionId)
- ->setStatement(Law::EFFECTIVE)
- ->setType(Law::TECHNOLOGYTRANSFER)
- ->setCreatedAt((new \DateTime('-2 days'))->format('Y-m-d H:i:s'))
- ->setVotedAt((new \DateTime('-3 hours'))->format('Y-m-d H:i:s'))
- ->setEndedAt((new \DateTime('+7 days'))->format('Y-m-d H:i:s'))
- ];
- }
-
- public function getNotificationManagerMock()
- {
- $notificationManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Hermes\Manager\NotificationManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $notificationManagerMock
- ->expects($this->any())
- ->method('add')
- ->willReturn(true)
- ;
- return $notificationManagerMock;
- }
-
- public function getConversationMessageManagerMock()
- {
- $conversationMessageManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Hermes\Manager\ConversationMessageManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $conversationMessageManagerMock;
- }
-
- public function getCommercialTaxManagerMock()
- {
- $commercialTaxManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Athena\Manager\CommercialTaxManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $commercialTaxManagerMock;
- }
-
- public function getSectorManagerMock()
- {
- $sectorManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Gaia\Manager\SectorManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $sectorManagerMock
- ->expects($this->any())
- ->method('get')
- ->willReturnCallback([$this, 'getSectorMock'])
- ;
- return $sectorManagerMock;
- }
-
- public function getSectorMock($id)
- {
- return
- (new Sector())
- ->setId($id)
- ->setName('Secteur ' . $id)
- ->setLifePlanet(500)
- ->setPoints(2)
- ->setPopulation(155000200)
- ->setRColor(1)
- ->setPrime(false)
- ->setTax(5)
-
- ;
- }
-
- public function getCommercialRouteManagerMock()
- {
- $commercialRouteManagerMock = $this
- ->getMockBuilder('Asylamba\Modules\Athena\Manager\CommercialRouteManager')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $commercialRouteManagerMock;
- }
-
- public function getParserMock()
- {
- $parserMock = $this
- ->getMockBuilder('Asylamba\Classes\Library\Parser')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- return $parserMock;
- }
-
- public function getCTCMock()
- {
- $ctcMock = $this
- ->getMockBuilder('Asylamba\Classes\Worker\CTC')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $ctcMock
- ->expects($this->any())
- ->method('createContext')
- ->willReturn(true)
- ;
- $ctcMock
- ->expects($this->any())
- ->method('add')
- ->willReturn(true)
- ;
- $ctcMock
- ->expects($this->any())
- ->method('applyContext')
- ->willReturn(true)
- ;
- return $ctcMock;
- }
-
- public function getSchedulerMock()
- {
- $schedulerMock = $this
- ->getMockBuilder('Asylamba\Classes\Scheduler\RealTimeActionScheduler')
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $schedulerMock
- ->expects($this->any())
- ->method('schedule')
- ->willReturn(true)
- ;
- return $schedulerMock;
- }
-}
diff --git a/tests/Modules/Demeter/Model/ColorTest.php b/tests/Modules/Demeter/Model/ColorTest.php
deleted file mode 100644
index bbdefa030..000000000
--- a/tests/Modules/Demeter/Model/ColorTest.php
+++ /dev/null
@@ -1,61 +0,0 @@
-setId(1)
- ->setAlive(TRUE)
- ->setisWinner(FALSE)
- ->setCredits(1000)
- //->setPlayers()
- //->setActivePlayers()
- ->setRankingPoints(820)
- ->setPoints(1200)
- //->setSectors()
- //->setElectionStatement()
- ->setIsClosed(TRUE)
- ->setDescription("Descrip")
- ->setDClaimVictory(TRUE)
- ->setDLastElection('2017-05-06 21:20:30')
- ->setIsInGame(TRUE)
- ;
-
- $this->assertEquals(1, $color->getId());
- $this->assertEquals(TRUE, $color->getAlive());
- $this->assertEquals(FALSE, $color->getIsWinner());
- $this->assertEquals(1000, $color->getCredits());
- //$this->assertEquals(, $color->getPlayers());
- //$this->assertEquals(, $color->getActivePlayers());
- $this->assertEquals(820, $color->getRankingPoints());
- $this->assertEquals(1200, $color->getPoints());
- //$this->assertEquals(, $color->getSectors());
- //$this->assertEquals(, $color->getElectionStatement());
- $this->assertEquals(TRUE, $color->getIsClosed());
- $this->assertEquals("Descrip", $color->getDescription());
- $this->assertEquals(TRUE, $color->getDClaimVictory());
- $this->assertEquals('2017-05-06 21:20:30', $color->getDLastElection());
- $this->assertEquals(TRUE, $color->getIsInGame());
- }
-
- public function testIncreaseCredit()
- {
- $color = new color();
- $color->setCredits(1000);
- $this->assertEquals(2324, $color->increaseCredit(1324));
- }
-
- public function testDecreaseCredit()
- {
- $color = new color();
- $color->setCredits(1000);
- $this->assertEquals(505, $color->decreaseCredit(495));
- }
-}
diff --git a/tests/Modules/Gaia/Application/Handler/GetDistanceBetweenPlacesTest.php b/tests/Modules/Gaia/Application/Handler/GetDistanceBetweenPlacesTest.php
new file mode 100644
index 000000000..609415303
--- /dev/null
+++ b/tests/Modules/Gaia/Application/Handler/GetDistanceBetweenPlacesTest.php
@@ -0,0 +1,68 @@
+ SystemFactory::createOne([
+ 'sector' => SectorFactory::createOne([])->object(),
+ 'xPosition' => $xA,
+ 'yPosition' => $yA,
+ ])->object(),
+ ])->object();
+
+ $to = PlaceFactory::createOne([
+ 'system' => SystemFactory::createOne([
+ 'sector' => SectorFactory::createOne([])->object(),
+ 'xPosition' => $xB,
+ 'yPosition' => $yB,
+ ])->object(),
+ ])->object();
+
+ static::assertSame($expectedDistance, $getDistanceBetweenPlaces($from, $to));
+ }
+
+ public static function provideData(): Generator
+ {
+ yield [
+ 20,
+ 25,
+ 40,
+ 60,
+ 40.0,
+ ];
+
+ yield [
+ 13,
+ 17,
+ 13,
+ 14,
+ 3.0,
+ ];
+
+ yield [
+ 26,
+ 17,
+ 13,
+ 16,
+ 13.0,
+ ];
+ }
+}
diff --git a/tests/Modules/Gaia/GaiaModuleTest.php b/tests/Modules/Gaia/GaiaModuleTest.php
deleted file mode 100644
index 424746f7c..000000000
--- a/tests/Modules/Gaia/GaiaModuleTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-module = new GaiaModule($this->getApplicationMock());
- }
-
- public function testGetName()
- {
- $this->assertEquals('Gaia', $this->module->getName());
- }
-
- public function getApplicationMock()
- {
- $applicationMock = $this
- ->getMockBuilder(Application::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $applicationMock
- ->expects($this->any())
- ->method('getContainer')
- ->willReturnCallback([$this, 'getContainerMock'])
- ;
- return $applicationMock;
- }
-
- public function getContainerMock()
- {
- $containerMock = $this
- ->getMockBuilder(Container::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $containerMock
- ->expects($this->any())
- ->method('getParameter')
- ->willReturn(realpath('.'))
- ;
- return $containerMock;
- }
-}
diff --git a/tests/Modules/Gaia/Handler/PlaceUpdateHandlerTest.php b/tests/Modules/Gaia/Handler/PlaceUpdateHandlerTest.php
new file mode 100644
index 000000000..9f6841d17
--- /dev/null
+++ b/tests/Modules/Gaia/Handler/PlaceUpdateHandlerTest.php
@@ -0,0 +1,107 @@
+ $placeData
+ */
+ #[DataProvider('provideData')]
+ public function test(string $currentDate, string $timeMode, array $placeData, int $expectedResources, int $expectedDanger): void
+ {
+ $_ENV['SERVER_TIME_MODE'] = $timeMode;
+ Clock::set(new MockClock($currentDate));
+ static::bootKernel();
+ /** @var PlaceUpdateHandler $handler */
+ $updatePlace = static::getContainer()->get(PlaceUpdateHandler::class);
+
+ $sector = SectorFactory::createOne();
+ $system = SystemFactory::createOne(['sector' => $sector->object()]);
+ $place = PlaceFactory::createOne([
+ 'system' => $system->object(),
+ 'updatedAt' => new \DateTimeImmutable($placeData['updated_at']),
+ 'resources' => $placeData['resources'],
+ 'population' => $placeData['population'],
+ 'danger' => $placeData['danger'],
+ 'maxDanger' => $placeData['max_danger'],
+ 'coefResources' => $placeData['resources_coeff'],
+ ])->object();
+
+ $updatePlace(new PlaceUpdateMessage($place->id));
+
+ static::assertSame($expectedResources, $place->resources);
+ static::assertSame($expectedDanger, $place->danger);
+ }
+
+ /**
+ * @return Generator,
+ * 3: int,
+ * 4: int
+ * }>
+ */
+ public static function provideData(): Generator
+ {
+ yield 'Update once' => [
+ '2024-05-01 10:10:00',
+ 'fast',
+ [
+ 'resources' => 1890,
+ 'resources_coeff' => 29,
+ 'population' => 105.0,
+ 'danger' => 5,
+ 'max_danger' => 7,
+ 'updated_at' => '2024-05-01 10:00:00',
+ ],
+ 2100,
+ 7,
+ ];
+
+ yield 'Update once and reach place max resources' => [
+ '2024-05-01 10:10:00',
+ 'fast',
+ [
+ 'resources' => 14300,
+ 'resources_coeff' => 60,
+ 'population' => 105.0,
+ 'danger' => 4,
+ 'max_danger' => 7,
+ 'updated_at' => '2024-05-01 10:00:00',
+ ],
+ 14400,
+ 6,
+ ];
+
+ yield 'Multiple updates and reach max danger' => [
+ '2024-05-01 10:32:00',
+ 'fast',
+ [
+ 'resources' => 1890,
+ 'resources_coeff' => 29,
+ 'population' => 105.0,
+ 'danger' => 5,
+ 'max_danger' => 7,
+ 'updated_at' => '2024-05-01 10:00:00',
+ ],
+ 2520,
+ 7,
+ ];
+ }
+}
diff --git a/tests/Modules/Hermes/HermesModuleTest.php b/tests/Modules/Hermes/HermesModuleTest.php
deleted file mode 100644
index a68928ebd..000000000
--- a/tests/Modules/Hermes/HermesModuleTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-module = new HermesModule($this->getApplicationMock());
- }
-
- public function testGetName()
- {
- $this->assertEquals('Hermes', $this->module->getName());
- }
-
- public function getApplicationMock()
- {
- $applicationMock = $this
- ->getMockBuilder(Application::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $applicationMock
- ->expects($this->any())
- ->method('getContainer')
- ->willReturnCallback([$this, 'getContainerMock'])
- ;
- return $applicationMock;
- }
-
- public function getContainerMock()
- {
- $containerMock = $this
- ->getMockBuilder(Container::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $containerMock
- ->expects($this->any())
- ->method('getParameter')
- ->willReturn(realpath('.'))
- ;
- return $containerMock;
- }
-}
diff --git a/tests/Modules/Promethee/Helper/ResearchHelperTest.php b/tests/Modules/Promethee/Helper/ResearchHelperTest.php
new file mode 100644
index 000000000..418e0a9a6
--- /dev/null
+++ b/tests/Modules/Promethee/Helper/ResearchHelperTest.php
@@ -0,0 +1,39 @@
+getInfo($research, 'level', $level, 'price'));
+ }
+
+ public static function provideData(): \Generator
+ {
+ yield [Research::MATH, 1, 1, 100];
+ yield [Research::PHYS, 1, 1, 3000];
+ yield [Research::PHYS, 1, 0.5, 1500];
+ yield [Research::CHEM, 1, 1, 7000];
+ yield [Research::LAW, 1, 1, 200];
+ yield [Research::LAW, 1, 2, 400];
+ yield [Research::COMM, 1, 1, 9000];
+ yield [Research::ECONO, 1, 1, 200];
+ yield [Research::PSYCHO, 1, 1, 9000];
+ yield [Research::NETWORK, 1, 1, 200];
+ yield [Research::ALGO, 1, 1, 4000];
+ yield [Research::STAT, 1, 1, 6000];
+ yield [Research::CHEM, 2, 1, 12582];
+ yield [Research::STAT, 2, 1, 12582];
+ yield [Research::ALGO, 10, 1, 142514];
+ }
+}
diff --git a/tests/Modules/Promethee/PrometheeModuleTest.php b/tests/Modules/Promethee/PrometheeModuleTest.php
deleted file mode 100644
index b171c5cc7..000000000
--- a/tests/Modules/Promethee/PrometheeModuleTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-module = new PrometheeModule($this->getApplicationMock());
- }
-
- public function testGetName()
- {
- $this->assertEquals('Promethee', $this->module->getName());
- }
-
- public function getApplicationMock()
- {
- $applicationMock = $this
- ->getMockBuilder(Application::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $applicationMock
- ->expects($this->any())
- ->method('getContainer')
- ->willReturnCallback([$this, 'getContainerMock'])
- ;
- return $applicationMock;
- }
-
- public function getContainerMock()
- {
- $containerMock = $this
- ->getMockBuilder(Container::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $containerMock
- ->expects($this->any())
- ->method('getParameter')
- ->willReturn(realpath('.'))
- ;
- return $containerMock;
- }
-}
diff --git a/tests/Modules/Shared/Application/PercentageApplierTest.php b/tests/Modules/Shared/Application/PercentageApplierTest.php
new file mode 100644
index 000000000..fd5b80d65
--- /dev/null
+++ b/tests/Modules/Shared/Application/PercentageApplierTest.php
@@ -0,0 +1,91 @@
+get(CountMissingSystemUpdates::class);
+
+ FactionFactory::createOne();
+ $player = PlayerFactory::createOne([
+ 'uPlayer' => new \DateTimeImmutable($lastUpdatedAt),
+ ])->object();
+
+ static::assertSame($expectedMissingUpdatesCount, $countMissingSystemUpdates($player));
+ }
+
+ public static function provideData(): Generator
+ {
+ yield [
+ '2024-05-01 10:00:00',
+ '2024-05-01 09:50:10',
+ 'fast',
+ 1,
+ ];
+
+ yield [
+ '2024-05-01 09:53:00',
+ '2024-05-01 09:50:10',
+ 'fast',
+ 0,
+ ];
+
+ yield [
+ '2024-05-01 10:00:00',
+ '2024-05-01 09:40:10',
+ 'fast',
+ 2,
+ ];
+
+ yield [
+ '2024-05-01 10:01:00',
+ '2024-05-01 09:40:10',
+ 'fast',
+ 2,
+ ];
+ }
+}
diff --git a/tests/Modules/Shared/Domain/Service/GameTimeConverterTest.php b/tests/Modules/Shared/Domain/Service/GameTimeConverterTest.php
new file mode 100644
index 000000000..091cda5e0
--- /dev/null
+++ b/tests/Modules/Shared/Domain/Service/GameTimeConverterTest.php
@@ -0,0 +1,109 @@
+value;
+ static::bootKernel();
+ /** @var GameTimeConverter $gameTimeConverter */
+ $gameTimeConverter = static::getContainer()->get(GameTimeConverter::class);
+
+ static::assertSame($gameCycles, $gameTimeConverter->convertSecondsToGameCycles($seconds));
+ }
+
+ #[DataProvider('provideGameCyclesConversionData')]
+ public function testConvertGameCyclesToSeconds(int $seconds, int $gameCycles, TimeMode $timeMode): void
+ {
+ $_ENV['SERVER_TIME_MODE'] = $timeMode->value;
+ static::bootKernel();
+ /** @var GameTimeConverter $gameTimeConverter */
+ $gameTimeConverter = static::getContainer()->get(GameTimeConverter::class);
+
+ static::assertSame($seconds, $gameTimeConverter->convertGameCyclesToSeconds($gameCycles));
+ }
+
+ #[DataProvider('provideGameDateConversionData')]
+ public function testConvertDatetimeToGameDate(TimeMode $timeMode, string $serverStartDate, int $serverSegmentShift, string $currentDate, string $expectedGameDate): void
+ {
+ $_ENV['SERVER_TIME_MODE'] = $timeMode->value;
+ $_ENV['SERVER_START_TIME'] = (new \DateTimeImmutable($serverStartDate))->format('Y-m-d H:i:s');
+ $_ENV['SERVER_SEGMENT_SHIFT'] = $serverSegmentShift;
+ static::bootKernel();
+ /** @var GameTimeConverter $gameTimeConverter */
+ $gameTimeConverter = static::getContainer()->get(GameTimeConverter::class);
+
+ $gameDate = $gameTimeConverter->convertDatetimeToGameDate(new \DateTimeImmutable($currentDate), false);
+
+ static::assertSame($expectedGameDate, $gameDate);
+ }
+
+ /**
+ * @return \Generator
+ */
+ public static function provideGameCyclesConversionData(): \Generator
+ {
+ yield [
+ 3600,
+ 1,
+ TimeMode::Standard,
+ ];
+
+ yield [
+ 600,
+ 1,
+ TimeMode::Fast,
+ ];
+
+ yield [
+ 3600,
+ 6,
+ TimeMode::Fast,
+ ];
+
+ yield [
+ 7200,
+ 2,
+ TimeMode::Standard,
+ ];
+ }
+
+ public static function provideGameDateConversionData(): \Generator
+ {
+ yield [
+ TimeMode::Standard,
+ '-2 days',
+ 100,
+ '+4 hours',
+ 'SEG102 REL4',
+ ];
+
+ yield [
+ TimeMode::Standard,
+ '-2 days',
+ 50,
+ '+36 hours',
+ 'SEG53 REL12',
+ ];
+
+ yield [
+ TimeMode::Fast,
+ '-1 days',
+ 50,
+ '+1 hour',
+ 'SEG56 REL6',
+ ];
+ }
+}
diff --git a/tests/Modules/Zeus/Application/Handler/CommanderWageHandlerTest.php b/tests/Modules/Zeus/Application/Handler/CommanderWageHandlerTest.php
new file mode 100644
index 000000000..16881efa7
--- /dev/null
+++ b/tests/Modules/Zeus/Application/Handler/CommanderWageHandlerTest.php
@@ -0,0 +1,18 @@
+get(CommanderWageHandler::class);
+
+ static::markTestIncomplete('Not implemented');
+ }
+}
diff --git a/tests/Modules/Zeus/Application/Handler/ShipsWageHandlerTest.php b/tests/Modules/Zeus/Application/Handler/ShipsWageHandlerTest.php
new file mode 100644
index 000000000..2ddae0d2f
--- /dev/null
+++ b/tests/Modules/Zeus/Application/Handler/ShipsWageHandlerTest.php
@@ -0,0 +1,18 @@
+get(ShipsWageHandler::class);
+
+ static::markTestIncomplete('Not implemented');
+ }
+}
diff --git a/tests/Modules/Zeus/Application/Handler/UniversityInvestmentHandlerTest.php b/tests/Modules/Zeus/Application/Handler/UniversityInvestmentHandlerTest.php
new file mode 100644
index 000000000..3b7fe9257
--- /dev/null
+++ b/tests/Modules/Zeus/Application/Handler/UniversityInvestmentHandlerTest.php
@@ -0,0 +1,18 @@
+get(UniversityInvestmentHandler::class);
+
+ static::markTestIncomplete('Not implemented');
+ }
+}
diff --git a/tests/Modules/Zeus/Infrastructure/GetAlivePlayersTest.php b/tests/Modules/Zeus/Infrastructure/GetAlivePlayersTest.php
new file mode 100644
index 000000000..9dfdc4c9f
--- /dev/null
+++ b/tests/Modules/Zeus/Infrastructure/GetAlivePlayersTest.php
@@ -0,0 +1,37 @@
+get(PlayerRepositoryInterface::class);
+
+ PlayerFactory::createMany(5, ['statement' => Player::ACTIVE]);
+ PlayerFactory::createMany(3, ['statement' => Player::DEAD]);
+
+ $alivePlayers = $playerRepository->getBySpecification(new IsPlayerAlive());
+
+ static::assertCount(5, $alivePlayers);
+
+ foreach ($alivePlayers as $player) {
+ static::assertNotSame(Player::DEAD, $player->statement);
+ }
+ }
+}
diff --git a/tests/Modules/Zeus/ZeusModuleTest.php b/tests/Modules/Zeus/ZeusModuleTest.php
deleted file mode 100644
index c79106c96..000000000
--- a/tests/Modules/Zeus/ZeusModuleTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-module = new ZeusModule($this->getApplicationMock());
- }
-
- public function testGetName()
- {
- $this->assertEquals('Zeus', $this->module->getName());
- }
-
- public function getApplicationMock()
- {
- $applicationMock = $this
- ->getMockBuilder(Application::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $applicationMock
- ->expects($this->any())
- ->method('getContainer')
- ->willReturnCallback([$this, 'getContainerMock'])
- ;
- return $applicationMock;
- }
-
- public function getContainerMock()
- {
- $containerMock = $this
- ->getMockBuilder(Container::class)
- ->disableOriginalConstructor()
- ->getMock()
- ;
- $containerMock
- ->expects($this->any())
- ->method('getParameter')
- ->willReturn(realpath('.'))
- ;
- return $containerMock;
- }
-}
diff --git a/tests/Shared/Handler/DurationHandlerTest.php b/tests/Shared/Handler/DurationHandlerTest.php
new file mode 100644
index 000000000..80a39ea58
--- /dev/null
+++ b/tests/Shared/Handler/DurationHandlerTest.php
@@ -0,0 +1,167 @@
+durationHandler = new DurationHandler();
+ }
+
+ /**
+ * @dataProvider provideGetHoursDiffData
+ */
+ public function testGetHoursDiff(\DateTimeImmutable $from, \DateTimeImmutable $to, int $expectedHours): void
+ {
+ static::assertSame(
+ $expectedHours,
+ $this->durationHandler->getHoursDiff($from, $to),
+ );
+ }
+
+ /**
+ * @dataProvider provideGetRemainingTimeData
+ */
+ public function testGetRemainingTime(DurationInterface $duration, int $expectedTime): void
+ {
+ static::assertSame(
+ round($expectedTime / 100),
+ round($this->durationHandler->getDurationRemainingTime($duration) / 100),
+ );
+ }
+
+ /**
+ * @dataProvider provideGetDurationEndData
+ */
+ public function testGetDurationEnd(\DateTimeImmutable $startedAt, int $seconds, \DateTimeInterface $expected): void
+ {
+ static::assertSame(
+ $expected->getTimestamp(),
+ $this->durationHandler->getDurationEnd($startedAt, $seconds)->getTimestamp(),
+ );
+ }
+
+ /**
+ * @return Generator>
+ */
+ public static function provideGetDurationEndData(): Generator
+ {
+ yield [
+ new \DateTimeImmutable(),
+ 3600,
+ new \DateTimeImmutable('+3600 seconds'),
+ ];
+
+ yield [
+ new \DateTimeImmutable(),
+ 10,
+ new \DateTimeImmutable('+10 seconds'),
+ ];
+
+ yield [
+ new \DateTimeImmutable(),
+ 600,
+ new \DateTimeImmutable('+10 minutes'),
+ ];
+ }
+
+ /**
+ * @return Generator>
+ */
+ public static function provideGetRemainingTimeData(): Generator
+ {
+ yield [
+ static::generateBuildingQueue(new \DateTimeImmutable('+1 hour')),
+ 3600,
+ ];
+ yield [
+ static::generateBuildingQueue(new \DateTimeImmutable('+1 day')),
+ 3600 * 24,
+ ];
+ yield [
+ static::generateBuildingQueue(new \DateTimeImmutable('-1 hour')),
+ 0,
+ ];
+ }
+
+ /**
+ * @return Generator>
+ */
+ public static function provideGetHoursDiffData(): Generator
+ {
+ yield [
+ new \DateTimeImmutable('+2 hours'),
+ new \DateTimeImmutable('+4 hours'),
+ 2,
+ ];
+
+ yield [
+ new \DateTimeImmutable('2022-01-05 10:00:00'),
+ new \DateTimeImmutable('2022-01-07 14:00:00'),
+ 52,
+ ];
+
+ yield [
+ new \DateTimeImmutable('2022-01-07 14:00:00'),
+ new \DateTimeImmutable('2022-01-05 10:00:00'),
+ 52,
+ ];
+ }
+
+ private static function generateBuildingQueue(\DateTimeImmutable $endedAt): BuildingQueue
+ {
+ return new BuildingQueue(
+ id: Uuid::v4(),
+ base: new OrbitalBase(
+ id: Uuid::v4(),
+ place: new Place(
+ id: Uuid::v4(),
+ player: new Player(),
+ base: null,
+ system: new System(
+ id: Uuid::v4(),
+ sector: null,
+ faction: null,
+ xPosition: 10,
+ yPosition: 20,
+ typeOfSystem: 0,
+ ),
+ typeOfPlace: Place::TERRESTRIAL,
+ position: 1,
+ population: 100,
+ coefResources: 60,
+ coefHistory: 20,
+ resources: 20000,
+ danger: 40,
+ maxDanger: 60,
+ updatedAt: new \DateTimeImmutable(),
+ ),
+ player: new Player(),
+ name: 'My wonderful base',
+ levelGenerator: 3,
+ levelRefinery: 2,
+ levelCommercialPlateforme: 0,
+ levelStorage: 5,
+ ),
+ buildingNumber: OrbitalBaseResource::SPATIOPORT,
+ targetLevel: 10,
+ startedAt: new \DateTimeImmutable(),
+ endedAt: $endedAt,
+ );
+ }
+}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 000000000..725ac047c
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,15 @@
+bootEnv(dirname(__DIR__).'/.env');
+}
+
+if ($_SERVER['APP_DEBUG']) {
+ umask(0000);
+}
diff --git a/worker.php b/worker.php
deleted file mode 100644
index a18150621..000000000
--- a/worker.php
+++ /dev/null
@@ -1,30 +0,0 @@
-usePutenv(false);
-$dotenv->load($projectDir.'/.env');
-
-$worker = new WorkerKernel($options['process'] ?? 'worker', $projectDir);
-$worker->boot();
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 000000000..aec02b206
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,861 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.0.0":
+ version "7.26.2"
+ resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz"
+ integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.25.9"
+ js-tokens "^4.0.0"
+ picocolors "^1.0.0"
+
+"@babel/helper-validator-identifier@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz"
+ integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
+
+"@hotwired/stimulus-webpack-helpers@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/@hotwired/stimulus-webpack-helpers/-/stimulus-webpack-helpers-1.0.1.tgz"
+ integrity sha512-wa/zupVG0eWxRYJjC1IiPBdt3Lruv0RqGN+/DTMmUWUyMAEB27KXmVY6a8YpUVTM7QwVuaLNGW4EqDgrS2upXQ==
+
+"@hotwired/stimulus@^3.0", "@hotwired/stimulus@^3.0.0", "@hotwired/stimulus@>= 3.0":
+ version "3.2.2"
+ resolved "https://registry.npmjs.org/@hotwired/stimulus/-/stimulus-3.2.2.tgz"
+ integrity sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A==
+
+"@hotwired/turbo@^7.1.0 || ^8.0", "@hotwired/turbo@^8.0.13":
+ version "8.0.13"
+ resolved "https://registry.npmjs.org/@hotwired/turbo/-/turbo-8.0.13.tgz"
+ integrity sha512-M7qXUqcGab6G5PKOiwhgbByTtrPgKPFCTMNQ52QhzUEXEqmp0/ApEguUesh/FPiUjrmFec+3lq98KsWnYY2C7g==
+
+"@jridgewell/gen-mapping@^0.3.5":
+ version "0.3.8"
+ resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz"
+ integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==
+ dependencies:
+ "@jridgewell/set-array" "^1.2.1"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
+"@jridgewell/resolve-uri@^3.1.0":
+ version "3.1.2"
+ resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz"
+ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
+"@jridgewell/set-array@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz"
+ integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
+
+"@jridgewell/source-map@^0.3.3":
+ version "0.3.6"
+ resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz"
+ integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.25"
+
+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
+ version "1.5.0"
+ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
+ integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
+
+"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+ version "0.3.25"
+ resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz"
+ integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.1.0"
+ "@jridgewell/sourcemap-codec" "^1.4.14"
+
+"@kurkle/color@^0.3.0":
+ version "0.3.4"
+ resolved "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz"
+ integrity sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==
+
+"@symfony/stimulus-bridge@^3.2.0":
+ version "3.2.3"
+ resolved "https://registry.npmjs.org/@symfony/stimulus-bridge/-/stimulus-bridge-3.2.3.tgz"
+ integrity sha512-36rQTihQ2MGOn8EmdOYCr3DQfP3WS1CNcUUXKTPY5ghtFOeb7OVuhbc32AjRowE2/vaVDOUCPOTv3VLf5VtXBA==
+ dependencies:
+ "@hotwired/stimulus-webpack-helpers" "^1.0.1"
+ "@types/webpack-env" "^1.16.4"
+ acorn "^8.0.5"
+ loader-utils "^2.0.0"
+ schema-utils "^3.0.0"
+
+"@symfony/ux-turbo@file:vendor/symfony/ux-turbo/assets":
+ version "0.1.0"
+ resolved "file:vendor/symfony/ux-turbo/assets"
+
+"@tailwindcss/forms@^0.5":
+ version "0.5.1"
+ resolved "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.1.tgz"
+ integrity sha512-QSwsFORnC2BAP0lRzQkz1pw+EzIiiPdk4e27vGQjyXkwJPeC7iLIRVndJzf9CJVbcrrIcirb/TfxF3gRTyFEVA==
+ dependencies:
+ mini-svg-data-uri "^1.2.3"
+
+"@types/eslint-scope@^3.7.7":
+ version "3.7.7"
+ resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz"
+ integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==
+ dependencies:
+ "@types/eslint" "*"
+ "@types/estree" "*"
+
+"@types/eslint@*":
+ version "9.6.1"
+ resolved "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz"
+ integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==
+ dependencies:
+ "@types/estree" "*"
+ "@types/json-schema" "*"
+
+"@types/estree@*", "@types/estree@^1.0.6":
+ version "1.0.6"
+ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz"
+ integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
+
+"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+ version "7.0.11"
+ resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz"
+ integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+
+"@types/node@*":
+ version "17.0.31"
+ resolved "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz"
+ integrity sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==
+
+"@types/webpack-env@^1.16.4":
+ version "1.18.5"
+ resolved "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.18.5.tgz"
+ integrity sha512-wz7kjjRRj8/Lty4B+Kr0LN6Ypc/3SymeCCGSbaXp2leH0ZVg/PriNiOwNj4bD4uphI7A8NXS4b6Gl373sfO5mA==
+
+"@webassemblyjs/ast@^1.14.1", "@webassemblyjs/ast@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz"
+ integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==
+ dependencies:
+ "@webassemblyjs/helper-numbers" "1.13.2"
+ "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+
+"@webassemblyjs/floating-point-hex-parser@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz"
+ integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==
+
+"@webassemblyjs/helper-api-error@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz"
+ integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==
+
+"@webassemblyjs/helper-buffer@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz"
+ integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==
+
+"@webassemblyjs/helper-numbers@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz"
+ integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==
+ dependencies:
+ "@webassemblyjs/floating-point-hex-parser" "1.13.2"
+ "@webassemblyjs/helper-api-error" "1.13.2"
+ "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/helper-wasm-bytecode@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz"
+ integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==
+
+"@webassemblyjs/helper-wasm-section@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz"
+ integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@webassemblyjs/helper-buffer" "1.14.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+ "@webassemblyjs/wasm-gen" "1.14.1"
+
+"@webassemblyjs/ieee754@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz"
+ integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==
+ dependencies:
+ "@xtuc/ieee754" "^1.2.0"
+
+"@webassemblyjs/leb128@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz"
+ integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==
+ dependencies:
+ "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/utf8@1.13.2":
+ version "1.13.2"
+ resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz"
+ integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==
+
+"@webassemblyjs/wasm-edit@^1.14.1":
+ version "1.14.1"
+ resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz"
+ integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@webassemblyjs/helper-buffer" "1.14.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+ "@webassemblyjs/helper-wasm-section" "1.14.1"
+ "@webassemblyjs/wasm-gen" "1.14.1"
+ "@webassemblyjs/wasm-opt" "1.14.1"
+ "@webassemblyjs/wasm-parser" "1.14.1"
+ "@webassemblyjs/wast-printer" "1.14.1"
+
+"@webassemblyjs/wasm-gen@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz"
+ integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+ "@webassemblyjs/ieee754" "1.13.2"
+ "@webassemblyjs/leb128" "1.13.2"
+ "@webassemblyjs/utf8" "1.13.2"
+
+"@webassemblyjs/wasm-opt@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz"
+ integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@webassemblyjs/helper-buffer" "1.14.1"
+ "@webassemblyjs/wasm-gen" "1.14.1"
+ "@webassemblyjs/wasm-parser" "1.14.1"
+
+"@webassemblyjs/wasm-parser@^1.14.1", "@webassemblyjs/wasm-parser@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz"
+ integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@webassemblyjs/helper-api-error" "1.13.2"
+ "@webassemblyjs/helper-wasm-bytecode" "1.13.2"
+ "@webassemblyjs/ieee754" "1.13.2"
+ "@webassemblyjs/leb128" "1.13.2"
+ "@webassemblyjs/utf8" "1.13.2"
+
+"@webassemblyjs/wast-printer@1.14.1":
+ version "1.14.1"
+ resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz"
+ integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==
+ dependencies:
+ "@webassemblyjs/ast" "1.14.1"
+ "@xtuc/long" "4.2.2"
+
+"@xtuc/ieee754@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz"
+ integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
+
+"@xtuc/long@4.2.2":
+ version "4.2.2"
+ resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz"
+ integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+
+acorn@^8.0.5, acorn@^8.14.0, acorn@^8.8.2:
+ version "8.14.0"
+ resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz"
+ integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
+
+ajv-formats@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz"
+ integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
+ dependencies:
+ ajv "^8.0.0"
+
+ajv-keywords@^3.5.2:
+ version "3.5.2"
+ resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz"
+ integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
+
+ajv-keywords@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz"
+ integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==
+ dependencies:
+ fast-deep-equal "^3.1.3"
+
+ajv@^6.12.5, ajv@^6.9.1:
+ version "6.12.6"
+ resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ajv@^8.0.0:
+ version "8.11.0"
+ resolved "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz"
+ integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ json-schema-traverse "^1.0.0"
+ require-from-string "^2.0.2"
+ uri-js "^4.2.2"
+
+ajv@^8.8.2, ajv@^8.9.0:
+ version "8.17.1"
+ resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz"
+ integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
+ dependencies:
+ fast-deep-equal "^3.1.3"
+ fast-uri "^3.0.1"
+ json-schema-traverse "^1.0.0"
+ require-from-string "^2.0.2"
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+autoprefixer@^10.4:
+ version "10.4.7"
+ resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz"
+ integrity sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==
+ dependencies:
+ browserslist "^4.20.3"
+ caniuse-lite "^1.0.30001335"
+ fraction.js "^4.2.0"
+ normalize-range "^0.1.2"
+ picocolors "^1.0.0"
+ postcss-value-parser "^4.2.0"
+
+big.js@^5.2.2:
+ version "5.2.2"
+ resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz"
+ integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
+
+browserslist@^4.20.3, browserslist@^4.24.0, "browserslist@>= 4.21.0":
+ version "4.24.4"
+ resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz"
+ integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==
+ dependencies:
+ caniuse-lite "^1.0.30001688"
+ electron-to-chromium "^1.5.73"
+ node-releases "^2.0.19"
+ update-browserslist-db "^1.1.1"
+
+buffer-from@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
+ integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+caniuse-lite@^1.0.30001335, caniuse-lite@^1.0.30001688:
+ version "1.0.30001695"
+ resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz"
+ integrity sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==
+
+chart.js@^4.4.3:
+ version "4.4.7"
+ resolved "https://registry.npmjs.org/chart.js/-/chart.js-4.4.7.tgz"
+ integrity sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==
+ dependencies:
+ "@kurkle/color" "^0.3.0"
+
+chrome-trace-event@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz"
+ integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
+
+commander@^2.20.0:
+ version "2.20.3"
+ resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+core-js@^3.37:
+ version "3.40.0"
+ resolved "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz"
+ integrity sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==
+
+cosmiconfig@^9.0.0:
+ version "9.0.0"
+ resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz"
+ integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==
+ dependencies:
+ env-paths "^2.2.1"
+ import-fresh "^3.3.0"
+ js-yaml "^4.1.0"
+ parse-json "^5.2.0"
+
+electron-to-chromium@^1.5.73:
+ version "1.5.85"
+ resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.85.tgz"
+ integrity sha512-UgTI7ZHxtSjOUwV0vZLpqT604U1Z8L3bq8mAtAKtuRPlMZ/6dLFMYgYnLdXSi/urbVTP2ykDb9EDDUrdIzw4Qg==
+
+emojis-list@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz"
+ integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
+
+enhanced-resolve@^5.17.1:
+ version "5.18.0"
+ resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz"
+ integrity sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==
+ dependencies:
+ graceful-fs "^4.2.4"
+ tapable "^2.2.0"
+
+env-paths@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz"
+ integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
+
+error-ex@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz"
+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+ dependencies:
+ is-arrayish "^0.2.1"
+
+es-module-lexer@^1.2.1:
+ version "1.6.0"
+ resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz"
+ integrity sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==
+
+escalade@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz"
+ integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
+
+eslint-scope@5.1.1:
+ version "5.1.1"
+ resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz"
+ integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^4.1.1"
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^4.1.1:
+ version "4.3.0"
+ resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz"
+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+estraverse@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+events@^3.2.0:
+ version "3.3.0"
+ resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
+ integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-uri@^3.0.1:
+ version "3.0.6"
+ resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz"
+ integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==
+
+file-loader@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz"
+ integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
+ dependencies:
+ loader-utils "^2.0.0"
+ schema-utils "^3.0.0"
+
+fraction.js@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz"
+ integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==
+
+glob-to-regexp@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz"
+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
+graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4:
+ version "4.2.11"
+ resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
+ integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+import-fresh@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz"
+ integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
+jest-worker@^27.4.5:
+ version "27.5.1"
+ resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz"
+ integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==
+ dependencies:
+ "@types/node" "*"
+ merge-stream "^2.0.0"
+ supports-color "^8.0.0"
+
+jiti@^1.20.0:
+ version "1.21.7"
+ resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz"
+ integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==
+
+js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
+json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz"
+ integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema-traverse@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz"
+ integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
+json5@^2.1.2:
+ version "2.2.3"
+ resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz"
+ integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
+lines-and-columns@^1.1.6:
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
+ integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
+loader-runner@^4.2.0:
+ version "4.3.0"
+ resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz"
+ integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
+
+loader-utils@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz"
+ integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
+ dependencies:
+ big.js "^5.2.2"
+ emojis-list "^3.0.0"
+ json5 "^2.1.2"
+
+merge-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz"
+ integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+mime-db@1.52.0:
+ version "1.52.0"
+ resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.27:
+ version "2.1.35"
+ resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+mini-svg-data-uri@^1.2.3:
+ version "1.4.4"
+ resolved "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz"
+ integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==
+
+nanoid@^3.3.8:
+ version "3.3.8"
+ resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz"
+ integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==
+
+neo-async@^2.6.2:
+ version "2.6.2"
+ resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz"
+ integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+
+node-releases@^2.0.19:
+ version "2.0.19"
+ resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz"
+ integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==
+
+normalize-range@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz"
+ integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
+
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
+parse-json@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz"
+ integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ error-ex "^1.3.1"
+ json-parse-even-better-errors "^2.3.0"
+ lines-and-columns "^1.1.6"
+
+picocolors@^1.0.0, picocolors@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz"
+ integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
+postcss-loader@^8.1:
+ version "8.1.1"
+ resolved "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz"
+ integrity sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==
+ dependencies:
+ cosmiconfig "^9.0.0"
+ jiti "^1.20.0"
+ semver "^7.5.4"
+
+postcss-value-parser@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
+ integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+"postcss@^7.0.0 || ^8.0.1", postcss@^8.1.0, postcss@^8.4:
+ version "8.5.1"
+ resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz"
+ integrity sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==
+ dependencies:
+ nanoid "^3.3.8"
+ picocolors "^1.1.1"
+ source-map-js "^1.2.1"
+
+punycode@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+randombytes@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz"
+ integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+ dependencies:
+ safe-buffer "^5.1.0"
+
+raw-loader@^4.0:
+ version "4.0.2"
+ resolved "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz"
+ integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==
+ dependencies:
+ loader-utils "^2.0.0"
+ schema-utils "^3.0.0"
+
+regenerator-runtime@^0.14:
+ version "0.14.1"
+ resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz"
+ integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
+
+require-from-string@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz"
+ integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+safe-buffer@^5.1.0:
+ version "5.1.2"
+ resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+schema-utils@^3.0.0, schema-utils@^3.2.0:
+ version "3.3.0"
+ resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz"
+ integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
+ dependencies:
+ "@types/json-schema" "^7.0.8"
+ ajv "^6.12.5"
+ ajv-keywords "^3.5.2"
+
+schema-utils@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz"
+ integrity sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==
+ dependencies:
+ "@types/json-schema" "^7.0.9"
+ ajv "^8.9.0"
+ ajv-formats "^2.1.1"
+ ajv-keywords "^5.1.0"
+
+semver@^7.5.4:
+ version "7.6.3"
+ resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz"
+ integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
+
+serialize-javascript@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz"
+ integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==
+ dependencies:
+ randombytes "^2.1.0"
+
+source-map-js@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz"
+ integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+
+source-map-support@~0.5.20:
+ version "0.5.21"
+ resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz"
+ integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map@^0.6.0:
+ version "0.6.1"
+ resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+supports-color@^8.0.0:
+ version "8.1.1"
+ resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz"
+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+ dependencies:
+ has-flag "^4.0.0"
+
+tailwindcss@^4.0.0, "tailwindcss@>=3.0.0 || >= 3.0.0-alpha.1":
+ version "4.0.0"
+ resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.0.tgz"
+ integrity sha512-ULRPI3A+e39T7pSaf1xoi58AqqJxVCLg8F/uM5A3FadUbnyDTgltVnXJvdkTjwCOGA6NazqHVcwPJC5h2vRYVQ==
+
+tapable@^2.1.1, tapable@^2.2.0:
+ version "2.2.1"
+ resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz"
+ integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
+
+terser-webpack-plugin@^5.3.10:
+ version "5.3.11"
+ resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz"
+ integrity sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==
+ dependencies:
+ "@jridgewell/trace-mapping" "^0.3.25"
+ jest-worker "^27.4.5"
+ schema-utils "^4.3.0"
+ serialize-javascript "^6.0.2"
+ terser "^5.31.1"
+
+terser@^5.31.1:
+ version "5.37.0"
+ resolved "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz"
+ integrity sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==
+ dependencies:
+ "@jridgewell/source-map" "^0.3.3"
+ acorn "^8.8.2"
+ commander "^2.20.0"
+ source-map-support "~0.5.20"
+
+update-browserslist-db@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz"
+ integrity sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==
+ dependencies:
+ escalade "^3.2.0"
+ picocolors "^1.1.1"
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+watchpack@^2.4.1:
+ version "2.4.2"
+ resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz"
+ integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==
+ dependencies:
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.1.2"
+
+webpack-sources@^3.2.3:
+ version "3.2.3"
+ resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz"
+ integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
+
+"webpack@^4.0.0 || ^5.0.0", webpack@^5.0.0, webpack@^5.1.0:
+ version "5.97.1"
+ resolved "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz"
+ integrity sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==
+ dependencies:
+ "@types/eslint-scope" "^3.7.7"
+ "@types/estree" "^1.0.6"
+ "@webassemblyjs/ast" "^1.14.1"
+ "@webassemblyjs/wasm-edit" "^1.14.1"
+ "@webassemblyjs/wasm-parser" "^1.14.1"
+ acorn "^8.14.0"
+ browserslist "^4.24.0"
+ chrome-trace-event "^1.0.2"
+ enhanced-resolve "^5.17.1"
+ es-module-lexer "^1.2.1"
+ eslint-scope "5.1.1"
+ events "^3.2.0"
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.2.11"
+ json-parse-even-better-errors "^2.3.1"
+ loader-runner "^4.2.0"
+ mime-types "^2.1.27"
+ neo-async "^2.6.2"
+ schema-utils "^3.2.0"
+ tapable "^2.1.1"
+ terser-webpack-plugin "^5.3.10"
+ watchpack "^2.4.1"
+ webpack-sources "^3.2.3"