Started working on making Visual Novel DS for Vita. For those who don't know VNDS are converted, rudimentary versions of popular popular visuals novels. Examples of some compatible games are Umineko, Never 7, Higurashi, Tsukihime, Song of Saya, Ever 17, Cross Channel.
This is pretty much non-functional at the moment. The only thing that works is it is able to navigate the scripts properly and it will display the backgrounds and debug text of the line currently being read.... And somehow just that totals around 350 lines.
So nothing really interesting now right now. Just stating my earnest attempt to do this. And do it right with proper programming. Last time I tried this on 3DS it turned into a mess because I neglected to use classes. This is basically a rewrite
What I have so far (Using Lua Player Plus for Vita)
This is pretty much non-functional at the moment. The only thing that works is it is able to navigate the scripts properly and it will display the backgrounds and debug text of the line currently being read.... And somehow just that totals around 350 lines.
So nothing really interesting now right now. Just stating my earnest attempt to do this. And do it right with proper programming. Last time I tried this on 3DS it turned into a mess because I neglected to use classes. This is basically a rewrite
What I have so far (Using Lua Player Plus for Vita)
Code:
--1000000 microseconds in 1 second
ip, port = Network.initFTP()
Sound.init()
vars = {}; --values in local save memory, to be kept in normal save files. For things like character flags and such.
gvars = {}; --sets variables in global.sav. For things like cleared path flags
Scr = { w = 960,
h = 544};
vndsPath = "ux0:/data/vnds/";
gameName = "Saya/";
gamePath = vndsPath .. "novels/" .. gameName;
backgroundPath = gamePath .. "/background/"
foregroundPath = gamePath .. "/foreground/"
scriptPath = gamePath .. "/script/"
soundPath = gamePath .. "/sound/"
white = Color.new(255,255,255);
black = Color.new(0,0,0);
gray = Color.new(82,82,82);
red = Color.new(157,62,62);
green = Color.new(62,157,62);
blue = Color.new(62,62,157);
function fileToTable(file) -- put every line of inputted file into a table and return the table.
returnTable = {};
local i = 1;
for line in io.lines(file) do
returnTable[i] = line;
i = i + 1;
end
return returnTable
end;
function evaluateIf(LHS, OP, RHS) --Evaluate VNDS if/fi conditionals
-- print ("--EVALUATING: " .. LHS .. OP .. RHS)
if (OP == "==") then
return (LHS == RHS)
elseif (OP == "!=") then
return (LHS ~= RHS)
elseif (OP == ">" ) then
return (LHS > RHS)
elseif (OP == "<" ) then
return (LHS < RHS)
elseif (OP == ">=" ) then
return (LHS >= RHS)
elseif (OP == "<=" ) then
return (LHS <= RHS)
end
end;
function wordList (input) --split input string into a table of separate words
local i = 1;
local list = {};
for word in input:gmatch("%S+") do
list[i] = word;
i = i+1;
end
return list;
end;
function strIsNumber(testers) --Check if string is a number
if tonumber(testers) ~= nil then
return 1;
else
return 0;
end;
end;
function lesserOf(a, b)
if (a <= b) then
return a;
else
return b;
end
end;
function script(input) --Script class and functions to handle a VNDS script.
local object = {}
object.filename = input;
object.currentLineNumber = 1;
object.table = fileToTable(object.filename)
object.currentText = object.table[object.currentLineNumber];
object.totalLines = #object.table;
function object:advanceLine(input)
if input == nil then input = 1 end;
if (self.currentLineNumber + input < 1) then
input = 0
elseif (self.currentLineNumber + input > self.totalLines) then
input = self.totalLines - 1
end;
--error handling ^
self.currentLineNumber = self.currentLineNumber + input;
self.currentText = self.table[self.currentLineNumber];
end;
function object:print()
-- print("PRINTING CURRENTLINE: " .. self.currentText);
end;
function object:jumpTo(input) --Jump to a given line number
if input == nil then input = 1 end;
if input < 1 then input = 1 end;
if input > self.totalLines then input = self.totalLines end;
--error handling ^
self.currentLineNumber = input;
self.currentText = self.table[self.currentLineNumber];
end;
function object:handle() --Does everything. Makes game work
local words = wordList(self.currentText);
Graphics.fillRect(0, 900, 0, 80, white)
Graphics.debugPrint(0, 0, self.currentLineNumber .. ": " .. self.currentText, black)
local switch = words[1]
if (switch == "bgload") then
bg = background(backgroundPath .. words[2])
bg:print()
elseif (switch == "setimg") then
elseif (switch == "sound") then
elseif (switch == "music") then
elseif (switch == "text") then
elseif (switch == "choice") then
elseif (switch == "jump") then
scr = script(scriptPath .. words[2])
elseif (switch == "delay") then
elseif (switch == "cleartext") then
elseif (switch == "if") then
--v[NEWLY ADDED TO PREVENT IF ENDING >= 1 error in saya main.scr where Ending has not been previously declared
if (vars[words[2]] == nil) then --fix error attempt to perform arithetic on nil value. Happens when "setvar var + (a number)" when var hasn't been previously stated
vars[words[2]] = tostring(0);
end;
local LHS = vars[words[2]];
local OP = words[3];
local RHS = words[4]; --Correct, VNDS doesn't support variables on the right
if (evaluateIf(LHS,OP,RHS) == false) then
-- IF FALSE
local count = 1;
while count > 0 do
self:advanceLine()
print("----skipping through conditional: ")
local cList = wordList(self.currentText);
if (cList[1] == "if") then
count = count + 1;
elseif (cList[1] == "fi") then
count = count - 1;
end;
end;
else
-- IF TRUE
end;
elseif (switch == "gsetvar" or switch == "setvar") then
local destination = {}; --create reference to one of the two variable lists, vars or gvars
if(switch == "gsetvar") then
destination = gvars;
else
destination = vars;
end;
local LHS = words[2];
local OP = words[3];
local RHS = words[4];
if (destination[LHS] == nil) then --fix error attempt to perform arithetic on nil value. Happens when "setvar var + (a number)" when var hasn't been previously stated
destination[LHS] = tostring(0);
end;
if (OP == "=") then
if ( strIsNumber(RHS) == 1 ) then
destination[LHS] = tostring(RHS);
else
destination[LHS] = tostring(destination[RHS]);
end;
elseif (OP == "+") then
if ( strIsNumber(RHS) == 1 ) then
destination[LHS] = tostring(math.floor(destination[LHS] + RHS));
else
destination[LHS] = tostring(math.floor(destination[LHS] + destination[RHS]));
end;
elseif (OP == "-") then
if ( strIsNumber(RHS) == 1 ) then
destination[LHS] = tostring(math.floor(destination[LHS] - RHS));
else
destination[LHS] = tostring(math.floor(destination[LHS] - destination[RHS]));
end;
end;
elseif (switch == "goto") then --goto a label in VNDS script.
for q = 1, #self.table, 1 do
if (string.find(self.table[q], ("label " .. words[2]) ) ) then
self:jumpTo(q);
end;
end
end;
self:advanceLine() -- after line has been handled, advance to next line;
end;
return object;
end;
function background(input)
local object = {}
object.filename = input;
object.image = Graphics.loadImage(object.filename)
object.w = Graphics.getImageWidth (object.image)
object.h = Graphics.getImageHeight (object.image)
object.scale = 3 -- scale '1' print scaled vertically
-- scale '2' print scaled horizontally
-- scale '3' print scaled to screen;
-- scale OTHER NUMBER print unscaled
function object:print()
local hScale = Scr.h / self.h
local wScale = Scr.w / self.w
local S = 1; --local scaling of image
if (object.scale==1) then
S = hScale
elseif (object.scale==2) then
S = wScale
elseif (object.scale==3) then
S = lesserOf(wScale, hScale)
end
Graphics.drawScaleImage( ( (Scr.w - self.w * S) / 2), 0, S, S, self.image)
Graphics.debugPrint(250, 250, S, Color.new(255,255,255))
end;
return object;
end;
function foreground(input, in_x, in_y)
if in_x==nil then in_x=0 end;
if in_y==nil then in_y=0 end;
local object = {}
object.filename = input;
object.image = Graphics.loadImage(object.filename)
object.w = Graphics.getImageWidth (object.image)
object.h = Graphics.getImageHeight (object.image)
object.x = in_x
object.y = in_y
function object:print()
local S = 1
--Graphics.drawScaleImage(self.x, self.y, S, S, self.image)
end;
return object;
end;
function audio(input, t, l)
local object = {}
object.filename = input;
object.type = t;
object.loop = l
object.format = string.sub(object.filename,string.len(object.filename)-2,string.len(object.filename)) -- grab last 3 character from filename
object.sound = {}
if (object.format == "mp3") then
object.sound = Sound.openMp3( object.filename )
elseif (object.format == "ogg") then
object.sound = Sound.openOgg( object.filename )
elseif (object.format == "wav") then
object.sound = Sound.openWav( object.filename )
elseif (object.format == "mid") then
object.sound = Sound.openMidi( object.filename )
end;
function object:play()
Sound.play(self.sound,NO_LOOP)
end;
function object:togglePlayStatus()
if Sound.isPlaying(self.sound) then
Sound.pause(self.sound)
else
Sound.resume(self.sound)
end
end;
return object
end;
function textbox()
local object = {}
object.font = Font.load(gamePath .. "default.ttf")
object.fontsize = 12
object.color = white
object.text = "The lazy brown fox."
return object
end;
bg = background(backgroundPath .. "/ba01me0.jpg")
fg = foreground(foregroundPath .. "/fumi01.png")
scr = script(scriptPath .. "s01.scr")
txt = textbox();
snd = audio(soundPath .. "/01.ogg", "sound", 1)
snd:play()
oldpad = Controls.read();
while true do
pad = Controls.read();
if Controls.check(pad, SCE_CTRL_TRIANGLE) then
Network.termFTP()
System.launchEboot("app0:/eboot.bin")
System.wait(800000)
System.exit()
end
-- Blend some images with different funcs (normal, rotated, scaled)
Graphics.initBlend()
-- Screen.clear()
if Controls.check(pad, SCE_CTRL_SQUARE) then
bg:print()
fg:print()
Graphics.debugPrint(0, 0, bg.filename, Color.new(255,255,255))
end
if Controls.check(pad, SCE_CTRL_CIRCLE) then
bg:print()
fg:print()
Graphics.debugPrint(200, 200, (math.floor(collectgarbage("count")*1024)), Color.new(255,255,255))
end
if Controls.check(pad, SCE_CTRL_CROSS) then
scr:handle()
collectgarbage()
end
Graphics.termBlend()
-- Flip screen
Screen.flip()
end