My Background (skip if uninterested): Hello! I am new to game development, slightly familiar with programming, and I have a lot of questions. Because of this, and the fact I'm due to go to college in August, I think this forum will be my coding home for now haha.
Problem part 1: So I made a conscious effort to learn Lua for Love despite my first project being a visual novel. There are alternatives like Ren'Py, but I want the flexibility that a game framework can offer. One of the things I gave up in this choice, however, is Ren'Py's built-in *interpreter*? I don't know the proper term which is properly my biggest concern. What do you call that? A part of a program where you put a script (or any kind of file) and it *interprets* it (I think?). This would then display the text in a formatted, separated way. I couldn't find the term online so I'd like to know! If the question can't necessarily be answered, I would appreciate knowing the name of this concept. It could help me into researching and learning how to do one myself.
Problem part 2: How would you go about writing an *interpreter* written in Love/Lua for displaying pieces of text (one - by - one) in a program? It's not just going through a table of string via using love.graphics.print(), it would also detect things besides just plain text in a txt or lua file.
Is there books or articles on this sort of thing to read? I don't mind if they're not Lua specific since I'm interested in learning conceptually. I want an idea of how one would go about it. Sorry for being vague and making this question unnecessarily complicated. Any advice or resources would be appreciated. I want to make games! But I might have made a bad first choice of game haha..
How to create an 'interpreter'? Or some other term closely related..
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
- zorg
- Party member
- Posts: 3465
- Joined: Thu Dec 13, 2012 2:55 pm
- Location: Absurdistan, Hungary
- Contact:
Re: How to create an 'interpreter'? Or some other term closely related..
Hi and welcome to the forums!
I think you could call it an intrpreter, or maybe a script parser or even a scripting engine... to be honest, it's not the name that matters, but what it does.
Now, implementing one is as hard as you want to make it; there is no one solution. Also, while löve doesn't have one of these inbuilt, depending on how much you know coding-wise, a simple design is easy to do.
Also, for what is worth, others might say to just use ren'py but i'd disagree simply because löve's more capable... and that i have seen visusl novel like interfaces in projects done by others here before.
I'm on mobile atm so unfortunately i cant link you any examples nor learning materials. :c
I think you could call it an intrpreter, or maybe a script parser or even a scripting engine... to be honest, it's not the name that matters, but what it does.
Now, implementing one is as hard as you want to make it; there is no one solution. Also, while löve doesn't have one of these inbuilt, depending on how much you know coding-wise, a simple design is easy to do.
Also, for what is worth, others might say to just use ren'py but i'd disagree simply because löve's more capable... and that i have seen visusl novel like interfaces in projects done by others here before.
I'm on mobile atm so unfortunately i cant link you any examples nor learning materials. :c
Me and my stuff True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Re: How to create an 'interpreter'? Or some other term closely related..
I wrote a markup interpreter in python, years ago. It was very simple. All I did was store a dozen or so regular expressions in a table and run them on each line of text in a series of if statements. This would be even easier in lua, since the pattern-matching is easier (at least for me) to use.
In some cases, you might just want to replace something, like *italic* with the word in italics. For that, I had a table with entries like:
In this case, I was writing out html, so there wasn't much to it. However, you could have the if statement change your font (or flag a change during rendering), insert an image, move the text around, etc.
I also looked for tags, such as "|title: My Title", and used that to keep entries for a table of contents as well as change the title font.
So, my advice would be, get familiar with lua pattern-matching, as that will do 90% of your work. Also, don't worry about getting it all working in one go. Get enough together that you can start writing, and add features as you write. You may not need all of the features other interpreters boast.
In some cases, you might just want to replace something, like *italic* with the word in italics. For that, I had a table with entries like:
Code: Select all
local rt = {
{ look_for = '%*([^%*]+)%*', replace = '<em>%1</em>' },
}
I also looked for tags, such as "|title: My Title", and used that to keep entries for a table of contents as well as change the title font.
Code: Select all
local tit_find = '^|title: (.+)'
Re: How to create an 'interpreter'? Or some other term closely related..
I was looking to procrastinate so i built a little text adventure engine game kinda thing
I hope it is a little bit useful,
I hope it is a little bit useful,
Code: Select all
function love.keypressed(key)
if key == 'escape' then love.event.quit() end
-- a little bit of magic down here:
-- we have a few keys that mean directions (up,down,left,right)
-- if the current room has that specific direction set in its table
-- it reads the string there (for example 'bar' or 'entrance')
-- and uses the _G lookup to find the variable that is named like that
-- and sets the current room to that.
local dirs = {'up', 'down'}
for i=1, #dirs do
if key == dirs[i] and currentRoom.directions[key] then
currentRoom = _G[currentRoom.directions[key]]
interactingWithActor = nil
currentDialogWithActor = nil
end
end
end
function love.load()
handCursor = love.mouse.getSystemCursor("hand")
arrowCursor = love.mouse.getSystemCursor("arrow")
love.mouse.setCursor(arrowCursor)
------------------------------------
-- actors
barkeeper = {
description = [[
This is a meaty guy,
Are that some back hairs I see?,
ah whatever he serves the drinks around here.
]],
dialogs = {
{
str= [[
"Hiya buddy! he shouts."
Over here at our place, its our policy to give anyone who doesnt have a whiskey some whiskey.
So waddaya say, you want a whiskey ?
Dont care what you say, here you go, a whiskey!
]],
action='addWhiskey', requirement='noWhiskey'
},
{
str= [[
"Hiya buddy! he shouts."
I see you have some fine whiskey!
Isnt it lovely?
]],
requirement='haveWhiskey'
}
}
}
drunk = {
description = [[
Someone has rolled on his ? hers? i dunno? .
Anyway, a very drunk person, snoring like an enormous pig.
his belly i am sure?,
Are that some back hairs too I see?,
What is this for a back haired place?.
Sorry i got distracted. this one is DRUNK.
]],
dialogs = {
{str=[["zzzz ggHHHGRRTTTTT zzZZZZ"]], requirement='noWhiskey'},
{str=[[
Dimpledodiree, thankyoufor
THE w hi isssskey
]], requirement='haveWhiskey', action='removeWhiskey'}
}
}
-- rooms
entrance = {
description = [[
You are out on the street in front of a bar, Lefty's it is called.
Kinda reminds you of something that name, this location. ah well who knows? dejavu?
Your feeling good today, your newest polyester suit on.
Will this be your lucky night?
]],
directions = {up='bar'},
}
bar = {
description = [[
You are inside the bar, it stinks, a few customers and a barkeeper are unaware of you.
]],
directions = {down='entrance', up='toilets'},
actors = {'barkeeper'}
}
toilets = {
description = [[
The stink here is unbearable!, A drunk has fallen asleep in a corner,
and all the toilets are broken.
Well, looks like it's gonna be one of those nights.
]],
directions = {down='bar'},
actors = {'drunk'}
}
---------------------------------------
inventory = {whiskey=0}
currentRoom = entrance
interactingWithActor = nil
currentDialogWithActor = nil
end
function addWhiskey()
inventory.whiskey = inventory.whiskey + 1
end
function removeWhiskey()
inventory.whiskey = inventory.whiskey - 1
end
function noWhiskey()
return inventory.whiskey == 0
end
function haveWhiskey()
return inventory.whiskey > 0
end
function pickBestDialog(dialogs)
-- todo instead of hard requirements think of a scoring system for more fuzzyness
local best = dialogs[1]
for i = 1, #dialogs do
if dialogs[i].requirement then
local met = _G[dialogs[i].requirement]()
if met then
best = dialogs[i]
end
end
end
return best
end
function button(id, x,y,str)
local font = love.graphics.getFont()
local w = font:getWidth(str)
local h = font:getHeight(str)
love.graphics.rectangle('fill', x,y, w+20, h)
love.graphics.setColor(0,0,0)
love.graphics.print(str, x, y)
love.graphics.setColor(1,1,1)
local clicked = false
local mx, my = love.mouse:getPosition()
if mx > x and mx < x+w+20 and my > y and my < y+h then
love.mouse.setCursor(handCursor)
end
if mouseState and mouseState.released then
if mx > x and mx < x+w+20 and my > y and my < y+h then
clicked = true
end
end
return {clicked=clicked}
end
function love.mousereleased(x,y)
mouseState = {released=true}
end
function love.draw()
love.mouse.setCursor(arrowCursor)
local descriptionWidth = love.graphics.getFont():getWidth(currentRoom.description)
love.graphics.print(currentRoom.description, 400-descriptionWidth/2, 60)
if currentRoom.actors then
local i = 1
for i = 1, #currentRoom.actors do
local str = currentRoom.actors[i]
local b = button('actor'..i, 100, 200+i*20, str)
if b.clicked then
interactingWithActor = _G[str]
end
i= i+1
end
if interactingWithActor then
love.graphics.print(interactingWithActor.description, 100, 200+30+i*20)
if interactingWithActor.dialogs then
local b = button('talk', 100, 230+i*20 + 100, 'talk')
if b.clicked then
local d = pickBestDialog(interactingWithActor.dialogs)
currentDialogWithActor = d
end
end
if interactingWithActor.dialogs then
local b = button('bye', 100 + 100, 230+i*20 + 100, 'bye')
if b.clicked then
interactingWithActor = nil
if currentDialogWithActor then
currentDialogWithActor.isDone = nil
end
currentDialogWithActor = nil
end
end
end
if currentDialogWithActor then
love.graphics.print(currentDialogWithActor.str, 100, 375+i*20)
if currentDialogWithActor.action then
local met = true
if currentDialogWithActor.requirement then
local result = _G[currentDialogWithActor.requirement]()
if not result then
met = false
end
end
if met then
_G[currentDialogWithActor.action]()
end
end
end
end
love.graphics.print('From here you go a few directions:', 500, 500)
local i = 1
for k,v in pairs(currentRoom.directions) do
love.graphics.print('You can press '..k..' to go to the '..v, 500, 530+20*i)
i = 1 + 1
end
mouseState = nil
end
Re: How to create an 'interpreter'? Or some other term closely related..
Well thank you for the replies everyone. @Nikki, I like your overall design and how you stored text was also real interesting. I'm wondering if I should stick to Lua strings like you or somehow use a txt file for parsing. I'll find out soon enough which is easier for me
As for @zorg, thanks! I think a script parser is exactly the word I was trying to use. I'll look around for visual novel projects and see what I can dig up to see if I can find one similar.
And lastly, @duaner, WOAH. Thank you! I think this is the exact concept that I will need to learn for building a proper text parser. I think learning pattern-matching will be a very useful skill. Thanks all for the assistance.
As for @zorg, thanks! I think a script parser is exactly the word I was trying to use. I'll look around for visual novel projects and see what I can dig up to see if I can find one similar.
And lastly, @duaner, WOAH. Thank you! I think this is the exact concept that I will need to learn for building a proper text parser. I think learning pattern-matching will be a very useful skill. Thanks all for the assistance.
Re: How to create an 'interpreter'? Or some other term closely related..
Lua expressions are simpler, but also less powerful. They lack two key features of regular expressions that would make them equivalent to finite automata, namely alternation like "expr1|expr2" and grouping of subexpressions like "(expr)". In Lua patterns, the parentheses are only used for capturing, but not for grouping, e.g. you can't do this: "match(es)?" to match either "match" or "matches".
They are sometimes good enough, but other times you have to add extra code to match what you need.
They are sometimes good enough, but other times you have to add extra code to match what you need.
- zorg
- Party member
- Posts: 3465
- Joined: Thu Dec 13, 2012 2:55 pm
- Location: Absurdistan, Hungary
- Contact:
Re: How to create an 'interpreter'? Or some other term closely related..
Then again, you might not even need a parser that can do full regex and lua's patterns may be enough... up to you really.
Me and my stuff True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Who is online
Users browsing this forum: No registered users and 2 guests