варианты кастомизации
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,8 @@ CHARACTER_FACING_INCREMENT = 2;
|
||||
MAX_RACES = 10;
|
||||
MAX_CLASSES_PER_RACE = 10;
|
||||
NUM_CHAR_CUSTOMIZATIONS = 5;
|
||||
MAX_EXTENDED_CHAR_CUSTOMIZATIONS = 20;
|
||||
EXTENDED_CUSTOMIZATION_ROW_SPACING = 48;
|
||||
MIN_CHAR_NAME_LENGTH = 2;
|
||||
CHARACTER_CREATE_ROTATION_START_X = nil;
|
||||
CHARACTER_ROTATION_INCREMENT = 90;
|
||||
@@ -809,6 +811,11 @@ local function CharacterCreate_UpdatePersonalizationStep()
|
||||
end
|
||||
end
|
||||
|
||||
if CharacterCreate_UpdateExtendedCustomization then
|
||||
CharacterCreate.extendedCustomizationStateKey = nil
|
||||
CharacterCreate_UpdateExtendedCustomization()
|
||||
end
|
||||
|
||||
CharacterCreate_UpdateGameModeVisibility();
|
||||
end
|
||||
|
||||
@@ -872,6 +879,8 @@ function CharacterCreate_OnLoad(self)
|
||||
_G["CharacterCustomizationButtonFrame"..i.."Text"]:SetText(_G["CHAR_CUSTOMIZATION"..i.."_DESC"])
|
||||
end
|
||||
|
||||
self.extendedCustomizationElapsed = 0
|
||||
|
||||
local backdropColor = FACTION_BACKDROP_COLOR_TABLE["Alliance"]
|
||||
CharacterCreateNameEdit:SetBackdropBorderColor(backdropColor[1], backdropColor[2], backdropColor[3])
|
||||
CharacterCreateNameEdit:SetBackdropColor(backdropColor[4], backdropColor[5], backdropColor[6])
|
||||
@@ -1117,13 +1126,30 @@ function CharacterCreateFrame_OnMouseUp(button)
|
||||
end
|
||||
end
|
||||
|
||||
function CharacterCreateFrame_OnUpdate()
|
||||
function CharacterCreateFrame_OnUpdate(elapsed)
|
||||
if ( CHARACTER_CREATE_ROTATION_START_X ) then
|
||||
local x = GetCursorPosition();
|
||||
local diff = (x - CHARACTER_CREATE_ROTATION_START_X) * CHARACTER_ROTATION_CONSTANT;
|
||||
CHARACTER_CREATE_ROTATION_START_X = GetCursorPosition();
|
||||
SetCharacterCreateFacing(GetCharacterCreateFacing() + diff);
|
||||
end
|
||||
|
||||
if CharacterCreate_ProcessPendingCustomization
|
||||
and CharacterCreate_ProcessPendingCustomization(elapsed or 0) then
|
||||
return
|
||||
end
|
||||
|
||||
CharacterCreate.extendedCustomizationElapsed =
|
||||
(CharacterCreate.extendedCustomizationElapsed or 0) + (elapsed or 0)
|
||||
if CharacterCreate.extendedCustomizationElapsed >= 0.10 then
|
||||
CharacterCreate.extendedCustomizationElapsed = 0
|
||||
if CharacterCreate_UpdateCustomizationAssetDebug then
|
||||
local ok, errorText = pcall(CharacterCreate_UpdateCustomizationAssetDebug)
|
||||
if not ok and CharacterCreate_LogExtendedCustomization then
|
||||
CharacterCreate_LogExtendedCustomization("update failed: "..tostring(errorText), true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CharacterCreateEnumerateRaces(...)
|
||||
@@ -1751,14 +1777,544 @@ function CharacterCreate_ResetState(immediate)
|
||||
end
|
||||
end
|
||||
|
||||
local EXTENDED_CUSTOMIZATION_SLOT_LABELS = {
|
||||
[1] = "Цвет кожи",
|
||||
[2] = "Лицо",
|
||||
[3] = "Прическа",
|
||||
[4] = "Цвет волос",
|
||||
[5] = "Борода и усы",
|
||||
}
|
||||
|
||||
local EXTENDED_CUSTOMIZATION_COMPONENT_LABELS = {
|
||||
[1] = "Внешность",
|
||||
[10] = "Цвет кожи",
|
||||
[11] = "Вариант кожи",
|
||||
[20] = "Лицо",
|
||||
[21] = "Верхняя часть лица",
|
||||
[22] = "Дополнительная особенность лица",
|
||||
[30] = "Прическа",
|
||||
[40] = "Цвет волос",
|
||||
[41] = "Вариант окраски волос",
|
||||
[50] = "Борода",
|
||||
[51] = "Усы",
|
||||
[52] = "Бакенбарды",
|
||||
[53] = "Особенность лица 1",
|
||||
[54] = "Особенность лица 2",
|
||||
[55] = "Нижняя текстура лица",
|
||||
[56] = "Верхняя текстура лица",
|
||||
[57] = "Дополнительная текстура лица",
|
||||
[59] = "Борода и усы",
|
||||
}
|
||||
|
||||
local function CharacterCreate_GetCustomizationState()
|
||||
-- The stock global is guaranteed to survive all Glue registration passes. The separately
|
||||
-- registered MoonWell global may exist as a Lua function object while still failing validation,
|
||||
-- so never prefer it over the verified dispatcher.
|
||||
local api = GetHairCustomization
|
||||
local useStockBridge = type(api) == "function"
|
||||
if not useStockBridge then api = MoonWellGetCharacterCustomizationState end
|
||||
if type(api) ~= "function" then
|
||||
CharacterCreate.extendedCustomizationFailure = "MoonWell API недоступен"
|
||||
return nil
|
||||
end
|
||||
local ok, skin, face, hairStyle, hairColor, facialHair, race, sex
|
||||
if useStockBridge then
|
||||
ok, skin, face, hairStyle, hairColor, facialHair, race, sex =
|
||||
pcall(api, "__MoonWellState")
|
||||
else
|
||||
ok, skin, face, hairStyle, hairColor, facialHair, race, sex = pcall(api)
|
||||
end
|
||||
if not ok or skin == nil or face == nil or hairStyle == nil or hairColor == nil
|
||||
or facialHair == nil or race == nil or sex == nil then
|
||||
CharacterCreate.extendedCustomizationFailure = ok
|
||||
and "Состояние персонажа не готово" or "Ошибка MoonWell API"
|
||||
return nil
|
||||
end
|
||||
CharacterCreate.extendedCustomizationFailure = nil
|
||||
local state = { skin, face, hairStyle, hairColor, facialHair }
|
||||
state.race = race
|
||||
state.sex = sex
|
||||
return state
|
||||
end
|
||||
|
||||
local function CharacterCreate_ValueCount(values)
|
||||
local count = 0
|
||||
for _ in pairs(values) do count = count + 1 end
|
||||
return count
|
||||
end
|
||||
|
||||
local function CharacterCreate_AttributesAreCoupled(catalog, leftKey, rightKey)
|
||||
local leftToRight = {}
|
||||
local rightToLeft = {}
|
||||
for _, candidate in ipairs(catalog.candidates) do
|
||||
local left = candidate.attributes[leftKey] or ""
|
||||
local right = candidate.attributes[rightKey] or ""
|
||||
if leftToRight[left] and leftToRight[left] ~= right then return false end
|
||||
if rightToLeft[right] and rightToLeft[right] ~= left then return false end
|
||||
leftToRight[left] = right
|
||||
rightToLeft[right] = left
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function CharacterCreate_BuildComponentGroups(catalog, slot)
|
||||
local keys = {}
|
||||
for key, meta in pairs(catalog.meta) do
|
||||
if CharacterCreate_ValueCount(meta.values) > 1 then keys[#keys + 1] = key end
|
||||
end
|
||||
table.sort(keys, function(left, right)
|
||||
return catalog.meta[left].order < catalog.meta[right].order
|
||||
end)
|
||||
|
||||
local parent = {}
|
||||
for _, key in ipairs(keys) do parent[key] = key end
|
||||
local function root(key)
|
||||
while parent[key] ~= key do
|
||||
parent[key] = parent[parent[key]]
|
||||
key = parent[key]
|
||||
end
|
||||
return key
|
||||
end
|
||||
local function merge(left, right)
|
||||
left = root(left)
|
||||
right = root(right)
|
||||
if left ~= right then parent[right] = left end
|
||||
end
|
||||
|
||||
for leftIndex=1, #keys do
|
||||
for rightIndex=leftIndex + 1, #keys do
|
||||
if CharacterCreate_AttributesAreCoupled(catalog, keys[leftIndex], keys[rightIndex]) then
|
||||
merge(keys[leftIndex], keys[rightIndex])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local groupsByRoot = {}
|
||||
for _, key in ipairs(keys) do
|
||||
local keyRoot = root(key)
|
||||
local group = groupsByRoot[keyRoot]
|
||||
if not group then
|
||||
group = { keys = {}, keySet = {}, order = catalog.meta[key].order,
|
||||
label = catalog.meta[key].label }
|
||||
groupsByRoot[keyRoot] = group
|
||||
end
|
||||
group.keys[#group.keys + 1] = key
|
||||
group.keySet[key] = true
|
||||
if catalog.meta[key].order < group.order then
|
||||
group.order = catalog.meta[key].order
|
||||
group.label = catalog.meta[key].label
|
||||
end
|
||||
end
|
||||
|
||||
catalog.groups = {}
|
||||
for _, group in pairs(groupsByRoot) do catalog.groups[#catalog.groups + 1] = group end
|
||||
table.sort(catalog.groups, function(left, right) return left.order < right.order end)
|
||||
if #catalog.groups == 0 and #catalog.candidates > 1 then
|
||||
catalog.groups[1] = {
|
||||
keys = {}, keySet = {}, raw = true, order = 1,
|
||||
label = EXTENDED_CUSTOMIZATION_SLOT_LABELS[slot],
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function CharacterCreate_CustomizationDependency(slot, state)
|
||||
local dependency = 0
|
||||
if slot == 2 then dependency = state[1]
|
||||
elseif slot == 3 or slot == 5 then dependency = state[4]
|
||||
elseif slot == 4 then dependency = state[3]
|
||||
end
|
||||
return dependency
|
||||
end
|
||||
|
||||
local function CharacterCreate_CustomizationCatalogKey(slot, race, sex, state)
|
||||
local dependency = CharacterCreate_CustomizationDependency(slot, state)
|
||||
return race..":"..sex..":"..slot..":"..dependency
|
||||
end
|
||||
|
||||
local function CharacterCreate_CallCustomizationCatalogAPI(api, useStockBridge, slot, race,
|
||||
sex, dependency, index)
|
||||
if useStockBridge then
|
||||
if index then
|
||||
return pcall(api, "__MoonWellCatalog", slot, race, sex, dependency, index)
|
||||
end
|
||||
return pcall(api, "__MoonWellCatalog", slot, race, sex, dependency)
|
||||
end
|
||||
if index then return pcall(api, slot, race, sex, dependency, index) end
|
||||
return pcall(api, slot, race, sex, dependency)
|
||||
end
|
||||
|
||||
local function CharacterCreate_BuildCustomizationCatalog(slot, race, sex, state)
|
||||
CharacterCreate.extendedCustomizationCatalogs = CharacterCreate.extendedCustomizationCatalogs or {}
|
||||
local key = CharacterCreate_CustomizationCatalogKey(slot, race, sex, state)
|
||||
local catalog = CharacterCreate.extendedCustomizationCatalogs[slot]
|
||||
if not catalog or catalog.key ~= key then
|
||||
catalog = {
|
||||
key = key, candidates = {}, byRaw = {}, meta = {}, ready = false,
|
||||
}
|
||||
CharacterCreate.extendedCustomizationCatalogs[slot] = catalog
|
||||
end
|
||||
if catalog.ready then return catalog end
|
||||
|
||||
local api = GetHairCustomization
|
||||
local useStockBridge = type(api) == "function"
|
||||
if not useStockBridge then api = MoonWellGetCharacterCustomizationCatalog end
|
||||
if type(api) ~= "function" then return nil end
|
||||
local dependency = CharacterCreate_CustomizationDependency(slot, state)
|
||||
local ok, count, componentCount, kind1, kind2, kind3, kind4, kind5, kind6, kind7, kind8 =
|
||||
CharacterCreate_CallCustomizationCatalogAPI(api, useStockBridge, slot, race, sex,
|
||||
dependency)
|
||||
if not ok or type(count) ~= "number" or type(componentCount) ~= "number" then
|
||||
return nil
|
||||
end
|
||||
local kinds = { kind1, kind2, kind3, kind4, kind5, kind6, kind7, kind8 }
|
||||
for component=1, componentCount do
|
||||
local keyName = "component_"..component
|
||||
local kind = kinds[component]
|
||||
catalog.meta[keyName] = {
|
||||
key = keyName,
|
||||
label = EXTENDED_CUSTOMIZATION_COMPONENT_LABELS[kind]
|
||||
or EXTENDED_CUSTOMIZATION_SLOT_LABELS[slot] or "Внешность",
|
||||
order = component,
|
||||
values = {},
|
||||
}
|
||||
end
|
||||
|
||||
for index=1, count do
|
||||
local entryOK, raw, value1, value2, value3, value4, value5, value6, value7, value8 =
|
||||
CharacterCreate_CallCustomizationCatalogAPI(api, useStockBridge, slot, race, sex,
|
||||
dependency, index)
|
||||
if not entryOK or raw == nil then return nil end
|
||||
local values = { value1, value2, value3, value4, value5, value6, value7, value8 }
|
||||
local candidate = { raw = raw, attributes = {} }
|
||||
for component=1, componentCount do
|
||||
local keyName = "component_"..component
|
||||
local value = tostring(values[component] or 0)
|
||||
candidate.attributes[keyName] = value
|
||||
catalog.meta[keyName].values[value] = true
|
||||
end
|
||||
catalog.candidates[#catalog.candidates + 1] = candidate
|
||||
catalog.byRaw[raw] = candidate
|
||||
end
|
||||
CharacterCreate_BuildComponentGroups(catalog, slot)
|
||||
catalog.ready = true
|
||||
return catalog
|
||||
end
|
||||
|
||||
local function CharacterCreate_GroupSignature(candidate, group)
|
||||
if group.raw then return tostring(candidate.raw) end
|
||||
local values = {}
|
||||
for _, key in ipairs(group.keys) do values[#values + 1] = candidate.attributes[key] or "" end
|
||||
return table.concat(values, "|")
|
||||
end
|
||||
|
||||
local function CharacterCreate_MatchesOutsideGroup(candidate, current, catalog, group)
|
||||
if group.raw then return true end
|
||||
for key in pairs(catalog.meta) do
|
||||
if not group.keySet[key] and (candidate.attributes[key] or "") ~=
|
||||
(current.attributes[key] or "") then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function CharacterCreate_GetGroupChoices(catalog, group, currentRaw)
|
||||
local current = catalog.byRaw[currentRaw]
|
||||
if not current then return {}, 0 end
|
||||
local choices = {}
|
||||
local signatures = {}
|
||||
local currentSignature = CharacterCreate_GroupSignature(current, group)
|
||||
local currentIndex = 0
|
||||
for _, candidate in ipairs(catalog.candidates) do
|
||||
if CharacterCreate_MatchesOutsideGroup(candidate, current, catalog, group) then
|
||||
local signature = CharacterCreate_GroupSignature(candidate, group)
|
||||
if not signatures[signature] then
|
||||
signatures[signature] = true
|
||||
choices[#choices + 1] = candidate
|
||||
if signature == currentSignature then currentIndex = #choices end
|
||||
end
|
||||
end
|
||||
end
|
||||
return choices, currentIndex
|
||||
end
|
||||
|
||||
local function CharacterCreate_EnsureCustomizationSelectorFrames()
|
||||
if CharacterCreate.customizationSelectorFrames then
|
||||
return CharacterCreate.customizationSelectorFrames
|
||||
end
|
||||
|
||||
local frames = {}
|
||||
for index=1, MAX_EXTENDED_CHAR_CUSTOMIZATIONS do
|
||||
local frame = _G["CharacterCustomizationButtonFrame"..index]
|
||||
if not frame then
|
||||
frame = CreateFrame("Frame", "MoonWellCharacterCustomizationSelector"..index,
|
||||
CharacterCreateFrame, "CharacterCustomizationFrameTemplate")
|
||||
frame:Hide()
|
||||
end
|
||||
if frame:GetParent() ~= CharacterCreateFrame then
|
||||
frame:SetParent(CharacterCreateFrame)
|
||||
end
|
||||
frame:SetFrameLevel(CharacterCreateFrame:GetFrameLevel() + 5)
|
||||
frame:SetID(index)
|
||||
frames[index] = frame
|
||||
end
|
||||
CharacterCreate.customizationSelectorFrames = frames
|
||||
return frames
|
||||
end
|
||||
|
||||
function CharacterCreate_LogExtendedCustomization(messageText, once)
|
||||
if once and CharacterCreate.extendedCustomizationLastLog == messageText then return end
|
||||
if once then CharacterCreate.extendedCustomizationLastLog = messageText end
|
||||
if type(GetHairCustomization) == "function" then
|
||||
pcall(GetHairCustomization, "__MoonWellLog", tostring(messageText))
|
||||
end
|
||||
end
|
||||
|
||||
local function CharacterCreate_ShowCustomizationSelectors(frames, selectors)
|
||||
local firstY = (#selectors - 1) * EXTENDED_CUSTOMIZATION_ROW_SPACING / 2
|
||||
local visibleCount = 0
|
||||
for index, frame in ipairs(frames) do
|
||||
local selector = selectors[index]
|
||||
if selector then
|
||||
frame:ClearAllPoints()
|
||||
frame:SetPoint("RIGHT", CharacterCreateFrame, "RIGHT", -80,
|
||||
firstY - (index - 1) * EXTENDED_CUSTOMIZATION_ROW_SPACING)
|
||||
frame:SetID(index)
|
||||
local text = _G[frame:GetName().."Text"]
|
||||
if selector.fallback then
|
||||
text:SetText(selector.label)
|
||||
else
|
||||
text:SetText(string.format("%s: %d/%d",
|
||||
selector.label, selector.currentIndex, #selector.choices))
|
||||
end
|
||||
frame:Show()
|
||||
visibleCount = visibleCount + 1
|
||||
else
|
||||
frame:Hide()
|
||||
end
|
||||
end
|
||||
return visibleCount
|
||||
end
|
||||
|
||||
function CharacterCreate_UpdateExtendedCustomization(activeState)
|
||||
local customizationVisible = CharacterCreate.personalizationMode
|
||||
and not CharacterCreate.gameModeSelectionMode
|
||||
if not customizationVisible then
|
||||
CharacterCreate.pendingExtendedCustomization = nil
|
||||
CharacterCreate.extendedCustomizationApplying = false
|
||||
if CharacterCreate.customizationSelectorFrames then
|
||||
for _, frame in ipairs(CharacterCreate.customizationSelectorFrames) do frame:Hide() end
|
||||
end
|
||||
CharacterCreate.extendedCustomizationStateKey = nil
|
||||
return
|
||||
end
|
||||
local frames = CharacterCreate_EnsureCustomizationSelectorFrames()
|
||||
|
||||
local state = activeState or CharacterCreate_GetCustomizationState()
|
||||
local race = state and state.race or 0
|
||||
local sex = state and state.sex or -1
|
||||
local stateKey
|
||||
if state then
|
||||
stateKey = string.format("%d:%d:%d:%d:%d:%d:%d", race, sex,
|
||||
state[1], state[2], state[3], state[4], state[5])
|
||||
if CharacterCreate.extendedCustomizationStateKey == stateKey then
|
||||
CharacterCreate_ShowCustomizationSelectors(frames,
|
||||
CharacterCreate.visibleCustomizationSelectors or {})
|
||||
return
|
||||
end
|
||||
end
|
||||
local selectors = {}
|
||||
local catalogCount = 0
|
||||
|
||||
if state and race > 0 and (sex == 0 or sex == 1) then
|
||||
for slot=1, NUM_CHAR_CUSTOMIZATIONS do
|
||||
local slotSelectorCount = 0
|
||||
local catalog = CharacterCreate_BuildCustomizationCatalog(slot, race, sex, state)
|
||||
if catalog then
|
||||
catalogCount = catalogCount + 1
|
||||
for _, group in ipairs(catalog.groups) do
|
||||
local choices, currentIndex = CharacterCreate_GetGroupChoices(
|
||||
catalog, group, state[slot])
|
||||
if #choices > 1 and currentIndex > 0 then
|
||||
selectors[#selectors + 1] = {
|
||||
slot = slot, group = group, catalog = catalog,
|
||||
choices = choices, currentIndex = currentIndex,
|
||||
currentRaw = state[slot],
|
||||
label = group.label,
|
||||
}
|
||||
slotSelectorCount = slotSelectorCount + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if slotSelectorCount == 0 then
|
||||
selectors[#selectors + 1] = {
|
||||
slot = slot, fallback = true,
|
||||
label = EXTENDED_CUSTOMIZATION_SLOT_LABELS[slot]
|
||||
or _G["CHAR_CUSTOMIZATION"..slot.."_DESC"],
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #selectors == 0 then
|
||||
for slot=1, NUM_CHAR_CUSTOMIZATIONS do
|
||||
selectors[#selectors + 1] = {
|
||||
slot = slot, fallback = true,
|
||||
label = EXTENDED_CUSTOMIZATION_SLOT_LABELS[slot]
|
||||
or _G["CHAR_CUSTOMIZATION"..slot.."_DESC"],
|
||||
}
|
||||
end
|
||||
if CharacterCreate.extendedCustomizationFailure then
|
||||
selectors[1].label = "MoonWell: "..CharacterCreate.extendedCustomizationFailure
|
||||
end
|
||||
end
|
||||
|
||||
CharacterCreate.visibleCustomizationSelectors = selectors
|
||||
local visibleCount = CharacterCreate_ShowCustomizationSelectors(frames, selectors)
|
||||
CharacterCreate.extendedCustomizationStateKey =
|
||||
catalogCount == NUM_CHAR_CUSTOMIZATIONS and stateKey or nil
|
||||
local firstFrame = frames[1]
|
||||
CharacterCreate_LogExtendedCustomization(string.format(
|
||||
"layout ready selectors=%d visible=%d catalogs=%d bounds=%.0f..%.0f root=%.0fx%.0f",
|
||||
#selectors, visibleCount, catalogCount, firstFrame:GetLeft() or -1,
|
||||
firstFrame:GetRight() or -1, CharacterCreateFrame:GetWidth() or -1,
|
||||
CharacterCreateFrame:GetHeight() or -1), true)
|
||||
end
|
||||
|
||||
local function CharacterCreate_PrimeMappedCustomization(slot, raw)
|
||||
local api = GetHairCustomization
|
||||
if type(api) ~= "function" then return false end
|
||||
local ok, primed = pcall(api, "__MoonWellPrime", slot, raw)
|
||||
return ok and primed == true
|
||||
end
|
||||
|
||||
local function CharacterCreate_FinishPendingCustomization(reason)
|
||||
local pending = CharacterCreate.pendingExtendedCustomization
|
||||
CharacterCreate.pendingExtendedCustomization = nil
|
||||
CharacterCreate.extendedCustomizationApplying = false
|
||||
CharacterCreate.extendedCustomizationStateKey = nil
|
||||
if reason then
|
||||
CharacterCreate_LogExtendedCustomization(string.format(
|
||||
"apply %s slot=%d target=%d steps=%d",
|
||||
reason, pending and pending.slot or -1, pending and pending.targetRaw or -1,
|
||||
pending and pending.completedSteps or 0), true)
|
||||
end
|
||||
CharacterCreate_UpdateExtendedCustomization()
|
||||
end
|
||||
|
||||
function CharacterCreate_ProcessPendingCustomization(elapsed)
|
||||
local pending = CharacterCreate.pendingExtendedCustomization
|
||||
if not pending then return false end
|
||||
|
||||
pending.elapsed = (pending.elapsed or 0) + elapsed
|
||||
if pending.elapsed < 0.02 then return true end
|
||||
pending.elapsed = 0
|
||||
|
||||
local state = CharacterCreate_GetCustomizationState()
|
||||
if not state then
|
||||
CharacterCreate_FinishPendingCustomization("cancelled: state unavailable")
|
||||
return false
|
||||
end
|
||||
if state[pending.slot] == pending.targetRaw then
|
||||
CharacterCreate_FinishPendingCustomization("complete")
|
||||
return false
|
||||
end
|
||||
if pending.remainingSteps <= 0 then
|
||||
CharacterCreate_FinishPendingCustomization("cancelled: target unreachable")
|
||||
return false
|
||||
end
|
||||
|
||||
pending.remainingSteps = pending.remainingSteps - 1
|
||||
pending.completedSteps = pending.completedSteps + 1
|
||||
CycleCharCustomization(pending.slot, pending.cycleDirection)
|
||||
return true
|
||||
end
|
||||
|
||||
local function CharacterCreate_CycleExtendedCustomization(selectorIndex, direction)
|
||||
local selectors = CharacterCreate.visibleCustomizationSelectors
|
||||
local selector = selectors and selectors[selectorIndex]
|
||||
if not selector then return false end
|
||||
if CharacterCreate.pendingExtendedCustomization then return true end
|
||||
|
||||
PlaySound("gsCharacterCreationLook")
|
||||
if selector.fallback then
|
||||
CycleCharCustomization(selector.slot, direction)
|
||||
return true
|
||||
end
|
||||
|
||||
local targetIndex = selector.currentIndex + direction
|
||||
if targetIndex < 1 then targetIndex = #selector.choices end
|
||||
if targetIndex > #selector.choices then targetIndex = 1 end
|
||||
local targetRaw = selector.choices[targetIndex].raw
|
||||
|
||||
-- The catalog is also a direct raw-value map. Prime the stock field with the mapped neighbour of
|
||||
-- the target, then make exactly one stock cycle call. Both operations happen inside this click,
|
||||
-- before a frame is rendered, while the stock call still performs its complete dependency/model
|
||||
-- refresh. Keep the incremental path only as a compatibility fallback for an older native bridge.
|
||||
local currentCandidateIndex
|
||||
local targetCandidateIndex
|
||||
for candidateIndex, candidate in ipairs(selector.catalog.candidates) do
|
||||
if candidate.raw == selector.currentRaw then
|
||||
currentCandidateIndex = candidateIndex
|
||||
end
|
||||
if candidate.raw == targetRaw then targetCandidateIndex = candidateIndex end
|
||||
end
|
||||
local candidateCount = #selector.catalog.candidates
|
||||
local cycleDirection = direction
|
||||
if currentCandidateIndex and targetCandidateIndex and candidateCount > 0 then
|
||||
local forward = (targetCandidateIndex - currentCandidateIndex) % candidateCount
|
||||
local backward = (currentCandidateIndex - targetCandidateIndex) % candidateCount
|
||||
cycleDirection = forward <= backward and 1 or -1
|
||||
end
|
||||
if targetCandidateIndex and candidateCount > 1 then
|
||||
local primeIndex = targetCandidateIndex - cycleDirection
|
||||
if primeIndex < 1 then primeIndex = candidateCount end
|
||||
if primeIndex > candidateCount then primeIndex = 1 end
|
||||
local primeRaw = selector.catalog.candidates[primeIndex].raw
|
||||
if CharacterCreate_PrimeMappedCustomization(selector.slot, primeRaw) then
|
||||
CharacterCreate.extendedCustomizationApplying = true
|
||||
CycleCharCustomization(selector.slot, cycleDirection)
|
||||
CharacterCreate.extendedCustomizationApplying = false
|
||||
local appliedState = CharacterCreate_GetCustomizationState()
|
||||
if appliedState and appliedState[selector.slot] == targetRaw then
|
||||
CharacterCreate.pendingExtendedCustomization = nil
|
||||
CharacterCreate.extendedCustomizationStateKey = nil
|
||||
CharacterCreate_LogExtendedCustomization(string.format(
|
||||
"apply mapped slot=%d target=%d", selector.slot, targetRaw), true)
|
||||
CharacterCreate_UpdateExtendedCustomization(appliedState)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
CharacterCreate.pendingExtendedCustomization = {
|
||||
slot = selector.slot,
|
||||
targetRaw = targetRaw,
|
||||
cycleDirection = cycleDirection,
|
||||
remainingSteps = candidateCount,
|
||||
completedSteps = 0,
|
||||
elapsed = 0.02,
|
||||
}
|
||||
CharacterCreate.extendedCustomizationApplying = true
|
||||
return true
|
||||
end
|
||||
|
||||
function CharacterCustomization_Left(id)
|
||||
PlaySound("gsCharacterCreationLook");
|
||||
CycleCharCustomization(id, -1);
|
||||
if not CharacterCreate_CycleExtendedCustomization(id, -1) then
|
||||
PlaySound("gsCharacterCreationLook")
|
||||
CycleCharCustomization(id, -1)
|
||||
end
|
||||
end
|
||||
|
||||
function CharacterCustomization_Right(id)
|
||||
PlaySound("gsCharacterCreationLook");
|
||||
CycleCharCustomization(id, 1);
|
||||
if not CharacterCreate_CycleExtendedCustomization(id, 1) then
|
||||
PlaySound("gsCharacterCreationLook")
|
||||
CycleCharCustomization(id, 1)
|
||||
end
|
||||
end
|
||||
|
||||
function CharacterCreate_UpdateCustomizationAssetDebug()
|
||||
if CharacterCreate.extendedCustomizationApplying then return end
|
||||
CharacterCreate_UpdateExtendedCustomization(CharacterCreate_GetCustomizationState())
|
||||
end
|
||||
|
||||
function CharacterCreate_Randomize()
|
||||
@@ -1792,6 +2348,9 @@ function CharacterCreate_UpdateHairCustomization()
|
||||
CharacterCustomizationButtonFrame3Text:SetText(_G["HAIR_"..GetHairCustomization().."_STYLE"]);
|
||||
CharacterCustomizationButtonFrame4Text:SetText(_G["HAIR_"..GetHairCustomization().."_COLOR"]);
|
||||
CharacterCustomizationButtonFrame5Text:SetText(_G["FACIAL_HAIR_"..GetFacialHairCustomization()]);
|
||||
if CharacterCreate.extendedCustomizationApplying then return end
|
||||
CharacterCreate.extendedCustomizationStateKey = nil
|
||||
CharacterCreate_UpdateExtendedCustomization()
|
||||
end
|
||||
|
||||
function SetButtonDesaturated(button, desaturated, r, g, b)
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
if( self.enable ) then
|
||||
CharacterClass_OnClick(self:GetID());
|
||||
CharacterClass_OnClick(self:GetID());
|
||||
CharacterCreateTooltip:Hide();
|
||||
end
|
||||
</OnClick>
|
||||
@@ -127,27 +127,27 @@
|
||||
</CheckButton>
|
||||
|
||||
<Frame name="CharacterCustomizationFrameTemplate" virtual="true">
|
||||
<Size>
|
||||
<AbsDimension x="230" y="32"/>
|
||||
</Size>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture name="$parentBackground" file="Interface\Glues\CharacterCreate\charactercreate">
|
||||
<Size>
|
||||
<AbsDimension x="150" y="34"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0.508300781" right="0.656738281" top="0.244140625" bottom="0.279296875"/>
|
||||
</Texture>
|
||||
<FontString name="$parentText" inherits="GlueFontHighlightSmall">
|
||||
<Anchors>
|
||||
<Anchor point="CENTER" x="0" y="2"/>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Size>
|
||||
<AbsDimension x="230" y="32"/>
|
||||
</Size>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture name="$parentBackground" file="Interface\Glues\CharacterCreate\charactercreate">
|
||||
<Size>
|
||||
<AbsDimension x="180" y="34"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
</Anchors>
|
||||
<TexCoords left="0.508300781" right="0.656738281" top="0.244140625" bottom="0.279296875"/>
|
||||
</Texture>
|
||||
<FontString name="$parentText" inherits="GlueFontHighlightSmall">
|
||||
<Anchors>
|
||||
<Anchor point="CENTER" x="0" y="2"/>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Button name="$parentRightButton">
|
||||
<Size x="24" y="24"/>
|
||||
@@ -540,7 +540,6 @@
|
||||
<CheckButton name="CharacterCreateClassButton10" inherits="CharacterCreateClassButtonTemplate" id="10"/>
|
||||
</Frames>
|
||||
</Frame>
|
||||
<!-- Frames de personalización independientes -->
|
||||
<Frame name="CharacterCustomizationButtonFrame1" inherits="CharacterCustomizationFrameTemplate" id="1">
|
||||
<Anchors>
|
||||
<Anchor point="CENTER" x="900" y="150"/>
|
||||
@@ -832,7 +831,7 @@
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
|
||||
|
||||
<Button name="CharCreateBackButton" inherits="GlueButtonTemplate" text="BACK">
|
||||
<Size x="150" y="50"/>
|
||||
<Anchors>
|
||||
@@ -858,7 +857,7 @@
|
||||
</OnUpdate>
|
||||
</Scripts>
|
||||
</Button>
|
||||
|
||||
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnMouseDown>
|
||||
@@ -868,7 +867,7 @@
|
||||
CharacterCreateFrame_OnMouseUp(button);
|
||||
</OnMouseUp>
|
||||
<OnUpdate>
|
||||
CharacterCreateFrame_OnUpdate();
|
||||
CharacterCreateFrame_OnUpdate(arg1);
|
||||
</OnUpdate>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
|
||||
Reference in New Issue
Block a user