#Gestalt

1 messages · Page 1 of 1 (latest)

cloud bear
#

So the poor "I play the game as intended" folks don't have to be troubled

stray prawn
#

Makes sense

cloud bear
#

So, your actor has still double their level in hit dice?

stray prawn
#

Yup, for some reason

#

module.json has the new javascript in, it's in a separate file

cloud bear
#

I’ll grab the whole thing and paste it in here in a few. When you say module.json, how exactly is your module set up?

stray prawn
#
{
  "id": "gestalt-support",
  "esmodules": [
    "Experience.js",
    "LevelOverride.js",
    "HitDiceOverride.js"
  ],
  "title": "Gestalt Support",
  "version": "1.0.0",
  "compatibility": {
    "minimum": "13",
    "verified": "13"
  },
  "flags": {
    "canUpload": true
  },
  "packs": [
    {
      "name": "gestalt-classes-custom",
      "label": "Gestalt Classes",
      "path": "packs/gestalt-classes-custom",
      "type": "Item",
      "ownership": {
        "PLAYER": "OBSERVER",
        "ASSISTANT": "OWNER"
      },
      "system": "dnd5e",
      "flags": {}
    }
  ],
  "relationships": {
    "requires": [
      {
        "id": "alches-stuff",
        "type": "module",
        "compatibility": {}
      }
    ]
  },
  "description": ""
}
cloud bear
#

Ah. And in HitDiceOverride do you have BOTH lines in the init hook? Because you shouldn't - I have them both because I have my level and hit dice thing in the same file

stray prawn
#

Oh, I was wondering if it was that

#

So I tried putting bits into the LevelOverride but it still didn't work, probably cause I pasted it in wrong

cloud bear
#

Yep - that new file isn't running because it's got an error. Walking the dog but in a few I’ll grab the full code and paste it here

stray prawn
#

🫡

#

In the meantime I'll take out the duplicate line and see if it works

#

Cool, it does

cloud bear
#
// GESTALT
Hooks.on("init", () => {
  dnd5e.dataModels.actor.config.character = CharacterDataGestalt;
  dnd5e.dataModels.item.config.class = ClassDataGestalt;
});

Hooks.on("dnd5e.computeLeveledProgression", (progression, actor, cls, spellcasting, count) => {
  // Stop if already did this type of caster
  if (count > 1 && progression.slot > 0) return false;
  const allProgressions = Object.values(actor.spellcastingClasses).map(i => i.spellcasting?.progression).filter(i => i);
  // Anything's better than third
  if (spellcasting.progression === "third" && allProgressions.some(i => i !== "third")) return false;
  // Otherwise go for it, unless "full" is out there
  if (spellcasting.progression !== "full" && allProgressions.includes("full")) return false;
});

class CharacterDataGestalt extends dnd5e.dataModels.actor.CharacterData {
  prepareBaseData() {
    this.attributes.hd = new dnd5e.documents.HitDice(this.parent);
    this.details.level = 0;
    this.attributes.attunement.value = 0;

    for ( const item of this.parent.items ) {
      const canAttune = !item.system.validProperties?.has?.("mgc") || item.system.properties?.has?.("mgc");
      if ( item.system.attuned && canAttune ) this.attributes.attunement.value += 1;
      if ( item.type === "class" ) this.details.level += item.system.levels;
  }

    // THIS IS THE GESTALT LINE
    this.details.level = Math.floor(this.details.level / 2);

    this.attributes.prof = dnd5e.documents.Proficiency.calculateMod(this.details.level);

    const { xp, level } = this.details;
    xp.max = level >= CONFIG.DND5E.maxLevel ? Infinity : this.parent.getLevelExp(level || 1);
    xp.min = level ? this.parent.getLevelExp(level - 1) : 0;
    if ( Number.isFinite(xp.max) ) {
      const required = xp.max - xp.min;
      const pct = Math.round((xp.value - xp.min) * 100 / required);
      xp.pct = Math.clamp(pct, 0, 100);
    } else if ( game.settings.get("dnd5e", "levelingMode") === "xpBoons" ) {
      const overflow = xp.value - this.parent.getLevelExp(CONFIG.DND5E.maxLevel);
      xp.boonsEarned = Math.max(0, Math.floor(overflow / CONFIG.DND5E.epicBoonInterval));
      const progress = overflow - (CONFIG.DND5E.epicBoonInterval * xp.boonsEarned);
      xp.pct = Math.clamp(Math.round((progress / CONFIG.DND5E.epicBoonInterval) * 100), 0, 100);
    } else {
      xp.pct = 100;
    }

    dnd5e.dataModels.actor.AttributesFields.prepareBaseArmorClass.call(this);
    dnd5e.dataModels.actor.AttributesFields.prepareBaseEncumbrance.call(this);
  }
}

class ClassDataGestalt extends dnd5e.dataModels.item.ClassData {
  prepareFinalData() {
    super.prepareFinalData();
    if (!this.parent.actor) return;
    const maxHdSize = Math.max(...this.parent.actor.system.attributes.hd.sizes);
    const bestClass = Object.values(this.parent.actor.classes).find(i => Number(i.system.hd.denomination.slice(1)) === maxHdSize);
    if (bestClass === this.parent) return;
    this.hd.max = Math.max(0, this.hd.additional);
    this.hd.value = Math.max(this.hd.max - this.hd.spent, 0);
  }
}
cloud bear
# stray prawn

Awesome - yeah, I kept it this way because if for whatever reason something were to give "additional" hit dice to the rogue class, you'll get them. It's just the base "same-as-level" amount you don't get

stray prawn
stray prawn
#

Eh, if it works, it works

#

Confirmed it works

cloud bear
#

Might've been some letter duplicated or deleted or something

stray prawn
#

Right, so character level and proficiency bonus sorted out, hit dice and hit points sorted out, spellcaster progression sorted out

#

I don't think there's anything else that Gestalt changes innately

cloud bear
#

Yeah, the only thing to keep in mind is what I mentioned before about "on this overall level you get X" stuff, since you'll become level 3 twice for instance

stray prawn
#

Experience Gain is optional depending on what rule you use and can be done through an array (for someone reading, reference this #dnd5e message), what you get from which class is also dependent on the rule and can be modified inside the class itself, that should be it

cloud bear
#

That's as easy as just having them delete the second version of the granted advancement though

stray prawn
#

Exactly

#

So that's not much of a problem, just gotta remember to do it

#

Oh, one problem that's left but is kind of there because of my own implementation is with consumption

#

Nevermind, it does work as it should actually

#

I was worried that when dragging the whole class folder from one compendium to another, the class would reference items from the new one, but thankfully it doesn't, so nothing should break as long as the old compendium still exists

stray prawn
#

@cloud bear I think the proficiency bonus is broken
You get to +3 after reaching character level 3, then to +4 on reaching character level 11, and +5 on level 19
I tested on a world with only the gestalt module activated in case it was some sort of module conflict, but the issue's still there

cloud bear
#

Hmm. In my world it seems to be working. Level 3 (3 monk, 3 rogue) has +2 proficiency

stray prawn
#

I wonder what I did that broke things

#

(Or what I didn't do)

#

Ohhhh

#

I know facepalmpicard

cloud bear
#

something else is modifying proficiency

stray prawn
#

Yeah

cloud bear
#

ohhhh

stray prawn
#

Pfffft

cloud bear
#

You still had the old override Jeff mentioned didn't you

stray prawn
#

I just checked Experience.js, what a surprise

#

Sorry for bothering you

cloud bear
#

No worries