Module:Battles
Jump to navigation
Jump to search
This module implements {{BattlePhase}}
and {{BattleEnemy}}
.
local p = {}
local getArgs = require('Module:Arguments').getArgs
-- Renders
function p.renderTemplate(frame)
local result = {}
-- prefix
local prefix = 'phase'
if frame.args.prefix ~= nil then
prefix = frame.args.prefix
end
local mainTemplate = frame.args.mainTemplate
local phaseTemplate = frame.args.phaseTemplate
local edit_title = frame.args.edit_title
local edit_link = frame.args.edit_link
local frameTemplate = frame
-- if we have a parent assume we're included from a template
if frame:getParent() ~= nil then
frameTemplate = frame:getParent()
end
--local edit_title = ''
--local edit_title_debug = ''
--local titleFrame = frameTemplate
--while (titleFrame ~= nil) do
-- edit_title = edit_title .. '--' .. titleFrame:getTitle()
-- titleFrame = titleFrame:getParent()
--end
--while (titleFrame ~= nil) and ((string.find(titleFrame:getTitle(), 'Template:') ~= nil) or (string.find(titleFrame:getTitle(), 'Module:') ~= nil)) do
-- edit_title_debug = edit_title_debug .. '--' .. titleFrame:getTitle()
-- titleFrame = titleFrame:getParent()
--end
--if titleFrame ~= nil then
-- edit_title = titleFrame:getTitle()
--end
local hasDescNotes = false
local phases = {}
for i=1,9 do
--local phaseKey = '%%%%'..prefix..i..'%%%%'
local phaseText = ''
local phase = frameTemplate.args[i]
if (phase ~= nil) and (string.len(phase) > 0) then
local temp = ''
if (frame.args.dumpPhases ~= nil) and (frame.args.dumpPhases == 'yes') then
table.insert(result, phase)
end
if pcall(function() temp = mw.text.jsonDecode(phase) end) then
phase = {}
phase.phase = '??'
if temp.phase ~= nil then
phase.phase = temp.phase
end
for j=1,9 do
for k=1,9 do
if (temp.enemies[j] ~= nil) and (temp.enemies[j][k] ~= nil) then
for ix,key in pairs({'name','row','lvl','element','hp','ct','od','ca_desc','resist','icon_s','notes'}) do
local enemy_count = 0
if temp.enemies[j][k][key] ~= nil then
local value = temp.enemies[j][k][key]
enemy_count = enemy_count + 1
if k == 1 then
phase['enemy'..j..'_'..key] = value
end
phase['enemy'..j..k..'_'..key] = value
if ((key == 'ca_desc') or (key == 'notes')) and (value ~= nil) and (string.len(value) > 0) then
hasDescNotes = true
end
end
phase['enemy'..j..'_count'] = enemy_count
end
end
end
end
phaseText = p.renderPhase(frame, phaseTemplate, phase, temp.enemies, frameTemplate.args)
else
mw.log('Failed to decode: ' .. phase)
phaseText = ''
end
end
phases[i] = phaseText
end
local args = {}
--mw.log(frameTemplate.args[1])
for key,value in pairs(frameTemplate.args) do
args[key] = value
end
args['has_descnotes'] = tostring(hasDescNotes)
args['edit_title'] = edit_title
args['edit_link'] = edit_link
--args['edit_title_debug'] = edit_title_debug
local text = frame:expandTemplate{ title = mainTemplate, args = args }
-- find parts we need to replace with phases
local parts = {}
for i=1,9 do
local phaseKey = '%%%%'..prefix..i..'%%%%'
local pos = string.find(text, phaseKey)
if pos ~= nil then
table.insert(parts, { phase = i, start = pos, len = string.len(phaseKey)-4 })
end
end
-- split up the text into an array and insert phases
local start_offset = 1
for i=1,9 do
local part_key = nil
local offset = string.len(text)
for key,part in pairs(parts) do
if (part.start >= start_offset) and (part.start < offset) then
part_key = key
offset = part.start
end
end
-- if we found a part replace it
if part_key ~= nil then
local part = parts[part_key]
--mw.log('part ' .. part.phase .. ': ' .. part.start .. ',' .. part.len)
-- add text between current position and start of part
--mw.log('start_offset: '..start_offset..' to offset:'..offset)
if offset > start_offset then
local sub = string.sub(text, start_offset, offset-1)
--mw.log('inserting: '..mw.text.jsonEncode(sub))
table.insert(result, sub)
end
start_offset = offset + part.len
-- add phase
if phases[part.phase] ~= nil then
local sub = phases[part.phase]
--mw.log('inserting: '..mw.text.jsonEncode(sub))
table.insert(result, phases[part.phase])
end
end
end
if start_offset < string.len(text) then
local sub = string.sub(text, start_offset)
--mw.log('appending: '..mw.text.jsonEncode(sub))
table.insert(result, sub)
end
return table.concat(result,'')
end
function p.renderPhase(frame, template, phase, enemies, context)
return frame:expandTemplate{ title = template, args = phase }
end
-- Enemy details as a JSON string for passing data between templates
function p.jsonEnemy(frame)
if frame:getParent() ~= nil then
frame = frame:getParent()
end
local result = {}
local params = {'name','row','lvl','element','hp','ct','od','ca_desc','resist','notes'}
for key,value in pairs(params) do
if frame.args[value] ~= nil then
result[value] = frame.args[value]
end
end
return mw.text.jsonEncode(result)
end
function p.jsonEnemy2(frame)
local args = getArgs(frame, {
trim = true,
removeBlanks = true,
})
return p._jsonEnemy2(args)
end
function p.jsonEnemy3(frame)
local args = getArgs(frame, {
trim = true,
removeBlanks = true,
})
return p._jsonEnemy3(args)
end
function p.format_int(number)
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
-- reverse the int-string and append a comma to all blocks of 3 digits
int = int:reverse():gsub("(%d%d%d)", "%1,")
-- reverse the int-string back remove an optional comma and put the
-- optional minus and fractional part back
return minus .. int:reverse():gsub("^,", "") .. fraction
end
function p._jsonEnemy2(args)
local result = {}
if args['id'] ~= nil then
local qf = 'id,name,lvl,element,hp,ct,od,rare,icon_s'
local qa = { limit = 1, where = 'id='..args['id'] }
for _, row in ipairs(mw.ext.cargo.query('enemies', qf, qa)) do
for key, value in pairs(row) do
result[key] = value
end
result['hp'] = p.format_int(result['hp'])
result['od'] = (result['od'] == '1') and 'yes' or 'no'
result['rare'] = (result['rare'] == '1') and 'yes' or 'no'
end
end
-- allow overriding values
local params = {'name','row','lvl','element','hp','ct','od','ca_desc','resist','notes'}
for key,value in pairs(params) do
if args[value] ~= nil then
result[value] = args[value]
end
end
return mw.text.jsonEncode(result)
end
function p._jsonEnemy3(args)
local result = {}
if args['id'] ~= nil then
local qf = 'id,name,lvl,element,hp,ct,od,rare,icon_s'
-- ,status_notes,status1,status2,status3,status4,status5,status6,status7,status8,status9
-- ,status1_label,status2_label,status3_label,status4_label,status5_label,status6_label,status7_label,status8_label,status9_label
local qa = { limit = 1, where = 'id='..args['id'] }
for _, row in ipairs(mw.ext.cargo.query('enemies', qf, qa)) do
for key, value in pairs(row) do
result[key] = value
end
if (tonumber(result['ct']) > 50) then
result['ct'] = '0'
end
result['hp'] = p.format_int(result['hp'])
result['od'] = (result['od'] == '1') and 'yes' or 'no'
result['rare'] = (result['rare'] == '1') and 'yes' or 'no'
end
end
-- allow overriding values
local params = {
'name','row','lvl','element','hp','ct','od','ca_desc','resist','notes'
}
for ix = 1, 9 do
table.insert(params, 'label'..ix)
table.insert(params, 'status'..ix)
table.insert(params, 'notes'..ix)
table.insert(params, 'ca_desc'..ix)
table.insert(params, 'resist'..ix)
end
for key,value in pairs(params) do
if args[value] ~= nil then
result[value] = args[value]
end
end
-- trim etc
result = getArgs(result, {
trim = true,
removeBlanks = false,
})
-- propagate status between modes
result = p._copyStatus(result)
result.phases = {}
result.has_phases = false
for ix = 1, 9 do
if ((result['label'..ix] or '') ~= '') or ((result['status'..ix] or '') ~= '') then
local phase = {}
phase.label = result['label'..ix] or ''
phase.status = result['status'..ix] or ''
phase.notes = result['notes'..ix] or ''
phase.ca_desc = result['ca_desc'..ix] or ''
phase.resist = result['resist'..ix] or ''
phase.icon_s = result['icon_s'..ix] or result.icon_s
phase.status.element = phase.status.element or result.element
phase.status.ct = phase.status.ct or tonumber(result.ct)
result.has_phases = true
table.insert(result.phases, phase)
end
end
local temp = {}
for _, v in ipairs({'id','name','row','lvl','element','hp','ct','od','ca_desc','resist','notes','phases'}) do
if result[v] ~= nil then
temp[v] = result[v]
end
end
result = temp
return mw.text.jsonEncode(result)
end
function p._copyStatus(params)
if (params.status1 or '') == '' then return params end
local known_debuffs = {
'DEF Down', 'ATK Down', 'Gravity', 'Delay', 'Lethal Hit', 'Poison', 'Burn', 'Putrefied', 'Blind', 'Charm', 'Zombified', 'Sleep', 'Paralyzed', 'Stone'
}
local known_keys = {
'atk', 'aoe', 'hp', 'element', 'ct',
'def', 'fire', 'water', 'earth', 'wind', 'light', 'dark'
}
for _, v in ipairs(known_debuffs) do table.insert(known_keys, v) end
local status = {}
local force_keys = {'def', 'DEF Down', 'ATK Down', 'Gravity', 'Delay', 'Lethal Hit', 'Poison', 'Burn', 'Putrefied', 'Blind', 'Charm', 'Zombified', 'Sleep', 'Paralyzed', 'Stone'}
for _, v in ipairs(force_keys) do status[v] = '?' end
status['hp'] = 100
status['aoe'] = 'NO'
for ix = 1, 9 do
if params['status'..ix] ~= nil then
status = p._splitStatus(params['status'..ix], status)
params['status'..ix] = status
end
end
return params
end
function p._splitStatus(status, copy)
local result = {}
for k, v in pairs(copy) do result[k] = v end
for _, v in ipairs(mw.text.split(status, ';', true)) do
local temp = mw.text.split(v, '=', true)
local key, value = unpack(temp)
if (value == nil) then value = '?' end
if (value ~= '?') then
if (key == 'hp') or (key == 'ct') or (key == 'atk') or (key == 'def') then value = tonumber(value) end
end
result[key] = value
end
return result
end
-- Phase details as a JSON string for passing data between templates
function p.jsonPhase(frame)
if frame:getParent() ~= nil then
frame = frame:getParent()
end
return mw.text.jsonEncode(p.parsePhase(frame))
end
function p.parsePhase(frame)
local result = {}
local phase = '???'
if frame.args.name ~= nil then
phase = frame.args.name
elseif frame.args.phase ~= nil then
phase = frame.args.phase
end
for ix,name in pairs {'label','label1','label2','label3'} do
if frame.args[name] ~= nil then
result[name] = frame.args[name]
end
end
if (result.label1 ~= nil) and (result.label == nil) then
result.label = result.label1
elseif (result.label ~= nil) and (result.label1 == nil) then
result.label1 = result.label
end
-- Find unindexed enemies
for i=1,9 do
if frame.args[i] ~= nil then
local enemy = mw.text.jsonDecode(frame.args[i])
for key,value in pairs(enemy) do
frame.args['enemy'..i..'_'..key] = value
end
end
end
-- Parse out all enemies and assign them a row
local rows = {}
local current_row = 0
for i=1,9 do
local key_name = 'enemy' .. i .. '_name'
local key_icon = 'enemy' .. i .. '_icon'
local key_icon_s = 'enemy' .. i .. '_icon_s'
local key_row = 'enemy' .. i .. '_row'
local key_lvl = 'enemy' .. i .. '_lvl'
local key_element = 'enemy' .. i .. '_element'
local key_hp = 'enemy' .. i .. '_hp'
local key_ct = 'enemy' .. i .. '_ct'
local key_od = 'enemy' .. i .. '_od'
local key_resist = 'enemy' .. i .. '_resist'
local key_ca_desc = 'enemy' .. i .. '_ca_desc'
local key_notes = 'enemy' .. i .. '_notes'
if frame.args[key_name] ~= nil then
local name = frame.args[key_name]
local row = 0
if frame.args[key_row] ~= nil then
row = tonumber(frame.args[key_row])
else
current_row = current_row + 1
row = current_row
end
local icon = ''
if frame.args[key_icon] ~= nil then
icon = frame.args[key_icon]
end
local icon_s = ''
if frame.args[key_icon_s] ~= nil then
icon_s = frame.args[key_icon_s]
end
local lvl = '??'
if frame.args[key_lvl] ~= nil then
lvl = frame.args[key_lvl]
end
local element = 'Unknown'
if frame.args[key_element] ~= nil then
element = string.lower(frame.args[key_element])
end
local hp = '???'
if frame.args[key_hp] ~= nil then
hp = frame.args[key_hp]
end
local ct = -1
if frame.args[key_ct] ~= nil then
ct = tonumber(frame.args[key_ct])
end
local od = false
if frame.args[key_od] ~= nil then
od = string.lower(frame.args[key_od])
od = ((od == 'yes') or (od == 'true') or (od == 'y'))
end
local resist = ''
if frame.args[key_resist] ~= nil then
resist = frame.args[key_resist]
end
local ca_desc = ''
if frame.args[key_ca_desc] ~= nil then
ca_desc = frame.args[key_ca_desc]
end
local notes = ''
if frame.args[key_notes] ~= nil then
notes = frame.args[key_notes]
end
table.insert(rows, { row=row, name=name, icon=icon, icon_s=icon_s, lvl=lvl, element=element, hp=hp, ct=ct, od=od, resist=resist, ca_desc=ca_desc, notes=notes })
end
end
local enemies = {}
for ix,value in pairs(rows) do
if enemies[value.row] == nil then
enemies[value.row] = {}
end
table.insert(enemies[value.row], value)
end
result.phase = phase
result.enemies = enemies
return result
end
function p.testParsePhase()
frame = mw.getCurrentFrame()
frame.args.phase = 'Wave 1'
frame.args.enemy1_name = 'mob1'
frame.args.enemy1_lvl = '123'
frame.args.enemy1_element = 'DaRk'
frame.args.enemy1_hp = '123,456,789'
frame.args.enemy1_ct = '3'
frame.args.enemy1_od = 'yes'
frame.args.enemy1_icon_s = 'test.png'
frame.args.enemy1_ca_desc = [[* Bonggg...]]
frame.args.enemy1_notes = [[Does not attack in bell form except for charge attacks.]]
frame.args.enemy2_name = 'mob2'
frame.args[3] = mw.text.jsonEncode({name='mob3',lvl=222,element='wind'})
return p.jsonPhase(frame)
end
function p.testRender()
frame = mw.getCurrentFrame()
frame.args.mainTemplate = 'BattleEventQuest/Table'
frame.args.phaseTemplate = 'BattlePhase/Raid'
frame.args['header'] = 'Nightmare'
frame.args.name = 'Level 70 Maddie-Blast Form'
frame.args.image = 'EventQuest Platinum Sky (Nightmare).jpg'
frame.args.cost = '0 AP'
frame.args.unlock = 'Randomly appears after hosting Very Hard or Extreme.'
frame.args.loot_clear = '{{Itm|Blue Sky Crystal,1}}'
frame.args.loot_honor = '25000'
frame.args.loot_token = '24'
frame.args.loot_loyalty = '100'
frame.args.loot_wood = '{{ItmLst|Gold Badge$nolink}}'
frame.args.loot_silver = '{{ItmLst|angel|Mechanic\'s Spanner$nolink}}'
frame.args.loot_gold = ''
--frame.args[1] = '{"enemies":[[{"od":true,"row":1,"ct":2,"ca_desc":"* Bonggg...\n: Plain damage to all allies and inflict Status AttackDown.pngAttack DOWN for 2 turns. (1st form only?)\n* Decemvir N\n: Damage to one ally and inflict Status Death.pngCounting Down for 5 turns.\n* Let Go OD\n: Damage to all allies and inflict Status Confused.pngIntoxicated for 1 turn.\n* Purify Mind TR\n: 108-hit damage to random allies. (2nd form only)","lvl":"30","hp":"???","element":"earth","name":"Joya","notes":"* Does not attack in bell form except for charge attacks.\n* If ally has Status Sleep.pngSleep\n: Casts Purify Mind.\n* 75% HP Trigger\n: Transforms\n* 25% HP Trigger\n: True Power\n: Base diamonds reduced to ◇.\n* Status Confused.pngIntoxicated\n: Ally randomly attacks an ally or enemy with plain damage. If Intoxicated is not removed after 1 turn, ally gains Status Sleep.pngSleep for 2 to 4 turns. If an Intoxicated ally is hit, debuff is removed.\n* Status Death.pngCounting Down\n: Take 100% of max HP in plain damage when count reaches 0."}]],"phase":"Boss"}'
frame.args[1] = '{"phase":"Wave 1","enemies":[[{"notes":"","ca_desc":"","hp":"20,000","element":"earth","resist":"","od":false,"name":"Raga","lvl":"10","ct":2,"icon_s":"","row":1,"icon":""}],[{"notes":"","ca_desc":"","hp":"33,000","element":"earth","resist":"","od":false,"name":"Dvesha","lvl":"10","ct":3,"icon_s":"","row":2,"icon":""}]]}'
return p.renderTemplate(frame)
end
function p.testRender2()
frame = mw.getCurrentFrame()
local testArgs = {}
frame.args.mainTemplate = 'BattleFreeQuest/Row'
frame.args.phaseTemplate = 'BattlePhase/Quest'
frame.args.stages = 3
frame.args.name = 'I Challenge You!'
frame.args.jpname = '若き騎士達からの挑戦'
frame.args.cost = '25 AP'
frame.args.element = 'dark'
frame.args.notes = '{{SR}} only'
frame.args.loot_clear = '{{Itm|Crystal,50}}'
frame.args.loot_side = '{{ItmLst|Spring Water Jug,2-2}}'
frame.args.loot_wood = '{{ItmLst|Normal Weapon,0-1|Falcon Feather,2-3}}'
frame.args.loot_silver = '{{ItmLst|Spring Water Jug,3-8|Vermilion Stone,2-3}}'
frame.args.loot_gold = ''
frame.args[1] = '{"enemies":[[{"od":false,"row":1,"ct":2,"ca_desc":"","lvl":"32","hp":"120,000","element":"light","name":"Light Warrior","notes":""},{"od":false,"row":1,"ct":1,"ca_desc":"","lvl":"32","hp":"75,000","element":"light","name":"Prism Fly","notes":""},{"od":false,"row":1,"ct":2,"ca_desc":"","lvl":"32","hp":"80,000","element":"light","name":"Sabrewolf","notes":""}],[{"od":false,"row":2,"ct":2,"ca_desc":"","lvl":"32","hp":"120,000","element":"light","name":"Goblin Knight","notes":""},{"od":false,"row":2,"ct":3,"ca_desc":"","lvl":"32","hp":"100,000","element":"light","name":"Mid Golem","notes":""}]],"label1":"1 of","phase":"Wave 1A","label":"1 of","label2":"1 of"}'
frame.args[2] = '{"enemies":[[{"od":false,"row":1,"ct":2,"ca_desc":"","lvl":"32","hp":"80,000","element":"light","name":"Sabrewolf","notes":""},{"od":false,"row":1,"ct":2,"ca_desc":"","lvl":"32","hp":"120,000","element":"light","name":"Goblin Knight","notes":""}],[{"od":false,"row":2,"ct":2,"ca_desc":"","lvl":"32","hp":"120,000","element":"light","name":"Light Warrior","notes":""}]],"label1":"1 of","phase":"Wave 2A","label":"1 of","label2":"1 of"}'
frame.args[3] = '{"enemies":[[{"od":true,"row":1,"ct":3,"ca_desc":"","lvl":"32","hp":"400,000","element":"light","name":"Tooty","notes":""}]],"phase":"Boss A"}'
frame.args[4] = ''
frame.args[5] = '{"enemies":[[{"od":false,"row":1,"ct":1,"ca_desc":"","lvl":"32","hp":"6,000","element":"light","name":"Wandering Goldslime","notes":""}]],"phase":"Wave 2B"}'
frame.args[6] = '{"enemies":[[{"od":false,"row":1,"ct":3,"ca_desc":"","lvl":"32","hp":"380,000","element":"light","name":"Mantis Overlord","notes":""}]],"phase":"Boss B"}'
testArgs.mainTemplate = 'BattleFreeQuest/Row'
testArgs.phaseTemplate = 'BattlePhase/Quest'
testArgs.stages = 3
testArgs.name = 'I Challenge You!'
testArgs.jpname = '若き騎士達からの挑戦'
testArgs.cost = '25 AP'
testArgs.element = 'dark'
testArgs.notes = '{{SR}} only'
testArgs.loot_clear = '{{Itm|Crystal,50}}'
testArgs.loot_side = '{{ItmLst|Spring Water Jug,2-2}}'
testArgs.loot_wood = '{{ItmLst|Normal Weapon,0-1|Falcon Feather,2-3}}'
testArgs.loot_silver = '{{ItmLst|Spring Water Jug,3-8|Vermilion Stone,2-3}}'
testArgs.loot_gold = ''
testArgs[1] = '{"enemies":[[{"od":false,"row":1,"ct":2,"ca_desc":"","lvl":"32","hp":"120,000","element":"light","name":"Light Warrior","notes":""},{"od":false,"row":1,"ct":1,"ca_desc":"","lvl":"32","hp":"75,000","element":"light","name":"Prism Fly","notes":""},{"od":false,"row":1,"ct":2,"ca_desc":"","lvl":"32","hp":"80,000","element":"light","name":"Sabrewolf","notes":""}],[{"od":false,"row":2,"ct":2,"ca_desc":"","lvl":"32","hp":"120,000","element":"light","name":"Goblin Knight","notes":""},{"od":false,"row":2,"ct":3,"ca_desc":"","lvl":"32","hp":"100,000","element":"light","name":"Mid Golem","notes":""}]],"label1":"1 of","phase":"Wave 1A","label":"1 of","label2":"1 of"}'
testArgs[2] = '{"enemies":[[{"od":false,"row":1,"ct":2,"ca_desc":"","lvl":"32","hp":"80,000","element":"light","name":"Sabrewolf","notes":""},{"od":false,"row":1,"ct":2,"ca_desc":"","lvl":"32","hp":"120,000","element":"light","name":"Goblin Knight","notes":""}],[{"od":false,"row":2,"ct":2,"ca_desc":"","lvl":"32","hp":"120,000","element":"light","name":"Light Warrior","notes":""}]],"label1":"1 of","phase":"Wave 2A","label":"1 of","label2":"1 of"}'
testArgs[3] = '{"enemies":[[{"od":true,"row":1,"ct":3,"ca_desc":"","lvl":"32","hp":"400,000","element":"light","name":"Tooty","notes":""}]],"phase":"Boss A"}'
testArgs[4] = ''
testArgs[5] = '{"enemies":[[{"od":false,"row":1,"ct":1,"ca_desc":"","lvl":"32","hp":"6,000","element":"light","name":"Wandering Goldslime","notes":""}]],"phase":"Wave 2B"}'
testArgs[6] = '{"enemies":[[{"od":false,"row":1,"ct":3,"ca_desc":"","lvl":"32","hp":"380,000","element":"light","name":"Mantis Overlord","notes":""}]],"phase":"Boss B"}'
return p.renderTemplate(frame, testArgs)
end
function p.testEnemy()
return p._jsonEnemy2({id = '1009020'})
end
function p.testEnemy3()
return p._jsonEnemy3({
id = '6064406',
label1 = '100%',
label2 = '50%',
label3 = '25%',
status1 = 'hp=100;def=15;atk=62000;aoe=yes;fire=90;water=0;earth=50;wind=50;light=50;dark=50;DEF Down=OK;ATK Down=OK;Gravity=OK;Delay=OK;Lethal Hit=RESIST;Poison=OK;Burn=RESIST;Putrefied=OK;Blind=RESIST;Charm=OK;Zombified=OK;Sleep=OK;Paralyzed=OK;Petrified=RESIST',
status2 = 'hp=50;atk=88000',
status3 = 'hp=25;def=14;atk=120000;Blind=OK;Petrified=OK',
status4 = 'hp=0;BROKEN1;BROKEN2 TEST',
notes1 = 'notes no 1',
notes2 = 'notes no 2',
notes3 = 'notes no 3',
ca_desc1 = 'ca_desc 1',
ca_desc2 = 'ca_desc 2',
ca_desc3 = 'ca_desc 3'
})
end
return p