This post is about how I implemented some encounter powers by building on top of a bunch of prior work for this project.
Tooltip for the power in question:
Code for an ability modifier bonus that is conditional on using a specific power and having a certain type of weapon equipped. GetBonus is called if the stat that is being computed matches the stat in an effect. Creating a copy of the Bonus is a point of friction that I'm considering changing to allow the Bonus object to define an ability modifier, but that implies passing in a Creature each time you want to roll the dice defined in the Bonus so it's a non-trivial change.
Aside: all of the stats and numbers are defined in terms of dice, not flat numbers. This allows for things like "you get a 1d6 bonus to attack rolls under these circumstances" so that 'Dice.Parse' line resolves to something like 3d1 when a flat number is needed.
Code: Select all
public partial class ConditionalAbilityBonusEffect : FixedEffect {
private ConditionalAbilityBonusEffect() { } //required by Godot
public ConditionalAbilityBonusEffect(EffectType[] effectTypes, Power power, Req condition, Ability abilityMod, Stat stat, BonusType bonusType, GameElement gameElement, SrcId srcId)
: base(effectTypes, Dice._0d, stat, bonusType, gameElement, srcId) {
Power = power;
Condition = condition;
AbilityMod = abilityMod;
}
public Power Power { get; protected init; }
public Req Condition { get; protected init; }
public Ability AbilityMod { get; protected init; }
public override void GetBonus(Creature host, Stat stat, List<Bonus> list, BonusContext bc) {
if(IsStat(stat) && bc.Attack_Power == Power && Condition.IsMet(host)) {
Bonus modBonus = Bonus.Create(Bonus);
modBonus.Value = Dice.Parse(host.GetAbilityMod(AbilityMod));
list.Add(modBonus);
}
}
public override string ToStringBbcode() {
return $"[b]{Condition.ToStringBbcode()}[/b] +{AbilityMod.Info().ShortTitle}{base.ToStringBbcode()}";
}
}
Scripting attached to the first power, defined as an "extra effect" rather than an effect that happens on hit/miss/self:
Code: Select all
(srcId) => new([
new ConditionalAbilityBonusEffect([], Power.ArmorPiercingThrust, new WeaponGroupReq(){ EligibleWeaponGroups = [WeaponGroup.LightBlade, WeaponGroup.Spear] }, Ability.Dex, Stat.Combat_Attack, BonusType.UntypedHidden, GameElement.Power_ArmorPiercingThrust, srcId),
new ConditionalAbilityBonusEffect([], Power.ArmorPiercingThrust, new WeaponGroupReq(){ EligibleWeaponGroups = [WeaponGroup.LightBlade, WeaponGroup.Spear] }, Ability.Dex, Stat.Combat_AttackDamage, BonusType.UntypedHidden, GameElement.Power_ArmorPiercingThrust, srcId)
])
Scripting attached to the other power, essentially another encounter power implemented with just one line of code:
Code: Select all
(srcId) => new([
new ConditionalAbilityBonusEffect([], Power.CrushingBlow, new WeaponGroupReq(){ EligibleWeaponGroups = [WeaponGroup.Axe, WeaponGroup.Hammer, WeaponGroup.Mace] }, Ability.Con, Stat.Combat_AttackDamage, BonusType.UntypedHidden, GameElement.Power_CrushingBlow, srcId)
])
As I build up more complexity in the system some things like this become easier. You can see that I'm leveraging the WeaponGroupReq class, which is related to all of the requirements checks that are defined for determining if a character is eligible for things like feats.
In order to finish all of the level 1-3 powers for these martial classes I'll need to add some additional capabilities, such as the ability to target more than one cell when performing a power. For example, a power that lets you shoot two different targets with an arrow. Or a power that lets you move an ally to another square. I have been putting this off for quite but there isn't really a way around it.
Four of the remaining powers for this class are multi-target.
The other is a close burst, which also needs feature work.