Example #1
0
        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);
        }
Example #2
0
        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);
        }
Example #3
0
        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);
        }
Example #4
0
        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 + ")");
                }
            }
        }
Example #5
0
        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++;
            }
        }
Example #6
0
        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);
        }
Example #7
0
        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));
        }
Example #9
0
        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);
        }
        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);
        }
Example #11
0
        /*
         * 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));
        }
Example #12
0
        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));
        }
Example #13
0
        //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);
        }
Example #14
0
 private bool ReferenceAndValueDifferentTypesAndValueIsNonNone()
 {
     return(!TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(this.value.TES5Type, this.Reference.TES5Type) && !(this.value is TES5None));
 }