public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { TES5LocalScope localScope = codeScope.LocalScope; TES4FunctionArguments functionArguments = function.Arguments; string arg0String = functionArguments[0].StringValue; ITES5Referencer targetReference = this.referenceFactory.CreateReadReference(arg0String, globalScope, multipleScriptsScope, localScope); var arg0Type = targetReference.TES5Type; string functionName; ITES5Referencer baseReference; if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(arg0Type, TES5BasicType.T_ACTORBASE)) { functionName = "GetActorOwner"; baseReference = targetReference; } else if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(arg0Type, TES5BasicType.T_ACTOR)) { functionName = "GetActorOwner"; baseReference = this.objectCallFactory.CreateGetActorBase(targetReference); } else if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(arg0Type, TES5BasicType.T_FACTION)) { functionName = "GetFactionOwner"; baseReference = targetReference; } else { throw new ConversionException(function.FunctionCall.FunctionName + " should be called with either an ActorBase or a Faction."); } TES5ObjectCall owner = this.objectCallFactory.CreateObjectCall(calledOn, functionName); TES5ComparisonExpression expression = TES5ExpressionFactory.CreateComparisonExpression(owner, TES5ComparisonExpressionOperator.OPERATOR_EQUAL, baseReference); return(expression); }
private void InferenceTypeOfCalledObject(TES5ObjectCall objectCall, TES5MultipleScriptsScope multipleScriptsScope) { ITES5Type inferencableType = objectCall.AccessedObject.TES5Type.NativeType; /* * Check if we have something to inference inside the code, not some static class or method call return */ if (objectCall.AccessedObject.ReferencesTo != null) { //this is not "exactly" nice solution, but its enough. For now. ITES5Type inferenceType = TES5InheritanceGraphAnalyzer.FindTypeByMethod(objectCall); if (inferencableType == null) { throw new ConversionException("Cannot inference a null type"); } if (inferencableType == inferenceType) { return; //We already have the good type. } if (this.InferenceType(objectCall.AccessedObject.ReferencesTo, inferenceType, multipleScriptsScope)) { return; } } }
public override void TrackRemoteScript(TES5ScriptHeader scriptHeader) { this.trackedScript = scriptHeader; ITES5Type ourNativeType = this.propertyType.NativeType; ITES5Type remoteNativeType = this.trackedScript.ScriptType.NativeType; /* * Scenario 1 - types are equal or the remote type is higher than ours in which case we do nothing as they have the good type anyway */ if (ourNativeType == remoteNativeType || TES5InheritanceGraphAnalyzer.IsExtending(remoteNativeType, ourNativeType)) { return; } /* * Scenario 2 - Our current native type is extending remote script"s extended type - we need to set it properly */ else if (TES5InheritanceGraphAnalyzer.IsExtending(ourNativeType, remoteNativeType)) { this.trackedScript.SetNativeType(ourNativeType); } else { throw new ConversionException(nameof(TES5Property) + "." + nameof(TrackRemoteScript) + ": The definitions of local property type and remote property type have diverged. (Ours: " + ourNativeType.Value + ", remote: " + remoteNativeType.Value); } }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { //@INCONSISTENCE - 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); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { TES4FunctionArguments functionArguments = function.Arguments; if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(calledOn.TES5Type, TES5BasicType.T_ACTOR)) { calledOn = this.objectCallFactory.CreateGetActorBase(calledOn); } TES5ObjectCall functionThis = this.objectCallFactory.CreateObjectCall(calledOn, "GetSex"); int operand; switch (functionArguments[0].StringValue.ToLower()) { case "male": { operand = 0; break; } case "female": { operand = 1; break; } default: { throw new ConversionException("GetIsSex used with unknown gender."); } } TES5ComparisonExpression expression = TES5ExpressionFactory.CreateComparisonExpression(functionThis, TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5Integer(operand)); return(expression); }
public void TrackRemoteScript(TES5ScriptHeader scriptHeader) { this.trackedScript = scriptHeader; ITES5Type ourNativeType = this.propertyType.NativeType; ITES5Type remoteNativeType = this.trackedScript.ScriptType.NativeType; /* * Scenario 1 - types are equal or the remote type is higher than ours in which case we do nothing as they have the good type anyway */ if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(remoteNativeType, ourNativeType)) { return; } /* * Scenario 2 - Our current native type is extending remote script"s extended type - we need to set it properly */ else if (TES5InheritanceGraphAnalyzer.IsExtending(ourNativeType, remoteNativeType)) { this.trackedScript.SetNativeType(ourNativeType); } else { //WTM: Note: Special Cases: Let some script transpile without throwing. if (!( (ourNativeType == TES5BasicType.T_ACTIVATOR && remoteNativeType == TES5BasicType.T_OBJECTREFERENCE && (scriptHeader.EDID == "SE01MetronomeScript" || scriptHeader.EDID == "SE11TrigZoneHowlingHallsEnterSCRIPT")) || (ourNativeType == TES5BasicType.T_ACTORBASE && remoteNativeType == TES5BasicType.T_ACTOR && (scriptHeader.EDID == "HieronymusLexScript" || scriptHeader.EDID == "SERunsInCirclesSCRIPT")))) { throw new ConversionException(nameof(TES5Property) + "." + nameof(TrackRemoteScript) + ": The definitions of local property type and remote property type have diverged. (Ours: " + ourNativeType.Value + ", remote: " + remoteNativeType.Value + ")"); } } }
/* * @throws ConversionException */ public void SetNativeType(ITES5Type scriptType, bool ensureNewTypeExtendsCurrentType = true) { if (ensureNewTypeExtendsCurrentType && !TES5InheritanceGraphAnalyzer.IsExtending(scriptType, this.ScriptType.NativeType)) { throw new ConversionException("Cannot set script type to non-extending type - current native type " + this.ScriptType.NativeType.Value + ", new type " + scriptType.Value); } this.ScriptType.NativeType = scriptType.NativeType; }
/* * Try to inference variable"s type with type. * * * Needed for proxifying the properties to other scripts * - Will return true if inferencing succeeded, false otherwise. * @throws ConversionException */ private bool InferenceType(ITES5VariableOrProperty variable, ITES5Type type, TES5MultipleScriptsScope multipleScriptsScope) { if (!TES5InheritanceGraphAnalyzer.IsExtending(type, variable.TES5Type.NativeType)) { return(false); } variable.TES5Type = type; return(true); }
public TES5VariableAssignation(ITES5Value reference, ITES5Value value) { this.Reference = reference; this.value = value; //If value is going to be casted but actually can't be casted, throw an exception. if (ReferenceAndValueDifferentTypesAndValueIsNonNone() && !ReferenceIsIntAndValueExtendsForm() && //Oblivion stores boolean-like values in short ints. Exclude such cases. !(reference.TES5Type == TES5BasicType.T_INT && value.TES5Type == TES5BasicType.T_BOOL) && !TES5InheritanceGraphAnalyzer.IsTypeOrExtendsTypeOrIsNumberType(value.TES5Type, reference.TES5Type, true)) { throw new ConversionTypeMismatchException(value.TES5Type.OriginalName + " : " + value.TES5Type.NativeType.Name + " cannot be assigned to " + reference.TES5Type.OriginalName + " : " + reference.TES5Type.NativeType.Name); } }
private void InferenceTypeOfMethodArguments(TES5ObjectCall objectCall, TES5MultipleScriptsScope multipleScriptsScope) { /* * Inference the arguments */ int argumentIndex = 0; ITES5Type calledOnType = objectCall.AccessedObject.TES5Type.NativeType; foreach (ITES5Value argument in objectCall.Arguments) { /* * Get the argument type according to TES5Inheritance graph. */ ITES5Type argumentTargetType = TES5InheritanceGraphAnalyzer.FindTypeByMethodParameter(calledOnType, objectCall.FunctionName, argumentIndex); if (argument.TES5Type != argumentTargetType) { /* * todo - maybe we should move getReferencesTo() to TES5Value and make all of the rest TES5Values just have null references as they do not reference anything? :) */ ITES5Referencer referencerArgument = argument as ITES5Referencer; if (referencerArgument != null && TES5InheritanceGraphAnalyzer.IsExtending(argumentTargetType, argument.TES5Type.NativeType)) { //HACKY! this.InferenceType(referencerArgument.ReferencesTo, argumentTargetType, multipleScriptsScope); } else { //So there"s one , one special case where we actually have to cast a var from one to another even though they are not ,,inheriting" from themselves, because they are primitives. //Scenario: there"s an T_INT argument, and we feed it with a T_FLOAT variable reference. It won"t work :( //We need to cast it on call level ( NOT inference it ) to make it work and not break other possible scenarios ( more specifically, when a float would be inferenced to int and there"s a //float assigment somewhere in the code ) if (argumentTargetType == TES5BasicType.T_INT && argument.TES5Type == TES5BasicType.T_FLOAT) { TES5Castable argumentCastable = argument as TES5Castable; if (argumentCastable != null) { //HACKY! When we"ll clean up this interface, it will dissapear :) argumentCastable.ManualCastTo = argumentTargetType; } } else if ( !TES5InheritanceGraphAnalyzer.IsExtending(argument.TES5Type, argumentTargetType) && !TES5InheritanceGraphAnalyzer.IsNumberTypeOrBoolAndInt(argument.TES5Type, argumentTargetType) && !(argument is TES5None && TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(argumentTargetType, (new TES5None()).TES5Type))) { bool expected = objectCall.AccessedObject.TES5Type.OriginalName == "TES4TimerHelper" && objectCall.FunctionName == "LegacySay"; throw new ConversionException("Argument type mismatch at " + objectCall.FunctionName + " index " + argumentIndex + ". Expected " + argumentTargetType.OriginalName + ". Found " + argument.TES5Type.OriginalName + ".", expected: expected); } } } argumentIndex++; } }
private static TES5BasicType?GetCommonBaseTES5Type(IReadOnlyList <TES4Record> tes4Records) { if (!tes4Records.Any()) { return(null); } TES5BasicType[] tes5Types = tes4Records.Select(r => { TES5BasicType basicType = TypeMapper.GetTES5BasicType(r.RecordType); return(basicType); }).ToArray(); TES5BasicType commonBaseType = TES5InheritanceGraphAnalyzer.GetCommonBaseType(tes5Types); return(commonBaseType); }
private void InferenceTypeOfCalledObject(TES5ObjectCall objectCall) { /* * Check if we have something to inference inside the code, not some static class or method call return */ if (objectCall.AccessedObject.ReferencesTo == null) { return; } ITES5Type inferencableType = objectCall.AccessedObject.TES5Type.NativeType; //this is not "exactly" nice solution, but its enough. For now. TES5BasicType inferenceType = TES5InheritanceGraphAnalyzer.FindTypeByMethod(objectCall, esmAnalyzer); if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(inferencableType, inferenceType)) { return; //We already have the good type. } this.InferenceType(objectCall.AccessedObject.ReferencesTo, inferenceType); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { TES4FunctionArguments functionArguments = function.Arguments; TES5LocalScope localScope = codeScope.LocalScope; TES5ObjectCallArguments arguments = new TES5ObjectCallArguments() { calledOn }; ITES5Value argument1 = this.valueFactory.CreateValue(functionArguments[0], codeScope, globalScope, multipleScriptsScope); if (argument1.TES5Type != TES5BasicType.T_TOPIC) { TES5Castable argument1Castable = argument1 as TES5Reference; if (argument1Castable != null && TES5InheritanceGraphAnalyzer.IsExtending(TES5BasicType.T_TOPIC, argument1.TES5Type)) { argument1Castable.ManualCastTo = TES5BasicType.T_TOPIC; } } arguments.Add(argument1); ITES4StringValue optionalFlag = functionArguments.GetOrNull(2); if (optionalFlag != null) { string optionalFlagDataString = optionalFlag.StringValue; if (this.analyzer.GetFormTypeByEDID(optionalFlagDataString).Value != TES4RecordType.REFR.Name) { this.metadataLogService.WriteLine("ADD_SPEAK_AS_ACTOR", new string[] { optionalFlagDataString }); optionalFlag = new TES4ApiToken(optionalFlag.Data + "Ref"); } arguments.Add(this.valueFactory.CreateValue(optionalFlag, codeScope, globalScope, multipleScriptsScope)); } else { arguments.Add(new TES5None()); } arguments.Add(new TES5Bool(true)); ITES5Referencer timerReference = this.referenceFactory.CreateTimerReadReference(globalScope, multipleScriptsScope, localScope); return(this.objectCallFactory.CreateObjectCall(timerReference, "LegacySay", multipleScriptsScope, arguments)); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { TES5LocalScope localScope = codeScope.LocalScope; TES4FunctionArguments functionArguments = function.Arguments; string newCalledOnString = functionArguments.Pop(0).StringValue; ITES5Referencer newCalledOn = this.referenceFactory.CreateReadReference(newCalledOnString, globalScope, multipleScriptsScope, localScope); if (!TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(newCalledOn.TES5Type, TES5BasicType.T_ACTORBASE)) { if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(newCalledOn.TES5Type, TES5BasicType.T_ACTOR)) { newCalledOn = this.objectCallFactory.CreateGetActorBase(newCalledOn); } else { throw new ConversionException(newFunctionName + " should be called with an ActorBase. Instead, it was called with " + newCalledOnString + " (" + newCalledOn.TES5Type.OriginalName + " : " + newCalledOn.TES5Type.NativeType.Name + ")."); } } return(this.objectCallFactory.CreateObjectCall(newCalledOn, this.newFunctionName, this.objectCallArgumentsFactory.CreateArgumentList(functionArguments, codeScope, globalScope, multipleScriptsScope))); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { TES4FunctionArguments functionArguments = function.Arguments; TES5ObjectCallArguments args; string functionName; if (functionArguments.Any()) { args = this.objectCallArgumentsFactory.CreateArgumentList(functionArguments, codeScope, globalScope, multipleScriptsScope); var arg0Type = args[0].TES5Type; if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(arg0Type, TES5BasicType.T_ACTORBASE)) { functionName = "SetActorOwner"; } else if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(arg0Type, TES5BasicType.T_ACTOR)) { args[0] = objectCallFactory.CreateGetActorBase((ITES5Referencer)args[0]); functionName = "SetActorOwner"; } else if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(arg0Type, TES5BasicType.T_FACTION)) { functionName = "SetFactionOwner"; } else { throw new ConversionException(function.FunctionCall.FunctionName + " should be called with either an ActorBase or a Faction. Instead, it was called with " + arg0Type.Value + " (" + arg0Type.OriginalName + " : " + arg0Type.NativeType.Name + ")."); } } else { functionName = "SetActorOwner"; args = new TES5ObjectCallArguments() { this.objectCallFactory.CreateGetActorBaseOfPlayer(globalScope) }; } return(this.objectCallFactory.CreateObjectCall(calledOn, functionName, args)); }
/* * Try to inference variable"s type with type. * * * Needed for proxifying the properties to other scripts * - Will return true if inferencing succeeded, false otherwise. * @throws ConversionTypeMismatchException */ private void InferenceType(ITES5VariableOrProperty variable, TES5BasicType type) { if (!TES5InheritanceGraphAnalyzer.IsTypeOrExtendsTypeOrIsNumberType(type, variable.TES5Type.NativeType, false)) { if (TES5InheritanceGraphAnalyzer.IsExtending(variable.TES5Type.NativeType, type)) { return; } throw new ConversionTypeMismatchException("Could not extend " + variable.TES5Type.NativeType.Value + " to " + type.Value + "."); } if (variable.TES5Type.AllowInference) { variable.TES5Type = type; } else if (variable.TES5Type.AllowNativeTypeInference) { variable.TES5Type.NativeType = type; } else { throw new ConversionTypeMismatchException(variable.Name + " (" + variable.TES5DeclaredType.OriginalName + " : " + variable.TES5DeclaredType.NativeType.Name + ") could not be inferenced to a " + type.Name + " because inference was not allowed."); } }
private static void InferEventBlockContainingType(string functionBlockName, TES5GlobalScope globalScope) { TES5BasicType[]? allowedTypes = eventNameToTypes[functionBlockName];//null indicates that any type is allowed if (allowedTypes == null || allowedTypes.Any(t => TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(globalScope.ScriptHeader.ScriptType, t))) { return; } if (allowedTypes.Length == 1) { TES5BasicType singleAllowedType = allowedTypes[0]; if (globalScope.ScriptHeader.ScriptType.AllowNativeTypeInference && TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(singleAllowedType, globalScope.ScriptHeader.ScriptType.NativeType)) { if (globalScope.ScriptHeader.ScriptType.NativeType != singleAllowedType) { globalScope.ScriptHeader.SetNativeType(singleAllowedType); } return; } } ITES5Type basicScriptType = globalScope.ScriptHeader.ScriptType.NativeType; bool expected = functionBlockName == "OnHit" && TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(basicScriptType, TES5BasicType.T_ACTIVEMAGICEFFECT); throw new ConversionTypeMismatchException("Event " + functionBlockName + " is not allowed on " + globalScope.ScriptHeader.ScriptType.Value + " (" + basicScriptType.Value + ").", expected: expected); }
private bool ReferenceAndValueDifferentTypesAndValueIsNonNone() { return(!TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(this.value.TES5Type, this.Reference.TES5Type) && !(this.value is TES5None)); }
public TES5CodeScope Modify(TES4CodeBlock block, TES5BlockList blockList, TES5FunctionScope blockFunctionScope, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { //https://cs.elderscrolls.com/index.php?title=Begin //WTM: Change: Added: I reorganized this method and forced each event to account for the first Oblivion parameter. bool accountedForParameter = false; switch (block.BlockType.ToLower()) { case "gamemode": case "scripteffectupdate": { TES5ObjectCall function = this.objectCallFactory.CreateRegisterForSingleUpdate(globalScope); codeScope.AddChunk(function); if (globalScope.ScriptHeader.ScriptType.NativeType == TES5BasicType.T_QUEST) { TES5EventCodeBlock onInitBlock = TES5BlockFactory.CreateOnInit(); onInitBlock.AddChunk(function); blockList.Add(onInitBlock); } break; } case "menumode": { TES5ComparisonExpression isInMenuModeComparisonExpression = GetIsInMenuModeComparisonExpression(); codeScope = SetUpBranch(blockFunctionScope, codeScope, isInMenuModeComparisonExpression); accountedForParameter = true; //Skyrim handles menus differently than Oblivion's MenuType. break; } case "onactivate": { TES5EventCodeBlock onInitBlock = TES5BlockFactory.CreateOnInit(); TES5ObjectCall function = this.objectCallFactory.CreateObjectCall(TES5ReferenceFactory.CreateReferenceToSelf(globalScope), "BlockActivation"); onInitBlock.AddChunk(function); blockList.Add(onInitBlock); //WTM: Change: Added: The following scripts erroneously add a parameter to their OnActivate block: //MS11BradonCorpse, SE01WaitingRoomScript, SE02LoveLetterScript, SE08Xeddefen03DoorSCRIPT, SE08Xeddefen05DoorSCRIPT, SE32TombEpitaph01SCRIPT, SEHillofSuicidesSCRIPT, SEXidPuzButton1, SEXidPuzButton2, SEXidPuzButton3, SEXidPuzButton4, SEXidPuzButtonSCRIPT, SEXidPuzHungerSCRIPT, XPEbroccaCrematorySCRIPT //But OnActivate does not take a parameter. I'm trying to use the author's intended parameter instead of just ignoring it. codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akActivateRef", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "onactorequip": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, TES5LocalVariableParameterMeaning.CONTAINER, globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "onadd": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akNewContainer", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "onalarm": { //@INCONSISTENCE - We don"t account for CrimeType or Criminal. codeScope.AddChunk(this.objectCallFactory.CreateObjectCall(TES5StaticReferenceFactory.Debug, "Trace", new TES5ObjectCallArguments() { new TES5String("This function does not account for OnAlarm's CrimeType or Criminal.") })); ITES5Value isAlarmed = TES5ExpressionFactory.CreateComparisonExpression(this.objectCallFactory.CreateObjectCall(TES5ReferenceFactory.CreateReferenceToSelf(globalScope), "IsAlarmed"), TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5Bool(true)); codeScope = SetUpBranch(blockFunctionScope, codeScope, isAlarmed); accountedForParameter = true; break; } /* * case "onalarm": * { * * this.skyrimGroupEventName = "onhit"; * * if (this.eventArgs[1] != 3) { * //Nothing eelse is supported really.. * this.omit = true; * break; * } * * branch = new TES4ConditionalBranch(); * expression = new TES4Expression(); * leftConstant = new TES4Constant("akAggressor", "ObjectReference"); * //actionConstant = new TES4Constant(this.eventArgs[1],"Package"); * actionConstant = TES4Factories.createReference(this.eventArgs[2], this); * * expression.left_side = leftConstant; * expression.right_side = actionConstant; * expression.comparision_operator = TES4Expression.COMPARISION_OPERATOR_EQUAL; * * codeBlock = new TES4CodeBlock(); * codeBlock.chunks = this.chunks; * * branch.ifs[] = array( * "rawExpression" => "SCRIPT_GENERATED", * "expression" => expression, * "codeBlock" => codeBlock * ); * this.chunks = new TES4ChunkContainer(); * this.chunks.parent = this; * this.chunks.addChunk(branch); * * break; * } */ case "ondeath": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akKiller", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "ondrop": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akOldContainer", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "onequip": case "onunequip": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akActor", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "onhit": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akAggressor", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "onhitwith": { TES4BlockParameterList?parameterList = block.BlockParameterList; if (parameterList != null) { TES5LocalScope localScope = codeScope.LocalScope; ITES5Referencer hitWithCriteria = this.referenceFactory.CreateReadReference(parameterList.Parameters[0].BlockParameter, globalScope, multipleScriptsScope, localScope); TES5SignatureParameter akSource = localScope.FunctionScope.GetParameter("akSource"); TES5ComparisonExpression hitWithEqualsSource = TES5ExpressionFactory.CreateComparisonExpression(TES5ReferenceFactory.CreateReferenceToVariableOrProperty(akSource), TES5ComparisonExpressionOperator.OPERATOR_EQUAL, hitWithCriteria); TES5CodeScope newCodeScope = TES5CodeScopeFactory.CreateCodeScopeRoot(blockFunctionScope); if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(TES5BasicType.T_AMMO, hitWithCriteria.TES5Type)) { newCodeScope.AddChunk(this.objectCallFactory.CreateObjectCall(TES5StaticReferenceFactory.Debug, "Trace", new TES5ObjectCallArguments() { new TES5String("OBScript called OnHitWith using Ammo, but it's unlikely Papyrus will handle it properly. When arrows are used, " + akSource.Name + " will be a bow.") })); } newCodeScope.AddChunk(new TES5Branch(new TES5SubBranch(hitWithEqualsSource, codeScope))); codeScope = newCodeScope; } accountedForParameter = true; break; } case "onmagiceffecthit": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akEffect", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "onpackagestart": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akNewPackage", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "onpackagechange": case "onpackagedone": case "onpackageend": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akOldPackage", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "onsell": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akSeller", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "onstartcombat": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akTarget", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "ontrigger": { codeScope = SetUpBranch(block, codeScope, blockFunctionScope, "akActivateRef", globalScope, multipleScriptsScope); accountedForParameter = true; break; } case "ontriggeractor": { TES4BlockParameterList? parameterList = block.BlockParameterList; TES5LocalScope localScope = codeScope.LocalScope; ITES5VariableOrProperty activator = localScope.GetVariableWithMeaning(TES5LocalVariableParameterMeaning.ACTIVATOR); TES5LocalVariable castedToActor = new TES5LocalVariable("akAsActor", TES5BasicType.T_ACTOR); TES5Reference referenceToCastedVariable = TES5ReferenceFactory.CreateReferenceToVariableOrProperty(castedToActor); TES5Reference referenceToNonCastedVariable = TES5ReferenceFactory.CreateReferenceToVariableOrProperty(activator); TES5ComparisonExpression expression = TES5ExpressionFactory.CreateComparisonExpression(referenceToCastedVariable, TES5ComparisonExpressionOperator.OPERATOR_NOT_EQUAL, new TES5None()); TES5CodeScope newCodeScope = TES5CodeScopeFactory.CreateCodeScopeRoot(blockFunctionScope); newCodeScope.AddVariable(castedToActor); newCodeScope.AddChunk(TES5VariableAssignationFactory.CreateAssignation(referenceToCastedVariable, referenceToNonCastedVariable)); TES5CodeScope outerBranchCode = TES5CodeScopeFactory.CreateCodeScopeRoot(blockFunctionScope); outerBranchCode.LocalScope.CopyVariablesFrom(codeScope.LocalScope); if (parameterList != null) { ITES5Referencer targetActor = this.referenceFactory.CreateReadReference(parameterList.Parameters[0].BlockParameter, globalScope, multipleScriptsScope, localScope); TES5ComparisonExpression interExpression = TES5ExpressionFactory.CreateComparisonExpression(TES5ReferenceFactory.CreateReferenceToVariableOrProperty(activator), TES5ComparisonExpressionOperator.OPERATOR_EQUAL, targetActor); outerBranchCode.AddChunk(new TES5Branch(new TES5SubBranch(interExpression, codeScope))); } else { outerBranchCode.AddChunks(codeScope.CodeChunks); } newCodeScope.AddChunk(new TES5Branch(new TES5SubBranch(expression, outerBranchCode))); codeScope = newCodeScope; accountedForParameter = true; break; } case "onload": case "onreset": case "ontriggermob": case "scripteffectstart": case "scripteffectfinish": { break; } default: { throw new InvalidOperationException(block.BlockType + " not found."); } } if (!accountedForParameter) { TES4BlockParameterList?parameterList = block.BlockParameterList; if (parameterList != null && parameterList.Parameters.Any()) { throw new ConversionException("Parameter not accounted for in " + block.BlockType + ": " + parameterList.Parameters[0].BlockParameter); } } return(codeScope); }
/* * Joins N QF subfragments into one QF fragment that can be properly binded into Skyrim VM * @throws ConversionException */ public TES5Target JoinQFFragments(IBuildTarget target, string resultingFragmentName, List <QuestStageScript> subfragmentsTrees) { int tes4FormID = GetTES4FormID(resultingFragmentName); StageMap stageMap = StageMapBuilder.Build(target, resultingFragmentName, esmAnalyzer, tes4FormID); /* * We need script fragment for objective handling for each stage, so when parsing the script fragments, * we"ll be marking them there, and intersecting this with stage. * This will give us an array of stages which don"t have script fragment, but will need it anyways * for objective handling. */ TES5ScriptHeader resultingScriptHeader = TES5ScriptHeaderFactory.GetFromCacheOrConstructByBasicType(resultingFragmentName, TES5BasicType.T_QUEST, TES5TypeFactory.TES4_Prefix, true); TES5GlobalScope resultingGlobalScope = new TES5GlobalScope(resultingScriptHeader); string[] referenceAliases = ReferenceAliasBuilder.Build(target, resultingFragmentName, esmAnalyzer, tes4FormID).ToArray(); foreach (string propertyName in referenceAliases) { resultingGlobalScope.AddProperty(TES5PropertyFactory.ConstructWithoutFormID(propertyName, TES5BasicType.T_REFERENCEALIAS, propertyName)); } List <QuestStageBlock> questStageBlocks = new List <QuestStageBlock>(); HashSet <int> implementedStages = new HashSet <int>(); HashSet <string> propertiesNamesDeclared = new HashSet <string>(); foreach (var subfragment in subfragmentsTrees) { TES5Target subfragmentsTree = subfragment.Script; TES5Script subfragmentScript = subfragmentsTree.Script; TES5GlobalScope subfragmentGlobalScope = subfragmentScript.GlobalScope; foreach (TES5Property subfragmentProperty in subfragmentGlobalScope.Properties) { /* * Move over the properties to the new global scope */ if (propertiesNamesDeclared.Add(subfragmentProperty.Name)) { resultingGlobalScope.AddProperty(subfragmentProperty); } else { if (subfragmentProperty.IsPlayerRef) { continue; } //WTM: Change: I don't think renaming these properties actually helps anything. /* * string propertyName = GeneratePropertyName(subfragmentScript.ScriptHeader, subfragmentProperty); * subfragmentProperty.Rename(propertyName); * if (!propertiesNamesDeclared.Add(subfragmentProperty.Name)) * { * throw new ConversionException(nameof(propertiesNamesDeclared) + " already contained property " + subfragmentProperty.Name + "."); * } */ //WTM: Change: I'm trying to unify properties and include extended type declarations. TES5Property existingProperty = resultingGlobalScope.GetPropertyByName(subfragmentProperty.Name); if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(existingProperty.TES5Type, subfragmentProperty.TES5Type)) { continue; } if (TES5InheritanceGraphAnalyzer.IsExtending(subfragmentProperty.TES5Type, existingProperty.TES5Type)) { existingProperty.TES5Type = subfragmentProperty.TES5Type; continue; } if (TES5InheritanceGraphAnalyzer.IsExtending(existingProperty.TES5Type, subfragmentProperty.TES5Type.NativeType)) { subfragmentProperty.TES5Type.NativeType = existingProperty.TES5Type.NativeType; existingProperty.TES5Type = subfragmentProperty.TES5Type; continue; } throw new ConversionException("Types were not compatible for property " + subfragmentProperty.Name + ": " + subfragmentProperty.TES5Type.Value + " should extend " + existingProperty.TES5Type.Value + " (" + existingProperty.TES5Type.NativeType.Value + ")."); } } List <ITES5CodeBlock> subfragmentBlocks = subfragmentScript.BlockList.Blocks; if (subfragmentBlocks.Count != 1) { throw new ConversionException("Wrong QF fragment, actual function count: " + subfragmentBlocks.Count + ".."); } ITES5CodeBlock subfragmentBlock = subfragmentBlocks[0]; if (subfragmentBlock.FunctionScope.BlockName != TES5FragmentFactory.GetFragmentName(0)) { throw new ConversionException("Wrong QF fragment funcname, actual function name: " + subfragmentBlock.FunctionScope.BlockName + "."); } string newFragmentFunctionName = TES5FragmentFactory.GetFragmentName(subfragment.Stage, subfragment.LogIndex); subfragmentBlock.FunctionScope.Rename(newFragmentFunctionName); List <int>?stageMapOfStage = stageMap.TryGetStageTargetsMap(subfragment.Stage); if (stageMapOfStage != null) { var objectiveCodeChunks = objectiveHandlingFactory.GenerateObjectiveHandling(subfragmentBlock, resultingGlobalScope, stageMapOfStage); foreach (var newCodeChunk in objectiveCodeChunks) { subfragmentBlock.AddChunk(newCodeChunk); } } questStageBlocks.Add(new QuestStageBlock(subfragment.Stage, subfragment.LogIndex, subfragmentBlock)); implementedStages.Add(subfragment.Stage); } /* * Diff to find stages which we still need to mark */ int[] nonDoneStages = stageMap.StageIDs.Where(stageID => !implementedStages.Contains(stageID)).ToArray(); foreach (int nonDoneStage in nonDoneStages) { TES5FunctionCodeBlock fragment = objectiveHandlingFactory.CreateEnclosedFragment(resultingGlobalScope, nonDoneStage, stageMap.GetStageTargetsMap(nonDoneStage)); questStageBlocks.Add(new QuestStageBlock(nonDoneStage, 0, fragment)); } this.mappedTargetsLogService.WriteScriptName(resultingFragmentName); foreach (var kvp in stageMap.MappedTargetsIndex) { var originalTargetIndex = kvp.Key; var mappedTargetIndexes = kvp.Value; this.mappedTargetsLogService.WriteLine(originalTargetIndex, mappedTargetIndexes); } TES5BlockList resultingBlockList = new TES5BlockList(questStageBlocks.OrderBy(b => b.StageID).ThenBy(b => b.LogIndex).Select(b => b.CodeBlock)); TES5Script resultingTree = new TES5Script(resultingGlobalScope, resultingBlockList, true); string outputPath = target.GetTranspileToPath(resultingFragmentName); return(new TES5Target(resultingTree, outputPath)); }
private bool ReferenceIsIntAndValueExtendsForm() { return(this.Reference.TES5Type == TES5BasicType.T_INT && TES5InheritanceGraphAnalyzer.IsExtending(this.value.TES5Type, TES5BasicType.T_FORM)); }
//WTM: Change: Added: //I think GetItemCount returns 0 for all Containers that have ObjectReference scripts before they are opened: //https://forums.nexusmods.com/index.php?/topic/7174696-huge-mystery-with-getitemcount/#entry65389511 //Once the player opens the Container once, then GetItemCount starts returning the correct count. //This does not seem to apply to ObjectRefence, so I've excluded it below. private TES5ComparisonExpression?ConvertGetItemCountCallToContainsItemCall(ITES5Value leftValue, TES5ComparisonExpressionOperator op, ITES5Value rightValue) { if (rightValue.TES5Type == TES5BasicType.T_INT) { TES5ObjectCall?value1ObjectCall = leftValue as TES5ObjectCall; if (value1ObjectCall != null && value1ObjectCall.FunctionName.Equals("GetItemCount", StringComparison.OrdinalIgnoreCase)) { ITES5Referencer accessedObject = value1ObjectCall.AccessedObject; if (accessedObject is TES5PlayerReference) { return(null); } //WTM: Note: GetItemCount works for the player, so the call doesn't need to be converted to ContainsItem. ITES5VariableOrProperty?calledOn = accessedObject.ReferencesTo; if (calledOn == null) { TES5ObjectCall?innerObjectCall = accessedObject as TES5ObjectCall; if (innerObjectCall == null) { throw new InvalidOperationException("Called on object could not be found."); } calledOn = innerObjectCall.AccessedObject.ReferencesTo; if (calledOn == null) { throw new InvalidOperationException("Called on object could not be found."); } } if (calledOn.ReferenceEDID == null) { throw new NullableException(nameof(calledOn.ReferenceEDID)); } ITES5Type?referenceType = esmAnalyzer.GetTypeByEDIDWithFollow(calledOn.ReferenceEDID, true, useScriptTypeCache: false); if (referenceType == null) { referenceType = calledOn.TES5Type; } //WTM: Note: Revert to type determined by previous inferences. /* * //WTM: Note: Example: * calledOn.ReferenceEDID: Dark11ChorrolSackRef * referenceType.OriginalName: Dark11ChorrolDropScript * referenceType.NativeType: Container * Dark11ChorrolSack "Old Sack" [CONT:00031B2B] SCRI: Dark11ChorrolDropScript [SCPT:00031B28] * Dark11ChorrolSackRef is an ObjectReference with a base object of a Container, * but Dark11ChorrolDropScript had to be inferred as an ObjectReference for transpilation to complete. */ if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(referenceType, TES5BasicType.T_OBJECTREFERENCE)) {//WTM: Note: I think GetItemCount works for ObjectReferences that are transpiled as ObjectReferences. //So they don't require the use of ContainsItem. return(null); } TES5Integer?value2Integer = rightValue as TES5Integer; if (value2Integer != null) { bool canConvert, negate = false; if ( (op == TES5ComparisonExpressionOperator.OPERATOR_GREATER && value2Integer.IntValue == 0) || (op == TES5ComparisonExpressionOperator.OPERATOR_GREATER_OR_EQUAL && value2Integer.IntValue == 1) || (op == TES5ComparisonExpressionOperator.OPERATOR_NOT_EQUAL && value2Integer.IntValue == 0) ) { canConvert = true; } else if ( (op == TES5ComparisonExpressionOperator.OPERATOR_LESS && value2Integer.IntValue == 1) || (op == TES5ComparisonExpressionOperator.OPERATOR_LESS_OR_EQUAL && value2Integer.IntValue == 0) || (op == TES5ComparisonExpressionOperator.OPERATOR_EQUAL && value2Integer.IntValue == 0) ) { canConvert = true; negate = true; } else { canConvert = CanConvertGetItemCountSpecialCases(value1ObjectCall, op, value2Integer); } if (canConvert) { TES5ObjectCall newObjectCall = objectCallFactory.CreateObjectCall(value1ObjectCall.AccessedObject, "ContainsItem", value1ObjectCall.Arguments); TES5ComparisonExpression comparisonExpression = TES5ExpressionFactory.CreateComparisonExpression(newObjectCall, TES5ComparisonExpressionOperator.OPERATOR_EQUAL, new TES5Bool(!negate)); return(comparisonExpression); } } } } return(null); }
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)); }