Temple+ New Feats

Discussion in 'General Modification' started by WinstonShnozwick, Sep 18, 2016.

Remove all ads!
  1. WinstonShnozwick

    WinstonShnozwick Established Member

    Joined:
    Mar 2, 2011
    Messages:
    628
    Likes Received:
    23
    How do we add new custom feats to the game through temple+?
     
  2. Onyx

    Onyx The Slayer

    Joined:
    Apr 13, 2009
    Messages:
    20
    Likes Received:
    3
    I'm not completely sure but I think the new framework for creating feats is going to be in the next update along with the assassin, which hasn't been released yet as far as I'm aware.
     
  3. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Edit: now with its own Wiki entry.

    -----------------

    Making a feat has two parts:
    1. Making a formatted .txt entry under rules\feats
    The format is
    Code:
    name: <feat name>
    flags: <flags>
    prereqs: <code> <value> <code> <value> ... <code> <value>
    description: <description; must be one line; use vertical tab character if you need line breaks ( 0xB in hex, see the help.tab file for example) >
    prereq descr: <short prerequisitive description>
    
    The flags field is a number representing a bitfield composed of the following flags:
    Code:
        FPF_CAN_GAIN_MULTIPLE_TIMES = 0x1,
        FPF_DISABLED = 0x2,
        FPF_RACE_AUTOMATIC = 0x4,
        FPF_CLASS_AUTMATIC = 0x8 ,
        FPF_FIGHTER_BONUS = 0x10,
        FPF_MONK_BONUS_1st = 0x20,
        FPF_MONK_BONUS_2nd = 0x40,
        FPF_MONK_BONUS_6th = 0x80,
        FPF_MULTI_SELECT_ITEM = 0x100,
        FPF_EXOTIC_WEAP_ITEM = 0x300,
        FPF_IMPR_CRIT_ITEM = 0x500,
        FPF_MARTIAL_WEAP_ITEM = 0x900,
        FPF_SKILL_FOCUS_ITEM = 0x1100,
        FPF_WEAP_FINESSE_ITEM = 0x2100,
        FPF_WEAP_FOCUS_ITEM = 0x4100,
        FPF_WEAP_SPEC_ITEM = 0x8100,
        FPF_GREATER_WEAP_FOCUS_ITEM = 0x10100,
        FPF_WIZARD_BONUS = 0x20000,
        FPF_ROGUE_BONUS = 0x40000, // rogue bonus at 10th level
    
        FPF_MULTI_MASTER = 0x80000, // head of multiselect class of feats (NEW)
        FPF_GREAT_WEAP_SPEC_ITEM = 0x100100, // NEW   
    You have to choose the relevant flag values, OR them, and input the resultant number in decimal. I think most of these aren't really relevant under the new class spec system except for:
    * FPF_CLASS_AUTMATIC - causes the feat to be hidden from normal feat selection
    * FPF_CAN_GAIN_MULTIPLE_TIMES - like it says
    * FPF_RACE_AUTOMATIC - in the future, if/when new races are added ;)

    Example:
    Let's say, for the sake of example, we want the flags FPF_CAN_GAIN_MULTIPLE_TIMES, FPF_RACE_AUTOMATIC and FPF_MULTI_SELECT_ITEM. The hex number is then 0x1 | 0x4 | 0x100 = 0x105 = 261.

    Making general Multiselect feats (a la Weapon Focus) is not yet supported, I want to generalize it as well since the current method is very shitty (you have to create a feat entry for each and every item)

    The "prereqs:" field is more interesting, this is where the prerequisite codes and prerequisite values for the feat are listed. These come in pairs of <code> <value>, where the codes are either:

    * A stat_X enum (e.g. stat_strength, stat_level_fighter, stat_attack_bonus for minimum BAB)
    * A number between 1000-1999 which represents a feat enum via feat_enum = value-1000 (e.g. if you want
    feat_dodge, the number is 1000 + 24 ; see constants.py inside tpdata\templeplus\lib\templeplus for the list of feat enums)

    * One of the following special codes:
    Code:
        featReqCodeMinCasterLevel = -2,
        featReqCodeTurnUndeadRelated = -3,
        featReqCodeWeaponFeat = -4,
        featReqCodeEvasionRelated = -5,
        featReqCodeFastMovement = -6,
        featReqCodeUncannyDodgeRelated = -7,
        featReqCodeMinArcaneCasterLevel = -8,
        featReqCodeAnimalCompanion = -9,
        featReqCodeCrossbowFeat = -10
    
    The <value> is contextual, e.g. if the feat code is MinCasterLevel (-2) then the value specified is the required minimum caster level. For feats keep it at 1, and for the special codes it's not always relevant, check out the _FeatPrereqsCheck function in feats.cpp.

    Still have to add support for prerequisite definitions that corresponding to new feats, which I imagine you'll want for some of the general Psi feats.


    2. Creating a PythonModifier to handle the actual effect
    This is a topic of its own, though you personally should already be familiar with it.

    To link a Modifier to the feat use the PythonModifier method .MapToFeat("<FEAT NAME>")
    Where <FEAT NAME> must be the same as the feat name in the .txt file.
    e.g.
    Code:
    death_attack_feat = PythonModifier("Death Attack Feat", 3)
    death_attack_feat.MapToFeat("Death Attack")
     
    Last edited: Sep 19, 2016
  4. WinstonShnozwick

    WinstonShnozwick Established Member

    Joined:
    Mar 2, 2011
    Messages:
    628
    Likes Received:
    23
    Allright, I'm just about finished messing around with the new particle effect for mind blade manifestation, so I'll be able to start working on the feats needed for the class. Would you be able to add a flag for FPF_PSIONIC_BONUS ? The group of psi specific feats are available as extra feats on some levels for psionic classes like fighters get fighter specific feats.

    And yeah, I'll probably need to be able to have new feats as prerequisite for other new feats.

    One thing I'm debating, Psionic feats mention that they can only be used by characters who have an existing power point pool. I have two options for this, but I'm not fully sure how to add them in the prerequisites.

    1. Prereq is that character either has taken Wild Talent as a feat, or has levels in a psionic class that gives power points.
    2. Prereq queries the max_psi modifier for a value > 0
     
  5. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    I can add a FPF_PSIONIC, but you should use the class definition files for class-specific bonus feat picks. I'm not sure if that's what you meant, but fighter-like bonus feat selection is handled that way now, the flag isn't really relevant.

    If anything I'm thinking the flags should be used to filter Psi Feats for when Psi isn't enabled in the House Rules menu.

    I think max_psi > 0 is the most general? Also seems fairly simple to do. I can add it to the "special codes" list, or alternatively add it to the stats list (so you'll also be able to access it via obj.stat_level_get(stat_max_psi) ), which should probably be the case for such a basic quantity
     
  6. WinstonShnozwick

    WinstonShnozwick Established Member

    Joined:
    Mar 2, 2011
    Messages:
    628
    Likes Received:
    23
    Yep, that's how it would work, like Psion gets a pick among the psionic feats as a bonus feat every x levels.

    I double checked it and it sounds like max_psi > 0 is the best. I'll probably want to query only the base max in this case? Not sure if there's a difference that would matter here, since temporary boosts to max psi such as through items increasing your key stat to increase psi only affects classes that have psi points to begin with. Whichever is simplest to check it for a prerequisite.
     
  7. WinstonShnozwick

    WinstonShnozwick Established Member

    Joined:
    Mar 2, 2011
    Messages:
    628
    Likes Received:
    23
    For my speed of thought feat, I have the following
    Code:
    from templeplus.pymod import PythonModifier
    from toee import *
    import tpdp
    import char_class_utils
    import d20_action_utils
    
    def SpeedOfThought(attachee, args, evt_obj):
      isFocused = attachee.d20_query("Psionically Focused") # Character must be psionically focused
      if not isFocused:
        return 0
       
      armor = obj.item_worn_at(5)                           # Character must not be wearing heavy armor
        if armor != OBJ_HANDLE_NULL:
            armorFlags = armor.obj_get_int(obj_f_armor_flags)
            if armorFlags == ARMOR_TYPE_HEAVY:
                return 0
               
       
       
      return 0
    
    feat_speed_of_thought = PythonModifier("feat_speed_of_thought", 1)
    feat_speed_of_thought.MapToFeat("feat_speed_of_thought")
    I'm not exactly sure what is needed to make it work. I need to give an insight bonus of 10 feet to the character in the function. http://www.d20srd.org/srd/theBasics.htm#insightBonus
    Could I make it so this would show up in the window if you click on speed to show that the reason of +10 is insight bonus?
     
  8. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    You need to hook it to a ET_OnGetMoveSpeed or ET_OnGetMoveSpeedBase event (depending on what this modifies). Then you just evt_obj.bonus_list.add() as usual.
    Does it matter to you that it actually displays as insight bonus rather than stemming from a feat?

    Also note that if you named your feat "feat_speed_of_thought" in the txt file then that's how it'll be displayed. The name has to match in the MapToFeat arg (though possibly it does convert to lower case and strip the underscores when indexing).

    Another thing, I think the armor check should be with an & operator rather than ==, gotta check though.
     
  9. WinstonShnozwick

    WinstonShnozwick Established Member

    Joined:
    Mar 2, 2011
    Messages:
    628
    Likes Received:
    23
    Actually yeah, it would probably be better to have it display as coming from the feat.

    How about this?
    Code:
    from templeplus.pymod import PythonModifier
    from toee import *
    import tpdp
    import char_class_utils
    import d20_action_utils
    
    def SpeedOfThought(attachee, args, evt_obj):
      isFocused = attachee.d20_query("Psionically Focused") # Character must be psionically focused
      if not isFocused:
        return 0
      
      armor = obj.item_worn_at(5)                           # Character must not be wearing heavy armor
        if armor != OBJ_HANDLE_NULL:
            armorFlags = armor.obj_get_int(obj_f_armor_flags)
            if armorFlags == ARMOR_TYPE_HEAVY:
                return 0
              
      evt_obj.bonus_list.add(10, ?, 114)                    # Add 10 insight bonus to move speed
      return 0
    
    feat_speed_of_thought = PythonModifier("feat_speed_of_thought", 1)
    feat_speed_of_thought.AddHook(ET_OnGetMoveSpeed, EK_NONE, SpeedOfThought, ())
    feat_speed_of_thought.MapToFeat("Speed of Thought")
    -Is it possible to make a new bonus type in ?, because it is an insight bonus which do not stack with others of their kind. Otherwise, which bonus type should I use?
    -Is 114 correct for the bonus construction string? Will it show in game that it came from Speed of Thought this way?
    -What makes sure about this file that speed of thought modifier only applies to characters who have the feat? I don't fully understand how any character with psionic focus and no heavy armor won't just get this bonus now.
     
  10. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Are there any insight bonuses in ToEE? As far as I can tell there are none.

    Here's the list of bonus types I found in ToEE:

    https://github.com/GrognardsFromHell/DllDocumentation/wiki/Bonus-Types

    If nothing from there fits, just pick a new number in the upper range (like 41).

    Also for feats use
    Code:
    evt_obj.bonus_list.add_from_feat(xx, xx, xx, feat)
    Where xx are the same as before and feat is either a feat enum or the feat identifier string.
    And yeah 114 is good (line 114 from bonus.mes which is {114}{~Feat~[TAG_FEATS_DES]}).

    The feat Modifier will be automatically applied to any character who has the feat. You get feats the usual way - either as a class bonus or by selecting it on levelup. The MapToFeat takes care of linking the feat ID (the string hash of "Speed of Thought" in this case) to the Modifier, and the engine handles the rest behind the scenes.

    I assume you've also created a matching .txt file?
     
  11. WinstonShnozwick

    WinstonShnozwick Established Member

    Joined:
    Mar 2, 2011
    Messages:
    628
    Likes Received:
    23
    Neat, I didn't know if I was allowed to use numbers that weren't on there. I guess I will make use of 41 for Insight Bonus, so avoid using it for other purposes please.

    Updated the modifier on git, check it to make sure I got it right. I do have .txt files, I made all of the .txt's for Soulknife stuff last night, you might wanna check those out too. Wild Talent and Speed of Thought only have the new psi flag, the rest that are specific to soulknife have the psi flag | class_automatic together.
     
  12. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Oh wow, you've been busy :)

    Note that you shouldn't use stat_wisdom and other python constants in the prereq fields, just substitute the actual number instead.

    I checked with the help.tab file, apparently True Strike also grants an insight bonus (type 18). Added it to the list, and you may also want to use that. (it won't interfere with True Strike since it's used in a different event)

    Another thing, you should put the PythonModifier files in data\scr\tpModifiers\
    As of 1.0.22 it'll automatically get picked up by the game without the need to edit __init.py. You can grab the latest build frmo GitHub if you want to test it.

    Likewise the class defs belong in data\rules\char_class\
     
  13. WinstonShnozwick

    WinstonShnozwick Established Member

    Joined:
    Mar 2, 2011
    Messages:
    628
    Likes Received:
    23
    I'm planning on doing the class modifiers now. Once I get those done I'll need to figure the UI stuff for it to be usable in game and then I can test.

    Also, on psionic feats, were you able to get a prereq number in so I can check power points, otherwise how can I do that?
     
  14. Sitra Achara

    Sitra Achara Senior Member

    Joined:
    Sep 1, 2003
    Messages:
    3,613
    Likes Received:
    537
    Right, just added stat_psi_points_max = 300 and stat_psi_points_cur = 301 to constants.py, and also to temple_enums.h and d20stats.cpp. They'll be using the d20 queries you made to get the stats.
     
  15. WinstonShnozwick

    WinstonShnozwick Established Member

    Joined:
    Mar 2, 2011
    Messages:
    628
    Likes Received:
    23
    Thank you very much.
    When you say max, which query are you referring to, since there is base max and modified max.
     
Our Host!