mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-24 15:08:44 -05:00
* Tables * Clocks * Maplist preference selector * Fix SSR * Nav icon * RankedOrScrim * Map pool * Create group * Redirect logic * Persist map pool * Advance from preparing page * Rename query * Fix merge * Fix migration order * Seed groups * Find looking groups SQL * Renders something * More UI work * Back to 30min * Likes/dislikes * Always return own group * Fix like order * 3 tc/rm/cb -> 2 * Show only 3 weapons * Pass group size * Handle both liked and liked by same group * Fix SQL * Group preference frontend work * Morphing * Styling * Don't show group controls if not manager * Give/remove manager * Leave group * Leave with confirm * Delete likes when morphing groups * Clocks consistency * Remove bad invariant * Persist settings to local storage * Fix initial value flashing * Fix never resolving loading indicator * REFRESH_GROUP * Flip animations * Tweaks * Auto refresh logic * Groups of 4 seed * Reduce throwing * Load full groups initial * Create match * Match UI initial * Score reporter initial * Push footer down on match page * Score reporter knows when set ended * Score reporting untested * Show score after report * Align better * Look again with same group functionality * More migrations * Team on match page * Show confirmer before reporting score * Report weapons * Report weapos again by admin + skill changing * Handle no tiebreaker given to MapPool * Remove unranked * Remove support for "team id skill" * no-wrap -> nowrap * Preparing page work * Use common GroupCard component * Add some metas * MemberAdder in looking page * Fix GroupCard actions * Fix SZ only map list including other modes * Add season info * Prompt login * Joining team * Manage group on preparing page * Manage group on preparing page * Seed past matches * Add to seed * No map list preference when full group + fix expiry * Fix skill matchesCount calculation * Tiers initial work * Some progress on tiers * Tiering logic * MMR in group cards * Name to challenge * Team MMR * Big team rank icons * Adjust todos * Match score report with confirm * Allow regular members to report score * Handle reporting weapons edge cases * Add tier images * Improve GroupCard spacing * Refactor looking page * Looking mobile UI * Calculate skill only for current season * Divide groups visually when reporting weapons * Fix match page weapons sorting * Add cache to user skills+tier calculation * Admin report match score * Initial leaderboard * Cached leaderboard * Weapon category lb's * Populate SkillTeamUser in SendouQ * Team leaderboard filtered down * Add TODOs * Seasons initlal * Season weapons initial * Weapons stylized * Show rest weapons as + * Hide peak if same as current * Load matches SQL initial * Season matches UI initial * Take user id in account * Add weapons * Paginated matches * Fix pages count logic * Scroll top on data change * Day headers for matches * Link from user page to user seasons page * Summarize maps + ui initial * Map stats * Player info tabs * MMR chart * Chart adjustments * Handle basing team MMR on player MMR * Set initial MMR * Add info about discord to match page * Season support to tournaments * Get tournament skills as well for the graph * WIP * New team rating logic + misc other * tiered -> tiered.server * Update season starting time * TODOs * Add rules page * Hide elements correctly when off-season * Fix crash when only one player with skill * How-to video * Fix StartRank showing when not logged in * Make user leaderboard the default * Make Skill season non-nullable * Add suggested pass to match * Add rule * identifierToUserIds helper * Fix tiers not showing
172 lines
3.8 KiB
TypeScript
172 lines
3.8 KiB
TypeScript
import {
|
|
mapPoolToSerializedString,
|
|
serializedStringToMapPool,
|
|
} from "./serializer";
|
|
import type { ReadonlyMapPoolObject, MapPoolObject } from "./types";
|
|
import clone from "just-clone";
|
|
import type { MapPoolMap } from "~/db/types";
|
|
import { mapPoolListToMapPoolObject } from "~/modules/map-list-generator";
|
|
import {
|
|
type ModeShort,
|
|
type StageId,
|
|
stageIds,
|
|
} from "~/modules/in-game-lists";
|
|
|
|
type DbMapPoolList = Array<Pick<MapPoolMap, "stageId" | "mode">>;
|
|
|
|
export class MapPool {
|
|
private source: string | ReadonlyMapPoolObject;
|
|
private asSerialized?: string;
|
|
private asObject?: ReadonlyMapPoolObject;
|
|
|
|
constructor(init: ReadonlyMapPoolObject | string | DbMapPoolList) {
|
|
this.source = Array.isArray(init) ? mapPoolListToMapPoolObject(init) : init;
|
|
}
|
|
|
|
static serialize(init: ReadonlyMapPoolObject | string | DbMapPoolList) {
|
|
return new MapPool(init).serialized;
|
|
}
|
|
|
|
static parse(init: MapPoolObject | string | DbMapPoolList) {
|
|
return new MapPool(init).parsed;
|
|
}
|
|
|
|
static toDbList(init: MapPoolObject | string | DbMapPoolList) {
|
|
return new MapPool(init).dbList;
|
|
}
|
|
|
|
get serialized(): string {
|
|
if (this.asSerialized !== undefined) {
|
|
return this.asSerialized;
|
|
}
|
|
|
|
return (this.asSerialized =
|
|
typeof this.source === "string"
|
|
? this.source
|
|
: mapPoolToSerializedString(this.source));
|
|
}
|
|
|
|
get parsed(): ReadonlyMapPoolObject {
|
|
if (this.asObject !== undefined) {
|
|
return this.asObject;
|
|
}
|
|
|
|
return (this.asObject =
|
|
typeof this.source === "string"
|
|
? serializedStringToMapPool(this.source)
|
|
: this.source);
|
|
}
|
|
|
|
get dbList(): DbMapPoolList {
|
|
return Object.entries(this.parsed).flatMap(([mode, stages]) =>
|
|
stages.flatMap((stageId) => ({ mode: mode as ModeShort, stageId }))
|
|
);
|
|
}
|
|
|
|
get stages() {
|
|
return Object.values(this.parsed).flat();
|
|
}
|
|
|
|
get stageModePairs() {
|
|
return Object.entries(this.parsed).flatMap(([mode, stages]) =>
|
|
stages.map((stageId) => ({ mode: mode as ModeShort, stageId }))
|
|
);
|
|
}
|
|
|
|
has({ stageId, mode }: { stageId: StageId; mode: ModeShort }) {
|
|
return this.parsed[mode].includes(stageId);
|
|
}
|
|
|
|
hasMode(mode: ModeShort): boolean {
|
|
return this.parsed[mode].length > 0;
|
|
}
|
|
|
|
hasStage(stageId: StageId): boolean {
|
|
return Object.values(this.parsed).some((stages) =>
|
|
stages.includes(stageId)
|
|
);
|
|
}
|
|
|
|
overlaps(other: MapPool): boolean {
|
|
return this.stageModePairs.some((pair) => other.has(pair));
|
|
}
|
|
|
|
isEmpty(): boolean {
|
|
return Object.values(this.parsed).every((stages) => stages.length === 0);
|
|
}
|
|
|
|
countMapsByMode(mode: ModeShort): number {
|
|
return this.parsed[mode].length;
|
|
}
|
|
|
|
get length() {
|
|
return this.stageModePairs.length;
|
|
}
|
|
|
|
getClonedObject(): MapPoolObject {
|
|
return clone(this.parsed) as MapPoolObject;
|
|
}
|
|
|
|
toString() {
|
|
return this.serialized;
|
|
}
|
|
|
|
toJSON() {
|
|
return this.parsed;
|
|
}
|
|
|
|
[Symbol.iterator]() {
|
|
var index = -1;
|
|
var data = this.stageModePairs;
|
|
|
|
return {
|
|
next: () => ({ value: data[++index]!, done: !(index in data) }),
|
|
};
|
|
}
|
|
|
|
static EMPTY = new MapPool({
|
|
SZ: [],
|
|
TC: [],
|
|
CB: [],
|
|
RM: [],
|
|
TW: [],
|
|
});
|
|
|
|
static ALL = new MapPool({
|
|
SZ: [...stageIds],
|
|
TC: [...stageIds],
|
|
CB: [...stageIds],
|
|
RM: [...stageIds],
|
|
TW: [...stageIds],
|
|
});
|
|
|
|
static ANARCHY = new MapPool({
|
|
SZ: [...stageIds],
|
|
TC: [...stageIds],
|
|
CB: [...stageIds],
|
|
RM: [...stageIds],
|
|
TW: [],
|
|
});
|
|
|
|
static SZ = new MapPool({
|
|
...MapPool.EMPTY.parsed,
|
|
SZ: [...stageIds],
|
|
});
|
|
static TC = new MapPool({
|
|
...MapPool.EMPTY.parsed,
|
|
TC: [...stageIds],
|
|
});
|
|
static CB = new MapPool({
|
|
...MapPool.EMPTY.parsed,
|
|
CB: [...stageIds],
|
|
});
|
|
static RM = new MapPool({
|
|
...MapPool.EMPTY.parsed,
|
|
RM: [...stageIds],
|
|
});
|
|
static TW = new MapPool({
|
|
...MapPool.EMPTY.parsed,
|
|
TW: [...stageIds],
|
|
});
|
|
}
|