5.7 KiB
DartGame
A 3D darts game where the core loop is dragging modifiers onto board sectors to create compounding scoring interactions. Balatro's joker system, applied to a dartboard.
Built in Godot 4.7. Modding is a first-class concern, not an afterthought — our own content ships through the same loader community mods will use.
⚠️ Install git-lfs BEFORE you clone
Clone without it and you'll get 130-byte text pointers where your textures and models should be. Godot will throw import errors that look completely baffling and you will lose an hour.
NixOS — Home Manager (also writes the LFS filter config, so a fresh machine is correct with no imperative step):
programs.git = {
enable = true;
lfs.enable = true;
};
Or system-wide:
environment.systemPackages = with pkgs; [ git git-lfs ];
Debian/Ubuntu — sudo apt install git-lfs
macOS — brew install git-lfs
Then, once per machine:
git lfs install
Clone
git clone ssh://git@gitea.lilpenn.org:30009/ced/DartGame.git
cd DartGame
Note the
git@. Without it, SSH tries to log in as your local username and you getPermission denied (publickey). Gitea's SSH user is alwaysgit.
Verify LFS worked
file game/core/board/dartboard.glb
Want: glTF binary model, version 2, length 3302888 bytes
Bad: ASCII text ← that's an unfetched LFS pointer. Stop and fix it before opening Godot.
Setup
# GDScript formatter + linter
nix-shell -p gdtoolkit_4 # or: pipx install "gdtoolkit==4.*"
# Pre-commit hooks
pre-commit install
Then point Godot 4 at game/project.godot.
Where things live
| Nextcloud | This repo | |
|---|---|---|
.blend, .kra, .psd |
✅ | ❌ never |
.glb, .png (exported) |
❌ | ✅ (LFS) |
.res (collision shapes) |
❌ | ✅ (LFS) |
Code, scenes, .tres |
❌ | ✅ |
| Moodboards, references | ✅ | ❌ |
| Documentation | ✅ | ❌ |
Source files live in the company Nextcloud share, not here. They churn constantly, never merge, and 99% of their revisions have no corresponding code change — they'd bloat LFS history forever, on every clone.
Exported artifacts do live here, because they must version in lockstep with the code that references them. If a UV layout changes and a script needs updating, those belong in one commit.
SOURCES.md maps each repo artifact back to its Nextcloud source.
Layout
game/
├── core/ ← THE ENGINE. Contains zero game content. Ever.
│ ├── board/ dartboard scene, mesh, 21-slot logic, trimesh collision
│ ├── dart/ dart scene, drag/throw/flight physics
│ ├── modding/ mod registry, modifier base class
│ └── ui/ drop catcher, modifier tray, sector swatch
├── content/official/ ← OUR CONTENT, loaded as a mod like any other
│ ├── mod.json
│ ├── skins/ bulls/ · sectors/ · flat_tire/
│ └── modifiers/ (empty — Phase 5)
└── scenes/ main.tscn, camera_3d.gd
The two rules
1. Nothing in core/ knows about any specific content.
About to write if modifier.id == "mjolnir" in core/? Stop — you need a new primitive.
2. content/official/ must be deletable.
Delete it, and the game boots to an empty state without crashing. That's the proof the mod
pipeline is real. If it can't, we don't have a mod system — we have a plugin folder.
The board is 21 slots
dartboard.glb exports 21 material slots: sector_1 … sector_20, plus bulls. Each sector
is its own material, so it can be skinned and modified independently.
Hit detection reads the mesh face index from the raycast and maps it to the material name — not an angle. That's what lets a modifier rotate or remap the board and still score correctly.
Full detail in the docs.
Docs
Full documentation is an Obsidian vault at Nextcloud/DartGame/documentation/.
Start with Build Plan. Then Conventions before your first commit.
| Note | |
|---|---|
| Build Plan | What we're building. Eight phases, current status. |
| Conventions | Naming, git workflow, linting. Read before your first commit. |
| Architecture | How the engine works and why. |
| Godot Gotchas | Every trap that cost us time. Read it. |
| Modding | The mod format and public API. |
| Asset Pipeline | Nextcloud ↔ repo handoff, Blender export settings. |
| File Tree | Full directory reference. |
Contributing
Branches: feat/, fix/, chore/ off main. main is protected and always runnable.
Commits: Conventional Commits.
feat(modding): add ModRegistry namespaced lookup
fix(board): face-to-slot map missed the last surface
Before you open a PR:
gdformat --check game/passesgdlint game/passes- Nothing in
core/references a specific content ID - If an asset changed: source updated in Nextcloud,
SOURCES.mdcurrent git lfs statusshows binaries as LFS
⚠️ Moving files
Commit first. Godot's refactoring is fragile. Move files from inside the Godot editor's FileSystem dock, never your file manager, so references get fixed up.
If things break after a move or branch switch:
rm -rf game/.godot/
then Project → Reload Current Project. Worse than that? git restore . — which only works if
you committed first.
Status
Pre-alpha. Phase 0 (repo hygiene) complete. Phase 1 (skeleton) in progress — board and dart scenes exist with collision; the throw mechanic is still being tuned.
See Build Plan in the docs vault.