forked from GitHub-Mirrors/foundry-sw5e
beginning npc sheet
This commit is contained in:
parent
27f5fa3670
commit
acd0e151e7
5 changed files with 326 additions and 4 deletions
|
@ -585,6 +585,7 @@
|
||||||
"SW5E.SheetClassCharacter": "Default Character Sheet",
|
"SW5E.SheetClassCharacter": "Default Character Sheet",
|
||||||
"SW5E.SheetClassCharacterOld": "Old Character Sheet",
|
"SW5E.SheetClassCharacterOld": "Old Character Sheet",
|
||||||
"SW5E.SheetClassNPC": "Default NPC Sheet",
|
"SW5E.SheetClassNPC": "Default NPC Sheet",
|
||||||
|
"SW5E.SheetClassNPCOld": "Old NPC Sheet",
|
||||||
"SW5E.SheetClassVehicle": "Default Vehicle Sheet",
|
"SW5E.SheetClassVehicle": "Default Vehicle Sheet",
|
||||||
"SW5E.SheetClassItem": "Default Item Sheet",
|
"SW5E.SheetClassItem": "Default Item Sheet",
|
||||||
|
|
||||||
|
|
132
module/actor/sheets/newSheet/npc.js
Normal file
132
module/actor/sheets/newSheet/npc.js
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
import ActorSheet5e from "../base.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Actor sheet for NPC type characters in the SW5E system.
|
||||||
|
* Extends the base ActorSheet5e class.
|
||||||
|
* @extends {ActorSheet5e}
|
||||||
|
*/
|
||||||
|
export default class ActorSheet5eNPCNew extends ActorSheet5e {
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
get template() {
|
||||||
|
if ( !game.user.isGM && this.actor.limited ) return "systems/sw5e/templates/actors/newActor/limited-sheet.html";
|
||||||
|
return `systems/sw5e/templates/actors/newActor/npc-sheet.html`;
|
||||||
|
}
|
||||||
|
/** @override */
|
||||||
|
static get defaultOptions() {
|
||||||
|
return mergeObject(super.defaultOptions, {
|
||||||
|
classes: ["sw5e", "sheet", "actor", "npc"],
|
||||||
|
width: 600,
|
||||||
|
height: 680
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Organize Owned Items for rendering the NPC sheet
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_prepareItems(data) {
|
||||||
|
|
||||||
|
// Categorize Items as Features and Powers
|
||||||
|
const features = {
|
||||||
|
weapons: { label: game.i18n.localize("SW5E.AttackPl"), items: [] , hasActions: true, dataset: {type: "weapon", "weapon-type": "natural"} },
|
||||||
|
actions: { label: game.i18n.localize("SW5E.ActionPl"), items: [] , hasActions: true, dataset: {type: "feat", "activation.type": "action"} },
|
||||||
|
passive: { label: game.i18n.localize("SW5E.Features"), items: [], dataset: {type: "feat"} },
|
||||||
|
equipment: { label: game.i18n.localize("SW5E.Inventory"), items: [], dataset: {type: "loot"}}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start by classifying items into groups for rendering
|
||||||
|
let [powers, other] = data.items.reduce((arr, item) => {
|
||||||
|
item.img = item.img || DEFAULT_TOKEN;
|
||||||
|
item.isStack = Number.isNumeric(item.data.quantity) && (item.data.quantity !== 1);
|
||||||
|
item.hasUses = item.data.uses && (item.data.uses.max > 0);
|
||||||
|
item.isOnCooldown = item.data.recharge && !!item.data.recharge.value && (item.data.recharge.charged === false);
|
||||||
|
item.isDepleted = item.isOnCooldown && (item.data.uses.per && (item.data.uses.value > 0));
|
||||||
|
item.hasTarget = !!item.data.target && !(["none",""].includes(item.data.target.type));
|
||||||
|
if ( item.type === "power" ) arr[0].push(item);
|
||||||
|
else arr[1].push(item);
|
||||||
|
return arr;
|
||||||
|
}, [[], []]);
|
||||||
|
|
||||||
|
// Apply item filters
|
||||||
|
powers = this._filterItems(powers, this._filters.powerbook);
|
||||||
|
other = this._filterItems(other, this._filters.features);
|
||||||
|
|
||||||
|
// Organize Powerbook
|
||||||
|
const powerbook = this._preparePowerbook(data, powers);
|
||||||
|
|
||||||
|
// Organize Features
|
||||||
|
for ( let item of other ) {
|
||||||
|
if ( item.type === "weapon" ) features.weapons.items.push(item);
|
||||||
|
else if ( item.type === "feat" ) {
|
||||||
|
if ( item.data.activation.type ) features.actions.items.push(item);
|
||||||
|
else features.passive.items.push(item);
|
||||||
|
}
|
||||||
|
else features.equipment.items.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign and return
|
||||||
|
data.features = Object.values(features);
|
||||||
|
data.powerbook = powerbook;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
getData() {
|
||||||
|
const data = super.getData();
|
||||||
|
|
||||||
|
// Challenge Rating
|
||||||
|
const cr = parseFloat(data.data.details.cr || 0);
|
||||||
|
const crLabels = {0: "0", 0.125: "1/8", 0.25: "1/4", 0.5: "1/2"};
|
||||||
|
data.labels["cr"] = cr >= 1 ? String(cr) : crLabels[cr] || 1;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Object Updates */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_updateObject(event, formData) {
|
||||||
|
|
||||||
|
// Format NPC Challenge Rating
|
||||||
|
const crs = {"1/8": 0.125, "1/4": 0.25, "1/2": 0.5};
|
||||||
|
let crv = "data.details.cr";
|
||||||
|
let cr = formData[crv];
|
||||||
|
cr = crs[cr] || parseFloat(cr);
|
||||||
|
if ( cr ) formData[crv] = cr < 1 ? cr : parseInt(cr);
|
||||||
|
|
||||||
|
// Parent ActorSheet update steps
|
||||||
|
super._updateObject(event, formData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Event Listeners and Handlers */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
activateListeners(html) {
|
||||||
|
super.activateListeners(html);
|
||||||
|
html.find(".health .rollable").click(this._onRollHealthFormula.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle rolling NPC health values using the provided formula
|
||||||
|
* @param {Event} event The original click event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_onRollHealthFormula(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const formula = this.actor.data.data.attributes.hp.formula;
|
||||||
|
if ( !formula ) return;
|
||||||
|
const hp = new Roll(formula).roll().total;
|
||||||
|
AudioHelper.play({src: CONFIG.sounds.dice});
|
||||||
|
this.actor.update({"data.attributes.hp.value": hp, "data.attributes.hp.max": hp});
|
||||||
|
}
|
||||||
|
}
|
9
sw5e.js
9
sw5e.js
|
@ -26,6 +26,7 @@ import ActorSheet5eCharacter from "./module/actor/sheets/oldSheets/character.js"
|
||||||
import ActorSheet5eNPC from "./module/actor/sheets/oldSheets/npc.js";
|
import ActorSheet5eNPC from "./module/actor/sheets/oldSheets/npc.js";
|
||||||
import ActorSheet5eVehicle from "./module/actor/sheets/oldSheets/vehicle.js";
|
import ActorSheet5eVehicle from "./module/actor/sheets/oldSheets/vehicle.js";
|
||||||
import ActorSheet5eCharacterNew from "./module/actor/sheets/newSheet/character.js";
|
import ActorSheet5eCharacterNew from "./module/actor/sheets/newSheet/character.js";
|
||||||
|
import ActorSheet5eNPCNew from "./module/actor/sheets/newSheet/npc.js";
|
||||||
import ItemSheet5e from "./module/item/sheet.js";
|
import ItemSheet5e from "./module/item/sheet.js";
|
||||||
import ShortRestDialog from "./module/apps/short-rest.js";
|
import ShortRestDialog from "./module/apps/short-rest.js";
|
||||||
import TraitSelector from "./module/apps/trait-selector.js";
|
import TraitSelector from "./module/apps/trait-selector.js";
|
||||||
|
@ -51,6 +52,7 @@ Hooks.once("init", function() {
|
||||||
ActorSheet5eCharacter,
|
ActorSheet5eCharacter,
|
||||||
ActorSheet5eCharacterNew,
|
ActorSheet5eCharacterNew,
|
||||||
ActorSheet5eNPC,
|
ActorSheet5eNPC,
|
||||||
|
ActorSheet5eNPCNew,
|
||||||
ActorSheet5eVehicle,
|
ActorSheet5eVehicle,
|
||||||
ItemSheet5e,
|
ItemSheet5e,
|
||||||
ShortRestDialog,
|
ShortRestDialog,
|
||||||
|
@ -99,11 +101,16 @@ Hooks.once("init", function() {
|
||||||
makeDefault: false,
|
makeDefault: false,
|
||||||
label: "SW5E.SheetClassCharacterOld"
|
label: "SW5E.SheetClassCharacterOld"
|
||||||
});
|
});
|
||||||
Actors.registerSheet("sw5e", ActorSheet5eNPC, {
|
Actors.registerSheet("sw5e", ActorSheet5eNPCNew, {
|
||||||
types: ["npc"],
|
types: ["npc"],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
label: "SW5E.SheetClassNPC"
|
label: "SW5E.SheetClassNPC"
|
||||||
});
|
});
|
||||||
|
Actors.registerSheet("sw5e", ActorSheet5eNPC, {
|
||||||
|
types: ["npc"],
|
||||||
|
makeDefault: false,
|
||||||
|
label: "SW5E.SheetClassNPCOld"
|
||||||
|
});
|
||||||
Actors.registerSheet('sw5e', ActorSheet5eVehicle, {
|
Actors.registerSheet('sw5e', ActorSheet5eVehicle, {
|
||||||
types: ['vehicle'],
|
types: ['vehicle'],
|
||||||
makeDefault: true,
|
makeDefault: true,
|
||||||
|
|
182
templates/actors/newActor/npc-sheet.html
Normal file
182
templates/actors/newActor/npc-sheet.html
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
<form class="{{cssClass}} swalt-sheet" autocomplete="off">
|
||||||
|
|
||||||
|
{{!-- NPC Sheet Header --}}
|
||||||
|
<header class="panel">
|
||||||
|
<img class="profile" src="{{actor.img}}" title="{{actor.name}}" data-edit="img"/>
|
||||||
|
<h1 class="character-name">
|
||||||
|
<input name="name" type="text" value="{{actor.name}}" placeholder="{{ localize 'SW5E.Name' }}" />
|
||||||
|
</h1>
|
||||||
|
<div class="level-experience">
|
||||||
|
<div class="charlevel">
|
||||||
|
{{ localize "SW5E.AbbreviationCR" }}
|
||||||
|
<input name="data.details.cr" type="text" value="{{labels.cr}}" placeholder="1"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="experience">
|
||||||
|
<span class="max">{{data.details.xp.value}} XP</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<section class="header-details flexrow" style="display: none;">
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <aside class="header-exp flexcol">
|
||||||
|
<div class="cr">
|
||||||
|
<label>{{ localize "SW5E.AbbreviationCR" }}</label>
|
||||||
|
<input name="data.details.cr" type="text" value="{{labels.cr}}" placeholder="1"/>
|
||||||
|
</div>
|
||||||
|
<div class="experience">
|
||||||
|
<span>{{data.details.xp.value}} XP</span>
|
||||||
|
</div>
|
||||||
|
</aside> -->
|
||||||
|
|
||||||
|
{{!-- Character Summary --}}
|
||||||
|
<ul class="summary flexrow">
|
||||||
|
<li>
|
||||||
|
<span>{{lookup config.actorSizes data.traits.size}}</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text" name="data.details.alignment" value="{{data.details.alignment}}" placeholder="{{ localize 'SW5E.Alignment' }}"/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text" name="data.details.type" value="{{data.details.type}}" placeholder="{{ localize 'SW5E.Type' }}"/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text" name="data.details.source" value="{{data.details.source}}" placeholder="{{ localize 'SW5E.Source' }}"/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{!-- Header Attributes --}}
|
||||||
|
<ul class="attributes flexrow">
|
||||||
|
<li class="attribute health">
|
||||||
|
<h4 class="attribute-name box-title rollable">{{ localize "SW5E.Health" }}</h4>
|
||||||
|
<div class="attribute-value multiple">
|
||||||
|
<input name="data.attributes.hp.value" type="text" value="{{data.attributes.hp.value}}" placeholder="10" data-dtype="Number"/>
|
||||||
|
<span class="sep"> / </span>
|
||||||
|
<input name="data.attributes.hp.max" type="text" value="{{data.attributes.hp.max}}" placeholder="10" data-dtype="Number"/>
|
||||||
|
</div>
|
||||||
|
<footer class="attribute-footer">
|
||||||
|
<input name="data.attributes.hp.formula" class="hpformula" type="text" placeholder="{{ localize 'SW5E.HealthFormula' }}"
|
||||||
|
value="{{data.attributes.hp.formula}}"/>
|
||||||
|
</footer>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="attribute">
|
||||||
|
<h4 class="attribute-name box-title">{{ localize "SW5E.ArmorClass" }}</h4>
|
||||||
|
<div class="attribute-value">
|
||||||
|
<input name="data.attributes.ac.value" type="number" value="{{data.attributes.ac.value}}" placeholder="10"/>
|
||||||
|
</div>
|
||||||
|
<footer class="attribute-footer">
|
||||||
|
<span>{{ localize "SW5E.Proficiency" }}</span>
|
||||||
|
<span>{{numberFormat data.attributes.prof decimals=0 sign=true}}</span>
|
||||||
|
</footer>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="attribute">
|
||||||
|
<h4 class="attribute-name box-title">{{ localize "SW5E.Speed" }}</h4>
|
||||||
|
<div class="attribute-value">
|
||||||
|
<input name="data.attributes.speed.value" type="text"
|
||||||
|
value="{{data.attributes.speed.value}}" placeholder="0"/>
|
||||||
|
</div>
|
||||||
|
<footer class="attribute-footer">
|
||||||
|
<input type="text" class="speed" name="data.attributes.speed.special"
|
||||||
|
value="{{data.attributes.speed.special}}" placeholder="{{ localize 'SW5E.SpeedSpecial' }}"/>
|
||||||
|
</footer>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{!-- NPC Sheet Navigation --}}
|
||||||
|
<nav class="sheet-navigation tabs" data-group="primary">
|
||||||
|
<a class="item active" data-tab="attributes">{{ localize "SW5E.Attributes" }}</a>
|
||||||
|
<a class="item" data-tab="features">{{ localize "SW5E.Features" }}</a>
|
||||||
|
<a class="item" data-tab="powerbook">{{ localize "SW5E.Powerbook" }}</a>
|
||||||
|
<a class="item" data-tab="biography">{{ localize "SW5E.Biography" }}</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{{!-- NPC Sheet Body --}}
|
||||||
|
<section class="sheet-body">
|
||||||
|
<div class="tab attributes flexrow" data-group="primary" data-tab="attributes">
|
||||||
|
|
||||||
|
{{!-- Ability Scores --}}
|
||||||
|
<ul class="ability-scores flexrow">
|
||||||
|
{{#each data.abilities as |ability id|}}
|
||||||
|
<li class="ability {{#if ability.proficient}}proficient{{/if}}" data-ability="{{id}}">
|
||||||
|
<h4 class="ability-name box-title rollable">{{ability.label}}</h4>
|
||||||
|
<input class="ability-score" name="data.abilities.{{id}}.value" type="number" value="{{ability.value}}" placeholder="10"/>
|
||||||
|
<div class="ability-modifiers flexrow">
|
||||||
|
<span class="ability-mod" title="Modifier">{{numberFormat ability.mod decimals=0 sign=true}}</span>
|
||||||
|
<input type="hidden" name="data.abilities.{{id}}.proficient" value="{{ability.proficient}}" data-dtype="Number"/>
|
||||||
|
<a class="proficiency-toggle ability-proficiency" title="{{ localize 'SW5E.Proficiency' }}">{{{ability.icon}}}</a>
|
||||||
|
<span class="ability-save" title="Saving Throw">{{numberFormat ability.save decimals=0 sign=true}}</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{!-- Skills --}}
|
||||||
|
<ul class="skills-list">
|
||||||
|
{{#each data.skills as |skill s|}}
|
||||||
|
<li class="skill flexrow {{#if skill.value}}proficient{{/if}}" data-skill="{{s}}">
|
||||||
|
<input type="hidden" name="data.skills.{{s}}.value" value="{{skill.value}}" data-dtype="Number"/>
|
||||||
|
<a class="proficiency-toggle skill-proficiency" title="{{skill.hover}}">{{{skill.icon}}}</a>
|
||||||
|
<h4 class="skill-name rollable">{{skill.label}}</h4>
|
||||||
|
<span class="skill-ability">{{skill.ability}}</span>
|
||||||
|
<span class="skill-mod">{{numberFormat skill.total decimals=0 sign=true}}</span>
|
||||||
|
<span class="skill-passive">({{skill.passive}})</span>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<section class="center-pane flexcol">
|
||||||
|
|
||||||
|
{{!-- Legendary Actions --}}
|
||||||
|
<div class="counters">
|
||||||
|
<div class="counter flexrow legendary">
|
||||||
|
<h4>{{ localize "SW5E.LegAct" }}</h4>
|
||||||
|
<div class="counter-value">
|
||||||
|
<input name="data.resources.legact.value" type="number" value="{{data.resources.legact.value}}" placeholder="0"/>
|
||||||
|
<span class="sep">/</span>
|
||||||
|
<input name="data.resources.legact.max" type="number" value="{{data.resources.legact.max}}" placeholder="0"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="counter flexrow legendary">
|
||||||
|
<h4>{{ localize "SW5E.LegRes" }}</h4>
|
||||||
|
<div class="counter-value">
|
||||||
|
<input name="data.resources.legres.value" type="number" value="{{data.resources.legres.value}}" placeholder="0"/>
|
||||||
|
<span class="sep">/</span>
|
||||||
|
<input name="data.resources.legres.max" type="number" value="{{data.resources.legres.max}}" placeholder="0"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="counter flexrow lair">
|
||||||
|
<h4>{{ localize "SW5E.LairAct" }}</h4>
|
||||||
|
<div class="counter-value">
|
||||||
|
<input name="data.resources.lair.value" type="checkbox" value="{{data.resources.lair.value}}"
|
||||||
|
data-dtype="Boolean" {{checked data.resources.lair.value}}/>
|
||||||
|
<input name="data.resources.lair.initiative" type="number" value="{{data.resources.lair.initiative}}" placeholder="20"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Traits --}}
|
||||||
|
{{> "systems/sw5e/templates/actors/oldActor/parts/actor-traits.html"}}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Features Tab --}}
|
||||||
|
<div class="tab features flexcol" data-group="primary" data-tab="features">
|
||||||
|
{{> "systems/sw5e/templates/actors/oldActor/parts/actor-features.html" sections=features}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Powerbook Tab --}}
|
||||||
|
<div class="tab powerbook flexcol" data-group="primary" data-tab="powerbook">
|
||||||
|
{{> "systems/sw5e/templates/actors/oldActor/parts/actor-powerbook.html"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Biography Tab --}}
|
||||||
|
<div class="tab biography flexcol" data-group="primary" data-tab="biography">
|
||||||
|
{{editor content=data.details.biography.value target="data.details.biography.value" button=true owner=owner editable=editable}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
|
@ -149,18 +149,18 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{!-- Traits --}}
|
{{!-- Traits --}}
|
||||||
{{> "systems/sw5e/templates/actors/parts/actor-traits.html"}}
|
{{> "systems/sw5e/templates/actors/oldActor/parts/actor-traits.html"}}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{!-- Features Tab --}}
|
{{!-- Features Tab --}}
|
||||||
<div class="tab features flexcol" data-group="primary" data-tab="features">
|
<div class="tab features flexcol" data-group="primary" data-tab="features">
|
||||||
{{> "systems/sw5e/templates/actors/parts/actor-features.html" sections=features}}
|
{{> "systems/sw5e/templates/actors/oldActor/parts/actor-features.html" sections=features}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{!-- Powerbook Tab --}}
|
{{!-- Powerbook Tab --}}
|
||||||
<div class="tab powerbook flexcol" data-group="primary" data-tab="powerbook">
|
<div class="tab powerbook flexcol" data-group="primary" data-tab="powerbook">
|
||||||
{{> "systems/sw5e/templates/actors/parts/actor-powerbook.html"}}
|
{{> "systems/sw5e/templates/actors/oldActor/parts/actor-powerbook.html"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{!-- Biography Tab --}}
|
{{!-- Biography Tab --}}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue