Ejemplo n.º 1
0
        public static AttackType ReduceMultiStrike(this AttackType attackType)
        {
            if (!attackType.IsMultiStrike())
            {
                return(AttackType.Undef);
            }

            switch (attackType)
            {
            case AttackType.DoubleThrust:
            case AttackType.TripleThrust:
                return(AttackType.Thrust);

            case AttackType.DoubleSlash:
            case AttackType.TripleSlash:
                return(AttackType.Slash);

            case AttackType.OffhandDoubleThrust:
            case AttackType.OffhandTripleThrust:
                return(AttackType.OffhandThrust);

            case AttackType.OffhandDoubleSlash:
            case AttackType.OffhandTripleSlash:
                return(AttackType.OffhandSlash);

            default:
                return(AttackType.Undef);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Selects a random combat maneuver for a monster's next attack
        /// </summary>
        public MotionCommand?GetCombatManeuver()
        {
            // similar to Player.GetSwingAnimation(), more logging

            if (CombatTable == null)
            {
                log.Error($"{Name} ({Guid}).GetCombatManeuver() - CombatTable is null");
                return(null);
            }

            //ShowCombatTable();

            var motionTable = DatManager.PortalDat.ReadFromDat <DatLoader.FileTypes.MotionTable>(MotionTableId);

            if (motionTable == null)
            {
                log.Error($"{Name} ({Guid}).GetCombatManeuver() - motionTable is null");
                return(null);
            }

            if (!CombatTable.Stances.TryGetValue(CurrentMotionState.Stance, out var stanceManeuvers))
            {
                log.Error($"{Name} ({Guid}).GetCombatManeuver() - couldn't find stance {CurrentMotionState.Stance} in CMT {CombatTableDID:X8}");
                return(null);
            }

            var stanceKey = (uint)CurrentMotionState.Stance << 16 | ((uint)MotionCommand.Ready & 0xFFFFF);

            motionTable.Links.TryGetValue(stanceKey, out var motions);
            if (motions == null)
            {
                log.Error($"{Name} ({Guid}).GetCombatManeuver() - couldn't find stance {CurrentMotionState.Stance} in MotionTable {MotionTableId:X8}");
                return(null);
            }

            // choose a random attack height?
            // apparently this might have been based on monster Z vs. player Z?

            // 28659 - Uber Penguin (CMT 30000040) doesn't have High attack height
            // do a more thorough investigation for this...
            var startHeight = stanceManeuvers.Table.Count == 3 ? 1 : 2;

            AttackHeight = (AttackHeight)ThreadSafeRandom.Next(startHeight, 3);

            if (!stanceManeuvers.Table.TryGetValue(AttackHeight.Value, out var attackTypes))
            {
                log.Error($"{Name} ({Guid}).GetCombatManeuver() - couldn't find attack height {AttackHeight} for stance {CurrentMotionState.Stance} in CMT {CombatTableDID:X8}");
                return(null);
            }

            if (IsDualWieldAttack)
            {
                DualWieldAlternate = !DualWieldAlternate;
            }

            var offhand = IsDualWieldAttack && !DualWieldAlternate;

            var weapon = GetEquippedMeleeWeapon();

            // monsters supposedly always used 0.5 PowerLevel according to anon docs,
            // which translates into a 1.0 PowerMod

            if (weapon != null)
            {
                AttackType = weapon.GetAttackType(CurrentMotionState.Stance, 0.5f, offhand);
            }
            else
            {
                if (AttackHeight != ACE.Entity.Enum.AttackHeight.Low)
                {
                    AttackType = AttackType.Punch;
                }
                else
                {
                    AttackType = AttackType.Kick;
                }
            }

            if (!attackTypes.Table.TryGetValue(AttackType, out var maneuvers) || maneuvers.Count == 0)
            {
                if (AttackType == AttackType.Kick)
                {
                    AttackType = AttackType.Punch;

                    if (!attackTypes.Table.TryGetValue(AttackType, out maneuvers) || maneuvers.Count == 0)
                    {
                        log.Error($"{Name} ({Guid}).GetCombatManeuver() - couldn't find attack type Kick or Punch for attack height {AttackHeight} and stance {CurrentMotionState.Stance} in CMT {CombatTableDID:X8}");
                        return(null);
                    }
                }
                else if (AttackType.IsMultiStrike())
                {
                    var reduced = AttackType.ReduceMultiStrike();

                    if (!attackTypes.Table.TryGetValue(reduced, out maneuvers) || maneuvers.Count == 0)
                    {
                        log.Error($"{Name} ({Guid}).GetCombatManeuver() - couldn't find attack type {reduced} for attack height {AttackHeight} and stance {CurrentMotionState.Stance} in CMT {CombatTableDID:X8}");
                        return(null);
                    }
                    //else
                    //log.Info($"{Name} ({Guid}).GetCombatManeuver() - successfully reduced attack type {AttackType} to {reduced} for attack height {AttackHeight} and stance {CurrentMotionState.Stance} in CMT {CombatTableDID:X8}");
                }
                else
                {
                    log.Error($"{Name} ({Guid}).GetCombatManeuver() - couldn't find attack type {AttackType} for attack height {AttackHeight} and stance {CurrentMotionState.Stance} in CMT {CombatTableDID:X8}");
                    return(null);
                }
            }

            var motionCommand = maneuvers[0];

            if (maneuvers.Count > 1)
            {
                // only used for special attacks?

                // note that with rolling for AttackHeight first,
                // for a CMT with high, med, med-special, and low
                // the chance of rolling the special attack is reduced from 1/4 to 1/6 -- investigate

                var rng = ThreadSafeRandom.Next(0, maneuvers.Count - 1);
                motionCommand = maneuvers[rng];
            }

            // ensure this motionCommand exists in monster's motion table
            if (!motions.ContainsKey((uint)motionCommand))
            {
                // for some reason, the combat maneuvers table can return stance motions that don't exist in the motion table
                // ie. skeletons (combat maneuvers table 0x30000000, motion table 0x09000025)
                // for sword combat, they have double and triple strikes (dagger / two-handed only?)
                if (motionCommand.IsMultiStrike())
                {
                    var singleStrike = motionCommand.ReduceMultiStrike();

                    if (motions.ContainsKey((uint)singleStrike))
                    {
                        //log.Info($"{Name} ({Guid}).GetCombatManeuver() - successfully reduced {motionCommand} to {singleStrike}");
                        return(singleStrike);
                    }
                }
                else if (motionCommand.IsSubsequent())
                {
                    var firstCommand = motionCommand.ReduceSubsequent();

                    if (motions.ContainsKey((uint)firstCommand))
                    {
                        //log.Info($"{Name} ({Guid}).GetCombatManeuver() - successfully reduced {motionCommand} to {firstCommand}");
                        return(firstCommand);
                    }
                }
                log.Error($"{Name} ({Guid}).GetCombatManeuver() - couldn't find {motionCommand} in MotionTable {MotionTableId:X8}");
                return(null);
            }

            //Console.WriteLine(motionCommand);

            return(motionCommand);
        }