Exemple #1
0
        /// <summary>
        /// Parses combat related data
        /// </summary>
        private IEnumerable <ParsedCombatItem> ParseCombatItems(int revision, ByteArrayBinaryReader reader)
        {
            switch (revision)
            {
            case 0:
                // 64 bytes: each combat item
                while (reader.Length - reader.Position >= 64)
                {
                    ParsedCombatItem combatItem = ReadCombatItemRevision0(reader);
                    yield return(combatItem);
                }

                break;

            case 1:
                // 64 bytes: each combat item
                while (reader.Length - reader.Position >= 64)
                {
                    ParsedCombatItem combatItem = ReadCombatItemRevision1(reader);
                    yield return(combatItem);
                }

                break;

            default:
                throw new NotSupportedException("Only EVTC revisions 0 and 1 are supported.");
            }
        }
Exemple #2
0
        /// <summary>
        /// Update the names of players in order to hide their identity. Also removes guild data.
        /// </summary>
        /// <param name="log">The log data that will be updated.</param>
        /// <exception cref="NotSupportedException">Thrown if the log is not a supported revision.</exception>
        public void AnonymizePlayers(ParsedLog log)
        {
            EnsureRevisionIsSupported(log.LogVersion);

            int playerIndex = 1;

            for (int i = 0; i < log.ParsedAgents.Count; i++)
            {
                var agent = log.ParsedAgents[i];
                if (agent.IsElite == 0xFFFFFFFF)
                {
                    // This agent is not a player
                    continue;
                }

                // The subgroup is encoded within the name, so we need to reconstruct this.
                // It is also possible that some parts may be missing in case the players in
                // the log are enemies, in that case we maintain that.
                var    nameParts       = agent.Name.Split(new[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
                string characterName   = nameParts[0];
                string accountName     = nameParts.Length > 1 ? nameParts[1] : null;
                string subgroupLiteral = nameParts.Length > 2 ? nameParts[2] : null;

                if (accountName != null)
                {
                    accountName = $":Anonymous.{playerIndex:0000}";
                }

                characterName = $"Player {playerIndex}";

                string updatedName = $"{characterName}\0{accountName ?? ""}\0{subgroupLiteral ?? ""}";

                var updatedAgent = new ParsedAgent(agent.Address, updatedName, agent.Prof, agent.IsElite,
                                                   agent.Toughness, agent.Concentration, agent.Healing, agent.Condition, agent.HitboxWidth,
                                                   agent.HitboxHeight);

                log.ParsedAgents[i] = updatedAgent;
                playerIndex++;
            }

            for (int i = 0; i < log.ParsedCombatItems.Count; i++)
            {
                var item = log.ParsedCombatItems[i];
                if (item.IsStateChange != StateChange.Guild)
                {
                    continue;
                }
                // We cannot just remove the events as that would break expectations for present for a specific log version.
                // Instead we set the guild guid to zero, which corresponds to having no guild.

                // dst, value and buffdmg have to be zeroed
                var updatedItem = new ParsedCombatItem(item.Time, item.SrcAgent, 0, 0, 0, item.OverstackValue, item.SkillId, item.SrcAgentId,
                                                       item.DstAgentId, item.SrcMasterId, item.DstMasterId, item.Iff, item.Buff, item.Result, item.IsActivation, item.IsBuffRemove, item.IsNinety,
                                                       item.IsFifty, item.IsMoving, item.IsStateChange, item.IsFlanking, item.IsShields, item.IsOffCycle, item.Padding);

                log.ParsedCombatItems[i] = updatedItem;
            }
        }
Exemple #3
0
        private Event GetEvent(LogProcessorState state,
                               IReadOnlyDictionary <uint, Skill> skillsById, ParsedCombatItem item)
        {
            Debug.Assert(state.AgentsByAddress != null);

            Agent GetAgentByAddress(ulong address)
            {
                if (state.AgentsByAddress.TryGetValue(address, out Agent agent))
                {
                    return(agent);
                }

                return(null);
            }

            Skill GetSkillById(uint id)
            {
                if (skillsById.TryGetValue(id, out Skill skill))
                {
                    return(skill);
                }

                return(null);
            }

            if (item.IsStateChange != StateChange.Normal)
            {
                switch (item.IsStateChange)
                {
                case StateChange.EnterCombat:
                    return(new AgentEnterCombatEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                     (int)item.DstAgent));

                case StateChange.ExitCombat:
                    return(new AgentExitCombatEvent(item.Time, GetAgentByAddress(item.SrcAgent)));

                case StateChange.ChangeUp:
                    return(new AgentRevivedEvent(item.Time, GetAgentByAddress(item.SrcAgent)));

                case StateChange.ChangeDead:
                    return(new AgentDeadEvent(item.Time, GetAgentByAddress(item.SrcAgent)));

                case StateChange.ChangeDown:
                    return(new AgentDownedEvent(item.Time, GetAgentByAddress(item.SrcAgent)));

                case StateChange.Spawn:
                    return(new AgentSpawnEvent(item.Time, GetAgentByAddress(item.SrcAgent)));

                case StateChange.Despawn:
                    return(new AgentDespawnEvent(item.Time, GetAgentByAddress(item.SrcAgent)));

                case StateChange.HealthUpdate:
                    var healthFraction = item.DstAgent / 10000f;
                    return(new AgentHealthUpdateEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                      healthFraction));

                case StateChange.WeaponSwap:
                    WeaponSet newWeaponSet;
                    switch (item.DstAgent)
                    {
                    case 0:
                        newWeaponSet = WeaponSet.Water1;
                        break;

                    case 1:
                        newWeaponSet = WeaponSet.Water2;
                        break;

                    case 4:
                        newWeaponSet = WeaponSet.Land1;
                        break;

                    case 5:
                        newWeaponSet = WeaponSet.Land2;
                        break;

                    default:
                        newWeaponSet = WeaponSet.Unknown;
                        break;
                    }

                    return(new AgentWeaponSwapEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                    newWeaponSet));

                case StateChange.MaxHealthUpdate:
                    return(new AgentMaxHealthUpdateEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                         item.DstAgent));

                case StateChange.Reward:
                    return(new RewardEvent(item.Time, item.DstAgent, item.Value));

                case StateChange.BuffInitial:
                    return(new InitialBuffEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                GetSkillById(item.SkillId)));

                case StateChange.Position:
                {
                    float x = BitConversions.ToSingle((uint)(item.DstAgent & 0xFFFFFFFF));
                    float y = BitConversions.ToSingle((uint)(item.DstAgent >> 32 & 0xFFFFFFFF));
                    float z = BitConversions.ToSingle(item.Value);

                    return(new PositionChangeEvent(item.Time, GetAgentByAddress(item.SrcAgent), x, y, z));
                }

                case StateChange.Velocity:
                {
                    float x = BitConversions.ToSingle((uint)(item.DstAgent & 0xFFFFFFFF));
                    float y = BitConversions.ToSingle((uint)(item.DstAgent >> 32 & 0xFFFFFFFF));
                    float z = BitConversions.ToSingle(item.Value);

                    return(new VelocityChangeEvent(item.Time, GetAgentByAddress(item.SrcAgent), x, y, z));
                }

                case StateChange.Rotation:
                {
                    float x = BitConversions.ToSingle((uint)(item.DstAgent & 0xFFFFFFFF));
                    float y = BitConversions.ToSingle((uint)(item.DstAgent >> 32 & 0xFFFFFFFF));

                    return(new FacingChangeEvent(item.Time, GetAgentByAddress(item.SrcAgent), x, y));
                }

                case StateChange.TeamChange:
                    return(new TeamChangeEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                               item.DstAgent));

                case StateChange.Targetable:
                {
                    var agent = GetAgentByAddress(item.SrcAgent);
                    if (agent is AttackTarget target)
                    {
                        return(new TargetableChangeEvent(item.Time, target, item.DstAgent != 0));
                    }
                    else
                    {
                        return(new UnknownEvent(item.Time, item));
                    }
                }

                case StateChange.ReplInfo:
                    return(new UnknownEvent(item.Time, item));

                case StateChange.StackActive:
                    return(new ActiveBuffStackEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                    (uint)item.DstAgent));

                case StateChange.StackReset:
                    return(new ResetBuffStackEvent(item.Time, GetAgentByAddress(item.SrcAgent), item.Padding,
                                                   item.Value));

                case StateChange.BreakbarState:
                    var breakbarState = item.Value switch
                    {
                        0 => DefianceBarStateUpdateEvent.DefianceBarState.Active,
                        1 => DefianceBarStateUpdateEvent.DefianceBarState.Recovering,
                        2 => DefianceBarStateUpdateEvent.DefianceBarState.Immune,
                        3 => DefianceBarStateUpdateEvent.DefianceBarState.None,
                        _ => DefianceBarStateUpdateEvent.DefianceBarState.Unknown
                    };
                    return(new DefianceBarStateUpdateEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                           breakbarState));

                case StateChange.BreakbarPercent:
                    // This encoding is inconsistent with the health update.
                    float breakbarHealthFraction = BitConversions.ToSingle(item.Value);
                    return(new DefianceBarHealthUpdateEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                            breakbarHealthFraction));

                case StateChange.BuffInfo:
                // TODO: Figure out what the contents are
                case StateChange.BuffFormula:
                // TODO: Figure out what the contents are
                case StateChange.SkillInfo:
                // TODO: Figure out what the contents are
                case StateChange.SkillTiming:
                // TODO: Figure out what the contents are
                case StateChange.Error:
                    // TODO: Implement
                    return(new UnknownEvent(item.Time, item));

                case StateChange.Tag:
                    return(new AgentTagEvent(item.Time, GetAgentByAddress(item.SrcAgent), item.Value));

                case StateChange.Unknown:
                    return(new UnknownEvent(item.Time, item));

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }
            else if (item.IsActivation != Activation.None)
            {
                switch (item.IsActivation)
                {
                case Activation.CancelCancel:
                    return(new EndSkillCastEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                 GetSkillById(item.SkillId), item.Value, EndSkillCastEvent.SkillEndType.Cancel));

                case Activation.CancelFire:
                    return(new EndSkillCastEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                 GetSkillById(item.SkillId), item.Value, EndSkillCastEvent.SkillEndType.Fire));

                case Activation.Normal:
                    return(new StartSkillCastEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                   GetSkillById(item.SkillId), item.Value, StartSkillCastEvent.SkillCastType.Normal));

                case Activation.Quickness:
                    return(new StartSkillCastEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                   GetSkillById(item.SkillId), item.Value,
                                                   StartSkillCastEvent.SkillCastType.WithQuickness));

                case Activation.Reset:
                    return(new ResetSkillCastEvent(item.Time, GetAgentByAddress(item.SrcAgent),
                                                   GetSkillById(item.SkillId), item.Value));

                case Activation.Unknown:
                    return(new UnknownEvent(item.Time, item));
                }
            }
            else if (item.Buff != 0 && item.IsBuffRemove != BuffRemove.None)
            {
                Skill buff = GetSkillById(item.SkillId);
                int   remainingDuration  = item.Value;
                int   remainingIntensity = item.BuffDmg;
                int   stacksRemoved      = (int)item.Result;
                var   cleansingAgent     = GetAgentByAddress(item.DstAgent);
                var   agent = GetAgentByAddress(item.SrcAgent);
                switch (item.IsBuffRemove)
                {
                case BuffRemove.All:
                    return(new AllStacksRemovedBuffEvent(item.Time, agent, buff, cleansingAgent,
                                                         stacksRemoved));

                case BuffRemove.Single:
                    uint stackId = item.Padding;
                    return(new SingleStackRemovedBuffEvent(item.Time, agent, buff, cleansingAgent,
                                                           remainingDuration, remainingIntensity, stackId));

                case BuffRemove.Manual:
                    return(new ManualStackRemovedBuffEvent(item.Time, agent, buff, cleansingAgent,
                                                           remainingDuration, remainingIntensity));

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }
            else if (item.Buff > 0 && item.BuffDmg == 0)
            {
                Skill buff                   = GetSkillById(item.SkillId);
                int   durationApplied        = item.Value;
                uint  durationOfRemovedStack = item.OverstackValue;
                var   agent                  = GetAgentByAddress(item.DstAgent);
                var   sourceAgent            = GetAgentByAddress(item.SrcAgent);
                return(new BuffApplyEvent(item.Time, agent, buff, sourceAgent, durationApplied,
                                          durationOfRemovedStack));
            }
            else if (item.Buff > 0 && item.Value == 0)
            {
                Skill buff       = GetSkillById(item.SkillId);
                int   buffDamage = item.BuffDmg;
                bool  isOffCycle = item.IsOffCycle > 0;
                Agent attacker   = GetAgentByAddress(item.SrcAgent);
                Agent defender   = GetAgentByAddress(item.DstAgent);
                bool  isMoving   = item.IsMoving > 0;
                bool  isNinety   = item.IsNinety > 0;
                bool  isFlanking = item.IsFlanking > 0;
                bool  isIgnored  = item.Result != 0;

                if (isIgnored)
                {
                    var reason = item.Result == (Result)1
                                                ? IgnoredBuffDamageEvent.Reason.InvulnerableBuff
                                                : IgnoredBuffDamageEvent.Reason.InvulnerableSkill;

                    return(new IgnoredBuffDamageEvent(item.Time, attacker, defender, buff, buffDamage,
                                                      isMoving, isNinety, isFlanking, reason));
                }
                else
                {
                    if (isOffCycle)
                    {
                        return(new OffCycleBuffDamageEvent(item.Time, attacker, defender, buff, buffDamage,
                                                           isMoving, isNinety, isFlanking));
                    }
                    else
                    {
                        return(new BuffDamageEvent(item.Time, attacker, defender, buff, buffDamage, isMoving,
                                                   isNinety, isFlanking));
                    }
                }
            }
            else if (item.Buff == 0)
            {
                int   damage       = item.Value;
                uint  shieldDamage = item.OverstackValue;
                Agent attacker     = GetAgentByAddress(item.SrcAgent);
                Agent defender     = GetAgentByAddress(item.DstAgent);
                Skill skill        = GetSkillById(item.SkillId);
                bool  isMoving     = item.IsMoving > 0;
                bool  isNinety     = item.IsNinety > 0;
                bool  isFlanking   = item.IsFlanking > 0;

                // TODO: Rewrite
                bool ignored      = false;
                var  hitResult    = PhysicalDamageEvent.Result.Normal;
                var  ignoreReason = IgnoredPhysicalDamageEvent.Reason.Absorbed;
                switch (item.Result)
                {
                case Result.Normal:
                    hitResult = PhysicalDamageEvent.Result.Normal;
                    break;

                case Result.Critical:
                    hitResult = PhysicalDamageEvent.Result.Critical;
                    break;

                case Result.Glance:
                    hitResult = PhysicalDamageEvent.Result.Glance;
                    break;

                case Result.Block:
                    ignored      = true;
                    ignoreReason = IgnoredPhysicalDamageEvent.Reason.Blocked;
                    break;

                case Result.Evade:
                    ignored      = true;
                    ignoreReason = IgnoredPhysicalDamageEvent.Reason.Evaded;
                    break;

                case Result.Interrupt:
                    hitResult = PhysicalDamageEvent.Result.Interrupt;
                    break;

                case Result.Absorb:
                    ignored      = true;
                    ignoreReason = IgnoredPhysicalDamageEvent.Reason.Absorbed;
                    break;

                case Result.Blind:
                    ignored      = true;
                    ignoreReason = IgnoredPhysicalDamageEvent.Reason.Missed;
                    break;

                case Result.KillingBlow:
                    hitResult = PhysicalDamageEvent.Result.KillingBlow;
                    break;

                case Result.Downed:
                    hitResult = PhysicalDamageEvent.Result.DowningBlow;
                    break;

                case Result.Unknown:
                    return(new UnknownEvent(item.Time, item));

                default:
                    return(new UnknownEvent(item.Time, item));
                }

                if (!ignored)
                {
                    return(new PhysicalDamageEvent(item.Time, attacker, defender, skill, damage, isMoving,
                                                   isNinety, isFlanking, shieldDamage, hitResult));
                }
                else
                {
                    return(new IgnoredPhysicalDamageEvent(item.Time, attacker, defender, skill, damage,
                                                          isMoving, isNinety, isFlanking, shieldDamage, ignoreReason));
                }
            }

            return(new UnknownEvent(item.Time, item));
        }