Skip to content

Commit

Permalink
Merge pull request #8 from FightCore/feat/hitbox-rework
Browse files Browse the repository at this point in the history
Feat/hitbox rework
  • Loading branch information
bartdebever authored Aug 8, 2024
2 parents c122f81 + a13e074 commit 6724422
Show file tree
Hide file tree
Showing 12 changed files with 1,313 additions and 548 deletions.
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

0 comments on commit 6724422

Please sign in to comment.