Initiative rework, with some broken css

This commit is contained in:
Vlyan
2021-01-15 20:14:50 +01:00
parent 58a4e71b32
commit 27e88be49d
23 changed files with 425 additions and 156 deletions

View File

@@ -1,5 +1,15 @@
# Changelog # Changelog
## 1.0.1 - Initiative first !
- Added initiative system :
- Now use the score rule (the real one if you prefer)
- Added global modifiers for Characters, Adversary and Minons in the combat tracker : Confrontation types, Prepared
- Added sheet modifiers for Characters and Adversary: Prepared
- Initiative buttons in character sheet now display the DicePicker and do the initiative roll
- Spanish real translation by Alejabar (thanks !)
- Added a GM Dialog Tool for setting global difficulty (TN) value / hidden (with DicePicker live refresh)
- Compendium now display Ring and Rank if any in list view
## 1.0.0 - First public release ## 1.0.0 - First public release
- Removed the 0ds if no skill point - Removed the 0ds if no skill point
- Added initiative roll (only tactics for the moment) - Added initiative roll (only tactics for the moment)

View File

@@ -283,7 +283,11 @@
"intrigue": "Intrigue", "intrigue": "Intrigue",
"duel": "Duel", "duel": "Duel",
"skirmish": "Skirmish", "skirmish": "Skirmish",
"mass_battle": "Mass Battle" "mass_battle": "Mass Battle",
"prepared_true": "Prepared",
"prepared_false": "Surprised",
"prepared_null": "Defined in the character sheet (default)",
"already_set": "Your initiative has already been drawn"
} }
}, },
"advancements": { "advancements": {
@@ -299,11 +303,10 @@
"curriculum": "In curriculum", "curriculum": "In curriculum",
"curriculum_validate": "Complete this rank" "curriculum_validate": "Complete this rank"
}, },
"npc": { "character_types": {
"types": { "character": "Player Character",
"adversary": "Adversary", "adversary": "Adversary",
"minion": "Minion" "minion": "Minion"
}
}, },
"twenty_questions": { "twenty_questions": {
"title": "Twenty questions", "title": "Twenty questions",

View File

@@ -283,7 +283,11 @@
"intrigue": "Intriga", "intrigue": "Intriga",
"duel": "Duelo", "duel": "Duelo",
"skirmish": "Escaramuza", "skirmish": "Escaramuza",
"mass_battle": "Batalla a gran escala" "mass_battle": "Batalla a gran escala",
"prepared_true": "Prepared",
"prepared_false": "Surprised",
"prepared_null": "Defined in the character sheet (default)",
"already_set": "Your initiative has already been drawn"
} }
}, },
"advancements": { "advancements": {
@@ -299,11 +303,10 @@
"curriculum": "En programa de estudio", "curriculum": "En programa de estudio",
"curriculum_validate": "Completar este rango" "curriculum_validate": "Completar este rango"
}, },
"npc": { "character_types": {
"types": { "character": "Personaje jugador",
"adversary": "Adversario", "adversary": "Adversario",
"minion": "Esbirro" "minion": "Esbirro"
}
}, },
"twenty_questions": { "twenty_questions": {
"title": "Veinte preguntas", "title": "Veinte preguntas",

View File

@@ -283,7 +283,11 @@
"intrigue": "Intrigue", "intrigue": "Intrigue",
"duel": "Duel", "duel": "Duel",
"skirmish": "Escarmouche", "skirmish": "Escarmouche",
"mass_battle": "Bataille rangée" "mass_battle": "Bataille rangée",
"prepared_true": "Prêt(e)",
"prepared_false": "Surpris(e)",
"prepared_null": "Défini par la fiche de personnage (défaut)",
"already_set": "Votre initiative a déjà été tirée"
} }
}, },
"advancements": { "advancements": {
@@ -299,11 +303,10 @@
"curriculum": "Inclus dans le cursus", "curriculum": "Inclus dans le cursus",
"curriculum_validate": "Valider la progression" "curriculum_validate": "Valider la progression"
}, },
"npc": { "character_types": {
"types": { "character": "Personnage Joueur",
"adversary": "Antagoniste", "adversary": "Antagoniste",
"minion": "Sous-fifre" "minion": "Sous-fifre"
}
}, },
"twenty_questions": { "twenty_questions": {
"title": "Le jeu des Vingt questions", "title": "Le jeu des Vingt questions",

View File

@@ -96,6 +96,12 @@ export class ActorL5r5e extends Actor {
if (data.void_points.value > data.void_points.max) { if (data.void_points.value > data.void_points.max) {
data.void_points.value = data.void_points.max; data.void_points.value = data.void_points.max;
} }
// *** Migration stuff ***
// TODO remove in patch 1.1+
if (data.prepared === undefined) {
data.prepared = true;
}
} }
} }
} }

View File

@@ -175,6 +175,7 @@ export class BaseSheetL5r5e extends ActorSheet {
new game.l5r5e.DicePickerDialog({ new game.l5r5e.DicePickerDialog({
skillId: li.data("skill") || null, skillId: li.data("skill") || null,
skillCatId: li.data("skillcat") || null, skillCatId: li.data("skillcat") || null,
isInitiativeRoll: li.data("initiative") || false,
actor: this.actor, actor: this.actor,
}).render(true); }).render(true);
}); });
@@ -186,6 +187,16 @@ export class BaseSheetL5r5e extends ActorSheet {
event.target.select(); event.target.select();
}); });
// Prepared (Initiative)
html.find(".prepared-control").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
const preparedId = $(event.currentTarget).data("id");
if (["adversary", "character"].includes(preparedId)) {
this._switchPrepared();
}
});
// *** Items : add, edit, delete *** // *** Items : add, edit, delete ***
html.find(".item-add").on("click", (event) => { html.find(".item-add").on("click", (event) => {
event.preventDefault(); event.preventDefault();
@@ -204,6 +215,20 @@ export class BaseSheetL5r5e extends ActorSheet {
}); });
} }
/**
* Switch the state "prepared" (initiative)
* @private
*/
_switchPrepared() {
this.actor.data.data.prepared = !this.actor.data.data.prepared;
this.actor.update({
data: {
prepared: this.actor.data.data.prepared,
},
});
this.render(false);
}
/** /**
* Add a generic item with sub type * Add a generic item with sub type
* @private * @private

View File

@@ -21,7 +21,7 @@ export class NpcSheetL5r5e extends BaseSheetL5r5e {
sheetData.data.types = NpcSheetL5r5e.types.map((e) => ({ sheetData.data.types = NpcSheetL5r5e.types.map((e) => ({
id: e, id: e,
label: game.i18n.localize("l5r5e.npc.types." + e), label: game.i18n.localize("l5r5e.character_types." + e),
})); }));
return sheetData; return sheetData;

View File

@@ -22,57 +22,90 @@ export class CombatL5r5e extends Combat {
ids = [ids]; ids = [ids];
} }
// Make combatants array // Get global modifiers
const combatants = []; const cfg = {
ids.forEach((combatantId) => { difficulty: game.settings.get("l5r5e", "initiative.difficulty.value"),
const combatant = game.combat.combatants.find((c) => c._id === combatantId); difficultyHidden: game.settings.get("l5r5e", "initiative.difficulty.hidden"),
if (combatant && combatant.actor) { prepared: {
combatants.push(combatant); character: game.settings.get("l5r5e", "initiative.prepared.character"),
} adversary: game.settings.get("l5r5e", "initiative.prepared.adversary"),
}); minion: game.settings.get("l5r5e", "initiative.prepared.minion"),
},
};
// Get modifiers // SkillId from DicePicker or global
const difficulty = game.settings.get("l5r5e", "initiative.difficulty.value"); const skillId = messageOptions.skillId
const difficultyHidden = game.settings.get("l5r5e", "initiative.difficulty.hidden"); ? messageOptions.skillId
const skillId = CONFIG.l5r5e.initiativeSkills[game.settings.get("l5r5e", "initiative.encounter")]; : CONFIG.l5r5e.initiativeSkills[game.settings.get("l5r5e", "initiative.encounter")];
const skillCat = CONFIG.l5r5e.skills.get(skillId); const skillCat = CONFIG.l5r5e.skills.get(skillId);
// Get score for each combatant // Get score for each combatant
const updatedCombatants = []; const updatedCombatants = [];
combatants.forEach((combatant) => { ids.forEach((combatantId) => {
const combatant = game.combat.combatants.find((c) => c._id === combatantId);
if (!combatant || !combatant.actor) {
return;
}
// shortcut to data
const data = combatant.actor.data.data; const data = combatant.actor.data.data;
// A characters initiative value is based on their state of preparedness when the conflict began. // A characters initiative value is based on their state of preparedness when the conflict began.
// If the character was ready for the conflict, their base initiative value is their focus attribute. // If the character was ready for the conflict, their base initiative value is their focus attribute.
// If the character was unprepared (such as when surprised), their base initiative value is their vigilance attribute. // If the character was unprepared (such as when surprised), their base initiative value is their vigilance attribute.
const isPrepared = true; // TODO in actor ? (pc and npc) let initiative = 0;
let initiative = isPrepared ? data.focus : data.vigilance;
if (combatant.actor.data.type === "npc" && combatant.actor.data.data.type === "minion") {
// Minion NPCs can generate initiative value without a check, using their focus or vigilance attribute
initiative = cfg.prepared.minion ? data.focus : data.vigilance;
} else {
// PC and Adversary // PC and Adversary
// (minion NPCs can generate initiative value without a check, using their focus or vigilance attribute) const isPc = combatant.actor.data.type === "character";
if (combatant.actor.data.type !== "npc" || combatant.actor.data.data.type === "minion") {
const formula = [`${data.rings[data.stance]}dr`]; // prepared is a boolean or if null we get the info in the actor sheet
const skillValue = let isPrepared = isPc ? cfg.prepared.character : cfg.prepared.adversary;
combatant.actor.data.type === "npc" ? data.skills[skillCat] : data.skills[skillCat][skillId]; if (isPrepared === "null") {
isPrepared = data.prepared;
}
initiative = isPrepared ? data.focus : data.vigilance;
// Roll formula
if (!formula) {
const createFormula = [`${data.rings[data.stance]}dr`];
const skillValue = isPc ? data.skills[skillCat][skillId] : data.skills[skillCat];
if (skillValue > 0) { if (skillValue > 0) {
formula.push(`${skillValue}ds`); createFormula.push(`${skillValue}ds`);
}
formula = createFormula.join("+");
} }
const roll = new game.l5r5e.RollL5r5e(formula.join("+")); const roll = new game.l5r5e.RollL5r5e(formula);
roll.actor = combatant.actor; roll.actor = combatant.actor;
roll.l5r5e.stance = data.stance; roll.l5r5e.stance = data.stance;
roll.l5r5e.skillId = skillId; roll.l5r5e.skillId = skillId;
roll.l5r5e.summary.difficulty = difficulty; roll.l5r5e.skillCatId = skillCat;
roll.l5r5e.summary.difficultyHidden = difficultyHidden; roll.l5r5e.summary.difficulty =
messageOptions.difficulty !== undefined ? messageOptions.difficulty : cfg.difficulty;
roll.l5r5e.summary.difficultyHidden =
messageOptions.difficultyHidden !== undefined
? messageOptions.difficultyHidden
: cfg.difficultyHidden;
roll.l5r5e.summary.voidPointUsed = !!messageOptions.useVoidPoint;
roll.roll(); roll.roll();
roll.toMessage({ flavor: game.i18n.localize("l5r5e.chatdices.initiative_roll") }); roll.toMessage({
flavor:
game.i18n.localize("l5r5e.chatdices.initiative_roll") +
" (" +
game.i18n.localize(`l5r5e.conflict.initiative.prepared_${isPrepared}`) +
")",
});
// if the character succeeded on their Initiative check, they add 1 to their base initiative value, // if the character succeeded on their Initiative check, they add 1 to their base initiative value,
// plus an additional amount equal to their bonus successes. // plus an additional amount equal to their bonus successes.
if (roll.l5r5e.summary.success >= difficulty) { if (roll.l5r5e.summary.success >= roll.l5r5e.summary.difficulty) {
initiative = initiative + 1 + Math.max(roll.l5r5e.summary.success - difficulty, 0); initiative =
initiative + 1 + Math.max(roll.l5r5e.summary.success - roll.l5r5e.summary.difficulty, 0);
} }
} }

View File

@@ -5,6 +5,8 @@
export class DicePickerDialog extends FormApplication { export class DicePickerDialog extends FormApplication {
/** /**
* Current Actor * Current Actor
* @type {Actor}
* @private
*/ */
_actor = null; _actor = null;
@@ -29,6 +31,7 @@ export class DicePickerDialog extends FormApplication {
add_void_point: true, add_void_point: true,
}, },
useVoidPoint: false, useVoidPoint: false,
isInitiativeRoll: false,
}; };
/** /**
@@ -84,8 +87,9 @@ export class DicePickerDialog extends FormApplication {
* skillCatId string (artisan) * skillCatId string (artisan)
* difficulty number (0-9) * difficulty number (0-9)
* difficultyHidden boolean * difficultyHidden boolean
* isInitiativeRoll boolean
* *
* @param options actor, actorId, ringId, actorName, skillId, skillCatId, difficulty, difficultyHidden * @param options actor, actorId, ringId, actorName, skillId, skillCatId, difficulty, difficultyHidden, isInitiativeRoll
*/ */
constructor(options = {}) { constructor(options = {}) {
super({}, options); super({}, options);
@@ -104,33 +108,38 @@ export class DicePickerDialog extends FormApplication {
}); });
// Ring // Ring
if (options?.ringId) { if (options.ringId) {
this.ringId = options.ringId; this.ringId = options.ringId;
} }
// Skill / SkillCategory // Skill / SkillCategory
if (options?.skillId) { if (options.skillId) {
this.skillId = options.skillId; this.skillId = options.skillId;
} }
// SkillCategory skillCatId // SkillCategory skillCatId
if (options?.skillCatId) { if (options.skillCatId) {
this.skillCatId = options.skillCatId; this.skillCatId = options.skillCatId;
} }
// Difficulty // Difficulty
if (options?.difficulty) { if (options.difficulty) {
this.difficulty = options.difficulty; this.difficulty = options.difficulty;
} else { } else {
this.difficulty = game.settings.get("l5r5e", "initiative.difficulty.value"); this.difficulty = game.settings.get("l5r5e", "initiative.difficulty.value");
} }
// difficultyHidden // DifficultyHidden
if (options?.difficultyHidden) { if (options.difficultyHidden) {
this.difficultyHidden = options.difficultyHidden; this.difficultyHidden = options.difficultyHidden;
} else { } else {
this.difficultyHidden = game.settings.get("l5r5e", "initiative.difficulty.hidden"); this.difficultyHidden = game.settings.get("l5r5e", "initiative.difficulty.hidden");
} }
// InitiativeRoll
if (options.isInitiativeRoll) {
this.object.isInitiativeRoll = !!options.isInitiativeRoll;
}
} }
/** /**
@@ -355,12 +364,13 @@ export class DicePickerDialog extends FormApplication {
return false; return false;
} }
let formula = []; // If initiative roll, check if player already have
if (this.object.ring.value > 0) { if (this.object.isInitiativeRoll) {
formula.push(`${this.object.ring.value}dr`); const combatant = game.combat.combatants.find((c) => c.actor._id === this._actor._id && c.initiative > 0);
if (combatant) {
ui.notifications.error(game.i18n.localize("l5r5e.conflict.initiative.already_set"));
return this.close();
} }
if (this.object.skill.value > 0) {
formula.push(`${this.object.skill.value}ds`);
} }
// Update Actor // Update Actor
@@ -381,12 +391,36 @@ export class DicePickerDialog extends FormApplication {
} }
// Update actor // Update actor
this._actor.update({ await this._actor.update({
data: diffObject(this._actor.data.data, actorData), data: diffObject(this._actor.data.data, actorData),
}); });
} }
// Let's roll ! // Build the formula
let formula = [];
if (this.object.ring.value > 0) {
formula.push(`${this.object.ring.value}dr`);
}
if (this.object.skill.value > 0) {
formula.push(`${this.object.skill.value}ds`);
}
if (this.object.isInitiativeRoll) {
// Initiative roll
this._actor.rollInitiative({
initiativeOptions: {
formula: formula.join("+"),
// updateTurn: true,
messageOptions: {
skillId: this.object.skill.id,
difficulty: this.object.difficulty.value,
difficultyHidden: this.object.difficulty.hidden,
useVoidPoint: this.object.useVoidPoint,
},
},
});
} else {
// Regular roll, so let's roll !
const roll = await new game.l5r5e.RollL5r5e(formula.join("+")); const roll = await new game.l5r5e.RollL5r5e(formula.join("+"));
roll.actor = this._actor; roll.actor = this._actor;
@@ -399,6 +433,8 @@ export class DicePickerDialog extends FormApplication {
await roll.roll(); await roll.roll();
await roll.toMessage(); await roll.toMessage();
}
return this.close(); return this.close();
} }

View File

@@ -109,7 +109,7 @@ export class GmToolsDialog extends FormApplication {
break; break;
case 3: case 3:
// right clic - minus 1 // right clic - minus 1
this.object.difficulty = Math.max(1, this.object.difficulty - 1); this.object.difficulty = Math.max(0, this.object.difficulty - 1);
break; break;
} }
game.settings.set("l5r5e", "initiative.difficulty.value", this.object.difficulty).then(() => this.submit()); game.settings.set("l5r5e", "initiative.difficulty.value", this.object.difficulty).then(() => this.submit());

View File

@@ -61,63 +61,80 @@ export default class HooksL5r5e {
* Combat tracker * Combat tracker
*/ */
static async renderCombatTracker(app, html, data) { static async renderCombatTracker(app, html, data) {
// TODO do this in partial // Display Combat bar (only for GMs)
let bar = ""; await this._gmCombatBar(app, html, data);
// *** Encounter Type ***
const encounterIcons = {
intrigue: "i_courtier",
duel: "fas fa-tint", // fa-tint / fa-blind
skirmish: "i_bushi",
mass_battle: "fa fa-users",
};
const encounterType = game.settings.get("l5r5e", "initiative.encounter");
Object.entries(CONFIG.l5r5e.initiativeSkills).forEach(([id, skill]) => {
bar =
bar +
`<a class="encounter encounter-control" data-id="${id}">` +
`<i class="${encounterIcons[id]}${id === encounterType ? " active" : ""}" title="${game.i18n.localize(
"l5r5e.conflict.initiative." + id
)}"></i>` +
`</a>`;
});
// *** Prepared ***
// TODO
// const encounterType = game.settings.get("l5r5e", "initiative.prepared");
bar =
bar +
`<a class="encounter prepared-control" data-id="tmp">` +
`<i class="fa fa-low-vision" title="npc prepared or not (WIP)"></i>` +
`</a>`;
const elmt = html.find("#l5r5e_encounter");
if (elmt.length > 0) {
elmt.html(bar);
} else {
html.find("#combat-round").append(`<nav class="encounters flexrow" id="l5r5e_encounter">${bar}</nav>`);
} }
// Buttons Listener /**
* Display a GM bar for Combat/Initiative
* @private
*/
static async _gmCombatBar(app, html, data) {
// Only for GMs
if (!game.user.isGM) {
return;
}
// *** Conf ***
const encounterTypeList = Object.keys(CONFIG.l5r5e.initiativeSkills);
const prepared = {
character: game.settings.get("l5r5e", "initiative.prepared.character"),
adversary: game.settings.get("l5r5e", "initiative.prepared.adversary"),
minion: game.settings.get("l5r5e", "initiative.prepared.minion"),
};
// *** Template ***
const tpl = await renderTemplate(`${CONFIG.l5r5e.paths.templates}gm/combat-tracker-bar.html`, {
encounterType: game.settings.get("l5r5e", "initiative.encounter"),
encounterTypeList,
prepared,
});
// Add/replace in bar
const elmt = html.find("#l5r5e_gm_combat_tracker_bar");
if (elmt.length > 0) {
elmt.replaceWith(tpl);
} else {
html.find("#combat-round").append(tpl);
}
// Buttons Listeners
// TODO event for multiple GM
html.find(".encounter-control").on("click", (event) => { html.find(".encounter-control").on("click", (event) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
const encounter = $(event.currentTarget).data("id"); const encounter = $(event.currentTarget).data("id");
if (!encounterTypeList.includes(encounter)) {
return;
}
game.settings game.settings
.set("l5r5e", "initiative.encounter", encounter) .set("l5r5e", "initiative.encounter", encounter)
.then(() => HooksL5r5e.renderCombatTracker(app, html, data)); .then(() => HooksL5r5e._gmCombatBar(app, html, data));
}); });
// html.find(".prepared-control").on("click", (event) => { html.find(".prepared-control").on("click", (event) => {
// event.preventDefault(); event.preventDefault();
// event.stopPropagation(); event.stopPropagation();
// let prepared = $(event.currentTarget).data('id'); let preparedId = $(event.currentTarget).data("id");
// // if same, unset it if (!Object.hasOwnProperty.call(prepared, preparedId)) {
// if (prepared === encounterType) { return;
// prepared = ""; }
// } let value = prepared[preparedId];
// game.settings.set("l5r5e", "initiative.prepared", prepared).then(() => HooksL5r5e.renderCombatTracker(app, html, data)); switch (value) {
// }); case "false":
value = "true";
break;
case "true":
value = preparedId === "minion" ? "false" : "null";
break;
case "null":
value = "false";
break;
}
game.settings
.set("l5r5e", `initiative.prepared.${preparedId}`, value)
.then(() => HooksL5r5e._gmCombatBar(app, html, data));
});
} }
/** /**

View File

@@ -21,6 +21,7 @@ export const PreloadTemplates = async function () {
"systems/l5r5e/templates/actors/npc/social.html", "systems/l5r5e/templates/actors/npc/social.html",
"systems/l5r5e/templates/actors/npc/rings.html", "systems/l5r5e/templates/actors/npc/rings.html",
"systems/l5r5e/templates/actors/npc/attributes.html", "systems/l5r5e/templates/actors/npc/attributes.html",
"systems/l5r5e/templates/actors/npc/conflict.html",
"systems/l5r5e/templates/actors/npc/skill.html", "systems/l5r5e/templates/actors/npc/skill.html",
"systems/l5r5e/templates/actors/npc/techniques.html", "systems/l5r5e/templates/actors/npc/techniques.html",
// items // items

View File

@@ -26,11 +26,25 @@ export const RegisterSettings = function () {
type: String, type: String,
default: "skirmish", default: "skirmish",
}); });
game.settings.register("l5r5e", "initiative.prepared", { game.settings.register("l5r5e", "initiative.prepared.character", {
name: "Initiative NPC prepared or not", name: "Initiative PC prepared or not",
scope: "world", scope: "world",
config: false, config: false,
type: Boolean, type: String,
default: true, default: "null",
});
game.settings.register("l5r5e", "initiative.prepared.adversary", {
name: "Initiative NPC adversary are prepared or not",
scope: "world",
config: false,
type: String,
default: "null",
});
game.settings.register("l5r5e", "initiative.prepared.minion", {
name: "Initiative NPC minion are prepared or not",
scope: "world",
config: false,
type: String,
default: "true",
}); });
}; };

File diff suppressed because one or more lines are too long

View File

@@ -264,6 +264,38 @@ sup {
} }
} }
.prepared {
&-character {
color: $l5r5e-earth;
}
&-adversary {
color: $l5r5e-fire;
}
&-minion {
color: $l5r5e-water;
}
&-icon {
font-weight: 900;
font-family: "Font Awesome 5 Free";
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
display: inline-block;
font-style: normal;
font-variant: normal;
text-rendering: auto;
line-height: 1;
&-true:before {
content: "\f06e";
}
&-false:before {
content: "\f070";
}
&-null:before {
content: "\f2a8";
}
}
}
// Fix for "search anywhere" draggable icon // Fix for "search anywhere" draggable icon
.window-draggable-handle { .window-draggable-handle {
z-index: 2; z-index: 2;

View File

@@ -24,25 +24,6 @@
} }
} }
} }
.initiative {
&-wrapper {
display: block;
flex: 100%;
text-align: center;
}
button {
width: 22%;
margin: 0 0.25rem 0.25rem;
padding: 0 0.5rem;
color: #5a6e5a;
background: rgba(255, 255, 255, 0.5);
border: 1px solid #5a6e5a;
border-radius: 1rem;
}
button:focus {
box-shadow: none;
}
}
fieldset { fieldset {
&.advancement { &.advancement {
display: block; display: block;
@@ -592,6 +573,25 @@
} }
&.actor, &.actor,
&.npc { &.npc {
.initiative {
&-wrapper {
display: block;
flex: 100%;
text-align: center;
}
button {
width: 22%;
margin: 0 0.25rem 0.25rem;
padding: 0 0.5rem;
color: #5a6e5a;
background: rgba(255, 255, 255, 0.5);
border: 1px solid #5a6e5a;
border-radius: 1rem;
}
button:focus {
box-shadow: none;
}
}
.limited { .limited {
h1 { h1 {
margin: 0.5rem 0; margin: 0.5rem 0;

View File

@@ -568,7 +568,6 @@ button {
h3 { h3 {
font-size: 0.85rem; font-size: 0.85rem;
} }
.encounter { .encounter {
i { i {
font-size: 23px; font-size: 23px;
@@ -576,7 +575,35 @@ button {
} }
.active { .active {
box-shadow: 0 1px 5px $l5r5e-red; color: $l5r5e-maho;
}
&-icon {
font-weight: 900;
font-family: "Font Awesome 5 Free";
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
display: inline-block;
font-style: normal;
font-variant: normal;
text-rendering: auto;
line-height: 1;
&-intrigue:before {
content: "\f21b";
}
&-duel:before {
content: "\f506";
}
&-skirmish:before {
content: "\f505";
}
&-mass_battle:before {
content: "\f447";
}
} }
} }
} }

View File

@@ -5,8 +5,27 @@
"version": "1.0.0", "version": "1.0.0",
"minimumCoreVersion": "0.7.9", "minimumCoreVersion": "0.7.9",
"compatibleCoreVersion": "0.7.9", "compatibleCoreVersion": "0.7.9",
"manifestPlusVersion": "1.0.0",
"socket": true, "socket": true,
"author": "Team L5R", "author": "Team L5R",
"authors": [
{
"name": "Vlyan",
"discord": "Vlyan#6771"
},
{
"name": "Mandar"
},
{
"name": "Carter"
},
{
"name": "Hrunh"
},
{
"name": "Sasmira"
}
],
"background": "L5R-Header.webp", "background": "L5R-Header.webp",
"scripts": [], "scripts": [],
"esmodules": ["./scripts/main-l5r5e.js"], "esmodules": ["./scripts/main-l5r5e.js"],

View File

@@ -85,7 +85,8 @@
"max": 1, "max": 1,
"value": 0 "value": 0
}, },
"stance": "void" "stance": "void",
"prepared": true
}, },
"advancement": { "advancement": {
"xp_total": 0, "xp_total": 0,

View File

@@ -1,9 +1,14 @@
<fieldset class="initiative initiative-wrapper"> <fieldset class="initiative initiative-wrapper">
<legend class="section-header">{{ localize 'l5r5e.conflict.initiative.title' }}</legend> <legend class="section-header">
<button class="initiative dice-picker" data-skill="sentiment">{{ localize 'l5r5e.conflict.initiative.intrigue'}}</button> {{ localize 'l5r5e.conflict.initiative.title' }}
<button class="initiative dice-picker" data-skill="meditation">{{ localize 'l5r5e.conflict.initiative.duel'}}</button> <a class="encounter prepared-control" data-id="{{entity.type}}">
<button class="initiative dice-picker" data-skill="tactics">{{ localize 'l5r5e.conflict.initiative.skirmish'}}</button> <i class="fa fas prepared-icon prepared-icon-{{data.prepared}} prepared-{{entity.type}}" title="{{localize (localize 'l5r5e.conflict.initiative.prepared_{value}' value=data.prepared)}}"></i>
<button class="initiative dice-picker" data-skill="command">{{ localize 'l5r5e.conflict.initiative.mass_battle'}}</button> </a>
</legend>
<button class="initiative dice-picker" data-initiative="true" data-skill="sentiment">{{ localize 'l5r5e.conflict.initiative.intrigue'}}</button>
<button class="initiative dice-picker" data-initiative="true" data-skill="meditation">{{ localize 'l5r5e.conflict.initiative.duel'}}</button>
<button class="initiative dice-picker" data-initiative="true" data-skill="tactics">{{ localize 'l5r5e.conflict.initiative.skirmish'}}</button>
<button class="initiative dice-picker" data-initiative="true" data-skill="command">{{ localize 'l5r5e.conflict.initiative.mass_battle'}}</button>
</fieldset> </fieldset>
<fieldset class="stances-content flexrow"> <fieldset class="stances-content flexrow">
<legend class="section-header">{{ localize 'l5r5e.conflict.stance' }}</legend> <legend class="section-header">{{ localize 'l5r5e.conflict.stance' }}</legend>

View File

@@ -20,7 +20,12 @@
</header> </header>
{{!-- Sheet Body --}} {{!-- Sheet Body --}}
<section class="sheet-body"> <section class="sheet-body">
<article>
{{> 'systems/l5r5e/templates/actors/npc/skill.html' }} {{> 'systems/l5r5e/templates/actors/npc/skill.html' }}
{{#ifCond data.type '==' 'adversary'}}
{{> 'systems/l5r5e/templates/actors/npc/conflict.html' }}
{{/ifCond}}
</article>
<article> <article>
{{> 'systems/l5r5e/templates/actors/npc/narrative.html' }} {{> 'systems/l5r5e/templates/actors/npc/narrative.html' }}
</article> </article>

View File

@@ -1,3 +1,15 @@
<fieldset class="initiative initiative-wrapper">
<legend class="section-header">
{{ localize 'l5r5e.conflict.initiative.title' }}
<a class="encounter prepared-control" data-id="{{data.type}}">
<i class="fa fas prepared-icon prepared-icon-{{data.prepared}} prepared-{{data.type}}" title="{{localize (localize 'l5r5e.conflict.initiative.prepared_{value}' value=data.prepared)}}"></i>
</a>
</legend>
<button class="initiative dice-picker" data-initiative="true" data-skill="sentiment">{{ localize 'l5r5e.conflict.initiative.intrigue'}}</button>
<button class="initiative dice-picker" data-initiative="true" data-skill="meditation">{{ localize 'l5r5e.conflict.initiative.duel'}}</button>
<button class="initiative dice-picker" data-initiative="true" data-skill="tactics">{{ localize 'l5r5e.conflict.initiative.skirmish'}}</button>
<button class="initiative dice-picker" data-initiative="true" data-skill="command">{{ localize 'l5r5e.conflict.initiative.mass_battle'}}</button>
</fieldset>
<fieldset class="stances-content flexrow"> <fieldset class="stances-content flexrow">
<legend class="section-header">{{ localize 'l5r5e.conflict.stance' }}</legend> <legend class="section-header">{{ localize 'l5r5e.conflict.stance' }}</legend>
<ul> <ul>

View File

@@ -0,0 +1,17 @@
<nav class="encounters flexrow" id="l5r5e_gm_combat_tracker_bar">
<div class="encounter">
{{#each encounterTypeList}}
<a class="encounter encounter-control" data-id="{{this}}">
<i class="fa fas encounter-icon-{{this}}{{#ifCond this '==' ../encounterType}} active{{/ifCond}}" title="{{localize (localize 'l5r5e.conflict.initiative.{id}' id=this)}}"></i>
</a>
{{/each}}
</div>
<div class="prepared">
{{#each prepared as |prepared charType|}}
<a class="encounter prepared-control" data-id="{{charType}}">
<i class="fa fas prepared-icon-{{prepared}} prepared-{{charType}}" title="{{localize (localize 'l5r5e.conflict.initiative.prepared_{value}' value=prepared)}} ({{localize (localize 'l5r5e.character_types.{type}' type=charType)}})"></i>
</a>
{{/each}}
</div>
</nav>