432 lines
12 KiB
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
|
|
}
|