@@ -1010,3 +1010,190 @@ table.insert(replicaDragonfangsFlight,
10101010)
10111011
10121012table.insert (data .uniques .generated , table.concat (replicaDragonfangsFlight , " \n " ))
1013+
1014+ -- Foulborn Uniques Generation
1015+ -- Each Foulborn unique is a separate generated item with variants for each possible Foulborn mutation mod.
1016+ -- This uses data.foulbornMap (loaded from ModFoulbornMap.lua) which maps unique name -> list of mutation mod descriptions.
1017+ if data .foulbornMap then
1018+ -- Build lookup: unique name -> raw item text from all loaded unique types
1019+ local uniqueLookup = {}
1020+ for _ , itemType in ipairs ({" axe" ," bow" ," claw" ," dagger" ," fishing" ," mace" ," staff" ," sword" ," wand" ,
1021+ " helmet" ," body" ," gloves" ," boots" ," shield" ," quiver" ," amulet" ," ring" ," belt" ," jewel" ,
1022+ " flask" ," tincture" ," graft" }) do
1023+ if data .uniques [itemType ] then
1024+ for _ , raw in ipairs (data .uniques [itemType ]) do
1025+ local name = raw :match (" ^%s*(.-)%s*\n " )
1026+ if name and not uniqueLookup [name ] then
1027+ uniqueLookup [name ] = raw
1028+ end
1029+ end
1030+ end
1031+ end
1032+ if data .uniques .race then
1033+ for _ , raw in ipairs (data .uniques .race ) do
1034+ local name = raw :match (" ^%s*(.-)%s*\n " )
1035+ if name and not uniqueLookup [name ] then
1036+ uniqueLookup [name ] = raw
1037+ end
1038+ end
1039+ end
1040+
1041+ -- Sort names for deterministic output
1042+ local sortedFoulbornNames = {}
1043+ for name in pairs (data .foulbornMap ) do
1044+ table.insert (sortedFoulbornNames , name )
1045+ end
1046+ table.sort (sortedFoulbornNames )
1047+
1048+ for _ , uniqueName in ipairs (sortedFoulbornNames ) do
1049+ local fbMods = data .foulbornMap [uniqueName ]
1050+ local raw = uniqueLookup [uniqueName ]
1051+ if raw then
1052+ -- Parse raw text into trimmed lines
1053+ local lines = {}
1054+ for line in (raw .. " \n " ):gmatch (" (.-)\n " ) do
1055+ line = line :match (" ^%s*(.-)%s*$" )
1056+ table.insert (lines , line )
1057+ end
1058+ while # lines > 0 and lines [1 ] == " " do table.remove (lines , 1 ) end
1059+ while # lines > 0 and lines [# lines ] == " " do table.remove (lines , # lines ) end
1060+
1061+ -- Count Variant: declarations to determine "current" variant (last = highest index)
1062+ local variantCount = 0
1063+ for _ , line in ipairs (lines ) do
1064+ if line :match (" ^Variant:" ) then
1065+ variantCount = variantCount + 1
1066+ end
1067+ end
1068+ local curVar = variantCount
1069+
1070+ -- Check if a line's {variant:X,Y} tag includes the current variant
1071+ local function applies (line )
1072+ local tag = line :match (" ^{variant:([%d,]+)}" )
1073+ if not tag then return true end
1074+ if curVar == 0 then return true end
1075+ for v in tag :gmatch (" %d+" ) do
1076+ if tonumber (v ) == curVar then return true end
1077+ end
1078+ return false
1079+ end
1080+
1081+ -- Strip {variant:...} tag from line start (preserves {tags:...} etc)
1082+ local function stripVar (line )
1083+ return (line :gsub (" ^{variant:[%d,]+}" , " " ))
1084+ end
1085+
1086+ -- Metadata lines to skip during generation
1087+ local function isMetadata (line )
1088+ return line :match (" ^Variant:" ) or line :match (" ^Selected Variant:" )
1089+ or line :match (" ^Selected Alt Variant" ) or line :match (" ^Has Alt Variant" )
1090+ or line :match (" ^League:" ) or line :match (" ^Source:" )
1091+ or line :match (" ^Upgrade:" ) or line :match (" ^Limited to:" )
1092+ or line :match (" ^Item Level:" ) or line :match (" ^Radius:" )
1093+ or line :match (" ^Shaper Item" ) or line :match (" ^Elder Item" )
1094+ or line :match (" ^Crusader Item" ) or line :match (" ^Redeemer Item" )
1095+ or line :match (" ^Hunter Item" ) or line :match (" ^Warlord Item" )
1096+ or line :match (" ^Corrupted$" ) or line :match (" ^Item Class:" )
1097+ or line :match (" ^Rarity:" ) or line :match (" ^Has no Sockets" )
1098+ end
1099+
1100+ -- Parse sections from the original unique
1101+ local baseName = nil
1102+ local requiresLine = nil
1103+ local implicitsCount = nil
1104+ local implicitLines = {}
1105+ local explicitLines = {}
1106+
1107+ local i = 1
1108+ -- Skip leading Item Class: / Rarity: lines (generated format)
1109+ while i <= # lines and (lines [i ]:match (" ^Item Class:" ) or lines [i ]:match (" ^Rarity:" )) do
1110+ i = i + 1
1111+ end
1112+ -- lines[i] is the name; skip it
1113+ i = i + 1
1114+
1115+ -- Find base type (line after name, possibly multiple variant-tagged lines)
1116+ if i <= # lines and lines [i ]:match (" ^{variant:" ) and not lines [i ]:match (" ^Variant:" ) then
1117+ -- Variant-tagged base type(s)
1118+ while i <= # lines and lines [i ]:match (" ^{variant:" ) and not lines [i ]:match (" ^Variant:" ) do
1119+ if applies (lines [i ]) then
1120+ baseName = stripVar (lines [i ])
1121+ end
1122+ i = i + 1
1123+ end
1124+ elseif i <= # lines then
1125+ -- Plain base type
1126+ baseName = lines [i ]
1127+ i = i + 1
1128+ end
1129+
1130+ -- Process remaining lines: metadata, requires, implicits, mods
1131+ local implicitsRemaining = 0
1132+ local inImplicits = false
1133+ while i <= # lines do
1134+ local line = lines [i ]
1135+ if isMetadata (line ) then
1136+ -- Skip
1137+ elseif line :match (" ^Requires Level" ) or line :match (" ^LevelReq:" ) then
1138+ if applies (line ) then
1139+ requiresLine = stripVar (line )
1140+ end
1141+ elseif line :match (" ^Implicits: (%d+)" ) then
1142+ implicitsCount = tonumber (line :match (" ^Implicits: (%d+)" ))
1143+ implicitsRemaining = implicitsCount
1144+ inImplicits = true
1145+ elseif inImplicits and implicitsRemaining > 0 then
1146+ if applies (line ) then
1147+ table.insert (implicitLines , stripVar (line ))
1148+ end
1149+ implicitsRemaining = implicitsRemaining - 1
1150+ if implicitsRemaining == 0 then
1151+ inImplicits = false
1152+ end
1153+ else
1154+ inImplicits = false
1155+ if applies (line ) and line ~= " " then
1156+ table.insert (explicitLines , stripVar (line ))
1157+ end
1158+ end
1159+ i = i + 1
1160+ end
1161+
1162+ -- Build the Foulborn item
1163+ if baseName then
1164+ local foulborn = {}
1165+ table.insert (foulborn , " Foulborn " .. uniqueName )
1166+ table.insert (foulborn , baseName )
1167+
1168+ -- Variant declarations (one per Foulborn mutation mod)
1169+ for _ , modLine in ipairs (fbMods ) do
1170+ table.insert (foulborn , " Variant: " .. modLine )
1171+ end
1172+
1173+ if requiresLine then
1174+ table.insert (foulborn , requiresLine )
1175+ end
1176+
1177+ -- Implicits (only if original specified them)
1178+ if implicitsCount ~= nil then
1179+ table.insert (foulborn , " Implicits: " .. # implicitLines )
1180+ for _ , impl in ipairs (implicitLines ) do
1181+ table.insert (foulborn , impl )
1182+ end
1183+ end
1184+
1185+ -- Base mods from original (current variant, tags stripped)
1186+ for _ , modLine in ipairs (explicitLines ) do
1187+ table.insert (foulborn , modLine )
1188+ end
1189+
1190+ -- Foulborn mutation mods (variant-tagged)
1191+ for idx , modLine in ipairs (fbMods ) do
1192+ table.insert (foulborn , " {variant:" .. idx .. " }" .. modLine )
1193+ end
1194+
1195+ table.insert (data .uniques .generated , table.concat (foulborn , " \n " ))
1196+ end
1197+ end
1198+ end
1199+ end
0 commit comments