feat: add secret rooms and mini-bosses on floors 4/9/14/19
Add RoomSecret (5% chance) and RoomMiniBoss room types. Add 4 mini-boss monsters at 60% of boss stats (Guardian's Herald, Warden's Shadow, Overlord's Lieutenant, Archlich's Harbinger) with IsMiniBoss flag and boss pattern logic. Secret rooms grant double treasure. Mini-boss rooms are placed on floors 4/9/14/19 at room index 1. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -111,6 +111,14 @@ func GenerateFloor(floorNum int) *Floor {
|
||||
// Last room is boss
|
||||
rooms[len(rooms)-1].Type = RoomBoss
|
||||
|
||||
// On floors 4, 9, 14, 19: assign one room as mini-boss
|
||||
if floorNum == 4 || floorNum == 9 || floorNum == 14 || floorNum == 19 {
|
||||
// Pick the second room (index 1), or any non-first non-last room
|
||||
if len(rooms) > 2 {
|
||||
rooms[1].Type = RoomMiniBoss
|
||||
}
|
||||
}
|
||||
|
||||
// Carve rooms into tile map
|
||||
for _, room := range rooms {
|
||||
for dy := 0; dy < room.H; dy++ {
|
||||
|
||||
@@ -37,7 +37,49 @@ func TestRoomTypeProbability(t *testing.T) {
|
||||
}
|
||||
combatPct := float64(counts[RoomCombat]) / float64(n) * 100
|
||||
if combatPct < 40 || combatPct > 50 {
|
||||
t.Errorf("Combat room probability: got %.1f%%, want ~45%%", combatPct)
|
||||
t.Errorf("Combat room probability: got %.1f%%, want ~45%% (range 5-50)", combatPct)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecretRoomInRandomType(t *testing.T) {
|
||||
counts := make(map[RoomType]int)
|
||||
n := 10000
|
||||
for i := 0; i < n; i++ {
|
||||
counts[RandomRoomType()]++
|
||||
}
|
||||
secretPct := float64(counts[RoomSecret]) / float64(n) * 100
|
||||
if secretPct < 2 || secretPct > 8 {
|
||||
t.Errorf("Secret room probability: got %.1f%%, want ~5%%", secretPct)
|
||||
}
|
||||
// Verify RoomMiniBoss is never returned by RandomRoomType
|
||||
if counts[RoomMiniBoss] > 0 {
|
||||
t.Errorf("MiniBoss rooms should not be generated randomly, got %d", counts[RoomMiniBoss])
|
||||
}
|
||||
}
|
||||
|
||||
func TestMiniBossRoomPlacement(t *testing.T) {
|
||||
for _, floorNum := range []int{4, 9, 14, 19} {
|
||||
floor := GenerateFloor(floorNum)
|
||||
found := false
|
||||
for _, r := range floor.Rooms {
|
||||
if r.Type == RoomMiniBoss {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Floor %d should have a mini-boss room", floorNum)
|
||||
}
|
||||
}
|
||||
// Non-miniboss floors should not have mini-boss rooms
|
||||
for _, floorNum := range []int{1, 3, 5, 10} {
|
||||
floor := GenerateFloor(floorNum)
|
||||
for _, r := range floor.Rooms {
|
||||
if r.Type == RoomMiniBoss {
|
||||
t.Errorf("Floor %d should not have a mini-boss room", floorNum)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,12 @@ const (
|
||||
RoomEvent
|
||||
RoomEmpty
|
||||
RoomBoss
|
||||
RoomSecret
|
||||
RoomMiniBoss
|
||||
)
|
||||
|
||||
func (r RoomType) String() string {
|
||||
return [...]string{"Combat", "Treasure", "Shop", "Event", "Empty", "Boss"}[r]
|
||||
return [...]string{"Combat", "Treasure", "Shop", "Event", "Empty", "Boss", "Secret", "MiniBoss"}[r]
|
||||
}
|
||||
|
||||
type Tile int
|
||||
@@ -47,13 +49,15 @@ type Floor struct {
|
||||
func RandomRoomType() RoomType {
|
||||
r := rand.Float64() * 100
|
||||
switch {
|
||||
case r < 45:
|
||||
case r < 5:
|
||||
return RoomSecret
|
||||
case r < 50:
|
||||
return RoomCombat
|
||||
case r < 60:
|
||||
case r < 65:
|
||||
return RoomTreasure
|
||||
case r < 70:
|
||||
case r < 75:
|
||||
return RoomShop
|
||||
case r < 85:
|
||||
case r < 90:
|
||||
return RoomEvent
|
||||
default:
|
||||
return RoomEmpty
|
||||
|
||||
Reference in New Issue
Block a user