Skip to content

Commit

Permalink
Merge pull request #608 from caioavidal/spells
Browse files Browse the repository at this point in the history
Add BloodRange spell and fix some combat stuff
  • Loading branch information
caioavidal authored Dec 18, 2024
2 parents 070c26b + 130953b commit c27cdfc
Show file tree
Hide file tree
Showing 15 changed files with 196 additions and 32 deletions.
45 changes: 45 additions & 0 deletions data/extensions/Spells/Support/Knight/BloodRage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using NeoServer.Game.Combat.Spells;
using NeoServer.Game.Common;
using NeoServer.Game.Common.Contracts.Creatures;
using NeoServer.Game.Common.Creatures;

namespace NeoServer.Extensions.Spells.Support.Knight;

public class BloodRage : Spell<Food>
{
public override uint Duration => 10_000;
public override ConditionType ConditionType => ConditionType.Strengthened;
public override EffectT Effect => EffectT.GlitterBlue;

public override bool OnCast(ICombatActor actor, string words, out InvalidOperation error)
{
error = InvalidOperation.None;

if (actor is not IPlayer player) return false;

player.AddSkillBonus(SkillType.Axe, (sbyte)Math.Abs(player.Skills[SkillType.Axe].Level * 0.35));
player.AddSkillBonus(SkillType.Sword, (sbyte)Math.Abs(player.Skills[SkillType.Axe].Level * 0.35));
player.AddSkillBonus(SkillType.Club, (sbyte)Math.Abs(player.Skills[SkillType.Axe].Level * 0.35));
player.AddSkillBonus(SkillType.Fist, (sbyte)Math.Abs(player.Skills[SkillType.Axe].Level * 0.35));

actor.DisableShieldDefense();
actor.IncreaseDamageReceived(15);
return true;
}

public override void OnEnd(ICombatActor actor)
{
if (actor is not IPlayer player) return;

player.RemoveSkillBonus(SkillType.Axe, (sbyte)Math.Abs(player.Skills[SkillType.Axe].Level * 0.35));
player.RemoveSkillBonus(SkillType.Sword, (sbyte)Math.Abs(player.Skills[SkillType.Axe].Level * 0.35));
player.RemoveSkillBonus(SkillType.Club, (sbyte)Math.Abs(player.Skills[SkillType.Axe].Level * 0.35));
player.RemoveSkillBonus(SkillType.Fist, (sbyte)Math.Abs(player.Skills[SkillType.Axe].Level * 0.35));

actor.EnableShieldDefense();
actor.DecreaseDamageReceived(15);

base.OnEnd(actor);
}
}
11 changes: 10 additions & 1 deletion data/spells/spells.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@
"cooldown":"1000",
"level": 23
},
{
{
"name":"WhirlwindThrow",
"words":"exori hur",
"script": "WhirlwindThrow",
Expand All @@ -145,6 +145,15 @@
"cooldown":"1000",
"level": 500
},
{
"name":"BloodRage",
"words":"utito tempo",
"script": "BloodRage",
"mana": 290,
"vocations":["knight","elite knight"],
"cooldown":"2000",
"level": 60
},
{
"name": "Create Item",
"words": "/i",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ public void Execute(ICreature creature, ICondition c)
if (!game.CreatureManager.GetPlayerConnection(creature.CreatureId, out var connection)) return;

ushort icons = 0;
foreach (var condition in player.Conditions) icons |= (ushort)ConditionIconParser.Parse(condition.Key);
foreach (var condition in player.Conditions)
{
icons |= (ushort)ConditionIconParser.Parse(condition.Key);
}

connection.OutgoingPackets.Enqueue(new ConditionIconPacket(icons));
connection.Send();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,21 +120,21 @@ private static async Task UpdatePlayer(IPlayer player, NeoContext neoContext)
playerEntity.PosY = player.Location.Y;
playerEntity.PosZ = player.Location.Z;

playerEntity.SkillFist = player.GetSkillLevel(SkillType.Fist);
playerEntity.SkillFist = player.GetRawSkillLevel(SkillType.Fist);
playerEntity.SkillFishingTries = player.GetSkillTries(SkillType.Fist);
playerEntity.SkillClub = player.GetSkillLevel(SkillType.Club);
playerEntity.SkillClub = player.GetRawSkillLevel(SkillType.Club);
playerEntity.SkillFishingTries = player.GetSkillTries(SkillType.Club);
playerEntity.SkillSword = player.GetSkillLevel(SkillType.Sword);
playerEntity.SkillSword = player.GetRawSkillLevel(SkillType.Sword);
playerEntity.SkillSwordTries = player.GetSkillTries(SkillType.Sword);
playerEntity.SkillAxe = player.GetSkillLevel(SkillType.Axe);
playerEntity.SkillAxe = player.GetRawSkillLevel(SkillType.Axe);
playerEntity.SkillAxeTries = player.GetSkillTries(SkillType.Axe);
playerEntity.SkillDist = player.GetSkillLevel(SkillType.Distance);
playerEntity.SkillDist = player.GetRawSkillLevel(SkillType.Distance);
playerEntity.SkillDistTries = player.GetSkillTries(SkillType.Distance);
playerEntity.SkillShielding = player.GetSkillLevel(SkillType.Shielding);
playerEntity.SkillShielding = player.GetRawSkillLevel(SkillType.Shielding);
playerEntity.SkillShieldingTries = player.GetSkillTries(SkillType.Shielding);
playerEntity.SkillFishing = player.GetSkillLevel(SkillType.Fishing);
playerEntity.SkillFishing = player.GetRawSkillLevel(SkillType.Fishing);
playerEntity.SkillFishingTries = player.GetSkillTries(SkillType.Fishing);
playerEntity.MagicLevel = player.GetSkillLevel(SkillType.Magic);
playerEntity.MagicLevel = player.GetRawSkillLevel(SkillType.Magic);
playerEntity.MagicLevelTries = player.GetSkillTries(SkillType.Magic);
playerEntity.Experience = player.Experience;
playerEntity.ChaseMode = player.ChaseMode;
Expand Down
11 changes: 5 additions & 6 deletions src/GameWorldSimulator/NeoServer.Game.Combat/Spells/Spell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,25 @@ public abstract class BaseSpell : ISpell
public bool InvokeOn(ICombatActor actor, ICombatActor onCreature, string words, out InvalidOperation error)
{
if (!CanBeUsedBy(actor, out error)) return false;
if (actor is IPlayer player) player.ConsumeMana(Mana);

OnSpellInvoked?.Invoke(onCreature, this);


if (!onCreature.HasCondition(ConditionType))
if (!OnCast(onCreature, words, out error))
return false;

AddCondition(onCreature);

if (actor is IPlayer) AddCooldown(actor);


OnSpellInvoked?.Invoke(onCreature, this);
if (actor is IPlayer player) player.ConsumeMana(Mana);

return true;
}

public bool Invoke(ICombatActor actor, string words, out InvalidOperation error)
{
if (!CanBeUsedBy(actor, out error)) return false;


if (!actor.HasCondition(ConditionType))
if (!OnCast(actor, words, out error))
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ public CombatDamage(ushort damage, DamageType type)
Damage = damage;
Type = type;
Effect = EffectT.None;
NoEffect = default;
NoEffect = false;
}

public CombatDamage(ushort damage, DamageType type, EffectT effect)
{
Damage = damage;
Type = type;
Effect = effect;
NoEffect = default;
NoEffect = false;
}

public CombatDamage(ushort damage, DamageType type, bool noEffect)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,8 @@ public interface ICombatActor : IWalkableCreature
bool IsHostileTo(ICombatActor enemy);
Result OnAttack(ICombatActor enemy, out CombatAttackResult[] combatAttacks);
event StopAttack OnAttackCanceled;
void DisableShieldDefense();
void EnableShieldDefense();
void IncreaseDamageReceived(byte percentage);
void DecreaseDamageReceived(byte percentage);
}
Original file line number Diff line number Diff line change
Expand Up @@ -237,4 +237,5 @@ Result<OperationResultList<IItem>> MoveItem(IItem item, IHasItem source, IHasIte
bool CanUseOutfit(IOutfit outFit);
void SetAsHungry();
void Use(IContainer item, byte openAtIndex);
ushort GetRawSkillLevel(SkillType skillType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ public enum ConditionType : uint
ExhaustHeal = 1 << 24, // unused
Pacified = 1 << 25,
Illusion = 1 << 26,
Hungry = 1 << 27
Hungry = 1 << 27,
Strengthened = 1 << 28
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ public enum CooldownType
Awaken,
Advertise,
WalkAround,
UseItem
UseItem,
SupportSpell,
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ protected CombatActor(ICreatureType type, IMapTool mapTool, IOutfit outfit = nul

public abstract int DefendUsingShield(int attack);
public abstract int DefendUsingArmor(int attack);
public bool IsShieldDefenseEnabled { get; private set; } = true;
public byte DamageReceivedPercentage { get; private set; }

public void AddCondition(ICondition condition)
{
Expand Down Expand Up @@ -95,9 +97,11 @@ public CombatDamage ReduceDamage(CombatDamage attack)
{
int damage = attack.Damage;

damage += damage * DamageReceivedPercentage / 100;

if (CanBlock(attack.Type))
{
damage = DefendUsingShield(attack.Damage);
damage = DefendUsingShield(damage);

if (damage <= 0)
{
Expand All @@ -110,7 +114,7 @@ public CombatDamage ReduceDamage(CombatDamage attack)
}
}

if (!attack.IsElementalDamage) damage = DefendUsingArmor(attack.Damage);
if (!attack.IsElementalDamage) damage = DefendUsingArmor(damage);

if (damage <= 0)
{
Expand Down Expand Up @@ -349,6 +353,7 @@ public Result Attack(ICombatActor enemy, ICombatAttack attack, CombatAttackValue
public virtual bool CanBlock(DamageType damage)
{
if (damage != DamageType.Melee) return false;
if (!IsShieldDefenseEnabled) return false;
var hasCoolDownExpired = Cooldowns.Expired(CooldownType.Block);

if (!hasCoolDownExpired && blockCount >= BLOCK_LIMIT) return false;
Expand Down Expand Up @@ -432,6 +437,18 @@ protected void InvokeAttackCanceled()
{
OnAttackCanceled?.Invoke(this);
}

public void DisableShieldDefense() => IsShieldDefenseEnabled = false;
public void EnableShieldDefense() => IsShieldDefenseEnabled = true;
public void IncreaseDamageReceived(byte percentage)
{
DamageReceivedPercentage += percentage;
}

public void DecreaseDamageReceived(byte percentage)
{
DamageReceivedPercentage -= percentage;
}

#region Events

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,12 @@ public ushort CalculateAttackPower(float attackRate, ushort attack)
public override bool CanBeSeen => FlagIsEnabled(PlayerFlag.CanBeSeen);
public virtual bool CanSeeInspectionDetails => false;

public ushort GetRawSkillLevel(SkillType skillType)
{
var hasSkill = Skills.TryGetValue(skillType, out var skill);
var skillLevel = hasSkill ? skill.Level : 1;
return (ushort)Math.Max(0, skillLevel);
}
public ushort GetSkillLevel(SkillType skillType)
{
var hasSkill = Skills.TryGetValue(skillType, out var skill);
Expand Down Expand Up @@ -408,10 +414,12 @@ public void ChangeSecureMode(byte mode)

public override int DefendUsingShield(int attack)
{
if (!IsShieldDefenseEnabled) return attack;

var defense = Inventory.TotalDefense * Skills[SkillType.Shielding].Level *
(DefenseFactor / 100d) - attack / 100d * ArmorRating * (Vocation.Formula?.Defense ?? 1f);

var resultDamage = (int)(attack - defense);
var resultDamage = Math.Max(0, (int)(attack - defense));
if (resultDamage <= 0) IncreaseSkillCounter(SkillType.Shielding, 1);
return resultDamage;
}
Expand Down
11 changes: 2 additions & 9 deletions src/GameWorldSimulator/NeoServer.Game.Creatures/Player/Skill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,8 @@ public Skill(SkillType type, ushort level = 0, double count = 0)

public sbyte Bonus { get; private set; }

public void AddBonus(sbyte increase)
{
Bonus = (sbyte)(Bonus + increase);
}

public void RemoveBonus(sbyte decrease)
{
Bonus = (sbyte)(Bonus - decrease);
}
public void AddBonus(sbyte increase) => Bonus = (sbyte)(Bonus + increase);
public void RemoveBonus(sbyte decrease) => Bonus = (sbyte)(Bonus - decrease);

public SkillType Type { get; }
public ushort Level { get; private set; }
Expand Down
77 changes: 77 additions & 0 deletions tests/NeoServer.Game.Creatures.Tests/Combat/CombatTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using FluentAssertions;
using NeoServer.Game.Common.Combat.Structs;
using NeoServer.Game.Common.Creatures.Players;
using NeoServer.Game.Common.Item;
using NeoServer.Game.Creatures.Player.Inventory;
using NeoServer.Game.Tests.Helpers;
using NeoServer.Game.Tests.Helpers.Player;
using Xunit;

namespace NeoServer.Game.Creatures.Tests.Combat;

public class CombatTests
{
[Fact]
public void Player_Gets_More_Damage_When_Has_Damage_Percentage_Increased()
{
//arrange
var victim = PlayerTestDataBuilder.Build(hp: 1000);
var attacker = PlayerTestDataBuilder.Build();

//act
victim.ReceiveAttack(attacker, new CombatDamage(100, DamageType.Physical));

//assert
victim.HealthPoints.Should().Be(900);

//act
victim.IncreaseDamageReceived(100);
victim.ReceiveAttack(attacker, new CombatDamage(100, DamageType.Physical));

//assert
victim.HealthPoints.Should().Be(700);

//act
victim.DecreaseDamageReceived(100);
victim.ReceiveAttack(attacker, new CombatDamage(100, DamageType.Physical));

//assert
victim.HealthPoints.Should().Be(600);
}

[Fact]
public void Player_Does_Not_Block_Attack_When_Shield_Defense_Is_Disabled()
{
//arrange
var inventory = InventoryTestDataBuilder.Build();
var shield = ItemTestData.CreateBodyEquipmentItem(7, "", "shield");
shield.Metadata.Attributes.SetAttribute(ItemAttribute.Defense, byte.MaxValue);

inventory.AddItem(shield, Slot.Right);

var victim = PlayerTestDataBuilder.Build(hp: 1000, capacity: uint.MaxValue );
victim.AddInventory(inventory);

var attacker = PlayerTestDataBuilder.Build();

//act
victim.ReceiveAttack(attacker, new CombatDamage(1, DamageType.Melee));

//assert
victim.HealthPoints.Should().Be(1000);

//act
victim.DisableShieldDefense();
victim.ReceiveAttack(attacker, new CombatDamage(1, DamageType.Melee));

//assert
victim.HealthPoints.Should().Be(999);

//act
victim.EnableShieldDefense();
victim.ReceiveAttack(attacker, new CombatDamage(1, DamageType.Melee));

//assert
victim.HealthPoints.Should().Be(999);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ public static IPlayer Build(uint id = 1, string name = "PlayerA", uint capacity
{
GetIncreaseRate = () => 1
}
},
{
SkillType.Shielding, new Skill(SkillType.Shielding, 10, 1)
{
GetIncreaseRate = () => 1
}
}
},
300, new Outfit(), speed, new Location(100, 100, 7),
Expand Down

0 comments on commit c27cdfc

Please sign in to comment.