Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Char Move Slot and Rename #101

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 91 additions & 2 deletions src/Misc.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,7 @@ sub charSelectScreen {
# An array which maps an index in @charNames to an index in @chars
my @charNameIndices;
my $mode;
my $charSlots;

# Check system version to delete a character
my $charDeleteVersion;
Expand Down Expand Up @@ -1250,8 +1251,8 @@ sub charSelectScreen {
$messageMapName = sprintf(", %s", $chars[$num]{last_map});
}
}
push @charNames, TF("Slot %d: %s (%s, %s, level %d/%d%s)%s",

my $char_str = TF("Slot %d: %s (%s, %s, level %d/%d%s)%s",
$num,
$chars[$num]{name},
$jobs_lut{$chars[$num]{'jobID'}},
Expand All @@ -1260,7 +1261,9 @@ sub charSelectScreen {
$chars[$num]{lv_job},
$messageMapName,
$messageDeleteDate);
push @charNames, $char_str;
push @charNameIndices, $num;
$charSlots->{$num} = $char_str;
}

if (@charNames) {
Expand Down Expand Up @@ -1297,6 +1300,8 @@ sub charSelectScreen {
} else {
push @choices, T('Delete a character');
}
push @choices, T('Move a character slot');
push @choices, T('Rename a character');
} else {
message T("There are no characters on this account.\n"), "connection";
if ($config{char} ne "switch" && defined($char)) {
Expand Down Expand Up @@ -1330,6 +1335,12 @@ sub charSelectScreen {
# 'Create character' chosen
$mode = "create";

} elsif ($choice == @charNames+2) {
# 'Move a character slot' chosen
$mode = "move";
} elsif ($choice == @charNames+3) {
# 'Rename a character' chosen
$mode = "rename";
} else {
# 'Delete character' chosen
$mode = "delete";
Expand Down Expand Up @@ -1438,6 +1449,84 @@ sub charSelectScreen {
$AI::temp::delIndex = $charIndex;
$timeout{charlogin}{time} = time;
}

} elsif ($mode eq "move") {
my $choice = $interface->showMenu(
T("Select the character you want to move."),
\@charNames,
title => T("Move a character - Origin"));
if ($choice == -1) {
goto TOP;
}
my $charIndex = @charNameIndices[$choice];

if (!$chars[$charIndex]{slot_addon}) {
message TF("Character %s cannot be moved.\n", $chars[$charIndex]{name}), "info";
goto TOP;
}
my @available_slots;
#TODO:
# 1. Check for premium service slots
# 2. Overslots. Example char slots are 40 in screen and producible only 6 but players have 27 chars!
# 3. Billing slots, idk how it works
for (my $i = 0; $i < ($charSvrSet{total_slot} > 0 ? $charSvrSet{total_slot} : $charSvrSet{producible_slot}); $i++) {
if ($charSlots->{$i} ne "") {
push @available_slots,$charSlots->{$i};
} else {
push @available_slots, TF("Slot %d: Empty", $i);
}
}
my $choice2 = $interface->showMenu(
T("Select the destination slot."),
\@available_slots,
title => T("Move a character - Destination"));
if ($choice2 == -1) {
goto TOP;
}
my $charToIndex = (defined @charNameIndices[$choice2] ? @charNameIndices[$choice2] : $choice2);

$messageSender->sendCharMoveSlot($charIndex, $charToIndex, $chars[$charIndex]{slot_addon});
message TF("Moving character %s from slot %d to %d...\n", $chars[$charIndex]{name}, $charIndex, $charToIndex), "connection";
$AI::temp::moveIndex = $charIndex;
$AI::temp::moveToIndex = $charToIndex;
$timeout{charlogin}{time} = time;

} elsif ($mode eq "rename") {
my $choice = $interface->showMenu(
T("Select the character you want to rename."),
\@charNames,
title => T("Rename a character"));
if ($choice == -1) {
goto TOP;
}
my $charIndex = @charNameIndices[$choice];

if (!$chars[$charIndex]{rename_addon}) {
message TF("Character %s cannot be renamed.\n", $chars[$charIndex]{name}), "info";
goto TOP;
}

my $message = TF("Input new name for \"%s\" (4-23 characters)\n", $chars[$charIndex]{name});
my $input = $interface->query($message);
unless ($input =~ /\S/) {
goto TOP;
} else {
my @args = parseArgs($input);
if (@args < 1) {
$interface->errorDialog(T("You didn't specify enough parameters."), 0);
goto TOP;
}
if (length $args[0] > 23) {
$interface->errorDialog(T("Name must not be longer than 23 characters."), 0);
goto TOP;
}

$messageSender->sendCharRename($accountID, $chars[$charIndex]{charID}, $args[0]);
message TF("Renaming character \"%s\" to \"%s\"...\n", $chars[$charIndex]{name}, $args[0]), "connection";
$AI::temp::charRenameIndex = $charIndex;
$AI::temp::charRenameName = $args[0];
$timeout{charlogin}{time} = time;
}
}
return 2;
}
Expand Down
123 changes: 123 additions & 0 deletions src/Network/Receive.pm
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,8 @@ sub character_creation_successful {

$character->{exp} = 0;
$character->{exp_job} = 0;
$character->{rename_addon} = 0;
$character->{slot_addon} = 0;

if (!exists($character->{sex})) { $character->{sex} = $accountSex2; }

Expand Down Expand Up @@ -7311,4 +7313,125 @@ sub cash_dealer {
message("-----------------------------------------------------\n", "list");
}

##
# 08D5 <unknown>.W <Reply>.W <MoveCountLeft>.W
# @author [Cydh]
##
sub char_move_slot_reply {
my ($self, $args) = @_;
if ($args->{reply} == 0) {
if (defined $AI::temp::moveIndex) {
message TF("Character %s moved from %d to %d.\n", $chars[$AI::temp::moveIndex]{name}, $AI::temp::moveIndex, $AI::temp::moveToIndex), "info";
my $movedChar = $chars[$AI::temp::moveIndex]; # Save temp
$chars[$AI::temp::moveIndex] = $chars[$AI::temp::moveToIndex];
$chars[$AI::temp::moveToIndex] = $movedChar;
$chars[$AI::temp::moveToIndex]{slot_addon} = $args->{slot_addon};
undef $AI::temp::moveIndex;
undef $AI::temp::moveToIndex;
} else {
message T("Character moved.\n"), "info";
relog(10);
return;
}
} else {
if (defined $AI::temp::moveIndex) {
message TF("Failed to move character %s from %d to %d.\n", $chars[$AI::temp::moveIndex]{name}, $AI::temp::moveIndex, $AI::temp::moveToIndex), "info";
undef $AI::temp::moveIndex;
undef $AI::temp::moveToIndex;
} else {
message T("Failed to move a character slot.\n"), "info";
}
}

if (charSelectScreen() == 1) {
$net->setState(3);
$firstLoginMap = 1;
$startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny;
$sentWelcomeMessage = 1;
}
debug "char_move_slot_reply unknown:$args->{unknown}, reply:$args->{reply}, slot_addon:$args->{slot_addon}\n", "parseMsg";
}

##
# 08E3 <charID>.L <unknown>.74B <CharInfo>.66B
# CharInfo: 'Z24 C8 v Z16 V4'
# @author [Cydh]
##
sub char_renamed {
my ($self, $args) = @_;

if (defined $AI::temp::charRenameIndex) {
my $charIndex = $AI::temp::charRenameIndex;
message TF("Character \"%s\" renamed to \"%s\"\n", $chars[$charIndex]{name}, $AI::temp::charRenameName), "info";
my $char_info = $self->received_characters_unpackString;
my $character = new Actor::You;
@{$character}{@{$char_info->{keys}}} = unpack($char_info->{types}, substr($args->{charInfo}, 0, $masterServer->{charBlockSize}));
my $name = bytesToString($character->{name});

if ($name ne $AI::temp::charRenameName) {
error TF("Name mismatch! %s <-> %s\n", $name, $AI::temp::charRenameName), "info";
}

$chars[$charIndex]{name} = $name;
$chars[$charIndex]{rename_addon} = $character->{rename_addon};

undef $AI::temp::charRenameIndex;
undef $AI::temp::charRenameName;

if (charSelectScreen() == 1) {
$net->setState(3);
$firstLoginMap = 1;
$startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny;
$sentWelcomeMessage = 1;
}
} else {
message T("Character renamed.\n"), "info";
relog(10);
return;
}

debug "char_renamed result:$args->{result}\n", "parseMsg";
}

##
# 08FD <Result>.W <unknown>.W
# @author [Cydh]
##
sub char_rename_result {
my ($self, $args) = @_;

# 0: Success
if ($args->{result} == 0 && defined $AI::temp::charRenameIndex) {
my $charIndex = $AI::temp::charRenameIndex;
message TF("Character \"%s\" renamed to \"%s\"\n", $chars[$charIndex]{name}, $AI::temp::charRenameName), "info";
return;
# 4: Fail already exists
} elsif ($args->{result} == 4) {
error TF("Cannot rename character to \"%s\". Name is already exists.\n", $AI::temp::charRenameName);
# 5: Fail in guild
} elsif ($args->{result} == 5) {
error TF("To rename a character you must withdraw from the guild.\n");
# 5: Fail in party
} elsif ($args->{result} == 6) {
error TF("To rename a character you must withdraw from the party.\n");
# 9: Invalid name
} elsif ($args->{result} == 9) {
error TF("Invalid new name \"%s\".\n", $AI::temp::charRenameName);
} else {
error TF("Unknown rename result occured:%d\n", $args->{result});
}

debug "char_rename_result result:$args->{result}\n";

undef $AI::temp::charRenameIndex;
undef $AI::temp::charRenameName;

if (charSelectScreen() == 1) {
$net->setState(3);
$firstLoginMap = 1;
$startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny;
$sentWelcomeMessage = 1;
}
}

1;
3 changes: 3 additions & 0 deletions src/Network/Receive/ServerType0.pm
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,9 @@ sub new {
'08CD' => ['actor_movement_interrupted', 'a4 v2', [qw(ID x y)]],
'08CF' => ['revolving_entity', 'a4 v v', [qw(sourceID type entity)]],
'08D2' => ['high_jump', 'a4 v2', [qw(ID x y)]],
'08D5' => ['char_move_slot_reply', 'v3', [qw(unknown reply moveCount)]],
'08E3' => ['char_renamed', 'a*', [qw(charInfo)]],
'08FD' => ['char_rename_result', 'v x', [qw(result)]],
'08FF' => ['actor_status_active', 'a4 v V4', [qw(ID type tick unknown1 unknown2 unknown3)]],
'0900' => ['inventory_items_stackable', 'v a*', [qw(len itemInfo)]],
'0901' => ['inventory_items_nonstackable', 'v a*', [qw(len itemInfo)]],
Expand Down
3 changes: 3 additions & 0 deletions src/Network/Receive/kRO/Sakexe_0.pm
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,9 @@ sub new {
'08D2' => ['high_jump', 'a4 v2', [qw(ID x y)]], # 10
'08B9' => ['login_pin_code_request', 'V a4 v', [qw(seed accountID flag)]],
'08C8' => ['actor_action', 'a4 a4 a4 V3 x v C V', [qw(sourceID targetID tick src_speed dst_speed damage div type dual_wield_damage)]],
'08D5' => ['char_move_slot_reply', 'v3', [qw(unknown reply moveCount)]],
'08E3' => ['char_renamed', 'a*', [qw(charInfo)]],
'08FD' => ['char_rename_result', 'v x', [qw(result)]],
'0906' => ['show_eq', 'v Z24 x17 a*', [qw(len name equips_info)]],
'0908' => ['inventory_item_favorite', 'a2 C', [qw(ID flag)]],#5
'090F' => ['actor_connected', 'v C a4 v3 V v11 a4 a2 v V C2 a3 C2 v2 a9 Z*', [qw(len object_type ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize lv font opt4 name)]],
Expand Down
41 changes: 41 additions & 0 deletions src/Network/Send.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3076,4 +3076,45 @@ sub sendStopSkillUse {
$self->sendToServer($self->reconstruct({switch => 'stop_skill_use',skillID => $ID}));
debug "Stop Skill Use: $ID\n", "sendPacket", 2;
}

##
# Move character slot
# 08D4 <FromSlot>.W <ToSlot>.W <MovesCount>.W
# @author [Cydh]
##
sub sendCharMoveSlot {
my ($self, $from, $to, $count) = @_;
$self->sendToServer($self->reconstruct({
switch => 'char_move_slot',
fromSlot => $from,
toSlot => $to,
movesCount => $count
}));
}

sub reconstruct_char_move_slot {
my ($self, $args) = @_;
debug "Move character slot from $args->{from} to $args->{to}. Move chance $args->{movesCount}\n", "sendPacket";
}

##
# Request to rename char
# 08FC <charID>.L <newName>.24B
# @author [Cydh]
##
sub sendCharRename {
my ($self, $accID, $charID, $newName) = @_;
$self->sendToServer($self->reconstruct({
switch => 'char_rename',
accountID => $accID,
charID => $charID,
newName => $newName
}));
}

sub reconstruct_char_rename {
my ($self, $args) = @_;
debug "Renaming character to $args->{newName} CID:".unpack("V",$args->{charID})."/AID:".unpack("V",$args->{accountID})."\n", "sendPacket";
}

1;
2 changes: 2 additions & 0 deletions src/Network/Send/ServerType0.pm
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ sub new {
'08C1' => ['macro_start'],#2
'08C2' => ['macro_stop'],#2
'08C9' => ['request_cashitems'],#2
'08D4' => ['char_move_slot','v3', [qw(fromSlot toSlot movesCount)]],
'08FC' => ['char_rename', 'a4 a24', [qw(charID newName)]],
'0970' => ['char_create', 'a24 C v2', [qw(name slot hair_style hair_color)]],
'097C' => ['rank_general', 'v', [qw(type)]],
'0987' => ['master_login', 'V Z24 a32 C', [qw(version username password_md5_hex master_version)]],
Expand Down
2 changes: 2 additions & 0 deletions src/Network/Send/kRO/Sakexe_0.pm
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ sub new {
'08B8' => ['send_pin_password','a4 Z*', [qw(accountID pin)]],
'08C1' => ['macro_start'],#2
'08C2' => ['macro_stop'],#2
'08D4' => ['char_move_slot','v3', [qw(fromSlot toSlot movesCount)]],
'08FC' => ['char_rename', 'a4 a24', [qw(charID newName)]],
'097C' => ['rank_general', 'v', [qw(type)]],
'098D' => ['clan_chat', 'v Z*', [qw(len message)]],
'09E9' => ['rodex_close_mailbox'], # 2 -- RodexCloseMailbox
Expand Down
5 changes: 5 additions & 0 deletions tables/packetdescriptions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@
02AE Initialize Message ID Encryption
0A3B Hat Effect Info
07E3 Skill Exchange Item
08D5 Char Move Slot Reply
08E3 Char Renamed
08FD Char Rename Result

[Send]
0064 Account Server Login
Expand Down Expand Up @@ -340,3 +343,5 @@
098F Char Delete Accept
082B Char Delete Cancel
07E4 Item List Window Selected
08D4 Char Move Slot
08FC Char Rename