Upload old System
This commit is contained in:
commit
13af30f74b
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Joscha Maier
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
25
LICENSE.txt
Normal file
25
LICENSE.txt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Asacolips Projects / Foundry Mods
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
This license does not apply to the compendium content listed in this software's
|
||||||
|
"packs" directory. See the README for licensing information for the compendium
|
||||||
|
packs.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
423
css/kidsonbrooms.css
Normal file
423
css/kidsonbrooms.css
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");
|
||||||
|
/* Global styles */
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");
|
||||||
|
.window-app {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rollable:hover, .rollable:focus {
|
||||||
|
color: #000;
|
||||||
|
text-shadow: 0 0 10px rgb(146, 0, 225);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-start-2 {
|
||||||
|
grid-column-start: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-span-2 {
|
||||||
|
grid-column-end: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-2col {
|
||||||
|
grid-column: span 2/span 2;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-start-3 {
|
||||||
|
grid-column-start: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-span-3 {
|
||||||
|
grid-column-end: span 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-3col {
|
||||||
|
grid-column: span 3/span 3;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-start-4 {
|
||||||
|
grid-column-start: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-span-4 {
|
||||||
|
grid-column-end: span 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-4col {
|
||||||
|
grid-column: span 4/span 4;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-start-5 {
|
||||||
|
grid-column-start: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-span-5 {
|
||||||
|
grid-column-end: span 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-5col {
|
||||||
|
grid-column: span 5/span 5;
|
||||||
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-start-6 {
|
||||||
|
grid-column-start: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-span-6 {
|
||||||
|
grid-column-end: span 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-6col {
|
||||||
|
grid-column: span 6/span 6;
|
||||||
|
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-start-7 {
|
||||||
|
grid-column-start: 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-span-7 {
|
||||||
|
grid-column-end: span 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-7col {
|
||||||
|
grid-column: span 7/span 7;
|
||||||
|
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-start-8 {
|
||||||
|
grid-column-start: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-span-8 {
|
||||||
|
grid-column-end: span 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-8col {
|
||||||
|
grid-column: span 8/span 8;
|
||||||
|
grid-template-columns: repeat(8, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-start-9 {
|
||||||
|
grid-column-start: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-span-9 {
|
||||||
|
grid-column-end: span 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-9col {
|
||||||
|
grid-column: span 9/span 9;
|
||||||
|
grid-template-columns: repeat(9, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-start-10 {
|
||||||
|
grid-column-start: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-span-10 {
|
||||||
|
grid-column-end: span 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-10col {
|
||||||
|
grid-column: span 10/span 10;
|
||||||
|
grid-template-columns: repeat(10, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-start-11 {
|
||||||
|
grid-column-start: 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-span-11 {
|
||||||
|
grid-column-end: span 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-11col {
|
||||||
|
grid-column: span 11/span 11;
|
||||||
|
grid-template-columns: repeat(11, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-start-12 {
|
||||||
|
grid-column-start: 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-span-12 {
|
||||||
|
grid-column-end: span 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-12col {
|
||||||
|
grid-column: span 12/span 12;
|
||||||
|
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-group-center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: center;
|
||||||
|
flex-wrap: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-group-left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: flex-start;
|
||||||
|
flex-wrap: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-group-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: flex-end;
|
||||||
|
flex-wrap: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexshrink {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexlarge {
|
||||||
|
flex: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: flex-start;
|
||||||
|
flex-wrap: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: flex-end;
|
||||||
|
flex-wrap: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: center;
|
||||||
|
flex-wrap: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-align-input {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: auto;
|
||||||
|
max-width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-app {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rollable:hover, .rollable:focus {
|
||||||
|
color: #000;
|
||||||
|
text-shadow: 0 0 10px rgb(179, 7, 217);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styles limited to kidsonbrooms sheets */
|
||||||
|
.kids-on-brooms .item-form {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .sheet-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
flex: 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .sheet-header .profile-img {
|
||||||
|
flex: 0 0 100px;
|
||||||
|
height: 100px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .sheet-header .header-fields {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .sheet-header h1.charname {
|
||||||
|
height: 50px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 5px 0;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .sheet-header h1.charname input {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.kids-on-brooms div.editor-border {
|
||||||
|
border: 2px solid rgb(81, 81, 81);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .sheet-tabs {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .sheet-body,
|
||||||
|
.kids-on-brooms .sheet-body .tab,
|
||||||
|
.kids-on-brooms .sheet-body .tab .editor {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .tox .tox-editor-container {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .tox .tox-edit-area {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .selection-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .resource-label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-header {
|
||||||
|
height: 28px;
|
||||||
|
margin: 2px 0;
|
||||||
|
padding: 0;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border: 2px groove #eeede0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-header > * {
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-header .item-name {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-left: 5px;
|
||||||
|
text-align: left;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .item-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 2px;
|
||||||
|
border-bottom: 1px solid #c9c7b8;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .item .item-name {
|
||||||
|
flex: 2;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: left;
|
||||||
|
display: flex;
|
||||||
|
color: #191813;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .item .item-name h3, .kids-on-brooms .items-list .item .item-name h4 {
|
||||||
|
margin: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .item .item-name .item-image {
|
||||||
|
flex: 0 0 30px;
|
||||||
|
height: 30px;
|
||||||
|
background-size: 30px;
|
||||||
|
border: none;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .item-controls {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 100px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .item-controls a {
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .item-prop {
|
||||||
|
text-align: center;
|
||||||
|
border-left: 1px solid #c9c7b8;
|
||||||
|
border-right: 1px solid #c9c7b8;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .items-header {
|
||||||
|
height: 28px;
|
||||||
|
margin: 2px 0;
|
||||||
|
padding: 0;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border: 2px groove #eeede0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .items-header > * {
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .items-list .items-header .item-name {
|
||||||
|
padding-left: 5px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .item-formula {
|
||||||
|
flex: 0 0 200px;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .effects .item .effect-source,
|
||||||
|
.kids-on-brooms .effects .item .effect-duration,
|
||||||
|
.kids-on-brooms .effects .item .effect-controls {
|
||||||
|
text-align: center;
|
||||||
|
border-left: 1px solid #c9c7b8;
|
||||||
|
border-right: 1px solid #c9c7b8;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .effects .item .effect-controls {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.kids-on-brooms .kids-on-brooms input:focus,
|
||||||
|
.kids-on-brooms .kids-on-brooms textarea:focus,
|
||||||
|
.kids-on-brooms .kids-on-brooms select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #8102dd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=kidsonbrooms.css.map */
|
1
css/kidsonbrooms.css.map
Normal file
1
css/kidsonbrooms.css.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sourceRoot":"","sources":["../scss/kidsonbrooms.scss","../scss/global/_base.scss","../scss/global/_window.scss","../scss/utils/_typography.scss","../scss/global/_grid.scss","../scss/global/_flex.scss","../scss/utils/_mixins.scss","../scss/utils/_variables.scss","../scss/components/_forms.scss","../scss/utils/_colors.scss","../scss/components/_resource.scss","../scss/components/_items.scss","../scss/components/_effects.scss"],"names":[],"mappings":"AACQ;AASR;ACTQ;ACDR;EACE,aCDa;;;ADKb;EAEE;EACA;EACA;;;AERJ;EACE;EACA;EACA;EACA;;;AAKA;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;ACrBJ;ECkBE;EACA,gBDlBiB;ECmBjB,WDnByB;ECoBzB,iBAJkD;EAKlD,aALsE;EDftE;;;AAGF;ECaE;EACA,gBDbiB;ECcjB,WDd6B;ECe7B,iBAJkD;EAKlD,aALsE;EDVtE;;;AAGF;ECQE;EACA,gBDRiB;ECSjB,WDT2B;ECU3B,iBAJkD;EAKlD,aALsE;EDLtE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIF;ECVE;EACA,gBDUiB;ECTjB,WDS6B;ECR7B,iBAJkD;EAKlD,aALsE;EDatE;;;AAGF;ECfE;EACA,gBDeiB;ECdjB,WDc2B;ECb3B,iBAJkD;EAKlD,aALsE;EDkBtE;;;AAGF;ECpBE;EACA,gBDoBiB;ECnBjB,WDmByB;EClBzB,iBAJkD;EAKlD,aALsE;EDuBtE;;;AAIF;EACE;EACA;EACA;;;AJ7CF;EACE,aMEW;;;ANEX;EAEE;EACA;EACA;;;ADIJ;AQhBA;EACE,aLDa;;AKIf;EFeE;EACA,gBEfiB;EFgBjB,WEhBsB;EFiBtB,iBEjB4B;EFkB5B,aALsE;EEZtE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAKN;EACE;EACA;;AAGF;EACE;;AAGF;AAAA;AAAA;EAGE;;AAIA;EACE,YCnDM;;ADsDR;EACE;;AAKJ;EACE;EACA;EACA;EACA;;AEhEF;EACE;;ACAF;EACE;EACA;EACA;EACA;EACA;EACA,QJHc;EIId;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA,OFxBM;;AE2BN;EACE;EACA;EACA;;AAIF;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;EACA,OFvDG;;AEyDH;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAMN;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA,QJrGc;EIsGd;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAKJ;EACE;EACA;;ACzHA;AAAA;AAAA;EAGE;EACA;EACA;EACA;;AAGF;EACE;;AAKJ;AAAA;AAAA;EAGE;EACA,cHVmB","file":"kidsonbrooms.css"}
|
1
css/test.css.map
Normal file
1
css/test.css.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sourceRoot":"","sources":["../scss/kidsonbrooms.scss","../scss/global/_base.scss","../scss/global/_window.scss","../scss/utils/_typography.scss","../scss/global/_grid.scss","../scss/global/_flex.scss","../scss/utils/_mixins.scss","../scss/utils/_variables.scss","../scss/components/_forms.scss","../scss/utils/_colors.scss","../scss/components/_resource.scss","../scss/components/_items.scss","../scss/components/_effects.scss"],"names":[],"mappings":"AACQ;AASR;ACTQ;ACDR;EACE,aCDa;;;ADKb;EAEE;EACA;EACA;;;AERJ;EACE;EACA;EACA;EACA;;;AAKA;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAZF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;ACrBJ;ECkBE;EACA,gBDlBiB;ECmBjB,WDnByB;ECoBzB,iBAJkD;EAKlD,aALsE;EDftE;;;AAGF;ECaE;EACA,gBDbiB;ECcjB,WDd6B;ECe7B,iBAJkD;EAKlD,aALsE;EDVtE;;;AAGF;ECQE;EACA,gBDRiB;ECSjB,WDT2B;ECU3B,iBAJkD;EAKlD,aALsE;EDLtE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIF;ECVE;EACA,gBDUiB;ECTjB,WDS6B;ECR7B,iBAJkD;EAKlD,aALsE;EDatE;;;AAGF;ECfE;EACA,gBDeiB;ECdjB,WDc2B;ECb3B,iBAJkD;EAKlD,aALsE;EDkBtE;;;AAGF;ECpBE;EACA,gBDoBiB;ECnBjB,WDmByB;EClBzB,iBAJkD;EAKlD,aALsE;EDuBtE;;;AJtCF;EACE,aMEW;;;ANEX;EAEE;EACA;EACA;;;ADIJ;AQhBA;EACE,aLDa;;AKIf;EFeE;EACA,gBEfiB;EFgBjB,WEhBsB;EFiBtB,iBEjB4B;EFkB5B,aALsE;EEZtE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAKN;EACE;EACA;;AAGF;EACE;;AAGF;AAAA;AAAA;EAGE;;AAIA;EACE,YCnDM;;ADsDR;EACE;;AEvDJ;EACE;;ACAF;EACE;EACA;EACA;EACA;EACA;EACA,QJHc;EIId;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA,OFxBM;;AE2BN;EACE;EACA;EACA;;AAIF;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;EACA,OFvDG;;AEyDH;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAMN;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA,QJrGc;EIsGd;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAKJ;EACE;EACA;;ACzHA;AAAA;AAAA;EAGE;EACA;EACA;EACA;;AAGF;EACE;;AAKJ;AAAA;AAAA;EAGE;EACA,cHVmB","file":"test.css"}
|
9
lang/en.json
Normal file
9
lang/en.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
"KIDSONBROOMS.EffectCreate": "Create Effect",
|
||||||
|
"KIDSONBROOMS.EffectToggle": "Toggle Effect",
|
||||||
|
"KIDSONBROOMS.EffectEdit": "Edit Effect",
|
||||||
|
"KIDSONBROOMS.EffectDelete": "Delete Effect",
|
||||||
|
|
||||||
|
"KIDSONBROOMS.Add": "Add"
|
||||||
|
}
|
0
lib/some-lib/some-lib.css
Normal file
0
lib/some-lib/some-lib.css
Normal file
0
lib/some-lib/some-lib.min.js
vendored
Normal file
0
lib/some-lib/some-lib.min.js
vendored
Normal file
59
module/documents/actor.mjs
Normal file
59
module/documents/actor.mjs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* Extend the base Actor document by defining a custom roll data structure which is ideal for the Simple system.
|
||||||
|
* @extends {Actor}
|
||||||
|
*/
|
||||||
|
export class KidsOnBroomsActor extends Actor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override getRollData() that's supplied to rolls.
|
||||||
|
*/
|
||||||
|
getRollData() {
|
||||||
|
let data = { ...this.system };
|
||||||
|
|
||||||
|
// Wand bonuses
|
||||||
|
data.wandBonus = {
|
||||||
|
wood: this._getWandBonus(this.system.wand.wood),
|
||||||
|
core: this._getWandBonus(this.system.wand.core)
|
||||||
|
};
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getWandBonus(type) {
|
||||||
|
const bonuses = {
|
||||||
|
"Wisteria": { stat: "brains", bonus: 1 },
|
||||||
|
"Hawthorn": { stat: "brains", bonus: 1 },
|
||||||
|
"Pine": { stat: "brawn", bonus: 1 },
|
||||||
|
"Oak": { stat: "brawn", bonus: 1 },
|
||||||
|
"Crabapple": { stat: "fight", bonus: 1 },
|
||||||
|
"Dogwood": { stat: "fight", bonus: 1 },
|
||||||
|
"Birch": { stat: "flight", bonus: 1 },
|
||||||
|
"Bamboo": { stat: "flight", bonus: 1 },
|
||||||
|
"Ironwood": { stat: "grit", bonus: 1 },
|
||||||
|
"Maple": { stat: "grit", bonus: 1 },
|
||||||
|
"Lilac": { stat: "charm", bonus: 1 },
|
||||||
|
"Cherry": { stat: "charm", bonus: 1 },
|
||||||
|
"Parchment": { stat: "brains", bonus: 1 },
|
||||||
|
"Phoenix Feather": { stat: "brains", bonus: 1 },
|
||||||
|
"Owl Feather": { stat: "brains", bonus: 1 },
|
||||||
|
"Gorilla Fur": { stat: "brawn", bonus: 1 },
|
||||||
|
"Ogre’s Fingernail": { stat: "brawn", bonus: 1 },
|
||||||
|
"Hippo’s Tooth": { stat: "brawn", bonus: 1 },
|
||||||
|
"Dragon’s Heartstring": { stat: "fight", bonus: 1 },
|
||||||
|
"Wolf’s Tooth": { stat: "fight", bonus: 1 },
|
||||||
|
"Elk’s Antler": { stat: "fight", bonus: 1 },
|
||||||
|
"Hawk’s Feather": { stat: "flight", bonus: 1 },
|
||||||
|
"Bat’s Bone": { stat: "flight", bonus: 1 },
|
||||||
|
"Changeling’s Hair": { stat: "charm", bonus: 1 },
|
||||||
|
"Gold": { stat: "charm", bonus: 1 },
|
||||||
|
"Mirror": { stat: "charm", bonus: 1 },
|
||||||
|
"Steel": { stat: "grit", bonus: 1 },
|
||||||
|
"Diamond": { stat: "grit", bonus: 1 },
|
||||||
|
"Lion’s Mane": { stat: "grit", bonus: 1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
return bonuses[type] || { stat: "", bonus: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
7
module/helpers/config.mjs
Normal file
7
module/helpers/config.mjs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export const KIDSONBROOMS = {};
|
||||||
|
|
||||||
|
// Define constants here, such as:
|
||||||
|
KIDSONBROOMS.foobar = {
|
||||||
|
'bas': 'KIDSONBROOMS.bas',
|
||||||
|
'bar': 'KIDSONBROOMS.bar'
|
||||||
|
};
|
15
module/helpers/templates.mjs
Normal file
15
module/helpers/templates.mjs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Define a set of template paths to pre-load
|
||||||
|
* Pre-loaded templates are compiled and cached for fast access when rendering
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
export const preloadHandlebarsTemplates = async function() {
|
||||||
|
return loadTemplates([
|
||||||
|
|
||||||
|
// Actor partials.
|
||||||
|
"systems/kids-on-brooms/templates/actor/parts/actor-features.html",
|
||||||
|
"systems/kids-on-brooms/templates/actor/parts/actor-adversity.html",
|
||||||
|
"systems/kids-on-brooms/templates/actor/parts/actor-stats.html",
|
||||||
|
"systems/kids-on-brooms/templates/actor/parts/actor-npc-stats.html",
|
||||||
|
]);
|
||||||
|
};
|
333
module/kidsonbrooms.mjs
Normal file
333
module/kidsonbrooms.mjs
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
// Import document classes.
|
||||||
|
import { KidsOnBroomsActor } from "./documents/actor.mjs";
|
||||||
|
|
||||||
|
// Import sheet classes.
|
||||||
|
import { KidsOnBroomsActorSheet } from "./sheets/actor-sheet.mjs";
|
||||||
|
|
||||||
|
// Import helper/utility classes and constants.
|
||||||
|
import { preloadHandlebarsTemplates } from "./helpers/templates.mjs";
|
||||||
|
import { KIDSONBROOMS } from "./helpers/config.mjs";
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Init Hook */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
Hooks.once('init', async function() {
|
||||||
|
|
||||||
|
// Register the helper
|
||||||
|
Handlebars.registerHelper('capitalizeFirst', function(string) {
|
||||||
|
if (typeof string === 'string') {
|
||||||
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add utility classes and functions to the global game object so that they're more easily
|
||||||
|
// accessible in global contexts.
|
||||||
|
game.kidsonbrooms = {
|
||||||
|
KidsOnBroomsActor,
|
||||||
|
_onTakeAdversityToken: _onTakeAdversityToken, // Add the function to the global object
|
||||||
|
_onSpendAdversityTokens: _onSpendAdversityTokens // Add the function to the global object
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add custom constants for configuration.
|
||||||
|
CONFIG.KIDSONBROOMS = KIDSONBROOMS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an initiative formula for the system
|
||||||
|
* @type {String}
|
||||||
|
*/
|
||||||
|
CONFIG.Combat.initiative = {
|
||||||
|
formula: "1d20",
|
||||||
|
decimals: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define custom Document classes
|
||||||
|
CONFIG.Actor.documentClass = KidsOnBroomsActor;
|
||||||
|
|
||||||
|
// Register sheet application classes
|
||||||
|
Actors.unregisterSheet("core", ActorSheet);
|
||||||
|
Actors.registerSheet("kids-on-brooms", KidsOnBroomsActorSheet, { makeDefault: true });
|
||||||
|
|
||||||
|
//If there is a new chat message that is a roll we add the adversity token controls
|
||||||
|
Hooks.on("renderChatMessage", (message, html, messageData) => {
|
||||||
|
const adversityControls = html.find('.adversity-controls');
|
||||||
|
if (adversityControls.length > 0) {
|
||||||
|
const messageToEdit = adversityControls.data("roll-id");
|
||||||
|
// Bind event listeners for the controls
|
||||||
|
adversityControls.find(".take-adversity").off("click").click((event) => {
|
||||||
|
|
||||||
|
const actorId = event.currentTarget.dataset.actorId;
|
||||||
|
const actor = game.actors.get(actorId);
|
||||||
|
|
||||||
|
// Check if the current user owns the actor - They can not claim if they are not
|
||||||
|
if (!actor.testUserPermission(game.user, "owner")) {
|
||||||
|
ui.notifications.warn("You don't own this character and cannot take adversity tokens.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the token has already been claimed -- Contigency if the button somehow activates again
|
||||||
|
if (message.getFlag("kids-on-brooms", "tokenClaimed")) {
|
||||||
|
ui.notifications.warn("This adversity token has already been claimed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onTakeAdversityToken(event, actor);
|
||||||
|
if (game.user.isGM) {
|
||||||
|
let tokenControls = game.messages.get(message.id);
|
||||||
|
console.log(tokenControls);
|
||||||
|
// Update the chat message content with the button disabled and text changed
|
||||||
|
const updatedContent = tokenControls.content.replace(
|
||||||
|
`<button class="take-adversity" data-actor-id="${actor.id}">Take Adversity Token</button>`,
|
||||||
|
`<button class="take-adversity" data-actor-id="${actor.id}" disabled>Token claimed</button>`
|
||||||
|
);
|
||||||
|
console.log("Removing Button");
|
||||||
|
// Update the message content
|
||||||
|
tokenControls.update({ content: updatedContent });
|
||||||
|
// Set the flag on the chat message to indicate that the token has been claimed
|
||||||
|
tokenControls.setFlag("kids-on-brooms", "tokenClaimed", true);
|
||||||
|
} else {
|
||||||
|
// Emit a socket request to update the message to show that the token has been claimed
|
||||||
|
game.socket.emit('system.kids-on-brooms', {
|
||||||
|
action: "takeToken",
|
||||||
|
messageID: message.id,
|
||||||
|
actorID: actor.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("Send socket message for taking a token");
|
||||||
|
});
|
||||||
|
|
||||||
|
adversityControls.find(".spend-adversity").off("click").click((event) => {
|
||||||
|
//This entails a lot more, so I offloaded it to a new function
|
||||||
|
_onSpendAdversityTokens(event, messageToEdit);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Preload Handlebars templates.
|
||||||
|
return preloadHandlebarsTemplates();
|
||||||
|
});
|
||||||
|
|
||||||
|
/***
|
||||||
|
* This handles the incoming socket requests.
|
||||||
|
* If a player wants to spend tokens on another players roll the gm has to approve first
|
||||||
|
* if a player wants to claim a token we will update the message since they do not have the permissions
|
||||||
|
*/
|
||||||
|
Hooks.once('ready', function() {
|
||||||
|
game.socket.on('system.kids-on-brooms', async (data) => {
|
||||||
|
console.log("Socket data received:", data);
|
||||||
|
|
||||||
|
if (data.action === "spendTokens") {
|
||||||
|
console.log(`Request to spend tokens: ${data.tokensToSpend} tokens for ${data.rollActorId} by ${data.spendingActorId}`);
|
||||||
|
|
||||||
|
// Only handle the request if the GM is logged in
|
||||||
|
if (!game.user.isGM) {
|
||||||
|
console.log("Not GM, ignoring the token spend request.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actor who made the roll
|
||||||
|
const rollActor = game.actors.get(data.rollActorId);
|
||||||
|
// The actor who is spending tokens
|
||||||
|
const spendingActor = game.actors.get(data.spendingActorId);
|
||||||
|
|
||||||
|
//If these for some reason do not exist
|
||||||
|
if (!rollActor || !spendingActor) {
|
||||||
|
console.warn("Actor not found:", data.rollActorId, data.spendingActorId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a confirmation dialog for the GM
|
||||||
|
new Dialog({
|
||||||
|
title: "Approve Adversity Token Spending?",
|
||||||
|
content: `<p>${spendingActor.name} wants to spend ${data.tokenCost} adversity tokens on ${rollActor.name}'s roll to increase it by ${data.tokensToSpend}. Approve?</p>`,
|
||||||
|
buttons: {
|
||||||
|
yes: {
|
||||||
|
label: "Yes",
|
||||||
|
callback: async () => {
|
||||||
|
|
||||||
|
|
||||||
|
const currentTokens = spendingActor.system.adversityTokens || 0;
|
||||||
|
|
||||||
|
// Update the spending actor's adversity token count
|
||||||
|
await spendingActor.update({ "system.adversityTokens": currentTokens - data.tokenCost });
|
||||||
|
|
||||||
|
|
||||||
|
// Modify the roll message with the new total
|
||||||
|
await _updateRollMessage(data.rollMessageId, data.tokensToSpend, false);
|
||||||
|
|
||||||
|
console.log(`${spendingActor.name} spent ${data.tokensToSpend} tokens, updated roll total to ${roll.cumulativeTotal}`);
|
||||||
|
ui.notifications.info(`${spendingActor.name} successfully spent ${data.tokensToSpend} tokens.`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
no: {
|
||||||
|
label: "No",
|
||||||
|
callback: () => {
|
||||||
|
ui.notifications.info(`The GM denied ${spendingActor.name}'s request to spend tokens.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
default: "yes"
|
||||||
|
}).render(true);
|
||||||
|
} else if (data.action === "takeToken") {
|
||||||
|
// Only handle the request if the GM is logged in
|
||||||
|
if (!game.user.isGM) {
|
||||||
|
console.log("Not GM, ignoring the token spend request.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let tokenControls = game.messages.get(data.messageID);
|
||||||
|
console.log(tokenControls);
|
||||||
|
// Update the chat message content with the button disabled and text changed
|
||||||
|
const updatedContent = tokenControls.content.replace(
|
||||||
|
`<button class="take-adversity" data-actor-id="${data.actorID}">Take Adversity Token</button>`,
|
||||||
|
`<button class="take-adversity" data-actor-id="${data.actorID}" disabled>Token claimed</button>`
|
||||||
|
);
|
||||||
|
console.log("Removing Button");
|
||||||
|
// Update the message content
|
||||||
|
tokenControls.update({ content: updatedContent });
|
||||||
|
// Set the flag on the chat message to indicate that the token has been claimed
|
||||||
|
tokenControls.setFlag("kids-on-brooms", "tokenClaimed", true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/***
|
||||||
|
* This function adds the adversity token to the actor that made the roll and logs it
|
||||||
|
*
|
||||||
|
* @param {Event} e - The button click event
|
||||||
|
* @param {Actor} actor - The actor object that made the roll
|
||||||
|
*/
|
||||||
|
async function _onTakeAdversityToken(e, actor) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
|
||||||
|
// Get the chat message ID (assuming it's stored in the dataset)
|
||||||
|
const messageId = e.currentTarget.closest('.message').dataset.messageId;
|
||||||
|
const message = game.messages.get(messageId);
|
||||||
|
|
||||||
|
// Add an adversity token to the actor
|
||||||
|
const currentTokens = actor.system.adversityTokens || 0;
|
||||||
|
await actor.update({ "system.adversityTokens": currentTokens + 1 });
|
||||||
|
|
||||||
|
|
||||||
|
// Notify the user
|
||||||
|
ui.notifications.info(`You gained 1 adversity token.`);
|
||||||
|
console.log(`Gave one adversity token to ${actor.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* This function allows players to spend tokens to change a roll. This will automatically be calculated in their sheet
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async function _onSpendAdversityTokens(e, rollMessageId) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// The actor who made the roll
|
||||||
|
const rollActorId = e.currentTarget.dataset.actorId;
|
||||||
|
const rollActor = game.actors.get(rollActorId); //technically redundant since it is also done in the main hook, but perfomance is good enuff
|
||||||
|
|
||||||
|
// Get the actor of the player who is spending tokens
|
||||||
|
const spendingPlayerActor = game.actors.get(game.user.character?.id || game.actors.filter(actor => actor.testUserPermission(game.user, "owner"))[0]?.id);
|
||||||
|
|
||||||
|
if (!spendingPlayerActor) {
|
||||||
|
ui.notifications.warn("You don't control any actors.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the tokens to be spend from the input field
|
||||||
|
const tokenInput = $(e.currentTarget).closest('.adversity-controls').find('.token-input').val();
|
||||||
|
const tokensToSpend = parseInt(tokenInput, 10);
|
||||||
|
|
||||||
|
if (isNaN(tokensToSpend) || tokensToSpend <= 0) {
|
||||||
|
ui.notifications.warn("Please enter a valid number of tokens.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokenCost = tokensToSpend;
|
||||||
|
|
||||||
|
// If the player spending tokens is not the owner of the actor who rolled, they spend double
|
||||||
|
//(note, this is a rule of mine, I have disabled it by default)
|
||||||
|
if ((!spendingPlayerActor.testUserPermission(game.user, "owner") || spendingPlayerActor.id !== rollActorId) && false) {
|
||||||
|
tokenCost = tokensToSpend * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the spending actor has enough adversity tokens
|
||||||
|
if (spendingPlayerActor.system.adversityTokens < tokenCost) {
|
||||||
|
ui.notifications.warn(`You do not have enough adversity tokens.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the player owns the actor who made the roll
|
||||||
|
if (spendingPlayerActor.id === rollActorId) {
|
||||||
|
// The player owns the actor, so they can spend tokens directly without GM approval
|
||||||
|
const currentTokens = spendingPlayerActor.system.adversityTokens || 0;
|
||||||
|
|
||||||
|
// Deduct the tokens from the player
|
||||||
|
await spendingPlayerActor.update({ "system.adversityTokens": currentTokens - tokenCost });
|
||||||
|
|
||||||
|
// Modify the roll message with the new total
|
||||||
|
await _updateRollMessage(rollMessageId, tokensToSpend, true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// The player does not own the actor, so request GM approval to spend the tokens
|
||||||
|
console.log(`Requesting to spend ${tokensToSpend} tokens for ${rollActor.name} by ${spendingPlayerActor.name} (cost: ${tokenCost})`);
|
||||||
|
|
||||||
|
// Emit a socket request to spend tokens
|
||||||
|
game.socket.emit('system.kids-on-brooms', {
|
||||||
|
action: "spendTokens",
|
||||||
|
rollActorId: rollActorId,
|
||||||
|
spendingActorId: spendingPlayerActor.id, // Send the player's actor who is spending the tokens
|
||||||
|
tokensToSpend: tokensToSpend,
|
||||||
|
tokenCost: tokenCost,
|
||||||
|
rollMessageId: rollMessageId // Pass message ID to update the roll result
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.notifications.info(`Requested to spend ${tokenCost} tokens for ${rollActor.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to send a new message with the updated roll result
|
||||||
|
async function _updateRollMessage(rollMessageId, tokensToSpend, isPlayerOfActor) {
|
||||||
|
const message = game.messages.get(rollMessageId);
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
|
console.error("Message not found with ID:", rollMessageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve current tokens spent from flags, or initialize to 0 if not found
|
||||||
|
let cumulativeTokensSpent = message.getFlag("kids-on-brooms", "tokensSpent") || 0;
|
||||||
|
let newTotal = message.getFlag("kids-on-brooms", "newRollTotal") || message.rolls[0].total;
|
||||||
|
|
||||||
|
/*if(isPlayerOfActor)
|
||||||
|
{
|
||||||
|
// Add the new tokens to the cumulative total
|
||||||
|
cumulativeTokensSpent += tokensToSpend;
|
||||||
|
} else {
|
||||||
|
cumulativeTokensSpent += 2*tokensToSpend;
|
||||||
|
}*/
|
||||||
|
cumulativeTokensSpent += tokensToSpend;
|
||||||
|
newTotal += tokensToSpend;
|
||||||
|
await message.setFlag("kids-on-brooms", "newRollTotal", newTotal);
|
||||||
|
|
||||||
|
// Update the message's flags to store the cumulative tokens spent
|
||||||
|
await message.setFlag("kids-on-brooms", "tokensSpent", cumulativeTokensSpent);
|
||||||
|
let newContent = "";
|
||||||
|
if(cumulativeTokensSpent === 1)
|
||||||
|
{
|
||||||
|
newContent = `You have now spent ${cumulativeTokensSpent} token. The new roll total is ${newTotal}.`;
|
||||||
|
} else {
|
||||||
|
newContent = `You have now spent ${cumulativeTokensSpent} tokens. The new roll total is ${newTotal}.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new chat message to display the updated total
|
||||||
|
await ChatMessage.create({
|
||||||
|
speaker: ChatMessage.getSpeaker({ actor: message.speaker.actor }),
|
||||||
|
content: newContent,
|
||||||
|
type: CONST.CHAT_MESSAGE_STYLES.OTHER,
|
||||||
|
});
|
||||||
|
}
|
147
module/sheets/actor-sheet.mjs
Normal file
147
module/sheets/actor-sheet.mjs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
* Extend the basic ActorSheet with some very simple modifications
|
||||||
|
* @extends {ActorSheet}
|
||||||
|
*/
|
||||||
|
export class KidsOnBroomsActorSheet extends ActorSheet {
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static get defaultOptions()
|
||||||
|
{
|
||||||
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
classes: ["kids-on-brooms", "sheet", "actor"],
|
||||||
|
width: 800,
|
||||||
|
height: 800,
|
||||||
|
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "features" }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
get template()
|
||||||
|
{
|
||||||
|
console.log("template", this.actor)
|
||||||
|
return `systems/kids-on-brooms/templates/actor/actor-${this.actor.type}-sheet.html`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
/** @override */
|
||||||
|
async getData()
|
||||||
|
{
|
||||||
|
// Retrieve the data structure from the base sheet.
|
||||||
|
const context = super.getData();
|
||||||
|
|
||||||
|
// Use a safe clone of the actor data for further operations.
|
||||||
|
const actorData = this.document.toObject(false);
|
||||||
|
|
||||||
|
// Add the actor's data to context.data for easier access, as well as flags.
|
||||||
|
context.system = actorData.system;
|
||||||
|
context.flags = actorData.flags;
|
||||||
|
|
||||||
|
// Add roll data for TinyMCE editors.
|
||||||
|
context.rollData = context.actor.getRollData();
|
||||||
|
// Add roll data for TinyMCE editors.
|
||||||
|
context.rollData = context.actor.getRollData();
|
||||||
|
|
||||||
|
|
||||||
|
console.log(context);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
activateListeners(html)
|
||||||
|
{
|
||||||
|
super.activateListeners(html);
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
// Everything below here is only needed if the sheet is editable
|
||||||
|
if (!this.isEditable) return;
|
||||||
|
|
||||||
|
// Rollable abilities.
|
||||||
|
html.find('.rollable').click(this._onRoll.bind(this));
|
||||||
|
|
||||||
|
//If the user changes their wand material save that
|
||||||
|
html.find('select[name="system.wand.wood"]').change(event => {
|
||||||
|
const value = event.target.value;
|
||||||
|
this.actor.update({ "system.wand.wood": value });
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find('select[name="system.wand.core"]').change(event => {
|
||||||
|
const value = event.target.value;
|
||||||
|
this.actor.update({ "system.wand.core": value });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle clickable rolls.
|
||||||
|
* @param {Event} event The originating click event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _onRoll(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const element = e.currentTarget;
|
||||||
|
const dataset = element.dataset;
|
||||||
|
|
||||||
|
// Handle rolls that supply the formula directly
|
||||||
|
if (dataset.roll) {
|
||||||
|
let label = dataset.label ? `${dataset.label}` : '';
|
||||||
|
// Get the roll data and include wand bonuses
|
||||||
|
let rollData = this.actor.getRollData();
|
||||||
|
let totalBonus = 0;
|
||||||
|
console.log(dataset.roll);
|
||||||
|
// Apply wood bonus if it matches the stat being rolled for
|
||||||
|
if (rollData.wandBonus.wood.stat === dataset.key) {
|
||||||
|
totalBonus += rollData.wandBonus.wood.bonus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply core bonus if it matches the stat being rolled for AND it's different from the wood bonus
|
||||||
|
if (rollData.wandBonus.core.stat === dataset.key && rollData.wandBonus.core.stat !== rollData.wandBonus.wood.stat) {
|
||||||
|
totalBonus += rollData.wandBonus.core.bonus;
|
||||||
|
}
|
||||||
|
let rollFormula = dataset.roll + `+${totalBonus}`;
|
||||||
|
let roll = new Roll(rollFormula, rollData);
|
||||||
|
console.log(rollFormula);
|
||||||
|
console.log(rollData);
|
||||||
|
|
||||||
|
// Send the roll message to chat
|
||||||
|
const rollMessage = await roll.toMessage({
|
||||||
|
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
|
||||||
|
flavor: label,
|
||||||
|
rollMode: game.settings.get('core', 'rollMode'),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Now send the follow-up message with the adversity controls
|
||||||
|
await this._sendAdversityControlsMessage(this.actor.id, rollMessage.id);
|
||||||
|
|
||||||
|
return roll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//This just sends the buttons for the adversity token system
|
||||||
|
async _sendAdversityControlsMessage(actorId, rollMessageId) {
|
||||||
|
// Create the content for the adversity controls
|
||||||
|
const adversityControlsHtml = this._createAdversityControls(actorId, rollMessageId);
|
||||||
|
|
||||||
|
// Send the adversity controls as a follow-up message
|
||||||
|
const controlMessage = await ChatMessage.create({
|
||||||
|
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
|
||||||
|
content: adversityControlsHtml,
|
||||||
|
});
|
||||||
|
|
||||||
|
return controlMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create HTML content for adversity controls
|
||||||
|
_createAdversityControls(actorId, rollMessageId) {
|
||||||
|
return `
|
||||||
|
<div class="adversity-controls" data-roll-id="${rollMessageId}">
|
||||||
|
<button class="take-adversity" data-actor-id="${actorId}">Take Adversity Token</button>
|
||||||
|
<input type="number" class="token-input" value="1" min="1" />
|
||||||
|
<button class="spend-adversity" data-actor-id="${actorId}">Spend Adversity Tokens</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
27
package.json
Normal file
27
package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "kids-on-brooms",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"description": "CSS compiler for the Kids On Brooms system",
|
||||||
|
"scripts": {
|
||||||
|
"build": "gulp build",
|
||||||
|
"compile": "gulp css",
|
||||||
|
"watch": "gulp",
|
||||||
|
"gulp": "gulp"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"last 5 versions"
|
||||||
|
],
|
||||||
|
"author": "Josiah Bradbury, Joscha Maier",
|
||||||
|
"license": "MIT",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"gulp": "^5",
|
||||||
|
"gulp-autoprefixer": "^8",
|
||||||
|
"gulp-sass": "^5",
|
||||||
|
"gulp-sourcemaps": "^3",
|
||||||
|
"kids-on-brooms": "file:"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"sass": "^1.77.8"
|
||||||
|
}
|
||||||
|
}
|
1
readme.md
Normal file
1
readme.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
The Kids On Brooms System Implemented in FoundryVTT, reupload from https://github.com/Singularity-Lathe-VTT/kids-on-brooms
|
22
scss/components/_effects.scss
Normal file
22
scss/components/_effects.scss
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.effects .item {
|
||||||
|
.effect-source,
|
||||||
|
.effect-duration,
|
||||||
|
.effect-controls {
|
||||||
|
text-align: center;
|
||||||
|
border-left: 1px solid #c9c7b8;
|
||||||
|
border-right: 1px solid #c9c7b8;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect-controls {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _effects.scss
|
||||||
|
.kids-on-brooms input:focus,
|
||||||
|
.kids-on-brooms textarea:focus,
|
||||||
|
.kids-on-brooms select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: $focus-border-color;
|
||||||
|
}
|
66
scss/components/_forms.scss
Normal file
66
scss/components/_forms.scss
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
.item-form {
|
||||||
|
font-family: $font-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-header {
|
||||||
|
@include flexbox(row, wrap, flex-start); // Use a mixin for flexbox
|
||||||
|
flex: 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.profile-img {
|
||||||
|
flex: 0 0 100px;
|
||||||
|
height: 100px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-fields {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.charname {
|
||||||
|
height: 50px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 5px 0;
|
||||||
|
border-bottom: 0;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.editor-border {
|
||||||
|
border: 2px solid $primary-border-color; // Replace the hardcoded color with a variable
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-tabs {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-body,
|
||||||
|
.sheet-body .tab,
|
||||||
|
.sheet-body .tab .editor {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tox {
|
||||||
|
.tox-editor-container {
|
||||||
|
background: $c-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tox-edit-area {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flexbox container for each row
|
||||||
|
.selection-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between; // Ensures label stays left and input moves to the right
|
||||||
|
align-items: center; // Vertically center the label and input
|
||||||
|
margin-bottom: 10px; // Optional: Add some space between rows
|
||||||
|
}
|
124
scss/components/_items.scss
Normal file
124
scss/components/_items.scss
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// Section Header
|
||||||
|
.items-header {
|
||||||
|
height: 28px;
|
||||||
|
margin: 2px 0;
|
||||||
|
padding: 0;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border: $border-groove;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-left: 5px;
|
||||||
|
text-align: left;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Items Lists
|
||||||
|
.items-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
color: $c-tan;
|
||||||
|
|
||||||
|
// Child lists
|
||||||
|
.item-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Individual Item
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 2px; // Align with the header border
|
||||||
|
border-bottom: 1px solid $c-faint;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item name and image
|
||||||
|
.item-name {
|
||||||
|
flex: 2;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: left;
|
||||||
|
display: flex;
|
||||||
|
color: $c-dark;
|
||||||
|
|
||||||
|
h3, h4 {
|
||||||
|
margin: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-image {
|
||||||
|
flex: 0 0 30px;
|
||||||
|
height: 30px;
|
||||||
|
background-size: 30px;
|
||||||
|
border: none;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control Buttons
|
||||||
|
.item-controls {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 100px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item Properties (like stats or details)
|
||||||
|
.item-prop {
|
||||||
|
text-align: center;
|
||||||
|
border-left: 1px solid $c-faint;
|
||||||
|
border-right: 1px solid $c-faint;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section Header inside Items List
|
||||||
|
.items-list .items-header {
|
||||||
|
height: 28px;
|
||||||
|
margin: 2px 0;
|
||||||
|
padding: 0;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border: $border-groove;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
padding-left: 5px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional item formula block
|
||||||
|
.item-formula {
|
||||||
|
flex: 0 0 200px;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
3
scss/components/_resource.scss
Normal file
3
scss/components/_resource.scss
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.resource-label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
15
scss/global/_base.scss
Normal file
15
scss/global/_base.scss
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// _base.scss
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");
|
||||||
|
|
||||||
|
.window-app {
|
||||||
|
font-family: $font-stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rollable {
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: #000;
|
||||||
|
text-shadow: $hover-text-shadow;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
50
scss/global/_flex.scss
Normal file
50
scss/global/_flex.scss
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Flexbox Utility Classes
|
||||||
|
.flex-group-center {
|
||||||
|
@include flexbox(center, center);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-group-left {
|
||||||
|
@include flexbox(flex-start, center);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-group-right {
|
||||||
|
@include flexbox(flex-end, center);
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexshrink {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexlarge {
|
||||||
|
flex: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alignment Utility Classes
|
||||||
|
.align-left {
|
||||||
|
@include flexbox(flex-start, center);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
@include flexbox(flex-end, center);
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
@include flexbox(center, center);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only apply the right alignment to specific inputs with this class
|
||||||
|
.right-align-input {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: auto; // Push the input to the far right
|
||||||
|
max-width: 260px; // Optional: Control the width of the input field
|
||||||
|
}
|
25
scss/global/_grid.scss
Normal file
25
scss/global/_grid.scss
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// _grid.scss
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 2 through 12 {
|
||||||
|
// Create grid-start-* classes for offsets
|
||||||
|
.grid-start-#{$i} {
|
||||||
|
grid-column-start: #{$i};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create grid-span-* classes for column spans
|
||||||
|
.grid-span-#{$i} {
|
||||||
|
grid-column-end: span #{$i};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create grid-*col classes for grid columns
|
||||||
|
.grid-#{$i}col {
|
||||||
|
grid-column: span #{$i} / span #{$i};
|
||||||
|
grid-template-columns: repeat(#{$i}, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
12
scss/global/_window.scss
Normal file
12
scss/global/_window.scss
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.window-app {
|
||||||
|
font-family: $font-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rollable {
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: #000;
|
||||||
|
text-shadow: 0 0 10px rgb(146, 0, 225);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
23
scss/kidsonbrooms.scss
Normal file
23
scss/kidsonbrooms.scss
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Add custom fonts by visiting and search https://fonts.google.com
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
|
||||||
|
// This is the font used for the book, will not buy it but for refrence https://www.myfonts.com/collections/dreadful-font-aiyari?queryId=undefined&index=universal_search_data&objectIDs=5368854002
|
||||||
|
// This is the font used for text https://www.myfonts.com/products/lapidary-333-lapidary-333-434881?queryId=undefined&index=universal_search_data&objectIDs=5468003002
|
||||||
|
// Import utilities.
|
||||||
|
@import 'utils/variables';
|
||||||
|
@import 'utils/typography';
|
||||||
|
@import 'utils/colors';
|
||||||
|
@import 'utils/mixins';
|
||||||
|
|
||||||
|
/* Global styles */
|
||||||
|
@import 'global/window';
|
||||||
|
@import 'global/grid';
|
||||||
|
@import 'global/flex';
|
||||||
|
@import 'global/base';
|
||||||
|
|
||||||
|
/* Styles limited to kidsonbrooms sheets */
|
||||||
|
.kids-on-brooms {
|
||||||
|
@import 'components/forms';
|
||||||
|
@import 'components/resource';
|
||||||
|
@import 'components/items';
|
||||||
|
@import 'components/effects';
|
||||||
|
}
|
13
scss/utils/_colors.scss
Normal file
13
scss/utils/_colors.scss
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
$c-white: #fff;
|
||||||
|
$c-black: #000;
|
||||||
|
|
||||||
|
$c-dark: #191813;
|
||||||
|
$c-faint: #c9c7b8;
|
||||||
|
$c-beige: #b5b3a4;
|
||||||
|
$c-tan: #444;
|
||||||
|
|
||||||
|
$primary-border-color: rgb(81, 81, 81);
|
||||||
|
|
||||||
|
$focus-border-color: #8102dd;
|
||||||
|
$hover-text-shadow: 0 0 10px rgb(179, 7, 217);
|
||||||
|
$border-color: #ccc;
|
25
scss/utils/_mixins.scss
Normal file
25
scss/utils/_mixins.scss
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
@mixin element-invisible {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
clip: rect(0 0 0 0);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the mixin to accept 3 arguments: direction, wrap, and justify
|
||||||
|
@mixin flexbox($direction, $wrap: nowrap, $justify: flex-start, $align: stretch) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: $direction;
|
||||||
|
flex-wrap: $wrap;
|
||||||
|
justify-content: $justify;
|
||||||
|
align-items: $align;
|
||||||
|
}
|
2
scss/utils/_typography.scss
Normal file
2
scss/utils/_typography.scss
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
$font-primary: 'Roboto', sans-serif;
|
||||||
|
$font-secondary: 'Roboto', sans-serif;
|
10
scss/utils/_variables.scss
Normal file
10
scss/utils/_variables.scss
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
$padding-sm: 5px;
|
||||||
|
$padding-md: 10px;
|
||||||
|
$padding-lg: 20px;
|
||||||
|
|
||||||
|
$border-groove: 2px groove #eeede0;
|
||||||
|
|
||||||
|
$font-stack: "Roboto", sans-serif;
|
||||||
|
$padding: 10px;
|
||||||
|
$border-radius: 5px;
|
||||||
|
$flex-align: center;
|
24
system.json
Normal file
24
system.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"id": "kids-on-brooms",
|
||||||
|
"title": "Kids on Brooms System",
|
||||||
|
"description": "The Kids on Brooms system for FoundryVTT! - Deprecated",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"compatibility": {
|
||||||
|
"minimum": 12,
|
||||||
|
"verified": 12
|
||||||
|
},
|
||||||
|
"authors": [{
|
||||||
|
"name": "Josiah Bradbury, Joscha Maier"
|
||||||
|
}],
|
||||||
|
"esmodules": ["module/kidsonbrooms.mjs"],
|
||||||
|
"styles": ["css/kidsonbrooms.css"],
|
||||||
|
"socket": true,
|
||||||
|
"grid": {
|
||||||
|
"distance": 5,
|
||||||
|
"units": "ft"
|
||||||
|
},
|
||||||
|
"primaryTokenAttribute": "system.adversityTokens",
|
||||||
|
"url": "https://github.com/josmaier/KidsOnBroomsFoundryVTT",
|
||||||
|
"manifest": "https://github.com/josmaier/KidsOnBroomsFoundryVTT/blob/2fb5d0a11d1c9d78945c2508d5aa2cc7daf297e0/system.json",
|
||||||
|
"download": "https://github.com/josmaier/KidsOnBroomsFoundryVTT/blob/2fb5d0a11d1c9d78945c2508d5aa2cc7daf297e0/kids-on-brooms.zip"
|
||||||
|
}
|
68
template.json
Normal file
68
template.json
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"Actor": {
|
||||||
|
"types": ["character", "npc"],
|
||||||
|
"templates": {
|
||||||
|
"base": {
|
||||||
|
"stats": {
|
||||||
|
"fight": {
|
||||||
|
"value": "d4",
|
||||||
|
"stat": 0,
|
||||||
|
"magic": 0
|
||||||
|
},
|
||||||
|
"flight": {
|
||||||
|
"value": "d4",
|
||||||
|
"stat": 0,
|
||||||
|
"magic": 0
|
||||||
|
},
|
||||||
|
"brains": {
|
||||||
|
"value": "d4",
|
||||||
|
"stat": 0,
|
||||||
|
"magic": 0
|
||||||
|
},
|
||||||
|
"brawn": {
|
||||||
|
"value": "d4",
|
||||||
|
"stat": 0,
|
||||||
|
"magic": 0
|
||||||
|
},
|
||||||
|
"charm": {
|
||||||
|
"value": "d4",
|
||||||
|
"stat": 0,
|
||||||
|
"magic": 0
|
||||||
|
},
|
||||||
|
"grit": {
|
||||||
|
"value": "d4",
|
||||||
|
"stat": 0,
|
||||||
|
"magic": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"character": {
|
||||||
|
"templates": ["base"],
|
||||||
|
"trope": "",
|
||||||
|
"age": "",
|
||||||
|
"pronouns": "",
|
||||||
|
"fear": "",
|
||||||
|
"motivation": "",
|
||||||
|
"grade":"",
|
||||||
|
"broom": {
|
||||||
|
"name": "",
|
||||||
|
"look": "",
|
||||||
|
"mechanicalbenifit": ""
|
||||||
|
},
|
||||||
|
"wand": {
|
||||||
|
"wood": "",
|
||||||
|
"core": ""
|
||||||
|
},
|
||||||
|
"animalfamiliar":"",
|
||||||
|
"schoolbag": "",
|
||||||
|
"adversityTokens": 0,
|
||||||
|
"tropequestions": "",
|
||||||
|
"strengths": ""
|
||||||
|
},
|
||||||
|
"npc": {
|
||||||
|
"templates": ["base"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
templates/actor/actor-character-sheet.html
Normal file
63
templates/actor/actor-character-sheet.html
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<form class="{{cssClass}} {{actor.type}} flexcol" autocomplete="off">
|
||||||
|
{{!-- Sheet Header --}}
|
||||||
|
<header class="sheet-header">
|
||||||
|
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100"/>
|
||||||
|
<div class="header-fields">
|
||||||
|
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Name"/></h1>
|
||||||
|
|
||||||
|
<div class="resources grid">
|
||||||
|
<div class="resource flex-group-center">
|
||||||
|
<label for="system.trope" class="resource-label">Trope</label>
|
||||||
|
<div class="resource-content flexrow flex-center flex-between">
|
||||||
|
<input type="text" name="system.trope" value="{{system.trope}}" data-dtype="String"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{!-- Sheet Tab Navigation --}}
|
||||||
|
<nav class="sheet-tabs tabs" data-group="primary">
|
||||||
|
{{!-- Default tab is specified in actor-sheet.mjs --}}
|
||||||
|
<a class="item" data-tab="features">Features</a>
|
||||||
|
<a class="item" data-tab="schoolbag">School Bag</a>
|
||||||
|
<a class="item" data-tab="strengths">Strengths</a>
|
||||||
|
<a class="item" data-tab="trope">Trope Questions</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{{!-- Sheet Body --}}
|
||||||
|
<section class="sheet-body">
|
||||||
|
|
||||||
|
{{!-- Owned Features Tab --}}
|
||||||
|
<div class="tab features" data-group="primary" data-tab="features">
|
||||||
|
<section class="grid grid-3col">
|
||||||
|
<section class="main grid-span-2">
|
||||||
|
{{> "systems/kids-on-brooms/templates/actor/parts/actor-features.html"}}
|
||||||
|
{{> "systems/kids-on-brooms/templates/actor/parts/actor-adversity.html"}}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<aside class="sidebar">
|
||||||
|
{{> "systems/kids-on-brooms/templates/actor/parts/actor-stats.html"}}
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
{{!-- Schoolbag Tab --}}
|
||||||
|
<div class="tab features editor-border" data-group="primary" data-tab="schoolbag">
|
||||||
|
{{editor schoolbag target="system.schoolbag" engine="prosemirror" button=false collaborate=false editable=true}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Strengths Tab --}}
|
||||||
|
<div class="tab features editor-border" data-group="primary" data-tab="strengths">
|
||||||
|
{{editor strengths target="system.strengths" engine="prosemirror" button=false collaborate=false editable=true}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Trope Questions Tab --}}
|
||||||
|
<div class="tab features editor-border" data-group="primary" data-tab="trope">
|
||||||
|
{{editor tropequestions target="system.tropequestions" engine="prosemirror" button=false collaborate=false editable=true}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
|
35
templates/actor/actor-npc-sheet.html
Normal file
35
templates/actor/actor-npc-sheet.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<form class="{{cssClass}} {{actor.type}} flexcol" autocomplete="off">
|
||||||
|
|
||||||
|
{{!-- Sheet Header --}}
|
||||||
|
<header class="sheet-header">
|
||||||
|
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100"/>
|
||||||
|
<div class="header-fields">
|
||||||
|
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Name"/></h1>
|
||||||
|
|
||||||
|
<div class="resources grid">
|
||||||
|
<div class="resource flex-group-center">
|
||||||
|
<label for="system.trope" class="resource-label">Trope</label>
|
||||||
|
<div class="resource-content flexrow flex-center flex-between">
|
||||||
|
<input type="text" name="system.trope" value="{{system.trope}}" data-dtype="String"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{!-- Sheet Tab Navigation --}}
|
||||||
|
<nav class="sheet-tabs tabs" data-group="primary">
|
||||||
|
{{!-- Default tab is specified in actor-sheet.mjs --}}
|
||||||
|
<a class="item" data-tab="features">Features</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{{!-- Sheet Body --}}
|
||||||
|
<section class="sheet-body">
|
||||||
|
|
||||||
|
{{!-- Owned Features Tab --}}
|
||||||
|
<div class="tab features" data-group="primary" data-tab="features">
|
||||||
|
{{> "systems/kids-on-brooms/templates/actor/parts/actor-npc-stats.html"}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
|
11
templates/actor/parts/actor-adversity.html
Normal file
11
templates/actor/parts/actor-adversity.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<fieldset>
|
||||||
|
<legend>Adversity Tokens</legend>
|
||||||
|
<div class="resource flexcol" >
|
||||||
|
<label for="system.adversity" class="resource-label">
|
||||||
|
Begin the game with 3
|
||||||
|
adversity tokens. Add 1
|
||||||
|
each time you fail a roll.
|
||||||
|
</label>
|
||||||
|
<input type="number" name="system.adversityTokens" value="{{system.adversityTokens}}" data-dtype="Number"/>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
156
templates/actor/parts/actor-features.html
Normal file
156
templates/actor/parts/actor-features.html
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<section class="grid grid-3col">
|
||||||
|
|
||||||
|
<fieldset class="resource grid-span-3 flexcol">
|
||||||
|
<div class="resource flexrow">
|
||||||
|
<label for="system.age" class="resource-label">Age</label>
|
||||||
|
<input type="text" name="system.age" value="{{system.age}}" data-dtype="String"/>
|
||||||
|
</div>
|
||||||
|
<div class="resource grid-span-2 flexrow">
|
||||||
|
<label for="system.pronouns" class="resource-label">Pronouns</label>
|
||||||
|
<input type="text" name="system.pronouns" value="{{system.pronouns}}" data-dtype="String"/>
|
||||||
|
</div>
|
||||||
|
<div class="resource grid-span-3 flexrow">
|
||||||
|
<label for="system.fear" class="resource-label">Fear</label>
|
||||||
|
<input type="text" name="system.fear" value="{{system.fear}}" data-dtype="String"/>
|
||||||
|
</div>
|
||||||
|
<div class="resource grid-span-3 flexrow" >
|
||||||
|
<label for="system.motivation" class="resource-label">Motivation</label>
|
||||||
|
<input type="text" name="system.motivation" value="{{system.motivation}}" data-dtype="String"/>
|
||||||
|
</div>
|
||||||
|
<div class="resource grid-span-3 flexrow" >
|
||||||
|
<label for="system.description" class="resource-label">Description</label>
|
||||||
|
<input type="text" name="system.description" value="{{system.description}}" data-dtype="String"/>
|
||||||
|
</div>
|
||||||
|
<div class="resource grid-span-3 flexrow">
|
||||||
|
<label for="system.grade" class="resource-label">Grade</label>
|
||||||
|
<input type="text" name="system.grade" value="{{system.grade}}" data-dtype="String"/>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="resource grid-span-3 flexcol">
|
||||||
|
<legend>Your Broom</legend>
|
||||||
|
|
||||||
|
<!-- Broom Name Input with Dropdown -->
|
||||||
|
<div class="resource flexrow">
|
||||||
|
<label for="broom-name" class="resource-label">Name</label>
|
||||||
|
<input list="broomOptions" id="broom-name" name="system.broom.name"
|
||||||
|
value="{{system.broom.name}}" data-dtype="String" placeholder="Select or Enter Broom Name"
|
||||||
|
oninput="updateBroomDetails()" onblur="updateBroomDetails()">
|
||||||
|
<datalist id="broomOptions">
|
||||||
|
<option value="The Blocker's Broom" data-look="Defensive" data-mechanical="Gain the Guardian Strength"></option>
|
||||||
|
<option value="Bolting 4000" data-look="Fast" data-mechanical="+1 to Flight checks"></option>
|
||||||
|
<option value="The Bruiser" data-look="Intense" data-mechanical="+1 to Fight checks"></option>
|
||||||
|
<option value="Cunning Captain’s Cruiser" data-look="Natural Leader" data-mechanical="Treat Snap Decisions as Planned Actions unless facing fear"></option>
|
||||||
|
<option value="Daredevil’s Duster" data-look="Flashy" data-mechanical="+3 to Charm checks when performing a stunt"></option>
|
||||||
|
<option value="The Daring Dodger 3000" data-look="Ambitious" data-mechanical="Each Adversity Token adds +2 to your roll instead of +1"></option>
|
||||||
|
<option value="Heartwood’s Helper" data-look="Outgoing" data-mechanical="Each successful check grants an ally one Adversity Token"></option>
|
||||||
|
<option value="Mapmaker’s Friend" data-look="Level-Headed" data-mechanical="Cannot get lost if you know the area"></option>
|
||||||
|
<option value="The Mastermind’s Sweeper" data-look="Confident" data-mechanical="+1 to Brains checks"></option>
|
||||||
|
<option value="The Strong Sweep 2500" data-look="Strong" data-mechanical="+1 to Brawn checks"></option>
|
||||||
|
<option value="The Suave Sweeper" data-look="Trustworthy" data-mechanical="+1 to Charm checks"></option>
|
||||||
|
<option value="The Tough Break" data-look="Tough" data-mechanical="+1 to Grit checks"></option>
|
||||||
|
<option value="Valiance 2400" data-look="Brave" data-mechanical="May ignore your fears"></option>
|
||||||
|
<option value="Weasel’s Whisk" data-look="Sneaky" data-mechanical="Gain the Unassuming Strength"></option>
|
||||||
|
</datalist>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Broom Look -->
|
||||||
|
<div class="resource flexrow">
|
||||||
|
<label for="broom-look" class="resource-label">Look</label>
|
||||||
|
<input type="text" id="broom-look" name="system.broom.look"
|
||||||
|
value="{{system.broom.look}}" data-dtype="String"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mechanical Benefit as Textarea -->
|
||||||
|
<div class="resource flexrow">
|
||||||
|
<label for="broom-mechanical" class="resource-label">Mechanical Benefit</label>
|
||||||
|
<textarea id="broom-mechanical" name="system.broom.mechanicalbenefit"
|
||||||
|
data-dtype="String" rows="3" style="resize:none;"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function updateBroomDetails() {
|
||||||
|
// Use a short delay to allow browser to properly handle the datalist input
|
||||||
|
setTimeout(function() {
|
||||||
|
const broomNameInput = document.getElementById("broom-name").value.trim();
|
||||||
|
const broomOptions = document.querySelectorAll("#broomOptions option");
|
||||||
|
|
||||||
|
let selectedLook = "";
|
||||||
|
let selectedMechanical = "";
|
||||||
|
|
||||||
|
// Loop through the datalist options to find a matching broom name
|
||||||
|
broomOptions.forEach(option => {
|
||||||
|
if (option.value.toLowerCase() === broomNameInput.toLowerCase()) {
|
||||||
|
selectedLook = option.getAttribute("data-look");
|
||||||
|
selectedMechanical = option.getAttribute("data-mechanical");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the look and mechanical benefit fields if a predefined broom is selected
|
||||||
|
document.getElementById("broom-look").value = selectedLook || "";
|
||||||
|
document.getElementById("broom-mechanical").value = selectedMechanical || "";
|
||||||
|
}, 100); // Delay of 100 milliseconds
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<fieldset class="resource grid-span-3 flexcol">
|
||||||
|
<legend>Wand Selection</legend>
|
||||||
|
|
||||||
|
<!-- Wood Selection -->
|
||||||
|
<div class="resource-flexrow">
|
||||||
|
<label for="system.wand.wood" class="resource-label">Wood Type</label>
|
||||||
|
<input list="WoodOptions" id="wandWoodChoice" name="system.wand.wood" value="{{system.wand.wood}}" placeholder="Select Wood type"
|
||||||
|
oninput="updateWandWoodDetails()" onblur="updateWandWoodDetails()">
|
||||||
|
<datalist id="WoodOptions">
|
||||||
|
<option value="">Select Wood</option>
|
||||||
|
<option value="Wisteria">(Brains)</option>
|
||||||
|
<option value="Hawthorn">(Brains)</option>
|
||||||
|
<option value="Pine">(Brawn)</option>
|
||||||
|
<option value="Oak">(Brawn)</option>
|
||||||
|
<option value="Crabapple">(Fight)</option>
|
||||||
|
<option value="Dogwood">(Fight)</option>
|
||||||
|
<option value="Birch">(Flight)</option>
|
||||||
|
<option value="Bamboo">(Flight)</option>
|
||||||
|
<option value="Ironwood">(Grit)</option>
|
||||||
|
<option value="Maple">(Grit)</option>
|
||||||
|
<option value="Lilac">(Charm)</option>
|
||||||
|
<option value="Cherry">(Charm)</option>
|
||||||
|
</datalist>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Core Selection -->
|
||||||
|
<div class="resource-flexrow">
|
||||||
|
<label for="system.wand.core" class="resource-label">Core Type</label>
|
||||||
|
<input list="CoreOptions" id="wandCoreChoice" name="system.wand.core" value="{{system.wand.core}}" placeholder="Select Core type"
|
||||||
|
oninput="updateWandCoreDetails()" onblur="updateWandCoreDetails()">
|
||||||
|
<datalist id="CoreOptions">
|
||||||
|
<option value="">Select Core</option>
|
||||||
|
<option value="Parchment">(Brains)</option>
|
||||||
|
<option value="Phoenix Feather">(Brains)</option>
|
||||||
|
<option value="Owl Feather">(Brains)</option>
|
||||||
|
<option value="Gorilla Fur">(Brawn)</option>
|
||||||
|
<option value="Ogre’s Fingernail">(Brawn)</option>
|
||||||
|
<option value="Hippo’s Tooth">(Brawn)</option>
|
||||||
|
<option value="Dragon’s Heartstring">(Fight)</option>
|
||||||
|
<option value="Wolf’s Tooth">(Fight)</option>
|
||||||
|
<option value="Elk’s Antler">(Fight)</option>
|
||||||
|
<option value="Hawk’s Feather">(Flight)</option>
|
||||||
|
<option value="Bat’s Bone">(Flight)</option>
|
||||||
|
<option value="Changeling’s Hair">(Charm)</option>
|
||||||
|
<option value="Gold">(Charm)</option>
|
||||||
|
<option value="Mirror">(Charm)</option>
|
||||||
|
<option value="Steel">(Grit)</option>
|
||||||
|
<option value="Diamond">(Grit)</option>
|
||||||
|
<option value="Lion’s Mane">(Grit)</option>
|
||||||
|
</datalist>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="resource grid-span-3 flexcol">
|
||||||
|
<legend>Animal Familiar</legend>
|
||||||
|
<div class="resource grid-span-3 flexrow">
|
||||||
|
<label for="system.animalfamiliar" class="resource-label">Animal Familiar</label>
|
||||||
|
<input type="text" name="system.animalfamiliar" value="{{system.animalfamiliar}}" data-dtype="String"/>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</section>
|
29
templates/actor/parts/actor-npc-stats.html
Normal file
29
templates/actor/parts/actor-npc-stats.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<section class="flexcol">
|
||||||
|
{{#each system.stats as |stat key|}}
|
||||||
|
<Fieldset class="grid grid-5col">
|
||||||
|
<legend>{{key}}</legend>
|
||||||
|
<select name="system.stats.{{key}}.value">
|
||||||
|
{{#select stat.value}}
|
||||||
|
<option value="d20">d20</option>
|
||||||
|
<option value="d12">d12</option>
|
||||||
|
<option value="d10">d10</option>
|
||||||
|
<option value="d8">d8</option>
|
||||||
|
<option value="d6">d6</option>
|
||||||
|
<option value="d4">d4</option>
|
||||||
|
{{/select}}
|
||||||
|
</select>
|
||||||
|
<div class="flexrow grid-span-4">
|
||||||
|
<Fieldset class="flexrow">
|
||||||
|
<legend>Stat</legend>
|
||||||
|
<span class="ability-mod rollable" data-roll="{{stat.value}}+{{stat.stat}}" data-label="Stat Roll for {{key}}"><i class="fas fa-dice-d20"></i></span>
|
||||||
|
<input type="text" name="system.stats.{{key}}.stat" value="{{stat.stat}}" data-dtype="String"/>
|
||||||
|
</Fieldset>
|
||||||
|
<Fieldset class="flexrow">
|
||||||
|
<legend>Magic</legend>
|
||||||
|
<span class="ability-mod rollable" data-roll="{{stat.value}}+{{stat.magic}}" data-label="Magic Roll for {{key}}"><i class="fas fa-dice-d20"></i></span>
|
||||||
|
<input type="text" name="system.stats.{{key}}.magic" value="{{stat.magic}}" data-dtype="String"/>
|
||||||
|
</Fieldset>
|
||||||
|
</div>
|
||||||
|
</Fieldset>
|
||||||
|
{{/each}}
|
||||||
|
</section>
|
37
templates/actor/parts/actor-stats.html
Normal file
37
templates/actor/parts/actor-stats.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<section class="flexcol">
|
||||||
|
{{#each system.stats as |stat key|}}
|
||||||
|
<Fieldset class="flexrow">
|
||||||
|
<legend>{{capitalizeFirst key}}</legend>
|
||||||
|
<div class="flexrow flex-group-center">
|
||||||
|
<!-- Die type dropdown -->
|
||||||
|
<select name="system.stats.{{key}}.value">
|
||||||
|
<option value="d20" {{#if (eq stat.value 'd20')}}selected{{/if}}>Superb</option>
|
||||||
|
<option value="d12" {{#if (eq stat.value 'd12')}}selected{{/if}}>Impressive</option>
|
||||||
|
<option value="d10" {{#if (eq stat.value 'd10')}}selected{{/if}}>Above Average</option>
|
||||||
|
<option value="d8" {{#if (eq stat.value 'd8')}}selected{{/if}}>Below Average</option>
|
||||||
|
<option value="d6" {{#if (eq stat.value 'd6')}}selected{{/if}}>Bad</option>
|
||||||
|
<option value="d4" {{#if (eq stat.value 'd4')}}selected{{/if}}>Terrible</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- Stat rolling and input -->
|
||||||
|
<Fieldset class="flexrow flex-group-center">
|
||||||
|
<legend>Stat</legend>
|
||||||
|
<span class="ability-mod rollable" data-roll="1{{stat.value}}x+{{stat.stat}}" data-label="Stat Roll for {{key}}" data-key="{{key}}">
|
||||||
|
<i class="fas fa-dice-d20"></i>
|
||||||
|
</span>
|
||||||
|
<input type="number" name="system.stats.{{key}}.stat" value="{{stat.stat}}" data-dtype="Number"/>
|
||||||
|
</Fieldset>
|
||||||
|
|
||||||
|
<!-- Magic rolling and input -->
|
||||||
|
<Fieldset class="flexrow flex-group-center">
|
||||||
|
<legend>Magic</legend>
|
||||||
|
<span class="ability-mod rollable" data-roll="1{{stat.value}}x+1d4+{{stat.stat}}" data-label="Magic Roll for {{key}}" data-key="{{key}}">
|
||||||
|
<i class="fas fa-dice-d20"></i>
|
||||||
|
</span>
|
||||||
|
</Fieldset>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</Fieldset>
|
||||||
|
{{/each}}
|
||||||
|
</section>
|
Loading…
Reference in New Issue
Block a user