Enhance sheets

This commit is contained in:
sladecraven 2021-02-03 15:33:07 +01:00
parent c65f1b8246
commit d248411e90
10 changed files with 276 additions and 13 deletions

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -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
View 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;
}
}

View 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( );
});
}
}

View File

@ -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();

View File

@ -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 );
});
}
}

View File

@ -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&nbsp;the Cover or Concealment consequences. To&nbsp;make the most of these, however, the character&nbsp;will need to make a take cover action. There&nbsp;is a difference between &ldquo;taking cover&rdquo; and simply having cover.&nbsp;Think of it like this: if Monique is being&nbsp;fired upon and runs to where a stack of barrels&nbsp;is between her and her attacker, she has cover&nbsp;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&rsquo;t &ldquo;taken cover&rdquo; to make the most of it.</p>\n<p>When a character takes cover, she needs&nbsp;to have moved immediately next to the object providing her with the cover consequence. She&nbsp;then performs the take cover action, setting&nbsp;herself up so that the cover does not hinder&nbsp;her attacks but still provides her with protection against the attacks of others. Without this,&nbsp;the cover&rsquo;s bonus to Defense applies both to&nbsp;incoming and outgoing attacks.</p>\n<p>&nbsp;</p>\n<p>&nbsp;</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>&nbsp;</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&rsquo;s boot. Some weapons may have longer reload times, requiring more AP. This will be noted in the weapon&rsquo;s properties.</p>\n<p>&nbsp;</p>\n<p>&nbsp;</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"}

View File

@ -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%);

View 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>