Author Topic: Need help/advice making an ability system w/ cooldown/mana  (Read 437 times)

Legacy_thepaphjort

  • Newbie
  • *
  • Posts: 14
  • Karma: +0/-0
Need help/advice making an ability system w/ cooldown/mana
« on: November 17, 2013, 08:54:18 pm »


                Hi again. 
I'm making a short mod for a group of DOTA-loving friends and I'm making playable characters out of heroes from DOTA2. 
Therefore I want to make a system that can handle abilities which use mana and have cooldown. Since I'm preparing the playable characters for them, I'm making the abilities themselves as undroppable items with the unique power ability for the effect. All on the quickbar.
Mana is a seperate item for each player, also on the quickbar. Mana = charges on the item, so they can keep track of how much they have. 
I planned on making the ability item, ex. Breathe Fire for the Dragon Knight use charges, ie. set it to 1 charge when its ready, 0 when not ready. (or making it use 2 charges per use and set it at 3 when ready,1 when not, so it never reaches  0 charges.)
That way they can easily see if the ability is ready or not. 

My problems are: 
1) when the ability item reaches 0 charges (or 1 charges if 2 charges/use) I cant make it go up to 1 (or 3 charges) again. The quickbar shows 0 uses, I can't use the item, but if I right-click it, it shows the right amount of charges, but I still can't activate the item!
Are there problems with the standard item charge system? 

Any other easy way / already made system for making abilities have a cooldown and use mana ? 

Here a typical ability script (I have forgotten how to post scriptcode)

#include "NW_I0_SPELLS"
#include "x2_inc_spellhook"

void main()
{


    if (!X2PreSpellCastCode())
    {
    // If code within the PreSpellCastHook (i.e. UMD) reports FALSE, do not run this spell
        return;
    }

// End of Spell Cast Hook
      //Declare major variables
     string sAbility = "dragon_dt";
     object oControl = GetObjectByTag("control_dk");
     object oPC = GetItemActivator();
     object oTarget = GetItemActivatedTarget();
     location lTarget = GetItemActivatedTargetLocation();
     object oAbility = GetObjectByTag(sAbility);


    int featlevel = GetLocalInt(oPC,sAbility);   // What level is the ability?
    int cooldown = 40;                           // Cooldown of the ability
    int manacost =  10 + (featlevel*2);          // Manacost of the ability
    object oMana = GetItemPossessedBy(oPC,"mana");   // Where is oPC mana?
    int iMana = GetItemCharges(oMana);         // How much mana does he have
    int iAbility = GetItemCharges(oAbility);   // Is the ability ready to be used (should be 20)
    int iNewmana = iMana-manacost;   // What will mana be after ability is used
    int iNewCharges = iAbility+2;// Get charges back if you couldnt use the spell.

    //DEBUG
    // SpeakString("old mana is " + IntToString(iMana));
    //AssignCommand(oControl,SpeakString("test speak"));
    AssignCommand(oControl,SpeakString("I will reset in" + IntToString(cooldown) + "seconds"));
    AssignCommand(oControl,SpeakString("New mana should be " + IntToString(iNewmana)));
    AssignCommand(oControl,DelayCommand(5.0,SpeakString("testing delaycommmand on control")));
    // DEBUG END

     if (GetItemCharges(oAbility) < 18)
      {
       AssignCommand(oPC,SpeakString("* The ability is not ready yet! *"));
       return;
      }

        if (iMana < manacost)
      {
       AssignCommand(oPC,SpeakString("* You dont have enough Mana! *"));
       AssignCommand(oControl,SetItemCharges(oAbility,iNewCharges));   // Get charges back
       return;
      }
    // DEBUG
   // SpeakString(IntToString(iNewmana) + " is the new mana");
    // DEBUG END
     else
     {
        AssignCommand(oControl,SetItemCharges(oMana,iNewmana));
        AssignCommand(oControl,DelayCommand(40.0,SetItemCharges(oAbility,20)));


      // EFFECT OF THE ABILITY

* Here follows the code of the spell/ability itself*


I've tried searching, but no luck for NWN1. Has anybody implemented a system like this? Making it feel more like a "modern" game :-)


    
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Need help/advice making an ability system w/ cooldown/mana
« Reply #1 on: November 17, 2013, 09:01:28 pm »


               Are there problems with the standard item charge system?

Yes. After setting charges from 0, try removing the cast spell itemproperty entirely and then adding a newly created one again
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Need help/advice making an ability system w/ cooldown/mana
« Reply #2 on: November 18, 2013, 03:36:21 am »


               Here's the include we use for this. For items like this, we typically do two charges per use, with an odd number of charges (3, for one use with the possibility of recharge).


const int IP_CONST_CASTSPELL_TALK = 536;

void RestoreItemSpellUses (object oItem) {
    int nSpell, nUses;
    string sSpell, sRestored = "";

    itemproperty ip = GetFirstItemProperty(oItem);

    while (GetIsItemPropertyValid(ip)) {
        if (GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT &&
            GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL) {

            nSpell = GetItemPropertySubType(ip);
            sSpell = " " + IntToString(nSpell) + " ";

            if (FindSubString(sRestored, sSpell) < 0) {
                sRestored += sSpell;
                nUses = GetItemPropertyCostTableValue(ip);

                RemoveItemProperty(oItem, ip);
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyCastSpell(nSpell, nUses), oItem);
            }
        }

        ip = GetNextItemProperty(oItem);
    }
}

int GetItemSpellUses (object oItem) {
    int nUses = GetItemCharges(oItem);

    if (nUses != 1)
        return nUses;

    itemproperty ip = GetFirstItemProperty(oItem);

    while (GetIsItemPropertyValid(ip)) {
        if (GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT &&
            GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL) {

            if (GetItemPropertyCostTableValue(ip) == IP_CONST_CASTSPELL_NUMUSES_2_CHARGES_PER_USE)
                return 0;
        }

        ip = GetNextItemProperty(oItem);
    }

    return 1;
}

void SetItemSpellUses (object oItem, int nUses, int nItemSpellId=-1) {
    int nSpell, nSpellUses, nCostTable, nLimit, nRadial = 0;
    string sSpell, sSet = "";

    if (nUses > 0 && nItemSpellId < 0) {
        if (nUses > 50)
            SetItemCharges(oItem, 50);
        else
            SetItemCharges(oItem, nUses);
    }

    itemproperty ip = GetFirstItemProperty(oItem);

    while (GetIsItemPropertyValid(ip)) {
        if (GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT &&
            GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL) {

            nSpell = GetItemPropertySubType(ip);
            sSpell = " " + IntToString(nSpell) + " ";

            if (FindSubString(sSet, sSpell) < 0) {
                sSet += sSpell;

                if ((nItemSpellId >= 0 && nSpell != nItemSpellId) ||
                    (nItemSpellId < 0 && nSpell == IP_CONST_CASTSPELL_TALK)) {
                    nCostTable = GetItemPropertyCostTableValue(ip);
                } else if (nUses == 0) {
                    nCostTable = IP_CONST_CASTSPELL_NUMUSES_2_CHARGES_PER_USE;
                } else if (nItemSpellId >= 0 || nUses < 0) {
                    if (nUses < 0) {
                        nLimit = GetLocalInt(oItem, "Radial_" + IntToString(nRadial) + "_Limit");
                        nSpellUses = nLimit - GetLocalInt(oItem, "Radial_" + IntToString(nRadial) + "_Used");

                        if (nLimit < 1 || nSpellUses < 0)
                            nSpellUses = -1;
                    } else
                        nSpellUses = nUses;

                    switch (nSpellUses) {
                        case -1: nCostTable = IP_CONST_CASTSPELL_NUMUSES_4_CHARGES_PER_USE; break;
                        case 0:  nCostTable = IP_CONST_CASTSPELL_NUMUSES_2_CHARGES_PER_USE; break;
                        case 1:  nCostTable = IP_CONST_CASTSPELL_NUMUSES_1_USE_PER_DAY;     break;
                        case 2:  nCostTable = IP_CONST_CASTSPELL_NUMUSES_2_USES_PER_DAY;    break;
                        case 3:  nCostTable = IP_CONST_CASTSPELL_NUMUSES_3_USES_PER_DAY;    break;
                        case 4:  nCostTable = IP_CONST_CASTSPELL_NUMUSES_4_USES_PER_DAY;    break;
                        case 5:  nCostTable = IP_CONST_CASTSPELL_NUMUSES_5_USES_PER_DAY;    break;
                        default: nCostTable = IP_CONST_CASTSPELL_NUMUSES_UNLIMITED_USE;     break;
                    }
                } else
                    nCostTable = IP_CONST_CASTSPELL_NUMUSES_1_CHARGE_PER_USE;

                nRadial++;

                RemoveItemProperty(oItem, ip);

                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyCastSpell(nSpell, nCostTable), oItem);
            }
        }

        ip = GetNextItemProperty(oItem);
    }

    if (nUses <= 0)
        SetItemCharges(oItem, 1);
}

Funky
               
               

               
            

Legacy_MerricksDad

  • Hero Member
  • *****
  • Posts: 2105
  • Karma: +0/-0
Need help/advice making an ability system w/ cooldown/mana
« Reply #3 on: November 18, 2013, 07:16:25 pm »


               I was just working on a similar system based on League of Legends type characters. Your comments on your work is already inspirational without pics or anything '<img'>

When I was making a mod for 4e spells using my pen and paper 4e token system, I had to set EVERYBODY (non-monster) to sorcerer class, which allowed me to increment remaining spell uses on timers, or via actions. Setting everybody to sorcerer class allowed me to create groups of spells and abilities I could master-key together for users to pick from.

I installed timers to increment spell uses after cast, basically giving me cooldown timers. However, it was not perfect, as resting or logging out and back in screwed with the timers. A better system would fix that, but I didn't bother.

Using that system, warrior classes had 4e style powers, every class used the same base attack system give or take feat-benefits they chose or were granted by items. I also modified the saves system to work more like 4e without too much fuss at all. I did however have to rewrite the spell hooks and saves functions but it was no big deal because every single spell was custom anyway.

I never did any mana mods, but merging this system with a cooldown timer would probably be pretty easy.

I'm hoping to resurrect the 4e token system for use with my LoL style character system I am working on. Should be out sometime this winter.
               
               

               
            

Legacy_thepaphjort

  • Newbie
  • *
  • Posts: 14
  • Karma: +0/-0
Need help/advice making an ability system w/ cooldown/mana
« Reply #4 on: November 20, 2013, 07:43:22 pm »


               Thanks for the answers!

MerricksDad: Good idea using Sorcerer, I had wondered about that myself,but couldn't figure out how to increment spell uses? I assume you mean simulating cooldown by allowing 1 use of a spell/ability, which goes to 0 when you use it, and then setting it to 1 again after a cooldown? I can only find DecrementRemainingSpellUses. How do you Increment uses of a sorcerer spell again afterwards?

Funkeswerve: Thanks for posting that. It's a bit more advanced than my scripting :-) Could you post an example of an item which has a cooldown and how you handle that?
Also my main problem is now how to make the quickbar update to reflect that you can use the item again. It seems that it doesn't update properly. Does your functions address this?

ShadoOow: Thanks for the suggestion, I'm experimenting with this and will post the new spellscript. The best solution for me (also referring to my other post about burning hands) seems to be to use the spell, give the item 3 charges, with 2 charges/use. This leaves me sofar with only the problem of the quickbar not updating properly. It keeps showing 0 uses left, event when rightcliking the item shows 3 charges. If I clear the quickslot and remake it, then it show 1 use as it is supposed to.
Have you made an item that works using a cooldown system? I would really appreciate it if you could post it? Thanks.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Need help/advice making an ability system w/ cooldown/mana
« Reply #5 on: November 21, 2013, 04:28:51 am »


               

thepaphjort wrote...

Funkeswerve: Thanks for posting that. It's a bit more advanced than my scripting :-) Could you post an example of an item which has a cooldown and how you handle that?
Also my main problem is now how to make the quickbar update to reflect that you can use the item again. It seems that it doesn't update properly. Does your functions address this?


My code is just an example of what Shad was suggesting you do - removing and readding the item prop. Here's a sample of what we do on our epic spells (implemented with items):


void RestoreEpicCharges (object oItem) {
    int nCharges = GetItemCharges(oItem);

    if (nCharges > 0 && !GetLocalInt(oItem, "FKY_CHAT_INSTANT")) {
        SetItemCharges(oItem, nCharges + 2);
        RestoreItemSpellUses(oItem);
    }
}

Yes, it should handle resetting of charges in the quickbar. Here's another example, the beginning of a script, where we restore charges in the event that the spell is used inproperly:



void main () {
    object oPC = GetEpicActivator();

    int nTimer = GetCooldownTimer(oPC, "IF");
    if (nTimer > 0) {
        FloatingTextStringOnCreature("You cannot use Immutable Force again for another " +
            IntToString(nTimer) + " second" + (nTimer == 1 ? "" : "s") + "!", oPC, FALSE);
        RestoreItemSpellUses(GetItemActivated());
        return;
    }

*snip*


How exactly you use it depends on how the item is configured. The typical 3 charge scenario is handled in the first code snippet for epics, above.

Funky
               
               

               
            

Legacy_thepaphjort

  • Newbie
  • *
  • Posts: 14
  • Karma: +0/-0
Need help/advice making an ability system w/ cooldown/mana
« Reply #6 on: November 30, 2013, 02:47:28 pm »


               Just wanted to say thanks for your help. It works flawlessly using Funky's include :

#include "inc_restore_spel"
#include "X0_I0_SPELLS"
#include "x2_inc_spellhook"
#include "x2_inc_itemprop"

void RestoreCharges (object oItem) {
   int nCharges = GetItemCharges(oItem);

   if (nCharges > 0 && !GetLocalInt(oItem, "FKY_CHAT_INSTANT")) {
       SetItemCharges(oItem, nCharges + 2);
       RestoreItemSpellUses(oItem);
   }
}



void main()
{

   if (!X2PreSpellCastCode())
   {
   // If code within the PreSpellCastHook (i.e. UMD) reports FALSE, do not run this spell
       return;
   }

// End of Spell Cast Hook
   int nEvent = GetUserDefinedItemEventNumber();
   if (nEvent == X2_ITEM_EVENT_ACQUIRE)

   {
     return;
   }

// End of Spell Cast Hook
     string sAbility = "lina_lsa";
 object oControl = GetObjectByTag("control_lina");
 object oAbility = GetObjectByTag(sAbility);
 object oPC = GetItemActivator();
 object oTarget = GetItemActivatedTarget();
 location lTarget = GetItemActivatedTargetLocation();
 itemproperty ip = GetFirstItemProperty(oAbility);

   int featlevel = GetLocalInt(oPC,sAbility);   // What level is the ability?
   float cooldown = 30.0;                            // Cooldown of the ability
   int manacost = 4 + featlevel;          // Manacost of the ability
   object oMana = GetItemPossessedBy(oPC,"mana");   // Where is oPC mana?
   int iMana = GetItemCharges(oMana);         // How much mana does he have
   int iAbility = GetItemCharges(oAbility);   // Is the ability ready to be used (should be 20)
   int iNewmana = iMana-manacost;   // What will mana be after ability is used



     if (iMana < manacost)
     {
      AssignCommand(oPC,SpeakString("* You dont have enough Mana! *"));
      AssignCommand(oControl,RestoreCharges(oAbility));

      return;
     }
   // DEBUG
  // SpeakString(IntToString(iNewmana) + " is the new mana");
   // DEBUG END
    else
    {
       AssignCommand(oControl,SetItemCharges(oMana,iNewmana));
       AssignCommand(oControl,DelayCommand(cooldown,RestoreCharges(oAbility)));

    // and here starts the spell itself: