Fixed so that creating regions without behaviors work for players. Fixed so that creating regions with behaviors works via GmEmit for players

This commit is contained in:
WBHarry 2026-05-02 13:54:13 +02:00
parent 4558fbdcf6
commit 02a489e8f7
4 changed files with 89 additions and 47 deletions

View file

@ -3220,7 +3220,8 @@
"domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used",
"knowTheTide": "Know The Tide gained a token",
"lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}",
"noTokenTargeted": "No token is targeted"
"noTokenTargeted": "No token is targeted",
"behaviorRegionRequiresGM": "Creating a Region with an attached Behavior requires an online GM"
},
"Progress": {
"migrationLabel": "Performing system migration. Please wait and do not close Foundry."

View file

@ -57,14 +57,14 @@ export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer {
}
async placeRegion(data, options = {}) {
const preConfirm = ({ _event, document, _create, _options }) => {
const shape = document.shapes[0];
const preConfirm = data => {
const shape = data.document.shapes[0];
const isEmanation = shape.type === 'emanation';
if (isEmanation) {
const token = this.#findTokenInBounds(shape.base.origin);
if (!token) return options.preConfirm?.() ?? true;
if (!token) return options.preConfirm?.(data) ?? true;
const shapeData = shape.toObject();
document.updateSource({
data.document.updateSource({
shapes: [
{
...shapeData,
@ -80,10 +80,10 @@ export default class DhRegionLayer extends foundry.canvas.layers.RegionLayer {
});
}
return options?.preConfirm?.() ?? true;
return options?.preConfirm?.(data) ?? true;
};
super.placeRegion(data, { ...options, preConfirm });
return await super.placeRegion(data, { ...options, preConfirm });
}
/** Searches for token at origin point, returning null if there are no tokens or multiple overlapping tokens */

View file

@ -1,4 +1,4 @@
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
import { emitAsGM, emitGMCreate, GMUpdateEvent } from '../systemRegistration/socket.mjs';
export default class DhpChatMessage extends foundry.documents.ChatMessage {
targetHook = null;
@ -259,29 +259,52 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
const { shape: type, size: range } = selectedArea;
const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({ type, range });
await canvas.regions.placeRegion(
{
const scene = game.scenes.get(game.user.viewedScene);
const level = scene.levels.find(x => x.isView);
const regionData = {
name: selectedArea.name,
levels: level ? [level.id] : [],
shapes: [shapeData],
restriction: { enabled: false, type: 'move', priority: 0 },
behaviors: [
behaviors: effects.length > 0 ? [
{
name: game.i18n.localize('TYPES.RegionBehavior.applyActiveEffect'),
type: 'applyActiveEffect',
system: {
effects: effects
}
}
],
}] : [],
displayMeasurements: true,
locked: false,
ownership: { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE },
visibility: CONST.REGION_VISIBILITY.ALWAYS
},
};
const placeRegion = (data) => {
canvas.regions.placeRegion(
data,
{ create: true }
);
};
const needsGMExecution = effects.length > 0;
if (needsGMExecution && !game.user.isGM) {
if (!game.users.activeGM)
return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.behaviorRegionRequiresGM'));
const region = await canvas.regions.placeRegion(regionData, { create: false });
emitGMCreate(
'Region',
placeRegion,
region,
scene.id,
);
} else {
placeRegion(regionData);
}
};
if (this.system.action.areas.length === 1) createArea(this.system.action.areas[0]);
else if (this.system.action.areas.length > 1) {
new foundry.applications.ux.ContextMenu.implementation(

View file

@ -6,6 +6,9 @@ export function handleSocketEvent({ action = null, data = {} } = {}) {
case socketEvent.GMUpdate:
Hooks.callAll(socketEvent.GMUpdate, data);
break;
case socketEvent.GMCreate:
Hooks.callAll(socketEvent.GMCreate, data);
break;
case socketEvent.DhpFearUpdate:
Hooks.callAll(socketEvent.DhpFearUpdate);
break;
@ -25,6 +28,7 @@ export function handleSocketEvent({ action = null, data = {} } = {}) {
export const socketEvent = {
GMUpdate: 'DhGMUpdate',
GMCreate: 'DhGMCreate',
Refresh: 'DhRefresh',
DhpFearUpdate: 'DhFearUpdate',
DowntimeTrigger: 'DowntimeTrigger',
@ -38,7 +42,7 @@ export const GMUpdateEvent = {
UpdateSetting: 'DhGMUpdateSetting',
UpdateFear: 'DhGMUpdateFear',
UpdateCountdowns: 'DhGMUpdateCountdowns',
UpdateSaveMessage: 'DhGMUpdateSaveMessage'
UpdateSaveMessage: 'DhGMUpdateSaveMessage',
};
export const RefreshType = {
@ -56,14 +60,14 @@ export const registerSocketHooks = () => {
const document = data.uuid ? await fromUuid(data.uuid) : null;
switch (data.action) {
case GMUpdateEvent.UpdateDocument:
if (document && data.update) await document.update(data.update);
if (document && data.data) await document.update(data.data);
break;
case GMUpdateEvent.UpdateEffect:
if (document && data.update)
await game.system.api.fields.ActionFields.EffectsField.applyEffects.call(document, data.update);
if (document && data.data)
await game.system.api.fields.ActionFields.EffectsField.applyEffects.call(document, data.data);
break;
case GMUpdateEvent.UpdateSetting:
await game.settings.set(CONFIG.DH.id, data.uuid, data.update);
await game.settings.set(CONFIG.DH.id, data.uuid, data.data);
break;
case GMUpdateEvent.UpdateFear:
await game.settings.set(
@ -73,22 +77,22 @@ export const registerSocketHooks = () => {
0,
Math.min(
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear,
data.update
data.data
)
)
);
break;
case GMUpdateEvent.UpdateCountdowns:
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data.update);
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data.data);
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown });
break;
case GMUpdateEvent.UpdateSaveMessage:
const message = game.messages.get(data.update.message);
const message = game.messages.get(data.data.message);
if (!message) return;
game.system.api.fields.ActionFields.SaveField.updateSaveMessage(
data.update.result,
data.data.result,
message,
data.update.token
data.data.token
);
break;
}
@ -102,6 +106,17 @@ export const registerSocketHooks = () => {
}
}
});
Hooks.on(socketEvent.GMCreate, async ({ data, documentType, scene }) => {
if (!game.user.isGM) return;
switch (documentType) {
default:
const cls = getDocumentClass(documentType);
cls.create(data, { parent: game.scenes.get(scene) });
break;
}
});
};
export const registerUserQueries = () => {
@ -109,18 +124,21 @@ export const registerUserQueries = () => {
CONFIG.queries.reactionRoll = game.system.api.fields.ActionFields.SaveField.rollSaveQuery;
};
export const emitAsGM = async (eventName, callback, update, uuid = null, refresh = null) => {
export const emitGMUpdate = async (eventName, callback, update, uuid = null, refresh = null) => {
return await emitAsGM(socketEvent.GMUpdate, { eventName, callback, data: update, uuid, refresh });
};
export const emitGMCreate = async (documentType, callback, data, scene) => {
return await emitAsGM(socketEvent.GMCreate, { documentType, callback, data, scene });
};
export const emitAsGM = async (event, data = { callback: () => {}, data: {} }) => {
if (!game.user.isGM) {
return await game.socket.emit(`system.${CONFIG.DH.id}`, {
action: socketEvent.GMUpdate,
data: {
action: eventName,
uuid,
update,
refresh
}
action: event,
data: data
});
} else return callback(update);
} else return data.callback(data.data);
};
export const emitAsOwner = (eventName, userId, args) => {