2025-08-30 11:51:05 -05:00

386 lines
11 KiB
Go

package combat
import (
"math"
"time"
)
// PVPManager handles all PVP-related logic and validation
type PVPManager struct {
config *CombatConfig
ruleManager RuleManager
}
// NewPVPManager creates a new PVP manager
func NewPVPManager(config *CombatConfig, ruleManager RuleManager) *PVPManager {
return &PVPManager{
config: config,
ruleManager: ruleManager,
}
}
// IsPVPAllowed checks if PVP is allowed between two players
func (pm *PVPManager) IsPVPAllowed(attacker Entity, victim Entity) bool {
if !pm.config.EnablePVP {
return false
}
// Both must be players
if !attacker.IsPlayer() || !victim.IsPlayer() {
return false
}
// Cannot attack self
if attacker.GetID() == victim.GetID() {
return false
}
// Check if victim is attackable
if victim.GetAttackable() == 0 {
return false
}
// Check PVP type restrictions
if !pm.checkPVPType(attacker, victim) {
return false
}
// Check level range restrictions
if !pm.checkLevelRange(attacker, victim) {
return false
}
// Check zone PVP rules
if !pm.checkZonePVPRules(attacker, victim) {
return false
}
// Check faction rules
if !pm.checkFactionPVPRules(attacker, victim) {
return false
}
// Check guild rules
if !pm.checkGuildPVPRules(attacker, victim) {
return false
}
// Check recent login protection
if !pm.checkRecentLoginProtection(victim) {
return false
}
return true
}
// checkPVPType validates PVP type rules
func (pm *PVPManager) checkPVPType(attacker Entity, victim Entity) bool {
switch pm.config.PVPType {
case PVPTypeNone:
return false
case PVPTypeOpen:
return true
case PVPTypeRace:
return pm.checkRacePVP(attacker, victim)
case PVPTypeClass:
return pm.checkClassPVP(attacker, victim)
case PVPTypeGuild:
return pm.checkGuildWarPVP(attacker, victim)
case PVPTypeAlignment:
return pm.checkAlignmentPVP(attacker, victim)
default:
return false
}
}
// checkLevelRange validates level-based PVP restrictions
func (pm *PVPManager) checkLevelRange(attacker Entity, victim Entity) bool {
if pm.config.PVPLevelRange <= 0 {
return true // No level restrictions
}
levelDiff := int32(math.Abs(float64(attacker.GetLevel() - victim.GetLevel())))
return levelDiff <= pm.config.PVPLevelRange
}
// checkZonePVPRules validates zone-specific PVP rules
func (pm *PVPManager) checkZonePVPRules(attacker Entity, victim Entity) bool {
zoneID := attacker.GetZoneID()
// Check if PVP is enabled in this zone
zonePVPEnabled := pm.ruleManager.GetZoneBool(zoneID, RuleCategoryPVP, RuleAllowPVP)
if !zonePVPEnabled {
return false
}
// Check for safe zones or newbie areas
if pm.isNewbieZone(zoneID) {
maxNewbieLevel := pm.ruleManager.GetZoneInt32(zoneID, RuleCategoryPVP, RulePVPNewbieLevel)
if victim.GetLevel() <= maxNewbieLevel {
return false
}
}
return true
}
// checkFactionPVPRules validates faction-based PVP rules
func (pm *PVPManager) checkFactionPVPRules(attacker Entity, victim Entity) bool {
attackerFaction := attacker.GetFactionID()
victimFaction := victim.GetFactionID()
// Same faction can only attack in guild wars or specific PVP types
if attackerFaction == victimFaction && attackerFaction > 0 {
return pm.config.PVPType == PVPTypeGuild || pm.config.PVPType == PVPTypeOpen
}
// Check faction standings
return pm.areFactionHostile(attackerFaction, victimFaction)
}
// checkGuildPVPRules validates guild-based PVP rules
func (pm *PVPManager) checkGuildPVPRules(attacker Entity, victim Entity) bool {
// This would integrate with guild system when available
// For now, simplified logic
// Same guild members cannot attack each other unless in guild war
attackerGuildID := pm.getPlayerGuildID(attacker.GetID())
victimGuildID := pm.getPlayerGuildID(victim.GetID())
if attackerGuildID > 0 && attackerGuildID == victimGuildID {
// Check if guild war is active
return pm.isGuildWarActive(attackerGuildID, victimGuildID)
}
return true
}
// checkRecentLoginProtection checks for new player login protection
func (pm *PVPManager) checkRecentLoginProtection(victim Entity) bool {
// This would check player login time when player system is integrated
// For now, assume no protection needed
return true
}
// checkRacePVP validates race-based PVP rules
func (pm *PVPManager) checkRacePVP(attacker Entity, victim Entity) bool {
// Different races can attack each other
return attacker.GetRace() != victim.GetRace()
}
// checkClassPVP validates class-based PVP rules
func (pm *PVPManager) checkClassPVP(attacker Entity, victim Entity) bool {
// Different classes can attack each other
return attacker.GetClass() != victim.GetClass()
}
// checkGuildWarPVP validates guild war PVP rules
func (pm *PVPManager) checkGuildWarPVP(attacker Entity, victim Entity) bool {
attackerGuildID := pm.getPlayerGuildID(attacker.GetID())
victimGuildID := pm.getPlayerGuildID(victim.GetID())
if attackerGuildID == 0 || victimGuildID == 0 {
return false // Both must be in guilds
}
return pm.isGuildWarActive(attackerGuildID, victimGuildID)
}
// checkAlignmentPVP validates alignment-based PVP rules
func (pm *PVPManager) checkAlignmentPVP(attacker Entity, victim Entity) bool {
attackerAlignment := attacker.GetInfoStruct().GetAlignment()
victimAlignment := victim.GetInfoStruct().GetAlignment()
// Evil vs Good, Neutral can attack anyone
return pm.areAlignmentsHostile(attackerAlignment, victimAlignment)
}
// CalculatePVPDamageModifier calculates damage modifiers for PVP combat
func (pm *PVPManager) CalculatePVPDamageModifier(attacker Entity, victim Entity) float32 {
modifier := float32(1.0)
// Level-based modifier
levelDiff := float32(attacker.GetLevel() - victim.GetLevel())
if levelDiff > 0 {
// Higher level attacker does less damage to lower level victim
modifier -= (levelDiff * 0.05)
} else if levelDiff < 0 {
// Lower level attacker does more damage to higher level victim
modifier += (float32(math.Abs(float64(levelDiff))) * 0.02)
}
// PVP damage reduction
pvpDamageMultiplier := pm.ruleManager.GetFloat32(RuleCategoryPVP, RulePVPDamageMultiplier)
modifier *= pvpDamageMultiplier
// Cap modifiers
if modifier > 2.0 {
modifier = 2.0
} else if modifier < 0.1 {
modifier = 0.1
}
return modifier
}
// CalculatePVPHealingModifier calculates healing modifiers for PVP combat
func (pm *PVPManager) CalculatePVPHealingModifier(caster Entity, target Entity) float32 {
// Healing is generally less effective in PVP
pvpHealingMultiplier := pm.ruleManager.GetFloat32(RuleCategoryPVP, RulePVPHealingMultiplier)
modifier := pvpHealingMultiplier
if modifier < 0.1 {
modifier = 0.1
} else if modifier > 1.0 {
modifier = 1.0
}
return modifier
}
// GetPVPExperienceModifier calculates experience modifiers for PVP kills
func (pm *PVPManager) GetPVPExperienceModifier(killer Entity, victim Entity) float32 {
levelDiff := float32(victim.GetLevel() - killer.GetLevel())
modifier := float32(1.0)
// Higher level victims give more experience
if levelDiff > 0 {
modifier += (levelDiff * 0.1)
} else if levelDiff < 0 {
// Lower level victims give less experience
modifier += (levelDiff * 0.05)
}
// Cap modifiers
if modifier > 3.0 {
modifier = 3.0
} else if modifier < 0.1 {
modifier = 0.1
}
return modifier
}
// HandlePVPKill processes a PVP kill
func (pm *PVPManager) HandlePVPKill(killer Entity, victim Entity) {
// This would integrate with experience, faction, and reputation systems
// For now, just basic handling
// Award PVP experience
expModifier := pm.GetPVPExperienceModifier(killer, victim)
baseExp := float32(victim.GetLevel() * 100)
_ = int32(baseExp * expModifier) // pvpExp - would be used when experience system is integrated
// This would call experience system when available
// killer.AddExperience(pvpExp)
// Handle faction changes if applicable
pm.handlePVPFactionChanges(killer, victim)
// Handle guild war points if applicable
pm.handleGuildWarPoints(killer, victim)
}
// Helper functions (would integrate with other systems when available)
// isNewbieZone checks if a zone is designated as a newbie/starting zone
func (pm *PVPManager) isNewbieZone(zoneID int32) bool {
// This would check against a list of newbie zones
newbieZones := []int32{1, 2, 3, 4} // Example zone IDs
for _, newbieZone := range newbieZones {
if zoneID == newbieZone {
return true
}
}
return false
}
// areFactionHostile checks if two factions are hostile to each other
func (pm *PVPManager) areFactionHostile(faction1, faction2 int32) bool {
// This would integrate with faction system
// For now, simplified logic: different factions are hostile
return faction1 != faction2
}
// getPlayerGuildID gets the guild ID for a player
func (pm *PVPManager) getPlayerGuildID(playerID int32) int32 {
// This would integrate with guild system
// For now, return 0 (no guild)
return 0
}
// isGuildWarActive checks if two guilds are at war
func (pm *PVPManager) isGuildWarActive(guild1ID, guild2ID int32) bool {
// This would integrate with guild war system
// For now, return false
return false
}
// areAlignmentsHostile checks if two alignments are hostile
func (pm *PVPManager) areAlignmentsHostile(alignment1, alignment2 int32) bool {
// Evil vs Good are always hostile
if (alignment1 < 0 && alignment2 > 0) || (alignment1 > 0 && alignment2 < 0) {
return true
}
// Neutral (0) can attack anyone
if alignment1 == 0 || alignment2 == 0 {
return true
}
return false
}
// handlePVPFactionChanges processes faction changes from PVP kills
func (pm *PVPManager) handlePVPFactionChanges(killer Entity, victim Entity) {
// This would integrate with faction system
// Could modify faction standings based on PVP kills
}
// handleGuildWarPoints processes guild war points from PVP kills
func (pm *PVPManager) handleGuildWarPoints(killer Entity, victim Entity) {
// This would integrate with guild war system
// Could award points for guild vs guild kills
}
// GetPVPStatistics returns PVP-related statistics
func (pm *PVPManager) GetPVPStatistics() map[string]interface{} {
return map[string]interface{}{
"pvp_enabled": pm.config.EnablePVP,
"pvp_type": pm.config.PVPType,
"level_range": pm.config.PVPLevelRange,
"damage_modifier": pm.ruleManager.GetFloat32(RuleCategoryPVP, RulePVPDamageMultiplier),
}
}
// ValidateAttack performs comprehensive PVP attack validation
func (pm *PVPManager) ValidateAttack(attacker Entity, victim Entity, attackType int8) (bool, string) {
if !pm.IsPVPAllowed(attacker, victim) {
return false, "PVP not allowed between these players"
}
// Additional attack-specific validation
if attackType == AttackTypeRanged {
// Check if ranged PVP is allowed in this zone
zoneID := attacker.GetZoneID()
if !pm.ruleManager.GetZoneBool(zoneID, RuleCategoryPVP, RulePVPRangedAllowed) {
return false, "Ranged attacks not allowed in this zone"
}
}
if attackType == AttackTypeSpell {
// Check if spell PVP is allowed
if !pm.ruleManager.GetBool(RuleCategoryPVP, RulePVPSpellsAllowed) {
return false, "Spell attacks not allowed in PVP"
}
}
return true, ""
}
// GetPVPCooldown calculates PVP cooldown time after combat
func (pm *PVPManager) GetPVPCooldown(player Entity) time.Duration {
baseCooldown := pm.ruleManager.GetInt32(RuleCategoryPVP, RulePVPCombatCooldown)
return time.Duration(baseCooldown) * time.Second
}