eq2go/internal/quests/actions.go

432 lines
12 KiB
Go

package quests
import (
"fmt"
"strings"
)
// Action management methods for Quest
// AddCompleteAction adds a completion action for a step
func (q *Quest) AddCompleteAction(stepID int32, action string) {
q.completeActionsMutex.Lock()
defer q.completeActionsMutex.Unlock()
q.CompleteActions[stepID] = action
q.SetSaveNeeded(true)
}
// AddProgressAction adds a progress action for a step
func (q *Quest) AddProgressAction(stepID int32, action string) {
q.progressActionsMutex.Lock()
defer q.progressActionsMutex.Unlock()
q.ProgressActions[stepID] = action
q.SetSaveNeeded(true)
}
// AddFailedAction adds a failure action for a step
func (q *Quest) AddFailedAction(stepID int32, action string) {
q.failedActionsMutex.Lock()
defer q.failedActionsMutex.Unlock()
q.FailedActions[stepID] = action
q.SetSaveNeeded(true)
}
// RemoveCompleteAction removes a completion action for a step
func (q *Quest) RemoveCompleteAction(stepID int32) bool {
q.completeActionsMutex.Lock()
defer q.completeActionsMutex.Unlock()
if _, exists := q.CompleteActions[stepID]; exists {
delete(q.CompleteActions, stepID)
q.SetSaveNeeded(true)
return true
}
return false
}
// RemoveProgressAction removes a progress action for a step
func (q *Quest) RemoveProgressAction(stepID int32) bool {
q.progressActionsMutex.Lock()
defer q.progressActionsMutex.Unlock()
if _, exists := q.ProgressActions[stepID]; exists {
delete(q.ProgressActions, stepID)
q.SetSaveNeeded(true)
return true
}
return false
}
// RemoveFailedAction removes a failure action for a step
func (q *Quest) RemoveFailedAction(stepID int32) bool {
q.failedActionsMutex.Lock()
defer q.failedActionsMutex.Unlock()
if _, exists := q.FailedActions[stepID]; exists {
delete(q.FailedActions, stepID)
q.SetSaveNeeded(true)
return true
}
return false
}
// GetCompleteAction returns the completion action for a step
func (q *Quest) GetCompleteAction(stepID int32) (string, bool) {
q.completeActionsMutex.RLock()
defer q.completeActionsMutex.RUnlock()
action, exists := q.CompleteActions[stepID]
return action, exists
}
// GetProgressAction returns the progress action for a step
func (q *Quest) GetProgressAction(stepID int32) (string, bool) {
q.progressActionsMutex.RLock()
defer q.progressActionsMutex.RUnlock()
action, exists := q.ProgressActions[stepID]
return action, exists
}
// GetFailedAction returns the failure action for a step
func (q *Quest) GetFailedAction(stepID int32) (string, bool) {
q.failedActionsMutex.RLock()
defer q.failedActionsMutex.RUnlock()
action, exists := q.FailedActions[stepID]
return action, exists
}
// HasCompleteAction checks if a step has a completion action
func (q *Quest) HasCompleteAction(stepID int32) bool {
q.completeActionsMutex.RLock()
defer q.completeActionsMutex.RUnlock()
_, exists := q.CompleteActions[stepID]
return exists
}
// HasProgressAction checks if a step has a progress action
func (q *Quest) HasProgressAction(stepID int32) bool {
q.progressActionsMutex.RLock()
defer q.progressActionsMutex.RUnlock()
_, exists := q.ProgressActions[stepID]
return exists
}
// HasFailedAction checks if a step has a failure action
func (q *Quest) HasFailedAction(stepID int32) bool {
q.failedActionsMutex.RLock()
defer q.failedActionsMutex.RUnlock()
_, exists := q.FailedActions[stepID]
return exists
}
// SetCompleteAction sets the general quest completion action
func (q *Quest) SetCompleteAction(action string) {
q.CompleteAction = action
q.SetSaveNeeded(true)
}
// GetQuestCompleteAction returns the general quest completion action
func (q *Quest) GetQuestCompleteAction() string {
return q.CompleteAction
}
// ClearAllActions removes all actions for all steps
func (q *Quest) ClearAllActions() {
q.completeActionsMutex.Lock()
q.CompleteActions = make(map[int32]string)
q.completeActionsMutex.Unlock()
q.progressActionsMutex.Lock()
q.ProgressActions = make(map[int32]string)
q.progressActionsMutex.Unlock()
q.failedActionsMutex.Lock()
q.FailedActions = make(map[int32]string)
q.failedActionsMutex.Unlock()
q.CompleteAction = ""
q.SetSaveNeeded(true)
}
// ClearStepActions removes all actions for a specific step
func (q *Quest) ClearStepActions(stepID int32) {
q.RemoveCompleteAction(stepID)
q.RemoveProgressAction(stepID)
q.RemoveFailedAction(stepID)
}
// GetAllCompleteActions returns a copy of all completion actions
func (q *Quest) GetAllCompleteActions() map[int32]string {
q.completeActionsMutex.RLock()
defer q.completeActionsMutex.RUnlock()
actions := make(map[int32]string)
for stepID, action := range q.CompleteActions {
actions[stepID] = action
}
return actions
}
// GetAllProgressActions returns a copy of all progress actions
func (q *Quest) GetAllProgressActions() map[int32]string {
q.progressActionsMutex.RLock()
defer q.progressActionsMutex.RUnlock()
actions := make(map[int32]string)
for stepID, action := range q.ProgressActions {
actions[stepID] = action
}
return actions
}
// GetAllFailedActions returns a copy of all failure actions
func (q *Quest) GetAllFailedActions() map[int32]string {
q.failedActionsMutex.RLock()
defer q.failedActionsMutex.RUnlock()
actions := make(map[int32]string)
for stepID, action := range q.FailedActions {
actions[stepID] = action
}
return actions
}
// ExecuteCompleteAction executes a completion action for a step
func (q *Quest) ExecuteCompleteAction(stepID int32) error {
action, exists := q.GetCompleteAction(stepID)
if !exists || action == "" {
return fmt.Errorf("no completion action for step %d", stepID)
}
// TODO: Execute Lua script with action
// This would integrate with the Lua interface when available
return q.executeLuaAction("complete", stepID, action)
}
// ExecuteProgressAction executes a progress action for a step
func (q *Quest) ExecuteProgressAction(stepID int32, progressAmount int32) error {
action, exists := q.GetProgressAction(stepID)
if !exists || action == "" {
return fmt.Errorf("no progress action for step %d", stepID)
}
// TODO: Execute Lua script with action and progress amount
return q.executeLuaActionWithProgress("progress", stepID, action, progressAmount)
}
// ExecuteFailedAction executes a failure action for a step
func (q *Quest) ExecuteFailedAction(stepID int32) error {
action, exists := q.GetFailedAction(stepID)
if !exists || action == "" {
return fmt.Errorf("no failure action for step %d", stepID)
}
// TODO: Execute Lua script with action
return q.executeLuaAction("failed", stepID, action)
}
// ExecuteQuestCompleteAction executes the general quest completion action
func (q *Quest) ExecuteQuestCompleteAction() error {
if q.CompleteAction == "" {
return nil // No action to execute
}
// TODO: Execute Lua script with quest completion action
return q.executeLuaAction("quest_complete", 0, q.CompleteAction)
}
// Placeholder methods for Lua integration (to be implemented when Lua interface is available)
// executeLuaAction executes a Lua action script
func (q *Quest) executeLuaAction(actionType string, stepID int32, action string) error {
// TODO: Implement Lua script execution
// This is a placeholder that would integrate with the Lua interface
// when it becomes available in the Go codebase
// For now, just log the action that would be executed
fmt.Printf("Quest %d: Would execute %s action for step %d: %s\n", q.ID, actionType, stepID, action)
return nil
}
// executeLuaActionWithProgress executes a Lua action script with progress information
func (q *Quest) executeLuaActionWithProgress(actionType string, stepID int32, action string, progress int32) error {
// TODO: Implement Lua script execution with progress parameter
// This is a placeholder that would integrate with the Lua interface
// For now, just log the action that would be executed
fmt.Printf("Quest %d: Would execute %s action for step %d with progress %d: %s\n", q.ID, actionType, stepID, progress, action)
return nil
}
// Action validation methods
// ValidateActions validates all quest actions
func (q *Quest) ValidateActions() error {
// Validate completion actions
q.completeActionsMutex.RLock()
for stepID, action := range q.CompleteActions {
if err := q.validateAction(stepID, action, "completion"); err != nil {
q.completeActionsMutex.RUnlock()
return err
}
}
q.completeActionsMutex.RUnlock()
// Validate progress actions
q.progressActionsMutex.RLock()
for stepID, action := range q.ProgressActions {
if err := q.validateAction(stepID, action, "progress"); err != nil {
q.progressActionsMutex.RUnlock()
return err
}
}
q.progressActionsMutex.RUnlock()
// Validate failure actions
q.failedActionsMutex.RLock()
for stepID, action := range q.FailedActions {
if err := q.validateAction(stepID, action, "failure"); err != nil {
q.failedActionsMutex.RUnlock()
return err
}
}
q.failedActionsMutex.RUnlock()
// Validate quest completion action
if q.CompleteAction != "" {
if err := q.validateAction(0, q.CompleteAction, "quest completion"); err != nil {
return err
}
}
return nil
}
// validateAction validates a single action
func (q *Quest) validateAction(stepID int32, action, actionType string) error {
if action == "" {
return fmt.Errorf("empty %s action for step %d", actionType, stepID)
}
if len(action) > MaxCompleteActionLength {
return fmt.Errorf("%s action too long for step %d (max %d)", actionType, stepID, MaxCompleteActionLength)
}
// Validate that the step exists (except for quest completion action)
if stepID > 0 {
if step := q.GetQuestStep(stepID); step == nil {
return fmt.Errorf("%s action references non-existent step %d", actionType, stepID)
}
}
// Basic Lua syntax validation (very basic check)
if err := q.validateLuaSyntax(action); err != nil {
return fmt.Errorf("invalid Lua syntax in %s action for step %d: %w", actionType, stepID, err)
}
return nil
}
// validateLuaSyntax performs basic Lua syntax validation
func (q *Quest) validateLuaSyntax(luaCode string) error {
// TODO: Implement proper Lua syntax validation
// This is a placeholder that does very basic checks
// Check for balanced parentheses, brackets, and braces
if err := q.checkBalancedDelimiters(luaCode); err != nil {
return err
}
// Check for obviously invalid syntax patterns
invalidPatterns := []string{
"--[[", // Unfinished multi-line comments
"function(", // Function without closing
}
for _, pattern := range invalidPatterns {
if strings.Contains(luaCode, pattern) {
// Do more thorough checking for these patterns
// This is just a basic check - real validation would be more sophisticated
}
}
return nil
}
// checkBalancedDelimiters checks if delimiters are balanced in Lua code
func (q *Quest) checkBalancedDelimiters(code string) error {
stack := make([]rune, 0)
pairs := map[rune]rune{
')': '(',
']': '[',
'}': '{',
}
inString := false
inComment := false
var stringChar rune
for i, char := range code {
// Handle string literals
if (char == '"' || char == '\'') && !inComment {
if !inString {
inString = true
stringChar = char
} else if char == stringChar {
inString = false
}
continue
}
// Skip everything inside strings
if inString {
continue
}
// Handle line comments
if i > 0 && code[i-1] == '-' && char == '-' && !inComment {
inComment = true
continue
}
// End line comment at newline
if inComment && char == '\n' {
inComment = false
continue
}
// Skip everything in comments
if inComment {
continue
}
// Check delimiters
switch char {
case '(', '[', '{':
stack = append(stack, char)
case ')', ']', '}':
if len(stack) == 0 {
return fmt.Errorf("unmatched closing delimiter '%c'", char)
}
expected := pairs[char]
if stack[len(stack)-1] != expected {
return fmt.Errorf("mismatched delimiter: expected '%c', got '%c'", expected, char)
}
stack = stack[:len(stack)-1]
}
}
if len(stack) > 0 {
return fmt.Errorf("unclosed delimiter '%c'", stack[len(stack)-1])
}
return nil
}