WIP: Treasure tables, hp rolls

master
U~man 2020-07-05 18:02:28 +02:00
parent 2acc9992f5
commit e1319b6215
15 changed files with 128 additions and 56 deletions

View File

@ -222,6 +222,7 @@ async function clean() {
"lang",
"templates",
"assets",
"packs",
"module",
`${name}.js`,
"module.json",

View File

@ -5,6 +5,8 @@
"OSE.Ok": "Ok",
"OSE.Cancel": "Cancel",
"OSE.Roll": "Roll",
"OSE.Success": "Success",
"OSE.Failure": "Failure",
"OSE.Formula": "Formula",
"OSE.SitMod": "Situational Modifier",
@ -20,6 +22,8 @@
"OSE.Experience": "Experience",
"OSE.ExperienceBonus": "Bonus Experience",
"OSE.Treasure": "Treasure type",
"OSE.TreasureTable": "Table",
"OSE.TreasureTableHint": "Drop a rollable table here to roll the monster treasure",
"OSE.Size": "Size",
"OSE.Morale": "Morale",
"OSE.Retainer": "Retainer",

View File

@ -125,6 +125,11 @@ export class OseActorSheet extends ActorSheet {
actorObject.rollAttack({label: this.actor.name, type: attack}, { event: event });
});
html.find(".hit-dice .attribute-name a").click((ev) => {
let actorObject = this.actor;
actorObject.rollHitDice({ event: event });
});
super.activateListeners(html);
}

View File

@ -146,11 +146,6 @@ export class OseActorSheetCharacter extends OseActorSheet {
actorObject.rollCheck(score, { event: event });
});
html.find(".hit-dice .attribute-name a").click((ev) => {
let actorObject = this.actor;
actorObject.rollHitDice({ event: event });
});
html.find(".exploration .attribute-name a").click((ev) => {
let actorObject = this.actor;
let element = event.currentTarget;

View File

@ -26,22 +26,42 @@ export class OseActor extends Actor {
/* Socket Listeners and Handlers
/* -------------------------------------------- */
getExperience(value, options = {}) {
console.log(this.data);
if (this.data.type != 'character') {
if (this.data.type != "character") {
return;
}
let modified = value + (this.data.data.details.xp.bonus * value) / 100;
console.log(modified);
let modified = Math.floor(
value + (this.data.data.details.xp.bonus * value) / 100
);
return this.update({
"data.details.xp.value": modified + this.data.data.details.xp.value
"data.details.xp.value": modified + this.data.data.details.xp.value,
}).then(() => {
const speaker = ChatMessage.getSpeaker({actor: this});
ChatMessage.create({content: game.i18n.format("OSE.messages.getExperience", {name: this.name, value: modified}), speaker});
const speaker = ChatMessage.getSpeaker({ actor: this });
ChatMessage.create({
content: game.i18n.format("OSE.messages.getExperience", {
name: this.name,
value: modified,
}),
speaker,
});
});
}
/* -------------------------------------------- */
/* Rolls */
/* -------------------------------------------- */
rollHP(options = {}) {
let roll = new Roll(this.data.data.hp.hd).roll();
console.log(roll);
return this.update({
data: {
hp: {
max: roll.total,
value: roll.total,
},
},
});
}
rollSave(save, options = {}) {
const label = game.i18n.localize(`OSE.saves.${save}.long`);
const rollParts = ["1d20"];
@ -57,7 +77,7 @@ export class OseActor extends Actor {
};
let skip = options.event && options.event.ctrlKey;
// Roll and return
return OseDice.Roll({
event: options.event,
@ -132,7 +152,7 @@ export class OseActor extends Actor {
...this.data,
...{
rollData: {
type: "Hit Dice"
type: "Hit Dice",
},
},
};
@ -186,20 +206,20 @@ export class OseActor extends Actor {
rollData: {
type: "Damage",
stat: attData.type,
scores: data.scores
scores: data.scores,
},
},
};
let dmgParts = [];
if (!attData.dmg || !game.settings.get('ose', 'variableWeaponDamage')) {
if (!attData.dmg || !game.settings.get("ose", "variableWeaponDamage")) {
dmgParts.push("1d6");
} else {
dmgParts.push(attData.dmg);
}
// Add Str to damage
if (attData.type == 'melee') {
if (attData.type == "melee") {
dmgParts.push(data.scores.str.mod);
}
@ -212,7 +232,7 @@ export class OseActor extends Actor {
speaker: ChatMessage.getSpeaker({ actor: this }),
flavor: `${attData.label} - ${game.i18n.localize("OSE.Damage")}`,
title: `${attData.label} - ${game.i18n.localize("OSE.Damage")}`,
})
});
}
rollAttack(attData, options = {}) {
@ -240,7 +260,7 @@ export class OseActor extends Actor {
rollData: {
type: "Attack",
stat: attData.type,
scores: data.scores
scores: data.scores,
},
},
};

View File

@ -140,6 +140,11 @@ export class OseActorSheetMonster extends OseActorSheet {
actorObject.rollMorale({ event: event });
});
html.find(".hp-roll").click((ev) => {
let actorObject = this.actor;
actorObject.rollHP({ event: event });
});
// Handle default listeners last so system listeners are triggered first
super.activateListeners(html);
}

View File

@ -1,6 +1,10 @@
export class OseDice {
static digestResult(data, roll) {
let details = "";
let result = {
isSuccess: false,
isFailure: false,
target: "",
};
// ATTACKS
let die = roll.parts[0].total;
if (data.rollData.type == "Attack") {
@ -11,12 +15,12 @@ export class OseDice {
} else if (data.rollData.stat == "missile") {
bba += data.data.thac0.mod.missile + data.rollData.scores.dex.mod;
}
details = `<div class='roll-result roll-fail'><b>Failure</b> (${bba})</div>`;
result.target = bba;
if (die == 1) {
return details;
result.isFailure = true;
return result;
}
details = `<div class='roll-result'><b>Hits AC ${roll.total}</b> (${bba})</div>`;
result.isSuccess = true;
} else {
// B/X Historic THAC0 Calculation
let thac = data.data.thac0.value;
@ -25,11 +29,12 @@ export class OseDice {
} else if (data.rollData.stat == "missile") {
thac -= data.data.thac0.mod.missile + data.rollData.scores.dex.mod;
}
details = `<div class='roll-result roll-fail'><b>Failure</b> (${thac})</div>`;
result.target = thac;
if (thac - roll.total > 9) {
return details;
result.isFailure = true;
return result;
}
details = `<div class='roll-result'><b>Hits AC ${Math.clamped(
result.details = `<div class='roll-result'><b>Hits AC ${Math.clamped(
thac - roll.total,
-3,
9
@ -38,37 +43,41 @@ export class OseDice {
} else if (data.rollData.type == "Above") {
// SAVING THROWS
let sv = data.rollData.target;
result.target = sv;
if (roll.total >= sv) {
details = `<div class='roll-result roll-success'><b>Success!</b> (${sv})</div>`;
result.isSuccess = true;
} else {
details = `<div class='roll-result roll-fail'><b>Failure</b> (${sv})</div>`;
result.isFailure = true;
}
} else if (data.rollData.type == "Below") {
// Morale
let m = data.rollData.target;
result.target = m;
if (roll.total <= m) {
details = `<div class='roll-result roll-success'><b>Success!</b> (${m})</div>`;
result.isSuccess = true;
} else {
details = `<div class='roll-result roll-fail'><b>Failure</b> (${m})</div>`;
result.isFailure = true;
}
} else if (data.rollData.type == "Check") {
// SCORE CHECKS
let sc = data.rollData.target;
result.target = sc;
if (die == 1 || (roll.total <= sc && die < 20)) {
details = `<div class='roll-result roll-success'><b>Success!</b> (${sc})</div>`;
result.isSuccess = true;
} else {
details = `<div class='roll-result roll-fail'><b>Failure</b> (${sc})</div>`;
result.isFailure = true;
}
} else if (data.rollData.type == "Exploration") {
// EXPLORATION CHECKS
let sc = data.data.exploration[data.rollData.stat];
result.target = sc;
if (roll.total <= sc) {
details = `<div class='roll-result roll-success'><b>Success!</b> (${sc})</div>`;
result.isSuccess = true;
} else {
details = `<div class='roll-result roll-fail'><b>Failure</b> (${sc})</div>`;
result.isFailure = true;
}
}
return details;
return result;
}
static async sendRoll({
@ -91,18 +100,18 @@ export class OseDice {
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);
templateData.result = OseDice.digestResult(data, roll);
return new Promise((resolve) => {
roll.render().then((r) => {

0
src/packs/macros.db Normal file
View File

View File

@ -14,14 +14,15 @@
.chat-details {
padding: 4px;
font-size: 13px;
.roll-result {
text-align: center;
&.roll-success {
color: #18520b;
}
&.roll-fail {
color: #aa0200;
}
}
.roll-result {
font-size: 13px;
text-align: center;
&.roll-success {
color: #18520b;
}
&.roll-fail {
color: #aa0200;
}
}
}

View File

@ -20,6 +20,21 @@
min-width: 75px;
}
}
.attributes {
.attribute.health {
position: relative;
.attribute-name .hp-roll {
font-size: 12px;
position: absolute;
right: 5px;
top: 5px;
color: $colorFaint;
&:hover {
color: white;
}
}
}
}
.attribute-group {
.attacks-description {
margin: 2px;

View File

@ -9,7 +9,15 @@
"author": "U~man",
"esmodules": ["ose.js"],
"styles": ["ose.css"],
"packs": [],
"packs": [
{
"name": "OSEMacros",
"label": "Old School Essentials Macros",
"system": "ose",
"path": "./packs/macros.db",
"entity": "Macro"
}
],
"languages": [
{
"lang": "en",

View File

@ -141,7 +141,10 @@
"biography": "",
"alignment": "",
"xp": 0,
"treasure": "",
"treasure": {
"table": "",
"type": ""
},
"appearing": "",
"morale": 0
},

View File

@ -1,15 +1,15 @@
<section class="flexrow">
<ul class="attributes flexrow">
<li class="attribute health">
<h4 class="attribute-name box-title" title="{{localize 'OSE.Health'}}">{{ localize "OSE.HealthShort" }}</h4>
<h4 class="attribute-name box-title" title="{{localize 'OSE.Health'}}">{{ localize "OSE.HealthShort" }} <a class="hp-roll"><i class="fas fa-dice"></i></a></h4>
<div class="attribute-value flexrow">
<input name="data.hp.value" type="text" value="{{data.hp.value}}" data-dtype="Number" placeholder="0" />
<span class="sep"> / </span>
<input name="data.hp.max" type="text" value="{{data.hp.max}}" data-dtype="Number" placeholder="0" />
</div>
</li>
<li class="attribute">
<h4 class="attribute-name box-title" title="{{localize 'OSE.HitDice'}}">{{ localize "OSE.HitDiceShort" }}
<li class="attribute hit-dice">
<h4 class="attribute-name box-title" title="{{localize 'OSE.HitDice'}}"><a>{{ localize "OSE.HitDiceShort" }}</a>
</h4>
<div class="attribute-value">
<input name="data.hp.hd" type="text" value="{{data.hp.hd}}" data-dtype="String" />

View File

@ -23,13 +23,17 @@
{{/if}}
</ul>
<ul class="summary flexrow">
<li class="flex2">
<li class="flex3">
<input type="text" name="data.details.xp" value="{{data.details.xp}}" />
<label>{{localize 'OSE.Experience'}}</label>
</li>
<li class="flex2">
<input type="text" name="data.details.treasure" value="{{data.details.treasure}}" />
<li class="flex3">
<input type="text" name="data.details.treasure.type" value="{{data.details.treasure.type}}" />
<label>{{localize 'OSE.Treasure'}}</label>
</li>
<li title="{{localize 'OSE.TreasureTableHint'}}">
<div>{{data.details.treasure.table}}</div>
<label>{{localize 'OSE.TreasureTable'}}</label>
</li>
</ul>
</section>

View File

@ -1,7 +1,9 @@
<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 result.details}}<div class="chat-details">{{{result.details}}}</div>{{/if}}
{{#if result.isFailure}}<div class='roll-result roll-fail'><b>{{localize 'OSE.Failure'}}</b> ({{result.target}})</div>{{/if}}
{{#if result.isSuccess}}<div class='roll-result roll-success'><b>{{localize 'OSE.Success'}}</b> ({{result.target}})</div>{{/if}}
{{#if rollOSE}}<div>{{{rollOSE}}}</div>{{/if}}
</div>
</section>