ENH: Chargen

master
U~man 2020-07-19 16:10:28 +02:00
parent 0ea0e25fba
commit 197d88eeb3
12 changed files with 147 additions and 53 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

After

Width:  |  Height:  |  Size: 232 KiB

View File

@ -110,11 +110,6 @@
wether your monster has a survival instinct or will fight till the
end.
</li>
<li><strong>Variable weapon damage</strong>:By default, even if the
damage field is shown in the weapons sheets, the rolled damage
will always be a d6, except for monsters. With this enabled, the
damage displayed will be effectively used for characters.
</li>
<li><strong>Encumbrance</strong>:The weight a character is carrying
is a significant part of the players treasure hunting adventures.
You have three options here.
@ -493,7 +488,7 @@
<img src="./images/treasure-toggle.png" />
</p>
<p>
You can toggle between the default rollable tables and the treasure table.
You can toggle between the default rollable tables and the treasure table with the Chest icon right next to the table name.
The default tables are selecting an entry by rolling a dice and returning the matching result. Treasure tables however, have a different behavior. Each entry is rolled with 1d100 and the value is compared to the 'chance field'. So it can return multiple items.
</p>
<p>

View File

@ -15,7 +15,9 @@
"OSE.dialog.partysheet": "Party Overview",
"OSE.dialog.selectActors": "Select PCs",
"OSE.dialog.dealXP": "Deal XP",
"OSE.dialog.generator": "Character generator",
"OSE.dialog.generateSaves": "Generate Saves",
"OSE.dialog.generateScore": "Rolling {score}",
"OSE.Formula": "Formula",
"OSE.SitMod": "Situational Modifier",
@ -162,8 +164,6 @@
"OSE.Setting.AscendingACHint": "The more the better",
"OSE.Setting.Morale": "Enable monsters Morale Rating",
"OSE.Setting.MoraleHint": "Morale Rating is shown on monster sheets",
"OSE.Setting.VariableWeaponDamage": "Variable Weapon Damage",
"OSE.Setting.VariableWeaponDamageHint": "Weapons have different damage dice",
"OSE.Setting.Encumbrance": "Encumbrance",
"OSE.Setting.EncumbranceHint": "Choose the way encumbrance is calculated",
"OSE.Setting.EncumbranceDisabled": "Disabled",

View File

@ -147,8 +147,6 @@
"OSE.Setting.AscendingACHint": "En cuanto más mejor",
"OSE.Setting.Morale": "Activar puntuación de Moral para monstruos",
"OSE.Setting.MoraleHint": "La puntuación de moral se ve en las hojas de monstruo",
"OSE.Setting.VariableWeaponDamage": "Daño de arma variable",
"OSE.Setting.VariableWeaponDamageHint": "Las armas tienen diferente dado de daño",
"OSE.Setting.Encumbrance": "Carga",
"OSE.Setting.EncumbranceHint": "Elige como se calcula la Carga",
"OSE.Setting.EncumbranceDisabled": "Disabled",

View File

@ -161,8 +161,6 @@
"OSE.Setting.AscendingACHint": "Le plus est le mieux",
"OSE.Setting.Morale": "Activer le Score de Moral",
"OSE.Setting.MoraleHint": "Le Score de Moral est affiché sur la fiche de Monstre",
"OSE.Setting.VariableWeaponDamage": "Dégâts d'Arme Variables",
"OSE.Setting.VariableWeaponDamageHint": "Les Armes peuvent avoir des dégâts d'arme différents",
"OSE.Setting.Encumbrance": "Encombrement",
"OSE.Setting.EncumbranceHint": "Choisissez comment l'encombrement est calculé",
"OSE.Setting.EncumbranceDisabled": "Désactivé",

View File

@ -57,11 +57,6 @@ export class OseActorSheetCharacter extends OseActorSheet {
getData() {
const data = super.getData();
// Settings
data.config.variableWeaponDamage = game.settings.get(
"ose",
"variableWeaponDamage"
);
data.config.ascendingAC = game.settings.get("ose", "ascendingAC");
data.config.individualInit = game.settings.get("ose", "individualInit");
data.config.encumbrance = game.settings.get("ose", "encumbranceOption");

View File

@ -69,7 +69,7 @@ export class OseActor extends Actor {
}
generator() {
}
generateSave(hd) {
@ -274,6 +274,9 @@ export class OseActor extends Actor {
rollHitDice(options = {}) {
const label = game.i18n.localize(`OSE.roll.hd`);
const rollParts = [this.data.data.hp.hd];
if (this.data.type == 'character') {
rollParts.push(this.data.data.scores.con.mod);
}
const data = {
...this.data,
@ -373,10 +376,7 @@ export class OseActor extends Actor {
};
let dmgParts = [];
if (
(!attData.dmg || !game.settings.get("ose", "variableWeaponDamage")) &&
this.type == "character"
) {
if (!attData.dmg) {
dmgParts.push("1d6");
} else {
dmgParts.push(attData.dmg);
@ -404,11 +404,7 @@ export class OseActor extends Actor {
const rollParts = ["1d20"];
const dmgParts = [];
let label = game.i18n.format("OSE.roll.attacks", { name: this.data.name });
if (
!attData.dmg ||
(!game.settings.get("ose", "variableWeaponDamage") &&
this.data.type == "character")
) {
if (!attData.dmg) {
dmgParts.push("1d6");
} else {
label = game.i18n.format("OSE.roll.attacksWith", { name: attData.label });

View File

@ -1,13 +1,14 @@
// eslint-disable-next-line no-unused-vars
import { OseActor } from '../actor/entity.js';
import { OseDice } from "../dice.js";
export class OseCharacterCreator extends FormApplication {
static get defaultOptions() {
const options = super.defaultOptions;
options.classes = ["ose", "dialog", "creator"],
options.id = 'character-creator';
options.template =
'systems/ose/templates/actors/dialogs/character-creation.html';
options.width = 380;
options.width = 235;
return options;
}
@ -18,7 +19,7 @@ export class OseCharacterCreator extends FormApplication {
* @type {String}
*/
get title() {
return `${this.object.name}: ${game.i18n.localize('OSE.dialog.tweaks')}`;
return `${this.object.name}: ${game.i18n.localize('OSE.dialog.generator')}`;
}
/* -------------------------------------------- */
@ -36,9 +37,66 @@ export class OseCharacterCreator extends FormApplication {
/* -------------------------------------------- */
doStats(ev) {
let list = $(ev.currentTarget).closest('.attribute-list');
let values = [];
list.find('.score-value').each((i, s) => {
if (s.value != 0) {
values.push(parseInt(s.value));
}
})
let n = values.length;
let sum = values.reduce((a,b) => a+b);
let mean = parseFloat(sum) / n;
let std = Math.sqrt(values.map(x => Math.pow(x-mean,2)).reduce((a,b) => a+b)/n);
let stats = list.siblings('.roll-stats');
stats.find('.sum').text(sum);
stats.find('.avg').text(Math.round(10 * sum / n) / 10);
stats.find('.std').text(Math.round(100 * std) / 100);
if (n >= 6) {
$(ev.currentTarget).closest('form').find('button[type="submit"]').removeAttr('disabled');
}
}
rollScore(score, options={}) {
const label = game.i18n.localize(`OSE.scores.${score}.long`);
const rollParts = ["3d6"];
const data = {
...this.object.data,
...{
rollData: {
type: "result"
},
},
};
// Roll and return
return OseDice.Roll({
event: options.event,
parts: rollParts,
data: data,
skipDialog: true,
speaker: ChatMessage.getSpeaker({ actor: this }),
flavor: game.i18n.format('OSE.dialog.generateScore', {score: label}),
title: game.i18n.format('OSE.dialog.generateScore', {score: label}),
});
}
/** @override */
activateListeners(html) {
super.activateListeners(html);
html.find('a.score-roll').click((ev) => {
let el = ev.currentTarget.parentElement.parentElement;
let score = el.dataset.score;
this.rollScore(score, {event: ev}).then(r => {
$(el).find('input').val(r.total).trigger('change');
});
});
html.find('input.score-value').change(ev => {
this.doStats(ev);
})
}
/**

View File

@ -103,12 +103,12 @@ export class OseDice {
)
.then((displayed) => {
ChatMessage.create(chatData);
resolve();
resolve(roll);
});
} else {
chatData.sound = CONFIG.sounds.dice;
ChatMessage.create(chatData);
resolve();
resolve(roll);
}
});
});
@ -215,17 +215,17 @@ export class OseDice {
)
.then(() => {
ChatMessage.create(chatData);
resolve();
resolve(roll);
});
} else {
ChatMessage.create(chatData);
resolve();
resolve(roll);
}
});
} else {
chatData.sound = CONFIG.sounds.dice;
ChatMessage.create(chatData);
resolve();
resolve(roll);
}
});
});

View File

@ -28,15 +28,6 @@ export const registerSettings = function () {
config: true,
});
game.settings.register("ose", "variableWeaponDamage", {
name: game.i18n.localize("OSE.Setting.VariableWeaponDamage"),
hint: game.i18n.localize("OSE.Setting.VariableWeaponDamageHint"),
default: false,
scope: "world",
type: Boolean,
config: true,
});
game.settings.register("ose", "encumbranceOption", {
name: game.i18n.localize("OSE.Setting.Encumbrance"),
hint: game.i18n.localize("OSE.Setting.EncumbranceHint"),

View File

@ -5,6 +5,33 @@
}
}
.ose.dialog.creator {
.attribute-list {
.form-fields {
flex: 0 0 50px;
input {
text-align: center;
font-weight: bold;
}
}
}
.roll-stats {
flex: 0 0 65px;
padding: 5px;
margin-left: 4px;
border-left: 1px solid $colorTan;
.form-group {
.form-fields {
span {
text-align: center;
line-height: 24px;
flex: 0;
}
}
}
}
}
.ose.dialog.party-sheet {
min-width: 250px;
min-height: 250px;
@ -45,7 +72,7 @@
margin-bottom: 2px;
font-size: 12px;
text-align: center;
.fields .field-row{
.fields .field-row {
&:nth-child(odd) {
background-color: rgba(0, 0, 0, 0.1);
}
@ -95,7 +122,7 @@
#sidebar #actors .directory-header .header-search {
.ose-party-sheet {
width: 32px;
text-align: center;
text-align: center;
line-height: 20px;
}
input {
@ -120,7 +147,7 @@
flex: 0 0 30px;
font-size: 26px;
line-height: 25px;
color:white;
color: white;
margin: 0 2px 5px 8px;
border-radius: 8px;
background: url("/systems/ose/assets/chest.png") no-repeat center;
@ -128,7 +155,8 @@
padding: 5px 8px;
cursor: pointer;
filter: grayscale(1) opacity(0.5);
&.active,&:hover {
&.active,
&:hover {
filter: none;
}
}
@ -172,12 +200,15 @@
box-shadow: 0 0 2px #fff inset;
.chat-title {
margin: 4px 0;
height: 30px;
overflow: hidden;
h2 {
border: none;
line-height: 34px;
margin: 0;
text-indent: 10px;
font-size: 16px;
word-break: break-all;
}
}
.chat-img {

View File

@ -1,7 +1,39 @@
<form autocomplete="off" onsubmit="event.preventDefault();">
<ol class="attribute-list">
{{#each config.scores as |score id| }}
<li data-score="{{id}}">{{score}}</li>
{{/each}}
</ol>
<div class="flexrow">
<div class="attribute-list">
{{#each config.scores as |score id| }}
<div data-score="{{id}}" class="form-group">
<label><a class="score-roll"><i class="fas fa-dice"></i></a> {{score}}</label>
<div class="form-fields">
<input class="score-value" name="data.scores.{{id}}.value" type="text" value="0" data-dtype="Number"/>
</div>
</div>
{{/each}}
</div>
<div class="roll-stats">
<div class="form-group">
<label>Sum</label>
<div class="form-fields">
<span class="sum">0</span>
</div>
</div>
<div class="form-group">
<label>Avg</label>
<div class="form-fields">
<span class="avg">0</span>
</div>
</div>
<div class="form-group">
<label>σ</label>
<div class="form-fields">
<span class="std">0</span>
</div>
</div>
</div>
</div>
<footer class="sheet-footer">
<button type="submit" disabled>
<i class="fas fa-save"></i>{{localize "Save Changes"}}
</button>
</footer>
</form>