Author Topic: Dynamic Message Box Creation  (Read 9912 times)

eric22222

  • Fanatic
  • ***
  • Posts: 177
    • View Profile
    • Eric Online
    • Email
Dynamic Message Box Creation
« on: 2006-11-29, 09:16:59 PM »
We finally got out of the physics testing stage and got into the real making of the game! We're anticipating a lot of dialogue, like, the same NPCs say different things at different points in the game. I figure it'd be much easier to create the special functions dynamically. I'd like to have one special function per NPC to raise an event. I'll test some value "progress" in script and create and immediately trigger a message box function.

I saw in an earlier thread (and in the GoldYoink code) how to create a function for changing maps, but I'll hazard a guess that message boxes are a little different. So, um... how exactly do I do that?

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Dynamic Message Box Creation
« Reply #1 on: 2006-11-30, 06:06:22 AM »
So you know how to create a SpecialFunction object by looking at GoldYoink code and just need to know how to make it a message function?  I think I can help there.  I'll just look at the GameDev source code to see what it sets when creating a message function.

Heh -- it appears to be very simple: the only thing unique to a message function is the ".Value" property which contains the message.  All the other properties are set by common code that set properties for all special functions (FuncType, Flags, Flags2, InvUseCount).  Oh, and the FuncType value for a message function would be SPECIAL_MESSAGE (or simply the value 1 if you have trouble using the named constant).

Does that help?

eric22222

  • Fanatic
  • ***
  • Posts: 177
    • View Profile
    • Eric Online
    • Email
Re: Dynamic Message Box Creation
« Reply #2 on: 2006-11-30, 08:27:57 AM »
I think that's all I need to know...

But just to be clear on syntax, I'd do something like

Code: [Select]
"Some message." = MessageFunction.Value
Right? Like, that's the correct way to use quotation marks?

durnurd

  • Lead Lemming
  • Expert
  • Fanatic
  • *****
  • Posts: 1234
  • Games completed so far: 0
    • MSN Messenger - durnurd@hotmail.com
    • View Profile
    • Find My Ed
Re: Dynamic Message Box Creation
« Reply #3 on: 2006-11-30, 09:18:22 AM »
Technically it would be the other way around:

Code: [Select]
MessageFunction.Value = "Some message."
But the quotations are correct, yes.  And if you want to concatenate several strings together, you simply put plus signs between them.  For example:

Code: [Select]
dim playerName
playerName = "Fred"
'...Other code
MessageFunction.Value = "Oh, " + playerName + ".  I see you've come back."

Which would show a message saying Oh, Fred.  I see you've come back.  This is useful for using variable strings in messages, where the player, for example, can specify their name.  You could also use this to display an amount of inventory, such as amount of gold you have, or something, within a string.
Edward Dassmesser

eric22222

  • Fanatic
  • ***
  • Posts: 177
    • View Profile
    • Eric Online
    • Email
Re: Dynamic Message Box Creation
« Reply #4 on: 2006-11-30, 12:16:53 PM »
Now that sounds cool. I'm gonna have to make use of that somehow. Thanks!

eric22222

  • Fanatic
  • ***
  • Posts: 177
    • View Profile
    • Eric Online
    • Email
Re: Dynamic Message Box Creation
« Reply #5 on: 2006-12-03, 02:29:47 PM »
I've started working on the script for the message boxes and had an idea to make things much easier. This was my first semester after changing to CS, so I've only recently learned about strings. Anyway, I have a small box around each NPC as a special function activated with shift. I have each one raise an event. I want to all the generic message stuff at once and just tailor the message. Example...

Code: [Select]
If func.name="NPC_001" or func.name="NPC_002" or... then
   Set to global, remove after use, etc.
   If func.name="NPC_001" Then Message Value= "Here's a message"
   If func.name="NPC_002" Then Message Value= "Here's another"
   Etc.
End If

However, this will require me to list EVERY NPC in the whole game that gets a message. Anyway, we recently discussed string comparison in class, so I got to thinking. Could I replace that first line with something like:

Code: [Select]
If func.name>"NPC_000" and func.name<"NPC_999"
Would that work in VB? If not, feel free to offer another way around this. Or if this way of doing things could be replaced with something more efficient, I'd be up for that, too.

durnurd

  • Lead Lemming
  • Expert
  • Fanatic
  • *****
  • Posts: 1234
  • Games completed so far: 0
    • MSN Messenger - durnurd@hotmail.com
    • View Profile
    • Find My Ed
Re: Dynamic Message Box Creation
« Reply #6 on: 2006-12-03, 03:16:41 PM »
That would work, yes.  However, this does of course restrict you to 998 messages throughout the game.  Here's a way I would do this:

Code: [Select]
if left(func.name,4) = "NPC_" then
   dim strTmp
   strTmp = mid(func.name,5)
   select case strTmp
      case 1
         Message = "Here's a message"
      case 2
         Message = "Here's another"
   end select
end if

This allows for functions to be named NPC_1, NPC_2, etc. or whatever you want, really.  You could call them NPC_Message and just use
Code: [Select]
      case "Message"
but that's getting a little bit more confusing, since you're using an untyped variable and checking for multiple datatypes.  If you just stick to numbers, this works well.  Also, using a select case statement is faster, I think, than using a multiple-alternative if statement.  This may not be true in the case of VBS due to the lack of variable types.  In any case, it's easier to make another message.
Edward Dassmesser

eric22222

  • Fanatic
  • ***
  • Posts: 177
    • View Profile
    • Eric Online
    • Email
Re: Dynamic Message Box Creation
« Reply #7 on: 2006-12-03, 03:30:43 PM »
 :o

*stares in amazement*

I could I use that "left" syntax on things like sprite identification, right? Like, in the tutorial, it demonstrates finding sprites of the definition "bomb". I've been using that for a while, but since I'm gonna have multiple maps, I could check just the left part of the name, right?

Code: [Select]
left(Def.Name.9)="Bug_enemy"
Would work for all sprites that start with that? I'm not really asking, so much as realizing how much work this will save when we start branching into multiple maps.

*Bakes cookies for durnurd*

durnurd

  • Lead Lemming
  • Expert
  • Fanatic
  • *****
  • Posts: 1234
  • Games completed so far: 0
    • MSN Messenger - durnurd@hotmail.com
    • View Profile
    • Find My Ed
Re: Dynamic Message Box Creation
« Reply #8 on: 2006-12-03, 07:30:10 PM »
Well, almost, anyway, yes.  Use a comma to separate the string name from the number of characters to take.  That is, use
if left(Def.Name,9)="Bug_enemy" then
instead of
if left(Def.Name.9)="Bug_enemy" then
Edward Dassmesser

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Dynamic Message Box Creation
« Reply #9 on: 2006-12-03, 08:21:30 PM »
How is this easier than just defining the messages in the IDE?  Or what does it accomplish that entering the messages as regular functions in the IDE doesn't?

eric22222

  • Fanatic
  • ***
  • Posts: 177
    • View Profile
    • Eric Online
    • Email
Re: Dynamic Message Box Creation
« Reply #10 on: 2006-12-03, 09:37:22 PM »
Well, we want to have a LOT of dialogue in this game. We want this to have an RPG feel in expansiveness and character development. One thing that is absolutely necessary in such a game is changing dialogue. I want to keep up with a variable "Progress" that keeps track of how far along in the game the player is. Technically I could do this without script, but it'd be pretty complex. I'd have a special function for each different message in the game instead of for each NPC. While I could do a "must have X of Progress" thing, this wouldn't remove the previous messages. Like, when you get to the second message, it'd display both the first and second since you meet the requirements of Progress for both.

It would also be a hassle if moving an NPC over a few tiles was ever necessary. Now I'd only have to remake one special function instead of three or more.

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Dynamic Message Box Creation
« Reply #11 on: 2006-12-04, 06:47:05 AM »
Now that I understand that, maybe I can offer a suggestion for a clever trick you could try.  Use the text of each message function to store all the data about the messages that will appear there, and then make the script just parse it instead of making the script store all the script text.  That way it might be easier to see, manage and update which text goes with which NPC.  So for example, you could create a message like this:
Code: [Select]
#COND 1=0
Oh woe is me, I have lost my wedding ring!  Who are you?  %PLAYER%?  Can you help me find my ring, %PLAYER%?
#COND 1=1
You found it!  Thank you so much.
#COND 1=2
Thanks again for finding my ring, %PLAYER%!
Then in script code, during startup, you could change the type of every message function that contains "#COND" to be SPECIAL_EVENT so that the function no longer displays a message, but just triggers an event to allow the script to do its work.  The script, when it received a special function activation event, would parse the string in the Value property of the activated function, which would still contain the original message string.  Using the information after each #COND, it could determine which condition is met (in this example, I figured you might want to be checking the number in inventory item #1, and when it is 0, display the first segment, when it is 1, display the second, and when it is 2, display the last segment.  That's what the "1=0", "1=1" and "1=2" are for).  After the script determines which portion of the string represents the message it should display, it changes the text (Value property) of some off-screen special function object and calls ActivateFunction on it to display the message.

You could use code similar to what you can find in the engine (I can help you find it if you want) -- the code which parses the font name and color at the beginning of the message.  It parses the string line by line using InStr to find the end of each line (vbLf) and Left to extract the text.  If it sees "#FCL" at the beginning of any line, it removes it from the message text and changes the font color.  In your case, you could check for "#COND" at the beginning of a line, and, in this case, see the "1=0" and parse that into some code that knows that this means you have to have 0 of inventory number 1 in order for this segment of the message to display.

This might sound complicated, but keep in mind that you'd write this code once (I'm guessing it'd be maybe 30-40 lines -- not too bad compared to the hundreds you'd have maintaining all the messages manually) and then you are able to maintain all your messages in the IDE without resorting to editing script whenever you want to change a message or the conditions under which a message applies.

Oh, and I almost forgot to mention, you can use a one-line call to the Replace function (I think that's a function available in VBScript -- I know I've used it in VB) to change text like "%PLAYER%" into the actual player name:
Code: [Select]
MsgSegment = Replace(MsgSegment, "%PLAYER%", PlayerName)

eric22222

  • Fanatic
  • ***
  • Posts: 177
    • View Profile
    • Eric Online
    • Email
Re: Dynamic Message Box Creation
« Reply #12 on: 2006-12-04, 10:22:07 AM »
Sounds a bit complex, but if anyone's willing to help me figure out to make it, I'll go with that strategy.

Would this allow me to use bounds for several values of progress? I assume greater than 12 would be easy enough, but what about from 7 to 10?

Oh, and would it be possible to check some different values, like if I had a series of "go find the item" style quests, could I use different inventory items per message box? Like, test Progress and Ring?
« Last Edit: 2006-12-04, 10:38:33 AM by eric22222 »

durnurd

  • Lead Lemming
  • Expert
  • Fanatic
  • *****
  • Posts: 1234
  • Games completed so far: 0
    • MSN Messenger - durnurd@hotmail.com
    • View Profile
    • Find My Ed
Re: Dynamic Message Box Creation
« Reply #13 on: 2006-12-04, 12:30:28 PM »
I think there's a much easier way to do this.  The VBScript functions "Eval" and "Execute".

Since VBScript is an interpreted language, rather than compiled, you can write code as a string and simply call Eval or Execute on it to have it run.  For example:

Code: [Select]
dim a, b, c
b = 15
c = 10
a = "b > c AND c = 10"
msgbox Eval(a)

Will throw up a messagebox saying "True", since b is greater than c and c is equal to 10.

The upshot of this is that you can use this to write your conditions in the textbox.  For example, instead of:

Code: [Select]
#COND 1=0
Oh woe is me, I have lost my wedding ring!  Who are you?  %PLAYER%?  Can you help me find my ring, %PLAYER%?
#COND 1=1
You found it!  Thank you so much.
#COND 1=2
Thanks again for finding my ring, %PLAYER%!

you could write:
Code: [Select]
#COND
dim PROGRESS
PROGRESS = ProjectObj.GamePlayer.InvQuantityOwned(1)
select case PROGRESS
   case 0
      msg = "Oh woe is me, I have lost my wedding ring!  Who are you?  %PLAYER%?  Can you help me find my ring, %PLAYER%?"
   case 1
      msg = "You found it!  Thank you so much."
   case else
      msg = "Thanks again for finding my ring, %PLAYER%!"
   end select

And the code would be quite simple, something along the lines of:
Code: [Select]
Sub Player_OnSpecialFunction(SpecialObj)
   If SpecialObj.FuncType = 1 AND left(SpecialObj.Value,7) = "#COND" + vbcrlf
      dim tmp, msg
      tmp = mid (SpecialObj.Value,8)
      Execute(tmp)
      ShowMessage(msg)
   End if
End Sub

Sub ShowMessage(msg)
   msg = Replace(msg,"%PLAYER%",PlayerName) 'Insert Players name where appropriate
   msg = Replace(msg,"%GOLD%",ProjectObj.GamePlayer.InvQuantityOwned(2)) 'Insert current amount of gold where appropriate

   'etc....

   dim msgSpecial
   Set msgSpecial = NewSpecialFunction
   msgSpecial.funcType = 1
   msgSpecial.Value = msg
   ProjectObj.GamePlayer.ActivateFunction(msgSpecial)
End Sub

Something like that anyway.  It may look confusing, but most of the code is just copy and paste.  All you would need to change is the case numbers and the messages.  Also, in VBScript, you can check for multiple values or ranges using case statements.  For example:
Code: [Select]
select case PROGRESS
   case 1, 7
      msg = "Message for when PROGRESS = 1 or 7"
   case 2 to 6
      msg = "Message for when PROGRESS is either 2, 3, 4, 5, or 6"
   case else
      msg = "Message for when PROGRESS is greater than 7"
end select

You can also check for multiple variables, like you asked.  However, you would need to use if statements in that case, instead of case statements.  Or, if you want, you could use both:

Code: [Select]
#COND
dim PROGRESS, RING
PROGRESS = HostObj.GamePlayer.InvQuantityOwned(1)
RING = HostObj.GamePlayer.InvQuantityOwned(6)
select case PROGRESS
   case 0
      msg = "Message 1"
   case 1
      msg = "Message 2"
   case 2
      if (RING = 0) then
         msg = "Message 3a (No Ring)"
      else
         msg = "Message 3b (Ring)"
   case else
      msg = "Message for all other occasions"
end select
« Last Edit: 2006-12-04, 12:45:06 PM by durnurd »
Edward Dassmesser

eric22222

  • Fanatic
  • ***
  • Posts: 177
    • View Profile
    • Eric Online
    • Email
Re: Dynamic Message Box Creation
« Reply #14 on: 2006-12-04, 12:36:55 PM »
That seems to be the best way so far, even though it may be a little more difficult than the previous one. This one seems like it'll give me the most flexibility, as far as checking multiple variables. It looks like I'd also be able to access some of the global script variables I have.

Oh, and if this is executed as script, I guess I can execute functions from it.
« Last Edit: 2006-12-04, 12:50:54 PM by eric22222 »