Register to post in forums, or Log in to your existing account
 

Play RetroMUD
Post new topic  Reply to topic     Home » Forums » zMUD General Discussion
Fitzwilliam
Beginner


Joined: 24 Aug 2006
Posts: 11

PostPosted: Sat Aug 26, 2006 5:14 pm   

Spellup Revisited
 
I have been reading the different posts on spellup scripts and am a bit puzzled.

I am just looking for a script that will keep my spells up continually without having to type anything.

Do I need to include the 'affect' command in the script? On my mud, if you type affect it shows:

Affected: fly
Affected: sneak

Can you make a script that keeps spells up continually? And what if you get disconnected or if a spell fails or if the variables just get confused (like you are fighting and a spell fades and the trigger cannot recast).

If a script can be made to work with just the spell fly, I could add the others myself.

I found a Spellup script for Realms of Despair and removed all but the Fly spell to test it. It is below. It does not seem very elegant :O And it really does not work yet... (probably my fault) though I am not sure why. Is there a better, cleaner way?

#CLASS {lspell}
#ALIAS af {lvlspells.fly = 0;#send af;#wa 1500;spells}
#ALIAS spells {#echo .;#echo ******** SPELL'S MISSING ********;#if ( @lvlspells.fly = 0) {#echo %ansi(bright,red)******** FL IS MISSING ********};#ECHO ******** END OF LIST ********}
#VAR lvlspells {fly 1}
#TRIGGER {Affected: ~((%S)(%D)~)(%S)(%W)(*)} {lvlspells.%4 = 1}
#CLASS 0

#CLASS {lspell|refresh_in_out}
#CLASS 0

#CLASS {lspell|refresh_in_out|outlspells}
#TRIGGER {you slowly float to the ground} {lvlspells.fly = 0;fl}
#CLASS 0

#CLASS {lspell|refresh_in_out|inlspells}
#TRIGGER {You rise into the currents of air...} {lvlspells.fly = 1}
#CLASS 0
Reply with quote
edb6377
Magician


Joined: 29 Nov 2005
Posts: 482

PostPosted: Sat Aug 26, 2006 10:18 pm   
 
you would need the spell wearing off trigger

Then simply add the recast command to that trigger
_________________
Confucious say "Bugs in Programs need Hammer"
Reply with quote
Qiz
Novice


Joined: 14 Aug 2006
Posts: 34
Location: Sweden

PostPosted: Sat Aug 26, 2006 10:54 pm   Re: Spellup Revisited
 
At a quick glance some things seem to be missing.

Fitzwilliam wrote:
#ALIAS af {lvlspells.fly = 0;#send af;#wa 1500;spells}

Is this ment to send the ALIAS af every 1500 seconds? What #SEND does is to send the contents of the FILE af to the mud... Do you have that file? If so, what does it look like?

Fitzwilliam wrote:
#ALIAS spells {#echo .;#echo ******** SPELL'S MISSING ********;#if ( @lvlspells.fly = 0) {#echo %ansi(bright,red)******** FL IS MISSING ********};#ECHO ******** END OF LIST ********}

This one goes through your spell variables and if they're set to 0 will notify you of this. No actual action is taken though, which makes this seem to be more of a test definition for this alias to be used while making everything work. To make it actually be useful I'd add in a "cast fly" in the true clause.

Fitzwilliam wrote:
#VAR lvlspells {fly 1}

{fly %1} perhaps? At least, if you are on RoD yourself, doing anything like "cast fly 1" makes no sense. It would try to cast fly on a target called 1, and since numbers are not valid names there, it would always fail (you'd see: "They aren't here" when executing that command).

Fitzwilliam wrote:
#TRIGGER {Affected: ~((%S)(%D)~)(%S)(%W)(*)} {lvlspells.%4 = 1}

Once again, if you're on RoD, this means trouble. Consider the following aff output:
Affected: ( 250) detect invisible
Affected: ( 549) detect hidden
The first line sets lvlspells.detect = 1
The second line sets lvlspells.detect = 1

You could _perhaps_ go with pattern:
~((%S)(%D)~)(%S)(*)
But, I suspect this will give you problems as well as the above would become:
lvlspells.detect invis = 1
Possible fix for this is:
{lvlspells.{%4} = 1} --> lvlspells.{detect invis} = 1
Try it and see. If it doesn't work, you'd have to first remove any whitespace from %4 to turn it into
lvlspells.detectinvis = 1
lvlspells.ogremight = 1
etc...


Fitzwilliam wrote:
#TRIGGER {you slowly float to the ground} {lvlspells.fly = 0;fl}

I'm guessing this one is ment to be (once again, if you're on RoD)
{lvlspells.fly = 0;c fl}
as fl is gibberish. Or perhaps there was initially an alias
#ALIAS fl {c fly}
Though that makes little sense. Might as well go ahead and just use "c fly" right away unless you want to be able to cast fly yourself by typing only fl.


If you intend to use the above you risk running into trouble if you want to always be affected by a spell that is not castable during combat. You will need a trigger to check your aff list as soon as you end combat. Same goes for being asleep and also if you don't have enough mana to cast a certain spell at any given time.
Reply with quote
Fitzwilliam
Beginner


Joined: 24 Aug 2006
Posts: 11

PostPosted: Sun Aug 27, 2006 12:38 am   
 
Quote:
You will need a trigger to check your aff list as soon as you end combat. Same goes for being asleep and also if you don't have enough mana to cast a certain spell at any given time.


This is what I would like! I really just want a clean version. I do not like the way the poster I quoted did it at all.

Is there a spellup script that does this somewhere on the boards? I have been searching, but most do not appear to work this way. If I can get just the fly spell working then others would be easy to modify.

Thank you for the replies.
Reply with quote
Fitzwilliam
Beginner


Joined: 24 Aug 2006
Posts: 11

PostPosted: Tue Aug 29, 2006 7:28 am   
 
I am really pulling my hair out with this and could use some more expert help.

----------------------------------------------------
PROBLEM 1. What trigger will match the pattern for my AFFECT command? The output looks like this and consists of a long list of spells and rounds remaining:

Affected: ( 131) aqua breath
Affected: ( 625) fly
Affected: (1630) kindred strength

This is does not seem to match--

#TRIGGER {Affected: ~((%S)(%D)~)(%S)(%W)(*)}

Neither does this--

#TRIGGER {Affected: ~((%S)(%D)~)(%S)(*)}

The idea is that when affect is activated, it determines if the spell is on the list and then sets the variable on or off.

------------------------------------------------------------------------
PROBLEM 2. How does the AFFECT command work into this process? Here is what I mean:

This is how I see the script working:

STEP 1

Somehow when the mud says You rise into the currents of air... a variable should be set that fly is activated.

Maybe: #TR {You rise into currents of air...} {#var vFLY " "}

STEP 2

When you get this message, you slowly float to the ground, a variable should be set that fly is deactivated.

Maybe: #TR {You slowly float to the ground} {#var vFLY "fly"}

STEP 3

Some automatic process checks to see if fly variable is ON or OFF and then casts the spell or not.

#IF (@vFly ="fly") {cast fly}

Maybe the On or Off state on the status bar: #ST {@vFly}

And somehow AFFECT is involved here. Possibly it should be setup so that the spells are checked/cast anytime I type AFFECT on the mud. But it would be nice to somehow automate this process. Though, I am not sure how I would avoid casting buffing spells during fighting if it were automatic.

I am looking for a simple, elegant solution to this. Maybe using ADDITEM to a list of spells that are down?

Sorry for the confusing post, this is beyond me.
Reply with quote
Qiz
Novice


Joined: 14 Aug 2006
Posts: 34
Location: Sweden

PostPosted: Tue Aug 29, 2006 9:27 pm   
 
Fitzwilliam wrote:
Affected: ( 131) aqua breath
Affected: ( 625) fly
Affected: (1630) kindred strength

This is does not seem to match--
#TRIGGER {Affected: ~((%S)(%D)~)(%S)(%W)(*)}
#TRIGGER {Affected: ~((%S)(%D)~)(%S)(*)}

First off, this is not correct:
Affected: (1630) kindred strength
It should instead be:
Affected: ( 1630) kindred strength

Also, there are more than one whitespace ' ' between "Affected:" and '(' while you only have one in your trigger. This should work:
#TRIGGER {Affected:%s~((%S)(%D)~)(%S)(%W)(*)}

On a side note, since you don't want to do anything with the whitespaces, there's really no need to store them in variables, so you can just go ahead and skip the () around your %s. Same goes for the number of remaining rounds, the %d (unless you want to recast aqua breath when it's below 50 or something. it's the only spell you can do this with)
As such, this would be sufficient:
#TRIGGER {Affected:%s~(%S%D~)%S(%W)(*)}



Fitzwilliam wrote:
The idea is that when affect is activated, it determines if the spell is on the list and then sets the variable on or off.

------------------------------------------------------------------------
STEP 1

Somehow when the mud says You rise into the currents of air... a variable should be set that fly is activated.

Maybe: #TR {You rise into currents of air...} {#var vFLY " "}

I've not looked into how much time zMud requires to perform different tasks, so this may be superflous information, or only become interesting if you run a lot of triggers. But as I see it, it's better to do things faster than slower.
If you do go ahead and keep one variable for each spell (and I recommend using a stringlist instead), don't set it to the actual spellname, use 0 and 1. 0 is false, non zero is true.
#IF (var) {say I'm flying!} {cast fly}
When var is 0, you will say "I'm flying!" and when var is 1 you will cast fly.

This is much faster than string comparison. String comparison can be speeded up, but if it's handled in the most basic fasion it compares each letter in both strings til one doesn't match or the strings end.
so, if("fly" = "fly") would compare 'f' with 'f', then 'l' with 'l', 'y' with 'y' and then realize the strings ended. That's actually four comparisons.


Fitzwilliam wrote:

STEP 3

Some automatic process checks to see if fly variable is ON or OFF and then casts the spell or not.

#IF (@vFly ="fly") {cast fly}

Maybe the On or Off state on the status bar: #ST {@vFly}

And somehow AFFECT is involved here. Possibly it should be setup so that the spells are checked/cast anytime I type AFFECT on the mud. But it would be nice to somehow automate this process. Though, I am not sure how I would avoid casting buffing spells during fighting if it were automatic.

I am looking for a simple, elegant solution to this. Maybe using ADDITEM to a list of spells that are down?.


Giving you a complete script would take a lot of time, even in pseudo code, so I'll just try to nudge you along the way and hope that is help enough.
Create a list consisting of:
What you need to do is determin every possible situation that may arise and that you wish to deal with.
Then go over what you would consider in each of these cases.
Finally determin each action you would take after these considerations.

Once the list is done, figure out how to handle each thing in zMud. Generally, a situation is handled by a trigger, a consideration by an if check and an action by updating a variable or casting a spell (or taking any other mud related action)
However, depending on how you state the non formalized situations it can either seem like you're reacting to a situation (trigger) or considering why things went wrong (if check). For example:
Failed casting spell. Why? if out of mana...

or situation: You don't have enough mana trigger
situation: You lost your concentration. trigger
Though in the above case, what you actually do is reacting to a situation.


Initial (non formalized) list to the left and how to do stuff in zMud to the right.

1. spell wears off _____________ spell wear off msg trigger
1.1 am I fighting? ____________ #if (@combat)
1.1.1 cast spell in combat? _____ #if (@combatCast) *NOTE1: see below
1.1.2 (true) cast spell _________ cast spell that wore off
1.1.2 (false) cast later ________ #additem (castList, some spell)

2. combat ended _____________ prompt trigger
2.1.1 _______________________ turn off prompt trigger
2.1.2 _______________________ turn on fprompt trigger
2.1.3 _______________________ combat = 0
2.2.1 any spells wore off? ______ #if (@castList)
2.2.2 (true) cast next spell _____ cast %item(@castList, 1)

3. combat started ____________ fprompt trigger
3.1.1 ______________________ turn off fprompt trigger
3.1.2 ______________________ turn on prompt trigger
3.1.3 ______________________ combat = 1

4. spell cast success? ________ spell success msg trigger
4.1 don't cast this again ______ castList = #delnitem (castList, 1)
4.2 still not fighting? _________ #if (@combat)
4.2.1 (true) cast next ________ as 2.2.1 and 2.2.2

This one deals with "You lost your concentration." type messages
5. spell cast failure (not "You failed.") spell failure trigger *NOTE2: see below
5.1 still not fighting? ____________ as 4.2
5.2 recast spell ________________ as 2.2.2

6. out of mana _______________ oom trigger
6.2.1 not fighting? ____________ #if (@combat)
6.2.2 _______________________ enable "mana check" trigger
6.2.3 regain mana ____________ sleep, quaff mana pot, trance

7. "enough" mana ____________ "mana check" trigger
7.1.1 not fighting? ____________ as 6.2.1
7.1.2 cast next ______________ as 2.2.1 and 2.2.2


*NOTE1:
Go through all the spells you want to autocast and divide them up in two groups; one consisting of those you want to cast even if fighting, one consisting of those that can wait til after combat.
Store the spells in whichever group is smaller in a strinlist. I just assumed the spells you want to cast in combat are fewer than those that can wait (no real thought behind this) and as such would store this smaller group in a string list variable called combatCast.

*NOTE2:
If you get a "You failed." message, this means you are allready affected by the spell you cast (goes for almost all spells you cast on yourself). This should not happen with the above approach, but just in case you could add:
a "You failed." trigger
and go ahead as per 4.1
Reply with quote
Fitzwilliam
Beginner


Joined: 24 Aug 2006
Posts: 11

PostPosted: Tue Aug 29, 2006 10:27 pm   
 
Thank you so much for your analysis--a lot of that information I can really use!

My big stumbling block is STILL the AFFECT trigger. I cannot seem to make it work. Can you give me a code snippet of how the affect trigger 'sees' whether the spell is in the list or not in the list, in this case fly, and then sets the variable on or off. Maybe I am missing a variable or class or something...
Reply with quote
Qiz
Novice


Joined: 14 Aug 2006
Posts: 34
Location: Sweden

PostPosted: Wed Aug 30, 2006 12:57 am   
 
The easiest way to go about it is to start with every spell, in a list or as bit flags in a variable (which is faster to handle than string lists), as there's no way to get info about missing spells, only active ones. Then, as you capture each active spell, you remove their bit flags (or remove them from the list). Those you've got left are what you lack.

I'll show how to do it with bit flags. The list works the same way if you choose that path: add every spell to a string list. as a spell is found with the "affected" trigger, remove it from the list. Use that list to cast. However, this is a lot slower than using bit flags as explained below.

If you're new to logical bit operators, see at the end how this works.

Set up variables for all spells, each having a distinct bit value, separate from the rest.
fly = 1
shield = 2
ogre = 4
sagacity = 8
slink = 16
dragon = 32
dragonskin = 64
aqua = 128

Get the sum of all those spells, in this case
255
We store this value in two variables (reason apparent later)
spellSum = 255
allSpells = 255, which in binary equals (whitespace for readbility only):
1111 1111


Affected: ( 625) fly
Affected: ( 1630) kindred strength

on each of the above lines your affected trigger will fire and get the spell name (note that spells like detect invis and detect hidden require you to check the (*) parameter as well, while for 'dragon wit' it's enough with the (%w) parameter since dragonskin is one word.

Code:

#tr {the affected pattern} {
  #if (match "fly") {allSpells = %bitxor(@allSpells,  @fly)} {
     #if (match "aqua") {allSpells = %bitxor(@allSpells, @aqua)} {
        ... and so on
 lots of }}}}}
}

Make sure you nest those IF statements. That way you'll just go through half of them on average. If you don't nest them, you'll always go through all of them.
Now, I can't remember off hand if spells with multiple effects show up on more than one line (such as bless). If they do you need to keep track of those spells and only perform one %bitxor.
it could look like:
earlier in the code (not in this trigger), make sure all those variables keeping track of such spells are set to 1.
Code:
blessNotFound = 1

in the trigger:
Code:

   #if (match "fly") {
       #if (blessNotFound) {
           blessNotFound = 0
           allSpells = %bitxor(@allSpells, @bless)
        }
   }



Logical bit operators.
They work like normal logical operators, except they perform an operation on each pair of corresponding bits.
XOR means OR with eXclusion. if both values are 1, the result is 0
xor(0, 0) = 0
xor(0, 1) = xor(1, 0) = 1
xor(1, 1) = 0

and(1, 1) = 1, all other cases = 0

Now, for a bitwise XOR, where the 'r' line is the result
bitxor(2, 7)
2: 0010
7: 0111
---------- bitXOR
r: 0101

In your spell case, let's assume there are only 4 spells to autocast, and let's also assume the system uses a 1 byte (8 bits) integer. It doesn't, but the effect would be the same. From the above example:
fly = 1
shield = 2
ogre = 4
sagacity = 8
The sum of these spells is 15.
allSpells = 15 which in binary is
0000 1111

With a 4 byte integer, there would just be a lot more zeroes to the left, and as we shall see that won't matter. The important thing is that all bits corresponding to our spells are set to 1.
Let's make the affect list be
Affected: ( 131) shield
Affected: ( 625) fly

When the first line fires the trigger:
allSpells = %bitxor(@allSpells, @shield), which is: %bitxor(0000 1111, 0000 0010)

15: 0000 1111
_2: 0000 0010
------------------- bitXOR
_r: 0000 1101
as such, the shield flag is set to 0. All other spells remains.
After the line with fly has triggered the result (without leading 0s) is
1100. In other words ogre and sagacity should be cast.

Now, to make effective use of these flags another we need another set of variable for spells:
spell1 = fly
spell2 = shield
spell4 = ogre
spell8 = sagacity

and to cast the spells:
Code:

currentSpell = 1
#while ( @currentSpell <= @spellSum) {
  #if (%bitand(@allSpells, @currentSpell)) {
      cast @{spell@currentSpell}
  }
  currentSpell = @currentSpell * 2
}


first pass:
%bitand(1100, 0001) - false
third pass:
%bitand(1100, 0100) - true
cast @{spell@currentSpell} -> cast @spell4 -> cast ogre

and so on...
Reply with quote
Fitzwilliam
Beginner


Joined: 24 Aug 2006
Posts: 11

PostPosted: Wed Aug 30, 2006 8:15 pm   
 
I have the core of the concept working right now. You can see now how paltry my skills are. This is the best I can do so far :O

OVERVIEW: You type AF to check what spells are active/inactive and you type SPELLUP to have all inactive spells cast on you. Here is the components using only two spells: fly and stone skin.

ALIASES

AF Alias has the value:

Code:
fly=1
stoneskin=1
#send affect


Explanation: this alias sets all spells as inactive (1) and then sends the affect command to the mud. The pattern trigger then picks up any active spells and sets them active (0)

SPELLUP Alias has the value:

Code:
#IF (@fly) {c fly}
#IF (@stoneskin) {c stone}


Explanation: this alias simply casts any spell that is inactive (equal to 1) and ignores active spells.

TRIGGER

Code:
Pattern Trigger: Affected:%s~(%s%d~)%s(%w)(*) Value: #var %concat( %1, %2) 0


Explanation: This trigger reads the output from the Affect command (example):

Affected: ( 221) stone skin
Affected: ( 33) fly

And the trigger turns active spells into a variable of the same name. In the above, the ouput from the affect command would create two variables, fly and stoneskin (note the space is gone to be used as a variable) and set their value as 0.

Questions: this manner of doing this creates a variable and assigns a 0 for every single spell in the affected list. This is a big problem because when you have Mage and Cleric spells on you the list of variables is going to get huge. How can I process my personal spell list and ignore any others to avoid creating superfluous variables with this trigger?

And secondly, what can I do to improve this basic setup (and its performance) before I move on to all of the suggestions you made previously? Possibly using ADDITEM?
Reply with quote
Qiz
Novice


Joined: 14 Aug 2006
Posts: 34
Location: Sweden

PostPosted: Thu Aug 31, 2006 3:02 pm   
 
Fitzwilliam wrote:
Questions: this manner of doing this creates a variable and assigns a 0 for every single spell in the affected list. This is a big problem because when you have Mage and Cleric spells on you the list of variables is going to get huge. How can I process my personal spell list and ignore any others to avoid creating superfluous variables with this trigger?

Two options as I see it.
1. keep a string list for all cleric spells, or a string list for all mage spells, depending on which is shorter.
for examle, check %ismember(spell, @magelist)
2. If you go with my suggestion of using bit flags for all spells, you're almost done allready. You only use flags for spells you will be recasting. If you want do take some other action on a mage when a cleric spell wears off (such as quaff sanc) just make sure all mage spells use the low bits and all clerics use the high bits. The reverse works as well of course, the important thing is to keep them separated. Also, any spells shared by the two classes go in the middle flags.
Assume you have 4 mage spells, 2 cleric spells. Mage spells get the low bits, cleric spells the high:
Spell values:
fly = 1
shield = 2
mystic = 4
armor = 8
sanctuary = 16

Now, whenever you check to see if a missing spell should be recast
(on a mage char)
if (somespell < 16) {cast it} {possibly do something else}
(n a cleric char)
if (somespell > 4) {cast it}

Note that if you go with your solution where you have one variable for each spell use other names for the above, vars like flyval, shieldval etc. Also, the above values no longer have to correspond to individual bits and you could go with
flyval = 1
shieldval = 2
mysticval = 3
armorval = 4
sanctuaryval = 5
and the mage check would be
if (somespell < 5) instead.

and then the trigger that captures the spell from affected list can make use of these variables like:
#IF (@{{%1}{val}) ...
which will expand as:
@{{fly}{val}}
@{flyval}
@flyval
1


Fitzwilliam wrote:
And secondly, what can I do to improve this basic setup (and its performance) before I move on to all of the suggestions you made previously? Possibly using ADDITEM?

Most notable performance improvement would be to use the bitflag solution I suggested, and I strongly recommend it.
If you for some reason don't want do go with that solution, at least get rid of all those spell variables and the related if checks you will have to do and instead go with a string list:
create a complete string list of all spells
for a mage:
allspells = fly|shield|mystic|armor|
for a cleric
allspells = armor|sanctuary

for each spell you capture in the affected list
delitem(somespell, @allspells)
After the affected list has been processed, go through all spells in the list. Just cast the first spell in the list and remove it if successful.


Fitzwilliam wrote:
I have the core of the concept working right now. You can see now how paltry my skills are. This is the best I can do so far :O

I think it looks good. If you wish to increase the level of automation all you have to do is add triggers to deal with the different aspects, one by one, like spell wear off triggers etc. And by adding piece after piece makes it easier to find mistakes as there is less new code to go over if it doesn't work as intended.
Reply with quote
Display posts from previous:   
Post new topic   Reply to topic     Home » Forums » zMUD General Discussion All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

© 2009 Zugg Software. Hosted by Wolfpaw.net