diff --git a/css/torgeternity.css b/css/torgeternity.css index 44de99a2..715d4b71 100644 --- a/css/torgeternity.css +++ b/css/torgeternity.css @@ -4786,4 +4786,68 @@ aside.carry-type-menu { background: #444; } } +} + +.torgeternity-active-effects.scrollable{ + --scroll-margin: 0.75rem; + margin-right: calc(-1 * var(--scroll-margin)); + padding-right: var(--scroll-margin); + overflow: hidden auto; + scrollbar-gutter: stable; +} + +.torgeternity-active-effects.changes { + .scrollable { + display: flex; + flex-direction: column; + gap: 8px; /* Breathing room between rows */ + padding: 0; + margin: 0; + list-style: none; + align-items: flex-start; + } + + .change-row { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + } + + .change-row select { + flex: 9; + height:2rem; + } + + .change-row .value-input { + flex: 1; + height:2rem; + } + + .change-button { + border: 1px solid; + margin: 1rem 0; + padding: 16px 12px; + } + + .torgchange-section{ + border: 1px solid; + padding: 0.5rem; + border-radius: 0.5rem; + margin: 0.5rem 0; + + h3 { + transform: translate(0, -1rem); + background: var(--color-cool-5); + display: block; + width: auto; + padding: 0 0.8rem; + display: inline-block; + } + + h4{ + color: var(--color-form-label); + margin: 12px 0; + } + } } \ No newline at end of file diff --git a/lang/de.json b/lang/de.json index 6a7ced5c..b5ea5806 100644 --- a/lang/de.json +++ b/lang/de.json @@ -5,7 +5,8 @@ }, "EFFECT": { "TABS": { - "torg": "Torg Eternity" + "torg": "Torg Eternity", + "torgChanges": "Torg Changes" } }, "CHAT": { @@ -53,6 +54,12 @@ "applyIfDefendTrait": { "label": "Anwenden bei Verteidiger-Eigenschaft", "hint": "Effekt wird nur angewendet, wenn der angegriffene Token (das Ziel) einen der angegebenen Eigenschaften besitzt." + }, + "skillsFavour": { + "label": "Bevorzugte Fertigkeiten:" + }, + "attributesFavor": { + "label": "Bevorzugte Attribute:" } } }, @@ -334,6 +341,29 @@ "sub": "Das Opfer erhält sofort eine letzte, dramatische Aktion, bevor es abtritt. Verwundungsmalusse werden hierbei ignoriert." } }, + "defenses": { + "dodge": "Ausweichen", + "meleeWeapons": "Nahkampfwaffe", + "unarmedCombat": "Waffenloser Kampf", + "intimidation": "Einschüchtern", + "maneuver": "Manövrieren", + "taunt": "Verspotten", + "trick": "Tricksen", + "armor": "Rüstung", + "toughness": "Robustheit", + "damageTraits" : { + "energyArmor": "Rüstung gegen Energieschaden", + "energyDefense": "Resistenz gegen Energieschaden", + "fireArmor": "Rüstung gegen Feuerschaden", + "fireDefense": "Resistenz gegen Feuerschaden", + "forceArmor": "Rüstung gegen Kraftschaden", + "forceDefense": "Resistenz gegen Kraftschaden", + "iceArmor": "Rüstung gegen Eisschaden", + "iceDefense": "Resistenz gegen Eisschaden", + "lightningArmor": "Rüstung gegen Blitzschaden", + "lightningDefense": "Resistenz gegen Blitzschaden" + } + }, "dialogPrompts": { "cardPooled": "Im Aktionenpool?", "cosmDeckDialogLabel": "Ziehe", @@ -664,6 +694,13 @@ "reachedMaximumAttr": "Du hast das Attributsmaximum, dass dein Volk vorgibt, erreicht. Eine weitere Erhöhung ist nicht möglich.", "clipEmpty": "Diese Munition ist leer und kann nicht zum Nachladen verwendet werden." }, + "otherActiveEffectsChanges": { + "movement": "Movement", + "health": "Heatlh", + "damageAndTest": "Damages and Test", + "axiomsAndReality": "Axioms and Reality", + "others": "Others" + }, "packs": { "basicRules": "torgeternity.system-de-basisregeln", "decks": "torgeternity.system-de-grundkarten" @@ -1175,7 +1212,57 @@ "backlash2Tooltip": "1 Schock anwenden", "backlash3Tooltip": "Sehr Angeschlagen anwenden", "additionalDamage": "+ Schaden", - "bonusDice": "BW im Voraus würfeln" + "bonusDice": "BW im Voraus würfeln", + "activeEffect": { + "skills": { + "title": "Fertigkeiten", + "description": "Wenn du Fertigkeitsboni ändern oder sie als bevorzugt markieren möchtest.", + "skillAdds": { + "label": "Fertigkeitsboni:", + "cta": "+ Fertigkeitsbonus hinzufügen" + }, + "skillsFavour": { + "label": "Bevorzugte Fertigkeiten:" + } + }, + "attributes": { + "title": "Attribute", + "description": "Wenn du Attributswerte ändern oder sie als bevorzugt markieren möchtest.", + "attributesValue": { + "label": "Attributswerte:", + "cta": "+ Attributswert-Änderung hinzufügen" + }, + "attributesFavor": { + "label": "Bevorzugte Attribute:" + } + }, + "defenses": { + "title": "Verteidigung", + "description": "Wenn du Rüstungs- oder Widerstandswerte global oder gegen bestimmte Elemente ändern möchtest.", + "defensesMod": { + "label": "Verteidigungsmodifikator", + "cta": "+ Verteidigungsmodifikator hinzufügen" + }, + "elements": { + "label": "Elementare Verteidigung", + "cta": "+ Elementaren Verteidigungsmodifikator hinzufügen" + } + }, + "other": { + "title": "Sonstiges", + "description": "Sonstige Änderungen, die nicht kategorisiert werden können, wie Möglichkeiten, Axiome, Gesundheit, Bewegung usw. Diese wurden hier zusammengefasst.", + "otherChanges": { + "label": "Sonstige Änderung", + "cta": "+ Sonstige Änderung hinzufügen" + } + } + } + }, + "skillGroup": { + "combat": "Kampf", + "power": "Kräfte", + "vehicle": "Fahrzeuge", + "other": "Sonstiges" }, "sizes": { "large": "Groß", diff --git a/lang/en.json b/lang/en.json index 70b899a5..f94a32ee 100644 --- a/lang/en.json +++ b/lang/en.json @@ -5,7 +5,8 @@ }, "EFFECT": { "TABS": { - "torg": "Torg Eternity" + "torg": "Torg Eternity", + "torgChanges": "Torg Changes" } }, "TYPES": { @@ -47,6 +48,12 @@ "applyIfDefendTrait": { "label": "Apply If Defend Trait", "hint": "Apply the effect only if the attacked token (the target) has one of these traits" + }, + "skillsFavor":{ + "label": "Skills Favored:" + }, + "attributesFavor": { + "label": "Attributes Favored:" } } }, @@ -328,6 +335,29 @@ "sub": "You get one final, dramatic action before expiring (ignoring wound penalties)" } }, + "defenses": { + "dodge": "Dodge", + "meleeWeapons": "Melee Weapons", + "unarmedCombat": "Unarmed Combat", + "intimidation": "Intimidation", + "maneuver": "Maneuver", + "taunt": "Taunt", + "trick": "Trick", + "armor": "Armor", + "toughness": "Toughness", + "damageTraits" : { + "energyArmor": "Armor against Energy Damage", + "energyDefense": "Toughness against Energy Dame", + "fireArmor": "Armor against Fire Damage", + "fireDefense": "Toughness against Fire Damage", + "forceArmor": "Armor against Force Damage", + "forceDefense": "Toughness against Force Damage", + "iceArmor": "Armor against Ice Damage", + "iceDefense": "Toughness against Ice Damage", + "lightningArmor": "Armor against Lightning Damage", + "lightningDefense": "Toughness against Lightning Damage" + } + }, "dialogPrompts": { "cardPooled": "In Pool?", "cosmDeckDialogLabel": "Draw", @@ -658,6 +688,13 @@ "reachedMaximumAttr": "You have reached the attribute maximum specified by your race. A further increase is not possible.", "clipEmpty": "This ammunition is empty and cannot be used for reloading." }, + "otherActiveEffectsChanges": { + "movement": "Movement", + "health": "Heatlh", + "damageAndTest": "Damages and Test", + "axiomsAndReality": "Axioms and Reality", + "others": "Others" + }, "packs": { "basicRules": "torgeternity.basic-rules", "decks": "torgeternity.core-card-set" @@ -1169,7 +1206,57 @@ "backlash2Tooltip": "Apply 1 shock", "backlash3Tooltip": "Apply Very Stymied", "additionalDamage": "Additional damage preset", - "bonusDice": "Rolled Bonus-Dice in advance" + "bonusDice": "Rolled Bonus-Dice in advance", + "activeEffect": { + "skills": { + "title": "Skills", + "description": "If you want to modify adds to skills or make them favored.", + "skillAdds": { + "label": "Skills adds:", + "cta": "+ Add a skill adds change" + }, + "skillsFavour": { + "label": "Skills Favored:" + } + }, + "attributes": { + "title": "Attributes", + "description": "If you want to modify attributes value or make them favored.", + "attributesValue": { + "label": "Attributes values:", + "cta": "+ Add an attribute value change" + }, + "attributesFavor": { + "label": "Attributes Favored:" + } + }, + "defenses": { + "title": "Defenses", + "description": "If you want to modify Armor or Toughness modifier globally or against certain elements", + "defensesMod": { + "label": "Defenses modifier", + "cta": "+ Add a Defense modifier" + }, + "elements": { + "label": "Elemental defense", + "cta": "+ Add an Elemental defense modifier" + } + }, + "other": { + "title": "Miscellaneous", + "description": "Other changes cannot be categorized such as Possibilities, Axiom, Health, Movement, etc. Those have been regrouped here.", + "otherChanges": { + "label": "Other change", + "cta": "+ Add a Miscellaneous change" + } + } + } + }, + "skillGroup": { + "combat": "Combat", + "power": "Power", + "vehicle": "Vehicles", + "other": "Other" }, "sizes": { "large": "Large", diff --git a/lang/es.json b/lang/es.json index 727e18bb..f21275bd 100644 --- a/lang/es.json +++ b/lang/es.json @@ -5,7 +5,8 @@ }, "EFFECT": { "TABS": { - "torg": "Torg Eternity" + "torg": "Torg Eternity", + "torgChanges": "Changes" } }, "CHAT": { diff --git a/lang/fr.json b/lang/fr.json index 74388e74..cd4af5ea 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -5,7 +5,8 @@ }, "EFFECT": { "TABS": { - "torg": "Torg Eternity" + "torg": "Torg Eternity", + "torgChanges": "Torg Changes" } }, "CHAT": { @@ -53,6 +54,12 @@ "applyIfDefendTrait": { "label": "Appliquer le trait de défense", "hint": "Appliquer l'Effet uniquement si le Token attaqué (la cible) possède l'un de ces traits" + }, + "skillsFavor":{ + "label": "Compétences favorisées:" + }, + "attributesFavor": { + "label": "Attributs favorisés:" } } }, @@ -334,6 +341,29 @@ "sub": "Vous pouvez effectuer une dernière action dramatique avant d’expirer. Vous ignorez les malus des Blessures pour ce dernier effort héroïque." } }, + "defenses": { + "dodge": "Esquive", + "meleeWeapons": "Défense en mélée", + "unarmedCombat": "Défense à mains nues", + "intimidation": "Intimidation", + "maneuver": "Manœuvre", + "taunt": "Provocation", + "trick": "Ruse", + "armor": "Armure", + "toughness": "Résistance", + "damageTraits" : { + "energyArmor": "Armure contre Arme à Énergie", + "energyDefense": "Résistance contre Arme à Énergie", + "fireArmor": "Armure contre Feu", + "fireDefense": "Résistance au Feu", + "forceArmor": "Armure contre les dégâts de Force", + "forceDefense": "Réistance au dégât de Force", + "iceArmor": "Armure contre le Froid", + "iceDefense": "Résistance au Froid", + "lightningArmor": "Armure contre l'Électricité", + "lightningDefense": "Résistance aux dégât Électrique" + } + }, "dialogPrompts": { "cardPooled": "Réserve d'action", "cosmDeckDialogLabel": "Piocher", @@ -664,6 +694,13 @@ "reachedMaximumAttr": "Tu as atteint le maximum d'attributs que ton peuple a défini. Une augmentation supplémentaire n'est pas possible.", "clipEmpty": "Ces munitions sont vides et ne peuvent pas être utilisées pour recharger." }, + "otherActiveEffectsChanges": { + "movement": "Déplacement", + "health": "Santé", + "damageAndTest": "Dégât et Tests", + "axiomsAndReality": "Axiomes and Réalité", + "others": "Autres" + }, "packs": { "basicRules": "torgeternity.basic-rules-fr", "decks": "torgeternity.core-card-set-fr" @@ -1175,7 +1212,57 @@ "backlash2Tooltip": "Appliquer 1 Choc", "backlash3Tooltip": "Appliquer Très Déstabilisé", "additionalDamage": "Dommages additionels", - "bonusDice": "DB lancés par avance" + "bonusDice": "DB lancés par avance", + "activeEffect": { + "skills": { + "title": "Compétences", + "description": "Pour augmenter ou diminuer le rang d'une ou plusieurs compétences ou les rendre favorisées.", + "skillAdds": { + "label": "Rand de compétence:", + "cta": "+ Ajouter une modification de rang" + }, + "skillsFavour": { + "label": "Compétences favorisées:" + } + }, + "attributes": { + "title": "Attributes", + "description": "Pour augmenter ou diminuer la valeur d'un ou plusieurs attributs ou les rendre favorisés", + "attributesValue": { + "label": "Valeur d'attribut :", + "cta": "+ Ajouter une modification de valeur" + }, + "attributesFavor": { + "label": "Attributs favorisés:" + } + }, + "defenses": { + "title": "Défenses", + "description": "Pour augmenter ou dominuer les différents modificateur de défenses ainsi que les résistances aux éléments", + "defensesMod": { + "label": "Modificateur de défense", + "cta": "+ Ajouter un modificateur de défense" + }, + "elements": { + "label": "Défense Élémentaire", + "cta": "+ Ajouter un modificateur de défense élémentaire" + } + }, + "other": { + "title": "Divers", + "description": "Toutes les modifications ne peuvent pas être regroupée (Possibilités, Axiomes, Mouvement, Dégâts) et ont été regroupée ci-dessous", + "otherChanges": { + "label": "Autre modification", + "cta": "+ Ajouter une modification diverse" + } + } + } + }, + "skillGroup": { + "combat": "Combat", + "power": "Pouvoirs", + "vehicle": "Véhicules", + "other": "Autres" }, "sizes": { "large": "Grande", diff --git a/module/config.js b/module/config.js index fb73f3a2..9744ba71 100644 --- a/module/config.js +++ b/module/config.js @@ -315,6 +315,50 @@ export function initConfig() { faith: 'torgeternity.powerSkills.faith', }; + torgeternity.interactionSkills = { + intimidation: 'torgeternity.skills.intimidation', + maneuver: 'torgeternity.skills.maneuver', + taunt: 'torgeternity.skills.taunt', + trick: 'torgeternity.skills.trick', + } + + torgeternity.combatSkills = { + dodge: 'torgeternity.skills.dodge', + energyWeapons: 'torgeternity.skills.energyWeapons', + fireCombat: 'torgeternity.skills.fireCombat', + heavyWeapons: 'torgeternity.skills.heavyWeapons', + meleeWeapons: 'torgeternity.skills.meleeWeapons', + missileWeapons: 'torgeternity.skills.missileWeapons', + unarmedCombat: 'torgeternity.skills.unarmedCombat', + } + + torgeternity.vehiclesSkills = { + airVehicles: 'torgeternity.skills.airVehicles', + beastRiding: 'torgeternity.skills.beastRiding', + landVehicles: 'torgeternity.skills.landVehicles', + waterVehicles: 'torgeternity.skills.waterVehicles', + } + + torgeternity.otherSkills = { + computers: 'torgeternity.skills.computers', + evidenceAnalysis: 'torgeternity.skills.evidenceAnalysis', + find: 'torgeternity.skills.find', + firstAid: 'torgeternity.skills.firstAid', + language: 'torgeternity.skills.language', + lockpicking: 'torgeternity.skills.lockpicking', + medicine: 'torgeternity.skills.medicine', + persuasion: 'torgeternity.skills.persuasion', + profession: 'torgeternity.skills.profession', + reality: 'torgeternity.skills.reality', + scholar: 'torgeternity.skills.scholar', + science: 'torgeternity.skills.science', + stealth: 'torgeternity.skills.stealth', + streetwise: 'torgeternity.skills.streetwise', + survival: 'torgeternity.skills.survival', + tracking: 'torgeternity.skills.tracking', + willpower: 'torgeternity.skills.willpower', + } + torgeternity.clearances = { alpha: 'torgeternity.clearances.alpha', beta: 'torgeternity.clearances.beta', @@ -376,6 +420,7 @@ export function initConfig() { willpower: 'torgeternity.skills.willpower', }; + torgeternity.concentrationSkills = [ // Spells 'alteration', @@ -632,6 +677,73 @@ export function initConfig() { ...Object.entries(torgeternity.rangedWeaponTraits).reduce((acc, ent) => { acc[ent[0]] = { label: ent[1], group: 'torgeternity.traitGroup.ranged' }; return acc }, {}), } + torgeternity.allSkillsGrouped = { + ...Object.entries(torgeternity.combatSkills).reduce((acc, ent) => { acc['skillFavor.'+ent[0]] = { label: ent[1], group: 'torgeternity.skillGroup.combat' }; return acc }, {}), + ...Object.entries(torgeternity.powerSkills).reduce((acc, ent) => { acc['skillFavor.'+ent[0]] = { label: ent[1], group: 'torgeternity.skillGroup.power' }; return acc }, {}), + ...Object.entries(torgeternity.vehiclesSkills).reduce((acc, ent) => { acc['skillFavor.'+ent[0]] = { label: ent[1], group: 'torgeternity.skillGroup.vehicle' }; return acc }, {}), + ...Object.entries(torgeternity.otherSkills).reduce((acc, ent) => { acc['skillFavor.'+ent[0]] = { label: ent[1], group: 'torgeternity.skillGroup.other' }; return acc }, {}), + } + + torgeternity.attributesFavorAndLabel = { + "attrFavor.charisma": {label: 'torgeternity.attributes.charisma'}, + "attrFavor.dexterity": {label: 'torgeternity.attributes.dexterity'}, + "attrFavor.mind": {label: 'torgeternity.attributes.mind'}, + "attrFavor.spirit": {label: 'torgeternity.attributes.spirit'}, + "attrFavor.strength": {label: 'torgeternity.attributes.strength'}, + } + + torgeternity.defensesModAndLabel = { + "defenses.dodge.mod": {label: 'torgeternity.defenses.dodge'}, + "defenses.meleeWeapons.mod": {label: 'torgeternity.defenses.meleeWeapons'}, + "defenses.unarmedCombat.mod": {label: 'torgeternity.defenses.unarmedCombat'}, + "defenses.intimidation.mod": {label: 'torgeternity.defenses.intimidation'}, + "defenses.maneuver.mod": {label: 'torgeternity.defenses.maneuver'}, + "defenses.taunt.mod": {label: 'torgeternity.defenses.taunt'}, + "defenses.trick.mod": {label: 'torgeternity.defenses.trick'}, + "defenses.armor": {label: 'torgeternity.defenses.armor'}, + "defenses.toughness": {label: 'torgeternity.defenses.toughness'}, + } + + torgeternity.defensesAgainstElementAndLabel = { + "defenses.damageTraits.energyArmor" : {label: 'torgeternity.defenses.damageTraits.energyArmor'}, + "defenses.damageTraits.energyDefense" : {label: 'torgeternity.defenses.damageTraits.energyDefense'}, + "defenses.damageTraits.fireArmor" : {label: 'torgeternity.defenses.damageTraits.fireArmor'}, + "defenses.damageTraits.fireDefense" : {label: 'torgeternity.defenses.damageTraits.fireDefense'}, + "defenses.damageTraits.forceArmor" : {label: 'torgeternity.defenses.damageTraits.forceArmor'}, + "defenses.damageTraits.forceDefense" : {label: 'torgeternity.defenses.damageTraits.forceDefense'}, + "defenses.damageTraits.iceArmor" : {label: 'torgeternity.defenses.damageTraits.iceArmor'}, + "defenses.damageTraits.iceDefense" : {label: 'torgeternity.defenses.damageTraits.iceDefense'}, + "defenses.damageTraits.lightningArmor" : {label: 'torgeternity.defenses.damageTraits.lightningArmor'}, + "defenses.damageTraits.lightningDefense" : {label: 'torgeternity.defenses.damageTraits.lightningDefense'}, + } + + torgeternity.otherChangesGroupedWithLabels = { + //Movement + "system.other.moveMod" : {label : "Move", group:"torgeternity.otherActiveEffectsChanges.movement"}, + "system.other.runMod" : {label : "Run", group:"torgeternity.otherActiveEffectsChanges.movement"}, + //Health + "fatigue": {label :"Fatigue", group:"torgeternity.otherActiveEffectsChanges.health"}, + "system.shock.max": {label :"Schock Max", group:"torgeternity.otherActiveEffectsChanges.health"}, + "system.wounds.max": {label :"Wounds Max", group:"torgeternity.otherActiveEffectsChanges.health"}, + //Damage and Tests + "system.unarmedDamageMod" : {label:"Unarmed Damage", group:"torgeternity.otherActiveEffectsChanges.damageAndTest"}, + "test.damage" : {label:"Test Damage", group:"torgeternity.otherActiveEffectsChanges.damageAndTest"}, + "test.weaponAP" : {label:"Weapon Armor Piercing", group:"torgeternity.otherActiveEffectsChanges.damageAndTest"}, + "test.wounds" : {label:"Test Wounds", group:"torgeternity.otherActiveEffectsChanges.damageAndTest"}, + "test.shock" : {label:"Test Schock", group:"torgeternity.otherActiveEffectsChanges.damageAndTest"}, + //Axioms and Reality + "system.axioms.magic": {label:"Magic Axiom", group:"torgeternity.otherActiveEffectsChanges.axiomsAndReality"}, + "system.axioms.social": {label:"Social Axiom", group:"torgeternity.otherActiveEffectsChanges.axiomsAndReality"}, + "system.axioms.spirit": {label:"Spirit Axiom", group:"torgeternity.otherActiveEffectsChanges.axiomsAndReality"}, + "system.axioms.tech": {label:"Tech Axiom", group:"torgeternity.otherActiveEffectsChanges.axiomsAndReality"}, + "system.other.possibilities.perAct": {label:"Possibilities per act", group:"torgeternity.otherActiveEffectsChanges.axiomsAndReality"}, + //Others + "system.other.maxDex":{label:"Max Dexterity", group:"torgeternity.otherActiveEffectsChanges.others"}, + "system.other.minStr":{label:"Min Strength", group:"torgeternity.otherActiveEffectsChanges.others"}, + "targetModifiers.darkness": {label:"Darkness Modifier", group:"torgeternity.otherActiveEffectsChanges.others"} + } + + torgeternity.specificItemTraits = { // ammunition diff --git a/module/data/active-effect.js b/module/data/active-effect.js index 02655f82..d22af78f 100644 --- a/module/data/active-effect.js +++ b/module/data/active-effect.js @@ -1,7 +1,150 @@ -import { newTraitsField } from './item/baseItemData.js'; +import { + newTraitsField +} from './item/baseItemData.js'; const fields = foundry.data.fields; +const toFieldSchema = (choiceList) => new fields.ArrayField( + new fields.SchemaField({ + _id: new fields.StringField({ + initial: foundry.utils.randomID(), + blank: true, + nullable: true, + }), + key: new fields.StringField({ + choices: choiceList, + initial: "", + blank: true, + nullable: false + }), + value: new fields.StringField({ + initial: "0", + blank: true + }), + mode: new fields.NumberField({ + initial: CONST.ACTIVE_EFFECT_MODES.ADD, + integer: true + }), + priority: new fields.NumberField({ + initial: null, + nullable: true + }) + }) +); + + +const setSkillsSchema = () => new fields.SetField( + new fields.StringField({ + blank: false, + choices: CONFIG.torgeternity.allSkillsGrouped, + textSearch: true, + trim: true, + }), { + nullable: false, + required: true, + initial: undefined + } +); + +const attributeSetSchema = () => new fields.SetField( + new fields.StringField({ + blank: false, + choices: CONFIG.torgeternity.attributesFavorAndLabel, + textSearch: true, + trim: true + }), { + nullable: false, + required: true, + initial: undefined + } +) + +const defensesModSchema = () => new fields.ArrayField( + new fields.SchemaField({ + _id: new fields.StringField({ + initial: foundry.utils.randomID(), + blank: true, + nullable: true, + }), + key: new fields.StringField({ + choices: CONFIG.torgeternity.defensesModAndLabel, + initial: "", + blank: true, + nullable: false + }), + value: new fields.StringField({ + initial: "0", + blank: true + }), + mode: new fields.NumberField({ + initial: CONST.ACTIVE_EFFECT_MODES.ADD, + integer: true + }), + priority: new fields.NumberField({ + initial: null, + nullable: true + }) + }) +); + +const elementalDefensesModSchema = () => new fields.ArrayField( + new fields.SchemaField({ + _id: new fields.StringField({ + initial: foundry.utils.randomID(), + blank: true, + nullable: true, + }), + key: new fields.StringField({ + choices: CONFIG.torgeternity.defensesAgainstElementAndLabel, + initial: "", + blank: true, + nullable: false + }), + value: new fields.StringField({ + initial: "0", + blank: true + }), + mode: new fields.NumberField({ + initial: CONST.ACTIVE_EFFECT_MODES.ADD, + integer: true + }), + priority: new fields.NumberField({ + initial: null, + nullable: true + }) + }) +); + +const otherActiveEffectsChangesSchema = () => new fields.ArrayField( + new fields.SchemaField({ + _id: new fields.StringField({ + initial: foundry.utils.randomID(), + blank: true, + nullable: true, + }), + key: new fields.StringField({ + choices: CONFIG.torgeternity.otherChangesGroupedWithLabels, + initial: "", + blank: true, + nullable: false + }), + value: new fields.StringField({ + initial: "0", + blank: true + }), + mode: new fields.NumberField({ + initial: CONST.ACTIVE_EFFECT_MODES.ADD, + integer: true + }), + priority: new fields.NumberField({ + initial: null, + nullable: true + }) + }) +); + + + /** * Addtional fields for TorgEternity ActiveEffect * @@ -9,35 +152,52 @@ const fields = foundry.data.fields; * @param {Boolean} transferOnOutcome Apply this effect to the target of the attack if the attack test has this specific outcome. * @param {SetField(StringField)} applyIfTrait Apply this effect to the item if the owning actor has one of these traits. * @param {SetField(StringField)} applyVsTrait Apply this effect to the item if the target has one of these traits. + * @param {SetField(StringField)} skillsFavor Wether a skill is favor by this active effect */ export class TorgActiveEffectData extends (foundry.data.ActiveEffectTypeDataModel ?? foundry.abstract.TypeDataModel) { // Foundry 14 - change base class to foundry.data.ActiveEffectTypeDataModel static LOCALIZATION_PREFIXES = ["torgeternity.activeEffect"]; - static defineSchema() { + const skillAddChoices = Object.fromEntries(Object.entries(CONFIG.torgeternity.skills) + .map(([k, v]) => [(v + '.adds').replace('torgeternity', 'system'), v])) + + const attributesAddsChoices = Object.fromEntries(Object.entries(CONFIG.torgeternity.attributeTypes) + .map(([k,v]) => [(v + '.value').replace('torgeternity', 'system'), v])) + const schema = (game.release.generation >= 14) ? foundry.data.ActiveEffectTypeDataModel.defineSchema() : {}; - Object.assign(schema, - { - // ...foundry.data.ActiveEffectTypeDataModel.defineSchema(), // Foundry 14+ - transferOnAttack: new fields.BooleanField({ initial: false, }), - transferOnOutcome: new fields.NumberField({ - choices: CONFIG.torgeternity.testOutcomeLabel, - initial: null, - integer: true, - nullable: true, - }), - applyIfAttackTrait: newTraitsField(), - applyIfDefendTrait: newTraitsField(), - combatToggle: new fields.BooleanField({ initial: false, }), - }) + Object.assign(schema, { + // ...foundry.data.ActiveEffectTypeDataModel.defineSchema(), // Foundry 14+ + transferOnAttack: new fields.BooleanField({ + initial: false, + }), + transferOnOutcome: new fields.NumberField({ + choices: CONFIG.torgeternity.testOutcomeLabel, + initial: null, + integer: true, + nullable: true, + }), + applyIfAttackTrait: newTraitsField(), + applyIfDefendTrait: newTraitsField(), + combatToggle: new fields.BooleanField({ + initial: false, + }), + skillsAdds: toFieldSchema(skillAddChoices), + attributesAdds: toFieldSchema(attributesAddsChoices), + skillsFavor: setSkillsSchema(), + attributesFavor: attributeSetSchema(), + defensesChanges: defensesModSchema(), + elementalDefenses: elementalDefensesModSchema(), + otherChanges: otherActiveEffectsChangesSchema(), + }) return schema; } static migrateData(source) { if (source.applyIfAttackTrait) source.applyIfAttackTrait = source.applyIfAttackTrait.map(t => (t === 'supernnaturalEvil') ? 'supernaturalEvil' : t) if (source.applyIfDefendTrait) source.applyIfDefendTrait = source.applyIfDefendTrait.map(t => (t === 'supernnaturalEvil') ? 'supernaturalEvil' : t) - return super.migrateData(source); + const migrated = super.migrateData(source); + return migrated } /** @@ -46,6 +206,6 @@ export class TorgActiveEffectData extends (foundry.data.ActiveEffectTypeDataMode */ get isSuppressed() { // Don't apply the AE to the owning actor if it is being transferred on an attack - return (this.transferOnAttack || this.transferOnOutcome); + return this.system && (this.system.transferOnAttack || this.system.transferOnOutcome); } -} \ No newline at end of file +} diff --git a/module/documents/active-effect/torgActiveEffect.js b/module/documents/active-effect/torgActiveEffect.js index 5ad42d40..0ac23c6d 100644 --- a/module/documents/active-effect/torgActiveEffect.js +++ b/module/documents/active-effect/torgActiveEffect.js @@ -1,4 +1,15 @@ import { TestResult } from '../../torgchecks.js'; +import { torgeternity as config} from '../../config.js'; + +const OTHER_AE_KEY_FRAGMENTS = [ +"system.other", +"fatigue", +"system.shock", +"system.wounds", +"test", +"system.axioms", +"targetModifiers", +] /** * Extend the basic ActiveEffect model with migrations and TORG specific handling @@ -11,6 +22,7 @@ export default class TorgActiveEffect extends foundry.documents.ActiveEffect { * @returns {object} the migrated data object */ static migrateData(source) { + console.log("Migrating Data", source) if (Object.hasOwn(source, 'changes')) { const migrationDictionary = { // SK and Threat attribute modifiers @@ -57,8 +69,11 @@ export default class TorgActiveEffect extends foundry.documents.ActiveEffect { change.value = change.value.toLowerCase(); } } - } - + // Assign to each changes a randomID as the default schema for those doesn't yield one + for(const change of source.changes){ + change._id = foundry.utils.randomID() + } + } // Replace flags if (source.flags?.torgeternity?.transferOnAttack !== undefined) { source.system.transferOnAttack = source.flags.torgeternity.transferOnAttack; @@ -69,6 +84,154 @@ export default class TorgActiveEffect extends foundry.documents.ActiveEffect { delete source.flags.torgeternity.testOutcome; } + // No changes made to systemField or changes, we can safely return the + // parent migration + if(!source.system && !source.changes){ + return super.migrateData(source) + } + // Changes made on systemField or Changes, if no changes, let's init it + if(!source.changes){ + source.changes = [] + } + // Reassign system attributes, attribute, defenses and others to comprehensible foundry changes + if(source.system?.skillsFavor && source.changes !== undefined){ + const changesToUpdate = source.changes.filter((c) => (c.key.includes('skills') && c.key.includes('.isFav')) ? false : true) + source.system.skillsFavor.forEach((sf) => { + const changeAlreadyExists = changesToUpdate.find(c => c.key.includes(sf)) + if(!changeAlreadyExists){ + changesToUpdate.push({ + key:'system.skills.'+sf.replace('skillFavor.', '')+'.isFav', + value: 'true', + mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE, + _id: foundry.utils.randomID() + }) + } + }) + source.changes = changesToUpdate + } + + if(source.system?.skillsAdds && source.changes !== undefined){ + const changesToUpdate = source.changes.filter((c) => (c.key.includes('skills') && !c.key.includes('.isFav')) ? false : true) + source.system.skillsAdds.forEach((sa) => { + const changeExists = changesToUpdate.find((c) => c._id === sa._id) + if(!changeExists){ + changesToUpdate.push({...sa, _id: foundry.utils.randomID()}) + } + }) + source.changes = changesToUpdate + } + + if(source.system?.attributesFavor && source.changes !== undefined){ + const changesToUpdate = source.changes.filter((c) => (c.key.includes('attributes') && c.key.includes('IsFav')) ? false : true) + source.system.attributesFavor.forEach((af) => { + const changeAlreadyExists = changesToUpdate.find(c => c.key.includes(af)) + if(!changeAlreadyExists){ + changesToUpdate.push({ + key:'system.attributes.'+af.replace('attrFavor.', '')+'IsFav', + value: 'true', + mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE, + _id: foundry.utils.randomID() + }) + } + }) + source.changes = changesToUpdate + } + + if(source.system?.attributesAdds && source.changes !== undefined){ + const changesToUpdate = source.changes.filter((c) => (c.key.includes('attributes') && !c.key.includes('IsFav')) ? false : true) + source.system.attributesAdds.forEach((aa) => { + const changeExists = changesToUpdate.find((c) => c._id === aa._id) + if(!changeExists){ + changesToUpdate.push({...aa, _id: foundry.utils.randomID()}) + } + }) + source.changes = changesToUpdate + } + + if(source.system?.defensesChanges && source.changes !== undefined){ + const changesToUpdate = source.changes.filter((c) => (c.key.includes('defenses') && !c.key.includes('damageTraits')) ? false : true) + source.system.defensesChanges.forEach((dc) => { + const changeExists = changesToUpdate.find((c) => c._id === dc._id) + if(!changeExists){ + changesToUpdate.push({...dc, _id: foundry.utils.randomID()}) + } + }) + source.changes = changesToUpdate + } + + if(source.system?.elementalDefenses && source.changes !== undefined){ + const changesToUpdate = source.changes.filter((c) => (c.key.includes('defenses') && c.key.includes('damageTraits')) ? false : true) + source.system.elementalDefenses.forEach((ed) => { + const changeExists = changesToUpdate.find((c) => c._id === ed._id) + if(!changeExists){ + changesToUpdate.push({...ed, _id: foundry.utils.randomID()}) + } + }) + source.changes = changesToUpdate + } + + if(source.system?.otherChanges && source.changes !== undefined){ + const changesToUpdate = source.changes.filter((c) => (OTHER_AE_KEY_FRAGMENTS.some(otherKey => c.key.includes(otherKey))) ? false : true) + source.system.otherChanges.forEach((ed) => { + const changeExists = changesToUpdate.find((c) => c._id === ed._id) + if(!changeExists){ + changesToUpdate.push({...ed, _id: foundry.utils.randomID()}) + } + }) + source.changes = changesToUpdate + } + // Reassign changes to system skills, attribute, defenses and other to ensure + // backward compatibility an UI stay compatible + if(source.changes !== undefined){ + const changesPerType = source.changes?.reduce((changesPerType, change) => { + if(change.key.includes('skills') && change.key.includes('isFav')){ + changesPerType.skillsFavor.push(change.key.replace('system.skills.', 'skillFavor.').replace('.isFav', '')) + return changesPerType + } + if(change.key.includes('attributes') && change.key.includes('IsFav')){ + changesPerType.attributesFavor.push(change.key.replace('system.attributes.', 'attrFavor.').replace('IsFav', '')) + return changesPerType + } + if(change.key.includes('skills') && !change.key.includes('isFav')){ + changesPerType.skillsAdds.push(change) + return changesPerType + } + if(change.key.includes('attributes') && !change.key.includes('IsFav')){ + changesPerType.attributesAdds.push(change) + return changesPerType + } + if(change.key.includes('defenses') && !change.key.includes('damageTraits')){ + changesPerType.defensesChanges.push(change) + return changesPerType + } + if(change.key.includes('defenses') && change.key.includes('damageTraits')){ + changesPerType.elementalDefenses.push(change) + return changesPerType + } + changesPerType.otherChanges.push(change) + return changesPerType + }, { + skillsAdds:[], + skillsFavor:[], + attributesAdds:[], + attributesFavor:[], + defensesChanges:[], + elementalDefenses:[], + otherChanges:[] + }) + console.log(source) + // Reassign for UI + source.system = { + ...(source.system ??{}), // Keep other fields change not manage by migration (attackTraits, etc.) + skillsAdds : changesPerType.skillsAdds ?? [], + skillsFavor : changesPerType.skillsFavor ?? [], + attributesAdds : changesPerType.attributesAdds ?? [], + attributesFavor : changesPerType.attributesFavor ?? [], + defensesChanges : changesPerType.defensesChanges ?? [], + elementalDefenses: changesPerType.elementalDefenses ?? [], + otherChanges : changesPerType.otherChanges ?? [], + } + } return super.migrateData(source); } @@ -84,26 +247,56 @@ export default class TorgActiveEffect extends foundry.documents.ActiveEffect { } /** - * Return if this effect modifies the target of the test rather than the owner of the AE. + * Should this effect be transferred to the target on a successful attack? + * @param {TestResult} result + * @param {Array | undefined} attackTraits array of traits on the actor performing the test + * @param {Array | undefined} defendTraits array of traits on the target of the test + */ + appliesToTest(result, attackTraits, defendTraits) { + return (!this.disabled && + (this.system.transferOnAttack && result >= TestResult.STANDARD) || (this.system.transferOnOutcome === result)) && + testTraits(this.system.applyIfAttackTrait, attackTraits) && + testTraits(this.system.applyIfDefendTrait, defendTraits); + } + + /** + * Return if this effect has at least one change which is not merely changing the test. * @type {boolean} */ get modifiesTarget() { return !this.disabled && - (this.system.transferOnAttack || this.system.transferOnOutcome); + (this.system.transferOnAttack || this.system.transferOnOutcome) && + (this.changes.find(change => !change.key.startsWith('test.')) || this.statuses.length !== 0); } static blank; /** - * Return a copy of this object with the various "attack" traits cleared. + * Return a copy of this object with the various "attack" traits cleared, + * and any 'test.*' changes removed from it. */ copyForTarget() { if (!this.blank) this.blank = new TorgActiveEffect({ name: "blank" }); let fx = this.toObject(); + fx.changes = fx.changes.filter(change => !change.key.startsWith('test.')); return Object.assign(fx, { disabled: false, system: this.blank.system, origin: this.parent.uuid, }); } +} + +/** + * Return true if testTraits contains at least one of the entries in actualTraits + * @param {Set} testTraits + * @param {Array} actualTraits + */ +function testTraits(testTraits, actualTraits) { + if (!testTraits?.size) return true; + if (!actualTraits?.length) return false; + for (const trait of testTraits) { + if (actualTraits.includes(trait)) return true; + } + return false; } \ No newline at end of file diff --git a/module/documents/actor/torgeternityActor.js b/module/documents/actor/torgeternityActor.js index 46c8f3dc..7d80e65c 100644 --- a/module/documents/actor/torgeternityActor.js +++ b/module/documents/actor/torgeternityActor.js @@ -117,7 +117,10 @@ export default class TorgeternityActor extends foundry.documents.Actor { * @inheritdoc */ prepareDerivedData() { + console.log("applied effect (prior derived)", this.appliedEffects) super.prepareDerivedData(); + console.log("applied effect (after derived)", this.appliedEffects) + // Here Effects are applied, whatever follow cannot be directly affected by Effects // apply status effects (note that base values of 0 might have been modified by Active Effects) diff --git a/module/sheets/torgeternityActiveEffectConfig.js b/module/sheets/torgeternityActiveEffectConfig.js index cc164a81..74af102e 100644 --- a/module/sheets/torgeternityActiveEffectConfig.js +++ b/module/sheets/torgeternityActiveEffectConfig.js @@ -2,34 +2,339 @@ export default class TorgActiveEffectConfig extends foundry.applications.sheets. /** @override */ static PARTS = { - header: { template: "templates/sheets/active-effect/header.hbs" }, - tabs: { template: "templates/generic/tab-navigation.hbs" }, - details: { template: "templates/sheets/active-effect/details.hbs", scrollable: [""] }, - torg: { template: "systems/torgeternity/templates/active-effect-torg.hbs", scrollable: [""] }, - duration: { template: "templates/sheets/active-effect/duration.hbs" }, - changes: { template: "templates/sheets/active-effect/changes.hbs", scrollable: ["ol[data-changes]"] }, - footer: { template: "templates/generic/form-footer.hbs" } + header: { + template: "templates/sheets/active-effect/header.hbs" + }, + tabs: { + template: "templates/generic/tab-navigation.hbs" + }, + details: { + template: "templates/sheets/active-effect/details.hbs", + scrollable: [""] + }, + torg: { + template: "systems/torgeternity_dev/templates/active_effects/torg_effects.hbs", + scrollable: [""] + }, + duration: { + template: "templates/sheets/active-effect/duration.hbs" + }, + changes: { + template: "templates/sheets/active-effect/changes.hbs", + scrollable: ["ol[data-changes]"] + }, + torgChanges: { + template: "systems/torgeternity_dev/templates/active_effects/torg_changes.hbs", + }, + footer: { + template: "templates/generic/form-footer.hbs" + } }; + /** @override */ static TABS = { sheet: { - tabs: [ - { id: "details", icon: "fa-solid fa-book" }, - { id: "torg", icon: "fa-solid fa-swords" }, - { id: "duration", icon: "fa-solid fa-clock" }, - { id: "changes", icon: "fa-solid fa-gears" } + tabs: [{ + id: "details", + icon: "fa-solid fa-book" + }, + { + id: "torg", + icon: "fa-solid fa-swords" + }, + { + id: "duration", + icon: "fa-solid fa-clock" + }, + { + id: "changes", + icon: "fa-solid fa-gear" + }, + { + id: "torgChanges", + icon: "fa-solid fa-gear" + } ], initial: "details", labelPrefix: "EFFECT.TABS" }, }; + /** @override **/ + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + classes: ["active-effect-config"], + window: { + contentClasses: ["standard-form"], + icon: "fa-solid fa-person-rays" + }, + position: { + width: 600 // bigger to avoid breaking tabs + }, + form: { + closeOnSubmit: true, + submitOnChange: false, + }, + actions: { + addSkillAddsChange: this.#onAddSkillAddsChange, + deleteSkillAddsChange: this.#onDeleteSkillAddsChange, + addAttributesChange: this.#onAddAttributeChange, + deleteAttributesChange: this.#onDeleteAttributesChange, + addDefensesMod: this.#onAddDefensesMod, + deleteDefenseMod: this.#onDeleteDefensesMod, + addElemDefenses: this.#onAddElemDefensesMod, + deleteElemDefense: this.#onDeleteElemDefensesMod, + deleteOtherChange: this.#onDeleteOtherChange, + addOtherChange: this.#onAddOtherChange, + } + }; + async _preparePartContext(partId, context) { const partContext = await super._preparePartContext(partId, context); - if (partId === 'torg') { + if (partId === 'torg' || partId === 'torgChanges') { partContext.systemFields = this.document.system.schema.fields; } return partContext; } -} \ No newline at end of file + /* ----------------------------------------- */ + /** + * Add a new skill change to the skill changes array + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #onAddSkillAddsChange() { + const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const wholeChange = Object.values(submitData.system) + const skillAddsChanges = Object.values(submitData.system.skillsAdds || {}); + skillAddsChanges.push({ + key: "system.skills.airVehicles.adds", + value: 0, + _id: foundry.utils.randomID() + }); // Push a default value otherwise recounciliation will skip it. + return this.submit({ + updateData: { + system: { + ...wholeChange, + skillsAdds: skillAddsChanges + } + } + }) + } + + /* ----------------------------------------- */ + /** + * Delete a change from the skills changes array. + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #onDeleteSkillAddsChange(event) { + const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const wholeChange = Object.values(submitData.system) + const changes = Object.values(submitData.system.skillsAdds); + const row = event.target.closest("li"); + const index = Number(row.dataset.skillAddsIndex) || 0; + changes.splice(index, 1); + return this.submit({ + updateData: { + system: { + ...wholeChange, + skillsAdds: changes + } + } + }); + } + + /* ----------------------------------------- */ + /** + * Add a new Attribute change to the attributes changes array + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #onAddAttributeChange() { + const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const wholeChange = Object.values(submitData.system) + const attributesAddsChanges = Object.values(submitData.system.attributesAdds || {}); + attributesAddsChanges.push({ + key: "system.attributes.charisma.value", + value: 0, + _id: foundry.utils.randomID() + }); // Push a default value otherwise recounciliation will skip it. + return this.submit({ + updateData: { + system: { + ...wholeChange, + attributesAdds: attributesAddsChanges + } + } + }) + } + + /* ----------------------------------------- */ + /** + * Delete a change from the attrributes changes array. + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #onDeleteAttributesChange(event) { + const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const wholeChange = Object.values(submitData.system) + const changes = Object.values(submitData.system.attributesAdds); + const row = event.target.closest("li"); + const index = Number(row.dataset.attributesAddsIndex) || 0; + changes.splice(index, 1); + return this.submit({ + updateData: { + system: { + ...wholeChange, + attributesAdds: changes + } + } + }); + } + +/* ----------------------------------------- */ + /** + * Add a new defenses modifier to the defenses changes array + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #onAddDefensesMod() { + const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const wholeChange = Object.values(submitData.system) + const defensesModChanges = Object.values(submitData.system.defensesChanges || {}); + defensesModChanges.push({ + key: "defenses.dodge.mod", + value: 0, + _id: foundry.utils.randomID() + }); // Push a default value otherwise recounciliation will skip it. + console.log(defensesModChanges) + return this.submit({ + updateData: { + system: { + ...wholeChange, + defensesChanges: defensesModChanges + } + } + }) + } + + /* ----------------------------------------- */ + /** + * Delete a change from the defenses mod array + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #onDeleteDefensesMod(event) { + const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const wholeChange = Object.values(submitData.system) + const changes = Object.values(submitData.system.defensesChanges); + const row = event.target.closest("li"); + const index = Number(row.dataset.defensesModIndex) || 0; + changes.splice(index, 1); + return this.submit({ + updateData: { + system: { + ...wholeChange, + defensesChanges: changes + } + } + }); + } + + /* ----------------------------------------- */ + /** + * Add a new elemental defenses modifier to the elemental defense changes array + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #onAddElemDefensesMod() { + const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const wholeChange = Object.values(submitData.system) + const elementalDefensesChanges = Object.values(submitData.system.elementalDefenses || {}); + elementalDefensesChanges.push({ + key: "defenses.damageTraits.energyArmor", + value: 0, + _id: foundry.utils.randomID() + }); // Push a default value otherwise recounciliation will skip it. + return this.submit({ + updateData: { + system: { + ...wholeChange, + elementalDefenses: elementalDefensesChanges + } + } + }) + } + + /* ----------------------------------------- */ + /** + * Delete a change from the defenses mod array + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #onDeleteElemDefensesMod(event) { + const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const wholeChange = Object.values(submitData.system) + const changes = Object.values(submitData.system.elementalDefenses); + const row = event.target.closest("li"); + const index = Number(row.dataset.elementalDefenses) || 0; + changes.splice(index, 1); + return this.submit({ + updateData: { + system: { + ...wholeChange, + elementalDefenses: changes + } + } + }); + } + + /* ----------------------------------------- */ + /** + * Add a new elemental defenses modifier to the elemental defense changes array + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #onAddOtherChange() { + const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const wholeChange = Object.values(submitData.system) + const otherChanges = Object.values(submitData.system.otherChanges || {}); + otherChanges.push({ + key: "system.other.moveMod", + value: 0, + _id: foundry.utils.randomID() + }); // Push a default value otherwise recounciliation will skip it. + return this.submit({ + updateData: { + system: { + ...wholeChange, + otherChanges: otherChanges + } + } + }) + } + + /* ----------------------------------------- */ + /** + * Delete a change from the defenses mod array + * @this {ActiveEffectConfig} + * @type {ApplicationClickAction} + */ + static async #onDeleteOtherChange(event) { + const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const wholeChange = Object.values(submitData.system) + const changes = Object.values(submitData.system.otherChanges); + const row = event.target.closest("li"); + const index = Number(row.dataset.otherChanges) || 0; + changes.splice(index, 1); + return this.submit({ + updateData: { + system: { + ...wholeChange, + otherChanges: changes + } + } + }); + } +} diff --git a/system.json b/system.json index 28bc3d04..ee20c1a3 100644 --- a/system.json +++ b/system.json @@ -1,6 +1,6 @@ { - "id": "torgeternity", - "title": "Torg Eternity", + "id": "torgeternity_dev", + "title": "Torg Eternity (DEV)", "description": "Torg Eternity System for Foundry VTT", "authors": [ { diff --git a/templates/active_effects/torg_changes.hbs b/templates/active_effects/torg_changes.hbs new file mode 100644 index 00000000..60cd7efd --- /dev/null +++ b/templates/active_effects/torg_changes.hbs @@ -0,0 +1,143 @@ +{{!-- TODO: Split to parts! --}} +
+{{!-- Skills --}} +
+

{{localize "torgeternity.sheetLabels.activeEffect.skills.title"}}

+

{{localize "torgeternity.sheetLabels.activeEffect.skills.description"}}

+
+

{{localize "torgeternity.sheetLabels.activeEffect.skills.skillAdds.label"}}

+
    + {{#each source.system.skillsAdds as |changeSkillAdd i|}} + {{#with ../systemFields.skillsAdds as |skillAddsChanges|}} +
  1. + + + + +
    + +
    +
  2. + {{/with}} + {{/each}} +
+ +
+
+ {{formGroup systemFields.skillsFavor value=source.system.skillsFavor rootId=rootId localize=true name="system.skillsFavor"}} +
+
+ +{{!-- Attributes --}} +
+

{{localize "torgeternity.sheetLabels.activeEffect.attributes.title"}}

+

{{localize "torgeternity.sheetLabels.activeEffect.attributes.description"}}

+
+

{{localize "torgeternity.sheetLabels.activeEffect.attributes.attributesValue.label"}}

+
    + {{#each source.system.attributesAdds as |changeAttrAdds i|}} + {{#with ../systemFields.attributesAdds as |attributesChanges|}} +
  1. + + + + +
    + +
    +
  2. + {{/with}} + {{/each}} +
+ +
+
+ {{formGroup systemFields.attributesFavor value=source.system.attributesFavor rootId=rootId localize=true + name="system.attributesFavor"}} +
+
+ +{{!-- Defenses --}} +
+

{{localize "torgeternity.sheetLabels.activeEffect.defenses.title"}}

+

{{localize "torgeternity.sheetLabels.activeEffect.defenses.description"}}

+
+

{{localize "torgeternity.sheetLabels.activeEffect.defenses.defensesMod.label"}}

+
    + {{#each source.system.defensesChanges as |changeDefensesMod i|}} + {{#with ../systemFields.defensesChanges as |defensesMod|}} +
  1. + + + + +
    + +
    +
  2. + {{/with}} + {{/each}} +
+ + +

{{localize "torgeternity.sheetLabels.activeEffect.defenses.elements.label"}}

+
    + {{#each source.system.elementalDefenses as |changeElementalDef i|}} + {{#with ../systemFields.elementalDefenses as |eleDefensesMod|}} +
  1. + + + + +
    + +
    +
  2. + {{/with}} + {{/each}} +
+ +
+
+ +{{!-- Others --}} +
+

{{localize "torgeternity.sheetLabels.activeEffect.other.title"}}

+

{{localize "torgeternity.sheetLabels.activeEffect.other.description"}}

+
+

{{localize "torgeternity.sheetLabels.activeEffect.other.otherChanges.label"}}

+
    + {{#each source.system.otherChanges as |otherChange i|}} + {{#with ../systemFields.otherChanges as |otherChangesField|}} +
  1. + + + + +
    + +
    +
  2. + {{/with}} + {{/each}} +
+ +
+
+
\ No newline at end of file diff --git a/templates/active-effect-torg.hbs b/templates/active_effects/torg_effects.hbs similarity index 100% rename from templates/active-effect-torg.hbs rename to templates/active_effects/torg_effects.hbs