diff options
Diffstat (limited to 'src')
36 files changed, 6969 insertions, 582 deletions
diff --git a/src/common/mmo.h b/src/common/mmo.h index 239e1dddd..f8ef63390 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -44,8 +44,8 @@ // 20110928 - 2011-09-28aRagexeRE+ - 0x6b, 0x6d #ifndef PACKETVER - #define PACKETVER 20081126 - //#define PACKETVER 20100707 + #define PACKETVER 20110609 + //#define PACKETVER 20100730 #endif // backward compatible PACKETVER 8 and 9 #if PACKETVER == 8 @@ -176,6 +176,7 @@ enum item_types { IT_UNKNOWN2,//9 IT_AMMO, //10 IT_DELAYCONSUME,//11 + IT_THROWWEAPON= 17,//17 IT_CASH = 18, IT_MAX }; @@ -646,6 +647,56 @@ enum { JOB_STAR_GLADIATOR, JOB_STAR_GLADIATOR2, JOB_SOUL_LINKER, + /** + * 3.1 (2.1) + **/ + JOB_RUNE_KNIGHT = 4054, + JOB_WARLOCK, + JOB_RANGER, + JOB_ARCH_BISHOP, + JOB_MECHANIC, + JOB_GUILLOTINE_CROSS, + /** + * 3.1 (2.1 Trans) + **/ + JOB_RUNE_KNIGHT_T, + JOB_WARLOCK_T, + JOB_RANGER_T, + JOB_ARCH_BISHOP_T, + JOB_MECHANIC_T, + JOB_GUILLOTINE_CROSS_T, + /** + * 3.2 (2.2) + **/ + JOB_ROYAL_GUARD, + JOB_SORCERER, + JOB_MINSTREL, + JOB_WANDERER, + JOB_SURA, + JOB_GENETIC, + JOB_SHADOW_CHASER, + /** + * 3.2 (2.2 Trans) + **/ + JOB_ROYAL_GUARD_T, + JOB_SORCERER_T, + JOB_MINSTREL_T, + JOB_WANDERER_T, + JOB_SURA_T, + JOB_GENETIC_T, + JOB_SHADOW_CHASER_T, + /** + * 3.x Mounts / Vehicles + **/ + JOB_RUNE_KNIGHT2, + JOB_RUNE_KNIGHT_T2, + JOB_ROYAL_GUARD2, + JOB_ROYAL_GUARD_T2, + JOB_RANGER2, + JOB_RANGER_T2, + JOB_MECHANIC2, + JOB_MECHANIC_T2, + JOB_MAX, }; diff --git a/src/common/showmsg.c b/src/common/showmsg.c index fc1badd26..a69c73ba2 100644 --- a/src/common/showmsg.c +++ b/src/common/showmsg.c @@ -4,6 +4,8 @@ #include "../common/cbasetypes.h" #include "../common/strlib.h" // StringBuf #include "showmsg.h" +#include "core.h" //[Ind] - For SERVER_TYPE +#include "version.h" //[Ind] - For SERVER_TYPE values #include <stdio.h> #include <string.h> @@ -51,6 +53,8 @@ int stdout_with_ansisequence = 0; int msg_silent = 0; //Specifies how silent the console is. +int console_msg_log = 0;//[Ind] msg error logging + /////////////////////////////////////////////////////////////////////////////// /// static/dynamic buffer for the messages @@ -684,6 +688,29 @@ int _vShowMessage(enum msg_type flag, const char *string, va_list ap) return 1; } if( + ( flag == MSG_WARNING && console_msg_log&1 ) || + ( ( flag == MSG_ERROR || flag == MSG_SQL ) && console_msg_log&2 ) || + ( flag == MSG_DEBUG && console_msg_log&4 ) ) {//[Ind] + FILE *log = NULL; + if( (log = fopen(SERVER_TYPE == ATHENA_SERVER_MAP ? "./log/map-msg_log.log" : "./log/unknown.log","a+")) ) { + char timestring[255]; + time_t curtime; + time(&curtime); + strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); + fprintf(log,"(%s) [ %s ] : ", + timestring, + flag == MSG_WARNING ? "Warning" : + flag == MSG_ERROR ? "Error" : + flag == MSG_SQL ? "SQL Error" : + flag == MSG_DEBUG ? "Debug" : + "Unknown"); + va_copy(apcopy, ap); + vfprintf(log,string,apcopy); + va_end(apcopy); + fclose(log); + } + } + if( (flag == MSG_INFORMATION && msg_silent&1) || (flag == MSG_STATUS && msg_silent&2) || (flag == MSG_NOTICE && msg_silent&4) || diff --git a/src/common/showmsg.h b/src/common/showmsg.h index 998296b66..5f80a4312 100644 --- a/src/common/showmsg.h +++ b/src/common/showmsg.h @@ -67,6 +67,7 @@ extern int stdout_with_ansisequence; //If the color ansi sequences are to be used. [flaviojs] extern int msg_silent; //Specifies how silent the console is. [Skotlex] +extern int console_msg_log; //Specifies what error messages to log. [Ind] extern char timestamp_format[20]; //For displaying Timestamps [Skotlex] enum msg_type { diff --git a/src/map/Makefile.in b/src/map/Makefile.in index 09e92dde2..60fc49f7c 100644 --- a/src/map/Makefile.in +++ b/src/map/Makefile.in @@ -34,7 +34,9 @@ MAP_H = map.h chrif.h clif.h pc.h status.h npc.h \ storage.h skill.h atcommand.h battle.h battleground.h \ intif.h trade.h party.h vending.h guild.h pet.h \ log.h mail.h date.h unit.h homunculus.h mercenary.h quest.h instance.h mapreg.h \ - buyingstore.h searchstore.h duel.h + buyingstore.h searchstore.h duel.h \ + RRConfig/Core.h RRConfig/Renewal.h RRConfig/Secure.h RRConfig/Data/Const.h \ + RRConfig/Skills/General.h RRConfig/Skills/Mage_Classes.h RRConfig/Skills/Swordsman_Classes.h HAVE_MYSQL=@HAVE_MYSQL@ ifeq ($(HAVE_MYSQL),yes) diff --git a/src/map/RRConfig/Core.h b/src/map/RRConfig/Core.h new file mode 100644 index 000000000..9cb161a3f --- /dev/null +++ b/src/map/RRConfig/Core.h @@ -0,0 +1,28 @@ +#ifndef _RRCONFIGS_
+#define _RRCONFIGS_
+/**
+ * Ragnarok Resources Configuration File (http://ro-resources.net)
+ * The following settings are applied upon compiling the program,
+ * therefore any settings you disable will not even be added to the program
+ * making these settings the most performance-effiecient possible
+ **/
+
+/**
+ * @INFO: RREmu Settings Core
+ * - For detailed guidance on these check http://trac.ro-resources.net/wiki/CoreConfiguration
+ **/
+
+/**
+ * No settings past this point
+ **/
+#include "./Renewal.h"
+#include "./Secure.h"
+#include "./Skills/General.h"
+/**
+ * Constants come last; so they process anything that could've been modified in early includes
+ **/
+#include "./Data/Const.h"
+/**
+ * End of File
+ **/
+#endif
diff --git a/src/map/RRConfig/Data/Const.h b/src/map/RRConfig/Data/Const.h new file mode 100644 index 000000000..6da8cdf13 --- /dev/null +++ b/src/map/RRConfig/Data/Const.h @@ -0,0 +1,33 @@ +#ifndef _RRCONFIGS_CONST_
+#define _RRCONFIGS_CONST_
+/**
+ * Ragnarok Resources Configuration File (http://ro-resources.net)
+ * The following settings are applied upon compiling the program,
+ * therefore any settings you disable will not even be added to the program
+ * making these settings the most performance-effiecient possible
+ **/
+
+/**
+ * @INFO: This file holds constants that aims at making code smoother and more efficient
+ */
+
+/**
+ * "Constants"
+ **/
+#define CONST_CASTRATE_SCALE ( RECASTING ? RECASTING_VMIN : battle_config.castrate_dex_scale )
+#define CONST_CASTRATE_CALC ( RECASTING ? ((status_get_dex(bl)*2)+status_get_int(bl)) : status_get_dex(bl) )
+
+/**
+ * "Sane Checks" to save you from compiling with cool bugs
+ **/
+#if SECURE_NPCTIMEOUT_INTERVAL <= 0
+ #error SECURE_NPCTIMEOUT_INTERVAL should be at least 1 (1s)
+#endif
+#if SECURE_NPCTIMEOUT < 0
+ #error SECURE_NPCTIMEOUT cannot be lower than 0
+#endif
+
+/**
+ * End of File
+ **/
+#endif
\ No newline at end of file diff --git a/src/map/RRConfig/Renewal.h b/src/map/RRConfig/Renewal.h new file mode 100644 index 000000000..db5a99008 --- /dev/null +++ b/src/map/RRConfig/Renewal.h @@ -0,0 +1,53 @@ +#ifndef _RRCONFIGS_RE_
+#define _RRCONFIGS_RE_
+/**
+ * Ragnarok Resources Configuration File (http://ro-resources.net)
+ * The following settings are applied upon compiling the program,
+ * therefore any settings you disable will not even be added to the program
+ * making these settings the most performance-effiecient possible
+ **/
+
+/**
+ * @INFO: This file holds general-purpose renewal settings, for class-specific ones check /src/map/RRConfig/Skills folder
+ **/
+
+/**
+ * Game Server Mode
+ * @values: 1 or 0
+ * 1 : renewal support, such as renewal-exclusive formulas
+ * -> Note some features may be enabled/disabled at this file despite this setting being ON
+ * 0 : renewal support disabled, use original formulas
+ **/
+#define RRMODE 1
+
+/**
+ * Renewal Cast Time
+ * @values: 1 (enabled) or 0 (disabled)
+ * 1 : Cast Time is decreased by DEX*2+INT, 20% of the cast time is not reduced by stats,
+ * - for example, on a skill whose cast time is 10s, only 8s may be reduced. other 2s are
+ * - part of a "fixed cast time" that is only reduced by special items and skills (such as
+ * - Arch Bishop's Sacrament skill).
+ * 0 : the old cast time method, influenced by dex, items and skills.
+ **/
+#define RECASTING 1
+
+/**
+ * Renewal Cast Time : Variable-Free
+ * - Value required for no variable cast time with stats.
+ * - Formula: (casterDex x 2) + (casterInt)
+ * Default: 530
+ **/
+#define RECASTING_VMIN 530
+
+/**
+ * Renewal Enchant Deadly Poison Change
+ * - In RE EDP no longer increases final damage by 400%.
+ * - it increases your weapon atk and your stat atk
+ * - it doesn't affect grimtooth
+ **/
+#define RE_EDP 1
+
+/**
+ * End of File
+ **/
+#endif
\ No newline at end of file diff --git a/src/map/RRConfig/Secure.h b/src/map/RRConfig/Secure.h new file mode 100644 index 000000000..1288b259f --- /dev/null +++ b/src/map/RRConfig/Secure.h @@ -0,0 +1,36 @@ +#ifndef _RRCONFIGS_SECURE_
+#define _RRCONFIGS_SECURE_
+/**
+ * Ragnarok Resources Configuration File (http://ro-resources.net)
+ * The following settings are applied upon compiling the program,
+ * therefore any settings you disable will not even be added to the program
+ * making these settings the most performance-effiecient possible
+ **/
+
+/**
+ * @INFO: This file holds optional security settings
+ **/
+
+/**
+ * Optional NPC Dialog Timer
+ * When enabled all npcs dialog will 'timeout' if user is on idle for longer than the amount of seconds allowed
+ * - On 'timeout' the npc dialog window changes it's next/menu to a 'close' button
+ * @values
+ * - ? : Desired idle time in seconds (e.g. 10)
+ * - 0 : Disabled
+ **/
+#define SECURE_NPCTIMEOUT 0
+
+/**
+ * (Secure) Optional NPC Dialog Timer
+ * @requirement : SECURE_NPCTIMEOUT must be enabled
+ * Minimum Interval Between timeout checks in seconds
+ * Default: 1s
+ **/
+#define SECURE_NPCTIMEOUT_INTERVAL 1
+
+
+/**
+ * End of File
+ **/
+#endif
\ No newline at end of file diff --git a/src/map/RRConfig/Skills/General.h b/src/map/RRConfig/Skills/General.h new file mode 100644 index 000000000..5944d6844 --- /dev/null +++ b/src/map/RRConfig/Skills/General.h @@ -0,0 +1,23 @@ +#ifndef _RRCONFIGS_SKILLS_GENERAL_
+#define _RRCONFIGS_SKILLS_GENERAL_
+/**
+ * Ragnarok Resources Configuration File (http://ro-resources.net)
+ * The following settings are applied upon compiling the program,
+ * therefore any settings you disable will not even be added to the program
+ * making these settings the most performance-effiecient possible
+ **/
+
+/**
+ * Default Magical Reflection Behavior
+ * - When reflecting, reflected damage depends on gears caster is wearing, not target
+ * - When disabled damage depends on gears target is wearing, not caster.
+ * @values 1 (enabled) or 0 (disabled)
+ **/
+#define RR_MAGIC_REFLECTION 1
+
+/**
+ * No settings past this point
+ **/
+#include "Mage_Classes.h"
+#include "Swordsman_Classes.h"
+#endif
diff --git a/src/map/RRConfig/Skills/Mage_Classes.h b/src/map/RRConfig/Skills/Mage_Classes.h new file mode 100644 index 000000000..cf0777787 --- /dev/null +++ b/src/map/RRConfig/Skills/Mage_Classes.h @@ -0,0 +1,19 @@ +#ifndef _RRCONFIGS_SKILLS_MAGE_
+#define _RRCONFIGS_SKILLS_MAGE_
+/**
+ * Ragnarok Resources Configuration File (http://ro-resources.net)
+ * The following settings are applied upon compiling the program,
+ * therefore any settings you disable will not even be added to the program
+ * making these settings the most performance-effiecient possible
+ **/
+
+/**
+ * (Wizard/HW/Warlock) enable Fire Ivy skill? (1 OR 0)
+ * Default: 0 (disabled)
+ **/
+#define FIREIVY_ON 0
+
+/**
+ * No settings past this point
+ **/
+#endif
diff --git a/src/map/RRConfig/Skills/Swordsman_Classes.h b/src/map/RRConfig/Skills/Swordsman_Classes.h new file mode 100644 index 000000000..4dddeec54 --- /dev/null +++ b/src/map/RRConfig/Skills/Swordsman_Classes.h @@ -0,0 +1,19 @@ +#ifndef _RRCONFIGS_SKILLS_SWORDS_
+#define _RRCONFIGS_SKILLS_SWORDS_
+/**
+ * Ragnarok Resources Configuration File (http://ro-resources.net)
+ * The following settings are applied upon compiling the program,
+ * therefore any settings you disable will not even be added to the program
+ * making these settings the most performance-effiecient possible
+ **/
+
+/**
+ * (Rune Knight) the maximum rune items a character may have of the same type
+ * Default: 20
+ **/
+#define MAX_RUNE 20
+
+/**
+ * No settings past this point
+ **/
+#endif
diff --git a/src/map/atcommand.c b/src/map/atcommand.c index e98481f22..e87074883 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -1255,7 +1255,7 @@ ACMD_FUNC(jobchange) if (!message || !*message || sscanf(message, "%d %d", &job, &upper) < 1) { int i, found = 0; - const struct { char name[16]; int id; } jobs[] = { + const struct { char name[24]; int id; } jobs[] = { { "novice", 0 }, { "swordsman", 1 }, { "mage", 2 }, @@ -1330,6 +1330,32 @@ ACMD_FUNC(jobchange) { "taekwon girl", 4046 }, { "star gladiator", 4047 }, { "soul linker", 4049 }, + { "rune knight", 4054 }, + { "warlock", 4055 }, + { "ranger", 4056 }, + { "arch bishop", 4057 }, + { "mechanic", 4058 }, + { "guillotine", 4059 }, + { "rune knight (Trans)", 4060 }, + { "warlock (Trans)", 4061 }, + { "ranger (Trans)", 4062 }, + { "arch bishop (Trans)", 4063 }, + { "mechanic (Trans)", 4064 }, + { "guillotine (Trans)", 4065 }, + { "royal guard", 4066 }, + { "sorcerer", 4067 }, + { "minstrel", 4068 }, + { "wanderer", 4069 }, + { "sura", 4070 }, + { "genetic", 4071 }, + { "shadow chaser", 4072 }, + { "royal guard (Trans)", 4073 }, + { "sorcerer (Trans)", 4074 }, + { "minstrel (Trans)", 4075 }, + { "wanderer (Trans)", 4076 }, + { "sura (Trans)", 4077 }, + { "genetic (Trans)", 4078 }, + { "shadow chaser (Trans)", 4079 }, }; for (i=0; i < ARRAYLENGTH(jobs); i++) { @@ -1343,28 +1369,56 @@ ACMD_FUNC(jobchange) if (!found) { clif_displaymessage(fd, "Please, enter job ID (usage: @job/@jobchange <job name/ID>)."); - clif_displaymessage(fd, " 0 Novice 7 Knight 14 Crusader 21 N/A"); - clif_displaymessage(fd, " 1 Swordman 8 Priest 15 Monk 22 N/A"); - clif_displaymessage(fd, " 2 Mage 9 Wizard 16 Sage 23 Super Novice"); - clif_displaymessage(fd, " 3 Archer 10 Blacksmith 17 Rogue 24 Gunslinger"); - clif_displaymessage(fd, " 4 Acolyte 11 Hunter 18 Alchemist 25 Ninja"); - clif_displaymessage(fd, " 5 Merchant 12 Assassin 19 Bard 26 N/A"); - clif_displaymessage(fd, " 6 Thief 13 N/A 20 Dancer 27 N/A"); - clif_displaymessage(fd, "4001 Novice High 4008 Lord Knight 4015 Paladin 4022 N/A"); - clif_displaymessage(fd, "4002 Swordman High 4009 High Priest 4016 Champion"); - clif_displaymessage(fd, "4003 Mage High 4010 High Wizard 4017 Professor"); - clif_displaymessage(fd, "4004 Archer High 4011 Whitesmith 4018 Stalker"); - clif_displaymessage(fd, "4005 Acolyte High 4012 Sniper 4019 Creator"); - clif_displaymessage(fd, "4006 Merchant High 4013 Assassin Cross 4020 Clown"); - clif_displaymessage(fd, "4007 Thief High 4014 N/A 4021 Gypsy"); - clif_displaymessage(fd, "4023 Baby Novice 4030 Baby Knight 4037 Baby Crusader 4044 N/A"); - clif_displaymessage(fd, "4024 Baby Swordsman 4031 Baby Priest 4038 Baby Monk 4045 Super Baby"); - clif_displaymessage(fd, "4025 Baby Mage 4032 Baby Wizard 4039 Baby Sage 4046 Taekwon Kid"); - clif_displaymessage(fd, "4026 Baby Archer 4033 Baby Blacksmith 4040 Baby Rogue 4047 Taekwon Master"); - clif_displaymessage(fd, "4027 Baby Acolyte 4034 Baby Hunter 4041 Baby Alchemist 4048 N/A"); - clif_displaymessage(fd, "4028 Baby Merchant 4035 Baby Assassin 4042 Baby Bard 4049 Soul Linker"); - clif_displaymessage(fd, "4029 Baby Thief 4036 N/A 4043 Baby Dancer"); - clif_displaymessage(fd, "[upper]: -1 (default) to automatically determine the 'level', 0 to force normal job, 1 to force high job."); + clif_displaymessage(fd, "----- Novice / 1st Class -----"); + clif_displaymessage(fd, " 0 Novice 1 Swordman 2 Mage 3 Archer"); + clif_displaymessage(fd, " 4 Acolyte 5 Merchant 6 Thief"); + clif_displaymessage(fd, "----- 2nd Class -----"); + clif_displaymessage(fd, " 7 Knight 8 Priest 9 Wizard 10 Blacksmith"); + clif_displaymessage(fd, " 11 Hunter 12 Assassin 14 Crusader 15 Monk"); + clif_displaymessage(fd, " 16 Sage 17 Rogue 18 Alchemist 19 Bard"); + clif_displaymessage(fd, " 20 Dancer"); + clif_displaymessage(fd, "----- High Novice / High 1st Class -----"); + clif_displaymessage(fd, "4001 Novice High 4002 Swordman High 4003 Mage High 4004 Archer High"); + clif_displaymessage(fd, "4005 Acolyte High 4006 Merchant High 4007 Thief High"); + clif_displaymessage(fd, "----- Transcendent 2nd Class -----"); + clif_displaymessage(fd, "4008 Lord Knight 4009 High Priest 4010 High Wizard 4011 Whitesmith"); + clif_displaymessage(fd, "4012 Sniper 4013 Assassin Cross 4015 Paladin 4016 Champion"); + clif_displaymessage(fd, "4017 Professor 4018 Stalker 4019 Creator 4020 Clown"); + clif_displaymessage(fd, "4021 Gypsy"); + clif_displaymessage(fd, "----- 3rd Class (Regular to 3rd) -----"); + clif_displaymessage(fd, "4054 Rune Knight 4055 Warlock 4056 Ranger 4057 Arch Bishop"); + clif_displaymessage(fd, "4058 Mechanic 4059 Guillotine Cross 4066 Royal Guard 4067 Sorcerer"); + clif_displaymessage(fd, "4068 Minstrel 4069 Wanderer 4070 Sura 4071 Genetic"); + clif_displaymessage(fd, "4072 Shadow Chaser"); + clif_displaymessage(fd, "----- 3rd Class (Transcendent to 3rd) -----"); + clif_displaymessage(fd, "4060 Rune Knight 4061 Warlock 4062 Ranger 4063 Arch Bishop"); + clif_displaymessage(fd, "4064 Mechanic 4065 Guillotine Cross 4073 Royal Guard 4074 Sorcerer"); + clif_displaymessage(fd, "4075 Minstrel 4076 Wanderer 4077 Sura 4078 Genetic"); + clif_displaymessage(fd, "4079 Shadow Chaser"); + clif_displaymessage(fd, "----- Expanded Class -----"); + clif_displaymessage(fd, " 23 Super Novice 24 Gunslinger 25 Ninja 26 Xmas"); + clif_displaymessage(fd, " 27 Summer 4046 Taekwon 4047 Star Gladiator 4049 Soul Linker"); + //clif_displaymessage(fd, "4050 Gangsi 4051 Death Knight 4052 Dark Collector"); + clif_displaymessage(fd, "---- 1st And 2nd Baby Class ----"); + clif_displaymessage(fd, "4023 Baby Novice 4024 Baby Swordsman 4025 Baby Mage 4026 Baby Archer"); + clif_displaymessage(fd, "4027 Baby Acolyte 4028 Baby Merchant 4029 Baby Thief 4030 Baby Knight"); + clif_displaymessage(fd, "4031 Baby Priest 4032 Baby Wizard 4033 Baby Blacksmith 4034 Baby Hunter"); + clif_displaymessage(fd, "4035 Baby Assassin 4037 Baby Crusader 4038 Baby Monk 4039 Baby Sage"); + clif_displaymessage(fd, "4040 Baby Rogue 4041 Baby Alchemist 4042 Baby Bard 4043 Baby Dancer"); + clif_displaymessage(fd, "4045 Super Baby"); + //clif_displaymessage(fd, "---- 3rd Baby Class ----"); + //clif_displaymessage(fd, "4096 Baby Rune Knight 4097 Baby Warlock 4098 Baby Ranger"); + //clif_displaymessage(fd, "4099 Baby Arch Bishop 4100 Baby Mechanic 4101 Baby Guillotine Cross"); + //clif_displaymessage(fd, "4102 Baby Royal Guard 4103 Baby Sorcerer 4104 Baby Minstrel"); + //clif_displaymessage(fd, "4105 Baby Wanderer 4106 Baby Sura 4107 Baby Genetic"); + //clif_displaymessage(fd, "4108 Baby Shadow Chaser"); + //clif_displaymessage(fd, "---- Mounts, Modes, And Others ----"); + //clif_displaymessage(fd, " 13 Knight (Peco) 21 Crusader (Peco) 22 Wedding 26 Christmas"); + //clif_displaymessage(fd, " 27 Summer 4014 Lord Knight (Peco) 4022 Paladin (Peco) 4036 Baby Knight (Peco)"); + //clif_displaymessage(fd, "4044 Baby Crusader (Peco) 4048 Star Gladiator (Union) 4080 Rune Knight (Dragon)"); + //clif_displaymessage(fd, "4081 Rune Knight Trans (Dragon) 4082 Royal Guard (Gryphon)"); + //clif_displaymessage(fd, "4083 Royal Guard Trans (Gryphon) 4084 Ranger (Warg) 4085 Ranger Trans (Warg)"); + //clif_displaymessage(fd, "4086 Mechanic (Mado) 4087 Mechanic Trans (Mado)"); return -1; } } @@ -1384,28 +1438,56 @@ ACMD_FUNC(jobchange) } } else { clif_displaymessage(fd, "Please, enter job ID (usage: @job/@jobchange <job name/ID>)."); - clif_displaymessage(fd, " 0 Novice 7 Knight 14 Crusader 21 N/A"); - clif_displaymessage(fd, " 1 Swordman 8 Priest 15 Monk 22 N/A"); - clif_displaymessage(fd, " 2 Mage 9 Wizard 16 Sage 23 Super Novice"); - clif_displaymessage(fd, " 3 Archer 10 Blacksmith 17 Rogue 24 Gunslinger"); - clif_displaymessage(fd, " 4 Acolyte 11 Hunter 18 Alchemist 25 Ninja"); - clif_displaymessage(fd, " 5 Merchant 12 Assassin 19 Bard 26 N/A"); - clif_displaymessage(fd, " 6 Thief 13 N/A 20 Dancer 27 N/A"); - clif_displaymessage(fd, "4001 Novice High 4008 Lord Knight 4015 Paladin 4022 N/A"); - clif_displaymessage(fd, "4002 Swordman High 4009 High Priest 4016 Champion"); - clif_displaymessage(fd, "4003 Mage High 4010 High Wizard 4017 Professor"); - clif_displaymessage(fd, "4004 Archer High 4011 Whitesmith 4018 Stalker"); - clif_displaymessage(fd, "4005 Acolyte High 4012 Sniper 4019 Creator"); - clif_displaymessage(fd, "4006 Merchant High 4013 Assassin Cross 4020 Clown"); - clif_displaymessage(fd, "4007 Thief High 4014 N/A 4021 Gypsy"); - clif_displaymessage(fd, "4023 Baby Novice 4030 Baby Knight 4037 Baby Crusader 4044 N/A"); - clif_displaymessage(fd, "4024 Baby Swordsman 4031 Baby Priest 4038 Baby Monk 4045 Super Baby"); - clif_displaymessage(fd, "4025 Baby Mage 4032 Baby Wizard 4039 Baby Sage 4046 Taekwon Kid"); - clif_displaymessage(fd, "4026 Baby Archer 4033 Baby Blacksmith 4040 Baby Rogue 4047 Taekwon Master"); - clif_displaymessage(fd, "4027 Baby Acolyte 4034 Baby Hunter 4041 Baby Alchemist 4048 N/A"); - clif_displaymessage(fd, "4028 Baby Merchant 4035 Baby Assassin 4042 Baby Bard 4049 Soul Linker"); - clif_displaymessage(fd, "4029 Baby Thief 4036 N/A 4043 Baby Dancer"); - clif_displaymessage(fd, "[upper]: -1 (default) to automatically determine the 'level', 0 to force normal job, 1 to force high job."); + clif_displaymessage(fd, "----- Novice / 1st Class -----"); + clif_displaymessage(fd, " 0 Novice 1 Swordman 2 Mage 3 Archer"); + clif_displaymessage(fd, " 4 Acolyte 5 Merchant 6 Thief"); + clif_displaymessage(fd, "----- 2nd Class -----"); + clif_displaymessage(fd, " 7 Knight 8 Priest 9 Wizard 10 Blacksmith"); + clif_displaymessage(fd, " 11 Hunter 12 Assassin 14 Crusader 15 Monk"); + clif_displaymessage(fd, " 16 Sage 17 Rogue 18 Alchemist 19 Bard"); + clif_displaymessage(fd, " 20 Dancer"); + clif_displaymessage(fd, "----- High Novice / High 1st Class -----"); + clif_displaymessage(fd, "4001 Novice High 4002 Swordman High 4003 Mage High 4004 Archer High"); + clif_displaymessage(fd, "4005 Acolyte High 4006 Merchant High 4007 Thief High"); + clif_displaymessage(fd, "----- Transcendent 2nd Class -----"); + clif_displaymessage(fd, "4008 Lord Knight 4009 High Priest 4010 High Wizard 4011 Whitesmith"); + clif_displaymessage(fd, "4012 Sniper 4013 Assassin Cross 4015 Paladin 4016 Champion"); + clif_displaymessage(fd, "4017 Professor 4018 Stalker 4019 Creator 4020 Clown"); + clif_displaymessage(fd, "4021 Gypsy"); + clif_displaymessage(fd, "----- 3rd Class (Regular to 3rd) -----"); + clif_displaymessage(fd, "4054 Rune Knight 4055 Warlock 4056 Ranger 4057 Arch Bishop"); + clif_displaymessage(fd, "4058 Mechanic 4059 Guillotine Cross 4066 Royal Guard 4067 Sorcerer"); + clif_displaymessage(fd, "4068 Minstrel 4069 Wanderer 4070 Sura 4071 Genetic"); + clif_displaymessage(fd, "4072 Shadow Chaser"); + clif_displaymessage(fd, "----- 3rd Class (Transcendent to 3rd) -----"); + clif_displaymessage(fd, "4060 Rune Knight 4061 Warlock 4062 Ranger 4063 Arch Bishop"); + clif_displaymessage(fd, "4064 Mechanic 4065 Guillotine Cross 4073 Royal Guard 4074 Sorcerer"); + clif_displaymessage(fd, "4075 Minstrel 4076 Wanderer 4077 Sura 4078 Genetic"); + clif_displaymessage(fd, "4079 Shadow Chaser"); + clif_displaymessage(fd, "----- Expanded Class -----"); + clif_displaymessage(fd, " 23 Super Novice 24 Gunslinger 25 Ninja 26 Xmas"); + clif_displaymessage(fd, " 27 Summer 4046 Taekwon 4047 Star Gladiator 4049 Soul Linker"); + //clif_displaymessage(fd, "4050 Gangsi 4051 Death Knight 4052 Dark Collector"); + clif_displaymessage(fd, "---- 1st And 2nd Baby Class ----"); + clif_displaymessage(fd, "4023 Baby Novice 4024 Baby Swordsman 4025 Baby Mage 4026 Baby Archer"); + clif_displaymessage(fd, "4027 Baby Acolyte 4028 Baby Merchant 4029 Baby Thief 4030 Baby Knight"); + clif_displaymessage(fd, "4031 Baby Priest 4032 Baby Wizard 4033 Baby Blacksmith 4034 Baby Hunter"); + clif_displaymessage(fd, "4035 Baby Assassin 4037 Baby Crusader 4038 Baby Monk 4039 Baby Sage"); + clif_displaymessage(fd, "4040 Baby Rogue 4041 Baby Alchemist 4042 Baby Bard 4043 Baby Dancer"); + clif_displaymessage(fd, "4045 Super Baby"); + //clif_displaymessage(fd, "---- 3rd Baby Class ----"); + //clif_displaymessage(fd, "4096 Baby Rune Knight 4097 Baby Warlock 4098 Baby Ranger"); + //clif_displaymessage(fd, "4099 Baby Arch Bishop 4100 Baby Mechanic 4101 Baby Guillotine Cross"); + //clif_displaymessage(fd, "4102 Baby Royal Guard 4103 Baby Sorcerer 4104 Baby Minstrel"); + //clif_displaymessage(fd, "4105 Baby Wanderer 4106 Baby Sura 4107 Baby Genetic"); + //clif_displaymessage(fd, "4108 Baby Shadow Chaser"); + //clif_displaymessage(fd, "---- Mounts, Modes, And Others ----"); + //clif_displaymessage(fd, " 13 Knight (Peco) 21 Crusader (Peco) 22 Wedding 26 Christmas"); + //clif_displaymessage(fd, " 27 Summer 4014 Lord Knight (Peco) 4022 Paladin (Peco) 4036 Baby Knight (Peco)"); + //clif_displaymessage(fd, "4044 Baby Crusader (Peco) 4048 Star Gladiator (Union) 4080 Rune Knight (Dragon)"); + //clif_displaymessage(fd, "4081 Rune Knight Trans (Dragon) 4082 Royal Guard (Gryphon)"); + //clif_displaymessage(fd, "4083 Royal Guard Trans (Gryphon) 4084 Ranger (Warg) 4085 Ranger Trans (Warg)"); + //clif_displaymessage(fd, "4086 Mechanic (Mado) 4087 Mechanic Trans (Mado)"); return -1; } @@ -2842,7 +2924,7 @@ ACMD_FUNC(displaystatus) if (i < 2) flag = 1; if (i < 3) tick = 0; - clif_status_change(&sd->bl, type, flag, tick); + clif_status_change(&sd->bl, type, flag, tick, 0, 0, 0); return 0; } @@ -4395,8 +4477,11 @@ ACMD_FUNC(mapinfo) strcat(atcmd_output, "Fireworks | "); if (map[m_id].flag.leaves) strcat(atcmd_output, "Leaves | "); - if (map[m_id].flag.rain) - strcat(atcmd_output, "Rain | "); + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //if (map[m_id].flag.rain) + // strcat(atcmd_output, "Rain | "); if (map[m_id].flag.nightenabled) strcat(atcmd_output, "Displays Night | "); clif_displaymessage(fd, atcmd_output); @@ -4510,7 +4595,26 @@ ACMD_FUNC(mapinfo) ACMD_FUNC(mount_peco) { nullpo_retr(-1, sd); - + if( pc_checkskill(sd,RK_DRAGONTRAINING) > 0 ) { + if( !(sd->sc.option&OPTION_DRAGON1) ) { + clif_displaymessage(sd->fd,"You have mounted your Dragon"); + pc_setoption(sd, sd->sc.option|OPTION_DRAGON1); + } else { + clif_displaymessage(sd->fd,"You have released your Dragon"); + pc_setoption(sd, sd->sc.option&~OPTION_DRAGON1); + } + return 0; + } + if( (sd->class_&MAPID_THIRDMASK) == MAPID_MECHANIC ) { + if( !(sd->sc.option&OPTION_MADOGEAR) ) { + clif_displaymessage(sd->fd,"You have mounted your Mado Gear"); + pc_setoption(sd, sd->sc.option|OPTION_MADOGEAR); + } else { + clif_displaymessage(sd->fd,"You have released your Mado Gear"); + pc_setoption(sd, sd->sc.option&~OPTION_MADOGEAR); + } + return 0; + } if (!pc_isriding(sd)) { // if actually no peco if (!pc_checkskill(sd, KN_RIDING)) { @@ -6117,24 +6221,26 @@ ACMD_FUNC(autolootitem) return 0; } - +/** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ /*========================================== * It is made to rain. *------------------------------------------*/ -ACMD_FUNC(rain) -{ - nullpo_retr(-1, sd); - if (map[sd->bl.m].flag.rain) { - map[sd->bl.m].flag.rain=0; - clif_weather(sd->bl.m); - clif_displaymessage(fd, "The rain has stopped."); - } else { - map[sd->bl.m].flag.rain=1; - clif_weather(sd->bl.m); - clif_displaymessage(fd, "It is made to rain."); - } - return 0; -} +//ACMD_FUNC(rain) +//{ +// nullpo_retr(-1, sd); +// if (map[sd->bl.m].flag.rain) { +// map[sd->bl.m].flag.rain=0; +// clif_weather(sd->bl.m); +// clif_displaymessage(fd, "The rain has stopped."); +// } else { +// map[sd->bl.m].flag.rain=1; +// clif_weather(sd->bl.m); +// clif_displaymessage(fd, "It is made to rain."); +// } +// return 0; +//} /*========================================== * It is made to snow. @@ -6273,7 +6379,10 @@ ACMD_FUNC(fireworks) ACMD_FUNC(clearweather) { nullpo_retr(-1, sd); - map[sd->bl.m].flag.rain=0; + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //map[sd->bl.m].flag.rain=0; map[sd->bl.m].flag.snow=0; map[sd->bl.m].flag.sakura=0; map[sd->bl.m].flag.clouds=0; @@ -7763,13 +7872,72 @@ ACMD_FUNC(fakename) } /*========================================== - * @mapflag [flag name] [1|0|on|off] [map name] by Lupus - * => Shows information about the map flags [map name] - * Also set flags + * Ragnarok Resources *------------------------------------------*/ -ACMD_FUNC(mapflag) -{ -// WIP +ACMD_FUNC(mapflag) { +#define checkflag( cmd ) if ( map[ sd->bl.m ].flag.cmd ) clif_displaymessage(sd->fd,#cmd) +#define setflag( cmd ) \ + if ( strcmp( flag_name , #cmd ) == 0 && ( flag == 0 || flag == 1 ) ){\ + map[ sd->bl.m ].flag.cmd = flag;\ + sprintf(atcmd_output,"[ @mapflag ] %s flag has been set to %s",#cmd,flag?"On":"Off");\ + clif_displaymessage(sd->fd,atcmd_output);\ + return 0;\ + } + unsigned char flag_name[100]; + int flag=9,i; + nullpo_retr(-1, sd); + memset(flag_name, '\0', sizeof(flag_name)); + + if (!message || !*message || (sscanf(message, "%99s %d", flag_name, &flag) < 1)) { + clif_displaymessage(sd->fd,"Enabled Mapflags in this map:"); + clif_displaymessage(sd->fd,"----------------------------------"); + checkflag(autotrade); checkflag(allowks); checkflag(nomemo); checkflag(noteleport); + checkflag(noreturn); checkflag(monster_noteleport); checkflag(nosave); checkflag(nobranch); + checkflag(noexppenalty); checkflag(pvp); checkflag(pvp_noparty); checkflag(pvp_noguild); + checkflag(pvp_nightmaredrop); checkflag(pvp_nocalcrank); checkflag(gvg_castle); checkflag(gvg); + checkflag(gvg_dungeon); checkflag(gvg_noparty); checkflag(battleground);checkflag(nozenypenalty); + checkflag(notrade); checkflag(noskill); checkflag(nowarp); checkflag(nowarpto); + checkflag(noicewall); checkflag(snow); checkflag(clouds); checkflag(clouds2); + checkflag(fog); checkflag(fireworks); checkflag(sakura); checkflag(leaves); + checkflag(nogo); checkflag(nobaseexp); + checkflag(nojobexp); checkflag(nomobloot); checkflag(nomvploot); checkflag(nightenabled); + checkflag(restricted); checkflag(nodrop); checkflag(novending); checkflag(loadevent); + checkflag(nochat); checkflag(partylock); checkflag(guildlock); checkflag(src4instance); + clif_displaymessage(sd->fd," "); + clif_displaymessage(sd->fd,"Usage: \"@mapflag monster_teleport 1\" (0=Off 1=On)"); + clif_displaymessage(sd->fd,"Use: \"@mapflag available\" to list the available mapflags"); + return 1; + } + for (i = 0; flag_name[i]; i++) flag_name[i] = tolower(flag_name[i]); //lowercase + + setflag(autotrade); setflag(allowks); setflag(nomemo); setflag(noteleport); + setflag(noreturn); setflag(monster_noteleport);setflag(nosave); setflag(nobranch); + setflag(noexppenalty); setflag(pvp); setflag(pvp_noparty); setflag(pvp_noguild); + setflag(pvp_nightmaredrop); setflag(pvp_nocalcrank); setflag(gvg_castle); setflag(gvg); + setflag(gvg_dungeon); setflag(gvg_noparty); setflag(battleground); setflag(nozenypenalty); + setflag(notrade); setflag(noskill); setflag(nowarp); setflag(nowarpto); + setflag(noicewall); setflag(snow); setflag(clouds); setflag(clouds2); + setflag(fog); setflag(fireworks); setflag(sakura); setflag(leaves); + setflag(nogo); setflag(nobaseexp); + setflag(nojobexp); setflag(nomobloot); setflag(nomvploot); setflag(nightenabled); + setflag(restricted); setflag(nodrop); setflag(novending); setflag(loadevent); + setflag(nochat); setflag(partylock); setflag(guildlock); setflag(src4instance); + + clif_displaymessage(sd->fd,"Invalid flag name or flag"); + clif_displaymessage(sd->fd,"Usage: \"@mapflag monster_teleport 1\" (0=Off | 1=On)"); + clif_displaymessage(sd->fd,"Available Flags:"); + clif_displaymessage(sd->fd,"----------------------------------"); + clif_displaymessage(sd->fd,"town, autotrade, allowks, nomemo, noteleport, noreturn, monster_noteleport, nosave,"); + clif_displaymessage(sd->fd,"nobranch, noexppenalty, pvp, pvp_noparty, pvp_noguild, pvp_nightmaredrop,"); + clif_displaymessage(sd->fd,"pvp_nocalcrank, gvg_castle, gvg, gvg_dungeon, gvg_noparty, battleground,"); + clif_displaymessage(sd->fd,"nozenypenalty, notrade, noskill, nowarp, nowarpto, noicewall, snow, clouds, clouds2,"); + clif_displaymessage(sd->fd,"fog, fireworks, sakura, leaves, nogo, nobaseexp, nojobexp, nomobloot,"); + clif_displaymessage(sd->fd,"nomvploot, nightenabled, restricted, nodrop, novending, loadevent, nochat, partylock,"); + clif_displaymessage(sd->fd,"guildlock, src4instance"); + +#undef checkflag +#undef setflag + return 0; } @@ -8581,8 +8749,17 @@ ACMD_FUNC(font) return 0; } - - +ACMD_FUNC(new_mount) { + clif_displaymessage(sd->fd,"NOTICE: If you crash with mount your LUA is outdated"); + if( !(sd->sc.option&OPTION_MOUNTING) ) { + clif_displaymessage(sd->fd,"You have mounted."); + pc_setoption(sd, sd->sc.option|OPTION_MOUNTING); + } else { + clif_displaymessage(sd->fd,"You have released your mount"); + pc_setoption(sd, sd->sc.option&~OPTION_MOUNTING); + } + return 0; +} /*========================================== * atcommand_info[] structure definition *------------------------------------------*/ @@ -8885,6 +9062,10 @@ AtCommandInfo atcommand_info[] = { { "delitem", 60,60, atcommand_delitem }, { "charcommands", 1,1, atcommand_commands }, { "font", 1,1, atcommand_font }, + /** + * For Testing Purposes, not going to be here after we're done. + **/ + { "newmount", 0,99, atcommand_new_mount }, }; @@ -9025,9 +9206,12 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message info = get_atcommandinfo_byname(command); if( info == NULL || info->func == NULL || ( type && ((*atcmd_msg == atcommand_symbol && pc_isGM(sd) < info->level) || (*atcmd_msg == charcommand_symbol && pc_isGM(sd) < info->level2)) ) ) { + if( pc_isGM(sd) ) { sprintf(output, msg_txt(153), command); // "%s is Unknown Command." clif_displaymessage(fd, output); return true; + } else + return false; } //Attempt to use the command diff --git a/src/map/battle.c b/src/map/battle.c index b9a408a84..556b51bbd 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -118,7 +118,7 @@ static int battle_getenemy_sub(struct block_list *bl, va_list ap) if (bl->id == target->id) return 0; - if (*c >= 24) + if (*c >= 23) return 0; if (status_isdead(bl)) return 0; @@ -136,8 +136,47 @@ struct block_list* battle_getenemy(struct block_list *target, int type, int rang int c = 0; memset(bl_list, 0, sizeof(bl_list)); map_foreachinrange(battle_getenemy_sub, target, range, type, bl_list, &c, target); - if (c == 0 || c > 24) + if (c == 0 ) + return NULL; + if( c >= 24 ) + c = 23; + return bl_list[rand()%c]; +} +static int battle_getenemyarea_sub(struct block_list *bl, va_list ap) +{ + struct block_list **bl_list, *src; + int *c, ignore_id; + + bl_list = va_arg(ap, struct block_list **); + c = va_arg(ap, int *); + src = va_arg(ap, struct block_list *); + ignore_id = va_arg(ap, int); + + if( bl->id == src->id || bl->id == ignore_id ) + return 0; // Ignores Caster and a possible pre-target + if( *c >= 23 ) + return 0; + if( status_isdead(bl) ) + return 0; + if( battle_check_target(src, bl, BCT_ENEMY) > 0 ) + { // Is Enemy!... + bl_list[(*c)++] = bl; + return 1; + } + return 0; +} + +// Pick a random enemy +struct block_list* battle_getenemyarea(struct block_list *src, int x, int y, int range, int type, int ignore_id) +{ + struct block_list *bl_list[24]; + int c = 0; + memset(bl_list, 0, sizeof(bl_list)); + map_foreachinarea(battle_getenemyarea_sub, src->m, x - range, y - range, x + range, y + range, type, bl_list, &c, src, ignore_id); + if( c == 0 ) return NULL; + if( c >= 24 ) + c = 23; return bl_list[rand()%c]; } @@ -212,7 +251,6 @@ int battle_delay_damage (unsigned int tick, int amotion, struct block_list *src, return 0; } - int battle_attr_ratio(int atk_elem,int def_type, int def_lv) { @@ -316,6 +354,20 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag d->dmg_lv = ATK_BLOCK; return 0; } + if( sc->data[SC_WHITEIMPRISON] && skill_num != HW_GRAVITATION && skill_num != PA_PRESSURE ) { // Gravitation and Pressure do damage without removing the effect + if( skill_num == MG_NAPALMBEAT || + skill_num == MG_SOULSTRIKE || + skill_num == WL_SOULEXPANSION || + (skill_num && skill_get_ele(skill_num, skill_lv) == ELE_GHOST) || + (!skill_num && (status_get_status_data(src))->rhw.ele == ELE_GHOST) + ) + status_change_end(bl,SC_WHITEIMPRISON,-1); // Those skills do damage and removes effect + else + { + d->dmg_lv = ATK_BLOCK; + return 0; + } + } if( sc->data[SC_SAFETYWALL] && (flag&(BF_SHORT|BF_MAGIC))==BF_SHORT ) { @@ -334,7 +386,13 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag d->dmg_lv = ATK_BLOCK; return 0; } - + if( sc->data[SC_WEAPONBLOCKING] && flag&(BF_SHORT|BF_WEAPON) && rand()%100 < sc->data[SC_WEAPONBLOCKING]->val2 ) + { + clif_skill_nodamage(bl,src,GC_WEAPONBLOCKING,1,1); + d->dmg_lv = ATK_NONE; + sc_start2(bl,SC_COMBO,100,GC_WEAPONBLOCKING,src->id,2000); + return 0; + } if( (sce=sc->data[SC_AUTOGUARD]) && flag&BF_WEAPON && !(skill_get_nk(skill_num)&NK_NO_CARDFIX_ATK) && rand()%100 < sce->val2 ) { int delay; @@ -487,6 +545,14 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag status_change_end(bl, SC_REJECTSWORD, INVALID_TIMER); } + //Finally added to remove the status of immobile when aimedbolt is used. [Jobbie] + if( skill_num == RA_AIMEDBOLT && (sc->data[SC_BITE] || sc->data[SC_ANKLE] || sc->data[SC_ELECTRICSHOCKER]) ) + { + status_change_end(bl, SC_BITE, -1); + status_change_end(bl, SC_ANKLE, -1); + status_change_end(bl, SC_ELECTRICSHOCKER, -1); + } + //Finally Kyrie because it may, or not, reduce damage to 0. if((sce = sc->data[SC_KYRIE]) && damage > 0){ sce->val2-=damage; @@ -539,6 +605,8 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag break; } } + if( sc->data[SC_POISONINGWEAPON] && skill_num != GC_VENOMPRESSURE && (flag&BF_WEAPON) && damage > 0 && rand()%100 < sc->data[SC_POISONINGWEAPON]->val3 ) + sc_start(bl,sc->data[SC_POISONINGWEAPON]->val2,100,sc->data[SC_POISONINGWEAPON]->val1,skill_get_time2(GC_POISONINGWEAPON,sc->data[SC_POISONINGWEAPON]->val1)); } if (battle_config.pk_mode && sd && bl->type == BL_PC && damage) @@ -720,6 +788,12 @@ int battle_addmastery(struct map_session_data *sd,struct block_list *target,int (battle_check_undead(status->race,status->def_ele) || status->race==RC_DEMON) ) damage += (skill*(int)(3+(sd->status.base_level+1)*0.05)); // submitted by orn //damage += (skill * 3); + if( (skill = pc_checkskill(sd, RA_RANGERMAIN)) > 0 && (status->race == RC_BRUTE || status->race == RC_PLANT || status->race == RC_FISH) ) + damage += (skill * 5); + if( (skill = pc_checkskill(sd,NC_RESEARCHFE)) > 0 && (status->def_ele == ELE_FIRE || status->def_ele == ELE_EARTH) ) + damage += (skill * 10); + if( (sd->sc.option&OPTION_MADOGEAR) ) + damage += 20 + 20 * pc_checkskill(sd, NC_MADOLICENCE); if((skill = pc_checkskill(sd,HT_BEASTBANE)) > 0 && (status->race==RC_BRUTE || status->race==RC_INSECT) ) { damage += (skill * 4); @@ -755,11 +829,15 @@ int battle_addmastery(struct map_session_data *sd,struct block_list *target,int case W_2HAXE: if((skill = pc_checkskill(sd,AM_AXEMASTERY)) > 0) damage += (skill * 3); + if((skill = pc_checkskill(sd,NC_TRAININGAXE)) > 0) + damage += (skill * 5); break; case W_MACE: case W_2HMACE: if((skill = pc_checkskill(sd,PR_MACEMASTERY)) > 0) damage += (skill * 3); + if((skill = pc_checkskill(sd,NC_TRAININGAXE)) > 0) + damage += (skill * 5); break; case W_FIST: if((skill = pc_checkskill(sd,TK_RUN)) > 0) @@ -1278,6 +1356,9 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo if(sd && pc_checkskill(sd,AS_SONICACCEL)>0) hitrate += hitrate * 50 / 100; break; + case GC_VENOMPRESSURE: + hitrate += 10 + 4 * skill_lv; + break; } // Weaponry Research hidden bonus @@ -1727,6 +1808,160 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo case NPC_VAMPIRE_GIFT: skillratio += ((skill_lv-1)%5+1)*100; break; + case RK_SONICWAVE: { + int level = status_get_lv(src); + skillratio += 400 + 100 * skill_lv; + if( level > 100 ) + skillratio += skillratio * (level - 100) / 200; + } + break; + case RK_HUNDREDSPEAR: { + int level = status_get_lv(src); + skillratio += 500 + 40 * skill_lv; + if( level > 100 ) + skillratio += skillratio * (level - 100) / 200; + } + break; + case RK_WINDCUTTER: { + int level = status_get_lv(src); + skillratio += 50 * skill_lv; + if( level > 100 ) + skillratio += skillratio * (level - 50) / 200; + } + break; + case RK_IGNITIONBREAK: { + int level = status_get_lv(src); + i = distance_bl(src,target); + if( i < 2 ) + skillratio = 200 + 200 * skill_lv; + else if( i < 4 ) + skillratio = 100 + 200 * skill_lv; + else + skillratio = 100 + 100 * skill_lv; + if( level > 100 ) + skillratio += skillratio * (level - 100) / 200; + if( sstatus->rhw.ele == ELE_FIRE ) + skillratio += skillratio / 2; + } + break; + case RK_CRUSHSTRIKE: + 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 = sstatus->rhw.atk + 100 * sd->inventory_data[index]->wlv * (sd->status.inventory[index].refine + 6); + } + break; + case RK_STORMBLAST: + skillratio = 100 * (sd ? pc_checkskill(sd,RK_RUNEMASTERY) : 1) + 100 * (sstatus->int_ / 4); + break; + case RK_PHANTOMTHRUST: + skillratio = 50 * skill_lv + 10 * ( sd ? pc_checkskill(sd,KN_SPEARMASTERY) : 10); + //if( s_level > 100 ) skillratio += skillratio * s_level / 150; // Base level bonus. This is official, but is disabled until I can confirm something with was changed or not. [Rytech] + //if( s_level > 100 ) skillratio += skillratio * (s_level - 100) / 200; // Base level bonus. + break; + /** + * GC Guilotine Cross + **/ + case GC_CROSSIMPACT: + skillratio += 1050 + 50 * skill_lv; + break; + case GC_PHANTOMMENACE: + skillratio += 200; + break; + case GC_COUNTERSLASH: + skillratio += 200 + (100 * skill_lv) + sstatus->agi; + break; + case GC_ROLLINGCUTTER: + skillratio += 20 * skill_lv; + break; + case GC_CROSSRIPPERSLASHER: + skillratio += 60 + 40 * skill_lv; + if( sc && sc->data[SC_ROLLINGCUTTER] ) + skillratio += 25 * sc->data[SC_ROLLINGCUTTER]->val1; + break; + /** + * Arch Bishop + **/ + case AB_DUPLELIGHT_MELEE: + skillratio += 10 * skill_lv; + break; + /** + * Ranger + **/ + case RA_ARROWSTORM: + skillratio += 100 + 50 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case RA_AIMEDBOLT: + skillratio += 400 + 50 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( tsc && (tsc->data[SC_BITE] || tsc->data[SC_ANKLE] || tsc->data[SC_ELECTRICSHOCKER]) ) + wd.div_ = tstatus->size + 2 + rand()%2; + break; + case RA_CLUSTERBOMB: + skillratio += 100 + 100 * skill_lv; + break; + case RA_WUGDASH: + skillratio = 500; + break; + case RA_WUGSTRIKE: + skillratio = 200 * skill_lv; + break; + case RA_WUGBITE: + skillratio += 300 + 200 * skill_lv; + if ( skill_lv == 5 ) skillratio += 100; + break; + case RA_SENSITIVEKEEN: + skillratio += 50 * skill_lv; + break; + /** + * Mechanic + **/ + case NC_BOOSTKNUCKLE: + skillratio += 100 + 100 * skill_lv + sstatus->dex; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_PILEBUNKER: + skillratio += 200 + 100 * skill_lv + sstatus->str; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_VULCANARM: + skillratio = 70 * skill_lv + sstatus->dex; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_FLAMELAUNCHER: + case NC_COLDSLOWER: + skillratio += 200 + 300 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_ARMSCANNON: + switch( tstatus->size ) { + case 0: skillratio += 100 + 500 * skill_lv; break;// Small + case 1: skillratio += 100 + 400 * skill_lv; break;// Medium + case 2: skillratio += 100 + 300 * skill_lv; break;// Large + } + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + //NOTE: Their's some other factors that affects damage, but not sure how exactly. Will recheck one day. [Rytech] + break; + case NC_AXEBOOMERANG: + skillratio += 60 + 40 * 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 += sd->inventory_data[index]->weight / 10;// Weight is divided by 10 since 10 weight in coding make 1 whole actural weight. [Rytech] + } + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_POWERSWING: + skillratio += 80 + 20 * skill_lv + sstatus->str + sstatus->dex; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_AXETORNADO: + skillratio += 100 + 100 * skill_lv + sstatus->vit; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + } ATK_RATE(skillratio); @@ -1755,6 +1990,16 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo case NJ_SYURIKEN: ATK_ADD(4*skill_lv); break; + /** + * Ranger + **/ + case RA_WUGDASH: + case RA_WUGSTRIKE: + case RA_WUGBITE: + if(sd) + ATK_ADD(30*pc_checkskill(sd, RA_TOOTHOFWUG)); + break; + } } //Div fix. @@ -1764,13 +2009,17 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo if (sc) { if(sc->data[SC_TRUESIGHT]) ATK_ADDRATE(2*sc->data[SC_TRUESIGHT]->val1); - + #if RE_EDP == 0 + /** + * In RE EDP doesn't affect your final damage but your atk and weapon atk + **/ if(sc->data[SC_EDP] && skill_num != ASC_BREAKER && skill_num != ASC_METEORASSAULT && skill_num != AS_SPLASHER && skill_num != AS_VENOMKNIFE) ATK_ADDRATE(sc->data[SC_EDP]->val3); + #endif } switch (skill_num) { @@ -1787,6 +2036,10 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo sc->data[SC_SPIRIT]->val2 == SL_CRUSADER) ATK_ADDRATE(100); break; + case NC_AXETORNADO: + if( (sstatus->rhw.ele) == ELE_WIND || (sstatus->lhw.ele) == ELE_WIND ) + ATK_ADDRATE(50); + break; } if( sd ) @@ -1881,6 +2134,9 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo if((battle_check_undead(sstatus->race,sstatus->def_ele) || sstatus->race==RC_DEMON) && //This bonus already doesnt work vs players src->type == BL_MOB && (skill=pc_checkskill(tsd,AL_DP)) > 0) vit_def += skill*(int)(3 +(tsd->status.base_level+1)*0.04); // submitted by orn + if( src->type == BL_MOB && (skill=pc_checkskill(tsd,RA_RANGERMAIN))>0 && + (sstatus->race == RC_BRUTE || sstatus->race == RC_FISH || sstatus->race == RC_PLANT) ) + vit_def += skill*5; } else { //Mob-Pet vit-eq //VIT + rnd(0,[VIT/20]^2-1) vit_def = (def2/20)*(def2/20); @@ -2293,20 +2549,34 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo struct Damage md = battle_calc_misc_attack(src, target, skill_num, skill_lv, wflag); wd.damage += md.damage; } - - //SG_FUSION hp penalty [Komurka] - if (sc && sc->data[SC_FUSION]) - { - int hp= sstatus->max_hp; - if (sd && tsd) { - hp = 8*hp/100; - if (100*sstatus->hp <= 20*sstatus->max_hp) - hp = sstatus->hp; - } else - hp = 2*hp/100; //2% hp loss per hit - status_zap(src, hp, 0); + if( sc ) { + //SG_FUSION hp penalty [Komurka] + if (sc->data[SC_FUSION]) + { + int hp= sstatus->max_hp; + if (sd && tsd) { + hp = 8*hp/100; + if (100*sstatus->hp <= 20*sstatus->max_hp) + hp = sstatus->hp; + } else + hp = 2*hp/100; //2% hp loss per hit + status_zap(src, hp, 0); + } + /** + * affecting non-skills + **/ + if( !skill_num ) { + /** + * RK Enchant Blade + **/ + if( sc->data[SC_ENCHANTBLADE] && sd && ( (flag.rh && sd->weapontype1) || (flag.lh && sd->weapontype2) ) ) { + struct Damage md = battle_calc_magic_attack(src, target, RK_ENCHANTBLADE, pc_checkskill(sd,RK_ENCHANTBLADE), wflag); + wd.damage += md.damage; + wd.flag |= md.flag; + } } + } return wd; } @@ -2398,6 +2668,10 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list case AL_HEAL: case PR_BENEDICTIO: case PR_SANCTUARY: + /** + * Arch Bishop + **/ + case AB_HIGHNESSHEAL: ad.damage = skill_calc_heal(src, target, skill_num, skill_lv, false); break; case PR_ASPERSIO: @@ -2417,14 +2691,31 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list case PF_SOULBURN: ad.damage = tstatus->sp * 2; break; + /** + * Arch Bishop + **/ + case AB_RENOVATIO: + //Damage calculation from iRO wiki. [Jobbie] + ad.damage = (int)((15 * status_get_lv(src)) + (1.5 * sstatus->int_)); + break; default: { + #if RRMODE //Renewal MATK Appliance according to doddler (?title=Renewal_Changes#Upgrade_MATK) + /** + * min: (weaponMATK+upgradeMATK) * 2 + 1.5 * statusMATK + * max: [weaponMATK+upgradeMATK+(wMatk*wLvl)/10] * 2 + 1.5 * statusMATK + * yes this formula MATCHES their site: matk_max already holds weaponmatk+upgradematk, and + * -> statusMATK holds the %Matk modifier stuff from earlier and lastly: + * -> the mdef part is not applied at this point, but later. + **/ + MATK_ADD(sstatus->matk_max * 2 + 15/10 * sstatus->matk_min + rand()% ( sstatus->matk_max + (sstatus->matk_max*sstatus->wlv) / 10 * 2 * 10/15 * sstatus->matk_min ) ); + #else //Ancient MATK Appliance if (sstatus->matk_max > sstatus->matk_min) { MATK_ADD(sstatus->matk_min+rand()%(1+sstatus->matk_max-sstatus->matk_min)); } else { MATK_ADD(sstatus->matk_min); } - + #endif if(nk&NK_SPLASHSPLIT){ // Divide MATK in case of multiple targets skill if(mflag>0) ad.damage/= mflag; @@ -2470,6 +2761,11 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list case WZ_SIGHTRASHER: skillratio += 20*skill_lv; break; +#if FIREIVY_ON + case WZ_FIREIVY: + skillratio += 20*skill_lv-15; + break; +#endif case WZ_VERMILION: skillratio += 20*skill_lv-20; break; @@ -2513,6 +2809,193 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list case NPC_EARTHQUAKE: skillratio += 100 +100*skill_lv +100*(skill_lv/2); break; + /** + * Arch Bishop + **/ + case AB_JUDEX: + skillratio += 180 + 20 * skill_lv; + if (skill_lv > 4) skillratio += 20; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case AB_ADORAMUS: + skillratio += 400 + 100 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case AB_DUPLELIGHT_MAGIC: + skillratio += 100 + 20 * skill_lv; + break; + /** + * Warlock + **/ + case WL_SOULEXPANSION: + skillratio += 300 + 100 * skill_lv + sstatus->int_; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_FROSTMISTY: + skillratio += 100 + 100 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_JACKFROST: + { + struct status_change *tsc = status_get_sc(target); + if( tsc && tsc->data[SC_FREEZING] ) + { + skillratio += 900 + 300 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + } + else + skillratio += 400 + 100 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + } + break; + case WL_DRAINLIFE: + skillratio = 200 * skill_lv + sstatus->int_; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_CRIMSONROCK: + skillratio += 1200 + 300 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_HELLINFERNO: + if( status_get_element(target) == ELE_FIRE ) + skillratio = 60 * skill_lv; + else + skillratio = 240 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_COMET: { + struct status_change * sc = status_get_sc(src); + if( sc ) + i = distance_xy(target->x, target->y, sc->comet_x, sc->comet_y); + else + i = 8; + if( i < 2 ) skillratio = 2500 + 500 * skill_lv; + else + if( i < 4 ) skillratio = 1600 + 400 * skill_lv; + else + if( i < 6 ) skillratio = 1200 + 300 * skill_lv; + else + skillratio = 800 + 200 * skill_lv; + } + break; + case WL_CHAINLIGHTNING_ATK: + skillratio += 100 + 300 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_EARTHSTRAIN: + skillratio += 1900 + 100 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_TETRAVORTEX_FIRE: + case WL_TETRAVORTEX_WATER: + case WL_TETRAVORTEX_WIND: + case WL_TETRAVORTEX_GROUND: + skillratio += 400 + 500 * skill_lv; + break; + case WL_SUMMON_ATK_FIRE: + case WL_SUMMON_ATK_WATER: + case WL_SUMMON_ATK_WIND: + case WL_SUMMON_ATK_GROUND: + skillratio = skill_lv * (status_get_lv(src) + ( sd ? sd->status.job_level : 50 ));// This is close to official, but lacking a little info to finalize. [Rytech] + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case LG_RAYOFGENESIS: + skillratio = (skillratio + 200) * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WM_METALICSOUND: + skillratio += 120 * skill_lv + 60 * ( sd? pc_checkskill(sd, WM_LESSON) : 10 ) - 100; + break; + case WM_SEVERE_RAINSTORM: + skillratio += 50 * skill_lv; + break; + case WM_REVERBERATION_MAGIC: + skillratio += 100 * (sd ? pc_checkskill(sd, WM_REVERBERATION) : 1); + break; + case SO_FIREWALK: { + struct status_change * sc = status_get_sc(src); + skillratio = 300; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_HEATER_OPTION] ) + skillratio += skillratio * sc->data[SC_HEATER_OPTION]->val3 / 100; + } + break; + case SO_ELECTRICWALK: { + struct status_change * sc = status_get_sc(src); + skillratio = 300; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_BLAST_OPTION] ) + skillratio += skillratio * sc->data[SC_BLAST_OPTION]->val2 / 100; + } + break; + case SO_EARTHGRAVE: { + struct status_change * sc = status_get_sc(src); + skillratio = ( 200 * ( sd ? pc_checkskill(sd, SA_SEISMICWEAPON) : 10 ) + sstatus->int_ * skill_lv ); + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_CURSED_SOIL_OPTION] ) + skillratio += skillratio * sc->data[SC_CURSED_SOIL_OPTION]->val2 / 100; + } + break; + case SO_DIAMONDDUST: { + struct status_change * sc = status_get_sc(src); + skillratio = ( 200 * ( sd ? pc_checkskill(sd, SA_FROSTWEAPON) : 10 ) + sstatus->int_ * skill_lv ); + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_COOLER_OPTION] ) + skillratio += skillratio * sc->data[SC_COOLER_OPTION]->val3 / 100; + } + break; + case SO_POISON_BUSTER: { + struct status_change * sc = status_get_sc(src); + skillratio += 1100 + 300 * skill_lv; + if( sc && sc->data[SC_CURSED_SOIL_OPTION] ) + skillratio += skillratio * sc->data[SC_CURSED_SOIL_OPTION]->val2 / 100; + } + break; + case SO_PSYCHIC_WAVE: { + struct status_change * sc = status_get_sc(src); + skillratio += -100 + skill_lv * 70 + (sstatus->int_ * 3); + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc ){ + if( sc->data[SC_HEATER_OPTION] ) + skillratio += skillratio * sc->data[SC_HEATER_OPTION]->val3 / 100; + else if(sc->data[SC_COOLER_OPTION] ) + skillratio += skillratio * sc->data[SC_COOLER_OPTION]->val3 / 100; + else if(sc->data[SC_BLAST_OPTION] ) + skillratio += skillratio * sc->data[SC_BLAST_OPTION]->val2 / 100; + else if(sc->data[SC_CURSED_SOIL_OPTION] ) + skillratio += skillratio * sc->data[SC_CURSED_SOIL_OPTION]->val3 / 100; + } + } + break; + case SO_VARETYR_SPEAR: { + struct status_change * sc = status_get_sc(src); + skillratio += -100 + ( 100 * ( sd ? pc_checkskill(sd, SA_LIGHTNINGLOADER) : 10 ) + sstatus->int_ * skill_lv ); + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_BLAST_OPTION] ) + skillratio += skillratio * sc->data[SC_BLAST_OPTION]->val2 / 100; + } + break; + case SO_CLOUD_KILL: { + struct status_change * sc = status_get_sc(src); + skillratio += -100 + skill_lv * 40; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_CURSED_SOIL_OPTION] ) + skillratio += skillratio * sc->data[SC_CURSED_SOIL_OPTION]->val2 / 100; + } + break; + case GN_DEMONIC_FIRE: + if( skill_lv > 20) + { // Fire expansion Lv.2 + skillratio += 110 + 20 * (skill_lv - 20) + status_get_int(src) * 3; // Need official INT bonus. [LimitLine] + } + else if( skill_lv > 10 ) + { // Fire expansion Lv.1 + skillratio += 110 + 20 * (skill_lv - 10) / 2; + } + else + skillratio += 110 + 20 * skill_lv; + break; + } MATK_RATE(skillratio); @@ -2550,10 +3033,19 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list //mdef2-= mdef2* i/100; } } + #if RRMODE + /** + * RE MDEF Reduction (from doddler:?title=Renewal_Changes#MDEF) + * Damage from magic = Magic Attack * 111.5/(111.5+eMDEF) + * Damage = Magic Attack * 111.5/(111.5+eMDEF) - sMDEF + **/ + ad.damage = ad.damage * ((1115/10) - mdef)/(1115/10) - mdef2; + #else if(battle_config.magic_defense_type) ad.damage = ad.damage - mdef*battle_config.magic_defense_type - mdef2; else ad.damage = ad.damage * (100-mdef)/100 - mdef2; + #endif } if (skill_num == NPC_EARTHQUAKE) @@ -2805,6 +3297,32 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list * case NPC_EVILLAND: md.damage = skill_calc_heal(src,target,skill_num,skill_lv,false); break; + case RK_DRAGONBREATH: + md.damage = ((status_get_hp(src) / 50) + (status_get_max_sp(src) / 4)) * skill_lv; + if (status_get_lv(src) > 100) md.damage = md.damage * status_get_lv(src) / 150; + if (sd) md.damage = md.damage * (100 + 5 * (pc_checkskill(sd,RK_DRAGONTRAINING) - 1)) / 100; + break; + /** + * Ranger + **/ + case RA_CLUSTERBOMB: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: + md.damage = (2 * skill_lv * (sstatus->dex + 100)); + if (status_get_lv(src) > 100) md.damage += md.damage * (status_get_lv(src) - 50) / 200 + 15 / 10; + md.damage = md.damage * 2;// Without BaseLv Bonus + md.damage = md.damage + (5 * sstatus->int_) + (40 * ( sd ? pc_checkskill(sd,RA_RESEARCHTRAP) : 10 ) ); + break; + /** + * Mechanic + **/ + case NC_SELFDESTRUCTION: + md.damage = (sd?pc_checkskill(sd,NC_MAINFRAME):10) * skill_lv * (status_get_sp(src) + sstatus->vit); + if (status_get_lv(src) > 100) md.damage = md.damage * status_get_lv(src) / 150;// Base level bonus. + if (sd) md.damage = md.damage + status_get_hp(src); + status_set_sp(src, 0, 0); + break; + } if (nk&NK_SPLASHSPLIT){ // Divide ATK among targets @@ -2906,11 +3424,23 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list * else if( map[target->m].flag.battleground ) md.damage=battle_calc_bg_damage(src,target,md.damage,md.div_,skill_num,skill_lv,md.flag); - if (skill_num == NJ_ZENYNAGE && sd) - { //Time to Pay Up. - if ( md.damage > sd->status.zeny ) - md.damage=sd->status.zeny; - pc_payzeny(sd, md.damage); + switch( skill_num ) { + case RA_CLUSTERBOMB: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: + { + struct Damage wd; + wd = battle_calc_weapon_attack(src,target,skill_num,skill_lv,mflag); + md.damage += wd.damage; + } + break; + case NJ_ZENYNAGE: + if( sd ) { + if ( md.damage > sd->status.zeny ) + md.damage = sd->status.zeny; + pc_payzeny(sd, md.damage); + } + break; } return md; @@ -2943,10 +3473,10 @@ struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct bl } //Calculates BF_WEAPON returned damage. -int battle_calc_return_damage(struct block_list* bl, int damage, int flag) +int battle_calc_return_damage(struct block_list* bl, struct block_list *src, int *dmg, int flag) { struct map_session_data* sd = NULL; - int rdamage = 0; + int rdamage = 0, damage = *dmg; sd = BL_CAST(BL_PC, bl); @@ -2959,10 +3489,24 @@ int battle_calc_return_damage(struct block_list* bl, int damage, int flag) if(rdamage < 1) rdamage = 1; } sc = status_get_sc(bl); - if (sc && sc->data[SC_REFLECTSHIELD]) - { - rdamage += damage * sc->data[SC_REFLECTSHIELD]->val2 / 100; - if (rdamage < 1) rdamage = 1; + if( sc && sc->count ) { + if (sc->data[SC_REFLECTSHIELD]) { + rdamage += damage * sc->data[SC_REFLECTSHIELD]->val2 / 100; + if (rdamage < 1) rdamage = 1; + } + if(sc->data[SC_DEATHBOUND] && !(src->type == BL_MOB && is_boss(src)) ) { + int dir = map_calc_dir(bl,src->x,src->y), + t_dir = unit_getdir(bl), rd1 = 0; + + if( distance_bl(src,bl) <= 0 || !map_check_dir(dir,t_dir) ) { + rd1 = min(damage,status_get_max_hp(bl)) * sc->data[SC_DEATHBOUND]->val2 / 100; // Amplify damage. + *dmg = rd1 * 30 / 100; // Received damge = 30% of amplifly damage. + clif_skill_damage(src,bl,gettick(), status_get_amotion(src), 0, -30000, 1, RK_DEATHBOUND, sc->data[SC_DEATHBOUND]->val1,6); + status_change_end(bl,SC_DEATHBOUND,-1); + rdamage += rd1; + if (rdamage < 1) rdamage = 1; + } + } } } else { if (sd && sd->long_weapon_damage_return) @@ -3165,7 +3709,17 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t damage = wd.damage + wd.damage2; if( damage > 0 && src != target ) { - rdamage = battle_calc_return_damage(target, damage, wd.flag); + if( sc && sc->data[SC_DUPLELIGHT] && (wd.flag&BF_SHORT) && rand()%100 <= 10+2*sc->data[SC_DUPLELIGHT]->val1 ) + { // Activates it only from melee damage + int skillid; + if( rand()%2 == 1 ) + skillid = AB_DUPLELIGHT_MELEE; + else + skillid = AB_DUPLELIGHT_MAGIC; + skill_attack(skill_get_type(skillid), src, src, target, skillid, sc->data[SC_DUPLELIGHT]->val1, tick, SD_LEVEL); + } + + rdamage = battle_calc_return_damage(target,src, &damage, wd.flag); if( rdamage > 0 ) { rdelay = clif_damage(src, src, tick, wd.amotion, sstatus->dmotion, rdamage, 1, 4, 0); @@ -3373,6 +3927,8 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f case WZ_SIGHTBLASTER: case SM_MAGNUM: case MS_MAGNUM: + case RA_DETONATOR: + case RA_SENSITIVEKEEN: state |= BCT_ENEMY; strip_enemy = 0; break; @@ -4023,6 +4579,10 @@ static const struct _battle_data { { "bg_magic_attack_damage_rate", &battle_config.bg_magic_damage_rate, 60, 0, INT_MAX, }, { "bg_misc_attack_damage_rate", &battle_config.bg_misc_damage_rate, 60, 0, INT_MAX, }, { "bg_flee_penalty", &battle_config.bg_flee_penalty, 20, 0, INT_MAX, }, + /** + * RR-Specific + **/ + { "max_third_parameter", &battle_config.max_third_parameter, 20, 0, INT_MAX, }, }; diff --git a/src/map/battle.h b/src/map/battle.h index 007499afe..a64c81041 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -35,7 +35,7 @@ struct block_list; struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int count); -int battle_calc_return_damage(struct block_list *bl, int damage, int flag); +int battle_calc_return_damage(struct block_list *bl, struct block_list *src, int *, int flag); void battle_drain(struct map_session_data *sd, struct block_list *tbl, int rdamage, int ldamage, int race, int boss); @@ -497,6 +497,8 @@ extern struct Battle_Config int bg_magic_damage_rate; int bg_misc_damage_rate; int bg_flee_penalty; + //[RR] + int max_third_parameter; } battle_config; void do_init_battle(void); @@ -507,4 +509,7 @@ extern void battle_set_defaults(void); int battle_set_value(const char* w1, const char* w2); int battle_get_value(const char* w1); +// +struct block_list* battle_getenemyarea(struct block_list *src, int x, int y, int range, int type, int ignore_id); + #endif /* _BATTLE_H_ */ diff --git a/src/map/chrif.c b/src/map/chrif.c index d1332fab2..947cb0bdc 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -505,7 +505,13 @@ void chrif_on_ready(void) ShowStatus("Map Server is now online.\n"); chrif_state = 2; chrif_check_shutdown(); - + /** + * while we're not fully ready + **/ + ShowMessage(""CL_XXBL""CL_BT_YELLOW"============= WARNING ============="CL_XXBL""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_XXBL"- "CL_BT_YELLOW"This version is under development and shouldn't be used as a real server"CL_XXBL""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_XXBL"- "CL_BT_YELLOW"For bugs, comments and suggestions: http://ro-resources.net "CL_XXBL""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_XXBL"- "CL_BT_YELLOW"Thank you for trying out"CL_XXBL""CL_CLL""CL_NORMAL"\n"); //If there are players online, send them to the char-server. [Skotlex] send_users_tochar(); diff --git a/src/map/clif.c b/src/map/clif.c index da2abe0f3..780b1af56 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -154,7 +154,8 @@ void clif_setbindip(const char* ip) } /*========================================== - * map鯖のport設定 + * Sets map port to 'port' + * is run from map.c upon loading map server configuration *------------------------------------------*/ void clif_setport(uint16 port) { @@ -162,7 +163,7 @@ void clif_setport(uint16 port) } /*========================================== - * map鯖のip読み出し + * Returns map server IP *------------------------------------------*/ uint32 clif_getip(void) { @@ -184,7 +185,7 @@ uint32 clif_refresh_ip(void) } /*========================================== - * map鯖のport読み出し + * Returns map port which is set by clif_setport() *------------------------------------------*/ uint16 clif_getport(void) { @@ -210,7 +211,14 @@ static inline unsigned char clif_bl_type(struct block_list *bl) { #endif /*========================================== - * clif_sendでAREA*指定時用 + * sub process of clif_send + * Called from a map_foreachinarea (grabs all players in specific area and subjects them to this function) + * In order to send area-wise packets, such as: + * - AREA : everyone nearby your area + * - AREA_WOSC (AREA WITHOUT SAME CHAT) : Not run for people in the same chat as yours + * - AREA_WOC (AREA WITHOUT CHAT) : Not run for people inside a chat + * - AREA_WOS (AREA WITHOUT SELF) : Not run for self + * - AREA_CHAT_WOC : Everyone in the area of your chat without a chat *------------------------------------------*/ int clif_send_sub(struct block_list *bl, va_list ap) { @@ -273,7 +281,8 @@ 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) { @@ -554,11 +563,10 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target return 0; } -// -// パケット作って送信 -// + /*========================================== - * + * Tells client that its player is fully loaded and that it can proceed to the map screen + * Provides client with player position and facing direction *------------------------------------------*/ int clif_authok(struct map_session_data *sd) { @@ -657,7 +665,8 @@ int clif_dropflooritem(struct flooritem_data* fitem) } /*========================================== - * + * Server tells client to remove item of ID ('fitem->bl.id') from FD player area + * If FD is 0 it tells all clients nearby this item that it is gone *------------------------------------------*/ int clif_clearflooritem(struct flooritem_data *fitem, int fd) { @@ -1105,7 +1114,8 @@ static void clif_setdisguise(struct block_list *bl, unsigned char *buf,int len) } /*========================================== - * クラスチェンジ typeはMobの場合は1で他は0? + * Acronym for 'clif_mob_class_change' used to tell clients around the monster that it's identity changed + * for example, it's run when a pupa transforms into a creamy. *------------------------------------------*/ int clif_class_change(struct block_list *bl,int class_,int type) { @@ -1124,7 +1134,7 @@ int clif_class_change(struct block_list *bl,int class_,int type) } /*========================================== - * + * Server tells client to display (sd->spiritball) amount of spiritballs on target of account id (sd->bl.id) *------------------------------------------*/ static void clif_spiritball_single(int fd, struct map_session_data *sd) { @@ -1136,7 +1146,8 @@ static void clif_spiritball_single(int fd, struct map_session_data *sd) } /*========================================== - * + * Run when player changes map / refreshes + * Tells its client to display all weather settings being used by this map *------------------------------------------*/ static void clif_weather_check(struct map_session_data *sd) { @@ -1148,7 +1159,10 @@ static void clif_weather_check(struct map_session_data *sd) || map[m].flag.fireworks || map[m].flag.sakura || map[m].flag.leaves - || map[m].flag.rain + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //|| map[m].flag.rain || map[m].flag.clouds2) { if (map[m].flag.snow) @@ -1168,11 +1182,16 @@ static void clif_weather_check(struct map_session_data *sd) clif_specialeffect_single(&sd->bl, 163, fd); if (map[m].flag.leaves) clif_specialeffect_single(&sd->bl, 333, fd); - if (map[m].flag.rain) - clif_specialeffect_single(&sd->bl, 161, fd); + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //if (map[m].flag.rain) + // clif_specialeffect_single(&sd->bl, 161, fd); } } - +/** + * Run when the weather on a map changes, throws all players in map id 'm' to clif_weather_check function + **/ void clif_weather(int m) { struct s_mapiterator* iter; @@ -1186,7 +1205,9 @@ void clif_weather(int m) } mapit_free(iter); } - +/** + * Main function to spawn a unit on the client (player/mob/pet/etc) + **/ int clif_spawn(struct block_list *bl) { unsigned char buf[128]; @@ -1218,6 +1239,10 @@ int clif_spawn(struct block_list *bl) clif_specialeffect(bl,421,AREA); if( sd->bg_id && map[sd->bl.m].flag.battleground ) clif_sendbgemblem_area(sd); + if( sd->sc.option&OPTION_MOUNTING ) { + //New Mounts are not complaint to the original method, so we gotta tell this guy that he is mounting. + clif_status_load_notick(&sd->bl,SI_ALL_RIDING,2,1,0,0); + } } break; case BL_MOB: @@ -1873,7 +1898,7 @@ int clif_viewpoint(struct map_session_data *sd, int npc_id, int type, int x, int } /*========================================== - * + * Server tells client to display cutin of name 'image' to client, in position 'type' (255, etc) *------------------------------------------*/ int clif_cutin(struct map_session_data* sd, const char* image, int type) { @@ -1945,7 +1970,8 @@ static void clif_addcards(unsigned char* buf, struct item* item) } /*========================================== - * + * Server tells client he got item of index 'n' and amount 'amount', + * when 'fail' is 1 it tells the client it failed to receive said item *------------------------------------------*/ int clif_additem(struct map_session_data *sd, int n, int amount, int fail) { @@ -2014,7 +2040,7 @@ int clif_additem(struct map_session_data *sd, int n, int amount, int fail) } /*========================================== - * + * *------------------------------------------*/ int clif_dropitem(struct map_session_data *sd,int n,int amount) { @@ -2457,8 +2483,9 @@ int clif_guild_xy_remove(struct map_session_data *sd) } /*========================================== - * ステータスを送りつける - * 表示専用数字はこの中で計算して送る + * Server tells client that data 'type' has changed and sends it's new value + * For example, when server updates the client max weight, say, due to higher STR, + * It calls this function with SP_MAXWEIGHT type *------------------------------------------*/ int clif_updatestatus(struct map_session_data *sd,int type) { @@ -2590,7 +2617,9 @@ int clif_updatestatus(struct map_session_data *sd,int type) WFIFOL(fd,4)=pc_nextjobexp(sd); break; - // 00be 終了 + /** + * SP_U<STAT> are used to update the amount of points necessary to increase that stat + **/ case SP_USTR: case SP_UAGI: case SP_UVIT: @@ -2602,14 +2631,15 @@ int clif_updatestatus(struct map_session_data *sd,int type) len=5; break; - // 013a 終了 + /** + * Tells the client how far it is allowed to attack (weapon range) + **/ case SP_ATTACKRANGE: WFIFOW(fd,0)=0x13a; WFIFOW(fd,2)=sd->battle_status.rhw.range; len=4; break; - // 0141 終了 case SP_STR: WFIFOW(fd,0)=0x141; WFIFOL(fd,2)=type; @@ -2699,7 +2729,7 @@ int clif_changestatus(struct block_list *bl,int type,int val) } /*========================================== - * + * Updates BL unit view data to nearby clients *------------------------------------------*/ void clif_changelook(struct block_list *bl,int type,int val) { @@ -2926,7 +2956,7 @@ int clif_initialstatus(struct map_session_data *sd) } /*========================================== - *矢装備 + * Server tells client item idx 'val' is meant to be shown in equipment's window arrow slot *------------------------------------------*/ int clif_arrowequip(struct map_session_data *sd,int val) { @@ -2968,7 +2998,8 @@ int clif_arrow_fail(struct map_session_data *sd,int type) } /*========================================== - * 作成可能 矢リスト送信 + * Server tells client to display a window similar to Mangifier (item) one + * Server populates the window with avilable arrow crafting options according to player's inventory *------------------------------------------*/ int clif_arrow_create_list(struct map_session_data *sd) { @@ -3004,7 +3035,7 @@ int clif_arrow_create_list(struct map_session_data *sd) } /*========================================== - * + * Server tells client his response regarding the earlier request to increase status *------------------------------------------*/ int clif_statusupack(struct map_session_data *sd,int type,int ok,int val) { @@ -3090,7 +3121,7 @@ int clif_misceffect(struct block_list* bl,int type) } /*========================================== - * 表示オプション変更 + * Server tells BL unit and all nearby clients that his unit-view options (e.g. stone curse appearance) changed *------------------------------------------*/ int clif_changeoption(struct block_list* bl) { @@ -3483,7 +3514,7 @@ void clif_tradestart(struct map_session_data* sd, uint8 type) } /*========================================== - * 相手方からのアイテム追加 + * Server tells 'tsd' player client info on the items 'sd' player just added to the trade window *------------------------------------------*/ void clif_tradeadditem(struct map_session_data* sd, struct map_session_data* tsd, int index, int amount) { @@ -3547,7 +3578,9 @@ void clif_tradeadditem(struct map_session_data* sd, struct map_session_data* tsd } /*========================================== - * アイテム追加成功/失敗 + * Server tells client on the status of it's OK request + * fail 1 : the other person did 'ok' + * fail 0 : you did 'ok' *------------------------------------------*/ void clif_tradeitemok(struct map_session_data* sd, int index, int fail) { @@ -3563,7 +3596,9 @@ void clif_tradeitemok(struct map_session_data* sd, int index, int fail) } /*========================================== - * 取り引きok押し + * Server tells client on the status of it's lock request + * fail 1 : The other trader lock request + * fail 0 : Your lock request *------------------------------------------*/ void clif_tradedeal_lock(struct map_session_data* sd, int fail) { @@ -3578,7 +3613,7 @@ void clif_tradedeal_lock(struct map_session_data* sd, int fail) } /*========================================== - * 取り引きがキャンセルされました + * Server tells client it's trade request was cancelled *------------------------------------------*/ void clif_tradecancelled(struct map_session_data* sd) { @@ -3592,7 +3627,7 @@ void clif_tradecancelled(struct map_session_data* sd) } /*========================================== - * 取り引き完了 + * Server tells client the final status on his trade request *------------------------------------------*/ void clif_tradecompleted(struct map_session_data* sd, int fail) { @@ -3607,7 +3642,7 @@ void clif_tradecompleted(struct map_session_data* sd, int fail) } /*========================================== - * カプラ倉庫のアイテム数を更新 + * Server tells client it's quantity of items in storage changed *------------------------------------------*/ void clif_updatestorageamount(struct map_session_data* sd, int amount) { @@ -3624,7 +3659,7 @@ void clif_updatestorageamount(struct map_session_data* sd, int amount) } /*========================================== - * カプラ倉庫にアイテムを追加する + * Server tells client it's status on his request to add a item to storage *------------------------------------------*/ void clif_storageitemadded(struct map_session_data* sd, struct item* i, int index, int amount) { @@ -3679,7 +3714,7 @@ void clif_updateguildstorageamount(struct map_session_data* sd, int amount) } /*========================================== - * カプラ倉庫からアイテムを取り去る + * Server tells client its status on his request to remove a item from storage *------------------------------------------*/ void clif_storageitemremoved(struct map_session_data* sd, int index, int amount) { @@ -3696,7 +3731,7 @@ void clif_storageitemremoved(struct map_session_data* sd, int index, int amount) } /*========================================== - * カプラ倉庫を閉じる + * Server tells client his storage was closed *------------------------------------------*/ void clif_storageclose(struct map_session_data* sd) { @@ -3709,9 +3744,9 @@ void clif_storageclose(struct map_session_data* sd) WFIFOW(fd,0) = 0xf8; // Storage Closed WFIFOSET(fd,packet_len(0xf8)); } - +int clif_status_load_single(int fd, int id,int type,int flag,int val1, int val2, int val3); /*========================================== - * PC表示 + * Server tells 'sd' player client the abouts of 'dstsd' player *------------------------------------------*/ static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_data* dstsd) { @@ -3735,7 +3770,10 @@ static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_d if(dstsd->spiritball > 0) clif_spiritball_single(sd->fd, dstsd); - + if( dstsd->sc.option&OPTION_MOUNTING ) { + //New Mounts are not complaint to the original method, so we gotta tell this guy that I'm mounting. + clif_status_load_single(sd->fd,dstsd->bl.id,SI_ALL_RIDING,2,1,0,0); + } if( (sd->status.party_id && dstsd->status.party_id == sd->status.party_id) || //Party-mate, or hpdisp setting. (sd->bg_id && sd->bg_id == dstsd->bg_id) || //BattleGround (battle_config.disp_hpmeter && (gmlvl = pc_isGM(sd)) >= battle_config.disp_hpmeter && gmlvl >= pc_isGM(dstsd)) ) @@ -4044,7 +4082,7 @@ void clif_getareachar_item(struct map_session_data* sd,struct flooritem_data* fi } /*========================================== - * 場所スキルエフェクトが視界に入る + * Server tells client 'sd' of all nearby skill units (e.g. safety wall) *------------------------------------------*/ static void clif_getareachar_skillunit(struct map_session_data *sd, struct skill_unit *unit) { @@ -4084,7 +4122,7 @@ static void clif_getareachar_skillunit(struct map_session_data *sd, struct skill } /*========================================== - * 場所スキルエフェクトが視界から消える + * Server tells client to remove unit of id 'unit->bl.id' *------------------------------------------*/ static void clif_clearchar_skillunit(struct skill_unit *unit, int fd) { @@ -4100,7 +4138,7 @@ static void clif_clearchar_skillunit(struct skill_unit *unit, int fd) } /*========================================== - * 場所スキルエフェクト削除 + * Server tells all clients in sight of 'unit->bl.id' ID to remove itself from sight (delete) *------------------------------------------*/ void clif_skill_delunit(struct skill_unit *unit) { @@ -4245,7 +4283,7 @@ int clif_insight(struct block_list *bl,va_list ap) } /*========================================== - * スキルリストを送信する + * Server tells the client information on 'sd' player's skill tree *------------------------------------------*/ int clif_skillinfoblock(struct map_session_data *sd) { @@ -4282,7 +4320,9 @@ int clif_skillinfoblock(struct map_session_data *sd) return 1; } - +/** + * Server tells client 'sd' to add skill of id 'id' to it's skill tree (e.g. with Ice Falcion item) + **/ int clif_addskill(struct map_session_data *sd, int id ) { int fd; @@ -4333,7 +4373,7 @@ int clif_deleteskill(struct map_session_data *sd, int id) } /*========================================== - * スキル割り振り通知 + * Server tells client it's skill of id 'skill_num' level changed *------------------------------------------*/ int clif_skillup(struct map_session_data *sd,int skill_num) { @@ -4668,7 +4708,7 @@ int clif_skill_damage2(struct block_list *src,struct block_list *dst,unsigned in */ /*========================================== - * 支援/回復スキルエフェクト + * Server tells client(s) that 'src' casted a skill of nodamage type (e.g. heal) on 'dst' *------------------------------------------*/ int clif_skill_nodamage(struct block_list *src,struct block_list *dst,int skill_id,int heal,int fail) { @@ -4701,7 +4741,8 @@ int clif_skill_nodamage(struct block_list *src,struct block_list *dst,int skill_ } /*========================================== - * 場所スキルエフェクト + * Server tells client 'src' to display effect of skill id 'skill_id' on location 'x' and 'y' + * 'val' is used for information that varies from skill to skill, usually it's the skill level *------------------------------------------*/ int clif_skill_poseffect(struct block_list *src,int skill_id,int val,int x,int y,int tick) { @@ -4727,7 +4768,7 @@ int clif_skill_poseffect(struct block_list *src,int skill_id,int val,int x,int y } /*========================================== - * 場所スキルエフェクト表示 + * Tells all client's nearby 'unit' sight range that it spawned *------------------------------------------*/ //FIXME: this is just an AREA version of clif_getareachar_skillunit() void clif_skill_setunit(struct skill_unit *unit) @@ -4765,7 +4806,7 @@ void clif_skill_setunit(struct skill_unit *unit) } /*========================================== - * ワープ場所選択 + * Used to display 'teleport' and 'warp portal' information on it's respective dialogs *------------------------------------------*/ void clif_skill_warppoint(struct map_session_data* sd, short skill_num, short skill_lv, unsigned short map1, unsigned short map2, unsigned short map3, unsigned short map4) { @@ -4832,7 +4873,7 @@ void clif_skill_teleportmessage(struct map_session_data *sd, int type) } /*========================================== - * モンスター情報 + * Server tells client to display 'estimation' (Sense) information for monster (bl) 'dst' *------------------------------------------*/ int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst) { @@ -4868,14 +4909,15 @@ int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst) return 0; } /*========================================== - * アイテム合成可能リスト + * Server tells client to display a window similar to Mangifier (item) one + * Server populates the window with avilable crafting options according to skill used to call this *------------------------------------------*/ -int clif_skill_produce_mix_list(struct map_session_data *sd, int trigger) +int clif_skill_produce_mix_list(struct map_session_data *sd, int skillid , int trigger) { int i,c,view,fd; nullpo_ret(sd); - if(sd->menuskill_id == AM_PHARMACY) + if(sd->menuskill_id == skillid) return 0; //Avoid resending the menu twice or more times... fd=sd->fd; WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB * 8 + 8); @@ -4895,7 +4937,7 @@ int clif_skill_produce_mix_list(struct map_session_data *sd, int trigger) WFIFOW(fd, 2)=c*8+8; WFIFOSET(fd,WFIFOW(fd,2)); if(c > 0) { - sd->menuskill_id = AM_PHARMACY; + sd->menuskill_id = skillid; sd->menuskill_val = trigger; return 1; } @@ -4966,9 +5008,9 @@ int clif_status_load(struct block_list *bl,int type, int flag) return 0; } /*========================================== - * 状態異常アイコン/メッセージ表示 + * Server tell's BL and nearby clients of his status change *------------------------------------------*/ -int clif_status_change(struct block_list *bl,int type,int flag,unsigned int tick) +int clif_status_change(struct block_list *bl,int type,int flag,unsigned int tick,int val1, int val2, int val3) { unsigned char buf[32]; @@ -4996,9 +5038,9 @@ int clif_status_change(struct block_list *bl,int type,int flag,unsigned int tick if( battle_config.display_status_timers && tick>0 ) { WBUFL(buf,9)=tick; - WBUFL(buf,13)=0; - WBUFL(buf,17)=0; - WBUFL(buf,21)=0; + WBUFL(buf,13) = val1; + WBUFL(buf,17) = val2; + WBUFL(buf,21) = val3; } clif_send(buf,packet_len(WBUFW(buf,0)),bl,AREA); return 0; @@ -5031,7 +5073,6 @@ int clif_displaymessage(const int fd, const char* mes) } /*========================================== - * 天の声を送信する * Send broadcast message in yellow or blue (without font formatting). * S 009A <len>.W <message>.?B *------------------------------------------*/ @@ -5055,7 +5096,8 @@ int clif_broadcast(struct block_list* bl, const char* mes, int len, int type, en } /*========================================== - * グローバルメッセージ + * Displays a message on a 'bl' to all it's nearby clients + * Used by npc_globalmessage *------------------------------------------*/ void clif_GlobalMessage(struct block_list* bl, const char* message) { @@ -5128,7 +5170,8 @@ int clif_broadcast2(struct block_list* bl, const char* mes, int len, unsigned lo return 0; } /*========================================== - * HPSP回復エフェクトを送信する + * Server tells self client to heal self for 'val', is either SP_HP or SP_SP + * It displays these green and blue heal numbers that show up at your body and go up until they fade away *------------------------------------------*/ int clif_heal(int fd,int type,int val) { @@ -5142,7 +5185,7 @@ int clif_heal(int fd,int type,int val) } /*========================================== - * 復活する + * Server tells nearby clients of 'bl' that it ressurected (and plays ress effect) *------------------------------------------*/ int clif_resurrection(struct block_list *bl,int type) { @@ -5190,7 +5233,8 @@ void clif_map_type(struct map_session_data* sd, enum map_type type) } /*========================================== - * PVP実装?(仮) + * Server tells client on it's pvp rank and map status, + * (it controls the counter on the bottom right of the map existent in pvp rooms) *------------------------------------------*/ int clif_pvpset(struct map_session_data *sd,int pvprank,int pvpnum,int type) { @@ -5239,7 +5283,8 @@ void clif_map_property_mapall(int map, enum map_property property) } /*========================================== - * 精錬エフェクトを送信する + * Server tells client the status on refine of item index 'index' from refine 'val' + * Message displayed depends on 'fail' (broken(red) or success(blue)) *------------------------------------------*/ void clif_refine(int fd, int fail, int index, int val) { @@ -5308,7 +5353,8 @@ int clif_wis_end(int fd, int flag) } /*========================================== - * キャラID名前引き結果を送信する + * Server tells client that char id 'charid' is to be assigned the name of 'name' + * This is used when client requests the server the name written in a item, e.g. crafted alche potions *------------------------------------------*/ int clif_solved_charname(int fd, int charid, const char* name) { @@ -5321,7 +5367,7 @@ int clif_solved_charname(int fd, int charid, const char* name) } /*========================================== - * カードの挿入可能リストを返す + * Server tells client to list all items that may be worn by card item of index 'idx' *------------------------------------------*/ int clif_use_card(struct map_session_data *sd,int idx) { @@ -5371,7 +5417,7 @@ int clif_use_card(struct map_session_data *sd,int idx) return 0; } /*========================================== - * カードの挿入終了 + * Server tells client his status on the previous clif_use_card (failed or OK) *------------------------------------------*/ int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag) { @@ -5390,7 +5436,7 @@ int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int } /*========================================== - * 鑑定可能アイテムリスト送信 + * Server tells client it's list of unidentified items *------------------------------------------*/ int clif_item_identify_list(struct map_session_data *sd) { @@ -5419,7 +5465,7 @@ int clif_item_identify_list(struct map_session_data *sd) } /*========================================== - * 鑑定結果 + * Server tells client his item of index 'idx' has been identified *------------------------------------------*/ int clif_item_identified(struct map_session_data *sd,int idx,int flag) { @@ -5437,7 +5483,7 @@ int clif_item_identified(struct map_session_data *sd,int idx,int flag) } /*========================================== - * 修理可能アイテムリスト送信 + * Server tells client the list of broken items *------------------------------------------*/ int clif_item_repair_list(struct map_session_data *sd,struct map_session_data *dstsd) { @@ -5537,7 +5583,9 @@ int clif_item_refine_list(struct map_session_data *sd) } /*========================================== - * アイテムによる一時的なスキル効果 + * Server tells client to display the 'green skill name' at the top of the screen + target cursor, + * for skill 'skillid' of 'skilllv' level + * Used for example when player uses a skill scroll (e.g. Fire Bolt Scroll) *------------------------------------------*/ int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv) { @@ -5561,7 +5609,7 @@ int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv) } /*========================================== - * カートにアイテム追加 + * Server tells client it's status on trying to add item of index 'n' and amount 'amount' to it's cart *------------------------------------------*/ int clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail) { @@ -5611,7 +5659,7 @@ int clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail) } /*========================================== - * カートからアイテム削除 + * Server tells client it's status on trying to remove item of index 'n' and amount 'amount' from it's cart to invent *------------------------------------------*/ int clif_cart_delitem(struct map_session_data *sd,int n,int amount) { @@ -5996,10 +6044,11 @@ void clif_party_inviteack(struct map_session_data* sd, const char* nick, int res /*========================================== - * パーティ設定送信 - * flag & 0x001=exp変更ミス - * 0x010=item変更ミス - * 0x100=一人にのみ送信 + * Server tells client (and it's party members) of a change in the party settings + * 'Flag' Options + * - 0x01 (exp) + * - 0x10 (item) + * - 0x100 (party member logged in / was added to party) *------------------------------------------*/ int clif_party_option(struct party_data *p,struct map_session_data *sd,int flag) { @@ -6034,7 +6083,7 @@ int clif_party_option(struct party_data *p,struct map_session_data *sd,int flag) return 0; } /*========================================== - * パーティ脱退(脱退前に呼ぶこと) + * Server tells party members of party 'p' that 'sd' player left *------------------------------------------*/ int clif_party_withdraw(struct party_data* p, struct map_session_data* sd, int account_id, const char* name, int flag) { @@ -6056,15 +6105,14 @@ int clif_party_withdraw(struct party_data* p, struct map_session_data* sd, int a WBUFL(buf,2)=account_id; memcpy(WBUFP(buf,6),name,NAME_LENGTH); WBUFB(buf,30)=flag&0x0f; - if((flag&0xf0)==0) clif_send(buf,packet_len(0x105),&sd->bl,PARTY); - else + else clif_send(buf,packet_len(0x105),&sd->bl,SELF); return 0; } /*========================================== - * パーティメッセージ送信 + * Server deploys a message to all party members, called from party.c:party_recv_message() *------------------------------------------*/ int clif_party_message(struct party_data* p, int account_id, const char* mes, int len) { @@ -6086,7 +6134,7 @@ int clif_party_message(struct party_data* p, int account_id, const char* mes, in return 0; } /*========================================== - * パーティ座標通知 + * Server tells all party members of 'sd' player that 'sd' player location changed *------------------------------------------*/ int clif_party_xy(struct map_session_data *sd) { @@ -6119,7 +6167,7 @@ int clif_party_xy_single(int fd, struct map_session_data *sd) /*========================================== - * パーティHP通知 + * Server tells nearby party members of 'sd' that his hp bar has updated *------------------------------------------*/ int clif_party_hp(struct map_session_data *sd) { @@ -6224,7 +6272,7 @@ int clif_hpmeter_sub(struct block_list *bl, va_list ap) } /*========================================== - * GMへ場所とHP通知 + * Server tells all nearby gms to 'sd' that 'sd' hp bar was updated *------------------------------------------*/ int clif_hpmeter(struct map_session_data *sd) { @@ -6237,7 +6285,7 @@ int clif_hpmeter(struct map_session_data *sd) } /*========================================== - * パーティ場所移動(未使用) + * (?) Server tells 'sd' party members that 'sd' state 'changed' *------------------------------------------*/ void clif_party_move(struct party* p, struct map_session_data* sd, int online) { @@ -6258,7 +6306,8 @@ void clif_party_move(struct party* p, struct map_session_data* sd, int online) clif_send(buf,packet_len(0x104),&sd->bl,PARTY); } /*========================================== - * 攻撃するために移動が必要 + * Server tells client to attack bl, if not in range of attack (rhw.range) it'll move to bl + * called from unit.c *------------------------------------------*/ int clif_movetoattack(struct map_session_data *sd,struct block_list *bl) { @@ -6280,7 +6329,7 @@ int clif_movetoattack(struct map_session_data *sd,struct block_list *bl) return 0; } /*========================================== - * 製造エフェクト + * Server tells client to display produce effect (refine-like), success or failure depends on 'flag' *------------------------------------------*/ int clif_produceeffect(struct map_session_data* sd,int flag,int nameid) { @@ -6331,7 +6380,7 @@ int clif_pet_roulette(struct map_session_data *sd,int data) } /*========================================== - * pet卵リスト作成 + * Server tells client to list it's eggs (used in hatching window to select a egg) *------------------------------------------*/ int clif_sendegg(struct map_session_data *sd) { @@ -6461,7 +6510,7 @@ int clif_pet_food(struct map_session_data *sd,int foodid,int fail) } /*========================================== - * オートスペル リスト送信 + * Server tells client to display autospell (Sage Skill) skill selection list *------------------------------------------*/ int clif_autospell(struct map_session_data *sd,int skilllv) { @@ -6549,7 +6598,7 @@ void clif_devotion(struct block_list *src, struct map_session_data *tsd) } /*========================================== - * 氣球 + * Server tells clients nearby 'sd' (and himself) to display 'sd->spiritball' number of spiritballs on 'sd' *------------------------------------------*/ int clif_spiritball(struct map_session_data *sd) { @@ -6581,7 +6630,8 @@ int clif_combo_delay(struct block_list *bl,int wait) return 0; } /*========================================== - *白刃取り + * Server tells client to display blade stop animation 'link' from 'src' to 'dst_id' (account id of target) + * active toggles the state *------------------------------------------*/ void clif_bladestop(struct block_list *src, int dst_id, int active) { @@ -6598,7 +6648,7 @@ void clif_bladestop(struct block_list *src, int dst_id, int active) } /*========================================== - * MVPエフェクト + * Server tells clients nearby 'sd' (and itself) to display MvP killed effect on 'sd' player *------------------------------------------*/ int clif_mvp_effect(struct map_session_data *sd) { @@ -6612,7 +6662,7 @@ int clif_mvp_effect(struct map_session_data *sd) return 0; } /*========================================== - * MVPアイテム所得 + * Server tells client to display mvp drop prize info to player 'sd' for item id 'nameid' *------------------------------------------*/ int clif_mvp_item(struct map_session_data *sd,int nameid) { @@ -6631,7 +6681,7 @@ int clif_mvp_item(struct map_session_data *sd,int nameid) return 0; } /*========================================== - * MVP経験値所得 + * Server tells client to display mvp exp prize to player 'sd' for amount 'exp' *------------------------------------------*/ int clif_mvp_exp(struct map_session_data *sd, unsigned int exp) { @@ -6693,7 +6743,7 @@ void clif_guild_belonginfo(struct map_session_data *sd, struct guild *g) /*========================================== - * ギルドメンバログイン通知 + * Server tells all members of 'g' guild that member of index 'idx' is online or offline (flag 1:0) *------------------------------------------*/ int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag) { @@ -6755,7 +6805,7 @@ int clif_guild_send_onlineinfo(struct map_session_data *sd) } /*========================================== - * ギルドマスター通知(14dへの応答) + * Tells 'sd' whether he is the guild master of his guild or not (relies on sd->state.gmaster_flag) *------------------------------------------*/ int clif_guild_masterormember(struct map_session_data *sd) { @@ -6814,7 +6864,7 @@ int clif_guild_basicinfo(struct map_session_data *sd) } /*========================================== - * ギルド同盟/敵対情報 + * Server tells client 'sd' it's guild alliances *------------------------------------------*/ int clif_guild_allianceinfo(struct map_session_data *sd) { @@ -6843,7 +6893,7 @@ int clif_guild_allianceinfo(struct map_session_data *sd) } /*========================================== - * ギルドメンバーリスト + * Server tells client it's guild member list *------------------------------------------*/ int clif_guild_memberlist(struct map_session_data *sd) { @@ -6873,7 +6923,7 @@ int clif_guild_memberlist(struct map_session_data *sd) WFIFOL(fd,c*104+22)=(int)cap_value(m->exp,0,INT32_MAX); WFIFOL(fd,c*104+26)=m->online; WFIFOL(fd,c*104+30)=m->position; - memset(WFIFOP(fd,c*104+34),0,50); // メモ? + memset(WFIFOP(fd,c*104+34),0,50); //[Ind] - This is displayed in the 'note' column but being you can't edit it it's sent empty. memcpy(WFIFOP(fd,c*104+84),m->name,NAME_LENGTH); c++; } @@ -6882,7 +6932,7 @@ int clif_guild_memberlist(struct map_session_data *sd) return 0; } /*========================================== - * ギルド役職名リスト + * Server tell client it's guild position list *------------------------------------------*/ int clif_guild_positionnamelist(struct map_session_data *sd) { @@ -6905,7 +6955,7 @@ int clif_guild_positionnamelist(struct map_session_data *sd) return 0; } /*========================================== - * ギルド役職情報リスト + * Server tell client about it's guild position permissions and tax *------------------------------------------*/ int clif_guild_positioninfolist(struct map_session_data *sd) { @@ -6931,7 +6981,7 @@ int clif_guild_positioninfolist(struct map_session_data *sd) return 0; } /*========================================== - * ギルド役職変更通知 + * Server tells client about position 'idx' information, being it changed *------------------------------------------*/ int clif_guild_positionchanged(struct guild *g,int idx) { @@ -6952,7 +7002,7 @@ int clif_guild_positionchanged(struct guild *g,int idx) return 0; } /*========================================== - * ギルドメンバ変更通知 + * Server tells client about a specific guild member index that changed *------------------------------------------*/ int clif_guild_memberpositionchanged(struct guild *g,int idx) { @@ -6971,7 +7021,7 @@ int clif_guild_memberpositionchanged(struct guild *g,int idx) return 0; } /*========================================== - * ギルドエンブレム送信 + * Server tells client about this new cool emblem a specific guild got *------------------------------------------*/ int clif_guild_emblem(struct map_session_data *sd,struct guild *g) { @@ -7076,7 +7126,7 @@ int clif_guild_notice(struct map_session_data* sd, struct guild* g) } /*========================================== - * ギルドメンバ勧誘 + * Server tells client 'sd' that guild 'g' wants to invite him *------------------------------------------*/ int clif_guild_invite(struct map_session_data *sd,struct guild *g) { @@ -7116,7 +7166,7 @@ int clif_guild_inviteack(struct map_session_data *sd,int flag) } /*========================================== - * ギルドメンバ脱退通知 + * Server tells guild members of 'sd' that he left his guild for a reason *------------------------------------------*/ int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes) { @@ -7132,7 +7182,7 @@ int clif_guild_leave(struct map_session_data *sd,const char *name,const char *me } /*========================================== - * ギルドメンバ追放通知 + * Server tells guild members of 'sd' that 'name' of account id 'account_id' was expelled for reason 'mes' *------------------------------------------*/ void clif_guild_expulsion(struct map_session_data* sd, const char* name, const char* mes, int account_id) { @@ -7155,7 +7205,7 @@ void clif_guild_expulsion(struct map_session_data* sd, const char* name, const c } /*========================================== - * ギルド追放メンバリスト + * Server tells client on sd's guild expulsion records *------------------------------------------*/ void clif_guild_expulsionlist(struct map_session_data* sd) { @@ -7225,7 +7275,7 @@ void clif_guild_message(struct guild *g,int account_id,const char *mes,int len) /*========================================== - * ギルドスキル割り振り通知 + * Server tells client 'sd' that his guild skill 'skill_num' gone to level 'lv' *------------------------------------------*/ int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv) { @@ -7245,7 +7295,7 @@ int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv) return 0; } /*========================================== - * ギルド同盟要請 + * Server tells client 'sd' that 'account_id' from guild name 'name' wants to invite 'sd's guild for alliance *------------------------------------------*/ int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name) { @@ -7284,7 +7334,7 @@ int clif_guild_allianceack(struct map_session_data *sd,int flag) return 0; } /*========================================== - * ギルド関係解消通知 + * Server tells client 'sd' that guild_id is either in or out of it's alliance list (depend on flag) *------------------------------------------*/ int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag) { @@ -7337,7 +7387,7 @@ int clif_guild_oppositionack(struct map_session_data *sd,int flag) }*/ /*========================================== - * ギルド解散通知 + * Server tells client 'sd' that guild broke because of 'flag' reason *------------------------------------------*/ int clif_guild_broken(struct map_session_data *sd,int flag) { @@ -7354,7 +7404,7 @@ int clif_guild_broken(struct map_session_data *sd,int flag) } /*========================================== - * エモーション + * Server tells all nearby clients of 'bl' to display emoticon number 'type' *------------------------------------------*/ void clif_emotion(struct block_list *bl,int type) { @@ -7369,7 +7419,7 @@ void clif_emotion(struct block_list *bl,int type) } /*========================================== - * トーキーボックス + * Server tells all clients nearby 'bl' that he stepped in a talkie box (and displays the message) *------------------------------------------*/ void clif_talkiebox(struct block_list* bl, const char* talkie) { @@ -7383,7 +7433,7 @@ void clif_talkiebox(struct block_list* bl, const char* talkie) } /*========================================== - * 結婚エフェクト + * Server tells bl and nearby clients to display marriage effect *------------------------------------------*/ void clif_wedding_effect(struct block_list *bl) { @@ -7396,7 +7446,7 @@ void clif_wedding_effect(struct block_list *bl) clif_send(buf, packet_len(0x1ea), bl, AREA); } /*========================================== - * ?なたに逢いたい使用時名前叫び + * Server tells client 'sd' to create a warp to call his partner (wedding skill) *------------------------------------------*/ void clif_callpartner(struct map_session_data *sd) @@ -7562,41 +7612,41 @@ void clif_GM_silence(struct map_session_data* sd, struct map_session_data* tsd, } /*========================================== - * Wis拒否許可応答 + * ? Unknown functionality : not called anywhere *------------------------------------------*/ -int clif_wisexin(struct map_session_data *sd,int type,int flag) -{ - int fd; - - nullpo_ret(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xd1)); - WFIFOW(fd,0)=0xd1; - WFIFOB(fd,2)=type; - WFIFOB(fd,3)=flag; - WFIFOSET(fd,packet_len(0xd1)); - - return 0; -} +//int clif_wisexin(struct map_session_data *sd,int type,int flag) +//{ +// int fd; +// +// nullpo_ret(sd); +// +// fd=sd->fd; +// WFIFOHEAD(fd,packet_len(0xd1)); +// WFIFOW(fd,0)=0xd1; +// WFIFOB(fd,2)=type; +// WFIFOB(fd,3)=flag; +// WFIFOSET(fd,packet_len(0xd1)); +// +// return 0; +//} /*========================================== - * Wis全拒否許可応答 + * ? Unknown functionality : not called anywhere *------------------------------------------*/ -int clif_wisall(struct map_session_data *sd,int type,int flag) -{ - int fd; - - nullpo_ret(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xd2)); - WFIFOW(fd,0)=0xd2; - WFIFOB(fd,2)=type; - WFIFOB(fd,3)=flag; - WFIFOSET(fd,packet_len(0xd2)); - - return 0; -} +//int clif_wisall(struct map_session_data *sd,int type,int flag) +//{ +// int fd; +// +// nullpo_ret(sd); +// +// fd=sd->fd; +// WFIFOHEAD(fd,packet_len(0xd2)); +// WFIFOW(fd,0)=0xd2; +// WFIFOB(fd,2)=type; +// WFIFOB(fd,3)=flag; +// WFIFOSET(fd,packet_len(0xd2)); +// +// return 0; +//} /*========================================== * Play a BGM! [Rikter/Yommy] @@ -7615,7 +7665,8 @@ void clif_playBGM(struct map_session_data* sd, const char* name) } /*========================================== - * サウンドエフェクト + * Server tells 'bl' to play a .wav music file in client's /wav/ folder named 'name' + * functionality of 'type' is unclear. it's normally sent as '0' *------------------------------------------*/ void clif_soundeffect(struct map_session_data* sd, struct block_list* bl, const char* name, int type) { @@ -8459,7 +8510,7 @@ static int clif_guess_PacketVer(int fd, int get_previous, int *error) // ------------ // clif_parse_* // ------------ -// パケット読み取って色々操作 +// Parses incoming (player) connection /*========================================== * *------------------------------------------*/ @@ -8548,8 +8599,9 @@ void clif_parse_WantToConnection(int fd, TBL_PC* sd) } /*========================================== - * 007d クライアント側マップ読み込み完了 - * map侵入時に必要なデータを全て送りつける + * 007d : Server/Client tells that he is able to proceed + * This is run by both server (from pc.c) and client (on map load/refresh + * (teleport/warping in same map also triggers this) *------------------------------------------*/ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) { @@ -9020,9 +9072,11 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd) const char* text = (char*)RFIFOP(fd,4); int textlen = RFIFOW(fd,2) - 4; - char *name, *message; + char *name, *message, *fakename = NULL; int namelen, messagelen; + bool is_fake; + // validate packet and retrieve name and message if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) ) return; @@ -9039,21 +9093,35 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd) return; sd->cantalk_tick = gettick() + battle_config.min_chat_delay; } - + /** + * Fake Name Design by FatalEror (bug report #9) + **/ + if( ( is_fake = ( sd->fakename[0] ) ) ) { + fakename = (char*) malloc(strlen(sd->fakename)+messagelen+3); + strcpy(fakename, sd->fakename); + strcat(fakename, " : "); + strcat(fakename, message); + textlen = strlen(fakename) + 1; + } // send message to others (using the send buffer for temp. storage) WFIFOHEAD(fd, 8 + textlen); WFIFOW(fd,0) = 0x8d; WFIFOW(fd,2) = 8 + textlen; WFIFOL(fd,4) = sd->bl.id; - safestrncpy((char*)WFIFOP(fd,8), text, textlen); + safestrncpy((char*)WFIFOP(fd,8), is_fake ? fakename : text, textlen); //FIXME: chat has range of 9 only clif_send(WFIFOP(fd,0), WFIFOW(fd,2), &sd->bl, sd->chatID ? CHAT_WOS : AREA_CHAT_WOC); // send back message to the speaker - memcpy(WFIFOP(fd,0), RFIFOP(fd,0), RFIFOW(fd,2)); - WFIFOW(fd,0) = 0x8e; + if( is_fake ) { + WFIFOW(fd,0) = 0x8e; + WFIFOW(fd,2) = textlen + 4; + safestrncpy((char*)WFIFOP(fd,4), fakename, textlen); + } else { + memcpy(WFIFOP(fd,0), RFIFOP(fd,0), RFIFOW(fd,2)); + WFIFOW(fd,0) = 0x8e; + } WFIFOSET(fd, WFIFOW(fd,2)); - #ifdef PCRE_SUPPORT // trigger listening npcs map_foreachinrange(npc_chat_sub, &sd->bl, AREA_SIZE, BL_NPC, text, textlen, &sd->bl); @@ -9837,7 +9905,7 @@ static void clif_noask_sub(struct map_session_data *src, struct map_session_data } /*========================================== - * 取引要請を相手に送る + * Client tells server to send a trade request to char id RFIFOL(fd,2) *------------------------------------------*/ void clif_parse_TradeRequest(int fd,struct map_session_data *sd) { @@ -9864,7 +9932,7 @@ void clif_parse_TradeRequest(int fd,struct map_session_data *sd) } /*========================================== - * 取引要請 + * Client tells server he replied to a trade request sent to him *------------------------------------------*/ void clif_parse_TradeAck(int fd,struct map_session_data *sd) { @@ -9872,7 +9940,7 @@ void clif_parse_TradeAck(int fd,struct map_session_data *sd) } /*========================================== - * アイテム追加 + * Client tells server to add RFIFOL(fd,4) quantity of item index RFIFOW(fd,2) *------------------------------------------*/ void clif_parse_TradeAddItem(int fd,struct map_session_data *sd) { @@ -9886,7 +9954,7 @@ void clif_parse_TradeAddItem(int fd,struct map_session_data *sd) } /*========================================== - * アイテム追加完了(ok押し) + * Client tells server player he is done adding items to his trade window *------------------------------------------*/ void clif_parse_TradeOk(int fd,struct map_session_data *sd) { @@ -9894,7 +9962,7 @@ void clif_parse_TradeOk(int fd,struct map_session_data *sd) } /*========================================== - * 取引キャンセル + * Client tells server player cancelled the trade *------------------------------------------*/ void clif_parse_TradeCancel(int fd,struct map_session_data *sd) { @@ -9902,7 +9970,7 @@ void clif_parse_TradeCancel(int fd,struct map_session_data *sd) } /*========================================== - * 取引許諾(trade押し) + * Client tells server player 'locked' the trade screen (can't add/remove items) *------------------------------------------*/ void clif_parse_TradeCommit(int fd,struct map_session_data *sd) { @@ -9918,7 +9986,7 @@ void clif_parse_StopAttack(int fd,struct map_session_data *sd) } /*========================================== - * カートへアイテムを移す + * Client tells server player dragged (RFIFOL(fd,4))x of item idx RIFOFW(fd,2)-2 to cart *------------------------------------------*/ void clif_parse_PutItemToCart(int fd,struct map_session_data *sd) { @@ -9929,7 +9997,7 @@ void clif_parse_PutItemToCart(int fd,struct map_session_data *sd) pc_putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4)); } /*========================================== - * カートからアイテムを出す + * Client tells server to take y (RFIFOL(fd,4)) amount of item (idx:RFIFOW(fd,2)-2) from cart and add to inventory *------------------------------------------*/ void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd) { @@ -9939,16 +10007,18 @@ void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd) } /*========================================== - * 付属品(鷹,ペコ,カート)をはずす + * Client tells server the user hit the 'OFF' button in the equip window (appears when mounting, with falcon, etc) *------------------------------------------*/ void clif_parse_RemoveOption(int fd,struct map_session_data *sd) { - //Can only remove Cart/Riding/Falcon. - pc_setoption(sd,sd->sc.option&~(OPTION_CART|OPTION_RIDING|OPTION_FALCON)); + /** + * Attempts to remove these options when this function is called (will remove all available) + **/ + pc_setoption(sd,sd->sc.option&~(OPTION_CART|OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR)); } /*========================================== - * チェンジカート + * Client tells server the user selected cart type 'type', comes from cart selection screen (Change Cart Skill) *------------------------------------------*/ void clif_parse_ChangeCart(int fd,struct map_session_data *sd) { @@ -9968,7 +10038,7 @@ void clif_parse_ChangeCart(int fd,struct map_session_data *sd) } /*========================================== - * ステータスアップ + * Client tells Server to process a /str, /vit, etc(others) *------------------------------------------*/ void clif_parse_StatusUp(int fd,struct map_session_data *sd) { @@ -9976,7 +10046,7 @@ void clif_parse_StatusUp(int fd,struct map_session_data *sd) } /*========================================== - * スキルレベルアップ + * Client tells server to level up skill (RFIFOW(fd,2)) by 1 *------------------------------------------*/ void clif_parse_SkillUp(int fd,struct map_session_data *sd) { @@ -10056,7 +10126,7 @@ static void clif_parse_UseSkillToPos_mercenary(struct mercenary_data *md, struct } /*========================================== - * スキル使用(ID指定) + * Client tells server he'd like to use skill of id 'skillnum' and level 'skilllv' on 'target_id' *------------------------------------------*/ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) { @@ -10163,7 +10233,7 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) } /*========================================== - * スキル使用(場所指定) + * Client tells server he'd like to use AoE skill id 'skillnum' of level 'skilllv' on 'x','y' location *------------------------------------------*/ void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, short skilllv, short skillnum, short x, short y, int skillmoreinfo) { @@ -10273,7 +10343,7 @@ void clif_parse_UseSkillToPosMoreInfo(int fd, struct map_session_data *sd) ); } /*========================================== - * スキル使用(map指定) + * (?) I *think* this one is for skills cast on self, not entirely sure *------------------------------------------*/ void clif_parse_UseSkillMap(int fd, struct map_session_data* sd) { @@ -10294,7 +10364,7 @@ void clif_parse_UseSkillMap(int fd, struct map_session_data* sd) skill_castend_map(sd,skill_num,map_name); } /*========================================== - * メモ要求 + * Client tells server he did '/memo' *------------------------------------------*/ void clif_parse_RequestMemo(int fd,struct map_session_data *sd) { @@ -10302,13 +10372,12 @@ void clif_parse_RequestMemo(int fd,struct map_session_data *sd) pc_memo(sd,-1); } /*========================================== - * アイテム合成 + * Client tells server he selected something from a crafting window (e.g. pharmacy) *------------------------------------------*/ void clif_parse_ProduceMix(int fd,struct map_session_data *sd) { - if (sd->menuskill_id != AM_PHARMACY) + if( sd->menuskill_id != -1 && sd->menuskill_id != AM_PHARMACY && sd->menuskill_id != RK_RUNEMASTERY ) return; - if (pc_istrading(sd)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif_skill_fail(sd,sd->ud.skillid,0,0); @@ -10341,7 +10410,7 @@ void clif_parse_Cooking(int fd,struct map_session_data *sd) sd->menuskill_val = sd->menuskill_id = 0; } /*========================================== - * 武器修理 + * Client tells server he selected something in his 'repair item list' *------------------------------------------*/ void clif_parse_RepairItem(int fd, struct map_session_data *sd) { @@ -10446,7 +10515,7 @@ void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd) } /*========================================== - * アイテム鑑定 + * Client tells server he selected something in his 'magnifier item list' *------------------------------------------*/ void clif_parse_ItemIdentify(int fd,struct map_session_data *sd) { @@ -10463,23 +10532,35 @@ void clif_parse_ItemIdentify(int fd,struct map_session_data *sd) sd->menuskill_val = sd->menuskill_id = 0; } /*========================================== - * 矢作成 + * Client tells server he selected something in his 'arrow crafting list' *------------------------------------------*/ void clif_parse_SelectArrow(int fd,struct map_session_data *sd) { - if (sd->menuskill_id != AC_MAKINGARROW) - return; if (pc_istrading(sd)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif_skill_fail(sd,sd->ud.skillid,0,0); sd->menuskill_val = sd->menuskill_id = 0; return; } - skill_arrow_create(sd,RFIFOW(fd,2)); + switch( sd->menuskill_id ) { + case AC_MAKINGARROW: + skill_arrow_create(sd,RFIFOW(fd,2)); + break; + case WL_READING_SB: + skill_spellbook(sd,RFIFOW(fd,2)); + break; + case GC_POISONINGWEAPON: + skill_poisoningweapon(sd,RFIFOW(fd,2)); + break; + case NC_MAGICDECOY: + skill_magicdecoy(sd,RFIFOW(fd,2)); + break; + } + sd->menuskill_val = sd->menuskill_id = 0; } /*========================================== - * オートスペル受信 + * Client tells server he selected something in his 'autospell skill list' *------------------------------------------*/ void clif_parse_AutoSpell(int fd,struct map_session_data *sd) { @@ -10489,7 +10570,7 @@ void clif_parse_AutoSpell(int fd,struct map_session_data *sd) sd->menuskill_val = sd->menuskill_id = 0; } /*========================================== - * カード使用 + * Client tells server he clicked on a card item, requests the can-add-to list *------------------------------------------*/ void clif_parse_UseCard(int fd,struct map_session_data *sd) { @@ -10498,7 +10579,7 @@ void clif_parse_UseCard(int fd,struct map_session_data *sd) clif_use_card(sd,RFIFOW(fd,2)-2); } /*========================================== - * カード挿入装備選択 + * Client tells server he selected something in his 'carding list' (the one that lists all items you got that can receive that card) *------------------------------------------*/ void clif_parse_InsertCard(int fd,struct map_session_data *sd) { @@ -10508,7 +10589,7 @@ void clif_parse_InsertCard(int fd,struct map_session_data *sd) } /*========================================== - * 0193 キャラID名前引き + * 0193 : Client asks server for nick reference to a specific char id *------------------------------------------*/ void clif_parse_SolveCharName(int fd, struct map_session_data *sd) { @@ -10567,7 +10648,7 @@ void clif_parse_LocalBroadcast(int fd, struct map_session_data* sd) } /*========================================== - * カプラ倉庫へ入れる + * Client tells server to move (item_amount) quantity of item idx (item_index) from inventory to storage *------------------------------------------*/ void clif_parse_MoveToKafra(int fd, struct map_session_data *sd) { @@ -10589,7 +10670,7 @@ void clif_parse_MoveToKafra(int fd, struct map_session_data *sd) } /*========================================== - * カプラ倉庫から出す + * Client tells server to move (item_amount) quantity of item idx (item_index) from storage to inventory *------------------------------------------*/ void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd) { @@ -10606,7 +10687,7 @@ void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd) } /*========================================== - * カプラ倉庫へカートから入れる + * Client tells server to move RFIFOL(fd,4) quantity of item idx RFIFOW(fd,2) from cart to storage *------------------------------------------*/ void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd) { @@ -10623,7 +10704,7 @@ void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd) } /*========================================== - * カプラ倉庫から出す + * Client tells server to move RFIFOL(fd,4) quantity of item idx RFIFOW(fd,2) from storage to cart *------------------------------------------*/ void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd) { @@ -10640,7 +10721,7 @@ void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd) } /*========================================== - * カプラ倉庫を閉じる + * Client tells server to close the kafra *------------------------------------------*/ void clif_parse_CloseKafra(int fd, struct map_session_data *sd) { @@ -10771,7 +10852,7 @@ void clif_parse_ReplyPartyInvite2(int fd,struct map_session_data *sd) } /*========================================== - * パーティ脱退要求 + * Client tells server to remove itself from it's party *------------------------------------------*/ void clif_parse_LeaveParty(int fd, struct map_session_data *sd) { @@ -10784,7 +10865,7 @@ void clif_parse_LeaveParty(int fd, struct map_session_data *sd) } /*========================================== - * パーティ除名要求 + * Client tells server to remove player account id RFIFOL(fd,2) with char name RFIFOP(fd,6) from his party *------------------------------------------*/ void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) { @@ -10797,7 +10878,11 @@ void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) } /*========================================== - * パーティ設定変更要求 + * Client tells server to change it's party configuration + * - clients before 20090603 + * -- It only may toggle exp sharing + * - 20090603 or newer + * -- It may toggle exp (RFIFOW(fd,2) and item sharing options (RFIFOB(fd,6) and RFIFOB(fd,7)) *------------------------------------------*/ void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) { @@ -11251,7 +11336,7 @@ void clif_parse_GuildChangeNotice(int fd, struct map_session_data* sd) } /*========================================== - * ギルド勧誘 + * Client tells server to invite account id RFIFOL(fd,2) to his guild *------------------------------------------*/ void clif_parse_GuildInvite(int fd,struct map_session_data *sd) { @@ -11275,7 +11360,8 @@ void clif_parse_GuildInvite(int fd,struct map_session_data *sd) } /*========================================== - * ギルド勧誘返信 + * Client tells server his reply on the request from guild ID RFIFOL(fd,2), + * - based on RFIFOB(fd,6) which is either 1 (accept) or 0 (reject) *------------------------------------------*/ void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd) { @@ -11283,7 +11369,7 @@ void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd) } /*========================================== - * ギルド脱退 + * Client tells server he wants to leave his current guild *------------------------------------------*/ void clif_parse_GuildLeave(int fd,struct map_session_data *sd) { @@ -11351,7 +11437,7 @@ void clif_parse_GuildMessage(int fd, struct map_session_data* sd) } /*========================================== - * ギルド同盟要求 + * Client tells server he'd like to send a alliance request to account id RFIFOL(fd,2) *------------------------------------------*/ void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd) { @@ -11378,7 +11464,8 @@ void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd) } /*========================================== - * ギルド同盟要求返信 + * Client tells server his response to the alliance request from, + * Guild ID RFIFOL(fd,2) based on RFIFOL(fd,6) which is 1 (accepted) or 0 (rejected) *------------------------------------------*/ void clif_parse_GuildReplyAlliance(int fd, struct map_session_data *sd) { @@ -11386,7 +11473,8 @@ void clif_parse_GuildReplyAlliance(int fd, struct map_session_data *sd) } /*========================================== - * ギルド関係解消 + * Client tells server he'd like to delete alliance from guild ID RFIFOL(fd,2), + * RFIFOL(fd,6) returns a 1 or 0 flag but apparently it is no longer used *------------------------------------------*/ void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd) { @@ -11402,7 +11490,7 @@ void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd) } /*========================================== - * ギルド敵対 + * Client tells server he'd like his guild to be set antagonist of account id RFIFOL(fd,2)'s guild *------------------------------------------*/ void clif_parse_GuildOpposition(int fd, struct map_session_data *sd) { @@ -11429,7 +11517,7 @@ void clif_parse_GuildOpposition(int fd, struct map_session_data *sd) } /*========================================== - * ギルド解散 + * Client tells server he'd like to break (delete) his own guild *------------------------------------------*/ void clif_parse_GuildBreak(int fd, struct map_session_data *sd) { @@ -11476,7 +11564,7 @@ void clif_parse_ChangePetName(int fd, struct map_session_data *sd) } /*========================================== - * /kill <???> + * /kill <account_id> * (or right click menu for GM "(name) force to quit") * S 00cc <id>.L *------------------------------------------*/ @@ -11997,7 +12085,7 @@ void clif_parse_PMIgnoreAll(int fd, struct map_session_data *sd) } /*========================================== - * Wis拒否リスト + * Client tells server he'd like the server to list him his ignore list *------------------------------------------*/ void clif_parse_PMIgnoreList(int fd,struct map_session_data *sd) { @@ -12015,7 +12103,7 @@ void clif_parse_PMIgnoreList(int fd,struct map_session_data *sd) } /*========================================== - * スパノビの/doridoriによるSPR2倍 + * Client tells server he did a /doridori *------------------------------------------*/ void clif_parse_NoviceDoriDori(int fd, struct map_session_data *sd) { @@ -13214,7 +13302,6 @@ void clif_parse_Auction_setitem(int fd, struct map_session_data *sd) clif_Auction_setitem(fd, idx + 2, false); } - /// Result from an auction action (ZC_AUCTION_RESULT) /// 0250 <result>.B /// result: @@ -14329,7 +14416,7 @@ void clif_displayexp(struct map_session_data *sd, unsigned int exp, char type, b /// 0: Displays 'value' for 5 seconds. /// 1: Incremental counter (1 tick/second), negated 'value' specifies start value (e.g. using -10 lets the counter start at 10). /// 2: Decremental counter (1 tick/second), negated 'value' specifies start value (does not stop when reaching 0, but overflows). -/// 3: Decremental counter (2 ticks/second), 'value' specifies start value (stops when reaching 0, displays at most 2 digits). +/// 3: Decremental counter (1 tick/second), 'value' specifies start value (stops when reaching 0, displays at most 2 digits). /// value: /// Except for type 3 it is interpreted as seconds for displaying as DD:HH:MM:SS, HH:MM:SS, MM:SS or SS (leftmost '00' is not displayed). void clif_showdigit(struct map_session_data* sd, unsigned char type, int value) @@ -14886,6 +14973,183 @@ void clif_parse_debug(int fd,struct map_session_data *sd) ShowDump(RFIFOP(fd,0), packet_len); } +/** + * Rune Knight + **/ +void clif_millenniumshield(struct map_session_data *sd, short shields ) { +#if PACKETVER >= 20081217 + unsigned char buf[10]; + + WBUFW(buf,0) = 0x440; + WBUFL(buf,2) = sd->bl.id; + WBUFW(buf,6) = shields; + WBUFW(buf,8) = 0; + clif_send(buf,packet_len(0x440),&sd->bl,AREA); +#endif +} +/** + * Warlock + **/ +/*========================================== + * Spellbook list [LimitLine/3CeAM] + *------------------------------------------*/ +int clif_spellbook_list(struct map_session_data *sd) +{ + int i, c; + int fd; + + nullpo_ret(sd); + + fd = sd->fd; + WFIFOHEAD(fd, 8 * 8 + 8); + WFIFOW(fd,0) = 0x1ad; + + for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) + { + if( itemdb_is_spellbook(sd->status.inventory[i].nameid) ) + { + WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid; + c ++; + } + } + + if( c > 0 ) + { + WFIFOW(fd,2) = c * 2 + 4; + WFIFOSET(fd, WFIFOW(fd, 2)); + sd->menuskill_id = WL_READING_SB; + sd->menuskill_val = c; + } + else + status_change_end(&sd->bl,SC_STOP,-1); + + return 1; +} +/** + * Mechanic + **/ +/*========================================== + * Magic Decoy Material List + *------------------------------------------*/ +int clif_magicdecoy_list(struct map_session_data *sd, int skill_lv, short x, short y) { + int i, c; + int fd; + + nullpo_ret(sd); + + fd = sd->fd; + WFIFOHEAD(fd, 8 * 8 + 8); + WFIFOW(fd,0) = 0x1ad; // This is the official packet. [pakpil] + + for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) { + if( itemdb_is_element(sd->status.inventory[i].nameid) ) { + WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid; + c ++; + } + } + if( c > 0 ) { + sd->menuskill_id = NC_MAGICDECOY; + sd->menuskill_val = skill_lv; + sd->sc.comet_x = x; + sd->sc.comet_y = y; + WFIFOW(fd,2) = c * 2 + 4; + WFIFOSET(fd, WFIFOW(fd, 2)); + } else { + clif_skill_fail(sd,NC_MAGICDECOY,0,0); + return 0; + } + + return 1; +} +/** + * Guilotine Cross + **/ +/*========================================== + * Guillotine Cross Poisons List + *------------------------------------------*/ +int clif_poison_list(struct map_session_data *sd, int skill_lv) { + int i, c; + int fd; + + nullpo_ret(sd); + + fd = sd->fd; + WFIFOHEAD(fd, 8 * 8 + 8); + WFIFOW(fd,0) = 0x1ad; // This is the official packet. [pakpil] + + for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) { + if( itemdb_is_poison(sd->status.inventory[i].nameid) ) { + WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid; + c ++; + } + } + if( c > 0 ) { + sd->menuskill_id = GC_POISONINGWEAPON; + sd->menuskill_val = skill_lv; + WFIFOW(fd,2) = c * 2 + 4; + WFIFOSET(fd, WFIFOW(fd, 2)); + } else { + clif_skill_fail(sd,GC_POISONINGWEAPON,0x2b,0); + return 0; + } + + return 1; +} +/** + * Sends a new status without a tick (currently used by the new mounts) + **/ +int clif_status_load_notick(struct block_list *bl,int type,int flag,int val1, int val2, int val3) { + unsigned char buf[32]; + + nullpo_ret(bl); + + WBUFW(buf,0)=0x043f; + WBUFW(buf,2)=type; + WBUFL(buf,4)=bl->id; + WBUFB(buf,8)=flag; + WBUFL(buf,9) = 0; + WBUFL(buf,13) = val1; + WBUFL(buf,17) = val2; + WBUFL(buf,21) = val3; + + clif_send(buf,packet_len(WBUFW(buf,0)),bl,AREA); + return 0; +} +//Notifies FD of ID's type +int clif_status_load_single(int fd, int id,int type,int flag,int val1, int val2, int val3) { + WFIFOHEAD(fd, packet_len(0x043f)); + WFIFOW(fd,0)=0x043f; + WFIFOW(fd,2)=type; + WFIFOL(fd,4)=id; + WFIFOB(fd,8)=flag; + WFIFOL(fd,9) = 0; + WFIFOL(fd,13) = val1; + WFIFOL(fd,17) = val2; + WFIFOL(fd,21) = val3; + WFIFOSET(fd, packet_len(0x043f)); + return 0; +} +// msgstringtable.txt +// 0x291 <line>.W +void clif_msgtable(int fd, int line) { + WFIFOHEAD(fd, packet_len(0x291)); + WFIFOW(fd, 0) = 0x291; + WFIFOW(fd, 2) = line; + WFIFOSET(fd, packet_len(0x291)); +} + +// msgstringtable.txt +// 0x7e2 <line>.W <value>.L +void clif_msgtable_num(int fd, int line, int num) { +#if PACKETVER >= 20090805 + WFIFOHEAD(fd, packet_len(0x7e2)); + WFIFOW(fd, 0) = 0x7e2; + WFIFOW(fd, 2) = line; + WFIFOL(fd, 4) = num; + WFIFOSET(fd, packet_len(0x7e2)); +#endif +} + /*========================================== * Main client packet processing function *------------------------------------------*/ @@ -15055,7 +15319,7 @@ int clif_parse(int fd) } /*========================================== - * パケットデータベース読み込み + * Reads packet_db.txt and setups its array reference *------------------------------------------*/ static int packetdb_readdb(void) { diff --git a/src/map/clif.h b/src/map/clif.h index 783c8f4a1..8b6271075 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -343,7 +343,7 @@ int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst); void clif_skill_warppoint(struct map_session_data* sd, short skill_num, short skill_lv, unsigned short map1, unsigned short map2, unsigned short map3, unsigned short map4); void clif_skill_memomessage(struct map_session_data* sd, int type); void clif_skill_teleportmessage(struct map_session_data* sd, int type); -int clif_skill_produce_mix_list(struct map_session_data *sd, int trigger); +int clif_skill_produce_mix_list(struct map_session_data *sd, int skillid, int trigger); void clif_cooking_list(struct map_session_data *sd, int trigger); int clif_produceeffect(struct map_session_data* sd,int flag,int nameid); @@ -361,7 +361,7 @@ void clif_bladestop(struct block_list* src, int dst_id, int active); void clif_changemapcell(int fd, int m, int x, int y, int type, enum send_target target); int clif_status_load(struct block_list *bl,int type, int flag); -int clif_status_change(struct block_list *bl,int type,int flag,unsigned int tick); +int clif_status_change(struct block_list *bl,int type,int flag,unsigned int tick,int val1, int val2, int val3); int clif_wis_message(int fd, const char* nick, const char* mes, int mes_len); int clif_wis_end(int fd,int flag); @@ -629,5 +629,29 @@ void clif_search_store_info_ack(struct map_session_data* sd); void clif_search_store_info_failed(struct map_session_data* sd, unsigned char reason); void clif_open_search_store_info(struct map_session_data* sd); void clif_search_store_info_click_ack(struct map_session_data* sd, short x, short y); - +/** + * 3CeAM + **/ +void clif_msgtable(int fd, int line); +void clif_msgtable_num(int fd, int line, int num); +/** + * Rune Knight + **/ +void clif_millenniumshield(struct map_session_data *sd, short shields ); +/** + * Warlock + **/ +int clif_spellbook_list(struct map_session_data *sd); +/** + * Mechanic + **/ +int clif_magicdecoy_list(struct map_session_data *sd, int skill_lv, short x, short y); +/** + * Guilotine Cross + **/ +int clif_poison_list(struct map_session_data *sd, int skill_lv); +/** + * [RRInd] for the new mounts + **/ +int clif_status_load_notick(struct block_list *bl,int type,int flag,int val1, int val2, int val3); #endif /* _CLIF_H_ */ diff --git a/src/map/itemdb.c b/src/map/itemdb.c index d8a8bb2cc..00d4176c5 100644 --- a/src/map/itemdb.c +++ b/src/map/itemdb.c @@ -706,7 +706,35 @@ static int itemdb_gendercheck(struct item_data *id) return (battle_config.ignore_items_gender) ? 2 : id->sex; } - +/** + * [RRInd] + * For backwards compatibility, in Renewal mode, MATK from weapons comes from the atk slot + * We use a ':' delimiter which, if not found, assumes the weapon does not provide any matk. + **/ +void itemdb_rr_split_atoi(char *str, int *atk, int *matk) { + int i, val[2]; + + for (i=0; i<2; i++) { + if (!str) break; + val[i] = atoi(str); + str = strchr(str,':'); + if (str) + *str++=0; + } + if( i == 0 ) { + *atk = *matk = 0; + return;//no data found + } + if( i == 1 ) {//Single Value, we assume it's the ATK + *atk = val[0]; + *matk = 0; + return; + } + //We assume we have 2 values. + *atk = val[0]; + *matk = val[1]; + return; +} /*========================================== * processes one itemdb entry *------------------------------------------*/ @@ -736,7 +764,7 @@ static bool itemdb_parse_dbrow(char** str, const char* source, int line, int scr id->type = atoi(str[3]); - if( id->type < 0 || id->type == IT_UNKNOWN || id->type == IT_UNKNOWN2 || ( id->type > IT_DELAYCONSUME && id->type < IT_CASH ) || id->type >= IT_MAX ) + if( id->type < 0 || id->type == IT_UNKNOWN || id->type == IT_UNKNOWN2 || ( id->type > IT_THROWWEAPON && id->type < IT_CASH ) || id->type >= IT_MAX ) {// catch invalid item types ShowWarning("itemdb_parse_dbrow: Invalid item type %d for item %d. IT_ETC will be used.\n", id->type, nameid); id->type = IT_ETC; @@ -773,7 +801,11 @@ static bool itemdb_parse_dbrow(char** str, const char* source, int line, int scr id->value_buy, id->value_sell, nameid, id->jname); id->weight = atoi(str[6]); +#if RRMODE + itemdb_rr_split_atoi(str[7],&id->atk,&id->matk); +#else id->atk = atoi(str[7]); +#endif id->def = atoi(str[8]); id->range = atoi(str[9]); id->slot = atoi(str[10]); @@ -835,7 +867,14 @@ static bool itemdb_parse_dbrow(char** str, const char* source, int line, int scr *------------------------------------------*/ static int itemdb_readdb(void) { - const char* filename[] = { "item_db.txt", "item_db2.txt" }; + /** + * ro-resources inheritance: item_db -> item_db_re -> item_db2 (user customs) + **/ +#if RRMODE + const char* filename[] = { "item_db.txt","item_db_re.txt","item_db2.txt" }; +#else + const char* filename[] = { "item_db.txt","item_db2.txt" }; +#endif int fi; for( fi = 0; fi < ARRAYLENGTH(filename); ++fi ) @@ -947,7 +986,11 @@ static int itemdb_readdb(void) *======================================*/ static int itemdb_read_sqldb(void) { +#if RRMODE + const char* item_db_name[] = { item_db_db, item_db_re_db, item_db2_db }; +#else const char* item_db_name[] = { item_db_db, item_db2_db }; +#endif int fi; for( fi = 0; fi < ARRAYLENGTH(item_db_name); ++fi ) diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 5b54acd67..801b81be8 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -5,6 +5,7 @@ #define _ITEMDB_H_ #include "../common/mmo.h" // ITEM_NAME_LENGTH +#include "map.h" //RRMODE #define MAX_RANDITEM 11000 @@ -13,6 +14,11 @@ #define MAX_SEARCH 5 //Designed for search functions, species max number of matches to display. +/** + * Arch Bishop + **/ +#define ITEMID_ANCILLA 12333 + #define ITEMID_YELLOW_GEMSTONE 715 #define ITEMID_RED_GEMSTONE 716 #define ITEMID_BLUE_GEMSTONE 717 @@ -50,6 +56,9 @@ struct item_data { int equip; int weight; int atk; +#if RRMODE + int matk;//[RRInd] -- used in RE for matk +#endif int def; int range; int slot; @@ -61,7 +70,7 @@ struct item_data { //Lupus: I rearranged order of these fields due to compatibility with ITEMINFO script command // some script commands should be revised as well... unsigned int class_base[3]; //Specifies if the base can wear this item (split in 3 indexes per type: 1-1, 2-1, 2-2) - unsigned class_upper : 3; //Specifies if the upper-type can equip it (bitfield, 1: normal, 2: upper, 3: baby) + unsigned class_upper : 4; //Specifies if the upper-type can equip it (bitfield, 1: normal, 2: upper, 3: baby,4:third) struct { unsigned short chance; int id; @@ -143,4 +152,35 @@ void itemdb_reload(void); void do_final_itemdb(void); int do_init_itemdb(void); +/** + * Rune Knight + **/ +enum { + ITEMID_NAUTHIZ = 12725, + ITEMID_RAIDO, + ITEMID_BERKANA, + ITEMID_ISA, + ITEMID_OTHILA, + ITEMID_URUZ, + ITEMID_THURISAZ, + ITEMID_WYRD, + ITEMID_HAGALAZ, +} rune_list; +#define itemdb_is_rune(n) (n >= ITEMID_NAUTHIZ && n <= ITEMID_HAGALAZ) +/** + * Warlock + **/ +#define itemdb_is_spellbook(n) (n >= 6188 && n <= 6205) +/** + * Ranger + **/ +#define ITEMID_TRAP_ALLOY 7940 +/** + * Mechanic + **/ +#define itemdb_is_element(n) (n >= 990 && n <= 993) +/** + * Guilotine Cross + **/ +#define itemdb_is_poison(n) (n >= 12717 && n <= 12724) #endif /* _ITEMDB_H_ */ diff --git a/src/map/map.c b/src/map/map.c index 39077de6c..9155a11f3 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -68,6 +68,7 @@ Sql* mmysql_handle; int db_use_sqldbs = 0; char item_db_db[32] = "item_db"; char item_db2_db[32] = "item_db2"; +char item_db_re_db[32] = "item_db_re"; char mob_db_db[32] = "mob_db"; char mob_db2_db[32] = "mob_db2"; @@ -3205,6 +3206,9 @@ int map_config_read(char *cfgName) if (strcmpi(w1, "import") == 0) map_config_read(w2); else + if (strcmpi(w1, "console_msg_log") == 0) + console_msg_log = atoi(w2);//[Ind] + else ShowWarning("Unknown setting '%s' in file %s\n", w1, cfgName); } @@ -3526,41 +3530,39 @@ void do_abort(void) /*====================================================== * Map-Server Version Screen [MC Cameri] *------------------------------------------------------*/ -void map_helpscreen(int flag) -{ - puts("Usage: map-server [options]"); - puts("Options:"); - puts(CL_WHITE" Commands\t\t\tDescription"CL_RESET); - puts("-----------------------------------------------------------------------------"); - puts(" --help, --h, --?, /? Displays this help screen"); - puts(" --map-config <file> Load map-server configuration from <file>"); - puts(" --battle-config <file> Load battle configuration from <file>"); - puts(" --atcommand-config <file> Load atcommand configuration from <file>"); - puts(" --script-config <file> Load script configuration from <file>"); - puts(" --msg-config <file> Load message configuration from <file>"); - puts(" --grf-path-file <file> Load grf path file configuration from <file>"); - puts(" --sql-config <file> Load inter-server configuration from <file>"); - puts(" (SQL Only)"); - puts(" --log-config <file> Load logging configuration from <file>"); - puts(" (SQL Only)"); - puts(" --version, --v, -v, /v Displays the server's version"); - puts("\n"); - if (flag) exit(EXIT_FAILURE); +static void map_helpscreen(bool do_exit) +{ + ShowInfo("Usage: %s [options]\n", SERVER_NAME); + ShowInfo("\n"); + ShowInfo("Options:\n"); + ShowInfo(" -?, -h [--help]\t\tDisplays this help screen.\n"); + ShowInfo(" -v [--version]\t\tDisplays the server's version.\n"); + ShowInfo(" --run-once\t\t\tCloses server after loading (testing).\n"); + ShowInfo(" --map-config <file>\t\tAlternative map-server configuration.\n"); + ShowInfo(" --battle-config <file>\tAlternative battle configuration.\n"); + ShowInfo(" --atcommand-config <file>\tAlternative atcommand configuration.\n"); + ShowInfo(" --script-config <file>\tAlternative script configuration.\n"); + ShowInfo(" --msg-config <file>\t\tAlternative message configuration.\n"); + ShowInfo(" --grf-path <file>\t\tAlternative GRF path configuration.\n"); + ShowInfo(" --inter-config <file>\t\tAlternative inter-server configuration.\n"); + ShowInfo(" --log-config <file>\t\tAlternative logging configuration.\n"); + if( do_exit ) + exit(EXIT_SUCCESS); } /*====================================================== * Map-Server Version Screen [MC Cameri] *------------------------------------------------------*/ -void map_versionscreen(int flag) +static void map_versionscreen(bool do_exit) { - ShowInfo(CL_WHITE "eAthena version %d.%02d.%02d, Athena Mod version %d" CL_RESET"\n", - ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION, - ATHENA_MOD_VERSION); - ShowInfo(CL_GREEN "Website/Forum:" CL_RESET "\thttp://eathena.deltaanime.net/\n"); - ShowInfo(CL_GREEN "IRC Channel:" CL_RESET "\tirc://irc.deltaanime.net/#athena\n"); - ShowInfo("\nOpen " CL_WHITE "readme.html" CL_RESET " for more information."); - if (ATHENA_RELEASE_FLAG) ShowNotice("This version is not for release.\n"); - if (flag) exit(EXIT_FAILURE); + ShowInfo(CL_WHITE"eAthena version %d.%02d.%02d, Athena Mod version %d" CL_RESET"\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION, ATHENA_MOD_VERSION); + ShowInfo(CL_GREEN"Website/Forum:"CL_RESET"\thttp://eathena.ws/\n"); + ShowInfo(CL_GREEN"IRC Channel:"CL_RESET"\tirc://irc.deltaanime.net/#athena\n"); + ShowInfo("Open "CL_WHITE"readme.html"CL_RESET" for more information.\n"); + if(ATHENA_RELEASE_FLAG) + ShowNotice("This version is not for release.\n"); + if( do_exit ) + exit(EXIT_SUCCESS); } /*====================================================== @@ -3591,6 +3593,16 @@ void do_shutdown(void) } } +static bool map_arg_next_value(const char* option, int i, int argc) +{ + if( i >= argc-1 ) + { + ShowWarning("Missing value for option '%s'.\n", option); + return false; + } + + return true; +} int do_init(int argc, char *argv[]) { @@ -3611,31 +3623,67 @@ int do_init(int argc, char *argv[]) srand(gettick()); - for (i = 1; i < argc ; i++) { - if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "--h") == 0 || strcmp(argv[i], "--?") == 0 || strcmp(argv[i], "/?") == 0) - map_helpscreen(1); - else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "--v") == 0 || strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "/v") == 0) - map_versionscreen(1); - else if (strcmp(argv[i], "--map_config") == 0 || strcmp(argv[i], "--map-config") == 0) - MAP_CONF_NAME=argv[i+1]; - else if (strcmp(argv[i],"--battle_config") == 0 || strcmp(argv[i],"--battle-config") == 0) - BATTLE_CONF_FILENAME = argv[i+1]; - else if (strcmp(argv[i],"--atcommand_config") == 0 || strcmp(argv[i],"--atcommand-config") == 0) - ATCOMMAND_CONF_FILENAME = argv[i+1]; - else if (strcmp(argv[i],"--script_config") == 0 || strcmp(argv[i],"--script-config") == 0) - SCRIPT_CONF_NAME = argv[i+1]; - else if (strcmp(argv[i],"--msg_config") == 0 || strcmp(argv[i],"--msg-config") == 0) - MSG_CONF_NAME = argv[i+1]; - else if (strcmp(argv[i],"--grf_path_file") == 0 || strcmp(argv[i],"--grf-path-file") == 0) - GRF_PATH_FILENAME = argv[i+1]; -#ifndef TXT_ONLY - else if (strcmp(argv[i],"--inter_config") == 0 || strcmp(argv[i],"--inter-config") == 0) - INTER_CONF_NAME = argv[i+1]; -#endif - else if (strcmp(argv[i],"--log_config") == 0 || strcmp(argv[i],"--log-config") == 0) - LOG_CONF_NAME = argv[i+1]; - else if (strcmp(argv[i],"--run_once") == 0) // close the map-server as soon as its done.. for testing [Celest] - runflag = 0; + for( i = 1; i < argc ; i++ ) + { + const char* arg = argv[i]; + + if( arg[0] != '-' && ( arg[0] != '/' || arg[1] == '-' ) ) + {// -, -- and / + ShowError("Unknown option '%s'.\n", argv[i]); + exit(EXIT_FAILURE); + } + else if( (++arg)[0] == '-' ) + {// long option + arg++; + + if( strcmp(arg, "help") == 0 ) + map_helpscreen(true); + else if( strcmp(arg, "version") == 0 ) + map_versionscreen(true); + else if( strcmp(arg, "map-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + MAP_CONF_NAME = argv[++i]; + } else if( strcmp(arg, "battle-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + BATTLE_CONF_FILENAME = argv[++i]; + } else if( strcmp(arg, "atcommand-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + ATCOMMAND_CONF_FILENAME = argv[++i]; + } else if( strcmp(arg, "script-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + SCRIPT_CONF_NAME = argv[++i]; + } else if( strcmp(arg, "msg-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + MSG_CONF_NAME = argv[++i]; + } else if( strcmp(arg, "grf-path-file") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + GRF_PATH_FILENAME = argv[++i]; + } else if( strcmp(arg, "inter-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + INTER_CONF_NAME = argv[++i]; + } else if( strcmp(arg, "log-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + LOG_CONF_NAME = argv[++i]; + } else if( strcmp(arg, "run-once") == 0 ) // close the map-server as soon as its done.. for testing [Celest] + runflag = CORE_ST_STOP; + else { + ShowError("Unknown option '%s'.\n", argv[i]); + exit(EXIT_FAILURE); + } + } + else switch( arg[0] ) + {// short option + case '?': + case 'h': + map_helpscreen(true); + break; + case 'v': + map_versionscreen(true); + break; + default: + ShowError("Unknown option '%s'.\n", argv[i]); + exit(EXIT_FAILURE); + } } map_config_read(MAP_CONF_NAME); diff --git a/src/map/map.h b/src/map/map.h index 8e4507599..18f5f3928 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -10,6 +10,11 @@ #include "../common/mapindex.h" #include "../common/db.h" +/** + * [ro-resources.net] + **/ +#include "./RRConfig/Core.h" + #include <stdarg.h> struct npc_data; @@ -46,7 +51,7 @@ enum E_MAPSERVER_ST #define NATURAL_HEAL_INTERVAL 500 #define MIN_FLOORITEM 2 #define MAX_FLOORITEM START_ACCOUNT_NUM -#define MAX_LEVEL 99 +#define MAX_LEVEL 150 #define MAX_DROP_PER_MAP 48 #define MAX_IGNORE_LIST 20 // official is 14 #define MAX_VENDING 12 @@ -68,10 +73,12 @@ enum E_MAPSERVER_ST #define JOBL_UPPER 0x1000 //4096 #define JOBL_BABY 0x2000 //8192 +#define JOBL_THIRD 0x4000 //16384 //for filtering and quick checking. #define MAPID_UPPERMASK 0x0fff #define MAPID_BASEMASK 0x00ff +#define MAPID_THIRDMASK (JOBL_THIRD|MAPID_UPPERMASK) //First Jobs //Note the oddity of the novice: //Super Novices are considered the 2-1 version of the novice! Novices are considered a first class type, too... @@ -154,6 +161,31 @@ enum { MAPID_BABY_ALCHEMIST, MAPID_BABY_ROGUE, MAPID_BABY_SOUL_LINKER, + MAPID_RUNE_KNIGHT = JOBL_THIRD|JOBL_2_1|0x1, + MAPID_WARLOCK, + MAPID_RANGER, + MAPID_ARCH_BISHOP, + MAPID_MECHANIC, + MAPID_GUILLOTINE_CROSS, + MAPID_ROYAL_GUARD = JOBL_THIRD|JOBL_2_2|0x1, + MAPID_SORCERER, + MAPID_MINSTRELWANDERER, + MAPID_SURA, + MAPID_GENETIC, + MAPID_SHADOW_CHASER, + MAPID_RUNE_KNIGHT_T = JOBL_THIRD|JOBL_UPPER|JOBL_2_1|0x1, + MAPID_WARLOCK_T, + MAPID_RANGER_T, + MAPID_ARCH_BISHOP_T, + MAPID_MECHANIC_T, + MAPID_GUILLOTINE_CROSS_T, + MAPID_ROYAL_GUARD_T = JOBL_THIRD|JOBL_UPPER|JOBL_2_2|0x1, + MAPID_SORCERER_T, + MAPID_MINSTRELWANDERER_T, + MAPID_SURA_T, + MAPID_GENETIC_T, + MAPID_SHADOW_CHASER_T, + }; //Max size for inputs to Graffiti, Talkie Box and Vending text prompts @@ -476,7 +508,10 @@ struct map_data { unsigned fireworks : 1; unsigned sakura : 1; // [Valaris] unsigned leaves : 1; // [Valaris] - unsigned rain : 1; // [Valaris] + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //unsigned rain : 1; // [Valaris] unsigned nogo : 1; // [Valaris] unsigned nobaseexp : 1; // [Lorky] added by Lupus unsigned nojobexp : 1; // [Lorky] @@ -643,7 +678,6 @@ int map_random_dir(struct block_list *bl, short *x, short *y); // [Skotlex] int cleanup_sub(struct block_list *bl, va_list ap); -void map_helpscreen(int flag); // [Valaris] int map_delmap(char* mapname); void map_flags_init(void); @@ -667,8 +701,6 @@ extern char *SCRIPT_CONF_NAME; extern char *MSG_CONF_NAME; extern char *GRF_PATH_FILENAME; -extern char *map_server_dns; - //Useful typedefs from jA [Skotlex] typedef struct map_session_data TBL_PC; typedef struct npc_data TBL_NPC; @@ -697,6 +729,7 @@ extern Sql* logmysql_handle; extern char item_db_db[32]; extern char item_db2_db[32]; +extern char item_db_re_db[32]; extern char mob_db_db[32]; extern char mob_db2_db[32]; diff --git a/src/map/mob.c b/src/map/mob.c index 5272b17e8..8181fb5e7 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -2163,8 +2163,12 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) merc_hom_gainexp(tmpsd[i]->hd, base_exp); if(base_exp || job_exp) { - if( md->dmglog[i].flag != MDLF_PET || battle_config.pet_attack_exp_to_master ) + if( md->dmglog[i].flag != MDLF_PET || battle_config.pet_attack_exp_to_master ) { +#if RRMODE + party_renewal_exp_mod(&base_exp,&job_exp,tmpsd[i]->status.base_level,md->level); +#endif pc_gainexp(tmpsd[i], &md->bl, base_exp, job_exp, false); + } } if(zeny) // zeny from mobs [Valaris] pc_getzeny(tmpsd[i], zeny); @@ -2185,6 +2189,11 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) struct item_drop_list *dlist = ers_alloc(item_drop_list_ers, struct item_drop_list); struct item_drop *ditem; int drop_rate; +#if RRMODE + int drop_modifier = mvp_sd ? party_renewal_drop_mod(mvp_sd->status.base_level - md->level) : + second_sd ? party_renewal_drop_mod(second_sd->status.base_level - md->level) : + third_sd ? party_renewal_drop_mod(third_sd->status.base_level - md->level) : 100; +#endif dlist->m = md->bl.m; dlist->x = md->bl.x; dlist->y = md->bl.y; @@ -2226,7 +2235,10 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) // Increase drop rate if user has SC_ITEMBOOST if (sd && sd->sc.data[SC_ITEMBOOST]) // now rig the drop rate to never be over 90% unless it is originally >90%. drop_rate = max(drop_rate,cap_value((int)(0.5+drop_rate*(sd->sc.data[SC_ITEMBOOST]->val1)/100.),0,9000)); - +#if RRMODE + if( drop_modifier != 100 ) + drop_rate = drop_rate * drop_modifier / 100; +#endif // attempt to drop the item if (rand() % 10000 >= drop_rate) continue; @@ -2437,8 +2449,11 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) delete_timer(md->deletetimer,mob_timer_delete); md->deletetimer = INVALID_TIMER; } - - mob_deleteslave(md); + /** + * Only loops if necessary (e.g. a poring would never need to loop) + **/ + if( md->can_summon ) + mob_deleteslave(md); map_freeblock_unlock(); @@ -2700,6 +2715,10 @@ int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id) if(mobdb_checkid(value[0]) == 0) return 0; + /** + * Flags this monster is able to summon; saves a worth amount of memory upon deletion + **/ + md2->can_summon = 1; while(count < 5 && mobdb_checkid(value[count])) count++; if(count < 1) return 0; diff --git a/src/map/mob.h b/src/map/mob.h index 9e86b8d63..2c6d882c9 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -161,6 +161,11 @@ struct mob_data { short skillidx; unsigned int skilldelay[MAX_MOBSKILL]; char npc_event[EVENT_NAME_LENGTH]; + /** + * Did this monster summon something? + * Used to flag summon deletions, saves a worth amount of memory + **/ + bool can_summon : 1; }; diff --git a/src/map/npc.c b/src/map/npc.c index dae395876..1762bc73b 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -199,7 +199,33 @@ struct npc_data* npc_name2id(const char* name) { return (struct npc_data *) strdb_get(npcname_db, name); } - +/** + * For the Secure NPC Timeout option (check RRConfig/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT +/** + * Timer to check for idle time and timeout the dialog if necessary + **/ +int npc_rr_secure_timeout_timer(int tid, unsigned int tick, int id, intptr_t data) { + struct map_session_data* sd = NULL; + if( (sd = map_id2sd(id)) == NULL || !sd->npc_id ) + return 0;//Not logged in anymore OR no longer attached to a npc + + if( DIFF_TICK(tick,sd->npc_idle_tick) > (SECURE_NPCTIMEOUT*1000) ) { + /** + * If we still have the NPC script attached, tell it to stop. + **/ + if( sd->st ) + sd->st->state = END; + /** + * This guy's been idle for longer than allowed, close him. + **/ + clif_scriptclose(sd,sd->npc_id); + } else //Create a new instance of ourselves to continue + sd->npc_idle_timer = add_timer(gettick() + (SECURE_NPCTIMEOUT_INTERVAL*1000),npc_rr_secure_timeout_timer,sd->bl.id,0); + return 0; +} +#endif /*========================================== * イベントキューのイベント処理 *------------------------------------------*/ @@ -1114,6 +1140,15 @@ int npc_scriptcont(struct map_session_data* sd, int id) return 1; } } + /** + * For the Secure NPC Timeout option (check RRConfig/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT + /** + * Update the last NPC iteration + **/ + sd->npc_idle_tick = gettick(); +#endif run_script_main(sd->st); return 0; @@ -3058,8 +3093,11 @@ static const char* npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4, con map[m].flag.sakura=state; else if (!strcmpi(w3,"leaves")) map[m].flag.leaves=state; - else if (!strcmpi(w3,"rain")) - map[m].flag.rain=state; + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //else if (!strcmpi(w3,"rain")) + // map[m].flag.rain=state; else if (!strcmpi(w3,"nightenabled")) map[m].flag.nightenabled=state; else if (!strcmpi(w3,"nogo")) diff --git a/src/map/npc.h b/src/map/npc.h index d40fb63b4..8f8d7eca9 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -157,4 +157,11 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po extern struct npc_data* fake_nd; +/** + * For the Secure NPC Timeout option (check RRConfig/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT +int npc_rr_secure_timeout_timer(int tid, unsigned int tick, int id, intptr_t data); +#endif + #endif /* _NPC_H_ */ diff --git a/src/map/party.c b/src/map/party.c index 9649505b4..f47f8e5c9 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -566,7 +566,7 @@ int party_member_withdraw(int party_id, int account_id, int char_id) ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id ); if( i < MAX_PARTY ) { - clif_party_withdraw(p,sd,account_id,p->party.member[i].name,0x00); + clif_party_withdraw(p,sd,account_id,p->party.member[i].name,0x0); memset(&p->party.member[i], 0, sizeof(p->party.member[0])); memset(&p->data[i], 0, sizeof(p->data[0])); p->party.count--; @@ -912,13 +912,64 @@ int party_send_xy_clear(struct party_data *p) } return 0; } - +#if RRMODE +/** + * Renewal Drop Earning Modifier + **/ +int party_renewal_drop_mod(int diff) { + if( diff >= -10 && diff <= 5 ) + return 100;//no change. + if( diff > 0 ) { + if( diff > 5 && diff < 10 ) + return 90; + if( diff > 9 && diff < 15 ) + return 75; + if( diff > 14 && diff < 30 ) + return 60; + } else { + if( diff <= -10 && diff <= -14 ) + return 75;//75% + } + //other chases: 50% + return 50; +} +/** + * Renewal Experience Earning Mode + **/ +void party_renewal_exp_mod(unsigned int *base_exp, unsigned int *job_exp, int lvl, int moblvl) { + int diff = lvl - moblvl, boost = 0; + //-2 ~ +5: 100% + if( diff >= -2 && diff <= 5 ) + return;//we don't change anything, it's 100% boost + //-3 ~ -10: +5% boost for each + if( diff >= -10 && diff <= -3 ) + boost = 100 + (( -diff * 5 ) - 15 ); + // 40% boost if difference is <= -10 + else if ( diff <= -10 ) + boost = 40; + else { + boost = ( diff > 5 && diff < 11 ) ? 95 : + ( diff > 10 && diff < 16 ) ? 90 : + ( diff > 15 && diff < 21 ) ? 85 : + ( diff > 20 && diff < 26 ) ? 60 : + ( diff > 25 && diff < 31 ) ? 35 : + 10; + } + if( *base_exp ) + *base_exp = (unsigned int)cap_value(*base_exp * boost / 100, 1, UINT_MAX); + if( *job_exp ) + *job_exp = (unsigned int)cap_value(*job_exp * boost / 100, 1, UINT_MAX); + return; +} +#endif // exp share and added zeny share [Valaris] int party_exp_share(struct party_data* p, struct block_list* src, unsigned int base_exp, unsigned int job_exp, int zeny) { struct map_session_data* sd[MAX_PARTY]; unsigned int i, c; - +#if RRMODE + int src_lvl = status_get_lv(src); +#endif nullpo_ret(p); // count the number of players eligible for exp sharing @@ -945,8 +996,10 @@ int party_exp_share(struct party_data* p, struct block_list* src, unsigned int b zeny = (unsigned int) cap_value(zeny * bonus/100, INT_MIN, INT_MAX); } - for (i = 0; i < c; i++) - { + for (i = 0; i < c; i++) { +#if RRMODE + party_renewal_exp_mod(&base_exp,&job_exp,sd[i]->status.base_level,src_lvl); +#endif pc_gainexp(sd[i], src, base_exp, job_exp, false); if (zeny) // zeny from mobs [Valaris] pc_getzeny(sd[i],zeny); diff --git a/src/map/party.h b/src/map/party.h index 9fde5a6a4..4918d9a3a 100644 --- a/src/map/party.h +++ b/src/map/party.h @@ -92,4 +92,9 @@ void party_booking_update(struct map_session_data *sd, short* job); void party_booking_search(struct map_session_data *sd, short level, short mapid, short job, unsigned long lastindex, short resultcount); bool party_booking_delete(struct map_session_data *sd); +#if RRMODE +void party_renewal_exp_mod(unsigned int *base_exp, unsigned int *job_exp, int lvl, int moblvl); +int party_renewal_drop_mod(int diff); +#endif + #endif /* _PARTY_H_ */ diff --git a/src/map/pc.c b/src/map/pc.c index b93334f35..6a6c24398 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -395,7 +395,7 @@ int pc_makesavestatus(struct map_session_data *sd) //Only copy the Cart/Peco/Falcon options, the rest are handled via //status change load/saving. [Skotlex] - sd->status.option = sd->sc.option&(OPTION_CART|OPTION_FALCON|OPTION_RIDING); + sd->status.option = sd->sc.option&(OPTION_CART|OPTION_FALCON|OPTION_RIDING|OPTION_DRAGON|OPTION_WUGRIDER|OPTION_WUG|OPTION_MADOGEAR|OPTION_MOUNTING); if (sd->sc.data[SC_JAILED]) { //When Jailed, do not move last point. @@ -765,10 +765,14 @@ int pc_isequip(struct map_session_data *sd,int n) //Not equipable by class. [Skotlex] if (!(1<<(sd->class_&MAPID_BASEMASK)&item->class_base[(sd->class_&JOBL_2_1)?1:((sd->class_&JOBL_2_2)?2:0)])) return 0; - - //Not equipable by upper class. [Skotlex] - if(!(1<<((sd->class_&JOBL_UPPER)?1:((sd->class_&JOBL_BABY)?2:0))&item->class_upper)) + //Not usable by upper class. [Inkfish] + while( 1 ) { + if( item->class_upper&1 && !(sd->class_&(JOBL_UPPER|JOBL_THIRD|JOBL_BABY)) ) break; + if( item->class_upper&2 && sd->class_&(JOBL_UPPER|JOBL_THIRD) ) break; + if( item->class_upper&4 && sd->class_&JOBL_BABY ) break; + if( item->class_upper&8 && sd->class_&JOBL_THIRD ) break; return 0; + } return 1; } @@ -825,7 +829,17 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->invincible_timer = INVALID_TIMER; sd->npc_timer_id = INVALID_TIMER; sd->pvp_timer = INVALID_TIMER; - + /** + * For the Secure NPC Timeout option (check RRConfig/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT + /** + * Initialize to defaults/expected + **/ + sd->npc_idle_timer = INVALID_TIMER; + sd->npc_idle_tick = tick; +#endif + sd->canuseitem_tick = tick; sd->canusecashfood_tick = tick; sd->canequip_tick = tick; @@ -915,7 +929,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim if (battle_config.display_version == 1){ char buf[256]; - sprintf(buf, "eAthena SVN version: %s", get_svn_revision()); + sprintf(buf, "SVN version: %s", get_svn_revision()); clif_displaymessage(sd->fd, buf); } @@ -3379,7 +3393,15 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount) w = data->weight*amount; if(sd->weight + w > sd->max_weight) return 2; - + if( itemdb_is_rune(item_data->nameid) ) { + int rune = pc_search_inventory(sd,item_data->nameid); + if( ( rune >= 0 && sd->status.inventory[rune].amount + amount > MAX_RUNE ) || + ( rune == -1 && amount > MAX_RUNE ) + ) { + clif_msgtable(sd->fd,0x61b); + return 1; + } + } i = MAX_INVENTORY; if( itemdb_isstackable2(data) && item_data->expire_time == 0 ) @@ -3662,6 +3684,17 @@ int pc_isUseitem(struct map_session_data *sd,int n) if( nameid >= 12153 && nameid <= 12182 && sd->md != NULL ) return 0; // Mercenary Scrolls + /** + * Only Rune Knights may use runes + **/ + if( itemdb_is_rune(nameid) && (sd->class_&MAPID_THIRDMASK) != MAPID_RUNE_KNIGHT ) + return 0; + /** + * Only GCross may use poisons + **/ + else if( itemdb_is_poison(nameid) && (sd->class_&MAPID_THIRDMASK) != MAPID_GUILLOTINE_CROSS ) + return 0; + //added item_noequip.txt items check by Maya&[Lupus] if ( (!map_flag_vs(sd->bl.m) && item->flag.no_equip&1) || // Normal @@ -3685,13 +3718,14 @@ int pc_isUseitem(struct map_session_data *sd,int n) (item->class_base[sd->class_&JOBL_2_1?1:(sd->class_&JOBL_2_2?2:0)]) )) return 0; - - //Not usable by upper class. [Skotlex] - if(!( - (1<<(sd->class_&JOBL_UPPER?1:(sd->class_&JOBL_BABY?2:0))) & - item->class_upper - )) + //Not usable by upper class. [Inkfish] + while( 1 ) { + if( item->class_upper&1 && !(sd->class_&(JOBL_UPPER|JOBL_THIRD|JOBL_BABY)) ) break; + if( item->class_upper&2 && sd->class_&(JOBL_UPPER|JOBL_THIRD) ) break; + if( item->class_upper&4 && sd->class_&JOBL_BABY ) break; + if( item->class_upper&8 && sd->class_&JOBL_THIRD ) break; return 0; + } //Dead Branch & Bloody Branch & Porings Box if((log_config.branch > 0) && (nameid == 604 || nameid == 12103 || nameid == 12109)) @@ -4534,6 +4568,36 @@ int pc_jobid2mapid(unsigned short b_class) case JOB_BABY_MONK: return MAPID_BABY_MONK; case JOB_BABY_ALCHEMIST: return MAPID_BABY_ALCHEMIST; case JOB_BABY_ROGUE: return MAPID_BABY_ROGUE; + //3.1 non-trans + case JOB_RUNE_KNIGHT: return MAPID_RUNE_KNIGHT; + case JOB_WARLOCK: return MAPID_WARLOCK; + case JOB_RANGER: return MAPID_RANGER; + case JOB_ARCH_BISHOP: return MAPID_ARCH_BISHOP; + case JOB_MECHANIC: return MAPID_MECHANIC; + case JOB_GUILLOTINE_CROSS: return MAPID_GUILLOTINE_CROSS; + //3.1 trans + case JOB_RUNE_KNIGHT_T: return MAPID_RUNE_KNIGHT_T; + case JOB_WARLOCK_T: return MAPID_WARLOCK_T; + case JOB_RANGER_T: return MAPID_RANGER_T; + case JOB_ARCH_BISHOP_T: return MAPID_ARCH_BISHOP_T; + case JOB_MECHANIC_T: return MAPID_MECHANIC_T; + case JOB_GUILLOTINE_CROSS_T:return MAPID_GUILLOTINE_CROSS_T; + //3.2 non-trans + case JOB_ROYAL_GUARD: return MAPID_ROYAL_GUARD; + case JOB_SORCERER: return MAPID_SORCERER; + case JOB_MINSTREL: return MAPID_MINSTRELWANDERER; + case JOB_WANDERER: return MAPID_MINSTRELWANDERER; + case JOB_SURA: return MAPID_SURA; + case JOB_GENETIC: return MAPID_GENETIC; + case JOB_SHADOW_CHASER: return MAPID_SHADOW_CHASER; + //3.2 trans + case JOB_ROYAL_GUARD_T: return MAPID_ROYAL_GUARD_T; + case JOB_SORCERER_T: return MAPID_SORCERER_T; + case JOB_MINSTREL_T: return MAPID_MINSTRELWANDERER_T; + case JOB_WANDERER_T: return MAPID_MINSTRELWANDERER_T; + case JOB_SURA_T: return MAPID_SURA_T; + case JOB_GENETIC_T: return MAPID_GENETIC_T; + case JOB_SHADOW_CHASER_T: return MAPID_SHADOW_CHASER_T; default: return -1; } @@ -4620,6 +4684,34 @@ int pc_mapid2jobid(unsigned short class_, int sex) case MAPID_BABY_MONK: return JOB_BABY_MONK; case MAPID_BABY_ALCHEMIST: return JOB_BABY_ALCHEMIST; case MAPID_BABY_ROGUE: return JOB_BABY_ROGUE; + //3.1 non-trans + case MAPID_RUNE_KNIGHT: return JOB_RUNE_KNIGHT; + case MAPID_WARLOCK: return JOB_WARLOCK; + case MAPID_RANGER: return JOB_RANGER; + case MAPID_ARCH_BISHOP: return JOB_ARCH_BISHOP; + case MAPID_MECHANIC: return JOB_MECHANIC; + case MAPID_GUILLOTINE_CROSS:return JOB_GUILLOTINE_CROSS; + //3.1 trans + case MAPID_RUNE_KNIGHT_T: return JOB_RUNE_KNIGHT_T; + case MAPID_WARLOCK_T: return JOB_WARLOCK_T; + case MAPID_RANGER_T: return JOB_RANGER_T; + case MAPID_ARCH_BISHOP_T: return JOB_ARCH_BISHOP_T; + case MAPID_MECHANIC_T: return JOB_MECHANIC_T; + case MAPID_GUILLOTINE_CROSS_T:return JOB_GUILLOTINE_CROSS_T; + //3.2 non-trans + case MAPID_ROYAL_GUARD: return JOB_ROYAL_GUARD; + case MAPID_SORCERER: return JOB_SORCERER; + case MAPID_MINSTRELWANDERER:return sex?JOB_MINSTREL:JOB_WANDERER; + case MAPID_SURA: return JOB_SURA; + case MAPID_GENETIC: return JOB_GENETIC; + case MAPID_SHADOW_CHASER: return JOB_SHADOW_CHASER; + //3.2 trans + case MAPID_ROYAL_GUARD_T: return JOB_ROYAL_GUARD_T; + case MAPID_SORCERER_T: return JOB_SORCERER_T; + case MAPID_MINSTRELWANDERER_T:return sex?JOB_MINSTREL_T:JOB_WANDERER_T; + case MAPID_SURA_T: return JOB_SURA_T; + case MAPID_GENETIC_T: return JOB_GENETIC_T; + case MAPID_SHADOW_CHASER_T: return JOB_SHADOW_CHASER_T; default: return -1; } @@ -4898,7 +4990,7 @@ int pc_checkjoblevelup(struct map_session_data *sd) status_calc_pc(sd,0); clif_misceffect(&sd->bl,1); if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd)) - clif_status_change(&sd->bl,SI_DEVIL, 1, 0); //Permanent blind effect from SG_DEVIL. + clif_status_change(&sd->bl,SI_DEVIL, 1, 0, 0, 0, 1); //Permanent blind effect from SG_DEVIL. npc_script_event(sd, NPCE_JOBLVUP); return 1; @@ -5131,7 +5223,11 @@ int pc_need_status_point(struct map_session_data* sd, int type, int val) swap(low, high); for ( ; low < high; low++ ) +#if RRMODE //Renewal Stat Cost Formula + sp += (low < 100) ? (2 + (low - 1) / 10) : (16 + 4 * ((low - 100) / 5)); +#else sp += ( 1 + (low + 9) / 10 ); +#endif return sp; } @@ -5480,7 +5576,16 @@ int pc_resetskill(struct map_session_data* sd, int flag) i &= ~OPTION_CART; if( i&OPTION_FALCON && pc_checkskill(sd, HT_FALCON) ) i &= ~OPTION_FALCON; - + if( i&OPTION_DRAGON && pc_checkskill(sd, RK_DRAGONTRAINING) ) + i &= ~OPTION_DRAGON; + if( i&OPTION_WUG && pc_checkskill(sd, RA_WUGMASTERY) ) + i &= ~OPTION_WUG; + if( i&OPTION_WUGRIDER && pc_checkskill(sd, RA_WUGRIDER) ) + i &= ~OPTION_WUGRIDER; + if( i&OPTION_MADOGEAR && ( sd->class_&MAPID_THIRDMASK ) == MAPID_MECHANIC ) + i &= ~OPTION_MADOGEAR; + if( i&OPTION_MOUNTING ) + i &= ~OPTION_MOUNTING; if( i != sd->sc.option ) pc_setoption(sd, i); @@ -6400,7 +6505,14 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) i&=~OPTION_CART; if(i&OPTION_FALCON && !pc_checkskill(sd, HT_FALCON)) i&=~OPTION_FALCON; - + if( i&OPTION_DRAGON && !pc_checkskill(sd,RK_DRAGONTRAINING) ) + i&=~OPTION_DRAGON; + if( i&OPTION_WUGRIDER && !pc_checkskill(sd,RA_WUGMASTERY) ) + i&=~OPTION_WUGRIDER; + if( i&OPTION_WUG && !pc_checkskill(sd,RA_WUGMASTERY) ) + i&=~OPTION_WUG; + if( i&OPTION_MADOGEAR ) //You do not need a skill for this. + i&=~OPTION_MADOGEAR; if(i != sd->sc.option) pc_setoption(sd, i); @@ -6528,15 +6640,15 @@ int pc_setoption(struct map_session_data *sd,int type) sd->sc.option=type; clif_changeoption(&sd->bl); - if (type&OPTION_RIDING && !(p_type&OPTION_RIDING) && (sd->class_&MAPID_BASEMASK) == MAPID_SWORDMAN) - { //We are going to mount. [Skotlex] + if( (type&OPTION_RIDING && !(p_type&OPTION_RIDING)) || (type&OPTION_DRAGON && !(p_type&OPTION_DRAGON) && pc_checkskill(sd,RK_DRAGONTRAINING) > 0) ) + { // Mounting clif_status_load(&sd->bl,SI_RIDING,1); - status_calc_pc(sd,0); //Mounting/Umounting affects walk and attack speeds. + status_calc_pc(sd,0); } - else if (!(type&OPTION_RIDING) && p_type&OPTION_RIDING && (sd->class_&MAPID_BASEMASK) == MAPID_SWORDMAN) - { //We are going to dismount. + else if( (!(type&OPTION_RIDING) && p_type&OPTION_RIDING) || (!(type&OPTION_DRAGON) && p_type&OPTION_DRAGON && pc_checkskill(sd,RK_DRAGONTRAINING) > 0) ) + { // Dismount clif_status_load(&sd->bl,SI_RIDING,0); - status_calc_pc(sd,0); //Mounting/Umounting affects walk and attack speeds. + status_calc_pc(sd,0); } if(type&OPTION_CART && !(p_type&OPTION_CART)) @@ -6553,11 +6665,49 @@ int pc_setoption(struct map_session_data *sd,int type) status_calc_pc(sd,0); //Remove speed penalty. } + if (type&OPTION_MOUNTING && !(p_type&OPTION_MOUNTING)) { + clif_status_load_notick(&sd->bl,SI_ALL_RIDING,2,1,0,0); + status_calc_pc(sd,0); + } else if (!(type&OPTION_MOUNTING) && p_type&OPTION_MOUNTING) { + clif_status_load_notick(&sd->bl,SI_ALL_RIDING,0,0,0,0); + status_calc_pc(sd,0); + } + + if (type&OPTION_FALCON && !(p_type&OPTION_FALCON)) //Falcon ON clif_status_load(&sd->bl,SI_FALCON,1); else if (!(type&OPTION_FALCON) && p_type&OPTION_FALCON) //Falcon OFF clif_status_load(&sd->bl,SI_FALCON,0); + if( (sd->class_&MAPID_THIRDMASK) == MAPID_RANGER ) { + if( type&OPTION_WUGRIDER && !(p_type&OPTION_WUGRIDER) ) { // Mounting + clif_status_load(&sd->bl,SI_WUGRIDER,1); + status_calc_pc(sd,0); + } else if( !(type&OPTION_WUGRIDER) && p_type&OPTION_WUGRIDER ) { // Dismount + clif_status_load(&sd->bl,SI_WUGRIDER,0); + status_calc_pc(sd,0); + } + } + if( (sd->class_&MAPID_THIRDMASK) == MAPID_MECHANIC ) { + if( type&OPTION_MADOGEAR && !(p_type&OPTION_MADOGEAR) ) { + status_calc_pc(sd, 0); + status_change_end(&sd->bl,SC_MAXIMIZEPOWER,-1); + status_change_end(&sd->bl,SC_OVERTHRUST,-1); + status_change_end(&sd->bl,SC_WEAPONPERFECTION,-1); + status_change_end(&sd->bl,SC_ADRENALINE,-1); + status_change_end(&sd->bl,SC_CARTBOOST,-1); + status_change_end(&sd->bl,SC_MELTDOWN,-1); + status_change_end(&sd->bl,SC_MAXOVERTHRUST,-1); + } else if( !(type&OPTION_MADOGEAR) && p_type&OPTION_MADOGEAR ) { + status_calc_pc(sd, 0); + status_change_end(&sd->bl,SC_SHAPESHIFT,-1); + status_change_end(&sd->bl,SC_HOVERING,-1); + status_change_end(&sd->bl,SC_ACCELERATION,-1); + status_change_end(&sd->bl,SC_OVERHEAT_LIMITPOINT,-1); + status_change_end(&sd->bl,SC_OVERHEAT,-1); + } + } + if (type&OPTION_FLYING && !(p_type&OPTION_FLYING)) new_look = JOB_STAR_GLADIATOR2; else if (!(type&OPTION_FLYING) && p_type&OPTION_FLYING) @@ -6644,7 +6794,7 @@ int pc_setriding(TBL_PC* sd, int flag) if( pc_checkskill(sd,KN_RIDING) > 0 ) // ライディングスキル所持 pc_setoption(sd, sd->sc.option|OPTION_RIDING); } else if( pc_isriding(sd) ){ - pc_setoption(sd, sd->sc.option&~OPTION_RIDING); + pc_setoption(sd, sd->sc.option&~OPTION_RIDING); } return 0; @@ -7801,6 +7951,30 @@ void pc_setstand(struct map_session_data *sd){ sd->state.dead_sit = sd->vd.dead_sit = 0; } +/** + * Mechanic (MADO GEAR) + **/ +void pc_overheat(struct map_session_data *sd, int val) { + int heat = val, skill, + limit[] = { 10, 20, 28, 46, 66 }; + + if( !(sd->sc.option&OPTION_MADOGEAR) || sd->sc.data[SC_OVERHEAT] ) + return; // already burning + + skill = cap_value(pc_checkskill(sd,NC_MAINFRAME),0,4); + if( sd->sc.data[SC_OVERHEAT_LIMITPOINT] ) { + heat += sd->sc.data[SC_OVERHEAT_LIMITPOINT]->val1; + status_change_end(&sd->bl,SC_OVERHEAT_LIMITPOINT,-1); + } + + heat = max(0,heat); // Avoid negative HEAT + if( heat >= limit[skill] ) + sc_start(&sd->bl,SC_OVERHEAT,100,0,1000); + else + sc_start(&sd->bl,SC_OVERHEAT_LIMITPOINT,100,heat,30000); + + return; +} int pc_split_str(char *str,char **val,int num) { int i; @@ -8060,7 +8234,11 @@ int pc_readdb(void) // スキルツリ? memset(statp,0,sizeof(statp)); i=1; +#if RRMODE + sprintf(line, "%s/statpoint_renewal.txt", db_path); +#else sprintf(line, "%s/statpoint.txt", db_path); +#endif fp=fopen(line,"r"); if(fp == NULL){ ShowWarning("Can't read '"CL_WHITE"%s"CL_RESET"'... Generating DB.\n",line); @@ -8079,7 +8257,11 @@ int pc_readdb(void) i++; } fclose(fp); + #if RRMODE + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","statpoint_renewal.txt"); + #else ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","statpoint.txt"); + #endif } // generate the remaining parts of the db if necessary k = battle_config.use_statpoint_table; //save setting diff --git a/src/map/pc.h b/src/map/pc.h index 4d1e929a0..51588c842 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -21,6 +21,9 @@ #define MAX_PC_SKILL_REQUIRE 5 #define MAX_PC_FEELHATE 3 +//For Warlock +#define MAX_SPELLBOOK 10 + struct weapon_data { int atkmods[3]; // all the variables except atkmods get zero'ed in each call of status_calc_pc @@ -405,6 +408,13 @@ struct map_session_data { bool changed; // if true, should sync with charserver on next mailbox request } mail; + // Reading SpellBook + struct { + unsigned short skillid; + unsigned char level; + unsigned char points; + } rsb[MAX_SPELLBOOK]; + //Quest log system [Kevin] [Inkfish] int num_quests; int avail_quests; @@ -420,13 +430,33 @@ struct map_session_data { unsigned int bg_id; unsigned short user_font; + /** + * For the Secure NPC Timeout option (check RRConfig/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT + /** + * ID of the timer + * @info + * - value is -1 (INVALID_TIMER constant) when not being used + * - timer is cancelled upon closure of the current npc's instance + **/ + int npc_idle_timer; + /** + * Tick on the last recorded NPC iteration (next/menu/whatever) + * @info + * - It is updated on every NPC iteration as mentioned above + **/ + unsigned int npc_idle_tick; +#endif + // temporary debugging of bug #3504 const char* delunit_prevfile; int delunit_prevline; }; //Update this max as necessary. 55 is the value needed for Super Baby currently -#define MAX_SKILL_TREE 55 +//Raised to 75 due to 3rds +#define MAX_SKILL_TREE 75 //Total number of classes (for data storage) #define CLASS_COUNT (JOB_MAX - JOB_NOVICE_HIGH + JOB_MAX_BASIC) @@ -538,7 +568,12 @@ enum equip_index { #define pc_isinvisible(sd) ( (sd)->sc.option&OPTION_INVISIBLE ) #define pc_is50overweight(sd) ( (sd)->weight*100 >= (sd)->max_weight*battle_config.natural_heal_weight_rate ) #define pc_is90overweight(sd) ( (sd)->weight*10 >= (sd)->max_weight*9 ) -#define pc_maxparameter(sd) ( (sd)->class_&JOBL_BABY ? battle_config.max_baby_parameter : battle_config.max_parameter ) +#define pc_maxparameter(sd) ( (sd)->class_&JOBL_THIRD ? battle_config.max_third_parameter : (sd)->class_&JOBL_BABY ? battle_config.max_baby_parameter : battle_config.max_parameter ) +/** + * Ranger + **/ +#define pc_iswug(sd) ( (sd)->sc.option&OPTION_WUG ) +#define pc_isridingwug(sd) ( (sd)->sc.option&OPTION_WUGRIDER ) #define pc_stop_walking(sd, type) unit_stop_walking(&(sd)->bl, type) #define pc_stop_attack(sd) unit_stop_attack(&(sd)->bl) @@ -550,7 +585,8 @@ enum equip_index { #define pcdb_checkid(class_) \ ( \ ( (class_) >= JOB_NOVICE && (class_) < JOB_MAX_BASIC ) \ -|| ( (class_) >= JOB_NOVICE_HIGH && (class_) < JOB_MAX ) \ +|| ( (class_) >= JOB_NOVICE_HIGH && (class_) <= JOB_SOUL_LINKER ) \ +|| ( (class_) >= JOB_RUNE_KNIGHT && (class_) < JOB_MAX ) \ ) int pc_class2idx(int class_); @@ -785,5 +821,8 @@ void pc_inventory_rental_add(struct map_session_data *sd, int seconds); int pc_read_motd(void); // [Valaris] int pc_disguise(struct map_session_data *sd, int class_); - +/** + * Mechanic (Mado Gear) + **/ +void pc_overheat(struct map_session_data *sd, int val); #endif /* _PC_H_ */ diff --git a/src/map/script.c b/src/map/script.c index 151535c2f..452212a7d 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -325,7 +325,10 @@ enum { MF_FOG, MF_SAKURA, MF_LEAVES, - MF_RAIN, //20 + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //MF_RAIN, //20 // 21 free MF_NOGO = 22, MF_CLOUDS, @@ -3365,7 +3368,18 @@ static void script_detach_state(struct script_state* st, bool dequeue_event) { sd->st = st->bk_st; sd->npc_id = st->bk_npcid; - + /** + * For the Secure NPC Timeout option (check RRConfig/Secure.h) [RR] + **/ + #if SECURE_NPCTIMEOUT + /** + * We're done with this NPC session, so we cancel the timer (if existent) and move on + **/ + if( sd->npc_idle_timer != INVALID_TIMER ) { + delete_timer(sd->npc_idle_timer,npc_rr_secure_timeout_timer); + sd->npc_idle_timer = INVALID_TIMER; + } + #endif if(st->bk_st) { //Remove tag for removal. @@ -3407,6 +3421,14 @@ static void script_attach_state(struct script_state* st) } sd->st = st; sd->npc_id = st->oid; +/** + * For the Secure NPC Timeout option (check RRConfig/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT + if( sd->npc_idle_timer == INVALID_TIMER ) + sd->npc_idle_timer = add_timer(gettick() + (SECURE_NPCTIMEOUT_INTERVAL*1000),npc_rr_secure_timeout_timer,sd->bl.id,0); + sd->npc_idle_tick = gettick(); +#endif } } @@ -7549,7 +7571,7 @@ BUILDIN_FUNC(checkriding) if( sd == NULL ) return 0;// no player attached, report source - if( pc_isriding(sd) ) + if( pc_isriding(sd) || sd->sc.option&OPTION_MOUNTING ) script_pushint(st, 1); else script_pushint(st, 0); @@ -7771,7 +7793,7 @@ BUILDIN_FUNC(produce) return 0; trigger=script_getnum(st,2); - clif_skill_produce_mix_list(sd, trigger); + clif_skill_produce_mix_list(sd, -1, trigger); return 0; } /*========================================== @@ -9146,7 +9168,7 @@ BUILDIN_FUNC(roclass) } /*========================================== - *携帯卵孵化機使用 + * Tells client to open a hatching window, used for pet incubator *------------------------------------------*/ BUILDIN_FUNC(birthpet) { @@ -9616,7 +9638,10 @@ BUILDIN_FUNC(getmapflag) case MF_FIREWORKS: script_pushint(st,map[m].flag.fireworks); break; case MF_SAKURA: script_pushint(st,map[m].flag.sakura); break; case MF_LEAVES: script_pushint(st,map[m].flag.leaves); break; - case MF_RAIN: script_pushint(st,map[m].flag.rain); break; + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //case MF_RAIN: script_pushint(st,map[m].flag.rain); break; case MF_NIGHTENABLED: script_pushint(st,map[m].flag.nightenabled); break; case MF_NOGO: script_pushint(st,map[m].flag.nogo); break; case MF_NOBASEEXP: script_pushint(st,map[m].flag.nobaseexp); break; @@ -9686,7 +9711,10 @@ BUILDIN_FUNC(setmapflag) case MF_FIREWORKS: map[m].flag.fireworks=1; break; case MF_SAKURA: map[m].flag.sakura=1; break; case MF_LEAVES: map[m].flag.leaves=1; break; - case MF_RAIN: map[m].flag.rain=1; break; + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //case MF_RAIN: map[m].flag.rain=1; break; case MF_NIGHTENABLED: map[m].flag.nightenabled=1; break; case MF_NOGO: map[m].flag.nogo=1; break; case MF_NOBASEEXP: map[m].flag.nobaseexp=1; break; @@ -9753,7 +9781,10 @@ BUILDIN_FUNC(removemapflag) case MF_FIREWORKS: map[m].flag.fireworks=0; break; case MF_SAKURA: map[m].flag.sakura=0; break; case MF_LEAVES: map[m].flag.leaves=0; break; - case MF_RAIN: map[m].flag.rain=0; break; + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //case MF_RAIN: map[m].flag.rain=0; break; case MF_NIGHTENABLED: map[m].flag.nightenabled=0; break; case MF_NOGO: map[m].flag.nogo=0; break; case MF_NOBASEEXP: map[m].flag.nobaseexp=0; break; @@ -14951,8 +14982,133 @@ BUILDIN_FUNC(searchstores) searchstore_open(sd, uses, effect); return 0; } +/// Displays a number as large digital clock. +/// showdigit <value>[,<type>]; +BUILDIN_FUNC(showdigit) +{ + unsigned int type = 0; + int value; + struct map_session_data* sd; + + if( ( sd = script_rid2sd(st) ) == NULL ) + { + return 0; + } + + value = script_getnum(st,2); + + if( script_hasdata(st,3) ) + { + type = script_getnum(st,3); + if( type > 3 ) + { + ShowError("buildin_showdigit: Invalid type %u.\n", type); + return 1; + } + } + clif_showdigit(sd, (unsigned char)type, value); + return 0; +} +/** + * Rune Knight + **/ +BUILDIN_FUNC(makerune) { + TBL_PC* sd; + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + clif_skill_produce_mix_list(sd,RK_RUNEMASTERY,24); + sd->itemid = script_getnum(st,2); + return 0; +} +/** + * checkdragon() returns 1 if mounting a dragon or 0 otherwise. + **/ +BUILDIN_FUNC(checkdragon) { + TBL_PC* sd; + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + if( sd->sc.option&OPTION_DRAGON ) + script_pushint(st,1); + else + script_pushint(st,0); + return 0; +} +/** + * setdragon({optional Color}) returns 1 on success or 0 otherwise + * - Toggles the dragon on a RK if he can mount; + * @param Color - when not provided uses the green dragon; + * - 1 : Green Dragon + * - 2 : Brown Dragon + * - 3 : Gray Dragon + * - 4 : Blue Dragon + * - 5 : Red Dragon + **/ +BUILDIN_FUNC(setdragon) { + TBL_PC* sd; + int color = script_hasdata(st,2) ? script_getnum(st,2) : 0; + unsigned int option = OPTION_DRAGON1; + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + if( !pc_checkskill(sd,RK_DRAGONTRAINING) || (sd->class_&MAPID_THIRDMASK) != MAPID_RUNE_KNIGHT ) + script_pushint(st,0);//Doesn't have the skill or it's not a Rune Knight + else if ( sd->sc.option&OPTION_DRAGON ) {//Is mounted; release + pc_setoption(sd, sd->sc.option&~OPTION_DRAGON); + script_pushint(st,1); + } else {//Not mounted; Mount now. + if( color ) { + option = ( color == 1 ? OPTION_DRAGON1 : + color == 2 ? OPTION_DRAGON2 : + color == 3 ? OPTION_DRAGON3 : + color == 4 ? OPTION_DRAGON4 : + color == 5 ? OPTION_DRAGON5 : 0); + if( !option ) { + ShowWarning("script_setdragon: Unknown Color %d used; changing to green (1)\n",color); + option = OPTION_DRAGON1; + } + } + pc_setoption(sd, sd->sc.option|option); + script_pushint(st,1); + } + return 0; +} + +/** + * ismounting() returns 1 if mounting a new mount or 0 otherwise + **/ +BUILDIN_FUNC(ismounting) { + TBL_PC* sd; + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + if( sd->sc.option&OPTION_MOUNTING ) + script_pushint(st,1); + else + script_pushint(st,0); + return 0; +} + +/** + * setmounting() returns 1 on success or 0 otherwise + * - Toggles new mounts on a player when he can mount + * - Will fail if the player is mounting a non-new mount, e.g. dragon, peco, wug, etc. + * - Will unmount the player is he is already mounting + **/ +BUILDIN_FUNC(setmounting) { + TBL_PC* sd; + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + if( sd->sc.option&(OPTION_WUGRIDER|OPTION_RIDING|OPTION_DRAGON|OPTION_MADOGEAR) ) + script_pushint(st,0);//can't mount with one of these + else { + if( sd->sc.option&OPTION_MOUNTING ) + pc_setoption(sd, sd->sc.option&~OPTION_MOUNTING);//release mount + else + pc_setoption(sd, sd->sc.option|OPTION_MOUNTING);//mount + script_pushint(st,1);//in both cases, return 1. + } + return 0; +} // declarations that were supposed to be exported from npc_chat.c #ifdef PCRE_SUPPORT BUILDIN_FUNC(defpattern); @@ -15317,6 +15473,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(pushpc,"ii"), BUILDIN_DEF(buyingstore,"i"), BUILDIN_DEF(searchstores,"ii"), + BUILDIN_DEF(showdigit,"i?"), // WoE SE BUILDIN_DEF(agitstart2,""), BUILDIN_DEF(agitend2,""), @@ -15348,7 +15505,14 @@ struct script_function buildin_func[] = { BUILDIN_DEF(instance_npcname,"s?"), BUILDIN_DEF(has_instance,"s?"), BUILDIN_DEF(instance_warpall,"sii?"), - + /** + * 3rd-related + **/ + BUILDIN_DEF(makerune,"i"), + BUILDIN_DEF(checkdragon,""),//[Ind] + BUILDIN_DEF(setdragon,"?"),//[Ind] + BUILDIN_DEF(ismounting,""),//[Ind] + BUILDIN_DEF(setmounting,""),//[Ind] //Quest Log System [Inkfish] BUILDIN_DEF(setquest, "i"), BUILDIN_DEF(erasequest, "i"), diff --git a/src/map/skill.c b/src/map/skill.c index 28bb9e389..41c3580ae 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -59,11 +59,23 @@ struct s_skill_db skill_db[MAX_SKILL_DB]; struct s_skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB]; struct s_skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB]; struct s_skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB]; +//Warlock +struct s_skill_spellbook_db { + int nameid; + int skillid; + int points; +}; + +struct s_skill_spellbook_db skill_spellbook_db[MAX_SKILL_SPELLBOOK_DB]; +//Guillotine Cross +struct s_skill_magicmushroom_db skill_magicmushroom_db[MAX_SKILL_MAGICMUSHROOM_DB]; struct s_skill_unit_layout skill_unit_layout[MAX_SKILL_UNIT_LAYOUT]; int firewall_unit_pos; int icewall_unit_pos; - +int earthstrain_unit_pos; +//early declaration +int skill_stasis_check(struct block_list *bl, int src_id, int skillid); //Since only mob-casted splash skills can hit ice-walls static inline int splash_target(struct block_list* bl) { @@ -170,6 +182,7 @@ int skill_get_unit_target( int id ) { skill_get (skill_db[id].unit_target& int skill_get_unit_bl_target( int id ) { skill_get (skill_db[id].unit_target&BL_ALL, id, 1); } int skill_get_unit_flag( int id ) { skill_get (skill_db[id].unit_flag, id, 1); } int skill_get_unit_layout_type( int id ,int lv ){ skill_get (skill_db[id].unit_layout_type[lv-1], id, lv); } +int skill_get_cooldown( int id ,int lv ) { skill_get (skill_db[id].cooldown[lv-1], id, lv); } int skill_tree_get_max(int id, int b_class) { @@ -241,6 +254,12 @@ int skill_get_range2 (struct block_list *bl, int id, int lv) case MA_CHARGEARROW: case SN_FALCONASSAULT: case HT_POWER: + /** + * Ranger + **/ + case RA_ARROWSTORM: + case RA_AIMEDBOLT: + case RA_WUGBITE: if( bl->type == BL_PC ) range += pc_checkskill((TBL_PC*)bl, AC_VULTURE); else @@ -261,6 +280,36 @@ int skill_get_range2 (struct block_list *bl, int id, int lv) if (bl->type == BL_PC) range = skill_get_range(NJ_SHADOWJUMP,pc_checkskill((TBL_PC*)bl,NJ_SHADOWJUMP)); break; + /** + * Warlock + **/ + case WL_WHITEIMPRISON: + case WL_SOULEXPANSION: + case WL_FROSTMISTY: + case WL_MARSHOFABYSS: + case WL_SIENNAEXECRATE: + case WL_DRAINLIFE: + case WL_CRIMSONROCK: + case WL_HELLINFERNO: + case WL_COMET: + case WL_CHAINLIGHTNING: + case WL_TETRAVORTEX: + case WL_RELEASE: + if( bl->type == BL_PC ) + range += pc_checkskill((TBL_PC*)bl, WL_RADIUS); + break; + /** + * Ranger Bonus + **/ + case HT_LANDMINE: + case HT_FREEZINGTRAP: + case HT_BLASTMINE: + case HT_CLAYMORETRAP: + case RA_CLUSTERBOMB: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: + if( bl->type == BL_PC ) + range += (1 + pc_checkskill((TBL_PC*)bl, RA_RESEARCHTRAP))/2; } if( !range && bl->type != BL_PC ) @@ -291,14 +340,25 @@ int skill_calc_heal(struct block_list *src, struct block_list *target, int skill default: if (skill_lv >= battle_config.max_heal_lv) return battle_config.max_heal; - - hp = ( status_get_lv(src)+status_get_int(src) )/8 *(4+ skill_lv*8); + #if RRMODE + /** + * Renewal Heal Formula (from Doddler) + * TODO: whats that( 1+ %Modifier / 100 ) ? currently using 'x1' (100/100) until found out + * - Min = ( [ ( BaseLvl + INT ) / 5 ] * 30 ) * (1+( %Modifier / 100)) * (HealLvl * 0.1) + StatusMATK + EquipMATK - [(WeaponMATK * WeaponLvl) / 10] + * - Max = ( [ ( BaseLvl + INT ) / 5 ] * 30 ) * (1+( %Modifier / 100)) * (HealLvl * 0.1) + StatusMATK + EquipMATK + [(WeaponMATK * WeaponLvl) / 10] + **/ + hp = ( ( ( ( status_get_lv(src) + status_get_int(src) ) / 5 ) * 30 ) * ( skill_lv / 10 ) + status_get_matk_min(src) + status_get_matk_max(src) - ( ( status_get_matk_max(src) * status_get_wlv(src) ) / 10 ) ) + rand()%( ( ( ( status_get_lv(src) + status_get_int(src) ) / 5 ) * 30 ) * ( skill_lv / 10 ) + status_get_matk_min(src) + status_get_matk_max(src) + ( ( status_get_matk_max(src) * status_get_wlv(src) ) / 10 ) ); + #else + hp = ( status_get_lv(src) + status_get_int(src) ) / 8 * (4 + ( skill_id == AB_HIGHNESSHEAL ? ( sd ? pc_checkskill(sd,AL_HEAL) : 10 ) : skill_lv ) * 8); + #endif if( sd && ((skill = pc_checkskill(sd, HP_MEDITATIO)) > 0) ) hp += hp * skill * 2 / 100; else if( src->type == BL_HOM && (skill = merc_hom_checkskill(((TBL_HOM*)src), HLIF_BRAIN)) > 0 ) hp += hp * skill * 2 / 100; break; } + if( skill_id == AB_HIGHNESSHEAL ) + hp = (hp * (20 + 3 * (skill_lv - 1))) / 10; if( ( (target && target->type == BL_MER) || !heal ) && skill_id != NPC_EVILLAND ) hp >>= 1; @@ -378,6 +438,9 @@ int skillnotok (int skillid, struct map_session_data *sd) if(map[m].flag.restricted && map[m].zone && skill_get_nocast (skillid) & (8*map[m].zone)) return 1; + if( sd->sc.option&OPTION_MOUNTING ) + return 1;//You can't use skills while in the new mounts (The client doesn't let you, this is to make cheat-safe) + switch (skillid) { case AL_WARP: if(map[m].flag.nowarp) { @@ -410,6 +473,12 @@ int skillnotok (int skillid, struct map_session_data *sd) return 1; } break; + case GC_DARKILLUSION: + if( map_flag_gvg(m) ) { + clif_skill_fail(sd,skillid,0,0); + return 1; + } + break; case GD_EMERGENCYCALL: if ( !(battle_config.emergency_call&((agit_flag || agit2_flag)?2:1)) || @@ -420,6 +489,23 @@ int skillnotok (int skillid, struct map_session_data *sd) return 1; } break; + case BS_GREED: + case WS_CARTBOOST: + case BS_HAMMERFALL: + case BS_ADRENALINE: + case MC_CARTREVOLUTION: + case MC_MAMMONITE: + case WS_MELTDOWN: + case MG_SIGHT: + case TF_HIDING: + /** + * These skills cannot be used while in mado gear (credits to Xantara) + **/ + if(sd->sc.option&OPTION_MADOGEAR) { + clif_skill_fail(sd,skillid,0,0); + return 1; + } + break; } return (map[m].flag.noskill); } @@ -471,6 +557,8 @@ struct s_skill_unit_layout* skill_get_unit_layout (int skillid, int skilllv, str return &skill_unit_layout [firewall_unit_pos + dir]; else if (skillid == WZ_ICEWALL) return &skill_unit_layout [icewall_unit_pos + dir]; + else if( skillid == WL_EARTHSTRAIN ) //Warlock + return &skill_unit_layout [earthstrain_unit_pos + dir]; ShowError("skill_get_unit_layout: unknown unit layout for skill %d (level %d)\n", skillid, skilllv); return &skill_unit_layout[0]; // default 1x1 layout @@ -587,6 +675,9 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int rate=(sd->status.job_level+9)/10; skill_castend_damage_id(src,bl,HT_BLITZBEAT,(skill<rate)?skill:rate,tick,SD_LEVEL); } + // Automatic trigger of Warg Strike [Jobbie] + if( pc_iswug(sd) && (sd->status.weapon == W_BOW || sd->status.weapon == W_FIST) && (skill=pc_checkskill(sd,RA_WUGSTRIKE)) > 0 && rand()%1000 <= sstatus->luk*10/3+1 ) + skill_castend_damage_id(src,bl,RA_WUGSTRIKE,skill,tick,0); // Gank if(dstmd && sd->status.weapon != W_BOW && (skill=pc_checkskill(sd,RG_SNATCHER)) > 0 && @@ -683,7 +774,14 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int case WZ_METEOR: sc_start(bl,SC_STUN,3*skilllv,skilllv,skill_get_time2(skillid,skilllv)); break; - +#if FIREIVY_ON + //case WZ_FIREIVY: + // Testing for Fire Ivy + //sc_start(bl,SC_STUN,3*skilllv,skilllv,skill_get_time2(skillid,skilllv)); + //sc_start(bl,SC_STUN,(5*skilllv+35),skilllv,skill_get_time2(skillid,skilllv)); + //sc_start(bl,SC_BURNING,(8*skilllv+35),skilllv,skill_get_time2(skillid,skilllv)); + //break; +#endif case WZ_VERMILION: sc_start(bl,SC_BLIND,4*skilllv,skilllv,skill_get_time2(skillid,skilllv)); break; @@ -929,6 +1027,104 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int case NPC_CRITICALWOUND: sc_start(bl,SC_CRITICALWOUND,100,skilllv,skill_get_time2(skillid,skilllv)); break; + /** + * Rune Knight + **/ + case RK_HUNDREDSPEAR: + if( !sd || pc_checkskill(sd,KN_SPEARBOOMERANG) == 0 ) + break; // Spear Boomerang auto cast chance only works if you have mastered Spear Boomerang. + rate = 10 + 3 * skilllv; + if( rand()%100 < rate ) + skill_castend_damage_id(src,bl,KN_SPEARBOOMERANG,1,tick,0); + break; + case RK_WINDCUTTER: + sc_start(bl,SC_FEAR,3+2*skilllv,skilllv,skill_get_time(skillid,skilllv)); + break; + case RK_DRAGONBREATH: + sc_start4(bl,SC_BURNING,5+5*skilllv,skilllv,1000,src->id,0,skill_get_time(skillid,skilllv)); + break; + /** + * Arch Bishop + **/ + case AB_ADORAMUS: + if( tsc && !tsc->data[SC_DECREASEAGI] ) //Prevent duplicate agi-down effect. + sc_start(bl, SC_ADORAMUS, 100, skilllv, skill_get_time(skillid, skilllv)); + break; + /** + * Warlock + **/ + case WL_CRIMSONROCK: + sc_start(bl, SC_STUN, 40, skilllv, skill_get_time(skillid, skilllv)); + break; + case WL_COMET: + sc_start4(bl,SC_BURNING,100,skilllv,1000,src->id,0,skill_get_time(skillid,skilllv)); + break; + case WL_EARTHSTRAIN: + { + int rate = 0, i; + const int pos[5] = { EQP_WEAPON, EQP_HELM, EQP_SHIELD, EQP_ARMOR, EQP_ACC }; + rate = 6 * skilllv + sstatus->dex / 10 + (sd? sd->status.job_level / 4 : 0) - tstatus->dex /5;// The tstatus->dex / 5 part is unofficial, but players gotta have some kind of way to have resistance. [Rytech] + //rate -= rate * tstatus->dex / 200; // Disabled until official resistance is found. + + for( i = 0; i < skilllv; i++ ) + skill_strip_equip(bl,pos[i],rate,skilllv,skill_get_time2(skillid,skilllv)); + } + break; + case WL_JACKFROST: + sc_start(bl,SC_FREEZE,100,skilllv,skill_get_time(skillid,skilllv)); + break; + /** + * Ranger + **/ + case RA_WUGBITE: + sc_start(bl, SC_BITE, 70, skilllv, skill_get_time(skillid, skilllv) + (sd ? pc_checkskill(sd,RA_TOOTHOFWUG) * 1000 : 0)); // Need official chance. + break; + case RA_SENSITIVEKEEN: + if( rand()%100 < 8 * skilllv ) + skill_castend_damage_id(src, bl, RA_WUGBITE, sd ? pc_checkskill(sd, RA_WUGBITE):skilllv, tick, SD_ANIMATION); + break; + case RA_MAGENTATRAP: + case RA_COBALTTRAP: + case RA_MAIZETRAP: + case RA_VERDURETRAP: + if( dstmd && !(dstmd->status.mode&MD_BOSS) ) + sc_start2(bl,SC_ELEMENTALCHANGE,100,skilllv,skill_get_ele(skillid,skilllv),skill_get_time2(skillid,skilllv)); + break; + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: + sc_start(bl, (skillid == RA_FIRINGTRAP) ? SC_BURNING:SC_FREEZING, 40 + 10 * skilllv, skilllv, skill_get_time2(skillid, skilllv)); + break; + /** + * Mechanic + **/ + case NC_PILEBUNKER: + if( rand()%100 < 5 + 15*skilllv ) + { //Deactivatable Statuses: Kyrie Eleison, Auto Guard, Steel Body, Assumptio, and Millennium Shield + status_change_end(bl, SC_KYRIE, -1); + status_change_end(bl, SC_AUTOGUARD, -1); + status_change_end(bl, SC_STEELBODY, -1); + status_change_end(bl, SC_ASSUMPTIO, -1); + status_change_end(bl, SC_MILLENNIUMSHIELD, -1); + } + break; + case NC_FLAMELAUNCHER: + sc_start4(bl, SC_BURNING, 50 + 10 * skilllv, skilllv, 1000, src->id, 0, skill_get_time2(skillid, skilllv)); + break; + case NC_COLDSLOWER: + sc_start(bl, SC_FREEZE, 10 * skilllv, skilllv, skill_get_time(skillid, skilllv)); + sc_start(bl, SC_FREEZING, 20 + 10 * skilllv, skilllv, skill_get_time2(skillid, skilllv)); + break; + case NC_POWERSWING: + sc_start(bl, SC_STUN, 5*skilllv, skilllv, skill_get_time(skillid, skilllv)); + if( rand()%100 < 5*skilllv ) + skill_castend_damage_id(src, bl, NC_AXEBOOMERANG, pc_checkskill(sd, NC_AXEBOOMERANG), tick, 1); + break; + /** + * Guilotine Cross + **/ + case GC_WEAPONCRUSH: + skill_castend_nodamage_id(src,bl,skillid,skilllv,tick,BCT_ENEMY); + break; } if (md && battle_config.summons_trigger_autospells && md->master_id && md->special_state.ai) @@ -1039,7 +1235,7 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ ud->canact_tick = tick+rate; if ( battle_config.display_status_timers && sd ) - clif_status_change(src, SI_ACTIONDELAY, 1, rate); + clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0); } } } @@ -1315,7 +1511,7 @@ int skill_counter_additional_effect (struct block_list* src, struct block_list * if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ ud->canact_tick = tick+rate; if ( battle_config.display_status_timers && dstsd ) - clif_status_change(bl, SI_ACTIONDELAY, 1, rate); + clif_status_change(bl, SI_ACTIONDELAY, 1, rate, 0, 0, 0); } } } @@ -1450,7 +1646,7 @@ int skill_strip_equip(struct block_list *bl, unsigned short where, int rate, int return 0; sc = status_get_sc(bl); - if (!sc) + if (!sc || sc->option&OPTION_MADOGEAR ) //Mado Gear cannot be divested [Ind] return 0; for (i = 0; i < ARRAYLENGTH(pos); i++) { @@ -1465,8 +1661,8 @@ int skill_strip_equip(struct block_list *bl, unsigned short where, int rate, int } return where?1:0; } - - +//Early declaration +static int skill_area_temp[8]; /*========================================================================= Used to knock back players, monsters, traps, etc - 'count' is the number of squares to knock back @@ -1655,8 +1851,14 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds sc->data[SC_SPIRIT]->val4 = dsrc->id; } } + /** + * Official Magic Reflection Behavior : damage reflected depends on gears caster wears, not target + **/ + #if RR_MAGIC_REFLECTION + if( dmg.dmg_lv != ATK_MISS )//Wiz SL cancelled and consumed fragment + dmg = battle_calc_attack(BF_MAGIC,bl,bl,skillid,skilllv,flag&0xFFF); + #endif } - if(sc && sc->data[SC_MAGICROD] && src == dsrc) { int sp = skill_get_sp(skillid,skilllv); dmg.damage = dmg.damage2 = 0; @@ -1678,7 +1880,7 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds if( damage > 0 && dmg.flag&BF_WEAPON && src != bl && ( src == dsrc || ( dsrc->type == BL_SKILL && ( skillid == SG_SUN_WARM || skillid == SG_MOON_WARM || skillid == SG_STAR_WARM ) ) ) && skillid != WS_CARTTERMINATION ) - rdamage = battle_calc_return_damage(bl, damage, dmg.flag); + rdamage = battle_calc_return_damage(bl,src, &damage, dmg.flag); //Skill hit type type=(skillid==0)?5:skill_get_hit(skillid); @@ -1804,7 +2006,31 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds else // the central target doesn't display an animation dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skillid, -2, 5); // needs -2(!) as skill level break; - + /** + * Warlock + **/ + case WL_HELLINFERNO: + dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,skillid,-2,6); + break; + case WL_SOULEXPANSION: + case WL_COMET: + dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skillid,skilllv,8); + break; + case WL_CHAINLIGHTNING_ATK: + dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,WL_CHAINLIGHTNING,-2,6); + break; + case WL_TETRAVORTEX_FIRE: + case WL_TETRAVORTEX_WATER: + case WL_TETRAVORTEX_WIND: + case WL_TETRAVORTEX_GROUND: + dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,WL_TETRAVORTEX_FIRE,-2,type); + break; + /** + * Arch Bishop + **/ + case AB_DUPLELIGHT_MELEE: + case AB_DUPLELIGHT_MAGIC: + dmg.amotion = 300; default: if( flag&SD_ANIMATION && dmg.div_ < 2 ) //Disabling skill animation doesn't works on multi-hit. type = 5; @@ -1819,8 +2045,22 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds && (!sc || !sc->data[SC_PRESERVE]) && damage < tsd->battle_status.hp) { //Updated to not be able to copy skills if the blow will kill you. [Skotlex] - if ((tsd->status.skill[skillid].id == 0 || tsd->status.skill[skillid].flag == SKILL_FLAG_PLAGIARIZED) && - can_copy(tsd,skillid,bl)) // Split all the check into their own function [Aru] + int copy_skill = skillid; + /** + * Copy Referal: dummy skills should point to their source upon copying + **/ + switch( skillid ) { + case AB_DUPLELIGHT_MELEE: + case AB_DUPLELIGHT_MAGIC: + copy_skill = AB_DUPLELIGHT; + break; + case WL_CHAINLIGHTNING_ATK: + copy_skill = WL_CHAINLIGHTNING; + break; + } + + if ((tsd->status.skill[copy_skill].id == 0 || tsd->status.skill[copy_skill].flag == SKILL_FLAG_PLAGIARIZED) && + can_copy(tsd,copy_skill,bl)) // Split all the check into their own function [Aru] { int lv = skilllv; if (tsd->cloneskill_id && tsd->status.skill[tsd->cloneskill_id].flag == SKILL_FLAG_PLAGIARIZED){ @@ -1833,11 +2073,11 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds if ((type = pc_checkskill(tsd,RG_PLAGIARISM)) < lv) lv = type; - tsd->cloneskill_id = skillid; - pc_setglobalreg(tsd, "CLONE_SKILL", skillid); + tsd->cloneskill_id = copy_skill; + pc_setglobalreg(tsd, "CLONE_SKILL", copy_skill); pc_setglobalreg(tsd, "CLONE_SKILL_LV", lv); - tsd->status.skill[skillid].id = skillid; + tsd->status.skill[skillid].id = copy_skill; tsd->status.skill[skillid].lv = lv; tsd->status.skill[skillid].flag = SKILL_FLAG_PLAGIARIZED; clif_addskill(tsd,skillid); @@ -1871,6 +2111,12 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds if( damage > 0 ) //Counter status effects [Skotlex] skill_counter_additional_effect(src,bl,skillid,skilllv,dmg.flag,tick); } + // Hell Inferno burning status only starts if Fire part hits. + if( skillid == WL_HELLINFERNO && dmg.damage > 0 ) + sc_start4(bl,SC_BURNING,55+5*skilllv,skilllv,1000,src->id,0,skill_get_time(skillid,skilllv)); + // Apply knock back chance in SC_TRIANGLESHOT skill. + else if( skillid == SC_TRIANGLESHOT && rand()%100 > (1 + skilllv) ) + dmg.blewcount = 0; //Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex] //Reflected spells do not bounce back (bl == dsrc since it only happens for direct skills) @@ -1882,6 +2128,8 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds case MG_FIREWALL: direction = unit_getdir(bl); break; // backwards case WZ_STORMGUST: direction = rand()%8; break; // randomly case PR_SANCTUARY: direction = unit_getdir(bl); break; // backwards + case WL_CRIMSONROCK: direction = map_calc_dir(bl,skill_area_temp[4],skill_area_temp[5]); break; + } skill_blown(dsrc,bl,dmg.blewcount,direction,0); } @@ -1937,6 +2185,19 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds battle_drain(tsd, src, rdamage, rdamage, sstatus->race, is_boss(src)); skill_additional_effect(bl, src, CR_REFLECTSHIELD, 1, BF_WEAPON|BF_SHORT|BF_NORMAL,ATK_DEF,tick); } + if( damage > 0 ) { + if( skillid == RK_CRUSHSTRIKE ) // Your weapon will not be broken if you miss. + skill_break_equip(src,EQP_WEAPON,10000,BCT_SELF); + + if( skillid == GC_VENOMPRESSURE ) { + struct status_change *ssc = status_get_sc(src); + if( ssc && ssc->data[SC_POISONINGWEAPON] && rand()%100 < 70 + 5*skilllv ) { + sc_start(bl,ssc->data[SC_POISONINGWEAPON]->val2,100,ssc->data[SC_POISONINGWEAPON]->val1,skill_get_time2(GC_POISONINGWEAPON,ssc->data[SC_POISONINGWEAPON]->val1)); + status_change_end(src,SC_POISONINGWEAPON,-1); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + } + } if (!(flag&2) && ( @@ -1963,7 +2224,6 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds * ffff=自由に使用可能 * 0 =予約?B0に固定 *------------------------------------------*/ -static int skill_area_temp[8]; typedef int (*SkillFunc)(struct block_list *, struct block_list *, int, int, unsigned int, int); int skill_area_sub (struct block_list *bl, va_list ap) { @@ -2034,6 +2294,17 @@ static int skill_check_unit_range_sub (struct block_list *bl, va_list ap) case HT_CLAYMORETRAP: case HT_TALKIEBOX: case HP_BASILICA: + /** + * Ranger + **/ + case RA_ELECTRICSHOCKER: + case RA_CLUSTERBOMB: + case RA_MAGENTATRAP: + case RA_COBALTTRAP: + case RA_MAIZETRAP: + case RA_VERDURETRAP: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: //Non stackable on themselves and traps (including venom dust which does not has the trap inf2 set) if (skillid != g_skillid && !(skill_get_inf2(g_skillid)&INF2_TRAP) && g_skillid != AS_VENOMDUST) return 0; @@ -2346,6 +2617,61 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data) } } break; + /** + * Warlock + **/ + case WL_CHAINLIGHTNING_ATK: + { + struct block_list *nbl = NULL; // Next Target of Chain + skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); // Hit a Lightning on the current Target + if( skl->type > 1 ) + { // Remaining Chains Hit + nbl = battle_getenemyarea(src,target->x,target->y,2,BL_CHAR|BL_SKILL,target->id); // Search for a new Target around current one... + if( nbl == NULL && skl->x > 1 ) + { + nbl = target; + skl->x--; + } + else skl->x = 3; + } + + if( nbl ) + skill_addtimerskill(src,tick+status_get_adelay(src),nbl->id,skl->x,0,WL_CHAINLIGHTNING_ATK,skl->skill_lv,skl->type-1,skl->flag); + } + break; + case WL_TETRAVORTEX_FIRE: + case WL_TETRAVORTEX_WATER: + case WL_TETRAVORTEX_WIND: + case WL_TETRAVORTEX_GROUND: + skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); + if( skl->type >= 3 ) + { // Final Hit + status_change_end(src,SC_MAGICPOWER,-1); // Removes Magic Power + if( !status_isdead(target) ) + { // Final Status Effect + int effects[4] = { SC_BURNING, SC_FREEZING, SC_BLEEDING, SC_STUN }, + applyeffects[4] = { 0, 0, 0, 0 }, + i, j = 0, k = 0; + for( i = 1; i <= 8; i = i + i ) + { + if( skl->x&i ) + { + applyeffects[j] = effects[k]; + j++; + } + k++; + } + if( j ) + { + i = applyeffects[rand()%j]; + status_change_start(target, i, 10000, skl->skill_lv, + (i == SC_BURNING ? 1000 : 0), + (i == SC_BURNING ? src->id : 0), + 0, skill_get_time(WL_TETRAVORTEX,skl->skill_lv), 0); + } + } + } + break; default: skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); break; @@ -2368,7 +2694,13 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data) else if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) ) skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,skl->flag); break; + case WL_EARTHSTRAIN: + skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,(skl->type<<16)|skl->flag); + break; + } + if( skl->skill_id >= WL_TETRAVORTEX_FIRE && skl->skill_id <= WL_TETRAVORTEX_GROUND ) + status_change_end(src,SC_MAGICPOWER,-1); } } while (0); //Free skl now that it is no longer needed. @@ -2569,6 +2901,43 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int case NPC_BLEEDING: case NPC_CRITICALWOUND: case NPC_HELLPOWER: + /** + * Rune Knight + **/ + case RK_SONICWAVE: + case RK_HUNDREDSPEAR: + case RK_WINDCUTTER: + /** + * Arch Bishop + **/ + case AB_DUPLELIGHT_MELEE: + /** + * Ranger + **/ + case RA_AIMEDBOLT: + /** + * Mechanic + **/ + case NC_AXEBOOMERANG: + case NC_POWERSWING: + /** + * Guilotinne Cross + **/ + case GC_CROSSIMPACT: + case GC_VENOMPRESSURE: + + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + break; + + /** + * Mechanic (MADO GEAR) + **/ + case NC_BOOSTKNUCKLE: + case NC_PILEBUNKER: + case NC_VULCANARM: + case NC_COLDSLOWER: + case NC_ARMSCANNON: + if (sd) pc_overheat(sd,1); skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); break; @@ -2637,6 +3006,8 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int } break; + case NC_FLAMELAUNCHER: + if (sd) pc_overheat(sd,1); case SN_SHARPSHOOTING: case MA_SHARPSHOOTING: case NJ_KAMAITACHI: @@ -2766,6 +3137,35 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int case NPC_PULSESTRIKE: case NPC_HELLJUDGEMENT: case NPC_VAMPIRE_GIFT: + /** + * Rune Knight + **/ + case RK_IGNITIONBREAK: + /** + * Arch Bishop + **/ + case AB_JUDEX: + /** + * Warlock + **/ + case WL_SOULEXPANSION: + case WL_CRIMSONROCK: + case WL_COMET: + /** + * Ranger + **/ + case RA_ARROWSTORM: + case RA_WUGDASH: + /** + * Mechanic + **/ + case NC_SELFDESTRUCTION: + case NC_AXETORNADO: + /** + * Guilotine Cross + **/ + case GC_ROLLINGCUTTER: + case GC_COUNTERSLASH: if( flag&1 ) { //Recursive invocation // skill_area_temp[0] holds number of targets in area @@ -2792,7 +3192,10 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int skill_area_temp[0] = 0; skill_area_temp[1] = bl->id; skill_area_temp[2] = 0; - + if( skillid == WL_CRIMSONROCK ) { + skill_area_temp[4] = bl->x; + skill_area_temp[5] = bl->y; + } // if skill damage should be split among targets, count them //SD_LEVEL -> Forced splash damage for Auto Blitz-Beat -> count targets //special case: Venom Splasher uses a different range for searching than for splashing @@ -2800,7 +3203,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int skill_area_temp[0] = map_foreachinrange(skill_area_sub, bl, (skillid == AS_SPLASHER)?1:skill_get_splash(skillid, skilllv), BL_CHAR, src, skillid, skilllv, tick, BCT_ENEMY, skill_area_sub_count); // recursive invocation of skill_castend_damage_id() with flag|1 - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skillid, skilllv), splash_target(src), src, skillid, skilllv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); + map_foreachinrange(skill_area_sub, bl, skill_get_splash(skillid, skilllv), (skillid == WL_CRIMSONROCK)?BL_CHAR|BL_SKILL:splash_target(src), src, skillid, skilllv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); //FIXME: Isn't EarthQuake a ground skill after all? if( skillid == NPC_EARTHQUAKE ) @@ -2920,6 +3323,20 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int case NJ_KOUENKA: case NJ_HYOUSENSOU: case NJ_HUUJIN: +#if FIREIVY_ON + case WZ_FIREIVY: +#endif + /** + * Arch Bishop + **/ + case AB_ADORAMUS: + case AB_RENOVATIO: + case AB_HIGHNESSHEAL: + case AB_DUPLELIGHT_MAGIC: + /** + * Warlock + **/ + case WL_HELLINFERNO: skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); break; @@ -3006,6 +3423,10 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int case NPC_SMOKING: case GS_FLING: case NJ_ZENYNAGE: + /** + * Rune Knight + **/ + case RK_DRAGONBREATH: skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag); break; @@ -3062,6 +3483,330 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int status_change_end(src, SC_HIDING, INVALID_TIMER); skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); break; + /** + * Rune Knight + **/ + case RK_PHANTOMTHRUST: + unit_setdir(src,map_calc_dir(src, bl->x, bl->y)); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + + skill_blown(src,bl,distance_bl(src,bl)-1,unit_getdir(src),0); + if( battle_check_target(src,bl,BCT_ENEMY) ) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + break; + + case RK_STORMBLAST: + case RK_CRUSHSTRIKE: + if( sd ) { + if( pc_checkskill(sd,RK_RUNEMASTERY) >= ( skillid == RK_CRUSHSTRIKE ? 7 : 3 ) ) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + else + clif_skill_fail(sd,skillid,0,0); + } else //non-sd support + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + break; + /** + * Guilotinne Cross + **/ + case GC_DARKILLUSION: + { + short x, y; + short dir = map_calc_dir(src,bl->x,bl->y); + + if( dir > 4 ) x = -1; + else if( dir > 0 && dir < 4 ) x = 1; + else x = 0; + if( dir < 3 || dir > 5 ) y = -1; + else if( dir > 3 && dir < 5 ) y = 1; + else y = 0; + + if( unit_movepos(src, bl->x+x, bl->y+y, 1, 1) ) + { + clif_slide(src,bl->x+x,bl->y+y); + clif_fixpos(src); // the official server send these two packts. + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + if( rand()%100 < 4 * skilllv ) + skill_castend_damage_id(src,bl,GC_CROSSIMPACT,skilllv,tick,flag); + } + + } + break; + + case GC_WEAPONCRUSH: + if( sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == GC_WEAPONBLOCKING ) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + else if( sd ) + clif_skill_fail(sd,skillid,0x1f,0); + break; + + case GC_CROSSRIPPERSLASHER: + if( sd && !(sc && sc->data[SC_ROLLINGCUTTER]) ) + clif_skill_fail(sd,skillid,0x17,0); + else + { + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + status_change_end(src,SC_ROLLINGCUTTER,-1); + } + break; + + case GC_PHANTOMMENACE: + if( flag&1 ) + { // Only Hits Invisible Targets + struct status_change *tsc = status_get_sc(bl); + if(tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY]) ) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + } + break; + /** + * Warlock + **/ + case WL_CHAINLIGHTNING: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_addtimerskill(src,tick + 150,bl->id,3,0,WL_CHAINLIGHTNING_ATK,skilllv,4+skilllv,flag); + break; + case WL_DRAINLIFE: + { + int heal = skill_attack(skill_get_type(skillid), src, src, bl, skillid, skilllv, tick, flag); + int rate = 70 + 4 * skilllv + ( sd ? sd->status.job_level : 50 ) / 5; + + heal = 8 * skilllv; + if( status_get_lv(src) > 100 ) heal = heal * status_get_lv(src) / 100; // Base level bonus. + + if( bl->type == BL_SKILL ) + heal = 0; // Don't absorb heal from Ice Walls or other skill units. + + if( heal && rand()%100 < rate ) + { + status_heal(src, heal, 0, 0); + clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1); + } + } + break; + + case WL_TETRAVORTEX: + if( sd ) + { + int spheres[5] = { 0, 0, 0, 0, 0 }, + positions[5] = {-1,-1,-1,-1,-1 }, + i, j = 0, k, subskill = 0; + + for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ ) + if( sc && sc->data[i] ) + { + spheres[j] = i; + positions[j] = sc->data[i]->val2; + j++; // + } + + if( j < 4 ) + { // Need 4 spheres minimum + clif_skill_fail(sd,skillid,0,0); + break; + } + + // Sphere Sort, this time from new to old + for( i = 0; i <= j - 2; i++ ) + for( k = i + 1; k <= j - 1; k++ ) + if( positions[i] < positions[k] ) + { + swap(positions[i],positions[k]); + swap(spheres[i],spheres[k]); + } + + k = 0; + for( i = 0; i < 4; i++ ) + { + switch( sc->data[spheres[i]]->val1 ) + { + case WLS_FIRE: subskill = WL_TETRAVORTEX_FIRE; k |= 1; break; + case WLS_WIND: subskill = WL_TETRAVORTEX_WIND; k |= 4; break; + case WLS_WATER: subskill = WL_TETRAVORTEX_WATER; k |= 2; break; + case WLS_STONE: subskill = WL_TETRAVORTEX_GROUND; k |= 8; break; + } + + skill_addtimerskill(src,tick+status_get_adelay(src)*i,bl->id,k,0,subskill,skilllv,i,flag); + status_change_end(src, spheres[i], INVALID_TIMER); + } + } + break; + + case WL_RELEASE: + if( sd ) + { + int i; + // Priority is to release SpellBook + ARR_FIND(0,MAX_SPELLBOOK,i,sd->rsb[i].skillid != 0); + if( i < MAX_SPELLBOOK ) + { // SpellBook + int rsb_skillid, rsb_skilllv; + + if( skilllv > 1 ) + { + ARR_FIND(0,MAX_SPELLBOOK,i,sd->rsb[i].skillid == 0); + i--; // At skilllvl 2, Release uses the last learned skill in spellbook + } + + rsb_skillid = sd->rsb[i].skillid; + rsb_skilllv = sd->rsb[i].level; + + if( skilllv > 1 ) + sd->rsb[i].skillid = 0; // Last position - only remove it from list + else + memmove(&sd->rsb[0],&sd->rsb[1],sizeof(sd->rsb) - sizeof(sd->rsb[0])); + + if( sd->rsb[0].skillid == 0 ) + status_change_end(src, SC_READING_SB, INVALID_TIMER); + + status_change_end(src, SC_MAGICPOWER, INVALID_TIMER); + + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( !skill_check_condition_castbegin(sd,rsb_skillid,rsb_skilllv) ) + break; + + switch( skill_get_casttype(rsb_skillid) ) + { + case CAST_GROUND: + skill_castend_pos2(src,bl->x,bl->y,rsb_skillid,rsb_skilllv,tick,0); + break; + case CAST_NODAMAGE: + skill_castend_nodamage_id(src,bl,rsb_skillid,rsb_skilllv,tick,0); + break; + case CAST_DAMAGE: + skill_castend_damage_id(src,bl,rsb_skillid,rsb_skilllv,tick,0); + break; + } + + sd->ud.canact_tick = tick + skill_delayfix(src, rsb_skillid, rsb_skilllv); + clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, rsb_skillid, rsb_skilllv), 0, 0, 0); + } + else + { // Summon Balls + int j = 0, k, skele; + int spheres[5] = { 0, 0, 0, 0, 0 }, + positions[5] = {-1,-1,-1,-1,-1 }; + + for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ ) + if( sc && sc->data[i] ) + { + spheres[j] = i; + positions[j] = sc->data[i]->val2; + sc->data[i]->val2--; // Prepares for next position + j++; + } + + if( j == 0 ) + { // No Spheres + clif_skill_fail(sd,skillid,0,0); + break; + } + + // Sphere Sort + for( i = 0; i <= j - 2; i++ ) + for( k = i + 1; k <= j - 1; k++ ) + if( positions[i] > positions[k] ) + { + swap(positions[i],positions[k]); + swap(spheres[i],spheres[k]); + } + + status_change_end(src, SC_MAGICPOWER, INVALID_TIMER); + + if( skilllv == 1 ) j = 1; // Limit only to one ball + for( i = 0; i < j; i++ ) + { + skele = WL_RELEASE - 5 + sc->data[spheres[i]]->val1 - WLS_FIRE; // Convert Ball Element into Skill ATK for balls + // WL_SUMMON_ATK_FIRE, WL_SUMMON_ATK_WIND, WL_SUMMON_ATK_WATER, WL_SUMMON_ATK_GROUND + skill_addtimerskill(src,tick+status_get_adelay(src)*i,bl->id,0,0,skele,sc->data[spheres[i]]->val3,BF_MAGIC,flag|SD_LEVEL); + status_change_end(src, spheres[i], INVALID_TIMER); // Eliminate ball + } + clif_skill_nodamage(src,bl,skillid,0,1); + } + } + break; + case WL_FROSTMISTY: + { + struct status_change *tsc = status_get_sc(bl); + if( tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY]) ) + break; // Doesn't hit/cause Freezing to invisible enemy + // Causes Freezing status through walls. + sc_start(bl,status_skill2sc(skillid),20+12*skilllv+(sd ? sd->status.job_level : 50)/5,skilllv,skill_get_time(skillid,skilllv)); + // Doesn't deal damage through non-shootable walls. + if( path_search(NULL,src->m,src->x,src->y,bl->x,bl->y,1,CELL_CHKWALL) ) + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + } + break; + + case WL_JACKFROST: { + struct status_change *tsc = status_get_sc(bl); + if( tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY]) ) + break; // Do not hit invisible enemy + skill_attack(skill_get_type(skillid), src, src, bl, skillid, skilllv, tick, flag); + } + break; + /** + * Ranger + **/ + case RA_WUGSTRIKE: + case RA_WUGBITE: + if( path_search(NULL,src->m,src->x,src->y,bl->x,bl->y,1,CELL_CHKNOREACH) ) { + if( skillid == RA_WUGSTRIKE ) { + if( sd && pc_isridingwug(sd) && !map_flag_gvg(src->m) && !map[src->m].flag.battleground && unit_movepos(src,bl->x,bl->y,1,1) ) + clif_slide(src, bl->x, bl->y); + } + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + } + break; + + case RA_SENSITIVEKEEN: + if( bl->type != BL_SKILL ) { // Only Hits Invisible Targets + struct status_change * tsc = status_get_sc(bl); + if(tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK) || tsc->data[SC__INVISIBILITY]) ) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + } + else + { + struct skill_unit *su = BL_CAST(BL_SKILL,bl); + struct skill_unit_group* sg; + + if( su && (sg=su->group) && skill_get_inf2(sg->skill_id)&INF2_TRAP && sg->src_id != src->id && + battle_check_target(src, map_id2bl(sg->src_id), BCT_ENEMY) > 0 ) + { + if( sd && !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) ) + { + struct item item_tmp; + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid = ( sg->unit_id >= UNT_MAGENTATRAP && sg->unit_id <= UNT_CLUSTERBOMB )?ITEMID_TRAP_ALLOY:ITEMID_TRAP; + item_tmp.identify = 1; + if( item_tmp.nameid ) + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); + } + skill_delunit(su); + } + } + break; + /** + * Mechanic + **/ + case NC_INFRAREDSCAN: + if( flag&1 ) + { //TODO: Need a confirmation if the other type of hidden status is included to be scanned. [Jobbie] + if( rand()%100 < 50 ) + sc_start(bl, SC_INFRAREDSCAN, 10000, skilllv, skill_get_time(skillid, skilllv)); + status_change_end(bl, SC_HIDING, -1); + status_change_end(bl, SC_CLOAKING, -1); + status_change_end(bl, SC_CLOAKINGEXCEED, -1); // Need confirm it. + } + else + { + map_foreachinrange(skill_area_sub, bl, skill_get_splash(skillid, skilllv), splash_target(src), src, skillid, skilllv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); + clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + if( sd ) pc_overheat(sd,1); + } + break; + + case NC_MAGNETICFIELD: + sc_start2(bl,SC_MAGNETICFIELD,100,skilllv,src->id,skill_get_time(skillid,skilllv)); + break; case 0: if(sd) { if (flag & 3){ @@ -3153,6 +3898,11 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case AL_HEAL: case ALL_RESURRECTION: case PR_ASPERSIO: + /** + * Arch Bishop + **/ + case AB_RENOVATIO: + case AB_HIGHNESSHEAL: //Apparently only player casted skills can be offensive like this. if (sd && battle_check_undead(tstatus->race,tstatus->def_ele)) { if (battle_check_target(src, bl, BCT_ENEMY) < 1) { @@ -3186,11 +3936,17 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in { case HLIF_HEAL: //[orn] case AL_HEAL: + /** + * Arch Bishop + **/ + case AB_HIGHNESSHEAL: { int heal = skill_calc_heal(src, bl, skillid, skilllv, true); int heal_get_jobexp; - if( status_isimmune(bl) || (dstmd && (dstmd->class_ == MOBID_EMPERIUM || mob_is_battleground(dstmd))) ) + if( status_isimmune(bl) || + (dstmd && (dstmd->class_ == MOBID_EMPERIUM || mob_is_battleground(dstmd))) || + (skillid == AL_HEAL && dstsd && dstsd->sc.option&OPTION_MADOGEAR) )//Mado is immune to AL_HEAL heal=0; if( sd && dstsd && sd->status.partner_id == dstsd->status.char_id && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.sex == 0 ) @@ -3599,7 +4355,10 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in sc_start4(src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skillid, skilllv)); if (sd) skill_blockpc_start (sd, skillid, skill_get_time(skillid, skilllv)); break; - + case ASC_EDP: + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv) + ( sd ? 3000 * pc_checkskill(sd,GC_RESEARCHNEWPOISON) : 0 ))); + break; case AL_INCAGI: case AL_BLESSING: case MER_INCAGI: @@ -3640,7 +4399,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case HW_MAGICPOWER: case PF_MEMORIZE: case PA_SACRIFICE: - case ASC_EDP: case PF_DOUBLECASTING: case SG_SUN_COMFORT: case SG_MOON_COMFORT: @@ -3657,6 +4415,32 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case ST_PRESERVE: case NPC_INVINCIBLE: case NPC_INVINCIBLEOFF: + /** + * Rune Knight + **/ + case RK_DEATHBOUND: + /** + * Arch Bishop + **/ + case AB_RENOVATIO: + case AB_EXPIATIO: + case AB_DUPLELIGHT: + case AB_SECRAMENT: + /** + * Mechanic + **/ + case NC_ACCELERATION: + case NC_HOVERING: + case NC_SHAPESHIFT: + /** + * Warlock + **/ + case WL_RECOGNIZEDSPELL: + /** + * Guillotine Cross + **/ + case GC_VENOMIMPRESS: + clif_skill_nodamage(src,bl,skillid,skilllv, sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); break; @@ -3914,14 +4698,14 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case AM_PHARMACY: if(sd) { - clif_skill_produce_mix_list(sd,22); + clif_skill_produce_mix_list(sd,skillid,22); clif_skill_nodamage(src,bl,skillid,skilllv,1); } break; case SA_CREATECON: if(sd) { - clif_skill_produce_mix_list(sd,23); + clif_skill_produce_mix_list(sd,skillid,23); clif_skill_nodamage(src,bl,skillid,skilllv,1); } break; @@ -3942,12 +4726,32 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case ASC_METEORASSAULT: case GS_SPREADATTACK: + /** + * Rune Knight + **/ + case RK_STORMBLAST: + /** + * Mechanic + **/ + case NC_AXETORNADO: + /** + * Guilotine Cross + **/ + case GC_COUNTERSLASH: skill_area_temp[1] = 0; clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skillid, skilllv), splash_target(src), + i = map_foreachinrange(skill_area_sub, bl, skill_get_splash(skillid, skilllv), splash_target(src), src, skillid, skilllv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); + if( !i && skillid == NC_AXETORNADO ) + clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); break; + case NC_EMERGENCYCOOL: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + status_change_end(src,SC_OVERHEAT_LIMITPOINT,-1); + status_change_end(src,SC_OVERHEAT,-1); + break; + case NC_INFRAREDSCAN: case NPC_EARTHQUAKE: case NPC_VAMPIRE_GIFT: case NPC_HELLJUDGEMENT: @@ -4091,6 +4895,11 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in clif_skill_nodamage(src,bl,skillid,-1,status_change_end(bl, type, INVALID_TIMER)); //Hide skill-scream animation. map_freeblock_unlock(); return 0; + } else if( tsc && tsc->option&OPTION_MADOGEAR ) { + //Mado Gear cannot hide + if( sd ) clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 0; } clif_skill_nodamage(src,bl,skillid,-1,sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); break; @@ -4106,6 +4915,11 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in clif_walkok(sd); // So aegis has to resend the walk ok. break; case AS_CLOAKING: + case RA_CAMOUFLAGE: + /** + * Guilotine Cross + **/ + case GC_CLOAKINGEXCEED: if (tsce) { i = status_change_end(bl, type, INVALID_TIMER); @@ -4425,6 +5239,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case RG_STRIPARMOR: case RG_STRIPHELM: case ST_FULLSTRIP: + case GC_WEAPONCRUSH: { unsigned short location = 0; int d = 0; @@ -4443,6 +5258,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in switch (skillid) { case RG_STRIPWEAPON: + case GC_WEAPONCRUSH: location = EQP_WEAPON; break; case RG_STRIPSHIELD: @@ -4460,14 +5276,14 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in } //Special message when trying to use strip on FCP [Jobbie] - if( sd && skillid == ST_FULLSTRIP && tsc && tsc->data[SC_CP_WEAPON] && tsc->data[SC_CP_HELM] && tsc->data[SC_CP_ARMOR] && tsc->data[SC_CP_SHIELD] ) + if( sd && skillid == ST_FULLSTRIP && tsc && tsc->data[SC_CP_WEAPON] && tsc->data[SC_CP_HELM] && tsc->data[SC_CP_ARMOR] && tsc->data[SC_CP_SHIELD]) { clif_gospel_info(sd, 0x28); break; } //Attempts to strip at rate i and duration d - if( (i = skill_strip_equip(bl, location, i, skilllv, d)) || skillid != ST_FULLSTRIP ) + if( (i = skill_strip_equip(bl, location, i, skilllv, d)) || (skillid != ST_FULLSTRIP && skillid != GC_WEAPONCRUSH ) ) clif_skill_nodamage(src,bl,skillid,skilllv,i); //Nothing stripped. @@ -4631,7 +5447,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in clif_skill_nodamage(src,bl,skillid,skilllv,1); if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) || (tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SL_ROGUE) //Rogue's spirit defends againt dispel. - || rand()%100 >= 50+10*skilllv) + || rand()%100 >= 50+10*skilllv + || ( tsc && tsc->option&OPTION_MADOGEAR ) )//Mado Gear is immune to dispell according to bug report 49 [Ind] { if (sd) clif_skill_fail(sd,skillid,0,0); @@ -5654,6 +6471,610 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in clif_skill_nodamage(src, bl, skillid, skilllv, buyingstore_setup(sd, MAX_BUYINGSTORE_SLOTS)); } break; + case RK_ENCHANTBLADE: + clif_skill_nodamage(src,bl,skillid,skilllv,// formula not confirmed + sc_start2(bl,type,100,skilllv,100+20*skilllv/*+sstatus->int_/2+status_get_lv(bl)/10*/,skill_get_time(skillid,skilllv))); + break; + case RK_DRAGONHOWLING: + if( flag&1) + sc_start(bl,type,50 + 6 * skilllv,skilllv,skill_get_time(skillid,skilllv)); + else + { + skill_area_temp[2] = 0; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_area_sub, src, + skill_get_splash(skillid,skilllv),BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_ENEMY|SD_PREAMBLE|1, + skill_castend_nodamage_id); + } + break; + case RK_IGNITIONBREAK: + //case LG_EARTHDRIVE: + clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + //if( skillid == LG_EARTHDRIVE ) + //{ + // int dummy = 1; + // i = skill_get_splash(skillid,skilllv); + // map_foreachinarea(skill_cell_overlap, src->m, src->x-i, src->y-i, src->x+i, src->y+i, BL_SKILL, LG_EARTHDRIVE, &dummy, src); + //} + map_foreachinrange(skill_area_sub, bl,skill_get_splash(skillid,skilllv),BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); + break; + case RK_STONEHARDSKIN: + if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 4 ) + { + int heal = sstatus->hp / 4; // 25% HP + if( status_charge(bl,heal,0) ) + clif_skill_nodamage(src,bl,skillid,skilllv,sc_start2(bl,type,100,skilllv,heal,skill_get_time(skillid,skilllv))); + else + clif_skill_fail(sd,skillid,0,0); + } + break; + case RK_REFRESH: + if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 8 ) + { + int heal = status_get_max_hp(bl) * 25 / 100; + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + status_heal(bl,heal,0,1); + status_change_clear_buffs(bl,2); + } + break; + + case RK_MILLENNIUMSHIELD: + if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 9 ) + { + short shields = (rand()%100<50) ? 4 : ((rand()%100<80) ? 3 : 2); + sc_start4(bl,type,100,skilllv,shields,1000,0,skill_get_time(skillid,skilllv)); + clif_millenniumshield(sd,shields); + clif_skill_nodamage(src,bl,skillid,1,1); + } + break; + + case RK_GIANTGROWTH: + case RK_VITALITYACTIVATION: + case RK_ABUNDANCE: + if( sd ) + { + int lv = 1; // RK_GIANTGROWTH + if( skillid == RK_VITALITYACTIVATION ) + lv = 2; + else if( skillid == RK_ABUNDANCE ) + lv = 6; + if( pc_checkskill(sd,RK_RUNEMASTERY) >= lv ) + clif_skill_nodamage(src,bl,skillid,skilllv,sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + } + break; + + case RK_FIGHTINGSPIRIT: + if( flag&1 ) { + if( src == bl ) + sc_start2(bl,type,100,skill_area_temp[5],10*(sd?pc_checkskill(sd,RK_RUNEMASTERY):10),skill_get_time(skillid,skilllv)); + else + sc_start(bl,type,100,skill_area_temp[5]/4,skill_get_time(skillid,skilllv)); + } else if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 5 ) { + if( sd->status.party_id ) { + i = party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skillid,skilllv),src,skillid,skilllv,tick,BCT_PARTY,skill_area_sub_count); + skill_area_temp[5] = 7 * i; // ATK + party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skillid,skilllv),src,skillid,skilllv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id); + } else + sc_start2(bl,type,100,7,5,skill_get_time(skillid,skilllv)); + } + clif_skill_nodamage(src,bl,skillid,1,1); + break; + /** + * Guilotine Cross + **/ + case GC_ROLLINGCUTTER: + { + short count = 1; + skill_area_temp[2] = 0; + map_foreachinrange(skill_area_sub,src,skill_get_splash(skillid,skilllv),BL_CHAR,src,skillid,skilllv,tick,flag|BCT_ENEMY|SD_PREAMBLE|SD_SPLASH|1,skill_castend_damage_id); + if( tsc && tsc->data[SC_ROLLINGCUTTER] ) + { // Every time the skill is casted the status change is reseted adding a counter. + count += (short)tsc->data[SC_ROLLINGCUTTER]->val1; + if( count > 10 ) + count = 10; // Max coounter + status_change_end(bl, SC_ROLLINGCUTTER, INVALID_TIMER); + } + sc_start(bl,SC_ROLLINGCUTTER,100,count,skill_get_time(skillid,skilllv)); + clif_skill_nodamage(src,src,skillid,skilllv,1); + } + break; + + case GC_WEAPONBLOCKING: + if( tsc && tsc->data[SC_WEAPONBLOCKING] ) + status_change_end(bl, SC_WEAPONBLOCKING, INVALID_TIMER); + else + sc_start(bl,SC_WEAPONBLOCKING,100,skilllv,skill_get_time(skillid,skilllv)); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + + case GC_CREATENEWPOISON: + if( sd ) + { + clif_skill_produce_mix_list(sd,skillid,25); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + } + break; + + case GC_POISONINGWEAPON: + if( sd ) { + clif_poison_list(sd,skilllv); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case GC_ANTIDOTE: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( tsc ) + { + status_change_end(bl, SC_PARALYSE, INVALID_TIMER); + status_change_end(bl, SC_PYREXIA, INVALID_TIMER); + status_change_end(bl, SC_DEATHHURT, INVALID_TIMER); + status_change_end(bl, SC_LEECHESEND, INVALID_TIMER); + status_change_end(bl, SC_VENOMBLEED, INVALID_TIMER); + status_change_end(bl, SC_MAGICMUSHROOM, INVALID_TIMER); + status_change_end(bl, SC_TOXIN, INVALID_TIMER); + status_change_end(bl, SC_OBLIVIONCURSE, INVALID_TIMER); + } + break; + + case GC_PHANTOMMENACE: + clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_area_sub,src,skill_get_splash(skillid,skilllv),BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); + break; + + case GC_HALLUCINATIONWALK: + { + int heal = status_get_max_hp(bl) / 10; + if( status_get_hp(bl) < heal ) { // if you haven't enough HP skill fails. + if( sd ) clif_skill_fail(sd,skillid,0x02,0); + break; + } + if( !status_charge(bl,heal,0) ) + { + if( sd ) clif_skill_fail(sd,skillid,0x02,0); + break; + } + clif_skill_nodamage(src,bl,skillid,skilllv,sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + } + break; + /** + * Arch Bishop + **/ + case AB_ANCILLA: + if( sd ) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_produce_mix(sd, skillid, ITEMID_ANCILLA, 0, 0, 0, 1); + } + break; + + case AB_CLEMENTIA: + case AB_CANTO: + { + int bless_lv = pc_checkskill(sd,AL_BLESSING); + int agi_lv = pc_checkskill(sd,AL_INCAGI); + if( sd == NULL || sd->status.party_id == 0 || flag&1 ) + clif_skill_nodamage(bl, bl, skillid, skilllv, sc_start(bl,type,100, + (skillid == AB_CLEMENTIA)? bless_lv : (skillid == AB_CANTO)? agi_lv : skilllv, skill_get_time(skillid,skilllv))); + else if( sd ) + party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); + } + break; + + case AB_PRAEFATIO: + if( sd == NULL || sd->status.party_id == 0 || flag&1 ) + clif_skill_nodamage(bl, bl, skillid, skilllv, sc_start4(bl, type, 100, skilllv, 0, 0, 1, skill_get_time(skillid, skilllv))); + else if( sd ) + party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); + break; + + case AB_CHEAL: + if( sd == NULL || sd->status.party_id == 0 || flag&1 ) + { + if( sd && tstatus && !battle_check_undead(tstatus->race, tstatus->def_ele) ) + { + i = skill_calc_heal(src, bl, AL_HEAL, pc_checkskill(sd, AL_HEAL), true); + status_heal(bl, i, 0, 1); + clif_skill_nodamage(bl, bl, skillid, i, 1); + } + } + else if( sd ) + party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); + break; + + case AB_ORATIO: + if( flag&1 ) + sc_start(bl, type, 40 + 5 * skilllv, skilllv, skill_get_time(skillid, skilllv)); + else + { + map_foreachinrange(skill_area_sub, src, skill_get_splash(skillid, skilllv), BL_CHAR, + src, skillid, skilllv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + } + break; + + case AB_LAUDAAGNUS: + if( flag&1 || sd == NULL ) + { + if( (tsc && (tsc->data[SC_FREEZE] || tsc->data[SC_STONE] || + tsc->data[SC_BLIND]))&& (rand()%100 < 30+5*skilllv) ) + { + status_change_end(bl, SC_FREEZE, -1); + status_change_end(bl, SC_STONE, -1); + status_change_end(bl, SC_BLIND, -1); + } + // Success rate only applies to the curing effect and not stat bonus. + clif_skill_nodamage(bl, bl, skillid, skilllv, + sc_start(bl, type, 100, skilllv, skill_get_time(skillid, skilllv))); + } + else if( sd ) + party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), + src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); + break; + + case AB_LAUDARAMUS: + if( flag&1 || sd == NULL ) + { + if( (tsc && (tsc->data[SC_SLEEP] || tsc->data[SC_STUN] || + tsc->data[SC_SILENCE]))&& (rand()%100 < 30+5*skilllv) ) + { + status_change_end(bl, SC_SLEEP, -1); + status_change_end(bl, SC_STUN, -1); + status_change_end(bl, SC_SILENCE, -1); + } + clif_skill_nodamage(bl, bl, skillid, skilllv, + sc_start(bl, type, 100, skilllv, skill_get_time(skillid, skilllv))); + //Success rate only applies to the curing effect and not stat bonus. + } + else if( sd ) + party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), + src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); + break; + + case AB_CLEARANCE: + if( flag&1 || (i = skill_get_splash(skillid, skilllv)) < 1 ) + { //As of the behavior in official server Clearance is just a super version of Dispell skill. [Jobbie] + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) || rand()%100 >= 30 + 10 * skilllv) + { + if (sd) + clif_skill_fail(sd,skillid,0,0); + break; + } + if(status_isimmune(bl) || !tsc || !tsc->count) + break; + for(i=0;i<SC_MAX;i++) + { + if (!tsc->data[i]) + continue; + switch (i) { + case SC_WEIGHT50: case SC_WEIGHT90: case SC_HALLUCINATION: + case SC_STRIPWEAPON: case SC_STRIPSHIELD: case SC_STRIPARMOR: + case SC_STRIPHELM: case SC_CP_WEAPON: case SC_CP_SHIELD: + case SC_CP_ARMOR: case SC_CP_HELM: case SC_COMBO: + case SC_STRFOOD: case SC_AGIFOOD: case SC_VITFOOD: + case SC_INTFOOD: case SC_DEXFOOD: case SC_LUKFOOD: + case SC_HITFOOD: case SC_FLEEFOOD: case SC_BATKFOOD: + case SC_WATKFOOD: case SC_MATKFOOD: case SC_DANCING: + case SC_GUILDAURA: case SC_SPIRIT: case SC_AUTOBERSERK: + case SC_CARTBOOST: case SC_MELTDOWN: case SC_SAFETYWALL: + case SC_SMA: case SC_SPEEDUP0: case SC_NOCHAT: + case SC_ANKLE: case SC_SPIDERWEB: case SC_JAILED: + case SC_ITEMBOOST: case SC_EXPBOOST: case SC_LIFEINSURANCE: + case SC_BOSSMAPINFO: case SC_PNEUMA: case SC_AUTOSPELL: + case SC_INCHITRATE: case SC_INCATKRATE: case SC_NEN: + case SC_READYSTORM: case SC_READYDOWN: case SC_READYTURN: + case SC_READYCOUNTER:case SC_DODGE: case SC_WARM: + case SC_SPEEDUP1: case SC_AUTOTRADE: case SC_CRITICALWOUND: + case SC_JEXPBOOST: case SC_INVINCIBLE: case SC_INVINCIBLEOFF: + case SC_HELLPOWER: case SC_MANU_ATK: case SC_MANU_DEF: + case SC_SPL_ATK: case SC_SPL_DEF: case SC_MANU_MATK: + case SC_SPL_MATK: case SC_RICHMANKIM: case SC_ETERNALCHAOS: + case SC_DRUMBATTLE: case SC_NIBELUNGEN: case SC_ROKISWEIL: + case SC_INTOABYSS: case SC_SIEGFRIED: case SC_WHISTLE: + case SC_ASSNCROS: case SC_POEMBRAGI: case SC_APPLEIDUN: + case SC_HUMMING: case SC_DONTFORGETME: case SC_FORTUNE: + case SC_SERVICE4U: case SC_FOOD_STR_CASH: case SC_FOOD_AGI_CASH: + case SC_FOOD_VIT_CASH: case SC_FOOD_DEX_CASH: case SC_FOOD_INT_CASH: + case SC_FOOD_LUK_CASH: /* case SC_ELECTRICSHOCKER: case SC_BITE: + case SC__STRIPACCESSORY: case SC__ENERVATION: case SC__GROOMY: + case SC__IGNORANCE: case SC__LAZINESS: case SC__UNLUCKY: + case SC__WEAKNESS: case SC_SAVAGE_STEAK: case SC_COCKTAIL_WARG_BLOOD: + case SC_MAGNETICFIELD:case SC_MINOR_BBQ: case SC_SIROMA_ICE_TEA: + case SC_DROCERA_HERB_STEAMED: case SC_PUTTI_TAILS_NOODLES: + case SC_NEUTRALBARRIER_MASTER: case SC_NEUTRALBARRIER: + case SC_STEALTHFIELD_MASTER: case SC_STEALTHFIELD: */ + continue; + case SC_ASSUMPTIO: + if( bl->type == BL_MOB ) + continue; + break; + } + if(i==SC_BERSERK /*|| i==SC_SATURDAYNIGHTFEVER*/) tsc->data[i]->val2=0; //Mark a dispelled berserk to avoid setting hp to 100 by setting hp penalty to 0. + status_change_end(bl,(sc_type)i,-1); + } + break; + } + map_foreachinrange(skill_area_sub, bl, i, BL_CHAR, src, skillid, skilllv, tick, flag|1, skill_castend_damage_id); + break; + + case AB_SILENTIUM: + // Should the level of Lex Divina be equivalent to the level of Silentium or should the highest level learned be used? [LimitLine] + map_foreachinrange(skill_area_sub, src, skill_get_splash(skillid, skilllv), BL_CHAR, + src, PR_LEXDIVINA, skilllv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + break; + /** + * Warlock + **/ + case WL_STASIS: + if( flag&1 ) + sc_start2(bl,type,100,skilllv,src->id,skill_get_time(skillid,skilllv)); + else + { + map_foreachinrange(skill_area_sub,src,skill_get_splash(skillid, skilllv),BL_CHAR,src,skillid,skilllv,tick,(map_flag_vs(src->m)?BCT_ALL:BCT_ENEMY|BCT_SELF)|flag|1,skill_castend_nodamage_id); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + } + break; + + case WL_WHITEIMPRISON: + if( !(tsc && tsc->data[type]) && (src == bl || battle_check_target(src, bl, BCT_ENEMY)) ) + { + int rate = 50 + 3 * skilllv + ( sd? sd->status.job_level : 50 ) / 4; + i = sc_start2(bl,type,rate,skilllv,src->id,(src == bl)?skill_get_time2(skillid,skilllv):skill_get_time(skillid, skilllv)); + clif_skill_nodamage(src,bl,skillid,skilllv,i); + if( sd && i ) + skill_blockpc_start(sd,skillid,4000); // Reuse Delay only activated on success + } + else if( sd ) + clif_skill_fail(sd,skillid,0,0); + break; + + case WL_FROSTMISTY: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_area_sub,bl,skill_get_splash(skillid,skilllv),BL_CHAR|BL_SKILL,src,skillid,skilllv,tick,flag|BCT_ENEMY,skill_castend_damage_id); + break; + + case WL_JACKFROST: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinshootrange(skill_area_sub,bl,skill_get_splash(skillid,skilllv),BL_CHAR|BL_SKILL,src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); + break; + + case WL_MARSHOFABYSS: + // Should marsh of abyss still apply half reduction to players after the 28/10 patch? [LimitLine] + clif_skill_nodamage(src, bl, skillid, skilllv, + sc_start4(bl, type, 100, skilllv, status_get_int(src), sd ? sd->status.job_level : 50, 0, + skill_get_time(skillid, skilllv))); + break; + + case WL_SIENNAEXECRATE: + if( status_isimmune(bl) || !tsc ) + break; + + if( flag&1 ) + { + if( bl->id == skill_area_temp[1] ) + break; // Already work on this target + + if( tsc && tsc->data[SC_STONE] ) + status_change_end(bl,SC_STONE,-1); + else + status_change_start(bl,SC_STONE,10000,skilllv,0,0,1000,(8+2*skilllv)*1000,2); + } + else + { + int rate = 40 + 8 * skilllv + ( sd? sd->status.job_level : 50 ) / 4; + // IroWiki says Rate should be reduced by target stats, but currently unknown + if( rand()%100 < rate ) + { // Success on First Target + rate = 0; + if( !tsc->data[SC_STONE] ) + rate = status_change_start(bl,SC_STONE,10000,skilllv,0,0,1000,(8+2*skilllv)*1000,2); + else + { + rate = 1; + status_change_end(bl,SC_STONE,-1); + } + + if( rate ) + { + skill_area_temp[1] = bl->id; + map_foreachinrange(skill_area_sub,bl,skill_get_splash(skillid,skilllv),BL_CHAR,src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_nodamage_id); + } + // Doesn't send failure packet if it fails on defense. + } + else if( sd ) // Failure on Rate + clif_skill_fail(sd,skillid,0,0); + } + break; + + case WL_SUMMONFB: + case WL_SUMMONBL: + case WL_SUMMONWB: + case WL_SUMMONSTONE: + { + short element = 0, sctype = 0, pos = -1; + struct status_change *sc = status_get_sc(src); + if( !sc ) break; + + for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ ) + { + if( !sctype && !sc->data[i] ) + sctype = i; // Take the free SC + if( sc->data[i] ) + pos = max(sc->data[i]->val2,pos); + } + + if( !sctype ) + { + if( sd ) // No free slots to put SC + clif_skill_fail(sd,skillid,0x13,0); + break; + } + + pos++; // Used in val2 for SC. Indicates the order of this ball + switch( skillid ) + { // Set val1. The SC element for this ball + case WL_SUMMONFB: element = WLS_FIRE; break; + case WL_SUMMONBL: element = WLS_WIND; break; + case WL_SUMMONWB: element = WLS_WATER; break; + case WL_SUMMONSTONE: element = WLS_STONE; break; + } + + sc_start4(src,sctype,100,element,pos,skilllv,0,skill_get_time(skillid,skilllv)); + clif_skill_nodamage(src,bl,skillid,0,0); + } + break; + + case WL_READING_SB: + if( sd ) + { + int i, preserved = 0, max_preserve = 4 * pc_checkskill(sd,WL_FREEZE_SP) + sstatus->int_ / 10 + sd->status.base_level / 10; + ARR_FIND(0, MAX_SPELLBOOK, i, sd->rsb[i].skillid == 0); // Search for a Free Slot + if( i == MAX_SPELLBOOK ) + { + clif_skill_fail(sd,skillid,0x04,0); + break; + } + for( i = 0; i < MAX_SPELLBOOK && sd->rsb[i].skillid; i++ ) + preserved += sd->rsb[i].points; + + if( preserved >= max_preserve ) + { + clif_skill_fail(sd,skillid,0x04,0); + break; + } + + sc_start(bl,SC_STOP,100,skilllv,-1); //Can't move while selecting a spellbook. + clif_spellbook_list(sd); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + /** + * Ranger + **/ + case RA_FEARBREEZE: + clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + clif_skill_nodamage(src, bl, skillid, skilllv, sc_start(bl, type, 100, skilllv, skill_get_time(skillid, skilllv))); + break; + + case RA_WUGMASTERY: + if( sd ) + { + if( pc_isridingwug(sd) ) + clif_skill_fail(sd,skillid,0,0); + else if( !pc_iswug(sd) ) + pc_setoption(sd,sd->sc.option|OPTION_WUG); + else + pc_setoption(sd,sd->sc.option&~OPTION_WUG); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case RA_WUGRIDER: + if( sd ) { + if( !pc_isridingwug(sd) && pc_iswug(sd) ) { + pc_setoption(sd,sd->sc.option&~OPTION_WUG); + pc_setoption(sd,sd->sc.option|OPTION_WUGRIDER); + } else if( pc_isridingwug(sd) ) { + pc_setoption(sd,sd->sc.option&~OPTION_WUGRIDER); + pc_setoption(sd,sd->sc.option|OPTION_WUG); + } else if( sd ) { + clif_skill_fail(sd,skillid,0,0); + } + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case RA_WUGDASH: + if( tsce ) { + clif_skill_nodamage(src,bl,skillid,skilllv,status_change_end(bl, type, -1)); + map_freeblock_unlock(); + return 0; + } + if( sd && pc_isridingwug(sd) ) { + clif_skill_nodamage(src,bl,skillid,skilllv,sc_start4(bl,type,100,skilllv,unit_getdir(bl),0,0,1)); + clif_walkok(sd); + } + break; + + case RA_SENSITIVEKEEN: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + map_foreachinrange(skill_area_sub,src,skill_get_splash(skillid,skilllv),BL_CHAR|BL_SKILL,src,skillid,skilllv,tick,flag|BCT_ENEMY,skill_castend_damage_id); + break; + /** + * Mechanic + **/ + case NC_F_SIDESLIDE: + case NC_B_SIDESLIDE: + { + int dir = (skillid == NC_F_SIDESLIDE) ? (unit_getdir(src)+4)%8 : unit_getdir(src); + skill_blown(src,bl,skill_get_blewcount(skillid,skilllv),dir,0x1); + clif_slide(src,src->x,src->y); + clif_fixpos(src); //Aegis sent this packet + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case NC_SELFDESTRUCTION: + if( sd ) { + if( sd->sc.option&OPTION_MADOGEAR ) + pc_setoption(sd, sd->sc.option&~OPTION_MADOGEAR); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + skill_castend_damage_id(src, src, skillid, skilllv, tick, flag); + } + break; + + case NC_ANALYZE: + clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + clif_skill_nodamage(src, bl, skillid, skilllv, + sc_start(bl,type, 30 + 12 * skilllv,skilllv,skill_get_time(skillid,skilllv))); + if( sd ) pc_overheat(sd,1); + break; + + case NC_MAGNETICFIELD: + if( (i = sc_start2(bl,type,100,skilllv,src->id,skill_get_time(skillid,skilllv))) ) + { + map_foreachinrange(skill_area_sub,src,skill_get_splash(skillid,skilllv),splash_target(src),src,skillid,skilllv,tick,flag|BCT_ENEMY|SD_SPLASH|1,skill_castend_damage_id);; + clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skillid,skilllv,6); + pc_overheat(sd,1); + } + clif_skill_nodamage(src,src,skillid,skilllv,i); + break; + + case NC_REPAIR: + if( sd ) + { + int heal; + if( dstsd && (dstsd->sc.option&OPTION_MADOGEAR) ) + { + heal = dstsd->status.max_hp * (3+3*skilllv) / 100; + status_heal(bl,heal,0,2); + } else { + heal = sd->status.max_hp * (3+3*skilllv) / 100; + status_heal(src,heal,0,2); + } + + clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + clif_skill_nodamage(src, bl, skillid, skilllv, heal); + } + break; + + case NC_DISJOINT: + { + if( bl->type != BL_MOB ) break; + md = map_id2md(bl->id); + if( md && md->class_ >= 2042 && md->class_ <= 2046 ) + status_kill(bl); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + } + break; + default: ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skillid); clif_skill_nodamage(src,bl,skillid,skilllv,1); @@ -5858,13 +7279,15 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data) if (ud->state.running && ud->skillid == TK_JUMPKICK) flag = 1; - if (ud->walktimer != INVALID_TIMER && ud->skillid != TK_RUN) + if (ud->walktimer != INVALID_TIMER && ud->skillid != TK_RUN && ud->skillid != RA_WUGDASH) unit_stop_walking(src,1); if( !sd || sd->skillitem != ud->skillid || skill_get_delay(ud->skillid,ud->skilllv) ) ud->canact_tick = tick + skill_delayfix(src, ud->skillid, ud->skilllv); //Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish] + if( sd && skill_get_cooldown(ud->skillid,ud->skilllv) > 0 ) + skill_blockpc_start(sd, ud->skillid, skill_get_cooldown(ud->skillid, ud->skilllv)); if( battle_config.display_status_timers && sd ) - clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skillid, ud->skilllv)); + clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skillid, ud->skilllv), 0, 0, 0); if( sd ) { switch( ud->skillid ) @@ -5900,7 +7323,7 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data) sc = status_get_sc(src); if(sc && sc->count) { if(sc->data[SC_MAGICPOWER] && - ud->skillid != HW_MAGICPOWER && ud->skillid != WZ_WATERBALL) + ud->skillid != HW_MAGICPOWER && ud->skillid != WZ_WATERBALL && ud->skillid != WL_TETRAVORTEX) status_change_end(src, SC_MAGICPOWER, INVALID_TIMER); if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_WIZARD && @@ -6077,7 +7500,7 @@ int skill_castend_pos(int tid, unsigned int tick, int id, intptr_t data) if( !sd || sd->skillitem != ud->skillid || skill_get_delay(ud->skillid,ud->skilllv) ) ud->canact_tick = tick + skill_delayfix(src, ud->skillid, ud->skilllv); if( battle_config.display_status_timers && sd ) - clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skillid, ud->skilllv)); + clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skillid, ud->skilllv), 0, 0, 0); // if( sd ) // { // switch( ud->skillid ) @@ -6209,6 +7632,9 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk case MG_SAFETYWALL: case MG_FIREWALL: case MG_THUNDERSTORM: +#if FIREIVY_ON + case WZ_FIREIVY: +#endif case AL_PNEUMA: case WZ_ICEWALL: case WZ_FIREPILLAR: @@ -6271,6 +7697,17 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk case NJ_RAIGEKISAI: case NJ_KAMAITACHI: case NPC_EVILLAND: + /** + * Ranger + **/ + case RA_ELECTRICSHOCKER: + case RA_CLUSTERBOMB: + case RA_MAGENTATRAP: + case RA_COBALTTRAP: + case RA_MAIZETRAP: + case RA_VERDURETRAP: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: flag|=1;//Set flag to 1 to prevent deleting ammo (it will be deleted on group-delete). case GS_GROUNDDRIFT: //Ammo should be deleted right away. skill_unitsetting(src,skillid,skilllv,x,y,0); @@ -6518,6 +7955,118 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk } } break; + /** + * Mechanic + **/ + case NC_COLDSLOWER: + case NC_ARMSCANNON: + /** + * Rune Knight + **/ + case RK_DRAGONBREATH: + case RK_WINDCUTTER: + i = skill_get_splash(skillid,skilllv); + map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); + break; + /** + * Guilotine Cross + **/ + case GC_POISONSMOKE: + if( !(sc && sc->data[SC_POISONINGWEAPON]) ) { + if( sd ) + clif_skill_fail(sd,skillid,0x20,0); + return 0; + } + clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skillid,skilllv,6); + skill_unitsetting(src, skillid, skilllv, x, y, flag); + status_change_end(src,SC_POISONINGWEAPON,-1); + break; + /** + * Arch Bishop + **/ + case AB_EPICLESIS: + if( (sg = skill_unitsetting(src, skillid, skilllv, x, y, 0)) ) { + i = sg->unit->range; + map_foreachinarea(skill_area_sub, src->m, x - i, y - i, x + i, y + i, BL_CHAR, src, ALL_RESURRECTION, 1, tick, flag|BCT_NOENEMY|1,skill_castend_nodamage_id); + } + break; + /** + * Warlock + **/ + case WL_COMET: + if( sc ) { + sc->comet_x = x; + sc->comet_y = y; + } + i = skill_get_splash(skillid,skilllv); + map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,BL_CHAR,src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); + break; + + case WL_EARTHSTRAIN: + { + int i, wave = skilllv + 4, dir = map_calc_dir(src,x,y); + int sx = x, sy = y; + + if( sc && sc->data[SC_MAGICPOWER] ) + flag = flag|2; //Store the magic power flag + + for( i = 0; i < wave; i++ ) + { + switch( dir ) + { + case 0: case 1: case 7: sy = src->y + i; break; + case 3: case 4: case 5: sy = src->y - i; break; + case 2: sx = src->x - i; break; + case 6: sx = src->x + i; break; + } + skill_addtimerskill(src,gettick() + (200 * i),0,sx,sy,skillid,skilllv,dir,flag&2); // Temp code until animation is replaced. [Rytech] + //skill_addtimerskill(src,gettick() + (150 * i),0,sx,sy,skillid,skilllv,dir,flag&2); // Official steping timer, but disabled due to too much noise. + } + } + break; + /** + * Ranger + **/ + case RA_DETONATOR: + i = skill_get_splash(skillid, skilllv); + map_foreachinarea(skill_detonator, src->m, x-i, y-i, x+i, y+i, BL_SKILL, src); + clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + break; + /** + * Mechanic + **/ + case NC_NEUTRALBARRIER: + case NC_STEALTHFIELD: + skill_clear_unitgroup(src); // To remove previous skills - cannot used combined + if( (sg = skill_unitsetting(src,skillid,skilllv,src->x,src->y,0)) != NULL ) + { + sc_start2(src,skillid == NC_NEUTRALBARRIER ? SC_NEUTRALBARRIER_MASTER : SC_STEALTHFIELD_MASTER,100,skilllv,sg->group_id,skill_get_time(skillid,skilllv)); + if( sd ) pc_overheat(sd,1); + } + break; + + case NC_SILVERSNIPER: + { + int class_ = 2042; + struct mob_data *md; + + md = mob_once_spawn_sub(src, src->m, x, y, status_get_name(src), class_, ""); + if( md ) + { + md->master_id = src->id; + md->special_state.ai = 3; + if( md->deletetimer != INVALID_TIMER ) + delete_timer(md->deletetimer, mob_timer_delete); + md->deletetimer = add_timer (gettick() + skill_get_time(skillid, skilllv), mob_timer_delete, md->bl.id, 0); + mob_spawn( md ); + } + } + break; + + case NC_MAGICDECOY: + if( sd ) clif_magicdecoy_list(sd,skilllv,x,y); + break; default: ShowWarning("skill_castend_pos2: Unknown skill used:%d\n",skillid); @@ -6566,7 +8115,12 @@ int skill_castend_map (struct map_session_data *sd, short skill_num, const char sd->sc.data[SC_DANCING] || sd->sc.data[SC_BERSERK] || sd->sc.data[SC_BASILICA] || - sd->sc.data[SC_MARIONETTE] + sd->sc.data[SC_MARIONETTE] || + /** + * Warlock + **/ + sd->sc.data[SC_WHITEIMPRISON] || + (sd->sc.data[SC_STASIS] && skill_stasis_check(&sd->bl, sd->sc.data[SC_STASIS]->val2, skill_num)) )) { skill_failed(sd); return 0; @@ -6855,6 +8409,17 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli case HT_FREEZINGTRAP: case MA_FREEZINGTRAP: case HT_BLASTMINE: + /** + * Ranger + **/ + case RA_ELECTRICSHOCKER: + case RA_CLUSTERBOMB: + case RA_MAGENTATRAP: + case RA_COBALTTRAP: + case RA_MAIZETRAP: + case RA_VERDURETRAP: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: if( map_flag_gvg(src->m) || map[src->m].flag.battleground ) limit *= 4; // longer trap times in WOE [celest] if( battle_config.vs_traps_bctall && map_flag_vs(src->m) && (src->type&battle_config.vs_traps_bctall) ) @@ -7004,6 +8569,17 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli break; } + /** + * Guilotine Cross + **/ + case GC_POISONSMOKE: + if( !(sc && sc->data[SC_POISONINGWEAPON]) ) + return NULL; + val1 = sc->data[SC_POISONINGWEAPON]->val1; // Level of Poison, to determine poisoning time + val2 = sc->data[SC_POISONINGWEAPON]->val2; // Type of Poison + limit = 4000 + 2000 * skilllv; + break; + } nullpo_retr(NULL, group=skill_initunitgroup(src,layout->count,skillid,skilllv,skill_get_unit_id(skillid,flag&1)+subunt, limit, interval)); @@ -7078,6 +8654,17 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli case HT_TALKIEBOX: case HT_SKIDTRAP: case MA_SKIDTRAP: + /** + * Ranger + **/ + case RA_ELECTRICSHOCKER: + case RA_CLUSTERBOMB: + case RA_MAGENTATRAP: + case RA_COBALTTRAP: + case RA_MAIZETRAP: + case RA_VERDURETRAP: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: val1 = 3500; break; case GS_DESPERADO: @@ -7157,8 +8744,8 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un sc = status_get_sc(bl); - if (sc && sc->option&OPTION_HIDE && sg->skill_id != WZ_HEAVENDRIVE) - return 0; //Hidden characters are immune to AoE skills except Heaven's Drive. [Skotlex] + if (sc && sc->option&OPTION_HIDE && sg->skill_id != WZ_HEAVENDRIVE && sg->skill_id != WL_EARTHSTRAIN ) + return 0; //Hidden characters are immune to AoE skills except to these. [Skotlex] type = status_skill2sc(sg->skill_id); sce = (sc && type != -1)?sc->data[type]:NULL; @@ -7541,6 +9128,16 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns case UNT_FLASHER: case UNT_FREEZINGTRAP: case UNT_FIREPILLAR_ACTIVE: + /** + * Ranger + **/ + case UNT_CLUSTERBOMB: + case UNT_MAGENTATRAP: + case UNT_COBALTTRAP: + case UNT_MAIZETRAP: + case UNT_VERDURETRAP: + case UNT_FIRINGTRAP: + case UNT_ICEBOUNDTRAP: map_foreachinrange(skill_trap_splash,&src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl,tick); if (sg->unit_id != UNT_FIREPILLAR_ACTIVE) clif_changetraplook(&src->bl, sg->unit_id==UNT_LANDMINE?UNT_FIREPILLAR_ACTIVE:UNT_USED_TRAPS); @@ -7707,6 +9304,10 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns break; case UNT_GRAVITATION: + case UNT_EARTHSTRAIN: + case UNT_FIREWALK: + case UNT_ELECTRICWALK: + case UNT_PSYCHIC_WAVE: skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); break; @@ -7722,6 +9323,71 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns //clif_changetraplook(&src->bl, UNT_FIREPILLAR_ACTIVE); sg->limit=DIFF_TICK(tick,sg->tick)+1500; break; + /** + * 3rd stuff + **/ + case UNT_POISONSMOKE: + if( battle_check_target(ss,bl,BCT_ENEMY) > 0 && !(tsc && tsc->data[sg->val2]) && rand()%100 < 20 ) + sc_start(bl,sg->val2,100,sg->val1,skill_get_time2(GC_POISONINGWEAPON,sg->val1)); + break; + + case UNT_EPICLESIS: + if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON ) + { + int hp, sp; + switch( sg->skill_lv ) + { + case 1: case 2: hp = 3; sp = 2; break; + case 3: case 4: hp = 4; sp = 3; break; + case 5: default: hp = 5; sp = 4; break; + } + hp = tstatus->max_hp * hp / 100; + sp = tstatus->max_sp * sp / 100; + status_heal(bl, hp, sp, 0); + if( tstatus->hp < tstatus->max_hp ) + clif_skill_nodamage(&src->bl, bl, AL_HEAL, hp, 1); + if( tstatus->sp < tstatus->max_sp ) + clif_skill_nodamage(&src->bl, bl, MG_SRECOVERY, sp, 1); + sc_start(bl, type, 100, sg->skill_lv, sg->interval + 100); + sg->val2++; + // Reveal hidden players every 5 seconds. + if( sg->val2 >= 5 ) + { + sg->val2 = 0; + // TODO: check if other hidden status can be removed. + status_change_end(bl,SC_HIDING,-1); + status_change_end(bl,SC_CLOAKING,-1); + } + } + /* Enable this if kRO fix the current skill. Currently no damage on undead and demon monster. [Jobbie] + else if( battle_check_target(ss, bl, BCT_ENEMY) > 0 && battle_check_undead(tstatus->race, tstatus->def_ele) ) + skill_castend_damage_id(&src->bl, bl, sg->skill_id, sg->skill_lv, 0, 0);*/ + break; + + case UNT_STEALTHFIELD: + if( bl->id == sg->src_id ) + break; // Dont work on Self (video shows that) + case UNT_NEUTRALBARRIER: + sc_start(bl,type,100,sg->skill_lv,sg->interval + 100); + break; + + case UNT_DIMENSIONDOOR: + if( tsd && !map[bl->m].flag.noteleport ) + pc_randomwarp(tsd,3); + else if( bl->type == BL_MOB && battle_config.mob_warp&8 ) + unit_warp(bl,-1,-1,-1,3); + break; + + case UNT_REVERBERATION: + clif_changetraplook(&src->bl,UNT_USED_TRAPS); + map_foreachinrange(skill_trap_splash,&src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl,tick); + sg->limit = DIFF_TICK(tick,sg->tick) + 1500; + break; + + case UNT_SEVERE_RAINSTORM: + if( battle_check_target(&src->bl, bl, BCT_ENEMY) ) + skill_attack(BF_WEAPON,ss,&src->bl,bl,WM_SEVERE_RAINSTORM_MELEE,sg->skill_lv,tick,0); + break; } if (sg->state.magic_power && sc && !sc->data[SC_MAGICPOWER]) @@ -7759,6 +9425,7 @@ int skill_unit_onout (struct skill_unit *src, struct block_list *bl, unsigned in switch(sg->unit_id){ case UNT_SAFETYWALL: case UNT_PNEUMA: + case UNT_EPICLESIS://Arch Bishop if (sce) status_change_end(bl, type, INVALID_TIMER); break; @@ -7767,7 +9434,6 @@ int skill_unit_onout (struct skill_unit *src, struct block_list *bl, unsigned in if( sce && sce->val4 == src->bl.id ) status_change_end(bl, type, INVALID_TIMER); break; - case UNT_HERMODE: //Clear Hermode if the owner moved. if (sce && sce->val3 == BCT_SELF && sce->val4 == sg->group_id) status_change_end(bl, type, INVALID_TIMER); @@ -8003,6 +9669,19 @@ static int skill_check_condition_char_sub (struct block_list *bl, va_list ap) p_sd[(*c)++]=tsd->bl.id; return 1; } + case AB_ADORAMUS: + { // Adoramus does not consume Blue Gemstone when there is at least 1 Priest class next to the caster + if( (tsd->class_&MAPID_UPPERMASK) == MAPID_PRIEST ) + p_sd[(*c)++] = tsd->bl.id; + return 1; + } + case WL_COMET: + { // Comet does not consume Red Gemstones when there is at least 1 Warlock class next to the caster + if( tsd->status.class_ == 4055 || tsd->status.class_ == 4061 ) + p_sd[(*c)++] = tsd->bl.id; + return 1; + } + default: //Warning: Assuming Ensemble Dance/Songs for code speed. [Skotlex] { int skilllv; @@ -8052,6 +9731,13 @@ int skill_check_pc_partner (struct map_session_data *sd, short skill_id, short* status_charge(&tsd->bl, 0, 10); } return c; + case AB_ADORAMUS: + if( c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL ) + { + i = 2 * (*skill_lv); + status_charge(&tsd->bl, 0, i); + } + break; default: //Warning: Assuming Ensemble skills here (for speed) if (c > 0 && sd->sc.data[SC_DANCING] && (tsd = map_id2sd(p_sd[0])) != NULL) { @@ -8484,6 +10170,97 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh return 0; } break; + /** + * Arch Bishop + **/ + case AB_ANCILLA: + { + int count = 0; + for( i = 0; i < MAX_INVENTORY; i ++ ) + if( sd->status.inventory[i].nameid == ITEMID_ANCILLA ) + count += sd->status.inventory[i].amount; + if( count >= 3 ) { + clif_skill_fail(sd, skill, 0x0c, 0); + return 0; + } + } + break; + /** + * Keeping as a note: + * Bug Report #17 provides a link to a sep-2011 changelog that shows this requirement was removed + **/ + //case AB_LAUDAAGNUS: + //case AB_LAUDARAMUS: + // if( !sd->status.party_id ) { + // clif_skill_fail(sd,skill,0,0); + // return 0; + // } + // break; + + case AB_ADORAMUS: + /** + * Warlock + **/ + case WL_COMET: + if( skill_check_pc_partner(sd,skill,&lv,1,0) <= 0 && ((i = pc_search_inventory(sd,require.itemid[0])) < 0 || sd->status.inventory[i].amount < require.amount[0]) ) + { + //clif_skill_fail(sd,skill,0x47,require.amount[0],require.itemid[0]); + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case WL_SUMMONFB: + case WL_SUMMONBL: + case WL_SUMMONWB: + case WL_SUMMONSTONE: + if( sc ) + { + ARR_FIND(SC_SPHERE_1,SC_SPHERE_5+1,i,!sc->data[i]); + if( i == SC_SPHERE_5+1 ) + { // No more free slots + clif_skill_fail(sd,skill,0x13,0); + return 0; + } + } + break; + /** + * Guilotine Cross + **/ + case GC_HALLUCINATIONWALK: + if( sc && (sc->data[SC_HALLUCINATIONWALK] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY]) ) { + clif_skill_fail(sd,skill,0x0,0); + return 0; + } + break; + case GC_COUNTERSLASH: + case GC_WEAPONCRUSH: + if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == GC_WEAPONBLOCKING) ) { + clif_skill_fail(sd, skill, 0x1f, 0); + return 0; + } + break; + case GC_CROSSRIPPERSLASHER: + if( !(sc && sc->data[SC_ROLLINGCUTTER]) ) { + clif_skill_fail(sd, skill, 0x17, 0); + return 0; + } + break; + case GC_POISONSMOKE: + case GC_VENOMPRESSURE: + if( !(sc && sc->data[SC_POISONINGWEAPON]) ) { + clif_skill_fail(sd, skill, 0x20, 0); + return 0; + } + break; + /** + * Ranger + **/ + case RA_SENSITIVEKEEN: + if(!pc_iswug(sd)) { + clif_skill_fail(sd,skill,0x17,0); + return 0; + } + break; } switch(require.state) { @@ -8568,6 +10345,51 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh break; clif_skill_fail(sd,skill,0,0); return 0; + /** + * Rune Knight + **/ + case ST_RIDINGDRAGON: + if( !(sd->sc.option&OPTION_DRAGON)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + /** + * Wug + **/ + case ST_WUG: + if( !(sd->sc.option&OPTION_WUG) ) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + /** + * Riding Wug + **/ + case ST_RIDINGWUG: + if( !(sd->sc.option&OPTION_WUGRIDER) ){ + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + /** + * Mechanic + **/ + case ST_MADO: + if( !(sd->sc.option&OPTION_MADOGEAR) ) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + /** + * Sorcerer + **/ + //case ST_ELEMENTALSPIRIT: + // if(!sd->ed) { + // clif_skill_fail(sd,skill,0x4f,0,0); + // return 0; + // } + // break; } if(require.mhp > 0 && get_percentage(status->hp, status->max_hp) > require.mhp) { @@ -8648,6 +10470,10 @@ int skill_check_condition_castend(struct map_session_data* sd, short skill, shor case PR_BENEDICTIO: skill_check_pc_partner(sd, skill, &lv, 1, 1); break; + case AB_ADORAMUS: + //if( skill_check_pc_partner(sd,skill,&lv, 1, 2) ) + // sd->state.no_gemstone = 1; // Mark this skill as it don't consume ammo because partners gives SP + break; case AM_CANNIBALIZE: case AM_SPHEREMINE: { @@ -8667,6 +10493,32 @@ int skill_check_condition_castend(struct map_session_data* sd, short skill, shor } break; } + case NC_SILVERSNIPER: + case NC_MAGICDECOY: + { + int c = 0, j; + int maxcount = skill_get_maxcount(skill,lv); + int mob_class = 2042; + if( skill == NC_MAGICDECOY ) + mob_class = 2043; + + if( battle_config.land_skill_limit && maxcount > 0 && ( battle_config.land_skill_limit&BL_PC ) ) + { + if( skill == NC_MAGICDECOY ) + { + for( j = mob_class; j <= 2046; j++ ) + i = map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, j, skill, &c); + } + else + i = map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, mob_class, skill, &c); + if( c >= maxcount ) + { + clif_skill_fail(sd , skill, 0, 0); + return 0; + } + } + } + break; } status = &sd->battle_status; @@ -8854,6 +10706,10 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, short if (sd->status.hom_id) //Don't delete items when hom is already out. continue; break; + case NC_SHAPESHIFT: + if( i < 4 ) + continue; + break; case WZ_FIREPILLAR: // celest if (lv <= 5) // no gems required at level 1-5 continue; @@ -8957,12 +10813,12 @@ int skill_castfix (struct block_list *bl, int skill_id, int skill_lv) sd = BL_CAST(BL_PC, bl); // calculate base cast time (reduced by dex) - if( !(skill_get_castnodex(skill_id, skill_lv)&1) ) - { - int scale = battle_config.castrate_dex_scale - status_get_dex(bl); + if( !(skill_get_castnodex(skill_id, skill_lv)&1) ) { + int scale = CONST_CASTRATE_SCALE - CONST_CASTRATE_CALC; if( scale > 0 ) // not instant cast - time = time * scale / battle_config.castrate_dex_scale; - else return 0; // instant cast + time = time * scale / CONST_CASTRATE_SCALE; + else + return 0; // instant cast } // calculate cast time reduced by item/card bonuses @@ -8980,11 +10836,9 @@ int skill_castfix (struct block_list *bl, int skill_id, int skill_lv) } } } - // config cast time multiplier if (battle_config.cast_rate != 100) time = time * battle_config.cast_rate / 100; - // return final cast time return (time > 0) ? time : 0; } @@ -8992,10 +10846,16 @@ int skill_castfix (struct block_list *bl, int skill_id, int skill_lv) /*========================================== * Does cast-time reductions based on sc data. *------------------------------------------*/ -int skill_castfix_sc (struct block_list *bl, int time) +int skill_castfix_sc (struct block_list *bl, int time, int skill_id, int skill_lv) { struct status_change *sc = status_get_sc(bl); - +#if RECASTING + int fixed = skill_get_cast(skill_id, skill_lv); + if( fixed > 1 ) + fixed = fixed * 20 / 100; + else + fixed = 0; +#endif if (sc && sc->count) { if (sc->data[SC_SLOWCAST]) time += time * sc->data[SC_SLOWCAST]->val2 / 100; @@ -9010,8 +10870,24 @@ int skill_castfix_sc (struct block_list *bl, int time) } if (sc->data[SC_POEMBRAGI]) time -= time * sc->data[SC_POEMBRAGI]->val2 / 100; - } +#if RECASTING + /** + * AB Sacrament reduces fixed cast time by (10 x Level)% (up to 50%) + **/ + if( sc->data[SC_SECRAMENT] ) + fixed -= fixed * sc->data[SC_SECRAMENT]->val2 / 100; +#endif + } +#if RECASTING + /** + * WL_RADIUS decreases 10/15/20% fixed cast time from warlock skills + **/ + if( bl->type == BL_PC && skill_id >= WL_WHITEIMPRISON && skill_id <= WL_FREEZE_SP && ( skill_lv = pc_checkskill((TBL_PC*)bl, WL_RADIUS) ) ) + fixed -= fixed * (5+(skill_lv*5)) / 100; + return (time > 0 || fixed > 0) ? cap_value( time , fixed , INT_MAX ) : 0; +#else return (time > 0) ? time : 0; +#endif } /*========================================== @@ -9572,8 +11448,8 @@ int skill_frostjoke_scream (struct block_list *bl, va_list ap) return 0; if (bl->type == BL_PC) { struct map_session_data *sd = (struct map_session_data *)bl; - if (sd && sd->sc.option&OPTION_INVISIBLE) - return 0; + if ( sd && sd->sc.option&(OPTION_INVISIBLE|OPTION_MADOGEAR) ) + return 0;//Frost Joke / Scream cannot target invisible or MADO Gear characters [Ind] } //It has been reported that Scream/Joke works the same regardless of woe-setting. [Skotlex] if(battle_check_target(src,bl,BCT_ENEMY) > 0) @@ -9731,6 +11607,48 @@ int skill_greed (struct block_list *bl, va_list ap) return 0; } +//For Ranger's Detonator [Jobbie/3CeAM] +int skill_detonator(struct block_list *bl, va_list ap) +{ + struct skill_unit *unit=NULL; + struct block_list *src; + int unit_id; + + nullpo_ret(bl); + nullpo_ret(ap); + src = va_arg(ap,struct block_list *); + + if( bl->type != BL_SKILL || (unit = (struct skill_unit *)bl) == NULL || !unit->group ) + return 0; + if( unit->group->src_id != src->id ) + return 0; + + unit_id = unit->group->unit_id; + switch( unit_id ) + { //List of Hunter and Ranger Traps that can be detonate. + case UNT_BLASTMINE: + case UNT_SANDMAN: + case UNT_CLAYMORETRAP: + case UNT_TALKIEBOX: + case UNT_CLUSTERBOMB: + case UNT_FIRINGTRAP: + case UNT_ICEBOUNDTRAP: + if( unit_id == UNT_TALKIEBOX ) + { + clif_talkiebox(bl,unit->group->valstr); + unit->group->val2 = -1; + } + else + map_foreachinrange(skill_trap_splash,bl,skill_get_splash(unit->group->skill_id,unit->group->skill_lv),unit->group->bl_flag,bl,unit->group->tick); + + clif_changetraplook(bl,unit_id == UNT_FIRINGTRAP ? UNT_DUMMYSKILL : UNT_USED_TRAPS); + unit->group->unit_id = UNT_USED_TRAPS; + unit->range = -1; + unit->group->limit = DIFF_TICK(gettick(),unit->group->tick) + (unit_id == UNT_TALKIEBOX ? 5000 : 1500); + break; + } + return 0; +} /*========================================== * @@ -9897,6 +11815,15 @@ static int skill_trap_splash (struct block_list *bl, va_list ap) if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1)) skill_blown(src,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),-1,0); break; + case UNT_ELECTRICSHOCKER: + clif_skill_damage(src,bl,tick,0,0,-30000,1,sg->skill_id,sg->skill_lv,5); + break; + case UNT_FIRINGTRAP: + case UNT_ICEBOUNDTRAP: + case UNT_CLUSTERBOMB: + if(skill_attack(BF_MISC,ss,bl,bl,sg->skill_id,sg->skill_lv,tick,sg->val1)) + clif_skill_damage(bl,bl,tick,0,0,-30000,1,sg->skill_id,sg->skill_lv,5); + break; default: skill_attack(skill_get_type(sg->skill_id),ss,src,bl,sg->skill_id,sg->skill_lv,tick,0); break; @@ -9964,6 +11891,37 @@ bool skill_check_cloaking(struct block_list *bl, struct status_change_entry *sce return wall; } +bool skill_check_camouflage(struct block_list *bl, struct status_change_entry *sce) +{ + static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1}; + static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1}; + bool wall = true; + int i; + + if( bl->type == BL_PC ) + { //Check for walls. + ARR_FIND( 0, 8, i, map_getcell(bl->m, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS) != 0 ); + if( i == 8 ) + wall = false; + } + + if( sce ) + { + if( !wall ) + { + if( sce->val1 < 3 ) //End camouflage. + status_change_end(bl, SC_CAMOUFLAGE, -1); + else + if( sce->val3&1 ) + { //Remove wall bonus + sce->val3&=~1; + status_calc_bl(bl,SCB_SPEED); + } + } + } + + return wall; +} /*========================================== * @@ -10005,6 +11963,16 @@ struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int case HP_BASILICA: skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_BASILICA,true); break; + /** + * Ranger + **/ + case RA_ELECTRICSHOCKER: + { + struct block_list* target = map_id2bl(group->val2); + if( target ) + status_change_end(target, SC_ELECTRICSHOCKER, -1); + } + break; default: if (group->state.song_dance&0x1) //Check for dissonance. skill_dance_overlap(unit, 1); @@ -10203,14 +12171,36 @@ int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int li } } - if (group->skill_id == SG_SUN_WARM || - group->skill_id == SG_MOON_WARM || - group->skill_id == SG_STAR_WARM) { - struct status_change *sc = status_get_sc(src); - if(sc && sc->data[SC_WARM]) { - sc->data[SC_WARM]->val4 = 0; - status_change_end(src, SC_WARM, INVALID_TIMER); - } + switch( group->skill_id ) { + case SG_SUN_WARM: + case SG_MOON_WARM: + case SG_STAR_WARM: + { + struct status_change *sc = NULL; + if( (sc = status_get_sc(src)) != NULL && sc->data[SC_WARM] ) { + sc->data[SC_WARM]->val4 = 0; + status_change_end(src, SC_WARM, INVALID_TIMER); + } + } + break; + case NC_NEUTRALBARRIER: + { + struct status_change *sc = NULL; + if( (sc = status_get_sc(src)) != NULL && sc->data[SC_NEUTRALBARRIER_MASTER] ) { + sc->data[SC_NEUTRALBARRIER_MASTER]->val2 = 0; + status_change_end(src,SC_NEUTRALBARRIER_MASTER,-1); + } + } + break; + case NC_STEALTHFIELD: + { + struct status_change *sc = NULL; + if( (sc = status_get_sc(src)) != NULL && sc->data[SC_STEALTHFIELD_MASTER] ) { + sc->data[SC_STEALTHFIELD_MASTER]->val2 = 0; + status_change_end(src,SC_STEALTHFIELD_MASTER,-1); + } + } + break; } if (src->type==BL_PC && group->state.ammo_consume) @@ -10379,6 +12369,14 @@ static int skill_unit_timer_sub (DBKey key, void* data, va_list ap) case UNT_FREEZINGTRAP: case UNT_CLAYMORETRAP: case UNT_TALKIEBOX: + case UNT_CLUSTERBOMB: + case UNT_MAGENTATRAP: + case UNT_COBALTTRAP: + case UNT_MAIZETRAP: + case UNT_VERDURETRAP: + case UNT_FIRINGTRAP: + case UNT_ICEBOUNDTRAP: + { struct block_list* src; if( unit->val1 > 0 && (src = map_id2bl(group->src_id)) != NULL && src->type == BL_PC ) @@ -10445,6 +12443,11 @@ static int skill_unit_timer_sub (DBKey key, void* data, va_list ap) case UNT_FREEZINGTRAP: case UNT_TALKIEBOX: case UNT_ANKLESNARE: + /** + * Ranger + **/ + case UNT_ELECTRICSHOCKER: + case UNT_CLUSTERBOMB: if( unit->val1 <= 0 ) { if( group->unit_id == UNT_ANKLESNARE && group->val2 > 0 ) skill_delunit(unit); @@ -10817,6 +12820,9 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in if (!skill_id) //A skill can be specified for some override cases. skill_id = skill_produce_db[idx].req_skill; + if( skill_id == GC_RESEARCHNEWPOISON ) + skill_id = GC_CREATENEWPOISON; + slot[0]=slot1; slot[1]=slot2; slot[2]=slot3; @@ -10838,13 +12844,35 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in ele=ele_table[slot[i]-994]; } } + if( skill_id == RK_RUNEMASTERY ) { + int temp_qty, skill_lv = pc_checkskill(sd,skill_id); + if( skill_lv == 10 ) temp_qty = 1 + rand()%3; + else if( skill_lv > 5 ) temp_qty = 1 + rand()%2; + else temp_qty = 1; + for( i = 0; i < MAX_INVENTORY; i++ ) { + if( sd->status.inventory[i].nameid == nameid ) { + if( sd->status.inventory[i].amount >= MAX_RUNE ) { + clif_msgtable(sd->fd,0x61b); + 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->status.inventory[i].amount >= MAX_RUNE ) + temp_qty = MAX_RUNE - sd->status.inventory[i].amount; + } + break; + } + } + qty = temp_qty; + } for(i=0;i<MAX_PRODUCE_RESOURCE;i++){ int j,id,x; if( (id=skill_produce_db[idx].mat_id[i]) <= 0 ) continue; num++; - x=qty*skill_produce_db[idx].mat_amount[i]; + x=( skill_id == RK_RUNEMASTERY ? 1 : qty)*skill_produce_db[idx].mat_amount[i]; do{ int y=0; j = pc_search_inventory(sd,id); @@ -10889,6 +12917,10 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in make_per = (2000 + 40*status->dex + 20*status->luk); break; case AL_HOLYWATER: + /** + * Arch Bishop + **/ + case AB_ANCILLA: make_per = 100000; //100% success break; case AM_PHARMACY: // Potion Preparation - reviewed with the help of various Ragnainfo sources [DracoRPG] @@ -10939,6 +12971,19 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in case SA_CREATECON: // Elemental Converter Creation make_per = 100000; // should be 100% success rate break; + /** + * Rune Knight + **/ + case RK_RUNEMASTERY: + make_per = 5 * (sd->itemid + pc_checkskill(sd,skill_id)) * 100; + break; + /** + * Guilotine Cross + **/ + case GC_CREATENEWPOISON: + make_per = 3000 + 500 * pc_checkskill(sd,GC_RESEARCHNEWPOISON); + qty = 1+rand()%pc_checkskill(sd,GC_RESEARCHNEWPOISON); + break; default: if (sd->menuskill_id == AM_PHARMACY && sd->menuskill_val > 10 && sd->menuskill_val <= 20) @@ -11008,6 +13053,10 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in flag = battle_config.produce_item_name_input&0x2; break; case AL_HOLYWATER: + /** + * Arch Bishop + **/ + case AB_ANCILLA: flag = battle_config.produce_item_name_input&0x8; break; case ASC_CDP: @@ -11086,6 +13135,11 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in clif_produceeffect(sd,0,nameid); clif_misceffect(&sd->bl,3); break; + case RK_RUNEMASTERY: + case GC_CREATENEWPOISON: + clif_produceeffect(sd,2,nameid); + clif_misceffect(&sd->bl,5); + break; default: //Those that don't require a skill? if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20) { //Cooking items. @@ -11130,6 +13184,11 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in clif_produceeffect(sd,1,nameid); clif_misceffect(&sd->bl,2); break; + case RK_RUNEMASTERY: + case GC_CREATENEWPOISON: + clif_produceeffect(sd,3,nameid); + clif_misceffect(&sd->bl,6); + break; default: if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20 ) { //Cooking items. @@ -11183,6 +13242,125 @@ int skill_arrow_create (struct map_session_data *sd, int nameid) return 0; } +int skill_poisoningweapon( struct map_session_data *sd, int nameid) { + sc_type type; + int t_lv = 0, chance, i; + nullpo_ret(sd); + if( nameid <= 0 || (i = pc_search_inventory(sd,nameid)) < 0 || pc_delitem(sd,i,1,0,0) ) { + clif_skill_fail(sd,GC_POISONINGWEAPON,0,0); + return 0; + } + switch( nameid ) + { // t_lv used to take duration from skill_get_time2 + case PO_PARALYSE: type = SC_PARALYSE; t_lv = 1; break; + case PO_PYREXIA: type = SC_PYREXIA; t_lv = 2; break; + case PO_DEATHHURT: type = SC_DEATHHURT; t_lv = 3; break; + case PO_LEECHESEND: type = SC_LEECHESEND; t_lv = 4; break; + case PO_VENOMBLEED: type = SC_VENOMBLEED; t_lv = 6; break; + case PO_TOXIN: type = SC_TOXIN; t_lv = 7; break; + case PO_MAGICMUSHROOM: type = SC_MAGICMUSHROOM; t_lv = 8; break; + case PO_OBLIVIONCURSE: type = SC_OBLIVIONCURSE; t_lv = 9; break; + default: + clif_skill_fail(sd,GC_POISONINGWEAPON,0,0); + return 0; + } + + chance = 2 + 2 * sd->menuskill_val; // 2 + 2 * skill_lv + sc_start4(&sd->bl,SC_POISONINGWEAPON,100,t_lv,type,chance,0,skill_get_time(GC_POISONINGWEAPON,sd->menuskill_val)); + + return 0; +} +int skill_magicdecoy(struct map_session_data *sd, int nameid) { + int x, y, i, class_, skill; + struct mob_data *md; + nullpo_ret(sd); + skill = sd->menuskill_val; + + if( nameid <= 0 || !itemdb_is_element(nameid) || (i = pc_search_inventory(sd,nameid)) < 0 || !skill || pc_delitem(sd,i,1,0,0) ) + { + clif_skill_fail(sd,NC_MAGICDECOY,0,0); + return 0; + } + + // Spawn Position + pc_delitem(sd,i,1,0,0); + x = sd->sc.comet_x; + y = sd->sc.comet_y; + sd->sc.comet_x = sd->sc.comet_y = 0; + sd->menuskill_val = 0; + + class_ = (nameid == 990 || nameid == 991) ? 2043 + nameid - 990 : (nameid == 992) ? 2046 : 2045; + + + md = mob_once_spawn_sub(&sd->bl, sd->bl.m, x, y, sd->status.name, class_, ""); + if( md ) { + md->master_id = sd->bl.id; + md->special_state.ai = 3; + if( md->deletetimer != INVALID_TIMER ) + delete_timer(md->deletetimer, mob_timer_delete); + md->deletetimer = add_timer (gettick() + skill_get_time(NC_MAGICDECOY,skill), mob_timer_delete, md->bl.id, 0); + mob_spawn(md); + md->status.matk_min = md->status.matk_max = 250 + (50 * skill); + } + + return 0; +} +// Warlock Spellbooks. [LimitLine/3CeAM] +int skill_spellbook (struct map_session_data *sd, int nameid) { + int i, j, points, skillid, preserved = 0, max_preserve; + nullpo_ret(sd); + + if( sd->sc.data[SC_STOP] ) status_change_end(&sd->bl,SC_STOP,-1); + if( nameid <= 0 ) return 0; + + if( pc_search_inventory(sd,nameid) < 0 ) + { // User with no item on inventory + clif_skill_fail(sd,WL_READING_SB,0x04,0); + return 0; + } + + ARR_FIND(0,MAX_SPELLBOOK,j,sd->rsb[j].skillid == 0); // Search for a free slot + if( j == MAX_SPELLBOOK ) + { // No more free slots + clif_skill_fail(sd,WL_READING_SB,0x35,0); + return 0; + } + + ARR_FIND(0,MAX_SKILL_SPELLBOOK_DB,i,skill_spellbook_db[i].nameid == nameid); // Search for information of this item + if( i == MAX_SKILL_SPELLBOOK_DB ) + { // Fake nameid + clif_skill_fail(sd,WL_READING_SB,0x04,0); + return 0; + } + + skillid = skill_spellbook_db[i].skillid; + points = skill_spellbook_db[i].points; + + if( !pc_checkskill(sd,skillid) ) + { // User don't know the skill + sc_start(&sd->bl,SC_SLEEP,100,1,skill_get_time(WL_READING_SB,pc_checkskill(sd,WL_READING_SB))); + clif_skill_fail(sd,WL_READING_SB,0x34,0); + return 0; + } + + max_preserve = 4 * pc_checkskill(sd,WL_FREEZE_SP) + status_get_int(&sd->bl) / 10 + sd->status.base_level / 10; + for( i = 0; i < MAX_SPELLBOOK && sd->rsb[i].skillid; i++ ) + preserved += sd->rsb[i].points; + + if( preserved + points >= max_preserve ) + { // No more free points + clif_skill_fail(sd,WL_READING_SB,0x04,0); + return 0; + } + + sd->rsb[j].skillid = skillid; + sd->rsb[j].level = pc_checkskill(sd,skillid); + sd->rsb[j].points = points; + sc_start2(&sd->bl,SC_READING_SB,100,0,preserved+points,-1); + + return 1; +} + /*========================================== * @@ -11360,6 +13538,7 @@ void skill_init_unit_layout (void) switch (i) { case MG_FIREWALL: case WZ_ICEWALL: + case WL_EARTHSTRAIN://Warlock // these will be handled later break; case PR_SANCTUARY: @@ -11566,6 +13745,90 @@ void skill_init_unit_layout (void) } pos++; } + earthstrain_unit_pos = pos; + for( i = 0; i < 8; i++ ) + { // For each Direction + skill_unit_layout[pos].count = 3; // Temp code being used as the official method makes too much noise in game. [Rytech] + //skill_unit_layout[pos].count = 15; // This line is here to replace the above one once gravity changes the animation. + switch( i ) + { + case 0: case 1: case 3: case 4: case 5: case 7: + { + int dx[] = {-5, 0, 5}; + int dy[] = { 0, 0, 0}; + //int dx[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7}; // Leave this here for future use. + //int dy[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } + break; + case 2: + case 6: + { + int dx[] = { 0, 0, 0}; + int dy[] = {-5, 0, 5}; + //int dx[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Leave this here for future use. + //int dy[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } + break; + } + pos++; + } + +} +// Stasis skill usage check. [LimitLine/3CeAM] +int skill_stasis_check(struct block_list *bl, int src_id, int skillid) +{ + int inf = 0; + if( !bl || skillid < 1 ) + return 0; // Can do it + inf = skill_get_inf2(skillid); + if( inf == INF2_SONG_DANCE || /*skill_get_inf2(skillid) == INF2_CHORUS_SKILL ||*/ inf == INF2_SPIRIT_SKILL ) + return 1; // Can't do it. + + switch( skillid ) + { + case NV_FIRSTAID: case TF_HIDING: case AS_CLOAKING: case WZ_SIGHTRASHER: + case RG_STRIPWEAPON: case RG_STRIPSHIELD: case RG_STRIPARMOR: case WZ_METEOR: + case RG_STRIPHELM: case SC_STRIPACCESSARY: case ST_FULLSTRIP: case WZ_SIGHTBLASTER: + case ST_CHASEWALK: case SC_ENERVATION: case SC_GROOMY: case WZ_ICEWALL: + case SC_IGNORANCE: case SC_LAZINESS: case SC_UNLUCKY: case WZ_STORMGUST: + case SC_WEAKNESS: case AL_RUWACH: case AL_PNEUMA: case WZ_JUPITEL: + case AL_HEAL: case AL_BLESSING: case AL_INCAGI: case WZ_VERMILION: + case AL_TELEPORT: case AL_WARP: case AL_HOLYWATER: case WZ_EARTHSPIKE: + case AL_HOLYLIGHT: case PR_IMPOSITIO: case PR_ASPERSIO: case WZ_HEAVENDRIVE: + case PR_SANCTUARY: case PR_STRECOVERY: case PR_MAGNIFICAT: case WZ_QUAGMIRE: + case ALL_RESURRECTION: case PR_LEXDIVINA: case PR_LEXAETERNA: case HW_GRAVITATION: + case PR_MAGNUS: case PR_TURNUNDEAD: case MG_SRECOVERY: case HW_MAGICPOWER: + case MG_SIGHT: case MG_NAPALMBEAT: case MG_SAFETYWALL: case HW_GANBANTEIN: + case MG_SOULSTRIKE: case MG_COLDBOLT: case MG_FROSTDIVER: case WL_DRAINLIFE: + case MG_STONECURSE: case MG_FIREBALL: case MG_FIREWALL: case WL_SOULEXPANSION: + case MG_FIREBOLT: case MG_LIGHTNINGBOLT: case MG_THUNDERSTORM: case MG_ENERGYCOAT: + case WL_WHITEIMPRISON: case WL_SUMMONFB: case WL_SUMMONBL: case WL_SUMMONWB: + case WL_SUMMONSTONE: case WL_SIENNAEXECRATE: case WL_RELEASE: case WL_EARTHSTRAIN: + case WL_RECOGNIZEDSPELL: case WL_READING_SB: case SA_MAGICROD: case SA_SPELLBREAKER: + case SA_DISPELL: case SA_FLAMELAUNCHER: case SA_FROSTWEAPON: case SA_LIGHTNINGLOADER: + case SA_SEISMICWEAPON: case SA_VOLCANO: case SA_DELUGE: case SA_VIOLENTGALE: + case SA_LANDPROTECTOR: case PF_HPCONVERSION: case PF_SOULCHANGE: case PF_SPIDERWEB: + case PF_FOGWALL: case TK_RUN: case TK_HIGHJUMP: case TK_SEVENWIND: + case SL_KAAHI: case SL_KAUPE: case SL_KAITE: +#if FIREIVY_ON + case WZ_FIREIVY: +#endif + // Skills that need to be confirmed. + case SO_FIREWALK: case SO_ELECTRICWALK: case SO_SPELLFIST: case SO_EARTHGRAVE: + case SO_DIAMONDDUST: case SO_POISON_BUSTER: case SO_PSYCHIC_WAVE: case SO_CLOUD_KILL: + case SO_STRIKING: case SO_WARMER: case SO_VACUUM_EXTREME: case SO_VARETYR_SPEAR: + case SO_ARRULLO: + return 1; // Can't do it. + + default: + return 0; // Can do it. + } + + return 0; // Can Cast anything else like Weapon Skills } /*========================================== @@ -11596,7 +13859,10 @@ static bool skill_parse_row_skilldb(char* split[], int columns, int current) i = skill_get_index(id); if( !i ) // invalid skill id return false; - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_split_atoi(split[1],skill_db[i].range); skill_db[i].hit = atoi(split[2]); skill_db[i].inf = atoi(split[3]); @@ -11638,7 +13904,10 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current) i = skill_get_index(i); if( !i ) // invalid skill id return false; - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_split_atoi(split[1],skill_db[i].hp); skill_split_atoi(split[2],skill_db[i].mhp); skill_split_atoi(split[3],skill_db[i].sp); @@ -11696,6 +13965,17 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current) else if( strcmpi(split[10],"recover_weight_rate")==0 ) skill_db[i].state = ST_RECOV_WEIGHT_RATE; else if( strcmpi(split[10],"move_enable")==0 ) skill_db[i].state = ST_MOVE_ENABLE; else if( strcmpi(split[10],"water")==0 ) skill_db[i].state = ST_WATER; + /** + * New States + **/ + else if( strcmpi(split[10],"dragon")==0 ) skill_db[i].state = ST_RIDINGDRAGON; + else if( strcmpi(split[10],"warg")==0 ) skill_db[i].state = ST_WUG; + else if( strcmpi(split[10],"ridingwarg")==0 ) skill_db[i].state = ST_RIDINGWUG; + else if( strcmpi(split[10],"mado")==0 ) skill_db[i].state = ST_MADO; + else if( strcmpi(split[10],"elementalspirit")==0 ) skill_db[i].state = ST_ELEMENTALSPIRIT; + /** + * Unknown or no state + **/ else skill_db[i].state = ST_NONE; skill_split_atoi(split[11],skill_db[i].spiritball); @@ -11713,13 +13993,16 @@ static bool skill_parse_row_castdb(char* split[], int columns, int current) i = skill_get_index(i); if( !i ) // invalid skill id return false; - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_split_atoi(split[1],skill_db[i].cast); skill_split_atoi(split[2],skill_db[i].delay); skill_split_atoi(split[3],skill_db[i].walkdelay); skill_split_atoi(split[4],skill_db[i].upkeep_time); skill_split_atoi(split[5],skill_db[i].upkeep_time2); - + skill_split_atoi(split[6],skill_db[i].cooldown); return true; } @@ -11729,7 +14012,10 @@ static bool skill_parse_row_castnodexdb(char* split[], int columns, int current) i = skill_get_index(i); if( !i ) // invalid skill id return false; - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_split_atoi(split[1],skill_db[i].castnodex); if( split[2] ) // optional column skill_split_atoi(split[2],skill_db[i].delaynodex); @@ -11743,7 +14029,10 @@ static bool skill_parse_row_nocastdb(char* split[], int columns, int current) i = skill_get_index(i); if( !i ) // invalid skill id return false; - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_db[i].nocast |= atoi(split[1]); return true; @@ -11829,6 +14118,50 @@ static bool skill_parse_row_createarrowdb(char* split[], int columns, int curren return true; } +static bool skill_parse_row_spellbookdb(char* split[], int columns, int current) +{// SkillID,PreservePoints + + int skillid = atoi(split[0]), + points = atoi(split[1]), + nameid = atoi(split[2]); + + if( !skill_get_index(skillid) || !skill_get_max(skillid) ) + ShowError("spellbook_db: Invalid skill ID %d\n", skillid); + if ( !skill_get_inf(skillid) ) + ShowError("spellbook_db: Passive skills cannot be memorized (%d/%s)\n", skillid, skill_get_name(skillid)); + if( points < 1 ) + ShowError("spellbook_db: PreservePoints have to be 1 or above! (%d/%s)\n", skillid, skill_get_name(skillid)); + else + { + skill_spellbook_db[current].skillid = skillid; + skill_spellbook_db[current].points = points; + skill_spellbook_db[current].nameid = nameid; + + return true; + } + + return false; +} +static bool skill_parse_row_magicmushroomdb(char* split[], int column, int current) +{ + int i = atoi(split[0]); + + if( !skill_get_index(i) || !skill_get_max(i) ) + { + ShowError("magicmushroom_db: Invalid skill ID %d\n", i); + return false; + } + if ( !skill_get_inf(i) ) + { + ShowError("magicmushroom_db: Passive skills cannot be casted (%d/%s)\n", i, skill_get_name(i)); + return false; + } + + skill_magicmushroom_db[current].skillid = i; + + return true; +} + static bool skill_parse_row_abradb(char* split[], int columns, int current) {// SkillID,DummyName,RequiredHocusPocusLevel,Rate @@ -11843,7 +14176,10 @@ static bool skill_parse_row_abradb(char* split[], int columns, int current) ShowError("abra_db: Passive skills cannot be casted (%d/%s)\n", i, skill_get_name(i)); return false; } - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_abra_db[current].skillid = i; skill_abra_db[current].req_lv = atoi(split[2]); skill_abra_db[current].per = atoi(split[3]); @@ -11859,13 +14195,14 @@ static void skill_readdb(void) memset(skill_produce_db,0,sizeof(skill_produce_db)); memset(skill_arrow_db,0,sizeof(skill_arrow_db)); memset(skill_abra_db,0,sizeof(skill_abra_db)); - + memset(skill_spellbook_db,0,sizeof(skill_spellbook_db)); + memset(skill_magicmushroom_db,0,sizeof(skill_magicmushroom_db)); // load skill databases safestrncpy(skill_db[0].name, "UNKNOWN_SKILL", sizeof(skill_db[0].name)); safestrncpy(skill_db[0].desc, "Unknown Skill", sizeof(skill_db[0].desc)); sv_readdb(db_path, "skill_db.txt" , ',', 17, 17, MAX_SKILL_DB, skill_parse_row_skilldb); sv_readdb(db_path, "skill_require_db.txt" , ',', 32, 32, MAX_SKILL_DB, skill_parse_row_requiredb); - sv_readdb(db_path, "skill_cast_db.txt" , ',', 6, 6, MAX_SKILL_DB, skill_parse_row_castdb); + sv_readdb(db_path, "skill_cast_db.txt" , ',', 7, 7, MAX_SKILL_DB, skill_parse_row_castdb); sv_readdb(db_path, "skill_castnodex_db.txt", ',', 2, 3, MAX_SKILL_DB, skill_parse_row_castnodexdb); sv_readdb(db_path, "skill_nocast_db.txt" , ',', 2, 2, MAX_SKILL_DB, skill_parse_row_nocastdb); sv_readdb(db_path, "skill_unit_db.txt" , ',', 8, 8, MAX_SKILL_DB, skill_parse_row_unitdb); @@ -11873,6 +14210,11 @@ static void skill_readdb(void) sv_readdb(db_path, "produce_db.txt" , ',', 4, 4+2*MAX_PRODUCE_RESOURCE, MAX_SKILL_PRODUCE_DB, skill_parse_row_producedb); sv_readdb(db_path, "create_arrow_db.txt" , ',', 1+2, 1+2*MAX_ARROW_RESOURCE, MAX_SKILL_ARROW_DB, skill_parse_row_createarrowdb); sv_readdb(db_path, "abra_db.txt" , ',', 4, 4, MAX_SKILL_ABRA_DB, skill_parse_row_abradb); + //Warlock + sv_readdb(db_path, "spellbook_db.txt" , ',', 3, 3, MAX_SKILL_SPELLBOOK_DB, skill_parse_row_spellbookdb); + //Guillotine Cross + sv_readdb(db_path, "magicmushroom_db.txt" , ',', 1, 1, MAX_SKILL_MAGICMUSHROOM_DB, skill_parse_row_magicmushroomdb); + } void skill_reload (void) diff --git a/src/map/skill.h b/src/map/skill.h index 1ad6ea25a..97bd54252 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -13,7 +13,7 @@ struct skill_unit_group; struct status_change_entry; #define MAX_SKILL_DB MAX_SKILL -#define MAX_SKILL_PRODUCE_DB 150 +#define MAX_SKILL_PRODUCE_DB 170 #define MAX_PRODUCE_RESOURCE 12 #define MAX_SKILL_ARROW_DB 150 #define MAX_ARROW_RESOURCE 5 @@ -91,7 +91,7 @@ struct s_skill_db { int range[MAX_SKILL_LEVEL],hit,inf,element[MAX_SKILL_LEVEL],nk,splash[MAX_SKILL_LEVEL],max; int num[MAX_SKILL_LEVEL]; int cast[MAX_SKILL_LEVEL],walkdelay[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL]; - int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL]; + int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL],cooldown[MAX_SKILL_LEVEL]; int castcancel,cast_def_rate; int inf2,maxcount[MAX_SKILL_LEVEL],skill_type; int blewcount[MAX_SKILL_LEVEL]; @@ -290,7 +290,7 @@ int skill_clear_group(struct block_list *bl, int flag); int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl,int damage,unsigned int tick); int skill_castfix( struct block_list *bl, int skill_id, int skill_lv); -int skill_castfix_sc( struct block_list *bl, int time); +int skill_castfix_sc( struct block_list *bl, int time, int skill_id, int skill_lv); int skill_delayfix( struct block_list *bl, int skill_id, int skill_lv); // Skill conditions check and remove [Inkfish] @@ -368,6 +368,14 @@ enum { ST_RECOV_WEIGHT_RATE, ST_MOVE_ENABLE, ST_WATER, + /** + * 3rd States + **/ + ST_RIDINGDRAGON, + ST_WUG, + ST_RIDINGWUG, + ST_MADO, + ST_ELEMENTALSPIRIT, }; enum e_skill { @@ -1310,7 +1318,7 @@ enum e_skill { GN_S_PHARMACY, GN_SLINGITEM_RANGEMELEEATK, - AB_SECRAMENT, + AB_SECRAMENT=2515, WM_SEVERE_RAINSTORM_MELEE, SR_HOWLINGOFLION, SR_RIDEINLIGHTNING, @@ -1547,5 +1555,46 @@ enum { UNT_MAX = 0x190 }; - +/** + * Warlock + **/ +#define MAX_SKILL_SPELLBOOK_DB 17 +enum wl_spheres { + WLS_FIRE = 0x44, + WLS_WIND, + WLS_WATER, + WLS_STONE, +}; +int skill_spellbook (struct map_session_data *sd, int nameid); +/** + * Guilottine Cross + **/ +#define MAX_SKILL_MAGICMUSHROOM_DB 22 +struct s_skill_magicmushroom_db { + int skillid; +}; +extern struct s_skill_magicmushroom_db skill_magicmushroom_db[MAX_SKILL_MAGICMUSHROOM_DB]; +/** + * Ranger + **/ +int skill_detonator(struct block_list *bl, va_list ap); +bool skill_check_camouflage(struct block_list *bl, struct status_change_entry *sce); +/** + * Mechanic + **/ +int skill_magicdecoy(struct map_session_data *sd, int nameid); +/** + * Guiltoine Cross + **/ +int skill_poisoningweapon( struct map_session_data *sd, int nameid); +enum gx_poison { + PO_PARALYSE = 12717, + PO_LEECHESEND, + PO_OBLIVIONCURSE, + PO_DEATHHURT, + PO_TOXIN, + PO_PYREXIA, + PO_MAGICMUSHROOM, + PO_VENOMBLEED +}; #endif /* _SKILL_H_ */ diff --git a/src/map/status.c b/src/map/status.c index 4df2c80d6..28506a4f6 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -56,7 +56,13 @@ static int refinebonus[MAX_REFINE_BONUS][3]; // 精錬ボーナステーブル(refine_db.t int percentrefinery[5][MAX_REFINE+1]; // 精錬成功率(refine_db.txt) static int atkmods[3][MAX_WEAPON_TYPE]; // 武器ATKサイズ修正(size_fix.txt) static char job_bonus[CLASS_COUNT][MAX_LEVEL]; - +#if RRMODE +enum { + SHIELD_ASPD, + RE_JOB_DB_MAX, +} RE_JOB_DB; +static int re_job_db[CLASS_COUNT][RE_JOB_DB_MAX];//[RRInd] +#endif static struct eri *sc_data_ers; //For sc_data entries static struct status_data dummy_status; @@ -445,6 +451,162 @@ void initChangeTables(void) set_sc( GD_LEADERSHIP , SC_GUILDAURA , SI_BLANK , SCB_STR|SCB_AGI|SCB_VIT|SCB_DEX ); set_sc( GD_BATTLEORDER , SC_BATTLEORDERS , SI_BLANK , SCB_STR|SCB_INT|SCB_DEX ); set_sc( GD_REGENERATION , SC_REGENERATION , SI_BLANK , SCB_REGEN ); + + /** + * Rune Knight + **/ + set_sc( RK_ENCHANTBLADE , SC_ENCHANTBLADE , SI_ENCHANTBLADE , SCB_NONE ); + set_sc( RK_DRAGONHOWLING , SC_FEAR , SI_BLANK , SCB_FLEE|SCB_HIT ); + set_sc( RK_DEATHBOUND , SC_DEATHBOUND , SI_DEATHBOUND , SCB_NONE ); + set_sc( RK_WINDCUTTER , SC_FEAR , SI_BLANK , SCB_FLEE|SCB_HIT ); + add_sc( RK_DRAGONBREATH , SC_BURNING ); + set_sc( RK_MILLENNIUMSHIELD , SC_MILLENNIUMSHIELD , SI_REUSE_MILLENNIUMSHIELD , SCB_NONE ); + set_sc( RK_REFRESH , SC_REFRESH , SI_REFRESH , SCB_NONE ); + set_sc( RK_GIANTGROWTH , SC_GIANTGROWTH , SI_GIANTGROWTH , SCB_STR ); + set_sc( RK_STONEHARDSKIN , SC_STONEHARDSKIN , SI_STONEHARDSKIN , SCB_NONE ); + set_sc( RK_VITALITYACTIVATION, SC_VITALITYACTIVATION, SI_VITALITYACTIVATION, SCB_REGEN ); + set_sc( RK_FIGHTINGSPIRIT , SC_FIGHTINGSPIRIT , SI_FIGHTINGSPIRIT , SCB_WATK|SCB_ASPD ); + set_sc( RK_ABUNDANCE , SC_ABUNDANCE , SI_ABUNDANCE , SCB_NONE ); + /** + * GC Guillotine Cross + **/ + set_sc( GC_VENOMIMPRESS , SC_VENOMIMPRESS , SI_VENOMIMPRESS , SCB_NONE ); + set_sc( GC_POISONINGWEAPON , SC_POISONINGWEAPON , SI_POISONINGWEAPON , SCB_NONE ); + set_sc( GC_WEAPONBLOCKING , SC_WEAPONBLOCKING , SI_WEAPONBLOCKING , SCB_NONE ); + set_sc( GC_CLOAKINGEXCEED , SC_CLOAKINGEXCEED , SI_CLOAKINGEXCEED , SCB_SPEED ); + set_sc( GC_HALLUCINATIONWALK , SC_HALLUCINATIONWALK, SI_HALLUCINATIONWALK, SCB_FLEE ); + set_sc( GC_ROLLINGCUTTER , SC_ROLLINGCUTTER , SI_ROLLINGCUTTER , SCB_NONE ); + /** + * Arch Bishop + **/ + set_sc( AB_ADORAMUS , SC_ADORAMUS , SI_ADORAMUS , SCB_AGI|SCB_SPEED ); + add_sc( AB_CLEMENTIA , SC_BLESSING ); + add_sc( AB_CANTO , SC_INCREASEAGI ); + set_sc( AB_EPICLESIS , SC_EPICLESIS , SI_EPICLESIS , SCB_MAXHP ); + add_sc( AB_PRAEFATIO , SC_KYRIE ); + set_sc( AB_ORATIO , SC_ORATIO , SI_ORATIO , SCB_NONE ); + set_sc( AB_LAUDAAGNUS , SC_LAUDAAGNUS , SI_LAUDAAGNUS , SCB_VIT ); + set_sc( AB_LAUDARAMUS , SC_LAUDARAMUS , SI_LAUDARAMUS , SCB_LUK ); + set_sc( AB_RENOVATIO , SC_RENOVATIO , SI_RENOVATIO , SCB_REGEN ); + set_sc( AB_EXPIATIO , SC_EXPIATIO , SI_EXPIATIO , SCB_ATK_ELE ); + set_sc( AB_DUPLELIGHT , SC_DUPLELIGHT , SI_DUPLELIGHT , SCB_NONE ); + set_sc( AB_SECRAMENT , SC_SECRAMENT , SI_SECRAMENT , SCB_NONE ); + /** + * Warlock + **/ + add_sc( WL_WHITEIMPRISON , SC_WHITEIMPRISON ); + set_sc( WL_FROSTMISTY , SC_FREEZING , SI_FROSTMISTY , SCB_ASPD|SCB_SPEED|SCB_DEF|SCB_DEF2 ); + set_sc( WL_MARSHOFABYSS , SC_MARSHOFABYSS , SI_MARSHOFABYSS , SCB_SPEED|SCB_FLEE|SCB_DEF|SCB_MDEF ); + set_sc( WL_RECOGNIZEDSPELL , SC_RECOGNIZEDSPELL , SI_RECOGNIZEDSPELL , SCB_NONE ); + set_sc( WL_STASIS , SC_STASIS , SI_STASIS , SCB_NONE ); + /** + * Ranger + **/ + set_sc( RA_FEARBREEZE , SC_FEARBREEZE , SI_FEARBREEZE , SCB_NONE ); + set_sc( RA_ELECTRICSHOCKER , SC_ELECTRICSHOCKER , SI_ELECTRICSHOCKER , SCB_NONE ); + set_sc( RA_WUGDASH , SC_WUGDASH , SI_WUGDASH , SCB_SPEED ); + set_sc( RA_CAMOUFLAGE , SC_CAMOUFLAGE , SI_CAMOUFLAGE , SCB_CRI|SCB_SPEED ); + add_sc( RA_MAGENTATRAP , SC_ELEMENTALCHANGE ); + add_sc( RA_COBALTTRAP , SC_ELEMENTALCHANGE ); + add_sc( RA_MAIZETRAP , SC_ELEMENTALCHANGE ); + add_sc( RA_VERDURETRAP , SC_ELEMENTALCHANGE ); + add_sc( RA_FIRINGTRAP , SC_BURNING ); + set_sc( RA_ICEBOUNDTRAP , SC_FREEZING , SI_FROSTMISTY , SCB_NONE ); + /** + * Mechanic + **/ + set_sc( NC_ACCELERATION , SC_ACCELERATION , SI_ACCELERATION , SCB_SPEED ); + set_sc( NC_HOVERING , SC_HOVERING , SI_HOVERING , SCB_SPEED ); + set_sc( NC_SHAPESHIFT , SC_SHAPESHIFT , SI_SHAPESHIFT , SCB_DEF_ELE ); + set_sc( NC_INFRAREDSCAN , SC_INFRAREDSCAN , SI_INFRAREDSCAN , SCB_FLEE ); + set_sc( NC_ANALYZE , SC_ANALYZE , SI_ANALYZE , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2 ); + set_sc( NC_MAGNETICFIELD , SC_MAGNETICFIELD , SI_MAGNETICFIELD , SCB_NONE ); + set_sc( NC_NEUTRALBARRIER , SC_NEUTRALBARRIER , SI_NEUTRALBARRIER , SCB_NONE ); + set_sc( NC_STEALTHFIELD , SC_STEALTHFIELD , SI_STEALTHFIELD , SCB_NONE ); + ///** + // * Shadow Chaser + // **/ + //set_sc( SC_REPRODUCE , SC__REPRODUCE , SI_REPRODUCE , SCB_NONE ); + //set_sc( SC_AUTOSHADOWSPELL , SC__AUTOSHADOWSPELL, SI_AUTOSHADOWSPELL , SCB_NONE ); + //set_sc( SC_SHADOWFORM , SC__SHADOWFORM , SI_SHADOWFORM , SCB_NONE ); + //set_sc( SC_BODYPAINT , SC__BODYPAINT , SI_BODYPAINTING , SCB_ASPD ); + //set_sc( SC_INVISIBILITY , SC__INVISIBILITY , SI_INVISIBILITY , SCB_ASPD|SCB_CRI|SCB_ATK_ELE ); + //set_sc( SC_DEADLYINFECT , SC__DEADLYINFECT , SI_DEADLYINFECT , SCB_NONE ); + //set_sc( SC_ENERVATION , SC__ENERVATION , SI_ENERVATION , SCB_BATK ); + //set_sc( SC_GROOMY , SC__GROOMY , SI_GROOMY , SCB_ASPD|SCB_HIT|SCB_SPEED ); + //set_sc( SC_IGNORANCE , SC__IGNORANCE , SI_IGNORANCE , SCB_NONE ); + //set_sc( SC_LAZINESS , SC__LAZINESS , SI_LAZINESS , SCB_FLEE ); + //set_sc( SC_UNLUCKY , SC__UNLUCKY , SI_UNLUCKY , SCB_CRI|SCB_FLEE2 ); + //set_sc( SC_WEAKNESS , SC__WEAKNESS , SI_WEAKNESS , SCB_FLEE2|SCB_MAXHP ); + //set_sc( SC_STRIPACCESSARY , SC__STRIPACCESSORY , SI_STRIPACCESSORY , SCB_DEX|SCB_INT|SCB_LUK ); + //set_sc( SC_MANHOLE , SC__MANHOLE , SI_MANHOLE , SCB_NONE ); + //add_sc( SC_CHAOSPANIC , SC_CHAOS ); + //set_sc( SC_BLOODYLUST , SC__BLOODYLUST , SI_BLOODYLUST , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK ); + ///** + // * Royal Guard + // **/ + //set_sc( LG_REFLECTDAMAGE , SC_REFLECTDAMAGE , SI_LG_REFLECTDAMAGE, SCB_NONE ); + //set_sc( LG_FORCEOFVANGUARD , SC_FORCEOFVANGUARD , SI_FORCEOFVANGUARD , SCB_MAXHP|SCB_DEF ); + //set_sc( LG_EXEEDBREAK , SC_EXEEDBREAK , SI_EXEEDBREAK , SCB_NONE ); + //set_sc( LG_PRESTIGE , SC_PRESTIGE , SI_PRESTIGE , SCB_DEF2 ); + //set_sc( LG_BANDING , SC_BANDING , SI_BANDING , SCB_DEF2|SCB_WATK );// Renewal: atk2 & def2 + //set_sc( LG_PIETY , SC_BENEDICTIO , SI_BENEDICTIO , SCB_DEF_ELE ); + //set_sc( LG_EARTHDRIVE , SC_EARTHDRIVE , SI_EARTHDRIVE , SCB_DEF|SCB_ASPD ); + //set_sc( LG_INSPIRATION , SC_INSPIRATION , SI_INSPIRATION , SCB_MAXHP|SCB_WATK|SCB_HIT|SCB_VIT|SCB_AGI|SCB_STR|SCB_DEX|SCB_INT|SCB_LUK); + ///** + // * Sura + // **/ + //add_sc( SR_DRAGONCOMBO , SC_STUN ); + //add_sc( SR_EARTHSHAKER , SC_STUN ); + //set_sc( SR_CRESCENTELBOW , SC_CRESCENTELBOW , SI_CRESCENTELBOW , SCB_NONE ); + //set_sc( SR_CURSEDCIRCLE , SC_CURSEDCIRCLE_TARGET, SI_CURSEDCIRCLE_TARGET , SCB_NONE ); + //set_sc( SR_LIGHTNINGWALK , SC_LIGHTNINGWALK , SI_LIGHTNINGWALK , SCB_NONE ); + //set_sc( SR_RAISINGDRAGON , SC_RAISINGDRAGON , SI_RAISINGDRAGON , SCB_REGEN|SCB_MAXHP|SCB_MAXSP/*|SCB_ASPD*/ ); + //set_sc( SR_GENTLETOUCH_ENERGYGAIN, SC_GT_ENERGYGAIN , SI_GENTLETOUCH_ENERGYGAIN, SCB_NONE ); + //set_sc( SR_GENTLETOUCH_CHANGE , SC_GT_CHANGE , SI_GENTLETOUCH_CHANGE , SCB_BATK|SCB_ASPD|SCB_DEF|SCB_MDEF ); + //set_sc( SR_GENTLETOUCH_REVITALIZE, SC_GT_REVITALIZE , SI_GENTLETOUCH_REVITALIZE, SCB_MAXHP|SCB_DEF2|SCB_REGEN|SCB_ASPD|SCB_SPEED ); + ///** + // * Wanderer / Mistrel + // **/ + //set_sc( WA_SWING_DANCE , SC_SWINGDANCE , SI_SWINGDANCE , SCB_SPEED|SCB_ASPD ); + //set_sc( WA_SYMPHONY_OF_LOVER , SC_SYMPHONYOFLOVER , SI_SYMPHONYOFLOVERS , SCB_MDEF ); + //set_sc( WA_MOONLIT_SERENADE , SC_MOONLITSERENADE , SI_MOONLITSERENADE , SCB_MATK ); + //set_sc( MI_RUSH_WINDMILL , SC_RUSHWINDMILL , SI_RUSHWINDMILL , SCB_BATK ); + //set_sc( MI_ECHOSONG , SC_ECHOSONG , SI_ECHOSONG , SCB_DEF2 ); + //set_sc( MI_HARMONIZE , SC_HARMONIZE , SI_HARMONIZE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); + //set_sc( WM_POEMOFNETHERWORLD , SC_STOP , SI_NETHERWORLD , SCB_NONE ); + //set_sc( WM_VOICEOFSIREN , SC_VOICEOFSIREN , SI_VOICEOFSIREN , SCB_NONE ); + //set_sc( WM_LULLABY_DEEPSLEEP , SC_DEEPSLEEP , SI_DEEPSLEEP , SCB_NONE ); + //set_sc( WM_SIRCLEOFNATURE , SC_SIRCLEOFNATURE , SI_SIRCLEOFNATURE , SCB_NONE ); + //set_sc( WM_GLOOMYDAY , SC_GLOOMYDAY , SI_GLOOMYDAY , SCB_FLEE|SCB_ASPD ); + //set_sc( WM_SONG_OF_MANA , SC_SONGOFMANA , SI_SONGOFMANA , SCB_NONE ); + //set_sc( WM_DANCE_WITH_WUG , SC_DANCEWITHWUG , SI_DANCEWITHWUG , SCB_ASPD ); + //set_sc( WM_SATURDAY_NIGHT_FEVER , SC_SATURDAYNIGHTFEVER , SI_SATURDAYNIGHTFEVER , SCB_BATK|SCB_DEF|SCB_FLEE|SCB_REGEN ); + //set_sc( WM_LERADS_DEW , SC_LERADSDEW , SI_LERADSDEW , SCB_MAXHP ); + //set_sc( WM_MELODYOFSINK , SC_MELODYOFSINK , SI_MELODYOFSINK , SCB_BATK|SCB_MATK ); + //set_sc( WM_BEYOND_OF_WARCRY , SC_BEYONDOFWARCRY , SI_WARCRYOFBEYOND , SCB_BATK|SCB_MATK ); + //set_sc( WM_UNLIMITED_HUMMING_VOICE, SC_UNLIMITEDHUMMINGVOICE, SI_UNLIMITEDHUMMINGVOICE, SCB_NONE ); + ///** + // * Sorcerer + // **/ + //set_sc( SO_FIREWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE ); + //set_sc( SO_ELECTRICWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE ); + //set_sc( SO_SPELLFIST , SC_SPELLFIST , SI_SPELLFIST , SCB_NONE ); + //set_sc( SO_CLOUD_KILL , SC_POISON , SI_CLOUDKILL , SCB_NONE ); + //set_sc( SO_STRIKING , SC_STRIKING , SI_STRIKING , SCB_WATK|SCB_CRI ); + //set_sc( SO_WARMER , SC_WARMER , SI_WARMER , SCB_NONE ); + //set_sc( SO_VACUUM_EXTREME , SC_VACUUM_EXTREME , SI_VACUUM_EXTREME , SCB_NONE ); + //set_sc( SO_ARRULLO , SC_DEEPSLEEP , SI_DEEPSLEEP , SCB_NONE ); + ///** + // * Genetic + // **/ + //set_sc( GN_CARTBOOST , SC_GN_CARTBOOST, SI_CARTSBOOST , SCB_SPEED ); + //set_sc( GN_THORNS_TRAP , SC_THORNSTRAP , SI_THORNTRAP , SCB_NONE ); + //set_sc( GN_BLOOD_SUCKER , SC_BLOODSUCKER , SI_BLOODSUCKER , SCB_NONE ); + //set_sc( GN_WALLOFTHORN , SC_STOP , SI_BLANK , SCB_NONE ); + //set_sc( GN_FIRE_EXPANSION_SMOKE_POWDER, SC_SMOKEPOWDER , SI_FIRE_EXPANSION_SMOKE_POWDER, SCB_NONE ); + //set_sc( GN_FIRE_EXPANSION_TEAR_GAS , SC_TEARGAS , SI_FIRE_EXPANSION_TEAR_GAS , SCB_NONE ); + //set_sc( GN_MANDRAGORA , SC_MANDRAGORA , SI_MANDRAGORA , SCB_INT ); // Storing the target job rather than simply SC_SPIRIT simplifies code later on. SkillStatusChangeTable[SL_ALCHEMIST] = (sc_type)MAPID_ALCHEMIST, @@ -516,6 +678,81 @@ void initChangeTables(void) StatusIconChangeTable[SC_MERC_HPUP] = SI_MERC_HPUP; StatusIconChangeTable[SC_MERC_SPUP] = SI_MERC_SPUP; StatusIconChangeTable[SC_MERC_HITUP] = SI_MERC_HITUP; + // Warlock Spheres + StatusIconChangeTable[SC_SPHERE_1] = SI_SPHERE_1; + StatusIconChangeTable[SC_SPHERE_2] = SI_SPHERE_2; + StatusIconChangeTable[SC_SPHERE_3] = SI_SPHERE_3; + StatusIconChangeTable[SC_SPHERE_4] = SI_SPHERE_4; + StatusIconChangeTable[SC_SPHERE_5] = SI_SPHERE_5; + + StatusIconChangeTable[SC_NEUTRALBARRIER_MASTER] = SI_NEUTRALBARRIER_MASTER; + StatusIconChangeTable[SC_STEALTHFIELD_MASTER] = SI_STEALTHFIELD_MASTER; + StatusIconChangeTable[SC_OVERHEAT] = SI_OVERHEAT; + StatusIconChangeTable[SC_OVERHEAT_LIMITPOINT] = SI_OVERHEAT_LIMITPOINT; + + StatusIconChangeTable[SC_HALLUCINATIONWALK_POSTDELAY] = SI_HALLUCINATIONWALK_POSTDELAY; + StatusIconChangeTable[SC_TOXIN] = SI_TOXIN; + StatusIconChangeTable[SC_PARALYSE] = SI_PARALYSE; + StatusIconChangeTable[SC_VENOMBLEED] = SI_VENOMBLEED; + StatusIconChangeTable[SC_MAGICMUSHROOM] = SI_MAGICMUSHROOM; + StatusIconChangeTable[SC_DEATHHURT] = SI_DEATHHURT; + StatusIconChangeTable[SC_PYREXIA] = SI_PYREXIA; + StatusIconChangeTable[SC_OBLIVIONCURSE] = SI_OBLIVIONCURSE; + StatusIconChangeTable[SC_LEECHESEND] = SI_LEECHESEND; + + StatusIconChangeTable[SC_SHIELDSPELL_DEF] = SI_SHIELDSPELL_DEF; + StatusIconChangeTable[SC_SHIELDSPELL_MDEF] = SI_SHIELDSPELL_MDEF; + StatusIconChangeTable[SC_SHIELDSPELL_REF] = SI_SHIELDSPELL_REF; + StatusIconChangeTable[SC_BANDING_DEFENCE] = SI_BANDING_DEFENCE; + + StatusIconChangeTable[SC_GLOOMYDAY_SK] = SI_GLOOMYDAY; + + StatusIconChangeTable[SC_CURSEDCIRCLE_ATKER] = SI_CURSEDCIRCLE_ATKER; + + StatusIconChangeTable[SC_STOMACHACHE] = SI_STOMACHACHE; + StatusIconChangeTable[SC_MYSTERIOUS_POWDER] = SI_MYSTERIOUS_POWDER; + StatusIconChangeTable[SC_MELON_BOMB] = SI_MELON_BOMB; + StatusIconChangeTable[SC_BANANA_BOMB] = SI_BANANA_BOMB; + StatusIconChangeTable[SC_BANANA_BOMB_SITDOWN] = SI_BANANA_BOMB_SITDOWN_POSTDELAY; + + //Genetics New Food Items Status Icons + StatusIconChangeTable[SC_SAVAGE_STEAK] = SI_SAVAGE_STEAK; + StatusIconChangeTable[SC_COCKTAIL_WARG_BLOOD] = SI_COCKTAIL_WARG_BLOOD; + StatusIconChangeTable[SC_MINOR_BBQ] = SI_MINOR_BBQ; + StatusIconChangeTable[SC_SIROMA_ICE_TEA] = SI_SIROMA_ICE_TEA; + StatusIconChangeTable[SC_DROCERA_HERB_STEAMED] = SI_DROCERA_HERB_STEAMED; + StatusIconChangeTable[SC_PUTTI_TAILS_NOODLES] = SI_PUTTI_TAILS_NOODLES; + + StatusIconChangeTable[SC_BOOST500] |= SI_BOOST500; + StatusIconChangeTable[SC_FULL_SWING_K] |= SI_FULL_SWING_K; + StatusIconChangeTable[SC_MANA_PLUS] |= SI_MANA_PLUS; + StatusIconChangeTable[SC_MUSTLE_M] |= SI_MUSTLE_M; + StatusIconChangeTable[SC_LIFE_FORCE_F] |= SI_LIFE_FORCE_F; + StatusIconChangeTable[SC_EXTRACT_WHITE_POTION_Z] |= SI_EXTRACT_WHITE_POTION_Z; + StatusIconChangeTable[SC_VITATA_500] |= SI_VITATA_500; + StatusIconChangeTable[SC_EXTRACT_SALAMINE_JUICE] |= SI_EXTRACT_SALAMINE_JUICE; + + // Elemental Spirit's 'side' status change icons. + StatusIconChangeTable[SC_CIRCLE_OF_FIRE] = SI_CIRCLE_OF_FIRE; + StatusIconChangeTable[SC_FIRE_CLOAK] = SI_FIRE_CLOAK; + StatusIconChangeTable[SC_WATER_SCREEN] = SI_WATER_SCREEN; + StatusIconChangeTable[SC_WATER_DROP] = SI_WATER_DROP; + StatusIconChangeTable[SC_WIND_STEP] = SI_WIND_STEP; + StatusIconChangeTable[SC_WIND_CURTAIN] = SI_WIND_CURTAIN; + StatusIconChangeTable[SC_SOLID_SKIN] = SI_SOLID_SKIN; + StatusIconChangeTable[SC_STONE_SHIELD] = SI_STONE_SHIELD; + StatusIconChangeTable[SC_PYROTECHNIC] = SI_PYROTECHNIC; + StatusIconChangeTable[SC_HEATER] = SI_HEATER; + StatusIconChangeTable[SC_TROPIC] = SI_TROPIC; + StatusIconChangeTable[SC_AQUAPLAY] = SI_AQUAPLAY; + StatusIconChangeTable[SC_COOLER] = SI_COOLER; + StatusIconChangeTable[SC_CHILLY_AIR] = SI_CHILLY_AIR; + StatusIconChangeTable[SC_GUST] = SI_GUST; + StatusIconChangeTable[SC_BLAST] = SI_BLAST; + StatusIconChangeTable[SC_WILD_STORM] = SI_WILD_STORM; + StatusIconChangeTable[SC_PETROLOGY] = SI_PETROLOGY; + StatusIconChangeTable[SC_CURSED_SOIL] = SI_CURSED_SOIL; + StatusIconChangeTable[SC_UPHEAVAL] = SI_UPHEAVAL; //Other SC which are not necessarily associated to skills. StatusChangeFlagTable[SC_ASPDPOTION0] = SCB_ASPD; @@ -573,7 +810,12 @@ void initChangeTables(void) StatusChangeFlagTable[SC_MERC_HPUP] |= SCB_MAXHP; StatusChangeFlagTable[SC_MERC_SPUP] |= SCB_MAXSP; StatusChangeFlagTable[SC_MERC_HITUP] |= SCB_HIT; - +#if RE_EDP + /** + * In RE EDP increases your atk and weapon atk + **/ + StatusChangeFlagTable[SC_EDP] |= SCB_BATK|SCB_WATK; +#endif if( !battle_config.display_hallucination ) //Disable Hallucination. StatusIconChangeTable[SC_HALLUCINATION] = SI_BLANK; } @@ -728,6 +970,7 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s status_change_end(target, SC_HIDING, INVALID_TIMER); status_change_end(target, SC_CLOAKING, INVALID_TIMER); status_change_end(target, SC_CHASEWALK, INVALID_TIMER); + status_change_end(target, SC_CAMOUFLAGE, INVALID_TIMER); if ((sce=sc->data[SC_ENDURE]) && !sce->val4) { //Endure count is only reduced by non-players on non-gvg maps. //val4 signals infinite endure. [Skotlex] @@ -745,6 +988,8 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s } if(sc->data[SC_DANCING] && (unsigned int)hp > status->max_hp>>2) status_change_end(target, SC_DANCING, INVALID_TIMER); + if(sc->data[SC_CLOAKINGEXCEED] && --(sc->data[SC_CLOAKINGEXCEED]->val2) <= 0) + status_change_end(target,SC_CLOAKINGEXCEED,-1); } unit_skillcastcancel(target, 2); } @@ -1176,6 +1421,8 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, int } if (sc->option&OPTION_CHASEWALK && skill_num != ST_CHASEWALK) return 0; + if(sc->option&OPTION_MOUNTING) + return 0;//New mounts can't attack nor use skills in the client; this check makes it cheat-safe [Ind] } if (target == NULL || target == src) //No further checking needed. return 1; @@ -1210,6 +1457,8 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, int if (tsc->option&hide_flag && !(status->mode&MD_BOSS) && (sd->special_state.perfect_hiding || !(status->mode&MD_DETECTOR))) return 0; + if( tsc->data[SC_CAMOUFLAGE] && !(status->mode&(MD_BOSS|MD_DETECTOR)) && !skill_num ) + return 0; } break; case BL_ITEM: //Allow targetting of items to pick'em up (or in the case of mobs, to loot them). @@ -1256,18 +1505,14 @@ int status_check_visibility(struct block_list *src, struct block_list *target) switch (target->type) { //Check for chase-walk/hiding/cloaking opponents. case BL_PC: - if(tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) && - !(status->mode&MD_BOSS) && - ( - ((TBL_PC*)target)->special_state.perfect_hiding || - !(status->mode&MD_DETECTOR) - )) + if( (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC_CAMOUFLAGE]) && !(status->mode&MD_BOSS) && + ( ((TBL_PC*)target)->special_state.perfect_hiding || !(status->mode&MD_DETECTOR) ) ) return 0; break; default: - if (tsc && tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) && - !(status->mode&(MD_BOSS|MD_DETECTOR))) - return 0; + if( tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC_CAMOUFLAGE]) && !(status->mode&(MD_BOSS|MD_DETECTOR)) ) + return 0; + } return 1; @@ -1288,7 +1533,24 @@ int status_base_amotion_pc(struct map_session_data* sd, struct status_data* stat // raw delay adjustment from bAspd bonus amotion+= sd->aspd_add; - +#if RRMODE + /** + * Bearing a shield decreases your ASPD by a fixed value depending on your class + **/ + if( sd->status.shield ) + amotion += re_job_db[pc_class2idx(sd->status.class_)][SHIELD_ASPD]; + /** + * RE Absolute aspd modifiers + **/ + if( sd->sc.count ) { + int i; + if ( sd->sc.data[i=SC_ASPDPOTION3] || + sd->sc.data[i=SC_ASPDPOTION2] || + sd->sc.data[i=SC_ASPDPOTION1] || + sd->sc.data[i=SC_ASPDPOTION0] ) + amotion -= sd->sc.data[i]->val1*10; + } +#endif return amotion; } @@ -1331,13 +1593,24 @@ static unsigned short status_base_atk(const struct block_list *bl, const struct static inline unsigned short status_base_matk_max(const struct status_data* status) { - return status->int_+(status->int_/5)*(status->int_/5); + #if RRMODE + return status->matk_max;//In RE maximum MATK signs weapon matk, which we store in this var + #else //Original Max MATK Formula + return status->int_+(status->int_/5)*(status->int_/5); + #endif } - +#if RRMODE +static inline unsigned short status_base_matk_min(const struct status_data* status, int lvl) +#else static inline unsigned short status_base_matk_min(const struct status_data* status) +#endif { - return status->int_+(status->int_/7)*(status->int_/7); + #if RRMODE //Renewal MATK Formula + return status->int_+(status->int_/2)+(status->dex/5)+(status->luk/3)+(lvl/4); + #else //Original Min MATK Formula + return status->int_+(status->int_/7)*(status->int_/7); + #endif } @@ -1350,14 +1623,31 @@ void status_calc_misc(struct block_list *bl, struct status_data *status, int lev status->hit = status->flee = status->def2 = status->mdef2 = status->cri = status->flee2 = 0; - +#if RRMODE + status->matk_min = status_base_matk_min(status, level); +#else status->matk_min = status_base_matk_min(status); +#endif status->matk_max = status_base_matk_max(status); +#if RRMODE //Renewal Formulas + status->hit += level + status->dex;//base level + ( every 1 dex = +1 hit ) + status->hit += status->luk / 3;//every 3 luk = +1 hit + status->flee += level + status->agi;//base level + ( every 1 agi = +1 flee ) + status->flee += status->luk/5;//every 5 luk = +1 flee + status->def2 += status->agi / 5;//every 5 agi = +1 def + status->def2 += status->vit / 2;//every 2 agi = +1 def + status->def2 += level / 2;//every 2 lvls = +1 def + status->mdef2 += status->int_ / 2;//every 2 int = +1 mdef + status->mdef2 += status->dex / 5;//every 5 dex = +1 mdef + status->mdef2 += level /4;//every 4 lvls = +1 mdef + //status->matk_min += level/4;//every 4 lvls = +1 matk +#else //Old Formulas status->hit += level + status->dex; status->flee += level + status->agi; status->def2 += status->vit; status->mdef2 += status->int_ + (status->vit>>1); +#endif if( bl->type&battle_config.enable_critical ) status->cri += status->luk*3 + 10; @@ -1374,6 +1664,10 @@ void status_calc_misc(struct block_list *bl, struct status_data *status, int lev status->batk = cap_value(temp, 0, USHRT_MAX); } else status->batk = status_base_atk(bl, status); +#if RRMODE //Renewal ATK Bonus Formula (after atk is calculated) + status->batk += status->luk / 3;//every 3 luk = +1ATK + status->batk += level / 4;//every 4 levels = +1 ATK +#endif if (status->cri) switch (bl->type) { case BL_MOB: @@ -1684,6 +1978,18 @@ static unsigned int status_base_pc_maxsp(struct map_session_data* sd, struct sta return val; } +#if RRMODE +/** + * Renewal Absolute Bonus to be applied after all bonuses were applied (so % bonuses on say, skills, don't affect them) + **/ +void status_renewal_postcalc(struct status_data* status, int flag) { + if( flag&SCB_FLEE ) + status->flee += 100; + if( flag&SCB_HIT ) + status->hit += 175; + return; +} +#endif //Calculates player data from scratch without counting SC adjustments. //Should be invoked whenever players raise stats, learn passive skills or change equipment. @@ -1909,7 +2215,6 @@ int status_calc_pc_(struct map_session_data* sd, bool first) int r,wlv = sd->inventory_data[index]->wlv; struct weapon_data *wd; struct weapon_atk *wa; - if (wlv >= MAX_REFINE_BONUS) wlv = MAX_REFINE_BONUS - 1; if(i == EQI_HAND_L && sd->status.inventory[index].equip == EQP_HAND_L) { @@ -1921,6 +2226,21 @@ int status_calc_pc_(struct map_session_data* sd, bool first) } wa->atk += sd->inventory_data[index]->atk; wa->atk2 = (r=sd->status.inventory[index].refine)*refinebonus[wlv][0]; + #if RRMODE + /** + * in RE matk_max is used as the weapon's matk. + * += is used so that two-wield weapons (in the case of, say, sinx) bonus stack. + **/ + status->matk_max += sd->inventory_data[index]->matk; + /** + * Refine Bonus + **/ + status->matk_max += sd->status.inventory[index].refine * refinebonus[wlv][0]; + /** + * In RE weapon level is used in several areas, this way we save performance + **/ + status->wlv = wlv; + #endif if((r-=refinebonus[wlv][2])>0) //Overrefine bonus. wd->overrefine = r*refinebonus[wlv][1]; @@ -2277,7 +2597,6 @@ int status_calc_pc_(struct map_session_data* sd, bool first) status->flee += skill*(sd->class_&JOBL_2 && (sd->class_&MAPID_BASEMASK) == MAPID_THIEF? 4 : 3); if((skill=pc_checkskill(sd,MO_DODGE))>0) status->flee += (skill*3)>>1; - // ----- EQUIPMENT-DEF CALCULATION ----- // Apply relative modifiers from equipment @@ -2429,7 +2748,6 @@ int status_calc_pc_(struct map_session_data* sd, bool first) sd->subele[ELE_WIND] += sc->data[SC_ARMOR_RESIST]->val4; } } - status_cpy(&sd->battle_status, status); // ----- CLIENT-SIDE REFRESH ----- @@ -2996,18 +3314,26 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag) if(flag&SCB_MATK) { //New matk + #if RRMODE + status->matk_min = status_base_matk_min(status,status_get_lv(bl)); + #else status->matk_min = status_base_matk_min(status); + #endif status->matk_max = status_base_matk_max(status); if( bl->type&BL_PC && sd->matk_rate != 100 ) { //Bonuses from previous matk + #if RRMODE == 0 //Only changed in non-re [RRInd] status->matk_max = status->matk_max * sd->matk_rate/100; + #endif status->matk_min = status->matk_min * sd->matk_rate/100; } status->matk_min = status_calc_matk(bl, sc, status->matk_min); - status->matk_max = status_calc_matk(bl, sc, status->matk_max); + #if RRMODE == 0 //Only changed in non-re [RRInd] + status->matk_max = status_calc_matk(bl, sc, status->matk_max); + #endif if(sc->data[SC_MAGICPOWER]) { //Store current matk values sc->mp_matk_min = status->matk_min; @@ -3088,14 +3414,15 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag) status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion); } } - +#if RRMODE + status_renewal_postcalc(status,flag); +#endif if(flag&(SCB_VIT|SCB_MAXHP|SCB_INT|SCB_MAXSP) && bl->type&BL_REGEN) status_calc_regen(bl, status, status_get_regen_data(bl)); if(flag&SCB_REGEN && bl->type&BL_REGEN) status_calc_regen_rate(bl, status_get_regen_data(bl), sc); } - /// Recalculates parts of an object's base status and battle status according to the specified flags. /// Also sends updates to the client wherever applicable. /// @param flag bitfield of values from enum scb_flag @@ -3265,6 +3592,11 @@ static unsigned short status_calc_str(struct block_list *bl, struct status_chang str += ((sc->data[SC_MARIONETTE2]->val3)>>16)&0xFF; if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && str < 50) str = 50; + /** + * RK Rune Skill + **/ + if(sc->data[SC_GIANTGROWTH]) + str += 30; return (unsigned short)cap_value(str,0,USHRT_MAX); } @@ -3304,6 +3636,11 @@ static unsigned short status_calc_agi(struct block_list *bl, struct status_chang agi += ((sc->data[SC_MARIONETTE2]->val3)>>8)&0xFF; if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && agi < 50) agi = 50; + /** + * Arch Bishop + **/ + if(sc->data[SC_ADORAMUS]) + agi -= sc->data[SC_ADORAMUS]->val2; return (unsigned short)cap_value(agi,0,USHRT_MAX); } @@ -3481,6 +3818,13 @@ static unsigned short status_calc_batk(struct block_list *bl, struct status_chan batk += sc->data[SC_GATLINGFEVER]->val3; if(sc->data[SC_MADNESSCANCEL]) batk += 100; +#if RE_EDP + /** + * in RE EDP increases your base atk by atk x Skill Level. + **/ + if( sc->data[SC_EDP] ) + batk = batk * sc->data[SC_EDP]->val1; +#endif return (unsigned short)cap_value(batk,0,USHRT_MAX); } @@ -3525,6 +3869,13 @@ static unsigned short status_calc_watk(struct block_list *bl, struct status_chan watk -= watk * sc->data[SC_STRIPWEAPON]->val2/100; if(sc->data[SC_MERC_ATKUP]) watk += sc->data[SC_MERC_ATKUP]->val2; +#if RE_EDP + /** + * in RE EDP increases your weapon atk by watk x Skill Level - 1 + **/ + if( sc->data[SC_EDP] && sc->data[SC_EDP]->val1 > 1 ) + watk = watk * (sc->data[SC_EDP]->val1 - 1); +#endif return (unsigned short)cap_value(watk,0,USHRT_MAX); } @@ -3563,6 +3914,8 @@ static signed short status_calc_critical(struct block_list *bl, struct status_ch critical += sc->data[SC_TRUESIGHT]->val2; if(sc->data[SC_CLOAKING]) critical += critical; + if(sc->data[SC_CAMOUFLAGE]) + critical += 100; return (short)cap_value(critical,10,SHRT_MAX); } @@ -3593,6 +3946,8 @@ static signed short status_calc_hit(struct block_list *bl, struct status_change hit += 20; // RockmanEXE; changed based on updated [Reddozen] if(sc->data[SC_MERC_HITUP]) hit += sc->data[SC_MERC_HITUP]->val2; + if(sc->data[SC_FEAR]) + hit -= hit * 20 / 100; return (short)cap_value(hit,1,SHRT_MAX); } @@ -3640,6 +3995,29 @@ static signed short status_calc_flee(struct block_list *bl, struct status_change flee += 10 + sc->data[SC_SPEED]->val1 * 10; if(sc->data[SC_MERC_FLEEUP]) flee += sc->data[SC_MERC_FLEEUP]->val2; + if(sc->data[SC_FEAR]) + flee -= flee * 20 / 100; + if(sc->data[SC_PARALYSE]) + flee -= flee / 10; // 10% Flee reduction + if(sc->data[SC_INFRAREDSCAN]) + flee -= flee * 30 / 100; + if( sc->data[SC__LAZINESS] ) + flee -= flee * sc->data[SC__LAZINESS]->val3 / 100; + if( sc->data[SC_GLOOMYDAY] ) + flee -= flee * sc->data[SC_GLOOMYDAY]->val2 / 100; + if( sc->data[SC_HALLUCINATIONWALK] ) + flee += sc->data[SC_HALLUCINATIONWALK]->val2; + if( sc->data[SC_SATURDAYNIGHTFEVER] ) + flee -= flee * (40 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100; + if( sc->data[SC_WATER_BARRIER] ) + flee -= sc->data[SC_WATER_BARRIER]->val3; + if( sc->data[SC_WIND_STEP_OPTION] ) + flee += flee * sc->data[SC_WIND_STEP_OPTION]->val2 / 100; + if( sc->data[SC_ZEPHYR] ) + flee += flee * sc->data[SC_ZEPHYR]->val2 / 100; + if( sc->data[SC_MARSHOFABYSS] ) + flee -= (9 * sc->data[SC_MARSHOFABYSS]->val3 / 10 + sc->data[SC_MARSHOFABYSS]->val2 / 10) * (bl->type == BL_MOB ? 2 : 1); + return (short)cap_value(flee,1,SHRT_MAX); } @@ -3696,6 +4074,26 @@ static signed char status_calc_def(struct block_list *bl, struct status_change * def -= def * sc->data[SC_STRIPSHIELD]->val2/100; if (sc->data[SC_FLING]) def -= def * (sc->data[SC_FLING]->val2)/100; + if( sc->data[SC_FREEZING] ) + def -= def * 3 / 10; + if( sc->data[SC_MARSHOFABYSS] ) + def -= def * ( 6 + 6 * sc->data[SC_MARSHOFABYSS]->val3/10 + (bl->type == BL_MOB ? 5 : 3) * sc->data[SC_MARSHOFABYSS]->val2/36 ) / 100; + if( sc->data[SC_ANALYZE] ) + def -= def * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100; + if( sc->data[SC__BLOODYLUST] ) + def -= def * 55 / 100; + if( sc->data[SC_FORCEOFVANGUARD] ) + def += def * 2 * sc->data[SC_FORCEOFVANGUARD]->val1 / 100; + if(sc->data[SC_SATURDAYNIGHTFEVER]) + def -= def * (10 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100; + if(sc->data[SC_EARTHDRIVE]) + def -= def * 25 / 100; + if( sc->data[SC_GT_CHANGE] ) + def -= def * sc->data[SC_GT_CHANGE]->val3 / 100; + if( sc->data[SC_ROCK_CRUSHER] ) + def -= def * sc->data[SC_ROCK_CRUSHER]->val2 / 100; + if( sc->data[SC_POWER_OF_GAIA] ) + def += def * sc->data[SC_POWER_OF_GAIA]->val2 / 100; return (signed char)cap_value(def,CHAR_MIN,CHAR_MAX); } @@ -3728,6 +4126,20 @@ static signed short status_calc_def2(struct block_list *bl, struct status_change + def2 * ( sc->data[SC_JOINTBEAT]->val2&BREAK_WAIST ? 25 : 0 ) / 100; if(sc->data[SC_FLING]) def2 -= def2 * (sc->data[SC_FLING]->val3)/100; + if( sc->data[SC_FREEZING] ) + def2 -= def2 * 3 / 10; + if(sc->data[SC_ANALYZE]) + def2 -= def2 * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100; + if( sc->data[SC_ECHOSONG] ) + def2 += def2 * sc->data[SC_ECHOSONG]->val2/100; + if( sc->data[SC_PRESTIGE] ) + def2 += def2 * sc->data[SC_PRESTIGE]->val1 / 100; + if( sc->data[SC_SHIELDSPELL_REF] && sc->data[SC_SHIELDSPELL_REF]->val1 == 1 ) + def2 += sc->data[SC_SHIELDSPELL_REF]->val2; + if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 0 ) + def2 += (5 + sc->data[SC_BANDING]->val1) * (sc->data[SC_BANDING]->val2); + if( sc->data[SC_GT_REVITALIZE] ) + def2 += def2 * ( 50 + 10 * sc->data[SC_GT_REVITALIZE]->val1 ) / 100; return (short)cap_value(def2,1,SHRT_MAX); } @@ -3755,6 +4167,16 @@ static signed char status_calc_mdef(struct block_list *bl, struct status_change mdef += sc->data[SC_ENDURE]->val1; if(sc->data[SC_CONCENTRATION]) mdef += 1; //Skill info says it adds a fixed 1 Mdef point. + if( sc->data[SC_MARSHOFABYSS] ) + mdef -= mdef * ( 6 + 6 * sc->data[SC_MARSHOFABYSS]->val3/10 + (bl->type == BL_MOB ? 5 : 3) * sc->data[SC_MARSHOFABYSS]->val2/36 ) / 100; + if(sc->data[SC_ANALYZE]) + mdef -= mdef * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100; + if(sc->data[SC_SYMPHONYOFLOVER]) + mdef += mdef * sc->data[SC_SYMPHONYOFLOVER]->val2 / 100; + if(sc->data[SC_GT_CHANGE]) + mdef -= mdef * sc->data[SC_GT_CHANGE]->val3 / 100; + if(sc->data[SC_WATER_BARRIER]) + mdef += sc->data[SC_WATER_BARRIER]->val2; return (signed char)cap_value(mdef,CHAR_MIN,CHAR_MAX); } @@ -3768,6 +4190,8 @@ static signed short status_calc_mdef2(struct block_list *bl, struct status_chang return 0; if(sc->data[SC_MINDBREAKER]) mdef2 -= mdef2 * sc->data[SC_MINDBREAKER]->val3/100; + if(sc->data[SC_ANALYZE]) + mdef2 -= mdef2 * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100; return (short)cap_value(mdef2,1,SHRT_MAX); } @@ -3794,9 +4218,17 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha if( sc->data[SC_FUSION] ) val = 25; - else - if( sd && pc_isriding(sd) ) - val = 25; + else if( sd ) { + if( pc_isriding(sd) || sd->sc.option&(OPTION_DRAGON|OPTION_MOUNTING) ) + val = 25;//Same bonus + else if( sd->sc.option&OPTION_WUGRIDER ) + val = 15 + 5 * pc_checkskill(sd, RA_WUGRIDER); + else if( sd->sc.option&OPTION_MADOGEAR ) { + val = (- 10 * (5 - pc_checkskill(sd,NC_MADOLICENCE))); + if( sc->data[SC_ACCELERATION] ) + val += 25; + } + } speed_rate -= val; } @@ -3821,7 +4253,7 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha if( sc->data[SC_DECREASEAGI] ) val = max( val, 25 ); - if( sc->data[SC_QUAGMIRE] ) + if( sc->data[SC_QUAGMIRE] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY]) val = max( val, 50 ); if( sc->data[SC_DONTFORGETME] ) val = max( val, sc->data[SC_DONTFORGETME]->val3 ); @@ -3845,6 +4277,24 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha val = max( val, sc->data[SC_SUITON]->val3 ); if( sc->data[SC_SWOO] ) val = max( val, 300 ); + if( sc->data[SC_FREEZING] ) + val = max( val, 70 ); + if( sc->data[SC_MARSHOFABYSS] ) + val = max( val, 40 + 10 * sc->data[SC_MARSHOFABYSS]->val1 ); + if( sc->data[SC_CAMOUFLAGE] && (sc->data[SC_CAMOUFLAGE]->val3&1) == 0 ) + val = max( val, sc->data[SC_CAMOUFLAGE]->val1 < 3 ? 300 : 25 * (6 - sc->data[SC_CAMOUFLAGE]->val1) ); + if( sc->data[SC__GROOMY] ) + val = max( val, sc->data[SC__GROOMY]->val2); + if( sc->data[SC_STEALTHFIELD_MASTER] ) + val = max( val, 30 ); + if( sc->data[SC_BANDING_DEFENCE] ) + val = max( val, sc->data[SC_BANDING_DEFENCE]->val1 );//+90% walking speed. + if( sc->data[SC_ROCK_CRUSHER_ATK] ) + val = max( val, sc->data[SC_ROCK_CRUSHER_ATK]->val2 ); + if( sc->data[SC_POWER_OF_GAIA] ) + val = max( val, sc->data[SC_POWER_OF_GAIA]->val2 ); + if( sc->data[SC_MELON_BOMB] ) + val = max( val, sc->data[SC_MELON_BOMB]->val1 ); if( sd && sd->speed_rate + sd->speed_add_rate > 0 ) // permanent item-based speedup val = max( val, sd->speed_rate + sd->speed_add_rate ); @@ -3912,7 +4362,12 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha /// Note that the scale of aspd_rate is 1000 = 100%. static short status_calc_aspd_rate(struct block_list *bl, struct status_change *sc, int aspd_rate) { +#if RRMODE == 0 + /** + * this variable is not used unless in non-RE + **/ int i; +#endif if(!sc || !sc->count) return cap_value(aspd_rate,0,SHRT_MAX); @@ -3981,12 +4436,16 @@ static short status_calc_aspd_rate(struct block_list *bl, struct status_change * else if(sc->data[SC_MADNESSCANCEL]) aspd_rate -= 200; } - +#if RRMODE == 0 + /** + * in RE they give a fixed boost -- we do so along SERVICE4U in status_base_amotion_pc + **/ if(sc->data[i=SC_ASPDPOTION3] || sc->data[i=SC_ASPDPOTION2] || sc->data[i=SC_ASPDPOTION1] || sc->data[i=SC_ASPDPOTION0]) aspd_rate -= sc->data[i]->val2; +#endif if(sc->data[SC_DONTFORGETME]) aspd_rate += 10 * sc->data[SC_DONTFORGETME]->val2; if(sc->data[SC_LONGING]) @@ -4007,6 +4466,41 @@ static short status_calc_aspd_rate(struct block_list *bl, struct status_change * if( sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE ) aspd_rate += 100; } + if( sc->data[SC_FREEZING] ) + aspd_rate += 300; + if( sc->data[SC_HALLUCINATIONWALK_POSTDELAY] ) + aspd_rate += 500; + if( sc->data[SC_FIGHTINGSPIRIT] && sc->data[SC_FIGHTINGSPIRIT]->val2 ) + aspd_rate -= sc->data[SC_FIGHTINGSPIRIT]->val2; + if( sc->data[SC_PARALYSE] ) + aspd_rate += 100; + if( sc->data[SC__BODYPAINT] ) + aspd_rate += aspd_rate * (20 + 5 * sc->data[SC__BODYPAINT]->val1) / 100; + if( sc->data[SC__INVISIBILITY] ) + aspd_rate += aspd_rate * sc->data[SC__INVISIBILITY]->val2 / 100; + if( sc->data[SC__GROOMY] ) + aspd_rate += aspd_rate * sc->data[SC__GROOMY]->val2 / 100; + if( sc->data[SC_SWINGDANCE] ) + aspd_rate -= aspd_rate * sc->data[SC_SWINGDANCE]->val2 / 100; + if( sc->data[SC_DANCEWITHWUG] ) + aspd_rate -= aspd_rate * sc->data[SC_DANCEWITHWUG]->val3 / 100; + if( sc->data[SC_GLOOMYDAY] ) + aspd_rate += aspd_rate * sc->data[SC_GLOOMYDAY]->val3 / 100; + if( sc->data[SC_EARTHDRIVE] ) + aspd_rate += aspd_rate * 25 / 100; + /*As far I tested the skill there is no ASPD addition is applied. [Jobbie] */ + //if( sc->data[SC_RAISINGDRAGON] ) + // aspd_rate -= 100; //FIXME: Need official ASPD bonus of this status. [Jobbie] + if( sc->data[SC_GT_CHANGE] ) + aspd_rate -= aspd_rate * (sc->data[SC_GT_CHANGE]->val2/200) / 100; + if( sc->data[SC_GT_REVITALIZE] ) + aspd_rate -= aspd_rate * sc->data[SC_GT_REVITALIZE]->val2 / 100; + if( sc->data[SC_MELON_BOMB] ) + aspd_rate += aspd_rate * sc->data[SC_MELON_BOMB]->val1 / 100; + if( sc->data[SC_BOOST500] ) + aspd_rate -= aspd_rate * sc->data[SC_BOOST500]->val1/100; + if(sc->data[SC_EXTRACT_SALAMINE_JUICE]) + aspd_rate -= aspd_rate * sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1/100; return (short)cap_value(aspd_rate,0,SHRT_MAX); } @@ -4020,7 +4514,7 @@ static unsigned short status_calc_dmotion(struct block_list *bl, struct status_c return 0; if( sc->data[SC_CONCENTRATION] ) return 0; - if( sc->data[SC_RUN] ) + if( sc->data[SC_RUN] || sc->data[SC_WUGDASH] ) return 0; return (unsigned short)cap_value(dmotion,0,USHRT_MAX); @@ -4045,6 +4539,12 @@ static unsigned int status_calc_maxhp(struct block_list *bl, struct status_chang if(sc->data[SC_MERC_HPUP]) maxhp += maxhp * sc->data[SC_MERC_HPUP]->val2/100; + if(sc->data[SC_EPICLESIS]) + maxhp += maxhp * 5 * sc->data[SC_EPICLESIS]->val1 / 100; + if(sc->data[SC_VENOMBLEED]) + maxhp -= maxhp * 15 / 100; + + return cap_value(maxhp,1,UINT_MAX); } @@ -4078,6 +4578,9 @@ static unsigned char status_calc_element(struct block_list *bl, struct status_ch return ELE_UNDEAD; if(sc->data[SC_ELEMENTALCHANGE]) return sc->data[SC_ELEMENTALCHANGE]->val2; + if(sc->data[SC_SHAPESHIFT]) + return sc->data[SC_SHAPESHIFT]->val2; + return (unsigned char)cap_value(element,0,UCHAR_MAX); } @@ -4096,6 +4599,8 @@ static unsigned char status_calc_element_lv(struct block_list *bl, struct status return 1; if(sc->data[SC_ELEMENTALCHANGE]) return sc->data[SC_ELEMENTALCHANGE]->val1; + if(sc->data[SC_SHAPESHIFT]) + return 1; return (unsigned char)cap_value(lv,1,4); } @@ -4567,7 +5072,7 @@ void status_change_init(struct block_list *bl) //the flag values are the same as in status_change_start. int status_get_sc_def(struct block_list *bl, enum sc_type type, int rate, int tick, int flag) { - int sc_def, tick_def = 0; + int sc_def = 0, tick_def = 0; struct status_data* status; struct status_change* sc; struct map_session_data *sd; @@ -4618,6 +5123,7 @@ int status_get_sc_def(struct block_list *bl, enum sc_type type, int rate, int ti sc_def = 3 +status->int_; break; case SC_DECREASEAGI: + case SC_ADORAMUS://Arch Bishop if (sd) tick>>=1; //Half duration for players. case SC_STONE: case SC_FREEZE: @@ -4647,6 +5153,40 @@ int status_get_sc_def(struct block_list *bl, enum sc_type type, int rate, int ti if (sd) //Duration greatly reduced for players. tick /= 15; //No defense against it (buff). + /** + * 3rd stuff + **/ + case SC_WHITEIMPRISON: + rate -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100; // Lineal Reduction of Rate + //tick_def = (int)floor(log10(status_get_lv(bl)) * 10.); + break; + case SC_BURNING: + // From iROwiki : http://forums.irowiki.org/showpost.php?p=577240&postcount=583 + tick -= 50*status->luk + 60*status->int_ + 170*status->vit; + tick = max(tick,10000); // Minimum Duration 10s. + break; + case SC_FREEZING: + tick -= 1000 * ((status->vit + status->dex) / 20); + tick = max(tick,10000); // Minimum Duration 10s. + break; + case SC_OBLIVIONCURSE: + sc_def = status->int_*4/5; //FIXME: info said this is the formula of status chance. Check again pls. [Jobbie] + break; + case SC_ELECTRICSHOCKER: + case SC_BITE: + { + if( bl->type == BL_MOB ) + tick -= 1000 * (status->agi/10); + if( sd && type != SC_ELECTRICSHOCKER ) + tick >>= 1; + } + break; + case SC_CRYSTALIZE: + tick -= (1000*(status->vit/10))+(status_get_lv(bl)/50); + break; + case SC_VACUUM_EXTREME: + tick -= 50*status->str; + break; default: //Effect that cannot be reduced? Likely a buff. if (!(rand()%10000 < rate)) @@ -4749,7 +5289,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val struct status_change_entry* sce; struct status_data *status; struct view_data *vd; - int opt_flag, calc_flag, undead_flag; + int opt_flag, calc_flag, undead_flag, val_flag = 0, tick_time = 0; nullpo_ret(bl); sc = status_get_sc(bl); @@ -4787,16 +5327,27 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val //Check for inmunities / sc fails switch (type) { - case SC_FREEZE: case SC_STONE: + if(sc->data[SC_POWER_OF_GAIA]) + return 0; + case SC_FREEZE: //Undead are immune to Freeze/Stone if (undead_flag && !(flag&1)) return 0; case SC_SLEEP: case SC_STUN: + case SC_FREEZING: if (sc->opt1) return 0; //Cannot override other opt1 status changes. [Skotlex] + if((type == SC_FREEZE || type == SC_FREEZING) && sc->data[SC_WARMER]) + return 0; //Immune to Frozen and Freezing status if under Warmer status. [Jobbie] + break; + + case SC_BURNING: + if(sc->opt1 || sc->data[SC_FREEZING]) + return 0; break; + case SC_SIGNUMCRUCIS: //Only affects demons and undead element (but not players) if((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) @@ -4813,12 +5364,16 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_OVERTHRUST: if (sc->data[SC_MAXOVERTHRUST]) return 0; //Overthrust can't take effect if under Max Overthrust. [Skotlex] + case SC_MAXOVERTHRUST: + if( sc->option&OPTION_MADOGEAR ) + return 0;//Overthrust and Overthrust Max cannot be used on Mado Gear [Ind] break; case SC_ADRENALINE: if(sd && !pc_check_weapontype(sd,skill_get_weapontype(BS_ADRENALINE))) return 0; if (sc->data[SC_QUAGMIRE] || - sc->data[SC_DECREASEAGI] + sc->data[SC_DECREASEAGI] || + sc->option&OPTION_MADOGEAR //Adrenaline doesn't affect Mado Gear [Ind] ) return 0; break; @@ -4830,6 +5385,9 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val ) return 0; break; + case SC_MAGNIFICAT: + if( sc->option&OPTION_MADOGEAR ) //Mado is immune to magnificat + break; case SC_ONEHAND: case SC_MERC_QUICKEN: case SC_TWOHANDQUICKEN: @@ -4844,6 +5402,8 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_ASSNCROS: if (sc->data[SC_QUAGMIRE]) return 0; + if(sc->option&OPTION_MADOGEAR) + return 0;//Mado is immune to increase agi, wind walk, cart boost, etc (others above) [Ind] break; case SC_CLOAKING: //Avoid cloaking with no wall and low skill level. [Skotlex] @@ -4995,6 +5555,10 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val if (sc->data[SC_LUKFOOD] && sc->data[SC_LUKFOOD]->val1 > val1) return 0; break; + case SC_CAMOUFLAGE: + if( sd && pc_checkskill(sd, RA_CAMOUFLAGE) < 3 && !skill_check_camouflage(bl,NULL) ) + return 0; + break; } //Check for BOSS resistances @@ -5003,8 +5567,6 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val return 0; switch (type) { case SC_BLESSING: - if (!undead_flag && status->race!=RC_DEMON) - break; case SC_DECREASEAGI: case SC_PROVOKE: case SC_COMA: @@ -5013,6 +5575,26 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_RICHMANKIM: case SC_ROKISWEIL: case SC_FOGWALL: + case SC_FREEZING: + case SC_BURNING: // Place here until we have info about its behavior on Boss-monsters. [pakpil] + case SC_MARSHOFABYSS: + case SC_ADORAMUS: + + // Exploid prevention - kRO Fix + case SC_PYREXIA: + case SC_DEATHHURT: + case SC_TOXIN: + case SC_PARALYSE: + case SC_VENOMBLEED: + case SC_MAGICMUSHROOM: + case SC_OBLIVIONCURSE: + case SC_LEECHESEND: + + // Ranger Effects + case SC_BITE: + case SC_ELECTRICSHOCKER: + case SC_MAGNETICFIELD: + return 0; } } @@ -5048,6 +5630,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER); status_change_end(bl, SC_ONEHAND, INVALID_TIMER); status_change_end(bl, SC_MERC_QUICKEN, INVALID_TIMER); + status_change_end(bl, SC_ACCELERATION, INVALID_TIMER); break; case SC_ONEHAND: //Removes the Aspd potion effect, as reported by Vicious. [Skotlex] @@ -5286,6 +5869,8 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_EDP: // [Celest] val2 = val1 + 2; //Chance to Poison enemies. val3 = 50*(val1+1); //Damage increase (+50 +50*lv%) + if( sd )//[Ind] - iROwiki says each level increases its duration by 3 seconds + tick += pc_checkskill(sd,GC_RESEARCHNEWPOISON)*3000; break; case SC_POISONREACT: val2=(val1+1)/2 + val1/10; // Number of counters [Skotlex] @@ -5421,10 +6006,10 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val //val3 : Brings the skilllv (merged into val1 here) //val4 : Partner if (val1 == CG_MOONLIT) - clif_status_change(bl,SI_MOONLIT,1,tick); + clif_status_change(bl,SI_MOONLIT,1,tick,0, 0, 0); val1|= (val3<<16); val3 = tick/1000; //Tick duration - tick = 1000; + tick_time = 1000; // [GodLesZ] tick time break; case SC_LONGING: val2 = 500-100*val1; //Aspd penalty. @@ -5432,9 +6017,14 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_EXPLOSIONSPIRITS: val2 = 75 + 25*val1; //Cri bonus break; +#if RRMODE == 0 + /** + * Only in non-RE it's var is changed + **/ case SC_ASPDPOTION0: case SC_ASPDPOTION1: case SC_ASPDPOTION2: +#endif case SC_ASPDPOTION3: val2 = 50*(2+type-SC_ASPDPOTION0); break; @@ -5455,6 +6045,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color); break; case SC_NOCHAT: + // [GodLesZ] FIXME: is this correct? a hardcoded interval of 60sec? what about configuration ?_? tick = 60000; val1 = battle_config.manner_system; //Mute filters. if (sd) @@ -5486,7 +6077,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_POISON: /* 毒 */ val3 = tick/1000; //Damage iterations if(val3 < 1) val3 = 1; - tick = 1000; + tick_time = 1000; // [GodLesZ] tick time //val4: HP damage if (bl->type == BL_PC) val4 = (type == SC_DPOISON) ? 3 + status->max_hp/50 : 3 + status->max_hp*3/200; @@ -5500,7 +6091,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_BLEEDING: val4 = tick/10000; if (!val4) val4 = 1; - tick = 10000; + tick_time = 10000; // [GodLesZ] tick time break; case SC_S_LIFEPOTION: case SC_L_LIFEPOTION: @@ -5511,7 +6102,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val if( val2 < 1 ) val2 = 1; if( (val4 = tick/(val2 * 1000)) < 1 ) val4 = 1; - tick = val2 * 1000; // val2 = Seconds between heals + tick_time = val2 * 1000; // [GodLesZ] tick time break; case SC_BOSSMAPINFO: if( sd != NULL ) @@ -5525,12 +6116,12 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val val1 = boss_md->bl.id; if( (val4 = tick/1000) < 1 ) val4 = 1; - tick = 1000; + tick_time = 1000; // [GodLesZ] tick time } break; case SC_HIDING: val2 = tick/1000; - tick = 1000; + tick_time = 1000; // [GodLesZ] tick time val3 = 0; // unused, previously speed adjustment val4 = val1+3; //Seconds before SP substraction happen. break; @@ -5560,7 +6151,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_SIGHTBLASTER: val3 = skill_get_splash(val2, val1); //Val2 should bring the skill-id. val2 = tick/250; - tick = 10; + tick_time = 10; // [GodLesZ] tick time break; //Permanent effects. @@ -5631,7 +6222,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val val2 = 12; //SP cost val4 = 10000; //Decrease at 10secs intervals. val3 = tick/val4; - tick = val4; + tick_time = val4; // [GodLesZ] tick time break; case SC_PARRYING: val2 = 20 + val1*3; //Block Chance @@ -5654,13 +6245,13 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val if (!val4) val4 = skill_get_time2(status_sc2skill(type),val1); if (!val4) val4 = 10000; //Val4 holds damage interval val3 = tick/val4; //val3 holds skill duration - tick = val4; + tick_time = val4; // [GodLesZ] tick time break; case SC_GOSPEL: if(val4 == BCT_SELF) { // self effect val2 = tick/10000; - tick = 10000; + tick_time = 10000; // [GodLesZ] tick time status_change_clear_buffs(bl,3); //Remove buffs/debuffs } break; @@ -5926,7 +6517,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_SKA: val2 = tick/1000; val3 = rand()%100; //Def changes randomly every second... - tick = 1000; + tick_time = 1000; // [GodLesZ] tick time break; case SC_JAILED: //Val1 is duration in minutes. Use INT_MAX to specify 'unlimited' time. @@ -6062,6 +6653,487 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_SPL_MATK: val2 = 2; // Splendide group break; + /** + * General + **/ + case SC_FEAR: + val2 = 2; + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_BURNING: + val4 = tick / 2000; // Total Ticks to Burn!! + tick_time = 2000; // [GodLesZ] tick time + break; + /** + * Rune Knight + **/ + case SC_DEATHBOUND: + val2 = 500 + 100 * val1; + break; + case SC_FIGHTINGSPIRIT: + val_flag |= 1|2; + break; + case SC_ABUNDANCE: + val4 = tick / 10000; + tick_time = 10000; // [GodLesZ] tick time + break; + case SC_GIANTGROWTH: + val2 = 10; // Triple damage success rate. + break; + /** + * Arch Bishop + **/ + case SC_RENOVATIO: + val4 = tick / 5000; + tick_time = 5000; + break; + case SC_SECRAMENT: + val2 = 10 * val1; + break; + case SC_VENOMIMPRESS: + val2 = 10 * val1; + val_flag |= 1|2; + break; + case SC_POISONINGWEAPON: + val_flag |= 1|2|4; + break; + case SC_WEAPONBLOCKING: + val2 = 10 + 2 * val1; // Chance + val4 = tick / 3000; + tick_time = 3000; // [GodLesZ] tick time + val_flag |= 1|2; + break; + case SC_TOXIN: + val4 = tick / 10000; + tick_time = 10000; // [GodLesZ] tick time + break; + case SC_MAGICMUSHROOM: + val4 = tick / 4000; + tick_time = 4000; // [GodLesZ] tick time + break; + case SC_PYREXIA: + val4 = tick / 3000; + tick_time = 4000; // [GodLesZ] tick time + break; + case SC_LEECHESEND: + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_OBLIVIONCURSE: + val4 = tick / 3000; + tick_time = 3000; // [GodLesZ] tick time + break; + case SC_ROLLINGCUTTER: + val_flag |= 1; + break; + case SC_CLOAKINGEXCEED: + val2 = ( val1 + 1 ) / 2; // Hits + val3 = ( val1 - 1 ) * 10; // Walk speed + val_flag |= 1|2|4; + if (bl->type == BL_PC) + val4 |= battle_config.pc_cloak_check_type&7; + else + val4 |= battle_config.monster_cloak_check_type&7; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_HALLUCINATIONWALK: + val2 = 50 * val1; // Evasion rate of physical attacks. Flee + val3 = 10 * val1; // Evasion rate of magical attacks. + val_flag |= 1|2|4; + break; + case SC_WHITEIMPRISON: + status_change_end(bl, SC_BURNING, -1); + status_change_end(bl, SC_FREEZING, -1); + status_change_end(bl, SC_FREEZE, -1); + status_change_end(bl, SC_STONE, -1); + break; + case SC_FREEZING: + status_change_end(bl, SC_BURNING, -1); + break; + case SC_READING_SB: + // val2 = sp reduction per second + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_SPHERE_1: + case SC_SPHERE_2: + case SC_SPHERE_3: + case SC_SPHERE_4: + case SC_SPHERE_5: + if( !sd ) + return 0; // Should only work on players. + val4 = tick / 1000; + if( val4 < 1 ) + val4 = 1; + tick_time = 1000; // [GodLesZ] tick time + val_flag |= 1; + break; + case SC_SHAPESHIFT: + switch( val1 ) + { + case 1: val2 = ELE_FIRE; break; + case 2: val2 = ELE_EARTH; break; + case 3: val2 = ELE_WIND; break; + case 4: val2 = ELE_WATER; break; + } + break; + case SC_ELECTRICSHOCKER: + case SC_CRYSTALIZE: + val4 = tick / 1000; + if( val4 < 1 ) + val4 = 1; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_CAMOUFLAGE: + //val3 |= battle_config.pc_camouflage_check_type&7; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_WUGDASH: + val4 = gettick(); //Store time at which you started running. + tick = -1; + break; + case SC__SHADOWFORM: + { + //struct map_session_data * s_sd = map_id2sd(val2); + //if( s_sd ) + // s_sd->shadowform_id = bl->id; + val4 = tick / 1000; + val_flag |= 1|2|4; + tick_time = 1000; // [GodLesZ] tick time + } + break; + case SC__STRIPACCESSORY: + if (!sd) + val2 = 20; + break; + case SC__INVISIBILITY: + val2 = 50 - 10 * val1; // ASPD + val3 = 20 * val1; // CRITICAL + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + val_flag |= 1|2; + break; + case SC__ENERVATION: + val2 = 20 + 10 * val1; // ATK Reduction + val_flag |= 1|2; + if( sd ) pc_delspiritball(sd,sd->spiritball,0); + break; + case SC__GROOMY: + val2 = 20 + 10 * val1; //ASPD. Need to confirm if Movement Speed reduction is the same. [Jobbie] + val3 = 20 * val1; //HIT + val_flag |= 1|2|4; + if( sd ) + { // Removes Animals + //if( pc_isriding(sd,OPTION_RIDING|OPTION_RIDING_DRAGON|OPTION_RIDING_WUG) ) pc_setriding(sd, 0); + //if( pc_iswarg(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUG); + if( pc_isfalcon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_FALCON); + if( sd->status.pet_id > 0 ) pet_menu(sd, 3); + if( merc_is_hom_active(sd->hd) ) merc_hom_vaporize(sd,1); + if( sd->md ) merc_delete(sd->md,3); + } + break; + case SC__LAZINESS: + val2 = 10 + 10 * val1; // Cast reduction + val3 = 10 * val1; // Flee Reduction + val_flag |= 1|2|4; + break; + case SC__UNLUCKY: + val2 = 10 * val1; // Crit and Flee2 Reduction + val_flag |= 1|2|4; + break; + case SC__WEAKNESS: + val2 = 10 * val1; + val_flag |= 1|2; + skill_strip_equip(bl,EQP_WEAPON|EQP_SHIELD,100,val1,tick); + break; + case SC__BLOODYLUST: + val_flag |= 1|2; + break; + case SC_GN_CARTBOOST: + if( val1 < 3 ) + val2 = 50; + else if( val1 < 5 ) + val2 = 75; + else + val2 = 100; + break; + case SC_PROPERTYWALK: + val_flag |= 1|2; + val3 = 0; + break; + case SC_WARMER: + status_change_end(bl, SC_FREEZE, -1); + status_change_end(bl, SC_FREEZING, -1); + status_change_end(bl, SC_CRYSTALIZE, -1); + break; + case SC_STRIKING: + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_BLOODSUCKER: + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_SWINGDANCE: + val2 = 4 * val1; // Walk speed and aspd reduction. + break; + case SC_SYMPHONYOFLOVER: + case SC_RUSHWINDMILL: + case SC_ECHOSONG: + val2 = 6 * val1; + val2 += val3; //Adding 1% * Lesson Bonus + val2 += (int)(val4*2/10); //Adding 0.2% per JobLevel + break; + case SC_MOONLITSERENADE: + val2 = 10 * val1; + break; + case SC_HARMONIZE: + val2 = 3 + 2 * val1; + break; + case SC_VOICEOFSIREN: + val4 = tick / 2000; + tick_time = 2000; // [GodLesZ] tick time + break; + case SC_DEEPSLEEP: + val4 = tick / 2000; + tick_time = 2000; // [GodLesZ] tick time + break; + case SC_SIRCLEOFNATURE: + val2 = 1 + val1; //SP consume + val3 = 40 * val1; //HP recovery + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_SONGOFMANA: + val3 = 10 + (2 * val2); + val4 = tick/3000; + tick_time = 3000; // [GodLesZ] tick time + break; + case SC_SATURDAYNIGHTFEVER: + if (!val4) val4 = skill_get_time2(status_sc2skill(type),val1); + if (!val4) val4 = 3000; + val3 = tick/val4; + tick_time = val4; // [GodLesZ] tick time + break; + case SC_GLOOMYDAY: + val2 = 3 + 2 * val1; // Flee reduction. + val3 = 3 * val1; // ASPD reduction. + break; + case SC_SITDOWN_FORCE: + case SC_BANANA_BOMB_SITDOWN: + if( sd && !pc_issit(sd) ) + { + pc_setsit(sd); + skill_sit(sd,1); + clif_sitting(bl); + } + break; + case SC_DANCEWITHWUG: + val3 = (5 * val1) + (1 * val2); //Still need official value. + break; + case SC_LERADSDEW: + val3 = (5 * val1) + (1 * val2); + break; + case SC_MELODYOFSINK: + val3 = (5 * val1) + (1 * val2); + break; + case SC_BEYONDOFWARCRY: + val3 = (5 * val1) + (1 * val2); + break; + case SC_UNLIMITEDHUMMINGVOICE: + { + struct unit_data *ud = unit_bl2ud(bl); + if( ud == NULL ) return 0; + ud->state.skillcastcancel = 0; + val3 = 15 - (2 * val2); + } + break; + case SC_REFLECTDAMAGE: + val2 = 15 + 5 * val1; + val3 = (val1==5)?20:(val1+4)*2; // SP consumption + val4 = tick/10000; + tick_time = 10000; // [GodLesZ] tick time + break; + case SC_FORCEOFVANGUARD: // This is not the official way to handle it but I think we should use it. [pakpil] + val2 = 20 + 12 * (val1 - 1); // Chance + val3 = 5 + (2 * val1); // Max rage counters + tick_time = 6000; // [GodLesZ] tick time + val_flag |= 1|2|4; + break; + case SC_EXEEDBREAK: + val1 *= 150; // 150 * skill_lv + if( sd ) + { // Chars. + struct item_data *id = sd->inventory_data[sd->equip_index[EQI_HAND_R]]; + if( id ) val1 += (id->weight/10 * id->wlv * status_get_lv(bl) / 100); // (weapon_weight * weapon_level * base_lvl)/100 + val1 += 15 * sd->status.job_level; // 15 * job_lvl + } + else // Mobs + val1 += (400 * status_get_lv(bl) / 100) + (15 * (status_get_lv(bl) / 2)); // About 1138% at mob_lvl 99. Is an aproximation to a standard weapon. [pakpil] + break; + + case SC_PRESTIGE: // Bassed on suggested formula in iRO Wiki and some test, still need more test. [pakpil] + val2 = ((status->int_ + status->luk) / 6) + 5; // Chance to evade magic damage. + val1 *= 15; // Defence added + if( sd ) + val1 += 10 * pc_checkskill(sd,CR_DEFENDER); + val_flag |= 1|2; + break; + case SC_BANDING: + tick_time = 5000; // [GodLesZ] tick time + val_flag |= 1; + break; + case SC_SHIELDSPELL_DEF: + case SC_SHIELDSPELL_MDEF: + case SC_SHIELDSPELL_REF: + val_flag |= 1|2; + break; + case SC_MAGNETICFIELD: + val3 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_INSPIRATION: + if( sd ) + { + val2 = (40 * val1) + (3 * sd->status.job_level); // ATK bonus + val3 = (sd->status.job_level / 10) * 2 + 12; // All stat bonus + } + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + status_change_clear_buffs(bl,3); //Remove buffs/debuffs + break; + case SC_SPELLFIST: + case SC_CURSEDCIRCLE_ATKER: + val_flag |= 1|2|4; + break; + case SC_CRESCENTELBOW: + val2 = 94 + val1; + val_flag |= 1|2; + break; + case SC_LIGHTNINGWALK: + val1 = 88 + 2 * val1; + val_flag |= 1; + break; + case SC_RAISINGDRAGON: + val3 = tick / 5000; + tick_time = 5000; // [GodLesZ] tick time + break; + case SC_GT_CHANGE: + if( sd ) val2 = (13 * val1 / 2) * sd->status.agi; //Aspd - old formula. + val3 = 20 + 1 * val1; //Base Atk, Reduction to DEF & MDEF + break; + case SC_GT_REVITALIZE: + val2 = 5 * val1; //Custom value VIT, ASPD, SPEED bonus. + val3 = 60 + 40 * val1; //HP recovery + break; + case SC_PYROTECHNIC_OPTION: + val2 = 60; // Watk TODO: Renewal (Atk2) + val3 = 11; // % Increase damage. + val_flag |= 1|2|4; + break; + case SC_HEATER_OPTION: + val2 = 120; // Watk. TODO: Renewal (Atk2) + val3 = 33; // % Increase effects. + val4 = 3; // Change into fire element. + val_flag |= 1|2|4; + break; + case SC_TROPIC_OPTION: + val2 = 180; // Watk. TODO: Renewal (Atk2) + val3 = MG_FIREBOLT; + break; + case SC_AQUAPLAY_OPTION: + val2 = 40; // Matk. TODO: Renewal (Matk1) + val3 = 33; // % Increase effects. + val_flag |= 1|2|4; + break; + case SC_COOLER_OPTION: + val2 = 80; // % Freezing chance + val3 = 33; // % increased damage + val4 = 1; // Change into water elemet + val_flag |= 1|2|4; + break; + case SC_CHILLY_AIR_OPTION: + val2 = 120; // Matk. TODO: Renewal (Matk1) + val3 = MG_COLDBOLT; + val_flag |= 1|2; + break; + case SC_GUST_OPTION: + val2 = 33; + val_flag |= 1|2; + break; + case SC_WIND_STEP_OPTION: + val2 = 50; // % Increase speed and flee. + break; + case SC_BLAST_OPTION: + val2 = 33; + val3 = 4; + val_flag |= 1|2|4; + break; + case SC_WILD_STORM_OPTION: + val2 = MG_LIGHTNINGBOLT; + val_flag |= 1|2; + break; + case SC_PETROLOGY_OPTION: + val2 = 5; + val3 = 33; + val_flag |= 1|2|4; + break; + case SC_CURSED_SOIL_OPTION: + val2 = 10; + val3 = 33; + val4 = 2; + val_flag |= 1|2|4; + break; + case SC_UPHEAVAL_OPTION: + val2 = WZ_EARTHSPIKE; + val_flag |= 1|2; + break; + case SC_CIRCLE_OF_FIRE_OPTION: + val2 = 300; + val_flag |= 1|2; + break; + case SC_FIRE_CLOAK_OPTION: + case SC_WATER_DROP_OPTION: + case SC_WIND_CURTAIN_OPTION: + case SC_STONE_SHIELD_OPTION: + val2 = 20; // Elemental modifier. Not confirmed. + break; + case SC_CIRCLE_OF_FIRE: + case SC_FIRE_CLOAK: + case SC_WATER_DROP: + case SC_WATER_SCREEN: + case SC_WIND_CURTAIN: + case SC_WIND_STEP: + case SC_STONE_SHIELD: + case SC_SOLID_SKIN: + val2 = 10; + tick_time = 2000; // [GodLesZ] tick time + break; + case SC_WATER_BARRIER: + val2 = 40; // Increasement. Mdef1 ??? + val3 = 20; // Reductions. Atk2, Flee1, Matk1 ???? + val_flag |= 1|2|4; + break; + case SC_ZEPHYR: + val2 = 22; // Flee. + break; + case SC_TIDAL_WEAPON: + val2 = 20; // Increase Elemental's attack. + break; + case SC_ROCK_CRUSHER: + case SC_ROCK_CRUSHER_ATK: + case SC_POWER_OF_GAIA: + val2 = 33; + break; + case SC_MELON_BOMB: + case SC_BANANA_BOMB: + val1 = 15; + break; + case SC_STOMACHACHE: + val2 = 8; // SP consume. + val4 = tick / 10000; + tick_time = 10000; // [GodLesZ] tick time + break; default: if( calc_flag == SCB_NONE && StatusSkillChangeTable[type] == 0 && StatusIconChangeTable[type] == 0 ) @@ -6107,12 +7179,15 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_CLOSECONFINE2: case SC_ANKLE: case SC_SPIDERWEB: + case SC_ELECTRICSHOCKER: unit_stop_walking(bl,1); break; case SC_HIDING: case SC_CLOAKING: + case SC_CLOAKINGEXCEED: case SC_CHASEWALK: case SC_WEIGHT90: + case SC_CAMOUFLAGE: unit_stop_attack(bl); break; case SC_SILENCE: @@ -6130,6 +7205,8 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_FREEZE: sc->opt1 = OPT1_FREEZE; break; case SC_STUN: sc->opt1 = OPT1_STUN; break; case SC_SLEEP: sc->opt1 = OPT1_SLEEP; break; + case SC_BURNING: sc->opt1 = OPT1_BURNING; break; // Burning need this to be showed correctly. [pakpil] + case SC_WHITEIMPRISON: sc->opt1 = OPT1_IMPRISON; break; //OPT2 case SC_POISON: sc->opt2 |= OPT2_POISON; break; case SC_CURSE: sc->opt2 |= OPT2_CURSE; break; @@ -6234,6 +7311,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val opt_flag = 2; break; case SC_CLOAKING: + case SC_CLOAKINGEXCEED: sc->option |= OPTION_CLOAK; opt_flag = 2; break; @@ -6280,10 +7358,15 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val calc_flag&=~SCB_DYE; } - if( vd && (pcdb_checkid(vd->class_) || bl->type == BL_MER ) ) //Only for players sprites, client crashes if they receive this for a mob o.O [Skotlex] - clif_status_change(bl,StatusIconChangeTable[type],1,tick); + if( vd && (pcdb_checkid(vd->class_) || bl->type == BL_MER || bl->type == BL_MOB ) ) + clif_status_change(bl,StatusIconChangeTable[type],1,tick,(val_flag&1)?val1:1,(val_flag&2)?val2:0,(val_flag&4)?val3:0); else if( sd ) //Send packet to self otherwise (disguised player?) clif_status_load(bl,StatusIconChangeTable[type],1); + /** + * used as temporary storage for scs with interval ticks, so that the actual duration is sent to the client first. + **/ + if( tick_time ) + tick = tick_time; //Don't trust the previous sce assignment, in case the SC ended somewhere between there and here. if((sce=sc->data[type])) @@ -6337,6 +7420,16 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_MERC_SPUP: status_percent_heal(bl, 0, 100); // Recover Full SP break; + /** + * Ranger + **/ + case SC_WUGDASH: + { + struct unit_data *ud = unit_bl2ud(bl); + if( ud ) + ud->state.running = unit_wugdash(bl, sd); + } + break; case SC_COMBO: switch (sce->val1) { case TK_STORMKICK: @@ -6658,7 +7751,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const } if((sce->val1&0xFFFF) == CG_MOONLIT) - clif_status_change(bl,SI_MOONLIT,0,0); + clif_status_change(bl,SI_MOONLIT,0,0,0,0,0); status_change_end(bl, SC_LONGING, INVALID_TIMER); } @@ -6803,6 +7896,88 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const status_change_end(tbl, SC_STOP, INVALID_TIMER); } break; + /** + * 3rd Stuff + **/ + case SC_MILLENNIUMSHIELD: + clif_millenniumshield(sd,0); + break; + case SC_HALLUCINATIONWALK: + sc_start(bl,SC_HALLUCINATIONWALK_POSTDELAY,100,sce->val1,skill_get_time2(GC_HALLUCINATIONWALK,sce->val1)); + break; + case SC_WHITEIMPRISON: + if( tid == -1 ) + break; // Terminated by Damage + clif_damage(bl,bl,0,0,0,400*sce->val1,0,0,0); + status_zap(bl,400*sce->val1,0); + break; + case SC_WUGDASH: + { + struct unit_data *ud = unit_bl2ud(bl); + if (ud) { + ud->state.running = 0; + if (ud->walktimer != -1) + unit_stop_walking(bl,1); + } + } + break; + case SC_ADORAMUS: + status_change_end(bl, SC_BLIND, -1); + break; + /* + case SC__SHADOWFORM: + { + struct map_session_data *s_sd = map_id2sd(sce->val2); + if( !s_sd ) + break; + s_sd->shadowform_id = 0; + } + break; + case SC_SITDOWN_FORCE: + if( sd && pc_issit(sd) ) + { + pc_setstand(sd); + clif_standing(bl,true); + } + break; + case SC_NEUTRALBARRIER_MASTER: + case SC_STEALTHFIELD_MASTER: + if( sce->val2 ) + { + struct skill_unit_group* group = skill_id2group(sce->val2); + sce->val2 = 0; + skill_delunitgroup(group); + } + break; + case SC_BANDING: + { + struct skill_unit_group *group; + if(sce->val4) + { + group = skill_id2group(sce->val4); + sce->val4 = 0; + skill_delunitgroup(group); + } + } + break; + case SC_CURSEDCIRCLE_ATKER: + if( sce->val3 ) + map_foreachinrange(status_change_timer_sub, bl, skill_get_splash(SR_CURSEDCIRCLE, sce->val1),BL_CHAR, bl, sce, SC_CURSEDCIRCLE_TARGET, gettick()); + break; + case SC_RAISINGDRAGON: + if( sd && sce->val2 && !pc_isdead(sd) ) + { + int i; + i = min(sd->spiritball,5); + pc_delspiritball(sd, sd->spiritball, 0); + status_change_end(bl, SC_EXPLOSIONSPIRITS, -1); + while( i > 0 ) + { + pc_addspiritball(sd, skill_get_time(MO_CALLSPIRITS, pc_checkskill(sd,MO_CALLSPIRITS)), 5); + --i; + } + } + break;*/ } opt_flag = 1; @@ -6832,6 +8007,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const opt_flag|= 2|4; //Check for warp trigger + AoE trigger break; case SC_CLOAKING: + case SC_CLOAKINGEXCEED: sc->option &= ~OPTION_CLOAK; opt_flag|= 2; break; @@ -6962,7 +8138,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const //On Aegis, when turning off a status change, first goes the sc packet, then the option packet. if( vd && (pcdb_checkid(vd->class_) || bl->type == BL_MER ) ) - clif_status_change(bl,StatusIconChangeTable[type],0,0); + clif_status_change(bl,StatusIconChangeTable[type],0,0,0,0,0); else if (sd) clif_status_load(bl,StatusIconChangeTable[type],0); @@ -7354,6 +8530,461 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data) return 0; } break; + case SC_ABUNDANCE: + if(--(sce->val4) > 0) + { + if( !sc->data[SC_BERSERK] ) + status_heal(bl,0,60,0); + sc_timer_next(10000+tick, status_change_timer, bl->id, data); + } + break; + + case SC_PYREXIA: + if( --(sce->val4) >= 0 ) + { + bool flag; + map_freeblock_lock(); + clif_damage(bl,bl,tick,status_get_amotion(bl),0,100,0,0,0); + status_fix_damage(NULL,bl,100,0); + flag = !sc->data[type]; + map_freeblock_unlock(); + if( !flag ) + { + if( sce->val4 == 10 ) + sc_start(bl,SC_BLIND,100,sce->val1,30000); // Blind status for the final 30 seconds + sc_timer_next(3000+tick,status_change_timer,bl->id,data); + } + return 0; + } + break; + + case SC_LEECHESEND: + if( --(sce->val4) >= 0 ) + { + bool flag; + int damage = status->max_hp/100; + if( sd && (sd->status.class_ == JOB_GUILLOTINE_CROSS || sd->status.class_ == JOB_GUILLOTINE_CROSS_T ) ) + damage += 3 * status->vit; + else + damage += 7 * status->vit; + + unit_skillcastcancel(bl,2); + + map_freeblock_lock(); + status_zap(bl,damage,0); + flag = !sc->data[type]; + map_freeblock_unlock(); + if( !flag ) { + sc_timer_next(1000 + tick, status_change_timer, bl->id, data ); + } + return 0; + } + break; + + case SC_MAGICMUSHROOM: + if( --(sce->val4) >= 0 ) + { + bool flag = 0; + int damage = status->max_hp * 3 / 100; + if( status->hp <= damage ) + damage = status->hp - 1; // Cannot Kill + + if( damage > 0 ) + { // 3% Damage each 4 seconds + map_freeblock_lock(); + status_zap(bl,damage,0); + flag = !sc->data[type]; // Killed? Should not + map_freeblock_unlock(); + } + + if( !flag ) + { // Random Skill Cast + if( sd ) + { + int mushroom_skillid = 0, i; + unit_stop_attack(bl); + unit_skillcastcancel(bl,1); + do + { + i = rand() % MAX_SKILL_MAGICMUSHROOM_DB; + mushroom_skillid = skill_magicmushroom_db[i].skillid; + } + while( mushroom_skillid == 0 ); + + switch( skill_get_casttype(mushroom_skillid) ) + { // Magic Mushroom skills are buffs or area damage + case CAST_GROUND: + skill_castend_pos2(bl,bl->x,bl->y,mushroom_skillid,1,tick,0); + break; + case CAST_NODAMAGE: + skill_castend_nodamage_id(bl,bl,mushroom_skillid,1,tick,0); + break; + case CAST_DAMAGE: + skill_castend_damage_id(bl,bl,mushroom_skillid,1,tick,0); + break; + } + } + + clif_emotion(bl,18); + sc_timer_next(4000+tick,status_change_timer,bl->id,data); + } + return 0; + } + break; + + case SC_TOXIN: + if( --(sce->val4) >= 0 ) + { //Damage is every 10 seconds including 3%sp drain. + bool flag; + map_freeblock_lock(); + clif_damage(bl,bl,tick,status_get_amotion(bl),1,1,0,0,0); + status_damage(NULL,bl,1,status->max_sp*3/100,0,16); + flag = !sc->data[type]; + map_freeblock_unlock(); + if( !flag ) { + sc_timer_next(10000 + tick, status_change_timer, bl->id, data ); + } + return 0; + } + break; + + case SC_OBLIVIONCURSE: + if( --(sce->val4) >= 0 ) + { + clif_emotion(bl,1); + sc_timer_next(3000 + tick, status_change_timer, bl->id, data ); + return 0; + } + break; + + case SC_WEAPONBLOCKING: + if( --(sce->val4) >= 0 ) + { + if( !status_charge(bl,0,3) ) + break; + sc_timer_next(3000+tick,status_change_timer,bl->id,data); + return 0; + } + break; + + case SC_CLOAKINGEXCEED: + if(!status_charge(bl,0,10-sce->val1)) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + + case SC_RENOVATIO: + if( --(sce->val4) >= 0 ) + { + status_heal(bl, status->max_hp * 3 / 100, 0, 2); + sc_timer_next(5000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_BURNING: + if( --(sce->val4) >= 0 ) + { + struct block_list *src = map_id2bl(sce->val3); + int flag, damage = 3 * status_get_max_hp(bl) / 100; // Non Elemental Damage + if( status ) + damage += battle_attr_fix(NULL, bl, sce->val2, ELE_FIRE, status->def_ele, status->ele_lv); + + map_freeblock_lock(); + status_fix_damage(src,bl,damage,clif_damage(bl,bl,tick,0,0,damage,0,0,0)); + flag = !sc->data[type]; + map_freeblock_unlock(); + if( !flag ) {// Target still lives. [LimitLine] + sc_timer_next(2000 + tick, status_change_timer, bl->id, data); + } + return 0; + } + break; + + case SC_FEAR: + if( --(sce->val4) >= 0 ) + { + if( sce->val2 > 0 ) + sce->val2--; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_SPHERE_1: + case SC_SPHERE_2: + case SC_SPHERE_3: + case SC_SPHERE_4: + case SC_SPHERE_5: + if( --(sce->val4) >= 0 ) + { + if( !status_charge(bl, 0, 1) ) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_READING_SB: + if( !status_charge(bl, 0, sce->val2) ) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + + case SC_ELECTRICSHOCKER: + if( --(sce->val4) >= 0 ) + { + status_charge(bl, 0, status->max_sp / 100 * sce->val1 ); + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_CAMOUFLAGE: + if( !status_charge(bl,0,7 - sce->val1) ) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + + case SC__REPRODUCE: + if(!status_charge(bl, 0, 1)) + break; + sc_timer_next(1000+tick, status_change_timer, bl->id, data); + return 0; + + case SC__SHADOWFORM: + if( --(sce->val4) >= 0 ) + { + if( !status_charge(bl, 0, sce->val1 - (sce->val1 - 1)) ) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC__INVISIBILITY: + if( --(sce->val4) >= 0 ) + { + if( !status_charge(bl, 0, (status->sp * 6 - sce->val1) / 100) )// 6% - skilllv. + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_STRIKING: + if( --(sce->val4) >= 0 ) + { + if( !status_charge(bl,0, sce->val1 ) ) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_BLOODSUCKER: + if( --(sce->val4) >= 0 ) + { + struct block_list *src = map_id2bl(sce->val2); + int damage; + bool flag; + if( !src || (src && (status_isdead(src) || src->m != bl->m || distance_bl(src, bl) >= 12)) ) + break; + map_freeblock_lock(); + damage = skill_attack(skill_get_type(GN_BLOOD_SUCKER), src, src, bl, GN_BLOOD_SUCKER, sce->val1, tick, 0); + flag = !sc->data[type]; + map_freeblock_unlock(); + status_heal(src, damage, 0, 0); + clif_skill_nodamage(src, bl, GN_BLOOD_SUCKER, 0, 1); + if (!flag) { + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + } + return 0; + } + break; + + case SC_VOICEOFSIREN: + if( --(sce->val4) >= 0 ) + { + clif_emotion(bl,3); + sc_timer_next(2000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_DEEPSLEEP: + if( --(sce->val4) >= 0 ) + { // Recovers 1% HP/SP every 2 seconds. + status_heal(bl, status->max_hp / 100, status->max_sp / 100, 2); + sc_timer_next(2000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_SIRCLEOFNATURE: + if( --(sce->val4) >= 0 ) + { + if( !status_charge(bl,0,sce->val2) ) + break; + status_heal(bl, sce->val3, 0, 1); + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_SONGOFMANA: + if( --(sce->val4) >= 0 ) + { + status_heal(bl,0,sce->val3,3); + sc_timer_next(3000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + + case SC_SATURDAYNIGHTFEVER: + // 1% HP/SP drain every 3 seconds [Jobbie] + if( --(sce->val3) >= 0 ) + { + int hp = status->hp / 100; + int sp = status->sp / 100; + if( !status_charge(bl, hp, sp) ) + break; + sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_CRYSTALIZE: + if( --(sce->val4) >= 0 ) + { // Drains 2% of HP and 1% of SP every seconds. + status_charge(bl, status->max_hp * 2 / 100, status->max_sp / 100); + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_FORCEOFVANGUARD: + if( !status_charge(bl,0,20) ) + break; + sc_timer_next(6000 + tick, status_change_timer, bl->id, data); + return 0; + + case SC_BANDING: + if( status_charge(bl, 0, 7 - sce->val1) ) + { + //if( sd ) pc_banding(sd, sce->val1); + sc_timer_next(5000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_REFLECTDAMAGE: + if( --(sce->val4) >= 0 ) { + if( !status_charge(bl,0,sce->val3) ) + break; + sc_timer_next(10000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_OVERHEAT_LIMITPOINT: + if( --(sce->val1) > 0 ) { // Cooling + sc_timer_next(30000 + tick, status_change_timer, bl->id, data); + } + break; + + case SC_OVERHEAT: + { + int flag, damage = status->max_hp / 100; // Suggestion 1% each second + if( damage >= status->hp ) damage = status->hp - 1; // Do not kill, just keep you with 1 hp minimum + map_freeblock_lock(); + status_fix_damage(NULL,bl,damage,clif_damage(bl,bl,tick,0,0,damage,0,0,0)); + flag = !sc->data[type]; + map_freeblock_unlock(); + if( !flag ) { + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + } + } + break; + + case SC_MAGNETICFIELD: + { + if( --(sce->val3) <= 0 ) + break; // Time out + if( sce->val2 == bl->id ) + { + if( !status_charge(bl,0,14 + (3 * sce->val1)) ) + break; // No more SP status should end, and in the next second will end for the other affected players + } + else + { + struct block_list *src = map_id2bl(sce->val2); + struct status_change *ssc; + if( !src || (ssc = status_get_sc(src)) == NULL || !ssc->data[SC_MAGNETICFIELD] ) + break; // Source no more under Magnetic Field + } + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + } + break; + + case SC_INSPIRATION: + if(--(sce->val4) >= 0) + { + int hp = status->max_hp * (7-sce->val1) / 100; + int sp = status->max_sp * (9-sce->val1) / 100; + + if( !status_charge(bl,hp,sp) ) break; + + sc_timer_next(1000+tick,status_change_timer,bl->id, data); + return 0; + } + break; + + case SC_RAISINGDRAGON: + // 1% every 5 seconds [Jobbie] + if( --(sce->val3)>0 && status_charge(bl, sce->val2, 0) ) + { + if( !sc->data[type] ) return 0; + sc_timer_next(5000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_CIRCLE_OF_FIRE: + case SC_FIRE_CLOAK: + case SC_WATER_DROP: + case SC_WATER_SCREEN: + case SC_WIND_CURTAIN: + case SC_WIND_STEP: + case SC_STONE_SHIELD: + case SC_SOLID_SKIN: + if( !status_charge(bl,0,sce->val2) ) + { + struct block_list *s_bl = battle_get_master(bl); + if( s_bl ) + status_change_end(s_bl,type+1,-1); + status_change_end(bl,type,-1); + break; + } + sc_timer_next(2000 + tick, status_change_timer, bl->id, data); + return 0; + + case SC_STOMACHACHE: + if( --(sce->val4) > 0 ) + { + status_charge(bl,0,sce->val2); // Reduce 8 every 10 seconds. + if( sd && !pc_issit(sd) ) // Force to sit every 10 seconds. + { + pc_stop_walking(sd,1|4); + pc_stop_attack(sd); + pc_setsit(sd); + clif_sitting(bl); + } + sc_timer_next(10000 + tick, status_change_timer, bl->id, data); + } + break; + } // default for all non-handled control paths is to end the status @@ -7384,11 +9015,15 @@ int status_change_timer_sub(struct block_list* bl, va_list ap) case SC_CONCENTRATE: status_change_end(bl, SC_HIDING, INVALID_TIMER); status_change_end(bl, SC_CLOAKING, INVALID_TIMER); + status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); + status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER); break; case SC_RUWACH: /* ルアフ */ - if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING])) { + if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] || tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_CLOAKINGEXCEED])) { status_change_end(bl, SC_HIDING, INVALID_TIMER); status_change_end(bl, SC_CLOAKING, INVALID_TIMER); + status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER); + status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); if(battle_check_target( src, bl, BCT_ENEMY ) > 0) skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0); } @@ -7716,6 +9351,25 @@ static int status_natural_heal_timer(int tid, unsigned int tick, int id, intptr_ * size_fix.txt - size adjustment table for weapons * refine_db.txt - refining data table *------------------------------------------*/ +#if RRMODE +static bool status_readdb_job_re(char* fields[], int columns, int current) { + int idx, class_; + unsigned int i; + + class_ = atoi(fields[0]); + + if(!pcdb_checkid(class_)) { + ShowWarning("status_readdb_job_re: Invalid job class %d specified.\n", class_); + return false; + } + idx = pc_class2idx(class_); + + for(i = 0; i < RE_JOB_DB_MAX; i++) { + re_job_db[idx][i] = atoi(fields[i+1]); + } + return true; +} +#endif static bool status_readdb_job1(char* fields[], int columns, int current) {// Job-specific values (weight, HP, SP, ASPD) int idx, class_; @@ -7801,7 +9455,9 @@ int status_readdb(void) memset(hp_coefficient2, 0, sizeof(hp_coefficient2)); memset(sp_coefficient, 0, sizeof(sp_coefficient)); memset(aspd_base, 0, sizeof(aspd_base)); - +#if RRMODE + memset(re_job_db, 0, sizeof(re_job_db)); +#endif // job_db2.txt memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus @@ -7828,6 +9484,10 @@ int status_readdb(void) sv_readdb(db_path, "size_fix.txt", ',', MAX_WEAPON_TYPE, MAX_WEAPON_TYPE, ARRAYLENGTH(atkmods), &status_readdb_sizefix); sv_readdb(db_path, "refine_db.txt", ',', 3+MAX_REFINE+1, 3+MAX_REFINE+1, ARRAYLENGTH(percentrefinery), &status_readdb_refine); +#if RRMODE + sv_readdb(db_path, "re_job_db.txt", ',', 1+RE_JOB_DB_MAX, 1+RE_JOB_DB_MAX, -1, &status_readdb_job_re); +#endif + return 0; } diff --git a/src/map/status.h b/src/map/status.h index dcd532577..8572b1bbb 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -329,7 +329,248 @@ typedef enum sc_type { SC_FOOD_VIT_CASH, SC_FOOD_DEX_CASH, SC_FOOD_INT_CASH, - SC_FOOD_LUK_CASH, + SC_FOOD_LUK_CASH,//308 + /** + * 3rd + **/ + SC_FEAR,//309 + SC_BURNING,//310 + SC_FREEZING,//311 + /** + * Rune Knight + **/ + SC_ENCHANTBLADE,//312 + SC_DEATHBOUND,//313 + SC_MILLENNIUMSHIELD, + SC_CRUSHSTRIKE,//315 + SC_REFRESH, + SC_REUSE_REFRESH, + SC_GIANTGROWTH, + SC_STONEHARDSKIN, + SC_VITALITYACTIVATION,//320 + SC_STORMBLAST, + SC_FIGHTINGSPIRIT, + SC_ABUNDANCE, + /** + * Arch Bishop + **/ + SC_ADORAMUS, + SC_EPICLESIS,//325 + SC_ORATIO, + SC_LAUDAAGNUS, + SC_LAUDARAMUS, + SC_RENOVATIO, + SC_EXPIATIO,//330 + SC_DUPLELIGHT, + SC_SECRAMENT, + /** + * Warlock + **/ + SC_WHITEIMPRISON, + SC_MARSHOFABYSS, + SC_RECOGNIZEDSPELL,//335 + SC_STASIS, + SC_SPHERE_1, + SC_SPHERE_2, + SC_SPHERE_3, + SC_SPHERE_4,//340 + SC_SPHERE_5, + SC_READING_SB, + SC_FREEZINGSPELL, + /** + * Ranger + **/ + SC_FEARBREEZE, + SC_ELECTRICSHOCKER,//345 + SC_WUGDASH, + SC_BITE, + SC_CAMOUFLAGE, + /** + * Mechanic + **/ + SC_ACCELERATION, + SC_HOVERING,//350 + SC_SHAPESHIFT, + SC_INFRAREDSCAN, + SC_ANALYZE, + SC_MAGNETICFIELD, + SC_NEUTRALBARRIER,//355 + SC_NEUTRALBARRIER_MASTER, + SC_STEALTHFIELD, + SC_STEALTHFIELD_MASTER, + SC_OVERHEAT, + SC_OVERHEAT_LIMITPOINT,//360 + /** + * Guillotine Cross + **/ + SC_VENOMIMPRESS, + SC_POISONINGWEAPON, + SC_WEAPONBLOCKING, + SC_CLOAKINGEXCEED, + SC_HALLUCINATIONWALK,//365 + SC_HALLUCINATIONWALK_POSTDELAY, + SC_ROLLINGCUTTER, + SC_TOXIN, + SC_PARALYSE, + SC_VENOMBLEED,//370 + SC_MAGICMUSHROOM, + SC_DEATHHURT, + SC_PYREXIA, + SC_OBLIVIONCURSE, + SC_LEECHESEND,//375 + /** + * Royal Guard + **/ + SC_REFLECTDAMAGE, + SC_FORCEOFVANGUARD, + SC_SHIELDSPELL_DEF, + SC_SHIELDSPELL_MDEF, + SC_SHIELDSPELL_REF,//380 + SC_EXEEDBREAK, + SC_PRESTIGE, + SC_BANDING, + SC_BANDING_DEFENCE, + SC_EARTHDRIVE,//385 + SC_INSPIRATION, + /** + * Sorcerer + **/ + SC_SPELLFIST, + SC_CRYSTALIZE, + SC_STRIKING, + SC_WARMER,//390 + SC_VACUUM_EXTREME, + SC_PROPERTYWALK, + /** + * Minstrel / Wanderer + **/ + SC_SWINGDANCE, + SC_SYMPHONYOFLOVER, + SC_MOONLITSERENADE,//395 + SC_RUSHWINDMILL, + SC_ECHOSONG, + SC_HARMONIZE, + SC_VOICEOFSIREN, + SC_DEEPSLEEP,//400 + SC_SIRCLEOFNATURE, + SC_GLOOMYDAY, + SC_GLOOMYDAY_SK, + SC_SONGOFMANA, + SC_DANCEWITHWUG,//405 + SC_SATURDAYNIGHTFEVER, + SC_LERADSDEW, + SC_MELODYOFSINK, + SC_BEYONDOFWARCRY, + SC_UNLIMITEDHUMMINGVOICE,//410 + SC_SITDOWN_FORCE, + /** + * Sura + **/ + SC_CRESCENTELBOW, + SC_CURSEDCIRCLE_ATKER, + SC_CURSEDCIRCLE_TARGET, + SC_LIGHTNINGWALK,//415 + SC_RAISINGDRAGON, + SC_GT_ENERGYGAIN, + SC_GT_CHANGE, + SC_GT_REVITALIZE, + /** + * Genetic + **/ + SC_GN_CARTBOOST,//420 + SC_THORNSTRAP, + SC_BLOODSUCKER, + SC_SMOKEPOWDER, + SC_TEARGAS, + SC_MANDRAGORA,//425 + SC_STOMACHACHE, + SC_MYSTERIOUS_POWDER, + SC_MELON_BOMB, + SC_BANANA_BOMB, + SC_BANANA_BOMB_SITDOWN,//430 + SC_SAVAGE_STEAK, + SC_COCKTAIL_WARG_BLOOD, + SC_MINOR_BBQ, + SC_SIROMA_ICE_TEA, + SC_DROCERA_HERB_STEAMED,//435 + SC_PUTTI_TAILS_NOODLES, + SC_BOOST500, + SC_FULL_SWING_K, + SC_MANA_PLUS, + SC_MUSTLE_M,//440 + SC_LIFE_FORCE_F, + SC_EXTRACT_WHITE_POTION_Z, + SC_VITATA_500, + SC_EXTRACT_SALAMINE_JUICE, + /** + * Shadow Chaser + **/ + SC__REPRODUCE,//445 + SC__AUTOSHADOWSPELL, + SC__SHADOWFORM, + SC__BODYPAINT, + SC__INVISIBILITY, + SC__DEADLYINFECT,//450 + SC__ENERVATION, + SC__GROOMY, + SC__IGNORANCE, + SC__LAZINESS, + SC__UNLUCKY,//455 + SC__WEAKNESS, + SC__STRIPACCESSORY, + SC__MANHOLE, + SC_CHAOS, + SC__BLOODYLUST,//460 + /** + * Elemental Spirits + **/ + SC_CIRCLE_OF_FIRE, + SC_CIRCLE_OF_FIRE_OPTION, + SC_FIRE_CLOAK, + SC_FIRE_CLOAK_OPTION, + SC_WATER_SCREEN,//465 + SC_WATER_SCREEN_OPTION, + SC_WATER_DROP, + SC_WATER_DROP_OPTION, + SC_WATER_BARRIER, + SC_WIND_STEP,//470 + SC_WIND_STEP_OPTION, + SC_WIND_CURTAIN, + SC_WIND_CURTAIN_OPTION, + SC_ZEPHYR, + SC_SOLID_SKIN,//475 + SC_SOLID_SKIN_OPTION, + SC_STONE_SHIELD, + SC_STONE_SHIELD_OPTION, + SC_POWER_OF_GAIA, + SC_PYROTECHNIC,//480 + SC_PYROTECHNIC_OPTION, + SC_HEATER, + SC_HEATER_OPTION, + SC_TROPIC, + SC_TROPIC_OPTION,//485 + SC_AQUAPLAY, + SC_AQUAPLAY_OPTION, + SC_COOLER, + SC_COOLER_OPTION, + SC_CHILLY_AIR,//490 + SC_CHILLY_AIR_OPTION, + SC_GUST, + SC_GUST_OPTION, + SC_BLAST, + SC_BLAST_OPTION,//495 + SC_WILD_STORM, + SC_WILD_STORM_OPTION, + SC_PETROLOGY, + SC_PETROLOGY_OPTION, + SC_CURSED_SOIL,//500 + SC_CURSED_SOIL_OPTION, + SC_UPHEAVAL, + SC_UPHEAVAL_OPTION, + SC_TIDAL_WEAPON, + SC_TIDAL_WEAPON_OPTION,//505 + SC_ROCK_CRUSHER, + SC_ROCK_CRUSHER_ATK, SC_MAX, //Automatically updated max, used in for's to check we are within bounds. } sc_type; @@ -652,7 +893,7 @@ enum si_type { SI_CASH_PLUSONLYJOBEXP = 312, // SI_PARTYFLEE = 313, // SI_ANGEL_PROTECT = 314, -/* + SI_ENDURE_MDEF = 315, SI_ENCHANTBLADE = 316, SI_DEATHBOUND = 317, @@ -701,11 +942,11 @@ enum si_type { SI_CAMOUFLAGE = 360, SI_ACCELERATION = 361, SI_HOVERING = 362, - SI_SUMMON1 = 363, - SI_SUMMON2 = 364, - SI_SUMMON3 = 365, - SI_SUMMON4 = 366, - SI_SUMMON5 = 367, + SI_SPHERE_1 = 363, + SI_SPHERE_2 = 364, + SI_SPHERE_3 = 365, + SI_SPHERE_4 = 366, + SI_SPHERE_5 = 367, SI_MVPCARD_TAOGUNKA = 368, SI_MVPCARD_MISTRESS = 369, SI_MVPCARD_ORCHERO = 370, @@ -719,15 +960,15 @@ enum si_type { SI_NEUTRALBARRIER_MASTER = 378, SI_STEALTHFIELD = 379, SI_STEALTHFIELD_MASTER = 380, -*/ + SI_MANU_ATK = 381, SI_MANU_DEF = 382, SI_SPL_ATK = 383, SI_SPL_DEF = 384, -// SI_REPRODUCE = 385, + SI_REPRODUCE = 385, SI_MANU_MATK = 386, SI_SPL_MATK = 387, -/* + SI_STR_SCROLL = 388, SI_INT_SCROLL = 389, SI_LG_REFLECTDAMAGE = 390, @@ -812,7 +1053,7 @@ enum si_type { SI_BLOCKING_PLAY = 469, SI_MANDRAGORA = 470, SI_ACTIVATE = 471, - SI_AB_SECRAMENT = 472, + SI_SECRAMENT = 472, SI_ASSUMPTIO2 = 473, SI_TK_SEVENWIND = 474, SI_LIMIT_ODINS_RECALL = 475, @@ -912,7 +1153,7 @@ enum si_type { SI_WIND_INSIGNIA = 569, SI_EARTH_INSIGNIA = 570, SI_EQUIPED_FLOOR = 571, -*/ + SI_ALL_RIDING = 613,//awesome 571-613 gap, we're missing quite a few stuff here. }; // JOINTBEAT stackable ailments @@ -1033,6 +1274,7 @@ enum { OPTION_DRAGON3 = 0x01000000, OPTION_DRAGON4 = 0x02000000, OPTION_DRAGON5 = 0x04000000, + OPTION_MOUNTING = 0x08000000,//dull name (cuz ind named it :/) // compound constants OPTION_CART = OPTION_CART1|OPTION_CART2|OPTION_CART3|OPTION_CART4|OPTION_CART5, OPTION_DRAGON = OPTION_DRAGON1|OPTION_DRAGON2|OPTION_DRAGON3|OPTION_DRAGON4|OPTION_DRAGON5, @@ -1123,6 +1365,12 @@ struct status_data { aspd_rate; unsigned char def_ele, ele_lv, +#if RRMODE + /** + * in RE weapon level is used in several areas, keeping it here saves performance + **/ + wlv, +#endif size, race; signed char def, mdef; @@ -1187,7 +1435,8 @@ struct status_change { //TODO: See if it is possible to implement the following SC's without requiring extra parameters while the SC is inactive. unsigned char jb_flag; //Joint Beat type flag unsigned short mp_matk_min, mp_matk_max; //Previous matk min/max for ground spells (Amplify magic power) - int sg_id; //ID of the previous Storm gust that hit you + //int sg_id; //ID of the previous Storm gust that hit you + short comet_x, comet_y; // Point where src casted Comet - required to calculate damage from this point unsigned char sg_counter; //Storm gust counter (previous hits from storm gust) struct status_change_entry *data[SC_MAX]; }; @@ -1260,6 +1509,12 @@ unsigned char status_calc_attack_element(struct block_list *bl, struct status_ch #define status_get_race(bl) status_get_status_data(bl)->race #define status_get_size(bl) status_get_status_data(bl)->size #define status_get_mode(bl) status_get_status_data(bl)->mode +#if RRMODE + /** + * in RE weapon level is used in several areas, keeping it here saves performance + **/ + #define status_get_wlv(bl) status_get_status_data(bl)->wlv +#endif int status_get_party_id(struct block_list *bl); int status_get_guild_id(struct block_list *bl); int status_get_emblem_id(struct block_list *bl); diff --git a/src/map/unit.c b/src/map/unit.c index a11b5dc28..52b479b25 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -444,7 +444,7 @@ int unit_run(struct block_list *bl) if(to_x == bl->x && to_y == bl->y) { //If you can't run forward, you must be next to a wall, so bounce back. [Skotlex] - clif_status_change(bl, SI_BUMP, 1, 0); + clif_status_change(bl, SI_BUMP, 1, 0, 0, 0, 0); //Set running to 0 beforehand so status_change_end knows not to enable spurt [Kevin] unit_bl2ud(bl)->state.running = 0; @@ -452,7 +452,7 @@ int unit_run(struct block_list *bl) skill_blown(bl,bl,skill_get_blewcount(TK_RUN,lv),unit_getdir(bl),0); clif_fixpos(bl); //Why is a clif_slide (skill_blown) AND a fixpos needed? Ask Aegis. - clif_status_change(bl, SI_BUMP, 0, 0); + clif_status_change(bl, SI_BUMP, 0, 0, 0, 0, 0); return 0; } if (unit_walktoxy(bl, to_x, to_y, 1)) @@ -464,7 +464,7 @@ int unit_run(struct block_list *bl) } while (--i > 0 && !unit_walktoxy(bl, to_x, to_y, 1)); if (i==0) { // copy-paste from above - clif_status_change(bl, SI_BUMP, 1, 0); + clif_status_change(bl, SI_BUMP, 1, 0, 0, 0, 0); //Set running to 0 beforehand so status_change_end knows not to enable spurt [Kevin] unit_bl2ud(bl)->state.running = 0; @@ -472,7 +472,73 @@ int unit_run(struct block_list *bl) skill_blown(bl,bl,skill_get_blewcount(TK_RUN,lv),unit_getdir(bl),0); clif_fixpos(bl); - clif_status_change(bl, SI_BUMP, 0, 0); + clif_status_change(bl, SI_BUMP, 0, 0, 0, 0, 0); + return 0; + } + return 1; +} + +//Exclusive function to Wug Dash state. [Jobbie/3CeAM] +int unit_wugdash(struct block_list *bl, struct map_session_data *sd) { + struct status_change *sc = status_get_sc(bl); + short to_x,to_y,dir_x,dir_y; + int lv; + int i; + if (!(sc && sc->data[SC_WUGDASH])) + return 0; + + nullpo_ret(sd); + nullpo_ret(bl); + + if (!unit_can_move(bl)) { + status_change_end(bl,SC_WUGDASH,-1); + return 0; + } + + lv = sc->data[SC_WUGDASH]->val1; + dir_x = dirx[sc->data[SC_WUGDASH]->val2]; + dir_y = diry[sc->data[SC_WUGDASH]->val2]; + + to_x = bl->x; + to_y = bl->y; + for(i=0;i<AREA_SIZE;i++) + { + if(!map_getcell(bl->m,to_x+dir_x,to_y+dir_y,CELL_CHKPASS)) + break; + + if(sc->data[SC_WUGDASH] && map_count_oncell(bl->m, to_x+dir_x, to_y+dir_y, BL_PC|BL_MOB|BL_NPC)) + break; + + to_x += dir_x; + to_y += dir_y; + } + + if(to_x == bl->x && to_y == bl->y) { + + unit_bl2ud(bl)->state.running = 0; + status_change_end(bl,SC_WUGDASH,-1); + + if( sd ){ + clif_fixpos(bl); + skill_castend_damage_id(bl, &sd->bl, RA_WUGDASH, lv, gettick(), SD_LEVEL); + } + return 0; + } + if (unit_walktoxy(bl, to_x, to_y, 1)) + return 1; + do { + to_x -= dir_x; + to_y -= dir_y; + } while (--i > 0 && !unit_walktoxy(bl, to_x, to_y, 1)); + if (i==0) { + + unit_bl2ud(bl)->state.running = 0; + status_change_end(bl,SC_WUGDASH,-1); + + if( sd ){ + clif_fixpos(bl); + skill_castend_damage_id(bl, &sd->bl, RA_WUGDASH, lv, gettick(), SD_LEVEL); + } return 0; } return 1; @@ -992,7 +1058,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, sh if( !target || src->m != target->m || !src->prev || !target->prev ) return 0; - if( mob_ksprotected(src, target) ) + if( battle_config.ksprotection && sd && mob_ksprotected(src, target) ) return 0; //Normally not needed because clif.c checks for it, but the at/char/script commands don't! [Skotlex] @@ -1130,7 +1196,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, sh // moved here to prevent Suffragium from ending if skill fails if (!(skill_get_castnodex(skill_num, skill_lv)&2)) - casttime = skill_castfix_sc(src, casttime); + casttime = skill_castfix_sc(src, casttime, skill_num, skill_lv); if( casttime > 0 || temp ) { @@ -1272,7 +1338,7 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, sh // moved here to prevent Suffragium from ending if skill fails if (!(skill_get_castnodex(skill_num, skill_lv)&2)) - casttime = skill_castfix_sc(src, casttime); + casttime = skill_castfix_sc(src, casttime, skill_num, skill_lv); ud->state.skillcastcancel = castcancel&&casttime>0?1:0; if( !sd || sd->skillitem != skill_num || skill_get_cast(skill_num,skill_lv) ) diff --git a/src/map/unit.h b/src/map/unit.h index e349a7466..f7b072739 100644 --- a/src/map/unit.h +++ b/src/map/unit.h @@ -126,6 +126,10 @@ int unit_changeviewsize(struct block_list *bl,short size); // 初期化ルーチン int do_init_unit(void); int do_final_unit(void); +/** + * Ranger + **/ +int unit_wugdash(struct block_list *bl, struct map_session_data *sd); extern const short dirx[8]; extern const short diry[8]; |