Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  

Author Topic: 2D RPG Engine  (Read 1386 times)

TSTwizby

  • Bay Watcher
    • View Profile
2D RPG Engine
« on: April 28, 2013, 03:52:23 pm »

Hello, it's been awhile. I've been working on this for a while on and off, and I'm pretty sure I actually posted about this before, but there's been a stretch of a few months where I haven't been posting much, due to some personal problems, so I figured it would be best to post a new topic.

I'm in the process of writing a generic 2D RPG Engine in Python, mainly because I'm too cheap to get RPGMaker. So far I haven't built much beyond the map, though I'm pretty happy with what I've managed to accomplish there. Currently, the following things have been implemented:

the construction of arbitrary animated non-tiled maps
maps built dynamically, loaded from .dat files
objects on the map have associated animations, can have an effect when interacted with that can be turned on and off
portals can be open/closed, made to open in only a certain direction
a single text box which could appear for example when interacting with a sign or something which prints text at variable speed, or instantaneously.
screen size, file types for maps/sprites, settings for textbox (font size, txt/back color, text loading speed etc.) set in a .cfg file
'camera' focused on an arbitrary rectangle around the player. When the player moves outside the rectangle, the camera moves to follow them.


features which have been partially implemented:

a scripting language to handle cutscenes and some more advanced interactions.
effects which can be applied to objects or text during cutscenes, such as fade in/out, changing color etc.


features that are planned but not implemented:

transitions between maps (e.g. fade to black, then fade into new map)
sound
events triggering on menu load
chest-type objects
and of course, the other two thirds of the game, menus and battles.


Once things are more complete I'll post the source code if there is any interest. If anyone would like to see it now, I'd be happy to send it to them, but be warned that I am a self-taught programmer which no concept of best practice. global and exec await.

Speaking of which, I'm currently running into a problem with my scripting language. One of the things I would like for it to be able to do is to modify or print the value of game variables, most usefully for things like the player's name or to say how much gold the player needs to buy a bridge or something like that. It is entirely possible to cover each of these cases individually, but considering that I want to make a general purpose engine it is also really stupid and would then require the user memorize a few dozen commands, as well as preventing them from adding in their own variables (for example, if they want to add a randomized password to a door or something. Again, I could just make a bunch of dummy variables or something, but again, that would be stupid and require memorizing a bunch of extra commands). The solution I am currently attempting to implement involves using exec, as I can think of no other way to reasonably handle this.
The following is called by a 'getNextEvent' type function when the script says to print a particular variable's value to the textbox:

Code: [Select]
                        #'next' is a string the first two characters of which are a code which determines what is to be done with the remaining characters. This is executed when the first two characters are ':['
param = 'Error' # storage variable, and placeholder value
exec('global ' + next[2:]) # intended to load  or create whatever the variable is.
exec('param = str(' + next[2:] + ')') # intended to set param equal to the variable's value cast as a string
txtbox.text = txtbox.text + param # adds the value of the variable as a string to the end of the function

for example, if the next command were ':[foo', and there were a variable 'foo' equal to seven, then it should execute:

global foo
param = str(foo)
txtbox.text = txtbox.text + param

and the result should be equivalent to

txtbox.text = txtbox.text + str(7)

In practice, the second 'exec' statement seems never to be called. There is no error of any kind, but param's value never is changed. The first exec may or may not be called, as since I am only checking the value of 'a' here it would assume global anyway. This problem has persisted even in what I believe is the above code's simplest incarnation, reproduced below:

Code: [Select]
a = 1

def b():
param = 'Error'
exec('global a')
exec('param = a')
print(param)
print(a)

b()

If anyone could explain why this is happening, or offer a workaround not using exec, it would be greatly appreciated.
Logged
I got a female and male dragon on my embark. I got cagetraps on the exits but im struggling to find a way to make them path into it.
Live bait.
3 dwarfs out of 7 dead so far

Mephisto

  • Bay Watcher
    • View Profile
Re: 2D RPG Engine
« Reply #1 on: April 30, 2013, 12:04:35 pm »

I have to admit, I don't quite understand what you're trying to do that couldn't be accomplished with a dict. Even that makes me a sad panda, though.

I'll do a deeper reading after I get off work.
« Last Edit: April 30, 2013, 12:39:50 pm by Mephisto »
Logged

TSTwizby

  • Bay Watcher
    • View Profile
Re: 2D RPG Engine
« Reply #2 on: April 30, 2013, 07:49:20 pm »

Thanks for the response. I guess I wasn't explaining clearly enough. I'm currently using a dict as a workaround, but if possible I would like to make it so that the user can work with the values of things in arrays and the like, which unless I am mistaken a dict cannot reference. For example, maybe they want to have the game screw around with the clock for some reason. The playtime is currently a tuple with days, hours, minutes. So if they for some reason want to display the number of hours played in a textbox without modifying the game code directly, they would need some way to reference the second element of the playtime tuple. Alternately I could make playtime three different variables, or add a command specifically to reference the playtime, but continuing on that road leads to stupid numbers of variables or commands floating around.
Logged
I got a female and male dragon on my embark. I got cagetraps on the exits but im struggling to find a way to make them path into it.
Live bait.
3 dwarfs out of 7 dead so far

Killjoy

  • Bay Watcher
    • View Profile
Re: 2D RPG Engine
« Reply #3 on: May 02, 2013, 10:47:31 am »

If anyone could explain why this is happening, or offer a workaround not using exec, it would be greatly appreciated.
'a' is in the global scope, and is a variable. So it is a global variable.
Therefore it will not be found in the local scope of the function.
You can explicitly define a global variable to be accessible in a function by using the keyword global.

Code: [Select]
a = 1
def b():
   global a
   b = a
   print(b)
b()

While I am at it.
DON'T EVER USE GLOBAL VARIABLES FOR ANYTHING SERIOUS, THEY WILL HURT YOU AND YOUR FAMILY DOWN THE LINE.
Seriously. Just don't.. If you can't live without global variables, then train your codefu until you can.
Logged
Merchants Quest me programming a trading game with roguelike elements.

TSTwizby

  • Bay Watcher
    • View Profile
Re: 2D RPG Engine
« Reply #4 on: May 02, 2013, 01:52:12 pm »

I think you're misunderstanding. What I want is for the end user to be able to specify whether they want to change a, b, c, d, e, f, g or whatever other variable they want, without having to make separate functions for a b c and so on and, with the only input from the user being strings. I understand scope and how global works, though I have never had it explained satisfactorily to me why they are evil beyond the somewhat contrived 'you might forget which variables are global and name other variables the same thing and get confused'. My issue is that there seems to be no way to get from 'string containing variable name' to 'variable' for things like arrays and tuples without going through exec, and exec is not working for no obvious reason.
Logged
I got a female and male dragon on my embark. I got cagetraps on the exits but im struggling to find a way to make them path into it.
Live bait.
3 dwarfs out of 7 dead so far

Killjoy

  • Bay Watcher
    • View Profile
Re: 2D RPG Engine
« Reply #5 on: May 02, 2013, 05:29:53 pm »

Yes, I am misunderstanding.
You don't use globals because they introduce global states. Something you don't care about, as you know how it is supposed to work. But future you, or anyone using the engine can and will make of mess of things. Also, it quickly makes game code a unmanageable mess.

Either way, you can use dicts instead of that thing you are doing with exec.
Code: [Select]
mapOfParameters = {} #Create a new map
mapOfParameters["playerHealth"]   = 100
mapOfParameters["playerName"]   = "hero"
mapOfParameters["playerCoins"]     = 0x1000
.... a lot of code and stuff

if mapOfParameter.has_key(next[2:]):
   param = mapOfParameter.has_key(next[2:])
else:
   #Throw an error or something, the user of the engine is attempting to reference something not currently mapped in the engine!
   pass
Logged
Merchants Quest me programming a trading game with roguelike elements.

TSTwizby

  • Bay Watcher
    • View Profile
Re: 2D RPG Engine
« Reply #6 on: May 02, 2013, 06:33:55 pm »

Okay, I get what you're saying, I think. I guess that I just don't realize how big of a problem that could be yet. The problem with using dicts, as I understand them, is that there doesn't seem to be a way to reference mutable objects in general and arrays in particular. I've done a bit more research, and I'm pretty sure tuples actually work, which invalidates my previous example, but if I wanted to instead have an array with the playable characters' names or something then it would still cause trouble.
Logged
I got a female and male dragon on my embark. I got cagetraps on the exits but im struggling to find a way to make them path into it.
Live bait.
3 dwarfs out of 7 dead so far

Mephisto

  • Bay Watcher
    • View Profile
Re: 2D RPG Engine
« Reply #7 on: May 02, 2013, 06:48:52 pm »

Code: [Select]
>>> foo = {}
>>> foo['bar'] = ['John', 'Paul', 'George', 'Ringo']
>>> foo['bar']
['John', 'Paul', 'George', 'Ringo']
>>> foo['bar'][2]
'George'
>>> foo['bar'] += ['Rumpelstiltzkin',]
>>> foo['bar']
['John', 'Paul', 'George', 'Ringo', 'Rumpelstiltzkin']
>>>

Python cares very little for types in instances like this.
Logged

TSTwizby

  • Bay Watcher
    • View Profile
Re: 2D RPG Engine
« Reply #8 on: May 02, 2013, 07:25:30 pm »

Hmmm... I was sure I'd tried that. Well, thank you.
Logged
I got a female and male dragon on my embark. I got cagetraps on the exits but im struggling to find a way to make them path into it.
Live bait.
3 dwarfs out of 7 dead so far

Killjoy

  • Bay Watcher
    • View Profile
Re: 2D RPG Engine
« Reply #9 on: May 03, 2013, 02:08:37 am »

What you are referring to are probably sets.
They can't contains mutable objects as python uses the data in the object to compute the hash.

They can have a similar syntax to dicts:
Code: [Select]
#set exmaple
s = {1, 2, 3, 4, 5}
s.add(10)
d = [1,2,3,4]
s.add(d) #exception
s.add(tuple(d)) #ok
Logged
Merchants Quest me programming a trading game with roguelike elements.

TSTwizby

  • Bay Watcher
    • View Profile
Re: 2D RPG Engine
« Reply #10 on: May 07, 2013, 03:37:07 pm »

That... may very well have been my problem. I'm not sure how I've gotten as far as I have without that screwing up something before now. Thanks.


-By the way, I'm currently in the middle of finals, so I won't have anything new here for a while, in case anyone cares. But I should have something in a week or two which is sufficiently complete to be played around with.
Logged
I got a female and male dragon on my embark. I got cagetraps on the exits but im struggling to find a way to make them path into it.
Live bait.
3 dwarfs out of 7 dead so far