From 8c215824fa4c2ea258cad4b1c1a8281ed310bdde Mon Sep 17 00:00:00 2001 From: Anima <18208134+animafps@users.noreply.github.com> Date: Fri, 15 Oct 2021 22:17:00 +0000 Subject: [PATCH] feat: added fov & fovconvert commands and fov helper functions (#133) * feat: added fov command and fov helper functions * feat: added fovconvert command and associated helper functions * docs: added relevant documentation for fovconvert command --- docs/CNAME | 1 - docs/commands/index.md | 2 +- docs/commands/math.md | 26 ++++++-- docs/games.md | 2 +- src/arguments/aspect-ratio.ts | 26 ++++++++ src/arguments/film.ts | 9 ++- src/commands/Math/fov.ts | 21 +++++-- src/commands/Math/fovConvert.ts | 12 +++- src/helpers/array.ts | 2 +- src/helpers/fovHelper.ts | 107 ++++++++++++++++++++++++++++++++ 10 files changed, 188 insertions(+), 20 deletions(-) delete mode 100644 docs/CNAME create mode 100644 src/arguments/aspect-ratio.ts create mode 100644 src/helpers/fovHelper.ts diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index dee2ae3..0000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -fpsmath.animafps.xyz \ No newline at end of file diff --git a/docs/commands/index.md b/docs/commands/index.md index 5644fed..8c87e34 100644 --- a/docs/commands/index.md +++ b/docs/commands/index.md @@ -25,7 +25,7 @@ Below is a shortlist of all the bot commands. | [`deg`](math.md#deg) | Converts Sensitivity to deg/mm | | [`mpi`](math.md#mpi) | Converts Sensitivity to MPI | | [`focal`](math.md#focal) | Focal Length Scales a desired sens between 2 fov values of the same type | -| [`fov`](math.md#fov) | Finds the true vertical and horizontal FOVs for certain aspect ratio and game/FOV scaling method\(FILM notation\) | +| [`fov`](math.md#fov) | Finds the true vertical and horizontal FoV that is being displayed on screen | | [`fovconvert`](math.md#fovconvert) | Converts a FOV value from one game or film notation to another | | [`inch`](math.md#inch) | Converts Sensitivity to inch/rev | | [`sens`](math.md#sens) | Converts cm/rev\|deg/mm\|MPI\|inch/rev\|arcmin to a game sensitivity default cm/rev | diff --git a/docs/commands/math.md b/docs/commands/math.md index c920938..e6b5f15 100644 --- a/docs/commands/math.md +++ b/docs/commands/math.md @@ -98,13 +98,31 @@ fps-focal ### fov -!!! error - Command not implemented yet +Finds the true vertical and horizontal FoV that is being displayed on screen + +#### Usage + +```text +fps-fov +``` + +#### Aliases + +`fov-scaling`, `film` ### fovconvert -!!! error - Command not implemented yet +Converts a FoV value from one game or FILM notation to another. + +#### Usage + +```text +fps-fovconvert +``` + +#### Aliases + +`fov-convert`, `film-convert`, `convert-fov` ### inch diff --git a/docs/games.md b/docs/games.md index 69d8618..d18aa51 100644 --- a/docs/games.md +++ b/docs/games.md @@ -1,5 +1,5 @@ --- -title: 'Supported Games' +title: Supported Games --- All the games that FPSMath supports and the features that are available for certain commands diff --git a/src/arguments/aspect-ratio.ts b/src/arguments/aspect-ratio.ts new file mode 100644 index 0000000..1cdd3b7 --- /dev/null +++ b/src/arguments/aspect-ratio.ts @@ -0,0 +1,26 @@ +import { Argument, PieceContext, ArgumentContext } from '@sapphire/framework' + +export default class GameArgument extends Argument { + public constructor(context: PieceContext) { + super(context, { name: 'aspectRatio' }) + } + + public run(parameter: string, context: ArgumentContext) { + if (/\d{1,4}:\d{1,4}/.test(parameter)) { + const split = parameter.split(':') + return this.ok(Number(split[0]) / Number(split[1])) + } + return this.error({ + parameter, + message: 'Not valid aspect ratio', + identifier: 'aspectRatioNoSupport', + context, + }) + } +} + +declare module '@sapphire/framework' { + interface ArgType { + aspectRatio: number + } +} diff --git a/src/arguments/film.ts b/src/arguments/film.ts index f24eebf..401c65c 100644 --- a/src/arguments/film.ts +++ b/src/arguments/film.ts @@ -1,4 +1,5 @@ import { Argument, PieceContext, ArgumentContext } from '@sapphire/framework' +import { getObject } from '../helpers/array' export class UserArgument extends Argument { public constructor(context: PieceContext) { @@ -7,11 +8,13 @@ export class UserArgument extends Argument { public run(parameter: string, context: ArgumentContext) { if ( - /^hm[lfi]$|^vm[lfi]$|^\d{1,2}m[lfi]\d{1,2}$/i.test( - parameter.toLowerCase() + /^HM[LFI]$|^VM[LFI]$|^\d{1,2}M[LFI]\d{1,2}$/.test( + parameter.toUpperCase() ) ) { - return this.ok(parameter.toLowerCase()) + return this.ok(parameter.toUpperCase()) + } else if (getObject(parameter, 'film')) { + return this.ok(getObject(parameter, 'film') as string) } return this.error({ parameter, diff --git a/src/commands/Math/fov.ts b/src/commands/Math/fov.ts index 7139c05..eb10ef0 100644 --- a/src/commands/Math/fov.ts +++ b/src/commands/Math/fov.ts @@ -1,11 +1,12 @@ -import { Command, CommandOptions } from '@sapphire/framework' +import { Args, Command, CommandOptions } from '@sapphire/framework' import type { Message } from 'discord.js' import { ApplyOptions } from '@sapphire/decorators' +import { filmToTrue } from '../../helpers/fovHelper' @ApplyOptions({ aliases: ['fov-scaling', 'film'], description: - 'Finds the true vertical and horizontal FOV that is being displayed on screen', + 'Finds the true vertical and horizontal FoV that is being displayed on screen', detailedDescription: ` 📝 **| Command Usage** → fps-fov *FoV* *GameName* *AspectRatio* @@ -14,7 +15,7 @@ import { ApplyOptions } from '@sapphire/decorators' 🖇️ **| Aliases**: \`fov-scaling\` and \`film\` 🔍 **| Extended Help** - Finds the true vertical and horizontal FOV for certain aspect ratio that the game is being rendered at plus game's FoV scaling method + Finds the true vertical or horizontal FoV for certain aspect ratio that the game is being rendered at plus game's FoV scaling method ⚙ **| Explained usage** → **FoV**: The in-game FoV value or equivalent FoV value. @@ -29,8 +30,16 @@ import { ApplyOptions } from '@sapphire/decorators' generateDashLessAliases: true, requiredClientPermissions: ['SEND_MESSAGES'], }) -export class UserCommand extends Command { - public async run(message: Message) { - return message.reply('Not fully implemented yet') +export default class UserCommand extends Command { + public async run(message: Message, args: Args) { + const fov = await args.pick('float') + const film = await args.pick('film') + const aspect = await args.pick('aspectRatio') + const { horizontalFOV, verticalFOV } = filmToTrue(fov, film, aspect) + return message.reply( + `Horizontal FoV: ${parseFloat( + horizontalFOV.toFixed(5) + )}°\nVertical FoV: ${parseFloat(verticalFOV.toFixed(5))}°` + ) } } diff --git a/src/commands/Math/fovConvert.ts b/src/commands/Math/fovConvert.ts index c40b156..30d7edd 100644 --- a/src/commands/Math/fovConvert.ts +++ b/src/commands/Math/fovConvert.ts @@ -1,6 +1,7 @@ -import { Command, CommandOptions } from '@sapphire/framework' +import { Args, Command, CommandOptions } from '@sapphire/framework' import type { Message } from 'discord.js' import { ApplyOptions } from '@sapphire/decorators' +import { filmToFilm } from '../../helpers/fovHelper' @ApplyOptions({ aliases: ['fov-convert', 'film-convert', 'convert-fov'], @@ -32,7 +33,12 @@ import { ApplyOptions } from '@sapphire/decorators' requiredClientPermissions: ['SEND_MESSAGES'], }) export class UserCommand extends Command { - public async run(message: Message) { - return message.reply('Not fully implemented yet') + public async run(message: Message, args: Args) { + const fov = await args.pick('float') + const inFILM = await args.pick('film') + const outFILM = await args.pick('film') + const aspect = await args.pick('aspectRatio') + const output = filmToFilm(fov, inFILM, outFILM, aspect) + return message.reply(`${parseFloat(output.toFixed(5))}°`) } } diff --git a/src/helpers/array.ts b/src/helpers/array.ts index 2b18fdd..4b3931e 100644 --- a/src/helpers/array.ts +++ b/src/helpers/array.ts @@ -36,7 +36,7 @@ export const array: { aliases: ['apex-legends', 'apex'], film: '4ML3', }, - Valorant: { yaw: 0.07, aliases: ['valorant', 'val'] }, + Valorant: { yaw: 0.07, aliases: ['valorant', 'val'], film: '16MS9' }, Overwatch: { yaw: 0.0066, aliases: ['overwatch', 'ow'], diff --git a/src/helpers/fovHelper.ts b/src/helpers/fovHelper.ts new file mode 100644 index 0000000..55cf6fc --- /dev/null +++ b/src/helpers/fovHelper.ts @@ -0,0 +1,107 @@ +export function convertFOV( + fov: number, + inputAspect: number, + outputAspect: number +) { + return ( + (Math.atan( + (outputAspect / inputAspect) * Math.tan((fov * Math.PI) / 360) + ) * + 360) / + Math.PI + ) +} + +export function filmToAspect(filmNotation: string) { + const startString = filmNotation.split(/M/)[0] + const endString = filmNotation.split(/M[FLI]/)[1] + return Number(startString) / Number(endString) +} + +export function filmToTrue( + fov: number, + film: string, + aspectRatio: number +): fovValues { + const filmAspect = filmToAspect(film) + if (/^\d{1,2}MS\d{1,2}$/.test(film)) { + return { + horizontalFOV: fov, + verticalFOV: convertFOV(fov, filmAspect, 1), + } + } else if (film.startsWith('H')) { + return lockHorizontal(fov, aspectRatio) + } else if (film.startsWith('V')) { + return lockVertical(fov, aspectRatio) + } else if (/^\d{1,2}ML\d{1,2}$/.test(film)) { + return lockVertical(fov, aspectRatio, filmAspect) + } else if (/^\d{1,2}MF\d{1,2}$/.test(film)) { + if (aspectRatio > filmAspect) { + return lockHorizontal(fov, aspectRatio) + } + return lockVertical(fov, aspectRatio, filmAspect) + } else if (/^\d{1,2}MI\d{1,2}$/.test(film)) { + if (aspectRatio < filmAspect) { + return lockHorizontal(fov, aspectRatio) + } + return lockVertical(fov, aspectRatio, filmAspect) + } + throw Error('parsing failed') +} + +export function trueToFILM( + { horizontalFOV, verticalFOV }: fovValues, + film: string, + aspectRatio: number +): number { + const filmAspect = filmToAspect(film) + if (/^\d{1,2}MS\d{1,2}$/.test(film) || film.startsWith('H')) { + return horizontalFOV + } else if (film.startsWith('V')) { + return verticalFOV + } else { + return convertFOV(horizontalFOV, filmAspect, aspectRatio) + } +} + +export function lockVertical( + fov: number, + aspectRatio: number, + filmAspect?: number +): fovValues { + if (filmAspect) { + return { + horizontalFOV: convertFOV(fov, filmAspect, aspectRatio), + verticalFOV: convertFOV(fov, filmAspect, 1), + } + } + return { + horizontalFOV: convertFOV(fov, 1, aspectRatio), + verticalFOV: fov, + } +} + +export function lockHorizontal(fov: number, aspectRatio: number): fovValues { + return { + horizontalFOV: fov, + verticalFOV: convertFOV(fov, aspectRatio, 1), + } +} + +export interface fovValues { + horizontalFOV: number + verticalFOV: number +} + +export function filmToFilm( + fov: number, + inFILM: string, + outFILM: string, + aspectRatio: number +): number { + return trueToFILM( + filmToTrue(fov, inFILM, aspectRatio), + outFILM, + aspectRatio + ) +}