Enhance sheets
This commit is contained in:
parent
c65f1b8246
commit
d248411e90
1
img/icons/action_none.svg
Normal file
1
img/icons/action_none.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><radialGradient id="guard13007-pause-button-gradient-0"><stop offset="0%" stop-color="#000" stop-opacity="1"></stop><stop offset="100%" stop-color="#9b9b9b" stop-opacity="1"></stop></radialGradient></defs><rect fill="url(#guard13007-pause-button-gradient-0)" stroke="#000000" stroke-opacity="1" stroke-width="1" height="510" width="510" rx="32" ry="32"></rect><g class="" style="" transform="translate(0,0)"><path d="M120.16 45A20.162 20.162 0 0 0 100 65.16v381.68A20.162 20.162 0 0 0 120.16 467h65.68A20.162 20.162 0 0 0 206 446.84V65.16A20.162 20.162 0 0 0 185.84 45h-65.68zm206 0A20.162 20.162 0 0 0 306 65.16v381.68A20.162 20.162 0 0 0 326.16 467h65.68A20.162 20.162 0 0 0 412 446.84V65.16A20.162 20.162 0 0 0 391.84 45h-65.68z" fill="#fff" fill-opacity="1"></path></g></svg>
|
After Width: | Height: | Size: 884 B |
BIN
img/ui/background_sheet01.webp
Normal file
BIN
img/ui/background_sheet01.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.9 KiB |
@ -168,8 +168,6 @@ export class SoSActor extends Actor {
|
||||
let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/dialog-flip.html', flipData);
|
||||
new SoSFlipDialog(flipData, html).render(true);
|
||||
|
||||
//console.log("STAT", this);
|
||||
//let result = this.cardDeck.doFlipStat( duplicate(this.data.data.stat[statKey]) );
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
30
module/sos-combat.js
Normal file
30
module/sos-combat.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { SoSCardDeck } from "./sos-card-deck.js";
|
||||
import { SoSUtility } from "./sos-utility.js";
|
||||
import { SoSFlipDialog } from "./sos-flip-dialog.js";
|
||||
import { SoSDialogCombatActions } from "./sos-dialog-combat-actions.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class SoSCombat extends Combat {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async nextRound() {
|
||||
console.log("NEXT ROUND !!!!");
|
||||
|
||||
if ( game.user.isGM ) {
|
||||
ChatMessage.create( { content: `New round !! Click on the button below to declare your actions for round ${this.round} !<br>
|
||||
<a class='chat-card-button' id='button-declare-actions' data-combat-id='${this._id} data-round='${this.round}'>Declare actions</a>`,
|
||||
whisper: game.users.map(user => user.data._id) } );
|
||||
}
|
||||
super.nextRound();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
setupActorActions(actionConf) {
|
||||
if ( !this.phaseSetup) this.phaseSetup = []; // Opportunistic init
|
||||
if ( !this.phaseSetup[this.round] ) this.phaseSetup[this.round] = {}; // Bis
|
||||
|
||||
// Keep track
|
||||
this.phaseSetup[this.round][actionConf.userId] = actionConf;
|
||||
}
|
||||
|
||||
}
|
130
module/sos-dialog-combat-actions.js
Normal file
130
module/sos-dialog-combat-actions.js
Normal file
@ -0,0 +1,130 @@
|
||||
import { SoSUtility } from "./sos-utility.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class SoSDialogCombatActions extends Dialog {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async create( combatId, round ) {
|
||||
|
||||
let combatActions = {
|
||||
actionsList: await SoSUtility.loadCompendium( 'foundryvtt-shadows-over-sol.combat-actions' ),
|
||||
actionPoints: SoSUtility.fillRange(0, 6),
|
||||
combatId: combatId,
|
||||
round: round
|
||||
}
|
||||
for ( let i=0; i<combatActions.actionsList.length; i++) {
|
||||
if ( combatActions.actionsList[i].name == 'No Action') {
|
||||
combatActions.noActionId = i;
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("ACTIONS", combatActions.actionsList );
|
||||
let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/dialog-combat-actions.html', combatActions);
|
||||
return new SoSDialogCombatActions(combatActions, html );
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
constructor(combatActions, html, options = {} ) {
|
||||
let dialogConf = {
|
||||
title: "Combat Actions",
|
||||
content: html,
|
||||
buttons: {
|
||||
validate: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "Validate",
|
||||
callback: (html) => { this.validateActions(html) }
|
||||
}
|
||||
},
|
||||
default: "validate"
|
||||
};
|
||||
super(dialogConf, options);
|
||||
|
||||
this.combatActions = combatActions;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async close() {
|
||||
let ap = Number($('#remain-ap').text());
|
||||
if ( ap >= 0 ) {
|
||||
super.close();
|
||||
|
||||
let action3Index = $('#action3').val();
|
||||
let action3 = duplicate(this.combatActions.actionsList[action3Index]);
|
||||
let action2Index = $('#action2').val();
|
||||
let action2 = duplicate(this.combatActions.actionsList[action2Index]._id);
|
||||
let action1Index = $('#action1').val();
|
||||
let action1 = duplicate(this.combatActions.actionsList[action1Index]._id);
|
||||
|
||||
game.socket.emit("system.foundryvtt-reve-de-dragon", {
|
||||
name: "msg_declare_actions", data: {
|
||||
userId: game.userId,
|
||||
phaseArray: [ action1, action2, action3],
|
||||
remainingAP: ap
|
||||
} } );
|
||||
} else {
|
||||
ui.notifications.warn("Action Points are below 0 ! Please check your phases !");
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
validateActions( html ) {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
resetAP() {
|
||||
let maxAP = $('#actionMax').val();
|
||||
$('#remain-ap').text( maxAP);
|
||||
|
||||
$('#action3').val( this.combatActions.noActionId );
|
||||
$('#action2').val( this.combatActions.noActionId );
|
||||
$('#action1').val( this.combatActions.noActionId );
|
||||
$('#action3').prop('disabled', false);
|
||||
$('#action2').prop('disabled', false);
|
||||
$('#action1').prop('disabled', false);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
updateAP( ) {
|
||||
let remainAP = $('#actionMax').val();
|
||||
|
||||
let action3Index = $('#action3').val();
|
||||
if ( this.combatActions.actionsList[action3Index].name != 'No Action') {
|
||||
remainAP -= 3;
|
||||
}
|
||||
|
||||
let action2Index = $('#action2').val();
|
||||
if ( this.combatActions.actionsList[action2Index].name != 'No Action') {
|
||||
remainAP -= 2;
|
||||
}
|
||||
|
||||
let action1Index = $('#action1').val();
|
||||
if ( this.combatActions.actionsList[action1Index].name != 'No Action') {
|
||||
remainAP -= 1;
|
||||
}
|
||||
|
||||
$('#remain-ap').text( remainAP);
|
||||
|
||||
if ( remainAP < 0 ) {
|
||||
$("#remain-ap").addClass("text-red");
|
||||
ui.notifications.warn("No more Action Points ! Please check your phases !");
|
||||
} else {
|
||||
$("#remain-ap").removeClass("text-red");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/** @override */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
html.find('#actionMax').change((event) => {
|
||||
this.resetAP( );
|
||||
});
|
||||
html.find('.action-select').change((event) => {
|
||||
this.updateAP( );
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -12,6 +12,7 @@ import { SoSActor } from "./actor.js";
|
||||
import { SoSItemSheet } from "./item-sheet.js";
|
||||
import { SoSActorSheet } from "./actor-sheet.js";
|
||||
import { SoSUtility } from "./sos-utility.js";
|
||||
import { SoSCombat } from "./sos-combat.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* Foundry VTT Initialization */
|
||||
@ -51,6 +52,7 @@ Hooks.once("init", async function () {
|
||||
/* -------------------------------------------- */
|
||||
// Define custom Entity classes
|
||||
CONFIG.Actor.entityClass = SoSActor;
|
||||
CONFIG.Combat.entityClass = SoSCombat;
|
||||
CONFIG.SoS = {
|
||||
}
|
||||
|
||||
@ -61,6 +63,11 @@ Hooks.once("init", async function () {
|
||||
Items.unregisterSheet("core", ItemSheet);
|
||||
Items.registerSheet("foundryvtt-shadows-over-sol", SoSItemSheet, { makeDefault: true });
|
||||
|
||||
// Init/registers
|
||||
Hooks.on('renderChatLog', (log, html, data) => {
|
||||
SoSUtility.registerChatCallbacks(html);
|
||||
});
|
||||
|
||||
// Patch the initiative formula
|
||||
_patch_initiative();
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
/* -------------------------------------------- */
|
||||
import { SoSDialogCombatActions } from "./sos-dialog-combat-actions.js";
|
||||
|
||||
export class SoSUtility {
|
||||
/* -------------------------------------------- */
|
||||
export class SoSUtility {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async preloadHandlebarsTemplates() {
|
||||
@ -24,6 +27,14 @@ export class SoSUtility {
|
||||
return Array(end - start + 1).fill().map((item, index) => start + index);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
onSocketMesssage( msg ) {
|
||||
if (msg.name == 'msg_declare_actions' ) {
|
||||
let combat = game.combats.get( msg.data.combatId); // Get the associated combat
|
||||
combat.setupActorActions( msg.data );
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async loadCompendiumNames(compendium) {
|
||||
const pack = game.packs.get(compendium);
|
||||
@ -49,4 +60,21 @@ export class SoSUtility {
|
||||
return list;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async openDeclareActions( event) {
|
||||
event.preventDefault();
|
||||
let round = event.currentTarget.attributes['data-round'].value;
|
||||
let combatId = event.currentTarget.attributes['data-combat-id'].value;
|
||||
let d = await SoSDialogCombatActions.create( combatId, round );
|
||||
d.render(true);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async registerChatCallbacks(html) {
|
||||
html.on("click", '#button-declare-actions', event => {
|
||||
SoSUtility.openDeclareActions( event );
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -17,3 +17,4 @@
|
||||
{"name":"Taking Cover","permission":{"default":0,"pJLHbu8WlBVyfXG4":3},"type":"action","data":{"type":"move","minap":1,"description":"<p>Sometimes a basic move or a change in posture will be enough to gain a character the Cover or Concealment consequences. To make the most of these, however, the character will need to make a take cover action. There is a difference between “taking cover” and simply having cover. Think of it like this: if Monique is being fired upon and runs to where a stack of barrels is between her and her attacker, she has cover from the attacks. At the same time, if she wants to shoot back, the barrels are in her way as well. She has cover, but she hasn’t “taken cover” to make the most of it.</p>\n<p>When a character takes cover, she needs to have moved immediately next to the object providing her with the cover consequence. She then performs the take cover action, setting herself up so that the cover does not hinder her attacks but still provides her with protection against the attacks of others. Without this, the cover’s bonus to Defense applies both to incoming and outgoing attacks.</p>\n<p> </p>\n<p> </p>"},"flags":{},"img":"systems/foundryvtt-shadows-over-sol/img/icons/action_cover.svg","effects":[],"_id":"pwq6CB24QhNkrt1s"}
|
||||
{"name":"Posture","permission":{"default":0,"pJLHbu8WlBVyfXG4":3},"type":"action","data":{"type":"move","minap":1,"description":"<p>By default, characters are assumed to be standing. There are times, however, when a character might want to kneel, crouch, sit or lay down. Changing between any of these postures is a move action, and one change in posture may be tacked on for free to another move action, such as a basic move or taking cover. If performed by itself, changing posture costs the usual amount of AP for the phase.</p>\n<p> </p>"},"flags":{},"img":"systems/foundryvtt-shadows-over-sol/img/icons/action_posture.svg","effects":[],"_id":"tF8mIKv0zi2HLv9B"}
|
||||
{"name":"Drawing/Reloading","permission":{"default":0,"pJLHbu8WlBVyfXG4":3},"type":"action","data":{"type":"interact","minap":1,"description":"<p>Drawing a weapon or reloading one is a simple interact action that involves taking a weapon out or putting ammunition into one. This costs at least 1 AP if the weapon or magazine was in a holster or other easy-to-access position. It costs at least 2 AP if it was harder to reach, such as stowed at the top of a pack or tucked inside one’s boot. Some weapons may have longer reload times, requiring more AP. This will be noted in the weapon’s properties.</p>\n<p> </p>\n<p> </p>"},"flags":{},"img":"systems/foundryvtt-shadows-over-sol/img/icons/action_reload.svg","effects":[],"_id":"uSQw858IiBrWkeSj"}
|
||||
{"name":"No Action","permission":{"default":0,"pJLHbu8WlBVyfXG4":3},"type":"action","data":{"type":"other","minap":0,"description":0},"flags":{},"img":"systems/foundryvtt-shadows-over-sol/img/icons/action_none.svg","effects":[],"_id":"bqSieyyOLSY8tU2U"}
|
||||
|
@ -203,7 +203,6 @@ table {border: 1px solid #7a7971;}
|
||||
}
|
||||
|
||||
/* Styles limited to foundryvtt-reve-de-dragon sheets */
|
||||
|
||||
.foundryvtt-shadows-over-sol .sheet-header {
|
||||
-webkit-box-flex: 0;
|
||||
-ms-flex: 0 0 210px;
|
||||
@ -394,7 +393,8 @@ table {border: 1px solid #7a7971;}
|
||||
/* ======================================== */
|
||||
/* Sheet */
|
||||
.window-app.sheet .window-content .sheet-header{
|
||||
background: #011d33 url("img/bg_header.webp") no-repeat left top;
|
||||
background: url('../img/ui/background_sheet01.webp') no-repeat left top;
|
||||
/*background: #011d33 url("img/bg_header.webp") no-repeat left top;*/
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
@ -405,6 +405,10 @@ table {border: 1px solid #7a7971;}
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.sheet .sheet-header {
|
||||
background: url('../img/ui/background_sheet01.webp') no-repeat left top;
|
||||
}
|
||||
|
||||
.window-app .window-content, .window-app.sheet .window-content .sheet-body{
|
||||
background: rgb(245,245,240) url("img/bg_left.jpg") no-repeat left top;
|
||||
}
|
||||
@ -876,7 +880,7 @@ ul, li {
|
||||
border-radius: 0;
|
||||
background: rgba(30, 25, 20, 1);
|
||||
background-origin: padding-box;
|
||||
border-image: url(img/ui/footer-button.png) 10 repeat;
|
||||
/*border-image: url(img/ui/footer-button.png) 10 repeat;*/
|
||||
border-image-width: 4px;
|
||||
border-image-outset: 0px;
|
||||
}
|
||||
@ -884,7 +888,7 @@ ul, li {
|
||||
#controls .scene-control.active, #controls .control-tool.active, #controls .scene-control:hover, #controls .control-tool:hover {
|
||||
background: rgba(72, 46, 28, 1);
|
||||
background-origin: padding-box;
|
||||
border-image: url(img/ui/footer-button.png) 10 repeat;
|
||||
/*border-image: url(img/ui/footer-button.png) 10 repeat;*/
|
||||
border-image-width: 4px;
|
||||
border-image-outset: 0px;
|
||||
box-shadow: 0 0 3px #ff6400;
|
||||
@ -896,7 +900,7 @@ ul, li {
|
||||
}
|
||||
|
||||
#hotbar #action-bar .macro {
|
||||
border-image: url(img/ui/bg_control.jpg) 21 repeat;
|
||||
/*border-image: url(img/ui/bg_control.jpg) 21 repeat;*/
|
||||
border-image-slice: 6 6 6 6 fill;
|
||||
border-image-width: 6px 6px 6px 6px;
|
||||
border-image-outset: 0px 0px 0px 0px;
|
||||
@ -909,7 +913,7 @@ ul, li {
|
||||
}
|
||||
|
||||
#players {
|
||||
border-image: url(img/ui/footer-button.png) 10 repeat;
|
||||
/*border-image: url(img/ui/footer-button.png) 10 repeat;*/
|
||||
border-image-width: 4px;
|
||||
border-image-outset: 0px;
|
||||
background: rgba(30, 25, 20, 1);
|
||||
@ -922,7 +926,7 @@ ul, li {
|
||||
#navigation #scene-list .scene.nav-item {
|
||||
background: rgba(30, 25, 20, 1);
|
||||
background-origin: padding-box;
|
||||
border-image: url(img/ui/footer-button.png) 10 repeat;
|
||||
/*border-image: url(img/ui/footer-button.png) 10 repeat;*/
|
||||
border-image-width: 4px;
|
||||
border-image-outset: 0px;
|
||||
}
|
||||
@ -930,7 +934,7 @@ ul, li {
|
||||
#navigation #scene-list .scene.view, #navigation #scene-list .scene.context {
|
||||
background: rgba(72, 46, 28, 1);
|
||||
background-origin: padding-box;
|
||||
border-image: url(img/ui/footer-button.png) 10 repeat;
|
||||
/*border-image: url(img/ui/footer-button.png) 10 repeat;*/
|
||||
border-image-width: 4px;
|
||||
border-image-outset: 0px;
|
||||
box-shadow: 0 0 3px #ff6400;
|
||||
@ -939,7 +943,7 @@ ul, li {
|
||||
#navigation #nav-toggle {
|
||||
background: rgba(30, 25, 20, 1);
|
||||
background-origin: padding-box;
|
||||
border-image: url(img/ui/footer-button.png) 10 repeat;
|
||||
/*border-image: url(img/ui/footer-button.png) 10 repeat;*/
|
||||
border-image-width: 4px;
|
||||
border-image-outset: 0px;
|
||||
}
|
||||
@ -971,7 +975,7 @@ ul, li {
|
||||
width: 360px;
|
||||
|
||||
background: rgba(30, 25, 20, 0.9);
|
||||
border-image: url(img/ui/bg_control.jpg) 21 repeat;
|
||||
/*border-image: url(img/ui/bg_control.jpg) 21 repeat;*/
|
||||
border-image-slice: 6 6 6 6 fill;
|
||||
border-image-width: 6px 6px 6px 6px;
|
||||
border-image-outset: 0px 0px 0px 0px;
|
||||
@ -1006,6 +1010,11 @@ ul, li {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.text-red {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.chat-card-button {
|
||||
box-shadow: inset 0px 1px 0px 0px #a6827e;
|
||||
background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%);
|
||||
|
59
templates/dialog-combat-actions.html
Normal file
59
templates/dialog-combat-actions.html
Normal file
@ -0,0 +1,59 @@
|
||||
<form class="combat-actions-dialog">
|
||||
|
||||
<h2 class="compdialog" id="combatActions">Combat Actions for round {{round}}</h2>
|
||||
|
||||
<section class="sheet-body">
|
||||
<div class="tab" data-group="primary">
|
||||
<div class="flexrow">
|
||||
<label for="categorie generic-label">AP available : </label>
|
||||
<select name="actionMax" id="actionMax" data-dtype="number">
|
||||
{{#select actionMax}}
|
||||
{{#each actionPoints as |point key|}}
|
||||
<option value={{key}} {{#if (eq key 3)}}selected{{/if}}>{{point}}</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="categorie generic-label">Remaining AP : <span id="remain-ap">3</span> </label>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="tab" data-group="primary">
|
||||
<div class="flexrow">
|
||||
<label for="categorie generic-label">Action in phase 3 (3 AP) </label>
|
||||
<select name="action3" class='action-select' id="action3" data-dtype="number">
|
||||
{{#select action3}}
|
||||
{{#each actionsList as |action key|}}
|
||||
<option value={{key}} {{#if (eq action.name "No Action")}}selected{{/if}}>{{action.name}} (min AP : {{action.data.minap}})</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="tab" data-group="primary">
|
||||
<div class="flexrow">
|
||||
<label for="categorie generic-label">Action in phase 2 (2 AP) : </label>
|
||||
<select name="action2" class='action-select' id="action2" data-dtype="number">
|
||||
{{#select action2}}
|
||||
{{#each actionsList as |action key|}}
|
||||
<option value={{key}} {{#if (eq action.name "No Action")}}selected{{/if}}>{{action.name}} (min AP : {{action.data.minap}})</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="tab" data-group="primary">
|
||||
<div class="flexrow">
|
||||
<label for="categorie generic-label">Action in phase 1 (1 AP) : </label>
|
||||
<select name="action1" class='action-select' id="action1" data-dtype="number">
|
||||
{{#select action1}}
|
||||
{{#each actionsList as |action key|}}
|
||||
<option value={{key}} {{#if (eq action.name "No Action")}}selected{{/if}}>{{action.name}} (min AP : {{action.data.minap}})</option>
|
||||
{{/each}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</form>
|
Loading…
Reference in New Issue
Block a user