ENH: Rolls!
parent
7c8ca4ae1f
commit
7be97146c2
|
@ -71,6 +71,8 @@
|
|||
"OSE.SpellDC": "Spell DC",
|
||||
"OSE.SpellDCShort": "DC",
|
||||
"OSE.Thac0": "THAC0",
|
||||
"OSE.ABShort": "AB",
|
||||
"OSE.AB": "Attack Bonus",
|
||||
"OSE.MeleeShort": "MEL",
|
||||
"OSE.Melee": "Melee",
|
||||
"OSE.MeleeBonus": "Melee Bonus",
|
||||
|
|
|
@ -16,11 +16,18 @@ export class OseActor extends Actor {
|
|||
const label = game.i18n.localize(`OSE.saves.${save}.long`);
|
||||
const rollParts = ['1d20'];
|
||||
|
||||
const data = {...this.data, ...{
|
||||
rollData : {
|
||||
type: 'Save',
|
||||
stat: save
|
||||
}
|
||||
}};
|
||||
|
||||
// Roll and return
|
||||
return OseDice.Roll({
|
||||
event: options.event,
|
||||
parts: rollParts,
|
||||
data: this.data,
|
||||
data: data,
|
||||
speaker: ChatMessage.getSpeaker({ actor: this }),
|
||||
flavor: `${label} ${game.i18n.localize('OSE.SavingThrow')}`,
|
||||
title: `${label} ${game.i18n.localize('OSE.SavingThrow')}`,
|
||||
|
@ -31,11 +38,18 @@ export class OseActor extends Actor {
|
|||
const label = game.i18n.localize(`OSE.scores.${score}.long`);
|
||||
const rollParts = ['1d20'];
|
||||
|
||||
const data = {...this.data, ...{
|
||||
rollData : {
|
||||
type: 'Check',
|
||||
stat: score
|
||||
}
|
||||
}};
|
||||
|
||||
// Roll and return
|
||||
return OseDice.Roll({
|
||||
event: options.event,
|
||||
parts: rollParts,
|
||||
data: this.data,
|
||||
data: data,
|
||||
speaker: ChatMessage.getSpeaker({ actor: this }),
|
||||
flavor: `${label} ${game.i18n.localize('OSE.AbilityCheck')}`,
|
||||
title: `${label} ${game.i18n.localize('OSE.AbilityCheck')}`,
|
||||
|
@ -62,12 +76,23 @@ export class OseActor extends Actor {
|
|||
this.data.data.thac0.mod.melee.toString()
|
||||
);
|
||||
}
|
||||
if (game.settings.get('ose', 'ascendingAC')) {
|
||||
rollParts.push('+', this.data.data.thac0.bba.toString());
|
||||
}
|
||||
|
||||
const data = {...this.data, ...{
|
||||
rollData : {
|
||||
type: 'Attack',
|
||||
stat: attack,
|
||||
mods: mods
|
||||
}
|
||||
}};
|
||||
|
||||
// Roll and return
|
||||
return OseDice.Roll({
|
||||
event: options.event,
|
||||
parts: rollParts,
|
||||
data: this.data,
|
||||
data: data,
|
||||
speaker: ChatMessage.getSpeaker({ actor: this }),
|
||||
flavor: `${label} ${game.i18n.localize('OSE.Attack')}`,
|
||||
title: `${label} ${game.i18n.localize('OSE.Attack')}`,
|
||||
|
|
|
@ -1,4 +1,111 @@
|
|||
export class OseDice {
|
||||
static digestResult(data, roll) {
|
||||
let details = "";
|
||||
|
||||
// ATTACKS
|
||||
let die = roll.parts[0].total;
|
||||
if (data.rollData.type == "Attack") {
|
||||
if (game.settings.get("ose", "ascendingAC")) {
|
||||
let bba = data.data.thac0.bba;
|
||||
bba +=
|
||||
data.rollData.stat == "Melee"
|
||||
? data.data.thac0.mod.melee + data.rollData.mods.str
|
||||
: data.data.thac0.mod.missile + data.rollData.mods.dex;
|
||||
details = `<div class='roll-result roll-fail'><b>Failure</b> (${bba})</div>`;
|
||||
if (die == 1) {
|
||||
return details;
|
||||
}
|
||||
details = `<div class='roll-result'><b>Hits AC ${roll.total}</b> (${bba})</div>`;
|
||||
} else {
|
||||
// B/X Historic THAC0 Calculation
|
||||
let thac = data.data.thac0.value;
|
||||
thac -=
|
||||
data.rollData.stat == "Melee"
|
||||
? data.data.thac0.mod.melee + data.rollData.mods.str
|
||||
: data.data.thac0.mod.missile + data.rollData.mods.dex;
|
||||
|
||||
details = `<div class='roll-result roll-fail'><b>Failure</b> (${thac})</div>`;
|
||||
if (thac - roll.total > 9) {
|
||||
return details;
|
||||
}
|
||||
details = `<div class='roll-result'><b>Hits AC ${Math.clamped(thac - roll.total,-3,9)}</b> (${thac})</div>`;
|
||||
}
|
||||
} else if (data.rollData.type == "Save") {
|
||||
// SAVING THROWS
|
||||
let sv = data.data.saves[data.rollData.stat].value;
|
||||
if (roll.total >= sv) {
|
||||
details = `<div class='roll-result roll-success'><b>Success!</b> (${sv})</div>`;
|
||||
} else {
|
||||
details = `<div class='roll-result roll-fail'><b>Failure</b> (${sv})</div>`;
|
||||
}
|
||||
} else if (data.rollData.type == "Check") {
|
||||
// SCORE CHECKS
|
||||
let sc = data.data.scores[data.rollData.stat].value;
|
||||
if (die == 1 || (roll.total <= sc && die < 20)) {
|
||||
details = `<div class='roll-result roll-success'><b>Success!</b> (${sc})</div>`;
|
||||
} else {
|
||||
details = `<div class='roll-result roll-fail'><b>Failure</b> (${sc})</div>`;
|
||||
}
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
static async sendRoll(
|
||||
parts = [],
|
||||
data = {},
|
||||
title = null,
|
||||
flavor = null,
|
||||
speaker = null,
|
||||
form = null
|
||||
) {
|
||||
const template = "systems/ose/templates/chat/roll-attack.html";
|
||||
|
||||
let chatData = {
|
||||
user: game.user._id,
|
||||
speaker: speaker,
|
||||
};
|
||||
|
||||
let templateData = {
|
||||
title: title,
|
||||
flavor: flavor,
|
||||
data: data,
|
||||
};
|
||||
|
||||
// Optionally include a situational bonus
|
||||
if (form !== null) data["bonus"] = form.bonus.value;
|
||||
if (data["bonus"]) parts.push(data["bonus"]);
|
||||
|
||||
const roll = new Roll(parts.join(""), data).roll();
|
||||
|
||||
// Convert the roll to a chat message and return the roll
|
||||
let rollMode = game.settings.get("core", "rollMode");
|
||||
rollMode = form ? form.rollMode.value : rollMode;
|
||||
|
||||
templateData.details = OseDice.digestResult(data, roll);
|
||||
roll.render().then((r) => {
|
||||
templateData.rollOSE = r;
|
||||
renderTemplate(template, templateData).then((content) => {
|
||||
chatData.content = content;
|
||||
chatData.sound = CONFIG.sounds.dice;
|
||||
if (game.dice3d) {
|
||||
game.dice3d
|
||||
.showForRoll(
|
||||
roll,
|
||||
game.user,
|
||||
true,
|
||||
chatData.whisper,
|
||||
chatData.blind
|
||||
)
|
||||
.then((displayed) => ChatMessage.create(chatData));
|
||||
} else {
|
||||
ChatMessage.create(chatData);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return roll;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
static async Roll({
|
||||
parts = [],
|
||||
|
@ -12,32 +119,10 @@ export class OseDice {
|
|||
} = {}) {
|
||||
let rollMode = game.settings.get("core", "rollMode");
|
||||
let rolled = false;
|
||||
let filtered = parts.filter(function (el) {
|
||||
return el != "" && el;
|
||||
});
|
||||
|
||||
const _roll = (form = null, raise = false) => {
|
||||
// Optionally include a situational bonus
|
||||
if (form !== null) data["bonus"] = form.bonus.value;
|
||||
if (data["bonus"]) filtered.push(data["bonus"]);
|
||||
|
||||
const roll = new Roll(filtered.join(""), data).roll();
|
||||
// Convert the roll to a chat message and return the roll
|
||||
rollMode = form ? form.rollMode.value : rollMode;
|
||||
roll.toMessage(
|
||||
{
|
||||
speaker: speaker,
|
||||
flavor: flavor,
|
||||
},
|
||||
{ rollMode }
|
||||
);
|
||||
rolled = true;
|
||||
return roll;
|
||||
};
|
||||
|
||||
const template = "systems/ose/templates/chat/roll-dialog.html";
|
||||
let dialogData = {
|
||||
formula: filtered.join(" "),
|
||||
formula: parts.join(" "),
|
||||
data: data,
|
||||
rollMode: rollMode,
|
||||
rollModes: CONFIG.Dice.rollModes,
|
||||
|
@ -48,7 +133,14 @@ export class OseDice {
|
|||
label: game.i18n.localize("OSE.Roll"),
|
||||
icon: '<i class="fas fa-dice-d20"></i>',
|
||||
callback: (html) => {
|
||||
roll = _roll(html[0].children[0]);
|
||||
roll = OseDice.sendRoll(
|
||||
parts,
|
||||
data,
|
||||
title,
|
||||
flavor,
|
||||
speaker,
|
||||
html[0].children[0]
|
||||
);
|
||||
},
|
||||
},
|
||||
cancel: {
|
||||
|
@ -70,7 +162,7 @@ export class OseDice {
|
|||
buttons: buttons,
|
||||
default: "ok",
|
||||
close: () => {
|
||||
resolve(rolled ? roll : false)
|
||||
resolve(rolled ? roll : false);
|
||||
},
|
||||
}).render(true);
|
||||
});
|
||||
|
|
|
@ -188,7 +188,7 @@
|
|||
text-indent: 8px;
|
||||
}
|
||||
font-weight: 300;
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
background: $darkBackground;
|
||||
color: white;
|
||||
input {
|
||||
|
|
|
@ -1,97 +1,124 @@
|
|||
|
||||
.ose.chat-card {
|
||||
font-style: normal;
|
||||
font-size: 12px;
|
||||
|
||||
.card-header {
|
||||
padding: 3px 0;
|
||||
border-top: 2px groove #FFF;
|
||||
border-bottom: 2px groove #FFF;
|
||||
|
||||
img {
|
||||
flex: 0 0 36px;
|
||||
margin-right: 5px;
|
||||
.ose.chat-block {
|
||||
margin: 0;
|
||||
.chat-title {
|
||||
background: $darkBackground;
|
||||
border: 1px solid black;
|
||||
border-radius: 3px;
|
||||
color: white;
|
||||
padding: 2px;
|
||||
box-shadow: 0 0 2px #FFF inset;
|
||||
text-align: center;
|
||||
margin: 4px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
.chat-details {
|
||||
padding: 4px;
|
||||
font-size: 13px;
|
||||
.roll-result {
|
||||
text-align: center;
|
||||
&.roll-success {
|
||||
color: #18520b;
|
||||
}
|
||||
|
||||
h3 {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
line-height: 36px;
|
||||
color: $colorOlive;
|
||||
&:hover {
|
||||
color: #111;
|
||||
text-shadow: 0 0 10px red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-content {
|
||||
margin: 5px 0;
|
||||
|
||||
h3 {
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
> * {
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
}
|
||||
|
||||
.card-buttons {
|
||||
margin: 5px 0;
|
||||
|
||||
span {
|
||||
display: block;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
border: 1px solid $colorTan;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 12px;
|
||||
height: 24px;
|
||||
line-height: 20px;
|
||||
margin: 2px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: 3px 0 0;
|
||||
border-top: 2px groove #FFF;
|
||||
|
||||
span {
|
||||
border-right: 2px groove #FFF;
|
||||
padding: 0 5px 0 0;
|
||||
font-size: 10px;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
padding-right: 0;
|
||||
}
|
||||
&.roll-fail {
|
||||
color: #aa0200;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dice-roll .dice-total {
|
||||
&.success {
|
||||
color: inherit;
|
||||
background: #c7d0c0;
|
||||
border: 1px solid #006c00;
|
||||
}
|
||||
|
||||
.ose.chat-card {
|
||||
font-style: normal;
|
||||
font-size: 12px;
|
||||
|
||||
.card-header {
|
||||
padding: 3px 0;
|
||||
border-top: 2px groove #fff;
|
||||
border-bottom: 2px groove #fff;
|
||||
|
||||
img {
|
||||
flex: 0 0 36px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
&.failure {
|
||||
color: inherit;
|
||||
background: #ffdddd;
|
||||
border: 1px solid #6e0000;
|
||||
|
||||
h3 {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
line-height: 36px;
|
||||
color: $colorOlive;
|
||||
&:hover {
|
||||
color: #111;
|
||||
text-shadow: 0 0 10px red;
|
||||
}
|
||||
}
|
||||
&.critical {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
margin: 5px 0;
|
||||
|
||||
h3 {
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
&.fumble {
|
||||
color: red;
|
||||
|
||||
> * {
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-buttons {
|
||||
margin: 5px 0;
|
||||
|
||||
span {
|
||||
display: block;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
border: 1px solid $colorTan;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 12px;
|
||||
height: 24px;
|
||||
line-height: 20px;
|
||||
margin: 2px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: 3px 0 0;
|
||||
border-top: 2px groove #fff;
|
||||
|
||||
span {
|
||||
border-right: 2px groove #fff;
|
||||
padding: 0 5px 0 0;
|
||||
font-size: 10px;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dice-roll .dice-total {
|
||||
&.success {
|
||||
color: inherit;
|
||||
background: #c7d0c0;
|
||||
border: 1px solid #006c00;
|
||||
}
|
||||
&.failure {
|
||||
color: inherit;
|
||||
background: #ffdddd;
|
||||
border: 1px solid #6e0000;
|
||||
}
|
||||
&.critical {
|
||||
color: green;
|
||||
}
|
||||
&.fumble {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,4 +37,4 @@
|
|||
.resizable {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@
|
|||
},
|
||||
"thac0": {
|
||||
"value": 19,
|
||||
"bba": 0,
|
||||
"mod": {
|
||||
"missile": 0,
|
||||
"melee": 0
|
||||
|
|
|
@ -114,15 +114,32 @@
|
|||
<div class="flexrow">
|
||||
<ul class="attributes flexrow">
|
||||
<li class="attribute attribute-secondaries attack" data-attack="Melee">
|
||||
<h4 class="attribute-name box-title" title="{{localize 'OSE.Melee'}}"><a>{{localize 'OSE.MeleeShort'}}</a></h4>
|
||||
<h4 class="attribute-name box-title" title="{{localize 'OSE.Melee'}}">
|
||||
<a>{{localize 'OSE.MeleeShort'}}</a></h4>
|
||||
<div class="flexrow">
|
||||
<div class="attribute-value">
|
||||
{{#if config.ascendingAC}}
|
||||
{{add data.thac0.mod.melee (add mods.str data.thac0.bba)}}
|
||||
{{else}}
|
||||
{{subtract data.thac0.mod.melee (subtract mods.str data.thac0.value)}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="attribute attack" data-attack="Attack">
|
||||
<h4 class="attribute-name box-title" title="{{ localize 'OSE.Thac0' }}"><a>{{ localize "OSE.Thac0" }}</a>
|
||||
{{#if config.ascendingAC}}
|
||||
<li class="attribute">
|
||||
<h4 class="attribute-name box-title" title="{{ localize 'OSE.AB' }}">{{ localize "OSE.ABShort"}}
|
||||
</h4>
|
||||
<div class="flexrow">
|
||||
<div class="attribute-value">
|
||||
<input name="data.thac0.bba" type="text" value="{{data.thac0.bba}}" placeholder="0"
|
||||
data-dtype="Number" />
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{{else}}
|
||||
<li class="attribute">
|
||||
<h4 class="attribute-name box-title" title="{{ localize 'OSE.Thac0' }}">{{ localize "OSE.Thac0"}}
|
||||
</h4>
|
||||
<div class="flexrow">
|
||||
<div class="attribute-value">
|
||||
|
@ -131,11 +148,17 @@
|
|||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{{/if}}
|
||||
<li class="attribute attribute-secondaries attack" data-attack="Missile">
|
||||
<h4 class="attribute-name box-title" title="{{localize 'OSE.Missile'}}"><a>{{localize 'OSE.MissileShort'}}</a></h4>
|
||||
<h4 class="attribute-name box-title" title="{{localize 'OSE.Missile'}}">
|
||||
<a>{{localize 'OSE.MissileShort'}}</a></h4>
|
||||
<div class="flexrow">
|
||||
<div class="attribute-value">
|
||||
{{#if config.ascendingAC}}
|
||||
{{add data.thac0.mod.missile (add mods.dex data.thac0.bba)}}
|
||||
{{else}}
|
||||
{{subtract data.thac0.mod.missile (subtract mods.dex data.thac0.value)}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -144,7 +167,8 @@
|
|||
<div class="flexrow">
|
||||
<ul class="attributes flexrow">
|
||||
<li class="attribute attribute-secondaries">
|
||||
<h4 class="attribute-name box-title" title="{{localize 'OSE.MovementEncounter'}}">{{localize 'OSE.MovementEncounterShort'}}</h4>
|
||||
<h4 class="attribute-name box-title" title="{{localize 'OSE.MovementEncounter'}}">
|
||||
{{localize 'OSE.MovementEncounterShort'}}</h4>
|
||||
<div class="flexrow">
|
||||
<div class="attribute-value">
|
||||
{{divide data.movement.base 3}}
|
||||
|
@ -160,7 +184,8 @@
|
|||
</div>
|
||||
</li>
|
||||
<li class="attribute attribute-secondaries">
|
||||
<h4 class="attribute-name box-title" title="{{localize 'OSE.MovementOverland'}}">{{localize 'OSE.MovementOverlandShort'}}</h4>
|
||||
<h4 class="attribute-name box-title" title="{{localize 'OSE.MovementOverland'}}">
|
||||
{{localize 'OSE.MovementOverlandShort'}}</h4>
|
||||
<div class="flexrow">
|
||||
<div class="attribute-value">
|
||||
{{divide data.movement.base 5}}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<section class="ose chat-message">
|
||||
<div class="ose chat-block">
|
||||
<h2 class="chat-title">{{title}}</h2>
|
||||
{{#if details}}<div class="chat-details">{{{details}}}</div>{{/if}}
|
||||
{{#if rollOSE}}<div>{{{rollOSE}}}</div>{{/if}}
|
||||
</div>
|
||||
</section>
|
Loading…
Reference in New Issue