So I'm playing again, and coding-wise, I'm starting mostly from scratch - I never coded much for dragon, and I stopped playing soon after I went from monk to serpent.
I'm curious about what the coders here have come up with as far as your scripts for multiclass. I'm not asking for your dragon aliases, but more like flow and structure of your scripts that deal with multiclass. For example, before I went dormant, I coded my hotkeys to limb attacks. Hotkey NUMPAD_4 would call a generic left arm attack function. That function would check my class, then either call a dragon or monk left arm attack function, which then sent the commands to the server.
That setup was very function heavy, but it kept me from having multiple hotkeys for the same keys. Performance-wise, I'm not sure which is better: Groups of identical hotkeys/aliases/triggers that are turned off/on on class change, or a single group of each that checks your class whenever activated.
Anyways, I'm interested to hear what solutions you've come up with!
0
Comments
I use aliases, rather than hotkeys, for my offense, but the general gist of it is:
aliases to select affliction combinations (actually one alias, it just recognises a lot of different inputs and works out the afflictions accordingly)
aliases to select targetted limb
a single attack alias (atk) with an optional modifier (differs depending on class)
The attack alias calls a core offense function that does four main things:
1. generates a list of generic commands (stand, summon mount, vault, handle parrying, etc) that aren't dependent on class
2. calls a class-specific offense function that generates and returns the list of commands to run for the actual attack (each class has its own namespace, so these functions will be: antonius.serpent.attack, antonius.depthswalker.attack, etc)
3. creates an in-game alias with all of those commands (controlled by a script - only actually updates the alias is the set of commands has changed)
4. queues said in-game alias (controlled by a script - only queues if not already queued, will replace anything already queued that uses balance)
Then in a fight, I'll be doing things like:
'curkal' - set afflictions to use to paralysis and asthma
'atk' - tries to attack; if I'm a dual cutting Paladin or Runewarden, this will doubleslash with curare and kalmia; if I'm a Serpent, it will doublestab with curare and kalmia; if I'm alchemist, will truewrack with paralysis and asthma; if I'm a Bard it will (try to) find a venom and song combination that allows me to give those afflictions; etc
'atkd' - tries to attack, plus do whatever the d modifier does; if I'm Depthswalker, it's attack (reap) + attune degeneration; if I'm Paladin, it's attack (depends on weaponmastery spec) plus lay rite of demons; etc
Results of disembowel testing | Knight limb counter | GMCP AB files
So just roll with whatever you like, really.
got gud
Gmcp data is read once, when it's received, then it's stuck into a variable. All you're doing [probably] is checking that variable that is already set. If you localize that gmcp variable inside your function, it'll go even faster, but just checking lua variables should never slow it down to any noticeable degree without extreme profiling.
Unless you're sending a gmcp packet on every function, waiting for a return packet, evaluating that packet, then acting accordingly. You'd definitely have lag then, just in ping for the gmcp request/reception.
Are you noticing a lag in processing from your functions?
Currently available: Abs, Cnote, Keepalive, Lootpet, Mapmod
got gud
That's what I personally use in all of my stuff. The only time I notice any real performance hit is when I'm spamming the hell out of something, which is honestly to be expected.
Penwize has cowardly forfeited the challenge to mortal combat issued by Atalkez.
got gud
(Reference: http://stackoverflow.com/questions/9132288/why-are-local-variables-accessed-faster-than-global-variables-in-lua)
As @Nazihk says though, in Mudlet if you're not doing something many times a second, a relatively modern computer isn't going to see issues crop up with inefficient code, you just have to watch out for things where you're doing a complicated thing many times a second, like if you're doing some sort of for loop 100+ times and you do a call to expandAlias instead of using a function, or if you're doing something that takes up a large amount of memory, like keeping a table of player information that has every player you have ever seen on WHO along with a bunch of text information that you're storing about them. Before I moved to an SSD, I was having seconds of lag when I would save the table that I used to hold player info, since it was 1000+ entries long and each entry also had their full description and basically most of the information from HONOURS.
Another place to be attentive is when making your triggers. If you find want to try and minimize the amount of complicated regex comparing you do, you can take your regex triggers and modify them so that they match a "beginning of line substring" of the first few words to make sure you're avoiding the regex option unless the line almost certainly matches, since the substring and exact match methods of matching triggers are much faster than regex parsing. (I think this is something that Vadi did in svo if I'm not mistaken, but I don't actually use it so I've not seen the code since it was first made open source)
On the original topic though, I generally have a big folder that contains smaller folders for every class I build an offense/defense for, and I have them toggled when my class changes, using things like enableAlias() and enableTrigger(). I find that after having used Mudlet for as many years as I did in another IRE(in which I had a system for at one point more than 1/3 of the classes in the game), I build something like my first classes offense and defense in whatever way I think will make it easiest to add in new classes at a future date. Keeping stuff modular(able to be expanded by adding small new parts that you already have a framework for) tends to feel much better to me than making one set of aliases/triggers/keys that has a bunch of if/then statements to handle every class.
Example of what I was mentioning that svo does:
if isClass("Depthswalker") then shadowShit() end
Then I map all common utility to the same aliases/keys and go from there depending on what's comfortable.
I find it's easiest to keep things straight if I put as much of it as I can on the same map (I have 4 knight specs, magi, alchemist, depthswalker, serpent, bard, BM, shaman, occultist. RIP Lucrescent nuts).
For macros, I began with the traditional if x then y structure, but quickly outgrew that.
Migrated to function calls for classes, still using an if class then function approach.
- allows us to avoid duplicate macros which are a PitA to figure out at times if folders aren't turned on/off properly like you said
- allows us to streamline free responses (stand, wield, what have you), but done differently to Antonius'; these things are calculated at the end of construction and pre-pended to the outgoing line via an alternate send function, reducing code for each function but is technically less efficient then Antonius'
- easier to swap functions during testing
Final macro structure was a bit overconstructed...
- all keys were bound to a single function passing the combination as the argument (macro.fire("s"), macro.fire("shft-backslash")
- the function would attempt to execute a class-specific function call, failing which, would call the module-specific function call (macro[arg]() might map to Indorani.s() or macro.s()), after a lookup
- this absolutely obviated the need to navigate to the Keys folder to do anything else ever again, and access to the macro structure was rarely required. Simply creating specifically named functions or replacing them allowed us to re-write offences without deleting old functions.
The thing I was working on was a method to integrate class-class synergy in a group fight while reducing access time within a UI-centric client, before my IRE game sputtered and dissipated (hi @Vessil). And then I became an Achaean bum. #bumlyf
I do like Antonius' on-the-fly construction.
I think that's the way I'm gonna take it though!
Here's the profiling for "SetMacro" function in MUSHclient:
Function Count Total(s) Average(ms)
Accelerator 39 0.0097 0.2495
You can see that loading almost 40 macros, took less than 1/100th of a second. Might not want to do it with 800 macros but drawing just a couple dozen nets a near immeasurable (with variances in ping) amount of lag.
This is the profiling script I use for mush, if anyone is interested:
http://gammon.com.au/forum/?id=10279
Currently available: Abs, Cnote, Keepalive, Lootpet, Mapmod