From b10b2f2d61c09ec39560a667e879e3312d669454 Mon Sep 17 00:00:00 2001 From: ya4ept Date: Wed, 25 Dec 2024 18:30:56 +0300 Subject: [PATCH 1/3] Minimum package support 0A15 (PACKET_ZC_GOLDPCCAFE_POINT) (#3916) --- src/Network/Receive.pm | 7 +++++++ src/Network/Receive/ServerType0.pm | 1 + src/Network/Receive/kRO/Sakexe_0.pm | 1 + 3 files changed, 9 insertions(+) diff --git a/src/Network/Receive.pm b/src/Network/Receive.pm index f75d459a9b..5f4427c000 100644 --- a/src/Network/Receive.pm +++ b/src/Network/Receive.pm @@ -12329,4 +12329,11 @@ sub repute_info { } } +# 0A15 - PACKET_ZC_GOLDPCCAFE_POINT +# TODO: this package is not supported yet. +sub gold_pc_cafe_point { + my ($self, $args) = @_; + debug TF("[gold_pc_cafe_point] isActive=%d, mode=%d, point=%d, playedTime=%d\n", $args->{isActive}, $args->{mode}, $args->{point}, $args->{playedTime}); +} + 1; diff --git a/src/Network/Receive/ServerType0.pm b/src/Network/Receive/ServerType0.pm index 35bd78f89f..e668dac8c2 100644 --- a/src/Network/Receive/ServerType0.pm +++ b/src/Network/Receive/ServerType0.pm @@ -635,6 +635,7 @@ sub new { '0A10' => ['storage_items_nonstackable', 'v Z24 a*', [qw(len title itemInfo)]], '0A12' => ['rodex_open_write', 'Z24 C', [qw(name result)]], # 27 '0A14' => ['rodex_check_player', 'V v2', [qw(char_id class base_level)]], + '0A15' => ['gold_pc_cafe_point', 'C2 V2', [qw(isActive mode point playedTime)]], # 12 '0A18' => ['map_loaded', 'V a3 C2 v C', [qw(syncMapSync coords xSize ySize font sex)]], # 14 '0A1A' => ['roulette_window', 'C V C2 v V3', [qw(result serial stage price additional_item gold silver bronze)]], '0A1C' => ['roulette_info', 'v V a*', [qw(len serial roulette_info)]], diff --git a/src/Network/Receive/kRO/Sakexe_0.pm b/src/Network/Receive/kRO/Sakexe_0.pm index 3d9d667b40..ed46959122 100644 --- a/src/Network/Receive/kRO/Sakexe_0.pm +++ b/src/Network/Receive/kRO/Sakexe_0.pm @@ -627,6 +627,7 @@ sub new { '0A10' => ['storage_items_nonstackable', 'v Z24 a*', [qw(len title itemInfo)]], '0A12' => ['rodex_open_write', 'Z24 C', [qw(name result)]], # 27 '0A14' => ['rodex_check_player', 'V v2', [qw(char_id class base_level)]], + '0A15' => ['gold_pc_cafe_point', 'C2 V2', [qw(isActive mode point playedTime)]], # 12 '0A18' => ['map_loaded', 'V a3 C2 v C', [qw(syncMapSync coords xSize ySize font sex)]], # 14 '0A1A' => ['roulette_window', 'C V C2 v V3', [qw(result serial stage price additional_item gold silver bronze)]], '0A1C' => ['roulette_info', 'v V a*', [qw(len serial roulette_info)]], From f50bb7a805406bd55d7d1610388d2563d4362ea8 Mon Sep 17 00:00:00 2001 From: Henrybk Date: Thu, 26 Dec 2024 09:20:50 -0300 Subject: [PATCH 2/3] Fix skill failed packet (#3918) * Fix skill failed packet * corrected the translation line --------- Co-authored-by: ya4ept --- src/Network/Receive.pm | 26 +++++++++++++------------- src/Network/Receive/ServerType0.pm | 5 ++++- src/Network/Receive/kRO/Sakexe_0.pm | 5 ++++- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/Network/Receive.pm b/src/Network/Receive.pm index 5f4427c000..5287f8c187 100644 --- a/src/Network/Receive.pm +++ b/src/Network/Receive.pm @@ -11686,12 +11686,6 @@ sub isvr_disconnect { sub skill_use_failed { my ($self, $args) = @_; - # skill fail/delay - my $skillID = $args->{skillID}; - my $btype = $args->{btype}; - my $fail = $args->{fail}; - my $type = $args->{type}; - my %basefailtype = ( 0 => $msgTable[160],#"skill failed" 1 => $msgTable[161],#"no emotions" @@ -11740,10 +11734,13 @@ sub skill_use_failed { ); my $errorMessage; - if ($skillID == 1 && $type == 0 && exists $basefailtype{$btype}) { - $errorMessage = $basefailtype{$btype}; - } elsif (exists $failtype{$type}) { - $errorMessage = $failtype{$type}; + if ($args->{skillID} == 1 && $args->{cause} == 0 && exists $basefailtype{$args->{btype}}) { + $errorMessage = $basefailtype{$args->{btype}}; + } elsif (exists $failtype{$args->{cause}}) { + $errorMessage = $failtype{$args->{cause}}; + if ($args->{cause} == 71) { + $errorMessage .= T(' - item ').$args->{itemId}; + } } else { $errorMessage = T('Unknown error'); } @@ -11751,14 +11748,17 @@ sub skill_use_failed { delete $char->{casting}; my %hookArgs; - $hookArgs{skillID} = $skillID; - $hookArgs{failType} = $type; + $hookArgs{skillID} = $args->{skillID}; + $hookArgs{btype} = $args->{btype}; + $hookArgs{itemId} = $args->{itemId}; + $hookArgs{flag} = $args->{flag}; + $hookArgs{cause} = $args->{cause}; $hookArgs{failMessage} = $errorMessage; $hookArgs{warn} = 1; Plugins::callHook('packet_skillfail', \%hookArgs); - warning(TF("Skill %s failed: %s (error number %s)\n", Skill->new(idn => $skillID)->getName(), $errorMessage, $type), "skill") if ($hookArgs{warn}); + warning(TF("Skill %s failed: %s (error number %s)\n", Skill->new(idn => $args->{skillID})->getName(), $errorMessage, $args->{cause}), "skill") if ($hookArgs{warn}); } sub open_store_status { diff --git a/src/Network/Receive/ServerType0.pm b/src/Network/Receive/ServerType0.pm index e668dac8c2..801dca4a1d 100644 --- a/src/Network/Receive/ServerType0.pm +++ b/src/Network/Receive/ServerType0.pm @@ -174,7 +174,10 @@ sub new { '010C' => ['mvp_other', 'a4', [qw(ID)]], '010E' => ['skill_update', 'v4 C', [qw(skillID lv sp range up)]], # range = skill range, up = this skill can be leveled up further '010F' => ['skills_list'], - '0110' => ['skill_use_failed', 'v V C2', [qw(skillID btype fail type)]], + '0110' => ($rpackets{'0110'}{length} == 14) # or 10 + ? ['skill_use_failed', 'v V2 C2', [qw(skillID btype itemId flag cause)]] + : ['skill_use_failed', 'v v2 C2', [qw(skillID btype itemId flag cause)]] + , '0111' => ['skill_add', 'v V v3 Z24 C', [qw(skillID target lv sp range name upgradable)]], '0114' => ['skill_use', 'v a4 a4 V3 v3 C', [qw(skillID sourceID targetID tick src_speed dst_speed damage level option type)]], '0117' => ['skill_use_location', 'v a4 v3 V', [qw(skillID sourceID lv x y tick)]], diff --git a/src/Network/Receive/kRO/Sakexe_0.pm b/src/Network/Receive/kRO/Sakexe_0.pm index ed46959122..49d0a1d5fd 100644 --- a/src/Network/Receive/kRO/Sakexe_0.pm +++ b/src/Network/Receive/kRO/Sakexe_0.pm @@ -180,7 +180,10 @@ sub new { '010D' => ['mvp_item_trow'], # 2 '010E' => ['skill_update', 'v4 C', [qw(skillID lv sp range up)]], # 11 # range = skill range, up = this skill can be leveled up further '010F' => ['skills_list'], # -1 - '0110' => ['skill_use_failed', 'v V C2', [qw(skillID btype fail type)]], # 10 + '0110' => ($rpackets{'0110'}{length} == 14) # or 10 + ? ['skill_use_failed', 'v V2 C2', [qw(skillID btype itemId flag cause)]] + : ['skill_use_failed', 'v v2 C2', [qw(skillID btype itemId flag cause)]] + , '0111' => ['skill_add', 'v V v3 Z24 C', [qw(skillID target lv sp range name upgradable)]], # 39 '0114' => ['skill_use', 'v a4 a4 V3 v3 C', [qw(skillID sourceID targetID tick src_speed dst_speed damage level option type)]], # 31 '0115' => ['skill_use_position', 'v a4 a4 V3 v5 C', [qw(skillID sourceID targetID tick src_speed dst_speed x y damage level option type)]], # 35 From 6a1c6a2e323b536259130b711c059e253132c650 Mon Sep 17 00:00:00 2001 From: Henrybk Date: Thu, 26 Dec 2024 11:51:14 -0300 Subject: [PATCH 3/3] Fix slave skills (#3914) * Fix slave skills * Move skills to slave AI- --- src/AI.pm | 17 +++++-- src/AI/CoreLogic.pm | 5 +- src/AI/Slave.pm | 112 +++++++++++++++++++++++++++++++++++++++++ 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, 167 insertions(+), 25 deletions(-) diff --git a/src/AI.pm b/src/AI.pm index e344fad09f..502d1978d6 100644 --- a/src/AI.pm +++ b/src/AI.pm @@ -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); } ## 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..3a3d560e0a 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; @@ -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()) @@ -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); 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 5287f8c187..0d4b2dc457 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);