Skip to content

Bug Report: unique prop on an item HELP!!

QuilistanQuilistan Member Posts: 177
edited July 2022 in Builders - Scripting
I can't seem to figure out getting the alternate script in the while loop to fire.

My idea here is I have a weapon that can cast a unique power (unleashing 3 spells, Call Lightning, Chain Lightning, and Gust of Wind, along with an animation that takes a full round). This part seems to be working just fine (maybe some timing tweaks needed, but if fires as it should).

The next idea was to have the weapon "recharge" its uses as it does damage in combat. So I am trying to get it to fire a second script once the unique power use/per/day is at 0.
This is the part that I can't figure out.........

The item is set with both a:
Cast Spell Unique Power
OnHitCastSpell Unique Power

which I think either will fire the script via tag based blah blah blah.... (maybe I am wrong here and this simply isn't possible?)
void main()
{
object oPC = GetItemActivator();
object oItem  =  GetSpellCastItem(); // The item casting triggering this spellscript
int nPower = GetHitDice(oPC);

itemproperty ip = GetFirstItemProperty(oItem);

   while (GetIsItemPropertyValid(ip))
    {
        if (GetItemPropertyType(ip) == IP_CONST_CASTSPELL_UNIQUE_POWER)
        {
            if (GetItemPropertyUsesPerDayRemaining(oItem, ip) == 0)
                {
                    ExecuteScript("storm_axe_chrg", oPC);
                    SendMessageToPC(oPC, "Sent Command to run recharge script");
                    return;
                }
        }
            ip = GetNextItemProperty(oItem);
   }

ClearAllActions();

       // oItem  =  GetSpellCastItem(); // The item casting triggering this spellscript

        object oSpellTarget = GetSpellTargetObject();

    // Have the item activator perform a sequence of actions.
    AssignCommand(oPC, ActionPlayAnimation(ANIMATION_FIREFORGET_VICTORY1));
    DelayCommand(1.0, PlayVoiceChat(VOICE_CHAT_TAUNT, oPC));

        // Have the item activator cast Call Lightning.
    DelayCommand(0.1, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_CALL_LIGHTNING, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));

    // Have the item activator cast Gust of Wind.
    DelayCommand(0.2, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_GUST_OF_WIND, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));

    // Have the item activator cast Chain Lightning.
    DelayCommand(0.3, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_CHAIN_LIGHTNING, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));
}
Post edited by Quilistan on

Comments

  • ForSeriousForSerious Member Posts: 446
    Since you have two things going on, you should need two scripts, right?
    From the looks of what you have here, it will call the recharge script when activated by the user once the charges run out. For debugging that's okay, but it's not what you described it working like.

    To get the onHit spell to work, you need GetModuleSwitchValue(MODULE_SWITCH_ENABLE_TAGBASED_SCRIPTS) to return TRUE.
    I think that is usually defined in the module loader script.
    Then you need your weapon to be tagged "storm_axe_chrg" for it to call the onHit script.
    (If you have "MODULE_VAR_TAGBASED_SCRIPT_PREFIX" set, you'll need that to be on the front of your onHit script name.)

    If that's not working for you, feel free to share your storm_axe_chrg script.
  • QuilistanQuilistan Member Posts: 177
    The main script is called storm_axe and the item has the same tag so that script fires when its unique spell is used.

    I am hoping the same item (same tag) can fire the onhit script through the "execute script" line.

    here is the storm_axe_chrg script
    void main()
    {
    object oItem;
    object oPC = OBJECT_SELF;
    oItem = GetItemPossessedBy(oPC, "storm_axe");
    itemproperty ip = GetFirstItemProperty(oItem);
    int iCharge = GetLocalInt(oItem, "iCharge");
    int nDamage = GetDamageDealtByType(DAMAGE_TYPE_BASE_WEAPON);
    SendMessageToPC(oPC, "Running recharge script 1");
    
    if (iCharge <= 100)
        {
        SetLocalInt(oItem, "iCharge", iCharge + nDamage);
        SendMessageToPC(oPC, "Storm's Wrath charging +" + IntToString(GetLocalInt(oItem, "iCharge")));
        }
    
    else if (iCharge >= 101)
        {
                SendMessageToPC(oPC, "Running recharge script 2");
    
    while (GetIsItemPropertyValid(ip))
        {
            if (GetItemPropertyType(ip) == IP_CONST_CASTSPELL_UNIQUE_POWER)
            {
                SetItemPropertyUsesPerDayRemaining(oItem, ip, 3);
                SendMessageToPC(oPC, "Storm's Wrath has recharged!");
                return;
            }
            ip = GetNextItemProperty(oItem);
        }
        }
    }
    
    
  • ForSeriousForSerious Member Posts: 446
    Ohh I see. You have the script that runs on item activate—that is defined by the item tag—and you also want a different one that fires on hit, but has to be defined by the item tag.
    New plan. When the item runs out of charges, set it's tag to the charge script. Then, when it's charged, change it back to the first script.
  • QuilistanQuilistan Member Posts: 177
    oh nice plan! will give it a shot.
  • meaglynmeaglyn Member Posts: 146
    You can use the same script. Just check the event type and do the code for each event. For tag based scripts you should be doing that anyway so it doesn't do things on acquire and other events you don't want.
    int nEvent = GetUserDefinedItemEventNumber();
    if (nEvent == X2_ITEM_EVENT_ACTIVATE) {
          // do active stuff
    	return;
    }
    if (nEvent == X2_ITEM_EVENT_ONHITCAST) {
         // do on hit stuff
         return;
    }
    
  • QuilistanQuilistan Member Posts: 177
    Thanks maeglyn for posting that.

    I have implimented the GetUserDefinedItemEventNumber() and it is getting the execute script to fire, so the recharge aspect of the item is working.

    Issue now is that it does not recognized the ACTIVATE event as it should, it constantly just runs the OnHit
    if I set the if(nEvent) check to the actual intiger 0 for X2_ITEM_EVENT_ACTIVATE it will run the script correctly for the first round of Activates only, following that it defaults to the OnHit event
    #include "x2_inc_switches"
    //#include "X0_I0_SPELLS"
    
    void main()
    {
    object oPC = GetItemActivator();
    object oItem  =  GetSpellCastItem(); // The item casting triggering this spellscript
    int nPower = GetHitDice(oPC);
    int nEvent = GetUserDefinedItemEventNumber();
    
        SendMessageToPC(oPC, "Event Number is" + IntToString(nEvent));
    
    if (nEvent == X2_ITEM_EVENT_ACTIVATE)
        {
          // do active stuff
    
        ClearAllActions();
    
           // oItem  =  GetSpellCastItem(); // The item casting triggering this spellscript
    
        object oSpellTarget = GetSpellTargetObject();
    
        // Have the item activator perform a sequence of actions.
        AssignCommand(oPC, ActionPlayAnimation(ANIMATION_FIREFORGET_VICTORY1));
        DelayCommand(1.0, PlayVoiceChat(VOICE_CHAT_TAUNT, oPC));
    
            // Have the item activator cast Call Lightning.
        DelayCommand(0.1, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_CALL_LIGHTNING, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));
    
        // Have the item activator cast Gust of Wind.
        DelayCommand(0.2, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_GUST_OF_WIND, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));
    
        // Have the item activator cast Chain Lightning.
        DelayCommand(1.3, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_CHAIN_LIGHTNING, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));
    
        return;
        }
    
    if (nEvent == X2_ITEM_EVENT_ONHITCAST)
    {
     // do on hit stuff
    SendMessageToPC(oPC, "OnHit Fired");
    itemproperty ip = GetFirstItemProperty(oItem);
    
       while (GetIsItemPropertyValid(ip))
        {
        //SendMessageToPC(oPC, "Property is valid");
            //if (GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL)
            //{
                //SendMessageToPC(oPC, "Property is CastSpell");
                if (GetItemPropertySubType(ip) == IP_CONST_CASTSPELL_UNIQUE_POWER)
                {
                    //SendMessageToPC(oPC, "Property is CastSpell: Unique Power" + IntToString(GetItemPropertyUsesPerDayRemaining(oItem, ip)));
                    if (!GetItemPropertyUsesPerDayRemaining(oItem, ip) >= 1)
                        {
                        ExecuteScript("storm_axe_chrg", oPC);
                        //SendMessageToPC(oPC, "Sent Command to run recharge script");
                        return;
                        }
                 }
            //}
                ip = GetNextItemProperty(oItem);
       }
       return;
    }
    
    
    }
    
  • meaglynmeaglyn Member Posts: 146
    "object oPC = GetItemActivator();" is only right for the X2_ITEM_EVENT_ACTIVATE case. Not sure I understand exactly what you are seeing but that and the item are probably not quite right for the different events (although oItem looks like it may be right). Take a look at x2_s3_onhitcast.nss it shows what the objects are for on hit cast events in the code and comments. Actually it looks like you have done that...

    It's been a while since I played with on hit cast but it looks like it runs as the hitting (at least with weapons) object rather than the module. So oPC may just be OBJECT_SELF in that case.
  • QuilistanQuilistan Member Posts: 177
    edited July 2022
    ok Let me try to explain a bit better:

    If I reset the server, so fresh start to everything.

    The axe has 3 uses of the Activate Item Unique Power. I use those and they fire the X2_ITEM_EVENT_ACTIVATE event correctly running that specific section of code.

    Following that ANY uses of the the Activate Item Unique Power will only fire the X2_ITEM_EVENT_ONHITCAST event section, so it fails to recognize the Activate Item use correctly
  • meaglynmeaglyn Member Posts: 146
    That last sentence still doesn't make sense. If you can still activate the item (rather than hitting something with it) then it will fire the active item event. Activating the item should not fire the onhitcast event.

    Once the item's per day uses goes to zero it should not be able to be activated any more. I don't know if EE has changed this but before in that case I think you had to rest. That is, even if you gave it another use it would still not be usable again that day until the PC rested. You may be hitting that or some variation if the item is not activating anymore at all.
  • QuilistanQuilistan Member Posts: 177
    Ok I think I am miscommunicating the fact that I am attempting to make the Activate Unique Power "RECHARGE" through combat.

    The weapon should allow the player to use 3 unique power actions (this unique power casts Call Lightning, Chain Lightning, and Gust of Wind as ONE GIANT ability), then once those are used up the player can recharge the uses via melee combat.

    The onhit event is setup to execute the script storm_axe_chrg script (posted previously, 2nd code posted in this thread) which after so much melee combat it reinstates the Active Unique Power uses.

    The issue I am having is once this script (storm_axe_chrg script) fires and the activate unique power uses are reinstated, Activating the item is not working. At that point is treats all activate unique power uses as onhits, and ONLY fires the onhit section of the script. This is regardless of if it is an onhit event or an activated event (it fails to return and activated event correctly)



  • meaglynmeaglyn Member Posts: 146
    Yeah I get that. What I don't understand is activating the item and getting the onhit event. That's not really possible. Are you still in combat when this is happening (i.e. still hitting things)? Is it really activating? There should be animations etc for you to tell. If so it _will_ fire the activate item event. It won't fire the wrong event unless you hacked up some of the base game scripts to do that.
  • QuilistanQuilistan Member Posts: 177
    I know it is not supposed to fire the wrong one but it is.......

    I can stop fighting, zone, rest and come back and the USE: Unique Power (Activate Item) will not fire the correct event, it only fires the OnHit event after that.


    New Test:
    ok this last test I used the 3 activate item uses, then without any combat (avoiding the execution of the storm_axe_chrg script) I ran away to rest. The next 3 activate item uses worked as intended.

    This means that something about executing the storm_axe_chrg script in the way that I am is breaking something and causing it to no longer recognize the item's Use: Unique Power as an "ACTIVATE_ITEM" event it then fires it as an OnHit event.
  • QuilistanQuilistan Member Posts: 177
    reposting code since I have been messing with it.
    storm_axe:
    #include "x2_inc_switches"
    
    
    void main()
    {
       object oItem;        // The item casting triggering this spellscript
       object oSpellTarget; // On a weapon: The one being hit. On an armor: The one hitting the armor
       object oSpellOrigin; // On a weapon: The one wielding the weapon. On an armor: The one wearing an armor
    
       // fill the variables
       oSpellOrigin = OBJECT_SELF;
       oSpellTarget = GetSpellTargetObject();
       oItem        =  GetSpellCastItem();
    
    int nEvent = GetUserDefinedItemEventNumber();
    
    if (nEvent == X2_ITEM_EVENT_ACTIVATE)// do active stuff
        {
        object oPC = GetItemActivator();
        int nPower = GetHitDice(oPC);
    
        ClearAllActions();
    
        // Have the item activator perform a sequence of actions.
        AssignCommand(oPC, ActionPlayAnimation(ANIMATION_FIREFORGET_VICTORY1));
        DelayCommand(1.0, PlayVoiceChat(VOICE_CHAT_TAUNT, oPC));
    
            // Have the item activator cast Call Lightning.
        DelayCommand(0.1, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_CALL_LIGHTNING, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));
    
        // Have the item activator cast Gust of Wind.
        DelayCommand(0.2, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_GUST_OF_WIND, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));
    
        // Have the item activator cast Chain Lightning.
        DelayCommand(1.3, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_CHAIN_LIGHTNING, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));
    
        return;
        }
    
    if (nEvent == X2_ITEM_EVENT_ONHITCAST)// do on hit stuff
        {
        //object oSpellTarget = GetSpellTargetObject();
        object oPC = OBJECT_SELF;
    
        SendMessageToPC(oPC, "OnHit Fired");
        itemproperty ip = GetFirstItemProperty(oItem);
    
        while (GetIsItemPropertyValid(ip))
            {
                if (GetItemPropertySubType(ip) == IP_CONST_CASTSPELL_UNIQUE_POWER)
                {
                    if (!GetItemPropertyUsesPerDayRemaining(oItem, ip) >= 1)
                        {
                        ExecuteScript("storm_axe_chrg", oPC);
                        return;
                        }
                 }
                ip = GetNextItemProperty(oItem);
            }
            return;
        }
    
    
    }
    

    storm_axe_chrg:
    #include "x2_inc_switches"
    void main()
    {
    object oItem;
    object oPC = OBJECT_SELF;
    oItem = GetItemPossessedBy(oPC, "storm_axe");
    itemproperty ip = GetFirstItemProperty(oItem);
    int iCharge = GetLocalInt(oItem, "iCharge");
    int nDamage = d4(1);
    
    if (iCharge <= 100)
        {
        SetLocalInt(oItem, "iCharge", iCharge + nDamage);
        SendMessageToPC(oPC, "Storm's Wrath charging +" + IntToString(GetLocalInt(oItem, "iCharge")));
        }
    
    else if (iCharge >= 101)
        {
        while (GetIsItemPropertyValid(ip))
        {
            if (GetItemPropertySubType(ip) == IP_CONST_CASTSPELL_UNIQUE_POWER)
            {
                SetItemPropertyUsesPerDayRemaining(oItem, ip, 3);
                SendMessageToPC(oPC, "Storm's Wrath has recharged!");
                SetLocalInt(oItem, "iCharge", 0);
                //SetExecutedScriptReturnValue(X2_EXECUTE_SCRIPT_END);
                return;
            }
            ip = GetNextItemProperty(oItem);
        }
        }
    }
    
  • ForSeriousForSerious Member Posts: 446
    Question: would it be possible to change it to just charges instead of charges per day? Would that destroy the item if they used all the charges?
  • QuilistanQuilistan Member Posts: 177
    I can certainly give that a shot, I don't think it destroys the item.
  • QuilistanQuilistan Member Posts: 177
    Well first off it did destroy the item. Then I figured out how to run it keeping enough charges where it wouldn't get destroyed.

    BUT it ends up with the same issue. After "recharging" it then fails to recognized the X2_ITEM_EVENT_ACTIVATE event......

    I think I am going to try and NOT execute the 2nd script.
  • ForSeriousForSerious Member Posts: 446
    I have not tried to make a setup to run your scripts yet, but from just looking, I'm running out of ideas. I do agree that you should not need to call the other script from the first. You should be able to just divide the code by the event type.
  • QuilistanQuilistan Member Posts: 177
    Wild it still refuses to recognize the X2_ITEM_EVENT_ACTIVATE event after it fires the OnHit event. It treats all events after as OnHit
    #include "x2_inc_switches"
    
    
    void main()
    {
       object oItem;        // The item casting triggering this spellscript
       object oSpellTarget; // On a weapon: The one being hit. On an armor: The one hitting the armor
       object oSpellOrigin; // On a weapon: The one wielding the weapon. On an armor: The one wearing an armor
    
       oSpellOrigin = OBJECT_SELF;
       oSpellTarget = GetSpellTargetObject();
       oItem        =  GetSpellCastItem();
    
       int nEvent = GetUserDefinedItemEventNumber();
    
    if (nEvent == X2_ITEM_EVENT_ACTIVATE)// do active stuff
        {
        object oPC = GetItemActivator();
        int iUse = GetItemCharges(oItem);
        int nPower = GetHitDice(oPC);
    
                if (iUse <= 23)
                {
                        SendMessageToPC(oPC, "Storm Axe fails to fire. It needs to be fully charged at 25 charges, if it reaches 0 charges it will destroy it's self.");
                        return;
                }
        else if (iUse >= 24)  //this has to be 24+ since a charge is removed prior to script firing
            {
    
                ClearAllActions();
    
                // Have the item activator perform a sequence of actions.
                AssignCommand(oPC, ActionPlayAnimation(ANIMATION_FIREFORGET_VICTORY1));
                DelayCommand(1.0, PlayVoiceChat(VOICE_CHAT_TAUNT, oPC));
    
                // Have the item activator cast Call Lightning.
                DelayCommand(0.1, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_CALL_LIGHTNING, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));
    
                // Have the item activator cast Gust of Wind.
                DelayCommand(0.2, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_GUST_OF_WIND, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));
    
                // Have the item activator cast Chain Lightning.
                DelayCommand(1.3, AssignCommand(oPC, ActionCastSpellAtObject(SPELL_CHAIN_LIGHTNING, oSpellTarget, METAMAGIC_ANY, TRUE, nPower, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));
    
                return;
            }
        }
    
    if (nEvent == X2_ITEM_EVENT_ONHITCAST)// do on hit stuff
        {
        object oPC = OBJECT_SELF;
        int iUse = GetItemCharges(oItem);
    
        int iCharge = GetLocalInt(oItem, "iCharge");
        int nDamage = d4(1);
    
        SendMessageToPC(oPC, "OnHit Fired");
    
                if (iUse <= 24)
                {
                       if (iCharge <= 100)
                    {
                    SetLocalInt(oItem, "iCharge", iCharge + nDamage);
                    SendMessageToPC(oPC, "Storm's Wrath charging +" + IntToString(GetLocalInt(oItem, "iCharge")));
                    }
                        else if (iCharge >= 101)
                    {
                            if (iUse <= 24)
                        {
                        SetItemCharges(oItem, 25);
                        SendMessageToPC(oPC, "Storm's Wrath has recharged!");
                        SetLocalInt(oItem, "iCharge", 0);
                        return;
                        }
                    }
                }
            return;
        }
    }
    
  • ForSeriousForSerious Member Posts: 446
    That's super broken. Even if I add SetUserDefinedItemEventNumber(X2_ITEM_EVENT_ACTIVATE); to the module OnActivate script, it still returns 3 or X2_ITEM_EVENT_ONHITCAST after attacking something with the weapon in question—but only on the X2_ITEM_EVENT_ACTIVATE event. All the other events like unequip drop and pickup return the correct numbers.

    I think you have found a bug and we would need Beamdog to fix it.
  • QuilistanQuilistan Member Posts: 177
    edited July 2022
    Yup!

    where do we report bugs? I changed the title to mention a bug.
  • ForSeriousForSerious Member Posts: 446
    edited July 2022
    I found where to officially make a bug report. I created a detailed one.
    Post edited by ForSerious on
  • ForSeriousForSerious Member Posts: 446
    So, I got a working version going.
    The main thing I did was make a new local variable on the module to keep track of if on activate has been called. I check that instead of GetUserDefinedItemEventNumber() but only for the on activate section.
    The biggest issue with this is, you would have to find all the scripts that get called by on activate and reset the variable to signify that on activate is done.
  • QuilistanQuilistan Member Posts: 177
    Interesting, that sounds pretty inefficient though?

    Perhaps your original "new plan" still needs to be visited. Changing the tag of the item for each behavior. I might mess around with that today.
  • ForSeriousForSerious Member Posts: 446
    That is how I originally set it up, but I forgot that on hit will still call the script with the same tag even if it's supposed to be the on activate script. Maybe I need to add the prefix option.
Sign In or Register to comment.