Repository navigation
Homebrew ability score increases the player places, and save-proficiency grants #69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
codeGlaze
wants to merge
33
commits into
integration
Choose a base branch
from
feature/asi-spreads
base: integration
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
cf18e73
Mark homebrew files that use new-format features, so an old build ref…
codeGlaze cc6252c
Homebrew ability score increases the player places, and save-proficie…
codeGlaze 805c548
What's New and the branch changelog: placed ability increases and sav…
codeGlaze f28e015
Fold the branch changelog into Summer Patch
codeGlaze 7d51c65
Give the three ASI widgets their own React keys in the race and subcl…
codeGlaze 8cea519
e2e: the whole life of ASI homebrew on the seeded server
codeGlaze 1356200
Subrace and feat builders author ability score increases and save grants
codeGlaze 3669c75
asi-journey: author the subrace and feat in their builders, and the r…
codeGlaze ec0b7a0
New keeps the Option Source, and the box shows the item's own source
codeGlaze da336a7
e2e: New keeps the Option Source in all 13 builders
codeGlaze 71418f8
e2e: drop the Option Source workarounds
codeGlaze 1240ae7
CHANGELOG: New keeps the Option Source (Summer Patch, Fixed)
codeGlaze cf0e64b
Merge origin/integration into feature/asi-spreads
codeGlaze 3e5b152
Skip ability and save entries the compilers cannot read, wherever the…
codeGlaze 0916761
Format detection never throws on a malformed marker
codeGlaze b702f86
Builder forms show an unreadable grant entry with a Remove, instead o…
codeGlaze a0b0c7e
e2e: the static test servers serve only resources/public, on loopback
codeGlaze ecb67d2
e2e: assert the column, not the page; wait for an import instead of f…
codeGlaze 8ae02d1
A save choice can always be finished
codeGlaze f2c6034
The raw export escape hatches always produce a file
codeGlaze 53dad6a
The ASI and save menus show a pool they have no entry for
codeGlaze dbb849e
Save warnings read only list fields
codeGlaze 7f6e270
The amount and count menus show a stored number they have no entry for
codeGlaze 69d2ce4
The feat's released checkboxes start a set from anything else
codeGlaze 060d62f
Guard the backgrounds list like races: a background that throws is se…
codeGlaze ed3b82f
Every grant reader and form is tested against malformed imported shapes
codeGlaze 75b7ed4
e2e: the messy pack's race and background open in their builders
codeGlaze 613ef3e
A list raises each ability once
codeGlaze 3216e5a
Export draft always downloads
codeGlaze a5b7c16
The picker lists only the increases applied
codeGlaze 20b288b
A feat reads a list spread, and junk increases or file markers never …
codeGlaze c037584
The feat and subclass builders say when their increases are junk
codeGlaze 654d848
A released feat set names what it cannot raise; a save count shows wh…
codeGlaze File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| (ns orcpub.dnd.e5.orcbrew-format | ||
| "Constants, serialization and format versioning for orcbrew files, on the JVM (demo emitter) | ||
| and in the browser (every export/import path). Content that is fully backward compatible is v1 | ||
| and ships PLAIN, so old builds read it. Any incompatible feature makes it v2, wrapped as | ||
| `{:orcbrew/format-version 2 :orcbrew/requires [...] :orcbrew/content <plugin map>}`, which old | ||
| builds fail to load rather than corrupt; new builds unwrap it, or refuse a version they cannot | ||
| read with a clear message. See docs/kb/orcbrew-format-versioning.md." | ||
| (:require [clojure.string :as str] | ||
| #?(:clj [clojure.pprint :as pprint] | ||
| :cljs [cljs.pprint :as pprint]))) | ||
|
|
||
| (def new-extension | ||
| "File extension for v2 (not-backward-compatible) files. NAME NOT FINAL — being | ||
| polled with the community; kept in this one constant so the final choice is a | ||
| single-line change. See docs/kb/orcbrew-format-versioning.md." | ||
| ".orcbrewx") | ||
|
|
||
| (def current-format-version | ||
| "The version stamped into content that uses non-backward-compatible features. | ||
| Bumped when a new, incompatible generation of features lands." | ||
| 2) | ||
|
|
||
| (def supported-format-version | ||
| "The highest format version this build can read. A file whose version exceeds | ||
| this is refused on import with a clear message (forward-compat insurance: a | ||
| build from this generation cleanly declines a future v-next file)." | ||
| 2) | ||
|
|
||
| ;; ── Classification ────────────────────────────────────────────────────────── | ||
| ;; What makes content v2. Bias CONSERVATIVE: over-tagging only gates a file needlessly; | ||
| ;; under-tagging lets an old build silently mangle it. Add each new incompatible marker here. | ||
|
|
||
| (def incompatible-content-types | ||
| "Whole content types old builds don't know — any content under these is v2." | ||
| #{:orcpub.dnd.e5/draconic-ancestries | ||
| :orcpub.dnd.e5/fighting-styles}) | ||
|
|
||
| (defn- present? | ||
| "A marker that counts: present and not an empty collection. A malformed value such as `true` | ||
| counts too, conservatively, and never reaches `seq`." | ||
| [v] | ||
| (and (some? v) (or (not (coll? v)) (boolean (seq v))))) | ||
|
|
||
| (defn- item-features | ||
| "The non-backward-compatible feature markers a single content ITEM carries." | ||
| [item] | ||
| (when (map? item) | ||
| (cond-> #{} | ||
| (present? (:grants item)) (conj :grants) | ||
| (present? (:save-proficiencies item)) (conj :save-proficiencies) | ||
| ;; the ability-increase SPREAD is the vector form [[amount pool] ...]; the | ||
| ;; legacy feat format is a set (#{:str}) and stays backward compatible. | ||
| (vector? (:ability-increases item)) (conj :ability-increase-spread)))) | ||
|
|
||
| (defn detect-incompatible-features | ||
| "Scan content for the features that make it v2. Returns a sorted set of feature | ||
| markers (empty = fully backward compatible). Crash-safe: malformed shapes yield | ||
| no markers rather than throwing, so it can run on any export (incl. emergency | ||
| dumps). Accepts either a multi-plugin `{source plugin}` map or a single | ||
| `{content-type items}` plugin." | ||
| [content] | ||
| (if-not (map? content) | ||
| (sorted-set) | ||
| (let [plugins (if (every? string? (keys content)) (vals content) [content])] | ||
| (into (sorted-set) | ||
| (mapcat | ||
| (fn [plugin] | ||
| (when (map? plugin) | ||
| (mapcat | ||
| (fn [[content-type items]] | ||
| (concat | ||
| (when (and (contains? incompatible-content-types content-type) | ||
| (present? items)) | ||
| [content-type]) | ||
| (when (map? items) | ||
| (mapcat item-features (vals items))))) | ||
| plugin))) | ||
| plugins))))) | ||
|
|
||
| (defn content-format-version | ||
| "1 if `content` is fully backward compatible, else `current-format-version`." | ||
| [content] | ||
| (if (seq (detect-incompatible-features content)) current-format-version 1)) | ||
|
|
||
| ;; ── Envelope (v2 files only) ──────────────────────────────────────────────── | ||
|
|
||
| (defn envelope? | ||
| "True if `data` is a version-stamped v2 envelope (vs. a plain plugin map)." | ||
| [data] | ||
| (and (map? data) (contains? data :orcbrew/format-version))) | ||
|
|
||
| (defn stamp | ||
| "Wrap `content` in the version envelope IFF it contains an incompatible feature, else return it | ||
| unchanged (plain v1). Idempotent: an envelope passes through, so every export path can call it. | ||
| `envelope-meta` (2-arity) merges into the envelope, e.g. `{:orcbrew/content-version 1}` (the | ||
| content's own revision, not the format version); it is dropped when the content is v1." | ||
| ([content] (stamp content nil)) | ||
| ([content envelope-meta] | ||
| (if (envelope? content) | ||
| content | ||
| (let [features (detect-incompatible-features content)] | ||
| (if (seq features) | ||
| (merge {:orcbrew/format-version current-format-version | ||
| :orcbrew/requires (vec features) | ||
| :orcbrew/content content} | ||
| envelope-meta) | ||
| content))))) | ||
|
|
||
| (defn unwrap | ||
| "The plugin map inside a v2 envelope, or `data` unchanged when it's a plain file." | ||
| [data] | ||
| (if (envelope? data) (:orcbrew/content data) data)) | ||
|
|
||
| (defn file-version | ||
| "The format version a parsed file declares: the envelope's tag, or 1 for a plain | ||
| (un-enveloped) file." | ||
| [data] | ||
| (if (envelope? data) (:orcbrew/format-version data) 1)) | ||
|
|
||
| (defn content-version | ||
| "The content's OWN revision number, read from a stamped envelope's | ||
| `:orcbrew/content-version` (nil when absent or on a plain file). Distinct from | ||
| the FORMAT version: this tracks a content pack's revisions — e.g. the demo pack's | ||
| release number, for copy-on-edit provenance / graduation — not the file-format | ||
| compatibility class. Read it from the raw parsed file BEFORE `unwrap` discards | ||
| the envelope." | ||
| [data] | ||
| (when (envelope? data) (:orcbrew/content-version data))) | ||
|
|
||
| (defn compat-check | ||
| "Decide whether this build can import `data`. Returns {:ok? true} for a plain | ||
| file or a v2 envelope this build supports; {:ok? false :message ...} when the | ||
| file's version exceeds `supported-format-version`, so the caller can refuse with | ||
| a clear, actionable message instead of mangling content it doesn't understand." | ||
| [data] | ||
| (let [v (file-version data)] | ||
| (if (and (number? v) (> v supported-format-version)) | ||
| {:ok? false | ||
| :version v | ||
| :requires (:orcbrew/requires data) | ||
| :message (str "This content is in a newer format (v" v ") than this app " | ||
| "version can read (v" supported-format-version "). Update the " | ||
| "app to import it" | ||
| (when-let [r (let [r (:orcbrew/requires data)] | ||
| (seq (keep #(when (or (keyword? %) (string? %)) (name %)) | ||
| (when (coll? r) r))))] | ||
| (str " — it uses: " (str/join ", " r))) | ||
| ".")} | ||
| {:ok? true :version v}))) | ||
|
|
||
| (defn serialize-orcbrew | ||
| "Pure serialize of homebrew content to .orcbrew text — no side effects, so it's | ||
| unit-testable and shared by every export path. pretty-print? is opt-in: pprint | ||
| inflates 3-5MB files to ~10-20MB and can freeze the UI." | ||
| [data & {:keys [pretty-print?]}] | ||
| (if pretty-print? | ||
| (with-out-str (pprint/pprint data)) | ||
| (str data))) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.