PDA

View Full Version : Learn how to script with this simple hunting script



Tgo01
05-25-2015, 04:57 PM
I've seen a lot of posts lately from people who are unable to get Bigshot working exactly the way they want it to and some people have expressed that they wish they knew how to code in Ruby so they can write their own scripts so I'm posting up this simple hunting script as a teaching exercise to those who want to learn more about coding in lich.

Feel free to ask any questions about anything you don't understand. If you don't know much about coding in lich then one day you just might be able to write your very own custom hunting that does everything exactly the way you want it to do.

Here is the script with a few features to get you started:



target_names = /kobold|squirrel|rolton/

$my_target = GameObj.npcs.find{ |npc| npc.name =~ target_names && npc.type =~ /aggressive/ }


if $my_target == nil
echo "No target found!"
exit
end

def this_script_hide_me
until hidden?
waitrt?
fput "hide"
end
end

def this_script_stand_me
until standing?
waitrt?
fput "stand"
end
end

def this_script_keep_haste
Spell[506].cast if (!Spell[506].active?) && (Spell[506].affordable?) && (Spell[506].known?)
end

def this_script_stun_target
Spell[505].cast($my_target) if (Spell[505].affordable?) && (Spell[505].known?) && ($my_target.status !~ /stun/)
end

def this_script_kill_target
Spell[901].cast($my_target) if (Spell[901].affordable?) && (Spell[901].known?)
end

def this_script_ambush_target
critter_needs_to_be_legged = true
waitrt?
if ($my_target.status =~ /prone|sit|lay|kneel|stun|sleep/i)
result = dothistimeout "ambush ##{$my_target.id} head", 2, /round(time)?|You cannot aim that high|does not have a/i
if result =~ /round(time)?/i
critter_needs_to_be_legged = nil
elsif result =~ /does not have a/
critter_needs_to_be_legged = nil
fput "attack ##{$my_target.id}"
elsif result =~ /You cannot aim that high/
nil
elsif result.nil?
nil
end
end
if (critter_needs_to_be_legged)
result = dothistimeout "ambush ##{$my_target.id} left leg", 2, /round(time)?|does not have a/i
if result =~ /round(time)?/i
nil
elsif result =~ /does not have a/
fput "attack ##{$my_target.id}"
elsif result.nil?
fput "attack ##{$my_target.id}"
end
end
end

start_script "loot" if !running? "loot"

loop{
break if ($my_target.status =~ /dead|gone/)
this_script_keep_haste
this_script_stand_me
this_script_stun_target
this_script_kill_target
sleep 1
}

if $my_target.status =~ /dead/
echo "Target dead!"
waitrt?
start_script "waitloot"
wait_while { running?('waitloot') }
elsif $my_target.status =~ /gone/
echo "Target gone!"
end


Here is that same code with some information:



###This is a regex where you put the names of critters you want the script to scan the current room for to kill.
target_names = /kobold|squirrel|rolton/

###This tells the script to set the variable $my_target to any NPC in the room that matches the target_names above and that the NPC is also aggressive.
$my_target = GameObj.npcs.find{ |npc| npc.name =~ target_names && npc.type =~ /aggressive/ }

###This tells the script to exit if no target was found.
if $my_target == nil
echo "No target found!"
exit
end

###This is a simple way to make sure your character keeps inputting HIDE until they are hidden. If this is run while you are already hidden then it will do nothing.
def this_script_hide_me
until hidden?
waitrt?
fput "hide"
end
end

###Same as the hide above except it makes sure you are standing before moving on.
def this_script_stand_me
until standing?
waitrt?
fput "stand"
end
end

###This checks to see if haste (506) is not active on your character, that your character has enough mana to cast 506, and that your character knows 506. If all of those are true it will cast 506 on yourself.
def this_script_keep_haste
Spell[506].cast if (!Spell[506].active?) && (Spell[506].affordable?) && (Spell[506].known?)
end

###This checks if your character has enough mana to cast Hand of Tonis (505) and that your character knows 505 and that your target is not already stunned. If all of those are true it will cast 505 on your target.
def this_script_stun_target
Spell[505].cast($my_target) if (Spell[505].affordable?) && (Spell[505].known?) && ($my_target.status !~ /stun/)
end

###Basically same as above except it doesn't check for stun status and this is checking for Minor Shock (901)
def this_script_kill_target
Spell[901].cast($my_target) if (Spell[901].affordable?) && (Spell[901].known?)
end

###This first sets the variable critter_needs_to_be_legged to true (which basically just means the variable has a value).
###Your character will then wait for any hard roundtime they are in, if they aren't in roundtime it will do nothing there.
###It will then check if your current target is prone, sitting, kneeling, or stunned, if any of that is true your character will then attempt to ambush the target's head.
###If your character ambushed the head the script sets critter_needs_to_be_legged to nil (which basically means the variable has no value) and moves on.
###If your character cannot aim that high then the script moves on.
###If for some reason the critter doesn't have a head then the script set critter_needs_to_be_legged to nil and your character will just do a simple attack on the target.
###If the script does not see "roundtime", "you cannot aim that high" "does not have a" in a 2 second timeframe then the script simply moves on.
###If critter_needs_to_be_legged is set to true then your character will attempt to ambush the critter's left leg.
###If your character attacked the leg (and thus you see a "roundtime") then the script will move on.
###If your target does not have a leg then your character will do a simple attack on the target.
###If the script doesn't see "roundtime" or "does not have a" in a 2 second time frame then your character will do a simple attack on the target.
def this_script_ambush_target
critter_needs_to_be_legged = true
waitrt?
if ($my_target.status =~ /prone|sit|lay|kneel|stun|sleep/i)
result = dothistimeout "ambush ##{$my_target.id} head", 2, /round(time)?|You cannot aim that high|does not have a/i
if result =~ /round(time)?/i
critter_needs_to_be_legged = nil
elsif result =~ /does not have a/
critter_needs_to_be_legged = nil
fput "attack ##{$my_target.id}"
elsif result =~ /You cannot aim that high/
nil
elsif result.nil?
nil
end
end
if (critter_needs_to_be_legged)
result = dothistimeout "ambush ##{$my_target.id} left leg", 2, /round(time)?|does not have a/i
if result =~ /round(time)?/i
nil
elsif result =~ /does not have a/
fput "attack ##{$my_target.id}"
elsif result.nil?
fput "attack ##{$my_target.id}"
end
end
end

###This tells the script to start the script "loot" if "loot" is not already running. You are running loot, right?
start_script "loot" if !running? "loot"

###This is where the magic happens. This is where you tell the script what coommands you want it to do and when. If your target is dead or no longer in the room with you then the loop is broken and it moves to the bit of code beyond the loop.
loop{
break if ($my_target.status =~ /dead|gone/)
this_script_keep_haste
this_script_stand_me
this_script_stun_target
this_script_kill_target
sleep 0.1
}

###If your target is dead then the script will wait until you're out of roundtime if you're in it then it will start "waitloot" then it will wait until "waitloot" is done running. Once all of that is done the script exits.
if $my_target.status =~ /dead/
echo "Target dead!"
waitrt?
start_script "waitloot"
wait_while { running?('waitloot') }
####This is if your target simply left before you could kill it. It just exits.
elsif $my_target.status =~ /gone/
echo "Target gone!"
end


So here are some ways to setup the loop part of the script with what's already in the script.



loop{
break if ($my_target.status =~ /dead|gone/)
this_script_keep_haste
this_script_stand_me
this_script_stun_target
this_script_kill_target
sleep 0.1
}


This tells the script to run the keep haste part of the script, then it will run the stand me part, then it will run the stun target, then it will run kill target. It will keep doing this in this exact order over and over again until your target is dead or gone.

Basically it will cast haste if your character needs it, it will then stand your character up if they are not already standing, it will then cast Hand of Tonis on your target if your target is not already stunned, then it will cast 901 on your target. Once it has done all of that the script will then check if your target is dead or gone, if it's not dead or gone it starts all over again.

You could also do this:



loop{
break if ($my_target.status =~ /dead|gone/)
this_script_stand_me
this_script_hide_me
this_script_ambush_target
sleep 0.1
}


This will make sure you're standing if you're not already standing, then it will hide you if you're not already hidden, then it will attempt to ambush your target's head if your target is not standing or stunned, if your target isn't prone or stunned or if you can't aim for its head then it will ambush your target's left leg. It will then repeat this process until your target is dead or gone.

Feel free to ask any questions or if you want your script to do something else not already mentioned here.

Tgo01
05-25-2015, 05:47 PM
Other fun things you can do:



$my_weapon = "runestaff"




def this_script_get_my_weapon
if (GameObj.right_hand !~ /#{$my_weapon}/)
empty_right_hand
if (GameObj.left_hand =~ /#{$my_weapon}/)
fput "swap"
else
fput "get my #{$my_weapon}"
end
end
end


Run this method and if your weapon is in your right hand then it does nothing. If your weapon is not in your right hand it will stow whatever is in your right hand, then the script checks your left hand and if your weapon is in your left hand it will do "swap" otherwise it will get your weapon in case it's been stowed or what have you.

WRoss
05-25-2015, 06:23 PM
So, how could I use this to run commands on other scripts. For example, if my weapon gets disarmed to ";send to crosschar wall|char1|char2 get weapon|incan 410"

Tgo01
05-25-2015, 07:08 PM
So, how could I use this to run commands on other scripts. For example, if my weapon gets disarmed to ";send to crosschar wall|char1|char2 get weapon|incan 410"

Sorry I'm not sure I understand what you're asking.

You're asking if you get disarmed on character A you want to send a command to the script crosschar of "wall|char1|char2 get weapon|incan 410"?

Tenlaar
05-25-2015, 07:31 PM
Ok, you've inspired me to finally start working on my own personalized hunting script. I have a possibly tough one to start with. Pulling lines from the game and acting based on that info is one area that I never got too far into when I was teaching myself to write scripts.

I'm a bard, and I want to use 1002 any and every time my target has a weapon or shield equipped. I assume the way to go about that would be to look at the target before every attack and check it through that somehow while squelching so that I don't have to see me looking before every attack.

WRoss
05-25-2015, 07:51 PM
Sorry I'm not sure I understand what you're asking.

You're asking if you get disarmed on character A you want to send a command to the script crosschar of "wall|char1|char2 get weapon|incan 410"?

Sort of.

Char A gets disarmed and immediately sends "send to crosschar charB get weapon"

Tgo01
05-25-2015, 08:38 PM
I'm a bard, and I want to use 1002 any and every time my target has a weapon or shield equipped. I assume the way to go about that would be to look at the target before every attack and check it through that somehow while squelching so that I don't have to see me looking before every attack.

Yeah I'm not aware of a way to see if a critter is holding a weapon or shield without looking at them first. If you want don't want the game lines from looking at the critter to show you're going to need to use Downstreamhooks.

Here is an example of how this would work:



target_names = /kobold|squirrel|rolton|orc/

$my_target = GameObj.npcs.find{ |npc| npc.name =~ target_names && npc.type =~ /aggressive/ }
critter_has_shield_or_weapon = nil


silence_the_look = proc{
action = proc { |server_string|
if server_string =~ /You see a fairly typical .*\.|.* appears to be in good shape\./
nil
elsif server_string =~ /.* has nothing at this time\./
critter_has_shield_or_weapon = "no"
DownstreamHook.remove("#{script.name}_silence_the_look")
nil
elsif server_string =~ /.*(He|She|It).* has a/
critter_has_shield_or_weapon = "no"
all_gear_list = server_string.split (/,| and /)
all_gear_list.each { |i|
if i !~ /\(worn\)/
critter_has_shield_or_weapon = "yes"
end
}
nil
else
server_string
end
}
critter_has_shield_or_weapon = nil
DownstreamHook.add("#{script.name}_silence_the_look", action)
silence_me
fput "look ##{$my_target.id}"
silence_me
}

before_dying { DownstreamHook.remove("#{script.name}_silence_the_look") }

silence_the_look.call
wait_until { critter_has_shield_or_weapon }
if (critter_has_shield_or_weapon == "yes") && (Spell[1002].affordable?) && (Spell[1002].known?)
Spell[1002].cast($my_target)
end


Here is the same code with some explanations as to what's going on:



target_names = /kobold|squirrel|rolton|orc/

$my_target = GameObj.npcs.find{ |npc| npc.name =~ target_names && npc.type =~ /aggressive/ }

critter_has_shield_or_weapon = nil

###Below is the procedure you would call whenever you want the script to see if the critter is holding a weapon or shield.
silence_the_look = proc{
action = proc { |server_string|
###The line below is where you put all game lines you don't want to show up while looking at a critter. Separate all game lines with a pipe symbol |
if server_string =~ /You see a fairly typical .*\.|.* appears to be in good shape\./
nil
###This is an example of a critter who is wearing nothing so the script sets the variable critter_has_shield_or_weapon to "no" since the critter has no gear at all. The nil at the end also means this game line won't show.
elsif server_string =~ /.* has nothing at this time\./
critter_has_shield_or_weapon = "no"
DownstreamHook.remove("#{script.name}_silence_the_look")
nil
###The following game line indicates the critter is wearing stuff so it sets the variable of critter_has_shield_or_weapon to no first in case the stuff it has is not a weapon or a shield.
elsif server_string =~ /.*(He|She|It).* has a/
critter_has_shield_or_weapon = "no"
###The following line take the game line it saw and divides it whenever it sees "," or " and " into an array.
all_gear_list = server_string.split (/,| and /)
###The following tells the script to go through each item it divided above and it calls each item the letter i.
###The script then checks if (worn) shows up in i, if (worn) does not show up it means the item is being held in the critter's hand and thus should be 1002able
all_gear_list.each { |i|
if i !~ /\(worn\)/
critter_has_shield_or_weapon = "yes"
end
}
nil
###The following just means if none of the above matches a game line then it simply sends the game line through without doing anything. This is so you don't miss any whispers or lnet message or whatnot.
else
server_string
end
}
critter_has_shield_or_weapon = nil
DownstreamHook.add("#{script.name}_silence_the_look", action)
###The silence_me feature toggles seeing the script input commands.
silence_me
fput "look ##{$my_target.id}"
silence_me
}

###This removes the Downstreamhook before the script is killed so you're not accidentally stuck with downstreamhooks when you don't want them.
before_dying { DownstreamHook.remove("#{script.name}_silence_the_look") }

###The following line calls up our look command.
silence_the_look.call
###The following line waits until the variable critter_has_shield_or_weapon has a value.
wait_until { critter_has_shield_or_weapon }
###The following line tells the script to cast 1002 at the target if the variable critter_has_shield_or_weapon has a value of "yes"
if (critter_has_shield_or_weapon == "yes") && (Spell[1002].affordable?) && (Spell[1002].known?)
Spell[1002].cast($my_target)
end

Tgo01
05-25-2015, 08:50 PM
Sort of.

Char A gets disarmed and immediately sends "send to crosschar charB get weapon"

If char A gets disarmed you want to have char B pick up char A's weapon?

send_to_script 'crosschar', 'charb get #$my_weapon'

Assuming you're talking about using the variable I mentioned earlier.

Soulance
05-25-2015, 11:13 PM
This is really cool Tgo, thanks for starting this. I'm going to really have to read through this to figure it out. What I notice is all of the indents and whatnot. Does that mean things are a part of another object when they are indented? Also, does lich have a specific set of codewords that it uses as a program? Is that part of the Obj.data.xml file or whatever it's called?

Sorry, but I might be asking quite a few questions along the way...

Tgo01
05-25-2015, 11:56 PM
What I notice is all of the indents and whatnot. Does that mean things are a part of another object when they are indented?

The indents just make the code a lot easier to read. Like if you're writing an if/else statement then you start the statement with if and end it with end, so you would write it like:



if a != b
if a == b
echo "Wait, what am I saying?"
else
echo "no idea"
end
else
if b == c
echo "Huh?
elsif 1 == 2
echo "1 can't equal 2!"
else
echo "Could you repeat that?"
end
end


This way you can easily see which else/elsif/end belong to which if statements, you can also easily tell which code falls under which if/else statement.

Likewise the following code would work just as well but if you ever want to edit the code in the future it might be a bit confusing:



if a != b
if a == b
echo "Wait, what am I saying?"
else
echo "no idea"
end
else
if b == c
echo "Huh?
elsif 1 == 2
echo "1 can't equal 2!"
else
echo "Could you repeat that?"
end
end



Also, does lich have a specific set of codewords that it uses as a program? Is that part of the Obj.data.xml file or whatever it's called?

I'm not sure what you're asking.


Sorry, but I might be asking quite a few questions along the way...

Questions are welcomed and encouraged! How else is one supposed to learn?

Donquix
05-26-2015, 12:11 AM
The indents just make the code a lot easier to read. Like if you're writing an if/else statement then you start the statement with if and end it with end, so you would write it like:



if a != b
if a == b
echo "Wait, what am I saying?"
else
echo "no idea"
end
else
if b == c
echo "Huh?
elsif 1 == 2
echo "1 can't equal 2!"
else
echo "Could you repeat that?"
end
end


This way you can easily see which else/elsif/end belong to which if statements, you can also easily tell which code falls under which if/else statement.

Likewise the following code would work just as well but if you ever want to edit the code in the future it might be a bit confusing:



if a != b
if a == b
echo "Wait, what am I saying?"
else
echo "no idea"
end
else
if b == c
echo "Huh?
elsif 1 == 2
echo "1 can't equal 2!"
else
echo "Could you repeat that?"
end
end




I'm not sure what you're asking.



Questions are welcomed and encouraged! How else is one supposed to learn?

I think he's asking about functions. to which the answer is, yes. They're defined in lich.rbw itself. Look for any lines that begin with "def name_of_some_function_here" or similar. That's where you would find things like where the code for "checkstamina" or "percentmind" (functions you can call in scripts) are.

http://www.howtogeek.com/howto/programming/ruby/ruby-function-method-syntax/

Tgo01
05-26-2015, 12:16 AM
Another thing you can do:



def this_script_cloud_go_away
GameObj.loot.each{ |i|
if i.noun =~ /cloud/
if (Spell[612].affordable?) && (Spell[612].known?)
Spell[612].cast
break
elsif (Spell[417].affordable?) && (Spell[417].known?)
Spell[417].cast(i)
elsif (Spell[1218].affordable?) && (Spell[1218].known?)
Spell[1218].cast(i)
elsif (Spell[119].affordable?) && (Spell[119].known?)
Spell[119].cast(i)
end
end
}
end


Put this in your loop and if there are no clouds in the room with you then great! Nothing will happen.

Otherwise if you know Breeze (612) and have enough mana for it it will cast breeze to get rid of the clouds, otherwise it goes through all of the dispels (cheaper ones first) and if you know that dispel and have enough mana to cast it it will cast that dispel on each cloud it finds.

Tgo01
05-26-2015, 12:21 AM
I think he's asking about functions.

Ah I see. Yeah, what Donquix said :p

You don't have to know all of that stuff to code your own script, but it sure as hell makes things easier. There is a lot of stuff in there I didn't even know existed, like just yesterday I found out about the empty_right_hand and emptyhands definitions.

Tgo01
05-26-2015, 06:55 PM
More fun things to do with your script:



def this_script_stay_down
GameObj.npcs.any? { |npc|
if (npc.status !~ /prone|sit|lay|kneel|stun|sleep/ && npc.type =~ /aggressive/) && (GameObj.pcs.length == nil)
Spell[410].cast if (Spell[410].known?) && (Spell[410].affordable?)
end
}
end

def this_script_help_people_up
this_script_stand_me
GameObj.pcs.each { |pc|
if pc.status =~ /prone|sit|lay|kneel/ && pc.status !~ /dead/
waitrt?
fput "pull #{pc}"
end
}
end


Run this_script_stay_down first and it will cast elemental wave only if you have enough mana for it and you know the spell 410 and if there is an aggressive NPC in the room that is not already prone, kneeling, sitting or stunned and there are no people in the room.

Run this_script_help_people_up right after that and it will pull up anyone in the room who is not already standing, y'know, just in case they entered the room during that millisecond before you actually cast the spell, you know how that seems to happen all the time.

this_script_help_people_up also makes use of the definition this_script_stand_me mentioned in the original post to make sure you yourself are standing first before attempting to help anyone else up.

Soulance
05-26-2015, 09:08 PM
I think he's asking about functions. to which the answer is, yes. They're defined in lich.rbw itself. Look for any lines that begin with "def name_of_some_function_here" or similar. That's where you would find things like where the code for "checkstamina" or "percentmind" (functions you can call in scripts) are.

http://www.howtogeek.com/howto/programming/ruby/ruby-function-method-syntax/
Yep, you got it here. Thank you. I was wondering if there was a set of built-in words to make the codes associate and whatnot. This looks like fun (and a bit confusing) but I'm definitely interested.

Thanks again for putting this up and keep rolling with it. I'm watching!

Soulance
05-26-2015, 09:09 PM
this_script_help_people_up
So....could something like this, tuned up a bit, help like ;helpfolks? (Which seems to be broken for me at the moment as well.)

Tgo01
05-26-2015, 09:17 PM
So....could something like this, tuned up a bit, help like ;helpfolks? (Which seems to be broken for me at the moment as well.)

Does it not work at all?

Only thing I could see possibly being an issue is it only does a check every 3 seconds so if you're passing by someone who needs help during this 3 second pause it won't work. You could try tuning the pause down to like 0.5 or even 0.1 seconds.

You want some sort of pause in a loop otherwise your game might crash. I've found that 0.1 seconds usually prevents this problem, if it's taxing your computer too much (which it shouldn't) you could try upping it to 0.2 or 0.5.

Tgo01
05-26-2015, 10:01 PM
$run_away_if_more_than_this_number_of_critters = 1

def this_script_check_number_of_critters
npc_count = 0
GameObj.npcs.each{ |npc| npc_count += 1 if (npc.status !~ /dead/) && (npc.type =~ /aggressive/) }
if npc_count > $run_away_if_more_than_this_number_of_critters
echo "Too many critters here!"
exit
end
end


Set the $run_away_if_more_than_this_number_of_critters variable and run this_script_check_number_of_critters during your loop and the script will add up the number of NPCs in the room that aren't dead and are aggressive (so it will ignore pets) and if the number of NPCs in the room is greater than the number you set in the $run_away_if_more_than_this_number_of_critters variable then the script will run the code listed. Right now it's just set to telling you there are too many critters in the room and exiting the script. Ideally you would have this break the kill part of the loop which will itself be in a loop that would move you to another room and keep moving you until it finds a room that doesn't have so many critters in it.

Soulance
05-27-2015, 06:48 AM
Does it not work at all?

Only thing I could see possibly being an issue is it only does a check every 3 seconds so if you're passing by someone who needs help during this 3 second pause it won't work. You could try tuning the pause down to like 0.5 or even 0.1 seconds.

You want some sort of pause in a loop otherwise your game might crash. I've found that 0.1 seconds usually prevents this problem, if it's taxing your computer too much (which it shouldn't) you could try upping it to 0.2 or 0.5.
Eh, it 'kinda' works here and there but I think you're right about it just being a timing thing. By the time it's doing what it is supposed to do the person is already out of the stun. Seems like it would start to prepare the spell, then miscast, then prepare it again, miscast. I'll have to go back and test a little more again because I stopped using it. It was nice for bandits though.

Iovan
05-27-2015, 01:09 PM
target_names = /kobold|squirrel|rolton/

$my_target = GameObj.npcs.find{ |npc| npc.name =~ target_names && npc.type =~ /aggressive/ }



I'm guessing the way this script is written, that the first npc that it finds that is on the list at all will become the target. Is a smart targeting setup outside the scope of this tutorial? I'm thinking that a better way would be to make a loop that will check all npcs for the first item on the list before checking for the second.

Soulance
05-27-2015, 02:21 PM
I'm guessing the way this script is written, that the first npc that it finds that is on the list at all will become the target. Is a smart targeting setup outside the scope of this tutorial? I'm thinking that a better way would be to make a loop that will check all npcs for the first item on the list before checking for the second.
Something to possibly tack on to this if you're not already saying it, but something I always thought would be good for ;bigshot would be the ability to make an order you want things killed. It seems pretty random, but let's say you're fighting the Shan in Sol and want to take out those Rangers first that always spike you in the head...

Tgo01
05-27-2015, 02:32 PM
Eh, it 'kinda' works here and there but I think you're right about it just being a timing thing. By the time it's doing what it is supposed to do the person is already out of the stun. Seems like it would start to prepare the spell, then miscast, then prepare it again, miscast. I'll have to go back and test a little more again because I stopped using it. It was nice for bandits though.

Hmm, try reducing the pause and see if that works. If not I can look at the code again.


I'm guessing the way this script is written, that the first npc that it finds that is on the list at all will become the target. Is a smart targeting setup outside the scope of this tutorial? I'm thinking that a better way would be to make a loop that will check all npcs for the first item on the list before checking for the second.



target_names = [ "rolton", "squirrel", "kobold", "orc" ]

$my_target = nil

target_names.each{ |i|
$my_target = GameObj.npcs.find{ |npc| npc.name =~ /#{i}/ && npc.type =~ /aggressive/ && npc.status !~ /dead/ }
break if $my_target
}



Something to possibly tack on to this if you're not already saying it, but something I always thought would be good for ;bigshot would be the ability to make an order you want things killed. It seems pretty random, but let's say you're fighting the Shan in Sol and want to take out those Rangers first that always spike you in the head...

Yup, the above code would do that. Just list the critters in the order you want to kill them.

Of course this is just when you first run the script it will find and kill the first thing listed, if you start the script then a more dangerous critter enters the room while you're killing your current target and you would rather kill the new more dangerous critter before you finish killing your current target then that would require some additional coding.

Tgo01
05-27-2015, 05:40 PM
Here is something new:



$silence_targets = /kobold/
$already_silenced_targets = Array.new

def this_script_silence_them_all
GameObj.npcs.each{ |npc|
if (npc.name =~ $silence_targets) && (!$already_silenced_targets.include?(npc.id)) && (Spell[210].affordable?) && (Spell[210].known?)
Spell[210].cast(npc)
$already_silenced_targets.push(npc.id)
end
}

end


In case you want to silence them dangerous kobolds from casting terrible spells at you you could set up your script like this. Define which dangerous critters you want silenced in the $silence_targets then add the this_script_silence_them_all definition to your loop.

Since silence doesn't show up as a status effect on the critter you have no way of knowing which critters you have already cast silence on, this definition keeps track of that. Every time it is run it will go through the list of critters in the room and if their name matches what you have set in the $silence_targets it will then check to see if that particular critter's id is part of the $already_silenced_targets array. If the id is not part of the array then it will cast silence on that critter then add that critter's id to the array (that's what push basically does). If the script comes along this particular critter again it will see that its id is already part of the $already_silenced_targets array and ignore it.

This would also check to make sure you have enough mana to cast silence and if you don't then it won't cast silence nor will it add the critter's id to the array of ids to check for.

Keep in mind every time the script is started it sets the $already_silenced_targets to nothing so it starts over again. You could change this so it doesn't set it to nil so it keeps track of the critters id even if the script stops and you start it again.

You could even get fancy and have the script automatically remove dead critters from the array.

Also keep in mind this definition is not setup to see if you were successful in your cast or if the silence wore off the critter, you could set up a script that would automatically check for these things as well.

Jawa
06-07-2015, 07:13 PM
would i be able to have the script target a specific limb with 708?


def this_script_stay_down
GameObj.npcs.any? { |npc|
if (npc.status !~ /prone|sit|lay|kneel|stun|sleep/ && npc.type =~ /aggressive/) && (GameObj.pcs.length == nil)
(Spell[708].cast left leg) if (Spell[708].known?) && (Spell[708].affordable?)
end
}
end

Tgo01
06-07-2015, 07:29 PM
You can use the aim verb for 708 so you could do...



def this_script_stay_down
fput "aim left leg"
GameObj.npcs.any? {|npc|
if (npc.status !~ /prone|sit|lay|kneel|stun|sleep/ && npc.type =~ /aggressive/)
Spell[708].cast($my_target) if (Spell[708].affordable?) && (Spell[708].known?)
end
}
end

Jawa
06-07-2015, 07:46 PM
Awesome, now last question, how would i get target names to be filled with what i enter at the time of the script? i.e ;ld radical combantant

Tgo01
06-07-2015, 07:57 PM
Awesome, now last question, how would i get target names to be filled with what i enter at the time of the script? i.e ;ld radical combantant

$my_target = "#{script.vars[1]}"

You would start the script the the critter's noun then, like ;ld combantant

Jawa
06-07-2015, 07:58 PM
I would leave the target_names = [ "rolton", "squirrel", "kobold", "orc" ] part?

Tgo01
06-07-2015, 07:59 PM
I would leave the target_names = [ "rolton", "squirrel", "kobold", "orc" ] part?

If you're not gonna use that part then it doesn't matter.

Jawa
06-07-2015, 08:00 PM
no no i would remove that and the target_names.each part too right?

Tgo01
06-07-2015, 08:03 PM
no no i would remove that and the target_names.each part too right?

You could either remove all of that or put

$my_target = "#{script.vars[1]}"

After all of it and it will work fine either way.

Jawa
06-07-2015, 08:08 PM
assume that i am building this from the original posted script :)

Tgo01
06-07-2015, 08:12 PM
assume that i am building this from the original posted script :)

Turn this part:



target_names = /kobold|squirrel|rolton/

$my_target = GameObj.npcs.find{ |npc| npc.name =~ target_names && npc.type =~ /aggressive/ }


Into this:



target_names = /#{script.vars[1]}/

$my_target = GameObj.npcs.find{ |npc| npc.name =~ target_names && npc.type =~ /aggressive/ }


My bad. I should have refreshed my memory of how this script worked before replying :p

This should work.

Jawa
06-07-2015, 08:48 PM
its only running this_script_kill_target twice before trying to blow a leg off again?

Tgo01
06-07-2015, 08:55 PM
its only running this_script_kill_target twice before trying to blow a leg off again?

Is the critter prone? You have it set up so it will cast 708 at any critter in the room that isn't prone or stunned.

Jawa
06-07-2015, 09:06 PM
ah ha! I figured it out! I took out the this_script_stay_down from the loop. Put it in front of the loop so it only does it once then loops the stand/attack scripts! I R LERN'D

Jawa
06-07-2015, 09:17 PM
would go great with

if (playeristhief (candor)
killplayer (candor)

Tgo01
06-07-2015, 09:18 PM
ah ha! I figured it out! I took out the this_script_stay_down from the loop. Put it in front of the loop so it only does it once then loops the stand/attack scripts! I R LERN'D

Woot!


If it were up to me, here's part of a script I might create...

if (playeriscripting (character))
killplayer (character)



GameObj.pcs.each{ |pc|
if pc.name == "Candor"
fput "moon candor"
fput "slap candor"
end
}

Jawa
06-07-2015, 09:30 PM
$leg_targets = /#{script.vars[0]}/
$already_legged_targets = Array.new

def this_script_leg_them_all
GameObj.npcs.each{ |npc|
if (npc.name =~ $leg_targets) && (!$already_legged_targets.include?(npc.id)) && (Spell[708].affordable?) && (Spell[708].known?)
Spell[708].cast(npc)
$already_legged_targets.push(npc.id)
end
}

end

loop{
break if GameObj.npcs = nil <---- will this stop the script if no npc's are present?
this_script_leg_them_all
sleep 1
}

Tgo01
06-07-2015, 09:38 PM
When you use one = sign you are setting that variable to equal that.

So for example:

best_person_ever = "Dreaven"

Means you're setting the variable of best_person_ever to be Dreaven.

If you want to see if two things are equal to each other you use two = signs.

For example:



if best_person_ever == "Dreaven"
echo "He sure is!"
end


This looks to see if the variable best_person_ever is set to Dreaven, which it is because it's true.

In any event what you want is:

break if GameObj.npcs.length == nil

The length part checks to see how many objects are inside of the GameObj.npcs variable, if none then GameObj.length is set to nil, or nothing.

Keep in mind npcs checks for all npcs in the room; friendly npcs, familiars, hostile critters and etc.

Also in this fashion it doesn't care if the critter is dead or not, it still lists them as part of the GameObj.npcs.

Tgo01
06-07-2015, 10:07 PM
This would break the loop if none of your listed targets are in the room.

break if !GameObj.npcs.find{ |npc| npc.name =~ $my_target}

Tgo01
06-18-2015, 07:46 PM
More fun with scripting!

Want to use mstrike whenever you're out of the cooldown period and thus it doesn't cost stamina to use?

Try this!



def this_script_use_mstrike
fput "mstrike ##{$my_target.id}" if (!Spell[9005].active?) && (!Spell[9699].active?)
end


9005 is Lich's code for mstrike's cooldown period, 9699 is if popped muscles are active, thus this would check to see if both are active on your character and if they aren't active then the script would use mstrike on your target.

This is focused mstrike, you could get fancy and for example have the script check if there are two or more critters in the room then you would use open mstrike instead.

Gelston
06-18-2015, 07:48 PM
You best get back to scripting, bitch.

Tgo01
06-18-2015, 07:49 PM
You best get back to scripting, bitch.

:(

Androidpk
06-18-2015, 08:02 PM
http://www.inquisitr.com/wp-content/uploads/2014/09/WWE-Jim-Cornette-Blasts-Leaked-Monday-Night-RAW-Script-Says-Writers-Have-Ruined-Wrestling-665x385.jpg

Luxelle
09-09-2015, 01:26 AM
Tg01:

Thank you so much for that quick little example. I found this today, and I figured that your example looked like enough to get me started, and now my aching wrists are blessing your name.

Modified it for bard hunting, and VOILA! It just worked.

::happy dance::

~L.

Tgo01
09-09-2015, 02:14 AM
Tg01:

Thank you so much for that quick little example. I found this today, and I figured that your example looked like enough to get me started, and now my aching wrists are blessing your name.

Modified it for bard hunting, and VOILA! It just worked.

::happy dance::

~L.

Woo! Glad it helped.

elcidcannon
09-15-2015, 09:46 PM
This is an awesome thread.

Mad props, Tgo!

Tgo01
09-15-2015, 09:48 PM
This is an awesome thread.

Mad props, Tgo!

Yes. Stop that.

elcidcannon
09-15-2015, 09:53 PM
Yes. Stop that.

Ok......I lol'd

elcidcannon
09-15-2015, 10:14 PM
This is focused mstrike, you could get fancy and for example have the script check if there are two or more critters in the room then you would use open mstrike instead.

Now that I've got you buttered up, how would I do this? Can lich tell the room population without having to do a look?

Tgo01
09-15-2015, 10:21 PM
Can lich tell the room population without having to do a look?

Yes.

elcidcannon
09-15-2015, 10:24 PM
Where could I find a list of the functions that lich uses to query the game?

Tgo01
09-15-2015, 10:26 PM
Where could I find a list of the functions that lich uses to query the game?

There are some functions here:

https://gswiki.play.net/mediawiki/index.php/Lich_scripting_reference

What you could do I think is:



if GameObj.npcs.length > 2
fput "do stuff"
end


And change the number to whatever. Use your grade school learning of greater than and less than, which number would the duck rather eat?

elcidcannon
09-15-2015, 11:13 PM
Awesome....I'm on the right track, I believe.

I'm having difficulty with multiple targets. It seems as if I'm only targeting one creature at a time, and have to re-run the script if there are two or more of the listed targets in the room.

Could I make a "targets?" script to go first inside of the loop, that exits if null and continues if 1 or more?

Tgo01
09-15-2015, 11:21 PM
I'm having difficulty with multiple targets. It seems as if I'm only targeting one creature at a time, and have to re-run the script if there are two or more of the listed targets in the room.

Yeah I think my example on the first post just deals with one critter then ends.


Could I make a "targets?" script to go first inside of the loop, that exits if null and continues if 1 or more?

You can do anything with Ruby that your mind can imagine.

bremerial
09-16-2015, 02:04 PM
Bloody useful post. Will scrutinise on weekend when time permits. Want to translate my stormfront scripts into lich.

SashaFierce
10-23-2015, 07:37 AM
Good stuff here!

Intel
10-30-2015, 06:40 PM
Is anyone else having problems with the initial code? I go to run the script and it just does this:

--- Lich: ambush active.
--- Lich: ambush has exited.

So it sees a target, it just doesn't engage.

Tgo01
10-30-2015, 07:52 PM
Is anyone else having problems with the initial code? I go to run the script and it just does this:

--- Lich: ambush active.
--- Lich: ambush has exited.

So it sees a target, it just doesn't engage.

Can you copy and paste the code you are using?

Intel
10-31-2015, 03:38 PM
target_names = /gnoll|hobgoblin|snowcat/

$my_target = GameObj.npcs.find{ |npc| npc.name =~ target_names && npc.type =~ /aggressive/
}


if $my_target == nil
echo "No target found!"
exit
end

def this_script_hide_me
until hidden?
waitrt?
fput "hide"
end
end

def this_script_stand_me
until standing?
waitrt?
fput "stand"
end
end

def this_script_keep_haste
Spell[506].cast if (!Spell[506].active?) && (Spell[506].affordable?) && (Spell[506].known?)
end

def this_script_stun_target
Spell[505].cast($my_target) if (Spell[505].affordable?) && (Spell[505].known?) && ($my_target.status !~

/stun/)
end

def this_script_kill_target
Spell[901].cast($my_target) if (Spell[901].affordable?) && (Spell[901].known?)
end

def this_script_ambush_target
critter_needs_to_be_legged = true
waitrt?
if ($my_target.status =~ /prone|sit|lay|kneel|stun|sleep/i)
result = dothistimeout "ambush ##{$my_target.id} head", 2, /round(time)?|You cannot aim that high|

does not have a/i
if result =~ /round(time)?/i
critter_needs_to_be_legged = nil
elsif result =~ /does not have a/
critter_needs_to_be_legged = nil
fput "attack ##{$my_target.id}"
elsif result =~ /You cannot aim that high/
nil
elsif result.nil?
nil
end
end
if (critter_needs_to_be_legged)
result = dothistimeout "ambush ##{$my_target.id} left leg", 2, /round(time)?|does not have a/i
if result =~ /round(time)?/i
nil
elsif result =~ /does not have a/
fput "attack ##{$my_target.id}"
elsif result.nil?
fput "attack ##{$my_target.id}"
end
end
end

start_script "sloot" if !running? "sloot"

loop{
break if ($my_target.status =~ /dead|gone/)
this_script_keep_haste
this_script_stand_me
this_script_stun_target
this_script_kill_target
sleep 1
}

if $my_target.status =~ /dead/
echo "Target dead!"
waitrt?
start_script "waitloot"
wait_while { running?('waitloot') }
elsif $my_target.status =~ /gone/
echo "Target gone!"
end


thanks!

Tgo01
10-31-2015, 03:54 PM
loop{
break if ($my_target.status =~ /dead|gone/)
this_script_keep_haste
this_script_stand_me
this_script_stun_target
this_script_kill_target
sleep 1
}

Going by the name of your script I assume you want to use the ambushing portion of the script but you aren't calling that part. You should change the above to:


loop{
break if ($my_target.status =~ /dead|gone/)
this_script_stand_me
this_script_ambush_target
sleep 1
}

Intel
11-02-2015, 06:11 PM
That worked, thank you.

SashaFierce
02-10-2016, 08:23 AM
def cmd_fire(target)
critter_needs_to_be_legged = true
waitrt?
if (target.status =~ /prone|sit|lay|kneel|stun|sleep/i)
fput "get 1 my arrow"
result = dothistimeout "fire ##{target.id} right eye", 2, /round(time)?/i
if result =~ /round(time)?/i
critter_needs_to_be_legged = nil
elsif result.nil?
nil
end
end
if (critter_needs_to_be_legged)
fput "get 1 my arrow"
result = dothistimeout "fire ##{target.id} right leg", 2, /round(time)?/i
if result =~ /round(time)?/i
nil
elsif result.nil?
fput "fire ##{$my_target.id} right leg"
end
end
end


>
>get 1 my arrow
You already have that.
>
[bigshot]>fire #19445912 right leg
I do not understand, please rephrase that.

[B]Changing FIRE to AMBUSH:

>get 1 my arrow
You already have that.
>
[bigshot]>ambush #19451339 right leg
You thrust with an arrow at a XXX!
AS: +101 vs DS: +141 with AvD: +4 + d100 roll: +26 = -10
A clean miss.
Roundtime: 6 sec.

[B]Why does it work differently for ranged?

0zymandius
02-10-2016, 10:41 AM
Since we're reviving this thread (which is amazing, and much appreciated):

How would I get a script to constantly monitor the game and only trigger on a series of specified incoming strings of text (from all sources; Room labels, player names, objects in the room)?

For instance:

loop {
#### Monitor the incoming feed at all times, assigning it to variable 'get'... is this the right way to do this?
get = server.string
if get == /GM/
fput "beg for RPA"
elsif get == /Scripting Lounge/
fput "say It was an accident"
elsif get == /keg/
fput "pour keg"
end
}

I feel like I'm in the general ballpark, but I wasn't able to get it to work the other day when tinkering, and I'm sure there are about a million more elegant ways to accomplish the same thing.

Thanks!

Tillmen
02-10-2016, 10:52 AM
while (line = get)
if line =~ /GM/
fput "beg for RPA"
elsif line =~ /Scripting Lounge/
fput "say It was an accident"
elsif line =~ /keg/
fput "pour keg" # this will probably cause the word keg to show up again, which is probably bad
end
end

0zymandius
02-10-2016, 03:10 PM
So... without current access to the game, but wanting to capitalize on the opportunity, is this the correct method for this sort of script? I bastardized something together based off of my memories of the ice sculpting game from BMC. (And it's not meant to run unattended, so I don't care about going for more silvers or containers filling up.)

Edited to add: Obviously I'd need the actual string where it would only trigger on my creations, not every one in the room, or anyone who mentioned one. I'm more curious if this is the right 'way' to go about it, to include waiting until something appears in the hand before proceeding to the next step.


def go
empty_right_hand
empty_left_hand
waitrt?
fput "get chisel"
waitrt?
end

while (line = get)
if line =~ /crushed ice/
go
elsif line =~ /carved ice sword/
waitrt?
wait_until (checkright)
fput "put right in crate"
go
elsif line =~ /carved ice tree/
waitrt?
wait_until (checkright)
fput "put right in crate"
go
elsif line =~ /carved ice rolton/
waitrt?
wait_until (checkright)
fput "put right in my pack"
go
elsif line =~ /majestic ice dragon/
waitrt?
wait_until (checkright)
fput "put right in my pouch"
go
end
end

Tgo01
02-10-2016, 04:55 PM
def cmd_fire(target)
critter_needs_to_be_legged = true
waitrt?
if (target.status =~ /prone|sit|lay|kneel|stun|sleep/i)
fput "get 1 my arrow"
result = dothistimeout "fire ##{target.id} right eye", 2, /round(time)?/i
if result =~ /round(time)?/i
critter_needs_to_be_legged = nil
elsif result.nil?
nil
end
end
if (critter_needs_to_be_legged)
fput "get 1 my arrow"
result = dothistimeout "fire ##{target.id} right leg", 2, /round(time)?/i
if result =~ /round(time)?/i
nil
elsif result.nil?
fput "fire ##{$my_target.id} right leg"
end
end
end


>
>get 1 my arrow
You already have that.
>
[bigshot]>fire #19445912 right leg
I do not understand, please rephrase that.

[B]Changing FIRE to AMBUSH:

>get 1 my arrow
You already have that.
>
[bigshot]>ambush #19451339 right leg
You thrust with an arrow at a XXX!
AS: +101 vs DS: +141 with AvD: +4 + d100 roll: +26 = -10
A clean miss.
Roundtime: 6 sec.

[B]Why does it work differently for ranged?

I'm not sure to be honest, I've never used ranged :(


So... without current access to the game, but wanting to capitalize on the opportunity, is this the correct method for this sort of script? I bastardized something together based off of my memories of the ice sculpting game from BMC. (And it's not meant to run unattended, so I don't care about going for more silvers or containers filling up.)

Edited to add: Obviously I'd need the actual string where it would only trigger on my creations, not every one in the room, or anyone who mentioned one. I'm more curious if this is the right 'way' to go about it, to include waiting until something appears in the hand before proceeding to the next step.


def go
empty_right_hand
empty_left_hand
waitrt?
fput "get chisel"
waitrt?
end

while (line = get)
if line =~ /crushed ice/
go
elsif line =~ /carved ice sword/
waitrt?
wait_until (checkright)
fput "put right in crate"
go
elsif line =~ /carved ice tree/
waitrt?
wait_until (checkright)
fput "put right in crate"
go
elsif line =~ /carved ice rolton/
waitrt?
wait_until (checkright)
fput "put right in my pack"
go
elsif line =~ /majestic ice dragon/
waitrt?
wait_until (checkright)
fput "put right in my pouch"
go
end
end

Nothing jumps out at me as being wrong.

Gnomad
02-10-2016, 11:44 PM
For ranged, you'll have to:

>AIM RIGHT LEG
>FIRE

Can't do it in one line.

CecilFitzgerald
08-24-2020, 04:20 PM
Hey. You can learn how to write code using some online tutorials. In the modern world, this training format is very popular. As for me, with the help of tutorials in which they teach how to write essays from companies like website (https://buypapercheap.net/cheap-custom-essay-for-sale.html), you can learn how to write not only essays but also term papers, just as with the help of tutorials in which they teach how to write code, you can learn how to write code of whole programs and think like a programmer.