Jump to content


Photo

Variable Timers for Sequential Dialogues


1 reply to this topic

#1 berelinde

berelinde

    The Typo Queen

  • Gibberlings
  • 8594 posts
  • Location:New Jersey, USA

Posted 06 August 2010 - 01:20 PM

Every gamer plays at a different pace. Some do as few side-quests as possible, others do every single one, and add quest or expansion mods as well. If you're writing a relationship of any type into your NPC mod, you may want to add a variable timer so that players can adjust the pace of the relationship to better suit their playing style.

If you're going to go this route, it's probably easiest if you do it when you first write the code. It can be added later, but since this is an example, let's start out with the timer variables in place. For the purposes of this example, we'll assume you're writing a romance, that ## is your personal prefix, that ##NPCJ is the name of your NPC's joined dialogue file, that ##NPCS is your NPC's override script, that ##NPC is your NPC's DV, and that your NPC's name is NPC. Not original, but it avoids confusion later.

So we've got

/* 
Name:	   NPC
DV:		 ##NPC
J dialogue: ##NPCJ
Override:   ##NPCS 
*/

If you were writing a mod without variables, you would need a romance match. Many people would put that in the NPC's override script, so let's do that. This NPC likes females, but doesn't have any other romance requirements.

// ##npcs.baf - NPC's override script starts here

/* Romance Match - in ##npcs.baf */ 

IF 
	InParty(Myself) 
	Global("##NPCMatch","GLOBAL",0) 
	Gender(Player1,FEMALE) 
THEN 
	RESPONSE #100 
		SetGlobal("##NPCRomanceActive","GLOBAL",1) 
		SetGlobal("##NPCLovetalk","GLOBAL",1) 
		RealSetGlobalTimer("##NPCTime","GLOBAL",3600) 
		SetGlobal("##NPCMatch","GLOBAL",1) 
END 

/* Exclude males if the PC should stumble across a Girdle of Masculinity/Femininity later */ 

IF 
	InParty(Myself) 
	Global("##NPCMatch","GLOBAL",0) 
	!Gender(Player1,FEMALE) 
THEN 
	RESPONSE #100 
		SetGlobal("##NPCMatch","GLOBAL",2) 
END

And that's it. Pretty straightforward, even with the additional script block for gender-bender belts.

Now we'll do it if the way we would if we were going to use variable timers. Most of the time, your prefix will contain characters that can go in an IDS table with no problem, but if your personal prefix contains unusual characters like !, you might just have to omit your prefix.

// ##npcs.baf - NPC's override script starts here

/* Romance Match - in ##npcs.baf */ 

IF 
	InParty(Myself) 
	Global("##NPCMatch","GLOBAL",0) 
	Gender(Player1,FEMALE) 
THEN 
	RESPONSE #100 
		SetGlobal("##NPCRomanceActive","GLOBAL",1) 
		SetGlobal("##NPCLovetalk","GLOBAL",1) 
		RealSetGlobalTimer("##NPCTime","GLOBAL",##NPC_TIMER) 
		SetGlobal("##NPCMatch","GLOBAL",1) 
END 

/* Exclude males if the PC should stumble across a Girdle of Masculinity/Femininity later */ 

IF 
	InParty(Myself) 
	Global("##NPCMatch","GLOBAL",0) 
	!Gender(Player1,FEMALE) 
THEN 
	RESPONSE #100 
		SetGlobal("##NPCMatch","GLOBAL",2) 
END

Yes, I know, timers need integers. We'll get to that later, when we make up the tp2. For now, on to the block where you cue the first talk.

/* Cues lovetalks - in ##npcs.baf */ 

IF 
	InParty(Myself) 
	RealGlobalTimerExpired("##NPCTime","GLOBAL") 
	Global("##NPCRomanceActive","GLOBAL",1) 
	CombatCounter(0) 
	See(Player1) 
	!StateCheck(Player1,CD_STATE_NOTVALID) 
	!StateCheck(Myself,CD_STATE_NOTVALID) 
	Global("##NPCLovetalk","GLOBAL",1)		 // Yes, this will be an OR() block, but for this example, we're only using one
THEN 
	RESPONSE #100 
		IncrementGlobal("##NPCLovetalk","GLOBAL",1) 
END 

/* Starts lovetalks - in ##npcs.baf */ 

IF 
	InParty(Myself) 
	RealGlobalTimerExpired("##NPCTime","GLOBAL") 
	Global("##NPCRomanceActive","GLOBAL",1) 
	CombatCounter(0) 
	See(Player1) 
	!StateCheck(Player1,CD_STATE_NOTVALID) 
	!StateCheck(Myself,CD_STATE_NOTVALID) 
	Global("##NPCLovetalk","GLOBAL",2)		 // Yes, this will be an OR() block, but for this example, we're only using one
THEN 
	RESPONSE #100 
		PlaySong(0) 
		PlaySound("NPCsong") 
		StartDialogueNoSet(Player1) 
END

You don't see the variable ##NPC_TIMER anywhere, so we don't need to do anything special to this block.

Now, let's look at the dialogue itself. We'll just show the first state. This is where you close the global and set the timer for the next talk. Please note the order. Closing the variable before setting the timer works fine, but setting the timer and then attempting to close the variable will result in the variable not getting set.

This is how you would do it if you weren't using variable timers.

// from ##NPCJ.d 

IF ~Global("##NPCLovetalk","GLOBAL",2)~ THEN BEGIN T1 
SAY ~If you aren't busy, I'd like to talk to you for a while.~ 
++ ~What's on your mind?~ DO ~SetGlobal("##NPCLovetalk","GLOBAL",3) RealSetGlobalTimer("##NPCTime","GLOBAL",3600)~ + T1.1 
++ ~I'm never too busy for you!~ DO ~SetGlobal("##NPCLovetalk","GLOBAL",3) RealSetGlobalTimer("##NPCTime","GLOBAL",3600)~ + T1.2 
+ ~InParty("Anomen") 
	  InMyArea("Anomen") 
	  !StateCheck("Anomen",CD_STATE_NOTVALID) 
	  Global("AnomenRomanceActive","GLOBAL",1)~ + ~Not now, NPC, I'm trying to get Anomen's attention.~ DO ~SetGlobal("##NPCLovetalk","GLOBAL",3) RealSetGlobalTimer("##NPCTime","GLOBAL",3600)~ + T1.3 
++ ~I'm in the middle of something.~ DO ~SetGlobal("##NPCLovetalk","GLOBAL",3) RealSetGlobalTimer("##NPCTime","GLOBAL",3600)~ + T1.4 
END

And this is how you would do it if you wanted to use variable timers. Note that the only difference is the substitution of ##NPC_TIMER for 3600.

// from ##NPCJ.d 

IF ~Global("##NPCLovetalk","GLOBAL",2)~ THEN BEGIN T1 
SAY ~If you aren't busy, I'd like to talk to you for a while.~ 
++ ~What's on your mind?~ DO ~SetGlobal("##NPCLovetalk","GLOBAL",3) RealSetGlobalTimer("##NPCTime","GLOBAL",##NPC_TIMER)~ + T1.1 
++ ~I'm never too busy for you!~ DO ~SetGlobal("##NPCLovetalk","GLOBAL",3) RealSetGlobalTimer("##NPCTime","GLOBAL",##NPC_TIMER)~ + T1.2 
+ ~InParty("Anomen") 
	  InMyArea("Anomen") 
	  !StateCheck("Anomen",CD_STATE_NOTVALID) 
	  Global("AnomenRomanceActive","GLOBAL",1)~ + ~Not now, NPC, I'm trying to get Anomen's attention.~ DO ~SetGlobal("##NPCLovetalk","GLOBAL",3) RealSetGlobalTimer("##NPCTime","GLOBAL",##NPC_TIMER)~ + T1.3 
++ ~I'm in the middle of something.~ DO ~SetGlobal("##NPCLovetalk","GLOBAL",3) RealSetGlobalTimer("##NPCTime","GLOBAL",##NPC_TIMER)~ + T1.4 
END

So far, so good. No rocket science. Now we're going to have to do the tp2 stuff to make it all come together. This is a little tricky, so I'll type slow put down the uncommented code first and then copy it as commented code so you can get some idea of what's going on at each step.

First, we have to define our variables.
// from setup-npc.tp2

PRINT ~Select NPC's dialogue timer:~  

PRINT ~Please choose one of the following:
[1] 15 minutes real time minimum between dialogues
[2] 30 minutes real time minimum between dialogues
[3] 45 minutes real time minimum between dialogues
[4] 1 hour real time minimum between dialogues (recommended) 
[5] 1 hour 30 minutes real time minimum between dialogues 
[6] 2 hours real time minimum between dialogues~ 

OUTER_SPRINT ~npctimer~ ~placeholder_value~ 
OUTER_WHILE (!(IS_AN_INT ~npctimer~) OR (~npctimer~ > 0x6) OR (~npctimer~ < 0x1)) BEGIN
  PRINT ~Please select 1, 2, 3, 4, 5 or 6 and press enter.~
  ACTION_READLN ~npctimer~
END
	  ACTION_IF ("npctimer" = 1) THEN BEGIN
	  APPEND ~gtimes.ids~ ~900 ##NPC_TIMER~
	  PRINT ~Speed: minimum 15 minutes real time between dialogues~ 
	  END
	  ACTION_IF ("npctimer" = 2) THEN BEGIN
	  APPEND ~gtimes.ids~ ~1800 ##NPC_TIMER~
	  PRINT ~Speed: minimum 30 minutes real time between dialogues~ 
	  END
	  ACTION_IF ("npctimer" = 3) THEN BEGIN
	  APPEND ~gtimes.ids~ ~2700 ##NPC_TIMER~
	  PRINT ~Speed: minimum 45 minutes real time between dialogues~ 
	  END
	  ACTION_IF ("npctimer" = 4) THEN BEGIN
	  APPEND ~gtimes.ids~ ~3600 ##NPC_TIMER~
	  PRINT ~Speed: minimum 1 hour real time between dialogues~ 
	  END
	  ACTION_IF ("npctimer" = 5) THEN BEGIN
	  APPEND ~gtimes.ids~ ~5400 ##NPC_TIMER~
	  PRINT ~Speed: minimum 1 hour 30 minutes real time between dialogues~ 
	  END
	  ACTION_IF ("npctimer" = 6) THEN BEGIN
	  APPEND ~gtimes.ids~ ~7200 ##NPC_TIMER~
	  PRINT ~Speed: minimum 2 hours real time between dialogues~ 
	  END

It isn't as bad as it looks. The first block is merely defining a temporary variable and saying what the acceptable range of values can be. The ACTION_IF lines that follow are associating the number the player enters with that temporary variable. Let's try it with comments.

// from setup-npc.tp2

/* This isn't a separate component. It's part of the mod. Since this is the case, we're going to have to tell the player what we're doing using PRINT */ 
PRINT ~Select NPC's dialogue timer:~  

/* Now we have to tell the player what his options are. */ 
PRINT ~Please choose one of the following:
[1] 15 minutes real time minimum between dialogues
[2] 30 minutes real time minimum between dialogues
[3] 45 minutes real time minimum between dialogues
[4] 1 hour real time minimum between dialogues (recommended) 
[5] 1 hour 30 minutes real time minimum between dialogues 
[6] 2 hours real time minimum between dialogues~ 

/* This first line defines the variable. It means, paraphrased 
IN_THE_NEXT_CHUNK_OF_CODE_THAT_FOLLOWS ~npctimer~ is a ~placeholder_value~ */ 
/* OK, the actual explanation is more complicated than that, but this is essential what it does. */ 
OUTER_SPRINT ~npctimer~ ~placeholder_value~ 

/* This section says, paraphrased 
IF_PLAYER_INPUTS_SOMETHING_THAT (is NOT an INTEGER) OR (is > 6) OR (is < 1) THEN 
PRINT ~Please select 1, 2, 3, 4, 5, or 6 and press enter.~ 
OTHERWISE READ ~npctimer~ and substitute the number the player enters*/ 

OUTER_WHILE (!(IS_AN_INT ~npctimer~) OR (~npctimer~ > 0x6) OR (~npctimer~ < 0x1)) BEGIN
  PRINT ~Please select 1, 2, 3, 4, 5 or 6 and press enter.~
  ACTION_READLN ~npctimer~
END 

/* This is where the timer variable you put in your script and dialogue gets defined */ 
/* Paraphrased, the first ACTION_IF block says 
IF_PLAYER_ENTERS 1, 
APPEND ~gtimes.ids~ with ~900 ##NPC_TIMER~ 
and then PRINT ~Speed: minimum 15 minutes real time between dialogues~ */ 
/* gtimes.ids is the IDS table where all those handy shortcuts like ONE_DAY and FIVE_ROUNDS are stored */ 

	  ACTION_IF ("npctimer" = 1) THEN BEGIN
	  APPEND ~gtimes.ids~ ~900 ##NPC_TIMER~
	  PRINT ~Speed: minimum 15 minutes real time between dialogues~ 
	  END
	  ACTION_IF ("npctimer" = 2) THEN BEGIN
	  APPEND ~gtimes.ids~ ~1800 ##NPC_TIMER~
	  PRINT ~Speed: minimum 30 minutes real time between dialogues~ 
	  END
	  ACTION_IF ("npctimer" = 3) THEN BEGIN
	  APPEND ~gtimes.ids~ ~2700 ##NPC_TIMER~
	  PRINT ~Speed: minimum 45 minutes real time between dialogues~ 
	  END
	  ACTION_IF ("npctimer" = 4) THEN BEGIN
	  APPEND ~gtimes.ids~ ~3600 ##NPC_TIMER~
	  PRINT ~Speed: minimum 1 hour real time between dialogues~ 
	  END
	  ACTION_IF ("npctimer" = 5) THEN BEGIN
	  APPEND ~gtimes.ids~ ~5400 ##NPC_TIMER~
	  PRINT ~Speed: minimum 1 hour 30 minutes real time between dialogues~ 
	  END
	  ACTION_IF ("npctimer" = 6) THEN BEGIN
	  APPEND ~gtimes.ids~ ~7200 ##NPC_TIMER~
	  PRINT ~Speed: minimum 2 hours real time between dialogues~ 
	  END

We're almost done. We've coded our scripts and dialogues to accept variable timers and we've defined those variable timers in the tp2. Now we have to compile the dialogues and scripts. Note that you have to define the variable timers in the tp2 before you attempt to compile anything using them. Doing it the other way around will result in errors.

Ordinarily, if you wanted to compile a dialogue or a script, you would just use this
// from setup-npc.tp2

COMPILE ~npc/dialogue/##npcj.d~ 

COMPILE ~npc/script/##npcs.baf~

Since you are using variable timers, you can't do that. Instead, you have to use something a little different. It's still easy.
COMPILE EVALUATE_BUFFER ~npc/dialogue/##npcj.d~ 

COMPILE EVALUATE_BUFFER ~npc/script/##npcs.baf~

And that's all there is to it.

Fine print: According to the WeiDU docs, OUTER_SPRINT will break --traify, but I just installed an untraified testing mod using OUTER_SPRINT in an untraified tp2, uninstalled the mod, traified the tp2, and then installed the mod again, and everything worked fine. If in doubt, use OUTER_TEXT_SPRINT with %'s around the variable instead. Check the WeiDU docs for more info.
Must. Write. Faster.

cmorgan: "None of us get old around here, just more proficient at doing more stuff with less braincells!"

berelinde's mods
TolkienAcrossTheWater website
TolkienAcrossTheWater Forum

#2 Ardanis

Ardanis

    A very GAR character

  • Members
  • 2250 posts
  • Gender:Male
  • Location:Saint-Petersburg, Russia

Posted 07 August 2010 - 08:22 AM

Ordinarily, if you wanted to compile a dialogue or a script, you would just use this

// from setup-npc.tp2

COMPILE ~npc/dialogue/##npcj.d~ 

COMPILE ~npc/script/##npcs.baf~

Since you are using variable timers, you can't do that. Instead, you have to use something a little different. It's still easy.
COMPILE EVALUATE_BUFFER ~npc/dialogue/##npcj.d~ 

COMPILE EVALUATE_BUFFER ~npc/script/##npcs.baf~

The first way will work as well, you do not need to use EVALUATE_BUFFER. This command serves to evaluate WeiDU variables, as this
++ ~What's on your mind?~ DO ~SetGlobal("##NPCLovetalk","GLOBAL",3) RealSetGlobalTimer("##NPCTime","GLOBAL",%weidu_variable%)~ + T1.1
when you SPRINT or SET a var in TP2 and then use it in dialog/script compilation. The abovementioned NPC_TIMER is an IDS variable, not WeiDU's, so it will be evaluated by default.
"Uguu~ Boku Ayu."

Before you start breaking wall tiles with your bare fists, ask yourself first - do you truly need it?



Reply to this topic



  


0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users