public TES5CodeChunkCollection CreateCodeChunk(TES4VariableAssignation chunk, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { TES5CodeChunkCollection codeChunkCollection = new TES5CodeChunkCollection(); string referenceName = chunk.Reference.StringValue; ITES5Referencer reference = this.referenceFactory.CreateReference(referenceName, globalScope, multipleScriptsScope, codeScope.LocalScope); ITES5Value value = this.valueFactory.CreateValue(chunk.Value, codeScope, globalScope, multipleScriptsScope); if (reference.TES5Type == TES5BasicType.T_GLOBALVARIABLE) { //if the reference is in reality a global variable, we will need to convert it by creating a Reference.SetValue(value); call //Object call creation TES5ObjectCallArguments objectCallArguments = new TES5ObjectCallArguments() { value }; TES5ObjectCall objectCall = this.objectCallFactory.CreateObjectCall(reference, "SetValue", objectCallArguments); codeChunkCollection.Add(objectCall); } else { if (reference.ReferencesTo == null) { throw new NullableException(nameof(reference.ReferencesTo)); } if (!reference.ReferencesTo.TES5Type.IsPrimitive && value.TES5Type.IsPrimitive) { //Hacky! TES5IntegerOrFloat?valueNumber = value as TES5IntegerOrFloat; if (valueNumber != null && valueNumber.ConvertedIntValue == 0) { value = new TES5None(); } } TES5VariableAssignation assignation = TES5VariableAssignationFactory.CreateAssignation(reference, value); this.typeInferencer.InferenceObjectByAssignation(reference, value); codeChunkCollection.Add(assignation); //post analysis. //Todo - rethink the prefix here ITES5Referencer?referencerValue = value as ITES5Referencer; if (referencerValue != null && referencerValue.Name == TES5Property.AddPropertyNameSuffix(TES5ReferenceFactory.MESSAGEBOX_VARIABLE_CONST)) { /* * Create block: * variable = this.TES4_MESSAGEBOX_RESULT; ; assignation * if(variable != -1) ; branch, expression * this.TES4_MESSAGEBOX_RESULT = -1; ; reassignation * endIf */ TES5Integer negativeOne = new TES5Integer(-1); TES5ComparisonExpression expression = TES5ExpressionFactory.CreateComparisonExpression(reference, TES5ComparisonExpressionOperator.OPERATOR_NOT_EQUAL, negativeOne); TES5VariableAssignation reassignation = TES5VariableAssignationFactory.CreateAssignation(referencerValue, negativeOne); TES5Branch branch = TES5BranchFactory.CreateSimpleBranch(expression, codeScope.LocalScope); branch.MainBranch.CodeScope.AddChunk(reassignation); codeChunkCollection.Add(branch); } } return(codeChunkCollection); }
public List <ITES5CodeChunk> GenerateObjectiveHandling(ITES5CodeBlock codeBlock, TES5GlobalScope globalScope, List <int> stageMap) { TES5LocalVariable castedToQuest = new TES5LocalVariable("__temp", TES5BasicType.T_QUEST); TES5Reference referenceToTemp = TES5ReferenceFactory.CreateReferenceToVariable(castedToQuest); List <ITES5CodeChunk> result = new List <ITES5CodeChunk>() { TES5VariableAssignationFactory.CreateAssignation(referenceToTemp, TES5ReferenceFactory.CreateReferenceToSelf(globalScope)) }; TES5LocalScope localScope = codeBlock.CodeScope.LocalScope; localScope.AddVariable(castedToQuest); int i = 0; foreach (var stageTargetState in stageMap) { TES5Integer targetIndex = new TES5Integer(i); if (stageTargetState != 0) { //Should be visible TES5ObjectCallArguments displayedArguments = new TES5ObjectCallArguments() { targetIndex }; TES5ObjectCall isObjectiveDisplayed = new TES5ObjectCall(referenceToTemp, "IsObjectiveDisplayed", displayedArguments); TES5ComparisonExpression expression = TES5ExpressionFactory.CreateComparisonExpression(isObjectiveDisplayed, TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5Integer(0)); TES5ObjectCallArguments arguments = new TES5ObjectCallArguments() { targetIndex, new TES5Integer(1) }; TES5ObjectCall showTheObjective = new TES5ObjectCall(referenceToTemp, "SetObjectiveDisplayed", arguments); TES5Branch branch = TES5BranchFactory.CreateSimpleBranch(expression, localScope); branch.MainBranch.CodeScope.AddChunk(showTheObjective); result.Add(branch); } else { TES5ObjectCallArguments displayedArguments = new TES5ObjectCallArguments() { targetIndex }; TES5ObjectCall isObjectiveDisplayed = new TES5ObjectCall(referenceToTemp, "IsObjectiveDisplayed", displayedArguments); TES5ComparisonExpression expression = TES5ExpressionFactory.CreateComparisonExpression(isObjectiveDisplayed, TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5Integer(1)); TES5ObjectCallArguments arguments = new TES5ObjectCallArguments() { targetIndex, new TES5Integer(1) }; TES5ObjectCall completeTheObjective = new TES5ObjectCall(referenceToTemp, "SetObjectiveCompleted", arguments); TES5Branch branch = TES5BranchFactory.CreateSimpleBranch(expression, localScope); branch.MainBranch.CodeScope.AddChunk(completeTheObjective); result.Add(branch); } ++i; } return(result); }
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) { TES5LocalScope localScope = codeScope.LocalScope; TES4FunctionArguments functionArguments = function.Arguments; TES5ObjectCallArguments fameArguments = new TES5ObjectCallArguments(); ITES4StringValue argument0 = functionArguments[0]; Nullable <int> argument0Int = argument0.Data as Nullable <int>; ITES5Value newArgument; if (argument0Int != null) { newArgument = new TES5Integer(argument0Int.Value); } else { newArgument = this.valueFactory.CreateValue(argument0, codeScope, globalScope, multipleScriptsScope); } fameArguments.Add(newArgument); TES5ObjectCall newFunction = this.objectCallFactory.CreateObjectCall(this.referenceFactory.CreateReference("Infamy", globalScope, multipleScriptsScope, localScope), "SetValue", fameArguments); return(newFunction); }
private ITES5Value ConvertComparisonExpression(ITES4BinaryExpression expression, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { Tuple <ITES4Value, ITES4Value>[] tes4ValueTuples = new Tuple <ITES4Value, ITES4Value>[] { new Tuple <ITES4Value, ITES4Value>(expression.LeftValue, expression.RightValue), new Tuple <ITES4Value, ITES4Value>(expression.RightValue, expression.LeftValue) }; /* * Scenario 1 - Special functions converted on expression level */ foreach (Tuple <ITES4Value, ITES4Value> tes4ValueTuple in tes4ValueTuples) { ITES4Callable?valueTupleItem1 = tes4ValueTuple.Item1 as ITES4Callable; if (valueTupleItem1 == null) { continue; } TES4Function function = valueTupleItem1.Function; switch (function.FunctionCall.FunctionName.ToLower()) { case "getweaponanimtype": { ITES5Referencer calledOn = this.CreateCalledOnReferenceOfCalledFunction(valueTupleItem1, codeScope, globalScope, multipleScriptsScope); TES5ObjectCall equippedWeaponLeftValue = this.objectCallFactory.CreateObjectCall(this.objectCallFactory.CreateObjectCall(calledOn, "GetEquippedWeapon"), "GetWeaponType"); int[] targetedWeaponTypes; switch ((int)tes4ValueTuple.Item2.Data) { case 0: { targetedWeaponTypes = new int[] { 0 }; break; } case 1: { targetedWeaponTypes = new int[] { 1, 2, 3, 4 }; break; } case 2: { targetedWeaponTypes = new int[] { 5, 6, 8 }; break; } case 3: { targetedWeaponTypes = new int[] { 7, 9 }; break; } default: { throw new ConversionException("GetWeaponAnimType() - Unknown weapon type in expression"); } } List <TES5ComparisonExpression> expressions = new List <TES5ComparisonExpression>(); foreach (var targetedWeaponType in targetedWeaponTypes) { expressions.Add(TES5ExpressionFactory.CreateComparisonExpression(equippedWeaponLeftValue, TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5Integer(targetedWeaponType))); } ITES5Expression resultExpression = expressions[0]; expressions.RemoveAt(0); while (expressions.Any()) { resultExpression = TES5ExpressionFactory.CreateLogicalExpression(resultExpression, TES5LogicalExpressionOperator.OPERATOR_OR, expressions.Last()); expressions.RemoveAt(expressions.Count - 1); } return(resultExpression); } case "getdetected": { TES5ObjectCallArguments inversedArgument = new TES5ObjectCallArguments() { this.CreateCalledOnReferenceOfCalledFunction(valueTupleItem1, codeScope, globalScope, multipleScriptsScope) }; TES5ObjectCall getDetectedLeftValue = this.objectCallFactory.CreateObjectCall(this.referenceFactory.CreateReadReference(function.Arguments[0].StringValue, globalScope, multipleScriptsScope, codeScope.LocalScope), "isDetectedBy", inversedArgument); TES5Integer getDetectedRightValue = new TES5Integer(((int)tes4ValueTuple.Item2.Data == 0) ? 0 : 1); return(TES5ExpressionFactory.CreateComparisonExpression(getDetectedLeftValue, TES5ComparisonExpressionOperator.OPERATOR_EQUAL, getDetectedRightValue)); } case "getdetectionlevel": { if (!tes4ValueTuple.Item2.HasFixedValue) { throw new ConversionException("Cannot convert getDetectionLevel calls with dynamic comparision"); } TES5Bool tes5Bool = new TES5Bool(((int)tes4ValueTuple.Item2.Data) == 3); //true only if the compared value was 3 TES5ObjectCallArguments inversedArgument = new TES5ObjectCallArguments() { this.CreateCalledOnReferenceOfCalledFunction(valueTupleItem1, codeScope, globalScope, multipleScriptsScope) }; return(TES5ExpressionFactory.CreateComparisonExpression ( this.objectCallFactory.CreateObjectCall(this.referenceFactory.CreateReadReference(function.Arguments[0].StringValue, globalScope, multipleScriptsScope, codeScope.LocalScope), "isDetectedBy", inversedArgument), TES5ComparisonExpressionOperator.OPERATOR_EQUAL, tes5Bool )); } case "getcurrentaiprocedure": { if (!tes4ValueTuple.Item2.HasFixedValue) { throw new ConversionException("Cannot convert getCurrentAIProcedure() calls with dynamic comparision"); } switch ((int)tes4ValueTuple.Item2.Data) { case 4: { return(TES5ExpressionFactory.CreateComparisonExpression( this.objectCallFactory.CreateObjectCall(this.CreateCalledOnReferenceOfCalledFunction(valueTupleItem1, codeScope, globalScope, multipleScriptsScope), "IsInDialogueWithPlayer"), TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5Bool(expression.Operator == TES4ComparisonExpressionOperator.OPERATOR_EQUAL) //cast to true if the original op was ==, false otherwise. )); } case 8: { //ref.getSleepState() == 3 return(TES5ExpressionFactory.CreateComparisonExpression( this.objectCallFactory.CreateObjectCall(this.CreateCalledOnReferenceOfCalledFunction(valueTupleItem1, codeScope, globalScope, multipleScriptsScope), "getSleepState"), TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5Integer(3) //SLEEPSTATE_SLEEP )); } case 13: { return(TES5ExpressionFactory.CreateComparisonExpression( this.objectCallFactory.CreateObjectCall(this.CreateCalledOnReferenceOfCalledFunction(valueTupleItem1, codeScope, globalScope, multipleScriptsScope), "IsInCombat"), TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5Bool(expression.Operator == TES4ComparisonExpressionOperator.OPERATOR_EQUAL) //cast to true if the original op was ==, false otherwise. )); } case 0: //Travel case 7: //Wander case 15: //Pursue case 17: //Done { //@INCONSISTENCE idk how to check it tbh. We return always true. Think about better representation return(new TES5Bool(expression.Operator == TES4ComparisonExpressionOperator.OPERATOR_EQUAL)); } default: { throw new ConversionException("Cannot convert GetCurrentAiProcedure - unknown TES4 procedure number arg " + ((int)tes4ValueTuple.Item2.Data).ToString()); } } } case "isidleplaying": case "getknockedstate": case "gettalkedtopc": { return(new TES5Bool(true)); //This is so unimportant that i think it"s not worth to find a good alternative and waste time. } case "getsitting": { //WARNING: Needs to implement Horse sittings, too. //SEE: http://www.gameskyrim.com/papyrus-isridinghorse-function-t255012.html int goTo; switch ((int)tes4ValueTuple.Item2.Data) { case 0: { goTo = 0; break; } case 1: case 2: case 11: case 12: { goTo = 2; break; } case 3: case 13: { goTo = 3; break; } case 4: case 14: { goTo = 4; break; } default: { throw new ConversionException("GetSitting - unknown state found"); } } //ref.getSleepState() == 3 return(TES5ExpressionFactory.CreateComparisonExpression ( this.objectCallFactory.CreateObjectCall(this.CreateCalledOnReferenceOfCalledFunction(valueTupleItem1, codeScope, globalScope, multipleScriptsScope), "GetSitState"), TES5ComparisonExpressionOperator.GetFirst(expression.Operator.Name), new TES5Integer(goTo) )); } } } ITES5Value leftValue = this.CreateValue(expression.LeftValue, codeScope, globalScope, multipleScriptsScope); ITES5Value rightValue = this.CreateValue(expression.RightValue, codeScope, globalScope, multipleScriptsScope); Tuple <ITES5Value, ITES5Value>[] tes5ValueTuples = new Tuple <ITES5Value, ITES5Value>[] { new Tuple <ITES5Value, ITES5Value>(leftValue, rightValue), new Tuple <ITES5Value, ITES5Value>(rightValue, leftValue) }; TES5BasicType objectReferenceType = TES5BasicType.T_FORM; //used just to make sure. TES5ComparisonExpressionOperator op = TES5ComparisonExpressionOperator.GetFirst(expression.Operator.Name); /* * Scenario 2: Comparision of ObjectReferences to integers ( quick formid check ) */ bool flipOperator = false; foreach (var valueTuple in tes5ValueTuples) { TES5ComparisonExpressionOperator newOp = !flipOperator ? op : op.Flip(); if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(valueTuple.Item1.TES5Type, objectReferenceType) || TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(valueTuple.Item1.TES5Type.NativeType, objectReferenceType)) { if (valueTuple.Item2.TES5Type == TES5BasicType.T_INT) { //Perhaps we should allow to try to cast upwards for primitives, .asPrimitive() or similar //In case we do know at compile time that we"re comparing against zero, then we can assume //we can compare against None, which allows us not call GetFormID() on most probably None object TES5Integer tes5SetItem2Integer = (TES5Integer)valueTuple.Item2; if (tes5SetItem2Integer.IntValue == 0) { newOp = op == TES5ComparisonExpressionOperator.OPERATOR_EQUAL ? op : TES5ComparisonExpressionOperator.OPERATOR_NOT_EQUAL; return(TES5ExpressionFactory.CreateComparisonExpression(valueTuple.Item1, newOp, new TES5None())); } else { ITES5Referencer callable = (ITES5Referencer)valueTuple.Item1; TES5ObjectCall tes5setNewItem1 = this.objectCallFactory.CreateObjectCall(callable, "GetFormID"); return(TES5ExpressionFactory.CreateComparisonExpression(tes5setNewItem1, newOp, valueTuple.Item2)); } } } else if (valueTuple.Item1.TES5Type.OriginalName == TES5VoidType.OriginalNameConst) { #if PHP_COMPAT TES5IntegerOrFloat tes5SetItem2Number = tes5set.Item2 as TES5IntegerOrFloat; if (tes5SetItem2Number != null && tes5SetItem2Number.ConvertedIntValue == 0) { return(TES5ExpressionFactory.createArithmeticExpression(tes5set.Item1, newOp, new TES5None())); } #else throw new ConversionException("Type was void.");//This shouldn't happen anymore. #endif } if (!TES5InheritanceGraphAnalyzer.IsTypeOrExtendsTypeOrIsNumberType(valueTuple.Item1.TES5Type, valueTuple.Item2.TES5Type, true))//WTM: Change: Added entire if branch { if (valueTuple.Item1.TES5Type.NativeType == TES5BasicType.T_QUEST && valueTuple.Item2.TES5Type == TES5BasicType.T_INT) { TES5ObjectCall getStage = this.objectCallFactory.CreateObjectCall((ITES5Referencer)valueTuple.Item1, "GetStage"); return(TES5ExpressionFactory.CreateComparisonExpression(getStage, newOp, valueTuple.Item2)); } if (valueTuple.Item1.TES5Type == TES5BasicType.T_BOOL && valueTuple.Item2.TES5Type == TES5BasicType.T_INT) { int item2Value = ((TES5Integer)valueTuple.Item2).IntValue; if (item2Value != 0 && item2Value != 1) { throw new ConversionException("Unexpected Value: " + item2Value.ToString()); } ITES5Value newItem2 = new TES5Bool(item2Value == 1); newOp = op == TES5ComparisonExpressionOperator.OPERATOR_EQUAL ? op : TES5ComparisonExpressionOperator.OPERATOR_NOT_EQUAL; return(TES5ExpressionFactory.CreateComparisonExpression(valueTuple.Item1, newOp, newItem2)); } if (valueTuple.Item1.TES5Type == TES5BasicType.T_ACTOR && valueTuple.Item2.TES5Type == TES5BasicType.T_ACTORBASE) { return(TES5ExpressionFactory.CreateComparisonExpression(objectCallFactory.CreateGetActorBase((TES5ObjectCall)valueTuple.Item1), newOp, valueTuple.Item2)); } //WTM: Change: Added for se08barriertriggerscript if (newOp == TES5ComparisonExpressionOperator.OPERATOR_EQUAL && TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(valueTuple.Item1.TES5Type, TES5BasicType.T_FORM) && TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(valueTuple.Item2.TES5Type, TES5BasicType.T_FORM)) { TES5Reference reference1 = (TES5Reference)valueTuple.Item1; TES5Reference reference2 = (TES5Reference)valueTuple.Item2; reference1.ManualCastTo = TES5BasicType.T_FORM; reference2.ManualCastTo = TES5BasicType.T_FORM; return(TES5ExpressionFactory.CreateComparisonExpression(reference1, newOp, reference2)); } throw new ConversionException("Type could not be converted."); } TES5ComparisonExpression?containsCall = ConvertGetItemCountCallToContainsItemCall(valueTuple.Item1, op, valueTuple.Item2); //WTM: Change: Added if (containsCall != null) { return(containsCall); } flipOperator = true; } return(TES5ExpressionFactory.CreateComparisonExpression(leftValue, op, rightValue)); }
//WTM: Change: Added private static bool CanConvertGetItemCountSpecialCases(TES5ObjectCall value1ObjectCall, TES5ComparisonExpressionOperator op, TES5Integer value2Integer) { if (op == TES5ComparisonExpressionOperator.OPERATOR_EQUAL && value2Integer.IntValue == 1)//Special cases { //I checked the meaning of these scripts, and ContainsItem is a suitable replacement. if (value1ObjectCall.AccessedObject.Name == "MethredhelsChestRef_p" && ((TES5Reference)value1ObjectCall.Arguments[0]).Name == "TG01AmantiusDiary_p") { return(true); } //TES4methredhelscript.psc else if (value1ObjectCall.AccessedObject is TES5SelfReference) { if ( ((TES5Reference)value1ObjectCall.Arguments[0]).Name == "Dark07RoderickPoison_p" || //TES4dark07cabinetscript.psc ((TES5Reference)value1ObjectCall.Arguments[0]).Name == "Dark09Finger_p" //TES4dark09deskscript.psc ) { return(true); } } else if (value1ObjectCall.AccessedObject.Name == "TG03MyvrynasCupboardRef_p" && ((TES5Reference)value1ObjectCall.Arguments[0]).Name == "TG03LlathasasBust_p") { return(true); } //TES4hieronymuslexscript.psc and tif__010034a3.psc //TES4orumgangcourier1script.psc: functionality of fake Skooma rock explained here: https://en.uesp.net/wiki/Oblivion:Camonna_Tong_Thug //Skooma is added to the fake rock if the rock has zero or one Skooma bottles, reaching a maximum of two bottles. //If I use ContainsItem, the rock will only reach a maximum of one bottle. //If I use GetItemCount, the rock will apparently fill infinitely. //I'll need to check this later. @INCONSISTENCE } return(false); }