By Year
- Architecture By Year
- Modèle de données By Year
- Cycle d’une année, calendrier et jobs
- Commandes serveur, réouverture et transitions By Year
- Décisions et génération des résultats By Year
- Bots et simulations asynchrones By Year
- Évaluations, logbook, scénarios et rankings By Year
- UI et prefabs By Year
- ENT1 — architecture et logique game-specific
- HR1 — architecture et logique game-specific
- MGT1 — architecture et logique game-specific (DEPRECATED)
- MK1 — architecture et logique game-specific
- Notifications et présence
- MK2 — architecture et logique game-specific
Architecture By Year
Rôle
By Year est le framework historique où une activité progresse année par année. À chaque année, les équipes prennent des décisions game-specific, le serveur génère les résultats, calcule les évaluations et prépare l'année suivante.
flowchart LR
ACT[ActivityByYear] --> Y[Current Year]
Y --> DEC[TeamDecisionsByYear]
DEC --> GAME[Game-specific logic]
GAME --> RES[ResultsByYear]
RES --> EVAL[TeamEvaluation / StudentEvaluation]
RES --> NEXT[Next Year]
Unity
| Couche | Emplacement |
|---|---|
| Modèles | Assets/Src/Game/Data/ByYear/ |
| Controllers | Assets/Src/Game/Controllers/*ByYear* |
| UI commune | Assets/Src/UI/SimByYear/ |
| Game-specific | Assets/GameSpecific/ent1|hr1|mgt1|mk1|mk2/ |
Serveur
_utils/utils_by_year.js: cycle, calendrier, jobs, bots et helpers annuels;_utils/utils_eval.js: évaluations communes;_utils/utils_rankings.js: rankings;_utils/factory/utils_factory_generate_results.js: sélection du moteur de résultats par GameUid;_utils/factory/utils_factory_eval.js: sélection du moteur d'évaluation par GameUid;<gameUid>/utils/*: décisions, events, résultats et scoring propres au jeu.
Game-specific
Contrairement à Accounting, By Year n'a pas une seule logique métier partagée. Le framework fournit le cycle; les jeux fournissent les décisions et les calculs. Il faut donc éviter de mettre les règles MK1, HR1 ou ENT1 dans une page “Common”.
Ajouter un nouveau jeu By Year de A à Z
Un nouveau jeu annuel n'est pas seulement un nouveau dossier GameSpecific. Il doit être branché côté Unity, static data, serveur, génération, évaluation, résultats, messages, UI et factories.
1. GameUid et composition Unity
- Ajouter le GameUid dans
SharedConstants. - Le mapper vers
SimType.ByYear. - Créer
Assets/GameSpecific/<gameUid>/. - Créer les modèles
StaticData,Team,TeamDecisions,Resultset les controllers game-specific nécessaires. - Brancher les classes dans
Factory.cs.
2. Static data
Ajouter le jeu dans node.js/exports/exportAllData.js avec Spreadsheet/GIDs et fichiers supplémentaires. Créer Resources/data_<gameUid>, importer puis vérifier StaticDataController<Game>.
3. Serveur game-specific
Créer server_scripts/<gameUid>/ avec :
utils/<gameUid>_utils.js—GameSpecificUtils*;utils/<gameUid>_utils_generate_results.js—GenerateResults*;utils/<gameUid>_utils_eval.js—Eval*si l'évaluation est spécifique;utils/..._bots.jssi async/bots;- wrappers
command/.
4. Brancher les factories serveur
utils_factory_game_specific_utils.js;utils_factory_generate_results.js;utils_factory_eval.js;takeBotsDecision()/ factory bots si le jeu supporte les bots.
5. Définir le cycle annuel du jeu
Implémenter au minimum startYearGameSpec(actData), getYearData(), generateEvents(), gameSpecificInitTeam(), startCash(), anyDecisionsTaken() et les helpers métier requis.
6. Génération
GenerateResults<Game>.generateCurrentYearResults(actData) doit être l'entrée unique du calcul annuel. Il doit lire les équipes/décisions, calculer les résultats, les sauvegarder et préparer l'état de l'année suivante sans dépendre de l'UI.
7. Unity UI
Créer les contrôles game-specific dans GameSpecific/<gameUid>/Src/UI, plus les prefabs/scenes/Resources. Les composants génériques année/eval/rankings restent sous Src/UI/SimByYear.
8. Textes, scénario, tutoriels, timelines
Ajouter les textes et assets propres au jeu. Si le jeu possède ses propres tutoriels/timelines, les placer dans ses dossiers GameSpecific et les brancher via les states/UI concernés.
9. Validation minimale
- Créer une simulation.
- Démarrer année 1.
- Prendre au moins une décision.
- Générer résultats.
- Vérifier résultats + cash + ActPerf.
- Passer à l'année suivante.
- Réouvrir une année.
- Tester async/bot si supporté.
- Tester rankings/final sequence.
Modèle de données By Year
Objets Unity
SimulationByYear: simulation et options propres à la famille;ActivityByYear: année courante, états, calendrier et données de l'activité;TeamByYear: équipe et données runtime;TeamDecisionsByYear: décisions par année;ResultsByYear: résultats annuels;TeamEvaluationByYear/StudentEvaluationByYear: évaluations;StaticDataByYear: base de static data des jeux annuels.
Controllers
Les classes ActivityByYearController, SimulationByYearController, TeamByYearController, TeamEvaluationByYearController et StudentEvaluationByYearController servent de couche runtime entre ces modèles et l'UI.
Documents serveur
flowchart TD
SIM[(simulation)] --> ACT[(activity)]
ACT --> TEAM[(teams)]
ACT --> GAME[(actDataGameSpec)]
TEAM --> DEC[team decisions / activities]
ACT --> RES[(results)]
TEAM --> ET[(evaluationTeam)]
SIM --> ES[(evaluation)]
TEAM --> LOG[(teamsLogbooks)]
currentYear / currentYearState
L'activité garde l'année courante et son état. Les transitions serveur utilisent notamment les états “not started”, “year started”, “generating” et “year finished”. Les contrôles Unity affichent l'écran approprié en fonction de cet état.
years[]
Chaque entrée annuelle peut porter les dates de début/fin et le flag de pratique. Le calendrier et le mode de gestion déterminent si les transitions se font manuellement ou automatiquement.
Ajouter un champ de décision ou de résultat By Year
Décision étudiante
- Ajouter le champ dans le modèle Unity
TeamDecisions<Game>ouYearDataselon son scope. - Ajouter la désérialisation avec une valeur compatible avec les anciennes équipes.
- Ajouter le contrôle UI qui édite cette décision.
- Ajouter la custom command serveur ou étendre celle qui possède déjà la décision.
- Sauvegarder dans le bon
yearDatade l'équipe. - Envoyer un RTT game-specific si les coéquipiers doivent voir le changement.
- Consommer la décision dans
GenerateResults<Game>. - Mettre à jour
anyDecisionsTaken()si cette décision compte pour déterminer si l'équipe a commencé. - Mettre à jour les bots si une équipe bot doit prendre cette décision.
Résultat annuel
- Ajouter le champ au modèle serveur de résultat produit par
generateCurrentYearResults(). - Le persister dans
results. - Ajouter le modèle Unity
Results/ResultsByYearet désérialiser. - Ajouter report/tooltip/UI.
- Si le champ participe à ActPerf, mettre à jour
Eval<Game>.computeActPerfScore(). - Si le champ participe au ranking, mettre à jour
updateRankings<Game>(). - Vérifier
deleteResults()/reopen year.
Où placer une donnée annuelle
| Donnée | Emplacement |
|---|---|
| Décision qui change chaque année | TeamDecisions<Game>.yearData[year] ou structure équivalente. |
| État d'équipe durable entre années | Team<Game> / team data persistante. |
| Résultat calculé d'une année | results. |
| Configuration immuable | Static data. |
| Donnée commune à toute l'activité | actDataGameSpec. |
Compatibilité anciens documents
Une nouvelle propriété de team/activity doit être tolérante à l'absence. Si sa valeur doit absolument exister sur les vieux documents, utiliser la migration correspondante dans _utils/version/.
Cycle d’une année, calendrier et jobs
startYear()
utils_by_year.startYear(actData, updateYearFinished) incrémente l'année si nécessaire, passe l'état à ACT_YEAR_STARTED, enregistre startTime, appelle startYearGameSpec(), initialise les stubs de présence puis envoie act_year_started.
stateDiagram-v2
[*] --> NotStarted
NotStarted --> YearStarted: startYear
YearStarted --> Generating: fin de période / commande
Generating --> YearFinished: génération terminée
YearFinished --> YearStarted: année suivante
YearFinished --> [*]: dernière année
nextGenerate
updateNextGenerateDate() calcule quand le prochain traitement automatique doit avoir lieu. En pratique, les années n'ont pas de génération automatique. En mode calendrier, nextGenerate pointe vers la fin de l'année active ou le début de la suivante.
Validation des dates
validateDates() corrige les années mal ordonnées ou placées dans le passé et garantit notamment un intervalle minimal de cinq minutes. Si des dates sont déplacées, le professeur peut recevoir years_date_modified.
Job planifié
gatherActsForJob() récupère les activités dont nextGenerate < now. runActJob() traite une activité active : si l'année est commencée, elle passe à generating et appelle generateResultsJob(); sinon elle peut démarrer l'année suivante.
generateResultsJob()
- envoie
act_generating; - fait jouer les bots si l'activité est async;
- génère les events game-specific;
- appelle
generateResultsByGameUid[gameUid].generateCurrentYearResults(actData); - décide s'il faut démarrer automatiquement l'année suivante;
- met à jour
nextGenerate; - sauvegarde l'activité;
- envoie
act_year_finishedlorsque requis.
Scripts de job
Les wrappers se trouvent sous server_scripts/_job/, notamment generate_results_job.js et le scheduler commun.
Modifier le cycle annuel ou le calendrier sans casser les jobs
Invariants du cycle
startYear()initialise l'année et appellestartYearGameSpec();- la génération passe par
generateResultsJob()ou la commande manuelle équivalente; nextGeneratedétermine le prochain traitement automatique;- les années de pratique ne suivent pas forcément le même calendrier automatique;
- le client est averti via les messages
act_year_started,act_generating,act_year_finished.
Changer la logique de dates
Les fonctions à auditer sont updateNextGenerateDate(), validateDates(), dateToUpper(), dateToLower() et gatherActsForJob().
- Définir le comportement attendu pour sync, async et practice.
- Vérifier que les dates passées sont corrigées de façon déterministe.
- Vérifier l'intervalle minimal attendu par
validateDates(). - Mettre à jour les messages professeur si des dates sont déplacées.
- Tester le job avec une activité dont
nextGenerateest juste avant/après “now”.
Modifier startYear()
Toute nouvelle initialisation annuelle doit être faite soit dans le socle commun si elle concerne tous les jeux, soit dans startYearGameSpec(). Ne pas ajouter des branches GameUid directement dans utils_by_year.startYear().
Tester un changement de cycle
| Cas | À vérifier |
|---|---|
| Année 1 manuelle | start state, presence, year data, RTT. |
| Fin manuelle | generating → results → finished. |
| Calendrier automatique | nextGenerate, scheduler, next year. |
| Async | bots avant génération. |
| Dernière année | pas de démarrage fantôme d'une année suivante. |
| Reopen | état + results + evals + logbook restaurés/cohérents. |
Debug d'un job qui ne part pas
- Lire
activity.nextGenerate. - Vérifier que l'activité est active.
- Vérifier
gatherActsForJob(). - Exécuter
runActJob()avec l'UID. - Inspecter l'état annuel avant/après.
- Vérifier le log de
generateResultsJob().
Commandes serveur, réouverture et transitions By Year
Entrées communes
Le framework annuel est exposé par des commandes communes et par des wrappers game-specific.
Commandes communes
change_year_state: change l'état annuel;student_take_decisions: marque/enregistre la prise de décisions selon le flux annuel;get_activity_current_year: année courante;ignore_year_from_average: exclut une année de certaines moyennes;save_market_size_modifier: modificateur de marché;set_year_sequence_seen: état de séquence côté utilisateur.
Wrappers game-specific
Les dossiers ent1/command, hr1/command, mgt1/command, mk1/command et mk2/command exposent typiquement :
<game>_create_team;<game>_custom_command;<game>_generate_results;<game>_start_year;<game>_start_next_activity;<game>_reopen_year;<game>_compute_scenario_score;<game>_compute_segment_score.
Certains jeux ajoutent des wrappers propres, par exemple des mises à jour de rankings.
Réouverture d'année
_utils/command/utils_reopen_year.js est la logique partagée. Une réouverture ne se limite pas à décrémenter currentYear : il faut supprimer/retourner les résultats, nettoyer les évaluations annuelles, logbook/presence associés et remettre l'activité/équipes dans un état cohérent.
Transition activité suivante
utils_start_next_activity.js gère la transition entre activités. Les modes sync/async et le GameUid influencent les messages et l'activité chargée côté clients.
Custom commands
utils_custom_command_by_year.js est le point commun de dispatch lorsque la commande n'est pas directement implémentée dans le wrapper game-specific. Les règles métier restent dans utilsByGameUid[gameUid].
Debug
flowchart LR
UNITY[BackEndBrainCloud] --> WRAP[gameUid/command]
WRAP --> COMMON[_utils/command]
COMMON --> SPEC[game-specific utils]
SPEC --> DOC[activity / teams / results]
DOC --> RTT[act_year_* / result messages]
Ajouter une Custom Command By Year
Les commandes communes comme market research, decisions finished ou financial help passent par utils_custom_command_by_year.js; les décisions métier propres au jeu doivent rester dans les utils game-specific.
Procédure
- Définir
commandUidet la payload. - Brancher le wrapper
<gameUid>_custom_command. - Si commande commune, ajouter le cas dans
executeCommandByYear(). - Sinon déléguer vers
utilsByGameUid[gameUid]/ utils custom du jeu. - Modifier
teamDecisionsData/teamData/actDataau bon niveau. - Sauvegarder les documents concernés.
- Envoyer un RTT avec la payload minimale.
- Ajouter le modèle/handler Unity.
Réouverture d'année — système destructif
utils_reopen_year.reopenYear() ne doit pas être traité comme un simple changement de state. Il doit remettre en cohérence :
- activity/current year;
- results;
- team decisions/cash si le jeu les reset;
- evaluationTeam de l'année;
- logbook annuel;
- présence;
- events/produits game-specific lorsque le moteur le prévoit;
- rankings si nécessaire.
Quand une nouvelle donnée annuelle est ajoutée
Auditer immédiatement reopenYear(), GenerateResults<Game>.deleteResults(), deleteProducts() et les helpers de reset du jeu. Si la donnée existe seulement après génération, elle doit généralement être supprimée/restaurée lors d'une réouverture.
Transition activité suivante
utils_start_next_activity.js porte la logique commune. Un nouveau jeu ou nouveau type d'activité doit être testé avec :
- activité précédente terminée;
- création/chargement de la suivante;
- première année de la suivante;
- étudiants/teams copiés ou regroupés;
- async vs sync;
- RTT
next_act_started/async_next_act_started.
Décisions et génération des résultats By Year
Principe
Le framework By Year ne connaît pas la structure détaillée des décisions. Il délègue à utilsByGameUid[gameUid] et au moteur generateResultsByGameUid[gameUid].
sequenceDiagram
participant U as Unity Decisions UI
participant B as BackEndBrainCloud
participant C as gameUid_custom_command
participant T as teams
participant J as generateResultsJob
participant G as GenerateResultsGame
participant R as results
U->>B: CustomCommand / décision
B->>C: script game-specific
C->>T: sauvegarde TeamDecisions
J->>G: generateCurrentYearResults(actData)
G->>T: lit décisions de toutes les équipes
G->>G: marché / RH / production / features...
G->>R: saveResults
G->>T: met à jour cash/état année suivante
Pattern des moteurs de résultats
Chaque jeu possède un objet GenerateResults* avec généralement :
- collecte des décisions de toutes les équipes;
- calcul d'averages/références;
- calcul des scores ou du marché;
- events;
finalizeTeamsData();saveResults();- reset/préparation de l'année suivante;
deleteResults()pour réouverture/rollback.
Factories serveur
utils_factory_generate_results.js instancie le moteur adapté à ent1, hr1, mgt1, mk1 ou mk2. Ajouter un nouveau jeu By Year exige donc de le brancher dans cette factory.
UI Unity
Le socle visuel se trouve dans DecisionsByYearControl, DecisionsPanelByYear et les contrôles SimByYear*. Les écrans de décisions réels sont souvent dans GameSpecific/<gameUid>/Src/UI.
Ajouter une nouvelle décision qui influence les résultats
Ce workflow est le cœur d'une feature By Year.
1. Static data/config
Ajouter la donnée de référence dans la Sheet/JSON game-specific si la décision dépend d'un coût, seuil, segment, feature, pays, employé, etc.
2. Modèle Unity
Ajouter la valeur dans TeamDecisions<Game>/YearData et l'UI qui l'édite.
3. Sauvegarde serveur
Ajouter la custom command game-specific. Valider cash, permissions et contraintes côté serveur, même si l'UI fait déjà la validation.
4. Synchronisation équipe
Émettre un message game-specific afin que les autres membres voient la décision. Ajouter la constante dans GameSpecific/<gameUid>/Src/Messages/MessageType*.cs si nécessaire.
5. Résultats
Consommer la décision dans GenerateResults<Game>. Choisir l'étape correcte : gather → averages → scores → sales/hiring/production → finalize/save.
6. Préparer l'année suivante
Si la décision crée un état durable, vérifier finalizeTeamsData() / reset team pour transporter seulement ce qui doit survivre.
7. Évaluation, ranking, report
Si la nouvelle décision change un score, mettre à jour Eval et ranking. Si elle produit un nouveau résultat, ajouter le modèle Unity et le report.
8. Bots
Pour les jeux async avec bots, décider explicitement quelle valeur le bot choisit. Sinon les bots peuvent générer des résultats invalides ou avoir un avantage/désavantage artificiel.
Debug d'un résultat “bizarre”
- Inspecter la décision persistée avant génération.
- Inspecter les données agrégées de toutes les équipes.
- Inspecter averages/références.
- Inspecter score intermédiaire.
- Inspecter résultat final.
- Inspecter cash/team state après
finalizeTeamsData(). - Comparer le modèle Unity désérialisé.
Bots et simulations asynchrones By Year
Quand les bots sont utilisés
Dans generateResultsJob(), si actData.isAsync est vrai, takeBotsDecision(actData) est appelé avant la génération des events et résultats.
Jeux avec bots explicites
Le routeur commun instancie actuellement BotsEnt1, BotsMk1 ou BotsMk2. Les équipes marquées isBot reçoivent alors des décisions générées avant le calcul des résultats.
flowchart LR
JOB[generateResultsJob] --> ASYNC{isAsync?}
ASYNC -->|oui| B[takeBotsDecision]
B --> BOT[Bot game-specific]
BOT --> TD[Team decisions]
ASYNC -->|non| EV[Events]
TD --> EV[Events]
EV --> RES[Generate Results]
ENT1
BotsEnt1 gère notamment prêts, capacité de production, construction d'usines, employés, matières premières, choix de pays et création/lancement de produit.
MK1/MK2
Les bots Marketing construisent des décisions de produit/marché selon un niveau et des heuristiques game-specific.
Attention
Le code désactive temporairement l'envoi de messages pendant la boucle bots afin d'éviter de spammer les clients avec chaque décision artificielle. Les documents équipes sont ensuite sauvegardés.
Ajouter ou modifier une décision de bot
Les bots sont exécutés par takeBotsDecision(actData) avant la génération de résultats des activités async. Une nouvelle décision métier qui est obligatoire doit être supportée par le bot du jeu.
Procédure
- Ajouter la logique dans
Bots<Game>.takeDecisions()ou un helper spécialisé. - Réutiliser les mêmes fonctions de coût/validation que les humains lorsque possible.
- Éviter de modifier directement les résultats : le bot doit produire des décisions, puis passer dans le même moteur de génération.
- Respecter le
botLevelsi le jeu simule plusieurs niveaux. - Vérifier cash/stock/capacité après chaque décision en chaîne.
- Ajouter une garde si une static data attendue manque.
Jeux actuellement supportés
Le socle crée explicitement des bots pour ENT1, MK1 et MK2. HR1/MGT1 ne doivent pas être supposés bot-compatible sans implémentation dédiée.
Messages pendant les bots
Le framework peut désactiver temporairement certains messages pendant la boucle bot. C'est voulu : on ne veut pas envoyer à chaque client un RTT pour chaque micro-décision artificielle. Vérifier la synchronisation finale après sauvegarde/génération.
Tester un bot
- cash très bas;
- année 1 et année suivante;
- marché/segment déjà utilisé;
- static data manquante ou vide;
- plusieurs bots ensemble;
- mêmes inputs avec seed/config stable;
- génération complète après décisions.
Évaluations, logbook, scénarios et rankings By Year
Évaluations
_utils/utils_eval.js fournit le cadre commun pour activité, présence, logbook, custom evaluations et stubs. Chaque jeu fournit ensuite son Eval<Game> via la factory d'évaluation.
Scores game-specific
Les classes EvalEnt1, EvalHr1, EvalMgt1, EvalMk1 et équivalents calculent notamment scenario score, segment score et activity performance à partir des résultats propres au jeu.
Logbook
Les données de logbook sont persistées dans teamsLogbooks. utils_by_year.deleteYearLogbook() montre que le logbook et ses évaluations sont liés au cycle annuel et doivent être nettoyés correctement lors d'une réouverture/suppression d'année.
Présence
startYear() crée un nouveau stub de présence pour les étudiants. Les helpers setYearPresence() / deleteYearPresence() maintiennent ces données lors des changements d'état.
Scénarios
Le client utilise StudentPlayScenarioByYearState et les pages/scénarios propres au jeu. Le serveur peut calculer le scenario score via la factory d'évaluation game-specific.
Rankings
_utils/utils_rankings.js gère les scores et mises à jour de rankings. Chaque jeu peut appliquer son propre calcul avec des fonctions comme updateRankingsEnt1, updateRankingsHr1, etc.
flowchart LR
RESULTS[Year Results] --> PERF[Act Performance]
SCEN[Scenario] --> EVAL[Student Evaluation]
LOG[Logbook] --> EVAL
PRES[Presence] --> EVAL
PERF --> EVAL
RESULTS --> RANK[Rankings]
UI
EvaluationByYearSyncControl, EvaluationByYearAsyncControl, lignes étudiant, detailed reports, logbook controls, presence popup et ranking controls composent la couche commune.
Ajouter un score ActPerf ou une composante de ranking
ActPerf
- Ajouter/calculer la métrique dans les résultats du jeu.
- Modifier
Eval<Game>.computeActPerfScore(). - Ajouter la pondération dans
act_perf_pondsi la métrique est configurable. - Vérifier le stub envoyé à
evaluationTeam. - Ajouter le label/report Unity.
- Tester score d'année en cours vs année terminée.
Ranking
Chaque jeu peut définir updateRankings<Game>(). Une nouvelle métrique de ranking doit être calculable depuis la donnée persistée après génération; éviter un ranking basé sur un état transitoire uniquement présent en mémoire.
Scénario
Les réponses scénario sont distinctes des décisions annuelles. Si une nouvelle question/segment influence l'évaluation :
- mettre à jour la static data/scenario;
- mettre à jour le prefab/page Unity si nouveau type visuel;
- vérifier
computeScenarioScoreAndSave()oucomputeSegmentScoreAndSave()du jeu; - vérifier les
Scenario.eScenarioStepuniquement s'ils contrôlent un flow/tutoriel, pas comme stockage des réponses.
Logbook et présence lors d'une nouvelle année
startYear() initialise la présence; reopen doit la supprimer/restaurer correctement. Le logbook est par activité/année et son évaluation est séparée du texte. Toute nouvelle logique annuelle doit conserver ces index d'année.
Debug d'une note globale fausse
- Inspecter le résultat game-specific brut.
- Appeler
computeActPerfScore()avec le même year/team. - Inspecter
evaluationTeam. - Inspecter les pondérations simulation/static data.
- Inspecter le stub Unity et les caches.
UI et prefabs By Year
Socle commun
Assets/Src/UI/SimByYear/ contient l'UI réutilisée par les jeux annuels. Le contenu métier propre à ENT1/HR1/MGT1/MK1/MK2 reste dans Assets/GameSpecific/<gameUid>/Src/UI et leurs prefabs Resources.
Contrôles principaux
| Domaine | Classes |
|---|---|
| Simulation sync/async | SimByYearControlBase, SimByYearSyncControl, SimByYearAsyncControl |
| Années | ActivityYearsControl, YearItemControl, WaitSimStartPopupControl |
| Décisions | DecisionsByYearControl, DecisionsPanelByYear |
| Évaluation | EvaluationByYearSyncControl, EvaluationByYearAsyncControl, StudentEval lines |
| Reports | DetailedReport*, ActPerfEval* |
| Logbook | LogBookControl, LogbookEvalPopupControl, LogbookRoleControl |
| Rankings | RankingsPanelControl, HiScoresPopupControl, FinalSequenceRankingsControl |
| Events | EventHistoryPopupControl + rows game-specific |
Final sequence
Les contrôles FinalSequence* gèrent la fin de simulation, les équipes, classements et trophées. Ils consomment les rankings/résultats générés côté serveur.
Règle de maintenance
Une UI qui ne connaît ni segment MK, ni employee HR, ni factory ENT, ni feature MGT appartient ici. Dès qu'un écran manipule une structure propre à un jeu, le code et le prefab doivent rester dans GameSpecific.
Ajouter un écran de décision ou un report By Year
Écran réellement commun
Les contrôles de cycle, années, eval, rankings, logbook et final sequence peuvent vivre sous Assets/Src/UI/SimByYear. Une UI qui connaît un type métier game-specific doit rester sous Assets/GameSpecific/<gameUid>/Src/UI.
Nouvel écran de décision game-specific
- Créer le contrôle sous
GameSpecific/<gameUid>/Src/UI. - Créer/mettre à jour le prefab dans les Resources du jeu.
- Lire la décision depuis
TeamController<Game>/TeamDecisions<Game>. - Envoyer les changements via la custom command game-specific, pas en modifiant seulement le modèle local.
- Écouter le message game-specific pour les modifications d'un coéquipier.
- Ajouter les textes.
- Ajouter un
TutorialUIObjectsi l'écran participe à un tutoriel.
Nouveau report de résultats
- Ajouter le champ dans le modèle
Results<Game>. - Créer le contrôle/report UI.
- Brancher le report panel/detailed report du jeu.
- Gérer année sélectionnée, équipe sélectionnée et teacher/student permissions.
- Tester résultat absent (année non générée), année courante et années historiques.
Sync vs async
Les écrans communs ont souvent des variantes SimByYearSyncControl et SimByYearAsyncControl, ainsi que des contrôles d'évaluation sync/async. Avant d'ajouter un bouton “commun”, vérifier s'il est valide dans les deux modes.
Checklist prefab/UI By Year
- student + teacher;
- sync + async si applicable;
- année 1 + historique;
- avant résultats + après résultats;
- RTT coéquipier;
- textes FR/EN;
- tutorial target si requis;
- WebGL.
ENT1 — architecture et logique game-specific
Positionnement
ENT1 est un jeu By Year centré sur une entreprise qui combine production, usines, matières premières, ressources humaines, financement et lancement de produits sur plusieurs pays. Le framework annuel reste commun; les décisions et calculs ENT1 vivent sous GameSpecific/ent1 et server_scripts/ent1.
Static data
exports/exportAllData.js associe ENT1 aux datasets suivants : scenario, salary, employees, raw_mat, factories, product_specs, comms, distrib, medias, events, price_specs, segments, countries et act_perf_pond, plus constants.
Modèles Unity
| Domaine | Classes principales |
|---|---|
| Production | Factory, FactoryData, RawMatData, ProductionResult |
| Produits/marché | ProductSpecs, LaunchedProduct, Market, Country, DistribData, MediaData, CommTool |
| RH | EmployeesData, SalaryData, ResignationResult |
| Résultats | Results, LaunchProductSales, LaunchProductScores, Ratio |
| Runtime | TeamDecisionsEnt1, TeamEnt1, YearData |
Commandes métier serveur
ent1_utils_custom_command.js couvre notamment :
- création/suppression/lancement de produit;
- construction et vente d'usine;
- achat de matières premières;
- affectation de production d'usine;
- embauche/licenciement;
- salaire;
- prêt/financement.
Génération des résultats
flowchart TD
DEC[Décisions équipes] --> PROD[Usines + matières premières]
DEC --> HR[Employés + salaires]
DEC --> LP[Produits lancés]
PROD --> CAP[Capacité / production]
HR --> CAP
LP --> MARKET[Marché par pays/segment]
CAP --> SALES[Ventes]
MARKET --> SALES
SALES --> FIN[Cash / résultats]
FIN --> SCORE[Scores + ratios + rankings]
GenerateResultsEnt1 rassemble produits et usines, calcule moyennes, démissions, production, scores et ventes, puis finalise les équipes et sauvegarde les résultats.
Bots
BotsEnt1 peut décider prêts, production souhaitée, construction d'usines, employés, choix de pays, matières premières et création de produits. Il est utilisé dans les simulations async.
UI
Les écrans game-specific sont sous Assets/GameSpecific/ent1/Src/UI : usines, production, raw materials, RH, salaires, finance, pays, portfolio, lancement produit, marché, rapports et checklists.
Debug
Pour un résultat ENT1 incorrect, suivre : custom command → TeamDecisionsEnt1 → GenerateResultsEnt1 → Results. Pour une incohérence de production, inspecter ensemble factories, raw mats, employés et la quantité réellement lancée.
Ajouter une feature ENT1 de A à Z
ENT1 croise plusieurs sous-systèmes; une décision peut affecter production, matières premières, employés, cash, produits et ventes. Avant de coder, identifier laquelle de ces chaînes la feature modifie.
Carte serveur à connaître
| Zone | Fonctions clés |
|---|---|
| Cycle/état | startYearGameSpec, getYearData, generateEvents, anyDecisionsTaken. |
| Usines / production | getTotalFactoryCost, getNeededRawMats, SetProductionsForProduct, computeFactoriesProduction. |
| RH | getNumEmployees, computeSalaryAverages, computeResignations. |
| Marché | gatherProducts, computeProductAverages, computeScores, generateSales. |
| Fin de génération | finalizeTeamsData, saveResults, resetTeam, deleteResults. |
Custom commands ENT1 existantes
Le fichier ent1_utils_custom_command.js gère notamment saveLaunch, deleteLaunch, buildFactory, sellFactory, buyRawMats, setFactoryProduction, hireEmployees, dismissEmployees, setEmployeeSalary et updateLoan.
Procédure pour une nouvelle décision
- Ajouter la static data si la décision dépend d'un coût/capacité/pays/employee.
- Ajouter le champ au
YearData/TeamDecisionsEnt1. - Ajouter l'UI dans le domaine approprié : factory, HR, finance, product/market.
- Ajouter la command et valider cash/capacité côté serveur.
- Envoyer un message ENT1 et mettre à jour
TeamControllerEnt1. - Consommer la décision dans la bonne phase de
GenerateResultsEnt1. - Si l'état survit à l'année, mettre à jour
finalizeTeamsData/resetTeam. - Si score/ratio change, mettre à jour Eval/rankings/report.
- Si bots supportés, ajouter la décision dans
BotsEnt1. - Tester reopen year.
Piège principal : dépendances physiques
Une décision de lancement produit peut être impossible même si le produit est valide : il faut production disponible, usines, matières premières et employés. Les validations UI ne remplacent jamais celles côté serveur.
Debug génération ENT1
- team decisions;
- factories + raw mats;
- employees/salaries;
- production calculée;
- products gathered;
- averages/scores;
- sales;
- cash/ratios finalisés;
- Results Unity/report.
HR1 — architecture et logique game-specific
Positionnement
HR1 est le jeu By Year orienté gestion des ressources humaines : staffing, rémunération, avantages, postings, contrats, CEO et segments d'employés.
Static data
Les datasets importés incluent ceo, employees, strategies, advantages, events, scenario, segments, contracts et act_perf_pond, plus constants.
Modèles Unity
CeoData;EmployeeData/EmployeesAdvantage/AdvantageData;PostingData,LaunchedPosting,PostingResult;ContractData,ContractResult;Segment,SegmentResult,SegmentScores;TeamDecisionsHr1,TeamHr1,YearData.
Hooks serveur
GameSpecificUtilsHr1 gère entre autres le démarrage d'année, génération de contrats, reset des avantages, events, CEOs, coûts d'avantages, coûts de postings/licenciements, coûts employés et rankings.
Génération
flowchart LR
DEC[Staffing + rémunération + avantages + postings] --> HIRE[Hiring]
DEC --> RET[Retention / belonging]
DEC --> COST[Employee costs]
DEC --> CEO[CEO]
HIRE --> RES[Segment Results]
RET --> RES
COST --> RES
CEO --> RES
RES --> SCORE[Scores / ActPerf]
GenerateResultsHr1 finalise contrats et CEOs, calcule hiring, averages, retention/belonging, scores de postings et segments, puis sauvegarde les résultats.
Évaluation
EvalHr1 fournit scenario score, segment score et performance annuelle. Les résultats HR alimentent ensuite le cadre commun d'évaluation By Year.
UI
Les vues spécifiques comprennent CEO, staffing, employés, avantages, stratégies, postings, contrats, rémunération et rapports. Les classes sont sous Assets/GameSpecific/hr1/Src/UI.
Debug
Pour un problème RH, isoler d'abord la décision (employee/segment/posting/advantage), puis le coût calculé dans hr1_utils.js, puis la phase correspondante de GenerateResultsHr1.
Ajouter une feature HR1 de A à Z
Custom commands réelles
hr1_custom_command.js route actuellement :
posting/delete_posting;dismiss/delete_dismiss;advantage/delete_advantage;salary;ceo_bid/delete_ceo_bid;change_wanted_contracts;- puis les commandes By Year communes en fallback.
Ces commandes ne font pas que sauvegarder une valeur : elles recalculent souvent un coût, modifient cash, sauvegardent teams, parfois actDataGameSpec, puis envoient des RTT comme posting_changed, salary_changed, team_new_bid.
Carte résultats
finalizeContracts()— contrats;solveCeos()— résolution des CEO bids;computeAverages()— références du marché RH;computeHiring()— embauche;computeRetentionAndBelonging()— rétention/appartenance;computeScores()— scores;gatherSegments()— collecte des décisions par segment;generateCurrentYearResults()/saveResults().
Ajouter une nouvelle décision RH
- Définir si la décision appartient au segment, à l'employé, aux avantages, aux postings, CEO ou contrats.
- Ajouter la donnée au bon niveau de
TeamDecisionsHr1/YearData. - Ajouter l'UI sous
GameSpecific/hr1/Src/UI. - Ajouter la branche dans
hr1_custom_command.js. - Calculer le coût avec un helper
utilsHr1partagé; ne pas dupliquer le calcul UI. - Appliquer/refund le delta de cash correctement lors d'une édition/suppression.
- Envoyer le RTT approprié.
- Consommer la décision dans
GenerateResultsHr1. - Adapter
EvalHr1.computeActPerfScore()si le score change. - Vérifier reopen/delete results.
CEO bids : exemple d'opération multi-document
Un bid CEO modifie à la fois teams et un compteur dans actDataGameSpec.ceos via saveDocumentIncrementData(). Une nouvelle feature concurrentielle partagée entre équipes peut nécessiter le même pattern; il faut alors tester la concurrence entre plusieurs teams.
Debug HR1
Quand un coût est faux : comparer static data → helper de coût → delta ancien/nouveau → cash sauvegardé. Quand le résultat annuel est faux : comparer segment decisions → averages → hiring/retention → result segment → ActPerf.
MGT1 — architecture et logique game-specific (DEPRECATED)
Positionnement
MGT1 modélise la gestion d'une équipe de développement : employés, temps de travail, features, segments, budget et lancement d'un jeu.
Static data
Les datasets comprennent employees, events, features, scenario, segments et act_perf_pond, plus constants.
Modèles Unity
Employee,EmployeesData;Feature,FeatureResults,GameFeatureData;Segment;LaunchedGame,GameResults;TeamDecisionsMgt1,TeamMgt1,YearData.
Logique serveur
GameSpecificUtilsMgt1 gère notamment :
activation/inactivation d'employés;création et noms d'employés;coûts salaire et coûts additionnels;temps de travail;affectation d'employés aux features;travail requis et complétion de feature;events et rankings.
Résultats
GenerateResultsMgt1 calcule le travail requis, les bonus énergie/discipline/bonheur, les employés à inactiver, les résultats par équipe et la transition des employés vers l'année suivante.
flowchart LR
EMP[Employés] --> WORK[Temps de travail]
FEAT[Features] --> NEED[Travail requis]
WORK --> PROG[Progression features]
NEED --> PROG
PROG --> COMPLETE[Features complétées]
COMPLETE --> GAME[Résultat du jeu]
EMP --> BONUS[Energy / discipline / happiness]
BONUS --> GAME
UI
L'UI game-specific contient employees list, team leader, producer, president, feature selectors, budgets, work allocation, lancement du jeu et rapports.
Debug
Pour une feature qui progresse mal, vérifier setWorkOnFeature(), le temps/coût de l'employé, getNeededWork() et les bonus appliqués dans la génération annuelle.
Ajouter une feature MGT1 de A à Z
Custom commands réelles
mgt1_custom_command.js gère :
launch_game/delete_game;add_feature/delete_feature;hire_employees/dismiss_employee;change_work_time;save_social_budget/save_remote_budget;save_feature_ratios;change_employee_active_status;employee_level_up/employee_level_down.
Carte du moteur
getEmployeeSalaryCost()/getEmployeeAdditionnalCost()— coûts;getEmployeeWorkTimeBonus()/getEmployeeWorkTimeCost()— temps;getFeatureMinimumBudget();setWorkOnFeature();completeFeature();getNeededWork();getEnergyBonus(),getDisciplinaryBonus(),getHappinessBonus();computeTeamsResults()/generateCurrentYearResults().
Ajouter une nouvelle feature de jeu MGT1
Ajouter la static data sousfeaturessi c'est une feature configurable.Ajouter le modèle UnityFeature/GameFeatureDatasi structure nouvelle.Ajouter le contrôle UI et les ratios/budget si nécessaire.Ajouter la command de création/édition.Vérifier les employés déjà assignés lors d'une suppression :deleteEmployeeFeature()doit nettoyer les travaux devenus invalides.Ajouter la contribution auworkNeededet àcomputeTeamsResults().Vérifier la complétion viacompleteFeature().Ajouter le résultat/report.
Employés : index et noms
Le code associe certains employés par index dans yearData.employees et par nom dans employeesNames. Toute opération d'ajout/suppression doit préserver l'alignement attendu; une suppression mal gérée peut faire pointer un nom vers le mauvais employé.
Work time : validation serveur obligatoire
changeWorkTime() valide game, feature, task level et niveau réel de l'employé. Une nouvelle allocation de travail doit conserver ce type de validation côté serveur.
Debug MGT1
Pour une feature qui ne progresse pas : feature exists → employee works structure → level/workTime → needed work → bonuses → computeTeamsResults → final result.
MK1 — architecture et logique game-specific
Positionnement
MK1 est le moteur marketing By Year centré sur création de produit, choix de segment, prix, distribution, communication et lancement sur le marché.
Static data
Les datasets comprennent scenario, segments, product_specs, shops, price_specs, medias, comms, distrib, events et act_perf_pond, plus constants.
Modèles Unity
Product,ProductSpecs,LaunchedProduct;Segment,Market;MediaData,CommTool,DistribData,PriceSpec;LaunchProductSales,LaunchProductScores,Results;TeamDecisionsMk1,TeamMk1.
Logique serveur
GameSpecificUtilsMk1 gère sauvegarde/suppression de produit, lancement, rentabilité, conversion cash↔quantité, sélection de segment, randomisation des décisions bots, événements et création/lancement de produit.
Résultats
flowchart LR
P[Produits] --> G[gatherProducts]
SEG[Segments] --> G
G --> AVG[Market averages]
AVG --> PS[Product score]
AVG --> CS[Communication score]
AVG --> DS[Distribution score]
PS --> SALES[Sales]
CS --> SALES
DS --> SALES
SALES --> TEAM[Cash / Results]
GenerateResultsMk1 calcule les moyennes de marché, scores produit/communication/distribution, ventes et résultats annuels.
Bots
BotsMk1 utilise les heuristiques du moteur MK1 pour générer les décisions d'équipes artificielles en async.
UI
Les écrans spécifiques couvrent portfolio, nouveaux produits, launched products, market, communications, media, shops, calculator, lancement et rapports. Les tutoriels MK1 couvrent notamment création du premier produit et premier lancement.
Ajouter une feature MK1 de A à Z
Entrées de décision principales
mk1_custom_command.js route :
save_product;save_launched_product;delete_product;delete_launched_product;delete_calc_product;- puis les commandes By Year communes.
Pipeline de résultats
flowchart LR
DEC[Products + launches] --> G[gatherProducts]
G --> AVG[computeAverages]
AVG --> PS[getProductScore]
AVG --> CS[getCommScore]
AVG --> DS[getDistScore]
PS --> SCORE[computeScores]
CS --> SCORE
DS --> SCORE
SCORE --> SALES[generateSales]
SALES --> SAVE[saveResults / finalizeTeamsData]
Ajouter un nouveau critère marketing
- Ajouter la static data (segment/product spec/media/distrib/etc.).
- Ajouter la propriété dans
ProductouLaunchedProductselon qu'elle est produit ou lancement. - Ajouter le champ UI dans
NewProductControl/LaunchProductControlou un nouveau contrôle. - Transmettre la valeur via
save_productousave_launched_product. - Ajouter le coût dans
getProductCost()/getLaunchProductCost()si applicable. - Ajouter la valeur au score pertinent : produit, comm ou distribution.
- Vérifier averages et normalisation si le score est relatif aux concurrents.
- Vérifier
generateSales(). - Ajouter le report Unity et éventuellement ActPerf/ranking.
- Adapter
BotsMk1.takeDecisions()si le critère est requis.
Calculateur vs décision réelle
MK1 contient des produits de calculateur en plus des produits réellement sauvegardés/lancés. Une nouvelle propriété doit être cohérente dans les deux chemins si le calculateur est censé la simuler. Auditer saveProduct, saveLaunchedProduct et deleteCalcProduct.
Debug ventes MK1
- produit/lancement persisted;
- segment cible;
- averages marché;
- product score;
- comm score;
- distribution score;
- sales generated;
- cash/result saved;
- report Unity.
Notifications et présence
Notifications envoyées depuis Unity
Le Game envoie les notifications au backend Web. NotificationManager.SaveNotification() lance la coroutine BackEndBase.SaveNotification(), qui effectue POST /saveNotification avec userUid, notificationType, isImportant et extraData.
userUid est le profil jeu du destinataire (products_code.game_oid); extraData.simUid identifie la simulation. NotificationController, protégé par AuthGuardUser, délègue à GameNotificationService.notifyGameProfile().
Le serveur vérifie le format, exclut les types réservés au serveur et contrôle que l'expéditeur et le destinataire appartiennent à la simulation BrainCloud. Le profil jeu destinataire est ensuite converti en compte Web pour NotificationService.notify().
La notification est enregistrée dans l'inbox Redis avant son annonce aux connexions du compte. Le push est tenté ensuite; un échec du push n'annule pas l'entrée enregistrée. L'inbox possède une limite de taille et une durée de rétention configurées dans NOTIFICATION_CONFIG.
Le Web charge GET /notifications, marque une entrée comme lue via POST /notifications/:notificationUid/read et la supprime via DELETE /notifications/:notificationUid. La réception live utilise notification.created sur le socket du compte /account.
Voir Notifications et Push pour le système Web, et Panel Web — interface superposée au jeu et intégration Unity pour son affichage au-dessus du jeu.
Trois mécanismes de présence
| Mécanisme | Donnée et usage | Code |
|---|---|---|
| Présence du compte Web | Statut online/away et jeu actif du compte; utilisé par les interfaces Web. | AccountSocketService, socket /account, updateStatus et updateGamePresence. |
| Heartbeat du Game | Session du jeu et derniers heartbeats utilisés par l'UI Unity pour les utilisateurs de la simulation. | User.DoHeartbeat(), endpoint /heartBeat et UsersLastHeartbeats. |
| Présence évaluée By Year | Participation enregistrée par étudiant, activité et année, utilisée pour calculer un score. | _command/set_presence.js et evaluation.presence[actUid][yearId]. |
Le nouveau statut Web ne remplace pas automatiquement les deux usages Game : le code local conserve des appels et des consommateurs distincts.
Présence Web du compte
AccountSocketService suit l'activité du navigateur, le focus et l'inactivité pour émettre online ou away. PageWebglComponent appelle setGamePresence(pid, gameUid) après validation de l'accès au jeu, puis clearGamePresence() à sa destruction.
À la reconnexion du socket, le service réémet le jeu actif conservé en mémoire. Cette information désigne le jeu ouvert; elle ne crée pas d'entrée de présence pédagogique dans BrainCloud.
Heartbeat utilisé par Unity
LoadUserState démarre le heartbeat. User.DoHeartbeat() utilise HEARTBEAT_RATE_SECONDS = 5 : l'intervalle d'attente est de 5 secondes entre les appels. La réponse peut mettre à jour les derniers heartbeats dans le controller de simulation.
SimulationBaseController.IsUserOnline() et ActivityBaseController.IsUserOnline() considèrent un utilisateur online si le dernier heartbeat date de moins de 5 × 12 = 60 secondes. ProfileImageControl consomme cet état pour l'affichage. Le heartbeat a donc encore un consommateur Unity.
Présence évaluée By Year
BackEndBrainCloud.SetPresence() appelle _command/set_presence. Le script traite seulement les utilisateurs avec isTeacher == 0, charge leur simulation et l'activité courante, puis enregistre la présence uniquement si currentYearState == 1.
setYearPresence() écrit true dans evaluation.presence[actUid][yearId] si l'entrée n'existe pas déjà, sauvegarde le document et envoie user_new_presence_eval à l'étudiant et au professeur.
getPresenceScore() compte les années enregistrées entre 0 et yearId, puis divise le cumul de 100 points par le nombre d'années. StudentState et ActivityByYearController appellent encore SetPresence() dans leurs transitions.
startYear() envoie le stub de score à chaque étudiant; il ne marque pas tous les étudiants présents. L'enregistrement effectif relève de set_presence.
Réouverture annuelle
utils_reopen_year.js appelle deleteYearPresence(student, actData.currentYear + 1, actData) après ajustement de l’année courante. Ce flux appartient au cycle des évaluations By Year.
Usages conservés dans le Game
Le heartbeat alimente l'UI Unity, et la présence By Year alimente les évaluations BrainCloud. La présence du compte Web décrit le compte connecté et son jeu actif; ces deux mécanismes Game conservent leurs consommateurs propres.
Sources vérifiées
Game : Assets/Src/Game/Notifications/NotificationManager.cs, Game/BackEnd/BackEndBase.cs, BackEndBrainCloud.cs, Game/User.cs, Game/GameStates/StudentState.cs, LoadUserState.cs, les controllers de simulation/activité et UI/ProfileImageControl.cs. BrainCloud : _command/set_presence.js, _utils/utils_by_year.js, utils_eval.js et _utils/command/utils_reopen_year.js.
Web : uniksim-back-end/src/app.controller.ts, service/game-notification.service.ts, notification.service.ts; frontend : service/account/account-socket.service.ts et Pages/PageWebgl/PageWebgl.component.ts.
Vérification par lecture du code Game et BrainCloud de la branche refactor_notifications, et du code Web de la branche messagerie_fixes, le 30 septembre 2026. Aucun envoi de notification ou changement de présence réel n'a été exécuté pour cette vérification.
MK2 — architecture et logique game-specific
Positionnement
MK2 reprend le cœur marketing de MK1 mais ajoute notamment la logique pays/segments, relance, stock, loyauté et notoriété. Il doit donc être traité comme un moteur distinct plutôt qu'une simple skin MK1.
Static data
Les datasets comprennent product_specs, comms, distrib, medias, events, price_specs, scenario, segments, countries, shops et act_perf_pond, plus constants.
Modèles Unity
Les classes sont proches de MK1 mais ajoutent notamment Country. L'UI ajoute CountryLaunchControl et des variantes MK2 du marché et des rapports.
Logique serveur spécifique
launchTypeCalculation();relaunchSegment();launchCountryCalculation();analyseRentability();redistributeStockAndRefund();getLaunchedProductsOfProduct();- création/lancement de produits avec paramètres pays, supports de communication et stock.
Résultats
flowchart TD
P[Products by country/segment] --> AVG[Market averages]
AVG --> SCORE[Product + Comm + Distrib]
SCORE --> SALES[Sales]
SALES --> LOY[Loyalities]
LOY --> NOT[Notoriety]
NOT --> RES[Team Results]
SALES --> STOCK[Stock / redistribution]
STOCK --> RES
GenerateResultsMk2 ajoute notamment computeLoyalities() et persiste la notoriété en plus des ventes et résultats.
Bots
BotsMk2 génère les décisions marketing async en tenant compte des règles MK2.
Debug
Si un résultat diffère de MK1, vérifier d'abord les branches pays/relaunch/loyalty/stock avant de comparer les formules de score communes en apparence.
Ajouter une feature MK2 sans supposer MK1
MK2 partage des concepts avec MK1, mais son moteur n'est pas un alias. Il possède son propre GameSpecificUtilsMk2, son propre générateur de résultats et ses propres modèles Unity.
Fonctions spécifiques MK2 à connaître
launchTypeCalculation();relaunchSegment();launchCountryCalculation();analyseRentability();createProduct()/launchProduct();getLaunchedProductsOfProduct();redistributeStockAndRefund();computeLoyalities();- notoriété persistée via
saveResults().
Custom commands
Les noms d'entrée sont similaires à MK1 : save_product, save_launched_product, delete product/launch/calc. Ils appellent toutefois utilsMk2, dont les structures incluent notamment country/stock/relaunch.
Ajouter un nouveau comportement de lancement
- Définir si le changement concerne produit, lancement initial, relaunch, country ou stock.
- Ajouter la donnée dans
Product/LaunchedProduct/Countrycôté Unity. - Ajouter la saisie UI, notamment
CountryLaunchControlsi géographique. - Ajouter la valeur aux save commands.
- Adapter
launchTypeCalculation/relaunchSegment/launchCountryCalculationselon le cas. - Vérifier les refunds/redistribution de stock lors d'une modification/suppression.
- Ajouter l'effet aux scores/ventes.
- Vérifier loyalty + notoriety sur l'année suivante.
- Adapter
BotsMk2.
Stock MK2
Le stock de lancement MK2 est distinct du système d'inventaire Accounting. Ne pas réutiliser mentalement les règles FIFO/periodic stock de By Module : ici il s'agit de quantités marketing liées aux lancements et à la redistribution/refund.
Debug différence MK1/MK2
Quand MK2 donne un résultat différent d'une intuition basée sur MK1, vérifier d'abord country, relaunch, stock, loyalities et notoriety. Ensuite seulement comparer product/comm/dist score.
Test de régression MK2
- premier lancement sans stock historique;
- relaunch même segment;
- lancement nouveau pays;
- modification avec refund de stock;
- loyalty année suivante;
- notoriety sauvegardée/rechargée;
- bot + génération async.