Results 1 to 6 of 6

Thread: Assistance with mechanical crossbow combat script

  1. #1

    Default Assistance with mechanical crossbow combat script

    I made this basic combat script for using a mechanical crossbow with bigshot. I would like for it to check how many bolts are in the crossbow when the script starts but I am unsure how to go about it. I basically want to look in the crossbow and record how many bolts are present into a variable called ammo. The script will then reload before starting combat if there are less than 3 bolts loaded.

    I'm unsure how acquire this information and save it into the variable. I tried messing with GetObject.container.contents, GetObject.left_hand.contents, GetObject.inv.contents (this one doesn't find the crossbow when its in your hands.) but I have no idea how they actually work or go together. Any assistance and education on how to handle this would be greatly appreciated.


    Code:
    target = nil
    
    #check_reload = proc{
    #	create ammo variable that is set to the number of bolts inside the crossbow.
    #	not sure how to reliably create this variable. 'look in my crossbow' displays each bolt individually
    #	perhaps there is a way to count the number of items inside the crossbow and save that to the ammo varaible.
    #
    #      >look in #199036991
    #      In the mechanical crossbow you see a hunter green heavy crossbow bolt, a hunter green heavy crossbow bolt, a hunter 
    #	green heavy crossbow bolt, a hunter green heavy crossbow bolt and a hunter green heavy crossbow bolt.
    #
    #
    #	ammo = 
    #	if ammo <= 2
    #		while ammo < 5
    #			multifput "get 1 bolt from thigh", "load crossbow"
    #			ammo += 1
    #		end
    #	end
    #}
    
    
    get_target = proc{
    	if target == nil
    		target = GameObj.npcs.find { |npc| npc.name =~ // }
    		(target = nil) if (target.name =~ /grizzled|ancient/) && (checkbounty !~ /hunt down and kill a particularly dangerous/)
    	end
    }
    
    
    kneel_prep = proc{
    	if checkkneeling != true
    		fput "kneel" until kneeling?
    	else
    	end
    }
    
    
    hide_prep = proc{
    	if checkhidden != true && Spell[608].known? && Spell[608].affordable? && !Spell[608].active?
    		Spell[608].cast
    	elsif checkhidden != true
    		fput "hide" until hiding?
    	end
    }
    
    
    attack_target = proc{
    	waitrt?
    	fput "cock my crossbow"
    	pause 0.1
    	waitrt?
    	fput "stance offensive"
    	pause 0.1
    	fput "fire"
    	pause 0.1
    	waitrt?
    	fput "stance defensive"
    }
    
    
    #check_reload.call
    target = nil if (target.status =~ /dead|gone/)
    get_target.call
    while target != nil
    	kneel_prep.call
    	hide_prep.call until hiding?
    	attack_target.call if (target != nil) && (target.status !~ /dead|gone/)
    	sleep 0.1
    	if target.status =~ /dead|gone/
    		target = nil
    	end
    end
    Last edited by Getho; 01-06-2019 at 08:13 PM.

  2. #2

    Default

    After finding Dreaven's scripting videos I have come up with a simple solution it seems.

    Code:
    =begin
    						Mechanical Crossbow Combat Script
    								     V 1.1
    								author: Getho
    							tags: ranged crossbow combat
    							
    	For use with a T2 mechanical crossbow (5 bolts). 
    	Checks number of bolts loaded and fills magazine if less than 3.
    	Will kneel
    	Will cast 608 if able, will hide if not.
    	Cocks and fires crossbow.
    
    =end
    							
    							
    							
    target = nil
    ammo = nil
    
    check_reload = proc{
    	fput "look in my crossbow"
    	while line = get
    		if line =~ /^In the mechanical crossbow you see (.*)\, (.*)\, (.*)\, (.*) and (.*)\.$/
    			ammo = 5
    			break
    		elsif line =~ /^In the mechanical crossbow you see (.*)\, (.*)\, (.*) and (.*)\.$/
    			ammo = 4
    			break
    		elsif line =~ /^In the mechanical crossbow you see (.*)\, (.*) and (.*)\.$/
    			ammo = 3
    			break
    		elsif line =~ /^In the mechanical crossbow you see (.*) and (.*)\.$/
    			ammo = 2
    			break
    		elsif line =~ /^In the mechanical crossbow you see (.*)\.$/
    			ammo = 1
    			break
    		elsif line =~ /^There is nothing in there\.$/
    			ammo = 0
    			break
    		end
    	end
    
    	echo ammo
    	if ammo <= 2
    		while ammo < 5
    			multifput "get 1 bolt from thigh", "load crossbow"
    			ammo += 1
    		end
    	end
    }
    
    
    get_target = proc{
    	if target == nil
    		target = GameObj.npcs.find { |npc| npc.name =~ // }
    		(target = nil) if (target.name =~ /grizzled|ancient/) && (checkbounty !~ /hunt down and kill a particularly dangerous/)
    	end
    }
    
    
    kneel_prep = proc{
    	if checkkneeling != true
    		fput "kneel" until kneeling?
    	else
    	end
    }
    
    
    hide_prep = proc{
    	if checkhidden != true && Spell[608].known? && Spell[608].affordable? && !Spell[608].active?
    		Spell[608].cast
    	elsif checkhidden != true
    		fput "hide" until hiding?
    	end
    }
    
    
    attack_target = proc{
    	waitrt?
    	fput "cock my crossbow"
    	pause 0.1
    	waitrt?
    	fput "stance offensive"
    	pause 0.1
    	fput "fire"
    	pause 0.1
    	waitrt?
    	fput "stance defensive"
    }
    
    
    check_reload.call
    target = nil if (target.status =~ /dead|gone/)
    get_target.call
    while target != nil
    	kneel_prep.call
    	hide_prep.call until hiding?
    	attack_target.call if (target != nil) && (target.status !~ /dead|gone/)
    	sleep 0.1
    	if target.status =~ /dead|gone/
    		target = nil
    	end
    end
    The next step was to make a loot script that would reload the crossbow. So i made a script that called sloot, once sloot finished it would would pick up bolts one at a time and load them into the crossbow, adding any excess back to the bundle. I accomplished this by taking sloot's gather ammo portions and making a couple tiny modifications. So it's like an sloot inception I guess ... heh. Can't say I fiully understand too much of what's going on.

    This will work until I can learn and make something cleaner. The bundle portion of it is really just there as a safety net for when I somehow end up shooting all 5 bolts and reloading without recovering ammo.


    Code:
    #
    #									Mechanical Crossbow Support for Sloot
    #											Author: Getho
    #
    #
    #   I blatantly ripped the ammo gather code from sloot and tweaked it a tiny bit to work with my mechanical crossbow.
    #											ALL PRIASE GOES TO SPIFFYJR
    #
    #	Script will run sloot, when sloot is complete it will pick up each bolt one at a time and load them back into
    #	the mechanical crossbow. Once the crossbow is full, it should add remaining bolts to the bundle in your quiver.
    #	The bundle portion is untested and may not work properly.
    #
    #
    #
    #
    #
    #
    #
    #
    
    
    # Gets bundle information for ammo
    get_bundle_details = proc do |bundle|
        details = {
            :id => bundle.id
        }
    
        put "look at ##{bundle.id}"
        while line = get
            if line =~ /a strength of (\d+) and a durability of (\d+)/
                details[:strength] = $1.to_i
                details[:durability] = $2.to_i
            elsif line =~ /Each individual projectile will be "([^"]+)"\./
                details[:name] = $1.to_s
                break
            end
        end
    
        details
    end
    
    waitrt?
    Script.start('sloot')
    
    while( running?('sloot'))
    	pause 1.0
    end
    sleep 0.25
    
    ammo_name = "bolt"
    quiver = GameObj.inv.find { |obj| obj.name =~ /#{UserVars.ammosack}/ }
    
    ammo_noun = nil
    if ammo_name =~ /\b(bolt|arrow|dart)\b/
        ammo_noun = $1.to_s
    else
    end
    
    bundles = quiver.contents.find_all do |loot|
        loot.type == 'ammo' and 
        loot.name =~ /bundle/i
    end
    
    $sloot_bundles ||= []
    $sloot_bundles.delete_if { |sb| not quiver.contents.find { |item| item.id == sb[:id] } }
    
    bundles.each do |bundle|
        if $sloot_bundles.find { |sb| sb[:id] == bundle.id }
            next
        end
    
        $sloot_bundles.push(get_bundle_details.call(bundle))
    end
    
    ammo = GameObj.loot.find_all do |loot|
        loot.type == 'ammo' and
        loot.name =~ /\b#{ammo_name}s?\b/
    end
    
    rounds = ammo.length
    
    while rounds > 0
        # get ammo first
        res = dothistimeout "get #{ammo_noun}", 2, /The bolt is out of your reach\.|You gather|You pick up/
    
    	if res =~ /You pick up/
    		rounds -= 1
            sleep 0.2 until GameObj.right_hand.id
    		fput "load my crossbow"
    		sleep 0.1
        end
    	
    	if checkright != false
    		if GameObj.right_hand.name =~ /#{ammo_noun}s/i
    			details = get_bundle_details.call(GameObj.right_hand)
    			bundle = $sloot_bundles.find { |bundle| bundle[:name] == details[:name] }
    
    			if bundle
    				fput "put ##{details[:id]} in ##{bundle[:id]}"
    			end
    		elsif GameObj.right_hand.name =~ /#{ammo_name}/i
    			bundle = $sloot_bundles.find { |bundle| bundle[:name] =~ /#{GameObj.right_hand.name}/i }
    
    			if bundle
    				fput "put ##{GameObj.right_hand.id} in ##{bundle[:id]}"
    			end
    		end
    	else
    	end
    end

  3. #3

    Default

    Assuming this behaves like the mechanical bows. (They're supposed to, but I've never verified.)

    GameObj.contents doesn't work because the game doesn't treat the item as a container. (You can check with "inventory containers").

    Instead, you'll have to parse text from looking in it. Assuming you've grabbed that to the variable "line":

    Code:
    line =~ /In the .* you see (.+)\.$/
    ammo = 0
    if (!$1.nil? )
       ammo = $1.split(/(?:,|and)/).length
    end

  4. #4

    Default

    So much cleaner than the basic crap I put together. Thanks =)

  5. #5

    Default

    I never got a "contents" part of the object when trying with mine. It's not really a container, per se, so don't think that is being included in the xml but i never bothered to look at the actual stream from the game at the time. I just did a quick peek now and there's an "exposeContainer" flag that is passed down from the simu servers for a regular container, but not for the crossbow so i'm assuming because of that it won't get caught by the lich code to populate the "contents" parameter. If that's the case it's just easier to get the string from the look and parse it yourself.

    So you need to do your LOOK IN and examine the string somehow for bolts. You can also just look AT the arbaelest which gives different messaging "something something loaded with a blah blah bolt and X additional bolts" or "not loaded at all" if empty

    If you do the LOOK IN you could do something like:

    https://rubular.com/r/ix8XxFy2geqSAx

    for the match, then do a SCAN on the line from look in.

    that should get you an a list whose values are single element lists that are a strings of "a/an blah blah blah bolt", then if you wanted to you could also make sure you aren't, say, loading in your PEARLY WHITE UNDEAD SLAYING BOLTS instead of your FIREY RED LIVING SLAVING BOLTS OF DOOOOM or whatever

    Code:
    x = "In the heavy arbalest you see a wooden heavy crossbow bolt, a wooden heavy crossbow bolt, a wooden heavy crossbow bolt and a wooden heavy crossbow bolt."
    irb(main):013:0> y = x.scan(/((?:a|an) .+? bolt)/)
    => [["a wooden heavy crossbow bolt"], ["a wooden heavy crossbow bolt"], ["a wooden heavy crossbow bolt"], ["a wooden heavy crossbow bolt"]]
    irb(main):014:0> y
    => [["a wooden heavy crossbow bolt"], ["a wooden heavy crossbow bolt"], ["a wooden heavy crossbow bolt"], ["a wooden heavy crossbow bolt"]]
    => y.length
    => 4
    now the result of the scan, the length is your number of arrows, and you can look deeper if you want to do some idiot checking on the bolt type loaded. There's a few ways to skin this cat but, that's just some spitballing.
    Mithrilschlong, 2015-03-10 to slightly later on 2015-03-10. You will not be forgotten!

  6. #6

    Default

    1
    Last edited by Getho; 01-08-2019 at 09:25 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •