Skip to content

Commit

Permalink
Cleaning up console errors on pyramid dice and items now have settabl…
Browse files Browse the repository at this point in the history
…e pictures.
  • Loading branch information
lupestro committed Aug 4, 2024
1 parent 7cb613c commit b6c3365
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 60 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Fudge system for FoundryVTT

## 2.3.0 - Small Improvements
- [INFO] Updated README.md with pyramid dice information
- [FEATURE] Provide UI for images on items
- [BUGFIX] Cleaned up V12 console noise around pyramid die support

## 2.2.0 - Basic FoundryVTT 12 compatibility
- [FEATURE] Verified that it comes up cleanly in both FoundryVTT 11 and 12. (Please report any issues I missed - testing here is sadly not yet comprehensive.)

Expand Down
56 changes: 54 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,64 @@ This project implements a FUDGE system for Foundry Virtual Tabletop (FoundryVTT)
## Using the System

Install the system. Build a world using the system.
* Configure whether your world should use the standard or extended trait levels. You must pick the attribute set to use (or create one of your own.)
* Configure whether your world should use the standard, expanded, or extended trait levels. You must pick the attribute set to use (or create one of your own.)
* Make characters and drag items onto them for attribute sets, skills, gifts, faults, and equipment. The compendia contain the traits used by Fantasy Fudge, supplied as OGL content. You may find them useful, or you can create your own items.
* Use the 5-point Fudge worksheet with the Fantasy Fudge skill set (or your own compendium of skills) for easy skill selection.
* Make a story, roll some dice, and take some damage.

That's about it.
That's about it, but I've added a couple of extras.

### Pyramid Fudge Dice

This also adds a Fudge die that has arguably better characteristics for gameplay.
* A Fudge die is essentially a d3, drawn on a d6, adjusted for a 0 center value
* A Pyramid Fudge die is a d5, drawn on a D10, adjusted for a 0 center value, with the extra values being -- and ++.

You roll two of them, and this leads to a curve between -4 and +4 with fewer rolls on the center values and more rolls in the wings.
More random outcomes lead to more interesting gameplay.
In the following diagram, the f graph is the 4dF outcome and the p graph is the 2dP outcome.
```
25% +
| f
|
|
|
20% + f p f
|
|
|
| p p
15% +
|
|
| pf fp
|
10% +
|
| p p
|
|
5% + f f
p p
|
|
f f
0% +----+----+----+----+----+----+----+----+
-4 -3 -2 -1 0 1 2 3 4
```
The "p" curve is a linear pyramid to a max of 20% at zero. The "f" curve is a sharp normal curve, with much lower
probability at the edges, and a much sharper zero peak at 24%. Your odds of hitting numbers like -2 and +2 are the
same with either type of roll, but your odds of hitting +/-3 or +/4 are really rare in the standard 4dF, with the
probability concentrated between -1 and +1, which is just plain boring if you end up rolling a lot.

You still won't hit the higher and lower values with 2dP nearly as often as you hit between -2 to +2, but your odds
of -4 or +4 are roughly comparable to hitting 1 or 20 on a d20, so the crit experience is similar to D&D, and with the
pyramid curve, the roleplaying experience is similar to a 2d6 system. This feels like the best of all possible worlds.

Meanwhile, all the values are still between -4 and +4, so nothing else in the system needs to be touched.

You can always roll 2dP or 4dF from the chat, but there is a configuration setting for which roll to use by default.
I built in what Dice So Nice needs to roll a D10 as a dP.

## Plan

Expand Down
16 changes: 10 additions & 6 deletions build/compilepacks.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import { promises as fs } from "fs";
const MODULE_ID = process.cwd();

const packs = await fs.readdir("./packs/src");
for (const pack of packs) {
if (pack === ".gitattributes") continue;
console.log("Packing " + pack);
await compilePack(
const packlist = packs.map((pack)=> {
// eslint-disable-next-line no-console
console.log("Starting packing: ", pack);
return compilePack(
`${MODULE_ID}/packs/src/${pack}`,
`${MODULE_ID}/packs/${pack}`
);
}
).then(() => {
// eslint-disable-next-line no-console
console.log("Completed packing: ", pack);
});
});
await Promise.all(packlist);
30 changes: 16 additions & 14 deletions module/pyramid-fudge-die.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/**
* A type of DiceTerm used to represent a five-sided Fate/Fudge die.
* Mathematically behaves like 1d5-3
* @extends {DiceTerm}
*/
/**
* A type of DiceTerm used to represent a five-sided Fate/Fudge die.
* Mathematically behaves like 1d5-3
* @extends {foundry.dice.terms.DiceTerm}
*/
const MIN_VALUE = -2;
const MAX_VALUE = 2;
const ZERO_OFFSET = 3;
export default class PyramidFudgeDie extends DiceTerm {
const VersionNeutralDiceTerm = foundry.dice.terms.DiceTerm ? foundry.dice.terms.DiceTerm : DiceTerm;
const VersionNeutralDie = foundry.dice.terms.Die ? foundry.dice.terms.Die : Die;
export default class PyramidFudgeDie extends VersionNeutralDiceTerm {
constructor(termData) {
super(termData);
this.faces = 5;
Expand All @@ -17,14 +19,14 @@ export default class PyramidFudgeDie extends DiceTerm {

/** @inheritdoc */
static MODIFIERS = {
"r": Die.prototype.reroll,
"rr": Die.prototype.rerollRecursive,
"k": Die.prototype.keep,
"kh": Die.prototype.keep,
"kl": Die.prototype.keep,
"d": Die.prototype.drop,
"dh": Die.prototype.drop,
"dl": Die.prototype.drop
"r": VersionNeutralDie.prototype.reroll,
"rr": VersionNeutralDie.prototype.rerollRecursive,
"k": VersionNeutralDie.prototype.keep,
"kh": VersionNeutralDie.prototype.keep,
"kl": VersionNeutralDie.prototype.keep,
"d": VersionNeutralDie.prototype.drop,
"dh": VersionNeutralDie.prototype.drop,
"dl": VersionNeutralDie.prototype.drop
};

/* -------------------------------------------- */
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fvtt-fudge",
"version": "2.2.0",
"version": "2.3.0",
"main": "modules/fudge.mjs",
"license": "MIT",
"scripts": {
Expand Down
10 changes: 9 additions & 1 deletion styles/fudge.css
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,15 @@ main {
font-size: 24px;
height: 30px;
}

.itemsheet .divided {
display: flex;
gap: 5px;
flex-direction: row;
}
.itemsheet img {
width: 100px;
height: 100px;
}
.itemsheet .properties {
display: grid;
gap: 5px;
Expand Down
9 changes: 5 additions & 4 deletions system.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"id": "fudge-rpg",
"title": "Fudge RPG",
"description": "Games based on the Freeform Universal Do-It-Yourself Gaming Engine (FUDGE).",
"version": "2.2.0",
"version": "2.3.0",
"authors": [
{
"name": "lupestro",
Expand All @@ -13,7 +13,7 @@
],
"compatibility": {
"minimum": "11",
"verified": "12.321",
"verified": "12.325",
"maximum": "12"
},
"esmodules": [
Expand Down Expand Up @@ -41,7 +41,8 @@
"ff-skills",
"ff-gifts",
"ff-faults"
]
],
"folders": []
}
]
}
Expand Down Expand Up @@ -130,5 +131,5 @@
],
"url": "https://github.com/lupestro/fvtt-fudge/",
"manifest": "https://raw.githubusercontent.com/lupestro/fvtt-fudge/main/system.json",
"download": "https://github.com/lupestro/fvtt-fudge/releases/download/release-2.2.0/fudge-rpg-release-2.2.0.zip"
"download": "https://github.com/lupestro/fvtt-fudge/releases/download/release-2.3.0/fudge-rpg-release-2.3.0.zip"
}
67 changes: 35 additions & 32 deletions templates/item.hbs
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
<form class="itemsheet">
<label class="hidden" for="itemname">{{localize "FUDGERPG.ItemName"}} </label>
<input class="fullwidth" name="itemname" id="itemname" type="text" value="{{item.name}}" placeholder={{localize "FUDGERPG.ItemName"}} />
<section class="properties">
{{#if (eq item.type "attributeset")}}
<label for="attributes">{{localize "FUDGERPG.AttributesEntry"}}</label>
<textarea id="attributes" class="attributeset">{{attributelist}}</textarea>
<label for="muscleweapon">{{localize "FUDGERPG.MuscleWeaponAttr"}}</label>
<select id="muscleweapon" class="attrchoose">
{{#each item.system.attributes as |attr|}}
<option value="{{attr.name}}" {{#if (eq attr.name ../item.system.muscleweapon)}} selected {{/if}}>{{attr.name}}</option>
{{/each}}
</select>
<label for="damagecapacity">{{localize "FUDGERPG.DamageCapacityAttr"}}</label>
<select id="damagecapacity" class="attrchoose">
{{#each item.system.attributes as |attr|}}
<option value="{{attr.name}}"{{#if (eq attr.name ../item.system.damagecap)}} selected {{/if}}>{{attr.name}}</option>
{{/each}}
</select>
{{/if}}
{{#if (eq item.type "equipment")}}
<label for="odf">{{localize "FUDGERPG.ODF"}} </label>
<input class="numeric" name="odf" id="odf" type="text" value="{{item.system.odf}}"/>
<label for="ddf">{{localize "FUDGERPG.DDF"}} </label>
<input class="numeric" name="ddf" id="ddf" type="text" value="{{item.system.ddf}}"/>
<label for="quantity">{{localize "FUDGERPG.Quantity"}}</label>
<input class="numeric" name="quantity" id="quantity" type="text" value="{{item.system.quantity}}" />
{{/if}}
{{#if (eq item.type "skill")}}
<label for="skillgroup">{{localize "FUDGERPG.SkillGroups"}}</label>
<input name="skillgroup" id="skillgroup" type="text" value="{{item.system.group}}" />
<label xfor="skillgroup2" class="hiddenlabel">{{localize "FUDGERPG.SkillGroup"}}</label>
<input name="skillgroup2" id="skillgroup2" type="text" value="{{item.system.group2}}" />
{{/if}}
</section>
<div class="divided">
<img class="picture" data-edit="img" title="{{item.name}}" src="{{item.img}}" />
<section class="properties">
{{#if (eq item.type "attributeset")}}
<label for="attributes">{{localize "FUDGERPG.AttributesEntry"}}</label>
<textarea id="attributes" class="attributeset">{{attributelist}}</textarea>
<label for="muscleweapon">{{localize "FUDGERPG.MuscleWeaponAttr"}}</label>
<select id="muscleweapon" class="attrchoose">
{{#each item.system.attributes as |attr|}}
<option value="{{attr.name}}" {{#if (eq attr.name ../item.system.muscleweapon)}} selected {{/if}}>{{attr.name}}</option>
{{/each}}
</select>
<label for="damagecapacity">{{localize "FUDGERPG.DamageCapacityAttr"}}</label>
<select id="damagecapacity" class="attrchoose">
{{#each item.system.attributes as |attr|}}
<option value="{{attr.name}}"{{#if (eq attr.name ../item.system.damagecap)}} selected {{/if}}>{{attr.name}}</option>
{{/each}}
</select>
{{/if}}
{{#if (eq item.type "equipment")}}
<label for="odf">{{localize "FUDGERPG.ODF"}} </label>
<input class="numeric" name="odf" id="odf" type="text" value="{{item.system.odf}}"/>
<label for="ddf">{{localize "FUDGERPG.DDF"}} </label>
<input class="numeric" name="ddf" id="ddf" type="text" value="{{item.system.ddf}}"/>
<label for="quantity">{{localize "FUDGERPG.Quantity"}}</label>
<input class="numeric" name="quantity" id="quantity" type="text" value="{{item.system.quantity}}" />
{{/if}}
{{#if (eq item.type "skill")}}
<label for="skillgroup">{{localize "FUDGERPG.SkillGroups"}}</label>
<input name="skillgroup" id="skillgroup" type="text" value="{{item.system.group}}" />
<label xfor="skillgroup2" class="hiddenlabel">{{localize "FUDGERPG.SkillGroup"}}</label>
<input name="skillgroup2" id="skillgroup2" type="text" value="{{item.system.group2}}" />
{{/if}}
</section>
</div>
<section class="item-description">
<h2>{{localize 'FUDGERPG.Description'}}</h2>
{{editor descriptionHTML class="desc" target="system.description" button=true
Expand Down

0 comments on commit b6c3365

Please sign in to comment.