From 7150810579235dc40744efee6c02d6ac3a78c6a6 Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:24:14 -0700 Subject: [PATCH 1/6] [sql] Add Job Point plumbing to blue traits sql Also annotate existing traits --- sql/blue_traits.sql | 94 +++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/sql/blue_traits.sql b/sql/blue_traits.sql index 1547194b562..32e11aa6300 100644 --- a/sql/blue_traits.sql +++ b/sql/blue_traits.sql @@ -9,54 +9,56 @@ CREATE TABLE `blue_traits` ( `traitid` tinyint(3) unsigned NOT NULL, `modifier` smallint(5) unsigned NOT NULL, `value` smallint(5) NOT NULL, - PRIMARY KEY (`trait_category`,`trait_points_needed`,`modifier`) + `tier` tinyint(3) unsigned NOT NULL, + `job_points_only` tinyint(1) unsigned NOT NULL, + PRIMARY KEY (`trait_category`,`trait_points_needed`,`modifier`,`tier`) ) ENGINE=Aria TRANSACTIONAL=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- ---------------------------- -- Records -- ---------------------------- -INSERT INTO `blue_traits` VALUES (1,2,32,230,8); -INSERT INTO `blue_traits` VALUES (2,2,9,370,1); -INSERT INTO `blue_traits` VALUES (3,2,35,227,8); -INSERT INTO `blue_traits` VALUES (4,2,24,295,1); -INSERT INTO `blue_traits` VALUES (4,4,24,295,2); -INSERT INTO `blue_traits` VALUES (4,6,24,295,3); -INSERT INTO `blue_traits` VALUES (4,8,24,295,4); -INSERT INTO `blue_traits` VALUES (5,2,48,240,2); -INSERT INTO `blue_traits` VALUES (6,2,5,28,20); -INSERT INTO `blue_traits` VALUES (7,2,39,231,8); -INSERT INTO `blue_traits` VALUES (8,2,3,23,10); -INSERT INTO `blue_traits` VALUES (8,2,3,24,10); -INSERT INTO `blue_traits` VALUES (9,2,11,359,10); -INSERT INTO `blue_traits` VALUES (10,2,8,5,10); -INSERT INTO `blue_traits` VALUES (10,4,8,5,30); -INSERT INTO `blue_traits` VALUES (11,2,4,1,10); -INSERT INTO `blue_traits` VALUES (12,2,33,229,8); -INSERT INTO `blue_traits` VALUES (13,2,6,29,10); -INSERT INTO `blue_traits` VALUES (14,8,10,369,1); -INSERT INTO `blue_traits` VALUES (15,8,7,1095,30); -- Max HP Boost 1 -INSERT INTO `blue_traits` VALUES (15,16,7,1095,60); -- Max HP Boost 2 -INSERT INTO `blue_traits` VALUES (15,24,7,1095,120); -- Max HP Boost 3 -INSERT INTO `blue_traits` VALUES (15,32,7,1095,180); -- Max HP Boost 4 --- INSERT INTO `blue_traits` VALUES (15,4,7,1095,240); -- Max HP Boost 5 TODO: Through BLU Job Point Gift 100 "Job Trait Bonus" --- INSERT INTO `blue_traits` VALUES (15,4,7,1095,280); -- Max HP Boost 6 TODO: Through BLU Job Point Gift 1200 "Job Trait Bonus" -INSERT INTO `blue_traits` VALUES (16,2,1,25,10); -INSERT INTO `blue_traits` VALUES (16,2,1,26,10); -INSERT INTO `blue_traits` VALUES (17,2,13,296,25); -INSERT INTO `blue_traits` VALUES (18,2,2,68,10); -INSERT INTO `blue_traits` VALUES (19,2,58,249,2); -INSERT INTO `blue_traits` VALUES (20,2,14,73,10); -INSERT INTO `blue_traits` VALUES (20,4,14,73,25); -INSERT INTO `blue_traits` VALUES (21,2,17,291,10); -INSERT INTO `blue_traits` VALUES (22,2,12,170,5); -INSERT INTO `blue_traits` VALUES (22,4,12,170,15); -INSERT INTO `blue_traits` VALUES (23,2,106,174,8); -INSERT INTO `blue_traits` VALUES (24,2,15,288,7); -INSERT INTO `blue_traits` VALUES (24,4,16,302,5); -INSERT INTO `blue_traits` VALUES (25,2,18,259,10); -INSERT INTO `blue_traits` VALUES (25,4,18,259,15); -INSERT INTO `blue_traits` VALUES (25,6,18,259,25); -INSERT INTO `blue_traits` VALUES (26,2,70,306,15); -INSERT INTO `blue_traits` VALUES (27,2,110,487,5); -INSERT INTO `blue_traits` VALUES (28,2,20,897,1); -INSERT INTO `blue_traits` VALUES (28,3,19,303,1); +INSERT INTO `blue_traits` VALUES (1,2,32,230,8,0,0); -- Beast Killer (1) +INSERT INTO `blue_traits` VALUES (2,2,9,370,1,0,0); -- Auto Regen (1) +INSERT INTO `blue_traits` VALUES (3,2,35,227,8,0,0); -- Lizard Killer (1) +INSERT INTO `blue_traits` VALUES (4,2,24,295,1,0,0); -- Clear Mind (1) +INSERT INTO `blue_traits` VALUES (4,4,24,295,2,1,0); -- Clear Mind (2) +INSERT INTO `blue_traits` VALUES (4,6,24,295,3,2,0); -- Clear Mind (3) +INSERT INTO `blue_traits` VALUES (4,8,24,295,4,3,0); -- Clear Mind (4) +INSERT INTO `blue_traits` VALUES (5,2,48,240,2,0,0); -- Reist Sleep (1) +INSERT INTO `blue_traits` VALUES (6,2,5,28,20,0,0); -- Magic Attack Bonus (1) +INSERT INTO `blue_traits` VALUES (7,2,39,231,8,0,0); -- Undead Killer (1) +INSERT INTO `blue_traits` VALUES (8,2,3,23,10,0,0); -- Attack Bonus (1) +INSERT INTO `blue_traits` VALUES (8,2,3,24,10,0,0); -- Attack Bonus (1) +INSERT INTO `blue_traits` VALUES (9,2,11,359,10,0,0); -- Rapid Shot (1) +INSERT INTO `blue_traits` VALUES (10,2,8,5,10,0,0); -- Max MP Boost (1) +INSERT INTO `blue_traits` VALUES (10,4,8,5,30,1,0); -- Max MP Boost (2) +INSERT INTO `blue_traits` VALUES (11,2,4,1,10,0,0); -- Defense Bonus (1) +INSERT INTO `blue_traits` VALUES (12,2,33,229,8,0,0); -- Plantoid Killer (1) +INSERT INTO `blue_traits` VALUES (13,2,6,29,10,0,0); -- Magic Defense Bonus (1) +INSERT INTO `blue_traits` VALUES (14,8,10,369,1,0,0); -- Auto Refresh (1) +INSERT INTO `blue_traits` VALUES (15,8,7,1095,30,0,0); -- Max HP Boost (1) +INSERT INTO `blue_traits` VALUES (15,16,7,1095,60,1,0); -- Max HP Boost (2) +INSERT INTO `blue_traits` VALUES (15,24,7,1095,120,2,0); -- Max HP Boost (3) +INSERT INTO `blue_traits` VALUES (15,32,7,1095,180,3,0); -- Max HP Boost (4) +INSERT INTO `blue_traits` VALUES (15,4,7,1095,240,4,1); -- Max HP Boost (5) +INSERT INTO `blue_traits` VALUES (15,4,7,1095,280,5,1); -- Max HP Boost (6) +INSERT INTO `blue_traits` VALUES (16,2,1,25,10,0,0); -- Accuracy Bonus (1) +INSERT INTO `blue_traits` VALUES (16,2,1,26,10,0,0); -- Accuracy Bonus (1) +INSERT INTO `blue_traits` VALUES (17,2,13,296,25,0,0); -- Conserve MP (1) +INSERT INTO `blue_traits` VALUES (18,2,2,68,10,0,0); -- Evasion Bonus (1) +INSERT INTO `blue_traits` VALUES (19,2,58,249,2,0,0); -- Resist Gravity (1) +INSERT INTO `blue_traits` VALUES (20,2,14,73,10,0,0); -- Store TP (1) +INSERT INTO `blue_traits` VALUES (20,4,14,73,25,1,0); -- Store TP (2) +INSERT INTO `blue_traits` VALUES (21,2,17,291,10,0,0); -- Counter (1) +INSERT INTO `blue_traits` VALUES (22,2,12,170,5,0,0); -- Fast Cast (1) +INSERT INTO `blue_traits` VALUES (22,4,12,170,15,1,0); -- Fast Cast (2) +INSERT INTO `blue_traits` VALUES (23,2,106,174,8,0,0); -- Skillchain Bonus (1) +INSERT INTO `blue_traits` VALUES (24,2,15,288,7,0,0); -- Double Attack (1) +INSERT INTO `blue_traits` VALUES (24,4,16,302,5,1,0); -- Triple Attack (1) +INSERT INTO `blue_traits` VALUES (25,2,18,259,10,0,0); -- Dual Wield (1) +INSERT INTO `blue_traits` VALUES (25,4,18,259,15,1,0); -- Dual Wield (2) +INSERT INTO `blue_traits` VALUES (25,6,18,259,25,2,0); -- Dual Wield (3) +INSERT INTO `blue_traits` VALUES (26,2,70,306,15,0,0); -- Zanshin (1) +INSERT INTO `blue_traits` VALUES (27,2,110,487,5,0,0); -- Magic Burst Bonus (1) +INSERT INTO `blue_traits` VALUES (28,2,20,897,1,0,0); -- Gilfinder (1) +INSERT INTO `blue_traits` VALUES (28,3,19,303,1,1,0); -- Treasure Hunter (1) From 1491ee68e7076976177c29582e3530164b9b15ba Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:34:15 -0700 Subject: [PATCH 2/6] [sql] Audit BLU trait values --- sql/blue_traits.sql | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sql/blue_traits.sql b/sql/blue_traits.sql index 32e11aa6300..9735f395beb 100644 --- a/sql/blue_traits.sql +++ b/sql/blue_traits.sql @@ -24,14 +24,14 @@ INSERT INTO `blue_traits` VALUES (4,2,24,295,1,0,0); -- Clear Mind (1) INSERT INTO `blue_traits` VALUES (4,4,24,295,2,1,0); -- Clear Mind (2) INSERT INTO `blue_traits` VALUES (4,6,24,295,3,2,0); -- Clear Mind (3) INSERT INTO `blue_traits` VALUES (4,8,24,295,4,3,0); -- Clear Mind (4) -INSERT INTO `blue_traits` VALUES (5,2,48,240,2,0,0); -- Reist Sleep (1) +INSERT INTO `blue_traits` VALUES (5,2,48,240,10,0,0); -- Resist Sleep (1) INSERT INTO `blue_traits` VALUES (6,2,5,28,20,0,0); -- Magic Attack Bonus (1) INSERT INTO `blue_traits` VALUES (7,2,39,231,8,0,0); -- Undead Killer (1) INSERT INTO `blue_traits` VALUES (8,2,3,23,10,0,0); -- Attack Bonus (1) INSERT INTO `blue_traits` VALUES (8,2,3,24,10,0,0); -- Attack Bonus (1) -INSERT INTO `blue_traits` VALUES (9,2,11,359,10,0,0); -- Rapid Shot (1) +INSERT INTO `blue_traits` VALUES (9,2,11,359,25,0,0); -- Rapid Shot (1) INSERT INTO `blue_traits` VALUES (10,2,8,5,10,0,0); -- Max MP Boost (1) -INSERT INTO `blue_traits` VALUES (10,4,8,5,30,1,0); -- Max MP Boost (2) +INSERT INTO `blue_traits` VALUES (10,4,8,5,20,1,0); -- Max MP Boost (2) INSERT INTO `blue_traits` VALUES (11,2,4,1,10,0,0); -- Defense Bonus (1) INSERT INTO `blue_traits` VALUES (12,2,33,229,8,0,0); -- Plantoid Killer (1) INSERT INTO `blue_traits` VALUES (13,2,6,29,10,0,0); -- Magic Defense Bonus (1) @@ -40,18 +40,18 @@ INSERT INTO `blue_traits` VALUES (15,8,7,1095,30,0,0); -- Max HP Boost (1) INSERT INTO `blue_traits` VALUES (15,16,7,1095,60,1,0); -- Max HP Boost (2) INSERT INTO `blue_traits` VALUES (15,24,7,1095,120,2,0); -- Max HP Boost (3) INSERT INTO `blue_traits` VALUES (15,32,7,1095,180,3,0); -- Max HP Boost (4) -INSERT INTO `blue_traits` VALUES (15,4,7,1095,240,4,1); -- Max HP Boost (5) -INSERT INTO `blue_traits` VALUES (15,4,7,1095,280,5,1); -- Max HP Boost (6) +INSERT INTO `blue_traits` VALUES (15,4,7,1095,240,4,1); -- Max HP Boost (5) (JP only) +INSERT INTO `blue_traits` VALUES (15,4,7,1095,280,5,1); -- Max HP Boost (6) (JP only) INSERT INTO `blue_traits` VALUES (16,2,1,25,10,0,0); -- Accuracy Bonus (1) INSERT INTO `blue_traits` VALUES (16,2,1,26,10,0,0); -- Accuracy Bonus (1) INSERT INTO `blue_traits` VALUES (17,2,13,296,25,0,0); -- Conserve MP (1) INSERT INTO `blue_traits` VALUES (18,2,2,68,10,0,0); -- Evasion Bonus (1) -INSERT INTO `blue_traits` VALUES (19,2,58,249,2,0,0); -- Resist Gravity (1) +INSERT INTO `blue_traits` VALUES (19,2,58,249,10,0,0); -- Resist Gravity (1) INSERT INTO `blue_traits` VALUES (20,2,14,73,10,0,0); -- Store TP (1) -INSERT INTO `blue_traits` VALUES (20,4,14,73,25,1,0); -- Store TP (2) +INSERT INTO `blue_traits` VALUES (20,4,14,73,15,1,0); -- Store TP (2) INSERT INTO `blue_traits` VALUES (21,2,17,291,10,0,0); -- Counter (1) INSERT INTO `blue_traits` VALUES (22,2,12,170,5,0,0); -- Fast Cast (1) -INSERT INTO `blue_traits` VALUES (22,4,12,170,15,1,0); -- Fast Cast (2) +INSERT INTO `blue_traits` VALUES (22,4,12,170,10,1,0); -- Fast Cast (2) INSERT INTO `blue_traits` VALUES (23,2,106,174,8,0,0); -- Skillchain Bonus (1) INSERT INTO `blue_traits` VALUES (24,2,15,288,7,0,0); -- Double Attack (1) INSERT INTO `blue_traits` VALUES (24,4,16,302,5,1,0); -- Triple Attack (1) From ba068604cc2c7e5a62929b20928a575210d94b3a Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:57:12 -0700 Subject: [PATCH 3/6] [sql] Adjust ranks of Blue Magic traits to match normal ones --- sql/blue_traits.sql | 86 ++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/sql/blue_traits.sql b/sql/blue_traits.sql index 9735f395beb..8925e60c0ed 100644 --- a/sql/blue_traits.sql +++ b/sql/blue_traits.sql @@ -17,48 +17,48 @@ CREATE TABLE `blue_traits` ( -- ---------------------------- -- Records -- ---------------------------- -INSERT INTO `blue_traits` VALUES (1,2,32,230,8,0,0); -- Beast Killer (1) -INSERT INTO `blue_traits` VALUES (2,2,9,370,1,0,0); -- Auto Regen (1) -INSERT INTO `blue_traits` VALUES (3,2,35,227,8,0,0); -- Lizard Killer (1) -INSERT INTO `blue_traits` VALUES (4,2,24,295,1,0,0); -- Clear Mind (1) -INSERT INTO `blue_traits` VALUES (4,4,24,295,2,1,0); -- Clear Mind (2) -INSERT INTO `blue_traits` VALUES (4,6,24,295,3,2,0); -- Clear Mind (3) -INSERT INTO `blue_traits` VALUES (4,8,24,295,4,3,0); -- Clear Mind (4) -INSERT INTO `blue_traits` VALUES (5,2,48,240,10,0,0); -- Resist Sleep (1) -INSERT INTO `blue_traits` VALUES (6,2,5,28,20,0,0); -- Magic Attack Bonus (1) -INSERT INTO `blue_traits` VALUES (7,2,39,231,8,0,0); -- Undead Killer (1) -INSERT INTO `blue_traits` VALUES (8,2,3,23,10,0,0); -- Attack Bonus (1) -INSERT INTO `blue_traits` VALUES (8,2,3,24,10,0,0); -- Attack Bonus (1) -INSERT INTO `blue_traits` VALUES (9,2,11,359,25,0,0); -- Rapid Shot (1) -INSERT INTO `blue_traits` VALUES (10,2,8,5,10,0,0); -- Max MP Boost (1) -INSERT INTO `blue_traits` VALUES (10,4,8,5,20,1,0); -- Max MP Boost (2) -INSERT INTO `blue_traits` VALUES (11,2,4,1,10,0,0); -- Defense Bonus (1) -INSERT INTO `blue_traits` VALUES (12,2,33,229,8,0,0); -- Plantoid Killer (1) -INSERT INTO `blue_traits` VALUES (13,2,6,29,10,0,0); -- Magic Defense Bonus (1) -INSERT INTO `blue_traits` VALUES (14,8,10,369,1,0,0); -- Auto Refresh (1) -INSERT INTO `blue_traits` VALUES (15,8,7,1095,30,0,0); -- Max HP Boost (1) -INSERT INTO `blue_traits` VALUES (15,16,7,1095,60,1,0); -- Max HP Boost (2) -INSERT INTO `blue_traits` VALUES (15,24,7,1095,120,2,0); -- Max HP Boost (3) -INSERT INTO `blue_traits` VALUES (15,32,7,1095,180,3,0); -- Max HP Boost (4) -INSERT INTO `blue_traits` VALUES (15,4,7,1095,240,4,1); -- Max HP Boost (5) (JP only) -INSERT INTO `blue_traits` VALUES (15,4,7,1095,280,5,1); -- Max HP Boost (6) (JP only) -INSERT INTO `blue_traits` VALUES (16,2,1,25,10,0,0); -- Accuracy Bonus (1) -INSERT INTO `blue_traits` VALUES (16,2,1,26,10,0,0); -- Accuracy Bonus (1) -INSERT INTO `blue_traits` VALUES (17,2,13,296,25,0,0); -- Conserve MP (1) -INSERT INTO `blue_traits` VALUES (18,2,2,68,10,0,0); -- Evasion Bonus (1) -INSERT INTO `blue_traits` VALUES (19,2,58,249,10,0,0); -- Resist Gravity (1) -INSERT INTO `blue_traits` VALUES (20,2,14,73,10,0,0); -- Store TP (1) -INSERT INTO `blue_traits` VALUES (20,4,14,73,15,1,0); -- Store TP (2) -INSERT INTO `blue_traits` VALUES (21,2,17,291,10,0,0); -- Counter (1) -INSERT INTO `blue_traits` VALUES (22,2,12,170,5,0,0); -- Fast Cast (1) -INSERT INTO `blue_traits` VALUES (22,4,12,170,10,1,0); -- Fast Cast (2) -INSERT INTO `blue_traits` VALUES (23,2,106,174,8,0,0); -- Skillchain Bonus (1) -INSERT INTO `blue_traits` VALUES (24,2,15,288,7,0,0); -- Double Attack (1) +INSERT INTO `blue_traits` VALUES (1,2,32,230,8,1,0); -- Beast Killer (1) +INSERT INTO `blue_traits` VALUES (2,2,9,370,1,1,0); -- Auto Regen (1) +INSERT INTO `blue_traits` VALUES (3,2,35,227,8,1,0); -- Lizard Killer (1) +INSERT INTO `blue_traits` VALUES (4,2,24,295,1,1,0); -- Clear Mind (1) +INSERT INTO `blue_traits` VALUES (4,4,24,295,2,2,0); -- Clear Mind (2) +INSERT INTO `blue_traits` VALUES (4,6,24,295,3,3,0); -- Clear Mind (3) +INSERT INTO `blue_traits` VALUES (4,8,24,295,4,4,0); -- Clear Mind (4) +INSERT INTO `blue_traits` VALUES (5,2,48,240,10,1,0); -- Resist Sleep (1) +INSERT INTO `blue_traits` VALUES (6,2,5,28,20,1,0); -- Magic Attack Bonus (1) +INSERT INTO `blue_traits` VALUES (7,2,39,231,8,1,0); -- Undead Killer (1) +INSERT INTO `blue_traits` VALUES (8,2,3,23,10,1,0); -- Attack Bonus (1) +INSERT INTO `blue_traits` VALUES (8,2,3,24,10,1,0); -- Attack Bonus (1) +INSERT INTO `blue_traits` VALUES (9,2,11,359,25,1,0); -- Rapid Shot (1) +INSERT INTO `blue_traits` VALUES (10,2,8,5,10,1,0); -- Max MP Boost (1) +INSERT INTO `blue_traits` VALUES (10,4,8,5,20,2,0); -- Max MP Boost (2) +INSERT INTO `blue_traits` VALUES (11,2,4,1,10,1,0); -- Defense Bonus (1) +INSERT INTO `blue_traits` VALUES (12,2,33,229,8,1,0); -- Plantoid Killer (1) +INSERT INTO `blue_traits` VALUES (13,2,6,29,10,1,0); -- Magic Defense Bonus (1) +INSERT INTO `blue_traits` VALUES (14,8,10,369,1,1,0); -- Auto Refresh (1) +INSERT INTO `blue_traits` VALUES (15,8,7,1095,30,1,0); -- Max HP Boost (1) +INSERT INTO `blue_traits` VALUES (15,16,7,1095,60,2,0); -- Max HP Boost (2) +INSERT INTO `blue_traits` VALUES (15,24,7,1095,120,3,0); -- Max HP Boost (3) +INSERT INTO `blue_traits` VALUES (15,32,7,1095,180,4,0); -- Max HP Boost (4) +INSERT INTO `blue_traits` VALUES (15,4,7,1095,240,5,1); -- Max HP Boost (5) (JP only) +INSERT INTO `blue_traits` VALUES (15,4,7,1095,280,6,1); -- Max HP Boost (6) (JP only) +INSERT INTO `blue_traits` VALUES (16,2,1,25,10,1,0); -- Accuracy Bonus (1) +INSERT INTO `blue_traits` VALUES (16,2,1,26,10,1,0); -- Accuracy Bonus (1) +INSERT INTO `blue_traits` VALUES (17,2,13,296,25,1,0); -- Conserve MP (1) +INSERT INTO `blue_traits` VALUES (18,2,2,68,10,1,0); -- Evasion Bonus (1) +INSERT INTO `blue_traits` VALUES (19,2,58,249,10,1,0); -- Resist Gravity (1) +INSERT INTO `blue_traits` VALUES (20,2,14,73,10,1,0); -- Store TP (1) +INSERT INTO `blue_traits` VALUES (20,4,14,73,15,2,0); -- Store TP (2) +INSERT INTO `blue_traits` VALUES (21,2,17,291,10,1,0); -- Counter (1) +INSERT INTO `blue_traits` VALUES (22,2,12,170,5,1,0); -- Fast Cast (1) +INSERT INTO `blue_traits` VALUES (22,4,12,170,10,2,0); -- Fast Cast (2) +INSERT INTO `blue_traits` VALUES (23,2,106,174,8,1,0); -- Skillchain Bonus (1) +INSERT INTO `blue_traits` VALUES (24,2,15,288,7,0,0); -- Double Attack (0) -- Tier Zero because this is weaker than WAR double attack (1). It is BLU exclusive INSERT INTO `blue_traits` VALUES (24,4,16,302,5,1,0); -- Triple Attack (1) -INSERT INTO `blue_traits` VALUES (25,2,18,259,10,0,0); -- Dual Wield (1) -INSERT INTO `blue_traits` VALUES (25,4,18,259,15,1,0); -- Dual Wield (2) -INSERT INTO `blue_traits` VALUES (25,6,18,259,25,2,0); -- Dual Wield (3) -INSERT INTO `blue_traits` VALUES (26,2,70,306,15,0,0); -- Zanshin (1) -INSERT INTO `blue_traits` VALUES (27,2,110,487,5,0,0); -- Magic Burst Bonus (1) -INSERT INTO `blue_traits` VALUES (28,2,20,897,1,0,0); -- Gilfinder (1) +INSERT INTO `blue_traits` VALUES (25,2,18,259,10,1,0); -- Dual Wield (1) +INSERT INTO `blue_traits` VALUES (25,4,18,259,15,2,0); -- Dual Wield (2) +INSERT INTO `blue_traits` VALUES (25,6,18,259,25,3,0); -- Dual Wield (3) +INSERT INTO `blue_traits` VALUES (26,2,70,306,15,1,0); -- Zanshin (1) +INSERT INTO `blue_traits` VALUES (27,2,110,487,5,1,0); -- Magic Burst Bonus (1) +INSERT INTO `blue_traits` VALUES (28,2,20,897,1,1,0); -- Gilfinder (1) INSERT INTO `blue_traits` VALUES (28,3,19,303,1,1,0); -- Treasure Hunter (1) From 57908cf31557948c71689bc8b09d6d3a3b7ad270 Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:32:12 -0700 Subject: [PATCH 4/6] [sql] Add blue mage Store TP 3-5 traits --- sql/blue_traits.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sql/blue_traits.sql b/sql/blue_traits.sql index 8925e60c0ed..7d0dbc48af3 100644 --- a/sql/blue_traits.sql +++ b/sql/blue_traits.sql @@ -49,6 +49,9 @@ INSERT INTO `blue_traits` VALUES (18,2,2,68,10,1,0); -- Evasion Bonus (1) INSERT INTO `blue_traits` VALUES (19,2,58,249,10,1,0); -- Resist Gravity (1) INSERT INTO `blue_traits` VALUES (20,2,14,73,10,1,0); -- Store TP (1) INSERT INTO `blue_traits` VALUES (20,4,14,73,15,2,0); -- Store TP (2) +INSERT INTO `blue_traits` VALUES (20,6,14,73,20,3,0); -- Store TP (3) +INSERT INTO `blue_traits` VALUES (20,8,14,73,25,4,1); -- Store TP (4) (JP onry) +INSERT INTO `blue_traits` VALUES (20,10,14,73,30,5,1); -- Store TP (5) (JP onry) INSERT INTO `blue_traits` VALUES (21,2,17,291,10,1,0); -- Counter (1) INSERT INTO `blue_traits` VALUES (22,2,12,170,5,1,0); -- Fast Cast (1) INSERT INTO `blue_traits` VALUES (22,4,12,170,10,2,0); -- Fast Cast (2) From f6c4a4da5da122273455fad392c66f7ba84209d1 Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:36:35 -0700 Subject: [PATCH 5/6] [core] Support Blue Mage job trait gifts --- src/map/blue_trait.cpp | 8 +- src/map/blue_trait.h | 4 +- src/map/trait.cpp | 8 +- src/map/utils/blueutils.cpp | 200 ++++++++++++++++++++++++++---------- 4 files changed, 161 insertions(+), 59 deletions(-) diff --git a/src/map/blue_trait.cpp b/src/map/blue_trait.cpp index bae4da52edb..74df4d0c7c0 100644 --- a/src/map/blue_trait.cpp +++ b/src/map/blue_trait.cpp @@ -21,10 +21,11 @@ #include "blue_trait.h" -CBlueTrait::CBlueTrait(uint8 category, uint8 id) +CBlueTrait::CBlueTrait(uint8 category, uint8 id, bool jobPointsOnly) : CTrait(id) , m_points(0) , m_category(category) +, m_jobPointsOnly(jobPointsOnly) { } @@ -38,6 +39,11 @@ uint8 CBlueTrait::getPoints() const return m_points; } +uint8 CBlueTrait::getJobPointsOnly() const +{ + return m_jobPointsOnly; +} + void CBlueTrait::setPoints(uint8 points) { m_points = points; diff --git a/src/map/blue_trait.h b/src/map/blue_trait.h index d854f17114b..d28d22a9200 100644 --- a/src/map/blue_trait.h +++ b/src/map/blue_trait.h @@ -27,16 +27,18 @@ class CBlueTrait : public CTrait { public: - CBlueTrait(uint8 category, uint8 id); + CBlueTrait(uint8 category, uint8 id, bool jobPointsOnly); void setPoints(uint8 points); uint8 getCategory() const; uint8 getPoints() const; + uint8 getJobPointsOnly() const; private: uint8 m_points; uint8 m_category; + bool m_jobPointsOnly; }; #endif diff --git a/src/map/trait.cpp b/src/map/trait.cpp index faf669d713d..91867525648 100644 --- a/src/map/trait.cpp +++ b/src/map/trait.cpp @@ -80,17 +80,17 @@ void LoadTraitsList() PTraitsList[PTrait->getJob()].emplace_back(PTrait); } - rset = db::preparedStmt("SELECT trait_category, trait_points_needed, traitid, modifier, value " + rset = db::preparedStmt("SELECT trait_category, trait_points_needed, traitid, modifier, value, tier, job_points_only " "FROM blue_traits " "WHERE traitid < ? " - "ORDER BY trait_category ASC, trait_points_needed DESC", + "ORDER BY tier ASC", MAX_TRAIT_ID); FOR_DB_MULTIPLE_RESULTS(rset) { - auto* PTrait = new CBlueTrait(rset->get("trait_category"), rset->get("traitid")); + auto* PTrait = new CBlueTrait(rset->get("trait_category"), rset->get("traitid"), rset->get("job_points_only")); PTrait->setJob(JOB_BLU); - PTrait->setRank(1); + PTrait->setRank(rset->get("tier")); PTrait->setPoints(rset->get("trait_points_needed")); PTrait->setMod(rset->get("modifier")); PTrait->setValue(rset->get("value")); diff --git a/src/map/utils/blueutils.cpp b/src/map/utils/blueutils.cpp index 697b8879c49..ae2d54004e8 100644 --- a/src/map/utils/blueutils.cpp +++ b/src/map/utils/blueutils.cpp @@ -420,11 +420,13 @@ void ValidateBlueSpells(CCharEntity* PChar) // then loops over all blue traits to see if they match the eligible traits // each eligible blue trait is compared against existing job traits // if a higher-tier blue trait is found to be valid, lower is removed +// ***note*** this function assumes Blue Traits are added with `tier` in ascending order to reduce complexity void CalculateTraits(CCharEntity* PChar) { TraitList_t* PTraitsList = traits::GetTraits(JOB_BLU); std::map points; std::vector traitsToAdd; + auto traitTierBonus = PChar->getMod(Mod::BLUE_JOB_TRAIT_BONUS); for (unsigned char m_SetBlueSpell : PChar->m_SetBlueSpells) { @@ -450,6 +452,7 @@ void CalculateTraits(CCharEntity* PChar) } } + // Add BLU traits that are eligible for (auto& point : points) { uint8 category = point.first; @@ -462,70 +465,161 @@ void CalculateTraits(CCharEntity* PChar) CBlueTrait* PTrait = (CBlueTrait*)i; // Player is eligible for this Blue Trait - if (PTrait && PTrait->getCategory() == category && totalWeight >= PTrait->getPoints()) + if (PTrait && PTrait->getCategory() == category && totalWeight >= PTrait->getPoints() && !PTrait->getJobPointsOnly()) { - bool add = true; - - // Check if any existing player Traits conflict - for (std::size_t j = 0; j < PChar->TraitList.size(); ++j) + // Check all the eligible Blue Traits for conflicts + for (auto it = traitsToAdd.begin(); it != traitsToAdd.end();) { - CTrait* PExistingTrait = PChar->TraitList.at(j); + auto currentTrait = *it; - if (PExistingTrait->getID() == PTrait->getID()) + // Same trait ID, trait mod, and is weaker than new trait + if (currentTrait->getID() == PTrait->getID() && currentTrait->getRank() <= PTrait->getRank() && currentTrait->getMod() == PTrait->getMod()) { - // Player has the real job trait, making them ineligible - // TODO remove the trait and add the blu trait if it's stronger - if (PExistingTrait->getLevel() > 0) - { - add = false; - break; - } + // Erase lower tier trait + it = traitsToAdd.erase(it); } - } - - if (add) - { - // Check all the eligible Blue Traits for conflicts - std::size_t j = 0; - for (j = 0; j < traitsToAdd.size(); ++j) + else { - auto iter = traitsToAdd.at(j); - // New Trait matches a Trait already marked to add - if (iter->getID() == PTrait->getID() && iter->getMod() == PTrait->getMod()) - { - if (iter->getValue() > PTrait->getValue()) - { - // New Trait is a lower tier - add = false; - break; - } - } - else if ((PTrait->getMod() == Mod::DOUBLE_ATTACK && iter->getMod() == Mod::TRIPLE_ATTACK) || - (PTrait->getMod() == Mod::GILFINDER && iter->getMod() == Mod::TREASURE_HUNTER)) - { - // Triple Attack (16 pts) overwrites Double Attack (8 pts) - // Treasure Hunter (18 pts) overwrites Gilfinder (12 pts) - add = false; - break; - } + ++it; } + } - if (add) - { - if (j != traitsToAdd.size()) - { - // New Trait is higher power than one already staged - traitsToAdd.at(j) = (CBlueTrait*)PTrait; - } - else - { - // New Trait/Mod combination is not staged yet - traitsToAdd.emplace_back((CBlueTrait*)PTrait); - } - } + traitsToAdd.emplace_back((CBlueTrait*)PTrait); + } + } + } + } + + std::vector upgradedTraits; + + auto isSameBluTrait = [](CBlueTrait* PBluTraitA, CBlueTrait* PBluTraitB) -> bool + { + if (PBluTraitA->getCategory() != PBluTraitB->getCategory()) + { + return false; + } + + // Edge case + // Double Attack upgrades to Triple Attack + // Gilfinder upgrades to Treasure Hunter + if (PBluTraitA->getMod() != PBluTraitB->getMod()) + { + if (PBluTraitA->getMod() == Mod::DOUBLE_ATTACK && PBluTraitB->getMod() == Mod::TRIPLE_ATTACK) + { + return true; + } + + if (PBluTraitA->getMod() == Mod::TRIPLE_ATTACK && PBluTraitB->getMod() == Mod::DOUBLE_ATTACK) + { + return true; + } + + if (PBluTraitA->getMod() == Mod::GILFINDER && PBluTraitB->getMod() == Mod::TREASURE_HUNTER) + { + return true; + } + + if (PBluTraitA->getMod() == Mod::TREASURE_HUNTER && PBluTraitB->getMod() == Mod::GILFINDER) + { + return true; + } + + return false; + } + + // Must be after mod ID check due to edge case. They have different trait IDs. "Gilfinder" and "Treasure hunter" etc are different in the menus + if (PBluTraitA->getID() != PBluTraitB->getID()) + { + return false; + } + + return true; + }; + + // Search for higher tier bonuses to boost them before we check if existing traits are stronger + if (traitTierBonus > 0) + { + for (auto it = traitsToAdd.begin(); it != traitsToAdd.end();) + { + auto currentTrait = (CBlueTrait*)*it; + auto newRank = currentTrait->getRank() + traitTierBonus; + CBlueTrait* newTrait = nullptr; + + for (auto& i : *PTraitsList) + { + if (i->getLevel() == 0) + { + CBlueTrait* PTrait = (CBlueTrait*)i; + + if (isSameBluTrait(PTrait, currentTrait) && newRank >= PTrait->getRank()) + { + newTrait = PTrait; } } } + + if (newTrait) + { + it = traitsToAdd.erase(it); + upgradedTraits.push_back(newTrait); + } + else + { + ++it; + } + } + } + + // Append in upgraded traits in place of ones that were removed in the search for upgraded traits, if any + if (upgradedTraits.size() > 0) + { + traitsToAdd.insert(traitsToAdd.end(), upgradedTraits.begin(), upgradedTraits.end()); + } + + // Remove weaker BLU traits + for (auto PExistingTrait : PChar->TraitList) + { + for (auto it = traitsToAdd.begin(); it != traitsToAdd.end();) + { + auto PTrait = *it; + + // Ensure they are the same modifier. BLU/THF can theoretically get Gilfinder from /THF and Treasure Hunter from BLU traits (though this is a weird edge case...) + // Need verification on that exact scenario... though not sure why you wouldn't just level your sub more? You can't get TH2/3 from BLU traits. + if (PExistingTrait->getID() == PTrait->getID() && PExistingTrait->getMod() == PTrait->getMod()) + { + // Player has the real job trait, remove ours + if (PExistingTrait->getRank() >= PTrait->getRank()) + { + it = traitsToAdd.erase(it); + continue; + } + } + + ++it; + } + } + + // Remove weaker player traits + for (auto PTrait : traitsToAdd) + { + for (auto it = PChar->TraitList.begin(); it != PChar->TraitList.end();) + { + auto PExistingTrait = *it; + + // Ensure they are the same modifier. BLU/THF can theoretically get Gilfinder from /THF and Treasure Hunter from BLU traits (though this is a weird edge case...) + // Need verification on that exact scenario... though not sure why you wouldn't just level your sub more? You can't get TH2/3 from BLU traits. + if (PExistingTrait->getID() == PTrait->getID() && PExistingTrait->getMod() == PTrait->getMod()) + { + // Player has the real job trait, and it's weaker, remove theirs + if (PExistingTrait->getRank() < PTrait->getRank()) + { + PChar->delModifier(PExistingTrait->getMod(), PExistingTrait->getValue()); + it = PChar->TraitList.erase(it); + continue; + } + } + + ++it; } } From ecd491a0da5bb5bce2ce5d043875d0255ebfd41a Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:37:00 -0700 Subject: [PATCH 6/6] [sql] Fix BLU Treasure Hunter/Gilfinder trait --- sql/blue_traits.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/blue_traits.sql b/sql/blue_traits.sql index 7d0dbc48af3..79f172002d8 100644 --- a/sql/blue_traits.sql +++ b/sql/blue_traits.sql @@ -64,4 +64,4 @@ INSERT INTO `blue_traits` VALUES (25,6,18,259,25,3,0); -- Dual Wield (3) INSERT INTO `blue_traits` VALUES (26,2,70,306,15,1,0); -- Zanshin (1) INSERT INTO `blue_traits` VALUES (27,2,110,487,5,1,0); -- Magic Burst Bonus (1) INSERT INTO `blue_traits` VALUES (28,2,20,897,1,1,0); -- Gilfinder (1) -INSERT INTO `blue_traits` VALUES (28,3,19,303,1,1,0); -- Treasure Hunter (1) +INSERT INTO `blue_traits` VALUES (28,3,19,303,1,2,0); -- Treasure Hunter (1)