Subject: Enhance combat mechanics and localization

- Introduced new combat-related properties (initiative, group, unable to act) to characters for an enriched gaming experience.
- Added German translations for newly introduced terms to ensure consistency within the game's interface.
- Capitalized the German translation for "magic" to align with existing convention.
- Refactored settings management in player and NPC sheets for better clarity and maintenance.
- Implemented logic for handling characters' inability to act in combat, including UI elements for managing this state, thereby increasing gameplay depth.
- Adjusted display and management of items and effects to accommodate new combat features, streamlining user interaction and integration with the combat system.
- Minor UI and styling adjustments to enhance readability and user experience across character and item sheets.

These changes aim to make combat encounters more dynamic and engaging, while also improving the user interface for ease of interaction and consistency.
This commit is contained in:
Ender 2024-05-14 13:33:53 +02:00
parent 3ecd2c6c1e
commit aef48dd46b
10 changed files with 129 additions and 13 deletions

View File

@ -20,6 +20,11 @@
"no-encounter": "Kein Kampf",
"encounter-not-started": "Kein aktiver Kampf",
"initiative": "Initiative",
"initiativeRoll": "Initiative würfeln",
"unableToAct": "Handlungsunfähig",
"unablesToAct": "Macht handlungsunfähig",
"group": "Gruppe",
"leader": "Anführer",
"actionrank": "Handlungsrang",
"combat-join": "Kampf Beitreten/Handlungsrang zurücksetzen",
@ -276,7 +281,7 @@
"defensive-weapon": "Verteidigungswaffe",
"defensive-weapons": "Verteidigungswaffen",
"no-skill": "Keine Fertigkeit",
"magic": "magisch",
"magic": "Magisch",
"valuable": "Vermögen",
"equipped": "Ausgerüstet",
"active": "Aktiv",

0
source/combat.ts Normal file
View File

View File

@ -162,6 +162,14 @@ export interface M5AttributeCalculated extends M5ModResult {
export interface M5CharacterCalculatedData {
level: number;
initiative: number;
unableToAct: {
effectId: string;
enabled: boolean;
rounds: number;
reason: string;
};
group: string;
movement: number;
attributes: {
st: M5AttributeCalculated;

View File

@ -1,3 +1,4 @@
import { log } from "console";
import { M5Item } from "../items/M5Item";
import { M5Attribute, M5CharacterCalculatedData, M5ItemMod, M5ItemType, M5ModOperation, M5ModResult, M5RollData, M5Skill, M5SkillCalculated } from "../M5Base";
import M5ModAggregate from "./M5ModAggregate";
@ -86,6 +87,7 @@ export class M5Character extends Actor {
): M5CharacterCalculatedData {
let ret: M5CharacterCalculatedData = {
level: 0,
initiative: 0,
attributes: {
st: { value: 0, bonus: 0, mods: [] },
gs: { value: 0, bonus: 0, mods: [] },
@ -151,6 +153,9 @@ export class M5Character extends Actor {
if (!data) return null;
ret.level = M5Character.levelFromExp(data.info.race === "Zwerg" ? Math.min(data.calc.stats?.hoard * 2 || 0, data.es) : data.es);
ret.initiative = M5Character.initiativeFromAnfuehren(data.calc.skills?.general, (game as Game).i18n.localize("midgard5.anfuehren"), data.skills.general.anfuehren.fw);
ret.unableToAct = M5Character.unableToActFromEffect(data.calc.gear?.effects);
ret.group = M5Character.groupFromDisposition(this);
ret.attributes.st.value = M5Character.attributeMinMax(data.attributes.st); // TODO item effects
ret.attributes.gs.value = M5Character.attributeMinMax(data.attributes.gs);
@ -494,6 +499,7 @@ export class M5Character extends Actor {
magic: item.system.magic,
calc: item.system.calc,
equipped: item.system?.equipped || false,
unablesToAct: item.system?.unablesToAct || false,
duration: item.system?.duration || { time: 0, unit: "" },
};
});
@ -604,6 +610,47 @@ export class M5Character extends Actor {
return ret === -1 ? M5Character.levelThreshold.length : ret;
}
static initiativeFromAnfuehren(list: object, anfuehren: string, unlearned: number): number {
for (const element in list) {
if (list[element].label.toLowerCase() === anfuehren.toLowerCase()) {
return list[element].calc.ew;
}
}
return unlearned;
}
static unableToActFromEffect(list: object): { effectId: string; enabled: boolean; rounds: number; reason: string } {
console.log("Effects:", list);
for (const element in list) {
if (list[element].unablesToAct) {
return {
effectId: list[element],
enabled: list[element].equipped,
rounds: list[element].duration.time,
reason: list[element].label,
};
}
}
return { effectId: "", enabled: false, rounds: 0, reason: "" };
}
static groupFromDisposition(actor: any): string {
console.log("Group:", actor);
let disposition:number = 0;
if (actor.isToken) {
disposition = actor.token.disposition;
} else {
disposition = actor.prototypeToken.disposition;
}
switch ( disposition ) {
case 1: return "Spieler";
case -1: return "Gegner";
case 0: return "Gegner 2";
case 2: return "Gegner 3";
default: return "Unbekannt";
}
}
static readonly defenseThreshold: Array<[number, number]> = [
[30, 18],
[25, 17],

View File

@ -64,6 +64,7 @@
.flexrow {
align-items: center;
flex-direction: row;
}
h3 {

View File

@ -23,13 +23,16 @@
"origin": "",
"faith": "",
"level": 1,
"leader": false,
"gold": 0,
"silver": 0,
"copper": 0,
"showAllItems": false,
"showUnlearned": false
"copper": 0
}
},
"settings": {
"showAllItems": false,
"showUnlearned": false
},
"characterBars": {
"lp": {
"value": 15,
@ -163,11 +166,11 @@
}
},
"character": {
"templates": ["characterBars", "attributes", "characterDescription", "characterHeader", "skills", "gear"],
"templates": ["characterBars", "settings", "attributes", "characterDescription", "characterHeader", "skills", "gear"],
"calc": {}
},
"npc": {
"templates": ["characterBars", "attributes", "characterDescription", "skills", "gear"],
"templates": ["characterBars", "settings", "attributes", "characterDescription", "skills", "gear"],
"calc": {}
}
},
@ -374,6 +377,7 @@
},
"effect": {
"templates": ["itemDescription", "equippable", "physical", "durationSelection"],
"unablesToAct": false,
"rolls": {
"formulas": {},
"output": ""

View File

@ -1,4 +1,50 @@
<div class="flexbox">
<div class="flexcolumn-3">
<div class="flexpart">
<div class="flexpart-header"><img src="icons/magic/time/arrows-circling-pink.webp" class="flexpart-icon">{{localize "midgard5.initiative"}}: {{data.calc.initiative}}</div>
<br>
<div class="flexrow">
<div style="font-weight: 800;">{{localize "midgard5.group"}}:</div>
<div class="fixed-value" style="font-weight: 800;">{{data.calc.group}}</div>
</div>
{{#if data.calc.unableToAct.enabled}}
<div class="flexrow">
<div>{{localize "midgard5.unableToAct"}}</div>
<div class="fixed-value;"><input type="checkbox" name="data.calc.unableToAct.enabled" {{checked data.calc.unableToAct.enabled}} disabled="disabled"></div>
<div class="fixed-value"><button class="roll-button roll-unableToAct-button"></button></div>
</div>
<div class="flexrow">
&nbsp; &nbsp;
<div>{{localize "midgard5.time-round"}}</div>
<div class="fixed-value"><input type="number" name="data.calc.unableToAct.roundsRemaining" value="{{data.calc.unableToAct.roundsRemaining}}"></div>
</div>
{{/if}}
{{#unless data.calc.unableToAct.enabled}}
<div class="flexrow">
<div>{{localize "midgard5.leader"}}</div>
<div class="fixed-value;"><input type="checkbox" name="data.info.leader" {{checked data.info.leader}}></div>
</div>
{{#if data.info.leader}}
<div class="flexrow">
<button class="wide-button roll-initiative-button">{{localize "midgard5.initiativeRoll"}}</button>
</div>
{{/if}}
{{/unless}}
</div>
</div>
<div class="flexcolumn-3">
<div class="flexpart">
<div class="flexpart-header"><img src="icons/magic/time/arrows-circling-pink.webp" class="flexpart-icon">{{localize "midgard5.movementPhase"}}</div>
</div>
</div>
<div class="flexcolumn-3">
<div class="flexpart">
<div class="flexpart-header"><img src="icons/magic/time/arrows-circling-pink.webp" class="flexpart-icon">{{localize "midgard5.actionPhase"}}</div>
</div>
</div>
<div class="flexcolumn-2">
<div class="flexpart">

View File

@ -3,7 +3,7 @@
<tr>
<th class="title"><img src="/icons/svg/eye.svg" class="table-icon"></th>
<th class="title">{{localize "TYPES.Item.effect"}}</th>
<td><a class="title add-effect"><i class="fa-regular fa-plus"></i></a></th>
<th><a class="title add-effect"><i class="fa-regular fa-plus"></i></a></th>
</tr>
</thead>

View File

@ -217,8 +217,8 @@
<h3>
{{localize "midgard5.allItems"}}
<input id="data.info.showAllItems" class="checkbox" type="checkbox" name="data.info.showAllItems" {{checked data.info.showAllItems}} style="float: right;">
<label for="data.info.showAllItems" style="font-size: small; font-weight: normal; font-style: italic; float: right;">{{localize "midgard5.showAll"}}&nbsp;</label>
<input id="data.settings.showAllItems" class="checkbox" type="checkbox" name="data.settings.showAllItems" {{checked data.settings.showAllItems}} style="float: right;">
<label for="data.settings.showAllItems" style="font-size: small; font-weight: normal; font-style: italic; float: right;">{{localize "midgard5.showAll"}}&nbsp;</label>
</h3>
<div class="flexbox">
<div class="flexcolumn-2">
@ -295,7 +295,7 @@
<tbody class="items-list">
<tr height = 10px></tr>
{{#each data.calc.gear.items as |item itemId|}}
{{#if (or ../data.info.showAllItems (eq item.containerId ""))}}
{{#if (or ../data.settings.showAllItems (eq item.containerId ""))}}
<tr data-item-id="{{itemId}}" class="item">
<td class="flexpart-img"><img src={{item.icon}} class="flexpart-icon"></td>
<td class="padding">
@ -351,7 +351,7 @@
</thead>
<tbody class="items-list">
{{#each data.calc.gear.weapons as |item itemId|}}
{{#if (or ../data.info.showAllItems (eq item.containerId ""))}}
{{#if (or ../data.settings.showAllItems (eq item.containerId ""))}}
<tr data-item-id="{{itemId}}" class="item">
<td class="flexpart-img"><img src={{item.icon}} class="flexpart-icon"></td>
<td class="padding edit-item">{{item.label}}</td>
@ -397,7 +397,7 @@
</thead>
<tbody class="items-list">
{{#each data.calc.gear.defensiveWeapons as |item itemId|}}
{{#if (or ../data.info.showAllItems (eq item.containerId ""))}}
{{#if (or ../data.settings.showAllItems (eq item.containerId ""))}}
<tr data-item-id="{{itemId}}" class="item">
<td class="flexpart-img"><img src={{item.icon}} class="flexpart-icon"></td>
<td class="padding edit-item">{{item.label}}</td>
@ -443,7 +443,7 @@
</thead>
<tbody class="items-list">
{{#each data.calc.gear.armor as |item itemId|}}
{{#if (or ../data.info.showAllItems (eq item.containerId ""))}}
{{#if (or ../data.settings.showAllItems (eq item.containerId ""))}}
<tr data-item-id="{{itemId}}" class="item">
<td class="flexpart-img"><img src={{item.icon}} class="flexpart-icon"></td>
<td class="padding edit-item">{{item.label}}</td>

View File

@ -16,6 +16,11 @@
<input id="data.magic" type="checkbox" name="data.magic" {{checked data.magic}}>
<label for="data.magic">{{localize "midgard5.magic"}}</label>
</span>
<span>
<input id="data.unablesToAct" type="checkbox" name="data.unablesToAct" {{checked data.unablesToAct}}>
<label for="data.unablesToAct">{{localize "midgard5.unablesToAct"}}</label>
</span>
</div>
</td>
</tr>