ID:116448
 
I'm still hoping to get a new gameplay video posted tomorrow night. Before working on the video there were a few things I wanted to get done code-wise. The first thing was cleaning up the code that manages boss fights. While working on this I found a handy way to use one of my libraries, so I figured I'd write a blog post about it:

Introduction

A lot of DM programmers don't like using libraries and I don't blame them - BYOND has a lot of libraries that are poorly written and a lot of well-written ones that perform useless tasks. This post is meant to show how handy the right library can be.

In Tiny Heroes I had already implemented one boss fight and it didn't take a whole lot of code, but it wasn't generic. I'd have to write just as much code again to add another boss fight. Eventually this would become too difficult to manage. I wanted to find a way to easily handle:

1. Starting boss fights. This includes tracking the status of the fight so that it can't start a second time once it's already in progress (but if you die, it resets, and can be started again). If the fight has been completed successfully it won't be started again.

2. Victory and defeat conditions. The game needs to be able to determine when the fight is over, and it if ended favorably.

The Problem

I wanted to create some type of object that would handle this. My first idea was some type of global variable that's an instance of a datum, one instance per boss fight. The problem is that boss fights are not just collections of vars, they are connected to maps. The boss fight object needs to know these two things:

1. What starts the fight. Usually, it'll be "when a player steps on this tile, the fight starts."

2. What turfs are part of the boss's room. There can be multiple players participating in the fight, so to check when the players have been defeated it'll need to check if there are no living players in the boss's room.

The problem is that these areas will overlap because the place you step to start the fight is inside of the boss's room. This means that we can't use /area objects to define these regions (because each turf can only be assigned to a single area). Luckily, there's a library to make this possible!

The Solution

The Region library gives us a new kind of object called /region. It's a type of atom so we can place it on the map. We create two regions, one for the boss's room and one for the trigger. When you run the game, the /region objects figure out what turfs also contain /region objects of the same type. When you enter a turf (and the turf's Entered() proc is called), the Region library figures out if you're entering a new region (it can figure this out because it knows what regions* each turf is in). This way we can use it to detect when you've entered a turf that triggers the boss fight.

* This is the key. "regions" there is plural because if you place multiple /region objects on the same turf, that turf will be inside multiple regions. You can't do this with areas.

All /region objects of a type are deleted except for one and the remaining one is removed from the map. The library does this automatically so that the region objects don't interfere with your game. If you did "for(var/obj/o in view())", you wouldn't want to iterate over a bunch of region objects. This has an added benefit because this is exactly what we want to happen for boss fights - we want a single, global instance of the boss fight object to store information about the fight (is it in progress? have you already won the fight? etc.)

In Tiny Heroes, I defined two types of regions:

region
boss_fight
boss_start


For each fight you'd define a new child type of each of those two types. The /boss_fight region stores all of the information about the fight and is responsible for the logic. The /boss_start object is just responsible for detecting when the fight should start. When a player enters a /boss_start type of region, the corresponding /boss_fight object is found and told to start (these objects correspond by having the same name, ex: /boss_fight/boss_01 and /boss_start/boss_01).

Those objects have a basic default implementation that handles starting the fight (and making sure it's only started once), checking for victory/defeat conditions, and resetting the fight when you lose. With this generic stuff handled for all fights, this is all it takes to make a custom fight:

region/boss_fight
dungeon_01_boss_01
start()
// ..() will return zero if the fight has already been started
if(..())

// close the doorway
turn_on(/area/bridge/dungeon_01/boss_doorway)

// center each player's camera
center_camera()

// spawn the boss
spawn(20 * world.tick_lag)
boss = new /mob/enemy/boss_01(locate(56, 21, 7))

// reset is called when all of the players died.
reset()
// ..() will return 1 if we should reset the fight
if(..())

// open the doorway
turn_off(/area/bridge/dungeon_01/boss_doorway)

// delete the boss
del boss

// The defeat and victory procs are named from the player's
// perspective - victory() is called when the player wins and
// defeat() is called when all players in the battle have died.
defeat()
world << "defeat!"
..()

victory()
world << "victory!"
..()

// This doesn't need any code, the default behavior of it's
// Entered proc handles everything as long as it has the same
// name as the /boss_fight one (dungeon_01_boss_01)
region/boss_start
dungeon_01_boss_01


The /boss_fight/dungeon_01_boss_01 object just has to define the new parts of the behavior. Most of the behavior is handled by the default implementation of each proc so we create a custom boss fight with very little code.

Conclusion

BYOND has lots of libraries that cover lots of topics. When you need to do something, most of the libraries will be useless to you because they're on a different topic. This leads to some interesting points:

1. There are lots of libraries you'll never use because you don't know that they exist. There are so many resources that you can't keep track of them all. If you don't use a resource often you won't remember it exists. There are so many junk resources on the hub that you can't simply look through the whole hub to find resources that would be potentially useful. Even if you could, you wouldn't remember most of them. What would help is...

2. People blogging about libraries they use. You'll never hear about the obscure library that you'd never need to use or the ones that are terribly written - people won't blog about those. If you read a blog post like the one you're currently reading, the Region library is more likely to stick in your head than if you stumbled across the library on your own. The blog posts give you a reason to use the library so you can say "yes, that sounds useful" instead of coming across the library with a clear mind and having to figure out "would this ever be useful?"

3. If you're going to make a resource (library, demo, etc.), take some time to find a topic that'll be useful. In three and a half years only 59 people have downloaded pif_ComplexNumbers. People seem to get a kick out of making things like this, but these creations aren't very useful to BYOND game developers.

As a comparison, Lummox JR has 18 hub entries on the developer page. These resources have a total of 24,075 downloads (an average of ~1337 downloads per resource) and they average ~12 combined downloads per day. Popisfizzy has 11 hub entries on the developer page. These resources have a total of 786 downloads (~71 DLs per resource) and have averaged a combined 0.79 downloads per day.

If you're going to make a resource, make something useful. It doesn't have to be monumental. My region library is very simple, 94 lines of code, much of which is whitespace and comments, but it's useful and has gotten over 200 downloads.

4. These points are surprisingly similar to the problems with BYOND game development - people want to make a game (or library) but don't have a good idea for one, so they end up making something terrible that nobody will ever enjoy (or use). The hub gets full of crappy games (or libraries) so if there's an old, undiscovered gem you'd never find it. People should just focus on making games (or libraries) that are fun (or useful). By blogging about how we develop games (or use libraries) we can identify better ways to use libraries (or develop games). When you start identifying dualities, I think that means it's time for bed.

Edit:
Update

I didn't have time to make a video (I never found my mic anyway), but I did take some new screenshots. I added backgrounds. Hopefully these will give the environments a little more detail and character:




Compare the last one to this screenshot that was taken before backgrounds were added.

The drop shadows look weird on the background because they backgrounds are supposed to appear distant. I'm not sure what I'll do about this, but overall I think it's an improvement. If nothing else, the backgrounds scrolling at a different speed than the rest of the image will make things more interesting.
The problem is that these areas will overlap because the place you step to start the fight is inside of the boss's room. This means that we can't use /area objects to define these regions (because each turf can only be assigned to a single area). Luckily, there's a library to make this possible!

Untrue.
Vermolius wrote:
The problem is that these areas will overlap because the place you step to start the fight is inside of the boss's room. This means that we can't use /area objects to define these regions (because each turf can only be assigned to a single area). Luckily, there's a library to make this possible!

Untrue.

Can you give me an example of how to have a turf that's in two areas?
Example. Yes?
Vermolius wrote:
Example. Yes?

I meant that it's impossible to place a turf inside of two areas. Obviously it's possible to write code that emulates having a turf inside two areas, that's exactly what the Region library does!

The region library is also a better example because you can define the regions by placing objects in the map editor, just like you'd place objects to define an area.
Libraries are made for un-experianced people. Verm's used his past experiance to program a workaround. Samething...?
Eh, I just skimmed his post is all. Wasting peoples' time, basically. :P
Avainer1 wrote:
Libraries are made for un-experianced people.

Huh? I'll give you one chance to back up this statement, good luck!

Verm's used his past experiance to program a workaround. Samething...?

Right, but he made it sound like there was a non-workaround way to do it. I knew it could be done with a workaround because I had already written one

Let's change the topic? How's the weather where you live?
Forum_account wrote:
Avainer1 wrote:
Libraries are made for un-experianced people.

Huh? I'll give you one chance to back up this statement, good luck!

Verm's used his past experiance to program a workaround. Samething...?

Right, but he made it sound like there was a non-workaround way to do it. I knew it could be done with a workaround because I had already written one

Let's change the topic? How's the weather where you live?

H.O.T. o.o
Oh now I remember Verm... He tought me how to code a bit.. then I was trying to rush myself to learn at high speed. He stopped teaching me, so I went and read guide twice till I learned how to program. :/
Ok, I don't have a video posted but I got a lot of housecleaning done. Actual house cleaning, I vacuumed the whole place and cleaned the bathroom too!

I added backgrounds to the game. The graphics are meant to look simple, but they're a little too simple. The quarter-sized tiles and ambient objects (tables, chairs, bushes, rocks, etc.) help to make the maps more detailed, but it still needs something more. Hopefully the backgrounds will help out with this. I added some screenshots to the blog post.

That bubble is nice. Can you tell me the font you use in it or create library showing how to add the text to it?
The font is Trebuchet MS, size 8pt. I'm not sure if I'll turn it into a library, the code for the speech bubbles is a bit of a mess.

I also just posted an update for the Region library. The library defined the "regions" var for turfs and initialized it to list(). Now, the library defines the var but leaves it null, it only becomes a list when necessary (if the turf is in a region).

In Tiny Heroes, this change made Dream Seeker use 66,304 K of memory instead of 71,616, a savings of 5312 K.
Why not figure out DmiFontsPlus? It's a cool library, and libraries are awesome.
I'm so excited to play this game,i can hardly wait @_@!
Kaiochao wrote:
Why not figure out DmiFontsPlus? It's a cool library, and libraries are awesome.

It hasn't been updated in a while. Based on the icons, it doesn't look like you can make taller speech bubbles. Also, I'm not sure how easily it would support certain effects (ex: make the letters appear one at a time).
Yut Put wrote:
This game has been in progress for a long time. Progress is probably slow and/or you have a tight time schedule. When can we expect a playable demo/open version?

I have been busy and any release schedule I set will be arbitrary and I will probably not stick to the schedule. The project is a hobby of mine so it ends up being a fairly low priority (it's higher priority than cleaning the house, but lower priority than work and vacation).

That being said, maybe I'll have something playable in 4-6 weeks. In the last 4-6 weeks I haven't had much time to work on the project. I'm hoping that the coming 4-6 weeks won't go the same way and I'll be able to make more progress. With just a little polish, what I have currently made would take about 30 minutes to complete. I'm hoping to get that up to an hour (with multiple character classes there's some replay value too).