[create]
The documentation for this module is missing. Click here to create it.
-- Author: t7ru [[User:Gabonnie]]
-- Version: 1.1
-- Description: Create an interactive conversation interface between people.
-- Forked from 0.2 with MIT License [[w:c:alter-ego:Module:Converse]]
local p = {}
local getArgs = require('Dev:Arguments').getArgs
local function getSortedSteps(args)
local steps = {}
local seen = {}
for k, _ in pairs(args) do
local numStr = string.match(k, '^step(%d+)[%-]')
if numStr then
local num = tonumber(numStr)
if not seen[num] then
table.insert(steps, num)
seen[num] = true
end
end
end
table.sort(steps)
return steps
end
function p._renderDialogue(args)
local type = args.type or 'npc'
local text = args.text or ''
if type == 'npc' then
local container = mw.html.create('div')
:addClass('conv-row conv-npc')
if args.image then
container:tag('div')
:addClass('conv-portrait')
:wikitext('[[File:' .. args.image .. '|64px]]')
end
local bubble = container:tag('div')
:addClass('conv-bubble')
if args.name then
bubble:tag('strong')
:addClass('conv-name')
:wikitext(args.name)
end
bubble:wikitext(text)
return tostring(container)
elseif type == 'player' then
local container = mw.html.create('div')
:addClass('conv-row conv-player')
container:tag('div')
:addClass('conv-bubble')
:css('max-width', args.width or '80%')
:wikitext(text)
return tostring(container)
elseif type == 'action' then
local container = mw.html.create('div')
:addClass('conv-action')
:wikitext("''" .. text .. "''")
return tostring(container)
elseif type == 'empty' then
return tostring(mw.html.create('div'):addClass('conv-spacer'))
end
return ''
end
function p._renderSequence(args, sortedSteps, branchFilter, runID, gcRef)
local html = ''
local openDivs = 0
local levelSteps = {}
for _, stepNum in ipairs(sortedSteps) do
if args['step' .. stepNum .. '-branch'] == branchFilter then
table.insert(levelSteps, stepNum)
end
end
local k = 1
while k <= #levelSteps do
local stepNum = levelSteps[k]
local stepType = args['step' .. stepNum .. '-type'] or 'npc'
if stepType == 'option' then
gcRef[1] = gcRef[1] + 1
local gc = gcRef[1]
local groupID = runID .. '-group-' .. gc
local options = {}
while k <= #levelSteps do
local currStep = levelSteps[k]
if (args['step' .. currStep .. '-type'] or 'npc') == 'option' then
local contVal = args['step' .. currStep .. '-continue']
local isStop = (contVal == 'no' or contVal == 'false')
table.insert(options, {
text = args['step' .. currStep .. '-text'],
result = args['step' .. currStep .. '-result-text'],
resultType = args['step' .. currStep .. '-result-type'],
resultName = args['step' .. currStep .. '-result-name'],
resultImg = args['step' .. currStep .. '-result-img'],
branchLabel = not isStop and contVal or nil,
noFlow = isStop,
})
k = k + 1
else
break
end
end
local hasBranches = false
for _, opt in ipairs(options) do
if opt.branchLabel then hasBranches = true; break end
end
local needsShared = false
if not hasBranches then
for _, opt in ipairs(options) do
if not opt.noFlow then needsShared = true; break end
end
end
local sharedNextID = needsShared and (runID .. '-group-content-' .. gc) or nil
html = html
.. '<div id="mw-customcollapsible-' .. groupID .. '" class="mw-collapsible">'
.. '<div class="conv-options-list">'
local resultsHtml = ''
for i, opt in ipairs(options) do
local resultID = groupID .. '-opt-' .. i
local branchDivID = opt.branchLabel and (runID .. '-branch-' .. opt.branchLabel) or nil
local toggles = 'mw-customtoggle-' .. groupID
.. ' mw-customtoggle-' .. resultID
if branchDivID then toggles = toggles .. ' mw-customtoggle-' .. branchDivID end
if sharedNextID and not opt.noFlow then
toggles = toggles .. ' mw-customtoggle-' .. sharedNextID
end
html = html
.. '<div class="conv-option ' .. toggles .. '">'
.. (opt.text or 'Option') .. '</div>'
resultsHtml = resultsHtml
.. '<div id="mw-customcollapsible-' .. resultID .. '" class="mw-collapsible mw-collapsed">'
.. '<div class="conv-response-wrapper">'
.. '<div class="conv-option-selected ' .. toggles .. '">'
.. (opt.text or 'Option') .. '</div>'
if (opt.result and opt.result ~= '') or opt.resultType == 'empty' then
resultsHtml = resultsHtml .. p._renderDialogue({
type = opt.resultType or 'player',
text = opt.result,
name = opt.resultName,
image = opt.resultImg,
})
end
resultsHtml = resultsHtml .. '</div></div>'
end
html = html .. '</div></div>' .. resultsHtml
if hasBranches then
local seenBranches = {}
for _, opt in ipairs(options) do
if opt.branchLabel and not seenBranches[opt.branchLabel] then
seenBranches[opt.branchLabel] = true
local branchDivID = runID .. '-branch-' .. opt.branchLabel
html = html
.. '<div id="mw-customcollapsible-' .. branchDivID .. '" class="mw-collapsible mw-collapsed">'
.. p._renderSequence(args, sortedSteps, opt.branchLabel, runID, gcRef)
.. '</div>'
end
end
else
if sharedNextID then
html = html
.. '<div id="mw-customcollapsible-' .. sharedNextID .. '" class="mw-collapsible mw-collapsed">'
openDivs = openDivs + 1
end
end
else
html = html .. p._renderDialogue({
type = stepType,
text = args['step' .. stepNum .. '-text'] or '',
image = args['step' .. stepNum .. '-img'],
name = args['step' .. stepNum .. '-name'],
})
local contVal = args['step' .. stepNum .. '-continue']
if contVal and contVal ~= 'no' and contVal ~= 'false' then
html = html .. p._renderSequence(args, sortedSteps, contVal, runID, gcRef)
end
k = k + 1
end
end
for _ = 1, openDivs do html = html .. '</div>' end
return html
end
function p.heeho(frame)
local args = getArgs(frame)
math.randomseed(os.clock() * 777777777)
local runID = 'conv-' .. math.random(100000, 999999)
local root = mw.html.create('div')
:addClass('conv-frame')
root:tag('div')
:addClass('conv-header')
:wikitext(args.header or 'Conversation Start')
local content = root:tag('div')
:addClass('conv-body')
if args.content then
content:wikitext(args.content)
else
local gcRef = {0}
content:wikitext(p._renderSequence(args, getSortedSteps(args), nil, runID, gcRef))
end
root:tag('div')
:addClass('conv-footer')
:wikitext(args.footer or 'Conversation End')
return tostring(root)
end
function p.npc(frame) return p._renderDialogue({type='npc', text=frame.args.text, name=frame.args.name, image=frame.args.image}) end
function p.player(frame) return p._renderDialogue({type='player', text=frame.args.text}) end
function p.action(frame) return p._renderDialogue({type='action', text=frame.args.text}) end
return p