From be028e106ddaf11abd9758831cafa4f45ad60996 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Mon, 2 Sep 2024 19:59:30 -0500 Subject: [PATCH 01/55] Add rendomness in numer of items --- archetype/level.go | 50 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/archetype/level.go b/archetype/level.go index 028b2c2..5ad0f68 100644 --- a/archetype/level.go +++ b/archetype/level.go @@ -176,7 +176,7 @@ func seedRooms(world donburi.World, level *component.LevelData) { ) } else { CreateMonster(world, level, room) - addRandomPickupToRoom(world, room) + addRandomPickupsToRoom(world, level, room, 3) } } exitRoomIndex := engine.GetDiceRoll(len(level.Rooms) - 1) @@ -187,17 +187,43 @@ func seedRooms(world donburi.World, level *component.LevelData) { exitTile.TileType = component.STAIR_DOWN } -func addRandomPickupToRoom(world donburi.World, room engine.Rect) { - // TODO: add a random chance for an item to appear +func addRandomPickupsToRoom( + world donburi.World, + level *component.LevelData, + room engine.Rect, + generosity int, +) { // TODO: create a random distribution of items - // for now, we'll just put a single health potion in each room - width := room.X2 - room.X1 - 2 - height := room.Y2 - room.Y1 - 2 - offsetX := engine.GetRandomInt(width) - offsetY := engine.GetRandomInt(height) - potion := CreateNewConsumable(world, consumables.HealthPotion) - err := PlaceItemInWorld(potion, room.X1+offsetX+1, room.Y1+offsetY+1, true) - if err != nil { - logger.ErrorLogger.Panic("Failed to place consumable in the world") + // TODO: add a random chance for an item to appear + switch d10Roll := engine.GetDiceRoll(10); { + case d10Roll <= generosity: + width := room.X2 - room.X1 - 2 + height := room.Y2 - room.Y1 - 2 + for i := 0; i < d10Roll; i++ { + offsetX := engine.GetRandomInt(width) + offsetY := engine.GetRandomInt(height) + x := room.X1 + offsetX + 1 + y := room.Y1 + offsetY + 1 + tile := level.GetFromXY(x, y) + if tile.Blocked { + continue + } + spotTaken := false + ConsumableTag.Each(world, func(entry *donburi.Entry) { + position := component.Position.Get(entry) + if position.X == x && position.Y == y { + spotTaken = true + } + }) + if spotTaken { + continue + } + potion := CreateNewConsumable(world, consumables.HealthPotion) + err := PlaceItemInWorld(potion, x, y, true) + if err != nil { + logger.ErrorLogger.Panic("Failed to place consumable in the world") + } + } } + } From 1b9573f1da63c86c836dec5855987e111f288c66 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Mon, 2 Sep 2024 19:59:42 -0500 Subject: [PATCH 02/55] Refactor to randomize position --- archetype/monster.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/archetype/monster.go b/archetype/monster.go index c4baf2f..37444ac 100644 --- a/archetype/monster.go +++ b/archetype/monster.go @@ -13,6 +13,18 @@ import ( var MonsterTag = donburi.NewTag("monster") func CreateMonster(world donburi.World, level *component.LevelData, room engine.Rect) { + + innerRoomWidth := room.X2 - room.X1 - 2 + innerRoomHeight := room.Y2 - room.Y1 - 2 + offsetX := engine.GetRandomInt(innerRoomWidth) + offsetY := engine.GetRandomInt(innerRoomHeight) + startingX := room.X1 + offsetX + 1 + startingY := room.Y1 + offsetY + 1 + tile := level.GetFromXY(startingX, startingY) + if tile.Blocked { + return + } + monster := world.Entry(world.Create( MonsterTag, component.Position, @@ -29,7 +41,6 @@ func CreateMonster(world donburi.World, level *component.LevelData, room engine. )) // Set position - startingX, startingY := room.Center() position := component.PositionData{ X: startingX, Y: startingY, From 9f10b035c743539388a87faa13556f97115990f6 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 11:37:46 -0500 Subject: [PATCH 03/55] Improve asset loading --- items/armors/armors.go | 26 ++++++++-------------- items/consumables/consumables.go | 38 +++++++++++++++++++++++--------- items/weapons/weapons.go | 16 +++++--------- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/items/armors/armors.go b/items/armors/armors.go index 70905a5..4721a49 100644 --- a/items/armors/armors.go +++ b/items/armors/armors.go @@ -22,39 +22,31 @@ const ( PlateArmor ) -type armorList []armorData - -// MAKE SURE THAT THIS NUMBER MATCHES THE NUMBER OF ARMORS DEFINED! -var Data = make(armorList, 4) - -func init() { - Data[LinenShirt] = armorData{ +var Data = []armorData{ + { Name: "Linen Shirt", Defense: 1, ArmorClass: 1, Sprite: mustBeValidImage(assets.LinenShirt, "LinenShirt"), - } - - Data[PaddedArmor] = armorData{ + }, + { Name: "Padded Armor", Defense: 5, ArmorClass: 6, Sprite: mustBeValidImage(assets.PaddedArmor, "PaddedArmor"), - } - - Data[Bones] = armorData{ + }, + { Name: "Bone", Defense: 3, ArmorClass: 4, Sprite: mustBeValidImage(assets.Bones, "Bones"), - } - - Data[PlateArmor] = armorData{ + }, + { Name: "Plate Armor", Defense: 15, ArmorClass: 18, Sprite: mustBeValidImage(assets.PlateArmor, "PlateArmor"), - } + }, } func mustBeValidImage(image *ebiten.Image, name string) *ebiten.Image { diff --git a/items/consumables/consumables.go b/items/consumables/consumables.go index 8ca8d4c..7921318 100644 --- a/items/consumables/consumables.go +++ b/items/consumables/consumables.go @@ -12,25 +12,43 @@ type consumable struct { Sprite *ebiten.Image } -type consumablesList []*consumable - type ConsumablesId int const ( HealthPotion ConsumablesId = iota + GreatHealthPotion + RoyalHealthPotion + + Apple + Bread + Carrot + Cheese + Egg + Fish + Ham + Milk + Pear + Steak ) -// MAKE SURE THAT THIS NUMBER MATCHES THE NUMBER OF WEAPONS DEFINED! -var Data consumablesList = make(consumablesList, 1) - -func init() { - - Data[HealthPotion] = &consumable{ +var Data = []*consumable{ + { Name: "Health Potion", AmountHeal: 10, Sprite: mustBeValidImage(assets.WorldHealthPotion, "WorldHealthPotion"), - } - + }, + + { + Name: "Great Heath Potion", + AmountHeal: 20, + Sprite: mustBeValidImage(assets.WorldGreatHealthPotion, "WorldGreatHealthPotion"), + }, + + { + Name: "Royal Heath Potion", + AmountHeal: 40, + Sprite: mustBeValidImage(assets.WorldRoyalHealthPotion, "WorldRoyalHealthPotion"), + }, } func mustBeValidImage(image *ebiten.Image, name string) *ebiten.Image { diff --git a/items/weapons/weapons.go b/items/weapons/weapons.go index 78af3f8..e526ec4 100644 --- a/items/weapons/weapons.go +++ b/items/weapons/weapons.go @@ -15,8 +15,6 @@ type weaponData struct { Sprite *ebiten.Image } -type weaponList []weaponData - type WeaponId int const ( @@ -25,27 +23,23 @@ const ( ) // MAKE SURE THAT THIS NUMBER MATCHES THE NUMBER OF WEAPONS DEFINED! -var Data weaponList = make(weaponList, 2) - -func init() { - - Data[ShortSword] = weaponData{ +var Data = []weaponData{ + { Name: "Short Sword", ActionText: "swings a short sword at", MinimumDamage: 2, MaximumDamage: 6, ToHitBonus: 0, Sprite: mustBeValidImage(assets.ShortSword, "ShortSword"), - } - - Data[BattleAxe] = weaponData{ + }, + { Name: "Battle Axe", ActionText: "cleaves a battle axe at", MinimumDamage: 10, MaximumDamage: 20, ToHitBonus: 3, Sprite: mustBeValidImage(assets.BattleAxe, "BattleAxe"), - } + }, } func mustBeValidImage(image *ebiten.Image, name string) *ebiten.Image { From c64dfd4f2f575a0189681df6980279ff0b0b776a Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:02:06 -0500 Subject: [PATCH 04/55] Restructure package --- items/armors/armors.go | 57 ------------------------------ items/consumables/consumables.go | 59 -------------------------------- items/weapons/weapons.go | 50 --------------------------- 3 files changed, 166 deletions(-) delete mode 100644 items/armors/armors.go delete mode 100644 items/consumables/consumables.go delete mode 100644 items/weapons/weapons.go diff --git a/items/armors/armors.go b/items/armors/armors.go deleted file mode 100644 index 4721a49..0000000 --- a/items/armors/armors.go +++ /dev/null @@ -1,57 +0,0 @@ -package armors - -import ( - "github.com/hajimehoshi/ebiten/v2" - "github.com/kensonjohnson/roguelike-game-go/assets" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" -) - -type armorData struct { - Name string - Defense int - ArmorClass int - Sprite *ebiten.Image -} - -type ArmorId int - -const ( - LinenShirt ArmorId = iota - PaddedArmor - Bones - PlateArmor -) - -var Data = []armorData{ - { - Name: "Linen Shirt", - Defense: 1, - ArmorClass: 1, - Sprite: mustBeValidImage(assets.LinenShirt, "LinenShirt"), - }, - { - Name: "Padded Armor", - Defense: 5, - ArmorClass: 6, - Sprite: mustBeValidImage(assets.PaddedArmor, "PaddedArmor"), - }, - { - Name: "Bone", - Defense: 3, - ArmorClass: 4, - Sprite: mustBeValidImage(assets.Bones, "Bones"), - }, - { - Name: "Plate Armor", - Defense: 15, - ArmorClass: 18, - Sprite: mustBeValidImage(assets.PlateArmor, "PlateArmor"), - }, -} - -func mustBeValidImage(image *ebiten.Image, name string) *ebiten.Image { - if image == nil { - logger.ErrorLogger.Panicf("%s asset not loaded!", name) - } - return image -} diff --git a/items/consumables/consumables.go b/items/consumables/consumables.go deleted file mode 100644 index 7921318..0000000 --- a/items/consumables/consumables.go +++ /dev/null @@ -1,59 +0,0 @@ -package consumables - -import ( - "github.com/hajimehoshi/ebiten/v2" - "github.com/kensonjohnson/roguelike-game-go/assets" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" -) - -type consumable struct { - Name string - AmountHeal int - Sprite *ebiten.Image -} - -type ConsumablesId int - -const ( - HealthPotion ConsumablesId = iota - GreatHealthPotion - RoyalHealthPotion - - Apple - Bread - Carrot - Cheese - Egg - Fish - Ham - Milk - Pear - Steak -) - -var Data = []*consumable{ - { - Name: "Health Potion", - AmountHeal: 10, - Sprite: mustBeValidImage(assets.WorldHealthPotion, "WorldHealthPotion"), - }, - - { - Name: "Great Heath Potion", - AmountHeal: 20, - Sprite: mustBeValidImage(assets.WorldGreatHealthPotion, "WorldGreatHealthPotion"), - }, - - { - Name: "Royal Heath Potion", - AmountHeal: 40, - Sprite: mustBeValidImage(assets.WorldRoyalHealthPotion, "WorldRoyalHealthPotion"), - }, -} - -func mustBeValidImage(image *ebiten.Image, name string) *ebiten.Image { - if image == nil { - logger.ErrorLogger.Panicf("%s asset not loaded!", name) - } - return image -} diff --git a/items/weapons/weapons.go b/items/weapons/weapons.go deleted file mode 100644 index e526ec4..0000000 --- a/items/weapons/weapons.go +++ /dev/null @@ -1,50 +0,0 @@ -package weapons - -import ( - "github.com/hajimehoshi/ebiten/v2" - "github.com/kensonjohnson/roguelike-game-go/assets" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" -) - -type weaponData struct { - Name string - ActionText string - MinimumDamage int - MaximumDamage int - ToHitBonus int - Sprite *ebiten.Image -} - -type WeaponId int - -const ( - ShortSword WeaponId = iota - BattleAxe -) - -// MAKE SURE THAT THIS NUMBER MATCHES THE NUMBER OF WEAPONS DEFINED! -var Data = []weaponData{ - { - Name: "Short Sword", - ActionText: "swings a short sword at", - MinimumDamage: 2, - MaximumDamage: 6, - ToHitBonus: 0, - Sprite: mustBeValidImage(assets.ShortSword, "ShortSword"), - }, - { - Name: "Battle Axe", - ActionText: "cleaves a battle axe at", - MinimumDamage: 10, - MaximumDamage: 20, - ToHitBonus: 3, - Sprite: mustBeValidImage(assets.BattleAxe, "BattleAxe"), - }, -} - -func mustBeValidImage(image *ebiten.Image, name string) *ebiten.Image { - if image == nil { - logger.ErrorLogger.Panicf("%s asset not loaded!", name) - } - return image -} From ea85c4b143b9c4984f41741a0a1f07c479849e13 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:03:02 -0500 Subject: [PATCH 05/55] Rework items structs in new items package --- archetype/armor.go | 7 +-- archetype/consumable.go | 7 +-- archetype/item.go | 19 +++--- archetype/level.go | 11 ++-- archetype/monster.go | 11 ++-- archetype/player.go | 11 ++-- archetype/weapon.go | 7 +-- items/armors.go | 53 ++++++++++++++++ items/consumables.go | 126 +++++++++++++++++++++++++++++++++++++ items/items.go | 8 +++ items/weapons.go | 41 ++++++++++++ system/scene/levelscene.go | 74 +++++++++++++++++++--- 12 files changed, 327 insertions(+), 48 deletions(-) create mode 100644 items/armors.go create mode 100644 items/consumables.go create mode 100644 items/items.go create mode 100644 items/weapons.go diff --git a/archetype/armor.go b/archetype/armor.go index afc262d..1b594fd 100644 --- a/archetype/armor.go +++ b/archetype/armor.go @@ -2,15 +2,14 @@ package archetype import ( "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/items/armors" + "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) var ArmorTag = donburi.NewTag("armor") -func CreateNewArmor(world donburi.World, armorId armors.ArmorId) *donburi.Entry { - armorData := armors.Data[armorId] - entry := CreateNewItem(world, int(armorId), armorData.Name, armorData.Sprite) +func CreateNewArmor(world donburi.World, armorData items.ArmorData) *donburi.Entry { + entry := CreateNewItem(world, &armorData.ItemData) // Mark as an armor entry.AddComponent(ArmorTag) diff --git a/archetype/consumable.go b/archetype/consumable.go index 9b1b452..f39fdb1 100644 --- a/archetype/consumable.go +++ b/archetype/consumable.go @@ -2,16 +2,15 @@ package archetype import ( "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/items/consumables" + "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) var ConsumableTag = donburi.NewTag("consumable") -func CreateNewConsumable(world donburi.World, consumablesId consumables.ConsumablesId) *donburi.Entry { - consumableData := consumables.Data[consumablesId] +func CreateNewConsumable(world donburi.World, consumableData items.ConsumableData) *donburi.Entry { - entry := CreateNewItem(world, int(consumablesId), consumableData.Name, consumableData.Sprite) + entry := CreateNewItem(world, &consumableData.ItemData) // Mark as a consumable entry.AddComponent(ConsumableTag) diff --git a/archetype/item.go b/archetype/item.go index c23bc28..c88e5ac 100644 --- a/archetype/item.go +++ b/archetype/item.go @@ -3,30 +3,27 @@ package archetype import ( "errors" - "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/component" + "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) -func CreateNewItem(world donburi.World, itemId int, itemName string, itemImage *ebiten.Image) *donburi.Entry { +var ItemTag = donburi.NewTag("item") + +func CreateNewItem(world donburi.World, itemData *items.ItemData) *donburi.Entry { item := world.Entry(world.Create( - component.ItemId, + ItemTag, component.Name, component.Sprite, )) - id := component.ItemIdData{ - Id: itemId, - } - component.ItemId.SetValue(item, id) - name := component.NameData{ - Value: itemName, + Value: itemData.Name, } component.Name.SetValue(item, name) sprite := component.SpriteData{ - Image: itemImage, + Image: itemData.Sprite, } component.Sprite.SetValue(item, sprite) @@ -34,7 +31,7 @@ func CreateNewItem(world donburi.World, itemId int, itemName string, itemImage * } func isItem(entry *donburi.Entry) bool { - return entry.HasComponent(component.ItemId) + return entry.HasComponent(ItemTag) } func PlaceItemInWorld(entry *donburi.Entry, x, y int, discoverable bool) error { diff --git a/archetype/level.go b/archetype/level.go index 5ad0f68..9c52edf 100644 --- a/archetype/level.go +++ b/archetype/level.go @@ -6,9 +6,7 @@ import ( "github.com/kensonjohnson/roguelike-game-go/config" "github.com/kensonjohnson/roguelike-game-go/engine" "github.com/kensonjohnson/roguelike-game-go/internal/logger" - "github.com/kensonjohnson/roguelike-game-go/items/armors" - "github.com/kensonjohnson/roguelike-game-go/items/consumables" - "github.com/kensonjohnson/roguelike-game-go/items/weapons" + "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) @@ -171,8 +169,8 @@ func seedRooms(world donburi.World, level *component.LevelData) { world, level, room, - weapons.BattleAxe, - armors.PlateArmor, + items.Weapons.BattleAxe, + items.Armor.PlateArmor, ) } else { CreateMonster(world, level, room) @@ -218,7 +216,8 @@ func addRandomPickupsToRoom( if spotTaken { continue } - potion := CreateNewConsumable(world, consumables.HealthPotion) + potion := CreateNewConsumable(world, items.Consumables.HealthPotion) + err := PlaceItemInWorld(potion, x, y, true) if err != nil { logger.ErrorLogger.Panic("Failed to place consumable in the world") diff --git a/archetype/monster.go b/archetype/monster.go index 37444ac..5997180 100644 --- a/archetype/monster.go +++ b/archetype/monster.go @@ -4,8 +4,7 @@ import ( "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/engine" - "github.com/kensonjohnson/roguelike-game-go/items/armors" - "github.com/kensonjohnson/roguelike-game-go/items/weapons" + "github.com/kensonjohnson/roguelike-game-go/items" "github.com/norendren/go-fov/fov" "github.com/yohamta/donburi" ) @@ -60,13 +59,13 @@ func CreateMonster(world donburi.World, level *component.LevelData, room engine. if coinflip == 2 { sprite.Image = assets.Orc name.Value = "Orc" - equipment.Armor = CreateNewArmor(world, armors.PaddedArmor) - equipment.Weapon = CreateNewWeapon(world, weapons.ShortSword) + equipment.Armor = CreateNewArmor(world, items.Armor.PaddedArmor) + equipment.Weapon = CreateNewWeapon(world, items.Weapons.ShortSword) } else { sprite.Image = assets.Skelly name.Value = "Skeleton" - equipment.Armor = CreateNewArmor(world, armors.Bones) - equipment.Weapon = CreateNewWeapon(world, weapons.ShortSword) + equipment.Armor = CreateNewArmor(world, items.Armor.Bones) + equipment.Weapon = CreateNewWeapon(world, items.Weapons.ShortSword) } component.Sprite.SetValue(monster, sprite) component.Name.SetValue(monster, name) diff --git a/archetype/player.go b/archetype/player.go index b3a2f48..dfcca0f 100644 --- a/archetype/player.go +++ b/archetype/player.go @@ -4,8 +4,7 @@ import ( "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/engine" - "github.com/kensonjohnson/roguelike-game-go/items/armors" - "github.com/kensonjohnson/roguelike-game-go/items/weapons" + "github.com/kensonjohnson/roguelike-game-go/items" "github.com/norendren/go-fov/fov" "github.com/yohamta/donburi" ) @@ -16,8 +15,8 @@ func CreateNewPlayer( world donburi.World, level *component.LevelData, startingRoom engine.Rect, - weaponId weapons.WeaponId, - armorId armors.ArmorId, + weapon items.WeaponData, + armorId items.ArmorData, ) { player := world.Entry(world.Create( PlayerTag, @@ -65,8 +64,8 @@ func CreateNewPlayer( // Add gear equipment := component.EquipmentData{ - Weapon: CreateNewWeapon(world, weaponId), - Armor: CreateNewArmor(world, armorId), + Weapon: CreateNewWeapon(world, items.Weapons.BattleAxe), + Armor: CreateNewArmor(world, items.Armor.PlateArmor), } component.Equipment.SetValue(player, equipment) diff --git a/archetype/weapon.go b/archetype/weapon.go index b89aea9..f34aeb8 100644 --- a/archetype/weapon.go +++ b/archetype/weapon.go @@ -2,15 +2,14 @@ package archetype import ( "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/items/weapons" + "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) var WeaponTag = donburi.NewTag("weapon") -func CreateNewWeapon(world donburi.World, weaponId weapons.WeaponId) *donburi.Entry { - weaponData := weapons.Data[weaponId] - entry := CreateNewItem(world, int(weaponId), weaponData.Name, weaponData.Sprite) +func CreateNewWeapon(world donburi.World, weaponData items.WeaponData) *donburi.Entry { + entry := CreateNewItem(world, &weaponData.ItemData) // Mark as a weapon entry.AddComponent(WeaponTag) diff --git a/items/armors.go b/items/armors.go new file mode 100644 index 0000000..74de68d --- /dev/null +++ b/items/armors.go @@ -0,0 +1,53 @@ +package items + +import ( + "github.com/kensonjohnson/roguelike-game-go/assets" +) + +type ArmorData struct { + ItemData + Defense int + ArmorClass int +} + +type armors struct { + LinenShirt ArmorData + PaddedArmor ArmorData + Bones ArmorData + PlateArmor ArmorData +} + +var Armor = armors{ + LinenShirt: ArmorData{ + ItemData: ItemData{ + Name: "Linen Shirt", + Sprite: assets.MustBeValidImage(assets.LinenShirt, "LinenShirt"), + }, + Defense: 1, + ArmorClass: 1, + }, + PaddedArmor: ArmorData{ + ItemData: ItemData{ + Name: "Padded Armor", + Sprite: assets.MustBeValidImage(assets.PaddedArmor, "PaddedArmor"), + }, + Defense: 5, + ArmorClass: 6, + }, + Bones: ArmorData{ + ItemData: ItemData{ + Name: "Bone", + Sprite: assets.MustBeValidImage(assets.Bones, "Bones"), + }, + Defense: 3, + ArmorClass: 4, + }, + PlateArmor: ArmorData{ + ItemData: ItemData{ + Name: "Plate Armor", + Sprite: assets.MustBeValidImage(assets.PlateArmor, "PlateArmor"), + }, + Defense: 15, + ArmorClass: 18, + }, +} diff --git a/items/consumables.go b/items/consumables.go new file mode 100644 index 0000000..1757478 --- /dev/null +++ b/items/consumables.go @@ -0,0 +1,126 @@ +package items + +import ( + "github.com/kensonjohnson/roguelike-game-go/assets" +) + +type ConsumableData struct { + ItemData + AmountHeal int +} + +type ConsumablesId int + +type consumables struct { + HealthPotion ConsumableData + GreatHealthPotion ConsumableData + RoyalHealthPotion ConsumableData + + Apple ConsumableData + Bread ConsumableData + Carrot ConsumableData + Cheese ConsumableData + Egg ConsumableData + Fish ConsumableData + Ham ConsumableData + Milk ConsumableData + Pear ConsumableData + Steak ConsumableData +} + +var Consumables = consumables{ + HealthPotion: ConsumableData{ + ItemData: ItemData{ + Name: "Health Potion", + Sprite: assets.MustBeValidImage(assets.WorldHealthPotion, "WorldHealthPotion"), + }, + AmountHeal: 10, + }, + + GreatHealthPotion: ConsumableData{ + ItemData: ItemData{ + Name: "Great Heath Potion", + Sprite: assets.MustBeValidImage(assets.WorldGreatHealthPotion, "WorldGreatHealthPotion"), + }, + AmountHeal: 20, + }, + + RoyalHealthPotion: ConsumableData{ + ItemData: ItemData{ + Name: "Royal Heath Potion", + Sprite: assets.MustBeValidImage(assets.WorldRoyalHealthPotion, "WorldRoyalHealthPotion"), + }, + AmountHeal: 40, + }, + + Apple: ConsumableData{ + ItemData: ItemData{ + Name: "Apple", + Sprite: assets.MustBeValidImage(assets.Apple, "Apple"), + }, + AmountHeal: 3, + }, + Bread: ConsumableData{ + ItemData: ItemData{ + Name: "Bread", + Sprite: assets.MustBeValidImage(assets.Bread, "Bread"), + }, + AmountHeal: 5, + }, + Carrot: ConsumableData{ + ItemData: ItemData{ + Name: "Carrot", + Sprite: assets.MustBeValidImage(assets.Carrot, "Carrot"), + }, + AmountHeal: 2, + }, + Cheese: ConsumableData{ + ItemData: ItemData{ + Name: "Cheese", + Sprite: assets.MustBeValidImage(assets.Cheese, "Cheese"), + }, + AmountHeal: 6, + }, + Egg: ConsumableData{ + ItemData: ItemData{ + Name: "Egg", + Sprite: assets.MustBeValidImage(assets.Egg, "Egg"), + }, + AmountHeal: 6, + }, + Fish: ConsumableData{ + ItemData: ItemData{ + Name: "Fish", + Sprite: assets.MustBeValidImage(assets.Fish, "Fish"), + }, + AmountHeal: 9, + }, + Ham: ConsumableData{ + ItemData: ItemData{ + Name: "Ham", + Sprite: assets.MustBeValidImage(assets.Ham, "Ham"), + }, + AmountHeal: 12, + }, + Milk: ConsumableData{ + ItemData: ItemData{ + Name: "Milk", + Sprite: assets.MustBeValidImage(assets.Milk, "Milk"), + }, + AmountHeal: 6, + }, + Pear: ConsumableData{ + ItemData: ItemData{ + Name: "Pear", + Sprite: assets.MustBeValidImage(assets.Pear, "Pear"), + }, + AmountHeal: 3, + }, + Steak: ConsumableData{ + ItemData: ItemData{ + Name: "Steak", + Sprite: assets.MustBeValidImage(assets.Steak, "Steak"), + }, + AmountHeal: 15, + }, +} diff --git a/items/items.go b/items/items.go new file mode 100644 index 0000000..2551650 --- /dev/null +++ b/items/items.go @@ -0,0 +1,8 @@ +package items + +import "github.com/hajimehoshi/ebiten/v2" + +type ItemData struct { + Name string + Sprite *ebiten.Image +} diff --git a/items/weapons.go b/items/weapons.go new file mode 100644 index 0000000..fa4741b --- /dev/null +++ b/items/weapons.go @@ -0,0 +1,41 @@ +package items + +import ( + "github.com/kensonjohnson/roguelike-game-go/assets" +) + +type WeaponData struct { + ItemData + ActionText string + MinimumDamage int + MaximumDamage int + ToHitBonus int +} + +type weapons struct { + ShortSword WeaponData + BattleAxe WeaponData +} + +var Weapons = weapons{ + ShortSword: WeaponData{ + ItemData: ItemData{ + Name: "Short Sword", + Sprite: assets.MustBeValidImage(assets.ShortSword, "ShortSword"), + }, + ActionText: "swings a short sword at", + MinimumDamage: 2, + MaximumDamage: 6, + ToHitBonus: 0, + }, + BattleAxe: WeaponData{ + ItemData: ItemData{ + Name: "Battle Axe", + Sprite: assets.MustBeValidImage(assets.BattleAxe, "BattleAxe"), + }, + ActionText: "cleaves a battle axe at", + MinimumDamage: 10, + MaximumDamage: 20, + ToHitBonus: 3, + }, +} diff --git a/system/scene/levelscene.go b/system/scene/levelscene.go index 6c7d69a..ef40a16 100644 --- a/system/scene/levelscene.go +++ b/system/scene/levelscene.go @@ -5,8 +5,8 @@ import ( "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/event" - "github.com/kensonjohnson/roguelike-game-go/items/armors" - "github.com/kensonjohnson/roguelike-game-go/items/weapons" + "github.com/kensonjohnson/roguelike-game-go/internal/logger" + "github.com/kensonjohnson/roguelike-game-go/items" "github.com/kensonjohnson/roguelike-game-go/layer" "github.com/kensonjohnson/roguelike-game-go/system" "github.com/yohamta/donburi" @@ -89,17 +89,77 @@ func copyPlayerInstance( currentPlayerHealth := component.Health.Get(currentPlayerEntry) currentPlayerEquipment := component.Equipment.Get(currentPlayerEntry) - weaponId := component.ItemId.Get(currentPlayerEquipment.Weapon) - armorId := component.ItemId.Get(currentPlayerEquipment.Armor) - playerEntry := archetype.PlayerTag.MustFirst(newWorld) component.Health.SetValue(playerEntry, *currentPlayerHealth) component.Equipment.SetValue(playerEntry, component.EquipmentData{ - Weapon: archetype.CreateNewWeapon(newWorld, weapons.WeaponId(weaponId.Id)), + Weapon: copyWeapon(newWorld, currentPlayerEquipment.Weapon), // Sheild: currentPlayerEquipment.Sheild, // Gloves: currentPlayerEquipment.Gloves, - Armor: archetype.CreateNewArmor(newWorld, armors.ArmorId(armorId.Id)), + Armor: copyArmor(newWorld, currentPlayerEquipment.Armor), // Boots: currentPlayerEquipment.Boots, }) } + +func copyWeapon(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { + if entry == nil { + logger.ErrorLogger.Fatal("Entry is nil when copying weapon data") + } + name := component.Name.Get(entry).Value + sprite := component.Sprite.Get(entry).Image + if sprite == nil { + logger.ErrorLogger.Fatal("Sprite missing when copying weapon data") + } + actionText := component.ActionText.Get(entry).Value + attack := component.Attack.Get(entry) + if attack == nil { + logger.ErrorLogger.Fatal("Attack data missing when copying weapon data") + } + + newWeaponData := items.WeaponData{ + ItemData: items.ItemData{ + Name: name, + Sprite: sprite, + }, + ActionText: actionText, + MinimumDamage: attack.MinimumDamage, + MaximumDamage: attack.MaximumDamage, + ToHitBonus: attack.ToHitBonus, + } + + newWeapon := archetype.CreateNewWeapon(newWorld, newWeaponData) + if newWeapon == nil { + logger.ErrorLogger.Fatal("Failed to create new weapon when copying weapon data") + } + return newWeapon +} + +func copyArmor(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { + if entry == nil { + logger.ErrorLogger.Fatal("Entry is nil when copying armor data") + } + name := component.Name.Get(entry).Value + sprite := component.Sprite.Get(entry).Image + if sprite == nil { + logger.ErrorLogger.Fatal("Sprite missing when copying armor data") + } + defense := component.Defense.Get(entry) + if defense == nil { + logger.ErrorLogger.Fatal("Defense data missing when copying armor data") + } + + newArmorData := items.ArmorData{ + ItemData: items.ItemData{ + Name: name, + Sprite: sprite, + }, + Defense: defense.Defense, + ArmorClass: defense.ArmorClass, + } + + newArmor := archetype.CreateNewArmor(newWorld, newArmorData) + if newArmor == nil { + logger.ErrorLogger.Fatal("Failed to create new armor when copying armor data") + } + return newArmor +} From 705b6f12016318475e518f5cbe6eb2e44e370a3d Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:03:11 -0500 Subject: [PATCH 06/55] Add helper function --- assets/efs.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/assets/efs.go b/assets/efs.go index 05de464..df74c8e 100644 --- a/assets/efs.go +++ b/assets/efs.go @@ -304,3 +304,10 @@ func mustLoadFont(font []byte) *text.GoTextFace { Size: float64(config.FontSize), } } + +func MustBeValidImage(image *ebiten.Image, name string) *ebiten.Image { + if image == nil { + logger.ErrorLogger.Panicf("%s asset not loaded!", name) + } + return image +} From e628cde51570938e4928768de5c88354f76c10c5 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:06:11 -0500 Subject: [PATCH 07/55] Fixed pickups --- system/action/player.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system/action/player.go b/system/action/player.go index 802b2b1..690b5a4 100644 --- a/system/action/player.go +++ b/system/action/player.go @@ -82,7 +82,7 @@ func TakePlayerAction(ecs *ecs.ECS) bool { query := donburi.NewQuery( filter.Contains( - component.ItemId, + archetype.ItemTag, component.Position, component.Name, )) @@ -95,7 +95,6 @@ func TakePlayerAction(ecs *ecs.ECS) bool { itemName := component.Name.Get(entry) playerMessages := component.UserMessage.Get(playerEntry) playerMessages.WorldInteractionMessage = fmt.Sprintf("Picked up %s!", itemName.Value) - // TODO: add pickup message to UIs // TODO: place in player's inventory } }) From e56aff27305506575fd917e8b8b4ee42668a61e4 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:09:28 -0500 Subject: [PATCH 08/55] Update go version 1.23 --- go.mod | 7 +++++-- go.sum | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index ef3e732..17dae09 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/kensonjohnson/roguelike-game-go -go 1.22.5 +go 1.23.0 require ( github.com/hajimehoshi/ebiten/v2 v2.7.8 @@ -8,7 +8,10 @@ require ( github.com/setanarut/kamera/v2 v2.5.1 ) -require github.com/ojrac/opensimplex-go v1.0.2 // indirect +require ( + github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 // indirect + github.com/ojrac/opensimplex-go v1.0.2 // indirect +) require ( github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc // indirect diff --git a/go.sum b/go.sum index 6ad3332..621678c 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDi github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo= github.com/go-text/typesetting v0.1.1/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI= -github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY= -github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= +github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= +github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/hajimehoshi/bitmapfont/v3 v3.0.0 h1:r2+6gYK38nfztS/et50gHAswb9hXgxXECYgE8Nczmi4= github.com/hajimehoshi/bitmapfont/v3 v3.0.0/go.mod h1:+CxxG+uMmgU4mI2poq944i3uZ6UYFfAkj9V6WqmuvZA= github.com/hajimehoshi/ebiten/v2 v2.7.8 h1:QrlvF2byCzMuDsbxFReJkOCbM3O2z1H/NKQaGcA8PKk= @@ -22,11 +22,45 @@ github.com/setanarut/kamera/v2 v2.5.1 h1:+bBPaUSoUsxBoZupRqWTTrzt0IetWCsIuZOk4oy github.com/setanarut/kamera/v2 v2.5.1/go.mod h1:XxIMAowz4s+6uTR+4B8YkinDrvOechvhCAH3AlzP9iU= github.com/yohamta/donburi v1.4.4 h1:j29uSVIherEsBGV1/MzGckxBdFoCMmRbIv9Gva80zMM= github.com/yohamta/donburi v1.4.4/go.mod h1:cx7C0ucl1ugqXSR+OpaCgfezWJXxh7BjTceaTxzO+3E= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ= golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 27a328e097eaa4fe1f221be4b4a3891cd2ec1eb7 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:11:01 -0500 Subject: [PATCH 09/55] Swap fatal for panic --- system/scene/levelscene.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/system/scene/levelscene.go b/system/scene/levelscene.go index ef40a16..c8db01f 100644 --- a/system/scene/levelscene.go +++ b/system/scene/levelscene.go @@ -103,17 +103,17 @@ func copyPlayerInstance( func copyWeapon(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { if entry == nil { - logger.ErrorLogger.Fatal("Entry is nil when copying weapon data") + logger.ErrorLogger.Panic("Entry is nil when copying weapon data") } name := component.Name.Get(entry).Value sprite := component.Sprite.Get(entry).Image if sprite == nil { - logger.ErrorLogger.Fatal("Sprite missing when copying weapon data") + logger.ErrorLogger.Panic("Sprite missing when copying weapon data") } actionText := component.ActionText.Get(entry).Value attack := component.Attack.Get(entry) if attack == nil { - logger.ErrorLogger.Fatal("Attack data missing when copying weapon data") + logger.ErrorLogger.Panic("Attack data missing when copying weapon data") } newWeaponData := items.WeaponData{ @@ -129,23 +129,23 @@ func copyWeapon(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { newWeapon := archetype.CreateNewWeapon(newWorld, newWeaponData) if newWeapon == nil { - logger.ErrorLogger.Fatal("Failed to create new weapon when copying weapon data") + logger.ErrorLogger.Panic("Failed to create new weapon when copying weapon data") } return newWeapon } func copyArmor(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { if entry == nil { - logger.ErrorLogger.Fatal("Entry is nil when copying armor data") + logger.ErrorLogger.Panic("Entry is nil when copying armor data") } name := component.Name.Get(entry).Value sprite := component.Sprite.Get(entry).Image if sprite == nil { - logger.ErrorLogger.Fatal("Sprite missing when copying armor data") + logger.ErrorLogger.Panic("Sprite missing when copying armor data") } defense := component.Defense.Get(entry) if defense == nil { - logger.ErrorLogger.Fatal("Defense data missing when copying armor data") + logger.ErrorLogger.Panic("Defense data missing when copying armor data") } newArmorData := items.ArmorData{ @@ -159,7 +159,7 @@ func copyArmor(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { newArmor := archetype.CreateNewArmor(newWorld, newArmorData) if newArmor == nil { - logger.ErrorLogger.Fatal("Failed to create new armor when copying armor data") + logger.ErrorLogger.Panic("Failed to create new armor when copying armor data") } return newArmor } From 66b7d71be752dbe1f2471ed58b4e36ba2dcc5282 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:56:46 -0500 Subject: [PATCH 10/55] Update donburi version --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 17dae09..0c4a14c 100644 --- a/go.mod +++ b/go.mod @@ -14,14 +14,14 @@ require ( ) require ( - github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc // indirect + github.com/ebitengine/gomobile v0.0.0-20240825043811-96c531f5bd83 // indirect github.com/ebitengine/hideconsole v1.0.0 // indirect github.com/ebitengine/purego v0.7.1 // indirect github.com/go-text/typesetting v0.1.1 // indirect github.com/jezek/xgb v1.1.1 // indirect - github.com/yohamta/donburi v1.4.4 + github.com/yohamta/donburi v1.15.3 golang.org/x/image v0.19.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect + golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.17.0 // indirect ) diff --git a/go.sum b/go.sum index 621678c..71c8b1a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc h1:76TYsaP1F48tiQRlrr71NsbfxBcFM9/8bEHS9/JbsQg= -github.com/ebitengine/gomobile v0.0.0-20240802043200-192f051f4fcc/go.mod h1:RM/c3pvru6dRqgGEW7RCTb6czFXYAa3MxbXu3u8/dcI= +github.com/ebitengine/gomobile v0.0.0-20240825043811-96c531f5bd83 h1:yA0CtFKYZI/db1snCOInRS0Z18QGZU6aBYkqUT0H6RI= +github.com/ebitengine/gomobile v0.0.0-20240825043811-96c531f5bd83/go.mod h1:n2NbB/F4d9wOXFzC7FT1ipERidmYWC5I4YNOYRs5N7I= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA= @@ -20,8 +20,8 @@ github.com/ojrac/opensimplex-go v1.0.2 h1:l4vs0D+JCakcu5OV0kJ99oEaWJfggSc9jiLpxa github.com/ojrac/opensimplex-go v1.0.2/go.mod h1:NwbXFFbXcdGgIFdiA7/REME+7n/lOf1TuEbLiZYOWnM= github.com/setanarut/kamera/v2 v2.5.1 h1:+bBPaUSoUsxBoZupRqWTTrzt0IetWCsIuZOk4oyKXZs= github.com/setanarut/kamera/v2 v2.5.1/go.mod h1:XxIMAowz4s+6uTR+4B8YkinDrvOechvhCAH3AlzP9iU= -github.com/yohamta/donburi v1.4.4 h1:j29uSVIherEsBGV1/MzGckxBdFoCMmRbIv9Gva80zMM= -github.com/yohamta/donburi v1.4.4/go.mod h1:cx7C0ucl1ugqXSR+OpaCgfezWJXxh7BjTceaTxzO+3E= +github.com/yohamta/donburi v1.15.3 h1:b5Z0hUez8eCnjuoDzecsj5epQflRgyqXpiT/Ppz+Pw0= +github.com/yohamta/donburi v1.15.3/go.mod h1:g5P6MQ7zQcFjiPTU3m5Ox1I+Yw8rqmwvQuxFkryQ8os= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -46,8 +46,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= From 372581992ba0d6c9ff12c0f36e34d0240eb5f3fa Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:58:28 -0500 Subject: [PATCH 11/55] Use new Iter func, order rendering --- system/render.go | 64 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/system/render.go b/system/render.go index aae0c3c..bf197ed 100644 --- a/system/render.go +++ b/system/render.go @@ -5,19 +5,28 @@ import ( "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/norendren/go-fov/fov" "github.com/yohamta/donburi" "github.com/yohamta/donburi/ecs" "github.com/yohamta/donburi/filter" ) type render struct { - query *donburi.Query + enemyQuery *donburi.Query + pickupsQuery *donburi.Query backgroundImage *ebiten.Image } var Render = &render{ - query: donburi.NewQuery( + enemyQuery: donburi.NewQuery( filter.Contains( + archetype.MonsterTag, + component.Sprite, + component.Position, + )), + pickupsQuery: donburi.NewQuery( + filter.Contains( + archetype.ItemTag, component.Position, component.Sprite, )), @@ -25,28 +34,43 @@ var Render = &render{ } func (r *render) Draw(ecs *ecs.ECS, screen *ebiten.Image) { - entry := archetype.PlayerTag.MustFirst(ecs.World) - playerVision := component.Fov.Get(entry).VisibleTiles - entry = archetype.CameraTag.MustFirst(ecs.World) + playerEntry := archetype.PlayerTag.MustFirst(ecs.World) + playerVision := component.Fov.Get(playerEntry).VisibleTiles + entry := archetype.CameraTag.MustFirst(ecs.World) camera := component.Camera.Get(entry) - r.query.Each(ecs.World, func(entry *donburi.Entry) { - position := component.Position.Get(entry) - sprite := component.Sprite.Get(entry) - - if playerVision.IsVisible(position.X, position.Y) { - camera.CamImageOptions.GeoM.Reset() - if sprite.Animating { - offsetX, offsetY := sprite.GetAnimationStep() - camera.CamImageOptions.GeoM.Translate(float64(position.X*config.TileWidth)+offsetX, float64(position.Y*config.TileHeight)+offsetY) - } else { - camera.CamImageOptions.GeoM.Translate(float64(position.X*config.TileWidth), float64(position.Y*config.TileHeight)) - } - camera.MainCamera.Draw(sprite.Image, camera.CamImageOptions, screen) - } - }) + for entry = range r.pickupsQuery.Iter(ecs.World) { + renderEntity(screen, camera, entry, playerVision) + } + + for entry = range r.enemyQuery.Iter(ecs.World) { + renderEntity(screen, camera, entry, playerVision) + } + + renderEntity(screen, camera, playerEntry, playerVision) camera.CamImageOptions.GeoM.Reset() camera.CamImageOptions.GeoM.Translate(0, 0) camera.MainCamera.Draw(camera.CamScreen, camera.CamImageOptions, screen) } + +func renderEntity( + screen *ebiten.Image, + camera *component.CameraData, + entry *donburi.Entry, + playerVision *fov.View, +) { + position := component.Position.Get(entry) + sprite := component.Sprite.Get(entry) + + if playerVision.IsVisible(position.X, position.Y) { + camera.CamImageOptions.GeoM.Reset() + if sprite.Animating { + offsetX, offsetY := sprite.GetAnimationStep() + camera.CamImageOptions.GeoM.Translate(float64(position.X*config.TileWidth)+offsetX, float64(position.Y*config.TileHeight)+offsetY) + } else { + camera.CamImageOptions.GeoM.Translate(float64(position.X*config.TileWidth), float64(position.Y*config.TileHeight)) + } + camera.MainCamera.Draw(sprite.Image, camera.CamImageOptions, screen) + } +} From 46b22b0a5aed5c6780f29e82e13b932330917a2d Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:58:37 -0500 Subject: [PATCH 12/55] Add pickup tag --- archetype/item.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/archetype/item.go b/archetype/item.go index c88e5ac..87b8192 100644 --- a/archetype/item.go +++ b/archetype/item.go @@ -34,11 +34,15 @@ func isItem(entry *donburi.Entry) bool { return entry.HasComponent(ItemTag) } +var PickupTag = donburi.NewTag("pickup") + func PlaceItemInWorld(entry *donburi.Entry, x, y int, discoverable bool) error { if !isItem(entry) { return errors.New("entry is not an Item Entity") } + entry.AddComponent(PickupTag) + entry.AddComponent(component.Position) position := component.PositionData{ X: x, @@ -56,6 +60,7 @@ func PlaceItemInWorld(entry *donburi.Entry, x, y int, discoverable bool) error { } func RemoveItemFromWorld(entry *donburi.Entry) { + entry.RemoveComponent(PickupTag) entry.RemoveComponent(component.Position) if entry.HasComponent(component.Discoverable) { entry.RemoveComponent(component.Discoverable) From ca0fadcf5b7c852d70aaa5813db607cb521f88e5 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:59:09 -0500 Subject: [PATCH 13/55] Remove need for filter --- system/action/player.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/system/action/player.go b/system/action/player.go index 690b5a4..147cfdb 100644 --- a/system/action/player.go +++ b/system/action/player.go @@ -10,7 +10,6 @@ import ( "github.com/kensonjohnson/roguelike-game-go/system/combat" "github.com/yohamta/donburi" "github.com/yohamta/donburi/ecs" - "github.com/yohamta/donburi/filter" ) func TakePlayerAction(ecs *ecs.ECS) bool { @@ -80,14 +79,7 @@ func TakePlayerAction(ecs *ecs.ECS) bool { } }) - query := donburi.NewQuery( - filter.Contains( - archetype.ItemTag, - component.Position, - component.Name, - )) - - query.Each(ecs.World, func(entry *donburi.Entry) { + archetype.PickupTag.Each(ecs.World, func(entry *donburi.Entry) { itemPosition := component.Position.Get(entry) if position.X == itemPosition.X && position.Y == itemPosition.Y { // The character has moved on top of a pickup From 50939b3fcd67198645e41a2d9a9be06d4e3edc55 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:14:31 -0500 Subject: [PATCH 14/55] Pickup item after walk animation completes --- system/action/player.go | 14 -------------- system/turn.go | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/system/action/player.go b/system/action/player.go index 147cfdb..bf73685 100644 --- a/system/action/player.go +++ b/system/action/player.go @@ -1,8 +1,6 @@ package action import ( - "fmt" - "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" @@ -79,18 +77,6 @@ func TakePlayerAction(ecs *ecs.ECS) bool { } }) - archetype.PickupTag.Each(ecs.World, func(entry *donburi.Entry) { - itemPosition := component.Position.Get(entry) - if position.X == itemPosition.X && position.Y == itemPosition.Y { - // The character has moved on top of a pickup - archetype.RemoveItemFromWorld(entry) - itemName := component.Name.Get(entry) - playerMessages := component.UserMessage.Get(playerEntry) - playerMessages.WorldInteractionMessage = fmt.Sprintf("Picked up %s!", itemName.Value) - // TODO: place in player's inventory - } - }) - if tile.TileType == component.STAIR_DOWN { // Move to the next level event.ProgressLevelEvent.Publish(ecs.World, event.ProgressLevel{}) diff --git a/system/turn.go b/system/turn.go index 0003eba..9b969e1 100644 --- a/system/turn.go +++ b/system/turn.go @@ -1,10 +1,11 @@ package system import ( + "fmt" + "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/system/action" - "github.com/yohamta/donburi" "github.com/yohamta/donburi/ecs" ) @@ -42,7 +43,7 @@ func (td *TurnData) Update(ecs *ecs.ECS) { level := component.Level.Get(archetype.LevelTag.MustFirst(ecs.World)) // Remove any enemies that died during the last turn - archetype.MonsterTag.Each(ecs.World, func(entry *donburi.Entry) { + for entry = range archetype.MonsterTag.Iter(ecs.World) { health := component.Health.Get(entry) if health.CurrentHealth <= 0 { position := component.Position.Get(entry) @@ -50,22 +51,42 @@ func (td *TurnData) Update(ecs *ecs.ECS) { tile.Blocked = false ecs.World.Remove(entry.Entity()) } - }) - component.Sprite.Each(ecs.World, func(entry *donburi.Entry) { - sprite := component.Sprite.Get(entry) + } + + for spriteEntry := range component.Sprite.Iter(ecs.World) { + sprite := component.Sprite.Get(spriteEntry) sprite.SetProgress(float64(td.TurnCounter) / 12) - }) + + } if td.TurnCounter > 12 { // Reset the progress of all sprites - component.Sprite.Each(ecs.World, func(entry *donburi.Entry) { + for entry = range component.Sprite.Iter(ecs.World) { sprite := component.Sprite.Get(entry) sprite.SetProgress(0) sprite.Animating = false sprite.OffestX = 0 sprite.OffestY = 0 - }) + } + + entry = archetype.PlayerTag.MustFirst(ecs.World) + playerPosition := component.Position.Get(entry) + playerMessages := component.UserMessage.Get(entry) + + for entry = range archetype.PickupTag.Iter(ecs.World) { + if !entry.HasComponent(component.Position) { + continue + } + pickupPosition := component.Position.Get(entry) + + if pickupPosition.X == playerPosition.X && pickupPosition.Y == playerPosition.Y { + archetype.RemoveItemFromWorld(entry) + itemName := component.Name.Get(entry) + playerMessages.WorldInteractionMessage = fmt.Sprintf("Picked up %s!", itemName.Value) + // TODO: place in player's inventory + } + } level.Redraw = true td.progressTurnState() From 1baad076e2f8c3f9250ce6e31a8b661fcf2043d1 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:03:31 -0500 Subject: [PATCH 15/55] Refactor to use iterators --- system/action/monster.go | 5 ++--- system/action/player.go | 8 ++++---- system/minimap.go | 6 +++--- system/ui.go | 9 +++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/system/action/monster.go b/system/action/monster.go index 7ecf922..27be263 100644 --- a/system/action/monster.go +++ b/system/action/monster.go @@ -5,7 +5,6 @@ import ( "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/engine/pathing" "github.com/kensonjohnson/roguelike-game-go/system/combat" - "github.com/yohamta/donburi" "github.com/yohamta/donburi/ecs" ) @@ -18,7 +17,7 @@ func TakeMonsterAction(ecs *ecs.ECS) { playerEntry := archetype.PlayerTag.MustFirst(ecs.World) playerPos := component.Position.Get(playerEntry) - archetype.MonsterTag.Each(ecs.World, func(entry *donburi.Entry) { + for entry = range archetype.MonsterTag.Iter(ecs.World) { position := component.Position.Get(entry) sprite := component.Sprite.Get(entry) monsterVision := component.Fov.Get(entry).VisibleTiles @@ -49,5 +48,5 @@ func TakeMonsterAction(ecs *ecs.ECS) { } } } - }) + } } diff --git a/system/action/player.go b/system/action/player.go index bf73685..11709a8 100644 --- a/system/action/player.go +++ b/system/action/player.go @@ -69,13 +69,13 @@ func TakePlayerAction(ecs *ecs.ECS) bool { // Update the player's field of view vision.VisibleTiles.Compute(level, position.X, position.Y, 8) // Update any discoverable entities - component.Discoverable.Each(ecs.World, func(entry *donburi.Entry) { + for entry := range component.Discoverable.Iter(ecs.World) { discoverablePosition := component.Position.Get(entry) if vision.VisibleTiles.IsVisible(discoverablePosition.X, discoverablePosition.Y) { discoverable := component.Discoverable.Get(entry) discoverable.SeenByPlayer = true } - }) + } if tile.TileType == component.STAIR_DOWN { // Move to the next level @@ -90,12 +90,12 @@ func TakePlayerAction(ecs *ecs.ECS) bool { Y: position.Y + moveY, } var monsterEntry *donburi.Entry - archetype.MonsterTag.Each(ecs.World, func(entry *donburi.Entry) { + for entry := range archetype.MonsterTag.Iter(ecs.World) { position := component.Position.Get(entry) if position.IsEqual(&enemyPosition) { monsterEntry = entry } - }) + } combat.AttackSystem(ecs.World, playerEntry, monsterEntry) } diff --git a/system/minimap.go b/system/minimap.go index 3c04778..31da6cd 100644 --- a/system/minimap.go +++ b/system/minimap.go @@ -8,7 +8,6 @@ import ( "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/config" - "github.com/yohamta/donburi" "github.com/yohamta/donburi/ecs" ) @@ -42,7 +41,8 @@ func DrawMinimap(ecs *ecs.ECS, screen *ebiten.Image) { } // Draw all discovered entities - component.Discoverable.Each(ecs.World, func(entry *donburi.Entry) { + for entry = range component.Discoverable.Iter(ecs.World) { + position := component.Position.Get(entry) if !level.InBounds(position.X, position.Y) { return @@ -58,7 +58,7 @@ func DrawMinimap(ecs *ecs.ECS, screen *ebiten.Image) { vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, color.RGBA{R: 255, G: 0, B: 0, A: 255}, false) } } - }) + } // Draw the player playerEntry := archetype.PlayerTag.MustFirst(ecs.World) diff --git a/system/ui.go b/system/ui.go index cfbf220..d3d22ab 100644 --- a/system/ui.go +++ b/system/ui.go @@ -36,7 +36,7 @@ var UI = &ui{ func (u *ui) Update(ecs *ecs.ECS) { // Get attack messages first - u.query.Each(ecs.World, func(entry *donburi.Entry) { + for entry := range u.query.Iter(ecs.World) { messages := component.UserMessage.Get(entry) if messages.AttackMessage != "" { u.lastMessages = append(u.lastMessages, messages.AttackMessage) @@ -46,9 +46,10 @@ func (u *ui) Update(ecs *ecs.ECS) { u.lastMessages = append(u.lastMessages, messages.WorldInteractionMessage) messages.WorldInteractionMessage = "" } - }) + } + // Then process any deaths, including the player's - u.query.Each(ecs.World, func(entry *donburi.Entry) { + for entry := range u.query.Iter(ecs.World) { messages := component.UserMessage.Get(entry) if messages.DeadMessage != "" { u.lastMessages = append(u.lastMessages, messages.DeadMessage) @@ -58,7 +59,7 @@ func (u *ui) Update(ecs *ecs.ECS) { u.lastMessages = append(u.lastMessages, messages.GameStateMessage) messages.GameStateMessage = "" } - }) + } if len(u.lastMessages) > 6 { // Save just the last 6 messages From 862f567a7f7e1c0f5040378500391bde257d7fa0 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:26:14 -0500 Subject: [PATCH 16/55] Update naming for consistency --- archetype/item.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/archetype/item.go b/archetype/item.go index 87b8192..8258913 100644 --- a/archetype/item.go +++ b/archetype/item.go @@ -11,7 +11,7 @@ import ( var ItemTag = donburi.NewTag("item") func CreateNewItem(world donburi.World, itemData *items.ItemData) *donburi.Entry { - item := world.Entry(world.Create( + entry := world.Entry(world.Create( ItemTag, component.Name, component.Sprite, @@ -20,14 +20,14 @@ func CreateNewItem(world donburi.World, itemData *items.ItemData) *donburi.Entry name := component.NameData{ Value: itemData.Name, } - component.Name.SetValue(item, name) + component.Name.SetValue(entry, name) sprite := component.SpriteData{ Image: itemData.Sprite, } - component.Sprite.SetValue(item, sprite) + component.Sprite.SetValue(entry, sprite) - return item + return entry } func isItem(entry *donburi.Entry) bool { From 038435a70285e8241358b8f99bcdeb6284ab881b Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:27:00 -0500 Subject: [PATCH 17/55] Add randomness and a loot table --- archetype/level.go | 78 +++++++++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/archetype/level.go b/archetype/level.go index 9c52edf..6ee4ad7 100644 --- a/archetype/level.go +++ b/archetype/level.go @@ -191,38 +191,58 @@ func addRandomPickupsToRoom( room engine.Rect, generosity int, ) { - // TODO: create a random distribution of items - // TODO: add a random chance for an item to appear - switch d10Roll := engine.GetDiceRoll(10); { - case d10Roll <= generosity: - width := room.X2 - room.X1 - 2 - height := room.Y2 - room.Y1 - 2 - for i := 0; i < d10Roll; i++ { - offsetX := engine.GetRandomInt(width) - offsetY := engine.GetRandomInt(height) - x := room.X1 + offsetX + 1 - y := room.Y1 + offsetY + 1 - tile := level.GetFromXY(x, y) - if tile.Blocked { - continue - } - spotTaken := false - ConsumableTag.Each(world, func(entry *donburi.Entry) { - position := component.Position.Get(entry) - if position.X == x && position.Y == y { - spotTaken = true - } - }) - if spotTaken { - continue - } - potion := CreateNewConsumable(world, items.Consumables.HealthPotion) + d10Roll := engine.GetDiceRoll(10) + if d10Roll > generosity { + return + } + width := room.X2 - room.X1 - 2 + height := room.Y2 - room.Y1 - 2 + for i := 0; i < d10Roll; i++ { + + offsetX := engine.GetRandomInt(width) + offsetY := engine.GetRandomInt(height) + x := room.X1 + offsetX + 1 + y := room.Y1 + offsetY + 1 + + tile := level.GetFromXY(x, y) + if tile.Blocked { + continue + } - err := PlaceItemInWorld(potion, x, y, true) - if err != nil { - logger.ErrorLogger.Panic("Failed to place consumable in the world") + spotTaken := false + for entry := range PickupTag.Iter(world) { + position := component.Position.Get(entry) + if position.X == x && position.Y == y { + spotTaken = true } } + + if spotTaken { + continue + } + + entry := createRandomPickup(world) + + err := PlaceItemInWorld(entry, x, y, true) + if err != nil { + logger.ErrorLogger.Panic("Failed to place consumable in the world") + } + } +} + +func createRandomPickup(world donburi.World) *donburi.Entry { + // This is where we can manipulate the randomness of which item drops + switch roll := engine.GetDiceRoll(10); roll { + case 1, 4: + return CreateNewConsumable(world, items.Consumables.HealthPotion) + case 2, 3, 5, 6: + return CreateCoins(world, items.Valuables.SmallCoin()) + case 7, 9, 10: + return CreateCoins(world, items.Valuables.CoinStack()) + case 8: + return CreateNewValuable(world, items.Valuables.Alcohol) + default: + return CreateNewConsumable(world, items.Consumables.HealthPotion) } } From c8cc144940ae316db8330bee5eb5c2db36f4c0fd Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:27:14 -0500 Subject: [PATCH 18/55] Remove unused component --- component/item-id.go | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 component/item-id.go diff --git a/component/item-id.go b/component/item-id.go deleted file mode 100644 index 6e86dd7..0000000 --- a/component/item-id.go +++ /dev/null @@ -1,9 +0,0 @@ -package component - -import "github.com/yohamta/donburi" - -type ItemIdData struct { - Id int -} - -var ItemId = donburi.NewComponentType[ItemIdData]() From 7d5499bfd4306d7f7d665c7e40d13107b554bf1d Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:27:30 -0500 Subject: [PATCH 19/55] Create a value component --- component/value.go | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 component/value.go diff --git a/component/value.go b/component/value.go new file mode 100644 index 0000000..837d835 --- /dev/null +++ b/component/value.go @@ -0,0 +1,9 @@ +package component + +import "github.com/yohamta/donburi" + +type ValueData struct { + Amount int +} + +var Value = donburi.NewComponentType[ValueData]() From 7d5b02771f6542d05108fa28c1334b1428f3ed8d Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:27:55 -0500 Subject: [PATCH 20/55] Add valuables to the world --- archetype/valuable.go | 37 ++++++++++++++++++++++++++++++++++ items/valuables.go | 47 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 archetype/valuable.go create mode 100644 items/valuables.go diff --git a/archetype/valuable.go b/archetype/valuable.go new file mode 100644 index 0000000..0cc0a3d --- /dev/null +++ b/archetype/valuable.go @@ -0,0 +1,37 @@ +package archetype + +import ( + "github.com/kensonjohnson/roguelike-game-go/component" + "github.com/kensonjohnson/roguelike-game-go/items" + "github.com/yohamta/donburi" +) + +var ValuableTag = donburi.NewTag("valuable") + +func CreateNewValuable(world donburi.World, valuableData *items.ValuableData) *donburi.Entry { + entry := CreateNewItem(world, &valuableData.ItemData) + + entry.AddComponent(ValuableTag) + + entry.AddComponent(component.Value) + value := component.ValueData{ + Amount: valuableData.Value, + } + component.Value.SetValue(entry, value) + + return entry +} + +var CoinTag = donburi.NewTag("coin") + +func CreateCoins(world donburi.World, valuableData *items.ValuableData) *donburi.Entry { + entry := CreateNewValuable(world, valuableData) + + entry.AddComponent(CoinTag) + + return entry +} + +func IsCoin(entry donburi.Entry) bool { + return entry.HasComponent(CoinTag) +} diff --git a/items/valuables.go b/items/valuables.go new file mode 100644 index 0000000..2680d97 --- /dev/null +++ b/items/valuables.go @@ -0,0 +1,47 @@ +package items + +import ( + "github.com/kensonjohnson/roguelike-game-go/assets" + "github.com/kensonjohnson/roguelike-game-go/engine" +) + +type ValuableData struct { + ItemData + Value int +} + +type valuables struct { + Alcohol *ValuableData +} + +var Valuables valuables = valuables{ + Alcohol: &ValuableData{ + ItemData: ItemData{ + Name: "Alcohol", + Sprite: assets.WorldAlcohol, + }, + Value: 20, + }, +} + +func (v valuables) SmallCoin() *ValuableData { + value := engine.GetRandomBetween(3, 11) + return &ValuableData{ + ItemData: ItemData{ + Name: "some coins", + Sprite: assets.WorldSmallCoin, + }, + Value: value, + } +} + +func (v valuables) CoinStack() *ValuableData { + value := engine.GetRandomBetween(15, 35) + return &ValuableData{ + ItemData: ItemData{ + Name: "a stack of coins", + Sprite: assets.WorldCoinStack, + }, + Value: value, + } +} From b161b1a9067ba6a7be1f8b0761a4b6d5415e7c24 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:28:10 -0500 Subject: [PATCH 21/55] Fix drawing for items --- system/minimap.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/minimap.go b/system/minimap.go index 31da6cd..ded3bb6 100644 --- a/system/minimap.go +++ b/system/minimap.go @@ -52,7 +52,7 @@ func DrawMinimap(ecs *ecs.ECS, screen *ebiten.Image) { y := startingYPixel + (position.Y * blipSize) if component.Discoverable.Get(entry).SeenByPlayer { - if entry.HasComponent(component.ItemId) { + if entry.HasComponent(archetype.ItemTag) { vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, color.RGBA{R: 15, G: 10, B: 222, A: 255}, false) } else { vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, color.RGBA{R: 255, G: 0, B: 0, A: 255}, false) From 8f9b80a71af6beab13683886008c09f3569a573f Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:16:19 -0500 Subject: [PATCH 22/55] Define initial inventory component --- component/inventory.go | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 component/inventory.go diff --git a/component/inventory.go b/component/inventory.go new file mode 100644 index 0000000..a080729 --- /dev/null +++ b/component/inventory.go @@ -0,0 +1,72 @@ +package component + +import ( + "errors" + + "github.com/yohamta/donburi" +) + +type InventoryData struct { + Items []*donburi.Entry + capacity int + holding int +} + +var Inventory = donburi.NewComponentType[InventoryData]() + +func (i *InventoryData) New(capacity int) InventoryData { + return InventoryData{ + Items: make([]*donburi.Entry, capacity), + capacity: capacity, + holding: 0, + } +} + +func (i *InventoryData) GetCapacityInfo() (holding, capacity int) { + return i.holding, i.capacity +} + +func (i *InventoryData) IncreaseCapacityByAmount(amount int) { + i.capacity += amount + newStorage := make([]*donburi.Entry, i.capacity) + copy(newStorage, i.Items) + i.Items = newStorage +} + +func (i *InventoryData) DecreaseCapacityByAmount(amount int) error { + if i.capacity-i.holding > amount { + return errors.New("holding too many items to reduce capacity") + } + + return nil +} + +func (i *InventoryData) AddItem(item *donburi.Entry) error { + if i.holding >= i.capacity { + return errors.New("inventory full") + } + + var targetIndex = -1 + + for index, element := range i.Items { + if element == nil { + targetIndex = index + break + } + } + + if targetIndex == -1 { + return errors.New("failed to find empty index for item") + } + + i.Items[targetIndex] = item + return nil +} + +func (i *InventoryData) RemoveItem(index int) error { + if index >= i.capacity { + return errors.New("index out of range in RemoveItem") + } + i.Items[index] = nil + return nil +} From 1bbed48c4aa91bbdb7f769507a06dccfe163a5c5 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:16:29 -0500 Subject: [PATCH 23/55] Define initial wallet component --- component/wallet.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 component/wallet.go diff --git a/component/wallet.go b/component/wallet.go new file mode 100644 index 0000000..c9182a9 --- /dev/null +++ b/component/wallet.go @@ -0,0 +1,17 @@ +package component + +import "github.com/yohamta/donburi" + +type WalletData struct { + Amount int +} + +var Wallet = donburi.NewComponentType[WalletData]() + +func (w *WalletData) AddAmount(amount int) { + w.Amount += amount +} + +func (w *WalletData) SubtractAmount(amount int) { + w.Amount -= amount +} From d7e8379911257d6d2d511634fad1fc74741d1b24 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:39:46 -0500 Subject: [PATCH 24/55] Move engine and globals into internal --- {config => internal/config}/globals.go | 0 {engine => internal/engine}/pathing/astar.go | 2 +- {engine => internal/engine}/random.go | 0 {engine => internal/engine}/rect.go | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename {config => internal/config}/globals.go (100%) rename {engine => internal/engine}/pathing/astar.go (98%) rename {engine => internal/engine}/random.go (100%) rename {engine => internal/engine}/rect.go (100%) diff --git a/config/globals.go b/internal/config/globals.go similarity index 100% rename from config/globals.go rename to internal/config/globals.go diff --git a/engine/pathing/astar.go b/internal/engine/pathing/astar.go similarity index 98% rename from engine/pathing/astar.go rename to internal/engine/pathing/astar.go index ebc2a83..989c006 100644 --- a/engine/pathing/astar.go +++ b/internal/engine/pathing/astar.go @@ -5,7 +5,7 @@ import ( "reflect" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" ) type node struct { diff --git a/engine/random.go b/internal/engine/random.go similarity index 100% rename from engine/random.go rename to internal/engine/random.go diff --git a/engine/rect.go b/internal/engine/rect.go similarity index 100% rename from engine/rect.go rename to internal/engine/rect.go From c87282dd27b38d86d02bbca57a95538f9002297a Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:41:10 -0500 Subject: [PATCH 25/55] Update import path --- archetype/camera.go | 2 +- archetype/level.go | 4 ++-- archetype/monster.go | 2 +- archetype/player.go | 2 +- archetype/ui.go | 2 +- assets/efs.go | 2 +- component/level.go | 4 ++-- component/sprite.go | 2 +- items/valuables.go | 2 +- main.go | 2 +- system/action/monster.go | 2 +- system/background.go | 2 +- system/camera.go | 2 +- system/combat/combat.go | 2 +- system/debug.go | 2 +- system/minimap.go | 2 +- system/render.go | 2 +- system/scene/titlescene.go | 3 +-- system/ui.go | 2 +- 19 files changed, 21 insertions(+), 22 deletions(-) diff --git a/archetype/camera.go b/archetype/camera.go index f19b048..bf93e7b 100644 --- a/archetype/camera.go +++ b/archetype/camera.go @@ -3,7 +3,7 @@ package archetype import ( "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/setanarut/kamera/v2" "github.com/yohamta/donburi" diff --git a/archetype/level.go b/archetype/level.go index 6ee4ad7..4c2d6b8 100644 --- a/archetype/level.go +++ b/archetype/level.go @@ -3,8 +3,8 @@ package archetype import ( "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/config" - "github.com/kensonjohnson/roguelike-game-go/engine" + "github.com/kensonjohnson/roguelike-game-go/internal/config" + "github.com/kensonjohnson/roguelike-game-go/internal/engine" "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" diff --git a/archetype/monster.go b/archetype/monster.go index 5997180..7a2079d 100644 --- a/archetype/monster.go +++ b/archetype/monster.go @@ -3,7 +3,7 @@ package archetype import ( "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/engine" + "github.com/kensonjohnson/roguelike-game-go/internal/engine" "github.com/kensonjohnson/roguelike-game-go/items" "github.com/norendren/go-fov/fov" "github.com/yohamta/donburi" diff --git a/archetype/player.go b/archetype/player.go index dfcca0f..843d2e8 100644 --- a/archetype/player.go +++ b/archetype/player.go @@ -3,7 +3,7 @@ package archetype import ( "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/engine" + "github.com/kensonjohnson/roguelike-game-go/internal/engine" "github.com/kensonjohnson/roguelike-game-go/items" "github.com/norendren/go-fov/fov" "github.com/yohamta/donburi" diff --git a/archetype/ui.go b/archetype/ui.go index 181efac..41b633a 100644 --- a/archetype/ui.go +++ b/archetype/ui.go @@ -3,7 +3,7 @@ package archetype import ( "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi" ) diff --git a/assets/efs.go b/assets/efs.go index df74c8e..1db4fdb 100644 --- a/assets/efs.go +++ b/assets/efs.go @@ -7,7 +7,7 @@ import ( "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/ebitenutil" "github.com/hajimehoshi/ebiten/v2/text/v2" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/kensonjohnson/roguelike-game-go/internal/logger" ) diff --git a/component/level.go b/component/level.go index e4d86f8..66b74f6 100644 --- a/component/level.go +++ b/component/level.go @@ -2,8 +2,8 @@ package component import ( "github.com/hajimehoshi/ebiten/v2" - "github.com/kensonjohnson/roguelike-game-go/config" - "github.com/kensonjohnson/roguelike-game-go/engine" + "github.com/kensonjohnson/roguelike-game-go/internal/config" + "github.com/kensonjohnson/roguelike-game-go/internal/engine" "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/yohamta/donburi" ) diff --git a/component/sprite.go b/component/sprite.go index 1d36e1b..2e71898 100644 --- a/component/sprite.go +++ b/component/sprite.go @@ -2,7 +2,7 @@ package component import ( "github.com/hajimehoshi/ebiten/v2" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi" ) diff --git a/items/valuables.go b/items/valuables.go index 2680d97..4b8d832 100644 --- a/items/valuables.go +++ b/items/valuables.go @@ -2,7 +2,7 @@ package items import ( "github.com/kensonjohnson/roguelike-game-go/assets" - "github.com/kensonjohnson/roguelike-game-go/engine" + "github.com/kensonjohnson/roguelike-game-go/internal/engine" ) type ValuableData struct { diff --git a/main.go b/main.go index b1b9813..90b8ef3 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,7 @@ import ( "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/assets" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/kensonjohnson/roguelike-game-go/system" "github.com/kensonjohnson/roguelike-game-go/system/scene" diff --git a/system/action/monster.go b/system/action/monster.go index 27be263..63b0798 100644 --- a/system/action/monster.go +++ b/system/action/monster.go @@ -3,7 +3,7 @@ package action import ( "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/engine/pathing" + "github.com/kensonjohnson/roguelike-game-go/internal/engine/pathing" "github.com/kensonjohnson/roguelike-game-go/system/combat" "github.com/yohamta/donburi/ecs" ) diff --git a/system/background.go b/system/background.go index 53d0be2..069df46 100644 --- a/system/background.go +++ b/system/background.go @@ -4,7 +4,7 @@ import ( "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi/ecs" ) diff --git a/system/camera.go b/system/camera.go index e4ef0c3..618b902 100644 --- a/system/camera.go +++ b/system/camera.go @@ -3,7 +3,7 @@ package system import ( "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi/ecs" ) diff --git a/system/combat/combat.go b/system/combat/combat.go index 10711f7..8735c4d 100644 --- a/system/combat/combat.go +++ b/system/combat/combat.go @@ -5,7 +5,7 @@ import ( "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/engine" + "github.com/kensonjohnson/roguelike-game-go/internal/engine" "github.com/yohamta/donburi" ) diff --git a/system/debug.go b/system/debug.go index 7e8da5f..bf44a9b 100644 --- a/system/debug.go +++ b/system/debug.go @@ -5,7 +5,7 @@ import ( "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/ebitenutil" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi/ecs" donburiDebug "github.com/yohamta/donburi/features/debug" ) diff --git a/system/minimap.go b/system/minimap.go index ded3bb6..c7ef800 100644 --- a/system/minimap.go +++ b/system/minimap.go @@ -7,7 +7,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/vector" "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi/ecs" ) diff --git a/system/render.go b/system/render.go index bf197ed..0e4c513 100644 --- a/system/render.go +++ b/system/render.go @@ -4,7 +4,7 @@ import ( "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/norendren/go-fov/fov" "github.com/yohamta/donburi" "github.com/yohamta/donburi/ecs" diff --git a/system/scene/titlescene.go b/system/scene/titlescene.go index c15933d..ef6c318 100644 --- a/system/scene/titlescene.go +++ b/system/scene/titlescene.go @@ -7,7 +7,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/text/v2" "github.com/kensonjohnson/roguelike-game-go/assets" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" ) type TitleScene struct { @@ -23,7 +23,6 @@ func (s *TitleScene) Update() { SceneManager.GoTo(CreateFirstLevel()) return } - } const scale = 4 diff --git a/system/ui.go b/system/ui.go index d3d22ab..3bad5e9 100644 --- a/system/ui.go +++ b/system/ui.go @@ -9,7 +9,7 @@ import ( "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/config" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi" "github.com/yohamta/donburi/ecs" "github.com/yohamta/donburi/filter" From 605271eb88d1201f1db7cf678d0aae8dbb4201f0 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:42:25 -0500 Subject: [PATCH 26/55] Move into system --- {layer => system/layer}/layer.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {layer => system/layer}/layer.go (100%) diff --git a/layer/layer.go b/system/layer/layer.go similarity index 100% rename from layer/layer.go rename to system/layer/layer.go From 5455cefe7e266585d6d15e203514d3708e87ef15 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:46:42 -0500 Subject: [PATCH 27/55] Rework transition system --- system/scene/scenemanager.go | 118 ++++++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 37 deletions(-) diff --git a/system/scene/scenemanager.go b/system/scene/scenemanager.go index e1c41e6..c01adb3 100644 --- a/system/scene/scenemanager.go +++ b/system/scene/scenemanager.go @@ -4,74 +4,118 @@ import ( "image/color" "github.com/hajimehoshi/ebiten/v2" - "github.com/kensonjohnson/roguelike-game-go/config" -) - -var ( - transitionFrom = ebiten.NewImage(config.ScreenWidth*config.TileWidth, config.ScreenHeight*config.TileHeight) - transitionTo = ebiten.NewImage(config.ScreenWidth*config.TileWidth, config.ScreenHeight*config.TileHeight) + "github.com/kensonjohnson/roguelike-game-go/archetype" + "github.com/kensonjohnson/roguelike-game-go/internal/config" + "github.com/kensonjohnson/roguelike-game-go/internal/logger" + "github.com/kensonjohnson/roguelike-game-go/items" + "github.com/yohamta/donburi" ) type Scene interface { Update() Draw(screen *ebiten.Image) + Setup(world donburi.World) + Teardown() + Ready() bool } const transitionMaxCount = 30 type SceneManagerData struct { - current Scene - next Scene - transitionCount int + current Scene + next Scene + fadeOut bool + transitionCount int + transitionTo *ebiten.Image + transitionFrom *ebiten.Image + transitionImagesCached bool + world donburi.World } -var SceneManager = &SceneManagerData{} +var SceneManager = &SceneManagerData{ + transitionTo: ebiten.NewImage(config.ScreenWidth*config.TileWidth, config.ScreenHeight*config.TileHeight), + transitionFrom: ebiten.NewImage(config.ScreenWidth*config.TileWidth, config.ScreenHeight*config.TileHeight), +} -func (s *SceneManagerData) Update() { - if s.transitionCount == 0 { - s.current.Update() +func (sm *SceneManagerData) Setup() { + if logger.DebugOn { + logger.DebugLogger.Println("SceneManager Setup") + } + sm.world = donburi.NewWorld() + archetype.CreateNewPlayer(sm.world, items.Weapons.BattleAxe, items.Armor.PlateArmor) + archetype.CreateNewCamera(sm.world) +} + +func (sm *SceneManagerData) Update() { + if sm.next == nil && sm.transitionCount == 0 { + sm.current.Update() return } - s.transitionCount-- - if s.transitionCount > 0 { + sm.transitionCount-- + if sm.transitionCount > 0 { return } - if s.next != nil { - s.current = s.next - s.next = nil - s.transitionCount = transitionMaxCount + if sm.fadeOut { + if logger.DebugOn { + logger.DebugLogger.Println("Running Teardown on current scene") + } + sm.fadeOut = false + sm.current.Teardown() + return + } + + if sm.transitionCount != 0 && sm.next != nil && sm.current.Ready() { + if logger.DebugOn { + logger.DebugLogger.Println("Running Setup on next scene") + } + sm.current = sm.next + sm.next = nil + sm.current.Setup(sm.world) + return + } + + if sm.transitionCount != 0 && sm.next == nil && sm.current.Ready() { + sm.transitionCount = transitionMaxCount + sm.transitionImagesCached = false } } -func (s *SceneManagerData) Draw(screen *ebiten.Image) { - if s.transitionCount == 0 { - s.current.Draw(screen) +func (sm *SceneManagerData) Draw(screen *ebiten.Image) { + + if sm.next == nil && sm.transitionCount == 0 { + sm.current.Draw(screen) return } - if s.next != nil { - s.current.Draw(transitionFrom) - transitionTo.Fill(color.Black) - screen.DrawImage(transitionFrom, nil) - } else { - s.current.Draw(transitionTo) - transitionFrom.Fill(color.Black) - screen.DrawImage(transitionFrom, nil) + if sm.fadeOut && !sm.transitionImagesCached { + sm.current.Draw(sm.transitionFrom) + sm.transitionTo.Fill(color.Black) + sm.transitionImagesCached = true + } + + if !sm.fadeOut && !sm.transitionImagesCached { + sm.current.Draw(sm.transitionTo) + sm.transitionFrom.Fill(color.Black) + sm.transitionImagesCached = true } - alpha := 1 - float32(s.transitionCount)/float32(transitionMaxCount) + + screen.DrawImage(sm.transitionFrom, nil) + alpha := 1 - float32(sm.transitionCount)/float32(transitionMaxCount) op := &ebiten.DrawImageOptions{} op.ColorScale.ScaleAlpha(alpha) - screen.DrawImage(transitionTo, op) + screen.DrawImage(sm.transitionTo, op) } -func (s *SceneManagerData) GoTo(scene Scene) { - if s.current == nil { - s.current = scene +func (sm *SceneManagerData) GoTo(scene Scene) { + if sm.current == nil { + sm.current = scene } else { - s.next = scene - s.transitionCount = transitionMaxCount + sm.next = scene + sm.fadeOut = true + sm.transitionCount = transitionMaxCount + sm.transitionImagesCached = false } } From df5c905bd02860adaf19f6b3fc9594b137ba0a8d Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:47:01 -0500 Subject: [PATCH 28/55] Call setup on scenemanager during init --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index 90b8ef3..19a2987 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ type Game struct { func (g *Game) configure() { g.sceneManager = scene.SceneManager + g.sceneManager.Setup() g.sceneManager.GoTo(&scene.TitleScene{ ImageBackground: assets.Floor, PixelWidth: config.ScreenWidth * config.TileWidth, From 350a1c19d7d46ddf1c09e85ab165ea080799e9b7 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:47:22 -0500 Subject: [PATCH 29/55] Decouple camera from player position --- archetype/camera.go | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/archetype/camera.go b/archetype/camera.go index bf93e7b..b7850e7 100644 --- a/archetype/camera.go +++ b/archetype/camera.go @@ -4,7 +4,6 @@ import ( "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/config" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/setanarut/kamera/v2" "github.com/yohamta/donburi" ) @@ -12,22 +11,14 @@ import ( var CameraTag = donburi.NewTag("camera") func CreateNewCamera(world donburi.World) { - var entry *donburi.Entry - var ok bool - if entry, ok = PlayerTag.First(world); !ok { - logger.ErrorLogger.Panic("CreateNewCamera failed: Player not found") - } - playerPosition := component.Position.Get(entry) - - entry = world.Entry(world.Create( + entry := world.Entry(world.Create( CameraTag, component.Camera, )) cameraData := &component.CameraData{ MainCamera: kamera.NewCamera( - float64((playerPosition.X*config.TileWidth)+config.TileWidth/2), - float64((playerPosition.Y*config.TileHeight)+config.TileHeight/2), + 0, 0, config.ScreenWidth*config.TileWidth, (config.ScreenHeight-config.UIHeight)*config.TileHeight, ), From 24f8850d89d61ec1f423b8a975a73a940116a0a5 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:47:42 -0500 Subject: [PATCH 30/55] Remove player creation from level gen --- archetype/level.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/archetype/level.go b/archetype/level.go index 4c2d6b8..c40f172 100644 --- a/archetype/level.go +++ b/archetype/level.go @@ -165,13 +165,9 @@ func min(x, y int) int { func seedRooms(world donburi.World, level *component.LevelData) { for index, room := range level.Rooms { if index == 0 { - CreateNewPlayer( - world, - level, - room, - items.Weapons.BattleAxe, - items.Armor.PlateArmor, - ) + playerEntry := PlayerTag.MustFirst(world) + playerPosition := component.Position.Get(playerEntry) + playerPosition.X, playerPosition.Y = room.Center() } else { CreateMonster(world, level, room) addRandomPickupsToRoom(world, level, room, 3) From f0f6f1086bad5fa25055e1f25375aef1b426af5f Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:48:18 -0500 Subject: [PATCH 31/55] Return entry from create func --- archetype/player.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/archetype/player.go b/archetype/player.go index 843d2e8..06b0cb2 100644 --- a/archetype/player.go +++ b/archetype/player.go @@ -3,7 +3,6 @@ package archetype import ( "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" - "github.com/kensonjohnson/roguelike-game-go/internal/engine" "github.com/kensonjohnson/roguelike-game-go/items" "github.com/norendren/go-fov/fov" "github.com/yohamta/donburi" @@ -13,11 +12,9 @@ var PlayerTag = donburi.NewTag("player") func CreateNewPlayer( world donburi.World, - level *component.LevelData, - startingRoom engine.Rect, weapon items.WeaponData, armorId items.ArmorData, -) { +) *donburi.Entry { player := world.Entry(world.Create( PlayerTag, component.Position, @@ -32,17 +29,11 @@ func CreateNewPlayer( component.Defense, )) - // Set starting position - startingX, startingY := startingRoom.Center() - position := component.PositionData{ - X: startingX, - Y: startingY, - } + position := component.PositionData{} component.Position.SetValue(player, position) // Update player's field of view vision := component.FovData{VisibleTiles: fov.New()} - vision.VisibleTiles.Compute(level, startingX, startingY, 8) component.Fov.SetValue(player, vision) // Set sprite @@ -95,4 +86,5 @@ func CreateNewPlayer( defense := component.Defense.Get(equipment.Armor) component.Defense.SetValue(player, *defense) + return player } From 402e31d70b1572538afa1f1d8ed5b41dee48f230 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:48:50 -0500 Subject: [PATCH 32/55] De-nest logic --- system/turn.go | 55 +++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/system/turn.go b/system/turn.go index 9b969e1..9154b48 100644 --- a/system/turn.go +++ b/system/turn.go @@ -60,38 +60,39 @@ func (td *TurnData) Update(ecs *ecs.ECS) { } - if td.TurnCounter > 12 { - // Reset the progress of all sprites - for entry = range component.Sprite.Iter(ecs.World) { - sprite := component.Sprite.Get(entry) - sprite.SetProgress(0) - sprite.Animating = false - sprite.OffestX = 0 - sprite.OffestY = 0 - } + if td.TurnCounter < 13 { + return + } + // Reset the progress of all sprites + for entry = range component.Sprite.Iter(ecs.World) { + sprite := component.Sprite.Get(entry) + sprite.SetProgress(0) + sprite.Animating = false + sprite.OffestX = 0 + sprite.OffestY = 0 + } - entry = archetype.PlayerTag.MustFirst(ecs.World) - playerPosition := component.Position.Get(entry) - playerMessages := component.UserMessage.Get(entry) + entry = archetype.PlayerTag.MustFirst(ecs.World) + playerPosition := component.Position.Get(entry) + playerMessages := component.UserMessage.Get(entry) - for entry = range archetype.PickupTag.Iter(ecs.World) { - if !entry.HasComponent(component.Position) { - continue - } - pickupPosition := component.Position.Get(entry) - - if pickupPosition.X == playerPosition.X && pickupPosition.Y == playerPosition.Y { - archetype.RemoveItemFromWorld(entry) - itemName := component.Name.Get(entry) - playerMessages.WorldInteractionMessage = fmt.Sprintf("Picked up %s!", itemName.Value) - // TODO: place in player's inventory - } + for entry = range archetype.PickupTag.Iter(ecs.World) { + if !entry.HasComponent(component.Position) { + continue } + pickupPosition := component.Position.Get(entry) - level.Redraw = true - td.progressTurnState() - td.resetCounter() + if pickupPosition.X == playerPosition.X && pickupPosition.Y == playerPosition.Y { + archetype.RemoveItemFromWorld(entry) + itemName := component.Name.Get(entry) + playerMessages.WorldInteractionMessage = fmt.Sprintf("Picked up %s!", itemName.Value) + // TODO: place in player's inventory + } } + + level.Redraw = true + td.progressTurnState() + td.resetCounter() } if td.TurnState == PlayerTurn { From 35baa806102f9fc6d899894d092c4f93f2289bd4 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:49:05 -0500 Subject: [PATCH 33/55] Add setup and teardown funcs --- system/scene/levelscene.go | 285 +++++++++++++++++++++---------------- 1 file changed, 165 insertions(+), 120 deletions(-) diff --git a/system/scene/levelscene.go b/system/scene/levelscene.go index c8db01f..c44d1e2 100644 --- a/system/scene/levelscene.go +++ b/system/scene/levelscene.go @@ -5,161 +5,206 @@ import ( "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/event" + "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/kensonjohnson/roguelike-game-go/internal/logger" - "github.com/kensonjohnson/roguelike-game-go/items" - "github.com/kensonjohnson/roguelike-game-go/layer" "github.com/kensonjohnson/roguelike-game-go/system" + "github.com/kensonjohnson/roguelike-game-go/system/layer" "github.com/yohamta/donburi" "github.com/yohamta/donburi/ecs" ) type LevelScene struct { - ecs ecs.ECS + ecs ecs.ECS + ready bool } -func (level *LevelScene) Update() { - level.ecs.Update() - event.ProgressLevelEvent.ProcessEvents(level.ecs.World) +func (ls *LevelScene) Update() { + ls.ecs.Update() + event.ProgressLevelEvent.ProcessEvents(ls.ecs.World) } -func (level *LevelScene) Draw(screen *ebiten.Image) { - level.ecs.Draw(screen) +func (ls *LevelScene) Draw(screen *ebiten.Image) { + ls.ecs.Draw(screen) } -func CreateFirstLevel() *LevelScene { - level := &LevelScene{} - level.configureECS(createWorld()) - return level +func (ls *LevelScene) Ready() bool { + return ls.ready } -func createWorld() donburi.World { - world := donburi.NewWorld() +func (ls *LevelScene) Setup(world donburi.World) { + ls.ready = false - // Create current level - archetype.GenerateLevel(world) + if logger.DebugOn { + logger.DebugLogger.Println("LevelScene setup") + } - // Create the UI - archetype.CreateNewUI(world) + go func() { - // Create the camera - archetype.CreateNewCamera(world) + levelData := archetype.GenerateLevel(world) - return world -} + if _, ok := archetype.UITag.First(world); !ok { + archetype.CreateNewUI(world) + } -func progressLevel(world donburi.World, eventData event.ProgressLevel) { + playerEntry := archetype.PlayerTag.MustFirst(world) + playerPosition := component.Position.Get(playerEntry) + startingRoom := levelData.Rooms[0] + playerPosition.X, playerPosition.Y = startingRoom.Center() + + playerSprite := component.Sprite.Get(playerEntry) + playerSprite.OffestX = 0 + playerSprite.OffestY = 0 - // Create a new world - newWorld := createWorld() + component.Fov.Get(playerEntry). + VisibleTiles.Compute(levelData, playerPosition.X, playerPosition.Y, 8) - // Apply the player's data to the new world - copyPlayerInstance(world, newWorld) + cameraEntry := archetype.CameraTag.MustFirst(world) + camera := component.Camera.Get(cameraEntry) + camera.MainCamera.Lerp = false + camera.MainCamera.LookAt( + float64((playerPosition.X*config.TileWidth)+config.TileWidth/2), + float64((playerPosition.Y*config.TileHeight)+config.TileHeight/2), + ) + camera.MainCamera.Lerp = true - level := &LevelScene{} - level.configureECS(newWorld) + ls.configureECS(world) - SceneManager.GoTo(level) + ls.ready = true + }() } -func (l *LevelScene) configureECS(world donburi.World) { - l.ecs = *ecs.NewECS(world) - // Add systems - l.ecs.AddSystem(system.Camera.Update) - l.ecs.AddSystem(system.Turn.Update) - l.ecs.AddSystem(system.UI.Update) +func (ls *LevelScene) Teardown() { + ls.ready = false - // Add renderers - l.ecs.AddRenderer(layer.Background, system.Render.DrawBackground) - l.ecs.AddRenderer(layer.Foreground, system.Render.Draw) - l.ecs.AddRenderer(layer.UI, system.UI.Draw) - l.ecs.AddRenderer(layer.UI, system.DrawMinimap) - if system.Debug.On { - l.ecs.AddRenderer(layer.UI, system.Debug.Draw) + if logger.DebugOn { + logger.DebugLogger.Println("LevelScene teardown") } - // Add event listeners - event.ProgressLevelEvent.Subscribe(l.ecs.World, progressLevel) + go func() { + archetype.LevelTag.MustFirst(ls.ecs.World).Remove() + + for entry := range archetype.MonsterTag.Iter(ls.ecs.World) { + if logger.DebugOn { + logger.DebugLogger.Println("Removing entry: ", entry.String()) + } + entry.Remove() + } + + for entry := range archetype.PickupTag.Iter(ls.ecs.World) { + if logger.DebugOn { + logger.DebugLogger.Println("Removing entry: ", entry.String()) + } + entry.Remove() + } + + ls.ready = true + }() } -func copyPlayerInstance( - oldWorld donburi.World, - newWorld donburi.World, -) { - currentPlayerEntry := archetype.PlayerTag.MustFirst(oldWorld) - currentPlayerHealth := component.Health.Get(currentPlayerEntry) - currentPlayerEquipment := component.Equipment.Get(currentPlayerEntry) - - playerEntry := archetype.PlayerTag.MustFirst(newWorld) - component.Health.SetValue(playerEntry, *currentPlayerHealth) - component.Equipment.SetValue(playerEntry, component.EquipmentData{ - Weapon: copyWeapon(newWorld, currentPlayerEquipment.Weapon), - // Sheild: currentPlayerEquipment.Sheild, - // Gloves: currentPlayerEquipment.Gloves, - Armor: copyArmor(newWorld, currentPlayerEquipment.Armor), - // Boots: currentPlayerEquipment.Boots, - }) - +func progressLevel(world donburi.World, eventData event.ProgressLevel) { + newLevelScene := &LevelScene{} + SceneManager.GoTo(newLevelScene) } -func copyWeapon(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { - if entry == nil { - logger.ErrorLogger.Panic("Entry is nil when copying weapon data") - } - name := component.Name.Get(entry).Value - sprite := component.Sprite.Get(entry).Image - if sprite == nil { - logger.ErrorLogger.Panic("Sprite missing when copying weapon data") - } - actionText := component.ActionText.Get(entry).Value - attack := component.Attack.Get(entry) - if attack == nil { - logger.ErrorLogger.Panic("Attack data missing when copying weapon data") - } +func (ls *LevelScene) configureECS(world donburi.World) { + ls.ecs = *ecs.NewECS(world) + // Add systems + ls.ecs.AddSystem(system.Camera.Update) + ls.ecs.AddSystem(system.Turn.Update) + ls.ecs.AddSystem(system.UI.Update) - newWeaponData := items.WeaponData{ - ItemData: items.ItemData{ - Name: name, - Sprite: sprite, - }, - ActionText: actionText, - MinimumDamage: attack.MinimumDamage, - MaximumDamage: attack.MaximumDamage, - ToHitBonus: attack.ToHitBonus, + // Add renderers + ls.ecs.AddRenderer(layer.Background, system.Render.DrawBackground) + ls.ecs.AddRenderer(layer.Foreground, system.Render.Draw) + ls.ecs.AddRenderer(layer.UI, system.UI.Draw) + ls.ecs.AddRenderer(layer.UI, system.DrawMinimap) + if system.Debug.On { + ls.ecs.AddRenderer(layer.UI, system.Debug.Draw) } - newWeapon := archetype.CreateNewWeapon(newWorld, newWeaponData) - if newWeapon == nil { - logger.ErrorLogger.Panic("Failed to create new weapon when copying weapon data") - } - return newWeapon + // Add event listeners + event.ProgressLevelEvent.Subscribe(ls.ecs.World, progressLevel) } -func copyArmor(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { - if entry == nil { - logger.ErrorLogger.Panic("Entry is nil when copying armor data") - } - name := component.Name.Get(entry).Value - sprite := component.Sprite.Get(entry).Image - if sprite == nil { - logger.ErrorLogger.Panic("Sprite missing when copying armor data") - } - defense := component.Defense.Get(entry) - if defense == nil { - logger.ErrorLogger.Panic("Defense data missing when copying armor data") - } - - newArmorData := items.ArmorData{ - ItemData: items.ItemData{ - Name: name, - Sprite: sprite, - }, - Defense: defense.Defense, - ArmorClass: defense.ArmorClass, - } - - newArmor := archetype.CreateNewArmor(newWorld, newArmorData) - if newArmor == nil { - logger.ErrorLogger.Panic("Failed to create new armor when copying armor data") - } - return newArmor -} +// func copyPlayerInstance( +// oldWorld donburi.World, +// newWorld donburi.World, +// ) { +// currentPlayerEntry := archetype.PlayerTag.MustFirst(oldWorld) +// currentPlayerHealth := component.Health.Get(currentPlayerEntry) +// currentPlayerEquipment := component.Equipment.Get(currentPlayerEntry) + +// playerEntry := archetype.PlayerTag.MustFirst(newWorld) +// component.Health.SetValue(playerEntry, *currentPlayerHealth) +// component.Equipment.SetValue(playerEntry, component.EquipmentData{ +// Weapon: copyWeapon(newWorld, currentPlayerEquipment.Weapon), +// // Sheild: currentPlayerEquipment.Sheild, +// // Gloves: currentPlayerEquipment.Gloves, +// Armor: copyArmor(newWorld, currentPlayerEquipment.Armor), +// // Boots: currentPlayerEquipment.Boots, +// }) + +// } + +// func copyWeapon(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { +// if entry == nil { +// logger.ErrorLogger.Panic("Entry is nil when copying weapon data") +// } +// name := component.Name.Get(entry).Value +// sprite := component.Sprite.Get(entry).Image +// if sprite == nil { +// logger.ErrorLogger.Panic("Sprite missing when copying weapon data") +// } +// actionText := component.ActionText.Get(entry).Value +// attack := component.Attack.Get(entry) +// if attack == nil { +// logger.ErrorLogger.Panic("Attack data missing when copying weapon data") +// } + +// newWeaponData := items.WeaponData{ +// ItemData: items.ItemData{ +// Name: name, +// Sprite: sprite, +// }, +// ActionText: actionText, +// MinimumDamage: attack.MinimumDamage, +// MaximumDamage: attack.MaximumDamage, +// ToHitBonus: attack.ToHitBonus, +// } + +// newWeapon := archetype.CreateNewWeapon(newWorld, newWeaponData) +// if newWeapon == nil { +// logger.ErrorLogger.Panic("Failed to create new weapon when copying weapon data") +// } +// return newWeapon +// } + +// func copyArmor(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { +// if entry == nil { +// logger.ErrorLogger.Panic("Entry is nil when copying armor data") +// } +// name := component.Name.Get(entry).Value +// sprite := component.Sprite.Get(entry).Image +// if sprite == nil { +// logger.ErrorLogger.Panic("Sprite missing when copying armor data") +// } +// defense := component.Defense.Get(entry) +// if defense == nil { +// logger.ErrorLogger.Panic("Defense data missing when copying armor data") +// } + +// newArmorData := items.ArmorData{ +// ItemData: items.ItemData{ +// Name: name, +// Sprite: sprite, +// }, +// Defense: defense.Defense, +// ArmorClass: defense.ArmorClass, +// } + +// newArmor := archetype.CreateNewArmor(newWorld, newArmorData) +// if newArmor == nil { +// logger.ErrorLogger.Panic("Failed to create new armor when copying armor data") +// } +// return newArmor +// } From 0044c18ca32b473601083dc2964bba30b68dbf69 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:49:27 -0500 Subject: [PATCH 34/55] Add needed methods for SceneManager --- system/scene/titlescene.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/system/scene/titlescene.go b/system/scene/titlescene.go index ef6c318..906f8e7 100644 --- a/system/scene/titlescene.go +++ b/system/scene/titlescene.go @@ -8,6 +8,8 @@ import ( "github.com/hajimehoshi/ebiten/v2/text/v2" "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/internal/config" + "github.com/kensonjohnson/roguelike-game-go/internal/logger" + "github.com/yohamta/donburi" ) type TitleScene struct { @@ -15,12 +17,13 @@ type TitleScene struct { ImageBackground *ebiten.Image PixelWidth int PixelHeight int + ready bool } func (s *TitleScene) Update() { s.count++ if inpututil.IsKeyJustPressed(ebiten.KeySpace) { - SceneManager.GoTo(CreateFirstLevel()) + SceneManager.GoTo(&LevelScene{}) return } } @@ -56,6 +59,24 @@ func (s *TitleScene) Draw(screen *ebiten.Image) { drawOrc(screen, x, y) } +func (s *TitleScene) Ready() bool { + return s.ready +} + +func (s *TitleScene) Teardown() { + if logger.DebugOn { + logger.DebugLogger.Println("TitleScene teardown") + } + s.ready = true +} + +func (s *TitleScene) Setup(world donburi.World) { + if logger.DebugOn { + logger.DebugLogger.Println("TitleScene setup") + } + s.ready = true +} + func (s *TitleScene) drawTitleBackground(screen *ebiten.Image, count int) { options := &ebiten.DrawImageOptions{} From 7eb2bc19c4e5bb4d30e439e713a64bc3f0d0e8e3 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:36:05 -0500 Subject: [PATCH 35/55] Update comment --- component/sprite.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/component/sprite.go b/component/sprite.go index 2e71898..54f5dff 100644 --- a/component/sprite.go +++ b/component/sprite.go @@ -15,8 +15,8 @@ type SpriteData struct { Animating bool } -// Returns the X and Y offset for the current animation frame. TODO: Add -// support for animation frames. +// Returns the X and Y offset for the current animation frame. +// TODO: Add support for animation frames. func (sd *SpriteData) GetAnimationStep() (float64, float64) { offsetX := (1 - sd.progress) * config.TileWidth * float64(sd.OffestX) offsetY := (1 - sd.progress) * config.TileHeight * float64(sd.OffestY) From ead5db97fd632f604c509a2a55c170834aa0492a Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Sat, 7 Sep 2024 09:42:41 -0500 Subject: [PATCH 36/55] Replace make with just --- Justfile | 54 +++++++++++++++++++++++++++++ Makefile | 101 ------------------------------------------------------- 2 files changed, 54 insertions(+), 101 deletions(-) create mode 100644 Justfile delete mode 100644 Makefile diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..695a4a2 --- /dev/null +++ b/Justfile @@ -0,0 +1,54 @@ +set quiet := true + +MAIN_PACKAGE_PATH := "." +BINARY_NAME := "rougelike-demo" + +[private] +help: + just --list --unsorted + +_confirm: + echo -n 'Are you sure? [y/N] ' && read ans && [ $${ans:-N} = y ] + +# Run dev server +dev: + go run . -debug + +# Run all Go test files +test: + go test -v -race -buildvcs ./... + +# Prepare and audit code, then push to Git Remote +push: tidy audit no-dirty + git push + +# Verify and Vet all Go files in project +audit: + go mod verify + go vet ./... + +[private] +no-dirty: + git diff --exit-code + +# Run formatter and tidy over all Go files in project +tidy: + go fmt ./... + go mod tidy -v + +# Build for current OS/Arch +build: + # Include additional build steps, like TypeScript, SCSS or Tailwind compilation here... + go build -o=/tmp/bin/{{ BINARY_NAME }} {{ MAIN_PACKAGE_PATH }} + +# Build for current OS/Arch and run the resulting binary +run: build + /tmp/bin/{{ BINARY_NAME }} + +# Matrix build for all OS/Architectures +production-deploy: _confirm tidy audit + GOOS=darwin GOARCH=arm64 go build -ldflags='-s' -o=/tmp/bin/macos_arm64/${BINARY_NAME} ${MAIN_PACKAGE_PATH} + GOOS=windows GOARCH=amd64 go build -ldflags='-s' -o=/tmp/bin/windows_amd64/${BINARY_NAME} ${MAIN_PACKAGE_PATH} + GOOS=windows GOARCH=arm go build -ldflags='-s' -o=/tmp/bin/windows_arm/${BINARY_NAME} ${MAIN_PACKAGE_PATH} + GOOS=windows GOARCH=arm64 go build -ldflags='-s' -o=/tmp/bin/windows_arm64/${BINARY_NAME} ${MAIN_PACKAGE_PATH} + # Include additional deployment steps here... diff --git a/Makefile b/Makefile deleted file mode 100644 index 7494d9b..0000000 --- a/Makefile +++ /dev/null @@ -1,101 +0,0 @@ -# Change these variables as necessary. -MAIN_PACKAGE_PATH := . -BINARY_NAME := rougelike-demo - -# ==================================================================================== # -# HELPERS -# ==================================================================================== # - -## help: print this help message -.PHONY: help -help: - @echo 'Usage:' - @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' - -.PHONY: confirm -confirm: - @echo -n 'Are you sure? [y/N] ' && read ans && [ $${ans:-N} = y ] - -.PHONY: no-dirty -no-dirty: - git diff --exit-code - - -# ==================================================================================== # -# QUALITY CONTROL -# ==================================================================================== # - -## tidy: format code and tidy modfile -.PHONY: tidy -tidy: - go fmt ./... - go mod tidy -v - -## audit: run quality control checks -.PHONY: audit -audit: - go mod verify - go vet ./... - go run golang.org/x/vuln/cmd/govulncheck@latest ./... - go test -race -buildvcs -vet=off ./... - - -# ==================================================================================== # -# DEVELOPMENT -# ==================================================================================== # - -## Run with dev flags -.PHONY: dev -dev: - go run . -debug - -## test: run all tests -.PHONY: test -test: - go test -v -race -buildvcs ./... - -## test/cover: run all tests and display coverage -.PHONY: test/cover -test/cover: - go test -v -race -buildvcs -coverprofile=/tmp/coverage.out ./... - go tool cover -html=/tmp/coverage.out - -## build: build the application -.PHONY: build -build: - # Include additional build steps, like TypeScript, SCSS or Tailwind compilation here... - go build -o=/tmp/bin/${BINARY_NAME} ${MAIN_PACKAGE_PATH} - -## run: run the rapplication -.PHONY: run -run: build - /tmp/bin/${BINARY_NAME} - -## run/live: run the application with reloading on file changes -.PHONY: run/live -run/live: - go run github.com/cosmtrek/air \ - --build.cmd "make build" --build.bin "/tmp/bin/${BINARY_NAME}" --build.delay "100" \ - --build.exclude_dir "" \ - --build.include_ext "go, tpl, tmpl, html, css, scss, js, ts, sql, jpeg, jpg, gif, png, bmp, svg, webp, ico" \ - --misc.clean_on_exit "true" - - -# ==================================================================================== # -# OPERATIONS -# ==================================================================================== # - -## push: push changes to the remote Git repository -.PHONY: push -push: tidy audit no-dirty - git push - -## production/deploy: deploy the application to production -.PHONY: production/deploy -production/deploy: confirm tidy audit - GOOS=darwin GOARCH=arm64 go build -ldflags='-s' -o=/tmp/bin/macos_arm64/${BINARY_NAME} ${MAIN_PACKAGE_PATH} - GOOS=windows GOARCH=amd64 go build -ldflags='-s' -o=/tmp/bin/windows_amd64/${BINARY_NAME} ${MAIN_PACKAGE_PATH} - GOOS=windows GOARCH=arm go build -ldflags='-s' -o=/tmp/bin/windows_arm/${BINARY_NAME} ${MAIN_PACKAGE_PATH} - GOOS=windows GOARCH=arm64 go build -ldflags='-s' -o=/tmp/bin/windows_arm64/${BINARY_NAME} ${MAIN_PACKAGE_PATH} - # Include additional deployment steps here... - \ No newline at end of file From 8ccb6f66df9af53e2d5e13428af416960bc29669 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Sat, 7 Sep 2024 10:01:51 -0500 Subject: [PATCH 37/55] Refactor tags to seperate package --- archetype/armor.go | 7 ++- archetype/camera.go | 5 +- archetype/consumable.go | 7 ++- archetype/item.go | 13 ++---- archetype/level.go | 9 ++-- archetype/monster.go | 5 +- archetype/tags/tags.go | 17 +++++++ archetype/ui.go | 7 ++- archetype/valuable.go | 5 +- archetype/weapon.go | 7 ++- system/action/monster.go | 8 ++-- system/action/player.go | 8 ++-- system/background.go | 8 ++-- system/camera.go | 6 +-- system/combat/combat.go | 4 +- system/minimap.go | 8 ++-- system/render.go | 10 ++-- system/scene/levelscene.go | 96 +++----------------------------------- system/ui.go | 4 +- 19 files changed, 79 insertions(+), 155 deletions(-) create mode 100644 archetype/tags/tags.go diff --git a/archetype/armor.go b/archetype/armor.go index 1b594fd..ee66ad9 100644 --- a/archetype/armor.go +++ b/archetype/armor.go @@ -1,18 +1,17 @@ package archetype import ( + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) -var ArmorTag = donburi.NewTag("armor") - func CreateNewArmor(world donburi.World, armorData items.ArmorData) *donburi.Entry { entry := CreateNewItem(world, &armorData.ItemData) // Mark as an armor - entry.AddComponent(ArmorTag) + entry.AddComponent(tags.ArmorTag) // Add defense data entry.AddComponent(component.Defense) @@ -26,5 +25,5 @@ func CreateNewArmor(world donburi.World, armorData items.ArmorData) *donburi.Ent } func IsArmor(entry *donburi.Entry) bool { - return entry.HasComponent(ArmorTag) + return entry.HasComponent(tags.ArmorTag) } diff --git a/archetype/camera.go b/archetype/camera.go index b7850e7..c008e80 100644 --- a/archetype/camera.go +++ b/archetype/camera.go @@ -2,17 +2,16 @@ package archetype import ( "github.com/hajimehoshi/ebiten/v2" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/setanarut/kamera/v2" "github.com/yohamta/donburi" ) -var CameraTag = donburi.NewTag("camera") - func CreateNewCamera(world donburi.World) { entry := world.Entry(world.Create( - CameraTag, + tags.CameraTag, component.Camera, )) diff --git a/archetype/consumable.go b/archetype/consumable.go index f39fdb1..53fe570 100644 --- a/archetype/consumable.go +++ b/archetype/consumable.go @@ -1,19 +1,18 @@ package archetype import ( + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) -var ConsumableTag = donburi.NewTag("consumable") - func CreateNewConsumable(world donburi.World, consumableData items.ConsumableData) *donburi.Entry { entry := CreateNewItem(world, &consumableData.ItemData) // Mark as a consumable - entry.AddComponent(ConsumableTag) + entry.AddComponent(tags.ConsumableTag) // Add heal data entry.AddComponent(component.Heal) @@ -26,5 +25,5 @@ func CreateNewConsumable(world donburi.World, consumableData items.ConsumableDat } func IsConsumable(entry *donburi.Entry) bool { - return entry.HasComponent(ConsumableTag) + return entry.HasComponent(tags.ConsumableTag) } diff --git a/archetype/item.go b/archetype/item.go index 8258913..bda5de8 100644 --- a/archetype/item.go +++ b/archetype/item.go @@ -3,16 +3,15 @@ package archetype import ( "errors" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) -var ItemTag = donburi.NewTag("item") - func CreateNewItem(world donburi.World, itemData *items.ItemData) *donburi.Entry { entry := world.Entry(world.Create( - ItemTag, + tags.ItemTag, component.Name, component.Sprite, )) @@ -31,17 +30,15 @@ func CreateNewItem(world donburi.World, itemData *items.ItemData) *donburi.Entry } func isItem(entry *donburi.Entry) bool { - return entry.HasComponent(ItemTag) + return entry.HasComponent(tags.ItemTag) } -var PickupTag = donburi.NewTag("pickup") - func PlaceItemInWorld(entry *donburi.Entry, x, y int, discoverable bool) error { if !isItem(entry) { return errors.New("entry is not an Item Entity") } - entry.AddComponent(PickupTag) + entry.AddComponent(tags.PickupTag) entry.AddComponent(component.Position) position := component.PositionData{ @@ -60,7 +57,7 @@ func PlaceItemInWorld(entry *donburi.Entry, x, y int, discoverable bool) error { } func RemoveItemFromWorld(entry *donburi.Entry) { - entry.RemoveComponent(PickupTag) + entry.RemoveComponent(tags.PickupTag) entry.RemoveComponent(component.Position) if entry.HasComponent(component.Discoverable) { entry.RemoveComponent(component.Discoverable) diff --git a/archetype/level.go b/archetype/level.go index c40f172..6ac6cbd 100644 --- a/archetype/level.go +++ b/archetype/level.go @@ -1,6 +1,7 @@ package archetype import ( + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/config" @@ -12,12 +13,10 @@ import ( const levelHeight = config.ScreenHeight - config.UIHeight -var LevelTag = donburi.NewTag("level") - // Creates a new Dungeon func GenerateLevel(world donburi.World) *component.LevelData { entry := world.Entry(world.Create( - LevelTag, + tags.LevelTag, component.Level, )) @@ -165,7 +164,7 @@ func min(x, y int) int { func seedRooms(world donburi.World, level *component.LevelData) { for index, room := range level.Rooms { if index == 0 { - playerEntry := PlayerTag.MustFirst(world) + playerEntry := tags.PlayerTag.MustFirst(world) playerPosition := component.Position.Get(playerEntry) playerPosition.X, playerPosition.Y = room.Center() } else { @@ -206,7 +205,7 @@ func addRandomPickupsToRoom( } spotTaken := false - for entry := range PickupTag.Iter(world) { + for entry := range tags.PickupTag.Iter(world) { position := component.Position.Get(entry) if position.X == x && position.Y == y { spotTaken = true diff --git a/archetype/monster.go b/archetype/monster.go index 7a2079d..b7ff3e6 100644 --- a/archetype/monster.go +++ b/archetype/monster.go @@ -1,6 +1,7 @@ package archetype import ( + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/engine" @@ -9,8 +10,6 @@ import ( "github.com/yohamta/donburi" ) -var MonsterTag = donburi.NewTag("monster") - func CreateMonster(world donburi.World, level *component.LevelData, room engine.Rect) { innerRoomWidth := room.X2 - room.X1 - 2 @@ -25,7 +24,7 @@ func CreateMonster(world donburi.World, level *component.LevelData, room engine. } monster := world.Entry(world.Create( - MonsterTag, + tags.MonsterTag, component.Position, component.Sprite, component.Name, diff --git a/archetype/tags/tags.go b/archetype/tags/tags.go new file mode 100644 index 0000000..544f3a8 --- /dev/null +++ b/archetype/tags/tags.go @@ -0,0 +1,17 @@ +package tags + +import "github.com/yohamta/donburi" + +var ( + ArmorTag = donburi.NewTag("armor") + CameraTag = donburi.NewTag("camera") + ConsumableTag = donburi.NewTag("consumable") + ItemTag = donburi.NewTag("item") + LevelTag = donburi.NewTag("level") + MonsterTag = donburi.NewTag("monster") + PickupTag = donburi.NewTag("pickup") + PlayerTag = donburi.NewTag("player") + UITag = donburi.NewTag("ui") + ValuableTag = donburi.NewTag("valuable") + WeaponTag = donburi.NewTag("weapon") +) diff --git a/archetype/ui.go b/archetype/ui.go index 41b633a..3fbce2f 100644 --- a/archetype/ui.go +++ b/archetype/ui.go @@ -1,17 +1,16 @@ package archetype import ( + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi" ) -var UITag = donburi.NewTag("ui") - func CreateNewUI(world donburi.World) { entity := world.Create( - UITag, + tags.UITag, component.UI, ) entry := world.Entry(entity) @@ -33,7 +32,7 @@ func CreateNewUI(world donburi.World) { // Configure player HUD playerHUDTopPixel := (config.ScreenHeight * config.TileHeight) - 220 - playerEntry := PlayerTag.MustFirst(world) + playerEntry := tags.PlayerTag.MustFirst(world) ui.PlayerHUD.Position = component.PositionData{ X: config.ScreenWidth * config.TileWidth / 2, Y: playerHUDTopPixel, diff --git a/archetype/valuable.go b/archetype/valuable.go index 0cc0a3d..0347496 100644 --- a/archetype/valuable.go +++ b/archetype/valuable.go @@ -1,17 +1,16 @@ package archetype import ( + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) -var ValuableTag = donburi.NewTag("valuable") - func CreateNewValuable(world donburi.World, valuableData *items.ValuableData) *donburi.Entry { entry := CreateNewItem(world, &valuableData.ItemData) - entry.AddComponent(ValuableTag) + entry.AddComponent(tags.ValuableTag) entry.AddComponent(component.Value) value := component.ValueData{ diff --git a/archetype/weapon.go b/archetype/weapon.go index f34aeb8..e1b4540 100644 --- a/archetype/weapon.go +++ b/archetype/weapon.go @@ -1,18 +1,17 @@ package archetype import ( + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) -var WeaponTag = donburi.NewTag("weapon") - func CreateNewWeapon(world donburi.World, weaponData items.WeaponData) *donburi.Entry { entry := CreateNewItem(world, &weaponData.ItemData) // Mark as a weapon - entry.AddComponent(WeaponTag) + entry.AddComponent(tags.WeaponTag) // Add attack information entry.AddComponent(component.Attack) @@ -34,5 +33,5 @@ func CreateNewWeapon(world donburi.World, weaponData items.WeaponData) *donburi. } func IsWeapon(entry *donburi.Entry) bool { - return entry.HasComponent(WeaponTag) + return entry.HasComponent(tags.WeaponTag) } diff --git a/system/action/monster.go b/system/action/monster.go index 63b0798..e60e7f9 100644 --- a/system/action/monster.go +++ b/system/action/monster.go @@ -1,7 +1,7 @@ package action import ( - "github.com/kensonjohnson/roguelike-game-go/archetype" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/engine/pathing" "github.com/kensonjohnson/roguelike-game-go/system/combat" @@ -10,14 +10,14 @@ import ( func TakeMonsterAction(ecs *ecs.ECS) { // Grab level data - entry := archetype.LevelTag.MustFirst(ecs.World) + entry := tags.LevelTag.MustFirst(ecs.World) level := component.Level.Get(entry) // Grab player data - playerEntry := archetype.PlayerTag.MustFirst(ecs.World) + playerEntry := tags.PlayerTag.MustFirst(ecs.World) playerPos := component.Position.Get(playerEntry) - for entry = range archetype.MonsterTag.Iter(ecs.World) { + for entry = range tags.MonsterTag.Iter(ecs.World) { position := component.Position.Get(entry) sprite := component.Sprite.Get(entry) monsterVision := component.Fov.Get(entry).VisibleTiles diff --git a/system/action/player.go b/system/action/player.go index 11709a8..cfabf25 100644 --- a/system/action/player.go +++ b/system/action/player.go @@ -2,7 +2,7 @@ package action import ( "github.com/hajimehoshi/ebiten/v2" - "github.com/kensonjohnson/roguelike-game-go/archetype" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/event" "github.com/kensonjohnson/roguelike-game-go/system/combat" @@ -42,11 +42,11 @@ func TakePlayerAction(ecs *ecs.ECS) bool { } // Grab current level - levelEntry := archetype.LevelTag.MustFirst(ecs.World) + levelEntry := tags.LevelTag.MustFirst(ecs.World) level := component.Level.Get(levelEntry) // Grab player data - playerEntry := archetype.PlayerTag.MustFirst(ecs.World) + playerEntry := tags.PlayerTag.MustFirst(ecs.World) position := component.Position.Get(playerEntry) sprite := component.Sprite.Get(playerEntry) vision := component.Fov.Get(playerEntry) @@ -90,7 +90,7 @@ func TakePlayerAction(ecs *ecs.ECS) bool { Y: position.Y + moveY, } var monsterEntry *donburi.Entry - for entry := range archetype.MonsterTag.Iter(ecs.World) { + for entry := range tags.MonsterTag.Iter(ecs.World) { position := component.Position.Get(entry) if position.IsEqual(&enemyPosition) { monsterEntry = entry diff --git a/system/background.go b/system/background.go index 069df46..307b353 100644 --- a/system/background.go +++ b/system/background.go @@ -2,23 +2,23 @@ package system import ( "github.com/hajimehoshi/ebiten/v2" - "github.com/kensonjohnson/roguelike-game-go/archetype" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi/ecs" ) func (r *render) DrawBackground(ecs *ecs.ECS, screen *ebiten.Image) { - entry := archetype.LevelTag.MustFirst(ecs.World) + entry := tags.LevelTag.MustFirst(ecs.World) level := component.Level.Get(entry) - entry = archetype.CameraTag.MustFirst(ecs.World) + entry = tags.CameraTag.MustFirst(ecs.World) camera := component.Camera.Get(entry) if !level.Redraw { camera.MainCamera.Draw(r.backgroundImage, camera.CamImageOptions, screen) return } r.backgroundImage.Clear() - entry = archetype.PlayerTag.MustFirst(ecs.World) + entry = tags.PlayerTag.MustFirst(ecs.World) playerVision := component.Fov.Get(entry).VisibleTiles maxTiles := config.ScreenWidth * (config.ScreenHeight - config.UIHeight) diff --git a/system/camera.go b/system/camera.go index 618b902..8e0e67a 100644 --- a/system/camera.go +++ b/system/camera.go @@ -1,7 +1,7 @@ package system import ( - "github.com/kensonjohnson/roguelike-game-go/archetype" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi/ecs" @@ -12,9 +12,9 @@ type camera struct{} var Camera = &camera{} func (c *camera) Update(ecs *ecs.ECS) { - entry := archetype.CameraTag.MustFirst(ecs.World) + entry := tags.CameraTag.MustFirst(ecs.World) camera := component.Camera.Get(entry) - entry = archetype.PlayerTag.MustFirst(ecs.World) + entry = tags.PlayerTag.MustFirst(ecs.World) position := component.Position.Get(entry) camera.MainCamera.LookAt( diff --git a/system/combat/combat.go b/system/combat/combat.go index 8735c4d..8876d67 100644 --- a/system/combat/combat.go +++ b/system/combat/combat.go @@ -3,7 +3,7 @@ package combat import ( "fmt" - "github.com/kensonjohnson/roguelike-game-go/archetype" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/engine" "github.com/yohamta/donburi" @@ -40,7 +40,7 @@ func AttackSystem(world donburi.World, attacker, defender *donburi.Entry) { if defenderHealth.CurrentHealth <= 0 { defenderMessages.DeadMessage = fmt.Sprintf("%s has died!\n", defenderName.Value) } - entry := archetype.CameraTag.MustFirst(world) + entry := tags.CameraTag.MustFirst(world) camera := component.Camera.Get(entry) camera.MainCamera.AddTrauma(0.2) } else { diff --git a/system/minimap.go b/system/minimap.go index c7ef800..2c91064 100644 --- a/system/minimap.go +++ b/system/minimap.go @@ -5,7 +5,7 @@ import ( "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/vector" - "github.com/kensonjohnson/roguelike-game-go/archetype" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi/ecs" @@ -14,7 +14,7 @@ import ( const blipSize = 4 func DrawMinimap(ecs *ecs.ECS, screen *ebiten.Image) { - entry := archetype.LevelTag.MustFirst(ecs.World) + entry := tags.LevelTag.MustFirst(ecs.World) level := component.Level.Get(entry) // The values of 330 and 210 are based on the size of the minimap image. @@ -52,7 +52,7 @@ func DrawMinimap(ecs *ecs.ECS, screen *ebiten.Image) { y := startingYPixel + (position.Y * blipSize) if component.Discoverable.Get(entry).SeenByPlayer { - if entry.HasComponent(archetype.ItemTag) { + if entry.HasComponent(tags.ItemTag) { vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, color.RGBA{R: 15, G: 10, B: 222, A: 255}, false) } else { vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, color.RGBA{R: 255, G: 0, B: 0, A: 255}, false) @@ -61,7 +61,7 @@ func DrawMinimap(ecs *ecs.ECS, screen *ebiten.Image) { } // Draw the player - playerEntry := archetype.PlayerTag.MustFirst(ecs.World) + playerEntry := tags.PlayerTag.MustFirst(ecs.World) playerPosition := component.Position.Get(playerEntry) x := startingXPixel + (playerPosition.X * blipSize) y := startingYPixel + (playerPosition.Y * blipSize) diff --git a/system/render.go b/system/render.go index 0e4c513..e4977e1 100644 --- a/system/render.go +++ b/system/render.go @@ -2,7 +2,7 @@ package system import ( "github.com/hajimehoshi/ebiten/v2" - "github.com/kensonjohnson/roguelike-game-go/archetype" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/norendren/go-fov/fov" @@ -20,13 +20,13 @@ type render struct { var Render = &render{ enemyQuery: donburi.NewQuery( filter.Contains( - archetype.MonsterTag, + tags.MonsterTag, component.Sprite, component.Position, )), pickupsQuery: donburi.NewQuery( filter.Contains( - archetype.ItemTag, + tags.ItemTag, component.Position, component.Sprite, )), @@ -34,9 +34,9 @@ var Render = &render{ } func (r *render) Draw(ecs *ecs.ECS, screen *ebiten.Image) { - playerEntry := archetype.PlayerTag.MustFirst(ecs.World) + playerEntry := tags.PlayerTag.MustFirst(ecs.World) playerVision := component.Fov.Get(playerEntry).VisibleTiles - entry := archetype.CameraTag.MustFirst(ecs.World) + entry := tags.CameraTag.MustFirst(ecs.World) camera := component.Camera.Get(entry) for entry = range r.pickupsQuery.Iter(ecs.World) { diff --git a/system/scene/levelscene.go b/system/scene/levelscene.go index c44d1e2..1d2fd33 100644 --- a/system/scene/levelscene.go +++ b/system/scene/levelscene.go @@ -3,6 +3,7 @@ package scene import ( "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/archetype" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/event" "github.com/kensonjohnson/roguelike-game-go/internal/config" @@ -42,11 +43,11 @@ func (ls *LevelScene) Setup(world donburi.World) { levelData := archetype.GenerateLevel(world) - if _, ok := archetype.UITag.First(world); !ok { + if _, ok := tags.UITag.First(world); !ok { archetype.CreateNewUI(world) } - playerEntry := archetype.PlayerTag.MustFirst(world) + playerEntry := tags.PlayerTag.MustFirst(world) playerPosition := component.Position.Get(playerEntry) startingRoom := levelData.Rooms[0] playerPosition.X, playerPosition.Y = startingRoom.Center() @@ -58,7 +59,7 @@ func (ls *LevelScene) Setup(world donburi.World) { component.Fov.Get(playerEntry). VisibleTiles.Compute(levelData, playerPosition.X, playerPosition.Y, 8) - cameraEntry := archetype.CameraTag.MustFirst(world) + cameraEntry := tags.CameraTag.MustFirst(world) camera := component.Camera.Get(cameraEntry) camera.MainCamera.Lerp = false camera.MainCamera.LookAt( @@ -81,16 +82,16 @@ func (ls *LevelScene) Teardown() { } go func() { - archetype.LevelTag.MustFirst(ls.ecs.World).Remove() + tags.LevelTag.MustFirst(ls.ecs.World).Remove() - for entry := range archetype.MonsterTag.Iter(ls.ecs.World) { + for entry := range tags.MonsterTag.Iter(ls.ecs.World) { if logger.DebugOn { logger.DebugLogger.Println("Removing entry: ", entry.String()) } entry.Remove() } - for entry := range archetype.PickupTag.Iter(ls.ecs.World) { + for entry := range tags.PickupTag.Iter(ls.ecs.World) { if logger.DebugOn { logger.DebugLogger.Println("Removing entry: ", entry.String()) } @@ -125,86 +126,3 @@ func (ls *LevelScene) configureECS(world donburi.World) { // Add event listeners event.ProgressLevelEvent.Subscribe(ls.ecs.World, progressLevel) } - -// func copyPlayerInstance( -// oldWorld donburi.World, -// newWorld donburi.World, -// ) { -// currentPlayerEntry := archetype.PlayerTag.MustFirst(oldWorld) -// currentPlayerHealth := component.Health.Get(currentPlayerEntry) -// currentPlayerEquipment := component.Equipment.Get(currentPlayerEntry) - -// playerEntry := archetype.PlayerTag.MustFirst(newWorld) -// component.Health.SetValue(playerEntry, *currentPlayerHealth) -// component.Equipment.SetValue(playerEntry, component.EquipmentData{ -// Weapon: copyWeapon(newWorld, currentPlayerEquipment.Weapon), -// // Sheild: currentPlayerEquipment.Sheild, -// // Gloves: currentPlayerEquipment.Gloves, -// Armor: copyArmor(newWorld, currentPlayerEquipment.Armor), -// // Boots: currentPlayerEquipment.Boots, -// }) - -// } - -// func copyWeapon(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { -// if entry == nil { -// logger.ErrorLogger.Panic("Entry is nil when copying weapon data") -// } -// name := component.Name.Get(entry).Value -// sprite := component.Sprite.Get(entry).Image -// if sprite == nil { -// logger.ErrorLogger.Panic("Sprite missing when copying weapon data") -// } -// actionText := component.ActionText.Get(entry).Value -// attack := component.Attack.Get(entry) -// if attack == nil { -// logger.ErrorLogger.Panic("Attack data missing when copying weapon data") -// } - -// newWeaponData := items.WeaponData{ -// ItemData: items.ItemData{ -// Name: name, -// Sprite: sprite, -// }, -// ActionText: actionText, -// MinimumDamage: attack.MinimumDamage, -// MaximumDamage: attack.MaximumDamage, -// ToHitBonus: attack.ToHitBonus, -// } - -// newWeapon := archetype.CreateNewWeapon(newWorld, newWeaponData) -// if newWeapon == nil { -// logger.ErrorLogger.Panic("Failed to create new weapon when copying weapon data") -// } -// return newWeapon -// } - -// func copyArmor(newWorld donburi.World, entry *donburi.Entry) *donburi.Entry { -// if entry == nil { -// logger.ErrorLogger.Panic("Entry is nil when copying armor data") -// } -// name := component.Name.Get(entry).Value -// sprite := component.Sprite.Get(entry).Image -// if sprite == nil { -// logger.ErrorLogger.Panic("Sprite missing when copying armor data") -// } -// defense := component.Defense.Get(entry) -// if defense == nil { -// logger.ErrorLogger.Panic("Defense data missing when copying armor data") -// } - -// newArmorData := items.ArmorData{ -// ItemData: items.ItemData{ -// Name: name, -// Sprite: sprite, -// }, -// Defense: defense.Defense, -// ArmorClass: defense.ArmorClass, -// } - -// newArmor := archetype.CreateNewArmor(newWorld, newArmorData) -// if newArmor == nil { -// logger.ErrorLogger.Panic("Failed to create new armor when copying armor data") -// } -// return newArmor -// } diff --git a/system/ui.go b/system/ui.go index 3bad5e9..e200a37 100644 --- a/system/ui.go +++ b/system/ui.go @@ -6,7 +6,7 @@ import ( "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/text/v2" - "github.com/kensonjohnson/roguelike-game-go/archetype" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/config" @@ -69,7 +69,7 @@ func (u *ui) Update(ecs *ecs.ECS) { } func (u *ui) Draw(ecs *ecs.ECS, screen *ebiten.Image) { - entry := archetype.UITag.MustFirst(ecs.World) + entry := tags.UITag.MustFirst(ecs.World) ui := component.UI.Get(entry) // Draw the user message box From edf3b84116ed36e91eae57a0f6f9a2cf0c5ee12e Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:05:01 -0500 Subject: [PATCH 38/55] Add inventory and wallet components to player --- archetype/player.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/archetype/player.go b/archetype/player.go index 06b0cb2..a01d3ca 100644 --- a/archetype/player.go +++ b/archetype/player.go @@ -1,6 +1,7 @@ package archetype import ( + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/items" @@ -8,20 +9,20 @@ import ( "github.com/yohamta/donburi" ) -var PlayerTag = donburi.NewTag("player") - func CreateNewPlayer( world donburi.World, weapon items.WeaponData, armorId items.ArmorData, ) *donburi.Entry { player := world.Entry(world.Create( - PlayerTag, + tags.PlayerTag, component.Position, component.Sprite, component.Name, component.Fov, component.Equipment, + component.Inventory, + component.Wallet, component.Health, component.UserMessage, component.Attack, @@ -60,6 +61,13 @@ func CreateNewPlayer( } component.Equipment.SetValue(player, equipment) + // Setup inventory + inventory := component.NewInventory(30) + component.Inventory.SetValue(player, inventory) + + wallet := component.WalletData{} + component.Wallet.SetValue(player, wallet) + // Set default messages component.UserMessage.SetValue( player, From c1025cf9c3b147824dfe3fd4626befbb299bbc7c Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:05:30 -0500 Subject: [PATCH 39/55] Move tag to tags package --- archetype/tags/tags.go | 1 + archetype/valuable.go | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/archetype/tags/tags.go b/archetype/tags/tags.go index 544f3a8..2df3de3 100644 --- a/archetype/tags/tags.go +++ b/archetype/tags/tags.go @@ -5,6 +5,7 @@ import "github.com/yohamta/donburi" var ( ArmorTag = donburi.NewTag("armor") CameraTag = donburi.NewTag("camera") + CoinTag = donburi.NewTag("coin") ConsumableTag = donburi.NewTag("consumable") ItemTag = donburi.NewTag("item") LevelTag = donburi.NewTag("level") diff --git a/archetype/valuable.go b/archetype/valuable.go index 0347496..ff25596 100644 --- a/archetype/valuable.go +++ b/archetype/valuable.go @@ -21,16 +21,14 @@ func CreateNewValuable(world donburi.World, valuableData *items.ValuableData) *d return entry } -var CoinTag = donburi.NewTag("coin") - func CreateCoins(world donburi.World, valuableData *items.ValuableData) *donburi.Entry { entry := CreateNewValuable(world, valuableData) - entry.AddComponent(CoinTag) + entry.AddComponent(tags.CoinTag) return entry } func IsCoin(entry donburi.Entry) bool { - return entry.HasComponent(CoinTag) + return entry.HasComponent(tags.CoinTag) } From 8cdbca3e38cdb62e3d7fa71c94f7d8bbe3187790 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:06:00 -0500 Subject: [PATCH 40/55] Check that entry is item when adding --- component/inventory.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/component/inventory.go b/component/inventory.go index a080729..118c418 100644 --- a/component/inventory.go +++ b/component/inventory.go @@ -3,6 +3,8 @@ package component import ( "errors" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" + "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/yohamta/donburi" ) @@ -14,7 +16,7 @@ type InventoryData struct { var Inventory = donburi.NewComponentType[InventoryData]() -func (i *InventoryData) New(capacity int) InventoryData { +func NewInventory(capacity int) InventoryData { return InventoryData{ Items: make([]*donburi.Entry, capacity), capacity: capacity, @@ -46,6 +48,10 @@ func (i *InventoryData) AddItem(item *donburi.Entry) error { return errors.New("inventory full") } + if !item.HasComponent(tags.ItemTag) { + logger.ErrorLogger.Panic("Entry is not an item: ", item) + } + var targetIndex = -1 for index, element := range i.Items { @@ -65,7 +71,7 @@ func (i *InventoryData) AddItem(item *donburi.Entry) error { func (i *InventoryData) RemoveItem(index int) error { if index >= i.capacity { - return errors.New("index out of range in RemoveItem") + logger.ErrorLogger.Panic("index out of range in RemoveItem. Recieved: ", index) } i.Items[index] = nil return nil From 769928b1bc24d8fdc96f887c0382e42d4de47698 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:07:04 -0500 Subject: [PATCH 41/55] Keep wallet from going negative --- component/wallet.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/component/wallet.go b/component/wallet.go index c9182a9..94e5cee 100644 --- a/component/wallet.go +++ b/component/wallet.go @@ -14,4 +14,7 @@ func (w *WalletData) AddAmount(amount int) { func (w *WalletData) SubtractAmount(amount int) { w.Amount -= amount + if w.Amount < 0 { + w.Amount = 0 + } } From 047e2d10471a34cb0374c404730df3c44a55f947 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:07:27 -0500 Subject: [PATCH 42/55] Add pickups to either wallet or inventory --- system/turn.go | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/system/turn.go b/system/turn.go index 9154b48..c09f2e3 100644 --- a/system/turn.go +++ b/system/turn.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/kensonjohnson/roguelike-game-go/archetype" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/system/action" "github.com/yohamta/donburi/ecs" @@ -33,7 +34,7 @@ func (td *TurnData) Update(ecs *ecs.ECS) { if td.TurnState == BeforePlayerAction { td.TurnCounter++ // Check if player survived the last cycle of monster turns - entry := archetype.PlayerTag.MustFirst(ecs.World) + entry := tags.PlayerTag.MustFirst(ecs.World) playerHealth := component.Health.Get(entry) if playerHealth.CurrentHealth <= 0 { td.gameOver() @@ -41,9 +42,9 @@ func (td *TurnData) Update(ecs *ecs.ECS) { playerMessages.GameStateMessage = "Game over!" } - level := component.Level.Get(archetype.LevelTag.MustFirst(ecs.World)) + level := component.Level.Get(tags.LevelTag.MustFirst(ecs.World)) // Remove any enemies that died during the last turn - for entry = range archetype.MonsterTag.Iter(ecs.World) { + for entry = range tags.MonsterTag.Iter(ecs.World) { health := component.Health.Get(entry) if health.CurrentHealth <= 0 { position := component.Position.Get(entry) @@ -72,21 +73,38 @@ func (td *TurnData) Update(ecs *ecs.ECS) { sprite.OffestY = 0 } - entry = archetype.PlayerTag.MustFirst(ecs.World) - playerPosition := component.Position.Get(entry) - playerMessages := component.UserMessage.Get(entry) + playerEntry := tags.PlayerTag.MustFirst(ecs.World) + playerPosition := component.Position.Get(playerEntry) + playerMessages := component.UserMessage.Get(playerEntry) - for entry = range archetype.PickupTag.Iter(ecs.World) { + for entry = range tags.PickupTag.Iter(ecs.World) { if !entry.HasComponent(component.Position) { continue } pickupPosition := component.Position.Get(entry) if pickupPosition.X == playerPosition.X && pickupPosition.Y == playerPosition.Y { - archetype.RemoveItemFromWorld(entry) - itemName := component.Name.Get(entry) - playerMessages.WorldInteractionMessage = fmt.Sprintf("Picked up %s!", itemName.Value) - // TODO: place in player's inventory + + // If pickup is coinage, add to wallet + if entry.HasComponent(tags.CoinTag) { + component.Wallet.Get(playerEntry).AddAmount( + component.Value.Get(entry).Amount, + ) + break + } + + // Otherwise, must be item, place in inventory + err := component.Inventory.Get(playerEntry).AddItem(entry) + if err != nil { + playerMessages.WorldInteractionMessage = "Inventory full! Can't pick up anymore items!" + } else { + archetype.RemoveItemFromWorld(entry) + itemName := component.Name.Get(entry) + playerMessages.WorldInteractionMessage = fmt.Sprintf("Picked up %s!", itemName.Value) + } + + // Only one pickup can fill a tile + break } } From 02009957401d25a9f1f8aa184776f17ba6afaf20 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:13:15 -0500 Subject: [PATCH 43/55] Add fix for camera jank on scene change --- system/scene/levelscene.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/system/scene/levelscene.go b/system/scene/levelscene.go index 1d2fd33..57ba5cc 100644 --- a/system/scene/levelscene.go +++ b/system/scene/levelscene.go @@ -10,6 +10,7 @@ import ( "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/kensonjohnson/roguelike-game-go/system" "github.com/kensonjohnson/roguelike-game-go/system/layer" + "github.com/setanarut/kamera/v2" "github.com/yohamta/donburi" "github.com/yohamta/donburi/ecs" ) @@ -61,12 +62,15 @@ func (ls *LevelScene) Setup(world donburi.World) { cameraEntry := tags.CameraTag.MustFirst(world) camera := component.Camera.Get(cameraEntry) - camera.MainCamera.Lerp = false - camera.MainCamera.LookAt( + + // FIX: This is a workaround to the kamera camera keeping a 'memory' of + // previous location, even after lerp is turned off. + camera.MainCamera = kamera.NewCamera( float64((playerPosition.X*config.TileWidth)+config.TileWidth/2), float64((playerPosition.Y*config.TileHeight)+config.TileHeight/2), + config.ScreenWidth*config.TileWidth, + (config.ScreenHeight-config.UIHeight)*config.TileHeight, ) - camera.MainCamera.Lerp = true ls.configureECS(world) From 8617024a0013d1f5a6a65850ceae0c165338fd54 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:03:13 -0500 Subject: [PATCH 44/55] Consitently replace camera --- archetype/camera.go | 14 ++++++++++++++ system/scene/levelscene.go | 9 ++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/archetype/camera.go b/archetype/camera.go index c008e80..e7e6999 100644 --- a/archetype/camera.go +++ b/archetype/camera.go @@ -35,3 +35,17 @@ func CreateNewCamera(world donburi.World) { component.Camera.Set(entry, cameraData) } + +func ReplaceCamera(world donburi.World, playerX, playerY float64) { + entry := tags.CameraTag.MustFirst(world) + camera := component.Camera.Get(entry) + camera.MainCamera = kamera.NewCamera( + playerX, playerY, + config.ScreenWidth*config.TileWidth, + (config.ScreenHeight-config.UIHeight)*config.TileHeight, + ) + camera.MainCamera.Lerp = true + camera.MainCamera.ZoomFactor = 100 + camera.MainCamera.ShakeOptions.MaxShakeAngle = 0 + camera.MainCamera.ShakeOptions.Decay = 0.5 +} diff --git a/system/scene/levelscene.go b/system/scene/levelscene.go index 57ba5cc..a0317de 100644 --- a/system/scene/levelscene.go +++ b/system/scene/levelscene.go @@ -10,7 +10,6 @@ import ( "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/kensonjohnson/roguelike-game-go/system" "github.com/kensonjohnson/roguelike-game-go/system/layer" - "github.com/setanarut/kamera/v2" "github.com/yohamta/donburi" "github.com/yohamta/donburi/ecs" ) @@ -60,16 +59,12 @@ func (ls *LevelScene) Setup(world donburi.World) { component.Fov.Get(playerEntry). VisibleTiles.Compute(levelData, playerPosition.X, playerPosition.Y, 8) - cameraEntry := tags.CameraTag.MustFirst(world) - camera := component.Camera.Get(cameraEntry) - // FIX: This is a workaround to the kamera camera keeping a 'memory' of // previous location, even after lerp is turned off. - camera.MainCamera = kamera.NewCamera( + archetype.ReplaceCamera( + world, float64((playerPosition.X*config.TileWidth)+config.TileWidth/2), float64((playerPosition.Y*config.TileHeight)+config.TileHeight/2), - config.ScreenWidth*config.TileWidth, - (config.ScreenHeight-config.UIHeight)*config.TileHeight, ) ls.configureECS(world) From 051d497118e3c0c885e421449efd555e98a8e5d2 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:36:32 -0500 Subject: [PATCH 45/55] Rename --- event/{progress-level.go => level-events.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename event/{progress-level.go => level-events.go} (100%) diff --git a/event/progress-level.go b/event/level-events.go similarity index 100% rename from event/progress-level.go rename to event/level-events.go From 8f73f9110b75c624adb2c14a84d24c45ee3fd985 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:10:22 -0500 Subject: [PATCH 46/55] Swap logger for slog --- main.go | 9 ++++++--- system/scene/scenemanager.go | 14 ++++---------- system/scene/titlescene.go | 10 +++------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/main.go b/main.go index 19a2987..75689d1 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,8 @@ package main import ( "flag" + "log" + "log/slog" "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/assets" @@ -50,14 +52,15 @@ func main() { ebiten.SetVsyncEnabled(false) system.Debug.On = true logger.SetDebug(*DebugOn) + slog.SetLogLoggerLevel(slog.LevelDebug) } + log.SetFlags(log.Lshortfile) + g := &Game{} g.configure() - if logger.DebugOn { - logger.DebugLogger.Println("Starting Game") - } + slog.Debug("Starting Game") if err := ebiten.RunGame(g); err != nil { logger.ErrorLogger.Panic(err) diff --git a/system/scene/scenemanager.go b/system/scene/scenemanager.go index c01adb3..ac712e5 100644 --- a/system/scene/scenemanager.go +++ b/system/scene/scenemanager.go @@ -2,11 +2,11 @@ package scene import ( "image/color" + "log/slog" "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/internal/config" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) @@ -38,9 +38,7 @@ var SceneManager = &SceneManagerData{ } func (sm *SceneManagerData) Setup() { - if logger.DebugOn { - logger.DebugLogger.Println("SceneManager Setup") - } + slog.Debug("SceneManager Setup") sm.world = donburi.NewWorld() archetype.CreateNewPlayer(sm.world, items.Weapons.BattleAxe, items.Armor.PlateArmor) archetype.CreateNewCamera(sm.world) @@ -58,18 +56,14 @@ func (sm *SceneManagerData) Update() { } if sm.fadeOut { - if logger.DebugOn { - logger.DebugLogger.Println("Running Teardown on current scene") - } + slog.Debug("Running Teardown on current scene") sm.fadeOut = false sm.current.Teardown() return } if sm.transitionCount != 0 && sm.next != nil && sm.current.Ready() { - if logger.DebugOn { - logger.DebugLogger.Println("Running Setup on next scene") - } + slog.Debug("Running Setup on next scene") sm.current = sm.next sm.next = nil sm.current.Setup(sm.world) diff --git a/system/scene/titlescene.go b/system/scene/titlescene.go index 906f8e7..9361913 100644 --- a/system/scene/titlescene.go +++ b/system/scene/titlescene.go @@ -2,13 +2,13 @@ package scene import ( "image/color" + "log/slog" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/text/v2" "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/internal/config" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/yohamta/donburi" ) @@ -64,16 +64,12 @@ func (s *TitleScene) Ready() bool { } func (s *TitleScene) Teardown() { - if logger.DebugOn { - logger.DebugLogger.Println("TitleScene teardown") - } + slog.Debug("TitleScene teardown") s.ready = true } func (s *TitleScene) Setup(world donburi.World) { - if logger.DebugOn { - logger.DebugLogger.Println("TitleScene setup") - } + slog.Debug("TitleScene setup") s.ready = true } From bde5d4b5d4dc3ecf71fc8b420897f66ffd15b31b Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:20:55 -0500 Subject: [PATCH 47/55] Remove logger package --- archetype/level.go | 5 +++-- assets/efs.go | 16 ++++++++-------- component/inventory.go | 6 +++--- component/level.go | 5 +++-- internal/logger/logger.go | 25 ------------------------- main.go | 4 +--- system/scene/levelscene.go | 21 ++++++--------------- 7 files changed, 24 insertions(+), 58 deletions(-) delete mode 100644 internal/logger/logger.go diff --git a/archetype/level.go b/archetype/level.go index 6ac6cbd..20ddc05 100644 --- a/archetype/level.go +++ b/archetype/level.go @@ -1,12 +1,13 @@ package archetype import ( + "log" + "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/kensonjohnson/roguelike-game-go/internal/engine" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/kensonjohnson/roguelike-game-go/items" "github.com/yohamta/donburi" ) @@ -220,7 +221,7 @@ func addRandomPickupsToRoom( err := PlaceItemInWorld(entry, x, y, true) if err != nil { - logger.ErrorLogger.Panic("Failed to place consumable in the world") + log.Panic("Failed to place consumable in the world") } } } diff --git a/assets/efs.go b/assets/efs.go index 1db4fdb..f4a0f76 100644 --- a/assets/efs.go +++ b/assets/efs.go @@ -3,12 +3,12 @@ package assets import ( "bytes" "embed" + "log" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/ebitenutil" "github.com/hajimehoshi/ebiten/v2/text/v2" "github.com/kensonjohnson/roguelike-game-go/internal/config" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" ) var ( @@ -153,17 +153,17 @@ func init() { kenneyMiniFontBytes, err := assetsFS.ReadFile("fonts/KenneyMini.ttf") if err != nil { - logger.ErrorLogger.Panic(err) + log.Panic(err) } KenneyMiniFont = mustLoadFont(kenneyMiniFontBytes) kenneyMiniSquaredFontBytes, err := assetsFS.ReadFile("fonts/KenneyMiniSquared.ttf") if err != nil { - logger.ErrorLogger.Panic(err) + log.Panic(err) } KenneyMiniSquaredFont = mustLoadFont(kenneyMiniSquaredFontBytes) kenneyPixelFontBytes, err := assetsFS.ReadFile("fonts/KenneyPixel.ttf") if err != nil { - logger.ErrorLogger.Panic(err) + log.Panic(err) } KenneyPixelFont = mustLoadFont(kenneyPixelFontBytes) // For some reason, the KenneyPixel shows up as half the size of the other fonts. @@ -284,11 +284,11 @@ func init() { func mustLoadImage(filePath string) *ebiten.Image { imgSource, err := assetsFS.ReadFile(filePath) if err != nil { - logger.ErrorLogger.Panic(err) + log.Panic(err) } image, _, err := ebitenutil.NewImageFromReader(bytes.NewReader(imgSource)) if err != nil { - logger.ErrorLogger.Panic(err) + log.Panic(err) } return image } @@ -297,7 +297,7 @@ func mustLoadImage(filePath string) *ebiten.Image { func mustLoadFont(font []byte) *text.GoTextFace { source, err := text.NewGoTextFaceSource(bytes.NewReader(font)) if err != nil { - logger.ErrorLogger.Panic(err) + log.Panic(err) } return &text.GoTextFace{ Source: source, @@ -307,7 +307,7 @@ func mustLoadFont(font []byte) *text.GoTextFace { func MustBeValidImage(image *ebiten.Image, name string) *ebiten.Image { if image == nil { - logger.ErrorLogger.Panicf("%s asset not loaded!", name) + log.Panicf("%s asset not loaded!", name) } return image } diff --git a/component/inventory.go b/component/inventory.go index 118c418..02804d3 100644 --- a/component/inventory.go +++ b/component/inventory.go @@ -2,9 +2,9 @@ package component import ( "errors" + "log" "github.com/kensonjohnson/roguelike-game-go/archetype/tags" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/yohamta/donburi" ) @@ -49,7 +49,7 @@ func (i *InventoryData) AddItem(item *donburi.Entry) error { } if !item.HasComponent(tags.ItemTag) { - logger.ErrorLogger.Panic("Entry is not an item: ", item) + log.Panic("Entry is not an item: ", item) } var targetIndex = -1 @@ -71,7 +71,7 @@ func (i *InventoryData) AddItem(item *donburi.Entry) error { func (i *InventoryData) RemoveItem(index int) error { if index >= i.capacity { - logger.ErrorLogger.Panic("index out of range in RemoveItem. Recieved: ", index) + log.Panic("index out of range in RemoveItem. Recieved: ", index) } i.Items[index] = nil return nil diff --git a/component/level.go b/component/level.go index 66b74f6..89e9f45 100644 --- a/component/level.go +++ b/component/level.go @@ -1,10 +1,11 @@ package component import ( + "log" + "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/kensonjohnson/roguelike-game-go/internal/engine" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/yohamta/donburi" ) @@ -46,7 +47,7 @@ func (l *LevelData) GetIndexFromXY(x int, y int) int { // This coordinate is logical tiles, not pixels. func (l *LevelData) GetFromXY(x, y int) *MapTile { if len(l.Tiles) == 0 { - logger.ErrorLogger.Panic("levelData.Tiles has no length") + log.Panic("levelData.Tiles has no length") } return l.Tiles[(y*config.ScreenWidth)+x] } diff --git a/internal/logger/logger.go b/internal/logger/logger.go deleted file mode 100644 index 93a8d8a..0000000 --- a/internal/logger/logger.go +++ /dev/null @@ -1,25 +0,0 @@ -package logger - -import ( - "log" - "os" -) - -var ( - DebugLogger *log.Logger - InfoLogger *log.Logger - WarnLogger *log.Logger - ErrorLogger *log.Logger - DebugOn bool -) - -func init() { - DebugLogger = log.New(os.Stdout, "DEBUG: ", log.Ltime|log.Lshortfile) - InfoLogger = log.New(os.Stdout, "INFO: ", log.Ltime|log.Lshortfile) - WarnLogger = log.New(os.Stdout, "WARN: ", log.Ltime|log.Lshortfile) - ErrorLogger = log.New(os.Stderr, "ERROR: ", log.Ltime|log.Lshortfile) -} - -func SetDebug(flag bool) { - DebugOn = flag -} diff --git a/main.go b/main.go index 75689d1..90794ca 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,6 @@ import ( "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/assets" "github.com/kensonjohnson/roguelike-game-go/internal/config" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/kensonjohnson/roguelike-game-go/system" "github.com/kensonjohnson/roguelike-game-go/system/scene" ) @@ -51,7 +50,6 @@ func main() { if DebugOn != nil && *DebugOn { ebiten.SetVsyncEnabled(false) system.Debug.On = true - logger.SetDebug(*DebugOn) slog.SetLogLoggerLevel(slog.LevelDebug) } @@ -63,6 +61,6 @@ func main() { slog.Debug("Starting Game") if err := ebiten.RunGame(g); err != nil { - logger.ErrorLogger.Panic(err) + log.Panic(err) } } diff --git a/system/scene/levelscene.go b/system/scene/levelscene.go index a0317de..8b2b4a5 100644 --- a/system/scene/levelscene.go +++ b/system/scene/levelscene.go @@ -1,13 +1,14 @@ package scene import ( + "log/slog" + "github.com/hajimehoshi/ebiten/v2" "github.com/kensonjohnson/roguelike-game-go/archetype" "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/event" "github.com/kensonjohnson/roguelike-game-go/internal/config" - "github.com/kensonjohnson/roguelike-game-go/internal/logger" "github.com/kensonjohnson/roguelike-game-go/system" "github.com/kensonjohnson/roguelike-game-go/system/layer" "github.com/yohamta/donburi" @@ -35,9 +36,7 @@ func (ls *LevelScene) Ready() bool { func (ls *LevelScene) Setup(world donburi.World) { ls.ready = false - if logger.DebugOn { - logger.DebugLogger.Println("LevelScene setup") - } + slog.Debug("LevelScene setup") go func() { @@ -75,25 +74,17 @@ func (ls *LevelScene) Setup(world donburi.World) { func (ls *LevelScene) Teardown() { ls.ready = false - - if logger.DebugOn { - logger.DebugLogger.Println("LevelScene teardown") - } - + slog.Debug("LevelScene teardown") go func() { tags.LevelTag.MustFirst(ls.ecs.World).Remove() for entry := range tags.MonsterTag.Iter(ls.ecs.World) { - if logger.DebugOn { - logger.DebugLogger.Println("Removing entry: ", entry.String()) - } + slog.Debug("Removing entry.", "entry", entry.String()) entry.Remove() } for entry := range tags.PickupTag.Iter(ls.ecs.World) { - if logger.DebugOn { - logger.DebugLogger.Println("Removing entry: ", entry.String()) - } + slog.Debug("Removing entry.", "entry", entry.String()) entry.Remove() } From 5719187c91483bc726121faabdb6ff85c1d82fa2 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:04:27 -0500 Subject: [PATCH 48/55] Add colors package --- internal/colors/colors.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 internal/colors/colors.go diff --git a/internal/colors/colors.go b/internal/colors/colors.go new file mode 100644 index 0000000..ff52469 --- /dev/null +++ b/internal/colors/colors.go @@ -0,0 +1,21 @@ +package colors + +import "image/color" + +var ( + Blue = color.RGBA{0, 0, 255, 255} + CornflowerBlue = color.RGBA{100, 149, 237, 255} + DeepSkyBlue = color.RGBA{0, 191, 255, 255} + Green = color.RGBA{0, 128, 0, 255} + Lime = color.RGBA{0, 255, 0, 255} + Brown = color.RGBA{165, 42, 42, 255} + Peru = color.RGBA{205, 133, 63, 255} + Gray = color.RGBA{128, 128, 128, 255} + LightGray = color.RGBA{211, 211, 211, 255} + DarkGray = color.RGBA{169, 169, 169, 255} + SlateGray = color.RGBA{112, 128, 144, 255} + Silver = color.RGBA{192, 192, 192, 255} + Yellow = color.RGBA{255, 255, 0, 255} + Orange = color.RGBA{255, 165, 0, 255} + Red = color.RGBA{255, 0, 0, 255} +) From a2c885e90aede7f8beb8334c45597668031f4dd3 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:05:29 -0500 Subject: [PATCH 49/55] Swap out for defined colors --- system/minimap.go | 13 +++++++------ system/scene/titlescene.go | 5 +++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/system/minimap.go b/system/minimap.go index 2c91064..332690a 100644 --- a/system/minimap.go +++ b/system/minimap.go @@ -7,6 +7,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/vector" "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" + "github.com/kensonjohnson/roguelike-game-go/internal/colors" "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi/ecs" ) @@ -32,11 +33,11 @@ func DrawMinimap(ecs *ecs.ECS, screen *ebiten.Image) { } if tile.TileType == component.WALL { - vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, color.RGBA{R: 202, G: 146, B: 74, A: 255}, false) + vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, colors.Peru, false) } else if tile.TileType == component.STAIR_DOWN { - vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, color.RGBA{R: 46, G: 204, B: 113, A: 255}, false) - } else { - vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, color.RGBA{R: 178, G: 182, B: 194, A: 255}, false) + vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, colors.Lime, false) + } else /* floor */ { + vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, colors.LightGray, false) } } @@ -53,9 +54,9 @@ func DrawMinimap(ecs *ecs.ECS, screen *ebiten.Image) { if component.Discoverable.Get(entry).SeenByPlayer { if entry.HasComponent(tags.ItemTag) { - vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, color.RGBA{R: 15, G: 10, B: 222, A: 255}, false) + vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, colors.DeepSkyBlue, false) } else { - vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, color.RGBA{R: 255, G: 0, B: 0, A: 255}, false) + vector.DrawFilledRect(screen, float32(x), float32(y), blipSize, blipSize, colors.Red, false) } } } diff --git a/system/scene/titlescene.go b/system/scene/titlescene.go index 9361913..5e1fe96 100644 --- a/system/scene/titlescene.go +++ b/system/scene/titlescene.go @@ -8,6 +8,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/text/v2" "github.com/kensonjohnson/roguelike-game-go/assets" + "github.com/kensonjohnson/roguelike-game-go/internal/colors" "github.com/kensonjohnson/roguelike-game-go/internal/config" "github.com/yohamta/donburi" ) @@ -42,7 +43,7 @@ func (s *TitleScene) Draw(screen *ebiten.Image) { screen, message, x, y, - color.RGBA{R: 178, G: 182, B: 194, A: 255}, + colors.DarkGray, text.AlignCenter, text.AlignStart, ) @@ -90,7 +91,7 @@ func (s *TitleScene) drawTitleBackground(screen *ebiten.Image, count int) { } func drawLogo(screen *ebiten.Image, str string, x, y float64) { - drawTextWithShadow(screen, str, x, y, color.RGBA{R: 202, G: 146, B: 74, A: 255}, text.AlignCenter, text.AlignStart) + drawTextWithShadow(screen, str, x, y, colors.Peru, text.AlignCenter, text.AlignStart) } func drawCharacter(screen *ebiten.Image, x, y float64) { From 7b1a61c412ea6fff577b15b34373ff37204a1ee0 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 10 Sep 2024 20:38:24 -0500 Subject: [PATCH 50/55] Add UI asset --- assets/efs.go | 4 ++++ assets/images/ui/UICorner.png | Bin 0 -> 195 bytes 2 files changed, 4 insertions(+) create mode 100644 assets/images/ui/UICorner.png diff --git a/assets/efs.go b/assets/efs.go index f4a0f76..3a818ab 100644 --- a/assets/efs.go +++ b/assets/efs.go @@ -4,6 +4,7 @@ import ( "bytes" "embed" "log" + "log/slog" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/ebitenutil" @@ -26,6 +27,7 @@ var ( // UI UIPanel *ebiten.Image UIPanelWithMinimap *ebiten.Image + UICorner *ebiten.Image KenneyMiniFont *text.GoTextFace KenneyMiniSquaredFont *text.GoTextFace KenneyPixelFont *text.GoTextFace @@ -135,6 +137,7 @@ var ( // Loads all required assets, panics if any one fails. func init() { + slog.Debug("Loading Assets...") /*----------------------- --------- Tiles --------- -----------------------*/ @@ -150,6 +153,7 @@ func init() { -----------------------*/ UIPanel = mustLoadImage("images/ui/UIPanel.png") UIPanelWithMinimap = mustLoadImage("images/ui/UIPanelWithMinimap.png") + UICorner = mustLoadImage("images/ui/UICorner.png") kenneyMiniFontBytes, err := assetsFS.ReadFile("fonts/KenneyMini.ttf") if err != nil { diff --git a/assets/images/ui/UICorner.png b/assets/images/ui/UICorner.png new file mode 100644 index 0000000000000000000000000000000000000000..966235e7a41fe2ff515d07501c5221beedde6002 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}wVp1HArY-_ zrw4K!FyL^0{-XA{pFvgAM&aG}6YW)kPxybjuJlbX$Dz3VzEo=3H{0~T-_ITY@4=uP zH|KMs)Sc^$SC9WIVQ1NJ#vwFg-33_zhu=I4ES<_77*6vnxEUvUmyt0dOklI@-{&)5 p@Np!Ryk}hjq!;WF4{-&m(U)|UJ7ON-un*`E22WQ%mvv4FO#q3 Date: Tue, 10 Sep 2024 22:39:05 -0500 Subject: [PATCH 51/55] Add inventory event types --- event/inventory-events.go | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 event/inventory-events.go diff --git a/event/inventory-events.go b/event/inventory-events.go new file mode 100644 index 0000000..71f9dc5 --- /dev/null +++ b/event/inventory-events.go @@ -0,0 +1,11 @@ +package event + +import "github.com/yohamta/donburi/features/events" + +type OpenInventory struct{} + +var OpenInventoryEvent = events.NewEventType[OpenInventory]() + +type CloseInventory struct{} + +var CloseInventoryEvent = events.NewEventType[CloseInventory]() From a719f353629e5a86d7047de06f0a145b11c35a23 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 10 Sep 2024 22:39:21 -0500 Subject: [PATCH 52/55] Simplify interface --- system/scene/scenemanager.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/scene/scenemanager.go b/system/scene/scenemanager.go index ac712e5..b13bff0 100644 --- a/system/scene/scenemanager.go +++ b/system/scene/scenemanager.go @@ -13,8 +13,8 @@ import ( type Scene interface { Update() - Draw(screen *ebiten.Image) - Setup(world donburi.World) + Draw(*ebiten.Image) + Setup(donburi.World) Teardown() Ready() bool } From 180a6560f758efceab1ba76022a019a72b0a800c Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 10 Sep 2024 22:40:26 -0500 Subject: [PATCH 53/55] Add inventory event trigger --- system/action/player.go | 4 ++++ system/scene/levelscene.go | 23 +++++++++++++++++------ system/turn.go | 8 ++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/system/action/player.go b/system/action/player.go index cfabf25..1957f1f 100644 --- a/system/action/player.go +++ b/system/action/player.go @@ -2,6 +2,7 @@ package action import ( "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/kensonjohnson/roguelike-game-go/archetype/tags" "github.com/kensonjohnson/roguelike-game-go/component" "github.com/kensonjohnson/roguelike-game-go/event" @@ -29,6 +30,9 @@ func TakePlayerAction(ecs *ecs.ECS) bool { if ebiten.IsKeyPressed(ebiten.KeyDown) || ebiten.IsKeyPressed(ebiten.KeyS) { moveY += 1 } + if inpututil.IsKeyJustPressed(ebiten.KeyI) { + event.OpenInventoryEvent.Publish(ecs.World, event.OpenInventory{}) + } if ebiten.IsKeyPressed(ebiten.KeyQ) { turnTaken = true } diff --git a/system/scene/levelscene.go b/system/scene/levelscene.go index 8b2b4a5..5ebadd4 100644 --- a/system/scene/levelscene.go +++ b/system/scene/levelscene.go @@ -13,6 +13,7 @@ import ( "github.com/kensonjohnson/roguelike-game-go/system/layer" "github.com/yohamta/donburi" "github.com/yohamta/donburi/ecs" + "github.com/yohamta/donburi/features/events" ) type LevelScene struct { @@ -22,7 +23,7 @@ type LevelScene struct { func (ls *LevelScene) Update() { ls.ecs.Update() - event.ProgressLevelEvent.ProcessEvents(ls.ecs.World) + events.ProcessAllEvents(ls.ecs.World) } func (ls *LevelScene) Draw(screen *ebiten.Image) { @@ -92,27 +93,37 @@ func (ls *LevelScene) Teardown() { }() } -func progressLevel(world donburi.World, eventData event.ProgressLevel) { - newLevelScene := &LevelScene{} - SceneManager.GoTo(newLevelScene) -} - func (ls *LevelScene) configureECS(world donburi.World) { ls.ecs = *ecs.NewECS(world) // Add systems ls.ecs.AddSystem(system.Camera.Update) ls.ecs.AddSystem(system.Turn.Update) ls.ecs.AddSystem(system.UI.Update) + ls.ecs.AddSystem(system.InventoryUI.Update) // Add renderers ls.ecs.AddRenderer(layer.Background, system.Render.DrawBackground) ls.ecs.AddRenderer(layer.Foreground, system.Render.Draw) ls.ecs.AddRenderer(layer.UI, system.UI.Draw) ls.ecs.AddRenderer(layer.UI, system.DrawMinimap) + ls.ecs.AddRenderer(layer.UI, system.InventoryUI.Draw) if system.Debug.On { ls.ecs.AddRenderer(layer.UI, system.Debug.Draw) } // Add event listeners event.ProgressLevelEvent.Subscribe(ls.ecs.World, progressLevel) + event.OpenInventoryEvent.Subscribe(ls.ecs.World, openInventory) +} + +func progressLevel(world donburi.World, eventData event.ProgressLevel) { + slog.Debug("Progress Level") + newLevelScene := &LevelScene{} + SceneManager.GoTo(newLevelScene) +} + +func openInventory(world donburi.World, eventData event.OpenInventory) { + slog.Debug("Open Inventory") + system.Turn.TurnState = system.UIOpen + system.InventoryUI.Open() } diff --git a/system/turn.go b/system/turn.go index c09f2e3..3a40fe8 100644 --- a/system/turn.go +++ b/system/turn.go @@ -24,6 +24,7 @@ var Turn = TurnData{ const ( BeforePlayerAction = iota + UIOpen PlayerTurn MonsterTurn GameOver @@ -90,6 +91,9 @@ func (td *TurnData) Update(ecs *ecs.ECS) { component.Wallet.Get(playerEntry).AddAmount( component.Value.Get(entry).Amount, ) + archetype.RemoveItemFromWorld(entry) + itemName := component.Name.Get(entry) + playerMessages.WorldInteractionMessage = fmt.Sprintf("Picked up %s!", itemName.Value) break } @@ -113,6 +117,10 @@ func (td *TurnData) Update(ecs *ecs.ECS) { td.resetCounter() } + if td.TurnState == UIOpen { + // Do some input detection for inventory + } + if td.TurnState == PlayerTurn { if turnTaken := action.TakePlayerAction(ecs); turnTaken { td.progressTurnState() From 6894a05ddc0643a9c97f6f291fd8807c12eb938c Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 10 Sep 2024 22:40:34 -0500 Subject: [PATCH 54/55] Add more colors --- internal/colors/colors.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/colors/colors.go b/internal/colors/colors.go index ff52469..0e6b017 100644 --- a/internal/colors/colors.go +++ b/internal/colors/colors.go @@ -3,6 +3,9 @@ package colors import "image/color" var ( + Transparent = color.RGBA{} + Black = color.RGBA{0, 0, 0, 255} + White = color.RGBA{255, 255, 255, 255} Blue = color.RGBA{0, 0, 255, 255} CornflowerBlue = color.RGBA{100, 149, 237, 255} DeepSkyBlue = color.RGBA{0, 191, 255, 255} From b98aee13b502ec8a46a4c00e65fa95db97b46177 Mon Sep 17 00:00:00 2001 From: Kenson Johnson <94240602+kensonjohnson@users.noreply.github.com> Date: Tue, 10 Sep 2024 22:40:55 -0500 Subject: [PATCH 55/55] Create inventory draw func and helpers --- system/inventory.go | 172 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 system/inventory.go diff --git a/system/inventory.go b/system/inventory.go new file mode 100644 index 0000000..71db103 --- /dev/null +++ b/system/inventory.go @@ -0,0 +1,172 @@ +package system + +import ( + "image/color" + "log/slog" + "math" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/inpututil" + "github.com/kensonjohnson/roguelike-game-go/internal/colors" + "github.com/kensonjohnson/roguelike-game-go/internal/config" + "github.com/yohamta/donburi/ecs" +) + +type inventoryUi struct { + open bool +} + +var InventoryUI = inventoryUi{open: false} + +func (i *inventoryUi) Update(ecs *ecs.ECS) { + if !i.open { + return + } + + if inpututil.IsKeyJustPressed(ebiten.KeyI) || inpututil.IsKeyJustPressed(ebiten.KeyEscape) { + slog.Debug("Close Inventory") + Turn.TurnState = PlayerTurn + i.open = false + } +} + +func (i *inventoryUi) Draw(ecs *ecs.ECS, screen *ebiten.Image) { + if !i.open { + return + } + image := makeBox( + (15)*config.TileWidth, + (8)*config.TileHeight, + colors.SlateGray, + colors.Black, + ) + options := &ebiten.DrawImageOptions{} + options.GeoM.Reset() + options.GeoM.Translate(float64(5*config.TileWidth), float64(5*config.TileHeight)) + screen.DrawImage(image, options) + + image = makeBox( + (15)*config.TileWidth, + (5)*config.TileHeight, + colors.White, + colors.Transparent, + ) + options.GeoM.Reset() + options.GeoM.Translate(float64(5*config.TileWidth), float64(15*config.TileHeight)) + screen.DrawImage(image, options) + + image = makeBox( + (15)*config.TileWidth, + (8)*config.TileHeight, + colors.Peru, + colors.SlateGray, + ) + options.GeoM.Reset() + options.GeoM.Translate(float64(5*config.TileWidth), float64(25*config.TileHeight)) + screen.DrawImage(image, options) +} + +func (i *inventoryUi) Open() { + i.open = true +} +func (i *inventoryUi) Close() { + i.open = false +} + +// TODO: Refactor to a proper place, probably internal +// TODO: Allow for corner variants +// w and h are pixel values +func makeBox(w, h int, border, fill color.RGBA) *ebiten.Image { + + image := ebiten.NewImage(w, h) + corner := makeCornerImage(border, fill) + size := corner.Bounds().Size() + options := &ebiten.DrawImageOptions{} + + // NW + image.DrawImage(corner, options) + + // NE + options.GeoM.Rotate(degreesToRadians(90)) + options.GeoM.Translate(float64(w), 0) + image.DrawImage(corner, options) + + // SE + options.GeoM.Reset() + options.GeoM.Rotate(degreesToRadians(180)) + options.GeoM.Translate(float64(w), float64(h)) + image.DrawImage(corner, options) + + // SW + options.GeoM.Reset() + options.GeoM.Rotate(degreesToRadians(270)) + options.GeoM.Translate(0, float64(h)) + image.DrawImage(corner, options) + + // Draw top and bottom lines, plus fill + line := ebiten.NewImage(w-(size.X*2), 1) + line.Fill(border) + for i := 0; i < size.Y; i++ { + if i == 4 { + line.Fill(fill) + } + options.GeoM.Reset() + options.GeoM.Translate(float64(size.X), float64(i)) + image.DrawImage(line, options) + options.GeoM.Translate(0, float64(h-(i*2)-1)) + image.DrawImage(line, options) + } + + // Draw vertical lines and fill + line = ebiten.NewImage(w, h-(size.Y*2)) + line.Fill(fill) + ends := ebiten.NewImage(4, h-(size.Y*2)) + ends.Fill(border) + options.GeoM.Reset() + options.GeoM.Translate(float64(w-4), 0) + line.DrawImage(ends, options) + options.GeoM.Reset() + line.DrawImage(ends, options) + options.GeoM.Translate(0, float64(size.Y)) + image.DrawImage(line, options) + + return image +} + +func makeCornerImage(border, fill color.RGBA) *ebiten.Image { + image := ebiten.NewImage(32, 32) + block := ebiten.NewImage(4, 4) + options := &ebiten.DrawImageOptions{} + for y, row := range cornerShape { + for x, value := range row { + options.GeoM.Reset() + options.GeoM.Translate(float64(x*4), float64(y*4)) + switch value { + case -1: + continue + case 0: + block.Fill(border) + case 1: + block.Fill(fill) + } + image.DrawImage(block, options) + } + } + + return image +} + +func degreesToRadians(degrees int) float64 { + return float64(degrees) * math.Pi / 180 +} + +var cornerShape = [][]int8{ + {0, 0, 0, 0, 0, -1, 0, 0}, + {0, 1, 1, 1, 0, -1, 0, 1}, + {0, 1, 1, 1, 0, 0, 0, 1}, + {0, 1, 1, 1, 0, 1, 1, 1}, + {0, 0, 0, 0, 0, 1, 1, 1}, + {-1, -1, 0, 1, 1, 1, 1, 1}, + {0, 0, 0, 1, 1, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1}, +}