Contributing
You are invitied to contribute to the project. Here is the CONTRIBUTING.md as served in the main repo. It is the style guide that gives you the basics you need to know to start developing in the project.
Contributing to SR3E
Unofficial Shadowrun 3rd Edition Homebrew system for Foundry VTT v13 (V2).
Legal Contributors must own SR3E. Specs cite page numbers only (e.g., “SR3 p. 115”). No reproduction of rules text or tables.
1) Philosophy
- Game rules are suggestions, enforcment should be light, where it matters (Karma), or maybe not at all. TTRPGs rely on trust.
- Write code so it works cleanly without fallbacks. Aim for simple, elegant solutions where
??
or other defaults aren’t needed. - Let the language and Foundry handle errors. Avoid adding extra
throw
; they are for development only, not production. - Prefer clarity and self‑documentation. Use full words and clear method names instead of comments or abbreviations.
- Treat
sr3e.*
config keys as the baseline reference. Build on them directly so dropdowns and UI stay consistent. - Use the gated logger:
DEBUG && LOG.*(...)
so log calls are stripped when DEBUG is false.
2) Tech
- JavaScript (ESNext): MDN JavaScript | ECMAScript Spec
- Svelte 5 (runes): Svelte Docs
- LESS: lesscss.org
- Vite bundler: Vite Guide
- Gulp automated build/watch: Gulp Quick Start
- Foundry v13.342 (V2): Foundry API v13 | Systems Guide
- Node 22.x: Node.js 22 Docs
3) Local dev
Option A: In-place development (Verified)
- Run
npm ci
. - Clone or copy the repo directly into
FoundryVTT/Data/systems/sr3e
. - For the first build you can run
npm run build
. - Builds are automated on save (via gulp).
npx gulp
js, LESS and svelte files are automatically handled in most scenarios.
Option B: Symlink (according to AI)
- Run
npm ci
. - Keep the repo in a separate folder.
- Create a symlink from Foundry
Data/systems/sr3e
to your repo:- Windows:
mklink /J "%LOCALAPPDATA%/FoundryVTT/Data/systems/sr3e" "C:\path\to\repo"
- macOS:
ln -s /path/to/repo ~/Library/Application\ Support/FoundryVTT/Data/systems/sr3e
- Linux:
ln -s /path/to/repo ~/.local/share/FoundryVTT/Data/systems/sr3e
- Windows:
- For the first build you can run
npm run build
. - Builds are automated on save (via gulp).
npx gulp
js, LESS and svelte files are automatically handled in most scenarios.
4) Standards
Code style & structure
- Follow JavaScript conventions (lower camelCase for variables/functions, PascalCase for classes). Follow LESS conventions (kebab-case filenames, variables/mixins in shared files).
- Keep modules focused (one purpose per file).
- Import via Vite aliases only. Aliases are defined in
vite.config.js
. - Prefer clear, explicit names over comments. Use whole words.
- Use DEBUG‑gated logging. Example:
DEBUG && LOG.info("Mounted FirearmSheet", [__FILE__, __LINE__], { actorId });
Foundry V2
- Sheets extend
DocumentSheetV2
/ApplicationV2
. - Data models are registration only: define schema, defaults, and constraints. Keep behavior in services.
- Avoid prototype patches; add helpers/utilities in modules instead.
Config
Treat sr3e.*
as the baseline reference used across UI and logic. Use these keys directly; add new options in config first, then add matching i18n keys. Changing existing keys without updating all call sites will break dropdowns and labels across sheets.
sr3e.damageType = { l: "sr3e.damageType.l", m: "sr3e.damageType.m", s: "sr3e.damageType.s", d: "sr3e.damageType.d", lStun: "sr3e.damageType.lStun", mStun: "sr3e.damageType.mStun", sStun: "sr3e.damageType.sStun", dStun: "sr3e.damageType.dStun" };
sr3e.weaponMode = { manual: "sr3e.weaponMode.manual", semiauto: "sr3e.weaponMode.semiauto", burst: "sr3e.weaponMode.burst", fullauto: "sr3e.weaponMode.fullauto", blade: "sr3e.weaponMode.blade", explosive: "sr3e.weaponMode.explosive", energy: "sr3e.weaponMode.energy", blunt: "sr3e.weaponMode.blunt" };
Procedures
Procedures govern who rolls what, when, and how results are posted to chat. The system has state‑machine‑like properties (discrete steps, explicit transitions, a lock policy), but full details live in the developer docs. This page stays high‑level.
Add a procedure
- Subclass
AbstractProcedure
. - Register it in
sr3e.js
. - Respect
ProcedureLock
.
See the upcoming Procedures chapter in the docs for end‑to‑end flow and examples.
Gadgets & Effects
Active Effects
- Express any temporary or toggleable gameplay state as an
ActiveEffect
(buffs, penalties, locks, transient config). - Keep persistent identity in models; keep variability in effects.
- Expose the Active Effect Viewer on relevant Actor/Item sheets and apps so users can inspect, enable, or disable the current stack.
Gadgets
- Gadgets are specialized ActiveEffects used for add‑ons and attachments (e.g., a weapon modification providing a stateful bonus).
- Canonical flags:
flags.sr3e.gadget.{origin,gadgetType,commodity,isEnabled}
. - Toggle gadgets on/off to apply or suspend their effect. Gadgets are not items and carry only flags + effect payload.
- Editors:
GadgetEditorSheet
,WeaponModApp
.
5) i18n
- Pass all UI strings through
localize(config.sr3e.*)
. - Define keys in
lang/en.json
. - Use
ui.notifications.*(localize(key))
for user messages.
6) Linting & formatting
- ESLint + Svelte plugin.
- Prettier in CI.
7) Tests
- Optional. Add unit tests where it helps (math, pure services, parsers, table lookups).
- For UI/Foundry-bound flows, include a short manual verification list in the PR (e.g., create actor → equip weapon → run opposed roll → check Active Effects → chat post).
8) Git & Pull Requests
Flow: Branch → Pull Request → main
A Pull Request (PR) is a request to merge your branch into main
. It’s where code is reviewed and CI runs.
Option A: GitHub Desktop (GUI)
- Open GitHub Desktop and select the
sr3e
repository (clone it if needed). - Create a branch: Branch → New Branch… → name it like
feature/short-branch-name
→ Create Branch. - Make changes in your editor. Return to GitHub Desktop.
- Commit with Conventional Commits:
- Summary examples:
feat(weapons): add smartlink gadget toggle (SR3 p. 115)
fix(procedures): prevent double-advance on resolve
docs(readme): add manifest URL
- If breaking:
feat!(procedures): unify attack/defense flow
and add a description in the extended area beginning withBREAKING CHANGE:
. - Click Commit to feature/….
- Summary examples:
- Push: click Push origin.
- Open a Pull Request: click Create Pull Request in Desktop. On GitHub, fill in:
- What changed and why (include SR3 page references).
- Manual verification steps you followed (e.g., create actor → equip weapon → run opposed roll → check Active Effects → chat post).
- Add screenshots/GIFs for UI changes.
- Request review (assign a reviewer). Address comments by committing more changes; push to update the PR. A maintainer merges when approved.
Option B: Command line (Git)
1) Create a branch
git checkout -b feature/short-branch-name
2) Commit using Conventional Commits
feat(scope): summary
— new functionality (bumps minor)fix(scope): summary
— bug fix (bumps patch)docs|refactor|perf|test|build|ci|chore(scope): summary
Examples:
git add .
git commit -m "feat(weapons): add smartlink gadget toggle (SR3 p. 115)"
# Breaking change:
# git commit -m "feat!(procedures): unify attack/defense flow" -m "BREAKING CHANGE: replaces legacy roll hooks"
3) Push and open a Pull Request
git push -u origin feature/short-branch-name
Open a Pull Request on GitHub from your branch → main
. Describe changes, include SR3 citations, and paste manual verification steps.
Pull Request Checklist
9) Releases
- Tags:
vX.Y.Z
. - Changelog maintained.
- Uses standard-version: commit types drive CHANGELOG and version bumps.
feat:
→ minor,fix:
→ patch,feat!
orBREAKING CHANGE:
→ major.
- Release flows:
npm run release:alpha|beta|release
.
10) Migrations (only in beta & release)
- Add version-gated migration if schema changes.
- Keep them idempotent.
11) Security
- ¯\_(ツ)_/¯
12) Done
- V2 patterns in use
- Models are state only
- Logging behind DEBUG
- i18n complete
- Critical logic is simple or tested where applicable
- Docs updated
13) Citations
- Reference page numbers in commits/PRs.
- Do not copy rules text, use page citations instead.