diff --git a/src/lang/en.json b/src/lang/en.json index fc7205ee..70a3d821 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -10,7 +10,8 @@ "OSE.Roll": "Roll", "OSE.Success": "Success", "OSE.Failure": "Failure", - + "OSE.Multiclass": "Multiclass Character", + "OSE.dialog.tweaks": "Tweaks", "OSE.dialog.partysheet": "Party Overview", "OSE.dialog.partyselect": "Select PCs", @@ -54,6 +55,7 @@ "OSE.details.name": "Name", "OSE.details.class": "Class", + "OSE.details.race": "Race", "OSE.details.title": "Title", "OSE.details.alignment": "Alignment", "OSE.details.level": "Level", @@ -68,6 +70,9 @@ "OSE.details.morale": "Morale", "OSE.details.reaction": "Reaction", "OSE.details.appearing": "NA", + "OSE.details.1stClass": "1st Class", + "OSE.details.2ndClass": "2nd Class", + "OSE.details.3rdClass": "3rd Class", "OSE.Attack": "Attack", "OSE.Encumbrance": "Encumbrance", @@ -109,6 +114,8 @@ "OSE.HealthShort": "HP", "OSE.HitDice": "Hit Dice", "OSE.HitDiceShort": "HD", + "OSE.FractionalHpShort": "HPF", + "OSE.FractionalHp": "Remaining HP Fraction", "OSE.movement.base": "Movement Rate", "OSE.movement.short": "MV", diff --git a/src/lang/es.json b/src/lang/es.json index 7855ffaa..aa421454 100644 --- a/src/lang/es.json +++ b/src/lang/es.json @@ -10,6 +10,7 @@ "OSE.Roll": "Tirar", "OSE.Success": "Éxito", "OSE.Failure": "Fallo", + "OSE.Multiclass": "Multiclass Character", "OSE.dialog.tweaks": "Ajustes", "OSE.dialog.partysheet": "Resumen del grupo", "OSE.dialog.partyselect": "Seleccionar PJ", @@ -49,6 +50,7 @@ "OSE.table.treasure.roll": "Tirada de tesoro", "OSE.details.name": "Nombre", "OSE.details.class": "Clase", + "OSE.details.race": "Race", "OSE.details.title": "Título", "OSE.details.alignment": "Alineamiento", "OSE.details.level": "Nivel", @@ -63,6 +65,9 @@ "OSE.details.morale": "Moral", "OSE.details.reaction": "Reacción", "OSE.details.appearing": "NA", + "OSE.details.1stClass": "1st Class", + "OSE.details.2ndClass": "2nd Class", + "OSE.details.3rdClass": "3rd Class", "OSE.Attack": "Ataque", "OSE.Encumbrance": "Carga", "OSE.Retainer": "Sirvientes", @@ -99,6 +104,8 @@ "OSE.HealthShort": "PG", "OSE.HitDice": "Dado de Golpe", "OSE.HitDiceShort": "DG", + "OSE.FractionalHpShort": "HPF", + "OSE.FractionalHp": "Remaining HP Fraction", "OSE.movement.base": "Movimiento", "OSE.movement.short": "MV", "OSE.movement.details": "Detalles de Movimiento", diff --git a/src/lang/fr.json b/src/lang/fr.json index 56dc4c15..52167f79 100644 --- a/src/lang/fr.json +++ b/src/lang/fr.json @@ -10,6 +10,7 @@ "OSE.Roll": "Lancer", "OSE.Success": "Succès", "OSE.Failure": "Échec", + "OSE.Multiclass": "Multiclass Character", "OSE.dialog.tweaks": "Ajuster", "OSE.dialog.partysheet": "Fiche de Groupe", @@ -52,6 +53,7 @@ "OSE.details.name": "Nom", "OSE.details.class": "Classe", "OSE.details.title": "Titre", + "OSE.details.race": "Race", "OSE.details.alignment": "Alignement", "OSE.details.level": "Niveau", "OSE.details.experience.base": "Expérience", @@ -65,6 +67,9 @@ "OSE.details.morale": "Moral", "OSE.details.reaction": "Réaction", "OSE.details.appearing": "NA", + "OSE.details.1stClass": "1st Class", + "OSE.details.2ndClass": "2nd Class", + "OSE.details.3rdClass": "3rd Class", "OSE.Attack": "Attaque", "OSE.Encumbrance": "Encombrement", @@ -106,6 +111,8 @@ "OSE.HealthShort": "PV", "OSE.HitDice": "Dé de Vie", "OSE.HitDiceShort": "DV", + "OSE.FractionalHpShort": "HPF", + "OSE.FractionalHp": "Remaining HP Fraction", "OSE.movement.base": "Déplacement", "OSE.movement.short": "DP", diff --git a/src/lang/it.json b/src/lang/it.json index 9ab00918..51d178a4 100644 --- a/src/lang/it.json +++ b/src/lang/it.json @@ -10,6 +10,7 @@ "OSE.Roll": "Tiro", "OSE.Success": "Successo", "OSE.Failure": "Fallimento", + "OSE.Multiclass": "Multiclass Character", "OSE.dialog.tweaks": "Modifiche", "OSE.dialog.partysheet": "Panoramica del Gruppo", "OSE.dialog.partyselect": "Scegli Pg", @@ -50,6 +51,7 @@ "OSE.details.name": "Nome", "OSE.details.class": "Classe", "OSE.details.title": "Titolo", + "OSE.details.race": "Race", "OSE.details.alignment": "Allineamento", "OSE.details.level": "Livello", "OSE.details.experience.base": "Esperienza", @@ -63,6 +65,9 @@ "OSE.details.morale": "Morale", "OSE.details.reaction": "Reazione", "OSE.details.appearing": "Nr. Mostri", + "OSE.details.1stClass": "1st Class", + "OSE.details.2ndClass": "2nd Class", + "OSE.details.3rdClass": "3rd Class", "OSE.Attack": "Attacco", "OSE.Encumbrance": "Ingombro", "OSE.Retainer": "Seguace", @@ -99,6 +104,8 @@ "OSE.HealthShort": "PF", "OSE.HitDice": "Dadi Vita", "OSE.HitDiceShort": "DV", + "OSE.FractionalHpShort": "HPF", + "OSE.FractionalHp": "Remaining HP Fraction", "OSE.movement.base": "Movimento", "OSE.movement.short": "MV", "OSE.movement.details": "Dettagli Movimento", diff --git a/src/lang/pt-BR.json b/src/lang/pt-BR.json index f78e2dea..75d9466a 100644 --- a/src/lang/pt-BR.json +++ b/src/lang/pt-BR.json @@ -280,5 +280,12 @@ "OSE.error.notEnoughGP": "Este Ator não tem PO suficiente para pagar por este inventário.", "OSE.error.itemNoLongerExistsOnActor": "O Item solicitado {itemId} não existe mais no Ator {actorName}.", "OSE.error.noTokenControlled": "Você deve ter um ou mais Tokens controlados para poder usar esta opção.", - "OSE.error.noGP": "Você precisa do item PO para usar este recurso." + "OSE.error.noGP": "Você precisa do item PO para usar este recurso.", + "OSE.Multiclass": "Multiclass Character", + "OSE.details.1stClass": "1st Class", + "OSE.details.2ndClass": "2nd Class", + "OSE.details.3rdClass": "3rd Class", + "OSE.FractionalHpShort": "HPF", + "OSE.FractionalHp": "Remaining HP Fraction", + "OSE.details.race": "Race" } diff --git a/src/module/actor/character-sheet.js b/src/module/actor/character-sheet.js index 2ee81cf8..5b5351d3 100644 --- a/src/module/actor/character-sheet.js +++ b/src/module/actor/character-sheet.js @@ -18,7 +18,7 @@ export class OseActorSheetCharacter extends OseActorSheet { * Extend and override the default options used by the 5e Actor Sheet * @returns {Object} */ - static get defaultOptions() { + static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["ose", "sheet", "actor", "character"], template: `${OSE.systemPath()}/templates/actors/character-sheet.html`, diff --git a/src/module/actor/entity.js b/src/module/actor/entity.js index f988315d..c7cc88ba 100644 --- a/src/module/actor/entity.js +++ b/src/module/actor/entity.js @@ -63,21 +63,82 @@ export class OseActor extends Actor { if (this.data.type != "character") { return; } - let modified = Math.floor( - value + (this.data.data.details.xp.bonus * value) / 100 - ); - return this.update({ - "data.details.xp.value": modified + this.data.data.details.xp.value, - }).then(() => { - const speaker = ChatMessage.getSpeaker({ actor: this }); - ChatMessage.create({ - content: game.i18n.format("OSE.messages.GetExperience", { - name: this.name, - value: modified, - }), - speaker, + if(!this.data.data.multiclass?.enabled){ + let modified = Math.floor( + value + (this.data.data.details.xp.bonus * value) / 100 + ); + return this.update({ + "data.details.xp.value": modified + this.data.data.details.xp.value, + }).then(() => { + const speaker = ChatMessage.getSpeaker({ actor: this }); + ChatMessage.create({ + content: game.i18n.format("OSE.messages.GetExperience", { + name: this.name, + value: modified, + }), + speaker, + }); }); - }); + } else { + let xpModified; + let xp2Modified; + let xp3Modified; + let xpForUpdate = value; + let xpsToDisperse = {}; + let total = 0; + const xp2Enabled = this.data.data.details.xp2.enabled; + const xp3Enabled = this.data.data.details.xp3.enabled; + + if(xp2Enabled){ + xpForUpdate = Math.floor(value / 2); + } + + if(xp2Enabled && xp3Enabled){ + xpForUpdate = Math.floor(value / 3); + } + + xpModified = Math.floor( + xpForUpdate + (this.data.data.details.xp.bonus * xpForUpdate) / 100 + ); + + total = xpModified + total; + xpsToDisperse = { + ...xpsToDisperse, + "data.details.xp.value": xpModified + this.data.data.details.xp.value + }; + + if(xp2Enabled){ + xp2Modified = Math.floor( + xpForUpdate + (this.data.data.details.xp2.bonus * xpForUpdate) / 100 + ); + xpsToDisperse = { + ...xpsToDisperse, + "data.details.xp2.value": + xp2Modified + this.data.data.details.xp2.value + }; + total = xp2Modified + total; + } + + if(xp3Enabled){ + xp3Modified = Math.floor( + xpForUpdate + (this.data.data.details.xp3.bonus * xpForUpdate) / 100 + ); + xpsToDisperse = {...xpsToDisperse, "data.details.xp3.value": xp3Modified + this.data.data.details.xp3.value}; + total = xp3Modified + total; + } + + return this.update(xpsToDisperse).then(() => { + const speaker = ChatMessage.getSpeaker({ actor: this }); + ChatMessage.create({ + content: game.i18n.format("OSE.messages.GetExperience", { + name: this.name, + value: total, + }), + speaker, + }); + }); + + } } isNew() { diff --git a/src/module/dialog/entity-tweaks.js b/src/module/dialog/entity-tweaks.js index f85db526..188fc5ae 100644 --- a/src/module/dialog/entity-tweaks.js +++ b/src/module/dialog/entity-tweaks.js @@ -42,6 +42,38 @@ export class OseEntityTweaks extends FormApplication { /** @override */ activateListeners(html) { + + html.find("#multiclass").change(e => { + const multiClassEnabled = $(e.currentTarget).is(":checked"); + if(!multiClassEnabled){ + html.find("#xp2enabled").val(false); + html.find("#xp3enabled").val(false); + html.find("#experience2").val(0); + html.find("#experience3").val(0); + html.find("#experiencenext2").val(0); + html.find("#experiencenext3").val(0); + html.find(".additional-classes").hide(); + html.find(".first-class-multiclass-label").hide(); + } else { + html.find(".additional-classes").show(); + html.find(".first-class-multiclass-label").show(); + } + }); + + html.find("#xp2enabled").change(e => { + const xp2Enabled = $(e.currentTarget).is(":checked"); + if(!xp2Enabled){ + html.find("#xp3enabled").val(false).attr("disabled", true); + html.find("#experience3").val(0).attr("readonly", true); + html.find("#experiencenext3").val(0).attr("readonly", true); + + } else { + html.find("#xp3enabled").attr("disabled", false); + html.find("#experience3").attr("readonly", false); + html.find("#experiencenext3").attr("readonly", false); + } + }); + super.activateListeners(html); } @@ -54,6 +86,17 @@ export class OseEntityTweaks extends FormApplication { async _updateObject(event, formData) { event.preventDefault(); // Update the actor + const multiClassEnabled = $("#multiclass").is(":checked"); + const xp2Enabled = $("#xp2enabled").is(":checked"); + + if(!multiClassEnabled){ + formData['data.details.xp2.enabled'] = false; + formData['data.details.xp3.enabled'] = false; + } + if(!xp2Enabled){ + formData['data.details.xp3.enabled'] = false; + } + this.object.update(formData); // Re-draw the updated sheet this.object.sheet.render(true); diff --git a/src/scss/character.scss b/src/scss/character.scss index 1198d0ab..d7fe4b12 100644 --- a/src/scss/character.scss +++ b/src/scss/character.scss @@ -7,6 +7,7 @@ min-width: 450px; min-height: 550px; + /* ----------------------------------------- */ /* Sheet Header */ /* ----------------------------------------- */ @@ -44,6 +45,10 @@ /* ----------------------------------------- */ /* Sheet Body */ /* ----------------------------------------- */ + .multiclass .sheet-body { + height: calc(100% - 240px); + } + .sheet-body { .tab { height: 100%; @@ -242,3 +247,12 @@ /* Item Controls */ /* ----------------------------------------- */ } + +/* Tweaks Dialog */ + +.additional-classes{ + margin-bottom: 15px; + &.hidden { + display: none; + } +} diff --git a/src/templates/actors/character-sheet.html b/src/templates/actors/character-sheet.html index 10404ec2..a09fac7a 100644 --- a/src/templates/actors/character-sheet.html +++ b/src/templates/actors/character-sheet.html @@ -1,4 +1,4 @@ -
+ {{! Sheet Header }}
{{> (path "/templates/actors/partials/character-header.html") }} diff --git a/src/templates/actors/dialogs/tweaks-dialog.html b/src/templates/actors/dialogs/tweaks-dialog.html index 7612df95..87964b09 100644 --- a/src/templates/actors/dialogs/tweaks-dialog.html +++ b/src/templates/actors/dialogs/tweaks-dialog.html @@ -21,6 +21,17 @@ {{#if (eq this.type 'character')}} +
+ +
+ +
+
+ +
+ +
@@ -35,6 +46,67 @@ data-dtype="Number" />
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ {{#if data.details.xp2.enabled}} + + {{else}} + + {{/if}} +
+
+
+ +
+ {{#if data.details.xp2.enabled}} + + {{else}} + + {{/if}} +
+
+
+ +
+ {{#if data.details.xp2.enabled}} + + {{else}} + + {{/if}} +
+
+
+
diff --git a/src/templates/actors/partials/character-attributes-tab.html b/src/templates/actors/partials/character-attributes-tab.html index 534e01f3..ce5dc1d3 100644 --- a/src/templates/actors/partials/character-attributes-tab.html +++ b/src/templates/actors/partials/character-attributes-tab.html @@ -109,6 +109,17 @@

data-dtype="String" />

+ {{#if data.multiclass.enabled}} +
  • +

    + {{ localize "OSE.FractionalHpShort" }} +

    +
    + +
    +
  • + {{/if}} {{#if config.initiative}}
  • diff --git a/src/templates/actors/partials/character-header.html b/src/templates/actors/partials/character-header.html index f7210d4c..f02baade 100644 --- a/src/templates/actors/partials/character-header.html +++ b/src/templates/actors/partials/character-header.html @@ -48,8 +48,17 @@

    data-dtype="String" /> -

  • + {{/if}} +
  • + + +
  • {{/if}}
  • + {{#if data.multiclass.enabled}} + {{#if data.details.xp2.enabled}} +
      +
    • + + +
    • +
    • + + +
    • +
    • + + + {{#if data.details.xp2.bonus}} + +{{data.details.xp2.bonus}}% + {{/if}} +
    • +
    + {{/if}} + {{#if data.details.xp3.enabled}} +
      +
    • + + +
    • +
    • + + +
    • +
    • + + + {{#if data.details.xp3.bonus}} + +{{data.details.xp3.bonus}}% + {{/if}} +
    • +
    + {{/if}} + {{/if}} + diff --git a/template.json b/template.json index a1d4c40f..06e032cc 100644 --- a/template.json +++ b/template.json @@ -10,6 +10,7 @@ }, "hp": { "hd": "1d8", + "fraction": "", "value": 20, "max": 20 }, @@ -54,6 +55,9 @@ "mod": 0 } }, + "multiclass": { + "enabled": false + }, "spellcaster": { "spells": { "enabled": false, @@ -87,14 +91,33 @@ "biography": "", "notes": "", "class": "", + "class2": "", + "class3": "", + "race": "", "title": "", "alignment": "", "level": 1, + "level2": 0, + "level3": 0, "xp": { "share": 100, "next": 2000, "value": 0, "bonus": 0 + }, + "xp2": { + "enabled": false, + "share": 100, + "next": 2000, + "value": 0, + "bonus": 0 + }, + "xp3": { + "enabled": false, + "share": 100, + "next": 2000, + "value": 0, + "bonus": 0 } }, "exploration": {