Improve shop UX, starting balance, and top bar branding.

Give new users 100 free points, allow shop previews regardless of balance, rename clothing items with descriptive labels, and show only the HaBit logo in the top-left.
This commit is contained in:
2026-05-24 04:51:01 -06:00
parent 69131ac8eb
commit 5dda46cb87
12 changed files with 286 additions and 49 deletions

View File

@@ -1,3 +1,4 @@
import { DEFAULT_STARTING_COINS } from "@/lib/coins";
import { notifyHabitPetDataUpdated } from "@/lib/app-events";
import {
hexToHsl,
@@ -204,6 +205,7 @@ export async function getAvatarCustomization(): Promise<AvatarCustomization> {
if (!data) {
const { error: insertError } = await supabase.from("avatar_state").insert({
user_id: userId,
coins: DEFAULT_STARTING_COINS,
avatar_name: DEFAULT_AVATAR_NAME,
skin_color: DEFAULT_HEX,
pants_style: NONE_VARIANT_ID,

View File

@@ -1,3 +1,4 @@
import { DEFAULT_STARTING_COINS } from "@/lib/coins";
import { createClient } from "@/lib/supabase/client";
async function getAuthenticatedUserId() {
@@ -35,13 +36,13 @@ export async function getCoins(): Promise<number> {
if (!data) {
const { error: insertError } = await supabase
.from("avatar_state")
.insert({ user_id: userId, coins: 0 });
.insert({ user_id: userId, coins: DEFAULT_STARTING_COINS });
if (insertError) {
throw new Error(insertError.message);
}
return 0;
return DEFAULT_STARTING_COINS;
}
return data.coins ?? 0;

View File

@@ -59,6 +59,33 @@ function presetFromHex(id: string, name: string, hex: string): ColorPreset {
return presetFromHsl(id, name, rgbToHsl(rgb.r, rgb.g, rgb.b));
}
export const VARIANT_DISPLAY_NAMES: Record<string, string> = {
"skin-1": "Default Form",
"pants-1": "Slim Fit Jeans",
"pants-2": "Rolled Cuff Shorts",
"pants-3": "Utility Cargo Pants",
"shoes-1": "Canvas Sneakers",
"shoes-2": "Chunky High-Tops",
"shoes-3": "Neon Glow Runners",
"torso-1": "Crew Neck Tee",
"torso-2": "Zip-Up Hoodie",
"torso-3": "Cropped Tank Top",
"torso-4": "Crimson Plate Armor",
"eyes-1": "Round Gentle Eyes",
"eyes-2": "Starry Spark Eyes",
"head-1": "Spiked Mohawk",
"head-2": "Cozy Knit Beanie",
"head-3": "Retro Snapback Cap",
"head-4": "Fiery Side Mohawk",
"head-5": "Crested Warrior Helm",
"head-6": "Flowing Side Locks",
"head-7": "Messy Shag Cut",
};
export function getVariantDisplayName(variantId: string, fallbackLabel = variantId) {
return VARIANT_DISPLAY_NAMES[variantId] ?? fallbackLabel;
}
function layerVariants(
layerId: Exclude<CharacterLayerId, "skin">,
label: string,
@@ -66,9 +93,10 @@ function layerVariants(
): LayerVariant[] {
return Array.from({ length: count }, (_, index) => {
const number = index + 1;
const id = `${layerId}-${number}`;
return {
id: `${layerId}-${number}`,
label: `${label} ${number}`,
id,
label: getVariantDisplayName(id, `${label} ${number}`),
src: `/character/${layerId}/${layerId}-${number}.png`,
};
});
@@ -117,7 +145,11 @@ export const CHARACTER_LAYERS: CharacterLayer[] = [
label: "Skin",
allowVariants: false,
variants: [
{ id: "skin-1", label: "Skin 1", src: "/character/skin/skin-1.png" },
{
id: "skin-1",
label: getVariantDisplayName("skin-1", "Skin 1"),
src: "/character/skin/skin-1.png",
},
],
},
{

View File

@@ -1,3 +1,5 @@
export const DEFAULT_STARTING_COINS = 100;
export const COINS_PER_TASK_COMPLETE = 50;
export const COINS_PER_TASK_UNCHECK = -50;