27 May 2015

Deep Thots Bout Vidya Games: Dynamic Room Detection

WARNING: ESOTERIC, NON-LANGUAGE/PLATFORM-SPECIFIC GEEKERY AHEAD

So, it's no secret that I like to design games. Finishing those designs is a different story, but I do enjoy the process, and I'm always super excited when I start a new project.

I'm thinking right now of dynamic room detection in tile-based games. Sometimes, you want to do something to a "room", rather than to individual tiles. How do you get the game to recognize what is a room, and what's not?

I have semi-dynamic room detection in Ion Trail. As part of ship design, you place room control nodes in the upper left corner of the room, and it detects the dimensions of the room, and tells the tiles which room they're a part of. It has to be a rectangular room, but it makes layout much easier.

But what if you didn't have to do that? What if you wanted non-rectangular rooms? What if you wanted something where you could change the layout in-play? That's what I want to discuss here.

Now, the system I have for Ion Trail, the pre-placed Room Control Node (RCN) upon starting the game immediately tags the tile it's on, and then uses that tile to start a cascade process of pinging the other tiles. It cascades down and right, which is why the RCN has to be in the upper-left node. It prevents any sort of duplicate processes gumming up the works. The tiles identify adjacency, and whether the adjacent tiles are across a wall. If they're adjacent and not across the wall, they name themselves part of the triggering RCN's room, and return data which is used to calculate the height and width. It's a pretty elegant system, if I have to say so myself.

But it's utterly inflexible. While I could add the ability to place the RCN in-game, it would still require proper placement, and rectangular rooms.

Now, I've been playing Prison Architect lately, and they've got a neat solution; manual zoning. You create zones, which are also rectangular. The zones have the ability to detect adjacent zones, and consider themselves part of the same zone, which allows for non-rectangular zones in play. They can detect if the tile beneath them is valid (indoor outdoor) and whether or not specific items are within their boundaries. It's elegant, but not without issues of its own. Forget to manually designate a zone, and no matter how many showers and drains you've got, the game will completely ignore it. This is a feature as well as a drawback though, as it can allow you to dezone cells and cause prisoners to move to another part of the prison, and similar operations.

But I still want something more. The zoning is nice, but not universally applicable. I just want something that tells the game, and the player, that this is A ROOM.

I think the solution lies in the tiles themselves. Each tile is an object, and thus should have a unique identifier. These identifiers can be sequenced, which can allow for a hierarchy. This'll be important in a minute.

So, okay. How do tiles determine if they're part of a room? First off, I want to differentiate tiles from empty space. Empty space is still potentially part of the playable space, but is irrelevant for room detection. So, there's some triggering event. Maybe the creation of a tile, maybe periodically during the game, whenever. It triggers on a tile. That tile then looks in all directions for other tiles. If it finds a wall, it will ignore tiles on the other side of that wall. If it finds a tile that's not walled off, it activates the search process for that tile. If the tile is already searching, it will not try to reactivate the process. The initial tile will set it's object ID (oID) as the RCN. Then, whenever two tiles that are actively searching talk, they'll compare their oID to the current RCN. If a new tile's oID is lower than the current RCN, then the RCN will be changed to the lower (older) oID.

So there's this cascading process. Eventually, you'll have all of the tiles in a given area (walled off) in "search mode". How do they know when they're done? Maybe once a tile has activated search, it'll drop into a wait mode; during wait mode, the tile will compare its FCN value to the FCN value of it's FCN, which will keep updates moving toward the oldest value. When there are no more tiles in search mode and they're all in wait mode, they should all be pointing to the oldest oID object as the RCN. Here's the rub. They're all waiting, nothing is going on as the RCN isn't updating... But each individual tile only knows it's own state. The RCN doesn't actually know which tiles are part of the room, though the RCN and all of the other tiles know who's in charge.

Does the RCN keep some sort of matrix, to keep track of all of its tiles, and their states? This seems problematic, as each tile will have to have the potential to keep this matrix... and when does the matrix get updated? When the room search is complete? We still don't know how to tell the RCN that it's done. The matrix could tell it that, but that means that there'd end up being multiple matrices being generated during the room detection, which would mean that, when each tile updates its RCN, the matrices would have to be merged. It'd be unwieldy fast.

So, okay, back up. Maybe the RCN doesn't know which tiles are which? Say there's a value on the RCN called v_roomstat. A tile will look at this value, and compare it to its own status; If the tile's status is searching, and v_roomstat = searching, it'll do nothing. If they don't match, it'll update that status to match it's own. Which means every tile that is waiting will try to tell the RCN that the room is waiting, and every tile that is searching will try to tell the RCN that the room is searching. Eventually, they'll all agree, and the value will stop being changed. When the value has been "waiting" for a designated period, the RCN will know that no more tiles are searching.

This is problematic only in as much as there might be an issue with multiple change operations being pushed simultaneously. That'll depend on the platform that the program is running on. Since this is still a thought experiment, we'll assume it's not a problem right now, and move forward.

So, we've got a blind RCN and a bunch of tiles that know who the RCN is. This is somewhat useful, because a thing can happen to/on a tile, and the tile will be able to communicate to the RCN to tell what happened. But how do we do things to the room as a whole? That's the primary purpose. We can call on ALL tiles, and pass the RCN as a parameter, so only the tiles that point to that RCN will actually run the function, but I think that would use a bunch of resources unnecessarily, even if it's only for a blip of time, as each tile was like "Who, me? Oh, never mind." Maybe we go back to that matrix idea, again, except we only really need the oIDs of the tiles, so an array should work fine. So, the RCN, once all of the room tiles are done searching, turns them all off (so status is now idle) and now generates the matrix. We'll probably have to do the all call at this point, but only the one time per room detection, not every time we want to do something to the room.

But still, how do we populate the array? An array is a list of values. You access the items in the array by calling the array and the index of the item. Depending on the platform, you can possibly just create a dynamic array, and pop each tile onto the end of it as it reports in, but I don't think this solution is ideal. In Ion Trail, it was easily done because the RCN could just call an itemAtPosition function incrementing the x and y values through a a pair of for-loops, and pop them into place on a pre-generated array, since it already knew the number of tiles in the room. As this never changed, It was done once at the beginning of the game, and never again. Obviously that solution won't work here, since we've got to allow for non-rectangular rooms, and an unpredictable number of tiles.

I think it might be worthwhile to use a linked list, instead of an array, since it allows for dynamic changes a lot better. The RCN would be the head of the linked list. We already know it's the oldest tile in the room, so we can use the oIDs as a list, starting with the oID. Any older tiles would simply not be called. Incrementing the oID call, look for the RCN value. If the RCN value matches the room, then pop that tile onto the end of the linked list. Then, when you want to do something in the room, the RCN calls the function, then sends the order down the linked list, until it hits the tail, and terminates.

I think that's workable. I want to go home and try it now... I guess I'll let you know how it goes.

05 May 2015

MMO Design Noodling, part 2: Living Economy and Ecology

Read Part 1...

So, this is going to be based in some thoughts I had many years ago, but I'm going to try to incorporate some more recent reading in my thoughts as I go.

Star Wars Galaxies was probably the first time I really thought about it, but what strikes me as the biggest problem with MMO economies is that they're completely open. Money (and in some, equipment/materials) flows into the system infinitely. Theoretically it also flows out of the system in the form of money sinks, crafting, breakage/loss, but in practice, the influx is always much, much faster than the outflux. The reason for this is players. Players will find ways to maximize gain and minimize loss. They'll hoard money and equipment, and learn to not lose equipment that isn't easily replaced (such as valuable weapons/armor/etc). The result is inflation on a massive scale. Certain base-level items will always be available for cheap in most MMOs, because the game ensures that NPC merchants will sell stuff that new players can afford. But once you start wanting to get past the basics, you usually need to start dealing with the player-based economy, whether it be through auction houses, 1-on-1 trading, player merchants, or whatever, because you can't buy the top of the line equipment from NPCs; It has to be quested for or crafted by players.

For instance, in Ultima Online, you could purchase a house for a relatively small sum. But if you wanted to actually put it somewhere, you were generally out of luck, unless you happened to find a decaying house and camp the spot until it fell down, or using player griefing to keep others from refreshing their house. Eventually, if you wanted a house, you'd have to but a pre-placed one from someone else, which meant that the demand was massive, the supply was completely fixed, and as the amount of money that was static in the system increased, the prices soared to exorbitant levels. I'm not going to lie; When Age of Shadows came out, promising empty land for housing, I spent $100 of real world money to buy 5M gold and bought several housing deeds, which I distributed to my trusted lieutenants. It was worthwhile for me to spend real money to get extremely difficult to acquire in-game assets. Though I stopped playing the game a year or so after that, I don't regret the purchase, despite the TOS violation.

So how do you fix this? My thoughts were to have a semi-closed economy. Resources like gold and materials would flow into and out of the player system, but there'd be a finite amount of those resources. Once a resource in the "pool" got low, it would flow into the system more slowly, until eventually it was depleted, or it was refreshed. Money is the easiest resource to demonstrate this.

So, if an NPC pays you to escort him safely to a town, that money enters the system from the pool. If you stick that money in a vault somewhere, it becomes static, and doesn't exit the system. If you pay for a service from an NPC, then the money exits the system, goes back into the pool, and can be put back into the system from another point of access, like maybe a dragon's treasure trove. If players start to hoard large amounts of gold, then NPCs will begin to have less to offer. Eventually, the money may become mostly static, until something changes, such as a raid on a rich player's holdings, or perhaps that player, hearing of such a raid, hires a force of NPC guardsmen to protect their vault.

One thing that I've considered is that as the player-base grows, then necessarily money will grow thinner, even without deliberate abuse. That's why I'm thinking of a semi-closed system. As the player base grows, the pool will grow to accommodate. There may also be a sort of trickle effect, perhaps to reflect the mining of precious metals and the minting of coin.

There are obviously ways this could be abused, which I'm still considering. I'd probably need to consult with people who've made a "career" of exploiting game mechanics to really cover my bases, as I've usually been one to play within the rules as much as possible.

So, how does all of this relate to a living ecology? Well, the ecology is a big source of the materials needed to build and play within the world. If you chop down a tree, you get wood which you can use to build houses, furniture, carts, weapons, and various other pieces of equipment. But you also have a tree stump, and one less tree. The normal system employed by MMOs is to have resource points which can never be depleted, or only temporarily. Trees which can be chopped for wood, and an hour later are refreshed so can be chopped for more wood. Mining nodes which yield 2-5 units of ore, every hour or so. This contributes to the same inflation problem you see with money. The system's normal way of dealing with this is to make the equipment you can make fairly low in value, until you've invested a massive amount of time building a few dozen swords, or what have you.

My thoughts on this are to make the endeavors take longer, but produce more. You take 10 minutes to chop down a tree, it's gone. But you get a whole tree's worth of wood. You spend an hour mining, you're going to get a lot of ore that you can then smelt... If you make a sword, you're not going to bang one out every 5 seconds, but when you've made one, you've also made some significant strides forward in your knowledge, and the sword won't sell for less than the cost of a meal. So there will still be the crafting grind, but in the end it'll take a similar amount of time, but a lot less individual items, and the associated equipment.

And the tree you chopped down? Plant a sapling, and it'll grow back in a comparably accelerated time (it's not going to take several real days of work to create a sword, but it will probably take several real hours) If you don't replant, then somewhere in a deep forest far away, another tree may begin to grow, but that will take significantly longer.

Extending this past wood and ore, things like climate, weather, seasons will have an effect on the recovery and growth of animals and plants. If you kill a lot of wolves, the rabbit and deer populations may grow, and conversely reduce the local plantlife. If you kill lots of deer and rabbits, the wolf populations may move away, die off, or start attacking human settlements. If you cut down a lot of trees, the wildlife in that area will become more scarce. If you never re-use the metal from old swords to make plowshares, you'll have to delve deeper and deeper into the earth to find more ore.

In the end, what I'm looking for is a world where player decisions matter. Players being selfish will eventually create a world bereft of natural resources. Players being good stewards of their lands, and learning to reduce, reuse and recycle will find themselves with more natural resources. Eventually, you may find some players at odds with others over how to treat the land. Eco-terrorists living in the deep woods, declaring themselves as defenders of the wilds, preying on the industrialists who send in their logging teams and their mining operations. In a more balanced place, industrialists will reuse materials, replant the forests amd promote animal husbandry.

I know that UO and SWG both tried something similar, and ran into issues. There was some suggestion that some of the issues were related to hardware limitations, but I doubt that was all of it. What I'm proposing is a bloody complicated system, with lots of small interactions that could eventually snowball into larger, unexpected problems.

So now that I've rambled at length, I'm turning the floor over to you. What problems do you see with the sort of system I've proposed? What solutions do you think might work? How would you address the problems I'm trying to address here?