Prestige
This custom NPC script will allow you to take a character and change their class into another. On my development server I am allowing all race/class/deity combinations.
The character will retain all spent AA points and AA XP.
Requirements to prestige can be changed at the top of the script but these are the defaults: - Must be level 100
- Have Epic 2.0 completed and have the item
You can change the minimum required level, required item, and the default level to attain after prestige.
The following will happen: - Summons and removes all corpses, so make sure you've looted any remaining corpses
- Changes from level 100 to level 1
- Sets the appropriate experience
- Resets all skills
- Resets all languages
- Untrain all discs
- Unscribe all spells
- WorldKick to login
Character must be naked (if not, the script will tell you to remove X item from Y slot)
When you hail the NPC, choose "prestige". If you meet the base requirements then you can select the class of your choice. A popup will ask you to confirm this change before continuing. Once you've confirmed, it's done! If you cancel, no changes will take place.
I tried to document the entire code base to make it easier to follow what happens during each stage.
If you find any problems, want changes, etc. Please let me know.
Example 1 - Meets requirements:
Example 2 - Does not meet the base requirements:
Prestige.pl
HTML Code:
######################
# Quest Name: Prestige
# Author: Diemuzi
# NPCs Involved: 1
# Items Involved: 1
######################
# Minimum level required to prestige
my $min_level_required = 100;
# Required Item ID for each class in order to prestige, currently set to Epic 2.0
# Class Bitmask => Required ItemID
my %required_item = (
1 => 60332, # Warrior
2 => 20076, # Cleric
4 => 48147, # Paladin
8 => 62649, # Ranger
16 => 48136, # Shadow Knight
32 => 62880, # Druid
64 => 67742, # Monk
128 => 77640, # Bard
256 => 52348, # Rogue
512 => 57405, # Shaman
1024 => 64067, # Necromancer
2048 => 16575, # Wizard
4096 => 19839, # Magician
8192 => 52962, # Enchanter
16384 => 57054, # Beastlord
32768 => 20072 # Berserker
);
# Default level after prestige
my $set_level = 1; # Level 1 - 100
############################################
# DO NOT CHANGE ANYTHING BEYOND THIS POINT #
############################################
my $can_prestige = 0; # 0 = No, 1 = Yes
my $class_type = undef; # Class type is undef until choosen later
my $has_gear_equipped = 0; # 0 = No, 1 or more = Yes
my $has_meet_requirement = 0; # 0 = No, 1 = Yes
# Available classes
my %classes = (
1 => 'Warrior',
2 => 'Cleric',
3 => 'Paladin',
4 => 'Ranger',
5 => 'Shadow Knight',
6 => 'Druid',
7 => 'Monk',
8 => 'Bard',
9 => 'Rogue',
10 => 'Shaman',
11 => 'Necromancer',
12 => 'Wizard',
13 => 'Magician',
14 => 'Enchanter',
15 => 'Beastlord',
16 => 'Berserker'
);
sub EVENT_SAY {
#:: Match text for "hail", case insensitive
if ($text =~ /hail/i) {
#:: Send a message that only the client can see, in yellow (15) text
$client->Message(15, "Hey $name. Would you like to [" . quest::saylink("prestige") . "]?");
# Reset to default values
$can_prestige = 0;
$has_gear_equipped = 0;
$has_meet_requirement = 0;
}
#:: Match text for "prestige", case insensitive
elsif ($text =~ /prestige/i) {
# Reset to default values
$can_prestige = 0;
$has_gear_equipped = 0;
$has_meet_requirement = 0;
# Check that the current class has the required item
if ($client->CountItem($required_item{$client->GetClassBitmask()}) == 1) {
$has_meet_requirement = 1;
}
# Check if the requirements have been met
if ($has_meet_requirement && $client->GetLevel() == $min_level_required) {
#:: Send a message that only the client can see, in yellow (15) text
$client->Message(15, 'You have the required item and have been granted access.');
# Make sure we are naked
INVENTORY_CHECK();
# Success, we are naked and can continue
if (!$has_gear_equipped) {
$client->Message(15, "You are naked and can continue.");
# Play Ding!
quest::ding();
# Can proceed after choosing a class
$can_prestige = 1;
# Choose a class
$client->Message(15, 'Which class would you like to be next?');
$client->Message(15, "Cloth classes [" . quest::saylink("Enchanter") . "] -- [" . quest::saylink("Magician") . "] -- [" . quest::saylink("Necromancer") . "] -- [" . quest::saylink("Wizard") . "]");
$client->Message(15, "Leather classes [" . quest::saylink("Beastlord") . "] -- [" . quest::saylink("Druid") . "] -- [" . quest::saylink("Monk") . "]");
$client->Message(15, "Chain classes [" . quest::saylink("Berserker") . "] -- [" . quest::saylink("Ranger") . "] -- [" . quest::saylink("Rogue") . "] -- [" . quest::saylink("Shaman") . "]");
$client->Message(15, "Plate classes [" . quest::saylink("Bard") . "] -- [" . quest::saylink("Cleric") . "] -- [" . quest::saylink("Paladin") . "] -- [" . quest::saylink("Shadow Knight") . "] -- [" . quest::saylink("Warrior") . "]");
}
}
else {
# Character does not meet the basic requirements
$client->Message(15, "Sorry but you do not meet the requirements. You must be at least level " . $min_level_required . " and have the required item.");
}
}
# We can prestige and continue after confirming
if ($can_prestige == 1) {
keys %classes;
while (my ($k, $v) = each %classes) {
if ($text =~ /$v/i) {
# Reset to default value
$can_prestige = 0;
# Chosen class type
$class_type = $k;
# Display confirmation window
my $dialogMessage = "{title: Confirm Character Class Change} {button_one: Yes} {button_two: No} wintype:1 popupid:1 secondresponseid:2 Are you sure you want to change your class to a " . $v . "?
{linebreak}
{in}{r}MAKE SURE ALL CORPSES ARE LOOTED BEFORE CONTINUING AS THEY WILL BE DESTORYED.~
{linebreak}
{in}{y}If you continue, you will be disconnected.~";
quest::crosszonedialoguewindowbycharid($client->CharacterID(), $dialogMessage);
}
}
}
}
sub EVENT_ITEM {
#:: Return unused items since we don't expect any handins
plugin::returnUnusedItems();
}
sub EVENT_POPUPRESPONSE {
# Yes
if ($popupid == 1) {
# Make sure we are still naked
INVENTORY_CHECK();
if (!$has_gear_equipped) {
# Summon and remove all corpses
REMOVE_CORPSES();
# Level
$client->SetClientMaxLevel($set_level);
# Experience per level
my %exp = (
1 => 0,
2 => 1000,
3 => 8000,
4 => 27000,
5 => 64000,
6 => 125000,
7 => 216000,
8 => 343000,
9 => 512000,
10 => 729000,
11 => 1000000,
12 => 1331000,
13 => 1728000,
14 => 2197000,
15 => 2744000,
16 => 3375000,
17 => 4096000,
18 => 4913000,
19 => 5832000,
20 => 6859000,
21 => 8000000,
22 => 9261000,
23 => 10648000,
24 => 12167000,
25 => 13824000,
26 => 15625000,
27 => 17576000,
28 => 19683000,
29 => 21952000,
30 => 24389000,
31 => 29700000,
32 => 32770100,
33 => 36044800,
34 => 39530700,
35 => 43234400,
36 => 51450000,
37 => 55987200,
38 => 60783600,
39 => 65846400,
40 => 71182800,
41 => 83200000,
42 => 89597296,
43 => 96314400,
44 => 103359104,
45 => 110739200,
46 => 127575000,
47 => 136270400,
48 => 145352192,
49 => 154828800,
50 => 164708608,
51 => 175000000,
52 => 198976496,
53 => 224972800,
54 => 253090896,
55 => 299181600,
56 => 349387488,
57 => 403916800,
58 => 462982496,
59 => 526802400,
60 => 616137024,
61 => 669600000,
62 => 703641088,
63 => 738816768,
64 => 775145728,
65 => 812646400,
66 => 851337472,
67 => 891237632,
68 => 932365312,
69 => 974739200,
70 => 1018377920,
71 => 1063299968,
72 => 1109524096,
73 => 1157068800,
74 => 1205952640,
75 => 1256194432,
76 => 1307812480,
77 => 1360825600,
78 => 1415252352,
79 => 1471111168,
80 => 1528420864,
81 => 1587200000,
82 => 1647467136,
83 => 1709240832,
84 => 1772539648,
85 => 1837382400,
86 => 1903787520,
87 => 1971773568,
88 => 2041359360,
89 => 2112563200,
90 => 2185403904,
91 => 2259899904,
92 => 2336070144,
93 => 2413932800,
94 => 2493506816,
95 => 2574810368,
96 => 2657862400,
97 => 2742681600,
98 => 2829286400,
99 => 2917695232,
100 => 3007926784
);
# Set Experience based on level
$client->SetEXP($exp{$set_level}, $client->GetAAExp());
# Reset skills
my @skills = (0 .. 77);
for (@skills) {
if ($client->HasSkill($_)) {
$client->SetSkill($_, 1);
}
}
# Reset languages
my @language = (0 .. 27);
for (@language) {
$client->SetLanguageSkill($_, 1);
}
# Untrain Discs
$client->UntrainDiscAll();
# Unsubscribe Spells
$client->UnscribeSpellAll();
# Set Class
$client->SetBaseClass($class_type);
# Kick to login
$client->WorldKick();
} else {
$client->Message(15, "Nice try, but you're not naked!");
}
}
# No
if ($popupid == 2) {
# No changes will take place
$client->Message(15, "You will remain a " . $client->GetClassAbbreviation() . ".");
}
}
sub INVENTORY_CHECK {
# Inventory items to check if equipped
my %inventory = (
0 => 'Charm', # Charm
1 => 'Ear', # Ear1
2 => 'Head', # Head
3 => 'Face', # Face
4 => 'Ear', # Ear2
5 => 'Neck', # Neck
6 => 'Shoulder', # Shoulders
7 => 'Arm', # Arms
8 => 'Back', # Back
9 => 'Wrist', # Wrist1
10 => 'Wrist', # Wrist2
11 => 'Range', # Range
12 => 'Hand', # Hands
13 => 'Primary', # Primary
14 => 'Secondary', # Secondary
15 => 'Finger', # Finger1
16 => 'Finger', # Finger2
17 => 'Chest', # Chest
18 => 'Leg', # Legs
19 => 'Feet', # Feet
20 => 'Waist', # Waist
21 => 'PowerSource', # PowerSource
22 => 'Ammo' # Ammo
);
# Make sure we are naked
keys %inventory;
while (my ($k, $v) = each %inventory) {
if ($client->GetItemAt($k)) {
$client->Message(15, "You have a " . lc($v) . " item equipped. Please remove it.");
$has_gear_equipped++;
}
}
}
sub REMOVE_CORPSES {
if ($client->GetCorpseCount() > 0) {
quest::summonallplayercorpses($client->CharacterID(), $client->GetX(), $client->GetY(), $client->GetZ(), 0);
}
my @corpse_list = $entity_list->GetCorpseList();
my $count = 0;
foreach $corpse_found (@corpse_list) {
if ($corpse_found->GetOwnerName() eq $client->GetName()) {
$corpse_found->Delete();
$count++;
}
}
$count;
}
Last edited by diemuzi; 09-25-2023 at 12:48 PM..
Reason: Inventory check twice, can't be too sneaky!
|