• You've discovered RedGuides, an EverQuest multi-boxing and scripting community 🧙‍♀️⚙️. We want you to play several EQ characters at once, come join us and say hello! 👋

  • A TLP without truebox has thawed (Very Vanilla ready)
    Frostreaver

/varsetEval plugin idea - The macroquest language, auto-eval,l and dpsifs

Joined
May 22, 2015
RedCents
3,467¢
Background
So, my opinion of the macroquest language is kind low, it's this nasty thing that evolved from eq /commands. I guess it's combination of the EQ /command engine, and evolving over time.

However, it's what we have and there is huge base of macros and and scripts using it. And it's firmly entrenched, and the already existing effort to replace it seems to have only limited success.

The problem
The language pretty much autoevals everything
In another langage you could pretty easily do something like
/varset expression "\${$MyVar}"
/varsetEval evalValue ${Expression}

But in macroquest it seems ridiculously hard

The idea
Create an eval plugin, this would pretty easy I think

Example code use
Rich (BB code):
/declare MyExpression string local "(#{Target.Named} AND #{Target.PctHp} > 20) OR ${Me.XTarget} > 2"
/declare Result bool local 
/varsetEval Result ${MyExpression}

/varsetEval would be provided by plugin and do a pretty simple string replace (# -> $), (AND -> &&), (OR -> ||) and execute
/varvar Result stringReplacedExpression

Then you do stuff like
Rich (BB code):
DPS1=Improved Twincast|90|(#{Target.Named} AND #{Target.PctHp} > 20) OR ${Me.XTarget} > 2

This seems so easy I feel like I'm missing something.
I've been seeing if I could do something like this with /noparse, but seems really difficult.

Why isn't it easier to do this? Does this plugin make sense?
 
i made this plugin, it is called mq2ifs. it is on all 3 sites available for use and there is a version of kiss that uses it.
 
Yeah, i've taken a look at kiss-ifs and looks cool and promising, but the purist in me thinks there must be a better way...
 
It is the easiest way via plugin. otherwise you just /noparse it. But you cant /noparse an ini because when you do it would access the ini every time anyway, which is what mq2ifs bypasses.

/noparse /declare this string outer "${If[${Target.Named} && ${Target.PctHPs}<50,1,0]}"

then in game:
/echo ${this} will return TRUE or FALSE based on whatever it really is.

but if you do it to an ini:
/noparse declare this string outer "${Ini[file,section,key,default]}"

That means every time you did:
/echo ${this}
That would access the ini and if you do it enough it lags the crap out of your system.
 
the ifs version of kiss is the most powerful macro I've seen but not enough use it so you have to do all the inis yourself for it. What I want it to do is work with mez so I can tell it to only use certain mezes in certain situations. Limiting an enchanter to 2 mez spells is just wrong.
 
I'm kind of heads down updating/fixing MQ2Twist atm and modding Kiss to use a few new goodies in MQ2Twist.

Hopefully in a week I can get back to this and proof it out.
 
I'm kind of heads down updating/fixing MQ2Twist atm and modding Kiss to use a few new goodies in MQ2Twist.

Hopefully in a week I can get back to this and proof it out.
Ya I loved mq2twist but it had some issues I didn't like so I wrote my own version internally in mq2bot to make it better and that uses /bottwist. I also had to add some TLO members to do a little more info that I wanted than what mq2twist could provide. If you are in poking around you might also want to consider using some of them:
-Bot.Twisting - bool <-- am I twisting using /bottwist?
-Bot.TwistList - string <-- string of songs I am twisting
-Bot.Twist[x] - spelltype <-- song at spot X in my twist list
-Bot.Twist[Name] - int <-- first found spot that specific song name is located in my twist
-Bot.TwistCurrent - int <-- currently twisting song
-Bot.TwistNext - int <-- next song in twist
-Bot.TwistLast - int <-- last song in twist
 
Cool, I've a Twist.Once (boolean) Is code MQ2Bot public?

I've got it MQ2Twist nearly usable, going to do my Kiss changes I think then work on my bard song scheduler, tentatively calling it MQ2Maestro
 
It is my pet project and not public, however, here is all the relevant twist code for you to see.

TLO related:
Rich (BB code):
Twist = 89,
TwistList = 90,
Twisting = 91,
TwistLast = 92,
TwistCurrent = 93,
TwistNext = 94,

		TypeMember(Twist);
		TypeMember(TwistList);
		TypeMember(Twisting);
		TypeMember(TwistLast);
		TypeMember(TwistCurrent);
		TypeMember(TwistNext);

			case Twist:
				if (ISINDEX())
				{
					if(ISNUMBER())
					{
						DWORD Count=GETNUMBER();
						if (Count>=vTwist.size()||Count==0)
							return false;
						Dest.Ptr=vTwistSpell.at(Count-1);
						Dest.Type=pSpellType;
						return true;
					}
					if(!ISNUMBER())
					{
						for(int i=0;i<vTwistSpell.size();i++)
						{
							if(strstr(vTwistSpell.at(i)->Name,GETFIRST()))
							{
								Dest.DWord=i+1;
								Dest.Type=pIntType;
								return true;
							}
						}
					}
				}
				return false;
			case TwistList:
				if(!vTwist.size())
					return false;
				Dest.Ptr=twistList;
				Dest.Type=pStringType;
				return true;
			case Twisting:
				Dest.DWord=twisting;
				Dest.Type=pBoolType;
				return true;
			case TwistLast:
				if(!vTwist.size())
					return false;
				Dest.DWord=songPrev+1;
				Dest.Type=pIntType;
				return true;
			case TwistCurrent:
				if(!vTwist.size())
					return false;
				Dest.DWord=songCurrent+1;
				Dest.Type=pIntType;
				return true;
			case TwistNext:
				if(!vTwist.size())
					return false;
				Dest.DWord=songNext+1;
				Dest.Type=pIntType;
				return true;

Command related:
Rich (BB code):
AddCommand("/bottwist", BotTwistCommand);
RemoveCommand("/bottwist");

void BotTwistCommand(PSPAWNINFO pChar,PCHAR szLine)
{
	if (!InGameOK())
		return;
	int songCount = 0;
	char song[MAX_STRING]="";
	bool once=false;
	if(!IsNumber(GetArg(song,szLine,1)))
	{
		if(GetArg(song,szLine,1))
			if(strstr(strlwr(szLine),"off"))
			{
				EzCommand("/stopsong");
				vTwist.clear();
				vTwistSpell.clear();
				sprintf(twistList,"");
				twisting=false;
				return;
			}
		if(GetArg(song,szLine,1))
			if(strstr(strlwr(szLine),"once"))
			{
				EzCommand("/stopsong");
				once=true;
				strlwr(song);
				
				char songOnce[MAX_STRING];
				GetArg(song,szLine,1,FALSE,FALSE,FALSE,'e ');
			}
	}
	if(!once)
	{
		twisting=true;
		vTwist.clear();
		vTwistSpell.clear();
		sprintf(twistList,"");
	}
	char test[MAX_STRING]="";
	for (int i = 1; i <= strlen(szLine); i++)
	{
		if(strlen(GetArg(song,szLine,i)))
			if(atoi(song)<=12 && atoi(song)>0)
			{
				PSPELL pSpell=GetSpellByID(GetCharInfo2()->MemorizedSpells[atoi(song)-1]);
				if(pSpell)
				{
					if(!once)
					{
						vTwist.push_back(atoi(song));
						vTwistSpell.push_back(pSpell);
						WriteChatf("%s",pSpell->Name);
					}
					else
					{
						EzCommand("/stopsong");
						sprintf(song,"/cast %d",atoi(song));
						EzCommand(song);
						TwistTimer=MQGetTickCount64()+3300;
					}
					
				}
			}	
	}
	if(vTwist.size())
	{
		songCurrent=0;
		songPrev=0;
		songNext=0;
	}
}

Function related:
Rich (BB code):
vector<int> vTwist;
vector<PSPELL> vTwistSpell;
ULONGLONG TwistTimer=0,SpellTimer = 0;
int songCurrent=0,songNext=0,songPrev=0;

void CheckTwist()
{
	if (!InGameOK())
		return;
	if (GetCharInfo()->pSpawn->StandState == STANDSTATE_SIT || MQGetTickCount64()<TwistTimer || MQGetTickCount64()<SpellTimer || !GetCharInfo()->pSpawn || (GetCharInfo()->pSpawn->CastingData.SpellETA - GetCharInfo()->pSpawn->TimeStamp)>0 && (GetCharInfo()->pSpawn->CastingData.SpellETA - GetCharInfo()->pSpawn->TimeStamp)<30000 && !BardClass)
		return;
	if((GetCharInfo()->pSpawn->CastingData.SpellSlot==255?0:GetCharInfo()->pSpawn->CastingData.SpellSlot+1)==13)
		return;
	char song[MAX_STRING];
	if (songNext > 0x0F)
		songNext = 0;
	if (songCurrent >= vTwistSpell.size())
	{
		songCurrent = 0;
		songNext = 1;
	}
	if(GetCharInfo2()->MemorizedSpells[songNext] != 0xFFFFFFFF||vTwistSpell.at(songCurrent)->Mana<GetCharInfo()->pSpawn->ManaCurrent) //check the < vs >
	{
		if(GetSpellGemTimer(songNext))
		{
			if(songNext+1>=vTwist.size())
				songNext=0;
			else
				songNext++;
			return;
		}
		else
		{
			//if(vTwistSpell.at(songCurrent)->Mana==0)
			//if((GetCharInfo()->pSpawn->CastingData.SpellSlot==255?0:GetCharInfo()->pSpawn->CastingData.SpellSlot+1)>0)
				EzCommand("/stopsong");
			sprintf(song,"/cast %d",vTwist.at(songNext));
			EzCommand(song);
			sprintf(song,"${Me.Gem[%d].MyCastTime}",vTwist.at(songNext));
			ParseMacroData(song);
			TwistTimer=MQGetTickCount64()+(atof(song)*1000)+200;
			songPrev=songCurrent;
			songCurrent=songNext;
			if(songNext+1>=vTwist.size())
				songNext=0;
			else
				songNext++;
			//WriteChatf("P: %s - C: %s - N: %s",vTwistSpell.at(songPrev)->Name,vTwistSpell.at(songCurrent)->Name,vTwistSpell.at(songNext)->Name);
		}
	}
	else
	{
		if(songNext+1>=vTwist.size())
			songNext=0;
		else
			songNext++;
	}

}
 
Got this original late eval idea working without plugin!

Still a bit rough, but get the idea
Rich (BB code):
| -------------------------------------------------------------------------------------
| Winnower mod
| SUB: LateEval
| argument kissPart - the kisspart argument to eval
|  
| example:
|   /call LateEval "eval:#{Target.Named} OR #{Me.XTarget} >=3"
| -------------------------------------------------------------------------------------
sub LateEval(string KissPart)
    /declare Result string 0

    /declare Expression string ${KissPart.Arg[2,:]}
    /declare OrExpression string ${Expression.Replace[OR,||]}
    /varset Result ${OrExpression.Replace[#,$]}
    /varset Result ${Math.Calc[${Result}]}
    |/echo Eval_exp(${KissPart}) = ${Result}
/return ${Result}

- - - Updated - - -

And I've updated my kiss mods with a prototype for Kiss DPS:
http://www.redguides.com/community/...UberBard-BurnOn-LateEvals?p=243239#post243239

Rich (BB code):
[DPS]
DPSOn=1
DPSSkip=0
DPSInterval=2
DPS1=Improved Twincast|100|x|eval:#{BurnOn}
DPS2=Group Spirit of the Black Wolf|100|x|eval:#{BurnOn}
DPS3=Season's Wrath|100|x|eval:#{Target.Named}
DPS4=Fire Storm|99|x|eval:#{Target.PctHPs} > 80 OR #{Target.Named}
DPS5=Remote Sunflash|98|x|eval:#{Spawn[=#{MainAssist}].PctHPs}<90
 
Last edited:
I think anyone who has done any serious macro development has at one time or another tried to work with dynamic statements(/if()) and storing and reading them from ini files. Just to figure out that it doesn't work as expected. I don't know how many times I have looked at the MQ2 source to see how to incorporate the /noparse /varset ${ini[]}. You have come to the same conclusion as 99% of most developers. Just store your statements in an ini file replacing the ${ with something that can be .replace[] later.

I had re-written KISS many times using the method you have described, but I thanked the day Pete released his plugin to the community. I am sure he remembers just how much of a PITA I was for the first couple of weeks. I can tell you from experience using Pete's plugin(MQ2ifs) is going to be your best option for what you are trying to do now and in the future. The source code for his plugin is available and is very easy to follow and modify.
 
If everybody had done the replace method before, why did nobody recommend this to me in this thread earlier?

For now, I really like the convenience of not having to deal with another plugin and another ini file. I'll take a 4 line sub routine over messing with another plugin any day. We'll see if I run into problems with this approach.
 
If everybody had done the replace method before, why did nobody recommend this to me in this thread earlier?

For now, I really like the convenience of not having to deal with another plugin and another ini file. I'll take a 4 line sub routine over messing with another plugin any day. We'll see if I run into problems with this approach.

You mentioned .Replace in your original post so it didnt seem worth mentioning again. Using the plugin just saves you over directly accessing an ini. But using your replace method does work, it is just a tiny bit slower. Over time, the bits add up.

But that goes more to streamlining how you write macros. Realize each line in a macro takes practically the same time to process as any other line. /echos, and /inis take the most time. Reducing the overall number of lines can dramatically speed up performance. Placing as many functions into plugins saves you time in macro. So if you come up with some handy dandy process, you might be interested in adding it to a compile for everyone to use.

For reference, i made a macro that showed how much different parts of macros take to process. I used it to have users report their times to me so i could figure out why their macros were laggy and if it was their system or if it was the macro. If you are interested (might want to change the for loops to lower or itll take awhile to process):
Rich (BB code):
#turbo 80
|#turbo 60
|#turbo 40
|#turbo 20

Sub Main
/declare StartTime float outer ${MacroQuest.Running}
/declare CurrentTime float outer ${MacroQuest.Running}
/declare i int local
/declare x int local
/mqlog ${Me.Class} StartTime = ${MacroQuest.Running}
/echo Standard int declare check
/for i 1 to 2000
	/declare var${i} int outer ${i}
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} Standard int declare check - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}

/echo /Call int declare check

/for i 1 to 2000
	/call Test1 ${var${i}}
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} /call int declare check - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}

/echo Ini write speed check

/for i 1 to 2000
	/ini "Diagnostic.ini" "Test" "Test${i}" "${i}"
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} Ini write speed check - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}

/echo Ini /varset read speed check

/for i 1 to 2000
	/varset var${i} ${Ini[Diagnostic.ini,Test,Test${i}]}
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} Ini /varset read speed check - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}

/echo Single /if check - 5 variables
/for i 1 to 2000
	/if (${i}>1000 && ${i}>1200 && ${i}>1400 && ${i}>1600 && ${i}>1800) /varset x ${i}
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} Single /if check - 5 variables - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}

/echo Multiple /if checks - 5 variables

/for i 1 to 2000
	/if (${i}>1000) {
		/if (${i}>1200) {
 			/if (${i}>1400) {
				/if (${i}>1600) {
					/if (${i}>1800) /varset x ${i}
				}
			}
		}
	}
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} Multiple /if check - 5 variables - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}

/echo Multiple /if checks + /goto - 5 variables

/for i 1 to 2000
	/if (${i}<1000) /goto :skipgoto1
	/if (${i}<1200) /goto :skipgoto1
 	/if (${i}<1400) /goto :skipgoto1
	/if (${i}<1600) /goto :skipgoto1
	/if (${i}<1800) /goto :skipgoto1
:skipgoto1			
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} Multiple /if check + /goto - 5 variables - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}

/echo Multiple /if checks + /goto + ini - 5 variables

/for i 1 to 2000
	/if (${Ini[Diagnostic.ini,Test,Test${i}]}<1000) /goto :skipgoto2
	/if (${Ini[Diagnostic.ini,Test,Test${i}]}<1200) /goto :skipgoto2
 	/if (${Ini[Diagnostic.ini,Test,Test${i}]}<1400) /goto :skipgoto2
	/if (${Ini[Diagnostic.ini,Test,Test${i}]}<1600) /goto :skipgoto2
	/if (${Ini[Diagnostic.ini,Test,Test${i}]}<1800) /goto :skipgoto2
:skipgoto2			
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} Multiple /if check + /goto - 5 variables - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}


/echo /goto speed check
/for i 1 to 2000
	/if (${i}>1) /goto :skipgoto3
:skipgoto3
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} /goto speed check - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}


/echo /call speed check
/for i 1 to 2000
	/if (${i}>1) /call Test2
:skipgoto
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} /call speed check - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}

/echo Single /if check - 1 variable
/for i 1 to 2000
	/if (${i}) /varset x ${i}
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} Single /if check - 1 variable - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}


/echo Single /if check - 1 variable from ini
/for i 1 to 2000
	/if (${Ini[Diagnostic.ini,Test,Test${i}]}) /varset x ${i}
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} Single /if check - 1 variable from ini - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}

/echo String declare - 2 variables - 1 variable from ini
/for i 1 to 2000
	/declare String1${i} string outer ${Ini[Diagnostic.ini,Test,Test${i}]}
	/declare String2${i} string outer String2${i}
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} String declare - 2 variables - 1 variable from ini - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}


/echo Array declare - 2 variables - 1 variable from ini
/declare StringArray[2000,2] string outer 0
/for i 1 to 2000
	/varset StringArray[${i},1] ${Ini[Diagnostic.ini,Test,Test${i}]}
	/varset StringArray[${i},2] String2${i}
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} Array declare - 2 variables - 1 variable from ini - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}

/echo Timer declare - 1 variable from ini
/for i 1 to 2000
	/declare Timer${i} timer outer ${Ini[Diagnostic.ini,Test,Test${i}]}s
/next i

/mqlog ${Me.Class} ${If[${Foreground},Foreground,BackGround]} Timer declare - 1 variable from ini - ${Math.Calc[${MacroQuest.Running}-${CurrentTime}]}ms
/varset CurrentTime ${MacroQuest.Running}


/echo Diagnostic finished
/mqlog ${Me.Class} TotalTime=${Math.Calc[${MacroQuest.Running}-${StartTime}]}ms
/return


Sub Test1(int test1)
/declare Test${test1} int outer ${test1}
/return

Sub Test2
/return
 
/varsetEval plugin idea - The macroquest language, auto-eval,l and dpsifs

Users who are viewing this thread

Back
Top
Cart