implements lootable creatures

pull/61/head
macniel 2025-10-26 17:44:08 +01:00
parent 2da34b6af3
commit a57cf98aa1
11 changed files with 281 additions and 74 deletions

View File

@ -50,6 +50,7 @@ const convert = function (from, to, ofType, overwrite = true) {
if (statSync(join(source, file)).isDirectory()) {
filewalker(join(source, file));
} else {
console.debug("processing file", join(source, file))
let originalSource = JSON.parse(readFileSync(join(source, file), {encoding: "utf8"}));
let id = randomID();

View File

@ -88,6 +88,76 @@ Hooks.once("init", () => {
decimals: 0
}
const setMovementSpeeds = () => {
const movementActions = CONFIG.Token.movement.actions
for (const key of ["swim", "climb", "crawl", "walk", "drive", "ride", "fly"]) {
delete movementActions[key]?.getCostFunction
}
movementActions.climb.canSelect = (token) => {
const actor = token.actor | null;
console.log
return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Klettern")?.system.taw > 0) || actor.type === "Creature"
}
movementActions.crawl.canSelect = (token) => {
const actor = token.actor | null;
return actor.type === "Character" || actor.type === "Creature"
}
movementActions.walk.canSelect = (token) => {
const actor = token.actor | null;
return actor.type === "Character" || actor.type === "Creature"
}
movementActions.swim = {
label: "TOKEN.MOVEMENT.ACTIONS.swim.label",
icon: "fa-solid fa-swim",
order: 0,
canSelect: (token) => {
const actor = token.actor | null;
return actor.type === "Character" || actor.type === "Creature"
},
deriveTerrainDifficulty: () => 1,
}
movementActions.drive = {
label: "TOKEN.MOVEMENT.ACTIONS.drive.label",
icon: "fa-solid fa-car-side",
order: 0,
canSelect: (token) => {
const actor = token.actor | null;
return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Fahrzeuge lenken")?.system.taw > 0) || actor.type === "Creature"
},
deriveTerrainDifficulty: () => 1,
}
movementActions.ride = {
label: "TOKEN.MOVEMENT.ACTIONS.ride.label",
icon: "fa-solid fa-horse",
order: 0,
canSelect: (token) => {
const actor = token.actor | null;
return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Reiten")?.system.taw > 0) || actor.type === "Creature"
},
deriveTerrainDifficulty: () => 1,
}
movementActions.fly = {
label: "TOKEN.MOVEMENT.ACTIONS.fly.label",
icon: "fa-solid fa-wings",
order: 0,
canSelect: (token) => {
const actor = token.actor | null;
return (actor.type === "Character" && actor.system.itemTypes["Skill"].find(p => p.name === "Fliegen")?.system.taw > 0) || actor.type === "Creature"
},
deriveTerrainDifficulty: () => 1,
}
}
setMovementSpeeds()
console.log("DSA 4.1 is ready for development!")
foundry.documents.collections.Actors.registerSheet('dsa41.character', CharacterSheet, {

View File

@ -1,5 +1,5 @@
const {
SchemaField, NumberField, StringField, HTMLField, ArrayField
SchemaField, BooleanField, NumberField, StringField, HTMLField, ArrayField
} = foundry.data.fields;
export class CreatureDataModel extends foundry.abstract.TypeDataModel {
@ -24,7 +24,13 @@ export class CreatureDataModel extends foundry.abstract.TypeDataModel {
tp: new StringField(),
name: new StringField(),
}), {min: 0}),
description: new HTMLField()
description: new HTMLField(),
visibility: new SchemaField({
meta: new BooleanField(),
attacks: new BooleanField(),
description: new BooleanField(),
loot: new BooleanField(),
})
}
}

View File

@ -220,19 +220,19 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
}
_getTabsConfig(group) {
const tabs = foundry.utils.deepClone(super._getTabsConfig(group))
Meta._getTabConfig(tabs, this);
Social._getTabConfig(tabs, this);
Advsf._getTabConfig(tabs, this)
Combat._getTabConfig(tabs, this)
Equipment._getTabConfig(tabs, this)
Skills._getTabConfig(tabs, this)
Spells._getTabConfig(tabs, this)
Liturgies._getTabConfig(tabs, this)
Effects._getTabConfig(tabs, this)
return tabs
}
_getTabsConfig(group) {
const tabs = foundry.utils.deepClone(super._getTabsConfig(group))
Meta._getTabConfig(tabs, this);
Social._getTabConfig(tabs, this);
Advsf._getTabConfig(tabs, this)
Combat._getTabConfig(tabs, this)
Equipment._getTabConfig(tabs, this)
Skills._getTabConfig(tabs, this)
Spells._getTabConfig(tabs, this)
Liturgies._getTabConfig(tabs, this)
Effects._getTabConfig(tabs, this)
return tabs
}
async _preparePartContext(partId, context) {
switch (partId) {
@ -494,8 +494,10 @@ class CharacterSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
async _onDrop(event) {
const data = TextEditor.implementation.getDragEventData(event);
const actor = this.actor;
const allowed = Hooks.call("dropActorSheetData", actor, this, data);
if (allowed === false) return;
//const allowed = Hooks.call("dropActorSheetData", actor, this, data);
//if (allowed === false) return;
console.log("looted or dropped", data)
// Dropped Documents
const documentClass = foundry.utils.getDocumentClass(data.type);

View File

@ -24,11 +24,7 @@ export class CreatureSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
static TABS = {
sheet: {
tabs: [
{id: 'meta', group: 'sheet', label: 'Meta'},
{id: 'attacks', group: 'sheet', label: 'Attacken'},
{id: 'description', group: 'sheet', label: 'Beschreibung'},
],
tabs: [],
initial: 'meta'
}
}
@ -46,7 +42,31 @@ export class CreatureSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
},
description: {
template: `systems/DSA_4-1/templates/actor/creature/tab-description.hbs`
},
loot: {
template: `systems/DSA_4-1/templates/actor/creature/tab-loot.hbs`
},
visibility: {
template: `systems/DSA_4-1/templates/actor/creature/tab-visibility.hbs`
},
}
_getTabsConfig(group) {
const tabs = foundry.utils.deepClone(super._getTabsConfig(group))
const tabDef = [
{id: 'meta', group: 'sheet', label: 'Meta'},
{id: 'attacks', group: 'sheet', label: 'Attacken'},
{id: 'description', group: 'sheet', label: 'Beschreibung'},
{id: 'loot', group: 'sheet', label: 'Loot'},
{id: 'visibility', group: 'sheet', label: 'Sichtbarkeit'},
]
for (const tab of tabDef) {
if (game.user.isGM || this.document.system.visibility[tab.id]) {
tabs.tabs.push(tab)
}
}
return tabs
}
/**
@ -125,7 +145,74 @@ export class CreatureSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
})
})
context.metaRevealed = actorData.system.visibility.meta
context.attacksRevealed = actorData.system.visibility.attacks
context.descriptionRevealed = actorData.system.visibility.description
context.lootRevealed = actorData.system.visibility.loot
context.inventoryItems = []
actorData.items.forEach((item, index) => {
if (item.type === "Equipment") {
context.inventoryItems.push({
index: index,
id: item._id,
quantity: item.system.quantity,
name: item.name,
icon: item.img
})
}
})
return context;
}
_canDragDrop(event, options) {
return game.user.isGM
}
_canDrag(event, options) {
return true
}
async _onDrop(event) {
const data = TextEditor.implementation.getDragEventData(event);
const actor = this.actor;
//const allowed = Hooks.call("dropActorSheetData", actor, this, data);
// if (allowed === false) return;
console.log("dropped")
// Dropped Documents
const documentClass = foundry.utils.getDocumentClass(data.type);
if (documentClass) {
const document = await documentClass.fromDropData(data);
if (document.type === "Equipment") {
// No duplication by moving items from one actor to another
if (document.parent && document.parent !== this.actor) {
document.parent.items.get(document._id).delete()
}
await this._onDropDocument(event, document);
}
}
}
_onRender(context, options) {
new foundry.applications.ux.DragDrop.implementation({
dropSelector: ".window-content",
dragSelector: ".equipment",
permissions: {
drop: this._canDragDrop.bind(this),
drag: this._canDrag.bind(this)
},
callbacks: {
dragstart: this._onDragStart.bind(this),
drop: this._onDrop.bind(this)
}
}).bind(this.element);
}
}

View File

@ -222,8 +222,7 @@ export class MerchantSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
}
_canDragDrop(event, options) {
console.log(event, options)
return true
return game.user.isGM
}
async _onDrop(event) {

View File

@ -0,0 +1,54 @@
@use "../atoms/assets";
@mixin equipment {
position: relative;
height: 32px;
display: grid;
grid-template-columns: 32px 24px 1fr 48px;
grid-template-rows: 1fr;
padding: 2px 0 0 2px;
margin: 4px 0 0 4px;
gap: 8px;
background: assets.$tab-pane-background;
.icon {
width: 32px;
padding: 0;
}
.name, .weight {
height: 32px;
line-height: 32px;
vertical-align: middle;
}
input.quantity {
padding: 0;
border: unset;
border-radius: 0;
height: 24px;
width: 24px;
margin: 4px 0;
text-align: right;
background: unset;
box-shadow: unset;
&:hover {
text-decoration: underline;
}
}
transition: box-shadow 0.2s, border 0.2s, margin 0.2s, padding 0.2s;
&:hover {
.name {
text-shadow: 0 0 10px rgb(255 0 0);
}
border: 1px solid #ccc;
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5);
margin: -4px 4px 4px -4px;
padding: 0 0 0 0;
z-index: 2;
}
}

View File

@ -1,6 +1,7 @@
@use "../atoms/colours" as colour;
@use 'sass:color';
@use "../atoms/numbers";
@use "../molecules/equipment";
.dsa41.sheet.actor.creature {
@ -163,6 +164,16 @@
padding: 16px;
}
.tab.loot {
div {
.equipment {
@include equipment.equipment
}
}
}
}
}

View File

@ -1,4 +1,5 @@
@use "../../atoms/assets";
@use "../../molecules/equipment";
@mixin tab {
@ -49,56 +50,7 @@
flex-direction: column;
.equipment {
position: relative;
height: 32px;
display: grid;
grid-template-columns: 32px 24px 1fr 48px;
grid-template-rows: 1fr;
padding: 2px 0 0 2px;
margin: 4px 0 0 4px;
gap: 8px;
background: assets.$tab-pane-background;
.icon {
width: 32px;
padding: 0;
}
.name, .weight {
height: 32px;
line-height: 32px;
vertical-align: middle;
}
input.quantity {
padding: 0;
border: unset;
border-radius: 0;
height: 24px;
width: 24px;
margin: 4px 0;
text-align: right;
background: unset;
box-shadow: unset;
&:hover {
text-decoration: underline;
}
}
transition: box-shadow 0.2s, border 0.2s, margin 0.2s, padding 0.2s;
&:hover {
.name {
text-shadow: 0 0 10px rgb(255 0 0);
}
border: 1px solid #ccc;
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5);
margin: -4px 4px 4px -4px;
padding: 0 0 0 0;
z-index: 2;
}
@include equipment.equipment
}
}
}

View File

@ -0,0 +1,7 @@
<section class="tab {{tabs.loot.id}} {{tabs.loot.cssClass}}"
data-tab="{{tabs.loot.id}}"
data-group="{{tabs.loot.group}}">
<div>
{{> "systems/DSA_4-1/templates/ui/partial-equipment-button.hbs" inventoryItems}}
</div>
</section>

View File

@ -0,0 +1,18 @@
<section class="tab {{tabs.visibility.id}} {{tabs.visibility.cssClass}}"
data-tab="{{tabs.visibility.id}}"
data-group="{{tabs.visibility.group}}">
<div>
<fieldset>
<legend>Folgendes für alle sichtbar machen</legend>
<label><input type="checkbox" name="system.visibility.meta" {{checked metaRevealed}}/> Meta</label>
<label><input type="checkbox" name="system.visibility.attacks" {{checked attacksRevealed}}/>
Attacken</label>
<label><input type="checkbox" name="system.visibility.description" {{checked descriptionRevealed}}/>
Beschreibung</label>
<label><input type="checkbox" name="system.visibility.loot" {{checked lootRevealed}}/> Loot</label>
<label><input type="checkbox" disabled="disabled"/> Sichtbarkeiten</label>
</fieldset>
</div>
</section>