/// <summary> /// Construction a conversation /// </summary> /// <param name="npc">NPC you will have a conversation with</param> /// <param name="state">The games current State</param> /// <param name="dataOvlRef">Data.OVL for reference</param> public Conversation(NonPlayerCharacterReference npc, GameState state, DataOvlReference dataOvlRef) { this.Npc = npc; script = npc.Script; this.gameStateRef = state; this.dataOvlRef = dataOvlRef; }
/// <summary> /// Construct an NPC /// </summary> /// <param name="mapRef">Which map are they on?</param> /// <param name="sched">daily schedule</param> /// <param name="npcType">type of NPC they are</param> /// <param name="dialogNumber">dialog number referencing data OVL</param> /// <param name="dialogIndex">0-31 index of it's position in the NPC arrays (used for saved.gam references)</param> /// <param name="talkScript">their conversation script</param> public NonPlayerCharacterReference(SmallMapReferences.SingleMapReference.Location location, GameState gameStateRef, NPC_Schedule sched, byte npcType, byte dialogNumber, int dialogIndex, TalkScript talkScript, int nKeySprite) { Schedule = new NonPlayerCharacterSchedule(sched); MapLocation = location; NPCKeySprite = nKeySprite; CharacterType = npcType; DialogNumber = dialogNumber; Script = talkScript; DialogIndex = dialogIndex; this.gameStateRef = gameStateRef; // no schedule? I guess you're not real if (!IsEmptySched(sched)) { System.Console.WriteLine(location.ToString() + " NPC Number: " + this.DialogNumber + " in " + location.ToString()); } }
/// <summary> /// Intializes an individual TalkingScript using the raw data created from InitalizeTalkScriptsRaw /// <remark>May God have mercy on my soul if I ever need to debug or troubleshoot this again.</remark> /// </summary> /// <param name="smallMapRef">the small map reference</param> /// <param name="index">NPC Index</param> private TalkScript InitializeTalkScriptFromRaw(SmallMapReferences.SingleMapReference.SmallMapMasterFiles smallMapRef, int index) { TalkScript talkScript = new TalkScript(); // the script we are building and will return List <bool> labelsSeenList = new List <bool>(TalkScript.TOTAL_LABELS); // keeps track of the labels we have already seen labelsSeenList.AddRange(Enumerable.Repeat(false, TalkScript.TOTAL_LABELS)); // creates a list of "false" bools to set the default labelsSeenList bool writingSingleCharacters = false; // are we currently writing a single character at a time? string buildAWord = string.Empty; // the word we are currently building if we are writingSingleCharacters=true foreach (byte byteWord in talkRefs[smallMapRef][index]) { // if a NULL byte is provided then you need to go the next line, resetting the writingSingleCharacters so that a space is not inserted next line // bh: Sept 21, 2019 - had to add a disgusting hack to account for what appears to be broken // data or a misunderstood algorithm - they seem to have an end of script line in the middle of a response // there could be a rule I simply don't undertstand, but for now - i hack if (byteWord == END_OF_SCRIPTLINE_BYTE && !buildAWord.EndsWith("to give unto charity!")) { buildAWord += "\n"; // we are done with the entire line, so lets add it the script talkScript.AddTalkCommand(TalkScript.TalkCommand.PlainString, buildAWord); // tells the script to move onto the next command talkScript.NextLine(); // reset some vars buildAWord = string.Empty; writingSingleCharacters = false; continue; } byte tempByte = (byte)((int)byteWord); // this is the byte that we will manipulate, leaving the byteWord in tact bool usePhraseLookup = false; // did we do a phrase lookup (else we are typing single letters) bool useCompressedWord = false; // did we succesfully use a compressed word? // if it's one of the bytes that requires a subraction of 0x80 (128) if (byteWord >= 165 && byteWord <= 218) { tempByte -= TALK_OFFSET_ADJUST; } else if (byteWord >= 225 && byteWord <= 250) { tempByte -= TALK_OFFSET_ADJUST; } else if (byteWord >= 160 && byteWord <= 161) { tempByte -= TALK_OFFSET_ADJUST; } else { // it didn't match which means that it's one the special phrases and we will perform a lookup usePhraseLookup = true; } // Debug code to help track down particular codes being written // if (tempByte == 119) { Console.Write(""); } // it wasn't a special phrase which means that the words are being typed one word at a time if (!usePhraseLookup) { // I'm writing single characters, we will keep track so that when we hit the end we can insert a space writingSingleCharacters = true; // this signifies the end of the printing (sample code enters a newline) if ((char)tempByte == '@') { //Console.WriteLine(""); continue; } //Console.Write((char)tempByte); buildAWord += (char)tempByte; // Debug code to help track down an NPCs question or response //if (buildAWord.Contains("to give unto charity")) { Console.Write(""); } } else // usePhraseLookup = true { // We were instructed to perform a lookup, either a compressed word lookup, or a special character // if we were previously writing single characters, but have moved onto lookups, then we add a space, and reset it if (writingSingleCharacters) { writingSingleCharacters = false; buildAWord += " "; } // we are going to lookup the word in the compressed word list, if we throw an exception then we know it wasn't in the list if (compressedWordRef.IsTalkingWord((int)tempByte)) { string talkingWord = compressedWordRef.GetTalkingWord((int)tempByte); useCompressedWord = true; buildAWord += talkingWord; } // this is a bit lazy, but if I ask for a string that is not captured in the lookup map, then we know it's a special case else { // oddly enough - we add an existing plain string that we have been building // at the very last second if (buildAWord != string.Empty) { talkScript.AddTalkCommand(TalkScript.TalkCommand.PlainString, buildAWord); buildAWord = string.Empty; } // if the tempByte is within these boundaries then it is a label // it is up to us to determine if it a Label or a GotoLabel if (tempByte >= TalkScript.MIN_LABEL && tempByte <= TalkScript.MAX_LABEL) { // create an offset starting at 0 for label numbering int offset = tempByte - TalkScript.MIN_LABEL; // have I already seen a label once? Then the next label is a definition if (labelsSeenList[offset] == true) { talkScript.AddTalkLabel(TalkScript.TalkCommand.DefineLabel, offset); } else { // the first time you see the label is a goto statement talkScript.AddTalkLabel(TalkScript.TalkCommand.DefineLabel, offset); //talkScript.AddTalkLabel(TalkScript.TalkCommand.GotoLabel, offset); labelsSeenList[offset] = true; } } else { // this is just a standard special command, so let's add it talkScript.AddTalkCommand((TalkScript.TalkCommand)tempByte, string.Empty); } } // we just used a compressed word which means we need to insert a space afterwards if (useCompressedWord) { buildAWord += " "; } } } talkScript.InitScript(); return(talkScript); }