generated from nhcarrigan/template
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
621f594018
|
|||
| 1e845b14ce |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@elysium/api",
|
"name": "@elysium/api",
|
||||||
"version": "0.1.2",
|
"version": "0.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./prod/src/index.js",
|
"main": "./prod/src/index.js",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@elysium/web",
|
"name": "@elysium/web",
|
||||||
"version": "0.1.2",
|
"version": "0.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -158,15 +158,6 @@ const howToPlay = [
|
|||||||
+ " is visible on your public profile page.",
|
+ " is visible on your public profile page.",
|
||||||
title: "📋 Character Sheet",
|
title: "📋 Character Sheet",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
body:
|
|
||||||
"Customise your adventurer's appearance from the Character tab. Choose"
|
|
||||||
+ " your skin tone, hair style, hair colour, outfit, and accessory."
|
|
||||||
+ " Your paper doll is displayed in the sidebar for you to see at all"
|
|
||||||
+ " times. Appearance settings are purely cosmetic and persist through"
|
|
||||||
+ " prestige and transcendence resets.",
|
|
||||||
title: "🎨 Paper Doll",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
body:
|
body:
|
||||||
"Earn Titles by reaching milestones — defeating bosses, completing"
|
"Earn Titles by reaching milestones — defeating bosses, completing"
|
||||||
|
|||||||
@@ -143,6 +143,10 @@ const AdventurerCard = ({
|
|||||||
{" essence/s each"}
|
{" essence/s each"}
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
|
<p>
|
||||||
|
{formatNumber(adventurer.combatPower)}
|
||||||
|
{" combat power each"}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="adventurer-count">
|
<div className="adventurer-count">
|
||||||
{"×"}
|
{"×"}
|
||||||
|
|||||||
@@ -267,6 +267,23 @@ const BossPanel = (): JSX.Element => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { zones, bosses, quests, autoBoss, prestige: playerPrestige } = state;
|
const { zones, bosses, quests, autoBoss, prestige: playerPrestige } = state;
|
||||||
|
|
||||||
|
const activeZone = zones.find((zone) => {
|
||||||
|
return zone.id === activeZoneId;
|
||||||
|
});
|
||||||
|
const zoneIsLocked = activeZone?.status === "locked";
|
||||||
|
const unlockBoss = activeZone?.unlockBossId === null
|
||||||
|
|| activeZone?.unlockBossId === undefined
|
||||||
|
? undefined
|
||||||
|
: bosses.find((boss) => {
|
||||||
|
return boss.id === activeZone.unlockBossId;
|
||||||
|
});
|
||||||
|
const unlockQuest = activeZone?.unlockQuestId === null
|
||||||
|
|| activeZone?.unlockQuestId === undefined
|
||||||
|
? undefined
|
||||||
|
: quests.find((quest) => {
|
||||||
|
return quest.id === activeZone.unlockQuestId;
|
||||||
|
});
|
||||||
const zoneBosses = bosses.filter((boss) => {
|
const zoneBosses = bosses.filter((boss) => {
|
||||||
return boss.zoneId === activeZoneId;
|
return boss.zoneId === activeZoneId;
|
||||||
});
|
});
|
||||||
@@ -393,6 +410,27 @@ const BossPanel = (): JSX.Element => {
|
|||||||
zones={zones}
|
zones={zones}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{zoneIsLocked && (unlockBoss !== undefined || unlockQuest !== undefined)
|
||||||
|
? <div className="exploration-zone-locked-hint">
|
||||||
|
<p>{"🔒 This zone is locked. Unlock bosses by:"}</p>
|
||||||
|
{unlockBoss === undefined
|
||||||
|
? null
|
||||||
|
: <p>
|
||||||
|
{"⚔️ Defeat: "}
|
||||||
|
{unlockBoss.name}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
{unlockQuest === undefined
|
||||||
|
? null
|
||||||
|
: <p>
|
||||||
|
{"📜 Complete: "}
|
||||||
|
{unlockQuest.name}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
<div className="party-combat-stats">
|
<div className="party-combat-stats">
|
||||||
<div className="combat-stat">
|
<div className="combat-stat">
|
||||||
<span className="stat-label">{"⚔️ Party DPS"}</span>
|
<span className="stat-label">{"⚔️ Party DPS"}</span>
|
||||||
|
|||||||
@@ -9,10 +9,8 @@
|
|||||||
/* eslint-disable max-statements -- Component requires many state declarations */
|
/* eslint-disable max-statements -- Component requires many state declarations */
|
||||||
/* eslint-disable max-lines -- Large component with editing and view modes */
|
/* eslint-disable max-lines -- Large component with editing and view modes */
|
||||||
import {
|
import {
|
||||||
defaultAppearance,
|
|
||||||
DEFAULT_PROFILE_SETTINGS,
|
DEFAULT_PROFILE_SETTINGS,
|
||||||
STORY_CHAPTERS,
|
STORY_CHAPTERS,
|
||||||
type Appearance,
|
|
||||||
type EquipmentBonus,
|
type EquipmentBonus,
|
||||||
type EquipmentRarity,
|
type EquipmentRarity,
|
||||||
type EquipmentType,
|
type EquipmentType,
|
||||||
@@ -89,7 +87,7 @@ const formatBonus = (bonus: EquipmentBonus): string => {
|
|||||||
* @returns The JSX element.
|
* @returns The JSX element.
|
||||||
*/
|
*/
|
||||||
const CharacterSheetPanel = (): JSX.Element => {
|
const CharacterSheetPanel = (): JSX.Element => {
|
||||||
const { state, loginStreak, updateAppearance } = useGame();
|
const { state, loginStreak } = useGame();
|
||||||
const player = state?.player;
|
const player = state?.player;
|
||||||
|
|
||||||
const [ sheet, setSheet ] = useState<CharacterSheetData>(emptySheet);
|
const [ sheet, setSheet ] = useState<CharacterSheetData>(emptySheet);
|
||||||
@@ -278,35 +276,6 @@ const CharacterSheetPanel = (): JSX.Element => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentAppearance = state?.appearance ?? defaultAppearance;
|
|
||||||
|
|
||||||
function handleAppearanceChange(
|
|
||||||
field: keyof Appearance,
|
|
||||||
value: string,
|
|
||||||
): void {
|
|
||||||
updateAppearance({ ...currentAppearance, [field]: value });
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSkinToneChange(event: ChangeEvent<HTMLSelectElement>): void {
|
|
||||||
handleAppearanceChange("skinTone", event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleHairStyleChange(event: ChangeEvent<HTMLSelectElement>): void {
|
|
||||||
handleAppearanceChange("hairStyle", event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleHairColourChange(event: ChangeEvent<HTMLSelectElement>): void {
|
|
||||||
handleAppearanceChange("hairColour", event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleOutfitChange(event: ChangeEvent<HTMLSelectElement>): void {
|
|
||||||
handleAppearanceChange("outfit", event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleAccessoryChange(event: ChangeEvent<HTMLSelectElement>): void {
|
|
||||||
handleAppearanceChange("accessory", event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<section className="panel">
|
<section className="panel">
|
||||||
@@ -604,116 +573,6 @@ const CharacterSheetPanel = (): JSX.Element => {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="character-sheet-section">
|
|
||||||
<h3 className="character-sheet-section-title">
|
|
||||||
{"🎨 Appearance"}
|
|
||||||
</h3>
|
|
||||||
<p className="character-sheet-hint">
|
|
||||||
{"Customise your adventurer's look. Changes save automatically."}
|
|
||||||
</p>
|
|
||||||
<div className="appearance-editor">
|
|
||||||
<label
|
|
||||||
className="character-sheet-label"
|
|
||||||
htmlFor="appearance-skin-tone"
|
|
||||||
>
|
|
||||||
{"Skin Tone"}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
className="character-sheet-select"
|
|
||||||
id="appearance-skin-tone"
|
|
||||||
onChange={handleSkinToneChange}
|
|
||||||
value={currentAppearance.skinTone}
|
|
||||||
>
|
|
||||||
<option value="pale">{"Pale"}</option>
|
|
||||||
<option value="light">{"Light"}</option>
|
|
||||||
<option value="tan">{"Tan"}</option>
|
|
||||||
<option value="medium">{"Medium"}</option>
|
|
||||||
<option value="dark">{"Dark"}</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<label
|
|
||||||
className="character-sheet-label"
|
|
||||||
htmlFor="appearance-hair-style"
|
|
||||||
>
|
|
||||||
{"Hair Style"}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
className="character-sheet-select"
|
|
||||||
id="appearance-hair-style"
|
|
||||||
onChange={handleHairStyleChange}
|
|
||||||
value={currentAppearance.hairStyle}
|
|
||||||
>
|
|
||||||
<option value="short">{"Short"}</option>
|
|
||||||
<option value="shoulder">{"Shoulder-length"}</option>
|
|
||||||
<option value="long">{"Long"}</option>
|
|
||||||
<option value="ponytail">{"Ponytail"}</option>
|
|
||||||
<option value="twintails">{"Twin Tails"}</option>
|
|
||||||
<option value="bun">{"Bun"}</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<label
|
|
||||||
className="character-sheet-label"
|
|
||||||
htmlFor="appearance-hair-colour"
|
|
||||||
>
|
|
||||||
{"Hair Colour"}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
className="character-sheet-select"
|
|
||||||
id="appearance-hair-colour"
|
|
||||||
onChange={handleHairColourChange}
|
|
||||||
value={currentAppearance.hairColour}
|
|
||||||
>
|
|
||||||
<option value="brown">{"Brown"}</option>
|
|
||||||
<option value="black">{"Black"}</option>
|
|
||||||
<option value="blonde">{"Blonde"}</option>
|
|
||||||
<option value="red">{"Red"}</option>
|
|
||||||
<option value="auburn">{"Auburn"}</option>
|
|
||||||
<option value="silver">{"Silver"}</option>
|
|
||||||
<option value="blue">{"Blue"}</option>
|
|
||||||
<option value="purple">{"Purple"}</option>
|
|
||||||
<option value="pink">{"Pink"}</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<label
|
|
||||||
className="character-sheet-label"
|
|
||||||
htmlFor="appearance-outfit"
|
|
||||||
>
|
|
||||||
{"Outfit"}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
className="character-sheet-select"
|
|
||||||
id="appearance-outfit"
|
|
||||||
onChange={handleOutfitChange}
|
|
||||||
value={currentAppearance.outfit}
|
|
||||||
>
|
|
||||||
<option value="warrior">{"Warrior"}</option>
|
|
||||||
<option value="mage">{"Mage"}</option>
|
|
||||||
<option value="rogue">{"Rogue"}</option>
|
|
||||||
<option value="archer">{"Archer"}</option>
|
|
||||||
<option value="bard">{"Bard"}</option>
|
|
||||||
<option value="ranger">{"Ranger"}</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<label
|
|
||||||
className="character-sheet-label"
|
|
||||||
htmlFor="appearance-accessory"
|
|
||||||
>
|
|
||||||
{"Accessory"}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
className="character-sheet-select"
|
|
||||||
id="appearance-accessory"
|
|
||||||
onChange={handleAccessoryChange}
|
|
||||||
value={currentAppearance.accessory}
|
|
||||||
>
|
|
||||||
<option value="none">{"None"}</option>
|
|
||||||
<option value="glasses">{"Glasses"}</option>
|
|
||||||
<option value="hat">{"Hat"}</option>
|
|
||||||
<option value="cape">{"Cape"}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="character-sheet-section">
|
<div className="character-sheet-section">
|
||||||
<h3 className="character-sheet-section-title">{"🗡️ Equipment"}</h3>
|
<h3 className="character-sheet-section-title">{"🗡️ Equipment"}</h3>
|
||||||
{sheet.equippedItems.length > 0
|
{sheet.equippedItems.length > 0
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import { LoginBonusModal } from "./loginBonusModal.js";
|
|||||||
import { MilestoneToast } from "./milestoneToast.js";
|
import { MilestoneToast } from "./milestoneToast.js";
|
||||||
import { OfflineModal } from "./offlineModal.js";
|
import { OfflineModal } from "./offlineModal.js";
|
||||||
import { OutdatedSchemaModal } from "./outdatedSchemaModal.js";
|
import { OutdatedSchemaModal } from "./outdatedSchemaModal.js";
|
||||||
import { PaperDoll } from "./paperDoll.js";
|
|
||||||
import { PrestigePanel } from "./prestigePanel.js";
|
import { PrestigePanel } from "./prestigePanel.js";
|
||||||
import { QuestPanel } from "./questPanel.js";
|
import { QuestPanel } from "./questPanel.js";
|
||||||
import { QuestCompleteToast, QuestFailedToast } from "./questToast.js";
|
import { QuestCompleteToast, QuestFailedToast } from "./questToast.js";
|
||||||
@@ -194,7 +193,6 @@ const GameLayout = (): JSX.Element => {
|
|||||||
<aside className="game-sidebar">
|
<aside className="game-sidebar">
|
||||||
<ClickArea />
|
<ClickArea />
|
||||||
<div id="tree-nation-offset-website" />
|
<div id="tree-nation-offset-website" />
|
||||||
<PaperDoll />
|
|
||||||
<p className="game-copyright">{"© NHCarrigan"}</p>
|
<p className="game-copyright">{"© NHCarrigan"}</p>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Paper doll component for displaying layered adventurer appearance.
|
|
||||||
* @copyright nhcarrigan
|
|
||||||
* @license Naomi's Public License
|
|
||||||
* @author Hikari
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
defaultAppearance,
|
|
||||||
type HairColour,
|
|
||||||
type SkinTone,
|
|
||||||
} from "@elysium/types";
|
|
||||||
import { useGame } from "../../context/gameContext.js";
|
|
||||||
import { cdnImage } from "../../utils/cdn.js";
|
|
||||||
import type { JSX } from "react";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSS filter strings for each skin tone, applied to the base body layer.
|
|
||||||
* Uses brightness + sepia + saturation to shift the neutral base skin.
|
|
||||||
*/
|
|
||||||
const skinToneFilters: Record<SkinTone, string> = {
|
|
||||||
dark: "brightness(0.55) saturate(0.55) sepia(0.5) contrast(1.1)",
|
|
||||||
light: "brightness(0.98) saturate(0.4) sepia(0.12)",
|
|
||||||
medium: "brightness(0.74) saturate(0.75) sepia(0.42)",
|
|
||||||
pale: "brightness(1.05) saturate(0.2) sepia(0.08)",
|
|
||||||
tan: "brightness(0.88) saturate(0.65) sepia(0.28)",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSS filter strings for each hair colour.
|
|
||||||
* Applied to the greyscale hair layer via sepia + hue-rotate tinting.
|
|
||||||
*/
|
|
||||||
const hairColourFilters: Record<HairColour, string> = {
|
|
||||||
auburn: "sepia(1) saturate(3) hue-rotate(350deg)",
|
|
||||||
black: "brightness(0.15)",
|
|
||||||
blonde: "sepia(1) saturate(3) hue-rotate(5deg) brightness(1.6)",
|
|
||||||
blue: "sepia(1) saturate(5) hue-rotate(190deg)",
|
|
||||||
brown: "sepia(1) saturate(2) hue-rotate(0deg)",
|
|
||||||
pink: "sepia(1) saturate(5) hue-rotate(305deg)",
|
|
||||||
purple: "sepia(1) saturate(5) hue-rotate(245deg)",
|
|
||||||
red: "sepia(1) saturate(4) hue-rotate(345deg)",
|
|
||||||
silver: "grayscale(1) brightness(1.9) contrast(0.8)",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the paper doll — a layered composite of body, outfit, hair, and
|
|
||||||
* accessory images that together represent the player's adventurer appearance.
|
|
||||||
* All layers use mix-blend-mode: multiply so white backgrounds become
|
|
||||||
* transparent, allowing the layers to composite cleanly.
|
|
||||||
* @returns The JSX element.
|
|
||||||
*/
|
|
||||||
const PaperDoll = (): JSX.Element => {
|
|
||||||
const { state } = useGame();
|
|
||||||
const appearance = state?.appearance ?? defaultAppearance;
|
|
||||||
const { skinTone, hairStyle, hairColour, outfit, accessory } = appearance;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="paper-doll">
|
|
||||||
{/* Base body — skin-toneable */}
|
|
||||||
<img
|
|
||||||
alt=""
|
|
||||||
className="paper-doll-layer paper-doll-body"
|
|
||||||
src={cdnImage("paper-doll", "body")}
|
|
||||||
style={{ filter: skinToneFilters[skinTone] }}
|
|
||||||
/>
|
|
||||||
{/* Outfit layer */}
|
|
||||||
<img
|
|
||||||
alt=""
|
|
||||||
className="paper-doll-layer paper-doll-outfit"
|
|
||||||
src={cdnImage("paper-doll", `outfit-${outfit}`)}
|
|
||||||
/>
|
|
||||||
{/* Hair layer — colour-tintable greyscale */}
|
|
||||||
<img
|
|
||||||
alt=""
|
|
||||||
className="paper-doll-layer paper-doll-hair"
|
|
||||||
src={cdnImage("paper-doll", `hair-${hairStyle}`)}
|
|
||||||
style={{ filter: hairColourFilters[hairColour] }}
|
|
||||||
/>
|
|
||||||
{accessory === "none"
|
|
||||||
? null
|
|
||||||
: <img
|
|
||||||
alt=""
|
|
||||||
className="paper-doll-layer paper-doll-accessory"
|
|
||||||
src={cdnImage("paper-doll", `accessory-${accessory}`)}
|
|
||||||
/>}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export { PaperDoll };
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
* @license Naomi's Public License
|
* @license Naomi's Public License
|
||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
/* eslint-disable max-lines -- QuestPanel with sub-component and helper functions */
|
||||||
/* eslint-disable react/no-multi-comp -- QuestCard sub-component is tightly coupled */
|
/* eslint-disable react/no-multi-comp -- QuestCard sub-component is tightly coupled */
|
||||||
/* eslint-disable max-lines-per-function -- Complex component with many render paths */
|
/* eslint-disable max-lines-per-function -- Complex component with many render paths */
|
||||||
/* eslint-disable complexity -- Many conditional render paths */
|
/* eslint-disable complexity -- Many conditional render paths */
|
||||||
@@ -148,8 +149,7 @@ const QuestCard = ({
|
|||||||
&& <p className="quest-failure-chance">
|
&& <p className="quest-failure-chance">
|
||||||
{"🎲 "}
|
{"🎲 "}
|
||||||
{String(Math.round((zoneFailureChance[quest.zoneId] ?? 0) * 100))}
|
{String(Math.round((zoneFailureChance[quest.zoneId] ?? 0) * 100))}
|
||||||
{"% failure chance — if failed, the quest resets"}
|
{"% failure chance"}
|
||||||
{" and must be retried."}
|
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
{quest.status === "available" && quest.lastFailedAt !== undefined
|
{quest.status === "available" && quest.lastFailedAt !== undefined
|
||||||
@@ -208,7 +208,24 @@ const QuestPanel = (): JSX.Element => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { adventurers, autoQuest, quests, zones } = state;
|
const { adventurers, autoQuest, bosses, quests, zones } = state;
|
||||||
|
|
||||||
|
const activeZone = zones.find((zone) => {
|
||||||
|
return zone.id === activeZoneId;
|
||||||
|
});
|
||||||
|
const zoneIsLocked = activeZone?.status === "locked";
|
||||||
|
const unlockBoss = activeZone?.unlockBossId === null
|
||||||
|
|| activeZone?.unlockBossId === undefined
|
||||||
|
? undefined
|
||||||
|
: bosses.find((boss) => {
|
||||||
|
return boss.id === activeZone.unlockBossId;
|
||||||
|
});
|
||||||
|
const unlockQuest = activeZone?.unlockQuestId === null
|
||||||
|
|| activeZone?.unlockQuestId === undefined
|
||||||
|
? undefined
|
||||||
|
: quests.find((quest) => {
|
||||||
|
return quest.id === activeZone.unlockQuestId;
|
||||||
|
});
|
||||||
let partyCombatPower = 0;
|
let partyCombatPower = 0;
|
||||||
for (const adventurer of adventurers) {
|
for (const adventurer of adventurers) {
|
||||||
const contribution = adventurer.combatPower * adventurer.count;
|
const contribution = adventurer.combatPower * adventurer.count;
|
||||||
@@ -307,6 +324,31 @@ const QuestPanel = (): JSX.Element => {
|
|||||||
zones={zones}
|
zones={zones}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{zoneIsLocked && (unlockBoss !== undefined || unlockQuest !== undefined)
|
||||||
|
? <div className="exploration-zone-locked-hint">
|
||||||
|
<p>{"🔒 This zone is locked. Unlock quests by:"}</p>
|
||||||
|
{unlockBoss === undefined
|
||||||
|
? null
|
||||||
|
: <p>
|
||||||
|
{"⚔️ Defeat: "}
|
||||||
|
{unlockBoss.name}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
{unlockQuest === undefined
|
||||||
|
? null
|
||||||
|
: <p>
|
||||||
|
{"📜 Complete: "}
|
||||||
|
{unlockQuest.name}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
<p className="quest-failure-note">
|
||||||
|
{"⚠️ If a quest fails, it resets with no rewards — you must retry."}
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className="quest-list">
|
<div className="quest-list">
|
||||||
{visibleQuests.map((quest) => {
|
{visibleQuests.map((quest) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
import {
|
import {
|
||||||
STORY_CHAPTERS,
|
STORY_CHAPTERS,
|
||||||
type Achievement,
|
type Achievement,
|
||||||
type Appearance,
|
|
||||||
type ApotheosisResponse,
|
type ApotheosisResponse,
|
||||||
type BossChallengeResponse,
|
type BossChallengeResponse,
|
||||||
type ExploreCollectResponse,
|
type ExploreCollectResponse,
|
||||||
@@ -453,12 +452,6 @@ interface GameContextValue {
|
|||||||
*/
|
*/
|
||||||
toggleAutoAdventurer: ()=> void;
|
toggleAutoAdventurer: ()=> void;
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the player's paper doll appearance customisation.
|
|
||||||
* @param appearance - The new appearance settings.
|
|
||||||
*/
|
|
||||||
updateAppearance: (appearance: Appearance)=> void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue of newly unlocked codex entry IDs (for toast notifications).
|
* Queue of newly unlocked codex entry IDs (for toast notifications).
|
||||||
*/
|
*/
|
||||||
@@ -1917,15 +1910,6 @@ export const GameProvider = ({
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const updateAppearance = useCallback((appearance: Appearance) => {
|
|
||||||
setState((previous) => {
|
|
||||||
if (previous === null) {
|
|
||||||
return previous;
|
|
||||||
}
|
|
||||||
return { ...previous, appearance };
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const setActiveCompanion = useCallback((companionId: string | null) => {
|
const setActiveCompanion = useCallback((companionId: string | null) => {
|
||||||
setState((previous) => {
|
setState((previous) => {
|
||||||
if (previous === null) {
|
if (previous === null) {
|
||||||
@@ -2255,7 +2239,6 @@ export const GameProvider = ({
|
|||||||
unlockedAchievements,
|
unlockedAchievements,
|
||||||
unlockedCodexEntryIds,
|
unlockedCodexEntryIds,
|
||||||
unlockedStoryChapterIds,
|
unlockedStoryChapterIds,
|
||||||
updateAppearance,
|
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
apotheosis,
|
apotheosis,
|
||||||
@@ -2328,7 +2311,6 @@ export const GameProvider = ({
|
|||||||
unlockedAchievements,
|
unlockedAchievements,
|
||||||
unlockedCodexEntryIds,
|
unlockedCodexEntryIds,
|
||||||
unlockedStoryChapterIds,
|
unlockedStoryChapterIds,
|
||||||
updateAppearance,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "elysium",
|
"name": "elysium",
|
||||||
"version": "0.1.2",
|
"version": "0.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@elysium/types",
|
"name": "@elysium/types",
|
||||||
"version": "0.1.2",
|
"version": "0.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./prod/src/index.js",
|
"main": "./prod/src/index.js",
|
||||||
|
|||||||
@@ -5,15 +5,6 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
export type { ApotheosisData } from "./interfaces/apotheosis.js";
|
export type { ApotheosisData } from "./interfaces/apotheosis.js";
|
||||||
export type {
|
|
||||||
Accessory,
|
|
||||||
Appearance,
|
|
||||||
HairColour,
|
|
||||||
HairStyle,
|
|
||||||
Outfit,
|
|
||||||
SkinTone,
|
|
||||||
} from "./interfaces/appearance.js";
|
|
||||||
export { defaultAppearance } from "./interfaces/appearance.js";
|
|
||||||
export type {
|
export type {
|
||||||
Companion,
|
Companion,
|
||||||
CompanionBonus,
|
CompanionBonus,
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Appearance type for the paper doll customisation system.
|
|
||||||
* @copyright nhcarrigan
|
|
||||||
* @license Naomi's Public License
|
|
||||||
* @author Hikari
|
|
||||||
*/
|
|
||||||
|
|
||||||
type SkinTone = "pale" | "light" | "tan" | "medium" | "dark";
|
|
||||||
|
|
||||||
type HairStyle =
|
|
||||||
| "short"
|
|
||||||
| "shoulder"
|
|
||||||
| "long"
|
|
||||||
| "ponytail"
|
|
||||||
| "twintails"
|
|
||||||
| "bun";
|
|
||||||
|
|
||||||
type HairColour =
|
|
||||||
| "brown"
|
|
||||||
| "black"
|
|
||||||
| "blonde"
|
|
||||||
| "red"
|
|
||||||
| "auburn"
|
|
||||||
| "silver"
|
|
||||||
| "blue"
|
|
||||||
| "purple"
|
|
||||||
| "pink";
|
|
||||||
|
|
||||||
type Outfit = "warrior" | "mage" | "rogue" | "archer" | "bard" | "ranger";
|
|
||||||
|
|
||||||
type Accessory = "none" | "glasses" | "hat" | "cape";
|
|
||||||
|
|
||||||
interface Appearance {
|
|
||||||
skinTone: SkinTone;
|
|
||||||
hairStyle: HairStyle;
|
|
||||||
hairColour: HairColour;
|
|
||||||
outfit: Outfit;
|
|
||||||
accessory: Accessory;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultAppearance: Appearance = {
|
|
||||||
accessory: "none",
|
|
||||||
hairColour: "brown",
|
|
||||||
hairStyle: "short",
|
|
||||||
outfit: "warrior",
|
|
||||||
skinTone: "pale",
|
|
||||||
};
|
|
||||||
|
|
||||||
export type { Accessory, Appearance, HairColour, HairStyle, Outfit, SkinTone };
|
|
||||||
export { defaultAppearance };
|
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
import type { Achievement } from "./achievement.js";
|
import type { Achievement } from "./achievement.js";
|
||||||
import type { Adventurer } from "./adventurer.js";
|
import type { Adventurer } from "./adventurer.js";
|
||||||
import type { ApotheosisData } from "./apotheosis.js";
|
import type { ApotheosisData } from "./apotheosis.js";
|
||||||
import type { Appearance } from "./appearance.js";
|
|
||||||
import type { Boss } from "./boss.js";
|
import type { Boss } from "./boss.js";
|
||||||
import type { CodexState } from "./codex.js";
|
import type { CodexState } from "./codex.js";
|
||||||
import type { CompanionState } from "./companion.js";
|
import type { CompanionState } from "./companion.js";
|
||||||
@@ -99,12 +98,6 @@ interface GameState {
|
|||||||
* Schema version — used to detect saves from older game versions.
|
* Schema version — used to detect saves from older game versions.
|
||||||
*/
|
*/
|
||||||
schemaVersion?: number;
|
schemaVersion?: number;
|
||||||
|
|
||||||
/**
|
|
||||||
* Paper doll appearance customisation — optional for backwards compatibility.
|
|
||||||
* Persists across prestige and transcendence resets.
|
|
||||||
*/
|
|
||||||
appearance?: Appearance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type { GameState };
|
export type { GameState };
|
||||||
|
|||||||
Reference in New Issue
Block a user