Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix slave skills #3914

Merged
merged 2 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions src/AI.pm
Original file line number Diff line number Diff line change
Expand Up @@ -586,12 +586,19 @@ 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;
}

if ($skill->getOwnerType == Skill::OWNER_CHAR) {
AI::queue("skill_use", \%args);
} else {
$owner->queue("skill_use", \%args);
}

AI::queue("skill_use", \%args);
}

##
Expand Down
5 changes: 4 additions & 1 deletion src/AI/CoreLogic.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}

Expand Down
112 changes: 112 additions & 0 deletions src/AI/Slave.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -161,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())
Expand Down Expand Up @@ -510,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);
Expand Down
1 change: 1 addition & 0 deletions src/AI/SlaveManager.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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")) {
Expand Down
2 changes: 2 additions & 0 deletions src/Actor/You.pm
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ sub nameString {
return T('You');
}

sub checkSkillOwnership { $_[1]->getOwnerType == Skill::OWNER_CHAR }

##
# int $char->getSkillLevel(Skill skill)
# Ensures: result >= 0
Expand Down
10 changes: 6 additions & 4 deletions src/Commands.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
24 changes: 16 additions & 8 deletions src/Misc.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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"}) {
Expand Down
21 changes: 14 additions & 7 deletions src/Network/Receive.pm
Original file line number Diff line number Diff line change
Expand Up @@ -9309,26 +9309,33 @@ 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}));

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);
Expand Down
Loading