EQEmulator Forums

EQEmulator Forums (https://www.eqemulator.org/forums/index.php)
-   Development::Bots (https://www.eqemulator.org/forums/forumdisplay.php?f=676)
-   -   Bot healing. Questions about options, control and configurations. (https://www.eqemulator.org/forums/showthread.php?t=41781)

jaspen 02-25-2018 02:16 PM

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

c0ncrete 02-25-2018 02:22 PM

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.

jaspen 02-25-2018 02:35 PM

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! :)

c0ncrete 02-25-2018 02:57 PM

I just now found my old source hackery from a lonnnng time ago. GAME ON! :D

kokey98 02-25-2018 09:34 PM

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.

Uleat 02-25-2018 10:26 PM

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.

kokey98 02-26-2018 10:42 PM

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)

jaspen 02-26-2018 11:23 PM

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?

Uleat 02-27-2018 01:02 AM

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

kokey98 02-27-2018 07:21 PM

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. :)

c0ncrete 03-01-2018 04:29 AM

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.

jaspen 03-01-2018 11:40 AM

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.

kokey98 03-01-2018 02:41 PM

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)

c0ncrete 03-01-2018 10:07 PM

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...

kokey98 03-03-2018 01:41 AM

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


All times are GMT -4. The time now is 04:54 PM.

Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.