eq2go/internal/common/visual_states.go

378 lines
9.3 KiB
Go

package common
import (
"fmt"
"strings"
"sync"
)
// VisualState represents a visual animation state
type VisualState struct {
id int
name string
}
// NewVisualState creates a new visual state
func NewVisualState(id int, name string) *VisualState {
return &VisualState{
id: id,
name: name,
}
}
// GetID returns the visual state ID
func (vs *VisualState) GetID() int {
return vs.id
}
// GetName returns the visual state name
func (vs *VisualState) GetName() string {
return vs.name
}
// Emote represents an emote with visual state and messages
type Emote struct {
name string
visualState int32
message string
targetedMessage string
}
// NewEmote creates a new emote
func NewEmote(name string, visualState int32, message, targetedMessage string) *Emote {
return &Emote{
name: name,
visualState: visualState,
message: message,
targetedMessage: targetedMessage,
}
}
// GetName returns the emote name
func (e *Emote) GetName() string {
return e.name
}
// GetVisualState returns the visual state ID
func (e *Emote) GetVisualState() int32 {
return e.visualState
}
// GetMessage returns the emote message
func (e *Emote) GetMessage() string {
return e.message
}
// GetTargetedMessage returns the targeted emote message
func (e *Emote) GetTargetedMessage() string {
return e.targetedMessage
}
// VersionRange represents a min/max version range
type VersionRange struct {
minVersion int32
maxVersion int32
}
// NewVersionRange creates a new version range
func NewVersionRange(min, max int32) *VersionRange {
return &VersionRange{
minVersion: min,
maxVersion: max,
}
}
// GetMinVersion returns the minimum version
func (vr *VersionRange) GetMinVersion() int32 {
return vr.minVersion
}
// GetMaxVersion returns the maximum version
func (vr *VersionRange) GetMaxVersion() int32 {
return vr.maxVersion
}
// InRange checks if a version is within this range
func (vr *VersionRange) InRange(version int32) bool {
return version >= vr.minVersion && (vr.maxVersion == 0 || version <= vr.maxVersion)
}
// EmoteVersionRange manages emotes across different client versions
type EmoteVersionRange struct {
name string
versionMap map[*VersionRange]*Emote
mutex sync.RWMutex
}
// NewEmoteVersionRange creates a new emote version range
func NewEmoteVersionRange(name string) *EmoteVersionRange {
return &EmoteVersionRange{
name: name,
versionMap: make(map[*VersionRange]*Emote),
}
}
// GetName returns the emote range name
func (evr *EmoteVersionRange) GetName() string {
return evr.name
}
// AddVersionRange adds an emote for a specific version range
func (evr *EmoteVersionRange) AddVersionRange(minVersion, maxVersion int32, name string, visualState int32, message, targetedMessage string) error {
evr.mutex.Lock()
defer evr.mutex.Unlock()
// Check for duplicate ranges
for vr := range evr.versionMap {
if evr.rangesOverlap(vr, minVersion, maxVersion) {
return fmt.Errorf("duplicate emote mapping of %s with range min %d max %d, existing found with range min %d max %d",
evr.name, minVersion, maxVersion, vr.minVersion, vr.maxVersion)
}
}
vr := NewVersionRange(minVersion, maxVersion)
emote := NewEmote(name, visualState, message, targetedMessage)
evr.versionMap[vr] = emote
return nil
}
// rangesOverlap checks if two version ranges overlap
func (evr *EmoteVersionRange) rangesOverlap(existing *VersionRange, minVersion, maxVersion int32) bool {
// Check various overlap conditions
if existing.minVersion <= minVersion && maxVersion <= existing.maxVersion {
return true
}
if existing.minVersion <= minVersion && existing.maxVersion == 0 {
return true
}
if existing.minVersion == 0 && maxVersion <= existing.maxVersion {
return true
}
return false
}
// FindEmoteByVersion finds the emote for a specific client version
func (evr *EmoteVersionRange) FindEmoteByVersion(version int32) *Emote {
evr.mutex.RLock()
defer evr.mutex.RUnlock()
for vr, emote := range evr.versionMap {
if vr.InRange(version) {
return emote
}
}
return nil
}
// VisualStates manages all visual states, emotes, and spell visuals
type VisualStates struct {
visualStateMap map[string]*VisualState
emoteMap map[string]*EmoteVersionRange
emoteMapID map[int32]*EmoteVersionRange
spellMap map[string]*EmoteVersionRange
spellMapID map[int32]*EmoteVersionRange
mutex sync.RWMutex
}
// NewVisualStates creates a new visual states manager
func NewVisualStates() *VisualStates {
return &VisualStates{
visualStateMap: make(map[string]*VisualState),
emoteMap: make(map[string]*EmoteVersionRange),
emoteMapID: make(map[int32]*EmoteVersionRange),
spellMap: make(map[string]*EmoteVersionRange),
spellMapID: make(map[int32]*EmoteVersionRange),
}
}
// InsertVisualState adds a visual state
func (vs *VisualStates) InsertVisualState(state *VisualState) {
vs.mutex.Lock()
defer vs.mutex.Unlock()
vs.visualStateMap[state.name] = state
}
// FindVisualState finds a visual state by name
func (vs *VisualStates) FindVisualState(name string) *VisualState {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
return vs.visualStateMap[name]
}
// InsertEmoteRange adds an emote range
func (vs *VisualStates) InsertEmoteRange(emote *EmoteVersionRange, animationID int32) {
vs.mutex.Lock()
defer vs.mutex.Unlock()
vs.emoteMap[emote.name] = emote
vs.emoteMapID[animationID] = emote
}
// FindEmoteRange finds an emote range by name
func (vs *VisualStates) FindEmoteRange(name string) *EmoteVersionRange {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
return vs.emoteMap[name]
}
// FindEmote finds an emote by name and version
func (vs *VisualStates) FindEmote(name string, version int32) *Emote {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
if emoteRange, exists := vs.emoteMap[name]; exists {
return emoteRange.FindEmoteByVersion(version)
}
return nil
}
// FindEmoteRangeByID finds an emote range by ID
func (vs *VisualStates) FindEmoteRangeByID(id int32) *EmoteVersionRange {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
return vs.emoteMapID[id]
}
// FindEmoteByID finds an emote by visual ID and version
func (vs *VisualStates) FindEmoteByID(visualID int32, version int32) *Emote {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
if emoteRange, exists := vs.emoteMapID[visualID]; exists {
return emoteRange.FindEmoteByVersion(version)
}
return nil
}
// InsertSpellVisualRange adds a spell visual range
func (vs *VisualStates) InsertSpellVisualRange(emote *EmoteVersionRange, spellVisualID int32) {
vs.mutex.Lock()
defer vs.mutex.Unlock()
vs.spellMap[emote.name] = emote
vs.spellMapID[spellVisualID] = emote
}
// FindSpellVisualRange finds a spell visual range by name
func (vs *VisualStates) FindSpellVisualRange(name string) *EmoteVersionRange {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
return vs.spellMap[name]
}
// FindSpellVisualRangeByID finds a spell visual range by ID
func (vs *VisualStates) FindSpellVisualRangeByID(id int32) *EmoteVersionRange {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
return vs.spellMapID[id]
}
// FindSpellVisual finds a spell visual by name and version
func (vs *VisualStates) FindSpellVisual(name string, version int32) *Emote {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
if spellRange, exists := vs.spellMap[name]; exists {
return spellRange.FindEmoteByVersion(version)
}
return nil
}
// FindSpellVisualByID finds a spell visual by ID and version
func (vs *VisualStates) FindSpellVisualByID(visualID int32, version int32) *Emote {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
if spellRange, exists := vs.spellMapID[visualID]; exists {
return spellRange.FindEmoteByVersion(version)
}
return nil
}
// ClearVisualStates clears all visual states
func (vs *VisualStates) ClearVisualStates() {
vs.mutex.Lock()
defer vs.mutex.Unlock()
vs.visualStateMap = make(map[string]*VisualState)
}
// ClearEmotes clears all emotes
func (vs *VisualStates) ClearEmotes() {
vs.mutex.Lock()
defer vs.mutex.Unlock()
vs.emoteMap = make(map[string]*EmoteVersionRange)
vs.emoteMapID = make(map[int32]*EmoteVersionRange)
}
// ClearSpellVisuals clears all spell visuals
func (vs *VisualStates) ClearSpellVisuals() {
vs.mutex.Lock()
defer vs.mutex.Unlock()
vs.spellMap = make(map[string]*EmoteVersionRange)
vs.spellMapID = make(map[int32]*EmoteVersionRange)
}
// Reset clears all data
func (vs *VisualStates) Reset() {
vs.ClearVisualStates()
vs.ClearEmotes()
vs.ClearSpellVisuals()
}
// GetEmoteList returns a list of all emote names
func (vs *VisualStates) GetEmoteList() []string {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
names := make([]string, 0, len(vs.emoteMap))
for name := range vs.emoteMap {
names = append(names, name)
}
return names
}
// GetVisualStateList returns a list of all visual state names
func (vs *VisualStates) GetVisualStateList() []string {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
names := make([]string, 0, len(vs.visualStateMap))
for name := range vs.visualStateMap {
names = append(names, name)
}
return names
}
// FindEmoteByPartialName finds emotes that contain the partial name
func (vs *VisualStates) FindEmoteByPartialName(partial string) []*EmoteVersionRange {
vs.mutex.RLock()
defer vs.mutex.RUnlock()
partial = strings.ToLower(partial)
results := make([]*EmoteVersionRange, 0)
for name, emote := range vs.emoteMap {
if strings.Contains(strings.ToLower(name), partial) {
results = append(results, emote)
}
}
return results
}