rawset(_G, "MintStory", {
version = {1, 0, 0},
globalBox = {}
})

local function BreakUpText(v, s)
local str = {s}
local i = 1
while v.stringWidth(str[i], V_ALLOWLOWERCASE, "normal") > 306 do
if i > 5  break end

str[i+1] = str[i]
local a
while true do
str[i] = string.sub($, 1, -2)
if v.stringWidth(str[i], V_ALLOWLOWERCASE, "normal") <= 306 and (string.sub(str[i], -1, -1) == " " or string.find(str[i], "\n"))
if string.find(str[i], "\n")  str[i] = string.sub($, 1, string.find($, "\n")-1) else str[i] = string.sub(str[i], 1, -2) end
a = string.len(str[i])
break
end
end
str[i+1] = string.sub($, a+2, -1)
i = $+1

if string.find(str[i], "\n")
str[i+1] = string.sub(str[i], string.find(str[i], "\n")+1, -1)
str[i] = string.sub($, 1, string.find($, "\n")-1)

if str[i]:len() == 0
str[i] = str[i+1]
str[i+1] = ""
end
end
str[i+2] = str[i+1]
end

if string.find(str[i], "\n")
str[i+1] = string.sub(str[i], string.find(str[i], "\n")+1, -1)
str[i] = string.sub($, 1, string.find($, "\n")-1)

if str[i]:len() == 0
str[i] = str[i+1]
str[i+1] = ""
end
end

if str[i+2] == str[i+1]  str[i+2] = "" end
return str
end

freeslot("SPR2_TBXA", "SPR2_TBXM")

local TB = MintStory
function TB:DisplayBox(p, table, move)
if p
p.mintstory = {
tree = table,
current = 1,
move = move
}
else
self.globalBox = {
tree = table,
current = 1,
move = move,
global = true
}
for p in players.iterate do
p.mintstory = self.globalBox
end
end
end

function TB:CloseBox(p)
if p  p.mintstory = {}
else self.globalBox = {} end
end

function TB.AdvanceBox(p)
if p.mintstory.choice  p.mintstory.current = p.mintstory.tree[p.mintstory.current].choices[p.mintstory.choice].box
p.mintstory.choice = 0
else p.mintstory.current = p.mintstory.tree[$].next end

if p.mintstory.current != 0 and p.mintstory.tree[p.mintstory.current].script  p.mintstory.tree[p.mintstory.current].script(p) end
p.mintstory.settings = {
timer = 1,
atonce = 1,
prev = nil,
curt = 1,
copied = 0,
wait = 0,
mode = {},
escape = {}
}
p.mintstory.render = ""
p.mintstory.txfilter = nil
end

local move, move2 = 0, 0

addHook("KeyDown", function(keyevent)
local aa, ab = input.gameControlToKeyNum(GC_STRAFELEFT)
local ac, ad = input.gameControlToKeyNum(GC_STRAFERIGHT)
local ba, bb = input.gameControl2ToKeyNum(GC_STRAFELEFT)
local bc, bd = input.gameControl2ToKeyNum(GC_STRAFERIGHT)

if keyevent.num == aa or keyevent.num == ab
move = -1
elseif keyevent.num == ac or keyevent.num == ad
move = 1
end
if keyevent.num == ba or keyevent.num == bb
move2 = -1
elseif keyevent.num == bc or keyevent.num == bd
move2 = 1
end
end)

local punct = {
["."] = true,
[","] = true,
[";"] = true,
[":"] = true,
["?"] = true,
["!"] = true,
}

addHook("PlayerThink", function(p)
p.mintstory = $ or {}
if not p.mintstory.tree  if not p.mintstory.complete  p.mintstory = TB.globalBox end; return end
if p.mintstory.global and not TB.globalBox.tree  p.mintstory = {}; return end

if not p.mintstory.move  p.pflags = $|PF_FULLSTASIS; p.powers[pw_flashing] = 1 end

local box = p.mintstory
local tree = box.tree
local curbox = tree[p.mintstory.current]

if p == consoleplayer  p.select = move
elseif p == secondarydisplayplayer  p.select = move2 end

if curbox.choices
box.choice = $ or 1

if p.select
box.choice = $+p.select
if box.choice > #curbox.choices  box.choice = 1
elseif box.choice < 1  box.choice = #curbox.choices end
S_StartSound(nil, sfx_menu1, p)
end
end

box.render = $ or ""
box.settings = $ or {
timer = 1,
atonce = 1,
prev = nil,

curt = 1,
copied = 0,

wait = 0,
mode = {},
escape = {}
}
if curbox.text
box.txfilter = curbox.text
local i = 1
while i < #box.txfilter do
while string.sub(box.txfilter, i, i) == '|' do
if string.sub(box.txfilter, i+1, i+3) == "del" or string.sub(box.txfilter, i+1, i+3) == "pau"
box.txfilter = string.sub($, 1, i-1) .. string.sub($, i+6, -1)
elseif string.sub(box.txfilter, i+1, i+3) == "shk" or string.sub(box.txfilter, i+1, i+3) == "esc"
box.txfilter = string.sub($, 1, i-1) .. string.sub($, i+5, -1)
elseif string.sub(box.txfilter, i+1, i+3) == "wav" or string.sub(box.txfilter, i+1, i+3) == "rst"
box.txfilter = string.sub($, 1, i-1) .. string.sub($, i+4, -1)
else
break
end
end
i = $+1
end
end

if box.txfilter and box.render != box.txfilter
box.settings.curt = $-1
if box.settings.curt == 0
box.settings.curt = box.settings.timer

local iter = 0
while iter < box.settings.atonce do
box.settings.copied = $+1
local cpy = string.sub(curbox.text, box.settings.copied, box.settings.copied)
while cpy == "|" do
box.settings.copied = $+3
cpy = string.sub(curbox.text, box.settings.copied-2, box.settings.copied)
local vd = false

if cpy == "del"
box.settings.copied = $+2
local x, y = tonumber(string.sub(curbox.text, box.settings.copied-1, box.settings.copied-1)) or 1,
tonumber(string.sub(curbox.text, box.settings.copied, box.settings.copied)) or 1
box.settings.atonce, box.settings.timer = x, y
vd = true
elseif cpy == "pau"
box.settings.copied = $+2
local x = tonumber(string.sub(curbox.text, box.settings.copied-1, box.settings.copied)) or 1
box.settings.curt = $+x
vd = true
iter = box.settings.atonce
elseif cpy == "shk"
box.settings.copied = $+1
local x = tonumber(string.sub(curbox.text, box.settings.copied, box.settings.copied))
box.settings.mode[#box.render+1] = x
vd = true
elseif cpy == "esc"
box.settings.copied = $+1
box.settings.escape[#box.render+1] = string.sub(curbox.text, box.settings.copied, box.settings.copied)
vd = true
elseif cpy == "wav"
box.settings.mode[#box.render+1] = 4
vd = true
elseif cpy == "rst"
box.settings.mode[#box.render+1] = 0
vd = true
end

if vd
box.settings.copied = $+1
cpy = string.sub(curbox.text, box.settings.copied, box.settings.copied)
else
box.settings.copied = $-3
cpy = string.sub(curbox.text, box.settings.copied, box.settings.copied)
break
end
end
box.render = $..cpy
iter = $+1

if box.settings.mode[#box.render] == nil  box.settings.mode[#box.render] = box.settings.mode[#box.render-1] or 0 end
if not box.settings.escape[#box.render]  box.settings.escape[#box.render] = box.settings.escape[#box.render-1] or "\x80" end
end
if punct[string.sub(box.render, -1, -1)]  box.settings.curt = $*4 end
end
else
box.settings.wait = $+1
if curbox.delay and box.settings.wait == curbox.delay  TB.AdvanceBox(p) end
end

if p.cmd.buttons & BT_JUMP and not p.mintstory.move
if not box.txfilter or box.render == box.txfilter
if not p.boxadvance
TB.AdvanceBox(p)
end
else
if not p.boxadvance  box.settings.prev = box.settings.atonce end
if box.settings.prev  box.settings.atonce = box.settings.prev*5; box.settings.curt = 1 end
end
p.boxadvance = true
else if p.boxadvance and box.settings.prev  box.settings.atonce = box.settings.prev; box.settings.prev = nil end; p.boxadvance = false end

if p.cmd.buttons & BT_SPIN and not p.mintstory.move
if box.txfilter and box.render != box.txfilter
if not p.boxskip  box.settings.prev = box.settings.atonce end
box.settings.atonce = string.len(box.txfilter); box.settings.curt = 1
end
p.boxskip = true
else if p.boxskip and box.settings.prev  box.settings.atonce = box.settings.prev; box.settings.prev = nil end; p.boxskip = false end

if p.mintstory.current == 0
if not p.mintstory.move  p.powers[pw_flashing] = TICRATE/2 end
p.mintstory = {complete = true}
return end

move, move2 = 0, 0
end)

local posTable = {
up = {x = 160, y = 155, a = "center"},
down = {x = 160, y = 183, a = "center"},
left = {x = 7, y = 169, a = "left"},
right = {x = 313, y = 169, a = "right"},
ul = {x = 7, y = 155, a = "left"},
ur = {x = 313, y = 155, a = "right"},
dl = {x = 7, y = 183, a = "left"},
dr = {x = 313, y = 183, a = "right"},
center = {x = 160, y = 169, a = "center"}
}
local cursTable = {
up = {x = 152, y = 163},
down = {x = 152, y = 191},
left = {x = 7, y = 177},
right = {x = 296, y = 177},
ul = {x = 7, y = 163},
ur = {x = 296, y = 163},
dl = {x = 7, y = 191},
dr = {x = 296, y = 191},
center = {x = 152, y = 177}
}

local function textboxStringDrawer(v, x, y, sss, f, box)
local t = BreakUpText(v, sss)

for i, str in ipairs(t) do
local space = 0
if not (box and box.settings)  return end
for j = 1, #str do
local k = 0
for l = 1, i-1 do
if t[l]  k = $+#t[l]+1 end
end
local rendstr = str:sub(j,j)
if box.settings.escape[j+k]  rendstr = box.settings.escape[j+k]..$ end

if box.settings.mode[j+k] == 0  -- Normal
v.drawString(x+space, y+8*(i-1), rendstr, f)
elseif box.settings.mode[j+k] == 4
v.drawString(x+space, y+8*(i-1)+(2*sin(FixedAngle(ease.linear(((j+leveltime)%10)*FU/10, 0, 360)*FU))/FU), rendstr, f)
else
local shkchnc = {FU/20, FU/5, FU/2}
local randx,randy = 0,0
if v.RandomChance(shkchnc[box.settings.mode[j+k]])
randx,randy = v.RandomRange(-1,1),v.RandomRange(-1,1)
end
v.drawString(x+space+randx, y+8*(i-1)+randy, rendstr, f)
end
space = $+v.stringWidth(rendstr, f)
end
end
end

hud.add(function(v, p)
if not p.mintstory or not p.mintstory.tree  return end
local tb = p.mintstory
local box = tb.tree[tb.current]

local spr, flip = v.cachePatch(box.portrait[1], box.portrait[2], false, box.portrait[3], box.portrait[4])
if box.portrait and spr
local colr = v.getColormap(box.portrait[1], box.color)
v.drawScaled(-3, 88<<16, 15000, spr, (flip and V_FLIP or 0)|V_SNAPTOBOTTOM, colr)
end

v.draw(0, 146, v.cachePatch("TBOXBACK"), V_SNAPTOBOTTOM)
v.draw(0, 146, v.cachePatch("TBOXBAC2"), V_SNAPTOBOTTOM|V_20TRANS)
if box.name v.drawString(258, 137, box.name, V_YELLOWMAP|V_SNAPTOBOTTOM) end

if tb.render
textboxStringDrawer(v, 7, 153, tb.render, V_ALLOWLOWERCASE|V_SNAPTOBOTTOM, tb)
end

if box.choices
local e = ""
for i, j in ipairs(box.choices) do
local h = (tb.choice == i) and V_YELLOWMAP or 0
v.drawString(posTable[j.pos].x, posTable[j.pos].y, j.text, V_SNAPTOBOTTOM|V_ALLOWLOWERCASE|h, posTable[j.pos].a)
if tb.choice == i  e = j.pos end
end
if e and cursTable[e]  v.draw(cursTable[e].x, cursTable[e].y, v.cachePatch("M_CURSOR"), V_SNAPTOBOTTOM) end
end

if box.mini
for i, j in ipairs(box.mini) do
local a = "thin-"..posTable[j.pos].a
if a == "thin-left"  a = "thin" end

local spr = v.getSprite2Patch(j.portrait[1], SPR2_TBXM, false, j.portrait[2], 1)
local col = v.getColormap(j.portrait[1], j.color)

if tb.settings.wait < 2+i and tb.settings.wait > i
if a == "thin"
v.draw(posTable[j.pos].x+8, posTable[j.pos].y+10, spr, V_SNAPTOBOTTOM|V_50TRANS, col)
v.drawString(posTable[j.pos].x+16, posTable[j.pos].y+2, j.text, V_SNAPTOBOTTOM|V_ALLOWLOWERCASE|V_50TRANS, a)
elseif a == "thin-right"
v.draw(posTable[j.pos].x-8, posTable[j.pos].y+10, spr, V_SNAPTOBOTTOM|V_FLIP|V_50TRANS, col)
v.drawString(posTable[j.pos].x-16, posTable[j.pos].y+2, j.text, V_SNAPTOBOTTOM|V_ALLOWLOWERCASE|V_50TRANS, a)
else
v.draw(posTable[j.pos].x-(v.stringWidth(j.text, V_ALLOWLOWERCASE, "thin")/2), posTable[j.pos].y+10, spr, V_SNAPTOBOTTOM|V_50TRANS, col)
v.drawString(posTable[j.pos].x+8, posTable[j.pos].y+2, j.text, V_SNAPTOBOTTOM|V_ALLOWLOWERCASE|V_50TRANS, a)
end
elseif tb.settings.wait >= 2+i
if a == "thin"
v.draw(posTable[j.pos].x+8, posTable[j.pos].y+8, spr, V_SNAPTOBOTTOM, col)
v.drawString(posTable[j.pos].x+16, posTable[j.pos].y, j.text, V_SNAPTOBOTTOM|V_ALLOWLOWERCASE, a)
elseif a == "thin-right"
v.draw(posTable[j.pos].x-8, posTable[j.pos].y+8, spr, V_SNAPTOBOTTOM|V_FLIP, col)
v.drawString(posTable[j.pos].x-16, posTable[j.pos].y, j.text, V_SNAPTOBOTTOM|V_ALLOWLOWERCASE, a)
else
v.draw(posTable[j.pos].x-(v.stringWidth(j.text, V_ALLOWLOWERCASE, "thin")/2), posTable[j.pos].y+8, spr, V_SNAPTOBOTTOM, col)
v.drawString(posTable[j.pos].x+8, posTable[j.pos].y, j.text, V_SNAPTOBOTTOM|V_ALLOWLOWERCASE, a)
end;end;end;end;end)