spell_utils for spells

Discussion in 'General Modification' started by Sagenlicht, May 15, 2021.

Remove all ads!
  1. Sagenlicht

    Sagenlicht Established Member

    Joined:
    Apr 14, 2004
    Messages:
    338
    Likes Received:
    119
    Now that @dolio wants to do some spells as well, I started to create a spell_utils.py that does most of the standard spell work and can be simply imported in any spell.

    Code:
    from templeplus.pymod import PythonModifier
    from toee import *
    import tpdp
    from utilities import *
    print "Registering sp-Appraising Touch"
    
    def appraisingTouchSpellBonus(attachee, args, evt_obj):
        evt_obj.bonus_list.add(10, 18, "~Appraising Touch~[TAG_SPELLS_APPRAISING_TOUCH] ~Insight~[TAG_MODIFIER_INSIGHT] Bonus") #Appraising Touch is a flat +10 insight bouns to Appraise
        return 0
    
    def appraisingTouchSpellTooltip(attachee, args, evt_obj):
        if args.get_arg(1) == 1:
            evt_obj.append("Appraising Touch ({} round)".format(args.get_arg(1)))
        else:
            evt_obj.append("Appraising Touch ({} rounds)".format(args.get_arg(1)))
        return 0
    
    def appraisingTouchSpellEffectTooltip(attachee, args, evt_obj):
        if args.get_arg(1) == 1:
            evt_obj.append(tpdp.hash("APPRAISING_TOUCH"), -2, " ({} round)".format(args.get_arg(1)))
        else:
            evt_obj.append(tpdp.hash("APPRAISING_TOUCH"), -2, " ({} rounds)".format(args.get_arg(1)))
        return 0
    
    def appraisingTouchSpellHasSpellActive(attachee, args, evt_obj):
        spellPacket = tpdp.SpellPacket(args.get_arg(0))
        if evt_obj.data1 == spellPacket.spell_enum:
            evt_obj.return_val = 1
        return 0
    
    def appraisingTouchSpellKilled(attachee, args, evt_obj):
        args.remove_spell()
        args.remove_spell_mod()
        return 0
    
    def appraisingTouchSpellSpellEnd(attachee, args, evt_obj):
        print "appraisingTouchSpellSpellEnd"
        return 0
    
    appraisingTouchSpell = PythonModifier("sp-Appraising Touch", 2) # spell_id, duration
    appraisingTouchSpell.AddHook(ET_OnGetSkillLevel, EK_SKILL_APPRAISE, appraisingTouchSpellBonus,())
    appraisingTouchSpell.AddHook(ET_OnGetTooltip, EK_NONE, appraisingTouchSpellTooltip, ())
    appraisingTouchSpell.AddHook(ET_OnGetEffectTooltip, EK_NONE, appraisingTouchSpellEffectTooltip, ())
    appraisingTouchSpell.AddHook(ET_OnD20Signal, EK_S_Spell_End, appraisingTouchSpellSpellEnd, ())
    appraisingTouchSpell.AddHook(ET_OnD20Query, EK_Q_Critter_Has_Spell_Active, appraisingTouchSpellHasSpellActive, ())
    appraisingTouchSpell.AddHook(ET_OnD20Signal, EK_S_Killed, appraisingTouchSpellKilled, ())
    appraisingTouchSpell.AddSpellDispelCheckStandard()
    appraisingTouchSpell.AddSpellTeleportPrepareStandard()
    appraisingTouchSpell.AddSpellTeleportReconnectStandard()
    appraisingTouchSpell.AddSpellCountdownStandardHook()

    Code:
    from templeplus.pymod import PythonModifier
    from toee import *
    import tpdp
    from utilities import *
    import spell_utils
    print "Registering sp-Appraising Touch"
    
    def appraisingTouchSpellBonus(attachee, args, evt_obj):
        evt_obj.bonus_list.add(10, 18, "~Appraising Touch~[TAG_SPELLS_APPRAISING_TOUCH] ~Insight~[TAG_MODIFIER_INSIGHT] Bonus") #Appraising Touch is a flat +10 insight bouns to Appraise
        return 0
    
    appraisingTouchSpell = PythonModifier("sp-Appraising Touch", 2) # spell_id, duration
    appraisingTouchSpell.AddHook(ET_OnGetSkillLevel, EK_SKILL_APPRAISE, appraisingTouchSpellBonus,())
    appraisingTouchSpell.AddHook(ET_OnGetTooltip, EK_NONE, spell_utils.spellTooltip, ())
    appraisingTouchSpell.AddHook(ET_OnGetEffectTooltip, EK_NONE, spell_utils.spellEffectTooltip, ())
    appraisingTouchSpell.AddHook(ET_OnD20Signal, EK_S_Spell_End, spell_utils.spellEnd, ())
    appraisingTouchSpell.AddHook(ET_OnD20Query, EK_Q_Critter_Has_Spell_Active, spell_utils.queryActiveSpell, ())
    appraisingTouchSpell.AddHook(ET_OnD20Signal, EK_S_Killed, spell_utils.spellKilled, ())
    appraisingTouchSpell.AddSpellDispelCheckStandard()
    appraisingTouchSpell.AddSpellTeleportPrepareStandard()
    appraisingTouchSpell.AddSpellTeleportReconnectStandard()
    appraisingTouchSpell.AddSpellCountdownStandardHook()

    I will add dismiss, concentration as templates as well and will post the spell_utils as soon as I am finished. Just ran out of time for now. Will be done latest on monday. Just a heads up for dolio :)

    Code:
    def spellTooltip(attachee, args, evt_obj):
        spellEnum = tpdp.SpellPacket(args.get_arg(0)).spell_enum
        spellName = game.get_spell_mesline(spellEnum)
        spellDuration = args.get_arg(1)
        if spellDuration == 1:
            evt_obj.append("{} ({} round)".format(spellName, spellDuration))
        else:
            evt_obj.append("{} ({} rounds)".format(spellName, spellDuration))
        return 0
    
    def spellEffectTooltip(attachee, args, evt_obj):
        spellEnum = tpdp.SpellPacket(args.get_arg(0)).spell_enum
        spellName = game.get_spell_mesline(spellEnum)
        spellName = (spellName.upper()).replace(" ", "_")
        spellDuration = args.get_arg(1)
        if args.get_arg(1) == 1:
            evt_obj.append(tpdp.hash("{}".format(spellName)), -2, " ({} round)".format(spellDuration))
        else:
            evt_obj.append(tpdp.hash("{}".format(spellName)), -2, " ({} rounds)".format(spellDuration))
        return 0
    
    def queryActiveSpell(attachee, args, evt_obj):
        spellPacket = tpdp.SpellPacket(args.get_arg(0))
        if evt_obj.data1 == spellPacket.spell_enum:
            evt_obj.return_val = 1
        return 0
    
    def spellKilled(attachee, args, evt_obj):
        args.remove_spell()
        args.remove_spell_mod()
        return 0
    
    def spellEnd(attachee, args, evt_obj):
        spellEnum = tpdp.SpellPacket(args.get_arg(0)).spell_enum
        spellName = game.get_spell_mesline(spellEnum)
        print "{} SpellEnd".format(spellName)
        return 0
    
    def checkRemoveSpell(attachee, args, evt_obj):
        if evt_obj.data1 == args.get_arg(0):
            args.remove_spell()
            args.remove_spell_mod()
        return 0
    
    def addConcentration(attachee, args, evt_obj):
        spellPacket = tpdp.SpellPacket(args.get_arg(0))
        spellPacket.caster.condition_add_with_args('sp-Concentrating', args.get_arg(0))
        return 0
    
    def addDimiss(attachee, args, evt_obj):
        spellPacket = tpdp.SpellPacket(args.get_arg(0))
        spellPacket.caster.condition_add_with_args('Dismiss', args.get_arg(0))
        return 0
    
    def removeTempHp(attachee, args, evt_obj):
        attachee.d20_send_signal(S_Spell_End, 'Temporary_Hit_Points')
        return 0
    

    I think this is what you meant by adding a template @Sitra Achara right?

    EDIT: added Concentration, Dismiss and RemoveTempHp
     
    Last edited: May 15, 2021
    _doug_ likes this.
  2. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Pretty much yeah. You can also go one step further and create an inherited class from PythonModifier that includes all the boilerplate hooks by default (with optional args to disable each one).
     
  3. dolio

    dolio Established Member Supporter

    Joined:
    May 30, 2005
    Messages:
    338
    Likes Received:
    81
    Oh, cool. I had noticed that a lot of stuff could seemingly be made general like that. In fact, some of that e.g. tooltip code is better than is in a lot of new conditions, because it takes the display from the spell that was cast rather than the condition, which is how it tends to work in the base game (e.g. if you make a spell 'Greater Enlarge Person', and it reuses the same condition as 'Enlarge Person', the tooltip still says 'Greater'). I was trying to make anything I added act this way.

    I basically have Kelgore's Fire Bolt done. The only minor issue is that it's impossible for it to be dual school (as far as I can tell). So I just made it evocation. The conjuration part is just making a small rock, which could just be lying around in most cases. I could change it if Temple+ gets support for dual school spells, but there's probably more interesting things to add than that.

    I'm trying to do tripping hand next.

    @Sagenlicht Do you want pull requests for spells? I wasn't really intending on just sticking to spell compendium or PHB2. So I could just submit spell compendium spells, or that plus PHB2 (since it's kind of core). Or whatever.
     
  4. Sagenlicht

    Sagenlicht Established Member

    Joined:
    Apr 14, 2004
    Messages:
    338
    Likes Received:
    119
    Ok :)

    No, you can ofc do spells form the Spell compendium too :) If you want to, you can pm me and I add your github acc to my spell_compendium hub. Then you can simply open a branch and push new spells to it :)
    Do you care what spells do you want to do? If not I can share you my list of spells, I am working on currently and you tell me what you wanna do from them so we avoid doing work twice. You can simply use then the next pell_enum from the list, no need to pick one form your fresh pool then :)

    And again if you need any help let me know, I will gladly try to help :)

    Edit: I added the first version of the spell_utils for you, so you can start using it. I will add more to it, but alot of standard wokr is already implemented.

    To use dimiss:
    [pythonModifier].AddHook(ET_OnConditionAdd, EK_NONE, spell_utils.addDimiss, ())
    [pythonModifier].AddHook(ET_OnD20Signal, EK_S_Dismiss_Spells, spell_utils.checkRemoveSpell, ())
    Concentration:
    [pythonModifier].AddHook(ET_OnConditionAdd, EK_NONE, spell_utils.addConcentration, ())
    [pythonModifier].AddHook(ET_OnD20Signal, EK_S_Concentration_Broken, spell_utils.checkRemoveSpell, ())
    removeTempHP:
    [pythonModifier].AddHook(ET_OnD20Signal, EK_S_Temporary_Hit_Points_Removed, spell_utils.removeTempHp, ())
     

    Attached Files:

    Last edited: May 15, 2021
  5. dolio

    dolio Established Member Supporter

    Joined:
    May 30, 2005
    Messages:
    338
    Likes Received:
    81
    I already have a pretty big list of spells I'd like to try implementing. I might just prioritize things that didn't make it into the compendium since you have that covered pretty well. I think the only compendium spell I've done anything on so far is Reciprocal Gyre.
     
  6. anatoliy

    anatoliy Established Member

    Joined:
    Feb 18, 2017
    Messages:
    635
    Likes Received:
    200
    I can also add helpful code which was developed for SGoS.

    One thing which helped me a lot was "header" like toee.py substitution file, which I generated \ coded for Visual Studio lookup. toee.py It was either lookup cpp code each time, or use VS lookup feature. I mostly used both.

    For modifiers, which I created like 60 of them, I also created substitution file tpdp.py, which is quite complex. One of the most lookuped thing was determination of which class instance will be present in the evt_obj argument.

    Code:
    def OnGetAC(attachee, args, evt_obj):
        assert isinstance(attachee, toee.PyObjHandle)
        assert isinstance(args, tpdp.EventArgs)
        assert isinstance(evt_obj, tpdp.EventObjAttack)
    
        evt_obj.bonus_list.add_cap(8, 0, 189)#{189}{~Blinded~[TAG_BLINDED]}
        evt_obj.bonus_list.add_cap(3, 0, 189)#{189}{~Blinded~[TAG_BLINDED]}
        return 0
    
    modObj = templeplus.pymod.PythonModifier(GetConditionName(), 3) # reserved
    modObj.AddHook(toee.ET_OnGetAC, toee.EK_NONE, OnGetAC, ())
    That is why I used such code: assert isinstance(evt_obj, tpdp.EventObjAttack), plus full evaluation path to methods. It allows VS to determine object type or class.

    For example blind.py, which I created as modifier for Blind Troll in my Adventure.

    And still I strongly recommend to look at Temple + modifiers created by Sitra and Doug, they are great place to learn.
     
  7. dolio

    dolio Established Member Supporter

    Joined:
    May 30, 2005
    Messages:
    338
    Likes Received:
    81
    By the way, there's some boilerplate for creating touch spells (the sort that actually use the charge holding mechanics) that I can add to this once it gets added to version control somewhere.
     
  8. Sagenlicht

    Sagenlicht Established Member

    Joined:
    Apr 14, 2004
    Messages:
    338
    Likes Received:
    119
    I sadly found no time to work on the spell_utils the last tow days :( Pushed it to the temple+ github (did create a new branch) what I've done so far.
     
Our Host!