Dynamic event creation

Need help with a macro you are writing? Ask here!

Moderator: MacroQuest Developers

notadruid
a ghoul
a ghoul
Posts: 143
Joined: Mon Dec 08, 2003 6:02 pm

Dynamic event creation

Post by notadruid » Wed Feb 16, 2005 5:59 pm

Is this possible? I'm writing a macro that collects data on each mob's resists based upon experimental data

As in, if Funeral pyre lands on mob X in zone Y, an ini file will add 1 to the value at:

Code: Select all

[zoney]
mobx.fire-100.land
But to do that I'd need to add events for every spell I cast on mobs. Would it be possible to have an event, "You begin to cast #1#" that would verify that it's a detrimental, targeted spell, look up the spell message and create an event, "#1# is enveloped in a funeral pyre" on the fly? And delete it once it's fired (to prevent other necro's FPs from messing with your data)

If not, I would have to have about a page full of events in my macro. Could I at least move that page of events to an .inc file?

notadruid
a ghoul
a ghoul
Posts: 143
Joined: Mon Dec 08, 2003 6:02 pm

Post by notadruid » Wed Feb 16, 2005 6:08 pm

And if I go through a page full of events, I would like to be able to automatically generate it =)) some macro that wouild look at all necro spells, and if they are detrimental and single target, spew #event spell_name_land "#1# got hit with your spell message" to a text file.

Hehe. If only I could find the macro that scanned your spellbook for missing spells, that would help me get this started if dynamic event creation/destruction is a no go.

TheNewGuy
a snow griffon
a snow griffon
Posts: 307
Joined: Sat Jul 10, 2004 9:33 am
Contact:

Post by TheNewGuy » Wed Feb 16, 2005 6:25 pm

Magelo does it...

In fact, I remember a macro around here somewhere that did it also. Searched your entire spellbook, and told you what you had, and what you were missing. I'd use the search and look if I were you, if I find it I'll edit this post and link it.
[quote="dont_know_at_all"][quote="sybarite"]Um.. search works fine for me (used spell_routines.inc)... Try scrolling down you lazy fuck.

http://www.macroquest2.com/phpBB2/viewtopic.php?t=8964&highlight=spellroutines+inc[/quote]

That's great but he's looking for move.inc, you fucking spastic colon.[/quote]

notadruid
a ghoul
a ghoul
Posts: 143
Joined: Mon Dec 08, 2003 6:02 pm

Post by notadruid » Wed Feb 16, 2005 6:45 pm

The post I was looking for

Anyone have a definite answer on dynamic event creation/destruction? If it's a definite no, I'll start writing the event generator. It would scan either your book or all necro spells (haven't decided whether to make it customized or future-proof), create the #event, and create the Sub for the event.to create the database entry. Hmm, this looks interesting.

TheNewGuy
a snow griffon
a snow griffon
Posts: 307
Joined: Sat Jul 10, 2004 9:33 am
Contact:

Post by TheNewGuy » Wed Feb 16, 2005 7:21 pm

Aye that's it.

As far as the dynamic event, I'm the farthest thing from an expert, but I'd say start writing your events heh
[quote="dont_know_at_all"][quote="sybarite"]Um.. search works fine for me (used spell_routines.inc)... Try scrolling down you lazy fuck.

http://www.macroquest2.com/phpBB2/viewtopic.php?t=8964&highlight=spellroutines+inc[/quote]

That's great but he's looking for move.inc, you fucking spastic colon.[/quote]

notadruid
a ghoul
a ghoul
Posts: 143
Joined: Mon Dec 08, 2003 6:02 pm

Post by notadruid » Fri Feb 18, 2005 10:48 pm

Writing the log code, Was going to harvest spell data quickly by logging events for any spell I mem .. but...

Code: Select all

Sub Event_memspell(message,memmed)
	/echo Memmed: ${memmed}
	/echo Logging infoz for ${Spell[${memmed}]}
	/if (${Spell[${memmed}].SpellType}.Equal[Detrimental]) /if (${Spell[${memmed}].ResistType}.NotEqual[Unresistable]}) /mqlog #event "${Spell[${memmed}].ResistType}${Spell[${memmed}].ResistAdj} #1#${Spell[${memmed}].CastOnAnother}"
/return
As soon as I added the /ifs to filter out buffs and such, I got the dreaded "non-numeric encountered" error whenever this code block is called. I think I'm missing something really basic here?

For clarity, my log looks like this:

Code: Select all

[color=red]#event Poison0 #1# staggers.[/color]
#event Magic-200 #1# screams in torment.
#event Fire-100 #1# is enveloped in a funeral pyre.
[color=red]#event Magic-100 #1# staggers.[/color]
#event Magic0 #1# is hindered by a shackle of spirit.
#event Magic-200 #1# looks pale.
#event Magic-10 #1# stops moaning.
[color=red]#event Disease-200 #1# staggers.[/color]
#event Magic-30 #1# screams in abject terror.
#event Poison-50 #1#'s veins turn a vile shade of green.
#event Magic-200 #1# screams in torment.
#event Magic0 #1# looks very afraid.
#event Magic-200 #1# staggers as a shadow pierces his soul.
#event Magic-20 #1# is engulfed in an embracing darkness.
And this is good, with the exception of the "Staggers"entry ... several necro spells use this message so I will have to have some logic in the event for that to determine which resist it was.

EDIT: Changed syntax of the if line to read:

Code: Select all

/if (${String[${Spell[${memmed}].SpellType}].Equal[Detrimental]}) /if (${String[${Spell[${memmed}].ResistType}].NotEqual[Unresistable]}) /mqlog #event "${Spell[${memmed}].ResistType}${Spell[${memmed}].ResistAdj} #1#${Spell[${memmed}].CastOnAnother}" <<-${Spell[${memmed}]}
and it worked. Why is ${String} necessary? Aren't Spells treated as strings?
Last edited by notadruid on Fri Feb 18, 2005 11:55 pm, edited 1 time in total.

Zeus
a hill giant
a hill giant
Posts: 180
Joined: Wed Feb 19, 2003 10:03 am
Contact:

Post by Zeus » Fri Feb 18, 2005 11:50 pm

Code: Select all

${Spell[${memmed}].SpellType}.Equal[Detrimental]) 
I'm not quite sure how to explain this but here it goes. The Equal function is a member of string. You dont have it inside the ${} so that expression evaluates to (assuming you have a detrimental spell memmed):

Code: Select all

If ("Detrimental".Equal[Detrimental])
Problem here, "Detrimental" is no longer a data type but a literal now so there's no functions for it. Whatever you put outside of the brackets doesn't corresponds to the Data.

Code: Select all

${String.Equal[textHere]}
Is what you want. So you got your string variable which is

Code: Select all

Spell[Name].SpellType
You got the Name of the spell you want to get the properties from on your variable memmed so you substitute and SpellType is a string which is what you want to compare to. Up to that part you had it right so substitute that inside the previous statement:

Code: Select all

${Spell[${Memmed}].SpellType.Equal[textHere]}
And finally the other part which you also got right which was the string to compare to "Detrimental" leaving you with:

Code: Select all

${Spell[${Memmed}].SpellType.Equal[Detrimental]}
That for a detrimental spell would evaluate by parts

Code: Select all

(Detrimental = Detrimental)
Which is a boolean expresion (true/false) and satisfies the conditions of an if statement (true/false; or anything different than 0 evaluates for true and 0 for false).

Sorry for the long and prolly boring explanation I'm sure someone could explain much better but I wanted to try and explain the concept rather than just giving an easy fix. I think you gonna get still a couple errors after that but it's a place to start.

notadruid
a ghoul
a ghoul
Posts: 143
Joined: Mon Dec 08, 2003 6:02 pm

Post by notadruid » Fri Feb 18, 2005 11:58 pm

Ok, so multiple dots would also fix it? Much simpler and cleaner fix. Thanks ^_^

notadruid
a ghoul
a ghoul
Posts: 143
Joined: Mon Dec 08, 2003 6:02 pm

Post by notadruid » Sat Feb 19, 2005 9:38 am

I found a way to implement dynamic events. The key is that you don't do it at the creation level but at the checking level.

Here's how I did it:

First, I recorded all #Events I'm going to possibly use through the logging code, and put them in an .inc file. As I need more events to possibly check, I'll just add them.

Then, I added subs for adding and checking events in a queue. Next I'm going to write a function to remove events from the queue.

My coding is VERY sloppy at this point because I could very easily change the goto loops and incrementing to /for and /next, which I just recently learned, but here's what I have so far for adding and checking ... it works =]

Code: Select all

Sub addevent(string curevent)
	|Adds an event to the queue of events to check.  First event is event1, second is event2, etc
	|These are checked by the main# func, checkevents.  I do this instead of /doevents because
	|When a user has disabled a feature, like pet attacking, it is more efficient to exclude the
	|event from being checked in the first place than having the event trigger a null function
	|every time it is called.
	/declare tempevent int local 1
	|tempevent goes out of scope upon /return, no need to delete
	:addeventloop
	/if (${Defined[event${tempevent}]}) {
		/varcalc tempevent ${tempevent}+1
		/goto :addeventloop
	}
	/declare event${tempevent} string outer ${curevent}
	/echo Debug: addevent called: event${tempevent} set to ${event${tempevent}}
/return

Sub checkevents
	|Checks events set when the macro inits ... and dynamic events created by other events
	|This is always a main# function.  I do this instead of /doevents because
	|When a user has disabled a feature, like pet attacking, it is more efficient to exclude the
	|event from being checked in the first place than having the event trigger a null function
	|every time it is called.  Less coding and less work for me ^^
	/declare tempevent int local 1
	|tempevent goes out of scope upon /return, no need to delete.
	:eventloop
	/if (${Defined[event${tempevent}]}) {
		/doevents ${event${tempevent}}
		/varcalc tempevent ${tempevent}+1
		/goto :eventloop
	}
/return
About the "main#" comment ... You can probably guess that I have the functions that are called by the main loop set up the same way as the events ^_^

And by the way .. /next is only mentioned in the manual as part of a /for loop ... You can increment variables ouitside /for too, right?

Lax
We're not worthy!
We're not worthy!
Posts: 3524
Joined: Thu Oct 17, 2002 1:01 pm
Location: ISBoxer
Contact:

Post by Lax » Sat Feb 19, 2005 11:35 am

Good god. Those two subs could take ages to complete. Turbo sets it to 20 commands per frame ("pulse) by default, and can be cranked up to 40.. addevent has 5+(3*n) commands, and checkevents has 3+(4*n) commands, not counting the subs executed by /doevents (n is the number of events). For n=5, you've already used a full frame at default turbo to just add an event and another to check it later. I would really suggest doing this in a plugin instead if at all possible.

As far as your last question, you cannot use /next except with /for, because /next jumps to the /for (if it is not found, the macro ends). You can of course increment variables with /varcalc
Lax Lacks
Master of MQ2 Disaster
Purveyor of premium, EULA-safe MMORPG Multiboxing Software
* Multiboxing with ISBoxer: Quick Start Video
* EQPlayNice, WinEQ 2.0

JGC84
a ghoul
a ghoul
Posts: 116
Joined: Sat Nov 29, 2003 2:51 am

Post by JGC84 » Sat Feb 19, 2005 12:02 pm

ahhhhh big O notation SAVE ME!

(this has been yet another worthless comment brought to you by JGC)
ACK! I think I blew it up....

I dont think your allowed to talk about this outside of a "free speech zone" anymore.

notadruid
a ghoul
a ghoul
Posts: 143
Joined: Mon Dec 08, 2003 6:02 pm

Post by notadruid » Sat Feb 19, 2005 2:03 pm

Thanks for the info, Lax. I wasn't aware of how MacroQuest calculates how long it takes to execute code. I'll be sure to optimize it before releasing the finished macro ... I had never considered using a plugin-within-a-macro as a form of optimization, but I know enough C++ to learn how to create a plugin, so I'll definitely consider it.

On the other hand, all of the addevent calls are handled either during initialization or when the macro is spending a good amount of time doing nothing (waiting for a casting time bar, etc) though checkevents is constantly being called (It's in the main loop). Would it be better if the macro called each event and had to verify that the event should have been called because of an ini setting? Because that would be easy to implement.

Lax
We're not worthy!
We're not worthy!
Posts: 3524
Joined: Thu Oct 17, 2002 1:01 pm
Location: ISBoxer
Contact:

Post by Lax » Sat Feb 19, 2005 2:16 pm

It's not that MQ calculates how long it takes, it's that you know how many macro commands per frame it executes, as well as how many frames per second you're getting.. with that information, you can determine with reasonable accuracy how long it will take to execute parts of your macro.

Ini can prove worse, because that may require doing actual disk accesses, which take forever... but I will suggest that if you have many events in this, you don't want to loop through each one in a macro :) You might consider keeping an outer scope variable for each one to see if it should continue..

Really, the best way to handle any events is in a plugin because they can do things directly, rather than in a secondary, interpreted script (macro) language... but it's really up to you, if it serves its purpose the way you have it and you dont see performance issues with what you're using it for, then you dont need to do anything about it... however, if others were to try use this in a large script with many events, etc, they would want to pursue other methods
Lax Lacks
Master of MQ2 Disaster
Purveyor of premium, EULA-safe MMORPG Multiboxing Software
* Multiboxing with ISBoxer: Quick Start Video
* EQPlayNice, WinEQ 2.0

notadruid
a ghoul
a ghoul
Posts: 143
Joined: Mon Dec 08, 2003 6:02 pm

Post by notadruid » Sat Feb 19, 2005 2:57 pm

Again, thanks for the info. In the meantime, I have optimized the subs:

Code: Select all

Sub init
	(*snip*)
	/declare events int outer 0
	(*snip*)
/return

Sub addevent(string curevent)
	/varcalc events ${events}+1
	/declare event${events} string outer ${curevent}
	/echo Debug: addevent called: event${events} set to ${event${events}} 
/return

Sub checkevents
	/declare tempevent int local 1
	/for tempevent 1 to ${events}
		/doevents ${event${tempevent}}
	/next tempevent
/return

notadruid
a ghoul
a ghoul
Posts: 143
Joined: Mon Dec 08, 2003 6:02 pm

Post by notadruid » Sat Feb 19, 2005 3:33 pm

This sub is kicking my ass:

Code: Select all

Sub remevent(string event)
	/declare tempevent int local 1
	/declare onehigher int local
	/declare onelower int local
	/varset onelower ${events}-1
	/for tempevent 1 to ${events}
		/if (${event.Equal[${event{tempevent}}]}) {
			/for tempevent ${tempevent} to ${onelower}
				/varset onehigher ${tempevent}+1
				/varset event${tempevent} ${event${onehigher}}
			/next tempevent
			/varcalc events ${events}-1
			/return
		}
	/next tempevent
/return
I have a feeling I can get rid of both onehigher and onelower, though I've gotten errors whenever I've tried to replace them with Math.Calc[] This code might not even work, as I cannot test it now.

The objective is to take a group of outer strings, remove one of them, and decrement the name of all events with a higher number. I'd also need to decrement the outer int events, as there will be one fewer total. As an example, say here are my initial events:

Code: Select all

event1=memspell
event2=watchspell
event3=Magic0
event4=resisted
events=4
And after I /call remevent Magic0 the events should have been changed to:

Code: Select all

event1=memspell
event2=watchspell
event3=resisted
events=3