Module:CombatAreas: Difference between revisions

From Melvor Idle
(_getAreaRequirements: Crude attempt to prevent the same requirement appearing twice)
(getAreaPassives now works for dungeons and strongholds)
 
(13 intermediate revisions by 4 users not shown)
Line 1: Line 1:
--NOTE: Some tables are in Module:CombatAreas/AreaTables to prevent loop from referencing Monsters
--NOTE: Some tables are in Module:CombatAreas/AreaTables to prevent loop from referencing Monsters
local p = {}
local p = {}
local AreaData = mw.loadData('Module:CombatAreas/data')


local Constants = require('Module:Constants')
local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local Common = require('Module:Common')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Shop = require('Module:Shop')


p.eventData = AreaData.event
local areaMap = {
["combat"] = 'combatAreas',
["dungeon"] = 'dungeons',
["slayer"] = 'slayerAreas',
["depth"] = 'abyssDepths',
["stronghold"] = 'strongholds'
}


function processArea(area, index, type)
function p.getArea(name)
  local result = Shared.clone(area)
--There are three types of areas but the lists are pretty short so looping all of them isn't a real issue
  result.id = index - 1
for k, areaType in pairs(areaMap) do
  if result.name == nil then
local area = GameData.getEntityByName(areaType, name)
    result.name = result.areaName
if area ~= nil then
  end
return area
  result.type = type
end
  return result
end
end
end


function p.getArea(name)
function p.getAreaByID(id, areaType)
  local result = nil
for aType, areaKey in pairs(areaMap) do
  --There are three types of areas but the lists are pretty short so looping all of them isn't a real issue
if areaType == nil or areaType == aType then
  for i, area in pairs(AreaData.combatAreas) do
local area = GameData.getEntityByID(areaKey, id)
    if area.name == name then
if area ~= nil then
      return processArea(area, i, 'combat')
return area
    end
end
  end
end
 
end
  for i, area in pairs(AreaData.slayerAreas) do
    if area.name == name then
      return processArea(area, i, 'slayer')
    end
  end
 
  for i, area in pairs(AreaData.dungeons) do
    if area.name == name then
      return processArea(area, i, 'dungeon')
    end
  end
 
  return nil
end
end


function p.getAreaByID(type, id)
function p.getAreaFilterType(name, type)
  if type == 'dungeon' then type = 'dungeons'
local areaType = areaMap[type]
  elseif type == 'combat' then type = 'combatAreas'
if areaType ~= nil then
  elseif type == 'slayer' then type = 'slayerAreas' end
return GameData.getEntityByName(areaType, name)
  return processArea(AreaData[type][id + 1], id + 1)
end
end
end


function p.getAreaFilterType(type, name)
function p.getAreas(checkFunc)
  local areaName = nil
local resultArray = nil
  if type == 'dungeon' then areas = AreaData.dungeons
  elseif type == 'combat' then areas = AreaData.combatAreas
  elseif type == 'slayer' then areas = AreaData.slayerAreas
  else return nil end


  for i, area in pairs(areas) do
for i, areaType in pairs(areaMap) do
    if type == 'dungeon' then areaName = area.name
local areas = GameData.getEntities(areaType, checkFunc)
    else areaName = area.areaName end
if resultArray == nil then
resultArray = areas
else
for k, area in ipairs(areas) do
table.insert(resultArray, area)
end
end
end
if resultArray == nil then
resultArray = {}
end


    if areaName == name then
return resultArray
      return processArea(area, i, type)
    end
  end
 
  return nil
end
end


function p.getAreas(checkFunc)
--Returns the expansion icon for the area if it has one
  local resultArray = {}
function p.getExpansionIcon(frame)
 
local areaName = frame.args ~= nil and frame.args[1] or frame
  for i, area in Shared.skpairs(AreaData.combatAreas) do
local area = p.getArea(areaName)
    local temp = processArea(area, i, 'combat')
if area == nil then
    if checkFunc(temp) then
return Shared.printError('No area named "' .. areaName .. '" exists in the data module')
      table.insert(resultArray, temp)
end
    end
  end
return Icons.getExpansionIcon(area.id)
  for i, area in Shared.skpairs(AreaData.slayerAreas) do
    local temp = processArea(area, i, 'slayer')
    if checkFunc(temp) then
      table.insert(resultArray, temp)
    end
  end
  for i, area in Shared.skpairs(AreaData.dungeons) do
    local temp = processArea(area, i, 'dungeon')
    if checkFunc(temp) then
      table.insert(resultArray, temp)
    end
  end
 
  return resultArray
end
end


function p._getAreaRequirements(area)
function p._getAreaRequirements(area)
local result = ''
local resultArray = {}
local resultArray = {}
local addReqsToArray = function(reqArray, requirements)
if area.entryRequirements ~= nil then
for i, reqDetails in Shared.skpairs(requirements) do
local reqText = Common.getRequirementString(area.entryRequirements)
if reqDetails.type == 'Level' then
if reqText ~= nil then
for j, lvlDetails in Shared.skpairs(reqDetails.levels) do
table.insert(resultArray, reqText)
local skill = Constants.getSkillName(lvlDetails.skill)
end
table.insert(reqArray, Icons._SkillReq(skill, lvlDetails.level))
end
end
 
elseif reqDetails.type == 'SlayerItem' then
if area.unlockRequirement ~= nil then
local item = Items.getItemByID(reqDetails.itemID)
-- Avoid repeating the same requirements twice, can happen for some dungeons e.g. Impending Darkness
table.insert(reqArray, Icons.Icon({item.name, type='item'})..' Equipped')
if area.entryRequirements == nil or mw.dumpObject(area.unlockRequirement) ~= mw.dumpObject(area.entryRequirements) then
elseif reqDetails.type == 'Dungeon' then
local reqText = Common.getRequirementString(area.unlockRequirement)
for j, dungDetails in Shared.skpairs(reqDetails.dungeons) do
if reqText ~= nil then
local dung = p.getAreaByID('dungeon', dungDetails.dungeonID)
table.insert(resultArray, reqText)
if dungDetails.count > 1 then
table.insert(reqArray, dungDetails.count..'x '..Icons.Icon({dung.name, type='dungeon'})..' Completions')
else
table.insert(reqArray, Icons.Icon({dung.name, type='dungeon'})..' Completed')
end
end
elseif reqDetails.type == 'ShopPurchase' then
local shopPurchase = Shop.processPurchase(reqDetails.category, reqDetails.id)
if shopPurchase ~= nil then
table.insert(reqArray, Shop._getPurchaseIcon({ shopPurchase }) .. ' Purchased')
end
else
table.insert(reqArray, 'ERROR: Unknown requirement type ' .. (reqDetails.type or 'nil') .. '[[Category:Pages with script errors]]')
end
end
end
end
end
end


if area.entryRequirements ~= nil then
return table.concat(resultArray, '<br/>')
addReqsToArray(resultArray, area.entryRequirements)
end
 
function p.getAreaRequirementsForBox(frame)
--Returns infobox formatting for requirements, or returns nothing if there are none.
local areaName = frame.args ~= nil and frame.args[1] or frame
local area = p.getArea(areaName)
if area == nil then
return Shared.printError('No area named "' .. areaName .. '" exists in the data module')
end
 
local reqs = p._getAreaRequirements(area)
if reqs ~= '' then
reqs = "|-\r\n|'''Requirements:'''\r\n"..reqs
end
end
return reqs
end


if area.unlockRequirement ~= nil then
function p._getAreaStat(area, statName)
-- Ensure this requirement isn't already part of the entry requirements
if statName == 'requirements' then
local addReq = true
return p._getAreaRequirements(area)
if area.entryRequirements ~= nil then
elseif statName == 'areaEffectDesc' then
local unlockReqStr = mw.dumpObject(area.unlockRequirement)
if area.areaEffect ~= nil then
for i, reqDetails in ipairs(area.entryRequirements) do
local descText, subIdx = string.gsub(area.areaEffectDescription, '${effectValue}', area.areaEffect.magnitude or 0)
-- Using mw.dumpObject() as a lazy way to compare tables
return descText
if area.unlockRequirement.type == reqDetails.type and mw.dumpObject(reqDetails) == unlockReqStr then
else
addReq = false
return 'None'
break
end
end
end
end
if addReq then
elseif statName == 'difficulty' then
addReqsToArray(resultArray, { area.unlockRequirement })
local result = Constants.getDifficultyString(area.difficulty[1])
if area.difficulty[2] ~= nil then
result = result..' - '..Constants.getDifficultyString(area.difficulty[2])
end
end
return result
end
end


result = table.concat(resultArray, '<br/>')
return area[statName]
return result
end
end


function p._getAreaStat(area, statName)
function p.getAreaStat(frame)
  if statName == 'requirements' then
local areaName = frame.args ~= nil and frame.args[1] or frame[1]
    return p._getAreaRequirements(area)
local statName = frame.args ~= nil and frame.args[2] or frame[2]
  elseif statName == 'areaEffectDesc' then
local area = p.getArea(areaName)
    if area.areaEffect ~= nil and area.areaEffect then
if area == nil then
      local descText, subIdx = string.gsub(area.areaEffectDescription, '${effectValue}', area.areaEffectValue or 0)
return Shared.printError('No area named "' .. areaName .. '" exists in the data module')
      return descText
end
    else
      return 'None'
    end
  elseif statName == 'difficulty' then
    local result = Constants.getDifficultyString(area.difficulty[1])
    if area.difficulty[2] ~= nil then
      result = result..' - '..Constants.getDifficultyString(area.difficulty[2])
    end
    return result
  end


  return area[statName]
return p._getAreaStat(area, statName)
end
end


function p.getAreaStat(frame)
function p._isMonsterInArea(monster, area)
  local areaName = frame.args ~= nil and frame.args[1] or frame[1]
return (
  local statName = frame.args ~= nil and frame.args[2] or frame[2]
Shared.contains(area.monsterIDs, monster.id)
  local area = p.getArea(areaName)
-- Check for Lair of the Spider Queen random spiders
  if area == nil then
or (
    return "ERROR: Could not find an area named "..areaName
Shared.contains(area.monsterIDs, 'melvorTotH:RandomSpiderLair')
  end
and Shared.contains(GameData.rawData.spiderLairMonsters, monster.id)
)
)
end


  return p._getAreaStat(area, statName)
function p._getMonsterAreas(monster)
-- Special handling for Lair of the Spider Queen, which has a random list of enemies
local randomSpiderCheck = Shared.contains(GameData.rawData.spiderLairMonsters, monster.id)
return p.getAreas(
function(area)
return p._isMonsterInArea(monster, area)
end)
end
end


function p.getMonsterAreas(monsterID)
function p.getDungeonRequirements(frame)
  local areaArray = {}
local areaName = frame.args ~= nil and frame.args[1] or frame
  --There are three types of areas but the lists are pretty short so looping all of them isn't a real issue
local area = p.getArea(areaName)
  for i, area in pairs(AreaData.combatAreas) do
if area == nil then
    if Shared.contains(area.monsters, monsterID) then
return Shared.printError('No area named "' .. areaName .. '" exists in the data module')
      table.insert(areaArray, processArea(area, i, 'combat'))
end
 
local result = p._getAreaStat(area, 'requirements')
if result ~= '' then
result = "\r\n|-\r\n|'''Requirements:'''<br/>"..result
end
return result
end
 
function p.getAreaPassives(frame)
local areaName = frame.args ~= nil and frame.args[1] or frame
local area = p.getArea(areaName)
if area == nil then
return Shared.printError('No area named "' .. areaName .. '" exists in the data module')
end
 
local bossPassives = nil
local nonBossPassives = nil
if area.eventID ~= nil and area.eventID ~= '' then -- currently just Impending Darkness Event
local event = GameData.getEntityByID('combatEvents', area.eventID)
bossPassives = event.bossPassives
-- nonBossPassives = event.enemyPassives -- melvorF:ControlledAffliction is missing from GameData
else
bossPassives = area.bossOnlyPassives -- Strongholds only
nonBossPassives = area.nonBossPassives -- Dungeons only
end
 
local result = ''
 
    if type(nonBossPassives) == 'table' and not Shared.tableIsEmpty(nonBossPassives) then
    result = result .. '\r\n===Non-Boss Monster Passives==='
for i, passiveID in ipairs(nonBossPassives) do
local passive = p.getPassiveByID(passiveID)
result = result .. '\r\n* ' .. passive.name .. '\r\n** ' .. Constants.getDescription(passive.customDescription, passive.modifiers)
end
     end
     end
  end


  for i, area in pairs(AreaData.slayerAreas) do
    if type(bossPassives) == 'table' and not Shared.tableIsEmpty(bossPassives) then
    if Shared.contains(area.monsters, monsterID) then
    result = result .. '\r\n===Boss Monster Passives==='
      table.insert(areaArray, processArea(area, i, 'slayer'))
for i, passiveID in ipairs(bossPassives) do
local passive = p.getPassiveByID(passiveID)
result = result .. '\r\n* ' .. passive.name .. '\r\n** ' .. Constants.getDescription(passive.customDescription, passive.modifiers)
end
     end
     end
  end


  --Hill Giants specifically ignore dungeons to prevent the issue with Into the Mist incorrectly being listed.
if type(area.tiers) == 'table' and not Shared.tableIsEmpty(area.tiers) then
  if monsterID ~= 1 then
result = result .. '\r\n===Stronghold Tier Passives==='
    for i, area in pairs(AreaData.dungeons) do
for tierName, tierData in pairs(area.tiers) do
      if Shared.contains(area.monsters, monsterID) then
result = result .. '\r\n* ' .. tierName .. ' Tier'
        table.insert(areaArray, processArea(area, i, 'dungeon'))
for i, passiveID in ipairs(tierData.passives) do
      end
local passive = p.getPassiveByID(passiveID)
    end
result = result .. '\r\n** ' .. passive.name .. '\r\n*** ' .. Constants.getDescription(passive.customDescription, passive.modifiers)
  end
end
  return areaArray
end
end
 
return result
end
end


function p.getDungeonRequirements(frame)
function p.getPassiveByID(ID)
  local areaName = frame.args ~= nil and frame.args[1] or frame
     return GameData.getEntityByID('combatPassives', ID)
  local area = p.getArea(areaName)
  if area == nil then
     return "ERROR: Could not find an area named "..areaName
  end
 
  local result = p._getAreaStat(area, 'requirements')
  if result ~= '' then
    result = "\r\n|-\r\n|'''Requirements:'''<br/>"..result
  end
  return result
end
end


return p
return p

Latest revision as of 00:35, 2 November 2024

Data is pulled from Module:GameData/data

NOTE: Some tables are in Module:CombatAreas/AreaTables to prevent loop from referencing Monsters


--NOTE: Some tables are in Module:CombatAreas/AreaTables to prevent loop from referencing Monsters
local p = {}

local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local Common = require('Module:Common')
local Icons = require('Module:Icons')

local areaMap = {
	["combat"] = 'combatAreas',
	["dungeon"] = 'dungeons',
	["slayer"] = 'slayerAreas',
	["depth"] = 'abyssDepths',
	["stronghold"] = 'strongholds'
}

function p.getArea(name)
	--There are three types of areas but the lists are pretty short so looping all of them isn't a real issue
	for k, areaType in pairs(areaMap) do
		local area = GameData.getEntityByName(areaType, name)
		if area ~= nil then
			return area
		end
	end
end

function p.getAreaByID(id, areaType)
	for aType, areaKey in pairs(areaMap) do
		if areaType == nil or areaType == aType then
			local area = GameData.getEntityByID(areaKey, id)
			if area ~= nil then
				return area
			end
		end
	end
end

function p.getAreaFilterType(name, type)
	local areaType = areaMap[type]
	if areaType ~= nil then
		return GameData.getEntityByName(areaType, name)
	end
end

function p.getAreas(checkFunc)
	local resultArray = nil

	for i, areaType in pairs(areaMap) do
		local areas = GameData.getEntities(areaType, checkFunc)
		if resultArray == nil then
			resultArray = areas
		else
			for k, area in ipairs(areas) do
				table.insert(resultArray, area)
			end
		end
	end
	if resultArray == nil then
		resultArray = {}
	end

	return resultArray
end

--Returns the expansion icon for the area if it has one
function p.getExpansionIcon(frame)
	local areaName = frame.args ~= nil and frame.args[1] or frame
	local area = p.getArea(areaName)
	if area == nil then
		return Shared.printError('No area named "' .. areaName .. '" exists in the data module')
	end
	
	return Icons.getExpansionIcon(area.id)
end

function p._getAreaRequirements(area)
	local resultArray = {}
	if area.entryRequirements ~= nil then
		local reqText = Common.getRequirementString(area.entryRequirements)
		if reqText ~= nil then
			table.insert(resultArray, reqText)
		end
	end

	if area.unlockRequirement ~= nil then
		-- Avoid repeating the same requirements twice, can happen for some dungeons e.g. Impending Darkness
		if area.entryRequirements == nil or mw.dumpObject(area.unlockRequirement) ~= mw.dumpObject(area.entryRequirements) then
			local reqText = Common.getRequirementString(area.unlockRequirement)
			if reqText ~= nil then
				table.insert(resultArray, reqText)
			end
		end
	end

	return table.concat(resultArray, '<br/>')
end

function p.getAreaRequirementsForBox(frame)
	--Returns infobox formatting for requirements, or returns nothing if there are none.
	local areaName = frame.args ~= nil and frame.args[1] or frame
	local area = p.getArea(areaName)
	if area == nil then
		return Shared.printError('No area named "' .. areaName .. '" exists in the data module')
	end

	local reqs = p._getAreaRequirements(area)
	if reqs ~= '' then
		reqs = "|-\r\n|'''Requirements:'''\r\n"..reqs
	end
	return reqs
end

function p._getAreaStat(area, statName)
	if statName == 'requirements' then
		return p._getAreaRequirements(area)
	elseif statName == 'areaEffectDesc' then
		if area.areaEffect ~= nil then
			local descText, subIdx = string.gsub(area.areaEffectDescription, '${effectValue}', area.areaEffect.magnitude or 0)
			return descText
		else
			return 'None'
		end
	elseif statName == 'difficulty' then
		local result = Constants.getDifficultyString(area.difficulty[1])
		if area.difficulty[2] ~= nil then
			result = result..' - '..Constants.getDifficultyString(area.difficulty[2])
		end
		return result
	end

	return area[statName]
end

function p.getAreaStat(frame)
	local areaName = frame.args ~= nil and frame.args[1] or frame[1]
	local statName = frame.args ~= nil and frame.args[2] or frame[2]
	local area = p.getArea(areaName)
	if area == nil then
		return Shared.printError('No area named "' .. areaName .. '" exists in the data module')
	end

	return p._getAreaStat(area, statName)
end

function p._isMonsterInArea(monster, area)
	return (
		Shared.contains(area.monsterIDs, monster.id)
		-- Check for Lair of the Spider Queen random spiders
		or (
			Shared.contains(area.monsterIDs, 'melvorTotH:RandomSpiderLair')
			and Shared.contains(GameData.rawData.spiderLairMonsters, monster.id)
		)
	)
end

function p._getMonsterAreas(monster)
	-- Special handling for Lair of the Spider Queen, which has a random list of enemies
	local randomSpiderCheck = Shared.contains(GameData.rawData.spiderLairMonsters, monster.id)
	return p.getAreas(
		function(area)
			return p._isMonsterInArea(monster, area)
		end)
end

function p.getDungeonRequirements(frame)
	local areaName = frame.args ~= nil and frame.args[1] or frame
	local area = p.getArea(areaName)
	if area == nil then
		return Shared.printError('No area named "' .. areaName .. '" exists in the data module')
	end

	local result = p._getAreaStat(area, 'requirements')
	if result ~= '' then
		result = "\r\n|-\r\n|'''Requirements:'''<br/>"..result
	end
	return result
end

function p.getAreaPassives(frame)
	local areaName = frame.args ~= nil and frame.args[1] or frame
	local area = p.getArea(areaName)
	if area == nil then
		return Shared.printError('No area named "' .. areaName .. '" exists in the data module')
	end

	local bossPassives = nil
	local nonBossPassives = nil
	if area.eventID ~= nil and area.eventID ~= '' then -- currently just Impending Darkness Event
		local event = GameData.getEntityByID('combatEvents', area.eventID)
		bossPassives = event.bossPassives
		-- nonBossPassives = event.enemyPassives -- melvorF:ControlledAffliction is missing from GameData
	else
		bossPassives = area.bossOnlyPassives -- Strongholds only
		nonBossPassives = area.nonBossPassives -- Dungeons only
	end

	local result = ''

    if type(nonBossPassives) == 'table' and not Shared.tableIsEmpty(nonBossPassives) then
    	result = result .. '\r\n===Non-Boss Monster Passives==='
		for i, passiveID in ipairs(nonBossPassives) do
			local passive = p.getPassiveByID(passiveID)
			result = result .. '\r\n* ' .. passive.name .. '\r\n** ' .. Constants.getDescription(passive.customDescription, passive.modifiers)
		end
    end

    if type(bossPassives) == 'table' and not Shared.tableIsEmpty(bossPassives) then
    	result = result .. '\r\n===Boss Monster Passives==='
		for i, passiveID in ipairs(bossPassives) do
			local passive = p.getPassiveByID(passiveID)
			result = result .. '\r\n* ' .. passive.name .. '\r\n** ' .. Constants.getDescription(passive.customDescription, passive.modifiers)
		end
    end

	if type(area.tiers) == 'table' and not Shared.tableIsEmpty(area.tiers) then
		result = result .. '\r\n===Stronghold Tier Passives==='
		for tierName, tierData in pairs(area.tiers) do
			result = result .. '\r\n* ' .. tierName .. ' Tier'
			for i, passiveID in ipairs(tierData.passives) do
				local passive = p.getPassiveByID(passiveID)
				result = result .. '\r\n** ' .. passive.name .. '\r\n*** ' .. Constants.getDescription(passive.customDescription, passive.modifiers)
			end
		end
	end

	return result
end

function p.getPassiveByID(ID)
    return GameData.getEntityByID('combatPassives', ID)
end

return p