osumodcalculator
Version: 
osu! calculator for converting values to DT & HT and other things
229 lines (214 loc) • 6.16 kB
text/typescript
import { Mods, types } from ".";
/**
 * Mod acronyms as an enum
 */
export enum ModInts {
    NM = 0,
    NF = 1,
    EZ = 2,
    TD = 4,
    HD = 8,
    HR = 16,
    SD = 32,
    DT = 64,
    RL = 128,
    HT = 256,
    NC = 512,
    FL = 1024,
    AT = 2048,
    SO = 4096,
    AP = 8192,
    PF = 16384,
    '4K' = 32768,
    '5K' = 65536,
    '6K' = 131072,
    '7K' = 262144,
    '8K' = 524288,
    FI = 1048576,
    RD = 2097152,
    CN = 4194304,
    TP = 8388608,
    '9K' = 16777216,
    CP = 33554432,
    '1K' = 67108864,
    '3K' = 134217728,
    '2K' = 268435456,
    SV2 = 536870912,
    MR = 1073741824
}
/**
 * STABLE MODS ONLY
 * 
 * Converts each mod to their enum value and adds them together
 * 
 * @includeExample src/examples/mod.ts:4-5
 */
export function toInt(mods: types.ModLegacy[]) {
    let modInt = 0;
    const map: [string, number][] = Mods.map(x => [x.acronym, x.enum] as [string, number]).filter(x => x[1] != -1);
    const searchmap = map.map(x => x[0]);
    for (const mod of mods) {
        if (searchmap.includes(mod)) {
            const idx = searchmap.indexOf(mod);
            const v: number = map[idx][1];
            modInt += v;
        }
    }
    return modInt;
}
/**
 * STABLE MODS ONLY
 * 
 * Converts an enum into the acronym of each mod
 * 
 * @includeExample src/examples/mod.ts:8-9
 */
export function intToAcronym(modInt: number) {
    const mods: types.ModLegacy[] = [];
    for (const mod of Mods) {
        if (mod.enum != -1 && modInt & mod.enum) {
            mods.push(mod.acronym as types.ModLegacy);
        }
    }
    removeIncompatible(mods);
    return mods;
}
/**
 * mod acronyms in order (subjective)
 */
function getOrderedMods(): types.Mod[] {
    return ['EZ', 'HD', 'FI', 'HT', 'DC', 'DT', 'NC', 'HR', 'FL', 'SD', 'PF', 'NF', 'AT', 'CN', 'RX', 'AP', 'TP', 'SO', 'TD', '1K', '2K', '3K', '4K', '5K', '6K', '7K', '8K', '9K', '10K', 'CO', 'RD', 'MR', 'SV2',
        'BL', 'ST', 'DA', 'CL', 'AL', 'ST', 'TR', 'WG', 'SI', 'GR', 'DF', 'WU', 'WD', 'TR', 'BR', 'AD', 'MU', 'NS', 'MG', 'RP', 'AS', 'FF'
    ];
}
/**
 * Converts mod names to their respective acronyms
 * 
 * mods are NOT case sensitive
 * 
 * @includeExample src/examples/mod.ts:12-13
 */
export function nameToAcronym(mods: string[]) {
    mods = mods.map(x => x.toLowerCase());
    const acronyms: types.Mod[] = [];
    const map: [string, types.Mod][] = Mods.map(x => [x.name.toLowerCase(), x.acronym] as [string, types.Mod]);
    const searchmap = map.map(x => x[0]);
    for (const mod of mods) {
        if (searchmap.includes(mod)) {
            const index = searchmap.indexOf(mod);
            acronyms.push(map[index][1]);
        }
    }
    return acronyms;
}
/**
 * Converts mod acronyms to their respective names
 * 
 * mods are NOT case sensitive
 * 
 * @includeExample src/examples/mod.ts:16-17
 */
export function acronymToName(mods: types.Mod[]) {
    mods = mods.map(x => x.toUpperCase() as types.Mod);
    const fullnames: types.ModLong[] = [];
    const map: [types.Mod, types.ModLong][] = Mods.map(x => [x.acronym, x.name] as [types.Mod, types.ModLong]);
    const searchmap = map.map(x => x[0]);
    for (const mod of mods) {
        if (searchmap.includes(mod)) {
            const index = searchmap.indexOf(mod);
            fullnames.push(map[index][1]);
        }
    }
    return fullnames;
}
/**
 * remove duplicate mods 
 * 
 * @includeExample src/examples/mod.ts:20-21
 */
export function removeDupe(mods: types.Mod[]) {
    const nodupe: types.Mod[] = [];
    for (const mod of mods) {
        if (!nodupe.includes(mod)) {
            nodupe.push(mod);
        }
    }
    return nodupe;
}
/**
 * orders mods
 * 
 * @includeExample src/examples/mod.ts:24-25
 */
export function order(mods: types.Mod[]) {
    const nt: types.Mod[] = [];
    for (const om of getOrderedMods()) {
        for (const mod of mods) {
            if (om === mod) {
                nt.push(mod);
            }
        }
    }
    return nt;
}
/**
 * removes incompatible mods such as ez+hr and mods from other mods
 * 
 * @includeExample src/examples/mod.ts:28-29
 */
export function removeIncompatible(mods: types.Mod[], mode: types.GameMode = 'osu') {
    const ignore: types.Mod[] = [];
    const map = Mods.map(x => x.acronym);
    const fr: types.Mod[] = [];
    for (const mod of mods) {
        const idx = map.indexOf(mod);
        const checkMod = Mods[idx];
        if (!ignore.includes(mod) && (
            checkMod.restrictMode && checkMod.restrictMode.includes(mode) || !checkMod.restrictMode || checkMod.restrictMode.length == 0
        )) {
            for (const ig of checkMod.incompatible) {
                if (ig.startsWith('(')) {
                    if (
                        (!ig.includes('!' + mode) && ig.includes(mode)) ||
                        (ig.includes('!') && !ig.includes(mode))
                    ) {
                        ignore.push(ig.split(')')[1] as types.Mod);
                    }
                } else {
                    ignore.push(ig as types.Mod);
                }
            }
            fr.push(mod);
        }
    }
    return fr;
}
/**
 * re-orders, removes duplicates, removes incompabitle
 * 
 * @includeExample src/examples/mod.ts:32-34
 */
export function fix(mods: types.Mod[], mode: types.GameMode = 'osu') {
    const nodupe = removeDupe(mods);
    const inc = removeIncompatible(nodupe, mode);
    const nt = order(inc);
    return nt;
}
/**
 * convert string into mods
 * 
 * make sure each mod is 2 characters
 * 
 * @includeExample src/examples/mod.ts:37-38
 */
export function fromString(str: string): types.Mod[] {
    const temp = str.replace(/(.{2})/g, "$1 ").split(' ');
    const map = Mods.map(x => x.acronym);
    const output: types.Mod[] = [];
    temp.forEach(mod => {
        if (map.includes(mod as types.Mod)) {
            output.push(mod as types.Mod);
        }
    });
    return output;
}