Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nodemon.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"src"
],
"ext": "ts",
"exec": "tsc && node -r dotenv/config ./build/index.js"
"exec": "tsc -p tsconfig-build.json && node -r dotenv/config ./build/index.js"
}
1,301 changes: 958 additions & 343 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,26 @@
},
"homepage": "https://github.com/FightCore/bot-js#readme",
"dependencies": {
"@apollo/client": "^3.10.5",
"@apollo/client": "^3.11.3",
"@datalust/winston-seq": "^2.0.0",
"@elastic/elasticsearch": "^8.14.0",
"axios": "^1.7.2",
"axios": "^1.7.3",
"discord.js": "^14.15.3",
"dotenv": "^16.4.5",
"inversify": "^6.0.2",
"jaro-winkler-typescript": "^1.0.1",
"reflect-metadata": "^0.1.14",
"sqlite3": "^5.1.7",
"winston": "^3.13.0"
"winston": "^3.13.1"
},
"devDependencies": {
"@jest/globals": "^29.7.0",
"@types/node": "^16.18.100",
"@types/node": "^16.18.104",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"eslint": "^8.57.0",
"jest": "^29.7.0",
"ts-jest": "^29.1.5",
"typescript": "^5.4.5"
"ts-jest": "^29.2.4",
"typescript": "^5.5.4"
}
}
}
2 changes: 1 addition & 1 deletion src/assets/framedata.json

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions src/data/unique-move-fixes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Character } from '../models/character';
import { Move } from '../models/move';

/**
* Fixes unique moves by modifying the provided move object.
*
* @param {Move} move - The move object to be fixed.
* @return {Move} The modified move object.
*/
export function fixUniqueMoves(move: Move, character: Character): Move {
if ((move.normalizedName === 'neutralb' || move.normalizedName === 'aneutralb') && character.normalizedName === 'ness') {
return fixNessNeutralB(move);
}
if (move.normalizedName == 'nessspecial' && character.normalizedName === 'kirby') {
return fixNessNeutralB(move);
}

return move;
}

function fixNessNeutralB(move: Move): Move {
move.hits = move.hits.slice(move.hits.length - 1);

// Add notes if they don't exist previously and ensure they start on a new line.
if (!move.notes) {
move.notes = '';
} else {
move.notes += '\n';
}
move.notes +=
'Damage starts as 11 but increases every frame by 1. Only the last hitbox is shown. Please check the website for more info.';
return move;
}
12 changes: 4 additions & 8 deletions src/embeds/field-creator/hitlag-field-creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,29 @@ export class HitlagFieldCreator {
value: hitlagValues.map((hitlag) => hitlag.hitlagAttacker).join('/'),
},
{
title: 'Hitlag for victim',
title: 'Hitlag for defender',
value: hitlagValues.map((hitlag) => hitlag.hitlagDefender).join('/'),
},
{
title: 'Hitlag for attacker (crouch canceled)',
value: hitlagValues.map((hitlag) => hitlag.hitlagAttackerCrouch).join('/'),
},
{
title: 'Hitlag for victim (crouch canceled)',
title: 'Hitlag for defender (crouch canceled)',
value: hitlagValues.map((hitlag) => hitlag.hitlagDefenderCrouch).join('/'),
},
]);
}

return BodyFormatter.create([
{
title: 'Hitlag',
title: 'Hitlag attacker & defender',
value: hitlagValues.map((hitlag) => hitlag.hitlagAttacker).join('/'),
},
{
title: 'Hitlag (crouch canceled)',
title: 'Hitlag attacker & defender (crouch canceled)',
value: hitlagValues.map((hitlag) => hitlag.hitlagAttackerCrouch).join('/'),
},
{
title: 'Note',
value: 'Hitlag is the same for attacker and defender',
},
]);
}
}
146 changes: 87 additions & 59 deletions src/embeds/knockback-embed-creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Loader } from '../data/loader';
import { Hitbox } from '../models/hitbox';
import { versionNumber } from '../meta-data';
import { getMoveLink } from '../utils/fightcore-link';
import { processDuplicateHits, processDuplicateHitboxesForCrouchCancel } from '../utils/hitbox-utils';

export abstract class KnockbackEmbedCreator extends BaseEmbedCreator {
constructor(private knockbackTarget: number, private longTerm: string, private shortTerm: string) {
Expand Down Expand Up @@ -40,80 +41,94 @@ export abstract class KnockbackEmbedCreator extends BaseEmbedCreator {
const characterEmote = CharacterEmoji.getEmoteId(character.normalizedName);
const targetEmote = CharacterEmoji.getEmoteId(target.normalizedName);
embedBuilder.setTitle(`${characterEmote} ${character.name} - ${move.name} vs ${target.name} ${targetEmote} `);
for (const hitbox of move.hitboxes.sort(this.orderHitboxes)) {
if (hitbox.angle > 179 && hitbox.angle != 361) {
hitboxMap.set(hitbox.name, `Can not be ${this.shortTerm} due to angle being higher than 179 (${hitbox.angle})`);
} else if (hitbox.angle === 0) {
hitboxMap.set(hitbox.name, `Can not be ${this.shortTerm} due to angle being 0`);
} else if (hitbox.setKnockback) {
const canBeCanceled = !CrouchCancelCalculator.meetsKnockbackTarget(hitbox, target, this.knockbackTarget);
hitboxMap.set(hitbox.name, `Can ${canBeCanceled ? 'always' : 'not'} be ${this.shortTerm}`);
} else {
const crouchCancelPercentage = this.getCrouchCancelPercentageOrImpossible(hitbox, target);
hitboxMap.set(hitbox.name, crouchCancelPercentage);
}

const hits = processDuplicateHits(processDuplicateHitboxesForCrouchCancel(move.hits));
if (hits.length > 20) {
return this.createErrorEmbed(embedBuilder);
}

let result = `${this.longTerm} breaks at the following percentages for each hitbox.\n`;
for (const keyValuePair of hitboxMap) {
result += InfoLine.createLineWithTitle(keyValuePair[0], keyValuePair[1]) + '\n';
for (const hit of hits) {
const name = hit.name ? hit.name : `Frames ${hit.aggregatedStart} - ${hit.aggregatedEnd}`;

for (const hitbox of hit.hitboxes.sort(this.orderHitboxes)) {
if (hitbox.angle > 179 && hitbox.angle != 361) {
hitboxMap.set(
name + ' - ' + hitbox.name,
`Can not be ${this.shortTerm} due to angle being higher than 179 (${hitbox.angle})`
);
} else if (hitbox.angle === 0) {
hitboxMap.set(name + ' - ' + hitbox.name, `Can not be ${this.shortTerm} due to angle being 0`);
} else if (hitbox.setKnockback) {
const canBeCanceled = !CrouchCancelCalculator.meetsKnockbackTarget(hitbox, target, this.knockbackTarget);
hitboxMap.set(name + ' - ' + hitbox.name, `Can ${canBeCanceled ? 'always' : 'not'} be ${this.shortTerm}`);
} else {
const crouchCancelPercentage = this.getCrouchCancelPercentageOrImpossible(hitbox, target);
hitboxMap.set(name + ' - ' + hitbox.name, crouchCancelPercentage);
}
}

for (const keyValuePair of hitboxMap) {
result += InfoLine.createLineWithTitle(keyValuePair[0], keyValuePair[1]) + '\n';
}
}

embedBuilder.addFields({ name: `${this.longTerm} percentage`, value: result });
return [embedBuilder];
}

private createForAll(character: Character, move: Move, embedBuilder: EmbedBuilder, dataLoader: Loader): EmbedBuilder[] {
const characterEmote = CharacterEmoji.getEmoteId(character.normalizedName);
embedBuilder.setTitle(`${characterEmote} ${character.name} - ${move.name}`);
for (const hitbox of move.hitboxes.sort(this.orderHitboxes)) {
if (hitbox.angle > 179 && hitbox.angle != 361) {
embedBuilder.addFields({
name: hitbox.name,
value: `Can not be ${this.shortTerm} due to angle being higher than 179 (${hitbox.angle})`,
});
continue;
} else if (hitbox.angle === 0) {
embedBuilder.addFields({ name: hitbox.name, value: `Can not be ${this.shortTerm} due to angle being 0` });
continue;
} else if (hitbox.setKnockback) {
this.addSetKnockbackField(embedBuilder, hitbox, dataLoader);
continue;
}

const hitboxMap = new Map<Character, string>();
for (const target of dataLoader.data
const hits = processDuplicateHits(processDuplicateHitboxesForCrouchCancel(move.hits));
if (hits.length > 20) {
return this.createErrorEmbed(embedBuilder);
}
for (const hit of hits) {
const hitboxes = hit.hitboxes;
const characters = dataLoader.data
.filter((character) => character.characterStatistics.weight > 0)
.sort(this.orderCharacters)) {
const crouchCancelPercentage = this.getCrouchCancelPercentageOrImpossible(hitbox, target);
hitboxMap.set(target, crouchCancelPercentage);
}
.sort(this.orderCharacters);
for (const hitbox of hitboxes) {
const name = hit.name ? hit.name : `Frames ${hit.aggregatedStart} - ${hit.aggregatedEnd}`;

if (hitbox.angle > 179 && hitbox.angle != 361) {
embedBuilder.addFields({
name: name + ' - ' + hitbox.name,
value: `Can not be ${this.shortTerm} due to angle being higher than 179 (${hitbox.angle})`,
});
continue;
} else if (hitbox.angle === 0) {
embedBuilder.addFields({
name: name + ' - ' + hitbox.name,
value: `Can not be ${this.shortTerm} due to angle being 0`,
});
continue;
} else if (hitbox.setKnockback) {
this.addSetKnockbackField(embedBuilder, hitbox, dataLoader, name);
continue;
}

let fieldText = '';
let iterator = 0;
for (const keyValuePair of hitboxMap) {
const emote = CharacterEmoji.getEmoteId(keyValuePair[0].normalizedName);
fieldText += `${emote} ${keyValuePair[1]} `;
iterator++;
if (iterator === 4) {
iterator = 0;
fieldText += '\n';
let fieldText = '';
let iterator = 0;
for (const character of characters) {
const emote = CharacterEmoji.getEmoteId(character.normalizedName);
fieldText += `${emote} ${this.getCrouchCancelPercentageOrImpossible(hitbox, character)} `;
iterator++;
if (iterator === 4) {
iterator = 0;
fieldText += '\n';
}
}
embedBuilder.addFields({ name: name + ' - ' + hitbox.name, value: fieldText, inline: false });
}

embedBuilder.addFields({ name: hitbox.name, value: fieldText });
}

// Discord has a max length size to the embed.
// With a large amount of hitboxes (like G&W)
if (embedBuilder.length >= 6000) {
const errorEmbed = this.baseEmbed();
errorEmbed.setColor(Colors.DarkRed);
errorEmbed.setTitle(embedBuilder.data.title!);
errorEmbed.addFields({
name: 'Too many hitboxes',
value: `This move has too many hitboxes to be displayed on Discord, either supply a target character or visit our [website](${embedBuilder
.data.url!})`,
});

return [errorEmbed];
return this.createErrorEmbed(embedBuilder);
}
return [embedBuilder];
}
Expand All @@ -135,7 +150,7 @@ export abstract class KnockbackEmbedCreator extends BaseEmbedCreator {
return hitboxOne.name.localeCompare(hitboxTwo.name, undefined, { numeric: true, sensitivity: 'base' });
}

private addSetKnockbackField(embedBuilder: EmbedBuilder, hitbox: Hitbox, dataLoader: Loader): void {
private addSetKnockbackField(embedBuilder: EmbedBuilder, hitbox: Hitbox, dataLoader: Loader, hitName: string): void {
const succeedsArray: Character[][] = [[], []];
for (const character of dataLoader.data
.filter((character) => character.characterStatistics.weight > 0)
Expand All @@ -149,13 +164,13 @@ export abstract class KnockbackEmbedCreator extends BaseEmbedCreator {

if (succeedsArray[0].length == 0) {
embedBuilder.addFields({
name: hitbox.name,
name: hitName + ' - ' + hitbox.name,
value: `Can never be ${this.shortTerm} by all characters.`,
});
return;
} else if (succeedsArray[1].length == 0) {
embedBuilder.addFields({
name: hitbox.name,
name: hitName + ' - ' + hitbox.name,
value: `Can always be ${this.shortTerm} by all characters.`,
});
return;
Expand All @@ -169,8 +184,21 @@ export abstract class KnockbackEmbedCreator extends BaseEmbedCreator {
succeedsArray[1].map((character) => CharacterEmoji.getEmoteId(character.normalizedName)).join(' ');

embedBuilder.addFields({
name: hitbox.name,
name: hitName + ' - ' + hitbox.name,
value: canText + '\n' + canNotText,
});
}

private createErrorEmbed(embedBuilder: EmbedBuilder): EmbedBuilder[] {
const errorEmbed = this.baseEmbed();
errorEmbed.setColor(Colors.DarkRed);
errorEmbed.setTitle(embedBuilder.data.title!);
errorEmbed.addFields({
name: 'Too many hitboxes',
value: `This move has too many hitboxes to be displayed on Discord, either supply a target character or visit our [website](${embedBuilder
.data.url!})`,
});

return [errorEmbed];
}
}
Loading