Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,480 changes: 1,336 additions & 1,144 deletions _datafiles/html/public/webclient-pure.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down
223 changes: 217 additions & 6 deletions internal/combat/combat.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import (
"github.com/GoMudEngine/GoMud/internal/buffs"
"github.com/GoMudEngine/GoMud/internal/characters"
"github.com/GoMudEngine/GoMud/internal/configs"
"github.com/GoMudEngine/GoMud/internal/events"
"github.com/GoMudEngine/GoMud/internal/items"
"github.com/GoMudEngine/GoMud/internal/mobs"
"github.com/GoMudEngine/GoMud/internal/mudlog"
"github.com/GoMudEngine/GoMud/internal/races"
"github.com/GoMudEngine/GoMud/internal/rooms"
"github.com/GoMudEngine/GoMud/internal/skills"
Expand All @@ -33,19 +33,69 @@ func AttackPlayerVsMob(user *users.UserRecord, mob *mobs.Mob) AttackResult {
attackResult := calculateCombat(*user.Character, mob.Character, User, Mob)

if attackResult.DamageToSource != 0 {
oldUserHealth := user.Character.Health
user.Character.ApplyHealthChange(attackResult.DamageToSource * -1)
newUserHealth := user.Character.Health
user.WimpyCheck()

if oldUserHealth != newUserHealth {
events.AddToQueue(events.CharacterVitalsChanged{
UserId: user.UserId,
})
}
}

oldHealth := mob.Character.Health
mob.Character.ApplyHealthChange(attackResult.DamageToTarget * -1)
newHealth := mob.Character.Health

if oldHealth != newHealth {
events.AddToQueue(events.MobVitalsChanged{
MobId: mob.InstanceId,
OldHealth: oldHealth,
NewHealth: newHealth,
OldMana: mob.Character.Mana,
NewMana: mob.Character.Mana,
ChangeType: "damage",
})
}

// Remember who has hit him
mob.Character.TrackPlayerDamage(user.UserId, attackResult.DamageToTarget)

if attackResult.Hit {
user.PlaySound(`hit-other`, `combat`)

if attackResult.DamageToTarget > 0 {
// Use priority 10 for damage events to ensure they're processed quickly
events.AddToQueue(events.DamageDealt{
SourceId: user.UserId,
SourceType: "player",
SourceName: user.Character.Name,
TargetId: mob.InstanceId,
TargetType: "mob",
TargetName: mob.Character.Name,
Amount: attackResult.DamageToTarget,
DamageType: "physical",
WeaponName: getWeaponName(user.Character),
SpellName: "",
IsCritical: attackResult.Crit,
IsKillingBlow: mob.Character.Health <= 0,
}, 10)
}
} else {
user.PlaySound(`miss`, `combat`)

events.AddToQueue(events.AttackAvoided{
AttackerId: user.UserId,
AttackerType: "player",
AttackerName: user.Character.Name,
DefenderId: mob.InstanceId,
DefenderType: "mob",
DefenderName: mob.Character.Name,
AvoidType: "miss",
WeaponName: getWeaponName(user.Character),
})
}

return attackResult
Expand All @@ -57,20 +107,64 @@ func AttackPlayerVsPlayer(userAtk *users.UserRecord, userDef *users.UserRecord)
attackResult := calculateCombat(*userAtk.Character, *userDef.Character, User, User)

if attackResult.DamageToSource != 0 {
oldAtkHealth := userAtk.Character.Health
userAtk.Character.ApplyHealthChange(attackResult.DamageToSource * -1)
newAtkHealth := userAtk.Character.Health
userAtk.WimpyCheck()

if oldAtkHealth != newAtkHealth {
events.AddToQueue(events.CharacterVitalsChanged{
UserId: userAtk.UserId,
})
}
}

if attackResult.DamageToTarget != 0 {
oldDefHealth := userDef.Character.Health
userDef.Character.ApplyHealthChange(attackResult.DamageToTarget * -1)
newDefHealth := userDef.Character.Health
userDef.WimpyCheck()

if oldDefHealth != newDefHealth {
events.AddToQueue(events.CharacterVitalsChanged{
UserId: userDef.UserId,
})
}
}

if attackResult.Hit {
userAtk.PlaySound(`hit-other`, `combat`)
userDef.PlaySound(`hit-self`, `combat`)

if attackResult.DamageToTarget > 0 {
events.AddToQueue(events.DamageDealt{
SourceId: userAtk.UserId,
SourceType: "player",
SourceName: userAtk.Character.Name,
TargetId: userDef.UserId,
TargetType: "player",
TargetName: userDef.Character.Name,
Amount: attackResult.DamageToTarget,
DamageType: "physical",
WeaponName: getWeaponName(userAtk.Character),
SpellName: "",
IsCritical: attackResult.Crit,
IsKillingBlow: userDef.Character.Health <= 0,
})
}
} else {
userAtk.PlaySound(`miss`, `combat`)

events.AddToQueue(events.AttackAvoided{
AttackerId: userAtk.UserId,
AttackerType: "player",
AttackerName: userAtk.Character.Name,
DefenderId: userDef.UserId,
DefenderType: "player",
DefenderName: userDef.Character.Name,
AvoidType: "miss",
WeaponName: getWeaponName(userAtk.Character),
})
}

return attackResult
Expand All @@ -81,15 +175,64 @@ func AttackMobVsPlayer(mob *mobs.Mob, user *users.UserRecord) AttackResult {

attackResult := calculateCombat(mob.Character, *user.Character, Mob, User)

oldHealth := mob.Character.Health
mob.Character.ApplyHealthChange(attackResult.DamageToSource * -1)
newHealth := mob.Character.Health

if oldHealth != newHealth {
events.AddToQueue(events.MobVitalsChanged{
MobId: mob.InstanceId,
OldHealth: oldHealth,
NewHealth: newHealth,
OldMana: mob.Character.Mana,
NewMana: mob.Character.Mana,
ChangeType: "damage",
})
}

if attackResult.DamageToTarget != 0 {
oldUserHealth := user.Character.Health
user.Character.ApplyHealthChange(attackResult.DamageToTarget * -1)
newUserHealth := user.Character.Health
user.WimpyCheck()

if oldUserHealth != newUserHealth {
events.AddToQueue(events.CharacterVitalsChanged{
UserId: user.UserId,
})
}
}

if attackResult.Hit {
user.PlaySound(`hit-self`, `combat`)

if attackResult.DamageToTarget > 0 {
events.AddToQueue(events.DamageDealt{
SourceId: mob.InstanceId,
SourceType: "mob",
SourceName: mob.Character.Name,
TargetId: user.UserId,
TargetType: "player",
TargetName: user.Character.Name,
Amount: attackResult.DamageToTarget,
DamageType: "physical",
WeaponName: getWeaponName(&mob.Character),
SpellName: "",
IsCritical: attackResult.Crit,
IsKillingBlow: user.Character.Health <= 0,
})
}
} else {
events.AddToQueue(events.AttackAvoided{
AttackerId: mob.InstanceId,
AttackerType: "mob",
AttackerName: mob.Character.Name,
DefenderId: user.UserId,
DefenderType: "player",
DefenderName: user.Character.Name,
AvoidType: "miss",
WeaponName: getWeaponName(&mob.Character),
})
}

return attackResult
Expand All @@ -98,17 +241,76 @@ func AttackMobVsPlayer(mob *mobs.Mob, user *users.UserRecord) AttackResult {
// Performs a combat round from a mob to a mob
func AttackMobVsMob(mobAtk *mobs.Mob, mobDef *mobs.Mob) AttackResult {

attackResult := calculateCombat(mobAtk.Character, mobDef.Character, Mob, User)
attackResult := calculateCombat(mobAtk.Character, mobDef.Character, Mob, Mob)

// Track attacker health changes
oldHealthAtk := mobAtk.Character.Health
mobAtk.Character.ApplyHealthChange(attackResult.DamageToSource * -1)
newHealthAtk := mobAtk.Character.Health

if oldHealthAtk != newHealthAtk {
events.AddToQueue(events.MobVitalsChanged{
MobId: mobAtk.InstanceId,
OldHealth: oldHealthAtk,
NewHealth: newHealthAtk,
OldMana: mobAtk.Character.Mana,
NewMana: mobAtk.Character.Mana,
ChangeType: "damage",
})
}

// Track defender health changes
oldHealthDef := mobDef.Character.Health
mobDef.Character.ApplyHealthChange(attackResult.DamageToTarget * -1)
newHealthDef := mobDef.Character.Health

if oldHealthDef != newHealthDef {
events.AddToQueue(events.MobVitalsChanged{
MobId: mobDef.InstanceId,
OldHealth: oldHealthDef,
NewHealth: newHealthDef,
OldMana: mobDef.Character.Mana,
NewMana: mobDef.Character.Mana,
ChangeType: "damage",
})
}

// If attacking mob was player charmed, attribute damage done to that player
if charmedUserId := mobAtk.Character.GetCharmedUserId(); charmedUserId > 0 {
// Remember who has hit him
mobDef.Character.TrackPlayerDamage(charmedUserId, attackResult.DamageToTarget)
}

if attackResult.Hit {
if attackResult.DamageToTarget > 0 {
events.AddToQueue(events.DamageDealt{
SourceId: mobAtk.InstanceId,
SourceType: "mob",
SourceName: mobAtk.Character.Name,
TargetId: mobDef.InstanceId,
TargetType: "mob",
TargetName: mobDef.Character.Name,
Amount: attackResult.DamageToTarget,
DamageType: "physical",
WeaponName: getWeaponName(&mobAtk.Character),
SpellName: "",
IsCritical: attackResult.Crit,
IsKillingBlow: mobDef.Character.Health <= 0,
})
}
} else {
events.AddToQueue(events.AttackAvoided{
AttackerId: mobAtk.InstanceId,
AttackerType: "mob",
AttackerName: mobAtk.Character.Name,
DefenderId: mobDef.InstanceId,
DefenderType: "mob",
DefenderName: mobDef.Character.Name,
AvoidType: "miss",
WeaponName: getWeaponName(&mobAtk.Character),
})
}

return attackResult
}

Expand Down Expand Up @@ -235,8 +437,6 @@ func calculateCombat(sourceChar characters.Character, targetChar characters.Char

for i := 0; i < attackCount; i++ {

mudlog.Debug(`calculateCombat`, `Atk`, fmt.Sprintf(`%d/%d`, i+1, attackCount), `Source`, fmt.Sprintf(`%s (%s)`, sourceChar.Name, sourceType), `Target`, fmt.Sprintf(`%s (%s)`, targetChar.Name, targetType))

attackWeapons := []items.Item{}

dualWieldLevel := sourceChar.GetSkillLevel(skills.DualWield)
Expand Down Expand Up @@ -341,8 +541,6 @@ func calculateCombat(sourceChar characters.Character, targetChar characters.Char
msgSeed = weapon.ItemId
}

mudlog.Debug("DiceRolls", "attacks", attacks, "dCount", dCount, "dSides", dSides, "dBonus", dBonus, "critBuffs", critBuffs)

// Individual weapons may get multiple attacks
for j := 0; j < attacks; j++ {

Expand Down Expand Up @@ -505,6 +703,7 @@ func calculateCombat(sourceChar characters.Character, targetChar characters.Char

attackResult.DamageToSource += attackSourceDamage
attackResult.DamageToSourceReduction += attackSourceReduction

}

if util.RollDice(1, 5) == 1 { // 20% chance to join
Expand Down Expand Up @@ -601,3 +800,15 @@ func Crits(sourceChar characters.Character, targetChar characters.Character) boo

return critRoll < critChance
}

// Helper functions for combat events

// getWeaponName safely gets the weapon name from a character
func getWeaponName(char *characters.Character) string {
if char.Equipment.Weapon.ItemId > 0 {
return char.Equipment.Weapon.DisplayName()
}
// Return unarmed name from race
raceInfo := races.GetRace(char.RaceId)
return raceInfo.UnarmedName
}
Loading
Loading