From 72617ac98b4c40407627fe86e7123a25bfd8a025 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Sat, 31 Jan 2015 19:45:52 +0700 Subject: [PATCH 1/3] Skill DB clean ups * Changed skill_db array to better memory allocation. * Reduced MAX_SKILL_LEVEL from 100 to 10 (part of Hercules https://github.com/HerculesWS/Hercules/commit/0f4a50d13538c3e5d3ca4d3822f92217c8da0479), also has changes on skill_get checks for skill level that more than 10. * Reduced MAX_SKILL from 5020 to 1200 (actually only 1109 skills are used). * Added macros for checking Homunculus, Guild, Mercenary, & Elemental skill ranges. * Added skill check & index validation when player logged in and when @reloadskilldb. * Corrected `enum e_skill_flag` order for SKILL_FLAG_REPLACED_LV_0's sake. * Merged 'addtoskill' script command just as alias of 'skill' script command. Summary: * Decreased memory usage by map-server reduced up to 50 MB. * Decreased memory for each player because of mmo_charstatus::skill[] only has 1200 array, before is 5020. * Please use skill_get_index() for accessing sd->status.skill[] or skill_db[], don't reckless use skill_id as array index. NOTE: * Please import upgrade_20150131_skillset.sql Signed-off-by: Cydh Ramdh --- db/const.txt | 5 + db/pre-re/skill_db.txt | 19 +- db/re/skill_db.txt | 19 +- doc/script_commands.txt | 8 +- npc/merchants/buying_shops.txt | 2 +- npc/other/gympass.txt | 4 +- .../upgrades/upgrade_20150131_skillset.sql | 6 + src/char/char.c | 51 +- src/common/mmo.h | 18 +- src/map/atcommand.c | 42 +- src/map/battle.c | 13 +- src/map/chrif.c | 24 +- src/map/clif.c | 107 ++- src/map/clif.h | 6 +- src/map/elemental.c | 18 +- src/map/guild.c | 95 +- src/map/guild.h | 2 +- src/map/homunculus.c | 48 +- src/map/homunculus.h | 2 + src/map/itemdb.h | 4 + src/map/mercenary.c | 39 +- src/map/mercenary.h | 1 + src/map/mob.c | 9 +- src/map/npc.c | 20 +- src/map/pc.c | 611 +++++++------ src/map/pc.h | 27 +- src/map/script.c | 42 +- src/map/skill.c | 836 ++++++++++-------- src/map/skill.h | 168 ++-- src/map/status.c | 50 +- src/map/unit.c | 14 +- 31 files changed, 1297 insertions(+), 1013 deletions(-) create mode 100644 sql-files/upgrades/upgrade_20150131_skillset.sql diff --git a/db/const.txt b/db/const.txt index fe3f82d58d..c8a3d6d375 100644 --- a/db/const.txt +++ b/db/const.txt @@ -4644,5 +4644,10 @@ BSF_REM_ON_MADOGEAR 0x080 BSF_REM_ON_DAMAGED 0x100 BSF_PERMANENT 0x200 +SKILL_PERM 0 +SKILL_TEMP 1 +SKILL_TEMPLEVEL 2 +SKILL_PERM_GRANT 3 + false 0 true 1 diff --git a/db/pre-re/skill_db.txt b/db/pre-re/skill_db.txt index ce122606e4..82f4da4e3d 100644 --- a/db/pre-re/skill_db.txt +++ b/db/pre-re/skill_db.txt @@ -504,7 +504,7 @@ 357,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0,0x0, LK_CONCENTRATION,Concentration 358,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0, LK_TENSIONRELAX,Relax 359,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0, LK_BERSERK,Frenzy -//360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0, LK_FURY,Fury +360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0, LK_FURY,Fury //**** // High Priest @@ -553,10 +553,10 @@ //**** // Whitesmith 384,0,0,4,0,0x1,0,10,1,yes,0,0,0,weapon,0,0x4000, WS_MELTDOWN,Shattering Strike -//385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0, WS_CREATECOIN,Create Coins -//386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0, WS_CREATENUGGET,Create Nuggets +385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0, WS_CREATECOIN,Create Coins +386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0, WS_CREATENUGGET,Create Nuggets 387,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x4000, WS_CARTBOOST,Cart Boost -//388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0,0x0, WS_SYSTEMCREATE,Auto Attack System +388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0,0x0, WS_SYSTEMCREATE,Auto Attack System //**** // Stalker @@ -768,6 +768,11 @@ 543,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0,0x0, NJ_NEN,Soul 544,-5,6,1,0,0x40,0,10,1,no,0,0,0,weapon,0,0x0, NJ_ISSEN,Final Strike +572,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0, SL_DEATHKNIGHT,SL_DEATHKNIGHT +573,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0, SL_COLLECTOR,SL_COLLECTOR +574,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0, SL_NINJA,SL_NINJA +575,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0, SL_GUNNER,SL_GUNNER + //**** // Additional NPC Skills (Episode 11.3) 653,0,8,4,0,0x6,5:7:9:11:13:5:7:9:11:13,10,1,no,0,0x2,0,magic,0,0x0, NPC_EARTHQUAKE,Earthquake @@ -818,12 +823,12 @@ 689,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0,0x1000, CASH_BLESSING,Party Blessing 690,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0,0x1000, CASH_INCAGI,Party Increase AGI 691,0,6,4,0,0x3,-1,5,1,yes,0,0x2,0,magic,0,0x0, CASH_ASSUMPTIO,Party Assumptio -//692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_CATCRY,Cat Cry +692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_CATCRY,Cat Cry 693,0,6,4,0,0x3,-1,1,1,yes,0,0x2,0,magic,0,0x0, ALL_PARTYFLEE,Party Flee //694,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_ANGEL_PROTECT,Angel's Protection -//695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_DREAM_SUMMERNIGHT,Summer Night Dream +695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_DREAM_SUMMERNIGHT,Summer Night Dream //696,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, NPC_CHANGEUNDEAD2,Change Undead -//697,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0,0x0, ALL_REVERSEORCISH,Reverse Orcish +697,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0,0x0, ALL_REVERSEORCISH,Reverse Orcish 698,0,6,4,0,0x01,0,1,1,no,0,0x2,0,none,0,0x0, ALL_WEWISH,Christmas Carol //699,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_SONKRAN,ALL_SONKRAN diff --git a/db/re/skill_db.txt b/db/re/skill_db.txt index a33ba5dd59..89269628bb 100644 --- a/db/re/skill_db.txt +++ b/db/re/skill_db.txt @@ -504,7 +504,7 @@ 357,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0,0x0, LK_CONCENTRATION,Concentration 358,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0, LK_TENSIONRELAX,Relax 359,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0, LK_BERSERK,Frenzy -//360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0, LK_FURY,Fury +360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0, LK_FURY,Fury //**** // High Priest @@ -553,10 +553,10 @@ //**** // Whitesmith 384,0,0,4,0,0x1,0,10,1,yes,0,0,0,weapon,0,0x4000, WS_MELTDOWN,Shattering Strike -//385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0, WS_CREATECOIN,Create Coins -//386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0, WS_CREATENUGGET,Create Nuggets +385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0, WS_CREATECOIN,Create Coins +386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0, WS_CREATENUGGET,Create Nuggets 387,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x4000, WS_CARTBOOST,Cart Boost -//388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0,0x0, WS_SYSTEMCREATE,Auto Attack System +388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0,0x0, WS_SYSTEMCREATE,Auto Attack System //**** // Stalker @@ -768,6 +768,11 @@ 543,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0,0x0, NJ_NEN,Soul 544,-5,8,1,0,0x40,0,10,1,no,0,0,0,misc,0,0x0, NJ_ISSEN,Final Strike +572,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0, SL_DEATHKNIGHT,SL_DEATHKNIGHT +573,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0, SL_COLLECTOR,SL_COLLECTOR +574,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0, SL_NINJA,SL_NINJA +575,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0, SL_GUNNER,SL_GUNNER + //**** // Additional NPC Skills (Episode 11.3) 653,0,8,4,0,0x6,5:7:9:11:13:5:7:9:11:13,10,1,no,0,0x2,0,magic,0,0x0, NPC_EARTHQUAKE,Earthquake @@ -818,10 +823,10 @@ 689,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0,0x0, CASH_BLESSING,Party Blessing 690,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0,0x0, CASH_INCAGI,Party Increase AGI 691,0,6,4,0,0x3,-1,5,1,yes,0,0x2,0,magic,0,0x0, CASH_ASSUMPTIO,Party Assumptio -//692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_CATCRY,Cat Cry +692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_CATCRY,Cat Cry 693,0,6,4,0,0x3,-1,1,1,yes,0,0x2,0,magic,0,0x0, ALL_PARTYFLEE,Party Flee -//694,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_ANGEL_PROTECT,Angel's Protection -//695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_DREAM_SUMMERNIGHT,Summer Night Dream +694,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_ANGEL_PROTECT,Angel's Protection +695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, ALL_DREAM_SUMMERNIGHT,Summer Night Dream //696,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0, NPC_CHANGEUNDEAD2,Change Undead 697,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0,0x0, ALL_REVERSEORCISH,Reverse Orcish 698,0,6,4,0,0x01,0,1,1,no,0,0x2,0,none,0,0x0, ALL_WEWISH,Christmas Carol diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 552755f325..07a2f71965 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -5411,9 +5411,15 @@ Flag 2 means that the level parameter is to be interpreted as a stackable additional bonus to the skill level. If the character did not have that skill previously, they will now at 0+the level given. -Flag 4 is the same as flag 1 in that it saves to the database. However, these skills +Flag 3 is the same as flag 1 in that it saves to the database. However, these skills are ignored when any action is taken that adjusts the skill tree (reset/job change). +Flag contants: + 0 - SKILL_PERM + 1 - SKILL_TEMP + 2 - SKILL_TEMPLEVEL + 3 - SKILL_PERM_GRANT + // This will permanently give the character Stone Throw (TF_THROWSTONE,152), at // level 1. skill 152,1,0; diff --git a/npc/merchants/buying_shops.txt b/npc/merchants/buying_shops.txt index 3da03e54cc..802be147c7 100644 --- a/npc/merchants/buying_shops.txt +++ b/npc/merchants/buying_shops.txt @@ -225,7 +225,7 @@ alberta_in,58,52,4 script Purchasing Team#Buying 59,{ mes "Okay, you're now approved to open the Bulk Buyer Shop."; set Zeny,Zeny-10000; getitem 6377,5; //Buy_Stall_Permit - skill "ALL_BUYING_STORE",1,4; + skill "ALL_BUYING_STORE",1,SKILL_PERM_GRANT; next; mes "[Mr. Hugh]"; mes "Currently, only normal items ^8C2121EXCEPT^000000 equipment, certain potions, and hand-crafted items can be purchased in bulk, but this can still be very beneficial to you, depending on how you use it."; diff --git a/npc/other/gympass.txt b/npc/other/gympass.txt index 078e5bc71d..b522012ecd 100644 --- a/npc/other/gympass.txt +++ b/npc/other/gympass.txt @@ -79,7 +79,7 @@ payon,173,141,4 script Ripped Cabus#GymPass 899,{ mes "training together like this."; delitem 7776,1; //Max_Weight_Up_Scroll set gympassmemory,.@add_carry; - skill "ALL_INCCARRY",.@add_carry,4; + skill "ALL_INCCARRY",.@add_carry,SKILL_PERM_GRANT; close; } else { @@ -135,7 +135,7 @@ payon,173,141,4 script Ripped Cabus#GymPass 899,{ mes "muscles grew back,"; mes "just like that! Try not to"; mes "wimp out again, okay?"; - skill "ALL_INCCARRY",gympassmemory,4; + skill "ALL_INCCARRY",gympassmemory,SKILL_PERM_GRANT; close; } else { diff --git a/sql-files/upgrades/upgrade_20150131_skillset.sql b/sql-files/upgrades/upgrade_20150131_skillset.sql new file mode 100644 index 0000000000..e60ea411aa --- /dev/null +++ b/sql-files/upgrades/upgrade_20150131_skillset.sql @@ -0,0 +1,6 @@ +-- Resetting all `lv` of skills that has `flag` >= 3 (the skill that its `learned_lv` has been changed by script or special case by `learned_lv` + SKILL_FLAG_REPLACED_LV_0) +-- If there's ALL_INCCARRY and ALL_BUYING_STORE, set the `flag` to SKILL_FLAG_PERM_GRANTED (new value is 3), those are exclusive skills given in our official scripts! + +UPDATE `skill` SET `lv` = `flag` - 3 WHERE `flag` >= 3; +UPDATE `skill` SET `flag` = 0 WHERE `flag` >= 3; +UPDATE `skill` SET `flag` = 3 WHERE `id` = 681 OR `id` = 2535; diff --git a/src/char/char.c b/src/char/char.c index 3b04ffc990..1ad603ff65 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -434,12 +434,6 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){ strcat(save_status, " memo"); } - //FIXME: is this neccessary? [ultramage] - for(i=0;iskill[i].lv != 0) && (p->skill[i].id == 0)) - p->skill[i].id = i; // Fix skill tree - - //skills if( memcmp(p->skill, cp->skill, sizeof(p->skill)) ) { @@ -944,7 +938,6 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) { //===================================================================================================== int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_everything) { int i,j; - char t_msg[128] = ""; struct mmo_charstatus* cp; StringBuf buf; SqlStmt* stmt; @@ -954,11 +947,13 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev struct point tmp_point; struct item tmp_item; struct s_skill tmp_skill; + uint16 skill_count = 0; struct s_friend tmp_friend; #ifdef HOTKEY_SAVING struct hotkey tmp_hotkey; int hotkey_num; #endif + StringBuf msg_buf; memset(p, 0, sizeof(struct mmo_charstatus)); @@ -1065,11 +1060,13 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev p->save_point.y = MAP_DEFAULT_Y; } - strcat(t_msg, " status"); + StringBuf_Init(&msg_buf); + StringBuf_AppendStr(&msg_buf, " status"); if (!load_everything) // For quick selection of data when displaying the char menu { SqlStmt_Free(stmt); + StringBuf_Destroy(&msg_buf); return 1; } @@ -1088,7 +1085,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev tmp_point.map = mapindex_name2id(point_map); memcpy(&p->memo_point[i], &tmp_point, sizeof(tmp_point)); } - strcat(t_msg, " memo"); + StringBuf_AppendStr(&msg_buf, " memo"); //read inventory //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `unique_id`) @@ -1120,7 +1117,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i ) memcpy(&p->inventory[i], &tmp_item, sizeof(tmp_item)); - strcat(t_msg, " inventory"); + StringBuf_AppendStr(&msg_buf, " inventory"); //read cart //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, expire_time`, `unique_id`) @@ -1150,33 +1147,34 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev for( i = 0; i < MAX_CART && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i ) memcpy(&p->cart[i], &tmp_item, sizeof(tmp_item)); - strcat(t_msg, " cart"); + StringBuf_AppendStr(&msg_buf, " cart"); //read storage storage_fromsql(p->account_id, &p->storage); - strcat(t_msg, " storage"); + StringBuf_AppendStr(&msg_buf, " storage"); //read skill //`skill` (`char_id`, `id`, `lv`) if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `id`, `lv`,`flag` FROM `%s` WHERE `char_id`=? LIMIT %d", schema_config.skill_db, MAX_SKILL) - || SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0) - || SQL_ERROR == SqlStmt_Execute(stmt) - || SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_USHORT, &tmp_skill.id , 0, NULL, NULL) - || SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_UCHAR , &tmp_skill.lv , 0, NULL, NULL) - || SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_UCHAR , &tmp_skill.flag, 0, NULL, NULL) ) + || SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0) + || SQL_ERROR == SqlStmt_Execute(stmt) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_UINT16, &tmp_skill.id , 0, NULL, NULL) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_UINT8 , &tmp_skill.lv , 0, NULL, NULL) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_UINT8 , &tmp_skill.flag, 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); if( tmp_skill.flag != SKILL_FLAG_PERM_GRANTED ) tmp_skill.flag = SKILL_FLAG_PERMANENT; - for( i = 0; i < MAX_SKILL && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i ) - { - if( tmp_skill.id < ARRAYLENGTH(p->skill) ) - memcpy(&p->skill[tmp_skill.id], &tmp_skill, sizeof(tmp_skill)); + for( i = 0; skill_count < MAX_SKILL && SQL_SUCCESS == SqlStmt_NextRow(stmt); i++ ) { + if( tmp_skill.id > 0 && tmp_skill.id < MAX_SKILL_ID ) { + memcpy(&p->skill[i], &tmp_skill, sizeof(tmp_skill)); + skill_count++; + } else ShowWarning("mmo_char_fromsql: ignoring invalid skill (id=%u,lv=%u) of character %s (AID=%d,CID=%d)\n", tmp_skill.id, tmp_skill.lv, p->name, p->account_id, p->char_id); } - strcat(t_msg, " skills"); + StringBuf_Printf(&msg_buf, " %d skills", skill_count); //read friends //`friends` (`char_id`, `friend_account`, `friend_id`) @@ -1190,7 +1188,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev for( i = 0; i < MAX_FRIENDS && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i ) memcpy(&p->friends[i], &tmp_friend, sizeof(tmp_friend)); - strcat(t_msg, " friends"); + StringBuf_AppendStr(&msg_buf, " friends"); #ifdef HOTKEY_SAVING //read hotkeys @@ -1211,20 +1209,21 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev else ShowWarning("mmo_char_fromsql: ignoring invalid hotkey (hotkey=%d,type=%u,id=%u,lv=%u) of character %s (AID=%d,CID=%d)\n", hotkey_num, tmp_hotkey.type, tmp_hotkey.id, tmp_hotkey.lv, p->name, p->account_id, p->char_id); } - strcat(t_msg, " hotkeys"); + StringBuf_AppendStr(&msg_buf, " hotkeys"); #endif /* Mercenary Owner DataBase */ mercenary_owner_fromsql(char_id, p); - strcat(t_msg, " mercenary"); + StringBuf_AppendStr(&msg_buf, " mercenary"); - if (charserv_config.save_log) ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfuly! + if (charserv_config.save_log) ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, StringBuf_Value(&msg_buf)); //ok. all data load successfuly! SqlStmt_Free(stmt); StringBuf_Destroy(&buf); cp = idb_ensure(char_db_, char_id, char_create_charstatus); memcpy(cp, p, sizeof(struct mmo_charstatus)); + StringBuf_Destroy(&msg_buf); return 1; } diff --git a/src/common/mmo.h b/src/common/mmo.h index 375e86dd5a..4f77068cd3 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -54,7 +54,7 @@ #define MAX_BANK_ZENY SINT32_MAX ///Max zeny in Bank #define MAX_FAME 1000000000 ///Max fame points #define MAX_CART 100 ///Maximum item in cart -#define MAX_SKILL 5020 ///Maximum skill data +#define MAX_SKILL 1200 ///Maximum skill can be hold by Player, Homunculus, & Mercenary (skill list) AND skill_db limit #define GLOBAL_REG_NUM 256 ///Max permanent character variables per char #define ACCOUNT_REG_NUM 64 ///Max permanent local account variables per account #define ACCOUNT_REG2_NUM 16 ///Max permanent global account variables per account @@ -223,10 +223,11 @@ enum e_skill_flag SKILL_FLAG_PERMANENT, SKILL_FLAG_TEMPORARY, SKILL_FLAG_PLAGIARIZED, - SKILL_FLAG_REPLACED_LV_0, // temporary skill overshadowing permanent skill of level 'N - SKILL_FLAG_REPLACED_LV_0', - SKILL_FLAG_PERM_GRANTED, // permanent, granted through someway e.g. script - SKILL_FLAG_TMP_COMBO, //@FIXME for homon combo atm - //... + SKILL_FLAG_PERM_GRANTED, // Permanent, granted through someway e.g. script + SKILL_FLAG_TMP_COMBO, //@FIXME for homunculus combo atm + + //! NOTE: This flag be the last flag, and don't change the value if not needed! + SKILL_FLAG_REPLACED_LV_0 = 10, // temporary skill overshadowing permanent skill of level 'N - SKILL_FLAG_REPLACED_LV_0', }; enum e_mmo_charstatus_opt { @@ -236,9 +237,9 @@ enum e_mmo_charstatus_opt { }; struct s_skill { - unsigned short id; - unsigned char lv; - unsigned char flag; // see enum e_skill_flag + uint16 id; + uint8 lv; + uint8 flag; // see enum e_skill_flag }; struct global_reg { @@ -624,6 +625,7 @@ enum e_guild_skill { GD_MAX, }; +#define MAX_SKILL_ID GD_MAX //These mark the ID of the jobs, as expected by the client. [Skotlex] enum e_job { diff --git a/src/map/atcommand.c b/src/map/atcommand.c index bd87d013d3..9e6ae1eee2 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -3250,7 +3250,7 @@ ACMD_FUNC(questskill) return -1; } - if (skill_id >= MAX_SKILL_DB) { + if (skill_id >= MAX_SKILL_ID) { clif_displaymessage(fd, msg_txt(sd,198)); // This skill number doesn't exist. return -1; } @@ -3263,7 +3263,7 @@ ACMD_FUNC(questskill) return -1; } - pc_skill(sd, skill_id, 1, 0); + pc_skill(sd, skill_id, 1, ADDSKILL_PERMANENT); clif_displaymessage(fd, msg_txt(sd,70)); // You have learned the skill. return 0; @@ -3274,7 +3274,7 @@ ACMD_FUNC(questskill) *------------------------------------------*/ ACMD_FUNC(lostskill) { - uint16 skill_id; + uint16 skill_id = 0, sk_idx = 0; nullpo_retr(-1, sd); if (!message || !*message || (skill_id = atoi(message)) <= 0) @@ -3294,7 +3294,7 @@ ACMD_FUNC(lostskill) return -1; } - if (skill_id >= MAX_SKILL) { + if (!(sk_idx = skill_get_index(skill_id))) { clif_displaymessage(fd, msg_txt(sd,198)); // This skill number doesn't exist. return -1; } @@ -3307,8 +3307,8 @@ ACMD_FUNC(lostskill) return -1; } - sd->status.skill[skill_id].lv = 0; - sd->status.skill[skill_id].flag = SKILL_FLAG_PERMANENT; + sd->status.skill[sk_idx].lv = 0; + sd->status.skill[sk_idx].flag = SKILL_FLAG_PERMANENT; clif_deleteskill(sd,skill_id); clif_displaymessage(fd, msg_txt(sd,71)); // You have forgotten the skill. @@ -5507,11 +5507,11 @@ ACMD_FUNC(skillid) { for( data = iter->first(iter,&key); iter->exists(iter); data = iter->next(iter,&key) ) { int idx = skill_get_index(db_data2i(data)); - if (strnicmp(key.str, message, skillen) == 0 || strnicmp(skill_db[idx].desc, message, skillen) == 0) { - sprintf(atcmd_output, msg_txt(sd,1164), db_data2i(data), skill_db[idx].desc, key.str); // skill %d: %s (%s) + if (strnicmp(key.str, message, skillen) == 0 || strnicmp(skill_db[idx]->desc, message, skillen) == 0) { + sprintf(atcmd_output, msg_txt(sd,1164), db_data2i(data), skill_db[idx]->desc, key.str); // skill %d: %s (%s) clif_displaymessage(fd, atcmd_output); - } else if ( found < MAX_SKILLID_PARTIAL_RESULTS && ( stristr(key.str,message) || stristr(skill_db[idx].desc,message) ) ) { - snprintf(partials[found++], MAX_SKILLID_PARTIAL_RESULTS_LEN, msg_txt(sd,1164), db_data2i(data), skill_db[idx].desc, key.str); + } else if ( found < MAX_SKILLID_PARTIAL_RESULTS && ( stristr(key.str,message) || stristr(skill_db[idx]->desc,message) ) ) { + snprintf(partials[found++], MAX_SKILLID_PARTIAL_RESULTS_LEN, msg_txt(sd,1164), db_data2i(data), skill_db[idx]->desc, key.str); } } @@ -5559,7 +5559,7 @@ ACMD_FUNC(useskill) return -1; } - if (skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE+MAX_HOMUNSKILL + if (SKILL_CHK_HOMUN(skill_id) && sd->hd && hom_is_active(sd->hd)) // (If used with @useskill, put the homunc as dest) bl = &sd->hd->bl; else @@ -5643,7 +5643,7 @@ ACMD_FUNC(skilltree) { if( ent->need[j].id && pc_checkskill(sd,ent->need[j].id) < ent->need[j].lv) { - sprintf(atcmd_output, msg_txt(sd,1170), ent->need[j].lv, skill_db[ent->need[j].id].desc); // Player requires level %d of skill %s. + sprintf(atcmd_output, msg_txt(sd,1170), ent->need[j].lv, skill_db[ent->need[j].id]->desc); // Player requires level %d of skill %s. clif_displaymessage(fd, atcmd_output); meets = 0; } @@ -9118,13 +9118,14 @@ ACMD_FUNC(unloadnpcfile) { return 0; } ACMD_FUNC(cart) { -#define MC_CART_MDFY(x) \ - sd->status.skill[MC_PUSHCART].id = x?MC_PUSHCART:0; \ - sd->status.skill[MC_PUSHCART].lv = x?1:0; \ - sd->status.skill[MC_PUSHCART].flag = x?SKILL_FLAG_TEMPORARY:SKILL_FLAG_PERMANENT; +#define MC_CART_MDFY(idx, x) \ + sd->status.skill[(idx)].id = x?MC_PUSHCART:0; \ + sd->status.skill[(idx)].lv = x?1:0; \ + sd->status.skill[(idx)].flag = x?SKILL_FLAG_TEMPORARY:SKILL_FLAG_PERMANENT; int val = atoi(message); bool need_skill = (pc_checkskill(sd, MC_PUSHCART) == 0); + uint16 sk_idx = 0; if( !message || !*message || val < 0 || val > MAX_CARTS ) { sprintf(atcmd_output, msg_txt(sd,1390),command,MAX_CARTS); // Unknown Cart (usage: %s <0-%d>). @@ -9137,19 +9138,22 @@ ACMD_FUNC(cart) { return -1; } + if (!(sk_idx = skill_get_index(MC_PUSHCART))) + return -1; + if( need_skill ) { - MC_CART_MDFY(1); + MC_CART_MDFY(sk_idx,1); } if( !pc_setcart(sd, val) ) { if( need_skill ) { - MC_CART_MDFY(0); + MC_CART_MDFY(sk_idx,0); } return -1;/* @cart failed */ } if( need_skill ) { - MC_CART_MDFY(0); + MC_CART_MDFY(sk_idx,0); } clif_displaymessage(fd, msg_txt(sd,1392)); // Cart Added diff --git a/src/map/battle.c b/src/map/battle.c index 084d9608ef..af3ab24d64 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -1883,10 +1883,10 @@ static int battle_skill_damage_skill(struct block_list *src, struct block_list * struct s_skill_damage *damage = NULL; struct map_data *mapd = &map[m]; - if (!idx || !skill_db[idx].damage.map) + if (!idx || !skill_db[idx]->damage.map) return 0; - damage = &skill_db[idx].damage; + damage = &skill_db[idx]->damage; //check the adjustment works for specified type if (!battle_skill_damage_iscaster(damage->caster, src->type)) @@ -6945,11 +6945,12 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t } } if (sd) { + uint16 r_skill = 0, sk_idx = 0; if( wd.flag&BF_SHORT && sc && sc->data[SC__AUTOSHADOWSPELL] && rnd()%100 < sc->data[SC__AUTOSHADOWSPELL]->val3 && - sd->status.skill[sc->data[SC__AUTOSHADOWSPELL]->val1].id != 0 && sd->status.skill[sc->data[SC__AUTOSHADOWSPELL]->val1].flag == SKILL_FLAG_PLAGIARIZED ) + (r_skill = (uint16)sc->data[SC__AUTOSHADOWSPELL]->val1) && (sk_idx = skill_get_index(r_skill)) && + sd->status.skill[sk_idx].id != 0 && sd->status.skill[sk_idx].flag == SKILL_FLAG_PLAGIARIZED ) { - int r_skill = sd->status.skill[sc->data[SC__AUTOSHADOWSPELL]->val1].id, - r_lv = sc->data[SC__AUTOSHADOWSPELL]->val2; + int r_lv = sc->data[SC__AUTOSHADOWSPELL]->val2; if (r_skill != AL_HOLYLIGHT && r_skill != PR_MAGNUS) { int type; @@ -7789,7 +7790,7 @@ static const struct _battle_data { { "show_hp_sp_gain", &battle_config.show_hp_sp_gain, 1, 0, 1, }, { "mob_npc_event_type", &battle_config.mob_npc_event_type, 1, 0, 1, }, { "character_size", &battle_config.character_size, 1|2, 0, 1|2, }, - { "mob_max_skilllvl", &battle_config.mob_max_skilllvl, MAX_SKILL_LEVEL, 1, MAX_SKILL_LEVEL, }, + { "mob_max_skilllvl", &battle_config.mob_max_skilllvl, MAX_MOBSKILL_LEVEL, 1, MAX_MOBSKILL_LEVEL, }, { "retaliate_to_master", &battle_config.retaliate_to_master, 1, 0, 1, }, { "rare_drop_announce", &battle_config.rare_drop_announce, 0, 0, 10000, }, { "duel_allow_pvp", &battle_config.duel_allow_pvp, 0, 0, 1, }, diff --git a/src/map/chrif.c b/src/map/chrif.c index 4810b7f524..e60dbfb729 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -951,19 +951,21 @@ int chrif_changedsex(int fd) { if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) { int i; // remove specifical skills of Bard classes - for(i = 315; i <= 322; i++) { - if (sd->status.skill[i].id > 0 && sd->status.skill[i].flag == SKILL_FLAG_PERMANENT) { - sd->status.skill_point += sd->status.skill[i].lv; - sd->status.skill[i].id = 0; - sd->status.skill[i].lv = 0; + for(i = BA_MUSICALLESSON; i <= BA_APPLEIDUN; i++) { + uint16 sk_idx = skill_get_index(i); + if (sd->status.skill[sk_idx].id > 0 && sd->status.skill[sk_idx].flag == SKILL_FLAG_PERMANENT) { + sd->status.skill_point += sd->status.skill[sk_idx].lv; + sd->status.skill[sk_idx].id = 0; + sd->status.skill[sk_idx].lv = 0; } } // remove specifical skills of Dancer classes - for(i = 323; i <= 330; i++) { - if (sd->status.skill[i].id > 0 && sd->status.skill[i].flag == SKILL_FLAG_PERMANENT) { - sd->status.skill_point += sd->status.skill[i].lv; - sd->status.skill[i].id = 0; - sd->status.skill[i].lv = 0; + for(i = DC_DANCINGLESSON; i <= DC_SERVICEFORYOU; i++) { + uint16 sk_idx = skill_get_index(i); + if (sd->status.skill[sk_idx].id > 0 && sd->status.skill[sk_idx].flag == SKILL_FLAG_PERMANENT) { + sd->status.skill_point += sd->status.skill[sk_idx].lv; + sd->status.skill[sk_idx].id = 0; + sd->status.skill[sk_idx].lv = 0; } } clif_updatestatus(sd, SP_SKILLPOINT); @@ -1030,7 +1032,7 @@ int chrif_divorceack(uint32 char_id, int partner_id) { *------------------------------------------*/ int chrif_deadopt(int father_id, int mother_id, int child_id) { struct map_session_data* sd; - int idx = skill_get_index(WE_CALLBABY); + uint16 idx = skill_get_index(WE_CALLBABY); if( father_id && ( sd = map_charid2sd(father_id) ) != NULL && sd->status.child == child_id ) { sd->status.child = 0; diff --git a/src/map/clif.c b/src/map/clif.c index a44138e6a5..928e5359d4 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1486,7 +1486,7 @@ int clif_homskillinfoblock(struct map_session_data *sd) { //[orn] struct homun_data *hd; int fd = sd->fd; - int i,j,len=4; + int i, len=4; WFIFOHEAD(fd, 4+37*MAX_HOMUNSKILL); hd = sd->hd; @@ -1498,15 +1498,17 @@ int clif_homskillinfoblock(struct map_session_data *sd) int id = hd->homunculus.hskill[i].id; if( id != 0 ){ int combo = (hd->homunculus.hskill[i].flag)&SKILL_FLAG_TMP_COMBO; - j = id - HM_SKILLBASE; + short idx = hom_skill_get_index(id); + if (idx == -1) + continue; WFIFOW(fd,len ) = id; WFIFOW(fd,len+2) = ((combo)?INF_SELF_SKILL:skill_get_inf(id)); WFIFOW(fd,len+4) = 0; - WFIFOW(fd,len+6) = hd->homunculus.hskill[j].lv; - WFIFOW(fd,len+8) = skill_get_sp(id,hd->homunculus.hskill[j].lv); - WFIFOW(fd,len+10)= skill_get_range2(&sd->hd->bl, id,hd->homunculus.hskill[j].lv); + WFIFOW(fd,len+6) = hd->homunculus.hskill[idx].lv; + WFIFOW(fd,len+8) = skill_get_sp(id,hd->homunculus.hskill[idx].lv); + WFIFOW(fd,len+10)= skill_get_range2(&sd->hd->bl, id,hd->homunculus.hskill[idx].lv); safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH); - WFIFOB(fd,len+36) = (hd->homunculus.hskill[j].lv < hom_skill_tree_get_max(id, hd->homunculus.class_))?1:0; + WFIFOB(fd,len+36) = (hd->homunculus.hskill[idx].lv < hom_skill_tree_get_max(id, hd->homunculus.class_))?1:0; len+=37; } } @@ -1519,12 +1521,15 @@ int clif_homskillinfoblock(struct map_session_data *sd) void clif_homskillup(struct map_session_data *sd, uint16 skill_id) { //[orn] struct homun_data *hd; - int fd, idx; + int fd; + short idx = -1; nullpo_retv(sd); - idx = skill_id - HM_SKILLBASE; - fd=sd->fd; - hd=sd->hd; + if ((idx = hom_skill_get_index(skill_id) == -1)) + return; + + fd = sd->fd; + hd = sd->hd; WFIFOHEAD(fd, packet_len(0x239)); WFIFOW(fd,0) = 0x239; @@ -4801,8 +4806,9 @@ void clif_skillinfoblock(struct map_session_data *sd) nullpo_retv(sd); - fd=sd->fd; - if (!fd) return; + fd = sd->fd; + if (!fd) + return; WFIFOHEAD(fd, MAX_SKILL * 37 + 4); WFIFOW(fd,0) = 0x10f; @@ -4846,28 +4852,30 @@ void clif_skillinfoblock(struct map_session_data *sd) /// Adds new skill to the skill tree (ZC_ADD_SKILL). /// 0111 .W .L .W .W .W .24B .B -void clif_addskill(struct map_session_data *sd, int id) +void clif_addskill(struct map_session_data *sd, int skill_id) { int fd; + uint16 idx = 0; nullpo_retv(sd); fd = sd->fd; - if (!fd) return; + if (!fd || !(idx = skill_get_index(skill_id))) + return; - if( sd->status.skill[id].id <= 0 ) + if( sd->status.skill[idx].id <= 0 ) return; WFIFOHEAD(fd, packet_len(0x111)); WFIFOW(fd,0) = 0x111; - WFIFOW(fd,2) = id; - WFIFOL(fd,4) = skill_get_inf(id); - WFIFOW(fd,8) = sd->status.skill[id].lv; - WFIFOW(fd,10) = skill_get_sp(id,sd->status.skill[id].lv); - WFIFOW(fd,12)= skill_get_range2(&sd->bl, id,sd->status.skill[id].lv); - safestrncpy((char*)WFIFOP(fd,14), skill_get_name(id), NAME_LENGTH); - if( sd->status.skill[id].flag == SKILL_FLAG_PERMANENT ) - WFIFOB(fd,38) = (sd->status.skill[id].lv < skill_tree_get_max(id, sd->status.class_))? 1:0; + WFIFOW(fd,2) = skill_id; + WFIFOL(fd,4) = skill_get_inf(skill_id); + WFIFOW(fd,8) = sd->status.skill[idx].lv; + WFIFOW(fd,10) = skill_get_sp(skill_id,sd->status.skill[idx].lv); + WFIFOW(fd,12)= skill_get_range2(&sd->bl, skill_id,sd->status.skill[idx].lv); + safestrncpy((char*)WFIFOP(fd,14), skill_get_name(skill_id), NAME_LENGTH); + if( sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT ) + WFIFOB(fd,38) = (sd->status.skill[idx].lv < skill_tree_get_max(skill_id, sd->status.class_))? 1:0; else WFIFOB(fd,38) = 0; WFIFOSET(fd,packet_len(0x111)); @@ -4876,18 +4884,21 @@ void clif_addskill(struct map_session_data *sd, int id) /// Deletes a skill from the skill tree (ZC_SKILLINFO_DELETE). /// 0441 .W -void clif_deleteskill(struct map_session_data *sd, int id) +void clif_deleteskill(struct map_session_data *sd, int skill_id) { #if PACKETVER >= 20081217 int fd; + uint16 idx = 0; nullpo_retv(sd); + fd = sd->fd; - if( !fd ) return; + if (!fd || !(idx = skill_get_index(skill_id))) + return; WFIFOHEAD(fd,packet_len(0x441)); WFIFOW(fd,0) = 0x441; - WFIFOW(fd,2) = id; + WFIFOW(fd,2) = skill_id; WFIFOSET(fd,packet_len(0x441)); #endif clif_skillinfoblock(sd); @@ -4896,7 +4907,7 @@ void clif_deleteskill(struct map_session_data *sd, int id) /// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE). /// 010e .W .W .W .W .B void clif_skillup(struct map_session_data *sd, uint16 skill_id, int lv, int range, int upgradable) { - int fd; + int fd; nullpo_retv(sd); @@ -4914,19 +4925,22 @@ void clif_skillup(struct map_session_data *sd, uint16 skill_id, int lv, int rang /// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE2). /// 07e1 .W .L .W .W .W .B -void clif_skillinfo(struct map_session_data *sd,int skill, int inf) +void clif_skillinfo(struct map_session_data *sd,int skill_id, int inf) { const int fd = sd->fd; + uint16 idx = skill_get_index(skill_id); + if (!idx) + return; WFIFOHEAD(fd,packet_len(0x7e1)); WFIFOW(fd,0) = 0x7e1; - WFIFOW(fd,2) = skill; - WFIFOL(fd,4) = inf?inf:skill_get_inf(skill); - WFIFOW(fd,8) = sd->status.skill[skill].lv; - WFIFOW(fd,10) = skill_get_sp(skill,sd->status.skill[skill].lv); - WFIFOW(fd,12) = skill_get_range2(&sd->bl,skill,sd->status.skill[skill].lv); - if( sd->status.skill[skill].flag == SKILL_FLAG_PERMANENT ) - WFIFOB(fd,14) = (sd->status.skill[skill].lv < skill_tree_get_max(skill, sd->status.class_))? 1:0; + WFIFOW(fd,2) = skill_id; + WFIFOL(fd,4) = inf?inf:skill_get_inf(skill_id); + WFIFOW(fd,8) = sd->status.skill[idx].lv; + WFIFOW(fd,10) = skill_get_sp(skill_id,sd->status.skill[idx].lv); + WFIFOW(fd,12) = skill_get_range2(&sd->bl,skill_id,sd->status.skill[idx].lv); + if( sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT ) + WFIFOB(fd,14) = (sd->status.skill[idx].lv < skill_tree_get_max(skill_id, sd->status.class_))? 1:0; else WFIFOB(fd,14) = 0; WFIFOSET(fd,packet_len(0x7e1)); @@ -11350,12 +11364,12 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) if (tmp&INF_GROUND_SKILL || !tmp) return; //Using a ground/passive skill on a target? WRONG. - if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL ) { + if( SKILL_CHK_HOMUN(skill_id) ) { clif_parse_UseSkillToId_homun(sd->hd, sd, tick, skill_id, skill_lv, target_id); return; } - if( skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL ) { + if( SKILL_CHK_MERC(skill_id) ) { clif_parse_UseSkillToId_mercenary(sd->md, sd, tick, skill_id, skill_lv, target_id); return; } @@ -11424,7 +11438,7 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) sd->skillitem = sd->skillitemlv = 0; - if( skill_id >= GD_SKILLBASE ) { + if( SKILL_CHK_GUILD(skill_id) ) { if( sd->state.gmaster_flag ) skill_lv = guild_checkskill(sd->guild, skill_id); else @@ -11451,12 +11465,12 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uin if( !(skill_get_inf(skill_id)&INF_GROUND_SKILL) ) return; //Using a target skill on the ground? WRONG. - if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL ) { + if( SKILL_CHK_HOMUN(skill_id) ) { clif_parse_UseSkillToPos_homun(sd->hd, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo); return; } - if( skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL ) { + if( SKILL_CHK_MERC(skill_id) ) { clif_parse_UseSkillToPos_mercenary(sd->md, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo); return; } @@ -15665,15 +15679,18 @@ void clif_mercenary_skillblock(struct map_session_data *sd) WFIFOW(fd,0) = 0x29d; for( i = 0; i < MAX_MERCSKILL; i++ ) { - int id, j; + uint16 id; + short idx = -1; if( (id = md->db->skill[i].id) == 0 ) continue; - j = id - MC_SKILLBASE; + if ((idx = mercenary_skill_get_index(id)) == -1) + continue; + WFIFOW(fd,len) = id; WFIFOL(fd,len+2) = skill_get_inf(id); - WFIFOW(fd,len+6) = md->db->skill[j].lv; - WFIFOW(fd,len+8) = skill_get_sp(id, md->db->skill[j].lv); - WFIFOW(fd,len+10) = skill_get_range2(&md->bl, id, md->db->skill[j].lv); + WFIFOW(fd,len+6) = md->db->skill[idx].lv; + WFIFOW(fd,len+8) = skill_get_sp(id, md->db->skill[idx].lv); + WFIFOW(fd,len+10) = skill_get_range2(&md->bl, id, md->db->skill[idx].lv); safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH); WFIFOB(fd,len+36) = 0; // Skillable for Mercenary? len += 37; diff --git a/src/map/clif.h b/src/map/clif.h index 1c46152683..4b40d56901 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -532,9 +532,9 @@ void clif_class_change(struct block_list *bl,int class_,int type); void clif_skillinfoblock(struct map_session_data *sd); void clif_skillup(struct map_session_data *sd, uint16 skill_id, int lv, int range, int upgradable); -void clif_skillinfo(struct map_session_data *sd,int skill, int inf); -void clif_addskill(struct map_session_data *sd, int id); -void clif_deleteskill(struct map_session_data *sd, int id); +void clif_skillinfo(struct map_session_data *sd,int skill_id, int inf); +void clif_addskill(struct map_session_data *sd, int skill_id); +void clif_deleteskill(struct map_session_data *sd, int skill_id); void clif_skillcasting(struct block_list* bl, int src_id, int dst_id, int dst_x, int dst_y, uint16 skill_id, int property, int casttime); void clif_skillcastcancel(struct block_list* bl); diff --git a/src/map/elemental.c b/src/map/elemental.c index 5d7017ec5d..643dd456aa 100644 --- a/src/map/elemental.c +++ b/src/map/elemental.c @@ -9,6 +9,7 @@ #include "../common/showmsg.h" #include "../common/random.h" #include "../common/strlib.h" +#include "../common/utils.h" #include "log.h" #include "clif.h" @@ -558,13 +559,12 @@ struct skill_condition elemental_skill_get_requirements(uint16 skill_id, uint16 memset(&req,0,sizeof(req)); if( idx == 0 ) // invalid skill id - return req; - - if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL ) return req; - req.hp = skill_db[idx].require.hp[skill_lv-1]; - req.sp = skill_db[idx].require.sp[skill_lv-1]; + skill_lv = cap_value(skill_lv, 1, MAX_SKILL_LEVEL); + + req.hp = skill_db[idx]->require.hp[skill_lv-1]; + req.sp = skill_db[idx]->require.sp[skill_lv-1]; return req; } @@ -815,6 +815,7 @@ void read_elementaldb(void) { uint8 i; elemental_count = 0; + memset(elemental_db, 0, sizeof(elemental_db)); for(i = 0; i= EL_SKILLBASE + MAX_ELEMENTALSKILL ) { - ShowError("read_elemental_skilldb_sub: Skill out of range, line %d.\n", current); + if( !SKILL_CHK_ELEM(skill_id) ) { + ShowError("read_elemental_skilldb_sub: Invalid Elemental skill '%d'.\n", skill_id); return false; } diff --git a/src/map/guild.c b/src/map/guild.c index 5e0b8c8cab..4ac3a0df1c 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -64,6 +64,20 @@ static int guild_send_xy_timer(int tid, unsigned int tick, int id, intptr_t data struct npc_data **guild_flags; unsigned short guild_flags_count; +/** + * Get guild skill index in guild_skill_tree + * @param skill_id + * @return Index in skill_tree or -1 + **/ +static short guild_skill_get_index(uint16 skill_id) { + if (!SKILL_CHK_GUILD(skill_id)) + return -1; + skill_id -= GD_SKILLBASE; + if (skill_id >= MAX_GUILDSKILL) + return -1; + return skill_id; +} + /*========================================== * Retrieves and validates the sd pointer for this guild member [Skotlex] *------------------------------------------*/ @@ -84,44 +98,41 @@ static TBL_PC* guild_sd_check(int guild_id, uint32 account_id, uint32 char_id) { // Modified [Komurka] int guild_skill_get_max (int id) { - if (id < GD_SKILLBASE || id >= GD_SKILLBASE+MAX_GUILDSKILL) + if ((id = guild_skill_get_index(id)) < 0) return 0; - return guild_skill_tree[id-GD_SKILLBASE].max; + return guild_skill_tree[id].max; } // Retrive skill_lv learned by guild int guild_checkskill(struct guild *g, int id) { - int idx = id - GD_SKILLBASE; - if (idx < 0 || idx >= MAX_GUILDSKILL) + if ((id = guild_skill_get_index(id)) < 0) return 0; - return g->skill[idx].lv; + return g->skill[id].lv; } /*========================================== * guild_skill_tree.txt reading - from jA [Komurka] *------------------------------------------*/ static bool guild_read_guildskill_tree_db(char* split[], int columns, int current) {// ,,,,,,,,,,, - int k, id, skill_id; + int k, skill_id = atoi(split[0]); + short idx = -1; - skill_id = atoi(split[0]); - id = skill_id - GD_SKILLBASE; - - if( id < 0 || id >= MAX_GUILDSKILL ) { - ShowWarning("guild_read_guildskill_tree_db: Invalid skill id %d.\n", skill_id); + if ((idx = guild_skill_get_index(skill_id)) < 0) { + ShowError("guild_read_guildskill_tree_db: Invalid Guild skill '%s'.\n", split[1]); return false; } - guild_skill_tree[id].id = skill_id; - guild_skill_tree[id].max = atoi(split[1]); + guild_skill_tree[idx].id = skill_id; + guild_skill_tree[idx].max = atoi(split[1]); - if( guild_skill_tree[id].id == GD_GLORYGUILD && battle_config.require_glory_guild && guild_skill_tree[id].max == 0 ) {// enable guild's glory when required for emblems - guild_skill_tree[id].max = 1; + if( guild_skill_tree[idx].id == GD_GLORYGUILD && battle_config.require_glory_guild && guild_skill_tree[idx].max == 0 ) {// enable guild's glory when required for emblems + guild_skill_tree[idx].max = 1; } for( k = 0; k < MAX_GUILD_SKILL_REQUIRE; k++ ) { - guild_skill_tree[id].need[k].id = atoi(split[k*2+2]); - guild_skill_tree[id].need[k].lv = atoi(split[k*2+3]); + guild_skill_tree[idx].need[k].id = atoi(split[k*2+2]); + guild_skill_tree[idx].need[k].lv = atoi(split[k*2+3]); } return true; @@ -131,13 +142,13 @@ static bool guild_read_guildskill_tree_db(char* split[], int columns, int curren * Guild skill check - from jA [Komurka] *------------------------------------------*/ int guild_check_skill_require(struct guild *g,int id) { - int i; - int idx = id-GD_SKILLBASE; + uint8 i; + short idx = -1; if(g == NULL) return 0; - if (idx < 0 || idx >= MAX_GUILDSKILL) + if ((idx = guild_skill_get_index(id)) < 0) return 0; for(i=0;i= MAX_GUILDSKILL || // not a guild skill - sd->status.guild_id == 0 || (g=sd->guild) == NULL || // no guild - strcmp(sd->status.name, g->master) ) // not the guild master - return 0; + if (idx == -1) + return; + + if( sd->status.guild_id == 0 || (g=sd->guild) == NULL || // no guild + strcmp(sd->status.name, g->master) ) // not the guild master + return; + + max = guild_skill_get_max(skill_id); if( g->skill_point > 0 && - g->skill[idx].id != 0 && - g->skill[idx].lv < max ) + g->skill[idx].id != 0 && + g->skill[idx].lv < max ) intif_guild_skillup(g->guild_id, skill_id, sd->status.account_id, max); - - return 0; } /*==================================================== * Notification of guildskill skill_id increase request *---------------------------------------------------*/ int guild_skillupack(int guild_id,uint16 skill_id,uint32 account_id) { - struct map_session_data *sd=map_id2sd(account_id); - struct guild *g=guild_search(guild_id); + struct map_session_data *sd = map_id2sd(account_id); + struct guild *g = guild_search(guild_id); int i; - if(g==NULL) + short idx = guild_skill_get_index(skill_id); + + if (g == NULL || idx == -1) return 0; - if( sd != NULL ) { - int lv = g->skill[skill_id-GD_SKILLBASE].lv; + if (sd != NULL) { + int lv = g->skill[idx].lv; int range = skill_get_range(skill_id, lv); clif_skillup(sd,skill_id,lv,range,1); @@ -1297,14 +1312,14 @@ int guild_skillupack(int guild_id,uint16 skill_id,uint32 account_id) { case GD_GLORYWOUNDS: case GD_SOULCOLD: case GD_HAWKEYES: - guild_guildaura_refresh(sd,skill_id,g->skill[skill_id-GD_SKILLBASE].lv); + guild_guildaura_refresh(sd,skill_id,g->skill[idx].lv); break; } } // Inform all members - for(i=0;imax_member;i++) - if((sd=g->member[i].sd)!=NULL) + for (i = 0; i < g->max_member; i++) + if ((sd = g->member[i].sd) != NULL) clif_guild_skillinfo(sd); return 0; diff --git a/src/map/guild.h b/src/map/guild.h index 36d8b93084..94b842551d 100644 --- a/src/map/guild.h +++ b/src/map/guild.h @@ -60,7 +60,7 @@ int guild_member_withdraw(int guild_id,uint32 account_id,uint32 char_id,int flag const char *name,const char *mes); int guild_expulsion(struct map_session_data *sd,int guild_id, uint32 account_id,uint32 char_id,const char *mes); -int guild_skillup(struct map_session_data* sd, uint16 skill_id); +void guild_skillup(struct map_session_data* sd, uint16 skill_id); void guild_block_skill(struct map_session_data *sd, int time); int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd); int guild_reply_reqalliance(struct map_session_data *sd,uint32 account_id,int flag); diff --git a/src/map/homunculus.c b/src/map/homunculus.c index 2577959fed..cb3947023f 100644 --- a/src/map/homunculus.c +++ b/src/map/homunculus.c @@ -38,10 +38,11 @@ static struct view_data hom_viewdb[MAX_HOMUNCULUS_CLASS]; * @param skill_id * @return -1 if invalid skill or skill index for homunculus skill_tree */ -static short hom_skill_get_index(int skill_id) { - if (!skill_get_index(skill_id)) +short hom_skill_get_index(uint16 skill_id) { + if (!SKILL_CHK_HOMUN(skill_id)) return -1; - if ((skill_id -= HM_SKILLBASE) < 0 || skill_id >= MAX_HOMUNSKILL) + skill_id -= HM_SKILLBASE; + if (skill_id >= MAX_HOMUNSKILL) return -1; return skill_id; } @@ -124,8 +125,8 @@ int hom_class2mapid(int hom_class) void hom_addspiritball(TBL_HOM *hd, int max) { nullpo_retv(hd); - if (max > MAX_SKILL_LEVEL) - max = MAX_SKILL_LEVEL; + if (max > MAX_SPIRITBALL) + max = MAX_SPIRITBALL; if (hd->homunculus.spiritball < 0) hd->homunculus.spiritball = 0; @@ -152,8 +153,8 @@ void hom_delspiritball(TBL_HOM *hd, int count, int type) { } if (count <= 0) return; - if (count > MAX_SKILL_LEVEL) - count = MAX_SKILL_LEVEL; + if (count > MAX_SPIRITBALL) + count = MAX_SPIRITBALL; if (count > hd->homunculus.spiritball) count = hd->homunculus.spiritball; @@ -268,7 +269,7 @@ void hom_calc_skilltree(struct homun_data *hd, int flag_evolve) /* load previous homunculus form skills first. */ if (hd->homunculus.prev_class != 0 && (c = hom_class2index(hd->homunculus.prev_class)) >= 0) { for (i = 0; i < MAX_SKILL_TREE && (skill_id = hskill_tree[c][i].id) > 0; i++) { - int idx = hom_skill_get_index(skill_id); + short idx = hom_skill_get_index(skill_id); if (idx < 0) continue; if (hd->homunculus.hskill[idx].id) @@ -296,7 +297,7 @@ void hom_calc_skilltree(struct homun_data *hd, int flag_evolve) for (i = 0; i < MAX_SKILL_TREE && (skill_id = hskill_tree[c][i].id) > 0; i++) { int intimacy; - int idx = hom_skill_get_index(skill_id); + short idx = hom_skill_get_index(skill_id); if (idx < 0) continue; if (hd->homunculus.hskill[idx].id) @@ -331,7 +332,7 @@ void hom_calc_skilltree(struct homun_data *hd, int flag_evolve) */ short hom_checkskill(struct homun_data *hd,uint16 skill_id) { - int idx = hom_skill_get_index(skill_id); + short idx = hom_skill_get_index(skill_id); if (idx < 0) // Invalid skill return 0; @@ -1460,8 +1461,9 @@ void read_homunculusdb(void) { */ static bool read_homunculus_skilldb_sub(char* split[], int columns, int current) {// ,,[,],,,,,,,,,,, - int skill_id, class_idx; - int i, j; + uint16 skill_id; + uint8 i; + short class_idx, idx = -1; int minJobLevelPresent = 0; if (columns == 14) @@ -1469,29 +1471,27 @@ static bool read_homunculus_skilldb_sub(char* split[], int columns, int current) // check for bounds [celest] if ((class_idx = hom_class2index(atoi(split[0]))) == -1) { - ShowWarning("read_homunculus_skilldb: Invalud homunculus class %d.\n", atoi(split[0])); + ShowWarning("read_homunculus_skilldb: Invalid homunculus class %d.\n", atoi(split[0])); return false; } - skill_id = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex] - // Search an empty line or a line with the same skill_id (stored in j) - ARR_FIND( 0, MAX_SKILL_TREE, j, !hskill_tree[class_idx][j].id || hskill_tree[class_idx][j].id == skill_id ); - if (j == MAX_SKILL_TREE) { - ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", skill_id, atoi(split[0])); + skill_id = atoi(split[1]); + if ((idx = hom_skill_get_index(skill_id)) == -1) { + ShowError("read_homunculus_skilldb: Invalid Homunculus skill '%s'.\n", split[1]); return false; } - hskill_tree[class_idx][j].id = skill_id; - hskill_tree[class_idx][j].max = atoi(split[2]); + hskill_tree[class_idx][idx].id = skill_id; + hskill_tree[class_idx][idx].max = atoi(split[2]); if (minJobLevelPresent) - hskill_tree[class_idx][j].joblv = atoi(split[3]); + hskill_tree[class_idx][idx].joblv = atoi(split[3]); for (i = 0; i < MAX_HOM_SKILL_REQUIRE; i++) { - hskill_tree[class_idx][j].need[i].id = atoi(split[3+i*2+minJobLevelPresent]); - hskill_tree[class_idx][j].need[i].lv = atoi(split[3+i*2+minJobLevelPresent+1]); + hskill_tree[class_idx][idx].need[i].id = atoi(split[3+i*2+minJobLevelPresent]); + hskill_tree[class_idx][idx].need[i].lv = atoi(split[3+i*2+minJobLevelPresent+1]); } - hskill_tree[class_idx][j].intimacylv = atoi(split[13+minJobLevelPresent]); + hskill_tree[class_idx][idx].intimacylv = atoi(split[13+minJobLevelPresent]); return true; } diff --git a/src/map/homunculus.h b/src/map/homunculus.h index f764dba5d6..fbdf5bd2f5 100644 --- a/src/map/homunculus.h +++ b/src/map/homunculus.h @@ -159,6 +159,8 @@ void hom_delspiritball(TBL_HOM *hd, int count, int type); uint8 hom_get_intimacy_grade(struct homun_data *hd); +short hom_skill_get_index(uint16 skill_id); + void do_final_homunculus(void); void do_init_homunculus(void); diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 4956786b60..882c97ee71 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -325,8 +325,12 @@ enum e_item_ammo AMMO_KUNAI, AMMO_CANNONBALL, AMMO_THROWABLE_ITEM, ///Sling items + + MAX_AMMO_TYPE, }; +#define AMMO_TYPE_ALL ((1<= MAX_MERCSKILL) + return -1; + return skill_id; +} + /** * Create a new Mercenary for Player * @param sd The Player @@ -445,14 +459,11 @@ void mercenary_kills(struct mercenary_data *md){ * @return Skill Level or 0 if Mercenary doesn't have the skill **/ int mercenary_checkskill(struct mercenary_data *md, uint16 skill_id) { - int i = skill_id - MC_SKILLBASE; + short idx = mercenary_skill_get_index(skill_id); - if( !md || !md->db ) + if( !md || !md->db || idx == -1) return 0; - if( md->db->skill[i].id == skill_id ) - return md->db->skill[i].lv; - - return 0; + return md->db->skill[idx].lv; } /** @@ -529,6 +540,7 @@ static bool mercenary_readdb_sub(char* str[], int columns, int current) void mercenary_readdb(void) { const char *filename[]={ "mercenary_db.txt",DBIMPORT"/mercenary_db.txt"}; uint8 i; + mercenary_count = 0; //Reset the counter memset(mercenary_db,0,sizeof(mercenary_db)); for(i = 0; i,, struct s_mercenary_db *db; - uint16 i, class_, skill_id, skill_lv; + uint16 class_, skill_id, skill_lv; + uint8 i = 0; + short idx = -1; class_ = atoi(str[0]); ARR_FIND(0, MAX_MERCENARY_CLASS, i, class_ == mercenary_db[i].class_); @@ -553,18 +567,16 @@ static bool mercenary_read_skilldb_sub(char* str[], int columns, int current) } skill_id = atoi(str[1]); - if( skill_id < MC_SKILLBASE || skill_id >= MC_SKILLBASE + MAX_MERCSKILL ) - { - ShowError("read_mercenary_skilldb : Skill %d out of range.\n", skill_id); + if( (idx = mercenary_skill_get_index(skill_id)) == -1 ) { + ShowError("read_mercenary_skilldb: Invalid Mercenary skill '%s'.\n", str[1]); return false; } db = &mercenary_db[i]; skill_lv = atoi(str[2]); - i = skill_id - MC_SKILLBASE; - db->skill[i].id = skill_id; - db->skill[i].lv = skill_lv; + db->skill[idx].id = skill_id; + db->skill[idx].lv = skill_lv; return true; } @@ -575,6 +587,7 @@ static bool mercenary_read_skilldb_sub(char* str[], int columns, int current) void mercenary_read_skilldb(void){ const char *filename[]={ "mercenary_skill_db.txt",DBIMPORT"/mercenary_skill_db.txt"}; uint8 i; + for(i = 0; i=0 && i< MAX_MOBSKILL ;j--) { - int skill_id = skill_tree[pc_class2idx(sd->status.class_)][j].id; - if (!skill_id || sd->status.skill[skill_id].lv < 1 || + uint16 skill_id = skill_tree[pc_class2idx(sd->status.class_)][j].id; + uint16 sk_idx = 0; + if (!skill_id || !(sk_idx = skill_get_index(skill_id)) || sd->status.skill[sk_idx].lv < 1 || (skill_get_inf2(skill_id)&(INF2_WEDDING_SKILL|INF2_GUILD_SKILL)) || skill_get_nocast(skill_id)&16 ) @@ -3513,7 +3514,7 @@ int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, cons memset (&ms[i], 0, sizeof(struct mob_skill)); ms[i].skill_id = skill_id; - ms[i].skill_lv = sd->status.skill[skill_id].lv; + ms[i].skill_lv = sd->status.skill[sk_idx].lv; ms[i].state = MSS_ANY; ms[i].permillage = 500*battle_config.mob_skill_rate/100; //Default chance of all skills: 5% ms[i].emotion = -1; @@ -4289,7 +4290,7 @@ static bool mob_parse_row_mobskilldb(char** str, int columns, int current) //Skill ID j = atoi(str[3]); - if (j <= 0 || j > MAX_SKILL_DB) //fixed Lupus + if (j <= 0 || j > MAX_SKILL_ID || !skill_get_index(j)) //fixed Lupus { if (mob_id < 0) ShowError("mob_parse_row_mobskilldb: Invalid Skill ID (%d) for all mobs\n", j); diff --git a/src/map/npc.c b/src/map/npc.c index 1a3c894c2e..b577a31f91 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -112,12 +112,12 @@ int npc_isnear_sub(struct block_list* bl, va_list args) { if (skill_id > 0) { //If skill_id > 0 that means is used for INF2_NO_NEARNPC [Cydh] uint16 idx = skill_get_index(skill_id); - if (idx > 0 && skill_db[idx].unit_nonearnpc_type) { + if (idx > 0 && skill_db[idx]->unit_nonearnpc_type) { while (1) { - if (skill_db[idx].unit_nonearnpc_type&1 && nd->subtype == NPCTYPE_WARP) break; - if (skill_db[idx].unit_nonearnpc_type&2 && nd->subtype == NPCTYPE_SHOP) break; - if (skill_db[idx].unit_nonearnpc_type&4 && nd->subtype == NPCTYPE_SCRIPT) break; - if (skill_db[idx].unit_nonearnpc_type&8 && nd->subtype == NPCTYPE_TOMB) break; + if (skill_db[idx]->unit_nonearnpc_type&1 && nd->subtype == NPCTYPE_WARP) break; + if (skill_db[idx]->unit_nonearnpc_type&2 && nd->subtype == NPCTYPE_SHOP) break; + if (skill_db[idx]->unit_nonearnpc_type&4 && nd->subtype == NPCTYPE_SCRIPT) break; + if (skill_db[idx]->unit_nonearnpc_type&8 && nd->subtype == NPCTYPE_TOMB) break; return 0; } } @@ -1687,8 +1687,9 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) // custom merchant shop exp bonus if( battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0 ) { - if( sd->status.skill[MC_DISCOUNT].flag >= SKILL_FLAG_REPLACED_LV_0 ) - skill = sd->status.skill[MC_DISCOUNT].flag - SKILL_FLAG_REPLACED_LV_0; + uint16 sk_idx = skill_get_index(MC_DISCOUNT); + if( sd->status.skill[sk_idx].flag >= SKILL_FLAG_REPLACED_LV_0 ) + skill = sd->status.skill[sk_idx].flag - SKILL_FLAG_REPLACED_LV_0; if( skill > 0 ) { @@ -1850,8 +1851,9 @@ int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) // custom merchant shop exp bonus if( battle_config.shop_exp > 0 && z > 0 && ( skill = pc_checkskill(sd,MC_OVERCHARGE) ) > 0) { - if( sd->status.skill[MC_OVERCHARGE].flag >= SKILL_FLAG_REPLACED_LV_0 ) - skill = sd->status.skill[MC_OVERCHARGE].flag - SKILL_FLAG_REPLACED_LV_0; + uint16 sk_idx = skill_get_index(MC_OVERCHARGE); + if( sd->status.skill[sk_idx].flag >= SKILL_FLAG_REPLACED_LV_0 ) + skill = sd->status.skill[sk_idx].flag - SKILL_FLAG_REPLACED_LV_0; if( skill > 0 ) { diff --git a/src/map/pc.c b/src/map/pc.c index f7fea6464b..6f8ed3aba7 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -934,12 +934,12 @@ bool pc_adoption(struct map_session_data *p1_sd, struct map_session_data *p2_sd, clif_updatestatus(b_sd, SP_JOBEXP); // Baby Skills - pc_skill(b_sd, WE_BABY, 1, 0); - pc_skill(b_sd, WE_CALLPARENT, 1, 0); + pc_skill(b_sd, WE_BABY, 1, ADDSKILL_PERMANENT); + pc_skill(b_sd, WE_CALLPARENT, 1, ADDSKILL_PERMANENT); // Parents Skills - pc_skill(p1_sd, WE_CALLBABY, 1, 0); - pc_skill(p2_sd, WE_CALLBABY, 1, 0); + pc_skill(p1_sd, WE_CALLBABY, 1, ADDSKILL_PERMANENT); + pc_skill(p2_sd, WE_CALLBABY, 1, ADDSKILL_PERMANENT); return true; } @@ -1248,6 +1248,7 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_ * Check if player have any item cooldowns on **/ pc_itemcd_do(sd,true); + pc_validate_skill(sd); #ifdef BOUND_ITEMS // Party bound item check @@ -1355,7 +1356,7 @@ void pc_reg_received(struct map_session_data *sd) if ((i = pc_checkskill(sd,RG_PLAGIARISM)) > 0) { sd->cloneskill_idx = skill_get_index(pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM)); - if (sd->cloneskill_idx >= 0) { + if (sd->cloneskill_idx > 0) { sd->status.skill[sd->cloneskill_idx].id = pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM); sd->status.skill[sd->cloneskill_idx].lv = pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM_LV); if (sd->status.skill[sd->cloneskill_idx].lv > i) @@ -1365,7 +1366,7 @@ void pc_reg_received(struct map_session_data *sd) } if ((i = pc_checkskill(sd,SC_REPRODUCE)) > 0) { sd->reproduceskill_idx = skill_get_index(pc_readglobalreg(sd,SKILL_VAR_REPRODUCE)); - if (sd->reproduceskill_idx >= 0) { + if (sd->reproduceskill_idx > 0) { sd->status.skill[sd->reproduceskill_idx].id = pc_readglobalreg(sd,SKILL_VAR_REPRODUCE); sd->status.skill[sd->reproduceskill_idx].lv = pc_readglobalreg(sd,SKILL_VAR_REPRODUCE_LV); if (i < sd->status.skill[sd->reproduceskill_idx].lv) @@ -1448,21 +1449,20 @@ void pc_reg_received(struct map_session_data *sd) static int pc_calc_skillpoint(struct map_session_data* sd) { - uint16 i, skill_point=0; + uint16 i, skill_point = 0; nullpo_ret(sd); - for(i=1;i 0) { - uint16 inf2 = skill_get_inf2(i); - if((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) && + for(i = 1; i < MAX_SKILL; i++) { + if( sd->status.skill[i].id && sd->status.skill[i].lv > 0) { + uint16 inf2 = skill_get_inf2(sd->status.skill[i].id); + if ((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) && !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL)) //Do not count wedding/link skills. [Skotlex] - ) { + ) + { if(sd->status.skill[i].flag == SKILL_FLAG_PERMANENT) - skill_point += skill_lv; - else - if(sd->status.skill[i].flag == SKILL_FLAG_REPLACED_LV_0) + skill_point += sd->status.skill[i].lv; + else if(sd->status.skill[i].flag == SKILL_FLAG_REPLACED_LV_0) skill_point += (sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0); } } @@ -1471,6 +1471,57 @@ static int pc_calc_skillpoint(struct map_session_data* sd) return skill_point; } +static bool pc_grant_allskills(struct map_session_data *sd, bool addlv) { + uint16 i = 0; + + if (!sd || !pc_has_permission(sd, PC_PERM_ALL_SKILL) || !SKILL_MAX_DB()) + return false; + + /** + * Dummy skills must NOT be added here otherwise they'll be displayed in the, + * skill tree and since they have no icons they'll give resource errors + * Get ALL skills except npc/guild ones. [Skotlex] + * Don't add SG_DEVIL [Komurka] and MO_TRIPLEATTACK and RG_SNATCHER [ultramage] + **/ + for( i = 0; i < MAX_SKILL; i++ ) { + uint16 skill_id = skill_idx2id(i); + if (!skill_id || (skill_get_inf2(skill_id)&(INF2_NPC_SKILL|INF2_GUILD_SKILL))) + continue; + switch (skill_id) { + case SM_SELFPROVOKE: + case AB_DUPLELIGHT_MELEE: + case AB_DUPLELIGHT_MAGIC: + case WL_CHAINLIGHTNING_ATK: + case WL_TETRAVORTEX_FIRE: + case WL_TETRAVORTEX_WATER: + case WL_TETRAVORTEX_WIND: + case WL_TETRAVORTEX_GROUND: + case WL_SUMMON_ATK_FIRE: + case WL_SUMMON_ATK_WIND: + case WL_SUMMON_ATK_WATER: + case WL_SUMMON_ATK_GROUND: + case LG_OVERBRAND_BRANDISH: + case LG_OVERBRAND_PLUSATK: + case WM_SEVERE_RAINSTORM_MELEE: + case RL_R_TRIP_PLUSATK: + case SG_DEVIL: + case MO_TRIPLEATTACK: + case RG_SNATCHER: + continue; + default: + { + uint8 lv = (uint8)skill_get_max(skill_id); + if (lv > 0) { + sd->status.skill[i].id = skill_id; + if (addlv) + sd->status.skill[i].lv = lv; + } + } + break; + } + } + return true; +} /*========================================== * Calculation of skill level. @@ -1478,8 +1529,8 @@ static int pc_calc_skillpoint(struct map_session_data* sd) *------------------------------------------*/ void pc_calc_skilltree(struct map_session_data *sd) { - int i,flag; - int c=0; + int i, flag; + int c = 0; nullpo_retv(sd); i = pc_calc_skilltree_normalize_job(sd); @@ -1496,41 +1547,52 @@ void pc_calc_skilltree(struct map_session_data *sd) sd->status.skill[i].id = 0; //First clear skills. /* permanent skills that must be re-checked */ if( sd->status.skill[i].flag == SKILL_FLAG_PERM_GRANTED ) { - switch( i ) { + uint16 sk_id = skill_idx2id(i); + if (!sk_id) { + sd->status.skill[i].id = 0; + sd->status.skill[i].lv = 0; + sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; + continue; + } + switch (sk_id) { case NV_TRICKDEAD: if( (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE ) { - sd->status.skill[i].id = 0; - sd->status.skill[i].lv = 0; - sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; + sd->status.skill[i].id = 0; + sd->status.skill[i].lv = 0; + sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; } break; } } } - for( i = 0; i < MAX_SKILL; i++ ) - { - if( sd->status.skill[i].flag != SKILL_FLAG_PERMANENT && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED && sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED ) - { // Restore original level of skills after deleting earned skills. + for( i = 0; i < MAX_SKILL; i++ ) { + uint16 skill_id = 0; + + // Restore original level of skills after deleting earned skills. + if( sd->status.skill[i].flag != SKILL_FLAG_PERMANENT && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED && sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED ) { sd->status.skill[i].lv = (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) ? 0 : sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0; sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; } - if( sd->sc.count && sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_BARDDANCER && i >= DC_HUMMING && i<= DC_SERVICEFORYOU ) - { //Enable Bard/Dancer spirit linked skills. - if( sd->status.sex ) - { //Link dancer skills to bard. + //Enable Bard/Dancer spirit linked skills. + if (!(skill_id = skill_idx2id(i)) || skill_id < DC_HUMMING || skill_id > DC_SERVICEFORYOU) + continue; + + if( &sd->sc && sd->sc.count && sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_BARDDANCER ) { + //Link Dancer skills to bard. + if( sd->status.sex ) { if( sd->status.skill[i-8].lv < 10 ) continue; - sd->status.skill[i].id = i; + sd->status.skill[i].id = skill_id; sd->status.skill[i].lv = sd->status.skill[i-8].lv; // Set the level to the same as the linking skill sd->status.skill[i].flag = SKILL_FLAG_TEMPORARY; // Tag it as a non-savable, non-uppable, bonus skill } - else - { //Link bard skills to dancer. + //Link Bard skills to dancer. + else { if( sd->status.skill[i].lv < 10 ) continue; - sd->status.skill[i-8].id = i - 8; + sd->status.skill[i-8].id = skill_id - 8; sd->status.skill[i-8].lv = sd->status.skill[i].lv; // Set the level to the same as the linking skill sd->status.skill[i-8].flag = SKILL_FLAG_TEMPORARY; // Tag it as a non-savable, non-uppable, bonus skill } @@ -1540,105 +1602,84 @@ void pc_calc_skilltree(struct map_session_data *sd) // Removes Taekwon Ranker skill bonus if ((sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON) { uint16 c_ = pc_class2idx(JOB_TAEKWON); + for (i = 0; i < MAX_SKILL_TREE; i++) { - uint16 x = skill_get_index(skill_tree[c_][i].id), skid = sd->status.skill[x].id; - if (skid && x > 0 && sd->status.skill[x].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[x].flag != SKILL_FLAG_PERM_GRANTED) { - if (skid == NV_BASIC || skid == NV_FIRSTAID || skid == WE_CALLBABY) + uint16 sk_id = skill_tree[c_][i].id; + uint16 sk_idx = 0; + + if (!sk_id || !(sk_idx = skill_get_index(skill_tree[c_][i].id))) + continue; + + if (sd->status.skill[sk_idx].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[sk_idx].flag != SKILL_FLAG_PERM_GRANTED) { + if (sk_id == NV_BASIC || sk_id == NV_FIRSTAID || sk_id == WE_CALLBABY) continue; - sd->status.skill[x].id = 0; + sd->status.skill[sk_idx].id = 0; + sd->status.skill[sk_idx].lv = 0; + sd->status.skill[sk_idx].flag = SKILL_FLAG_PERMANENT; } } } - if( pc_has_permission(sd, PC_PERM_ALL_SKILL) ) { - for( i = 0; i < MAX_SKILL; i++ ) { - switch(i) { - /** - * Dummy skills must be added here otherwise they'll be displayed in the, - * skill tree and since they have no icons they'll give resource errors - **/ - case SM_SELFPROVOKE: - case AB_DUPLELIGHT_MELEE: - case AB_DUPLELIGHT_MAGIC: - case WL_CHAINLIGHTNING_ATK: - case WL_TETRAVORTEX_FIRE: - case WL_TETRAVORTEX_WATER: - case WL_TETRAVORTEX_WIND: - case WL_TETRAVORTEX_GROUND: - case WL_SUMMON_ATK_FIRE: - case WL_SUMMON_ATK_WIND: - case WL_SUMMON_ATK_WATER: - case WL_SUMMON_ATK_GROUND: - case LG_OVERBRAND_BRANDISH: - case LG_OVERBRAND_PLUSATK: - case WM_SEVERE_RAINSTORM_MELEE: - case RL_R_TRIP_PLUSATK: - continue; - default: - break; - } - if( skill_get_inf2(i)&(INF2_NPC_SKILL|INF2_GUILD_SKILL) ) - continue; //Only skills you can't have are npc/guild ones - if( skill_get_max(i) > 0 ) - sd->status.skill[i].id = i; - } - return; - } + // Grant all skills + pc_grant_allskills(sd, false); do { - short skid=0; + uint16 skid = 0; + flag = 0; - for( i = 0; i < MAX_SKILL_TREE && (skid = skill_tree[c][i].id) > 0; i++ ) - { - int f; - if( sd->status.skill[skid].id ) + for (i = 0; i < MAX_SKILL_TREE && (skid = skill_tree[c][i].id) > 0; i++) { + bool fail = false; + uint16 sk_idx = skill_get_index(skid); + + if (sd->status.skill[sk_idx].id) continue; //Skill already known. - f = 1; - if(!battle_config.skillfree) { - int j; + if (!battle_config.skillfree) { + uint8 j; for(j = 0; j < MAX_PC_SKILL_REQUIRE; j++) { - int k; - if((k=skill_tree[c][i].need[j].id)) - { - if (sd->status.skill[k].id == 0 || sd->status.skill[k].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[k].flag == SKILL_FLAG_PLAGIARIZED) - k = 0; //Not learned. + uint16 sk_need_id = skill_tree[c][i].need[j].id; + uint16 sk_need_idx = 0; + + if (sk_need_id && (sk_need_idx = skill_get_index(sk_need_id))) { + short sk_need = 0; + + if (sd->status.skill[sk_need_idx].id == 0 || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_PLAGIARIZED) + sk_need = sk_need_id; //Not learned. + else if (sd->status.skill[sk_need_idx].flag >= SKILL_FLAG_REPLACED_LV_0) //Real learned level + sk_need = sd->status.skill[skill_tree[c][i].need[j].id].flag - SKILL_FLAG_REPLACED_LV_0; else - if (sd->status.skill[k].flag >= SKILL_FLAG_REPLACED_LV_0) //Real learned level - k = sd->status.skill[skill_tree[c][i].need[j].id].flag - SKILL_FLAG_REPLACED_LV_0; - else - k = pc_checkskill(sd,k); - if (k < skill_tree[c][i].need[j].lv) - { - f = 0; + sk_need = pc_checkskill(sd,sk_need_id); + + if (sk_need < skill_tree[c][i].need[j].lv) { + fail = true; break; } } } - if( sd->status.job_level < skill_tree[c][i].joblv ) { //We need to get the actual class in this case + + if (sd->status.job_level < skill_tree[c][i].joblv) { //We need to get the actual class in this case int class_ = pc_mapid2jobid(sd->class_, sd->status.sex); class_ = pc_class2idx(class_); if (class_ == c || (class_ != c && sd->status.job_level < skill_tree[class_][i].joblv)) - f = 0; // job level requirement wasn't satisfied + fail = true; // job level requirement wasn't satisfied } } - if( f ) { - int inf2; - inf2 = skill_get_inf2(skid); + if (!fail) { + int inf2 = skill_get_inf2(skid); - if(!sd->status.skill[skid].lv && ( + if (!sd->status.skill[skid].lv && ( (inf2&INF2_QUEST_SKILL && !battle_config.quest_skill_learn) || inf2&INF2_WEDDING_SKILL || (inf2&INF2_SPIRIT_SKILL && !sd->sc.data[SC_SPIRIT]) )) continue; //Cannot be learned via normal means. Note this check DOES allows raising already known skills. - sd->status.skill[skid].id = skid; + sd->status.skill[sk_idx].id = skid; if(inf2&INF2_SPIRIT_SKILL) { //Spirit skills cannot be learned, they will only show up on your tree when you get buffed. - sd->status.skill[skid].lv = 1; // need to manually specify a skill level - sd->status.skill[skid].flag = SKILL_FLAG_TEMPORARY; //So it is not saved, and tagged as a "bonus" skill. + sd->status.skill[sk_idx].lv = 1; // need to manually specify a skill level + sd->status.skill[sk_idx].flag = SKILL_FLAG_TEMPORARY; //So it is not saved, and tagged as a "bonus" skill. } flag = 1; // skill list has changed, perform another pass } @@ -1646,7 +1687,7 @@ void pc_calc_skilltree(struct map_session_data *sd) } while(flag); if( c > 0 && sd->status.skill_point == 0 && pc_is_taekwon_ranker(sd) ) { - short skid=0; + unsigned short skid = 0; /* Taekwon Ranker Bonus Skill Tree ============================================ - Grant All Taekwon Tree, but only as Bonus Skills in case they drop from ranking. @@ -1654,25 +1695,28 @@ void pc_calc_skilltree(struct map_session_data *sd) - (sd->status.skill_point == 0) to wait until all skill points are assigned to avoid problems with Job Change quest. */ for( i = 0; i < MAX_SKILL_TREE && (skid = skill_tree[c][i].id) > 0; i++ ) { + uint16 sk_idx = 0; + if (!(sk_idx = skill_get_index(skid))) + continue; if( (skill_get_inf2(skid)&(INF2_QUEST_SKILL|INF2_WEDDING_SKILL)) ) continue; //Do not include Quest/Wedding skills. - if( sd->status.skill[skid].id == 0 ) { //do we really want skid as index ? //Lighta - sd->status.skill[skid].id = skid; - sd->status.skill[skid].flag = SKILL_FLAG_TEMPORARY; // So it is not saved, and tagged as a "bonus" skill. + if( sd->status.skill[sk_idx].id == 0 ) { //do we really want skid as index ? //Lighta + sd->status.skill[sk_idx].id = skid; + sd->status.skill[sk_idx].flag = SKILL_FLAG_TEMPORARY; // So it is not saved, and tagged as a "bonus" skill. } else if( skid != NV_BASIC ) - sd->status.skill[skid].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[skid].lv; // Remember original level - sd->status.skill[skid].lv = skill_tree_get_max(skid, sd->status.class_); + sd->status.skill[sk_idx].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[sk_idx].lv; // Remember original level + sd->status.skill[sk_idx].lv = skill_tree_get_max(skid, sd->status.class_); } } } //Checks if you can learn a new skill after having leveled up a skill. -static void pc_check_skilltree(struct map_session_data *sd, int skill) +static void pc_check_skilltree(struct map_session_data *sd) { - int i,id=0,flag; - int c=0; + int i, flag = 0; + int c = 0; - if(battle_config.skillfree) + if (battle_config.skillfree) return; //Function serves no purpose if this is set i = pc_calc_skilltree_normalize_job(sd); @@ -1682,44 +1726,54 @@ static void pc_check_skilltree(struct map_session_data *sd, int skill) return; } c = pc_class2idx(c); + do { + uint16 skid = 0; + flag = 0; - for( i = 0; i < MAX_SKILL_TREE && (id=skill_tree[c][i].id)>0; i++ ) - { - int j, f = 1; - if( sd->status.skill[id].id ) //Already learned + for (i = 0; i < MAX_SKILL_TREE && (skid = skill_tree[c][i].id) > 0; i++ ) { + uint16 sk_idx = skill_get_index(skid); + bool fail = false; + uint8 j = 0; + + if (sd->status.skill[sk_idx].id) //Already learned continue; - for( j = 0; j < MAX_PC_SKILL_REQUIRE; j++ ){ - int k = skill_tree[c][i].need[j].id; - if( k != 0 ){ - if( sd->status.skill[k].id == 0 || sd->status.skill[k].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[k].flag == SKILL_FLAG_PLAGIARIZED ) - k = 0; //Not learned. + + for (j = 0; j < MAX_PC_SKILL_REQUIRE; j++) { + uint16 sk_need_id = skill_tree[c][i].need[j].id; + uint16 sk_need_idx = 0; + + if (sk_need_id && (sk_need_idx = skill_get_index(sk_need_id))) { + short sk_need = sk_need_id; + + if (sd->status.skill[sk_need_idx].id == 0 || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_PLAGIARIZED) + sk_need = 0; //Not learned. + else if (sd->status.skill[sk_need_idx].flag >= SKILL_FLAG_REPLACED_LV_0) //Real lerned level + sk_need = sd->status.skill[sk_need_idx].flag - SKILL_FLAG_REPLACED_LV_0; else - if( sd->status.skill[k].flag >= SKILL_FLAG_REPLACED_LV_0) //Real lerned level - k = sd->status.skill[skill_tree[c][i].need[j].id].flag - SKILL_FLAG_REPLACED_LV_0; - else - k = pc_checkskill(sd,k); - if( k < skill_tree[c][i].need[j].lv ) - { - f = 0; + sk_need = pc_checkskill(sd,sk_need_id); + + if (sk_need < skill_tree[c][i].need[j].lv) { + fail = true; break; } } } - if( !f ) + + if( fail ) continue; if( sd->status.job_level < skill_tree[c][i].joblv ) continue; - j = skill_get_inf2(id); - if( !sd->status.skill[id].lv && ( + j = skill_get_inf2(skid); + if( !sd->status.skill[sk_idx].lv && ( (j&INF2_QUEST_SKILL && !battle_config.quest_skill_learn) || j&INF2_WEDDING_SKILL || (j&INF2_SPIRIT_SKILL && !sd->sc.data[SC_SPIRIT]) ) ) continue; //Cannot be learned via normal means. - sd->status.skill[id].id = id; + sd->status.skill[sk_idx].id = skid; flag = 1; } } while(flag); @@ -1731,14 +1785,12 @@ void pc_clean_skilltree(struct map_session_data *sd) { uint16 i; for (i = 0; i < MAX_SKILL; i++){ - if (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED) - { + if (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED) { sd->status.skill[i].id = 0; sd->status.skill[i].lv = 0; sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; } - else - if (sd->status.skill[i].flag == SKILL_FLAG_REPLACED_LV_0){ + else if (sd->status.skill[i].flag == SKILL_FLAG_REPLACED_LV_0){ sd->status.skill[i].lv = sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0; sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; } @@ -3816,74 +3868,78 @@ void pc_bonus5(struct map_session_data *sd,int type,int type2,int type3,int type * 2 - Like 1, except the level granted can stack with previously learned level. * 4 - Like 0, except the skill will ignore skill tree (saves through job changes and resets). *------------------------------------------*/ -int pc_skill(TBL_PC* sd, int id, int level, int flag) -{ +bool pc_skill(TBL_PC* sd, uint16 skill_id, int level, enum e_addskill_type type) { + uint16 idx = 0; nullpo_ret(sd); - if( id <= 0 || id >= MAX_SKILL || skill_db[id].name == NULL) { - ShowError("pc_skill: Skill with id %d does not exist in the skill database\n", id); - return 0; + if (!skill_id || !(idx = skill_get_index(skill_id))) { + ShowError("pc_skill: Skill with id %d does not exist in the skill database\n", skill_id); + return false; } - if( level > MAX_SKILL_LEVEL ) { + if (level > MAX_SKILL_LEVEL) { ShowError("pc_skill: Skill level %d too high. Max lv supported is %d\n", level, MAX_SKILL_LEVEL); - return 0; + return false; } - if( flag == 2 && sd->status.skill[id].lv + level > MAX_SKILL_LEVEL ) { - ShowError("pc_skill: Skill level bonus %d too high. Max lv supported is %d. Curr lv is %d\n", level, MAX_SKILL_LEVEL, sd->status.skill[id].lv); - return 0; + if (type == ADDSKILL_TEMP_ADDLEVEL && sd->status.skill[idx].lv + level > MAX_SKILL_LEVEL) { + ShowWarning("pc_skill: Skill level bonus %d too high. Max lv supported is %d. Curr lv is %d. Set to max level.\n", level, MAX_SKILL_LEVEL, sd->status.skill[idx].lv); + level = MAX_SKILL_LEVEL - sd->status.skill[idx].lv; } - switch( flag ){ - case 0: //Set skill data overwriting whatever was there before. - sd->status.skill[id].id = id; - sd->status.skill[id].lv = level; - sd->status.skill[id].flag = SKILL_FLAG_PERMANENT; - if( level == 0 ) { //Remove skill. - sd->status.skill[id].id = 0; - clif_deleteskill(sd,id); + switch (type) { + case ADDSKILL_PERMANENT: //Set skill data overwriting whatever was there before. + sd->status.skill[idx].id = skill_id; + sd->status.skill[idx].lv = level; + sd->status.skill[idx].flag = SKILL_FLAG_PERMANENT; + if (level == 0) { //Remove skill. + sd->status.skill[idx].id = 0; + clif_deleteskill(sd,skill_id); } else - clif_addskill(sd,id); - if( !skill_get_inf(id) ) //Only recalculate for passive skills. + clif_addskill(sd,skill_id); + if (!skill_get_inf(skill_id)) //Only recalculate for passive skills. status_calc_pc(sd, SCO_NONE); break; - case 1: //Item bonus skill. - if( sd->status.skill[id].id == id ) { - if( sd->status.skill[id].lv >= level ) - return 0; - if( sd->status.skill[id].flag == SKILL_FLAG_PERMANENT ) //Non-granted skill, store it's level. - sd->status.skill[id].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[id].lv; + + case ADDSKILL_TEMP: //Item bonus skill. + if (sd->status.skill[idx].id != 0) { + if (sd->status.skill[idx].lv >= level) + return true; + if (sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT) //Non-granted skill, store it's level. + sd->status.skill[idx].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[idx].lv; } else { - sd->status.skill[id].id = id; - sd->status.skill[id].flag = SKILL_FLAG_TEMPORARY; + sd->status.skill[idx].id = skill_id; + sd->status.skill[idx].flag = SKILL_FLAG_TEMPORARY; } - sd->status.skill[id].lv = level; + sd->status.skill[idx].lv = level; break; - case 2: //Add skill bonus on top of what you had. - if( sd->status.skill[id].id == id ) { - if( sd->status.skill[id].flag == SKILL_FLAG_PERMANENT ) - sd->status.skill[id].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[id].lv; // Store previous level. + + case ADDSKILL_TEMP_ADDLEVEL: //Add skill bonus on top of what you had. + if (sd->status.skill[idx].id != 0) { + if (sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT) + sd->status.skill[idx].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[idx].lv; // Store previous level. } else { - sd->status.skill[id].id = id; - sd->status.skill[id].flag = SKILL_FLAG_TEMPORARY; //Set that this is a bonus skill. + sd->status.skill[idx].id = skill_id; + sd->status.skill[idx].flag = SKILL_FLAG_TEMPORARY; //Set that this is a bonus skill. } - sd->status.skill[id].lv += level; + sd->status.skill[idx].lv += level; break; - case 4: //Permanent granted skills ignore the skill tree - sd->status.skill[id].id = id; - sd->status.skill[id].lv = level; - sd->status.skill[id].flag = SKILL_FLAG_PERM_GRANTED; - if( level == 0 ) { //Remove skill. - sd->status.skill[id].id = 0; - clif_deleteskill(sd,id); + + case ADDSKILL_PERMANENT_GRANTED: //Permanent granted skills ignore the skill tree + sd->status.skill[idx].id = skill_id; + sd->status.skill[idx].lv = level; + sd->status.skill[idx].flag = SKILL_FLAG_PERM_GRANTED; + if (level == 0) { //Remove skill. + sd->status.skill[idx].id = 0; + clif_deleteskill(sd,skill_id); } else - clif_addskill(sd,id); - if( !skill_get_inf(id) ) //Only recalculate for passive skills. + clif_addskill(sd,skill_id); + if (!skill_get_inf(skill_id)) //Only recalculate for passive skills. status_calc_pc(sd, SCO_NONE); break; - default: //Unknown flag? - return 0; + + default: + return false; } - return 1; + return true; } /*========================================== * Append a card to an item ? @@ -5429,8 +5485,8 @@ int pc_get_skillcooldown(struct map_session_data *sd, uint16 skill_id, uint16 sk int cooldown = 0, cooldownlen = ARRAYLENGTH(sd->skillcooldown); if (!idx) return 0; - if (skill_db[idx].cooldown[skill_lv - 1]) - cooldown = skill_db[idx].cooldown[skill_lv - 1]; + if (skill_db[idx]->cooldown[skill_lv - 1]) + cooldown = skill_db[idx]->cooldown[skill_lv - 1]; ARR_FIND(0, cooldownlen, i, sd->skillcooldown[i].id == skill_id); if (i < cooldownlen) { @@ -5443,24 +5499,23 @@ int pc_get_skillcooldown(struct map_session_data *sd, uint16 skill_id, uint16 sk /*========================================== * Return player sd skill_lv learned for given skill *------------------------------------------*/ -uint8 pc_checkskill(struct map_session_data *sd,uint16 skill_id) +uint8 pc_checkskill(struct map_session_data *sd, uint16 skill_id) { - if(sd == NULL) return 0; - if( skill_id >= GD_SKILLBASE && skill_id < GD_MAX ) { + uint16 i = 0, idx = 0; + if (sd == NULL) + return 0; + if ((idx = skill_get_index(skill_id)) == 0) { + ShowError("pc_checkskill: Invalid skill id %d (char_id=%d).\n", skill_id, sd->status.char_id); + return 0; + } + if (SKILL_CHK_GUILD(skill_id) ) { struct guild *g; if( sd->status.guild_id>0 && (g=sd->guild)!=NULL) return guild_checkskill(g,skill_id); return 0; - } else if(skill_id >= ARRAYLENGTH(sd->status.skill) ) { - ShowError("pc_checkskill: Invalid skill id %d (char_id=%d).\n", skill_id, sd->status.char_id); - return 0; } - - if(sd->status.skill[skill_id].id == skill_id) - return (sd->status.skill[skill_id].lv); - - return 0; + return sd->status.skill[idx].lv; } /** @@ -6632,52 +6687,57 @@ int pc_statusup2(struct map_session_data* sd, int type, int val) * Update skill_lv for player sd * Skill point allocation *------------------------------------------*/ -int pc_skillup(struct map_session_data *sd,uint16 skill_id) +void pc_skillup(struct map_session_data *sd,uint16 skill_id) { - nullpo_ret(sd); + uint16 idx = skill_get_index(skill_id); - if( skill_id >= GD_SKILLBASE && skill_id < GD_SKILLBASE+MAX_GUILDSKILL ) - { + nullpo_retv(sd); + + if (!idx) { + if (skill_id) + ShowError("pc_skillup: Player attempts to level up invalid skill '%d'\n", skill_id); + return; + } + + // Level up guild skill + if (SKILL_CHK_GUILD(skill_id)) { guild_skillup(sd, skill_id); - return 0; + return; } - - if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE+MAX_HOMUNSKILL && sd->hd ) - { + // Level up homunculus skill + else if (sd->hd && SKILL_CHK_HOMUN(skill_id)) { hom_skillup(sd->hd, skill_id); - return 0; + return; } + else { + if( sd->status.skill_point > 0 && + sd->status.skill[idx].id && + sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT && //Don't allow raising while you have granted skills. [Skotlex] + sd->status.skill[idx].lv < skill_tree_get_max(skill_id, sd->status.class_) ) + { + int lv, range, upgradable; + sd->status.skill[idx].lv++; + sd->status.skill_point--; + if( !skill_get_inf(skill_id) ) + status_calc_pc(sd,SCO_NONE); // Only recalculate for passive skills. + else if( sd->status.skill_point == 0 && pc_is_taekwon_ranker(sd) ) + pc_calc_skilltree(sd); // Required to grant all TK Ranker skills. + else + pc_check_skilltree(sd); // Check if a new skill can Lvlup - if(skill_id >= MAX_SKILL ) - return 0; - - if( sd->status.skill_point > 0 && - sd->status.skill[skill_id].id && - sd->status.skill[skill_id].flag == SKILL_FLAG_PERMANENT && //Don't allow raising while you have granted skills. [Skotlex] - sd->status.skill[skill_id].lv < skill_tree_get_max(skill_id, sd->status.class_) ) - { - int lv,range, upgradable; - sd->status.skill[skill_id].lv++; - sd->status.skill_point--; - if( !skill_get_inf(skill_id) ) - status_calc_pc(sd,SCO_NONE); // Only recalculate for passive skills. - else if( sd->status.skill_point == 0 && pc_is_taekwon_ranker(sd) ) - pc_calc_skilltree(sd); // Required to grant all TK Ranker skills. + lv = sd->status.skill[idx].lv; + range = skill_get_range2(&sd->bl, skill_id, lv); + upgradable = (lv < skill_tree_get_max(sd->status.skill[idx].id, sd->status.class_)) ? 1 : 0; + clif_skillup(sd,skill_id,lv,range,upgradable); + clif_updatestatus(sd,SP_SKILLPOINT); + if( skill_id == GN_REMODELING_CART ) /* cart weight info was updated by status_calc_pc */ + clif_updatestatus(sd,SP_CARTINFO); + if (!pc_has_permission(sd, PC_PERM_ALL_SKILL)) // may skill everything at any time anyways, and this would cause a huge slowdown + clif_skillinfoblock(sd); + } else - pc_check_skilltree(sd, skill_id); // Check if a new skill can Lvlup - - lv = sd->status.skill[skill_id].lv; - range = skill_get_range2(&sd->bl, skill_id, lv); - upgradable = (lv < skill_tree_get_max(sd->status.skill[skill_id].id, sd->status.class_)) ? 1 : 0; - clif_skillup(sd,skill_id,lv,range,upgradable); - clif_updatestatus(sd,SP_SKILLPOINT); - if( skill_id == GN_REMODELING_CART ) /* cart weight info was updated by status_calc_pc */ - clif_updatestatus(sd,SP_CARTINFO); - if (!pc_has_permission(sd, PC_PERM_ALL_SKILL)) // may skill everything at any time anyways, and this would cause a huge slowdown - clif_skillinfoblock(sd); + ShowDebug("Skill Level up failed. ID:%d idx:%d (CID=%d. AID=%d)\n", skill_id, idx, sd->status.char_id, sd->status.account_id); } - - return 0; } /*========================================== @@ -6689,7 +6749,7 @@ int pc_allskillup(struct map_session_data *sd) nullpo_ret(sd); - for(i=0;istatus.skill[i].flag != SKILL_FLAG_PERMANENT && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED && sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED) { sd->status.skill[i].lv = (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) ? 0 : sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0; sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; @@ -6698,34 +6758,23 @@ int pc_allskillup(struct map_session_data *sd) } } - if (pc_has_permission(sd, PC_PERM_ALL_SKILL)) - { //Get ALL skills except npc/guild ones. [Skotlex] - //and except SG_DEVIL [Komurka] and MO_TRIPLEATTACK and RG_SNATCHER [ultramage] - for(i=0;istatus.skill[i].lv = skill_get_max(i) ) )//Nonexistant skills should return a max of 0 anyway. - sd->status.skill[i].id = i; - } - } - } else { - int id; - for(i=0;i < MAX_SKILL_TREE && (id=skill_tree[pc_class2idx(sd->status.class_)][i].id)>0;i++){ - int inf2 = skill_get_inf2(id); + if (!pc_grant_allskills(sd, true)) { + uint16 sk_id; + for (i = 0; i < MAX_SKILL_TREE && (sk_id = skill_tree[pc_class2idx(sd->status.class_)][i].id) > 0;i++){ + int inf2 = 0; + uint16 sk_idx = 0; + if (!sk_id || !(sk_idx = skill_get_index(sk_id))) + continue; + inf2 = skill_get_inf2(sk_id); if ( (inf2&INF2_QUEST_SKILL && !battle_config.quest_skill_learn) || (inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL)) || - id==SG_DEVIL + sk_id == SG_DEVIL ) continue; //Cannot be learned normally. - sd->status.skill[id].id = id; - sd->status.skill[id].lv = skill_tree_get_max(id, sd->status.class_); // celest + sd->status.skill[sk_idx].id = sk_id; + sd->status.skill[sk_idx].lv = skill_tree_get_max(sk_id, sd->status.class_); // celest } } status_calc_pc(sd,SCO_NONE); @@ -6765,8 +6814,8 @@ int pc_resetlvl(struct map_session_data* sd,int type) if(sd->status.class_ == JOB_NOVICE_HIGH) { sd->status.status_point=100; // not 88 [celest] // give platinum skills upon changing - pc_skill(sd,142,1,0); - pc_skill(sd,143,1,0); + pc_skill(sd,NV_FIRSTAID,1,ADDSKILL_PERMANENT); + pc_skill(sd,NV_TRICKDEAD,1,ADDSKILL_PERMANENT); } } @@ -6940,9 +6989,11 @@ int pc_resetskill(struct map_session_data* sd, int flag) for( i = 1; i < MAX_SKILL; i++ ) { - int lv = sd->status.skill[i].lv; + uint8 lv = sd->status.skill[i].lv; int inf2; - if (lv < 1) continue; + uint16 skill_id = skill_idx2id(i); + if (lv == 0 || skill_id == 0) + continue; inf2 = skill_get_inf2(i); @@ -6950,7 +7001,7 @@ int pc_resetskill(struct map_session_data* sd, int flag) continue; // Don't reset trick dead if not a novice/baby - if( i == NV_TRICKDEAD && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE ) + if( skill_id == NV_TRICKDEAD && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE ) { sd->status.skill[i].lv = 0; sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; @@ -6958,13 +7009,13 @@ int pc_resetskill(struct map_session_data* sd, int flag) } // do not reset basic skill - if( i == NV_BASIC && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE ) + if( skill_id == NV_BASIC && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE ) continue; if( sd->status.skill[i].flag == SKILL_FLAG_PERM_GRANTED ) continue; - if( flag&4 && !skill_ischangesex(i) ) + if( flag&4 && !skill_ischangesex(skill_id) ) continue; if( inf2&INF2_QUEST_SKILL && !battle_config.quest_skill_learn ) @@ -8013,26 +8064,26 @@ bool pc_jobchange(struct map_session_data *sd,int job, char upper) pc_setglobalreg (sd, "jobchange_level_3rd", sd->change_level_3rd); } - if(sd->cloneskill_idx >= 0) { + if(sd->cloneskill_idx > 0) { if( sd->status.skill[sd->cloneskill_idx].flag == SKILL_FLAG_PLAGIARIZED ) { sd->status.skill[sd->cloneskill_idx].id = 0; sd->status.skill[sd->cloneskill_idx].lv = 0; sd->status.skill[sd->cloneskill_idx].flag = SKILL_FLAG_PERMANENT; clif_deleteskill(sd,pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM)); } - sd->cloneskill_idx = -1; + sd->cloneskill_idx = 0; pc_setglobalreg(sd,SKILL_VAR_PLAGIARISM, 0); pc_setglobalreg(sd,SKILL_VAR_PLAGIARISM_LV, 0); } - if(sd->reproduceskill_idx >= 0) { + if(sd->reproduceskill_idx > 0) { if( sd->status.skill[sd->reproduceskill_idx].flag == SKILL_FLAG_PLAGIARIZED ) { sd->status.skill[sd->reproduceskill_idx].id = 0; sd->status.skill[sd->reproduceskill_idx].lv = 0; sd->status.skill[sd->reproduceskill_idx].flag = SKILL_FLAG_PERMANENT; clif_deleteskill(sd,pc_readglobalreg(sd,SKILL_VAR_REPRODUCE)); } - sd->reproduceskill_idx = -1; + sd->reproduceskill_idx = 0; pc_setglobalreg(sd,SKILL_VAR_REPRODUCE,0); pc_setglobalreg(sd,SKILL_VAR_REPRODUCE_LV,0); } @@ -11283,6 +11334,32 @@ uint64 pc_generate_unique_id(struct map_session_data *sd) { return ((uint64)sd->status.char_id << 32) | sd->status.uniqueitem_counter++; } +/** + * Validating skill from player after logged on + * @param sd + **/ +void pc_validate_skill(struct map_session_data *sd) { + if (sd) { + uint16 i = 0, count = 0; + struct s_skill tmp_skills[MAX_SKILL] = {{ 0 }}; + + memcpy(tmp_skills, sd->status.skill, sizeof(sd->status.skill)); + memset(sd->status.skill, 0, sizeof(sd->status.skill)); + + for (i = 0; i < MAX_SKILL; i++) { + uint16 idx = 0; + if (tmp_skills[i].id == 0 || tmp_skills[i].lv == 0) + continue; + if ((idx = skill_get_index(tmp_skills[i].id))) { + memcpy(&sd->status.skill[idx], &tmp_skills[i], sizeof(tmp_skills[i])); + count++; + } + else + ShowWarning("pc_validate_skill: Removing invalid skill '%d' from player (AID=%d CID=%d).\n", tmp_skills[i].id, sd->status.account_id, sd->status.char_id); + } + } +} + /*========================================== * pc Init/Terminate *------------------------------------------*/ diff --git a/src/map/pc.h b/src/map/pc.h index 3032717c78..73b8fe7836 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -437,7 +437,7 @@ struct map_session_data { short catch_target_class; // pet catching, stores a pet class to catch (short now) [zzo] - short spiritball, spiritball_old; + int8 spiritball, spiritball_old; int spirit_timer[MAX_SPIRITBALL]; short talisman[ELE_POISON+1]; // There are actually 5 talisman Fire, Ice, Wind, Earth & Poison maybe because its color violet. int talisman_timer[ELE_POISON+1][10]; @@ -657,6 +657,8 @@ enum weapon_type { W_DOUBLE_SA, // sword + axe }; +#define WEAPON_TYPE_ALL ((1< defaults to 2 -/// -/// addtoskill ,, -/// addtoskill , -/// addtoskill "",, -/// addtoskill "", -/// -/// @see skill -BUILDIN_FUNC(addtoskill) -{ - int id; - int level; - int flag = 2; - TBL_PC* sd; - struct script_data *data; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - data = script_getdata(st, 2); - get_val(st, data); // Convert into value in case of a variable - id = ( data_isstring(data) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) ); - level = script_getnum(st,3); - if( script_hasdata(st,4) ) - flag = script_getnum(st,4); - pc_skill(sd, id, level, flag); + pc_skill(sd, id, level, (enum e_addskill_type)flag); return SCRIPT_CMD_SUCCESS; } @@ -19376,7 +19348,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(autobonus2,"sii??"), BUILDIN_DEF(autobonus3,"siiv?"), BUILDIN_DEF(skill,"vi?"), - BUILDIN_DEF(addtoskill,"vi?"), // [Valaris] + BUILDIN_DEF2(skill,"addtoskill","vi?"), // [Valaris] BUILDIN_DEF(guildskill,"vi"), BUILDIN_DEF(getskilllv,"v"), BUILDIN_DEF(getgdskilllv,"iv"), diff --git a/src/map/skill.c b/src/map/skill.c index e94631f53a..d25b381feb 100755 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -53,10 +53,17 @@ #define EL_SKILLRANGEMAX EL_SKILLRANGEMIN + MAX_ELEMENTALSKILL #define GD_SKILLRANGEMIN EL_SKILLRANGEMAX + 1 #define GD_SKILLRANGEMAX GD_SKILLRANGEMIN + MAX_GUILDSKILL - #if GD_SKILLRANGEMAX > 999 #error GD_SKILLRANGEMAX is greater than 999 #endif + + +static DBMap *skilldb_id2idx; /// Skill ID to Index lookup: skill_index = skill_get_index(skill_id) +struct s_skill_db **skill_db; /// Skill DB +static uint16 skill_num; /// Skill count, also as last index +#define skill_next_idx() ( skill_num++ ) /// Macro to get&increase last skill number/index +static uint16 skill_db_create(uint16 skill_id); + static struct eri *skill_unit_ers = NULL; //For handling skill_unit's [Skotlex] static struct eri *skill_timer_ers = NULL; //For handling skill_timerskills [Skotlex] static DBMap* bowling_db = NULL; // int mob_id -> struct mob_data* @@ -71,8 +78,6 @@ struct skill_usave { uint16 skill_id, skill_lv; }; -struct s_skill_db skill_db[MAX_SKILL_DB]; - struct s_skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB]; static unsigned short skill_produce_count; @@ -137,7 +142,15 @@ static inline int splash_target(struct block_list* bl) { #endif } -/// Returns the id of the skill, or 0 if not found. +uint16 SKILL_MAX_DB(void) { + return skill_num; +} + +/** + * Get skill id from name + * @param name + * @return Skill ID of the skill, or 0 if not found. + **/ int skill_name2id(const char* name) { if( name == NULL ) return 0; @@ -145,39 +158,60 @@ int skill_name2id(const char* name) { return strdb_iget(skilldb_name2id, name); } -/// Maps skill ids to skill db offsets. -/// Returns the skill's array index, or 0 (Unknown Skill). -int skill_get_index( uint16 skill_id ) { - // avoid ranges reserved for mapping guild/homun/mercenary skills - if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX) - || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX) - || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX) - || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) - return 0; - - // map skill id to skill db index - if( skill_id >= GD_SKILLBASE ) - skill_id = GD_SKILLRANGEMIN + skill_id - GD_SKILLBASE; - else if( skill_id >= EL_SKILLBASE ) - skill_id = EL_SKILLRANGEMIN + skill_id - EL_SKILLBASE; - else if( skill_id >= MC_SKILLBASE ) - skill_id = MC_SKILLRANGEMIN + skill_id - MC_SKILLBASE; - else if( skill_id >= HM_SKILLBASE ) - skill_id = HM_SKILLRANGEMIN + skill_id - HM_SKILLBASE; - - // validate result - if( !skill_id || skill_id >= MAX_SKILL_DB ) - return 0; - - return skill_id; +/** + * Get Skill ID from Skill Index + * @param idx + * @return Skill ID or 0 if not found + **/ +uint16 skill_idx2id(uint16 idx) { + if (idx < SKILL_MAX_DB() && skill_db[idx]) + return skill_db[idx]->nameid; + return 0; } +/** + * Get skill index from skill_db array. The index is also being used for skill lookup in mmo_charstatus::skill[] + * @param skill_id + * @param silent If Skill is undefined, show error message! + * @return Skill Index or 0 if not found/unset + **/ +int skill_get_index_( uint16 skill_id, bool silent ) { + uint16 idx = (uint16)uidb_iget(skilldb_id2idx, skill_id); + if (!idx && skill_id != 0 && !silent) + ShowError("Skill '%d' is undefined!\n", skill_id); + return idx; +} + +/** + * Check if skill is set yet. If not, create new one (for skill_db files reading purpose) + * @param skill_id + * @return Skill index + **/ +static uint16 skill_db_isset(uint16 skill_id, const char *func) { + uint16 idx = skill_get_index2(skill_id); + if (idx || idx == skill_id) + return idx; + ShowWarning("%s: Skill '%d' isn't created in 'skill_db.txt' yet. Creating dummy value...\n", func, skill_id); + idx = skill_db_create(skill_id); + return idx; +} + +/** + * Get Skill name + * @param skill_id + * @return AEGIS Skill name + **/ const char* skill_get_name( uint16 skill_id ) { - return skill_db[skill_get_index(skill_id)].name; + return skill_db[skill_get_index(skill_id)]->name; } +/** + * Get Skill name + * @param skill_id + * @return English Skill name + **/ const char* skill_get_desc( uint16 skill_id ) { - return skill_db[skill_get_index(skill_id)].desc; + return skill_db[skill_get_index(skill_id)]->desc; } /// out of bounds error checking [celest] @@ -186,70 +220,87 @@ static void skill_chk(uint16 *skill_id) { } // checks/adjusts level static void skill_chk2(uint16 *skill_lv) { - *skill_lv = (*skill_lv < 1) ? 1 : (*skill_lv > MAX_SKILL_LEVEL) ? MAX_SKILL_LEVEL : *skill_lv; + *skill_lv = cap_value(*skill_lv, 1, MAX_SKILL_LEVEL); } // checks/adjusts index. make sure we don't use negative index static void skill_chk3(int *idx) { if (*idx < 0) *idx = 0; } -#define skill_get(var,id) { skill_chk(&id); if(!id) return 0; return var; } -#define skill_get2(var,id,lv) { skill_chk(&id); if (!id) return 0; skill_chk2(&lv); return var; } -#define skill_get3(var,id,x) { skill_chk(&id); if (!id) return 0; skill_chk3(&x); return var; } +#define skill_get(id,var) { skill_chk(&id); if (!id) return 0; return var; } +#define skill_get2(id, lv, arrvar) do {\ + int idx;\ + skill_chk(&(id));\ + if (!(id))\ + return 0;\ + idx = min((lv), MAX_SKILL_LEVEL) - 1;\ + if ((lv) > MAX_SKILL_LEVEL && (arrvar)[idx] > 1) {\ + int lv__ = (lv);\ + (lv) = skill_db[(id)]->max-1;\ + return ((arrvar)[(lv)] + ((lv__-(lv))/2));\ + }\ + return ((arrvar)[idx]);\ +} while(0) +#define skill_get3(id,x,var) { skill_chk(&id); if (!id) return 0; skill_chk3(&x); return var; } // Skill DB -int skill_get_hit( uint16 skill_id ) { skill_get (skill_db[skill_id].hit, skill_id); } -int skill_get_inf( uint16 skill_id ) { skill_get (skill_db[skill_id].inf, skill_id); } -int skill_get_ele( uint16 skill_id , uint16 skill_lv ) { skill_get2 (skill_db[skill_id].element[skill_lv-1], skill_id, skill_lv); } -int skill_get_nk( uint16 skill_id ) { skill_get (skill_db[skill_id].nk, skill_id); } -int skill_get_max( uint16 skill_id ) { skill_get (skill_db[skill_id].max, skill_id); } -int skill_get_range( uint16 skill_id , uint16 skill_lv ) { skill_get2 (skill_db[skill_id].range[skill_lv-1], skill_id, skill_lv); } -int skill_get_splash( uint16 skill_id , uint16 skill_lv ) { skill_get2 ( (skill_db[skill_id].splash[skill_lv-1]>=0?skill_db[skill_id].splash[skill_lv-1]:AREA_SIZE), skill_id, skill_lv); } -int skill_get_num( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].num[skill_lv-1], skill_id, skill_lv); } -int skill_get_cast( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].cast[skill_lv-1], skill_id, skill_lv); } -int skill_get_delay( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].delay[skill_lv-1], skill_id, skill_lv); } -int skill_get_walkdelay( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].walkdelay[skill_lv-1], skill_id, skill_lv); } -int skill_get_time( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].upkeep_time[skill_lv-1], skill_id, skill_lv); } -int skill_get_time2( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].upkeep_time2[skill_lv-1], skill_id, skill_lv); } -int skill_get_castdef( uint16 skill_id ) { skill_get (skill_db[skill_id].cast_def_rate, skill_id); } -int skill_get_inf2( uint16 skill_id ) { skill_get (skill_db[skill_id].inf2, skill_id); } -int skill_get_inf3( uint16 skill_id ) { skill_get (skill_db[skill_id].inf3, skill_id); } -int skill_get_castcancel( uint16 skill_id ) { skill_get (skill_db[skill_id].castcancel, skill_id); } -int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].maxcount[skill_lv-1], skill_id, skill_lv); } -int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].blewcount[skill_lv-1], skill_id, skill_lv); } -int skill_get_castnodex( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].castnodex[skill_lv-1], skill_id, skill_lv); } -int skill_get_delaynodex( uint16 skill_id ,uint16 skill_lv ){ skill_get2 (skill_db[skill_id].delaynodex[skill_lv-1], skill_id, skill_lv); } -int skill_get_nocast ( uint16 skill_id ) { skill_get (skill_db[skill_id].nocast, skill_id); } -int skill_get_type( uint16 skill_id ) { skill_get (skill_db[skill_id].skill_type, skill_id); } -int skill_get_unit_id ( uint16 skill_id, int flag ){ skill_get3 (skill_db[skill_id].unit_id[flag], skill_id, flag); } -int skill_get_unit_interval( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_interval, skill_id); } -int skill_get_unit_range( uint16 skill_id, uint16 skill_lv ){ skill_get2 (skill_db[skill_id].unit_range[skill_lv-1], skill_id, skill_lv); } -int skill_get_unit_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BCT_ALL, skill_id); } -int skill_get_unit_bl_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BL_ALL, skill_id); } -int skill_get_unit_flag( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_flag, skill_id); } -int skill_get_unit_layout_type( uint16 skill_id ,uint16 skill_lv ){ skill_get2 (skill_db[skill_id].unit_layout_type[skill_lv-1], skill_id, skill_lv); } -int skill_get_cooldown( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_db[skill_id].cooldown[skill_lv-1], skill_id, skill_lv); } +int skill_get_hit( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->hit); } +int skill_get_inf( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->inf); } +int skill_get_ele( uint16 skill_id , uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->element); } +int skill_get_nk( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->nk); } +int skill_get_max( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->max); } +int skill_get_range( uint16 skill_id , uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->range); } +int skill_get_splash_( uint16 skill_id , uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->splash); } +int skill_get_num( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->num); } +int skill_get_cast( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->cast); } +int skill_get_delay( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->delay); } +int skill_get_walkdelay( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->walkdelay); } +int skill_get_time( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->upkeep_time); } +int skill_get_time2( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->upkeep_time2); } +int skill_get_castdef( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->cast_def_rate); } +int skill_get_inf2( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->inf2); } +int skill_get_inf3( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->inf3); } +int skill_get_castcancel( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->castcancel); } +int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->maxcount); } +int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->blewcount); } +int skill_get_castnodex( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->castnodex); } +int skill_get_delaynodex( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->delaynodex); } +int skill_get_nocast ( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->nocast); } +int skill_get_type( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->skill_type); } +int skill_get_unit_id ( uint16 skill_id, int flag ) { skill_get (skill_id, skill_db[skill_id]->unit_id[flag]); } +int skill_get_unit_interval( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->unit_interval); } +int skill_get_unit_range( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->unit_range); } +int skill_get_unit_target( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->unit_target&BCT_ALL); } +int skill_get_unit_bl_target( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->unit_target&BL_ALL); } +int skill_get_unit_flag( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->unit_flag); } +int skill_get_unit_layout_type( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->unit_layout_type); } +int skill_get_cooldown( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->cooldown); } #ifdef RENEWAL_CAST -int skill_get_fixed_cast( uint16 skill_id ,uint16 skill_lv ){ skill_get2 (skill_db[skill_id].fixed_cast[skill_lv-1], skill_id, skill_lv); } +int skill_get_fixed_cast( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->fixed_cast); } #endif // Skill requirements -int skill_get_hp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.hp[skill_lv-1], skill_id, skill_lv); } -int skill_get_mhp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.mhp[skill_lv-1], skill_id, skill_lv); } -int skill_get_sp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.sp[skill_lv-1], skill_id, skill_lv); } -int skill_get_hp_rate( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.hp_rate[skill_lv-1], skill_id, skill_lv); } -int skill_get_sp_rate( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.sp_rate[skill_lv-1], skill_id, skill_lv); } -int skill_get_zeny( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.zeny[skill_lv-1], skill_id, skill_lv); } -int skill_get_weapontype( uint16 skill_id ) { skill_get (skill_db[skill_id].require.weapon, skill_id); } -int skill_get_ammotype( uint16 skill_id ) { skill_get (skill_db[skill_id].require.ammo, skill_id); } -int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.ammo_qty[skill_lv-1], skill_id, skill_lv); } -int skill_get_state( uint16 skill_id ) { skill_get (skill_db[skill_id].require.state, skill_id); } -//int skill_get_status( uint16 skill_id, int idx ) { skill_get3 (skill_db[skill_id].require.status[idx], skill_id, idx); } -int skill_get_status_count( uint16 skill_id ) { skill_get (skill_db[skill_id].require.status_count, skill_id); } -int skill_get_spiritball( uint16 skill_id, uint16 skill_lv ){ skill_get2 (skill_db[skill_id].require.spiritball[skill_lv-1], skill_id, skill_lv); } -int skill_get_itemid( uint16 skill_id, int idx ) { skill_get3 (skill_db[skill_id].require.itemid[idx], skill_id, idx); } -int skill_get_itemqty( uint16 skill_id, int idx ) { skill_get3 (skill_db[skill_id].require.amount[idx], skill_id, idx); } -int skill_get_itemeq( uint16 skill_id, int idx ) { skill_get3 (skill_db[skill_id].require.eqItem[idx], skill_id, idx); } +int skill_get_hp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.hp); } +int skill_get_mhp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.mhp); } +int skill_get_sp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.sp); } +int skill_get_hp_rate( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.hp_rate); } +int skill_get_sp_rate( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.sp_rate); } +int skill_get_zeny( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.zeny); } +int skill_get_weapontype( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->require.weapon); } +int skill_get_ammotype( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->require.ammo); } +int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.ammo_qty); } +int skill_get_state( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->require.state); } +int skill_get_status_count( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->require.status_count); } +int skill_get_spiritball( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.spiritball); } +int skill_get_itemid( uint16 skill_id, int idx ) { skill_get3 (skill_id, idx, skill_db[skill_id]->require.itemid[idx]); } +int skill_get_itemqty( uint16 skill_id, int idx ) { skill_get3 (skill_id, idx, skill_db[skill_id]->require.amount[idx]); } +int skill_get_itemeq( uint16 skill_id, int idx ) { skill_get3 (skill_id, idx, skill_db[skill_id]->require.eqItem[idx]); } +int skill_get_splash( uint16 skill_id , uint16 skill_lv ) { + int splash = skill_get_splash_(skill_id, skill_lv); + if (splash < 0) + return AREA_SIZE; + return splash; +} int skill_tree_get_max(uint16 skill_id, int b_class) { @@ -455,16 +506,14 @@ int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 sk * @return 0 - Cannot be copied; 1 - Can be copied by Plagiarism 2 - Can be copied by Reproduce * @author Aru - for previous check; Jobbie for class restriction idea; Cydh expands the copyable skill */ -static char skill_isCopyable(struct map_session_data *sd, uint16 skill_id) { - uint16 idx = skill_get_index(skill_id); - +static int8 skill_isCopyable(struct map_session_data *sd, uint16 skill_idx) { // Only copy skill that player doesn't have or the skill is old clone - if (sd->status.skill[idx].id != 0 && sd->status.skill[idx].flag != SKILL_FLAG_PLAGIARIZED) + if (sd->status.skill[skill_idx].id != 0 && sd->status.skill[skill_idx].flag != SKILL_FLAG_PLAGIARIZED) return 0; // Check if the skill is copyable by class if (!pc_has_permission(sd,PC_PERM_ALL_SKILL)) { - uint16 job_allowed = skill_db[idx].copyable.joballowed; + uint16 job_allowed = skill_db[skill_idx]->copyable.joballowed; while (1) { if (job_allowed&0x01 && sd->status.class_ == JOB_ROGUE) break; if (job_allowed&0x02 && sd->status.class_ == JOB_STALKER) break; @@ -477,11 +526,11 @@ static char skill_isCopyable(struct map_session_data *sd, uint16 skill_id) { } //Plagiarism only able to copy skill while SC_PRESERVE is not active and skill is copyable by Plagiarism - if (skill_db[idx].copyable.option&1 && pc_checkskill(sd,RG_PLAGIARISM) && !sd->sc.data[SC_PRESERVE]) + if (skill_db[skill_idx]->copyable.option&1 && pc_checkskill(sd,RG_PLAGIARISM) && !sd->sc.data[SC_PRESERVE]) return 1; //Reproduce can copy skill if SC__REPRODUCE is active and the skill is copyable by Reproduce - if (skill_db[idx].copyable.option&2 && pc_checkskill(sd,SC_REPRODUCE) && &sd->sc && sd->sc.data[SC__REPRODUCE] && sd->sc.data[SC__REPRODUCE]->val1) + if (skill_db[skill_idx]->copyable.option&2 && pc_checkskill(sd,SC_REPRODUCE) && &sd->sc && sd->sc.data[SC__REPRODUCE] && sd->sc.data[SC__REPRODUCE]->val1) return 2; return 0; @@ -2500,35 +2549,38 @@ bool skill_is_combo(uint16 skill_id) { void skill_combo_toogle_inf(struct block_list* bl, uint16 skill_id, int inf){ TBL_PC *sd = BL_CAST(BL_PC, bl); switch (skill_id) { - case MH_MIDNIGHT_FRENZY: - case MH_EQC:{ - int skill_id2 = ((skill_id==MH_EQC)?MH_TINDER_BREAKER:MH_SONIC_CRAW); - int idx = skill_id2 - HM_SKILLBASE; - int flag = (inf?SKILL_FLAG_TMP_COMBO:SKILL_FLAG_PERMANENT); - TBL_HOM *hd = BL_CAST(BL_HOM, bl); - sd = hd->master; - hd->homunculus.hskill[idx].flag= flag; - if(sd) clif_homskillinfoblock(sd); //refresh info //@FIXME we only want to refresh one skill - } - break; - case MO_COMBOFINISH: - case CH_TIGERFIST: - case CH_CHAINCRUSH: - if (sd) clif_skillinfo(sd,MO_EXTREMITYFIST, inf); - break; - case TK_JUMPKICK: - if (sd) clif_skillinfo(sd,TK_JUMPKICK, inf); - break; - case MO_TRIPLEATTACK: - if (sd && pc_checkskill(sd, SR_DRAGONCOMBO) > 0) - clif_skillinfo(sd,SR_DRAGONCOMBO, inf); - break; - case SR_FALLENEMPIRE: - if (sd){ - clif_skillinfo(sd,SR_GATEOFHELL, inf); - clif_skillinfo(sd,SR_TIGERCANNON, inf); - } - break; + case MH_MIDNIGHT_FRENZY: + case MH_EQC: + { + int skill_id2 = ((skill_id==MH_EQC)?MH_TINDER_BREAKER:MH_SONIC_CRAW); + short idx = hom_skill_get_index(skill_id2); + int flag = (inf?SKILL_FLAG_TMP_COMBO:SKILL_FLAG_PERMANENT); + TBL_HOM *hd = BL_CAST(BL_HOM, bl); + if (idx == -1) + break; + sd = hd->master; + hd->homunculus.hskill[idx].flag= flag; + if(sd) clif_homskillinfoblock(sd); //refresh info //@FIXME we only want to refresh one skill + } + break; + case MO_COMBOFINISH: + case CH_TIGERFIST: + case CH_CHAINCRUSH: + if (sd) clif_skillinfo(sd,MO_EXTREMITYFIST, inf); + break; + case TK_JUMPKICK: + if (sd) clif_skillinfo(sd,TK_JUMPKICK, inf); + break; + case MO_TRIPLEATTACK: + if (sd && pc_checkskill(sd, SR_DRAGONCOMBO) > 0) + clif_skillinfo(sd,SR_DRAGONCOMBO, inf); + break; + case SR_FALLENEMPIRE: + if (sd){ + clif_skillinfo(sd,SR_GATEOFHELL, inf); + clif_skillinfo(sd,SR_TIGERCANNON, inf); + } + break; } } @@ -2686,10 +2738,10 @@ static void skill_do_copy(struct block_list* src,struct block_list *bl, uint16 s if (!(idx = skill_get_index(skill_id))) return; - switch (skill_isCopyable(tsd,skill_id)) { + switch (skill_isCopyable(tsd,idx)) { case 1: //Copied by Plagiarism { - if (tsd->cloneskill_idx >= 0 && tsd->status.skill[tsd->cloneskill_idx].flag == SKILL_FLAG_PLAGIARIZED) { + if (tsd->cloneskill_idx > 0 && tsd->status.skill[tsd->cloneskill_idx].flag == SKILL_FLAG_PLAGIARIZED) { tsd->status.skill[tsd->cloneskill_idx].id = 0; tsd->status.skill[tsd->cloneskill_idx].lv = 0; tsd->status.skill[tsd->cloneskill_idx].flag = SKILL_FLAG_PERMANENT; @@ -2709,7 +2761,7 @@ static void skill_do_copy(struct block_list* src,struct block_list *bl, uint16 s //Already did SC check //Skill level copied depends on Reproduce skill that used lv = (tsc) ? tsc->data[SC__REPRODUCE]->val1 : 1; - if( tsd->reproduceskill_idx >= 0 && tsd->status.skill[tsd->reproduceskill_idx].flag == SKILL_FLAG_PLAGIARIZED ) { + if( tsd->reproduceskill_idx > 0 && tsd->status.skill[tsd->reproduceskill_idx].flag == SKILL_FLAG_PLAGIARIZED ) { tsd->status.skill[tsd->reproduceskill_idx].id = 0; tsd->status.skill[tsd->reproduceskill_idx].lv = 0; tsd->status.skill[tsd->reproduceskill_idx].flag = SKILL_FLAG_PERMANENT; @@ -3183,7 +3235,7 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * map_freeblock_lock(); - if (bl->type == BL_PC && skill_id && skill_get_index(skill_id) > 0 && skill_db[skill_get_index(skill_id)].copyable.option && //Only copy skill that copyable [Cydh] + if (bl->type == BL_PC && skill_id && skill_get_index(skill_id) > 0 && skill_db[skill_get_index(skill_id)]->copyable.option && //Only copy skill that copyable [Cydh] dmg.flag&BF_SKILL && dmg.damage+dmg.damage2 > 0 && damage < status_get_hp(bl)) //Cannot copy skills if the blow will kill you. [Skotlex] skill_do_copy(src,bl,skill_id,skill_lv); @@ -3495,8 +3547,8 @@ static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 } //Check the additional range [Cydh] - if (isNearNPC && skill_db[skill_get_index(skill_id)].unit_nonearnpc_range) - range += skill_db[skill_get_index(skill_id)].unit_nonearnpc_range; + if (isNearNPC && skill_db[skill_get_index(skill_id)]->unit_nonearnpc_range) + range += skill_db[skill_get_index(skill_id)]->unit_nonearnpc_range; if (!isNearNPC) { //Doesn't check the NPC range //If the caster is a monster/NPC, only check for players. Otherwise just check characters @@ -3528,8 +3580,6 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill, int uint16 idx; int itemid[MAX_SKILL_ITEM_REQUIRE],amount[ARRAYLENGTH(itemid)],index[ARRAYLENGTH(itemid)]; - if( lv < 1 || lv > MAX_SKILL_LEVEL ) - return 0; nullpo_ret(bl); switch( bl->type ) @@ -3542,18 +3592,20 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill, int if( (idx = skill_get_index(skill)) == 0 ) return 0; + lv = cap_value(lv, 1, MAX_SKILL_LEVEL); + // Requirements for( i = 0; i < ARRAYLENGTH(itemid); i++ ) { - itemid[i] = skill_db[idx].require.itemid[i]; - amount[i] = skill_db[idx].require.amount[i]; + itemid[i] = skill_db[idx]->require.itemid[i]; + amount[i] = skill_db[idx]->require.amount[i]; } - hp = skill_db[idx].require.hp[lv-1]; - sp = skill_db[idx].require.sp[lv-1]; - hp_rate = skill_db[idx].require.hp_rate[lv-1]; - sp_rate = skill_db[idx].require.sp_rate[lv-1]; - state = skill_db[idx].require.state; - if( (mhp = skill_db[idx].require.mhp[lv-1]) > 0 ) + hp = skill_db[idx]->require.hp[lv-1]; + sp = skill_db[idx]->require.sp[lv-1]; + hp_rate = skill_db[idx]->require.hp_rate[lv-1]; + sp_rate = skill_db[idx]->require.sp_rate[lv-1]; + state = skill_db[idx]->require.state; + if( (mhp = skill_db[idx]->require.mhp[lv-1]) > 0 ) hp += (status->max_hp * mhp) / 100; if( hp_rate > 0 ) hp += (status->hp * hp_rate) / 100; @@ -6749,7 +6801,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui // custom hack to make the mob display the skill, because these skills don't show the skill use text themselves //NOTE: mobs don't have the sprite animation that is used when performing this skill (will cause glitches) char temp[70]; - snprintf(temp, sizeof(temp), "%s : %s !!",md->name,skill_db[skill_get_index(skill_id)].desc); + snprintf(temp, sizeof(temp), "%s : %s !!",md->name,skill_db[skill_get_index(skill_id)]->desc); clif_disp_overhead(&md->bl,temp); } break; @@ -14098,8 +14150,9 @@ bool skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_i return false; } } - if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL ) - return false; + + //if (skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL) + // return false; require = skill_get_requirement(sd,skill_id,skill_lv); @@ -15173,23 +15226,23 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 idx = skill_get_index(skill_id); if( idx == 0 ) // invalid skill id - return req; - if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL ) return req; + skill_lv = cap_value(skill_lv, 1, MAX_SKILL_LEVEL); + status = &sd->battle_status; - req.hp = skill_db[idx].require.hp[skill_lv-1]; - hp_rate = skill_db[idx].require.hp_rate[skill_lv-1]; + req.hp = skill_db[idx]->require.hp[skill_lv-1]; + hp_rate = skill_db[idx]->require.hp_rate[skill_lv-1]; if(hp_rate > 0) req.hp += (status->hp * hp_rate)/100; else req.hp += (status->max_hp * (-hp_rate))/100; - req.sp = skill_db[idx].require.sp[skill_lv-1]; + req.sp = skill_db[idx]->require.sp[skill_lv-1]; if((sd->skill_id_old == BD_ENCORE) && skill_id == sd->skill_id_dance) req.sp /= 2; - sp_rate = skill_db[idx].require.sp_rate[skill_lv-1]; + sp_rate = skill_db[idx]->require.sp_rate[skill_lv-1]; if(sp_rate > 0) req.sp += (status->sp * sp_rate)/100; else @@ -15219,7 +15272,7 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 req.sp -= req.sp * sc->data[SC_TELEKINESIS_INTENSE]->val2 / 100; } - req.zeny = skill_db[idx].require.zeny[skill_lv-1]; + req.zeny = skill_db[idx]->require.zeny[skill_lv-1]; if( sc && sc->data[SC__UNLUCKY] ) { if(sc->data[SC__UNLUCKY]->val1 < 3) @@ -15228,33 +15281,33 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 req.zeny += 1000; } - req.spiritball = skill_db[idx].require.spiritball[skill_lv-1]; - req.state = skill_db[idx].require.state; + req.spiritball = skill_db[idx]->require.spiritball[skill_lv-1]; + req.state = skill_db[idx]->require.state; - req.mhp = skill_db[idx].require.mhp[skill_lv-1]; - req.weapon = skill_db[idx].require.weapon; - req.ammo_qty = skill_db[idx].require.ammo_qty[skill_lv-1]; + req.mhp = skill_db[idx]->require.mhp[skill_lv-1]; + req.weapon = skill_db[idx]->require.weapon; + req.ammo_qty = skill_db[idx]->require.ammo_qty[skill_lv-1]; if (req.ammo_qty) - req.ammo = skill_db[idx].require.ammo; + req.ammo = skill_db[idx]->require.ammo; if (!req.ammo && skill_id && skill_isammotype(sd, skill_id)) { //Assume this skill is using the weapon, therefore it requires arrows. - req.ammo = 0xFFFFFFFF; //Enable use on all ammo types. + req.ammo = AMMO_TYPE_ALL; //Enable use on all ammo types. req.ammo_qty = 1; } - req.status_count = skill_db[idx].require.status_count; - req.status = skill_db[idx].require.status; - req.eqItem_count = skill_db[idx].require.eqItem_count; - req.eqItem = skill_db[idx].require.eqItem; + req.status_count = skill_db[idx]->require.status_count; + req.status = skill_db[idx]->require.status; + req.eqItem_count = skill_db[idx]->require.eqItem_count; + req.eqItem = skill_db[idx]->require.eqItem; switch( skill_id ) { /* Skill level-dependent checks */ case NC_SHAPESHIFT: case NC_REPAIR: //NOTE: Please make sure Magic_Gear_Fuel in the last position in skill_require_db.txt - req.itemid[1] = skill_db[idx].require.itemid[MAX_SKILL_ITEM_REQUIRE-1]; - req.amount[1] = skill_db[idx].require.amount[MAX_SKILL_ITEM_REQUIRE-1]; + req.itemid[1] = skill_db[idx]->require.itemid[MAX_SKILL_ITEM_REQUIRE-1]; + req.amount[1] = skill_db[idx]->require.amount[MAX_SKILL_ITEM_REQUIRE-1]; case GN_FIRE_EXPANSION: case SO_SUMMON_AGNI: case SO_SUMMON_AQUA: @@ -15265,8 +15318,8 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 case SO_WIND_INSIGNIA: case SO_EARTH_INSIGNIA: case WZ_FIREPILLAR: // no gems required at level 1-5 [celest] - req.itemid[0] = skill_db[idx].require.itemid[min(skill_lv-1,MAX_SKILL_ITEM_REQUIRE-1)]; - req.amount[0] = skill_db[idx].require.amount[min(skill_lv-1,MAX_SKILL_ITEM_REQUIRE-1)]; + req.itemid[0] = skill_db[idx]->require.itemid[min(skill_lv-1,MAX_SKILL_ITEM_REQUIRE-1)]; + req.amount[0] = skill_db[idx]->require.amount[min(skill_lv-1,MAX_SKILL_ITEM_REQUIRE-1)]; level_dependent = true; /* Normal skill requirements and gemstone checks */ @@ -15286,17 +15339,17 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 continue; break; case AB_ADORAMUS: - if( itemid_isgemstone(skill_db[idx].require.itemid[i]) && (sd->special_state.no_gemstone == 2 || skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 2)) ) + if( itemid_isgemstone(skill_db[idx]->require.itemid[i]) && (sd->special_state.no_gemstone == 2 || skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 2)) ) continue; break; case WL_COMET: - if( itemid_isgemstone(skill_db[idx].require.itemid[i]) && (sd->special_state.no_gemstone == 2 || skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 0)) ) + if( itemid_isgemstone(skill_db[idx]->require.itemid[i]) && (sd->special_state.no_gemstone == 2 || skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 0)) ) continue; break; } - req.itemid[i] = skill_db[idx].require.itemid[i]; - req.amount[i] = skill_db[idx].require.amount[i]; + req.itemid[i] = skill_db[idx]->require.itemid[i]; + req.amount[i] = skill_db[idx]->require.amount[i]; if( skill_id >= HT_SKIDTRAP && skill_id <= HT_TALKIEBOX && pc_checkskill(sd, RA_RESEARCHTRAP) > 0){ int16 itIndex; @@ -15418,7 +15471,7 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 //Check if player is using the copied skill [Cydh] if (sd->status.skill[idx].flag == SKILL_FLAG_PLAGIARIZED) { - uint16 req_opt = skill_db[idx].copyable.req_opt; + uint16 req_opt = skill_db[idx]->copyable.req_opt; if (req_opt&0x0001) req.hp = 0; if (req_opt&0x0002) req.mhp = 0; @@ -15452,7 +15505,7 @@ int skill_castfix(struct block_list *bl, uint16 skill_id, uint16 skill_lv) { sd = BL_CAST(BL_PC, bl); // calculate base cast time (reduced by dex) - if( !(skill_get_castnodex(skill_id, skill_lv)&1) ) { + if( !(skill_get_castnodex(skill_id)&1) ) { int scale = battle_config.castrate_dex_scale - status_get_dex(bl); if( scale > 0 ) // not instant cast time = time * scale / battle_config.castrate_dex_scale; @@ -15461,7 +15514,7 @@ int skill_castfix(struct block_list *bl, uint16 skill_id, uint16 skill_lv) { } // calculate cast time reduced by item/card bonuses - if( !(skill_get_castnodex(skill_id, skill_lv)&4) && sd ) + if( !(skill_get_castnodex(skill_id)&4) && sd ) { int i; if( sd->castrate != 100 ) @@ -15548,7 +15601,7 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 struct map_session_data *sd = BL_CAST(BL_PC,bl); int fixed = skill_get_fixed_cast(skill_id, skill_lv); short fixcast_r = 0; - uint8 i = 0, flag = skill_get_castnodex(skill_id, skill_lv); + uint8 i = 0, flag = skill_get_castnodex(skill_id); #define FIXEDCASTRATE2(val) ( FIXEDCASTRATE(fixcast_r,(val)) ) @@ -15678,7 +15731,7 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 *------------------------------------------*/ int skill_delayfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv) { - int delaynodex = skill_get_delaynodex(skill_id, skill_lv); + int delaynodex = skill_get_delaynodex(skill_id); int time = skill_get_delay(skill_id, skill_lv); struct map_session_data *sd; struct status_change *sc = status_get_sc(bl); @@ -17083,7 +17136,7 @@ struct skill_unit_group* skill_id2group(int group_id) { return (struct skill_unit_group*)idb_get(skillunit_group_db, group_id); } -static int skill_unit_group_newid = MAX_SKILL_DB; /// Skill Unit Group ID +static int skill_unit_group_newid = MAX_SKILL; /// Skill Unit Group ID /** * Returns a new group_id that isn't being used in skillunit_group_db. @@ -17091,14 +17144,14 @@ static int skill_unit_group_newid = MAX_SKILL_DB; /// Skill Unit Group ID */ static int skill_get_new_group_id(void) { - if( skill_unit_group_newid >= MAX_SKILL_DB && skill_id2group(skill_unit_group_newid) == NULL ) + if( skill_unit_group_newid >= MAX_SKILL && skill_id2group(skill_unit_group_newid) == NULL ) return skill_unit_group_newid++;// available {// find next id int base_id = skill_unit_group_newid; while( base_id != ++skill_unit_group_newid ) { - if( skill_unit_group_newid < MAX_SKILL_DB ) - skill_unit_group_newid = MAX_SKILL_DB; + if( skill_unit_group_newid < MAX_SKILL ) + skill_unit_group_newid = MAX_SKILL; if( skill_id2group(skill_unit_group_newid) == NULL ) return skill_unit_group_newid++;// available } @@ -18839,7 +18892,8 @@ void skill_spellbook (struct map_session_data *sd, unsigned short nameid) { } int skill_select_menu(struct map_session_data *sd,uint16 skill_id) { - int id, lv, prob, aslvl = 0; + int lv, prob, aslvl = 0; + uint16 sk_idx = 0; nullpo_ret(sd); if (sd->sc.data[SC_STOP]) { @@ -18847,15 +18901,18 @@ int skill_select_menu(struct map_session_data *sd,uint16 skill_id) { status_change_end(&sd->bl,SC_STOP,INVALID_TIMER); } - if( !(skill_get_inf2(sd->status.skill[skill_id].id)&INF2_AUTOSHADOWSPELL) || (id = sd->status.skill[skill_id].id) == 0 || sd->status.skill[skill_id].flag != SKILL_FLAG_PLAGIARIZED ) { - clif_skill_fail(sd,SC_AUTOSHADOWSPELL,0,0); + if (!skill_id || (sk_idx = skill_get_index(skill_id))) + return 0; + + if( !(skill_get_inf2(skill_id)&INF2_AUTOSHADOWSPELL) || sd->status.skill[sk_idx].id == 0 || sd->status.skill[sk_idx].flag != SKILL_FLAG_PLAGIARIZED ) { + clif_skill_fail(sd,SC_AUTOSHADOWSPELL,USESKILL_FAIL_LEVEL,0); return 0; } lv = (aslvl + 1) / 2; // The level the skill will be autocasted - lv = min(lv,sd->status.skill[skill_id].lv); + lv = min(lv,sd->status.skill[sk_idx].lv); prob = (aslvl >= 10) ? 15 : (30 - 2 * aslvl); // Probability at level 10 was increased to 15. - sc_start4(&sd->bl,&sd->bl,SC__AUTOSHADOWSPELL,100,id,lv,prob,0,skill_get_time(SC_AUTOSHADOWSPELL,aslvl)); + sc_start4(&sd->bl,&sd->bl,SC__AUTOSHADOWSPELL,100,skill_id,lv,prob,0,skill_get_time(SC_AUTOSHADOWSPELL,aslvl)); return 0; } @@ -19101,7 +19158,7 @@ int skill_blockhomun_end(int tid, unsigned int tick, int id, intptr_t data) //[o { struct homun_data *hd = (TBL_HOM*) map_id2bl(id); - if (data <= 0 || data >= MAX_SKILL) + if (data <= 0 || data >= SKILL_MAX_DB()) return 0; if (hd) @@ -19133,7 +19190,7 @@ int skill_blockmerc_end(int tid, unsigned int tick, int id, intptr_t data) //[or { struct mercenary_data *md = (TBL_MER*)map_id2bl(id); - if( data <= 0 || data >= MAX_SKILL ) + if( data <= 0 || data >= SKILL_MAX_DB() ) return 0; if( md ) @@ -19274,25 +19331,22 @@ void skill_init_unit_layout (void) { // afterwards add special ones pos = i; - for (i=0;i= HM_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) { - int skill = i; + if (!skill_db[i]->unit_id[0] || skill_db[i]->unit_layout_type[0] != -1) + continue; + skill_id = skill_idx2id(i); - if( i >= EL_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) { - skill -= EL_SKILLRANGEMIN; - skill += EL_SKILLBASE; - } - if( skill == EL_FIRE_MANTLE ) { - static const int dx[] = {-1, 0, 1, 1, 1, 0,-1,-1}; - static const int dy[] = { 1, 1, 1, 0,-1,-1,-1, 0}; - skill_unit_layout[pos].count = 8; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } + if( skill_id == EL_FIRE_MANTLE ) { + static const int dx[] = {-1, 0, 1, 1, 1, 0,-1,-1}; + static const int dy[] = { 1, 1, 1, 0,-1,-1,-1, 0}; + skill_unit_layout[pos].count = 8; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); } else { - switch (i) { + switch (skill_id) { case MG_FIREWALL: case WZ_ICEWALL: case WL_EARTHSTRAIN: @@ -19398,26 +19452,26 @@ void skill_init_unit_layout (void) { skill_unit_layout[pos].count = 4; memcpy(skill_unit_layout[pos].dx,dx1,sizeof(dx1)); memcpy(skill_unit_layout[pos].dy,dy1,sizeof(dy1)); - skill_db[i].unit_layout_type[j] = pos; + skill_db[i]->unit_layout_type[j] = pos; //lv2/3 j++; pos++; skill_unit_layout[pos].count = 8; memcpy(skill_unit_layout[pos].dx,dx2,sizeof(dx2)); memcpy(skill_unit_layout[pos].dy,dy2,sizeof(dy2)); - skill_db[i].unit_layout_type[j] = pos; - skill_db[i].unit_layout_type[++j] = pos; + skill_db[i]->unit_layout_type[j] = pos; + skill_db[i]->unit_layout_type[++j] = pos; //lv4/5 j++; pos++; skill_unit_layout[pos].count = 12; memcpy(skill_unit_layout[pos].dx,dx3,sizeof(dx3)); memcpy(skill_unit_layout[pos].dy,dy3,sizeof(dy3)); - skill_db[i].unit_layout_type[j] = pos; - skill_db[i].unit_layout_type[++j] = pos; + skill_db[i]->unit_layout_type[j] = pos; + skill_db[i]->unit_layout_type[++j] = pos; //Fill in the rest using lv 5. for (;junit_layout_type[j] = pos; //Skip, this way the check below will fail and continue to the next skill. pos++; } @@ -19438,7 +19492,7 @@ void skill_init_unit_layout (void) { if (!skill_unit_layout[pos].count) continue; for (j=0;junit_layout_type[j] = pos; pos++; } @@ -19830,49 +19884,46 @@ int skill_get_elemental_type( uint16 skill_id , uint16 skill_lv ) { static bool skill_parse_row_skilldb(char* split[], int columns, int current) {// id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description uint16 skill_id = atoi(split[0]); - uint16 idx; - if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX) - || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX) - || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX) - || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) { - ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with guild/homun/mercenary skill mapping)!\n", skill_id); - return false; + uint16 idx = skill_get_index2(skill_id); + + if (!idx) { + if (SKILL_MAX_DB() >= MAX_SKILL) { + ShowError("Cannot add new skill. Limit is reached '%d' (mmo.h::MAX_SKILL).\n", MAX_SKILL); + return false; + } + idx = skill_db_create(skill_id); } - idx = skill_get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill_split_atoi(split[1],skill_db[idx].range); - skill_db[idx].hit = atoi(split[2]); - skill_db[idx].inf = atoi(split[3]); - skill_split_atoi(split[4],skill_db[idx].element); - skill_db[idx].nk = (int)strtol(split[5], NULL, 0); - skill_split_atoi(split[6],skill_db[idx].splash); - skill_db[idx].max = atoi(split[7]); - skill_split_atoi(split[8],skill_db[idx].num); + skill_split_atoi(split[1],skill_db[idx]->range); + skill_db[idx]->hit = atoi(split[2]); + skill_db[idx]->inf = atoi(split[3]); + skill_split_atoi(split[4],skill_db[idx]->element); + skill_db[idx]->nk = (uint8)strtol(split[5], NULL, 0); + skill_split_atoi(split[6],skill_db[idx]->splash); + skill_db[idx]->max = atoi(split[7]); + skill_split_atoi(split[8],skill_db[idx]->num); if( strcmpi(split[9],"yes") == 0 ) - skill_db[idx].castcancel = 1; + skill_db[idx]->castcancel = true; else - skill_db[idx].castcancel = 0; - skill_db[idx].cast_def_rate = atoi(split[10]); - skill_db[idx].inf2 = (int)strtol(split[11], NULL, 0); - skill_split_atoi(split[12],skill_db[idx].maxcount); + skill_db[idx]->castcancel = false; + skill_db[idx]->cast_def_rate = atoi(split[10]); + skill_db[idx]->inf2 = (unsigned int)strtol(split[11], NULL, 0); + skill_split_atoi(split[12],skill_db[idx]->maxcount); if( strcmpi(split[13],"weapon") == 0 ) - skill_db[idx].skill_type = BF_WEAPON; + skill_db[idx]->skill_type = BF_WEAPON; else if( strcmpi(split[13],"magic") == 0 ) - skill_db[idx].skill_type = BF_MAGIC; + skill_db[idx]->skill_type = BF_MAGIC; else if( strcmpi(split[13],"misc") == 0 ) - skill_db[idx].skill_type = BF_MISC; + skill_db[idx]->skill_type = BF_MISC; else - skill_db[idx].skill_type = 0; - skill_split_atoi(split[14],skill_db[idx].blewcount); - skill_db[idx].inf3 = (int)strtol(split[15], NULL,0); - safestrncpy(skill_db[idx].name, trim(split[16]), sizeof(skill_db[idx].name)); - safestrncpy(skill_db[idx].desc, trim(split[17]), sizeof(skill_db[idx].desc)); - strdb_iput(skilldb_name2id, skill_db[idx].name, skill_id); + skill_db[idx]->skill_type = 0; + skill_split_atoi(split[14],skill_db[idx]->blewcount); + skill_db[idx]->inf3 = (unsigned int)strtol(split[15], NULL,0); + safestrncpy(skill_db[idx]->name, trim(split[16]), sizeof(skill_db[idx]->name)); + safestrncpy(skill_db[idx]->desc, trim(split[17]), sizeof(skill_db[idx]->desc)); + strdb_iput(skilldb_name2id, skill_db[idx]->name, skill_id); return true; } @@ -19913,16 +19964,16 @@ uint8 skill_split_atoi2(char *str, int *val, const char *delim, int min_value, u } /// Clear status data from skill requirement -static void skill_destroy_requirement(void) { - uint16 i; - for (i = 0; i < MAX_SKILL; i++) { - if (skill_db[i].require.status_count) - aFree(skill_db[i].require.status); - skill_db[i].require.status_count = 0; - if (skill_db[i].require.eqItem_count) - aFree(skill_db[i].require.eqItem); - skill_db[i].require.eqItem_count = 0; - } +static void skill_destroy_requirement(uint16 idx) { + if (skill_db[idx]->require.status_count) + aFree(skill_db[idx]->require.status); + skill_db[idx]->require.status = NULL; + skill_db[idx]->require.status_count = 0; + + if (skill_db[idx]->require.eqItem_count) + aFree(skill_db[idx]->require.eqItem); + skill_db[idx]->require.eqItem = NULL; + skill_db[idx]->require.eqItem_count = 0; } /** @@ -19934,25 +19985,24 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current) char* p; uint16 skill_id = atoi(split[0]), idx, i; - if (!(idx = skill_get_index(skill_id))) // invalid skill id - return false; + idx = skill_db_isset(atoi(split[0]), __FUNCTION__); - skill_split_atoi(split[1],skill_db[idx].require.hp); - skill_split_atoi(split[2],skill_db[idx].require.mhp); - skill_split_atoi(split[3],skill_db[idx].require.sp); - skill_split_atoi(split[4],skill_db[idx].require.hp_rate); - skill_split_atoi(split[5],skill_db[idx].require.sp_rate); - skill_split_atoi(split[6],skill_db[idx].require.zeny); + skill_split_atoi(split[1],skill_db[idx]->require.hp); + skill_split_atoi(split[2],skill_db[idx]->require.mhp); + skill_split_atoi(split[3],skill_db[idx]->require.sp); + skill_split_atoi(split[4],skill_db[idx]->require.hp_rate); + skill_split_atoi(split[5],skill_db[idx]->require.sp_rate); + skill_split_atoi(split[6],skill_db[idx]->require.zeny); //Witch weapon type are required, see doc/item_db for weapon types (View column) p = split[7]; while (p) { int l = atoi(p); if( l == 99 ) { // Any weapon - skill_db[idx].require.weapon = 0; + skill_db[idx]->require.weapon = 0; break; } else - skill_db[idx].require.weapon |= 1<require.weapon |= 1<require.ammo = AMMO_TYPE_ALL; break; } else if( l ) // 0 stands for no requirement - skill_db[idx].require.ammo |= 1<require.ammo |= 1<require.ammo_qty); - if( strcmpi(split[10],"hidden") == 0 ) skill_db[idx].require.state = ST_HIDDEN; - else if( strcmpi(split[10],"riding") == 0 ) skill_db[idx].require.state = ST_RIDING; - else if( strcmpi(split[10],"falcon") == 0 ) skill_db[idx].require.state = ST_FALCON; - else if( strcmpi(split[10],"cart") == 0 ) skill_db[idx].require.state = ST_CART; - else if( strcmpi(split[10],"shield") == 0 ) skill_db[idx].require.state = ST_SHIELD; - else if( strcmpi(split[10],"recover_weight_rate") == 0 ) skill_db[idx].require.state = ST_RECOV_WEIGHT_RATE; - else if( strcmpi(split[10],"move_enable") == 0 ) skill_db[idx].require.state = ST_MOVE_ENABLE; - else if( strcmpi(split[10],"water") == 0 ) skill_db[idx].require.state = ST_WATER; - else if( strcmpi(split[10],"dragon") == 0 ) skill_db[idx].require.state = ST_RIDINGDRAGON; - else if( strcmpi(split[10],"warg") == 0 ) skill_db[idx].require.state = ST_WUG; - else if( strcmpi(split[10],"ridingwarg") == 0 ) skill_db[idx].require.state = ST_RIDINGWUG; - else if( strcmpi(split[10],"mado") == 0 ) skill_db[idx].require.state = ST_MADO; - else if( strcmpi(split[10],"elementalspirit") == 0 ) skill_db[idx].require.state = ST_ELEMENTALSPIRIT; - else if( strcmpi(split[10],"peco") == 0 ) skill_db[idx].require.state = ST_PECO; - else skill_db[idx].require.state = ST_NONE; // Unknown or no state + if( strcmpi(split[10],"hidden") == 0 ) skill_db[idx]->require.state = ST_HIDDEN; + else if( strcmpi(split[10],"riding") == 0 ) skill_db[idx]->require.state = ST_RIDING; + else if( strcmpi(split[10],"falcon") == 0 ) skill_db[idx]->require.state = ST_FALCON; + else if( strcmpi(split[10],"cart") == 0 ) skill_db[idx]->require.state = ST_CART; + else if( strcmpi(split[10],"shield") == 0 ) skill_db[idx]->require.state = ST_SHIELD; + else if( strcmpi(split[10],"recover_weight_rate") == 0 ) skill_db[idx]->require.state = ST_RECOV_WEIGHT_RATE; + else if( strcmpi(split[10],"move_enable") == 0 ) skill_db[idx]->require.state = ST_MOVE_ENABLE; + else if( strcmpi(split[10],"water") == 0 ) skill_db[idx]->require.state = ST_WATER; + else if( strcmpi(split[10],"dragon") == 0 ) skill_db[idx]->require.state = ST_RIDINGDRAGON; + else if( strcmpi(split[10],"warg") == 0 ) skill_db[idx]->require.state = ST_WUG; + else if( strcmpi(split[10],"ridingwarg") == 0 ) skill_db[idx]->require.state = ST_RIDINGWUG; + else if( strcmpi(split[10],"mado") == 0 ) skill_db[idx]->require.state = ST_MADO; + else if( strcmpi(split[10],"elementalspirit") == 0 ) skill_db[idx]->require.state = ST_ELEMENTALSPIRIT; + else if( strcmpi(split[10],"peco") == 0 ) skill_db[idx]->require.state = ST_PECO; + else skill_db[idx]->require.state = ST_NONE; // Unknown or no state //Status requirements //FIXME: Default entry should be -1/SC_ALL in skill_require_db.txt but it's 0/SC_STONE. trim(split[11]); if (split[11][0] != '\0' || atoi(split[11])) { int require[MAX_SKILL_STATUS_REQUIRE]; - if ((skill_db[idx].require.status_count = skill_split_atoi2(split[11], require, ":", SC_STONE, ARRAYLENGTH(require)))) { - CREATE(skill_db[idx].require.status, enum sc_type, skill_db[idx].require.status_count); - for (i = 0; i < skill_db[idx].require.status_count; i++) - skill_db[idx].require.status[i] = (sc_type)require[i]; + if ((skill_db[idx]->require.status_count = skill_split_atoi2(split[11], require, ":", SC_STONE, ARRAYLENGTH(require)))) { + CREATE(skill_db[idx]->require.status, enum sc_type, skill_db[idx]->require.status_count); + for (i = 0; i < skill_db[idx]->require.status_count; i++) + skill_db[idx]->require.status[i] = (sc_type)require[i]; } } - skill_split_atoi(split[12],skill_db[idx].require.spiritball); + skill_split_atoi(split[12],skill_db[idx]->require.spiritball); for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++ ) { - skill_db[idx].require.itemid[i] = atoi(split[13+ 2*i]); - skill_db[idx].require.amount[i] = atoi(split[14+ 2*i]); + skill_db[idx]->require.itemid[i] = atoi(split[13+ 2*i]); + skill_db[idx]->require.amount[i] = atoi(split[14+ 2*i]); } //Equipped Item requirements. @@ -20015,10 +20065,10 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current) trim(split[33]); if (split[33][0] != '\0' || atoi(split[33])) { int require[MAX_SKILL_EQUIP_REQUIRE]; - if ((skill_db[idx].require.eqItem_count = skill_split_atoi2(split[33], require, ":", 500, ARRAYLENGTH(require)))) { - CREATE(skill_db[idx].require.eqItem, uint16, skill_db[idx].require.eqItem_count); - for (i = 0; i < skill_db[idx].require.eqItem_count; i++) - skill_db[idx].require.eqItem[i] = require[i]; + if ((skill_db[idx]->require.eqItem_count = skill_split_atoi2(split[33], require, ":", 500, ARRAYLENGTH(require)))) { + CREATE(skill_db[idx]->require.eqItem, uint16, skill_db[idx]->require.eqItem_count); + for (i = 0; i < skill_db[idx]->require.eqItem_count; i++) + skill_db[idx]->require.eqItem[i] = require[i]; } } return true; @@ -20029,19 +20079,16 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current) */ static bool skill_parse_row_castdb(char* split[], int columns, int current) { - uint16 idx = skill_get_index(atoi(split[0])); + uint16 idx = skill_db_isset(atoi(split[0]), __FUNCTION__); - if( !idx ) // invalid skill id - return false; - - skill_split_atoi(split[1],skill_db[idx].cast); - skill_split_atoi(split[2],skill_db[idx].delay); - skill_split_atoi(split[3],skill_db[idx].walkdelay); - skill_split_atoi(split[4],skill_db[idx].upkeep_time); - skill_split_atoi(split[5],skill_db[idx].upkeep_time2); - skill_split_atoi(split[6],skill_db[idx].cooldown); + skill_split_atoi(split[1],skill_db[idx]->cast); + skill_split_atoi(split[2],skill_db[idx]->delay); + skill_split_atoi(split[3],skill_db[idx]->walkdelay); + skill_split_atoi(split[4],skill_db[idx]->upkeep_time); + skill_split_atoi(split[5],skill_db[idx]->upkeep_time2); + skill_split_atoi(split[6],skill_db[idx]->cooldown); #ifdef RENEWAL_CAST - skill_split_atoi(split[7],skill_db[idx].fixed_cast); + skill_split_atoi(split[7],skill_db[idx]->fixed_cast); #endif return true; } @@ -20051,14 +20098,11 @@ static bool skill_parse_row_castdb(char* split[], int columns, int current) */ static bool skill_parse_row_castnodexdb(char* split[], int columns, int current) { - uint16 idx = skill_get_index(atoi(split[0])); + uint16 idx = skill_db_isset(atoi(split[0]), __FUNCTION__); - if( !idx ) // invalid skill id - return false; - - skill_split_atoi(split[1],skill_db[idx].castnodex); + skill_db[idx]->castnodex = atoi(split[1]); if( split[2] ) // optional column - skill_split_atoi(split[2],skill_db[idx].delaynodex); + skill_db[idx]->delaynodex = atoi(split[2]); return true; } @@ -20068,12 +20112,9 @@ static bool skill_parse_row_castnodexdb(char* split[], int columns, int current) */ static bool skill_parse_row_nocastdb(char* split[], int columns, int current) { - uint16 idx = skill_get_index(atoi(split[0])); + uint16 idx = skill_db_isset(atoi(split[0]), __FUNCTION__); - if( !idx ) // invalid skill id - return false; - - skill_db[idx].nocast |= atoi(split[1]); + skill_db[idx]->nocast |= atoi(split[1]); return true; } @@ -20083,42 +20124,39 @@ static bool skill_parse_row_nocastdb(char* split[], int columns, int current) */ static bool skill_parse_row_unitdb(char* split[], int columns, int current) { - uint16 idx = skill_get_index(atoi(split[0])); + uint16 idx = skill_db_isset(atoi(split[0]), __FUNCTION__); - if( !idx ) // invalid skill id - return false; + skill_db[idx]->unit_id[0] = (uint16)strtol(split[1],NULL,16); + skill_db[idx]->unit_id[1] = (uint16)strtol(split[2],NULL,16); + skill_split_atoi(split[3],skill_db[idx]->unit_layout_type); + skill_split_atoi(split[4],skill_db[idx]->unit_range); + skill_db[idx]->unit_interval = atoi(split[5]); - skill_db[idx].unit_id[0] = strtol(split[1],NULL,16); - skill_db[idx].unit_id[1] = strtol(split[2],NULL,16); - skill_split_atoi(split[3],skill_db[idx].unit_layout_type); - skill_split_atoi(split[4],skill_db[idx].unit_range); - skill_db[idx].unit_interval = atoi(split[5]); + if( strcmpi(split[6],"noenemy")==0 ) skill_db[idx]->unit_target = BCT_NOENEMY; + else if( strcmpi(split[6],"friend")==0 ) skill_db[idx]->unit_target = BCT_NOENEMY; + else if( strcmpi(split[6],"party")==0 ) skill_db[idx]->unit_target = BCT_PARTY; + else if( strcmpi(split[6],"ally")==0 ) skill_db[idx]->unit_target = BCT_PARTY|BCT_GUILD; + else if( strcmpi(split[6],"guild")==0 ) skill_db[idx]->unit_target = BCT_GUILD; + else if( strcmpi(split[6],"all")==0 ) skill_db[idx]->unit_target = BCT_ALL; + else if( strcmpi(split[6],"enemy")==0 ) skill_db[idx]->unit_target = BCT_ENEMY; + else if( strcmpi(split[6],"self")==0 ) skill_db[idx]->unit_target = BCT_SELF; + else if( strcmpi(split[6],"sameguild")==0 ) skill_db[idx]->unit_target = BCT_GUILD|BCT_SAMEGUILD; + else if( strcmpi(split[6],"noone")==0 ) skill_db[idx]->unit_target = BCT_NOONE; + else skill_db[idx]->unit_target = strtol(split[6],NULL,16); - if( strcmpi(split[6],"noenemy")==0 ) skill_db[idx].unit_target = BCT_NOENEMY; - else if( strcmpi(split[6],"friend")==0 ) skill_db[idx].unit_target = BCT_NOENEMY; - else if( strcmpi(split[6],"party")==0 ) skill_db[idx].unit_target = BCT_PARTY; - else if( strcmpi(split[6],"ally")==0 ) skill_db[idx].unit_target = BCT_PARTY|BCT_GUILD; - else if( strcmpi(split[6],"guild")==0 ) skill_db[idx].unit_target = BCT_GUILD; - else if( strcmpi(split[6],"all")==0 ) skill_db[idx].unit_target = BCT_ALL; - else if( strcmpi(split[6],"enemy")==0 ) skill_db[idx].unit_target = BCT_ENEMY; - else if( strcmpi(split[6],"self")==0 ) skill_db[idx].unit_target = BCT_SELF; - else if( strcmpi(split[6],"sameguild")==0 ) skill_db[idx].unit_target = BCT_GUILD|BCT_SAMEGUILD; - else if( strcmpi(split[6],"noone")==0 ) skill_db[idx].unit_target = BCT_NOONE; - else skill_db[idx].unit_target = strtol(split[6],NULL,16); + skill_db[idx]->unit_flag = strtol(split[7],NULL,16); - skill_db[idx].unit_flag = strtol(split[7],NULL,16); - - if (skill_db[idx].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy) - skill_db[idx].unit_target = BCT_NOENEMY; + if (skill_db[idx]->unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy) + skill_db[idx]->unit_target = BCT_NOENEMY; //By default, target just characters. - skill_db[idx].unit_target |= BL_CHAR; - if (skill_db[idx].unit_flag&UF_NOPC) - skill_db[idx].unit_target &= ~BL_PC; - if (skill_db[idx].unit_flag&UF_NOMOB) - skill_db[idx].unit_target &= ~BL_MOB; - if (skill_db[idx].unit_flag&UF_SKILL) - skill_db[idx].unit_target |= BL_SKILL; + skill_db[idx]->unit_target |= BL_CHAR; + if (skill_db[idx]->unit_flag&UF_NOPC) + skill_db[idx]->unit_target &= ~BL_PC; + if (skill_db[idx]->unit_flag&UF_NOMOB) + skill_db[idx]->unit_target &= ~BL_MOB; + if (skill_db[idx]->unit_flag&UF_SKILL) + skill_db[idx]->unit_target |= BL_SKILL; return true; } @@ -20329,26 +20367,24 @@ static bool skill_parse_row_copyabledb(char* split[], int column, int current) else id = skill_name2id(split[0]); - if ((id = skill_get_index(id)) == 0) { - ShowError("skill_parse_row_copyabledb: Invalid skill '%s'\n",split[0]); - return false; - } + id = skill_db_isset(id, __FUNCTION__); + if ((option = atoi(split[1])) > 3) { ShowError("skill_parse_row_copyabledb: Invalid option '%s'\n",split[1]); return false; } // Import just for clearing/disabling from original data if (option == 0) { - memset(&skill_db[id].copyable, 0, sizeof(skill_db[id].copyable)); + memset(&skill_db[id]->copyable, 0, sizeof(skill_db[id]->copyable)); //ShowInfo("skill_parse_row_copyabledb: Skill %s removed from list.\n", split[0]); return true; } - skill_db[id].copyable.option = option; - skill_db[id].copyable.joballowed = 63; + skill_db[id]->copyable.option = option; + skill_db[id]->copyable.joballowed = 63; if (atoi(split[2])) - skill_db[id].copyable.joballowed = cap_value(atoi(split[2]),1,63); - skill_db[id].copyable.req_opt = cap_value(atoi(split[3]),0,(0x2000)-1); + skill_db[id]->copyable.joballowed = cap_value(atoi(split[2]),1,63); + skill_db[id]->copyable.req_opt = cap_value(atoi(split[3]),0,(0x2000)-1); return true; } @@ -20366,13 +20402,10 @@ static bool skill_parse_row_nonearnpcrangedb(char* split[], int column, int curr else id = skill_name2id(split[0]); - if ((id = skill_get_index(id)) == 0) { // invalid skill id - ShowError("skill_parse_row_nonearnpcrangedb: Invalid skill '%s'\n",split[0]); - return false; - } + id = skill_db_isset(atoi(split[0]), __FUNCTION__); - skill_db[id].unit_nonearnpc_range = max(atoi(split[1]),0); - skill_db[id].unit_nonearnpc_type = (atoi(split[2])) ? cap_value(atoi(split[2]),1,15) : 15; + skill_db[id]->unit_nonearnpc_range = max(atoi(split[1]),0); + skill_db[id]->unit_nonearnpc_type = (atoi(split[2])) ? cap_value(atoi(split[2]),1,15) : 15; return true; } @@ -20475,24 +20508,62 @@ static bool skill_parse_row_changematerialdb(char* split[], int columns, int cur static bool skill_parse_row_skilldamage(char* split[], int columns, int current) { uint16 skill_id = skill_name2id(split[0]), idx; - if ((idx = skill_get_index(skill_id)) == 0) { // invalid skill id - ShowWarning("skill_parse_row_skilldamage: Invalid skill '%s'. Skipping..",split[0]); - return false; - } - memset(&skill_db[idx].damage,0,sizeof(struct s_skill_damage)); - skill_db[idx].damage.caster |= atoi(split[1]); - skill_db[idx].damage.map |= atoi(split[2]); - skill_db[idx].damage.pc = cap_value(atoi(split[3]),-100,INT_MAX); + + idx = skill_db_isset(atoi(split[0]), __FUNCTION__); + + memset(&skill_db[idx]->damage,0,sizeof(struct s_skill_damage)); + skill_db[idx]->damage.caster |= atoi(split[1]); + skill_db[idx]->damage.map |= atoi(split[2]); + skill_db[idx]->damage.pc = cap_value(atoi(split[3]),-100,INT_MAX); if (split[3]) - skill_db[idx].damage.mob = cap_value(atoi(split[4]),-100,INT_MAX); + skill_db[idx]->damage.mob = cap_value(atoi(split[4]),-100,INT_MAX); if (split[4]) - skill_db[idx].damage.boss = cap_value(atoi(split[5]),-100,INT_MAX); + skill_db[idx]->damage.boss = cap_value(atoi(split[5]),-100,INT_MAX); if (split[5]) - skill_db[idx].damage.other = cap_value(atoi(split[6]),-100,INT_MAX); + skill_db[idx]->damage.other = cap_value(atoi(split[6]),-100,INT_MAX); return true; } #endif +/** + * Init dummy skill db also init Skill DB allocation + * @param skill_id + * @return Skill Index + **/ +static uint16 skill_db_create(uint16 skill_id) { + if (skill_num >= MAX_SKILL) { + ShowError("Cannot add more skill. Limit is reached '%d'. Change 'MAX_SKILL' in mmo.h\n", MAX_SKILL); + return 0; + } + if (!skill_num) + CREATE(skill_db, struct s_skill_db *, 1); + else + RECREATE(skill_db, struct s_skill_db *, skill_num+1); + + CREATE(skill_db[skill_num], struct s_skill_db, 1); + if (skill_id > 0) { + safestrncpy(skill_db[skill_num]->name, "UNKNOWN_SKILL", sizeof(skill_db[skill_num]->name)); + safestrncpy(skill_db[skill_num]->desc, "Unknown Skill", sizeof(skill_db[skill_num]->desc)); + } + skill_db[skill_num]->nameid = skill_id; + uidb_iput(skilldb_id2idx, skill_id, skill_num); + return skill_next_idx(); +} + +static void skill_db_destroy(void) { + uint16 i; + for (i = 0; i < SKILL_MAX_DB(); i++) { + if (skill_db[i]) { + skill_destroy_requirement(i); + aFree(skill_db[i]); + } + skill_db[i] = NULL; + } + skill_num = 0; + aFree(skill_db); + skill_db = NULL; +} + /*=============================== * DB reading. * skill_db.txt @@ -20514,9 +20585,12 @@ static void skill_readdb(void) //add other path here }; - // init skill db structures db_clear(skilldb_name2id); - memset(skill_db,0,sizeof(skill_db)); + db_clear(skilldb_id2idx); + + skill_db_destroy(); + skill_db_create(0); + memset(skill_produce_db,0,sizeof(skill_produce_db)); memset(skill_arrow_db,0,sizeof(skill_arrow_db)); memset(skill_abra_db,0,sizeof(skill_abra_db)); @@ -20525,9 +20599,6 @@ static void skill_readdb(void) memset(skill_changematerial_db,0,sizeof(skill_changematerial_db)); skill_produce_count = skill_arrow_count = skill_abra_count = skill_improvise_count = skill_changematerial_count = skill_spellbook_count = skill_magicmushroom_count = 0; - // load skill databases - safestrncpy(skill_db[0].name, "UNKNOWN_SKILL", sizeof(skill_db[0].name)); - safestrncpy(skill_db[0].desc, "Unknown Skill", sizeof(skill_db[0].desc)); for(i=0; i= HM_SKILLBASE && (skill_id) < HM_SKILLBASE+MAX_HOMUNSKILL ) +#define SKILL_CHK_MERC(skill_id) ( (skill_id) >= MC_SKILLBASE && (skill_id) < MC_SKILLBASE+MAX_MERCSKILL ) +#define SKILL_CHK_ELEM(skill_id) ( (skill_id) >= EL_SKILLBASE && (skill_id) < EL_SKILLBASE+MAX_ELEMENTALSKILL ) +#define SKILL_CHK_GUILD(skill_id) ( (skill_id) >= GD_SKILLBASE && (skill_id) < GD_SKILLBASE+MAX_GUILDSKILL ) + #endif /* _SKILL_H_ */ diff --git a/src/map/status.c b/src/map/status.c index 1c3c64ea46..f4da6b5449 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -154,11 +154,11 @@ static void set_sc(uint16 skill_id, sc_type sc, int icon, unsigned int flag) { uint16 idx = skill_get_index(skill_id); if( idx == 0 ) { - ShowError("set_sc: Unsupported skill id %d\n", skill_id); + ShowError("set_sc: Unsupported skill id %d (SC: %d. Icon: %d)\n", skill_id, sc, icon); return; } if( sc < 0 || sc >= SC_MAX ) { - ShowError("set_sc: Unsupported status change id %d\n", sc); + ShowError("set_sc: Unsupported status change id %d (Skill: %d. Icon: %d)\n", sc, skill_id, icon); return; } @@ -172,6 +172,16 @@ static void set_sc(uint16 skill_id, sc_type sc, int icon, unsigned int flag) SkillStatusChangeTable[idx] = sc; } +static void set_sc_with_vfx_noskill(sc_type sc, int icon, unsigned flag) { + if (sc > SC_NONE && sc < SC_MAX) { + if (StatusIconChangeTable[sc] == SI_BLANK) + StatusIconChangeTable[sc] = icon; + StatusChangeFlagTable[sc] |= flag; + } + if (icon > SI_BLANK && icon < SI_MAX) + StatusRelevantBLTypes[icon] |= BL_SCEFFECT; +} + void initChangeTables(void) { int i; @@ -791,8 +801,6 @@ void initChangeTables(void) set_sc( OB_OBOROGENSOU , SC_GENSOU , SI_GENSOU , SCB_NONE ); set_sc( ALL_FULL_THROTTLE , SC_FULL_THROTTLE , SI_FULL_THROTTLE , SCB_SPEED|SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); - set_sc_with_vfx( SC_MOONSTAR , SC_MOONSTAR , SI_MOONSTAR , SCB_NONE ); - set_sc_with_vfx( SC_SUPER_STAR , SC_SUPER_STAR , SI_SUPER_STAR , SCB_NONE ); /* Rebellion */ add_sc( RL_MASS_SPIRAL , SC_BLEEDING ); @@ -806,24 +814,26 @@ void initChangeTables(void) set_sc_with_vfx( RL_C_MARKER , SC_C_MARKER , SI_C_MARKER , SCB_FLEE ); set_sc_with_vfx( RL_AM_BLAST , SC_ANTI_M_BLAST , SI_ANTI_M_BLAST , SCB_NONE ); - set_sc_with_vfx( SC_ALL_RIDING , SC_ALL_RIDING , SI_ALL_RIDING , SCB_SPEED ); + set_sc_with_vfx_noskill( SC_MOONSTAR , SI_MOONSTAR , SCB_NONE ); + set_sc_with_vfx_noskill( SC_SUPER_STAR , SI_SUPER_STAR , SCB_NONE ); + set_sc_with_vfx_noskill( SC_ALL_RIDING , SI_ALL_RIDING , SCB_SPEED ); /* Storing the target job rather than simply SC_SPIRIT simplifies code later on */ - SkillStatusChangeTable[SL_ALCHEMIST] = (sc_type)MAPID_ALCHEMIST, - SkillStatusChangeTable[SL_MONK] = (sc_type)MAPID_MONK, - SkillStatusChangeTable[SL_STAR] = (sc_type)MAPID_STAR_GLADIATOR, - SkillStatusChangeTable[SL_SAGE] = (sc_type)MAPID_SAGE, - SkillStatusChangeTable[SL_CRUSADER] = (sc_type)MAPID_CRUSADER, - SkillStatusChangeTable[SL_SUPERNOVICE] = (sc_type)MAPID_SUPER_NOVICE, - SkillStatusChangeTable[SL_KNIGHT] = (sc_type)MAPID_KNIGHT, - SkillStatusChangeTable[SL_WIZARD] = (sc_type)MAPID_WIZARD, - SkillStatusChangeTable[SL_PRIEST] = (sc_type)MAPID_PRIEST, - SkillStatusChangeTable[SL_BARDDANCER] = (sc_type)MAPID_BARDDANCER, - SkillStatusChangeTable[SL_ROGUE] = (sc_type)MAPID_ROGUE, - SkillStatusChangeTable[SL_ASSASIN] = (sc_type)MAPID_ASSASSIN, - SkillStatusChangeTable[SL_BLACKSMITH] = (sc_type)MAPID_BLACKSMITH, - SkillStatusChangeTable[SL_HUNTER] = (sc_type)MAPID_HUNTER, - SkillStatusChangeTable[SL_SOULLINKER] = (sc_type)MAPID_SOUL_LINKER, + SkillStatusChangeTable[skill_get_index(SL_ALCHEMIST)] = (sc_type)MAPID_ALCHEMIST, + SkillStatusChangeTable[skill_get_index(SL_MONK)] = (sc_type)MAPID_MONK, + SkillStatusChangeTable[skill_get_index(SL_STAR)] = (sc_type)MAPID_STAR_GLADIATOR, + SkillStatusChangeTable[skill_get_index(SL_SAGE)] = (sc_type)MAPID_SAGE, + SkillStatusChangeTable[skill_get_index(SL_CRUSADER)] = (sc_type)MAPID_CRUSADER, + SkillStatusChangeTable[skill_get_index(SL_SUPERNOVICE)] = (sc_type)MAPID_SUPER_NOVICE, + SkillStatusChangeTable[skill_get_index(SL_KNIGHT)] = (sc_type)MAPID_KNIGHT, + SkillStatusChangeTable[skill_get_index(SL_WIZARD)] = (sc_type)MAPID_WIZARD, + SkillStatusChangeTable[skill_get_index(SL_PRIEST)] = (sc_type)MAPID_PRIEST, + SkillStatusChangeTable[skill_get_index(SL_BARDDANCER)] = (sc_type)MAPID_BARDDANCER, + SkillStatusChangeTable[skill_get_index(SL_ROGUE)] = (sc_type)MAPID_ROGUE, + SkillStatusChangeTable[skill_get_index(SL_ASSASIN)] = (sc_type)MAPID_ASSASSIN, + SkillStatusChangeTable[skill_get_index(SL_BLACKSMITH)] = (sc_type)MAPID_BLACKSMITH, + SkillStatusChangeTable[skill_get_index(SL_HUNTER)] = (sc_type)MAPID_HUNTER, + SkillStatusChangeTable[skill_get_index(SL_SOULLINKER)] = (sc_type)MAPID_SOUL_LINKER, /* Status that don't have a skill associated */ StatusIconChangeTable[SC_WEIGHT50] = SI_WEIGHT50; diff --git a/src/map/unit.c b/src/map/unit.c index ad1fcbed32..04180f064a 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -1796,7 +1796,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui // Moved here to prevent Suffragium from ending if skill fails #ifndef RENEWAL_CAST - if (!(skill_get_castnodex(skill_id, skill_lv)&2)) + if (!(skill_get_castnodex(skill_id)&2)) casttime = skill_castfix_sc(src, casttime); #else casttime = skill_vfcastfix(src, casttime, skill_id, skill_lv); @@ -2013,7 +2013,7 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui // Moved here to prevent Suffragium from ending if skill fails #ifndef RENEWAL_CAST - if (!(skill_get_castnodex(skill_id, skill_lv)&2)) + if (!(skill_get_castnodex(skill_id)&2)) casttime = skill_castfix_sc(src, casttime); #else casttime = skill_vfcastfix(src, casttime, skill_id, skill_lv ); @@ -2035,11 +2035,11 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui // } // } - ud->skill_id = skill_id; - ud->skill_lv = skill_lv; - ud->skillx = skill_x; - ud->skilly = skill_y; - ud->skilltarget = 0; + ud->skill_id = skill_id; + ud->skill_lv = skill_lv; + ud->skillx = skill_x; + ud->skilly = skill_y; + ud->skilltarget = 0; if( sc ) { // These 3 status do not stack, so it's efficient to use if-else From 5e58ab6fc9d9f530d233cbc208fac95480991c27 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Tue, 3 Feb 2015 12:17:15 +0700 Subject: [PATCH 2/3] Follow up 72617ac98b4c40407627fe86e7123a25bfd8a025 * Fixed an error Signed-off-by: Cydh Ramdh --- src/map/atcommand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 9e6ae1eee2..98866be41b 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -32,6 +32,7 @@ #include "trade.h" #include "mapreg.h" #include "quest.h" +#include "pc.h" #include #include From 7357f9c3840bfb2ceb55df35548d7f77e0d1be9a Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Thu, 5 Feb 2015 12:11:13 +0700 Subject: [PATCH 3/3] Another follow up Signed-off-by: Cydh Ramdh --- src/map/pc.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/map/pc.h b/src/map/pc.h index 73b8fe7836..55e00683b7 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -916,6 +916,14 @@ void pc_bonus2(struct map_session_data *sd, int type, int type2, int val); void pc_bonus3(struct map_session_data *sd, int type, int type2, int type3, int val); void pc_bonus4(struct map_session_data *sd, int type, int type2, int type3, int type4, int val); void pc_bonus5(struct map_session_data *sd, int type, int type2, int type3, int type4, int type5, int val); + +enum e_addskill_type { + ADDSKILL_PERMANENT = 0, ///< Permanent skill. Remove the skill if level is 0 + ADDSKILL_TEMP = 1, ///< Temporary skill. If player learned the skill and the given level is higher, level will be replaced and learned level will be palced in skill flag. `flag = learned + SKILL_FLAG_REPLACED_LV_0; learned_level = level;` + ADDSKILL_TEMP_ADDLEVEL = 2, ///< Like PCSKILL_TEMP, except the level will be stacked. `learned_level += level`. The flag is used to store original learned level + ADDSKILL_PERMANENT_GRANTED = 3, ///< Grant permanent skill, ignore skill tree and learned level +}; + bool pc_skill(struct map_session_data *sd, uint16 skill_id, int level, enum e_addskill_type type); int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip); @@ -1080,13 +1088,6 @@ enum e_additem_result { ADDITEM_STACKLIMIT = 7 }; -enum e_addskill_type { - ADDSKILL_PERMANENT = 0, ///< Permanent skill. Remove the skill if level is 0 - ADDSKILL_TEMP = 1, ///< Temporary skill. If player learned the skill and the given level is higher, level will be replaced and learned level will be palced in skill flag. `flag = learned + SKILL_FLAG_REPLACED_LV_0; learned_level = level;` - ADDSKILL_TEMP_ADDLEVEL = 2, ///< Like PCSKILL_TEMP, except the level will be stacked. `learned_level += level`. The flag is used to store original learned level - ADDSKILL_PERMANENT_GRANTED = 3, ///< Grant permanent skill, ignore skill tree and learned level -}; - // timer for night.day extern int day_timer_tid; extern int night_timer_tid;