Putting the 'role' back in role-playing games since 2002.
Donate to Codex
Good Old Games
  • Welcome to rpgcodex.net, a site dedicated to discussing computer based role-playing games in a free and open fashion. We're less strict than other forums, but please refer to the rules.

    "This message is awaiting moderator approval": All new users must pass through our moderation queue before they will be able to post normally. Until your account has "passed" your posts will only be visible to yourself (and moderators) until they are approved. Give us a week to get around to approving / deleting / ignoring your mundane opinion on crap before hassling us about it. Once you have passed the moderation period (think of it as a test), you will be able to post normally, just like all the other retards.

Making new spells for ToEE - Discussion

Sitra Achara

Arcane
Joined
Sep 1, 2003
Messages
1,860
Codex 2012 Codex 2013 Codex 2014 PC RPG Website of the Year, 2015
Q5 - Some explanations for the following event types would be helpful (ET_OnDispelCheck, ET_OnSpellImmunityCheck, ET_OnImmunityTrigger), what are they used for, and what do I need to code in these hooks manually? Is dispel magic smart enough to cancel a custom condition or do I need to code it in?

OnImmunityTrigger - basically almost every spell, and some of the spell effects, have this hook. (See https://github.com/GrognardsFromHell/DllDocumentation/wiki/Conditions-(List) and https://github.com/GrognardsFromHell/DllDocumentation/wiki/Conditions-List-Pt2-(spell-effects) )
What this does is signal to the game that before applying the effect (Modifier), it should trigger an immunity check. It also returns the type of immunity it entails (spell / racial / special / other), which is used in the big-ass hardcoded immunity handler function (which triggers on a ET_OnSpellImmunityCheck event) that's currently shared across the various protective ward spells like Death Ward, Prot from Alignment, Minor Globe and others.

They all use the same callback function (at address 100ED5A0), which does the following:

Code:
int __cdecl ImmunityTriggerCallback(DispCallbackArgs args)
{
  DispIoType21 *evtObj; // esi@1
  int result; // eax@4

  evtObj = DispIoCheckIoType21(args.dispIO);
  if ( args.subDispNode->condNode != evtObj->condNode || (void *)args.dispKey != args.subDispNode->subDispDef->data1 )
  {
LABEL_11:
    result = 0;
  }
  else
  {
    evtObj->shouldInterrupt = 1;
    evtObj->SDDKey1 = (int)args.subDispNode->subDispDef->data1;
    switch ( args.subDispNode->subDispDef->data1 )
    {
      case Disp_Key_Immunity_Spell:
        evtObj->spellId = CondNodeGetArg(args.subDispNode->condNode, 0);
        result = 10;
        break;
      case DK_Immunity_11:
        result = 11;
        break;
      case DK_Immunity_12:
        result = 12;
        break;
      case DK_Immunity_Courage:
        result = 13;
        break;
      case DK_Immunity_Racial:
        result = 14;
        break;
      case DK_Immunity_15:
        result = 15;
        break;
      case DK_Immunity_Special:
        result = 16;
        break;
      default:
        goto LABEL_11;
    }
  }
  return result;
}

It looks like I haven't exposed the Event Object for Immunity Queries yet, so maybe now would be a good time :) Though perhaps it'd be simpler to add a method ".AddStandardImmunityTrigger" that uses the above standard function since they all use the same one.

Similarly, DispelCheck has a standard Dispel Handler function that's shared by most of the spell effects (only exception is the spell effect for Bestow Curse, which has a special handler that only reacts to Break Enchantment rather than Dispel Magic).

Q6 - is there a list of all possible variables and flags for the data/rules/spells file?

Not that I know of. You can find the Temple+ extensions and a few of the vanilla ones in spell.cpp \ SpellEntryFileParse.

Q7 - is it possible to code a custom saving throw/opposing roll (without using a function like reflex_save_and_damage) where the result/breakdown of the roll appears in the roll information box as a hypertext link instead of directly writing it in with create_history_freeform.
Not in python, though perhaps what you're after already exists and just has to be exposed to it. If you want to delve deeper into it you should check out history.h / .cpp.
 

Sitra Achara

Arcane
Joined
Sep 1, 2003
Messages
1,860
Codex 2012 Codex 2013 Codex 2014 PC RPG Website of the Year, 2015
Also figured out Q8 - but might as well post it here if anyone else if having trouble with it. It's actually in the wiki but kinda hard to find.

In the function you define for the ET_OnGetEffectTooltip hook, you must put evt_obj.append(effect_type_id, spell_enum, text_string)

effect_type_id is the tpdp.hash("ID_string") from the wiki, and text_string has to be defined (can't be left blank), you can use "" since tooltip_base_text: is in the custom indicator .txt file.

You can find all vanilla icons in ToEE1.dat, there's a nice .psd file with the background frame already done (along with all icons), but you have to create an alpha mask manually (black is transparent, white is opaque).

Thanks, added a link in the wiki Indicators page to that part.

Q9 - I'm using the same kind of structure in my AoE as Aura of Despair, meaning...

Spell script creates spell object and adds condition #1 to it -> condition #1 adds condition #2 to those who enter the AoE

In condition #2 I'm using damage() function/method in a function hooked to ET_OnBeginRound, but the first arg of damage() is the damager, now how can I pass the spellcaster object to it when attachee refers to target and tpdp.SpellPacket doesn't have anything that returns the caster? I don't want to use OBJ_HANDLE_NULL because it's ugly in the roll information box.

Also what's the difference between damage() and spell_damage() functions?

Heh, how didn't I add the caster property to SpellPacket? Added for next version.

As mentioned spell_damage will regard usage of metamagic Maximize and Empower. It also won't register in the logbook under melee damage. I think those are the only two difference (other than some internal code to do with awarding XP which leads me to suspect it might bug out sometimes...)
 

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
I'm making Evard's Black Tentacles with the idea I saw here about using reskinned entangle (although I might try something in maya later), but ran into an unforeseen problem. I can't make it function exactly like P&P because AI obviously isn't programmed to use a radial entry for "Break Free", so I guess I'll have to allow a grapple save every round.

Now while playing with ET_OnBuildRadialMenuEntry I noticed that tpdp.RadialMenuStandardNode. values are offset. Eg. radial_action.add_as_child(attachee, tpdp.RadialMenuStandardNode.Alchemy) actually places the entry under "movement" in game. Bug or purpose?
 

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
Actually nevermind about that bug, I think I used add_as_child instead of add_child_to_standard.
 

Sitra Achara

Arcane
Joined
Sep 1, 2003
Messages
1,860
Codex 2012 Codex 2013 Codex 2014 PC RPG Website of the Year, 2015
I can't make it function exactly like P&P because AI obviously isn't programmed to use a radial entry for "Break Free", so I guess I'll have to allow a grapple save every round.
In Temple+ AI will do a breakfree attempt if it's possible and it has nothing else to do.
 

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
What are the prerequisites for that, coding wise?

Which queries have to return_val 1 for the AI to attempt to break free? It's not EK_Q_Is_BreakFree_Possible because I tested. Or does the AI check for specific conditions? Etc...

Another thing I ran into that made me confused... this is from prayer_beads.py.

def PrBeadsRadial(attachee, args, evt_obj):
invIdx = args.get_arg(2)
radialAction = tpdp.RadialMenuEntryAction("Prayer Beads (Karma)", D20A_ACTIVATE_DEVICE_FREE, invIdx, "TAG_INTERFACE_HELP")
radialAction.add_child_to_standard(attachee, tpdp.RadialMenuStandardNode.Items)
return 0

prbd.AddHook(ET_OnD20ActionPerform, EK_D20A_ACTIVATE_DEVICE_FREE, PrBeadsPerform, ())

If I try the same setup with D20A_BREAK_FREE / EK_D20A_BREAK_FREE (replacing invIdx with spell.id and radial menu with .movement), the callback function PrBeadsPerform doesn't fire (print doesn't show up on console). I can get it to work with ET_OnD20Signal / EK_S_BreakFree on the hook but I'm curious.
 
Last edited:

Sitra Achara

Arcane
Joined
Sep 1, 2003
Messages
1,860
Codex 2012 Codex 2013 Codex 2014 PC RPG Website of the Year, 2015
What are the prerequisites for that, coding wise?

Which queries have to return_val 1 for the AI to attempt to break free? It's not EK_Q_Is_BreakFree_Possible because I tested. Or does the AI check for specific conditions? Etc...
It is EK_Q_Is_BreakFree_Possible. The AI does a query for that if the other options are exhausted, and if positive and it has a remaining standard action, it will attempt a Break Free.
Just to be sure, when adding the callback, are you modifying the object event .return_val property? (as opposed to the function's return value)

If I try the same setup with D20A_BREAK_FREE / EK_D20A_BREAK_FREE (replacing invIdx with spell.id and radial menu with .movement), the callback function PrBeadsPerform doesn't fire (print doesn't show up on console). I can get it to work with ET_OnD20Signal / EK_S_BreakFree on the hook but I'm curious.
Not sure what you mean by .movement.

Can I suggest you create a github repository with your code? It'll be easier to go over it and make pull requests than commenting on paraphrased bits of code.
You can also post code snippets on gist.github.com.
 

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
It is EK_Q_Is_BreakFree_Possible. The AI does a query for that if the other options are exhausted, and if positive and it has a remaining standard action, it will attempt a Break Free.
Just to be sure, when adding the callback, are you modifying the object event .return_val property? (as opposed to the function's return value)

Yes I was modifying evt_obj.return_val instead of the normal return. Anyway I did some further testing with a simplified new spell and figured out how it works.

The evt_obj.return_val seems to be totally meaningless (source of my confusion).

By itself, the callback for EK_Q_Is_BreakFree_Possible doesn't fire.

If the critter has something debilitating like EK_Q_Critter_Is_Grappling returning 1, EK_Q_Is_BreakFree_Possible fires it's attached callback (where you attach any possible save/condition removal etc).

Radial stuff is only used for the player.

At least I think this is the proper way to do it.

Not sure what you mean by .movement.

By .movement I was referring to tpdp.RadialMenuStandardNode, anyway doesn't matter anymore, I'm reasonably sure I can get it to work.
 
Last edited:

Sitra Achara

Arcane
Joined
Sep 1, 2003
Messages
1,860
Codex 2012 Codex 2013 Codex 2014 PC RPG Website of the Year, 2015
Yes I was modifying evt_obj.return_val instead of the normal return. Anyway I did some further testing with a simplified new spell and figured out how it works.

The evt_obj.return_val seems to be totally meaningless (source of my confusion).

By itself, the callback for EK_Q_Is_BreakFree_Possible doesn't fire.

If the critter has something debilitating like EK_Q_Critter_Is_Grappling returning 1, EK_Q_Is_BreakFree_Possible fires it's attached callback (where you attach any possible save/condition removal etc).

Radial stuff is only used for the player.

At least I think this is the proper way to do it.

No, that shouldn't be so. I can't comment on the tests you've done but it definitely does a BreakFree query in the engine for anything Break Free related.
The only thing I can think of without looking at your code is that perhaps you're getting a python error somewhere in the code - that can break the script. Have you checked the logfile for python errors?
 

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
Well here's the code, evt_obj.return_val for EK_Q_Is_BreakFree_Possible doesn't change anything in any scenario (even if it returns 0, the AI breaks the grapple from EK_Q_Critter_Is_Grappling). It doesn't have any syntax/console errors. Couldn't see anything in the logfile that seems related.
 
Last edited:

Sitra Achara

Arcane
Joined
Sep 1, 2003
Messages
1,860
Codex 2012 Codex 2013 Codex 2014 PC RPG Website of the Year, 2015
Well here's the code, evt_obj.return_val for EK_Q_Is_BreakFree_Possible doesn't change anything in any scenario (even if it returns 0, the AI breaks the grapple from EK_Q_Critter_Is_Grappling). It doesn't have any syntax/console errors. Couldn't see anything in the logfile that seems related.

Ok I tested your code, it does seem to run correctly, except that it doesn't react to a d20_query for breakfree unless you add the return_val = 1 to the callback.
I've added comments in the linked gist.
 

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
Is there a way to prevent a specific D20A_ action (like D20A_DOUBLE_MOVE) from taking place before it happens via condition hooks?
 

Sitra Achara

Arcane
Joined
Sep 1, 2003
Messages
1,860
Codex 2012 Codex 2013 Codex 2014 PC RPG Website of the Year, 2015
Is there a way to prevent a specific D20A_ action (like D20A_DOUBLE_MOVE) from taking place before it happens via condition hooks?
I don't think you can do so directly, but you can make a character start its turn with just a Standard Action rather than a Full Round Action available (i.e. hourglass half drained). Is that what you're after?

You can also alter the move speed.
 

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
What I actually need is to prevent someone from attacking. Also I can't restrict the player to a move action only with tb_status.hourglass_state because break free is a standard action and moving while grappled is a double move action (in ToEE to make sure movement depletes standard action).

I guess I can go around this by giving a 100% miss chance if it's not even possible.
 
Last edited:

Sitra Achara

Arcane
Joined
Sep 1, 2003
Messages
1,860
Codex 2012 Codex 2013 Codex 2014 PC RPG Website of the Year, 2015
What I actually need is to prevent someone from attacking. Also I can't restrict the player to a move action only with tb_status.hourglass_state because break free is a standard action and moving while grappled is a double move action (in ToEE to make sure movement depletes standard action).

I guess I can go around this by giving a 100% miss chance if it's not even possible.

Ah, for that you can add a query hook that's used in most action checks - Q_IsActionInvalid_CheckAction.

However I'll need to add a method to cast the evt_obj.data1 field to a D20Action (luckily easy to do, in for next version - d20a = evt_obj.get_d20_action() ) so you can then check its action type.
(Note that most D20Queries do not have a D20Action stored in that property, this is specific to this query and a few others).
 

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
Got it to work, I actually tried that before and bunch of other stuff, but I used an IF check for the data1 field comparing it to the standard attack constant number.

Does that query returning 1 disable any other actions besides attack?
 

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
I'm trying to to determine if an incoming attack is a ranged (not melee) touch attack in ET_OnGetDefenderConcealmentMissChance. Ranged touch attacks are not included in attack_packet.get_flags() == D20CAF_RANGED.

Any idea?
 

Sitra Achara

Arcane
Joined
Sep 1, 2003
Messages
1,860
Codex 2012 Codex 2013 Codex 2014 PC RPG Website of the Year, 2015
Got it to work, I actually tried that before and bunch of other stuff, but I used an IF check for the data1 field comparing it to the standard attack constant number.

Does that query returning 1 disable any other actions besides attack?
It should also disable spellcasting, ranged attacks, touch attacks, AoOs, Whirlwind Attack, Charge Attack, Coup De Grace, Trip Attack, Disarm Attack, Throw, and maybe a few others I'm missing.
Shouldn't block move actions, and the various Paladin / Monk etc abilities dispatch an event for action check.

I'm trying to to determine if an incoming attack is a ranged (not melee) touch attack in ET_OnGetDefenderConcealmentMissChance. Ranged touch attacks are not included in attack_packet.get_flags() == D20CAF_RANGED.

Any idea?

Don't use ==, instead use the binary operator &
Code:
( attack_packet.get_flags() & D20CAF_RANGED ) != 0
 

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
How does ET_OnActionCostMod hook work? Basically how do I modify the cost of any action?
 

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
Some questions.

Q1 - what's the difference between particles_end() and particles_kill()?

Q2 - how does d20_send_signal_ex() differ from the normal signal?

Q3 - what's the difference between d20_query(Q_Critter_Is_AIControlled) == 1 vs. obj_get_int(obj_f_type) == obj_t_npc? Isn't every AI controlled creature an NPC?

Q4 - how to build an entry in dismiss spells menu for the caster only? I obviously can't hook it to the AoE object condition or the hit condition caused by the AoE.

Q5 - why does ET_OnD20ActionCheck / EK_D20A_BARBARIAN_RAGE fire but EK_D20A_CAST_SPELL doesn't? Both actions have "Action Check" according to this list at the bottom (unless the list uses different numbering from constants.py?). I'd just like to understand without testing every d20 action which ones work with those ET_OnD20Action event types (since queries and signals can't access EventObjD20Action).

Q6 - it's still not possible to pass MetaMagicData to ET_OnConditionAdd, right? It's missing from SpellPacket.

Q7 - is it possible to check which class casted the spell? I'm having trouble figuring out if the spell is casted by wizard sorc or bard, and something like wiz 1/bard x is completely possible so I can't just check if the caster has class x. On that note, what is .spell_class in SpellPacket? Which numbers do they point to?

Q8 - how to check if spell was casted from scroll?
 
Last edited:

cowking

Scholar
Joined
Oct 18, 2016
Messages
115
Well I realized I can just add another condition to the caster to build the dismiss radial, derp. That's Q4 off the list.
 

Sitra Achara

Arcane
Joined
Sep 1, 2003
Messages
1,860
Codex 2012 Codex 2013 Codex 2014 PC RPG Website of the Year, 2015
Some questions.

Q1 - what's the difference between particles_end() and particles_kill()?
"Kill" should completely remove the entire particle system from memory. "End" causes emitters to stop emitting new particles. I think it's less abrupt to use "End".

Q2 - how does d20_send_signal_ex() differ from the normal signal?

d20_send_signal_ex(S_xxx, target_obj) internally generates a D20Action, and sets the D20Action's target to be the second arg (should be a PyObjHandle object).
The internally generated D20Action is stored then in arg1.
It's intended to be used with touch attacks apparently.


Q3 - what's the difference between d20_query(Q_Critter_Is_AIControlled) == 1 vs. obj_get_int(obj_f_type) == obj_t_npc? Isn't every AI controlled creature an NPC?

AIControlled also applies when you have a PC charmed by NPCs (e.g. through Charm Person or Suggestion).

Q4 - how to build an entry in dismiss spells menu for the caster only? I obviously can't hook it to the AoE object condition or the hit condition caused by the AoE.

IIRC you need to apply the "Dismiss" condition, with the first arg being the spell ID.
See https://github.com/GrognardsFromHell/DllDocumentation/wiki/Conditions-(List)#dismiss

Q5 - why does ET_OnD20ActionCheck / EK_D20A_BARBARIAN_RAGE fire but EK_D20A_CAST_SPELL doesn't? Both actions have "Action Check" according to this list at the bottom (unless the list uses different numbering from constants.py?). I'd just like to understand without testing every d20 action which ones work with those ET_OnD20Action event types (since queries and signals can't access EventObjD20Action).

There's a difference between the action check callback (which is defined on a per-action-type basis), and a dispatch call made through the event system which may or may not take place.
To illustrate, this is what happens for Barbarian Rage (D20A_BARBARIAN_RAGE):

d20ActionDef->actionCheckFunc() -> internally does a dispatch for ET_OnActionCheck with EK_D20A_BARBARIAN_RAGE -> invokes the EK_D20A_BARBARIAN_RAGE hooks ( which exists for critter with the Barbarian_Rage condition - see https://github.com/GrognardsFromHell/DllDocumentation/wiki/Conditions-(List)#barbarian_rage ).

Whereas for D20A_CAST_SPELL, there's a actionCheckFunc that DOES NOT perform a dispatch for ET_OnActionCheck.
You can see it here:
https://github.com/GrognardsFromHel...24948c6b130777adbe05/TemplePlus/d20.cpp#L2781

Unfortunately I don't think there's a way for you to know this for every action. As a rule of thumb though, class-specific abilities such as Barbarian Rage, Smite Evil etc. ususally go through the dispatcher system, whereas "generic" actions do not.

Q6 - it's still not possible to pass MetaMagicData to ET_OnConditionAdd, right? It's missing from SpellPacket.

You're right. Added it for next version (added .get_metamagic_data() method to SpellPacket).
That said, what do you need it for? Widen Area? That sort of thing is usually handled engine-side, and as you noted it's probably not implemented due to the particle effects. If that's the intended use then it's better to leave it to me to enforce it through the "picker" code.

Q7 - is it possible to check which class casted the spell? I'm having trouble figuring out if the spell is casted by wizard sorc or bard, and something like wiz 1/bard x is completely possible so I can't just check if the caster has class x. On that note, what is .spell_class in SpellPacket? Which numbers do they point to?

Q8 - how to check if spell was casted from scroll?
I've added some doc for SpellPacket that should answer those two:

https://github.com/GrognardsFromHell/TemplePlus/wiki/Python-builtin-tpdp-module#spellpacket
 

qed

Novice
Joined
May 13, 2017
Messages
5
How can I add my spell into spell list? I've made scripts, added string to spell.mes, created .txt file with spell rules but nothing seems to work. Btw, I've noticed that in spell.mes 1 spell takes 2 entries, something like {xxx}, {5xxx}. What to do if my spell number is a 4 digit number? {5xxxx} dosn't look right to me. Also I've tried to change spell number to 3 digit numbers, but it doesn't work also.
 

Sitra Achara

Arcane
Joined
Sep 1, 2003
Messages
1,860
Codex 2012 Codex 2013 Codex 2014 PC RPG Website of the Year, 2015
How can I add my spell into spell list? I've made scripts, added string to spell.mes, created .txt file with spell rules but nothing seems to work.

Can you post the files? You can use gist.github.com


Btw, I've noticed that in spell.mes 1 spell takes 2 entries, something like {xxx}, {5xxx}. What to do if my spell number is a 4 digit number? {5xxxx} dosn't look right to me. Also I've tried to change spell number to 3 digit numbers, but it doesn't work also.
Add 5000 to the number (e.g. for xxxx=2097 it'll be 7097).
 

As an Amazon Associate, rpgcodex.net earns from qualifying purchases.
Back
Top Bottom