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); }
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(); } }
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); }
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; } }
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); } }
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; }
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); }
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); }
private static bool IsFlatFootedTo(this UnitEntityData unit1, UnitEntityData unit2) { return(Rulebook.Trigger(new RuleCheckTargetFlatFooted(unit2, unit1)).IsFlatFooted); }
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); }