Skip to content

Commit 6a0827c

Browse files
authored
Merge pull request #63 from orffen/50-add-additional-sheets
50 add additional sheets
2 parents 52e71e8 + c44795c commit 6a0827c

27 files changed

+1233
-210
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@ The system does not yet automatically reset initiative at the end of each combat
1818

1919
### Roll Formulas
2020

21-
To add ability bonuses/penalties to roll formulas (damage or special ability), you can use `@str.bonus`. Replace `str` with `int`, `wis`, `dex`, `con` or `cha`. You can use the full ability with just `@str`, for example if you wanted to do something like `d20<=@str`. `@lvl` is available as short-hand for the character level.
21+
To add ability bonuses/penalties to roll formulas (damage or special ability), you can use `@str.bonus`. Replace `str` with `int`, `wis`, `dex`, `con` or `cha`. You can use the full ability value with `@str.value`, for example if you wanted to do something like `d20<=@str.value`. `@lvl` is available as short-hand for the character level.
2222

2323
### Character Special Abilities
2424

25-
Special Abilities are a flexible item type with just a description and a roll formula. They can be used for thief abilities (formula: d100), open doors checks (d6), or even just as text (formula left blank). Clicking the icon in the list will either roll the formula if present, or output the description to the chat window.
25+
Special Abilities are a flexible item type with just a description and a roll formula and an optional target number. They can be used for thief abilities (formula: d100), open doors checks (d6), or even just as text (formula left blank). Clicking the icon in the list will either roll the formula if present, or output the description to the chat window.
2626

2727
### Monster Special Abilities
2828

2929
The monster sheet has a "special abilities" field. This field should be 0, 1, or 2, depending on how many asterisks appear after the monster's hit dice value. XP values and attack bonus for monsters are automatically calculated.
3030

3131
## License
3232

33-
All software components are licensed under the MIT license - see *LICENSE.txt* for details.
33+
All software components are licensed under the MIT license - see [LICENSE.txt](https://raw.githubusercontent.com/orffen/basicfantasyrpg/main/LICENSE.txt) for details.
3434

3535
Basic Fantasy Role-Playing Game content is distributed under the terms of the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/).
3636

lang/en.json

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
11
{
22
"ACTOR.TypeCharacter": "Character",
33
"ACTOR.TypeMonster": "Monster",
4+
"ACTOR.TypeSiegeEngine": "Siege Engine",
5+
"ACTOR.TypeStronghold": "Stronghold",
6+
"ACTOR.TypeVehicle": "Vehicle",
7+
8+
"ERROR.InvalidTargetNumber": "Couldn't parse target number for",
49

510
"ITEM.TypeItem": "Equipment",
611
"ITEM.TypeWeapon": "Weapon",
712
"ITEM.TypeArmor": "Armor",
813
"ITEM.TypeSpell": "Spell",
914
"ITEM.TypeFeature": "Special Ability",
15+
"ITEM.TypeFloor": "Floor",
16+
"ITEM.TypeWall": "Wall",
1017

1118
"TYPES.Actor.character": "Character",
1219
"TYPES.Actor.monster": "Monster",
20+
"TYPES.Actor.siegeEngine": "Siege Engine",
21+
"TYPES.Actor.stronghold": "Stronghold",
22+
"TYPES.Actor.vehicle": "Vehicle",
1323

1424
"TYPES.Item.item": "Equipment",
1525
"TYPES.Item.weapon": "Weapon",
1626
"TYPES.Item.armor": "Armor",
1727
"TYPES.Item.spell": "Spell",
1828
"TYPES.Item.feature": "Special Ability",
29+
"TYPES.Item.floor": "Floor",
30+
"TYPES.Item.wall": "Wall",
1931

20-
"BASICFANTASYRPG.CombatTab": "Combat",
21-
"BASICFANTASYRPG.DescriptionTab": "Description",
22-
"BASICFANTASYRPG.ItemsTab": "Equipment",
23-
"BASICFANTASYRPG.SpellsTab": "Spells",
24-
"BASICFANTASYRPG.FeaturesTab": "Special Abilities",
32+
"BASICFANTASYRPG.TabCargo": "Cargo",
33+
"BASICFANTASYRPG.TabCombat": "Combat",
34+
"BASICFANTASYRPG.TabDescription": "Description",
35+
"BASICFANTASYRPG.TabFeatures": "Special Abilities",
36+
"BASICFANTASYRPG.TabFloors": "Floors & Walls",
37+
"BASICFANTASYRPG.TabItems": "Equipment",
38+
"BASICFANTASYRPG.TabSpells": "Spells",
2539

2640
"BASICFANTASYRPG.AbilityStr": "Strength",
2741
"BASICFANTASYRPG.AbilityCon": "Constitution",
@@ -48,54 +62,98 @@
4862
"BASICFANTASYRPG.Silver": "Silver",
4963
"BASICFANTASYRPG.Copper": "Copper",
5064

65+
"BASICFANTASYRPG.AbilityCheck": "Ability Check",
5166
"BASICFANTASYRPG.Add": "Add",
5267
"BASICFANTASYRPG.Age": "Age",
68+
"BASICFANTASYRPG.Area": "Area",
5369
"BASICFANTASYRPG.ArmorClass": "Armor Class",
5470
"BASICFANTASYRPG.ArmorClassAbbr": "AC",
5571
"BASICFANTASYRPG.Attack": "Attack",
5672
"BASICFANTASYRPG.AttackBonus": "Attack Bonus",
5773
"BASICFANTASYRPG.AttackBonusAbbr": "AB",
74+
"BASICFANTASYRPG.AttackPenalty": "Attack Penalty",
5875
"BASICFANTASYRPG.BonusAttackBonus": "Bonus AB",
76+
"BASICFANTASYRPG.BuildTime": "Time to Build",
77+
"BASICFANTASYRPG.Cargo": "Cargo",
5978
"BASICFANTASYRPG.CarriedWeight": "Load",
6079
"BASICFANTASYRPG.Class": "Class",
80+
"BASICFANTASYRPG.Cost": "Cost",
81+
"BASICFANTASYRPG.CostMultiplier": "Cost Multiplier",
6182
"BASICFANTASYRPG.Damage": "Damage",
6283
"BASICFANTASYRPG.DamageAbbr": "Dmg",
84+
"BASICFANTASYRPG.Days": "Days",
6385
"BASICFANTASYRPG.Duration": "Duration",
6486
"BASICFANTASYRPG.ExperiencePoints": "Experience Points",
6587
"BASICFANTASYRPG.ExperiencePointsAbbr": "XP",
6688
"BASICFANTASYRPG.Failure": "Failure",
89+
"BASICFANTASYRPG.FeetAbbr": "ft.",
90+
"BASICFANTASYRPG.Floors": "Floors",
91+
"BASICFANTASYRPG.Followers": "Followers",
6792
"BASICFANTASYRPG.Formula": "Roll Formula",
93+
"BASICFANTASYRPG.Hardness": "Hardness",
94+
"BASICFANTASYRPG.Height": "Height",
6895
"BASICFANTASYRPG.HitDice": "Hit Dice",
6996
"BASICFANTASYRPG.HitDiceAbbr": "HD",
7097
"BASICFANTASYRPG.HitPoints": "Hit Points",
7198
"BASICFANTASYRPG.HitPointsAbbr": "HP",
99+
"BASICFANTASYRPG.Immobilized": "Immobilized",
72100
"BASICFANTASYRPG.InitiativeBonus": "Init. Bonus",
101+
"BASICFANTASYRPG.Length": "Length",
73102
"BASICFANTASYRPG.Level": "Level",
103+
"BASICFANTASYRPG.Maneuverability": "Maneuverability",
104+
"BASICFANTASYRPG.Material": "Material",
105+
"BASICFANTASYRPG.MaterialBrick": "Brick",
106+
"BASICFANTASYRPG.MaterialStoneHard": "Hard Stone",
107+
"BASICFANTASYRPG.MaterialStoneSoft": "Soft Stone",
108+
"BASICFANTASYRPG.MaterialWood": "Wood",
74109
"BASICFANTASYRPG.Melee": "Melee",
75110
"BASICFANTASYRPG.Morale": "Morale",
76-
"BASICFANTASYRPG.Movement": "Move",
111+
"BASICFANTASYRPG.Movement": "Movement",
77112
"BASICFANTASYRPG.Name": "Name",
78113
"BASICFANTASYRPG.NextLevel": "Next Level",
79114
"BASICFANTASYRPG.NumberAppearing": "No. Appearing",
115+
"BASICFANTASYRPG.PoundsAbbr": "lbs.",
80116
"BASICFANTASYRPG.Prepared": "Prepared",
81117
"BASICFANTASYRPG.Price": "Price",
82118
"BASICFANTASYRPG.Quantity": "Quantity",
83119
"BASICFANTASYRPG.Race": "Race",
84120
"BASICFANTASYRPG.Range": "Range",
121+
"BASICFANTASYRPG.RangeBonus": "Range Bonus",
85122
"BASICFANTASYRPG.Ranged": "Ranged",
123+
"BASICFANTASYRPG.RangeLong": "Long (-2)",
124+
"BASICFANTASYRPG.RangeMedium": "Medium (+0)",
125+
"BASICFANTASYRPG.RangeShort": "Short (+1)",
126+
"BASICFANTASYRPG.RateOfFire": "Rate of Fire",
86127
"BASICFANTASYRPG.Roll": "Roll",
128+
"BASICFANTASYRPG.RollUnder": "Roll Under",
129+
"BASICFANTASYRPG.RoofSlate": "Slate-shingled Roof",
130+
"BASICFANTASYRPG.RoofThatched": "Thatched Roof",
131+
"BASICFANTASYRPG.RoofWood": "Wood-shingled Roof",
132+
"BASICFANTASYRPG.SavingThrow": "Saving Throw",
87133
"BASICFANTASYRPG.Sex": "Gender",
134+
"BASICFANTASYRPG.SideAft": "Aft",
135+
"BASICFANTASYRPG.SideForward": "Forward",
136+
"BASICFANTASYRPG.SidePort": "Port",
137+
"BASICFANTASYRPG.SideStarboard": "Starboard",
88138
"BASICFANTASYRPG.Size": "Size",
89-
"BASICFANTASYRPG.SpecialAbility": "Special Abilities",
90-
"BASICFANTASYRPG.Spells": "Spells",
139+
"BASICFANTASYRPG.SpecialAbility": "Special Ability",
140+
"BASICFANTASYRPG.SpecialAbilityXPBonus": "Special Ability XP Bonus",
91141
"BASICFANTASYRPG.SpellLevel": "Level",
142+
"BASICFANTASYRPG.Spells": "Spells",
92143
"BASICFANTASYRPG.SpellsPerLevel": "Spells",
93144
"BASICFANTASYRPG.Success": "Success",
145+
"BASICFANTASYRPG.Sunk": "Sunk",
146+
"BASICFANTASYRPG.TargetAC": "Target AC",
94147
"BASICFANTASYRPG.TargetNumber": "Target Number",
148+
"BASICFANTASYRPG.Thick": "Thick",
149+
"BASICFANTASYRPG.Thickness": "Thickness",
95150
"BASICFANTASYRPG.TreasureType": "Treasure Type",
96151
"BASICFANTASYRPG.Versus": "Versus",
97152
"BASICFANTASYRPG.VersusAbbr": "vs.",
98153
"BASICFANTASYRPG.Weight": "Weight",
154+
"BASICFANTASYRPG.Width": "Width",
155+
"BASICFANTASYRPG.WorkerDays": "Worker-days",
156+
"BASICFANTASYRPG.Workers": "Workers",
99157

100158
"BASICFANTASYRPG.EffectCreate": "Create Effect",
101159
"BASICFANTASYRPG.EffectToggle": "Toggle Effect",

module/basicfantasyrpg.mjs

Lines changed: 81 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// Import document classes.
2-
import { BasicFantasyRPGActor } from "./documents/actor.mjs";
3-
import { BasicFantasyRPGItem } from "./documents/item.mjs";
2+
import { BasicFantasyRPGActor } from './documents/actor.mjs';
3+
import { BasicFantasyRPGItem } from './documents/item.mjs';
44
// Import sheet classes.
5-
import { BasicFantasyRPGActorSheet } from "./sheets/actor-sheet.mjs";
6-
import { BasicFantasyRPGItemSheet } from "./sheets/item-sheet.mjs";
5+
import { BasicFantasyRPGActorSheet } from './sheets/actor-sheet.mjs';
6+
import { BasicFantasyRPGItemSheet } from './sheets/item-sheet.mjs';
77
// Import helper/utility classes and constants.
8-
import { preloadHandlebarsTemplates } from "./helpers/templates.mjs";
9-
import { BASICFANTASYRPG } from "./helpers/config.mjs";
8+
import { preloadHandlebarsTemplates } from './helpers/templates.mjs';
9+
import { BASICFANTASYRPG } from './helpers/config.mjs';
1010

1111
/* -------------------------------------------- */
1212
/* Init Hook */
@@ -30,7 +30,7 @@ Hooks.once('init', async function() {
3030
* @type {String}
3131
*/
3232
CONFIG.Combat.initiative = {
33-
formula: "max(1, 1d6 + @abilities.dex.bonus + @initBonus.value)",
33+
formula: 'max(1, 1d6 + @abilities.dex.bonus + @initBonus.value)',
3434
decimals: 0
3535
};
3636

@@ -39,80 +39,122 @@ Hooks.once('init', async function() {
3939
CONFIG.Item.documentClass = BasicFantasyRPGItem;
4040

4141
// Register sheet application classes
42-
Actors.unregisterSheet("core", ActorSheet);
43-
Actors.registerSheet("basicfantasyrpg", BasicFantasyRPGActorSheet, { makeDefault: true });
44-
Items.unregisterSheet("core", ItemSheet);
45-
Items.registerSheet("basicfantasyrpg", BasicFantasyRPGItemSheet, { makeDefault: true });
42+
Actors.unregisterSheet('core', ActorSheet);
43+
Actors.registerSheet('basicfantasyrpg', BasicFantasyRPGActorSheet, { makeDefault: true });
44+
Items.unregisterSheet('core', ItemSheet);
45+
Items.registerSheet('basicfantasyrpg', BasicFantasyRPGItemSheet, { makeDefault: true });
4646

4747
// Preload Handlebars templates.
4848
return preloadHandlebarsTemplates();
4949
});
5050

5151
/* -------------------------------------------- */
52-
/* Handlebars Helpers */
52+
/* Handlebars Helpers & Partials */
5353
/* -------------------------------------------- */
5454

55-
// If you need to add Handlebars helpers, here are a few useful examples:
56-
Handlebars.registerHelper('concat', function() {
57-
var outStr = '';
58-
for (var arg in arguments) {
59-
if (typeof arguments[arg] != 'object') {
60-
outStr += arguments[arg];
61-
}
55+
Handlebars.registerHelper('calculateAbilityTargetNumber', function(lvl) {
56+
return Math.floor(17 - (lvl / 2 - lvl % 2));
57+
});
58+
59+
Handlebars.registerHelper('localizeFloorMaterial', function(type) {
60+
switch (type) {
61+
case 'roofThatch': return game.i18n.localize('BASICFANTASYRPG.RoofThatched');
62+
case 'roofSlate' : return game.i18n.localize('BASICFANTASYRPG.RoofSlate');
63+
case 'roofWood' : return game.i18n.localize('BASICFANTASYRPG.RoofWood');
64+
default : return game.i18n.localize('ITEM.TypeFloor');
6265
}
63-
return outStr;
6466
});
6567

66-
Handlebars.registerHelper('toLowerCase', function(str) {
67-
return str.toLowerCase();
68+
Handlebars.registerHelper('localizeWallMaterial', function(type) {
69+
switch (type) {
70+
case 'stoneHard' : return game.i18n.localize('BASICFANTASYRPG.MaterialStoneHard');
71+
case 'stoneSoft' : return game.i18n.localize('BASICFANTASYRPG.MaterialStoneSoft');
72+
case 'brick' : return game.i18n.localize('BASICFANTASYRPG.MaterialBrick');
73+
case 'wood' : return game.i18n.localize('BASICFANTASYRPG.MaterialWood');
74+
default : return game.i18n.localize('ITEM.TypeFloor');
75+
}
76+
});
77+
78+
Handlebars.registerHelper('localizeItemNameForActor', function(type) {
79+
if (type === 'stronghold') {
80+
return game.i18n.localize('ITEM.TypeFloor');
81+
} else if (type === 'vehicle') {
82+
return game.i18n.localize('BASICFANTASYRPG.Cargo');
83+
} else {
84+
return game.i18n.localize('ITEM.TypeItem');
85+
}
6886
});
6987

7088
Handlebars.registerHelper('localizeLowerCase', function(str) {
7189
return game.i18n.localize(str).toLowerCase();
7290
});
7391

74-
Handlebars.registerHelper('calculateAbilityTargetNumber', function(lvl) {
75-
return Math.floor(17 - (lvl / 2 - lvl % 2));
76-
})
92+
Handlebars.registerHelper('toLowerCase', function(str) {
93+
return str.toLowerCase();
94+
});
95+
96+
Handlebars.registerHelper('selected', function(value) {
97+
return Boolean(value) ? "selected" : "";
98+
});
99+
100+
Handlebars.registerPartial('iconDamage', `<i class="fa-solid fa-heart-crack fa-2xl" title="{{localize 'BASICFANTASYRPG.Roll'}} {{localize 'BASICFANTASYRPG.Damage'}}"></i>`);
101+
//`<img src="systems/basicfantasyrpg/styles/damage.svg" title="{{localize 'BASICFANTASYRPG.Roll'}} {{localize 'BASICFANTASYRPG.Damage'}}" width="24" height="24"/>`
102+
103+
Handlebars.registerPartial('iconMelee', `<i class="fa-solid fa-hand-fist fa-2xl" title="{{localize 'BASICFANTASYRPG.Roll'}} {{localize 'BASICFANTASYRPG.Melee'}} {{localize 'BASICFANTASYRPG.Attack'}}"></i>`);
104+
//`<img src="systems/basicfantasyrpg/styles/melee.svg" title="{{localize 'BASICFANTASYRPG.Roll'}} {{localize 'BASICFANTASYRPG.Melee'}} {{localize 'BASICFANTASYRPG.Attack'}}" width="24" height="24"/>`
105+
106+
Handlebars.registerPartial('iconRanged', `<i class="fa-solid fa-crosshairs fa-2xl" title="{{localize 'BASICFANTASYRPG.Roll'}} {{localize 'BASICFANTASYRPG.Ranged'}} {{localize 'BASICFANTASYRPG.Attack'}}"></i>`);
107+
//`<img src="systems/basicfantasyrpg/styles/ranged.svg" title="{{localize 'BASICFANTASYRPG.Roll'}} {{localize 'BASICFANTASYRPG.Ranged'}} {{localize 'BASICFANTASYRPG.Attack'}}" width="24" height="24"/>`
77108

78109
/* -------------------------------------------- */
79-
/* Ready Hook */
110+
/* Ready Hook & Others */
80111
/* -------------------------------------------- */
81112

82-
Hooks.once("ready", async function() {
113+
Hooks.once('ready', async function() {
83114
// Wait to register hotbar drop hook on ready so that modules could register earlier if they want to
84-
Hooks.on("hotbarDrop", (bar, data, slot) => createItemMacro(data, slot));
115+
Hooks.on('hotbarDrop', (bar, data, slot) => createItemMacro(data, slot));
116+
});
117+
118+
// Hide certain types from being created through the UI
119+
Hooks.on("renderDialog", (dialog, html) => {
120+
let hiddenTypes = ["floor", "wall"];
121+
Array.from(html.find("#document-create option")).forEach(i => {if (hiddenTypes.includes(i.value)) i.remove()});
85122
});
86123

87124
/* -------------------------------------------- */
88125
/* Character Creation Hooks */
89126
/* -------------------------------------------- */
90127

91-
Hooks.on("createActor", async function(actor) {
92-
if (actor.type === "character") {
128+
Hooks.on('createActor', async function(actor) {
129+
if (actor.type === 'character') {
93130
actor.updateSource({
94131
prototypeToken: {
95132
actorLink: true,
96133
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
97134
}
98135
});
99-
}
100-
else if (actor.type === "monster") {
136+
} else if (actor.type === 'monster') {
101137
actor.updateSource({
102138
prototypeToken: {
103139
appendNumber: true,
104140
displayName: CONST.TOKEN_DISPLAY_MODES.OWNER
105141
}
106142
});
143+
} else if (actor.type === 'stronghold') {
144+
const floor = {
145+
name: `New ${game.i18n.localize('ITEM.TypeFloor')}`,
146+
type: 'floor'
147+
};
148+
if (!actor.items.size) await actor.createEmbeddedDocuments('Item', [floor]);
107149
}
108150
});
109151

110152
/* -------------------------------------------- */
111153
/* Token Creation Hooks */
112154
/* -------------------------------------------- */
113155

114-
Hooks.on("createToken", async function(token, options, id) {
115-
if (token.actor.type === "monster") {
156+
Hooks.on('createToken', async function(token, options, id) {
157+
if (token.actor.type === 'monster') {
116158
let newHitPoints = new Roll(`${token.actor.system.hitDice.number}${token.actor.system.hitDice.size}+${token.actor.system.hitDice.mod}`);
117159
await newHitPoints.evaluate({ async: true });
118160
token.actor.system.hitPoints.value = Math.max(1, newHitPoints.total);
@@ -132,20 +174,20 @@ Hooks.on("createToken", async function(token, options, id) {
132174
* @returns {Promise}
133175
*/
134176
async function createItemMacro(data, slot) {
135-
if (data.type !== "Item") return;
136-
if (!("data" in data)) return ui.notifications.warn("You can only create macro buttons for owned Items");
177+
if (data.type !== 'Item') return;
178+
if (!('data' in data)) return ui.notifications.warn('You can only create macro buttons for owned Items');
137179
const item = data.data;
138180

139181
// Create the macro command
140-
const command = `game.basicfantasyrpg.rollItemMacro("${item.name}");`;
182+
const command = `game.basicfantasyrpg.rollItemMacro('${item.name}');`;
141183
let macro = game.macros.find(m => (m.name === item.name) && (m.command === command));
142184
if (!macro) {
143185
macro = await Macro.create({
144186
name: item.name,
145-
type: "script",
187+
type: 'script',
146188
img: item.img,
147189
command: command,
148-
flags: { "basicfantasyrpg.itemMacro": true }
190+
flags: { 'basicfantasyrpg.itemMacro': true }
149191
});
150192
}
151193
game.user.assignHotbarMacro(macro, slot);

0 commit comments

Comments
 (0)