430 lines
11 KiB
Go
430 lines
11 KiB
Go
package quests
|
|
|
|
import "fmt"
|
|
|
|
// Reward management methods for Quest
|
|
|
|
// AddRewardCoins adds coin rewards
|
|
func (q *Quest) AddRewardCoins(copper, silver, gold, platinum int32) {
|
|
q.RewardCoins = int64(copper) + int64(silver)*100 + int64(gold)*10000 + int64(platinum)*1000000
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// AddRewardCoinsMax sets the maximum coin reward
|
|
func (q *Quest) AddRewardCoinsMax(coins int64) {
|
|
q.RewardCoinsMax = coins
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// AddRewardFaction adds a faction reward
|
|
func (q *Quest) AddRewardFaction(factionID int32, amount int32) {
|
|
q.RewardFactions[factionID] = amount
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// RemoveRewardFaction removes a faction reward
|
|
func (q *Quest) RemoveRewardFaction(factionID int32) bool {
|
|
if _, exists := q.RewardFactions[factionID]; exists {
|
|
delete(q.RewardFactions, factionID)
|
|
q.SetSaveNeeded(true)
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// SetRewardStatus sets the status point reward
|
|
func (q *Quest) SetRewardStatus(amount int32) {
|
|
q.RewardStatus = amount
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// SetRewardComment sets the reward comment text
|
|
func (q *Quest) SetRewardComment(comment string) {
|
|
q.RewardComment = comment
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// SetRewardXP sets the experience point reward
|
|
func (q *Quest) SetRewardXP(xp int32) {
|
|
q.RewardExp = xp
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// SetRewardTSXP sets the tradeskill experience point reward
|
|
func (q *Quest) SetRewardTSXP(xp int32) {
|
|
q.RewardTSExp = xp
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// SetGeneratedCoin sets the generated coin amount
|
|
func (q *Quest) SetGeneratedCoin(coin int64) {
|
|
q.GeneratedCoin = coin
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// GetCoinsReward returns the base coin reward
|
|
func (q *Quest) GetCoinsReward() int64 {
|
|
return q.RewardCoins
|
|
}
|
|
|
|
// GetCoinsRewardMax returns the maximum coin reward
|
|
func (q *Quest) GetCoinsRewardMax() int64 {
|
|
return q.RewardCoinsMax
|
|
}
|
|
|
|
// GetGeneratedCoin returns the generated coin amount
|
|
func (q *Quest) GetGeneratedCoin() int64 {
|
|
return q.GeneratedCoin
|
|
}
|
|
|
|
// GetExpReward returns the experience point reward
|
|
func (q *Quest) GetExpReward() int32 {
|
|
return q.RewardExp
|
|
}
|
|
|
|
// GetTSExpReward returns the tradeskill experience point reward
|
|
func (q *Quest) GetTSExpReward() int32 {
|
|
return q.RewardTSExp
|
|
}
|
|
|
|
// GetStatusPoints returns the status point reward
|
|
func (q *Quest) GetStatusPoints() int32 {
|
|
return q.RewardStatus
|
|
}
|
|
|
|
// GetRewardFactions returns the faction rewards map
|
|
func (q *Quest) GetRewardFactions() map[int32]int32 {
|
|
return q.RewardFactions
|
|
}
|
|
|
|
// GetRewardFaction returns the faction reward amount for a specific faction
|
|
func (q *Quest) GetRewardFaction(factionID int32) (int32, bool) {
|
|
amount, exists := q.RewardFactions[factionID]
|
|
return amount, exists
|
|
}
|
|
|
|
// HasRewardFaction checks if a faction has a reward
|
|
func (q *Quest) HasRewardFaction(factionID int32) bool {
|
|
_, exists := q.RewardFactions[factionID]
|
|
return exists
|
|
}
|
|
|
|
// ClearAllRewards removes all rewards
|
|
func (q *Quest) ClearAllRewards() {
|
|
q.RewardCoins = 0
|
|
q.RewardCoinsMax = 0
|
|
q.RewardStatus = 0
|
|
q.RewardComment = ""
|
|
q.RewardExp = 0
|
|
q.RewardTSExp = 0
|
|
q.GeneratedCoin = 0
|
|
q.RewardFactions = make(map[int32]int32)
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// Temporary reward methods
|
|
|
|
// SetStatusTmpReward sets the temporary status reward
|
|
func (q *Quest) SetStatusTmpReward(status int32) {
|
|
q.TmpRewardStatus = status
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// GetStatusTmpReward returns the temporary status reward
|
|
func (q *Quest) GetStatusTmpReward() int32 {
|
|
return q.TmpRewardStatus
|
|
}
|
|
|
|
// SetCoinTmpReward sets the temporary coin reward
|
|
func (q *Quest) SetCoinTmpReward(coins int64) {
|
|
q.TmpRewardCoins = coins
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// GetCoinTmpReward returns the temporary coin reward
|
|
func (q *Quest) GetCoinTmpReward() int64 {
|
|
return q.TmpRewardCoins
|
|
}
|
|
|
|
// ClearTmpRewards clears temporary rewards
|
|
func (q *Quest) ClearTmpRewards() {
|
|
q.TmpRewardStatus = 0
|
|
q.TmpRewardCoins = 0
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// Status earning methods
|
|
|
|
// SetStatusToEarnMin sets the minimum status to earn
|
|
func (q *Quest) SetStatusToEarnMin(value int32) {
|
|
q.StatusToEarnMin = value
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// GetStatusToEarnMin returns the minimum status to earn
|
|
func (q *Quest) GetStatusToEarnMin() int32 {
|
|
return q.StatusToEarnMin
|
|
}
|
|
|
|
// SetStatusToEarnMax sets the maximum status to earn
|
|
func (q *Quest) SetStatusToEarnMax(value int32) {
|
|
q.StatusToEarnMax = value
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// GetStatusToEarnMax returns the maximum status to earn
|
|
func (q *Quest) GetStatusToEarnMax() int32 {
|
|
return q.StatusToEarnMax
|
|
}
|
|
|
|
// SetStatusEarned sets the quest status earned
|
|
func (q *Quest) SetStatusEarned(status int32) {
|
|
q.Status = status
|
|
q.SetSaveNeeded(true)
|
|
}
|
|
|
|
// GetStatusEarned returns the quest status earned
|
|
func (q *Quest) GetStatusEarned() int32 {
|
|
return q.Status
|
|
}
|
|
|
|
// Reward calculation methods
|
|
|
|
// CalculateCoinsReward calculates the actual coin reward based on level and other factors
|
|
func (q *Quest) CalculateCoinsReward(playerLevel int8) int64 {
|
|
baseReward := q.RewardCoins
|
|
if baseReward <= 0 {
|
|
return 0
|
|
}
|
|
|
|
// Use generated coin if set
|
|
if q.GeneratedCoin > 0 {
|
|
return q.GeneratedCoin
|
|
}
|
|
|
|
// Level-based coin scaling (simplified version)
|
|
levelMultiplier := float64(playerLevel) / float64(q.Level)
|
|
if levelMultiplier > 1.5 {
|
|
levelMultiplier = 1.5 // Cap the multiplier
|
|
} else if levelMultiplier < 0.5 {
|
|
levelMultiplier = 0.5 // Floor the multiplier
|
|
}
|
|
|
|
calculatedReward := int64(float64(baseReward) * levelMultiplier)
|
|
|
|
// Apply max reward cap if set
|
|
if q.RewardCoinsMax > 0 && calculatedReward > q.RewardCoinsMax {
|
|
calculatedReward = q.RewardCoinsMax
|
|
}
|
|
|
|
return calculatedReward
|
|
}
|
|
|
|
// CalculateExpReward calculates the actual experience reward based on level and other factors
|
|
func (q *Quest) CalculateExpReward(playerLevel int8) int32 {
|
|
baseReward := q.RewardExp
|
|
if baseReward <= 0 {
|
|
return 0
|
|
}
|
|
|
|
// Level-based experience scaling
|
|
if playerLevel > q.Level {
|
|
// Reduced XP for overleveled players
|
|
levelDiff := playerLevel - q.Level
|
|
reduction := float64(levelDiff) * 0.1
|
|
if reduction > 0.8 {
|
|
reduction = 0.8 // Max 80% reduction
|
|
}
|
|
return int32(float64(baseReward) * (1.0 - reduction))
|
|
} else if playerLevel < q.Level {
|
|
// Bonus XP for underleveled players (small bonus)
|
|
levelDiff := q.Level - playerLevel
|
|
bonus := float64(levelDiff) * 0.05
|
|
if bonus > 0.25 {
|
|
bonus = 0.25 // Max 25% bonus
|
|
}
|
|
return int32(float64(baseReward) * (1.0 + bonus))
|
|
}
|
|
|
|
return baseReward
|
|
}
|
|
|
|
// CalculateTSExpReward calculates the actual tradeskill experience reward
|
|
func (q *Quest) CalculateTSExpReward(playerTSLevel int8) int32 {
|
|
baseReward := q.RewardTSExp
|
|
if baseReward <= 0 {
|
|
return 0
|
|
}
|
|
|
|
// Similar scaling as regular XP but for tradeskill level
|
|
if playerTSLevel > q.Level {
|
|
levelDiff := playerTSLevel - q.Level
|
|
reduction := float64(levelDiff) * 0.1
|
|
if reduction > 0.8 {
|
|
reduction = 0.8
|
|
}
|
|
return int32(float64(baseReward) * (1.0 - reduction))
|
|
} else if playerTSLevel < q.Level {
|
|
levelDiff := q.Level - playerTSLevel
|
|
bonus := float64(levelDiff) * 0.05
|
|
if bonus > 0.25 {
|
|
bonus = 0.25
|
|
}
|
|
return int32(float64(baseReward) * (1.0 + bonus))
|
|
}
|
|
|
|
return baseReward
|
|
}
|
|
|
|
// CalculateStatusReward calculates the actual status reward
|
|
func (q *Quest) CalculateStatusReward() int32 {
|
|
// Use status earned if set, otherwise use base reward status
|
|
if q.Status > 0 {
|
|
return q.Status
|
|
}
|
|
return q.RewardStatus
|
|
}
|
|
|
|
// Reward validation methods
|
|
|
|
// ValidateRewards validates all quest rewards
|
|
func (q *Quest) ValidateRewards() error {
|
|
// Validate coin rewards
|
|
if q.RewardCoins < 0 {
|
|
return fmt.Errorf("reward coins cannot be negative")
|
|
}
|
|
|
|
if q.RewardCoinsMax < 0 {
|
|
return fmt.Errorf("reward coins max cannot be negative")
|
|
}
|
|
|
|
if q.RewardCoinsMax > 0 && q.RewardCoinsMax < q.RewardCoins {
|
|
return fmt.Errorf("reward coins max cannot be less than base reward coins")
|
|
}
|
|
|
|
// Validate experience rewards
|
|
if q.RewardExp < 0 {
|
|
return fmt.Errorf("reward experience cannot be negative")
|
|
}
|
|
|
|
if q.RewardTSExp < 0 {
|
|
return fmt.Errorf("reward tradeskill experience cannot be negative")
|
|
}
|
|
|
|
// Validate status rewards
|
|
if q.RewardStatus < 0 {
|
|
return fmt.Errorf("reward status cannot be negative")
|
|
}
|
|
|
|
if q.Status < 0 {
|
|
return fmt.Errorf("status earned cannot be negative")
|
|
}
|
|
|
|
// Validate temporary rewards
|
|
if q.TmpRewardCoins < 0 {
|
|
return fmt.Errorf("temporary reward coins cannot be negative")
|
|
}
|
|
|
|
if q.TmpRewardStatus < 0 {
|
|
return fmt.Errorf("temporary reward status cannot be negative")
|
|
}
|
|
|
|
// Validate status to earn ranges
|
|
if q.StatusToEarnMin < 0 {
|
|
return fmt.Errorf("status to earn min cannot be negative")
|
|
}
|
|
|
|
if q.StatusToEarnMax < 0 {
|
|
return fmt.Errorf("status to earn max cannot be negative")
|
|
}
|
|
|
|
if q.StatusToEarnMax > 0 && q.StatusToEarnMax < q.StatusToEarnMin {
|
|
return fmt.Errorf("status to earn max cannot be less than min")
|
|
}
|
|
|
|
// Validate faction rewards
|
|
for factionID, amount := range q.RewardFactions {
|
|
if factionID <= 0 {
|
|
return fmt.Errorf("invalid faction ID in rewards: %d", factionID)
|
|
}
|
|
|
|
// Faction amounts can be negative (reputation loss)
|
|
if amount < -50000 || amount > 50000 {
|
|
return fmt.Errorf("faction reward amount out of range for faction %d: %d", factionID, amount)
|
|
}
|
|
}
|
|
|
|
// Validate reward comment length
|
|
if len(q.RewardComment) > MaxCompleteActionLength {
|
|
return fmt.Errorf("reward comment too long (max %d)", MaxCompleteActionLength)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Helper methods for coin conversion
|
|
|
|
// ConvertCoinsToComponents breaks down coins into copper, silver, gold, platinum
|
|
func ConvertCoinsToComponents(totalCoins int64) (copper, silver, gold, platinum int32) {
|
|
platinum = int32(totalCoins / 1000000)
|
|
remaining := totalCoins % 1000000
|
|
|
|
gold = int32(remaining / 10000)
|
|
remaining = remaining % 10000
|
|
|
|
silver = int32(remaining / 100)
|
|
copper = int32(remaining % 100)
|
|
|
|
return copper, silver, gold, platinum
|
|
}
|
|
|
|
// ConvertComponentsToCoins converts individual coin components to total coins
|
|
func ConvertComponentsToCoins(copper, silver, gold, platinum int32) int64 {
|
|
return int64(copper) + int64(silver)*100 + int64(gold)*10000 + int64(platinum)*1000000
|
|
}
|
|
|
|
// FormatCoinsString formats coins as a readable string
|
|
func FormatCoinsString(totalCoins int64) string {
|
|
if totalCoins == 0 {
|
|
return "0 copper"
|
|
}
|
|
|
|
copper, silver, gold, platinum := ConvertCoinsToComponents(totalCoins)
|
|
|
|
var parts []string
|
|
if platinum > 0 {
|
|
parts = append(parts, fmt.Sprintf("%d platinum", platinum))
|
|
}
|
|
if gold > 0 {
|
|
parts = append(parts, fmt.Sprintf("%d gold", gold))
|
|
}
|
|
if silver > 0 {
|
|
parts = append(parts, fmt.Sprintf("%d silver", silver))
|
|
}
|
|
if copper > 0 {
|
|
parts = append(parts, fmt.Sprintf("%d copper", copper))
|
|
}
|
|
|
|
if len(parts) == 0 {
|
|
return "0 copper"
|
|
}
|
|
|
|
if len(parts) == 1 {
|
|
return parts[0]
|
|
}
|
|
|
|
// Join with commas and "and" for the last item
|
|
result := ""
|
|
for i, part := range parts {
|
|
if i == 0 {
|
|
result = part
|
|
} else if i == len(parts)-1 {
|
|
result += " and " + part
|
|
} else {
|
|
result += ", " + part
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|