16,253
edits
(Amend error handling) |
(Implement modType criteria for checking if a modifier is deemed to have a positive or negative effect upon the target) |
||
(5 intermediate revisions by 2 users not shown) | |||
Line 6: | Line 6: | ||
local Shared = require('Module:Shared') | local Shared = require('Module:Shared') | ||
local Common = require('Module:Common') | local Common = require('Module:Common') | ||
local Num = require('Module:Number') | |||
-- Initialisation | -- Initialisation | ||
Line 32: | Line 33: | ||
['mod'] = modDefn, | ['mod'] = modDefn, | ||
['alias'] = modAlias, | ['alias'] = modAlias, | ||
[' | ['props'] = { ['valueType'] = keyDefn.type } | ||
} | } | ||
end | end | ||
Line 51: | Line 52: | ||
['key'] = 'damageType', | ['key'] = 'damageType', | ||
['templateKey'] = 'damageType', | ['templateKey'] = 'damageType', | ||
['templateKey2'] = 'resistanceName' | ['templateKey2'] = 'resistanceName', | ||
['gameDataKey'] = 'damageTypes' | |||
}, | }, | ||
['realmID'] = { | ['realmID'] = { | ||
['key'] = 'realm', | ['key'] = 'realm', | ||
['templateKey'] = 'realmName' | ['templateKey'] = 'realmName', | ||
['gameDataKey'] = 'realms' | |||
}, | }, | ||
['currencyID'] = { | ['currencyID'] = { | ||
['key'] = 'currency', | ['key'] = 'currency', | ||
['templateKey'] = 'currencyName' | ['templateKey'] = 'currencyName', | ||
['gameDataKey'] = 'currencies' | |||
}, | }, | ||
['categoryID'] = { | ['categoryID'] = { | ||
Line 75: | Line 79: | ||
['itemID'] = { | ['itemID'] = { | ||
['key'] = 'item', | ['key'] = 'item', | ||
['templateKey'] = 'itemName' | ['templateKey'] = 'itemName', | ||
['gameDataKey'] = 'items' | |||
}, | }, | ||
['effectGroupID'] = { | ['effectGroupID'] = { | ||
['key'] = 'effectGroup', | ['key'] = 'effectGroup', | ||
['templateKey'] = 'effectGroupName' | ['templateKey'] = 'effectGroupName', | ||
['gameDataKey'] = 'combatEffectGroups' | |||
}, | }, | ||
} | } | ||
local ScopeKeyToIDMap = {} | |||
for idKey, defn in pairs(ScopeKeyMap) do | |||
ScopeKeyToIDMap[defn.key] = idKey | |||
end | |||
-- Retrieves a modifier definition by ID | -- Retrieves a modifier definition by ID | ||
Line 126: | Line 137: | ||
-- At this point, both definitions have the same elements | -- At this point, both definitions have the same elements | ||
return true | return true | ||
end | |||
-- Given a table containing one or more sets of data criteria, combines those criteria into a | |||
-- single set of criteria. Should the same criteria key be specified more than once, the value from | |||
-- the last set containing that key will be retained | |||
function p.combineDataCriteria(dataCriteriaList) | |||
local rv = {} | |||
for _, dataCriteria in ipairs(dataCriteriaList) do | |||
for dataKey, dataValue in pairs(dataCriteria) do | |||
rv[dataKey] = dataValue | |||
end | |||
end | |||
return rv | |||
end | |||
-- Given data criteria where the values are entity names, converts those names to IDs. | |||
function p.convertCriteriaNamesToIDs(dataCriteriaByName) | |||
local dataCriteria = {} | |||
for criteriaKey, criteriaName in pairs(dataCriteriaByName) do | |||
if criteriaName == nil or criteriaName == '' then | |||
error('Value for criteria ' .. criteriaKey .. ' cannot be nil', 2) | |||
end | |||
if criteriaKey == 'valueType' or criteriaKey == 'modType' then | |||
-- Special cases: | |||
-- valueType restricts by sign of modifier value | |||
-- modType restricts by whether the modifier is deemed to be a positive or | |||
-- negative to the target | |||
dataCriteria[criteriaKey] = string.lower(criteriaName) | |||
elseif ScopeKeyMap[criteriaKey] ~= nil then | |||
-- ID specified directly | |||
dataCriteria[criteriaKey] = criteriaName | |||
else | |||
local criteriaIDKey = ScopeKeyToIDMap[criteriaKey] | |||
if criteriaIDKey == nil then | |||
error('Invalid criteria specified: ' .. criteriaKey, 2) | |||
end | |||
local keyMap = ScopeKeyMap[criteriaIDKey] | |||
local criteriaID = nil | |||
if keyMap.gameDataKey ~= nil then | |||
local criteriaData = GameData.getEntityByName(keyMap.gameDataKey, criteriaName) | |||
if criteriaData ~= nil then | |||
criteriaID = criteriaData.id | |||
end | |||
elseif criteriaKey == 'skill' then | |||
criteriaID = Common.getSkillID(criteriaName) | |||
else | |||
error('Criteria ' .. criteriaKey .. ' is currently unsupported', 2) | |||
end | |||
if criteriaID == nil then | |||
error('Unknown ' .. criteriaKey .. ': ' .. criteriaName, 2) | |||
end | |||
dataCriteria[criteriaIDKey] = criteriaID | |||
end | |||
end | |||
return dataCriteria | |||
end | end | ||
Line 134: | Line 203: | ||
-- "valueType". This indicates whether the value of the modifier should be positive ('pos') or | -- "valueType". This indicates whether the value of the modifier should be positive ('pos') or | ||
-- negative ('neg') in order for the criteria to be met | -- negative ('neg') in order for the criteria to be met | ||
function p.checkScopeDataMeetsCriteria(scopeData, scopeDefn, dataCriteria) | function p.checkScopeDataMeetsCriteria(modDefn, scopeData, scopeDefn, dataCriteria) | ||
for criteriaKey, criteriaValue in pairs(dataCriteria) do | for criteriaKey, criteriaValue in pairs(dataCriteria) do | ||
if criteriaKey == 'valueType' then | if criteriaKey == 'valueType' then | ||
Line 147: | Line 216: | ||
-- Value criteria not met | -- Value criteria not met | ||
return false | return false | ||
end | |||
elseif criteriaKey == 'modType' then | |||
if criteriaValue ~= nil and scopeData.value ~= nil then | |||
local isInverted = modDefn.inverted | |||
if isInverted == nil then | |||
isInverted = false | |||
end | |||
local isPositive = ((isInverted and scopeData.value < 0) or (not isInverted and scopeData.value > 0)) | |||
if not ( | |||
(criteriaValue == 'pos' and isPositive) | |||
or (criteriaValue == 'neg' and not isPositive) | |||
) then | |||
-- Mod type criteria not met | |||
return false | |||
end | |||
end | end | ||
elseif criteriaValue ~= nil and criteriaKey ~= 'key' then | elseif criteriaValue ~= nil and criteriaKey ~= 'key' then | ||
Line 173: | Line 257: | ||
-- Given a list of modifier IDs and aliases, returns all match criteria for these. | -- Given a list of modifier IDs and aliases, returns all match criteria for these. | ||
-- This matching criteria can then be pased to p.getMatchingModifiers() | -- This matching criteria can then be pased to p.getMatchingModifiers() | ||
function p.getMatchCriteriaFromIDs(modifierIDs | function p.getMatchCriteriaFromIDs(modifierIDs) | ||
local matchCriteria = {} | local matchCriteria = {} | ||
-- For modifier IDs, find the relevant mod definition and add all allowed scopes | -- For modifier IDs, find the relevant mod definition and add all allowed scopes | ||
if modifierIDs ~= nil then | if modifierIDs ~= nil then | ||
for _, | for _, idObject in ipairs(modifierIDs) do | ||
local modDefn = p.getModifierByID(modifierID) | local modifierID = idObject.id | ||
local modType = idObject.type | |||
local modProps = idObject.props or {} | |||
if modType == 'id' then | |||
local modDefn = p.getModifierByID(modifierID) | |||
if modDefn == nil then | |||
error('No such modifier ID: ' .. modifierID, 2) | |||
end | |||
-- Add all scopes | |||
for _, allowedScope in ipairs(modDefn.allowedScopes) do | |||
table.insert(matchCriteria, { | table.insert(matchCriteria, { | ||
["mod"] = modDefn, | ["mod"] = modDefn, | ||
["scope"] = {} | ["scope"] = allowedScope.scopes or {}, | ||
["props"] = modProps | |||
}) | }) | ||
end | end | ||
elseif modType == 'alias' then | |||
-- For alias IDs, simply add these one at a time to the table | |||
table.insert(matchCriteria, { | |||
["alias"] = modifierID, | |||
["props"] = modProps | |||
}) | |||
else | |||
error('Unknown modifier ID type: ' .. (modType or 'nil'), 2) | |||
end | end | ||
end | end | ||
end | end | ||
Line 218: | Line 299: | ||
-- when they relate to that given skill ID | -- when they relate to that given skill ID | ||
-- matchCriteria is a table of elements structured as follows: | -- matchCriteria is a table of elements structured as follows: | ||
-- { ["mod"] = modDefn, ["scope"] = scopeDefn, ["alias"] = modAlias } | -- { | ||
-- ["mod"] = modDefn, | |||
-- ["scope"] = scopeDefn, | |||
-- ["alias"] = modAlias, | |||
-- ["props"] = modProps | |||
-- } | |||
-- Examples of valid mod and scope definitions can be obtained from | -- Examples of valid mod and scope definitions can be obtained from | ||
-- p.getModifierByID() and p.getScope() | -- p.getModifierByID() and p.getScope() | ||
-- alias is an optional property, if specified mod and scope are derived | -- alias is an optional property, if specified mod and scope are derived | ||
-- from the modAlias. | -- from the modAlias. | ||
function p.getMatchingModifiers(modifiers, matchCriteria | -- props is an optional property, if specified then a modifier is only matched | ||
-- if the properties (skillID, itemID, etc.) also match. | |||
function p.getMatchingModifiers(modifiers, matchCriteria) | |||
local resultMods = { | local resultMods = { | ||
["matched"] = {}, | ["matched"] = {}, | ||
Line 232: | Line 320: | ||
local matchCriteriaMap = {} | local matchCriteriaMap = {} | ||
for _, matchDefn in ipairs(matchCriteria) do | for _, matchDefn in ipairs(matchCriteria) do | ||
local modDefn, scopeDefn, modAlias, | local modDefn, scopeDefn, modAlias, propCriteria = matchDefn.mod, matchDefn.scope, {}, (matchDefn.props or {}) | ||
if matchDefn.alias ~= nil then | if matchDefn.alias ~= nil then | ||
local aliasData = p.getModifierByAlias(matchDefn.alias) | local aliasData = p.getModifierByAlias(matchDefn.alias) | ||
Line 238: | Line 326: | ||
error('No such modifier alias: ' .. matchDefn.alias, 2) | error('No such modifier alias: ' .. matchDefn.alias, 2) | ||
else | else | ||
modDefn, scopeDefn, modAlias, | local aliasProps = p.combineDataCriteria({propCriteria, aliasData.props}) | ||
modDefn, scopeDefn, modAlias, propCriteria = aliasData.mod, aliasData.scope.scopes, aliasData.alias, aliasProps | |||
end | end | ||
Line 246: | Line 335: | ||
matchCriteriaMap[modLocalID] = {} | matchCriteriaMap[modLocalID] = {} | ||
end | end | ||
table.insert(matchCriteriaMap[modLocalID], { ["scope"] = scopeDefn, ["alias"] = modAlias, [" | table.insert(matchCriteriaMap[modLocalID], { | ||
["mod"] = modDefn, | |||
["scope"] = scopeDefn, | |||
["alias"] = modAlias, | |||
["props"] = propCriteria | |||
}) | |||
end | end | ||
Line 268: | Line 362: | ||
local modScopeDefn = p.convertScopeDataToDefinition(scopeData) | local modScopeDefn = p.convertScopeDataToDefinition(scopeData) | ||
for _, matchDefn in ipairs(modMatchCriteria) do | for _, matchDefn in ipairs(modMatchCriteria) do | ||
local scopeDefn, modAlias = matchDefn.scope, matchDefn.alias | local modDefn, scopeDefn, modAlias, propCriteria = matchDefn.mod, matchDefn.scope, matchDefn.alias, matchDefn.props | ||
local matchKey = 'unmatched' | local matchKey = 'unmatched' | ||
-- Check that: | -- Check that: | ||
Line 280: | Line 370: | ||
if ( | if ( | ||
p.doScopeDefinitionsMatch(modScopeDefn, scopeDefn) | p.doScopeDefinitionsMatch(modScopeDefn, scopeDefn) | ||
and p.checkScopeDataMeetsCriteria(scopeData, scopeDefn, modAlias) | and p.checkScopeDataMeetsCriteria(modDefn, scopeData, scopeDefn, modAlias) | ||
and p.checkScopeDataMeetsCriteria(scopeData, scopeDefn, | and p.checkScopeDataMeetsCriteria(modDefn, scopeData, scopeDefn, propCriteria) | ||
) then | ) then | ||
-- Add to matched table | -- Add to matched table | ||
Line 388: | Line 478: | ||
-- affect certain categories of spells | -- affect certain categories of spells | ||
local scopeSourceID, scopeSourceData = scopeDefn.scopeSource or scopeData.skillID, nil | local scopeSourceID, scopeSourceData = scopeDefn.scopeSource or scopeData.skillID, nil | ||
if scopeSourceID == 'AttackSpell' then | if scopeSourceID == 'melvorD:AttackSpell' then | ||
-- Uses spell categories, contained within magic skill data | -- Uses spell categories, contained within magic skill data | ||
scopeSourceData = GameData.skillData.Magic | scopeSourceData = GameData.skillData.Magic | ||
elseif scopeSourceID == 'melvorD:CombatArea' then | |||
scopeSourceData = GameData.rawData | |||
elseif scopeSourceID ~= nil then | elseif scopeSourceID ~= nil then | ||
-- Assumed to be a skill ID | -- Assumed to be a skill ID | ||
Line 433: | Line 525: | ||
elseif tKey == 'categoryName' then | elseif tKey == 'categoryName' then | ||
if scopeSourceData == nil then | if scopeSourceData == nil then | ||
error(' | error('Scope data is required for scope type ' .. tKey, 2) | ||
end | end | ||
local catKey = ( | local catKey = ( | ||
(scopeSourceID == 'AttackSpell' and 'spellCategories') | (scopeSourceID == 'melvorD:AttackSpell' and 'spellCategories') | ||
or (scopeSourceID == 'melvorD:CombatArea' and 'combatAreaCategories') | |||
or (scopeSourceID == 'melvorD:Thieving' and 'areas') | or (scopeSourceID == 'melvorD:Thieving' and 'areas') | ||
or (scopeSourceID == 'melvorD:Township' and 'biomes') | or (scopeSourceID == 'melvorD:Township' and 'biomes') | ||
Line 470: | Line 563: | ||
if scopeSourceData == nil then | if scopeSourceData == nil then | ||
error('Scope source data is required for scope type ' .. tKey, 2) | error('Scope source data is required for scope type ' .. tKey, 2) | ||
elseif scopeSourceID == 'AttackSpell' then | elseif scopeSourceID == 'melvorD:AttackSpell' then | ||
subcategory = GameData.getEntityByID(scopeSourceData.spellCategories, v) | subcategory = GameData.getEntityByID(scopeSourceData.spellCategories, v) | ||
else | else | ||
Line 601: | Line 694: | ||
overflowText = table.concat({ | overflowText = table.concat({ | ||
'<br><span class="mw-collapsible mw-collapsed" data-expandtext=', | '<br><span class="mw-collapsible mw-collapsed" data-expandtext=', | ||
'"Show ' .. | '"Show ' .. Num.formatnum(modCount.overflow) .. ' more modifiers" ', | ||
'data-collapsetext="Hide">', | 'data-collapsetext="Hide">', | ||
table.concat(modArray.overflow, entrySep), | table.concat(modArray.overflow, entrySep), | ||
Line 646: | Line 739: | ||
function p.test2() | function p.test2() | ||
local | local obj = GameData.getEntityByID('pets', 'melvorF:Harley') --'melvorD:Aorpheats_Signet_Ring') | ||
return p.getModifiersText( | return p.getModifiersText(obj.modifiers, true, false, 5) | ||
--return p.getModifierSkills(item.modifiers) | --return p.getModifierSkills(item.modifiers) | ||
end | end | ||
function p.test3() | function p.test3() | ||
local item = GameData.getEntityByID('items', ' | local item = GameData.getEntityByID('items', 'melvorItA:Corrupted_Light_Consumable_I') | ||
local matchCriteria = { | local matchCriteria = { | ||
{ | { | ||
Line 660: | Line 753: | ||
--} | --} | ||
-- Should no longer match if changed to increased | -- Should no longer match if changed to increased | ||
["alias"] = ' | ["alias"] = 'increasedChanceNoDamageMining', | ||
}, | }, | ||
{ | { | ||
Line 680: | Line 773: | ||
["damageType"] = true | ["damageType"] = true | ||
} | } | ||
}, | |||
{ | |||
["mod"] = p.getModifierByID('flatBaseRandomProductQuantity'), | |||
["scope"] = { | |||
["item"] = true, | |||
["skill"] = true | |||
}, | |||
["props"] = p.convertCriteriaNamesToIDs({ ["item"] = 'Abyssal Stardust' }) | |||
} | } | ||
} | } | ||
return p.getMatchingModifiers(item.modifiers, matchCriteria | return p.getMatchingModifiers(item.modifiers, matchCriteria) | ||
end | end | ||
Line 696: | Line 797: | ||
return p.getModifierValue(matchedMods.matched) | return p.getModifierValue(matchedMods.matched) | ||
--return p.getMatchingModifiers(purch.contains.modifiers, matchCriteria) | --return p.getMatchingModifiers(purch.contains.modifiers, matchCriteria) | ||
end | |||
-- Checks p.getMatchCriteriaFromIDs() and p.getMatchingModifiers() | |||
-- Any entity processed by this should return a table with all modifiers | |||
-- matched and none unmatched. Unmatched modifiers implies an issue with one | |||
-- of the aforementioned functions | |||
function p.test5() | |||
local ent = GameData.getEntityByID('items', 'melvorF:Miners_Helmet') | |||
local entMods = ent.modifiers | |||
if entMods ~= nil then | |||
local modIDs = {} | |||
for modID, modDet in pairs(entMods) do | |||
table.insert(modIDs, { | |||
["id"] = modID, | |||
["type"] = 'id', | |||
["props"] = {} | |||
}) | |||
end | |||
local matchCriteria = p.getMatchCriteriaFromIDs(modIDs) | |||
mw.logObject(matchCriteria) | |||
mw.log('=======================================================') | |||
local matchingMods = p.getMatchingModifiers(entMods, matchCriteria) | |||
return matchingMods | |||
end | |||
return nil | |||
end | end | ||
return p | return p |