386 lines
11 KiB
Go
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
|
|
} |