Skip to content

Commit

Permalink
Merge pull request #624 from fantasycalendar/3.0.9
Browse files Browse the repository at this point in the history
3.0.9
  • Loading branch information
Haxxer authored Aug 16, 2024
2 parents 7ba62f9 + a88b45b commit 54ac74f
Show file tree
Hide file tree
Showing 16 changed files with 353 additions and 291 deletions.
10 changes: 10 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Item Piles Changelog

## Version 3.0.9

- Added support for secretly giving items to others via the right-click context menu on items on the D&D5e system
- Fixed some systems not allowing players to preview items by clicking on the names in item piles when the player had no permissions configured on the item pile
- Fixed item pile on interact macros not working when utilizing module or system macro compendiums
- Fixed updating item piles tokens would sometimes reset it to a default non-item pile token
- Fixed item piles not being deleted after being emptied even though they were configured to be deleted
- Fixed being unable to switch inspecting character in item piles
- Fixed some interfaces being broken when other modules were active due to them declaring global CSS classes

## Version 3.0.8

- Fixed not being able to drop items onto scenes (thanks diwako on github!)
Expand Down
1 change: 1 addition & 0 deletions languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@
"Header": "Giving Item: {item_name}",
"SelectPlaceholder": "Pick character to send item to",
"ContentMultipleQuantity": "You have {quantity} of this item, how many do you want to give away?",
"Secret": "Give without letting others know (except GMs)",
"Submit": "Send"
},
"ReceiveItem": {
Expand Down
42 changes: 30 additions & 12 deletions src/API/chat-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import TradeAPI from "./trade-api.js";

export default class ChatAPI {

static CHAT_MESSAGE_STYLES = CONSTANTS.IS_V12 ? CONST.CHAT_MESSAGE_STYLES : CONST.CHAT_MESSAGE_TYPES;
static CHAT_MESSAGE_STYLES = CONSTANTS.IS_V12 ? CONST.CHAT_MESSAGE_TYPES : CONST.CHAT_MESSAGE_STYLES;

static initialize() {

Expand Down Expand Up @@ -152,12 +152,14 @@ export default class ChatAPI {
* @param target
* @param item
* @param userId
* @param targetUserId
* @param secret
* @returns {Promise}
*/
static async _outputGiveItem(source, target, item, userId) {
static async _outputGiveItem(source, target, item, userId, targetUserId, secret) {
if (game.user.id !== userId || !Helpers.getSetting(SETTINGS.OUTPUT_TO_CHAT)) return;
const [itemData, itemCurrencies] = await this._formatItemData(source, [item]);
return this._giveChatMessage(source, target, itemData.concat(itemCurrencies), userId);
return this._giveChatMessage(source, target, itemData.concat(itemCurrencies), userId, targetUserId, secret);
}

/**
Expand Down Expand Up @@ -434,7 +436,7 @@ export default class ChatAPI {

return this._createNewChatMessage(game.user.id, {
user: game.user.id,
type: isPrivate ? CONST.CHAT_MESSAGE_STYLES.WHISPER : ChatAPI.CHAT_MESSAGE_STYLES.OTHER,
type: isPrivate ? ChatAPI.CHAT_MESSAGE_STYLES.WHISPER : ChatAPI.CHAT_MESSAGE_STYLES.OTHER,
content: chatCardHtml,
flavor: "Item Piles" + (isPrivate ? ": " + game.i18n.localize("ITEM-PILES.Chat.PrivateTrade") : ""),
speaker: ChatMessage.getSpeaker({ alias: game.user.name }),
Expand Down Expand Up @@ -495,7 +497,7 @@ export default class ChatAPI {

}

static async _giveChatMessage(source, target, items) {
static async _giveChatMessage(source, target, items, userId, targetUserId, secret) {

const now = (+new Date());

Expand All @@ -510,7 +512,7 @@ export default class ChatAPI {

for (const message of messages) {
const flags = foundry.utils.getProperty(message, CONSTANTS.FLAGS.PILE);
if (flags && flags.version && !foundry.utils.isNewerVersion(Helpers.getModuleVersion(), flags.version) && flags.source === sourceActor.uuid && flags.target === targetActor.uuid && message.isAuthor) {
if (flags && flags.version && !foundry.utils.isNewerVersion(Helpers.getModuleVersion(), flags.version) && flags.source === sourceActor.uuid && flags.target === targetActor.uuid && message.isAuthor && (flags.secret === undefined || flags.secret === secret)) {
return this._updateExistingGiveMessage(message, sourceActor, targetActor, items)
}
}
Expand All @@ -522,19 +524,33 @@ export default class ChatAPI {
items: items
});

return this._createNewChatMessage(game.user.id, {
user: game.user.id,
const user = game.users.get(userId);

const chatData = {
user: user.id,
type: ChatAPI.CHAT_MESSAGE_STYLES.OTHER,
content: chatCardHtml,
flavor: "Item Piles",
speaker: ChatMessage.getSpeaker({ alias: game.user.name }),
speaker: ChatMessage.getSpeaker({ alias: user.name }),
[CONSTANTS.FLAGS.PILE]: {
version: Helpers.getModuleVersion(),
source: sourceActor.uuid,
target: targetActor.uuid,
items: items
items: items,
secret
}
})
}

if (secret) {
chatData.whisper = Array.from(game.users)
.filter(user => user.isGM)
.map(user => user.id);
chatData.whisper.push(userId);
chatData.whisper.push(targetUserId);
chatData.type = ChatAPI.CHAT_MESSAGE_STYLES.WHISPER;
}

return this._createNewChatMessage(user.id, chatData)

}

Expand Down Expand Up @@ -631,9 +647,11 @@ export default class ChatAPI {
if (mode === 2) {
chatData.whisper.push(userId);
}
chatData.type = CONST.CHAT_MESSAGE_STYLES.WHISPER;
chatData.type = ChatAPI.CHAT_MESSAGE_STYLES.WHISPER;
}

} else if (chatData.whisper.length) {
chatData.whisper = Array.from(new Set(chatData.whisper));
}

return ChatMessage.create(chatData);
Expand Down
16 changes: 9 additions & 7 deletions src/API/private-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,12 @@ export default class PrivateAPI {
}

static _onPreUpdateToken(doc, changes) {
if (!foundry.utils.hasProperty(changes, "actorLink")) return;
const diff = foundry.utils.diffObject(doc, changes);
if (!foundry.utils.hasProperty(diff, "actorLink")) return;
if (!PileUtilities.isValidItemPile(doc)) return;
const flagData = PileUtilities.getActorFlagData(doc);
const cleanFlagData = PileUtilities.cleanFlagData(flagData);
changes[CONSTANTS.FLAGS.PILE] = doc.actorLink ? cleanFlagData : null;
changes[CONSTANTS.FLAGS.PILE] = diff.actorLink ? cleanFlagData : null;
}

/**
Expand Down Expand Up @@ -1477,7 +1478,8 @@ export default class PrivateAPI {
* @private
*/
static async _evaluateItemPileChange(doc, changes = {}, force = false) {
const duplicatedChanges = foundry.utils.deepClone(changes);
const diff = foundry.utils.diffObject(doc, changes);
const duplicatedChanges = foundry.utils.deepClone(diff);
const target = doc?.token ?? doc;
if (!Helpers.isResponsibleGM()) return;
if (!force && !PileUtilities.shouldEvaluateChange(target, duplicatedChanges)) return;
Expand Down Expand Up @@ -1875,13 +1877,13 @@ export default class PrivateAPI {
Helpers.custom_notify(game.i18n.format("ITEM-PILES.Notifications.ItemTransferred", {
source_actor_name: sourceActor.name, target_actor_name: targetActor.name, item_name: item.name
}));
Hooks.callAll(CONSTANTS.HOOKS.ITEM.GIVE, sourceActor, targetActor, dropData.itemData, game.user.id);
Hooks.callAll(CONSTANTS.HOOKS.ITEM.GIVE, sourceActor, targetActor, dropData.itemData, game.user.id, game.user.id, dropData?.secret);
return this._transferItems(sourceUuid, targetUuid, [dropData.itemData], game.user.id)
}
}

return ItemPileSocket.executeForUsers(ItemPileSocket.HANDLERS.GIVE_ITEMS, [user ? user.id : gms[0]], {
userId: game.user.id, sourceUuid, targetUuid, itemData: dropData.itemData
userId: game.user.id, sourceUuid, targetUuid, itemData: dropData.itemData, secret: dropData?.secret
});
}
}
Expand Down Expand Up @@ -1911,10 +1913,10 @@ export default class PrivateAPI {

}

static async _giveItemsResponse({ userId, accepted, sourceUuid, targetUuid, itemData } = {}) {
static async _giveItemsResponse({ userId, accepted, sourceUuid, targetUuid, itemData, secret } = {}) {
const user = game.users.get(userId);
if (accepted) {
await ItemPileSocket.callHook(CONSTANTS.HOOKS.ITEM.GIVE, sourceUuid, targetUuid, itemData, game.user.id, userId)
await ItemPileSocket.callHook(CONSTANTS.HOOKS.ITEM.GIVE, sourceUuid, targetUuid, itemData, game.user.id, userId, secret)
await PrivateAPI._removeItems(sourceUuid, [itemData], game.user.id);
return Helpers.custom_notify(game.i18n.format("ITEM-PILES.Notifications.GiveItemAccepted", { user_name: user.name }));
}
Expand Down
11 changes: 7 additions & 4 deletions src/applications/components/ActorPicker.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,22 @@
let editQuantities = store.editQuantities;
let changingActor = false;
let playerActors = game.actors.filter(actor => actor.isOwner && actor !== store.pileActor && actor.prototypeToken.actorLink);
let playerActors = game.actors.filter(actor => actor.isOwner && actor !== store.actor && actor.prototypeToken.actorLink);
let recipientUuid = Utilities.getUuid(store.recipient);
const recipientDoc = store.recipientDocument;
$: {
$recipientDoc;
recipientUuid = store.recipient ? Utilities.getUuid(store.recipient) : false;
if (!changingActor) {
recipientUuid = store.recipient ? Utilities.getUuid(store.recipient) : false;
}
}
function changeRecipientActor() {
store.recipient = playerActors.find(actor => Utilities.getUuid(actor) === recipientUuid);
store.update();
const newRecipient = playerActors.find(actor => Utilities.getUuid(actor) === recipientUuid);
changingActor = false;
if (recipientUuid === store.recipient.uuid) return;
store.updateRecipient(newRecipient)
}
</script>
Expand Down
28 changes: 15 additions & 13 deletions src/applications/dialogs/give-items-dialog/give-items-shell.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,26 @@
const canItemStack = PileUtilities.canItemStack(item);
let quantity = 1;
let secret = false;
let selectedActor = localStorage.getItem("item-piles-give-item") ?? false;
let items = Array.from(game.actors)
.filter(actor => {
return (actor.type === "character" || actor.type === "npc")
&& actor !== item.parent
&& !PileUtilities.isValidItemPile(actor)
&& (game.user.isGM || (actor.ownership["default"] >= 1 || actor.ownership[game.user.id] >= 1))
return game.user.isGM || (actor.ownership["default"] >= 1 || actor.ownership[game.user.id] >= 1);
})
.concat(game.users.map(user => user.character).filter(Boolean))
.filter(actor => actor !== item.parent)
.filter((actor, index, self) => {
return index === self.findIndex(a => a.uuid === actor.uuid)
})
.map(actor => ({
value: actor.uuid,
label: actor.name,
actor,
group: actor.hasPlayerOwner ? "Player Characters" : "Other Characters"
group: actor.hasPlayerOwner ? "Player Characters" : "Unassigned Characters"
}))
.sort((a, b) => {
return a.name > b.name ? ((b.actor.hasPlayerOwner - a.actor.hasPlayerOwner) - 1) : ((b.actor.hasPlayerOwner - a.actor.hasPlayerOwner) + 1);
return (a.group >= b.group ? 100000 : -100000) + (a.label >= b.label ? 1 : -1);
});
if (selectedActor && !items.some(data => data.value === selectedActor)) {
Expand All @@ -48,7 +51,7 @@
function submit() {
localStorage.setItem("item-piles-give-item", selectedActor.value)
application.options.resolve({ quantity, target: selectedActor.value });
application.options.resolve({ quantity, secret, target: selectedActor.value });
application.close();
}
Expand All @@ -73,7 +76,6 @@
<SliderInput min={1} max={sliderQuantity} maxInput={sliderQuantity} divideBy={1} bind:value={quantity}/>
{/if}
<div class="form-group">
<Select
--background="rgba(0, 0, 0, 0.05)"
Expand All @@ -97,6 +99,11 @@
></Select>
</div>
<div class="form-group item-piles-flexrow">
<input id="item-piles-give-item-secret" type="checkbox" bind:checked={secret}/>
<label for="item-piles-give-item-secret">{localize("ITEM-PILES.Dialogs.GiveItems.Secret")}</label>
</div>
<footer class="sheet-footer item-piles-flexrow" style="margin-top: 0.25rem;">
<button disabled={!selectedActor} on:click|once={requestSubmit} type="button">
<i class="fas fa-box"></i>
Expand All @@ -109,8 +116,3 @@
</footer>
</form>
<style lang="scss">
</style>
4 changes: 2 additions & 2 deletions src/applications/item-editor/item-editor-shell.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@
{ value: "vault", label: localize("ITEM-PILES.Applications.ItemEditor.Vault") },
]}/>

<section class="tab-body">
<section class="item-piles-tab-body">

<div class="tab flex">
<div class="item-piles-tab">

{#if activeTab === 'general'}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,13 @@
{/if}

<footer class="sheet-footer item-piles-flexrow item-piles-top-divider">
{#if editQuantities}
{#if editQuantities && !$deleted}
<button type="button" on:click={() => { store.update() }}>
<i class="fas fa-save"></i> {localize("ITEM-PILES.Applications.ItemPileConfig.Update")}
</button>
{/if}

{#if $pileData.splitAllEnabled}
{#if $pileData.splitAllEnabled && !$deleted}
<button type="button" on:click={() => { store.splitAll() }} disabled="{isPileEmpty || !canBeSplit}"
data-tooltip={num_players === 0 && !isPileEmpty ? localize("ITEM-PILES.Inspect.SplitNoPlayers") : ""}>
<i class="fas fa-handshake"></i>
Expand All @@ -194,13 +194,13 @@
</button>
{/if}

{#if store.recipient && $pileData.takeAllEnabled}
{#if store.recipient && $pileData.takeAllEnabled && !$deleted}
<button type="submit" on:click={() => { store.takeAll() }} disabled="{isPileEmpty}">
<i class="fas fa-fist-raised"></i> {localize("ITEM-PILES.Inspect.TakeAll")}
</button>
{/if}

{#if isContainer && !application.options.remote}
{#if isContainer && !application.options.remote && !$deleted}
<button type="submit" on:click={() => { store.closeContainer(); application.close(); }}>
<i class="fas fa-box"></i> {localize("ITEM-PILES.Inspect.Close")}
</button>
Expand Down
22 changes: 16 additions & 6 deletions src/applications/settings-app/settings-shell.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,10 @@
<Tabs bind:activeTab {tabs}/>
{/if}
<section class="tab-body">
<section class="item-piles-tab-body">
<div class="tab flex" class:active={activeTab === 'local'} data-scope="primary" data-tab="local">
<div class="item-piles-tab" class:active={activeTab === 'local'} data-scope="primary"
data-tab="local">
<Setting bind:data="{settings[SETTINGS.INVERT_SHEET_OPEN]}" key={SETTINGS.INVERT_SHEET_OPEN}/>
<Setting bind:data="{settings[SETTINGS.HIDE_ACTOR_HEADER_TEXT]}" key={SETTINGS.HIDE_ACTOR_HEADER_TEXT}/>
Expand Down Expand Up @@ -162,7 +163,7 @@
</div>
{#if userCanChangeSettings}
<div class="tab flex" class:active={activeTab === 'module'} data-scope="primary" data-tab="module">
<div class="item-piles-tab" class:active={activeTab === 'module'}>
<Setting key={SETTINGS.ENABLE_DROPPING_ITEMS} bind:data="{settings[SETTINGS.ENABLE_DROPPING_ITEMS]}"/>
<Setting key={SETTINGS.ENABLE_GIVING_ITEMS} bind:data="{settings[SETTINGS.ENABLE_GIVING_ITEMS]}"/>
<Setting key={SETTINGS.ENABLE_TRADING} bind:data="{settings[SETTINGS.ENABLE_TRADING]}"/>
Expand All @@ -176,12 +177,12 @@
<SettingButton key={SETTINGS.CUSTOM_ITEM_CATEGORIES} bind:data="{settings[SETTINGS.CUSTOM_ITEM_CATEGORIES]}"/>
</div>
<div class="tab flex" class:active={activeTab === 'styles'} data-scope="primary" data-tab="styles">
<div class="item-piles-tab" class:active={activeTab === 'styles'}>
<SettingButton key={SETTINGS.CSS_VARIABLES} bind:data="{settings[SETTINGS.CSS_VARIABLES]}"/>
<SettingButton key={SETTINGS.VAULT_STYLES} bind:data="{settings[SETTINGS.VAULT_STYLES]}"/>
</div>
<div class="tab flex" class:active={activeTab === 'system'} data-scope="primary" data-tab="system">
<div class="item-piles-tab" class:active={activeTab === 'system'}>
<SettingButton data={{
name: "ITEM-PILES.Settings.Reset.Title",
hint: "ITEM-PILES.Settings.Reset.Hint",
Expand Down Expand Up @@ -283,11 +284,19 @@
}
}
.tab-body {
.item-piles-tab-body {
max-height: 729px;
min-height: 729px;
overflow-y: scroll;
padding: 5px;
.item-piles-tab {
display: none;
&.active {
display: block;
}
}
}
footer {
Expand All @@ -296,4 +305,5 @@
padding-top: 0.5rem;
}
</style>
Loading

0 comments on commit 54ac74f

Please sign in to comment.