BigAtom

by Shadowdarke
Easy and flexible multi-tile atoms
ID:34695
 

Developers want bigger atoms in their games. Not just bigger icons which can easily be done with overlays, but large fully functional atoms that can bump into one another and interact with one another properly. Just as importantly, developers want to be able to expand their existing games to take advantage of bigger atoms without having to redesign the entire thing from the ground up. That's what the BigAtom library (http://developer.byond.com/hub/Shadowdarke/BigAtom) is all about.

If you're designing a brand new game, BigAtom can handle that too. However when you're working from the ground up, you can plan ahead for big atoms and make optimizations specific to your world that a general purpose library like BigAtom can not anticipate. If you haven't begun your game yet (or aren't too far into it to consider a rewrite), you might want to look into some of the other resources available. For example, the G-Move library (http://developer.byond.com/hub/Gakumerasara/G-Move) by Gakumerasara goes a step beyond BigAtom and includes pathfinding for oversized atoms.

This article focuses specifically on plugging BigAtom into an existing project. The BigAtom documentation explains everything about the library in detail, for those of you who want more information. If that doesn't solve your problems, post details about whatever is troubling you on my forum (http://shadowdarke.byondhome.com/forum/forum.cgi?forum=3) and I'll try to help.

Getting Started

To walk you through adding BigAtom to a project, we need a common starting point. Since nearly every BYOND developer has worked through Zilal's Beginner Tutorial at one point or another, I'm going to use it as our "existing game." If you don't happen to have your ZBT Testworld available, you might want to head over to http://zilal.byondhome.com/tutorials/zbt.html and take a few minutes to put it together. Compile it and make sure it is running properly before you move on.

Alright we have an existing game, now how do we use BigAtom to make gargantuan bugs to squish, colossal players, a huge heaps of gold for the spoils?

  1. Download the BigAtom library from http://developer.byond.com/hub/Shadowdarke/BigAtom.
  2. Open the game (Testworld in our example) in Dream Maker.
  3. Click on + sign by "Lib" in the file tab to open the list of libraries you've installed on your computer.
  4. Check the box by Shadowdarke.BigAtom.
  5. Compile.

Tada! You now have the BigAtom library in your project. It will automatically turn movable atoms with large icons into multi-tile atoms. Unfortunately, we don't have any large icons yet.

Making Big Icons

We'll make three new icons: "bigbug.dmi", "biggold.dmi" and "bigperson.dmi". It is best to make sure our dimensions are evenly divisible by 32. We don't have to make them evenly divisible, but internally BYOND will chop it into 32x32 chunks and any leftovers will get their own 32x32 pixel space anyway. The BigAtom library does not support partial tiles.

Dream Maker doesn't directly support making icons that are larger than 32x32 pixels. We'll have to use an external program for that. I use Paint Shop Pro 8 from Jasc Software. (Corel absorbed Jasc and has released newer versions of PSP, but version 8 has everything I need.) Many BYOND developers swear by the GIMP available for free from http://www.gimp.org/. If you have a large pile of money just waiting to be spent, you could go for the big guns and get Adobe Photoshop. (Better yet, you could get the GIMP and send your large pile of money to me!) Whatever editor you prefer is fine, as long as it edits PNG files.

Nadrew's article "How to use BYOND's new 'Big Icon' support" (http://members.byond.com/DreamMakers?command=view_post&post=34006) explains how to make large icons using a 3rd party program. If you need help making large icons, you should read this article. I try to avoid posting redundant material here.

Let's make our big bug truly monstrous, 256 pixels wide and 256 pixels high. That will be 8 x 8 tiles. Our person will be a relatively small 64x96, two tiles wide and three tiles high. Our new gold pile will only be 64x32, wide but short. You can easily make the bug and gold icons following the instructions in Nadrew's article.

If you followed ZBT to the letter, your 'person.dmi' will have three icon states. The easiest way to get these icon states is to just use your graphics editor to make three separate 64x96 pngs. I named them "bigneuter.png", "bigmale.png", and "bigfemale.png". Now return to Dream Maker and open the neuter png. Make sure the "read only" checkbox is unchecked, so that we can modify it, and save the file as 'bigperson.dmi'. Double click on the space immediately below the icon and name the icon state "neuter". You can add new states, modify the state names, even add new animated movie states with numerous directions and frames; they will all have the 64x96 size that we started with

Now create a new pixmap by hitting ctrl+P or clicking the icon that looks like an artist's palette with a paintbrush through it. Name this state "male", then double click on it. You should see what looks like a movie icon with one direction and one frame. A single grayish icon hangs alone in the white box. Select that icon by clicking on it. Go to the "Graphic" menu and select "Import..." (or just hit ctrl+I if you're a keyboard shortcut lover like I am). Find your male 64x96 png and import it. If the Import command is grayed out, you haven't selected the icon yet. Click on it and try again. Import the female state the same way that you imported the male one.

Now we just have to change the program to use our new icons.

Change the lines:

icon = 'person.dmi' icon = 'bug.dmi' icon = 'gold.dmi'

To

icon = 'bigperson.dmi' icon = 'bigbug.dmi' icon = 'biggold.dmi'

Compile and run. Our bugs have automatically become big.

You may notice that bugs which are too close together or too near the edge of the map might only look like a single chunk of bug. You'll have to reposition and/or resize your map to fix those bugs. The BigAtom library won't move them, because it's not quite sure where you might want them.

Handling BigAtom Vars

When you run TestWorld, you'll also probably notice that your person is still only 32x32. That's because the default "" icon_state doesn't exist so the BigAtom library assumes it's a single tile atom. When you login Testworld directly sets the icon_state to one matching the gender. The library can't react to a direct assignment, so it provides some procs you can use to allow the library to automatically adjust to things. One of the most important procs for this is the bigatom_Var() proc that belongs to all movable atoms.

The format of bigatom_Var() is:

bigatom_Var(var_name, new_value)

The bigatom_Var() proc sets the atom var with the specified var_name to new_value for every part.

When we are changing any vars that have anything to do with appearance such as icon, icon_state, text, name, invisibility, etc., we need to use the movable atom bigatom_Var() proc. If you change the icon or icon_state, it will even resize the BigAtom if it needs to and make sure that every part of the BigAtom reflects the changes.

Let's change our Login() proc to change the icon_state properly:

Login() bigatom_Var("icon_state",gender) //when a player logs in, get them the right icon state for ..() //the gender of their key. Then call the parent!

Compile and run. You've gained 3 times the height and twice the girth!

Anatomy of a BigAtom

BigAtoms are actually a collection of single 32x32 atoms. One of these atoms is considered to be the one in charge. We'll call this source atom "bigatom_src", since that's the var the library uses to identify it. In the case of player mobs, the client is attached to the bigatom_src. BigAtom attempts to put the bigatom_src near the center of the BigAtom for a number of reasons. All the other atoms in the BigAtom are there to help with the display and to help your game deal with density, opacity, and targeting issues.

Most of the vars for a BigAtom will be handled by the bigatom_src. That way you only have to worry about these vars in one place, without bogging down the game server with looping through every part of a BigAtom every time a single bit of data changes. You shouldn't use bigatom_Var() for these vars. In Testworld, mob hp and wealth vars and the gold's amount var only need to be tracked by the bigatom_src.

The BigAtom library tells all these parts to work together, but your game's custom procs and verbs think that each one is a unique mob or obj and will treat them as such unless you tell it differently.

Getting the Correct Target

If you try to play Testworld as it is now, you'll run into a few problems.

  1. You can't attack bugs or get gold unless you are standing in just the right spot.
  2. You can attack parts of yourself.
  3. If you do manage to attack a bug or get gold, you only effect one tile of it.

This is because you're targeting parts of a BigAtom, when you actually want to interact with the whole BigAtom through its bigatom_src. BigAtom can't handle this stuff automatically, because the game uses custom code to make them happen. You'll have to modify the code just a little for it to work as it used to. As you can probably guess, the BigAtom library has a handy proc to help with this situation as well. This proc is called bigatom_ValidTarget().

The format is:

bigatom_ValidTarget(atom/target, atom/relative_to = usr, range = 1, flags = BIGATOM_TARGET_NOT_SELF)

This proc is used to get and verify the correct target for verbs and other actions, since verb src settings and some other methods of target verification don't work well with BigAtoms.

Args:

target
the atom to be verified.
relative_to
the atom relative to which the target will be verified.
DEFAULT: usr
range
minimum range allowed between the edges of relative_to atom and target.
If range is less than 0 then range is not verified.
DEFAULT: 1
flags
Optional flags for target validation.
Currently the only flag available is BIGATOM_TARGET_NOT_SELF. If this flag is set, then do not allow an atom to target itself. Just set flags to 0 if you want to be able to target yourself.
DEFAULT: BIGATOM_TARGET_NOT_SELF

Returns: The correct target, or null if the target is invalid.

First we'll work on the attack() verb. Change the beginning of the verb to the following:

attack(mob/M as mob in oview(2)) M = bigatom_ValidTarget(M) if(!M) return //invalid target if (M.HP <= 0) //if M's HP are at or below 0... // The rest of the verb can be used exactly as it is.

I'll explain the differences line by line.

attack(mob/M as mob in oview(2))

We had to change the oview() distance to 2, so that the target could be any mob within 2 tiles of the usr. Remember that the usr is really only a single tile near the center of our 2x3 tile mob. The furthest tile in our BigAtom will be 1 tile distant and we want to be able to target mobs up to one tile beyond that.

M = bigatom_ValidTarget(M)

If the target (M) is a BigAtom, this will switch the target to it bigatom_src. It will then verify if part of the target is actually within one tile of the edge of the mob. This particular bigatom_ValidTarget() call is using all the default values for bigatom_ValidTarget().

if(!M) return // invalid target

If bigatom_ValidTarget returns null, then the original M wasn't a valid target, so we should abort the proc.

The rest of the attack verb will work fine the way it is.

There is one annoyance that I don't have a good solution for. The attack verb is always visible, and when you stand next to a bug and try to attack it, you'll see your name as a potential target. The client still thinks you can target your sub-parts, so it throws them in the list of verb arguments, even though the server will reject them in the beginning of the verb call.

There is one alternative that removes your parts from the list, but it has its own problems as well. You can change the attack verb to not use arguments at all, then use an input() to select the target. The problems are that the verb will still always be visible, you can no longer access the verb through another mob's right click menu, and you can't macro a specific target to the command. If those problems annoy you less than seeing yourself in the options, here's an example of how to do it:

attack() var/mob/M = input("Select a target") as null|mob in oview(2) - bigatom_parts M = bigatom_ValidTarget(M) if(!M) return //invalid target // The rest of the verb can be used exactly as it is.

Now for the get() verb. It works pretty much the same way:

get() //obj/gold/verb/get() set src in view(2) //src must be close src = bigatom_ValidTarget(src) if(!src) return usr << "You pick up [amount] gold." usr.wealth += amount //add to usr.wealth del(src) //delete the gold

These are pretty much the same changes that we did for the attack, except that we had to modify the src setting distance, instead of the distance in the argument.

Is it safe to change src in the middle of a proc? Well... it is if you know exactly what you're doing. The BigAtom library makes each part the exact same type as bigatom_src, so the new value of src will have all the same vars and procs available to it that the old src had. Don't try this at home, kids.

Any verb or proc (including things like Click()) in your game will need to verify the target in the same way.

Too Much Gold

There's another problem with our BigAtom Testworld. When you kill a bug you end up with 64 gold piles (assuming you went for the 256x256 pixel bugs like I did). If you placed bugs on your map after changing them to use 'bigbug.dmi', you'll also get these big squares of gold on boot up.

This is because the ZBT overrides the mob/Del() proc to do something special when the mob is deleted. It's convenient with single tile bugs, but we don't want to drop a gold every time a sub part of a bug is deleted. Let's fix the Del() proc:

Del() if(bigatom_src == src) // if this is the bigatom_src of the mob, place gold var/obj/gold/G = new(loc) //create a new obj from the gold blueprint G.amount = rand(1,100) //set its amount variable randomly ..() //call the parent

Alright! The project is now full of BigAtoms and works as it was intended.

The Big Flick Off

We're not quite done. ZBT is a very basic game framework and hardly represents all the flash that you probably want to do in your games.

One of the most basic things you'll want to do is flick icons or icon states. One good example would be to flick an "attack" state whenever the mob attacks another. Let's add a new "attack" icon_state in 'bigpersom.dmi'. Now we'll add this line to the beginning of the attack() proc:

flick("attack",src)

Since I'm writing a section about this, you probably suspect that this is another instance where you need a special proc. Your suspicions are right. If you compile and run it with that flick() call, you'll see a single part of your mob flick a miniature "attack" state. That just won't do.

Our special proc is bigatom_flick(). It works just like flick(), but compensates for BigAtoms. Change the flick line to:

bigatom_flick("attack", src)

Now when you compile and run, you can see gorgeous (or not so gorgeous) animated 64x96 players slaughter giant vermin. Terrific!

Built In Movement

The BigAtom library automates everything it can with regards to movement, however there are many built in procs in the DM language that simply will not work properly with the BigAtom library. These are:

step_away(Ref,Trg,Max) step_rand(Ref) step_to(Ref,Trg,Min) step_towards(Ref,Trg) walk(Ref,Dir,Lag) * walk_away(Ref,Trg,Max,Lag) walk_rand(Ref,Lag) walk_to(Ref,Trg,Min,Lag) walk_towards(Ref,Trg,Lag)

Each of these procs has a BigAtom equivelent. Just put the bigatom_ prefix on the proc call and it will work in basically the same way. For example,

walk_to(src, my_target, 2, 3)

would become

bigatom_walk_to(src, my_target, 2, 3)

* Technically, walk() works with bigatoms but the bigatom walk actions operate in a different way than regular walk actions, so in order to clear bigatom walk actions you have to use bigatom_walk(atom, 0).

Let's go ahead and make the bugs in ZBT wander around the map. We'll expand the declaration of bug:

bug //new prototype icon = 'bigbug.dmi' //override the parent's icon, which was 'person.dmi' New() ..() if(bigatom_src == src) // if this is the bug's src bigatom_walk_rand(src, 10) // take a random step every second

Where Do I Go From Here?

Now you have all the basics you need to place BigAtom into your own games, but this is just a sample of what it is capable of. Some advanced features include overlay mode big atoms that you can use to decrease atom counts without compromising display, as well as built in methods to alter the layer and/or density of parts depending on where they are in the big atom. I plan to cover these topics in future articles, so keeps your eyes on the Dream Makers guild. If you crave more now and can't wait for detailed articles, the library documentation explains how everything works but may be a little difficult to understand.

If you have any questions, comments, or bug reports for the BigAtom library please make a post on my forum at http://shadowdarke.byondhome.com/forum/forum.cgi?forum=3.

Now you're ready. Go forth and multiply the size of your atoms.

Splendid! Thanks, SD!
Amazing! I always did it a more CPU-hogging way, this will come in handy.
cool
When you set "top density" to 0, your bottom left tile's density is also set to 0.
What's the largest canvas size the library supports?