feat(isac): add specialization data management

This commit is contained in:
JB
2026-07-26 16:20:04 -04:00
parent ed31748102
commit 6722665f5c
25 changed files with 3369 additions and 25 deletions
+146 -2
View File
@@ -277,7 +277,7 @@ INSERT OR IGNORE INTO role_permissions(role_id,permission_id) VALUES('role-moder
CREATE TABLE IF NOT EXISTS oauth_states (state_hash TEXT PRIMARY KEY, provider TEXT NOT NULL, mode TEXT NOT NULL, user_id TEXT REFERENCES users(id) ON DELETE CASCADE, expires_at TEXT NOT NULL, created_at TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS data_change_proposals (
id TEXT PRIMARY KEY,
module TEXT NOT NULL CHECK(module IN ('configuration','attributes','talents','brands','gear','weapons','weaponmods','protocols','skillchips')),
module TEXT NOT NULL CHECK(module IN ('configuration','attributes','talents','brands','gear','weapons','weaponmods','gearmods','protocols','skillchips','specializations')),
action TEXT NOT NULL,
entity_id TEXT,
draft_entity_id TEXT,
@@ -322,6 +322,7 @@ CREATE UNIQUE INDEX IF NOT EXISTS users_login_username_nocase_idx ON users(login
CREATE TABLE IF NOT EXISTS stat_attributes (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
type TEXT NOT NULL CHECK(type IN ('Offensive', 'Defensive')),
category TEXT NOT NULL CHECK(category IN ('Weapon', 'Skill', 'Character')),
calculation_type TEXT NOT NULL CHECK(calculation_type IN ('Additive', 'Multiplicative')),
@@ -776,8 +777,151 @@ CREATE TABLE IF NOT EXISTS gear_mod_secondary_quality_values (
FOREIGN KEY(gear_mod_id,stat_attribute_id) REFERENCES gear_mod_secondary_options(gear_mod_id,stat_attribute_id) ON DELETE CASCADE,
CHECK(max_value>=min_value)
);
CREATE TABLE IF NOT EXISTS specialization_progression_config (
id TEXT PRIMARY KEY CHECK(id='default'),
xp_points INTEGER NOT NULL CHECK(xp_points>=0),
shd_cache_points INTEGER NOT NULL CHECK(shd_cache_points>=0),
updated_at TEXT NOT NULL
);
INSERT OR IGNORE INTO specialization_progression_config(id,xp_points,shd_cache_points,updated_at)
VALUES('default',30,19,'2026-07-26T00:00:00.000Z');
CREATE TABLE IF NOT EXISTS specialization_rank_cost_profiles (
rank INTEGER PRIMARY KEY CHECK(rank BETWEEN 1 AND 3),
point_cost INTEGER NOT NULL CHECK(point_cost>0),
credit_cost INTEGER NOT NULL CHECK(credit_cost>=0),
updated_at TEXT NOT NULL
);
INSERT OR IGNORE INTO specialization_rank_cost_profiles(rank,point_cost,credit_cost,updated_at)
VALUES(1,1,2333,'2026-07-26T00:00:00.000Z');
INSERT OR IGNORE INTO specialization_rank_cost_profiles(rank,point_cost,credit_cost,updated_at)
VALUES(2,2,10000,'2026-07-26T00:00:00.000Z');
INSERT OR IGNORE INTO specialization_rank_cost_profiles(rank,point_cost,credit_cost,updated_at)
VALUES(3,3,90000,'2026-07-26T00:00:00.000Z');
CREATE TABLE IF NOT EXISTS upgrade_resources (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
resource_type TEXT NOT NULL CHECK(resource_type IN ('Currency','Component','Progression')),
quality TEXT CHECK(quality IS NULL OR quality IN ('Standard','Specialized','Superior','High-end')),
active INTEGER NOT NULL DEFAULT 1 CHECK(active IN (0,1)),
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS upgrade_resources_name_nocase_idx ON upgrade_resources(name COLLATE NOCASE);
INSERT OR IGNORE INTO upgrade_resources(id,name,resource_type,quality,created_at,updated_at)
VALUES('resource-credits','Credits','Currency',NULL,'2026-07-26T00:00:00.000Z','2026-07-26T00:00:00.000Z');
INSERT OR IGNORE INTO upgrade_resources(id,name,resource_type,quality,created_at,updated_at)
VALUES('resource-electronics-alpha','Electronics Alpha Component','Component','Specialized','2026-07-26T00:00:00.000Z','2026-07-26T00:00:00.000Z');
INSERT OR IGNORE INTO upgrade_resources(id,name,resource_type,quality,created_at,updated_at)
VALUES('resource-electronics-beta','Electronics Beta Component','Component','Specialized','2026-07-26T00:00:00.000Z','2026-07-26T00:00:00.000Z');
INSERT OR IGNORE INTO upgrade_resources(id,name,resource_type,quality,created_at,updated_at)
VALUES('resource-electronics-gamma','Electronics Gamma Component','Component','Specialized','2026-07-26T00:00:00.000Z','2026-07-26T00:00:00.000Z');
INSERT OR IGNORE INTO upgrade_resources(id,name,resource_type,quality,created_at,updated_at)
VALUES('resource-electronics-omega','Electronics Omega Component','Component','High-end','2026-07-26T00:00:00.000Z','2026-07-26T00:00:00.000Z');
INSERT OR IGNORE INTO upgrade_resources(id,name,resource_type,quality,created_at,updated_at)
VALUES('resource-shd-tech-cache','SHD Tech Cache','Progression','Superior','2026-07-26T00:00:00.000Z','2026-07-26T00:00:00.000Z');
CREATE TABLE IF NOT EXISTS specializations (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
active INTEGER NOT NULL DEFAULT 1 CHECK(active IN (0,1)),
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS specializations_name_nocase_idx ON specializations(name COLLATE NOCASE);
CREATE TABLE IF NOT EXISTS specialization_focuses (
id TEXT PRIMARY KEY,
specialization_id TEXT NOT NULL REFERENCES specializations(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT,
primary_attribute TEXT NOT NULL CHECK(primary_attribute IN ('Firepower','Toughness','Engineering')),
display_order INTEGER NOT NULL DEFAULT 0 CHECK(display_order>=0),
builder_ready INTEGER NOT NULL DEFAULT 0 CHECK(builder_ready IN (0,1)),
active INTEGER NOT NULL DEFAULT 1 CHECK(active IN (0,1)),
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(specialization_id,name)
);
CREATE INDEX IF NOT EXISTS specialization_focuses_specialization_idx ON specialization_focuses(specialization_id,display_order,name);
CREATE TABLE IF NOT EXISTS specialization_abilities (
id TEXT PRIMARY KEY,
specialization_id TEXT NOT NULL REFERENCES specializations(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT NOT NULL,
duration_seconds REAL CHECK(duration_seconds IS NULL OR duration_seconds>0),
cooldown_seconds REAL CHECK(cooldown_seconds IS NULL OR cooldown_seconds>0),
active INTEGER NOT NULL DEFAULT 1 CHECK(active IN (0,1)),
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(specialization_id,name)
);
CREATE INDEX IF NOT EXISTS specialization_abilities_specialization_idx ON specialization_abilities(specialization_id,name);
CREATE TABLE IF NOT EXISTS specialization_ability_effects (
id TEXT PRIMARY KEY,
ability_id TEXT NOT NULL REFERENCES specialization_abilities(id) ON DELETE CASCADE,
effect_order INTEGER NOT NULL CHECK(effect_order>=1),
stat_attribute_id TEXT REFERENCES stat_attributes(id),
label TEXT NOT NULL,
value REAL,
unit TEXT,
UNIQUE(ability_id,effect_order)
);
CREATE TABLE IF NOT EXISTS specialization_focus_abilities (
focus_id TEXT NOT NULL REFERENCES specialization_focuses(id) ON DELETE CASCADE,
ability_slot INTEGER NOT NULL CHECK(ability_slot BETWEEN 1 AND 3),
ability_id TEXT NOT NULL REFERENCES specialization_abilities(id),
PRIMARY KEY(focus_id,ability_slot),
UNIQUE(focus_id,ability_id)
);
CREATE TABLE IF NOT EXISTS specialization_tree_nodes (
id TEXT PRIMARY KEY,
focus_id TEXT NOT NULL REFERENCES specialization_focuses(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description_template TEXT NOT NULL,
row_number INTEGER NOT NULL CHECK(row_number>=1),
column_number INTEGER NOT NULL CHECK(column_number>=1),
max_rank INTEGER NOT NULL CHECK(max_rank BETWEEN 1 AND 3),
active INTEGER NOT NULL DEFAULT 1 CHECK(active IN (0,1)),
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(focus_id,row_number,column_number),
UNIQUE(focus_id,name)
);
CREATE INDEX IF NOT EXISTS specialization_tree_nodes_focus_idx ON specialization_tree_nodes(focus_id,row_number,column_number);
CREATE TABLE IF NOT EXISTS specialization_tree_node_ranks (
node_id TEXT NOT NULL REFERENCES specialization_tree_nodes(id) ON DELETE CASCADE,
rank INTEGER NOT NULL CHECK(rank BETWEEN 1 AND 3),
description TEXT NOT NULL,
PRIMARY KEY(node_id,rank)
);
CREATE TABLE IF NOT EXISTS specialization_tree_rank_effects (
id TEXT PRIMARY KEY,
node_id TEXT NOT NULL,
rank INTEGER NOT NULL,
effect_order INTEGER NOT NULL CHECK(effect_order>=1),
effect_type TEXT NOT NULL CHECK(effect_type IN ('Attribute','Ability','Custom')),
stat_attribute_id TEXT REFERENCES stat_attributes(id),
ability_id TEXT REFERENCES specialization_abilities(id),
label TEXT NOT NULL,
value REAL,
unit TEXT,
FOREIGN KEY(node_id,rank) REFERENCES specialization_tree_node_ranks(node_id,rank) ON DELETE CASCADE,
UNIQUE(node_id,rank,effect_order),
CHECK(
(effect_type='Attribute' AND stat_attribute_id IS NOT NULL)
OR (effect_type='Ability' AND ability_id IS NOT NULL)
OR effect_type='Custom'
)
);
CREATE TABLE IF NOT EXISTS specialization_tree_rank_costs (
node_id TEXT NOT NULL,
rank INTEGER NOT NULL,
resource_id TEXT NOT NULL REFERENCES upgrade_resources(id),
quantity INTEGER NOT NULL CHECK(quantity>0),
PRIMARY KEY(node_id,rank,resource_id),
FOREIGN KEY(node_id,rank) REFERENCES specialization_tree_node_ranks(node_id,rank) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS game_data_icons (
entity_type TEXT NOT NULL CHECK(entity_type IN ('talent', 'brand_set', 'os_protocol', 'skill_chip')),
entity_type TEXT NOT NULL CHECK(entity_type IN ('talent', 'brand_set', 'os_protocol', 'skill_chip', 'specialization', 'specialization_ability', 'specialization_node')),
entity_id TEXT NOT NULL,
storage_key TEXT NOT NULL UNIQUE,
byte_size INTEGER NOT NULL CHECK(byte_size > 0),