|
|
Go to Hardcrawler Page: 1 2 3 4 5 6 7 8 9
|
I am going to keep track of my progress on this project until I'm either a) Done or b) Find a new obsession. |
|
|
|
2011-06-30: Like the Phoenix
|
Oh, I got a drug and I got the bug and I got something better than love.
Oh yes. It's back. I'm whipping out the Blender. I'm mixing up some sprites. I'm dusting off the old Lua book. I'm making 3D models of loaves of bread.
Hardcrawler for the iPhone. Yes it's true.
Permalink to this post.
|
|
|
|
2006-11-20: Rapid Progress
|
I know I haven't been updating as often as I should. When I get all jazzed about a project I'd much rather write code than write about writing code. The decision to gut the networking from Hardcrawler was a good one. It didn't take much time, and it freed me up from a ton of headaches. Adding new client-side features no longer requires a laborious addition of networking code or paranoid consideration of the potential for hacked clients. Now I can freely add client functionality with the only slowdown being any bad decisions I may have made with my windowing code.
The decision to use Nethack for the rough game design was also a good one. Nethack has been in development for over 20 years, and there is a very rich set of gameplay ideas to pull from. Luckily, the interface design of Hardcrawler is so different from Nethack that even though I'm heavily borrowing from Nethack gameplay, the resulting game is actually quite a different experience.
A very interesting challenge has been getting in a lot of the complex Nethack features without making my interface as clusterfucked as Nethack's. I wanted to avoid requiring the player to memorize many many arcane keyboard commands, but still retain all of the complex item interactions; it has been difficult to do. I have made a couple of sacrifices, and so I have implemented one of these odd commands (dipping) as a different action cursor similar to the old C64 and PC adventure games. In a lot of those games, Quest for Glory comes to mind, you were able to change your cursor from a "get" cursor, to an "attack" cursor, to a "use" cursor etc.
The fundamental interface in Hardcrawler is still basically like Ultima Online. I have drag-and-drop style inventory management with double-clicking to use an item. 90% of the mechanics of the game use this paradigm. For attacking, I also use the UO style attcack cursor. The only odd mechanic I have is dipping. Dip may seem like a very odd action, but it is fundamental for a lot of different item interactions in Nethack, so it is the one place so far where I have violated my simple drag-and-drop and double-click mechanism for item interaction. You switch to the "dip" cursor which allows you to select an item to dip, and then a target to dip into.
There are a lot of Nethack behaviors that have been a challenge to implement intuitively. The entire religion and praying subsystem is an example. First, I want to make religion simple for the player to understand and use, and second I want as much of the religion logic as possible in Lua code. In Nethack itself, there is a pray command, as well as a sacrifice command. I didn't want to have to implement a whole new command for the user to memorize, so I introduced the concept of a religious "fetish" object that always exists in the starting player's inventory. Double clicking this item causes the player to pray, and all of the religion-specific state of the game is actually tracked through this item's Lua code. To sacrifice, the user double clicks on an altar entity in the game, which prompts them to select a target corpse.
So far I have succeeded in avoiding the addition of arcane keyboard commands, but it has been difficult. There are many many commands in nethack, and it has been a very interesting challenge to implement these complex behaviors only using the drag and drop/double click interface.
On a totally unrelated topic, I have had a blast using Blender to make 3D models of the myriad objects and monsters n Nethack. One of the advantages of everything ultimately getting rendered down to a maximum of 96x96 pixels is that I can be relatively sloppy with some of the details in Blender...Although the small size in itself is actually pretty challenging -- it can be difficult to even tell what something is when it's that small. In the screenshot you can see an ant model I made. I am now using my "dye" trick with my monsters, which allows one model to be a giant ant, a soldier and, and a fire ant depending on the r,g,b values in its entity file. This trick will go a long way in helping me implement a large chunk of the many many monsters in Nethack.
Permalink to this post.
|
|
|
|
2006-11-07: Hardcrawler -- How things change
|
A lot has changed since we last talked about Hardcrawler. I went to the lua channel on IRC and immediately solved my Lua fragility problem. I consolodated all of my lua states into one global lua state. I bought the lua book and discovered how to implement very powerful object-oriented data structures in Lua. At this point, I am not implementing any new behaviors in C++. In C++, I only implement interface functions, and ALL of my custom behavior is implemented in Lua. I have implemented many many Nethack behaviors without my C++ code even being aware of the behavior. From tinning kits, to wands, to Magic Markers, I am able to implement extremely complex objects completely in Lua.
I had to make a painful decision, though. Hardcrawler is no longer a multiplayer game. I was keeping the client/server functionality in simply because I had devoted so much time to the coding. In reality, there was no compelling gameplay reason to have a client/server architecture. In fact, the networked design slowed my progress. I was still dealing with minor bugs related to tracking of server entity state, notification of item destruction, etc.
I gutted all of the networking from Hardcrawler in a day. This is partially because my implementation had an essentially identical client and server. The client made requests to the server, and the server modified its internal state based on these requests. Any changes in state on the server resulted in packets being fired to the client notifying it to change its state. Fundamentally, the client and the server were identical. All I had to do was pull all of my server responder code into the client request functions, and the client state was correctly updated.
The magic of properly abstracted code. Don't look at it, though, I can't take the criticism.
Immediately upon moving the a standalone program, I was able to implement a slew of functionality that was difficult before. Implementing fog of war and player blindness was now trivial. (Before, on the server, if I were to implement fog-of-war I would have to actively track where the player had been and only send packets from the revealed squares, plus, if the player was blind, I could not send any packets from unseen entities due to the risk of hacked clients.)
On the day I moved over to standalone Hardcrawler, I implemented (fully functional) the following: Blindfolds, blindness intrinsic, potion of blindness, warning intrinsic, ring of warning, fog of war, saving/loading of fog of war. That may not seem like a lot, but it took one day!
Permalink to this post.
|
|
|
|
2006-10-01: Encoding of Custom Entity Bahavior
|
I'm working on some Nethack-like richness for Hardcrawler. One of the problems is that there are many many many types of items, and almost all of their behaviors are unique. i.e. a healing potion has drastically different behavior when drunk, when thrown, when something is dipped in it, etc. than say a polymorph potion. Here are a couple different possible approaches:
1) Scripting
Use inheritance for classes of entities (CPotionEntity CScrollEntity CWeaponEntity) but embed lua or similar scripting language into the engine for the fine-grained behaviors of the different entities
2) Go inheritance crazy
Have a specific .cpp and .h file for every item that has specific behaviors. You'd end up with CHealingPotionEntity.cpp/.h for every item, pretty much. One thing making me lean away from this is that I have a "factory" class (I think that's the pattern) called CEntityManager that is in charge of how to create all of my entities, and I have to have an enumeration type for each entity type, and a specialized case in my creation function per entity type...not THAT big a deal, but kinda messy
3) Old fashioned If/Then
Put a bunch of "if" cases in the CPotionEntity OnUse class based on the specific entity type and call a specialized function
I posed this problem to Tony, and he suggested that I go with approach 2, but use some fancy macro-wrangling to handle the factory code overhead. He sent me some example code and I pored through it for a couple of days. After some tweaking, I saw how the approach would definitely work. At the end of the day, however, I decided that I did not like this approach.
There were two big issues that pushed me away from the macro trick. First, it would end up obfuscating my code. What you see in the text editor is NOT what gets sent to the compiler. In the case of the relatively complex case statement in the factory for entity creation, this ends up being quite a large amount of code. Second, the macros are just really ugly and complex. I have been a C++ programmer for over 10 years at this point, and I am finding these macros difficult to digest. If I intend to have anyone else look at this code, I have to expect that these macros will be a huge barrier to entry.
Tony's main reason for recommending against a run-time scripting language was speed. By their very nature, interpreted languages are much slower than straight C/C++. For what I am doing, however, I don't think it is going to matter very much. I am not using the scripting language to handle per-click behaviors like movement or physics, but rather responses to user actions such as using an item. The scripts will only be called as responses to user input, which by its very nature will not be multiple times per click.
I decided to go ahead and whip out the old LUA interpreter. I had integrated LUA into our old Revolution project a few years ago, so I have a basic idea of how it works. Basically, you define a function layer between your engine and the LUA scripts that allow the scripts to query and modify game state information at run time.
I started this task about two days ago, and have already successfully implemented the entirety of the "Scroll of Identify" behavior in a Lua script. The really cool thing about this is that any and all custom behavior of this type of scroll is now handled in the Lua script. My C++ code is pristine, and only concerned with *generic* scroll behaviors. I can now go hog wild with implementing scroll behaviors without any regard for the C++ code. All of the entity behaviors will be contained in the Lua scripts, and the C++ code will not change...The entity behaviors become data which is really cool.
All is not ponies and rainbows in Lua land, however. I still have two major stumbling blocks that are a problem. First, my entity factory is a "clone" style factory, meaning that it keeps an internal array of all possible entity types, and when one is requested, it clones one of its entities to give to the caller. The problem now is that a lua_state structure, created from parsing the entity .lua file, is one of the member variables, and I need some way to clone it. From what I can tell there is no facility to do this, and of course a shallow copy of the structure will not work. I definitely do not want to have to re-parse the .lua file for each entity created in the world...that would be very expensive. If I keep absolutely NO state data in the lua_state, then I should be fine...But there is the possibility that a custom entity will need to track custom variables (i.e. a poison potion may need to store its potency) and these custom variables cannot be shared across entity types.
The other major problem I have encountered is that the Lua runtime seems to be *extremely* fragile. The simple act of misspelling a function name will actually exit out of the program with no exception of any sort. My understanding is that part of the *point* of a high-level extensible scripting language is that your program is protected from errors in the custom code, so I'm pretty sure that there is a way to trap this and avoid it, but so far I haven't found a way.
(Update)
It turns out I was using the non-protected function to call my lua scripts. Lua errors with descriptive text now. All is well in the world.
Permalink to this post.
|
|
|
|
2006-09-21: Relational Databases VS XML for Game Persistence
|
I have used XML a bit in the past. I used it for the House Editor for Hardcrawler, and a few other assorted things, so I am not completely clueless about it, but I never really understood what the big deal was. Actually, I still don't, even though I now file it under the "pretty useful" category in my toolbelt.
As I have said before, I tend to be a late adopter of technology -- particularly when it is some new-fangled fix-all hype machine. XML definitely falls under the hype-ball category. In the hype-laden world of computers and programming, overhype is pretty much par for the course. Some new crap will come out, and every scrub you know who calls himself a programmer is suddenly incorporating said crap into their program just because they can. They don't care if it fits, hell, 9 times out of 10 they don't even really understand the technology, they just do it because it makes them l33t.
When I first moved Hardcrawler over to the land of MMORPG, I decided to use a relational database for persistence (i.e. retaining game state in between executions of the server). Since there is so much data in a MMORPG, I thought that using a relational database (RDBMS) would be a good match. Since I already had quite a bit of experience from PGDesigner, It didn't take me long to write the code necessary to dump out all of Hardcrawler's data into a PostgreSQL database. As the entity handling in Hardcrawler got more complex, however, I started noticing some definite limitations with using a RDMS for object serialization.
All of my dynamic entities in hardcrawler inherit from the base class CEntity. For the sake of this explanation, all a CEntity has is the ability to render itself, an x,y coordinate, and a blocking flag to determine if blocking entities can occupy the same x,y coordinate as itself. CItemEntity inherits from this class, and adds to it the ability to be carried in a player's inventory, as well as the concept of stacking multiples. CItemEntity keeps track of its x,y coordinates in the player's bag by the variables bagx and bagy, and has a quantity variable to track stacking.
Of course, I have many of other types of entities. Armor, Weapons, Potions, Scrolls, Players, Monsters, Spawns, etc. and each of these have their own variables which must be stored for persistence. The question is, how do you represent this in a relational database? I run into this problem all of the time in C++, and the answer is inheritance. And I know that PostgreSQL has support for object oriented databases, but I have no desire to move away from the Relational paradigm. I wanted to store things in a standard relational model. The way I saw it, I had two basic options.
Option one was to properly break the tables out into third normal form and only store what was necessary for each entity type. Therefore you would have an entity table with just basic entity data, and if the entity was also an item, you would have a corresponding entry in the item table with item data. This continued for every level of inheritence, meaning that a sword would have an entry in the weapon table, the wearable item table, the item table, and the entity table. After much thought, this just seemed like it would be a funky mess of queries any time I needed to pull a complex entity out of the database.
Option two was to simply create one giant entity table that contained every possible variable. This is a pretty dirty solution, as even a spawn entity would need a bagx,bagy column since these columns were needed by items. The net result is that the entity table would be huge. Even though this is really nasty, it is the option I used.
Even with the simple database, my actual database updates ended up being pretty complex. In my C++ code, I wanted each child class to only have to worry about its own variables when saving to the database. I didn't want the weaponentity to have to worry about saving out x,y coordinates that should be the responsibility of the base entity class. How do you go about doing this in C++? One way I considered was to build a giant SQL string starting at the deepest child level with each class appending its own variables to the update query, and then bubbling up to the base entity class which would then run the query. Given the horrible mess of C++ strings (even with STL), I decided not to do this. What I did instead was have each class run a SQL query which only updated the variables in the entity table that were its responsibility, and then called the parent class's update function which did the same thing until the CEntity class was reached. This was relatively straightforward, but very expensive (each subclass did its own update query) and just felt wrong.
Another problem with keeping everything in an RDBMS is that the C++ database access code I have is just funky. It's kind of bloated. Working with databases in QT is a breeze, but I'm not willing to put a QT dependency in Hardcrawler. Every time I added a new variable to a class, I had to go into the database and add a corresponding column, and then add code to pull it from the database, and add code to save it to the database. The overhead for even small additions was heavy, let alone an array or complex structure!
Due to these difficulties, I decided to move my entire backend over to XML using the TinyXML library. I was able to do the entire port in a couple of days, and I am very satisfied with the result. The heirarchical structure of XML lends itself very well to (de)serialization of object oriented data. I was able to very elegantly structure my save code to allow each subclass to only handle its own variables, and have one final dump of XML at the end. The loading was just as easy. The code necessary to do all of this is about 300% smaller, and much much cleaner. And when I want to add a variable, it only takes a few lines of code to save/load it from the XML file. As an added bonus, arrays and complex data structures are inherently easier to dump out to the XML file due to the flexible nature of its storage.
Another huge plus for the XML solution is I don't need a RDMS server running on the server machine. Everything that is needed is right there in my C++ code. This also adds a huge amount of portability to the codebase. Even though I could use something like SQLite etc., my opinion is that if you're going to use an RDBMS, don't half-ass it. That's why I don't use MySQL. (ZING! Let the hate mail fly!)
So far, there are only a couple of advantages I can see of using an RDBMS over XML for this specific task. First, XML is an all or nothing proposition. If you want to get ANY data about an entity, you have to parse the whole file. This is why, for example, I don't store the user's password in the XML file. I don't want to have to parse the whole thing into a CPlayerEntity object just to be able to check if the user has the right password when logging in. The other advantage of the RDBMS is that it makes large-scale changes to your data inherently easier. If, for example, I decided that I wanted all swords to be 1d6 instead of 1d4, I could write one simple query in SQL and update every entity that ever did or ever will exist. To do the same for hundreds of XML files is a far far more complex proposition, and in fact a very compelling reason to use an RDBMS even with all of the other disadvantages, depending on the scale of your project.
So, for this specific problem, I think XML is the way to go. It is just a closer fit to the object-oriented design of my C++ code. Don't get me wrong, I don't think XML files are a replacement for an RDMS. I am not a damn idiot. I would never consider using an XML backend for 99% of web-based business apps, or in fact any business app I have ever written. But for serialization/deserialization of complex, polymorphic C++ objects that do *not* need the rich querying functionality of SQL, XML is the way to go.
Postscriptum
After thinking about it, it shouldn't be too difficult to write a little command-line tool that would allow me to do batch modifications of my XML files when things change. So, if I wanted to change every swordentity to have 1d6 instead of 1d4 I could run something like:
xmlquery --Element=Entity --EntityName=Sword --change Formula=1d6
And this would basically go through every .xml file in the directory, and when it hit an entity element whose entityname was Sword, it would modify the Formula attribute to 1d6...This would address one of the disadvantages to XML vs. SQL.
Permalink to this post.
|
Go to Hardcrawler Page: 1 2 3 4 5 6 7 8 9
|
Philosophy |
My Weight Loss Program |
Computer Stuph |
Misc Stuff |
Dream Page |
My Adventures |
Media Reviews |
Poker |
People |
Hardcrawler |
Toilets |
Gods of F*!@ING Rock! |
Starcraft II |
Video Games |
Random People |
Live Show Reviews |
John's Guide to Being a Metrosexual |
My MAME Project |
The Coolest Men on Earth |
Hottest Hotties of Hollywood |
 My Taiwan Adventure
 My Hong Kong Hijinks
|