Fat Old Yeti

Fat Old Yeti

Being a blog of thoughts and tutorials from a hobby game developer.

09 Feb 2022

Roguelike Tutorial 17

Roguelike in Go - Part 17 (Combat Fixes)

All of the source code for this tutorial can be found here.

Ok, we now have a UI which shows us how punishing combat is on the player. Let’s make it more interesting by giving the player a real weapon and armor to start with. That way, we can look at what happens when a monster dies.

Open up world.go and look for the following code:

		AddComponent(meleeWeapon, &MeleeWeapon{
			Name:          "Fist",
			MinimumDamage: 1,
			MaximumDamage: 3,
			ToHitBonus:    2,
		}).
		AddComponent(armor, &Armor{
			Name:       "Burlap Sack",
			Defense:    1,
			ArmorClass: 1,
		}).

As you can see, that’s pretty weak for the poor player. Let’s give him some plate armor and a battleaxe to start with. Obviously we wont want to keep that in the end, but for now, it makes it a lot easier to test our game as we go. Give the player some ridiculous values for now to make them nearly invincible.

		AddComponent(meleeWeapon, &MeleeWeapon{
			Name:          "Battle Axe",
			MinimumDamage: 10,
			MaximumDamage: 20,
			ToHitBonus:    3,
		}).
		AddComponent(armor, &Armor{
			Name:       "Plate Armor",
			Defense:    15,
			ArmorClass: 18,
		}).

Now run the game and find your first skeleton. Upon killing it, a few bugs are found. First, looking at the new UI Log, I see a problem.

attack after death

Oops. We killed the skeleton and it got to attack us back. We need to change that.

We need to look at current health before making an attack. Open up combat_system.go and find the following line:

	//Roll a d10 to hit
	toHitRoll := GetDiceRoll(10)

Right above this, we want to put the following:

	//if the attacker is dead, don't let them attackerWeapon
	if attacker.Components[health].(*Health).CurrentHealth <= 0 {
		return
	}

So now, things that have been killed don’t get a last after-death attack. Run the game again and verify this is working.

This leaves us with one more bug to be seen. When we kill a skeleton, the space it occupied is still blocked from movement. This will not do.

Now, we really only care if the monsters die here, as player death results in the game being over. So, the best place to do this would be in the monster_system.

Open monster_systems.go and look for the following:

	//The monster is right next to the player.  Just smack him down
	AttackSystem(game, pos, &playerPosition)

When this call returns, we know the health status of the monster. We also have the position of the monster already, so everything we need is currently available. Directly under that call, add the following:

if result.Components[health].(*Health).CurrentHealth <= 0 {
	//this monster is dead
	//clear the tile
	t := l.Tiles[l.GetIndexFromXY(pos.X, pos.Y)]
	t.Blocked = false
}

Now when the monster dies, the tile is free to move into again. At this point, combat is working a lot better than it was.

If you have any questions on this tutorial, feel free to hit me up at fatoldyeti@gmail.com or @idiotcoder on the gophers slack. In addition, I’m on the Ebiten Discord as Idiotcoder. Also, there is my discord linked above.