Module:Modifiers: Difference between revisions

Implement modType criteria for checking if a modifier is deemed to have a positive or negative effect upon the target
(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,
['type'] = keyDefn.type
['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, modifierAliases)
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 _, modifierID in ipairs(modifierIDs) do
for _, idObject in ipairs(modifierIDs) do
local modDefn = p.getModifierByID(modifierID)
local modifierID = idObject.id
if modDefn == nil then
local modType = idObject.type
error('No such modifier ID: ' .. modifierID)
local modProps = idObject.props or {}
end
 
if modType == 'id' then
local modDefn = p.getModifierByID(modifierID)
if modDefn == nil then
error('No such modifier ID: ' .. modifierID, 2)
end


for _, allowedScope in ipairs(modDefn.allowedScopes) do
-- Add all scopes
if Shared.tableIsEmpty(allowedScope.scopes) then
for _, allowedScope in ipairs(modDefn.allowedScopes) do
-- No scope definitions, so add modifier once with an empty scope
table.insert(matchCriteria, {
table.insert(matchCriteria, {
["mod"] = modDefn,
["mod"] = modDefn,
["scope"] = {}
["scope"] = allowedScope.scopes or {},
["props"] = modProps
})
})
else
-- Add all available scope definitions
for _, scopeDefn in ipairs(allowedScope.scopes) do
table.insert(matchCriteria, {
["mod"] = modDefn,
["scope"] = scopeDefn
})
end
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
-- For alias IDs, simply add these one at a time to the table
if modifierAliases ~= nil then
for _, modifierAlias in ipairs(modifierAliases) do
table.insert(matchCriteria, {
["alias"] = modifierAlias
})
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, skillID)
-- 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, valueType = matchDefn.mod, matchDefn.scope, {}, matchDefn.type
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, valueType = aliasData.mod, aliasData.scope.scopes, aliasData.alias, aliasData.type
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, ["valueType"] = valueType })
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 dataCriteria = {
["skillID"] = skillID,
["valueType"] = matchDefn.valueType
}
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, dataCriteria)
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('Skill data is required for scope type ' .. tKey, 2)
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 ' .. Shared.formatnum(modCount.overflow) .. ' more modifiers" ',
'"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 item = GameData.getEntityByID('items', 'melvorF:Miners_Helmet') --'melvorD:Aorpheats_Signet_Ring')
local obj = GameData.getEntityByID('pets', 'melvorF:Harley') --'melvorD:Aorpheats_Signet_Ring')
return p.getModifiersText(item.modifiers, true, false, 5)
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', 'melvorD:Aorpheats_Signet_Ring')
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"] = 'decreasedChanceToDoubleItemsSkill',
["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, 'melvorD:Thieving')
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