About movement AOOs, movement is a special case. I think you need a hook for EK_QUE_AOOIncurs. See here: https://github.com/GrognardsFromHell/TemplePlus/blob/master/TemplePlus/d20.cpp#L991 Target list: that's right, currently not possible. I could add such a method, but I'm not 100% that's the best way to handle this. Will have to get back to you on that (remind me on the weekend).
Thanks Sitra, AOOIncurs did it , about the target list, sure About Masters Touch, this is more complex, as I have a working code but I think this is not the way to do it as I did not find a way to parse certain values and added them as lists. There must be a better way: Spoiler: Code Code: def OnSpellEffect(spell): print "Masters Touch OnSpellEffect" spell.duration = 10 * spell.caster_level # 1 Min/cl spellTarget = spell.target_list[0] spellEnum = (str(spell)[str(spell).index('(')+len('('):str(spell).index(')')]) mastersTouchPossiblyTargetSlots = [] mastersTouchPossiblyTargetSlots.append(spell.caster.item_worn_at(3)) #mainhand mastersTouchPossiblyTargetSlots.append(spell.caster.item_worn_at(4)) #offhand mastersTouchPossiblyTargetSlots.append(spell.caster.item_worn_at(11)) #shield casterHasProficiency = False casterBaseProficiencies = [] weaponProficiencyBard = [wt_sap, wt_short_sword, wt_longsword, wt_rapier, wt_shortbow, wt_composite_shortbow, wt_whip] weaponProficiencyDruid = [wt_dagger, wt_sickle, wt_club, wt_shortspear, wt_quarterstaff, wt_spear, wt_dart, wt_sling, wt_scimitar, wt_longspear] weaponProficiencyMonk = [wt_dagger, wt_club, wt_quarterstaff, wt_light_crossbow, wt_sling, wt_heavy_crossbow, wt_javelin, wt_handaxe, wt_kama, wt_nunchaku, wt_siangham, wt_shuriken] weaponProficiencyRogue = [wt_hand_crossbow, wt_rapier, wt_short_sword, wt_sap, wt_shortbow, wt_composite_shortbow] weaponProficiencyWizard = [wt_dagger, wt_club, wt_quarterstaff, wt_light_crossbow, wt_heavy_crossbow] weaponProficiencyElf = [wt_longsword, wt_rapier, wt_shortbow, wt_composite_shortbow, wt_longbow, wt_composite_longbow] if spell.caster.has_feat(feat_simple_weapon_proficiency_bard): casterBaseProficiencies.extend(weaponProficiencyBard) hasFullSimpleProficiency = True if spell.caster.has_feat(feat_simple_weapon_proficiency_druid): casterBaseProficiencies.extend(weaponProficiencyDruid) if spell.caster.has_feat(feat_simple_weapon_proficiency_monk): casterBaseProficiencies.extend(weaponProficiencyMonk) if spell.caster.has_feat(feat_simple_weapon_proficiency_rogue): casterBaseProficiencies.extend(weaponProficiencyRogue) hasFullSimpleProficiency = True if spell.caster.has_feat(feat_simple_weapon_proficiency_wizard): casterBaseProficiencies.extend(weaponProficiencyWizard) if spell.caster.has_feat(feat_simple_weapon_proficiency_elf): casterBaseProficiencies.extend(weaponProficiencyElf) if spellTarget.obj == OBJ_HANDLE_NULL: spell.caster.float_text_line("No item equipped", tf_red) game.particles( 'Fizzle', spell.caster ) spell.target_list.remove_target( spellTarget.obj) else: try: #Masters Gift only works on equipped items in main or offhand itemSlot = mastersTouchPossiblyTargetSlots.index(spellTarget.obj) except ValueError: spell.caster.float_text_line("Item must be an equipped weapon or shield") game.particles('Fizzle', spell.caster) spell.target_list.remove_target(spellTarget.obj) else: if itemSlot == 0: wornItemType = spell.caster.item_worn_at(3).obj_get_int(197) #Get weapon type mainhand elif itemSlot == 1: wornItemType = spell.caster.item_worn_at(4).obj_get_int(197) #Get weapon type offhand else: wornItemType = 0 #check for Proficiency if wornItemType == 0: if spell.caster.has_feat(278): #check if caster is already proficient with shields; Tower Shields don't seem to differenciate??? casterHasProficiency = True else: featNeeded = game.get_feat_for_weapon_type(wornItemType) if spell.caster.has_feat(featNeeded): casterHasProficiency = True if spell.caster.has_feat(feat_martial_weapon_proficiency_all): if featNeeded in range(228, 259): #range inlcudes lower and excludes upper value casterHasProficiency = True if featNeeded == 281: #every simple weapon returns 281 when querying game.get_feat_for_weapon_type(wornItemType) if hasFullSimpleProficiency: casterHasProficiency = True if wornItemType in casterBaseProficiencies: casterHasProficiency = True if not casterHasProficiency: spell.caster.condition_add_with_args('sp-Masters Touch', spell.id, spell.duration, int(spellEnum), wornItemType) spellTarget.partsys_id = game.particles('sp-Detect Magic 2 Med', spell.caster) else: spell.caster.float_text_line("Already proficient") game.particles('Fizzle', spell.caster) spell.target_list.remove_target(spellTarget.obj) spell.spell_end( spell.id) Here is my problem: game.get_feat_for_weapon_type(wornItemType) returns only (e.g. feat_martial_weapon_proficiency_battleaxe) but not any class based feats that also grants the proficiency for it. For example Longsword has not only feat_martial_weapon_proficiency_longsword (which is returned) but also martial_all, simple_bard and simple_elf which all are not returned. I assume that all of these were not part of the original game (more of this later). I am aware of the weapon.cpp (https://github.com/GrognardsFromHell/TemplePlus/blob/master/TemplePlus/weapon.cpp) from which I took the data for my lists in the code, but it would be much better if I actually could access the data. It's getting even worse with simple weapons as game.get_feat_for_weapon_type(wornItemType) always returns 281 (simple weapon prof) for them. All those return values are taken from rules\feat_enum.mes (at least I think so) and obviously the game originally granted all classes simple weapon prof, so there was no need to return something else. Long story short, the information I would like to have is in weapon.cpp, but how do I access it? As mentioned, at the moment I simply added the lists to the spell, but this feels bad.
How do I unset Code: attachee.ai_flee_add(tpdp.SpellPacket(spell_id).caster) I did take a look at https://github.com/GrognardsFromHell/TemplePlus/blob/master/TemplePlus/python/python_object.cpp and https://github.com/GrognardsFromHell/TemplePlus/blob/master/TemplePlus/ai.cpp but could not find an answer. Code: attachee.obj_get_int(obj_f_critter_fleeing_from) still returns a 0 after I do set ai_flee_add but the critter runs away. When I tried to do simply Code: attachee.critter_flag_set(OCF_FLEEING) the mob did not act as he was in flee mode but did not move, I guess because I would have to set obj_f_critter_fleeing_from but the arg only takes an integer, so I cannot simply pass the tpdp.SpellPacket(spell_id).caster arg.
I don't understand why you go through all this work to determine whether the caster is already proficient. Just apply the condition - worst case, it's a pointless cast, which will have already been wasted regardless of whether it fizzles or not. Also I don't think it's too much to expect from the player to use the spell judiciously. Nevertheless for future reference, if you ever need to check whether a character is proficient with a certain weapon, this is handled here in the C++ side: https://github.com/GrognardsFromHell/TemplePlus/blob/master/TemplePlus/feat.cpp#L1461 https://github.com/GrognardsFromHell/TemplePlus/blob/master/TemplePlus/feat.cpp#L1559 it is easy to expose it to python, and is much more preferable than duplicating all the code in python. Some further general notes: 1. Please do not use the explicit values of enumerations - such as 197 instead of obj_f_weapon_type, or the shield prof. feat. If you've seen it used elsewhere this way - that is also bad practice. 2. spellEnum - again, there is no reason to store it in the condition args. This is a static quantity known in advance. 3. Also, parsing the spell enum from the spell string representation is very hacky. If you really have need it for whatever reason, it's easy to add a "spell_enum" property to the PySpell to directly obtain it.
BTW. It's kinda hard to keep track of outstanding issues in this format. Could you make a public google doc where I can reply inline and cross off addressed issues?
I don't want to apply the condition I just want to negate the penalty because I want to avoid that the game belives you are proficient with the weapon when you are not. Else you could simply cast the spell before leveling up and get a weapon focus for something you actually would not qualify for. At least this is what I am afraid of. I fully agree, I would prefer not to have duplicates, this is really bad. 1. Will fix it. 2. + 3. I fully agree that it's hacky. I am even unsure if I need it, I just added it due to the sanctuary thing. If these codelines are not needed in every spell: Code: def warCrySpellHasSpellActive(attachee, args, evt_obj): if evt_obj.data1 == args.get_arg(2): evt_obj.return_val = 1 return 0 warCrySpell.AddHook(ET_OnD20Query, EK_Q_Critter_Has_Spell_Active, warCrySpellHasSpellActive, ()) I would simply drop it altogether. I just don't know if they are needed. I took dougs spell Moment of Presciency as a template (which I hope does not sound negative at all, I am pretty thankful I had a good starting point!). I am aware that you don't like passing the spell enum as it's a static property, if you really dislike it, I can change that but I personally like to gather all information that I know I need in the beginning. But as I said if you really dislike it, I can change it, no problem. Sure I can do a google docs document, no problem
Oh, I see now. But in this case there's a simpler solution - make the condition apply a bonus capper to bonus type 37 (which is what the non-proficiency penalty is). You'll want this to occur on the OnGetToHitBonus2 event. Although, perhaps that is not accurate either. For example, what if a character is wearing bracers of archery? In that case, shouldn't the bracers apply the bonus that proficient characters get? To address that, perhaps it's better to modify the proficiency check with a flag indicating permanent / non-permanent status effects. I've done sthg similar for stat boosters like Fox Cunning vs. Headband of Intellect. Unfortunately it's a bit hairier in this case so I'm not sure that's the best solution. Also, I think I've seen that it is ok to get the weapon focus feat - ideally the game should check if you have the proficiency, and enable/disable its effects accordingly. All those things considered - I think it's best to simply grant the proficiency. Exploiters gonna exploit, enjoy it while you can There's a simple solution actually. You can retrieve the SpellPacket inside the HasSpellActive() callback, and then get its spell_enum, like so: Code: spell_id = args.get_arg(0) spell_packet = tpdp.SpellPacket(spell_id) spell_enum = spell_packet.spell_enum Cool
P.S. here's the latest decompiled C code. I think this will be a handy reference for things not covered in the Temple+ code base (e.g. check out GlobalWeaponProficiencyToHitPenalty). https://fil.email/LwNXiYVK
Sitra, could you explain how I have to handle spells that require concentration? In this link https://github.com/GrognardsFromHell/TemplePlus/wiki/Spell-Expiry#stop-concentration there is a sp-Concentrating mentioned. Do I have to add it? I assume that I have to add a hook to the normal spell condition that reacts to signal sent.
Yes, you have to add sp-Concentrating when the condition is added, using the ET_OnAdd event. It should have the same spell id in its args naturally. Your own condition should have a hook for S_Concentration_Broken.
Thanks, works like a charm with the exception that the sp-concentration does not expire at the end of the spells duration. Code: spellPacket.caster.condition_add_with_args('sp-Concentrating', args.get_arg(0), args.get_arg(1)) seems to ignore the second argument (which would be the duration). No big thing I guess, I just noticed it, when I mouseover over the caster and can still see the concentration tooltip when the spell expired naturally.