{"results":{"result":{"added-files":{"code-health":10.0,"old-code-health":0.0,"files":[{"file":"Core/src/core/database/game/migrations/028-add-karma.ts","loc":13,"code-health":10.0},{"file":"Core/src/core/database/logs/migrations/013-add-karma.ts","loc":27,"code-health":10.0},{"file":"Core/src/core/database/logs/models/LogsPlayersKarma.ts","loc":15,"code-health":10.0},{"file":"Core/src/commands/admin/testCommands/Player/PlayerKarmaTestCommand.ts","loc":12,"code-health":10.0},{"file":"Core/src/commands/admin/testCommands/Player/SetKarmaTestCommand.ts","loc":20,"code-health":10.0}]},"external-review-url":"https://github.com/Crownicles/Crownicles/pull/3329","old-code-health":7.670610415313466,"modified-files":{"code-health":7.68821886525491,"old-code-health":7.670610415313466,"files":[{"file":"Core/src/data/DataController.ts","loc":76,"old-loc":76,"code-health":10.0,"old-code-health":10.0},{"file":"Core/src/data/events/PossibilityOutcome.ts","loc":376,"old-loc":243,"code-health":8.502934016329126,"old-code-health":8.545379580978913},{"file":"Core/src/commands/player/ReportCommand.ts","loc":547,"old-loc":544,"code-health":7.650635066647554,"old-code-health":7.650635066647554},{"file":"Core/src/core/database/game/models/Player.ts","loc":1182,"old-loc":1168,"code-health":6.329314542870192,"old-code-health":6.3428605361519885},{"file":"Core/src/core/database/logs/LogsDatabase.ts","loc":845,"old-loc":841,"code-health":6.64098252627818,"old-code-health":6.64098252627818},{"file":"Core/src/commands/guild/GuildShopCommand.ts","loc":167,"old-loc":167,"code-health":10.0,"old-code-health":10.0},{"file":"Core/src/commands/mission/MissionShopCommand.ts","loc":283,"old-loc":283,"code-health":10.0,"old-code-health":10.0},{"file":"Core/src/commands/player/ShopCommand.ts","loc":296,"old-loc":296,"code-health":10.0,"old-code-health":10.0},{"file":"Core/src/core/utils/CommandUtils.ts","loc":237,"old-loc":231,"code-health":8.526780179894821,"old-code-health":8.526780179894821},{"file":"Lib/src/types/Effect.ts","loc":46,"old-loc":44,"code-health":10.0,"old-code-health":10.0}]},"removed-files":{"code-health":0.0,"old-code-health":0.0,"files":[]},"external-review-id":"3329","analysis-time":"2025-09-24T22:18:16Z","negative-impact-count":2,"suppressions":{"number-of-types":0,"number-of-files-touched":0,"findings":[]},"affected-hotspots":2,"commits":["7d0cbf0df12eeaf32347d8bc0012929137f0731e","02ff49c6b60c265b2c60d572373cd6715680d8df","c45bbc96cbc2da34aebbed36d4a115b4a0a30ed7","a7f624a0d335d2ef20233a5aca89d6e6c777b10f","50b3f584c668597b36e4d469bb20f7b3c31b4bee","c4cde89fd58fc6c62b1f9c55e998961d2699f1fb","5553990278c26225ec08fbe79b40875dd1b386ab","b8501acf0ed486dd809fc10168950fe2599c526b","c46d82d89c94c9a803cb2f0ce832cff858712244","ee7186dcff93fab60a053b248b3cceb0d93df1ad","1dfc7921f2e56e0f65afdeb569d9a88144043b17","9dfa6cc9bede75361da59e40a36c5199c3cdcdf6","1a579a0e9fb8c79c4138fd01172b83c0f1bc06ea","b800b5c1a1e56b29f15aa23743dc038f193a6313","d4c97f72e2c44afec527d5c836ad5488e31b0a15","9808f398fc4034cd2e0be6265dc918a9ffb6cb87","bf260f2a5e8318d24cf16de0781409a33c18aedc","51f84e8e5e5c4b24eb75a385538d464285705e74","901b4eaa26f318688bf7eb906b111e114dafae59","9cb0f004777f2b900ce46a00356a1b858b8b18e3","1bc6f209fe535572287e5e1bd2da8768152d272d","d3cb0a99a700985e4e62a9344bf828a049176c37","2f9da2cdbd0a7896e14eff89416e30fb58a38ee7","84bef70a7dbbb352dbb55e7529baf46d8fa049d3","19ad97fe10221542c921dcfd201562a39064773e","2b89f933f5dcca844fcd416193ee3901e81197a4","81906cfba55a48c7f74925def53bd1ea4eb17e03","055cc86a93ad196f8fe2a2ad14191d4447a53ed7","8d4c72e5a2cf4f9109af4352b37bc774aa397ccc","c8de71c36ca88b7a5ff0742275362a1e751898e1","83b51066fcf4b9eb227b376d6d16274bfa4d2cdd","f9267d0ccfed1d3c7e106b796758ea4331075fbe","e4499949696afb8e3f639825bf74bc1ea3bdb166","19babe56958ca72235648407ab8f38302dbe2d0c","8608105d2931eb1ce926ea042182aeeec7233020","830762fa6e7d8c21425f5b1e87c14e47cff64326","1963eb0ad323581f07a7d9e84f288aba3c528f9d","6eb550eca93e53d9f76a194bfcbf0e7d0e6b70a0","b3d5527466ef97747b3c4b40fe240a803a518411","238ea97b02c502cfdd911e23290c028780b7c832","d7ffbd9e7f28e8cf35a0df3b2bd8f3dcc10976fe","fd2800bbabd5e0dd1d5b85998fcd4f4aee1d2413","55bbe368ab32fce2a7eb450a00003ad29ac5dcc9","91dafbfc98dbfcb14a69772f4510e92b05f4dc59","e4ca86a9727f08ad3e57eca86c93922f2dae5ec8","9d029ba68f4a58314df980634ac00ffb5f7da02a","4236ac4706d550d7f8e802cae38692fbf55d2a1c","379d9613cb856fbb6b8aae3d98427b2c19ecdf81","beb3d7aad3b3629b3be7013b9382b4a3b0386057","11a34e2d6ab4d7ff1845ef9559812924d3aaa5ed","a8a617747cbe29480401c2a4b954ac6a0aad45db","ed7845f366dc7ff0373e7f75e5abe00e460361fc","0d8fbd9d84c52ba59914527492331590e2310dd9","bb20f50958c1bc07044235e772e0fa4f7a98496b","2ed5a05916d51a140a04876666f5e230de549aa9","fad026f785b671e4bfd260cef08a3d6f607585b6","0126671d78b62fb33bfa02e1fee7ae2d40271417","1a1df318b94cdd98309dd148715b66f57424db1b","8e00dce0960bbc6ec026e166daa409e7a5ff1051"],"is-negative-review":true,"negative-findings":{"number-of-types":2,"number-of-files-touched":2,"findings":[{"method":"isOutcomeValidForPlayer","why-it-occurs":"A Complex Method has a high cyclomatic complexity. The recommended threshold for the TypeScript language is a cyclomatic complexity lower than 9.","name":"Complex Method","file":"Core/src/data/events/PossibilityOutcome.ts","refactoring-examples":[{"architectural-component-id":null,"author-name":"BastLast","training-data":{"loc-added":"1","loc-deleted":"3","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"lastaccount@fastmail.com","commit-full-message":"","commit-date":"2025-03-02T19:39:27Z","current-rev":"7055587f9","filename":"Crownicles/Discord/src/commands/player/FightCommand.ts","previous-rev":"40790914d","commit-title":"bugged fight error management #2276","language":"TypeScript","id":"9c3ee393c37ccf4922ca68a2ec72fb0fbe55953a","model-score":0.96,"author-id":null,"project-id":49537,"delta-file-score":0.31179166,"diff":"diff --git a/Discord/src/commands/player/FightCommand.ts b/Discord/src/commands/player/FightCommand.ts\nindex bff9399e1..c131de60f 100644\n--- a/Discord/src/commands/player/FightCommand.ts\n+++ b/Discord/src/commands/player/FightCommand.ts\n@@ -249,15 +249,13 @@ export async function handleEndOfFight(context: PacketContext, packet: CommandFi\n \tconst embed = new DraftBotEmbed()\n-\t\t.setTitle(packet.fightBugged\n-\t\t\t? i18n.t(\"commands:fight.end.bugged\", {lng: interaction.userLanguage})\n-\t\t\t: isDraw\n-\t\t\t\t? i18n.t(\"commands:fight.end.draw\", {\n-\t\t\t\t\tlng: interaction.userLanguage,\n-\t\t\t\t\tplayer1: winnerName,\n-\t\t\t\t\tplayer2: looserName\n-\t\t\t\t})\n-\t\t\t\t: i18n.t(\"commands:fight.end.win\", {\n-\t\t\t\t\tlng: interaction.userLanguage,\n-\t\t\t\t\twinner: winnerName,\n-\t\t\t\t\tloser: looserName\n-\t\t\t\t}))\n+\t\t.setTitle(isDraw\n+\t\t\t? i18n.t(\"commands:fight.end.draw\", {\n+\t\t\t\tlng: interaction.userLanguage,\n+\t\t\t\tplayer1: winnerName,\n+\t\t\t\tplayer2: looserName\n+\t\t\t})\n+\t\t\t: i18n.t(\"commands:fight.end.win\", {\n+\t\t\t\tlng: interaction.userLanguage,\n+\t\t\t\twinner: winnerName,\n+\t\t\t\tloser: looserName\n+\t\t\t}))\n \t\t.setDescription(description);\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Feiryn","training-data":{"loc-added":"2","loc-deleted":"5","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"98115666+feiryn@users.noreply.github.com","commit-full-message":"","commit-date":"2025-03-18T19:44:38Z","current-rev":"d946dab8d","filename":"Crownicles/Discord/src/commands/pet/PetTransferCommand.ts","previous-rev":"340f9754d","commit-title":"Add location requirement for commands + some fixes #2805","language":"TypeScript","id":"ca6a9d6fbb33bd3ff58e0476cf46d34bfa244d3c","model-score":0.96,"author-id":null,"project-id":49537,"delta-file-score":0.31179166,"diff":"diff --git a/Discord/src/commands/pet/PetTransferCommand.ts b/Discord/src/commands/pet/PetTransferCommand.ts\nindex 7bbcae17f..84006970e 100644\n--- a/Discord/src/commands/pet/PetTransferCommand.ts\n+++ b/Discord/src/commands/pet/PetTransferCommand.ts\n@@ -37,2 +37,3 @@ import {DiscordCollectorUtils} from \"../../utils/DiscordCollectorUtils\";\n import {EmoteUtils} from \"../../utils/EmoteUtils\";\n+import {MessagesUtils} from \"../../utils/MessagesUtils\";\n \n@@ -44,7 +45,3 @@ async function getPacket(interaction: DraftbotInteraction): Promise<CommandPetTr\n export async function handlePetTransferSuccess(context: PacketContext, packet: CommandPetTransferSuccessPacket): Promise<void> {\n-\tconst interaction: DraftbotInteraction = (context.discord!.stringSelectMenuInteraction\n-\t\t? DraftbotInteraction.cast(DiscordCache.getStringSelectMenuInteraction(context.discord!.stringSelectMenuInteraction)!)\n-\t\t: context.discord!.buttonInteraction\n-\t\t\t? DraftbotInteraction.cast(DiscordCache.getButtonInteraction(context.discord!.buttonInteraction)!)\n-\t\t\t: DiscordCache.getInteraction(context.discord!.interaction))!;\n+\tconst interaction: DraftbotInteraction = MessagesUtils.getCurrentInteraction(context);\n \n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"BastLast","training-data":{"loc-added":"1","loc-deleted":"2","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"lastaccount@fastmail.com","commit-full-message":"","commit-date":"2025-05-04T22:54:51Z","current-rev":"ac7f0d9e3","filename":"Crownicles/Core/src/core/fights/actions/interfaces/pets/rainbowPower.ts","previous-rev":"0737ee28d","commit-title":"simplify magic action check in rainbowPower","language":"TypeScript","id":"1b75aba18d43e840406de1289b4dbb5c9bb6d553","model-score":0.92,"author-id":null,"project-id":49537,"delta-file-score":0.7593437,"diff":"diff --git a/Core/src/core/fights/actions/interfaces/pets/rainbowPower.ts b/Core/src/core/fights/actions/interfaces/pets/rainbowPower.ts\nindex 06ec489b6..d312ab2a1 100644\n--- a/Core/src/core/fights/actions/interfaces/pets/rainbowPower.ts\n+++ b/Core/src/core/fights/actions/interfaces/pets/rainbowPower.ts\n@@ -16,4 +16,3 @@ const use: PetAssistanceFunc = (fighter, opponent, turn, _fightController): Prom\n \n-\tif ((!opponent.getLastFightActionUsed() || opponent.getLastFightActionUsed()?.type !== FightActionType.MAGIC)\n-\t\t&& (!fighter.getLastFightActionUsed() || fighter.getLastFightActionUsed()?.type !== FightActionType.MAGIC)) {\n+\tif (!(opponent.getLastFightActionUsed()?.type === FightActionType.MAGIC || fighter.getLastFightActionUsed()?.type === FightActionType.MAGIC)) {\n \t\treturn null;\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"BastLast","training-data":{"loc-added":"8","loc-deleted":"13","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"lastaccount@fastmail.com","commit-full-message":"","commit-date":"2025-03-18T20:18:10Z","current-rev":"5dc4baaba","filename":"Crownicles/Core/src/core/fights/aiClassBehaviors/HorseRiderFightBehavior.ts","previous-rev":"2107e0eea","commit-title":"revert of last commit for a better solution #2276","language":"TypeScript","id":"f0f4440ca74b1ab2fc74d5f145112a86d337280b","model-score":0.67,"author-id":null,"project-id":49537,"delta-file-score":0.31179166,"diff":"diff --git a/Core/src/core/fights/aiClassBehaviors/HorseRiderFightBehavior.ts b/Core/src/core/fights/aiClassBehaviors/HorseRiderFightBehavior.ts\nindex ecd50ddbf..16631b0f2 100644\n--- a/Core/src/core/fights/aiClassBehaviors/HorseRiderFightBehavior.ts\n+++ b/Core/src/core/fights/aiClassBehaviors/HorseRiderFightBehavior.ts\n@@ -1,6 +1,6 @@\n-import {ClassBehavior} from \"../AiBehaviorManager\";\n-import {AiPlayerFighter} from \"../fighter/AiPlayerFighter\";\n-import {FightView} from \"../FightView\";\n-import {FightAction, FightActionDataController} from \"../../../data/FightAction\";\n-import {FightConstants} from \"../../../../../Lib/src/constants/FightConstants\";\n+import { ClassBehavior } from \"../AiBehaviorManager\";\n+import { AiPlayerFighter } from \"../fighter/AiPlayerFighter\";\n+import { FightView } from \"../FightView\";\n+import { FightAction, FightActionDataController } from \"../../../data/FightAction\";\n+import { FightConstants } from \"../../../../../Lib/src/constants/FightConstants\";\n import {simpleOrQuickAttack} from \"./EsquireFightBehavior\";\n@@ -8,4 +8,3 @@ import {simpleOrQuickAttack} from \"./EsquireFightBehavior\";\n class HorseRiderFightBehavior implements ClassBehavior {\n-\t// WeakMap to store fighter-specific rest counts\n-\tprivate restCountMap = new WeakMap<AiPlayerFighter, number>();\n+\tprivate restCount = 0; // Track how many times we've rested\n \n@@ -15,9 +14,5 @@ class HorseRiderFightBehavior implements ClassBehavior {\n \n-\t\t// Get fighter-specific rest count or initialize to 0 if not set\n-\t\tlet restCount = this.restCountMap.get(me) || 0;\n-\n \t\t// Initialize defense tracking on first round\n \t\tif (currentRound <= 1) {\n-\t\t\trestCount = 0; // Reset rest counter at the beginning of a fight\n-\t\t\tthis.restCountMap.set(me, restCount);\n+\t\t\tthis.restCount = 0; // Reset rest counter at the beginning of a fight\n \t\t}\n@@ -27,4 +22,3 @@ class HorseRiderFightBehavior implements ClassBehavior {\n \t\tif (me.getEnergy() < 125 && opponent.getEnergy() > 400) {\n-\t\t\trestCount++;\n-\t\t\tthis.restCountMap.set(me, restCount);\n+\t\t\tthis.restCount++;\n \t\t\treturn FightActionDataController.instance.getById(FightConstants.FIGHT_ACTIONS.PLAYER.RESTING);\n@@ -33,5 +27,4 @@ class HorseRiderFightBehavior implements ClassBehavior {\n \t\t// REST WHEN NEEDED: Not enough breath for actions (only if we haven't rested 4 times)\n-\t\tif (me.getBreath() < 2 && restCount < 4) {\n-\t\t\trestCount++;\n-\t\t\tthis.restCountMap.set(me, restCount);\n+\t\tif (me.getBreath() < 2 && this.restCount < 4) {\n+\t\t\tthis.restCount++;\n \t\t\treturn FightActionDataController.instance.getById(FightConstants.FIGHT_ACTIONS.PLAYER.RESTING);\n@@ -44,4 +37,6 @@ class HorseRiderFightBehavior implements ClassBehavior {\n \n-\t\treturn simpleOrQuickAttack(me, opponent, restCount);\n+\t\treturn simpleOrQuickAttack(me, opponent);\n \t}\n+\n+\n }\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Romain NEGRO","training-data":{"loc-added":"22","loc-deleted":"9","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.44","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"romrom22222@hotmail.fr","commit-full-message":"","commit-date":"2025-04-28T13:38:31Z","current-rev":"9b9b617c3","filename":"Crownicles/Discord/src/utils/DiscordCollectorUtils.ts","previous-rev":"1ca279a65","commit-title":"eslint fixes","language":"TypeScript","id":"87c9d352a381295f07ae10a768ac7e193865c759","model-score":0.62,"author-id":null,"project-id":49537,"delta-file-score":0.5950201,"diff":"diff --git a/Discord/src/utils/DiscordCollectorUtils.ts b/Discord/src/utils/DiscordCollectorUtils.ts\nindex 56c02d48c..cd84e49dd 100644\n--- a/Discord/src/utils/DiscordCollectorUtils.ts\n+++ b/Discord/src/utils/DiscordCollectorUtils.ts\n@@ -1,2 +1,4 @@\n-import {makePacket, PacketContext} from \"../../../Lib/src/packets/DraftBotPacket\";\n+import {\n+\tmakePacket, PacketContext\n+} from \"../../../Lib/src/packets/DraftBotPacket\";\n import {\n@@ -7,18 +9,24 @@ import {\n } from \"../../../Lib/src/packets/interaction/ReactionCollectorPacket\";\n-import {DiscordCache} from \"../bot/DiscordCache\";\n-import {KeycloakUser} from \"../../../Lib/src/keycloak/KeycloakUser\";\n-import {ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, InteractionCallbackResponse, InteractionResponse, Message, MessageComponentInteraction, parseEmoji} from \"discord.js\";\n-import {DraftBotIcons} from \"../../../Lib/src/DraftBotIcons\";\n-import {DraftBotEmbed} from \"../messages/DraftBotEmbed\";\n-import {DraftbotInteraction} from \"../messages/DraftbotInteraction\";\n-import {sendInteractionNotForYou, SendManner} from \"./ErrorUtils\";\n-import {PacketUtils} from \"./PacketUtils\";\n-import {keycloakConfig, shardId} from \"../bot/DraftBotShard.js\";\n-import {KeycloakUtils} from \"../../../Lib/src/keycloak/KeycloakUtils.js\";\n-import {ReactionCollectorReturnTypeOrNull} from \"../packetHandlers/handlers/ReactionCollectorHandlers\";\n-import {DiscordMQTT} from \"../bot/DiscordMQTT\";\n-import {RequirementEffectPacket} from \"../../../Lib/src/packets/commands/requirements/RequirementEffectPacket\";\n-import {Effect} from \"../../../Lib/src/types/Effect\";\n-import {PacketConstants} from \"../../../Lib/src/constants/PacketConstants\";\n-import {DiscordConstants} from \"../DiscordConstants\";\n+import { DiscordCache } from \"../bot/DiscordCache\";\n+import { KeycloakUser } from \"../../../Lib/src/keycloak/KeycloakUser\";\n+import {\n+\tActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, InteractionCallbackResponse, InteractionResponse, Message, MessageComponentInteraction, parseEmoji\n+} from \"discord.js\";\n+import { DraftBotIcons } from \"../../../Lib/src/DraftBotIcons\";\n+import { DraftBotEmbed } from \"../messages/DraftBotEmbed\";\n+import { DraftbotInteraction } from \"../messages/DraftbotInteraction\";\n+import {\n+\tsendInteractionNotForYou, SendManner\n+} from \"./ErrorUtils\";\n+import { PacketUtils } from \"./PacketUtils\";\n+import {\n+\tkeycloakConfig, shardId\n+} from \"../bot/DraftBotShard.js\";\n+import { KeycloakUtils } from \"../../../Lib/src/keycloak/KeycloakUtils.js\";\n+import { ReactionCollectorReturnTypeOrNull } from \"../packetHandlers/handlers/ReactionCollectorHandlers\";\n+import { DiscordMQTT } from \"../bot/DiscordMQTT\";\n+import { RequirementEffectPacket } from \"../../../Lib/src/packets/commands/requirements/RequirementEffectPacket\";\n+import { Effect } from \"../../../Lib/src/types/Effect\";\n+import { PacketConstants } from \"../../../Lib/src/constants/PacketConstants\";\n+import { DiscordConstants } from \"../DiscordConstants\";\n \n@@ -27,3 +35,3 @@ type SendingContext = {\n \tcontext: PacketContext;\n-}\n+};\n \n@@ -32,3 +40,3 @@ type SendingValues = {\n \titems: string[];\n-}\n+};\n \n@@ -50,3 +58,3 @@ const MANNER_TO_METHOD = {\n function getSendingManner(interaction: DraftbotInteraction, sendManners: SendManner[]): SendManner {\n-\treturn sendManners.length === 1 ? sendManners[0] : (interaction.replied ? sendManners[1] : sendManners[0]);\n+\treturn sendManners.length === 1 ? sendManners[0] : interaction.replied ? sendManners[1] : sendManners[0];\n }\n@@ -243,3 +251,8 @@ export class DiscordCollectorUtils {\n \t\t}: SendingValues,\n-\t\toptions: { refuse: { can: boolean; reactionIndex?: number }; sendManners?: SendManner[] }\n+\t\toptions: {\n+\t\t\trefuse: {\n+\t\t\t\tcan: boolean; reactionIndex?: number;\n+\t\t\t};\n+\t\t\tsendManners?: SendManner[];\n+\t\t}\n \t): Promise<ReactionCollectorReturnTypeOrNull> {\n@@ -294,7 +307,7 @@ export class DiscordCollectorUtils {\n \t\t// Edit message\n-\t\tconst msg: Message | InteractionResponse | InteractionCallbackResponse | null = await (MANNER_TO_METHOD[sendManner](interaction))({\n+\t\tconst msg: Message | InteractionResponse | InteractionCallbackResponse | null = await MANNER_TO_METHOD[sendManner](interaction)({\n \t\t\tcomponents: rows,\n \t\t\t...embed instanceof DraftBotEmbed\n-\t\t\t\t? {embeds: [embed]}\n-\t\t\t\t: {content: embed}\n+\t\t\t\t? { embeds: [embed] }\n+\t\t\t\t: { content: embed }\n \t\t});\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Romain NEGRO","training-data":{"loc-added":"26","loc-deleted":"14","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.35","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"romrom22222@hotmail.fr","commit-full-message":"","commit-date":"2025-03-17T17:05:39Z","current-rev":"e75135228","filename":"Crownicles/Discord/src/utils/StringUtils.ts","previous-rev":"85d67c5bb","commit-title":"fix codescenes","language":"TypeScript","id":"ed5f2f90994366319df4604b6bae91dff81edd49","model-score":0.57,"author-id":null,"project-id":49537,"delta-file-score":0.41834745,"diff":"diff --git a/Discord/src/utils/StringUtils.ts b/Discord/src/utils/StringUtils.ts\nindex 2afe8b8c9..ecda4ee0f 100644\n--- a/Discord/src/utils/StringUtils.ts\n+++ b/Discord/src/utils/StringUtils.ts\n@@ -34,9 +34,3 @@ export class StringUtils {\n \t\tcase LANGUAGE.FRENCH:\n-\t\t\tif (toOrdinal === 1) {\n-\t\t\t\tif (modifier && modifier === \"f\") {\n-\t\t\t\t\treturn \"1ère\";\n-\t\t\t\t}\n-\t\t\t\treturn \"1er\";\n-\t\t\t}\n-\t\t\treturn `${toOrdinal}ème`;\n+\t\t\treturn this.getFrenchOrdinal(toOrdinal, modifier);\n \t\tcase LANGUAGE.GERMAN:\n@@ -44,9 +38,3 @@ export class StringUtils {\n \t\tcase LANGUAGE.SPANISH:\n-\t\t\tif (toOrdinal === 2) {\n-\t\t\t\treturn `${toOrdinal}do`;\n-\t\t\t}\n-\t\t\tif ([1, 3].includes(toOrdinal)) {\n-\t\t\t\treturn `${toOrdinal}ro`;\n-\t\t\t}\n-\t\t\treturn `${toOrdinal}°`;\n+\t\t\treturn this.getSpanishOrdinal(toOrdinal);\n \t\tcase LANGUAGE.ITALIAN:\n@@ -58,7 +46,31 @@ export class StringUtils {\n \t\tdefault: // English\n-\t\t\tif (toOrdinal % 100 >= 10 && toOrdinal % 100 <= 20) {\n-\t\t\t\treturn `${toOrdinal}th`;\n+\t\t\treturn this.getEnglishOrdinal(toOrdinal);\n+\t\t}\n+\t}\n+\n+\tprivate static getEnglishOrdinal(toOrdinal: number): string {\n+\t\tif (toOrdinal % 100 >= 10 && toOrdinal % 100 <= 20) {\n+\t\t\treturn `${toOrdinal}th`;\n+\t\t}\n+\t\treturn toOrdinal + ([\"th\", \"st\", \"nd\", \"rd\"][toOrdinal % 10] || \"th\");\n+\t}\n+\n+\tprivate static getSpanishOrdinal(toOrdinal: number): string {\n+\t\tif (toOrdinal === 2) {\n+\t\t\treturn `${toOrdinal}do`;\n+\t\t}\n+\t\tif ([1, 3].includes(toOrdinal)) {\n+\t\t\treturn `${toOrdinal}ro`;\n+\t\t}\n+\t\treturn `${toOrdinal}°`;\n+\t}\n+\n+\tprivate static getFrenchOrdinal(toOrdinal: number, modifier?: string): string {\n+\t\tif (toOrdinal === 1) {\n+\t\t\tif (modifier && modifier === \"f\") {\n+\t\t\t\treturn \"1ère\";\n \t\t\t}\n-\t\t\treturn toOrdinal + ([\"th\", \"st\", \"nd\", \"rd\"][toOrdinal % 10] || \"th\");\n+\t\t\treturn \"1er\";\n \t\t}\n+\t\treturn `${toOrdinal}ème`;\n \t}\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Feiryn","training-data":{"loc-added":"15","loc-deleted":"13","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"98115666+feiryn@users.noreply.github.com","commit-full-message":"","commit-date":"2025-04-01T19:12:24Z","current-rev":"8d5b216c2","filename":"Crownicles/Discord/src/messages/DraftbotButtonReactionMessage.ts","previous-rev":"87cb6f643","commit-title":"Fix collectors stop #2894 #2802","language":"TypeScript","id":"7d25465e13c6f560d85cc669f0a6cc0730858843","model-score":0.53,"author-id":null,"project-id":49537,"delta-file-score":0.31179166,"diff":"diff --git a/Discord/src/messages/DraftbotButtonReactionMessage.ts b/Discord/src/messages/DraftbotButtonReactionMessage.ts\nindex 8a5736f8d..07c29990f 100644\n--- a/Discord/src/messages/DraftbotButtonReactionMessage.ts\n+++ b/Discord/src/messages/DraftbotButtonReactionMessage.ts\n@@ -54,2 +54,13 @@ export class DraftbotButtonReactionMessage {\n \n+\tsendReaction(customId: string | null): void {\n+\t\tconst indexes = this._messageOptions.reactions.map((r) => r.customId);\n+\t\tDiscordCollectorUtils.sendReaction(\n+\t\t\tthis._messageOptions.packet,\n+\t\t\tthis._messageOptions.context,\n+\t\t\tthis._messageOptions.context.keycloakId!,\n+\t\t\tnull,\n+\t\t\tindexes.findIndex((r) => r === customId)\n+\t\t);\n+\t}\n+\n \tpublic async send(): Promise<ReactionCollectorReturnType> {\n@@ -74,4 +85,4 @@ export class DraftbotButtonReactionMessage {\n \t\t\t}\n-\t\t\tbuttonCollector.stop();\n-\t\t\treactionCollector?.stop();\n+\n+\t\t\tthis.sendReaction(i.customId);\n \t\t});\n@@ -79,7 +90,6 @@ export class DraftbotButtonReactionMessage {\n \t\treactionCollector?.on(\"collect\", () => {\n-\t\t\treactionCollector?.stop();\n-\t\t\tbuttonCollector.stop();\n+\t\t\tthis.sendReaction(null);\n \t\t});\n \n-\t\tbuttonCollector.on(\"end\", async (collected) => {\n+\t\tbuttonCollector.on(\"end\", async () => {\n \t\t\tawait this._interaction.editReply({\n@@ -88,10 +98,2 @@ export class DraftbotButtonReactionMessage {\n \t\t\t});\n-\t\t\tconst indexes = this._messageOptions.reactions.map((r) => r.customId);\n-\t\t\tDiscordCollectorUtils.sendReaction(\n-\t\t\t\tthis._messageOptions.packet,\n-\t\t\t\tthis._messageOptions.context,\n-\t\t\t\tthis._messageOptions.context.keycloakId!,\n-\t\t\t\tnull,\n-\t\t\t\tindexes.findIndex((r) => r === collected.last()?.customId)\n-\t\t\t);\n \t\t});\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"BastLast","training-data":{"loc-added":"41","loc-deleted":"22","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.314","delta-n-functions":"0","current-file-score":"9.536386775820924"},"author-email":"lastaccount@fastmail.com","commit-full-message":"","commit-date":"2025-03-31T22:46:47Z","current-rev":"dec45d106","filename":"Crownicles/Core/src/commands/player/FightCommand.ts","previous-rev":"15981fb94","commit-title":"fix codescene #2888","language":"TypeScript","id":"7244e571f43053605ee1b4502cc7a23b4909b2ad","model-score":0.36,"author-id":null,"project-id":49537,"delta-file-score":0.6688015,"diff":"diff --git a/Core/src/commands/player/FightCommand.ts b/Core/src/commands/player/FightCommand.ts\nindex d91391d5e..19d15b361 100644\n--- a/Core/src/commands/player/FightCommand.ts\n+++ b/Core/src/commands/player/FightCommand.ts\n@@ -168,2 +168,42 @@ async function calculateScoreReward(fightInitiatorInformation: FightInitiatorInf\n \n+/**\n+ * Update the players' glory and cooldowns after a fight\n+ * @param player1\n+ * @param player2\n+ * @param player1GameResult\n+ * @param player2GameResult\n+ * @param response\n+ * @param fightLogId\n+ */\n+async function updatePlayersEloAndCooldowns(\n+\tplayer1: Player,\n+\tplayer2: Player,\n+\tplayer1GameResult: EloGameResult,\n+\tplayer2GameResult: EloGameResult,\n+\tresponse: DraftBotPacket[],\n+\tfightLogId: number\n+): Promise<void> {\n+\t// Calculate elo\n+\tconst player1KFactor = EloUtils.getKFactor(player1);\n+\tconst player2KFactor = EloUtils.getKFactor(player2);\n+\tconst player1NewRating = EloUtils.calculateNewRating(player1.attackGloryPoints, player2.defenseGloryPoints, player1GameResult, player1KFactor);\n+\tconst player2NewRating = EloUtils.calculateNewRating(player2.defenseGloryPoints, player1.attackGloryPoints, player2GameResult, player2KFactor);\n+\n+\t// Change glory and fightCountdown and save\n+\tawait player1.setGloryPoints(player1NewRating, false, NumberChangeReason.FIGHT, response, fightLogId);\n+\tplayer1.fightCountdown--;\n+\tif (player1.fightCountdown < 0) {\n+\t\tplayer1.fightCountdown = 0;\n+\t}\n+\tawait player2.setGloryPoints(player2NewRating, true, NumberChangeReason.FIGHT, response, fightLogId);\n+\tplayer2.fightCountdown--;\n+\tif (player2.fightCountdown < 0) {\n+\t\tplayer2.fightCountdown = 0;\n+\t}\n+\tawait Promise.all([\n+\t\tplayer1.save(),\n+\t\tplayer2.save()\n+\t]);\n+}\n+\n /**\n@@ -185,3 +225,2 @@ async function fightEndCallback(fight: FightController, response: DraftBotPacket\n \n-\n \t// Player variables\n@@ -219,24 +258,3 @@ async function fightEndCallback(fight: FightController, response: DraftBotPacket\n \tconst player2OldGlory = player2.getGloryPoints();\n-\n-\t// Calculate elo\n-\tconst player1KFactor = EloUtils.getKFactor(player1);\n-\tconst player2KFactor = EloUtils.getKFactor(player2);\n-\tconst player1NewRating = EloUtils.calculateNewRating(player1.attackGloryPoints, player2.defenseGloryPoints, player1GameResult, player1KFactor);\n-\tconst player2NewRating = EloUtils.calculateNewRating(player2.defenseGloryPoints, player1.attackGloryPoints, player2GameResult, player2KFactor);\n-\n-\t// Change glory and fightCountdown and save\n-\tawait player1.setGloryPoints(player1NewRating, false, NumberChangeReason.FIGHT, response, fightLogId);\n-\tplayer1.fightCountdown--;\n-\tif (player1.fightCountdown < 0) {\n-\t\tplayer1.fightCountdown = 0;\n-\t}\n-\tawait player2.setGloryPoints(player2NewRating, true, NumberChangeReason.FIGHT, response, fightLogId);\n-\tplayer2.fightCountdown--;\n-\tif (player2.fightCountdown < 0) {\n-\t\tplayer2.fightCountdown = 0;\n-\t}\n-\tawait Promise.all([\n-\t\tplayer1.save(),\n-\t\tplayer2.save()\n-\t]);\n+\tawait updatePlayersEloAndCooldowns(player1, player2, player1GameResult, player2GameResult, response, fightLogId);\n \n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"BastLast","training-data":{"loc-added":"17","loc-deleted":"43","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"9.387218218812514"},"author-email":"lastaccount@fastmail.com","commit-full-message":"","commit-date":"2025-01-16T18:06:37Z","current-rev":"70f958a73","filename":"Crownicles/Core/src/commands/player/FightCommand.ts","previous-rev":"a7470c88b","commit-title":"improvements matchmaking #2276","language":"TypeScript","id":"3f241e57c24730dd56b6734d43dad1f01aa639e4","model-score":0.19,"author-id":null,"project-id":49537,"delta-file-score":0.14656192,"diff":"diff --git a/Core/src/commands/player/FightCommand.ts b/Core/src/commands/player/FightCommand.ts\nindex 1d7d10cd1..9aba657ba 100644\n--- a/Core/src/commands/player/FightCommand.ts\n+++ b/Core/src/commands/player/FightCommand.ts\n@@ -13,3 +13,3 @@ import {BlockingConstants} from \"../../../../Lib/src/constants/BlockingConstants\n import {InventorySlots} from \"../../core/database/game/models/InventorySlot\";\n-import {LogsReadRequests} from \"../../core/database/logs/LogsReadRequests\";\n+import {LogsReadRequests, RankedFightResult} from \"../../core/database/logs/LogsReadRequests\";\n \n@@ -56,2 +56,10 @@ async function getPlayerStats(player: Player): Promise<PlayerStats> {\n \n+/**\n+ * Check if a BO3 is already finished (3 games played or 2 wins)\n+ * @param bo3\n+ */\n+function bo3isAlreadyFinished(bo3: RankedFightResult) {\n+\treturn bo3.won > 1 || bo3.lost > 1 || bo3.draw + bo3.won + bo3.lost >= 3;\n+}\n+\n /**\n@@ -63,4 +71,4 @@ async function getPlayerStats(player: Player): Promise<PlayerStats> {\n async function findOpponent(player: Player, offset: number): Promise<Player | null> {\n-\tconst closestPlayers = await Players.findByDefenseGlory(\n-\t\tplayer.attackGloryPoints,\n+\tconst validOpponents = await Players.findPotentialOpponent(\n+\t\tplayer,\n \t\tFightConstants.PLAYER_PER_OPPONENT_SEARCH,\n@@ -69,72 +77,38 @@ async function findOpponent(player: Player, offset: number): Promise<Player | nu\n \n-\t// Remove the current player from the list (cannot fight itself)\n-\tconst opponentCandidates = closestPlayers.filter(\n-\t\t(closestPlayer) => closestPlayer.id !== player.id\n-\t);\n-\n-\t// Filter opponents based on level and ELO gap\n-\tconst validOpponents = opponentCandidates.filter(\n-\t\t(opponent) =>\n-\t\t\topponent.level >= FightConstants.REQUIRED_LEVEL &&\n-\t\t\tMath.abs(player.defenseGloryPoints - opponent.attackGloryPoints) <= FightConstants.ELO.MAX_ELO_GAP\n-\t);\n-\n-\tif (validOpponents.length === 0) {\n-\t\t// No valid opponents found at this offset\n-\t\tif (offset > FightConstants.MAX_OFFSET_FOR_OPPONENT_SEARCH) {\n-\t\t\treturn null;\n-\t\t}\n-\t\t// Recursively search with increased offset\n-\t\treturn findOpponent(player, offset + 1);\n-\t}\n-\n-\t// Shuffle array\n-\tvalidOpponents.sort(() => Math.random() - 0.5);\n-\n-\t// Get the keycloak IDs of valid opponents\n-\tconst opponentKeycloakIds = validOpponents.map((opponent) => opponent.keycloakId);\n-\n-\t// Check if these players have been defenders recently\n-\tconst haveBeenDefenderRecently = await LogsReadRequests.hasBeenADefenderInRankedFightSinceMinute(\n-\t\topponentKeycloakIds,\n-\t\tFightConstants.DEFENDER_COOLDOWN_MINUTES\n-\t);\n-\n-\t// Filter out opponents who have been defenders recently\n-\tconst opponentsNotOnCooldown = validOpponents.filter(\n-\t\t(opponent) => !haveBeenDefenderRecently[opponent.keycloakId]\n-\t);\n+\tif (validOpponents.length !== 0) {\n+\t\t// Shuffle array\n+\t\tvalidOpponents.sort(() => Math.random() - 0.5);\n \n-\tif (opponentsNotOnCooldown.length === 0) {\n-\t\t// No valid opponents found after defender cooldown filter\n-\t\tif (offset > FightConstants.MAX_OFFSET_FOR_OPPONENT_SEARCH) {\n-\t\t\treturn null;\n-\t\t}\n-\t\t// Recursively search with increased offset\n-\t\treturn findOpponent(player, offset + 1);\n-\t}\n-\n-\t// Now get the keycloak IDs of the remaining opponents\n-\tconst remainingOpponentKeycloakIds = opponentsNotOnCooldown.map((opponent) => opponent.keycloakId);\n-\n-\t// Fetch the fight results against all remaining valid opponents in one call\n-\tconst bo3Map = await LogsReadRequests.getRankedFightsThisWeek(\n-\t\tplayer.keycloakId,\n-\t\tremainingOpponentKeycloakIds\n-\t);\n+\t\t// Check if these players have been defenders recently\n+\t\tconst haveBeenDefenderRecently = await LogsReadRequests.hasBeenADefenderInRankedFightSinceMinute(\n+\t\t\tvalidOpponents.map((opponent) => opponent.keycloakId),\n+\t\t\tFightConstants.DEFENDER_COOLDOWN_MINUTES\n+\t\t);\n \n-\t// Now iterate over opponentsNotOnCooldown and find the first one that meets all conditions\n-\tfor (const opponent of opponentsNotOnCooldown) {\n-\t\t// Get the fight result for this opponent from the map\n-\t\tconst bo3 = bo3Map.get(opponent.keycloakId) || { won: 0, lost: 0, draw: 0 };\n+\t\t// Filter out opponents who have been defenders recently\n+\t\tconst opponentsNotOnCooldown = validOpponents.filter(\n+\t\t\t(opponent) => !haveBeenDefenderRecently[opponent.keycloakId]\n+\t\t);\n \n-\t\tif (bo3.won > 1 || bo3.lost > 1 || bo3.draw + bo3.won + bo3.lost >= 3) {\n-\t\t\t// Max fights already played with this opponent\n-\t\t\tcontinue;\n+\t\tif (opponentsNotOnCooldown.length !== 0) {\n+\t\t\t// Now get the keycloak IDs of the remaining opponents\n+\t\t\tconst remainingOpponentKeycloakIds = opponentsNotOnCooldown.map((opponent) => opponent.keycloakId);\n+\n+\t\t\t// Fetch the fight results against all remaining valid opponents in one call\n+\t\t\tconst bo3Map = await LogsReadRequests.getRankedFightsThisWeek(\n+\t\t\t\tplayer.keycloakId,\n+\t\t\t\tremainingOpponentKeycloakIds\n+\t\t\t);\n+\n+\t\t\t// Now iterate over opponentsNotOnCooldown and find the first one that meets all conditions\n+\t\t\tfor (const opponent of opponentsNotOnCooldown) {\n+\t\t\t\t// Get the fight result for this opponent from the map\n+\t\t\t\tif (bo3isAlreadyFinished( bo3Map.get(opponent.keycloakId) || {won: 0, lost: 0, draw: 0})) {\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t\t// Found a valid opponent\n+\t\t\t\treturn opponent;\n+\t\t\t}\n \t\t}\n-\n-\t\t// Found a valid opponent\n-\t\treturn opponent;\n \t}\n-\n \t// No valid opponents found in this batch, recursively search with increased offset\n@@ -143,3 +117,2 @@ async function findOpponent(player: Player, offset: number): Promise<Player | nu\n \t}\n-\n \treturn findOpponent(player, offset + 1);\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Romain NEGRO","training-data":{"loc-added":"69","loc-deleted":"62","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.926","delta-n-functions":"0","current-file-score":"9.6882083290695"},"author-email":"romrom22222@hotmail.fr","commit-full-message":"","commit-date":"2025-04-09T08:49:56Z","current-rev":"7d1d0895a","filename":"Crownicles/Discord/src/messages/DraftbotHistoryCachedMessage.ts","previous-rev":"c77155225","commit-title":"large method refactoring","language":"TypeScript","id":"bb7b801e45d9722b6b5d8f26d611cd92490c8b1d","model-score":0.15,"author-id":null,"project-id":49537,"delta-file-score":0.7122546,"diff":"diff --git a/Discord/src/messages/DraftbotHistoryCachedMessage.ts b/Discord/src/messages/DraftbotHistoryCachedMessage.ts\nindex d25c52ec3..669b183e3 100644\n--- a/Discord/src/messages/DraftbotHistoryCachedMessage.ts\n+++ b/Discord/src/messages/DraftbotHistoryCachedMessage.ts\n@@ -19,2 +19,3 @@ import { StringConstants } from \"../../../Lib/src/constants/StringConstants\";\n import { DisplayUtils } from \"../utils/DisplayUtils\";\n+import { DraftbotInteraction } from \"./DraftbotInteraction\";\n \n@@ -34,3 +35,3 @@ export class DraftbotHistoryCachedMessage extends DraftbotCachedMessage<CommandF\n \n-\tupdateMessage = async (packet: CommandFightHistoryItemPacket, context: PacketContext): Promise<undefined> => {\n+\tupdateMessage = async (packet: CommandFightHistoryItemPacket, context: PacketContext): Promise<void> => {\n \t\tconst interaction = DiscordCache.getInteraction(context.discord!.interaction)!;\n@@ -46,4 +47,50 @@ export class DraftbotHistoryCachedMessage extends DraftbotCachedMessage<CommandF\n \t\t\tfighter\n-\t\t});\n-\t\tlet attackName = \"\"; // Name of the attack, used to display the attack name in the message\n+\t\t}) + this.manageMainMessage(packet, interaction);\n+\n+\t\tif (packet.fightActionEffectReceived) {\n+\t\t\tnewLine += this.manageReceivedEffects(packet, interaction);\n+\t\t}\n+\n+\t\t// Then we need to display the side effects of the attack or alteration if there are any\n+\t\tif (packet.fightActionEffectDealt) {\n+\t\t\tnewLine += this.manageSideEffects(packet, interaction);\n+\t\t}\n+\n+\t\tif (this.historyContent.length + newLine.length <= FightConstants.MAX_HISTORY_LENGTH) {\n+\t\t\tthis.historyContent = `${this.historyContent}\\n${newLine}`;\n+\t\t\tawait this.post({ content: this.historyContent });\n+\t\t\treturn;\n+\t\t}\n+\t\tthis.storedMessage = undefined;\n+\t\tthis.historyContent = newLine;\n+\t\tawait this.post({ content: this.historyContent });\n+\t\tDraftbotCachedMessages.markAsReupload(DraftbotCachedMessages.getOrCreate(this.originalMessageId, DraftbotFightStatusCachedMessage));\n+\t\tDraftbotCachedMessages.markAsReupload(DraftbotCachedMessages.getOrCreate(this.originalMessageId, DraftbotActionChooseCachedMessage));\n+\t};\n+\n+\tprivate manageSideEffects(packet: CommandFightHistoryItemPacket, interaction: DraftbotInteraction): string {\n+\t\tlet sideEffectString = \"\";\n+\t\tObject.entries(packet.fightActionEffectDealt!)\n+\t\t\t.forEach(([key, value]) => {\n+\t\t\t\tif (typeof value === \"number\") {\n+\t\t\t\t\tconst operator = value >= 0 ? FightConstants.OPERATOR.PLUS : FightConstants.OPERATOR.MINUS;\n+\t\t\t\t\tsideEffectString += i18n.t(`commands:fight.actions.fightActionEffects.opponent.${key}`, {\n+\t\t\t\t\t\tlng: interaction.userLanguage,\n+\t\t\t\t\t\toperator,\n+\t\t\t\t\t\tamount: Math.abs(value)\n+\t\t\t\t\t});\n+\t\t\t\t}\n+\t\t\t\telse if (value) {\n+\t\t\t\t\tsideEffectString += i18n.t(`commands:fight.actions.fightActionEffects.opponent.${key}`, {\n+\t\t\t\t\t\tlng: interaction.userLanguage,\n+\t\t\t\t\t\teffect: i18n.t(`models:fight_actions.${value}.name`, {\n+\t\t\t\t\t\t\tlng: interaction.userLanguage\n+\t\t\t\t\t\t})\n+\t\t\t\t\t});\n+\t\t\t\t}\n+\t\t\t});\n+\t\treturn sideEffectString;\n+\t}\n+\n+\tprivate manageMainMessage(packet: CommandFightHistoryItemPacket, interaction: DraftbotInteraction): string {\n \t\tif (\n@@ -55,14 +102,11 @@ export class DraftbotHistoryCachedMessage extends DraftbotCachedMessage<CommandF\n \t\t) {\n-\t\t\tconst petNickname\n-\t\t\t\t= packet.pet\n-\t\t\t\t\t? packet.pet.nickname\n-\t\t\t\t\t\t? packet.pet.nickname\n-\t\t\t\t\t\t: DisplayUtils.getPetTypeName(interaction.userLanguage, packet.pet.typeId, packet.pet.sex)\n-\t\t\t\t\t: undefined;\n-\n \t\t\t// The fightAction is an alteration or pet assistance\n-\t\t\tnewLine += i18n.t(`models:fight_actions.${packet.fightActionId}.${packet.status}`, {\n+\t\t\treturn i18n.t(`models:fight_actions.${packet.fightActionId}.${packet.status}`, {\n \t\t\t\tlng: interaction.userLanguage,\n \t\t\t\tinterpolation: { escapeValue: false },\n-\t\t\t\tpetNickname\n+\t\t\t\tpetNickname: packet.pet\n+\t\t\t\t\t? packet.pet.nickname\n+\t\t\t\t\t\t? packet.pet.nickname\n+\t\t\t\t\t\t: DisplayUtils.getPetTypeName(interaction.userLanguage, packet.pet.typeId, packet.pet.sex)\n+\t\t\t\t\t: undefined\n \t\t\t});\n@@ -70,3 +114,3 @@ export class DraftbotHistoryCachedMessage extends DraftbotCachedMessage<CommandF\n \t\telse if (packet.customMessage) {\n-\t\t\tnewLine += i18n.t(`models:fight_actions.${packet.fightActionId}.customMessage`, {\n+\t\t\treturn i18n.t(`models:fight_actions.${packet.fightActionId}.customMessage`, {\n \t\t\t\tlng: interaction.userLanguage,\n@@ -75,77 +119,40 @@ export class DraftbotHistoryCachedMessage extends DraftbotCachedMessage<CommandF\n \t\t}\n-\t\telse {\n-\t\t\t// The fightAction is an attack\n-\t\t\tattackName = i18n.t(`models:fight_actions.${packet.fightActionId}.name`, {\n-\t\t\t\tlng: interaction.userLanguage,\n-\t\t\t\tinterpolation: { escapeValue: false },\n-\t\t\t\tcount: 1\n-\t\t\t});\n-\t\t\tnewLine += StringUtils.getRandomTranslation(\n-\t\t\t\t`commands:fight.actions.attacksResults.${packet.status}`,\n-\t\t\t\tinteraction.userLanguage,\n-\t\t\t\t{\n-\t\t\t\t\tattack: attackName,\n-\t\t\t\t\tinterpolation: { escapeValue: false }\n-\t\t\t\t}\n-\t\t\t);\n-\t\t}\n-\n-\t\tif (packet.fightActionEffectReceived) {\n-\t\t\tObject.entries(packet.fightActionEffectReceived!)\n-\t\t\t\t.forEach(([key, value]) => {\n-\t\t\t\t\tif (typeof value === \"number\") {\n-\t\t\t\t\t\tconst operator = value >= 0 ? FightConstants.OPERATOR.PLUS : FightConstants.OPERATOR.MINUS;\n-\t\t\t\t\t\tnewLine += i18n.t(`commands:fight.actions.fightActionEffects.self.${key}`, {\n-\t\t\t\t\t\t\tlng: interaction.userLanguage,\n-\t\t\t\t\t\t\toperator,\n-\t\t\t\t\t\t\tamount: Math.abs(value)\n-\t\t\t\t\t\t});\n-\t\t\t\t\t}\n-\t\t\t\t\telse if (value) {\n-\t\t\t\t\t\tnewLine += i18n.t(`commands:fight.actions.fightActionEffects.self.${key}`, {\n-\t\t\t\t\t\t\tlng: interaction.userLanguage,\n-\t\t\t\t\t\t\teffect: i18n.t(`models:fight_actions.${value}.name`, {\n-\t\t\t\t\t\t\t\tlng: interaction.userLanguage\n-\t\t\t\t\t\t\t})\n-\t\t\t\t\t\t});\n-\t\t\t\t\t}\n-\t\t\t\t});\n-\t\t}\n-\n-\t\t// Then we need to display the side effects of the attack or alteration if there are any\n-\t\tif (packet.fightActionEffectDealt) {\n-\t\t\tObject.entries(packet.fightActionEffectDealt!)\n-\t\t\t\t.forEach(([key, value]) => {\n-\t\t\t\t\tif (typeof value === \"number\") {\n-\t\t\t\t\t\tconst operator = value >= 0 ? FightConstants.OPERATOR.PLUS : FightConstants.OPERATOR.MINUS;\n-\t\t\t\t\t\tnewLine += i18n.t(`commands:fight.actions.fightActionEffects.opponent.${key}`, {\n-\t\t\t\t\t\t\tlng: interaction.userLanguage,\n-\t\t\t\t\t\t\toperator,\n-\t\t\t\t\t\t\tamount: Math.abs(value)\n-\t\t\t\t\t\t});\n-\t\t\t\t\t}\n-\t\t\t\t\telse if (value) {\n-\t\t\t\t\t\tnewLine += i18n.t(`commands:fight.actions.fightActionEffects.opponent.${key}`, {\n-\t\t\t\t\t\t\tlng: interaction.userLanguage,\n-\t\t\t\t\t\t\teffect: i18n.t(`models:fight_actions.${value}.name`, {\n-\t\t\t\t\t\t\t\tlng: interaction.userLanguage\n-\t\t\t\t\t\t\t})\n-\t\t\t\t\t\t});\n-\t\t\t\t\t}\n-\t\t\t\t});\n-\t\t}\n \n+\t\t// The fightAction is an attack\n+\t\treturn StringUtils.getRandomTranslation(\n+\t\t\t`commands:fight.actions.attacksResults.${packet.status}`,\n+\t\t\tinteraction.userLanguage,\n+\t\t\t{\n+\t\t\t\tattack: i18n.t(`models:fight_actions.${packet.fightActionId}.name`, {\n+\t\t\t\t\tlng: interaction.userLanguage,\n+\t\t\t\t\tinterpolation: { escapeValue: false },\n+\t\t\t\t\tcount: 1\n+\t\t\t\t}),\n+\t\t\t\tinterpolation: { escapeValue: false }\n+\t\t\t}\n+\t\t);\n+\t}\n \n-\t\tif (this.historyContent.length + newLine.length <= FightConstants.MAX_HISTORY_LENGTH) {\n-\t\t\tthis.historyContent = `${this.historyContent}\\n${newLine}`;\n-\t\t\tawait this.post({ content: this.historyContent });\n-\t\t\treturn undefined;\n-\t\t}\n-\t\tthis.storedMessage = undefined;\n-\t\tthis.historyContent = newLine;\n-\t\tawait this.post({ content: this.historyContent });\n-\t\tDraftbotCachedMessages.markAsReupload(DraftbotCachedMessages.getOrCreate(this.originalMessageId, DraftbotFightStatusCachedMessage));\n-\t\tDraftbotCachedMessages.markAsReupload(DraftbotCachedMessages.getOrCreate(this.originalMessageId, DraftbotActionChooseCachedMessage));\n-\t\treturn undefined;\n-\t};\n+\tprivate manageReceivedEffects(packet: CommandFightHistoryItemPacket, interaction: DraftbotInteraction): string {\n+\t\tlet effectsString = \"\";\n+\t\tObject.entries(packet.fightActionEffectReceived!)\n+\t\t\t.forEach(([key, value]) => {\n+\t\t\t\tif (typeof value === \"number\") {\n+\t\t\t\t\teffectsString += i18n.t(`commands:fight.actions.fightActionEffects.self.${key}`, {\n+\t\t\t\t\t\tlng: interaction.userLanguage,\n+\t\t\t\t\t\toperator: value >= 0 ? FightConstants.OPERATOR.PLUS : FightConstants.OPERATOR.MINUS,\n+\t\t\t\t\t\tamount: Math.abs(value)\n+\t\t\t\t\t});\n+\t\t\t\t}\n+\t\t\t\telse if (value) {\n+\t\t\t\t\teffectsString += i18n.t(`commands:fight.actions.fightActionEffects.self.${key}`, {\n+\t\t\t\t\t\tlng: interaction.userLanguage,\n+\t\t\t\t\t\teffect: i18n.t(`models:fight_actions.${value}.name`, {\n+\t\t\t\t\t\t\tlng: interaction.userLanguage\n+\t\t\t\t\t\t})\n+\t\t\t\t\t});\n+\t\t\t\t}\n+\t\t\t});\n+\t\treturn effectsString;\n+\t}\n }\n","improvement-type":"Complex Method"}],"change-level":"warning","is-hotspot?":false,"line":442,"what-changed":"isOutcomeValidForPlayer has a cyclomatic complexity of 11, threshold = 9","how-to-fix":"There are many reasons for Complex Method. Sometimes, another design approach is beneficial such as a) modeling state using an explicit state machine rather than conditionals, or b) using table lookup rather than long chains of logic. In other scenarios, the function can be split using [EXTRACT FUNCTION](https://refactoring.com/catalog/extractFunction.html). Just make sure you extract natural and cohesive functions. Complex Methods can also be addressed by identifying complex conditional expressions and then using the [DECOMPOSE CONDITIONAL](https://refactoring.com/catalog/decomposeConditional.html) refactoring.","change-type":"introduced"},{"method":"initModel","why-it-occurs":"Overly long functions make the code harder to read. The recommended maximum function length for the TypeScript language is 70 lines of code. Severity: Brain Method - Complex Method - Long Method.","name":"Large Method","file":"Core/src/core/database/game/models/Player.ts","refactoring-examples":null,"change-level":"warning","is-hotspot?":true,"line":1530,"what-changed":"initModel increases from 155 to 159 lines of code, threshold = 70","how-to-fix":"We recommend to be careful here -- just splitting long functions don't necessarily make the code easier to read. Instead, look for natural chunks inside the functions that expresses a specific task or concern. Often, such concerns are indicated by a Code Comment followed by an if-statement. Use the [EXTRACT FUNCTION](https://refactoring.com/catalog/extractFunction.html) refactoring to encapsulate that concern.","change-type":"degraded"}]},"positive-impact-count":1,"repo":"Crownicles","code-health":7.7367763154535645,"version":"3.0","authors":["BastLast","ntalcme"],"directives":{"added":[],"removed":[]},"positive-findings":{"number-of-types":1,"number-of-files-touched":1,"findings":[{"name":"Primitive Obsession","file":"Core/src/core/database/game/models/Player.ts","change-type":"improved","change-level":"improvement","is-hotspot?":true,"why-it-occurs":"Code that uses a high degree of built-in, primitives such as integers, strings, floats, lacks a domain language that encapsulates the validation and semantics of function arguments. Primitive Obsession has several consequences: 1) In a statically typed language, the compiler will detect less erroneous assignments. 2) Security impact since the possible value range of a variable/argument isn't retricted.\n\nIn this module, 49 % of all functions have primitive types as arguments.","how-to-fix":"Primitive Obsession indicates a missing domain language. Introduce data types that encapsulate the details and constraints of your domain. For example, instead of `int userId`, consider `User clicked`.","what-changed":"The ratio of primitive types in function arguments decreases from 49.30% to 48.68%, threshold = 30.0%"}]},"notices":{"number-of-types":0,"number-of-files-touched":0,"findings":[]},"external-review-provider":"GitHub"},"analysistime":"2025-09-24T22:18:15.000Z","project-name":"Crownicles","repository":"https://github.com/Crownicles/Crownicles.git"}}