Exemple #1
0
        internal static List <Messages.Combat> ApplyStance(EcsRegistrar rgs, long agentId, string stance)
        {
            var result = new Messages.Combat {
                Tick = rgs.NewId(), ActorId = agentId
            };
            var stances = rgs.GetParts <Parts.SkillsModifier>(agentId).Where(p => Stances.Contains(p.Tag));

            rgs.RemoveParts(agentId, stances);

            result.ActorAction = stance;
            if (stance == Vals.CombatAction.StanceAggressive)
            {
                rgs.AddPart(agentId, new Parts.SkillsModifier {
                    Tag         = Vals.CombatAction.StanceAggressive,
                    SkillDeltas = new Dictionary <string, int> {
                        [Vals.EntitySkillPhysical.Melee] = 1,
                        [Vals.EntitySkillPhysical.Dodge] = -1
                    }
                });
            }
            else if (stance == Vals.CombatAction.StanceDefensive)
            {
                rgs.AddPart(agentId, new Parts.SkillsModifier
                {
                    Tag         = Vals.CombatAction.StanceDefensive,
                    SkillDeltas = new Dictionary <string, int>
                    {
                        [Vals.EntitySkillPhysical.Melee] = -1,
                        [Vals.EntitySkillPhysical.Dodge] = 1
                    }
                });
            }
            else if (stance == Vals.CombatAction.StanceStandGround)
            {
                //default stance, so the removal of previous ones means we're done.
            }
            else
            {
                throw new ArgumentException($"Invalid stance {stance}.");
            }

            return(new List <Messages.Combat> {
                result
            });
        }
Exemple #2
0
        // do we want to pass in the random function for better testing? not sure, but let's try it.
        public static List <Messages.Combat> ResolveSingleTargetMelee(EcsRegistrar rgs, long attackerId, long weaponId, long targetId, Func <int> randoRange7)
        {
            var attacker = Bundle.GetAgentBundle(rgs, attackerId);

            if (weaponId == 0)
            {
                return(new List <Messages.Combat> {
                    new Messages.Combat {
                        Tick = rgs.NewId(), ActorId = attackerId, ActorAction = $"{attacker.EntityName.ProperName} starts to attack, but merely spins around in place, because its weaponId is 0. SHOULD NOT EVER HAPPEN."
                    }
                });
            }

            var weapon = Bundle.GetWeaponBundle(rgs, weaponId);

            //this is only temporary, later we'll have a clock/scheduler.
            var msg = new Messages.Combat
            {
                Tick         = rgs.NewId(),
                ActorId      = attackerId,
                TargetId     = targetId,
                ActorAction  = Vals.CombatAction.AttackWeaponMelee,
                TargetAction = Vals.CombatAction.Dodge,
                AttackVerb   = weapon.PhysicalObject.MeleeVerb
            };

            var attackerAdjustedSkills = Skills.GetAdjustedSkills(rgs, attackerId);
            var targetAdjustedSkills   = Skills.GetAdjustedSkills(rgs, targetId);

            int     attackRoll           = randoRange7();
            decimal attackCritMultiplier = GetDamageMultiplierFromRange7(attackRoll);
            int     attackerMeleeSkill   = attackerAdjustedSkills[Vals.EntitySkillPhysical.Melee];
            int     attackerAdjustedRoll = attackRoll + attackerMeleeSkill;

            msg.ActorCritical      = attackCritMultiplier.ToString();
            msg.ActorAdjustedSkill = attackerMeleeSkill;
            msg.ActorAdjustedRoll  = attackerAdjustedRoll;

            int targetDodgeRoll  = randoRange7();
            int targetDodgeSkill = targetAdjustedSkills[Vals.EntitySkillPhysical.Dodge];
            //dodge is a difficult skill and always reduced by 1.
            int targetAdjustedDodgeRoll = Math.Max(0, targetDodgeRoll + targetDodgeSkill - 1);

            msg.TargetAdjustedSkill = targetDodgeSkill;
            msg.TargetAdjustedRoll  = targetAdjustedDodgeRoll;

            int netAttack = Math.Max(0, attackerAdjustedRoll - targetAdjustedDodgeRoll);

            msg.NetActorRoll = netAttack;

            //a good attack gets whatever the attack crit damage multiplier is, a barely-attack gets a .5, and less gets a 0.
            decimal finalAttackMultiplier = (netAttack > 1) ? attackCritMultiplier
                : (netAttack == 1) ? 0.5m
                : 0m;

            var target = Bundle.GetAgentBundle(rgs, targetId);
            //heh, that distinct is important, since the same armor can now occupy multiple slots.
            var targetEquipmentIds = target.Anatomy.SlotsEquipped.Where(s => s.Value != 0).Select(s => s.Value).Distinct().ToList();

            // ReSharper disable once ConvertClosureToMethodGroup
            var targetDamagePreventers = targetEquipmentIds.SelectMany(eid => rgs.GetParts <Parts.DamagePreventer>(eid)).ToList();

            var damageAttempted = weapon.Damagers.Sum(d => d.DamageAmount) * finalAttackMultiplier;
            var damagePrevented = target.PhysicalObject.DefaultDamageThreshold + targetDamagePreventers.Sum(p => p.DamageThreshold);

            msg.AttemptedDamage = damageAttempted;

            //so we apply crit/attack multipliers first, then we subtract damage prevention, then we apply default damage multiplier and armor multipliers. whew!
            var damageDealt = Math.Max(0,
                                       (damageAttempted - damagePrevented)
                                       * target.PhysicalObject.DefaultDamageMultiplier
                                       * (targetDamagePreventers.Select(p => p.DamageMultiplier).Aggregate(1m, (p1, p2) => p1 * p2)));

            msg.NetDamage = damageDealt;

            target.PhysicalObject.Condition = target.PhysicalObject.Condition - (int)damageDealt;
            msg.TargetCondition             = target.PhysicalObject.Condition;

            if (target.PhysicalObject.Condition <= 0)
            {
                if (!target.Agent.CombatStatusTags.Contains(Vals.CombatStatusTag.Dead))
                {
                    target.Agent.CombatStatusTags.Add(Vals.CombatStatusTag.Dead);
                    msg.NewTargetCombatTags.Add(Vals.CombatStatusTag.Dead);
                }
            }

            return(new List <Messages.Combat> {
                msg
            });
        }