// End Container SLOTs interface

        public List <VMPieMenuInteraction> GetPieMenu(VM vm,VMEntity caller)
        {
            var pie = new List <VMPieMenuInteraction>();

            if (TreeTable == null)
            {
                return(pie);
            }

            for (int i = 0; i < TreeTable.Interactions.Length; i++)
            {
                var action = TreeTable.Interactions[i];

                bool CanRun = false;
                if (action.TestFunction != 0 && (((TTABFlags)action.Flags & TTABFlags.Debug) != TTABFlags.Debug))
                {
                    caller.ObjectData[(int)VMStackObjectVariable.HideInteraction] = 0;
                    var Behavior = GetBHAVWithOwner(action.TestFunction,vm.Context);
                    CanRun = (VMThread.EvaluateCheck(vm.Context,caller,new VMQueuedAction()
                    {
                        Callee = this,
                        CodeOwner = Behavior.owner,
                        StackObject = this,
                        Routine = vm.Assemble(Behavior.bhav),
                    }) == VMPrimitiveExitCode.RETURN_TRUE);
                    if (caller.ObjectData[(int)VMStackObjectVariable.HideInteraction] == 1)
                    {
                        CanRun = false;
                    }
                }
                else
                {
                    CanRun = true;
                }

                if (CanRun)
                {
                    pie.Add(new VMPieMenuInteraction()
                    {
                        Name = TreeTableStrings.GetString((int)action.TTAIndex),
                        ID   = (byte)action.TTAIndex
                    });
                }
            }

            return(pie);
        }
示例#2
0
 public string GetTTA(uint index)
 {
     if (Strings == null || Strings.Length <= index)
     {
         return("???");
     }
     return(Strings.GetString((int)index, ActiveLanguage));
 }
示例#3
0
        public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args)
        {
            //if we already have some action, do nothing.
            if (context.Caller.Thread.Queue.Any(x => x.Mode != VMQueueMode.Idle))
            {
                return(VMPrimitiveExitCode.GOTO_TRUE);
            }

            var ents      = new List <VMEntity>(context.VM.Entities);
            var processed = new HashSet <short>();
            var pos1      = context.Caller.Position;

            List <VMPieMenuInteraction> validActions  = new List <VMPieMenuInteraction>();
            List <VMPieMenuInteraction> betterActions = new List <VMPieMenuInteraction>();

            foreach (var iobj in ents)
            {
                if (iobj.Position == LotTilePos.OUT_OF_WORLD)
                {
                    continue;
                }
                var obj = iobj.MultitileGroup.GetInteractionGroupLeader(iobj);
                if (processed.Contains(obj.ObjectID) || (obj is VMGameObject && ((VMGameObject)obj).Disabled > 0))
                {
                    continue;
                }
                processed.Add(obj.ObjectID);

                var pos2     = obj.Position;
                var distance = (short)Math.Floor(Math.Sqrt(Math.Pow(pos1.x - pos2.x, 2) + Math.Pow(pos1.y - pos2.y, 2)) / 16.0);

                if (obj.TreeTable == null)
                {
                    continue;
                }
                foreach (var entry in obj.TreeTable.Interactions)
                {
                    //motive scores must be above their threshold.
                    //pick maximum motive score as our base score
                    int  baseScore = 0;
                    int  negScore  = 0;
                    bool hasAuto   = false;
                    for (int i = 0; i < entry.MotiveEntries.Length; i++)
                    {
                        var motiveScore = entry.MotiveEntries[i];
                        if (motiveScore.EffectRangeMaximum == 0 && motiveScore.EffectRangeMinimum == 0)
                        {
                            continue;
                        }
                        hasAuto = true;
                        //LINEAR INTERPOLATE MIN SCORE TO MAX, using motive data of caller
                        //MAX is when motive is the lowest.
                        //can be in reverse too!
                        var myMotive = ((VMAvatar)context.Caller).GetMotiveData((VMMotive)i);

                        var rangeScore = motiveScore.EffectRangeMinimum + motiveScore.EffectRangeMaximum - myMotive; //0 at max, EffectRangeMaximum at minimum.
                        if (motiveScore.EffectRangeMaximum > 0)
                        {
                            rangeScore = Math.Max(0, Math.Min(motiveScore.EffectRangeMaximum, rangeScore));                                     //enforce range
                        }
                        else
                        {
                            rangeScore = Math.Max(motiveScore.EffectRangeMaximum, Math.Max(0, rangeScore));  //also for negative
                        }
                        //todo: personality ads add values 0-100 for their given personality. makes things like viewing flamingo much more common.
                        baseScore = Math.Max(baseScore, rangeScore);
                        negScore  = Math.Min(negScore, rangeScore);

                        //int interpScore = (myMotive*motiveScore.EffectRangeMinimum + (100-myMotive)*(motiveScore.EffectRangeMinimum+motiveScore.EffectRangeMaximum)) / 100;
                        //if (interpScore > baseScore) baseScore = interpScore;
                    }
                    baseScore -= negScore;
                    float atten      = (entry.AttenuationCode == 0) ? entry.AttenuationValue : TTAB.AttenuationValues[entry.AttenuationCode];
                    int   attenScore = (int)Math.Max(0, baseScore * (1f - (distance * atten)));
                    if (attenScore != 0)
                    {
                        attenScore += (int)context.VM.Context.NextRandom(31) - 15;
                    }



                    if (hasAuto)
                    {
                        var  id     = entry.TTAIndex;
                        var  caller = context.Caller;
                        var  action = obj.GetAction((int)id, caller, context.VM.Context, false);
                        TTAs ttas   = obj.TreeTableStrings;

                        caller.ObjectData[(int)VMStackObjectVariable.HideInteraction] = 0;
                        if (action != null)
                        {
                            action.Flags &= ~TTABFlags.MustRun;
                        }
                        var actionStrings = caller.Thread.CheckAction(action);

                        var pie = new List <VMPieMenuInteraction>();
                        if (actionStrings != null)
                        {
                            if (actionStrings.Count > 0)
                            {
                                foreach (var actionS in actionStrings)
                                {
                                    actionS.ID     = (byte)id;
                                    actionS.Entry  = entry;
                                    actionS.Global = false;
                                    pie.Add(actionS);
                                }
                            }
                            else
                            {
                                if (ttas != null)
                                {
                                    pie.Add(new VMPieMenuInteraction()
                                    {
                                        Name   = ttas.GetString((int)id),
                                        ID     = (byte)id,
                                        Entry  = entry,
                                        Global = false
                                    });
                                }
                            }
                        }
                        foreach (var item in pie)
                        {
                            item.Score  = attenScore;
                            item.Callee = obj;
                            validActions.Add(item);
                        }
                    }
                    //TODO: Lockout interactions that have been used before for a few sim hours (in ts1 ticks. same # of ticks for tso probably)
                    //TODO: special logic for socials?
                }
            }

            var sorted    = validActions.OrderBy(x => - x.Score).ToList();
            var selection = sorted.FirstOrDefault();

            if (selection == null)
            {
                return(VMPrimitiveExitCode.GOTO_FALSE);
            }
            if (!selection.Entry.AutoFirst)
            {
                selection = sorted[(int)context.VM.Context.NextRandom((ulong)Math.Min(4, sorted.Count))];
            }

            selection.Callee.PushUserInteraction(selection.ID, context.Caller, context.VM.Context, false, new short[] { selection.Param0, 0, 0, 0 });
            return(VMPrimitiveExitCode.GOTO_TRUE);
        }
示例#4
0
        public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args)
        {
            //if we already have some action, do nothing
            var better = context.Caller.Thread.Queue.FirstOrDefault(x => x.Priority > (context.Caller as VMAvatar).GetPersonData(VMPersonDataVariable.Priority));

            if (better != null)
            {
                context.StackObject = better.Callee;
                return(VMPrimitiveExitCode.GOTO_TRUE);
            }

            var ents      = new List <VMEntity>(context.VM.Context.ObjectQueries.WithAutonomy);
            var processed = new HashSet <short>();
            var caller    = (VMAvatar)context.Caller;
            var pos1      = caller.Position;

            var visitor          = (caller.GetPersonData(VMPersonDataVariable.PersonType) == 1);
            var child            = (caller.IsChild && context.VM.TS1);
            var attenTable       = visitor ? TTAB.VisitorAttenuationValues : TTAB.AttenuationValues;
            var global           = Content.Content.Get().WorldObjectGlobals;
            var interactionCurve = child ? global.InteractionScoreChild : global.InteractionScore;
            var happyCurve       = child ? global.HappyWeightChild : global.HappyWeight;

            var isStray = caller.IsPet && context.VM.TS1 && caller.GetPersonData(VMPersonDataVariable.GreetStatus) == 0 && caller.GetPersonData(VMPersonDataVariable.PersonType) == 1;

            // === HAPPY CALCULATION ===

            var newStyle = false;
            // TODO: new style
            var weights = newStyle ? new float[9] : new float[9] {
                1, 1, 1, 1, 1, 1, 1, 1, 1
            };                                                                                  //TODO: weights from curves for new
            var totalWeight = weights.Sum();

            for (int i = 0; i < 9; i++)
            {
                weights[i] /= totalWeight;
            }
            var minScore = (caller.GetPersonData(VMPersonDataVariable.Posture) > 0) ? 1e-6 : 1e-7;

            var motives    = WeightMotives.Select(x => caller.GetMotiveData(x));
            var happyParts = motives
                             .Zip(interactionCurve, (motive, curve) => curve.GetPoint(motive))
                             .Zip(weights, (motive, weight) => motive * weight)
                             .ToArray();

            float baseHappy = happyParts.Sum();

            List <VMPieMenuInteraction> validActions = new List <VMPieMenuInteraction>();

            foreach (var iobj in ents)
            {
                if (iobj.Position == LotTilePos.OUT_OF_WORLD)
                {
                    continue;
                }
                var obj = iobj.MultitileGroup.GetInteractionGroupLeader(iobj);
                if (processed.Contains(obj.ObjectID) || (obj is VMGameObject && ((VMGameObject)obj).Disabled > 0))
                {
                    continue;
                }
                processed.Add(obj.ObjectID);

                if (isStray)
                {
                    if (obj is VMGameObject)
                    {
                        continue;
                    }
                    //determine if the object is indoors
                    var roomID = context.VM.Context.GetObjectRoom(obj);
                    roomID = (ushort)Math.Max(0, Math.Min(context.VM.Context.RoomInfo.Length - 1, roomID));
                    var room    = context.VM.Context.RoomInfo[roomID];
                    var outside = room.Room.IsOutside;
                    if (!outside)
                    {
                        continue;
                    }
                }
                var pos2     = obj.Position;
                var distance = (float)Math.Sqrt(Math.Pow(pos1.x - pos2.x, 2) + Math.Pow(pos1.y - pos2.y, 2) + Math.Pow((pos1.Level - pos2.Level) * 320, 2.0)) / 16.0f;
                var inUse    = obj.GetFlag(VMEntityFlags.Occupied);

                if (obj.TreeTable == null)
                {
                    continue;
                }
                foreach (var entry in obj.TreeTable.AutoInteractions)
                {
                    var id = entry.TTAIndex;
                    if (inUse && !obj.TreeTable.Interactions.Any(x => x.JoiningIndex == id))
                    {
                        continue;
                    }
                    var advertisements = entry.ActiveMotiveEntries; //TODO: cache this
                    if (advertisements.Length == 0)
                    {
                        continue;                             //no ads on this object.
                    }
                    var  action = obj.GetAction((int)id, caller, context.VM.Context, false);
                    TTAs ttas   = obj.TreeTableStrings;

                    caller.ObjectData[(int)VMStackObjectVariable.HideInteraction] = 0;
                    var actionStrings = caller.Thread.CheckAction(action, true);

                    var pie = new List <VMPieMenuInteraction>();
                    if (actionStrings != null)
                    {
                        if (actionStrings.Count > 0)
                        {
                            foreach (var actionS in actionStrings)
                            {
                                if (actionS.Name == null)
                                {
                                    actionS.Name = ttas?.GetString((int)id) ?? "***MISSING***";
                                }
                                actionS.ID     = (byte)id;
                                actionS.Entry  = entry;
                                actionS.Global = false;
                                pie.Add(actionS);
                            }
                        }
                        else
                        {
                            if (ttas != null)
                            {
                                pie.Add(new VMPieMenuInteraction()
                                {
                                    Name   = ttas.GetString((int)id),
                                    ID     = (byte)id,
                                    Entry  = entry,
                                    Global = false,
                                });
                            }
                        }
                    }
                    var first = pie.FirstOrDefault();
                    if (first != null)
                    {
                        // calculate score for this tree.
                        // start with the base happy value, and modify it for each motive changed.
                        float score = baseHappy;
                        for (int i = 0; i < advertisements.Length; i++)
                        {
                            var   motiveI     = advertisements[i].MotiveIndex;
                            var   motiveScore = entry.MotiveEntries[motiveI];
                            short min         = motiveScore.EffectRangeMinimum;
                            short max         = motiveScore.EffectRangeDelta;
                            short personality = (short)motiveScore.PersonalityModifier;
                            if (first.MotiveAdChanges != null)
                            {
                                first.MotiveAdChanges.TryGetValue((0 << 16) | motiveI, out min);
                                first.MotiveAdChanges.TryGetValue((1 << 16) | motiveI, out max);
                                first.MotiveAdChanges.TryGetValue((2 << 16) | motiveI, out personality);
                            }

                            if (max == 0 && min > 0)
                            {
                                //invalid delta. do from 0..delta instead (fix child-talk preference?)
                                max = min;
                                min = 0;
                            }
                            max += min; //it's a delta, add min to it

                            var myMotive = caller.GetMotiveData((VMMotive)motiveI);
                            if (min != 0 && myMotive > min)
                            {
                                continue;
                            }

                            // subtract the base contribution for this motive from happy
                            var weightInd = MotiveToWeight[motiveI];
                            if (weightInd == -1)
                            {
                                continue;
                            }
                            score -= happyParts[weightInd];

                            float personalityMul = 1;
                            if (personality > 0 && personality < VaryByTypes.Length)
                            {
                                personalityMul  = caller.GetPersonData(VaryByTypes[personality]);
                                personalityMul /= 1000f;
                                if (personality < 13)
                                {
                                    if ((personality & 1) == 0)
                                    {
                                        personalityMul = 1 - personalityMul;
                                    }
                                }
                                else
                                {
                                    personalityMul *= 2;
                                }
                            }

                            // then add the new contribution for this motive.
                            score += interactionCurve[weightInd].GetPoint(myMotive + (max * personalityMul) / 1000f) * weights[weightInd];
                        }

                        // score relative to base
                        score -= baseHappy;
                        // modify score using attenuation
                        float atten = (entry.AttenuationCode == 0 || entry.AttenuationCode >= attenTable.Length) ?
                                      entry.AttenuationValue : attenTable[entry.AttenuationCode];

                        score = score / (1 + atten * distance);

                        if (score > minScore)
                        {
                            foreach (var item in pie)
                            {
                                item.Score  = score;
                                item.Callee = obj;
                                validActions.Add(first);
                            }
                        }
                    }
                    //if (attenScore != 0) attenScore += (int)context.VM.Context.NextRandom(31) - 15;

                    //TODO: Lockout interactions that have been used before for a few sim hours (in ts1 ticks. same # of ticks for tso probably)
                    //TODO: special logic for socials?
                }
            }
            List <VMPieMenuInteraction> sorted = validActions.OrderByDescending(x => x.Score).ToList();

            sorted = TakeTopActions(sorted, 4);
            var selection = sorted.FirstOrDefault();

            if (selection == null)
            {
                return(VMPrimitiveExitCode.GOTO_FALSE);
            }
            if (!selection.Entry.AutoFirst)
            {
                // weighted random selection
                //var slice = sorted.Take(Math.Min(4, sorted.Count)).ToList();
                var totalScore = sorted.Sum(x => x.Score);
                var random     = context.VM.Context.NextRandom(10000);

                float randomTotal = 0;
                for (int i = 0; i < sorted.Count; i++)
                {
                    var action = sorted[i];
                    randomTotal += (sorted[i].Score / totalScore) * 10000;
                    if (random <= randomTotal)
                    {
                        selection = action;
                        break;
                    }
                }
            }

            var qaction = selection.Callee.GetAction(selection.ID, context.Caller, context.VM.Context, false, new short[] { selection.Param0, 0, 0, 0 });

            if (qaction != null)
            {
                qaction.Priority = (short)VMQueuePriority.Autonomous;
                context.Caller.Thread.EnqueueAction(qaction);
                context.StackObject = selection.Callee;
                return(VMPrimitiveExitCode.GOTO_TRUE);
            }
            else
            {
                return(VMPrimitiveExitCode.GOTO_FALSE);
            }
        }