- Site Map >
- Modding and Creation >
- Sims 3 Creation >
- Modding Discussion >
- (Finished) Custom OpportunityLoader
- Site Map >
- Modding and Creation >
- Sims 3 Creation >
- Modding Discussion >
- (Finished) Custom OpportunityLoader
Replies: 3 (Who?), Viewed: 707 times.
#1
10th May 2021 at 12:32 PM
Last edited by Lyralei : 13th Jul 2021 at 1:27 PM.
Reason: Named it custom because that just sounds cooler :p
Posts: 3,860
Thanks: 8548 in 67 Posts
(Finished) Custom OpportunityLoader
Hey everyone!I was talking to some people about adding your own custom opportunities and been using the Skill's way of parsing the xml file as a reference After a lot of wrestling (And you might have known that if you saw the first version of this thread ;p) It's currently working flawlessly!
Before we get to the code, I really want to thank Arsil for creating a function that lets you add Enum Values to Enums! This isn't usually possible due to the nature of Enums
Here's the code for loading (And even specifically firing a particular opportunity, which can also be written to work for EA's own Opportunity!)
NOTE: THIS is SUPER long! So make sure to mentally prepare yourself for that :p
Code:
using Sims3.Gameplay;
using Sims3.Gameplay.Actors;
using Sims3.Gameplay.Autonomy;
using Sims3.Gameplay.EventSystem;
using Sims3.Gameplay.Opportunities;
using Sims3.Gameplay.Rewards;
using Sims3.Gameplay.Roles;
using Sims3.Gameplay.Utilities;
using Sims3.SimIFace;
using Sims3.UI;
using Sims3.UI.Hud;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Loaders
{
public class OpportunityLoader
{
[Tunable]
public static bool kInstantiator = false;
static OpportunityLoader()
{
Simulator.PostInit += PostInit;
}
// Name this OpportunityList something different! just for the sake of organising
public static Dictionary<OpportunityNames, Opportunity> sYourCustomOpportunityList = new Dictionary<OpportunityNames, Opportunity>();
private static void PostInit(object sender, EventArgs e)
{
// Hash string 64 you put in the name of your xml. Make sure that the S3PE xml file's instance ID is the hash of the name.
// For example, if the name would be "opportunity_Lyralei" then the instance ID would be 0x623CC4619BB991E5
// To get the name > hash in s3pe, simply go to Tools > FNVhash and ALWAYS get the FNV64 instance for XML files.
try
{
XmlDbData xmlDbData = XmlDbData.ReadData(new ResourceKey(ResourceUtils.HashString64("opportunity_Lyralei"), 0x0333406C, 0x0), false);
// Call our function to parse the xml data.
ParseCustomopportunityData(xmlDbData, true);
SimpleMessageDialog.Show("Custom Opportunity loader", "Loaded!");
}
catch(Exception ex)
{
// If there is anything wrong with the XML, you'll get an debug error
SimpleMessageDialog.Show("Custom Opportunity OnPreload", ex.ToString());
}
}
public static void ParseCustomopportunityData(XmlDbData customOpportunityData, bool ShouldIgnoreEAsOppTypes)
{
XmlDbTable xmlDbTable = null;
// If the table can't be found (or rather, the <OpportunitiesSetup> tag ) then stop the function.
if(!customOpportunityData.Tables.TryGetValue("OpportunitiesSetup", out xmlDbTable))
{
print("Couldn't find opportunitiesSetup table in XML");
return;
}
foreach (XmlDbRow row in xmlDbTable.Rows)
{
// As we can't ever expect that the user ever screws up a row, it's probably good to keep this in.
if(row == null)
{
continue;
}
// create/instantiate a new opportunity, as we're currently going to make our opportunity!
Opportunity.OpportunitySharedData opportunitySharedData = new Opportunity.OpportunitySharedData();
// Get the GUID tag and read the value
string @string = row.GetString("GUID");
if(string.IsNullOrEmpty(@string))
{
// If GUID is empty, don't bother checking.
print("Couldn't find GUID in XML");
return;
}
// Split the GUID lines in half where the space is and parse this into an array. Name becomes [0], the guidkey becomes [1]
string[] guidToGet;
ParserFunctions.ParseCommaSeparatedString(@string, out guidToGet);
// Using ARSIL's wonderful function to add the enum value to the OpportunityNames enum.
AddEnumValue<OpportunityNames>(guidToGet[0], Convert.ToUInt64(guidToGet[1], 16));
// After parsing it into the AddEnumValue function, we also want to check if the enum is now present in the OpportunityNames enum. If it isn't then we want to stop the function as we'd have broken data anyways.
if(!ParserFunctions.EnumContainsValueType(typeof(OpportunityNames), Convert.ToUInt64(guidToGet[1], 16) , true))
{
print("Guid couldn't be added to the enum for some reason. Make sure your Instance is a 64-bit instance id, and that your notation is sort of like: lyraleiTest,0x8B4F9ECFC92D53C9. Also make sure if you have more Opportunities that they don't all have the same identical GUID!");
return;
}
// Since we're now 100% sure the guid is included as an enum entry, we want to add the GUID to the generic manager, which is a dictionary that is always used to check any opportunity data outside of the OpportunityManager class.
opportunitySharedData.mGuid = GenericManager<OpportunityNames, Opportunity, Opportunity>.ParseGuid(guidToGet[0]);
/* -----------------------------------------------
*
* Anything at this point is all EA's parsing data and I didn't change anything at all! Well, except for eventually adding our opportunity to your own list so you can easily get a random item from your list from your interaction
*
* ------------------------------------------------
*/
// Setup our icon. I did notice you don't always need a comma to split it, but kept the support in anyways
string string2 = row.GetString("Icon");
if (!string.IsNullOrEmpty(string2))
{
string[] array;
ParserFunctions.ParseCommaSeparatedString(string2, out array);
uint group = 0u;
if (array.Length > 1)
{
ProductVersion version;
ParserFunctions.TryParseEnum(array[1], out version, ProductVersion.BaseGame);
group = ResourceUtils.ProductVersionToGroupId(version);
}
opportunitySharedData.mIconKey = ResourceKey.CreatePNGKey(array[0], group);
}
ParserFunctions.TryParseEnum(row["AcceptDialogDescriptionBackground"], out opportunitySharedData.mAcceptDialogDescriptionBackground, OpportunityDialog.DescriptionBackgroundType.NotSet);
ParserFunctions.TryParseEnum(row["RepeatLevel"], out opportunitySharedData.mRepeatLevel, Repeatability.Undefined);
opportunitySharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.Ordered, row.GetBool("IsOrdered"));
opportunitySharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.Counted, row.GetBool("IsCounted"));
opportunitySharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.Totaled, row.GetBool("IsTotaled"));
opportunitySharedData.mCountOrTotal = row.GetFloat("CountOrTotal", 0f);
string a = row["Timeout"];
if (a != string.Empty)
{
ParserFunctions.TryParseEnum(row["Timeout"], out opportunitySharedData.mTimeout, Opportunity.OpportunitySharedData.TimeoutCondition.None);
}
if (opportunitySharedData.mTimeout == Opportunity.OpportunitySharedData.TimeoutCondition.SimTime)
{
opportunitySharedData.mTimeoutData = ParserFunctions.ParseTime(row.GetString("TimeoutData"));
}
else
{
opportunitySharedData.mTimeoutData = row.GetFloat("TimeoutData");
opportunitySharedData.mTimeoutEnd = ParserFunctions.ParseTime(row.GetString("TimeoutEnd"));
}
string text = row["Loss"];
if (text != string.Empty)
{
ParserFunctions.TryParseEnum(text, out opportunitySharedData.mLoss, Opportunity.OpportunitySharedData.TimeoutCondition.None);
}
if (opportunitySharedData.mLoss == Opportunity.OpportunitySharedData.TimeoutCondition.SimTime)
{
opportunitySharedData.mLossData = ParserFunctions.ParseTime(row.GetString("LossData"));
}
else
{
opportunitySharedData.mLossData = row.GetFloat("LossData");
}
opportunitySharedData.mChanceToGetOnPhone = row.GetFloat("ChanceToGetOnPhone");
row.TryGetEnum("Availability", out opportunitySharedData.mOpportunityAvailability, OpportunityAvailability.Undefined);
opportunitySharedData.mOpportunityType = OpportunityType.Undefined;
row.TryGetEnum("OpportunityType", out opportunitySharedData.mOpportunityType, OpportunityType.Undefined);
opportunitySharedData.mEventList = OpportunityManager.ParseEvents(row, opportunitySharedData.mGuid);
List<string> stringList = row.GetStringList("CompletionEvent", ',', false);
if (stringList.Count == 0)
{
opportunitySharedData.mCompletionEvent = null;
}
else
{
opportunitySharedData.mCompletionEvent = OpportunityManager.ParseOneEvent(stringList, row, opportunitySharedData.mGuid);
if (opportunitySharedData.mOpportunityType == OpportunityType.Career)
{
opportunitySharedData.mCompletionEvent.mEventDelegate = Opportunity.ProcessCompletionEventDelegateCareer;
}
else if (opportunitySharedData.mOpportunityType == OpportunityType.Skill)
{
opportunitySharedData.mCompletionEvent.mEventDelegate = Opportunity.ProcessCompletionEventDelegateSkill;
}
else if (opportunitySharedData.mOpportunityType == OpportunityType.Location || opportunitySharedData.mOpportunityType == OpportunityType.Social)
{
opportunitySharedData.mCompletionEvent.mEventDelegate = Opportunity.ProcessCompletionEventDelegateSpecial;
}
else if (opportunitySharedData.mOpportunityType == OpportunityType.AdventureChina)
{
opportunitySharedData.mCompletionEvent.mEventDelegate = Opportunity.ProcessCompletionEventDelegateAdventureChina;
}
else if (opportunitySharedData.mOpportunityType == OpportunityType.AdventureEgypt)
{
opportunitySharedData.mCompletionEvent.mEventDelegate = Opportunity.ProcessCompletionEventDelegateAdventureEgypt;
}
else if (opportunitySharedData.mOpportunityType == OpportunityType.AdventureFrance)
{
opportunitySharedData.mCompletionEvent.mEventDelegate = Opportunity.ProcessCompletionEventDelegateAdventureFrance;
}
else if (opportunitySharedData.mOpportunityType == OpportunityType.SocialGroup)
{
opportunitySharedData.mCompletionEvent.mEventDelegate = Opportunity.ProcessCompletionEventDelegateSocialGroup;
}
else if (opportunitySharedData.mOpportunityType == OpportunityType.DayJob)
{
opportunitySharedData.mCompletionEvent.mEventDelegate = Opportunity.ProcessCompletionEventDelegateDayJob;
}
}
opportunitySharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.AllowPhoneCompletion, row.GetBool("AllowPhoneCompletion"));
string string3 = row.GetString("CompletionProceduralTestFunction");
if (!string.IsNullOrEmpty(string3) && OpportunityManager.sOpportunitiesTestClassType != null)
{
MethodInfo method = OpportunityManager.sOpportunitiesTestClassType.GetMethod(string3, BindingFlags.Static | BindingFlags.Public);
if (method != null)
{
opportunitySharedData.mTargetProceduralTestDelegate = (Delegate.CreateDelegate(typeof(OpportunityTargetProceduralTestDelegate), method) as OpportunityTargetProceduralTestDelegate);
}
}
Opportunity opportunity = null;
string string4 = row.GetString("CustomOpportunityClass");
if (string4.Length > 0)
{
Type type = Type.GetType(string4);
if (type != null)
{
Type[] types = new Type[1]
{
typeof(Opportunity.OpportunitySharedData)
};
ConstructorInfo constructor = type.GetConstructor(types);
object obj = constructor.Invoke(new object[1]
{
opportunitySharedData
});
opportunity = (Opportunity)obj;
}
}
else
{
opportunity = ((!opportunitySharedData.HasFlags(Opportunity.OpportunitySharedData.FlagField.Counted)) ? ((!opportunitySharedData.HasFlags(Opportunity.OpportunitySharedData.FlagField.Totaled)) ? ((!opportunitySharedData.HasFlags(Opportunity.OpportunitySharedData.FlagField.Ordered)) ? new Opportunity(opportunitySharedData) : new OrderedOpportunity(opportunitySharedData)) : new TotaledOpportunity(opportunitySharedData)) : new CountedOpportunity(opportunitySharedData));
}
if (!opportunitySharedData.HasFlags(Opportunity.OpportunitySharedData.FlagField.Ordered))
{
continue;
}
if (opportunity != null)
{
// Opportunity.Guid seems to share the same guid that we extracted from the XML and parsed in opportunitySharedData
//OpportunityNames guid = opportunity.Guid;
GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary.Add((ulong)opportunity.Guid, opportunity);
if(!GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary.ContainsKey((ulong)opportunity.Guid))
{
print("Adding the GUID to the generic manager didn't work unfortunately. Check if there is another error about your GUID as I added some debug reasons for you there ");
}
// If we should ignore EA's default opportunity types, then only load in our custom opportunity to our own list.
if(ShouldIgnoreEAsOppTypes)
{
// CHANGE THIS DICTIONARY NAME IF YOU DECIDED TO RENAME THE DICTIONARY AT THE TOP
sYourCustomOpportunityList.Add(opportunity.Guid, opportunity);
}
// Else go through EA's checks and if literally nothing fits it's requirements, add things to our opportunity list
else
{
if (opportunity.IsLocationBased && opportunity.Guid != OpportunityNames.Special_RestoreGhost && !opportunity.IsEP4LocationBasedOpportunity && !opportunity.IsEP10LocationBasedOpportunity)
{
if (opportunity.IsCelebritySystemOpportunity)
{
if (OpportunityManager.sCelebritySystemOpportunityList == null)
{
OpportunityManager.sCelebritySystemOpportunityList = new Dictionary<OpportunityNames, Opportunity>();
}
OpportunityManager.sCelebritySystemOpportunityList.Add(opportunity.Guid, opportunity);
}
else
{
OpportunityManager.sLocationBasedOpportunityList.Add(opportunity.Guid, opportunity);
}
}
else if (opportunity.IsSkill || opportunity.Guid == OpportunityNames.Special_RestoreGhost)
{
OpportunityManager.sSkillOpportunityList.Add(opportunity.Guid, opportunity);
}
else if (opportunity.IsAdventureChina)
{
OpportunityManager.sAdventureChinaOpportunityList.Add(opportunity.Guid, opportunity);
}
else if (opportunity.IsAdventureEgypt)
{
OpportunityManager.sAdventureEgyptOpportunityList.Add(opportunity.Guid, opportunity);
}
else if (opportunity.IsAdventureFrance)
{
OpportunityManager.sAdventureFranceOpportunityList.Add(opportunity.Guid, opportunity);
}
else if (opportunity.IsCareer && opportunity.ChanceToGetOnPhone > 0f)
{
OpportunityManager.sCareerPhoneCallOpportunityList.Add(opportunity.Guid, opportunity);
}
else if (opportunity.IsDare)
{
OpportunityManager.sDareOpportunityList.Add(opportunity.Guid, opportunity);
}
else if (opportunity.IsSocialGroup)
{
OpportunityManager.sSocialGroupOpportunityList.Add(opportunity.Guid, opportunity);
}
else if (opportunity.IsDayJob)
{
OpportunityManager.sDayJobOpportunityList.Add(opportunity.Guid, opportunity);
}
else
{
sYourCustomOpportunityList.Add(opportunity.Guid, opportunity);
}
}
}
else{
print("Opportunity was considered empty...");
return;
}
}
// Everything here is also EA's code. I'm however parsing the GUID the same way as explained at the top where we're loading in the GUID for the first time.
ParseOpportunityNames(customOpportunityData);
ParseOpportunityRequirements(customOpportunityData);
ParseOpportunitySetup(customOpportunityData);
ParseOpportunityCompletion(customOpportunityData);
}
public static void ParseOpportunityNames(XmlDbData opportunitiesData)
{
XmlDbTable xmlDbTable = null;
opportunitiesData.Tables.TryGetValue("Names", out xmlDbTable);
foreach (XmlDbRow row in xmlDbTable.Rows)
{
string stringGuid = row.GetString("GUID");
if(string.IsNullOrEmpty(stringGuid))
{
print("Couldn't find GUID in XML");
}
string[] guidToGet;
ParserFunctions.ParseCommaSeparatedString(stringGuid, out guidToGet);
AddEnumValue<OpportunityNames>(guidToGet[0], Convert.ToUInt64(guidToGet[1], 16));
OpportunityNames key = GenericManager<OpportunityNames, Opportunity, Opportunity>.ParseGuid(guidToGet[0]);
if (GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary.ContainsKey((ulong)key))
{
Opportunity.OpportunitySharedData sharedData = GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary[(ulong)key].SharedData;
string @string = row.GetString("OpportunityName");
sharedData.mName.Add("Gameplay/Excel/Opportunities/Names:" + @string);
string string2 = row.GetString("OpportunityDescription");
sharedData.mDescription.Add("Gameplay/Excel/Opportunities/Names:" + string2);
string string3 = row.GetString("OpportunityHint");
sharedData.mHint.Add(string.IsNullOrEmpty(string3) ? string.Empty : ("Gameplay/Excel/Opportunities/Names:" + string3));
string string4 = row.GetString("CompletionText");
sharedData.mCompletionText.Add(string.IsNullOrEmpty(string4) ? string.Empty : ("Gameplay/Excel/Opportunities/Names:" + string4));
string string5 = row.GetString("SecondaryCompletionText");
sharedData.mSecondaryCompletionText.Add(string.IsNullOrEmpty(string5) ? string.Empty : ("Gameplay/Excel/Opportunities/Names:" + string5));
string string6 = row.GetString("FailureText");
sharedData.mFailureText.Add(string.IsNullOrEmpty(string6) ? string.Empty : ("Gameplay/Excel/Opportunities/Names:" + string6));
string string7 = row.GetString("ProgressText");
sharedData.mProgressText.Add(string.IsNullOrEmpty(string7) ? string.Empty : ("Gameplay/Excel/Opportunities/Names:" + string7));
}
}
}
public static void ParseOpportunityRequirements(XmlDbData opportunitiesData)
{
XmlDbTable xmlDbTable = null;
opportunitiesData.Tables.TryGetValue("OpportunitiesRequirements", out xmlDbTable);
foreach (XmlDbRow row in xmlDbTable.Rows)
{
string @string = row.GetString("GUID");
if(string.IsNullOrEmpty(@string))
{
print("Couldn't find GUID in XML");
}
string[] guidToGet;
ParserFunctions.ParseCommaSeparatedString(@string, out guidToGet);
AddEnumValue<OpportunityNames>(guidToGet[0], Convert.ToUInt64(guidToGet[1], 16));
OpportunityNames opportunityNames = GenericManager<OpportunityNames, Opportunity, Opportunity>.ParseGuid(guidToGet[0]);
if (GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary.ContainsKey((ulong)opportunityNames))
{
Opportunity.OpportunitySharedData sharedData = GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary[(ulong)opportunityNames].SharedData;
sharedData.mRequirementList = OpportunityManager.ParseRequirements(row, opportunityNames);
List<string> stringList = row.GetStringList("CustomRequirementFunction", ',');
if (stringList.Count > 0 && stringList.Count == 2)
{
sharedData.mRequirementDelegate = (OpportunityRequirementDelegate)OpportunityManager.ParseDelegatePair(stringList[0], stringList[1], typeof(OpportunityRequirementDelegate));
}
}
}
}
public static void ParseOpportunitySetup(XmlDbData opportunitiesData)
{
XmlDbTable xmlDbTable = null;
opportunitiesData.Tables.TryGetValue("OpportunitiesSetup", out xmlDbTable);
foreach (XmlDbRow row in xmlDbTable.Rows)
{
string stringGuid = row.GetString("GUID");
if(string.IsNullOrEmpty(stringGuid))
{
print("Couldn't find GUID in XML");
}
string[] guidToGet;
ParserFunctions.ParseCommaSeparatedString(stringGuid, out guidToGet);
AddEnumValue<OpportunityNames>(guidToGet[0], Convert.ToUInt64(guidToGet[1], 16));
OpportunityNames key = GenericManager<OpportunityNames, Opportunity, Opportunity>.ParseGuid(guidToGet[0]);
if (GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary.ContainsKey((ulong)key))
{
Opportunity opportunity = GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary[(ulong)key];
Opportunity.OpportunitySharedData sharedData = opportunity.SharedData;
string @string = row.GetString("Object");
if (@string != string.Empty)
{
ParserFunctions.TryParseEnum(@string, out sharedData.mSetupObjectType, OpportunityObjectTypes.Undefined);
}
sharedData.mSetupObjectData = row.GetString("ObjectData");
if (sharedData.mSetupObjectType == OpportunityObjectTypes.Keystone)
{
OpportunityManager.sKeystonesThatComeFromAdventures[sharedData.mSetupObjectData] = true;
}
sharedData.mSetupObjectTreasureSpawner = row.GetString("ObjectTreasureSpawner");
sharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.SetupObjectRequiredOnCompletion, row.GetBool("RequiredOnCompletion"));
sharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.DeleteSetupObjectOnCompletion, row.GetBool("DeleteOnCompletion"));
if (!string.IsNullOrEmpty(row.GetString("DeleteOnFailure")))
{
sharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.DeleteSetupObjectOnFailure, row.GetBool("DeleteOnFailure"));
}
else
{
sharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.DeleteSetupObjectOnFailure, sharedData.HasFlags(Opportunity.OpportunitySharedData.FlagField.DeleteSetupObjectOnCompletion));
}
if (sharedData.HasFlags(Opportunity.OpportunitySharedData.FlagField.DeleteSetupObjectOnCompletion))
{
sharedData.HasFlags(Opportunity.OpportunitySharedData.FlagField.DeleteSetupObjectOnFailure);
}
string string2 = row.GetString("ObjectStateForCompletion");
if (string2 != string.Empty)
{
string[] array = null;
if (ParserFunctions.ParseCommaSeparatedString(string2, out array))
{
ParserFunctions.TryParseEnum(array[0], out sharedData.mSetupObjectCompletionState, OpportunityObjectState.Undefined);
if (array.Length == 2)
{
sharedData.mSetupObjectCompletionStateData = array[1];
}
}
}
row.TryGetEnum("CompletionTNSType", out sharedData.mCompletionTNSType, OpportunityCompletionTNSType.Info);
Opportunity.OpportunitySharedData.DestinationInventoryType mDestinationInventoryType = Opportunity.OpportunitySharedData.DestinationInventoryType.SimInventory;
row.TryGetEnum("DestinationInventory", out mDestinationInventoryType, Opportunity.OpportunitySharedData.DestinationInventoryType.SimInventory);
sharedData.mDestinationInventoryType = mDestinationInventoryType;
string string3 = row.GetString("Target");
if (string3 != string.Empty)
{
ParserFunctions.TryParseEnum(string3, out sharedData.mTargetType, OpportunityTargetTypes.Undefined);
}
else if (@string != string.Empty)
{
sharedData.mTargetType = OpportunityTargetTypes.SetupObject;
}
sharedData.mTargetData = row.GetString("TargetData");
string string4 = row.GetString("Source");
if (string4 != string.Empty)
{
ParserFunctions.TryParseEnum(string4, out sharedData.mSourceType, OpportunityTargetTypes.Undefined);
}
sharedData.mSourceData = row.GetString("SourceData");
string string5 = row.GetString("TargetInteractionName");
if (string5 != string.Empty)
{
sharedData.mTargetInteractionName = "Gameplay/Excel/Opportunities/OpportunitiesSetup:" + string5;
}
sharedData.mSocialTextKeys = row.GetStringList("TargetSocialTextKeys", ',');
sharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.TargetInteractionIgnoreTarget, row.GetBool("TargetInteractionIgnoreTarget"));
sharedData.mTargetInteractionLikingToAccept = row.GetFloat("TargetInteractionLikingToAccept");
ParserFunctions.TryParseEnum(row.GetString("TargetRoleRequired"), out sharedData.mTargetRoleRequired, Role.RoleType.None);
string string6 = row.GetString("TargetWorldRequired");
if (string6 == "HomeWorld" || sharedData.mProductVersion == ProductVersion.BaseGame)
{
sharedData.mTargetWorldRequired = WorldName.SunsetValley;
}
else
{
ParserFunctions.TryParseEnum(string6, out sharedData.mTargetWorldRequired, WorldName.Undefined);
}
sharedData.mTargetInteractionLength = row.GetFloat("TargetInteractionLength");
sharedData.mTargetInteractionDays = row.GetString("TargetInteractionDays");
sharedData.mTargetInteractionStartTime = ParserFunctions.ParseTime(row.GetString("TargetInteractionStartTime"));
sharedData.mTargetInteractionEndTime = ParserFunctions.ParseTime(row.GetString("TargetInteractionEndTime"));
string string7 = row.GetString("TargetInteractionCommodity");
if (!string.IsNullOrEmpty(string7))
{
string[] array2 = null;
if (ParserFunctions.ParseCommaSeparatedString(string7, out array2) && array2.Length == 2)
{
ParserFunctions.TryParseEnum(array2[0], out sharedData.mTargetCommodity, CommodityKind.None);
float.TryParse(array2[1], out sharedData.mTargetCommodityDesiredValue);
}
}
string string8 = row.GetString("TargetInteractionItemRequired");
if (string8 != string.Empty)
{
string[] array3 = null;
if (ParserFunctions.ParseSemicolonSeparatedString(string8, out array3, false))
{
for (int i = 0; i < array3.Length; i++)
{
string[] array4 = null;
string names = array3[i].Trim();
if (ParserFunctions.ParseCommaSeparatedString(names, out array4, false))
{
for (int j = 0; j < array4.Length; j++)
{
array4[j] = array4[j].Trim();
}
if (!OpportunityManager.ParseTargetInteractionItemInfo(array4, ref sharedData) && OpportunityManager.sDoValidation && sharedData.mRequiredItems != null)
{
int count = sharedData.mRequiredItems.Count;
}
}
}
}
}
if (sharedData.mRequiredItems != null)
{
int count2 = sharedData.mRequiredItems.Count;
}
sharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.UseCollectibleProgressText, row.GetBool("TargetUseCollectibleProgressText"));
string string9 = row.GetString("TargetDeletionSortingFunction");
string[] array5;
if (!string.IsNullOrEmpty(string9) && OpportunityManager.sOpportunitiesSortClassType != null && ParserFunctions.ParseSemicolonSeparatedString(string9, out array5))
{
int num = array5.Length;
for (int k = 0; k < array5.Length; k++)
{
MethodInfo method = OpportunityManager.sOpportunitiesSortClassType.GetMethod(array5[k], BindingFlags.Static | BindingFlags.Public);
if (method != null)
{
sharedData.mRequiredItems[k].mInventorySortFn = (Delegate.CreateDelegate(typeof(InventoryItemSortDelegate), method) as InventoryItemSortDelegate);
}
}
}
string string10 = row.GetString("TargetInteractionNumberItemsRequired");
string[] array6;
if (ParserFunctions.ParseSemicolonSeparatedString(string10, out array6))
{
int num2 = array6.Length;
for (int l = 0; l < array6.Length; l++)
{
int mTargetInteractionNumberItemsRequiredMin = 0;
int mTargetInteractionNumberItemsRequiredMax = 0;
List<int> list;
ParserFunctions.ParseCommaSeperatedInt(array6[l], out list);
if (list.Count == 1)
{
mTargetInteractionNumberItemsRequiredMin = (mTargetInteractionNumberItemsRequiredMax = list[0]);
}
else if (list.Count == 2)
{
mTargetInteractionNumberItemsRequiredMin = Math.Min(list[0], list[1]);
mTargetInteractionNumberItemsRequiredMax = Math.Max(list[0], list[1]);
}
sharedData.mRequiredItems[l].mTargetInteractionNumberItemsRequiredMin = mTargetInteractionNumberItemsRequiredMin;
sharedData.mRequiredItems[l].mTargetInteractionNumberItemsRequiredMax = mTargetInteractionNumberItemsRequiredMax;
}
}
if (sharedData.mRequiredItems == null || (sharedData.mRequiredItems.Count == 1 && sharedData.mRequiredItems[0].mTargetInteractionNumberItemsRequiredMin <= 0))
{
sharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.UseCollectibleProgressText, false);
}
sharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.TargetInteractionItemsDeleted, row.GetBool("TargetInteractionItemsDeleted"));
string string11 = row.GetString("TargetProceduralDeletionFunction");
if (!string.IsNullOrEmpty(string11) && OpportunityManager.sOpportunitiesDeletionDelegateClassType != null)
{
MethodInfo method2 = OpportunityManager.sOpportunitiesDeletionDelegateClassType.GetMethod(string11, BindingFlags.Static | BindingFlags.Public);
if (method2 != null)
{
sharedData.mTargetProceduralDeletionDelegate = (Delegate.CreateDelegate(typeof(OpportunityTargetProceduralDeletionDelegate), method2) as OpportunityTargetProceduralDeletionDelegate);
}
}
if (OpportunityManager.sDoValidation && sharedData.mRequiredItems != null && sharedData.mRequiredItems.Count > 0)
{
for (int m = 0; m < sharedData.mRequiredItems.Count; m++)
{
if (sharedData.TargetInteractionItemRequired(m) != null && sharedData.mTargetProceduralDeletionDelegate == null && sharedData.mRequiredItems[m].mTargetInteractionNumberItemsRequiredMin <= 0 && sharedData.mGuid != OpportunityNames.EP1_Quest_Necteaux6)
{
OpportunityNames mGuid = sharedData.mGuid;
}
}
}
string string12 = row.GetString("AdventureMapTagTarget");
if (!string.IsNullOrEmpty(string12))
{
string[] array7;
ParserFunctions.ParseCommaSeparatedString(string12, out array7);
if (array7.Length == 2)
{
ParserFunctions.TryParseEnum(array7[0], out sharedData.mAdventureMapTagTargetType, AdventureMapTagTargetType.None);
if (sharedData.mAdventureMapTagTargetType != 0)
{
sharedData.mAdventureMapTagTarget = array7[1];
}
}
}
string string13 = row.GetString("OppMapDagDisplayType");
if (!string.IsNullOrEmpty(string13))
{
ParserFunctions.TryParseEnum(string13, out sharedData.MapTagDisplayType, Opportunity.OpportunitySharedData.MapTagOppDisplayType.Normal);
}
DaysOfTheWeek mEventDay = DaysOfTheWeek.None;
if (row.TryGetEnum("EventDay", out mEventDay, DaysOfTheWeek.None))
{
sharedData.mEventDay = mEventDay;
sharedData.mEventStartTime = ParserFunctions.ParseTime(row.GetString("EventStartTime"));
sharedData.mEventEndTime = ParserFunctions.ParseTime(row.GetString("EventEndTime"));
sharedData.mEventSetup = row.GetString("EventSetup");
}
opportunity.SourceData = sharedData.mSourceData;
opportunity.SourceType = sharedData.mSourceType;
opportunity.TargetData = sharedData.mTargetData;
opportunity.TargetType = sharedData.mTargetType;
}
}
}
public static void ParseOpportunityCompletion(XmlDbData opportunitiesData)
{
XmlDbTable xmlDbTable = null;
opportunitiesData.Tables.TryGetValue("OpportunitiesCompletion", out xmlDbTable);
foreach (XmlDbRow row in xmlDbTable.Rows)
{
string @string = row.GetString("GUID");
if(string.IsNullOrEmpty(@string))
{
print("Couldn't find GUID in XML");
}
string[] guidToGet;
ParserFunctions.ParseCommaSeparatedString(@string, out guidToGet);
AddEnumValue<OpportunityNames>(guidToGet[0], Convert.ToUInt64(guidToGet[1], 16));
OpportunityNames opportunityNames = GenericManager<OpportunityNames, Opportunity, Opportunity>.ParseGuid(guidToGet[0]);
OpportunityNames opportunityNames2 = GenericManager<OpportunityNames, Opportunity, Opportunity>.ParseGuid(row["TriggerOpportunityOnCompletion"]);
if (GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary.ContainsKey((ulong)opportunityNames))
{
Opportunity.OpportunitySharedData sharedData = GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary[(ulong)opportunityNames].SharedData;
sharedData.mCompletionTriggerOpportunity = opportunityNames2;
if (opportunityNames2 != OpportunityNames.Undefined)
{
GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary[(ulong)opportunityNames2].SharedData.mParentOpportunity = opportunityNames;
}
sharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.TriggerQuietly, row.GetBool("TriggerQuietly"));
sharedData.SetFlags(Opportunity.OpportunitySharedData.FlagField.ShowRewardText, row.GetBool("ShowRewardText"));
row.TryGetEnum("ShowRewardForOpp", out sharedData.mShowRewardForOpp, OpportunityNames.Undefined);
sharedData.mCompletionWinChance = row.GetFloat("CompletionWinChance");
sharedData.mWinRewardsList = RewardsManager.ParseRewards(row, OpportunityManager.sCompletionWinRewardColumns, 4);
sharedData.mLossRewardsList = RewardsManager.ParseRewards(row, OpportunityManager.sCompletionLossRewardColumns, 4);
sharedData.mFailureRewardsList = RewardsManager.ParseRewards(row, OpportunityManager.sFailureRewardColumns, 4);
sharedData.mModifierList = RewardsManager.ParseModifiers(row, OpportunityManager.sCompletionModifierColumns, 4);
List<string> stringList = row.GetStringList("CustomOnWinCompletionFunction", ',');
if (stringList.Count > 0)
{
MethodInfo method = OpportunityManager.sOpportunitiesWinCompletionType.GetMethod(stringList[0], BindingFlags.Static | BindingFlags.Public);
if (method != null)
{
sharedData.mWinCompletionDelegate = (Delegate.CreateDelegate(typeof(OpportunityCompletionDelegate), method) as OpportunityCompletionDelegate);
}
}
List<string> stringList2 = row.GetStringList("CustomOnLossCompletionFunction", ',');
if (stringList2.Count > 0)
{
sharedData.mLossCompletionDelegate = (OpportunityCompletionDelegate)OpportunityManager.ParseDelegatePair(stringList2[0], stringList2[1], typeof(OpportunityCompletionDelegate));
}
}
}
foreach (Opportunity value in GenericManager<OpportunityNames, Opportunity, Opportunity>.sDictionary.Values)
{
OpportunityTargetTypes mOriginalSourceType;
string text;
if ((value.SharedData.mSourceType == OpportunityTargetTypes.PreviousSource || value.SharedData.mSourceType == OpportunityTargetTypes.PreviousTarget) && OpportunityManager.FindOriginalSourceOrTarget(value, value.SharedData.mSourceType, out mOriginalSourceType, out text) != null)
{
value.SharedData.mOriginalSourceType = mOriginalSourceType;
}
OpportunityTargetTypes mOriginalTargetType;
string text2;
if ((value.SharedData.mTargetType == OpportunityTargetTypes.PreviousSource || value.SharedData.mTargetType == OpportunityTargetTypes.PreviousTarget) && OpportunityManager.FindOriginalSourceOrTarget(value, value.SharedData.mTargetType, out mOriginalTargetType, out text2) != null)
{
value.SharedData.mOriginalTargetType = mOriginalTargetType;
}
}
}
// public static string[] GetGuid(string guid_key)
// {
// string[] array = guid_key.Split('=');
// if (array.Length == 2)
// {
// return array;
// }
// return null;
// }
public static void TriggerSpecificOpportunity(string name, Sim actor)
{
try
{
string[] guidToGet;
ParserFunctions.ParseCommaSeparatedString(name, out guidToGet);
if(actor != null)
{
if(!actor.OpportunityManager.AddOpportunityNow((OpportunityNames)Convert.ToUInt64(guidToGet[1], 16), true, false))
{
// This is also considered 'false' if the user clicked 'canceled'. So for debugging the option, you might want to add a print statement here, else just leave return.
return;
}
}
else
{
return;
}
}
catch(Exception ex)
{
SimpleMessageDialog.Show("Custom Opportunity trigger", ex.ToString());
}
}
public static void print(string entry)
{
SimpleMessageDialog.Show("Custom Opportunity loader", entry);
}
// Arsil's enum function.
public static void AddEnumValue<T>(string key, object value) where T : struct
{
Type typeFromHandle = typeof(T);
EnumParser enumParser;
if (!ParserFunctions.sCaseInsensitiveEnumParsers.TryGetValue(typeFromHandle, out enumParser))
{
enumParser = new EnumParser(typeFromHandle, true);
ParserFunctions.sCaseInsensitiveEnumParsers.Add(typeFromHandle, enumParser);
}
EnumParser enumParser2;
if (!ParserFunctions.sCaseSensitiveEnumParsers.TryGetValue(typeFromHandle, out enumParser2))
{
enumParser2 = new EnumParser(typeFromHandle, false);
ParserFunctions.sCaseSensitiveEnumParsers.Add(typeFromHandle, enumParser2);
}
if (!enumParser.mLookup.ContainsKey(key.ToLowerInvariant()) && !enumParser2.mLookup.ContainsKey(key))
{
enumParser.mLookup.Add(key.ToLowerInvariant(), value);
enumParser2.mLookup.Add(key, value);
}
}
}
}
And of course, an example use! Sims:
Code:
public class OpportunityTriggerTester : ImmediateInteraction<Sim, Sim> { public class Definition : InteractionDefinition<Sim, Sim, OpportunityTriggerTester> { public override bool Test(Sim a, Sim target, bool isAutonomous, ref GreyedOutTooltipCallback greyedOutTooltipCallback) { return true; } public override string GetInteractionName(Sim actor, Sim target, InteractionObjectPair iop) { return "Test opportunity"; } } public static InteractionDefinition Singleton = new Definition(); public override bool Run() { base.StandardEntry(); // UNCOMMENT ONLY ONE METHOD! ELSE YOU WILL TRIGGER 2 OPPORTUNITY DIALOGUES // Let's trigger our SPECIFIC opportunity! // OpportunityLoader.TriggerSpecificOpportunity("lyraleiTest,0x8B4F9ECFC92D53C9", base.Actor); // Let's trigger something random from our 'sYourCustomOpportunityList' dictionary... Comment this out if you're testing this for the first time! //Opportunity opportunity = RandomUtil.GetRandomObjectFromDictionary(OpportunityLoader.sYourCustomOpportunityList); //base.Actor.OpportunityManager.AddOpportunityNow(opportunity.Guid, true, false); base.StandardExit(); return true; } }
Objects:
Code:
public class OpportunityTriggerTester : ImmediateInteraction<Sim, SewingTable> { public class Definition : InteractionDefinition<Sim, SewingTable, OpportunityTriggerTester> { public override bool Test(Sim a, SewingTable target, bool isAutonomous, ref GreyedOutTooltipCallback greyedOutTooltipCallback) { return true; } public override string GetInteractionName(Sim actor, SewingTable target, InteractionObjectPair iop) { return "Test opportunity for sim using object"; } } public static InteractionDefinition Singleton = new Definition(); public override bool Run() { base.StandardEntry(); // UNCOMMENT ONLY ONE METHOD! ELSE YOU WILL TRIGGER 2 OPPORTUNITY DIALOGUES // Let's trigger our SPECIFIC opportunity! // OpportunityLoader.TriggerSpecificOpportunity("lyraleiTest,0x8B4F9ECFC92D53C9", base.Actor); // Let's trigger something random from our 'sYourCustomOpportunityList' dictionary... Comment this out if you're testing this for the first time! //Opportunity opportunity = RandomUtil.GetRandomObjectFromDictionary(OpportunityLoader.sYourCustomOpportunityList); //base.Actor.OpportunityManager.AddOpportunityNow(opportunity.Guid, true, false); base.StandardExit(); return true; } }
I've added the Solution with it's examples and code in the thread as well! And the example package so you know how to use it with the XML!
If you have any questions about adding your own custom opportunity or my code sets your computer on fire, then let me know too!
Attached files:
OpportunityLoader.zip (366.0 KB, 17 downloads) | ||
Description: Source Code VSproject | ||
OpportunityLoaderExample.zip (15.5 KB, 15 downloads) | ||
Description: Example package |
Advertisement
Field Researcher
#2
13th May 2021 at 8:27 AM
Posts: 224
Thanks: 996 in 7 Posts
There is a function to add enums?
Could you give me the link to that? I would really like to create custom Commodity Types to use in Ituns.
Could you give me the link to that? I would really like to create custom Commodity Types to use in Ituns.
#3
13th May 2021 at 12:14 PM
Last edited by Lyralei : 13th May 2021 at 12:26 PM.
Posts: 3,860
Thanks: 8548 in 67 Posts
Quote: Originally posted by KittyTheSnowcat
There is a function to add enums? Could you give me the link to that? I would really like to create custom Commodity Types to use in Ituns. |
It's actually inlined in the code, but here it is again, since it's a loooot of code, so It's hard to miss:
Code:
Again, this is Arsil's way of parsing a custom Enum, so all credits goes to him! And Zoe for pointing it out :p
// Arsil's enum function.
public static void AddEnumValue<T>(string key, object value) where T : struct
{
Type typeFromHandle = typeof(T);
EnumParser enumParser;
if (!ParserFunctions.sCaseInsensitiveEnumParsers.TryGetValue(typeFromHandle, out enumParser))
{
enumParser = new EnumParser(typeFromHandle, true);
ParserFunctions.sCaseInsensitiveEnumParsers.Add(typeFromHandle, enumParser);
}
EnumParser enumParser2;
if (!ParserFunctions.sCaseSensitiveEnumParsers.TryGetValue(typeFromHandle, out enumParser2))
{
enumParser2 = new EnumParser(typeFromHandle, false);
ParserFunctions.sCaseSensitiveEnumParsers.Add(typeFromHandle, enumParser2);
}
if (!enumParser.mLookup.ContainsKey(key.ToLowerInvariant()) && !enumParser2.mLookup.ContainsKey(key))
{
enumParser.mLookup.Add(key.ToLowerInvariant(), value);
enumParser2.mLookup.Add(key, value);
}
}
// Usage would be something like:
// Do convert "MyCustomCommodityType" to a FNV64 in S3PE (Tools > FNV64) and then the converter below will convert it to a 16-numeric decimal for you
AddEnumValue<OpportunityNames>("MyCustomCommodityType", Convert.ToUInt64(0x5BC056891B5FA161, 16));
#4
4th Sep 2022 at 9:20 AM
Last edited by FloTheory : 4th Sep 2022 at 11:09 AM.
Hi Lyralei, thank you to both Arsil and you for joining forces and giving us this new means to create custom opportunities !
I'm loading up your solution in SharpDevelop and It doesn't build because of "PostInit" apparently.
The first thing that comes to my mind is : are you using custom libraries maybe ?
Edit : problem solved thanks to MissPat who gave me your custom DLLs !
I'm loading up your solution in SharpDevelop and It doesn't build because of "PostInit" apparently.
The first thing that comes to my mind is : are you using custom libraries maybe ?
Edit : problem solved thanks to MissPat who gave me your custom DLLs !
Who Posted
|