Aller au contenu

Photo

Very Basic Question


  • Veuillez vous connecter pour répondre
16 réponses à ce sujet

#1
trelephant

trelephant
  • Members
  • 29 messages
My goal here is to create an object that calls a script upon quick use.  You know, select the left option in the wheel.  So according to my understanding of events and the toolkit, I need to create an item with the Properties:

Module: Core Game Resources
Owner Module: <mine>

If I understand things correctly, this would mean that "using" the item would trigger the MODULES's script with the event EVENT_TYPE_SPELLSCRIPT_CAST.  Assuming I have no other resources in the module that would trigger events, it would make sense to code a reaction whenever that event is given.

Correct me if I'm wrong here.

But I can't figure out how to design an item that has a "use" option in the wheel.  I've got the item into the inventory no problem.  I just can't get the damn thing to trigger the rest of the script.

#2
Talian Kross

Talian Kross
  • Members
  • 239 messages

trelephant wrote...

But I can't figure out how to design an item that has a "use" option in the wheel.

Admittedly, I didn't try too hard to figure it out, so maybe there's a way, but I tried doing that with a ring and could get it to work.

I just changed it to a tome (something I knew already had built-in "use" support) and said the heck with it.

So, as a quick recap:

1.) Duplicate some tome (e.g., _Global/Quick Items/Unique/gen_im_qck_book_skill);
2.) Change first property ("Activated Ability") from ITEM_SKILL_POINT to ITEM_ABILITY_UNIQUE_POWER_UNLIMITED_USE;
3.) Save, export, and update Module/Manifest XML's;
4.) Now, in your module's override script, add the following to your event handler:

case EVENT_TYPE_UNIQUE_POWER:
{
    object oItem      = GetEventObject( ev, 0 );    //  item activated (tome object)
    object oActivator = GetEventObject( ev, 1 );    //  player or NPC activating item
    
    //  do whatever
}
break;

Edit:  BTW, at this point, you can go in-game and then drag the item to your quick-launch bar where just clicking it activates it (no more kludgy command wheel). Once it's there, save the game, exit, go back to the toolset and change the "Plot Item" attribute (in its properties) to true to prevent it from taking up inventory space.  (save, export, update Module/Manifest XML's)  Once it's in your plot item tab, you can't drag it to your quick-launch bar anymore, so don't remove it from there.

This is just a general tip.  I realize it may not be applicable to whatever it is you are trying to accomplish.

Modifié par Talian Kross, 26 novembre 2009 - 11:13 .


#3
AND04

AND04
  • Members
  • 154 messages

Talian Kross wrote...

trelephant wrote...

But I can't figure out how to design an item that has a "use" option in the wheel.

Admittedly, I didn't try too hard to figure it out, so maybe there's a way, but I tried doing that with a ring and could get it to work.

I just changed it to a tome (something I knew already had built-in "use" support) and said the heck with it.

So, as a quick recap:

1.) Duplicate some tome (e.g., _Global/Quick Items/Unique/gen_im_qck_book_skill);
2.) Change first property ("Activated Ability") from ITEM_SKILL_POINT to ITEM_ABILITY_UNIQUE_POWER_UNLIMITED_USE;
3.) Save, export, and update Module/Manifest XML's;
4.) Now, in your module's override script, add the following to your event handler:

case EVENT_TYPE_UNIQUE_POWER:
{
    object oItem      = GetEventObject( ev, 0 );    //  item activated (tome object)
    object oActivator = GetEventObject( ev, 1 );    //  player or NPC activating item
    
    //  do whatever
}
break;



that only works if the game isn'T using that event - aka you would override any other Unique Powers (like the Litanny on top of the mage-tower)

what you have to do is the following:

* create a new ABI_BASE M2DA (copy any Item-ability) and change the source + any other params like you wish
* create a script (the same that u used in the M2DA under spellscript) and handle the Cast-Event (copy it from an existing one and delete all unused Event-Switch'es)

thats just the basic breakdwon without going into detail - i can upload a sample later as well if needed.

#4
Talian Kross

Talian Kross
  • Members
  • 239 messages

AND04 wrote...

that only works if the game isn'T using that event - aka you would override any other Unique Powers (like the Litanny on top of the mage-tower)


If he were making more than one item and attaching it to his module, I presume he would be clever enough to do a check.

if( GetTag( oItem ) == "my_custom_item1" )
{
}
if( GetTag( oItem ) == "my_custom_item2" )
{
}
//etc....

Edit:

{off experimenting with an old savegame}

Alright, well, in my mod, I only have one UNIQUE_POWER item, but I do block it off like I show above.  If I take away the GetTag() check, clicking on the LItany scroll does activate my tome as well.

With the check, though, everything behaves as it should.

Interesting.

Modifié par Talian Kross, 26 novembre 2009 - 11:51 .


#5
AND04

AND04
  • Members
  • 154 messages

Talian Kross wrote...

AND04 wrote...

that only works if the game isn'T using that event - aka you would override any other Unique Powers (like the Litanny on top of the mage-tower)


If he were making more than one item and attaching it to his module, I presume he would be clever enough to do a check.

if( GetTag( oItem ) == "my_custom_item1" )
{
}
if( GetTag( oItem ) == "my_custom_item2" )
{
}
//etc....

Edit:

{off experimenting with an old savegame}

Alright, well, in my mod, I only have one UNIQUE_POWER item, but I do block it off like I show above.  If I take away the GetTag() check, clicking on the LItany scroll does activate my tome as well.

With the check, though, everything behaves as it should.

Interesting.




well my items that i set to Unique power invoked the same effect as the litany on top of the mage-tower - that said if you correctly override or catch the event you could do so - but well imo thats not what this Ability is indented to be used for - well if its a unique object maybe - but most defnitly not for anything that can be in the game in multiples (like any potion or suich)

Modifié par AND04, 26 novembre 2009 - 11:55 .


#6
Talian Kross

Talian Kross
  • Members
  • 239 messages

AND04 wrote...

- but well imo thats not what this Ability is indented to be used for

I disagree based on historical precedent.  That exactly how it was used in NWN's engine--and NWN2, but Obsidian purposely didn't change much.

That said, though, you are never very verbose or detailed in your explanations of how you do things (here and on the Wiki) in regard to events, but if you are claiming we have the ability to create our own unique events (i.e., not just override the ones already handled by the game), then sure, that's the best option.  (Edit: Are you claiming that?)

Edit 2: And remember, these events aren't hooked (or daisy-chained).  In this specific case, you should not be passing this event along via HandleEvent().  That probably explains why my code works as intended and your code ended up activating the Litany tome in the Mage's tower.

Modifié par Talian Kross, 26 novembre 2009 - 12:55 .


#7
Nodrak

Nodrak
  • Members
  • 144 messages
Every item has a property 'Activated Ability', which is essentially the same function as casting a spell or using a talent. The ITEM_ABILITY_UNIQUE_POWER_UNLIMITED_USE is defined in ABI_base.xls. One can add an ability to this list, or use any of the abilities from an item. You can (in theory) make a sword shoot a fireball when 'used'. Or make an item run any script you want directly via its name referenced in the ABI_ M2DA (which is the ABI_base.xls combined with any ABI_ M2DAs you custom make).

In this, it should be relativley easy to make an item do almost anything.

In item_unique_power.nss after some event routing:
case EVENT_TYPE_SPELLSCRIPT_IMPACT:
{
//--------------------------------------------------------------
// Get a structure with the event parameters
//--------------------------------------------------------------
struct EventSpellScriptImpactStruct stEvent = Events_GetEventSpellScriptImpactParameters(ev);


Log_Trace(LOG_CHANNEL_COMBAT_ABILITY, GetCurrentScriptName() + ".EVENT_TYPE_SPELLSCRIPT_IMPACT",Log_GetAbilityNameById(stEvent.nAbility));


event ev = Event(EVENT_TYPE_UNIQUE_POWER);
ev = SetEventInteger(ev,0, stEvent.nAbility);
ev = SetEventObject(ev,0, stEvent.oItem);
ev = SetEventObject(ev,1, stEvent.oCaster);
ev = SetEventObject(ev,2, stEvent.oTarget);

[b]DelayEvent(0.0, GetModule(), ev);[/b]


break;

This is why you can hook into the unique item event via the module script.  Interestingly, module_core does not hook EVENT_TYPE_UNIQUE_POWER...  I am led to assume that module_core is not the same module script the OC uses, much like nwn2 and the default# scripts.  There is also nothing 'wrong' with calling the same even with different items, and disambiguating the events in script like propsed above, and is rather similar to routing multiple abilities (ability events) into the same script, like how the spells are done.  All spells are cast from the same person, so you cannot use the same event and disambiguate in this case (items have one ability, and one tag, players have one tag and tons of abilities).

For example, health pots.  They have 4 different ability events, one for each tier.  They all reference the same script.  If you instead, had one event for all four, and routed based on tag or resref, they would share a cooldown.

Modifié par Nodrak, 26 novembre 2009 - 02:45 .


#8
AND04

AND04
  • Members
  • 154 messages

Talian Kross wrote...

AND04 wrote...

- but well imo thats not what this Ability is indented to be used for

I disagree based on historical precedent.  That exactly how it was used in NWN's engine--and NWN2, but Obsidian purposely didn't change much.

That said, though, you are never very verbose or detailed in your explanations of how you do things (here and on the Wiki) in regard to events, but if you are claiming we have the ability to create our own unique events (i.e., not just override the ones already handled by the game), then sure, that's the best option.  (Edit: Are you claiming that?)


well as Nodrak wrote allready we can create any ability without having to override that event as every ability can have its own spellscript attached - + you can define cost/animation colldown and so on there.
I didn't mean that we shouldn't use the same script for different abilitys - as i am doing that as well - though grouped by type (activated, sustained, item, etc...)

as for me claiming that - well seeing as nearly every other ability is
defined as such via ABI_Base and only those that should only be
availible and/or work in specific areas as the mentioned Unique_Power i
kinda assumed that yes.
- its just that imo that specific Ability/Event isn't supposed to be
used for an item that should work anywhere - you may be of different
opinion on that and thats fine.

as for my former testing with Unique_Ability - might be that i didn't actually check for tags back then (that was before i discovered the 2DA's power in customizing)

and for me not being very detailed - sorry i am at work atm and can't write on "Game-Forums" for hours - wouldn't be "healthy" + i am a bit low on sleep lately - Modding be damned /o\\\\\\\\

Modifié par AND04, 26 novembre 2009 - 03:02 .


#9
trelephant

trelephant
  • Members
  • 29 messages
Alright, thanks for the guidance, fellas. I remade the item, and it now has a "use" ability, but I can't seem to get the proper case to trigger. I don't know if it's my own code at fault or my more general methods. I'll keep at this.

#10
AND04

AND04
  • Members
  • 154 messages
Here is a simple sample of a Handle item-ability script - its mostly a copy paste from some other scripts with lots of variables that isn't used (atm) - but it compiles and if you can get the Floaty to Display you should be able to figure the rest out yourself:


// ---------- Script Starts Here ----------

#include "utility_h"
#include "wrappers_h"
#include "events_h"
#include "items_h"
#include "ability_h"
#include "placeable_h"

#include "log_h"
#include "abi_templates"
#include "item_constants_h"

const int ABILITY_ITEM_SOMETHING               = 123456789;


void _HandleImpact(struct EventSpellScriptImpactStruct stEvent)
{
    // -------------------------------------------------------------------------
    // VAR BLOCK
    // -------------------------------------------------------------------------
    int    nScalingVector   = SCALING_VECTOR_DURATION;
    int    nAttackingValue  = PROPERTY_ATTRIBUTE_SPELLPOWER;
    int    nResistance      = RESISTANCE_INVALID;
    float  fDuration        = 0.0f;
    float  fScaledValue     = 0.0f;
    int    nEffect          = 0;
    int    nHandler         = SPELL_HANDLER_CUSTOM;
    effect eDamage;
    effect eEffect;
    effect[] eEffects;

    // make sure there is a location, just in case
    if (IsObjectValid(stEvent.oTarget) == TRUE)
    {
        stEvent.lTarget = GetLocation(stEvent.oTarget);
    }

    // -------------------------------------------------------------------------
    // Handle Items
    // -------------------------------------------------------------------------
    switch (stEvent.nAbility)
    {
        case ABILITY_ITEM_SOMETHING:
            {

                event   ev          = GetCurrentEvent();
                int     nEventType    = GetEventType(ev);
                int     bEventHandled = FALSE;
        
                object  oUser           = GetEventCreator(ev);
                int     nVariation      = GetEventInteger(ev, 0);
                int     nActionResult   = TRUE;    
               
                DisplayFloatyMessage(GetHero(), "This actually did something!", FLOATY_MESSAGE, 14654488, 10.0);
                //DoSomething();

                bEventHandled = TRUE;

                break;
            }
    }
}

void main()
{
    event ev = GetCurrentEvent();
    int nEventType = GetEventType(ev);

    switch(nEventType)
    {
        case EVENT_TYPE_SPELLSCRIPT_PENDING:
        {
            Ability_SetSpellscriptPendingEventResult(COMMAND_RESULT_SUCCESS);

            break;
        }

        case EVENT_TYPE_SPELLSCRIPT_CAST:
        {
            // Get a structure with the event parameters
            struct EventSpellScriptCastStruct stEvent = Events_GetEventSpellScriptCastParameters(ev);

            // Hand this through to cast_impact
            SetAbilityResult(stEvent.oCaster, stEvent.nResistanceCheckResult);

            break;
        }

        case EVENT_TYPE_SPELLSCRIPT_IMPACT:
        {
            // Get a structure with the event parameters
            struct EventSpellScriptImpactStruct stEvent = Events_GetEventSpellScriptImpactParameters(ev);

            Log_Trace(LOG_CHANNEL_COMBAT_ABILITY, GetCurrentScriptName() + ".EVENT_TYPE_SPELLSCRIPT_IMPACT",Log_GetAbilityNameById(stEvent.nAbility));

            // Handle impact
            _HandleImpact(stEvent);

            break;
        }
    }
}
// ---------- Script Ends Here ----------

Modifié par AND04, 27 novembre 2009 - 03:52 .


#11
sillyrobot

sillyrobot
  • Members
  • 171 messages
edit never mind, I'm an idiot

Modifié par sillyrobot, 27 novembre 2009 - 10:43 .


#12
sillyrobot

sillyrobot
  • Members
  • 171 messages

AND04 wrote...



        case EVENT_TYPE_SPELLSCRIPT_CAST:
        {
            // Get a structure with the event parameters
            struct EventSpellScriptCastStruct stEvent = Events_GetEventSpellScriptCastParameters(ev);

            // Hand this through to cast_impact
            SetAbilityResult(stEvent.oCaster, stEvent.nResistanceCheckResult);

            break;
        }

        case EVENT_TYPE_SPELLSCRIPT_IMPACT:
        {
            // Get a structure with the event parameters
            struct EventSpellScriptImpactStruct stEvent = Events_GetEventSpellScriptImpactParameters(ev);

            Log_Trace(LOG_CHANNEL_COMBAT_ABILITY, GetCurrentScriptName() + ".EVENT_TYPE_SPELLSCRIPT_IMPACT",Log_GetAbilityNameById(stEvent.nAbility));

            // Handle impact
            _HandleImpact(stEvent);

            break;
        }
    }
}
// ---------- Script Ends Here ----------



You can't afford to let the game handle the EVENT_TYPE_SPELLSCRIPT_IMPACT normally.  The decision whether ort not to decrement the item stack count is made at the bottom of the function and it isn't attribute driven.  Hardcoding allows only ITEM_ABILITY_UNIQUE_POWER_UNLIMITED_USE and ITEM_ABILITY_KOLGRIMS_HORN to avoid inventory reduction.

So you need to adjust EVENT_TYPE_SPELLSCRIPT_CAST to not pass through and instead call an equivalent handler.  I'm not sure that's possible.  From the looks of it,EVENT_TYPE_SPELLSCRIPT_IMPACT looks like an auto-generated event that occurs after EVENT_TYPE_SPELLSCRIPT_CAST.

The only solution looks to be to replace ability_code.nss and that isn't very friendly to other mods.

Modifié par sillyrobot, 27 novembre 2009 - 11:06 .


#13
sillyrobot

sillyrobot
  • Members
  • 171 messages
Well, it's not the ONLY solution, of course.



You could increment the inventory count in your dedicated EVENT_TYPE_SPELLSCRIPT_CAST handler, but that still ends up with nasty side effects like an inability to hang local variables on your item as it is going to be continually replaced.

#14
AND04

AND04
  • Members
  • 154 messages

sillyrobot wrote...

Well, it's not the ONLY solution, of course.

You could increment the inventory count in your dedicated EVENT_TYPE_SPELLSCRIPT_CAST handler, but that still ends up with nasty side effects like an inability to hang local variables on your item as it is going to be continually replaced.


not neccesary - you can increment the stack beforehand  if needed - the item stays the same as long as its not destroyed the variables should be persitant on the object.

#15
sillyrobot

sillyrobot
  • Members
  • 171 messages

AND04 wrote...

<snip>

not neccesary - you can increment the stack beforehand  if needed - the item stays the same as long as its not destroyed the variables should be persitant on the object.


Thanks.  I tried that this morning and was getting the opposite result.  I'll try again tonight when I get a chance and fiddle with the placement.  I was using CreateItemOnObject to add the second item.  Is there a  function to adjust stack count or is the stack count directly adjustable?

#16
AND04

AND04
  • Members
  • 154 messages
idd, there is - "SetItemStackSize"

#17
Talian Kross

Talian Kross
  • Members
  • 239 messages
Well, I've been away eating turkey, and I see like ten new responses and a lot of code.

I've only skimmed through it, but if I understand you correctly, then definitely your method is the best (and correct!) method assuming we can create our own unique Ability IDs.

(I know this isn't in dispute or anything.  I just wanted to publicly acknowledge that you were right and I was wrong.) B)

Update: After now actually reading all the new responses, I think I'll just stick with overriding the UNIQUE_POWER event.  It works fine and there are no negative side-effects (if done correctly, but that's true of anything), so why do it the hard way?
:P

Modifié par Talian Kross, 28 novembre 2009 - 04:36 .