Add setting to limit how many Aspects a character can have equipped

This commit is contained in:
Oliver-Akins 2024-01-14 01:00:51 -07:00
parent 2fff1b84b5
commit 3e40d0f8c5
6 changed files with 77 additions and 11 deletions

View file

@ -71,6 +71,10 @@
"openEmbeddedOnCreate": {
"name": "Edit Custom Items Immediately When Created",
"description": "Tells the character sheets that have \"Add\" buttons to open the Item's sheet when you create the new item so that you can immediately edit it without needing to click more buttons."
},
"aspectLimit": {
"name": "Character Aspect Limit",
"description": "Limit how many Aspects a single character can have. The Server is able to bypass this restriction."
}
},
"sheet-names": {
@ -229,6 +233,9 @@
"item-not-found": "Failed to find an item to delete",
"spell-create-failed": "Failed to create a custom spell",
"aspect-limit-reached": "You cannot have more than {limit} aspects"
},
"warn": {
"negative-aspect-limit": "The Aspect limit must be 0 or greater"
}
},
"dialogs": {

View file

@ -1,7 +1,7 @@
import PlayerActor from "./Player.mjs";
export class ActorHandler extends Actor {
actorTypes = {
proxyTargets = {
player: PlayerActor,
};
@ -11,7 +11,12 @@ export class ActorHandler extends Actor {
/** @type {class|undefined} */
get fn() {
return this.actorTypes[this.type];
return this.proxyTargets[this.type];
};
async proxyFunction(funcName, ...args) {
if (!this.fn?.[funcName]) return;
return await this.fn?.[funcName].bind(this)(...args);
};
async openEmbeddedSheet($event) {

View file

@ -70,17 +70,23 @@ async function createCustomSpell() {
}]);
};
/** @this {Actor} */
async function atAspectLimit() {
let limit = game.settings.get(`dotdungeon`, `aspectLimit`);
console.log(this.itemTypes.aspect.length, `>=`, limit, `-->`, this.itemTypes.aspect.length >= limit)
return this.itemTypes.aspect.length >= limit;
};
/**
* @param {ItemHandler} item
* @this {Actor}
*/
async function preAspectEmbed(item) {
let limit = 1
if (this.itemTypes.aspect.length >= limit) {
if (await atAspectLimit.bind(this)()) {
ui.notifications.error(
game.i18n.format(
`dotdungeon.notification.error.aspect-limit-reached`,
{ limit }
{ limit: game.settings.get(`dotdungeon`, `aspectLimit`) }
),
{ console: false }
);
@ -89,10 +95,11 @@ async function preAspectEmbed(item) {
};
export default {
genericEmbeddedDelete,
genericEmbeddedUpdate,
atAspectLimit,
createCustomItem,
createCustomAspect,
createCustomSpell,
genericEmbeddedDelete,
genericEmbeddedUpdate,
preAspectEmbed,
};

View file

@ -6,7 +6,7 @@ import AspectItem from "./Aspect.mjs";
export class ItemHandler extends Item {
/** @override */
itemTypes = {
proxyTargets = {
aspect: AspectItem,
};
@ -16,7 +16,12 @@ export class ItemHandler extends Item {
/** @type {class|undefined} */
get fn() {
return this.itemTypes[this.type];
return this.proxyTargets[this.type];
};
async proxyFunction(funcName, ...args) {
if (!this.fn?.[funcName]) return;
return await this.fn?.[funcName].bind(this)(...args);
};
async _preCreate(...args) {

View file

@ -22,4 +22,41 @@ export default function() {
default: "supplies",
requiresReload: false,
});
/*
These two settings are used in coordination in order to prevent the Aspect
Limit from being set to a value that just absolutely stupid (i.e. negative
values, non-integer values). The preSaveAspectLimit is the one that's actually
displayed in the settings dialogue, while the aspectLimit is the one that's
used by all of the code.
*/
game.settings.register(`dotdungeon`, `aspectLimit`, {
scope: `world`,
default: 1,
type: Number,
});
game.settings.register(`dotdungeon`, `preSaveAspectLimit`, {
name: `dotdungeon.settings.aspectLimit.name`,
hint: `dotdungeon.settings.aspectLimit.description`,
scope: `world`,
config: true,
default: 1,
type: Number,
onChange(value) {
const current = game.settings.get(`dotdungeon`, `aspectLimit`);
if (current == value) return;
if (value < 0) {
ui.notifications.warn(
`dotdungeon.notification.warn.negative-aspect-limit`,
{ localize: true, console: false }
);
game.settings.set(`dotdungeon`, `preSaveAspectLimit`, current);
return;
};
let floored = Math.floor(value);
game.settings.set(`dotdungeon`, `aspectLimit`, floored);
game.settings.set(`dotdungeon`, `preSaveAspectLimit`, floored);
},
});
};

View file

@ -1,6 +1,10 @@
import { ActorHandler } from "../documents/Actor/Handler.mjs";
import { GenericActorSheet } from "./GenericActorSheet.mjs";
export class PlayerSheet extends GenericActorSheet {
/** @override {ActorHandler} actor */
static get defaultOptions() {
let opts = mergeObject(
super.defaultOptions,
@ -22,7 +26,8 @@ export class PlayerSheet extends GenericActorSheet {
async getData() {
const ctx = await super.getData();
const actor = this.actor.toObject(false);
/** @type {ActorHandler} */
const actor = this.actor;
ctx.system = actor.system;
ctx.flags = actor.flags;
@ -30,7 +35,7 @@ export class PlayerSheet extends GenericActorSheet {
ctx.computed = {
canChangeGroup: ctx.settings.playersCanChangeGroup || ctx.isGM,
canAddAspect: ctx.items.aspect.length == 0,
canAddAspect: !await actor.proxyFunction.bind(actor)(`atAspectLimit`),
};
console.log(actor.uuid, `context:`, ctx)