379 lines
9.3 KiB
Go
379 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
|
|
}
|