ENH: Rolls!

master
U~man 2020-07-03 23:05:49 +02:00
parent 7c8ca4ae1f
commit 7be97146c2
9 changed files with 304 additions and 125 deletions

View File

@ -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",

View File

@ -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')}`,

View File

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

View File

@ -188,7 +188,7 @@
text-indent: 8px;
}
font-weight: 300;
font-size: 12px;
font-size: 13px;
background: $darkBackground;
color: white;
input {

View File

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

View File

@ -37,4 +37,4 @@
.resizable {
overflow: auto;
}
}
}

View File

@ -24,6 +24,7 @@
},
"thac0": {
"value": 19,
"bba": 0,
"mod": {
"missile": 0,
"melee": 0

View File

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

View File

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