ENH: Initiative and licensing
parent
83a1c7b717
commit
f8265fc4ed
11
README.md
11
README.md
|
@ -1,11 +1,16 @@
|
||||||
# Old School Essentials System for Foundry VTT
|
# Old School Essentials System for Foundry VTT
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
You can now find this Foundry VTT game system within Foundry VTT in the system browser.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Old-School Essentials is a trademark of Necrotic Gnome.
|
This Foundry VTT system requires Old-School Essentials Core Rules that you can find here [here](https://necroticgnome.com).
|
||||||
This Foundry VTT system requires Old-School Essentials Core Rules and does not contain any copyrighted material.
|
|
||||||
|
This third party product is not affiliated with or approved by Necrotic Gnome.
|
||||||
|
Old-School Essentials is a trademark of Necrotic Gnome.The trademark and Old-School Essentials logo are used with permission of Necrotic Gnome, under license
|
||||||
|
|
||||||
## Contributions
|
## Contributions
|
||||||
This system is currently under heavy development.
|
This system is currently in Beta.
|
||||||
Feel free to grab a TO DO issue from the gitlab board. You can then do a merge request on the `development` branch.
|
Feel free to grab a TO DO issue from the gitlab board. You can then do a merge request on the `development` branch.
|
||||||
|
|
||||||
[](https://ko-fi.com/H2H21WMKA)
|
[](https://ko-fi.com/H2H21WMKA)
|
|
@ -72,6 +72,7 @@
|
||||||
"OSE.HitDice": "Hit Dice",
|
"OSE.HitDice": "Hit Dice",
|
||||||
"OSE.HitDiceShort": "HD",
|
"OSE.HitDiceShort": "HD",
|
||||||
"OSE.Movement": "Movement Rate",
|
"OSE.Movement": "Movement Rate",
|
||||||
|
"OSE.MovementDetails": "Movement Details",
|
||||||
"OSE.MovementEncounter": "Encounter Movement Rate",
|
"OSE.MovementEncounter": "Encounter Movement Rate",
|
||||||
"OSE.MovementEncounterShort": "En",
|
"OSE.MovementEncounterShort": "En",
|
||||||
"OSE.MovementOverland": "Overland Movement Rate",
|
"OSE.MovementOverland": "Overland Movement Rate",
|
||||||
|
@ -197,5 +198,13 @@
|
||||||
"OSE.messages.AttackFailure": "<b>Attack fails</b> ({bonus})",
|
"OSE.messages.AttackFailure": "<b>Attack fails</b> ({bonus})",
|
||||||
"OSE.messages.InflictsDamage": "Inflicts damage!",
|
"OSE.messages.InflictsDamage": "Inflicts damage!",
|
||||||
"OSE.ChatContextDamage": "Apply Damage",
|
"OSE.ChatContextDamage": "Apply Damage",
|
||||||
"OSE.ChatContextHealing": "Apply Healing"
|
"OSE.ChatContextHealing": "Apply Healing",
|
||||||
|
|
||||||
|
"OSE.colors.green": "Green",
|
||||||
|
"OSE.colors.red": "Red",
|
||||||
|
"OSE.colors.yellow": "Yellow",
|
||||||
|
"OSE.colors.purple": "Purple",
|
||||||
|
"OSE.colors.blue": "Blue",
|
||||||
|
"OSE.colors.orange": "Orange",
|
||||||
|
"OSE.colors.white": "White"
|
||||||
}
|
}
|
|
@ -110,7 +110,9 @@ export class OseActorSheetCharacter extends OseActorSheet {
|
||||||
|
|
||||||
_calculateMovement(data, weight) {
|
_calculateMovement(data, weight) {
|
||||||
if (data.config.encumbrance == "detailed") {
|
if (data.config.encumbrance == "detailed") {
|
||||||
if (weight > 800) {
|
if (weight > data.encumbrance.max) {
|
||||||
|
data.data.movement.base = 0;
|
||||||
|
} else if (weight > 800) {
|
||||||
data.data.movement.base = 30;
|
data.data.movement.base = 30;
|
||||||
} else if (weight > 600) {
|
} else if (weight > 600) {
|
||||||
data.data.movement.base = 60;
|
data.data.movement.base = 60;
|
||||||
|
|
|
@ -80,7 +80,6 @@ export class OseActorSheetMonster extends OseActorSheet {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onCountChange(event) {
|
async _onCountChange(event) {
|
||||||
console.log("CHANGE", event);
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const itemId = event.currentTarget.closest(".item").dataset.itemId;
|
const itemId = event.currentTarget.closest(".item").dataset.itemId;
|
||||||
const item = this.actor.getOwnedItem(itemId);
|
const item = this.actor.getOwnedItem(itemId);
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
import { OseDice } from "./dice.js";
|
||||||
|
|
||||||
|
export class OseCombat {
|
||||||
|
static rollInitiative(combat, data, diff, id) {
|
||||||
|
// Check groups
|
||||||
|
data.combatants = [];
|
||||||
|
let groups = {};
|
||||||
|
combat.data.combatants.forEach((cbt) => {
|
||||||
|
groups[cbt.flags.ose.group] = {present: true};
|
||||||
|
data.combatants.push(cbt);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Roll init
|
||||||
|
Object.keys(groups).forEach((group) => {
|
||||||
|
let roll = new Roll("1d6").roll();
|
||||||
|
roll.toMessage({flavor: `${CONFIG.OSE.colors[group]} group rolls initiative`});
|
||||||
|
groups[group].initiative = roll.total;
|
||||||
|
})
|
||||||
|
|
||||||
|
// Set init
|
||||||
|
for (let i = 0; i < data.combatants.length; ++i) {
|
||||||
|
data.combatants[i].initiative = groups[data.combatants[i].flags.ose.group].initiative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static format(object, html, user) {
|
||||||
|
html.find('.combat-control[data-control="rollNPC"]').remove();
|
||||||
|
html.find('.combat-control[data-control="rollAll"]').remove();
|
||||||
|
html.find(".combatant").each((_, ct) => {
|
||||||
|
// Can't roll individual inits
|
||||||
|
$(ct).find(".roll").remove();
|
||||||
|
|
||||||
|
// Get group color
|
||||||
|
let cmbtant = object.combat.getCombatant(ct.dataset.combatantId);
|
||||||
|
let color = cmbtant.flags.ose.group;
|
||||||
|
|
||||||
|
// Append colored flag
|
||||||
|
let controls = $(ct).find(".combatant-controls");
|
||||||
|
controls.prepend(
|
||||||
|
`<a class='combatant-control flag' style='color:${color}' title="${CONFIG.OSE.colors[color]}"><i class='fas fa-flag'></i></a>`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
OseCombat.addListeners(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
static addListeners(html) {
|
||||||
|
// Cycle through colors
|
||||||
|
html.find(".combatant-control.flag").click((ev) => {
|
||||||
|
let currentColor = ev.currentTarget.style.color;
|
||||||
|
let colors = Object.keys(CONFIG.OSE.colors);
|
||||||
|
let index = colors.indexOf(currentColor);
|
||||||
|
if (index + 1 == colors.length) {
|
||||||
|
index = 0;
|
||||||
|
} else {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
let id = $(ev.currentTarget).closest(".combatant")[0].dataset.combatantId;
|
||||||
|
game.combat.updateCombatant({
|
||||||
|
_id: id,
|
||||||
|
flags: { ose: { group: colors[index] } },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static addCombatant(combat, data, options, id) {
|
||||||
|
let token = canvas.tokens.get(data.tokenId);
|
||||||
|
let color = "black";
|
||||||
|
switch (token.data.disposition) {
|
||||||
|
case -1:
|
||||||
|
color = "red";
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
color = "yellow";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
color = "green";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data.flags = {
|
||||||
|
ose: {
|
||||||
|
group: color,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,5 +26,14 @@ export const OSE = {
|
||||||
light: "OSE.armor.light",
|
light: "OSE.armor.light",
|
||||||
heavy: "OSE.armor.heavy",
|
heavy: "OSE.armor.heavy",
|
||||||
shield: "OSE.armor.shield",
|
shield: "OSE.armor.shield",
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
green: "OSE.colors.green",
|
||||||
|
red: "OSE.colors.red",
|
||||||
|
yellow: "OSE.colors.yellow",
|
||||||
|
purple: "OSE.colors.purple",
|
||||||
|
blue: "OSE.colors.blue",
|
||||||
|
orange: "OSE.colors.orange",
|
||||||
|
white: "OSE.colors.white"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
39
src/ose.js
39
src/ose.js
|
@ -10,6 +10,7 @@ import { registerSettings } from "./module/settings.js";
|
||||||
import { registerHelpers } from "./module/helpers.js";
|
import { registerHelpers } from "./module/helpers.js";
|
||||||
import * as chat from "./module/chat.js";
|
import * as chat from "./module/chat.js";
|
||||||
import * as macros from "./module/macros.js";
|
import * as macros from "./module/macros.js";
|
||||||
|
import { OseCombat } from "./module/combat.js";
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
/* Foundry VTT Initialization */
|
/* Foundry VTT Initialization */
|
||||||
|
@ -61,7 +62,7 @@ Hooks.once("init", async function () {
|
||||||
*/
|
*/
|
||||||
Hooks.once("setup", function () {
|
Hooks.once("setup", function () {
|
||||||
// Localize CONFIG objects once up-front
|
// Localize CONFIG objects once up-front
|
||||||
const toLocalize = ["saves_short", "saves_long", "scores", "armor"];
|
const toLocalize = ["saves_short", "saves_long", "scores", "armor", "colors"];
|
||||||
for (let o of toLocalize) {
|
for (let o of toLocalize) {
|
||||||
CONFIG.OSE[o] = Object.entries(CONFIG.OSE[o]).reduce((obj, e) => {
|
CONFIG.OSE[o] = Object.entries(CONFIG.OSE[o]).reduce((obj, e) => {
|
||||||
obj[e[0]] = game.i18n.localize(e[1]);
|
obj[e[0]] = game.i18n.localize(e[1]);
|
||||||
|
@ -74,21 +75,37 @@ Hooks.once("ready", async () => {
|
||||||
Hooks.on("hotbarDrop", (bar, data, slot) =>
|
Hooks.on("hotbarDrop", (bar, data, slot) =>
|
||||||
macros.createOseMacro(data, slot)
|
macros.createOseMacro(data, slot)
|
||||||
);
|
);
|
||||||
const template = 'systems/ose/templates/chat/license.html';
|
const template = "systems/ose/templates/chat/license.html";
|
||||||
const html = await renderTemplate(template);
|
const html = await renderTemplate(template);
|
||||||
$('#settings .game-system').append(html);
|
$("#settings .game-system").append(html);
|
||||||
});
|
});
|
||||||
|
|
||||||
Hooks.on(
|
Hooks.on("preCreateCombatant", (combat, data, options, id) => {
|
||||||
"preUpdateCombat",
|
OseCombat.addCombatant(combat, data, options, id);
|
||||||
async (combat, updateData, options, userId) => {
|
});
|
||||||
if (!updateData.round) {
|
|
||||||
|
Hooks.on("preUpdateCombatant", (combat, combatant, data, diff, id) => {
|
||||||
|
if (data.initiative) {
|
||||||
|
let groupInit = data.initiative;
|
||||||
|
combat.combatants.forEach((ct) => {
|
||||||
|
if (ct.initiative && ct._id != data._id && ct.flags.ose.group == combatant.flags.ose.group) {
|
||||||
|
groupInit = ct.initiative;
|
||||||
|
data.initiative = parseInt(groupInit);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Hooks.on("renderCombatTracker", (object, html, data) => {
|
||||||
|
OseCombat.format(object, html, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
Hooks.on("preUpdateCombat", async (combat, data, diff, id) => {
|
||||||
|
if (!data.round) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (game.settings.get('ose', 'individualInit')) {
|
OseCombat.rollInitiative(combat, data, diff, id);
|
||||||
}
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
Hooks.on("renderChatLog", (app, html, data) => OseItem.chatListeners(html));
|
Hooks.on("renderChatLog", (app, html, data) => OseItem.chatListeners(html));
|
||||||
Hooks.on("getChatLogEntryContext", chat.addChatMessageContextOptions);
|
Hooks.on("getChatLogEntryContext", chat.addChatMessageContextOptions);
|
||||||
|
|
|
@ -46,7 +46,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"movement": {
|
"movement": {
|
||||||
"base": 120
|
"base": 120,
|
||||||
|
"value": ""
|
||||||
},
|
},
|
||||||
"initiative": {
|
"initiative": {
|
||||||
"value": 0,
|
"value": 0,
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
<h4 class="attribute-name box-title" title="{{localize 'OSE.Movement'}}">{{ localize "OSE.MovementShort" }}
|
<h4 class="attribute-name box-title" title="{{localize 'OSE.Movement'}}">{{ localize "OSE.MovementShort" }}
|
||||||
</h4>
|
</h4>
|
||||||
<div class="attribute-value">
|
<div class="attribute-value">
|
||||||
<input name="data.movement.value" type="text" value="{{data.movement.value}}" placeholder="0"
|
<input name="data.movement.base" type="text" value="{{data.movement.base}}" placeholder="0"
|
||||||
data-dtype="Number" />
|
data-dtype="Number" />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -138,8 +138,8 @@
|
||||||
{{!-- Saving throws --}}
|
{{!-- Saving throws --}}
|
||||||
<div class="attribute-group">
|
<div class="attribute-group">
|
||||||
<div class="attacks-description">
|
<div class="attacks-description">
|
||||||
<label>{{ localize "OSE.Attacks" }}</label>
|
<label>{{ localize "OSE.MovementDetails" }}</label>
|
||||||
<input name="data.att" type="text" value="{{data.att}}" data-dtype="String" />
|
<input name="data.movement.value" type="text" value="{{data.movement.value}}" data-dtype="String" />
|
||||||
</div>
|
</div>
|
||||||
<ul class="attributes">
|
<ul class="attributes">
|
||||||
<li class="attribute saving-throw" data-save="death">
|
<li class="attribute saving-throw" data-save="death">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="ose game-license">
|
<div class="ose game-license">
|
||||||
<p class="ose game-license">
|
<p class="ose game-license">
|
||||||
This fan-made system requires requires Old-School Essentials Core Rules that
|
This fan-made system requires Old-School Essentials Core Rules that
|
||||||
you can find <a href="https://necroticgnome.com">here</a>.
|
you can find <a href="https://necroticgnome.com">here</a>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
Loading…
Reference in New Issue