Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 41 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

> Turn household chores into a pixel art RPG adventure for the whole family.

Each family member gets a hero and a daily monster to fight. Complete chores to deal damage defeat the monster before midnight to earn gold, or it strikes back. Spend gold on rewards you've agreed on as a family.
Each family member gets a hero and a daily monster to fight. Complete chores to deal damage - defeat the monster before midnight to earn gold, or it strikes back. Spend gold on rewards you've agreed on as a family.

Built to run on a kitchen tablet, locked to a browser, always-on.

![Questboard main screen](screenshot.png)
![Questboard main screen](screenshots_combined.png)

[![Release](https://img.shields.io/github/v/release/thillygooth/questboard)](https://github.com/thillygooth/questboard/releases)
[![License](https://img.shields.io/github/license/thillygooth/questboard)](LICENSE)
Expand All @@ -16,14 +16,14 @@ Built to run on a kitchen tablet, locked to a browser, always-on.

## What it looks like

The interface runs fullscreen in the browser. Each player gets a card with their hero, live HP bar, and current monster. Completing chores hits the monster chain them fast for combo damage, score a crit, or find loot drops.
The interface runs fullscreen in the browser. Each player gets a card with their hero, live HP bar, and current monster. Completing chores hits the monster - chain them fast for combo damage, score a crit, or find loot drops.

**Player card** hero class, gold, XP bar, crit %, streak
**Monster section** animated pixel art enemy, segmented HP bar, kill = gold reward
**Chore grid** daily / weekly / monthly quests shown with damage values
**Dungeon map** per-player fog-of-war grid; chores earn moves, rooms hide gold, traps, and mini-bosses
**Reward shop** spend gold on family rewards you configure in setup
**History** full log of kills, loot drops, rewards redeemed, badges earned
**Player card** - hero class, gold, XP bar, crit %, streak
**Monster section** - animated pixel art enemy, segmented HP bar, kill = gold reward
**Chore grid** - daily / weekly / monthly quests shown with damage values
**Dungeon map** - per-player fog-of-war grid; chores earn moves, rooms hide gold, traps, and mini-bosses
**Reward shop** - spend gold on family rewards you configure in setup
**History** - full log of kills, loot drops, rewards redeemed, badges earned

---

Expand All @@ -41,21 +41,43 @@ The interface runs fullscreen in the browser. Each player gets a card with their
| 🛡 Shield Aura | Active power-up blocks the midnight gold penalty |
| 🏅 Badges & titles | Unlock achievements and choose your hero title |
| ⭐ Prestige | Reset XP at level 10 for a permanent gold % bonus |
| 🗺 Dungeon map | Explore a per-player fog-of-war dungeon chores = moves |
| 🗺 Dungeon map | Explore a per-player fog-of-war dungeon - chores = moves |
| 🏆 Weekly leaderboard | See who earned the most gold this week |
| ⚡ Auto-resets | Daily/weekly/monthly chores reset at exactly the right time |
| 🌙 Overnight penalty | Fail to kill your monster and lose gold at midnight |
| 👥 Up to 6 players | Each with their own hero, monster, gold, XP, and dungeon |
| 👤 Solo chores | Personal tasks (brushing teeth, homework) tracked per player |
| 📱 Kids & adults modes | Separate difficulty scaling kids get easier monsters |
| 📱 Kids & adults modes | Separate difficulty scaling - kids get easier monsters |
| 🎮 CRT overlay | Optional scanline filter for maximum retro vibes |
| 🔍 UI Scale | Mini / Heroic / Epic zoom modes for any screen size |

📖 **[Full game guide →](questboard/DOCS.md)** - hero classes, dungeon mechanics, combat details, badges, power-ups, and more.

---

## Quick Reference

### Hero Classes

Classes are cosmetic - they change your hero's sprite but don't affect stats. Available: Warrior, Mage, Witch, Rogue, Paladin, Ranger, Frost Knight.

### Midnight Monster Penalty

If you don't defeat your daily monster before midnight, it **attacks** - you lose gold equal to its attack power. Tougher monsters hit harder. Shield Aura power-up blocks the penalty.

### Dungeon

A per-player fog-of-war dungeon you explore by earning moves from chores. Use the on-screen d-pad to navigate. Find gold, treasure chests, keys, and fight dungeon monsters - but watch out for traps. Deeper floors have better rewards.

### Reset Week

Manual full reset: clears all gold, chore progress, streaks, and power-ups for every player. History, XP, levels, and badges are kept. Triggers immediately (not aligned to a specific day).

---

## Install on Home Assistant

**Step 1 Run the container**
**Step 1 - Run the container**

In the HA Terminal add-on:

Expand All @@ -66,7 +88,7 @@ docker run -d --restart unless-stopped -p 8099:8099 \
ghcr.io/thillygooth/questboard:latest
```

**Step 2 Add to the HA sidebar**
**Step 2 - Add to the HA sidebar**

In `configuration.yaml` (use the File Editor add-on):

Expand Down Expand Up @@ -102,7 +124,7 @@ Open `http://localhost:8099`.

## Running on a Separate Host

Questboard is a standalone web app no connection to Home Assistant required. Run it on any machine on your network.
Questboard is a standalone web app - no connection to Home Assistant required. Run it on any machine on your network.

```yaml
panel_iframe:
Expand All @@ -123,11 +145,11 @@ A setup wizard runs the first time you open the app:

1. Set the number of players (1–6)
2. For each player: name, difficulty (kids / adults), avatar class
3. Choose which chores to track toggle on/off, set solo vs. shared, adjust values
4. Configure the reward shop enable/disable rewards, set custom costs
3. Choose which chores to track - toggle on/off, set solo vs. shared, adjust values
4. Configure the reward shop - enable/disable rewards, set custom costs
5. Configure power-ups and display options (CRT overlay, UI scale)

After launch, tap **Settings** for a tabbed editor: Party, Quests, Rewards, Power-Ups, and Display no need to re-run the wizard.
After launch, tap **Settings** for a tabbed editor: Party, Quests, Rewards, Power-Ups, and Display - no need to re-run the wizard.

---

Expand All @@ -148,12 +170,12 @@ The dev server proxies `/api/*` to the backend automatically.

## License

[CC BY-NC 4.0](LICENSE) free to share and adapt for non-commercial purposes with attribution. Commercial use is prohibited.
[CC BY-NC 4.0](LICENSE) - free to share and adapt for non-commercial purposes with attribution. Commercial use is prohibited.

Sprite assets from [OpenGameArt.org](https://opengameart.org) under CC-BY / CC0 licenses. Font: [Pixelated Elegance](https://www.fontspace.com/pixelated-elegance-font-f126145) by GGBotNet (CC0).

---

## Credits

Overkill system, power-ups, solo chore mode, tabbed settings, new hero classes, and gold economy rebalancing contributed by **[TreasuryMatt](https://github.com/TreasuryMatt)** thanks for the excellent fork!
Overkill system, power-ups, solo chore mode, tabbed settings, new hero classes, and gold economy rebalancing contributed by **[TreasuryMatt](https://github.com/TreasuryMatt)** - thanks for the excellent fork!
2 changes: 1 addition & 1 deletion frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export default function App() {
const base = ALL_CHORES
.filter(c => enabled.has(c.id))
.map(c => overrides[c.id] ? { ...c, ...overrides[c.id] } : c);
return [...base, ...(config.customChores ?? [])];
return [...base, ...(config.customChores ?? []).map(c => overrides[c.id] ? { ...c, ...overrides[c.id] } : c)];
}, [config]);

const bonusChoreId = useMemo(() => {
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/components/HistoryTab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const TYPE_TILE = {
gold: 55,
penalty: 123,
reward: 41,
badge: 29,
loot: 72,
};

export default function HistoryTab({ history, players, weeklyGold = {} }) {
Expand Down Expand Up @@ -60,8 +62,14 @@ export default function HistoryTab({ history, players, weeklyGold = {} }) {
<div className="redeemed-list">
{hist.map((h, i) => {
const tile = TYPE_TILE[h.type] ?? 118;
const action = h.type === 'chore' ? 'completed' : h.type === 'penalty' ? 'attacked by' : 'slew';
const pts = h.type === 'chore' ? `(+${h.pts} dmg${h.crit ? ' CRIT' : ''})` : h.type === 'penalty' ? `(-${h.pts} gold)` : `(+${h.pts} gold${h.lucky ? ' LUCKY' : ''})`;
let action, pts;
switch (h.type) {
case 'chore': action = 'completed'; pts = `(+${h.pts} dmg${h.crit ? ' CRIT' : ''})`; break;
case 'penalty': action = 'attacked by'; pts = `(-${h.pts} gold)`; break;
case 'badge': action = 'earned'; pts = h.icon || '🏅'; break;
case 'loot': action = 'found'; pts = h.pts ? `(+${h.pts} gold)` : (h.xp ? `(+${h.xp} XP)` : ''); break;
default: action = 'slew'; pts = h.pts != null ? `(+${h.pts} gold${h.lucky ? ' LUCKY' : ''})` : ''; break;
}
return (
<div key={i} className="redeemed-item">
<TileSprite tile={tile} display={14} />
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/SetupWizard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ function CustomForm({ form, setForm, onSubmit, onCancel, extraFields }) {
// ── Shared: chore list (wizard step + Quests tab) ─────────────────────────────
function ChoreSection({ players, enabledChores, onToggle, choreOverrides, onOverride, customChores, onAddCustom, onRemoveCustom }) {
const [addingCustom, setAddingCustom] = useState(false);
const [form, setForm] = useState({ name: '', icon: '⭐', pts: 2, who: 'all', freq: 'daily' });
const [form, setForm] = useState({ name: '', icon: '⭐', pts: 2, who: 'all', freq: 'daily', mode: 'party' });

const modes = new Set(players.map(p => p.mode));
const daily = ALL_CHORES.filter(c => c.freq === 'daily');
Expand All @@ -317,7 +317,7 @@ function ChoreSection({ players, enabledChores, onToggle, choreOverrides, onOver
function submitCustom() {
if (!form.name.trim()) return;
onAddCustom({ ...form, id: `custom_${Date.now()}`, name: form.name.trim() });
setForm({ name: '', icon: '⭐', pts: 2, who: 'all', freq: 'daily' });
setForm({ name: '', icon: '⭐', pts: 2, who: 'all', freq: 'daily', mode: 'party' });
setAddingCustom(false);
}

Expand Down Expand Up @@ -399,6 +399,10 @@ function ChoreSection({ players, enabledChores, onToggle, choreOverrides, onOver
<option value="weekly">Weekly</option>
<option value="monthly">Monthly</option>
</select>
<select style={{ ...S.input, flex: 1 }} value={form.mode} onChange={e => setForm(f => ({ ...f, mode: e.target.value }))}>
<option value="party">ALL (shared)</option>
<option value="solo">1P (per player)</option>
</select>
</>
}
/>
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ body.crt::after {
.torch-wrap.torch-left { left: 14px; }
.torch-wrap.torch-right { right: 14px; }

/* Hide torches on screens where they overlap player cards (tablets, small laptops) */
@media (max-width: 1400px) {
.torch-wrap { display: none; }
}

.torch-sprite {
width: 48px;
height: 96px;
Expand Down
Loading
Loading