This tiny game was made in a week for Bitsy Jam #69, where the theme was "public domain." Inspired by Sherlock Holmes entering the public domain, I created a miniature Pokémon-style battler where the copyright-friendly Herlock Sholmes faces off against the original Sherlock Holmes.
Bitsy is primarily designed for simple narrative adventures, so implementing a turn-based combat system required extending the engine far beyond its intended use case.
Core Features
- Built a turn-based battle system inspired by Pokémon
- Implemented enemy AI and combat resolution logic
- Created dynamic health bars by modifying Bitsy tile data at runtime
- Developed game-state management for player stats, enemy stats, and win/loss conditions
- Added randomized battle dialogue and item usage systems
Building a Turn-Based Battler
Bitsy isn't designed for RPG-style combat, so I needed to build most of the battle logic myself. I used a small object-oriented architecture consisting of a Game object that managed player state and combat flow, and an Enemy object responsible for AI behavior and enemy-specific data.
Each turn generated a new Data object containing combat results, dialogue, and game-over information that was then passed back into Bitsy's scripting system. On game startup (and whenever a round ended), a new Game() instance was created to reset the battle state.
class Data {
constructor(txt, enemyTxt, gameOver, win) {
this.txt = txt;
this.enemyTxt = enemyTxt;
this.gameOver = gameOver;
this.win = win;
}
}
\\ example method showing player's `attack` functionality
attack() {
var enemyTurn = this.enemy.play(this.stunned);
let opt = SstunOpt;
if (!this.stunned) {
opt = playerAttackOptions[Math.floor(Math.random() * playerAttackOptions.length)];
this.defended = false;
this.enemy.takeDamage(opt.atk)
}
else {
this.stunned = false;
}
if (enemyTurn.atk > 0) {
this.takeDamage(enemyTurn.atk);
}
else if (enemyTurn.effect) {
his.takeEffect(enemyTurn.effect)
}
return new Data(opt.txt, enemyTurn.txt, this.hp <= 0 || this.enemy.hp < 0, this.enemy.hp <= 0);
}
Creating Dynamic Health Bars
One of the biggest challenges was creating health bars. Bitsy's tiles are normally static (except for things like items and characters/sprites), so there isn't a built-in way to display UI elements that change during gameplay. To work around this, I tracked the locations of health bar tiles and replaced them with different sprites as health values changed.
const SHERLOCK_HEALTHBAR_SPRITES = {
row: 14,
col_start: 12,
col_end: 4,
full: {
tile_id: "28",
tile_name: "tile 28",
},
half: {
tile_id: "2g",
tile_name: "s_half_health",
},
empty: {
tile_id: "2e",
tile_name: "s_empty_health",
}
}
The example above shows how I stored Sherlock's health bar sprite data. row and col variables help track where on the canvas these tiles were (as Bitsy games are organized in a 16x16 grid of 8-bit sprites). The tile data determines which sprite should be displayed as health values change. I then use this data inside updateTilemap(), which runs at the end of each turn.
updateTilemap(tilemap, player) {
if (player === "enemy") {
let data = HERLOCK_HEALTHBAR_SPRITES;
let curTile = this.enemy.currentHealthTile;
if (this.enemy.hp < this.enemy.lastHp) {
tilemap[data.row][curTile] = data.empty.tile_id;
this.enemy.currentHealthTile--;
this.enemy.lastHp = this.enemy.hp;
}
}
else {
let data = SHERLOCK_HEALTHBAR_SPRITES
let curTile = this.currentHealthTile;
if (this.hp < this.lastHp) {
tilemap[data.row][curTile] = data.empty.tile_id;
this.currentHealthTile--;
this.lastHp = this.hp;
}
else if (this.hp > this.lastHp) {
let i = 2; // num of tiles to restore
while (this.currentHealthTile != data.col_start && i > 0) {
this.currentHealthTile++;
i--;
tilemap[data.row][this.currentHealthTile] = data.full.tile_id;
}
this.lastHp = this.hp;
}
}
}
\\ called after updateTilemap() in Bitsy's game code
drawRoom(room[state.room], { redrawAll: true });
By directly modifying the room's tile data and forcing a redraw, I was able to simulate health bars increasing and decreasing during combat, helping the game feel much closer to a traditional RPG despite being built in Bitsy.
Closing Thoughts
Herlock vs. Sherlock was a fun exercise in pushing Bitsy beyond its intended use case. While Bitsy is primarily designed for simple narrative experiences, this project challenged me to build turn-based combat, state management, and dynamic UI systems within its limitations.
The project reinforced one of my favorite aspects of working with small game engines: finding creative solutions to problems the engine was never designed to solve. Building a functioning battler inside Bitsy ended up being just as enjoyable as making the game itself.