273 lines
7.6 KiB
Go
273 lines
7.6 KiB
Go
package sign
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Copy creates a deep copy of the sign with size randomization
|
|
func (s *Sign) Copy() *Sign {
|
|
newSign := NewSign()
|
|
|
|
// Copy spawn data
|
|
if s.Spawn != nil {
|
|
// Copy basic spawn properties that exist
|
|
newSign.Spawn.SetSize(s.Spawn.GetSize())
|
|
newSign.Spawn.SetDatabaseID(s.Spawn.GetDatabaseID())
|
|
newSign.Spawn.SetName(s.Spawn.GetName())
|
|
newSign.Spawn.SetLevel(s.Spawn.GetLevel())
|
|
newSign.Spawn.SetX(s.Spawn.GetX())
|
|
newSign.Spawn.SetY(s.Spawn.GetY(), false)
|
|
newSign.Spawn.SetZ(s.Spawn.GetZ())
|
|
newSign.Spawn.SetSpawnType(s.Spawn.GetSpawnType())
|
|
newSign.Spawn.SetFactionID(s.Spawn.GetFactionID())
|
|
|
|
// TODO: Copy appearance data when spawn system is fully integrated
|
|
// TODO: Copy command lists when command system is integrated
|
|
// TODO: Copy transporter ID, sounds, loot properties, etc.
|
|
}
|
|
|
|
// Copy sign-specific properties
|
|
newSign.widgetID = s.widgetID
|
|
newSign.widgetX = s.widgetX
|
|
newSign.widgetY = s.widgetY
|
|
newSign.widgetZ = s.widgetZ
|
|
newSign.signType = s.signType
|
|
newSign.title = s.title
|
|
newSign.description = s.description
|
|
newSign.language = s.language
|
|
newSign.zoneX = s.zoneX
|
|
newSign.zoneY = s.zoneY
|
|
newSign.zoneZ = s.zoneZ
|
|
newSign.zoneHeading = s.zoneHeading
|
|
newSign.zoneID = s.zoneID
|
|
newSign.signDistance = s.signDistance
|
|
newSign.includeLocation = s.includeLocation
|
|
newSign.includeHeading = s.includeHeading
|
|
|
|
return newSign
|
|
}
|
|
|
|
// Serialize creates a packet for sending the sign to a client
|
|
func (s *Sign) Serialize(player Player, version int16) ([]byte, error) {
|
|
// TODO: Implement serialization when spawn system supports it
|
|
// Delegate to spawn serialization
|
|
// if s.Spawn != nil {
|
|
// return s.Spawn.Serialize(player, version)
|
|
// }
|
|
|
|
return nil, fmt.Errorf("sign serialization not yet implemented")
|
|
}
|
|
|
|
// HandleUse processes player interaction with the sign
|
|
func (s *Sign) HandleUse(client Client, command string) error {
|
|
if client == nil {
|
|
return fmt.Errorf("client is nil")
|
|
}
|
|
|
|
player := client.GetPlayer()
|
|
if player == nil {
|
|
return fmt.Errorf("player is nil")
|
|
}
|
|
|
|
// Check quest requirements if this is from a client (not script)
|
|
if !s.meetsQuestRequirements(client) {
|
|
return nil // Silently fail if quest requirements not met
|
|
}
|
|
|
|
// Handle transporter functionality first
|
|
// TODO: Enable when transporter system is implemented in spawn
|
|
// if s.Spawn != nil && s.Spawn.GetTransporterID() > 0 {
|
|
// return s.handleTransporter(client)
|
|
// }
|
|
|
|
// Handle zone transport signs
|
|
if s.signType == SignTypeZone && s.zoneID > 0 {
|
|
return s.handleZoneTransport(client)
|
|
}
|
|
|
|
// Handle entity commands
|
|
if len(command) > 0 {
|
|
return s.handleEntityCommand(client, command)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// meetsQuestRequirements checks if the player meets quest requirements to use the sign
|
|
func (s *Sign) meetsQuestRequirements(client Client) bool {
|
|
// This is a placeholder implementation
|
|
// In the full implementation, this would check:
|
|
// - MeetsSpawnAccessRequirements(client.GetPlayer())
|
|
// - GetQuestsRequiredOverride() flags
|
|
// - appearance.show_command_icon
|
|
|
|
// For now, assume all requirements are met
|
|
return true
|
|
}
|
|
|
|
// handleTransporter processes transporter functionality
|
|
// TODO: Enable when transporter system is implemented in spawn
|
|
func (s *Sign) handleTransporter(client Client) error {
|
|
// Placeholder implementation for transporter functionality
|
|
// This will be enabled when the spawn system supports transporters
|
|
return fmt.Errorf("transporter system not yet implemented")
|
|
}
|
|
|
|
// handleZoneTransport processes zone transport functionality
|
|
func (s *Sign) handleZoneTransport(client Client) error {
|
|
player := client.GetPlayer()
|
|
|
|
// Check distance if sign has distance requirement
|
|
if s.signDistance > 0 {
|
|
distance := player.GetDistance(s.Spawn)
|
|
if distance > s.signDistance {
|
|
client.SimpleMessage(ChannelColorYellow, "You are too far away!")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Get zone name from database
|
|
zoneName, err := client.GetDatabase().GetZoneName(s.zoneID)
|
|
if err != nil || len(zoneName) == 0 {
|
|
client.Message(ChannelColorYellow, "Unable to find zone with ID: %d", s.zoneID)
|
|
return fmt.Errorf("zone not found: %d", s.zoneID)
|
|
}
|
|
|
|
// Check zone access
|
|
if !client.CheckZoneAccess(zoneName) {
|
|
return nil // Access denied (client handles message)
|
|
}
|
|
|
|
// Set coordinates if sign has valid zone coordinates
|
|
useZoneDefaults := !s.HasZoneCoordinates()
|
|
|
|
if !useZoneDefaults {
|
|
player.SetX(s.zoneX)
|
|
player.SetY(s.zoneY)
|
|
player.SetZ(s.zoneZ)
|
|
player.SetHeading(s.zoneHeading)
|
|
} else {
|
|
client.SimpleMessage(ChannelColorYellow, "Invalid zone in coords, taking you to a safe point.")
|
|
}
|
|
|
|
// Try instanced zone first, then regular zone
|
|
if !client.TryZoneInstance(s.zoneID, useZoneDefaults) {
|
|
return client.Zone(zoneName, useZoneDefaults)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// handleEntityCommand processes entity commands
|
|
func (s *Sign) handleEntityCommand(client Client, command string) error {
|
|
if s.Spawn == nil {
|
|
return fmt.Errorf("spawn is nil")
|
|
}
|
|
|
|
// TODO: Implement entity command finding when spawn system supports it
|
|
// entityCommand := s.Spawn.FindEntityCommand(command)
|
|
// For now, handle mark command directly
|
|
if strings.ToLower(command) == "mark" {
|
|
return s.handleMarkCommand(client)
|
|
}
|
|
|
|
// TODO: Process other entity commands when system is implemented
|
|
// zone := client.GetCurrentZone()
|
|
// if zone == nil {
|
|
// return fmt.Errorf("player not in zone")
|
|
// }
|
|
//
|
|
// player := client.GetPlayer()
|
|
// target := player.GetTarget()
|
|
//
|
|
// return zone.ProcessEntityCommand(entityCommand, player, target)
|
|
|
|
return nil // Command not handled
|
|
}
|
|
|
|
// handleMarkCommand processes the mark command for marking signs
|
|
func (s *Sign) handleMarkCommand(client Client) error {
|
|
charID := client.GetCharacterID()
|
|
charName, err := client.GetDatabase().GetCharacterName(charID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get character name: %w", err)
|
|
}
|
|
|
|
return client.GetDatabase().SaveSignMark(charID, s.widgetID, charName, client)
|
|
}
|
|
|
|
// GetDisplayText returns the formatted display text for the sign
|
|
func (s *Sign) GetDisplayText() string {
|
|
var text strings.Builder
|
|
|
|
if s.HasTitle() {
|
|
text.WriteString(s.title)
|
|
}
|
|
|
|
if s.HasDescription() {
|
|
if text.Len() > 0 {
|
|
text.WriteByte('\n')
|
|
}
|
|
text.WriteString(s.description)
|
|
}
|
|
|
|
// Add location information if requested
|
|
if s.includeLocation && s.HasZoneCoordinates() {
|
|
if text.Len() > 0 {
|
|
text.WriteByte('\n')
|
|
}
|
|
text.WriteString(fmt.Sprintf("Location: %.2f, %.2f, %.2f", s.zoneX, s.zoneY, s.zoneZ))
|
|
}
|
|
|
|
// Add heading information if requested
|
|
if s.includeHeading && s.zoneHeading != 0 {
|
|
if text.Len() > 0 {
|
|
text.WriteByte('\n')
|
|
}
|
|
text.WriteString(fmt.Sprintf("Heading: %.2f", s.zoneHeading))
|
|
}
|
|
|
|
return text.String()
|
|
}
|
|
|
|
// Validate checks if the sign configuration is valid
|
|
func (s *Sign) Validate() []string {
|
|
var issues []string
|
|
|
|
if s.Spawn == nil {
|
|
issues = append(issues, "Sign has no spawn data")
|
|
return issues
|
|
}
|
|
|
|
if s.widgetID == 0 {
|
|
issues = append(issues, "Sign has no widget ID")
|
|
}
|
|
|
|
if len(s.title) > MaxSignTitleLength {
|
|
issues = append(issues, fmt.Sprintf("Sign title too long: %d > %d", len(s.title), MaxSignTitleLength))
|
|
}
|
|
|
|
if len(s.description) > MaxSignDescriptionLength {
|
|
issues = append(issues, fmt.Sprintf("Sign description too long: %d > %d", len(s.description), MaxSignDescriptionLength))
|
|
}
|
|
|
|
if s.signType == SignTypeZone {
|
|
if s.zoneID == 0 {
|
|
issues = append(issues, "Zone sign has no zone ID")
|
|
}
|
|
|
|
if s.signDistance < 0 {
|
|
issues = append(issues, "Sign distance cannot be negative")
|
|
}
|
|
}
|
|
|
|
return issues
|
|
}
|
|
|
|
// IsValid returns true if the sign configuration is valid
|
|
func (s *Sign) IsValid() bool {
|
|
issues := s.Validate()
|
|
return len(issues) == 0
|
|
}
|