August's Workshop - Duskblade

Discussion in 'General Modification' started by August, Mar 14, 2023.

Remove all ads!
  1. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    Most of the way through this but stuck on the spell swapping. Posting my bodged python so someone else can have a look while I start on the spells and on Quick Cast. Other than the spell swapping, it works, so I'm not gonna get hung up on the bug just yet.

    Code:
    def InitSpellSelection( obj, classLvlNew = -1, classLvlIncrement = 1):
        classLvl = obj.stat_level_get(classEnum)
        if classLvlNew <= 0:
            classLvlNew = classLvl + 1
        maxSpellLvl = char_editor.get_max_spell_level( obj, classEnum, classLvlNew ) # this regards spell list extension by stuff like Mystic Theurge
       
        # Available Spells (spells you can choose, on the left side)
        spAvail = char_editor.get_learnable_spells(obj, classEnum, maxSpellLvl)
        # add spell level labels
        for p in range(0,maxSpellLvl+1):
            spAvail.append(char_editor.KnownSpellInfo(spell_label_level_0 + p, 0, classEnum))
        spAvail.sort()
        char_editor.append_available_spells(spAvail)
       
        # Create Known Spells list (right side) spEnums
            spEnums = []
       
       
        # Case 1 - newly taken class
        if classLvlNew == 1:
            spEnums.append(char_editor.KnownSpellInfo(spell_label_level_0, 0, classEnum)) # add "Level 0" label
            intScore = obj.stat_level_get(stat_intelligence)
            intModCantrips = ((intScore - 10) / 2) + 2
            for p in range(0, intModCantrips): # 2 plus int mod cantrips
                spEnums.append(char_editor.KnownSpellInfo(spell_new_slot_lvl_0, 3, classEnum))
            spEnums.append(char_editor.KnownSpellInfo(spell_label_level_1, 0, classEnum)) # add "Level 1" label
            for p in range(0,2): # 2 level 1 spells
                spEnums.append(char_editor.KnownSpellInfo(spell_new_slot_lvl_1, 3, classEnum))
            char_editor.append_spell_enums(spEnums)
            return 0
       
        # Case 2 - Incrementing class level
        if classLvl <= 20:
            #Duskblades always gain a single spell of any level they can cast.
            spEnums = char_editor.get_known_class_spells(obj, classEnum) # get all spells known by this character for this class
            for spellLvl in range(0, maxSpellLvl+1):
                spEnums = []
                vacant_slot = char_editor.KnownSpellInfo(spell_vacant, 3, classEnum) # sets it to spell level -1
                spEnums.append(vacant_slot)
        else: # Epic Level duskblades don't get more spells.
            return 0
       
        # Handle spell replacement on even levels
        isReplacing = 0
        if classLvlNew >= 5 and ((classLvlNew - 5) % 2) == 0: # spell replacement for odd levels
            isReplacing = 1
        if char_editor.get_class_code() !=  classEnum: #grant this benefit only for strict levelup (also to prevent some headache...)
            isReplacing = 0
       
        if isReplacing == 0:
            spEnums.sort()
            char_editor.append_spell_enums(spEnums)
            return 0
       
        # mark as replaceable
        for p in range(0,len(spEnums)):
            spEnum = spEnums[p].spell_enum
            if spell_vacant <= spEnum <= spell_label_level_5:
                continue
            if spell_new_slot_lvl_0 <= spEnum <= spell_new_slot_lvl_5:
                continue
            if char_editor.get_spell_level(spEnum, classEnum) <= maxSpellLvl-2:
                spEnums[p].spell_status = 1 # marked as replaceable
       
        # Finally, return spEnums list to the engine
        spEnums.sort()
        char_editor.append_spell_enums(spEnums)
        return 0
     
    Buffed Rabbit likes this.
  2. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    Quick Cast is functional! Also got the spell list set up with what's currently available. Definitely seeing the need to tidy up what Sagenlicht had left behind to thicken out the spell list. Anyone know what happened to him?

    Still having problems with spell swapping but it works fine otherwise.
     
    Last edited: Mar 28, 2023
  3. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    Minor errors with Quick Cast. I'm a miserable coder, so my work is buggy. It's not taking charges right, although it is quickening. I've tried writing a way to set charges based on level a few different ways and have biffed it each time so I'm moving on to the next thing.

    Code:
    def DSKQuickCastRadial(attachee, args, evt_obj):
        #Add a checkbox to turn on and off quick cast if there is a charge available, otherwise just don't show it
        if args.get_arg(0):
            checkboxQuickcast = tpdp.RadialMenuEntryToggle("Quick Cast", "TAG_INTERFACE_HELP")
            checkboxQuickcast.link_to_args(args, 1)
            checkboxQuickcast.add_child_to_standard(attachee, tpdp.RadialMenuStandardNode.Class)
        return 0
    
    def DSKQuickCastNewDay(attachee, args, evt_obj):
        #One charge per day
        args.set_arg(0, 1)
        #Set the checkbox to off at the begining of the day
        args.set_arg(1, 0)
        
        return 0
    
    def DSKQuickCastMetamagicUpdate(attachee, args, evt_obj):
        #Check for a charge
        charges = args.get_arg(0)
        if charges < 1:
            return 0
        
        #Check if this is turned on
        if not args.get_arg(1):
            return 0
        
        #Get the metamagic info
        metaMagicData = evt_obj.meta_magic
        
        #Don't quicken more than once we don't want this to stack with other Quickens
        if metaMagicData.get_quicken() < 1:
            metaMagicData.set_quicken(1)
        
        return 0
        
    def DSKQuickCastDeductCharge(attachee, args, evt_obj):
        #Check for a charge and the enable flag
        charges = args.get_arg(0)
        if charges < 1 or not args.get_arg(1):
            return 0
            
        #Decriment the charges
        charges = charges - 1
        args.set_arg(0, charges)
    
        return 0
    
    #Setup the feat
    tpdp.register_metamagic_feat("Sudden Quicken")
    DSKQuickCast = PythonModifier("Duskblade Quick Cast", 4) #Charges, Toggeled On, Spare, Spare
    DSKQuickCast.MapToFeat("Duskblade Quick Cast")
    DSKQuickCast.AddHook(ET_OnBuildRadialMenuEntry, EK_NONE, DSKQuickCastRadial, ())
    DSKQuickCast.AddHook(ET_OnConditionAdd, EK_NONE, DSKQuickCastNewDay, ())
    DSKQuickCast.AddHook(ET_OnNewDay, EK_NEWDAY_REST, DSKQuickCastNewDay, ())
    DSKQuickCast.AddHook(ET_OnMetaMagicMod, EK_NONE, DSKQuickCastMetamagicUpdate, ())
    DSKQuickCast.AddHook(ET_OnD20PythonSignal, "Quick Cast Deduct Charge", DSKQuickCastDeductCharge, ())
    
     
  4. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    - Spells work, the spell swap at 5th/every other is not working
    - Quick cast works but has no limit on spell level or charges yet

    Good progress for today, I think.
     
    Buffed Rabbit likes this.
  5. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    It's another beautiful day. Plenty of time today to work. My darling and I have dinner plans, but the day is mine.

    On the agenda for today:
    - Fix Quick Cast.
    - Make Arcane Attunement! It'll be a pretty simple radial ability; 3+Int uses a day, casts detect magic, flare, and read magic.

    One piece at a time... trying not to go nuts... but I may stop and come up with my own idea for how ghost sound can work since it will also go into Arcane Attunement.
     
    Marcelo Abner likes this.
  6. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    Done some reading and figured out some odds and ends today but still not figured out what I'm doing wrong with Quick Cast or with the spellcasting for that matter. My sweetheart's had a hard day so I'm focusing on that :(
     
    Last edited: Mar 29, 2023
    Buffed Rabbit likes this.
  7. Shiningted

    Shiningted I want my goat back Administrator

    Joined:
    Oct 23, 2004
    Messages:
    12,651
    Likes Received:
    350
    Love is the death of duty - Maester Aemon Targaryen

    Get back to work! :p
     
    August likes this.
  8. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    Stumped on Quick Cast. Not sure what I'm doing wrong - advice is welcome. Sticking and moving to Arcane Attunement setup. Also reading up on how spells work - I think I will try to make ghost sound.

    Ghost sound
    is an illusion cantrip, and I see it as basically being a weaker version of phantom threat that only lasts for a round. Would fit nice next to daze and flare, and is an honest to goodness basekit cantrip missing from the game.

    The unfinished Spell Compendium work by dolio and Sagenlicht fleshes out the spell list so much. I would love to help finalize it for the next Temple Plus version, whenever that is, as well as just get a functional modded state of it together for now.

    Dolio had already made a few of the lower level duskblade spells:
    https://github.com/dolio/toee-mod
    That's Kelgore's fire bolt, rouse, and Bigby's tripping hand! The others are missing beguiler spells.
     
    Last edited: Mar 30, 2023
  9. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    Hey @Shiningted how do I add a spell to the main lists (like sor/wiz)? Kinda teaching myself spell creation and not getting that part since most of the spell lists for custom classes are defined in the file.
    EDIT: Nevermind, figured it out. The answer is always check your resources first.
    https://github.com/GrognardsFromHell/TemplePlus/wiki/Step-by-Step-Guide-for-creating-a-new-spell

    Also thanks for the support.

    I've managed to make my first spell this morning - just a script kiddie modification of Sagenlicht's Phantom Threat to be only applicable for a round (frankly phantom threat always a weaksauce spell; even as a cantrip it's weak when compared to daze and flare, but it has applications for arcane tricksters). I wanted Arcane Attunement to have a point, so for now it's ghost sound.

    I have an idea of reworking it to instead create a sort of aural focal point (sort of like grease) which flanks anyone in the range to reflect the sounds messing with them, and then give it the original duration of 1 round/level, but to keep it weak you get a save every round to disbelieve. If a creature with Int 11+ disbelieves, it ends the illusion. Plus, that way phantom threat can still also have a point.

    With that knocked out (for now), making Arcane Attunement is next for the day.
     
    Last edited: Mar 31, 2023
    Buffed Rabbit likes this.
  10. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    Great progress today. First, I got Quick Cast debugged! I think I might redo the radial to show charges later, or maybe not work off a toggle or have some printed text (for now it's just a Sudden Quicken copy with charges, but the charges work). Arcane Attunement is now my focus. The radial menu pointing to some spells is simple enough, but now that I've learned how to make spells, the idea of implementing ghost sound 'properly' is very enticing so I'm going for it.

    Here's the rules on ghost sound: https://www.d20srd.org/srd/spells/ghostSound.htm

    Here's my notes on how to make this work. @anatoliy has made some great RAW/balance discussions in the forums here - hope you don't mind me tagging you for an opinion.

    - Ghost sound makes the noise equivalent of four humans (that's a small radius, 5ft circle), +4 humans per caster level to a max of 20 at 5th level. I see this as a spell that makes an AOE equivalent to that size radius; I would argue the equivalent is a 5ft circle at 1st level, increasing to a 10ft circle at 5th level.
    - Ghost sound can make threatening noises that distract or mislead. I think causing a target to think a threat is behind them is a basic application, and if they fall for it, they're flanked.
    - If anyone beats ghost sound's save, it should be proven fake and disbelieved, dissipating the illusion entirely.
    - Every turn you are affected, you get a new save, since ghost sound is a cantrip. The sound that ghost sound makes is the sound of me smashing it in the kneecaps so it doesn't outshine phantom threat.

    Honestly phantom threat will be better for consistency, since its a simple save-or-suck and has a better DC.

    In truth, I'm considering making a take on all the big illusion spells that haven't been done yet next, including silent, minor, major, and persistent image. The idea is to make transparent illusion summons that can't hurt anyone, but can mislead the enemy and confound the AI. This would enable an actual illusionist play style that I feel is missing.

    Illusions will need some unifying mechanics to help them stand out.
    - Figments. Some illusions create a figment. The figment is controlled like a summon. The AC of a figment is 10 + size modifier. The figment never does damage but is a big honkin' distraction, flanking and bugging people and generally giving the AI a hard time.
    - Disbelief. When an illusion is disbelieved, all their allies gain +4 to saves to also disbelieve.
    - Flimsyness. The rules say "a character faced with proof that an illusion isn’t real needs no saving throw." Certain illusions are so simple that they are flimsy; once disbelieved, they are so obvious nobody "needs to save" against them, so they disapparate. Ghost sound is a cantrip so of course it is flimsy. Once someone makes the save, the effect dies.

    Also need to figure out how I glitched up spell retraining, but one thing at a time...
     
    Last edited: Mar 31, 2023
  11. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    Spent a lot of day reading. I think I'm going to put the ghost sound project on hold for now as I researched more about where Sagenlicht had left the spell compendium project before disappearing. AoeSpellHandleModifier and AoESpellEffectModifier are how I want to make it work. Tomorrow I will instead just finish up getting arcane attunement working and then move on to the big one - arcane channeling - and of course the duskblade spell power ability. I've got a pretty good idea on how to get both working now. Once those abilities are locked in the duskblade will be mostly feature complete; I'll then go back and clean up the help files for the NPC classes and duskblade, then get a fork going and prep everything for the next Temple+ version.

    I'm hoping to have it all done very soon, and I'm finally allowing myself to commit to what the next thing I should work on is, and that is helping get all the beta spells people have worked on cleaned up and ready for Temple+ implementation. If I can help with that in a meaningful way, I will build off of that to make my illusion magic overhaul happen.
     
  12. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    Did a lot of reading, made a shortlist of spells I will make, started getting a Github ready, fiddling with arcane attunement. Almost done with it. Very excited to get on arcane channeling.
     
    Buffed Rabbit and Endarire like this.
  13. Illustair

    Illustair First Order Wizard

    Joined:
    Jan 14, 2011
    Messages:
    78
    Likes Received:
    11
    Looking forward to the finished product of your work! Glad there’s interest in this game. Just got back recently myself.
     
    August likes this.
  14. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    You know what's annoying? Radial menus. Very, very annoying!
    I can get Arcane Attunement working but getting the radial menu working nice is making me want to scream.

    Where we're at:
    - Still to do: arcane channeling, spell power
    - Still to brush up: Arcane attunement's radials suck, make some help files, get 5th+ level spell swap working
     
    Last edited: Apr 2, 2023
  15. August

    August Established Member

    Joined:
    Jun 28, 2006
    Messages:
    119
    Likes Received:
    83
    It's been a morning of it. I considered being really fancy and hooking into the dispatcher to apply for all touch spells... and I still am, because a duskblade/cleric/mystic theurge could be a lot of fun (abjurant champion eat your heart out), and RAW arcane channeling applies to all touch spells you cast. However, it only applies to eight duskblade spells, two of which I'm going to be making soon (though dispelling touch and slashing dispel seem like they'll be very easy to put together), so the idea crossed my mind to just do something easy and hacky...

    I kinda want to do it the "right" way which means rooting through the dispatcher. It'll work as follows:
    - A toggle to turn on, sets the flag
    - When a spell is cast, query if it is a hostile touch attack spell
    - Replace the touch attack with a melee attack, or if a ranged weapon is equipped, an unarmed attack
    - If a lv13 duskblade, a full attack is possible with the spell - this will be the annoying part

    Having a hard time figuring out what to hook up where... I wish I asked this two years ago (when I was also trying to make a duskblade on a different computer!) when people were more active, but I was doing pandemic response stuff and my free time would just disappear for long stretches...

    I'm also reading on the philosophy of what this attack actually really does - rules lawyering welcome:
    https://forums.giantitp.com/showthread.php?369995-The-Duskblade-and-Arcane-Channeling
    Takeaways:
    - It should be able to crit.
    - Despite some people who pulled a fast one on their DM saying otherwise, ranged spells don't matter. A "touch spell" has a range of Touch.
    - The spell isn't discharged at end of round leaving it free for an AoO if you miss - until you hit level 13 at which point it's always dispelled at end of round, per Sage Advice. This sounds dumb, I'm probably gonna have it always dispel at end of round for consistency, come fix it yourself if you don't like it nerd
    - On Full Attack mode after Level 13 you can hit everyone with a full attack and affect each struck target with the spell, and this should theoretically apply to Whirlwind Attack. Spiked chain duskblade stay winning.
    - Chill Touch is going to be annoying.
     
    Last edited: Apr 2, 2023
Our Host!