• You've discovered RedGuides 📕 an EverQuest multi-boxing community 🛡️🧙🗡️. We want you to play several EQ characters at once, come join us and say hello! 👋
  • IS THIS SITE UGLY? Change the look. To dismiss this notice, click the X --->
Resource icon

Plugin - MQ2Twist

With the nerf of MotP, it is almost impossible for a bard to get through 4 songs in a twist twice. Since I play a Bard Main, I thought it would be nice to find a fix for MQ2Twist that would allow you to play Song of the Dryads in a normal twist. The old MQ2Twist and ones included in the recent compiles don't allow this to twist in normally and skips 4 or 5 times before it plays it again.

So, after some thorough searching on the MQ2 Forums, I found a fix. All credit for the coding of this goes to the original author of Twist and oortcloud, for the modified section of code that allows for Song of the Dryads to be twisted in normally without skipping. I started making my own compiles lately, so that's how I have it compiled. I think I'll post my compile here too, as soon as I get a few bugs out.

Enjoy!

Rich (BB code):
// MQ2Twist.cpp - Bard song twisting plugin for MacroQuest2
//
//    koad 03-24-04 Original plugin (http://macroquest.sourceforge.net/phpBB2/viewtopic.php?t=5962&start=2)
//    CyberTech 03-31-04 w/ code/ideas from Falco72 & Space-boy
//    Cr4zyb4rd 08-19-04 taking over janitorial duties
//    Pheph 08-24-04 cleaning up use of MQ2Data

/*
   MQ2Twist Version 1.3

      Usage:
         /twist # # # # # - Twists in the order given.
            Valid options are 1 thru 9 for song gems, and 10 thru 19 for item clicks.
            These may be mixed in any order, and repeats are allowable. Up to 10 may be
            specified.
            If a song is specified with a duration longer than standard (ie, selos)
            that song will be twisted based on it's duration.  For example, riz+mana+selos
            would be a 2 song twist with selos pulsed every 2.5 min.
         /twist once # # # # # - Twists in the order given, then reverts to original twist
         /twist hold <gem #> - Pause twisting and sing only the specified song
            /sing <gem#> - alias for /twist hold
         /twist stop/end/off - stop twisting, does not clear the twist queue
            /stoptwist - alias for above
         /twist or /twist start - Resume the twist after using /twist hold or /twist stop
       /twist reset - Reset timers for item clicks and long duration songs
         /twist delay # - 10ths of a second, minimum of 30, default 33
         /twist adjust # - in ticks, how early to recast long duration songs
         /twist reload - reload the INI file to update item clicks
         /twist slots - List the slots/items defined in the INI and their #'s
         /twist quiet - Toggles songs listing and start/stop messages for one-shot twists

      ----------------------------
      Item Click Method:
         MQ2Twist uses /itemnotify slotname rightmouseup to perform item clicks.

         The INI file allows you to specify items by name (with name=itemname), or by
       inventory slot (with slot=slotname).  If both a name and slot are defined for an
       item, the plugin will attempt to swap the item into that slot (via the /exchange
       command) and replace the original item when casting is complete.

       The example INI file below contains examples of the types of usage.

      ----------------------------
      Examples:
         /twist 1
            Sing gem 1 forever
         /twist 1 2 3
            Twist gems 1,2, and 3 forever
         /twist 1 2 3 10
            Twist gems 1,2,3, and clicky 10, forever
         /twist hold 4 or /sing 4
            Sing gem 4 until another singing-related /twist command is given

      ----------------------------
      MQ2Data Variables:
         bool   Twist         Currently Twisting: true/false, if NULL plugin is not loaded
         Members:
            bool     Twisting  Currently twisting: true/false.
            int      Current   Returns the curent gem number being sung, -1 for item, or 0 if not twisting
            int      Next      Returns the next gem number to be sung, -1 for item, or 0 if not twistsing
            string   List      Returns the twist sequence in a format suitable for /twist

      ----------------------------

     The ini file has the format:
         [MQ2Twist]
         Delay=32       Delay between twists. Lag & System dependant.
       Adjust=1       This defines  how many ticks before the 'normal' recast time to cast a long song.
                        Long songs are defined as songs greater than 3 ticks in length.  If set to 1 tick,
                        and a song lasts 10 ticks, the song will be recast at the 8 tick mark, instead of
                        at the 9 tick mark as it normally would.

         [Click_10] thru [Click_19]
         CastTime=30              Casting Time, -1 to use the normal song delay
         ReCastTime=0             How often to recast, 0 to twist normally.
         Name="Fife of Battle"    Item name for /itemnotify
       Slot=neck                Slot name for /itemnotify

         Delay, CastTime and ReCastTime are specified in 10ths of a
         second, so 10 = 1 second, and so on.

         INI File Example:
            [MQ2Twist]
            Delay=31
         Quiet=0

            ;Shadowsong cloak
            [Click_10]
            CastTime=30
            ReCastTime=350
            Name=Shadowsong Cloak
            Slot=DISABLED

            ;girdle of living thorns (current belt will be swapped out)
            [Click_11]
            CastTime=0
            ReCastTime=11600
            Name=Girdle of Living Thorns
            Slot=waist

            ;nature's melody
            [Click_12]
            CastTime=-1
            ReCastTime=135
            Name=DISABLED
            Slot=mainhand

            ;lute of the flowing waters
            [Click_13]
            CastTime=0
            ReCastTime=0
            Name=Lute of the Flowing Waters
            Slot=DISABLED

            [Click_14] ... [Click_19]
            CastTime=33
            ReCastTime=0
            Name=DISABLED
            Slot=DISABLED

      ----------------------------

Changes:
   10-05-04
      Support "swap in and click" items

    09-15-04
      Support extra spell slot from Omens of War AA

    09-01-04
      Command: /twist quiet to toggle some of the spam on/off
      Various code fixes/speedups

    08-29-04
      Moved LONGSONG_ADJUST into INI file and made /twist adjust command to set it on
      the fly

   08-25-04
      Changed output for /twist once to be slightly less misleading
      Reset click/song timers every time they're called with /twist hold or /twist once;
      if the user's specifying that song, they obviously want to cast it anyway.
      Removed the variable MissedNote as close inspection revealed the only place it was
      checked for was the line that set it. /boggle
      Minor code tweaks, cleanups, formatting changes, etc

   08-24-04 (Pheph)
      Modified it to use only one TLO, as I found it somewhat messy having 4 different ones.
      All the functionality of the old TLO's are now members of ${Twist}
      ${Twising} is now ${Twist.Twisting}, or just ${Twist}
      ${TwistCurrent} is now ${Twist.Current}
      ${TwistNext} is now ${Twist.Next}
      ${TwistList} is now ${Twist.List}

   08-23-04
      Reset_ItemClick_Timers was being called far too often.  Now the only time we reset
     is if a new list of songs are specified.  "/twist ${TwistList}" is a useful alias
     if you for some reason want the old behavior.
      Sing or /twist hold now resets the cast/item timer for that song only, rather than
     the entire list.
     Command: /twist reset calls Reset_ItemClick_Timers without interfering with the
     state of the current twists.

   08-22-04
      Command: /twist once [songlist] will cycle through the songs entered once, then
     revert to the old twist, starting with the song that was interrupted.
     Removed command "/twist on", it was making the string compare for "once" annoying,
     and I didn't think it was worth the effort for a redundant command.
      /twist delay with no argument now returns the delay without resetting it.  Values
     less than 30 now give a warning...maybe they're not bards or have some other
     reason for using a low value.

   08-19-04
      Minor revamp of item notification.  Removed ITEMNOTIFY define and kludged in some
      changes from Virtuoso65 to get casting by item name working.  /cast is no longer
      used.
      Added INI file support for above change.  File now uses distinct entries for item
      names and slots.  *Quotes not required for multi-word item names in INI.*
      Fixed the MQ2Data value TwistCurrent to display the current song as-advertised, and
      added a new value TwistNext with the old behavior of showing the next song in the
      queue. (Useful in scripting)
      Removed a few DebugSpews that were mega-spamming my debugger output.
      CastTime of -1 in the INI file now causes the default delay to be used.

   06-01-04
      Added LONGSONG_ADJUST (default to 1 tick) to help with the timing of recasting long
      songs, such as selo's.
      Twisting is now paused when you sit (this would include camping).  This fixes
      problems reported by Chyld989 (twisting across chars) and Kiniktoo (new autostand on
      cast 'feature' in EQ makes twisting funky)

   05-19-04
      Added workaround for incorrect duration assumption for durationtype=5 songs, such as
      Cassindra's Chant of Clarity or Cassindra's Chorus of Clarity.
      Added check of char state before casting a song. Actually added for 1.05
         Checked states and resulting action are:
            Feigned, or Ducking = /stand
            Stunned = Delay
            Dead - Stop twisting.
         If you're a monk using this to click your epic, you'll want to disable the autostand on feign code =)


   05-05-05
      Fixed CTD on song unmem or death, while twisting.  Oops
      Removed circle functionality.  It's better suited for a plugin like the MQ2MoveUtils
         plugin by tonio at http://macroquest.sourceforge.net/phpBB2/viewtopic.php?t=6973

   05-01-04
      Fixed problem with using pchar before state->ingame causing CTD on eq load (thanks MTBR)
      Fixed vc6 compile error w/ reset_itemclick_timers
      Replaced various incantations of pChar and pSpawn with GetCharInfo()
      Fixed /circle behavior w/ unspecified y/x
      Fixed /circle on when already circling and you want to update loc
      Added output of parsed circle parameters on start.

   04-25-04
      Converted to MQ2Data
         Top Level Objects:
            bool   Twisting      (if NULL plugin is not loaded)
            int      TwistCurrent
            string   TwistList
      Removed $Param synatax for above
      Added check to make sure item twists specified are defined
      Fixed error with twist parameter processing
      Changed twist startup output to be more verbose
      Command: /twist on added as alias for /twist start
      INI File is now named per-character (MQ2Twist_Charname.ini)
         * Be sure to rename existing ini files
      Modified twist routine to take into account songs with
         non-0 recast times or longer than 3 tick durations,
         and only re-cast them after the appropriate delay.
         This is for songs like Selos 2.5 min duration, etc.
         * Note that this makes no attempt to recover if the song
         effect is dispelled, your macro will need to take care
         of that.
      Added ability to compile-time change the method used for
         clicking items.

   04-13-04
      Changed /circle command to allow calling w/o specifying loc
      Corrected a problem with multiple consecutive missed notes
      Added handling of attempting to sing while stunned
      Command: /twist slots, to list the slot to # associations
      Command: /twist reload, to reload the ini file on the fly
      Command: /twist end, /twist off as aliases for /twist stop
      Command: /sing #, as an alias for /twist hold #

      Added support for item clickies.  Clickies are specified
      as "gem" 10-19. For example, /twist 1 2 10 12

      Added INI file support for storing item clicky info
      and default twist delay.

   04-11-04
      Integrated the /circle code from Easar, runs in a circle.  type
      /circle for help.
*/

Rich (BB code):
#include "../MQ2Plugin.h"

PreSetup("MQ2Twist");

typedef struct _ITEMCLICK {
   int cast_time;
   int recast;
   long castdue;
   int disabled;
   int nousename;
   CHAR slot[MAX_STRING];
   CHAR name[MAX_STRING];
} ITEMCLICK;

int MQ2TwistEnabled = 0;
const int MAX_SONG=10;
int LONGSONG_ADJUST=1; // In TICKS, not seconds.  Used for long songs (greater than 3 ticks in duration). See docs.
int CAST_TIME=33;
int NumSongs=0;
int AltNumSongs=0;
int Song[MAX_SONG*2];
int AltSong[MAX_SONG*2];
long SongNextCast[MAX_SONG*2];
ITEMCLICK ItemClick[MAX_SONG];
int CurrSong=0;
int AltCurrSong=0;
int PrevSong=0;
int HoldSong=0;
long CastDue=0;
bool bTwist=false;
bool altTwist=false;
bool quiet;
CHAR SwappedOutItem[MAX_STRING];
CHAR SwappedOutSlot[MAX_STRING];

long GetTime();
VOID TwistCommand(PSPAWNINFO pChar, PCHAR szLine);
VOID StopTwistCommand(PSPAWNINFO pChar, PCHAR szLine);
VOID SingCommand(PSPAWNINFO pChar, PCHAR szLine);
BOOL dataTwist(PCHAR szIndex, MQ2TYPEVAR &Ret);
CHAR MQ2TwistTypeTemp[MAX_STRING]={0};

//get current timestamp in tenths of a second
long GetTime()
{
   SYSTEMTIME st;
   ::GetSystemTime(&st);
   long lCurrent=0;
   lCurrent  = st.wDay    * 24 * 60 * 60 * 10;
   lCurrent += st.wHour        * 60 * 60 * 10;
   lCurrent += st.wMinute           * 60 * 10;
   lCurrent += st.wSecond                * 10;
   lCurrent += (long)(st.wMilliseconds/100);
   return (lCurrent);
}

VOID MQ2TwistDoCommand(PSPAWNINFO pChar, PCHAR szLine)
{
   HideDoCommand(pChar, szLine, FromPlugin);
}

VOID DoSwapOut()
{
   CHAR szTemp[MAX_STRING];
   if (SwappedOutItem[0]) {
      sprintf(szTemp,"/exchange \"%s\" %s",SwappedOutItem,SwappedOutSlot);
      MQ2TwistDoCommand(NULL, szTemp);
      SwappedOutItem[0]=0;
   }
}

VOID DoSwapIn(int Index)
{
   CHAR szTemp[MAX_STRING];
   if (strnicmp(ItemClick[Index].slot,"DISABLED",8)) {
      sprintf(szTemp,"${InvSlot[%s].Item.Name}",ItemClick[Index].slot);
      ParseMacroData(szTemp);
      strcpy(SwappedOutItem,szTemp);
      strcpy(SwappedOutSlot,ItemClick[Index].slot);
      sprintf(szTemp,"/exchange \"%s\" %s",ItemClick[Index].name,ItemClick[Index].slot);
      MQ2TwistDoCommand(NULL, szTemp);
   }
}

VOID Reset_ItemClick_Timers()
{
   int i;
   for (i=0;i<10;i++) {
      ItemClick.castdue = 0;
   }
   for (i=0;i<MAX_SONG*2;i++) {
      SongNextCast = 0;
   }
}


VOID Update_INIFileName() {
   if (GetCharInfo()) {
      sprintf(INIFileName,"%s\\MQ2Twist_%s.ini",gszINIPath,GetCharInfo()->Name);
   } else {
      sprintf(INIFileName,"%s\\MQ2Twist.ini",gszINIPath);
   }
}

VOID Load_MQ2Twist_INI()
{
   CHAR szTemp[MAX_STRING]={0};
   CHAR szSection[MAX_STRING]={0};

   Update_INIFileName();

   CAST_TIME = GetPrivateProfileInt("MQ2Twist","Delay",33,INIFileName);
   sprintf(szTemp, "%d", CAST_TIME);
   WritePrivateProfileString("MQ2Twist","Delay",szTemp,INIFileName);
   quiet = GetPrivateProfileInt("MQ2Twist","Quiet",0,INIFileName)? 1 : 0;
   sprintf(szTemp, "%d", quiet);
   WritePrivateProfileString("MQ2Twist","Quiet",szTemp,INIFileName);

   LONGSONG_ADJUST = GetPrivateProfileInt("MQ2Twist","Adjust",1,INIFileName);
   sprintf(szTemp, "%d", LONGSONG_ADJUST);
   WritePrivateProfileString("MQ2Twist","Adjust",szTemp,INIFileName);

   for (int i=0;i<10;i++) {
      sprintf(szSection, "Click_%d", i+10);
      ItemClick.cast_time = GetPrivateProfileInt(szSection,"CastTime",0,INIFileName);
      ItemClick.recast = GetPrivateProfileInt(szSection,"ReCastTime",0,INIFileName);

      GetPrivateProfileString(szSection,"Name","DISABLED",ItemClick.name,MAX_STRING,INIFileName);
      GetPrivateProfileString(szSection,"Slot","DISABLED",ItemClick.slot,MAX_STRING,INIFileName);
      if(!strnicmp("DISABLED", ItemClick.name, 8)) {
         if (!strnicmp("DISABLED", ItemClick.slot, 8)) {
            ItemClick.disabled = true;
            DebugSpew("MQ2Twist: Slot %d disabled",i+1);
         } else {
            ItemClick.nousename = true;
            ItemClick.disabled = false;
         }
      } else ItemClick.disabled = false;
      // Write the values above back to disk, mostly to initialize it for easy editing.
      sprintf(szTemp, "%d", ItemClick.cast_time);
      WritePrivateProfileString(szSection,"CastTime",szTemp,INIFileName);
      // If the CastTime is set to -1 in the INI file, use the default.
      ItemClick.cast_time = ItemClick.cast_time==-1 ? CAST_TIME : ItemClick.cast_time;

      sprintf(szTemp, "%d", ItemClick.recast);
      WritePrivateProfileString(szSection,"ReCastTime",szTemp,INIFileName);
      WritePrivateProfileString(szSection,"Name",ItemClick.name,INIFileName);
      WritePrivateProfileString(szSection,"Slot",ItemClick.slot,INIFileName);
      DebugSpewAlways("Initializing MQ2Twist: Processed %s", szSection);
   }
}


Rich (BB code):
VOID SingCommand(PSPAWNINFO pChar, PCHAR szLine)
{
   CHAR szTemp[MAX_STRING]={0};
   CHAR szMsg[MAX_STRING]={0};
   int i;

   GetArg(szTemp,szLine,1);
   i=atoi(szTemp);

   if (i>=1 && i<=19) { // valid range?
      HoldSong = i;
      bTwist=true;
      CastDue = -1;
      sprintf(szMsg, "MQ2Twist::Holding Twist and casting gem %d", HoldSong);
      WriteChatColor(szMsg,USERCOLOR_DEFAULT);
      MQ2TwistDoCommand(pChar,"/stopsong");
      if (i>10) { //item?
         ItemClick[i-10].castdue = 0;
      } else SongNextCast = 0; //nope, song
   } else WriteChatColor("MQ2Twist::Invalid gem specified, ignoring",USERCOLOR_DEFAULT);
}

VOID StopTwistCommand(PSPAWNINFO pChar, PCHAR szLine)
{
   bTwist=false;
   HoldSong=0;
   MQ2TwistDoCommand(pChar,"/stopsong");
   WriteChatColor("MQ2Twist::Stopping Twist",USERCOLOR_DEFAULT);
}

VOID PrepNextSong() {
   if (CurrSong>NumSongs) {
      if (altTwist) {
         NumSongs=AltNumSongs;
         CurrSong=PrevSong=AltCurrSong;
         for (int i=0; i<NumSongs; i++) Song=AltSong;
         altTwist=false;
         if (!quiet) WriteChatColor("MQ2Twist::One-shot twist ended, normal twist will resume next pulse",USERCOLOR_DEFAULT);
      } else CurrSong=1;
   }
}

VOID DisplayTwistHelp() {
   WriteChatColor("MQ2Twist - Twist song or songs",USERCOLOR_DEFAULT);
   WriteChatColor("Usage:   /twist <gem#> - Twists in the order given.",USERCOLOR_DEFAULT);
   WriteChatColor("  Valid options are 1 thru 9 for song gems, and 10 thru 19 for item clicks.",USERCOLOR_DEFAULT);
   WriteChatColor("  These may be mixed in any order, and repeats are allowable.",USERCOLOR_DEFAULT);
   WriteChatColor("Usage: /twist hold <gem #> - Pause twisting and sing only the specified song",USERCOLOR_DEFAULT);
   WriteChatColor("  /sing <gem#> - alias for /twist hold",USERCOLOR_DEFAULT);
   WriteChatColor("Usage: /twist once <gem#> Twists once in the order given, then reverts to original twist",USERCOLOR_DEFAULT);
   WriteChatColor("Usage: /twist or /twist start - Resume the twist after using /twist hold or /twist stop",USERCOLOR_DEFAULT);
   WriteChatColor("Usage: /twist reset - Reset timers for item clicks and long duration songs",USERCOLOR_DEFAULT);
   WriteChatColor("Usage: /twist delay # - 10ths of a second, minimum of 30, default 33",USERCOLOR_DEFAULT);
   WriteChatColor("Usage: /twist adjust # - in ticks, how early to recast long duration songs",USERCOLOR_DEFAULT);
   WriteChatColor("Usage: /twist stop/end/off - stop twisting, does not clear the twist queue",USERCOLOR_DEFAULT);
   WriteChatColor("  /stoptwist - alias for /twist stop",USERCOLOR_DEFAULT);
   WriteChatColor("Usage: /twist reload - reload the INI file to update item clicks",USERCOLOR_DEFAULT);
   WriteChatColor("Usage: /twist slots - List the slots defined in the INI and their #'s",USERCOLOR_DEFAULT);
}

// ***************************************************************************
// Function:      TwistCommand
// Description:   Our /twist command. sing for me!
// ***************************************************************************
VOID TwistCommand(PSPAWNINFO pChar, PCHAR szLine)
{
   CHAR szTemp[MAX_STRING]={0};
   CHAR szMsg[MAX_STRING]={0};
   CHAR szChat[MAX_STRING]={0};
   PSPELL pSpell;
   int i;

   GetArg(szTemp,szLine,1);

   if (NumSongs && (!strlen(szTemp) || !strnicmp(szTemp,"start", 5))) {
      WriteChatColor("MQ2Twist::Starting Twist",USERCOLOR_DEFAULT);
      DoSwapOut();
      bTwist=true;
      HoldSong=0;
      CastDue = -1;
      return;
   }

   if (!strnicmp(szTemp,"stop", 4) || !strnicmp(szTemp,"end", 3) || !strnicmp(szTemp,"off", 3)) {
      DoSwapOut();
      StopTwistCommand(pChar, szTemp);
      return;
   }

   if (!strnicmp(szTemp,"slots", 5)) {
      WriteChatColor("MQ2Twist 'Song' Numbers for right click effects:",USERCOLOR_DEFAULT);
      for (i=0;i<10;i++) {
         if (ItemClick.disabled) break;
         if (ItemClick.nousename) {
            sprintf(szMsg, "  %d = %s (slot)", i+10, ItemClick.slot);
         } else {
            sprintf(szMsg, "  %d = %s (name) %s (slot)", i+10, ItemClick.name, ItemClick.slot);
       }
         WriteChatColor(szMsg,USERCOLOR_DEFAULT);
      }
      WriteChatColor("---",USERCOLOR_DEFAULT);
      return;
   }

   if (!strnicmp(szTemp,"reload", 6)) {
      WriteChatColor("MQ2Twist::Re-Loading INI Values",USERCOLOR_DEFAULT);
      Load_MQ2Twist_INI();
      return;
   }

   if (!strnicmp(szTemp,"delay", 5)) {
      GetArg(szTemp,szLine,2);
      if (strlen(szTemp)>0) {
         i=atoi(szTemp);
         if (i<=30) {
            WriteChatColor("MQ2Twist::WARNING delay specified is less than standard song cast time",CONCOLOR_RED);
         }
         CAST_TIME=i;
         Update_INIFileName();
         WritePrivateProfileString("MQ2Twist","Delay",itoa(CAST_TIME, szTemp, 10),INIFileName);
         sprintf(szMsg, "MQ2Twist::Set delay to %d, INI updated", CAST_TIME);
      } else sprintf(szMsg, "MQ2Twist::Delay %d", CAST_TIME);
      WriteChatColor(szMsg,USERCOLOR_DEFAULT);
      return;
   }

   if (!strnicmp(szTemp,"quiet", 5)) {
      quiet=!quiet;
      sprintf(szTemp,"%d",quiet);
      WritePrivateProfileString("MQ2Twist","Quiet",szTemp,INIFileName);
      sprintf(szMsg,"MQ2Twist::Now being %s",quiet ? "quiet" : "noisy");
      WriteChatColor(szMsg,USERCOLOR_DEFAULT);
      return;
   }

   if (!strnicmp(szTemp,"adjust", 6)) {
      GetArg(szTemp,szLine,2);
      if (strlen(szTemp)>0) {
         i=atoi(szTemp);
         LONGSONG_ADJUST=i;
         Update_INIFileName();
         WritePrivateProfileString("MQ2Twist","Adjust",itoa(LONGSONG_ADJUST, szTemp, 10),INIFileName);
         sprintf(szMsg, "MQ2Twist::Long song adjustment set to %d, INI updated", LONGSONG_ADJUST);
      } else sprintf(szMsg, "MQ2Twist::Long song adjustment: %d", LONGSONG_ADJUST);
      WriteChatColor(szMsg,USERCOLOR_DEFAULT);
      return;
   }

   if (!strnicmp(szTemp,"hold", 4)) {
      GetArg(szTemp,szLine,2);
      SingCommand(pChar, szTemp);
      return;
   }

   if (!strnicmp(szTemp,"reset", 5)) {
      Reset_ItemClick_Timers();
      WriteChatColor("MQ2Twist::Timers reset",CONCOLOR_YELLOW);
      return;
   }

   // check help arg, or display if we have no songs defined and /twist was used
   if (!strlen(szTemp) || !strnicmp(szTemp,"help", 4)) {
      DisplayTwistHelp();
      return;
   }

   // if we are "one-shot twisting", save the current song array and current song
   if (!strnicmp(szTemp,"once", 4)) {
      WriteChatColor("MQ2Twist one-shot twisting:",CONCOLOR_YELLOW);
      if (altTwist) {
         CurrSong=NumSongs+1;
         PrepNextSong(); // If CurrSong > NumSongs relaod the song list
      }
      if (NumSongs) {
         AltNumSongs=NumSongs;
         AltCurrSong=CurrSong;
         for (i=0; i<NumSongs; i++) AltSong=Song;
      }
      altTwist=true;
   } else altTwist=false;



I hope the other compilers on this site will use this as well in their next updates as it helps us raiding bards out a lot.
 
Last edited:
Ok. Had to make it two posts to fit all the code.

Rich (BB code):
   DoSwapOut();
   DebugSpew("MQ2Twist::TwistCommand Parsing twist order");
   NumSongs=0;
   HoldSong=0;
   if (!altTwist) {
      if (!quiet) {
         WriteChatColor("MQ2Twist Twisting:",CONCOLOR_YELLOW);
      } else WriteChatColor("MQ2Twist::Starting Twist",USERCOLOR_DEFAULT);
   }
   for (i=0 + altTwist ? 1 : 0; i<20; i++)
   {
      GetArg(szTemp,szLine,i+1);
      if (!strlen(szTemp))  break;

      Song[NumSongs]=atoi(szTemp);
      if (Song[NumSongs]>=1 && Song[NumSongs]<=19) {
         if ((Song[NumSongs]>9) && ItemClick[Song[NumSongs]-10].disabled) {
            sprintf(szChat, " Undefined item specified (%s) - ignoring (see INI file)", szTemp);
            WriteChatColor(szChat,CONCOLOR_RED);
         } else {
            sprintf(szMsg, " %s - ", szTemp);

            if (Song[NumSongs]<=9) {
               pSpell=GetSpellByID(GetCharInfo2()->MemorizedSpells[Song[NumSongs]-1]);
               if (altTwist) SongNextCast[NumSongs] = 0;
               if (pSpell) strcat(szMsg, pSpell->Name);
            } else {
               if (ItemClick[Song[NumSongs]-10].nousename) {
                  strcat(szMsg, ItemClick[Song[NumSongs]-10].slot);
               } else {
                  strcat(szMsg, ItemClick[Song[NumSongs]-10].name);
               }
               if (altTwist) ItemClick[NumSongs].castdue = 0;
            }
            if (!quiet) WriteChatColor(szMsg,COLOR_LIGHTGREY);
            NumSongs++;
         }
      } else {
         sprintf(szChat, " Invalid gem specified (%s) - ignoring", szTemp);
         WriteChatColor(szChat,CONCOLOR_RED);
      }
   }

   sprintf(szTemp, "Twisting %d song%s", NumSongs, NumSongs>1 ? "s" : "");
   if (!quiet) WriteChatColor(szTemp,CONCOLOR_YELLOW);

   if (NumSongs>0) bTwist=true;
   CurrSong = 1;
   PrevSong = 1;
   CastDue = -1;
   MQ2TwistDoCommand(pChar,"/stopsong");
   if (!altTwist) Reset_ItemClick_Timers();
}

/*
Checks to see if character is in a fit state to cast next song/item

Note 1: Do not try to correct SIT state, or you will have to stop the
twist before re-memming songs

Note 2: Since the auto-stand-on-cast bullcrap added to EQ a few patches ago,
chars would stand up every time it tried to twist a song.  So now
we stop twisting at sit.
*/
BOOL CheckCharState() {
   if (!bTwist) return FALSE;

   if (GetCharInfo()) {
      if (GetCharInfo()->Stunned==1) return FALSE;
      switch (GetCharInfo()->standstate) {
       case STANDSTATE_SIT:
          WriteChatColor("MQ2Twist::Stopping Twist",USERCOLOR_DEFAULT);
          bTwist = FALSE;
          return FALSE;
          break;
       case STANDSTATE_FEIGN:
          MQ2TwistDoCommand(NULL,"/stand");
          return FALSE;
          break;
       case STANDSTATE_DEAD:
          WriteChatColor("MQ2Twist::Stopping Twist",USERCOLOR_DEFAULT);
          bTwist = FALSE;
          return FALSE;
          break;
       default:
          break;
      }
   }

   if (pCastingWnd) {
      PCSIDLWND pCastingWindow = (PCSIDLWND)pCastingWnd;
      if (pCastingWindow->Show == 1) return FALSE;
      // Don't try to twist if the casting window is up, it implies the previous song
      // is still casting, or the user is manually casting a song between our twists
   }
   return TRUE;
}
class MQ2TwistType *pTwistType=0;

class MQ2TwistType : public MQ2Type
{
public:
   enum TwistMembers
   {
      Twisting=1,
      Next=2,
      Current=3,
      List=4,
   };

   MQ2TwistType():MQ2Type("twist")
   {
      TypeMember(Twisting);
      TypeMember(Next);
      TypeMember(Current);
      TypeMember(List);
   }
   ~MQ2TwistType()
   {
   }

   bool GetMember(MQ2VARPTR VarPtr, PCHAR Member, PCHAR Index, MQ2TYPEVAR &Dest)
   {
      PMQ2TYPEMEMBER pMember=MQ2TwistType::FindMember(Member);
      if (!pMember)
         return false;
      switch((TwistMembers)pMember->ID)
      {
      case Twisting:
         /* Returns: bool
            0 - Not Twisting
            1 - Twisting
         */
         Dest.Int=bTwist;
         Dest.Type=pBoolType;
         return true;
      case Next:
         /* Returns: int
            0 - Not Twisting
            -1 - Casting Item
            1-9 - Current Gem
         */
         Dest.Int=HoldSong ? HoldSong : Song[CurrSong-1];
         if (Dest.Int>9) Dest.Int = -1;
         if (!bTwist) Dest.Int = 0;

         Dest.Type=pIntType;
         return true;
      case Current:
         Dest.Int=HoldSong ? HoldSong : Song[PrevSong-1];
         if (Dest.Int>9) Dest.Int = -1;
         if (!bTwist) Dest.Int = 0;

         Dest.Type=pIntType;
         return true;
      case List:
         /* Returns: string
            Space separated list of gem and item #'s being twisted, in order
         */
         int a;
         CHAR szTemp[MAX_STRING] = {0};

         MQ2TwistTypeTemp[0] = 0;
         for (a=0; a<NumSongs; a++) {
            sprintf(szTemp, "%d ", Song[a]);
            strcat(MQ2TwistTypeTemp, szTemp);
         }

         Dest.Ptr=&MQ2TwistTypeTemp[0];
         Dest.Type=pStringType;
         return true;
      }
      return false;
   }

   bool ToString(MQ2VARPTR VarPtr, PCHAR Destination)
   {
      if (bTwist)
         strcpy(Destination,"TRUE");
      else
         strcpy(Destination,"FALSE");
      return true;
   }

   bool FromData(MQ2VARPTR &VarPtr, MQ2TYPEVAR &Source)
   {
      return false;
   }
   bool FromString(MQ2VARPTR &VarPtr, PCHAR Source)
   {
      return false;
   }
};

BOOL dataTwist(PCHAR szName, MQ2TYPEVAR &Dest)
{
   Dest.DWord=1;
   Dest.Type=pTwistType;
   return true;
}

Rich (BB code):
// ******************************
// **** MQ2 API Calls Follow ****
// ******************************

PLUGIN_API VOID InitializePlugin(VOID)
{
   DebugSpewAlways("Initializing MQ2Twist");

   AddCommand("/twist",TwistCommand,0,1,1);
   AddCommand("/sing",SingCommand,0,1,1);
   AddCommand("/stoptwist",StopTwistCommand,0,0,1);;
    AddMQ2Data("Twist",dataTwist);

    pTwistType = new MQ2TwistType;

}

PLUGIN_API VOID ShutdownPlugin(VOID)
{
   DebugSpewAlways("MQ2Twist::Shutting down");

   RemoveCommand("/twist");
   RemoveCommand("/sing");
   RemoveCommand("/stoptwist");
    RemoveMQ2Data("Twist");

    delete pTwistType;
}

PLUGIN_API VOID OnPulse(VOID)
{
   CHAR szTemp[MAX_STRING] = {0};
   PSPELL pSpell;
   int a,b;

   if (!MQ2TwistEnabled || !CheckCharState()) return;

   if ((HoldSong>0) || ((NumSongs==1) && !altTwist)) {
      // DebugSpew("MQ2Twist::Pulse - Single Song");
      if ( CastDue<0 || ( ((CastDue-GetTime()) <= 0 ) && (GetCharInfo()->pSpawn->CastingSpellID == -1) ) ) {
         int SongTodo = HoldSong ? HoldSong : Song[0];
         if (SongTodo <= 9) {
            DebugSpew("MQ2Twist::Pulse - Single Song (Casting Gem %d)", SongTodo);
            sprintf(szTemp,"/multiline ; /stopsong ; /cast %d", SongTodo);
            MQ2TwistDoCommand(NULL,szTemp);
            CastDue = GetTime()+CAST_TIME;
         } else {
            if (ItemClick[SongTodo-10].castdue-GetTime() <= 0) {
               if (ItemClick[SongTodo-10].nousename) {
                  DebugSpew("MQ2Twist::Pulse - Single Song (Casting Item %d - %s)", SongTodo, ItemClick[SongTodo-10].slot);
                  sprintf(szTemp,"/multiline ; /stopsong ; /itemnotify %s rightmouseup", ItemClick[SongTodo-10].slot);
               } else {
                  DebugSpew("MQ2Twist::Pulse - Single Song (Casting Item %d - %s)", SongTodo, ItemClick[SongTodo-10].name);
                  DoSwapIn(SongTodo-10);
                  sprintf(szTemp,"/multiline ; /stopsong ; /itemnotify ${FindItem[%s].InvSlot.Name} rightmouseup", ItemClick[SongTodo-10].name);
               }
               MQ2TwistDoCommand(NULL,szTemp);
               ItemClick[SongTodo-10].castdue = ItemClick[SongTodo-10].recast ? (GetTime()+ItemClick[SongTodo-10].cast_time+ItemClick[SongTodo-10].recast) : (GetTime()+CAST_TIME);
               CastDue = ItemClick[SongTodo-10].castdue;
            }
         }
      }
   } else {
      int SongTodo = Song[CurrSong-1];
      if (NumSongs && ((CastDue-GetTime()) <= 0)) {
         DoSwapOut();
         if (SongTodo <= 9) {
            if (SongNextCast[CurrSong-1]-GetTime() <= 0) {
               DebugSpew("MQ2Twist::OnPulse - Next Song = %s", szTemp);
               sprintf(szTemp,"/multiline ; /stopsong ; /cast %d", SongTodo);
               MQ2TwistDoCommand(NULL,szTemp);
               pSpell=GetSpellByID(GetCharInfo2()->MemorizedSpells[Song[CurrSong-1]-1]);
               if(!pSpell) {
                  WriteChatColor("Songs not present - suspending twist.  /twist to resume",CONCOLOR_RED);
                  bTwist = FALSE;
                  return;
               }
               a = pSpell->RecastTime/100;                     // recasttime in 10's of a second 
               b = GetSpellDuration(pSpell,GetCharInfo()->pSpawn) * 60;   // duration in 10's of a second
               if (pSpell->DurationType == 5 && !pSpell->DurationValue1) {
                  b = 18;   //FIXME - Remove once GetSpellDuration handles duration type5
               }
               CastDue = GetTime()+CAST_TIME;
                  // duration > 18 secs
    if (b > 180) SongNextCast[CurrSong-1] = CastDue + b - (LONGSONG_ADJUST*60);

    // recast > 0 secs
    else if (a > 0) SongNextCast[CurrSong-1] = CastDue + a;

    // normal
    else SongNextCast[CurrSong-1] = CastDue; 

               PrevSong=CurrSong;
            } // if it's not time for currsong to be re-sung, skip it in the twist
            CurrSong++;
            PrepNextSong();
         } else {
            if (ItemClick[SongTodo-10].castdue-GetTime() <= 0) {
               if (ItemClick[SongTodo-10].nousename) {
                  DebugSpew("MQ2Twist::Pulse - Next Song (Casting Slot %d - %s)", SongTodo, ItemClick[SongTodo-10].slot);
                  sprintf(szTemp,"/multiline ; /stopsong ; /itemnotify %s rightmouseup", ItemClick[SongTodo-10].slot);
               } else {
                  DebugSpew("MQ2Twist::Pulse - Next Song (Casting Item %d - %s)", SongTodo, ItemClick[SongTodo-10].name);
                  DoSwapIn(SongTodo-10);
                  sprintf(szTemp,"/multiline ; /stopsong ; /itemnotify ${FindItem[%s].InvSlot.Name} rightmouseup", ItemClick[SongTodo-10].name);
               }
               MQ2TwistDoCommand(NULL,szTemp);
               ItemClick[SongTodo-10].castdue = ItemClick[SongTodo-10].recast ? (GetTime()+ItemClick[SongTodo-10].cast_time+ItemClick[SongTodo-10].recast) : (GetTime()+CAST_TIME);
               CastDue = GetTime()+ItemClick[SongTodo-10].cast_time;
            }
            PrevSong=CurrSong;   // Increment twist position even if we didn't do an itemnotify - this might have a long recast
            CurrSong++;         // interval set, and we just skip it until it's time to recast, rather than keep a separate timer.
            PrepNextSong();
         }
      }
   }
}

PLUGIN_API DWORD OnIncomingChat(PCHAR Line, DWORD Color)
{
   if (!bTwist || !MQ2TwistEnabled) return 0;
   // DebugSpew("MQ2Twist::OnIncomingChat(%s)",Line);

   if ( !strcmp(Line,"You miss a note, bringing your song to a close!") ||
      !strcmp(Line,"You haven't recovered yet...") ||
      !strcmp(Line,"Your spell is interrupted.") ) {
         DebugSpew("MQ2Twist::OnIncomingChat - Song Interrupt Event");
         if (!HoldSong) CurrSong=PrevSong;
         CastDue = -1;
         SongNextCast[CurrSong-1] = -1;
         return 0;
      }

   if (!strcmp(Line,"You can't cast spells while stunned!") ) {
      DebugSpew("MQ2Twist::OnIncomingChat - Song Interrupt Event (stun)");
      if (!HoldSong) CurrSong=PrevSong;
      CastDue = GetTime() + 10;
      // Wait one second before trying again, to avoid spamming the trigger text w/ cast attempts
      return 0;
   }
   return 0;
}

PLUGIN_API VOID SetGameState(DWORD GameState)
{
   DebugSpew("MQ2Twist::SetGameState()");
   if (GameState==GAMESTATE_INGAME)
   {
      MQ2TwistEnabled = true;
      Load_MQ2Twist_INI();
   } else {
      MQ2TwistEnabled = false;
   }
}

Make sure to include all 5 parts of the code in 1 file for the plugin.
 
Last edited:
You're a fucking lifesaver... I never understood why Dryads would be skipped so often.

I don't throw around many red cents, but you got one from me.
 
Not a clue, may just be the compiler I used. The source is only about 5-10 lines shorter than the original with the fix in it, so I don't think that that makes much of a difference. I use Microsoft Visual Studio 6.0.
 
I'm not very plugin-savvy, but seeing a plugin half the size that I'm used to kinds of throws up red flags for me... lol probably just me being paranoid...

Can anyone take a quick look at the source and give a thumbs up or thumbs down, please?
 
I looked at my old compile and the MQ2Twist.dll is 104 kb, the one I posted is 92.1 kb. Not a huge difference, and like I said, it's 10 or so lines of code shorter. Anyway, the best I can do is promise you it's not a Trojan or anything as I'm guessing that's what you're thinking. But I'm sure someone else can confirm this as well. =)
 
The one I had was more like 192kb ... lol

Either way, I moved it into my MQ folder. I probably won't have a chance to test it out today (damn homework), but hopefully I'll be able to give it a test run sometime this weekend.
 
Heya, this twist seems to work fine.

To the size, there is alot of explanatory text in it, when ya cut that out it would be even smaler hehe :)

2 Problems I have with twist, the old and this. Looks like when ya find the cause you fix both with.
First my bard keeps trying to twist even when dead, and on recieveing a Rez he is crashing to Char or serverselect therefor if I dont stop the twist manually first before takeing the rez. On the opposite all my other NON-Bard chars are getting spammed in the MQ window on death, with the Line
"Died. Stopping twist."
(Didnt write it down but thats what it says approx)

Anyway RC comming for your fix so far, hopeing you can fix the rest too.

Thx Crystane.
 
OK I DID not come up with this Thez did and offered it in another thread but I will drop it in here and see if someone can complile and test it


Rich (BB code):
PLUGIN_API VOID OnPulse(VOID)
{
   CHAR szTemp[MAX_STRING] = {0};
   PSPELL pSpell;
   int a,b;

if (IsWindowOpen("RespawnWnd")) {
      DoSwapOut();
      bTwist=false;
      HoldSong=0;
      MQ2TwistDoCommand(pChar,"/stopsong");
      WriteChatColor("You died! Stopping twist!",USERCOLOR_DEFAULT);
      return;
}

   if (!MQ2TwistEnabled || !CheckCharState()) return;

   if ((HoldSong>0) || ((NumSongs==1) && !altTwist)) {
 
If JMO posts a diff of this, I'll add the changes into RQ...don't have the time to go through and get one myself, though.

*edit* NM, found it on the MQ2 boards, applying it to the code.
 
I hate to be a spoilsport.. but Dryads has a recast time of 9 min. So even if you are "re-singing" , you are probly not RECASTING the song. -- no matter WHAT you do to MQ2Twist.
 
No it doesn't. Check your sources. Dryads is 9 Sec. recast or so, not 9 minutes. It is always up and ready after the other 3 songs have played.
 
Sum1 said:
Was this the version of MQ2Twist used in the redguides compile this time around?
It wasn't. Sorry about that. The good news is I released a new RedQuest compile a few minutes ago that contains this MQ2Twist fix. Thanks for bringing it to our attention and sorry we didnt get to it sooner.

--RQ
 
Hi!

first time trying a bard and im trying this MQ2Twist plugin.
Anyone have any tips on how i should do?

Right now all i have done is 3 hotkeys

1 is: /twist stop
2 is: /twist start
3 is: /twist 8 1

is this how i should do it? or can i add alot of stuff in only 1 hotkey?
i used macro before but thinked of changing to MQ2Twist but right now it feels worst the macro 2 songs in 1 hotkey

Thanks
 
You don't need the /twist start. that command is used for restarting your twist order after you have issued a /twist stop. It remembers the twist ordrer after stopping.

1 is: /twist stop
2 is: /stop song
3 is: /twist 8 1
 
what does /stop song do? just stop the curent order or just stop the curent song?
If i want to add more song i just do /twist 1 2 3 4 ?
 
/stop song is the legit EQ command to stop any song in case you just clicked on med song or selos while running around.
If i want to add more song i just do /twist 1 2 3 4 ? and yes that is correct
 
k, one more question
i want selos to be cast each 9 sec, how do i do that? i want it to stop twisting those song i have on 1 twisting macro and then cast selo before i goes out
 
k, one more question
i want selos to be cast each 9 sec, how do i do that? i want it to stop twisting those song i have on 1 twisting macro and then cast selo before i goes out

Twist knows that it's Selos and will only re-cast it before it wears off. My kiting macro used to be /twist 8 1 2 3 4 with 1-4 being my Dots and 8 being Selos. This way, it would start me off with the speed buff, then keep dotting the mob. Right before Selos was about to wear off, it would cast it again to keep it up. It's a very smart plugin like that. =)
 
He is playing on p99 and is like like level 6 so selos is not the 2 min version but even so JMO is correct. Mq2twist does keep track of song durations.
 
oki, most be that im low lvl and it recast everthing again becaus its low duration on it
right now my twist macro is /twist 9 1 2 3 this with DoT and DD and Regen
 
MQ2Twist - This plugin automates the twisting of bard songs.
SYNTAX for MQ2Twist

Commands:

Rich (BB code):
/twist # # # # #
Twists the songs/items in the order specified (up to 10 can be specified).
Valid options are 1 thru NUM_SPELL_GEMS (EQData.h) for song gems, and 21 thru 29 for item clicks. These may be mixed in any order, and repeats are allowable. If a song is specified with a duration longer than standard (eg. Selo's Accelerating Chorus), that song will be twisted in based on it's duration. For example, riz+mana+selos

           
	
	





	



	











Rich (BB code):
would be a 2 song twist with selos pulsed every 2.5 min.
/twist once # # # # # Twists in songs once the order given, then reverts back to original twist /twist hold # Pause twisting and sing only the specified song /twist stop/end/off Stop twisting, does not clear the twist queue /twist or /twist start Resume the twist after using /twist hold or /twist stop /twist reset Reset timers for item clicks and long duration songs /twist delay # Specify the delay to be used before starting a new song (in 10ths of a second). The minimum is 30, default is 33 /twist adjust # How early to recast long duration songs (in ticks) /twist reload Reload the INI file to update item clicks /twist slots List the slots/items defined in the INI and their item numbers /twist quiet Toggles songs listing and start/stop messages for one-shot twists /sing Alias for /twist hold /stoptwist Alias for /twist stop

Examples:

Rich (BB code):
/twist 1           Sing gem 1 forever 
/twist 1 2 3       Twist gems 1,2, and 3 forever 
/twist 1 2 3 10    Twist gems 1,2,3, and clicky 10, forever 
/twist hold 4      Sing gem 4 forever, until another singing-related /twist command is given
/sing 4            Same as above

/twist set 16 32 120 "Cassindra's Chorus of Clarity" AA

Rich (BB code):
 Set Click_16 to CastTime=32, ReCastTime=120, Name="Cassindra's Chorus of Clarity", Slot=AA ; and save to INI

INI File:

Rich (BB code):
[MQ2Twist] 
Delay=32                 Delay between twists (in 10ths of a second). Lag & System dependant. 
Adjust=1                 This defines  how many ticks before the 'normal' 
                         recast time to cast a long song.

Long songs are defined as songs greater than 3 ticks in length. If set to 1 tick, and a song lasts 10 ticks, the song will be recast at the 8 tick mark, instead of at the 9 tick mark as it normally would.

Rich (BB code):
[Click_21] through [Click_29] 
CastTime=30              Casting Time, -1 to use the normal song delay 
ReCastTime=0             How often to recast, 0 to twist normally. 
Name="Fife of Battle"    Item name for /itemnotify 
Slot=neck                Slot name for /itemnotify

Delay, CastTime and ReCastTime are specified in 10ths of a second (so 10 = 1 second)
The INI file allows you to specify items by name (with Name=<itemname>), or by inventory slot (with Slot=<slotname>).
If both a Name and Slot are defined for an item, the plugin will attempt to swap the item into that slot using the /exchange command. After casting, it will replace the original item.
If only Name or Slot are specified, "/itemnotify Slot rightmouseup" is used to perform item clicks.

item click method
Rich (BB code):
MQ2Twist uses /itemnotify slotname rightmouseup to perform item clicks.

Rich (BB code):
 The INI file allows you to specify items by name (with name=itemname), or by
      inventory slot (with slot=slotname).  If both a name and slot are defined for an
      item, the plugin will attempt to swap the item into that slot (via the /exchange
      command) and replace the original item when casting is complete.
    
      The example INI file below contains examples of the types of usage.

ini file example
Rich (BB code):
  [MQ2Twist]
            Delay=31
            Quiet=0

            ;Shadowsong cloak
            [Click_21]
            CastTime=30
            ReCastTime=350
            Name=Shadowsong Cloak
            Slot=DISABLED

            ;girdle of living thorns (current belt will be swapped out)
            [Click_22]
            CastTime=0
            ReCastTime=11600
            Name=Girdle of Living Thorns
            Slot=waist

            ;nature's melody
            [Click_23]
            CastTime=-1
            ReCastTime=135
            Name=DISABLED
            Slot=mainhand

            ;lute of the flowing waters
            [Click_24]
            CastTime=0
            ReCastTime=0
            Name=Lute of the Flowing Waters
            Slot=DISABLED

            ;lute of the flowing waters
            [Click_25]
            CastTime=32
            ReCastTime=120
            Name=Cassindra's Chorus of Clarity
            Slot=AA

         [Click_26] ... [Click_30]
            CastTime=33
            ReCastTime=0
            Name=DISABLED
            Slot=DISABLED
 
Using Twist to save me the hassle of restarting melody after interruptions/stuns meaning I seldom get to the end of long twists. Quite often if I try to mez, it starts then MQ2Twist stops and carry on, annoying and possible fatal! When I click items like the tash clicky or epic it does the same.

Anyway to stop this? I'm guessing not, just hoping someone found a way
 
@ctaylor22 reported this issue, i'm wondering if anyone can nail it down further:


  • MQ2Twist Problem when casting items. When using /twist # # # # and you issue a /twist off command and then use an /useitem SomeItem command. MQ2Twist interrupts the casting of the item and primarily issues this command. MQ2Twist::One-shot twist ended, normal twist will resume next pulse. It will also interrupt casting Items with just an Interrupted message.
  • Running kissassist Set up a bard with a normal twist and in the buffs section setup to cast Songblade of the Eternal. MQ2Twist will continually interrupt the casting of the item even though twist is off and the /useitem command is being used outside to twist.
 
Great plugin, seems like no updates in quite a while though? I would love an option for /twist next to queue a song rather than /twist once which interrupts the rotation
 
Plugin - MQ2Twist

Users who are viewing this thread

Back
Top