string handling (and streams)

BlitzMax Forums/BlitzMax Programming/string handling (and streams)

blackwater(Posted 2011) [#1]
Hi everyone, hopefully someone can point me in the right direction. I have created a NPC dialog system for displaying text on the screen with multiple choices for the user to click on.

My problem is figuring out how to break up a block of text into multiple lines on the screen so it stays within a dialog graphic. I experimented with the "mid" command but it just takes characters with whatever index you specify so you can't control it breaking a word into 2 parts.

I looked at Streams but when I used the command OpenStream (or ReadStream) on a simple string it returned a null object so I must be doing it wrong?

Can anyone give me any suggestions on this? Basically, read in a block of text and break it into pre-specified line sizes while keeping whole words intact.


Arabia(Posted 2011) [#2]
I was going to try and code something for you as it's an interesting exercise, but in the end I thought it would make my brain hurt too much on a Sunday morning :)

Check out this link - works very nicely and is a done a hell of a lot more efficiently than I would have done it :D

http://www.blitzbasic.com/Community/posts.php?topic=86784#984758


Gabriel(Posted 2011) [#3]
Streams are not even remotely connected to what you're trying to do. They're for reading data in from disk. They're returning null on your strings because your string is supposed to be a filename. I'll grant you the documentation for OpenStream is not the most beginner-friendly, but you can safely ignore streams for what you're trying to achieve.

You're certainly able to do it with Mid though. Of course you can control it breaking a word into 2 parts. You have to write the code to examine the string and determine where the breaks in words are.

Anyway, there are some basic implementations here:

http://www.blitzbasic.com/Community/posts.php?topic=86784

Anything more complex and you might get caught up in the graphics implementation of the function. It should give you a good starting point anyway.


Czar Flavius(Posted 2011) [#4]
You could try using a 3rd party GUI module. A good one will have some kind of multiline text or label you can use, plus it will make other parts of your NPC dialogue easier.


blackwater(Posted 2011) [#5]
Thanks, I was actually pretty hungover when I posted this, I had to laugh that the term "word wrap" never occurred to me when I did some forum searches. I might have to lay off the beer for a while.


Kryzon(Posted 2011) [#6]
I never got drunk on beer, I think it takes too much of an effort.

All hail vodka.

Last edited 2011


blackwater(Posted 2011) [#7]
Ha, ever have Bakon Vodka? Yea it tastes like it sounds...


Shortwind(Posted 2011) [#8]
@Blackwater,

Hey, Since posting the mid centric coding, I've (with help from Jesse) have posted much more robust and less contrived coding to do what you need.

You can look at the forum posts, but here is the last coding I did on this technique. You can easily pull out the relevant routines you need.

Strict
Graphics 1024,768,0,60

Local mywindow:windowbox=WindowBox.Create()

mywindow.setwindowfont("c:\windows\fonts\consola.ttf",22)
'mywindow.setwindowfont("c:\windows\fonts\arial.ttf",22)
mywindow.setpos(150,100)
mywindow.setsize(50,20)
'mywindow.settexture("text_texture.png")

Cls
SetColor 255,255,255
SetBlend AlphaBlend

mywindow.show()
mywindow.CurX=0
mywindow.CurY=0
mywindow.printwrap("{Y}Hello.~n~n")
Flip
Delay(250)

mywindow.printwrap("{W}This is a {Red}test {W}of something.")
Flip
Delay(500)

mywindow.printwrap("And more testing is in order.")
Flip
Delay(500)

mywindow.printwrap("Because the {Y}National {W}concensis is that I'm more {Red}off {W}than more on.")
Flip
Delay(500)

mywindow.printwrap("Even more {Red}unbelievable {W}is that this thing might be working.~n~n")
Flip
Delay(500)

mywindow.printwrap("Here is more testing to see if the testing is testing the testing that is tested.")
Flip


Repeat
Until KeyDown(KEY_ESCAPE) Or AppTerminate()

Type WindowBox
	'Global WindowBoxList:TList = New TList
	Global WinCodes:Codes=New Codes.Create()
		
	Field Font_Name:String
	Field Font_Size:Int
	Field FontMaxWidth:Int
	Field FontMaxHeight:Int
	Field ScreenWidth:Int
	Field ScreenHeight:Int
	Field WindowXPos:Int
	Field WindowYPos:Int
	Field WindowWidth:Int
	Field WindowHeight:Int
	Field TexturePath:String
	Field TextureImage:TImage = New TImage
	Field CurX:Int
	Field CurY:Int
	Field TextLines:TextLine[100]
	Field CurTextLine:Int
	Field CurTopLine:Int
	Field CurRed:Int
	Field CurGreen:Int
	Field CurBlue:Int
	
	Function Create:WindowBox()
		Local w:WindowBox = New WindowBox
		w.SetWindowFont(Null,8)
		w.ScreenWidth=GraphicsWidth()
		w.ScreenHeight=GraphicsHeight()
		w.SetPos(100,100)
		w.SetSize(10,10)
		w.CurX=0
		w.CurY=0
		w.CurRed=255
		w.CurGreen=255
		w.CurBlue=255
		w.CurTextLine=0
		w.CurTopLine=0
		w.TextLines[0]=New TextLine.Create()
'		WindowBoxList.addlast(w:WindowBox)
		Return w:WindowBox
	End Function

	Method SetWindowFont(Font_Namex:String,Font_Sizex:Int)
		Local font:timagefont=LoadImageFont(font_namex,font_sizex,SMOOTHFONT)
		SetImageFont(font)
		Font_Name=Font_Namex
		Font_Size=Font_Sizex
		FontMaxWidth=TextWidth("z")
		FontMaxHeight=TextHeight("z")
		Rem
		FontMaxWidth=TextWidth(Chr(0))
		FontMaxHeight=TextHeight(Chr(0))
		For Local i:Int=65 To 91
			If TextWidth(Chr(i))>FontMaxWidth Then FontMaxWidth=TextWidth(Chr(i))
			If TextHeight(Chr(i))>FontMaxHeight Then FontMaxHeight=TextWidth(Chr(i))
		Next
		End Rem
	End Method
	
	Method SetPos(xpos:Int, ypos:Int)
		WindowXPos=xpos
		windowYPos=ypos
	End Method
	
	Method SetSize(xsize:Int, ysize:Int)
		WindowWidth=xsize
		WindowHeight=ysize
	End Method
	
	Method DrawBorder()
		SetColor 255,255,255
		Local x:Int=WindowXPos
		Local x1:Int=WindowXPos+(WindowWidth*FontMaxWidth)+FontMaxWidth
		Local y:Int=WindowYPos
		Local y1:Int=WindowYPos+(WindowHeight*FontMaxHeight)
		
		DrawLine(x-5,y-5,x1+5,y-5)
		DrawLine(x-5,y-5,x-5,y1+5)
		DrawLine(x1+5,y-5,x1+5,y1+5)
		DrawLine(x-5,y1+5,x1+5,y1+5)
	End Method
			
	Method Show()
		Local x1:Int=(WindowWidth*(FontMaxWidth))
		Local y1:Int=(WindowHeight*(FontMaxHeight))
		DrawBorder()
		'DrawImageRect(TextureImage,WindowXPos,WindowYPos,x1,y1)
	End Method
	
	Method SetTexture(TexturePathx:String)
		TextureImage = LoadImage(TexturePathx)
	End Method

	Method SetDefaultColor(Redx:Int,Greenx:Int,Bluex:Int)
		CurRed=Redx
		CurGreen=Greenx
		CurBlue=Bluex
	End Method
	
	Method PrintWrap(Text:String)
		Local words:String[]
		Local Ret:String[]
		Local soffx:Int=WindowXPos
		Local soffy:Int=WindowYPos
		Local codex:String
		Local wordx:String
		Local kcodevalue:Int
		
		words=Text.split(Chr(32))
		
		For Local t:String=EachIn words
			Ret=t.split(Chr(10))
			t=Left(t,Len(t)-(Len(ret)-1))
			If Left(t,1)="{" Then
				Local y:String[]
				y=t.split("{")
				For Local j:String=EachIn y
					Local tt:Int=Instr(j,"}")
					codex=Left(j,tt-1)
					wordx=Mid(j,tt+1)
					kcodevalue=WinCodes.GetCode(codex)
				Next
			Else
				wordx=t
				codex="None"
				kcodevalue=0
			EndIf
			If wordx.length<WindowWidth-CurX Then
				If CurX=0 Then CurY=CurY+1
					TextLines[CurTextLine].AddWord(wordx,wordx.length,codex,kcodevalue)
				Else
					CurX=0
					CurY=CurY+1
					If CurY > WindowHeight Then
						CurTopLine=CurTopLine+1
					EndIf
					CurTextLine=CurTextLine+1
					TextLines[CurTextLine]=New TextLine.Create()
					TextLines[CurTextLine].AddWord(wordx,wordx.length,codex,kcodevalue)
				End If
				CurX=CurX+wordx.length+1
			
				If CurX > WindowWidth Then
					CurX=0
					CurY=CurY+1
					If CurY > WindowHeight Then
						CurTopLine=CurTopLine+1
					EndIf
					CurTextLine=CurTextLine+1
				EndIf			
			
				If Len(Ret)>1 Then
					For Local i:Int=1 To Len(ret)-1
						CurX=0
						CurY=CurY+1
						If CurY > WindowHeight Then
							CurTopLine=CurTopLine+1
						EndIf
						CurTextLine=CurTextLine+1
						TextLines[CurTextLine]=New TextLine.Create()
						TextLines[CurTextLine].AddWord("~n",-1,"~n",10)
					Next
				EndIf
		Next
		
		SetColor 0,0,0
		DrawRect(WindowXPos,WindowYPos,WindowWidth*FontMaxWidth,(WindowHeight)*FontMaxHeight)
		SetColor CurRed,CurGreen,CurBlue
		Local i:Int=CurTopLine
		Local xpos:Int
		Local ypos:Int
		While (TextLines[i]<>Null) And (i<=(WindowHeight+CurTopLine-1))
			xpos=0
			ypos=(i-CurTopLine)*FontMaxHeight
			For Local j:Words=EachIn TextLines[i].WordList
				If j.thecodevalue<>0 Then
					WinCodes.codefunc[j.thecodevalue]
				EndIf
				DrawText(j.theword,soffx+xpos,soffy+ypos)
				xpos=xpos+((j.thelength+1)*FontMaxWidth)

			Next
		i=i+1
		Wend
	End Method
	 
End Type

Type Words
	Field theword:String
	Field thecode:String
	Field thecodevalue:Int
	Field thelength:Int
	
	Function Create:Words(thewordx:String,thelengthx:Int,thecodex:String,thecodevaluex:Int)
		Local w:Words=New Words
		w.theword=thewordx
		w.thelength=thelengthx
		w.thecode=thecodex
		w.thecodevalue=thecodevaluex
		Return w
	End Function
End Type

Type TextLine
	Field wordlist:TList=CreateList()

	Function Create:TextLine()
		Local w:Textline=New Textline
		Return w
	End Function
	
	Method AddWord(w:String,l:Int,c:String,v:Int)
			ListAddLast(wordlist,words.Create(w,l,c,v))
	End Method

End Type

Type Codes
	Field codefunc()[]
	
	Function Create:Codes()
		Local w:codes=New codes
		w.codefunc=w.codefunc[..255]
		w.codefunc[0]=w.f0
		w.codefunc[1]=w.f1
		w.codefunc[2]=w.f2
		w.codefunc[3]=w.f3
		w.codefunc[4]=w.f4
		w.codefunc[10]=w.f10
		w.codefunc[32]=w.f32
		Return w:Codes
	End Function
	
	Function f0()
		'Print "None"
	End Function
	
	Function f1()
		'Black
		SetColor 0,0,0
	End Function
	
	Function f2()
		'White
		SetColor 255,255,255
	End Function
	
	Function f3()
		'Yellow
		SetColor 255,255,0
	End Function
	
	Function f4()
		'Red
		SetColor 255,0,0
	End Function
	
	Function f10()
		'Print "chr(10)"
	End Function
	
	Function f32()
		'Print "Space"
	End Function
	
	Method GetCode:Int(x:String)
		Select x
		Case "B"; Return 1
		Case "W"; Return 2
		Case "Y"; Return 3
		Case "Red"; Return 4
		Case "~n"; Return 10
		End Select	
	End Method
End Type




blackwater(Posted 2011) [#9]
That's awesome, thanks!


Kryzon(Posted 2011) [#10]
I second his compliment; very nice, thanks for sharing.


Shortwind(Posted 2011) [#11]
blackwater, were you able to make any heads or tails out of my code? A bit over the top for what you were probably looking for....

:)