ID:34226
 
Another BYONDscape Classic!

by Zilal

Welcome back. Today I'll show you one way to represent numbers in the map window, like I do in my game BYOND Backgammon.

What do we need to do this? An icon that has a state for every digit, for one. That's 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10. For Backgammon, I opened up MS Paint and used the text tool to write the numbers in a font I liked at the size I wanted. Then I cut and pasted them into the icon maker, a separate state for each. (Click on the button that looks like a palette to make a new icon state, if you don't know already. Even if you do know already, you still have to click that button.) Click below each and enter the name for each icon state: its number. 0, 1, 2, 3 and so on again.

Now we have icon states of each digit and can refer to them. So obviously whatever number we want to display on the map will have to be broken up into its separate digits, so we can find the right icon states. And we have to figure out how many digits there are in the first place. And we have to find the right places on the map and put them there!

Let's start with that last problem: the right places on the map. Familiar with the tag variable? That's a variable of all atoms you can assign a text value to. The catch is that the text value must be unique. And unique means atoms can be easily located by tag.

Decide on the maximum number of digits you'll allow to be displayed. If you'll be displaying player scores in a game where there's no limit how high the score can get, you should probably go one, maybe two digits beyond the highest you expect players to score. It's better to have an extra spot and not need it than to need it and not have it.

Next decide which turfs you'd like to display the number over. Open your map, and right-click on the beginning display turf, the one furthest left. Choose "[/turf] >" and then "Edit...". Now you're in the instance editor, where you can edit the specific instances of your prototypes. In the tag field, put "digit_1" between the quotes, and click Okay. Then choose the turf immediately to the right, and make its tag "digit_2". Continue until you've altered as many turfs as you need.

Now we can easily find our digit-displaying turfs when we need them. So let's get going with a proc to handle the display!



mob/proc/Display()
    var/scoretext = num2text(score)
    var/list/digitlist = new
    do
        digitlist += copytext(scoretext,1,2)
        scoretext = copytext(scoretext,2,0)

    while (scoretext)
    var/counter
    for (var/v in digitlist)
        counter++
        var/turf/T = locate("digit_[counter]")
        T.overlays.len = 0
        T.overlays += 'digits.dmi'
        T.icon_state = v

Shall we go through that line by line to see what it does?

mob/proc/Display()

I made this a mob proc so that it would be easy to display a particular mob's score. You could call it whenever the score changes. If you wanted to display a number that doesn't pertain to any particular mob, you'd make it a regular old proc.

    var/scoretext = num2text(score)

Here we turn the number (mob.score, in this case) into a text string so we can easily manipulate it.

    var/list/digitlist = new

We make a list in which to store each of the number's digits in text form.

    do
        digitlist += copytext(scoretext,1,2)
        scoretext = copytext(scoretext,2,0)

This adds the first character of scoretext to the list, and changes scoretext so that first character is now gone...

    while (scoretext)

...as long as scoretext has any length. When nothing is left of it, the loop stops.

    var/counter
    for (var/v in digitlist)
        counter++

Now we loop through the digits in the list, keeping track of which one we're on with the counter variable.

var/turf/T = locate("digit_[counter]")

We find the turf whose tag is "digit_1" (and so on, as the loop continues).

        T.overlays.len = 0
        T.overlays += 'digits.dmi'
        T.icon_state = v

We remove any overlays from the turf, in case this isn't the first time a score is being displayed. Then we add a digits icon to the turf's overlays, and make its icon state the digit we got out of the list. The loop continues until all the digits in the list are done. Voila!

This proc will work pretty well if you only try to display things you're supposed to -- that is, numbers that fit on the turfs allotted. But in a situation like this, a good programmer would put in some checks to deal with the unexpected. What if the score is a number too big for the tagged turfs?

If you only tagged 5 turfs but the number contains 6 digits, the line where we locate the right turf will return null, and you'll get an error when you try to change null.overlays. To prevent that, simply put:


if (!T) return 0

below the var/turf/T line. That means the proc will simply stop when you run out of space.

In another unexpected event -- the event that the player's score is, weirdly, a text string instead of a number -- num2text(score) won't cause an error, but will return the value 0. That's okay.

Now, you're ready to display numbers galore on your maps -- and words, too, if you're clever.

Z's Peeves

Score in games should be meaningful. I have upheld this belief with varying success in my own games. Failing Sheep II early often results in a higher score than making it to level 20. Nobody ever seems to run out of money in Sixes. The +100 bonus in Runica for filling the screen is impossible to achieve with even an iota of lag. And Sheep III... too traumatic to even talk about.

Some common problems:

- Players may increase score indefinitely simply by playing as long (or as many times) as they're able
- Score increases at such a steep curve that points earned in the first levels make no difference later on
- Similarly, a finishing bonus is so large as to render all the work in getting there largely unimportant
- High score allows access to game advantages at no cost, leading to a "rich get richer" syndrome

And of course, points earned should be appropriate to how difficult/desirable the task is. If you don't want PvP in your games, handing out points for killing people is an obvious no-no.

I've had to make some scoring changes over the years in my games. In Sheep II multiplayer, I made one's own sheep worth more than other people's sheep, to shift some emphasis from screwing other players to saving yourself. In Lexiconomy I borrowed a couple necessary scoring strategies from Acrophobia: players get a point if they vote for whoever ends up winning the round (discouraging voting for crappy definitions some), they can't score if they don't vote (or nobody would ever vote!) and the fastest answer only gets credit for being fastest if it receives a vote (otherwise people would just type "afg" to be fastest).

The right scoring system not only makes a game more fun, but it can encourage your players to behave in certain ways.

2 points to everyone for reading this Z-Tip.

Nice article, though I'm a little curious as to why the program part was split into two loops; it seems like everything that happened in the for() loop could have been handled in the do/while() loop, eliminating the list part entirely, simply by defining var/counter before the do/while() and manipulating the result of copytext(scoretext,1,2). Following is an example of what I'm saying, basically a copy&paste of the given example, and a bit of reordering.

var/counter
do
var/v = copytext(scoretext,1,2)
scoretext = copytext(scoretext,2,0)
counter++
var/turf/T = locate("digit_[counter]")
T.overlays.len = 0
T.overlays += 'digits.dmi'
T.icon_state = v
while(scoretext)