All checks were successful
Test and deploy staging / staging (push) Successful in 18s
Fold catchemall into /projects/game-tracker (old URL redirects) and add Call of Duty tracking for Black Ops through WWII. - Pokémon catalog is now owned: locations, encounters and per-game evolution methods are built from the pinned PokéAPI snapshot and seeded. - Pokémon you can evolve or breed in a game count as obtainable; mythicals without another route are marked event-only. - Detail dialog with evolution chain and where to find it, location pages with admin notes and pictures, and admin corrections that survive reseeds. - Call of Duty weapons, milestones and Steam achievements per game, with derived Diamond / Dark Matter / Solar / Black Sky / Chrome tiers. - New migration (db:migrate:game-tracker), seeds (db:seed, db:seed:cod) and deploy preflight checks for them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
128 lines
5.6 KiB
SQL
128 lines
5.6 KiB
SQL
-- Game tracker: owned Pokémon locations/encounters/evolutions and checklist games.
|
|
-- Additive and repeatable. Apply before seeding (`npm run db:seed`, `npm run db:seed:cod`).
|
|
|
|
ALTER TYPE availability ADD VALUE IF NOT EXISTS 'evolve';
|
|
ALTER TYPE availability ADD VALUE IF NOT EXISTS 'breed';
|
|
|
|
ALTER TABLE pokemon ADD COLUMN IF NOT EXISTS evolves_from integer;
|
|
ALTER TABLE pokemon ADD COLUMN IF NOT EXISTS evolution_chain integer;
|
|
ALTER TABLE pokemon ADD COLUMN IF NOT EXISTS is_baby boolean NOT NULL DEFAULT false;
|
|
ALTER TABLE pokemon ADD COLUMN IF NOT EXISTS is_legendary boolean NOT NULL DEFAULT false;
|
|
ALTER TABLE pokemon ADD COLUMN IF NOT EXISTS is_mythical boolean NOT NULL DEFAULT false;
|
|
|
|
ALTER TABLE games ADD COLUMN IF NOT EXISTS slug text;
|
|
ALTER TABLE games ADD COLUMN IF NOT EXISTS sort_order integer NOT NULL DEFAULT 0;
|
|
ALTER TABLE games ADD COLUMN IF NOT EXISTS breeding boolean NOT NULL DEFAULT true;
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'games_slug_key') THEN
|
|
ALTER TABLE games ADD CONSTRAINT games_slug_key UNIQUE (slug);
|
|
END IF;
|
|
END $$;
|
|
|
|
-- Seeds used to mark their own rows with a "PokéAPI:" note prefix; anything else
|
|
-- was hand-maintained and must survive reseeding.
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'pokemon_game_entries' AND column_name = 'source') THEN
|
|
ALTER TABLE pokemon_game_entries ADD COLUMN source text NOT NULL DEFAULT 'pokeapi';
|
|
UPDATE pokemon_game_entries SET source = 'manual' WHERE note IS NULL OR note NOT LIKE 'PokéAPI:%';
|
|
END IF;
|
|
END $$;
|
|
|
|
CREATE TABLE IF NOT EXISTS pokemon_locations (
|
|
id serial PRIMARY KEY,
|
|
source_id integer UNIQUE,
|
|
slug text NOT NULL,
|
|
name text NOT NULL,
|
|
region text,
|
|
description text,
|
|
image_key text,
|
|
image_credit text,
|
|
source text NOT NULL DEFAULT 'pokeapi',
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS pokemon_encounters (
|
|
id serial PRIMARY KEY,
|
|
pokemon_id integer NOT NULL,
|
|
game_id integer NOT NULL,
|
|
location_id integer NOT NULL,
|
|
area text,
|
|
method text NOT NULL,
|
|
min_level integer,
|
|
max_level integer,
|
|
rarity integer,
|
|
form text,
|
|
conditions text[] NOT NULL DEFAULT '{}'::text[],
|
|
note text,
|
|
source text NOT NULL DEFAULT 'pokeapi',
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT pokemon_encounters_pokemon_id_pokemon_id_fkey FOREIGN KEY (pokemon_id) REFERENCES pokemon(id) ON DELETE CASCADE,
|
|
CONSTRAINT pokemon_encounters_game_id_games_id_fkey FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE,
|
|
CONSTRAINT pokemon_encounters_location_id_pokemon_locations_id_fkey FOREIGN KEY (location_id) REFERENCES pokemon_locations(id) ON DELETE CASCADE
|
|
);
|
|
CREATE INDEX IF NOT EXISTS pokemon_encounters_pokemon_id_game_id_index ON pokemon_encounters (pokemon_id, game_id);
|
|
CREATE INDEX IF NOT EXISTS pokemon_encounters_location_id_index ON pokemon_encounters (location_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS pokemon_evolutions (
|
|
id serial PRIMARY KEY,
|
|
from_pokemon_id integer NOT NULL,
|
|
to_pokemon_id integer NOT NULL,
|
|
game_id integer NOT NULL,
|
|
method text NOT NULL,
|
|
source text NOT NULL DEFAULT 'pokeapi',
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT pokemon_evolutions_from_pokemon_id_to_pokemon_id_game_id_method_unique UNIQUE (from_pokemon_id, to_pokemon_id, game_id, method),
|
|
CONSTRAINT pokemon_evolutions_from_pokemon_id_pokemon_id_fkey FOREIGN KEY (from_pokemon_id) REFERENCES pokemon(id) ON DELETE CASCADE,
|
|
CONSTRAINT pokemon_evolutions_to_pokemon_id_pokemon_id_fkey FOREIGN KEY (to_pokemon_id) REFERENCES pokemon(id) ON DELETE CASCADE,
|
|
CONSTRAINT pokemon_evolutions_game_id_games_id_fkey FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
);
|
|
CREATE INDEX IF NOT EXISTS pokemon_evolutions_game_id_index ON pokemon_evolutions (game_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS tracker_games (
|
|
id serial PRIMARY KEY,
|
|
slug text NOT NULL UNIQUE,
|
|
franchise text NOT NULL,
|
|
name text NOT NULL,
|
|
short_name text NOT NULL,
|
|
release_year integer,
|
|
sort_order integer NOT NULL DEFAULT 0,
|
|
rules jsonb NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS tracker_items (
|
|
id serial PRIMARY KEY,
|
|
game_id integer NOT NULL,
|
|
key text NOT NULL,
|
|
kind text NOT NULL,
|
|
category text NOT NULL,
|
|
name text NOT NULL,
|
|
description text,
|
|
icon text,
|
|
post_launch boolean NOT NULL DEFAULT false,
|
|
sort_order integer NOT NULL DEFAULT 0,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT tracker_items_game_id_key_unique UNIQUE (game_id, key),
|
|
CONSTRAINT tracker_items_game_id_tracker_games_id_fkey FOREIGN KEY (game_id) REFERENCES tracker_games(id) ON DELETE CASCADE
|
|
);
|
|
CREATE INDEX IF NOT EXISTS tracker_items_game_id_index ON tracker_items (game_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS tracker_progress (
|
|
id serial PRIMARY KEY,
|
|
user_id text NOT NULL,
|
|
item_id integer NOT NULL,
|
|
milestone text NOT NULL DEFAULT 'done',
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
CONSTRAINT tracker_progress_user_id_item_id_milestone_unique UNIQUE (user_id, item_id, milestone),
|
|
CONSTRAINT tracker_progress_user_id_user_id_fkey FOREIGN KEY (user_id) REFERENCES "user"(id) ON DELETE CASCADE,
|
|
CONSTRAINT tracker_progress_item_id_tracker_items_id_fkey FOREIGN KEY (item_id) REFERENCES tracker_items(id) ON DELETE CASCADE
|
|
);
|
|
CREATE INDEX IF NOT EXISTS tracker_progress_user_id_index ON tracker_progress (user_id);
|