From 6a929e077edf40860136653f778079603b23c090 Mon Sep 17 00:00:00 2001 From: Henrique Date: Sun, 22 Dec 2024 22:26:20 -0300 Subject: [PATCH 1/2] Fix slave skills --- src/AI.pm | 9 ++++++--- src/AI/CoreLogic.pm | 5 ++++- src/AI/Slave.pm | 10 ++++++++++ src/AI/SlaveManager.pm | 1 + src/Actor/You.pm | 2 ++ src/Commands.pm | 10 ++++++---- src/Misc.pm | 24 ++++++++++++++++-------- src/Network/Receive.pm | 21 ++++++++++++++------- 8 files changed, 59 insertions(+), 23 deletions(-) diff --git a/src/AI.pm b/src/AI.pm index e344fad09f..b986d60087 100644 --- a/src/AI.pm +++ b/src/AI.pm @@ -586,9 +586,12 @@ sub ai_skillUse { delete $args{target}; } - if ($char->{skills}{$args{skillHandle}}{lv} < $args{lv}) { - debug "Attempted to use skill (".$args{skillHandle}.") level ".$args{lv}." which you do not have, adjusting to level ".$char->{skills}{$args{skillHandle}}{lv}.".\n", "ai"; - $args{lv} = $char->{skills}{$args{skillHandle}}{lv}; + my $skill = Skill->new(auto => $args{skillHandle}); + my $owner = $skill->getOwner(); + my $lvl = $owner->getSkillLevel($skill); + if ($lvl < $args{lv}) { + debug "[$owner] Attempted to use skill (".$args{skillHandle}.") level ".$args{lv}." which you do not have, adjusting to level ".$lvl.".\n", "ai"; + $args{lv} = $lvl; } AI::queue("skill_use", \%args); diff --git a/src/AI/CoreLogic.pm b/src/AI/CoreLogic.pm index 892c6e02a5..dad42ce551 100644 --- a/src/AI/CoreLogic.pm +++ b/src/AI/CoreLogic.pm @@ -750,7 +750,10 @@ sub processSkillUse { # Give an error if we don't actually possess this skill my $skill = new Skill(handle => $handle); - if ($char->{skills}{$handle}{lv} <= 0 && (!$char->{permitSkill} || $char->{permitSkill}->getHandle() ne $handle)) { + my $owner = $skill->getOwner(); + my $lvl = $owner->getSkillLevel($skill); + + if ($lvl <= 0 && (!$char->{permitSkill} || $char->{permitSkill}->getHandle() ne $handle)) { debug "Attempted to use skill (".$skill->getName().") which you do not have.\n"; } diff --git a/src/AI/Slave.pm b/src/AI/Slave.pm index a621a9cf45..56ae92e722 100644 --- a/src/AI/Slave.pm +++ b/src/AI/Slave.pm @@ -20,6 +20,16 @@ use constant MAX_DISTANCE => 17; sub checkSkillOwnership {} +sub getSkillLevel { + my ($self, $skill) = @_; + my $handle = $skill->getHandle(); + if ($self->{skills}{$handle}) { + return $self->{skills}{$handle}{lv}; + } else { + return 0; + } +} + sub action { my $slave = shift; diff --git a/src/AI/SlaveManager.pm b/src/AI/SlaveManager.pm index fa6b03f479..2c382bf19f 100644 --- a/src/AI/SlaveManager.pm +++ b/src/AI/SlaveManager.pm @@ -18,6 +18,7 @@ sub addSlave { $actor->{slave_ai_seq} = []; $actor->{slave_ai_seq_args} = []; $actor->{slave_skillsID} = []; + $actor->{skills} = {}; $actor->{slave_AI} = AI::AUTO; if ($actor->isa("Actor::Slave::Homunculus")) { diff --git a/src/Actor/You.pm b/src/Actor/You.pm index eedf44e425..970ec516d9 100644 --- a/src/Actor/You.pm +++ b/src/Actor/You.pm @@ -114,6 +114,8 @@ sub nameString { return T('You'); } +sub checkSkillOwnership { $_[1]->getOwnerType == Skill::OWNER_CHAR } + ## # int $char->getSkillLevel(Skill skill) # Ensures: result >= 0 diff --git a/src/Commands.pm b/src/Commands.pm index 77cb0d2bc2..f2eb8e6764 100644 --- a/src/Commands.pm +++ b/src/Commands.pm @@ -3100,10 +3100,12 @@ sub cmdSlave { T(" # Skill Name Lv SP\n"); foreach my $handle (@{$slave->{slave_skillsID}}) { my $skill = new Skill(handle => $handle); - my $sp = $char->{skills}{$handle}{sp} || ''; - $msg .= swrite( - "@>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>> @>>>", - [$skill->getIDN(), $skill->getName(), $char->getSkillLevel($skill), $sp]); + if ($slave->checkSkillOwnership($skill)) { + my $sp = $slave->{skills}{$handle}{sp} || ''; + $msg .= swrite( + "@>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>> @>>>", + [$skill->getIDN(), $skill->getName(), $slave->getSkillLevel($skill), $sp]); + } } $msg .= TF("\nSkill Points: %d\n", $slave->{points_skill}) if defined $slave->{points_skill}; $msg .= ('-'x46) . "\n"; diff --git a/src/Misc.pm b/src/Misc.pm index 959494418d..024e13ae58 100644 --- a/src/Misc.pm +++ b/src/Misc.pm @@ -4752,14 +4752,22 @@ sub checkSelfCondition { # check skill use SP if this is a 'use skill' condition if ($prefix =~ /skill|attackComboSlot/i) { my $skill = Skill->new(auto => $config{$prefix}); - return 0 unless ($char->getSkillLevel($skill) - || $config{$prefix."_equip_leftAccessory"} - || $config{$prefix."_equip_rightAccessory"} - || $config{$prefix."_equip_leftHand"} - || $config{$prefix."_equip_rightHand"} - || $config{$prefix."_equip_robe"} - ); - return 0 unless ($char->{sp} >= $skill->getSP($config{$prefix . "_lvl"} || $char->getSkillLevel($skill))); + if ($char->checkSkillOwnership ($skill)) { + return 0 unless ($char->getSkillLevel($skill) + || $config{$prefix."_equip_leftAccessory"} + || $config{$prefix."_equip_rightAccessory"} + || $config{$prefix."_equip_leftHand"} + || $config{$prefix."_equip_rightHand"} + || $config{$prefix."_equip_robe"} + ); + return 0 unless ($char->{sp} >= $skill->getSP($config{$prefix . "_lvl"} || $char->getSkillLevel($skill))); + + } elsif ($has_homunculus && $char->{homunculus}->checkSkillOwnership($skill)) { + return 0 unless ($char->{homunculus}->getSkillLevel($skill)); + + } elsif ($has_mercenary && $char->{mercenary}->checkSkillOwnership($skill)) { + return 0 unless ($char->{mercenary}->getSkillLevel($skill)); + } } if (defined $config{$prefix . "_skill"}) { diff --git a/src/Network/Receive.pm b/src/Network/Receive.pm index f75d459a9b..d208c885ee 100644 --- a/src/Network/Receive.pm +++ b/src/Network/Receive.pm @@ -9309,18 +9309,25 @@ sub skills_list { # TODO: per-actor, if needed at all # Skill::DynamicInfo::clear; my ($ownerType, $hook, $actor) = @{{ - '010F' => [Skill::OWNER_CHAR, 'packet_charSkills'], + '010F' => [Skill::OWNER_CHAR, 'packet_charSkills', $char], '0235' => [Skill::OWNER_HOMUN, 'packet_homunSkills', $char->{homunculus}], '029D' => [Skill::OWNER_MERC, 'packet_mercSkills', $char->{mercenary}], - '0B32' => [Skill::OWNER_CHAR, 'packet_charSkills'], + '0B32' => [Skill::OWNER_CHAR, 'packet_charSkills', $char], }->{$args->{switch}}}; - my $skillsIDref = $actor ? \@{$actor->{slave_skillsID}} : \@skillsID; - delete @{$char->{skills}}{@$skillsIDref}; + my $skillsIDref; + if ($ownerType == Skill::OWNER_CHAR) { + $skillsIDref = \@skillsID; + delete @{$char->{skills}}{@$skillsIDref}; + } elsif ($ownerType == Skill::OWNER_HOMUN) { + $skillsIDref = \@{$char->{homunculus}->{slave_skillsID}}; + delete @{$char->{homunculus}->{skills}}{@$skillsIDref}; + } elsif ($ownerType == Skill::OWNER_MERC) { + $skillsIDref = \@{$char->{mercenary}->{slave_skillsID}}; + delete @{$char->{mercenary}->{skills}}{@$skillsIDref}; + } @$skillsIDref = (); - # TODO: $actor can be undefined here - undef @{$actor->{slave_skillsID}}; for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += $skill_info->{len}) { my $skill; @{$skill}{@{$skill_info->{keys}}} = unpack($skill_info->{types}, substr($msg, $i, $skill_info->{len})); @@ -9328,7 +9335,7 @@ sub skills_list { my $handle = Skill->new(idn => $skill->{ID})->getHandle; foreach(@{$skill_info->{keys}}) { - $char->{skills}{$handle}{$_} = $skill->{$_}; + $actor->{skills}{$handle}{$_} = $skill->{$_}; } binAdd($skillsIDref, $handle) unless defined binFind($skillsIDref, $handle); From 9eb68fe1cf58b845b0d529e30affedfe3999fa6f Mon Sep 17 00:00:00 2001 From: Henrique Date: Sun, 22 Dec 2024 23:15:41 -0300 Subject: [PATCH 2/2] Move skills to slave AI- --- src/AI.pm | 8 +++- src/AI/Slave.pm | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/src/AI.pm b/src/AI.pm index b986d60087..502d1978d6 100644 --- a/src/AI.pm +++ b/src/AI.pm @@ -593,8 +593,12 @@ sub ai_skillUse { debug "[$owner] Attempted to use skill (".$args{skillHandle}.") level ".$args{lv}." which you do not have, adjusting to level ".$lvl.".\n", "ai"; $args{lv} = $lvl; } - - AI::queue("skill_use", \%args); + + if ($skill->getOwnerType == Skill::OWNER_CHAR) { + AI::queue("skill_use", \%args); + } else { + $owner->queue("skill_use", \%args); + } } ## diff --git a/src/AI/Slave.pm b/src/AI/Slave.pm index 56ae92e722..3a3d560e0a 100644 --- a/src/AI/Slave.pm +++ b/src/AI/Slave.pm @@ -171,6 +171,7 @@ sub iterate { ##### MANUAL AI STARTS HERE ##### AI::SlaveAttack::process($slave); + $slave->processSkillUse; $slave->processTask('route', onError => sub { my ($task, $error) = @_; if (!($task->isa('Task::MapRoute') && $error->{code} == Task::MapRoute::TOO_MUCH_TIME()) @@ -520,6 +521,107 @@ sub processAutoAttack { #Benchmark::end("ai_homunculus_autoAttack") if DEBUG; } +##### SKILL USE ##### +sub processSkillUse { + my ($slave) = @_; + + #FIXME: need to move closer before using skill on player, + #there might be line of sight problem too + #or the player disappers from the area + + if ($slave->action eq "skill_use" && $slave->args->{suspended}) { + $slave->args->{giveup}{time} += time - $slave->args->{suspended}; + $slave->args->{minCastTime}{time} += time - $slave->args->{suspended}; + $slave->args->{maxCastTime}{time} += time - $slave->args->{suspended}; + delete $slave->args->{suspended}; + } + + SKILL_USE: { + last SKILL_USE if ($slave->action ne "skill_use"); + my $args = $slave->args; + + if ($args->{monsterID} && $skillsArea{$args->{skillHandle}} == 2) { + delete $args->{monsterID}; + } + + if (timeOut($args->{waitBeforeUse})) { + if (defined $args->{monsterID} && !defined $monsters{$args->{monsterID}}) { + # This skill is supposed to be used for attacking a monster, but that monster has died + $slave->dequeue; + ${$args->{ret}} = 'target gone' if ($args->{ret}); + + # Use skill if we haven't done so yet + } elsif (!$args->{skill_used}) { + #if ($slave->{last_skill_used_is_continuous}) { + # message T("Stoping rolling\n"); + # $messageSender->sendStopSkillUse($slave->{last_continuous_skill_used}); + #} elsif(($slave->{last_skill_used} == 2027 || $slave->{last_skill_used} == 147) && !$slave->{selected_craft}) { + # message T("No use skill due to not select the craft / poison\n"); + # last SKILL_USE; + #} + my $handle = $args->{skillHandle}; + if (!defined $args->{skillID}) { + my $skill = new Skill(handle => $handle); + $args->{skillID} = $skill->getIDN(); + } + my $skillID = $args->{skillID}; + + $args->{skill_used} = 1; + $args->{giveup}{time} = time; + + # Stop attacking, otherwise skill use might fail + my $attackIndex = $slave->findAction("attack"); + if (defined($attackIndex) && $slave->args($attackIndex)->{attackMethod}{type} eq "weapon") { + # 2005-01-24 pmak: Commenting this out since it may + # be causing bot to attack slowly when a buff runs + # out. + #$slave->stopAttack(); + } + + # Give an error if we don't actually possess this skill + my $skill = new Skill(handle => $handle); + my $owner = $skill->getOwner(); + my $lvl = $owner->getSkillLevel($skill); + + if ($lvl <= 0) { + debug "Attempted to use skill (".$skill->getName().") which you do not have.\n"; + } + + $args->{maxCastTime}{time} = time; + if ($skillsArea{$handle} == 2) { + $messageSender->sendSkillUse($skillID, $args->{lv}, $accountID); + } elsif ($args->{x} ne "") { + $messageSender->sendSkillUseLoc($skillID, $args->{lv}, $args->{x}, $args->{y}); + } elsif ($args->{isStartSkill}) { + $messageSender->sendStartSkillUse($skillID, $args->{lv}, $args->{target}); + } else { + $messageSender->sendSkillUse($skillID, $args->{lv}, $args->{target}); + } + $args->{skill_use_last} = $slave->{skills}{$handle}{time_used}; + + delete $slave->{cast_cancelled}; + + } elsif (timeOut($args->{minCastTime})) { + if ($args->{skill_use_last} != $slave->{skills}{$args->{skillHandle}}{time_used}) { + $slave->dequeue; + ${$args->{ret}} = 'ok' if ($args->{ret}); + + } elsif ($slave->{cast_cancelled} > $slave->{time_cast}) { + $slave->dequeue; + ${$args->{ret}} = 'cancelled' if ($args->{ret}); + + } elsif (timeOut($slave->{time_cast}, $slave->{time_cast_wait} + 0.5) + && ( (timeOut($slave->{giveup}) && (!$slave->{time_cast} || !$args->{maxCastTime}{timeout}) ) + || ( $args->{maxCastTime}{timeout} && timeOut($args->{maxCastTime})) ) + ) { + $slave->dequeue; + ${$args->{ret}} = 'timeout' if ($args->{ret}); + } + } + } + } +} + sub sendAttack { my ($slave, $targetID) = @_; $messageSender->sendSlaveAttack ($slave->{ID}, $targetID);