Script for printing text one letter at a time?

Ask and answer questions about making games and related topics. Unrelated topics go in that other forum.

Moderators: marionline, SDHawk

Post Reply
User avatar
Foxley
Metal Slime
Posts: 832
Joined: Sat Nov 09, 2013 5:54 pm

Script for printing text one letter at a time?

Post by Foxley »

I'm pretty sure I've seen this done in other games, so it must be doable. Basically, in the style of Zelda 1 and 2, and Castlevania 2, each string character shows one at a time.

Does anyone have any recommendations on how to implement this sort of thing?
User avatar
Gizmog
Metal King Slime
Posts: 2622
Joined: Tue Feb 19, 2008 5:41 am

Post by Gizmog »

This problem is very programmery and organizational, so I'm sure someone else is way more qualified to handle the answer than me, but there's some basic pointers I can give.

The most important issue at the start, is how are you going to be storing your text? Zelda 1 seemed to do it on a room by room basis. It only cleared the string when a new one was drawn or when you left the room. In a game like that, you can be sloppy and just have scripts that are like..

Code: Select all

#Dragons Room
Light Torches
Draw Dragon
String1="I am a dragon, who are you?"
FancyTextyStringyStuff (String1)
You could do the same for Ninja Gaiden or Megaman or anything that did cutscenes that way, assuming there's not too many of 'em. Zelda 2 and Castlevania 2 were more RPG-like, and having so much text it might be obnoxious to try to store all the text in the Hss (But not impossible!) If that were the case, you might try to keep the text in the default textbox editor, but access it with something the Textboxline command like this

Code: Select all

Have the NPC pass the textbox he wants to show as an argument
plotscript,StealTextboxText,WhatTextbox,begin

TextboxLine (1,WhatTextbox,0)
TextboxLine (2,WhatTextbox,1)
TextboxLine (3,WhatTextbox,2)
SO ON

End
What these scripts are going to have in common is a way to send a certain letter from a source to a destination.

Code: Select all

script,DeliverLetter,SourceString,WhatLetter,DestinationString,begin
variable (LetterToSend)

LetterToSend := AsciiFromString (SourceString,WhatLetter)
AppendASCII (DestinationString,LetterToSend)

end
And a means to call that script many times. You could use a for loop if you wanted, but a while loop would give you more opportunities to let the player fast-forward or skip the textbox, add multiple letters (or even multiple lines) at a time for the exact effect you wanted.

I suppose if you wanted, you could just write the desired text on screen (as a string or a textslice) and then cover it with a rectangle or container slice and shrink/expand the "cover" to gradually reveal the text, but it might look weird? Again, I'm not the best person to answer this.
User avatar
Foxley
Metal Slime
Posts: 832
Joined: Sat Nov 09, 2013 5:54 pm

Post by Foxley »

That last idea sounds hacky and totally awesome. I could even just do that in the slice collection editor and run a script to free the obscuring slice children one at a time, with a pause. If there isn't a more efficient way to do it entirely via scripting, I'll totally give it a try.
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

Do you want a regular textbox to appear one letter at a time? Well you can always have text with no box, so I'll assume "yes". I know that Giz did it before script commands to manipulate the textbox existed, but doing it now is pretty easy actually. I'm surprised there was no script for it on the wiki yet.

Something like (untested!)

Code: Select all

define constant(99, temp str)

# Shorten a string if it's longer than 'length'
script, trim string to length, str, length, begin
  variable(i)
  for(i, string length(str), length + 1, -1) do (delete char(str, i))
end

# This script is also the equivalent of "wait for textbox"
script, textbox letterwise reveal, letters per tick = 2, begin
  variable(boxnum, txtsl, length)
  txtsl := lookup slice(sl:textbox text)
  if (txtsl == false) then (exit script)
  boxnum := current textbox
  while(boxnum == current textbox) do (
    get slice text(temp str, txtsl)
    trim string to length(temp str, length)
    set slice text(txtsl, temp str)
    length += letters per tick  # keeps increasing to infinity, doesn't matter
    wait(1)
  )
end
To use:

Code: Select all

show textbox(1)
textbox letterwise reveal   # Optionally specify a speed argument
Note that the engine's builtin line-by-line fade in will still happen, but if this script works you shouldn't notice, unless maybe you have several blank/short lines at the start so that the script gets ahead of the builtin scroll-in. Despite the builtin effect, the textbox slice always contains the full text; a different mechanism is use to cause only part of the text to display, something that's currently invisible to scripts.

You should make sure the "showtextbox happens immediately" backcompat bitset is on; otherwise you would have to wait(1) before calling the script.
Last edited by TMC on Thu Nov 24, 2016 12:11 pm, edited 2 times in total.
User avatar
Gizmog
Metal King Slime
Posts: 2622
Joined: Tue Feb 19, 2008 5:41 am

Post by Gizmog »

TMC wrote:I know that Giz did it before script commands to manipulate the textbox existed, but doing it now is pretty easy actually. I'm surprised there was no script for it on the wiki yet.
Yeah, I deliberately didn't bring that up because it was a shitshow. Every textbox in the game was stored in a single string, and when I used a textbox, it'd skip two hundred characters into that string (I had 5 rows of 40) times the argument of the NPC.

The downside of this was that one string had to be METICULOUSLY formatted. If you fix a typo halfway through, you have to make sure you didn't add or subtract any characters, because that would throw off every other box in the game. Not a good way to do business! You can see how goofy it looked here (Why Shiz decided to use this particular textbox in the gamelisting, I'll never know)

Image

The only piece of code I like (and mind you I still had to clean it up a ton, I was a moron 8 years ago) was the one that made text-y sounds.

Code: Select all

#What letter is the last ascii added to the string.

 
script,NoisyText,WhatLetter,begin
#Make no noise for space.

Switch (WhatLetter)
do (
	
	#No sound for a space

	#If the case doesn't do anything, it gets smushed in with the next one. Usually I have a script that's like "DoNothing" for situations like this, but why muddy the waters?
	
	Case (32) 
	SystemYear


	#Vowels play Sound 0
	#Upper and Lower case covered
	Case (97)
	 playsound (0,false,true)
	  


	Case (65)
	playsound (0,false,true)
	  

	Case (101) 
	playsound (0,false,true)

	Case (69)
    playsound (0,false,true)

	Case (105)
   playsound (0,false,true)
	
	Case (73)
	 playsound (0,false,true)

	Case (111)
	 playsound (0,false,true)

	Case (79)
	 playsound (0,false,true)

	Case (117)
	 playsound (0,false,true)

	Case (85)
	 playsound (0,false,true)

	 
	#Anything else plays Sound 1, to generate some contrast
	Case (Else)
	playsound (1,false,true)
	 
	)
end

# My script worked differently than TMC's so I'm not exactly sure how I'd want to trigger it from his. Maybe slip it in between like

#trim string to length(temp str, length)
#NoisyText (ASCIIFromString (TempStr,Length)) 
#set slice text(txtsl, temp str)

#In that while loop

We got a Holiday called Thanksgiving over here I gotta go to, so this script is kinda rushed and hacked, just in case you'd want sounds with your thingy. TMC, you always do such a good job of having something efficient and awesome for people who're asking. We're all thankful for you, and you should lobby your local government to honor Thanksgiving so you can get recognition from your countrymen too. [/code]
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

Oh, I never noticed that vowels played a different sound effect (that's something really hard to notice), though I think I do vaguely remember that you used more than one sound effect, which gives a nice effect.

Giz's script is pretty easy to insert into mine, just add a line like:

Code: Select all

if &#40;string length&#40;temp str&#41; <= length&#41; then &#40;NoisyText&#40;ascii from string&#40;temp str, length&#41;&#41;&#41;
after the "get slice text". That'll be silent if it hits a space even it it revealed several other characters the same tick.

(Hah, SystemYear. The way to have a case do nothing is to just write "do()" after it.)
Post Reply