add group roll dialog

This commit is contained in:
moliloo 2025-09-19 23:10:05 -03:00
parent 5a874ae268
commit 9e9c1d2ac0
9 changed files with 324 additions and 3 deletions

View file

@ -10,3 +10,4 @@ export { default as OwnershipSelection } from './ownershipSelection.mjs';
export { default as RerollDamageDialog } from './rerollDamageDialog.mjs';
export { default as ResourceDiceDialog } from './resourceDiceDialog.mjs';
export { default as ActionSelectionDialog } from './actionSelectionDialog.mjs';
export { default as GroupRollDialog } from './group-roll-dialog.mjs';

View file

@ -0,0 +1,165 @@
import autocomplete from 'autocompleter';
import { abilities } from '../../config/actorConfig.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class GroupRollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(actors) {
super();
this.actors = actors;
this.actorLeader = {};
this.actorsMembers = [];
}
get title() {
return 'Group Roll';
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'group-roll'],
position: { width: 'auto', height: 'auto' },
actions: {
roll: GroupRollDialog.#roll,
removeLeader: GroupRollDialog.#removeLeader,
removeMember: GroupRollDialog.#removeMember
},
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
};
static PARTS = {
application: {
id: 'group-roll',
template: 'systems/daggerheart/templates/dialogs/group-roll/group-roll.hbs'
}
};
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
const changeChoices = this.actors;
htmlElement.querySelectorAll('.leader-change-input').forEach(element => {
autocomplete({
input: element,
fetch: function (text, update) {
if (!text) {
update(changeChoices);
} else {
text = text.toLowerCase();
var suggestions = changeChoices.filter(n => n.name.toLowerCase().includes(text));
update(suggestions);
}
},
render: function (actor, search) {
const actorName = game.i18n.localize(actor.name);
const matchIndex = actorName.toLowerCase().indexOf(search);
const beforeText = actorName.slice(0, matchIndex);
const matchText = actorName.slice(matchIndex, matchIndex + search.length);
const after = actorName.slice(matchIndex + search.length, actorName.length);
const img = document.createElement('img');
img.src = actor.img;
const element = document.createElement('li');
element.appendChild(img);
const label = document.createElement('span');
label.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
element.appendChild(label);
return element;
},
renderGroup: function (label) {
const itemElement = document.createElement('div');
itemElement.textContent = game.i18n.localize(label);
return itemElement;
},
onSelect: actor => {
element.value = actor.uuid;
this.actorLeader = { actor: actor, trait: '', difficulty: 0 };
this.render();
},
click: e => e.fetch(),
customize: function (_input, _inputRect, container) {
container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ;
},
minLength: 0
});
});
htmlElement.querySelectorAll('.team-push-input').forEach(element => {
autocomplete({
input: element,
fetch: function (text, update) {
if (!text) {
update(changeChoices);
} else {
text = text.toLowerCase();
var suggestions = changeChoices.filter(n => n.name.toLowerCase().includes(text));
update(suggestions);
}
},
render: function (actor, search) {
const actorName = game.i18n.localize(actor.name);
const matchIndex = actorName.toLowerCase().indexOf(search);
const beforeText = actorName.slice(0, matchIndex);
const matchText = actorName.slice(matchIndex, matchIndex + search.length);
const after = actorName.slice(matchIndex + search.length, actorName.length);
const img = document.createElement('img');
img.src = actor.img;
const element = document.createElement('li');
element.appendChild(img);
const label = document.createElement('span');
label.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
element.appendChild(label);
return element;
},
renderGroup: function (label) {
const itemElement = document.createElement('div');
itemElement.textContent = game.i18n.localize(label);
return itemElement;
},
onSelect: actor => {
element.value = actor.uuid;
this.actorsMembers.push({ actor: actor, trait: '', difficulty: 0 });
this.render({ force: true });
},
click: e => e.fetch(),
customize: function (_input, _inputRect, container) {
container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ;
},
minLength: 0
});
});
}
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.leader = this.actorLeader;
context.members = [...this.actorsMembers];
context.traitList = Object.values(abilities).map(trait => ({
label: game.i18n.localize(trait.label),
value: trait.id
}));
return context;
}
static async #removeLeader(_, button) {
this.actorLeader = null;
this.render();
}
static async #removeMember(_, button) {
this.actorsMembers = this.actorsMembers.filter(m => m.actor.uuid !== button.dataset.memberUuid);
this.render();
}
static async #roll(_, button) {
console.log(this.leader, this.members);
console.log(this);
}
}

View file

@ -4,6 +4,7 @@ import { ItemBrowser } from '../../ui/itemBrowser.mjs';
import FilterMenu from '../../ux/filter-menu.mjs';
import DaggerheartMenu from '../../sidebar/tabs/daggerheartMenu.mjs';
import { socketEvent } from '../../../systemRegistration/socket.mjs';
import GroupRollDialog from '../../dialogs/group-roll-dialog.mjs';
export default class Party extends DHBaseActorSheet {
constructor(options) {
@ -30,6 +31,7 @@ export default class Party extends DHBaseActorSheet {
tempBrowser: Party.#tempBrowser,
refeshActions: Party.#refeshActions,
triggerRest: Party.#triggerRest,
groupRoll: Party.#groupRoll,
selectRefreshable: DaggerheartMenu.selectRefreshable,
refreshActors: DaggerheartMenu.refreshActors
},
@ -268,6 +270,10 @@ export default class Party extends DHBaseActorSheet {
});
}
static async #groupRoll(params) {
new GroupRollDialog(this.document.system.partyMembers).render({ force: true });
}
/**
* Get the set of ContextMenu options for Consumable and Loot.
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance

View file

@ -0,0 +1,47 @@
@import '../../utils/colors.less';
.application.daggerheart.group-roll {
fieldset.one-column {
min-width: 500px;
margin-bottom: 10px;
}
.actor-item {
display: flex;
align-items: center;
gap: 15px;
width: 100%;
img {
height: 40px;
width: 40px;
border-radius: 50%;
object-fit: cover;
}
.actor-info {
display: flex;
flex-direction: column;
gap: 10px;
.actor-check-info {
display: flex;
gap: 10px;
.form-fields {
display: flex;
gap: 5px;
align-items: center;
input {
max-width: 40px;
text-align: center;
}
}
}
}
.controls {
margin-left: auto;
}
}
}

View file

@ -30,3 +30,5 @@
@import './multiclass-choice/sheet.less';
@import './reroll-dialog/sheet.less';
@import './group-roll/group-roll.less';

View file

@ -67,6 +67,10 @@
}
}
.standard-form {
font-family: @font-body;
}
&.two-big-buttons {
.window-content {
padding-top: 0;

View file

@ -1,3 +1,6 @@
@import '../../utils/colors.less';
@import '../../utils/fonts.less';
.theme-light .autocomplete {
background-image: url('../assets/parchments/dh-parchment-light.png');
color: black;
@ -27,11 +30,15 @@
}
li[role='option'] {
display: flex;
align-items: center;
gap: 10px;
font-size: var(--font-size-14);
padding-left: 10px;
padding: 0 10px;
cursor: pointer;
&:hover {
&:hover,
&.selected {
background-color: light-dark(@dark, @beige);
color: light-dark(@beige, var(--color-dark-3));
}
@ -39,5 +46,16 @@
> div {
white-space: nowrap;
}
img {
height: 40px;
width: 40px;
border-radius: 50%;
margin-bottom: 10px;
&:first-child {
margin-top: 10px;
}
}
}
}

View file

@ -0,0 +1,78 @@
<div class="group-roll">
<header class="dialog-header">
<h1>Group Roll</h1>
</header>
<fieldset class="one-column">
<legend>Leader</legend>
{{#unless leader.actor}}
<input type="text" class="leader-change-input" name="leader"/>
<div class="drag-area">
<span>Select a Leader</span>
</div>
{{else}}
<div class="actor-item">
<img src="{{leader.actor.img}}" alt="{{leader.actor.name}}">
<div class="actor-info">
<span class="actor-name">{{leader.actor.name}}</span>
<div class="actor-check-info">
<div class="form-fields">
<label>{{localize "DAGGERHEART.GENERAL.Trait.single"}}</label>
<select name="leader.trait" data-dtype="Number">
{{selectOptions traitList selected=leader.trait labelAttr="label" valueAttr="value" }}
</select>
</div>
<div class="form-fields">
<label>{{localize "DAGGERHEART.GENERAL.difficulty"}}</label>
<input type="number" name="leader.difficulty" value="{{leader.difficulty}}">
</div>
</div>
</div>
<div class="controls">
<a data-action="removeLeader" data-leader-uuid="{{leader.actor.uuid}}">
<i class="fa-solid fa-trash"></i>
</a>
</div>
</div>
{{/unless}}
</fieldset>
<fieldset class="one-column">
<legend>Party Team</legend>
<input type="text" class="team-push-input" name="member"/>
{{#if (gt this.members.length 0)}}
{{#each members as |member|}}
<div class="actor-item">
<img src="{{member.actor.img}}" alt="{{member.actor.name}}">
<div class="actor-info">
<span class="actor-name">{{member.actor.name}}</span>
<div class="actor-check-info">
<div class="form-fields">
<label>{{localize "DAGGERHEART.GENERAL.Trait.single"}}</label>
<select name="member.trait" data-dtype="Number">
{{selectOptions traitList selected=member.trait labelAttr="label" valueAttr="value" }}
</select>
</div>
<div class="form-fields">
<label>{{localize "DAGGERHEART.GENERAL.difficulty"}}</label>
<input type="number" name="member.difficulty" value="{{member.difficulty}}">
</div>
</div>
</div>
<div class="controls">
<a data-action="removemember" data-member-uuid="{{member.actor.uuid}}">
<i class="fa-solid fa-trash"></i>
</a>
</div>
</div>
{{/each}}
{{/if}}
<div class="drag-area">
<span>Select a Member</span>
</div>
</fieldset>
<button class="submit-btn" data-action="roll">
<i class="fa-solid fa-dice"></i>
<span>Roll</span>
</button>
</div>

View file

@ -9,7 +9,7 @@
<i class="fa-solid fa-user-group"></i>
<span>Tag Team Roll</span>
</button>
<button>
<button data-action="groupRoll">
<i class="fa-solid fa-users"></i>
<span>Group Roll</span>
</button>