public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { //WTM: Note: I'd prefer a single method call obviously, but this is the best solution I can imagine. //https://www.creationkit.com/index.php?title=GetIsPlayableRace /* * TES5ObjectCall calledOnRaceName = objectCallFactory.CreateObjectCall(objectCallFactory.CreateObjectCall(calledOn, "GetRace"), "GetName"); * string[] playableRaces = new string[] { "Altmer", "Argonian", "Bosmer", "Breton", "Dunmer", "Imperial", "Khajiit", "Nord", "Orc", "Redguard" }; * ITES5Expression? accumulatedStatement = null; * foreach (string race in playableRaces) * { * TES5ComparisonExpression newExpression = TES5ExpressionFactory.CreateComparisonExpression(calledOnRaceName, TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5String(race)); * if (accumulatedStatement == null) * { * accumulatedStatement = newExpression; * } * else * { * accumulatedStatement = TES5ExpressionFactory.CreateLogicalExpression(accumulatedStatement, TES5LogicalExpressionOperator.OPERATOR_OR, newExpression); * } * } * if (accumulatedStatement == null) { throw new NullableException(nameof(accumulatedStatement)); } * return accumulatedStatement; */ //Actor.GetRace().IsPlayable() might work: //https://www.creationkit.com/index.php?title=Race_Script return(objectCallFactory.CreateObjectCall(objectCallFactory.CreateObjectCall(calledOn, "GetRace"), "IsPlayable")); }
public ITES5ValueCodeChunk CreateLogCall(TES4Function function, string?reason = null) { string message = "OBScript called " + function.FunctionCall.FunctionName + "(" + string.Join(", ", function.Arguments.Select(a => a.StringValue)) + "), but script converter didn't know a conversion." + (reason != null ? " " + reason : ""); TES5CodeChunkCollection codeChunks = new TES5CodeChunkCollection(); TES5ObjectCallArguments arguments = new TES5ObjectCallArguments() { new TES5String(message) }; codeChunks.Add(objectCallFactory.CreateObjectCall(TES5StaticReferenceFactory.Debug, "Trace", arguments)); #if UNKNOWN_FUNCTIONS_MESSAGE_BOX codeChunks.Add(objectCallFactory.CreateObjectCall(TES5StaticReferenceFactory.Debug, "MessageBox", arguments));//Debug.Notification might be a useful alternative. #endif return(codeChunks); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { //WTM: Note: Just like DisablePlayerControlsFactory, I want to emulate this: //https://cs.elderscrolls.com/index.php?title=DisablePlayerControls //Player cannot move, wait, activate anything, or access his journal interface. string[] functionNames = new string[] { "IsMovementControlsEnabled", "IsMenuControlsEnabled" /*closest to "IsWaitControlsEnabled" I could find*/, "IsActivateControlsEnabled", "IsJournalControlsEnabled" }; TES5ObjectCall[] objectCalls = functionNames.Select(f => objectCallFactory.CreateObjectCall(TES5StaticReferenceFactory.Game, f)).ToArray(); ITES5ValueCodeChunk?accumulatedStatement = null; foreach (TES5ObjectCall objectCall in objectCalls) { if (accumulatedStatement == null) { accumulatedStatement = objectCall; } else { accumulatedStatement = TES5ExpressionFactory.CreateLogicalExpression(accumulatedStatement, TES5LogicalExpressionOperator.OPERATOR_AND, objectCall); } } if (accumulatedStatement == null) { throw new NullableException(nameof(accumulatedStatement)); } TES5ComparisonExpression negatedExpression = TES5ExpressionFactory.CreateComparisonExpression(accumulatedStatement, TES5ComparisonExpressionOperator.OPERATOR_NOT_EQUAL, new TES5Bool(true)); return(negatedExpression); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { TES5PlayerReference player = TES5ReferenceFactory.CreateReferenceToPlayer(globalScope); TES5ObjectCallArguments arguments = objectCallArgumentsFactory.CreateArgumentList(function.Arguments, codeScope, globalScope, multipleScriptsScope); return(objectCallFactory.CreateObjectCall(player, "SetDisplayName", arguments)); }
private TES5FunctionCodeBlock GetGetArmorRatingOfWornFormFunctionCodeBlock(ITES5Referencer calledOn, TES5CodeScope codeScope) { TES5FunctionCodeBlock functionCodeBlock = new TES5FunctionCodeBlock(new TES5FunctionScope(functionName), TES5CodeScopeFactory.CreateCodeScope(codeScope.LocalScope), TES5BasicType.T_INT, false, false); TES5SignatureParameter slotMaskParameter = new TES5SignatureParameter("slotMask", TES5BasicType.T_INT, true); functionCodeBlock.FunctionScope.AddParameter(slotMaskParameter); TES5ObjectCall getWornForm = GetGetWornFormObjectCall(calledOn, TES5ReferenceFactory.CreateReferenceToVariableOrProperty(slotMaskParameter)); TES5LocalVariable wornFormVariable = new TES5LocalVariable("wornForm", TES5BasicType.T_ARMOR); functionCodeBlock.CodeScope.AddVariable(wornFormVariable); TES5Reference wornFormVariableReference = TES5ReferenceFactory.CreateReferenceToVariableOrProperty(wornFormVariable); functionCodeBlock.AddChunk(TES5VariableAssignationFactory.CreateAssignation(wornFormVariableReference, getWornForm)); functionCodeBlock.AddChunk(new TES5Return(objectCallFactory.CreateObjectCall(wornFormVariableReference, "GetArmorRating"))); return(functionCodeBlock); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { //WTM: Note: This always uses the player to send the tresspass alarm. This might mean the player ultimately sends the trespass alarm for the player. //Oblivion doesn't seem to have a sender, only a trespasser. TES5ObjectCallArguments arguments = objectCallArgumentsFactory.CreateArgumentList(function.Arguments, codeScope, globalScope, multipleScriptsScope); return(objectCallFactory.CreateObjectCall(TES5ReferenceFactory.CreateReferenceToPlayer(globalScope), function.FunctionCall.FunctionName, arguments)); }
public static void GenerateINFOAddTopicScripts(ESMAnalyzer esmAnalyzer, BuildTracker buildTracker, IBuildTarget tifBuildTarget) { TES5TypeInferencer typeInferencer = new TES5TypeInferencer(esmAnalyzer); TES5ObjectCallFactory objectCallFactory = new TES5ObjectCallFactory(typeInferencer); TES4TopicsToTES5GlobalVariableFinder globalVariableFinder = new TES4TopicsToTES5GlobalVariableFinder(); TES5GlobalVariables globalVariables = esmAnalyzer.GlobalVariables; var builtTIFs = buildTracker.GetBuiltScripts(BuildTargetFactory.TIFName); foreach (TES4Record infoRecord in esmAnalyzer.GetRecords().Where(r => r.RecordType == TES4RecordType.INFO)) { TES4SubrecordData[] names = infoRecord.GetSubrecords("NAME").ToArray(); if (names.Any()) { string fragment0Name = TES5FragmentFactory.GetFragmentName(0); string nameTES5FormIDHex = (infoRecord.FormID + 0x01000000).ToString("x8"); string scriptName = TES5ReferenceFactory.tif_Prefix + "_" + nameTES5FormIDHex; TES5Script? infoTIF = builtTIFs.Where(s => s.Key == scriptName).Select(s => s.Value.Script).FirstOrDefault(); TES5FunctionCodeBlock fragment0; if (infoTIF != null) { fragment0 = infoTIF.BlockList.Blocks.OfType <TES5FunctionCodeBlock>().Where(b => b.BlockName == fragment0Name).First(); } else { TES5ScriptHeader scriptHeader = TES5ScriptHeaderFactory.GetFromCacheOrConstructByBasicType(scriptName, TES5BasicType.T_TOPICINFO, TES5TypeFactory.TES4_Prefix, true); TES5GlobalScope globalScope = new TES5GlobalScope(scriptHeader); TES5FunctionScope functionScope = new TES5FunctionScope(fragment0Name); functionScope.AddParameter(new TES5SignatureParameter("akSpeakerRef", TES5BasicType.T_OBJECTREFERENCE, true)); TES5LocalScope localScope = new TES5LocalScope(functionScope); TES5CodeScope codeScope = TES5CodeScopeFactory.CreateCodeScope(localScope); fragment0 = new TES5FunctionCodeBlock(functionScope, codeScope, TES5VoidType.Instance, false, true); TES5BlockList blockList = new TES5BlockList() { fragment0 }; infoTIF = new TES5Script(globalScope, blockList, true); string outputPath = tifBuildTarget.GetTranspileToPath(scriptName); TES5Target target = new TES5Target(infoTIF, outputPath); buildTracker.RegisterBuiltScript(tifBuildTarget, target); } foreach (TES4SubrecordData name in names) { int formID = infoRecord.ExpandBytesIntoFormID(name); TES4Record addedTopic = esmAnalyzer.GetRecordByFormID(formID); Tuple <int, string>?globalVariable = globalVariableFinder.GetGlobalVariableNullable(addedTopic.FormID); string globalVariableEditorID = globalVariable != null ? globalVariable.Item2 : globalVariableFinder.GetGlobalVariableEditorID(addedTopic.GetEditorID()); Nullable <int> globalVariableTES5FormID = globalVariable != null ? globalVariable.Item1 : (Nullable <int>)null; TES5Property topicAddedProperty = TES5PropertyFactory.ConstructWithTES5FormID(globalVariableEditorID, TES5BasicType.T_GLOBALVARIABLE, globalVariableEditorID, globalVariableTES5FormID); infoTIF.GlobalScope.AddPropertyIfNotExists(topicAddedProperty); TES5Reference topicAddedReference = TES5ReferenceFactory.CreateReferenceToVariableOrProperty(topicAddedProperty); fragment0.AddChunk(objectCallFactory.CreateObjectCall(topicAddedReference, "SetValueInt", new TES5ObjectCallArguments() { new TES5Integer(1) })); } } } }
public List <ITES5CodeChunk> GenerateObjectiveHandling(ITES5CodeBlock codeBlock, TES5GlobalScope globalScope, List <int> stageMap) { List <ITES5CodeChunk> result = new List <ITES5CodeChunk>(); //WTM: Change: if (!stageMap.Any()) { return(result); } TES5LocalVariable castedToQuest = new TES5LocalVariable("__temp", TES5BasicType.T_QUEST);//WTM: Note: Why is this variable even necessary? TES5Reference referenceToTemp = TES5ReferenceFactory.CreateReferenceToVariableOrProperty(castedToQuest); TES5SelfReference questSelfReference = TES5ReferenceFactory.CreateReferenceToSelf(globalScope); TES5VariableAssignation tempQuestAssignation = TES5VariableAssignationFactory.CreateAssignation(referenceToTemp, questSelfReference); result.Add(tempQuestAssignation); TES5LocalScope localScope = codeBlock.CodeScope.LocalScope; localScope.AddVariable(castedToQuest); int i = 0; foreach (var stageTargetState in stageMap) { TES5Integer targetIndex = new TES5Integer(i); TES5ObjectCallArguments displayedArguments = new TES5ObjectCallArguments() { targetIndex }; TES5ObjectCall isObjectiveDisplayed = objectCallFactory.CreateObjectCall(referenceToTemp, "IsObjectiveDisplayed", displayedArguments, inference: false); int isObjectiveDisplayedArgument = stageTargetState != 0 ? 0 : 1; TES5ComparisonExpression expression = TES5ExpressionFactory.CreateComparisonExpression(isObjectiveDisplayed, TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5Integer(isObjectiveDisplayedArgument)); TES5ObjectCallArguments arguments = new TES5ObjectCallArguments() { targetIndex, new TES5Integer(1) }; string setObjectiveFunction = stageTargetState != 0 ? "SetObjectiveDisplayed" : "SetObjectiveCompleted"; TES5ObjectCall setObjectiveObjectCall = objectCallFactory.CreateObjectCall(referenceToTemp, setObjectiveFunction, arguments, inference: false); TES5Branch branch = TES5BranchFactory.CreateSimpleBranch(expression, localScope); branch.MainBranch.CodeScope.AddChunk(setObjectiveObjectCall); result.Add(branch); ++i; } return(result); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { /* * //evaluated in random compile time, cannot assure all inference has happened already. * TES5Bool boolean = new TES5Bool(TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(calledOn.TES5Type, TES5BasicType.T_ACTOR)); * return boolean; */ //WTM: Change: Added: TES5ObjectCall actorTypeNPCObjectCall = objectCallFactory.CreateObjectCall(TES5StaticReferenceFactory.Keyword, "GetKeyword", new TES5ObjectCallArguments() { new TES5String("ActorTypeNPC") }); //GetKeyword is an SKSE function TES5ObjectCallArguments arguments = new TES5ObjectCallArguments() { actorTypeNPCObjectCall }; return(objectCallFactory.CreateObjectCall(calledOn, "HasKeyword", arguments)); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { //WTM: Change: Apparently moving the player wakes up the player. I want to see if this works. var playerRef = TES5ReferenceFactory.CreateReferenceToPlayer(globalScope); return(objectCallFactory.CreateObjectCall(playerRef, "MoveTo", new TES5ObjectCallArguments() { playerRef })); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { //WTM: @Inconsistence: //SKSE has Game.SetPlayerLevel(int level), but Oblivion calls this function on non-player actors. //Since we can't call SetLevel directly, we are instead modifying actor values. TES4FunctionArguments tes4Arguments = function.Arguments; int newLevel = (int)tes4Arguments[0].Data; bool levelToPC = tes4Arguments.Count > 1 ? (int)tes4Arguments[1].Data == 1 : false;//When false, newLevel should be evaluated absolutely. When true, newLevel should be evaluated relative to the current level. string newFunctionName = !levelToPC ? "SetActorValue" : "ModActorValue"; int attributeLevel = newLevel, skillLevel = newLevel; const int minAttributeValue = 25, minSkillLevel = 10; if (levelToPC) {//If levelToPC is true, increase or decrease attributes 10 times greater than the level change and skills 2 times greater than the level change. attributeLevel *= 10; skillLevel *= 2; } else {//If levelToPC is false, ensure values are not set to values less than minimums. if (attributeLevel < minAttributeValue) { attributeLevel = minAttributeValue; } if (skillLevel < minSkillLevel) { skillLevel = minSkillLevel; } } TES5CodeChunkCollection codeChunks = new TES5CodeChunkCollection(); string[] attributes = new string[] { "Health", "Magicka", "Stamina" }; codeChunks.AddRange(attributes.Select(a => objectCallFactory.CreateObjectCall(calledOn, newFunctionName, multipleScriptsScope, new TES5ObjectCallArguments() { new TES5String(a), new TES5Float(attributeLevel) }))); string[] skills = new string[] { "OneHanded", "TwoHanded", "Marksman", "Block", "Smithing", "HeavyArmor", "LightArmor", "Pickpocket", "Lockpicking", "Sneak", "Alchemy", "Speechcraft", "Alteration", "Conjuration", "Destruction", "Illusion", "Restoration", "Enchanting" }; codeChunks.AddRange(skills.Select(s => objectCallFactory.CreateObjectCall(calledOn, newFunctionName, multipleScriptsScope, new TES5ObjectCallArguments() { new TES5String(s), new TES5Float(skillLevel) }))); return(codeChunks); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { int flag = ((TES4Integer)function.Arguments[0]).IntValue; bool restrained = flag != 0; TES5ObjectCallArguments arguments = new TES5ObjectCallArguments() { new TES5Bool(restrained) }; return(objectCallFactory.CreateObjectCall(calledOn, "SetRestrained", arguments)); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { int willIgnore = ((TES4Integer)function.Arguments[0]).IntValue; bool ignore = willIgnore != 0; TES5ObjectCallArguments arguments = new TES5ObjectCallArguments() { new TES5Bool(ignore) }; return(objectCallFactory.CreateObjectCall(calledOn, "IgnoreFriendlyHits", arguments)); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { //WTM: Note: I'd prefer a single method call obviously, but this is the best solution I can imagine. TES5ObjectCall calledOnRaceName = objectCallFactory.CreateObjectCall(objectCallFactory.CreateObjectCall(calledOn, "GetRace", multipleScriptsScope), "GetName", multipleScriptsScope); string[] playableRaces = new string[] { "Altmer", "Argonian", "Bosmer", "Breton", "Dunmer", "Imperial", "Khajiit", "Nord", "Orc", "Redguard" }; ITES5Expression completeStatement = null; foreach (string race in playableRaces) { TES5ComparisonExpression newExpression = TES5ExpressionFactory.CreateComparisonExpression(calledOnRaceName, TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5String(race)); if (completeStatement == null) { completeStatement = newExpression; } else { completeStatement = TES5ExpressionFactory.CreateLogicalExpression(completeStatement, TES5LogicalExpressionOperator.OPERATOR_OR, newExpression); } } return(completeStatement); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { const string functionName = "QueryStat"; ITES5Referencer newCalledOn = TES5StaticReferenceFactory.Game; TES4FunctionArguments functionArguments = function.Arguments; int oldArgValue = (int)functionArguments.Single().Data; string newArgValue = statMap[oldArgValue]; TES4FunctionArguments tes4Arguments = new TES4FunctionArguments() { new TES4String(newArgValue) }; TES5ObjectCallArguments tes5Arguments = objectCallArgumentsFactory.CreateArgumentList(tes4Arguments, codeScope, globalScope, multipleScriptsScope); return(objectCallFactory.CreateObjectCall(newCalledOn, functionName, tes5Arguments)); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { ITES5Referencer topic = referenceFactory.CreateReference(function.Arguments[0].StringValue, globalScope, multipleScriptsScope, codeScope.LocalScope); TES5Property property = (TES5Property)topic.ReferencesTo !; Tuple <int, string>?newGlobalVariable; if (globalVariableFinder.TryGetGlobalVariable(property.TES4FormID !.Value, out newGlobalVariable)) { TES5Property globalVariableProperty = TES5PropertyFactory.ConstructWithTES5FormID(newGlobalVariable.Item2, TES5BasicType.T_GLOBALVARIABLE, newGlobalVariable.Item2, newGlobalVariable.Item1); globalScope.AddPropertyIfNotExists(globalVariableProperty); TES5Reference globalVariableReference = TES5ReferenceFactory.CreateReferenceToVariableOrProperty(globalVariableProperty); return(objectCallFactory.CreateObjectCall(globalVariableReference, "SetValueInt", new TES5ObjectCallArguments() { new TES5Integer(1) })); } return(popCalledRenameFunctionFactory.ConvertFunction(calledOn, function, codeScope, globalScope, multipleScriptsScope)); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { TES4FunctionArguments tes4Arguments = function.Arguments; int statInt = ((TES4Integer)tes4Arguments[0]).IntValue; string statName; if (statInt == 14) { statName = "Horses Owned"; } else if (statInt == 15) { statName = "Houses Owned"; } else if (statInt == 16) { statName = "Stores Invested In"; } else if (statInt == 27) { statName = "Nirnroots Found"; } else { //Oblivion contains many calls to statInt = 19 (Artifacts Found) return(logUnknownFunctionFactory.CreateLogCall(function)); } TES5ObjectCallArguments arguments = new TES5ObjectCallArguments(); arguments.Add(new TES5String(statName)); if (tes4Arguments.Count > 1) { int modAmount = ((TES4Integer)tes4Arguments[1]).IntValue; arguments.Add(new TES5Integer(modAmount)); } return(objectCallFactory.CreateObjectCall(TES5StaticReferenceFactory.Game, "IncrementStat", arguments)); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { //WTM: Note: @INCONSISTENCE: This is supposed to check if the actor is being forced to sneak. //Since Papyrus can't do that, I'm instead checking if the actor is sneaking at all, force or not. return(objectCallFactory.CreateObjectCall(calledOn, "IsSneaking")); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { //return new TES5Integer(0);//WTM: Change return(objectCallFactory.CreateObjectCall(TES5StaticReferenceFactory.Utility, "IsInMenuMode")); }