fix: game balance — gold scaling, solo DEF, floor-scaled items, healer targeting, AI fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -97,14 +97,17 @@ func MonsterAI(m *entity.Monster, players []*entity.Player, turnNumber int) (tar
|
||||
}
|
||||
if rand.Float64() < 0.3 {
|
||||
minHP := int(^uint(0) >> 1)
|
||||
minIdx := 0
|
||||
minIdx := -1
|
||||
for i, p := range players {
|
||||
if !p.IsDead() && p.HP < minHP {
|
||||
minHP = p.HP
|
||||
minIdx = i
|
||||
}
|
||||
}
|
||||
return minIdx, false
|
||||
if minIdx >= 0 {
|
||||
return minIdx, false
|
||||
}
|
||||
// Fall through to default targeting if no alive player found
|
||||
}
|
||||
for i, p := range players {
|
||||
if !p.IsDead() {
|
||||
|
||||
@@ -88,6 +88,7 @@ func (s *GameSession) spawnMonsters() {
|
||||
m.HP = 1
|
||||
}
|
||||
m.MaxHP = m.HP
|
||||
m.DEF = m.DEF / 2
|
||||
}
|
||||
s.state.Monsters[i] = m
|
||||
}
|
||||
@@ -111,21 +112,25 @@ func (s *GameSession) spawnBoss() {
|
||||
if s.state.SoloMode {
|
||||
boss.HP = boss.HP / 2
|
||||
boss.MaxHP = boss.HP
|
||||
boss.DEF = boss.DEF / 2
|
||||
}
|
||||
s.state.Monsters = []*entity.Monster{boss}
|
||||
}
|
||||
|
||||
func (s *GameSession) grantTreasure() {
|
||||
floor := s.state.FloorNum
|
||||
for _, p := range s.state.Players {
|
||||
if rand.Float64() < 0.5 {
|
||||
bonus := 3 + rand.Intn(6) + floor/3
|
||||
item := entity.Item{
|
||||
Name: "Iron Sword", Type: entity.ItemWeapon, Bonus: 3 + rand.Intn(6),
|
||||
Name: weaponName(floor), Type: entity.ItemWeapon, Bonus: bonus,
|
||||
}
|
||||
p.Inventory = append(p.Inventory, item)
|
||||
s.addLog(fmt.Sprintf("%s found %s (ATK+%d)", p.Name, item.Name, item.Bonus))
|
||||
} else {
|
||||
bonus := 2 + rand.Intn(4) + floor/4
|
||||
item := entity.Item{
|
||||
Name: "Iron Shield", Type: entity.ItemArmor, Bonus: 2 + rand.Intn(4),
|
||||
Name: armorName(floor), Type: entity.ItemArmor, Bonus: bonus,
|
||||
}
|
||||
p.Inventory = append(p.Inventory, item)
|
||||
s.addLog(fmt.Sprintf("%s found %s (DEF+%d)", p.Name, item.Name, item.Bonus))
|
||||
@@ -134,10 +139,48 @@ func (s *GameSession) grantTreasure() {
|
||||
}
|
||||
|
||||
func (s *GameSession) generateShopItems() {
|
||||
floor := s.state.FloorNum
|
||||
// Weapon bonus scales: base 3-8 + floor/3
|
||||
weaponBonus := 3 + rand.Intn(6) + floor/3
|
||||
// Armor bonus scales: base 2-5 + floor/4
|
||||
armorBonus := 2 + rand.Intn(4) + floor/4
|
||||
// Prices scale with power
|
||||
weaponPrice := 40 + weaponBonus*5
|
||||
armorPrice := 30 + armorBonus*5
|
||||
// Potion heals more on higher floors
|
||||
potionHeal := 30 + floor
|
||||
potionPrice := 20 + floor/2
|
||||
|
||||
s.state.ShopItems = []entity.Item{
|
||||
{Name: "HP Potion", Type: entity.ItemConsumable, Bonus: 30, Price: 20},
|
||||
{Name: "Iron Sword", Type: entity.ItemWeapon, Bonus: 3 + rand.Intn(6), Price: 40 + rand.Intn(41)},
|
||||
{Name: "Iron Shield", Type: entity.ItemArmor, Bonus: 2 + rand.Intn(4), Price: 30 + rand.Intn(31)},
|
||||
{Name: "HP Potion", Type: entity.ItemConsumable, Bonus: potionHeal, Price: potionPrice},
|
||||
{Name: weaponName(floor), Type: entity.ItemWeapon, Bonus: weaponBonus, Price: weaponPrice},
|
||||
{Name: armorName(floor), Type: entity.ItemArmor, Bonus: armorBonus, Price: armorPrice},
|
||||
}
|
||||
}
|
||||
|
||||
func weaponName(floor int) string {
|
||||
switch {
|
||||
case floor >= 15:
|
||||
return "Mythril Blade"
|
||||
case floor >= 10:
|
||||
return "Steel Sword"
|
||||
case floor >= 5:
|
||||
return "Bronze Sword"
|
||||
default:
|
||||
return "Iron Sword"
|
||||
}
|
||||
}
|
||||
|
||||
func armorName(floor int) string {
|
||||
switch {
|
||||
case floor >= 15:
|
||||
return "Mythril Shield"
|
||||
case floor >= 10:
|
||||
return "Steel Shield"
|
||||
case floor >= 5:
|
||||
return "Bronze Shield"
|
||||
default:
|
||||
return "Iron Shield"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
17
game/turn.go
17
game/turn.go
@@ -117,9 +117,19 @@ func (s *GameSession) resolvePlayerActions() {
|
||||
case entity.ClassHealer:
|
||||
targetIdx := action.TargetIdx
|
||||
if targetIdx < 0 || targetIdx >= len(s.state.Players) {
|
||||
targetIdx = 0 // heal self by default
|
||||
targetIdx = 0
|
||||
}
|
||||
target := s.state.Players[targetIdx]
|
||||
if target.IsDead() {
|
||||
// Find first alive player to heal instead
|
||||
for j, candidate := range s.state.Players {
|
||||
if !candidate.IsOut() {
|
||||
target = candidate
|
||||
targetIdx = j
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
before := target.HP
|
||||
target.Heal(30)
|
||||
s.addLog(fmt.Sprintf("%s healed %s for %d HP", p.Name, target.Name, target.HP-before))
|
||||
@@ -203,10 +213,7 @@ func (s *GameSession) resolvePlayerActions() {
|
||||
// Award gold only for monsters that JUST died this turn
|
||||
for i, m := range s.state.Monsters {
|
||||
if m.IsDead() && aliveBeforeTurn[i] {
|
||||
goldReward := 5 + s.state.FloorNum
|
||||
if goldReward > 15 {
|
||||
goldReward = 15
|
||||
}
|
||||
goldReward := 5 + s.state.FloorNum*2
|
||||
for _, p := range s.state.Players {
|
||||
if !p.IsOut() {
|
||||
bonus := 0
|
||||
|
||||
Reference in New Issue
Block a user