public override void RestoreSaveData(object dataIn) { if (dataIn == null) { return; } SaveData_v1 data = (SaveData_v1)dataIn; FactionFile.FactionData dsfactionData; if (!GameManager.Instance.PlayerEntity.FactionData.GetFactionData(data.factionID, out dsfactionData) && data.factionID != 0) { throw new Exception("Could not deserialize Person resource FactionID to FactionData"); } race = data.race; nameBank = data.nameBank; npcGender = data.npcGender; faceIndex = data.faceIndex; nameSeed = data.nameSeed; isQuestor = data.isQuestor; isIndividualNPC = data.isIndividualNPC; isIndividualAtHome = data.isIndividualAtHome; displayName = data.displayName; homePlaceSymbol = data.homePlaceSymbol; lastAssignedPlaceSymbol = data.lastAssignedPlaceSymbol; assignedToHome = data.assignedToHome; factionData = dsfactionData; factionTableKey = data.factionTableKey; questorData = data.questorData; discoveredThroughTalkManager = data.discoveredThroughTalkManager; isMuted = data.isMuted; isDestroyed = data.isDestroyed; }
public override IQuestAction CreateNew(string source, Quest parentQuest) { // Source must match pattern Match match = Test(source); if (!match.Success) { return(null); } // Factory new action WhenNpcIsAvailable action = new WhenNpcIsAvailable(parentQuest); // Confirm this is an individual NPC string individualNPCName = match.Groups["individualNPCName"].Value; int factionID = Person.GetIndividualFactionID(individualNPCName); if (factionID != -1) { FactionFile.FactionData factionData = Person.GetFactionData(factionID); if (factionData.type != (int)FactionFile.FactionTypes.Individual) { throw new Exception(string.Format("WhenNpcIsAvailable: NPC {0} with FactionID {1} is not an individual NPC", individualNPCName, factionID)); } } action.npcName = individualNPCName; action.npcFactionID = factionID; return(action); }
// Creates an individual NPC like King Gothryd or Brisienna void SetupIndividualNPC(string individualNPCName) { // Get faction data int factionID = GetIndividualFactionID(individualNPCName); if (factionID != -1) { FactionFile.FactionData factionData = GetFactionData(factionID); // This must be an individual NPC or Daedra if (factionData.type != (int)FactionFile.FactionTypes.Individual && factionData.type != (int)FactionFile.FactionTypes.Daedra) { throw new Exception(string.Format("Named NPC {0} with FactionID {1} is not an individual NPC or Daedra", individualNPCName, factionID)); } // Setup Person resource isIndividualNPC = true; this.factionTableKey = individualNPCName; this.factionData = factionData; } else { Debug.LogErrorFormat("SetupIndividualNPC() failed to setup {0}", individualNPCName); } }
/// <summary> /// Get faction2 relation to faction1. Returns 1 if faction2 is the parent of faction1, 2 if faction1 and faction2 share the same parent /// 3 if faction2 is a child of faction1, and 0 if none of the above. /// </summary> public int GetFaction2ARelationToFaction1(int factionID1, int factionID2) { if (factionDict.ContainsKey(factionID1) && factionDict.ContainsKey(factionID2)) { FactionFile.FactionData factionData1 = factionDict[factionID1]; if (factionData1.parent == factionID2) { return(1); } if (factionDict.ContainsKey(factionData1.parent)) { FactionFile.FactionData parentData = factionDict[factionData1.parent]; if (parentData.children != null && parentData.children.Contains(factionID2)) { return(2); } } if (factionData1.children != null && factionData1.children.Contains(factionID2)) { return(3); } } return(0); }
/// <summary> /// Finds faction of the given type and for the given region. /// If a match for both is not found, the last faction that matched type and had -1 for region is returned. /// A function like this exists in classic and is used in a number of places. /// One known use case is for finding the region factions. If no specific /// faction exists for the region, Random Ruler (region -1) is returned. /// </summary> /// <param name="type">Type to match.</param> /// <param name="oneBasedRegionIndex">Region index to match. Must be ONE-BASED region index used by FACTION.TXT.</param> /// <param name="factionDataOut">Receives faction data.</param> /// <returns>True if successful.</returns> public bool FindFactionByTypeAndRegion(int type, int oneBasedRegionIndex, out FactionFile.FactionData factionDataOut) { bool foundPartialMatch = false; factionDataOut = new FactionFile.FactionData(); FactionFile.FactionData partialMatch = new FactionFile.FactionData(); // Match faction items foreach (FactionFile.FactionData item in factionDict.Values) { if (type == item.type && oneBasedRegionIndex == item.region) { factionDataOut = item; return(true); } else if (type == item.type && item.region == -1) { foundPartialMatch = true; partialMatch = item; } } if (foundPartialMatch) { factionDataOut = partialMatch; return(true); } else { Debug.LogFormat("PersistentFactionData.FindFactionByTypeAndRegion() couldn't find the requested faction. Type {0}, RegionIndex {1}", type, oneBasedRegionIndex); return(false); } }
public override void RestoreSaveData(object dataIn) { SaveData_v1 data = (SaveData_v1)dataIn; if (dataIn == null) { return; } FactionFile.FactionData dsfactionData; if (!GameManager.Instance.PlayerEntity.FactionData.GetFactionData(data.factionID, out dsfactionData)) { throw new Exception("Could not deserialize Person resource FactionID to FactionData"); } race = data.race; npcGender = data.npcGender; faceIndex = data.faceIndex; nameSeed = data.nameSeed; isQuestor = data.isQuestor; isIndividualNPC = data.isIndividualNPC; isIndividualAtHome = data.isIndividualAtHome; displayName = data.displayName; godName = data.godName; homeTownName = data.homeTownName; factionData = dsfactionData; questorData = data.questorData; }
/// <summary> /// Finds faction of the given type and for the given region. /// If a match for both is not found, the last faction that matched type and had -1 for region is returned. /// A function like this exists in classic and is used in a number of places. /// One known use case is for finding the region factions. If no specific /// faction exists for the region, Random Ruler (region -1) is returned. /// </summary> /// <param name="type">Type to match.</param> /// <param name="regionIndex">Zero-based region index to find in persistent faction data.</param> /// <param name="factionDataOut">Receives faction data out.</param> /// <returns>True if successful.</returns> public bool FindFactionByTypeAndRegion(int type, int regionIndex, out FactionFile.FactionData factionDataOut) { bool foundPartialMatch = false; factionDataOut = new FactionFile.FactionData(); FactionFile.FactionData partialMatch = new FactionFile.FactionData(); // Match faction items foreach (FactionFile.FactionData item in factionDict.Values) { if (type == item.type && regionIndex == item.region) { factionDataOut = item; return(true); } else if (type == item.type && item.region == -1) { foundPartialMatch = true; partialMatch = item; } } if (foundPartialMatch) { factionDataOut = partialMatch; return(true); } else { return(false); } }
// Creates a career-based NPC like a Shopkeeper or Banker void SetupCareerAllianceNPC(string careerAllianceName) { // Special handling for Questor class // Will revisit this later when guilds are more integrated if (careerAllianceName.Equals("Questor", StringComparison.InvariantCultureIgnoreCase)) { if (SetupQuestorNPC()) { return; } } // Get faction data int factionID = GetCareerFactionID(careerAllianceName); if (factionID != -1) { FactionFile.FactionData factionData = GetFactionData(factionID); // Setup Person resource this.factionData = factionData; } else { Debug.LogErrorFormat("SetupCareerAllianceNPC() failed to setup {0}", careerAllianceName); } }
/// <summary> /// Change reputation value by amount. Propagation is matched to classic. /// </summary> /// <param name="factionID">Faction ID of faction initiate reputation change.</param> /// <param name="amount">Amount to change reputation, positive or negative.</param> /// <param name="propagate">True if reputation change should propagate to affiliated factions and allies/enemies.</param> /// <returns></returns> public bool ChangeReputation(int factionID, int amount, bool propagate = false) { if (factionDict.ContainsKey(factionID)) { FactionFile.FactionData factionData = factionDict[factionID]; if (!propagate) { factionData.rep = Mathf.Clamp(factionData.rep + amount, minReputation, maxReputation); factionDict[factionID] = factionData; } else { // If a knightly order faction, propagate rep for the generic order only // (this is what classic does - assume due to all affiliated nobles being aloof from such matters..) if (factionData.ggroup == (int)FactionFile.GuildGroups.KnightlyOrder) { ChangeReputation(factionID, amount, false); // Note: classic doesn't propagate to the generic child factions (smiths etc) which is an (assumed) bug so is done here. ChangeReputation((int)FactionFile.FactionIDs.Generic_Knightly_Order, amount, true); } else { // Change ally and enemy faction reputations first (for guild faction or social questgiver npc) int[] allies = { factionData.ally1, factionData.ally2, factionData.ally3 }; int[] enemies = { factionData.enemy1, factionData.enemy2, factionData.enemy3 }; for (int i = 0; i < 3; ++i) { ChangeReputation(allies[i], amount / 2); ChangeReputation(enemies[i], -amount / 2); } // Navigate up to the root faction (treat Dark Brotherhood faction as a root faction) while (factionDict.ContainsKey(factionData.parent) && factionData.id != (int)FactionFile.FactionIDs.The_Dark_Brotherhood) { factionData = factionDict[factionData.parent]; } // Propagate reputation changes for all children of the root, or just the sindle faction if (factionData.children != null) { PropagateReputationChange(factionData, factionID, amount); } else { ChangeReputation(factionID, amount); } // If a temple deity faction, also propagate rep for generic temple faction hierarchy if (factionData.type == (int)FactionFile.FactionTypes.God) { ChangeReputation((int)FactionFile.FactionIDs.Generic_Temple, amount, true); } } } return(true); } return(false); }
public bool GetFlag(int factionID, FactionFile.Flags flag) { if (factionDict.ContainsKey(factionID)) { FactionFile.FactionData factionData = factionDict[factionID]; return((factionData.flags & (int)flag) > 0); } return(false); }
/// <summary> /// Gets reputation value. /// </summary> public int GetReputation(int factionID) { if (factionDict.ContainsKey(factionID)) { FactionFile.FactionData factionData = factionDict[factionID]; return(factionData.rep); } return(0); }
public bool SetFlag(int factionID, FactionFile.Flags flag) { if (factionDict.ContainsKey(factionID)) { FactionFile.FactionData factionData = factionDict[factionID]; factionData.flags |= (int)flag; factionDict[factionID] = factionData; return(true); } return(false); }
/// <summary> /// Find the top-level parent group of a given faction. This parent can be a Group, a Province or a Temple. /// </summary> /// <param name="faction">The faction to get the parent of.</param> /// <param name="parentFaction">The parent group faction.</param> public void GetParentGroupFaction(FactionFile.FactionData faction, out FactionFile.FactionData parentFaction) { parentFaction = faction; while (parentFaction.parent != 0 && parentFaction.type != (int)FactionFile.FactionTypes.Group && parentFaction.type != (int)FactionFile.FactionTypes.Province && parentFaction.type != (int)FactionFile.FactionTypes.Temple) { GameManager.Instance.PlayerEntity.FactionData.GetFactionData(parentFaction.parent, out parentFaction); } }
/// <summary> /// Change reputation value by amount. /// </summary> public bool ChangeReputation(int factionID, int amount) { if (factionDict.ContainsKey(factionID)) { FactionFile.FactionData factionData = factionDict[factionID]; factionData.rep = Mathf.Clamp(factionData.rep + amount, minReputation, maxReputation); factionDict[factionID] = factionData; return(true); } return(false); }
/// <summary> /// Set reputation to a specific value. /// </summary> public bool SetReputation(int factionID, int value) { if (factionDict.ContainsKey(factionID)) { FactionFile.FactionData factionData = factionDict[factionID]; factionData.rep = Mathf.Clamp(value, minReputation, maxReputation); factionDict[factionID] = factionData; return(true); } return(false); }
/// <summary> /// Change power value by amount. /// </summary> public bool ChangePower(int factionID, int amount) { if (factionDict.ContainsKey(factionID)) { FactionFile.FactionData factionData = factionDict[factionID]; factionData.power = Mathf.Clamp(factionData.power + amount, minPower, maxPower); factionDict[factionID] = factionData; return(true); } return(false); }
// Creates a career-based NPC like a Shopkeeper or Banker void SetupCareerAllianceNPC(string careerAllianceName) { // Special handling for Questor class // Will revisit this later when guilds are more integrated if (careerAllianceName.Equals("Questor", StringComparison.InvariantCultureIgnoreCase)) { if (SetupQuestorNPC()) { return; } } // Handle Local_3.x group NPCs (limited) // These appear to be a special case of assigning a residential person who is automatically instantiated to home Place // Creating a full target Place for this person automatically and storing in Quest // NOTE: Understanding is still being developed here, likely will need to rework this later if (QuestMachine.Instance.FactionsTable.HasValue(careerAllianceName)) { // Get params for this case int p1 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p1", careerAllianceName)); int p2 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p2", careerAllianceName)); //int p3 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p3", careerAllianceName)); // Only supporting specific cases for now - can expand later based on testing and iteration of support // This will support Local_3.0 - Local_3.3 // Referencing quest Sx009 here where player must locate and click an NPC with only a home location to go by if (p1 == 0 && p2 == -3) { // Just using "house2" here as actual meaning of p3 unknown string homeSymbol = string.Format("_{0}_home_", Symbol.Name); string source = string.Format("Place {0} remote house2", homeSymbol); Place home = new Place(ParentQuest, source); homePlaceSymbol = home.Symbol.Clone(); ParentQuest.AddResource(home); } } // Get faction data int factionID = GetCareerFactionID(careerAllianceName); if (factionID != -1) { FactionFile.FactionData factionData = GetFactionData(factionID); // Setup Person resource this.factionData = factionData; } else { Debug.LogErrorFormat("SetupCareerAllianceNPC() failed to setup {0}", careerAllianceName); } }
public bool SetNewRulerData(int factionID) { if (factionDict.ContainsKey(factionID)) { FactionFile.FactionData faction = factionDict[factionID]; faction.rulerPowerBonus = DFRandom.random_range_inclusive(0, 50) + 20; uint random = DFRandom.rand() << 16; faction.rulerNameSeed = DFRandom.rand() | random; return(true); } return(false); }
void ReadFactionData(BinaryReader reader) { // Step through factions factions.Clear(); int factionCount = (int)(reader.BaseStream.Length - factionDataOffset) / factionDataLength; for (int i = 0; i < factionCount; i++) { FactionFile.FactionData faction = new FactionFile.FactionData(); reader.BaseStream.Position = factionDataOffset + (i * factionDataLength); faction.type = reader.ReadByte(); faction.region = reader.ReadSByte(); faction.ruler = reader.ReadSByte(); faction.name = FileProxy.ReadCString(reader, 26); faction.rep = reader.ReadInt16(); faction.power = reader.ReadInt16(); faction.id = reader.ReadInt16(); faction.vam = reader.ReadInt16(); faction.flags = reader.ReadInt16(); faction.rulerNameSeed = reader.ReadUInt32(); faction.rulerPowerBonus = reader.ReadInt32(); // Random(0, 50) + 20 faction.flat1 = reader.ReadInt16(); faction.flat2 = reader.ReadInt16(); faction.face = reader.ReadSByte(); reader.BaseStream.Position += 1; // Second face index is always -1 faction.race = reader.ReadSByte(); faction.sgroup = reader.ReadSByte(); faction.ggroup = reader.ReadSByte(); faction.ally1 = reader.ReadInt32(); faction.ally2 = reader.ReadInt32(); faction.ally3 = reader.ReadInt32(); faction.enemy1 = reader.ReadInt32(); faction.enemy2 = reader.ReadInt32(); faction.enemy3 = reader.ReadInt32(); faction.ptrToNextFactionAtSameHierarchyLevel = reader.ReadInt32(); faction.ptrToFirstChildFaction = reader.ReadInt32(); faction.ptrToParentFaction = reader.ReadInt32(); factions.Add(faction); } }
public bool IsEnemyStatePermanentUntilWarOver(FactionFile.FactionData faction1, FactionFile.FactionData faction2) { if (faction1.region != -1 && faction2.region != -1) { for (int i = 0; i < 11; ++i) { if (borderRegions[(11 * faction1.region) + i] == faction2.region) { return(true); } } } return(false); }
public bool IsFaction2APotentialWarEnemyOfFaction1(int factionID1, int factionID2) { if (factionDict.ContainsKey(factionID1) && factionDict.ContainsKey(factionID2)) { FactionFile.FactionData factionData1 = factionDict[factionID1]; FactionFile.FactionData factionData2 = factionDict[factionID2]; return(factionData1.region != -1 && factionData1.type == (int)FactionFile.FactionTypes.Province && factionData2.region != -1 && factionData2.type == (int)FactionFile.FactionTypes.Province && IsFaction2AnEnemyOfFaction1(factionID1, factionID2) && IsEnemyStatePermanentUntilWarOver(factionData1, factionData2)); } return(false); }
void ReadFactionData(BinaryReader reader) { // Step through factions factions.Clear(); int factionCount = (int)(reader.BaseStream.Length - factionDataOffset) / factionDataLength; for (int i = 0; i < factionCount; i++) { FactionFile.FactionData faction = new FactionFile.FactionData(); reader.BaseStream.Position = factionDataOffset + (i * factionDataLength); faction.type = reader.ReadByte(); faction.region = reader.ReadSByte(); faction.ruler = reader.ReadSByte(); faction.name = FileProxy.ReadCString(reader, 26); faction.rep = reader.ReadInt16(); faction.power = reader.ReadInt16(); faction.id = reader.ReadInt16(); faction.vam = reader.ReadInt16(); faction.flags = reader.ReadInt16(); reader.BaseStream.Position += 8; // Skip 8 unknown bytes faction.flat1 = reader.ReadInt16(); faction.flat2 = reader.ReadInt16(); faction.face = reader.ReadSByte(); reader.BaseStream.Position += 1; // Second face index is always -1 faction.race = reader.ReadSByte(); faction.sgroup = reader.ReadSByte(); faction.ggroup = reader.ReadSByte(); faction.ally1 = reader.ReadInt32(); faction.ally2 = reader.ReadInt32(); faction.ally3 = reader.ReadInt32(); faction.enemy1 = reader.ReadInt32(); faction.enemy2 = reader.ReadInt32(); faction.enemy3 = reader.ReadInt32(); reader.BaseStream.Position += 12; // Skip 12 unknown bytes factions.Add(faction); } }
/// <summary> /// Change reputation value by amount. /// </summary> public bool ChangeReputation(int factionID, int amount, bool propagate = false) { if (factionDict.ContainsKey(factionID)) { FactionFile.FactionData factionData = factionDict[factionID]; factionData.rep = Mathf.Clamp(factionData.rep + amount, minReputation, maxReputation); factionDict[factionID] = factionData; return(true); } if (propagate) { // TODO: Propagate reputation changes to related factions } return(false); }
/// <summary> /// Recursively checks if faction2 is related to faction1 or to its parents. /// </summary> public bool IsFaction2RelatedToFaction1(int factionID1, int factionID2) { if (GetFaction2RelationToFaction1(factionID1, factionID2) > -1 || IsFaction2AnAllyOfFaction1(factionID1, factionID2) || IsFaction2AnEnemyOfFaction1(factionID1, factionID2)) { return(true); } FactionFile.FactionData factionData1 = factionDict[factionID1]; if (factionData1.parent != 0) { return(IsFaction2RelatedToFaction1(factionData1.parent, factionID2)); } return(false); }
/// <summary> /// Recursively propagate reputation changes to affiliated factions using parent/child faction relationships. /// </summary> /// <param name="factionData">Faction data of parent faction node to change rep for it and children.</param> /// <param name="factionID">Faction ID of faction where rep change was initiated.</param> /// <param name="amount">Amount to change reputation. (half applied to all but init and questor factions)</param> public void PropagateReputationChange(FactionFile.FactionData factionData, int factionID, int amount) { // Do full reputation change for specific faction, a root parent, and questor npcs. Then half reputation change for all other factions in hierarchy ChangeReputation(factionData.id, (factionData.id == factionID || factionData.parent == 0 || questorIds.Contains((GuildNpcServices)factionData.id)) ? amount : amount / 2); // Recursively propagate reputation changes to all child factions if (factionData.children != null) { foreach (int id in factionData.children) { if (factionDict.ContainsKey(id)) { PropagateReputationChange(factionDict[id], factionID, amount); } } } }
// Creates an NPC based on faction type // This can yield some strange results - even Daggerfall uses it selectively // Some guesses have been made and blanks filled in as not all faction types even exist in game // For example, faction types 11, 12, 13 have no matching faction type and are never used in quests // Redirecting these to known and similar factions to ensure an NPC is created over crashing the game void SetupFactionTypeNPC(string factionTypeName) { // Get faction data int factionID = GetFactionTypeFactionID(factionTypeName); if (factionID != -1) { FactionFile.FactionData factionData = GetFactionData(factionID); // Setup Person resource this.factionData = factionData; } else { Debug.LogErrorFormat("SetupFactionTypeNPC() failed to setup {0}", factionTypeName); } }
// Create an NPC aligned to a specific faction void SetupFactionAllianceNPC(string factionAllianceName) { // Get faction data // This also comes from a specific factionID int factionID = GetIndividualFactionID(factionAllianceName); if (factionID != -1) { FactionFile.FactionData factionData = GetFactionData(factionID); // Setup Person resource this.factionData = factionData; } else { Debug.LogErrorFormat("SetupFactionAllianceNPC() failed to setup {0}", factionAllianceName); } }
/// <summary> /// Gets faction data from faction ID. /// </summary> /// <param name="factionID">Faction ID.</param> /// <param name="factionDataOut">Receives faction data.</param> /// <returns>True if successful.</returns> public bool GetFactionData(int factionID, out FactionFile.FactionData factionDataOut) { // Reset if no faction data available if (factionDict.Count == 0) { Reset(); } // Try to get requested faction factionDataOut = new FactionFile.FactionData(); if (factionDict.ContainsKey(factionID)) { factionDataOut = factionDict[factionID]; return(true); } return(false); }
/// <summary> /// Get faction2 relation to faction1. Returns: /// -1 if factions are unrelated /// 0 if factions are the same /// 1 if faction2 is the parent of faction1 /// 2 if faction1 and faction2 share the same parent /// 3 if faction2 is a child of faction1 /// </summary> public int GetFaction2RelationToFaction1(int factionID1, int factionID2) { if (factionDict.ContainsKey(factionID1) && factionDict.ContainsKey(factionID2)) { // Faction1 and faction2 are the same if (factionID1 == factionID2) { return(0); } FactionFile.FactionData factionData1 = factionDict[factionID1]; while (factionData1.parent != 0) { // One of faction1 ancestor is faction2 if (factionData1.parent == factionID2) { return(1); } factionData1 = factionDict[factionData1.parent]; } FactionFile.FactionData factionData2 = factionDict[factionID2]; while (factionData2.parent != 0) { // One of faction2 ancestor is faction1 if (factionData2.parent == factionID1) { return(3); } factionData2 = factionDict[factionData2.parent]; } // Faction1 and faction2 share the same ancestor if (factionData1.id != factionID1 && factionData2.id != factionID2 && factionData1.id == factionData2.id) { return(2); } } return(-1); }
/// <summary> /// Check whether faction 2 is in faction 1's enemy list. /// </summary> public bool IsFaction2AnEnemyOfFaction1(int factionID1, int factionID2) { if (factionDict.ContainsKey(factionID1) && factionDict.ContainsKey(factionID2)) { FactionFile.FactionData factionData1 = factionDict[factionID1]; int[] enemiesOf1 = { factionData1.enemy1, factionData1.enemy2, factionData1.enemy3 }; for (int i = 0; i < 3; ++i) { if (factionID2 == enemiesOf1[i]) { return(true); } } } return(false); }