add game tracker with owned pokemon data and call of duty mastery
All checks were successful
Test and deploy staging / staging (push) Successful in 18s
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>
This commit is contained in:
127
scripts/game-tracker-migration.sql
Normal file
127
scripts/game-tracker-migration.sql
Normal file
@@ -0,0 +1,127 @@
|
||||
-- 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);
|
||||
Reference in New Issue
Block a user