Нет описания правки |
Нет описания правки |
||
(не показано 17 промежуточных версий 2 участников) | |||
Строка 16: | Строка 16: | ||
local i18n = { |
local i18n = { |
||
cats = { |
cats = { |
||
− | data = ' |
+ | data = 'Данные монстров', |
+ | boss = 'Босс', |
||
}, |
}, |
||
tooltips = { |
tooltips = { |
||
Строка 48: | Строка 49: | ||
life = 'Здоровье', |
life = 'Здоровье', |
||
damage = 'Урон', |
damage = 'Урон', |
||
+ | aps = 'Attacks per second', |
||
+ | critical_strike_chance_total = 'Critical strike chance', |
||
+ | armour = 'Armour rating', |
||
+ | evasion = 'Evasion rating', |
||
+ | accuracy = 'Accuracy rating', |
||
+ | experience = 'Experience', |
||
+ | summon_life = 'Summon life', |
||
+ | monster_data = 'Monster data', |
||
+ | |||
}, |
}, |
||
Строка 212: | Строка 222: | ||
function h.stat_match(stats, strings, result) |
function h.stat_match(stats, strings, result) |
||
--[[ |
--[[ |
||
− | Match |
+ | Match strings to ids in result and append them to stats. |
+ | |||
+ | Parameters |
||
+ | ---------- |
||
+ | stats : Array, required. |
||
+ | Array to append values to. |
||
+ | strings : array, required. |
||
+ | Array of ids to to match result to. |
||
+ | result : array, required. |
||
+ | Row result from a cargo_query. Must contain 'mod_stats.id' and |
||
+ | 'mod_stats.max'. |
||
Examples |
Examples |
||
Строка 252: | Строка 272: | ||
--[[ |
--[[ |
||
Format the stat value. |
Format the stat value. |
||
+ | |||
+ | Parameters |
||
+ | ---------- |
||
+ | args : Array, required. |
||
+ | Array of arguments to modify the stat format. Supported keys are: |
||
+ | args.fmt |
||
+ | args.level |
||
+ | args.value |
||
+ | args.value_verbose |
||
]] |
]] |
||
− | local fmt = '%0. |
+ | local fmt = '%0.2f' |
if args.value > 10000 then |
if args.value > 10000 then |
||
fmt = '%0.3E' |
fmt = '%0.3E' |
||
+ | elseif args.value > 100 then |
||
+ | fmt = '%0.0f' |
||
end |
end |
||
return m_util.html.abbr( |
return m_util.html.abbr( |
||
− | string.format(fmt, args.value), |
+ | string.format(args.fmt or fmt, args.value), |
string.format('Lvl. %0.0f: %s', args.level, args.value_verbose) |
string.format('Lvl. %0.0f: %s', args.level, args.value_verbose) |
||
) |
) |
||
end |
end |
||
+ | |||
+ | function h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings) |
||
+ | --[[ |
||
+ | Calculate the total stat value per monster level. |
||
+ | |||
+ | Parameters |
||
+ | ---------- |
||
+ | f_stats : Function, required. |
||
+ | Function returning an array of stat values per monster level. |
||
+ | f_strings : function, required. |
||
+ | Function returning an array of stat ids to match to cargo results per |
||
+ | monster level. |
||
+ | ]] |
||
+ | |||
+ | -- Stop if no monster level was found: |
||
+ | if #tpl_args.monster_level == 0 then |
||
+ | return nil |
||
+ | end |
||
+ | |||
+ | -- Calculate the total stat value for each monster level: |
||
+ | local out = {} |
||
+ | for i, result in ipairs(tpl_args._mod_data['monster_level']) do |
||
+ | -- Initial stats for monsters: |
||
+ | local stats = f_stats(tpl_args, frame, result) |
||
+ | |||
+ | -- Append matching stats from the modifiers: |
||
+ | for _, modid in ipairs(tpl_args._mods) do |
||
+ | local mod = tpl_args._mod_data[modid] |
||
+ | for _, v in ipairs(mod) do |
||
+ | h.stat_match( |
||
+ | stats, |
||
+ | f_strings(tpl_args, frame, result), |
||
+ | v |
||
+ | ) |
||
+ | end |
||
+ | end |
||
+ | |||
+ | -- Calculate the total stat value and format the output: |
||
+ | out[i] = h.stat_format{ |
||
+ | level=result['monster_base_stats.level'], |
||
+ | value=h.stat_calc(stats), |
||
+ | value_verbose=h.stat_calc_verbose(stats), |
||
+ | } |
||
+ | end |
||
+ | |||
+ | return table.concat(out, ', ') |
||
+ | end |
||
+ | |||
function h.intro_text(tpl_args, frame) |
function h.intro_text(tpl_args, frame) |
||
Строка 291: | Строка 370: | ||
return table.concat(out) |
return table.concat(out) |
||
+ | end |
||
+ | |||
+ | function h.info_box(tpl_args, frame, tbl_view) |
||
+ | -- Create the infobox: |
||
+ | local container = mw.html.create('div') |
||
+ | container |
||
+ | :attr('class', 'modbox floatright') |
||
+ | |||
+ | local tbl = container:tag('table') |
||
+ | tbl |
||
+ | :attr('class', 'wikitable') |
||
+ | -- :attr('style', 'float:right; margin-left: 10px;') |
||
+ | |||
+ | for _, data in ipairs(tbl_view) do |
||
+ | local v = data.func(tpl_args, frame) |
||
+ | |||
+ | if v ~= nil and v ~= '' then |
||
+ | local tr = tbl:tag('tr') |
||
+ | if data.header then |
||
+ | tr:tag('th'):wikitext(data.header):done() |
||
+ | tr:tag('td'):wikitext(v):done() |
||
+ | else |
||
+ | tr:tag('th'):attr('colspan', 2):wikitext(v):done() |
||
+ | end |
||
+ | end |
||
+ | end |
||
+ | |||
+ | return tostring(container) |
||
+ | end |
||
+ | |||
+ | function h.stat_box(tpl_args, frame) |
||
+ | --[[ |
||
+ | Display the stat box. |
||
+ | ]] |
||
+ | local container = mw.html.create('div') |
||
+ | container |
||
+ | :attr('class', 'modbox floatright') |
||
+ | |||
+ | -- stat table |
||
+ | local tbl = container:tag('table') |
||
+ | tbl |
||
+ | :attr('class', 'wikitable sortable') |
||
+ | -- :attr('style', 'style="width: 100%;"') |
||
+ | :tag('tr') |
||
+ | :tag('th') |
||
+ | :attr('colspan', 4) |
||
+ | :wikitext('Stats') |
||
+ | :done() |
||
+ | :done() |
||
+ | :tag('tr') |
||
+ | :tag('th') |
||
+ | :wikitext('#') |
||
+ | :done() |
||
+ | :tag('th') |
||
+ | :wikitext('Stat Id') |
||
+ | :done() |
||
+ | :tag('th') |
||
+ | :wikitext('Min') |
||
+ | :done() |
||
+ | :tag('th') |
||
+ | :wikitext('Max') |
||
+ | :done() |
||
+ | :done() |
||
+ | :done() |
||
+ | |||
+ | local i = 0 |
||
+ | for _, modid in ipairs(tpl_args._mods) do |
||
+ | local mod = tpl_args._mod_data[modid] |
||
+ | for k, v in ipairs(mod) do |
||
+ | if v['mod_stats.id'] then |
||
+ | i = i + 1 |
||
+ | |||
+ | local linked_stat = v['mod_stats.id'] |
||
+ | if v['mod_stats._pageName'] then |
||
+ | linked_stat = string.format( |
||
+ | '[[%s|%s]]', |
||
+ | v['mod_stats._pageName'], |
||
+ | v['mod_stats.id'] |
||
+ | ) |
||
+ | end |
||
+ | |||
+ | tbl |
||
+ | :tag('tr') |
||
+ | :tag('td') |
||
+ | :wikitext(i) |
||
+ | :done() |
||
+ | :tag('td') |
||
+ | :wikitext(linked_stat) |
||
+ | :done() |
||
+ | :tag('td') |
||
+ | :wikitext(v['mod_stats.min']) |
||
+ | :done() |
||
+ | :tag('td') |
||
+ | :wikitext(v['mod_stats.max']) |
||
+ | :done() |
||
+ | :done() |
||
+ | :done() |
||
+ | end |
||
+ | end |
||
+ | end |
||
+ | |||
+ | return tostring(container) |
||
end |
end |
||
Строка 300: | Строка 481: | ||
tables.monsters = { |
tables.monsters = { |
||
table = 'monsters', |
table = 'monsters', |
||
+ | order = { |
||
− | order = {'metadata_id', 'tags', 'monster_type_id', 'mod_ids', 'part1_mod_ids', 'part2_mod_ids', 'endgame_mod_ids', 'skill_ids', 'name', 'size', 'minimum_attack_distance', 'maximum_attack_distance', 'model_size_multiplier', 'experience_multiplier', 'damage_multiplier', 'health_multiplier', 'critical_strike_chance', 'attack_speed', 'mods', 'is_boss', 'rarity_id', 'rarity'}, |
||
+ | 'metadata_id', |
||
+ | 'tags', |
||
+ | 'monster_type_id', |
||
+ | 'mod_ids', |
||
+ | 'part1_mod_ids', |
||
+ | 'part2_mod_ids', |
||
+ | 'endgame_mod_ids', |
||
+ | 'skill_ids', |
||
+ | 'name', |
||
+ | 'size', |
||
+ | 'minimum_attack_distance', |
||
+ | 'maximum_attack_distance', |
||
+ | 'model_size_multiplier', |
||
+ | 'experience_multiplier', |
||
+ | 'damage_multiplier', |
||
+ | 'health_multiplier', |
||
+ | 'critical_strike_chance', |
||
+ | 'attack_speed', |
||
+ | 'mods', |
||
+ | 'is_boss', |
||
+ | 'rarity_id', |
||
+ | 'rarity' |
||
+ | }, |
||
fields = { |
fields = { |
||
metadata_id = { |
metadata_id = { |
||
Строка 307: | Строка 511: | ||
func = function (tpl_args, frame, value) |
func = function (tpl_args, frame, value) |
||
tpl_args.monster_usages = m_cargo.query( |
tpl_args.monster_usages = m_cargo.query( |
||
− | {'areas', 'maps'}, |
+ | {'areas', 'maps', 'items'}, |
{ |
{ |
||
'areas.name', |
'areas.name', |
||
Строка 313: | Строка 517: | ||
'areas.area_level', |
'areas.area_level', |
||
'areas.boss_monster_ids', |
'areas.boss_monster_ids', |
||
+ | 'areas.modifier_ids', |
||
'areas.main_page', |
'areas.main_page', |
||
'areas._pageName', |
'areas._pageName', |
||
'maps.area_level', |
'maps.area_level', |
||
+ | 'maps._pageName', |
||
+ | 'items.drop_enabled' |
||
}, |
}, |
||
{ |
{ |
||
− | join= |
+ | join=[[ |
− | + | areas.id=maps.area_id, |
|
+ | maps._pageID=items._pageID |
||
+ | ]], |
||
+ | where=m_cargo.replace_holds{ |
||
+ | string=string.format( |
||
+ | [[ |
||
+ | CASE WHEN maps.area_level IS NOT NULL THEN |
||
+ | areas.boss_monster_ids HOLDS "%s" |
||
+ | AND items.drop_enabled = True |
||
+ | ELSE |
||
+ | areas.boss_monster_ids HOLDS "%s" |
||
+ | END |
||
+ | ]], |
||
+ | value, |
||
+ | value |
||
+ | ), |
||
+ | mode='regex' |
||
+ | }, |
||
+ | orderBy='maps.area_level, areas.area_level' |
||
} |
} |
||
) |
) |
||
Строка 355: | Строка 580: | ||
where=string.format('monster_types.id = "%s"', value), |
where=string.format('monster_types.id = "%s"', value), |
||
} |
} |
||
− | )[1] |
+ | )[1] or {} |
if tpl_args.monster_type['monster_types.tags'] then |
if tpl_args.monster_type['monster_types.tags'] then |
||
Строка 535: | Строка 760: | ||
'mod_stats.max', |
'mod_stats.max', |
||
'mod_stats.min', |
'mod_stats.min', |
||
+ | 'mod_stats._pageName', |
||
}, |
}, |
||
{ |
{ |
||
Строка 584: | Строка 810: | ||
'mods.generation_type', |
'mods.generation_type', |
||
'mod_stats.id', |
'mod_stats.id', |
||
+ | 'mod_stats.min', |
||
'mod_stats.max', |
'mod_stats.max', |
||
+ | 'mod_stats._pageName', |
||
}, |
}, |
||
{ |
{ |
||
Строка 716: | Строка 944: | ||
evasion = { |
evasion = { |
||
field = 'evasion', |
field = 'evasion', |
||
+ | type = 'Integer', |
||
+ | }, |
||
+ | armour = { |
||
+ | field = 'armour', |
||
type = 'Integer', |
type = 'Integer', |
||
}, |
}, |
||
Строка 840: | Строка 1072: | ||
-- header = i18n.tooltips.image, |
-- header = i18n.tooltips.image, |
||
func = function (tpl_args, frame) |
func = function (tpl_args, frame) |
||
+ | local image_name = tpl_args.name or tpl_args.monster_type_id |
||
+ | image_name = string.gsub(image_name, '[%[%]]', '') |
||
return string.format( |
return string.format( |
||
'[[File:%s monster screenshot.jpg|296x500px]]', |
'[[File:%s monster screenshot.jpg|296x500px]]', |
||
− | + | image_name |
|
) |
) |
||
end, |
end, |
||
Строка 898: | Строка 1132: | ||
'monster_map_multipliers.life', |
'monster_map_multipliers.life', |
||
'monster_map_multipliers.boss_life', |
'monster_map_multipliers.boss_life', |
||
+ | |||
− | |||
-- Damage: |
-- Damage: |
||
'monster_base_stats.damage', |
'monster_base_stats.damage', |
||
'monster_map_multipliers.damage', |
'monster_map_multipliers.damage', |
||
'monster_map_multipliers.boss_damage', |
'monster_map_multipliers.boss_damage', |
||
+ | |||
− | |||
+ | 'monster_base_stats.armour', |
||
'monster_base_stats.evasion', |
'monster_base_stats.evasion', |
||
'monster_base_stats.accuracy', |
'monster_base_stats.accuracy', |
||
Строка 964: | Строка 1199: | ||
header = i18n.tooltips.life, |
header = i18n.tooltips.life, |
||
func = function(tpl_args, frame) |
func = function(tpl_args, frame) |
||
+ | local f_stats = function(tpl_args, frame, result) |
||
− | --[[ |
||
− | TODO: Should this be stored to cargo? |
||
− | ]] |
||
− | |||
− | -- Stop if no monster level was found: |
||
− | if #tpl_args.monster_level == 0 then |
||
− | return nil |
||
− | end |
||
− | |||
− | -- Calculate the total stat value for each monster level: |
||
− | local out = {} |
||
− | for i, result in ipairs(tpl_args._mod_data['monster_level']) do |
||
− | -- Initial stats for monsters: |
||
local stats = { |
local stats = { |
||
added={ |
added={ |
||
Строка 985: | Строка 1208: | ||
}, |
}, |
||
-- more={}, |
-- more={}, |
||
− | + | m_map = (tpl_args.health_multiplier or 1) + (result['monster_map_multipliers.life'] or 0) |
|
− | m_map = (result['monster_map_multipliers.life'] or 0) + 1 |
||
} |
} |
||
− | |||
− | -- Bossses have different multipliers: |
||
if tpl_args.is_boss then |
if tpl_args.is_boss then |
||
− | + | stats.m_map = stats.m_map + (result['monster_map_multipliers.boss_life'] or 0)/100 |
|
− | stats.m_map2 = (result['monster_map_multipliers.boss_life'] or 0)/100 + 1 |
||
end |
end |
||
− | + | return stats |
|
− | + | end |
|
+ | |||
− | local mod = tpl_args._mod_data[modid] |
||
− | + | local f_strings = function(tpl_args, frame, result) |
|
− | + | local strings = { |
|
− | + | added={'base_maximum_life'}, |
|
− | + | increased={'maximum_life_+%', 'map_monsters_life_+%'}, |
|
− | + | more={ |
|
− | + | 'maximum_life_+%_final', |
|
− | + | 'monster_life_+%_final_from_rarity', |
|
− | + | }, |
|
− | + | } |
|
− | + | if tpl_args.is_boss then |
|
− | + | table.insert(strings.increased, 'map_boss_maximum_life_+%') |
|
− | v |
||
− | ) |
||
− | end |
||
end |
end |
||
− | + | return strings |
|
− | out[i] = h.stat_format{ |
||
− | level=result['monster_base_stats.level'], |
||
− | value=h.stat_calc(stats), |
||
− | value_verbose=h.stat_calc_verbose(stats), |
||
− | } |
||
end |
end |
||
− | return |
+ | return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings) |
+ | |||
end, |
end, |
||
}, |
}, |
||
Строка 1028: | Строка 1240: | ||
header = i18n.tooltips.damage, |
header = i18n.tooltips.damage, |
||
func = function(tpl_args, frame) |
func = function(tpl_args, frame) |
||
− | + | local f_stats = function(tpl_args, frame, result) |
|
− | |||
− | -- Stop if no monster level was found: |
||
− | if #tpl_args.monster_level == 0 then |
||
− | return nil |
||
− | end |
||
− | |||
− | -- Calculate the total stat value for each monster level: |
||
− | local out = {} |
||
− | for i, result in ipairs(tpl_args._mod_data['monster_level']) do |
||
− | -- Initial stats for monsters: |
||
local stats = { |
local stats = { |
||
added={ |
added={ |
||
Строка 1045: | Строка 1247: | ||
-- increased={}, |
-- increased={}, |
||
-- more={}, |
-- more={}, |
||
− | + | m_map = (tpl_args.damage_multiplier or 1) + (result['monster_map_multipliers.damage'] or 0) |
|
− | m_map = (result['monster_map_multipliers.damage'] or 0) + 1 |
||
} |
} |
||
+ | if tpl_args.is_boss then |
||
+ | stats.m_map = stats.m_map + (result['monster_map_multipliers.boss_damage'] or 0) |
||
+ | end |
||
− | + | return stats |
|
+ | end |
||
+ | |||
+ | local f_strings = function(tpl_args, frame, result) |
||
+ | local strings = { |
||
+ | -- added={}, |
||
+ | increased={'map_monsters_damage_+%'}, |
||
+ | more={'monster_rarity_damage_+%_final'}, |
||
+ | less={ |
||
+ | 'monster_rarity_attack_cast_speed_+%_and_damage_-%_final', |
||
+ | 'monster_base_type_attack_cast_speed_+%_and_damage_-%_final', |
||
+ | }, |
||
+ | } |
||
if tpl_args.is_boss then |
if tpl_args.is_boss then |
||
− | + | table.insert(strings.increased, 'map_boss_damage_+%') |
|
end |
end |
||
− | + | return strings |
|
− | + | end |
|
+ | |||
− | local mod = tpl_args._mod_data[modid] |
||
+ | return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings) |
||
− | for _, v in ipairs(mod) do |
||
− | + | end, |
|
+ | }, |
||
− | stats, |
||
+ | { |
||
− | { |
||
− | + | header = i18n.tooltips.aps, |
|
+ | func = function(tpl_args, frame) |
||
− | -- increased={}, |
||
+ | local f_stats = function(tpl_args, frame, result) |
||
− | more={'monster_rarity_damage_+%_final'}, |
||
+ | local stats = { |
||
− | less={'monster_rarity_attack_cast_speed_+%_and_damage_-%_final'}, |
||
− | + | added={ |
|
− | + | tpl_args.attack_speed or 1, |
|
− | + | }, |
|
− | + | } |
|
+ | return stats |
||
+ | end |
||
+ | |||
+ | local f_strings = function(tpl_args, frame, result) |
||
+ | local strings = { |
||
+ | increased={'map_monsters_attack_speed_+%'}, -- map_monsters_cast_speed_+% |
||
+ | more={ |
||
+ | 'monster_rarity_attack_cast_speed_+%_and_damage_-%_final', |
||
+ | 'monster_base_type_attack_cast_speed_+%_and_damage_-%_final', |
||
+ | }, |
||
+ | } |
||
+ | if tpl_args.is_boss then |
||
+ | table.insert(strings.increased, 'map_boss_attack_and_cast_speed_+%') |
||
end |
end |
||
− | + | return strings |
|
− | + | end |
|
+ | |||
− | level=result['monster_base_stats.level'], |
||
+ | return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings) |
||
− | value=h.stat_calc(stats), |
||
+ | end, |
||
− | value_verbose=h.stat_calc_verbose(stats), |
||
+ | }, |
||
+ | { |
||
+ | header = i18n.tooltips.critical_strike_chance_total, |
||
+ | func = function(tpl_args, frame) |
||
+ | local f_stats = function(tpl_args, frame, result) |
||
+ | local stats = { |
||
+ | added={ |
||
+ | tpl_args.critical_strike_chance, |
||
+ | }, |
||
} |
} |
||
+ | return stats |
||
end |
end |
||
− | + | local f_strings = function(tpl_args, frame, result) |
|
+ | local strings = { |
||
+ | increased={'map_monsters_critical_strike_chance_+%'}, |
||
+ | } |
||
+ | |||
+ | return strings |
||
+ | end |
||
+ | |||
+ | return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings) |
||
+ | end, |
||
+ | }, |
||
+ | { |
||
+ | header = i18n.tooltips.armour, |
||
+ | func = function(tpl_args, frame) |
||
+ | local f_stats = function(tpl_args, frame, result) |
||
+ | local stats = { |
||
+ | added={ |
||
+ | result['monster_base_stats.armour'], |
||
+ | }, |
||
+ | } |
||
+ | return stats |
||
+ | end |
||
+ | |||
+ | local f_strings = function(tpl_args, frame, result) |
||
+ | local strings = { |
||
+ | -- more={}, |
||
+ | } |
||
+ | |||
+ | return strings |
||
+ | end |
||
+ | |||
+ | return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings) |
||
+ | end, |
||
+ | }, |
||
+ | { |
||
+ | header = i18n.tooltips.evasion, |
||
+ | func = function(tpl_args, frame) |
||
+ | local f_stats = function(tpl_args, frame, result) |
||
+ | local stats = { |
||
+ | added={ |
||
+ | result['monster_base_stats.evasion'], |
||
+ | }, |
||
+ | } |
||
+ | return stats |
||
+ | end |
||
+ | |||
+ | local f_strings = function(tpl_args, frame, result) |
||
+ | local strings = { |
||
+ | -- more={}, |
||
+ | } |
||
+ | |||
+ | return strings |
||
+ | end |
||
+ | |||
+ | return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings) |
||
+ | end, |
||
+ | }, |
||
+ | { |
||
+ | header = i18n.tooltips.accuracy, |
||
+ | func = function(tpl_args, frame) |
||
+ | local f_stats = function(tpl_args, frame, result) |
||
+ | local stats = { |
||
+ | added={ |
||
+ | result['monster_base_stats.accuracy'], |
||
+ | }, |
||
+ | } |
||
+ | return stats |
||
+ | end |
||
+ | |||
+ | local f_strings = function(tpl_args, frame, result) |
||
+ | local strings = { |
||
+ | increased = {'map_monsters_accuracy_rating_+%'}, |
||
+ | -- more={}, |
||
+ | } |
||
+ | |||
+ | return strings |
||
+ | end |
||
+ | |||
+ | return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings) |
||
end, |
end, |
||
}, |
}, |
||
Строка 1158: | Строка 1475: | ||
return tostring(tbl) |
return tostring(tbl) |
||
+ | end, |
||
+ | }, |
||
+ | { |
||
+ | header = i18n.tooltips.experience, |
||
+ | func = function(tpl_args, frame) |
||
+ | local f_stats = function(tpl_args, frame, result) |
||
+ | local stats = { |
||
+ | added={ |
||
+ | result['monster_base_stats.experience'], |
||
+ | }, |
||
+ | m = tpl_args.experience_multiplier, |
||
+ | } |
||
+ | return stats |
||
+ | end |
||
+ | |||
+ | local f_strings = function(tpl_args, frame, result) |
||
+ | local strings = { |
||
+ | increased={'monster_slain_experience_+%'}, |
||
+ | } |
||
+ | |||
+ | return strings |
||
+ | end |
||
+ | |||
+ | return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings) |
||
+ | end, |
||
+ | }, |
||
+ | { |
||
+ | header = i18n.tooltips.summon_life, |
||
+ | func = function(tpl_args, frame) |
||
+ | -- Uniques cannot be summoned: |
||
+ | if tpl_args.rarity_id == 'unique' then |
||
+ | return nil |
||
+ | end |
||
+ | |||
+ | local f_stats = function(tpl_args, frame, result) |
||
+ | local stats = { |
||
+ | added={ |
||
+ | result['monster_base_stats.summon_life'], |
||
+ | }, |
||
+ | m = tpl_args.experience_multiplier, |
||
+ | } |
||
+ | return stats |
||
+ | end |
||
+ | |||
+ | local f_strings = function(tpl_args, frame, result) |
||
+ | local strings = { |
||
+ | -- increased={}, |
||
+ | } |
||
+ | |||
+ | return strings |
||
+ | end |
||
+ | |||
+ | return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings) |
||
+ | end, |
||
+ | }, |
||
+ | { |
||
+ | header = i18n.tooltips.metadata_id, |
||
+ | func = display.value{arg='metadata_id'}, |
||
+ | }, |
||
+ | |||
+ | } |
||
+ | |||
+ | local tbl_view_detailed = { |
||
+ | { |
||
+ | func = function(tpl_args, frame) |
||
+ | return i18n.tooltips.monster_data |
||
+ | end, |
||
+ | }, |
||
+ | { |
||
+ | header = i18n.tooltips.metadata_id, |
||
+ | func = display.value{arg='metadata_id'}, |
||
+ | }, |
||
+ | { |
||
+ | header = i18n.tooltips.monster_type_id, |
||
+ | func = display.value{arg='monster_type_id'}, |
||
+ | }, |
||
+ | { |
||
+ | header = i18n.tooltips.tags, |
||
+ | func = function (tpl_args, frame) |
||
+ | if tpl_args.tags == nil or #tpl_args.tags == 0 then |
||
+ | return |
||
+ | end |
||
+ | |||
+ | return table.concat(tpl_args.tags, '<br>') |
||
end, |
end, |
||
}, |
}, |
||
Строка 1174: | Строка 1575: | ||
{ |
{ |
||
header = i18n.tooltips.attack_speed, |
header = i18n.tooltips.attack_speed, |
||
− | func = display.value{arg='attack_speed', fmt='%.3fs',}, |
+ | func = display.value{arg='attack_speed', fmt='%.3fs<sup>-1</sup>',}, |
}, |
}, |
||
{ |
{ |
||
Строка 1195: | Строка 1596: | ||
header = i18n.tooltips.model_size_multiplier, |
header = i18n.tooltips.model_size_multiplier, |
||
func = display.value{arg='model_size_multiplier'}, |
func = display.value{arg='model_size_multiplier'}, |
||
− | }, |
||
− | { |
||
− | header = i18n.tooltips.tags, |
||
− | func = function (tpl_args, frame) |
||
− | if tpl_args.tags == nil or #tpl_args.tags == 0 then |
||
− | return |
||
− | end |
||
− | |||
− | return table.concat(tpl_args.tags, '<br>') |
||
− | end, |
||
− | }, |
||
− | { |
||
− | header = i18n.tooltips.metadata_id, |
||
− | func = display.value{arg='metadata_id'}, |
||
− | }, |
||
− | { |
||
− | header = i18n.tooltips.monster_type_id, |
||
− | func = display.value{arg='monster_type_id'}, |
||
}, |
}, |
||
} |
} |
||
− | |||
local list_view = { |
local list_view = { |
||
} |
} |
||
Строка 1294: | Строка 1676: | ||
} |
} |
||
− | -- Create the |
+ | -- Create the infoboxes: |
− | local tbl = mw.html.create('table') |
||
− | tbl |
||
− | :attr('class', 'wikitable') |
||
− | :attr('style', 'float:right; margin-left: 10px;') |
||
− | |||
− | for _, data in ipairs(tbl_view) do |
||
− | local v = data.func(tpl_args, frame) |
||
− | |||
− | if v ~= nil and v ~= '' then |
||
− | local tr = tbl:tag('tr') |
||
− | if data.header then |
||
− | tr:tag('th'):wikitext(data.header):done() |
||
− | tr:tag('td'):wikitext(v):done() |
||
− | else |
||
− | tr:tag('th'):attr('colspan', 2):wikitext(v):done() |
||
− | end |
||
− | end |
||
− | end |
||
− | |||
local out = { |
local out = { |
||
− | + | h.info_box(tpl_args, frame, tbl_view), |
|
+ | h.info_box(tpl_args, frame, tbl_view_detailed), |
||
+ | h.stat_box(tpl_args, frame), |
||
h.intro_text(tpl_args, frame), |
h.intro_text(tpl_args, frame), |
||
} |
} |
||
Строка 1322: | Строка 1687: | ||
end |
end |
||
+ | -- Categories: |
||
local cats = { |
local cats = { |
||
i18n.cats.data, |
i18n.cats.data, |
||
} |
} |
||
+ | local cats_type |
||
+ | if tpl_args.is_boss then |
||
+ | cats_type = i18n.cats.boss |
||
+ | else |
||
+ | cats_type = tpl_args.rarity |
||
+ | end |
||
+ | cats[#cats+1] = string.format('%s (%s)', string.lower(i18n.cats.data), cats_type) |
||
+ | |||
return table.concat(out) .. m_util.misc.add_category(cats) |
return table.concat(out) .. m_util.misc.add_category(cats) |
||
end |
end |
Версия от 08:35, 19 мая 2021
Этот модуль зависит от следующих других модулей: |
Модуль и его настройки связанны с монстрами.
Подстраницы
- Модуль:Monster/doc
- Модуль:Monster/monster base stats
- Модуль:Monster/monster base stats/doc
- Модуль:Monster/monster life scaling
- Модуль:Monster/monster life scaling/doc
- Модуль:Monster/monster map multipliers
- Модуль:Monster/monster map multipliers/doc
- Модуль:Monster/monster resistances
- Модуль:Monster/monster resistances/doc
- Модуль:Monster/monster types
- Модуль:Monster/monster types/doc
Вышеприведенная документация извлекается из Модуль:Monster/doc.
Редакторы могут экспериментировать в sandbox этого модуля и в testcases страницах.
Подстраницы этого модуля.
Редакторы могут экспериментировать в sandbox этого модуля и в testcases страницах.
Подстраницы этого модуля.
-- ----------------------------------------------------------------------------
-- Imports
-- ----------------------------------------------------------------------------
local m_cargo = require('Module:Cargo')
local getArgs = require('Module:Arguments').getArgs
local m_util = require('Module:Util')
local m_game = require('Module:Game')
local f_skill_link = require('Module:Skill link').skill_link
local p = {}
-- ----------------------------------------------------------------------------
-- Strings
-- ----------------------------------------------------------------------------
local i18n = {
cats = {
data = 'Данные монстров',
boss = 'Босс',
},
tooltips = {
name = 'Название',
rarity = 'Редкость',
experience_multiplier = 'Базовый множитель опыта',
health_multiplier = 'Базовый множитель здоровья',
damage_multiplier = 'Базовый множитель урона',
attack_speed = 'Базовая скорость атаки',
critical_strike_chance = 'Базовый шанс критического удара',
minimum_attack_distance = 'Минимальное расстояние атаки',
maximum_attack_distance = 'Максимальное расстояние атаки',
difficulty = 'Акт',
resistances = 'Сопротивления',
part1 = '[[Акт 1|1]]-[[Акт 5|5]]',
part2 = '[[Акт 5|5]]-[[Акт 10|10]]',
maps = '[[Акт 10|10]]-',
fire = m_game.constants.damage_types.fire.short_upper,
cold = m_game.constants.damage_types.cold.short_upper,
lightning = m_game.constants.damage_types.lightning.short_upper,
chaos = m_game.constants.damage_types.chaos.short_upper,
stat_text = 'Эффекты от свойств',
size = 'Размер',
model_size_multiplier = 'Множитель размера модели',
tags = 'Внутренние метки',
metadata_id = 'Metadata id',
monster_type_id = 'Monster type id',
area = 'Область',
monster_level = 'Уровень',
skills = 'Умения',
life = 'Здоровье',
damage = 'Урон',
aps = 'Attacks per second',
critical_strike_chance_total = 'Critical strike chance',
armour = 'Armour rating',
evasion = 'Evasion rating',
accuracy = 'Accuracy rating',
experience = 'Experience',
summon_life = 'Summon life',
monster_data = 'Monster data',
},
intro = {
text_with_name = "'''%s''' — это внутренний идентификатор [[монстр]]а [[%s|%s]]. ",
text_without_name = "'''%s''' — это внутренний идентификатор безымянного [[монстр]]а. ",
},
errors = {
invalid_rarity_id = 'The rarity id "%s" is invalid. Acceptable values are "normal", "magic", "rare" and "unique".',
},
}
-- ----------------------------------------------------------------------------
-- Helpers
-- ----------------------------------------------------------------------------
local h = {}
function h.add_mod_id(tpl_args, frame, value)
--[[
Add mod ids in an ordered way.
]]
if type(value) ~= 'table' then
value = {value}
end
for _, id in ipairs(value or {}) do
if tpl_args._mods[id] == nil then
tpl_args._mods[id] = true
tpl_args._mods[#tpl_args._mods+1] = id
end
end
return value
end
function h.stat_calc(stats)
--[[
Calculates a modified stat.
Parameters
----------
stats : List
Associated List with added, increased, more and less as keys.
Examples
--------
stats = {
added={6,4},
increased={100, 50}, -- [%]
more={20, 30}, -- [%]
}
= h.stat_calc(stats)
]]
local funcs = {
added=function(stats)
--[[
Sum the added terms.
]]
local out = 0
for _, v in ipairs(stats['added']) do
out = v + out
end
return out
end,
increased=function(stats)
--[[
Sum the increased terms.
Values should be in percent.
]]
local out = 1
for _, v in ipairs(stats['increased']) do
out = v/100 + out
end
return out
end,
more=function(stats)
--[[
Calculate the product of the more terms.
Values should be in percent.
]]
local out = 1
for _, v in ipairs(stats['more']) do
out = (1 + v/100) * out
end
return out
end,
less=function(stats)
--[[
Calculate the product of the less terms.
Values should be in percent.
Should only be used for stats that defines directions in the
stat id. Prefer the more function instead.
]]
local out = 1
for _, v in ipairs(stats['less']) do
out = (1 - v/100) * out
end
return out
end,
}
local out = 1
for k, v in pairs(stats) do
if type(funcs[k]) == 'function' then
out = funcs[k](stats) * out
else
out = v * out
end
end
return out
end
h.stat_calc_verbose = function(stats)
--[[
Show how the stats list is calculated.
]]
local verbose_funcs = {
added=function(stats)
local st = {}
for _, v in ipairs(stats['added']) do
st[#st+1] = v/1
end
return string.format('(%s)', table.concat(st, ' + '))
end,
increased=function(stats)
local st = {}
for _, v in ipairs(stats['increased']) do
st[#st+1] = v/100
end
return string.format('(1 + %s)', table.concat(st, ' + '))
end,
more=function(stats)
local st = {}
for _, v in ipairs(stats['more']) do
st[#st+1] = string.format('(1 + %s)', v/100)
end
return string.format('(%s)', table.concat(st, ' * '))
end,
less=function(stats)
local st = {}
for _, v in ipairs(stats['less']) do
st[#st+1] = string.format('(1 - %s)', v/100)
end
return string.format('(%s)', table.concat(st, ' * '))
end,
}
local out = {}
for k, v in pairs(stats) do
if type(verbose_funcs[k]) == 'function' then
out[#out+1] = verbose_funcs[k](stats)
else
out[#out+1] = string.format('%s', v)
end
end
return table.concat(out, ' * ')
end
function h.stat_match(stats, strings, result)
--[[
Match strings to ids in result and append them to stats.
Parameters
----------
stats : Array, required.
Array to append values to.
strings : array, required.
Array of ids to to match result to.
result : array, required.
Row result from a cargo_query. Must contain 'mod_stats.id' and
'mod_stats.max'.
Examples
--------
stats = {
added={2},
increased={0},
more={0},
}
strings = {
added={'base_maximum_life'},
increased={'maximum_life_+%'},
more={'maximum_life_+%_final'},
}
result = {
['mod_stats.id'] = 'maximum_life_+%',
['mod_stats.max'] = 100,
}
h.stat_match(stats, strings, result)
mw.logObject(stats)
]]
for k, stat_ids in pairs(strings) do
for _, stat_id in ipairs(stat_ids) do
-- Match the stat. TODO: Is there a smarter way? Can't just find
-- the pattern within the string though since increased and more
-- are so similar.
if stat_id == result['mod_stats.id'] then
if stats[k] == nil then
stats[k] = {}
end
stats[k][#stats[k]+1] = result['mod_stats.max'] -- TODO: add range.
end
end
end
end
function h.stat_format(args)
--[[
Format the stat value.
Parameters
----------
args : Array, required.
Array of arguments to modify the stat format. Supported keys are:
args.fmt
args.level
args.value
args.value_verbose
]]
local fmt = '%0.2f'
if args.value > 10000 then
fmt = '%0.3E'
elseif args.value > 100 then
fmt = '%0.0f'
end
return m_util.html.abbr(
string.format(args.fmt or fmt, args.value),
string.format('Lvl. %0.0f: %s', args.level, args.value_verbose)
)
end
function h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings)
--[[
Calculate the total stat value per monster level.
Parameters
----------
f_stats : Function, required.
Function returning an array of stat values per monster level.
f_strings : function, required.
Function returning an array of stat ids to match to cargo results per
monster level.
]]
-- Stop if no monster level was found:
if #tpl_args.monster_level == 0 then
return nil
end
-- Calculate the total stat value for each monster level:
local out = {}
for i, result in ipairs(tpl_args._mod_data['monster_level']) do
-- Initial stats for monsters:
local stats = f_stats(tpl_args, frame, result)
-- Append matching stats from the modifiers:
for _, modid in ipairs(tpl_args._mods) do
local mod = tpl_args._mod_data[modid]
for _, v in ipairs(mod) do
h.stat_match(
stats,
f_strings(tpl_args, frame, result),
v
)
end
end
-- Calculate the total stat value and format the output:
out[i] = h.stat_format{
level=result['monster_base_stats.level'],
value=h.stat_calc(stats),
value_verbose=h.stat_calc_verbose(stats),
}
end
return table.concat(out, ', ')
end
function h.intro_text(tpl_args, frame)
--[[
Display an introductory text about the monster data.
]]
local out = {}
if mw.ustring.find(tpl_args['metadata_id'], '_') then
out[#out+1] = frame:expandTemplate{
title='Incorrect title',
args = {title=tpl_args['id']}
}
end
if tpl_args['name'] then
out[#out+1] = string.format(
i18n.intro.text_with_name,
tpl_args['metadata_id'],
tpl_args['main_page'] or tostring(mw.title.getCurrentTitle()),
tpl_args['name']
)
else
out[#out+1] = string.format(
i18n.intro.text_without_name,
tpl_args['metadata_id']
)
end
return table.concat(out)
end
function h.info_box(tpl_args, frame, tbl_view)
-- Create the infobox:
local container = mw.html.create('div')
container
:attr('class', 'modbox floatright')
local tbl = container:tag('table')
tbl
:attr('class', 'wikitable')
-- :attr('style', 'float:right; margin-left: 10px;')
for _, data in ipairs(tbl_view) do
local v = data.func(tpl_args, frame)
if v ~= nil and v ~= '' then
local tr = tbl:tag('tr')
if data.header then
tr:tag('th'):wikitext(data.header):done()
tr:tag('td'):wikitext(v):done()
else
tr:tag('th'):attr('colspan', 2):wikitext(v):done()
end
end
end
return tostring(container)
end
function h.stat_box(tpl_args, frame)
--[[
Display the stat box.
]]
local container = mw.html.create('div')
container
:attr('class', 'modbox floatright')
-- stat table
local tbl = container:tag('table')
tbl
:attr('class', 'wikitable sortable')
-- :attr('style', 'style="width: 100%;"')
:tag('tr')
:tag('th')
:attr('colspan', 4)
:wikitext('Stats')
:done()
:done()
:tag('tr')
:tag('th')
:wikitext('#')
:done()
:tag('th')
:wikitext('Stat Id')
:done()
:tag('th')
:wikitext('Min')
:done()
:tag('th')
:wikitext('Max')
:done()
:done()
:done()
local i = 0
for _, modid in ipairs(tpl_args._mods) do
local mod = tpl_args._mod_data[modid]
for k, v in ipairs(mod) do
if v['mod_stats.id'] then
i = i + 1
local linked_stat = v['mod_stats.id']
if v['mod_stats._pageName'] then
linked_stat = string.format(
'[[%s|%s]]',
v['mod_stats._pageName'],
v['mod_stats.id']
)
end
tbl
:tag('tr')
:tag('td')
:wikitext(i)
:done()
:tag('td')
:wikitext(linked_stat)
:done()
:tag('td')
:wikitext(v['mod_stats.min'])
:done()
:tag('td')
:wikitext(v['mod_stats.max'])
:done()
:done()
:done()
end
end
end
return tostring(container)
end
-- ----------------------------------------------------------------------------
-- Tables
-- ----------------------------------------------------------------------------
local tables = {}
tables.monsters = {
table = 'monsters',
order = {
'metadata_id',
'tags',
'monster_type_id',
'mod_ids',
'part1_mod_ids',
'part2_mod_ids',
'endgame_mod_ids',
'skill_ids',
'name',
'size',
'minimum_attack_distance',
'maximum_attack_distance',
'model_size_multiplier',
'experience_multiplier',
'damage_multiplier',
'health_multiplier',
'critical_strike_chance',
'attack_speed',
'mods',
'is_boss',
'rarity_id',
'rarity'
},
fields = {
metadata_id = {
field = 'metadata_id',
type = 'String',
func = function (tpl_args, frame, value)
tpl_args.monster_usages = m_cargo.query(
{'areas', 'maps', 'items'},
{
'areas.name',
'areas.id',
'areas.area_level',
'areas.boss_monster_ids',
'areas.modifier_ids',
'areas.main_page',
'areas._pageName',
'maps.area_level',
'maps._pageName',
'items.drop_enabled'
},
{
join=[[
areas.id=maps.area_id,
maps._pageID=items._pageID
]],
where=m_cargo.replace_holds{
string=string.format(
[[
CASE WHEN maps.area_level IS NOT NULL THEN
areas.boss_monster_ids HOLDS "%s"
AND items.drop_enabled = True
ELSE
areas.boss_monster_ids HOLDS "%s"
END
]],
value,
value
),
mode='regex'
},
orderBy='maps.area_level, areas.area_level'
}
)
return value
end,
},
monster_type_id = {
field = 'monster_type_id',
type = 'String',
func = function (tpl_args, frame, value)
tpl_args.monster_type = m_cargo.query(
{'monster_types', 'monster_resistances'},
{
'monster_types.tags',
'monster_types.armour_multiplier',
'monster_types.evasion_multiplier',
'monster_types.energy_shield_multiplier',
'monster_types.damage_spread',
'monster_resistances.part1_fire',
'monster_resistances.part1_cold',
'monster_resistances.part1_lightning',
'monster_resistances.part1_chaos',
'monster_resistances.part2_fire',
'monster_resistances.part2_cold',
'monster_resistances.part2_lightning',
'monster_resistances.part2_chaos',
'monster_resistances.maps_fire',
'monster_resistances.maps_cold',
'monster_resistances.maps_lightning',
'monster_resistances.maps_chaos'
},
{
join='monster_types.monster_resistance_id = monster_resistances.id',
where=string.format('monster_types.id = "%s"', value),
}
)[1] or {}
if tpl_args.monster_type['monster_types.tags'] then
local tags = m_util.string.split(
tpl_args.monster_type['monster_types.tags'],
',%s+'
)
-- TODO: Maybe this can be fixed earlier?
if tpl_args.tags == nil then
tpl_args.tags = {}
end
for _, tag in ipairs(tags) do
tpl_args.tags[#tpl_args.tags+1] = tag
end
end
return value
end,
},
mod_ids = {
field = 'mod_ids',
type = 'List (,) of String',
func = h.add_mod_id,
},
part1_mod_ids = {
field = 'part1_mod_ids',
type = 'List (,) of String',
func = h.add_mod_id,
},
part2_mod_ids = {
field = 'part2_mod_ids',
type = 'List (,) of String',
func = h.add_mod_id,
},
endgame_mod_ids = {
field = 'endgame_mod_ids',
type = 'List (,) of String',
func = h.add_mod_id,
},
tags = {
field = 'tags',
type = 'List (,) of String',
},
skill_ids = {
field = 'skill_ids',
type = 'List (,) of String',
--TODO
},
-- add base type info or just parse it?
name = {
field = 'name',
type = 'String',
},
size = {
field = 'size',
type = 'Integer',
},
minimum_attack_distance = {
field = 'minimum_attack_distance',
type = 'Integer',
},
maximum_attack_distance = {
field = 'maximum_attack_distance',
type = 'Integer',
},
model_size_multiplier = {
field = 'model_size_multiplier',
type = 'Float',
},
experience_multiplier = {
field = 'experience_multiplier',
type = 'Float',
},
damage_multiplier = {
field = 'damage_multiplier',
type = 'Float',
},
health_multiplier = {
field = 'health_multiplier',
type = 'Float',
},
critical_strike_chance = {
field = 'critical_strike_chance',
type = 'Float',
},
attack_speed = {
field = 'attack_speed',
type = 'Float',
},
is_boss = {
field = 'is_boss',
type = 'Boolean',
func = function (tpl_args, frame)
-- If the monster is used in some area it's most likely
-- an unique boss:
if #tpl_args.monster_usages > 0 then
tpl_args.is_boss = true
else
tpl_args.is_boss = false
end
return tpl_args.is_boss
end,
},
rarity_id = {
field = 'rarity_id',
type = 'String',
func = function (tpl_args, frame)
--[[
Define the rarity of the monster. There's no obvious
parameter that can be datamined for this so this will
be mostly guess work.
]]
-- User defined rarity takes priority:
if tpl_args.rarity_id ~= nil then
if m_game.constants.rarities[tpl_args.rarity_id] == nil then
error(string.format(i18n.errors.invalid_rarity_id,
tostring(tpl_args.rarity_id)))
end
return tpl_args.rarity_id
end
-- If the monster is used in some area it's most likely
-- an unique boss:
if tpl_args.is_boss then
tpl_args.rarity_id = 'unique'
return tpl_args.rarity_id
end
-- If there are no mods it's probably a normal monster:
if #tpl_args._mods == 0 then
tpl_args.rarity_id = 'normal'
return tpl_args.rarity_id
end
-- Try to determine rarity from mods:
for _, modid in ipairs(tpl_args._mods) do
local mod = tpl_args._mod_data[modid]
-- Check if the mod contains the monster rarity stat:
for _, v in ipairs(mod) do
if v['mod_stats.id'] == 'monster_rarity' then
-- TODO: m_game rarity id does not match the stat:
local int_id = tonumber(v['mod_stats.max']) + 1
for k, row in pairs(m_game.constants.rarities) do
if int_id == row['id'] then
tpl_args.rarity_id = k
return tpl_args.rarity_id
end
end
end
end
end
-- If none of the mods contains the monster rarity
-- stat then it might be an unique:
if tpl_args.rarity_id == nil then
tpl_args.rarity_id = 'unique'
return tpl_args.rarity_id
end
end,
},
rarity = {
field = 'rarity',
type = 'String',
func = function (tpl_args, frame)
local results = m_cargo.map_results_to_id{
results=m_cargo.query(
{
'mods',
'mod_stats',
},
{
'mods.domain',
'mods.generation_type',
'mods.mod_group',
'mods.id',
'mod_stats.id',
'mod_stats.max',
'mod_stats.min',
'mod_stats._pageName',
},
{
join='mods._pageID=mod_stats._pageID',
where=string.format([[
mods.domain = 3
AND mods.generation_type = 3
AND mods.id REGEXP "Monster%s[0-9]*$"
]],
tpl_args.rarity_id
),
}
),
field='mods.id',
keep_id_field=false,
}
for modid, mod in pairs(results) do
h.add_mod_id(tpl_args, frame, modid)
tpl_args._mod_data[modid] = mod
end
return m_game.constants.rarities[tpl_args.rarity_id]['full']
end
},
--
-- Processing fields
--
mods = {
func = function (tpl_args, frame)
-- Format the mod ids for cargo queries:
local mlist = {}
for _, key in ipairs(tpl_args._mods) do
mlist[#mlist+1] = string.format('"%s"', key)
end
tpl_args._mod_data = {}
if #mlist > 0 then
tpl_args._mod_data = m_cargo.map_results_to_id{
results=m_cargo.query(
{
'mods',
'mod_stats',
},
{
'mods.id',
'mods.stat_text',
'mods.generation_type',
'mod_stats.id',
'mod_stats.min',
'mod_stats.max',
'mod_stats._pageName',
},
{
join=[[
mods._pageID=mod_stats._pageID
]],
where=string.format([[
mods.id IN (%s)
]], table.concat(mlist, ',')),
}
),
field='mods.id',
keep_id_field=false,
}
end
end,
},
}
}
tables.monster_types = {
table = 'monster_types',
order = {'id', 'tags', 'monster_resistance_id'},
fields = {
id = {
field = 'id',
type = 'String',
},
tags = {
field = 'tags',
type = 'List (,) of String',
},
monster_resistance_id = {
field = 'monster_resistance_id',
type = 'String',
},
armour_multiplier = {
field = 'armour_multiplier',
type = 'Float',
},
evasion_multiplier = {
field = 'evasion_multiplier',
type = 'Float',
},
energy_shield_multiplier = {
field = 'energy_shield_multiplier',
type = 'Float',
},
damage_spread = {
field = 'damage_spread',
type = 'Float',
},
}
}
tables.monster_resistances = {
table = 'monster_resistances',
order = {'id', 'part1_fire', 'part1_cold', 'part1_lightning',
'part1_chaos', 'part2_fire', 'part2_cold', 'part2_lightning',
'part2_chaos', 'maps_fire', 'maps_cold', 'maps_lightning',
'maps_chaos'},
fields = {
id = {
field = 'id',
type = 'String',
},
part1_fire = {
field = 'part1_fire',
type = 'Integer',
},
part1_cold = {
field = 'part1_cold',
type = 'Integer',
},
part1_lightning = {
field = 'part1_lightning',
type = 'Integer',
},
part1_chaos = {
field = 'part1_chaos',
type = 'Integer',
},
part2_fire = {
field = 'part2_fire',
type = 'Integer',
},
part2_cold = {
field = 'part2_cold',
type = 'Integer',
},
part2_lightning = {
field = 'part2_lightning',
type = 'Integer',
},
part2_chaos = {
field = 'part2_chaos',
type = 'Integer',
},
maps_fire = {
field = 'maps_fire',
type = 'Integer',
},
maps_cold = {
field = 'maps_cold',
type = 'Integer',
},
maps_lightning = {
field = 'maps_lightning',
type = 'Integer',
},
maps_chaos = {
field = 'maps_chaos',
type = 'Integer',
},
}
}
tables.monster_base_stats = {
table = 'monster_base_stats',
order = {'level', 'damage', 'evasion', 'accuracy', 'life', 'experience',
'summon_life'},
fields = {
level = {
field = 'level',
type = 'Integer',
},
damage = {
field = 'damage',
type = 'Float',
},
evasion = {
field = 'evasion',
type = 'Integer',
},
armour = {
field = 'armour',
type = 'Integer',
},
accuracy = {
field = 'accuracy',
type = 'Integer',
},
life = {
field = 'life',
type = 'Integer',
},
experience = {
field = 'experience',
type = 'Integer',
},
summon_life = {
field = 'summon_life',
type = 'Integer',
},
-- whole bunch of other values I have no clue about ...
}
}
tables.monster_map_multipliers = {
table = 'monster_map_multipliers',
order = {'level', 'life', 'damage', 'boss_life', 'boss_damage',
'boss_item_rarity', 'boss_item_quantity'},
fields = {
level = {
field = 'level',
type = 'Integer',
},
life = {
field = 'life',
type = 'Integer',
},
damage = {
field = 'damage',
type = 'Integer',
},
boss_life = {
field = 'boss_life',
type = 'Integer',
},
boss_damage = {
field = 'boss_damage',
type = 'Integer',
},
boss_item_rarity = {
field = 'boss_item_rarity',
type = 'Integer',
},
boss_item_quantity = {
field = 'boss_item_quantity',
type = 'Integer',
},
}
}
tables.monster_life_scaling = {
table = 'monster_life_scaling',
order = {'level', 'magic', 'rare'},
fields = {
level = {
field = 'level',
type = 'Integer',
},
magic = {
field = 'magic',
type = 'Integer',
},
rare = {
field = 'rare',
type = 'Integer',
},
}
}
-- ----------------------------------------------------------------------------
-- Monster box sections
-- ----------------------------------------------------------------------------
local display = {}
function display.value (args)
return function (tpl_args, frame)
local v
if args.sub then
v = tpl_args[args.sub][args.arg]
else
v = tpl_args[args.arg]
end
if v and args.fmt then
return string.format(args.fmt, v)
else
return v
end
end
end
function display.sub_value (args)
return function (tpl_args, frame)
return tpl_args[args.sub][args.arg]
end
end
local tbl_view = {
{
-- header = i18n.tooltips.name,
func = function (tpl_args, frame)
if tpl_args.name == nil then
return
end
local linked_name = string.format(
'[[Monster:%s|%s]]',
string.gsub(tpl_args.metadata_id, '_', '~'),
tpl_args.name
)
return m_util.html.poe_color(tpl_args.rarity_id, linked_name)
end,
},
{
-- header = i18n.tooltips.image,
func = function (tpl_args, frame)
local image_name = tpl_args.name or tpl_args.monster_type_id
image_name = string.gsub(image_name, '[%[%]]', '')
return string.format(
'[[File:%s monster screenshot.jpg|296x500px]]',
image_name
)
end,
},
-- {
-- header = i18n.tooltips.rarity,
-- func = display.value{arg='rarity'},
-- },
{
header = i18n.tooltips.area,
func = function(tpl_args, frame)
local out = {}
for i, v in ipairs(tpl_args.monster_usages) do
out[#out+1] = string.format(
'[[%s|%s]]',
v['areas.main_page'] or v['areas._pageName'],
v['areas.name'] or v['areas.id']
)
end
return table.concat(out, ', ')
end
},
{
header = i18n.tooltips.monster_level,
func = function(tpl_args, frame)
-- Get monster level from the area level unless it's been
-- user defined.
local monster_level = {}
if tpl_args.monster_level then
monster_level = m_util.string.split(tpl_args.monster_level, ',')
else
for _, v in ipairs(tpl_args.monster_usages) do
local lvl = v['maps.area_level'] or v['areas.area_level']
monster_level[#monster_level+1] = lvl
end
end
tpl_args.monster_level = monster_level
-- Add monster stats specific to monster level:
if #tpl_args.monster_level > 0 then
tpl_args._mod_data['monster_level'] = m_cargo.query(
{
'monster_base_stats',
'monster_life_scaling',
'monster_map_multipliers',
},
{
'monster_base_stats.level',
-- Life:
'monster_base_stats.life',
'monster_life_scaling.magic',
'monster_life_scaling.rare',
'monster_map_multipliers.life',
'monster_map_multipliers.boss_life',
-- Damage:
'monster_base_stats.damage',
'monster_map_multipliers.damage',
'monster_map_multipliers.boss_damage',
'monster_base_stats.armour',
'monster_base_stats.evasion',
'monster_base_stats.accuracy',
'monster_base_stats.experience',
'monster_base_stats.summon_life',
},
{
join=[[
monster_base_stats.level=monster_life_scaling.level,
monster_base_stats.level=monster_map_multipliers.level
]],
where=string.format(
'monster_base_stats.level IN (%s)',
table.concat(tpl_args.monster_level, ', ')
),
}
)
end
return table.concat(tpl_args.monster_level, ', ')
end
},
{
header = i18n.tooltips.stat_text,
func = function (tpl_args, frame)
local out = {}
for _, modid in ipairs(tpl_args._mods) do
local mod = tpl_args._mod_data[modid] or {}
local stat_text = {}
-- Add stat_text for each modifier, ignore duplicates:
for _, v in ipairs(mod) do
if v['mods.stat_text'] then
if stat_text[v['mods.stat_text']] == nil then
stat_text[v['mods.stat_text']] = true
out[#out+1] = v['mods.stat_text']
end
end
end
end
return table.concat(out, '<br>')
end,
},
{
header = i18n.tooltips.skills,
func = function (tpl_args, frame)
local out = {}
for _, id in ipairs(tpl_args.skill_ids or {}) do
out[#out+1] = f_skill_link{id=id}
if string.find(out[#out], 'class="module%-error"') then
out[#out] = id
end
end
return table.concat(out, '<br>')
end,
},
{
header = i18n.tooltips.life,
func = function(tpl_args, frame)
local f_stats = function(tpl_args, frame, result)
local stats = {
added={
result['monster_base_stats.life'],
},
increased={
result['monster_life_scaling.' .. tpl_args.rarity_id] or 0,
},
-- more={},
m_map = (tpl_args.health_multiplier or 1) + (result['monster_map_multipliers.life'] or 0)
}
if tpl_args.is_boss then
stats.m_map = stats.m_map + (result['monster_map_multipliers.boss_life'] or 0)/100
end
return stats
end
local f_strings = function(tpl_args, frame, result)
local strings = {
added={'base_maximum_life'},
increased={'maximum_life_+%', 'map_monsters_life_+%'},
more={
'maximum_life_+%_final',
'monster_life_+%_final_from_rarity',
},
}
if tpl_args.is_boss then
table.insert(strings.increased, 'map_boss_maximum_life_+%')
end
return strings
end
return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings)
end,
},
{
header = i18n.tooltips.damage,
func = function(tpl_args, frame)
local f_stats = function(tpl_args, frame, result)
local stats = {
added={
result['monster_base_stats.damage'],
},
-- increased={},
-- more={},
m_map = (tpl_args.damage_multiplier or 1) + (result['monster_map_multipliers.damage'] or 0)
}
if tpl_args.is_boss then
stats.m_map = stats.m_map + (result['monster_map_multipliers.boss_damage'] or 0)
end
return stats
end
local f_strings = function(tpl_args, frame, result)
local strings = {
-- added={},
increased={'map_monsters_damage_+%'},
more={'monster_rarity_damage_+%_final'},
less={
'monster_rarity_attack_cast_speed_+%_and_damage_-%_final',
'monster_base_type_attack_cast_speed_+%_and_damage_-%_final',
},
}
if tpl_args.is_boss then
table.insert(strings.increased, 'map_boss_damage_+%')
end
return strings
end
return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings)
end,
},
{
header = i18n.tooltips.aps,
func = function(tpl_args, frame)
local f_stats = function(tpl_args, frame, result)
local stats = {
added={
tpl_args.attack_speed or 1,
},
}
return stats
end
local f_strings = function(tpl_args, frame, result)
local strings = {
increased={'map_monsters_attack_speed_+%'}, -- map_monsters_cast_speed_+%
more={
'monster_rarity_attack_cast_speed_+%_and_damage_-%_final',
'monster_base_type_attack_cast_speed_+%_and_damage_-%_final',
},
}
if tpl_args.is_boss then
table.insert(strings.increased, 'map_boss_attack_and_cast_speed_+%')
end
return strings
end
return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings)
end,
},
{
header = i18n.tooltips.critical_strike_chance_total,
func = function(tpl_args, frame)
local f_stats = function(tpl_args, frame, result)
local stats = {
added={
tpl_args.critical_strike_chance,
},
}
return stats
end
local f_strings = function(tpl_args, frame, result)
local strings = {
increased={'map_monsters_critical_strike_chance_+%'},
}
return strings
end
return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings)
end,
},
{
header = i18n.tooltips.armour,
func = function(tpl_args, frame)
local f_stats = function(tpl_args, frame, result)
local stats = {
added={
result['monster_base_stats.armour'],
},
}
return stats
end
local f_strings = function(tpl_args, frame, result)
local strings = {
-- more={},
}
return strings
end
return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings)
end,
},
{
header = i18n.tooltips.evasion,
func = function(tpl_args, frame)
local f_stats = function(tpl_args, frame, result)
local stats = {
added={
result['monster_base_stats.evasion'],
},
}
return stats
end
local f_strings = function(tpl_args, frame, result)
local strings = {
-- more={},
}
return strings
end
return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings)
end,
},
{
header = i18n.tooltips.accuracy,
func = function(tpl_args, frame)
local f_stats = function(tpl_args, frame, result)
local stats = {
added={
result['monster_base_stats.accuracy'],
},
}
return stats
end
local f_strings = function(tpl_args, frame, result)
local strings = {
increased = {'map_monsters_accuracy_rating_+%'},
-- more={},
}
return strings
end
return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings)
end,
},
{
header = i18n.tooltips.resistances,
func = function (tpl_args, frame)
local tbl = mw.html.create('table')
tbl
:attr('class', 'wikitable')
:tag('tr')
:tag('th')
:wikitext(i18n.tooltips.difficulty)
:attr('rowspan', 2)
:done()
-- :tag('th')
-- :wikitext(i18n.tooltips.resistances)
-- :attr('colspan', 4)
-- :done()
:done()
:tag('tr')
:tag('th')
:wikitext(i18n.tooltips.fire)
:done()
:tag('th')
:wikitext(i18n.tooltips.cold)
:done()
:tag('th')
:wikitext(i18n.tooltips.lightning)
:done()
:tag('th')
:wikitext(i18n.tooltips.chaos)
:done()
:done()
local difficulties = {'part1', 'part2', 'maps'}
local elements = {'fire', 'cold', 'lightning', 'chaos'}
for _, k in ipairs(difficulties) do
local tr = tbl:tag('tr')
tr
:tag('th')
:wikitext(i18n.tooltips[k])
:done()
for _, element in ipairs(elements) do
local field = string.format(
'monster_resistances.%s_%s',
k,
element
)
tr
:tag('td')
:attr('class', 'tc -' .. element)
:wikitext(tpl_args.monster_type[field])
:done()
end
end
-- -- Compressed resistance table:
-- local tbl = mw.html.create('table')
-- local tr = tbl:tag('tr')
-- local res = {}
-- for _, element in ipairs(elements) do
-- if res[element] == nil then
-- res[element] = {}
-- end
-- for _, k in ipairs(difficulties) do
-- local r = string.format('monster_resistances.%s_%s', k, element)
-- res[element][#res[element]+1] = m_util.html.abbr(
-- tpl_args.monster_type[r],
-- k
-- )
-- end
-- tr
-- :tag('td')
-- :attr('class', 'tc -' .. element)
-- :wikitext(table.concat(res[element], '/'))
-- :done()
-- end
return tostring(tbl)
end,
},
{
header = i18n.tooltips.experience,
func = function(tpl_args, frame)
local f_stats = function(tpl_args, frame, result)
local stats = {
added={
result['monster_base_stats.experience'],
},
m = tpl_args.experience_multiplier,
}
return stats
end
local f_strings = function(tpl_args, frame, result)
local strings = {
increased={'monster_slain_experience_+%'},
}
return strings
end
return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings)
end,
},
{
header = i18n.tooltips.summon_life,
func = function(tpl_args, frame)
-- Uniques cannot be summoned:
if tpl_args.rarity_id == 'unique' then
return nil
end
local f_stats = function(tpl_args, frame, result)
local stats = {
added={
result['monster_base_stats.summon_life'],
},
m = tpl_args.experience_multiplier,
}
return stats
end
local f_strings = function(tpl_args, frame, result)
local strings = {
-- increased={},
}
return strings
end
return h.stat_calc_per_monster_level(tpl_args, frame, f_stats, f_strings)
end,
},
{
header = i18n.tooltips.metadata_id,
func = display.value{arg='metadata_id'},
},
}
local tbl_view_detailed = {
{
func = function(tpl_args, frame)
return i18n.tooltips.monster_data
end,
},
{
header = i18n.tooltips.metadata_id,
func = display.value{arg='metadata_id'},
},
{
header = i18n.tooltips.monster_type_id,
func = display.value{arg='monster_type_id'},
},
{
header = i18n.tooltips.tags,
func = function (tpl_args, frame)
if tpl_args.tags == nil or #tpl_args.tags == 0 then
return
end
return table.concat(tpl_args.tags, '<br>')
end,
},
{
header = i18n.tooltips.experience_multiplier,
func = display.value{arg='experience_multiplier'},
},
{
header = i18n.tooltips.health_multiplier,
func = display.value{arg='health_multiplier'},
},
{
header = i18n.tooltips.damage_multiplier,
func = display.value{arg='damage_multiplier'},
},
{
header = i18n.tooltips.attack_speed,
func = display.value{arg='attack_speed', fmt='%.3fs<sup>-1</sup>',},
},
{
header = i18n.tooltips.critical_strike_chance,
func = display.value{arg='critical_strike_chance', fmt='%.2f%%',},
},
{
header = i18n.tooltips.minimum_attack_distance,
func = display.value{arg='minimum_attack_distance'},
},
{
header = i18n.tooltips.maximum_attack_distance,
func = display.value{arg='maximum_attack_distance'},
},
{
header = i18n.tooltips.size,
func = display.value{arg='size'},
},
{
header = i18n.tooltips.model_size_multiplier,
func = display.value{arg='model_size_multiplier'},
},
}
local list_view = {
}
-- ----------------------------------------------------------------------------
-- Page views
-- ----------------------------------------------------------------------------
p.table_monsters = m_cargo.declare_factory{data=tables.monsters}
p.table_monster_types = m_cargo.declare_factory{data=tables.monster_types}
p.table_monster_resistances = m_cargo.declare_factory{data=tables.monster_resistances}
p.table_monster_base_stats = m_cargo.declare_factory{data=tables.monster_base_stats}
p.table_monster_map_multipliers = m_cargo.declare_factory{data=tables.monster_map_multipliers}
p.table_monster_life_scaling = m_cargo.declare_factory{data=tables.monster_life_scaling}
p.store_data = m_cargo.store_from_lua{tables=tables, module='Monster'}
function p.monster(frame)
--[[
Stores data and display infoboxes of monsters.
Example
-------
= p.monster{
metadata_id='Metadata/Monsters/Bandits/BanditBossHeavyStrike_',
monster_type_id='BanditBoss',
mod_ids='MonsterAttackBlock30Bypass20, MonsterExileLifeInMerciless_',
tags='red_blood',
skill_ids='Melee, MonsterHeavyStrike',
name='Calaf, Headstaver',
size=3,
minimum_attack_distance=4,
maximum_attack_distance=5,
model_size_multiplier=1.15,
experience_multiplier=1.0,
damage_multiplier=1.0,
health_multiplier=1.0,
critical_strike_chance=5.0,
attack_speed=1.35,
rarity_id = 'unique'
}
= p.monster{
metadata_id='Metadata/Monsters/Atziri/Atziri',
monster_type_id='Atziri',
mod_ids='MonsterAtziriMapBoss, MapMonsterReducedCurseEffect, AtziriReflectCurses, AtziriMinorDamageReflect, MonsterImplicitCannotBeStunned1, CannotBeSlowedBelowValueBosses, TauntImmunityDurationMapBoss',
tags='red_blood',
skill_ids='AtziriMirrorImage, AtziriSummonDemons, AtziriStormCall, AtziriStormCallEmpowered, AtziriFlameblast, AtziriFlameblastEmpowered, AtziriSpearThrow, AtziriSpearThrowEmpowered',
name='Atziri, Queen of the Vaal',
size=4,
minimum_attack_distance=4,
maximum_attack_distance=16,
model_size_multiplier=1.65,
experience_multiplier=2.0,
damage_multiplier=2.5,
health_multiplier=9.36,
critical_strike_chance=5.0,
attack_speed=1.5,
}
]]
-- Get args
tpl_args = getArgs(frame, {
parentFirst = true
})
frame = m_util.misc.get_frame(frame)
tpl_args._mods = {}
-- Parse and store the monster table:
m_cargo.parse_field_arguments{
tpl_args=tpl_args,
frame=frame,
table_map=tables.monsters,
}
-- Create the infoboxes:
local out = {
h.info_box(tpl_args, frame, tbl_view),
h.info_box(tpl_args, frame, tbl_view_detailed),
h.stat_box(tpl_args, frame),
h.intro_text(tpl_args, frame),
}
for _, data in ipairs(list_view) do
out[#out+1] = data.func(tpl_args, frame)
end
-- Categories:
local cats = {
i18n.cats.data,
}
local cats_type
if tpl_args.is_boss then
cats_type = i18n.cats.boss
else
cats_type = tpl_args.rarity
end
cats[#cats+1] = string.format('%s (%s)', string.lower(i18n.cats.data), cats_type)
return table.concat(out) .. m_util.misc.add_category(cats)
end
return p