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", "domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used",
"knowTheTide": "Know The Tide gained a token", "knowTheTide": "Know The Tide gained a token",
"lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}", "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": { "Progress": {
"migrationLabel": "Performing system migration. Please wait and do not close Foundry." "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 = {}) { async placeRegion(data, options = {}) {
const preConfirm = ({ _event, document, _create, _options }) => { const preConfirm = data => {
const shape = document.shapes[0]; const shape = data.document.shapes[0];
const isEmanation = shape.type === 'emanation'; const isEmanation = shape.type === 'emanation';
if (isEmanation) { if (isEmanation) {
const token = this.#findTokenInBounds(shape.base.origin); const token = this.#findTokenInBounds(shape.base.origin);
if (!token) return options.preConfirm?.() ?? true; if (!token) return options.preConfirm?.(data) ?? true;
const shapeData = shape.toObject(); const shapeData = shape.toObject();
document.updateSource({ data.document.updateSource({
shapes: [ shapes: [
{ {
...shapeData, ...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 */ /** 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 { export default class DhpChatMessage extends foundry.documents.ChatMessage {
targetHook = null; targetHook = null;
@ -258,28 +258,51 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
const effects = selectedArea.effects.map(effect => this.system.action.item.effects.get(effect).uuid); const effects = selectedArea.effects.map(effect => this.system.action.item.effects.get(effect).uuid);
const { shape: type, size: range } = selectedArea; const { shape: type, size: range } = selectedArea;
const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({ type, range }); const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({ type, range });
const scene = game.scenes.get(game.user.viewedScene);
const level = scene.levels.find(x => x.isView);
await canvas.regions.placeRegion( const regionData = {
{ name: selectedArea.name,
name: selectedArea.name, levels: level ? [level.id] : [],
shapes: [shapeData], shapes: [shapeData],
restriction: { enabled: false, type: 'move', priority: 0 }, restriction: { enabled: false, type: 'move', priority: 0 },
behaviors: [ behaviors: effects.length > 0 ? [
{ {
name: game.i18n.localize('TYPES.RegionBehavior.applyActiveEffect'), name: game.i18n.localize('TYPES.RegionBehavior.applyActiveEffect'),
type: 'applyActiveEffect', type: 'applyActiveEffect',
system: { system: {
effects: effects effects: effects
}
} }
], }] : [],
displayMeasurements: true, displayMeasurements: true,
locked: false, locked: false,
ownership: { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE }, ownership: { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE },
visibility: CONST.REGION_VISIBILITY.ALWAYS visibility: CONST.REGION_VISIBILITY.ALWAYS
}, };
{ create: true } 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]); if (this.system.action.areas.length === 1) createArea(this.system.action.areas[0]);

View file

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