Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Bots

Development::Bots Forum for bots.

Reply
 
Thread Tools Display Modes
  #1  
Old 02-25-2018, 02:16 PM
jaspen
Sarnak
 
Join Date: Apr 2016
Posts: 74
Default Bot healing. Questions about options, control and configurations.

I did my best to research these questions and never found a clear answer and due to the various revision and time frame of bot development it is often hard to tell what is or is not still relevant. With that said here are my following questions.

1. Is there a way to change when a bot tries to heal? If my shaman bot decides to heal me, which it seems to slack sometimes, it will start to heal me at around 35% health. The heal maybe brings me back up to 65%. At 35% I could be dead before the heal even finishes. I have tried different stances and nothing seems to change. I briefly tested the Min/Max health thing in case that was an indicator but I believe that is a self only, for them, setting? Is there a way to tell my shaman to heal when any group member is at 75% as an example? With them having low HP heals sooner is far better than later.

2. How are stances supposed to affect healing? Is this more so for clerics?

3. I saw very old mention of this but it appears it either no longer exists or was taken out but is there a simple ^heal, ^heal hot, ^heal CH, etc. command to force them to heal someone? That would at least be a somewhat workaround.

Thanks in advance!

EDIT: Emu db version 9017 - Bot db version 9018
Reply With Quote
  #2  
Old 02-25-2018, 02:22 PM
c0ncrete's Avatar
c0ncrete
Dragon
 
Join Date: Dec 2009
Posts: 709
Default

It is possible to alter the source code to allow for a user-specified threshold for any bot action, but I don't think it's set up that way now. If you want granular control, you're going to have to look at the source code and alter it or wait until it gets written that way. I want to say there used to be a heal override command (back in the # days) or something... I'm still getting back into the swing of things around here, but bot's are certainly a big interest.
__________________
I muck about @ The Forge.
say(rand 99>49?'try '.('0x'.join '',map{unpack 'H*',chr rand 256}1..2):'incoherent nonsense')while our $Noport=1;
Reply With Quote
  #3  
Old 02-25-2018, 02:35 PM
jaspen
Sarnak
 
Join Date: Apr 2016
Posts: 74
Default

I just started up a server myself so I am trying to learn as much as possible. Even simple things that I do learn about and can resolve myself, I will still bring it up here in case it can be implemented to benefit all. I would love to see bots and the project as a whole get better for everyone!
Reply With Quote
  #4  
Old 02-25-2018, 02:57 PM
c0ncrete's Avatar
c0ncrete
Dragon
 
Join Date: Dec 2009
Posts: 709
Default

I just now found my old source hackery from a lonnnng time ago. GAME ON!
__________________
I muck about @ The Forge.
say(rand 99>49?'try '.('0x'.join '',map{unpack 'H*',chr rand 256}1..2):'incoherent nonsense')while our $Noport=1;
Reply With Quote
  #5  
Old 02-25-2018, 09:34 PM
kokey98
Hill Giant
 
Join Date: Dec 2012
Location: terra firma
Posts: 111
Default

there's all sorts of stuff in the db you can use to make the bots act differently. priority of the spell etc.. even which spells they cast at which levels.

there's some info on wiki about the abbreviations you'll see. i dont have the right link but https://github.com/EQEmu/Server/wiki...asting_chances has the abbrevations to search for. all to do with healer/buffer etc or combat/non combat situations.

there are new min/max hp columns for spells,.. not sure if it's the target or caster? assume target. db must be updated to that point too, of course.

i have no idea if heal rotations work or not.. i could add memebers and this and that but i never really tried to use it yet.
Reply With Quote
  #6  
Old 02-25-2018, 10:26 PM
Uleat's Avatar
Uleat
Developer
 
Join Date: Apr 2012
Location: North Carolina
Posts: 2,395
Default

Bots are not coded to use the min/max fields of spells.

Most of the issues with bots healing have to do with the bot ai and the spells chosen for any healing bot.

I bet these really became an issue in the late-40s - early-50s, didn't they?


There's really only 3 stances that are accounted for in any casting check: PASSIVE, AE and not-AE.

I'm not sure..but, I doubt there's any AE healing going on.


Heal Rotations are probably what you're looking for. Those do give a little more user control over healing opportunties..though, there can be conflicts with
existing bot healing ai calls.

I rewrote that system some time ago. It has its pluses and minuses.
__________________
Uleat of Bertoxxulous

Compilin' Dirty
Reply With Quote
  #7  
Old 02-26-2018, 10:42 PM
kokey98
Hill Giant
 
Join Date: Dec 2012
Location: terra firma
Posts: 111
Default

cool beans, great info, thanks.

there's no 1 way with healing as far as timing / editing the code.. either it will be too slow or too fast. edit code to work well in 40's/50's then it won't work well for 71-75 or somethign like that - not just raid vs non-raid npc. faster less deadly than slower, obviously. that does seem to be the way it works out of the box -- good default in other words?

maybe rename one of the unused stances as "Raid" Stance - slightly different healing and melee strategies for longer fights? (completely rhetorical, no reply expected - devil's advocate only)
Reply With Quote
  #8  
Old 02-26-2018, 11:23 PM
jaspen
Sarnak
 
Join Date: Apr 2016
Posts: 74
Default

Keep in mind I just started up an emu a month or so ago so I am sure there is some ignorance in some of what I say and talk about. I did have one a little over two years ago that I played for about half a year.

I leveled a necro up to around 50 solo before finally testing the bots. I do sometimes group with a friend. My experience at lower levels and heals is zero right now. At first I only allowed 1 bot and now I allow 2 in some zones. I am trying to find a balance that allows you to get something done without cheesing the content. I even tried lowering their power by using the bot AAexpansion to 0 but it appears that is broken and they still get the AAs anyways. Then i find out a monk without weapons quads for around 300 a hit... Okay, with that quick bit of history out of the way...

I am currently grouping with a shaman bot and a mage bot. I haven't tested a cleric nor a druid yet so everything I have said in this post so far has been about shamans. The problem with the shaman bot is that it waits till around 35-40 percent health to heal. It never heals above that much health. When you are fighting mobs that hit for 400 plus and using heals that do 2k, it isn't very useful to wait till that low on health. There seems to be other situations where the shaman just doesn't care and doesn't bother to heal. I need to test whether the necro Lifetap dot causes that or not.

Is there a place in the code I can edit to say instead of healing at the 35-40 range to heal at 75 percent health instead? Or is there something in the bot_spells_casting_chances that I can modify to make them start the casting earlier? I tried looking in this section but haven't been able to decipher most of the columns. Just trying to figure out where the code is that says use this spell at this condition so I can change it.

Several have mentioned healing rotations. I will look into it but it sounds more so for raids and a mana killer / over kill for group. I would have to add the entire group at that point. Perhaps I am missing something here. I want anyone that is getting hammered to be healed. Any dead party member is a bad thing for a group.

Could the ^heal command be entered back into the code so you can simply force a bot to heal you or a person? Or have it where someone can use a phrase that causes the bot to heal them?
Reply With Quote
  #9  
Old 02-27-2018, 01:02 AM
Uleat's Avatar
Uleat
Developer
 
Join Date: Apr 2012
Location: North Carolina
Posts: 2,395
Default

I reviewed the original commands and there was no #bot heal - which is why it didn't make the transistion.


Heal Rotations are relatively simple to use and can be set up with one target and one healer.

http://wiki.eqemulator.org/p?Heal_Ro...--Bot_Commands
__________________
Uleat of Bertoxxulous

Compilin' Dirty
Reply With Quote
  #10  
Old 02-27-2018, 07:21 PM
kokey98
Hill Giant
 
Join Date: Dec 2012
Location: terra firma
Posts: 111
Default

that documentation on hr's is way different than the last time i read it or i was drunk the last time i read it. makes a lot more sense now as far as how to use it and the function.

really doesn't feel like i read it before april 9, 2016. oh my god, how old am i, now? prefer to be ignorant of how many laps i've completed around the sun.

some servers had custom #heal commands, that's probably why some are curious where it went. it would be a nice thing to have, too. ^heal ^cheal ^hot .. i bet i could contribute that much by using other ^commands that cast from existing code.

even with hr's it'd be a nice "oh sh&%" hotkey.

i am capable of monkey see, monkey do in this simple context. give me a upto a year, lol... and i'll need help submitting it, if the time comes and if it's wanted to begin with.
Reply With Quote
  #11  
Old 03-01-2018, 04:29 AM
c0ncrete's Avatar
c0ncrete
Dragon
 
Join Date: Dec 2009
Posts: 709
Default

You may be able to use perl (or lua) to get what you're after if you aren't comfortable modifying and editing source code. Below is a rough example of how you might go about it.

The current plugin::GetGroupMembers() only returns clients, so I made one that should only return a list of bots in your group.

Code:
# USAGE: plugin::GetGroupedBots($client)
sub plugin::GetGroupedBots {

	my @b;
	if (my $g = shift->GetGroup()) {
		for (my $i = 0; $i < 6; $i++) {
			next unless (my $m = $g->GetMember($i));
			next if ($m->IsClient() || $m->IsNPC());
			push(@b, $m);
		}
	}
	return @b;
}
Something like this would go in your global_player.pl and it allows for "commands" that have fallen through to EVENT_SAY to be parsed and used. You likely want to use the Chat, SuppressCommandErrors rule in the database to avoid seeing error messages when you prepend your custom commands with # (and possibly ^). Or you could just match for healme without the hashmark. It's really entirely up to you and your PCRE skills.

Code:
sub EVENT_SAY {

	  $text =~ /#testing/ ? plugin::Debug( "ping" )
	: $text =~ /#healme/  ? HealMe()
	:                       return;
}

sub HealMe {

	foreach my $bot (plugin::GetGroupedBots($client)) {
		plugin::Debug($bot->GetCleanName()." is a bot.");
		# EXAMPLES OF STEPS THAT MIGHT GO HERE
		# 1. verify bot ownership
		# 2. determine best available spell for situation
		# 3. cast selected spell (interrupt any current spell)
	}
}
I mean you could go all kinds of crazy if you really wanted to...
Code:
use strict;
use warnings;
use EQEmu::Client;
use EQEmu::Bot;
use EQEmu::Group;
use EQEmu::constants qw(:races :classes :groups);
use Switch;

my $c = EQEmu::Client->new(
    name  => 'Abracadaver',
    class => NECROMANCER
);

my $g = EQEmu::Group->new(
    $c,
    EQEmu::Bot->new(
        name  => 'Peregrin',
        class => RANGER
    ),
    EQEmu::Bot->new(
        name  => 'Galen',
        class => CLERIC
    )
);

my $hB = undef;    # healer bot
my $hC = 0;        # healer class

# look for a healer
for ( my $i = 0 ; $i < MAX_GROUP_MEMBERS ; $i++ ) {
    next if not my $m = $g->members($i);
    $c->Message( 15,
            $m->GetCleanName() . " the "
          . (CLASS_L)[ $m->GetClass() ]
          . " is at "
          . $m->GetHPRatio()
          . "% health" );

    # only want bots
    next if not $m->IsBot();
    switch ( $m->GetClass() ) {
        case CLERIC {
            $hB = $m;
            $hC = CLERIC;
        }
        case DRUID {
            if ( $hC != CLERIC ) {
                $hB = $m;
                $hC = DRUID;
            }
        }
        case RANGER {
            if ( !$hC ) {
                $hB = $m;
                $hC = RANGER;
            }
        }
        else {
            next;
        }
    }

    # break loop at first cleric bot
    last if $hC == CLERIC;
}

print $hC;
I'm not so sure it's a good idea if you value your sanity...
Code:
package EQEmu::Mob;

use strict;
use warnings::register;
use Carp qw( confess );

# construction
sub new
{
    my $c = shift;
    my $p = length @_ == 1 && ref $_[0] ? shift : {@_};
    # required parameters
    foreach ('name', 'class') {
        exists $p->{$_}
            or confess "$_ is a required attribute";
    }
    # validate and initialize these attributes
    my $_name   = delete $p->{name};
    my $_class  = delete $p->{class};
    my $_health = delete $p->{health} || 100;
    $p->{_name}   = $c->_validateName($_name);
    $p->{_class}  = $c->_validateClass($_class);
    $p->{_health} = $c->_validateHealth($_health);
    # set flags
    $p->{_isClient} = ($c =~ /Client/) || 0;
    $p->{_isBot}    = ($c =~ /Bot/)    || 0;
    $p->{_isNPC}    = ($c =~ /NPC/)    || 0;
    $c->_initDone($p) if $c =~ /Mob/;
    return bless $p, $c;
}
sub _initDone
{
    my ($s, $p) = @_;
    my $c = ref $s || $s;
    foreach my $k (keys %{$p}) {
        next if $k =~ /^_/;
        warnings::warn("unhandled attribute ( $k => ".$p->{$k}." ) in $c");
    }
}

sub _validateName
{
    my ($self, $name) = @_;
    local $Carp::CarpLevel = $Carp::CarpLevel + 1;
    $name =~ /^#?[a-z_]*$/i
        or confess "invalid name ( $name )";
    return $name;
}
sub _validateClass
{
    my ($self, $class) = @_;
    local $Carp::CarpLevel = $Carp::CarpLevel + 1;
    $class ~~ [1..16]  ||
    $class ~~ [20..35] ||
    $class ~~ [40..41] ||
    $class ~~ [59..64] ||
    $class ~~ [67..71]
        or confess "invalid class ( $class )";
    return $class;
}
sub _validateHealth
{
    my ($self, $health) = @_;
    local $Carp::CarpLevel = $Carp::CarpLevel + 1;
    $health ~~ [0..100]
        or confess "invalid health % ( $health )";
    return $health;
}

sub GetClass
{
    shift->{_class};
}
sub GetCleanName
{
    shift->{_name};
}
sub IsBot
{
    shift->{_isBot};
}
sub IsClient
{
    shift->{_isClient};
}
sub IsNPC
{
    shift->{_isNPC};
}
sub GetHPRatio
{
    shift->{_health};
}
sub GetGroup
{
    return 0;
}

1;
But that's completely up to you...
Code:
package EQEmu::constants;

use Exporter qw(import);

# playable class long names
use constant CLASS_L => qw(
    Unknown Warrior Cleric Paladin Ranger Shadowknight Druid Monk Bard Rogue
    Shaman Necromancer Wizard Magician Enchanter Beastlord Berserker
);
# bitmasks for playable class abbreviations
use constant
{
    WAR  =>     1, CLR =>     2, PAL =>     4, RNG =>     8,
    SHD  =>    16, DRU =>    32, MNK =>    64, BRD =>   128,
    ROG  =>   256, SHM =>   512, NEC =>  1024, WIZ =>  2048,
    MAG  =>  4096, ENC =>  8192, BST => 16384, BER => 32768
};
# numeric values for playable class long names
use constant
{
    WARRIOR      =>  1, CLERIC    =>  2, PALADIN     =>  3, RANGER    =>  4,
    SHADOWKNIGHT =>  5, DRUID     =>  6, MONK        =>  7, BARD      =>  8,
    ROGUE        =>  9, SHAMAN    => 10, NECROMANCER => 11, WIZARD    => 12,
    MAGICIAN     => 13, ENCHANTER => 14, BEASTMASTER => 15, BERSERKER => 16
};
# playable race long names
use constant RACE_L =>
    '', # 0
    'Human', 'Barbarian', 'Erudite', 'Wood Elf', 'High Elf', 'Dark Elf',
    'Half-Elf', 'Dwarf', 'Troll', 'Ogre', 'Halfling', 'Gnome',
    '','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','Iksar','','Vah Shir',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','Froglok',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','','','','','','','','','',
    '','Drakkin'
;
# bitmasks for playable race abbreviations
use constant
{
    HUM =>    1, BAR =>    2, ERU =>     4, ELF =>     8,
    HIE =>   16, DEF =>   32, HEL =>    64, DWF =>   128,
    TRL =>  256, OGR =>  512, HLF =>  1024, GNM =>  2048,
    IKS => 4096, VAH => 8192, FRG => 16384, DRK => 32768
};
# numeric values for playable class long names
use constant
{
    HUMAN   =>  1, BARBARIAN =>  2, ERUDITE  =>  3, WOODELF =>  4,
    HIGHELF =>  5, DARKELF   =>  6, HALFELF  =>  7, DWARF   =>  8,
    TROLL   =>  9, OGRE      => 10, HALFLING => 11, GNOME   => 12,
    IKSAR   => 13, VAHSHIR   => 14, FROGLOK  => 15, DRAKKIN => 16
};
# groups stuff
use constant
{
    MAX_GROUP_MEMBERS => 6
};

# every exportable constant must be listed here
our @EXPORT_OK = (qw(
    RACE_L CLASS_L
    WAR CLR PAL RNG SHD DRU MNK BRD ROG SHM NEC WIZ MAG ENC BST BER
    WARRIOR      CLERIC     PALADIN      RANGER
    SHADOWKNIGHT DRUID      MONK         BARD
    ROGUE        SHAMAN     NECROMANCER  WIZARD
    MAGICIAN     ENCHANTER  BEASTMASTER  BERSERKER
    HUM BAR ERU ELF HIE DEF HEL DWF TRL OGR HLF GNM IKS VAH FRG DRK
    HUMAN   BARBARIAN ERUDITE  WOODELF
    HIGHELF DARKELF   HALFELF  DWARF
    TROLL   OGRE      HALFLING GNOME
    IKSAR   VAHSHIR   FROGLOK  DRAKKIN
    MAX_GROUP_MEMBERS
));
# tags for collections of exportable constants
our %EXPORT_TAGS = (
    races => [qw(
        RACE_L
        HUM BAR ERU ELF HIE DEF HEL DWF TRL OGR HLF GNM IKS VAH FRG DRK
        HUMAN   BARBARIAN ERUDITE  WOODELF
        HIGHELF DARKELF   HALFELF  DWARF
        TROLL   OGRE      HALFLING GNOME
        IKSAR   VAHSHIR   FROGLOK  DRAKKIN
    )],
    classes => [qw(
        CLASS_L
        WAR CLR PAL RNG SHD DRU MNK BRD ROG SHM NEC WIZ MAG ENC BST BER
        WARRIOR      CLERIC     PALADIN      RANGER
        SHADOWKNIGHT DRUID      MONK         BARD
        ROGUE        SHAMAN     NECROMANCER  WIZARD
        MAGICIAN     ENCHANTER  BEASTMASTER  BERSERKER
    )],
    groups => [qw(
        MAX_GROUP_MEMBERS
    )]
);

1;
I've played on some servers with the custom heal command. I could have sworn I had some source patches somewhere for it, but I haven't come across it yet.
__________________
I muck about @ The Forge.
say(rand 99>49?'try '.('0x'.join '',map{unpack 'H*',chr rand 256}1..2):'incoherent nonsense')while our $Noport=1;
Reply With Quote
  #12  
Old 03-01-2018, 11:40 AM
jaspen
Sarnak
 
Join Date: Apr 2016
Posts: 74
Default

Thanks c0ncrete for that detailed reply. It will be a while before I know enough to attempt that but I am working towards learning. Learning this would help with other modifications I would like to make like a ^slow command as currently there can be 5 mobs beating on everyone and only one gets slowed. But that's a little off topic for now.

Uleat, I am not sure how well heal rotations will work as I don't want focus on only one person. I want everyone to be healed in a timely fashion, whatever group makeup that may be at the time. Keep in mind I am mainly focusing on group content and not raid.

With that said, do you or anyone know more about bot_spells_casting_chances? Perhaps part of my problem may be here. I see that casters, for example, have special entries while tanks do not along with the different stances. Perhaps, this is to give healing priority to the tank for example and assumes the caster can taken care of themselves? I have done search for the various columns and haven't dug up any information online. Things like nHSND_value elude me to what they are referencing.

Thanks again guys for all the help.
Reply With Quote
  #13  
Old 03-01-2018, 02:41 PM
kokey98
Hill Giant
 
Join Date: Dec 2012
Location: terra firma
Posts: 111
Default

bookmarked, thanks

side note: i hav a cleric named galen too.. Did you also name it after the bloodletting doctor? he may not have been first to use leeches, but he was famous for it... or infamous is the word i'd use.

dumb, dumb... dumb,dumb,dumb. (lyrics of music from south park)
Reply With Quote
  #14  
Old 03-01-2018, 10:07 PM
c0ncrete's Avatar
c0ncrete
Dragon
 
Join Date: Dec 2009
Posts: 709
Default

nah, i generally try to pick names that mean something, not after people unless it's somehow tied to mythology. not counting my handle, which is short for buried in concrete, which is from a belgian electronic industrial group from the 80s...
__________________
I muck about @ The Forge.
say(rand 99>49?'try '.('0x'.join '',map{unpack 'H*',chr rand 256}1..2):'incoherent nonsense')while our $Noport=1;
Reply With Quote
  #15  
Old 03-03-2018, 01:41 AM
kokey98
Hill Giant
 
Join Date: Dec 2012
Location: terra firma
Posts: 111
Default

i'm big on using foreign languages, historical names real and myth. words for mundane things related to the class in latin is a "goto." by luck star wars rogue one at the time had a galen character too. may have been subconsciously planted when i saw it searching for name inspiration. was looking for a bloodletting docter with a fairly useable name... a "johnson" or something similar just wasn't going to do it.

Veneficus from in game is just latin for magician or something of that sort, i forget at this point. eq stole my name! jk
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 11:32 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3