Skip to content

Commit

Permalink
Merge branch '4.1.x' into publish-wiki
Browse files Browse the repository at this point in the history
  • Loading branch information
arbron committed Nov 8, 2024
2 parents d174b59 + 9a6c0d8 commit a8b19d5
Show file tree
Hide file tree
Showing 81 changed files with 3,122 additions and 3,015 deletions.
6 changes: 6 additions & 0 deletions dnd5e.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ Hooks.once("init", function() {
game.dnd5e.moduleArt = new ModuleArt();
registerModuleData();

// Configure bastions
game.dnd5e.bastion = new documents.Bastion();

// Configure tooltips
game.dnd5e.tooltips = new Tooltips5e();

Expand Down Expand Up @@ -476,6 +479,9 @@ Hooks.once("ready", function() {
// Chat message listeners
documents.ChatMessage5e.activateListeners();

// Bastion initialization
game.dnd5e.bastion.initializeUI();

// Determine whether a system migration is required and feasible
if ( !game.user.isGM ) return;
const cv = game.settings.get("dnd5e", "systemMigrationVersion") || game.world.flags.dnd5e?.version;
Expand Down
1 change: 1 addition & 0 deletions icons/LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ The dnd5e system for Foundry Virtual Tabletop includes icon artwork licensed fro
/svg/damage/temphp.svg - "Eternal love" by Delapouite under CC BY 3.0
/svg/damage/thunder.svg - "Tornado discs" by Lorc under CC BY 3.0
/svg/facilities/build.svg - "Concrete bag" by Delapouite under CC BY 3.0
/svg/facilities/change.svg - "Cycle" by Lorc under CC BY 3.0
/svg/facilities/craft.svg - "Anvil" by Lorc under CC BY 3.0
/svg/facilities/empower.svg - "Pentacle" by Skoll under CC BY 3.0
/svg/facilities/enlarge.svg - "Upgrade" by Delapouite under CC BY 3.0
Expand Down
2 changes: 2 additions & 0 deletions icons/svg/facilities/change.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions icons/svg/facilities/repair.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 48 additions & 2 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -571,19 +571,53 @@
"DND5E.BackgroundName": "Background Name",

"DND5E.Bastion": {
"Action": {
"BastionTurn": "Advance Bastion Turn"
},
"Attack": {
"Automatic": "Resolve Automatically",
"Formula": "Attack Formula",
"NoActorWarning": "No character selected as target of the attack. Either select a token, or open their character sheet.",
"Resolve": "Resolve Attack",
"Result": {
"Damaged": "{link} damaged in the attack.",
"Deaths": {
"one": "<strong>{deaths}</strong> defender was slain in an attack on their bastion!",
"other": "<strong>{deaths}</strong> defenders were slain in an attack on their bastion!"
},
"NoDeaths": "Fended off an attack on their bastion with no casualties!",
"Undefended": "Undefended bastion attacked!"
},
"Title": "Bastion Attack"
},
"Button": {
"Label": "Show Bastion Turn Button",
"Hint": "Display a button for advancing bastion turns in the main game interface."
},
"Configuration": {
"Name": "Bastions",
"Label": "Configure Bastions",
"Hint": "Various configuration options for the Bastion system as presented in the DMG 2024."
},
"Confirm": "Advance one bastion turn?",
"Duration": {
"Label": "Bastion Turn Duration (days)"
},
"Enabled": {
"Label": "Enable Bastion Functionality",
"Hint": "When enabled, player characters will have access to a new Bastion tab on their character sheets when reaching level 5, and will be able to build facilities and issue orders to them."
},
"Label": "Bastion"
"Gold": {
"Claim": "Claim Income",
"Claimed": "claimed",
"Unclaimed": "unclaimed"
},
"Label": "Bastion",
"Summary": {
"Gold": "{value} ({claimed})",
"NoOrders": "No orders issued to special facilities this turn.",
"Order": "{link} completed the <strong>{order}</strong> order."
}
},

"DND5E.Biography": "Biography",
Expand Down Expand Up @@ -654,7 +688,7 @@
},
"SECTIONS": {
"Spell": "Spell",
"Spellbook": "Item Spells"
"Spellbook": "Additional Spells"
},
"Action": {
"RemoveSpell": "Remove Spell"
Expand Down Expand Up @@ -1627,6 +1661,10 @@
"label": "Defenders"
}
},
"disabled": {
"hint": "Disabled facilities cannot be issued orders and are repaired at the beginning of the next turn.",
"label": "Disabled"
},
"enlargeable": {
"hint": "Certain special facilities may have their size categories increased for a cost.",
"label": "Can Be Enlarged"
Expand Down Expand Up @@ -1704,6 +1742,10 @@
"inf": "Build",
"present": "Building"
},
"change": {
"inf": "Change",
"present": "Changing"
},
"craft": {
"inf": "Craft",
"present": "Crafting"
Expand All @@ -1728,6 +1770,10 @@
"inf": "Recruit",
"present": "Recruiting"
},
"repair": {
"inf": "Repair",
"present": "Repairing"
},
"research": {
"inf": "Research",
"present": "Researching"
Expand Down
40 changes: 40 additions & 0 deletions less/v2/activities.less
Original file line number Diff line number Diff line change
Expand Up @@ -267,5 +267,45 @@
.order-description {
padding: 8px;
text-align: center;

+ .order-description { margin-top: -.5em; }
}

.bastion-card {
display: flex;
flex-direction: column;
gap: 6px;
}

.order-summary {
display: flex;
flex-direction: column;
gap: 6px;
font-size: var(--font-size-12);
}

.item-summary {
display: flex;
gap: 4px;

li {
flex: 0 0 32px;
width: 32px;
height: 32px;
border: var(--dnd5e-border-gold);
border-radius: 3px;
overflow: hidden;
cursor: pointer;

> img {
pointer-events: none;
border: none;
width: 100%;
height: 100%;
max-width: unset;
object-fit: cover;
background: var(--dnd5e-color-iron-gray);
}
}
}
}
18 changes: 18 additions & 0 deletions less/v2/apps.less
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,24 @@
}
}

/* ---------------------------------- */
/* Bastions */
/* ---------------------------------- */

&#bastion-turn {
pointer-events: all;
margin: 0 5px 10px 15px;
color: var(--color-text-light-highlight);
width: var(--players-width);
background: rgb(0 0 0 / 80%);
border: 1px solid var(--color-border-dark);

&:hover {
border-color: var(--color-border-highlight-alt);
box-shadow: 0 0 10px var(--color-shadow-highlight);
}
}

/* ---------------------------------- */
/* Pause */
/* ---------------------------------- */
Expand Down
18 changes: 12 additions & 6 deletions module/applications/activity/order-usage-dialog.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export default class OrderUsageDialog extends ActivityUsageDialog {
* @protected
*/
async _prepareCraftContext(context, options) {
const { craft } = this.item.system;
context.craft = {
legend: game.i18n.localize(`DND5E.FACILITY.Orders.${this.activity.order}.present`),
item: {
Expand All @@ -89,10 +90,11 @@ export default class OrderUsageDialog extends ActivityUsageDialog {

if ( this.activity.order === "harvest" ) {
context.craft.isHarvesting = true;
context.craft.item.value = this.config.craft?.item ?? craft.item ?? "";
context.craft.quantity = {
field: new NumberField({ nullable: false, integer: true, positive: true }),
name: "craft.quantity",
value: this.config.craft?.quantity ?? 1
value: this.config.craft?.quantity ?? craft.quantity ?? 1
};
} else {
context.craft.baseItem = {
Expand All @@ -105,8 +107,8 @@ export default class OrderUsageDialog extends ActivityUsageDialog {
};
}

if ( this.config.craft?.item ) {
const item = await fromUuid(this.config.craft.item);
if ( context.craft.item.value ) {
const item = await fromUuid(context.craft.item.value);
context.craft.value = {
img: item.img,
name: item.name,
Expand Down Expand Up @@ -173,13 +175,17 @@ export default class OrderUsageDialog extends ActivityUsageDialog {
return;
}

let { duration } = game.settings.get("dnd5e", "bastionConfiguration");
if ( (this.activity.order === "craft") || (this.activity.order === "harvest") ) {
await this._prepareCraftContext(context, options);
}
else if ( this.activity.order === "trade" ) await this._prepareTradeContext(context, options);
else {
const config = CONFIG.DND5E.facilities.orders[this.activity.order];
if ( config?.duration ) duration = config.duration;
}

if ( this.activity.order === "trade" ) await this._prepareTradeContext(context, options);

this._prepareCostsContext(context, options);
this._prepareCostsContext(context, { ...options, days: duration });
}

/* -------------------------------------------- */
Expand Down
4 changes: 2 additions & 2 deletions module/applications/actor/character-sheet-2.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -787,13 +787,13 @@ export default class ActorSheet5eCharacter2 extends ActorSheetV2Mixin(ActorSheet
// TODO: Consider batching compendium lookups. Most occupants are likely to all be from the same compendium.
for ( const facility of Object.values(this.actor.itemTypes.facility) ) {
const { id, img, labels, name, system } = facility;
const { building, craft, defenders, free, hirelings, progress, size, trade, type } = system;
const { building, craft, defenders, disabled, free, hirelings, progress, size, trade, type } = system;
const subtitle = [
building.built ? CONFIG.DND5E.facilities.sizes[size].label : game.i18n.localize("DND5E.FACILITY.Build.Unbuilt")
];
if ( trade.stock.max ) subtitle.push(`${trade.stock.value ?? 0} &sol; ${trade.stock.max}`);
const context = {
id, labels, name, building, free, progress,
id, labels, name, building, disabled, free, progress,
craft: craft.item ? await fromUuid(craft.item) : null,
creatures: await this._prepareFacilityOccupants(trade.creatures),
defenders: await this._prepareFacilityOccupants(defenders),
Expand Down
3 changes: 3 additions & 0 deletions module/applications/bastion.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ export class BastionSetting extends foundry.abstract.DataModel {
/** @override */
static defineSchema() {
return {
button: new BooleanField({
required: true, label: "DND5E.Bastion.Button.Label", hint: "DND5E.Bastion.Button.Hint"
}),
duration: new NumberField({
required: true, positive: true, integer: true, initial: 7, label: "DND5E.Bastion.Duration.Label"
}),
Expand Down
9 changes: 8 additions & 1 deletion module/applications/item/item-sheet-2.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,18 @@ export default class ItemSheet5e2 extends ItemSheetV2Mixin(ItemSheet5e) {

// Facilities
if ( this.item.type === "facility" ) {
context.orders = Object.entries(CONFIG.DND5E.facilities.orders).reduce((obj, [value, { label, basic }]) => {
context.orders = Object.entries(CONFIG.DND5E.facilities.orders).reduce((obj, [value, config]) => {
const { label, basic, hidden } = config;
if ( hidden ) return obj;
// TODO: More hard-coding that we can potentially avoid.
if ( value === "build" ) {
if ( !building.built ) obj.executable.push({ value, label });
return obj;
}
if ( value === "change" ) {
if ( type.subtype === "garden" ) obj.executable.push({ value, label });
return obj;
}
if ( type.value === "basic" ) {
if ( !building.built ) return obj;
if ( basic ) obj.executable.push({ value, label });
Expand Down
5 changes: 3 additions & 2 deletions module/applications/item/sheet-v2-mixin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,9 @@ export default function ItemSheetV2Mixin(Base) {

if ( activeTab === "effects" ) {
return ActiveEffect.implementation.create({
name: game.i18n.localize("DND5E.EffectNew"),
img: "icons/svg/aura.svg"
name: this.document.name,
img: this.document.img,
origin: this.document.uuid
}, { parent: this.item, renderSheet: true });
}

Expand Down
18 changes: 15 additions & 3 deletions module/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -493,9 +493,11 @@ DND5E.ammoIds = {

/**
* @typedef FacilityOrder
* @property {string} label The human-readable name of the order.
* @property {string} icon The SVG icon for this order.
* @property {boolean} [basic] Whether this order can be issued to basic facilities.
* @property {string} label The human-readable name of the order.
* @property {string} icon The SVG icon for this order.
* @property {boolean} [basic] Whether this order can be issued to basic facilities.
* @property {number} [duration] The amount of time taken to complete the order if different to a normal bastion turn.
* @property {boolean} [hidden] This order is not normally available for execution.
*/

/**
Expand All @@ -520,6 +522,11 @@ DND5E.facilities = {
label: "DND5E.FACILITY.Orders.build.inf",
icon: "systems/dnd5e/icons/svg/facilities/build.svg"
},
change: {
label: "DND5E.FACILITY.Orders.change.inf",
icon: "systems/dnd5e/icons/svg/facilities/change.svg",
duration: 21
},
craft: {
label: "DND5E.FACILITY.Orders.craft.inf",
icon: "systems/dnd5e/icons/svg/facilities/craft.svg"
Expand All @@ -545,6 +552,11 @@ DND5E.facilities = {
label: "DND5E.FACILITY.Orders.recruit.inf",
icon: "systems/dnd5e/icons/svg/facilities/recruit.svg"
},
repair: {
label: "DND5E.FACILITY.Orders.repair.inf",
icon: "systems/dnd5e/icons/svg/facilities/repair.svg",
hidden: true
},
research: {
label: "DND5E.FACILITY.Orders.research.inf",
icon: "systems/dnd5e/icons/svg/facilities/research.svg"
Expand Down
7 changes: 7 additions & 0 deletions module/data/item/facility.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const { ArrayField, BooleanField, DocumentUUIDField, NumberField, SchemaField, S
* @property {string} craft.item The Item the facility is currently crafting.
* @property {number} craft.quantity The number of Items being crafted.
* @property {FacilityOccupants} defenders The facility's configured defenders.
* @property {boolean} disabled Whether the facility is currently disabled.
* @property {boolean} enlargeable Whether the facility is capable of being enlarged.
* @property {boolean} free Whether the facility counts towards the character's maximum special
* facility cap.
Expand Down Expand Up @@ -75,6 +76,7 @@ export default class FacilityData extends ItemDataModel.mixin(ActivitiesTemplate
value: new ArrayField(new DocumentUUIDField({ type: "Actor" })),
max: new NumberField({ required: true, integer: true, positive: true })
}),
disabled: new BooleanField({ required: true }),
enlargeable: new BooleanField({ required: true }),
free: new BooleanField({ required: true }),
hirelings: new SchemaField({
Expand Down Expand Up @@ -162,6 +164,9 @@ export default class FacilityData extends ItemDataModel.mixin(ActivitiesTemplate
if ( (this.type.value === "special") && this.order ) this._createOrderActivity("dnd5eFacOrder", this.order);
if ( this.enlargeable ) this._createOrderActivity("dnd5eFacEnlarge", "enlarge");
if ( !this.building.built ) this._createOrderActivity("dnd5eFacBuild", "build");

// TODO: Allow order activities to be user-creatable and configurable to avoid having to hard-code this.
if ( this.type.subtype === "garden" ) this._createOrderActivity("dnd5eFacChange", "change");
}

/* -------------------------------------------- */
Expand Down Expand Up @@ -191,6 +196,8 @@ export default class FacilityData extends ItemDataModel.mixin(ActivitiesTemplate
this.progress.pct = Math.round((this.progress.value / max) * 100);
}

if ( this.disabled ) this.progress.order = "repair";

// Labels
const labels = this.parent.labels ??= {};
if ( this.order ) labels.order = game.i18n.localize(`DND5E.FACILITY.Orders.${this.order}.present`);
Expand Down
Loading

0 comments on commit a8b19d5

Please sign in to comment.