Monday, January 30, 2017

SUBstandard text framework

A TODO list for your features is actually really good tool to keep you focused. Who knew?

A week ago I was messing around with code, adding small things and fixing things that weren't really broken. After making that silly todo list I started cracking on designing the bosses (only on paper so far) and making the logic behind the 80s computer style, letter-by-letter, text printer so I can add more theme to the game through it. Also, because I hate myself, I added text wrapping so I don't have to split the text into multiple strings.

There are some much more sophisticated textbox solutions for Pico-8 here and here, but to work for my game they both would need a lot of tweaking of a code I didn't really understand and I thought it would be a good learning experience to write one from scratch. I managed to squeeze one in under 300 tokens.

The logic is a little janky, but works well enough for what I need. You can get it from the .png to the right, the BBS or check out the source and explanation below, but beware, it is a noobish code...but hey, it works.

Before I started writing this thing, I wasn't even aware of the SUB function. Fortunately, I stumbled upon in thanks to the fact that you can look up others' code and Pico-8 highlights built in functions. Then it was only a short trip to the Pico-8 wiki (which is a great resource btw) to learn what it is about. From that, making the printing letter-by-letter happen was pretty simple by using the SUB function with a simple timer (e.g print(sub(text,1,counter),x,y,col) counter+=1) The tricky part was making the text wrap and not split words and after a lot of trial and error I made a framework that works.

The framework comes in 4 functions. All the tbx_ functions need to be called in their respective Pico-8 functions (_init, _update and _draw) and you call the textbox function whenever you want to start a new text being printed. Because as it is now, the function only allows for one text to be printed at a time. You can change that by expanding on the tbx_lines table, by storing each new piece of text under an index. I didn't really need that for BIT.bash, and I wanted to keep the token count low.

function tbx_init()
 tbx_counter=1
 tbx_width=25 --characters not pixels
 tbx_lines={} --table that stores text in lines
 tbx_cur_line=1 --current line
 tbx_com_line=0 --completed lines
 tbx_text=nil --text to print
 tbx_x=nil --x to where printing starts
 tbx_y=nil --y to where printing starts
end


function tbx_update()
 if tbx_text!=nil then --don't call if there is no text
  --local variables for first and last letter
  local first=nil
  local last=nil
  --create enough rows to fit all the text
  local rows=flr(#tbx_text/tbx_width)+2 
 
 --split text into lines
  for i=1,rows do
   first =first or 1+i*tbx_width-tbx_width
   last = last or i*tbx_width
   
  --cut off incomplete words
  --check if the row's last letter and the one 
  --after that are not spaces
   if sub(tbx_text,last+1,last+1)!="" or sub(tbx_text,last,last)!=" " 
   and sub(tbx_text,last+1,last+1)!=" " then
    --if true then check where the word ends
    --by checking for space
    for j=1,tbx_width/3 do --
     if sub(tbx_text,last-j,last-j)==" " and i<rows then
      --once you find the space, cut off that
      --line at the space by changing last letter
      last=last-j
      --and end the function there
      break
     end
    end
   end
  
  --if first char is a space, remove the space
  if sub(tbx_text,first,first)==" " then
  --if first letter is space then move text
  --one to the left
   tbx_lines[i]=sub(tbx_text,first+1,last)
  else
   --otherwise, print as is.
   tbx_lines[i]=sub(tbx_text,first,last)
  end
   --jump to the next part of text to create 
   --new line by changing first and last vars
   first=last
   last=last+tbx_width
 end
 
 --lines are now made
 
 
 --change lines after finishing a line
 --when the counter is equal to line width
 if tbx_counter%tbx_width==0 and tbx_cur_line<#tbx_lines then
  tbx_com_line+=1
  tbx_cur_line+=1
  tbx_counter=1 --and reset counter  
 end

 --update text counter
 tbx_counter+=1
 --make it skip over spaces
 if (sub(tbx_text,tbx_counter,tbx_counter)=="") tbx_counter+=1
 end
end


function tbx_draw()
 if #tbx_lines>0 then 
  --print current line one char at a time
  print(sub(tbx_lines[tbx_cur_line],1,tbx_counter),tbx_x,tbx_y+tbx_cur_line*6-6,tbx_col)

 
  --print complete lines
  for i=0,tbx_com_line do
   if i>0 then
    print(tbx_lines[i],tbx_x,tbx_y+i*6-6,tbx_col)
   end
  end
 end 
end


function textbox(text,x,y,col)
 --x,y and col are optional
 tbx_init()
 tbx_x=x or 4
 tbx_y=y or 4 
 tbx_col=col or 7
 tbx_text=text
end

The whole thing looks pretty complicated, and I bet could be simplified (especially to remove some of those nested functions of doom), but I don't want to focus on that now. The purpose behind all of this is to finish and release BIT.bash. It already suffers from a serious case of spaghetti code, so I will focus on cleaner code for my next game.

Now it is time to add the rest of the cool text to the game (there will be some after finishing a level and for game over) and then moving to implementing the bosses (of which names and descriptions you can see in the gif above)!

No comments:

Post a Comment