Skip to content

Commit

Permalink
Combo colour parsing & Skin tinting
Browse files Browse the repository at this point in the history
  • Loading branch information
solstice23 committed Sep 6, 2024
1 parent a111598 commit 4432993
Show file tree
Hide file tree
Showing 23 changed files with 115 additions and 13 deletions.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
3 changes: 3 additions & 0 deletions src/contexts/SettingsContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const SettingsProvider = ({children}) => {

const [volume, volumeRef, setVolume] = useSetting("volume", 1, true);

const [useLegacyDOMRenderer, useLegacyDOMRendererRef, setUseLegacyDOMRenderer] = useSetting("useLegacyDOMrenderer", false, true);

return (
<SettingsContext.Provider value={{
verticalScale, setVerticalScale, verticalScaleRef,
Expand All @@ -33,6 +35,7 @@ export const SettingsProvider = ({children}) => {
showFPS, setShowFPS, showFPSRef,
backgroundDim, setBackgroundDim, backgroundDimRef,
volume, setVolume, volumeRef,
useLegacyDOMRenderer, setUseLegacyDOMRenderer, useLegacyDOMRendererRef,
}}>
{children}
</SettingsContext.Provider>
Expand Down
28 changes: 23 additions & 5 deletions src/contexts/SkinContext.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createContext, useLayoutEffect, useState } from "react";
import { createContext, useCallback, useLayoutEffect, useState } from "react";
import { parsePresetSkin } from "../parser/SkinParser";
import { Assets } from "pixi.js";
import transparentSVG from "../assets/transparent.svg?base64";
Expand All @@ -13,15 +13,20 @@ const loadTransparentTexture = async () => {
}

export const SKinProvider = ({children}) => {
const [skinName, setSkinName] = useState(null);
const [skinID, setSkinID] = useState(null); // Skin ID is either ^default-[a-z]$ or a hash
const [skin, setSkin] = useState(null);
const [skinAssets, setSkinAssets] = useState(null);

const loadSkin = async (skin) => {
console.log("loaded skin", skin);
const loadSkin = async (skin, skinID, skinName) => {
console.log("loaded skin", skinName, skin);

setSkin(skin);
console.log(skin);

setSkinID(skinID);
setSkinName(skinName);

// TODO: Unload previous skin assets

// Load skin as PIXI textures
Expand Down Expand Up @@ -53,11 +58,22 @@ export const SKinProvider = ({children}) => {
console.log("loaded skin assets", textures);
}

const loadPresetSkin = useCallback(async (id) => {
if (id == "default-classic") {
loadSkin(await parsePresetSkin("classic"), "default-classic", "Classic");
} else if (id == "default-simple") {
loadSkin(await parsePresetSkin("simple"), "default-simple", "Simple");
} else {
console.error("unknown skin id for default skin", id);
}
}, []);

useLayoutEffect(() => {
// Load default skin
(async () => {
console.log("loading default skin");
loadSkin(await parsePresetSkin("default"));
await loadPresetSkin("default-classic");
//await loadPresetSkin("default-simple");
})();
}, []);

Expand All @@ -66,7 +82,9 @@ export const SKinProvider = ({children}) => {
<SkinContext.Provider value={{
skin,
loadSkin,
skinAssets
skinAssets,
skinName,
loadPresetSkin,
}}>
{ skin && <SkinCSSLayer skin={skin} /> }
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { calculatePreempt } from "../../../utils/ApproachRate";
import { PlayStateContext } from "../../../contexts/PlayStateContext";
import { CalculateScaleFromCircleSize } from "../../../utils/CalculateCSScale";
import useRefState from "../../../hooks/useRefState";
import "./ObjectsCanvas.scss";
//import "./LegacyObjectsCanvas.scss";

export function ObjectsCanvas({ beatmap, ctbObjects, catcherPath }) {
export function LegacyObjectsCanvas({ beatmap, ctbObjects, catcherPath }) {
const ref = useRef(null);

const [width, widthRef, setWidth] = useRefState(0);
Expand Down
20 changes: 17 additions & 3 deletions src/modules/Main/Playfield/ObjectsCanvas.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export function ObjectsCanvas({
}) {
const ref = useRef(null);

const {skinAssets} = useContext(SkinContext);
const { skin, skinAssets } = useContext(SkinContext);
const skinComboColours = skin?.comboColours ?? [0xffffff];


const { verticalScale,
Expand All @@ -42,6 +43,10 @@ export function ObjectsCanvas({
managerRef.current.setSkinAssets(skinAssets);
}, [skinAssets]);

useEffect(() => {
managerRef.current.setComboColours(skinComboColours);
}, [skinComboColours]);

useEffect(() => {
managerRef.current.setObjects(ctbObjects);
}, [ctbObjects, catcherPath]);
Expand Down Expand Up @@ -171,6 +176,10 @@ class PixiManager {
this.skinAssets = skin;
this.applyToAllFruits(fruit => fruit.updateTexture());
}
setComboColours(comboColours) {
this.comboColours = comboColours;
this.applyToAllFruits(fruit => fruit.updateComboColour());
}
setPlayer(playerRef) {
this.playerRef = playerRef.current;
}
Expand Down Expand Up @@ -290,6 +299,7 @@ class Fruit {
this.setVisiblity(false);

this.updateSize();
this.updateComboColour();
this.updateVisualStyle();

manager.app.stage.addChild(this.sprite);
Expand Down Expand Up @@ -333,8 +343,12 @@ class Fruit {
updateSize() {
this.sprite.width = this.sprite.height = this.overlay.width = this.overlay.height
= this.manager.getFruitSize() *
(this.obj.type === 'droplet' ? 0.5 : 1) *
(this.bananaScale ?? 1);
(this.obj.type === 'droplet' ? 0.5 : 1) *
(this.bananaScale ?? 1);
}
updateComboColour() {
if (this.obj.type === "banana") return;
this.sprite.tint = this.manager.comboColours[this.obj.combo % this.manager.comboColours.length];
}
updateVisualStyle() {
const spriteFilters = [], overlayFilters = [];
Expand Down
8 changes: 7 additions & 1 deletion src/modules/Main/Playfield/Playfield.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import "./Playfield.scss";
import { Grids } from './Grids';
import { ActualPlayfieldBorder } from './ActualPlayfieldBorder';
import { ObjectsCanvas } from './ObjectsCanvas';
import { LegacyObjectsCanvas } from './LegacyObjectsCanvas';
import { AutoCatcher } from './AutoCatcher';

const inDevelopmentBuild = import.meta.env.DEV;
Expand Down Expand Up @@ -46,6 +47,7 @@ export function Playfield({ beatmap }) {
easy,
gameSpeed,
maxSpinLeniency,
useLegacyDOMRenderer,
} = useContext(SettingsContext);

const ctbObjectsKey = JSON.stringify([
Expand Down Expand Up @@ -147,7 +149,11 @@ export function Playfield({ beatmap }) {
>
<Grids />
<ActualPlayfieldBorder />
<ObjectsCanvas beatmap={beatmap} ctbObjects={ctbObjects} catcherPath={bestCatcherPath || catcherPath} />
{
useLegacyDOMRenderer ?
<LegacyObjectsCanvas beatmap={beatmap} ctbObjects={ctbObjects} catcherPath={bestCatcherPath || catcherPath} /> :
<ObjectsCanvas beatmap={beatmap} ctbObjects={ctbObjects} catcherPath={bestCatcherPath || catcherPath} />
}
<AutoCatcher beatmap={beatmap} catcherPath={bestCatcherPath || catcherPath} />
<BananaPathCalculatingOverlay progress={pathCalcProgress} calculating={calculatingPath} />
<StatsOverlay beatmap={beatmap} ctbObjects={ctbObjects} progress={pathCalcProgress} calculating={calculatingPath} />
Expand Down
24 changes: 24 additions & 0 deletions src/modules/Navbar/SettingsPanel.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useContext, useRef, useState } from 'react'
import { MapPackContext } from '../../contexts/MapPackContext'
import { SettingsContext } from '../../contexts/SettingsContext'
import { SkinContext } from '../../contexts/SkinContext'
import { MdRefresh, MdSettings } from "react-icons/md";
import ClickAwayListener from 'react-click-away-listener';
import clsx from 'clsx';
Expand All @@ -18,8 +19,13 @@ export function SettingsPanel () {
gameSpeed, setGameSpeed,
showFPS, setShowFPS,
backgroundDim, setBackgroundDim,
useLegacyDOMRenderer, setUseLegacyDOMRenderer,
} = useContext(SettingsContext);

const {
skinName, skinID, loadPresetSkin
} = useContext(SkinContext);

const [open, setOpen] = useState(false);

const mapPack = useContext(MapPackContext).mapPack;
Expand Down Expand Up @@ -132,6 +138,13 @@ export function SettingsPanel () {
setShowFPS(value);
}}
/>
<Checkbox
label="Use Legacy DOM Renderer"
description="Low performance, not recommended"
value={useLegacyDOMRenderer}
onChange={(value) => setUseLegacyDOMRenderer(value)}
/>
<SkinSelector />
</div>
</button>
</ClickAwayListener>
Expand Down Expand Up @@ -192,4 +205,15 @@ function Mod({ label, acronym, description, semiSelected, value, onChange }) {
<div className="mod-label">{label}</div>
</div>
)
}

function SkinSelector() {
return (
<div className="skin-selector">
<div className="skin-selector-content">
<div className="skin-selector-label">Skin</div>
<div className="skin-selector-value">Default</div>
</div>
</div>
)
}
9 changes: 9 additions & 0 deletions src/parser/HitobjectsParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,16 @@ export const parseHitObjects = (beatmap, hardRock, easy, gameSpeed) => {
applyVisualPresentation(nestedFruits);
applyPositionOffsets(nestedFruits);
applyHyperFruits(nestedFruits, beatmap, hardRock, easy, gameSpeed);
// denest nestedFruits
const fruits = nestedFruits.flatMap((nested) => nested.fruits);
fruits.sort((a, b) => a.time - b.time); // sort again because some maps have simultaneous hitobjects
// apply combo ids
let combo = -1;
for (let i = 0; i < fruits.length; i++) {
if (fruits[i].isNewCombo) combo++;
fruits[i].combo = combo;
}
console.log(fruits);
return fruits;
}

Expand Down Expand Up @@ -332,6 +340,7 @@ const applyHyperFruits = (nestedFruits, beatmap, hardRock, easy, gameSpeed) => {




const getSliderPointByPercent = (slider, percent) => {
const baseX = slider.startPosition.x;
const baseY = slider.startPosition.y;
Expand Down
32 changes: 30 additions & 2 deletions src/parser/SkinParser.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { parseZipFromBuffer } from '../utils/parseZipFromBuffer';
import defaultSkin from "../assets/default-skin.zip?base64";
import classicSkin from "../assets/classic-skin.zip?base64";
import simpleSkin from "../assets/simple-skin.zip?base64";
import { parseZip } from './ZipParser';

Expand Down Expand Up @@ -56,12 +56,40 @@ export async function parseSkinFromZipFile(zipFile) {
}
}

// read combo colours from skin.ini
const skinIni = (await (async () => {
try {
return await zipFile["skin.ini"].async("text");
} catch (e) {
return "";
}
})()).split("\n").map(line => line.trim()).filter(line => line.startsWith("Combo"));

let comboColours = [];

for (let i = 1; i <= 8; i++) {
const line = skinIni.find(line => line.startsWith(`Combo${i}:`));
if (!line) break;
const [r, g, b] = line.split(":")[1].split(",").map(x => parseInt(x));
comboColours.push([r, g, b]);
}
console.log('combo colours', comboColours);
if (!comboColours.length) {
comboColours.push([255, 192, 0]);
comboColours.push([0, 202, 0]);
comboColours.push([18, 124, 255]);
comboColours.push([242, 24, 57]);
}
console.log('combo colours', comboColours);
comboColours = comboColours.map(([r, g, b]) => r * 256 * 256 + g * 256 + b);
skin.comboColours = comboColours;

return skin;
}

export async function parsePresetSkin(name) {
console.log('loading preset skin', name);
const base64 = name === "default" ? defaultSkin : simpleSkin;
const base64 = (name === "classic" ? classicSkin : simpleSkin);
const buffer = atob(base64);
const zipFile = await parseZipFromBuffer(buffer);
return await parseSkinFromZipFile(zipFile);
Expand Down

0 comments on commit 4432993

Please sign in to comment.