feat: add combo skill system with 5 combos
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
91
combat/combo.go
Normal file
91
combat/combo.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package combat
|
||||
|
||||
import "github.com/tolelom/catacombs/entity"
|
||||
|
||||
type ComboAction struct {
|
||||
Class entity.Class
|
||||
ActionType string // "attack", "skill", "item"
|
||||
}
|
||||
|
||||
type ComboEffect struct {
|
||||
DamageMultiplier float64 // multiplied onto each AttackIntent.Multiplier
|
||||
BonusDamage int // added to each AttackIntent.PlayerATK
|
||||
HealAll int // heal all players after resolution
|
||||
Message string // shown in combat log
|
||||
}
|
||||
|
||||
type ComboDef struct {
|
||||
Name string
|
||||
Required []ComboAction
|
||||
Effect ComboEffect
|
||||
}
|
||||
|
||||
var comboDefs = []ComboDef{
|
||||
{
|
||||
Name: "Ice Shatter",
|
||||
Required: []ComboAction{
|
||||
{Class: entity.ClassMage, ActionType: "skill"},
|
||||
{Class: entity.ClassWarrior, ActionType: "attack"},
|
||||
},
|
||||
Effect: ComboEffect{DamageMultiplier: 1.5, Message: "💥 ICE SHATTER! Frozen enemies shatter!"},
|
||||
},
|
||||
{
|
||||
Name: "Holy Assault",
|
||||
Required: []ComboAction{
|
||||
{Class: entity.ClassHealer, ActionType: "skill"},
|
||||
{Class: entity.ClassWarrior, ActionType: "attack"},
|
||||
},
|
||||
Effect: ComboEffect{DamageMultiplier: 1.3, HealAll: 10, Message: "✨ HOLY ASSAULT! Blessed strikes heal the party!"},
|
||||
},
|
||||
{
|
||||
Name: "Shadow Strike",
|
||||
Required: []ComboAction{
|
||||
{Class: entity.ClassRogue, ActionType: "skill"},
|
||||
{Class: entity.ClassMage, ActionType: "attack"},
|
||||
},
|
||||
Effect: ComboEffect{DamageMultiplier: 1.4, Message: "🗡️ SHADOW STRIKE! Magical shadows amplify the attack!"},
|
||||
},
|
||||
{
|
||||
Name: "Full Assault",
|
||||
Required: []ComboAction{
|
||||
{Class: entity.ClassWarrior, ActionType: "attack"},
|
||||
{Class: entity.ClassMage, ActionType: "attack"},
|
||||
{Class: entity.ClassRogue, ActionType: "attack"},
|
||||
},
|
||||
Effect: ComboEffect{DamageMultiplier: 1.3, BonusDamage: 5, Message: "⚔️ FULL ASSAULT! Combined attack overwhelms!"},
|
||||
},
|
||||
{
|
||||
Name: "Restoration",
|
||||
Required: []ComboAction{
|
||||
{Class: entity.ClassHealer, ActionType: "skill"},
|
||||
{Class: entity.ClassRogue, ActionType: "item"},
|
||||
},
|
||||
Effect: ComboEffect{HealAll: 20, Message: "💚 RESTORATION! Combined healing surges!"},
|
||||
},
|
||||
}
|
||||
|
||||
func DetectCombos(actions map[string]ComboAction) []ComboDef {
|
||||
var triggered []ComboDef
|
||||
for _, combo := range comboDefs {
|
||||
if matchesCombo(combo.Required, actions) {
|
||||
triggered = append(triggered, combo)
|
||||
}
|
||||
}
|
||||
return triggered
|
||||
}
|
||||
|
||||
func matchesCombo(required []ComboAction, actions map[string]ComboAction) bool {
|
||||
for _, req := range required {
|
||||
found := false
|
||||
for _, act := range actions {
|
||||
if act.Class == req.Class && act.ActionType == req.ActionType {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
45
combat/combo_test.go
Normal file
45
combat/combo_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package combat
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tolelom/catacombs/entity"
|
||||
)
|
||||
|
||||
func TestDetectCombo_IceShatter(t *testing.T) {
|
||||
actions := map[string]ComboAction{
|
||||
"mage": {Class: entity.ClassMage, ActionType: "skill"},
|
||||
"warrior": {Class: entity.ClassWarrior, ActionType: "attack"},
|
||||
}
|
||||
combos := DetectCombos(actions)
|
||||
if len(combos) == 0 {
|
||||
t.Fatal("expected Ice Shatter combo")
|
||||
}
|
||||
if combos[0].Name != "Ice Shatter" {
|
||||
t.Errorf("expected 'Ice Shatter', got %q", combos[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectCombo_NoMatch(t *testing.T) {
|
||||
actions := map[string]ComboAction{
|
||||
"w1": {Class: entity.ClassWarrior, ActionType: "attack"},
|
||||
"w2": {Class: entity.ClassWarrior, ActionType: "attack"},
|
||||
}
|
||||
combos := DetectCombos(actions)
|
||||
if len(combos) != 0 {
|
||||
t.Errorf("expected no combos, got %d", len(combos))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectCombo_Multiple(t *testing.T) {
|
||||
// Healer skill + Warrior attack + Rogue item → Holy Assault + Restoration
|
||||
actions := map[string]ComboAction{
|
||||
"healer": {Class: entity.ClassHealer, ActionType: "skill"},
|
||||
"warrior": {Class: entity.ClassWarrior, ActionType: "attack"},
|
||||
"rogue": {Class: entity.ClassRogue, ActionType: "item"},
|
||||
}
|
||||
combos := DetectCombos(actions)
|
||||
if len(combos) != 2 {
|
||||
t.Errorf("expected 2 combos, got %d", len(combos))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user