Files
DartGame/README.md
T

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/Ubuntusudo apt install git-lfs macOSbrew 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 get Permission denied (publickey). Gitea's SSH user is always git.

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_1sector_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/ passes
  • gdlint game/ passes
  • Nothing in core/ references a specific content ID
  • If an asset changed: source updated in Nextcloud, SOURCES.md current
  • git lfs status shows 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.