Video Coming Soon...
30: Rogue Part 3: Finishing Touches
WARNING This exercise is in DRAFT status, so there may be errors. If you find any, please email me at help@learncodethehardway.com so I can fix them.
The final exercise in this module should be the simplest one. You are only going to add basic combat to the game. Combat in games like this is generally based on "player moves into square where monster is." You'll get a basic form of this working, then I'll give you some extra things you can do to push this further so you can make your own game.
data.go
Changes
Let's turn off SHOW_PATHS
now that pathing is working:
View Source file curse-you-go-rogue/04_combat/data.go.diff Only
--- 03_pathing_enemies/data.go 2025-10-06 02:44:43.658927000 -0400
+++ 04_combat/data.go 2025-10-05 23:24:48.595383100 -0400
@@ -11,5 +11,5 @@
RENDER = true
SHOW_RENDER = false
- SHOW_PATHS = true
+ SHOW_PATHS = false
HEARING_DISTANCE = 6
)
movement.go
Changes
To make combat work you only need to detect that the player is trying to move into an Occupied()
cell and then Attack()
:
View Source file curse-you-go-rogue/04_combat/movement.go.diff Only
--- 03_pathing_enemies/movement.go 2025-10-06 12:00:47.091211600 -0400
+++ 04_combat/movement.go 2025-10-06 12:00:49.969794700 -0400
@@ -7,5 +7,7 @@
}
- if !game.Occupied(target) {
+ if game.Occupied(target) {
+ game.Attack(target)
+ } else {
game.Player.Pos = target
}
This is only a first step, and the combat can get very complex from here if you want, but this is mostly what you do.
New main.go
It's easier to give you a new main.go
then to use a .diff
. The algorithm seems to really hate the changes to this file.
View Source file curse-you-go-rogue/04_combat/main.go Only
package main
func main() {
DebugInit()
game := NewGame(17, 11)
game.InitScreen()
for {
game.NewMap()
dead_ends := game.NewMaze()
game.PlaceEnemies(dead_ends)
game.Render()
for game.HandleEvents() && game.Player.HP > 0 {
game.EnemyDeath()
game.CalculatePaths()
game.EnemyPathing()
game.Render()
}
if game.Player.HP <= 0 {
game.Restart()
} else {
game.Exit()
}
}
}
New combat.go
There's now a new combat.go
file that has the combat system:
View Source file curse-you-go-rogue/04_combat/combat.go Only
package main
import (
"fmt"
"math/rand"
)
func (game *Game) EnemyDeath() {
is_dead := make([]Position, 0, len(game.Enemies))
for pos, enemy := range game.Enemies {
if enemy.HP < 0 {
is_dead = append(is_dead, pos)
}
}
for _, pos := range is_dead {
delete(game.Enemies, pos)
}
}
func (game *Game) ApplyDamage(attacker *Enemy, defender *Enemy) {
damage := rand.Int() % attacker.Damage
defender.HP -= damage
if damage == 0 {
game.SetStatus("MISSED!")
} else if defender.HP > 0 {
game.SetStatus(fmt.Sprintf("HIT %d damage", damage))
} else {
game.SetStatus("DEAD!")
}
}
func (game *Game) Attack(target Position) {
enemy, hit_enemy := game.Enemies[target]
if hit_enemy {
game.ApplyDamage(&game.Player, enemy)
game.ApplyDamage(enemy, &game.Player)
}
}
The combat system consists of three functions:
EnemyDeath()
-- This is run once every turn and simply goes through themap
ofgame.Enemies
and checks if any haveHP
below zero. If they do then it removes them. This would be where you can put loot drops.ApplyDamage()
-- This does the damage calculations ifAttack()
determines the player or an enemy gets hit.Attack()
-- This is called and it use thegame.Enemies
map to determine if an enemy is in that position, and if it is then it callsApplyDamage()
on both the enemy and the player.
Going Farther
You have now reached the end of Module 2, but you aren't done yet. You should spend some time pushing this game as far as you can. Here's some suggestions you can implement:
- Add rooms to the map generation. You can do this by running
NewMaze()
twice. First you runNewMaze()
and get a list ofdead_ends
. Randomly select some of thedead_ends
. Reset the map back to all#
(WALL) and then "cut" rooms at the randomly selecteddead_ends
. Once you have holes cut out where the rooms go you can re-runNewMaze()
and the hunt-and-kill algorithm will walk around them producing a working maze with the rooms. - Play with the size and positioning of these rooms. You may notice an interaction when the room is odd sized (3, 5, 7) or placed on an odd coordinate (3,5).
- Combat could definitely be improved.
- Make the map bigger.
- Create a "fog of war" system. This is easily done by having another grid of
bool
that only records where the player has been. Then change yourDrawMap()
to only draw the cells that have atrue
in the fog map. - Better UI under the map.
- Loot! Right now you have no concept of weapons, loot, gold, or healing items.
- Healing. Can't really have a Rogue without healing.
Try to get those to work, and then see what you can do from there. Can you make the code better than mine?
Register for Learn Go the Hard Way
Register today for the course and get the all currently available videos and lessons, plus all future modules for no extra charge.