From 73140bd078036ef173568918102e42f824ee148c Mon Sep 17 00:00:00 2001 From: Daegaladh Date: Sat, 9 May 2020 15:53:17 +0200 Subject: [PATCH 001/141] Fixed monster messages not loading (#4908) --- src/map/mob.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 218cb387dd..56ac411314 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -3768,7 +3768,7 @@ int mobskill_use(struct mob_data *md, t_tick tick, int event) } } //Skill used. Post-setups... - if ( ms[ i ].msg_id ){ //Display color message [SnakeDrak] + if ( ms[i].msg_id ){ //Display color message [SnakeDrak] struct mob_chat *mc = mob_chat(ms[i].msg_id); if (mc) { @@ -5525,10 +5525,9 @@ static void mob_load(void) }; // First we parse all the possible monsters to add additional data in the second loop - if( db_use_sqldbs ){ + if( db_use_sqldbs ) mob_read_sqldb(); - mob_read_sqlskilldb(); - }else{ + else { for(int i = 0; i < ARRAYLENGTH(dbsubpath); i++){ int n2 = strlen(db_path)+strlen(DBPATH)+strlen(dbsubpath[i])+1; char* dbsubpath2 = (char*)aMalloc(n2+1); @@ -5553,7 +5552,7 @@ static void mob_load(void) char* dbsubpath1 = (char*)aMalloc(n1+1); char* dbsubpath2 = (char*)aMalloc(n2+1); bool silent = i > 0; - + if(i==0) { safesnprintf(dbsubpath1,n1,"%s%s",db_path,dbsubpath[i]); safesnprintf(dbsubpath2,n2,"%s/%s%s",db_path,DBPATH,dbsubpath[i]); @@ -5561,14 +5560,16 @@ static void mob_load(void) safesnprintf(dbsubpath1,n1,"%s%s",db_path,dbsubpath[i]); safesnprintf(dbsubpath2,n1,"%s%s",db_path,dbsubpath[i]); } - - if( !db_use_sqldbs ){ + + sv_readdb(dbsubpath1, "mob_chat_db.txt", '#', 3, 3, -1, &mob_parse_row_chatdb, silent); + + if( db_use_sqldbs ) + mob_read_sqlskilldb(); + else mob_readskilldb(dbsubpath2, silent); - } sv_readdb(dbsubpath2, "mob_race2_db.txt", ',', 2, MAX_RACE2_MOBS, -1, &mob_readdb_race2, silent); sv_readdb(dbsubpath1, "mob_item_ratio.txt", ',', 2, 2+MAX_ITEMRATIO_MOBS, -1, &mob_readdb_itemratio, silent); - sv_readdb(dbsubpath1, "mob_chat_db.txt", '#', 3, 3, -1, &mob_parse_row_chatdb, silent); sv_readdb(dbsubpath2, "mob_random_db.txt", ',', 4, 4, -1, &mob_readdb_group, silent); sv_readdb(dbsubpath2, "mob_branch.txt", ',', 4, 4, -1, &mob_readdb_group, silent); sv_readdb(dbsubpath2, "mob_poring.txt", ',', 4, 4, -1, &mob_readdb_group, silent); @@ -5577,7 +5578,7 @@ static void mob_load(void) sv_readdb(dbsubpath1, "mob_mission.txt", ',', 4, 4, -1, &mob_readdb_group, silent); sv_readdb(dbsubpath1, "mob_classchange.txt", ',', 4, 4, -1, &mob_readdb_group, silent); sv_readdb(dbsubpath2, "mob_drop.txt", ',', 3, 5, -1, &mob_readdb_drop, silent); - + aFree(dbsubpath1); aFree(dbsubpath2); } From fb890c747c7f591aab7290d5e84b69c6ab51e024 Mon Sep 17 00:00:00 2001 From: Daegaladh Date: Sun, 10 May 2020 04:12:29 +0200 Subject: [PATCH 002/141] Fixed Piano Key Quest hidden warp position in pre-re (#4888) --- npc/pre-re/quests/quests_niflheim.txt | 10 ++++++++++ npc/pre-re/scripts_athena.conf | 1 + npc/quests/quests_niflheim.txt | 2 +- npc/re/quests/quests_niflheim.txt | 10 ++++++++++ npc/re/scripts_athena.conf | 1 + 5 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 npc/pre-re/quests/quests_niflheim.txt create mode 100644 npc/re/quests/quests_niflheim.txt diff --git a/npc/pre-re/quests/quests_niflheim.txt b/npc/pre-re/quests/quests_niflheim.txt new file mode 100644 index 0000000000..0a193cfa55 --- /dev/null +++ b/npc/pre-re/quests/quests_niflheim.txt @@ -0,0 +1,10 @@ +//===== rAthena Script ======================================= +//= Quest NPCs related to Niflheim +//===== Description: ========================================= +//= [Official Conversion] +//= Piano Key Ouest +//===== Additional Comments: ================================= +//= 1.0 Moved Piano3 to pre-re/re paths. [Daegaladh] +//============================================================ + +nif_in,114,181,0 duplicate(Piano3) #Piano3 HIDDEN_NPC,1,1 diff --git a/npc/pre-re/scripts_athena.conf b/npc/pre-re/scripts_athena.conf index 4cb4306aee..d58007278c 100644 --- a/npc/pre-re/scripts_athena.conf +++ b/npc/pre-re/scripts_athena.conf @@ -93,5 +93,6 @@ npc: npc/pre-re/quests/quests_izlude.txt npc: npc/pre-re/quests/quests_lighthalzen.txt npc: npc/pre-re/quests/quests_morocc.txt npc: npc/pre-re/quests/quests_nameless.txt +npc: npc/pre-re/quests/quests_niflheim.txt npc: npc/pre-re/quests/the_sign_quest.txt npc: npc/pre-re/quests/quests_veins.txt diff --git a/npc/quests/quests_niflheim.txt b/npc/quests/quests_niflheim.txt index 5953066c60..d475b80268 100644 --- a/npc/quests/quests_niflheim.txt +++ b/npc/quests/quests_niflheim.txt @@ -249,7 +249,7 @@ OnTouch_: end; } -nif_in,118,151,0 script #Piano3 111,1,1,{ +- script Piano3 HIDDEN_NPC,{ end; OnTouch_: diff --git a/npc/re/quests/quests_niflheim.txt b/npc/re/quests/quests_niflheim.txt new file mode 100644 index 0000000000..2184ae3090 --- /dev/null +++ b/npc/re/quests/quests_niflheim.txt @@ -0,0 +1,10 @@ +//===== rAthena Script ======================================= +//= Quest NPCs related to Niflheim +//===== Description: ========================================= +//= [Official Conversion] +//= Piano Key Ouest +//===== Additional Comments: ================================= +//= 1.0 Moved Piano3 to pre-re/re paths. [Daegaladh] +//============================================================ + +nif_in,118,151,0 duplicate(Piano3) #Piano3 HIDDEN_NPC,1,1 diff --git a/npc/re/scripts_athena.conf b/npc/re/scripts_athena.conf index 99f1ef0ed4..9371c883aa 100644 --- a/npc/re/scripts_athena.conf +++ b/npc/re/scripts_athena.conf @@ -198,6 +198,7 @@ npc: npc/re/quests/quests_malaya.txt npc: npc/re/quests/quests_mora.txt npc: npc/re/quests/quests_morocc.txt npc: npc/re/quests/quests_nameless.txt +npc: npc/re/quests/quests_niflheim.txt npc: npc/re/quests/quests_rockridge.txt npc: npc/re/quests/quests_veins.txt npc: npc/re/quests/the_sign_quest.txt From 39f3a17f54dc626d69f5ee3655f0699ed1062a48 Mon Sep 17 00:00:00 2001 From: ~Neutral~ <33843418+NeutralDev@users.noreply.github.com> Date: Sat, 9 May 2020 21:21:41 -0500 Subject: [PATCH 003/141] Fixed Dual Wielding Axes breaking when they should be unbreakable (#4518) --- src/map/skill.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index cf7158f411..4de4a72568 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -2659,6 +2659,9 @@ int skill_break_equip(struct block_list *src, struct block_list *bl, unsigned sh case W_2HSTAFF: case W_BOOK: //Rods and Books can't be broken [Skotlex] case W_HUUMA: + case W_DOUBLE_AA: // Axe usage during dual wield should also prevent breaking [Neutral] + case W_DOUBLE_DA: + case W_DOUBLE_SA: where &= ~EQP_WEAPON; } } From c4609b19d6910ae05d6424a8cb1da2142d870ef0 Mon Sep 17 00:00:00 2001 From: Daegaladh Date: Sun, 10 May 2020 05:30:24 +0200 Subject: [PATCH 004/141] Follow up to b2aa7e698da55bcae198dfcda7ab706f35d958e3 --- src/map/skill.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 4de4a72568..3985450d76 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -8568,7 +8568,9 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui case TF_BACKSLIDING: //This is the correct implementation as per packet logging information. [Skotlex] skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),unit_getdir(bl),(enum e_skill_blown)(BLOWN_IGNORE_NO_KNOCKBACK|BLOWN_DONT_SEND_PACKET)); clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); +#ifdef RENEWAL clif_blown(src); // Always blow, otherwise it shows a casting animation. [Lemongrass] +#endif break; case TK_HIGHJUMP: From 49c4ddf8f904a3dde22b092adc41da3fb1438968 Mon Sep 17 00:00:00 2001 From: Sader Fawall Date: Sun, 10 May 2020 08:34:00 +0200 Subject: [PATCH 005/141] npctalk script command now accepts color parameter (#4910) Co-authored-by: Jittapan Pluemsumran Co-authored-by: Lemongrass3110 Co-authored-by: Aleos --- doc/script_commands.txt | 5 +++-- src/map/script.cpp | 10 +++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 49bae2d921..60fd044798 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -6596,13 +6596,14 @@ Returns true if the command was executed on the other NPC successfully, false if --------------------------------------- -*npctalk ""{,"",""}; +*npctalk ""{,"",{,}}; This command will display a message as if the NPC object running it was a player talking - that is, above their head and in the chat window. The display name of the NPC won't get appended in front of the message. If the option is given and not empty, then that NPC will display the message, -else the attached NPC will display the message. +else the attached NPC will display the message, +the color format is in RGB (0xRRGGBB). The color is White by default. Target for : - bc_all : Broadcast message is sent server-wide (only in the chat window). diff --git a/src/map/script.cpp b/src/map/script.cpp index 8b5ebd683e..7ff47a67fd 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -15048,12 +15048,16 @@ BUILDIN_FUNC(npctalk) { struct npc_data* nd = NULL; const char* str = script_getstr(st,2); + int color = 0xFFFFFF; if (script_hasdata(st, 3) && strlen(script_getstr(st,3)) > 0) nd = npc_name2id(script_getstr(st, 3)); else nd = (struct npc_data *)map_id2bl(st->oid); + if (script_hasdata(st, 5)) + color = script_getnum(st, 5); + if (nd != NULL) { send_target target = AREA; char message[CHAT_SIZE_MAX]; @@ -15069,12 +15073,12 @@ BUILDIN_FUNC(npctalk) } safesnprintf(message, sizeof(message), "%s", str); if (target != SELF) - clif_messagecolor(&nd->bl, color_table[COLOR_WHITE], message, false, target); + clif_messagecolor(&nd->bl, color, message, true, target); else { TBL_PC *sd = map_id2sd(st->rid); if (sd == NULL) return SCRIPT_CMD_FAILURE; - clif_messagecolor_target(&nd->bl, color_table[COLOR_WHITE], message, false, target, sd); + clif_messagecolor_target(&nd->bl, color, message, true, target, sd); } } return SCRIPT_CMD_SUCCESS; @@ -24950,7 +24954,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF2(atcommand,"charcommand","s"), // [MouseJstr] BUILDIN_DEF(movenpc,"sii?"), // [MouseJstr] BUILDIN_DEF(message,"ss"), // [MouseJstr] - BUILDIN_DEF(npctalk,"s??"), // [Valaris] + BUILDIN_DEF(npctalk,"s???"), // [Valaris] BUILDIN_DEF(chatmes,"s?"), // [Jey] BUILDIN_DEF(mobcount,"ss"), BUILDIN_DEF(getlook,"i?"), From 8d2c26d115f0c44fb2d53efb5409c43ad0103946 Mon Sep 17 00:00:00 2001 From: Yashim Wong Date: Sun, 10 May 2020 14:49:46 +0800 Subject: [PATCH 006/141] Removed sonic blow attack animation (#4926) --- src/map/skill.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 3985450d76..5d7af188e3 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -2270,9 +2270,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 !battle_check_range(bl, tbl, skill_get_range2(src, skill, autospl_skill_lv, true))) continue; - if (skill == AS_SONICBLOW) - pc_stop_attack(sd); //Special case, Sonic Blow autospell should stop the player attacking. - else if (skill == PF_SPIDERWEB) //Special case, due to its nature of coding. + if (skill == PF_SPIDERWEB) //Special case, due to its nature of coding. type = CAST_GROUND; sd->state.autocast = 1; From 48fd0e859b7ae3b640700b7b7c82e1b239805562 Mon Sep 17 00:00:00 2001 From: Daegaladh Date: Sun, 10 May 2020 15:54:43 +0200 Subject: [PATCH 007/141] Follow up to 8d2c26d115f0c44fb2d53efb5409c43ad0103946 --- src/map/skill.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 5d7af188e3..76dc470300 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -2272,6 +2272,10 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 if (skill == PF_SPIDERWEB) //Special case, due to its nature of coding. type = CAST_GROUND; +#ifndef RENEWAL + else if (skill == AS_SONICBLOW) + pc_stop_attack(sd); //Special case, Sonic Blow autospell should stop the player attacking. +#endif sd->state.autocast = 1; skill_consume_requirement(sd,skill,autospl_skill_lv,1); From c5a0279b5ca86a91e09c18c2f7de57c39443d87d Mon Sep 17 00:00:00 2001 From: Aleos Date: Mon, 11 May 2020 14:08:33 -0400 Subject: [PATCH 008/141] Adds skill placeholder for Super Novice Expanded (#4919) * Adds skill placeholder for Super Novice Expanded in the pre-renewal skill database. * Prevents undefined skill errors for pc_checkskill. Thanks to @gidzdlcrz! --- db/pre-re/skill_db.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/db/pre-re/skill_db.yml b/db/pre-re/skill_db.yml index 3c4031a070..f9dffe81d0 100644 --- a/db/pre-re/skill_db.yml +++ b/db/pre-re/skill_db.yml @@ -30115,6 +30115,18 @@ Body: Name: AB_CONVENIO Description: Convenio MaxLevel: 1 + - Id: 5075 + Name: NV_BREAKTHROUGH + Description: Break Through + MaxLevel: 1 + - Id: 5076 + Name: NV_HELPANGEL + Description: Help Angel + MaxLevel: 1 + - Id: 5077 + Name: NV_TRANSCENDENCE + Description: Transcendence + MaxLevel: 1 - Id: 8001 Name: HLIF_HEAL Description: Healing Touch From 5154e078999ea8b0ae8680126933fe11a2506dd9 Mon Sep 17 00:00:00 2001 From: Daegaladh Date: Tue, 12 May 2020 23:09:22 +0200 Subject: [PATCH 009/141] Follow up to 73140bd078036ef173568918102e42f824ee148c --- src/map/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 56ac411314..1a8f47c532 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -5563,7 +5563,7 @@ static void mob_load(void) sv_readdb(dbsubpath1, "mob_chat_db.txt", '#', 3, 3, -1, &mob_parse_row_chatdb, silent); - if( db_use_sqldbs ) + if( db_use_sqldbs && i == 0 ) mob_read_sqlskilldb(); else mob_readskilldb(dbsubpath2, silent); From 147214cb1bd12ed262745517b15703a4e3d5895b Mon Sep 17 00:00:00 2001 From: Joam <35770095+attackjom@users.noreply.github.com> Date: Thu, 14 May 2020 03:37:56 +0700 Subject: [PATCH 010/141] Fixes Meteor Assault and card bonuses (#4932) * Fixes #4912. * Meteor assault should increase damage with ATK item bonus. Thanks to @attackjom! --- db/re/skill_db.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index 47f53dea02..e4226fda02 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -11274,7 +11274,6 @@ Body: TargetType: Self DamageFlags: Splash: true - IgnoreAtkCard: true Flags: TargetTrap: true IgnoreAutoGuard: true From 132e74bd13629898c0d1f0c16be5a6a33607c734 Mon Sep 17 00:00:00 2001 From: Daegaladh Date: Thu, 14 May 2020 02:57:13 +0200 Subject: [PATCH 011/141] Follow up to c4609b19d6910ae05d6424a8cb1da2142d870ef0 --- src/map/skill.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 76dc470300..34b1862845 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -8568,7 +8568,11 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui break; case TF_BACKSLIDING: //This is the correct implementation as per packet logging information. [Skotlex] - skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),unit_getdir(bl),(enum e_skill_blown)(BLOWN_IGNORE_NO_KNOCKBACK|BLOWN_DONT_SEND_PACKET)); + skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),unit_getdir(bl),(enum e_skill_blown)(BLOWN_IGNORE_NO_KNOCKBACK +#ifdef RENEWAL + |BLOWN_DONT_SEND_PACKET +#endif + )); clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); #ifdef RENEWAL clif_blown(src); // Always blow, otherwise it shows a casting animation. [Lemongrass] From 061e586a1fc7330a0f98ac0c6076b6c6c840d6dc Mon Sep 17 00:00:00 2001 From: Aleos Date: Wed, 13 May 2020 21:02:57 -0400 Subject: [PATCH 012/141] Few Genetic skill fixes (#4813) * Fixes Acid Demonstration to use the target's VIT and not casters. * Fixes Fire Expansion - Acid to behave like Acid Demonstration. * Fixes Crazy Weed not doing 2 damage attacks. * Crazy Weed is now a long ranged physical attack. * Adjusts Hell's Plant behavior to no longer be removed on map change. * Adds missing status icon for Hell's Plant. * Fixes Hell's Plant attack formula when learning Summon Flora. * Hell's Plant now uses the caster's weapon as the element. * Hell's Plant no longer ignores the target's DEF and Element. * Fixes Hell's Plant removing Thorns Trap. * Removes fixed cast time from Xeno Slasher. * Adds new Genetic Pharmacy potions (items are disabled until int32 item ID support). * Fixes Spore Explosion's splash attack doing the same damage as the initial. Thanks to @teededung, @Badarosk0, @cahya1992, and @OptimusM! --- db/pre-re/skill_db.yml | 4 +-- db/re/item_db.txt | 7 ++++++ db/re/produce_db.txt | 4 +-- db/re/skill_db.yml | 16 +++--------- src/map/battle.cpp | 49 +++++++++++++----------------------- src/map/pc.cpp | 14 +++++++---- src/map/script_constants.hpp | 3 +++ src/map/skill.cpp | 13 ++-------- src/map/status.cpp | 5 +++- src/map/status.hpp | 3 +++ src/map/unit.cpp | 1 - 11 files changed, 53 insertions(+), 66 deletions(-) diff --git a/db/pre-re/skill_db.yml b/db/pre-re/skill_db.yml index f9dffe81d0..a01fc0e56f 100644 --- a/db/pre-re/skill_db.yml +++ b/db/pre-re/skill_db.yml @@ -26706,8 +26706,8 @@ Body: DamageFlags: Splash: true Range: 11 - Hit: Single - HitCount: 3 + Hit: Multi_Hit + HitCount: 2 Element: Weapon SplashArea: - Level: 1 diff --git a/db/re/item_db.txt b/db/re/item_db.txt index 32f4bc30d2..0c7c77e842 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -13457,3 +13457,10 @@ //=================================================================== 32350,Farthezan,Farthezan,5,20,,1100,130:180,,1,2,0x00004000,56,2,2,4,170,1,2,{ .@r = getrefine(); bonus2 bSkillAtk,"PA_PRESSURE",40; bonus bVariableCastrate,-.@r; if (.@r>=9) bonus2 bSkillAtk,"LG_RAYOFGENESIS",30; if (.@r>=11) { bonus2 bSkillAtk,"LG_RAYOFGENESIS",20; bonus2 bSkillAtk,"PA_PRESSURE",20; } },{},{} 32351,Estal,Estal,5,20,,700,195,,1,2,0x00040000,56,2,2,4,170,1,2,{ .@r = getrefine(); bonus2 bSkillCooldown,"GN_SPORE_EXPLOSION",-1000; bonus bBaseAtk,4*.@r; if (.@r>=11) .@val = 50; else if (.@r>=9) .@val = 30; bonus2 bSkillAtk,"GN_SPORE_EXPLOSION",.@val; },{},{} + +//100231,Ref_T_Potion,Golden X,0,10,,30,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_REF_T_POTION,30000,0; },{},{} +//100232,Add_Atk_Potion,Red Herb Activator,0,10,,30,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_ADD_ATK_DAMAGE,500000,15; },{},{} +//100233,Add_Matk_Potion,Blue Herb Activator,0,10,,30,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_ADD_MATK_DAMAGE,500000,15; },{},{} +//1100003,Concentrated_R_P,Concentrated Red Potion,0,10,,2,,,,,0xFFFFFFFF,63,2,,,120,,,{ itemheal rand(655,675),0; },{},{} +//1100004,Concentrated_B_P,Concentrated Blue Potion,0,10,,2,,,,,0xFFFFFFFF,63,2,,,120,,,{ itemheal 0,rand(340,360); },{},{} +//1100005,Concentrated_G_P,Concentrated Gold Potion,0,10,,2,,,,,0xFFFFFFFF,63,2,,,180,,,{ itemheal rand(2730,2750),0; },{},{} diff --git a/db/re/produce_db.txt b/db/re/produce_db.txt index 6160b53b91..8016bc9fd0 100644 --- a/db/re/produce_db.txt +++ b/db/re/produce_db.txt @@ -665,8 +665,8 @@ //267,1100003,29,2497,1,1092,10,1093,10,11621,15 //-- Concentrated Blue Syrup Potion <-- GN_S_PHARMACY Lvl, 10 Empty Testtube, 10 Empty Potion Bottle, 15 Blue Syrup //268,1100004,29,2497,1,1092,10,1093,10,11624,15 -//-- Concentrated Golden Syrup Potion <-- GN_S_PHARMACY Lvl, 10 Empty Testtube, 10 Empty Potion Bottle, 15 White Syrup, 15 Yellow Syrup -//269,1100005,29,2497,1,1092,10,1093,10,11623,15,11622,15 +//-- Concentrated Golden Syrup Potion <-- GN_S_PHARMACY Lvl, 10 Empty Testtube, 10 Empty Potion Bottle, 15 White Syrup, 10 Yellow Syrup +//269,1100005,29,2497,1,1092,10,1093,10,11623,15,11622,10 //=============================================== //--------------------LEVEL 30----------- diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index e4226fda02..523b3f10c7 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -27990,6 +27990,7 @@ Body: Flags: TargetTrap: true IgnoreLandProtector: true + Range: 5 Hit: Multi_Hit HitCount: 2 Element: Weapon @@ -28177,15 +28178,9 @@ Body: Name: GN_FIRE_EXPANSION_ACID Description: Fire Expansion Acid MaxLevel: 10 - Type: Misc + Type: Weapon TargetType: Attack - DamageFlags: - IgnoreDefense: true - IgnoreFlee: true - Flags: - IgnoreBgReduction: true - IgnoreGvgReduction: true - Range: 11 + Range: 9 Hit: Multi_Hit HitCount: - Level: 1 @@ -28273,11 +28268,9 @@ Body: MaxLevel: 5 Type: Weapon TargetType: Attack - DamageFlags: - IgnoreElement: true - IgnoreDefCard: true Hit: Single HitCount: 1 + Element: Weapon CopyFlags: Skill: Reproduce: true @@ -34493,7 +34486,6 @@ Body: Time: 3500 Duration1: 500 Duration2: 120000 - FixedCastTime: 500 Requires: SpCost: - Level: 1 diff --git a/src/map/battle.cpp b/src/map/battle.cpp index b2d7d05fd2..205ae04655 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -1351,13 +1351,7 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam } #endif - if (sc->data[SC_DEFENDER] && - skill_id != NJ_ZENYNAGE && skill_id != KO_MUCHANAGE && -#ifdef RENEWAL - ((flag&(BF_LONG|BF_WEAPON)) == (BF_LONG|BF_WEAPON) || skill_id == GN_FIRE_EXPANSION_ACID)) -#else - (flag&(BF_LONG|BF_WEAPON)) == (BF_LONG|BF_WEAPON)) -#endif + if (sc->data[SC_DEFENDER] && skill_id != NJ_ZENYNAGE && skill_id != KO_MUCHANAGE && (flag&(BF_LONG|BF_WEAPON)) == (BF_LONG|BF_WEAPON)) damage -= damage * sc->data[SC_DEFENDER]->val2 / 100; if(sc->data[SC_ADJUSTMENT] && (flag&(BF_LONG|BF_WEAPON)) == (BF_LONG|BF_WEAPON)) @@ -1625,6 +1619,11 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam if (hd && (rnd()%100<50) ) hom_addspiritball(hd, 10); // According to WarpPortal, this is a flat 50% chance } + + if (flag & BF_WEAPON && (sce = sc->data[SC_ADD_ATK_DAMAGE])) + damage += damage * sce->val1 / 100; + if (flag & BF_MAGIC && (sce = sc->data[SC_ADD_MATK_DAMAGE])) + damage += damage * sce->val1 / 100; } //End of caster SC_ check if (tsc && tsc->count) { @@ -3902,9 +3901,10 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list * skillratio += 100 + 50 * skill_lv; #endif break; + case GN_FIRE_EXPANSION_ACID: #ifdef RENEWAL case CR_ACIDDEMONSTRATION: - skillratio += -100 + 200 * skill_lv + sstatus->int_ + sstatus->vit; // !TODO: Confirm status bonus + skillratio += -100 + 200 * skill_lv + sstatus->int_ + tstatus->vit; // !TODO: Confirm status bonus if (target->type == BL_PC) skillratio /= 2; break; @@ -4446,9 +4446,9 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list * RE_LVL_DMOD(100); break; case GN_SPORE_EXPLOSION: - if (wd->miscflag & 2048) - skillratio += 200; // Target - skillratio += -100 + 180 * skill_lv + sstatus->int_; + skillratio += -100 + 180 * skill_lv; + if (wd->miscflag & 8) + skillratio += 200 + sstatus->int_; // Target receives extra damage RE_LVL_DMOD(100); break; case GN_WALLOFTHORN: @@ -4488,7 +4488,7 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list * } break; case GN_HELLS_PLANT_ATK: - skillratio += -100 + 500 * skill_lv + sstatus->int_ * (10 - (sd ? pc_checkskill(sd, AM_CANNIBALIZE) : 0)); // !TODO: Confirm INT and Cannibalize bonus + skillratio += -100 + 500 * skill_lv + sstatus->int_ * (sd ? pc_checkskill(sd, AM_CANNIBALIZE) : 5); // !TODO: Confirm INT and Cannibalize bonus RE_LVL_DMOD(100); break; // Physical Elemantal Spirits Attack Skills @@ -5864,7 +5864,6 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl switch(skill_id) { case NJ_ISSEN: case ASC_BREAKER: - case GN_FIRE_EXPANSION_ACID: break; //These skills will do a card fix later default: #endif @@ -5885,7 +5884,6 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl case MC_CARTREVOLUTION: case MO_INVESTIGATE: case SR_GATEOFHELL: - case GN_FIRE_EXPANSION_ACID: case KO_BAKURETSU: //case NC_MAGMA_ERUPTION: // Forced to neutral element @@ -5945,7 +5943,6 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl switch (skill_id) { case NJ_ISSEN: case ASC_BREAKER: - case GN_FIRE_EXPANSION_ACID: return wd; //These skills will do a GVG fix later default: #endif @@ -6940,30 +6937,15 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list * md.damage = 0; } break; - case GN_FIRE_EXPANSION_ACID: -#ifdef RENEWAL - // Official Renewal formula [helvetica] - // damage = 7 * ((atk + matk)/skill level) * (target vit/100) - // skill is a "forced neutral" type skill, it benefits from weapon element but final damage - // is considered "neutral" for purposes of resistances - { - struct Damage atk = battle_calc_weapon_attack(src, target, skill_id, skill_lv, 0); - struct Damage matk = battle_calc_magic_attack(src, target, skill_id, skill_lv, 0); - md.damage = 7 * ((atk.damage/skill_lv + matk.damage/skill_lv) * tstatus->vit / 100 ); - - // AD benefits from endow/element but damage is forced back to neutral - md.damage = battle_attr_fix(src, target, md.damage, ELE_NEUTRAL, tstatus->def_ele, tstatus->ele_lv); - } - // Fall through -#else +#ifndef RENEWAL case CR_ACIDDEMONSTRATION: if(tstatus->vit+sstatus->int_) //crash fix md.damage = (int)((int64)7*tstatus->vit*sstatus->int_*sstatus->int_ / (10*(tstatus->vit+sstatus->int_))); else md.damage = 0; if (tsd) md.damage>>=1; -#endif break; +#endif case NJ_ZENYNAGE: case KO_MUCHANAGE: md.damage = skill_get_zeny(skill_id, skill_lv); @@ -7274,6 +7256,9 @@ int64 battle_calc_return_damage(struct block_list* bl, struct block_list *src, i if (sc && sc->data[SC_WHITEIMPRISON]) return 0; // White Imprison does not reflect any damage + if (ssc && ssc->data[SC_REF_T_POTION]) + return 0; + if (flag & BF_SHORT) {//Bounces back part of the damage. if ( (skill_get_inf2(skill_id, INF2_ISTRAP) || !status_reflect) && sd && sd->bonus.short_weapon_damage_return ) { rdamage += damage * sd->bonus.short_weapon_damage_return / 100; diff --git a/src/map/pc.cpp b/src/map/pc.cpp index ded9f0889e..be33c709d3 100755 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -5822,6 +5822,7 @@ enum e_setpos pc_setpos(struct map_session_data* sd, unsigned short mapindex, in int16 m = map_mapindex2mapid(mapindex); struct map_data *mapdata = map_getmapdata(m); + status_change *sc = status_get_sc(&sd->bl); sd->state.changemap = (sd->mapindex != mapindex); sd->state.warping = 1; @@ -5842,8 +5843,8 @@ enum e_setpos pc_setpos(struct map_session_data* sd, unsigned short mapindex, in bg_team_leave(sd, false, true); sd->state.pmap = sd->bl.m; - if (sd->sc.count) { // Cancel some map related stuff. - if (sd->sc.data[SC_JAILED]) + if (sc && sc->count) { // Cancel some map related stuff. + if (sc->data[SC_JAILED]) return SETPOS_MAPINDEX; //You may not get out! status_change_end(&sd->bl, SC_BOSSMAPINFO, INVALID_TIMER); status_change_end(&sd->bl, SC_WARM, INVALID_TIMER); @@ -5851,8 +5852,8 @@ enum e_setpos pc_setpos(struct map_session_data* sd, unsigned short mapindex, in status_change_end(&sd->bl, SC_MOON_COMFORT, INVALID_TIMER); status_change_end(&sd->bl, SC_STAR_COMFORT, INVALID_TIMER); status_change_end(&sd->bl, SC_MIRACLE, INVALID_TIMER); - if (sd->sc.data[SC_KNOWLEDGE]) { - struct status_change_entry *sce = sd->sc.data[SC_KNOWLEDGE]; + if (sc->data[SC_KNOWLEDGE]) { + struct status_change_entry *sce = sc->data[SC_KNOWLEDGE]; if (sce->timer != INVALID_TIMER) delete_timer(sce->timer, status_change_timer); sce->timer = add_timer(gettick() + skill_get_time(SG_KNOWLEDGE, sce->val1), status_change_timer, sd->bl.id, SC_KNOWLEDGE); @@ -5962,6 +5963,9 @@ enum e_setpos pc_setpos(struct map_session_data* sd, unsigned short mapindex, in } else if(sd->state.active) //Tag player for rewarping after map-loading is done. [Skotlex] sd->state.rewarp = 1; + if (sc && sc->data[SC_HELLS_PLANT]) + skill_unit_move_unit_group(skill_id2group(sc->data[SC_HELLS_PLANT]->val4), m, x - sd->bl.x, y - sd->bl.y); + sd->mapindex = mapindex; sd->bl.m = m; sd->bl.x = sd->ud.to_x = x; @@ -6006,7 +6010,7 @@ enum e_setpos pc_setpos(struct map_session_data* sd, unsigned short mapindex, in } pc_cell_basilica(sd); - + //check if we gonna be rewarped [lighta] if(npc_check_areanpc(1,m,x,y,0)){ sd->count_rewarp++; diff --git a/src/map/script_constants.hpp b/src/map/script_constants.hpp index fb2b68cc1a..cae9051e67 100644 --- a/src/map/script_constants.hpp +++ b/src/map/script_constants.hpp @@ -1579,6 +1579,9 @@ export_constant(SC_INCREASE_MAXHP); export_constant(SC_INCREASE_MAXSP); export_constant(SC_HELPANGEL); + export_constant(SC_REF_T_POTION); + export_constant(SC_ADD_ATK_DAMAGE); + export_constant(SC_ADD_MATK_DAMAGE); #ifdef RENEWAL export_constant(SC_EXTREMITYFIST2); #endif diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 34b1862845..549c238fff 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -3631,7 +3631,6 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * case EL_HURRICANE: case EL_HURRICANE_ATK: case KO_BAKURETSU: - case GN_CRAZYWEED_ATK: case GN_HELLS_PLANT_ATK: case SU_SV_ROOTTWIST_ATK: dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,DMG_SPLASH); @@ -5217,13 +5216,6 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint if( skill_area_temp[1] != bl->id && !inf2[INF2_ISNPC] ) sflag |= SD_ANIMATION; // original target gets no animation (as well as all NPC skills) - switch (skill_id) { - case GN_SPORE_EXPLOSION: - if (flag&2 && skill_area_temp[1] == bl->id) - sflag |= 2048; // Flag for main target - break; - } - // If a enemy player is standing next to a mob when splash Es- skill is casted, the player won't get hurt. if ((skill_id == SP_SHA || skill_id == SP_SWHOO) && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) break; @@ -11101,7 +11093,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui break; case GN_SPORE_EXPLOSION: clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - skill_castend_damage_id(src, bl, skill_id, skill_lv, tick, flag|1|2); // First attack to target + skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag|8); sc_start4(src, bl, type, 100, skill_lv, skill_id, src->id, 0, skill_get_time(skill_id, skill_lv)); break; case GN_MANDRAGORA: @@ -13046,7 +13038,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui break; case GN_HELLS_PLANT: - skill_clear_unitgroup(src); + status_change_end(src, type, INVALID_TIMER); // Remove previous group if ((sg = skill_unitsetting(src, skill_id, skill_lv, src->x, src->y, 0)) != nullptr) sc_start4(src, src, type, 100, skill_lv, 0, 0, sg->group_id, skill_get_time(skill_id, skill_lv)); break; @@ -19110,7 +19102,6 @@ int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int li case SG_MOON_WARM: case SG_STAR_WARM: case LG_BANDING: - case GN_HELLS_PLANT: { status_change *sc = status_get_sc(src); sc_type type = status_skill2sc(group->skill_id); diff --git a/src/map/status.cpp b/src/map/status.cpp index 98fce72184..acadfaa983 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -966,7 +966,7 @@ void initChangeTables(void) set_sc( GN_SPORE_EXPLOSION , SC_SPORE_EXPLOSION , EFST_SPORE_EXPLOSION, SCB_NONE ); set_sc( GN_FIRE_EXPANSION_SMOKE_POWDER , SC_SMOKEPOWDER , EFST_FIRE_EXPANSION_SMOKE_POWDER, SCB_FLEE ); set_sc( GN_FIRE_EXPANSION_TEAR_GAS , SC_TEARGAS , EFST_FIRE_EXPANSION_TEAR_GAS , SCB_HIT|SCB_FLEE ); - add_sc( GN_HELLS_PLANT , SC_HELLS_PLANT ); + set_sc( GN_HELLS_PLANT , SC_HELLS_PLANT , EFST_HELLS_PLANT_ARMOR , SCB_NONE ); set_sc( GN_MANDRAGORA , SC_MANDRAGORA , EFST_MANDRAGORA , SCB_INT ); set_sc_with_vfx( GN_ILLUSIONDOPING , SC_ILLUSIONDOPING , EFST_ILLUSIONDOPING , SCB_HIT ); @@ -1381,6 +1381,9 @@ void initChangeTables(void) StatusIconChangeTable[SC_ANCILLA] = EFST_ANCILLA; StatusIconChangeTable[SC_WEAPONBLOCK_ON] = EFST_WEAPONBLOCK_ON; + StatusIconChangeTable[SC_REF_T_POTION] = EFST_REF_T_POTION; + StatusIconChangeTable[SC_ADD_ATK_DAMAGE] = EFST_ADD_ATK_DAMAGE; + StatusIconChangeTable[SC_ADD_MATK_DAMAGE] = EFST_ADD_MATK_DAMAGE; // Battleground Queue StatusIconChangeTable[SC_ENTRY_QUEUE_APPLY_DELAY] = EFST_ENTRY_QUEUE_APPLY_DELAY; diff --git a/src/map/status.hpp b/src/map/status.hpp index ff114ac473..447316faec 100644 --- a/src/map/status.hpp +++ b/src/map/status.hpp @@ -923,6 +923,9 @@ enum sc_type : int16 { SC_HELLS_PLANT, SC_INCREASE_MAXHP, // EFST_ATKER_ASPD SC_INCREASE_MAXSP, // EFST_ATKER_MOVESPEED + SC_REF_T_POTION, + SC_ADD_ATK_DAMAGE, + SC_ADD_MATK_DAMAGE, SC_HELPANGEL, diff --git a/src/map/unit.cpp b/src/map/unit.cpp index 25acc3c751..6c8b6de35b 100644 --- a/src/map/unit.cpp +++ b/src/map/unit.cpp @@ -2957,7 +2957,6 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER); status_change_end(bl, SC_NEUTRALBARRIER_MASTER, INVALID_TIMER); status_change_end(bl, SC_STEALTHFIELD_MASTER, INVALID_TIMER); - status_change_end(bl, SC_HELLS_PLANT, INVALID_TIMER); status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER); status_change_end(bl, SC__MANHOLE, INVALID_TIMER); status_change_end(bl, SC_VACUUM_EXTREME, INVALID_TIMER); From 6ed4dbd6d9dfb04e8924221986baf63a7c000e23 Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Thu, 14 May 2020 03:03:05 +0200 Subject: [PATCH 013/141] SQL synchronization --- sql-files/item_db_re.sql | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sql-files/item_db_re.sql b/sql-files/item_db_re.sql index d991a3d07c..359d863265 100644 --- a/sql-files/item_db_re.sql +++ b/sql-files/item_db_re.sql @@ -13490,3 +13490,10 @@ REPLACE INTO `item_db_re` VALUES (32302,'Crimson_Rose','Crimson Rose',5,20,NULL, REPLACE INTO `item_db_re` VALUES (32350,'Farthezan','Farthezan',5,20,NULL,1100,'130:180',NULL,1,2,0x00004000,56,2,2,4,'170',1,2,'.@r = getrefine(); bonus2 bSkillAtk,"PA_PRESSURE",40; bonus bVariableCastrate,-.@r; if (.@r>=9) bonus2 bSkillAtk,"LG_RAYOFGENESIS",30; if (.@r>=11) { bonus2 bSkillAtk,"LG_RAYOFGENESIS",20; bonus2 bSkillAtk,"PA_PRESSURE",20; }',NULL,NULL); REPLACE INTO `item_db_re` VALUES (32351,'Estal','Estal',5,20,NULL,700,'195',NULL,1,2,0x00040000,56,2,2,4,'170',1,2,'.@r = getrefine(); bonus2 bSkillCooldown,"GN_SPORE_EXPLOSION",-1000; bonus bBaseAtk,4*.@r; if (.@r>=11) .@val = 50; else if (.@r>=9) .@val = 30; bonus2 bSkillAtk,"GN_SPORE_EXPLOSION",.@val;',NULL,NULL); +#REPLACE INTO `item_db_re` VALUES (100231,'Ref_T_Potion','Golden X',0,10,NULL,30,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_REF_T_POTION,30000,0;',NULL,NULL); +#REPLACE INTO `item_db_re` VALUES (100232,'Add_Atk_Potion','Red Herb Activator',0,10,NULL,30,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_ADD_ATK_DAMAGE,500000,15;',NULL,NULL); +#REPLACE INTO `item_db_re` VALUES (100233,'Add_Matk_Potion','Blue Herb Activator',0,10,NULL,30,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_ADD_MATK_DAMAGE,500000,15;',NULL,NULL); +#REPLACE INTO `item_db_re` VALUES (1100003,'Concentrated_R_P','Concentrated Red Potion',0,10,NULL,2,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,'120',NULL,NULL,'itemheal rand(655,675),0;',NULL,NULL); +#REPLACE INTO `item_db_re` VALUES (1100004,'Concentrated_B_P','Concentrated Blue Potion',0,10,NULL,2,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,'120',NULL,NULL,'itemheal 0,rand(340,360);',NULL,NULL); +#REPLACE INTO `item_db_re` VALUES (1100005,'Concentrated_G_P','Concentrated Gold Potion',0,10,NULL,2,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,'180',NULL,NULL,'itemheal rand(2730,2750),0;',NULL,NULL); + From f60da2b3fa76a6121f00a802ca19d0f99b2751d4 Mon Sep 17 00:00:00 2001 From: Mark Hester Date: Thu, 14 May 2020 03:02:10 +0100 Subject: [PATCH 014/141] Allow blank SQL passwords (#4827) * Allow blank password for log and login schema. Thanks to @marky291! --- src/login/account.cpp | 2 +- src/map/map.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/login/account.cpp b/src/login/account.cpp index 522bdb6e40..7c4a3c9a6d 100644 --- a/src/login/account.cpp +++ b/src/login/account.cpp @@ -81,7 +81,7 @@ AccountDB* account_db_sql(void) { safestrncpy(db->db_hostname, "127.0.0.1", sizeof(db->db_hostname)); db->db_port = 3306; safestrncpy(db->db_username, "ragnarok", sizeof(db->db_username)); - safestrncpy(db->db_password, "ragnarok", sizeof(db->db_password)); + safestrncpy(db->db_password, "", sizeof(db->db_password)); safestrncpy(db->db_database, "ragnarok", sizeof(db->db_database)); safestrncpy(db->codepage, "", sizeof(db->codepage)); // other settings diff --git a/src/map/map.cpp b/src/map/map.cpp index 5aab4518b8..fbee5a8c7e 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -93,7 +93,7 @@ char guild_storage_log_table[32] = "guild_storage_log"; char log_db_ip[64] = "127.0.0.1"; int log_db_port = 3306; char log_db_id[32] = "ragnarok"; -char log_db_pw[32] = "ragnarok"; +char log_db_pw[32] = ""; char log_db_db[32] = "log"; Sql* logmysql_handle; From 9bbf4f17e3ec3e862ea34971586f323c1915614c Mon Sep 17 00:00:00 2001 From: Aleos Date: Thu, 14 May 2020 08:58:50 -0400 Subject: [PATCH 015/141] Fixes Ur's and Peuz's Greave refine bonus (#4890) * Fixes #4889. * Fixes Ur's and Peuz's Greave giving negative stats if the refine is less than 8. Thanks to @Questune09! --- db/re/item_db.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/re/item_db.txt b/db/re/item_db.txt index 0c7c77e842..0400afa62e 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -1697,8 +1697,8 @@ 2472,Shoes_Of_Judgement,Shoes Of Judgement,4,20,,300,,12,,0,0x00000100,63,2,64,,100,1,0,{ bonus2 bSkillAtk,"AB_JUDEX",30; bonus2 bSkillUseSP,"AB_JUDEX",-40; bonus bMaxSP,150; },{},{} 2473,Para_Team_Boots4,Eden Team Boots IV,4,0,,0,,20,,0,0xFFFFFFFF,63,2,64,,60,0,0,{ bonus bAgi,1; bonus bVit,1; bonus bHPrecovRate,28; bonus bSPrecovRate,12; },{},{} 2474,Lehmannza_Shoes,Lehmannza Shoes,4,20,,500,,3,,1,0xFFFFFFFF,63,2,64,,60,1,0,{ bonus bMdef,3; },{},{} -2475,Ur_Greave,Ur's Greaves,4,20,,900,,32,,1,0x00000080,56,2,64,,100,1,0,{ bonus bMaxSP,40; .@r = getrefine(); bonus bMaxHPrate,(.@r<=7?.@r-7:1); },{},{} -2476,Peuz_Greave,Peuz's Greaves,4,20,,900,,32,,1,0x00000080,56,2,64,,100,1,0,{ bonus bMaxSP,40; .@r = getrefine(); bonus bAgi,(.@r<=7?.@r-7:1); },{},{} +2475,Ur_Greave,Ur's Greaves,4,20,,900,,32,,1,0x00000080,56,2,64,,100,1,0,{ bonus bMaxSP,40; .@r = getrefine(); if(.@r>7) bonus bMaxHPrate,.@r-7; },{},{} +2476,Peuz_Greave,Peuz's Greaves,4,20,,900,,32,,1,0x00000080,56,2,64,,100,1,0,{ bonus bMaxSP,40; .@r = getrefine(); if(.@r>7) bonus bAgi,.@r-7; },{},{} 2477,Sabah_Shoes,Sapha Shoes,4,20,,400,,18,,1,0x00001000,63,2,64,,100,1,0,{ bonus bMaxSP,30; bonus bLuk,3; },{},{} 2478,Nab_Shoes,Nab Shoes,4,20,,400,,18,,1,0x00001000,63,2,64,,100,1,0,{ bonus bFlee,3; bonus bInt,2; },{},{} 2479,White_Wing_Boots,White Wing Boots,4,20,,400,,18,,1,0x00000800,63,2,64,,100,1,0,{ bonus bAgi,2; bonus2 bSkillUseSP,"RA_AIMEDBOLT",10; },{},{} From 0a0b05e6a1801050d1b49d7993140a2dd7a7b05e Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Thu, 14 May 2020 14:58:58 +0200 Subject: [PATCH 016/141] SQL synchronization --- sql-files/item_db_re.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql-files/item_db_re.sql b/sql-files/item_db_re.sql index 359d863265..124e9efb4c 100644 --- a/sql-files/item_db_re.sql +++ b/sql-files/item_db_re.sql @@ -1729,8 +1729,8 @@ REPLACE INTO `item_db_re` VALUES (2471,'Shoes_Of_Affection','Shoes Of Affection' REPLACE INTO `item_db_re` VALUES (2472,'Shoes_Of_Judgement','Shoes Of Judgement',4,20,NULL,300,NULL,12,NULL,0,0x00000100,63,2,64,NULL,'100',1,0,'bonus2 bSkillAtk,"AB_JUDEX",30; bonus2 bSkillUseSP,"AB_JUDEX",-40; bonus bMaxSP,150;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (2473,'Para_Team_Boots4','Eden Team Boots IV',4,0,NULL,0,NULL,20,NULL,0,0xFFFFFFFF,63,2,64,NULL,'60',0,0,'bonus bAgi,1; bonus bVit,1; bonus bHPrecovRate,28; bonus bSPrecovRate,12;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (2474,'Lehmannza_Shoes','Lehmannza Shoes',4,20,NULL,500,NULL,3,NULL,1,0xFFFFFFFF,63,2,64,NULL,'60',1,0,'bonus bMdef,3;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (2475,'Ur_Greave','Ur\'s Greaves',4,20,NULL,900,NULL,32,NULL,1,0x00000080,56,2,64,NULL,'100',1,0,'bonus bMaxSP,40; .@r = getrefine(); bonus bMaxHPrate,(.@r<=7?.@r-7:1);',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (2476,'Peuz_Greave','Peuz\'s Greaves',4,20,NULL,900,NULL,32,NULL,1,0x00000080,56,2,64,NULL,'100',1,0,'bonus bMaxSP,40; .@r = getrefine(); bonus bAgi,(.@r<=7?.@r-7:1);',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (2475,'Ur_Greave','Ur\'s Greaves',4,20,NULL,900,NULL,32,NULL,1,0x00000080,56,2,64,NULL,'100',1,0,'bonus bMaxSP,40; .@r = getrefine(); if(.@r>7) bonus bMaxHPrate,.@r-7;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (2476,'Peuz_Greave','Peuz\'s Greaves',4,20,NULL,900,NULL,32,NULL,1,0x00000080,56,2,64,NULL,'100',1,0,'bonus bMaxSP,40; .@r = getrefine(); if(.@r>7) bonus bAgi,.@r-7;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (2477,'Sabah_Shoes','Sapha Shoes',4,20,NULL,400,NULL,18,NULL,1,0x00001000,63,2,64,NULL,'100',1,0,'bonus bMaxSP,30; bonus bLuk,3;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (2478,'Nab_Shoes','Nab Shoes',4,20,NULL,400,NULL,18,NULL,1,0x00001000,63,2,64,NULL,'100',1,0,'bonus bFlee,3; bonus bInt,2;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (2479,'White_Wing_Boots','White Wing Boots',4,20,NULL,400,NULL,18,NULL,1,0x00000800,63,2,64,NULL,'100',1,0,'bonus bAgi,2; bonus2 bSkillUseSP,"RA_AIMEDBOLT",10;',NULL,NULL); From 7004fe3d95610a8311628a53e84201dd8f6f8863 Mon Sep 17 00:00:00 2001 From: aleos Date: Thu, 14 May 2020 22:26:54 -0400 Subject: [PATCH 017/141] Fixes Basilica duration * Fixes #4913. * Fixes Basilica duration to be 30 + 30 * skill_lv. Thanks to @Badarosk0! --- db/re/skill_db.yml | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index 523b3f10c7..60e84670ad 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -9900,26 +9900,15 @@ Body: Time: 1000 Duration1: - Level: 1 - Time: 20000 + Time: 60000 - Level: 2 - Time: 25000 + Time: 90000 - Level: 3 - Time: 30000 + Time: 120000 - Level: 4 - Time: 35000 + Time: 150000 - Level: 5 - Time: 40000 - Duration2: - - Level: 1 - Time: 20000 - - Level: 2 - Time: 25000 - - Level: 3 - Time: 30000 - - Level: 4 - Time: 35000 - - Level: 5 - Time: 40000 + Time: 180000 Cooldown: 30000 FixedCastTime: - Level: 1 From 15c954ef23d9a6bfa6ea542424df47d29ff80dea Mon Sep 17 00:00:00 2001 From: Aleos Date: Sun, 17 May 2020 15:28:13 -0400 Subject: [PATCH 018/141] Fixes instance map name faults (#4945) * Fixes #4915. * Fixes instance map names when it reaches 1000. * Fixes instances with more than 8 character crashing the client. * Other minor cleanups. Thanks to @voyfmyuh and @Atemo! Co-authored-by: Atemo --- src/map/instance.cpp | 25 +++++++++++++------------ src/map/map.cpp | 8 ++++---- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/map/instance.cpp b/src/map/instance.cpp index f56c768bc7..3a9004d6bd 100644 --- a/src/map/instance.cpp +++ b/src/map/instance.cpp @@ -616,7 +616,7 @@ int instance_create(int owner_id, const char *name, e_instance_mode mode) { clif_instance_create(instance_id, instance_wait.id.size()); instance_subscription_timer(0,0,0,0); - ShowInfo("[Instance] Created: %s (%d).\n", name, instance_id); + ShowInfo("[Instance] Created: %s (%d)\n", name, instance_id); // Start the instance timer on instance creation instance_startkeeptimer(entry, instance_id); @@ -708,11 +708,13 @@ int instance_addmap(int instance_id) { * Returns an instance map ID * @param m: Source map ID * @param instance_id: Instance to search - * @return Map ID in this instance + * @return Map ID in this instance or -1 on failure */ int16 instance_mapid(int16 m, int instance_id) { - if(m < 0) { + const char *name = map_mapid2mapname(m); + + if (name == nullptr) { ShowError("instance_mapid: Map ID %d does not exist.\n", m); return -1; } @@ -722,18 +724,17 @@ int16 instance_mapid(int16 m, int instance_id) if(!idata || idata->state != INSTANCE_BUSY) return -1; - const char *iname = map_mapid2mapname(m); - for (const auto &it : idata->map) { if (it.src_m == m) { - char alt_name[MAP_NAME_LENGTH]; + char iname[MAP_NAME_LENGTH], alt_name[MAP_NAME_LENGTH]; + + strcpy(iname, name); if (!(strchr(iname, '@')) && strlen(iname) > 8) { - memmove((void*)iname, iname + (strlen(iname) - 9), strlen(iname)); - snprintf(alt_name, sizeof(alt_name), "%d#%s", instance_id, iname); - } - else - snprintf(alt_name, sizeof(alt_name), "%.3d%s", instance_id, iname); + memmove(iname, iname + (strlen(iname) - 9), strlen(iname)); + snprintf(alt_name, sizeof(alt_name), "%d#%s", (instance_id % 1000), iname); + } else + snprintf(alt_name, sizeof(alt_name), "%.3d%s", (instance_id % 1000), iname); return map_mapname2mapid(alt_name); } } @@ -847,7 +848,7 @@ bool instance_destroy(int instance_id) if( idata->regs.arrays ) idata->regs.arrays->destroy(idata->regs.arrays, script_free_array_db); - ShowInfo("[Instance] Destroyed %d.\n", instance_id); + ShowInfo("[Instance] Destroyed: %s (%d)\n", instance_db.find(idata->id)->name.c_str(), instance_id); instances.erase(instance_id); diff --git a/src/map/map.cpp b/src/map/map.cpp index fbee5a8c7e..6227ffb4ed 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -2703,7 +2703,7 @@ int map_addinstancemap(int src_m, int instance_id) if(strlen(name) > 20) { // against buffer overflow - ShowError("map_addinstancemap: can't add long map name \"%s\"\n", name); + ShowError("map_addinstancemap: Map name \"%s\" is too long.\n", name); return -2; } @@ -2732,7 +2732,7 @@ int map_addinstancemap(int src_m, int instance_id) // Alter the name // Due to this being custom we only worry about preserving as many characters as necessary for accurate map distinguishing // This also allows us to maintain complete independence with main map functions - if ((strchr(iname, '@') == NULL) && strlen(iname) > 8) { + if ((strchr(iname, '@') == nullptr) && strlen(iname) > 8) { memmove(iname, iname + (strlen(iname) - 9), strlen(iname)); snprintf(dst_map->name, sizeof(dst_map->name), "%d#%s", (instance_id % 1000), iname); } else @@ -2766,12 +2766,12 @@ int map_addinstancemap(int src_m, int instance_id) dst_map->block_mob = (struct block_list **)aCalloc(1,size); dst_map->index = mapindex_addmap(-1, dst_map->name); - dst_map->channel = NULL; + dst_map->channel = nullptr; dst_map->mob_delete_timer = INVALID_TIMER; map_data_copy(dst_map, src_map); - ShowInfo("[Instance] Created map '%s' ('%d') from map '%s' ('%d')\n", dst_map->name, dst_map->m, name, src_map->m); + ShowInfo("[Instance] Created map '%s' (%d) from '%s' (%d).\n", dst_map->name, dst_map->m, name, src_map->m); map_addmap2db(dst_map); From 4534be1a253b2d32d5862061c0bf90979ff0933d Mon Sep 17 00:00:00 2001 From: flamefury Date: Sun, 17 May 2020 15:49:29 -0700 Subject: [PATCH 019/141] Suffragium should be a self AoE to party (#4955) * Fixes #4954. * Suffragium is a self cast skill that applies to the party in a 31x31 area, including self. Thanks to @flamefury! --- db/re/skill_db.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index 60e84670ad..29eea36040 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -2381,16 +2381,13 @@ Body: Description: Suffragium MaxLevel: 3 Type: Magic - TargetType: Support + TargetType: Self DamageFlags: NoDamage: true Splash: true - Flags: - NoTargetSelf: true - Range: 9 Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 18 CastCancel: true CastTime: 1000 AfterCastActDelay: 1000 From 9dd553b87d9019084bb417a0bb7a91f9886bfe3b Mon Sep 17 00:00:00 2001 From: Sader Fawall Date: Mon, 18 May 2020 01:22:14 +0200 Subject: [PATCH 020/141] Updates item storages documentation (#4937) * Updates the documentation for the different types of player item storages to reflect that current maximum capacity. Thanks to @sader1992! --- src/common/mmo.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/mmo.hpp b/src/common/mmo.hpp index 896faadb3b..d30158aff9 100644 --- a/src/common/mmo.hpp +++ b/src/common/mmo.hpp @@ -377,7 +377,7 @@ struct s_storage { unsigned get : 1; unsigned put : 1; } state; - union { // Max for inventory, storage, cart, and guild storage are 1637 each without changing this struct and struct item [2014/10/27] + union { // Max for inventory, storage, cart, and guild storage are 818 each without changing this struct and struct item [2016/08/14] struct item items_inventory[MAX_INVENTORY]; struct item items_storage[MAX_STORAGE]; struct item items_cart[MAX_CART]; From 83fc3e270a297b88f6ba31ee6c31a53fe6fe2792 Mon Sep 17 00:00:00 2001 From: Aleos Date: Sun, 17 May 2020 19:47:40 -0400 Subject: [PATCH 021/141] Adjusts area size config back to 14 cells (#4967) * Partial revert of ea8da71. * Adjusts the battle config for the area size back to 14 cells. * Adjusts the Bard/Dancer songs to be 15 cells rather than attempting to use AREA_SIZE. --- conf/battle/client.conf | 2 +- db/re/skill_db.yml | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/conf/battle/client.conf b/conf/battle/client.conf index dfe0ede82a..3bb10b7cd6 100644 --- a/conf/battle/client.conf +++ b/conf/battle/client.conf @@ -38,7 +38,7 @@ hide_woe_damage: no pet_hair_style: 100 // Visible area size (how many squares away from a player they can see) -area_size: 15 +area_size: 14 // Maximum walk path (how many cells a player can walk going to cursor) max_walk_path: 17 diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index 29eea36040..cad49b89ca 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -8553,7 +8553,7 @@ Body: IsEnsemble: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 60000 @@ -8607,7 +8607,7 @@ Body: IsEnsemble: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 60000 @@ -8639,7 +8639,7 @@ Body: IsEnsemble: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 60000 @@ -8693,7 +8693,7 @@ Body: IsEnsemble: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 60000 @@ -8715,7 +8715,7 @@ Body: IsEnsemble: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 60000 @@ -8833,7 +8833,7 @@ Body: IsSong: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 20000 @@ -8875,7 +8875,7 @@ Body: IsSong: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 20000 @@ -8917,7 +8917,7 @@ Body: IsSong: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 20000 @@ -8959,7 +8959,7 @@ Body: IsSong: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 20000 @@ -9085,7 +9085,7 @@ Body: IsSong: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 20000 @@ -9169,7 +9169,7 @@ Body: IsSong: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 20000 @@ -9211,7 +9211,7 @@ Body: IsSong: true Hit: Single HitCount: 1 - SplashArea: -1 + SplashArea: 15 CastTime: 1000 Duration1: 60000 Duration2: 20000 From fde031fb19a0017b2e9c6f0e4a398c9f48c51078 Mon Sep 17 00:00:00 2001 From: Aleos Date: Mon, 18 May 2020 09:44:12 -0400 Subject: [PATCH 022/141] Job Improvement Project - Minstrel/Wanderer (#4701) * Fixes #4211. * kRO Changelog: http://ro.gnjoy.com/news/update/View.asp?seq=235&curpage=1 * kRO Changelog 2: http://ro.gnjoy.com/news/update/View.asp?seq=246&curpage=1 Thanks to @Angelic234, @Litro, @LordWhiplash, @Feelmeone, @ecdarreola, and @teededung! --- db/import-tmpl/improvise_db.yml | 31 --- db/improvise_db.yml | 37 --- db/pre-re/skill_db.yml | 19 +- db/pre-re/skill_tree.txt | 24 +- db/re/improvise_db.yml | 65 ----- db/re/skill_db.yml | 337 ++++++++---------------- db/re/skill_nocast_db.txt | 4 + db/re/skill_tree.txt | 24 +- doc/skill_db.txt | 1 - doc/yaml/db/improvise_db.yml | 10 - sql-files/upgrades/upgrade_2020xxxx.sql | 3 + src/map/battle.cpp | 44 ++-- src/map/map-server.vcxproj | 1 - src/map/script_constants.hpp | 2 +- src/map/skill.cpp | 315 ++++++---------------- src/map/skill.hpp | 5 +- src/map/status.cpp | 121 ++++----- src/map/status.hpp | 1 + src/tool/csv2yaml.cpp | 28 -- 19 files changed, 296 insertions(+), 776 deletions(-) delete mode 100644 db/import-tmpl/improvise_db.yml delete mode 100644 db/improvise_db.yml delete mode 100644 db/re/improvise_db.yml delete mode 100644 doc/yaml/db/improvise_db.yml create mode 100644 sql-files/upgrades/upgrade_2020xxxx.sql diff --git a/db/import-tmpl/improvise_db.yml b/db/import-tmpl/improvise_db.yml deleted file mode 100644 index 2c0afce465..0000000000 --- a/db/import-tmpl/improvise_db.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This file is a part of rAthena. -# Copyright(C) 2019 rAthena Development Team -# https://rathena.org - https://github.com/rathena -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -########################################################################### -# Improvised Song Database -########################################################################### -# -# Improvised Song Settings -# -########################################################################### -# - Skill Skill to be casted by Improvised Song. -# Probability Probability of skill compared to others in database (1 = 0.01%, 10000 = 100%). -########################################################################### - -Header: - Type: IMPROVISED_SONG_DB - Version: 1 diff --git a/db/improvise_db.yml b/db/improvise_db.yml deleted file mode 100644 index 0e53f56229..0000000000 --- a/db/improvise_db.yml +++ /dev/null @@ -1,37 +0,0 @@ -# This file is a part of rAthena. -# Copyright(C) 2019 rAthena Development Team -# https://rathena.org - https://github.com/rathena -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -########################################################################### -# Improvised Song Database -########################################################################### -# -# Improvised Song Settings -# -########################################################################### -# - Skill Skill to be casted by Improvised Song. -# Probability Probability of skill compared to others in database (1 = 0.01%, 10000 = 100%). -########################################################################### - -Header: - Type: IMPROVISED_SONG_DB - Version: 1 - -Footer: - Imports: - - Path: db/re/improvise_db.yml - Mode: Renewal - - Path: db/import/improvise_db.yml diff --git a/db/pre-re/skill_db.yml b/db/pre-re/skill_db.yml index a01fc0e56f..66e834f131 100644 --- a/db/pre-re/skill_db.yml +++ b/db/pre-re/skill_db.yml @@ -2071,8 +2071,6 @@ Body: TargetType: Attack DamageFlags: NoDamage: true - Flags: - IncreaseGloomyDayDamage: true Range: -2 Hit: Single HitCount: 1 @@ -6699,8 +6697,6 @@ Body: MaxLevel: 5 Type: Weapon TargetType: Attack - Flags: - IncreaseGloomyDayDamage: true Range: - Level: 1 Size: 3 @@ -10658,7 +10654,6 @@ Body: IgnoreDefense: true Flags: TargetTrap: true - IncreaseGloomyDayDamage: true Range: 5 Hit: Multi_Hit HitCount: 5 @@ -12834,8 +12829,6 @@ Body: MaxLevel: 5 Type: Weapon TargetType: Attack - Flags: - IncreaseGloomyDayDamage: true Range: 4 Hit: Multi_Hit HitCount: 5 @@ -16847,8 +16840,6 @@ Body: MaxLevel: 10 Type: Weapon TargetType: Attack - Flags: - IncreaseGloomyDayDamage: true Range: 5 Hit: Multi_Hit HitCount: -5 @@ -19889,9 +19880,9 @@ Body: IgnoreDefCard: true Flags: AllowOnWarg: true - IncreaseDanceWithWugDamage: true IgnoreAutoGuard: true IgnoreCicada: true + IncreaseDanceWithWugDamage: true Range: 9 Hit: Single HitCount: 1 @@ -19919,9 +19910,9 @@ Body: IgnoreDefCard: true Flags: AlterRangeVulture: true - IncreaseDanceWithWugDamage: true IgnoreAutoGuard: true IgnoreCicada: true + IncreaseDanceWithWugDamage: true Range: 9 Hit: Single HitCount: 1 @@ -24313,7 +24304,7 @@ Body: Reproduce: true Requires: SpCost: 1 - - Id: 2417 + - Id: 2417 # Removed on kRO Name: WM_DOMINION_IMPULSE Description: Dominion Impulse MaxLevel: 1 @@ -24326,6 +24317,7 @@ Body: HitCount: 1 SplashArea: 5 AfterCastActDelay: 1000 + FixedCastTime: -1 Requires: SpCost: 10 - Id: 2418 @@ -32136,8 +32128,6 @@ Body: TargetType: Attack DamageFlags: NoDamage: true - Flags: - IncreaseGloomyDayDamage: true Range: -2 Hit: Single HitCount: 1 @@ -32162,7 +32152,6 @@ Body: IgnoreDefense: true Flags: TargetTrap: true - IncreaseGloomyDayDamage: true Range: 5 Hit: Multi_Hit HitCount: 5 diff --git a/db/pre-re/skill_tree.txt b/db/pre-re/skill_tree.txt index 30d0ab48ec..7cb347fe2a 100644 --- a/db/pre-re/skill_tree.txt +++ b/db/pre-re/skill_tree.txt @@ -3219,9 +3219,9 @@ 4068,2382,5,2422,1,0,0,0,0,0,0,0,0 //MI_ECHOSONG#Echo Song# 4068,2383,5,2422,1,0,0,0,0,0,0,0,0 //MI_HARMONIZE#Harmonize# 4068,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4068,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4068,2413,5,0,0,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4068,2414,5,317,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4068,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4068,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4068,2418,5,316,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4068,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4068,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# @@ -3275,9 +3275,9 @@ 4069,2351,5,2422,1,0,0,0,0,0,0,0,0 //WA_SYMPHONY_OF_LOVER#Symphony of Lovers# 4069,2352,5,2422,1,0,0,0,0,0,0,0,0 //WA_MOONLIT_SERENADE#Moonlit Serenade# 4069,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4069,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4069,2413,5,0,0,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4069,2414,5,325,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4069,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4069,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4069,2418,5,324,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4069,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4069,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# @@ -3649,9 +3649,9 @@ 4075,2382,5,2422,1,0,0,0,0,0,0,0,0 //MI_ECHOSONG#Echo Song# 4075,2383,5,2422,1,0,0,0,0,0,0,0,0 //MI_HARMONIZE#Harmonize# 4075,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4075,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4075,2413,5,0,0,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4075,2414,5,317,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4075,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4075,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4075,2418,5,316,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4075,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4075,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# @@ -3711,9 +3711,9 @@ 4076,2351,5,2422,1,0,0,0,0,0,0,0,0 //WA_SYMPHONY_OF_LOVER#Symphony of Lovers# 4076,2352,5,2422,1,0,0,0,0,0,0,0,0 //WA_MOONLIT_SERENADE#Moonlit Serenade# 4076,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4076,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4076,2413,5,0,0,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4076,2414,5,325,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4076,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4076,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4076,2418,5,324,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4076,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4076,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# @@ -4870,9 +4870,9 @@ 4104,2382,5,2422,1,0,0,0,0,0,0,0,0 //MI_ECHOSONG#Echo Song# 4104,2383,5,2422,1,0,0,0,0,0,0,0,0 //MI_HARMONIZE#Harmonize# 4104,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4104,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4104,2413,5,0,0,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4104,2414,5,317,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4104,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4104,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4104,2418,5,316,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4104,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4104,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# @@ -4927,9 +4927,9 @@ 4105,2351,5,2422,1,0,0,0,0,0,0,0,0 //WA_SYMPHONY_OF_LOVER#Symphony of Lovers# 4105,2352,5,2422,1,0,0,0,0,0,0,0,0 //WA_MOONLIT_SERENADE#Moonlit Serenade# 4105,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4105,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4105,2413,5,0,0,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4105,2414,5,325,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4105,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4105,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4105,2418,5,324,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4105,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4105,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# diff --git a/db/re/improvise_db.yml b/db/re/improvise_db.yml deleted file mode 100644 index 4961d20f8d..0000000000 --- a/db/re/improvise_db.yml +++ /dev/null @@ -1,65 +0,0 @@ -# This file is a part of rAthena. -# Copyright(C) 2019 rAthena Development Team -# https://rathena.org - https://github.com/rathena -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -########################################################################### -# Improvised Song Database -########################################################################### -# -# Improvised Song Settings -# -########################################################################### -# - Skill Skill to be casted by Improvised Song. -# Probability Probability of skill compared to others in database (1 = 0.01%, 10000 = 100%). -########################################################################### - -Header: - Type: IMPROVISED_SONG_DB - Version: 1 - -Body: - - Skill: MG_NAPALMBEAT - Probability: 6000 - - Skill: MG_SAFETYWALL - Probability: 4000 - - Skill: MG_SOULSTRIKE - Probability: 6000 - - Skill: MG_COLDBOLT - Probability: 6000 - - Skill: MG_FROSTDIVER - Probability: 6000 - - Skill: MG_FIREBALL - Probability: 6000 - - Skill: MG_FIREWALL - Probability: 4000 - - Skill: MG_FIREBOLT - Probability: 6000 - - Skill: MG_LIGHTNINGBOLT - Probability: 6000 - - Skill: MG_THUNDERSTORM - Probability: 4000 - - Skill: WZ_FIREPILLAR - Probability: 4000 - - Skill: WZ_METEOR - Probability: 4000 - - Skill: WZ_JUPITEL - Probability: 6000 - - Skill: WZ_VERMILION - Probability: 4000 - - Skill: WZ_WATERBALL - Probability: 6000 - - Skill: WZ_STORMGUST - Probability: 4000 diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index cad49b89ca..4c2a2fe18e 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -2106,8 +2106,6 @@ Body: TargetType: Attack DamageFlags: NoDamage: true - Flags: - IncreaseGloomyDayDamage: true Range: -2 Hit: Multi_Hit HitCount: -3 @@ -7077,8 +7075,6 @@ Body: MaxLevel: 5 Type: Weapon TargetType: Attack - Flags: - IncreaseGloomyDayDamage: true Range: - Level: 1 Size: 3 @@ -8555,7 +8551,7 @@ Body: HitCount: 1 SplashArea: 15 CastTime: 1000 - Duration1: 60000 + Duration1: 180000 Duration2: 60000 Cooldown: 20000 Requires: @@ -8609,7 +8605,7 @@ Body: HitCount: 1 SplashArea: 15 CastTime: 1000 - Duration1: 60000 + Duration1: 180000 Duration2: 60000 Cooldown: 20000 Requires: @@ -8695,7 +8691,7 @@ Body: HitCount: 1 SplashArea: 15 CastTime: 1000 - Duration1: 60000 + Duration1: 180000 Duration2: 60000 Cooldown: 20000 Requires: @@ -8717,7 +8713,7 @@ Body: HitCount: 1 SplashArea: 15 CastTime: 1000 - Duration1: 60000 + Duration1: 180000 Duration2: 60000 Cooldown: 20000 Requires: @@ -8835,7 +8831,7 @@ Body: HitCount: 1 SplashArea: 15 CastTime: 1000 - Duration1: 60000 + Duration1: 180000 Duration2: 20000 Cooldown: 20000 Requires: @@ -8877,7 +8873,7 @@ Body: HitCount: 1 SplashArea: 15 CastTime: 1000 - Duration1: 60000 + Duration1: 180000 Duration2: 20000 Cooldown: 20000 Requires: @@ -8919,7 +8915,7 @@ Body: HitCount: 1 SplashArea: 15 CastTime: 1000 - Duration1: 60000 + Duration1: 180000 Duration2: 20000 Cooldown: 20000 Requires: @@ -8961,7 +8957,7 @@ Body: HitCount: 1 SplashArea: 15 CastTime: 1000 - Duration1: 60000 + Duration1: 180000 Duration2: 20000 Cooldown: 20000 Requires: @@ -9087,7 +9083,7 @@ Body: HitCount: 1 SplashArea: 15 CastTime: 1000 - Duration1: 60000 + Duration1: 180000 Duration2: 20000 Cooldown: 20000 Requires: @@ -9171,7 +9167,7 @@ Body: HitCount: 1 SplashArea: 15 CastTime: 1000 - Duration1: 60000 + Duration1: 180000 Duration2: 20000 Cooldown: 20000 Requires: @@ -9213,7 +9209,7 @@ Body: HitCount: 1 SplashArea: 15 CastTime: 1000 - Duration1: 60000 + Duration1: 180000 Duration2: 20000 Cooldown: 20000 Requires: @@ -10976,7 +10972,6 @@ Body: TargetType: Attack Flags: TargetTrap: true - IncreaseGloomyDayDamage: true Range: 5 Hit: Multi_Hit HitCount: 5 @@ -13209,8 +13204,6 @@ Body: MaxLevel: 5 Type: Weapon TargetType: Attack - Flags: - IncreaseGloomyDayDamage: true Range: - Level: 1 Size: 7 @@ -17536,8 +17529,6 @@ Body: MaxLevel: 10 Type: Weapon TargetType: Attack - Flags: - IncreaseGloomyDayDamage: true Range: 5 Hit: Multi_Hit HitCount: -5 @@ -20668,9 +20659,9 @@ Body: IgnoreDefCard: true Flags: AllowOnWarg: true - IncreaseDanceWithWugDamage: true IgnoreAutoGuard: true IgnoreCicada: true + IncreaseDanceWithWugDamage: true Range: 9 Hit: Single HitCount: 1 @@ -20698,9 +20689,9 @@ Body: IgnoreDefCard: true Flags: AlterRangeVulture: true - IncreaseDanceWithWugDamage: true IgnoreAutoGuard: true IgnoreCicada: true + IncreaseDanceWithWugDamage: true Range: 9 Hit: Single HitCount: 1 @@ -23106,8 +23097,6 @@ Body: MaxLevel: 10 Type: Weapon TargetType: Attack - Flags: - IncreaseGloomyDayDamage: true Range: 1 Hit: Single HitCount: 5 @@ -24842,21 +24831,11 @@ Body: Splash: true Hit: Single HitCount: 1 - SplashArea: - - Level: 1 - Area: 7 - - Level: 2 - Area: 8 - - Level: 3 - Area: 9 - - Level: 4 - Area: 10 - - Level: 5 - Area: 11 + SplashArea: -1 CastCancel: true CastTime: 1000 AfterCastActDelay: 2000 - Duration1: 60000 + Duration1: 180000 FixedCastTime: -1 Requires: SpCost: @@ -24883,21 +24862,11 @@ Body: Splash: true Hit: Single HitCount: 1 - SplashArea: - - Level: 1 - Area: 7 - - Level: 2 - Area: 8 - - Level: 3 - Area: 9 - - Level: 4 - Area: 10 - - Level: 5 - Area: 11 + SplashArea: -1 CastCancel: true CastTime: 1000 AfterCastActDelay: 2000 - Duration1: 60000 + Duration1: 180000 FixedCastTime: -1 Requires: SpCost: @@ -24924,21 +24893,11 @@ Body: Splash: true Hit: Single HitCount: 1 - SplashArea: - - Level: 1 - Area: 7 - - Level: 2 - Area: 8 - - Level: 3 - Area: 9 - - Level: 4 - Area: 10 - - Level: 5 - Area: 11 + SplashArea: -1 CastCancel: true CastTime: 1000 AfterCastActDelay: 2000 - Duration1: 60000 + Duration1: 180000 FixedCastTime: -1 Requires: SpCost: @@ -24965,21 +24924,11 @@ Body: Splash: true Hit: Single HitCount: 1 - SplashArea: - - Level: 1 - Area: 7 - - Level: 2 - Area: 8 - - Level: 3 - Area: 9 - - Level: 4 - Area: 10 - - Level: 5 - Area: 11 + SplashArea: -1 CastCancel: true CastTime: 1000 AfterCastActDelay: 2000 - Duration1: 60000 + Duration1: 180000 FixedCastTime: -1 Requires: SpCost: @@ -25020,7 +24969,7 @@ Body: CastCancel: true CastTime: 1000 AfterCastActDelay: 2000 - Duration1: 60000 + Duration1: 180000 FixedCastTime: -1 Requires: SpCost: @@ -25088,29 +25037,7 @@ Body: Skill: Reproduce: true CastCancel: true - CastTime: # !TODO: Confirm all times - - Level: 1 - Time: 1000 - - Level: 2 - Time: 1500 - - Level: 3 - Time: 2000 - - Level: 4 - Time: 2500 - - Level: 5 - Time: 3000 - - Level: 6 - Time: 3500 - - Level: 7 - Time: 4000 - - Level: 8 - Time: 4500 - - Level: 9 - Time: 5000 - - Level: 10 - Time: 5500 - AfterCastActDelay: 1000 - Cooldown: + CastTime: # !TODO: Confirm cast times - Level: 1 Time: 1000 - Level: 2 @@ -25131,6 +25058,8 @@ Body: Time: 5000 - Level: 10 Time: 5500 + AfterCastActDelay: 500 + Cooldown: 2500 FixedCastTime: -1 Requires: SpCost: @@ -25158,44 +25087,33 @@ Body: Name: WM_REVERBERATION Description: Reverberation MaxLevel: 5 - TargetType: Ground + TargetType: Attack DamageFlags: - NoDamage: true Splash: true Flags: IsTrap: true Range: 9 Hit: Single HitCount: 1 - SplashArea: 2 + SplashArea: + - Level: 1 + Area: 2 + - Level: 2 + Area: 2 + - Level: 3 + Area: 2 + - Level: 4 + Area: 3 + - Level: 5 + Area: 3 CopyFlags: Skill: Reproduce: true CastCancel: true - CastTime: - - Level: 1 - Time: 1100 - - Level: 2 - Time: 1200 - - Level: 3 - Time: 1300 - - Level: 4 - Time: 1400 - - Level: 5 - Time: 1500 - AfterCastActDelay: 1000 - Duration1: - - Level: 1 - Time: 9000 - - Level: 2 - Time: 10000 - - Level: 3 - Time: 11000 - - Level: 4 - Time: 12000 - - Level: 5 - Time: 13000 - FixedCastTime: -1 + CastTime: 1000 + AfterCastActDelay: 500 + Cooldown: 150 + FixedCastTime: 500 Requires: SpCost: - Level: 1 @@ -25208,13 +25126,10 @@ Body: Amount: 42 - Level: 5 Amount: 48 - Unit: - Id: Reverberation - Interval: 1000 - Target: Enemy - Flag: - NoKnockback: true - - Id: 2415 + Ammo: + Arrow: true + AmmoAmount: 10 + - Id: 2415 # Removed on kRO Name: WM_REVERBERATION_MELEE Description: Reverberation Melee MaxLevel: 5 @@ -25222,7 +25137,6 @@ Body: TargetType: Attack DamageFlags: Splash: true - SplashSplit: true Hit: Single HitCount: 1 Element: Weapon @@ -25238,18 +25152,12 @@ Body: MaxLevel: 5 Type: Magic TargetType: Attack - DamageFlags: - Splash: true - SplashSplit: true - Hit: Single - HitCount: 1 - SplashArea: 2 + Hit: Multi_Hit + HitCount: 10 CopyFlags: Skill: Reproduce: true - Requires: - SpCost: 1 - - Id: 2417 + - Id: 2417 # Removed on kRO Name: WM_DOMINION_IMPULSE Description: Dominion Impulse MaxLevel: 1 @@ -25387,7 +25295,7 @@ Body: Whip: true ItemCost: - Item: Protect_Neck_Candy - Amount: 1 + Amount: 2 Unit: Id: Netherworld Range: 1 @@ -25505,7 +25413,7 @@ Body: Name: WM_LULLABY_DEEPSLEEP Description: Deep Sleep Lullaby MaxLevel: 5 - TargetType: Self + TargetType: Attack DamageFlags: NoDamage: true Splash: true @@ -25513,19 +25421,19 @@ Body: HitCount: 1 SplashArea: - Level: 1 - Area: 5 + Area: 1 - Level: 2 - Area: 6 + Area: 1 - Level: 3 - Area: 7 + Area: 1 - Level: 4 - Area: 8 + Area: 2 - Level: 5 - Area: 9 + Area: 2 CastCancel: true CastTime: 2000 AfterCastActDelay: 1000 - Duration1: + Duration1: # !TODO: What's the updated duration? - Level: 1 Time: 12000 - Level: 2 @@ -25553,6 +25461,9 @@ Body: Weapon: Musical: true Whip: true + ItemCost: + - Item: Protect_Neck_Candy + Amount: 2 - Id: 2423 Name: WM_SIRCLEOFNATURE Description: Circle of Nature's Sound @@ -25563,21 +25474,11 @@ Body: Splash: true Hit: Single HitCount: 1 - SplashArea: - - Level: 1 - Area: 3 - - Level: 2 - Area: 4 - - Level: 3 - Area: 5 - - Level: 4 - Area: 6 - - Level: 5 - Area: 7 + SplashArea: -1 CastCancel: true CastTime: 2000 AfterCastActDelay: 1000 - Duration1: 60000 + Duration1: 180000 Cooldown: 15000 FixedCastTime: -1 Requires: @@ -25600,7 +25501,7 @@ Body: Description: Improvised Song MaxLevel: 5 Type: Magic - TargetType: Self + TargetType: Attack DamageFlags: NoDamage: true Range: 9 @@ -25638,17 +25539,7 @@ Body: CastCancel: true CastTime: 1000 AfterCastActDelay: 1000 - Duration1: - - Level: 1 - Time: 30000 - - Level: 2 - Time: 45000 - - Level: 3 - Time: 60000 - - Level: 4 - Time: 75000 - - Level: 5 - Time: 90000 + Duration1: 60000 Cooldown: 10000 FixedCastTime: 500 Requires: @@ -25719,7 +25610,7 @@ Body: Amount: 120 ItemCost: - Item: Protect_Neck_Candy - Amount: 1 + Amount: 2 - Id: 2427 Name: WM_SONG_OF_MANA Description: Song of Mana @@ -25746,17 +25637,7 @@ Body: CastCancel: true CastTime: 1000 AfterCastActDelay: 1000 - Duration1: - - Level: 1 - Time: 30000 - - Level: 2 - Time: 60000 - - Level: 3 - Time: 90000 - - Level: 4 - Time: 120000 - - Level: 5 - Time: 150000 + Duration1: 120000 Cooldown: 90000 FixedCastTime: 500 Requires: @@ -25774,6 +25655,9 @@ Body: Weapon: Musical: true Whip: true + ItemCost: + - Item: Protect_Neck_Candy + Amount: 1 - Id: 2428 Name: WM_DANCE_WITH_WUG Description: Dance With A Warg @@ -25810,17 +25694,7 @@ Body: - Level: 5 Time: 3500 AfterCastActDelay: 1000 - Duration1: - - Level: 1 - Time: 30000 - - Level: 2 - Time: 60000 - - Level: 3 - Time: 90000 - - Level: 4 - Time: 120000 - - Level: 5 - Time: 150000 + Duration1: 120000 Cooldown: 90000 FixedCastTime: 500 Requires: @@ -25838,30 +25712,32 @@ Body: Weapon: Musical: true Whip: true + ItemCost: + - Item: Protect_Neck_Candy + Amount: 1 - Id: 2429 Name: WM_SOUND_OF_DESTRUCTION Description: Sound of Destruction MaxLevel: 5 - TargetType: Ground + TargetType: Self DamageFlags: + NoDamage: true Splash: true - IgnoreFlee: true Flags: IsChorus: true - Range: 9 Hit: Single HitCount: 1 SplashArea: - Level: 1 - Area: 4 + Area: 5 - Level: 2 - Area: 4 + Area: 5 - Level: 3 - Area: 5 - - Level: 4 - Area: 5 - - Level: 5 Area: 6 + - Level: 4 + Area: 6 + - Level: 5 + Area: 7 CastCancel: true CastTime: - Level: 2 @@ -25873,18 +25749,8 @@ Body: - Level: 5 Time: 2000 AfterCastActDelay: 1000 - Duration1: 5000 - Cooldown: - - Level: 1 - Time: 6000 - - Level: 2 - Time: 7000 - - Level: 3 - Time: 8000 - - Level: 4 - Time: 9000 - - Level: 5 - Time: 10000 + Duration1: 10000 + Cooldown: 60000 FixedCastTime: 500 Requires: SpCost: @@ -25901,6 +25767,9 @@ Body: Weapon: Musical: true Whip: true + ItemCost: + - Item: Protect_Neck_Candy + Amount: 10 - Id: 2430 Name: WM_SATURDAY_NIGHT_FEVER Description: Saturday Night Fever @@ -25959,7 +25828,7 @@ Body: Time: 4000 - Level: 5 Time: 2000 - Cooldown: 180000 + Cooldown: 60000 FixedCastTime: 1000 Requires: SpCost: @@ -25976,6 +25845,9 @@ Body: Weapon: Musical: true Whip: true + ItemCost: + - Item: Protect_Neck_Candy + Amount: 5 - Id: 2431 Name: WM_LERADS_DEW Description: Lerad's Dew @@ -26002,17 +25874,7 @@ Body: CastCancel: true CastTime: 1000 AfterCastActDelay: 1000 - Duration1: - - Level: 1 - Time: 20000 - - Level: 2 - Time: 30000 - - Level: 3 - Time: 40000 - - Level: 4 - Time: 50000 - - Level: 5 - Time: 60000 + Duration1: 120000 Cooldown: 180000 FixedCastTime: 500 Requires: @@ -26030,6 +25892,9 @@ Body: Weapon: Musical: true Whip: true + ItemCost: + - Item: Protect_Neck_Candy + Amount: 1 - Id: 2432 Name: WM_MELODYOFSINK Description: Melody of Sink @@ -26067,7 +25932,7 @@ Body: Time: 50000 - Level: 5 Time: 60000 - Cooldown: 180000 + Cooldown: 20000 FixedCastTime: 500 Requires: SpCost: @@ -26084,6 +25949,9 @@ Body: Weapon: Musical: true Whip: true + ItemCost: + - Item: Protect_Neck_Candy + Amount: 2 - Id: 2433 Name: WM_BEYOND_OF_WARCRY Description: Warcry of Beyond @@ -26121,7 +25989,7 @@ Body: Time: 50000 - Level: 5 Time: 60000 - Cooldown: 180000 + Cooldown: 20000 FixedCastTime: 500 Requires: SpCost: @@ -26138,6 +26006,9 @@ Body: Weapon: Musical: true Whip: true + ItemCost: + - Item: Protect_Neck_Candy + Amount: 2 - Id: 2434 Name: WM_UNLIMITED_HUMMING_VOICE Description: Unlimited Humming Voice @@ -26202,6 +26073,9 @@ Body: Weapon: Musical: true Whip: true + ItemCost: + - Item: Protect_Neck_Candy + Amount: 5 - Id: 2516 Name: WM_SEVERE_RAINSTORM_MELEE Description: Severe Rainstorm Melee @@ -35983,8 +35857,6 @@ Body: TargetType: Attack DamageFlags: NoDamage: true - Flags: - IncreaseGloomyDayDamage: true Range: -2 Hit: Single HitCount: 1 @@ -36009,7 +35881,6 @@ Body: TargetType: Attack Flags: TargetTrap: true - IncreaseGloomyDayDamage: true Range: 5 Hit: Multi_Hit HitCount: 5 diff --git a/db/re/skill_nocast_db.txt b/db/re/skill_nocast_db.txt index 2b84a15287..85d621d0fd 100644 --- a/db/re/skill_nocast_db.txt +++ b/db/re/skill_nocast_db.txt @@ -33,7 +33,11 @@ 328,1 //DC_DONTFORGETME 2422,1 //WM_LULLABY_DEEPSLEEP 2423,1 //WM_SIRCLEOFNATURE +2425,1 //WM_GLOOMYDAY +2429,1 //WM_SOUND_OF_DESTRUCTION 2430,1 //WM_SATURDAY_NIGHT_FEVER +2432,1 //WM_MELODYOFSINK +2433,1 //WM_BEYOND_OF_WARCRY 2455,1 //SO_ARRULLO 2299,1 //SC_MANHOLE diff --git a/db/re/skill_tree.txt b/db/re/skill_tree.txt index 9a8b1fc302..0b1c054d44 100644 --- a/db/re/skill_tree.txt +++ b/db/re/skill_tree.txt @@ -3250,9 +3250,9 @@ 4068,2382,5,2422,1,0,0,0,0,0,0,0,0 //MI_ECHOSONG#Echo Song# 4068,2383,5,2422,1,0,0,0,0,0,0,0,0 //MI_HARMONIZE#Harmonize# 4068,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4068,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4068,2413,10,2414,5,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4068,2414,5,317,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4068,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4068,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4068,2418,5,316,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4068,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4068,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# @@ -3306,9 +3306,9 @@ 4069,2351,5,2422,1,0,0,0,0,0,0,0,0 //WA_SYMPHONY_OF_LOVER#Symphony of Lovers# 4069,2352,5,2422,1,0,0,0,0,0,0,0,0 //WA_MOONLIT_SERENADE#Moonlit Serenade# 4069,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4069,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4069,2413,10,2414,5,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4069,2414,5,325,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4069,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4069,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4069,2418,5,324,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4069,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4069,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# @@ -3681,9 +3681,9 @@ 4075,2382,5,2422,1,0,0,0,0,0,0,0,0 //MI_ECHOSONG#Echo Song# 4075,2383,5,2422,1,0,0,0,0,0,0,0,0 //MI_HARMONIZE#Harmonize# 4075,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4075,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4075,2413,10,2414,5,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4075,2414,5,317,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4075,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4075,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4075,2418,5,316,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4075,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4075,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# @@ -3744,9 +3744,9 @@ 4076,2351,5,2422,1,0,0,0,0,0,0,0,0 //WA_SYMPHONY_OF_LOVER#Symphony of Lovers# 4076,2352,5,2422,1,0,0,0,0,0,0,0,0 //WA_MOONLIT_SERENADE#Moonlit Serenade# 4076,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4076,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4076,2413,10,2414,5,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4076,2414,5,325,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4076,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4076,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4076,2418,5,324,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4076,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4076,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# @@ -4913,9 +4913,9 @@ 4104,2382,5,2422,1,0,0,0,0,0,0,0,0 //MI_ECHOSONG#Echo Song# 4104,2383,5,2422,1,0,0,0,0,0,0,0,0 //MI_HARMONIZE#Harmonize# 4104,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4104,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4104,2413,10,2414,5,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4104,2414,5,317,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4104,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4104,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4104,2418,5,316,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4104,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4104,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# @@ -4971,9 +4971,9 @@ 4105,2351,5,2422,1,0,0,0,0,0,0,0,0 //WA_SYMPHONY_OF_LOVER#Symphony of Lovers# 4105,2352,5,2422,1,0,0,0,0,0,0,0,0 //WA_MOONLIT_SERENADE#Moonlit Serenade# 4105,2412,10,0,0,0,0,0,0,0,0,0,0 //WM_LESSON#Lesson# -4105,2413,10,2417,1,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# +4105,2413,10,2414,5,0,0,0,0,0,0,0,0 //WM_METALICSOUND#Metallic Sound# 4105,2414,5,325,5,0,0,0,0,0,0,0,0 //WM_REVERBERATION#Reverberation# -4105,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# +//4105,2417,1,2414,1,0,0,0,0,0,0,0,0 //WM_DOMINION_IMPULSE#Dominion Impulse# 4105,2418,5,324,5,0,0,0,0,0,0,0,0 //WM_SEVERE_RAINSTORM#Severe Rainstorm# 4105,2419,5,2412,1,0,0,0,0,0,0,0,0 //WM_POEMOFNETHERWORLD#Poem Of The Netherworld# 4105,2420,5,2419,3,0,0,0,0,0,0,0,0 //WM_VOICEOFSIREN#Voice Of Siren# diff --git a/doc/skill_db.txt b/doc/skill_db.txt index 05a36d1904..7f2582e560 100644 --- a/doc/skill_db.txt +++ b/doc/skill_db.txt @@ -97,7 +97,6 @@ AllowOnWarg - Usable while riding Warg. AllowOnMado - Usable while on Madogear. TargetManHole - Target enemy with SC__MANHOLE. TargetHidden - Target enemy with OPTION_HIDE. -IncreaseGloomyDayDamage - Increase SC_GLOOMYDAY_SK damage. IncreaseDanceWithWugDamage - Increase SC_DANCEWITHWUG damage. IgnoreWugBite - Ignore RA_WUGBITE. IgnoreAutoGuard - Not blocked by SC_AUTOGUARD (When TargetType is Weapon only). diff --git a/doc/yaml/db/improvise_db.yml b/doc/yaml/db/improvise_db.yml deleted file mode 100644 index aa84b08bd3..0000000000 --- a/doc/yaml/db/improvise_db.yml +++ /dev/null @@ -1,10 +0,0 @@ -########################################################################### -# Improvised Song Database -########################################################################### -# -# Improvised Song Settings -# -########################################################################### -# - Skill Skill to be casted by Improvised Song. -# Probability Probability of skill compared to others in database (1 = 0.01%, 10000 = 100%). -########################################################################### diff --git a/sql-files/upgrades/upgrade_2020xxxx.sql b/sql-files/upgrades/upgrade_2020xxxx.sql new file mode 100644 index 0000000000..cdc536564e --- /dev/null +++ b/sql-files/upgrades/upgrade_2020xxxx.sql @@ -0,0 +1,3 @@ +-- WM_DOMINION_IMPULSE +UPDATE `char` c, `skill` s SET `c`.skill_point = `c`.skill_point + `s`.lv WHERE `s`.id = 2417 AND `c`.char_id = `s`.char_id; +DELETE FROM `skill` WHERE `id` = 2417; diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 205ae04655..4691e291bc 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -1340,6 +1340,9 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam status_change_end(bl,SC_VOICEOFSIREN,INVALID_TIMER); } + if (sc->data[SC_SOUNDOFDESTRUCTION]) + damage <<= 1; + // Damage reductions // Assumptio increases DEF on RE mode, otherwise gives a reduction on the final damage. [Igniz] #ifndef RENEWAL @@ -1624,6 +1627,10 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam damage += damage * sce->val1 / 100; if (flag & BF_MAGIC && (sce = sc->data[SC_ADD_MATK_DAMAGE])) damage += damage * sce->val1 / 100; + if (sc->data[SC_DANCEWITHWUG] && (flag&(BF_LONG|BF_WEAPON)) == (BF_LONG|BF_WEAPON)) + damage += damage * sc->data[SC_DANCEWITHWUG]->val1 / 100; + if (sc->data[SC_UNLIMITEDHUMMINGVOICE] && flag&BF_MAGIC) + damage += damage * sc->data[SC_UNLIMITEDHUMMINGVOICE]->val3 / 100; } //End of caster SC_ check if (tsc && tsc->count) { @@ -2393,7 +2400,7 @@ bool is_infinite_defense(struct block_list *target, int flag) if(target->type == BL_SKILL) { TBL_SKILL *su = ((TBL_SKILL*)target); - if (su && su->group && (su->group->skill_id == WM_REVERBERATION || su->group->skill_id == NPC_REVERBERATION || su->group->skill_id == WM_POEMOFNETHERWORLD)) + if (su && su->group && (su->group->skill_id == NPC_REVERBERATION || su->group->skill_id == WM_POEMOFNETHERWORLD)) return true; } @@ -4411,11 +4418,6 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list * skillratio += skillratio * 25 / 100; RE_LVL_DMOD(100); break; - case WM_REVERBERATION_MELEE: - // ATK [{(Skill Level x 100) + 300} x Caster Base Level / 100] - skillratio += 200 + 100 * ((sd) ? pc_checkskill(sd, WM_REVERBERATION) : 1); - RE_LVL_DMOD(100); - break; case WM_SEVERE_RAINSTORM_MELEE: //ATK [{(Caster DEX + AGI) x (Skill Level / 5)} x Caster Base Level / 100] % skillratio = (status_get_dex(src) + status_get_agi(src)) * skill_lv / 5; @@ -4424,13 +4426,11 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list * RE_LVL_DMOD(100); break; case WM_GREAT_ECHO: - skillratio += 300 + 200 * skill_lv; + skillratio += -100 + 250 + 500 * skill_lv; if (sd) { - int chorusbonus = battle_calc_chorusbonus(sd); - - // Chorus bonus don't count the first 2 Minstrels/Wanderers and only increases when their are 3 or more. [Rytech] - if (chorusbonus >= 1 && chorusbonus <= 5) - skillratio += 100<<(chorusbonus-1); // 1->100; 2->200; 3->400; 4->800; 5->1600 + skillratio += pc_checkskill(sd, WM_LESSON) * 50; // !TODO: Confirm bonus + if (skill_check_pc_partner(sd, skill_id, &skill_lv, AREA_SIZE, 0) > 0) + skillratio <<= 1; } RE_LVL_DMOD(100); break; @@ -4843,10 +4843,6 @@ static void battle_attack_sc_bonus(struct Damage* wd, struct block_list *src, st #endif } } - if (sc->data[SC_GLOOMYDAY_SK] && skill_get_inf2(skill_id, INF2_INCREASEGLOOMYDAYDAMAGE)) { - ATK_ADDRATE(wd->damage, wd->damage2, sc->data[SC_GLOOMYDAY_SK]->val2); - RE_ALLATK_ADDRATE(wd, sc->data[SC_GLOOMYDAY_SK]->val2); - } if (sc->data[SC_DANCEWITHWUG]) { if (skill_get_inf2(skill_id, INF2_INCREASEDANCEWITHWUGDAMAGE)) { ATK_ADDRATE(wd->damage, wd->damage2, sc->data[SC_DANCEWITHWUG]->val1 * 10 * battle_calc_chorusbonus(sd)); @@ -6034,6 +6030,10 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list if (mflag&ELE_DARK) s_ele = ELE_DARK; break; + case WM_REVERBERATION_MAGIC: + if (sd) + s_ele = sd->bonus.arrow_ele; + break; case SO_PSYCHIC_WAVE: if( sc && sc->count ) { if( sc->data[SC_HEATER_OPTION] ) @@ -6449,11 +6449,13 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list break; case WM_METALICSOUND: skillratio += -100 + 120 * skill_lv + 60 * ((sd) ? pc_checkskill(sd, WM_LESSON) : 1); + if (tsc && tsc->data[SC_SLEEP]) + skillratio += 100; // !TODO: Confirm target sleeping bonus RE_LVL_DMOD(100); break; case WM_REVERBERATION_MAGIC: - // MATK [{(Skill Level x 100) + 100} x Casters Base Level / 100] % - skillratio += 100 * skill_lv; + // MATK [{(Skill Level x 300) + 400} x Casters Base Level / 100] % + skillratio += -100 + 700 + 300 * skill_lv; RE_LVL_DMOD(100); break; case SO_FIREWALK: @@ -7009,10 +7011,6 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list * case NC_MAGMA_ERUPTION_DOTDAMAGE: // 'Eruption' damage md.damage = 800 + 200 * skill_lv; break; - case WM_SOUND_OF_DESTRUCTION: - md.damage = 1000 * skill_lv + sstatus->int_ * ((sd) ? pc_checkskill(sd,WM_LESSON) : 1); - md.damage += md.damage * 10 * ((sd) ? battle_calc_chorusbonus(sd) / 100 : 0); - break; case GN_THORNS_TRAP: md.damage = 100 + 200 * skill_lv + status_get_int(src); break; @@ -8099,7 +8097,7 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f if( !su || !su->group) return 0; if( skill_get_inf2(su->group->skill_id, INF2_ISTRAP) && su->group->unit_id != UNT_USED_TRAPS) { - if (!skill_id || su->group->skill_id == WM_REVERBERATION || su->group->skill_id == NPC_REVERBERATION || su->group->skill_id == WM_POEMOFNETHERWORLD) { + if (!skill_id || su->group->skill_id == NPC_REVERBERATION || su->group->skill_id == WM_POEMOFNETHERWORLD) { ; } else if (skill_get_inf2(skill_id, INF2_TARGETTRAP)) { // Only a few skills can target traps diff --git a/src/map/map-server.vcxproj b/src/map/map-server.vcxproj index b4955a18a7..60c680616e 100644 --- a/src/map/map-server.vcxproj +++ b/src/map/map-server.vcxproj @@ -307,7 +307,6 @@ - diff --git a/src/map/script_constants.hpp b/src/map/script_constants.hpp index cae9051e67..77092b5f6a 100644 --- a/src/map/script_constants.hpp +++ b/src/map/script_constants.hpp @@ -1582,6 +1582,7 @@ export_constant(SC_REF_T_POTION); export_constant(SC_ADD_ATK_DAMAGE); export_constant(SC_ADD_MATK_DAMAGE); + export_constant(SC_SOUNDOFDESTRUCTION); #ifdef RENEWAL export_constant(SC_EXTREMITYFIST2); #endif @@ -7518,7 +7519,6 @@ export_constant(INF2_ALLOWONMADO); export_constant(INF2_TARGETMANHOLE); export_constant(INF2_TARGETHIDDEN); - export_constant(INF2_INCREASEGLOOMYDAYDAMAGE); export_constant(INF2_INCREASEDANCEWITHWUGDAMAGE); export_constant(INF2_IGNOREWUGBITE); export_constant(INF2_IGNOREAUTOGUARD); diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 549c238fff..4e0ff08903 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -89,8 +89,6 @@ static unsigned short skill_arrow_count; AbraDatabase abra_db; -ImprovisedSongDatabase improvised_song_db; - ReadingSpellbookDatabase reading_spellbook_db; #define MAX_SKILL_CHANGEMATERIAL_DB 75 @@ -932,6 +930,7 @@ bool skill_isNotOk(uint16 skill_id, struct map_session_data *sd) case WM_SIRCLEOFNATURE: case WM_SOUND_OF_DESTRUCTION: case WM_LULLABY_DEEPSLEEP: + case WM_GLOOMYDAY: case WM_SATURDAY_NIGHT_FEVER: if( !mapdata_flag_vs(mapdata) ) { clif_skill_teleportmessage(sd,2); // This skill uses this msg instead of skill fails. @@ -1832,47 +1831,6 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 case SR_HOWLINGOFLION: sc_start(src,bl, SC_FEAR, 5 + 5 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv)); break; - case WM_SOUND_OF_DESTRUCTION: - if( tsc && ( tsc->data[SC_SWINGDANCE] || tsc->data[SC_SYMPHONYOFLOVER] || tsc->data[SC_MOONLITSERENADE] || - tsc->data[SC_RUSHWINDMILL] || tsc->data[SC_ECHOSONG] || tsc->data[SC_HARMONIZE] || - tsc->data[SC_VOICEOFSIREN] || tsc->data[SC_DEEPSLEEP] || tsc->data[SC_SIRCLEOFNATURE] || - tsc->data[SC_GLOOMYDAY] || tsc->data[SC_GLOOMYDAY_SK] || tsc->data[SC_SONGOFMANA] || - tsc->data[SC_DANCEWITHWUG] || tsc->data[SC_SATURDAYNIGHTFEVER] || tsc->data[SC_LERADSDEW] || - tsc->data[SC_MELODYOFSINK] || tsc->data[SC_BEYONDOFWARCRY] || tsc->data[SC_UNLIMITEDHUMMINGVOICE] ) && - rnd()%100 < 4 * skill_lv + 2 * ((sd) ? pc_checkskill(sd, WM_LESSON) : skill_get_max(WM_LESSON)) + 10 * battle_calc_chorusbonus(sd)) { - status_change_start(src, bl, SC_STUN, 10000, skill_lv, 0, 0, 0, skill_get_time(skill_id,skill_lv), SCSTART_NOTICKDEF); - status_change_end(bl, SC_DANCING, INVALID_TIMER); - status_change_end(bl, SC_RICHMANKIM, INVALID_TIMER); - status_change_end(bl, SC_ETERNALCHAOS, INVALID_TIMER); - status_change_end(bl, SC_DRUMBATTLE, INVALID_TIMER); - status_change_end(bl, SC_NIBELUNGEN, INVALID_TIMER); - status_change_end(bl, SC_INTOABYSS, INVALID_TIMER); - status_change_end(bl, SC_SIEGFRIED, INVALID_TIMER); - status_change_end(bl, SC_WHISTLE, INVALID_TIMER); - status_change_end(bl, SC_ASSNCROS, INVALID_TIMER); - status_change_end(bl, SC_POEMBRAGI, INVALID_TIMER); - status_change_end(bl, SC_APPLEIDUN, INVALID_TIMER); - status_change_end(bl, SC_HUMMING, INVALID_TIMER); - status_change_end(bl, SC_FORTUNE, INVALID_TIMER); - status_change_end(bl, SC_SERVICE4U, INVALID_TIMER); -#ifndef RENEWAL - status_change_end(bl, SC_LONGING, INVALID_TIMER); -#endif - status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER); - status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER); - status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER); - status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER); - status_change_end(bl, SC_ECHOSONG, INVALID_TIMER); - status_change_end(bl, SC_HARMONIZE, INVALID_TIMER); - status_change_end(bl, SC_WINKCHARM, INVALID_TIMER); - status_change_end(bl, SC_SONGOFMANA, INVALID_TIMER); - status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER); - status_change_end(bl, SC_LERADSDEW, INVALID_TIMER); - status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER); - status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER); - status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER); - } - break; case SO_EARTHGRAVE: sc_start2(src,bl, SC_BLEEDING, 5 * skill_lv, skill_lv, src->id, skill_get_time2(skill_id, skill_lv)); // Need official rate. [LimitLine] break; @@ -3681,6 +3639,8 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * type = DMG_SPLASH; if (!(flag&SD_ANIMATION)) clif_skill_nodamage(dsrc, bl, skill_id, skill_lv, 1); + // Fall through + case WM_REVERBERATION_MAGIC: dmg.dmotion = clif_skill_damage(dsrc, bl, tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -2, dmg_type); break; case SJ_FALLINGSTAR_ATK: @@ -3962,7 +3922,6 @@ static int skill_check_unit_range_sub(struct block_list *bl, va_list ap) case SC_DIMENSIONDOOR: case SC_BLOODYLUST: case NPC_REVERBERATION: - case WM_REVERBERATION: case GN_THORNS_TRAP: case RL_B_TRAP: case SC_ESCAPE: @@ -4382,8 +4341,6 @@ static TIMER_FUNC(skill_timerskill){ } break; case NPC_REVERBERATION_ATK: - case WM_REVERBERATION_MELEE: - case WM_REVERBERATION_MAGIC: skill_castend_damage_id(src,target,skl->skill_id,skl->skill_lv,tick,skl->flag|SD_LEVEL|SD_ANIMATION); break; case LG_MOONSLASHER: @@ -4550,7 +4507,7 @@ static int skill_active_reverberation(struct block_list *bl, va_list ap) { if (bl->type != BL_SKILL) return 0; - if (su->alive && (sg = su->group) && (sg->skill_id == WM_REVERBERATION || sg->skill_id == NPC_REVERBERATION)) { + if (su->alive && (sg = su->group) && sg->skill_id == NPC_REVERBERATION) { map_foreachinallrange(skill_trap_splash, bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, bl, gettick()); su->limit = DIFF_TICK(gettick(), sg->tick); sg->unit_id = UNT_USED_TRAPS; @@ -5173,8 +5130,6 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint case SR_SKYNETBLOW: case SR_WINDMILL: case SR_RIDEINLIGHTNING: - case WM_REVERBERATION_MELEE: - case WM_REVERBERATION_MAGIC: case SO_VARETYR_SPEAR: case GN_CART_TORNADO: case GN_CARTCANNON: @@ -5261,8 +5216,6 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint skill_addtimerskill(src,tick+250,src->id,0,0,skill_id,skill_lv,2,flag|BCT_ENEMY|SD_SPLASH|1); break; case NPC_REVERBERATION_ATK: - case WM_REVERBERATION_MELEE: - case WM_REVERBERATION_MAGIC: case NC_ARMSCANNON: skill_area_temp[1] = 0; starget = splash_target(src); @@ -5688,7 +5641,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint break; case RK_STORMBLAST: if( flag&1 ) - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); + skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); else { clif_skill_nodamage(src,bl,skill_id,skill_lv,1); map_foreachinallrange(skill_area_sub, bl,skill_get_splash(skill_id, skill_lv),BL_CHAR,src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,skill_castend_nodamage_id); @@ -6075,6 +6028,16 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint } break; + case WM_REVERBERATION: + if (flag & 1) + skill_attack(skill_get_type(WM_REVERBERATION_MAGIC), src, src, bl, WM_REVERBERATION_MAGIC, skill_lv, tick, flag); + else { + clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); + map_foreachinallrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR|BL_SKILL, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); + battle_consume_ammo(sd, skill_id, skill_lv); // Consume here since Magic/Misc attacks reset arrow_atk + } + break; + case SO_POISON_BUSTER: if( tsc && tsc->data[SC_POISON] ) { skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); @@ -10713,25 +10676,15 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui if( !status_isdead(bl) ) break; - if( rnd()%100 < 88 + 2 * skill_lv ) { - int heal = tstatus->sp; - if( heal <= 0 ) - heal = 1; - tstatus->hp = heal; - tstatus->sp -= tstatus->sp * ( 60 - 10 * skill_lv ) / 100; - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - pc_revive((TBL_PC*)bl,heal,0); - clif_resurrection(bl,1); - } - } - break; + int heal = tstatus->sp; - case WM_SIRCLEOFNATURE: - if( flag&1 ) - sc_start(src,bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - else { - map_foreachinallrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_PC,src,skill_id,skill_lv,tick,flag|BCT_ALL|1,skill_castend_nodamage_id); + if( heal <= 0 ) + heal = 1; + tstatus->hp = heal; + tstatus->sp -= tstatus->sp * ( 60 - 10 * skill_lv ) / 100; clif_skill_nodamage(src,bl,skill_id,skill_lv,1); + pc_revive((TBL_PC*)bl,heal,0); + clif_resurrection(bl,1); } break; @@ -10752,7 +10705,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui if( dstsd && ( pc_checkskill(dstsd,KN_BRANDISHSPEAR) || pc_checkskill(dstsd,LK_SPIRALPIERCE) || pc_checkskill(dstsd,CR_SHIELDCHARGE) || pc_checkskill(dstsd,CR_SHIELDBOOMERANG) || pc_checkskill(dstsd,PA_SHIELDCHAIN) || pc_checkskill(dstsd,LG_SHIELDPRESS) ) ) - { + { // !TODO: Which skills aren't boosted anymore? sc_start(src,bl,SC_GLOOMYDAY_SK,100,skill_lv,skill_get_time(skill_id,skill_lv)); break; } @@ -10760,31 +10713,17 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui break; case WM_SATURDAY_NIGHT_FEVER: - if( flag&1 ) { // Affect to all targets arround the caster and caster too. - if (tsc && ((tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK)) || tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_STEALTHFIELD] || tsc->data[SC__SHADOWFORM])) - break; - sc_start(src,bl, type, 100, skill_lv,skill_get_time(skill_id, skill_lv)); - } else if( flag&2 ) { - if( src->id != bl->id && battle_check_target(src,bl,BCT_ENEMY) > 0 ) - status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,DMG_NORMAL,0,false),skill_id); - } else if( sd ) { - short chance = sstatus->int_/6 + sd->status.job_level/5 + skill_lv*4; - if( !sd->status.party_id || (rnd()%100 > chance)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_HELPER,0); - break; + if( flag&1 ) { + sc_start(src,bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); + } else if (sd) { + if( rnd()%100 < sstatus->int_ / 6 + sd->status.job_level / 5 + skill_lv * 4 + pc_checkskill(sd, WM_LESSON) ) { // !TODO: What's the Lesson bonus? + map_foreachinallrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); + clif_skill_nodamage(src,bl,skill_id,skill_lv,1); } - if( map_foreachinallrange(skill_area_sub, bl, skill_get_splash(skill_id,skill_lv), - BL_PC, src, skill_id, skill_lv, tick, BCT_ENEMY, skill_area_sub_count) > 7 ) - flag |= 2; - else - flag |= 1; - map_foreachinallrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|BCT_SELF, skill_castend_nodamage_id); - clif_skill_nodamage(src, bl, skill_id, skill_lv, - sc_start(src,src,SC_STOP,100,skill_lv,skill_get_time2(skill_id,skill_lv))); - if( flag&2 ) // Dealed here to prevent conflicts - status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,DMG_NORMAL,0,false),skill_id); } break; + + case WM_SIRCLEOFNATURE: case WM_SONG_OF_MANA: case WM_DANCE_WITH_WUG: case WM_LERADS_DEW: @@ -10792,87 +10731,58 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui if( flag&1 ) { // These affect to to all party members near the caster. struct status_change *sc = status_get_sc(src); if( sc && sc->data[type] ) { - sc_start2(src,bl,type,100,skill_lv,battle_calc_chorusbonus(sd),skill_get_time(skill_id,skill_lv)); + sc_start2(src,bl,type,100,skill_lv,pc_checkskill(sd, WM_LESSON),skill_get_time(skill_id,skill_lv)); } } else if( sd ) { - if( sc_start2(src,bl,type,100,skill_lv,battle_calc_chorusbonus(sd),skill_get_time(skill_id,skill_lv)) ) + if( sc_start2(src,bl,type,100,skill_lv,pc_checkskill(sd, WM_LESSON),skill_get_time(skill_id,skill_lv)) ) party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id); clif_skill_nodamage(src,bl,skill_id,skill_lv,1); } break; case WM_MELODYOFSINK: - case WM_BEYOND_OF_WARCRY: if( flag&1 ) { - sc_start2(src,bl,type,100,skill_lv,battle_calc_chorusbonus(sd),skill_get_time(skill_id,skill_lv)); - } else { // These affect to all targets arround the caster. - if( rnd()%100 < 15 + 5 * skill_lv * 5 * battle_calc_chorusbonus(sd) ) { + sc_start(src,bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); + } else { // These affect to all targets around the caster. + if( rnd()%100 < 5 + 5 * skill_lv + pc_checkskill(sd, WM_LESSON) ) { // !TODO: What's the Lesson bonus? map_foreachinallrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); clif_skill_nodamage(src,bl,skill_id,skill_lv,1); } } break; - case WM_RANDOMIZESPELL: - if (improvised_song_db.empty()) { - clif_skill_nodamage (src, bl, skill_id, skill_lv, 1); - break; + case WM_BEYOND_OF_WARCRY: + if( flag&1 ) { + sc_start(src,bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); + } else { // These affect to all targets around the caster. + if( rnd()%100 < 12 + 3 * skill_lv + (sd ? pc_checkskill(sd, WM_LESSON) : 0) ) { // !TODO: What's the Lesson bonus? + map_foreachinallrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); + clif_skill_nodamage(src,bl,skill_id,skill_lv,1); + } } - else { - int improv_skill_id = 0, improv_skill_lv, checked = 0, checked_max = improvised_song_db.size() * 3; + break; - do { - auto improvise_spell = improvised_song_db.random(); + case WM_SOUND_OF_DESTRUCTION: + if (flag&1) { + sc_start(src, bl, type, 100, skill_lv, (sd ? pc_checkskill(sd, WM_LESSON) * 500 : 0) + skill_get_time(skill_id, skill_lv)); // !TODO: Confirm Lesson increase + } else { + map_foreachinallrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); + clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); + } + break; - improv_skill_id = improvise_spell->skill_id; + case WM_RANDOMIZESPELL: + if (rnd() % 100 < 30 + (10 * skill_lv)) { + status_change_end(bl, SC_SONGOFMANA, INVALID_TIMER); + status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER); + status_change_end(bl, SC_LERADSDEW, INVALID_TIMER); + status_change_end(bl, SC_SATURDAYNIGHTFEVER, INVALID_TIMER); + status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER); + status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER); + status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER); + status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER); - if( rnd() % 10000 >= improvise_spell->per ){ - break; - } - } while (checked++ < checked_max); - - if (!skill_get_index(improv_skill_id)) { - if (sd) - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - break; - } - - improv_skill_lv = min(4 + skill_lv, skill_get_max(improv_skill_id)); - clif_skill_nodamage (src, bl, skill_id, skill_lv, 1); - - if( sd ) { - sd->state.abra_flag = 2; - sd->skillitem = improv_skill_id; - sd->skillitemlv = improv_skill_lv; - sd->skillitem_keep_requirement = false; - clif_item_skill(sd, improv_skill_id, improv_skill_lv); - } else { - struct unit_data *ud = unit_bl2ud(src); - int inf = skill_get_inf(improv_skill_id); - if (!ud) break; - if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) { - if (src->type == BL_PET) - bl = (struct block_list*)((TBL_PET*)src)->master; - if (!bl) bl = src; - unit_skilluse_id(src, bl->id, improv_skill_id, improv_skill_lv); - } else { - int target_id = 0; - if (ud->target) - target_id = ud->target; - else switch (src->type) { - case BL_MOB: target_id = ((TBL_MOB*)src)->target_id; break; - case BL_PET: target_id = ((TBL_PET*)src)->target_id; break; - } - if (!target_id) - break; - if (skill_get_casttype(improv_skill_id) == CAST_GROUND) { - bl = map_id2bl(target_id); - if (!bl) bl = src; - unit_skilluse_pos(src, bl->x, bl->y, improv_skill_id, improv_skill_lv); - } else - unit_skilluse_id(src, target_id, improv_skill_id, improv_skill_lv); - } - } + clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); } break; @@ -10976,7 +10886,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui sc_start(src, bl, type, rate, skill_lv, duration); } else { clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - map_foreachinallrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ALL|BCT_WOS|1, skill_castend_nodamage_id); + map_foreachinallrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); } break; @@ -12099,7 +12009,7 @@ TIMER_FUNC(skill_castend_id){ #endif } - if (sd && ud->skill_id != SA_ABRACADABRA && ud->skill_id != WM_RANDOMIZESPELL) // they just set the data so leave it as it is.[Inkfish] + if (sd && ud->skill_id != SA_ABRACADABRA) // they just set the data so leave it as it is.[Inkfish] sd->skillitem = sd->skillitemlv = sd->skillitem_keep_requirement = 0; if (ud->skilltimer == INVALID_TIMER) { @@ -12487,7 +12397,6 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case SC_CHAOSPANIC: case SC_MAELSTROM: case SC_BLOODYLUST: - case WM_REVERBERATION: case WM_POEMOFNETHERWORLD: case SO_PSYCHIC_WAVE: case SO_VACUUM_EXTREME: @@ -12964,7 +12873,6 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui break; case WM_GREAT_ECHO: - case WM_SOUND_OF_DESTRUCTION: i = skill_get_splash(skill_id,skill_lv); map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); break; @@ -13863,7 +13771,6 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_ unit_val2 = 0; break; case NPC_REVERBERATION: - case WM_REVERBERATION: unit_val1 = 1 + skill_lv; break; case WM_POEMOFNETHERWORLD: @@ -15424,9 +15331,10 @@ int skill_check_pc_partner(struct map_session_data *sd, uint16 skill_id, uint16 struct map_session_data* tsd; switch (skill_id) { case PR_BENEDICTIO: + case WM_GREAT_ECHO: for (i = 0; i < c; i++) { if ((tsd = map_id2sd(p_sd[i])) != NULL) - status_charge(&tsd->bl, 0, 10); + status_charge(&tsd->bl, 0, (skill_id == PR_BENEDICTIO) ? 10 : skill_get_sp(skill_id, *skill_lv) / 2); } return c; case AB_ADORAMUS: @@ -15456,7 +15364,7 @@ int skill_check_pc_partner(struct map_session_data *sd, uint16 skill_id, uint16 memset (p_sd, 0, sizeof(p_sd)); i = map_foreachinallrange(skill_check_condition_char_sub, &sd->bl, range, BL_PC, &sd->bl, &c, &p_sd, skill_id); - if ( skill_id != PR_BENEDICTIO && skill_id != AB_ADORAMUS && skill_id != WL_COMET ) //Apply the average lv to encore skills. + if ( skill_id != PR_BENEDICTIO && skill_id != AB_ADORAMUS && skill_id != WL_COMET && skill_id != WM_GREAT_ECHO ) //Apply the average lv to encore skills. *skill_lv = (i+(*skill_lv))/(c+1); //I know c should be one, but this shows how it could be used for the average of n partners. return c; } @@ -15680,7 +15588,7 @@ bool skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_i sd->state.arrow_atk = require.ammo?1:0; // perform skill-group checks - if(inf2[INF2_ISCHORUS]) { + if(skill_id != WM_GREAT_ECHO && inf2[INF2_ISCHORUS]) { if (skill_check_pc_partner(sd, skill_id, &skill_lv, AREA_SIZE, 0) < 1) { clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); return false; @@ -16247,13 +16155,10 @@ bool skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_i } break; case WM_GREAT_ECHO: { - int count; - count = skill_check_pc_partner(sd, skill_id, &skill_lv, AREA_SIZE, 0); - if( count < 1 ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_HELPER,0); - return false; - } else - require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party. + int count = skill_check_pc_partner(sd, skill_id, &skill_lv, AREA_SIZE, 1); + + if (count > 0) + require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party. } break; case SO_FIREWALK: @@ -16981,8 +16886,6 @@ struct s_skill_condition skill_get_requirement(struct map_session_data* sd, uint if( sc ) { if( sc->data[SC__LAZINESS] ) req.sp += req.sp + sc->data[SC__LAZINESS]->val1 * 10; - if (sc->data[SC_UNLIMITEDHUMMINGVOICE]) - req.sp += req.sp * sc->data[SC_UNLIMITEDHUMMINGVOICE]->val3 / 100; if( sc->data[SC_RECOGNIZEDSPELL] ) req.sp += req.sp / 4; if( sc->data[SC_OFFERTORIUM]) @@ -16995,6 +16898,8 @@ struct s_skill_condition skill_get_requirement(struct map_session_data* sd, uint if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_SPCONSUM) req.sp -= req.sp * 30 / 100; #endif + if (sc->data[SC_GLOOMYDAY]) + req.sp += req.sp * (skill_lv * 10) / 100; } req.zeny = skill->require.zeny[skill_lv-1]; @@ -17472,6 +17377,8 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 fixcast_r = max(fixcast_r, sc->data[SC_HEAT_BARREL]->val2); if (sc->data[SC_FREEZING]) fixcast_r -= 50; + if (sc->data[SC_SWINGDANCE]) + fixcast_r = max(fixcast_r, skill_lv * 6); // Additive Fixed CastTime values if (sc->data[SC_MANDRAGORA]) fixed += sc->data[SC_MANDRAGORA]->val1 * 500; @@ -17479,6 +17386,8 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 fixed -= 1000; if (sc->data[SC_IZAYOI]) fixed = 0; + if (sc->data[SC_GLOOMYDAY]) + fixed += skill_lv * 500; } if (sc && sc->data[SC_SECRAMENT] && skill_id == HW_MAGICPOWER && (flag&2)) // Sacrament lowers Mystical Amplification cast time fixcast_r = max(fixcast_r, sc->data[SC_SECRAMENT]->val2); @@ -17510,7 +17419,7 @@ int skill_delayfix(struct block_list *bl, uint16 skill_id, uint16 skill_lv) nullpo_ret(bl); sd = BL_CAST(BL_PC, bl); - if (skill_id == SA_ABRACADABRA || skill_id == WM_RANDOMIZESPELL) + if (skill_id == SA_ABRACADABRA) return 0; //Will use picked skill's delay. if (bl->type&battle_config.no_skill_delay) @@ -18460,12 +18369,7 @@ static int skill_trap_splash(struct block_list *bl, va_list ap) } break; case UNT_REVERBERATION: // For proper skill delay animation when used with Dominion Impulse - if (ss->type != BL_PC) - skill_addtimerskill(ss, tick + 50, bl->id, 0, 0, NPC_REVERBERATION_ATK, sg->skill_lv, BF_WEAPON, 0); - else { - skill_addtimerskill(ss, tick + status_get_amotion(ss), bl->id, 0, 0, WM_REVERBERATION_MELEE, sg->skill_lv, BF_WEAPON, 0); - skill_addtimerskill(ss, tick + status_get_amotion(ss) * 2, bl->id, 0, 0, WM_REVERBERATION_MAGIC, sg->skill_lv, BF_MAGIC, 0); - } + skill_addtimerskill(ss, tick + 50, bl->id, 0, 0, NPC_REVERBERATION_ATK, sg->skill_lv, BF_WEAPON, 0); break; case UNT_FIRINGTRAP: case UNT_ICEBOUNDTRAP: @@ -22804,60 +22708,6 @@ std::shared_ptr ReadingSpellbookDatabase::findBook(int32 n return nullptr; } - -const std::string ImprovisedSongDatabase::getDefaultLocation() { - return std::string(db_path) + "/improvise_db.yml"; -} - -/** -* Reads and parses an entry from the improvise_db. -* @param node: YAML node containing the entry. -* @return count of successfully parsed rows -*/ -uint64 ImprovisedSongDatabase::parseBodyNode(const YAML::Node &node) { - std::string skill_name; - - if (!this->asString(node, "Skill", skill_name)) - return 0; - - uint16 skill_id = skill_name2id(skill_name.c_str()); - - if (!skill_id) { - this->invalidWarning(node["Skill"], "Invalid Improvised Song skill name \"%s\", skipping.\n", skill_name.c_str()); - return 0; - } - - if (!skill_get_inf(skill_id)) { - this->invalidWarning(node["Skill"], "Passive skill %s cannot be casted by Improvised Song.\n", skill_name.c_str()); - return 0; - } - - std::shared_ptr improvise = this->find(skill_id); - bool exists = improvise != nullptr; - - if (!exists) { - if (!this->nodesExist(node, { "Probability" })) - return 0; - - improvise = std::make_shared(); - improvise->skill_id = skill_id; - } - - if (this->nodeExists(node, "Probability")) { - uint16 probability; - - if (!this->asUInt16Rate(node, "Probability", probability)) - return 0; - - improvise->per = probability; - } - - if (!exists) - this->put(skill_id, improvise); - - return 1; -} - const std::string MagicMushroomDatabase::getDefaultLocation() { return std::string(db_path) + "/magicmushroom_db.yml"; } @@ -23247,7 +23097,6 @@ static void skill_readdb(void) } abra_db.load(); - improvised_song_db.load(); magic_mushroom_db.load(); reading_spellbook_db.load(); diff --git a/src/map/skill.hpp b/src/map/skill.hpp index c13854c3c2..451697b659 100644 --- a/src/map/skill.hpp +++ b/src/map/skill.hpp @@ -101,7 +101,6 @@ enum e_skill_inf2 : uint8 { INF2_ALLOWONMADO, // Skill that can be used while on Madogear INF2_TARGETMANHOLE, // Skill that can be used to target while under SC__MANHOLE effect INF2_TARGETHIDDEN, // Skill that affects hidden targets - INF2_INCREASEGLOOMYDAYDAMAGE, // Skill that affects SC_GLOOMYDAY_SK INF2_INCREASEDANCEWITHWUGDAMAGE, // Skill that is affected by SC_DANCEWITHWUG INF2_IGNOREWUGBITE, // Skill blocked by RA_WUGBITE INF2_IGNOREAUTOGUARD , // Skill is not blocked by SC_AUTOGUARD (physical-skill only) @@ -1693,9 +1692,9 @@ enum e_skill { WM_LESSON = 2412, WM_METALICSOUND, WM_REVERBERATION, - WM_REVERBERATION_MELEE, + WM_REVERBERATION_MELEE, // Removed on kRO WM_REVERBERATION_MAGIC, - WM_DOMINION_IMPULSE, + WM_DOMINION_IMPULSE, // Removed on kRO WM_SEVERE_RAINSTORM, WM_POEMOFNETHERWORLD, WM_VOICEOFSIREN, diff --git a/src/map/status.cpp b/src/map/status.cpp index acadfaa983..551d6ec58e 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -927,20 +927,21 @@ void initChangeTables(void) set_sc( WA_SWING_DANCE , SC_SWINGDANCE , EFST_SWING, SCB_SPEED|SCB_ASPD ); set_sc( WA_SYMPHONY_OF_LOVER , SC_SYMPHONYOFLOVER , EFST_SYMPHONY_LOVE, SCB_MDEF ); set_sc( WA_MOONLIT_SERENADE , SC_MOONLITSERENADE , EFST_MOONLIT_SERENADE, SCB_MATK ); - set_sc( MI_RUSH_WINDMILL , SC_RUSHWINDMILL , EFST_RUSH_WINDMILL, SCB_WATK ); + set_sc( MI_RUSH_WINDMILL , SC_RUSHWINDMILL , EFST_RUSH_WINDMILL, SCB_WATK|SCB_SPEED ); set_sc( MI_ECHOSONG , SC_ECHOSONG , EFST_ECHOSONG , SCB_DEF ); set_sc( MI_HARMONIZE , SC_HARMONIZE , EFST_HARMONIZE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); set_sc_with_vfx( WM_POEMOFNETHERWORLD , SC_NETHERWORLD , EFST_NETHERWORLD , SCB_NONE ); set_sc_with_vfx( WM_VOICEOFSIREN , SC_VOICEOFSIREN , EFST_SIREN, SCB_NONE ); set_sc_with_vfx( WM_LULLABY_DEEPSLEEP , SC_DEEPSLEEP , EFST_HANDICAPSTATE_DEEP_SLEEP, SCB_NONE ); - set_sc( WM_SIRCLEOFNATURE , SC_SIRCLEOFNATURE , EFST_SIRCLEOFNATURE , SCB_NONE ); + set_sc( WM_SIRCLEOFNATURE , SC_SIRCLEOFNATURE , EFST_SIRCLEOFNATURE , SCB_REGEN ); set_sc( WM_GLOOMYDAY , SC_GLOOMYDAY , EFST_GLOOMYDAY , SCB_FLEE|SCB_SPEED|SCB_ASPD ); - set_sc( WM_SONG_OF_MANA , SC_SONGOFMANA , EFST_SONG_OF_MANA, SCB_NONE ); + set_sc( WM_SONG_OF_MANA , SC_SONGOFMANA , EFST_SONG_OF_MANA, SCB_REGEN ); set_sc( WM_DANCE_WITH_WUG , SC_DANCEWITHWUG , EFST_DANCE_WITH_WUG, SCB_ASPD ); - set_sc( WM_SATURDAY_NIGHT_FEVER , SC_SATURDAYNIGHTFEVER , EFST_SATURDAY_NIGHT_FEVER, SCB_BATK|SCB_DEF|SCB_FLEE|SCB_REGEN ); + set_sc( WM_SOUND_OF_DESTRUCTION , SC_SOUNDOFDESTRUCTION , EFST_SOUND_OF_DESTRUCTION, SCB_NONE ); + set_sc( WM_SATURDAY_NIGHT_FEVER , SC_SATURDAYNIGHTFEVER , EFST_SATURDAY_NIGHT_FEVER, SCB_HIT|SCB_FLEE|SCB_REGEN ); set_sc( WM_LERADS_DEW , SC_LERADSDEW , EFST_LERADS_DEW, SCB_MAXHP ); - set_sc( WM_MELODYOFSINK , SC_MELODYOFSINK , EFST_MELODYOFSINK , SCB_INT ); - set_sc( WM_BEYOND_OF_WARCRY , SC_BEYONDOFWARCRY , EFST_BEYOND_OF_WARCRY, SCB_STR|SCB_CRI|SCB_MAXHP ); + set_sc( WM_MELODYOFSINK , SC_MELODYOFSINK , EFST_MELODYOFSINK , SCB_INT|SCB_MAXSP ); + set_sc( WM_BEYOND_OF_WARCRY , SC_BEYONDOFWARCRY , EFST_BEYOND_OF_WARCRY, SCB_STR|SCB_MAXHP ); set_sc( WM_UNLIMITED_HUMMING_VOICE , SC_UNLIMITEDHUMMINGVOICE , EFST_UNLIMITED_HUMMING_VOICE, SCB_NONE ); set_sc( WM_FRIGG_SONG , SC_FRIGG_SONG , EFST_FRIGG_SONG , SCB_MAXHP ); @@ -1560,6 +1561,8 @@ void initChangeTables(void) StatusIconChangeTable[SC_USE_SKILL_SP_SPA] = EFST_USE_SKILL_SP_SPA; StatusIconChangeTable[SC_USE_SKILL_SP_SHA] = EFST_USE_SKILL_SP_SHA; + StatusChangeFlagTable[SC_ANCILLA] |= SCB_REGEN; + #ifdef RENEWAL // renewal EDP increases your weapon atk StatusChangeFlagTable[SC_EDP] |= SCB_WATK; @@ -3337,8 +3340,6 @@ static int status_get_hpbonus(struct block_list *bl, enum e_status_bonus type) { bonus += sc->data[SC_INCMHP]->val1; if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2) bonus += 500; - if(sc->data[SC_LERADSDEW]) - bonus += sc->data[SC_LERADSDEW]->val3; if (sc->data[SC_PROMOTE_HEALTH_RESERCH]) bonus += sc->data[SC_PROMOTE_HEALTH_RESERCH]->val3; if(sc->data[SC_INSPIRATION]) @@ -3384,6 +3385,8 @@ static int status_get_hpbonus(struct block_list *bl, enum e_status_bonus type) { bonus += sc->data[SC_EPICLESIS]->val2; if(sc->data[SC_FRIGG_SONG]) bonus += sc->data[SC_FRIGG_SONG]->val2; + if(sc->data[SC_LERADSDEW]) + bonus += sc->data[SC_LERADSDEW]->val3; if(sc->data[SC_FORCEOFVANGUARD]) bonus += (3 * sc->data[SC_FORCEOFVANGUARD]->val1); if(sc->data[SC_INSPIRATION]) @@ -3415,7 +3418,7 @@ static int status_get_hpbonus(struct block_list *bl, enum e_status_bonus type) { if(sc->data[SC_VENOMBLEED]) bonus -= 15; if(sc->data[SC_BEYONDOFWARCRY]) - bonus -= sc->data[SC_BEYONDOFWARCRY]->val4; + bonus -= sc->data[SC_BEYONDOFWARCRY]->val3; if(sc->data[SC__WEAKNESS]) bonus -= sc->data[SC__WEAKNESS]->val2; if(sc->data[SC_EQC]) @@ -3542,6 +3545,7 @@ static int status_get_spbonus(struct block_list *bl, enum e_status_bonus type) { //Bonus by SC if (sc) { + //Increasing if(sc->data[SC_INCMSPRATE]) bonus += sc->data[SC_INCMSPRATE]->val1; if(sc->data[SC_RAISINGDRAGON]) @@ -3550,6 +3554,10 @@ static int status_get_spbonus(struct block_list *bl, enum e_status_bonus type) { bonus += sc->data[SC_SERVICE4U]->val2; if(sc->data[SC_MERC_SPUP]) bonus += sc->data[SC_MERC_SPUP]->val2; + + //Decreasing + if (sc->data[SC_MELODYOFSINK]) + bonus -= sc->data[SC_MELODYOFSINK]->val3; } // Max rate reduce is -100% bonus = cap_value(bonus,-100,INT_MAX); @@ -4670,6 +4678,10 @@ int status_calc_pc_sub(struct map_session_data* sd, enum e_status_calc_opt opt) if (sc->data[SC_FORTUNE]) sd->bonus.crit_atk_rate += 2 * sc->data[SC_FORTUNE]->val1; #endif + if (sc->data[SC_SYMPHONYOFLOVER]) { + sd->subele[ELE_GHOST] += sc->data[SC_SYMPHONYOFLOVER]->val1 * 3; + sd->subele[ELE_HOLY] += sc->data[SC_SYMPHONYOFLOVER]->val1 * 3; + } } status_cpy(&sd->battle_status, base_status); @@ -5180,6 +5192,10 @@ void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, str regen->rate.sp += 100; } #endif + if (sc->data[SC_SIRCLEOFNATURE]) + regen->rate.hp += sc->data[SC_SIRCLEOFNATURE]->val2; + if (sc->data[SC_SONGOFMANA]) + regen->rate.sp += sc->data[SC_SONGOFMANA]->val3; } /** @@ -5992,7 +6008,7 @@ static unsigned short status_calc_str(struct block_list *bl, struct status_chang if(sc->data[SC_GIANTGROWTH]) str += 30; if(sc->data[SC_BEYONDOFWARCRY]) - str -= sc->data[SC_BEYONDOFWARCRY]->val3; + str -= sc->data[SC_BEYONDOFWARCRY]->val2; if(sc->data[SC_SAVAGE_STEAK]) str += sc->data[SC_SAVAGE_STEAK]->val1; if(sc->data[SC_INSPIRATION]) @@ -6226,7 +6242,7 @@ static unsigned short status_calc_int(struct block_list *bl, struct status_chang if(sc->data[SC_INSPIRATION]) int_ += sc->data[SC_INSPIRATION]->val3; if(sc->data[SC_MELODYOFSINK]) - int_ -= sc->data[SC_MELODYOFSINK]->val3; + int_ -= sc->data[SC_MELODYOFSINK]->val2; if(sc->data[SC_MANDRAGORA]) int_ -= 4 * sc->data[SC_MANDRAGORA]->val1; if(sc->data[SC_COCKTAIL_WARG_BLOOD]) @@ -6473,8 +6489,6 @@ static unsigned short status_calc_batk(struct block_list *bl, struct status_chan batk += batk * sc->data[SC_FLEET]->val3/100; if(sc->data[SC__ENERVATION]) batk -= batk * sc->data[SC__ENERVATION]->val2 / 100; - if(sc->data[SC_SATURDAYNIGHTFEVER]) - batk += 100 * sc->data[SC_SATURDAYNIGHTFEVER]->val1; if( sc->data[SC_ZANGETSU] ) batk += sc->data[SC_ZANGETSU]->val2; if(sc->data[SC_QUEST_BUFF1]) @@ -6765,10 +6779,10 @@ static signed short status_calc_critical(struct block_list *bl, struct status_ch critical += sc->data[SC__INVISIBILITY]->val3 * 10; if (sc->data[SC__UNLUCKY]) critical -= sc->data[SC__UNLUCKY]->val2; - if(sc->data[SC_BEYONDOFWARCRY]) - critical += sc->data[SC_BEYONDOFWARCRY]->val3; if (sc->data[SC_SOULSHADOW]) critical += 10 * sc->data[SC_SOULSHADOW]->val3; + if(sc->data[SC_BEYONDOFWARCRY]) + critical += sc->data[SC_BEYONDOFWARCRY]->val3; return (short)cap_value(critical,10,SHRT_MAX); } @@ -6835,6 +6849,8 @@ static signed short status_calc_hit(struct block_list *bl, struct status_change #endif if (sc->data[SC_SOULFALCON]) hit += sc->data[SC_SOULFALCON]->val3; + if (sc->data[SC_SATURDAYNIGHTFEVER]) + hit -= 50 + 50 * sc->data[SC_SATURDAYNIGHTFEVER]->val1; return (short)cap_value(hit,1,SHRT_MAX); } @@ -6924,7 +6940,7 @@ static signed short status_calc_flee(struct block_list *bl, struct status_change if( sc->data[SC_GLOOMYDAY] ) flee -= flee * sc->data[SC_GLOOMYDAY]->val2 / 100; if( sc->data[SC_SATURDAYNIGHTFEVER] ) - flee -= flee * (40 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100; + flee -= 20 + 30 * sc->data[SC_SATURDAYNIGHTFEVER]->val1; if( sc->data[SC_WIND_STEP_OPTION] ) flee += flee * sc->data[SC_WIND_STEP_OPTION]->val2 / 100; if( sc->data[SC_TINDER_BREAKER] || sc->data[SC_TINDER_BREAKER2] ) @@ -7052,9 +7068,7 @@ static defType status_calc_def(struct block_list *bl, struct status_change *sc, if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 1 ) def += (5 + sc->data[SC_BANDING]->val1) * sc->data[SC_BANDING]->val2 / 10; if( sc->data[SC_ECHOSONG] ) - def += def * sc->data[SC_ECHOSONG]->val3 / 100; - if( sc->data[SC_SATURDAYNIGHTFEVER] ) - def -= def * (10 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100; + def += sc->data[SC_ECHOSONG]->val3; if( sc->data[SC_EARTHDRIVE] ) def -= def * 25 / 100; if( sc->data[SC_CAMOUFLAGE] ) @@ -7409,6 +7423,8 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha val = max(val, sc->data[SC_ARCLOUSEDASH]->val3); if( sc->data[SC_DORAM_WALKSPEED] ) val = max(val, sc->data[SC_DORAM_WALKSPEED]->val1); + if (sc->data[SC_RUSHWINDMILL]) + val = max(val, 25); // !TODO: Confirm bonus movement speed // !FIXME: official items use a single bonus for this [ultramage] if( sc->data[SC_SPEEDUP0] ) // Temporary item-based speedup @@ -11325,17 +11341,17 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty tick_time = 1000; // [GodLesZ] tick time break; case SC_SWINGDANCE: - val3 = 5 * val1 + val2; // Walk speed and aspd reduction. + val3 = 3 * val1 + val2; // Walk speed and aspd reduction. break; case SC_SYMPHONYOFLOVER: - val3 = 12 * val1 + val2 + (sd?sd->status.job_level:50) / 4; // MDEF Increase in % + val3 = 2 * val1 + val2 + (sd?sd->status.job_level:50) / 4; // MDEF Increase break; case SC_MOONLITSERENADE: // MATK Increase case SC_RUSHWINDMILL: // ATK Increase - val3 = 6 * val1 + val2 + (sd?sd->status.job_level:50) / 5; + val3 = 4 + val1 * 3 + val2 + (sd?sd->status.job_level:50) / 5; break; case SC_ECHOSONG: - val3 = 6 * val1 + val2 + (sd?sd->status.job_level:50) / 4; // DEF Increase in % + val3 = 6 * val1 + val2 + (sd?sd->status.job_level:50) / 4; // DEF Increase break; case SC_HARMONIZE: val2 = 5 + 5 * val1; @@ -11349,15 +11365,11 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty tick_time = 2000; // [GodLesZ] tick time break; case SC_SIRCLEOFNATURE: - val2 = 40 * val1; // HP recovery - val3 = 4 * val1; // SP consume - val4 = tick / 1000; - tick_time = 1000; // [GodLesZ] tick time + val2 = 50 * val1; // HP recovery rate break; case SC_SONGOFMANA: - val3 = 10 + min(5 * val2, 35); - val4 = tick/5000; - tick_time = 5000; // [GodLesZ] tick time + status_heal(bl, 0, status->max_sp * (val1 <= 2 ? 10 : val1 <= 4 ? 15 : 20) / 100, 1); + val3 = 50 * val1; break; case SC_SATURDAYNIGHTFEVER: if (!val4) val4 = skill_get_time2(status_sc2skill(type),val1); @@ -11387,28 +11399,22 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty } break; case SC_DANCEWITHWUG: - val3 = 5 + 5 * val2; // ASPD Increase - val4 = 20 + 10 * val2; // Fixed Cast Time Reduction + val3 = 5 * val1; // ASPD Increase + val4 = 20 + 10 * val1; // Fixed Cast Time Reduction break; case SC_LERADSDEW: - val3 = 200 * val1 + min(300 * val2, 2500); // MaxHP Increase + val3 = 2 + 3 * val1 + min(3 * val2, 25); // MaxHP Increase break; case SC_MELODYOFSINK: - val3 = val1 * val2; // INT Reduction. - val4 = tick/1000; - tick_time = 1000; + val2 = 10 * val1; // INT Reduction. + val3 = 2 + 2 * val1; // MaxSP reduction break; case SC_BEYONDOFWARCRY: - val3 = val1 * val2; // STR and CRIT Reduction - val4 = 4 * val1 + min(4 * (val2 - 2), 40); // MaxHP Reduction + val2 = 10 + 10 * val1; // STR Reduction + val3 = 4 * val1; // MaxHP Reduction break; case SC_UNLIMITEDHUMMINGVOICE: - { - struct unit_data *ud = unit_bl2ud(bl); - if( ud == NULL ) return 0; - ud->state.skillcastcancel = 0; - val3 = 15 - min(3 * val2, 15); - } + val3 = 4 * val1 + min(3 * val2, 15); // !TODO: What's the Lesson bonus? break; case SC_REFLECTDAMAGE: val2 = 15 + 5 * val1; // Reflect amount @@ -14268,25 +14274,6 @@ TIMER_FUNC(status_change_timer){ } break; - case SC_SIRCLEOFNATURE: - if( --(sce->val4) >= 0 ) { - if( !status_charge(bl,0,sce->val3) ) - break; - status_heal(bl, sce->val2, 0, 1); - sc_timer_next(1000 + tick); - return 0; - } - break; - - case SC_SONGOFMANA: - if( --(sce->val4) >= 0 ) { - status_heal(bl,0,sce->val3,3); - sc_timer_next(5000 + tick); - return 0; - } - break; - - case SC_SATURDAYNIGHTFEVER: // 1% HP/SP drain every val4 seconds [Jobbie] if( --(sce->val3) >= 0 ) { @@ -14297,14 +14284,6 @@ TIMER_FUNC(status_change_timer){ } break; - case SC_MELODYOFSINK: - if( --(sce->val4) >= 0 ) { - status_charge(bl, 0, status->max_sp * ( 2 * sce->val1 + 2 * sce->val2 ) / 100); - sc_timer_next(1000+tick); - return 0; - } - break; - case SC_CRYSTALIZE: if( --(sce->val4) >= 0 ) { // Drains 2% of HP and 1% of SP every seconds. if (!status_charge(bl, status->max_hp * 2 / 100, status->max_sp / 100)) diff --git a/src/map/status.hpp b/src/map/status.hpp index 447316faec..2f53737b4e 100644 --- a/src/map/status.hpp +++ b/src/map/status.hpp @@ -928,6 +928,7 @@ enum sc_type : int16 { SC_ADD_MATK_DAMAGE, SC_HELPANGEL, + SC_SOUNDOFDESTRUCTION, #ifdef RENEWAL SC_EXTREMITYFIST2, //! NOTE: This SC should be right before SC_MAX, so it doesn't disturb if RENEWAL is disabled diff --git a/src/tool/csv2yaml.cpp b/src/tool/csv2yaml.cpp index 93036d99f4..34bef42aa8 100644 --- a/src/tool/csv2yaml.cpp +++ b/src/tool/csv2yaml.cpp @@ -91,7 +91,6 @@ static bool guild_read_guildskill_tree_db( char* split[], int columns, int curre static bool pet_read_db( const char* file ); static bool skill_parse_row_magicmushroomdb(char* split[], int column, int current); static bool skill_parse_row_abradb(char* split[], int columns, int current); -static bool skill_parse_row_improvisedb(char* split[], int columns, int current); static bool skill_parse_row_spellbookdb(char* split[], int columns, int current); static bool mob_readdb_mobavail(char *str[], int columns, int current); static bool skill_parse_row_requiredb(char* split[], int columns, int current); @@ -325,12 +324,6 @@ int do_init( int argc, char** argv ){ return 0; } - if (!process("IMPROVISED_SONG_DB", 1, root_paths, "skill_improvise_db", [](const std::string& path, const std::string& name_ext) -> bool { - return sv_readdb(path.c_str(), name_ext.c_str(), ',', 2, 2, -1, &skill_parse_row_improvisedb, false); - }, "improvise_db")) { - return 0; - } - if (!process("READING_SPELLBOOK_DB", 1, root_paths, "spellbook_db", [](const std::string& path, const std::string& name_ext) -> bool { return sv_readdb(path.c_str(), name_ext.c_str(), ',', 3, 3, -1, &skill_parse_row_spellbookdb, false); })) { @@ -930,25 +923,6 @@ static bool skill_parse_row_abradb(char* split[], int columns, int current) return true; } -// Copied and adjusted from skill.cpp -static bool skill_parse_row_improvisedb(char* split[], int columns, int current) -{ - uint16 skill_id = atoi(split[0]); - std::string *skill_name = util::umap_find(aegis_skillnames, skill_id); - - if (skill_name == nullptr) { - ShowError("Skill name for Improvised Song skill ID %hu is not known.\n", skill_id); - return false; - } - - body << YAML::BeginMap; - body << YAML::Key << "Skill" << YAML::Value << *skill_name; - body << YAML::Key << "Probability" << YAML::Value << atoi(split[1]) / 10; - body << YAML::EndMap; - - return true; -} - // Copied and adjusted from skill.cpp static bool skill_parse_row_spellbookdb(char* split[], int columns, int current) { @@ -1591,8 +1565,6 @@ static bool skill_parse_row_skilldb(char* split[], int columns, int current) { body << YAML::Key << "TargetManHole" << YAML::Value << "true"; if (inf3_val & 0x10000) body << YAML::Key << "TargetHidden" << YAML::Value << "true"; - if (inf3_val & 0x20000) - body << YAML::Key << "IncreaseGloomyDayDamage" << YAML::Value << "true"; if (inf3_val & 0x40000) body << YAML::Key << "IncreaseDanceWithWugDamage" << YAML::Value << "true"; if (inf3_val & 0x80000) From 341d0938024dc818a5e496859b4a811e6fac3ae6 Mon Sep 17 00:00:00 2001 From: aleos Date: Mon, 18 May 2020 10:31:17 -0400 Subject: [PATCH 023/141] Renames SQL Upgrade script --- sql-files/upgrades/{upgrade_2020xxxx.sql => upgrade_20200518.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sql-files/upgrades/{upgrade_2020xxxx.sql => upgrade_20200518.sql} (100%) diff --git a/sql-files/upgrades/upgrade_2020xxxx.sql b/sql-files/upgrades/upgrade_20200518.sql similarity index 100% rename from sql-files/upgrades/upgrade_2020xxxx.sql rename to sql-files/upgrades/upgrade_20200518.sql From a23c84d34514e8fd425a0bffee476b131c2de277 Mon Sep 17 00:00:00 2001 From: Aleos Date: Mon, 18 May 2020 10:47:21 -0400 Subject: [PATCH 024/141] Job Improvement Project - Guillotine Cross (#4705) * Fixes #4704. * kRO Changelog: http://ro.gnjoy.com/news/update/View.asp?seq=243&curpage=1 Thanks to @Angelic234, @nehpetskie, @ecdarreola, @Haydrich, @Badarosk0, and @LordWhiplash! --- db/re/skill_db.yml | 73 +++++--------------- src/map/battle.cpp | 138 +++++++++++++++++-------------------- src/map/pc.cpp | 2 +- src/map/skill.cpp | 77 ++++++++++++--------- src/map/skill.hpp | 2 +- src/map/status.cpp | 156 ++++++++++++++++++++++++++++++++---------- src/tool/csv2yaml.cpp | 6 +- 7 files changed, 247 insertions(+), 207 deletions(-) diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index 4c2a2fe18e..778145acee 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -10360,62 +10360,21 @@ Body: Name: ASC_BREAKER Description: Soul Destroyer MaxLevel: 10 - Type: Misc + Type: Weapon TargetType: Attack DamageFlags: - IgnoreAtkCard: true - IgnoreDefense: true - IgnoreFlee: true + Critical: true Range: 9 Hit: Single HitCount: 1 Element: Weapon CastCancel: true CastTime: 250 - AfterCastActDelay: - - Level: 1 - Time: 1000 - - Level: 2 - Time: 1200 - - Level: 3 - Time: 1400 - - Level: 4 - Time: 1600 - - Level: 5 - Time: 1800 - - Level: 6 - Time: 2000 - - Level: 7 - Time: 2200 - - Level: 8 - Time: 2400 - - Level: 9 - Time: 2600 - - Level: 10 - Time: 2800 + AfterCastActDelay: 2000 + Cooldown: 150 FixedCastTime: 250 Requires: - SpCost: - - Level: 1 - Amount: 20 - - Level: 2 - Amount: 20 - - Level: 3 - Amount: 20 - - Level: 4 - Amount: 20 - - Level: 5 - Amount: 20 - - Level: 6 - Amount: 30 - - Level: 7 - Amount: 30 - - Level: 8 - Amount: 30 - - Level: 9 - Amount: 30 - - Level: 10 - Amount: 30 + SpCost: 20 - Id: 380 Name: SN_SIGHT Description: Falcon Eyes @@ -19091,7 +19050,9 @@ Body: MaxLevel: 5 Type: Weapon TargetType: Attack - Range: 3 + DamageFlags: + Critical: true + Range: 7 Hit: Multi_Hit HitCount: -7 Element: Weapon @@ -19109,11 +19070,12 @@ Body: Time: 1000 - Level: 5 Time: 500 + Cooldown: 700 FixedCastTime: -1 CastDelayFlags: IgnoreStatus: true Requires: - SpCost: 25 + SpCost: 40 State: Move_Enable - Id: 2023 Name: GC_DARKILLUSION @@ -19463,13 +19425,13 @@ Body: - Level: 2 Area: 1 - Level: 3 - Area: 1 - - Level: 4 - Area: 1 - - Level: 5 Area: 2 + - Level: 4 + Area: 2 + - Level: 5 + Area: 3 AfterCastActDelay: 200 - Duration1: 3000 + Duration1: 5000 FixedCastTime: -1 Requires: SpCost: 5 @@ -19495,7 +19457,8 @@ Body: Hit: Single HitCount: 1 Element: Weapon - AfterCastActDelay: 1000 + AfterCastActDelay: 500 + Cooldown: 500 FixedCastTime: -1 Requires: SpCost: @@ -31587,7 +31550,7 @@ Body: CopyFlags: Skill: Reproduce: true - Duration1: 5000 + Duration1: 10000 Cooldown: 60000 FixedCastTime: -1 Requires: diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 4691e291bc..b9e3a0e25b 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -1342,6 +1342,8 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam if (sc->data[SC_SOUNDOFDESTRUCTION]) damage <<= 1; + if (sc->data[SC_DARKCROW] && (flag&(BF_SHORT|BF_MAGIC)) == BF_SHORT) + damage += damage * sc->data[SC_DARKCROW]->val2 / 100; // Damage reductions // Assumptio increases DEF on RE mode, otherwise gives a reduction on the final damage. [Igniz] @@ -1438,9 +1440,6 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam damage = i64max(damage, 1); } - if( sc->data[SC_DARKCROW] && (flag&(BF_SHORT|BF_MAGIC)) == BF_SHORT ) - damage += damage * sc->data[SC_DARKCROW]->val2 / 100; - if( (sce=sc->data[SC_MAGMA_FLOW]) && (rnd()%100 <= sce->val2) ) skill_castend_damage_id(bl,src,MH_MAGMA_FLOW,sce->val1,gettick(),0); @@ -1609,10 +1608,11 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam status_change_end(src,SC_SHIELDSPELL_REF,INVALID_TIMER); } - if( sc->data[SC_POISONINGWEAPON] - && ((flag&BF_WEAPON) && (!skill_id || skill_id == GC_VENOMPRESSURE)) //check skill type poison_smoke is a unit - && (damage > 0 && rnd()%100 < sc->data[SC_POISONINGWEAPON]->val3 )) //did some damage and chance ok (why no additional effect ??) - sc_start2(src,bl,(enum sc_type)sc->data[SC_POISONINGWEAPON]->val2,100,sc->data[SC_POISONINGWEAPON]->val1,sc->data[SC_POISONINGWEAPON]->val4,skill_get_time2(GC_POISONINGWEAPON, 1)); + if (sc->data[SC_POISONINGWEAPON] && flag&BF_SHORT && (skill_id == 0 || skill_id == GC_VENOMPRESSURE) && damage > 0) { + damage += damage * 10 / 100; + if (rnd() % 100 < sc->data[SC_POISONINGWEAPON]->val3) + sc_start4(src, bl, (sc_type)sc->data[SC_POISONINGWEAPON]->val2, 100, sc->data[SC_POISONINGWEAPON]->val1, 0, 1, 0, skill_get_time2(GC_POISONINGWEAPON, 1)); + } if( sc->data[SC__DEADLYINFECT] && (flag&(BF_SHORT|BF_MAGIC)) == BF_SHORT && damage > 0 && rnd()%100 < 30 + 10 * sc->data[SC__DEADLYINFECT]->val1 ) status_change_spread(src, bl, 0); @@ -2237,6 +2237,10 @@ static int battle_range_type(struct block_list *src, struct block_list *target, if (src->type == BL_MOB && (skill_id == AC_SHOWER || skill_id == AM_DEMONSTRATION)) return BF_SHORT; + // Cast range is 7 cells and player jumps to target but skill is considered melee + if (skill_id == GC_CROSSIMPACT) + return BF_SHORT; + //Skill Range Criteria if (battle_config.skillrange_by_distance && (src->type&battle_config.skillrange_by_distance) @@ -2568,6 +2572,12 @@ static bool is_attack_critical(struct Damage* wd, struct block_list *src, struct case NJ_KIRIKAGE: cri += 250 + 50*skill_lv; break; +#ifdef RENEWAL + case ASC_BREAKER: +#endif + case GC_CROSSIMPACT: + cri /= 2; + break; } if(tsd && tsd->bonus.critical_def) cri = cri * ( 100 - tsd->bonus.critical_def ) / 100; @@ -2592,13 +2602,12 @@ static int is_attack_piercing(struct Damage* wd, struct block_list *src, struct if(src != NULL) { struct map_session_data *sd = BL_CAST(BL_PC, src); struct status_data *tstatus = status_get_status_data(target); -#ifdef RENEWAL - if( skill_id != PA_SACRIFICE && skill_id != CR_GRANDCROSS && skill_id != NPC_GRANDDARKNESS - && skill_id != PA_SHIELDCHAIN && skill_id != KO_HAPPOKUNAI && skill_id != ASC_BREAKER ) // Renewal: Soul Breaker no longer gains ice pick effect and ice pick effect gets crit benefit [helvetica] -#else - if( skill_id != PA_SACRIFICE && skill_id != CR_GRANDCROSS && skill_id != NPC_GRANDDARKNESS - && skill_id != PA_SHIELDCHAIN && skill_id != KO_HAPPOKUNAI && !is_attack_critical(wd, src, target, skill_id, skill_lv, false) ) + + if( skill_id != PA_SACRIFICE && skill_id != CR_GRANDCROSS && skill_id != NPC_GRANDDARKNESS && skill_id != PA_SHIELDCHAIN && skill_id != KO_HAPPOKUNAI +#ifndef RENEWAL + && !is_attack_critical(wd, src, target, skill_id, skill_lv, false) #endif + ) { //Elemental/Racial adjustments if( sd && (sd->right_weapon.def_ratio_atk_ele & (1<def_ele) || sd->right_weapon.def_ratio_atk_ele & (1<right_weapon.def_ratio_atk_race & (1<race) || sd->right_weapon.def_ratio_atk_race & (1<data[SC_FUSION]) return true; -#ifdef RENEWAL - else if (skill_id != CR_GRANDCROSS && skill_id != NPC_GRANDDARKNESS && skill_id != ASC_BREAKER) // Renewal: Soul Breaker no longer gains ignore DEF from weapon [helvetica] -#else else if (skill_id != CR_GRANDCROSS && skill_id != NPC_GRANDDARKNESS) -#endif { //Ignore Defense? if (sd && (sd->right_weapon.ignore_def_ele & (1<def_ele) || sd->right_weapon.ignore_def_ele & (1<right_weapon.ignore_def_race & (1<race) || sd->right_weapon.ignore_def_race & (1<data[SC_KAGEMUSYA]) max_rate = sc->data[SC_KAGEMUSYA]->val1 * 10; // Same rate as even levels of TF_DOUBLE else +#ifdef RENEWAL + max_rate = max(7 * skill_lv, sd->bonus.double_rate); +#else max_rate = max(5 * skill_lv, sd->bonus.double_rate); +#endif if( rnd()%100 < max_rate ) { wd->div_ = skill_get_num(TF_DOUBLE,skill_lv?skill_lv:1); @@ -3933,12 +3942,15 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list * if(sd) skillratio += 20 * pc_checkskill(sd,AS_POISONREACT); break; -#ifndef RENEWAL - // Pre-Renewal: skill ratio for weapon part of damage [helvetica] case ASC_BREAKER: +#ifdef RENEWAL + skillratio += -100 + 140 * skill_lv + sstatus->str + sstatus->int_; // !TODO: Confirm stat modifier + RE_LVL_DMOD(100); +#else + // Pre-Renewal: skill ratio for weapon part of damage [helvetica] skillratio += -100 + 100 * skill_lv; - break; #endif + break; case PA_SACRIFICE: skillratio += -10 + 10 * skill_lv; break; @@ -4128,8 +4140,8 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list * RE_LVL_DMOD(150); // Base level bonus. break; case GC_CROSSIMPACT: - skillratio += 900 + 100 * skill_lv; - RE_LVL_DMOD(120); + skillratio += -100 + 1000 + 150 * skill_lv; + RE_LVL_DMOD(100); break; case GC_COUNTERSLASH: //ATK [{(Skill Level x 150) + 300} x Caster's Base Level / 120]% + ATK [(AGI x 2) + (Caster's Job Level x 4)]% @@ -4143,14 +4155,14 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list * skillratio += 200; break; case GC_ROLLINGCUTTER: - skillratio += -50 + 50 * skill_lv; + skillratio += -100 + 50 + 80 * skill_lv; RE_LVL_DMOD(100); break; case GC_CROSSRIPPERSLASHER: - skillratio += 300 + 80 * skill_lv; + skillratio += -100 + 80 * skill_lv + (sstatus->agi * 3); RE_LVL_DMOD(100); if (sc && sc->data[SC_ROLLINGCUTTER]) - skillratio += sc->data[SC_ROLLINGCUTTER]->val1 * status_get_agi(src); + skillratio += sc->data[SC_ROLLINGCUTTER]->val1 * 200; break; case GC_DARKCROW: skillratio += 100 * (skill_lv - 1); @@ -4909,6 +4921,11 @@ static void battle_attack_sc_bonus(struct Damage* wd, struct block_list *src, st RE_ALLATK_ADDRATE(wd, sc->data[SC_GVG_GIANT]->val3); } + if (sc->data[SC_PYREXIA] && sc->data[SC_PYREXIA]->val3 == 0) { + ATK_ADDRATE(wd->damage, wd->damage2, sc->data[SC_PYREXIA]->val2); + RE_ALLATK_ADDRATE(wd, sc->data[SC_PYREXIA]->val2); + } + if (sc->data[SC_MIRACLE]) anger_id = 2; // Always treat all monsters as star flagged monster when in miracle state } @@ -5859,7 +5876,6 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl #ifdef RENEWAL switch(skill_id) { case NJ_ISSEN: - case ASC_BREAKER: break; //These skills will do a card fix later default: #endif @@ -5938,7 +5954,6 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl #ifdef RENEWAL switch (skill_id) { case NJ_ISSEN: - case ASC_BREAKER: return wd; //These skills will do a GVG fix later default: #endif @@ -6602,13 +6617,7 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list } } #ifdef RENEWAL - switch(skill_id) { // These skills will do a card fix later - case ASC_BREAKER: - break; - default: - ad.damage += battle_calc_cardfix(BF_MAGIC, src, target, nk, s_ele, 0, ad.damage, 0, ad.flag); - break; - } + ad.damage += battle_calc_cardfix(BF_MAGIC, src, target, nk, s_ele, 0, ad.damage, 0, ad.flag); #endif if(sd) { @@ -6704,7 +6713,7 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list } } - if (!nk[NK_IGNOREELEMENT] && skill_id != ASC_BREAKER) // Soul Breaker's magic portion is non-elemental. [Secret] + if (!nk[NK_IGNOREELEMENT]) ad.damage = battle_attr_fix(src, target, ad.damage, s_ele, tstatus->def_ele, tstatus->ele_lv); //Apply the physical part of the skill's damage. [Skotlex] @@ -6737,13 +6746,6 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list //Apply DAMAGE_DIV_FIX and check for min damage battle_apply_div_fix(&ad, skill_id); -#ifdef RENEWAL - switch(skill_id) { - case ASC_BREAKER: - return ad; //These skills will do a GVG fix later - } -#endif - struct map_data *mapdata = map_getmapdata(target->m); ad.damage = battle_calc_damage(src,target,&ad,ad.damage,skill_id,skill_lv); @@ -6887,35 +6889,12 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list * case NPC_EVILLAND: md.damage = skill_calc_heal(src,target,skill_id,skill_lv,false); break; +#ifndef RENEWAL case ASC_BREAKER: -#ifdef RENEWAL - // Official Renewal formula [helvetica] - // damage = ((atk + matk) * (3 + (.5 * skill level))) - (edef + sdef + emdef + smdef) - // atk part takes weapon element, matk part is non-elemental - // modified def formula - { - short totaldef, totalmdef; - struct Damage atk, matk; - - atk = battle_calc_weapon_attack(src, target, skill_id, skill_lv, 0); - nk.set(NK_IGNOREELEMENT); // atk part takes on weapon element, matk part is non-elemental - matk = battle_calc_magic_attack(src, target, skill_id, skill_lv, 0); - - // (atk + matk) * (3 + (.5 * skill level)) - md.damage = ((30 + (5 * skill_lv)) * (atk.damage + matk.damage)) / 10; - - // modified def reduction, final damage = base damage - (edef + sdef + emdef + smdef) - totaldef = tstatus->def2 + (short)status_get_def(target); - totalmdef = tstatus->mdef + tstatus->mdef2; - md.damage -= totaldef + totalmdef; - } -#else md.damage = 500 + rnd()%500 + 5 * skill_lv * sstatus->int_; nk.set(NK_IGNOREFLEE); nk.set(NK_IGNOREELEMENT); //These two are not properties of the weapon based part. -#endif break; -#ifndef RENEWAL case HW_GRAVITATION: md.damage = 200 + 200 * skill_lv; md.dmotion = 0; //No flinch animation @@ -7251,8 +7230,11 @@ int64 battle_calc_return_damage(struct block_list* bl, struct block_list *src, i sc = status_get_sc(bl); ssc = status_get_sc(src); - if (sc && sc->data[SC_WHITEIMPRISON]) - return 0; // White Imprison does not reflect any damage + if (sc) { // These statuses do not reflect any damage (off the target) + if (sc->data[SC_WHITEIMPRISON] || sc->data[SC_DARKCROW] || + (sc->data[SC_KYOMU] && (!ssc || !ssc->data[SC_SHIELDSPELL_DEF]))) // Nullify reflecting ability except for Shield Spell - Def + return 0; + } if (ssc && ssc->data[SC_REF_T_POTION]) return 0; @@ -7320,20 +7302,22 @@ int64 battle_calc_return_damage(struct block_list* bl, struct block_list *src, i } } - if (ssc && ssc->data[SC_INSPIRATION]) { - rdamage += damage / 100; + if (ssc) { + if (ssc->data[SC_INSPIRATION]) { + rdamage += damage / 100; #ifdef RENEWAL - rdamage = cap_value(rdamage, 1, max_damage); + rdamage = cap_value(rdamage, 1, max_damage); #else - rdamage = i64max(rdamage,1); + rdamage = i64max(rdamage, 1); #endif + } + if (ssc->data[SC_VENOMBLEED] && ssc->data[SC_VENOMBLEED]->val3 == 0) + rdamage -= damage * ssc->data[SC_VENOMBLEED]->val2 / 100; } - if (sc && sc->data[SC_KYOMU] && (!ssc || !ssc->data[SC_SHIELDSPELL_DEF])) // Nullify reflecting ability except for Shield Spell - Def - rdamage = 0; - - if (sc && sc->data[SC_MAXPAIN]) { - rdamage = damage * sc->data[SC_MAXPAIN]->val1 * 10 / 100; + if (sc) { + if (sc->data[SC_MAXPAIN]) + rdamage = damage * sc->data[SC_MAXPAIN]->val1 * 10 / 100; } return cap_value(min(rdamage,max_damage),INT_MIN,INT_MAX); diff --git a/src/map/pc.cpp b/src/map/pc.cpp index be33c709d3..dfc661fb0e 100755 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -8977,7 +8977,7 @@ int pc_itemheal(struct map_session_data *sd, int itemid, int hp, int sp) if (sd->sc.data[SC_CRITICALWOUND]) penalty += sd->sc.data[SC_CRITICALWOUND]->val2; - if (sd->sc.data[SC_DEATHHURT]) + if (sd->sc.data[SC_DEATHHURT] && sd->sc.data[SC_DEATHHURT]->val3 == 1) penalty += 20; if (sd->sc.data[SC_NORECOVER_STATE]) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 4e0ff08903..b369fc5e67 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -740,7 +740,7 @@ int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 sk if (tsc->data[SC_CRITICALWOUND]) penalty += tsc->data[SC_CRITICALWOUND]->val2; - if (tsc->data[SC_DEATHHURT]) + if (tsc->data[SC_DEATHHURT] && tsc->data[SC_DEATHHURT]->val3 == 1) penalty += 20; if (tsc->data[SC_NORECOVER_STATE]) penalty = 100; @@ -1233,7 +1233,11 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 if( sd ) { // These statuses would be applied anyway even if the damage was blocked by some skills. [Inkfish] - if( skill_id != WS_CARTTERMINATION && skill_id != AM_DEMONSTRATION && skill_id != CR_REFLECTSHIELD && skill_id != MS_REFLECTSHIELD && skill_id != ASC_BREAKER ) { + if( skill_id != WS_CARTTERMINATION && skill_id != AM_DEMONSTRATION && skill_id != CR_REFLECTSHIELD && skill_id != MS_REFLECTSHIELD +#ifndef RENEWAL + && skill_id != ASC_BREAKER +#endif + ) { // Trigger status effects enum sc_type type; unsigned int time; @@ -4818,9 +4822,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint case GS_FULLBUSTER: case NJ_SYURIKEN: case NJ_KUNAI: -#ifndef RENEWAL case ASC_BREAKER: -#endif case HFLI_MOON: //[orn] case HFLI_SBR44: //[orn] case NPC_BLEEDING: @@ -4834,7 +4836,6 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint case NC_AXEBOOMERANG: case NC_POWERSWING: case NC_MAGMA_ERUPTION: - case GC_CROSSIMPACT: case GC_WEAPONCRUSH: case GC_VENOMPRESSURE: case SC_TRIANGLESHOT: @@ -5518,9 +5519,6 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint case CR_ACIDDEMONSTRATION: #endif case TF_THROWSTONE: -#ifdef RENEWAL - case ASC_BREAKER: -#endif case NPC_SMOKING: case GS_FLING: case NJ_ZENYNAGE: @@ -5675,7 +5673,14 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint else { skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - status_change_end(src,SC_ROLLINGCUTTER,INVALID_TIMER); + } + break; + case GC_CROSSIMPACT: { + uint8 dir = map_calc_dir(bl, src->x, src->y); + + if (skill_check_unit_movepos(5, src, bl->x, bl->y, 1, 1)) + skill_blown(src, src, 1, (dir + 4) % 8, BLOWN_NONE); // Target position is actually one cell next to the target + skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); } break; @@ -8301,7 +8306,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui } if (tsc->data[SC_CRITICALWOUND]) penalty += tsc->data[SC_CRITICALWOUND]->val2; - if (tsc->data[SC_DEATHHURT]) + if (tsc->data[SC_DEATHHURT] && tsc->data[SC_DEATHHURT]->val3) penalty += 20; if (tsc->data[SC_NORECOVER_STATE]) penalty = 100; @@ -8532,6 +8537,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui #ifdef RENEWAL clif_blown(src); // Always blow, otherwise it shows a casting animation. [Lemongrass] #endif + status_change_end(src, SC_ROLLINGCUTTER, INVALID_TIMER); break; case TK_HIGHJUMP: @@ -9169,7 +9175,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui } if (tsc->data[SC_CRITICALWOUND]) penalty += tsc->data[SC_CRITICALWOUND]->val2; - if (tsc->data[SC_DEATHHURT]) + if (tsc->data[SC_DEATHHURT] && tsc->data[SC_DEATHHURT]->val3 == 1) penalty += 20; if (tsc->data[SC_NORECOVER_STATE]) penalty = 100; @@ -17468,16 +17474,18 @@ int skill_delayfix(struct block_list *bl, uint16 skill_id, uint16 skill_lv) } } - if (sc && sc->data[SC_SPIRIT]) { - switch (skill_id) { - case CR_SHIELDBOOMERANG: - if (sc->data[SC_SPIRIT]->val2 == SL_CRUSADER) - time /= 2; - break; - case AS_SONICBLOW: - if (!map_flag_gvg2(bl->m) && !map_getmapflag(bl->m, MF_BATTLEGROUND) && sc->data[SC_SPIRIT]->val2 == SL_ASSASIN) - time /= 2; - break; + if (sc && sc->count) { + if (sc->data[SC_SPIRIT]) { + switch (skill_id) { + case CR_SHIELDBOOMERANG: + if (sc->data[SC_SPIRIT]->val2 == SL_CRUSADER) + time /= 2; + break; + case AS_SONICBLOW: + if (!map_flag_gvg2(bl->m) && !map_getmapflag(bl->m, MF_BATTLEGROUND) && sc->data[SC_SPIRIT]->val2 == SL_ASSASIN) + time /= 2; + break; + } } } @@ -17487,6 +17495,8 @@ int skill_delayfix(struct block_list *bl, uint16 skill_id, uint16 skill_lv) time -= time * sc->data[SC_POEMBRAGI]->val3 / 100; if (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 3 && skill_get_type(skill_id) == BF_MAGIC && skill_get_ele(skill_id, skill_lv) == ELE_WIND) time /= 2; // After Delay of Wind element spells reduced by 50%. + if (sc->data[SC_MAGICMUSHROOM] && sc->data[SC_MAGICMUSHROOM]->val3 == 0) + time -= time * sc->data[SC_MAGICMUSHROOM]->val2 / 100; } } @@ -20477,27 +20487,27 @@ bool skill_arrow_create(struct map_session_data *sd, unsigned short nameid) */ int skill_poisoningweapon(struct map_session_data *sd, unsigned short nameid) { - sc_type type; - int chance, i, val4 = 0; - //uint16 msg = 1443; //Official is using msgstringtable.txt - char output[CHAT_SIZE_MAX]; - const char *msg; - nullpo_ret(sd); - if( !nameid || (i = pc_search_inventory(sd,nameid)) < 0 || pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME) ) { + if( !nameid || pc_delitem(sd,pc_search_inventory(sd,nameid),1,0,0,LOG_TYPE_CONSUME) ) { clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0); return 0; } - switch( nameid ) { // t_lv used to take duration from skill_get_time2 + sc_type type; + int chance; + //uint16 msg = 1443; //Official is using msgstringtable.txt + char output[CHAT_SIZE_MAX]; + const char *msg; + + switch( nameid ) { case ITEMID_PARALYSE: type = SC_PARALYSE; /*msg = 1444;*/ msg = "Paralyze"; break; case ITEMID_PYREXIA: type = SC_PYREXIA; /*msg = 1448;*/ msg = "Pyrexia"; break; case ITEMID_DEATHHURT: type = SC_DEATHHURT; /*msg = 1447;*/ msg = "Deathhurt"; break; - case ITEMID_LEECHESEND: type = SC_LEECHESEND; /*msg = 1450;*/ msg = "Leech End"; val4 = sd->bl.id; break; + case ITEMID_LEECHESEND: type = SC_LEECHESEND; /*msg = 1450;*/ msg = "Leech End"; break; case ITEMID_VENOMBLEED: type = SC_VENOMBLEED; /*msg = 1445;*/ msg = "Venom Bleed"; break; - case ITEMID_TOXIN: type = SC_TOXIN; /*msg = 1443;*/ msg = "Toxin"; val4 = sd->bl.id; break; - case ITEMID_MAGICMUSHROOM: type = SC_MAGICMUSHROOM; /*msg = 1446;*/ msg = "Magic Mushroom"; val4 = sd->bl.id; break; + case ITEMID_TOXIN: type = SC_TOXIN; /*msg = 1443;*/ msg = "Toxin"; break; + case ITEMID_MAGICMUSHROOM: type = SC_MAGICMUSHROOM; /*msg = 1446;*/ msg = "Magic Mushroom"; break; case ITEMID_OBLIVIONCURSE: type = SC_OBLIVIONCURSE; /*msg = 1449;*/ msg = "Oblivion Curse"; break; default: clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0); @@ -20507,7 +20517,8 @@ int skill_poisoningweapon(struct map_session_data *sd, unsigned short nameid) status_change_end(&sd->bl, SC_POISONINGWEAPON, INVALID_TIMER); // End the status so a new poison can be applied (if changed) chance = 2 + 2 * sd->menuskill_val; // 2 + 2 * skill_lv sc_start4(&sd->bl,&sd->bl, SC_POISONINGWEAPON, 100, pc_checkskill(sd, GC_RESEARCHNEWPOISON), //in Aegis it store the level of GC_RESEARCHNEWPOISON in val1 - type, chance, val4, skill_get_time(GC_POISONINGWEAPON, sd->menuskill_val)); + type, chance, 0, skill_get_time(GC_POISONINGWEAPON, sd->menuskill_val)); + status_change_start(&sd->bl, &sd->bl, type, 10000, sd->menuskill_val, 0, 0, 0, skill_get_time(GC_POISONINGWEAPON, sd->menuskill_val), SCSTART_NOAVOID | SCSTART_NOICON); // Apply bonus to caster sprintf(output, msg_txt(sd,721), msg); clif_messagecolor(&sd->bl,color_table[COLOR_WHITE],output,false,SELF); diff --git a/src/map/skill.hpp b/src/map/skill.hpp index 451697b659..06cccbffc0 100644 --- a/src/map/skill.hpp +++ b/src/map/skill.hpp @@ -2294,7 +2294,7 @@ public: } const std::string getDefaultLocation(); - uint64 parseBodyNode(const YAML::Node& node); + uint64 parseBodyNode(const YAML::Node &node); }; extern MagicMushroomDatabase magic_mushroom_db; diff --git a/src/map/status.cpp b/src/map/status.cpp index 551d6ec58e..c491497fe7 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -1469,7 +1469,7 @@ void initChangeTables(void) StatusChangeFlagTable[SC_DEATHHURT] |= SCB_REGEN; StatusChangeFlagTable[SC_VENOMBLEED] |= SCB_MAXHP; StatusChangeFlagTable[SC_MAGICMUSHROOM] |= SCB_REGEN; - StatusChangeFlagTable[SC_PYREXIA] |= SCB_HIT|SCB_FLEE; + StatusChangeFlagTable[SC_PYREXIA] |= SCB_ALL; StatusChangeFlagTable[SC_OBLIVIONCURSE] |= SCB_REGEN; StatusChangeFlagTable[SC_BANDING_DEFENCE] |= SCB_SPEED; StatusChangeFlagTable[SC_SHIELDSPELL_DEF] |= SCB_WATK; @@ -1694,7 +1694,7 @@ void initChangeTables(void) #endif StatusChangeStateTable[SC__BLOODYLUST] |= SCS_NOCAST; StatusChangeStateTable[SC_DEATHBOUND] |= SCS_NOCAST; - StatusChangeStateTable[SC_OBLIVIONCURSE] |= SCS_NOCAST; + StatusChangeStateTable[SC_OBLIVIONCURSE] |= SCS_NOCAST|SCS_NOCASTCOND; StatusChangeStateTable[SC_WHITEIMPRISON] |= SCS_NOCAST; StatusChangeStateTable[SC__SHADOWFORM] |= SCS_NOCAST; StatusChangeStateTable[SC__INVISIBILITY] |= SCS_NOCAST; @@ -3415,7 +3415,7 @@ static int status_get_hpbonus(struct block_list *bl, enum e_status_bonus type) { bonus += sc->data[SC_LUNARSTANCE]->val2; //Decreasing - if(sc->data[SC_VENOMBLEED]) + if (sc->data[SC_VENOMBLEED] && sc->data[SC_VENOMBLEED]->val3 == 1) bonus -= 15; if(sc->data[SC_BEYONDOFWARCRY]) bonus -= sc->data[SC_BEYONDOFWARCRY]->val3; @@ -4682,6 +4682,8 @@ int status_calc_pc_sub(struct map_session_data* sd, enum e_status_calc_opt opt) sd->subele[ELE_GHOST] += sc->data[SC_SYMPHONYOFLOVER]->val1 * 3; sd->subele[ELE_HOLY] += sc->data[SC_SYMPHONYOFLOVER]->val1 * 3; } + if (sc->data[SC_PYREXIA] && sc->data[SC_PYREXIA]->val3 == 0) + sd->bonus.crit_atk_rate += sc->data[SC_PYREXIA]->val2; } status_cpy(&sd->battle_status, base_status); @@ -5098,7 +5100,7 @@ void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, str || sc->data[SC_BERSERK] || sc->data[SC_TRICKDEAD] || sc->data[SC_BLEEDING] - || sc->data[SC_MAGICMUSHROOM] + || (sc->data[SC_MAGICMUSHROOM] && sc->data[SC_MAGICMUSHROOM]->val3 == 1) || sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC_REBOUND]) regen->flag = RGN_NONE; @@ -5115,7 +5117,7 @@ void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, str (bl->type == BL_PC && (((TBL_PC*)bl)->class_&MAPID_UPPERMASK) == MAPID_MONK && sc->data[SC_EXTREMITYFIST] && (!sc->data[SC_SPIRIT] || sc->data[SC_SPIRIT]->val2 != SL_MONK)) || #endif - sc->data[SC_OBLIVIONCURSE] || sc->data[SC_VITALITYACTIVATION]) + (sc->data[SC_OBLIVIONCURSE] && sc->data[SC_OBLIVIONCURSE]->val3 == 1) || sc->data[SC_VITALITYACTIVATION]) regen->flag &= ~RGN_SP; if (sc->data[SC_TENSIONRELAX]) { @@ -5233,6 +5235,8 @@ void status_calc_state( struct block_list *bl, struct status_change *sc, enum sc if( flag&SCS_NOCAST ) { if( !(flag&SCS_NOCASTCOND) ) sc->cant.cast += ( start ? 1 : -1 ); + else if (sc->data[SC_OBLIVIONCURSE] && sc->data[SC_OBLIVIONCURSE]->val3 == 1) + sc->cant.cast += (start ? 1 : -1); } // Can't chat @@ -6931,7 +6935,7 @@ static signed short status_calc_flee(struct block_list *bl, struct status_change flee -= flee * 25/100; if(sc->data[SC_FEAR]) flee -= flee * 20 / 100; - if(sc->data[SC_PARALYSE]) + if(sc->data[SC_PARALYSE] && sc->data[SC_PARALYSE]->val3 == 1) flee -= flee * 10 / 100; if(sc->data[SC_INFRAREDSCAN]) flee -= flee * 30 / 100; @@ -7409,6 +7413,8 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha val = max( val, 75 ); if( sc->data[SC_CLOAKINGEXCEED] ) val = max( val, sc->data[SC_CLOAKINGEXCEED]->val3); + if (sc->data[SC_PARALYSE] && sc->data[SC_PARALYSE]->val3 == 0) + val = max(val, 50); if( sc->data[SC_HOVERING] ) val = max( val, 10 ); if( sc->data[SC_GN_CARTBOOST] ) @@ -7441,7 +7447,7 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha // GetSpeed() if( sd && pc_iscarton(sd) ) speed += speed * (50 - 5 * pc_checkskill(sd,MC_PUSHCART)) / 100; - if( sc->data[SC_PARALYSE] ) + if( sc->data[SC_PARALYSE] && sc->data[SC_PARALYSE]->val3 == 1 ) speed += speed * 50 / 100; if( speed_rate != 100 ) speed = speed * speed_rate / 100; @@ -7542,7 +7548,7 @@ static short status_calc_aspd(struct block_list *bl, struct status_change *sc, b bonus -= 30; if (sc->data[SC_HALLUCINATIONWALK_POSTDELAY]) bonus -= 50; - if (sc->data[SC_PARALYSE]) + if (sc->data[SC_PARALYSE] && sc->data[SC_PARALYSE]->val3 == 1) bonus -= 10; if (sc->data[SC__BODYPAINT]) bonus -= 5 * sc->data[SC__BODYPAINT]->val1; @@ -7742,7 +7748,7 @@ static short status_calc_aspd_rate(struct block_list *bl, struct status_change * aspd_rate += 300; if( sc->data[SC_HALLUCINATIONWALK_POSTDELAY] ) aspd_rate += 500; - if( sc->data[SC_PARALYSE] ) + if( sc->data[SC_PARALYSE] && sc->data[SC_PARALYSE]->val3 == 1 ) aspd_rate += 100; if( sc->data[SC__BODYPAINT] ) aspd_rate += 50 * sc->data[SC__BODYPAINT]->val1; @@ -8549,8 +8555,9 @@ static int status_get_sc_interval(enum sc_type type) { switch (type) { case SC_POISON: - case SC_DPOISON: case SC_LEECHESEND: + case SC_DPOISON: + case SC_DEATHHURT: return 1000; case SC_BURNING: case SC_PYREXIA: @@ -8765,10 +8772,11 @@ t_tick status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_ tick_def2 = (status->vit + status->dex)*50; break; case SC_OBLIVIONCURSE: // 100% - (100 - 0.8 x INT) - sc_def = status->int_*80; + sc_def = status->int_ * 80; sc_def = max(sc_def, 500); // minimum of 5% resist tick_def = 0; - //Fall through + tick_def2 = (status->vit + status->luk) * 500; + break; case SC_TOXIN: case SC_PARALYSE: case SC_VENOMBLEED: @@ -8853,6 +8861,19 @@ t_tick status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_ #endif else if (sc->data[SC_SHIELDSPELL_REF] && sc->data[SC_SHIELDSPELL_REF]->val1 == 2) sc_def += sc->data[SC_SHIELDSPELL_REF]->val3*100; + else if (sc->data[SC_LEECHESEND] && sc->data[SC_LEECHESEND]->val3 == 0) { + switch (type) { + case SC_BLIND: + case SC_STUN: + return 0; // Immune + } + } else if (sc->data[SC_OBLIVIONCURSE] && sc->data[SC_OBLIVIONCURSE]->val3 == 0) { + switch (type) { + case SC_SILENCE: + case SC_CURSE: + return 0; // Immune + } + } } // When tick def not set, reduction is the same for both. @@ -9497,12 +9518,13 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty case SC_PYREXIA: case SC_OBLIVIONCURSE: case SC_LEECHESEND: - { // It doesn't stack or even renew - int i = SC_TOXIN; - for(; i<= SC_LEECHESEND; i++) - if(sc->data[i]) return 0; + for (int32 i = SC_TOXIN; i <= SC_LEECHESEND; i++) { + if (sc->data[i] && sc->data[i]->val3 == 1) // It doesn't stack or even renew on the target + return 0; + else if (sc->data[i] && sc->data[i]->val3 == 0) + status_change_end(bl, static_cast(i), INVALID_TIMER); // End the bonus part on the caster } - break; + break; case SC_SATURDAYNIGHTFEVER: if (sc->data[SC_BERSERK] || sc->data[SC_INSPIRATION]) return 0; @@ -10248,8 +10270,14 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty #ifndef RENEWAL val3 = 50*(val1+1); // Damage increase (+50 +50*lv%) #endif - if( sd )// [Ind] - iROwiki says each level increases its duration by 3 seconds - tick += pc_checkskill(sd,GC_RESEARCHNEWPOISON)*3000; + if (sd) { + uint16 poison_level = pc_checkskill(sd, GC_RESEARCHNEWPOISON); + + if (poison_level > 0) { + tick += 30000; // Base of 30 seconds + tick += poison_level * 15 * 1000; // Additional 15 seconds per level + } + } break; case SC_POISONREACT: val2=(val1+1)/2 + val1/10; // Number of counters [Skotlex] @@ -10522,18 +10550,46 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty case SC_POISON: case SC_BLEEDING: case SC_BURNING: - case SC_TOXIN: - case SC_MAGICMUSHROOM: - case SC_LEECHESEND: tick_time = status_get_sc_interval(type); - val4 = tick-tick_time; // Remaining time + val4 = tick - tick_time; // Remaining time break; - - case SC_PYREXIA: - //Causes blind for duration of pyrexia, unreducable and unavoidable, but can be healed with e.g. green potion - status_change_start(src,bl,SC_BLIND,10000,val1,0,0,0,tick,SCSTART_NOAVOID|SCSTART_NOTICKDEF|SCSTART_NORATEDEF); + case SC_TOXIN: + if (val3 == 1) // Target + tick_time = status_get_sc_interval(type); + else // Caster + tick_time = 1000; + val4 = tick - tick_time; // Remaining time + break; + case SC_DEATHHURT: + if (val3 == 1) + break; tick_time = status_get_sc_interval(type); - val4 = tick-tick_time; // Remaining time + val4 = tick - tick_time; // Remaining time + case SC_LEECHESEND: + if (val3 == 0) + break; + tick_time = status_get_sc_interval(type); + val4 = tick - tick_time; // Remaining time + break; + case SC_PYREXIA: + if (val3 == 1) { // Target + // Causes blind for duration of pyrexia, unreducable and unavoidable, but can be healed with e.g. green potion + status_change_start(src, bl, SC_BLIND, 10000, val1, 0, 0, 0, tick, SCSTART_NOAVOID | SCSTART_NOTICKDEF | SCSTART_NORATEDEF); + tick_time = status_get_sc_interval(type); + val4 = tick - tick_time; // Remaining time + } else // Caster + val2 = 15; // CRIT % and ATK % increase + break; + case SC_VENOMBLEED: + if (val3 == 0) // Caster + val2 = 30; // Reflect damage % reduction + break; + case SC_MAGICMUSHROOM: + if (val3 == 1) { // Target + tick_time = status_get_sc_interval(type); + val4 = tick - tick_time; // Remaining time + } else // Caster + val2 = 10; // After-cast delay % reduction break; case SC_CONFUSION: @@ -11175,6 +11231,8 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty tick_time = 5000; // [GodLesZ] tick time break; case SC_OBLIVIONCURSE: + if (val3 == 0) + break; val4 = tick / 3000; tick_time = 3000; // [GodLesZ] tick time break; @@ -11759,7 +11817,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty val2 = 20 * val1 + 20; // atk bonus break; case SC_DARKCROW: - val2 = 30 * val1; + val2 = 30 * val1; // ATK bonus break; case SC_UNLIMIT: val2 = 50 * val1; @@ -12098,11 +12156,21 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty case SC_BLEEDING: case SC_BURNING: case SC_TOXIN: + tick_time = tick; + tick = tick_time + max(val4, 0); + break; + case SC_DEATHHURT: + if (val3 == 1) + break; + tick_time = tick; + tick = tick_time + max(val4, 0); case SC_MAGICMUSHROOM: case SC_PYREXIA: case SC_LEECHESEND: + if (val3 == 0) + break; tick_time = tick; - tick = tick_time + max(val4,0); + tick = tick_time + max(val4, 0); break; case SC_SWORDCLAN: case SC_ARCWANDCLAN: @@ -13861,12 +13929,19 @@ TIMER_FUNC(status_change_timer){ status_fix_damage(bl, bl, damage, clif_damage(bl, bl, tick, 0, 1, damage, 1, DMG_NORMAL, 0, false),0); } break; - + case SC_TOXIN: if (sce->val4 >= 0) { // Damage is every 10 seconds including 3%sp drain. - map_freeblock_lock(); - dounlock = true; - status_damage(bl, bl, 1, status->max_sp * 3 / 100, clif_damage(bl, bl, tick, status->amotion, status->dmotion + 500, 1, 1, DMG_NORMAL, 0, false), 0, 0); + if (sce->val3 == 1) { // Target + map_freeblock_lock(); + dounlock = true; + status_damage(bl, bl, 1, status->max_sp * 3 / 100, clif_damage(bl, bl, tick, status->amotion, status->dmotion + 500, 1, 1, DMG_NORMAL, 0, false), 0, 0); + } else { // Caster + interval = 1000; // Assign here since status_get_sc_internval() contains the target interval. + + if (status->sp < status->max_sp) + status_heal(bl, 0, (int)status->max_sp * 1 / 100, 1); + } } break; @@ -13914,7 +13989,7 @@ TIMER_FUNC(status_change_timer){ } } break; - + case SC_PYREXIA: if (sce->val4 >= 0) { map_freeblock_lock(); @@ -13922,7 +13997,7 @@ TIMER_FUNC(status_change_timer){ status_fix_damage(bl, bl, 100, clif_damage(bl, bl, tick, status->amotion, status->dmotion + 500, 100, 1, DMG_NORMAL, 0, false),0); } break; - + case SC_LEECHESEND: if (sce->val4 >= 0) { int64 damage = status->vit * (sce->val1 - 3) + (int)status->max_hp / 100; // {Target VIT x (New Poison Research Skill Level - 3)} + (Target HP/100) @@ -13933,6 +14008,13 @@ TIMER_FUNC(status_change_timer){ } break; + case SC_DEATHHURT: + if (sce->val4 >= 0) { + if (status->hp < status->max_hp) + status_heal(bl, (int)status->max_hp * 1 / 100, 0, 1); + } + break; + case SC_TENSIONRELAX: if(status->max_hp > status->hp && --(sce->val3) >= 0) { sc_timer_next(10000 + tick); @@ -14116,7 +14198,7 @@ TIMER_FUNC(status_change_timer){ sc_timer_next(10000+tick); } break; - + case SC_OBLIVIONCURSE: if( --(sce->val4) >= 0 ) { clif_emotion(bl,ET_QUESTION); diff --git a/src/tool/csv2yaml.cpp b/src/tool/csv2yaml.cpp index 34bef42aa8..729f891678 100644 --- a/src/tool/csv2yaml.cpp +++ b/src/tool/csv2yaml.cpp @@ -89,7 +89,7 @@ std::unordered_map skill_nearnpc; // Forward declaration of conversion functions static bool guild_read_guildskill_tree_db( char* split[], int columns, int current ); static bool pet_read_db( const char* file ); -static bool skill_parse_row_magicmushroomdb(char* split[], int column, int current); +static bool skill_parse_row_magicmushroomdb(char *split[], int column, int current); static bool skill_parse_row_abradb(char* split[], int columns, int current); static bool skill_parse_row_spellbookdb(char* split[], int columns, int current); static bool mob_readdb_mobavail(char *str[], int columns, int current); @@ -312,7 +312,7 @@ int do_init( int argc, char** argv ){ return 0; } - if (!process("MAGIC_MUSHROOM_DB", 1, root_paths, "magicmushroom_db", [](const std::string& path, const std::string& name_ext) -> bool { + if (!process("MAGIC_MUSHROOM_DB", 1, root_paths, "magicmushroom_db", [](const std::string &path, const std::string &name_ext) -> bool { return sv_readdb(path.c_str(), name_ext.c_str(), ',', 1, 1, -1, &skill_parse_row_magicmushroomdb, false); })) { return 0; @@ -865,7 +865,7 @@ static bool pet_read_db( const char* file ){ } // Copied and adjusted from skill.cpp -static bool skill_parse_row_magicmushroomdb(char* split[], int column, int current) +static bool skill_parse_row_magicmushroomdb(char *split[], int column, int current) { uint16 skill_id = atoi(split[0]); std::string *skill_name = util::umap_find(aegis_skillnames, skill_id); From d1e51277284628423a237ed28ad0c7e40f619dea Mon Sep 17 00:00:00 2001 From: Daegaladh Date: Mon, 18 May 2020 18:52:08 +0200 Subject: [PATCH 025/141] Fixed getcharid error in Continental Guard quest (#4970) --- npc/quests/quests_morocc.txt | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/npc/quests/quests_morocc.txt b/npc/quests/quests_morocc.txt index 702ea24e8e..ae13d7154e 100644 --- a/npc/quests/quests_morocc.txt +++ b/npc/quests/quests_morocc.txt @@ -948,13 +948,11 @@ moc_fild20,354,183,3 script Continental Guard#01::MocConGuard 707,3,3,{ close; case 2: if ($@re_moc < 3) { - getpartymember(getcharid(1)); - set .@partymembercount,$@partymembercount; - copyarray .@partymembername$[0],$@partymembername$[0],.@partymembercount; - for(.@i = 0; .@i < .@partymembercount; .@i++) { - if (isloggedin(getcharid(3,.@partymembername$[.@i]))) { - set .@onlinemembers,.@onlinemembers + 1; - } + getpartymember getcharid(1), 1, .@partymembercid; + .@partymembercount = $@partymembercount; + for (.@i = 0; .@i < .@partymembercount; .@i++) { + if (convertpcinfo(.@partymembercid[.@i], CPC_ACCOUNT)) + .@onlinemembers++; } if ((.@onlinemembers > 1) && (countitem(7826) > 0)) { mes "[Continental Guard]"; @@ -1009,13 +1007,11 @@ moc_fild20,354,183,3 script Continental Guard#01::MocConGuard 707,3,3,{ mes "[Continental Guard]"; mes "Ah, you're an adventurer working for the Continental Guard. Nice to meet you. Feel free to ask me if you need my assistance."; next; - getpartymember(getcharid(1)); - set .@partymembercount,$@partymembercount; - copyarray .@partymembername$[0],$@partymembername$[0],.@partymembercount; - for(.@i = 0; .@i < .@partymembercount; .@i++) { - if (isloggedin(getcharid(3,.@partymembername$[.@i]))) { - set .@onlinemembers,.@onlinemembers + 1; - } + getpartymember getcharid(1), 1, .@partymembercid; + .@partymembercount = $@partymembercount; + for (.@i = 0; .@i < .@partymembercount; .@i++) { + if (convertpcinfo(.@partymembercid[.@i], CPC_ACCOUNT)) + .@onlinemembers++; } switch(select("Enter the First Field to Investigate:Enter the Second Field to Investigate:Return to Morocc's Accident Site:Cancel Conversation")) { case 1: From 44e5c5bcc7c19ab34dc284b18d4e9a40a96ebbbe Mon Sep 17 00:00:00 2001 From: Aleos Date: Mon, 18 May 2020 13:35:07 -0400 Subject: [PATCH 026/141] Job Improvement Project - Rune Knight (Part 2) (#4709) * Fixes #4708. * kRO Changelog: http://ro.gnjoy.com/news/update/View.asp?seq=243&curpage=1 Thanks to @ecdarreola, @Litro, @Haydrich, and @LordWhiplash! --- db/re/item_db.txt | 30 ++++---- db/re/item_delay.txt | 2 + db/re/item_stack.txt | 9 --- db/re/skill_db.yml | 90 +++++++++++++++++------- doc/item_bonus.txt | 3 +- src/map/battle.cpp | 82 ++++++++++++++-------- src/map/map.hpp | 2 +- src/map/pc.cpp | 9 ++- src/map/pc.hpp | 1 + src/map/script_constants.hpp | 3 + src/map/skill.cpp | 130 +++++++++-------------------------- src/map/status.cpp | 28 ++++++-- src/map/status.hpp | 3 + 13 files changed, 205 insertions(+), 187 deletions(-) diff --git a/db/re/item_db.txt b/db/re/item_db.txt index 0400afa62e..1b35e114b2 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -7044,23 +7044,23 @@ //=================================================================== // Rune Knight's Rune Stones //=================================================================== -12725,Runstone_Nosiege,Nauthiz Rune,2,100,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_REFRESH",1; },{},{} -12726,Runstone_Rhydo,Raido Rune,2,100,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_CRUSHSTRIKE",1; },{},{} -12727,Runstone_Verkana,Berkana Rune,2,100,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_MILLENNIUMSHIELD",1; },{},{} -12728,Runstone_Isia,Isa Rune,2,100,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_VITALITYACTIVATION",1; },{},{} -12729,Runstone_Asir,Othila Rune,2,100,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_FIGHTINGSPIRIT",1; },{},{} -12730,Runstone_Urj,Uruz Rune,2,100,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_ABUNDANCE",1; },{},{} -12731,Runstone_Turisus,Thurisaz Rune,2,100,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_GIANTGROWTH",1; },{},{} -12732,Runstone_Pertz,Wyrd Rune,2,100,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_STORMBLAST",1; },{},{} -12733,Runstone_Hagalas,Hagalaz Rune,2,100,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_STONEHARDSKIN",1; },{},{} +12725,Runstone_Nosiege,Nauthiz Rune,2,100,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_REFRESH",1; },{},{} +12726,Runstone_Rhydo,Raido Rune,2,100,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_CRUSHSTRIKE",1; },{},{} +12727,Runstone_Verkana,Berkana Rune,2,100,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_MILLENNIUMSHIELD",1; },{},{} +12728,Runstone_Isia,Isa Rune,2,100,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_VITALITYACTIVATION",1; },{},{} +12729,Runstone_Asir,Othila Rune,2,100,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_FIGHTINGSPIRIT",1; },{},{} +12730,Runstone_Urj,Uruz Rune,2,100,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_ABUNDANCE",1; },{},{} +12731,Runstone_Turisus,Thurisaz Rune,2,100,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_GIANTGROWTH",1; },{},{} +12732,Runstone_Pertz,Wyrd Rune,2,100,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_STORMBLAST",1; },{},{} +12733,Runstone_Hagalas,Hagalaz Rune,2,100,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_STONEHARDSKIN",1; },{},{} //=================================================================== // Rune Knight Rune Ores //=================================================================== -12734,Runstone_Quality,Luxurious Rune,0,2,,100,,,,,0x00000080,56,2,,,,,,{ makerune 5; },{},{} -12735,Runstone_Ancient,Ancient Rune,0,2,,100,,,,,0x00000080,56,2,,,,,,{ makerune 11; },{},{} -12736,Runstone_Mystic,Mystic Rune,0,2,,100,,,,,0x00000080,56,2,,,,,,{ makerune 14; },{},{} -12737,Runstone_Ordinary,General Rune,0,2,,100,,,,,0x00000080,56,2,,,,,,{ makerune 2; },{},{} -12738,Runstone_Rare,Rare Rune,0,2,,100,,,,,0x00000080,56,2,,,,,,{ makerune 8; },{},{} +12734,Runstone_Quality,Luxurious Rune,0,2,,100,,,,,0x00000080,56,2,,,,,,{ makerune 8; },{},{} +12735,Runstone_Ancient,Ancient Rune,0,2,,100,,,,,0x00000080,56,2,,,,,,{ makerune 30; },{},{} +12736,Runstone_Mystic,Mystic Rune,0,2,,100,,,,,0x00000080,56,2,,,,,,{ makerune 60; },{},{} +12737,Runstone_Ordinary,General Rune,0,2,,100,,,,,0x00000080,56,2,,,,,,{ makerune 4; },{},{} +12738,Runstone_Rare,Rare Rune,0,2,,100,,,,,0x00000080,56,2,,,,,,{ makerune 15; },{},{} //=================================================================== // More usable items //=================================================================== @@ -11371,7 +11371,7 @@ 22536,WorkerScroll_B,Scroll Summoning Workers(Female),2,10,,10,,,,0,0xFFFFFFFF,63,2,,,,,,{},{},{} 22537,PrizeOfHero,Prize Of Hero,2,0,,100,,,,0,0xFFFFFFFF,63,2,,,1,,,{ getrandgroupitem(IG_PrizeOfHero,1); },{},{} 22538,Hanbok_bag,Hanbok bag,2,0,,10,,,,0,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_HANBOK,600000,0; },{},{} -22540,Runstone_Lux,Lux Anima Runestone,11,2,,100,,,,,0xFFFFFFFF,56,2,,,,,,{ itemskill "RK_LUXANIMA",1; },{},{} +22540,Runstone_Lux,Lux Anima Runestone,11,2,,10,,,,,0xFFFFFFFF,56,2,,,,,,{ itemskill "RK_LUXANIMA",1; },{},{} 22541,PC_Room_Coupon_Box_VI,PC Room Coupon Box VI,18,10,,10,,,,0,0xFFFFFFFF,63,2,,,,,,{},{},{} 22542,Center_Potion_B,Concentration Potion,2,10,,100,,,,0,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_ASPDPOTION0,1800000,4; },{},{} 22543,Berserk_Potion_B,Berserk Potion,2,10,,200,,,,0,0x6,63,2,,,,,,{ sc_start SC_ASPDPOTION2,1800000,9; },{},{} diff --git a/db/re/item_delay.txt b/db/re/item_delay.txt index 11d0ed0063..01fb16edc4 100644 --- a/db/re/item_delay.txt +++ b/db/re/item_delay.txt @@ -99,6 +99,8 @@ //12207,60000 //Vit_Dish10_ 22508,1200000 //Para_Team_Mark_ +22540,5000,SC_REUSE_LIMIT_LUXANIMA //Runstone_Lux + // Bed of Honor 22687,5000,SC_REUSE_LIMIT_F // Pieces_Of_Sentiment diff --git a/db/re/item_stack.txt b/db/re/item_stack.txt index 877bcb1454..e61f69b0cd 100644 --- a/db/re/item_stack.txt +++ b/db/re/item_stack.txt @@ -17,14 +17,5 @@ // Example: // 512,4,12 // Will not allow more than 4 Apples in storages. -// Rune Knight -12725,60,1 // Nauthiz Rune -12726,60,1 // Raido Rune -12727,60,1 // Berkana Rune -12728,60,1 // Isa Rune -12730,60,1 // Uruz Rune -12733,60,1 // Hagalaz Rune -22540,60,1 // Lux Anima Rune - // Arch Bishop 12333,3,1 // Ancilla diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index 778145acee..d326054ac0 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -17392,6 +17392,8 @@ Body: MaxLevel: 10 Type: Weapon TargetType: Attack + DamageFlags: + Critical: true Range: - Level: 1 Size: 7 @@ -17488,10 +17490,33 @@ Body: MaxLevel: 10 Type: Weapon TargetType: Attack - Range: 5 + DamageFlags: + Splash: true + Range: 7 Hit: Multi_Hit HitCount: -5 Element: Weapon + SplashArea: + - Level: 1 + Area: 1 + - Level: 2 + Area: 1 + - Level: 3 + Area: 1 + - Level: 4 + Area: 1 + - Level: 5 + Area: 2 + - Level: 6 + Area: 2 + - Level: 7 + Area: 2 + - Level: 8 + Area: 2 + - Level: 9 + Area: 3 + - Level: 10 + Area: 3 CastTime: - Level: 1 Time: 1000 @@ -17526,33 +17551,50 @@ Body: Description: Wind Cutter MaxLevel: 5 Type: Weapon - TargetType: Ground + TargetType: Self DamageFlags: Splash: true - Range: 1 Hit: Single HitCount: 1 - Element: Wind - SplashArea: 2 - Knockback: 3 + Element: Weapon + SplashArea: + - Level: 1 + Area: 1 + - Level: 2 + Area: 1 + - Level: 3 + Area: 2 + - Level: 4 + Area: 2 + - Level: 5 + Area: 3 CopyFlags: Skill: Reproduce: true - Duration1: 15000 - Cooldown: 2000 - FixedCastTime: -1 + AfterCastActDelay: 1000 + Cooldown: + - Level: 1 + Time: 800 + - Level: 2 + Time: 650 + - Level: 3 + Time: 500 + - Level: 4 + Time: 350 + - Level: 5 + Time: 200 Requires: SpCost: - Level: 1 - Amount: 20 + Amount: 34 - Level: 2 - Amount: 24 + Amount: 38 - Level: 3 - Amount: 28 + Amount: 42 - Level: 4 - Amount: 32 + Amount: 46 - Level: 5 - Amount: 36 + Amount: 50 Weapon: Dagger: true 1hSword: true @@ -17567,6 +17609,7 @@ Body: TargetType: Self DamageFlags: Splash: true + Critical: true Hit: Single HitCount: 1 Element: Weapon @@ -17717,7 +17760,7 @@ Body: Hit: Single HitCount: 1 AfterCastActDelay: 1000 - Duration1: 180000 + Duration1: 900000 Duration2: 60000 FixedCastTime: -1 Requires: @@ -17770,7 +17813,7 @@ Body: Hit: Single HitCount: 1 CastCancel: true - Duration1: 180000 + Duration1: 900000 FixedCastTime: 1000 CastTimeFlags: IgnoreDex: true @@ -17788,7 +17831,7 @@ Body: Hit: Single HitCount: 1 CastCancel: true - Duration1: 180000 + Duration1: 900000 Duration2: 10000 FixedCastTime: 2000 CastTimeFlags: @@ -17807,7 +17850,7 @@ Body: Hit: Single HitCount: 1 CastCancel: true - Duration1: 180000 + Duration1: 900000 FixedCastTime: -1 Requires: SpCost: 1 @@ -17819,13 +17862,13 @@ Body: TargetType: Self DamageFlags: Splash: true + Critical: true Flags: TargetTrap: true Hit: Single HitCount: 1 Element: Weapon SplashArea: 3 - Knockback: 7 CastTime: 2000 FixedCastTime: -1 Requires: @@ -17840,9 +17883,8 @@ Body: Splash: true Hit: Single HitCount: 1 - SplashArea: -1 CastCancel: true - Duration1: 180000 + Duration1: 900000 FixedCastTime: -1 Requires: SpCost: 1 @@ -17858,7 +17900,7 @@ Body: HitCount: 1 Element: Holy CastCancel: true - Duration1: 180000 + Duration1: 900000 FixedCastTime: -1 Requires: SpCost: 1 @@ -31739,11 +31781,9 @@ Body: TargetType: Self DamageFlags: NoDamage: true - Splash: true Hit: Single HitCount: 1 - SplashArea: 3 - Duration1: 60000 + Duration1: 900000 FixedCastTime: -1 Requires: SpCost: 1 diff --git a/doc/item_bonus.txt b/doc/item_bonus.txt index 36f10e9e64..7428fd900d 100644 --- a/doc/item_bonus.txt +++ b/doc/item_bonus.txt @@ -168,7 +168,8 @@ bonus2 bSkillUseSPrate,sk,n; Decreases SP consumption of skill sk by n% Atk/Def ------- bonus2 bSkillAtk,sk,n; Increases damage of skill sk by n% -bonus bLongAtkRate,n; Increases damage of ranged attacks by n% +bonus bShortAtkRate,n; Increases damage of short ranged attacks by n% +bonus bLongAtkRate,n; Increases damage of long ranged attacks by n% bonus bCritAtkRate,n; Increases critical damage by +n% bonus bCriticalDef,n; Decreases the chance of being hit by critical hits by n% bonus2 bWeaponAtk,w,n; Adds n ATK when weapon of type w is equipped diff --git a/src/map/battle.cpp b/src/map/battle.cpp index b9e3a0e25b..044f665c95 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -852,6 +852,8 @@ int battle_calc_cardfix(int attack_type, struct block_list *src, struct block_li } } #ifndef RENEWAL + if (flag & BF_SHORT) + cardfix = cardfix * (100 + sd->bonus.short_attack_atk_rate) / 100; if( flag&BF_LONG ) cardfix = cardfix * (100 + sd->bonus.long_attack_atk_rate) / 100; #endif @@ -2575,6 +2577,7 @@ static bool is_attack_critical(struct Damage* wd, struct block_list *src, struct #ifdef RENEWAL case ASC_BREAKER: #endif + case RK_IGNITIONBREAK: case GC_CROSSIMPACT: cri /= 2; break; @@ -2865,7 +2868,8 @@ static bool attack_ignores_def(struct Damage* wd, struct block_list *src, struct } else if (weapon_position == EQI_HAND_L) return true; } - } + } else if (skill_id == RK_WINDCUTTER && sd && sd->status.weapon == W_2HSWORD) + return true; return nk[NK_IGNOREDEFENSE] != 0; } @@ -2957,6 +2961,22 @@ static int battle_get_weapon_element(struct Damage* wd, struct block_list *src, if (!sd) element = ELE_NEUTRAL; //forced neutral for monsters break; + case RK_DRAGONBREATH: + if (sc) { + if (sc->data[SC_LUXANIMA]) // Lux Anima has priority over Giant Growth + element = ELE_DARK; + else if (sc->data[SC_GIANTGROWTH]) + element = ELE_HOLY; + } + break; + case RK_DRAGONBREATH_WATER: + if (sc) { + if (sc->data[SC_LUXANIMA]) // Lux Anima has priority over Fighting Spirit + element = ELE_NEUTRAL; + else if (sc->data[SC_FIGHTINGSPIRIT]) + element = ELE_GHOST; + } + break; case LG_HESPERUSLIT: if (sc && sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 4) element = ELE_HOLY; @@ -3541,6 +3561,10 @@ static void battle_calc_multi_attack(struct Damage* wd, struct block_list *src,s } switch (skill_id) { + case RK_WINDCUTTER: + if (sd && sd->weapontype1 == W_2HSWORD) + wd->div_ = 2; + break; case RA_AIMEDBOLT: wd->div_ = 2 + tstatus->size + rnd()%2; break; @@ -4098,42 +4122,34 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list * skillratio += ((skill_lv - 1) % 5 + 1) * 100; break; case RK_SONICWAVE: - skillratio += -100 + (skill_lv + 7) * 100; // ATK = {((Skill Level + 7) x 100) x (1 + [(Caster's Base Level - 100) / 200])} % - skillratio = skillratio * (100 + (status_get_lv(src) - 100) / 2) / 100; + skillratio += -100 + 500 + 100 * skill_lv; + RE_LVL_DMOD(100); break; case RK_HUNDREDSPEAR: - skillratio += 500 + (80 * skill_lv); - if (sd) { - short index = sd->equip_index[EQI_HAND_R]; - - if (index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON) - skillratio += max(10000 - sd->inventory_data[index]->weight, 0) / 10; + skillratio += -100 + 600 + 200 * skill_lv; + if (sd) skillratio += 50 * pc_checkskill(sd,LK_SPIRALPIERCE); - } // (1 + [(Casters Base Level - 100) / 200]) - skillratio = skillratio * (100 + (status_get_lv(src) - 100) / 2) / 100; + RE_LVL_DMOD(100); break; case RK_WINDCUTTER: - skillratio += -100 + (skill_lv + 2) * 50; + if (sd) { + if (sd->weapontype1 == W_2HSWORD) + skillratio += -100 + 250 * skill_lv; + else if (sd->weapontype1 == W_1HSPEAR || sd->weapontype1 == W_2HSPEAR) + skillratio += -100 + 400 * skill_lv; + else + skillratio += -100 + 300 * skill_lv; + } else + skillratio += -100 + 300 * skill_lv; RE_LVL_DMOD(100); break; case RK_IGNITIONBREAK: - // 3x3 cell Damage = ATK [{(Skill Level x 300) x (1 + [(Caster's Base Level - 100) / 100])}] % - // 7x7 cell Damage = ATK [{(Skill Level x 250) x (1 + [(Caster's Base Level - 100) / 100])}] % - // 11x11 cell Damage = ATK [{(Skill Level x 200) x (1 + [(Caster's Base Level - 100) / 100])}] % - i = distance_bl(src,target); - if (i < 2) - skillratio += -100 + 300 * skill_lv; - else if (i < 4) - skillratio += -100 + 250 * skill_lv; - else - skillratio += -100 + 200 * skill_lv; - skillratio = skillratio * status_get_lv(src) / 100; - // Elemental check, 1.5x damage if your weapon element is fire. - if (sstatus->rhw.ele == ELE_FIRE) - skillratio += 100 * skill_lv; + skillratio += -100 + 400 * skill_lv; + RE_LVL_DMOD(100); break; case RK_STORMBLAST: skillratio += -100 + (((sd) ? pc_checkskill(sd,RK_RUNEMASTERY) : 0) + status_get_str(src) / 8) * 100; // ATK = [{Rune Mastery Skill Level + (Caster's STR / 8)} x 100] % + RE_LVL_DMOD(100); break; case RK_PHANTOMTHRUST: // ATK = [{(Skill Level x 50) + (Spear Master Level x 10)} x Caster's Base Level / 150] % skillratio += -100 + 50 * skill_lv + 10 * (sd ? pc_checkskill(sd,KN_SPEARMASTERY) : 5); @@ -5570,6 +5586,9 @@ static struct Damage initialize_weapon_data(struct block_list *src, struct block case LK_SPIRALPIERCE: if (!sd) wd.flag = (wd.flag&~(BF_RANGEMASK|BF_WEAPONMASK))|BF_LONG|BF_MISC; break; + case RK_WINDCUTTER: + if (sd && (sd->status.weapon == W_1HSPEAR || sd->status.weapon == W_2HSPEAR)) + wd.flag |= BF_LONG; // The number of hits is set to 3 by default for use in Inspiration status. // When in Banding, the number of hits is equal to the number of Royal Guards in Banding. @@ -5615,7 +5634,10 @@ void battle_do_reflect(int attack_type, struct Damage *wd, struct block_list* sr rdamage = battle_calc_return_damage(target, src, &damage, wd->flag, skill_id,true); if( rdamage > 0 ) { struct block_list *d_bl = battle_check_devotion(src); + status_change *sc = status_get_sc(src); + if (sc && sc->data[SC_VITALITYACTIVATION]) + rdamage /= 2; if (tsc->data[SC_MAXPAIN]) { tsc->data[SC_MAXPAIN]->val2 = (int)rdamage; skill_castend_damage_id(target, src, NPC_MAXPAIN_ATK, tsc->data[SC_MAXPAIN]->val1, tick, wd->flag); @@ -5751,6 +5773,8 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl if (sd) { //monsters, homuns and pets have their damage computed directly wd.damage = wd.statusAtk + wd.weaponAtk + wd.equipAtk + wd.masteryAtk; wd.damage2 = wd.statusAtk2 + wd.weaponAtk2 + wd.equipAtk2 + wd.masteryAtk2; + if (wd.flag & BF_SHORT) + ATK_ADDRATE(wd.damage, wd.damage2, sd->bonus.short_attack_atk_rate); if(wd.flag&BF_LONG && (skill_id != RA_WUGBITE && skill_id != RA_WUGSTRIKE)) //Long damage rate addition doesn't use weapon + equip attack ATK_ADDRATE(wd.damage, wd.damage2, sd->bonus.long_attack_atk_rate); } @@ -7691,10 +7715,8 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t } else status_change_end(src,SC_SPELLFIST,INVALID_TIMER); } - if (sc->data[SC_GIANTGROWTH] && (wd.flag&BF_SHORT) && rnd()%100 < sc->data[SC_GIANTGROWTH]->val2 && !is_infinite_defense(target, wd.flag) && !vellum_damage) { - wd.damage <<= 1; // Double Damage - skill_break_equip(src, src, EQP_WEAPON, 10, BCT_SELF); // Break chance happens on successful damage increase - } + if (sc->data[SC_GIANTGROWTH] && (wd.flag&BF_SHORT) && rnd()%100 < sc->data[SC_GIANTGROWTH]->val2 && !is_infinite_defense(target, wd.flag) && !vellum_damage) + wd.damage += wd.damage * 150 / 100; // 2.5 times damage if( sd && battle_config.arrow_decrement && sc->data[SC_FEARBREEZE] && sc->data[SC_FEARBREEZE]->val4 > 0) { short idx = sd->equip_index[EQI_AMMO]; diff --git a/src/map/map.hpp b/src/map/map.hpp index 7fa4a8b598..1026c0fc33 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -498,7 +498,7 @@ enum _sp { SP_STATE_NORECOVER_RACE, SP_CRITICAL_RANGEATK, SP_MAGIC_ADDRACE2, SP_IGNORE_MDEF_RACE2_RATE, // 2079-2082 SP_WEAPON_ATK_RATE, SP_WEAPON_MATK_RATE, SP_DROP_ADDRACE, SP_DROP_ADDCLASS, SP_NO_MADO_FUEL, // 2083-2087 SP_IGNORE_DEF_CLASS_RATE, SP_REGEN_PERCENT_HP, SP_REGEN_PERCENT_SP, SP_SKILL_DELAY, SP_NO_WALK_DELAY, //2088-2093 - SP_LONG_SP_GAIN_VALUE, SP_LONG_HP_GAIN_VALUE // 2094-2095 + SP_LONG_SP_GAIN_VALUE, SP_LONG_HP_GAIN_VALUE, SP_SHORT_ATK_RATE // 2094-2096 }; enum _look { diff --git a/src/map/pc.cpp b/src/map/pc.cpp index dfc661fb0e..6f25667997 100755 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -3416,6 +3416,10 @@ void pc_bonus(struct map_session_data *sd,int type,int val) if(sd->state.lr_flag !=2) sd->bonus.classchange=val; break; + case SP_SHORT_ATK_RATE: + if(sd->state.lr_flag != 2) //[Lupus] it should stack, too. As any other cards rate bonuses + sd->bonus.short_attack_atk_rate+=val; + break; case SP_LONG_ATK_RATE: if(sd->state.lr_flag != 2) //[Lupus] it should stack, too. As any other cards rate bonuses sd->bonus.long_attack_atk_rate+=val; @@ -8634,6 +8638,7 @@ int64 pc_readparam(struct map_session_data* sd,int64 type) case SP_UNBREAKABLE_GARMENT: val = (sd->bonus.unbreakable_equip&EQP_GARMENT)?1:0; break; case SP_UNBREAKABLE_SHOES: val = (sd->bonus.unbreakable_equip&EQP_SHOES)?1:0; break; case SP_CLASSCHANGE: val = sd->bonus.classchange; break; + case SP_SHORT_ATK_RATE: val = sd->bonus.short_attack_atk_rate; break; case SP_LONG_ATK_RATE: val = sd->bonus.long_attack_atk_rate; break; case SP_BREAK_WEAPON_RATE: val = sd->bonus.break_weapon_rate; break; case SP_BREAK_ARMOR_RATE: val = sd->bonus.break_armor_rate; break; @@ -8983,10 +8988,8 @@ int pc_itemheal(struct map_session_data *sd, int itemid, int hp, int sp) if (sd->sc.data[SC_NORECOVER_STATE]) penalty = 100; - if (sd->sc.data[SC_VITALITYACTIVATION]) { + if (sd->sc.data[SC_VITALITYACTIVATION]) hp += hp / 2; // 1.5 times - sp -= sp / 2; - } if (sd->sc.data[SC_WATER_INSIGNIA] && sd->sc.data[SC_WATER_INSIGNIA]->val1 == 2) { hp += hp / 10; diff --git a/src/map/pc.hpp b/src/map/pc.hpp index bf135dbb1a..148f640244 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -486,6 +486,7 @@ struct map_session_data { int arrow_atk,arrow_ele,arrow_cri,arrow_hit; int nsshealhp,nsshealsp; int critical_def,double_rate; + int short_attack_atk_rate; // Short range atk rate, not weapon based. int long_attack_atk_rate; //Long range atk rate, not weapon based. [Skotlex] int near_attack_def_rate,long_attack_def_rate,magic_def_rate,misc_def_rate; int ignore_mdef_ele; diff --git a/src/map/script_constants.hpp b/src/map/script_constants.hpp index 77092b5f6a..83fb621c11 100644 --- a/src/map/script_constants.hpp +++ b/src/map/script_constants.hpp @@ -671,6 +671,7 @@ export_constant2("bUnbreakableArmor",SP_UNBREAKABLE_ARMOR); export_constant2("bUnbreakableHelm",SP_UNBREAKABLE_HELM); export_constant2("bUnbreakableShield",SP_UNBREAKABLE_SHIELD); + export_constant2("bShortAtkRate",SP_SHORT_ATK_RATE); export_constant2("bLongAtkRate",SP_LONG_ATK_RATE); export_constant2("bCritAtkRate",SP_CRIT_ATK_RATE); export_constant2("bCriticalAddRace",SP_CRITICAL_ADDRACE); @@ -1583,6 +1584,8 @@ export_constant(SC_ADD_ATK_DAMAGE); export_constant(SC_ADD_MATK_DAMAGE); export_constant(SC_SOUNDOFDESTRUCTION); + export_constant(SC_LUXANIMA); + export_constant(SC_REUSE_LIMIT_LUXANIMA); #ifdef RENEWAL export_constant(SC_EXTREMITYFIST2); #endif diff --git a/src/map/skill.cpp b/src/map/skill.cpp index b369fc5e67..695c9dfb4c 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -1343,6 +1343,9 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 if((sce=sc->data[SC_EDP])) sc_start4(src,bl,SC_DPOISON,sce->val2, sce->val1,src->id,0,0, skill_get_time2(ASC_EDP,sce->val1)); + + if ((sce = sc->data[SC_LUXANIMA]) && rnd() % 100 < sce->val2) + skill_castend_nodamage_id(src, bl, RK_STORMBLAST, 1, tick, 0); } } break; @@ -1692,9 +1695,6 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 case NPC_CRITICALWOUND: sc_start(src,bl,SC_CRITICALWOUND,100,skill_lv,skill_get_time2(skill_id,skill_lv)); break; - case RK_WINDCUTTER: - sc_start(src,bl,SC_FEAR,3+2*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv)); - break; case RK_DRAGONBREATH: sc_start4(src,bl,SC_BURNING,15,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv)); break; @@ -2033,6 +2033,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 case SC_DRESSUP: case SC_HANBOK: case SC_OKTOBERFEST: case SC_LHZ_DUN_N1: case SC_LHZ_DUN_N2: case SC_LHZ_DUN_N3: case SC_LHZ_DUN_N4: case SC_ENTRY_QUEUE_APPLY_DELAY: case SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT: + case SC_REUSE_LIMIT_LUXANIMA: continue; case SC_WHISTLE: case SC_ASSNCROS: case SC_POEMBRAGI: case SC_APPLEIDUN: case SC_HUMMING: case SC_DONTFORGETME: @@ -4862,7 +4863,6 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint break; case MO_TRIPLEATTACK: - case RK_WINDCUTTER: skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag|SD_ANIMATION); break; @@ -5112,6 +5112,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint case NPC_REVERBERATION_ATK: case NPC_ARROWSTORM: case RK_IGNITIONBREAK: + case RK_HUNDREDSPEAR: case AB_JUDEX: case AB_ADORAMUS: case WL_SOULEXPANSION: @@ -5560,8 +5561,9 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint case RK_DRAGONBREATH: if( tsc && tsc->data[SC_HIDING] ) clif_skill_nodamage(src,src,skill_id,skill_lv,1); - else + else { skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); + } break; case NPC_SELFDESTRUCTION: @@ -5619,16 +5621,6 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint status_change_end(src, SC_HIDING, INVALID_TIMER); skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); break; - case RK_HUNDREDSPEAR: - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - if(rnd()%100 < (10 + 3*skill_lv)) { - int skill_req = ((sd) ? pc_checkskill(sd,KN_SPEARBOOMERANG) : skill_get_max(KN_SPEARBOOMERANG)); - if( !skill_req ) - break; // Spear Boomerang auto cast chance only works if you have Spear Boomerang. - skill_blown(src,bl,6,-1,BLOWN_NONE); - skill_castend_damage_id(src,bl,KN_SPEARBOOMERANG,skill_req,tick,0); - } - break; case RK_PHANTOMTHRUST: unit_setdir(src,map_calc_dir(src, bl->x, bl->y)); clif_skill_nodamage(src,bl,skill_id,skill_lv,1); @@ -5637,6 +5629,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint if( battle_check_target(src,bl,BCT_ENEMY) > 0 ) skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); break; + case RK_WINDCUTTER: case RK_STORMBLAST: if( flag&1 ) skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); @@ -7516,6 +7509,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui //List of self skills that give damage around caster case ASC_METEORASSAULT: case GS_SPREADATTACK: + case RK_WINDCUTTER: case RK_STORMBLAST: case NC_AXETORNADO: case GC_COUNTERSLASH: @@ -7560,7 +7554,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui clif_skill_nodamage(src,bl,skill_id,skill_lv,1); i = map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), starget, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); - if( !i && ( skill_id == NC_AXETORNADO || skill_id == SR_SKYNETBLOW || skill_id == KO_HAPPOKUNAI ) ) + if( !i && ( skill_id == RK_WINDCUTTER || skill_id == NC_AXETORNADO || skill_id == SR_SKYNETBLOW || skill_id == KO_HAPPOKUNAI ) ) clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, DMG_SINGLE); } break; @@ -8498,6 +8492,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui case SC_WEDDING: case SC_XMAS: case SC_SUMMER: case SC_DRESSUP: case SC_HANBOK: case SC_OKTOBERFEST: case SC_LHZ_DUN_N1: case SC_LHZ_DUN_N2: case SC_LHZ_DUN_N3: case SC_LHZ_DUN_N4: + case SC_REUSE_LIMIT_LUXANIMA: continue; case SC_WHISTLE: case SC_ASSNCROS: @@ -9632,7 +9627,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui break; case RK_ENCHANTBLADE: clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start2(src,bl,type,100,skill_lv,((100+20*skill_lv)*status_get_lv(src))/150+sstatus->int_,skill_get_time(skill_id,skill_lv))); + sc_start2(src,bl,type,100,skill_lv,((100+20*skill_lv)*status_get_lv(src))/100+sstatus->int_,skill_get_time(skill_id,skill_lv))); break; case RK_DRAGONHOWLING: if( flag&1) @@ -9657,6 +9652,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui map_foreachinrange(skill_area_sub, bl,i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); } break; + case RK_LUXANIMA: + status_change_clear_buffs(bl, SCCB_LUXANIMA); // For bonus_script case RK_GIANTGROWTH: case RK_STONEHARDSKIN: case RK_VITALITYACTIVATION: @@ -9690,57 +9687,14 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui break; case RK_FIGHTINGSPIRIT: { - // val1: ATKBonus: Caster: 70 + 7 * PartyMember. Member: (70 + 7 * PartyMember) / 2 + uint8 runemastery_skill_lv = (sd ? pc_checkskill(sd, RK_RUNEMASTERY) : skill_get_max(RK_RUNEMASTERY)); + + // val1: ATKBonus: ? // !TODO: Confirm new ATK formula // val2: ASPD boost: [RK_RUNEMASTERYlevel * 4 / 10] * 10 ==> RK_RUNEMASTERYlevel * 4 - if( flag&1 ) { - if( skill_area_temp[1] == bl->id ) - sc_start2(src,bl,type,100,70 + 7 * skill_area_temp[0],4 * ((sd) ? pc_checkskill(sd,RK_RUNEMASTERY) : skill_get_max(RK_RUNEMASTERY)),skill_area_temp[4]); - else - sc_start(src,bl,type,100,skill_area_temp[3],skill_area_temp[4]); - } else { - if( sd && sd->status.party_id ) { - skill_area_temp[0] = party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,BCT_PARTY,skill_area_sub_count); - skill_area_temp[1] = src->id; - skill_area_temp[3] = (70 + 7 * skill_area_temp[0]) / 2; - skill_area_temp[4] = skill_get_time(skill_id,skill_lv); - party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id); - } - else - sc_start2(src,bl,type,100,77,4 * ((sd) ? pc_checkskill(sd,RK_RUNEMASTERY) : skill_get_max(RK_RUNEMASTERY)),skill_get_time(skill_id,skill_lv)); - clif_skill_nodamage(src,bl,skill_id,1,1); - } + sc_start2(src,bl,type,100,70 + 7 * runemastery_skill_lv,4 * runemastery_skill_lv,skill_get_time(skill_id,skill_lv)); + clif_skill_nodamage(src,bl,skill_id,skill_lv,1); + break; } - break; - - case RK_LUXANIMA: - { - sc_type runes[] = { SC_MILLENNIUMSHIELD, SC_REFRESH, SC_GIANTGROWTH, SC_STONEHARDSKIN, SC_VITALITYACTIVATION, SC_ABUNDANCE }; - - if (sd == NULL || sd->status.party_id == 0 || flag&1) { - if (src->id == bl->id) // Don't give it back to the RK - break; - - sc_start(src, bl, runes[skill_area_temp[5]], 100, skill_lv, skill_get_time(skill_id, skill_lv)); - status_change_clear_buffs(bl, SCCB_LUXANIMA); // For bonus_script - } else if (sd) { // Find which SC is going to be given - int recent = 0, result = -1; - - for (i = 0; i < ARRAYLENGTH(runes); i++) { - if (sd->sc.data[runes[i]] && ((sd->sc.data[runes[i]]->timer * (runes[i] == SC_REFRESH? 3 : 1)) > recent || recent == 0)) { - recent = sd->sc.data[runes[i]]->timer; - result = i; - } - } - - if (result != -1) { - skill_area_temp[5] = result; - status_change_end(src, runes[result], INVALID_TIMER); - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - clif_skill_nodamage(src, src, skill_id, skill_lv, 1); - } - } - } - break; case GC_ROLLINGCUTTER: { @@ -10028,6 +9982,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui case SC_DRESSUP: case SC_HANBOK: case SC_OKTOBERFEST: case SC_LHZ_DUN_N1: case SC_LHZ_DUN_N2: case SC_LHZ_DUN_N3: case SC_LHZ_DUN_N4: case SC_ENTRY_QUEUE_APPLY_DELAY: case SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT: + case SC_REUSE_LIMIT_LUXANIMA: continue; case SC_ASSUMPTIO: if( bl->type == BL_MOB ) @@ -12722,11 +12677,8 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui } break; - case RK_WINDCUTTER: - clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, DMG_SINGLE); case AC_SHOWER: - if (skill_id == AC_SHOWER) - status_change_end(src, SC_CAMOUFLAGE, INVALID_TIMER); + status_change_end(src, SC_CAMOUFLAGE, INVALID_TIMER); case MA_SHOWER: case NC_COLDSLOWER: case RK_DRAGONBREATH: @@ -19821,7 +19773,6 @@ bool skill_produce_mix(struct map_session_data *sd, uint16 skill_id, unsigned sh int i, sc, ele, idx, equip, wlv, make_per = 0, flag = 0, skill_lv = 0; int num = -1; // exclude the recipe struct status_data *status; - struct item_data* data; nullpo_ret(sd); @@ -19870,32 +19821,6 @@ bool skill_produce_mix(struct map_session_data *sd, uint16 skill_id, unsigned sh } } - if (skill_id == RK_RUNEMASTERY) { - int temp_qty, runemastery_skill_lv = pc_checkskill(sd,skill_id); - data = itemdb_search(nameid); - - if (runemastery_skill_lv >= 10) temp_qty = 1 + rnd()%3; - else if (runemastery_skill_lv > 4) temp_qty = 1 + rnd()%2; - else temp_qty = 1; - - if (data->stack.inventory) { - for (i = 0; i < MAX_INVENTORY; i++) { - if (sd->inventory.u.items_inventory[i].nameid == nameid) { - if (sd->inventory.u.items_inventory[i].amount >= data->stack.amount) { - clif_msg(sd,RUNE_CANT_CREATE); - return 0; - } else { - // The amount fits, say we got temp_qty 4 and 19 runes, we trim temp_qty to 1. - if (temp_qty + sd->inventory.u.items_inventory[i].amount >= data->stack.amount) - temp_qty = data->stack.amount - sd->inventory.u.items_inventory[i].amount; - } - break; - } - } - } - qty = temp_qty; - } - for (i = 0; i < MAX_PRODUCE_RESOURCE; i++) { short id, x, j; @@ -20004,7 +19929,7 @@ bool skill_produce_mix(struct map_session_data *sd, uint16 skill_id, unsigned sh break; case RK_RUNEMASTERY: { - int A = 100 * (51 + 2 * pc_checkskill(sd, skill_id)); + int A = 100 * (30 + 2 * pc_checkskill(sd, skill_id)); int B = 100 * status->dex / 30 + 10 * (status->luk + sd->status.job_level); int C = 100 * cap_value(sd->itemid,0,100); //itemid depend on makerune() int D = 0; @@ -20032,6 +19957,15 @@ bool skill_produce_mix(struct map_session_data *sd, uint16 skill_id, unsigned sh break; //not specified =-15% } make_per = A + B + C + D; + + uint8 runemastery_skill_lv = pc_checkskill(sd,skill_id); + + if (runemastery_skill_lv > 9) + qty = 2 + rnd() % 5; // 2~6 + else if (runemastery_skill_lv > 4) + qty = 2 + rnd() % 3; // 2~4 + else + qty = 2; } break; diff --git a/src/map/status.cpp b/src/map/status.cpp index c491497fe7..7cd2e1f138 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -814,11 +814,12 @@ void initChangeTables(void) set_sc( RK_REFRESH , SC_REFRESH , EFST_REFRESH , SCB_NONE ); set_sc( RK_GIANTGROWTH , SC_GIANTGROWTH , EFST_GIANTGROWTH , SCB_STR ); set_sc( RK_STONEHARDSKIN , SC_STONEHARDSKIN , EFST_STONEHARDSKIN , SCB_DEF|SCB_MDEF ); - set_sc( RK_VITALITYACTIVATION , SC_VITALITYACTIVATION , EFST_VITALITYACTIVATION , SCB_REGEN ); + set_sc( RK_VITALITYACTIVATION , SC_VITALITYACTIVATION , EFST_VITALITYACTIVATION , SCB_NONE ); set_sc( RK_FIGHTINGSPIRIT , SC_FIGHTINGSPIRIT , EFST_FIGHTINGSPIRIT , SCB_WATK|SCB_ASPD ); set_sc( RK_ABUNDANCE , SC_ABUNDANCE , EFST_ABUNDANCE , SCB_NONE ); set_sc( RK_CRUSHSTRIKE , SC_CRUSHSTRIKE , EFST_CRUSHSTRIKE , SCB_NONE ); set_sc_with_vfx( RK_DRAGONBREATH_WATER , SC_FREEZING , EFST_FROSTMISTY , SCB_ASPD|SCB_SPEED|SCB_DEF ); + set_sc( RK_LUXANIMA , SC_LUXANIMA , EFST_LUXANIMA , SCB_ALL ); /* GC Guillotine Cross */ set_sc_with_vfx( GC_VENOMIMPRESS, SC_VENOMIMPRESS , EFST_VENOMIMPRESS , SCB_NONE ); @@ -3413,6 +3414,8 @@ static int status_get_hpbonus(struct block_list *bl, enum e_status_bonus type) { #endif if(sc->data[SC_LUNARSTANCE]) bonus += sc->data[SC_LUNARSTANCE]->val2; + if (sc->data[SC_LUXANIMA]) + bonus += sc->data[SC_LUXANIMA]->val3; //Decreasing if (sc->data[SC_VENOMBLEED] && sc->data[SC_VENOMBLEED]->val3 == 1) @@ -3554,6 +3557,8 @@ static int status_get_spbonus(struct block_list *bl, enum e_status_bonus type) { bonus += sc->data[SC_SERVICE4U]->val2; if(sc->data[SC_MERC_SPUP]) bonus += sc->data[SC_MERC_SPUP]->val2; + if (sc->data[SC_LUXANIMA]) + bonus += sc->data[SC_LUXANIMA]->val3; //Decreasing if (sc->data[SC_MELODYOFSINK]) @@ -4684,6 +4689,12 @@ int status_calc_pc_sub(struct map_session_data* sd, enum e_status_calc_opt opt) } if (sc->data[SC_PYREXIA] && sc->data[SC_PYREXIA]->val3 == 0) sd->bonus.crit_atk_rate += sc->data[SC_PYREXIA]->val2; + if (sc->data[SC_LUXANIMA]) { + pc_bonus2(sd, SP_ADDSIZE, SZ_ALL, sc->data[SC_LUXANIMA]->val3); + sd->bonus.crit_atk_rate += sc->data[SC_LUXANIMA]->val3; + sd->bonus.short_attack_atk_rate += sc->data[SC_LUXANIMA]->val3; + sd->bonus.long_attack_atk_rate += sc->data[SC_LUXANIMA]->val3; + } } status_cpy(&sd->battle_status, base_status); @@ -5117,7 +5128,7 @@ void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, str (bl->type == BL_PC && (((TBL_PC*)bl)->class_&MAPID_UPPERMASK) == MAPID_MONK && sc->data[SC_EXTREMITYFIST] && (!sc->data[SC_SPIRIT] || sc->data[SC_SPIRIT]->val2 != SL_MONK)) || #endif - (sc->data[SC_OBLIVIONCURSE] && sc->data[SC_OBLIVIONCURSE]->val3 == 1) || sc->data[SC_VITALITYACTIVATION]) + (sc->data[SC_OBLIVIONCURSE] && sc->data[SC_OBLIVIONCURSE]->val3 == 1)) regen->flag &= ~RGN_SP; if (sc->data[SC_TENSIONRELAX]) { @@ -6010,7 +6021,7 @@ static unsigned short status_calc_str(struct block_list *bl, struct status_chang if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH) str += ((sc->data[SC_SPIRIT]->val3)>>16)&0xFF; if(sc->data[SC_GIANTGROWTH]) - str += 30; + str += sc->data[SC_GIANTGROWTH]->val2; if(sc->data[SC_BEYONDOFWARCRY]) str -= sc->data[SC_BEYONDOFWARCRY]->val2; if(sc->data[SC_SAVAGE_STEAK]) @@ -7620,7 +7631,7 @@ static short status_calc_fix_aspd(struct block_list *bl, struct status_change *s if ((sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION] || sc->data[SC_WILD_STORM_OPTION])) aspd -= 50; // +5 ASPD - if (sc->data[SC_FIGHTINGSPIRIT] && sc->data[SC_FIGHTINGSPIRIT]->val2) + if (sc->data[SC_FIGHTINGSPIRIT]) aspd -= sc->data[SC_FIGHTINGSPIRIT]->val2; if (sc->data[SC_MTF_ASPD]) aspd -= sc->data[SC_MTF_ASPD]->val1; @@ -10124,6 +10135,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty case SC_REUSE_LIMIT_ASPD_POTION: case SC_DORAM_BUF_01: case SC_DORAM_BUF_02: + case SC_REUSE_LIMIT_LUXANIMA: return 0; case SC_PUSH_CART: case SC_COMBO: @@ -11211,7 +11223,11 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty tick_time = 10000; // [GodLesZ] tick time break; case SC_GIANTGROWTH: - val2 = 15; // Double damage success rate. + val2 = 30; // Damage success rate and STR increase + break; + case SC_LUXANIMA: + val2 = 15; // Storm Blast success % + val3 = 30; // Damage/HP/SP % increase break; /* Arch Bishop */ @@ -12854,6 +12870,7 @@ int status_change_clear(struct block_list* bl, int type) case SC_LHZ_DUN_N4: case SC_ENTRY_QUEUE_APPLY_DELAY: case SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT: + case SC_REUSE_LIMIT_LUXANIMA: // Costumes case SC_MOONSTAR: case SC_SUPER_STAR: @@ -14983,6 +15000,7 @@ void status_change_clear_buffs(struct block_list* bl, uint8 type) case SC_LHZ_DUN_N2: case SC_LHZ_DUN_N3: case SC_LHZ_DUN_N4: + case SC_REUSE_LIMIT_LUXANIMA: // Clans case SC_CLAN_INFO: case SC_SWORDCLAN: diff --git a/src/map/status.hpp b/src/map/status.hpp index 2f53737b4e..bbe61a84d4 100644 --- a/src/map/status.hpp +++ b/src/map/status.hpp @@ -930,6 +930,9 @@ enum sc_type : int16 { SC_HELPANGEL, SC_SOUNDOFDESTRUCTION, + SC_LUXANIMA, + SC_REUSE_LIMIT_LUXANIMA, + #ifdef RENEWAL SC_EXTREMITYFIST2, //! NOTE: This SC should be right before SC_MAX, so it doesn't disturb if RENEWAL is disabled #endif From 1fe4e4f17f9f6df1d84748b332d8293a73e7cdb3 Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Mon, 18 May 2020 19:35:16 +0200 Subject: [PATCH 027/141] SQL synchronization --- sql-files/item_db_re.sql | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/sql-files/item_db_re.sql b/sql-files/item_db_re.sql index 124e9efb4c..e03d351b56 100644 --- a/sql-files/item_db_re.sql +++ b/sql-files/item_db_re.sql @@ -7076,23 +7076,23 @@ REPLACE INTO `item_db_re` VALUES (12724,'Poison_Fatigue','Venom Bleed',2,2,NULL, #=================================================================== # Rune Knight's Rune Stones #=================================================================== -REPLACE INTO `item_db_re` VALUES (12725,'Runstone_Nosiege','Nauthiz Rune',2,100,NULL,100,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_REFRESH",1;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12726,'Runstone_Rhydo','Raido Rune',2,100,NULL,100,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_CRUSHSTRIKE",1;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12727,'Runstone_Verkana','Berkana Rune',2,100,NULL,100,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_MILLENNIUMSHIELD",1;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12728,'Runstone_Isia','Isa Rune',2,100,NULL,100,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_VITALITYACTIVATION",1;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12729,'Runstone_Asir','Othila Rune',2,100,NULL,100,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_FIGHTINGSPIRIT",1;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12730,'Runstone_Urj','Uruz Rune',2,100,NULL,100,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_ABUNDANCE",1;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12731,'Runstone_Turisus','Thurisaz Rune',2,100,NULL,100,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_GIANTGROWTH",1;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12732,'Runstone_Pertz','Wyrd Rune',2,100,NULL,100,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_STORMBLAST",1;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12733,'Runstone_Hagalas','Hagalaz Rune',2,100,NULL,100,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_STONEHARDSKIN",1;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12725,'Runstone_Nosiege','Nauthiz Rune',2,100,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_REFRESH",1;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12726,'Runstone_Rhydo','Raido Rune',2,100,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_CRUSHSTRIKE",1;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12727,'Runstone_Verkana','Berkana Rune',2,100,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_MILLENNIUMSHIELD",1;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12728,'Runstone_Isia','Isa Rune',2,100,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_VITALITYACTIVATION",1;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12729,'Runstone_Asir','Othila Rune',2,100,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_FIGHTINGSPIRIT",1;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12730,'Runstone_Urj','Uruz Rune',2,100,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_ABUNDANCE",1;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12731,'Runstone_Turisus','Thurisaz Rune',2,100,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_GIANTGROWTH",1;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12732,'Runstone_Pertz','Wyrd Rune',2,100,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_STORMBLAST",1;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12733,'Runstone_Hagalas','Hagalaz Rune',2,100,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'if ((eaclass()&EAJ_THIRDMASK) == EAJ_RUNE_KNIGHT) unitskilluseid getcharid(3),"RK_STONEHARDSKIN",1;',NULL,NULL); #=================================================================== # Rune Knight Rune Ores #=================================================================== -REPLACE INTO `item_db_re` VALUES (12734,'Runstone_Quality','Luxurious Rune',0,2,NULL,100,NULL,NULL,NULL,NULL,0x00000080,56,2,NULL,NULL,NULL,NULL,NULL,'makerune 5;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12735,'Runstone_Ancient','Ancient Rune',0,2,NULL,100,NULL,NULL,NULL,NULL,0x00000080,56,2,NULL,NULL,NULL,NULL,NULL,'makerune 11;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12736,'Runstone_Mystic','Mystic Rune',0,2,NULL,100,NULL,NULL,NULL,NULL,0x00000080,56,2,NULL,NULL,NULL,NULL,NULL,'makerune 14;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12737,'Runstone_Ordinary','General Rune',0,2,NULL,100,NULL,NULL,NULL,NULL,0x00000080,56,2,NULL,NULL,NULL,NULL,NULL,'makerune 2;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (12738,'Runstone_Rare','Rare Rune',0,2,NULL,100,NULL,NULL,NULL,NULL,0x00000080,56,2,NULL,NULL,NULL,NULL,NULL,'makerune 8;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12734,'Runstone_Quality','Luxurious Rune',0,2,NULL,100,NULL,NULL,NULL,NULL,0x00000080,56,2,NULL,NULL,NULL,NULL,NULL,'makerune 8;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12735,'Runstone_Ancient','Ancient Rune',0,2,NULL,100,NULL,NULL,NULL,NULL,0x00000080,56,2,NULL,NULL,NULL,NULL,NULL,'makerune 30;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12736,'Runstone_Mystic','Mystic Rune',0,2,NULL,100,NULL,NULL,NULL,NULL,0x00000080,56,2,NULL,NULL,NULL,NULL,NULL,'makerune 60;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12737,'Runstone_Ordinary','General Rune',0,2,NULL,100,NULL,NULL,NULL,NULL,0x00000080,56,2,NULL,NULL,NULL,NULL,NULL,'makerune 4;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (12738,'Runstone_Rare','Rare Rune',0,2,NULL,100,NULL,NULL,NULL,NULL,0x00000080,56,2,NULL,NULL,NULL,NULL,NULL,'makerune 15;',NULL,NULL); #=================================================================== # More usable items #=================================================================== @@ -11403,7 +11403,7 @@ REPLACE INTO `item_db_re` VALUES (22535,'WorkerScroll_A','Scroll Summoning Worke REPLACE INTO `item_db_re` VALUES (22536,'WorkerScroll_B','Scroll Summoning Workers(Female)',2,10,NULL,10,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); REPLACE INTO `item_db_re` VALUES (22537,'PrizeOfHero','Prize Of Hero',2,0,NULL,100,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,NULL,NULL,'1',NULL,NULL,'getrandgroupitem(IG_PrizeOfHero,1);',NULL,NULL); REPLACE INTO `item_db_re` VALUES (22538,'Hanbok_bag','Hanbok bag',2,0,NULL,10,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_HANBOK,600000,0;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (22540,'Runstone_Lux','Lux Anima Runestone',11,2,NULL,100,NULL,NULL,NULL,NULL,0xFFFFFFFF,56,2,NULL,NULL,NULL,NULL,NULL,'itemskill "RK_LUXANIMA",1;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (22540,'Runstone_Lux','Lux Anima Runestone',11,2,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,56,2,NULL,NULL,NULL,NULL,NULL,'itemskill "RK_LUXANIMA",1;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (22541,'PC_Room_Coupon_Box_VI','PC Room Coupon Box VI',18,10,NULL,10,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); REPLACE INTO `item_db_re` VALUES (22542,'Center_Potion_B','Concentration Potion',2,10,NULL,100,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_ASPDPOTION0,1800000,4;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (22543,'Berserk_Potion_B','Berserk Potion',2,10,NULL,200,NULL,NULL,NULL,0,0x6,63,2,NULL,NULL,NULL,NULL,NULL,'sc_start SC_ASPDPOTION2,1800000,9;',NULL,NULL); From 4411ee4c4e8716f97ed8d805db564ec7c309d621 Mon Sep 17 00:00:00 2001 From: qwerty7vp <64515036+qwerty7vp@users.noreply.github.com> Date: Tue, 19 May 2020 02:13:24 +0700 Subject: [PATCH 028/141] Corrects Happy Balloon item script (#4976) * Fixes #4963. * Corrects Happy Balloon drop rate bonus and Aegis name. Co-authored-by: Atemo --- db/re/item_db.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/re/item_db.txt b/db/re/item_db.txt index 1b35e114b2..ef9150b717 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -10122,7 +10122,7 @@ 19092,Machoman_Glasses_,Machoman's Glasses,4,36000,,100,,1,,1,0xFFFFFFFE,63,2,512,,0,0,92,{},{},{} 19093,Spinning_Eyes_,Geek Glasses,4,20000,,100,,1,,1,0xFFFFFFFF,63,2,512,,0,0,27,{ bonus2 bResEff,Eff_Blind,800; },{},{} 19094,Mr_Smile_,Mr. Smile,4,60,,100,,1,,1,0xFFFFFFFF,63,2,513,,0,0,65,{},{},{} -19095,Happy_Balloon,Happy Balloon,4,0,,10,,0,,0,0xFFFFFFFF,63,2,1,,1,0,1289,{ bonus2 bDropAddRace,RC_All,5; bonus2 bExpAddRace,RC_All,5; },{},{} +19095,Happy_Balloon_K,Happy Balloon,4,0,,10,,0,,0,0xFFFFFFFF,63,2,1,,1,0,1289,{ bonus2 bDropAddRace,RC_All,10; bonus2 bExpAddRace,RC_All,5; },{},{} 19101,Glastheim_Observer,Glastheim Observer,4,20,,100,,0,,0,0xFFFFFFFF,63,2,1,,80,1,1041,{ bonus2 bAddClass,Class_All,2; bonus bDelayrate,-5; },{},{} 19102,Pale_Moon_Hat,Pale Moon Hat,4,20,,500,,2,,1,0xFFFFFFFF,63,2,256,,30,1,913,{ bonus bFlee,20; bonus2 bAddRace2,RC2_THANATOS,5; bonus2 bSubRace2,RC2_THANATOS,5; bonus2 bMagicAddRace2,RC2_THANATOS,5; if(getrefine()>4) { bonus2 bAddRace2,RC2_THANATOS,10; bonus2 bSubRace2,RC2_THANATOS,10; bonus2 bMagicAddRace2,RC2_THANATOS,10; } if(getrefine()>6) { bonus2 bAddRace2,RC2_THANATOS,15; bonus2 bSubRace2,RC2_THANATOS,15; bonus2 bMagicAddRace2,RC2_THANATOS,15; } if(getrefine()>8) { bonus2 bAddRace2,RC2_THANATOS,20; bonus2 bSubRace2,RC2_THANATOS,20; bonus2 bMagicAddRace2,RC2_THANATOS,20; } /* skill 3044,1; */ },{},{} 19109,Valhalla_Idol,Valhalla Idol,4,0,,300,,2,,0,0xFFFFFFFF,63,2,512,,70,0,1423,{ bonus bMaxSP,50; bonus4 bAutoSpellWhenHit,"MG_SAFETYWALL",10,50,0; hateffect(HAT_EF_VALHALLA_IDOL,true); },{},{ hateffect(HAT_EF_VALHALLA_IDOL,false); } From 936d5617b27d4faccf3f6aee836987f17624d0cb Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Mon, 18 May 2020 21:13:33 +0200 Subject: [PATCH 029/141] SQL synchronization --- sql-files/item_db_re.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql-files/item_db_re.sql b/sql-files/item_db_re.sql index e03d351b56..b5c5cde2c6 100644 --- a/sql-files/item_db_re.sql +++ b/sql-files/item_db_re.sql @@ -10154,7 +10154,7 @@ REPLACE INTO `item_db_re` VALUES (19091,'Opera_Ghost_Mask_','Opera Phantom Mask' REPLACE INTO `item_db_re` VALUES (19092,'Machoman_Glasses_','Machoman\'s Glasses',4,36000,NULL,100,NULL,1,NULL,1,0xFFFFFFFE,63,2,512,NULL,'0',0,92,NULL,NULL,NULL); REPLACE INTO `item_db_re` VALUES (19093,'Spinning_Eyes_','Geek Glasses',4,20000,NULL,100,NULL,1,NULL,1,0xFFFFFFFF,63,2,512,NULL,'0',0,27,'bonus2 bResEff,Eff_Blind,800;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (19094,'Mr_Smile_','Mr. Smile',4,60,NULL,100,NULL,1,NULL,1,0xFFFFFFFF,63,2,513,NULL,'0',0,65,NULL,NULL,NULL); -REPLACE INTO `item_db_re` VALUES (19095,'Happy_Balloon','Happy Balloon',4,0,NULL,10,NULL,0,NULL,0,0xFFFFFFFF,63,2,1,NULL,'1',0,1289,'bonus2 bDropAddRace,RC_All,5; bonus2 bExpAddRace,RC_All,5;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (19095,'Happy_Balloon_K','Happy Balloon',4,0,NULL,10,NULL,0,NULL,0,0xFFFFFFFF,63,2,1,NULL,'1',0,1289,'bonus2 bDropAddRace,RC_All,10; bonus2 bExpAddRace,RC_All,5;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (19101,'Glastheim_Observer','Glastheim Observer',4,20,NULL,100,NULL,0,NULL,0,0xFFFFFFFF,63,2,1,NULL,'80',1,1041,'bonus2 bAddClass,Class_All,2; bonus bDelayrate,-5;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (19102,'Pale_Moon_Hat','Pale Moon Hat',4,20,NULL,500,NULL,2,NULL,1,0xFFFFFFFF,63,2,256,NULL,'30',1,913,'bonus bFlee,20; bonus2 bAddRace2,RC2_THANATOS,5; bonus2 bSubRace2,RC2_THANATOS,5; bonus2 bMagicAddRace2,RC2_THANATOS,5; if(getrefine()>4) { bonus2 bAddRace2,RC2_THANATOS,10; bonus2 bSubRace2,RC2_THANATOS,10; bonus2 bMagicAddRace2,RC2_THANATOS,10; } if(getrefine()>6) { bonus2 bAddRace2,RC2_THANATOS,15; bonus2 bSubRace2,RC2_THANATOS,15; bonus2 bMagicAddRace2,RC2_THANATOS,15; } if(getrefine()>8) { bonus2 bAddRace2,RC2_THANATOS,20; bonus2 bSubRace2,RC2_THANATOS,20; bonus2 bMagicAddRace2,RC2_THANATOS,20; } /* skill 3044,1; */',NULL,NULL); REPLACE INTO `item_db_re` VALUES (19109,'Valhalla_Idol','Valhalla Idol',4,0,NULL,300,NULL,2,NULL,0,0xFFFFFFFF,63,2,512,NULL,'70',0,1423,'bonus bMaxSP,50; bonus4 bAutoSpellWhenHit,"MG_SAFETYWALL",10,50,0; hateffect(HAT_EF_VALHALLA_IDOL,true);',NULL,'hateffect(HAT_EF_VALHALLA_IDOL,false);'); From d9f533165ea731a3b624d87b8bf95aa671b140fb Mon Sep 17 00:00:00 2001 From: Aleos Date: Mon, 18 May 2020 16:51:46 -0400 Subject: [PATCH 030/141] Fixes Adoramus' interaction with Land Protector (#4920) * Fixes #4876. * Adoramus should not do damage to anyone inside the range of Adoramus. Thanks to @teededung and @Tokeiburu! --- src/map/skill.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 695c9dfb4c..361d8d71b0 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -5168,6 +5168,9 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint if (tsc && tsc->data[SC_HOVERING] && inf2[INF2_IGNOREHOVERING]) break; // Under Hovering characters are immune to select trap and ground target skills. + if (skill_id == AB_ADORAMUS && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR)) + break; // No damage should happen if the target is on Land Protector + if( flag&SD_LEVEL ) sflag |= SD_LEVEL; // -1 will be used in packets instead of the skill level if( skill_area_temp[1] != bl->id && !inf2[INF2_ISNPC] ) From 18b642c1740433a4002f28bf54dbc2849b7fea8a Mon Sep 17 00:00:00 2001 From: Aleos Date: Tue, 19 May 2020 15:32:57 -0400 Subject: [PATCH 031/141] Skill Database cleanups (#4902) * When reloading the skill_db clear out Abra, Magic Mushroom, and Reading Spell Book databases as well. * Fixes NC_SHAPESHIFT and NC_REPAIR checking an invalid ItemCost slot based on the old implementation. * Updated a lot of references to the old skill databases. --- conf/battle/skill.conf | 8 ++++---- doc/atcommands.txt | 2 +- doc/item_bonus.txt | 2 +- doc/mapflags.txt | 2 +- doc/script_commands.txt | 10 +++++----- src/map/skill.cpp | 25 ++++++++++++------------- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/conf/battle/skill.conf b/conf/battle/skill.conf index d5b9e3b0fd..d124fba658 100644 --- a/conf/battle/skill.conf +++ b/conf/battle/skill.conf @@ -85,7 +85,7 @@ clear_skills_on_warp: 15 //Setting this to YES will override the target mode of ground-based skills with the flag 0x01 to "No Enemies" //The two skills affected by default are Pneuma and Safety Wall (if set to yes, those two skills will not protect everyone, but only allies) -//See db/skill_unit_db.txt for more info. +//See db/(pre-)re/skill_db.yml for more info. defunit_not_enemy: no // Should skills always do at least 'hits' damage when they don't miss/are blocked? @@ -107,11 +107,11 @@ auto_counter_type: 15 // Can ground skills be placed on top of each other? (Note 3) // By default, skills with UF_NOREITERATION set cannot be stacked on top of -// other skills, this setting will override that. (skill_unit_db) +// other skills, this setting will override that. skill_reiteration: 0 // Can ground skills NOT be placed underneath/near players/monsters? (Note 3) -// If set, only skills with UF_NOFOOTSET set will be affected (skill_unit_db) +// If set, only skills with UF_NOFOOTSET set will be affected. skill_nofootset: 1 // Should traps (hunter traps + quagmire) change their target to "all" inside gvg/pvp grounds? (Note 3) @@ -343,7 +343,7 @@ stormgust_knockback: yes // For RENEWAL_CAST (Note 2) // By default skill that has '0' value for Fixed Casting Time will use 20% of cast time // as Fixed Casting Time, and the rest (80%) as Variable Casting Time. -// Put it 0 to disable default Fixed Casting Time (just like -1 is the skill_cast_db.txt). +// Put it 0 to disable default Fixed Casting Time (just like -1 in the skill_db.yml). default_fixed_castrate: 20 // On official servers, skills that hit all targets on a path (e.g. Focused Arrow Strike and First Wind) first diff --git a/doc/atcommands.txt b/doc/atcommands.txt index abf2ba0266..54c2bcae13 100644 --- a/doc/atcommands.txt +++ b/doc/atcommands.txt @@ -1367,7 +1367,7 @@ Affected files: -- pcdb: statpoint.txt, job_exp.txt, skill_tree.txt, attr_fix.txt, job_db1.txt, job_db2.txt, job_basehpsp_db.txt, job_maxhpsp_db.txt, job_param_db.txt, level_penalty.txt -- questdb: quest_db.txt -- script: /npc/*.txt, /npc/*.conf --- skilldb: skill_db.txt, const.txt, skill_require_db.txt, skill_cast_db.txt, skill_castnodex_db.txt, skill_nocast_db.txt, skill_copyable_db.txt, skill_improvise_db.txt, skill_changematerial_db.txt, skill_nonearnpc_db.txt, skill_damage_db.txt, skill_unit_db.txt, abra_db.txt, create_arrow_db.txt, produce_db.txt, spellbook_db.txt, magicmushroom_db.txt +-- skilldb: skill_db.yml, const.txt, skill_nocast_db.txt, skill_changematerial_db.txt, skill_damage_db.txt, abra_db.yml, create_arrow_db.txt, produce_db.txt, spellbook_db.yml, magicmushroom_db.yml -- statusdb: attr_fix.txt, size_fix.txt, refine_db.txt -- achievementdb: achievement_db.conf diff --git a/doc/item_bonus.txt b/doc/item_bonus.txt index 7428fd900d..21e030bd4d 100644 --- a/doc/item_bonus.txt +++ b/doc/item_bonus.txt @@ -65,7 +65,7 @@ This list contains all available constants referenced in the 'bonus' commands. ATF_MISC = Trigger on misc skills * Other values: - Skill (sk): see 'db/(pre-)re/skill_db.txt' (NOTE: Both skill IDs and names, in quotes, are supported.) + Skill (sk): see 'db/(pre-)re/skill_db.yml' (NOTE: Both skill IDs and names, in quotes, are supported.) Monster id (mid): see 'db/(pre-)re/mob_db.txt' Item id (iid): see 'db/(pre-)re/item_db.txt' Item group (ig): see 'db/(pre-)re/item_group_db.txt' and the constants in 'db/const.txt', prefixed with IG_* diff --git a/doc/mapflags.txt b/doc/mapflags.txt index aa6cb66554..bfcc9285f9 100644 --- a/doc/mapflags.txt +++ b/doc/mapflags.txt @@ -304,7 +304,7 @@ for 'Map' type 16 will be applied. This mapflag can also be used to adjust the damage of one skill by a percentage: - skill_name: - Name of the skill in 'db/(pre-)re/skill_db.txt' (ex. SM_BASH). + Name of the skill in 'db/(pre-)re/skill_db.yml' (ex. SM_BASH). To adjust all skill damage, write "all" (without quotes). - caster: the groups for which the adjustment takes effect. (bitmask) BL_PC = Player diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 60fd044798..a6b716f4d3 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -3426,7 +3426,7 @@ recalculating guardians' HP. This function returns the level of the skill of the guild . If the guild does not have that skill, 0 is returned. If the guild does not exist, -1 is returned. -Refer to 'db/(pre-)re/skill_db.txt' for the full list of skills. (GD_* are guild skills) +Refer to 'db/(pre-)re/skill_db.yml' for the full list of skills. (GD_* are guild skills) --------------------------------------- @@ -3459,7 +3459,7 @@ mes "You have " + getMapGuildUsers("prontera",getcharid(2)) + " guild members in This function returns the level of the specified skill that the invoking character has. If they don't have the skill, 0 will be returned. The full list -of character skills is available in 'db/(pre-)re/skill_db.txt'. +of character skills is available in 'db/(pre-)re/skill_db.yml'. There are two main uses for this function, it can check whether the character has a skill or not, and it can tell you if the level is high enough. @@ -5583,7 +5583,7 @@ levels. This refers to the invoking character and will only work if the invoking character is a member of a guild AND its guild master, otherwise no failure message will be given and no error will occur, but nothing will happen - same about the guild skill trying to exceed the possible maximum. The full list of -guild skills is available in 'db/(pre-)re/skill_db.txt', these are all the GD_ skills at +guild skills is available in 'db/(pre-)re/skill_db.yml', these are all the GD_ skills at the end. // This would give your character's guild one level of Approval (GD_APPROVAL ID @@ -5970,7 +5970,7 @@ These commands will give the invoking character a specified skill. This is also used for item scripts. Level is obvious. Skill id is the ID number of the skill in question as per -'db/(pre-)re/skill_db.txt'. It is not known for certain whether this can be used to give +'db/(pre-)re/skill_db.yml'. It is not known for certain whether this can be used to give a character a monster's skill, but you're welcome to try with the numbers given in 'db/(pre-)re/mob_skill_db.txt'. @@ -9702,7 +9702,7 @@ when pet performance is activated. This will make the pet use a specified support skill on the owner whenever the HP and SP are below the given percent values, with a specified delay time -between activations. The skill numbers are as per 'db/(pre-)re/skill_db.txt'. +between activations. The skill numbers are as per 'db/(pre-)re/skill_db.yml'. It's not quite certain who's stats will be used for the skills cast, the character's or the pets. Probably, Skotlex can answer that question. diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 361d8d71b0..645a951ec3 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -765,7 +765,7 @@ int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 sk /** * Making Plagiarism and Reproduce check their own function - * Previous prevention for NPC skills, Wedding skills, and INF3_DIS_PLAGIA are removed since we use skill_copyable_db.txt [Cydh] + * Previous prevention for NPC skills, Wedding skills, and INF3_DIS_PLAGIA are removed since we use skill_db.yml [Cydh] * @param sd: Player who will copy the skill * @param skill_id: Target skill * @return 0 - Cannot be copied; 1 - Can be copied by Plagiarism 2 - Can be copied by Reproduce @@ -1507,7 +1507,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 sc_start(src,bl,SC_STUN,(10+3*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv)); sc_start(src,bl,SC_BLIND,(10+3*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)); #ifdef RENEWAL - sc_start(src, bl, SC_RAID, 100, skill_lv, 10000); // Hardcoded to 10 seconds since skill_cast_db is full + sc_start(src, bl, SC_RAID, 100, skill_lv, 10000); // Hardcoded to 10 seconds since Duration1 and Duration2 are used break; case RG_BACKSTAP: @@ -14148,7 +14148,7 @@ static int skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, t_ } /** - * Process skill unit each interval (sg->interval, see interval field of skill_unit_db.txt) + * Process skill unit each interval (sg->interval, see interval field of skill_db.yml) * @param unit Skill unit * @param bl Valid 'target' above the unit, that has been check in skill_unit_timer_sub_onplace * @param tick @@ -16892,10 +16892,10 @@ struct s_skill_condition skill_get_requirement(struct map_session_data* sd, uint switch( skill_id ) { /* Skill level-dependent checks */ - case NC_SHAPESHIFT: // NOTE: Please make sure Magic_Gear_Fuel in the last position in skill_require_db.txt - case NC_REPAIR: // NOTE: Please make sure Repair_Kit in the last position in skill_require_db.txt - req.itemid[1] = skill->require.itemid[MAX_SKILL_ITEM_REQUIRE-1]; - req.amount[1] = skill->require.amount[MAX_SKILL_ITEM_REQUIRE-1]; + case NC_SHAPESHIFT: // NOTE: Magic_Gear_Fuel must be last in the ItemCost list depending on the skill's max level + case NC_REPAIR: // NOTE: Repair_Kit must be last in the ItemCost list depending on the skill's max level + req.itemid[1] = skill->require.itemid[skill->max - 1]; + req.amount[1] = skill->require.amount[skill->max - 1]; case KO_MAKIBISHI: case GN_FIRE_EXPANSION: case SO_SUMMON_AGNI: @@ -18811,7 +18811,7 @@ static int skill_get_new_group_id(void) * @param count How many 'cells' used that needed. Related with skill layout * @param skill_id ID of used skill * @param skill_lv Skill level of used skill - * @param unit_id Unit ID (look at skill_unit_db.txt) + * @param unit_id Unit ID (see skill.hpp::e_skill_unit_id) * @param limit Lifetime for skill unit, uses skill_get_time(skill_id, skill_lv) * @param interval Time interval * @return skill_unit_group @@ -22995,12 +22995,8 @@ static bool skill_parse_row_skilldamage(char* split[], int columns, int current) /*=============================== * DB reading. - * skill_db.txt - * skill_require_db.txt - * skill_cast_db.txt - * skill_castnodex_db.txt + * skill_db.yml * skill_nocast_db.txt - * skill_unit_db.txt * produce_db.txt * create_arrow_db.txt *------------------------------*/ @@ -23054,6 +23050,9 @@ static void skill_readdb(void) void skill_reload (void) { skill_db.clear(); + abra_db.clear(); + magic_mushroom_db.clear(); + reading_spellbook_db.clear(); skill_readdb(); initChangeTables(); // Re-init Status Change tables From 5d4fd1d3b71e3e336add4709c27fc816d6819a46 Mon Sep 17 00:00:00 2001 From: Aleos Date: Tue, 19 May 2020 17:19:34 -0400 Subject: [PATCH 032/141] Resolves Autospell displaying errors to console (#4921) * Fixes #4883 * Fixes Autospell displaying invalid skill errors when clicking Ok or Cancel. Thanks to @admkakaroto! --- src/map/skill.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 645a951ec3..5489bd8eaa 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -17636,15 +17636,15 @@ int skill_autospell(struct map_session_data *sd, uint16 skill_id) { nullpo_ret(sd); - if (SKILL_CHK_GUILD(skill_id)) + if (skill_id == 0 || skill_get_index_(skill_id, true, __FUNCTION__, __FILE__, __LINE__) == 0 || SKILL_CHK_GUILD(skill_id)) return 0; uint16 lv = pc_checkskill(sd, skill_id), skill_lv = sd->menuskill_val; - - if(!skill_lv || !lv) return 0; // Player must learn the skill before doing auto-spell [Lance] - uint16 maxlv = 1; + if (skill_lv == 0 || lv == 0) + return 0; // Player must learn the skill before doing auto-spell [Lance] + #ifdef RENEWAL if ((skill_id == MG_COLDBOLT || skill_id == MG_FIREBOLT || skill_id == MG_LIGHTNINGBOLT) && sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_SAGE) maxlv = 10; //Soul Linker bonus. [Skotlex] From 92a466c758fe8cea800bbe8b1d55f5f63f089e13 Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Wed, 20 May 2020 06:00:28 +0200 Subject: [PATCH 033/141] Mob and NPC Identity Update --- db/re/mob_db.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/db/re/mob_db.txt b/db/re/mob_db.txt index d237060a85..aece8b1699 100644 --- a/db/re/mob_db.txt +++ b/db/re/mob_db.txt @@ -3983,6 +3983,24 @@ //20884,G_ILL_STROUF //20885,G_ILL_PHEN +//20901,DISASTER_OMEN +//20902,DISASTER_WIND +//20903,CONQUER_INCARNATION +//20904,FAMINE_INCARNATION +//20905,APPETITE_INCARNATION +//20906,DISASTER_SYMBOL +//20907,DEVIL_EYE +//20908,WAR_INCARNATION +//20909,DEATH_INCARNATION +//20910,CARNIVOROUS +//20911,SINS_JUSTICE +//20912,SINS_BRAVE +//20913,SINS_MODERATION +//20914,SINS_WISDOM +//20915,G_SINS_JUSTICE +//20916,G_SINS_BRAVE +//20917,G_SINS_MODERATION +//20918,G_SINS_WISDOM //20919,CARAT_TWEVENT //31999,HUNTING_GID_DEFAULT From c3dda07180538755eb4a774ee64dddc91da4612d Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Wed, 20 May 2020 06:00:42 +0200 Subject: [PATCH 034/141] SQL synchronization --- sql-files/mob_db_re.sql | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sql-files/mob_db_re.sql b/sql-files/mob_db_re.sql index 6161c5686a..1853b43ba7 100644 --- a/sql-files/mob_db_re.sql +++ b/sql-files/mob_db_re.sql @@ -4050,6 +4050,24 @@ REPLACE INTO `mob_db_re` VALUES (3790,'SWEETS_DROPS','Sweets Drops','Sweets Drop #20884,G_ILL_STROUF #20885,G_ILL_PHEN +#20901,DISASTER_OMEN +#20902,DISASTER_WIND +#20903,CONQUER_INCARNATION +#20904,FAMINE_INCARNATION +#20905,APPETITE_INCARNATION +#20906,DISASTER_SYMBOL +#20907,DEVIL_EYE +#20908,WAR_INCARNATION +#20909,DEATH_INCARNATION +#20910,CARNIVOROUS +#20911,SINS_JUSTICE +#20912,SINS_BRAVE +#20913,SINS_MODERATION +#20914,SINS_WISDOM +#20915,G_SINS_JUSTICE +#20916,G_SINS_BRAVE +#20917,G_SINS_MODERATION +#20918,G_SINS_WISDOM #20919,CARAT_TWEVENT #31999,HUNTING_GID_DEFAULT From bf4b41bd66b3d372b09ad0076216ebc834937e09 Mon Sep 17 00:00:00 2001 From: CairoLee Date: Wed, 20 May 2020 21:22:53 +0800 Subject: [PATCH 035/141] Fixes a crash caused by invalid skill ID (#4935) * Fixes #4934. * Fixes a potential crash when using atcommand useskill. Thanks to @Stingor and @CairoLee! --- src/map/atcommand.cpp | 8 ++++++++ src/map/unit.cpp | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index b5d04a12c4..d719469e11 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -5835,6 +5835,14 @@ ACMD_FUNC(useskill) return -1; } + if (!skill_id || !skill_db.find(skill_id)) { + clif_displaymessage(fd, msg_txt(sd, 198)); // This skill number doesn't exist. + return -1; + } + + if (!skill_lv) + skill_lv = 1; + if(!strcmp(atcmd_player_name,"self")) pl_sd = sd; //quick keyword else if ( (pl_sd = map_nick2sd(atcmd_player_name,true)) == NULL ){ diff --git a/src/map/unit.cpp b/src/map/unit.cpp index 6c8b6de35b..2d0a74cbff 100644 --- a/src/map/unit.cpp +++ b/src/map/unit.cpp @@ -1558,6 +1558,9 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui int inf = skill_get_inf(skill_id); std::shared_ptr skill = skill_db.find(skill_id); + if (!skill) + return 0; + // temp: used to signal combo-skills right now. if (sc && sc->data[SC_COMBO] && skill_is_combo(skill_id) && @@ -2035,6 +2038,9 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui if (sc && !sc->count) sc = NULL; + if (!skill_db.find(skill_id)) + return 0; + if( sd ) { if( skill_isNotOk(skill_id, sd) || !skill_check_condition_castbegin(sd, skill_id, skill_lv) ) return 0; From 746c1bdfa9d3eb71405a00ecc6bfe59a91e8a93a Mon Sep 17 00:00:00 2001 From: LordWhiplash <62680611+LordWhiplash@users.noreply.github.com> Date: Wed, 20 May 2020 17:18:31 -0300 Subject: [PATCH 036/141] Fixes MO_EXTREMITYFIST (#4886) --- src/map/battle.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 044f665c95..831542a766 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -3846,11 +3846,11 @@ static int battle_calc_attack_skill_ratio(struct Damage* wd, struct block_list * #endif break; case MO_EXTREMITYFIST: + skillratio += 100 * (7 + sstatus->sp / 10); #ifdef RENEWAL if (wd->miscflag&1) - skillratio += 100; // More than 5 spirit balls active + skillratio *= 2; // More than 5 spirit balls active #endif - skillratio += 100 * (7 + sstatus->sp / 10); skillratio = min(500000,skillratio); //We stop at roughly 50k SP for overflow protection break; case MO_TRIPLEATTACK: From ab628b23690a45b6acce359178f4e39ff0876984 Mon Sep 17 00:00:00 2001 From: Aleos Date: Wed, 20 May 2020 16:54:42 -0400 Subject: [PATCH 037/141] Remove Summoner Hide when changing maps (#4958) * Fixes #4953. * Remove Summoner Hide when changing maps to avoid equipment from being stripped due to the inability to change equipment while the status is active. Thanks to @Everade! --- src/map/pc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/map/pc.cpp b/src/map/pc.cpp index 6f25667997..0184a56ffd 100755 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -5865,6 +5865,7 @@ enum e_setpos pc_setpos(struct map_session_data* sd, unsigned short mapindex, in status_change_end(&sd->bl, SC_PROPERTYWALK, INVALID_TIMER); status_change_end(&sd->bl, SC_CLOAKING, INVALID_TIMER); status_change_end(&sd->bl, SC_CLOAKINGEXCEED, INVALID_TIMER); + status_change_end(&sd->bl, SC_SUHIDE, INVALID_TIMER); } for(int i = 0; i < EQI_MAX; i++ ) { if( sd->equip_index[i] >= 0 ) From a34a2f6de085132184cd270c400e7f2a830e3f93 Mon Sep 17 00:00:00 2001 From: Aleos Date: Wed, 20 May 2020 17:20:37 -0400 Subject: [PATCH 038/141] Fixes Blood Sucker not being able to be recast (#4968) * Fixes #4964. * Fixes Blood Sucker not being able to be recast on another map if caster dies while active. * Minor cleanups and added safety checks. Thanks to @Everade! --- src/map/skill.cpp | 4 ++-- src/map/status.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 5489bd8eaa..8b4c49f591 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -10953,12 +10953,12 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui if( sc && sc->bs_counter < skill_get_maxcount( skill_id , skill_lv) ) { if( tsc && tsc->data[type] ){ - (sc->bs_counter)--; + sc->bs_counter--; status_change_end(src, type, INVALID_TIMER); // the first one cancels and the last one will take effect resetting the timer } clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); sc_start2(src,bl, type, 100, skill_lv, src->id, skill_get_time(skill_id,skill_lv)); - (sc->bs_counter)++; + sc->bs_counter++; } else if( sd ) { clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); break; diff --git a/src/map/status.cpp b/src/map/status.cpp index 7cd2e1f138..cf25084fc7 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -13428,9 +13428,12 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const case SC_BLOODSUCKER: if( sce->val2 ) { struct block_list *src = map_id2bl(sce->val2); - if(src) { + + if (src && !status_isdead(src)) { struct status_change *sc2 = status_get_sc(src); - sc2->bs_counter--; + + if (sc2) + sc2->bs_counter--; } } break; From 20945d71db2f93909af751a10541837cef0a3edf Mon Sep 17 00:00:00 2001 From: Aleos Date: Wed, 20 May 2020 18:09:44 -0400 Subject: [PATCH 039/141] Removes nullpo checks from CSV2YAML (#4979) * Fixes #4791. * Removes two nullpo checks from CSV2YAML. * Fixes undefined reference compile errors. Thanks to @ecdarreola! --- src/tool/csv2yaml.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tool/csv2yaml.cpp b/src/tool/csv2yaml.cpp index 729f891678..5aa379cafd 100644 --- a/src/tool/csv2yaml.cpp +++ b/src/tool/csv2yaml.cpp @@ -150,7 +150,8 @@ void script_set_constant_( const char* name, int64 value, const char* constant_n } const char* constant_lookup( int32 value, const char* prefix ){ - nullpo_retr( nullptr, prefix ); + if (prefix == nullptr) + return nullptr; for( auto const& pair : constants ){ // Same prefix group and same value @@ -163,7 +164,8 @@ const char* constant_lookup( int32 value, const char* prefix ){ } int64 constant_lookup_int(const char* constant) { - nullpo_retr(-100, constant); + if (constant == nullptr) + return -100; for (auto const &pair : constants) { if (strlen(pair.first) == strlen(constant) && strncasecmp(pair.first, constant, strlen(constant)) == 0) { From 70d6876494d958d37e024c1c0652c49c7d7c8206 Mon Sep 17 00:00:00 2001 From: Aleos Date: Wed, 20 May 2020 18:50:04 -0400 Subject: [PATCH 040/141] Corrects NPC_DARKBREATH behavior (#4982) * Fixes #4864. * Adjusts damage formula to official. * Adjusts damage type from Misc to Magic. * Skill now has a 50% success chance to damage the target. * Removes Flee and Defense Card ignore flags. Thanks to @Masao87 and @mrjnumber1! --- db/pre-re/skill_db.yml | 5 +---- db/re/skill_db.yml | 5 +---- src/map/battle.cpp | 6 +++--- src/map/skill.cpp | 2 ++ 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/db/pre-re/skill_db.yml b/db/pre-re/skill_db.yml index 66e834f131..22f97f1eed 100644 --- a/db/pre-re/skill_db.yml +++ b/db/pre-re/skill_db.yml @@ -5836,11 +5836,8 @@ Body: Name: NPC_DARKBREATH Description: Dark Breath MaxLevel: 5 - Type: Misc + Type: Magic TargetType: Attack - DamageFlags: - IgnoreFlee: true - IgnoreDefCard: true Flags: IsNpc: true Range: 9 diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index d326054ac0..85b8450c08 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -6121,11 +6121,8 @@ Body: Name: NPC_DARKBREATH Description: Dark Breath MaxLevel: 5 - Type: Misc + Type: Magic TargetType: Attack - DamageFlags: - IgnoreFlee: true - IgnoreDefCard: true Flags: IsNpc: true Range: 9 diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 831542a766..772581dd49 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -6164,6 +6164,9 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list #endif } break; + case NPC_DARKBREATH: + ad.damage = tstatus->hp * (skill_lv <= 5 ? 100 / (2 * 6 - skill_lv) : 50) / 100; + break; case PF_SOULBURN: ad.damage = tstatus->sp * 2; break; @@ -6907,9 +6910,6 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list * case NPC_SMOKING: md.damage = 3; break; - case NPC_DARKBREATH: - md.damage = tstatus->max_hp * skill_lv * 10 / 100; - break; case NPC_EVILLAND: md.damage = skill_calc_heal(src,target,skill_id,skill_lv,false); break; diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 8b4c49f591..81eb74496e 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -5517,6 +5517,8 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint case NPC_DARKBREATH: clif_emotion(src,ET_ANGER); + if (rnd() % 2 == 0) + break; // 50% chance case SN_FALCONASSAULT: #ifndef RENEWAL case PA_PRESSURE: From 0fba10c3246af7a6f4f3b1fa98f50656a5458413 Mon Sep 17 00:00:00 2001 From: Aleos Date: Wed, 20 May 2020 21:21:31 -0400 Subject: [PATCH 041/141] Fixes a potential crash with atcommand time (#4989) * Fixes #4960. * Adds a timer data validation check for atcommand time. Thanks to @marky291! --- src/map/atcommand.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index d719469e11..c518459e57 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -4851,18 +4851,22 @@ ACMD_FUNC(servertime) clif_displaymessage(fd, msg_txt(sd,232)); // Game time: The game is in permanent night. } else if (battle_config.night_duration == 0) if (night_flag == 1) { // we start with night - timer_data = get_timer(day_timer_tid); - sprintf(temp, msg_txt(sd,233), txt_time(DIFF_TICK(timer_data->tick,gettick())/1000)); // Game time: The game is in night for %s. - clif_displaymessage(fd, temp); - clif_displaymessage(fd, msg_txt(sd,234)); // Game time: After, the game will be in permanent daylight. + if ((timer_data = get_timer(day_timer_tid)) != nullptr) { + sprintf(temp, msg_txt(sd,233), txt_time(DIFF_TICK(timer_data->tick,gettick())/1000)); // Game time: The game is in night for %s. + clif_displaymessage(fd, temp); + clif_displaymessage(fd, msg_txt(sd,234)); // Game time: After, the game will be in permanent daylight. + } else + clif_displaymessage(fd, msg_txt(sd,232)); // Game time: The game is in permanent night. } else clif_displaymessage(fd, msg_txt(sd,231)); // Game time: The game is in permanent daylight. else if (battle_config.day_duration == 0) if (night_flag == 0) { // we start with day - timer_data = get_timer(night_timer_tid); - sprintf(temp, msg_txt(sd,235), txt_time(DIFF_TICK(timer_data->tick,gettick())/1000)); // Game time: The game is in daylight for %s. - clif_displaymessage(fd, temp); - clif_displaymessage(fd, msg_txt(sd,236)); // Game time: After, the game will be in permanent night. + if ((timer_data = get_timer(night_timer_tid)) != nullptr) { + sprintf(temp, msg_txt(sd,235), txt_time(DIFF_TICK(timer_data->tick,gettick())/1000)); // Game time: The game is in daylight for %s. + clif_displaymessage(fd, temp); + clif_displaymessage(fd, msg_txt(sd,236)); // Game time: After, the game will be in permanent night. + } else + clif_displaymessage(fd, msg_txt(sd,231)); // Game time: The game is in permanent daylight. } else clif_displaymessage(fd, msg_txt(sd,232)); // Game time: The game is in permanent night. else { From 43caf7cc0acde6cdb5ee9b040737b26c9ca6ec60 Mon Sep 17 00:00:00 2001 From: LordWhiplash <62680611+LordWhiplash@users.noreply.github.com> Date: Thu, 21 May 2020 10:02:23 -0300 Subject: [PATCH 042/141] SU_HISS Perfect dodge fix (#4991) * Fixes #4990. * Adds missing x10 rate for perfect dodge calculations. Thanks to @Balferian and @LordWhiplash! --- src/map/status.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/status.cpp b/src/map/status.cpp index cf25084fc7..776f2a6a85 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -6997,7 +6997,7 @@ static signed short status_calc_flee2(struct block_list *bl, struct status_chang if(sc->data[SC__UNLUCKY]) flee2 -= flee2 * sc->data[SC__UNLUCKY]->val2 / 100; if (sc->data[SC_HISS]) - flee2 += sc->data[SC_HISS]->val2; + flee2 += sc->data[SC_HISS]->val2*10; if (sc->data[SC_DORAM_FLEE2]) flee2 += sc->data[SC_DORAM_FLEE2]->val1; From b8ee97b6a15d453c6473bf898429ead14bb0a887 Mon Sep 17 00:00:00 2001 From: Atemo Date: Thu, 21 May 2020 21:13:45 +0200 Subject: [PATCH 043/141] Added rental_item_novalue config (#3749) * Added a config to always sell the rental items to NPC for 0 Thanks to @Lemongrass3110, @cydh, @aleos89, @Daegaladh for the review ! --- conf/battle/items.conf | 3 +++ src/map/battle.cpp | 1 + src/map/battle.hpp | 1 + src/map/clif.cpp | 10 +++++++--- src/map/npc.cpp | 5 ++++- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/conf/battle/items.conf b/conf/battle/items.conf index be5e6448d0..bc2c21ce5a 100644 --- a/conf/battle/items.conf +++ b/conf/battle/items.conf @@ -134,6 +134,9 @@ broadcast_hide_name: 2 // Enable to sell rental item to NPC shop? (Note 1) rental_transaction: yes +// Sell rental item for 0 to NPC shop regardless of the item value in item_db? (Note 1) +rental_item_novalue: no + // Minimum purchase price of items at a normal Shop // Officially items cannot be purchased for less than 1 Zeny min_shop_buy: 1 diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 772581dd49..712db7102c 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -8952,6 +8952,7 @@ static const struct _battle_data { { "devotion_standup_fix", &battle_config.devotion_standup_fix, 1, 0, 1, }, { "feature.bgqueue", &battle_config.feature_bgqueue, 1, 0, 1, }, { "homunculus_exp_gain", &battle_config.homunculus_exp_gain, 10, 0, 100, }, + { "rental_item_novalue", &battle_config.rental_item_novalue, 1, 0, 1, }, #include "../custom/battle_config_init.inc" }; diff --git a/src/map/battle.hpp b/src/map/battle.hpp index 17c53d4305..5c0938e9cc 100644 --- a/src/map/battle.hpp +++ b/src/map/battle.hpp @@ -676,6 +676,7 @@ struct Battle_Config int devotion_standup_fix; int feature_bgqueue; int homunculus_exp_gain; + int rental_item_novalue; #include "../custom/battle_config_struct.inc" }; diff --git a/src/map/clif.cpp b/src/map/clif.cpp index d3a7adbd85..94a7291cbe 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -2038,9 +2038,13 @@ void clif_selllist(struct map_session_data *sd) if( !pc_can_sell_item(sd, &sd->inventory.u.items_inventory[i], nd->subtype)) continue; - val=sd->inventory_data[i]->value_sell; - if( val < 0 ) - continue; + if (battle_config.rental_item_novalue && sd->inventory.u.items_inventory[i].expire_time) + val = 0; + else { + val = sd->inventory_data[i]->value_sell; + if( val < 0 ) + continue; + } WFIFOW(fd,4+c*10)=i+2; WFIFOL(fd,6+c*10)=val; WFIFOL(fd,10+c*10)=pc_modifysellvalue(sd,val); diff --git a/src/map/npc.cpp b/src/map/npc.cpp index f65a3c5755..f86995f35e 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -2211,7 +2211,10 @@ uint8 npc_selllist(struct map_session_data* sd, int n, unsigned short *item_list return 1; // In official server, this illegal attempt the player will be disconnected } - value = pc_modifysellvalue(sd, sd->inventory_data[idx]->value_sell); + if (battle_config.rental_item_novalue && sd->inventory.u.items_inventory[idx].expire_time) + value = 0; + else + value = pc_modifysellvalue(sd, sd->inventory_data[idx]->value_sell); z+= (double)value*amount; } From 46aa15a1827aece8caa8547c80f8cd54f477de49 Mon Sep 17 00:00:00 2001 From: LordWhiplash <62680611+LordWhiplash@users.noreply.github.com> Date: Thu, 21 May 2020 23:24:29 -0300 Subject: [PATCH 044/141] Summoner corrections (#4993) * Fixes SU_POWEROFSEA max HP and max SP bonus when investing more than 20 skill points. * Fixes SU_SPIRITOFLAND and SU_CN_POWDERING combo's perfect dodge bonus. Thanks to @LordWhiplash, @qwerty7vp, and @Balferian! --- src/map/skill.cpp | 2 +- src/map/status.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 81eb74496e..2646dd40bb 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -12439,7 +12439,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case SU_NYANGGRASS: if (sd && pc_checkskill(sd, SU_SPIRITOFLAND)) { if (skill_id == SU_CN_POWDERING) - sc_start(src, src, SC_DORAM_FLEE2, 100, sd->status.base_level / 12, skill_get_time(SU_SPIRITOFLAND, 1)); + sc_start(src, src, SC_DORAM_FLEE2, 100, sd->status.base_level * 10 / 12, skill_get_time(SU_SPIRITOFLAND, 1)); else sc_start(src, src, SC_DORAM_MATK, 100, sd->status.base_level, skill_get_time(SU_SPIRITOFLAND, 1)); } diff --git a/src/map/status.cpp b/src/map/status.cpp index 776f2a6a85..b48a95e6ea 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -3321,7 +3321,7 @@ static int status_get_hpbonus(struct block_list *bl, enum e_status_bonus type) { bonus += 1000; if ((pc_checkskill(sd, SU_TUNABELLY) + pc_checkskill(sd, SU_TUNAPARTY) + pc_checkskill(sd, SU_BUNCHOFSHRIMP) + pc_checkskill(sd, SU_FRESHSHRIMP) + pc_checkskill(sd, SU_GROOMING) + pc_checkskill(sd, SU_PURRING) + pc_checkskill(sd, SU_SHRIMPARTY)) > 19) - bonus += 2000; + bonus += 3000; } if ((skill_lv = pc_checkskill(sd, NV_BREAKTHROUGH)) > 0) bonus += 350 * skill_lv + (skill_lv > 4 ? 250 : 0); @@ -3499,7 +3499,7 @@ static int status_get_spbonus(struct block_list *bl, enum e_status_bonus type) { bonus += 100; if ((pc_checkskill(sd, SU_TUNABELLY) + pc_checkskill(sd, SU_TUNAPARTY) + pc_checkskill(sd, SU_BUNCHOFSHRIMP) + pc_checkskill(sd, SU_FRESHSHRIMP) + pc_checkskill(sd, SU_GROOMING) + pc_checkskill(sd, SU_PURRING) + pc_checkskill(sd, SU_SHRIMPARTY)) > 19) - bonus += 200; + bonus += 300; } if ((skill_lv = pc_checkskill(sd, NV_BREAKTHROUGH)) > 0) bonus += 30 * skill_lv + (skill_lv > 4 ? 50 : 0); From e1aed76410f305014cd84373ca81b046ed39d3c7 Mon Sep 17 00:00:00 2001 From: Atemo Date: Fri, 22 May 2020 15:10:26 +0200 Subject: [PATCH 045/141] Added enchant npc for temporal shoes slotted and ogh card exchange (#4985) --- npc/re/merchants/OldGlastHeim_merchants.txt | 257 ++++++++++++++++++++ 1 file changed, 257 insertions(+) diff --git a/npc/re/merchants/OldGlastHeim_merchants.txt b/npc/re/merchants/OldGlastHeim_merchants.txt index 01e8d47718..b9c2b5cd9c 100644 --- a/npc/re/merchants/OldGlastHeim_merchants.txt +++ b/npc/re/merchants/OldGlastHeim_merchants.txt @@ -339,3 +339,260 @@ S_Slot: } close; } + +// Temporal shoes slotted - npc enchant +glast_01,184,283,4 script Dark magic master#pa082 1_F_01,{ + disable_items; + if ((MaxWeight - Weight) < 1000) { + mes "Your bag is too heavy. Reduce some weight and come back."; + close; + } + if (checkweight(1201,1) == 0) { + mes "You seem to be carrying too many items. Put some items in storage and come back again."; + close; + } + // requirements + .@coagulated_id = 6608;// Coagulated_Spell + .@polluted_id = 6755;// Polluted_Spell + .@cost_zeny = 100000; + + mes "[Dark magic master]"; + mes "I deal with the Temporal Boots with socket only."; + next; + if (select( "How does the enchanting work?", "Give effect to my Temporal Boots" ) == 1) { + mes "[Dark magic master]"; + mes "You should ask Hugin's magic master all the basic things. I want deeper conversation about enchating work."; + next; + mes "[Dark magic master]"; + mes "I need 4 items. ^0000ff" + callfunc("F_InsertComma",.@cost_zeny) + ", " + getitemname(.@polluted_id) + ", " + getitemname(.@coagulated_id) + ", Temporal Boots(Socket) Series^000000. If you don't have any of these, I cannot help you."; + next; + mes "[Dark magic master]"; + mes "It's my small hobby.. To enchant Socketed Temporal Boots using polluted and coagulated spell."; + next; + mes "[Dark magic master]"; + mes "Of course, you must cope with bigger risk of destruction whenever you'd like to enchant again and again."; + next; + mes "[Dark magic master]"; + mes "The enchant order is same as Hugin's magic master's enchantment. Please refer to his explanation."; + close; + } + .@equip_id = getequipid(EQI_SHOES); + + switch(.@equip_id) { + case 22006: // Temporal_Str_Boots_ + case 22007: // Temporal_Vit_Boots_ + case 22008: // Temporal_Dex_Boots_ + case 22009: // Temporal_Int_Boots_ + case 22010: // Temporal_Agi_Boots_ + case 22011: // Temporal_Luk_Boots_ + case 22113: // Modified_Str_Boots_ + case 22114: // Modified_Int_Boots_ + case 22115: // Modified_Agi_Boots_ + case 22116: // Modified_Vit_Boots_ + case 22117: // Modified_Dex_Boots_ + case 22118: // Modified_Luk_Boots_ + break; + case -1: + mes "[Dark magic master]"; + mes "Are you wearing the item?"; + close; + default: + mes "[Dark magic master]"; + mes "This is not the right item for this enchant. Remember, only those 6 types of Temporal Boots with socket are available for hidden enchanting.."; + close; + } + // enchants ID + setarray .@enchant_1[0],4808,4832,4814,4741,4869,4752;// Fighting_Spirit4 Expert_Archer1 Spell2 Vitality2 DelayafterAttack1Lv Luck3 + setarray .@enchant_2[0],4820,4833,4813,4742,4872,4753;// Fighting_Spirit5 Expert_Archer2 Spell3 Vitality3 DelayafterAttack2Lv Luck4 + setarray .@enchant_3[0],4821,4834,4812,4861,4873,4754;// Fighting_Spirit6 Expert_Archer3 Spell4 MHP1 DelayafterAttack3Lv Luck5 + setarray .@enchant_4[0],4822,4835,4826,4862,4881,4755;// Fighting_Spirit7 Expert_Archer4 Spell5 MHP2 DelayafterAttack4Lv Luck6 + + // requirements amount + setarray .@en_amount_coag[0],3,10,20,40,50; + setarray .@en_amount_polluted[0],1,2,4,7,10; + + // (custom) chances of success (official value unknown) + setarray .@chances[0],100,70,70,70,70; + + // data of item equipped + .@equip_name$ = getequipname(EQI_SHOES); + setarray .@card[0], + getequipcardid(EQI_SHOES,0), + getequipcardid(EQI_SHOES,1), + getequipcardid(EQI_SHOES,2), + getequipcardid(EQI_SHOES,3); + copyarray .@equip_card[0], .@card[0], 4; + .@equip_refine = getequiprefinerycnt(EQI_SHOES); + + if (.@card[2] > 0) { + mes "[Dark magic master]"; + mes "These boots have already passed the enchanting limit. We can't enchant them any more."; + close; + } + if (.@card[3] == 0) {// 4th slot 1st try enchanting + .@amount_coag = .@en_amount_coag[0]; + .@amount_poll = .@en_amount_polluted[0]; + + mes "[Dark magic master]"; + mes "Want to enchant ^0000ff" + .@equip_name$ + " (Socket)^000000? For the 1st enchanting, you need ^0000ff" + .@amount_poll + " " + getitemname(.@polluted_id) + ", " + .@amount_coag + " " + getitemname(.@coagulated_id) + ", and " + callfunc("F_InsertComma",.@cost_zeny) + " zeny^000000."; + mes "It has some risk of failing.."; + next; + .@s = select( "Quit", "Fighting Spirit", "Archery", "Spell", "Vitality", "Attack Speed", "Lucky" ) - 2; + if (.@s == -1) { + mes "[Dark magic master]"; + mes "Ok, come back when you are ready."; + close; + } + .@card[3] = .@enchant_1[.@s]; + .@string$ = "enchant number ^6300001^000000."; + .@num = 0; + } + else { + for ( .@num = 1; .@num < 5; .@num++ ) { + for ( .@type = 0; .@type < 6 && .@card[3] != getd( ".@enchant_" + .@num + "[" + .@type + "]" ); .@type++ ) + continue; + if (.@type < 6) + break; + } + if (.@num == 5) { + mes "[Dark magic master]"; + mes "Something wrong happened."; + close; + } + .@amount_coag = .@en_amount_coag[.@num]; + .@amount_poll = .@en_amount_polluted[.@num]; + + mes "[Dark magic master]"; + if (.@num == 4) { + .@card[2] = callfunc("F_Rand",4875,4876,4877,4878,4879,4880);// Bear's_Power, Runaway_Magic, Speed_Of_Light, Muscle_Fool, Hawkeye, Lucky_Day + .@string$ = "^990000 Bonus effect ^000000 upgrade."; + mes "Would you like a random bonus effect for the 3rd slot? You need ^0000ff" + .@amount_poll + " " + getitemname(.@polluted_id) + ", " + .@amount_coag + " " + getitemname(.@coagulated_id) + ", and " + callfunc("F_InsertComma",.@cost_zeny) + " zeny^000000. But it has high risk of failing.."; + } + else { + .@level = .@num + 1; + .@card[3] = getd( ".@enchant_" + (.@num+1) + "[" + .@type + "]" ); + .@string$ = "enchant number ^990000" + .@level + "^000000."; + mes "Enchanting ^0000ff" + .@equip_name$ + " (Socket)^000000's 4th slot as ^0000ff" + .@level + "^000000 level effect. Requires ^0000ff" + .@amount_poll + " " + getitemname(.@polluted_id) + ", " + .@amount_coag + " " + getitemname(.@coagulated_id) + ", and " + callfunc("F_InsertComma",.@cost_zeny) + " z^000000. It has some risk of failing.."; + } + next; + if (select( "Quit", "Effect Upgrade!" ) == 1) { + mes "[Dark magic master]"; + mes "Ok, come back when you are ready."; + close; + } + } + if (countitem(.@coagulated_id) < .@amount_coag) { + mes "[Dark magic master]"; + mes "Hmm, you are missing " + (.@amount_coag - countitem(.@coagulated_id)) + " " + getitemname(.@coagulated_id) + " items. Go get more, and then we can talk about more enchants."; + close; + } + if (countitem(.@polluted_id) < .@amount_poll) { + mes "[Dark magic master]"; + mes "Hmm, you are missing " + (.@amount_poll - countitem(.@polluted_id)) + " " + getitemname(.@polluted_id) + " items. Go get more, and then we can talk about more enchants."; + close; + } + if (Zeny < .@cost_zeny) { + mes "[Dark magic master]"; + mes "You must bring ^0000ff" + callfunc("F_InsertComma",.@cost_zeny) + " zeny^000000. Nothing is free you know..."; + close; + } + delitem .@coagulated_id, .@amount_coag; + delitem .@polluted_id, .@amount_poll; + Zeny -= .@cost_zeny; + + // anti-hack + if (callfunc("F_IsEquipIDHack", EQI_SHOES, .@equip_id) || callfunc("F_IsEquipCardHack", EQI_SHOES, .@equip_card[0], .@equip_card[1], .@equip_card[2], .@equip_card[3]) || callfunc("F_IsEquipRefineHack", EQI_SHOES, .@equip_refine)) + close; + + delequip EQI_SHOES; + if (.@chances[.@num] < rand(1,100)) { + specialeffect2 EF_LORD; + mes "[Dark magic master]"; + mes "Arrggg, we failed. Better luck next time."; + close; + } + specialeffect2 EF_REPAIRWEAPON; + mes "[Hugin's Magician]"; + mes "Trying for " + .@string$; + getitem2 .@equip_id,1,1,.@equip_refine,0,0,0,.@card[2],.@card[3]; + close; +} + +// Items exchange for card +glast_01,216,292,5 script Portrait collector#0002 4_M_OILMAN,{ + disable_items; + if ((MaxWeight - Weight) < 1000) { + mes "Your bag is too heavy. Reduce some weight and come back."; + close; + } + if (checkweight(1201,1) == 0) { + mes "You seem to be carrying too many items. Put some items in storage and come back again."; + close; + } + .@coagulated_id = 6608; // Coagulated_Spell + .@polluted_id = 6755; // Polluted_Spell + .@white_card = 4608; // White_Knightage_Card + .@khalitzburg_card = 4609; // Khaliz_Knightage_Card + + mes "[Portrait collector]"; + mes "I have been collecting portraits for an year already. Now it's time to show people my collection."; + next; + mes "[Portrait collector]"; + mes "Hey, do you have a lot of " + getitemname(.@polluted_id) + " or " + getitemname(.@coagulated_id) + "? If so, why don't you trade a nice portrait that I have?"; + next; + switch( select( "Cancel", "Exchange for " + getitemname(.@white_card), "Exchange for " + getitemname(.@khalitzburg_card) ) ) { + case 1: + mes "[Portrait collector]"; + mes "Well, OK. Come back when you are ready."; + close; + case 2: + .@reward_id = .@white_card; + .@amount_coag = 3000; + .@amount_polluted = 70; + break; + case 3: + .@reward_id = .@khalitzburg_card; + .@amount_coag = 5000; + .@amount_polluted = 100; + break; + } + mes "[Portrait collector]"; + mes "What would you like in exchange for the " + getitemname(.@reward_id) + "?"; + next; + if (countitem(.@coagulated_id) < .@amount_coag) + .@color$[0] = "^666666"; + if (countitem(.@polluted_id) < .@amount_polluted) + .@color$[1] = "^666666"; + switch( select( "Cancel", sprintf("%s%s %s^000000", .@color$[0], callfunc("F_InsertComma",.@amount_coag), getitemname(.@coagulated_id)), sprintf("%s%s %s^000000", .@color$[1], callfunc("F_InsertComma",.@amount_polluted), getitemname(.@polluted_id)) ) ) { + case 1: + mes "[Portrait collector]"; + mes "Well, OK. Come back when you are ready."; + close; + case 2: + .@id = .@coagulated_id; + .@amount = .@amount_coag; + break; + case 3: + .@id = .@polluted_id; + .@amount = .@amount_polluted; + break; + } + if (countitem(.@id) < .@amount) { + mes "[Portrait collector]"; + mes "Hmm, you don't have enough Spell now. Go get more."; + close; + } + mes "[Portrait collector]"; + mes "Are you sure that you won't regret it?"; + next; + if (select( "Cancel", "Sure" ) == 1) { + mes "[Portrait collector]"; + mes "Well, OK. Come back when you are ready."; + close; + } + mes "[Portrait collector]"; + mes "Cool, let's make a deal."; + delitem .@id, .@amount; + getitem .@reward_id,1; + close; +} From 81a2b8c99571f56aac68ae0ede1e142d2c71bc65 Mon Sep 17 00:00:00 2001 From: aleos Date: Fri, 22 May 2020 09:18:52 -0400 Subject: [PATCH 046/141] Fixes NPC_DARKBREATH attack type * Follow up to 70d6876. * Fixes #4997. * The attack type for NPC_DARKBREATH will now properly read the database value. Thanks to @qwerty7vp! --- src/map/skill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 2646dd40bb..dc5094104e 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -5530,7 +5530,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint case NJ_ZENYNAGE: case GN_THORNS_TRAP: case RL_B_TRAP: - skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag); + skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); break; #ifdef RENEWAL case NJ_ISSEN: { From e6bffc11fe15f676873f4e4621e109a061732a17 Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Sun, 24 May 2020 14:05:26 +0200 Subject: [PATCH 047/141] Effect State Update --- src/map/script_constants.hpp | 1 + src/map/status.hpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/map/script_constants.hpp b/src/map/script_constants.hpp index 83fb621c11..a75f75ff4e 100644 --- a/src/map/script_constants.hpp +++ b/src/map/script_constants.hpp @@ -3503,6 +3503,7 @@ export_constant(EFST_MAGIC_POISON); export_constant(EFST_KAUTE); export_constant(EFST_JPNONLY_TACTICS); + export_constant(EFST_MADOGEAR); export_constant(EFST_LUXANIMA); export_constant(EFST_BATH_FOAM_A); export_constant(EFST_BATH_FOAM_B); diff --git a/src/map/status.hpp b/src/map/status.hpp index bbe61a84d4..10e7d44f0d 100644 --- a/src/map/status.hpp +++ b/src/map/status.hpp @@ -2034,6 +2034,8 @@ enum efst_types : short{ EFST_JPNONLY_TACTICS = 1147, + EFST_MADOGEAR = 1149, + EFST_LUXANIMA = 1154, EFST_BATH_FOAM_A, EFST_BATH_FOAM_B, From 14c388b40186ca2045d7e6c325e492968cff0ad7 Mon Sep 17 00:00:00 2001 From: Aleos Date: Mon, 25 May 2020 12:39:36 -0400 Subject: [PATCH 048/141] Resolves some Battleground issues (#4725) * Queue data will now be kept available until a Battleground event is over. * Players can now join an active Battleground. * Adds a more detailed queue state tracking feature. * When a battleground is being prepared and not enough players click accept, stop the battleground from beginning. * When a player logs off or leaves a queue they will now properly be removed from the queue. * Cleans up MinPlayers and MaxPlayers parsing to not accept values less than 1. * Resolves players not being able to join an active battleground (up to MaxPlayers) unless someone quit early. * Adds a team size priority check to avoid adding more players to a larger team. * Fixes battlechat messages. * Adds a battle_config to disable the ability for players on maps with MF_NOWARP to join Battlegrounds. * Adds a new optional ActiveEvent label to the database. * Fixes a typo in Flavius 2 during unbooking process. * Cleans up the behavior of the Battle Therapists to use areapercentheal and areawarp. * Fixes KVM score not properly set on the first Battleground. * Fixes the global timer checks not ending the main timers on Flavius and Tierra Gorge. * Added an extra bg_reserve on global timer checks in case someone tries to join the battleground just before it's ended. * Fixed OnReadyCheck being called on global timer checks, causing the battlegrounds to start when there were no players on the map. * Fixed a bug on Tierra Gorge where multiple barricade walls could be stacked. * Fixes Croix also receiving 9 badges when Guillaume wins. * Adjusts StartDelay default to 0. * Add a database setting to give the ability to enable or disable joining as an individual, party, or guild. * Add a database setting to restrict jobs from entering Battlegrounds. * Better documents script commands bg_reserve and bg_unbook. * Other fixes and cleanups. Thanks to @roSBK and @Daegaladh, @admkakaroto, @Artuvazro, and @Atemo! Co-authored-by: Daegaladh Co-authored-by: Atemo --- conf/battle/battleground.conf | 4 + conf/msg_conf/map_msg.conf | 3 +- db/battleground_db.yml | 145 +++--- db/import-tmpl/battleground_db.yml | 9 +- doc/script_commands.txt | 14 +- npc/battleground/flavius/flavius01.txt | 92 ++-- npc/battleground/flavius/flavius02.txt | 100 +++-- npc/battleground/kvm/kvm01.txt | 12 +- npc/battleground/kvm/kvm02.txt | 12 +- npc/battleground/kvm/kvm03.txt | 12 +- npc/battleground/tierra/tierra01.txt | 89 ++-- npc/battleground/tierra/tierra02.txt | 90 ++-- src/map/battle.cpp | 1 + src/map/battle.hpp | 1 + src/map/battleground.cpp | 587 +++++++++++++++++++------ src/map/battleground.hpp | 33 +- src/map/clif.cpp | 31 +- src/map/clif.hpp | 2 +- src/map/map.cpp | 2 +- src/map/pc.cpp | 55 ++- src/map/pc.hpp | 11 +- src/map/script.cpp | 11 +- 22 files changed, 940 insertions(+), 376 deletions(-) diff --git a/conf/battle/battleground.conf b/conf/battle/battleground.conf index f6ca66e30e..05537ea85f 100644 --- a/conf/battle/battleground.conf +++ b/conf/battle/battleground.conf @@ -30,3 +30,7 @@ bg_flee_penalty: 20 // Interval before updating the bg-member map mini-dots (milliseconds) bg_update_interval: 1000 + +// Before a player is warped into a Battleground from the Battleground Queue, +// check to see if the player's current map has MF_NOWARP. +bgqueue_nowarp_mapflag: no diff --git a/conf/msg_conf/map_msg.conf b/conf/msg_conf/map_msg.conf index 5cc08208ef..49ba23bfbe 100644 --- a/conf/msg_conf/map_msg.conf +++ b/conf/msg_conf/map_msg.conf @@ -354,9 +354,10 @@ 334: Total Domination // Battlegrounds Queue -337: You may not join a battleground queue when you're in a battleground map. +337: You can't apply to a battleground queue from this map. 338: You can't apply to a battleground queue due to recently deserting a battleground. Time remaining: %d minutes and %d seconds. 339: You can't apply to a battleground queue for %d seconds due to recently leaving one. +340: Participants were unable to join. Delaying entry for more participants. // Templates for @who output 343: Name: %s diff --git a/db/battleground_db.yml b/db/battleground_db.yml index 2dbed729ad..a86612801d 100644 --- a/db/battleground_db.yml +++ b/db/battleground_db.yml @@ -29,7 +29,12 @@ # MinLevel Minimum level required to join the battleground. (Default: 1) # MaxLevel Maximum level to join the battleground. (Default: MAX_LEVEL value) # Deserter Amount of time in seconds a player is marked deserter. (Default: 600) -# StartDelay Amount of time in seconds once a queue is filled before a start message is sent to players. (Default: 30) +# StartDelay Amount of time in seconds once a queue is filled before players are warped. (Default: 0) +# Join: Which application type is accepted. The entryqueuelist.lub can visually disable these options. +# Solo Able to join a queue as an individual. (Default: true) +# Party Able to join a queue as a party. (Default: true) +# Guild Able to join a queue as a guild. (Default: true) +# JobRestrictions List of jobs that are unable to join the battleground. # Locations: Battleground location settings. # - Map The map on which the battleground will be played. # StartEvent NPC event triggered when the battleground starts. @@ -38,12 +43,14 @@ # RespawnY Y coordinate for warping on death. # DeathEvent NPC event triggered when a player dies. # QuitEvent NPC event triggered when a player quits. +# ActiveEvent NPC event triggered when a player joints an active battleground. # Variable Name of BG ID variable used in the battleground script. # TeamB: TeamB settings. # RespawnX X coordinate for warping on death. # RespawnY Y coordinate for warping on death. # DeathEvent NPC event triggered when a player dies. # QuitEvent NPC event triggered when a player quits. +# ActiveEvent NPC event triggered when a player joints an active battleground. # Variable Name of BG ID variable used in the battleground script. ########################################################################### @@ -53,121 +60,151 @@ Header: Body: - Id: 1 - Name: "Tierra Gorge" + Name: Tierra Gorge MinPlayers: 6 MinLevel: 80 + JobRestrictions: + Novice: true + SuperNovice: true + Novice_High: true + Baby: true + Super_Baby: true + Super_Novice_E: true + Super_Baby_E: true Locations: - - Map: "bat_a01" - StartEvent: "start#bat_a01::OnReadyCheck" + - Map: bat_a01 + StartEvent: start#bat_a01::OnReadyCheck TeamA: RespawnX: 50 RespawnY: 374 - QuitEvent: "start#bat_a01::OnGuillaumeQuit" - Variable: "$@TierraBG1_id1" + QuitEvent: start#bat_a01::OnGuillaumeQuit + ActiveEvent: start#bat_a01::OnGuillaumeActive + Variable: $@TierraBG1_id1 TeamB: RespawnX: 42 RespawnY: 16 - QuitEvent: "start#bat_a01::OnCroixQuit" - Variable: "$@TierraBG1_id2" - - Map: "bat_a02" - StartEvent: "start#bat_a02::OnReadyCheck" + QuitEvent: start#bat_a01::OnCroixQuit + ActiveEvent: start#bat_a01::OnCroixActive + Variable: $@TierraBG1_id2 + - Map: bat_a02 + StartEvent: start#bat_a02::OnReadyCheck TeamA: RespawnX: 50 RespawnY: 374 - QuitEvent: "start#bat_a02::OnGuillaumeQuit" - Variable: "$@TierraBG2_id1" + QuitEvent: start#bat_a02::OnGuillaumeQuit + ActiveEvent: start#bat_a02::OnGuillaumeActive + Variable: $@TierraBG2_id1 TeamB: RespawnX: 42 RespawnY: 16 - QuitEvent: "start#bat_a02::OnCroixQuit" - Variable: "$@TierraBG2_id2" + QuitEvent: start#bat_a02::OnCroixQuit + ActiveEvent: start#bat_a02::OnCroixActive + Variable: $@TierraBG2_id2 - Id: 2 - Name: "Flavius" + Name: Flavius MinPlayers: 6 MinLevel: 80 + JobRestrictions: + Novice: true + SuperNovice: true + Novice_High: true + Baby: true + Super_Baby: true + Super_Novice_E: true + Super_Baby_E: true Locations: - - Map: "bat_b01" - StartEvent: "start#bat_b01::OnReadyCheck" + - Map: bat_b01 + StartEvent: start#bat_b01::OnReadyCheck TeamA: RespawnX: 10 RespawnY: 290 - QuitEvent: "start#bat_b01::OnGuillaumeQuit" - Variable: "$@FlaviusBG1_id1" + QuitEvent: start#bat_b01::OnGuillaumeQuit + ActiveEvent: start#bat_b01::OnGuillaumeActive + Variable: $@FlaviusBG1_id1 TeamB: RespawnX: 390 RespawnY: 10 - QuitEvent: "start#bat_b01::OnCroixQuit" - Variable: "$@FlaviusBG1_id2" - - Map: "bat_b02" - StartEvent: "start#bat_b02::OnReadyCheck" + QuitEvent: start#bat_b01::OnCroixQuit + ActiveEvent: start#bat_b01::OnCroixActive + Variable: $@FlaviusBG1_id2 + - Map: bat_b02 + StartEvent: start#bat_b02::OnReadyCheck TeamA: RespawnX: 10 RespawnY: 290 - QuitEvent: "start#bat_b02::OnGuillaumeQuit" - Variable: "$@FlaviusBG2_id1" + QuitEvent: start#bat_b02::OnGuillaumeQuit + ActiveEvent: start#bat_b02::OnGuillaumeActive + Variable: $@FlaviusBG2_id1 TeamB: RespawnX: 390 RespawnY: 10 - QuitEvent: "start#bat_b02::OnCroixQuit" - Variable: "$@FlaviusBG2_id2" + QuitEvent: start#bat_b02::OnCroixQuit + ActiveEvent: start#bat_b02::OnCroixActive + Variable: $@FlaviusBG2_id2 - Id: 3 - Name: "KVM (Level 80 and up)" + Name: KVM (Level 80 and up) MinPlayers: 5 MinLevel: 80 Locations: - - Map: "bat_c01" - StartEvent: "KvM01_BG::OnStart" + - Map: bat_c01 + StartEvent: KvM01_BG::OnStart TeamA: RespawnX: 52 RespawnY: 129 - DeathEvent: "KvM01_BG::OnGuillaumeDie" - QuitEvent: "KvM01_BG::OnGuillaumeQuit" - Variable: "$@KvM01BG_id1" + DeathEvent: KvM01_BG::OnGuillaumeDie + QuitEvent: KvM01_BG::OnGuillaumeQuit + ActiveEvent: KvM01_BG::OnGuillaumeActive + Variable: $@KvM01BG_id1 TeamB: RespawnX: 147 RespawnY: 55 - DeathEvent: "KvM01_BG::OnCroixDie" - QuitEvent: "KvM01_BG::OnCroixQuit" - Variable: "$@KvM01BG_id2" + DeathEvent: KvM01_BG::OnCroixDie + QuitEvent: KvM01_BG::OnCroixQuit + ActiveEvent: KvM01_BG::OnCroixActive + Variable: $@KvM01BG_id2 - Id: 4 - Name: "KVM (Level 60~79)" + Name: KVM (Level 60~79) MinPlayers: 5 MinLevel: 60 MaxLevel: 79 Locations: - - Map: "bat_c02" - StartEvent: "KvM02_BG::OnStart" + - Map: bat_c02 + StartEvent: KvM02_BG::OnStart TeamA: RespawnX: 52 RespawnY: 129 - DeathEvent: "KvM02_BG::OnGuillaumeDie" - QuitEvent: "KvM02_BG::OnGuillaumeQuit" - Variable: "$@KvM02BG_id1" + DeathEvent: KvM02_BG::OnGuillaumeDie + QuitEvent: KvM02_BG::OnGuillaumeQuit + ActiveEvent: KvM02_BG::OnGuillaumeActive + Variable: $@KvM02BG_id1 TeamB: RespawnX: 147 RespawnY: 55 - DeathEvent: "KvM02_BG::OnCroixDie" - QuitEvent: "KvM02_BG::OnCroixQuit" - Variable: "$@KvM02BG_id2" + DeathEvent: KvM02_BG::OnCroixDie + QuitEvent: KvM02_BG::OnCroixQuit + ActiveEvent: KvM02_BG::OnCroixActive + Variable: $@KvM02BG_id2 - Id: 5 - Name: "KVM (Level 59 and below" + Name: KVM (Level 59 and below MinPlayers: 5 MaxLevel: 59 Locations: - - Map: "bat_c03" - StartEvent: "KvM03_BG::OnStart" + - Map: bat_c03 + StartEvent: KvM03_BG::OnStart TeamA: RespawnX: 52 RespawnY: 129 - DeathEvent: "KvM03_BG::OnGuillaumeDie" - QuitEvent: "KvM03_BG::OnGuillaumeQuit" - Variable: "$@KvM03BG_id1" + DeathEvent: KvM03_BG::OnGuillaumeDie + QuitEvent: KvM03_BG::OnGuillaumeQuit + ActiveEvent: KvM03_BG::OnGuillaumeActive + Variable: $@KvM03BG_id1 TeamB: RespawnX: 147 RespawnY: 55 - DeathEvent: "KvM03_BG::OnCroixDie" - QuitEvent: "KvM03_BG::OnCroixQuit" - Variable: "$@KvM03BG_id2" + DeathEvent: KvM03_BG::OnCroixDie + QuitEvent: KvM03_BG::OnCroixQuit + ActiveEvent: KvM03_BG::OnCroixActive + Variable: $@KvM03BG_id2 Footer: Imports: diff --git a/db/import-tmpl/battleground_db.yml b/db/import-tmpl/battleground_db.yml index 9d7300bdd7..6b727c1ea2 100644 --- a/db/import-tmpl/battleground_db.yml +++ b/db/import-tmpl/battleground_db.yml @@ -29,7 +29,12 @@ # MinLevel Minimum level required to join the battleground. (Default: 1) # MaxLevel Maximum level to join the battleground. (Default: MAX_LEVEL value) # Deserter Amount of time in seconds a player is marked deserter. (Default: 600) -# StartDelay Amount of time in seconds once a queue is filled before a start message is sent to players. (Default: 30) +# StartDelay Amount of time in seconds once a queue is filled before players are warped. (Default: 0) +# Join: Which application type is accepted. The entryqueuelist.lub can visually disable these options. +# Solo Able to join a queue as an individual. (Default: true) +# Party Able to join a queue as a party. (Default: true) +# Guild Able to join a queue as a guild. (Default: true) +# JobRestrictions List of jobs that are unable to join the battleground. # Locations: Battleground location settings. # - Map The map on which the battleground will be played. # StartEvent NPC event triggered when the battleground starts. @@ -38,12 +43,14 @@ # RespawnY Y coordinate for warping on death. # DeathEvent NPC event triggered when a player dies. # QuitEvent NPC event triggered when a player quits. +# ActiveEvent NPC event triggered when a player joints an active battleground. # Variable Name of BG ID variable used in the battleground script. # TeamB: TeamB settings. # RespawnX X coordinate for warping on death. # RespawnY Y coordinate for warping on death. # DeathEvent NPC event triggered when a player dies. # QuitEvent NPC event triggered when a player quits. +# ActiveEvent NPC event triggered when a player joints an active battleground. # Variable Name of BG ID variable used in the battleground script. ########################################################################### diff --git a/doc/script_commands.txt b/doc/script_commands.txt index a6b716f4d3..ecd78cd2ee 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -9393,19 +9393,19 @@ Example: --------------------------------------- -*bg_reserve(""); +*bg_reserve(""{,}); -Reserves a slot for the given Battleground for the Battleground UI System. +Reserves a Battleground map for the Battleground UI System. When a map is booked it prevents another similar +queue from being created and will allow players to join an active Battlegrounds event. -Note: 'bg_reserve' and 'bg_unbook' prevent the Battlegrounds queue from joining players in an active Battleground. +If is true, then the Battleground is marked as over to prevent new players from joining. This state is meant +for the period where players can get their Badges. --------------------------------------- -*bg_unbook(""); +*bg_unbook(""); -Removes a spot for the given Battleground for the Battleground UI System. - -Note: 'bg_reserve' and 'bg_unbook' prevent the Battlegrounds queue from joining players in an active Battleground. +Removes a Battleground map for the Battleground UI System. When a map is unbooked it allows a queue to be created. --------------------------------------- diff --git a/npc/battleground/flavius/flavius01.txt b/npc/battleground/flavius/flavius01.txt index e4f7c93420..74a3be178a 100644 --- a/npc/battleground/flavius/flavius01.txt +++ b/npc/battleground/flavius/flavius01.txt @@ -58,25 +58,21 @@ OnStop: OnTimer1000: stopnpctimer; - if (!getbattleflag("feature.bgqueue")) - initnpctimer; - set .@chk_bat_a01,getmapusers("bat_b01"); - if (.@chk_bat_a01 < 1) { - set $@FlaviusBG1, 0; - if( $@FlaviusBG1_id1 ) { bg_destroy $@FlaviusBG1_id1; set $@FlaviusBG1_id1, 0; } - if( $@FlaviusBG1_id2 ) { bg_destroy $@FlaviusBG1_id2; set $@FlaviusBG1_id2, 0; } - if (getbattleflag("feature.bgqueue")) { + if (bg_get_data($@FlaviusBG1_id1, 0) == 0 && bg_get_data($@FlaviusBG1_id2, 0) == 0) { + donpcevent "countdown#bat_b01::OnStop"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_b01", true; + $@FlaviusBG1 = 0; + if( $@FlaviusBG1_id1 ) { bg_destroy $@FlaviusBG1_id1; $@FlaviusBG1_id1 = 0; } + if( $@FlaviusBG1_id2 ) { bg_destroy $@FlaviusBG1_id2; $@FlaviusBG1_id2 = 0; } + if (getbattleflag("feature.bgqueue")) bg_unbook "bat_b01"; - end; - } else - donpcevent "start#bat_b01::OnReadyCheck"; + end; } - if (getbattleflag("feature.bgqueue")) - initnpctimer; + initnpctimer; end; } - // Flavius Battleground Engine //============================================================ bat_b01,15,15,3 script start#bat_b01 844,{ @@ -139,13 +135,14 @@ OnReset: donpcevent "guardian#bat_b01_b::OnEnable"; donpcevent "cell#bat_b01_a::OnRed"; donpcevent "cell#bat_b01_b::OnRed"; - donpcevent "time#bat_b01::OnEnable"; - disablenpc "Guillaume Vintenar#b01_a"; - disablenpc "Croix Vintenar#b01_b"; - disablenpc "Vintenar#bat_b01_aover"; - disablenpc "Vintenar#bat_b01_bover"; - bg_warp $@FlaviusBG1_id1,"bat_b01",87,75; - bg_warp $@FlaviusBG1_id2,"bat_b01",311,224; + end; + +OnGuillaumeActive: + warp "bat_b01",87,75; + end; + +OnCroixActive: + warp "bat_b01",311,224; end; OnGuillaumeQuit: @@ -181,6 +178,8 @@ OnMyMobDead: enablenpc "Guillaume Vintenar#b01_a"; enablenpc "Croix Vintenar#b01_b"; donpcevent "time#bat_b01::OnStop"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_b01", true; } else { set $@Croix_ScoreBG1,1; @@ -215,6 +214,8 @@ OnMyMobDead: enablenpc "Guillaume Vintenar#b01_a"; enablenpc "Croix Vintenar#b01_b"; donpcevent "time#bat_b01::OnStop"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_b01", true; } else { set $@Guill_ScoreBG1,1; @@ -314,12 +315,14 @@ bat_b01,10,294,3 script Battle Therapist#b01_a 95,{ OnTimer25000: specialeffect EF_SANCTUARY; - enablenpc "bat_b01_rp1_a_warp"; + // enablenpc "bat_b01_rp1_a_warp"; + areapercentheal "bat_b01",0,280,20,300,100,100; + areawarp "bat_b01",0,280,20,300,"bat_b01",87,73; end; -OnTimer26000: - disablenpc "bat_b01_rp1_a_warp"; - end; +// OnTimer26000: + // disablenpc "bat_b01_rp1_a_warp"; + // end; OnTimer26500: stopnpctimer; @@ -332,22 +335,25 @@ OnEnable: end; OnStop: - disablenpc "bat_b01_rp1_a_warp"; + // disablenpc "bat_b01_rp1_a_warp"; disablenpc "Battle Therapist#b01_a"; stopnpctimer; end; } +/* +// replaced by areapercentheal and areawarp to prevent enqueue issue bat_b01,10,290,0 script bat_b01_rp1_a_warp 45,10,10,{ OnInit: disablenpc "bat_b01_rp1_a_warp"; end; -OnTouch_: +OnTouch: percentheal 100,100; warp "bat_b01",87,73; end; } +*/ bat_b01,389,14,3 script Battle Therapist#b01_b 95,{ specialeffect2 EF_HEAL; @@ -359,12 +365,14 @@ bat_b01,389,14,3 script Battle Therapist#b01_b 95,{ OnTimer25000: specialeffect EF_SANCTUARY; - enablenpc "bat_b01_rp1_b_warp"; + // enablenpc "bat_b01_rp1_b_warp"; + areapercentheal "bat_b01",379,0,399,20,100,100; + areawarp "bat_b01",379,0,399,20,"bat_b01",312,225; end; -OnTimer26000: - disablenpc "bat_b01_rp1_b_warp"; - end; +// OnTimer26000: + // disablenpc "bat_b01_rp1_b_warp"; + // end; OnTimer26500: stopnpctimer; @@ -377,15 +385,16 @@ OnEnable: end; OnStop: - disablenpc "bat_b01_rp1_b_warp"; + // disablenpc "bat_b01_rp1_b_warp"; disablenpc "Battle Therapist#b01_b"; stopnpctimer; end; } -bat_b01,389,10,0 script bat_b01_rp1_b_warp 45,9,9,{ +/* +bat_b01,389,10,0 script bat_b01_rp1_b_warp 45,10,10,{ OnInit: - disablenpc "bat_b01_rp1_a_warp"; + disablenpc "bat_b01_rp1_b_warp"; end; OnTouch: @@ -393,6 +402,7 @@ OnTouch: warp "bat_b01",312,225; end; } +*/ bat_b01,87,76,0 script A_CODE#bat_b01 -1,5,5,{ OnTouch: @@ -490,6 +500,8 @@ OnTimer1830000: bg_warp $@FlaviusBG1_id2,"bat_b01",390,10; enablenpc "Vintenar#bat_b01_aover"; enablenpc "Vintenar#bat_b01_bover"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_b01", true; end; OnTimer1900000: @@ -526,11 +538,11 @@ bat_b01,10,294,3 script Vintenar#bat_b01_aover 419,{ set .@A_B_gap,$@Guill_ScoreBG1 - $@Croix_ScoreBG1; if ($@FlaviusBG1_id1 == getcharid(4)) { if (.@A_B_gap > 0) - callfunc "F_BG_Badge",1,"Guillaume","Flavius"; + callfunc "F_BG_Badge",1,"Guillaume","Flavius"; //Guillaume wins else if (.@A_B_gap == 0) - callfunc "F_BG_Badge",0,"Guillaume","Flavius"; + callfunc "F_BG_Badge",0,"Guillaume","Flavius"; //Tie else - callfunc "F_BG_Badge",0,"Guillaume","Flavius"; + callfunc "F_BG_Badge",0,"Guillaume","Flavius"; //Croix wins } else { mes "[Axl Rose]"; @@ -551,11 +563,11 @@ bat_b01,389,14,3 script Vintenar#bat_b01_bover 415,{ set .@A_B_gap,$@Guill_ScoreBG1 - $@Croix_ScoreBG1; if ($@FlaviusBG1_id2 == getcharid(4)) { if (.@A_B_gap > 0) - callfunc "F_BG_Badge",1,"Croix","Flavius"; + callfunc "F_BG_Badge",0,"Croix","Flavius"; //Guillaume wins else if (.@A_B_gap == 0) - callfunc "F_BG_Badge",0,"Croix","Flavius"; + callfunc "F_BG_Badge",0,"Croix","Flavius"; //Tie else - callfunc "F_BG_Badge",1,"Croix","Flavius"; + callfunc "F_BG_Badge",1,"Croix","Flavius"; //Croix wins } else { mes "[Swandery]"; diff --git a/npc/battleground/flavius/flavius02.txt b/npc/battleground/flavius/flavius02.txt index c99bef9d34..875c0b60a1 100644 --- a/npc/battleground/flavius/flavius02.txt +++ b/npc/battleground/flavius/flavius02.txt @@ -59,21 +59,18 @@ OnStop: OnTimer1000: stopnpctimer; - if (!getbattleflag("feature.bgqueue")) - initnpctimer; - set .@chk_bat_a01,getmapusers("bat_b02"); - if (.@chk_bat_a01 < 1) { - set $@FlaviusBG2, 0; - if( $@FlaviusBG2_id1 ) { bg_destroy $@FlaviusBG2_id1; set $@FlaviusBG2_id1, 0; } - if( $@FlaviusBG2_id2 ) { bg_destroy $@FlaviusBG2_id2; set $@FlaviusBG2_id2, 0; } - if (getbattleflag("feature.bgqueue")) { - bg_unbook "bat_b01"; - end; - } else - donpcevent "start#bat_b01::OnReadyCheck"; + if (bg_get_data($@FlaviusBG2_id1, 0) == 0 && bg_get_data($@FlaviusBG2_id2, 0) == 0) { + donpcevent "countdown#bat_b02::OnStop"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_b02", true; + $@FlaviusBG2 = 0; + if( $@FlaviusBG2_id1 ) { bg_destroy $@FlaviusBG2_id1; $@FlaviusBG2_id1 = 0; } + if( $@FlaviusBG2_id2 ) { bg_destroy $@FlaviusBG2_id2; $@FlaviusBG2_id2 = 0; } + if (getbattleflag("feature.bgqueue")) + bg_unbook "bat_b02"; + end; } - if (getbattleflag("feature.bgqueue")) - initnpctimer; + initnpctimer; end; } @@ -89,7 +86,7 @@ OnReadyCheck: if( $@FlaviusBG2 ) end; if (!getbattleflag("feature.bgqueue")) { - set .@Guillaume, getwaitingroomstate(0,"Lieutenant Huvas"); + set .@Guillaume, getwaitingroomstate(0,"Lieutenant Huvas"); set .@Croix, getwaitingroomstate(0,"Lieutenant Yukon"); if( !.@Guillaume && !.@Croix ) { donpcevent "#bat_b02_timer::OnStop"; @@ -102,8 +99,8 @@ OnReadyCheck: set $@FlaviusBG2_Victory, 0; set $@Croix_ScoreBG2, 0; set $@Guill_ScoreBG2, 0; - bg_updatescore "bat_b02",$@Guill_ScoreBG2,$@Croix_ScoreBG2; + if (!getbattleflag("feature.bgqueue")) { donpcevent "Lieutenant Huvas::OnEnterBG"; donpcevent "Lieutenant Yukon::OnEnterBG"; @@ -140,13 +137,14 @@ OnReset: donpcevent "guardian#bat_b02_b::OnEnable"; donpcevent "cell#bat_b02_a::OnRed"; donpcevent "cell#bat_b02_b::OnRed"; - donpcevent "time#bat_b02::OnEnable"; - disablenpc "Guillaume Vintenar#b02_a"; - disablenpc "Croix Vintenar#b02_b"; - disablenpc "Vintenar#bat_b02_aover"; - disablenpc "Vintenar#bat_b02_bover"; - bg_warp $@FlaviusBG2_id1,"bat_b02",87,75; - bg_warp $@FlaviusBG2_id2,"bat_b02",311,224; + end; + +OnGuillaumeActive: + warp "bat_b02",87,75; + end; + +OnCroixActive: + warp "bat_b02",311,224; end; OnGuillaumeQuit: @@ -182,6 +180,8 @@ OnMyMobDead: enablenpc "Guillaume Vintenar#b02_a"; enablenpc "Croix Vintenar#b02_b"; donpcevent "time#bat_b02::OnStop"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_b02", true; } else { set $@Croix_ScoreBG2,1; @@ -216,6 +216,8 @@ OnMyMobDead: enablenpc "Guillaume Vintenar#b02_a"; enablenpc "Croix Vintenar#b02_b"; donpcevent "time#bat_b02::OnStop"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_b02", true; } else { set $@Guill_ScoreBG2,1; @@ -315,12 +317,14 @@ bat_b02,10,294,3 script Battle Therapist#b02_a 95,{ OnTimer25000: specialeffect EF_SANCTUARY; - enablenpc "bat_b02_rp1_a_warp"; + // enablenpc "bat_b02_rp1_a_warp"; + areapercentheal "bat_b02",0,280,20,300,100,100; + areawarp "bat_b02",0,280,20,300,"bat_b02",87,73; end; -OnTimer26000: - disablenpc "bat_b02_rp1_a_warp"; - end; +// OnTimer26000: + // disablenpc "bat_b02_rp1_a_warp"; + // end; OnTimer26500: stopnpctimer; @@ -333,22 +337,24 @@ OnEnable: end; OnStop: - disablenpc "bat_b02_rp1_a_warp"; + // disablenpc "bat_b02_rp1_a_warp"; disablenpc "Battle Therapist#b02_a"; stopnpctimer; end; } +/* bat_b02,10,290,0 script bat_b02_rp1_a_warp 45,10,10,{ OnInit: disablenpc "bat_b02_rp1_a_warp"; end; -OnTouch_: +OnTouch: percentheal 100,100; warp "bat_b02",87,73; end; } +*/ bat_b02,389,14,3 script Battle Therapist#b02_b 95,{ specialeffect2 EF_HEAL; @@ -360,12 +366,14 @@ bat_b02,389,14,3 script Battle Therapist#b02_b 95,{ OnTimer25000: specialeffect EF_SANCTUARY; - enablenpc "bat_b02_rp1_b_warp"; + areapercentheal "bat_b02",379,0,399,20,100,100; + areawarp "bat_b02",379,0,399,20,"bat_b02",312,225; + // enablenpc "bat_b02_rp1_b_warp"; end; -OnTimer26000: - disablenpc "bat_b02_rp1_b_warp"; - end; +// OnTimer26000: + // disablenpc "bat_b02_rp1_b_warp"; + // end; OnTimer26500: stopnpctimer; @@ -378,15 +386,16 @@ OnEnable: end; OnStop: - disablenpc "bat_b02_rp1_b_warp"; + // disablenpc "bat_b02_rp1_b_warp"; disablenpc "Battle Therapist#b02_b"; stopnpctimer; end; } -bat_b02,389,10,0 script bat_b02_rp1_b_warp 45,9,9,{ +/* +bat_b02,389,10,0 script bat_b02_rp1_b_warp 45,10,10,{ OnInit: - disablenpc "bat_b02_rp1_a_warp"; + disablenpc "bat_b02_rp1_b_warp"; end; OnTouch: @@ -394,18 +403,19 @@ OnTouch: warp "bat_b02",312,225; end; } +*/ bat_b02,87,76,0 script A_CODE#bat_b02 -1,5,5,{ OnTouch: if (checkquest(2070) < 0) - //setquest 2070; + setquest 2070; end; } bat_b02,312,224,0 script B_CODE#bat_b02 -1,5,5,{ OnTouch: if (checkquest(2070) < 0) - //setquest 2070; + setquest 2070; end; } @@ -491,6 +501,8 @@ OnTimer1830000: bg_warp $@FlaviusBG2_id2,"bat_b02",390,10; enablenpc "Vintenar#bat_b02_aover"; enablenpc "Vintenar#bat_b02_bover"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_b02", true; end; OnTimer1900000: @@ -527,11 +539,11 @@ bat_b02,10,294,3 script Vintenar#bat_b02_aover 419,{ set .@A_B_gap,$@Guill_ScoreBG2 - $@Croix_ScoreBG2; if ($@FlaviusBG2_id1 == getcharid(4)) { if (.@A_B_gap > 0) - callfunc "F_BG_Badge",1,"Guillaume","Flavius"; + callfunc "F_BG_Badge",1,"Guillaume","Flavius"; //Guillaume wins else if (.@A_B_gap == 0) - callfunc "F_BG_Badge",0,"Guillaume","Flavius"; + callfunc "F_BG_Badge",0,"Guillaume","Flavius"; //Tie else - callfunc "F_BG_Badge",0,"Guillaume","Flavius"; + callfunc "F_BG_Badge",0,"Guillaume","Flavius"; //Croix wins } else { mes "[Axl Rose]"; @@ -552,11 +564,11 @@ bat_b02,389,14,3 script Vintenar#bat_b02_bover 415,{ set .@A_B_gap,$@Guill_ScoreBG2 - $@Croix_ScoreBG2; if ($@FlaviusBG2_id2 == getcharid(4)) { if (.@A_B_gap > 0) - callfunc "F_BG_Badge",1,"Croix","Flavius"; + callfunc "F_BG_Badge",0,"Croix","Flavius"; //Guillaume wins else if (.@A_B_gap == 0) - callfunc "F_BG_Badge",0,"Croix","Flavius"; + callfunc "F_BG_Badge",0,"Croix","Flavius"; //Tie else - callfunc "F_BG_Badge",1,"Croix","Flavius"; + callfunc "F_BG_Badge",1,"Croix","Flavius"; //Croix wins } else { mes "[Swandery]"; diff --git a/npc/battleground/kvm/kvm01.txt b/npc/battleground/kvm/kvm01.txt index 0341aa465d..2c8e07aeb7 100644 --- a/npc/battleground/kvm/kvm01.txt +++ b/npc/battleground/kvm/kvm01.txt @@ -155,6 +155,14 @@ OnCroixDie: } end; +OnGuillaumeActive: + warp "bat_c01",61,120; + end; + +OnCroixActive: + warp "bat_c01",138,63; + end; + OnStart: disablenpc "KVM Officer#KVM01A"; disablenpc "KVM Officer#KVM01B"; @@ -224,6 +232,7 @@ OnTimer61000: end; } } + bg_updatescore "bat_c01",.Guillaume_Count,.Croix_Count; set $@KvM01BG, 2; // Playing bg_warp $@KvM01BG_id1,"bat_c01",61,120; bg_warp $@KvM01BG_id2,"bat_c01",138,63; @@ -292,6 +301,8 @@ OnStop: bg_warp $@KvM01BG_id1,"bat_c01",53,128; bg_warp $@KvM01BG_id2,"bat_c01",146,55; donpcevent "KvM01_BG_Out::OnBegin"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_c01", true; end; } @@ -333,7 +344,6 @@ OnTimer60000: disablenpc "KVM Officer#KVM01B"; mapwarp "bat_c01","bat_room",154,150; maprespawnguildid "bat_c01",0,3; // Just in case someone else - bg_updatescore "bat_c01",5,5; set $@KvM01BG, 0; OnGuillaumeJoin: diff --git a/npc/battleground/kvm/kvm02.txt b/npc/battleground/kvm/kvm02.txt index 97a15be08d..b2ec1d5cc8 100644 --- a/npc/battleground/kvm/kvm02.txt +++ b/npc/battleground/kvm/kvm02.txt @@ -155,6 +155,14 @@ OnCroixDie: } end; +OnGuillaumeActive: + warp "bat_c02",62,119; + end; + +OnCroixActive: + warp "bat_c02",137,64; + end; + OnStart: disablenpc "KVM Officer#KVM02A"; disablenpc "KVM Officer#KVM02B"; @@ -224,6 +232,7 @@ OnTimer61000: end; } } + bg_updatescore "bat_c02",.Guillaume_Count,.Croix_Count; set $@KvM02BG, 2; // Playing bg_warp $@KvM02BG_id1,"bat_c02",62,119; bg_warp $@KvM02BG_id2,"bat_c02",137,64; @@ -292,6 +301,8 @@ OnStop: bg_warp $@KvM02BG_id1,"bat_c02",53,128; bg_warp $@KvM02BG_id2,"bat_c02",146,55; donpcevent "KvM02_BG_Out::OnBegin"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_c02", true; end; } @@ -333,7 +344,6 @@ OnTimer60000: disablenpc "KVM Officer#KVM02B"; mapwarp "bat_c02","bat_room",154,150; maprespawnguildid "bat_c02",0,3; // Just in case someone else - bg_updatescore "bat_c02",5,5; set $@KvM02BG, 0; OnGuillaumeJoin: diff --git a/npc/battleground/kvm/kvm03.txt b/npc/battleground/kvm/kvm03.txt index 6e15648916..c27df35dc7 100644 --- a/npc/battleground/kvm/kvm03.txt +++ b/npc/battleground/kvm/kvm03.txt @@ -155,6 +155,14 @@ OnCroixDie: } end; +OnGuillaumeActive: + warp "bat_c03",62,119; + end; + +OnCroixActive: + warp "bat_c03",137,64; + end; + OnStart: disablenpc "KVM Officer#KVM03A"; disablenpc "KVM Officer#KVM03B"; @@ -224,6 +232,7 @@ OnTimer61000: end; } } + bg_updatescore "bat_c03",.Guillaume_Count,.Croix_Count; set $@KvM03BG, 2; // Playing bg_warp $@KvM03BG_id1,"bat_c03",62,119; bg_warp $@KvM03BG_id2,"bat_c03",137,64; @@ -292,6 +301,8 @@ OnStop: bg_warp $@KvM03BG_id1,"bat_c03",53,128; bg_warp $@KvM03BG_id2,"bat_c03",146,55; donpcevent "KvM03_BG_Out::OnBegin"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_c03", true; end; } @@ -333,7 +344,6 @@ OnTimer60000: disablenpc "KVM Officer#KVM03B"; mapwarp "bat_c03","bat_room",154,150; maprespawnguildid "bat_c03",0,3; // Just in case someone else - bg_updatescore "bat_c03",5,5; set $@KvM03BG, 0; OnGuillaumeJoin: diff --git a/npc/battleground/tierra/tierra01.txt b/npc/battleground/tierra/tierra01.txt index dd0282789e..ce61f0c641 100644 --- a/npc/battleground/tierra/tierra01.txt +++ b/npc/battleground/tierra/tierra01.txt @@ -58,21 +58,18 @@ OnStop: OnTimer1000: stopnpctimer; - if (!getbattleflag("feature.bgqueue")) - initnpctimer; - set .@chk_bat_a01,getmapusers("bat_a01"); - if (.@chk_bat_a01 < 1) { - set $@TierraBG1,0; set $@TierraBG1_Victory, 0; - if( $@TierraBG1_id1 ) { bg_destroy $@TierraBG1_id1; set $@TierraBG1_id1, 0; } - if( $@TierraBG1_id2 ) { bg_destroy $@TierraBG1_id2; set $@TierraBG1_id2, 0; } - if (getbattleflag("feature.bgqueue")) { + if (bg_get_data($@TierraBG1_id1, 0) == 0 && bg_get_data($@TierraBG1_id2, 0) == 0) { + donpcevent "countdown#bat_a01::OnStop"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_a01", true; + $@TierraBG1 = 0; $@TierraBG1_Victory = 0; + if( $@TierraBG1_id1 ) { bg_destroy $@TierraBG1_id1; $@TierraBG1_id1 = 0; } + if( $@TierraBG1_id2 ) { bg_destroy $@TierraBG1_id2; $@TierraBG1_id2 = 0; } + if (getbattleflag("feature.bgqueue")) bg_unbook "bat_a01"; - end; - } else - donpcevent "start#bat_a01::OnReadyCheck"; + end; } - if (getbattleflag("feature.bgqueue")) - initnpctimer; + initnpctimer; end; } @@ -126,6 +123,14 @@ OnEnable: disablenpc "Croix Vintenar#a01_b"; end; +OnGuillaumeActive: + warp "bat_a01",352,342; + end; + +OnCroixActive: + warp "bat_a01",353,52; + end; + OnGuillaumeQuit: OnCroixQuit: if (getbattleflag("feature.bgqueue")) @@ -183,6 +188,8 @@ OnMyMobDead: mapannounce "bat_a01", "Croix Vintenar Swandery: We destroyed Guillaume's Food Storage. We won that! Wow!",bc_map,"0xFFCE00"; bg_warp $@TierraBG1_id1,"bat_a01",50,374; bg_warp $@TierraBG1_id2,"bat_a01",42,16; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_a01", true; } end; } @@ -206,6 +213,8 @@ OnMyMobDead: mapannounce "bat_a01", "Guillaume Vintenar Axl Rose : We destroyed Croix's Food Storage. We won that! Wow!",bc_map,"0xFFCE00"; bg_warp $@TierraBG1_id1,"bat_a01",50,374; bg_warp $@TierraBG1_id2,"bat_a01",42,16; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_a01", true; } end; } @@ -364,8 +373,10 @@ bat_a01,185,270,1 script Guillaume Blacksmith#a01 851,{ mes "[Guillaume Blacksmith]"; mes "Wow! It's done."; mes "We are relieved."; - delitem 7049,50; //Stone - donpcevent "barricade#bat_a01_a::OnEnable"; + if (mobcount("bat_a01","barricade#bat_a01_a::OnMyMobDead") < 17) { + delitem 7049,50; //Stone + donpcevent "barricade#bat_a01_a::OnEnable"; + } close2; disablenpc "Guillaume Blacksmith#a01"; end; @@ -448,8 +459,10 @@ bat_a01,170,121,5 script Croix Blacksmith#bat_a01 851,{ mes "[Croix Blacksmith]"; mes "Wow! It's done."; mes "We are relieved."; - delitem 7049,50; //Stone - donpcevent "barricade#bat_a01_b::OnEnable"; + if (mobcount("bat_a01","barricade#bat_a01_b::OnMyMobDead") < 17) { + delitem 7049,50; //Stone + donpcevent "barricade#bat_a01_b::OnEnable"; + } close2; disablenpc "Croix Blacksmith#bat_a01"; end; @@ -500,35 +513,39 @@ OnEnable: end; OnStop: - disablenpc "bat_a01_rp1_a_warp"; + // disablenpc "bat_a01_rp1_a_warp"; disablenpc "Battle Therapist#a01_a"; stopnpctimer; end; OnTimer25000: specialeffect EF_SANCTUARY; - enablenpc "bat_a01_rp1_a_warp"; + // enablenpc "bat_a01_rp1_a_warp"; + areapercentheal "bat_a01",41,365,61,385,100,100; + areawarp "bat_a01",41,365,61,385,"bat_a01",352,342; end; -OnTimer26000: - disablenpc "bat_a01_rp1_a_warp"; - end; +// OnTimer26000: + // disablenpc "bat_a01_rp1_a_warp"; + // end; OnTimer26500: donpcevent "Battle Therapist#a01_a::OnEnable"; end; } +/* bat_a01,51,375,0 script bat_a01_rp1_a_warp -1,10,10,{ OnInit: disablenpc "bat_a01_rp1_a_warp"; end; -OnTouch_: +OnTouch: percentheal 100,100; warp "bat_a01",352,342; end; } +*/ bat_a01,45,19,3 script Battle Therapist#a01_b 95,{ specialeffect2 EF_HEAL; @@ -550,25 +567,28 @@ OnEnable: end; OnStop: - disablenpc "bat_a01_rp1_b_warp"; + // disablenpc "bat_a01_rp1_b_warp"; disablenpc "Battle Therapist#a01_b"; stopnpctimer; end; OnTimer25000: specialeffect EF_SANCTUARY; - enablenpc "bat_a01_rp1_b_warp"; + areapercentheal "bat_a01",33,7,53,27,100,100; + areawarp "bat_a01",33,7,53,27,"bat_a01",353,52; + // enablenpc "bat_a01_rp1_b_warp"; end; -OnTimer26000: - disablenpc "bat_a01_rp1_b_warp"; - end; +// OnTimer26000: + // disablenpc "bat_a01_rp1_b_warp"; + // end; OnTimer26500: donpcevent "Battle Therapist#a01_b::OnEnable"; end; } +/* bat_a01,43,17,0 script bat_a01_rp1_b_warp -1,10,10,{ OnInit: disablenpc "bat_a01_rp1_b_warp"; @@ -579,6 +599,7 @@ OnTouch: warp "bat_a01",353,52; end; } +*/ bat_a01,60,216,3 script Valley Ghost#bat_a01_n 950,{ specialeffect2 EF_HEAL; @@ -597,18 +618,21 @@ OnEnable: OnTimer25000: specialeffect EF_SANCTUARY; - enablenpc "bat_a01_rp1_n_warp"; + areapercentheal "bat_a01",45,203,65,223,100,100; + areawarp "bat_a01",45,203,65,223,"bat_a01",301,209; + // enablenpc "bat_a01_rp1_n_warp"; end; -OnTimer26000: - disablenpc "bat_a01_rp1_n_warp"; - end; +// OnTimer26000: + // disablenpc "bat_a01_rp1_n_warp"; + // end; OnTimer26500: donpcevent "Valley Ghost#bat_a01_n::OnEnable"; end; } +/* bat_a01,55,213,0 script bat_a01_rp1_n_warp -1,10,10,{ OnInit: disablenpc "bat_a01_rp1_n_warp"; @@ -619,6 +643,7 @@ OnTouch: warp "bat_a01",301,209; end; } +*/ bat_a01,194,267,0 script barri_warp_up#bat_a01_a -1,7,0,{ OnTouch: diff --git a/npc/battleground/tierra/tierra02.txt b/npc/battleground/tierra/tierra02.txt index a7ebd1798e..04d01165ed 100644 --- a/npc/battleground/tierra/tierra02.txt +++ b/npc/battleground/tierra/tierra02.txt @@ -57,21 +57,19 @@ OnStop: OnTimer1000: stopnpctimer; - if (!getbattleflag("feature.bgqueue")) - initnpctimer; - set .@chk_bat_a02,getmapusers("bat_a02"); - if (.@chk_bat_a02 < 1) { - set $@TierraBG2,0; set $@TierraBG2_Victory, 0; - if( $@TierraBG2_id1 ) { bg_destroy $@TierraBG2_id1; set $@TierraBG2_id1, 0; } - if( $@TierraBG2_id2 ) { bg_destroy $@TierraBG2_id2; set $@TierraBG2_id2, 0; } - if (getbattleflag("feature.bgqueue")) { + if (!bg_get_data($@TierraBG2_id1, 0) && !bg_get_data($@TierraBG2_id2, 0)) { + donpcevent "countdown#bat_a02::OnStop"; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_a02", true; + mapwarp "bat_a02","bat_room",154,150; + $@TierraBG2 = 0; $@TierraBG2_Victory = 0; + if( $@TierraBG2_id1 ) { bg_destroy $@TierraBG2_id1; $@TierraBG2_id1 = 0; } + if( $@TierraBG2_id2 ) { bg_destroy $@TierraBG2_id2; $@TierraBG2_id2 = 0; } + if (getbattleflag("feature.bgqueue")) bg_unbook "bat_a02"; - end; - } else - donpcevent "start#bat_a02::OnReadyCheck"; + end; } - if (getbattleflag("feature.bgqueue")) - initnpctimer; + initnpctimer; end; } @@ -125,6 +123,14 @@ OnEnable: disablenpc "Croix Vintenar#a02_b"; end; +OnGuillaumeActive: + warp "bat_a02",352,342; + end; + +OnCroixActive: + warp "bat_a02",353,52; + end; + OnGuillaumeQuit: OnCroixQuit: if (getbattleflag("feature.bgqueue")) @@ -182,6 +188,8 @@ OnMyMobDead: mapannounce "bat_a02", "Croix Vintenar Swandery: We destroyed Guillaume's Food Storage. We won that! Wow!",bc_map,"0xFFCE00"; bg_warp $@TierraBG2_id1,"bat_a02",50,374; bg_warp $@TierraBG2_id2,"bat_a02",42,16; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_a02", true; } end; } @@ -205,6 +213,8 @@ OnMyMobDead: mapannounce "bat_a02", "Guillaume Vintenar Axl Rose : We destroyed Croix's Food Storage. We won that! Wow!",bc_map,"0xFFCE00"; bg_warp $@TierraBG2_id1,"bat_a02",50,374; bg_warp $@TierraBG2_id2,"bat_a02",42,16; + if (getbattleflag("feature.bgqueue")) + bg_reserve "bat_a02", true; } end; } @@ -363,8 +373,10 @@ bat_a02,185,270,1 script Guillaume Blacksmith#a02 851,{ mes "[Guillaume Blacksmith]"; mes "Wow! It's done."; mes "We are relieved."; - delitem 7049,50; //Stone - donpcevent "barricade#bat_a02_a::OnEnable"; + if (mobcount("bat_a02","barricade#bat_a02_a::OnMyMobDead") < 17) { + delitem 7049,50; //Stone + donpcevent "barricade#bat_a02_a::OnEnable"; + } close2; disablenpc "Guillaume Blacksmith#a02"; end; @@ -447,8 +459,10 @@ bat_a02,170,121,5 script Croix Blacksmith#bat_a02 851,{ mes "[Croix Blacksmith]"; mes "Wow! It's done."; mes "We are relieved."; - delitem 7049,50; //Stone - donpcevent "barricade#bat_a02_b::OnEnable"; + if (mobcount("bat_a02","barricade#bat_a02_b::OnMyMobDead") < 17) { + delitem 7049,50; //Stone + donpcevent "barricade#bat_a02_b::OnEnable"; + } close2; disablenpc "Croix Blacksmith#bat_a02"; end; @@ -499,35 +513,39 @@ OnEnable: end; OnStop: - disablenpc "bat_a02_rp1_a_warp"; + // disablenpc "bat_a02_rp1_a_warp"; disablenpc "Battle Therapist#a02_a"; stopnpctimer; end; OnTimer25000: specialeffect EF_SANCTUARY; - enablenpc "bat_a02_rp1_a_warp"; + // enablenpc "bat_a02_rp1_a_warp"; + areapercentheal "bat_a02",41,365,61,385,100,100; + areawarp "bat_a02",41,365,61,385,"bat_a02",352,342; end; -OnTimer26000: - disablenpc "bat_a02_rp1_a_warp"; - end; +// OnTimer26000: + // disablenpc "bat_a02_rp1_a_warp"; + // end; OnTimer26500: donpcevent "Battle Therapist#a02_a::OnEnable"; end; } +/* bat_a02,51,375,0 script bat_a02_rp1_a_warp -1,10,10,{ OnInit: disablenpc "bat_a02_rp1_a_warp"; end; -OnTouch_: +OnTouch: percentheal 100,100; warp "bat_a02",352,342; end; } +*/ bat_a02,45,19,3 script Battle Therapist#a02_b 95,{ specialeffect2 EF_HEAL; @@ -549,25 +567,28 @@ OnEnable: end; OnStop: - disablenpc "bat_a02_rp1_b_warp"; + // disablenpc "bat_a02_rp1_b_warp"; disablenpc "Battle Therapist#a02_b"; stopnpctimer; end; OnTimer25000: specialeffect EF_SANCTUARY; - enablenpc "bat_a02_rp1_b_warp"; + // enablenpc "bat_a02_rp1_b_warp"; + areapercentheal "bat_a02",33,7,53,27,100,100; + areawarp "bat_a02",33,7,53,27,"bat_a02",353,52; end; -OnTimer26000: - disablenpc "bat_a02_rp1_b_warp"; - end; +// OnTimer26000: + // disablenpc "bat_a02_rp1_b_warp"; + // end; OnTimer26500: donpcevent "Battle Therapist#a02_b::OnEnable"; end; } +/* bat_a02,43,17,0 script bat_a02_rp1_b_warp -1,10,10,{ OnInit: disablenpc "bat_a02_rp1_b_warp"; @@ -578,6 +599,7 @@ OnTouch: warp "bat_a02",353,52; end; } +*/ bat_a02,60,216,3 script Valley Ghost#bat_a02_n 950,{ specialeffect2 EF_HEAL; @@ -596,18 +618,21 @@ OnEnable: OnTimer25000: specialeffect EF_SANCTUARY; - enablenpc "bat_a02_rp1_n_warp"; + // enablenpc "bat_a02_rp1_n_warp"; + areapercentheal "bat_a02",45,203,65,223,100,100; + areawarp "bat_a02",45,203,65,223,"bat_a02",301,209; end; -OnTimer26000: - disablenpc "bat_a02_rp1_n_warp"; - end; +// OnTimer26000: + // disablenpc "bat_a02_rp1_n_warp"; + // end; OnTimer26500: donpcevent "Valley Ghost#bat_a02_n::OnEnable"; end; } +/* bat_a02,55,213,0 script bat_a02_rp1_n_warp -1,10,10,{ OnInit: disablenpc "bat_a02_rp1_n_warp"; @@ -618,6 +643,7 @@ OnTouch: warp "bat_a02",301,209; end; } +*/ bat_a02,194,267,0 script barri_warp_up#bat_a02_a -1,7,0,{ OnTouch: diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 712db7102c..c440f75992 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -8951,6 +8951,7 @@ static const struct _battle_data { { "idletime_hom_option", &battle_config.idletime_hom_option, 0x1F, 0x1, 0xFFF, }, { "devotion_standup_fix", &battle_config.devotion_standup_fix, 1, 0, 1, }, { "feature.bgqueue", &battle_config.feature_bgqueue, 1, 0, 1, }, + { "bgqueue_nowarp_mapflag", &battle_config.bgqueue_nowarp_mapflag, 0, 0, 1, }, { "homunculus_exp_gain", &battle_config.homunculus_exp_gain, 10, 0, 100, }, { "rental_item_novalue", &battle_config.rental_item_novalue, 1, 0, 1, }, diff --git a/src/map/battle.hpp b/src/map/battle.hpp index 5c0938e9cc..d9dae4b05d 100644 --- a/src/map/battle.hpp +++ b/src/map/battle.hpp @@ -675,6 +675,7 @@ struct Battle_Config int idletime_hom_option; int devotion_standup_fix; int feature_bgqueue; + int bgqueue_nowarp_mapflag; int homunculus_exp_gain; int rental_item_novalue; diff --git a/src/map/battleground.cpp b/src/map/battleground.cpp index 71bb9b236f..7c25a4370c 100644 --- a/src/map/battleground.cpp +++ b/src/map/battleground.cpp @@ -32,6 +32,7 @@ using namespace rathena; BattlegroundDatabase battleground_db; std::unordered_map> bg_team_db; std::vector> bg_queues; +int bg_queue_count = 1; const std::string BattlegroundDatabase::getDefaultLocation() { return std::string(db_path) + "/battleground_db.yml"; @@ -75,6 +76,11 @@ uint64 BattlegroundDatabase::parseBodyNode(const YAML::Node &node) { if (!this->asInt32(node, "MinPlayers", min)) return 0; + if (min < 1) { + this->invalidWarning(node["MinPlayers"], "Minimum players %d cannot be less than 1, capping to 1.\n", min); + min = 1; + } + if (min * 2 > MAX_BG_MEMBERS) { this->invalidWarning(node["MinPlayers"], "Minimum players %d exceeds MAX_BG_MEMBERS, capping to %d.\n", min, MAX_BG_MEMBERS / 2); min = MAX_BG_MEMBERS / 2; @@ -92,6 +98,11 @@ uint64 BattlegroundDatabase::parseBodyNode(const YAML::Node &node) { if (!this->asInt32(node, "MaxPlayers", max)) return 0; + if (max < 1) { + this->invalidWarning(node["MaxPlayers"], "Maximum players %d cannot be less than 1, capping to 1.\n", max); + max = 1; + } + if (max * 2 > MAX_BG_MEMBERS) { this->invalidWarning(node["MaxPlayers"], "Maximum players %d exceeds MAX_BG_MEMBERS, capping to %d.\n", max, MAX_BG_MEMBERS / 2); max = MAX_BG_MEMBERS / 2; @@ -158,7 +169,77 @@ uint64 BattlegroundDatabase::parseBodyNode(const YAML::Node &node) { bg->start_delay = delay; } else { if (!exists) - bg->start_delay = 30; + bg->start_delay = 0; + } + + if (this->nodeExists(node, "Join")) { + const YAML::Node &joinNode = node["Join"]; + + if (this->nodeExists(joinNode, "Solo")) { + bool active; + + if (!this->asBool(joinNode, "Solo", active)) + return 0; + + bg->solo = active; + } else { + if (!exists) + bg->solo = true; + } + + if (this->nodeExists(joinNode, "Party")) { + bool active; + + if (!this->asBool(joinNode, "Party", active)) + return 0; + + bg->party = active; + } else { + if (!exists) + bg->party = true; + } + + if (this->nodeExists(joinNode, "Guild")) { + bool active; + + if (!this->asBool(joinNode, "Guild", active)) + return 0; + + bg->guild = active; + } else { + if (!exists) + bg->guild = true; + } + } else { + if (!exists) { + bg->solo = true; + bg->party = true; + bg->guild = true; + } + } + + if (this->nodeExists(node, "JobRestrictions")) { + const YAML::Node &jobsNode = node["JobRestrictions"]; + + for (const auto &jobit : jobsNode) { + std::string job_name = jobit.first.as(), job_name_constant = "JOB_" + job_name; + int64 constant; + + if (!script_get_constant(job_name_constant.c_str(), &constant)) { + this->invalidWarning(node["JobRestrictions"], "Job %s does not exist.\n", job_name.c_str()); + continue; + } + + bool active; + + if (!this->asBool(jobsNode, job_name, active)) + return 0; + + if (active) + bg->job_restrictions.push_back(static_cast(constant)); + else + util::vector_erase_if_exists(bg->job_restrictions, static_cast(constant)); + } } if (this->nodeExists(node, "Locations")) { @@ -174,9 +255,9 @@ uint64 BattlegroundDatabase::parseBodyNode(const YAML::Node &node) { if (!this->asString(location, "Map", map_name)) return 0; - map_entry.mapid = map_mapname2mapid(map_name.c_str()); + map_entry.mapindex = mapindex_name2id(map_name.c_str()); - if (map_entry.mapid == -1) { + if (map_entry.mapindex == 0) { this->invalidWarning(location["Map"], "Invalid battleground map name %s, skipping.\n", map_name.c_str()); return 0; } @@ -245,6 +326,18 @@ uint64 BattlegroundDatabase::parseBodyNode(const YAML::Node &node) { } } + if (this->nodeExists(team[it], "ActiveEvent")) { + if (!this->asString(team[it], "ActiveEvent", team_ptr->active_event)) + return 0; + + team_ptr->active_event.resize(EVENT_NAME_LENGTH); + + if (team_ptr->active_event.find("::On") == std::string::npos) { + this->invalidWarning(team["ActiveEvent"], "Battleground ActiveEvent label %s should begin with '::On', skipping.\n", team_ptr->active_event.c_str()); + return 0; + } + } + if (this->nodeExists(team[it], "Variable")) { if (!this->asString(team[it], "Variable", team_ptr->bg_id_var)) return 0; @@ -284,6 +377,21 @@ std::shared_ptr bg_search_name(const char *name) return nullptr; } +/** + * Search for a Battleground queue based on the given queue ID + * @param queue_id: Queue ID + * @return s_battleground_queue on success or nullptr on failure + */ +std::shared_ptr bg_search_queue(int queue_id) +{ + for (const auto &queue : bg_queues) { + if (queue_id == queue->queue_id) + return queue; + } + + return nullptr; +} + /** * Search for an available player in Battleground * @param bg: Battleground data @@ -293,7 +401,12 @@ struct map_session_data* bg_getavailablesd(s_battleground_data *bg) { nullpo_retr(nullptr, bg); - return (bg->members.size() != 0) ? bg->members[0].sd : nullptr; + for (const auto &member : bg->members) { + if (member.sd != nullptr) + return member.sd; + } + + return nullptr; } /** @@ -514,7 +627,7 @@ int bg_create(uint16 mapindex, s_battleground_team* team) bg->cemetery.y = team->warp_y; bg->logout_event = team->quit_event.c_str(); bg->die_event = team->death_event.c_str(); - bg->members.clear(); + bg->active_event = team->active_event.c_str(); return bg->id; } @@ -569,7 +682,7 @@ void bg_send_message(struct map_session_data *sd, const char *mes, int len) { nullpo_retv(sd); - if (!sd->bg_id) + if (sd->bg_id == 0) return; std::shared_ptr bgteam = util::umap_find(bg_team_db, sd->bg_id); @@ -617,7 +730,7 @@ TIMER_FUNC(bg_send_xy_timer) } /** - * Mark a Battleground as ready to begin queuing + * Mark a Battleground as ready to begin queuing for a free map * @param tid: Timer ID * @param tick: Timer * @param id: ID @@ -625,14 +738,18 @@ TIMER_FUNC(bg_send_xy_timer) */ static TIMER_FUNC(bg_on_ready_loopback) { - s_battleground_queue *queue = (s_battleground_queue*)data; + int queue_id = (int)data; + std::shared_ptr queue = bg_search_queue(queue_id); - nullpo_retr(1, queue); + if (queue == nullptr) { + ShowError("bg_on_ready_loopback: Invalid battleground queue %d.\n", queue_id); + return 1; + } std::shared_ptr bg = battleground_db.find(queue->id); if (bg) { - bg_queue_on_ready(bg->name.c_str(), std::shared_ptr(queue)); + bg_queue_on_ready(bg->name.c_str(), queue); return 0; } else { ShowError("bg_on_ready_loopback: Can't find battleground %d in the battlegrounds database.\n", queue->id); @@ -641,7 +758,7 @@ static TIMER_FUNC(bg_on_ready_loopback) } /** - * Reset Battleground queue data + * Reset Battleground queue data if players don't accept in time * @param tid: Timer ID * @param tick: Timer * @param id: ID @@ -649,31 +766,32 @@ static TIMER_FUNC(bg_on_ready_loopback) */ static TIMER_FUNC(bg_on_ready_expire) { - s_battleground_queue *queue = (s_battleground_queue*)data; + int queue_id = (int)data; + std::shared_ptr queue = bg_search_queue(queue_id); - nullpo_retr(1, queue); - - queue->in_ready_state = false; - queue->map->isReserved = false; // Remove reservation to free up for future queue - queue->map = nullptr; - queue->accepted_players = 0; // Reset the queue count + if (queue == nullptr) { + ShowError("bg_on_ready_expire: Invalid battleground queue %d.\n", queue_id); + return 1; + } std::string bg_name = battleground_db.find(queue->id)->name; for (const auto &sd : queue->teama_members) { - sd->bg_queue_accept_state = false; clif_bg_queue_apply_result(BG_APPLY_QUEUE_FINISHED, bg_name.c_str(), sd); + clif_bg_queue_entry_init(sd); } for (const auto &sd : queue->teamb_members) { - sd->bg_queue_accept_state = false; clif_bg_queue_apply_result(BG_APPLY_QUEUE_FINISHED, bg_name.c_str(), sd); + clif_bg_queue_entry_init(sd); } + + bg_queue_clear(queue, true); return 0; } /** - * Start a Battleground + * Start a Battleground when all players have accepted * @param tid: Timer ID * @param tick: Timer * @param id: ID @@ -681,9 +799,13 @@ static TIMER_FUNC(bg_on_ready_expire) */ static TIMER_FUNC(bg_on_ready_start) { - s_battleground_queue *queue = (s_battleground_queue*)data; + int queue_id = (int)data; + std::shared_ptr queue = bg_search_queue(queue_id); - nullpo_retr(1, queue); + if (queue == nullptr) { + ShowError("bg_on_ready_start: Invalid battleground queue %d.\n", queue_id); + return 1; + } queue->tid_start = INVALID_TIMER; bg_queue_start_battleground(queue); @@ -702,7 +824,7 @@ bool bg_player_is_in_bg_map(struct map_session_data *sd) for (const auto &pair : battleground_db) { for (const auto &it : pair.second->maps) { - if (it.mapid == sd->bl.m) + if (it.mapindex == sd->mapindex) return true; } } @@ -724,7 +846,7 @@ static bool bg_queue_check_status(struct map_session_data* sd, const char *name) if (sd->sc.data[SC_ENTRY_QUEUE_APPLY_DELAY]) { // Exclude any player who's recently left a battleground queue char buf[CHAT_SIZE_MAX]; - sprintf(buf, msg_txt(sd, 339), (get_timer(sd->sc.data[SC_ENTRY_QUEUE_APPLY_DELAY]->timer)->tick - gettick()) / 1000); // You can't apply to a battleground queue for %d seconds due to recently leaving one. + sprintf(buf, msg_txt(sd, 339), static_cast((get_timer(sd->sc.data[SC_ENTRY_QUEUE_APPLY_DELAY]->timer)->tick - gettick()) / 1000)); // You can't apply to a battleground queue for %d seconds due to recently leaving one. clif_bg_queue_apply_result(BG_APPLY_NONE, name, sd); clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], buf, false, SELF); return false; @@ -732,9 +854,9 @@ static bool bg_queue_check_status(struct map_session_data* sd, const char *name) if (sd->sc.data[SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT]) { // Exclude any player who's recently deserted a battleground char buf[CHAT_SIZE_MAX]; - t_tick status_tick = get_timer(sd->sc.data[SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT]->timer)->tick, tick = gettick(); + int32 status_tick = static_cast(DIFF_TICK(get_timer(sd->sc.data[SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT]->timer)->tick, gettick()) / 1000); - sprintf(buf, msg_txt(sd, 338), ((status_tick - tick) / 1000) / 60, ((status_tick - tick) / 1000) % 60); // You can't apply to a battleground queue due to recently deserting a battleground. Time remaining: %d minutes and %d seconds. + sprintf(buf, msg_txt(sd, 338), status_tick / 60, status_tick % 60); // You can't apply to a battleground queue due to recently deserting a battleground. Time remaining: %d minutes and %d seconds. clif_bg_queue_apply_result(BG_APPLY_NONE, name, sd); clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], buf, false, SELF); return false; @@ -755,43 +877,64 @@ bool bg_queue_check_joinable(std::shared_ptr bg, struct map { nullpo_retr(false, sd); - if (bg->min_lvl && sd->status.base_level < bg->min_lvl) { // Check min level if min_lvl isn't 0 + for (const auto &job : bg->job_restrictions) { // Check class requirement + if (sd->class_ == job) { + clif_bg_queue_apply_result(BG_APPLY_PLAYER_CLASS, name, sd); + return false; + } + } + + if (bg->min_lvl > 0 && sd->status.base_level < bg->min_lvl) { // Check minimum level requirement clif_bg_queue_apply_result(BG_APPLY_PLAYER_LEVEL, name, sd); return false; } - if (bg->max_lvl && sd->status.base_level > bg->max_lvl) { // Check max level if max_lvl isn't 0 + if (bg->max_lvl > 0 && sd->status.base_level > bg->max_lvl) { // Check maximum level requirement clif_bg_queue_apply_result(BG_APPLY_PLAYER_LEVEL, name, sd); return false; } - if (!bg_queue_check_status(sd, name)) + if (!bg_queue_check_status(sd, name)) // Check status blocks return false; if (bg_player_is_in_bg_map(sd)) { // Is the player currently in a battleground map? Reject them. clif_bg_queue_apply_result(BG_APPLY_NONE, name, sd); - clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You may not join a battleground queue when you're in a battleground map. + clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You can't apply to a battleground queue from this map. return false; } - return true; // Return true if all conditions are met. + if (battle_config.bgqueue_nowarp_mapflag > 0 && map_getmapflag(sd->bl.m, MF_NOWARP)) { // Check player's current position for mapflag check + clif_bg_queue_apply_result(BG_APPLY_NONE, name, sd); + clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You can't apply to a battleground queue from this map. + return false; + } + + return true; } /** - * Sub function for reserving a slot in the Battleground if it's joinable + * Mark a map as reserved for a Battleground * @param name: Battleground map name * @param state: Whether to mark reserved or not + * @param ended: Whether the Battleground event is complete; players getting prize * @return True on success or false otherwise */ -bool bg_queue_reservation(const char *name, bool state) +bool bg_queue_reservation(const char *name, bool state, bool ended) { - int16 mapid = map_mapname2mapid(name); + uint16 mapindex = mapindex_name2id(name); - for (const auto &pair : battleground_db) { - // Bound checking isn't needed since we iterate within battleground_db's bound. - for (auto &it : pair.second->maps) { - if (it.mapid == mapid) { - it.isReserved = state; + for (auto &pair : battleground_db) { + for (auto &map : pair.second->maps) { + if (map.mapindex == mapindex) { + map.isReserved = state; + for (auto &queue : bg_queues) { + if (queue->map == &map) { + if (ended) // The ended flag is applied from bg_reserve (bg_unbook clears it for the next queue) + queue->state = QUEUE_STATE_ENDED; + if (!state) + bg_queue_clear(queue, true); + } + } return true; } } @@ -800,6 +943,33 @@ bool bg_queue_reservation(const char *name, bool state) return false; } +/** + * Join as an individual into a Battleground + * @param name: Battleground name + * @param sd: Player who requested to join the battlegrounds + */ +void bg_queue_join_solo(const char *name, struct map_session_data *sd) +{ + if (!sd) { + ShowError("bg_queue_join_solo: Tried to join non-existent player\n."); + return; + } + + std::shared_ptr bg = bg_search_name(name); + + if (!bg) { + ShowWarning("bq_queue_join_solo: Could not find battleground \"%s\" requested by %s (AID: %d / CID: %d)\n", name, sd->status.name, sd->status.account_id, sd->status.char_id); + return; + } + + if (!bg->solo) { + clif_bg_queue_apply_result(BG_APPLY_INVALID_APP, name, sd); + return; + } + + bg_queue_join_multi(name, sd, { sd }); // Join as solo +} + /** * Join a party onto the same side of a Battleground * @param name: Battleground name @@ -807,6 +977,11 @@ bool bg_queue_reservation(const char *name, bool state) */ void bg_queue_join_party(const char *name, struct map_session_data *sd) { + if (!sd) { + ShowError("bg_queue_join_party: Tried to join non-existent player\n."); + return; + } + struct party_data *p = party_search(sd->status.party_id); if (!p) { @@ -824,6 +999,11 @@ void bg_queue_join_party(const char *name, struct map_session_data *sd) std::shared_ptr bg = bg_search_name(name); if (bg) { + if (!bg->party) { + clif_bg_queue_apply_result(BG_APPLY_INVALID_APP, name, sd); + return; + } + int p_online = 0; for (const auto &it : p->party.member) { @@ -835,7 +1015,7 @@ void bg_queue_join_party(const char *name, struct map_session_data *sd) clif_bg_queue_apply_result(BG_APPLY_PLAYER_COUNT, name, sd); return; // Too many party members online } - + std::vector list; for (const auto &it : p->party.member) { @@ -865,6 +1045,11 @@ void bg_queue_join_party(const char *name, struct map_session_data *sd) */ void bg_queue_join_guild(const char *name, struct map_session_data *sd) { + if (!sd) { + ShowError("bg_queue_join_guild: Tried to join non-existent player\n."); + return; + } + if (!sd->guild) { clif_bg_queue_apply_result(BG_APPLY_INVALID_APP, name, sd); return; // Someone has bypassed the client check for being in a guild @@ -878,6 +1063,11 @@ void bg_queue_join_guild(const char *name, struct map_session_data *sd) std::shared_ptr bg = bg_search_name(name); if (bg) { + if (!bg->guild) { + clif_bg_queue_apply_result(BG_APPLY_INVALID_APP, name, sd); + return; + } + struct guild* g = sd->guild; if (g->connect_member > bg->max_players) { @@ -932,43 +1122,55 @@ void bg_queue_join_multi(const char *name, struct map_session_data *sd, std::vec } for (const auto &queue : bg_queues) { - if (queue->id != bg->id) - continue; - if (queue->in_ready_state) + if (queue->id != bg->id || queue->state == QUEUE_STATE_SETUP_DELAY || queue->state == QUEUE_STATE_ENDED) continue; // Make sure there's enough space on one side to join as a party/guild in this queue - if (queue->teama_members.size() + list.size() > bg->required_players && queue->teamb_members.size() + list.size() > bg->required_players) { + if (queue->teama_members.size() + list.size() > bg->max_players && queue->teamb_members.size() + list.size() > bg->max_players) { break; } bool r = rnd() % 2 != 0; - std::vector* team = r ? &queue->teamb_members : &queue->teama_members; + std::vector *team = r ? &queue->teamb_members : &queue->teama_members; - // If the designated team is full, put the player into the other team - if (team->size() + list.size() > bg->required_players) { - team = r ? &queue->teama_members : &queue->teamb_members; + if (queue->state == QUEUE_STATE_ACTIVE) { + // If one team has lesser members try to balance (on an active BG) + if (r && queue->teama_members.size() < queue->teamb_members.size()) + team = &queue->teama_members; + else if (!r && queue->teamb_members.size() < queue->teama_members.size()) + team = &queue->teamb_members; + } else { + // If the designated team is full, put the player into the other team + if (team->size() + list.size() > bg->required_players) + team = r ? &queue->teama_members : &queue->teamb_members; } - while (!list.empty() && team->size() < bg->required_players) { + while (!list.empty() && team->size() < bg->max_players) { struct map_session_data *sd2 = list.back(); list.pop_back(); - if (!sd2 || sd2->bg_queue) + if (!sd2 || sd2->bg_queue_id > 0) continue; if (!bg_queue_check_joinable(bg, sd2, name)) continue; - sd2->bg_queue = queue; + sd2->bg_queue_id = queue->queue_id; team->push_back(sd2); clif_bg_queue_apply_result(BG_APPLY_ACCEPT, name, sd2); clif_bg_queue_apply_notify(name, sd2); } - // Enough players have joined - if (queue->teamb_members.size() == bg->required_players && queue->teama_members.size() == bg->required_players) + if (queue->state == QUEUE_STATE_ACTIVE) { // Battleground is already active + for (auto &pl_sd : *team) { + if (queue->map->mapindex == pl_sd->mapindex) + continue; + + pc_set_bg_queue_timer(pl_sd); + clif_bg_queue_lobby_notify(name, pl_sd); + } + } else if (queue->state == QUEUE_STATE_SETUP && queue->teamb_members.size() >= bg->required_players && queue->teama_members.size() >= bg->required_players) // Enough players have joined bg_queue_on_ready(name, queue); return; @@ -981,10 +1183,11 @@ void bg_queue_join_multi(const char *name, struct map_session_data *sd, std::vec /** * Clear Battleground queue for next one * @param queue: Queue to clean up + * @param ended: If a Battleground has ended through normal means (by script command bg_unbook) */ -static void bg_queue_clear(s_battleground_queue *queue) +void bg_queue_clear(std::shared_ptr queue, bool ended) { - if (!queue) + if (queue == nullptr) return; if (queue->tid_requeue != INVALID_TIMER) { @@ -1002,49 +1205,47 @@ static void bg_queue_clear(s_battleground_queue *queue) queue->tid_start = INVALID_TIMER; } - if (queue->map != nullptr) { - queue->map->isReserved = false; // Remove reservation to free up for future queue - queue->map = nullptr; + if (ended) { + if (queue->map != nullptr) { + queue->map->isReserved = false; // Remove reservation to free up for future queue + queue->map = nullptr; + } + + for (const auto &sd : queue->teama_members) + sd->bg_queue_id = 0; + + for (const auto &sd : queue->teamb_members) + sd->bg_queue_id = 0; + + queue->teama_members.clear(); + queue->teamb_members.clear(); + queue->teama_members.shrink_to_fit(); + queue->teamb_members.shrink_to_fit(); + queue->accepted_players = 0; + queue->state = QUEUE_STATE_SETUP; } - queue->in_ready_state = false; - queue->accepted_players = 0; // Reset the queue count } /** * Sub function for leaving a Battleground queue * @param sd: Player leaving - * @param lista: List of players in queue data - * @param listb: List of players in second queue data + * @param members: List of players in queue data * @return True on success or false otherwise */ -static bool bg_queue_leave_sub(struct map_session_data *sd, std::vector lista, std::vector listb) +static bool bg_queue_leave_sub(struct map_session_data *sd, std::vector &members) { if (!sd) return false; - auto list_it = lista.begin(); + auto list_it = members.begin(); - while (list_it != lista.end()) { - struct map_session_data *player = *list_it; - - if (player == sd) { - if (sd->bg_queue->in_ready_state) { - sd->bg_queue->accepted_players = 0; - sd->bg_queue->in_ready_state = false; - sd->bg_queue_accept_state = false; - } - - lista.erase(list_it); - - if (lista.empty() && listb.empty()) { // If there are no players left in the queue, discard it - for (auto &queue : bg_queues) { - if (sd->bg_queue == queue) - bg_queue_clear(queue.get()); - } - } + while (list_it != members.end()) { + if (*list_it == sd) { + members.erase(list_it); sc_start(nullptr, &sd->bl, SC_ENTRY_QUEUE_APPLY_DELAY, 100, 1, 60000); - sd->bg_queue = nullptr; + sd->bg_queue_id = 0; + pc_delete_bg_queue_timer(sd); return true; } else { list_it++; @@ -1061,14 +1262,26 @@ static bool bg_queue_leave_sub(struct map_session_data *sd, std::vectorbg_queue) + if (!sd || sd->bg_queue_id == 0) return false; - if (!bg_queue_leave_sub(sd, sd->bg_queue->teama_members, sd->bg_queue->teamb_members) && !bg_queue_leave_sub(sd, sd->bg_queue->teamb_members, sd->bg_queue->teama_members)) { - ShowError("bg_queue_leave: Couldn't find player %s in battlegrounds queue.\n", sd->status.name); - return false; - } else - return true; + pc_delete_bg_queue_timer(sd); + + for (auto &queue : bg_queues) { + if (sd->bg_queue_id == queue->queue_id) { + if (!bg_queue_leave_sub(sd, queue->teama_members) && !bg_queue_leave_sub(sd, queue->teamb_members)) { + ShowError("bg_queue_leave: Couldn't find player %s in battlegrounds queue.\n", sd->status.name); + return false; + } else { + if ((queue->state == QUEUE_STATE_SETUP || queue->state == QUEUE_STATE_SETUP_DELAY) && queue->teama_members.empty() && queue->teamb_members.empty()) // If there are no players left in the queue (that hasn't started), discard it + bg_queue_clear(queue, true); + + return true; + } + } + } + + return false; } /** @@ -1086,29 +1299,27 @@ bool bg_queue_on_ready(const char *name, std::shared_ptr q return false; } - queue->accepted_players = 0; // Reset the counter just in case. - - if (queue->teama_members.size() != queue->required_players || queue->teamb_members.size() != queue->required_players) + if (queue->teama_members.size() < queue->required_players || queue->teamb_members.size() < queue->required_players) return false; // Return players to the queue and stop reapplying the timer - s_battleground_map *bgmap = nullptr; + bool map_reserved = false; - for (auto &it : bg->maps) { - if (!it.isReserved) { - it.isReserved = true; - bgmap = ⁢ - queue->map = ⁢ + for (auto &map : bg->maps) { + if (!map.isReserved) { + map.isReserved = true; + map_reserved = true; + queue->map = ↦ break; } } - if (!bgmap) { // All the battleground maps are reserved. Set a timer to check for an open battleground every 10 seconds. - queue->tid_requeue = add_timer(gettick() + 10000, bg_on_ready_loopback, 0, (intptr_t)queue.get()); + if (!map_reserved) { // All the battleground maps are reserved. Set a timer to check for an open battleground every 10 seconds. + queue->tid_requeue = add_timer(gettick() + 10000, bg_on_ready_loopback, 0, (intptr_t)queue->queue_id); return false; } - queue->in_ready_state = true; - queue->tid_expire = add_timer(gettick() + 20000, bg_on_ready_expire, 0, (intptr_t)queue.get()); + queue->state = QUEUE_STATE_SETUP_DELAY; + queue->tid_expire = add_timer(gettick() + 20000, bg_on_ready_expire, 0, (intptr_t)queue->queue_id); for (const auto &sd : queue->teama_members) clif_bg_queue_lobby_notify(name, sd); @@ -1119,25 +1330,150 @@ bool bg_queue_on_ready(const char *name, std::shared_ptr q return true; } +/** + * Send a player into an active Battleground + * @param sd: Player to send in + * @param queue: Queue data + */ +void bg_join_active(map_session_data *sd, std::shared_ptr queue) +{ + if (sd == nullptr || queue == nullptr) + return; + + // Check player's current position for mapflag check + if (battle_config.bgqueue_nowarp_mapflag > 0 && map_getmapflag(sd->bl.m, MF_NOWARP)) { + clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You can't apply to a battleground queue from this map. + bg_queue_leave(sd); + clif_bg_queue_entry_init(sd); + return; + } + + int bg_id_team_1 = static_cast(mapreg_readreg(add_str(queue->map->team1.bg_id_var.c_str()))); + std::shared_ptr bgteam_1 = util::umap_find(bg_team_db, bg_id_team_1); + + for (auto &pl_sd : queue->teama_members) { + if (sd != pl_sd) + continue; + + if (bgteam_1 == nullptr) { + bg_queue_leave(sd); + clif_bg_queue_apply_result(BG_APPLY_RECONNECT, battleground_db.find(queue->id)->name.c_str(), sd); + clif_bg_queue_entry_init(sd); + return; + } + + clif_bg_queue_entry_init(pl_sd); + bg_team_join(bg_id_team_1, pl_sd, true); + npc_event(pl_sd, bgteam_1->active_event.c_str(), 0); + return; + } + + int bg_id_team_2 = static_cast(mapreg_readreg(add_str(queue->map->team2.bg_id_var.c_str()))); + std::shared_ptr bgteam_2 = util::umap_find(bg_team_db, bg_id_team_2); + + for (auto &pl_sd : queue->teamb_members) { + if (sd != pl_sd) + continue; + + if (bgteam_2 == nullptr) { + bg_queue_leave(sd); + clif_bg_queue_apply_result(BG_APPLY_RECONNECT, battleground_db.find(queue->id)->name.c_str(), sd); + clif_bg_queue_entry_init(sd); + return; + } + + clif_bg_queue_entry_init(pl_sd); + bg_team_join(bg_id_team_2, pl_sd, true); + npc_event(pl_sd, bgteam_2->active_event.c_str(), 0); + return; + } + + return; +} + +/** + * Check to see if any players in the queue are on a map with MF_NOWARP and remove them from the queue + * @param queue: Queue data + * @return True if the player is on a map with MF_NOWARP or false otherwise + */ +bool bg_mapflag_check(std::shared_ptr queue) { + if (queue == nullptr || battle_config.bgqueue_nowarp_mapflag == 0) + return false; + + bool found = false; + + for (const auto &sd : queue->teama_members) { + if (map_getmapflag(sd->bl.m, MF_NOWARP)) { + clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You can't apply to a battleground queue from this map. + bg_queue_leave(sd); + clif_bg_queue_entry_init(sd); + found = true; + } + } + + for (const auto &sd : queue->teamb_members) { + if (map_getmapflag(sd->bl.m, MF_NOWARP)) { + clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 337), false, SELF); // You can't apply to a battleground queue from this map. + bg_queue_leave(sd); + clif_bg_queue_entry_init(sd); + found = true; + } + } + + if (found) { + queue->state = QUEUE_STATE_SETUP; // Set back to queueing state + queue->accepted_players = 0; // Reset acceptance count + + // Free map to avoid creating a reservation delay + if (queue->map != nullptr) { + queue->map->isReserved = false; + queue->map = nullptr; + } + + // Announce failure to remaining players + for (const auto &sd : queue->teama_members) + clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 340), false, SELF); // Participants were unable to join. Delaying entry for more participants. + + for (const auto &sd : queue->teamb_members) + clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], msg_txt(sd, 340), false, SELF); // Participants were unable to join. Delaying entry for more participants. + } + + return found; +} + /** * Update the Battleground queue when the player accepts the invite * @param queue: Battleground queue * @param sd: Player data */ -void bg_queue_on_accept_invite(std::shared_ptr queue, struct map_session_data *sd) +void bg_queue_on_accept_invite(struct map_session_data *sd) { nullpo_retv(sd); - sd->bg_queue_accept_state = true; + std::shared_ptr queue = bg_search_queue(sd->bg_queue_id); + + if (queue == nullptr) { + ShowError("bg_queue_on_accept_invite: Couldn't find player %s in battlegrounds queue.\n", sd->status.name); + return; + } + queue->accepted_players++; - clig_bg_queue_ack_lobby(true, map_mapid2mapname(queue->map->mapid), map_mapid2mapname(queue->map->mapid), sd); + clif_bg_queue_ack_lobby(true, mapindex_id2name(queue->map->mapindex), mapindex_id2name(queue->map->mapindex), sd); - if (queue->accepted_players == queue->required_players * 2) { - queue->tid_start = add_timer(gettick() + battleground_db.find(queue->id)->start_delay * 1000, bg_on_ready_start, 0, (intptr_t)queue.get()); + if (queue->state == QUEUE_STATE_ACTIVE) // Battleground is already active + bg_join_active(sd, queue); + else if (queue->state == QUEUE_STATE_SETUP_DELAY) { + if (queue->accepted_players == queue->required_players * 2) { + if (queue->tid_expire != INVALID_TIMER) { + delete_timer(queue->tid_expire, bg_on_ready_expire); + queue->tid_expire = INVALID_TIMER; + } - if (queue->tid_expire != INVALID_TIMER) { - delete_timer(queue->tid_expire, bg_on_ready_expire); - queue->tid_expire = INVALID_TIMER; + // Check player's current position for mapflag check + if (battle_config.bgqueue_nowarp_mapflag > 0 && bg_mapflag_check(queue)) + return; + + queue->tid_start = add_timer(gettick() + battleground_db.find(queue->id)->start_delay * 1000, bg_on_ready_start, 0, (intptr_t)queue->queue_id); } } } @@ -1146,31 +1482,33 @@ void bg_queue_on_accept_invite(std::shared_ptr queue, stru * Begin the Battleground from the given queue * @param queue: Battleground queue */ -void bg_queue_start_battleground(s_battleground_queue *queue) +void bg_queue_start_battleground(std::shared_ptr queue) { + if (queue == nullptr) + return; + std::shared_ptr bg = battleground_db.find(queue->id); if (!bg) { - queue->map->isReserved = false; // Remove reservation to free up for future queue - queue->map = nullptr; + bg_queue_clear(queue, true); ShowError("bg_queue_start_battleground: Could not find battleground ID %d in battlegrounds database.\n", queue->id); return; } - uint16 map_idx = map_id2index(queue->map->mapid); + // Check player's current position for mapflag check + if (battle_config.bgqueue_nowarp_mapflag > 0 && bg_mapflag_check(queue)) + return; + + uint16 map_idx = queue->map->mapindex; int bg_team_1 = bg_create(map_idx, &queue->map->team1); int bg_team_2 = bg_create(map_idx, &queue->map->team2); for (const auto &sd : queue->teama_members) { - sd->bg_queue = nullptr; - sd->bg_queue_accept_state = false; clif_bg_queue_entry_init(sd); bg_team_join(bg_team_1, sd, true); } for (const auto &sd : queue->teamb_members) { - sd->bg_queue = nullptr; - sd->bg_queue_accept_state = false; clif_bg_queue_entry_init(sd); bg_team_join(bg_team_2, sd, true); } @@ -1178,11 +1516,9 @@ void bg_queue_start_battleground(s_battleground_queue *queue) mapreg_setreg(add_str(queue->map->team1.bg_id_var.c_str()), bg_team_1); mapreg_setreg(add_str(queue->map->team2.bg_id_var.c_str()), bg_team_2); npc_event_do(queue->map->bgcallscript.c_str()); - queue->teama_members.clear(); - queue->teamb_members.clear(); - queue->teama_members.shrink_to_fit(); - queue->teamb_members.shrink_to_fit(); - bg_queue_clear(queue); + queue->state = QUEUE_STATE_ACTIVE; + + bg_queue_clear(queue, false); } /** @@ -1195,13 +1531,14 @@ static void bg_queue_create(int bg_id, int req_players) { auto queue = std::make_shared(); + queue->queue_id = bg_queue_count++; queue->id = bg_id; queue->required_players = req_players; queue->accepted_players = 0; queue->tid_expire = INVALID_TIMER; queue->tid_start = INVALID_TIMER; queue->tid_requeue = INVALID_TIMER; - queue->in_ready_state = false; + queue->state = QUEUE_STATE_SETUP; bg_queues.push_back(queue); } diff --git a/src/map/battleground.hpp b/src/map/battleground.hpp index e2377d69bf..cd68b2a380 100644 --- a/src/map/battleground.hpp +++ b/src/map/battleground.hpp @@ -27,32 +27,43 @@ struct s_battleground_data { struct point cemetery; ///< Respawn point for players who die std::string logout_event; ///< NPC Event to call on log out events std::string die_event; ///< NPC Event to call on death events + std::string active_event; ///< NPC Event to call on players joining an active battleground }; struct s_battleground_team { int16 warp_x, warp_y; ///< Team respawn coordinates std::string quit_event, ///< Team NPC Event to call on log out events death_event, ///< Team NPC Event to call on death events + active_event, ///< Team NPC Event to call on players joining an active battleground bg_id_var; ///< Team NPC variable name }; struct s_battleground_map { int id; ///< Battleground ID - int16 mapid; ///< ID of the map + uint16 mapindex; ///< Index of the map s_battleground_team team1, team2; ///< Team data std::string bgcallscript; ///< Script to be called when players join the battleground bool isReserved; ///< Reserve BG maps that are used so that the system won't create multiple BG instances on the same map }; +/// Enum for queue state tracking +enum e_queue_state : uint16 { + QUEUE_STATE_SETUP = 0, ///< The initial setup of a queue (a required amount of players hasn't been met) + QUEUE_STATE_SETUP_DELAY, ///< The initial setup of a queue but a required amount of players have accepted and the delay timer is active + QUEUE_STATE_ACTIVE, ///< The queue is active script side and more players can join (players may or may not be on the field) + QUEUE_STATE_ENDED, ///< The queue is no longer joinable (players are getting prizes) +}; + /// Battlegrounds client interface queue system [MasterOfMuppets] struct s_battleground_queue { + int queue_id; ///< Battlegrounds Queue ID int id; ///< Battlegrounds database ID std::vector teama_members; ///< List of members on team A std::vector teamb_members; ///< List of members on team B int required_players; ///< Amount of players required on each side to start int max_players; ///< Maximum amount of players on each side int accepted_players; ///< Amount of players who accepted the offer to enter the battleground - bool in_ready_state; ///< Is this BG queue waiting for players to enter the BG? + e_queue_state state; ///< See @e_queue_state int tid_expire; ///< Timer ID associated with the time out at the ready to enter window int tid_start; ///< Timer ID associated with the start delay int tid_requeue; ///< Timer ID associated with requeuing this group if all BG maps are reserved @@ -69,6 +80,10 @@ struct s_battleground_type { std::vector maps; ///< List of battleground locations uint32 deserter_time; ///< Amount of time a player is marked deserter (seconds) uint32 start_delay; ///< Amount of time before the start message is sent to players (seconds) + bool solo; ///< Ability to join a queue as an individual. + bool party; ///< Ability to join a queue as a party. + bool guild; ///< Ability to join a queue as a guild. + std::vector job_restrictions; ///< List of jobs that are unable to join. }; /// Enum of responses when applying for a Battleground @@ -109,15 +124,17 @@ public: extern BattlegroundDatabase battleground_db; extern std::unordered_map> bg_team_db; +extern std::vector> bg_queues; std::shared_ptr bg_search_name(const char *name); +std::shared_ptr bg_search_queue(int queue_id); void bg_send_dot_remove(struct map_session_data *sd); int bg_team_get_id(struct block_list *bl); struct map_session_data *bg_getavailablesd(s_battleground_data *bg); -bool bg_queue_reservation(const char *name, bool state); -#define bg_queue_reserve(name) (bg_queue_reservation(name, true)) -#define bg_queue_unbook(name) (bg_queue_reservation(name, false)) +bool bg_queue_reservation(const char *name, bool state, bool ended); +#define bg_queue_reserve(name, end) (bg_queue_reservation(name, true, end)) +#define bg_queue_unbook(name) (bg_queue_reservation(name, false, false)) int bg_create(uint16 mapindex, s_battleground_team* team); bool bg_team_join(int bg_id, struct map_session_data *sd, bool is_queue); @@ -126,13 +143,15 @@ int bg_team_leave(struct map_session_data *sd, bool quit, bool deserter); bool bg_team_warp(int bg_id, unsigned short mapindex, short x, short y); bool bg_player_is_in_bg_map(struct map_session_data *sd); bool bg_queue_check_joinable(std::shared_ptr bg, struct map_session_data *sd, const char *name); +void bg_queue_join_solo(const char *name, struct map_session_data *sd); void bg_queue_join_party(const char *name, struct map_session_data *sd); void bg_queue_join_guild(const char *name, struct map_session_data *sd); void bg_queue_join_multi(const char *name, struct map_session_data *sd, std::vector list); +void bg_queue_clear(std::shared_ptr queue, bool ended); bool bg_queue_leave(struct map_session_data *sd); bool bg_queue_on_ready(const char *name, std::shared_ptr queue); -void bg_queue_on_accept_invite(std::shared_ptr queue, struct map_session_data *sd); -void bg_queue_start_battleground(s_battleground_queue *queue); +void bg_queue_on_accept_invite(struct map_session_data *sd); +void bg_queue_start_battleground(std::shared_ptr queue); bool bg_member_respawn(struct map_session_data *sd); void bg_send_message(struct map_session_data *sd, const char *mes, int len); diff --git a/src/map/clif.cpp b/src/map/clif.cpp index 94a7291cbe..d702311fac 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -609,13 +609,12 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target case BG_SAMEMAP_WOS: case BG: case BG_WOS: - if( sd && sd->bg_id && (bg = util::umap_find(bg_team_db, sd->bg_id))) + if( sd && sd->bg_id > 0 && (bg = util::umap_find(bg_team_db, sd->bg_id))) { - for( i = 0; i < bg->members.size(); i++ ) - { - if( (sd = bg->members[i].sd) == NULL || !(fd = sd->fd) ) + for (const auto &member : bg->members) { + if((sd = member.sd) == nullptr || (fd = sd->fd) == 0) continue; - if( sd->bl.id == bl->id && (type == BG_WOS || type == BG_SAMEMAP_WOS || type == BG_AREA_WOS) ) + if(sd->bl.id == bl->id && (type == BG_WOS || type == BG_SAMEMAP_WOS || type == BG_AREA_WOS) ) continue; if( type != BG && type != BG_WOS && sd->bl.m != bl->m ) continue; @@ -17630,12 +17629,12 @@ void clif_parse_bg_queue_apply_request(int fd, struct map_session_data *sd) safestrncpy(name, RFIFOCP(fd, 4), NAME_LENGTH); - if (sd->bg_queue) { - ShowWarning("clif_parse_bg_queue_apply_request: Received duplicate queue application: %d from player %s (AID:%d CID:%d).\n", type, sd->status.name, sd->status.account_id, sd->status.char_id); + if (sd->bg_queue_id > 0) { + //ShowWarning("clif_parse_bg_queue_apply_request: Received duplicate queue application: %d from player %s (AID:%d CID:%d).\n", type, sd->status.name, sd->status.account_id, sd->status.char_id); clif_bg_queue_apply_result(BG_APPLY_DUPLICATE, name, sd); // Duplicate application warning return; } else if (type == 1) // Solo - bg_queue_join_multi(name, sd, { sd }); + bg_queue_join_solo(name, sd); else if (type == 2) // Party bg_queue_join_party(name, sd); else if (type == 4) // Guild @@ -17670,9 +17669,9 @@ void clif_bg_queue_apply_notify(const char *name, struct map_session_data *sd) { nullpo_retv(sd); - std::shared_ptr queue = sd->bg_queue; + std::shared_ptr queue = bg_search_queue(sd->bg_queue_id); - if (!queue) { + if (queue == nullptr) { ShowError("clif_bg_queue_apply_notify: Player is not in a battleground queue.\n"); return; } @@ -17712,8 +17711,10 @@ void clif_parse_bg_queue_cancel_request(int fd, struct map_session_data *sd) bool success; - if (sd->bg_queue) { - if (sd->bg_queue->in_ready_state) + if (sd->bg_queue_id > 0) { + std::shared_ptr queue = bg_search_queue(sd->bg_queue_id); + + if (queue && queue->state == QUEUE_STATE_SETUP_DELAY) return; // Make the cancel button do nothing if the entry window is open. Otherwise it'll crash the game when you click on both the queue status and entry status window. else success = bg_queue_leave(sd); @@ -17751,11 +17752,11 @@ void clif_parse_bg_queue_lobby_reply(int fd, struct map_session_data *sd) { nullpo_retv(sd); - if(sd->bg_queue) { + if(sd->bg_queue_id > 0) { uint8 result = RFIFOB(fd, 2); if(result == 1) { // Accept - bg_queue_on_accept_invite(sd->bg_queue, sd); + bg_queue_on_accept_invite(sd); } else if(result == 2) { // Decline bg_queue_leave(sd); clif_bg_queue_entry_init(sd); @@ -17765,7 +17766,7 @@ void clif_parse_bg_queue_lobby_reply(int fd, struct map_session_data *sd) /// Plays a gong sound, signaling that someone has accepted the invite to enter a battleground. /// 0x8e1 .B .24B .24B (ZC_REPLY_ACK_LOBBY_ADMISSION) -void clig_bg_queue_ack_lobby(bool result, const char *name, const char *lobbyname, struct map_session_data *sd) +void clif_bg_queue_ack_lobby(bool result, const char *name, const char *lobbyname, struct map_session_data *sd) { nullpo_retv(sd); diff --git a/src/map/clif.hpp b/src/map/clif.hpp index a2bfdbdb5c..469082956f 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -833,7 +833,7 @@ void clif_bg_queue_apply_result(e_bg_queue_apply_ack result, const char *name, s void clif_bg_queue_apply_notify(const char *name, struct map_session_data *sd); void clif_bg_queue_entry_init(struct map_session_data *sd); void clif_bg_queue_lobby_notify(const char *name, struct map_session_data *sd); -void clig_bg_queue_ack_lobby(bool result, const char *name, const char *lobbyname, struct map_session_data *sd); +void clif_bg_queue_ack_lobby(bool result, const char *name, const char *lobbyname, struct map_session_data *sd); // Instancing void clif_instance_create(int instance_id, int num); diff --git a/src/map/map.cpp b/src/map/map.cpp index 6227ffb4ed..50bc1c1ab5 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -2063,7 +2063,7 @@ int map_quit(struct map_session_data *sd) { if (sd->bg_id) bg_team_leave(sd, true, true); - if (sd->bg_queue != nullptr) + if (sd->bg_queue_id > 0) bg_queue_leave(sd); if( sd->status.clan_id ) diff --git a/src/map/pc.cpp b/src/map/pc.cpp index 0184a56ffd..0c0f945aec 100755 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -354,6 +354,54 @@ int pc_get_group_level(struct map_session_data *sd) { return sd->group_level; } +/** + * Remove a player from queue after timeout + * @param tid: Timer ID + * @param tick: Timer + * @param id: ID + * @return 0 on success or 1 otherwise + */ +static TIMER_FUNC(pc_on_expire_active) +{ + map_session_data *sd = (map_session_data *)data; + + nullpo_retr(1, sd); + + sd->tid_queue_active = INVALID_TIMER; + + bg_queue_leave(sd); + clif_bg_queue_entry_init(sd); + return 0; +} + +/** + * Function used to set timer externally + * @param sd: Player data + */ +void pc_set_bg_queue_timer(map_session_data *sd) { + nullpo_retv(sd); + + if (sd->tid_queue_active != INVALID_TIMER) { + delete_timer(sd->tid_queue_active, pc_on_expire_active); + sd->tid_queue_active = INVALID_TIMER; + } + + sd->tid_queue_active = add_timer(gettick() + 20000, pc_on_expire_active, 0, (intptr_t)sd); +} + +/** + * Function used to delete timer externally + * @param sd: Player data + */ +void pc_delete_bg_queue_timer(map_session_data *sd) { + nullpo_retv(sd); + + if (sd->tid_queue_active != INVALID_TIMER) { + delete_timer(sd->tid_queue_active, pc_on_expire_active); + sd->tid_queue_active = INVALID_TIMER; + } +} + static TIMER_FUNC(pc_invincible_timer){ struct map_session_data *sd; @@ -1471,6 +1519,7 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_ sd->expiration_tid = INVALID_TIMER; sd->autotrade_tid = INVALID_TIMER; sd->respawn_tid = INVALID_TIMER; + sd->tid_queue_active = INVALID_TIMER; #ifdef SECURE_NPCTIMEOUT // Initialize to defaults/expected @@ -1617,9 +1666,8 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_ sd->bonus_script.head = NULL; sd->bonus_script.count = 0; - // Initialize BG queue pointer - sd->bg_queue = nullptr; - sd->bg_queue_accept_state = false; + // Initialize BG queue + sd->bg_queue_id = 0; #if PACKETVER >= 20150513 sd->hatEffectIDs = NULL; @@ -13417,6 +13465,7 @@ void do_init_pc(void) { add_timer_func_list(pc_global_expiration_timer, "pc_global_expiration_timer"); add_timer_func_list(pc_expiration_timer, "pc_expiration_timer"); add_timer_func_list(pc_autotrade_timer, "pc_autotrade_timer"); + add_timer_func_list(pc_on_expire_active, "pc_on_expire_active"); add_timer(gettick() + autosave_interval, pc_autosave, 0, 0); diff --git a/src/map/pc.hpp b/src/map/pc.hpp index 148f640244..1f22c79296 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -634,10 +634,6 @@ struct map_session_data { bool changed; // if true, should sync with charserver on next mailbox request } mail; - // Battlegrounds queue system [MasterOfMuppets] - std::shared_ptr bg_queue; - bool bg_queue_accept_state; // Set this to true when someone has accepted the invite to join BGs - //Quest log system int num_quests; ///< Number of entries in quest_log int avail_quests; ///< Number of Q_ACTIVE and Q_INACTIVE entries in quest log (index of the first Q_COMPLETE entry) @@ -668,7 +664,9 @@ struct map_session_data { int debug_line; const char* debug_func; - int bg_id; + // Battlegrounds queue system [MasterOfMuppets] + int bg_id, bg_queue_id; + int tid_queue_active; ///< Timer ID associated with players joining an active BG #ifdef SECURE_NPCTIMEOUT /** @@ -1296,6 +1294,9 @@ struct sg_data { }; extern const struct sg_data sg_info[MAX_PC_FEELHATE]; +void pc_set_bg_queue_timer(map_session_data *sd); +void pc_delete_bg_queue_timer(map_session_data *sd); + void pc_setinvincibletimer(struct map_session_data* sd, int val); void pc_delinvincibletimer(struct map_session_data* sd); diff --git a/src/map/script.cpp b/src/map/script.cpp index 7ff47a67fd..c4a084a4f4 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -20016,20 +20016,21 @@ BUILDIN_FUNC(bg_get_data) /** * Reserves a slot for the given Battleground. - * bg_reserve(<"bg_name">); + * bg_reserve(""{,}); */ BUILDIN_FUNC(bg_reserve) { const char *str = script_getstr(st, 2); + bool ended = script_hasdata(st, 3) ? script_getnum(st, 3) != 0 : false; - if (!bg_queue_reserve(str)) + if (!bg_queue_reserve(str, ended)) ShowWarning("buildin_bg_reserve: Could not reserve battleground with name %s\n", str); return SCRIPT_CMD_SUCCESS; } /** * Removes a spot for the given Battleground. - * bg_unbook(<"bg_name">); + * bg_unbook(""); */ BUILDIN_FUNC(bg_unbook) { @@ -20075,7 +20076,7 @@ BUILDIN_FUNC(bg_info) size_t i; for (i = 0; i < bg->maps.size(); i++) - setd_sub_str(st, nullptr, ".@bgmaps$", i, map_mapid2mapname(bg->maps[i].mapid), nullptr); + setd_sub_str(st, nullptr, ".@bgmaps$", i, mapindex_id2name(bg->maps[i].mapindex), nullptr); setd_sub_num(st, nullptr, ".@bgmapscount", 0, i, nullptr); script_pushint(st, i); break; @@ -25143,7 +25144,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(bg_updatescore,"sii"), BUILDIN_DEF(bg_join,"i????"), BUILDIN_DEF(bg_create,"sii??"), - BUILDIN_DEF(bg_reserve,"s"), + BUILDIN_DEF(bg_reserve,"s?"), BUILDIN_DEF(bg_unbook,"s"), BUILDIN_DEF(bg_info,"si"), From 7cc59c670822695833e9ac812cd5b46c9146eff2 Mon Sep 17 00:00:00 2001 From: Aleos Date: Mon, 25 May 2020 20:28:11 -0400 Subject: [PATCH 049/141] Adjusts a few checks for item tradability (#4995) * Follow up to bfb6972. * Adds a new return array for script command inventorylist: `@inventorylist_tradable` * Adjusts script command countitem to not count rental items. --- doc/script_commands.txt | 4 +++- src/map/pc.cpp | 18 ++++++++++++++++++ src/map/pc.hpp | 1 + src/map/script.cpp | 15 ++++++--------- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/doc/script_commands.txt b/doc/script_commands.txt index ecd78cd2ee..93693c3f7c 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -2910,6 +2910,7 @@ recreate these items perfectly if they are destroyed. Here's what you get: @inventorylist_option_id5[] - fifth array of random option IDs @inventorylist_option_value5[] - fifth array of random option values @inventorylist_option_parameter5[] - fifth array of random option parameters +@inventorylist_tradable - Returns if an item is tradable or not (Pass item_trade.txt, bound, and rental restrictions). This could be handy to save/restore a character's inventory, since no other command returns such a complete set of data, and could also be the only way to @@ -4798,8 +4799,9 @@ for special cases such as removing a status change or resetting a variable or st of the player. This command can not be used to rent stackable items. Rental items cannot be -dropped, traded, sold to NPCs, or placed in guild storage. (i.e. trade mask 75) +dropped, traded, or placed in guild storage. (i.e. trade mask 67) Note: 'delitem' in an NPC script can still remove rental items. +Note: 'countitem' will not count any item with a rental timer. --------------------------------------- diff --git a/src/map/pc.cpp b/src/map/pc.cpp index 0c0f945aec..ed287c2522 100755 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -916,6 +916,24 @@ bool pc_can_give_bounded_items(struct map_session_data *sd) return pc_has_permission(sd, PC_PERM_TRADE_BOUNDED); } +/** + * Determine if an item in a player's inventory is tradeable based on several merits. + * Checks for item_trade, bound, and rental restrictions. + * @param sd: Player data + * @param index: Item inventory index + * @return True if the item can be traded or false otherwise + */ +bool pc_can_trade_item(map_session_data *sd, int index) { + if (sd && index >= 0) { + return (sd->inventory.u.items_inventory[index].expire_time == 0 && + (sd->inventory.u.items_inventory[index].bound == 0 || pc_can_give_bounded_items(sd)) && + itemdb_cantrade(&sd->inventory.u.items_inventory[index], pc_get_group_level(sd), pc_get_group_level(sd)) + ); + } + + return false; +} + /*========================================== * Prepares character for saving. * @param sd diff --git a/src/map/pc.hpp b/src/map/pc.hpp index 1f22c79296..4df3e616bd 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -1051,6 +1051,7 @@ int pc_get_group_id(struct map_session_data *sd); bool pc_can_sell_item(struct map_session_data* sd, struct item * item, enum npc_subtype shoptype); bool pc_can_give_items(struct map_session_data *sd); bool pc_can_give_bounded_items(struct map_session_data *sd); +bool pc_can_trade_item(map_session_data *sd, int index); bool pc_can_use_command(struct map_session_data *sd, const char *command, AtCommandType type); #define pc_has_permission(sd, permission) ( ((sd)->permissions&permission) != 0 ) diff --git a/src/map/script.cpp b/src/map/script.cpp index c4a084a4f4..5dcfd74633 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -6935,7 +6935,7 @@ int script_countitem_sub(struct item *items, struct item_data *id, int size, boo unsigned short nameid = id->nameid; for (int i = 0; i < size; i++) { - if (&items[i] && items[i].nameid == nameid) + if (&items[i] && items[i].nameid == nameid && items[i].expire_time == 0) count += items[i].amount; } } else { // For expanded functions @@ -6967,7 +6967,7 @@ int script_countitem_sub(struct item *items, struct item_data *id, int size, boo for (int i = 0; i < size; i++) { struct item *itm = &items[i]; - if (!itm || !itm->nameid || itm->amount < 1) + if (!itm || !itm->nameid || itm->amount < 1 || items[i].expire_time > 0) continue; if (itm->nameid != it.nameid || itm->identify != it.identify || itm->refine != it.refine || itm->attribute != it.attribute) continue; @@ -7708,6 +7708,7 @@ BUILDIN_FUNC(rentitem2) { it.card[2] = (short)c3; it.card[3] = (short)c4; it.expire_time = (unsigned int)(time(NULL) + seconds); + it.bound = BOUND_NONE; if (funcname[strlen(funcname)-1] == '3') { int res = script_getitem_randomoption(st, sd, &it, funcname, 11); @@ -14058,6 +14059,7 @@ BUILDIN_FUNC(getinventorylist) sprintf(randopt_var, "@inventorylist_option_parameter%d",k+1); pc_setreg(sd,reference_uid(add_str(randopt_var), j),sd->inventory.u.items_inventory[i].option[k].param); } + pc_setreg(sd,reference_uid(add_str("@inventorylist_tradable"), j),pc_can_trade_item(sd, i)); j++; } } @@ -24062,13 +24064,8 @@ BUILDIN_FUNC(getequiptradability) { return SCRIPT_CMD_FAILURE; } - if (i >= 0) { - bool tradable = (sd->inventory.u.items_inventory[i].expire_time == 0 && - (!sd->inventory.u.items_inventory[i].bound || pc_can_give_bounded_items(sd)) && - itemdb_cantrade(&sd->inventory.u.items_inventory[i], pc_get_group_level(sd), pc_get_group_level(sd)) - ); - script_pushint(st, tradable); - } + if (i >= 0) + script_pushint(st, pc_can_trade_item(sd, i)); else script_pushint(st, false); From c81e6ffc092378448f958856951a9cd748790fb7 Mon Sep 17 00:00:00 2001 From: Aleos Date: Tue, 26 May 2020 09:41:08 -0400 Subject: [PATCH 050/141] Fixes Decorate Cart requirement (#5012) * Fixes #4980. * Decorate Cart requires a Cart be active. Thanks to @LordWhiplash! --- db/re/skill_db.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index 85b8450c08..c70dc2f64f 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -28372,6 +28372,7 @@ Body: HitCount: 1 Requires: SpCost: 40 + State: Cart - Id: 2552 Name: RL_RICHS_COIN Description: Rich's Coin From 0e42b738c59be3330395e8c79c51500d534fcf55 Mon Sep 17 00:00:00 2001 From: Aleos Date: Tue, 26 May 2020 10:30:59 -0400 Subject: [PATCH 051/141] Fixes looter mobs getting stuck (#5000) * Follow up to b6b6b13. * Fixes #4941. * Fixes cases of monsters getting stuck when they lose sight of their target item that is looted by another monster. Thanks to @iraciz and @SeravySensei! --- src/map/mob.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 1a8f47c532..2d0420bfba 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -1723,8 +1723,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, t_tick tick) )) { //No valid target if (mob_warpchase(md, tbl)) return true; //Chasing this target. - if(md->ud.walktimer != INVALID_TIMER && (!can_move || md->ud.walkpath.path_pos <= battle_config.mob_chase_refresh) - && (tbl || md->ud.walkpath.path_pos == 0)) + if (tbl && md->ud.walktimer != INVALID_TIMER && (!can_move || md->ud.walkpath.path_pos <= battle_config.mob_chase_refresh)) return true; //Walk at least "mob_chase_refresh" cells before dropping the target unless target is non-existent mob_unlocktarget(md, tick); //Unlock target tbl = NULL; From 7efd696af7da0f46bf291a3ed25509dbb9e164d2 Mon Sep 17 00:00:00 2001 From: Atemo Date: Wed, 27 May 2020 15:52:37 +0200 Subject: [PATCH 052/141] Added several check for OnTouch and OnTouch_ before running the event (#5009) * Check if the player is still in npc range * Check if the player is still able to interact with the npc * Check if the npc is cloaked --- src/map/npc.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/map/npc.cpp b/src/map/npc.cpp index f86995f35e..e4524f276a 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -989,6 +989,26 @@ int npc_event_sub(struct map_session_data* sd, struct event_data* ev, const char npc_event_dequeue(sd); return 2; } + + char ontouch_event_name[EVENT_NAME_LENGTH]; + char ontouch2_event_name[EVENT_NAME_LENGTH]; + + safesnprintf(ontouch_event_name, ARRAYLENGTH(ontouch_event_name), "%s::%s", ev->nd->exname, script_config.ontouch_event_name); + safesnprintf(ontouch2_event_name, ARRAYLENGTH(ontouch2_event_name), "%s::%s", ev->nd->exname, script_config.ontouch2_event_name); + + // recheck some conditions for OnTouch/OnTouch_ + if (strcmp(eventname, ontouch_event_name) == 0 || strcmp(eventname, ontouch2_event_name) == 0) { + int xs = ev->nd->u.scr.xs; + int ys = ev->nd->u.scr.ys; + int x = ev->nd->bl.x; + int y = ev->nd->bl.y; + + if (x > 0 && y > 0 && (xs > -1 && ys > -1) && ((sd->bl.x < x - xs) || (sd->bl.x > x + xs) || (sd->bl.y < y - ys) || (sd->bl.y > y + ys)) || + (sd->state.block_action & PCBLOCK_NPCCLICK) || npc_is_cloaked(ev->nd, sd)) { + npc_event_dequeue(sd); + return 2; + } + } run_script(ev->nd->u.scr.script,ev->pos,sd->bl.id,ev->nd->bl.id); return 0; } From 55e3d45fa12bbebba60b537996a0858730f94e28 Mon Sep 17 00:00:00 2001 From: LordWhiplash <62680611+LordWhiplash@users.noreply.github.com> Date: Wed, 27 May 2020 17:53:55 -0300 Subject: [PATCH 053/141] Fix Whikebain's Black Tail (#5016) * Fixes #5005. * Whikebain's Black Tail should not apply curse. Thanks to @reunite-ro and @LordWhiplash! Co-authored-by: LordWhiplash --- db/re/item_db.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/re/item_db.txt b/db/re/item_db.txt index ef9150b717..9309251dc0 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -2077,7 +2077,7 @@ 2852,E_Rosary_C,Rosary,4,1,,0,,0,,0,0xFFFFFFFE,63,2,136,,1,0,0,{ bonus bLuk,4; bonus bMdef,5; },{},{} 2853,Telekinetic_Orb,Telekinetic Orb,4,20,,200,,2,,0,0xFFFFFFFE,63,2,136,,110,0,0,{ bonus bMdef,1; bonus bInt,3; bonus bMaxSP,30; bonus2 bSkillAtk,"WL_SOULEXPANSION",10; bonus2 bSkillAtk,"SO_PSYCHIC_WAVE",10; bonus2 bSkillUseSP,"WL_SOULEXPANSION",-50; bonus2 bSkillUseSP,"SO_PSYCHIC_WAVE",-50; },{},{} 2854,Alchemy_Glove,Alchemy Glove,4,20,,100,,1,,1,0xFFFFFFFE,63,2,136,,125,0,0,{ bonus bMdef,2; bonus bInt,1; bonus2 bMagicAtkEle,Ele_Fire,10; bonus2 bMagicAtkEle,Ele_Water,-20; bonus2 bSubEle,Ele_Water,-30; bonus3 bAutoSpell,"MG_FIREBALL",5,30; bonus5 bAutoSpell,"MG_FIREBOLT",5,30,BF_MAGIC,1; },{},{} -2855,Whike_Black_Tail,Whike Black Tail,4,20,,100,,0,,0,0xFFFFFFFF,63,2,136,,45,0,0,{ bonus bCritical,7; bonus bAspdRate,3; bonus2 bAddEff2,Eff_Curse,10; },{},{} +2855,Whike_Black_Tail,Whike Black Tail,4,20,,100,,0,,0,0xFFFFFFFF,63,2,136,,45,0,0,{ bonus bCritical,7; bonus bAspdRate,3; },{},{} 2856,Half_Megin,Half Megingjard,4,20,,0,,1,,0,0xFFFFFFFF,63,2,136,,47,0,0,{ bonus bMdef,3; bonus bStr,20; },{},{} 2857,Half_Brysing,Half Brisingamen,4,20,,0,,1,,0,0xFFFFFFFF,63,2,136,,47,0,0,{ bonus bStr,3; bonus bInt,5; bonus bVit,3; bonus bDex,3; bonus bAgi,3; bonus bLuk,5; bonus bMdef,2; },{},{} 2858,Pendant_Of_Guardian,Pendant Of Guardian,4,0,,0,,0,,0,0xFFFFFFFF,63,2,136,,70,0,0,{ skill "ALL_GUARDIAN_RECALL",1; },{},{} From 7e38cc6fa8658cf5b23a903a162b6cfb68c777a4 Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Wed, 27 May 2020 22:54:02 +0200 Subject: [PATCH 054/141] SQL synchronization --- sql-files/item_db_re.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql-files/item_db_re.sql b/sql-files/item_db_re.sql index b5c5cde2c6..067bc2ed2d 100644 --- a/sql-files/item_db_re.sql +++ b/sql-files/item_db_re.sql @@ -2109,7 +2109,7 @@ REPLACE INTO `item_db_re` VALUES (2851,'E_Necklace_C','Necklace',4,1,NULL,0,NULL REPLACE INTO `item_db_re` VALUES (2852,'E_Rosary_C','Rosary',4,1,NULL,0,NULL,0,NULL,0,0xFFFFFFFE,63,2,136,NULL,'1',0,0,'bonus bLuk,4; bonus bMdef,5;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (2853,'Telekinetic_Orb','Telekinetic Orb',4,20,NULL,200,NULL,2,NULL,0,0xFFFFFFFE,63,2,136,NULL,'110',0,0,'bonus bMdef,1; bonus bInt,3; bonus bMaxSP,30; bonus2 bSkillAtk,"WL_SOULEXPANSION",10; bonus2 bSkillAtk,"SO_PSYCHIC_WAVE",10; bonus2 bSkillUseSP,"WL_SOULEXPANSION",-50; bonus2 bSkillUseSP,"SO_PSYCHIC_WAVE",-50;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (2854,'Alchemy_Glove','Alchemy Glove',4,20,NULL,100,NULL,1,NULL,1,0xFFFFFFFE,63,2,136,NULL,'125',0,0,'bonus bMdef,2; bonus bInt,1; bonus2 bMagicAtkEle,Ele_Fire,10; bonus2 bMagicAtkEle,Ele_Water,-20; bonus2 bSubEle,Ele_Water,-30; bonus3 bAutoSpell,"MG_FIREBALL",5,30; bonus5 bAutoSpell,"MG_FIREBOLT",5,30,BF_MAGIC,1;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (2855,'Whike_Black_Tail','Whike Black Tail',4,20,NULL,100,NULL,0,NULL,0,0xFFFFFFFF,63,2,136,NULL,'45',0,0,'bonus bCritical,7; bonus bAspdRate,3; bonus2 bAddEff2,Eff_Curse,10;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (2855,'Whike_Black_Tail','Whike Black Tail',4,20,NULL,100,NULL,0,NULL,0,0xFFFFFFFF,63,2,136,NULL,'45',0,0,'bonus bCritical,7; bonus bAspdRate,3;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (2856,'Half_Megin','Half Megingjard',4,20,NULL,0,NULL,1,NULL,0,0xFFFFFFFF,63,2,136,NULL,'47',0,0,'bonus bMdef,3; bonus bStr,20;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (2857,'Half_Brysing','Half Brisingamen',4,20,NULL,0,NULL,1,NULL,0,0xFFFFFFFF,63,2,136,NULL,'47',0,0,'bonus bStr,3; bonus bInt,5; bonus bVit,3; bonus bDex,3; bonus bAgi,3; bonus bLuk,5; bonus bMdef,2;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (2858,'Pendant_Of_Guardian','Pendant Of Guardian',4,0,NULL,0,NULL,0,NULL,0,0xFFFFFFFF,63,2,136,NULL,'70',0,0,'skill "ALL_GUARDIAN_RECALL",1;',NULL,NULL); From 023dcac49fd40ef6c9037ed4d713f394df6bac9e Mon Sep 17 00:00:00 2001 From: Aleos Date: Thu, 28 May 2020 11:37:31 -0400 Subject: [PATCH 055/141] Adjusts Reverberation behavior (#5013) * Reverberation no longer uses any sub-attack skill. * Adjusts hit count to be split on each hit. Thanks to @attackjom! --- db/re/skill_db.yml | 7 ++++--- src/map/battle.cpp | 4 ++-- src/map/skill.cpp | 4 ++-- src/map/skill.hpp | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index c70dc2f64f..735bda1266 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -25089,14 +25089,15 @@ Body: Name: WM_REVERBERATION Description: Reverberation MaxLevel: 5 + Type: Magic TargetType: Attack DamageFlags: Splash: true Flags: IsTrap: true Range: 9 - Hit: Single - HitCount: 1 + Hit: Multi_Hit + HitCount: -10 SplashArea: - Level: 1 Area: 2 @@ -25148,7 +25149,7 @@ Body: Reproduce: true Requires: SpCost: 1 - - Id: 2416 + - Id: 2416 # Removed on kRO Name: WM_REVERBERATION_MAGIC Description: Reverberation Magic MaxLevel: 5 diff --git a/src/map/battle.cpp b/src/map/battle.cpp index c440f75992..0b1aa9fe47 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -6069,7 +6069,7 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list if (mflag&ELE_DARK) s_ele = ELE_DARK; break; - case WM_REVERBERATION_MAGIC: + case WM_REVERBERATION: if (sd) s_ele = sd->bonus.arrow_ele; break; @@ -6495,7 +6495,7 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list skillratio += 100; // !TODO: Confirm target sleeping bonus RE_LVL_DMOD(100); break; - case WM_REVERBERATION_MAGIC: + case WM_REVERBERATION: // MATK [{(Skill Level x 300) + 400} x Casters Base Level / 100] % skillratio += -100 + 700 + 300 * skill_lv; RE_LVL_DMOD(100); diff --git a/src/map/skill.cpp b/src/map/skill.cpp index dc5094104e..ab78946f49 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -3645,7 +3645,7 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * if (!(flag&SD_ANIMATION)) clif_skill_nodamage(dsrc, bl, skill_id, skill_lv, 1); // Fall through - case WM_REVERBERATION_MAGIC: + case WM_REVERBERATION: dmg.dmotion = clif_skill_damage(dsrc, bl, tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -2, dmg_type); break; case SJ_FALLINGSTAR_ATK: @@ -6033,7 +6033,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint case WM_REVERBERATION: if (flag & 1) - skill_attack(skill_get_type(WM_REVERBERATION_MAGIC), src, src, bl, WM_REVERBERATION_MAGIC, skill_lv, tick, flag); + skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); else { clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); map_foreachinallrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR|BL_SKILL, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); diff --git a/src/map/skill.hpp b/src/map/skill.hpp index 06cccbffc0..36a9f97472 100644 --- a/src/map/skill.hpp +++ b/src/map/skill.hpp @@ -1693,7 +1693,7 @@ enum e_skill { WM_METALICSOUND, WM_REVERBERATION, WM_REVERBERATION_MELEE, // Removed on kRO - WM_REVERBERATION_MAGIC, + WM_REVERBERATION_MAGIC, // Removed on kRO WM_DOMINION_IMPULSE, // Removed on kRO WM_SEVERE_RAINSTORM, WM_POEMOFNETHERWORLD, From 4f68d0c144603aebbacbb4748fcbde6e83fd4262 Mon Sep 17 00:00:00 2001 From: Aleos Date: Thu, 28 May 2020 13:22:31 -0400 Subject: [PATCH 056/141] Fixes Mercenary kill counter (#5010) * Fixes #4329. * The master or the Mercenary are able to increase the kill count. Thanks to @Indigo000! --- src/map/mob.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 2d0420bfba..56c914c67e 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -3009,7 +3009,8 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) if (achievement_db.mobexists(md->mob_id)) achievement_update_objective(sd, AG_BATTLE, 1, md->mob_id); - if (sd->md && src && src->type == BL_MER && mob_db(md->mob_id)->lv > sd->status.base_level / 2) + // The master or Mercenary can increase the kill count + if (sd->md && src && (src->type == BL_PC || src->type == BL_MER) && mob_db(md->mob_id)->lv > sd->status.base_level / 2) mercenary_kills(sd->md); } From d386d515a2813b215111cf73f7cd58cb604e2605 Mon Sep 17 00:00:00 2001 From: LordWhiplash <62680611+LordWhiplash@users.noreply.github.com> Date: Fri, 29 May 2020 14:40:20 -0300 Subject: [PATCH 057/141] Fix Racing Cap combo with Racing 3Lv (#5020) * Fixes #4998. * Adjusts Cart Boost bonus to level 3. * Adjusts duration to 120 seconds. Thanks to @Angelic234 and @LordWhiplash! --- db/re/item_combo_db.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/re/item_combo_db.txt b/db/re/item_combo_db.txt index 0ef4d3a414..3a78f2bdf5 100644 --- a/db/re/item_combo_db.txt +++ b/db/re/item_combo_db.txt @@ -843,7 +843,7 @@ 19203:29351,{ bonus bBaseAtk,20; bonus bMatk,20; if (getskilllv("OB_OBOROGENSOU") == 5) bonus bVariableCastrate,-15; autobonus3 "{ bonus bCritAtkRate,30; }",1000,60000,"KG_KAGEMUSYA"; autobonus "{ bonus bNoSizeFix; }",50,5000,BF_WEAPON; } 19204:29352,{ bonus bMaxHP,500; bonus bBaseAtk,10; } 19204:29353,{ bonus bMaxHP,1000; bonus bBaseAtk,20; skill "WS_CARTBOOST",1; autobonus3 "{ bonus bBaseAtk,30; }",1000,60000,"WS_CARTBOOST"; } -19204:29354,{ bonus bMaxHP,1500; bonus bBaseAtk,40; skill "WS_CARTBOOST",1; autobonus3 "{ bonus bBaseAtk,50; }",1000,60000,"WS_CARTBOOST"; } +19204:29354,{ bonus bMaxHP,1500; bonus bBaseAtk,40; skill "WS_CARTBOOST",3; autobonus3 "{ bonus bBaseAtk,50; }",1000,120000,"WS_CARTBOOST"; } 19205:29355,{ bonus2 bSkillAtk,"SU_CN_METEOR",10; bonus2 bSkillAtk,"SU_LUNATICCARROTBEAT",10; } 19205:29356,{ bonus2 bSkillAtk,"SU_CN_METEOR",20; bonus2 bSkillAtk,"SU_LUNATICCARROTBEAT",20; bonus bFixedCast,-200; } 19205:29357,{ bonus2 bSkillAtk,"SU_CN_METEOR",60; bonus2 bSkillAtk,"SU_LUNATICCARROTBEAT",60; bonus bFixedCast,-500; } From ec05f56ecb6003b26b796abd551315b710075864 Mon Sep 17 00:00:00 2001 From: qwerty7vp <64515036+qwerty7vp@users.noreply.github.com> Date: Sat, 30 May 2020 01:20:10 +0700 Subject: [PATCH 058/141] Whikebain Ears should Ignore 100% MDEF (#5025) * Fixes MDEF ignore to 100% for Normal and Boss class monsters only. * Fixes bonus for slotted version. Thanks to @qwerty7vp! --- db/re/item_db.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/re/item_db.txt b/db/re/item_db.txt index 9309251dc0..65ebc7f2f2 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -3549,7 +3549,7 @@ 5357,Wings_Of_Victory,Wings Of Victory,4,20,,200,,10,,0,0xFFFFFFFF,63,2,768,,0,0,365,{ bonus bMdef,10; bonus bUnbreakableHelm; },{},{} 5358,Pecopeco_Wing_Ears,Peco Ears,4,20,,100,,2,,0,0xFFFFFFFF,63,2,512,,70,0,366,{ bonus bAgi,1; bonus bMdef,2; bonus bUnbreakableHelm; },{},{} 5359,J_Captain_Hat,Ship Captain Hat,4,20,,300,,3,,1,0xFFFFFFFF,63,2,256,,60,1,367,{ bonus bDex,1; bonus bMaxHP,100; bonus bLongAtkRate,7; },{},{} -5360,Whikebain_Ears,Hyuke's Black Cat Ears,4,20,,200,,4,,0,0xFFFFFFFE,63,2,256,,45,1,368,{ bonus bFlee,10; bonus bCritAtkRate,10; autobonus "{ bonus2 bIgnoreMdefRaceRate,RC_ALL,25; }",50,5000,BF_MAGIC,"{ specialeffect2 EF_AGIUP; }"; },{},{} +5360,Whikebain_Ears,Hyuke's Black Cat Ears,4,20,,200,,4,,0,0xFFFFFFFE,63,2,256,,45,1,368,{ bonus bFlee,10; bonus bCritAtkRate,10; autobonus "{ bonus2 bIgnoreMdefClassRate,Class_Normal,100; bonus2 bIgnoreMdefClassRate,Class_Boss,100; }",50,5000,BF_MAGIC,"{ specialeffect2 EF_STEAL; }"; },{},{} 5361,Gang_Scarf,Gangster Scarf,4,20,,100,,0,,0,0xFFFFFFFF,63,2,1,,0,0,369,{ bonus bBaseAtk,5; if(BaseJob==Job_Rogue) skill "RG_GANGSTER",1; },{},{} 5362,Ninja_Scroll,Ninja Scroll,4,20,,200,,0,,0,0xFFFFFFFF,63,2,1,,0,0,370,{ bonus bMatkRate,1; },{},{} 5363,Helm_Of_Abyss,Helm Of Abyss,4,20,,1000,,9,,1,0x000654E2,63,2,256,,70,1,371,{ bonus2 bSubClass,Class_Normal,-5; bonus2 bSubClass,Class_Boss,10; },{},{} @@ -10141,7 +10141,7 @@ 19131,Radio_Antenna_,Radio Antenna,4,0,,1500,,2,,1,0xFFFFFFFF,63,2,256,,1,1,347,{ bonus bMdef,5; bonus bCritical,5; bonus bFlee,5; skill "MG_LIGHTNINGBOLT",1; bonus4 bAutoSpellWhenHit,"MG_THUNDERSTORM",5,30,1; },{},{} 19132,Masquerade_,Masquerade,4,20,,100,,0,,1,0xFFFFFFFE,63,2,512,,0,0,78,{ bonus2 bAddRace,RC_DemiHuman,3; bonus2 bAddRace,RC_Player,3; },{},{} 19133,Odin_Mask_,Odin's Mask,4,20,,100,,1,,1,0xFFFFFFFF,63,2,513,,1,0,480,{ bonus2 bSubClass,Class_Boss,2; },{},{} -19134,Wickebines_Black_Cat_Ears,Wickebine's Black Cat Ears,4,20,,200,,4,,1,0xFFFFFFFE,63,2,256,,45,1,368,{ bonus bFlee,10; bonus bCritAtkRate,10; autobonus "{ bonus2 bIgnoreMdefRaceRate,RC_ALL,25; }",50,5000,BF_MAGIC,"{ specialeffect2 EF_AGIUP; }"; },{},{} +19134,Wickebines_Black_Cat_Ears,Wickebine's Black Cat Ears,4,20,,200,,4,,1,0xFFFFFFFE,63,2,256,,45,1,368,{ bonus bFlee,10; bonus bCritAtkRate,10; autobonus "{ bonus2 bIgnoreMdefClassRate,Class_Normal,100; bonus2 bIgnoreMdefClassRate,Class_Boss,100; }",50,5000,BF_MAGIC,"{ specialeffect2 EF_STEAL; }"; },{},{} 19137,Strawberry_Mouth_Guard,Strawberry Mouth Guard,4,20,,100,,0,,0,0xFFFFFFFF,63,2,1,,80,0,861,{ bonus bVit,2; bonus bLongAtkDef,5; bonus bShortWeaponDamageReturn,5; if (Baselevel>=100) { bonus bMaxHP,2000; } else { bonus bMaxHP,200; } },{},{} 19138,Seraphim_Coronet,Seraphim Coronet,4,20,,300,,0,,0,0xFFFFFFFF,63,2,512,,70,0,1487,{ bonus bStr,2; .@int = readparam(bInt); bonus bBaseAtk,(.@int/8)*5; bonus bHealPower,.@int/8; bonus bVariableCastrate,.@int/8; if ((.@int>=108)) { bonus bBaseAtk,50; bonus bHealPower,5; bonus bVariableCastrate,4; } if ((.@int>=120)) { bonus bBaseAtk,125; bonus bHealPower,10; bonus bVariableCastrate,6; } },{},{} 19139,SurviveOrb,Survive Orb,4,0,,300,,,,0,0xFFFFFFFF,63,2,1,,50,0,1488,{ skill "TF_HIDING",1; bonus2 bAddClass,Class_All,2; bonus bMatkRate,2; bonus bVariableCastrate,-3; },{},{} From 0de5244c5694837e765946ca4b035cd63d7f8af3 Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Fri, 29 May 2020 20:20:20 +0200 Subject: [PATCH 059/141] SQL synchronization --- sql-files/item_db_re.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql-files/item_db_re.sql b/sql-files/item_db_re.sql index 067bc2ed2d..3509c4b4b5 100644 --- a/sql-files/item_db_re.sql +++ b/sql-files/item_db_re.sql @@ -3581,7 +3581,7 @@ REPLACE INTO `item_db_re` VALUES (5356,'Pumpkin_Hat_H','Festival Pumpkin Hat',4, REPLACE INTO `item_db_re` VALUES (5357,'Wings_Of_Victory','Wings Of Victory',4,20,NULL,200,NULL,10,NULL,0,0xFFFFFFFF,63,2,768,NULL,'0',0,365,'bonus bMdef,10; bonus bUnbreakableHelm;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (5358,'Pecopeco_Wing_Ears','Peco Ears',4,20,NULL,100,NULL,2,NULL,0,0xFFFFFFFF,63,2,512,NULL,'70',0,366,'bonus bAgi,1; bonus bMdef,2; bonus bUnbreakableHelm;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (5359,'J_Captain_Hat','Ship Captain Hat',4,20,NULL,300,NULL,3,NULL,1,0xFFFFFFFF,63,2,256,NULL,'60',1,367,'bonus bDex,1; bonus bMaxHP,100; bonus bLongAtkRate,7;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (5360,'Whikebain_Ears','Hyuke\'s Black Cat Ears',4,20,NULL,200,NULL,4,NULL,0,0xFFFFFFFE,63,2,256,NULL,'45',1,368,'bonus bFlee,10; bonus bCritAtkRate,10; autobonus "{ bonus2 bIgnoreMdefRaceRate,RC_ALL,25; }",50,5000,BF_MAGIC,"{ specialeffect2 EF_AGIUP; }";',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (5360,'Whikebain_Ears','Hyuke\'s Black Cat Ears',4,20,NULL,200,NULL,4,NULL,0,0xFFFFFFFE,63,2,256,NULL,'45',1,368,'bonus bFlee,10; bonus bCritAtkRate,10; autobonus "{ bonus2 bIgnoreMdefClassRate,Class_Normal,100; bonus2 bIgnoreMdefClassRate,Class_Boss,100; }",50,5000,BF_MAGIC,"{ specialeffect2 EF_STEAL; }";',NULL,NULL); REPLACE INTO `item_db_re` VALUES (5361,'Gang_Scarf','Gangster Scarf',4,20,NULL,100,NULL,0,NULL,0,0xFFFFFFFF,63,2,1,NULL,'0',0,369,'bonus bBaseAtk,5; if(BaseJob==Job_Rogue) skill "RG_GANGSTER",1;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (5362,'Ninja_Scroll','Ninja Scroll',4,20,NULL,200,NULL,0,NULL,0,0xFFFFFFFF,63,2,1,NULL,'0',0,370,'bonus bMatkRate,1;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (5363,'Helm_Of_Abyss','Helm Of Abyss',4,20,NULL,1000,NULL,9,NULL,1,0x000654E2,63,2,256,NULL,'70',1,371,'bonus2 bSubClass,Class_Normal,-5; bonus2 bSubClass,Class_Boss,10;',NULL,NULL); @@ -10173,7 +10173,7 @@ REPLACE INTO `item_db_re` VALUES (19130,'Magic_Eyes_','Magic Eyes',4,20,NULL,300 REPLACE INTO `item_db_re` VALUES (19131,'Radio_Antenna_','Radio Antenna',4,0,NULL,1500,NULL,2,NULL,1,0xFFFFFFFF,63,2,256,NULL,'1',1,347,'bonus bMdef,5; bonus bCritical,5; bonus bFlee,5; skill "MG_LIGHTNINGBOLT",1; bonus4 bAutoSpellWhenHit,"MG_THUNDERSTORM",5,30,1;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (19132,'Masquerade_','Masquerade',4,20,NULL,100,NULL,0,NULL,1,0xFFFFFFFE,63,2,512,NULL,'0',0,78,'bonus2 bAddRace,RC_DemiHuman,3; bonus2 bAddRace,RC_Player,3;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (19133,'Odin_Mask_','Odin\'s Mask',4,20,NULL,100,NULL,1,NULL,1,0xFFFFFFFF,63,2,513,NULL,'1',0,480,'bonus2 bSubClass,Class_Boss,2;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (19134,'Wickebines_Black_Cat_Ears','Wickebine\'s Black Cat Ears',4,20,NULL,200,NULL,4,NULL,1,0xFFFFFFFE,63,2,256,NULL,'45',1,368,'bonus bFlee,10; bonus bCritAtkRate,10; autobonus "{ bonus2 bIgnoreMdefRaceRate,RC_ALL,25; }",50,5000,BF_MAGIC,"{ specialeffect2 EF_AGIUP; }";',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (19134,'Wickebines_Black_Cat_Ears','Wickebine\'s Black Cat Ears',4,20,NULL,200,NULL,4,NULL,1,0xFFFFFFFE,63,2,256,NULL,'45',1,368,'bonus bFlee,10; bonus bCritAtkRate,10; autobonus "{ bonus2 bIgnoreMdefClassRate,Class_Normal,100; bonus2 bIgnoreMdefClassRate,Class_Boss,100; }",50,5000,BF_MAGIC,"{ specialeffect2 EF_STEAL; }";',NULL,NULL); REPLACE INTO `item_db_re` VALUES (19137,'Strawberry_Mouth_Guard','Strawberry Mouth Guard',4,20,NULL,100,NULL,0,NULL,0,0xFFFFFFFF,63,2,1,NULL,'80',0,861,'bonus bVit,2; bonus bLongAtkDef,5; bonus bShortWeaponDamageReturn,5; if (Baselevel>=100) { bonus bMaxHP,2000; } else { bonus bMaxHP,200; }',NULL,NULL); REPLACE INTO `item_db_re` VALUES (19138,'Seraphim_Coronet','Seraphim Coronet',4,20,NULL,300,NULL,0,NULL,0,0xFFFFFFFF,63,2,512,NULL,'70',0,1487,'bonus bStr,2; .@int = readparam(bInt); bonus bBaseAtk,(.@int/8)*5; bonus bHealPower,.@int/8; bonus bVariableCastrate,.@int/8; if ((.@int>=108)) { bonus bBaseAtk,50; bonus bHealPower,5; bonus bVariableCastrate,4; } if ((.@int>=120)) { bonus bBaseAtk,125; bonus bHealPower,10; bonus bVariableCastrate,6; }',NULL,NULL); REPLACE INTO `item_db_re` VALUES (19139,'SurviveOrb','Survive Orb',4,0,NULL,300,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,1,NULL,'50',0,1488,'skill "TF_HIDING",1; bonus2 bAddClass,Class_All,2; bonus bMatkRate,2; bonus bVariableCastrate,-3;',NULL,NULL); From bb5fd45d09a7f37f0a2879a76430320f599362c5 Mon Sep 17 00:00:00 2001 From: qwerty7vp <64515036+qwerty7vp@users.noreply.github.com> Date: Sat, 30 May 2020 02:08:35 +0700 Subject: [PATCH 060/141] Corrects Valkyrie Knife item script (#4948) * Adds missing Super Novice class bonuses. Thanks to @Questune09 and @qwerty7vp! --- db/re/item_db.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/re/item_db.txt b/db/re/item_db.txt index 65ebc7f2f2..f1efa6a485 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -12563,7 +12563,7 @@ 28703,Infinity_Dagger,Infinity Dagger,5,10,,500,125:100,,1,1,0x028F5EEF,56,2,2,4,100,1,1,{},{},{} 28705,Crimson_Dagger,Crimson Dagger,5,20,,550,55,,1,2,0x028F5EEF,63,2,2,3,70,1,1,{ .@r = getrefine(); bonus bBaseAtk,((BaseLevel/10)*5)+(.@r<=15?pow(.@r,2):225); bonus bMatk,(.@r<=15?(pow(.@r,2)/2):225); },{},{} 28706,Dagger_of_Vicious_Mind,Dagger of Vicious Mind,5,20,,1050,105:50,,1,1,0x028F5EEF,63,2,2,4,160,1,1,{ bonus bAtk,pow(min(getrefine(),15),2); bonus bMatk,pow(min(getrefine(),15),2)/2; },{},{} -28717,Valkyrie_Knife,Valkyrie Knife,5,50,,100,50:50,,1,4,0x228F5EEF,63,2,2,4,70,1,1,{ .@r = getrefine(); .@b = readparam(bDex)/10; if (.@r>7) { .@c = .@r-7; if (BaseClass==Job_Novice||BaseJob==Job_Mage) { bonus2 bSubRace,RC_DemiHuman,10; bonus2 bSubRace,RC_Player,10; } } if (BaseClass==Job_Novice) { bonus bMaxHP,300+(200*.@c); bonus bMaxSP,300+(20*.@c); bonus bBaseAtk,100; bonus bMatk,150+(15*.@c); bonus bUseSPrate,-5; bonus bUnbreakableWeapon; bonus bInt,2*.@c; bonus bDex,(2*.@c)-(.@c*.@b); bonus bCritical,7*.@c; bonus bAspdRate,10*.@c; bonus bCritAtkRate,1*.@c; } if (BaseClass==Job_Thief) { bonus bMaxSP,100; bonus bMatk,150; bonus bCritAtkRate,.@r; } if (BaseClass==Job_Mage && .@c) { bonus bMaxHP,200*.@c; bonus bMaxSP,20*.@c; } if (BaseJob==Job_Hunter) { bonus bMaxHP,200; bonus bUseSPrate,-5; bonus bInt,2*.@r; bonus bDex,2*.@r; } if (BaseJob==Job_Bard||BaseJob==Job_Dancer) { bonus bBaseAtk,100; bonus bAspdRate,10*.@r; bonus bUnbreakableWeapon; bonus bDex,-1*(.@c*.@b); } },{},{} +28717,Valkyrie_Knife,Valkyrie Knife,5,50,,100,50:50,,1,4,0x228F5EEF,63,2,2,4,70,1,1,{ .@r = getrefine(); .@b = readparam(bDex)/10; if (BaseClass==Job_Novice) { bonus bMaxHP,300+(200*.@r); bonus bMaxSP,300+(20*.@r); bonus bBaseAtk,100; bonus bMatk,150+(15*.@r); bonus bUseSPrate,-5; bonus bUnbreakableWeapon; bonus bInt,2*.@r; bonus bDex,(2*.@r)-(.@r*.@b); bonus bCritical,7*.@r; bonus bAspdRate,10*.@r; bonus bCritAtkRate,1*.@r; } if (BaseClass==Job_Thief) { bonus bMaxSP,100; bonus bMatk,150; bonus bCritAtkRate,.@r; } if (BaseClass==Job_Mage) { bonus bMaxHP,200*.@r; bonus bMaxSP,20*.@r; } if (BaseJob==Job_Hunter) { bonus bMaxHP,200; bonus bUseSPrate,-5; bonus bInt,2*.@r; bonus bDex,2*.@r; } if (BaseJob==Job_Bard||BaseJob==Job_Dancer) { bonus bBaseAtk,100; bonus bAspdRate,10*.@r; bonus bUnbreakableWeapon; bonus bDex,-1*(.@r*.@b); } if (.@r>=7) { if (BaseClass==Job_Novice||BaseJob==Job_Mage) { bonus2 bSubRace,RC_DemiHuman,10; bonus2 bSubRace,RC_Player,10; } }},{},{} 28721,Monokage,Monokage,5,10,,800,100,,1,2,0x20000000,63,2,2,3,100,1,1,{ .@r = getrefine(); bonus2 bSkillAtk,"KO_BAKURETSU",getskilllv("NJ_TOBIDOUGU"); if (.@r>=9) { bonus bLongAtkRate,5; } else if (.@r>=7) { bonus bLongAtkRate,2; } },{},{} 28725,Illusion_Moonlight_Dagger,Illusion Moonlight Dagger,5,20,,700,150,,1,1,0x028F5EEE,63,2,2,4,99,1,1,{ bonus bMaxSPrate,10; .@val = 3; if (getrefine() >= 10) { .@val += 4; } bonus bSPDrainValue,.@val; },{},{} 28745,Illusion_Counter_Dagger,Illusion Counter Dagger,5,0,,900,170,,1,2,0x00810204,63,2,2,4,120,0,1,{ .@r = getrefine(); bonus bCritical,90; bonus bCritAtkRate,(3*(.@r/2)); if (.@r >= 7) { bonus2 bAddClass,Class_All,5; if (.@r >= 9) { bonus2 bAddSize,Size_All,20; if (.@r >= 11) { bonus2 bAddEle,Ele_All,20; } } } },{},{} From 2c1477d4ce688d2e7994c0940a2bf7d7c36c10c2 Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Fri, 29 May 2020 21:08:43 +0200 Subject: [PATCH 061/141] SQL synchronization --- sql-files/item_db_re.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql-files/item_db_re.sql b/sql-files/item_db_re.sql index 3509c4b4b5..aae03aab5e 100644 --- a/sql-files/item_db_re.sql +++ b/sql-files/item_db_re.sql @@ -12595,7 +12595,7 @@ REPLACE INTO `item_db_re` VALUES (28702,'Ru_Gold_Ashura','Ru Gold Ashura',5,0,NU REPLACE INTO `item_db_re` VALUES (28703,'Infinity_Dagger','Infinity Dagger',5,10,NULL,500,'125:100',NULL,1,1,0x028F5EEF,56,2,2,4,'100',1,1,NULL,NULL,NULL); REPLACE INTO `item_db_re` VALUES (28705,'Crimson_Dagger','Crimson Dagger',5,20,NULL,550,'55',NULL,1,2,0x028F5EEF,63,2,2,3,'70',1,1,'.@r = getrefine(); bonus bBaseAtk,((BaseLevel/10)*5)+(.@r<=15?pow(.@r,2):225); bonus bMatk,(.@r<=15?(pow(.@r,2)/2):225);',NULL,NULL); REPLACE INTO `item_db_re` VALUES (28706,'Dagger_of_Vicious_Mind','Dagger of Vicious Mind',5,20,NULL,1050,'105:50',NULL,1,1,0x028F5EEF,63,2,2,4,'160',1,1,'bonus bAtk,pow(min(getrefine(),15),2); bonus bMatk,pow(min(getrefine(),15),2)/2;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (28717,'Valkyrie_Knife','Valkyrie Knife',5,50,NULL,100,'50:50',NULL,1,4,0x228F5EEF,63,2,2,4,'70',1,1,'.@r = getrefine(); .@b = readparam(bDex)/10; if (.@r>7) { .@c = .@r-7; if (BaseClass==Job_Novice||BaseJob==Job_Mage) { bonus2 bSubRace,RC_DemiHuman,10; bonus2 bSubRace,RC_Player,10; } } if (BaseClass==Job_Novice) { bonus bMaxHP,300+(200*.@c); bonus bMaxSP,300+(20*.@c); bonus bBaseAtk,100; bonus bMatk,150+(15*.@c); bonus bUseSPrate,-5; bonus bUnbreakableWeapon; bonus bInt,2*.@c; bonus bDex,(2*.@c)-(.@c*.@b); bonus bCritical,7*.@c; bonus bAspdRate,10*.@c; bonus bCritAtkRate,1*.@c; } if (BaseClass==Job_Thief) { bonus bMaxSP,100; bonus bMatk,150; bonus bCritAtkRate,.@r; } if (BaseClass==Job_Mage && .@c) { bonus bMaxHP,200*.@c; bonus bMaxSP,20*.@c; } if (BaseJob==Job_Hunter) { bonus bMaxHP,200; bonus bUseSPrate,-5; bonus bInt,2*.@r; bonus bDex,2*.@r; } if (BaseJob==Job_Bard||BaseJob==Job_Dancer) { bonus bBaseAtk,100; bonus bAspdRate,10*.@r; bonus bUnbreakableWeapon; bonus bDex,-1*(.@c*.@b); }',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (28717,'Valkyrie_Knife','Valkyrie Knife',5,50,NULL,100,'50:50',NULL,1,4,0x228F5EEF,63,2,2,4,'70',1,1,'.@r = getrefine(); .@b = readparam(bDex)/10; if (BaseClass==Job_Novice) { bonus bMaxHP,300+(200*.@r); bonus bMaxSP,300+(20*.@r); bonus bBaseAtk,100; bonus bMatk,150+(15*.@r); bonus bUseSPrate,-5; bonus bUnbreakableWeapon; bonus bInt,2*.@r; bonus bDex,(2*.@r)-(.@r*.@b); bonus bCritical,7*.@r; bonus bAspdRate,10*.@r; bonus bCritAtkRate,1*.@r; } if (BaseClass==Job_Thief) { bonus bMaxSP,100; bonus bMatk,150; bonus bCritAtkRate,.@r; } if (BaseClass==Job_Mage) { bonus bMaxHP,200*.@r; bonus bMaxSP,20*.@r; } if (BaseJob==Job_Hunter) { bonus bMaxHP,200; bonus bUseSPrate,-5; bonus bInt,2*.@r; bonus bDex,2*.@r; } if (BaseJob==Job_Bard||BaseJob==Job_Dancer) { bonus bBaseAtk,100; bonus bAspdRate,10*.@r; bonus bUnbreakableWeapon; bonus bDex,-1*(.@r*.@b); } if (.@r>=7) { if (BaseClass==Job_Novice||BaseJob==Job_Mage) { bonus2 bSubRace,RC_DemiHuman,10; bonus2 bSubRace,RC_Player,10; } }',NULL,NULL); REPLACE INTO `item_db_re` VALUES (28721,'Monokage','Monokage',5,10,NULL,800,'100',NULL,1,2,0x20000000,63,2,2,3,'100',1,1,'.@r = getrefine(); bonus2 bSkillAtk,"KO_BAKURETSU",getskilllv("NJ_TOBIDOUGU"); if (.@r>=9) { bonus bLongAtkRate,5; } else if (.@r>=7) { bonus bLongAtkRate,2; }',NULL,NULL); REPLACE INTO `item_db_re` VALUES (28725,'Illusion_Moonlight_Dagger','Illusion Moonlight Dagger',5,20,NULL,700,'150',NULL,1,1,0x028F5EEE,63,2,2,4,'99',1,1,'bonus bMaxSPrate,10; .@val = 3; if (getrefine() >= 10) { .@val += 4; } bonus bSPDrainValue,.@val;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (28745,'Illusion_Counter_Dagger','Illusion Counter Dagger',5,0,NULL,900,'170',NULL,1,2,0x00810204,63,2,2,4,'120',0,1,'.@r = getrefine(); bonus bCritical,90; bonus bCritAtkRate,(3*(.@r/2)); if (.@r >= 7) { bonus2 bAddClass,Class_All,5; if (.@r >= 9) { bonus2 bAddSize,Size_All,20; if (.@r >= 11) { bonus2 bAddEle,Ele_All,20; } } }',NULL,NULL); From cd0937c4684304eb375869e2c80664d20750ebeb Mon Sep 17 00:00:00 2001 From: SeravySensei <47065845+SeravySensei@users.noreply.github.com> Date: Sun, 31 May 2020 16:11:01 +0200 Subject: [PATCH 062/141] Fix missing pet egg checks (#5029) Fixes #5024 --- src/map/clif.cpp | 2 +- src/map/skill.cpp | 3 +++ src/map/storage.cpp | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/map/clif.cpp b/src/map/clif.cpp index d702311fac..a01f5d188c 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -6813,7 +6813,7 @@ void clif_item_repair_list(struct map_session_data *sd,struct map_session_data * WFIFOW(fd,0)=0x1fc; for(i=c=0;iinventory.u.items_inventory[i].nameid) > 0 && dstsd->inventory.u.items_inventory[i].attribute!=0){// && skill_can_repair(sd,nameid)){ + if((nameid=dstsd->inventory.u.items_inventory[i].nameid) > 0 && dstsd->inventory.u.items_inventory[i].attribute!=0 && (!itemdb_ishatched_egg(&dstsd->inventory.u.items_inventory[i]))) {// && skill_can_repair(sd,nameid)){ WFIFOW(fd,c*13+4) = i; WFIFOW(fd,c*13+6) = nameid; WFIFOB(fd,c*13+8) = dstsd->inventory.u.items_inventory[i].refine; diff --git a/src/map/skill.cpp b/src/map/skill.cpp index ab78946f49..e172b443c6 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -17500,6 +17500,9 @@ void skill_repairweapon(struct map_session_data *sd, int idx) { if( !item->nameid || !item->attribute ) return; //Again invalid item.... + if (itemdb_ishatched_egg(item)) + return; + if (sd != target_sd && !battle_check_range(&sd->bl, &target_sd->bl, skill_get_range2(&sd->bl, sd->menuskill_id, sd->menuskill_val2, true))) { clif_item_repaireffect(sd, idx, 1); return; diff --git a/src/map/storage.cpp b/src/map/storage.cpp index 328ddd99df..fcfdc3589e 100644 --- a/src/map/storage.cpp +++ b/src/map/storage.cpp @@ -920,6 +920,9 @@ void storage_guild_storageadd(struct map_session_data* sd, int index, int amount if( amount < 1 || amount > sd->inventory.u.items_inventory[index].amount ) return; + if (itemdb_ishatched_egg(&sd->inventory.u.items_inventory[index])) + return; + if( stor->lock ) { storage_guild_storageclose(sd); return; From 3691a21e3df4028f8aede18b16e47b735068b895 Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Tue, 2 Jun 2020 04:00:28 +0200 Subject: [PATCH 063/141] Mob and NPC Identity Update --- db/re/mob_db.txt | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/db/re/mob_db.txt b/db/re/mob_db.txt index aece8b1699..b9470366d0 100644 --- a/db/re/mob_db.txt +++ b/db/re/mob_db.txt @@ -3946,7 +3946,33 @@ //20813,MD_EVENT_AMDARAIS //20814,G_PAYONSOLDIER //20815,G_PAYONSOLDIER2 - +//20816,EM_DILUVIO +//20817,EM_ARDOR +//20818,EM_PROCELLA +//20819,EM_TERREMOTUS +//20820,EM_SERPENS +//20821,4JOB_VOID +//20822,4JOB_WRAITH +//20823,4JOB_KINGS_NIGHT +//20824,4JOB_AGONY_NIGHT +//20825,4JOB_DEVOTION_NIGHT +//20826,4JOB_ARMED_NIGHT +//20827,4JOB_DOOMK +//20828,4JOB_VERKHASEL +//20829,4JOB_BAPHOMET +//20830,4JOB_H_FALCON +//20831,4JOB_S_FALCON +//20832,4JOB_R_FALCON +//20833,4JOB_WORG +//20834,MEISTER_ABR1 +//20835,MEISTER_ABR2 +//20836,MEISTER_ABR3 +//20837,MEISTER_ABR4 +//20838,ELEMETAL_MASTER_S1 +//20839,ELEMETAL_MASTER_S2 +//20840,ELEMETAL_MASTER_S3 +//20841,ELEMETAL_MASTER_S4 +//20842,ELEMETAL_MASTER_S5 //20843,ILL_ABYSMAL_WITCH //20844,PRAY_GIVER //20845,SMILE_GIVER From a956d407477b5a86cf0c0d0bbad16bb0d8cd641e Mon Sep 17 00:00:00 2001 From: rAthenaAPI Date: Tue, 2 Jun 2020 04:00:41 +0200 Subject: [PATCH 064/141] SQL synchronization --- sql-files/mob_db_re.sql | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/sql-files/mob_db_re.sql b/sql-files/mob_db_re.sql index 1853b43ba7..b693516b54 100644 --- a/sql-files/mob_db_re.sql +++ b/sql-files/mob_db_re.sql @@ -4013,7 +4013,33 @@ REPLACE INTO `mob_db_re` VALUES (3790,'SWEETS_DROPS','Sweets Drops','Sweets Drop #20813,MD_EVENT_AMDARAIS #20814,G_PAYONSOLDIER #20815,G_PAYONSOLDIER2 - +#20816,EM_DILUVIO +#20817,EM_ARDOR +#20818,EM_PROCELLA +#20819,EM_TERREMOTUS +#20820,EM_SERPENS +#20821,4JOB_VOID +#20822,4JOB_WRAITH +#20823,4JOB_KINGS_NIGHT +#20824,4JOB_AGONY_NIGHT +#20825,4JOB_DEVOTION_NIGHT +#20826,4JOB_ARMED_NIGHT +#20827,4JOB_DOOMK +#20828,4JOB_VERKHASEL +#20829,4JOB_BAPHOMET +#20830,4JOB_H_FALCON +#20831,4JOB_S_FALCON +#20832,4JOB_R_FALCON +#20833,4JOB_WORG +#20834,MEISTER_ABR1 +#20835,MEISTER_ABR2 +#20836,MEISTER_ABR3 +#20837,MEISTER_ABR4 +#20838,ELEMETAL_MASTER_S1 +#20839,ELEMETAL_MASTER_S2 +#20840,ELEMETAL_MASTER_S3 +#20841,ELEMETAL_MASTER_S4 +#20842,ELEMETAL_MASTER_S5 #20843,ILL_ABYSMAL_WITCH #20844,PRAY_GIVER #20845,SMILE_GIVER From 6c5ed525724fd7cca23f9d07015bdfe3b5e88e62 Mon Sep 17 00:00:00 2001 From: Lemongrass3110 Date: Tue, 2 Jun 2020 15:49:11 +0200 Subject: [PATCH 065/141] Added a missing percentheal (#5033) Fixes #5031 Thanks to @Masao87 --- npc/re/quests/quests_rockridge.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/npc/re/quests/quests_rockridge.txt b/npc/re/quests/quests_rockridge.txt index 0f7981b4dd..5c14aee6eb 100644 --- a/npc/re/quests/quests_rockridge.txt +++ b/npc/re/quests/quests_rockridge.txt @@ -5888,6 +5888,7 @@ harboro1,156,215,5 script Innkeeper#rockridge 1_ETC_01,{ mes "Enjoy your stay at the Lazy Owl's."; close2; Zeny = Zeny - 5000; + percentheal 100,100; warp "harboro1",157,210; end; case 3: From b10caa039b2faef0354d82d3b50552691fb8278d Mon Sep 17 00:00:00 2001 From: Lemongrass3110 Date: Tue, 2 Jun 2020 19:36:49 +0200 Subject: [PATCH 066/141] Fixed slave recalculation on reloadmobdb Slaves sometimes ended up with weird status calculations or insane speed, because their status got recalculated before their master and this way their speed could not be copied from the master. Thanks to @Daegaladh --- src/map/mob.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 56c914c67e..53be39c348 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -5647,6 +5647,20 @@ void mob_reload_itemmob_data(void) { * @return 0 */ static int mob_reload_sub( struct mob_data *md, va_list args ){ + bool slaves_only = va_arg( args, bool ); + + if( slaves_only ){ + if( md->master_id == 0 ){ + // Only slaves should be processed now + return 0; + } + }else{ + if( md->master_id != 0 ){ + // Slaves will be processed later + return 0; + } + } + // Relink the mob to the new database entry md->db = mob_db(md->mob_id); @@ -5695,7 +5709,10 @@ static int mob_reload_sub_npc( struct npc_data *nd, va_list args ){ void mob_reload(void) { do_final_mob(true); mob_db_load(true); - map_foreachmob(mob_reload_sub); + // First only normal monsters + map_foreachmob( mob_reload_sub, false ); + // Then slaves only + map_foreachmob( mob_reload_sub, true ); map_foreachnpc(mob_reload_sub_npc); } From b394b93100d6e9e201e459d7912840f9d6288766 Mon Sep 17 00:00:00 2001 From: Lemongrass3110 Date: Tue, 2 Jun 2020 20:12:02 +0200 Subject: [PATCH 067/141] Fixed instance YAML conversion handling --- src/tool/csv2yaml.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tool/csv2yaml.cpp b/src/tool/csv2yaml.cpp index 5aa379cafd..b4c263f7c1 100644 --- a/src/tool/csv2yaml.cpp +++ b/src/tool/csv2yaml.cpp @@ -358,7 +358,7 @@ int do_init( int argc, char** argv ){ return 0; } - if (process("INSTANCE_DB", 1, root_paths, "instance_db", [](const std::string& path, const std::string& name_ext) -> bool { + if (!process("INSTANCE_DB", 1, root_paths, "instance_db", [](const std::string& path, const std::string& name_ext) -> bool { return sv_readdb(path.c_str(), name_ext.c_str(), ',', 7, 7 + MAX_MAP_PER_INSTANCE, -1, &instance_readdb_sub, false); })) { return 0; From 5784231946c76c4d50b9351bef21fcc56fc02b7b Mon Sep 17 00:00:00 2001 From: Lemongrass3110 Date: Tue, 2 Jun 2020 20:15:32 +0200 Subject: [PATCH 068/141] Removed a sample entry in item_avail.txt This should have never been enabled by default... --- db/item_avail.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/item_avail.txt b/db/item_avail.txt index c5de56ff41..02d7dd9f29 100644 --- a/db/item_avail.txt +++ b/db/item_avail.txt @@ -11,7 +11,7 @@ // Think of it as a way to disguise items. // Don't sell the item in same shop with the source. Example, don't put 2240 & 2241 in same place! -2240,2241 //Beard - Grampa Beard +//2240,2241 //Beard - Grampa Beard //Treasure Hunters Quest Items //use these aliases if your game client doesn't support them normally From 47ebf332ab155a0522a159af3f2de2f4edaa574e Mon Sep 17 00:00:00 2001 From: Lemongrass3110 Date: Tue, 2 Jun 2020 23:58:57 +0200 Subject: [PATCH 069/141] Follow up to b10caa0 --- src/map/mob.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 53be39c348..8f30e48437 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -5647,7 +5647,7 @@ void mob_reload_itemmob_data(void) { * @return 0 */ static int mob_reload_sub( struct mob_data *md, va_list args ){ - bool slaves_only = va_arg( args, bool ); + bool slaves_only = va_arg( args, int ) != 0; if( slaves_only ){ if( md->master_id == 0 ){ @@ -5710,9 +5710,9 @@ void mob_reload(void) { do_final_mob(true); mob_db_load(true); // First only normal monsters - map_foreachmob( mob_reload_sub, false ); + map_foreachmob( mob_reload_sub, 0 ); // Then slaves only - map_foreachmob( mob_reload_sub, true ); + map_foreachmob( mob_reload_sub, 1 ); map_foreachnpc(mob_reload_sub_npc); } From 929c30c4143dc78cb85bc70d022113e75f62d0cc Mon Sep 17 00:00:00 2001 From: Daegaladh Date: Wed, 3 Jun 2020 04:46:33 +0200 Subject: [PATCH 070/141] Fixed death counter not updating properly Also fixes Super Novice bonus not being removed on death and a possible crash *Thanks to @aleos89 --- src/map/pc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map/pc.cpp b/src/map/pc.cpp index ed287c2522..ddbefa87aa 100755 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -8277,7 +8277,7 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) clif_party_dead( sd ); - pc_setglobalreg(sd, add_str(PCDIECOUNTER_VAR), sd->die_counter+1); + pc_setparam(sd, SP_PCDIECOUNTER, sd->die_counter+1); pc_setparam(sd, SP_KILLERRID, src?src->id:0); //Reset menu skills/item skills @@ -8951,7 +8951,7 @@ bool pc_setparam(struct map_session_data *sd,int64 type,int64 val_tmp) if (sd->die_counter == val) return true; sd->die_counter = val; - if (!sd->die_counter && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) + if (!sd->state.connect_new && sd->die_counter == 1 && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) status_calc_pc(sd, SCO_NONE); // Lost the bonus. pc_setglobalreg(sd, add_str(PCDIECOUNTER_VAR), sd->die_counter); return true; From 45cd5808b7cb90f4549141d2f36fa1b20102dccf Mon Sep 17 00:00:00 2001 From: Lemongrass3110 Date: Wed, 3 Jun 2020 16:35:10 +0200 Subject: [PATCH 071/141] Initial support for newer packet versions (#4944) Basic support for packets up to April 2020 Changing the default packet version to 2020-04-01 Thanks to everyone involved! --- conf/battle/client.conf | 7 + sql-files/main.sql | 1 + sql-files/upgrades/upgrade_20200603.sql | 1 + src/char/char.cpp | 18 +- src/common/mmo.hpp | 9 +- src/common/socket.cpp | 4 + src/common/socket.hpp | 1 + src/config/packets.hpp | 19 +- src/login/loginclif.cpp | 18 + src/map/battle.cpp | 2 + src/map/battle.hpp | 2 + src/map/buyingstore.cpp | 198 +- src/map/buyingstore.hpp | 4 +- src/map/cashshop.cpp | 23 +- src/map/cashshop.hpp | 2 +- src/map/clif.cpp | 4785 ++++++++++++----------- src/map/clif.hpp | 35 +- src/map/clif_packetdb.hpp | 76 +- src/map/clif_shuffle.hpp | 310 +- src/map/map-server.vcxproj | 6 +- src/map/map-server.vcxproj.filters | 6 + src/map/map.hpp | 6 + src/map/npc.cpp | 16 +- src/map/npc.hpp | 18 +- src/map/packets.hpp | 178 + src/map/packets_struct.hpp | 3877 ++++++++++++++++++ src/map/pc.cpp | 27 +- src/map/pc.hpp | 7 + src/map/searchstore.cpp | 10 +- src/map/searchstore.hpp | 7 +- src/map/skill.cpp | 26 +- src/map/skill.hpp | 1 + src/map/storage.cpp | 5 +- src/map/vending.cpp | 6 +- 34 files changed, 6983 insertions(+), 2728 deletions(-) create mode 100644 sql-files/upgrades/upgrade_20200603.sql create mode 100644 src/map/packets.hpp create mode 100644 src/map/packets_struct.hpp diff --git a/conf/battle/client.conf b/conf/battle/client.conf index 3bb10b7cd6..c429287279 100644 --- a/conf/battle/client.conf +++ b/conf/battle/client.conf @@ -138,3 +138,10 @@ spawn_direction: no // kRO removed the packet and this re-enables the message. // Official: Disabled. mvp_exp_reward_message: no + +// Send ping timer +// Interval in seconds for each timer invoke. +ping_timer_inverval: 30 + +// Send packets timeout in seconds before ping packet can be sent. +ping_time: 20 diff --git a/sql-files/main.sql b/sql-files/main.sql index 9d7df5d633..21cdedf361 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -254,6 +254,7 @@ CREATE TABLE IF NOT EXISTS `char` ( `uniqueitem_counter` int(11) unsigned NOT NULL default '0', `sex` ENUM('M','F','U') NOT NULL default 'U', `hotkey_rowshift` tinyint(3) unsigned NOT NULL default '0', + `hotkey_rowshift2` tinyint(3) unsigned NOT NULL default '0', `clan_id` int(11) unsigned NOT NULL default '0', `last_login` datetime DEFAULT NULL, `title_id` INT(11) unsigned NOT NULL default '0', diff --git a/sql-files/upgrades/upgrade_20200603.sql b/sql-files/upgrades/upgrade_20200603.sql new file mode 100644 index 0000000000..8520c4532d --- /dev/null +++ b/sql-files/upgrades/upgrade_20200603.sql @@ -0,0 +1 @@ +ALTER TABLE `char` ADD COLUMN `hotkey_rowshift2` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `hotkey_rowshift`; diff --git a/src/char/char.cpp b/src/char/char.cpp index 721593acc5..ea37316645 100644 --- a/src/char/char.cpp +++ b/src/char/char.cpp @@ -299,7 +299,7 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){ (p->rename != cp->rename) || (p->robe != cp->robe) || (p->character_moves != cp->character_moves) || (p->unban_time != cp->unban_time) || (p->font != cp->font) || (p->uniqueitem_counter != cp->uniqueitem_counter) || (p->hotkey_rowshift != cp->hotkey_rowshift) || (p->clan_id != cp->clan_id ) || (p->title_id != cp->title_id) || - (p->show_equip != cp->show_equip) + (p->show_equip != cp->show_equip) || (p->hotkey_rowshift2 != cp->hotkey_rowshift2) ) { //Save status if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `base_level`='%d', `job_level`='%d'," @@ -310,7 +310,7 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){ "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d'," "`delete_date`='%lu',`robe`='%d',`moves`='%d',`font`='%u',`uniqueitem_counter`='%u'," - "`hotkey_rowshift`='%d', `clan_id`='%d', `title_id`='%lu', `show_equip`='%d'" + "`hotkey_rowshift`='%d', `clan_id`='%d', `title_id`='%lu', `show_equip`='%d', `hotkey_rowshift2`='%d'" " WHERE `account_id`='%d' AND `char_id` = '%d'", schema_config.char_db, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, @@ -322,7 +322,7 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){ mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->rename, (unsigned long)p->delete_date, // FIXME: platform-dependent size p->robe, p->character_moves, p->font, p->uniqueitem_counter, - p->hotkey_rowshift, p->clan_id, p->title_id, p->show_equip, + p->hotkey_rowshift, p->clan_id, p->title_id, p->show_equip, p->hotkey_rowshift2, p->account_id, p->char_id) ) { Sql_ShowDebug(sql_handle); @@ -924,7 +924,8 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf, uint8* coun "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`," "`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`," "`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`," - "`robe`,`moves`,`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`title_id`,`show_equip`" + "`robe`,`moves`,`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`title_id`,`show_equip`," + "`hotkey_rowshift2`" " FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", schema_config.char_db, sd->account_id, MAX_CHARS) || SQL_ERROR == SqlStmt_Execute(stmt) || SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &p.char_id, 0, NULL, NULL) @@ -972,6 +973,7 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf, uint8* coun || SQL_ERROR == SqlStmt_BindColumn(stmt, 42, SQLDT_UCHAR, &p.hotkey_rowshift, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 43, SQLDT_ULONG, &p.title_id, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 44, SQLDT_UINT16, &p.show_equip, 0, NULL, NULL) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 45, SQLDT_UCHAR, &p.hotkey_rowshift2, 0, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); @@ -1039,7 +1041,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`," "`hair_color`,`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`," "`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`, `moves`," - "`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`title_id`,`show_equip`" + "`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`title_id`,`show_equip`,`hotkey_rowshift2`" " FROM `%s` WHERE `char_id`=? LIMIT 1", schema_config.char_db) || SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0) || SQL_ERROR == SqlStmt_Execute(stmt) @@ -1105,6 +1107,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev || SQL_ERROR == SqlStmt_BindColumn(stmt, 59, SQLDT_INT, &p->clan_id, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 60, SQLDT_ULONG, &p->title_id, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 61, SQLDT_UINT16, &p->show_equip, 0, NULL, NULL) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 62, SQLDT_UCHAR, &p->hotkey_rowshift2, 0, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); @@ -1211,7 +1214,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev while( SQL_SUCCESS == SqlStmt_NextRow(stmt) ) { - if( hotkey_num >= 0 && hotkey_num < MAX_HOTKEYS ) + if( hotkey_num >= 0 && hotkey_num < MAX_HOTKEYS_DB ) memcpy(&p->hotkeys[hotkey_num], &tmp_hotkey, sizeof(tmp_hotkey)); 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); @@ -2318,7 +2321,8 @@ bool char_checkdb(void){ "`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`,`hair_color`,`clothes_color`,`weapon`," "`shield`,`head_top`,`head_mid`,`head_bottom`,`robe`,`last_map`,`last_x`,`last_y`,`save_map`," "`save_x`,`save_y`,`partner_id`,`online`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`," - "`moves`,`unban_time`,`font`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`,`title_id`,`show_equip`" + "`moves`,`unban_time`,`font`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`,`title_id`,`show_equip`," + "`hotkey_rowshift2`" " FROM `%s` LIMIT 1;", schema_config.char_db) ){ Sql_ShowDebug(sql_handle); return false; diff --git a/src/common/mmo.hpp b/src/common/mmo.hpp index d30158aff9..6a41312646 100644 --- a/src/common/mmo.hpp +++ b/src/common/mmo.hpp @@ -33,6 +33,12 @@ #define MAX_HOTKEYS 38 #endif +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190508 || PACKETVER_ZERO_NUM >= 20190605 + #define MAX_HOTKEYS_DB ((MAX_HOTKEYS) * 2) +#else + #define MAX_HOTKEYS_DB MAX_HOTKEYS +#endif + #define MAX_MAP_PER_SERVER 1500 /// Maximum amount of maps available on a server #define MAX_INVENTORY 100 ///Maximum items in player inventory /** Max number of characters per account. Note that changing this setting alone is not enough if the client is not hexed to support more characters as well. @@ -518,7 +524,7 @@ struct mmo_charstatus { struct s_friend friends[MAX_FRIENDS]; //New friend system [Skotlex] #ifdef HOTKEY_SAVING - struct hotkey hotkeys[MAX_HOTKEYS]; + struct hotkey hotkeys[MAX_HOTKEYS_DB]; #endif bool show_equip,allow_party; short rename; @@ -536,6 +542,7 @@ struct mmo_charstatus { uint32 uniqueitem_counter; unsigned char hotkey_rowshift; + unsigned char hotkey_rowshift2; unsigned long title_id; }; diff --git a/src/common/socket.cpp b/src/common/socket.cpp index 8638f5fc2b..0a4350763e 100644 --- a/src/common/socket.cpp +++ b/src/common/socket.cpp @@ -428,6 +428,8 @@ int send_from_fifo(int fd) if( len > 0 ) { + session[fd]->wdata_tick = last_tick; + // some data could not be transferred? // shift unsent data to the beginning of the queue if( (size_t)len < session[fd]->wdata_size ) @@ -587,6 +589,7 @@ int make_listen_bind(uint32 ip, uint16 port) create_session(fd, connect_client, null_send, null_parse); session[fd]->client_addr = 0; // just listens session[fd]->rdata_tick = 0; // disable timeouts on this socket + session[fd]->wdata_tick = 0; return fd; } @@ -727,6 +730,7 @@ static int create_session(int fd, RecvFunc func_recv, SendFunc func_send, ParseF session[fd]->func_send = func_send; session[fd]->func_parse = func_parse; session[fd]->rdata_tick = last_tick; + session[fd]->wdata_tick = last_tick; return 0; } diff --git a/src/common/socket.hpp b/src/common/socket.hpp index e5db4f4ec9..3a5eeeef65 100644 --- a/src/common/socket.hpp +++ b/src/common/socket.hpp @@ -98,6 +98,7 @@ struct socket_data size_t rdata_size, wdata_size; size_t rdata_pos; time_t rdata_tick; // time of last recv (for detecting timeouts); zero when timeout is disabled + time_t wdata_tick; // time of last send (for detecting timeouts); RecvFunc func_recv; SendFunc func_send; diff --git a/src/config/packets.hpp b/src/config/packets.hpp index 02c9687f49..91e6d37ee1 100644 --- a/src/config/packets.hpp +++ b/src/config/packets.hpp @@ -13,7 +13,7 @@ /// Do NOT edit this line! To set your client version, please do this instead: /// In Windows: Add this line in your src\custom\defines_pre.hpp file: #define PACKETVER YYYYMMDD /// In Linux: The same as above or run the following command: ./configure --enable-packetver=YYYYMMDD - #define PACKETVER 20180620 + #define PACKETVER 20200401 #endif #ifndef PACKETVER_RE @@ -24,6 +24,23 @@ #endif #endif +#ifndef PACKETVER_RE + #define PACKETVER_MAIN_NUM PACKETVER + + // Undefine all sakray server definitions + #undef PACKETVER_RE + #undef PACKETVER_RE_NUM +#else + // Undefine existing definition + #undef PACKETVER_RE + + #define PACKETVER_RE PACKETVER + #define PACKETVER_RE_NUM PACKETVER + + // Undefine all main server definitions + #undef PACKETVER_MAIN_NUM +#endif + #if PACKETVER >= 20110817 /// Comment to disable the official packet obfuscation support. /// This requires PACKETVER 2011-08-17 or newer. diff --git a/src/login/loginclif.cpp b/src/login/loginclif.cpp index 8672bf2cee..9d80fb309a 100644 --- a/src/login/loginclif.cpp +++ b/src/login/loginclif.cpp @@ -457,6 +457,20 @@ static int logclif_parse_reqcharconnec(int fd, struct login_session_data *sd, ch return 1; } +int logclif_parse_otp_login( int fd, struct login_session_data* sd ){ + RFIFOSKIP( fd, 68 ); + + WFIFOHEAD( fd, 34 ); + WFIFOW( fd, 0 ) = 0xae3; + WFIFOW( fd, 2 ) = 34; + WFIFOL( fd, 4 ) = 0; // normal login + safestrncpy( WFIFOCP( fd, 8 ), "S1000", 6 ); + safestrncpy( WFIFOCP( fd, 28 ), "token", 6 ); + WFIFOSET( fd, 34 ); + + return 1; +} + /** * Entry point from client to log-server. * Function that checks incoming command, then splits it to the correct handler. @@ -521,6 +535,10 @@ int logclif_parse(int fd) { break; // Sending request of the coding key case 0x01db: next = logclif_parse_reqkey(fd, sd); break; + // OTP token login + case 0x0acf: + next = logclif_parse_otp_login( fd, sd ); + break; // Connection request of a char-server case 0x2710: logclif_parse_reqcharconnec(fd,sd, ip); return 0; // processing will continue elsewhere default: diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 0b1aa9fe47..892f22c3ad 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -8954,6 +8954,8 @@ static const struct _battle_data { { "bgqueue_nowarp_mapflag", &battle_config.bgqueue_nowarp_mapflag, 0, 0, 1, }, { "homunculus_exp_gain", &battle_config.homunculus_exp_gain, 10, 0, 100, }, { "rental_item_novalue", &battle_config.rental_item_novalue, 1, 0, 1, }, + { "ping_timer_inverval", &battle_config.ping_timer_interval, 30, 0, 99999999, }, + { "ping_time", &battle_config.ping_time, 20, 0, 99999999, }, #include "../custom/battle_config_init.inc" }; diff --git a/src/map/battle.hpp b/src/map/battle.hpp index d9dae4b05d..15767c13b9 100644 --- a/src/map/battle.hpp +++ b/src/map/battle.hpp @@ -678,6 +678,8 @@ struct Battle_Config int bgqueue_nowarp_mapflag; int homunculus_exp_gain; int rental_item_novalue; + int ping_timer_interval; + int ping_time; #include "../custom/battle_config_struct.inc" }; diff --git a/src/map/buyingstore.cpp b/src/map/buyingstore.cpp index e79b4137be..7d4ccbf6a7 100644 --- a/src/map/buyingstore.cpp +++ b/src/map/buyingstore.cpp @@ -113,8 +113,7 @@ int8 buyingstore_setup(struct map_session_data* sd, unsigned char slots){ * @param at Autotrader info, or NULL if requetsed not from autotrade persistance * @return 0 If success, 1 - Cannot open, 2 - Manner penalty, 3 - Mapflag restiction, 4 - Cell restriction, 5 - Invalid count/result, 6 - Cannot give item, 7 - Will be overweight */ -int8 buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count, struct s_autotrader *at) -{ +int8 buyingstore_create( struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub* itemlist, unsigned int count, struct s_autotrader *at ){ unsigned int i, weight, listidx; char message_sql[MESSAGE_SIZE*2]; StringBuf buf; @@ -161,50 +160,53 @@ int8 buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha weight = sd->weight; // check item list - for( i = 0; i < count; i++ ) - {// itemlist: .W .W .L - unsigned short nameid, amount; - int price, idx; - struct item_data* id; + for( i = 0; i < count; i++ ){ + const struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub *item = &itemlist[i]; - nameid = RBUFW(itemlist,i*8+0); - amount = RBUFW(itemlist,i*8+2); - price = RBUFL(itemlist,i*8+4); + struct item_data* id = itemdb_exists( item->itemId ); - if( ( id = itemdb_exists(nameid) ) == NULL || amount == 0 ) - {// invalid input + // invalid input + if( id == NULL || item->amount == 0 ){ break; } - if( price <= 0 || price > BUYINGSTORE_MAX_PRICE ) - {// invalid price: unlike vending, items cannot be bought at 0 Zeny + // invalid price: unlike vending, items cannot be bought at 0 Zeny + if( item->price <= 0 || item->price > BUYINGSTORE_MAX_PRICE ){ break; } - if( !id->flag.buyingstore || !itemdb_cantrade_sub(id, pc_get_group_level(sd), pc_get_group_level(sd)) || ( idx = pc_search_inventory(sd, nameid) ) == -1 ) - {// restrictions: allowed, no character-bound items and at least one must be owned + // restrictions: allowed and no character-bound items + if( !id->flag.buyingstore || !itemdb_cantrade_sub( id, pc_get_group_level( sd ), pc_get_group_level( sd ) ) ){ break; } - if( sd->inventory.u.items_inventory[idx].amount + amount > BUYINGSTORE_MAX_AMOUNT ) - {// too many items of same kind + int idx = pc_search_inventory( sd, item->itemId ); + + // At least one must be owned + if( idx < 0 ){ break; } - if( i ) - {// duplicate check. as the client does this too, only malicious intent should be caught here - ARR_FIND( 0, i, listidx, sd->buyingstore.items[listidx].nameid == nameid ); - if( listidx != i ) - {// duplicate - ShowWarning("buyingstore_create: Found duplicate item on buying list (nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", nameid, amount, sd->status.account_id, sd->status.char_id); + // too many items of same kind + if( sd->inventory.u.items_inventory[idx].amount + item->amount > BUYINGSTORE_MAX_AMOUNT ){ + break; + } + + // duplicate check. as the client does this too, only malicious intent should be caught here + if( i ){ + ARR_FIND( 0, i, listidx, sd->buyingstore.items[listidx].nameid == item->itemId ); + + // duplicate + if( listidx != i ){ + ShowWarning( "buyingstore_create: Found duplicate item on buying list (nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", item->itemId, item->amount, sd->status.account_id, sd->status.char_id ); break; } } - weight+= id->weight*amount; - sd->buyingstore.items[i].nameid = nameid; - sd->buyingstore.items[i].amount = amount; - sd->buyingstore.items[i].price = price; + weight+= id->weight*item->amount; + sd->buyingstore.items[i].nameid = item->itemId; + sd->buyingstore.items[i].amount = item->amount; + sd->buyingstore.items[i].price = item->price; } if( i != count ) @@ -322,10 +324,9 @@ void buyingstore_open(struct map_session_data* sd, uint32 account_id) * @param *itemlist List of sold items { .W, .W, .W }* * @param count Number of item on the itemlist */ -void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count) -{ +void buyingstore_trade( struct map_session_data* sd, uint32 account_id, unsigned int buyer_id, const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub* itemlist, unsigned int count ){ int zeny = 0; - unsigned int i, weight, listidx, k; + unsigned int weight; struct map_session_data* pl_sd; nullpo_retv(sd); @@ -362,98 +363,94 @@ void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned searchstore_clearremote(sd); - if( pl_sd->status.zeny < pl_sd->buyingstore.zenylimit ) - {// buyer lost zeny in the mean time? fix the limit + // buyer lost zeny in the mean time? fix the limit + if( pl_sd->status.zeny < pl_sd->buyingstore.zenylimit ){ pl_sd->buyingstore.zenylimit = pl_sd->status.zeny; } weight = pl_sd->weight; // check item list - for( i = 0; i < count; i++ ) - {// itemlist: .W .W .W - unsigned short nameid, amount; - int index; + for( int i = 0; i < count; i++ ){ + const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub* item = &itemlist[i]; - index = RBUFW(itemlist,i*6+0)-2; - nameid = RBUFW(itemlist,i*6+2); - amount = RBUFW(itemlist,i*6+4); - - if( i ) - {// duplicate check. as the client does this too, only malicious intent should be caught here - ARR_FIND( 0, i, k, RBUFW(itemlist,k*6+0)-2 == index ); - if( k != i ) - {// duplicate - ShowWarning("buyingstore_trade: Found duplicate item on selling list (prevnameid=%hu, prevamount=%hu, nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", - RBUFW(itemlist,k*6+2), RBUFW(itemlist,k*6+4), nameid, amount, sd->status.account_id, sd->status.char_id); - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + // duplicate check. as the client does this too, only malicious intent should be caught here + for( int k = 0; k < i; k++ ){ + // duplicate + if( itemlist[k].index == item->index && k != i ){ + ShowWarning( "buyingstore_trade: Found duplicate item on selling list (prevnameid=%hu, prevamount=%hu, nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", itemlist[k].itemId, itemlist[k].amount, item->itemId, item->amount, sd->status.account_id, sd->status.char_id ); + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } } - if( index < 0 || index >= ARRAYLENGTH(sd->inventory.u.items_inventory) || sd->inventory_data[index] == NULL || sd->inventory.u.items_inventory[index].nameid != nameid || sd->inventory.u.items_inventory[index].amount < amount ) - {// invalid input - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + int index = item->index - 2; // TODO: clif::server_index + + // invalid input + if( index < 0 || index >= ARRAYLENGTH( sd->inventory.u.items_inventory ) || sd->inventory_data[index] == NULL || sd->inventory.u.items_inventory[index].nameid != item->itemId || sd->inventory.u.items_inventory[index].amount < item->amount ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } - if( sd->inventory.u.items_inventory[index].expire_time || (sd->inventory.u.items_inventory[index].bound && !pc_can_give_bounded_items(sd)) || !itemdb_cantrade(&sd->inventory.u.items_inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->inventory.u.items_inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) - {// non-tradable item - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + // non-tradable item + if( sd->inventory.u.items_inventory[index].expire_time || ( sd->inventory.u.items_inventory[index].bound && !pc_can_give_bounded_items( sd ) ) || !itemdb_cantrade( &sd->inventory.u.items_inventory[index], pc_get_group_level( sd ), pc_get_group_level( pl_sd ) ) || memcmp( sd->inventory.u.items_inventory[index].card, buyingstore_blankslots, sizeof( buyingstore_blankslots ) ) ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } - ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid ); - if( listidx == pl_sd->buyingstore.slots || pl_sd->buyingstore.items[listidx].amount == 0 ) - {// there is no such item or the buyer has already bought all of them - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + int listidx; + + ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == item->itemId ); + + // there is no such item or the buyer has already bought all of them + if( listidx == pl_sd->buyingstore.slots || pl_sd->buyingstore.items[listidx].amount == 0 ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } - if( pl_sd->buyingstore.items[listidx].amount < amount ) - {// buyer does not need that much of the item - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_COUNT, nameid); + // buyer does not need that much of the item + if( pl_sd->buyingstore.items[listidx].amount < item->amount ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_COUNT, item->itemId ); return; } - if( pc_checkadditem(pl_sd, nameid, amount) == CHKADDITEM_OVERAMOUNT ) - {// buyer does not have enough space for this item - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + // buyer does not have enough space for this item + if( pc_checkadditem( pl_sd, item->itemId, item->amount ) == CHKADDITEM_OVERAMOUNT ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } - if( amount*(unsigned int)sd->inventory_data[index]->weight > pl_sd->max_weight-weight ) - {// normally this is not supposed to happen, as the total weight is - // checked upon creation, but the buyer could have gained items - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + // normally this is not supposed to happen, as the total weight is + // checked upon creation, but the buyer could have gained items + if( item->amount * (unsigned int)sd->inventory_data[index]->weight > pl_sd->max_weight - weight ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } - weight+= amount*sd->inventory_data[index]->weight; - if( amount*pl_sd->buyingstore.items[listidx].price > pl_sd->buyingstore.zenylimit-zeny ) - {// buyer does not have enough zeny - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_ZENY, nameid); + weight += item->amount * sd->inventory_data[index]->weight; + + // buyer does not have enough zeny + if( item->amount * pl_sd->buyingstore.items[listidx].price > pl_sd->buyingstore.zenylimit - zeny ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_ZENY, item->itemId ); return; } - zeny+= amount*pl_sd->buyingstore.items[listidx].price; + + zeny += item->amount * pl_sd->buyingstore.items[listidx].price; } // process item list - for( i = 0; i < count; i++ ) - {// itemlist: .W .W .W - unsigned short nameid, amount; - int index; + for( int i = 0; i < count; i++ ){ + const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub* item = &itemlist[i]; + int listidx; - index = RBUFW(itemlist,i*6+0)-2; - nameid = RBUFW(itemlist,i*6+2); - amount = RBUFW(itemlist,i*6+4); + ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == item->itemId ); + zeny = item->amount * pl_sd->buyingstore.items[listidx].price; - ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid ); - zeny = amount*pl_sd->buyingstore.items[listidx].price; + int index = item->index - 2; // TODO: clif::server_index // move item - pc_additem(pl_sd, &sd->inventory.u.items_inventory[index], amount, LOG_TYPE_BUYING_STORE); - pc_delitem(sd, index, amount, 1, 0, LOG_TYPE_BUYING_STORE); - pl_sd->buyingstore.items[listidx].amount-= amount; + pc_additem(pl_sd, &sd->inventory.u.items_inventory[index], item->amount, LOG_TYPE_BUYING_STORE); + pc_delitem(sd, index, item->amount, 1, 0, LOG_TYPE_BUYING_STORE); + pl_sd->buyingstore.items[listidx].amount -= item->amount; if( pl_sd->buyingstore.items[listidx].amount > 0 ){ if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `amount` = %d WHERE `buyingstore_id` = %d AND `index` = %d;", buyingstore_items_table, pl_sd->buyingstore.items[listidx].amount, pl_sd->buyer_id, listidx ) != SQL_SUCCESS ){ @@ -471,8 +468,8 @@ void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned pl_sd->buyingstore.zenylimit-= zeny; // notify clients - clif_buyingstore_delete_item(sd, index, amount, pl_sd->buyingstore.items[listidx].price); - clif_buyingstore_update_item(pl_sd, nameid, amount, sd->status.char_id, zeny); + clif_buyingstore_delete_item(sd, index, item->amount, pl_sd->buyingstore.items[listidx].price); + clif_buyingstore_update_item(pl_sd, item->itemId, item->amount, sd->status.char_id, zeny); } if( save_settings&CHARSAVE_VENDING ) { @@ -481,6 +478,7 @@ void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned } // check whether or not there is still something to buy + int i; ARR_FIND( 0, pl_sd->buyingstore.slots, i, pl_sd->buyingstore.items[i].amount != 0 ); if( i == pl_sd->buyingstore.slots ) {// everything was bought @@ -548,7 +546,7 @@ bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_st for( idx = 0; idx < s->item_count; idx++ ) { - ARR_FIND( 0, sd->buyingstore.slots, i, sd->buyingstore.items[i].nameid == s->itemlist[idx] && sd->buyingstore.items[i].amount ); + ARR_FIND( 0, sd->buyingstore.slots, i, sd->buyingstore.items[i].nameid == s->itemlist[idx].itemId && sd->buyingstore.items[i].amount ); if( i == sd->buyingstore.slots ) {// not found continue; @@ -591,23 +589,15 @@ void buyingstore_reopen( struct map_session_data* sd ){ // Ready to open buyingstore for this char if ((at = (struct s_autotrader *)uidb_get(buyingstore_autotrader_db, sd->status.char_id)) && at->count && at->entries) { - uint8 *data, *p; - uint16 j, count; + struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub* data; // Init buyingstore data for autotrader - CREATE(data, uint8, at->count * 8); + CREATE(data, struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub, at->count ); - for (j = 0, p = data, count = at->count; j < at->count; j++) { - struct s_autotrade_entry *entry = at->entries[j]; - unsigned short *item_id = (uint16*)(p + 0); - uint16 *amount = (uint16*)(p + 2); - uint32 *price = (uint32*)(p + 4); - - *item_id = entry->item_id; - *amount = entry->amount; - *price = entry->price; - - p += 8; + for( int j = 0; j < at->count; j++) { + data[j].itemId = at->entries[j]->item_id; + data[j].amount = at->entries[j]->amount; + data[j].price = at->entries[j]->price; } sd->state.autotrade = 1; @@ -633,7 +623,7 @@ void buyingstore_reopen( struct map_session_data* sd ){ chrif_save(sd, CSAVE_AUTOTRADE); ShowInfo("Buyingstore loaded for '" CL_WHITE "%s" CL_RESET "' with '" CL_WHITE "%d" CL_RESET "' items at " CL_WHITE "%s (%d,%d)" CL_RESET "\n", - sd->status.name, count, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y); + sd->status.name, at->count, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y); } aFree(data); } diff --git a/src/map/buyingstore.hpp b/src/map/buyingstore.hpp index 0c052d4722..eab26da3ac 100644 --- a/src/map/buyingstore.hpp +++ b/src/map/buyingstore.hpp @@ -56,10 +56,10 @@ struct s_autotrader { }; int8 buyingstore_setup(struct map_session_data* sd, unsigned char slots); -int8 buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count, struct s_autotrader *at); +int8 buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub* itemlist, unsigned int count, struct s_autotrader *at); void buyingstore_close(struct map_session_data* sd); void buyingstore_open(struct map_session_data* sd, uint32 account_id); -void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count); +void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned int buyer_id, const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub* itemlist, unsigned int count); bool buyingstore_search(struct map_session_data* sd, unsigned short nameid); bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_store_search* s); DBMap *buyingstore_getdb(void); diff --git a/src/map/cashshop.cpp b/src/map/cashshop.cpp index 862a02e59a..50d77d89c7 100644 --- a/src/map/cashshop.cpp +++ b/src/map/cashshop.cpp @@ -467,7 +467,7 @@ static void cashshop_read_db( void ){ * @param item_list Array of item ID * @return true: success, false: fail */ -bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, uint16* item_list ){ +bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, struct PACKET_CZ_SE_PC_BUY_CASHITEM_LIST_sub* item_list ){ uint32 totalcash = 0; uint32 totalweight = 0; int i,new_; @@ -487,9 +487,9 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u new_ = 0; for( i = 0; i < n; ++i ){ - unsigned short nameid = *( item_list + i * 5 ); - uint32 quantity = *( item_list + i * 5 + 2 ); - uint8 tab = (uint8)*( item_list + i * 5 + 4 ); + unsigned short nameid = item_list[i].itemId; + uint32 quantity = item_list[i].amount; + uint16 tab = item_list[i].tab; int j; if( tab >= CASHSHOP_TAB_MAX ){ @@ -504,7 +504,7 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u return false; } - nameid = *( item_list + i * 5 ) = cash_shop_items[tab].item[j]->nameid; //item_avail replacement + nameid = item_list[i].itemId = cash_shop_items[tab].item[j]->nameid; //item_avail replacement id = itemdb_exists(nameid); if( !id ){ @@ -569,10 +569,10 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u } for( i = 0; i < n; ++i ){ - unsigned short nameid = *( item_list + i * 5 ); - uint32 quantity = *( item_list + i * 5 + 2 ); + unsigned short nameid = item_list[i].itemId; + uint32 quantity = item_list[i].amount; #if PACKETVER_SUPPORTS_SALES - uint16 tab = *(item_list + i * 5 + 4); + uint16 tab = item_list[i].tab; #endif struct item_data *id = itemdb_search(nameid); @@ -580,12 +580,12 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u continue; if (!pet_create_egg(sd, nameid)) { - unsigned short get_amt = quantity, j; + unsigned short get_amt = quantity; if (id->flag.guid || !itemdb_isstackable2(id)) get_amt = 1; - for (j = 0; j < quantity; j += get_amt) { + for (uint32 j = 0; j < quantity; j += get_amt) { struct item item_tmp = { 0 }; item_tmp.nameid = nameid; @@ -606,6 +606,8 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u return false; } + clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_SUCCESS ); + #if PACKETVER_SUPPORTS_SALES if( tab == CASHSHOP_TAB_SALE ){ uint32 new_amount = sale->amount - get_amt; @@ -627,7 +629,6 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u } } - clif_cashshop_result( sd, 0, CASHSHOP_RESULT_SUCCESS ); //Doesn't show any message? return true; } diff --git a/src/map/cashshop.hpp b/src/map/cashshop.hpp index a7c3530286..06636543fe 100644 --- a/src/map/cashshop.hpp +++ b/src/map/cashshop.hpp @@ -13,7 +13,7 @@ struct map_session_data; void do_init_cashshop( void ); void do_final_cashshop( void ); void cashshop_reloaddb( void ); -bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, uint16* item_list ); +bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, struct PACKET_CZ_SE_PC_BUY_CASHITEM_LIST_sub* item_list ); // Taken from AEGIS enum CASH_SHOP_TAB_CODE diff --git a/src/map/clif.cpp b/src/map/clif.cpp index a01f5d188c..05dee826bf 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -1,4 +1,5 @@ // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL +// Copyright (c) Hercules Dev Team - Licensed under GNU GPL // For more information, see LICENCE in the main folder #include "clif.hpp" @@ -73,6 +74,7 @@ unsigned long color_table[COLOR_MAX]; #include "clif_obfuscation.hpp" static bool clif_session_isValid(struct map_session_data *sd); +static void clif_loadConfirm( struct map_session_data *sd ); #if PACKETVER >= 20150513 enum mail_type { @@ -83,6 +85,13 @@ enum mail_type { }; #endif +enum e_inventory_type{ + INVTYPE_INVENTORY = 0, + INVTYPE_CART = 1, + INVTYPE_STORAGE = 2, + INVTYPE_GUILD_STORAGE = 3, +}; + /** Converts item type to display it on client if necessary. * @param nameid: Item ID * @return item type. For IT_PETEGG will be displayed as IT_ARMOR. If Shadow Weapon of IT_SHADOWGEAR as IT_WEAPON and else as IT_ARMOR @@ -99,6 +108,44 @@ static inline int itemtype(unsigned short nameid) { return ( type == IT_PETEGG ) ? IT_ARMOR : type; } +// TODO: doc +static inline uint16 client_index( uint16 server_index ){ + return server_index + 2; +} + +static inline uint16 server_index( uint16 client_index ){ + return client_index - 2; +} + +static inline uint16 client_storage_index( uint16 server_index ){ + return server_index + 1; +} + +static inline uint16 server_storage_index( uint16 client_index ){ + return client_index - 1; +} + +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 +static inline uint32 client_nameid( uint32 server_nameid ){ + uint32 view = itemdb_viewid( server_nameid ); + + if( view > 0 ){ + return view; + }else{ + return server_nameid; + } +} +#else +static inline uint16 client_nameid( uint32 server_nameid ){ + uint32 view = itemdb_viewid( server_nameid ); + + if( view > 0 ){ + return (uint16)view; + }else{ + return (uint16)server_nameid; + } +} +#endif static inline void WBUFPOS(uint8* p, unsigned short pos, short x, short y, unsigned char dir) { p += pos; @@ -391,7 +438,7 @@ static int clif_send_sub(struct block_list *bl, va_list ap) * Packet Delegation (called on all packets that require data to be sent to more than one client) * functions that are sent solely to one use whose ID it posses use WFIFOSET *------------------------------------------*/ -int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target type) +int clif_send(const void* buf, int len, struct block_list* bl, enum send_target type) { int i; struct map_session_data *sd, *tsd; @@ -781,56 +828,46 @@ void clif_charselectok(int id, uint8 ok) /// 009E .L .W .B .W .W .B .B .W (ZC_ITEM_FALL_ENTRY) /// 084B .L .W .W .B .W .W .B .B .W (ZC_ITEM_FALL_ENTRY4) /// 0ADD .L .W .W .B .W .W .B .B .W .B .W (ZC_ITEM_FALL_ENTRY5) -void clif_dropflooritem(struct flooritem_data* fitem, bool canShowEffect) -{ -#if PACKETVER >= 20180418 - uint8 buf[22]; - uint32 header = 0xadd; -#elif PACKETVER >= 20130000 - uint8 buf[19]; - uint32 header=0x84b; -#else - uint8 buf[17]; - uint32 header=0x09e; -#endif - int view, offset=0; - +void clif_dropflooritem( struct flooritem_data* fitem, bool canShowEffect ){ nullpo_retv(fitem); - if (fitem->item.nameid == 0) + if( fitem->item.nameid <= 0 ){ return; + } - WBUFW(buf, offset+0) = header; - WBUFL(buf, offset+2) = fitem->bl.id; - WBUFW(buf, offset+6) = ((view = itemdb_viewid(fitem->item.nameid)) > 0) ? view : fitem->item.nameid; -#if PACKETVER >= 20130000 - WBUFW(buf, offset+8) = itemtype(fitem->item.nameid); - offset +=2; + int view = itemdb_viewid( fitem->item.nameid ); + + struct packet_dropflooritem p; + + p.PacketType = dropflooritemType; + p.ITAID = fitem->bl.id; + p.ITID = ( view > 0 ) ? view : fitem->item.nameid; +#if PACKETVER >= 20130000 /* not sure date */ + p.type = itemtype( fitem->item.nameid ); #endif - WBUFB(buf, offset+8) = fitem->item.identify; - WBUFW(buf, offset+9) = fitem->bl.x; - WBUFW(buf, offset+11) = fitem->bl.y; - WBUFB(buf, offset+13) = fitem->subx; - WBUFB(buf, offset+14) = fitem->suby; - WBUFW(buf, offset+15) = fitem->item.amount; -#if PACKETVER >= 20180418 + p.IsIdentified = fitem->item.identify ? 1 : 0; + p.xPos = fitem->bl.x; + p.yPos = fitem->bl.y; + p.subX = fitem->subx; + p.subY = fitem->suby; + p.count = fitem->item.amount; +#if defined(PACKETVER_ZERO) || PACKETVER >= 20180418 if( canShowEffect ){ - uint8 dropEffect = itemdb_dropeffect(fitem->item.nameid); + uint8 dropEffect = itemdb_dropeffect( fitem->item.nameid ); if( dropEffect > 0 ){ - WBUFB(buf, offset+17) = 1; - WBUFW(buf, offset+18) = dropEffect - 1; + p.showdropeffect = 1; + p.dropeffectmode = dropEffect - 1; }else{ - WBUFB(buf, offset+17) = 0; - WBUFW(buf, offset+18) = 0; + p.showdropeffect = 0; + p.dropeffectmode = 0; } }else{ - WBUFB(buf, offset+17) = 0; - WBUFW(buf, offset+18) = 0; + p.showdropeffect = 0; + p.dropeffectmode = 0; } #endif - - clif_send(buf, packet_len(header), &fitem->bl, AREA); + clif_send( &p, sizeof(p), &fitem->bl, AREA ); } @@ -982,349 +1019,398 @@ static int clif_setlevel(struct block_list* bl) { /*========================================== * Prepares 'unit standing/spawning' packet *------------------------------------------*/ -static int clif_set_unit_idle(struct block_list* bl, unsigned char* buffer, bool spawn, bool option, bool walking, unsigned int option_val) -{ - struct map_session_data* sd; - struct status_change* sc = status_get_sc(bl); - struct view_data* vd = status_get_viewdata(bl); +static void clif_set_unit_idle( struct block_list* bl, bool walking, send_target target, struct block_list* tbl ){ + nullpo_retv( bl ); - unsigned char *buf = WBUFP(buffer, 0); -#if PACKETVER < 20091103 - bool type = !pcdb_checkid(vd->class_); -#endif - unsigned short offset = 0; -#if PACKETVER >= 20091103 - const char *name; -#endif - sd = BL_CAST(BL_PC, bl); - - if (!option) - option_val = ((sc) ? sc->option : 0); + struct map_session_data* sd = BL_CAST( BL_PC, bl ); + struct status_change* sc = status_get_sc( bl ); + struct view_data* vd = status_get_viewdata( bl ); + int g_id = status_get_guild_id( bl ); #if PACKETVER < 20091103 - if(type) - WBUFW(buf,0) = spawn ? 0x7c : 0x78; - else -#endif -#if PACKETVER < 4 - WBUFW(buf,0) = spawn ? 0x79 : 0x78; -#elif PACKETVER < 7 - WBUFW(buf,0) = spawn ? 0x1d9 : 0x1d8; -#elif PACKETVER < 20080102 - WBUFW(buf,0) = spawn ? 0x22b : 0x22a; -#elif PACKETVER < 20091103 - WBUFW(buf,0) = spawn ? 0x2ed : 0x2ee; -#elif PACKETVER < 20101124 - WBUFW(buf,0) = spawn ? 0x7f8 : 0x7f9; -#elif PACKETVER < 20120221 - WBUFW(buf,0) = spawn ? 0x858 : 0x857; -#elif PACKETVER < 20131223 - WBUFW(buf,0) = spawn ? 0x90f : 0x915; -#elif PACKETVER < 20150513 - WBUFW(buf,0) = spawn ? 0x9dc : 0x9dd; -#else - WBUFW(buf,0) = spawn ? 0x9fe : 0x9ff; -#endif + if( !pcdb_checkid( vd->class_ ) ){ + struct packet_idle_unit2 p; -#if PACKETVER >= 20091103 - name = status_get_name(bl); -#if PACKETVER < 20110111 - WBUFW(buf,2) = (uint16)((spawn ? 62 : 63)+strlen(name)); -#elif PACKETVER < 20120221 - WBUFW(buf,2) = (uint16)((spawn ? 64 : 65)+strlen(name)); -#elif PACKETVER < 20130807 - WBUFW(buf,2) = (uint16)((spawn ? 77 : 78)+strlen(name)); -#else - WBUFW(buf,2) = (uint16)((spawn ? 79 : 80)+strlen(name)); + p.PacketType = idle_unit2Type; +#if PACKETVER >= 20071106 + p.objecttype = clif_bl_type( bl, walking ); #endif - WBUFB(buf,4) = clif_bl_type(bl,walking); - offset+=3; - buf = WBUFP(buffer,offset); -#elif PACKETVER >= 20071106 - if (type) { //Non-player packets - WBUFB(buf,2) = clif_bl_type(bl); - offset++; - buf = WBUFP(buffer,offset); + p.GID = bl->id; + p.speed = status_get_speed( bl ); + p.bodyState = ( sc ) ? sc->opt1 : 0; + p.healthState = ( sc ) ? sc->opt2 : 0; + p.effectState = ( sc ) ? sc->option : 0; + p.job = vd->class_; + p.head = vd->hair_style; + p.weapon = vd->weapon; + p.accessory = vd->head_bottom; + if( bl->type == BL_NPC && vd->class_ == JT_GUILD_FLAG ){ + // The hell, why flags work like this? + p.shield = status_get_emblem_id( bl ); + p.accessory2 = GetWord( g_id, 1 ); + p.accessory3 = GetWord( g_id, 0 ); + }else{ + p.shield = vd->shield; + p.accessory2 = vd->head_top; + p.accessory3 = vd->head_mid; + } + p.headpalette = vd->hair_color; + p.bodypalette = vd->cloth_color; + p.headDir = ( sd )? sd->head_dir : 0; + p.GUID = g_id; + p.GEmblemVer = status_get_emblem_id( bl ); + p.honor = ( sd ) ? sd->status.manner : 0; + p.virtue = ( sc ) ? sc->opt3 : 0; + p.isPKModeON = ( sd && sd->status.karma ) ? 1 : 0; + p.sex = vd->sex; + WBUFPOS( &p.PosDir[0], 0, bl->x, bl->y, unit_getdir( bl ) ); + p.xSize = p.ySize = ( sd ) ? 5 : 0; + p.state = vd->dead_sit; + p.clevel = clif_setlevel( bl ); + + clif_send( &p, sizeof( p ), tbl, target ); + + return; } #endif - WBUFL(buf, 2) = bl->id; + + struct packet_idle_unit p; + + p.PacketType = idle_unitType; +#if PACKETVER >= 20091103 + p.PacketLength = sizeof(p); + p.objecttype = clif_bl_type( bl, walking ); +#endif #if PACKETVER >= 20131223 - WBUFL(buf,6) = (sd) ? sd->status.char_id : 0; // GID/CCODE - offset+=4; - buf = WBUFP(buffer,offset); -#endif - WBUFW(buf, 6) = status_get_speed(bl); - WBUFW(buf, 8) = (sc)? sc->opt1 : 0; - WBUFW(buf,10) = (sc)? sc->opt2 : 0; -#if PACKETVER < 20091103 - if (type&&spawn) { //uses an older and different packet structure - WBUFW(buf,12) = option_val; - WBUFW(buf,14) = vd->hair_style; - WBUFW(buf,16) = vd->weapon; - WBUFW(buf,18) = vd->head_bottom; - WBUFW(buf,20) = vd->class_; //Pet armor (ignored by client) - WBUFW(buf,22) = vd->shield; - } else { -#endif -#if PACKETVER >= 20091103 - WBUFL(buf,12) = option_val; - offset+=2; - buf = WBUFP(buffer,offset); -#elif PACKETVER >= 7 - if (!type) { - WBUFL(buf,12) = option_val; - offset+=2; - buf = WBUFP(buffer,offset); - } else - WBUFW(buf,12) = option_val; + p.AID = bl->id; + p.GID = (sd) ? sd->status.char_id : 0; // CCODE #else - WBUFW(buf,12) = option_val; + p.GID = bl->id; #endif - WBUFW(buf,14) = vd->class_; - WBUFW(buf,16) = vd->hair_style; - WBUFW(buf,18) = vd->weapon; -#if PACKETVER < 4 - WBUFW(buf,20) = vd->head_bottom; - WBUFW(buf,22) = vd->shield; -#else - WBUFW(buf,20) = vd->shield; - WBUFW(buf,22) = vd->head_bottom; -#endif -#if PACKETVER < 20091103 - } -#endif - WBUFW(buf,24) = vd->head_top; - WBUFW(buf,26) = vd->head_mid; + p.speed = status_get_speed(bl); + p.bodyState = (sc) ? sc->opt1 : 0; + p.healthState = (sc) ? sc->opt2 : 0; - if( bl->type == BL_NPC && vd->class_ == JT_GUILD_FLAG ) - { //The hell, why flags work like this? - WBUFW(buf,22) = status_get_emblem_id(bl); - WBUFW(buf,24) = GetWord(status_get_guild_id(bl), 1); - WBUFW(buf,26) = GetWord(status_get_guild_id(bl), 0); - } + // npc option changed? + if( tbl && tbl->type == BL_PC && bl->type == BL_NPC ){ + struct map_session_data* sd = (struct map_session_data*)tbl; + struct npc_data* nd = (struct npc_data*)bl; + int option = (sc) ? sc->option : 0; - WBUFW(buf,28) = vd->hair_color; - WBUFW(buf,30) = vd->cloth_color; - WBUFW(buf,32) = (sd)? sd->head_dir : 0; -#if PACKETVER < 20091103 - if (type&&spawn) { //End of packet 0x7c - WBUFB(buf,34) = (sd) ? sd->status.karma : 0; // karma - WBUFB(buf,35) = vd->sex; - WBUFPOS(buf,36, bl->x, bl->y, unit_getdir(bl)); - WBUFB(buf,39) = 0; - WBUFB(buf,40) = 0; - return packet_len(0x7c); + if( !nd->vd.dead_sit ){ + if( std::find( sd->cloaked_npc.begin(), sd->cloaked_npc.end(), nd->bl.id ) != sd->cloaked_npc.end() ){ + option ^= OPTION_CLOAK; + } + } + + p.effectState = option; + }else{ + p.effectState = (sc) ? sc->option : 0; } + p.job = vd->class_; + p.head = vd->hair_style; + p.weapon = vd->weapon; +#if PACKETVER < 7 || PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + p.shield = vd->shield; #endif -#if PACKETVER >= 20110111 - WBUFW(buf,34) = vd->robe; - offset+= 2; - buf = WBUFP(buffer,offset); -#endif - WBUFL(buf,34) = status_get_guild_id(bl); - WBUFW(buf,38) = status_get_emblem_id(bl); - WBUFW(buf,40) = (sd)? sd->status.manner : 0; -#if PACKETVER >= 20091103 - WBUFL(buf,42) = (sc)? sc->opt3 : 0; - offset+=2; - buf = WBUFP(buffer,offset); -#elif PACKETVER >= 7 - if (!type) { - WBUFL(buf,42) = (sc)? sc->opt3 : 0; - offset+=2; - buf = WBUFP(buffer,offset); - } else - WBUFW(buf,42) = (sc)? sc->opt3 : 0; -#else - WBUFW(buf,42) = (sc)? sc->opt3 : 0; -#endif - WBUFB(buf,44) = (sd)? sd->status.karma : 0; - WBUFB(buf,45) = vd->sex; - WBUFPOS(buf,46,bl->x,bl->y,unit_getdir(bl)); - WBUFB(buf,49) = (sd)? 5 : 0; - WBUFB(buf,50) = (sd)? 5 : 0; - if (!spawn) { - WBUFB(buf,51) = vd->dead_sit; - offset++; - buf = WBUFP(buffer,offset); + if( bl->type == BL_NPC && vd->class_ == JT_GUILD_FLAG ){ + // The hell, why flags work like this? + p.accessory = status_get_emblem_id( bl ); + p.accessory2 = GetWord( g_id, 1 ); + p.accessory3 = GetWord( g_id, 0 ); + }else{ + p.accessory = vd->head_bottom; + p.accessory2 = vd->head_top; + p.accessory3 = vd->head_mid; } - WBUFW(buf,51) = clif_setlevel(bl); -#if PACKETVER < 20091103 - if (type) //End for non-player packet - return packet_len(WBUFW(buffer,0)); + p.headpalette = vd->hair_color; + p.bodypalette = vd->cloth_color; + p.headDir = (sd)? sd->head_dir : 0; +#if PACKETVER >= 20101124 + p.robe = vd->robe; #endif + p.GUID = g_id; + p.GEmblemVer = status_get_emblem_id( bl ); + p.honor = (sd) ? sd->status.manner : 0; + p.virtue = (sc) ? sc->opt3 : 0; + p.isPKModeON = (sd && sd->status.karma) ? 1 : 0; + p.sex = vd->sex; + WBUFPOS( &p.PosDir[0], 0, bl->x, bl->y, unit_getdir( bl ) ); + p.xSize = p.ySize = (sd) ? 5 : 0; + p.state = vd->dead_sit; + p.clevel = clif_setlevel( bl ); #if PACKETVER >= 20080102 - WBUFW(buf,53) = (sd ? sd->status.font : 0); + p.font = (sd) ? sd->status.font : 0; #endif #if PACKETVER >= 20120221 - if ( battle_config.monster_hp_bars_info && bl->type == BL_MOB && !map_getmapflag(bl->m, MF_HIDEMOBHPBAR) && (status_get_hp(bl) < status_get_max_hp(bl)) ) { - WBUFL(buf,55) = status_get_max_hp(bl); // maxHP - WBUFL(buf,59) = status_get_hp(bl); // HP - } else { - WBUFL(buf,55) = -1; // maxHP - WBUFL(buf,59) = -1; // HP + if( battle_config.monster_hp_bars_info && !map_getmapflag( bl->m, MF_HIDEMOBHPBAR ) && bl->type == BL_MOB && ( status_get_hp( bl ) < status_get_max_hp( bl ) ) ){ + p.maxHP = status_get_max_hp(bl); + p.HP = status_get_hp(bl); + }else{ + p.maxHP = -1; + p.HP = -1; } - - WBUFB(buf,63) = ( bl->type == BL_MOB && (((TBL_MOB*)bl)->db->mexp > 0) ) ? 1 : 0; // isBoss + p.isBoss = ( bl->type == BL_MOB && ( ( ( TBL_MOB *)bl )->db->mexp > 0 ) ) ? 1 : 0; #endif #if PACKETVER >= 20150513 - WBUFW(buf,64) = vd->body_style; // body - offset+= 2; - buf = WBUFP(buffer,offset); + p.body = vd->body_style; #endif +/* Might be earlier, this is when the named item bug began */ +#if PACKETVER >= 20131223 + safestrncpy(p.name, status_get_name( bl ), NAME_LENGTH); +#endif + + if( disguised( bl ) ){ #if PACKETVER >= 20091103 + p.objecttype = pcdb_checkid( status_get_viewdata( bl )->class_ ) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE +#if PACKETVER >= 20131223 + p.AID = -bl->id; +#else + p.GID = -bl->id; +#endif +#else + p.GID = -bl->id; +#endif + } + + clif_send( &p, sizeof( p ), tbl, target ); +} + +static void clif_spawn_unit( struct block_list *bl, enum send_target target ){ + nullpo_retv( bl ); + + struct map_session_data* sd = BL_CAST( BL_PC, bl ); + struct status_change* sc = status_get_sc( bl ); + struct view_data* vd = status_get_viewdata( bl ); + int g_id = status_get_guild_id( bl ); + +#if PACKETVER < 20091103 + if( !pcdb_checkid( vd->class_ ) ){ + struct packet_spawn_unit2 p; + + p.PacketType = spawn_unit2Type; +#if PACKETVER >= 20071106 + p.objecttype = clif_bl_type( bl, false ); +#endif + p.GID = bl->id; + p.speed = status_get_speed( bl ); + p.bodyState = ( sc ) ? sc->opt1 : 0; + p.healthState = ( sc ) ? sc->opt2 : 0; + p.effectState = ( sc ) ? sc->option : 0; + p.head = vd->hair_style; + p.weapon = vd->weapon; + p.accessory = vd->head_bottom; + p.job = vd->class_; + if( bl->type == BL_NPC && vd->class_ == JT_GUILD_FLAG ){ + // The hell, why flags work like this? + p.shield = status_get_emblem_id( bl ); + p.accessory2 = GetWord( g_id, 1 ); + p.accessory3 = GetWord( g_id, 0 ); + }else{ + p.shield = vd->shield; + p.accessory2 = vd->head_top; + p.accessory3 = vd->head_mid; + } + p.headpalette = vd->hair_color; + p.bodypalette = vd->cloth_color; + p.headDir = ( sd ) ? sd->head_dir : 0; + p.isPKModeON = ( sd && sd->status.karma ) ? 1 : 0; + p.sex = vd->sex; + WBUFPOS( &p.PosDir[0], 0, bl->x, bl->y, unit_getdir( bl ) ); + p.xSize = p.ySize = ( sd ) ? 5 : 0; + + clif_send( &p, sizeof( p ), bl, target ); + return; + } +#endif + + struct packet_spawn_unit p; + + p.PacketType = spawn_unitType; +#if PACKETVER >= 20091103 + p.PacketLength = sizeof(p); + p.objecttype = clif_bl_type( bl, false ); +#endif +#if PACKETVER >= 20131223 + p.AID = bl->id; + p.GID = (sd) ? sd->status.char_id : 0; // CCODE +#else + p.GID = bl->id; +#endif + p.speed = status_get_speed( bl ); + p.bodyState = (sc) ? sc->opt1 : 0; + p.healthState = (sc) ? sc->opt2 : 0; + p.effectState = (sc) ? sc->option : 0; + p.job = vd->class_; + p.head = vd->hair_style; + p.weapon = vd->weapon; +#if PACKETVER < 7 || PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + p.shield = vd->shield; +#endif + if( bl->type == BL_NPC && vd->class_ == JT_GUILD_FLAG ){ + // The hell, why flags work like this? + p.accessory = status_get_emblem_id( bl ); + p.accessory2 = GetWord( g_id, 1 ); + p.accessory3 = GetWord( g_id, 0 ); + }else{ + p.accessory = vd->head_bottom; + p.accessory2 = vd->head_top; + p.accessory3 = vd->head_mid; + } + p.headpalette = vd->hair_color; + p.bodypalette = vd->cloth_color; + p.headDir = (sd)? sd->head_dir : 0; +#if PACKETVER >= 20101124 + p.robe = vd->robe; +#endif + p.GUID = g_id; + p.GEmblemVer = status_get_emblem_id( bl ); + p.honor = (sd) ? sd->status.manner : 0; + p.virtue = (sc) ? sc->opt3 : 0; + p.isPKModeON = (sd && sd->status.karma) ? 1 : 0; + p.sex = vd->sex; + WBUFPOS( &p.PosDir[0], 0, bl->x, bl->y, unit_getdir( bl ) ); + p.xSize = p.ySize = (sd) ? 5 : 0; + p.clevel = clif_setlevel( bl ); +#if PACKETVER >= 20080102 + p.font = (sd) ? sd->status.font : 0; +#endif #if PACKETVER >= 20120221 - safestrncpy(WBUFCP(buf,64), name, NAME_LENGTH); -#else - safestrncpy(WBUFCP(buf,55), name, NAME_LENGTH); + if( battle_config.monster_hp_bars_info && bl->type == BL_MOB && !map_getmapflag( bl->m, MF_HIDEMOBHPBAR ) && ( status_get_hp( bl ) < status_get_max_hp( bl ) ) ){ + p.maxHP = status_get_max_hp( bl ); + p.HP = status_get_hp( bl ); + }else{ + p.maxHP = -1; + p.HP = -1; + } + + p.isBoss = ( bl->type == BL_MOB && ( ( ( TBL_MOB *)bl)->db->mexp > 0 ) ) ? 1 : 0; #endif - return WBUFW(buffer,2); -#else - return packet_len(WBUFW(buffer,0)); +#if PACKETVER >= 20150513 + p.body = vd->body_style; #endif +/* Might be earlier, this is when the named item bug began */ +#if PACKETVER >= 20131223 + safestrncpy( p.name, status_get_name( bl ), NAME_LENGTH ); +#endif + + if( disguised( bl ) ){ + nullpo_retv( sd ); + + if( sd->status.class_ != sd->disguise ){ + clif_send( &p, sizeof( p ), bl, target ); + } + +#if PACKETVER >= 20091103 + p.objecttype = pcdb_checkid( status_get_viewdata(bl)->class_ ) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE +#if PACKETVER >= 20131223 + p.AID = -bl->id; +#else + p.GID = -bl->id; +#endif +#else + p.GID = -bl->id; +#endif + clif_send( &p, sizeof( p ), bl, SELF ); + }else{ + clif_send( &p, sizeof( p ), bl, target ); + } } /*========================================== * Prepares 'unit walking' packet *------------------------------------------*/ -static int clif_set_unit_walking(struct block_list* bl, struct unit_data* ud, unsigned char* buffer) -{ - struct map_session_data* sd; - struct status_change* sc = status_get_sc(bl); - struct view_data* vd = status_get_viewdata(bl); +static void clif_set_unit_walking( struct block_list *bl, struct map_session_data *tsd, struct unit_data *ud, enum send_target target ){ + nullpo_retv( bl ); + nullpo_retv( ud ); - unsigned char* buf = WBUFP(buffer,0); -#if PACKETVER >= 7 - unsigned short offset = 0; -#endif -#if PACKETVER >= 20091103 - const char *name; -#endif + struct map_session_data* sd; + struct status_change* sc = status_get_sc( bl ); + struct view_data* vd = status_get_viewdata( bl ); + struct packet_unit_walking p; + int g_id = status_get_guild_id(bl); sd = BL_CAST(BL_PC, bl); -#if PACKETVER < 4 - WBUFW(buf, 0) = 0x7b; -#elif PACKETVER < 7 - WBUFW(buf, 0) = 0x1da; -#elif PACKETVER < 20080102 - WBUFW(buf, 0) = 0x22c; -#elif PACKETVER < 20091103 - WBUFW(buf, 0) = 0x2ec; -#elif PACKETVER < 20101124 - WBUFW(buf, 0) = 0x7f7; -#elif PACKETVER < 20120221 - WBUFW(buf, 0) = 0x856; -#elif PACKETVER < 20131223 - WBUFW(buf, 0) = 0x914; -#elif PACKETVER < 20150513 - WBUFW(buf, 0) = 0x9db; -#else - WBUFW(buf, 0) = 0x9fd; -#endif - + p.PacketType = unit_walkingType; #if PACKETVER >= 20091103 - name = status_get_name(bl); -#if PACKETVER < 20110111 - WBUFW(buf, 2) = (uint16)(69+strlen(name)); -#elif PACKETVER < 20120221 - WBUFW(buf, 2) = (uint16)(71+strlen(name)); -#elif PACKETVER < 20130807 - WBUFW(buf, 2) = (uint16)(84+strlen(name)); -#else - WBUFW(buf, 2) = (uint16)(86+strlen(name)); -#endif - offset+=2; - buf = WBUFP(buffer,offset); + p.PacketLength = sizeof(p); #endif #if PACKETVER >= 20071106 - WBUFB(buf, 2) = clif_bl_type(bl,true); - offset++; - buf = WBUFP(buffer,offset); + p.objecttype = clif_bl_type( bl, true ); #endif - WBUFL(buf, 2) = bl->id; #if PACKETVER >= 20131223 - WBUFL(buf,6) = (sd) ? sd->status.char_id : 0; // GID/CCODE - offset+=4; - buf = WBUFP(buffer,offset); -#endif - WBUFW(buf, 6) = status_get_speed(bl); - WBUFW(buf, 8) = (sc)? sc->opt1 : 0; - WBUFW(buf,10) = (sc)? sc->opt2 : 0; -#if PACKETVER < 7 - WBUFW(buf,12) = (sc)? sc->option : 0; + p.AID = bl->id; + p.GID = (sd) ? sd->status.char_id : 0; // CCODE #else - WBUFL(buf,12) = (sc)? sc->option : 0; - offset+=2; //Shift the rest of elements by 2 bytes. - buf = WBUFP(buffer,offset); + p.GID = bl->id; #endif - WBUFW(buf,14) = vd->class_; - WBUFW(buf,16) = vd->hair_style; - WBUFW(buf,18) = vd->weapon; -#if PACKETVER < 4 - WBUFW(buf,20) = vd->head_bottom; - WBUFL(buf,22) = client_tick(gettick()); - WBUFW(buf,26) = vd->shield; -#else - WBUFW(buf,20) = vd->shield; - WBUFW(buf,22) = vd->head_bottom; - WBUFL(buf,24) = client_tick(gettick()); + p.speed = status_get_speed(bl); + p.bodyState = (sc) ? sc->opt1 : 0; + p.healthState = (sc) ? sc->opt2 : 0; + p.effectState = (sc) ? sc->option : 0; + p.job = vd->class_; + p.head = vd->hair_style; + p.weapon = vd->weapon; + p.accessory = vd->head_bottom; + p.moveStartTime = client_tick(gettick()); +#if PACKETVER < 7 || PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + p.shield = vd->shield; #endif - WBUFW(buf,28) = vd->head_top; - WBUFW(buf,30) = vd->head_mid; - WBUFW(buf,32) = vd->hair_color; - WBUFW(buf,34) = vd->cloth_color; - WBUFW(buf,36) = (sd)? sd->head_dir : 0; -#if PACKETVER >= 20110111 - WBUFW(buf,38) = vd->robe; - offset+= 2; - buf = WBUFP(buffer,offset); + p.accessory2 = vd->head_top; + p.accessory3 = vd->head_mid; + p.headpalette = vd->hair_color; + p.bodypalette = vd->cloth_color; + p.headDir = (sd) ? sd->head_dir : 0; +#if PACKETVER >= 20101124 + p.robe = vd->robe; #endif - WBUFL(buf,38) = status_get_guild_id(bl); - WBUFW(buf,42) = status_get_emblem_id(bl); - WBUFW(buf,44) = (sd)? sd->status.manner : 0; -#if PACKETVER < 7 - WBUFW(buf,46) = (sc)? sc->opt3 : 0; -#else - WBUFL(buf,46) = (sc)? sc->opt3 : 0; - offset+=2; //Shift the rest of elements by 2 bytes. - buf = WBUFP(buffer,offset); -#endif - WBUFB(buf,48) = (sd)? sd->status.karma : 0; - WBUFB(buf,49) = vd->sex; - WBUFPOS2(buf,50,bl->x,bl->y,ud->to_x,ud->to_y,8,8); - WBUFB(buf,56) = (sd)? 5 : 0; - WBUFB(buf,57) = (sd)? 5 : 0; - WBUFW(buf,58) = clif_setlevel(bl); + p.GUID = g_id; + p.GEmblemVer = status_get_emblem_id(bl); + p.honor = (sd) ? sd->status.manner : 0; + p.virtue = (sc) ? sc->opt3 : 0; + p.isPKModeON = (sd && sd->status.karma) ? 1 : 0; + p.sex = vd->sex; + WBUFPOS2( &p.MoveData[0], 0, bl->x, bl->y, ud->to_x, ud->to_y, 8, 8 ); + p.xSize = p.ySize = (sd) ? 5 : 0; + p.clevel = clif_setlevel(bl); #if PACKETVER >= 20080102 - WBUFW(buf,60) = (sd ? sd->status.font : 0); + p.font = (sd) ? sd->status.font : 0; #endif #if PACKETVER >= 20120221 - if ( battle_config.monster_hp_bars_info && !map_getmapflag(bl->m, MF_HIDEMOBHPBAR) && bl->type == BL_MOB && (status_get_hp(bl) < status_get_max_hp(bl)) ) { - WBUFL(buf,62) = status_get_max_hp(bl); // maxHP - WBUFL(buf,66) = status_get_hp(bl); // HP + if( battle_config.monster_hp_bars_info && !map_getmapflag(bl->m, MF_HIDEMOBHPBAR) && bl->type == BL_MOB && (status_get_hp(bl) < status_get_max_hp( bl ) ) ){ + p.maxHP = status_get_max_hp(bl); + p.HP = status_get_hp(bl); } else { - WBUFL(buf,62) = -1; // maxHP - WBUFL(buf,66) = -1; // HP + p.maxHP = -1; + p.HP = -1; } - WBUFB(buf,70) = ( bl->type == BL_MOB && (((TBL_MOB*)bl)->db->mexp > 0) ) ? 1 : 0; // isBoss + p.isBoss = ( bl->type == BL_MOB && (((TBL_MOB*)bl)->db->mexp > 0) ) ? 1 : 0; #endif #if PACKETVER >= 20150513 - WBUFW(buf,71) = vd->body_style; // body - offset+= 2; - buf = WBUFP(buffer,offset); + p.body = vd->body_style; #endif +/* Might be earlier, this is when the named item bug began */ +#if PACKETVER >= 20131223 + safestrncpy(p.name, status_get_name(bl), NAME_LENGTH); +#endif + + clif_send( &p, sizeof(p), tsd ? &tsd->bl : bl, target ); + + if( disguised( bl ) ){ #if PACKETVER >= 20091103 -#if PACKETVER >= 20120221 - safestrncpy(WBUFCP(buf,71), name, NAME_LENGTH); + p.objecttype = pcdb_checkid( status_get_viewdata(bl)->class_ ) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE +#if PACKETVER >= 20131223 + p.AID = -bl->id; #else - safestrncpy(WBUFCP(buf,62), name, NAME_LENGTH); + p.GID = -bl->id; #endif - return WBUFW(buffer,2); #else - return packet_len(WBUFW(buffer,0)); + p.GID = -bl->id; #endif + clif_send(&p,sizeof(p),bl,SELF); + } } //Modifies the buffer for disguise characters and sends it to self. @@ -1467,11 +1553,8 @@ void clif_weather(int16 m) /** * Main function to spawn a unit on the client (player/mob/pet/etc) **/ -int clif_spawn(struct block_list *bl, bool walking) -{ - unsigned char buf[128]; +int clif_spawn( struct block_list *bl, bool walking ){ struct view_data *vd; - int len; vd = status_get_viewdata(bl); if( !vd || vd->class_ == JT_INVISIBLE ) @@ -1483,10 +1566,11 @@ int clif_spawn(struct block_list *bl, bool walking) if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE)) return 0; - len = clif_set_unit_idle(bl, buf, (bl->type == BL_NPC && vd->dead_sit ? false : true), false, walking, 0); - clif_send(buf, len, bl, AREA_WOS); - if (disguised(bl)) - clif_setdisguise(bl, buf, len); + if( bl->type == BL_NPC && !vd->dead_sit ){ + clif_set_unit_idle( bl, walking, AREA_WOS, bl ); + }else{ + clif_spawn_unit( bl, AREA_WOS ); + } if (vd->cloth_color) clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS); @@ -1548,95 +1632,87 @@ int clif_spawn(struct block_list *bl, bool walking) /// Sends information about owned homunculus to the client . [orn] /// 022e .24B .B .W .W .W .W .W .W .W .W .W .W .W .W .W .W .W .W .L .L .W .W (ZC_PROPERTY_HOMUN) /// 09f7 .24B .B .W .W .W .W .W .W .W .W .W .W .W .W .L .L .W .W .L .L .W .W (ZC_PROPERTY_HOMUN_2) -void clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int flag) -{ - struct status_data *status; - unsigned char buf[128]; - int offset; -#if PACKETVER < 20141016 - const int cmd = 0x22e; -#else - const int cmd = 0x9f7; -#endif - int htype; +void clif_hominfo( struct map_session_data *sd, struct homun_data *hd, int flag ){ +#if PACKETVER_MAIN_NUM >= 20101005 || PACKETVER_RE_NUM >= 20080827 || defined(PACKETVER_ZERO) + nullpo_retv( sd ); + nullpo_retv( hd ); - nullpo_retv(hd); + struct status_data *status = &hd->battle_status; + struct PACKET_ZC_PROPERTY_HOMUN p; - if (!clif_session_isValid(sd)) - return; - - status = &hd->battle_status; - htype = hom_class2type(hd->homunculus.class_); - - memset(buf,0,packet_len(cmd)); - WBUFW(buf,0) = cmd; - safestrncpy(WBUFCP(buf,2), hd->homunculus.name, NAME_LENGTH); + p.packetType = HEADER_ZC_PROPERTY_HOMUN; + safestrncpy( p.name, hd->homunculus.name, sizeof( p.name ) ); // Bit field, bit 0 : rename_flag (1 = already renamed), bit 1 : homunc vaporized (1 = true), bit 2 : homunc dead (1 = true) - WBUFB(buf,26) = (battle_config.hom_rename ? 0 : hd->homunculus.rename_flag) | (hd->homunculus.vaporize << 1) | (hd->homunculus.hp ? 0 : 4); - WBUFW(buf,27) = hd->homunculus.level; - WBUFW(buf,29) = hd->homunculus.hunger; - WBUFW(buf,31) = (unsigned short) (hd->homunculus.intimacy / 100) ; - WBUFW(buf,33) = 0; // equip id - WBUFW(buf,35) = cap_value(status->rhw.atk2 + status->batk, 0, INT16_MAX); - WBUFW(buf,37)=i16min(status->matk_max, INT16_MAX); //FIXME capping to INT16 here is too late - WBUFW(buf,39)=status->hit; - if (battle_config.hom_setting&HOMSET_DISPLAY_LUK) - WBUFW(buf,41) = status->luk/3 + 1; //crit is a +1 decimal value! Just display purpose.[Vicious] - else - WBUFW(buf,41) = status->cri/10; -#ifdef RENEWAL - WBUFW(buf,43) = status->def + status->def2; - WBUFW(buf,45) = status->mdef + status->mdef2; -#else - WBUFW(buf,43) = status->def + status->vit; - WBUFW(buf,45) = status->mdef; + p.flags = ( !battle_config.hom_rename && hd->homunculus.rename_flag ? 0x1 : 0x0 ) | ( hd->homunculus.vaporize == HOM_ST_REST ? 0x2 : 0 ) | ( hd->homunculus.hp > 0 ? 0x4 : 0 ); + p.level = hd->homunculus.level; + p.hunger = hd->homunculus.hunger; + p.intimacy = hd->homunculus.intimacy / 100; +#if !(PACKETVER_MAIN_NUM >= 20190619 || PACKETVER_RE_NUM >= 20190605 || PACKETVER_ZERO_NUM >= 20190626) + p.itemId = 0; // equip id #endif - WBUFW(buf,47) = status->flee; - WBUFW(buf,49) = (flag) ? 0 : status->amotion; + p.atk2 = cap_value( status->rhw.atk2 + status->batk, 0, INT16_MAX ); + p.matk = i16min( status->matk_max, INT16_MAX ); //FIXME capping to INT16 here is too late + p.hit = status->hit; + if( battle_config.hom_setting&HOMSET_DISPLAY_LUK ){ + p.crit = status->luk / 3 + 1; //crit is a +1 decimal value! Just display purpose.[Vicious] + }else{ + p.crit = status->cri / 10; + } +#ifdef RENEWAL + p.def = status->def + status->def2; + p.mdef = status->mdef + status->mdef2; +#else + p.def = status->def + status->vit; + p.mdef = status->mdef; +#endif + p.flee = status->flee; + p.amotion = (flag) ? 0 : status->amotion; #if PACKETVER >= 20141016 // Homunculus HP bar will screw up if the percentage calculation exceeds signed values // Tested maximum: 21474836(=INT32_MAX/100), any value above will screw up the HP bar - if (status->max_hp > (INT32_MAX/100)) { - WBUFL(buf,51) = status->hp/(status->max_hp/100); - WBUFL(buf,55) = 100; - } else { - WBUFL(buf,51) = status->hp; - WBUFL(buf,55) = status->max_hp; + if( status->max_hp > ( INT32_MAX / 100 ) ){ + p.hp = status->hp / ( status->max_hp / 100 ); + p.maxHp = 100; + }else{ + p.hp = status->hp; + p.maxHp = status->max_hp; } - offset = 4; #else - if (status->max_hp > INT16_MAX) { - WBUFW(buf,51) = status->hp/(status->max_hp/100); - WBUFW(buf,53) = 100; - } else { - WBUFW(buf,51) = status->hp; - WBUFW(buf,53) = status->max_hp; + if( status->max_hp > INT16_MAX ){ + p.hp = status->hp / ( status->max_hp / 100 ); + p.maxHp = 100; + }else{ + p.hp = status->hp; + p.maxHp = status->max_hp; } - offset = 0; #endif - if (status->max_sp > INT16_MAX) { - WBUFW(buf,55+offset) = status->sp/(status->max_sp/100); - WBUFW(buf,57+offset) = 100; - } else { - WBUFW(buf,55+offset) = status->sp; - WBUFW(buf,57+offset) = status->max_sp; + if( status->max_sp > INT16_MAX ){ + p.sp = status->sp / ( status->max_sp / 100 ); + p.maxSp = 100; + }else{ + p.sp = status->sp; + p.maxSp = status->max_sp; } - WBUFL(buf,59+offset) = hd->homunculus.exp; - WBUFL(buf,63+offset) = hd->exp_next; - switch( htype ) { + p.exp = hd->homunculus.exp; + p.expNext = hd->exp_next; + switch( hom_class2type( hd->homunculus.class_ ) ){ case HT_REG: case HT_EVO: - if( hd->homunculus.level >= battle_config.hom_max_level ) - WBUFL(buf,63+offset) = 0; + if( hd->homunculus.level >= battle_config.hom_max_level ){ + p.expNext = 0; + } break; case HT_S: - if( hd->homunculus.level >= battle_config.hom_S_max_level ) - WBUFL(buf,63+offset) = 0; + if( hd->homunculus.level >= battle_config.hom_S_max_level ){ + p.expNext = 0; + } break; } - WBUFW(buf,67+offset) = hd->homunculus.skillpts; - WBUFW(buf,69+offset) = status_get_range(&hd->bl); - clif_send(buf,packet_len(cmd),&sd->bl,SELF); + p.skillPoints = hd->homunculus.skillpts; + p.range = status_get_range( &hd->bl ); + + clif_send( &p, sizeof( p ), &sd->bl, SELF ); +#endif } @@ -1728,16 +1804,21 @@ void clif_homskillup(struct map_session_data *sd, uint16 skill_id) WFIFOSET(fd,packet_len(0x239)); } -int clif_hom_food(struct map_session_data *sd,int foodid,int fail) //[orn] -{ - int fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x22f)); - WFIFOW(fd,0)=0x22f; - WFIFOB(fd,2)=fail; - WFIFOW(fd,3)=foodid; - WFIFOSET(fd,packet_len(0x22f)); +/// Result of request to feed a homun/merc. +/// 022f .B .W (ZC_FEED_MER) +/// result: +/// 0 = failure +/// 1 = success +void clif_hom_food( struct map_session_data *sd, int foodid, int fail ){ + nullpo_retv( sd ); - return 0; + struct PACKET_ZC_FEED_MER p; + + p.packetType = 0x22f; + p.result = fail; + p.itemId = client_nameid( foodid ); + + clif_send( &p, sizeof( p ), &sd->bl, SELF ); } @@ -1755,19 +1836,13 @@ void clif_walkok(struct map_session_data *sd) } -static void clif_move2(struct block_list *bl, struct view_data *vd, struct unit_data *ud) -{ - uint8 buf[128]; - int len; +static void clif_move2( struct block_list *bl, struct view_data *vd, struct unit_data *ud ){ struct status_change *sc = NULL; if ((sc = status_get_sc(bl)) && sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE|OPTION_CHASEWALK)) clif_ally_only = true; - len = clif_set_unit_walking(bl,ud,buf); - clif_send(buf,len,bl,AREA_WOS); - if (disguised(bl)) - clif_setdisguise(bl, buf, len); + clif_set_unit_walking( bl, nullptr, ud, AREA_WOS ); if(vd->cloth_color) clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS); @@ -1982,37 +2057,36 @@ void clif_npcbuysell(struct map_session_data* sd, int id) } -/// Presents list of items, that can be bought in an NPC shop (ZC_PC_PURCHASE_ITEMLIST). -/// 00c6 .W { .L .L .B .W }* -void clif_buylist(struct map_session_data *sd, struct npc_data *nd) -{ - int fd,i,c; - bool discount; +/// Presents list of items, that can be bought in an NPC shop. +/// 00c6 .W { .L .L .B .W }* (ZC_PC_PURCHASE_ITEMLIST) +void clif_buylist( struct map_session_data *sd, struct npc_data *nd ){ + nullpo_retv( sd ); + nullpo_retv( nd ); - nullpo_retv(sd); - nullpo_retv(nd); + int fd = sd->fd; - fd = sd->fd; - WFIFOHEAD(fd, 4 + nd->u.shop.count * 11); - WFIFOW(fd,0) = 0xc6; - - c = 0; - discount = npc_shop_discount(nd); - for( i = 0; i < nd->u.shop.count; i++ ) - { - struct item_data* id = itemdb_exists(nd->u.shop.shop_item[i].nameid); - int val = nd->u.shop.shop_item[i].value; - if( id == NULL ) - continue; - WFIFOL(fd, 4+c*11) = val; - WFIFOL(fd, 8+c*11) = (discount) ? pc_modifybuyvalue(sd,val) : val; - WFIFOB(fd,12+c*11) = itemtype(id->nameid); - WFIFOW(fd,13+c*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid; - c++; + if( !session_isActive( fd ) ){ + return; } - WFIFOW(fd,2) = 4 + c*11; - WFIFOSET(fd,WFIFOW(fd,2)); + uint16 len = sizeof( struct PACKET_ZC_PC_PURCHASE_ITEMLIST ) + nd->u.shop.count * sizeof( struct PACKET_ZC_PC_PURCHASE_ITEMLIST_sub ); + WFIFOHEAD( fd, len ); + struct PACKET_ZC_PC_PURCHASE_ITEMLIST *p = (struct PACKET_ZC_PC_PURCHASE_ITEMLIST *)WFIFOP( fd, 0 ); + p->packetType = 0xc6; + + int count = 0; + for( int i = 0, discount = npc_shop_discount( nd ); i < nd->u.shop.count; i++ ){ + int val = nd->u.shop.shop_item[i].value; + + p->items[count].price = val; + p->items[count].discountPrice = ( discount ) ? pc_modifybuyvalue( sd, val ) : val; + p->items[count].itemType = itemtype( nd->u.shop.shop_item[i].nameid ); + p->items[count].itemId = client_nameid( nd->u.shop.shop_item[i].nameid ); + count++; + } + + p->packetLength = sizeof( struct PACKET_ZC_PC_PURCHASE_ITEMLIST ) + count * sizeof( struct PACKET_ZC_PC_PURCHASE_ITEMLIST_sub ); + WFIFOSET( fd, p->packetLength ); } @@ -2067,39 +2141,44 @@ void clif_parse_NPCShopClosed(int fd, struct map_session_data *sd) { **/ void clif_npc_market_open(struct map_session_data *sd, struct npc_data *nd) { #if PACKETVER >= 20131223 - struct npc_item_list *shop = nd->u.shop.shop_item; - unsigned short shop_size = nd->u.shop.count, i, c, cmd = 0x9d5; - struct item_data *id = NULL; - struct s_packet_db *info; - int fd; + nullpo_retv( sd ); + nullpo_retv( nd ); - nullpo_retv(sd); - - if (sd->state.trading) + if( sd->state.trading ){ return; - - info = &packet_db[cmd]; - if (!info || info->len == 0) - return; - - fd = sd->fd; - - WFIFOHEAD(fd, 4 + shop_size * 13); - WFIFOW(fd,0) = cmd; - - for (i = 0, c = 0; i < shop_size; i++) { - if (shop[i].nameid && (id = itemdb_exists(shop[i].nameid))) { - WFIFOW(fd, 4+c*13) = shop[i].nameid; - WFIFOB(fd, 6+c*13) = itemtype(id->nameid); - WFIFOL(fd, 7+c*13) = shop[i].value; - WFIFOL(fd,11+c*13) = shop[i].qty; - WFIFOW(fd,15+c*13) = id->weight; - c++; - } } - WFIFOW(fd,2) = 4 + c*13; - WFIFOSET(fd,WFIFOW(fd,2)); + int fd = sd->fd; + + WFIFOHEAD( fd, sizeof( struct PACKET_ZC_NPC_MARKET_OPEN ) + nd->u.shop.count * sizeof( struct PACKET_ZC_NPC_MARKET_OPEN_sub ) ); + struct PACKET_ZC_NPC_MARKET_OPEN *p = (struct PACKET_ZC_NPC_MARKET_OPEN *)WFIFOP( fd, 0 ); + p->packetType = HEADER_ZC_NPC_MARKET_OPEN; + + int count = 0; + for( int i = 0; i < nd->u.shop.count; i++ ){ + struct npc_item_list *item = &nd->u.shop.shop_item[i]; + + if( !item->nameid ){ + continue; + } + + struct item_data *id = itemdb_exists( item->nameid ); + + if( !id ){ + continue; + } + + p->list[count].nameid = client_nameid( item->nameid ); + p->list[count].type = itemtype( item->nameid ); + p->list[count].price = item->value; + p->list[count].qty = item->qty; + p->list[count].weight = id->weight; + count++; + } + + p->packetLength = sizeof( struct PACKET_ZC_NPC_MARKET_OPEN ) + count * sizeof( struct PACKET_ZC_NPC_MARKET_OPEN_sub ); + WFIFOSET( fd, p->packetLength ); + sd->state.trading = 1; #endif } @@ -2116,42 +2195,45 @@ void clif_parse_NPCMarketClosed(int fd, struct map_session_data *sd) { /// Purchase item from Market shop. void clif_npc_market_purchase_ack(struct map_session_data *sd, uint8 res, uint8 n, struct s_npc_buy_list *list) { #if PACKETVER >= 20131223 - unsigned short cmd = 0x9d7, len = 0; - struct npc_data* nd; - uint8 result = (res == 0 ? 1 : 0); - int fd = 0; - struct s_packet_db *info; + nullpo_retv( sd ); + nullpo_retv( list ); - nullpo_retv(sd); - nullpo_retv((nd = map_id2nd(sd->npc_shopid))); + struct npc_data *nd = map_id2nd( sd->npc_shopid ); - info = &packet_db[cmd]; - if (!info || info->len == 0) - return; + nullpo_retv( nd ); - fd = sd->fd; - len = 5 + 8*n; + int fd = sd->fd; - WFIFOHEAD(fd, len); - WFIFOW(fd, 0) = cmd; - WFIFOW(fd, 2) = len; + WFIFOHEAD( fd, sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT ) + n * sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub ) ); + struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT *p = (struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT *)WFIFOP( fd, 0 ); + p->PacketType = HEADER_ZC_NPC_MARKET_PURCHASE_RESULT; - if (result) { - uint8 i, j; - struct npc_item_list *shop = nd->u.shop.shop_item; - unsigned short count = nd->u.shop.count; +#if PACKETVER_MAIN_NUM >= 20190807 || PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190814 + p->result = ( res == 0 ? 0 : -1 ); +#else + p->result = ( res == 0 ? 1 : 0 ); +#endif - for (i = 0; i < n; i++) { - WFIFOW(fd, 5+i*8) = list[i].nameid; - WFIFOW(fd, 7+i*8) = list[i].qty; + int count = 0; - ARR_FIND(0, count, j, list[i].nameid == shop[j].nameid); - WFIFOL(fd, 9+i*8) = (j != count) ? shop[j].value : 0; + if( p->result ){ + for( int i = 0, j; i < n; i++ ){ + ARR_FIND( 0, nd->u.shop.count, j, list[i].nameid == nd->u.shop.shop_item[j].nameid ); + + // Not found + if( j == nd->u.shop.count ){ + continue; + } + + p->list[count].ITID = client_nameid( list[i].nameid ); + p->list[count].qty = list[i].qty; + p->list[count].price = nd->u.shop.shop_item[j].value; + count++; } } - WFIFOB(fd, 4) = n; - WFIFOSET(fd, len); + p->PacketLength = sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT ) + count * sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub ); + WFIFOSET( fd, p->PacketLength ); #endif } @@ -2159,31 +2241,30 @@ void clif_npc_market_purchase_ack(struct map_session_data *sd, uint8 res, uint8 /// Purchase item from Market shop. void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd) { #if PACKETVER >= 20131223 - struct s_packet_db* info; - struct s_npc_buy_list *item_list; - uint16 len = 0, i = 0; - uint8 res = 0, n = 0; + nullpo_retv( sd ); - nullpo_retv(sd); - - if (!sd->npc_shopid) + if( !sd->npc_shopid ){ return; - - info = &packet_db[RFIFOW(fd,0)]; - if (!info || info->len == 0) - return; - len = RFIFOW(fd,info->pos[0]); - n = (len-4) / 6; - - CREATE(item_list, struct s_npc_buy_list, n); - for (i = 0; i < n; i++) { - item_list[i].nameid = RFIFOW(fd,info->pos[1]+i*6); - item_list[i].qty = (uint16)min(RFIFOL(fd,info->pos[2]+i*6),USHRT_MAX); } - res = npc_buylist(sd, n, item_list); - clif_npc_market_purchase_ack(sd, res, n, item_list); - aFree(item_list); + const struct packet_npc_market_purchase *p = (struct packet_npc_market_purchase *)RFIFOP( fd, 0 ); + + int count = ( p->PacketLength - sizeof( struct packet_npc_market_purchase ) ) / sizeof( struct packet_npc_market_purchase_sub ); + + struct s_npc_buy_list *list; + + CREATE( list, struct s_npc_buy_list, count ); + + // Sadly order is reverse + for( int i = 0; i < count; i++ ){ + list[i].nameid = p->list[i].ITID; + list[i].qty = p->list[i].qty; + } + + uint8 res = npc_buylist( sd, count, list ); + clif_npc_market_purchase_ack( sd, res, count, list ); + + aFree( list ); #endif } @@ -2473,76 +2554,98 @@ void clif_cutin(struct map_session_data* sd, const char* image, int type) /*========================================== * Fills in card data from the given item and into the buffer. [Skotlex] *------------------------------------------*/ -static void clif_addcards(unsigned char* buf, struct item* item) -{ - int i=0,j; - if( item == NULL ) { //Blank data - WBUFW(buf,0) = 0; - WBUFW(buf,2) = 0; - WBUFW(buf,4) = 0; - WBUFW(buf,6) = 0; +static void clif_addcards( struct EQUIPSLOTINFO* buf, struct item* item ){ + nullpo_retv( buf ); + + // Blank data + if( item == nullptr ){ + buf->card[0] = 0; + buf->card[1] = 0; + buf->card[2] = 0; + buf->card[3] = 0; return; } - if( item->card[0] == CARD0_PET ) { //pet eggs - WBUFW(buf,0) = 0; - WBUFW(buf,2) = 0; - WBUFW(buf,4) = 0; - WBUFW(buf,6) = item->card[3]; //Pet renamed flag. + + // Pet eggs + if( item->card[0] == CARD0_PET ){ + buf->card[0] = 0; + buf->card[1] = 0; + buf->card[2] = 0; + buf->card[3] = item->card[3]; //Pet renamed flag. return; } - if( item->card[0] == CARD0_FORGE || item->card[0] == CARD0_CREATE ) { //Forged/created items - WBUFW(buf,0) = item->card[0]; - WBUFW(buf,2) = item->card[1]; - WBUFW(buf,4) = item->card[2]; - WBUFW(buf,6) = item->card[3]; + + // Forged/created items + if( item->card[0] == CARD0_FORGE || item->card[0] == CARD0_CREATE ){ + buf->card[0] = item->card[0]; + buf->card[1] = item->card[1]; + buf->card[2] = item->card[2]; + buf->card[3] = item->card[3]; return; } - //Client only receives four cards.. so randomly send them a set of cards. [Skotlex] - if( MAX_SLOTS > 4 && (j = itemdb_slot(item->nameid)) > 4 ) - i = rnd()%(j-3); //eg: 6 slots, possible i values: 0->3, 1->4, 2->5 => i = rnd()%3; - //Normal items. - if( item->card[i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,0) = j; - else - WBUFW(buf,0) = item->card[i]; + int i = 0, j; - if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,2) = j; - else - WBUFW(buf,2) = item->card[i]; + // Client only receives four cards.. so randomly send them a set of cards. [Skotlex] + if( MAX_SLOTS > 4 && ( j = itemdb_slot( item->nameid ) ) > 4 ){ + i = rnd() % ( j - 3 ); //eg: 6 slots, possible i values: 0->3, 1->4, 2->5 => i = rnd()%3; + } - if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,4) = j; - else - WBUFW(buf,4) = item->card[i]; + // Normal items. + if( item->card[i] > 0 && ( j = itemdb_viewid( item->card[i] ) ) > 0 ){ + buf->card[0] = j; + }else{ + buf->card[0] = item->card[i]; + } - if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,6) = j; - else - WBUFW(buf,6) = item->card[i]; + if( item->card[++i] > 0 && ( j = itemdb_viewid( item->card[i] ) ) > 0 ){ + buf->card[1] = j; + }else{ + buf->card[1] = item->card[i]; + } + + if( item->card[++i] > 0 && ( j = itemdb_viewid( item->card[i] ) ) > 0 ){ + buf->card[2] = j; + }else{ + buf->card[2] = item->card[i]; + } + + if( item->card[++i] > 0 && ( j = itemdb_viewid( item->card[i] ) ) > 0 ){ + buf->card[3] = j; + }else{ + buf->card[3] = item->card[i]; + } } /// Fills in part of the item buffers that calls for variable bonuses data. [Napster] /// A maximum of 5 random options can be supported. -void clif_add_random_options(unsigned char* buf, struct item *it) { -#if PACKETVER >= 20150225 - int i; +static uint8 clif_add_random_options( struct ItemOptions buf[MAX_ITEM_RDM_OPT], struct item* it ){ + nullpo_retr( 0, it ); - for (i = 0; i < MAX_ITEM_RDM_OPT; i++) { - WBUFW(buf, i*5 + 0) = it->option[i].id; // OptIndex - WBUFW(buf, i*5 + 2) = it->option[i].value; // Value - WBUFB(buf, i*5 + 4) = it->option[i].param; // Param1 + uint8 count = 0; + + for( int i = 0; i < MAX_ITEM_RDM_OPT; i++ ){ + if( it->option[i].id ){ + buf[i].index = it->option[i].id; // OptIndex + buf[i].value = it->option[i].value; // Value + buf[i].param = it->option[i].param; // Param1 + count++; + }else{ + buf[i].index = 0; + buf[i].value = 0; + buf[i].param = 0; + } } #if MAX_ITEM_RDM_OPT < 5 - for ( ; i < 5; i++) { - WBUFW(buf, i*5 + 0) = 0; // OptIndex - WBUFW(buf, i*5 + 2) = 0; // Value - WBUFB(buf, i*5 + 4) = 0; // Param1 + for( ; i < 5; i++ ){ + buf[i].index = 0; // OptIndex + buf[i].value = 0; // Value + buf[i].param = 0; // Param1 } #endif -#endif + + return count; } /// Notifies the client, about a received inventory item or the result of a pick-up request. @@ -2552,105 +2655,55 @@ void clif_add_random_options(unsigned char* buf, struct item *it) { /// 0990 .W .W .W .B .B .B .W .W .W .W .L .B .B .L .W (ZC_ITEM_PICKUP_ACK_V5) /// 0a0c .W .W .W .B .B .B .W .W .W .W .L .B .B .L .W {