ID:33582
 

This is the second article in a series. To jump to the other articles, follow these links. [Lesson 1 | Lesson 2 | Lesson 3 | Lesson 4]

Lesson 2

  • Output control
  • Appearance properties
  • Font selection
  • Grid control
  • Grid properties
  • Filling grids
  • winset() and output()

Download project files

So now that you know how to place controls in a window, you've taken the first step toward making a skin file. It's time to play with some settings and see if we can't get this project looking good. Where we last left off, we had a map and an input control. Most BYOND games use an output control for text, too, so let's add that now. Place it to the right of the map somewhere, give it a good position/size, and anchor it to the top right and bottom right. Now go to its properties and check the Default box, like you did with the input and map controls before. All text output that is not specifically sent somewhere else, will go here.

From using older versions of BYOND, you probably already know how to change the style of your text output by using client/script or a .dms file. You can still do that, but why bother when you can change the style for the output control directly? Let's try that now.

Go to the output control's properties and open the Appearance tab. Here you'll see a button that lets you select a font, and buttons for four colors: Foreground, background, link, and visited link. Click the background color button. Instead of a white background, let's say this background should be brown. Once you've clicked the button, a color selection box will come up asking you to make a choice. Let's pick a good brown, not too dark but not too light either, say 96 red, 48 green, 16 blue, or #603010 in HTML format. It's a good start. For a foreground color, click the foreground color button and then pick something light; we'll go with simple white. Now link colors should change too, and since this is an RPG, let's pick something green for a woodsy feel. For regular links, bright green (0 red and blue, 255 green) should do, and for visited links, a bit of a darker shade (192 green) will work great.

Next, let's play with the font. Of course picking something too fancy might not be a good idea, but as long as it's easily readable, we have a lot of freedom. Click the font button. The font selection box should open up. The first thing you'll see is that you have a list on the left side, which is empty. This will be the list of fonts we'll try to use, in order. Yes, I said try—remember, the font you pick might not be installed on somebody else's machine. You have a list of choices for fallbacks, so the game will still look good anyway. In Runt I favored Tempus Sans ITC, so let's pick that first. Go over to the dropdown combo box on the right. You can either open it up and find the font you want in that list, or click in the edit box and start typing. Just typing "temp" will probably be enough to bring up the rest of the font name, assuming you have it yourself. (If you don't, go download it. It's a great font! I'll never understand why Microsoft makes a font standard on one installation and then yanks it from the next, but then if I had a buck for every stupid decision Microsoft ever made, Bill Gates would be my pool guy.)

Click the left arrow button to add Tempus Sans ITC to your list of choices. This is now your first choice. Go up to the box that lets you pick the font's size, and change it from 0 (just using a default size) to 9. 9 point Tempus should look pretty good. But we need a fallback. Go back to the font selection box and type in Tahoma, a good plain text font. Click the left arrow again to add it. For safety, let's add a couple more. Add Comic Sans MS, Verdana, Arial, and then finally just for safety type in sans-serif, which should be near the top of the dropdown list, and add that. That's our absolute last-ditch backup choice, which will pull a default sans-serif font off the system for the player to use.

Now our font list has six choices: Tempus Sans ITC, Tahoma, Comic Sans MS, Verdana, Arial, and sans-serif. But wait, why is Tahoma, a plain font, above Comic Sans MS which is probably the best second choice? Click on Comic Sans MS in the list to select it. Now hit the up arrow, which will move it up. It is now the second choice. (In the list you can select more than one item with Ctrl+click or Shift+click, if you want to move multiple choices up or down at the same time.) And now that I think about it, we don't really need Verdana as a backup. Click Verdana and then click the right arrow button, which will remove it from the list.

All right, the font choices look good, so let's save them. Hit OK. Hit OK again to get out of the output control's properties.

Now it's time to make a radical design decision. We've got map, input, and output controls. There's nothing yet on the left side of the window. Should we put an info control there for statpanels? Maybe, if we want to use them at all. This is BYOND 4.0! There's no need to rely on statpanels to display incidental things like equipment and inventory lists. In fact now we can display both at once! So let's add a grid. In fact, let's add two grids.

Click the grid button in the editor panel, and click in the upper left part of the window. Then add another grid right below it. To position these nicely, select the top left grid and then Ctrl+click to select the output control as well. Use the Align top edges layout command. Now select the bottom left grid, and Ctrl+click the output control again, and Align bottom edges. Finally, click one grid and Ctrl+click the other. Align left edges, Align right edges, Join top and bottom edges, and then last of all, click the layout button that says Distribute height evenly.

Now go to the grids' properties. Before we do anything else, name the top grid "inventory" and the bottom one "equipment". To make sure these controls behave nicely on resize, we'll give them special anchors. The inventory grid will use top left as one anchor, and for the second anchor select Other from the list; type in 0 for X, 50 for Y. The equipment grid will use the same 0,50 as its first anchor, but bottom left for the second anchor. Now try resizing the window a bit to see how everything looks.

For each of the grids, it'd be nice if they looked like our output control. So go to their properties, the Appearance tab, and give them the same colors that the output control had. Also give them the very same font choices. It's important for this to look consistent. You'll see that grids also have a line color option. Click the line color button, and instead of gray we'll choose a darker shade of the same brown we used earlier. Use 72 red, 36 green, 12 blue for the line color.

Now here's where the fun will start. For the inventory grid, go to its properties and find the Options tab. Click the checkbox that says Is a flexible list of items. Also click Small icons. Change the Show lines box to None. For the equipment grid, check the Small icons box, and change Show lines to Horizontal. Then, give the equipment grid 2 columns.

Since we've spent all this time working out what to put into the interface, it's only fair to take a quick side trip into how we'll actually use it in the game. Create a new .dm file called grids.dm and add this code:

mob/proc/UpdateInventory()
var/items = 0
for(var/obj/O in src)
if(equipment && O.slot && equipment[O.slot]==O)
continue // don't show equipped items
winset(src, "inventory", "current-cell=[++items]")
src << output(O, "inventory")
winset(src, "inventory", "cells=[items]") // cut off any remaining cells

mob/proc/UpdateEquipment()
var/items = 0
for(var/slot in equipment)
winset(src, "equipment", "current-cell=1,[++items]")
src << output(slot, "equipment")
winset(src, "equipment", "current-cell=2,[items]")
src << output(equipment[slot], "equipment")
winset(src, "equipment", "cells=2x[items]") // cut off any remaining rows

This is assuming your equipment system is set up like so:

mob
var/list/equipment

obj
var/slot // equipment slot

Any time your player's contents or equipment change, call the appropriate UpdateInventory() or UpdateEquipment() routines. While this doesn't update on its own like stat() does, it does give you a lot more flexibility in how you display everything.

In the code above you'll see there are two new procs, winset() and output(). The output() proc exists to send any text/HTML/file output to a specific control. In this case, text is being sent to the current cell in one of the grids. winset() changes parameters for the controls. It takes three arguments: The player whose skin will be used, the name of the control, and the parameters to set. If the name of the control is unique, you don't need to be any more specific, but if we had two grids named inventory in different windows, we'd need to use mainwindow.inventory as the name, in order to tell them apart.

That's about it for this stage, but as always I'll leave you with some challenges. When you feel like it's time to move on to the next thing, move on to lesson 3.

Exercises:

  1. Try using a font that isn't installed on your system, and put it at the top of the font list. When you view the control in your game, the next backup choice will appear instead. But if someone else has that font, they'll see it!
  2. If your screen shows a gap between the grids/output and the map, change that now. Make the grids touch the left side of the map and input controls, and make the output control touch them on the right. Play around with the sizing so the map is exactly 50% of the total width, and the grids and output are all 25%.
  3. After doing the last exercise, change the anchors like so:

    map: 25,0 and 75,100
    input: 25,100 and 75,100
    inventory: Top Left and 25,50
    equipment: 0,50 and 25,100
    output: 75,0 and Bottom Right

    Notice how everything sticks together in the right proportions as you resize the window.

  4. Test out the grids by creating some verbs to pick up and drop items, and equip or unequip them. Remember to call UpdateInventory() and UpdateEquipment() as needed. The project files for this lesson show an example of how to do this.
step 2 helped alot :D. I was confused with grids. I've been using output but wow, didnt know it could be THAT useful.
Well Tac, there's a pretty steep learning curve to skins. It threw off a lot of our first beta testers, and these tutorials haven't even scratched the surface yet of what you can do. Lesson 3 goes into panes, and that's where skins get really interesting.
Nice guide; exactly what I needed to finish my interfce.
For Grids, the is-list setting, is that strictly so that the grid resizes on it's own? Or do Grids actually support somehow displaying a full list (as stat(usr.contents) would, for example).
And I STILL can't get grids to work.

I wish someone could explain the syntax of the proc you use to place each item in the grid, as the built-in DM guide(Help on..) doesn't explain it well.
is-list has nothing to do with a list(); grids don't auto-display anything like stat() does. All it means is that the items you add fill in the rows with as many cells as possible per row; the number of columns and rows resizes automatically to fit your items. It's good for an inventory list, but as you see I did not ues it for the equipment list, because equipment uses a 2-column format.
*Drops to knees chanting: "We're not worthy...We're not worthy"*

Now that that's out of my system...Thanks alot for taking the time to do this :) I had a basic grip before, having not worked much with interfaces yet. Now though, I feel I have a confident enough grasp on how it works that I'm going to fiddle around a bit. Keep up the good work ^.^
"No use Comic Sans as font. Ungh."

-- Data
couldent you just make stat() call proc/UpdateEquipment() (or inventory) for you? it'd be a lot simpler than making it be called constantly in a (for lack of a better word) never ending loop.
stat() IS a never ending loop.
yes, i never said it wasent. but i meant its better than making your own(by recalling it over and over) as opposed to updateing it only when you get a new object added to the inventory.

*edit* actually i'm not even sure myself why i asked this because its really simple to update it when you get a new object but i thought it'd be convenient to just auto update.
Small typo, Is a flexible list of items, entries not items. Just trying to help :)
After using this "set current-cell, then output into this cell" method for awhile, I have to say that it was a lot more intuitive when I learned I could just go:

winset(src, "inventory", "cells=[myColumns]x[myRows]")
src << output(whateverImOutputting,"inventory:[columnToOutputTo],[r owToOutputTo]")

Though this seems to need to have the "flexible list of entries" unchecked to work, there doesn't seem to be be any situations it can't adapt to. (I suspect the "flexible list of entries" function is actually supposed to point to a list variable, which would be awesome.)

I'm just saying that proved to be a pretty important omission to an introductory guide. ;) (But then, maybe that syntax wasn't available when this was written.)