sd_Text

by Shadowdarke
Display text on any atom! Includes mobile chat balloons and client.screen (HUD) based text/input boxes.
ID:76576
 
Keywords: icons, libraries

(This review is geared for developers with an intermediate knowledge of DM. If you're new to DM please see Dream Makers for beginner's resources)

A while ago when I was developing in DM, I was looking for a good text on screen library. There are a few out there and one of the libraries I was considering was sd_Text from Shadowdarke. Recently, I decided to check it out again to see how well it worked and how it worked.



Jump to:



Why You Should Use Libraries

One thing you hear constantly in software development education is the value of code reuse or avoiding 'reinventing the wheel'. In fact, a lot of the language conventions we have right now (such as object oriented programming, encapsulation, or really anything to do readability or documentation) have, in part, been put in place to allow programmers to reuse their or someone else's code.

Why does this matter to you or any game on BYOND? Because code reuse means saving time and effort. Why spend days programming your own RPG inventory system when someone has already done it? Why spend hours debugging a random dungeon generator when someone has already done that? Time saved by using libraries and code someone else has created and tested is time you can spend finishing your game.

However, every library was designed (and tested) for specific purposes and these purposes may not be exactly what your looking for. The library could also have limitations that will make it unusable for your project - limitations you may not know about until after you've spent time working it into your game. So how do you know what library is right for your project? You don't - at least until you've explored it. Spend some time reading the library code, playing with the demo, or writing verbs that test each library feature with all the params you might use. Either that or, if you're lucky, someone's written a review of the library that will help you decide.



The sd_Text Library

In this review:

  • I'll briefly talk about some of the features and mechanics SD used in the library,
  • present some positives and negatives about how well it works and the format of the documentation and code,
  • present some fixes for the more outdated and problematic parts,
  • Talk about the example verbs used in the demo,
  • and finally, give some code examples of some other common uses a game might want from the library.

While reading this, if you find yourself wondering whether a tennis ball will actually fit inside a human nose, you can skip to the Summary here



Inside the Library

  • sd_Text and sd_OverlayText

    The workhorses of the library are sd_Text and sd_OverlayText. Although these can be used directly, they're mainly meant (as far as I can tell) to work behind the scenes in procs like ImprovedWordBalloon and the TextWindow datum.

    How they work:

    The main difference between the two is the method used to draw that text - in sd_Text's case characters are drawn using the /image datum - and in sd_OverlayText's case characters are drawn using /overlay. This means that any text drawn with sd_Text will only be visible to the clients that you send to the proc in the 'To' argument (you can send a single /client or a list of /clients). With sd_OverlayText, text is drawn to overlays which are then placed on an atom making the text visible to any client that in view() of the atom to which the overlays are attached.

    Both procs have a loc argument and this requires a bit of explaining as SD made some interesting design choices here. The loc argument can be any atom or atoms that, in the case of sd_Text, could be the loc argument for an /image (see image in the reference) or, in the case of sd_OverlayText, have an overlays list (see overlays in the ref).

    If the loc argument is a single atom, only one icon of text will be drawn on that loc (4 characters - 8 if you use two lines). In order to draw more than one icon's width of text, you'll need to send a list of locs to the procs. For example, in the example code I wrote below for drawing a name over a mob, I had to send a list of images three icons wide in order to get text lines 12 characters long.

    Strangely, if you send a turf as a loc argument, both procs will convert that into a list of turfs (using block) starting on the left/east with the turf you passed in all the way to the farthest right/west turf on the current z level of the map. You can avoid this side effect by sending your own (smaller) block() of turfs as the Loc argument.

    Both procs also have line and offset arguments that control the placement of the text:
    How line and offset place text

    Fairly straightforward stuff, I think. Unfortunately, I think the procs were written when pixel offsets (pixel_x and pixel_y - which both procs used for line/offset placement) were unpredictable. As a result, there's an unnecessary error when offsets are greater than 3 (past the right edge of the icon). Using offsets in large negatives - and large positive or negative line numbers - both work fine, tho allowing you to place text pretty far away from the loc atom.

    sd_Text returns the images the text is drawn on, which is handy because once you get that list back, you can use it to change the position, change the font (I believe), or remove them from the client screen (to 'erase' them). Nice! Unfortunately, sd_OverlayText does not return the text overlays for saving or changing after they're placed. This is one area where the library could have been improved.

    The layers, fonts, and backgrounds used are all settable with arguments to the procs which makes things very customizable. All these arguments are programmed with default values as well, which makes using them easy and quick.

  • sd_MapFrame and sd_OverlayFrame

    sd_MapFrame and sd_OverlayFrame are two other 'inner' procs of the system that deserve mention. They build backgrounds to draw text over using images and overlays respectively. sd_MapFrame is used in sd_WordBalloon to construct it's background, but sd_OverlayFrame doesn't seem to be used anywhere in the system.

    Both of these could be easily adapted to build larger hud elements and on-the-map user interfaces.

  • sd_ImprovedWordBalloon and sd_WordBalloon

    The simplest feature to use 'right out of the box' is sd_ImprovedWordBalloon (which uses sd_OverlayText) and its relative sd_WordBalloon (which uses sd_Text).

    These two procs will display text above an atom enclosed in a comic book style word balloon.

    sd_ImprovedWordBalloon (and sd_TextWindow) from the demo
    They look pretty good and function fairly well both returning datums called sd_BalloonTail (which are held in all /atom under a var called sd_BalloonTails when you use the library). The most impressive use of this is in the demo of the library when you use the fixed() verb.

    These procs will not display all the text sent to them, however - only the first portion. I wrote a (somewhat hacky) version of sd_ImprovedWordBalloon that will display long text lines if you need one.

    sd_BalloonText proc - uses sd_Text - is an older, deprecated version included for compatibility. SD advises you use the two procs above instead.

  • sd_Input

    One of the nice features of the library is Shadowdarke's system for displaying menus on the map. sd_Input uses a sd_TextWindow (below) and the MouseDown proc to allow a client to choose one or more items from a list shown on the client's map screen.

    The sd_Input menu system from the demo

    Unfortunately, when Mouse control procs were changed ~4.0, sd_Input's system for grabbing the icon_y of a mouse click broke. So, you'll get a runtime error clicking one of the sd_Input menus in the demo if you try it as is. You can find a fix for that here.

    If you decide to perform the fix, start the demo and check out the simplemenu, itemmenu, and multimenu verbs that SD created to show how sd_Input works. They're very impressive and are good examples of what you can do with the system.

    NOTE: when using sd_Input, keep in mind that the call to sd_Input won't return and begin executing the code after the call until the user makes a selection from the menu. It's very similar to DM's input in that way and something to keep in mind.

  • sd_TextWindow

    The sd_TextWindow datum is a fairly powerful method for creating something close to the output interface control on the map. Text is displayed on your desired background (with word wrap!) and new lines can be appended at any time, each one displayed underneath the last. When enough text is added that earlier lines can't be displayed inside the height of the box, scroll buttons appear and allow the client to scroll back (or forward) to any line not being displayed.

    A sd_TextWindow with (custom) scroll arrows from the demo
    I do believe you can even display atoms/icons with the text you send, but I haven't fully explored that.

    The window is togglable - you can add or remove it from the map easily and has some automatic sizing on creation that reads the dimensions of the client's map screen (using client.sd_ScreenHeight and client.sd_ScreenWidth - both pretty handy by themselves) in order to bounds check the window placement.



The Good Stuff

  • allows easy use of your own dmi files for the characters drawn - this means you can make your own crazy font and just send it as a argument to most of the procs (eg. ..., charset = 'myFont.dmi', ...)
  • you don't hafta have all characters in font file if you're only using some (ex. the charset icon only has to have the numbers 0-9 if you are displaying only numbers)
  • the DM icon editor lets you easily change color of font (use color replace in colors used) - or possibly runtime recoloring with icon.SwapColor (I haven't tried it)
  • allows easy use of your own 'frame' and 'balloon' dmi files for backgrounds (so word balloons outlines could be customized for various emotions)
  • you can alter pixel_x and y using returned image list from sd_Text
  • the menus and list selector system is pretty nice (although to work right it needs the 4.0 fix for Mouse params in sd_Input), includes a scrollable text window, and allows some customization, too
  • The Docs
    • the documentation is thorough and very good - well formatted in a style similar to the DM reference
    • the use of links is also nice, allowing easy navigation and scanning when your looking for specific info


Drawbacks and Problems

  • (A big one:) sd_Input hasn't been updated to deal with icon_x and icon_y being moved into the params argument. Fixable.
  • fixed width (in other words !, 1, and l all are 8 pixels wide even though they don't need to be) - that's ok, tho, as most video game fonts have traditionally been fixed width.
  • unless you're willing to alter SD's library code, can't change character size from 8x16.
  • 50% dither on background images used in word balloons, frames and text boxes can mangle fonts when displayed. Fixable.
  • sd_Text, OverlayText can display successfully in +/-lines and negative offsets despite what the documentation states. SD programmed offsets over 3 (positive x) to cause errors likely because using large pixel offsets were problematic at one time.
  • some procs fail silently (bail without even a debug message) - not always a bad design choice, tho.
  • sd_OverlayText should return the overlays for direct manipulation
  • magic numbers make comprehension difficult at times
  • usr in proc in several places (as default arguments to procs)
  • commenting and code styles
    • not a lot inside the code - notes to self on some of the more difficult areas. It could use a bit more just to help library users explore what's really happening within the procs.
    • not a lot of whitespace - lots of dense text makes reading/following the procs more difficult than it needs to be
    • SD's mixed use of capitalization and short var names decreases readability as well


Some Fixes

  1. The dither pattern on the original versions of 'balloon50.dmi', 'frame.dmi', and 'window50.dmi' can mangle the fonts used over them. Here are new versions that use a 50% alpha instead of the 50% dither: While we're at it, here are a few alternatives to some of the other icons in the library (feel free to grab and use them):
    • New scroll arrows: newScrollArrows.dmi Used in the screenshot shown for sd_TextWindow
    • The font used in some of the screenshots for this review (I needed an outlined font only because they show up a bit better if you're not using a background): outlinedCharset.dmi
    • A PNG file of that font that you can use as a template for making your own (click the image below to see the png - the thumbnail scaling was harsh):

      (To use it: edit the characters, create a new icon in DreamMaker, uncheck 'Use size from file', and import the PNG file. It'll cut the PNG into 32x32 letters - you'll hafta go into each state and give it the proper name, tho - eg. 'A','a','B','b',...)

  2. If you want to use the menu and list selector systems, you'll hafta fix sd_Input. That means going directly into the code and changing a few lines. Normally I wouldn't recommend this, but I'm not sure SD is around anymore (I'd love to be corrected on this). Until he does update sd_Input, if you're comfortable changing the code, here's the deal:
    The main problem is that sd_Input uses the Mouse proc MouseDown with the pre-4.0 arguments icon_x and icon_y. These pixel positions of where the mouse was clicked (on loc) where replaced with a param string in the form "pixel_x=20;pixel_y=16;..." We're going to add code that extracts those values from the param string and makes sd_Input usable again. So:
    1. Go to the MouseDown proc of sd_WindowPiece in atom/movable (around line 830) of the library and comment out the line:
        //MouseDown(loc,icon_x,icon_y)
      
    2. Underneath that, add the line:
        MouseDown( loc, control, params )
      
    3. After SD inits the vars 'Left' and 'selection', add two lines - like so:
        
      var/atom/movable/sd_WindowPiece/Left = Window.Pieces[Y][1]
      var/selection

      var/list/paramList = params2list( params ) //add this
      var/icon_y = text2num( paramList[ "icon-y" ] ) // and this
    These three steps should get sd_Input and all the menu selection code working again. Try the change and check it using the demo verbs (in test.dm of the library): simplemenu, itemmenu, and multimenu.


The Demo Example Verbs:

There are some great starter examples in the demo (which you can check out by compiling the library by itself and running it), including:

  • frame(): which displays ~105 characters of the text you enter in a frame-like word balloon.
  • fixed(): which displays ~characters of the text you enter in a cartoon word balloon that also will flip and move around the player so it stays on the map.
  • say(): adds the text entered to the output control, a word balloon above the player's mob, and to the text window if it's currently displayed.
  • window(): this toggles a large white frame on the map screen that will display all the 'say' text said in the demo. Each 'say' line displays the text and the name of the client that said it as well as storing each line and allowing the client to click scroll arrows to scroll back and display older lines. Pretty damn nifty.
  • display(): adds text (in this case, using the ledcharset font) to the turf you're standing on. You can adjust the distance (the 'height') of where the text is drawn with the second argument - higher numbers are displayed farther north. display() uses sd_Text which draws on /images - therefore, you can control who sees the text.
  • overlay(): similar to display() but uses overlays (sd_OverlayText) instead of images, so everyone on the map can see the text.
  • simplemenu(): uses sd_Input to display a text menu that you can select an entry from.
  • itemmenu(): shows you how the how sd_input will let you put menus with icons on the map.
  • multimenu(): similar to simple menu, but shows how sd_Input can add a prompt and lets the user slelect or deselect e more than one entry (highlighting each choice).
  • clear(): will clear (most of) the text from the map and your mob. It doesn't, however, clear any text added to turfs with the overlay() verb.


Some More Code Examples

Here's some example code for feature I thought might be commonly wanted.

  • a hud counter: (simplest)


    Long text messages are split into multiple balloons
    The point here was to make a datum that would display a numerical counter for some stat (in my case, I did a hitpoint counter). The code has most of the explanation and use in the comments of the file. It can handle numbers ranging from -999 to 9999. It uses SD's ledcharset icon for the font.

    The code is here.

    Here's the background icon I used for this counter: HPCounterBG.dmi

  • speech bubble (more difficult):

    I was exploring sd_ImprovedSpeechBubble and found that it could only display the first part of the text entered. Having seen Lummox JR's demo for DMIFonts do speech bubbles sequentially displaying each portion of text, I wanted to try something like that here. For example, entering "Oh dear! I seem to have lost my monocle!" would display "Oh dear! I seem to have lost...", wait a bit, and then display "my monocle!"

    Unfortunately, I wanted to do it in a way that wouldn't alter the original proc of the library. Going into the code and having sd_ImprovedSpeechBubble save or return the undisplayed portion of the text would have been the best bet. Instead, I stole the bit of code from the library that breaks the text up and put into my own proc. I called that splitter first, saved the text that wouldn't fit in the first balloon, called sd_ImprovedSpeechBubble on the first portion, and then recursively called the whole proc on the remainder. Not ideal - but it works.
     
    Long text messages are split into multiple balloons

    The code is here.

  • a name overlay for mobs (center justification was a pain)


    Long text messages are split into multiple balloons
    I wanted to make a proc that would draw the names of mobs over them in pretty standard mmorpg style. The hard part was making the name center justified (which is what most of the code in the proc is handling).

    The code is here.

    A couple of things: You can vary MAX_TILES_WIDE from 1 to 4 allowing 8 to 32 characters. It doesn't handle breaking up the text very gracefully but maybe you can improve on that.

    The font used in this and the word balloons is located in the fixes section in part 1.



Performance

Some basic and (realllly) simple testing on my part showed that sd_Text is pretty fast (your mileage may vary):

  • sd_Text: 2/10ths of second to draw the word "blah" 1000 times.
  • sd_OverlayText: 5/10ths of second to draw the word "blah" 1000 times.
  • sd_ImprovedWordBalloon: 7/10ths of second to draw the word balloon containing "blah blah blah" 1000 times.


Alternatives to sd_Text

  • DMIFontsPlus by Lummox JR

    DMIFontsPlus is an excellent alternative to sd_Text. The system is very flexible using variable width fonts, your choice of justification, and excellent word wrap. Documentation for DMIFontsPlus is the gold standard for BYOND libraries.

    Some users may be intimidated by all the choices and options when using it (don't be - they're worth the time to learn) and I've had concerns about speed with the library since icons moved to 32bit. Ultimately, tho, this is your other best bet for screen text on BYOND at the moment (until SD ups the ante).

  • There are other text-on-the-map demos and libraries that I haven't checked out yet: These are worth an exploratory look, too and you'd be encouraged to write a review for any of them and submit it to Dream Makers.


Support and Further Reading

ShadowDarke has a forum (from many moons ago) with a section for all his libraries:
ShadowDarke's Forums

For more general programming help, you can search the Dream Makers by keyword and article:
Dream Makers

and for specific programming questions, try the Dream Makers forums:
How To
Code Problems

More information on some of the DM indluded here, click these:



tl;dr (Summary):

sd_Text is a good library for displaying text on the map screen. Originally designed as a more fully featured version of sd_MapText, the library has a few really useful features like:

Unfortunately, the library is a bit outdated. Currently, the on-map menu system will only work with 4.0 Mouse procs if you're willing to go into the code and make a small edit. Some of the background icons used for the library could be updated too, as their 50% dithers (an older style of transparency) can make Shadowdarke's fonts hard to read. I've included in this review some replacements for these that use the more recent alpha transparencies for the same purpose.

Overall, if you need some simple text drawing capabilities for your game, sd_Text is worth a look and hopefully this review will help you explore it a bit. With a few workarounds and fixes (from ShadowDarke even?), this is a very useful library.

What does (link) mean?

...language conventions we have right now (such as object oriented programming (link), encapsulation (link)...
I think he was supposed to replace them with Wikipedia links on those topics but forgot to.
Wow, that's a great article. Really solid coverage of the library in every way.
What's causing the massive horizontal scrolling?
Foomer wrote:
What's causing the massive horizontal scrolling?

the list of screenshots in the beginning, which is automatically added to reviews
SD is alive and well, just not to terribly active on BYOND these days. I'll work on those fixes. Thanks for the terrific and detailed review!
You saved my life man, thanks for the help! xD