Beispiel #1
0
        public void OnEventAboutToTrigger(RuleCalculateAttackBonusWithoutTarget evt)
        {
            if (evt.Weapon == null)
            {
                return;
            }

            if (!canBeUsedOn(evt.Weapon))
            {
                return;
            }

            var shield  = (evt.Weapon?.HoldingSlot as HandSlot)?.PairSlot.MaybeShield;
            int penalty = Rulebook.Trigger <RuleCalculateArmorCheckPenalty>(new RuleCalculateArmorCheckPenalty(evt.Initiator, shield.ArmorComponent)).Penalty;


            evt.AddBonus(penalty, this.Fact);
        }
        public void OnNewRound()
        {
            if (!this.Owner.Ensure <UnitPartBleed>().isCurrentBleedBuff(this.Fact))
            {
                return;
            }

            var dices = new DiceFormula(this.dice_value.DiceCountValue.Calculate(this.Context), this.dice_value.DiceType);
            var bonus = this.dice_value.BonusValue.Calculate(this.Context) + this.Owner.Ensure <UnitPartBleed>().bleed_bonus;

            var base_dmg = (BaseDamage) new DirectDamage(dices, bonus);

            base_dmg.IgnoreReduction = true;

            RuleDealDamage evt_dmg = new RuleDealDamage(this.Context.MaybeCaster, this.Owner.Unit, new DamageBundle(new BaseDamage[] { base_dmg }));

            evt_dmg.SourceAbility = Context?.SourceAbility;
            evt_dmg.Reason        = this.Fact;
            Rulebook.Trigger <RuleDealDamage>(evt_dmg);
        }
Beispiel #3
0
        public static void RestSelected()
        {
            foreach (var selectedUnit in Game.Instance.UI.SelectionManager.SelectedUnits)
            {
                if (selectedUnit.Descriptor.State.IsFinallyDead)
                {
                    selectedUnit.Descriptor.Resurrect();
                    selectedUnit.Position = Game.Instance.Player.MainCharacter.Value.Position;
                }

                RestController.RemoveNegativeEffects(selectedUnit.Descriptor);
                RestController.ApplyRest(selectedUnit.Descriptor);
                Rulebook.Trigger(new RuleHealDamage(selectedUnit, selectedUnit, default(DiceFormula), selectedUnit.Descriptor.Stats.HitPoints.ModifiedValue));
                foreach (var attribute in selectedUnit.Stats.Attributes)
                {
                    attribute.Damage = 0;
                    attribute.Drain  = 0;
                }
            }
        }
        static void Postfix(ItemEntityArmor __instance)
        {
            if (__instance?.Wielder == null || __instance?.Owner == null || __instance?.Blueprint == null)
            {
                return;
            }

            if (__instance.Owner.Proficiencies.Contains(__instance.Blueprint.ProficiencyGroup))
            {
                return;
            }

            var tr      = Harmony12.Traverse.Create(__instance);
            int penalty = Rulebook.Trigger <RuleCalculateArmorCheckPenalty>(new RuleCalculateArmorCheckPenalty(__instance.Wielder.Unit, __instance)).Penalty;

            if (penalty < 0)
            {
                tr.Method("AddModifier", (ModifiableValue)__instance.Wielder.Stats.AdditionalAttackBonus, penalty).GetValue();
            }
        }
Beispiel #5
0
        static void Postfix(ItemEntityArmor __instance)
        {
            if (__instance?.Wielder == null || __instance?.Owner == null || __instance?.Blueprint == null)
            {
                return;
            }

            if (__instance.Owner.Proficiencies.Contains(__instance.Blueprint.ProficiencyGroup) ||
                !Game.Instance.Player.PartyCharacters.Any(c => c.Value == __instance?.Wielder?.Unit))    //a lot of units in the  game do not have required proficiencies, so we will consider only party members
            {
                return;
            }

            var tr      = Harmony12.Traverse.Create(__instance);
            int penalty = Rulebook.Trigger <RuleCalculateArmorCheckPenalty>(new RuleCalculateArmorCheckPenalty(__instance.Wielder.Unit, __instance)).Penalty;

            if (penalty < 0)
            {
                tr.Method("AddModifier", (ModifiableValue)__instance.Wielder.Stats.AdditionalAttackBonus, penalty).GetValue();
            }
        }
        static bool Prefix(FlankedAttackBonus __instance, RuleCalculateAttackBonus evt)
        {
            bool isFlatFooted = Rulebook.Trigger <RuleCheckTargetFlatFooted>(new RuleCheckTargetFlatFooted(evt.Initiator, evt.Target)).IsFlatFooted;

            bool is_flanked = false;

            foreach (var u in evt.Target.CombatState.EngagedBy)
            {
                is_flanked = evt.Target.isFlankedByAttacker(u);
                if (is_flanked)
                {
                    break;
                }
            }

            if (is_flanked || isFlatFooted)
            {
                evt.AddBonus(__instance.AttackBonus * __instance.Fact.GetRank(), __instance.Fact);
            }

            return(false);
        }
Beispiel #7
0
        public override void OnEventDidTrigger(RuleAttackWithWeapon evt)
        {
            if (evt.Weapon == null)
            {
                return;
            }

            if (use_swift_action && evt.Initiator.CombatState.Cooldown.SwiftAction > 0.0f)
            {
                return;
            }


            if (!categories.Empty() && !categories.Contains(evt.Weapon.Blueprint.Category))
            {
                return;
            }

            if (!evt.AttackRoll.IsHit)
            {
                return;
            }

            if (this.maneuver == CombatManeuver.Trip && (evt.Target.Descriptor.State.Prone.Active || (bool)(evt.Target.View) && evt.Target.View.IsGetUp))
            {
                return;
            }

            var rule = new RuleCombatManeuver(this.Owner.Unit, evt.Target, this.maneuver);

            rule.AddBonus(this.bonus.Calculate(this.Fact.MaybeContext), this.Fact);
            Rulebook.Trigger <RuleCombatManeuver>(new RuleCombatManeuver(this.Owner.Unit, evt.Target, this.maneuver));

            if (use_swift_action)
            {
                evt.Initiator.CombatState.Cooldown.SwiftAction = 6.0f;
            }
        }
Beispiel #8
0
        private static bool tryBreakFree(UnitEntityData target, UnitEntityData grappler, MechanicsContext context)
        {
            int dc = 0;

            if (context != null)
            {
                dc = context.Params.DC;
            }
            RuleCalculateCMB rule_calcualte_cmb1 = new RuleCalculateCMB(target, grappler, CombatManeuver.Grapple);
            RuleCalculateCMB rule_calcualte_cmb2 = context?.TriggerRule <RuleCalculateCMB>(rule_calcualte_cmb1) ?? Rulebook.Trigger <RuleCalculateCMB>(rule_calcualte_cmb1);

            if (Math.Max((int)(target.Stats.SkillMobility), (int)(target.Stats.SkillAthletics)) < rule_calcualte_cmb2.Result)
            {
                RuleSkillCheck rule_skill_check = new RuleSkillCheck(target, StatType.AdditionalCMB, dc);
                rule_skill_check.Bonus.AddModifier(rule_calcualte_cmb2.Result - target.Stats.AdditionalCMB.ModifiedValue, null, ModifierDescriptor.UntypedStackable);
                return((context?.TriggerRule <RuleSkillCheck>(rule_skill_check) ?? Rulebook.Trigger <RuleSkillCheck>(rule_skill_check)).IsPassed);
            }
            else
            {
                StatType       stat             = (int)(target.Stats.SkillMobility) > (int)(target.Stats.SkillAthletics) ? StatType.SkillMobility : StatType.SkillAthletics;
                RuleSkillCheck rule_skill_check = new RuleSkillCheck(target, stat, dc);
                return((context?.TriggerRule <RuleSkillCheck>(rule_skill_check) ?? Rulebook.Trigger <RuleSkillCheck>(rule_skill_check)).IsPassed);
            }
        }
Beispiel #9
0
        public override float Score(DecisionContext context)
        {
            float chance_to_hit_weight = Main.settings.ai_chance_to_hit_weight;
            float engaged_by_weight    = Main.settings.ai_engaged_by_target_weight;
            float distance_weight      = Main.settings.ai_distance_to_target_weight;

            UnitEntityData attacker = context.Unit;
            UnitEntityData target   = context.Target.Unit ?? context.Unit;

            if (attacker == null || target == null || !target.IsEnemy(attacker) || attacker.CombatState.AIData.UnreachableUnits.Contains(target) || attacker.IsPlayerFaction)
            {
                return(min_score);
            }
            var weapon = attacker.Body?.PrimaryHand?.MaybeWeapon;

            if (!attacker.Body.HandsAreEnabled && attacker.Body.AdditionalLimbs.Count > 0)
            {
                weapon = attacker.Body.AdditionalLimbs[0].MaybeWeapon;
            }
            int odds = 0;

            if (weapon != null)
            {
                var attack_bonus = Rulebook.Trigger(new RuleCalculateAttackBonus(attacker, target, weapon, 0)).Result;
                var target_ac    = Rulebook.Trigger(new RuleCalculateAC(attacker, target, weapon.Blueprint.AttackType)).TargetAC;
                odds = attack_bonus - target_ac + 10;  // 0 means 50% chance to hit more is better
            }

            int certainty = 0;
            var unit_part_attack_scores = attacker.Ensure <UnitPartAttackScoreStorage>();

            if (unit_part_attack_scores.attack_scores.ContainsKey(target))
            {
                certainty = unit_part_attack_scores.attack_scores[target];
            }
            else
            {
                var check_bonus = attacker.Stats.Intelligence.Bonus + attacker.Stats.Wisdom.Bonus + attacker.Descriptor.Progression.CharacterLevel;
                var dc          = 10 + (target.Stats.CheckBluff.ModifiedValue + target.Stats.SkillStealth.ModifiedValue) / 2 + attacker.Descriptor.Progression.CharacterLevel;
                certainty = rng.Next(1, 20) + check_bonus - dc;
                certainty = Math.Max(Math.Min(10, certainty), -10); // from -10 to 10

                unit_part_attack_scores.attack_scores[target] = certainty;
            }

            odds = Math.Min(Math.Max(odds, -10), 10);                 //from -10 to 10
            float certainty_coeff = (1.0f + certainty * 0.1f) * 0.5f; //from 0 to 1
            float odds_coeff      = (1.0f + odds * 0.1f) * 0.5f;      //1 for auto hit, 0 for auto miss

            float score = 1.0f - chance_to_hit_weight * (0.5f + (0.5f - odds_coeff) * certainty_coeff);

            var distance       = Math.Max((target.Position - attacker.Position).magnitude, 5.Feet().Meters);
            var distance_coeff = 5.Feet().Meters / distance; //from 1 to 0;

            if ((weapon != null && weapon.Blueprint.IsRanged) ||
                (context.Ability?.Blueprint != null && context.Ability.Blueprint.Range >= AbilityRange.Close &&
                 context.Ability.Blueprint.Range <= AbilityRange.Unlimited)
                )
            {
                distance_coeff = 1.0f;
            }

            score *= (1.0f - distance_weight * (1.0f - distance_coeff)); //will more likely attack closer units if does not have ranged weapon

            var tumble_toggle = attacker.ActivatableAbilities.Enumerable.Where(a => a.Blueprint == tumble).FirstOrDefault();

            if (tumble_toggle != null && !attacker.IsPlayerFaction)
            {
                tumble_toggle.IsOn = attacker.CombatState.IsEngaged && attacker.Stats.GetStat(Kingmaker.EntitySystem.Stats.StatType.SkillMobility).ModifiedValue >= 10;
            }

            var engaged_score = 1.0f - engaged_by_weight;

            if (attacker.CombatState.EngagedBy.Contains(target))
            {
                engaged_score = 1.0f;
            }

            score *= engaged_score;

            return(Math.Max(Math.Min(score, max_score), min_score));
        }
 /*
  * Pause Kingdom works by keeping current day constant, and increasing kingdom start day to compensate
  * To manage kingdom while paused, event start and finish days are moved backwards to simulate currentday moving forwards
  * any events that depend on CurrentDay need to be manually triggered
  */
 public static void FixTimeline(int delta)
 {
     if (delta < 1)
     {
         return;
     }
     if (!Main.settings.enablePausedKingdomManagement)
     {
         return;
     }
     foreach (KingdomBuff kingdomBuff in KingdomState.Instance.ActiveBuffs.Enumerable)
     {
         if (kingdomBuff.EndsOnDay > 0 && KingdomState.Instance.CurrentDay >= kingdomBuff.EndsOnDay)
         {
             kingdomBuff.EndsOnDay = (int)Math.Max(1, kingdomBuff.EndsOnDay - delta);
         }
     }
     foreach (RegionState regionState in KingdomState.Instance.Regions)
     {
         SettlementState settlement = regionState.Settlement;
         if (settlement != null)
         {
             foreach (var building in settlement.Buildings)
             {
                 if (building.IsFinished)
                 {
                     continue;
                 }
                 building.FinishedOn = building.FinishedOn - delta;
             }
         }
     }
     foreach (RegionState regionState2 in KingdomState.Instance.Regions)
     {
         foreach (Artisan artisan in regionState2.Artisans)
         {
             if (artisan.HasHelpProject)
             {
                 continue;
             }
             artisan.ProductionStartedOn = artisan.ProductionStartedOn - delta;
             artisan.ProductionEndsOn    = artisan.ProductionEndsOn - delta;
         }
     }
     foreach (KingdomTask task in KingdomState.Instance.ActiveTasks)
     {
         if (!(task is KingdomTaskEvent kte))
         {
             continue;
         }
         if (!kte.IsInProgress)
         {
             continue;
         }
         if (kte.Event.EventBlueprint is BlueprintKingdomProject bkp)
         {
             StartedOnSetter(task, task.StartedOn - delta);
         }
     }
     foreach (var kingdomEvent in KingdomState.Instance.ActiveEvents)
     {
         if (kingdomEvent.IsFinished)
         {
             continue;
         }
         m_StartedOnRef(kingdomEvent) = m_StartedOnRef(kingdomEvent) - delta;
     }
     for (int i = 0; i < delta; i++)
     {
         var totalDays = (int)Game.Instance.TimeController.GameTime.TotalDays;
         if ((totalDays - delta) % 7 == 0)
         {
             KingdomState.Instance.BPPerTurnTotal = Rulebook.Trigger <RuleCalculateBPGain>(new RuleCalculateBPGain()).BPToAdd;
             KingdomState.Instance.BuildPoints   += KingdomState.Instance.BPPerTurnTotal;
             KingdomState.Instance.CurrentTurn++;
             EventBus.RaiseEvent(delegate(IKingdomLogHandler h)
             {
                 h.OnBPGained(KingdomState.Instance.BPPerTurnTotal);
             });
         }
     }
     KingdomState.Instance.LastRavenVisitDay -= delta;
 }
Beispiel #11
0
        static bool Prefix(RulePartySkillCheck __instance, bool isTrigger, ref int ___m_D20, ref int ___m_StatValue, StatType ___m_StatType, int ___m_DifficultyClass)
        {
            ___m_StatValue = int.MinValue;
            var tr = Harmony12.Traverse.Create(__instance);

            tr.Property("Roller").SetValue(null);
            RuleSkillCheck selected_evt = null;
            var            units        = Game.Instance.Player.Party.ToArray().ToList();

            foreach (var u in units.ToArray())
            {
                if (u.Descriptor.Pet != null)
                {
                    units.Add(u.Descriptor.Pet);
                }
            }

            foreach (UnitEntityData unitEntityData in units)
            {
                if (unitEntityData.Descriptor.State.CanAct)
                {
                    ModifiableValue stat = unitEntityData.Stats.GetStat(___m_StatType);
                    ModifiableValueAttributeStat valueAttributeStat = stat as ModifiableValueAttributeStat;
                    int            num = valueAttributeStat != null ? valueAttributeStat.Bonus : stat.ModifiedValue;
                    RuleSkillCheck evt = new RuleSkillCheck(unitEntityData, ___m_StatType, ___m_DifficultyClass)
                    {
                        Voice         = __instance.Voice,
                        EnsureSuccess = __instance.EnsureSuccess
                    };

                    if (isTrigger)
                    {
                        evt.Silent = true;;
                        Rulebook.Trigger <RuleSkillCheck>(evt);
                        num += (int)evt.Bonus;
                    }

                    if (___m_StatValue < num)
                    {
                        ___m_StatValue = num;
                        tr.Property("Roller").SetValue(unitEntityData);
                        selected_evt = evt;
                    }
                }
            }
            if (__instance.Roller == null)
            {
                UberDebug.Log("Roller is null, in the party skillcheck", (object[])Array.Empty <object>());
            }
            else
            {
                if (!isTrigger)
                {
                    selected_evt.Calculate();
                }
                else
                {
                    selected_evt.Silent = false;
                    EventBus.RaiseEvent <IRollSkillCheckHandler>((Action <IRollSkillCheckHandler>)(h => h.HandleOnRuleSkillCheck(selected_evt)));
                    //Game.Instance?.UI?.BattleLogManager?.HandleUnitSkillCheckRolled(selected_evt);
                }
                ___m_D20 = selected_evt.D20;
            }
            return(false);
        }
        public static void runActionOnDamageDealt(RuleDealDamage evt, ActionList action, int min_dmg = 1, bool only_critical = false, SavingThrowType save_type = SavingThrowType.Unknown,
                                                  BlueprintBuff context_buff = null, DamageEnergyType energy_descriptor      = DamageEnergyType.Acid, bool use_damage_energy_type = false)
        {
            if (only_critical && (evt.AttackRoll == null || !evt.AttackRoll.IsCriticalConfirmed))
            {
                return;
            }


            var target = evt.Target;

            if (target == null)
            {
                return;
            }

            if (evt.Damage <= min_dmg)
            {
                return;
            }

            if (use_damage_energy_type)
            {
                bool damage_found = false;
                foreach (var dmg in evt.DamageBundle)
                {
                    var energy_damage = (dmg as EnergyDamage);

                    if (energy_damage != null && energy_damage.EnergyType == energy_descriptor)
                    {
                        damage_found = true;
                        break;
                    }
                }

                if (!damage_found)
                {
                    return;
                }
            }

            if (save_type != SavingThrowType.Unknown)
            {
                var context_params = evt.Initiator.Buffs?.GetBuff(context_buff)?.MaybeContext?.Params;
                if (context_params == null)
                {
                    return;
                }

                var dc = context_params.DC;
                var rule_saving_throw = new RuleSavingThrow(target, save_type, dc);
                Rulebook.Trigger(rule_saving_throw);

                if (rule_saving_throw.IsPassed)
                {
                    return;
                }
            }

            var context_fact = evt.Initiator.Buffs?.GetBuff(context_buff);

            (context_fact as IFactContextOwner)?.RunActionInContext(action, target);
        }
        static bool Prefix(UnitCombatState __instance, UnitEntityData target, ref bool __result)
        {
            __result = false;
            if (__instance.PreventAttacksOfOpporunityNextFrame || target.CombatState.PreventAttacksOfOpporunityNextFrame || !__instance.CanActInCombat && !__instance.Unit.Descriptor.State.HasCondition(UnitCondition.AttackOfOpportunityBeforeInitiative) || (!__instance.CanAttackOfOpportunity || !__instance.Unit.Descriptor.State.CanAct))
            {
                return(false);
            }
            UnitPartForceMove unitPartForceMove = target.Get <UnitPartForceMove>();

            if (unitPartForceMove && !unitPartForceMove.ProvokeAttackOfOpportunity || (UnitCommand.CommandTargetUntargetable(__instance.Unit, target, null) || __instance.Unit.HasMotionThisTick) || (__instance.Unit.GetThreatHand() == null || __instance.AttackOfOpportunityCount <= 0 || (!target.Memory.Contains(__instance.Unit) || target.Descriptor.State.HasCondition(UnitCondition.ImmuneToAttackOfOpportunity))))
            {
                return(false);
            }
            if (target.Descriptor.State.HasCondition(UnitCondition.UseMobilityToNegateAttackOfOpportunity))
            {
                RuleCalculateCMD ruleCalculateCmd = Rulebook.Trigger(new RuleCalculateCMD(target, __instance.Unit, CombatManeuver.None));
                if (Rulebook.Trigger(new RuleSkillCheck(target, StatType.SkillMobility, ruleCalculateCmd.Result)).IsPassed)
                {
                    return(false);
                }
            }

            // Changed code: instantly trigger AoO check (from UnitAttackOfOpportunity.OnAction)
            //  === Original Start ===
            //  __instance.Unit.Commands.Run((UnitCommand) new UnitAttackOfOpportunity(target));
            //  EventBus.RaiseEvent<IAttackOfOpportunityHandler>((Action<IAttackOfOpportunityHandler>)(h => h.HandleAttackOfOpportunity(__instance.Unit, target)));
            //  === Original End   ===
            //  === Changed Start  ===

            RuleAttackWithWeapon aoo = new RuleAttackWithWeapon(__instance.Unit, target, __instance.Unit.GetThreatHand().Weapon, 0)
            {
                IsAttackOfOpportunity = true
            };
            var combatManeuver = Rulebook.Trigger(new RuleCheckCombatManeuverReplaceAttack(__instance.Unit, target, __instance.Unit.GetThreatHand().Weapon.Blueprint)).Result;

            if (!target.Descriptor.State.IsDead)
            {
                EventBus.RaiseEvent <IAttackOfOpportunityHandler>(h => h.HandleAttackOfOpportunity(__instance.Unit, target));
                if (combatManeuver != CombatManeuver.None)
                {
                    __instance.Unit.TriggerAttackReplacementCombatManeuver(target, __instance.Unit.GetThreatHand().Weapon, 0, combatManeuver);
                }
                else
                {
                    Rulebook.Trigger(aoo);
                }
            }
            // === Changed End    ===

            if (__instance.AttackOfOpportunityCount == __instance.AttackOfOpportunityPerRound)
            {
                __instance.Cooldown.AttackOfOpportunity = 5.4f;
            }
            --__instance.AttackOfOpportunityCount;

            // ===  Added start  === (from UnitAttack.TriggerAttackRule)
            if (combatManeuver == CombatManeuver.None && target.View != null && target.View.HitFxManager != null)
            {
                target.View.HitFxManager.HandleAttackHit(aoo);
            }
            // ===  Added end    ===

            __result = true;
            return(false);
        }
Beispiel #14
0
        static void RunTest(string name, UnitEntityData initiator, UnitEntityData target)
        {
            var weapon        = initiator.GetFirstWeapon();
            var rolls         = new ushort[NumberOfTests];
            var resultBuckets = new uint[Enum.GetNames(typeof(AttackResult)).Length];
            var weaponStats   = new RuleCalculateWeaponStats(initiator, weapon, null);

            Rulebook.Trigger(weaponStats);
            using (var sw = new StreamWriter($"{ModEntry.Path}/{name}_rolls.txt"))
            {
                for (int i = 0; i < NumberOfTests; i++)
                {
                    var rule = new RuleAttackRoll(initiator, target, weaponStats, 0);
                    rule.SuspendCombatLog = true;
                    var roll = Rulebook.Trigger(rule);
                    if (roll.Roll.Value > 20 || roll.Roll.Value < 1)
                    {
                        Error("Roll out of range");
                        return;
                    }
                    rolls[i] = (ushort)roll.Roll.Value;
                    resultBuckets[(int)roll.Result] += 1;
                    sw.WriteLine("Roll: {0} Result: {1}", roll.Roll.Value, roll.Result);
                }
            }
            var   buckets    = new ulong[20];
            ulong sum        = 0;
            var   max1Seq    = new SequenceCounter(SequenceType.LessThen, 2);
            var   max20Seq   = new SequenceCounter(SequenceType.GreaterThen, 19);
            var   maxHighSeq = new SequenceCounter(SequenceType.GreaterThen, 13);
            var   maxLowSeq  = new SequenceCounter(SequenceType.LessThen, 8);

            foreach (var roll in rolls)
            {
                buckets[roll - 1] += 1;
                var prevSum = sum;
                sum += roll;
                if (sum < prevSum)
                {
                    Error("Overflow while calculating sum");
                    break;
                }
                max1Seq.Add(roll);
                max20Seq.Add(roll);
                maxHighSeq.Add(roll);
                maxLowSeq.Add(roll);
            }
            var maxBucket        = buckets.Max();
            var minBucket        = buckets.Min();
            var bucketDifference = maxBucket - minBucket;
            var average          = sum / (double)NumberOfTests;

            using (var sw = new StreamWriter($"{ModEntry.Path}/{name}_summary.txt"))
            {
                sw.WriteLine("Initiator: {0}", initiator.CharacterName);
                sw.WriteLine("Target: {0}", target.CharacterName);
                sw.WriteLine("Weapon: {0}", weapon.Name);
                sw.WriteLine("Number of rolls: {0}", NumberOfTests);
                sw.WriteLine("Sum: {0}", sum);
                sw.WriteLine("Average: {0}", average);
                for (int i = 0; i < 20; i++)
                {
                    sw.WriteLine("Number of {0}: {1}", i + 1, buckets[i]);
                }
                sw.WriteLine("Highest count in set {0}", maxBucket);
                sw.WriteLine("Lowest count in set {0}", minBucket);
                sw.WriteLine("Difference {0}", bucketDifference);
                sw.WriteLine("Max 1 in a row: {0}", max1Seq.MaxLength);
                sw.WriteLine("Max 20 in a row: {0}", max20Seq.MaxLength);
                sw.WriteLine("Max > 13 in a row: {0}", maxHighSeq.MaxLength);
                sw.WriteLine("Max < 8 in a row: {0}", maxLowSeq.MaxLength);
                var resultNames = Enum.GetNames(typeof(AttackResult));
                for (int i = 0; i < resultNames.Length; i++)
                {
                    sw.WriteLine("{0} count: {1} ({2}%)", resultNames[i], resultBuckets[i], resultBuckets[i] / (float)NumberOfTests * 100f);
                }

                var rule = new RuleAttackRoll(initiator, target, weaponStats, 0);
                rule.SuspendCombatLog = true;
                var roll = Rulebook.Trigger(rule);
                sw.WriteLine("AttackBonus: {0}", roll.AttackBonus);
                sw.WriteLine("IsTargetFlatFooted: {0}", roll.IsTargetFlatFooted);
                sw.WriteLine("TargetAC: {0}", roll.TargetAC);
                sw.WriteLine("IsSneakAttack: {0}", roll.IsSneakAttack);
                sw.WriteLine("Target.IsFlanked: {0}", roll.Target.CombatState.IsFlanked);
                sw.WriteLine("Weapon.CriticalEdge: {0}", roll.WeaponStats.CriticalEdge);
                sw.WriteLine("ImmuneToCriticalHit: {0}", roll.ImmuneToCriticalHit);
                sw.WriteLine("ImmuneToSneakAttack: {0}", roll.ImmuneToSneakAttack);
                sw.WriteLine("TargetUseFortification: {0}", roll.TargetUseFortification);
            }
            var imageSize = (int)Math.Sqrt(NumberOfTests);

            if (imageSize > 2800)
            {
                imageSize = 2800;
            }
            if (imageSize > 0 && imageSize <= 2800)
            {
                var texture    = new Texture2D(imageSize, imageSize);
                int pixelIndex = 0;
                for (int y = 0; y < texture.height; y++)
                {
                    for (int x = 0; x < texture.width; x++)
                    {
                        texture.SetPixel(x, y, rolls[pixelIndex++] > 10 ? Color.white : Color.black);
                    }
                }
                var data = texture.EncodeToPNG();
                File.WriteAllBytes($"{ModEntry.Path}/{name}_image.png", data);
            }
        }
        public bool mod_SpendCharges(UnitDescriptor user)
        {
            if (!KingmakerPatchSettings.Cheats.InfiniteItemUse)
            {
                return(this.source_SpendCharges(user));
            }

            var blueprintItemEquipment = this.Blueprint as BlueprintItemEquipment;

            if (!blueprintItemEquipment || !blueprintItemEquipment.GainAbility)
            {
                UberDebug.LogError(this.Blueprint, $"Item {this.Blueprint} doesn't gain ability");
                return(false);
            }

            if (!this.IsSpendCharges)
            {
                return(true);
            }

            var hasCharges = false;

            if (this.Charges > 0)
            {
                var itemEntityUsable = new ItemEntityUsable((BlueprintItemEquipmentUsable)this.Blueprint);

                if (user.State.Features.HandOfMagusDan && itemEntityUsable.Blueprint.Type == UsableItemType.Scroll)
                {
                    var ruleRollDice = new RuleRollDice(user.Unit, new DiceFormula(1, DiceType.D100));

                    Rulebook.Trigger(ruleRollDice);

                    if (ruleRollDice.Result <= 25)
                    {
                        return(true);
                    }
                }

                if (user.IsPlayerFaction)
                {
                    return(true);
                }

                --this.Charges;
            }
            else
            {
                hasCharges = true;
                UberDebug.LogError("Has no charges");
            }

            if (this.Charges >= 1 || blueprintItemEquipment.RestoreChargesOnRest)
            {
                return(!hasCharges);
            }

            if (this.Count > 1)
            {
                this.DecrementCount(1);
                this.Charges = 1;
            }
            else
            {
                ItemsCollection collection = this.Collection;
                collection?.Remove(this);
            }

            return(!hasCharges);
        }
 internal static bool IsFlatFootedTo(this UnitEntityData target, UnitEntityData attacker)
 {
     return(Rulebook.Trigger(new RuleCheckTargetFlatFooted(attacker, target)).IsFlatFooted);
 }
            static bool Prefix(UnitConfusionController __instance, UnitEntityData unit)
            {
                var allowed_states = new ConfusionState[0];

                if (unit.Descriptor.State.HasCondition(UnitCondition.AttackNearest))
                {
                    allowed_states = new ConfusionState[] { ConfusionState.AttackNearest };
                }
                else
                {
                    allowed_states = unit.Ensure <UnitPartConfusionControl>().allowedConfusionStates();
                }
                if (unit.Descriptor.State.HasCondition(UnitCondition.Confusion) || unit.Descriptor.State.HasCondition(UnitCondition.AttackNearest))
                {
                    var tr = Harmony12.Traverse.Create <UnitConfusionController>();
                    UnitPartConfusion part = unit.Ensure <UnitPartConfusion>();
                    bool flag = !unit.CombatState.HasCooldownForCommand(UnitCommand.CommandType.Standard);
                    if (Game.Instance.TimeController.GameTime - part.RoundStartTime > tr.Field("RoundDuration").GetValue <TimeSpan>() && flag)
                    {
                        do
                        {
                            RuleRollDice ruleRollDice = Rulebook.Trigger <RuleRollDice>(new RuleRollDice(unit, new DiceFormula(1, DiceType.D100)));
                            int          num          = ruleRollDice.Result;
                            part.State = num >= 26 ? (num >= 51 ? (num >= 76 ? ConfusionState.AttackNearest : ConfusionState.SelfHarm) : ConfusionState.DoNothing) : ConfusionState.ActNormally;
                        } while (!allowed_states.Contains(part.State));
                        if (part.State == ConfusionState.ActNormally)
                        {
                            part.ReleaseControl();
                        }
                        else
                        {
                            part.RetainControl();
                        }
                        part.RoundStartTime = Game.Instance.TimeController.GameTime;
                        part.Cmd?.Interrupt();
                        part.Cmd = (UnitCommand)null;
                    }
                    if (part.Cmd != null || !unit.Descriptor.State.CanAct || part.State == ConfusionState.ActNormally)
                    {
                        return(false);
                    }
                    if (flag)
                    {
                        switch (part.State)
                        {
                        case ConfusionState.DoNothing:
                            part.Cmd = tr.Method("DoNothing", part).GetValue <UnitCommand>();
                            break;

                        case ConfusionState.SelfHarm:
                            part.Cmd = tr.Method("SelfHarm", part).GetValue <UnitCommand>();
                            break;

                        case ConfusionState.AttackNearest:
                            part.Cmd = tr.Method("AttackNearest", part).GetValue <UnitCommand>();
                            break;

                        default:
                            throw new ArgumentOutOfRangeException();
                        }
                    }
                    else
                    {
                        part.Cmd = tr.Method("DoNothing", part).GetValue <UnitCommand>();
                    }
                    if (part.Cmd == null)
                    {
                        return(false);
                    }
                    part.Owner.Unit.Commands.Run(part.Cmd);
                }
                else
                {
                    unit.Remove <UnitPartConfusion>();
                }

                return(false);
            }
Beispiel #18
0
 private static bool IsFlatFootedTo(this UnitEntityData unit1, UnitEntityData unit2)
 {
     return(Rulebook.Trigger(new RuleCheckTargetFlatFooted(unit2, unit1)).IsFlatFooted);
 }
Beispiel #19
0
            public static void Postfix(bool __state, ItemEntity __instance, ref bool __result, UnitDescriptor user)
            {
                if (__state)
                {
                    BlueprintItemEquipment blueprintItemEquipment = __instance.Blueprint as BlueprintItemEquipment;
                    if (!blueprintItemEquipment || !blueprintItemEquipment.GainAbility)
                    {
                        __result = false;
                        return;
                    }
                    if (!__instance.IsSpendCharges)
                    {
                        __result = true;
                        return;
                    }
                    bool hasNoCharges = false;
                    if (__instance.Charges > 0)
                    {
                        ItemEntityUsable itemEntityUsable = new ItemEntityUsable((BlueprintItemEquipmentUsable)__instance.Blueprint);
                        if (user.State.Features.HandOfMagusDan && itemEntityUsable.Blueprint.Type == UsableItemType.Scroll)
                        {
                            RuleRollDice ruleRollDice = new RuleRollDice(user.Unit, new DiceFormula(1, DiceType.D100));
                            Rulebook.Trigger(ruleRollDice);
                            if (ruleRollDice.Result <= 25)
                            {
                                __result = true;
                                return;
                            }
                        }

                        if (user.IsPlayerFaction)
                        {
                            __result = true;
                            return;
                        }

                        --__instance.Charges;
                    }
                    else
                    {
                        hasNoCharges = true;
                    }

                    if (__instance.Charges >= 1 || blueprintItemEquipment.RestoreChargesOnRest)
                    {
                        __result = !hasNoCharges;
                        return;
                    }

                    if (__instance.Count > 1)
                    {
                        __instance.DecrementCount(1);
                        __instance.Charges = 1;
                    }
                    else
                    {
                        ItemsCollection collection = __instance.Collection;
                        collection?.Remove(__instance);
                    }

                    __result = !hasNoCharges;
                }
            }
        static bool Prefix(RuleCalculateBaseCMB __instance)
        {
            if (__instance.ReplaceBAB.HasValue)
            {
                // if bab is replaced it is not an attack (very likely a spell or some other ability)
                return(true);
            }

            var attack   = Rulebook.CurrentContext.AllEvents.LastOfType <RuleAttackWithWeapon>();
            var maneuver = Rulebook.CurrentContext.AllEvents.LastOfType <RuleCombatManeuver>();

            if (maneuver == null || !maneuverAsAttack(maneuver.Type, __instance.Initiator))
            {
                return(true);
            }

            var weapon  = __instance.Initiator.Body?.PrimaryHand?.MaybeWeapon;
            var penalty = 0;

            if (attack != null)
            {
                weapon = attack.Weapon;
                if (attack.AttackRoll != null && attack.AttackRoll.IsTriggererd)
                {
                    //if maneuver is after attack - it is a free attempt that is using iterative attack bonus
                    penalty = attack.AttackBonusPenalty;
                }
            }
            else if (__instance.Initiator.Ensure <UnitPartUseWeaponForCombatManeuver>().active())
            {
                var forced_weapon = __instance.Initiator.Ensure <UnitPartUseWeaponForCombatManeuver>().forcedWeapon();
                if (forced_weapon != null)
                {
                    weapon = forced_weapon;
                }
                penalty = __instance.Initiator.Ensure <UnitPartUseWeaponForCombatManeuver>().forcedPenalty();
            }
            else
            {
                return(true);
            }

            if (weapon == null || !weapon.Blueprint.IsMelee)
            {
                //no maneuvers without weapon or ranged weapon
                return(true);
            }
            // in order to properly get attack bonus we normally need to trigger RuleCalculateAttackBonus
            // the problem is that a lot of abilities add additional attack bonuses in OnEventAboutToTrigger( RuleAttackRoll),
            // and thus might happen after we trigger this combat maneuver
            // so instead we trigger a complete RuleAttackRoll to correctly get the bonus
            // as an unfortunate side effect it might trigger something that should not be triggered (like limited use rerolls), so it is not ideal
            // so we make a trick - we patch RuleAttackRoll to always trigger RuleCalculateAttackBonus and than call a fake RuleAttackRoll with auto hit
            // which does not make a roll

            var attack_roll = new RuleAttackRoll(maneuver.Initiator, maneuver.Target, weapon, penalty);

            attack_roll.IgnoreConcealment = true;
            attack_roll.AutoHit           = true;
            attack_roll.SuspendCombatLog  = true;

            var AttackBonus = Rulebook.Trigger <RuleAttackRoll>(attack_roll).AttackBonus;

            var ResultSizeBonus = __instance.Initiator.Descriptor.State.Size.GetModifiers().CMDAndCMD + __instance.Initiator.Descriptor.State.Size.GetModifiers().AttackAndAC;
            var ResultMiscBonus = (int)__instance.Initiator.Stats.AdditionalCMB;

            /*Main.logger.Log("Attack Detected: " + AttackBonus.ToString());
             * Main.logger.Log("Misc: " + ResultMiscBonus.ToString());
             * Main.logger.Log("Size: " + ResultSizeBonus.ToString());
             * Main.logger.Log("Additional Bonus: " + __instance.AdditionalBonus.ToString());*/

            var tr = Harmony12.Traverse.Create(__instance);

            tr.Property("Result").SetValue(AttackBonus + ResultSizeBonus + ResultMiscBonus + __instance.AdditionalBonus);
            return(false);
        }