Receive.pm
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:226k
- } else {
- my $actor = Actor::get($ID);
- my $itemDisplay = itemNameSimple($itemID);
- message TF("%s used Item: %s - %s leftn", $actor, $itemDisplay, $remaining), "useItem", 2;
- }
- Plugins::callHook('packet_useitem', %hook_args);
- }
- sub married {
- my ($self, $args) = @_;
- my $actor = Actor::get($args->{ID});
- message TF("%s got married!n", $actor);
- }
- sub revolving_entity {
- my ($self, $args) = @_;
- # Monk Spirits or Gunslingers' coins
- my $sourceID = $args->{sourceID};
- my $entities = $args->{entity};
- my $entityType = "spirit";
- $entityType = "coin" if ($char->{'jobID'} == 24);
- if ($sourceID eq $accountID) {
- message TF("You have %s ".$entityType."(s) nown", $entities), "parseMsg_statuslook", 1 if $entities != $char->{spirits};
- $char->{spirits} = $entities;
- } elsif (my $actor = Actor::get($sourceID)) {
- $actor->{spirits} = $entities;
- message TF("%s has %s ".$entityType."(s) nown", $actor, $entities), "parseMsg_statuslook", 2 if $entities != $actor->{spirits};
- }
- }
- sub inventory_items_nonstackable {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my ($newmsg, $psize);
- $self->decrypt($newmsg, substr($args->{RAW_MSG}, 4));
- my $msg = substr($args->{RAW_MSG}, 0, 4) . $newmsg;
- if ($args->{switch} eq '0295') {
- $psize = 24;
- } elsif ($args->{switch} eq '02D0') {
- $psize = 26;
- } else {
- $psize = 20;
- }
- for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += $psize) {
- my $index = unpack("v1", substr($msg, $i, 2));
- my $ID = unpack("v1", substr($msg, $i + 2, 2));
- my $item = $char->inventory->getByServerIndex($index);
- my $add;
- if (!$item) {
- $item = new Actor::Item();
- $add = 1;
- }
- $item->{index} = $index;
- $item->{nameID} = $ID;
- $item->{amount} = 1;
- $item->{type} = unpack("C1", substr($msg, $i + 4, 1));
- $item->{identified} = unpack("C1", substr($msg, $i + 5, 1));
- $item->{type_equip} = unpack("v1", substr($msg, $i + 6, 2));
- $item->{equipped} = unpack("v1", substr($msg, $i + 8, 2));
- $item->{broken} = unpack("C1", substr($msg, $i + 10, 1));
- $item->{upgrade} = unpack("C1", substr($msg, $i + 11, 1));
- $item->{cards} = ($args->{switch} eq '0295') ? substr($msg, $i + 12, 12) : substr($msg, $i + 12, 8);
- $item->{name} = itemName($item);
- if ($item->{equipped}) {
- foreach (%equipSlot_rlut){
- if ($_ & $item->{equipped}){
- next if $_ == 10; #work around Arrow bug
- $char->{equipment}{$equipSlot_lut{$_}} = $item;
- }
- }
- }
- $char->inventory->add($item) if ($add);
- debug "Inventory: $item->{name} ($item->{invIndex}) x $item->{amount} - $itemTypes_lut{$item->{type}} - $equipTypes_lut{$item->{type_equip}}n", "parseMsg";
- Plugins::callHook('packet_inventory', {index => $item->{invIndex}});
- }
- $ai_v{'inventory_time'} = time + 1;
- $ai_v{'cart_time'} = time + 1;
- }
- sub inventory_items_stackable {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my ($newmsg, $psize);
- $self->decrypt($newmsg, substr($args->{RAW_MSG}, 4));
- my $msg = substr($args->{RAW_MSG}, 0, 4).$newmsg;
- if ($args->{switch} eq '00A3') {
- $psize = 10;
- } elsif ($args->{switch} eq '02E8') {
- $psize = 22;
- } else {
- $psize = 18;
- }
- for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += $psize) {
- my $index = unpack("v1", substr($msg, $i, 2));
- my $ID = unpack("v1", substr($msg, $i + 2, 2));
- my $item = $char->inventory->getByServerIndex($index);
- my $add;
- if (!$item) {
- $item = new Actor::Item();
- $add = 1;
- }
- $item->{index} = $index;
- $item->{nameID} = $ID;
- $item->{amount} = unpack("v1", substr($msg, $i + 6, 2));
- $item->{type} = unpack("C1", substr($msg, $i + 4, 1));
- $item->{identified} = 1;
- $item->{cards} = substr($msg, $i + 10, 8) if ($psize == 18);
- if (defined $char->{arrow} && $index == $char->{arrow}) {
- $item->{equipped} = 32768;
- $char->{equipment}{arrow} = $item;
- }
- $item->{name} = itemName($item);
- $char->inventory->add($item) if ($add);
-
- debug "Inventory: $item->{name} ($item->{invIndex}) x $item->{amount} - " .
- "$itemTypes_lut{$item->{type}}n", "parseMsg";
- Plugins::callHook('packet_inventory', {index => $item->{invIndex}, item => $item});
- }
- $ai_v{'inventory_time'} = time + 1;
- $ai_v{'cart_time'} = time + 1;
- }
- sub item_appeared {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my $item = $itemsList->getByID($args->{ID});
- my $mustAdd;
- if (!$item) {
- $item = new Actor::Item();
- $item->{appear_time} = time;
- $item->{amount} = $args->{amount};
- $item->{nameID} = $args->{type};
- $item->{name} = itemName($item);
- $item->{ID} = $args->{ID};
- $mustAdd = 1;
- }
- $item->{pos}{x} = $args->{x};
- $item->{pos}{y} = $args->{y};
- $item->{pos_to}{x} = $args->{x};
- $item->{pos_to}{y} = $args->{y};
- $itemsList->add($item) if ($mustAdd);
- # Take item as fast as possible
- if ($AI == 2 && pickupitems(lc($item->{name})) == 2
- && ($config{'itemsTakeAuto'} || $config{'itemsGatherAuto'})
- && (percent_weight($char) < $config{'itemsMaxWeight'})
- && distance($item->{pos}, $char->{pos_to}) <= 5) {
- $messageSender->sendTake($args->{ID});
- }
- message TF("Item Appeared: %s (%d) x %d (%d, %d)n", $item->{name}, $item->{binID}, $item->{amount}, $args->{x}, $args->{y}), "drop", 1;
- }
- sub item_exists {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my $item = $itemsList->getByID($args->{ID});
- my $mustAdd;
- if (!$item) {
- $item = new Actor::Item();
- $item->{appear_time} = time;
- $item->{amount} = $args->{amount};
- $item->{nameID} = $args->{type};
- $item->{ID} = $args->{ID};
- $item->{name} = itemName($item);
- $mustAdd = 1;
- }
- $item->{pos}{x} = $args->{x};
- $item->{pos}{y} = $args->{y};
- $item->{pos_to}{x} = $args->{x};
- $item->{pos_to}{y} = $args->{y};
- $itemsList->add($item) if ($mustAdd);
- message TF("Item Exists: %s (%d) x %dn", $item->{name}, $item->{binID}, $item->{amount}), "drop", 1;
- }
- sub item_disappeared {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my $item = $itemsList->getByID($args->{ID});
- if ($item) {
- if ($config{attackLooters} && AI::action ne "sitAuto" && pickupitems(lc($item->{name})) > 0) {
- foreach my Actor::Monster $monster (@{$monstersList->getItems()}) { # attack looter code
- if (my $control = mon_control($monster->name,$monster->{nameID})) {
- next if ( ($control->{attack_auto} ne "" && $control->{attack_auto} == -1)
- || ($control->{attack_lvl} ne "" && $control->{attack_lvl} > $char->{lv})
- || ($control->{attack_jlvl} ne "" && $control->{attack_jlvl} > $char->{lv_job})
- || ($control->{attack_hp} ne "" && $control->{attack_hp} > $char->{hp})
- || ($control->{attack_sp} ne "" && $control->{attack_sp} > $char->{sp})
- );
- }
- if (distance($item->{pos}, $monster->{pos}) == 0) {
- attack($monster->{ID});
- message TF("Attack Looter: %s looted %sn", $monster->nameIdx, $item->{name}), "looter";
- last;
- }
- }
- }
- debug "Item Disappeared: $item->{name} ($item->{binID})n", "parseMsg_presence";
- my $ID = $args->{ID};
- $items_old{$ID} = $item->deepCopy();
- $items_old{$ID}{disappeared} = 1;
- $items_old{$ID}{gone_time} = time;
- $itemsList->removeByID($ID);
- }
- }
- sub item_skill {
- my ($self, $args) = @_;
- my $skillID = $args->{skillID};
- my $targetType = $args->{targetType}; # we don't use this yet
- my $skillLv = $args->{skillLv};
- my $sp = $args->{sp}; # we don't use this yet
- my $skillName = $args->{skillName};
- my $skill = new Skill(idn => $skillID);
- message TF("Permitted to use %s (%d), level %dn", $skill->getName(), $skillID, $skillLv);
- unless ($config{noAutoSkill}) {
- $messageSender->sendSkillUse($skillID, $skillLv, $accountID);
- undef $char->{permitSkill};
- } else {
- $char->{permitSkill} = $skill;
- }
- Plugins::callHook('item_skill', {
- ID => $skillID,
- level => $skillLv,
- name => $skillName
- });
- }
- sub item_upgrade {
- my ($self, $args) = @_;
- my ($type, $index, $upgrade) = @{$args}{qw(type index upgrade)};
- my $item = $char->inventory->getByServerIndex($index);
- if ($item) {
- $item->{upgrade} = $upgrade;
- message TF("Item %s has been upgraded to +%sn", $item->{name}, $upgrade), "parseMsg/upgrade";
- $item->setName(itemName($item));
- }
- }
- sub job_equipment_hair_change {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my $actor = Actor::get($args->{ID});
- assert(UNIVERSAL::isa($actor, "Actor")) if DEBUG;
- if ($args->{part} == 0) {
- # Job change
- $actor->{jobID} = $args->{number};
- message TF("%s changed job to: %sn", $actor, $jobs_lut{$args->{number}}), "parseMsg/job", ($actor->isa('Actor::You') ? 0 : 2);
- } elsif ($args->{part} == 3) {
- # Bottom headgear change
- message TF("%s changed bottom headgear to: %sn", $actor, headgearName($args->{number})), "parseMsg_statuslook", 2 unless $actor->isa('Actor::You');
- $actor->{headgear}{low} = $args->{number} if ($actor->isa('Actor::Player') || $actor->isa('Actor::You'));
- } elsif ($args->{part} == 4) {
- # Top headgear change
- message TF("%s changed top headgear to: %sn", $actor, headgearName($args->{number})), "parseMsg_statuslook", 2 unless $actor->isa('Actor::You');
- $actor->{headgear}{top} = $args->{number} if ($actor->isa('Actor::Player') || $actor->isa('Actor::You'));
- } elsif ($args->{part} == 5) {
- # Middle headgear change
- message TF("%s changed middle headgear to: %sn", $actor, headgearName($args->{number})), "parseMsg_statuslook", 2 unless $actor->isa('Actor::You');
- $actor->{headgear}{mid} = $args->{number} if ($actor->isa('Actor::Player') || $actor->isa('Actor::You'));
- } elsif ($args->{part} == 6) {
- # Hair color change
- $actor->{hair_color} = $args->{number};
- message TF("%s changed hair color to: %s (%s)n", $actor, $haircolors{$args->{number}}, $args->{number}), "parseMsg/hairColor", ($actor->isa('Actor::You') ? 0 : 2);
- }
- #my %parts = (
- # 0 => 'Body',
- # 2 => 'Right Hand',
- # 3 => 'Low Head',
- # 4 => 'Top Head',
- # 5 => 'Middle Head',
- # 8 => 'Left Hand'
- #);
- #if ($part == 3) {
- # $part = 'low';
- #} elsif ($part == 4) {
- # $part = 'top';
- #} elsif ($part == 5) {
- # $part = 'mid';
- #}
- #
- #my $name = getActorName($ID);
- #if ($part == 3 || $part == 4 || $part == 5) {
- # my $actor = Actor::get($ID);
- # $actor->{headgear}{$part} = $items_lut{$number} if ($actor);
- # my $itemName = $items_lut{$itemID};
- # $itemName = 'nothing' if (!$itemName);
- # debug "$name changes $parts{$part} ($part) equipment to $itemNamen", "parseMsg";
- #} else {
- # debug "$name changes $parts{$part} ($part) equipment to item #$numbern", "parseMsg";
- #}
- }
- sub hp_sp_changed {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my $type = $args->{type};
- my $amount = $args->{amount};
- if ($type == 5) {
- $char->{hp} += $amount;
- $char->{hp} = $char->{hp_max} if ($char->{hp} > $char->{hp_max});
- } elsif ($type == 7) {
- $char->{sp} += $amount;
- $char->{sp} = $char->{sp_max} if ($char->{sp} > $char->{sp_max});
- }
- }
- sub local_broadcast {
- my ($self, $args) = @_;
- my $message = bytesToString($args->{message});
- message "$messagen", "schat";
- }
- sub login_error {
- my ($self, $args) = @_;
- $net->serverDisconnect();
- if ($args->{type} == 0) {
- error T("Account name doesn't existn"), "connection";
- if (!$net->clientAlive() && !$config{'ignoreInvalidLogin'} && !UNIVERSAL::isa($net, 'Network::XKoreProxy')) {
- my $username = $interface->query(T("Enter your Ragnarok Online username again."));
- if (defined($username)) {
- configModify('username', $username, 1);
- $timeout_ex{master}{time} = 0;
- $conState_tries = 0;
- } else {
- quit();
- return;
- }
- }
- } elsif ($args->{type} == 1) {
- error T("Password Errorn"), "connection";
- if (!$net->clientAlive() && !$config{'ignoreInvalidLogin'} && !UNIVERSAL::isa($net, 'Network::XKoreProxy')) {
- my $password = $interface->query(T("Enter your Ragnarok Online password again."), isPassword => 1);
- if (defined($password)) {
- configModify('password', $password, 1);
- $timeout_ex{master}{time} = 0;
- $conState_tries = 0;
- } else {
- quit();
- return;
- }
- }
- } elsif ($args->{type} == 3) {
- error T("The server has denied your connection.n"), "connection";
- } elsif ($args->{type} == 4) {
- $interface->errorDialog(T("Critical Error: Your account has been blocked."));
- $quit = 1 unless ($net->clientAlive());
- } elsif ($args->{type} == 5) {
- my $master = $masterServer;
- error TF("Connect failed, something is wrong with the login settings:n" .
- "version: %sn" .
- "master_version: %sn" .
- "serverType: %sn", $master->{version}, $master->{master_version}, $config{serverType}), "connection";
- relog(30);
- } elsif ($args->{type} == 6) {
- error T("The server is temporarily blocking your connectionn"), "connection";
- }
- if ($args->{type} != 5 && $versionSearch) {
- $versionSearch = 0;
- writeSectionedFileIntact(Settings::getTableFilename("servers.txt"), %masterServers);
- }
- }
- sub login_error_game_login_server {
- error T("Error logging into Character Server (invalid character specified)...n"), 'connection';
- $net->setState(1);
- undef $conState_tries;
- $timeout_ex{master}{time} = time;
- $timeout_ex{master}{timeout} = $timeout{'reconnect'}{'timeout'};
- $net->serverDisconnect();
- }
- # The difference between map_change and map_changed is that map_change
- # represents a map change event on the current map server, while
- # map_changed means that you've changed to a different map server.
- # map_change also represents teleport events.
- sub map_change {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my $oldMap = $field ? $field->name() : undef;
- my ($map) = $args->{map} =~ /([sS]*)./;
- checkAllowedMap($map);
- if (!$field || $map ne $field->name()) {
- eval {
- $field = new Field(name => $map);
- # Temporary backwards compatibility code.
- %field = %{$field};
- };
- if (my $e = caught('FileNotFoundException', 'IOException')) {
- error TF("Cannot load field %s: %sn", $map, $e);
- undef $field;
- } elsif ($@) {
- die $@;
- }
- }
- if ($ai_v{temp}{clear_aiQueue}) {
- AI::clear;
- AI::SlaveManager::clear();
- }
- main::initMapChangeVars();
- for (my $i = 0; $i < @ai_seq; $i++) {
- ai_setMapChanged($i);
- }
- AI::SlaveManager::setMapChanged ();
- if ($net->version == 0) {
- $ai_v{portalTrace_mapChanged} = time;
- }
- my %coords = (
- x => $args->{x},
- y => $args->{y}
- );
- $char->{pos} = {%coords};
- $char->{pos_to} = {%coords};
- message TF("Map Change: %s (%s, %s)n", $args->{map}, $char->{pos}{x}, $char->{pos}{y}), "connection";
- if ($net->version == 1) {
- ai_clientSuspend(0, 10);
- } else {
- $messageSender->sendMapLoaded();
- # Sending sync packet. Perhaps not only for server types 13 and 11
- my $serverType = $masterServer->{serverType};
- if ($serverType == 11 || $serverType == 12 || $serverType == 13 || $serverType == 14 || $serverType == 15
- || $serverType == 16 || $serverType == 17 || $serverType == 18 || $serverType == 19 || $serverType == 20) {
- $messageSender->sendSync(1);
- }
- $timeout{ai}{time} = time;
- }
- Plugins::callHook('Network::Receive::map_changed', {
- oldMap => $oldMap,
- });
-
- $timeout{ai}{time} = time;
- }
- sub map_changed {
- my ($self, $args) = @_;
- $net->setState(4);
- my $oldMap = $field ? $field->name() : undef;
- my ($map) = $args->{map} =~ /([sS]*)./;
- checkAllowedMap($map);
- if (!$field || $map ne $field->name()) {
- eval {
- $field = new Field(name => $map);
- # Temporary backwards compatibility code.
- %field = %{$field};
- };
- if (my $e = caught('FileNotFoundException', 'IOException')) {
- error TF("Cannot load field %s: %sn", $map, $e);
- undef $field;
- } elsif ($@) {
- die $@;
- }
- }
- undef $conState_tries;
- for (my $i = 0; $i < @ai_seq; $i++) {
- ai_setMapChanged($i);
- }
- AI::SlaveManager::setMapChanged ();
- $ai_v{portalTrace_mapChanged} = time;
- $map_ip = makeIP($args->{IP});
- $map_port = $args->{port};
- message(swrite(
- "---------Map Info----------", [],
- "MAP Name: @<<<<<<<<<<<<<<<<<<",
- [$args->{map}],
- "MAP IP: @<<<<<<<<<<<<<<<<<<",
- [$map_ip],
- "MAP Port: @<<<<<<<<<<<<<<<<<<",
- [$map_port],
- "-------------------------------", []),
- "connection");
- message T("Closing connection to Map Servern"), "connection";
- $net->serverDisconnect unless ($net->version == 1);
- # Reset item and skill times. The effect of items (like aspd potions)
- # and skills (like Twohand Quicken) disappears when we change map server.
- # NOTE: with the newer servers, this isn't true anymore
- my $i = 0;
- while (exists $config{"useSelf_item_$i"}) {
- if (!$config{"useSelf_item_$i"}) {
- $i++;
- next;
- }
- $ai_v{"useSelf_item_$i"."_time"} = 0;
- $i++;
- }
- $i = 0;
- while (exists $config{"useSelf_skill_$i"}) {
- if (!$config{"useSelf_skill_$i"}) {
- $i++;
- next;
- }
- $ai_v{"useSelf_skill_$i"."_time"} = 0;
- $i++;
- }
- $i = 0;
- while (exists $config{"doCommand_$i"}) {
- if (!$config{"doCommand_$i"}) {
- $i++;
- next;
- }
- $ai_v{"doCommand_$i"."_time"} = 0;
- $i++;
- }
- if ($char) {
- delete $char->{statuses};
- $char->{spirits} = 0;
- delete $char->{permitSkill};
- delete $char->{encoreSkill};
- }
- $cart{exists} = 0;
- undef %guild;
- Plugins::callHook('Network::Receive::map_changed', {
- oldMap => $oldMap,
- });
- $timeout{ai}{time} = time;
- }
- sub map_loaded {
- # Note: ServerType0 overrides this function
- my ($self, $args) = @_;
- undef $conState_tries;
- $char = $chars[$config{char}];
- $syncMapSync = pack('V1', $args->{syncMapSync});
- if ($net->version == 1) {
- $net->setState(4);
- message T("Waiting for map to load...n"), "connection";
- ai_clientSuspend(0, 10);
- main::initMapChangeVars();
- } else {
- $messageSender->sendGuildInfoRequest();
- # Replies 01B6 (Guild Info) and 014C (Guild Ally/Enemy List)
- $messageSender->sendGuildRequest(0);
- # Replies 0166 (Guild Member Titles List) and 0154 (Guild Members List)
- $messageSender->sendGuildRequest(1);
- $messageSender->sendMapLoaded();
- $messageSender->sendSync(1);
- debug "Sent initial syncn", "connection";
- $timeout{'ai'}{'time'} = time;
- }
- if ($char && changeToInGameState()) {
- $net->setState(Network::IN_GAME) if ($net->getState() != Network::IN_GAME);
- $char->{pos} = {};
- makeCoords($char->{pos}, $args->{coords});
- $char->{pos_to} = {%{$char->{pos}}};
- message(TF("Your Coordinates: %s, %sn", $char->{pos}{x}, $char->{pos}{y}), undef, 1);
- message(T("You are now in the gamen"), "connection");
- Plugins::callHook('in_game');
- }
- $messageSender->sendIgnoreAll("all") if ($config{ignoreAll});
- }
- sub memo_success {
- my ($self, $args) = @_;
- if ($args->{fail}) {
- warning T("Memo Failedn");
- } else {
- message T("Memo Succeededn"), "success";
- }
- }
- {
- my %mercenaryParam = (
- 0x05 => 'hp',
- 0x06 => 'hp_max',
- 0x07 => 'sp',
- 0x08 => 'sp_max',
- 0x29 => 'atk',
- 0x31 => 'hit',
- 0x35 => 'aspd',
- 0xA5 => 'flee',
- 0xBD => 'kills',
- 0xBE => 'faith',
- );
-
- sub mercenary_param_change {
- my ($self, $args) = @_;
-
- return unless $char->{mercenary};
-
- if (my $type = $mercenaryParam{$args->{type}}) {
- $char->{mercenary}{$type} = $args->{param};
-
- $char->{mercenary}{aspdDisp} = int (200 - (($char->{mercenary}{aspd} < 10) ? 10 : ($char->{mercenary}{aspd} / 10)));
- $char->{mercenary}{hpPercent} = $char->{mercenary}{hp_max} ? 100 * $char->{mercenary}{hp} / $char->{mercenary}{hp_max} : 0;
- $char->{mercenary}{spPercent} = $char->{mercenary}{sp_max} ? 100 * $char->{mercenary}{sp} / $char->{mercenary}{sp_max} : 0;
-
- debug "Mercenary: $type = $args->{param}n";
- } else {
- message "Unknown mercenary param received (type: $args->{type}; param: $args->{param}; raw: " . unpack ('H*', $args->{RAW_MSG}) . ")n";
- }
- }
- }
- # +message_string
- sub mercenary_off {
- delete $char->{mercenary};
- }
- # -message_string
- sub message_string {
- my ($self, $args) = @_;
-
- if ($args->{msg_id} == 0x04F2) {
- message "Mercenary soldier's duty hour is over.n";
- $self->mercenary_off ();
-
- } elsif ($args->{msg_id} == 0x04F3) {
- message "Your mercenary soldier has been killed.n";
- $self->mercenary_off ();
-
- } elsif ($args->{msg_id} == 0x04F4) {
- message "Your mercenary soldier has been fired.n";
- $self->mercenary_off ();
-
- } elsif ($args->{msg_id} == 0x04F5) {
- message "Your mercenary soldier has ran away.n";
- $self->mercenary_off ();
-
- } else {
- message "Unknown message received ($args->{msg_id})n";
- }
- }
- sub minimap_indicator {
- my ($self, $args) = @_;
- if ($args->{type} == 2) {
- message TF("Minimap indicator at location %d, %d " .
- "with the color %s clearedn", $args->{x}, $args->{y}, "[R:$args->{red}, G:$args->{green}, B:$args->{blue}, A:$args->{alpha}]"),
- "info";
- } else {
- message TF("Minimap indicator at location %d, %d " .
- "with the color %s shownn", $args->{x}, $args->{y}, "[R:$args->{red}, G:$args->{green}, B:$args->{blue}, A:$args->{alpha}]"),
- "info";
- }
- }
- sub monster_typechange {
- my ($self, $args) = @_;
- # Class change / monster type change
- # 01B0 : long ID, byte WhateverThisIs, long type
- my $ID = $args->{ID};
- my $type = $args->{type};
- my $monster = $monstersList->getByID($ID);
- if ($monster) {
- my $oldName = $monster->name;
- if ($monsters_lut{$type}) {
- $monster->setName($monsters_lut{$type});
- } else {
- $monster->setName(undef);
- }
- $monster->{nameID} = $type;
- $monster->{dmgToParty} = 0;
- $monster->{dmgFromParty} = 0;
- $monster->{missedToParty} = 0;
- message TF("Monster %s (%d) changed to %sn", $oldName, $monster->{binID}, $monster->name);
- }
- }
- sub monster_ranged_attack {
- my ($self, $args) = @_;
- my $ID = $args->{ID};
- my $type = $args->{type};
- my %coords1;
- $coords1{x} = $args->{sourceX};
- $coords1{y} = $args->{sourceY};
- my %coords2;
- $coords2{x} = $args->{targetX};
- $coords2{y} = $args->{targetY};
- my $monster = $monstersList->getByID($ID);
- $monster->{pos_attack_info} = {%coords1} if ($monster);
- $char->{pos} = {%coords2};
- $char->{pos_to} = {%coords2};
- debug "Received attack location - monster: $coords1{x},$coords1{y} - " .
- "you: $coords2{x},$coords2{y}n", "parseMsg_move", 2;
- }
- sub mvp_item {
- my ($self, $args) = @_;
- my $display = itemNameSimple($args->{itemID});
- message TF("Get MVP item %sn", $display);
- chatLog("k", TF("Get MVP item %sn", $display));
- }
- sub mvp_other {
- my ($self, $args) = @_;
- my $display = Actor::get($args->{ID});
- message TF("%s become MVP!n", $display);
- chatLog("k", TF("%s became MVP!n", $display));
- }
- sub mvp_you {
- my ($self, $args) = @_;
- my $msg = TF("Congratulations, you are the MVP! Your reward is %s exp!n", $args->{expAmount});
- message $msg;
- chatLog("k", $msg);
- }
- sub npc_image {
- my ($self, $args) = @_;
- my ($imageName) = bytesToString($args->{npc_image});
- if ($args->{type} == 2) {
- debug "Show NPC image: $imageNamen", "parseMsg";
- } elsif ($args->{type} == 255) {
- debug "Hide NPC image: $imageNamen", "parseMsg";
- } else {
- debug "NPC image: $imageName ($args->{type})n", "parseMsg";
- }
- }
- sub npc_sell_list {
- my ($self, $args) = @_;
- #sell list, similar to buy list
- if (length($args->{RAW_MSG}) > 4) {
- my $newmsg;
- $self->decrypt($newmsg, substr($args->{RAW_MSG}, 4));
- my $msg = substr($args->{RAW_MSG}, 0, 4).$newmsg;
- }
- undef $talk{buyOrSell};
- message T("Ready to start selling itemsn");
- # continue talk sequence now
- $ai_v{npc_talk}{time} = time;
- }
- sub npc_store_begin {
- my ($self, $args) = @_;
- undef %talk;
- $talk{buyOrSell} = 1;
- $talk{ID} = $args->{ID};
- $ai_v{npc_talk}{talk} = 'buy';
- $ai_v{npc_talk}{time} = time;
- my $name = getNPCName($args->{ID});
- message TF("%s: Type 'store' to start buying, or type 'sell' to start sellingn", $name), "npc";
- }
- sub npc_store_info {
- my ($self, $args) = @_;
- my $newmsg;
- $self->decrypt($newmsg, substr($args->{RAW_MSG}, 4));
- my $msg = substr($args->{RAW_MSG}, 0, 4).$newmsg;
- undef @storeList;
- my $storeList = 0;
- undef $talk{'buyOrSell'};
- for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += 11) {
- my $price = unpack("V1", substr($msg, $i, 4));
- my $type = unpack("C1", substr($msg, $i + 8, 1));
- my $ID = unpack("v1", substr($msg, $i + 9, 2));
- my $store = $storeList[$storeList] = {};
- my $display = ($items_lut{$ID} ne "")
- ? $items_lut{$ID}
- : "Unknown ".$ID;
- $store->{name} = $display;
- $store->{nameID} = $ID;
- $store->{type} = $type;
- $store->{price} = $price;
- debug "Item added to Store: $store->{name} - $price zn", "parseMsg", 2;
- $storeList++;
- }
- my $name = getNPCName($talk{ID});
- $ai_v{npc_talk}{talk} = 'store';
- # continue talk sequence now
- $ai_v{'npc_talk'}{'time'} = time;
- if (AI::action ne 'buyAuto') {
- message TF("----------%s's Store List-----------n" .
- "# Name Type Pricen", $name), "list";
- my $display;
- for (my $i = 0; $i < @storeList; $i++) {
- $display = $storeList[$i]{'name'};
- message(swrite(
- "@< @<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<< @>>>>>>>z",
- [$i, $display, $itemTypes_lut{$storeList[$i]{'type'}}, $storeList[$i]{'price'}]),
- "list");
- }
- message("-------------------------------n", "list");
- }
- }
- sub npc_talk {
- my ($self, $args) = @_;
- my $newmsg;
- $self->decrypt($newmsg, substr($args->{RAW_MSG}, 8));
- my $msg = substr($args->{RAW_MSG}, 0, 8) . $newmsg;
- my $ID = substr($msg, 4, 4);
- my $talkMsg = unpack("Z*", substr($msg, 8));
- $talk{ID} = $ID;
- $talk{nameID} = unpack("V1", $ID);
- $talk{msg} = bytesToString($talkMsg);
- # Remove RO color codes
- $talk{msg} =~ s/^[a-fA-F0-9]{6}//g;
- $ai_v{npc_talk}{talk} = 'initiated';
- $ai_v{npc_talk}{time} = time;
- my $name = getNPCName($ID);
- Plugins::callHook('npc_talk', {
- ID => $ID,
- nameID => $talk{nameID},
- name => $name,
- msg => $talk{msg},
- });
- message "$name: $talk{msg}n", "npc";
- }
- sub npc_talk_close {
- my ($self, $args) = @_;
- # 00b6: long ID
- # "Close" icon appreared on the NPC message dialog
- my $ID = $args->{ID};
- my $name = getNPCName($ID);
- message TF("%s: Done talkingn", $name), "npc";
- # I noticed that the RO client doesn't send a 'talk cancel' packet
- # when it receives a 'npc_talk_closed' packet from the server'.
- # But on pRO Thor (with Kapra password) this is required in order to
- # open the storage.
- #
- # UPDATE: not sending 'talk cancel' breaks autostorage on iRO.
- # This needs more investigation.
- if (!$talk{canceled}) {
- $messageSender->sendTalkCancel($ID);
- }
- $ai_v{npc_talk}{talk} = 'close';
- $ai_v{npc_talk}{time} = time;
- undef %talk;
- Plugins::callHook('npc_talk_done', {ID => $ID});
- }
- sub npc_talk_continue {
- my ($self, $args) = @_;
- my $ID = substr($args->{RAW_MSG}, 2, 4);
- my $name = getNPCName($ID);
- $ai_v{npc_talk}{talk} = 'next';
- $ai_v{npc_talk}{time} = time;
- if ($config{autoTalkCont}) {
- message TF("%s: Auto-continuing talkingn", $name), "npc";
- $messageSender->sendTalkContinue($ID);
- # This time will be reset once the NPC responds
- $ai_v{npc_talk}{time} = time + $timeout{'ai_npcTalk'}{'timeout'} + 5;
- } else {
- message TF("%s: Type 'talk cont' to continue talkingn", $name), "npc";
- }
- }
- sub npc_talk_number {
- my ($self, $args) = @_;
- my $ID = $args->{ID};
- my $name = getNPCName($ID);
- $ai_v{npc_talk}{talk} = 'number';
- $ai_v{npc_talk}{time} = time;
- message TF("%s: Type 'talk num <number #>' to input a number.n", $name), "input";
- $ai_v{'npc_talk'}{'talk'} = 'num';
- $ai_v{'npc_talk'}{'time'} = time;
- }
- sub npc_talk_responses {
- my ($self, $args) = @_;
- # 00b7: word len, long ID, string str
- # A list of selections appeared on the NPC message dialog.
- # Each item is divided with ':'
- my $newmsg;
- $self->decrypt($newmsg, substr($args->{RAW_MSG}, 8));
- my $msg = substr($args->{RAW_MSG}, 0, 8).$newmsg;
- my $ID = substr($msg, 4, 4);
- $talk{ID} = $ID;
- my $talk = unpack("Z*", substr($msg, 8));
- $talk = substr($msg, 8) if (!defined $talk);
- $talk = bytesToString($talk);
- my @preTalkResponses = split /:/, $talk;
- $talk{responses} = [];
- foreach my $response (@preTalkResponses) {
- # Remove RO color codes
- $response =~ s/^[a-fA-F0-9]{6}//g;
- if ($response =~ /^^nItemID^(d+)$/) {
- $response = itemNameSimple($1);
- }
- push @{$talk{responses}}, $response if ($response ne "");
- }
- $talk{responses}[@{$talk{responses}}] = "Cancel Chat";
- $ai_v{'npc_talk'}{'talk'} = 'select';
- $ai_v{'npc_talk'}{'time'} = time;
- my $list = T("----------Responses-----------n" .
- "# Responsen");
- for (my $i = 0; $i < @{$talk{responses}}; $i++) {
- $list .= swrite(
- "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
- [$i, $talk{responses}[$i]]);
- }
- $list .= "-------------------------------n";
- message($list, "list");
- my $name = getNPCName($ID);
- Plugins::callHook('npc_talk_responses', {
- ID => $ID,
- name => $name,
- responses => $talk{responses},
- });
- message TF("%s: Type 'talk resp #' to choose a response.n", $name), "npc";
- }
- sub npc_talk_text {
- my ($self, $args) = @_;
- my $ID = $args->{ID};
- my $name = getNPCName($ID);
- message TF("%s: Type 'talk text' (Respond to NPC)n", $name), "npc";
- $ai_v{npc_talk}{talk} = 'text';
- $ai_v{npc_talk}{time} = time;
- }
- # Structure of packet by eAthena
- sub objective_info {
- my ($self, $args) = @_;
- my ($questID, $time, $mobnum, $mobCount, $mobName, $i, $k);
- for ($i = 8; $i < $args->{RAW_MSG_SIZE}; $i += 104) {
- ($questID, $time, $mobnum) = unpack('V x4 V v', substr($args->{RAW_MSG}, $i, 14));
- # TODO
- for ($k = 14; $k < 104; $k += 30) {
- ($mobCount, $mobName) = unpack('x4 v Z24', substr($args->{RAW_MSG}, $k, 30));
- $mobName = bytesToString($mobName);
- # TODO
- }
- }
- }
- sub party_allow_invite {
- my ($self, $args) = @_;
- if ($args->{type}) {
- message T("Not allowed other player invite to Partyn"), "party", 1;
- } else {
- message T("Allowed other player invite to Partyn"), "party", 1;
- }
- }
- sub party_chat {
- my ($self, $args) = @_;
- my $msg;
- $self->decrypt($msg, $args->{message});
- $msg = bytesToString($msg);
- # Type: String
- my ($chatMsgUser, $chatMsg) = $msg =~ /(.*?) : (.*)/;
- $chatMsgUser =~ s/ $//;
- stripLanguageCode($chatMsg);
- # Type: String
- my $chat = "$chatMsgUser : $chatMsg";
- message TF("[Party] %sn", $chat), "partychat";
- chatLog("p", "$chatn") if ($config{'logPartyChat'});
- ChatQueue::add('p', $args->{ID}, $chatMsgUser, $chatMsg);
- Plugins::callHook('packet_partyMsg', {
- MsgUser => $chatMsgUser,
- Msg => $chatMsg
- });
- }
- sub party_exp {
- my ($self, $args) = @_;
- $char->{party}{share} = $args->{type};
- if ($args->{type} == 0) {
- message T("Party EXP set to Individual Taken"), "party", 1;
- } elsif ($args->{type} == 1) {
- message T("Party EXP set to Even Sharen"), "party", 1;
- } else {
- error T("Error setting party optionn");
- }
- }
- sub party_hp_info {
- my ($self, $args) = @_;
- my $ID = $args->{ID};
- $char->{party}{users}{$ID}{hp} = $args->{hp};
- $char->{party}{users}{$ID}{hp_max} = $args->{hp_max};
- }
- sub party_invite {
- my ($self, $args) = @_;
- message TF("Incoming Request to join party '%s'n", bytesToString($args->{name}));
- $incomingParty{ID} = $args->{ID};
- $timeout{ai_partyAutoDeny}{time} = time;
- }
- sub party_invite_result {
- my ($self, $args) = @_;
- my $name = bytesToString($args->{name});
- if ($args->{type} == 0) {
- warning TF("Join request failed: %s is already in a partyn", $name);
- } elsif ($args->{type} == 1) {
- warning TF("Join request failed: %s denied requestn", $name);
- } elsif ($args->{type} == 2) {
- message TF("%s accepted your requestn", $name), "info";
- } elsif ($args->{type} == 3) {
- message T("Join request failed: Party is full.n"), "info";
- } elsif ($args->{type} == 4) {
- message TF("Join request failed: same account of %s allready joined the party.n", $name), "info";
- }
- }
- sub party_join {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my ($ID, $x, $y, $type, $name, $user, $map) = @{$args}{qw(ID x y type name user map)};
- $name = bytesToString($name);
- $user = bytesToString($user);
- if (!$char->{party} || !%{$char->{party}} || !$char->{party}{users}{$ID} || !%{$char->{party}{users}{$ID}}) {
- binAdd(@partyUsersID, $ID) if (binFind(@partyUsersID, $ID) eq "");
- if ($ID eq $accountID) {
- message TF("You joined party '%s'n", $name), undef, 1;
- $char->{party} = {};
- } else {
- message TF("%s joined your party '%s'n", $user, $name), undef, 1;
- }
- }
- $char->{party}{users}{$ID} = new Actor::Party if ($char->{party}{users}{$ID}{name});
- if ($type == 0) {
- $char->{party}{users}{$ID}{online} = 1;
- } elsif ($type == 1) {
- $char->{party}{users}{$ID}{online} = 0;
- delete $char->{party}{users}{$ID}{statuses};
- }
- $char->{party}{name} = $name;
- $char->{party}{users}{$ID}{pos}{x} = $x;
- $char->{party}{users}{$ID}{pos}{y} = $y;
- $char->{party}{users}{$ID}{map} = $map;
- $char->{party}{users}{$ID}{name} = $user;
- if ($config{partyAutoShare} && $char->{party} && $char->{party}{users}{$accountID}{admin}) {
- $messageSender->sendPartyShareEXP(1);
- }
- }
- sub party_leave {
- my ($self, $args) = @_;
- my $ID = $args->{ID};
- delete $char->{party}{users}{$ID};
- binRemove(@partyUsersID, $ID);
- if ($ID eq $accountID) {
- message T("You left the partyn");
- delete $char->{party};
- undef @partyUsersID;
- } else {
- message TF("%s left the partyn", bytesToString($args->{name}));
- }
- }
- sub party_location {
- my ($self, $args) = @_;
- my $ID = $args->{ID};
- $char->{party}{users}{$ID}{pos}{x} = $args->{x};
- $char->{party}{users}{$ID}{pos}{y} = $args->{y};
- $char->{party}{users}{$ID}{online} = 1;
- debug "Party member location: $char->{party}{users}{$ID}{name} - $args->{x}, $args->{y}n", "parseMsg";
- }
- sub party_organize_result {
- my ($self, $args) = @_;
- if ($args->{fail}) {
- warning T("Can't organize party - party name existsn");
- } else {
- $char->{party}{users}{$accountID}{admin} = 1;
- }
- }
- sub party_users_info {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my $msg;
- $self->decrypt($msg, substr($args->{RAW_MSG}, 28));
- $msg = substr($args->{RAW_MSG}, 0, 28).$msg;
- $char->{party}{name} = bytesToString($args->{party_name});
- for (my $i = 28; $i < $args->{RAW_MSG_SIZE}; $i += 46) {
- my $ID = substr($msg, $i, 4);
- my $num = unpack("C1", substr($msg, $i + 44, 1));
- if (binFind(@partyUsersID, $ID) eq "") {
- binAdd(@partyUsersID, $ID);
- }
- $char->{party}{users}{$ID} = new Actor::Party();
- $char->{party}{users}{$ID}{name} = bytesToString(unpack("Z24", substr($msg, $i + 4, 24)));
- message TF("Party Member: %sn", $char->{party}{users}{$ID}{name}), "party", 1;
- $char->{party}{users}{$ID}{map} = unpack("Z16", substr($msg, $i + 28, 16));
- $char->{party}{users}{$ID}{online} = !(unpack("C1",substr($msg, $i + 45, 1)));
- $char->{party}{users}{$ID}{admin} = 1 if ($num == 0);
- }
- if ($config{partyAutoShare} && $char->{party} && %{$char->{party}}) {
- $messageSender->sendPartyShareEXP(1);
- }
- }
- sub pet_capture_result {
- my ($self, $args) = @_;
- if ($args->{success}) {
- message T("Pet capture successn");
- } else {
- message T("Pet capture failedn");
- }
- }
- sub pet_emotion {
- my ($self, $args) = @_;
- my ($ID, $type) = ($args->{ID}, $args->{type});
- my $emote = $emotions_lut{$type}{display} || "/e$type";
- if ($pets{$ID}) {
- message $pets{$ID}->name . " : $emoten", "emotion";
- }
- }
- sub pet_food {
- my ($self, $args) = @_;
- if ($args->{success}) {
- message TF("Fed pet with %sn", itemNameSimple($args->{foodID})), "pet";
- } else {
- error TF("Failed to feed pet with %s: no food in inventory.n", itemNameSimple($args->{foodID}));
- }
- }
- sub pet_info {
- my ($self, $args) = @_;
- $pet{name} = bytesToString($args->{name});
- $pet{nameflag} = $args->{nameflag};
- $pet{level} = $args->{level};
- $pet{hungry} = $args->{hungry};
- $pet{friendly} = $args->{friendly};
- $pet{accessory} = $args->{accessory};
- $pet{type} = $args->{type} if $args->{type};
- debug "Pet status: name: $pet{name} name set?: ". ($pet{nameflag} ? 'yes' : 'no') ." level=$pet{level} hungry=$pet{hungry} intimacy=$pet{friendly} accessory=".itemNameSimple($pet{accessory})." type=".$pet{type}||"N/A"."n", "pet";
- }
- sub pet_info2 {
- my ($self, $args) = @_;
- my ($type, $ID, $value) = @{$args}{qw(type ID value)};
- # receive information about your pet
- # related freya functions: clif_pet_equip clif_pet_performance clif_send_petdata
- # these should never happen, pets should spawn like normal actors (at least on Freya)
- # this isn't even very useful, do we want random pets with no location info?
- #if (!$pets{$ID} || !%{$pets{$ID}}) {
- # binAdd(@petsID, $ID);
- # $pets{$ID} = {};
- # %{$pets{$ID}} = %{$monsters{$ID}} if ($monsters{$ID} && %{$monsters{$ID}});
- # $pets{$ID}{'name_given'} = "Unknown";
- # $pets{$ID}{'binID'} = binFind(@petsID, $ID);
- # debug "Pet spawned (unusually): $pets{$ID}{'name'} ($pets{$ID}{'binID'})n", "parseMsg";
- #}
- #if ($monsters{$ID}) {
- # if (%{$monsters{$ID}}) {
- # objectRemoved('monster', $ID, $monsters{$ID});
- # }
- # # always clear these in case
- # binRemove(@monstersID, $ID);
- # delete $monsters{$ID};
- #}
- if ($type == 0) {
- # You own no pet.
- undef $pet{ID};
- } elsif ($type == 1) {
- $pet{friendly} = $value;
- debug "Pet friendly: $valuen";
- } elsif ($type == 2) {
- $pet{hungry} = $value;
- debug "Pet hungry: $valuen";
- } elsif ($type == 3) {
- # accessory info for any pet in range
- #debug "Pet accessory info: $valuen";
- } elsif ($type == 4) {
- # performance info for any pet in range
- #debug "Pet performance info: $valuen";
- } elsif ($type == 5) {
- # You own pet with this ID
- $pet{ID} = $ID;
- }
- }
- sub player_equipment {
- my ($self, $args) = @_;
- my ($sourceID, $type, $ID1, $ID2) = @{$args}{qw(sourceID type ID1 ID2)};
- my $player = ($sourceID ne $accountID)? $playersList->getByID($sourceID) : $char;
- return unless $player;
- if ($type == 0) {
- # Player changed job
- $player->{jobID} = $ID1;
- } elsif ($type == 2) {
- if ($ID1 ne $player->{weapon}) {
- message TF("%s changed Weapon to %sn", $player, itemName({nameID => $ID1})), "parseMsg_statuslook", 2;
- $player->{weapon} = $ID1;
- }
- if ($ID2 ne $player->{shield}) {
- message TF("%s changed Shield to %sn", $player, itemName({nameID => $ID2})), "parseMsg_statuslook", 2;
- $player->{shield} = $ID2;
- }
- } elsif ($type == 3) {
- $player->{headgear}{low} = $ID1;
- } elsif ($type == 4) {
- $player->{headgear}{top} = $ID1;
- } elsif ($type == 5) {
- $player->{headgear}{mid} = $ID1;
- } elsif ($type == 9) {
- if ($player->{shoes} && $ID1 ne $player->{shoes}) {
- message TF("%s changed Shoes to: %sn", $player, itemName({nameID => $ID1})), "parseMsg_statuslook", 2;
- }
- $player->{shoes} = $ID1;
- }
- }
- sub public_chat {
- my ($self, $args) = @_;
- # Type: String
- my $message = bytesToString($args->{message});
- my ($chatMsgUser, $chatMsg); # Type: String
- my ($actor, $dist);
- if ($message =~ /:/) {
- ($chatMsgUser, $chatMsg) = split /:/, $message, 2;
- $chatMsgUser =~ s/ $//;
- $chatMsg =~ s/^ //;
- stripLanguageCode($chatMsg);
- $actor = Actor::get($args->{ID});
- $dist = "unknown";
- if (!$actor->isa('Actor::Unknown')) {
- $dist = distance($char->{pos_to}, $actor->{pos_to});
- $dist = sprintf("%.1f", $dist) if ($dist =~ /./);
- }
- $message = "$chatMsgUser ($actor->{binID}): $chatMsg";
- } else {
- $chatMsg = $message;
- }
- my $position = sprintf("[%s %d, %d]",
- $field ? $field->name() : T("Unknown field,"),
- $char->{pos_to}{x}, $char->{pos_to}{y});
- my $distInfo;
- if ($actor) {
- $position .= sprintf(" [%d, %d] [dist=%s] (%d)",
- $actor->{pos_to}{x}, $actor->{pos_to}{y},
- $dist, $actor->{nameID});
- $distInfo = "[dist=$dist] ";
- }
- # this code autovivifies $actor->{pos_to} but it doesnt matter
- chatLog("c", "$position $messagen") if ($config{logChat});
- message TF("%s%sn", $distInfo, $message), "publicchat";
- ChatQueue::add('c', $args->{ID}, $chatMsgUser, $chatMsg);
- Plugins::callHook('packet_pubMsg', {
- pubID => $args->{ID},
- pubMsgUser => $chatMsgUser,
- pubMsg => $chatMsg,
- MsgUser => $chatMsgUser,
- Msg => $chatMsg
- });
- }
- sub private_message {
- my ($self, $args) = @_;
- my ($newmsg, $msg); # Type: Bytes
- return unless changeToInGameState();
- # Type: String
- my $privMsgUser = bytesToString($args->{privMsgUser});
- my $privMsg = bytesToString($args->{privMsg});
- if ($privMsgUser ne "" && binFind(@privMsgUsers, $privMsgUser) eq "") {
- push @privMsgUsers, $privMsgUser;
- Plugins::callHook('parseMsg/addPrivMsgUser', {
- user => $privMsgUser,
- msg => $privMsg,
- userList => @privMsgUsers
- });
- }
- stripLanguageCode($privMsg);
- chatLog("pm", TF("(From: %s) : %sn", $privMsgUser, $privMsg)) if ($config{'logPrivateChat'});
- message TF("(From: %s) : %sn", $privMsgUser, $privMsg), "pm";
- ChatQueue::add('pm', undef, $privMsgUser, $privMsg);
- Plugins::callHook('packet_privMsg', {
- privMsgUser => $privMsgUser,
- privMsg => $privMsg,
- MsgUser => $privMsgUser,
- Msg => $privMsg
- });
- if ($config{dcOnPM} && $AI == 2) {
- chatLog("k", T("*** You were PM'd, auto disconnect! ***n"));
- message T("Disconnecting on PM!n");
- quit();
- }
- }
- sub private_message_sent {
- my ($self, $args) = @_;
- if ($args->{type} == 0) {
- message TF("(To %s) : %sn", $lastpm[0]{'user'}, $lastpm[0]{'msg'}), "pm/sent";
- chatLog("pm", "(To: $lastpm[0]{user}) : $lastpm[0]{msg}n") if ($config{'logPrivateChat'});
- Plugins::callHook('packet_sentPM', {
- to => $lastpm[0]{user},
- msg => $lastpm[0]{msg}
- });
- } elsif ($args->{type} == 1) {
- warning TF("%s is not onlinen", $lastpm[0]{user});
- } elsif ($args->{type} == 2) {
- warning T("Player ignored your messagen");
- } else {
- warning T("Player doesn't want to receive messagesn");
- }
- shift @lastpm;
- }
- # Structure of Packet by eAthena
- sub quest_list {
- my ($self, $args) = @_;
- my ($questID, $state, $i);
- for ($i = 8; $i < $args->{RAW_MSG_SIZE}; $i += 5) {
- ($questID, $state) = unpack('V C', substr($args->{RAW_MSG}, $i, 5));
- # TODO
- }
- }
- # The block size in the received_characters packet varies from server to server.
- # This method may be overrided in other ServerType handlers to return
- # the correct block size.
- sub received_characters_blockSize {
- if ($masterServer && $masterServer->{charBlockSize}) {
- return $masterServer->{charBlockSize};
- } else {
- return 106;
- }
- }
- sub received_characters {
- return if ($net->getState() == Network::IN_GAME);
- my ($self, $args) = @_;
- message T("Received characters from Character Servern"), "connection";
- $net->setState(Network::CONNECTED_TO_LOGIN_SERVER);
- undef $conState_tries;
- undef @chars;
- Plugins::callHook('parseMsg/recvChars', $args->{options});
- if ($args->{options} && exists $args->{options}{charServer}) {
- $charServer = $args->{options}{charServer};
- } else {
- $charServer = $net->serverPeerHost . ":" . $net->serverPeerPort;
- }
- my $num;
- my $blockSize = $self->received_characters_blockSize();
- for (my $i = $args->{RAW_MSG_SIZE} % $blockSize; $i < $args->{RAW_MSG_SIZE}; $i += $blockSize) {
- #exp display bugfix - chobit andy 20030129
- $num = unpack("C1", substr($args->{RAW_MSG}, $i + 104, 1));
- $chars[$num] = new Actor::You;
- $chars[$num]{ID} = $accountID;
- $chars[$num]{charID} = substr($args->{RAW_MSG}, $i, 4);
- $chars[$num]{nameID} = unpack("V", $chars[$num]{ID});
- $chars[$num]{exp} = unpack("V", substr($args->{RAW_MSG}, $i + 4, 4));
- $chars[$num]{zenny} = unpack("V", substr($args->{RAW_MSG}, $i + 8, 4));
- $chars[$num]{exp_job} = unpack("V", substr($args->{RAW_MSG}, $i + 12, 4));
- $chars[$num]{lv_job} = unpack("v", substr($args->{RAW_MSG}, $i + 16, 2));
- $chars[$num]{hp} = unpack("v", substr($args->{RAW_MSG}, $i + 42, 2));
- $chars[$num]{hp_max} = unpack("v", substr($args->{RAW_MSG}, $i + 44, 2));
- $chars[$num]{sp} = unpack("v", substr($args->{RAW_MSG}, $i + 46, 2));
- $chars[$num]{sp_max} = unpack("v", substr($args->{RAW_MSG}, $i + 48, 2));
- $chars[$num]{jobID} = unpack("v", substr($args->{RAW_MSG}, $i + 52, 2));
- $chars[$num]{hair_style} = unpack("v", substr($args->{RAW_MSG}, $i + 54, 2));
- $chars[$num]{lv} = unpack("v", substr($args->{RAW_MSG}, $i + 58, 2));
- $chars[$num]{headgear}{low} = unpack("v", substr($args->{RAW_MSG}, $i + 62, 2));
- $chars[$num]{headgear}{top} = unpack("v", substr($args->{RAW_MSG}, $i + 66, 2));
- $chars[$num]{headgear}{mid} = unpack("v", substr($args->{RAW_MSG}, $i + 68, 2));
- $chars[$num]{hair_color} = unpack("v", substr($args->{RAW_MSG}, $i + 70, 2));
- $chars[$num]{clothes_color} = unpack("v", substr($args->{RAW_MSG}, $i + 72, 2));
- ($chars[$num]{name}) = unpack("Z*", substr($args->{RAW_MSG}, $i + 74, 24));
- $chars[$num]{str} = unpack("C1", substr($args->{RAW_MSG}, $i + 98, 1));
- $chars[$num]{agi} = unpack("C1", substr($args->{RAW_MSG}, $i + 99, 1));
- $chars[$num]{vit} = unpack("C1", substr($args->{RAW_MSG}, $i + 100, 1));
- $chars[$num]{int} = unpack("C1", substr($args->{RAW_MSG}, $i + 101, 1));
- $chars[$num]{dex} = unpack("C1", substr($args->{RAW_MSG}, $i + 102, 1));
- $chars[$num]{luk} = unpack("C1", substr($args->{RAW_MSG}, $i + 103, 1));
- $chars[$num]{sex} = $accountSex2;
- $chars[$num]{name} = bytesToString($chars[$num]{name});
- }
- # gradeA says it's supposed to send this packet here, but
- # it doesn't work...
- # 30 Dec 2005: it didn't work before because it wasn't sending the accountiD -> fixed (kaliwanagan)
- $messageSender->sendBanCheck($accountID) if (!$net->clientAlive && $config{serverType} == 2);
- if (charSelectScreen(1) == 1) {
- $firstLoginMap = 1;
- $startingZenny = $chars[$config{'char'}]{'zenny'} unless defined $startingZenny;
- $sentWelcomeMessage = 1;
- }
- }
- sub received_character_ID_and_Map {
- my ($self, $args) = @_;
- message T("Received character ID and Map IP from Character Servern"), "connection";
- $net->setState(4);
- undef $conState_tries;
- $charID = $args->{charID};
- if ($net->version == 1) {
- undef $masterServer;
- $masterServer = $masterServers{$config{master}} if ($config{master} ne "");
- }
- my ($map) = $args->{mapName} =~ /([sS]*)./;
- if (!$field || $map ne $field->name()) {
- eval {
- $field = new Field(name => $map);
- # Temporary backwards compatibility code.
- %field = %{$field};
- };
- if (my $e = caught('FileNotFoundException', 'IOException')) {
- error TF("Cannot load field %s: %sn", $map, $e);
- undef $field;
- } elsif ($@) {
- die $@;
- }
- }
- $map_ip = makeIP($args->{mapIP});
- $map_ip = $masterServer->{ip} if ($masterServer && $masterServer->{private});
- $map_port = $args->{mapPort};
- message TF("----------Game Info----------n" .
- "Char ID: %s (%s)n" .
- "MAP Name: %sn" .
- "MAP IP: %sn" .
- "MAP Port: %sn" .
- "-----------------------------n", getHex($charID), unpack("V1", $charID),
- $args->{mapName}, $map_ip, $map_port), "connection";
- ($map) = $args->{mapName} =~ /([sS]*)./;
- checkAllowedMap($map);
- message(T("Closing connection to Character Servern"), "connection") unless ($net->version == 1);
- $net->serverDisconnect();
- main::initStatVars();
- }
- sub received_sync {
- return unless changeToInGameState();
- debug "Received Syncn", 'parseMsg', 2;
- $timeout{'play'}{'time'} = time;
- }
- sub refine_result {
- my ($self, $args) = @_;
- if ($args->{fail} == 0) {
- message TF("You successfully refined a weapon (ID %s)!n", $args->{nameID});
- } elsif ($args->{fail} == 1) {
- message TF("You failed to refine a weapon (ID %s)!n", $args->{nameID});
- } elsif ($args->{fail} == 2) {
- message TF("You successfully made a potion (ID %s)!n", $args->{nameID});
- } elsif ($args->{fail} == 3) {
- message TF("You failed to make a potion (ID %s)!n", $args->{nameID});
- } else {
- message TF("You tried to refine a weapon (ID %s); result: unknown %sn", $args->{nameID}, $args->{fail});
- }
- }
- sub blacksmith_points {
- my ($self, $args) = @_;
- message TF("[POINT] Blacksmist Ranking Point is increasing by %s. Now, The total is %s points.n", $args->{points}, $args->{total}, "list");
- }
- sub alchemist_point {
- my ($self, $args) = @_;
- message TF("[POINT] Alchemist Ranking Point is increasing by %s. Now, The total is %s points.n", $args->{points}, $args->{total}, "list");
- }
- sub repair_list {
- my ($self, $args) = @_;
- my $msg = T("--------Repair List--------n");
- undef %repairList;
- for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += 13) {
- my $listID = unpack("C1", substr($args->{RAW_MSG}, $i+12, 1));
- $repairList{$listID}{index} = unpack("v1", substr($args->{RAW_MSG}, $i, 2));
- $repairList{$listID}{nameID} = unpack("v1", substr($args->{RAW_MSG}, $i+2, 2));
- # what are these two?
- $repairList{$listID}{status} = unpack("V1", substr($args->{RAW_MSG}, $i+4, 4));
- $repairList{$listID}{status2} = unpack("V1", substr($args->{RAW_MSG}, $i+8, 4));
- $repairList{$listID}{listID} = $listID;
-
- my $name = itemNameSimple($repairList{$listID}{nameID});
- $msg .= "$listID $namen";
- }
- $msg .= "---------------------------n";
- message $msg, "list";
- }
- sub repair_result {
- my ($self, $args) = @_;
- undef %repairList;
- my $itemName = itemNameSimple($args->{nameID});
- if ($args->{flag}) {
- message TF("Repair of %s failed.n", $itemName);
- } else {
- message TF("Successfully repaired %s.n", $itemName);
- }
- }
- sub resurrection {
- my ($self, $args) = @_;
- my $targetID = $args->{targetID};
- my $player = $playersList->getByID($targetID);
- my $type = $args->{type};
- if ($targetID eq $accountID) {
- message T("You have been resurrectedn"), "info";
- undef $char->{'dead'};
- undef $char->{'dead_time'};
- $char->{'resurrected'} = 1;
- } else {
- if ($player) {
- undef $player->{'dead'};
- $player->{deltaHp} = 0;
- }
- message TF("%s has been resurrectedn", getActorName($targetID)), "info";
- }
- }
- sub sage_autospell {
- # Sage Autospell - list of spells availible sent from server
- if ($config{autoSpell}) {
- my $skill = new Skill(name => $config{autoSpell});
- $messageSender->sendAutoSpell($skill->getIDN());
- }
- }
- sub secure_login_key {
- my ($self, $args) = @_;
- $secureLoginKey = $args->{secure_key};
- }
- sub self_chat {
- my ($self, $args) = @_;
- my ($message, $chatMsgUser, $chatMsg); # Type: String
- $message = bytesToString($args->{message});
- ($chatMsgUser, $chatMsg) = $message =~ /([sS]*?) : ([sS]*)/;
- # Note: $chatMsgUser/Msg may be undefined. This is the case on
- # eAthena servers: it uses this packet for non-chat server messages.
- if (defined $chatMsgUser) {
- stripLanguageCode($chatMsg);
- } else {
- $message = $message;
- }
- chatLog("c", "$messagen") if ($config{'logChat'});
- message "$messagen", "selfchat";
- Plugins::callHook('packet_selfChat', {
- user => $chatMsgUser,
- msg => $chatMsg
- });
- }
- sub sync_request {
- my ($self, $args) = @_;
- # 0187 - long ID
- # I'm not sure what this is. In inRO this seems to have something
- # to do with logging into the game server, while on
- # oRO it has got something to do with the sync packet.
- if ($masterServer->{serverType} == 1) {
- my $ID = $args->{ID};
- if ($ID == $accountID) {
- $timeout{ai_sync}{time} = time;
- $messageSender->sendSync() unless ($net->clientAlive);
- debug "Sync packet requestedn", "connection";
- } else {
- warning T("Sync packet requested for wrong IDn");
- }
- }
- }
- sub taekwon_rank {
- my ($self, $args) = @_;
- message T("TaeKwon Mission Rank : ".$args->{rank}."n"), "info";
- }
- sub taekwon_mission_receive {
- my ($self, $args) = @_;
- message T("TaeKwon Mission : ".$args->{monName}."(".$args->{value}."%)"."n"), "info";
- }
- sub gospel_buff_aligned {
- my ($self, $args) = @_;
- my $status = unpack("V1", $args->{ID});
-
- if ($status == 21) {
- message T("All abnormal status effects have been removed.n"), "info";
- } elsif ($status == 22) {
- message T("You will be immune to abnormal status effects for the next minute.n"), "info";
- } elsif ($status == 23) {
- message T("Your Max HP will stay increased for the next minute.n"), "info";
- } elsif ($status == 24) {
- message T("Your Max SP will stay increased for the next minute.n"), "info";
- } elsif ($status == 25) {
- message T("All of your Stats will stay increased for the next minute.n"), "info";
- } elsif ($status == 28) {
- message T("Your weapon will remain blessed with Holy power for the next minute.n"), "info";
- } elsif ($status == 29) {
- message T("Your armor will remain blessed with Holy power for the next minute.n"), "info";
- } elsif ($status == 30) {
- message T("Your Defense will stay increased for the next 10 seconds.n"), "info";
- } elsif ($status == 31) {
- message T("Your Attack strength will stay increased for the next minute.n"), "info";
- } elsif ($status == 32) {
- message T("Your Accuracy and Flee Rate will stay increased for the next minute.n"), "info";
- } else {
- #message T("Unknown buff from Gospel: " . $status . "n"), "info";
- }
- }
- sub no_teleport {
- my ($self, $args) = @_;
- my $fail = $args->{fail};
- if ($fail == 0) {
- error T("Unavailable Area To Teleportn");
- AI::clear(qw/teleport/);
- } elsif ($fail == 1) {
- error T("Unavailable Area To Memon");
- } else {
- error TF("Unavailable Area To Teleport (fail code %s)n", $fail);
- }
- }
- sub pvp_mode1 {
- my ($self, $args) = @_;
- my $type = $args->{type};
- if ($type == 0) {
- $pvp = 0;
- } elsif ($type == 1) {
- message T("PvP Display Moden"), "map_event";
- $pvp = 1;
- } elsif ($type == 3) {
- message T("GvG Display Moden"), "map_event";
- $pvp = 2;
- }
-
- if ($pvp) {
- Plugins::callHook('pvp_mode', {
- pvp => $pvp # If set to 1 its PvP and if set to 2 its GvG
- });
- }
- }
- sub pvp_mode2 {
- my ($self, $args) = @_;
- my $type = $args->{type};
- if ($type == 0) {
- $pvp = 0;
- } elsif ($type == 6) {
- message T("PvP Display Moden"), "map_event";
- $pvp = 1;
- } elsif ($type == 8) {
- message T("GvG Display Moden"), "map_event";
- $pvp = 2;
- } elsif ($type == 19) {
- message T("Battleground Display Moden"), "map_event";
- $pvp = 3;
- }
-
- if ($pvp) {
- Plugins::callHook('pvp_mode', {
- pvp => $pvp # If set to 1 its PvP and if set to 2 its GvG
- });
- }
- }
- sub pvp_rank {
- my ($self, $args) = @_;
- # 9A 01 - 14 bytes long
- my $ID = $args->{ID};
- my $rank = $args->{rank};
- my $num = $args->{num};;
- if ($rank != $ai_v{temp}{pvp_rank} ||
- $num != $ai_v{temp}{pvp_num}) {
- $ai_v{temp}{pvp_rank} = $rank;
- $ai_v{temp}{pvp_num} = $num;
- if ($ai_v{temp}{pvp}) {
- message TF("Your PvP rank is: %s/%sn", $rank, $num), "map_event";
- }
- }
- }
- sub sense_result {
- my ($self, $args) = @_;
- # nameID level size hp def race mdef element ice earth fire wind poison holy dark spirit undead
- my @race_lut = qw(Formless Undead Beast Plant Insect Fish Demon Demi-Human Angel Dragon Boss Non-Boss);
- my @size_lut = qw(Small Medium Large);
- message TF("=====================Sense========================n" .
- "Monster: %-16s Level: %-12sn" .
- "Size: %-16s Race: %-12sn" .
- "Def: %-16s MDef: %-12sn" .
- "Element: %-16s HP: %-12sn" .
- "=================Damage Modifiers=================n" .
- "Ice: %-3s Earth: %-3s Fire: %-3s Wind: %-3sn" .
- "Poison: %-3s Holy: %-3s Dark: %-3s Spirit: %-3sn" .
- "Undead: %-3sn" .
- "==================================================n",
- $monsters_lut{$args->{nameID}}, $args->{level}, $size_lut[$args->{size}], $race_lut[$args->{race}],
- $args->{def}, $args->{mdef}, $elements_lut{$args->{element}}, $args->{hp},
- $args->{ice}, $args->{earth}, $args->{fire}, $args->{wind}, $args->{poison}, $args->{holy}, $args->{dark},
- $args->{spirit}, $args->{undead}), "list";
- }
- sub shop_sold {
- my ($self, $args) = @_;
- # sold something
- my $number = $args->{number};
- my $amount = $args->{amount};
- $articles[$number]{sold} += $amount;
- my $earned = $amount * $articles[$number]{price};
- $shopEarned += $earned;
- $articles[$number]{quantity} -= $amount;
- my $msg = TF("sold: %s - %s %szn", $amount, $articles[$number]{name}, $earned);
- shopLog($msg);
- message($msg, "sold");
- if ($articles[$number]{quantity} < 1) {
- message TF("sold out: %sn", $articles[$number]{name}), "sold";
- #$articles[$number] = "";
- if (!--$articles){
- message T("Items have been sold out.n"), "sold";
- closeShop();
- }
- }
- }
- sub shop_skill {
- my ($self, $args) = @_;
- # Used the shop skill.
- my $number = $args->{number};
- message TF("You can sell %s items!n", $number);
- }
- sub show_equipment {
- my ($self, $args) = @_;
- if ($args->{type}) {
- message T("Allowed other player view Equipmentn");
- } else {
- message T("Not allowed other player view Equipmentn");
- }
- }
- sub skill_cast {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my $sourceID = $args->{sourceID};
- my $targetID = $args->{targetID};
- my $x = $args->{x};
- my $y = $args->{y};
- my $skillID = $args->{skillID};
- my $type = $args->{type};
- my $wait = $args->{wait};
- my ($dist, %coords);
- # Resolve source and target
- my $source = Actor::get($sourceID);
- my $target = Actor::get($targetID);
- my $verb = $source->verb('are casting', 'is casting');
- Misc::checkValidity("skill_cast part 1");
- my $skill = new Skill(idn => $skillID);
- $source->{casting} = {
- skill => $skill,
- target => $target,
- x => $x,
- y => $y,
- startTime => time,
- castTime => $wait
- };
- # Since we may have a circular reference, weaken this reference
- # to prevent memory leaks.
- Scalar::Util::weaken($source->{casting}{target});
- my $targetString;
- if ($x != 0 || $y != 0) {
- # If $dist is positive we are in range of the attack?
- $coords{x} = $x;
- $coords{y} = $y;
- $dist = judgeSkillArea($skillID) - distance($char->{pos_to}, %coords);
- $targetString = "location ($x, $y)";
- undef $targetID;
- } else {
- $targetString = $target->nameString($source);
- }
- # Perform trigger actions
- if ($sourceID eq $accountID) {
- $char->{time_cast} = time;
- $char->{time_cast_wait} = $wait / 1000;
- delete $char->{cast_cancelled};
- }
- countCastOn($sourceID, $targetID, $skillID, $x, $y);
- Misc::checkValidity("skill_cast part 2");
- my $domain = ($sourceID eq $accountID) ? "selfSkill" : "skill";
- my $disp = skillCast_string($source, $target, $x, $y, $skill->getName(), $wait);
- message $disp, $domain, 1;
- Plugins::callHook('is_casting', {
- sourceID => $sourceID,
- targetID => $targetID,
- source => $source,
- target => $target,
- skillID => $skillID,
- skill => $skill,
- time => $source->{casting}{time},
- castTime => $wait,
- x => $x,
- y => $y
- });
- Misc::checkValidity("skill_cast part 3");
- # Skill Cancel
- my $monster = $monstersList->getByID($sourceID);
- my $control;
- $control = mon_control($monster->name,$monster->{nameID}) if ($monster);
- if ($AI == 2 && $control->{skillcancel_auto}) {
- if ($targetID eq $accountID || $dist > 0 || (AI::action eq "attack" && AI::args->{ID} ne $sourceID)) {
- message TF("Monster Skill - switch Target to : %s (%d)n", $monster->name, $monster->{binID});
- stopAttack();
- AI::dequeue;
- attack($sourceID);
- }
- # Skill area casting -> running to monster's back
- my $ID;
- if ($dist > 0 && AI::action eq "attack" && ($ID = AI::args->{ID}) && (my $monster2 = $monstersList->getByID($ID))) {
- # Calculate X axis
- if ($char->{pos_to}{x} - $monster2->{pos_to}{x} < 0) {
- $coords{x} = $monster2->{pos_to}{x} + 3;
- } else {
- $coords{x} = $monster2->{pos_to}{x} - 3;
- }
- # Calculate Y axis
- if ($char->{pos_to}{y} - $monster2->{pos_to}{y} < 0) {
- $coords{y} = $monster2->{pos_to}{y} + 3;
- } else {
- $coords{y} = $monster2->{pos_to}{y} - 3;
- }
- my (%vec, %pos);
- getVector(%vec, %coords, $char->{pos_to});
- moveAlongVector(%pos, $char->{pos_to}, %vec, distance($char->{pos_to}, %coords));
- ai_route($field{name}, $pos{x}, $pos{y},
- maxRouteDistance => $config{attackMaxRouteDistance},
- maxRouteTime => $config{attackMaxRouteTime},
- noMapRoute => 1);
- message TF("Avoid casting Skill - switch position to : %s,%sn", $pos{x}, $pos{y}), 1;
- }
- Misc::checkValidity("skill_cast part 4");
- }
- }
- sub skill_update {
- my ($self, $args) = @_;
- my ($ID, $lv, $sp, $range, $up) = ($args->{skillID}, $args->{lv}, $args->{sp}, $args->{range}, $args->{up});
- my $skill = new Skill(idn => $ID);
- my $handle = $skill->getHandle();
- my $name = $skill->getName();
- $char->{skills}{$handle}{lv} = $lv;
- $char->{skills}{$handle}{sp} = $sp;
- $char->{skills}{$handle}{range} = $range;
- $char->{skills}{$handle}{up} = $up;
- Skill::DynamicInfo::add($ID, $handle, $lv, $sp, $range, $skill->getTargetType(), Skill::OWNER_CHAR);
- # Set $skillchanged to 2 so it knows to unset it when skill points are updated
- if ($skillChanged eq $handle) {
- $skillChanged = 2;
- }
- debug "Skill $name: $lvn", "parseMsg";
- }
- sub skill_use {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- if (my $spell = $spells{$args->{sourceID}}) {
- # Resolve source of area attack skill
- $args->{sourceID} = $spell->{sourceID};
- }
- my $source = Actor::get($args->{sourceID});
- my $target = Actor::get($args->{targetID});
- $args->{source} = $source;
- $args->{target} = $target;
- delete $source->{casting};
- # Perform trigger actions
- if ($args->{switch} eq "0114") {
- $args->{damage} = intToSignedShort($args->{damage});
- } else {
- $args->{damage} = intToSignedInt($args->{damage});
- }
- updateDamageTables($args->{sourceID}, $args->{targetID}, $args->{damage}) if ($args->{damage} != -30000);
- setSkillUseTimer($args->{skillID}, $args->{targetID}) if (
- $args->{sourceID} eq $accountID
- or $char->{slaves} && $char->{slaves}{$args->{sourceID}}
- );
- setPartySkillTimer($args->{skillID}, $args->{targetID}) if (
- $args->{sourceID} eq $accountID
- or $char->{slaves} && $char->{slaves}{$args->{sourceID}}
- or $args->{sourceID} eq $args->{targetID} # wtf?
- );
- countCastOn($args->{sourceID}, $args->{targetID}, $args->{skillID});
- # Resolve source and target names
- my $skill = new Skill(idn => $args->{skillID});
- $args->{skill} = $skill;
- my $disp = skillUse_string($source, $target, $skill->getName(), $args->{damage},
- $args->{level}, ($args->{src_speed}/10));
- if ($args->{damage} != -30000 &&
- $args->{sourceID} eq $accountID &&
- $args->{targetID} ne $accountID) {
- calcStat($args->{damage});
- }
- my $domain = ($args->{sourceID} eq $accountID) ? "selfSkill" : "skill";
- if ($args->{damage} == 0) {
- $domain = "attackMonMiss" if (($args->{sourceID} eq $accountID && $args->{targetID} ne $accountID) || ($char->{homunculus} && $args->{sourceID} eq $char->{homunculus}{ID} && $args->{targetID} ne $char->{homunculus}{ID}));
- $domain = "attackedMiss" if (($args->{sourceID} ne $accountID && $args->{targetID} eq $accountID) || ($char->{homunculus} && $args->{sourceID} ne $char->{homunculus}{ID} && $args->{targetID} eq $char->{homunculus}{ID}));
- } elsif ($args->{damage} != -30000) {
- $domain = "attackMon" if (($args->{sourceID} eq $accountID && $args->{targetID} ne $accountID) || ($char->{homunculus} && $args->{sourceID} eq $char->{homunculus}{ID} && $args->{targetID} ne $char->{homunculus}{ID}));
- $domain = "attacked" if (($args->{sourceID} ne $accountID && $args->{targetID} eq $accountID) || ($char->{homunculus} && $args->{sourceID} ne $char->{homunculus}{ID} && $args->{targetID} eq $char->{homunculus}{ID}));
- }
- if ((($args->{sourceID} eq $accountID) && ($args->{targetID} ne $accountID)) ||
- (($args->{sourceID} ne $accountID) && ($args->{targetID} eq $accountID))) {
- my $status = sprintf("[%3d/%3d] ", $char->hp_percent, $char->sp_percent);
- $disp = $status.$disp;
- } elsif ($char->{slaves} && $char->{slaves}{$args->{sourceID}} && !$char->{slaves}{$args->{targetID}}) {
- my $status = sprintf("[%3d/%3d] ", $char->{slaves}{$args->{sourceID}}{hpPercent}, $char->{slaves}{$args->{sourceID}}{spPercent});
- $disp = $status.$disp;
- } elsif ($char->{slaves} && !$char->{slaves}{$args->{sourceID}} && $char->{slaves}{$args->{targetID}}) {
- my $status = sprintf("[%3d/%3d] ", $char->{slaves}{$args->{targetID}}{hpPercent}, $char->{slaves}{$args->{targetID}}{spPercent});
- $disp = $status.$disp;
- }
- $target->{sitting} = 0 unless $args->{type} == 4 || $args->{type} == 9 || $args->{damage} == 0;
- Plugins::callHook('packet_skilluse', {
- 'skillID' => $args->{skillID},
- 'sourceID' => $args->{sourceID},
- 'targetID' => $args->{targetID},
- 'damage' => $args->{damage},
- 'amount' => 0,
- 'x' => 0,
- 'y' => 0,
- 'disp' => $disp
- });
- message $disp, $domain, 1;
- if ($args->{targetID} eq $accountID && $args->{damage} > 0) {
- $damageTaken{$source->{name}}{$skill->getName()} += $args->{damage};
- }
- }
- 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 %failtype = (
- 0 => 'Basic',
- 1 => 'Insufficient SP',
- 2 => 'Insufficient HP',
- 3 => 'No Memo',
- 4 => 'Mid-Delay',
- 5 => 'No Zeny',
- 6 => 'Wrong Weapon Type',
- 7 => 'Red Gem Needed',
- 8 => 'Blue Gem Needed',
- 9 => '90% Overweight',
- 10 => 'Requirement'
- );
- warning TF("Skill %s failed (%s)n", Skill->new(idn => $skillID)->getName(), $failtype{$type}), "skill";
- Plugins::callHook('packet_skillfail', {
- skillID => $skillID,
- failType => $type,
- failMessage => $failtype{$type}
- });
- }
- sub skill_use_location {
- my ($self, $args) = @_;
- # Skill used on coordinates
- my $skillID = $args->{skillID};
- my $sourceID = $args->{sourceID};
- my $lv = $args->{lv};
- my $x = $args->{x};
- my $y = $args->{y};
- # Perform trigger actions
- setSkillUseTimer($skillID) if $sourceID eq $accountID;
- # Resolve source name
- my $source = Actor::get($sourceID);
- my $skillName = Skill->new(idn => $skillID)->getName();
- my $disp = skillUseLocation_string($source, $skillName, $args);
- # Print skill use message
- my $domain = ($sourceID eq $accountID) ? "selfSkill" : "skill";
- message $disp, $domain;
- Plugins::callHook('packet_skilluse', {
- 'skillID' => $skillID,
- 'sourceID' => $sourceID,
- 'targetID' => '',
- 'damage' => 0,
- 'amount' => $lv,
- 'x' => $x,
- 'y' => $y
- });
- }
- sub skill_used_no_damage {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- # Skill used on target, with no damage done
- if (my $spell = $spells{$args->{sourceID}}) {
- # Resolve source of area attack skill
- $args->{sourceID} = $spell->{sourceID};
- }
- # Perform trigger actions
- setSkillUseTimer($args->{skillID}, $args->{targetID}) if ($args->{sourceID} eq $accountID
- && $skillsArea{$args->{skillHandle}} != 2); # ignore these skills because they screw up monk comboing
- setPartySkillTimer($args->{skillID}, $args->{targetID}) if
- $args->{sourceID} eq $accountID or $args->{sourceID} eq $args->{targetID};
- countCastOn($args->{sourceID}, $args->{targetID}, $args->{skillID});
- if ($args->{sourceID} eq $accountID) {
- my $pos = calcPosition($char);
- $char->{pos_to} = $pos;
- $char->{time_move} = 0;
- $char->{time_move_calc} = 0;
- }
- # Resolve source and target names
- my $source = $args->{source} = Actor::get($args->{sourceID});
- my $target = $args->{target} = Actor::get($args->{targetID});
- my $verb = $source->verb('use', 'uses');
- delete $source->{casting};
- # Print skill use message
- my $extra = "";
- if ($args->{skillID} == 28) {
- $extra = ": $args->{amount} hp gained";
- updateDamageTables($args->{sourceID}, $args->{targetID}, -$args->{amount});
- } elsif ($args->{amount} != 65535) {
- $extra = ": Lv $args->{amount}";
- }
- my $domain = ($args->{sourceID} eq $accountID) ? "selfSkill" : "skill";
- my $skill = $args->{skill} = new Skill(idn => $args->{skillID});
- my $disp = skillUseNoDamage_string($source, $target, $skill->getIDN(), $skill->getName(), $args->{amount});
- message $disp, $domain;
- # Set teleport time
- if ($args->{sourceID} eq $accountID && $skill->getHandle() eq 'AL_TELEPORT') {
- $timeout{ai_teleport_delay}{time} = time;
- }
- if ($AI == 2 && $config{'autoResponseOnHeal'}) {
- # Handle auto-response on heal
- my $player = $playersList->getByID($args->{sourceID});
- if ($player && ($args->{skillID} == 28 || $args->{skillID} == 29 || $args->{skillID} == 34)) {
- if ($args->{targetID} eq $accountID) {
- chatLog("k", "***$source ".$skill->getName()." on $target$extra***n");
- sendMessage("pm", getResponse("skillgoodM"), $player->name);
- } elsif ($monstersList->getByID($args->{targetID})) {
- chatLog("k", "***$source ".$skill->getName()." on $target$extra***n");
- sendMessage("pm", getResponse("skillbadM"), $player->name);
- }
- }
- }
- Plugins::callHook('packet_skilluse', {
- skillID => $args->{skillID},
- sourceID => $args->{sourceID},
- targetID => $args->{targetID},
- damage => 0,
- amount => $args->{amount},
- x => 0,
- y => 0
- });
- }
- sub skills_list {
- my ($self, $args) = @_;
-
- return unless changeToInGameState;
-
- my ($slave, $owner, $hook, $msg, $newmsg);
-
- $msg = $args->{RAW_MSG};
- $self->decrypt($newmsg, substr $msg, 4);
- $msg = substr ($msg, 0, 4) . $newmsg;
-
- if ($args->{switch} eq '010F') {
- $hook = 'packet_charSkills'; $owner = Skill::OWNER_CHAR;
-
- undef @skillsID;
- delete $char->{skills};
- Skill::DynamicInfo::clear();
-
- } elsif ($args->{switch} eq '0235') {
- $slave = $char->{homunculus}; $hook = 'packet_homunSkills'; $owner = Skill::OWNER_HOMUN;
-
- } elsif ($args->{switch} eq '029D') {
- $slave = $char->{mercenary}; $hook = 'packet_mercSkills'; $owner = Skill::OWNER_MERC;
- }
-
- my $skillsIDref = $slave ? @{$slave->{slave_skillsID}} : @skillsID;
-
- undef @{$slave->{slave_skillsID}};
- for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += 37) {
- my ($skillID, $targetType, $level, $sp, $range, $handle, $up)
- = unpack 'v1 V1 v3 Z24 C1', substr $msg, $i, 37;
- $handle = Skill->new (idn => $skillID)->getHandle unless $handle;
-
- $char->{skills}{$handle}{ID} = $skillID;
- $char->{skills}{$handle}{sp} = $sp;
- $char->{skills}{$handle}{range} = $range;
- $char->{skills}{$handle}{up} = $up;
- $char->{skills}{$handle}{targetType} = $targetType;
- $char->{skills}{$handle}{lv} = $level unless $char->{skills}{$handle}{lv};
-
- binAdd ($skillsIDref, $handle) unless binFind ($skillsIDref, $handle);
- Skill::DynamicInfo::add($skillID, $handle, $level, $sp, $range, $targetType, $owner);
-
- Plugins::callHook($hook, {
- ID => $skillID,
- handle => $handle,
- level => $level,
- });
- }
- }
- sub linker_skill {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my $handle = ($args->{name}) ? $args->{name} : Skill->new(idn => $args->{skillID})->getHandle();
- $char->{skills}{$handle}{ID} = $args->{skillID};
- $char->{skills}{$handle}{sp} = $args->{sp};
- $char->{skills}{$handle}{range} = $args->{range};
- $char->{skills}{$handle}{up} = 0;
- $char->{skills}{$handle}{targetType} = $args->{target};
- $char->{skills}{$handle}{lv} = $args->{lv};
- $char->{skills}{$handle}{new} = 1;
- #Fix bug , receive status "Night" 2 time
- binAdd(@skillsID, $handle) if (binFind(@skillsID, $handle) eq "");
- Skill::DynamicInfo::add($args->{skillID}, $handle, $args->{lv}, $args->{sp}, $args->{target}, $args->{target}, Skill::OWNER_CHAR);
- Plugins::callHook('packet_charSkills', {
- ID => $args->{skillID},
- handle => $handle,
- level => $args->{lv},
- });
- }
- sub stats_added {
- my ($self, $args) = @_;
- if ($args->{val} == 207) {
- error T("Not enough stat points to addn");
- } else {
- if ($args->{type} == 13) {
- $char->{str} = $args->{val};
- debug "Strength: $args->{val}n", "parseMsg";
- # Reset $statChanged back to 0 to tell kore that a stat can be raised again
- $statChanged = 0 if ($statChanged eq "str");
- } elsif ($args->{type} == 14) {
- $char->{agi} = $args->{val};
- debug "Agility: $args->{val}n", "parseMsg";
- $statChanged = 0 if ($statChanged eq "agi");
- } elsif ($args->{type} == 15) {
- $char->{vit} = $args->{val};
- debug "Vitality: $args->{val}n", "parseMsg";
- $statChanged = 0 if ($statChanged eq "vit");
- } elsif ($args->{type} == 16) {
- $char->{int} = $args->{val};
- debug "Intelligence: $args->{val}n", "parseMsg";
- $statChanged = 0 if ($statChanged eq "int");
- } elsif ($args->{type} == 17) {
- $char->{dex} = $args->{val};
- debug "Dexterity: $args->{val}n", "parseMsg";
- $statChanged = 0 if ($statChanged eq "dex");
- } elsif ($args->{type} == 18) {
- $char->{luk} = $args->{val};
- debug "Luck: $args->{val}n", "parseMsg";
- $statChanged = 0 if ($statChanged eq "luk");
- } else {
- debug "Something: $args->{val}n", "parseMsg";
- }
- }
- Plugins::callHook('packet_charStats', {
- type => $args->{type},
- val => $args->{val},
- });
- }
- sub stats_info {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- $char->{points_free} = $args->{points_free};
- $char->{str} = $args->{str};
- $char->{points_str} = $args->{points_str};
- $char->{agi} = $args->{agi};
- $char->{points_agi} = $args->{points_agi};
- $char->{vit} = $args->{vit};
- $char->{points_vit} = $args->{points_vit};
- $char->{int} = $args->{int};
- $char->{points_int} = $args->{points_int};
- $char->{dex} = $args->{dex};
- $char->{points_dex} = $args->{points_dex};
- $char->{luk} = $args->{luk};
- $char->{points_luk} = $args->{points_luk};
- $char->{attack} = $args->{attack};
- $char->{attack_bonus} = $args->{attack_bonus};
- $char->{attack_magic_min} = $args->{attack_magic_min};
- $char->{attack_magic_max} = $args->{attack_magic_max};
- $char->{def} = $args->{def};
- $char->{def_bonus} = $args->{def_bonus};
- $char->{def_magic} = $args->{def_magic};
- $char->{def_magic_bonus} = $args->{def_magic_bonus};
- $char->{hit} = $args->{hit};
- $char->{flee} = $args->{flee};
- $char->{flee_bonus} = $args->{flee_bonus};
- $char->{critical} = $args->{critical};
- debug "Strength: $char->{str} #$char->{points_str}n"
- ."Agility: $char->{agi} #$char->{points_agi}n"
- ."Vitality: $char->{vit} #$char->{points_vit}n"
- ."Intelligence: $char->{int} #$char->{points_int}n"
- ."Dexterity: $char->{dex} #$char->{points_dex}n"
- ."Luck: $char->{luk} #$char->{points_luk}n"
- ."Attack: $char->{attack}n"
- ."Attack Bonus: $char->{attack_bonus}n"
- ."Magic Attack Min: $char->{attack_magic_min}n"
- ."Magic Attack Max: $char->{attack_magic_max}n"
- ."Defense: $char->{def}n"
- ."Defense Bonus: $char->{def_bonus}n"
- ."Magic Defense: $char->{def_magic}n"
- ."Magic Defense Bonus: $char->{def_magic_bonus}n"
- ."Hit: $char->{hit}n"
- ."Flee: $char->{flee}n"
- ."Flee Bonus: $char->{flee_bonus}n"
- ."Critical: $char->{critical}n"
- ."Status Points: $char->{points_free}n", "parseMsg";
- }
- sub stat_info {
- my ($self,$args) = @_;
- return unless changeToInGameState();
- if ($args->{type} == 0) {
- $char->{walk_speed} = $args->{val} / 1000;
- debug "Walk speed: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 3) {
- debug "Something2: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 4) {
- if ($args->{val} == 0) {
- delete $char->{muted};
- delete $char->{mute_period};
- message T("Mute period expired.n");
- } else {
- my $val = (0xFFFFFFFF - $args->{val}) + 1;
- $char->{mute_period} = $val * 60;
- $char->{muted} = time;
- if ($config{dcOnMute}) {
- message TF("You've been muted for %s minutes, auto disconnect!n", $val);
- chatLog("k", TF("*** You have been muted for %s minutes, auto disconnect! ***n", $val));
- quit();
- } else {
- message TF("You've been muted for %s minutesn", $val);
- }
- }
- } elsif ($args->{type} == 5) {
- $char->{hp} = $args->{val};
- debug "Hp: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 6) {
- $char->{hp_max} = $args->{val};
- debug "Max Hp: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 7) {
- $char->{sp} = $args->{val};
- debug "Sp: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 8) {
- $char->{sp_max} = $args->{val};
- debug "Max Sp: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 9) {
- $char->{points_free} = $args->{val};
- debug "Status Points: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 11) {
- $char->{lv} = $args->{val};
- message TF("You are now level %sn", $args->{val}), "success";
- if ($config{dcOnLevel} && $char->{lv} >= $config{dcOnLevel}) {
- message TF("Disconnecting on level %s!n", $config{dcOnLevel});
- chatLog("k", TF("Disconnecting on level %s!n", $config{dcOnLevel}));
- quit();
- }
- } elsif ($args->{type} == 12) {
- $char->{points_skill} = $args->{val};
- debug "Skill Points: $args->{val}n", "parseMsg", 2;
- # Reset $skillChanged back to 0 to tell kore that a skill can be auto-raised again
- if ($skillChanged == 2) {
- $skillChanged = 0;
- }
- } elsif ($args->{type} == 24) {
- $char->{weight} = $args->{val} / 10;
- debug "Weight: $char->{weight}n", "parseMsg", 2;
- } elsif ($args->{type} == 25) {
- $char->{weight_max} = int($args->{val} / 10);
- debug "Max Weight: $char->{weight_max}n", "parseMsg", 2;
- } elsif ($args->{type} == 41) {
- $char->{attack} = $args->{val};
- debug "Attack: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 42) {
- $char->{attack_bonus} = $args->{val};
- debug "Attack Bonus: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 43) {
- $char->{attack_magic_max} = $args->{val};
- debug "Magic Attack Max: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 44) {
- $char->{attack_magic_min} = $args->{val};
- debug "Magic Attack Min: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 45) {
- $char->{def} = $args->{val};
- debug "Defense: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 46) {
- $char->{def_bonus} = $args->{val};
- debug "Defense Bonus: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 47) {
- $char->{def_magic} = $args->{val};
- debug "Magic Defense: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 48) {
- $char->{def_magic_bonus} = $args->{val};
- debug "Magic Defense Bonus: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 49) {
- $char->{hit} = $args->{val};
- debug "Hit: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 50) {
- $char->{flee} = $args->{val};
- debug "Flee: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 51) {
- $char->{flee_bonus} = $args->{val};
- debug "Flee Bonus: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 52) {
- $char->{critical} = $args->{val};
- debug "Critical: $args->{val}n", "parseMsg", 2;
- } elsif ($args->{type} == 53) {
- $char->{attack_delay} = $args->{val};
- $char->{attack_speed} = 200 - $args->{val}/10;
- debug "Attack Speed: $char->{attack_speed}n", "parseMsg", 2;
- } elsif ($args->{type} == 55) {
- $char->{lv_job} = $args->{val};
- message TF("You are now job level %sn", $args->{val}), "success";
- if ($config{dcOnJobLevel} && $char->{lv_job} >= $config{dcOnJobLevel}) {
- message TF("Disconnecting on job level %s!n", $config{dcOnJobLevel});
- chatLog("k", TF("Disconnecting on job level %s!n", $config{dcOnJobLevel}));
- quit();
- }
- } elsif ($args->{type} == 124) {
- debug "Something3: $args->{val}n", "parseMsg", 2;
- } else {
- debug "Something: $args->{val}n", "parseMsg", 2;
- }
-
- if (!$char->{walk_speed}) {
- $char->{walk_speed} = 0.15; # This is the default speed, since xkore requires this and eA (And aegis?) do not send this if its default speed
- }
- }
- sub stat_info2 {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my ($type, $val, $val2) = @{$args}{qw(type val val2)};
- if ($type == 13) {
- $char->{str} = $val;
- $char->{str_bonus} = $val2;
- debug "Strength: $val + $val2n", "parseMsg";
- } elsif ($type == 14) {
- $char->{agi} = $val;
- $char->{agi_bonus} = $val2;
- debug "Agility: $val + $val2n", "parseMsg";
- } elsif ($type == 15) {
- $char->{vit} = $val;
- $char->{vit_bonus} = $val2;
- debug "Vitality: $val + $val2n", "parseMsg";
- } elsif ($type == 16) {
- $char->{int} = $val;
- $char->{int_bonus} = $val2;
- debug "Intelligence: $val + $val2n", "parseMsg";
- } elsif ($type == 17) {
- $char->{dex} = $val;
- $char->{dex_bonus} = $val2;
- debug "Dexterity: $val + $val2n", "parseMsg";
- } elsif ($type == 18) {
- $char->{luk} = $val;
- $char->{luk_bonus} = $val2;
- debug "Luck: $val + $val2n", "parseMsg";
- }
- }
- sub stats_points_needed {
- my ($self, $args) = @_;
- if ($args->{type} == 32) {
- $char->{points_str} = $args->{val};
- debug "Points needed for Strength: $args->{val}n", "parseMsg";
- } elsif ($args->{type} == 33) {
- $char->{points_agi} = $args->{val};
- debug "Points needed for Agility: $args->{val}n", "parseMsg";
- } elsif ($args->{type} == 34) {
- $char->{points_vit} = $args->{val};
- debug "Points needed for Vitality: $args->{val}n", "parseMsg";
- } elsif ($args->{type} == 35) {
- $char->{points_int} = $args->{val};
- debug "Points needed for Intelligence: $args->{val}n", "parseMsg";
- } elsif ($args->{type} == 36) {
- $char->{points_dex} = $args->{val};
- debug "Points needed for Dexterity: $args->{val}n", "parseMsg";
- } elsif ($args->{type} == 37) {
- $char->{points_luk} = $args->{val};
- debug "Points needed for Luck: $args->{val}n", "parseMsg";
- }
- }
- sub storage_closed {
- message T("Storage closed.n"), "storage";
- delete $ai_v{temp}{storage_opened};
- delete $storage{opened};
- Plugins::callHook('packet_storage_close');
- # Storage log
- writeStorageLog(0);
- }
- sub storage_item_added {
- my ($self, $args) = @_;
- my $index = $args->{index};
- my $amount = $args->{amount};
- my $item = $storage{$index} ||= {};
- if ($item->{amount}) {
- $item->{amount} += $amount;
- } else {
- binAdd(@storageID, $index);
- $item->{nameID} = $args->{ID};
- $item->{index} = $index;
- $item->{amount} = $amount;
- $item->{type} = $args->{type};
- $item->{identified} = $args->{identified};
- $item->{broken} = $args->{broken};
- $item->{upgrade} = $args->{upgrade};
- $item->{cards} = $args->{cards};
- $item->{name} = itemName($item);
- $item->{binID} = binFind(@storageID, $index);
- }
- message TF("Storage Item Added: %s (%d) x %sn", $item->{name}, $item->{binID}, $amount), "storage", 1;
- $itemChange{$item->{name}} += $amount;
- $args->{item} = $item;
- }
- sub storage_item_removed {
- my ($self, $args) = @_;
- my ($index, $amount) = @{$args}{qw(index amount)};
- my $item = $storage{$index};
- $item->{amount} -= $amount;
- message TF("Storage Item Removed: %s (%d) x %sn", $item->{name}, $item->{binID}, $amount), "storage";
- $itemChange{$item->{name}} -= $amount;
- $args->{item} = $item;
- if ($item->{amount} <= 0) {
- delete $storage{$index};
- binRemove(@storageID, $index);
- }
- }
- sub storage_items_nonstackable {
- my ($self, $args) = @_;
- # Retrieve list of non-stackable (weapons & armor) storage items.
- # This packet is sent immediately after 00A5/01F0.
- my ($newmsg, $psize);
- $self->decrypt($newmsg, substr($args->{RAW_MSG}, 4));
- my $msg = substr($args->{RAW_MSG}, 0, 4).$newmsg;
- if ($args->{switch} eq '0296') {
- $psize = 24;
- } elsif ($args->{switch} eq '02D1') {
- $psize = 26;
- } else {
- $psize = 20;
- }
- for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += $psize) {
- my $index = unpack("v1", substr($msg, $i, 2));
- my $ID = unpack("v1", substr($msg, $i + 2, 2));
- binAdd(@storageID, $index);
- my $item = $storage{$index} = {};
- $item->{index} = $index;
- $item->{nameID} = $ID;
- $item->{amount} = 1;
- $item->{type} = unpack("C1", substr($msg, $i + 4, 1));
- $item->{identified} = unpack("C1", substr($msg, $i + 5, 1));
- $item->{broken} = unpack("C1", substr($msg, $i + 10, 1));
- $item->{upgrade} = unpack("C1", substr($msg, $i + 11, 1));
- $item->{cards} = ($args->{switch} eq '0296') ? substr($msg, $i + 12, 12) : substr($msg, $i + 12, 8);
- $item->{name} = itemName($item);
- $item->{binID} = binFind(@storageID, $index);
- debug "Storage: $item->{name} ($item->{binID})n", "parseMsg";
- }
- }
- sub storage_items_stackable {
- my ($self, $args) = @_;
- # Retrieve list of stackable storage items
- my ($newmsg, $psize);
- $self->decrypt($newmsg, substr($args->{RAW_MSG}, 4));
- my $msg = substr($args->{RAW_MSG}, 0, 4).$newmsg;
- undef %storage;
- undef @storageID;
- if ($args->{switch} eq '00A5') {
- $psize = 10;
- } elsif ($args->{switch} eq '02EA') {
- $psize = 22;
- } else {
- $psize = 18;
- }
- for (my $i = 4; $i < $args->{RAW_MSG_SIZE}; $i += $psize) {
- my $index = unpack("v1", substr($msg, $i, 2));
- my $ID = unpack("v1", substr($msg, $i + 2, 2));
- binAdd(@storageID, $index);
- my $item = $storage{$index} = {};
- $item->{index} = $index;
- $item->{nameID} = $ID;
- $item->{type} = unpack("C1", substr($msg, $i + 4, 1));
- $item->{amount} = unpack("V1", substr($msg, $i + 6, 4)) & ~0x80000000;
- $item->{cards} = substr($msg, $i + 10, 8) if ($psize == 18);
- $item->{name} = itemName($item);
- $item->{binID} = binFind(@storageID, $index);
- $item->{identified} = 1;
- debug "Storage: $item->{name} ($item->{binID}) x $item->{amount}n", "parseMsg";
- }
- }
- sub storage_opened {
- my ($self, $args) = @_;
- $storage{items} = $args->{items};
- $storage{items_max} = $args->{items_max};
- $ai_v{temp}{storage_opened} = 1;
- if (!$storage{opened}) {
- $storage{opened} = 1;
- $storage{openedThisSession} = 1;
- message T("Storage opened.n"), "storage";
- Plugins::callHook('packet_storage_open');
- }
- }
- sub storage_password_request {
- my ($self, $args) = @_;
- if ($args->{flag} == 0) {
- if ($args->{switch} eq '023E') {
- message T("Please enter a new character password:n");
- } else {
- if ($config{storageAuto_password} eq '') {
- my $input = $interface->query(T("You've never set a storage password before.nYou must set a storage password before you can use the storage.nPlease enter a new storage password:"), isPassword => 1);
- if (!defined($input)) {
- return;
- }
- configModify('storageAuto_password', $input, 1);
- }
- }
- my @key = split /[, ]+/, $config{storageEncryptKey};
- if (!@key) {
- error (($args->{switch} eq '023E') ?
- T("Unable to send character password. You must set the 'storageEncryptKey' option in config.txt or servers.txt.n") :
- T("Unable to send storage password. You must set the 'storageEncryptKey' option in config.txt or servers.txt.n"));
- return;
- }
- my $crypton = new Utils::Crypton(pack("V*", @key), 32);
- my $num = ($args->{switch} eq '023E') ? $config{charSelect_password} : $config{storageAuto_password};
- $num = sprintf("%d%08d", length($num), $num);
- my $ciphertextBlock = $crypton->encrypt(pack("V*", $num, 0, 0, 0));
- message TF("Storage password set to: %sn", $config{storageAuto_password}), "success";
- $messageSender->sendStoragePassword($ciphertextBlock, 2);
- $messageSender->sendStoragePassword($ciphertextBlock, 3);
- } elsif ($args->{flag} == 1) {
- if ($args->{switch} eq '023E') {
- if ($config{charSelect_password} eq '') {
- my $input = $interface->query(T("Please enter your character password."), isPassword => 1);
- if (!defined($input)) {
- return;
- }
- configModify('charSelect_password', $input, 1);
- message TF("Character password set to: %sn", $input), "success";
- }
- } else {
- if ($config{storageAuto_password} eq '') {
- my $input = $interface->query(T("Please enter your storage password."), isPassword => 1);
- if (!defined($input)) {
- return;
- }
- configModify('storageAuto_password', $input, 1);
- message TF("Storage password set to: %sn", $input), "success";
- }
- }
- my @key = split /[, ]+/, $config{storageEncryptKey};
- if (!@key) {
- error (($args->{switch} eq '023E') ?
- T("Unable to send character password. You must set the 'storageEncryptKey' option in config.txt or servers.txt.n") :
- T("Unable to send storage password. You must set the 'storageEncryptKey' option in config.txt or servers.txt.n"));
- return;
- }
- my $crypton = new Utils::Crypton(pack("V*", @key), 32);
- my $num = ($args->{switch} eq '023E') ? $config{charSelect_password} : $config{storageAuto_password};
- $num = sprintf("%d%08d", length($num), $num);
- my $ciphertextBlock = $crypton->encrypt(pack("V*", $num, 0, 0, 0));
- $messageSender->sendStoragePassword($ciphertextBlock, 3);
- } elsif ($args->{flag} == 8) { # apparently this flag means that you have entered the wrong password
- # too many times, and now the server is blocking you from using storage
- error T("You have entered the wrong password 5 times. Please try again later.n");
- # temporarily disable storageAuto
- $config{storageAuto} = 0;
- my $index = AI::findAction('storageAuto');
- if (defined $index) {
- AI::args($index)->{done} = 1;
- while (AI::action ne 'storageAuto') {
- AI::dequeue;
- }
- }
- } else {
- debug(($args->{switch} eq '023E') ?
- "Character password: unknown flag $args->{flag}n" :
- "Storage password: unknown flag $args->{flag}n");
- }
- }
- sub storage_password_result {
- my ($self, $args) = @_;
- if ($args->{type} == 4) {
- message T("Successfully changed storage password.n"), "success";
- } elsif ($args->{type} == 5) {
- error T("Error: Incorrect storage password.n");
- } elsif ($args->{type} == 6) {
- message T("Successfully entered storage password.n"), "success";
- } elsif ($args->{type} == 7) {
- error T("Error: Incorrect storage password.n");
- # disable storageAuto or the Kafra storage will be blocked
- configModify("storageAuto", 0);
- my $index = AI::findAction('storageAuto');
- if (defined $index) {
- AI::args($index)->{done} = 1;
- while (AI::action ne 'storageAuto') {
- AI::dequeue;
- }
- }
- } else {
- #message "Storage password: unknown type $args->{type}n";
- }
- # $args->{val}
- # unknown, what is this for?
- }
- sub login_pin_code_request {
- my ($self, $args) = @_;
- my $done;
- if ($args->{flag} == 0) {
- # PIN code has never been set before, so set it.
- return if ($config{loginPinCode} eq '' && !queryAndSaveLoginPinCode());
- my @key = split /[, ]+/, $masterServer->{PINEncryptKey};
- if (!@key) {
- $interface->errorDialog(T("Unable to send PIN code. You must set the 'PINEncryptKey' option in servers.txt."));
- quit();
- return;
- }
- $messageSender->sendLoginPinCode($config{loginPinCode}, $config{loginPinCode}, $args->{key}, 2, @key);
- } elsif ($args->{flag} == 1) {
- # PIN code query request.
- return if ($config{loginPinCode} eq '' && !queryAndSaveLoginPinCode());
- my @key = split /[, ]+/, $masterServer->{PINEncryptKey};
- if (!@key) {
- $interface->errorDialog(T("Unable to send PIN code. You must set the 'PINEncryptKey' option in servers.txt."));
- quit();
- return;
- }
- $messageSender->sendLoginPinCode($config{loginPinCode}, 0, $args->{key}, 3, @key);
- } elsif ($args->{flag} == 2) {
- message T("Login PIN code has been changed successfully.n");
- } elsif ($args->{flag} == 3) {
- warning TF("Failed to change the login PIN code. Please try again.n");
- configModify('loginPinCode', '', silent => 1);
- my $oldPin = queryLoginPinCode(T("Please enter your old login PIN code:"));
- if (!defined($oldPin)) {
- return;
- }
- my $newPinCode = queryLoginPinCode(T("Please enter a new login PIN code:"));
- if (!defined($newPinCode)) {
- return;
- }
- configModify('loginPinCode', $newPinCode, silent => 1);
- my @key = split /[, ]+/, $masterServer->{PINEncryptKey};
- if (!@key) {
- $interface->errorDialog(T("Unable to send PIN code. You must set the 'PINEncryptKey' option in servers.txt."));
- quit();
- return;
- }
- $messageSender->sendLoginPinCode($oldPin, $newPinCode, $args->{key}, 2, @key);
- } elsif ($args->{flag} == 4) {
- # PIN code incorrect.
- configModify('loginPinCode', '', 1);
- return if (!queryAndSaveLoginPinCode(T("The login PIN code that you entered is incorrect. Please re-enter your login PIN code.")));
- my @key = split /[, ]+/, $masterServer->{PINEncryptKey};
- if (!@key) {
- $interface->errorDialog(T("Unable to send PIN code. You must set the 'PINEncryptKey' option in servers.txt."));
- quit();
- return;
- }
- $messageSender->sendPinCode($config{loginPinCode}, 0, $args->{key}, 3, @key);
- } elsif ($args->{flag} == 5) {
- # PIN Entered 3 times Wrong, Disconnect
- warning T("You have entered 3 incorrect login PIN codes in a row. Reconnecting...n");
- configModify('loginPinCode', '', silent => 1);
- $timeout_ex{master}{time} = time;
- $timeout_ex{master}{timeout} = $timeout{reconnect}{timeout};
- $net->serverDisconnect();
- } else {
- debug("login_pin_code_request: unknown flag $args->{flag}n");
- }
- $timeout{master}{time} = time;
- }
- sub initialize_message_id_encryption {
- my ($self, $args) = @_;
- if ($masterServer->{messageIDEncryption} ne '0') {
- $messageSender->sendMessageIDEncryptionInitialized();
- my @c;
- my $shtmp = $args->{param1};
- for (my $i = 8; $i > 0; $i--) {
- $c[$i] = $shtmp & 0x0F;
- $shtmp >>= 4;
- }
- my $w = ($c[6]<<12) + ($c[4]<<8) + ($c[7]<<4) + $c[1];
- $enc_val1 = ($c[2]<<12) + ($c[3]<<8) + ($c[5]<<4) + $c[8];
- $enc_val2 = (((($enc_val1 ^ 0x0000F3AC) + $w) << 16) | (($enc_val1 ^ 0x000049DF) + $w)) ^ $args->{param2};
- }
- }
- sub switch_character {
- # User is switching characters in X-Kore
- $net->setState(Network::CONNECTED_TO_MASTER_SERVER);
- $net->serverDisconnect();
- }
- sub system_chat {
- my ($self, $args) = @_;
- my $message = bytesToString($args->{message});
- stripLanguageCode($message);
- chatLog("s", "$messagen") if ($config{logSystemChat});
- # Translation Comment: System/GM chat
- message TF("[GM] %sn", $message), "schat";
- ChatQueue::add('gm', undef, undef, $message);
- Plugins::callHook('packet_sysMsg', {
- Msg => $message
- });
- }
- sub top10_alchemist_rank {
- my ($self, $args) = @_;
- my $textList = bytesToString(top10Listing($args));
- message TF("============= ALCHEMIST RANK ================n" .
- "# Name Pointsn".
- "%s" .
- "=============================================n", $textList), "list";
- }
- sub top10_blacksmith_rank {
- my ($self, $args) = @_;
- my $textList = bytesToString(top10Listing($args));
- message TF("============= BLACKSMITH RANK ===============n" .
- "# Name Pointsn".
- "%s" .
- "=============================================n", $textList), "list";
- }
- sub top10_pk_rank {
- my ($self, $args) = @_;
- my $textList = bytesToString(top10Listing($args));
- message TF("================ PVP RANK ===================n" .
- "# Name Pointsn".
- "%s" .
- "=============================================n", $textList), "list";
- }
- sub top10_taekwon_rank {
- my ($self, $args) = @_;
- my $textList = bytesToString(top10Listing($args));
- message TF("=============== TAEKWON RANK ================n" .
- "# Name Pointsn".
- "%s" .
- "=============================================n", $textList), "list";
- }
- sub unequip_item {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my $item = $char->inventory->getByServerIndex($args->{index});
- delete $item->{equipped};
- if ($args->{type} == 10) {
- delete $char->{equipment}{arrow};
- } else {
- foreach (%equipSlot_rlut){
- if ($_ & $args->{type}){
- next if $_ == 10; #work around Arrow bug
- delete $char->{equipment}{$equipSlot_lut{$_}};
- }
- }
- }
- if ($item) {
- message TF("You unequip %s (%d) - %sn",
- $item->{name}, $item->{invIndex},
- $equipTypes_lut{$item->{type_equip}}), 'inventory';
- }
- }
- sub unit_levelup {
- my ($self, $args) = @_;
- my $ID = $args->{ID};
- my $type = $args->{type};
- my $name = getActorName($ID);
- if ($type == 0) {
- message TF("%s gained a level!n", $name);
- Plugins::callHook('base_level', {name => $name});
- } elsif ($type == 1) {
- message TF("%s gained a job level!n", $name);
- Plugins::callHook('job_level', {name => $name});
- } elsif ($type == 2) {
- message TF("%s failed to refine a weapon!n", $name), "refine";
- } elsif ($type == 3) {
- message TF("%s successfully refined a weapon!n", $name), "refine";
- }
- }
- sub use_item {
- my ($self, $args) = @_;
- return unless changeToInGameState();
- my $item = $char->inventory->getByServerIndex($args->{index});
- if ($item) {
- $item->{amount} -= $args->{amount};
- message TF("You used Item: %s (%d) x %sn", $item->{name}, $item->{invIndex}, $args->{amount}), "useItem";
- if ($item->{amount} <= 0) {
- $char->inventory->remove($item);
- }
- }
- }
- sub users_online {
- my ($self, $args) = @_;
- message TF("There are currently %s users onlinen", $args->{users}), "info";
- }
- sub vender_found {
- my ($self, $args) = @_;
- my $ID = $args->{ID};
- if (!$venderLists{$ID} || !%{$venderLists{$ID}}) {
- binAdd(@venderListsID, $ID);
- Plugins::callHook('packet_vender', {ID => $ID});
- }
- $venderLists{$ID}{title} = bytesToString($args->{title});
- $venderLists{$ID}{id} = $ID;
- }
- sub vender_items_list {
- my ($self, $args) = @_;
- my $msg = $args->{RAW_MSG};
- my $msg_size = $args->{RAW_MSG_SIZE};
- undef @venderItemList;
- undef $venderID;
- $venderID = substr($msg,4,4);
- my $player = Actor::get($venderID);
- message TF("%sn" .
- "# Name Type Amount Pricen",
- center(' Vender: ' . $player->nameIdx . ' ', 79, '-')), "list";
- for (my $i = 8; $i < $msg_size; $i+=22) {
- my $number = unpack("v1", substr($msg, $i + 6, 2));
- my $item = $venderItemList[$number] = {};
- $item->{price} = unpack("V1", substr($msg, $i, 4));
- $item->{amount} = unpack("v1", substr($msg, $i + 4, 2));
- $item->{type} = unpack("C1", substr($msg, $i + 8, 1));
- $item->{nameID} = unpack("v1", substr($msg, $i + 9, 2));
- $item->{identified} = unpack("C1", substr($msg, $i + 11, 1));
- $item->{broken} = unpack("C1", substr($msg, $i + 12, 1));
- $item->{upgrade} = unpack("C1", substr($msg, $i + 13, 1));
- $item->{cards} = substr($msg, $i + 14, 8);
- $item->{name} = itemName($item);
- debug("Item added to Vender Store: $item->{name} - $item->{price} zn", "vending", 2);
- Plugins::callHook('packet_vender_store', {
- venderID => $venderID,
- number => $number,
- name => $item->{name},
- amount => $item->{amount},
- price => $item->{price},
- upgrade => $item->{upgrade},
- cards => $item->{cards},
- type => $item->{type}
- });
- message(swrite(
- "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<< @>>>>> @>>>>>>>>>z",
- [$number, $item->{name}, $itemTypes_lut{$item->{type}}, $item->{amount}, formatNumber($item->{price})]),
- "list");
- }
- message("-------------------------------------------------------------------------------n", "list");
- Plugins::callHook('packet_vender_store2', {
- venderID => $venderID,
- itemList => @venderItemList
- });
- }
- sub vender_lost {
- my ($self, $args) = @_;
- my $ID = $args->{ID};
- binRemove(@venderListsID, $ID);
- delete $venderLists{$ID};
- }
- sub vender_buy_fail {
- my ($self, $args) = @_;
- my $reason;
- if ($args->{fail} == 1) {
- error TF("Failed to buy %s of item #%s from vender (insufficient zeny).n", $args->{amount}, $args->{index});
- } elsif ($args->{fail} == 2) {
- error TF("Failed to buy %s of item #%s from vender (overweight).n", $args->{amount}, $args->{index});
- } else {
- error TF("Failed to buy %s of item #%s from vender (unknown code %s).n", $args->{amount}, $args->{index}, $args->{fail});
- }
- }
- sub vending_start {
- my ($self, $args) = @_;
- my $msg = $args->{RAW_MSG};
- my $msg_size = unpack("v1",substr($msg, 2, 2));
- #started a shop.
- @articles = ();
- # FIXME: why do we need a seperate variable to track how many items are left in the store?
- $articles = 0;
- # FIXME: Read the packet the server sends us to determine
- # the shop title instead of using $shop{title}.
- message TF("%sn" .
- "# Name Type Amount Pricen",
- center(" $shop{title} ", 79, '-')), "list";
- for (my $i = 8; $i < $msg_size; $i += 22) {
- my $number = unpack("v1", substr($msg, $i + 4, 2));
- my $item = $articles[$number] = {};
- $item->{nameID} = unpack("v1", substr($msg, $i + 9, 2));
- $item->{quantity} = unpack("v1", substr($msg, $i + 6, 2));
- $item->{type} = unpack("C1", substr($msg, $i + 8, 1));
- $item->{identified} = unpack("C1", substr($msg, $i + 11, 1));
- $item->{broken} = unpack("C1", substr($msg, $i + 12, 1));
- $item->{upgrade} = unpack("C1", substr($msg, $i + 13, 1));
- $item->{cards} = substr($msg, $i + 14, 8);
- $item->{price} = unpack("V1", substr($msg, $i, 4));
- $item->{name} = itemName($item);
- $articles++;
- debug("Item added to Vender Store: $item->{name} - $item->{price} zn", "vending", 2);
- message(swrite(
- "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<< @>>>>> @>>>>>>>>>z",
- [$articles, $item->{name}, $itemTypes_lut{$item->{type}}, $item->{quantity}, formatNumber($item->{price})]),
- "list");
- }
- message(('-'x79)."n", "list");
- $shopEarned ||= 0;
- }
- sub warp_portal_list {
- my ($self, $args) = @_;
- # strip gat extension
- ($args->{memo1}) = $args->{memo1} =~ /^(.*).gat/;
- ($args->{memo2}) = $args->{memo2} =~ /^(.*).gat/;
- ($args->{memo3}) = $args->{memo3} =~ /^(.*).gat/;
- ($args->{memo4}) = $args->{memo4} =~ /^(.*).gat/;
- # Auto-detect saveMap
- if ($args->{type} == 26) {
- configModify('saveMap', $args->{memo2}) if ($args->{memo2} && $config{'saveMap'} ne $args->{memo2});
- } elsif ($args->{type} == 27) {
- configModify('saveMap', $args->{memo1}) if ($args->{memo1} && $config{'saveMap'} ne $args->{memo1});
- }
- $char->{warp}{type} = $args->{type};
- undef @{$char->{warp}{memo}};
- push @{$char->{warp}{memo}}, $args->{memo1} if $args->{memo1} ne "";
- push @{$char->{warp}{memo}}, $args->{memo2} if $args->{memo2} ne "";
- push @{$char->{warp}{memo}}, $args->{memo3} if $args->{memo3} ne "";
- push @{$char->{warp}{memo}}, $args->{memo4} if $args->{memo4} ne "";
- message T("----------------- Warp Portal --------------------n" .
- "# Place Mapn"), "list";
- for (my $i = 0; $i < @{$char->{warp}{memo}}; $i++) {
- message(swrite(
- "@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<",
- [$i, $maps_lut{$char->{warp}{memo}[$i].'.rsw'},
- $char->{warp}{memo}[$i]]),
- "list");
- }
- message("--------------------------------------------------n", "list");
- }
- sub mail_refreshinbox {
- my ($self, $args) = @_;
- undef $mailList;
- my $count = $args->{count};
- if (!$count) {
- message T("There is no mail in your inbox.n"), "info";
- return;
- }
- message TF("You've got Mail! (%s)n", $count), "info";
- my $msg;
- $msg .= center(" " . T("Inbox") . " ", 79, '-') . "n";
- # truncating the title from 39 to 34, the user will be able to read the full title when reading the mail
- # truncating the date with precision of minutes and leave year out
- $msg .= swrite(TF("@> R @%s @%s @%s", ('<'x34), ('<'x24), ('<'x11)),
- ["#", "Title", "Sender", "Date"]);
- $msg .= sprintf("%sn", ('-'x79));
-
- my $j = 0;
- for (my $i = 8; $i < 8 + $count * 73; $i+=73) {
- $mailList->[$j]->{mailID} = unpack("V1", substr($args->{RAW_MSG}, $i, 4));
- $mailList->[$j]->{title} = bytesToString(unpack("Z40", substr($args->{RAW_MSG}, $i+4, 40)));
- $mailList->[$j]->{read} = unpack("C1", substr($args->{RAW_MSG}, $i+44, 1));
- $mailList->[$j]->{sender} = bytesToString(unpack("Z24", substr($args->{RAW_MSG}, $i+45, 24)));
- $mailList->[$j]->{timestamp} = unpack("V1", substr($args->{RAW_MSG}, $i+69, 4));
- $msg .= swrite(
- TF("@> %s @%s @%s @%s", $mailList->[$j]->{read}, ('<'x34), ('<'x24), ('<'x11)),
- [$j, $mailList->[$j]->{title}, $mailList->[$j]->{sender}, getFormattedDate(int($mailList->[$j]->{timestamp}))]);
- $j++;
- }
- $msg .= ("%sn", ('-'x79));
- message($msg . "n", "list");
- }
- sub mail_read {
- my ($self, $args) = @_;
- my $item = new Actor::Item();
- $item->{nameID} = $args->{nameID};
- $item->{upgrade} = $args->{upgrade};
- $item->{cards} = $args->{cards};
- $item->{broken} = $args->{broken};
- $item->{name} = itemName($item);
- my $msg;
- $msg .= center(" " . T("Mail") . " ", 79, '-') . "n";
- $msg .= swrite(TF("Title: @%s Sender: @%s", ('<'x39), ('<'x24)),
- [bytesToString($args->{title}), bytesToString($args->{sender})]);
- $msg .= TF("Message: %sn", bytesToString($args->{message}));
- $msg .= ("%sn", ('-'x79));
- $msg .= TF( "Item: %s %sn" .
- "Zeny: %szn",
- $item->{name}, ($args->{amount}) ? "x " . $args->{amount} : "", formatNumber($args->{zeny}));
- $msg .= sprintf("%sn", ('-'x79));
- message($msg, "info");
- }
- sub mail_getattachment {
- my ($self, $args) = @_;
- if (!$args->{fail}) {
- message T("Successfully added attachment to inventory.n"), "info";
- } elsif ($args->{fail} == 2) {
- error T("Failed to get the attachment to inventory due to your weight.n"), "info";
- } else {
- error T("Failed to get the attachment to inventory.n"), "info";
- }
- }
- sub mail_send {
- my ($self, $args) = @_;
- ($args->{fail}) ?
- error T("Failed to send mail, the recipient does not exist.n"), "info" :
- message T("Mail sent succesfully.n"), "info";
- }
- sub mail_new {
- my ($self, $args) = @_;
- message TF("New mail from sender: %s titled: %s.n", $args->{sender}, $args->{title}), "info";
- }
- sub mail_setattachment {
- my ($self, $args) = @_;
- message TF("%s to attach %s.n", ($args->{fail}) ? "Failed" : "Succeeded", ($args->{index}) ? "item: ".$char->inventory->getByServerIndex($args->{index}) : "zeny"), "info";
- }
- sub mail_delete {
- my ($self, $args) = @_;
- message TF("%s to delete mail with ID: %s.n", ($args->{fail}) ? "Failed" : "Succeeded", $args->{mailID}), "info";
- }
- sub mail_window {
- my ($self, $args) = @_;
- message TF("Mail window is now %s.n", ($args->{flag}) ? "closed" : "opened"), "info";
- }
- sub mail_return {
- my ($self, $args) = @_;
- ($args->{fail}) ?
- error TF("The mail with ID: %s does not exist.n", $args->{mailID}), "info" :
- message TF("The mail with ID: %s is returned to the sender.n", $args->{mailID}), "info";
- }
- sub auction_result {
- my ($self, $args) = @_;
- my $flag = $args->{flag};
- if ($flag == 0) {
- message T("You have failed to bid into the auction.n"), "info";
- } elsif ($flag == 1) {
- message T("You have successfully bid in the auction.n"), "info";
- } elsif ($flag == 2) {
- message T("The auction has been canceled.n"), "info";
- } elsif ($flag == 3) {
- message T("An auction with at least one bidder cannot be canceled.n"), "info";
- } elsif ($flag == 4) {
- message T("You cannot register more than 5 items in an auction at a time.n"), "info";
- } elsif ($flag == 5) {
- message T("You do not have enough Zeny to pay the Auction Fee.n"), "info";
- } elsif ($flag == 6) {
- message T("You have won the auction.n"), "info";
- } elsif ($flag == 7) {
- message T("You have failed to win the auction.n"), "info";
- } elsif ($flag == 8) {
- message T("You do not have enough Zeny.n"), "info";
- } elsif ($flag == 9) {
- message T("You cannot place more than 5 bids at a time.n"), "info";
- }
- }
- sub auction_item_request_search {
- my ($self, $args) = @_;
- #$pages = $args->{pages};$size = $args->{size};
- undef $auctionList;
- my $count = $args->{count};
- if (!$count) {
- message T("No item in auction.n"), "info";
- return;
- }
- message TF("Found %s items in auction.n", $count), "info";
- my $msg;
- $msg .= center(" " . T("Auction") . " ", 79, '-') . "n";
- $msg .= swrite(TF("@%s @%s @%s @%s @%s", ('>'x2), ('<'x37), ('>'x10), ('>'x10), ('<'x11)),
- ["#", "Item", "High Bid", "Purchase", "End-Date"]);
- $msg .= sprintf("%sn", ('-'x79));
- my $j = 0;
- for (my $i = 12; $i < 12 + $count * 83; $i += 83) {
- $auctionList->[$j]->{ID} = unpack("V1", substr($args->{RAW_MSG}, $i, 4));
- $auctionList->[$j]->{seller} = bytesToString(unpack("Z24", substr($args->{RAW_MSG}, $i+4, 24)));
- $auctionList->[$j]->{nameID} = unpack("v1", substr($args->{RAW_MSG}, $i+28, 2));
- $auctionList->[$j]->{type} = unpack("v1", substr($args->{RAW_MSG}, $i+30, 2));
- $auctionList->[$j]->{unknown} = unpack("v1", substr($args->{RAW_MSG}, $i+32, 2));
- $auctionList->[$j]->{amount} = unpack("v1", substr($args->{RAW_MSG}, $i+34, 2));
- $auctionList->[$j]->{identified} = unpack("C1", substr($args->{RAW_MSG}, $i+36, 1));
- $auctionList->[$j]->{broken} = unpack("C1", substr($args->{RAW_MSG}, $i+37, 1));
- $auctionList->[$j]->{upgrade} = unpack("C1", substr($args->{RAW_MSG}, $i+38, 1));
- # TODO
- #$auctionList->[$j]->{card}->[0] = unpack("v1", substr($args->{RAW_MSG}, $i+39, 2));
- #$auctionList->[$j]->{card}->[1] = unpack("v1", substr($args->{RAW_MSG}, $i+41, 2));
- #$auctionList->[$j]->{card}->[2] = unpack("v1", substr($args->{RAW_MSG}, $i+43, 2));
- #$auctionList->[$j]->{card}->[3] = unpack("v1", substr($args->{RAW_MSG}, $i+45, 2));
- $auctionList->[$j]->{cards} = unpack("a8", substr($args->{RAW_MSG}, $i+39, 8));
- $auctionList->[$j]->{price} = unpack("V1", substr($args->{RAW_MSG}, $i+47, 4));
- $auctionList->[$j]->{buynow} = unpack("V1", substr($args->{RAW_MSG}, $i+51, 4));
- $auctionList->[$j]->{buyer} = bytesToString(unpack("Z24", substr($args->{RAW_MSG}, $i+55, 24)));
- $auctionList->[$j]->{timestamp} = unpack("V1", substr($args->{RAW_MSG}, $i+79, 4));
- my $item = new Actor::Item();
- $item->{nameID} = $auctionList->[$j]->{nameID};
- $item->{upgrade} = $auctionList->[$j]->{upgrade};
- $item->{cards} = $auctionList->[$j]->{cards};
- $item->{broken} = $auctionList->[$j]->{broken};
- $item->{name} = itemName($item);
-
- $msg .= swrite(TF("@%s @%s @%s @%s @%s", ('>'x2),, ('<'x37), ('>'x10), ('>'x10), ('<'x11)),
- [$j, $item->{name}, formatNumber($auctionList->[$j]->{price}),
- formatNumber($auctionList->[$j]->{buynow}), getFormattedDate(int($auctionList->[$j]->{timestamp}))]);
- $j++;
- }
- $msg .= sprintf("%sn", ('-'x79));
- message($msg, "list");
- }
- sub auction_my_sell_stop {
- my ($self, $args) = @_;
- my $flag = $args->{flag};
- if ($flag == 0) {
- message T("You have ended the auction.n"), "info";
- } elsif ($flag == 1) {
- message T("You cannot end the auction.n"), "info";
- } elsif ($flag == 2) {
- message T("Bid number is incorrect.n"), "info";
- }
- }
- sub auction_windows {
- my ($self, $args) = @_;
- message TF("Auction window is now %s.n", ($args->{flag}) ? "closed" : "opened"), "info";
- }
- sub auction_add_item {
- my ($self, $args) = @_;
- message TF("%s to add item with index: %s.n", ($args->{fail}) ? "Failed (note: usable items can't be auctioned)" : "Succeeded", $args->{index}), "info";
- }
- # this info will be sent to xkore 2 clients
- sub hotkeys {
- my ($self, $args) = @_;
- my $msg;
- $msg .= center(" " . T("Hotkeys") . " ", 79, '-') . "n";
- $msg .= swrite(TF("@%s @%s @%s @%s", ('>'x3), ('<'x30), ('<'x5), ('>'x3)),
- ["#", "Name", "Type", "Lv"]);
- $msg .= sprintf("%sn", ('-'x79));
- my $j = 0;
- for (my $i = 2; $i < $args->{RAW_MSG_SIZE}; $i+=7) {
- $hotkeyList->[$j]->{type} = unpack("C1", substr($args->{RAW_MSG}, $i, 1));
- $hotkeyList->[$j]->{ID} = unpack("V1", substr($args->{RAW_MSG}, $i+1, 4));
- $hotkeyList->[$j]->{lvl} = unpack("v1", substr($args->{RAW_MSG}, $i+5, 2));
- $msg .= swrite(TF("@%s @%s @%s @%s", ('>'x3), ('<'x30), ('<'x5), ('>'x3)),
- [$j, $hotkeyList->[$j]->{type} ? Skill->new(idn => $hotkeyList->[$j]->{ID})->getName() : itemNameSimple($hotkeyList->[$j]->{ID}),
- $hotkeyList->[$j]->{type} ? "skill" : "item",
- $hotkeyList->[$j]->{lvl}]);
- $j++;
- }
- $msg .= sprintf("%sn", ('-'x79));
- debug($msg, "list");
- }
- sub hack_shield_alarm {
- error T("Error: You have been forced to disconnect by a Hack Shield.n Please check Poseidon.n"), "connection";
- Commands::run('relog 100000000');
- }
- 1;