summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorshennetsind <shennetsind@54d463be-8e91-2dee-dedb-b68131a5f0ec>2012-04-20 18:05:14 +0000
committershennetsind <shennetsind@54d463be-8e91-2dee-dedb-b68131a5f0ec>2012-04-20 18:05:14 +0000
commit92249d9f8e219916670589ba6c5f9fce47808ed8 (patch)
tree0d1b0aa06c3490b3dbf7140bebd8bda126bfd65e /src
parent1b3bd9ff1725a68d66fd4015c60d6fe28e4b2334 (diff)
downloadhercules-92249d9f8e219916670589ba6c5f9fce47808ed8.tar.gz
hercules-92249d9f8e219916670589ba6c5f9fce47808ed8.tar.bz2
hercules-92249d9f8e219916670589ba6c5f9fce47808ed8.tar.xz
hercules-92249d9f8e219916670589ba6c5f9fce47808ed8.zip
Initial support for Genetic, Sorcerer and Elemental Summons. Special Thanks to 3CeAM for the base.
Notice this revision onwards requires you to update your char sql table and add the elemental sql table (check sql-files/upgrade_svn15885_log.sql) If you step by any bugs, let us know at http://rathena.org/board/tracker/ Thank you very much. ARRIBA ARRIBA. git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@15885 54d463be-8e91-2dee-dedb-b68131a5f0ec
Diffstat (limited to 'src')
-rw-r--r--src/char/Makefile.in4
-rw-r--r--src/char/char.c54
-rw-r--r--src/char/int_elemental.c164
-rw-r--r--src/char/int_elemental.h15
-rw-r--r--src/char/inter.c6
-rw-r--r--src/common/mmo.h19
-rw-r--r--src/map/Makefile.in4
-rw-r--r--src/map/atcommand.c3
-rw-r--r--src/map/battle.c252
-rw-r--r--src/map/chrif.c3
-rw-r--r--src/map/clif.c305
-rw-r--r--src/map/clif.h39
-rw-r--r--src/map/elemental.c825
-rw-r--r--src/map/elemental.h95
-rw-r--r--src/map/intif.c97
-rw-r--r--src/map/intif.h6
-rw-r--r--src/map/itemdb.h1
-rw-r--r--src/map/map.c11
-rw-r--r--src/map/map.h6
-rw-r--r--src/map/mob.c10
-rw-r--r--src/map/npc_chat.c2
-rw-r--r--src/map/pc.c11
-rw-r--r--src/map/pc.h3
-rw-r--r--src/map/script.c2
-rw-r--r--src/map/skill.c2245
-rw-r--r--src/map/skill.h91
-rw-r--r--src/map/status.c477
-rw-r--r--src/map/status.h22
-rw-r--r--src/map/unit.c287
29 files changed, 3857 insertions, 1202 deletions
diff --git a/src/char/Makefile.in b/src/char/Makefile.in
index 4ef8ae7ea..1c93cfa6d 100644
--- a/src/char/Makefile.in
+++ b/src/char/Makefile.in
@@ -25,8 +25,8 @@ COMMON_SQL_OBJ = ../common/obj_sql/sql.o
COMMON_H = ../common/sql.h
CHAR_OBJ = obj_sql/char.o obj_sql/inter.o obj_sql/int_party.o obj_sql/int_guild.o \
- obj_sql/int_storage.o obj_sql/int_pet.o obj_sql/int_homun.o obj_sql/int_mail.o obj_sql/int_auction.o obj_sql/int_quest.o obj_sql/int_mercenary.o
-CHAR_H = char.h inter.h int_party.h int_guild.h int_storage.h int_pet.h int_homun.h int_mail.h int_auction.h int_quest.h int_mercenary.h
+ obj_sql/int_storage.o obj_sql/int_pet.o obj_sql/int_homun.o obj_sql/int_mail.o obj_sql/int_auction.o obj_sql/int_quest.o obj_sql/int_mercenary.o obj_sql/int_elemental.o
+CHAR_H = char.h inter.h int_party.h int_guild.h int_storage.h int_pet.h int_homun.h int_mail.h int_auction.h int_quest.h int_mercenary.h int_elemental.h
HAVE_MYSQL=@HAVE_MYSQL@
ifeq ($(HAVE_MYSQL),yes)
diff --git a/src/char/char.c b/src/char/char.c
index a26f81174..46a255716 100644
--- a/src/char/char.c
+++ b/src/char/char.c
@@ -16,6 +16,7 @@
#include "int_guild.h"
#include "int_homun.h"
#include "int_mercenary.h"
+#include "int_elemental.h"
#include "int_party.h"
#include "int_storage.h"
#include "char.h"
@@ -471,7 +472,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p)
(p->option != cp->option) ||
(p->party_id != cp->party_id) || (p->guild_id != cp->guild_id) ||
(p->pet_id != cp->pet_id) || (p->weapon != cp->weapon) || (p->hom_id != cp->hom_id) ||
- (p->shield != cp->shield) || (p->head_top != cp->head_top) ||
+ (p->ele_id != cp->ele_id) || (p->shield != cp->shield) || (p->head_top != cp->head_top) ||
(p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom) || (p->delete_date != cp->delete_date) ||
(p->rename != cp->rename) || (p->robe != cp->robe)
)
@@ -480,7 +481,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p)
"`base_exp`='%u', `job_exp`='%u', `zeny`='%d',"
"`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
"`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
- "`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',"
+ "`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',`elemental_id`='%d',"
"`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
"`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d',"
"`delete_date`='%lu',`robe`='%d'"
@@ -489,7 +490,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p)
p->base_exp, p->job_exp, p->zeny,
p->max_hp, p->hp, p->max_sp, p->sp, p->status_point, p->skill_point,
p->str, p->agi, p->vit, p->int_, p->dex, p->luk,
- p->option, p->party_id, p->guild_id, p->pet_id, p->hom_id,
+ p->option, p->party_id, p->guild_id, p->pet_id, p->hom_id, p->ele_id,
p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom,
mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y,
mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->rename,
@@ -969,7 +970,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT "
"`char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`,"
"`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`,"
- "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`hair`,"
+ "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`,"
"`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`,"
"`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`"
" FROM `%s` WHERE `char_id`=? LIMIT 1", char_db)
@@ -1004,28 +1005,29 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 26, SQLDT_INT, &p->guild_id, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 27, SQLDT_INT, &p->pet_id, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 28, SQLDT_INT, &p->hom_id, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 29, SQLDT_SHORT, &p->hair, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 30, SQLDT_SHORT, &p->hair_color, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 31, SQLDT_SHORT, &p->clothes_color, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 32, SQLDT_SHORT, &p->weapon, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 33, SQLDT_SHORT, &p->shield, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 34, SQLDT_SHORT, &p->head_top, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 35, SQLDT_SHORT, &p->head_mid, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 36, SQLDT_SHORT, &p->head_bottom, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 37, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 38, SQLDT_SHORT, &p->last_point.x, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 39, SQLDT_SHORT, &p->last_point.y, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 40, SQLDT_STRING, &save_map, sizeof(save_map), NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 41, SQLDT_SHORT, &p->save_point.x, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 42, SQLDT_SHORT, &p->save_point.y, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 43, SQLDT_INT, &p->partner_id, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 44, SQLDT_INT, &p->father, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 45, SQLDT_INT, &p->mother, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 46, SQLDT_INT, &p->child, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 47, SQLDT_INT, &p->fame, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 48, SQLDT_SHORT, &p->rename, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 49, SQLDT_UINT32, &p->delete_date, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 50, SQLDT_SHORT, &p->robe, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 29, SQLDT_INT, &p->ele_id, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 30, SQLDT_SHORT, &p->hair, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 31, SQLDT_SHORT, &p->hair_color, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 32, SQLDT_SHORT, &p->clothes_color, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 33, SQLDT_SHORT, &p->weapon, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 34, SQLDT_SHORT, &p->shield, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 35, SQLDT_SHORT, &p->head_top, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 36, SQLDT_SHORT, &p->head_mid, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 37, SQLDT_SHORT, &p->head_bottom, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 38, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 39, SQLDT_SHORT, &p->last_point.x, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 40, SQLDT_SHORT, &p->last_point.y, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 41, SQLDT_STRING, &save_map, sizeof(save_map), NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 42, SQLDT_SHORT, &p->save_point.x, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 43, SQLDT_SHORT, &p->save_point.y, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 44, SQLDT_INT, &p->partner_id, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 45, SQLDT_INT, &p->father, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 46, SQLDT_INT, &p->mother, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 47, SQLDT_INT, &p->child, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 48, SQLDT_INT, &p->fame, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 49, SQLDT_SHORT, &p->rename, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 50, SQLDT_UINT32, &p->delete_date, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 51, SQLDT_SHORT, &p->robe, 0, NULL, NULL)
)
{
SqlStmt_ShowDebug(stmt);
diff --git a/src/char/int_elemental.c b/src/char/int_elemental.c
new file mode 100644
index 000000000..0146c8926
--- /dev/null
+++ b/src/char/int_elemental.c
@@ -0,0 +1,164 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/mmo.h"
+#include "../common/malloc.h"
+#include "../common/strlib.h"
+#include "../common/showmsg.h"
+#include "../common/socket.h"
+#include "../common/utils.h"
+#include "../common/sql.h"
+#include "char.h"
+#include "inter.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+bool mapif_elemental_save(struct s_elemental* ele) {
+ bool flag = true;
+
+ if( ele->elemental_id == 0 ) { // Create new DB entry
+ if( SQL_ERROR == Sql_Query(sql_handle,
+ "INSERT INTO `elemental` (`char_id`,`class`,`mode`,`hp`,`sp`,`max_hp`,`max_sp`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`life_time`)"
+ "VALUES ('%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%u')",
+ ele->char_id, ele->class_, ele->mode, ele->hp, ele->sp, ele->max_hp, ele->max_sp, ele->str, ele->agi, ele->vit, ele->int_, ele->dex, ele->luk, ele->life_time) )
+ {
+ Sql_ShowDebug(sql_handle);
+ flag = false;
+ }
+ else
+ ele->elemental_id = (int)Sql_LastInsertId(sql_handle);
+ } else if( SQL_ERROR == Sql_Query(sql_handle,
+ "UPDATE `elemental` SET `char_id` = '%d', `class` = '%d', `mode` = '%d', `hp` = '%d', `sp` = '%d',"
+ "`max_hp` = '%d', `max_sp` = '%d', `str` = '%d', `agi` = '%d', `vit` = '%d', `int` = '%d', `dex` = '%d',"
+ "`luk` = '%d', `life_time` = '%u' WHERE `ele_id` = '%d'",
+ ele->char_id, ele->class_, ele->mode, ele->hp, ele->sp, ele->max_hp, ele->max_sp, ele->str, ele->agi,
+ ele->vit, ele->int_, ele->dex, ele->luk, ele->life_time, ele->elemental_id) )
+ { // Update DB entry
+ Sql_ShowDebug(sql_handle);
+ flag = false;
+ }
+ return flag;
+}
+
+bool mapif_elemental_load(int ele_id, int char_id, struct s_elemental *ele) {
+ char* data;
+
+ memset(ele, 0, sizeof(struct s_elemental));
+ ele->elemental_id = ele_id;
+ ele->char_id = char_id;
+
+ if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `class`, `mode`, `hp`, `sp`, `max_hp`, `max_sp`, `str`, `agi`, `vit`, `int`, `dex`,"
+ "`luk`, `life_time` FROM `elemental` WHERE `ele_id` = '%d' AND `char_id` = '%d'",
+ ele_id, char_id) ) {
+ Sql_ShowDebug(sql_handle);
+ return false;
+ }
+
+ if( SQL_SUCCESS != Sql_NextRow(sql_handle) ) {
+ Sql_FreeResult(sql_handle);
+ return false;
+ }
+
+ Sql_GetData(sql_handle, 0, &data, NULL); ele->class_ = atoi(data);
+ Sql_GetData(sql_handle, 1, &data, NULL); ele->mode = atoi(data);
+ Sql_GetData(sql_handle, 2, &data, NULL); ele->hp = atoi(data);
+ Sql_GetData(sql_handle, 3, &data, NULL); ele->sp = atoi(data);
+ Sql_GetData(sql_handle, 4, &data, NULL); ele->max_hp = atoi(data);
+ Sql_GetData(sql_handle, 5, &data, NULL); ele->max_sp = atoi(data);
+ Sql_GetData(sql_handle, 6, &data, NULL); ele->str = atoi(data);
+ Sql_GetData(sql_handle, 7, &data, NULL); ele->agi = atoi(data);
+ Sql_GetData(sql_handle, 8, &data, NULL); ele->vit = atoi(data);
+ Sql_GetData(sql_handle, 9, &data, NULL); ele->int_ = atoi(data);
+ Sql_GetData(sql_handle, 10, &data, NULL); ele->dex = atoi(data);
+ Sql_GetData(sql_handle, 11, &data, NULL); ele->luk = atoi(data);
+ Sql_GetData(sql_handle, 12, &data, NULL); ele->life_time = atoi(data);
+ Sql_FreeResult(sql_handle);
+ if( save_log )
+ ShowInfo("Elemental loaded (%d - %d).\n", ele->elemental_id, ele->char_id);
+
+ return true;
+}
+
+bool mapif_elemental_delete(int ele_id) {
+ if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `elemental` WHERE `ele_id` = '%d'", ele_id) ) {
+ Sql_ShowDebug(sql_handle);
+ return false;
+ }
+
+ return true;
+}
+
+#ifndef TXT_SQL_CONVERT
+
+static void mapif_elemental_send(int fd, struct s_elemental *ele, unsigned char flag) {
+ int size = sizeof(struct s_elemental) + 5;
+
+ WFIFOHEAD(fd,size);
+ WFIFOW(fd,0) = 0x387c;
+ WFIFOW(fd,2) = size;
+ WFIFOB(fd,4) = flag;
+ memcpy(WFIFOP(fd,5),ele,sizeof(struct s_elemental));
+ WFIFOSET(fd,size);
+}
+
+static void mapif_parse_elemental_create(int fd, struct s_elemental* ele) {
+ bool result = mapif_elemental_save(ele);
+ mapif_elemental_send(fd, ele, result);
+}
+
+static void mapif_parse_elemental_load(int fd, int ele_id, int char_id) {
+ struct s_elemental ele;
+ bool result = mapif_elemental_load(ele_id, char_id, &ele);
+ mapif_elemental_send(fd, &ele, result);
+}
+
+static void mapif_elemental_deleted(int fd, unsigned char flag) {
+ WFIFOHEAD(fd,3);
+ WFIFOW(fd,0) = 0x387d;
+ WFIFOB(fd,2) = flag;
+ WFIFOSET(fd,3);
+}
+
+static void mapif_parse_elemental_delete(int fd, int ele_id) {
+ bool result = mapif_elemental_delete(ele_id);
+ mapif_elemental_deleted(fd, result);
+}
+
+static void mapif_elemental_saved(int fd, unsigned char flag) {
+ WFIFOHEAD(fd,3);
+ WFIFOW(fd,0) = 0x387e;
+ WFIFOB(fd,2) = flag;
+ WFIFOSET(fd,3);
+}
+
+static void mapif_parse_elemental_save(int fd, struct s_elemental* ele) {
+ bool result = mapif_elemental_save(ele);
+ mapif_elemental_saved(fd, result);
+}
+
+int inter_elemental_sql_init(void) {
+ return 0;
+}
+void inter_elemental_sql_final(void) {
+ return;
+}
+
+/*==========================================
+ * Inter Packets
+ *------------------------------------------*/
+int inter_elemental_parse_frommap(int fd) {
+ unsigned short cmd = RFIFOW(fd,0);
+
+ switch( cmd ) {
+ case 0x307c: mapif_parse_elemental_create(fd, (struct s_elemental*)RFIFOP(fd,4)); break;
+ case 0x307d: mapif_parse_elemental_load(fd, (int)RFIFOL(fd,2), (int)RFIFOL(fd,6)); break;
+ case 0x307e: mapif_parse_elemental_delete(fd, (int)RFIFOL(fd,2)); break;
+ case 0x307f: mapif_parse_elemental_save(fd, (struct s_elemental*)RFIFOP(fd,4)); break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+#endif //TXT_SQL_CONVERT
diff --git a/src/char/int_elemental.h b/src/char/int_elemental.h
new file mode 100644
index 000000000..89001dd95
--- /dev/null
+++ b/src/char/int_elemental.h
@@ -0,0 +1,15 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INT_ELEMENTAL_SQL_H_
+#define _INT_ELEMENTAL_SQL_H_
+
+struct s_elemental;
+
+int inter_elemental_sql_init(void);
+void inter_elemental_sql_final(void);
+int inter_elemental_parse_frommap(int fd);
+
+bool mapif_elemental_delete(int ele_id);
+
+#endif /* _INT_ELEMENTAL_SQL_H_ */
diff --git a/src/char/inter.c b/src/char/inter.c
index 752fe5163..6d4073c57 100644
--- a/src/char/inter.c
+++ b/src/char/inter.c
@@ -19,6 +19,7 @@
#include "int_mail.h"
#include "int_auction.h"
#include "int_quest.h"
+#include "int_elemental.h"
#include <stdio.h>
#include <string.h>
@@ -50,7 +51,7 @@ int inter_recv_packet_length[] = {
-1, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040-
-1,-1,10,10, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus]
6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish]
- -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3070- Mercenary packets [Zephyrus]
+ -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil]
48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080-
-1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator]
};
@@ -295,6 +296,7 @@ int inter_init_sql(const char *file)
inter_pet_sql_init();
inter_homunculus_sql_init();
inter_mercenary_sql_init();
+ inter_elemental_sql_init();
inter_accreg_sql_init();
inter_mail_sql_init();
inter_auction_sql_init();
@@ -313,6 +315,7 @@ void inter_final(void)
inter_pet_sql_final();
inter_homunculus_sql_final();
inter_mercenary_sql_final();
+ inter_elemental_sql_init();
inter_mail_sql_final();
inter_auction_sql_final();
@@ -723,6 +726,7 @@ int inter_parse_frommap(int fd)
|| inter_pet_parse_frommap(fd)
|| inter_homunculus_parse_frommap(fd)
|| inter_mercenary_parse_frommap(fd)
+ || inter_elemental_parse_frommap(fd)
|| inter_mail_parse_frommap(fd)
|| inter_auction_parse_frommap(fd)
|| inter_quest_parse_frommap(fd)
diff --git a/src/common/mmo.h b/src/common/mmo.h
index 6643eb1dd..55dcd9ed1 100644
--- a/src/common/mmo.h
+++ b/src/common/mmo.h
@@ -154,6 +154,14 @@
#define MAX_MERCSKILL 40
#define MAX_MERCENARY_CLASS 44
+//Elemental System
+#define MAX_ELEMENTALSKILL 42
+#define EL_SKILLBASE 8401
+#define MAX_ELESKILLTREE 3
+#define MAX_ELEMENTAL_CLASS 12
+#define EL_CLASS_BASE 2114
+#define EL_CLASS_MAX (EL_CLASS_BASE+MAX_ELEMENTAL_CLASS-1)
+
enum item_types {
IT_HEALING = 0,
IT_UNKNOWN, //1
@@ -292,6 +300,15 @@ struct s_mercenary {
unsigned int life_time;
};
+struct s_elemental {
+ int elemental_id;
+ int char_id;
+ short class_;
+ int mode;
+ int hp, sp, max_hp, max_sp, str, agi, vit, int_, dex, luk;
+ int life_time;
+};
+
struct s_friend {
int account_id;
int char_id;
@@ -324,7 +341,7 @@ struct mmo_charstatus {
short manner;
unsigned char karma;
short hair,hair_color,clothes_color;
- int party_id,guild_id,pet_id,hom_id,mer_id;
+ int party_id,guild_id,pet_id,hom_id,mer_id,ele_id;
int fame;
// Mercenary Guilds Rank
diff --git a/src/map/Makefile.in b/src/map/Makefile.in
index 0b5ba023d..beeab32dd 100644
--- a/src/map/Makefile.in
+++ b/src/map/Makefile.in
@@ -31,7 +31,7 @@ MAP_OBJ = map.o chrif.o clif.o pc.o status.o npc.o \
storage.o skill.o atcommand.o battle.o battleground.o \
intif.o trade.o party.o vending.o guild.o pet.o \
log.o mail.o date.o unit.o homunculus.o mercenary.o quest.o instance.o \
- buyingstore.o searchstore.o duel.o pc_groups.o
+ buyingstore.o searchstore.o duel.o pc_groups.o elemental.o
MAP_SQL_OBJ = $(MAP_OBJ:%=obj_sql/%) \
obj_sql/mapreg_sql.o
MAP_H = map.h chrif.h clif.h pc.h status.h npc.h \
@@ -41,7 +41,7 @@ MAP_H = map.h chrif.h clif.h pc.h status.h npc.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 pc_groups.h \
config/core.h config/renewal.h config/secure.h config/const.h \
- config/classes/general.h config/classes/swordsman.h
+ config/classes/general.h config/classes/swordsman.h elemental.h
HAVE_MYSQL=@HAVE_MYSQL@
ifeq ($(HAVE_MYSQL),yes)
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index 8a918633a..c19164d1a 100644
--- a/src/map/atcommand.c
+++ b/src/map/atcommand.c
@@ -34,6 +34,7 @@
#include "homunculus.h"
#include "mail.h"
#include "mercenary.h"
+#include "elemental.h"
#include "party.h"
#include "guild.h"
#include "script.h"
@@ -3764,6 +3765,7 @@ ACMD_FUNC(reloadmobdb)
read_petdb();
merc_reload();
read_mercenarydb();
+ reload_elementaldb();
clif_displaymessage(fd, msg_txt(98)); // Monster database has been reloaded.
return 0;
@@ -3777,6 +3779,7 @@ ACMD_FUNC(reloadskilldb)
nullpo_retr(-1, sd);
skill_reload();
merc_skill_reload();
+ reload_elemental_skilldb();
read_mercenary_skilldb();
clif_displaymessage(fd, msg_txt(99)); // Skill database has been reloaded.
diff --git a/src/map/battle.c b/src/map/battle.c
index 48f8e1cab..cea562661 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -18,6 +18,7 @@
#include "skill.h"
#include "homunculus.h"
#include "mercenary.h"
+#include "elemental.h"
#include "mob.h"
#include "itemdb.h"
#include "clif.h"
@@ -103,6 +104,7 @@ int battle_gettarget(struct block_list* bl)
case BL_PET: return ((struct pet_data*)bl)->target_id;
case BL_HOM: return ((struct homun_data*)bl)->ud.target;
case BL_MER: return ((struct mercenary_data*)bl)->ud.target;
+ case BL_ELEM: return ((struct elemental_data*)bl)->ud.target;
}
return 0;
}
@@ -299,8 +301,7 @@ int battle_attr_fix(struct block_list *src, struct block_list *target, int damag
}
ratio = attr_fix_table[def_lv-1][atk_elem][def_type];
- if (sc && sc->count)
- {
+ if (sc && sc->count) {
if(sc->data[SC_VOLCANO] && atk_elem == ELE_FIRE)
ratio += enchant_eff[sc->data[SC_VOLCANO]->val1-1];
if(sc->data[SC_VIOLENTGALE] && atk_elem == ELE_WIND)
@@ -308,8 +309,27 @@ int battle_attr_fix(struct block_list *src, struct block_list *target, int damag
if(sc->data[SC_DELUGE] && atk_elem == ELE_WATER)
ratio += enchant_eff[sc->data[SC_DELUGE]->val1-1];
}
- if( atk_elem == ELE_FIRE && tsc && tsc->count && tsc->data[SC_SPIDERWEB] )
- {
+ if( target && target->type == BL_SKILL ) {
+ if( atk_elem == ELE_FIRE && battle_getcurrentskill(target) == GN_WALLOFTHORN ) {
+ struct skill_unit *su = (struct skill_unit*)target;
+ struct skill_unit_group *sg;
+ struct block_list *src;
+ int x,y;
+
+ if( !su || !su->alive || (sg = su->group) == NULL || !sg || sg->val3 == -1 ||
+ (src = map_id2bl(su->val2)) == NULL || status_isdead(src) )
+ return 0;
+
+ if( sg->unit_id != UNT_FIREWALL ) {
+ x = sg->val3 >> 16;
+ y = sg->val3 & 0xffff;
+ skill_unitsetting(src,su->group->skill_id,su->group->skill_lv,x,y,1);
+ sg->val3 = -1;
+ sg->limit = DIFF_TICK(gettick(),sg->tick)+300;
+ }
+ }
+ }
+ if( atk_elem == ELE_FIRE && tsc && tsc->count && tsc->data[SC_SPIDERWEB] ){
tsc->data[SC_SPIDERWEB]->val1 = 0; // free to move now
if( tsc->data[SC_SPIDERWEB]->val2-- > 0 )
damage <<= 1; // double damage
@@ -932,6 +952,8 @@ int battle_addmastery(struct map_session_data *sd,struct block_list *target,int
case W_DAGGER:
if((skill = pc_checkskill(sd,SM_SWORD)) > 0)
damage += (skill * 4);
+ if((skill = pc_checkskill(sd,GN_TRAINING_SWORD)) > 0)
+ damage += skill * 10;
break;
case W_2HSWORD:
#ifdef RENEWAL
@@ -1486,15 +1508,27 @@ 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 MC_CARTREVOLUTION:
+ case GN_CART_TORNADO:
+ case GN_CARTCANNON:
+ if( sd && pc_checkskill(sd, GN_REMODELING_CART) )
+ hitrate += pc_checkskill(sd, GN_REMODELING_CART) * 4;
+ break;
case GC_VENOMPRESSURE:
hitrate += 10 + 4 * skill_lv;
break;
}
- // Weaponry Research hidden bonus
- if (sd && (skill = pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0)
- hitrate += hitrate * ( 2 * skill ) / 100;
-
+ if( sd ) {
+ // Weaponry Research hidden bonus
+ if ((skill = pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0)
+ hitrate += hitrate * ( 2 * skill ) / 100;
+
+ if( (sd->status.weapon == W_1HSWORD || sd->status.weapon == W_DAGGER) &&
+ (skill = pc_checkskill(sd, GN_TRAINING_SWORD))>0 )
+ hitrate += 3 * skill;
+ }
+
hitrate = cap_value(hitrate, battle_config.min_hitrate, battle_config.max_hitrate);
if(rnd()%100 >= hitrate)
@@ -2238,6 +2272,76 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
case WM_SOUND_OF_DESTRUCTION:
skillratio += 400;
break;
+ case GN_CART_TORNADO:
+ if( sd )
+ skillratio += 50 * skill_lv + pc_checkskill(sd, GN_REMODELING_CART) * 100 - 100;
+ if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus.
+ if( sc && sc->data[SC_GN_CARTBOOST] )
+ skillratio += 10 * sc->data[SC_GN_CARTBOOST]->val1;
+ break;
+ case GN_CARTCANNON:
+ if( sd ) skillratio += 250 + 50 * skill_lv + pc_checkskill(sd, GN_REMODELING_CART) * (sstatus->int_ / 2);
+ if( sc && sc->data[SC_GN_CARTBOOST] )
+ skillratio += 10 * sc->data[SC_GN_CARTBOOST]->val1;
+ break;
+ case GN_SPORE_EXPLOSION:
+ skillratio += 200 + 100 * skill_lv;
+ break;
+ case GN_CRAZYWEED_ATK:
+ skillratio += 400 + 100 * skill_lv;
+ break;
+ case GN_SLINGITEM_RANGEMELEEATK:
+ if( sd ) {
+ switch( sd->itemid ) {
+ case 13260: // Apple Bomob
+ case 13261: // Coconut Bomb
+ case 13262: // Melon Bomb
+ case 13263: // Pinapple Bomb
+ skillratio += 400; // Unconfirded
+ break;
+ case 13264: // Banana Bomb 2000%
+ skillratio += 1900;
+ break;
+ case 13265: skillratio -= 75; break; // Black Lump 25%
+ case 13266: skillratio -= 25; break; // Hard Black Lump 75%
+ case 13267: skillratio += 100; break; // Extremely Hard Black Lump 200%
+ }
+ } else
+ skillratio += 300; // Bombs
+ break;
+ case SO_VARETYR_SPEAR: //Assumed Formula.
+ skillratio += -100 + 200 * ( sd ? pc_checkskill(sd, SA_LIGHTNINGLOADER) : 1 );
+ if( sc && sc->data[SC_BLAST_OPTION] )
+ skillratio += skillratio * sc->data[SC_BLAST_OPTION]->val2 / 100;
+ break;
+ // Physical Elemantal Spirits Attack Skills
+ case EL_CIRCLE_OF_FIRE:
+ case EL_FIRE_BOMB_ATK:
+ case EL_STONE_RAIN:
+ skillratio += 200;
+ break;
+ case EL_FIRE_WAVE_ATK:
+ skillratio += 500;
+ break;
+ case EL_TIDAL_WEAPON:
+ skillratio += 1400;
+ break;
+ case EL_WIND_SLASH:
+ skillratio += 100;
+ break;
+ case EL_HURRICANE:
+ skillratio += 600;
+ break;
+ case EL_TYPOON_MIS:
+ case EL_WATER_SCREW_ATK:
+ skillratio += 900;
+ break;
+ case EL_STONE_HAMMER:
+ skillratio += 400;
+ break;
+ case EL_ROCK_CRUSHER:
+ skillratio += 700;
+ break;
}
ATK_RATE(skillratio);
@@ -2920,7 +3024,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int mflag)
{
int i, nk;
- short s_ele;
+ short s_ele = 0;
unsigned int skillratio = 100; //Skill dmg modifiers.
struct map_session_data *sd, *tsd;
@@ -2954,16 +3058,38 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
sd = BL_CAST(BL_PC, src);
tsd = BL_CAST(BL_PC, target);
- //Initialize variables that will be used afterwards
- s_ele = skill_get_ele(skill_num, skill_lv);
-
- if (s_ele == -1) // pl=-1 : the skill takes the weapon's element
- s_ele = sstatus->rhw.ele;
- else if (s_ele == -2) //Use status element
- s_ele = status_get_attack_sc_element(src,status_get_sc(src));
- else if( s_ele == -3 ) //Use random element
- s_ele = rnd()%ELE_MAX;
-
+ if( skill_num == SO_PSYCHIC_WAVE ) {
+ struct status_change *sc = status_get_sc(src);
+ if( sc && sc->count && ( sc->data[SC_HEATER_OPTION] || sc->data[SC_COOLER_OPTION] ||
+ sc->data[SC_BLAST_OPTION] || sc->data[SC_CURSED_SOIL_OPTION] ) ) {
+ if( sc->data[SC_HEATER_OPTION] ) s_ele = sc->data[SC_HEATER_OPTION]->val4;
+ else if( sc->data[SC_COOLER_OPTION] ) s_ele = sc->data[SC_COOLER_OPTION]->val4;
+ else if( sc->data[SC_BLAST_OPTION] ) s_ele = sc->data[SC_BLAST_OPTION]->val3;
+ else if( sc->data[SC_CURSED_SOIL_OPTION] ) s_ele = sc->data[SC_CURSED_SOIL_OPTION]->val4;
+ } else {
+ //#HALP# I didn't get a clue on how to do this without unnecessary adding a overhead of status_change on every call while this is a per-skill case.
+ //, - so i duplicated this code. make yourself comfortable to fix if you have any better ideas.
+ //Initialize variables that will be used afterwards
+ s_ele = skill_get_ele(skill_num, skill_lv);
+
+ if (s_ele == -1) // pl=-1 : the skill takes the weapon's element
+ s_ele = sstatus->rhw.ele;
+ else if (s_ele == -2) //Use status element
+ s_ele = status_get_attack_sc_element(src,status_get_sc(src));
+ else if( s_ele == -3 ) //Use random element
+ s_ele = rnd()%ELE_MAX;
+ }
+ } else {
+ //Initialize variables that will be used afterwards
+ s_ele = skill_get_ele(skill_num, skill_lv);
+
+ if (s_ele == -1) // pl=-1 : the skill takes the weapon's element
+ s_ele = sstatus->rhw.ele;
+ else if (s_ele == -2) //Use status element
+ s_ele = status_get_attack_sc_element(src,status_get_sc(src));
+ else if( s_ele == -3 ) //Use random element
+ s_ele = rnd()%ELE_MAX;
+ }
//Set miscellaneous data that needs be filled
if(sd) {
sd->state.arrow_atk = 0;
@@ -3334,6 +3460,24 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
else
skillratio += 110 + 20 * skill_lv;
break;
+ // Magical Elemental Spirits Attack Skills
+ case EL_FIRE_MANTLE:
+ case EL_WATER_SCREW:
+ skillratio += 900;
+ break;
+ case EL_FIRE_ARROW:
+ case EL_ROCK_CRUSHER_ATK:
+ skillratio += 200;
+ break;
+ case EL_FIRE_BOMB:
+ case EL_ICE_NEEDLE:
+ case EL_HURRICANE_ATK:
+ skillratio += 400;
+ break;
+ case EL_FIRE_WAVE:
+ case EL_TYPOON_MIS_ATK:
+ skillratio += 1100;
+ break;
}
@@ -3493,6 +3637,13 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
ad.damage=battle_calc_gvg_damage(src,target,ad.damage,ad.div_,skill_num,skill_lv,ad.flag);
else if( map[target->m].flag.battleground )
ad.damage=battle_calc_bg_damage(src,target,ad.damage,ad.div_,skill_num,skill_lv,ad.flag);
+
+
+ if( skill_num == SO_VARETYR_SPEAR ) { // Physical damage.
+ struct Damage wd = battle_calc_weapon_attack(src,target,skill_num,skill_lv,mflag);
+ ad.damage += wd.damage;
+ }
+
return ad;
}
@@ -3675,7 +3826,15 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list *
if (sd) md.damage = md.damage + status_get_hp(src);
status_set_sp(src, 0, 0);
break;
-
+ case GN_THORNS_TRAP:
+ md.damage = 100 + 200 * skill_lv + sstatus->int_;
+ break;
+ case GN_BLOOD_SUCKER:
+ md.damage = 200 + 100 * skill_lv + sstatus->int_;
+ break;
+ case GN_HELLS_PLANT_ATK:
+ md.damage = sstatus->int_ * 4 * skill_lv * (10 / (10 - pc_checkskill(sd,AM_CANNIBALIZE)));//Need accurate official formula. [Rytech]
+ break;
}
if (nk&NK_SPLASHSPLIT){ // Divide ATK among targets
@@ -4168,24 +4327,38 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
map_freeblock_lock();
battle_delay_damage(tick, wd.amotion, src, target, wd.flag, 0, 0, damage, wd.dmg_lv, wd.dmotion);
-
- if( tsc && tsc->data[SC_DEVOTION] )
- {
- struct status_change_entry *sce = tsc->data[SC_DEVOTION];
- struct block_list *d_bl = map_id2bl(sce->val1);
-
- if( d_bl && (
- (d_bl->type == BL_MER && ((TBL_MER*)d_bl)->master && ((TBL_MER*)d_bl)->master->bl.id == target->id) ||
- (d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce->val2] == target->id)
- ) && check_distance_bl(target, d_bl, sce->val3) )
- {
- clif_damage(d_bl, d_bl, gettick(), 0, 0, damage, 0, 0, 0);
- status_fix_damage(NULL, d_bl, damage, 0);
- }
- else
- status_change_end(target, SC_DEVOTION, INVALID_TIMER);
+ if( tsc ) {
+ if( tsc->data[SC_DEVOTION] ) {
+ struct status_change_entry *sce = tsc->data[SC_DEVOTION];
+ struct block_list *d_bl = map_id2bl(sce->val1);
+
+ if( d_bl && (
+ (d_bl->type == BL_MER && ((TBL_MER*)d_bl)->master && ((TBL_MER*)d_bl)->master->bl.id == target->id) ||
+ (d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce->val2] == target->id)
+ ) && check_distance_bl(target, d_bl, sce->val3) )
+ {
+ clif_damage(d_bl, d_bl, gettick(), 0, 0, damage, 0, 0, 0);
+ status_fix_damage(NULL, d_bl, damage, 0);
+ }
+ else
+ status_change_end(target, SC_DEVOTION, INVALID_TIMER);
+ } else if( tsc->data[SC_CIRCLE_OF_FIRE_OPTION] && (wd.flag&BF_SHORT) && target->type == BL_PC ) {
+ struct elemental_data *ed = ((TBL_PC*)target)->ed;
+ if( ed ) {
+ clif_skill_damage(&ed->bl, target, tick, status_get_amotion(src), 0, -30000, 1, EL_CIRCLE_OF_FIRE, tsc->data[SC_CIRCLE_OF_FIRE_OPTION]->val1, 6);
+ skill_attack(BF_MAGIC,&ed->bl,&ed->bl,src,EL_CIRCLE_OF_FIRE,tsc->data[SC_CIRCLE_OF_FIRE_OPTION]->val1,tick,wd.flag);
+ }
+ } else if( tsc->data[SC_WATER_SCREEN_OPTION] && tsc->data[SC_WATER_SCREEN_OPTION]->val1 ) {
+ struct block_list *e_bl = map_id2bl(tsc->data[SC_WATER_SCREEN_OPTION]->val1);
+ if( e_bl && !status_isdead(e_bl) ) {
+ clif_damage(e_bl,e_bl,tick,wd.amotion,wd.dmotion,damage,wd.div_,wd.type,wd.damage2);
+ status_damage(target,e_bl,damage,0,0,0);
+ // Just show damage in target.
+ clif_damage(src, target, tick, wd.amotion, wd.dmotion, damage, wd.div_, wd.type, wd.damage2 );
+ return ATK_NONE;
+ }
+ }
}
-
if (sc && sc->data[SC_AUTOSPELL] && rnd()%100 < sc->data[SC_AUTOSPELL]->val4) {
int sp = 0;
int skillid = sc->data[SC_AUTOSPELL]->val2;
@@ -4314,6 +4487,10 @@ struct block_list* battle_get_master(struct block_list *src)
if (((TBL_MER*)src)->master)
src = (struct block_list*)((TBL_MER*)src)->master;
break;
+ case BL_ELEM:
+ if (((TBL_ELEM*)src)->master)
+ src = (struct block_list*)((TBL_ELEM*)src)->master;
+ break;
case BL_SKILL:
if (((TBL_SKILL*)src)->group && ((TBL_SKILL*)src)->group->src_id)
src = map_id2bl(((TBL_SKILL*)src)->group->src_id);
@@ -4409,6 +4586,7 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
//Valid targets with no special checks here.
case BL_MER:
case BL_HOM:
+ case BL_ELEM:
break;
//All else not specified is an invalid target.
default:
diff --git a/src/map/chrif.c b/src/map/chrif.c
index c4ff4b662..095129e80 100644
--- a/src/map/chrif.c
+++ b/src/map/chrif.c
@@ -22,6 +22,7 @@
#include "homunculus.h"
#include "instance.h"
#include "mercenary.h"
+#include "elemental.h"
#include "chrif.h"
#include "quest.h"
#include "storage.h"
@@ -307,6 +308,8 @@ int chrif_save(struct map_session_data *sd, int flag)
merc_save(sd->hd);
if( sd->md && mercenary_get_lifetime(sd->md) > 0 )
mercenary_save(sd->md);
+ if( sd->ed && elemental_get_lifetime(sd->ed) > 0 )
+ elemental_save(sd->ed);
if( sd->save_quest )
intif_quest_save(sd);
diff --git a/src/map/clif.c b/src/map/clif.c
index 3533c7eff..bfe78a58d 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -36,6 +36,7 @@
#include "homunculus.h"
#include "instance.h"
#include "mercenary.h"
+#include "elemental.h"
#include "log.h"
#include "clif.h"
#include "mail.h"
@@ -273,7 +274,7 @@ static inline unsigned char clif_bl_type(struct block_list *bl) {
case BL_PET: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x7; //NPC_PET_TYPE
case BL_HOM: return 0x8; //NPC_HOM_TYPE
case BL_MER: return 0x9; //NPC_MERSOL_TYPE
-// case BL_ELEM: return 0xA; //NPC_ELEMENTAL_TYPE
+ case BL_ELEM: return 0xa; //NPC_ELEMENTAL_TYPE
default: return 0x1; //NPC_TYPE
}
}
@@ -5193,44 +5194,60 @@ void clif_skill_produce_mix_list(struct map_session_data *sd, int skillid , int
/// 4 = GN_MIX_COOKING
/// 5 = GN_MAKEBOMB
/// 6 = GN_S_PHARMACY
-void clif_cooking_list(struct map_session_data *sd, int trigger)
+void clif_cooking_list(struct map_session_data *sd, int trigger, int skill_id, int qty, int list_type)
{
int fd;
int i, c;
int view;
-
+
nullpo_retv(sd);
fd = sd->fd;
-
- WFIFOHEAD(fd, 6 + 2*MAX_SKILL_PRODUCE_DB);
+
+ WFIFOHEAD(fd, 6 + 2 * MAX_SKILL_PRODUCE_DB);
WFIFOW(fd,0) = 0x25a;
- WFIFOW(fd,4) = 1; // list type
-
+ WFIFOW(fd,4) = list_type; // list type
+
c = 0;
- for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ )
- {
- if( !skill_can_produce_mix(sd,skill_produce_db[i].nameid,trigger, 1) )
+ for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) {
+ if( !skill_can_produce_mix(sd,skill_produce_db[i].nameid,trigger, qty) )
continue;
-
+
if( (view = itemdb_viewid(skill_produce_db[i].nameid)) > 0 )
- WFIFOW(fd, 6+2*c)= view;
+ WFIFOW(fd, 6 + 2 * c) = view;
else
- WFIFOW(fd, 6+2*c)= skill_produce_db[i].nameid;
-
+ WFIFOW(fd, 6 + 2 * c) = skill_produce_db[i].nameid;
+
c++;
}
-
- WFIFOW(fd,2) = 6 + 2*c;
- WFIFOSET(fd,WFIFOW(fd,2));
-
- //TODO: replace with proper solution
- if( c > 0 )
- {
- sd->menuskill_id = AM_PHARMACY;
+
+ if( skill_id == AM_PHARMACY ) { // Only send it while Cooking else check for c.
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+
+ if( c > 0 ) {
+ sd->menuskill_id = skill_id;
sd->menuskill_val = trigger;
+ if( skill_id != AM_PHARMACY ) {
+ sd->menuskill_val2 = qty; // amount.
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ } else {
+ clif_menuskill_clear(sd);
+ if( skill_id != AM_PHARMACY ) { // AM_PHARMACY is used to Cooking.
+ // It fails.
+#if PACKETVER >= 20090922
+ clif_msg_skill(sd,skill_id,0x625);
+#else
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+#endif
+ }
}
}
+
/// Notifies clients of a status change.
/// 0196 <index>.W <id>.L <state>.B (ZC_MSG_STATE_CHANGE) [used for ending status changes and starting them on non-pc units (when needed)]
/// 043f <index>.W <id>.L <state>.B <remain msec>.L { <val>.L }*3 (ZC_MSG_STATE_CHANGE2) [used exclusively for starting statuses on pcs]
@@ -8107,11 +8124,12 @@ void clif_refresh(struct map_session_data *sd)
clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
if(merc_is_hom_active(sd->hd))
clif_send_homdata(sd,SP_ACK,0);
- if( sd->md )
- {
+ if( sd->md ) {
clif_mercenary_info(sd);
clif_mercenary_skillblock(sd);
}
+ if( sd->ed )
+ clif_elemental_info(sd);
map_foreachinrange(clif_getareachar,&sd->bl,AREA_SIZE,BL_ALL,sd);
clif_weather_check(sd);
if( sd->chatID )
@@ -8255,6 +8273,9 @@ void clif_charnameack (int fd, struct block_list *bl)
// memcpy(WBUFP(buf,6), (struct chat*)->title, NAME_LENGTH);
// break;
return;
+ case BL_ELEM:
+ memcpy(WBUFP(buf,6), ((TBL_ELEM*)bl)->db->name, NAME_LENGTH);
+ break;
default:
ShowError("clif_charnameack: bad type %d(%d)\n", bl->type, bl->id);
return;
@@ -9082,14 +9103,22 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately
}
- if( sd->md )
- {
+ if( sd->md ) {
map_addblock(&sd->md->bl);
clif_spawn(&sd->md->bl);
clif_mercenary_info(sd);
clif_mercenary_skillblock(sd);
}
+ if( sd->ed ) {
+ map_addblock(&sd->ed->bl);
+ clif_spawn(&sd->ed->bl);
+ clif_elemental_info(sd);
+ clif_elemental_updatestatus(sd,SP_HP);
+ clif_hpmeter_single(sd->fd,sd->ed->bl.id,sd->ed->battle_status.hp,sd->ed->battle_status.matk_max);
+ clif_elemental_updatestatus(sd,SP_SP);
+ }
+
if(sd->state.connect_new) {
int lv;
sd->state.connect_new = 0;
@@ -10587,15 +10616,13 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
if( sd->sc.data[SC_BASILICA] && (skillnum != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
return; // On basilica only caster can use Basilica again to stop it.
- if( sd->menuskill_id )
- {
- if( sd->menuskill_id == SA_TAMINGMONSTER )
- sd->menuskill_id = sd->menuskill_val = 0; //Cancel pet capture.
- else if( sd->menuskill_id != SA_AUTOSPELL )
+ if( sd->menuskill_id ) {
+ if( sd->menuskill_id == SA_TAMINGMONSTER ) {
+ clif_menuskill_clear(sd); //Cancel pet capture.
+ } else if( sd->menuskill_id != SA_AUTOSPELL )
return; //Can't use skills while a menu is open.
}
- if( sd->skillitem == skillnum )
- {
+ if( sd->skillitem == skillnum ) {
if( skilllv != sd->skillitemlv )
skilllv = sd->skillitemlv;
if( !(tmp&INF_SELF_SKILL) )
@@ -10606,15 +10633,12 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
sd->skillitem = sd->skillitemlv = 0;
- if( skillnum >= GD_SKILLBASE )
- {
+ if( skillnum >= GD_SKILLBASE ) {
if( sd->state.gmaster_flag )
skilllv = guild_checkskill(sd->state.gmaster_flag, skillnum);
else
skilllv = 0;
- }
- else
- {
+ } else {
tmp = pc_checkskill(sd, skillnum);
if( skilllv > tmp )
skilllv = tmp;
@@ -10661,10 +10685,8 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, sho
if( sd->ud.skilltimer != INVALID_TIMER )
return;
- if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 )
- {
- if( sd->skillitem != skillnum )
- {
+ if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 ) {
+ if( sd->skillitem != skillnum ) {
clif_skill_fail(sd, skillnum, USESKILL_FAIL_SKILLINTERVAL, 0);
return;
}
@@ -10676,28 +10698,23 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, sho
if( sd->sc.data[SC_BASILICA] && (skillnum != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
return; // On basilica only caster can use Basilica again to stop it.
- if( sd->menuskill_id )
- {
- if( sd->menuskill_id == SA_TAMINGMONSTER )
- sd->menuskill_id = sd->menuskill_val = 0; //Cancel pet capture.
- else if( sd->menuskill_id != SA_AUTOSPELL )
+ if( sd->menuskill_id ) {
+ if( sd->menuskill_id == SA_TAMINGMONSTER ) {
+ clif_menuskill_clear(sd); //Cancel pet capture.
+ } else if( sd->menuskill_id != SA_AUTOSPELL )
return; //Can't use skills while a menu is open.
}
pc_delinvincibletimer(sd);
- if( sd->skillitem == skillnum )
- {
+ if( sd->skillitem == skillnum ) {
if( skilllv != sd->skillitemlv )
skilllv = sd->skillitemlv;
unit_skilluse_pos(&sd->bl, x, y, skillnum, skilllv);
- }
- else
- {
+ } else {
int lv;
sd->skillitem = sd->skillitemlv = 0;
- if( (lv = pc_checkskill(sd, skillnum)) > 0 )
- {
+ if( (lv = pc_checkskill(sd, skillnum)) > 0 ) {
if( skilllv > lv )
skilllv = lv;
unit_skilluse_pos(&sd->bl, x, y, skillnum,skilllv);
@@ -10759,9 +10776,8 @@ void clif_parse_UseSkillMap(int fd, struct map_session_data* sd)
if(skill_num != sd->menuskill_id)
return;
- if( pc_cant_act(sd) )
- {
- sd->menuskill_id = sd->menuskill_val = 0;
+ if( pc_cant_act(sd) ) {
+ clif_menuskill_clear(sd);
return;
}
@@ -10795,12 +10811,12 @@ void clif_parse_ProduceMix(int fd,struct map_session_data *sd)
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,USESKILL_FAIL_LEVEL,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
if( skill_can_produce_mix(sd,RFIFOW(fd,2),sd->menuskill_val, 1) )
skill_produce_mix(sd,0,RFIFOW(fd,2),RFIFOW(fd,4),RFIFOW(fd,6),RFIFOW(fd,8), 1);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -10813,24 +10829,22 @@ void clif_parse_ProduceMix(int fd,struct map_session_data *sd)
/// 4 = GN_MIX_COOKING
/// 5 = GN_MAKEBOMB
/// 6 = GN_S_PHARMACY
-void clif_parse_Cooking(int fd,struct map_session_data *sd)
-{
- //int type = RFIFOW(fd,2);
+void clif_parse_Cooking(int fd,struct map_session_data *sd) {
+ int type = RFIFOW(fd,2);
int nameid = RFIFOW(fd,4);
-
- if( sd->menuskill_id != AM_PHARMACY ) {
+ int amount = sd->menuskill_val2?sd->menuskill_val2:1;
+ if( type == 6 && sd->menuskill_id != GN_MIX_COOKING && sd->menuskill_id != GN_S_PHARMACY )
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,USESKILL_FAIL_LEVEL,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
- if( skill_can_produce_mix(sd,nameid,sd->menuskill_val, 1) )
- skill_produce_mix(sd,0,nameid,0,0,0,1);
- sd->menuskill_val = sd->menuskill_id = 0;
+ if( skill_can_produce_mix(sd,nameid,sd->menuskill_val, amount) )
+ skill_produce_mix(sd,sd->menuskill_id,nameid,0,0,0,amount);
+ clif_menuskill_clear(sd);
}
@@ -10843,11 +10857,11 @@ void clif_parse_RepairItem(int fd, struct map_session_data *sd)
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,USESKILL_FAIL_LEVEL,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
skill_repairweapon(sd,RFIFOW(fd,2));
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -10862,12 +10876,12 @@ void clif_parse_WeaponRefine(int fd, struct map_session_data *sd)
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,USESKILL_FAIL_LEVEL,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
idx = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
skill_weaponrefine(sd, idx-2);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -10952,13 +10966,12 @@ void clif_parse_ItemIdentify(int fd,struct map_session_data *sd)
if (sd->menuskill_id != MC_IDENTIFY)
return;
- if( idx == -1 )
- {// cancel pressed
- sd->menuskill_val = sd->menuskill_id = 0;
+ if( idx == -1 ) {// cancel pressed
+ clif_menuskill_clear(sd);
return;
}
skill_identify(sd,idx-2);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -10969,7 +10982,7 @@ void clif_parse_SelectArrow(int fd,struct map_session_data *sd)
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,USESKILL_FAIL_LEVEL,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
switch( sd->menuskill_id ) {
@@ -10990,7 +11003,7 @@ void clif_parse_SelectArrow(int fd,struct map_session_data *sd)
break;
}
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -11001,7 +11014,7 @@ void clif_parse_AutoSpell(int fd,struct map_session_data *sd)
if (sd->menuskill_id != SA_AUTOSPELL)
return;
skill_autospell(sd,RFIFOL(fd,2));
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -12074,7 +12087,7 @@ void clif_parse_SelectEgg(int fd, struct map_session_data *sd)
return;
}
pet_select_egg(sd,RFIFOW(fd,2)-2);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -13038,7 +13051,7 @@ void clif_parse_FeelSaveOk(int fd,struct map_session_data *sd)
// clif_misceffect2(&sd->bl, 0x1b0);
// clif_misceffect2(&sd->bl, 0x21f);
clif_feel_info(sd, i, 0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -15051,6 +15064,94 @@ void clif_parse_LessEffect(int fd, struct map_session_data* sd)
sd->state.lesseffect = ( isLess != 0 );
}
+/// S 07e4 <length>.w <option>.l <val>.l {<index>.w <amount>.w).4b*
+void clif_parse_ItemListWindowSelected(int fd, struct map_session_data* sd) {
+ int n = (RFIFOW(fd,2)-12) / 4;
+ int type = RFIFOL(fd,4);
+ int flag = RFIFOL(fd,8); // Button clicked: 0 = Cancel, 1 = OK
+ unsigned short* item_list = (unsigned short*)RFIFOP(fd,12);
+
+ if( sd->state.trading || sd->npc_shopid )
+ return;
+
+ if( flag == 0 || n == 0) {
+ clif_menuskill_clear(sd);
+ return; // Canceled by player.
+ }
+
+ if( sd->menuskill_id != SO_EL_ANALYSIS && sd->menuskill_id != GN_CHANGEMATERIAL ) {
+ clif_menuskill_clear(sd);
+ return; // Prevent hacking.
+ }
+
+ switch( type ) {
+ case 0: // Change Material
+ skill_changematerial(sd,n,item_list);
+ break;
+ case 1: // Level 1: Pure to Rough
+ case 2: // Level 2: Rough to Pure
+ skill_elementalanalysis(sd,n,type,item_list);
+ break;
+ }
+ clif_menuskill_clear(sd);
+
+ return;
+}
+
+/*==========================================
+ * Elemental System
+ *==========================================*/
+void clif_elemental_updatestatus(struct map_session_data *sd, int type) {
+ struct elemental_data *ed;
+ struct status_data *status;
+ int fd;
+
+ if( sd == NULL || (ed = sd->ed) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &ed->battle_status;
+ WFIFOHEAD(fd,8);
+ WFIFOW(fd,0) = 0x81e;
+ WFIFOW(fd,2) = type;
+ switch( type ) {
+ case SP_HP:
+ WFIFOL(fd,4) = status->hp;
+ break;
+ case SP_MAXHP:
+ WFIFOL(fd,4) = status->max_hp;
+ break;
+ case SP_SP:
+ WFIFOL(fd,4) = status->sp;
+ break;
+ case SP_MAXSP:
+ WFIFOL(fd,4) = status->max_sp;
+ break;
+ }
+ WFIFOSET(fd,8);
+}
+
+void clif_elemental_info(struct map_session_data *sd) {
+ int fd;
+ struct elemental_data *ed;
+ struct status_data *status;
+
+ if( sd == NULL || (ed = sd->ed) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &ed->battle_status;
+
+ WFIFOHEAD(fd,22);
+ WFIFOW(fd, 0) = 0x81d;
+ WFIFOL(fd, 2) = ed->bl.id;
+ WFIFOL(fd, 6) = status->hp;
+ WFIFOL(fd,10) = status->max_hp;
+ WFIFOL(fd,14) = status->sp;
+ WFIFOL(fd,18) = status->max_sp;
+ WFIFOSET(fd,22);
+}
+
/// Buying Store System
///
@@ -15762,6 +15863,35 @@ int clif_autoshadowspell_list(struct map_session_data *sd) {
return 1;
}
+/*===========================================
+ * Skill list for Four Elemental Analysis
+ * and Change Material skills.
+ *------------------------------------------*/
+int clif_skill_itemlistwindow( struct map_session_data *sd, int skill_id, int skill_lv )
+{
+#if PACKETVER >= 20090922
+ int fd;
+
+ nullpo_ret(sd);
+
+ sd->menuskill_id = skill_id; // To prevent hacking.
+ sd->menuskill_val = skill_lv;
+
+ if( skill_id == GN_CHANGEMATERIAL )
+ skill_lv = 0; // Changematerial
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x7e3));
+ WFIFOW(fd,0) = 0x7e3;
+ WFIFOL(fd,2) = skill_lv;
+ WFIFOL(fd,4) = 0;
+ WFIFOSET(fd,packet_len(0x7e3));
+
+#endif
+
+ return 1;
+
+}
/**
* Sends a new status without a tick (currently used by the new mounts)
**/
@@ -15826,11 +15956,11 @@ void clif_parse_SkillSelectMenu(int fd, struct map_session_data *sd) {
if( pc_istrading(sd) ) {
clif_skill_fail(sd,sd->ud.skillid,0,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
skill_select_menu(sd,RFIFOL(fd,2),RFIFOW(fd,6));
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
/*==========================================
@@ -16202,13 +16332,13 @@ static int packetdb_readdb(void)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
//#0x07C0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#if PACKETVER < 20090617
6, 2, -1, 4, 4, 4, 4, 8, 8,254, 6, 8, 6, 54, 30, 54,
#else // 0x7d9 changed
6, 2, -1, 4, 4, 4, 4, 8, 8,268, 6, 8, 6, 54, 30, 54,
#endif
- 0, 15, 8, 0, 0, 8, 8, 32, -1, 5, 0, 0, 0, 0, 0, 0,
+ 0, 15, 8, 6, -1, 8, 8, 32, -1, 5, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 14, -1, -1, -1, 8, 25, 0, 0, 26, 0,
//#0x0800
#if PACKETVER < 20091229
@@ -16406,6 +16536,7 @@ static int packetdb_readdb(void)
{clif_parse_mercenary_action,"mermenu"},
{clif_parse_progressbar,"progressbar"},
{clif_parse_SkillSelectMenu,"skillselectmenu"},
+ {clif_parse_ItemListWindowSelected,"itemlistwindowselected"},
#if PACKETVER >= 20091229
{clif_parse_PartyBookingRegisterReq,"bookingregreq"},
{clif_parse_PartyBookingSearchReq,"bookingsearchreq"},
diff --git a/src/map/clif.h b/src/map/clif.h
index 575798c6f..3dcab7379 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -429,7 +429,7 @@ void clif_skill_warppoint(struct map_session_data* sd, short skill_num, short sk
void clif_skill_memomessage(struct map_session_data* sd, int type);
void clif_skill_teleportmessage(struct map_session_data *sd, int type);
void 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);
+void clif_cooking_list(struct map_session_data *sd, int trigger, int skill_id, int qty, int list_type);
void clif_produceeffect(struct map_session_data* sd,int flag,int nameid);
@@ -714,35 +714,27 @@ void clif_search_store_info_click_ack(struct map_session_data* sd, short x, shor
**/
void clif_msgtable(int fd, int line);
void clif_msgtable_num(int fd, int line, int num);
-/**
- * Elemental Converter List
- **/
+
int clif_elementalconverter_list(struct map_session_data *sd);
-/**
- * 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);
-/**
- * Shadow Chaser
- **/
+
int clif_autoshadowspell_list(struct map_session_data *sd);
-/**
- * New Mounts
- **/
+
int clif_status_load_notick(struct block_list *bl,int type,int flag,int val1, int val2, int val3);
int clif_status_load_single(int fd, int id,int type,int flag,int val1, int val2, int val3);
+
+
+int clif_skill_itemlistwindow( struct map_session_data *sd, int skill_id, int skill_lv );
+void clif_elemental_info(struct map_session_data *sd);
+void clif_elemental_updatestatus(struct map_session_data *sd, int type);
+
/**
* Color Table
**/
@@ -753,4 +745,7 @@ enum clif_colors {
};
unsigned long color_table[COLOR_MAX];
int clif_colormes(struct map_session_data * sd, enum clif_colors color, const char* msg);
+
+#define clif_menuskill_clear(sd) (sd)->menuskill_id = (sd)->menuskill_val = (sd)->menuskill_val2 = 0;
+
#endif /* _CLIF_H_ */
diff --git a/src/map/elemental.c b/src/map/elemental.c
new file mode 100644
index 000000000..fc8c17e0e
--- /dev/null
+++ b/src/map/elemental.c
@@ -0,0 +1,825 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/cbasetypes.h"
+#include "../common/malloc.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/utils.h"
+
+#include "log.h"
+#include "clif.h"
+#include "chrif.h"
+#include "intif.h"
+#include "itemdb.h"
+#include "map.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "mob.h"
+#include "pet.h"
+#include "battle.h"
+#include "party.h"
+#include "guild.h"
+#include "atcommand.h"
+#include "script.h"
+#include "npc.h"
+#include "trade.h"
+#include "unit.h"
+#include "elemental.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+struct s_elemental_db elemental_db[MAX_ELEMENTAL_CLASS]; // Elemental Database
+
+int elemental_search_index(int class_) {
+ int i;
+ ARR_FIND(0, MAX_ELEMENTAL_CLASS, i, elemental_db[i].class_ == class_);
+ return (i == MAX_ELEMENTAL_CLASS)?-1:i;
+}
+
+bool elemental_class(int class_) {
+ return (bool)(elemental_search_index(class_) > -1);
+}
+
+struct view_data * elemental_get_viewdata(int class_) {
+ int i = elemental_search_index(class_);
+ if( i < 0 )
+ return 0;
+
+ return &elemental_db[i].vd;
+}
+
+int elemental_create(struct map_session_data *sd, int class_, unsigned int lifetime) {
+ struct s_elemental ele;
+ struct s_elemental_db *db;
+ int i;
+
+ nullpo_retr(1,sd);
+
+ if( (i = elemental_search_index(class_)) < 0 )
+ return 0;
+
+ db = &elemental_db[i];
+ memset(&ele,0,sizeof(struct s_elemental));
+
+ ele.char_id = sd->status.char_id;
+ ele.class_ = class_;
+ ele.mode = EL_MODE_PASSIVE; // Initial mode
+ ele.hp = db->status.max_hp;
+ ele.sp = db->status.max_sp;
+ ele.life_time = lifetime;
+
+ // Request Char Server to create this elemental
+ intif_elemental_create(&ele);
+
+ return 1;
+}
+
+int elemental_get_lifetime(struct elemental_data *ed) {
+ const struct TimerData * td;
+ if( ed == NULL || ed->summon_timer == INVALID_TIMER )
+ return 0;
+
+ td = get_timer(ed->summon_timer);
+ return (td != NULL) ? DIFF_TICK(td->tick, gettick()) : 0;
+}
+
+int elemental_save(struct elemental_data *ed) {
+ ed->elemental.hp = ed->battle_status.hp;
+ ed->elemental.sp = ed->battle_status.sp;
+ ed->elemental.life_time = elemental_get_lifetime(ed);
+
+ intif_elemental_save(&ed->elemental);
+ return 1;
+}
+
+static int elemental_summon_end(int tid, unsigned int tick, int id, intptr data) {
+ struct map_session_data *sd;
+ struct elemental_data *ed;
+
+ if( (sd = map_id2sd(id)) == NULL )
+ return 1;
+ if( (ed = sd->ed) == NULL )
+ return 1;
+
+ if( ed->summon_timer != tid ) {
+ ShowError("elemental_summon_end %d != %d.\n", ed->summon_timer, tid);
+ return 0;
+ }
+
+ ed->summon_timer = INVALID_TIMER;
+ elemental_delete(ed, 0); // Elemental's summon time is over.
+
+ return 0;
+}
+
+void elemental_summon_stop(struct elemental_data *ed) {
+ nullpo_retv(ed);
+ if( ed->summon_timer != INVALID_TIMER )
+ delete_timer(ed->summon_timer, elemental_summon_end);
+ ed->summon_timer = INVALID_TIMER;
+}
+
+int elemental_delete(struct elemental_data *ed, int reply) {
+ struct map_session_data *sd;
+
+ nullpo_ret(ed);
+
+ sd = ed->master;
+ ed->elemental.life_time = 0;
+
+ elemental_clean_effect(ed);
+ elemental_summon_stop(ed);
+
+ if( !sd )
+ return unit_free(&ed->bl, 0);
+
+ sd->ed = NULL;
+ sd->status.ele_id = 0;
+
+ return unit_remove_map(&ed->bl, 0);
+}
+
+void elemental_summon_init(struct elemental_data *ed) {
+ if( ed->summon_timer == INVALID_TIMER )
+ ed->summon_timer = add_timer(gettick() + ed->elemental.life_time, elemental_summon_end, ed->master->bl.id, 0);
+
+ ed->regen.state.block = 0;
+}
+
+int elemental_data_received(struct s_elemental *ele, bool flag) {
+ struct map_session_data *sd;
+ struct elemental_data *ed;
+ struct s_elemental_db *db;
+ int i = elemental_search_index(ele->class_);
+
+ if( (sd = map_charid2sd(ele->char_id)) == NULL )
+ return 0;
+
+ if( !flag || i < 0 ) { // Not created - loaded - DB info
+ sd->status.ele_id = 0;
+ return 0;
+ }
+
+ db = &elemental_db[i];
+ if( !sd->ed ) { // Initialize it after first summon.
+ sd->ed = ed = (struct elemental_data*)aCalloc(1,sizeof(struct elemental_data));
+ ed->bl.type = BL_ELEM;
+ ed->bl.id = npc_get_new_npc_id();
+ ed->master = sd;
+ ed->db = db;
+ memcpy(&ed->elemental, ele, sizeof(struct s_elemental));
+ status_set_viewdata(&ed->bl, ed->elemental.class_);
+ ed->vd->head_mid = 10; // Why?
+ status_change_init(&ed->bl);
+ unit_dataset(&ed->bl);
+ ed->ud.dir = sd->ud.dir;
+
+ ed->bl.m = sd->bl.m;
+ ed->bl.x = sd->bl.x;
+ ed->bl.y = sd->bl.y;
+ unit_calc_pos(&ed->bl, sd->bl.x, sd->bl.y, sd->ud.dir);
+ ed->bl.x = ed->ud.to_x;
+ ed->bl.y = ed->ud.to_y;
+
+ map_addiddb(&ed->bl);
+ status_calc_elemental(ed,1);
+ ed->last_thinktime = gettick();
+ ed->summon_timer = INVALID_TIMER;
+ ed->battle_status.mode = ele->mode = EL_MODE_PASSIVE; // Initial mode.
+ elemental_summon_init(ed);
+ } else {
+ memcpy(&sd->ed->elemental, ele, sizeof(struct s_elemental));
+ ed = sd->ed;
+ }
+
+ sd->status.ele_id = ele->elemental_id;
+ ed->battle_status.mode = ele->mode = EL_MODE_PASSIVE; // Initial mode.
+
+ if( ed->bl.prev == NULL && sd->bl.prev != NULL ) {
+ map_addblock(&ed->bl);
+ clif_spawn(&ed->bl);
+ clif_elemental_info(sd);
+ clif_elemental_updatestatus(sd,SP_HP);
+ clif_hpmeter_single(sd->fd,ed->bl.id,ed->battle_status.hp,ed->battle_status.matk_max);
+ clif_elemental_updatestatus(sd,SP_SP);
+ }
+
+ return 1;
+}
+
+int elemental_clean_single_effect(struct elemental_data *ed, int skill_num) {
+ struct block_list *bl;
+ sc_type type = status_skill2sc(skill_num);
+
+ nullpo_ret(ed);
+
+ bl = battle_get_master(&ed->bl);
+
+ if( type ) {
+ switch( type ) {
+ // Just remove status change.
+ case SC_PYROTECHNIC_OPTION:
+ case SC_HEATER_OPTION:
+ case SC_TROPIC_OPTION:
+ case SC_FIRE_CLOAK_OPTION:
+ case SC_AQUAPLAY_OPTION:
+ case SC_WATER_SCREEN_OPTION:
+ case SC_COOLER_OPTION:
+ case SC_CHILLY_AIR_OPTION:
+ case SC_GUST_OPTION:
+ case SC_WIND_STEP_OPTION:
+ case SC_BLAST_OPTION:
+ case SC_WATER_DROP_OPTION:
+ case SC_WIND_CURTAIN_OPTION:
+ case SC_WILD_STORM_OPTION:
+ case SC_PETROLOGY_OPTION:
+ case SC_SOLID_SKIN_OPTION:
+ case SC_CURSED_SOIL_OPTION:
+ case SC_STONE_SHIELD_OPTION:
+ case SC_UPHEAVAL_OPTION:
+ case SC_CIRCLE_OF_FIRE_OPTION:
+ case SC_TIDAL_WEAPON_OPTION:
+ if( bl ) status_change_end(bl,type,-1); // Master
+ status_change_end(&ed->bl,type-1,-1); // Elemental Spirit
+ break;
+ case SC_ZEPHYR:
+ if( bl ) status_change_end(bl,type,-1);
+ break;
+ }
+ }
+ if( skill_get_unit_id(skill_num,0) )
+ skill_clear_unitgroup(&ed->bl);
+
+ return 1;
+}
+
+int elemental_clean_effect(struct elemental_data *ed) {
+ struct map_session_data *sd;
+
+ nullpo_ret(ed);
+
+ // Elemental side
+ status_change_end(&ed->bl, SC_TROPIC, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_HEATER, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_AQUAPLAY, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_COOLER, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_CHILLY_AIR, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_PYROTECHNIC, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_FIRE_CLOAK, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_WATER_DROP, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_WATER_SCREEN, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_GUST, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_WIND_STEP, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_BLAST, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_WIND_CURTAIN, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_WILD_STORM, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_PETROLOGY, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_SOLID_SKIN, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_CURSED_SOIL, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_STONE_SHIELD, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_UPHEAVAL, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_CIRCLE_OF_FIRE, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_TIDAL_WEAPON, INVALID_TIMER);
+
+ skill_clear_unitgroup(&ed->bl);
+
+ if( (sd = ed->master) == NULL )
+ return 0;
+
+ // Master side
+ status_change_end(&sd->bl, SC_TROPIC_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_HEATER_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_AQUAPLAY_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_COOLER_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_CHILLY_AIR_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_PYROTECHNIC_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_FIRE_CLOAK_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WATER_DROP_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WATER_SCREEN_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_GUST_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WIND_STEP_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_BLAST_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WATER_DROP_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WIND_CURTAIN_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WILD_STORM_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_ZEPHYR, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WIND_STEP_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_PETROLOGY_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_SOLID_SKIN_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_CURSED_SOIL_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_STONE_SHIELD_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_UPHEAVAL_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_CIRCLE_OF_FIRE_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_TIDAL_WEAPON_OPTION, INVALID_TIMER);
+
+ return 1;
+}
+
+int elemental_action(struct elemental_data *ed, struct block_list *bl, unsigned int tick) {
+ short skillnum, skilllv;
+ int i;
+
+ nullpo_ret(ed);
+ nullpo_ret(bl);
+
+ if( !ed->master )
+ return 0;
+
+ if( ed->target_id )
+ elemental_unlocktarget(ed); // Remove previous target.
+
+ ARR_FIND(0, MAX_ELESKILLTREE, i, ed->db->skill[i].id && (ed->db->skill[i].mode&EL_SKILLMODE_AGGRESSIVE));
+ if( i == MAX_ELESKILLTREE )
+ return 0;
+
+ skillnum = ed->db->skill[i].id;
+ skilllv = ed->db->skill[i].lv;
+
+ if( elemental_skillnotok(skillnum, ed) )
+ return 0;
+
+ if( ed->ud.skilltimer != -1 )
+ return 0;
+ else if( DIFF_TICK(tick, ed->ud.canact_tick) < 0 )
+ return 0;
+
+ ed->target_id = ed->ud.skilltarget = bl->id; // Set new target
+ ed->last_thinktime = tick;
+
+ // Not in skill range.
+ if( !battle_check_range(&ed->bl,bl,skill_get_range(skillnum,skilllv)) ) {
+ // Try to walk to the target.
+ if( !unit_walktobl(&ed->bl, bl, skill_get_range(skillnum,skilllv), 2) )
+ elemental_unlocktarget(ed);
+ else {
+ // Walking, waiting to be in range. Client don't handle it, then we must handle it here.
+ int walk_dist = distance_bl(&ed->bl,bl) - skill_get_range(skillnum,skilllv);
+ ed->ud.skillid = skillnum;
+ ed->ud.skilllv = skilllv;
+
+ if( skill_get_inf(skillnum) & INF_GROUND_SKILL )
+ ed->ud.skilltimer = add_timer( tick+status_get_speed(&ed->bl)*walk_dist, skill_castend_pos, ed->bl.id, 0 );
+ else
+ ed->ud.skilltimer = add_timer( tick+status_get_speed(&ed->bl)*walk_dist, skill_castend_id, ed->bl.id, 0 );
+ }
+ return 1;
+
+ }
+ //Otherwise, just cast the skill.
+ if( skill_get_inf(skillnum) & INF_GROUND_SKILL )
+ unit_skilluse_pos(&ed->bl, bl->x, bl->y, skillnum, skilllv);
+ else
+ unit_skilluse_id(&ed->bl, bl->id, skillnum, skilllv);
+
+ // Reset target.
+ ed->target_id = 0;
+
+ return 1;
+}
+
+/*===============================================================
+ * Action that elemental perform after changing mode.
+ * Activates one of the skills of the new mode.
+ *-------------------------------------------------------------*/
+int elemental_change_mode_ack(struct elemental_data *ed, int mode) {
+ struct block_list *bl = &ed->master->bl;
+ short skillnum, skilllv;
+ int i;
+
+ nullpo_ret(ed);
+
+ if( !bl )
+ return 0;
+
+ // Select a skill.
+ ARR_FIND(0, MAX_ELESKILLTREE, i, ed->db->skill[i].id && (ed->db->skill[i].mode&mode));
+ if( i == MAX_ELESKILLTREE )
+ return 0;
+
+ skillnum = ed->db->skill[i].id;
+ skilllv = ed->db->skill[i].lv;
+
+ if( elemental_skillnotok(skillnum, ed) )
+ return 0;
+
+ if( ed->ud.skilltimer != -1 )
+ return 0;
+ else if( DIFF_TICK(gettick(), ed->ud.canact_tick) < 0 )
+ return 0;
+
+ ed->target_id = bl->id; // Set new target
+ ed->last_thinktime = gettick();
+
+ if( skill_get_inf(skillnum) & INF_GROUND_SKILL )
+ unit_skilluse_pos(&ed->bl, bl->x, bl->y, skillnum, skilllv);
+ else
+ unit_skilluse_id(&ed->bl,bl->id,skillnum,skilllv);
+
+ ed->target_id = 0; // Reset target after casting the skill to avoid continious attack.
+
+ return 1;
+}
+
+/*===============================================================
+ * Change elemental mode.
+ *-------------------------------------------------------------*/
+int elemental_change_mode(struct elemental_data *ed, int mode) {
+ nullpo_ret(ed);
+
+ // Remove target
+ elemental_unlocktarget(ed);
+
+ // Removes the effects of the previous mode.
+ if(ed->elemental.mode != mode ) elemental_clean_effect(ed);
+
+ ed->battle_status.mode = ed->elemental.mode = mode;
+
+ // Normalize elemental mode to elemental skill mode.
+ if( mode == EL_MODE_AGGRESSIVE ) mode = EL_SKILLMODE_AGGRESSIVE; // Aggressive spirit mode -> Aggressive spirit skill.
+ else if( mode == EL_MODE_ASSIST ) mode = EL_SKILLMODE_ASSIST; // Assist spirit mode -> Assist spirit skill.
+ else mode = EL_SKILLMODE_PASIVE; // Passive spirit mode -> Passive spirit skill.
+
+ // Use a skill inmediately after every change mode.
+ if( mode != EL_SKILLMODE_AGGRESSIVE )
+ elemental_change_mode_ack(ed,mode);
+ return 1;
+}
+
+void elemental_damage(struct elemental_data *ed, struct block_list *src, int hp, int sp) {
+ if( hp )
+ clif_elemental_updatestatus(ed->master, SP_HP);
+ if( sp )
+ clif_elemental_updatestatus(ed->master, SP_SP);
+}
+
+void elemental_heal(struct elemental_data *ed, int hp, int sp) {
+ if( hp )
+ clif_elemental_updatestatus(ed->master, SP_HP);
+ if( sp )
+ clif_elemental_updatestatus(ed->master, SP_SP);
+}
+
+int elemental_dead(struct elemental_data *ed, struct block_list *src) {
+ elemental_delete(ed, 1);
+ return 0;
+}
+
+int elemental_unlocktarget(struct elemental_data *ed) {
+ nullpo_ret(ed);
+
+ ed->target_id = 0;
+ elemental_stop_attack(ed);
+ elemental_stop_walking(ed,1);
+ return 0;
+}
+
+int elemental_skillnotok(int skillid, struct elemental_data *ed) {
+ int i = skill_get_index(skillid);
+ nullpo_retr(1,ed);
+
+ if (i == 0)
+ return 1; // invalid skill id
+
+ if( ed->blockskill[i] > 0 )
+ return 1;
+
+ return skillnotok(skillid, ed->master);
+}
+
+int elemental_set_target( struct map_session_data *sd, struct block_list *bl ) {
+ struct elemental_data *ed = sd->ed;
+
+ nullpo_ret(ed);
+ nullpo_ret(bl);
+
+ if( ed->bl.m != bl->m || !check_distance_bl(&ed->bl, bl, ed->db->range2) )
+ return 0;
+
+ if( !status_check_skilluse(&ed->bl, bl, 0, 0) )
+ return 0;
+
+ if( ed->target_id == 0 )
+ ed->target_id = bl->id;
+
+ return 1;
+}
+
+static int elemental_ai_sub_timer_activesearch(struct block_list *bl, va_list ap) {
+ struct elemental_data *ed;
+ struct block_list **target;
+ int dist;
+
+ nullpo_ret(bl);
+
+ ed = va_arg(ap,struct elemental_data *);
+ target = va_arg(ap,struct block_list**);
+
+ //If can't seek yet, not an enemy, or you can't attack it, skip.
+ if( (*target) == bl || !status_check_skilluse(&ed->bl, bl, 0, 0) )
+ return 0;
+
+ if( battle_check_target(&ed->bl,bl,BCT_ENEMY) <= 0 )
+ return 0;
+
+ switch( bl->type ) {
+ case BL_PC:
+ if( !map_flag_vs(ed->bl.m) )
+ return 0;
+ default:
+ dist = distance_bl(&ed->bl, bl);
+ if( ((*target) == NULL || !check_distance_bl(&ed->bl, *target, dist)) && battle_check_range(&ed->bl,bl,ed->db->range2) ) { //Pick closest target?
+ (*target) = bl;
+ ed->target_id = bl->id;
+ ed->min_chase = dist + ed->db->range3;
+ if( ed->min_chase > AREA_SIZE )
+ ed->min_chase = AREA_SIZE;
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int elemental_ai_sub_timer(struct elemental_data *ed, struct map_session_data *sd, unsigned int tick) {
+ struct block_list *target = NULL;
+ int master_dist, view_range, mode;
+
+ nullpo_ret(ed);
+ nullpo_ret(sd);
+
+ if( ed->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL )
+ return 0;
+
+ if( DIFF_TICK(tick,ed->last_thinktime) < MIN_ELETHINKTIME )
+ return 0;
+
+ ed->last_thinktime = tick;
+
+ if( ed->ud.skilltimer != -1 )
+ return 0;
+
+ if( ed->ud.walktimer != -1 && ed->ud.walkpath.path_pos <= 2 )
+ return 0; //No thinking when you just started to walk.
+
+ if(ed->ud.walkpath.path_pos < ed->ud.walkpath.path_len && ed->ud.target == sd->bl.id)
+ return 0; //No thinking until be near the master.
+
+ if( ed->sc.count && ed->sc.data[SC_BLIND] )
+ view_range = 3;
+ else
+ view_range = ed->db->range2;
+
+ mode = status_get_mode(&ed->bl);
+
+ master_dist = distance_bl(&sd->bl, &ed->bl);
+ if( master_dist > AREA_SIZE ) { // Master out of vision range.
+ elemental_unlocktarget(ed);
+ unit_warp(&ed->bl,sd->bl.m,sd->bl.x,sd->bl.y,3);
+ return 0;
+ } else if( master_dist > MAX_ELEDISTANCE ) { // Master too far, chase.
+ short x = sd->bl.x, y = sd->bl.y;
+ if( ed->target_id )
+ elemental_unlocktarget(ed);
+ if( ed->ud.walktimer != -1 && ed->ud.target == sd->bl.id )
+ return 0; //Already walking to him
+ if( DIFF_TICK(tick, ed->ud.canmove_tick) < 0 )
+ return 0; //Can't move yet.
+ if( map_search_freecell(&ed->bl, sd->bl.m, &x, &y, MIN_ELEDISTANCE, MIN_ELEDISTANCE, 1)
+ && unit_walktoxy(&ed->bl, x, y, 0) )
+ return 0;
+ }
+
+ if( mode == EL_MODE_AGGRESSIVE ) {
+ target = map_id2bl(ed->ud.target);
+
+ if( !target )
+ map_foreachinrange(elemental_ai_sub_timer_activesearch, &ed->bl, ed->db->range2, BL_CHAR, ed, &target, status_get_mode(&ed->bl));
+
+ if( !target ) { //No targets available.
+ elemental_unlocktarget(ed);
+ return 1;
+ }
+
+ if( battle_check_range(&ed->bl,target,ed->db->range2) && rand()%100 < 2 ) { // 2% chance to cast attack skill.
+ if( elemental_action(ed,target,tick) )
+ return 1;
+ }
+
+ //Attempt to attack.
+ //At this point we know the target is attackable, we just gotta check if the range matches.
+ if( ed->ud.target == target->id && ed->ud.attacktimer != -1 ) //Already locked.
+ return 1;
+
+ if( battle_check_range(&ed->bl, target, ed->base_status.rhw.range) ) {//Target within range, engage
+ unit_attack(&ed->bl,target->id,1);
+ return 1;
+ }
+
+ //Follow up if possible.
+ if( !unit_walktobl(&ed->bl, target, ed->base_status.rhw.range, 2) )
+ elemental_unlocktarget(ed);
+ }
+
+ return 0;
+}
+
+static int elemental_ai_sub_foreachclient(struct map_session_data *sd, va_list ap) {
+ unsigned int tick = va_arg(ap,unsigned int);
+ if(sd->status.ele_id && sd->ed)
+ elemental_ai_sub_timer(sd->ed,sd,tick);
+
+ return 0;
+}
+
+static int elemental_ai_timer(int tid, unsigned int tick, int id, intptr data) {
+ map_foreachpc(elemental_ai_sub_foreachclient,tick);
+
+ return 0;
+}
+
+int read_elementaldb(void) {
+ FILE *fp;
+ char line[1024], *p;
+ char *str[26];
+ int i, j = 0, k = 0, ele;
+ struct s_elemental_db *db;
+ struct status_data *status;
+
+ sprintf(line, "%s/%s", db_path, "elemental_db.txt");
+ memset(elemental_db,0,sizeof(elemental_db));
+
+ fp = fopen(line, "r");
+ if( !fp ) {
+ ShowError("read_elementaldb : can't read elemental_db.txt\n");
+ return -1;
+ }
+
+ while( fgets(line, sizeof(line), fp) && j < MAX_ELEMENTAL_CLASS ) {
+ k++;
+ if( line[0] == '/' && line[1] == '/' )
+ continue;
+
+ i = 0;
+ p = strtok(line, ",");
+ while( p != NULL && i < 26 ) {
+ str[i++] = p;
+ p = strtok(NULL, ",");
+ }
+ if( i < 26 ) {
+ ShowError("read_elementaldb : Incorrect number of columns at elemental_db.txt line %d.\n", k);
+ continue;
+ }
+
+ db = &elemental_db[j];
+ db->class_ = atoi(str[0]);
+ strncpy(db->sprite, str[1], NAME_LENGTH);
+ strncpy(db->name, str[2], NAME_LENGTH);
+ db->lv = atoi(str[3]);
+
+ status = &db->status;
+ db->vd.class_ = db->class_;
+
+ status->max_hp = atoi(str[4]);
+ status->max_sp = atoi(str[5]);
+ status->rhw.range = atoi(str[6]);
+ status->rhw.atk = atoi(str[7]);
+ status->rhw.atk2 = status->rhw.atk + atoi(str[8]);
+ status->def = atoi(str[9]);
+ status->mdef = atoi(str[10]);
+ status->str = atoi(str[11]);
+ status->agi = atoi(str[12]);
+ status->vit = atoi(str[13]);
+ status->int_ = atoi(str[14]);
+ status->dex = atoi(str[15]);
+ status->luk = atoi(str[16]);
+ db->range2 = atoi(str[17]);
+ db->range3 = atoi(str[18]);
+ status->size = atoi(str[19]);
+ status->race = atoi(str[20]);
+
+ ele = atoi(str[21]);
+ status->def_ele = ele%10;
+ status->ele_lv = ele/20;
+ if( status->def_ele >= ELE_MAX ) {
+ ShowWarning("Elemental %d has invalid element type %d (max element is %d)\n", db->class_, status->def_ele, ELE_MAX - 1);
+ status->def_ele = ELE_NEUTRAL;
+ }
+ if( status->ele_lv < 1 || status->ele_lv > 4 ) {
+ ShowWarning("Elemental %d has invalid element level %d (max is 4)\n", db->class_, status->ele_lv);
+ status->ele_lv = 1;
+ }
+
+ status->aspd_rate = 1000;
+ status->speed = atoi(str[22]);
+ status->adelay = atoi(str[23]);
+ status->amotion = atoi(str[24]);
+ status->dmotion = atoi(str[25]);
+
+ j++;
+ }
+
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' elementals in '"CL_WHITE"db/elemental_db.txt"CL_RESET"'.\n",j);
+
+ return 0;
+}
+
+int read_elemental_skilldb(void) {
+ FILE *fp;
+ char line[1024], *p;
+ char *str[4];
+ struct s_elemental_db *db;
+ int i, j = 0, k = 0, class_;
+ int skillid, skilllv, skillmode;
+
+ sprintf(line, "%s/%s", db_path, "elemental_skill_db.txt");
+ fp = fopen(line, "r");
+ if( !fp ) {
+ ShowError("read_elemental_skilldb : can't read elemental_skill_db.txt\n");
+ return -1;
+ }
+
+ while( fgets(line, sizeof(line), fp) ) {
+ k++;
+ if( line[0] == '/' && line[1] == '/' )
+ continue;
+
+ i = 0;
+ p = strtok(line, ",");
+ while( p != NULL && i < 4 ) {
+ str[i++] = p;
+ p = strtok(NULL, ",");
+ }
+ if( i < 4 ) {
+ ShowError("read_elemental_skilldb : Incorrect number of columns at elemental_skill_db.txt line %d.\n", k);
+ continue;
+ }
+
+ class_ = atoi(str[0]);
+ ARR_FIND(0, MAX_ELEMENTAL_CLASS, i, class_ == elemental_db[i].class_);
+ if( i == MAX_ELEMENTAL_CLASS ) {
+ ShowError("read_elemental_skilldb : Class not found in elemental_db for skill entry, line %d.\n", k);
+ continue;
+ }
+
+ skillid = atoi(str[1]);
+ if( skillid < EL_SKILLBASE || skillid >= EL_SKILLBASE + MAX_ELEMENTALSKILL ) {
+ ShowError("read_elemental_skilldb : Skill out of range, line %d.\n", k);
+ continue;
+ }
+
+ db = &elemental_db[i];
+ skilllv = atoi(str[2]);
+
+ skillmode = atoi(str[3]);
+ if( skillmode < EL_SKILLMODE_PASIVE || skillmode > EL_SKILLMODE_AGGRESSIVE ) {
+ ShowError("read_elemental_skilldb : Skillmode out of range, line %d.\n",k);
+ skillmode = EL_SKILLMODE_PASIVE;
+ continue;
+ }
+ ARR_FIND( 0, MAX_ELESKILLTREE, i, db->skill[i].id == 0 || db->skill[i].id == skillid );
+ if( i == MAX_ELESKILLTREE ) {
+ ShowWarning("Unable to load skill %d into Elemental %d's tree. Maximum number of skills per elemental has been reached.\n", skillid, class_);
+ continue;
+ }
+ db->skill[i].id = skillid;
+ db->skill[i].lv = skilllv;
+ db->skill[i].mode = skillmode;
+ j++;
+ }
+
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"db/elemental_skill_db.txt"CL_RESET"'.\n",j);
+ return 0;
+}
+
+void reload_elementaldb(void) {
+ read_elementaldb();
+ reload_elemental_skilldb();
+}
+
+void reload_elemental_skilldb(void) {
+ read_elemental_skilldb();
+}
+
+int do_init_elemental(void) {
+ read_elementaldb();
+ read_elemental_skilldb();
+
+ add_timer_func_list(elemental_ai_timer,"elemental_ai_timer");
+ add_timer_interval(gettick()+MIN_ELETHINKTIME,elemental_ai_timer,0,0,MIN_ELETHINKTIME);
+
+ return 0;
+}
+
+void do_final_elemental(void) {
+ return;
+}
diff --git a/src/map/elemental.h b/src/map/elemental.h
new file mode 100644
index 000000000..9f8ef1e22
--- /dev/null
+++ b/src/map/elemental.h
@@ -0,0 +1,95 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _ELEMENTAL_H_
+#define _ELEMENTAL_H_
+
+#include "status.h" // struct status_data, struct status_change
+#include "unit.h" // struct unit_data
+
+#define MIN_ELETHINKTIME 100
+#define MIN_ELEDISTANCE 2
+#define MAX_ELEDISTANCE 6
+
+#define EL_MODE_AGGRESSIVE (MD_CANMOVE|MD_AGGRESSIVE|MD_CANATTACK)
+#define EL_MODE_ASSIST (MD_CANMOVE|MD_ASSIST)
+#define EL_MODE_PASSIVE MD_CANMOVE
+
+#define EL_SKILLMODE_PASIVE 0x1
+#define EL_SKILLMODE_ASSIST 0x2
+#define EL_SKILLMODE_AGGRESSIVE 0x4
+
+struct elemental_skill {
+ unsigned short id, lv;
+ short mode;
+};
+
+struct s_elemental_db {
+ int class_;
+ char sprite[NAME_LENGTH], name[NAME_LENGTH];
+ unsigned short lv;
+ short range2, range3;
+ struct status_data status;
+ struct view_data vd;
+ struct elemental_skill skill[MAX_ELESKILLTREE];
+};
+
+extern struct s_elemental_db elemental_db[MAX_ELEMENTAL_CLASS];
+
+struct elemental_data {
+ struct block_list bl;
+ struct unit_data ud;
+ struct view_data *vd;
+ struct status_data base_status, battle_status;
+ struct status_change sc;
+ struct regen_data regen;
+
+ struct s_elemental_db *db;
+ struct s_elemental elemental;
+ char blockskill[MAX_SKILL];
+
+ struct map_session_data *master;
+ int summon_timer;
+ int skill_timer;
+
+ unsigned last_thinktime, last_linktime;
+ short min_chase;
+ int target_id, attacked_id;
+};
+
+bool elemental_class(int class_);
+struct view_data * elemental_get_viewdata(int class_);
+
+int elemental_create(struct map_session_data *sd, int class_, unsigned int lifetime);
+int elemental_data_received(struct s_elemental *ele, bool flag);
+int elemental_save(struct elemental_data *ed);
+
+int elemental_change_mode_ack(struct elemental_data *ed, int mode);
+int elemental_change_mode(struct elemental_data *ed, int mode);
+
+void elemental_damage(struct elemental_data *ed, struct block_list *src, int hp, int sp);
+void elemental_heal(struct elemental_data *ed, int hp, int sp);
+int elemental_dead(struct elemental_data *ed, struct block_list *src);
+
+int elemental_delete(struct elemental_data *ed, int reply);
+void elemental_summon_stop(struct elemental_data *ed);
+
+int elemental_get_lifetime(struct elemental_data *ed);
+
+int elemental_unlocktarget(struct elemental_data *ed);
+int elemental_skillnotok(int skillid, struct elemental_data *ed);
+int elemental_set_target( struct map_session_data *sd, struct block_list *bl );
+int elemental_clean_single_effect(struct elemental_data *ed, int skill_num);
+int elemental_clean_effect(struct elemental_data *ed);
+int elemental_action(struct elemental_data *ed, struct block_list *bl, unsigned int tick);
+
+#define elemental_stop_walking(ed, type) unit_stop_walking(&(ed)->bl, type)
+#define elemental_stop_attack(ed) unit_stop_attack(&(ed)->bl)
+
+int read_elemental_skilldb(void);
+void reload_elementaldb(void);
+void reload_elemental_skilldb(void);
+int do_init_elemental(void);
+void do_final_elemental(void);
+
+#endif /* _ELEMENTAL_H_ */
diff --git a/src/map/intif.c b/src/map/intif.c
index 78f3d585c..ce0ad8e6a 100644
--- a/src/map/intif.c
+++ b/src/map/intif.c
@@ -21,6 +21,7 @@
#include "atcommand.h"
#include "mercenary.h"
#include "homunculus.h"
+#include "elemental.h"
#include "mail.h"
#include "quest.h"
@@ -40,7 +41,7 @@ static const int packet_len_table[]={
-1, 0, 0,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840
-1,-1, 7, 7, 7,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus]
-1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish]
- -1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3870 Mercenaries [Zephyrus]
+ -1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, 3, 0, //0x3870 Mercenaries [Zephyrus] / Elemental [pakpil]
11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880
-1,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator]
};
@@ -1988,6 +1989,94 @@ int intif_parse_mercenary_saved(int fd)
return 0;
}
+/*==========================================
+ * Elemental's System
+ *------------------------------------------*/
+int intif_elemental_create(struct s_elemental *ele)
+{
+ int size = sizeof(struct s_elemental) + 4;
+
+ if( CheckForCharServer() )
+ return 0;
+
+ WFIFOHEAD(inter_fd,size);
+ WFIFOW(inter_fd,0) = 0x307c;
+ WFIFOW(inter_fd,2) = size;
+ memcpy(WFIFOP(inter_fd,4), ele, sizeof(struct s_elemental));
+ WFIFOSET(inter_fd,size);
+ return 0;
+}
+
+int intif_parse_elemental_received(int fd)
+{
+ int len = RFIFOW(fd,2) - 5;
+ if( sizeof(struct s_elemental) != len )
+ {
+ if( battle_config.etc_log )
+ ShowError("intif: create elemental data size error %d != %d\n", sizeof(struct s_elemental), len);
+ return 0;
+ }
+
+ elemental_data_received((struct s_elemental*)RFIFOP(fd,5), RFIFOB(fd,4));
+ return 0;
+}
+
+int intif_elemental_request(int ele_id, int char_id)
+{
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,10);
+ WFIFOW(inter_fd,0) = 0x307d;
+ WFIFOL(inter_fd,2) = ele_id;
+ WFIFOL(inter_fd,6) = char_id;
+ WFIFOSET(inter_fd,10);
+ return 0;
+}
+
+int intif_elemental_delete(int ele_id)
+{
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x307e;
+ WFIFOL(inter_fd,2) = ele_id;
+ WFIFOSET(inter_fd,6);
+ return 0;
+}
+
+int intif_parse_elemental_deleted(int fd)
+{
+ if( RFIFOB(fd,2) != 1 )
+ ShowError("Elemental data delete failure\n");
+
+ return 0;
+}
+
+int intif_elemental_save(struct s_elemental *ele)
+{
+ int size = sizeof(struct s_elemental) + 4;
+
+ if( CheckForCharServer() )
+ return 0;
+
+ WFIFOHEAD(inter_fd,size);
+ WFIFOW(inter_fd,0) = 0x307f;
+ WFIFOW(inter_fd,2) = size;
+ memcpy(WFIFOP(inter_fd,4), ele, sizeof(struct s_elemental));
+ WFIFOSET(inter_fd,size);
+ return 0;
+}
+
+int intif_parse_elemental_saved(int fd)
+{
+ if( RFIFOB(fd,2) != 1 )
+ ShowError("Elemental data save failure\n");
+
+ return 0;
+}
+
//-----------------------------------------------------------------
// inter serverからの通信
// エラーがあれば0(false)を返すこと
@@ -2076,7 +2165,11 @@ int intif_parse(int fd)
case 0x3870: intif_parse_mercenary_received(fd); break;
case 0x3871: intif_parse_mercenary_deleted(fd); break;
case 0x3872: intif_parse_mercenary_saved(fd); break;
-
+// Elemental System
+ case 0x387c: intif_parse_elemental_received(fd); break;
+ case 0x387d: intif_parse_elemental_deleted(fd); break;
+ case 0x387e: intif_parse_elemental_saved(fd); break;
+
case 0x3880: intif_parse_CreatePet(fd); break;
case 0x3881: intif_parse_RecvPetData(fd); break;
case 0x3882: intif_parse_SavePetOk(fd); break;
diff --git a/src/map/intif.h b/src/map/intif.h
index 847523593..c5f6cb158 100644
--- a/src/map/intif.h
+++ b/src/map/intif.h
@@ -11,6 +11,7 @@ struct guild_position;
struct s_pet;
struct s_homunculus;
struct s_mercenary;
+struct s_elemental;
struct mail_message;
struct auction_data;
@@ -97,6 +98,11 @@ int intif_Auction_register(struct auction_data *auction);
int intif_Auction_cancel(int char_id, unsigned int auction_id);
int intif_Auction_close(int char_id, unsigned int auction_id);
int intif_Auction_bid(int char_id, const char* name, unsigned int auction_id, int bid);
+// ELEMENTAL SYSTEM
+int intif_elemental_create(struct s_elemental *ele);
+int intif_elemental_request(int ele_id, int char_id);
+int intif_elemental_delete(int ele_id);
+int intif_elemental_save(struct s_elemental *ele);
int CheckForCharServer(void);
diff --git a/src/map/itemdb.h b/src/map/itemdb.h
index f2e6ae61d..390954e3f 100644
--- a/src/map/itemdb.h
+++ b/src/map/itemdb.h
@@ -160,6 +160,7 @@ struct item_data* itemdb_exists(int nameid);
#define itemdb_is_poison(n) (n >= 12717 && n <= 12724)
#define itemid_isgemstone(id) ( (id) >= ITEMID_YELLOW_GEMSTONE && (id) <= ITEMID_BLUE_GEMSTONE )
#define itemdb_iscashfood(id) ( (id) >= 12202 && (id) <= 12207 )
+#define itemdb_is_GNbomb(n) (n >= 13260 && n <= 13267)
const char* itemdb_typename(int type);
int itemdb_group_bonus(struct map_session_data* sd, int itemid);
diff --git a/src/map/map.c b/src/map/map.c
index fd0d43aa4..d0b454a52 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -41,6 +41,7 @@
#include "homunculus.h"
#include "instance.h"
#include "mercenary.h"
+#include "elemental.h"
#include "atcommand.h"
#include "log.h"
#include "mail.h"
@@ -1722,8 +1723,14 @@ int map_quit(struct map_session_data *sd)
// Return loot to owner
if( sd->pd ) pet_lootitem_drop(sd->pd, sd);
+
if( sd->state.storage_flag == 1 ) sd->state.storage_flag = 0; // No need to Double Save Storage on Quit.
+ if( sd->ed ) {
+ elemental_clean_effect(sd->ed);
+ unit_remove_map(&sd->ed->bl,CLR_TELEPORT);
+ }
+
unit_remove_map_pc(sd,CLR_TELEPORT);
if( map[sd->bl.m].instance_id )
@@ -3588,6 +3595,7 @@ void do_final(void)
do_final_unit();
do_final_battleground();
do_final_duel();
+ do_final_elemental();
map_db->destroy(map_db, map_db_final);
@@ -3914,7 +3922,8 @@ int do_init(int argc, char *argv[])
do_init_unit();
do_init_battleground();
do_init_duel();
-
+ do_init_elemental();
+
npc_event_do_oninit(); // npcのOnInitイベント?行
if( console )
diff --git a/src/map/map.h b/src/map/map.h
index 660174055..b78e71b0c 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -233,12 +233,13 @@ enum bl_type {
BL_SKILL = 0x040,
BL_NPC = 0x080,
BL_CHAT = 0x100,
-
+ BL_ELEM = 0x200,
+
BL_ALL = 0xFFF,
};
//For common mapforeach calls. Since pets cannot be affected, they aren't included here yet.
-#define BL_CHAR (BL_PC|BL_MOB|BL_HOM|BL_MER)
+#define BL_CHAR (BL_PC|BL_MOB|BL_HOM|BL_MER|BL_ELEM)
enum npc_subtype { WARP, SHOP, SCRIPT, CASHSHOP };
@@ -738,6 +739,7 @@ typedef struct skill_unit TBL_SKILL;
typedef struct pet_data TBL_PET;
typedef struct homun_data TBL_HOM;
typedef struct mercenary_data TBL_MER;
+typedef struct elemental_data TBL_ELEM;
#define BL_CAST(type_, bl) \
( ((bl) == (struct block_list*)NULL || (bl)->type != (type_)) ? (T ## type_ *)NULL : (T ## type_ *)(bl) )
diff --git a/src/map/mob.c b/src/map/mob.c
index 95b357e4f..ee5bda342 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -23,6 +23,7 @@
#include "mob.h"
#include "homunculus.h"
#include "mercenary.h"
+#include "elemental.h"
#include "guild.h"
#include "itemdb.h"
#include "skill.h"
@@ -1896,6 +1897,15 @@ void mob_log_damage(struct mob_data *md, struct block_list *src, int damage)
md->attacked_id = src->id;
break;
}
+ case BL_ELEM:
+ {
+ struct elemental_data *ele = (TBL_ELEM*)src;
+ if( ele->master )
+ char_id = ele->master->status.char_id;
+ if( damage )
+ md->attacked_id = src->id;
+ break;
+ }
default: //For all unhandled types.
md->attacked_id = src->id;
}
diff --git a/src/map/npc_chat.c b/src/map/npc_chat.c
index 77c875a7e..84a6fab22 100644
--- a/src/map/npc_chat.c
+++ b/src/map/npc_chat.c
@@ -14,7 +14,7 @@
#include "pc.h" // struct map_session_data
#include "script.h" // set_var()
-#include <pcre.h>
+#include "pcre.h"
#include <stdio.h>
#include <stdlib.h>
diff --git a/src/map/pc.c b/src/map/pc.c
index 9e548b038..7a0fc67fa 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -29,6 +29,7 @@
#include "homunculus.h"
#include "instance.h"
#include "mercenary.h"
+#include "elemental.h"
#include "npc.h" // fake_nd
#include "pet.h" // pet_unlocktarget()
#include "party.h" // party_search()
@@ -1213,7 +1214,9 @@ int pc_reg_received(struct map_session_data *sd)
intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
if( sd->status.mer_id > 0 )
intif_mercenary_request(sd->status.mer_id, sd->status.char_id);
-
+ if( sd->status.ele_id > 0 )
+ intif_elemental_request(sd->status.ele_id, sd->status.char_id);
+
map_addiddb(&sd->bl);
map_delnickdb(sd->status.char_id, sd->status.name);
if (!chrif_auth_finished(sd))
@@ -6155,6 +6158,9 @@ void pc_damage(struct map_session_data *sd,struct block_list *src,unsigned int h
if( sd->status.pet_id > 0 && sd->pd && battle_config.pet_damage_support )
pet_target_check(sd,src,1);
+ if( sd->status.ele_id > 0 )
+ elemental_set_target(sd,src);
+
sd->canlog_tick = gettick();
}
@@ -6189,6 +6195,9 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
if( sd->md )
merc_delete(sd->md, 3); // Your mercenary soldier has ran away.
+ if( sd->ed )
+ elemental_delete(sd->ed, 0);
+
// Leave duel if you die [LuzZza]
if(battle_config.duel_autoleave_when_die) {
if(sd->duel_group > 0)
diff --git a/src/map/pc.h b/src/map/pc.h
index e74d4a5d0..39ac3100a 100644
--- a/src/map/pc.h
+++ b/src/map/pc.h
@@ -205,7 +205,7 @@ struct map_session_data {
short cook_mastery; // range: [0,1999] [Inkfish]
unsigned char blockskill[MAX_SKILL];
int cloneskill_id, reproduceskill_id;
- int menuskill_id, menuskill_val;
+ int menuskill_id, menuskill_val, menuskill_val2;
int invincible_timer;
unsigned int canlog_tick;
@@ -377,6 +377,7 @@ struct map_session_data {
struct pet_data *pd;
struct homun_data *hd; // [blackhole89]
struct mercenary_data *md;
+ struct elemental_data *ed;
struct{
int m; //-1 - none, other: map index corresponding to map name.
diff --git a/src/map/script.c b/src/map/script.c
index d58233dc5..a2ffb54a0 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -7835,7 +7835,7 @@ BUILDIN_FUNC(cooking)
return 0;
trigger=script_getnum(st,2);
- clif_cooking_list(sd, trigger);
+ clif_cooking_list(sd, trigger, AM_PHARMACY, 1, 1);
return 0;
}
/*==========================================
diff --git a/src/map/skill.c b/src/map/skill.c
index d3ddc5562..252bb4bf1 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -20,6 +20,7 @@
#include "pet.h"
#include "homunculus.h"
#include "mercenary.h"
+#include "elemental.h"
#include "mob.h"
#include "npc.h"
#include "battle.h"
@@ -49,6 +50,8 @@
#define MC_SKILLRANGEMAX (MC_SKILLRANGEMIN+MAX_MERCSKILL)
#define HM_SKILLRANGEMIN 700
#define HM_SKILLRANGEMAX (HM_SKILLRANGEMIN+MAX_HOMUNSKILL)
+#define EL_SKILLRANGEMIN MC_SKILLRANGEMAX + 1
+#define EL_SKILLRANGEMAX EL_SKILLRANGEMIN + MAX_ELEMENTALSKILL
static struct eri *skill_unit_ers = NULL; //For handling skill_unit's [Skotlex]
static struct eri *skill_timer_ers = NULL; //For handling skill_timerskills [Skotlex]
@@ -132,20 +135,19 @@ int skill_get_index( int id )
// avoid ranges reserved for mapping guild/homun/mercenary skills
if( (id >= GD_SKILLRANGEMIN && id <= GD_SKILLRANGEMAX)
|| (id >= HM_SKILLRANGEMIN && id <= HM_SKILLRANGEMAX)
- || (id >= MC_SKILLRANGEMIN && id <= MC_SKILLRANGEMAX) )
+ || (id >= MC_SKILLRANGEMIN && id <= MC_SKILLRANGEMAX)
+ || (id >= EL_SKILLRANGEMIN && id <= EL_SKILLRANGEMAX) )
return 0;
-
+
// map skill id to skill db index
if( id >= GD_SKILLBASE )
id = GD_SKILLRANGEMIN + id - GD_SKILLBASE;
- else
- if( id >= MC_SKILLBASE )
+ else if( id >= EL_SKILLBASE )
+ id = EL_SKILLRANGEMIN + id - EL_SKILLBASE;
+ else if( id >= MC_SKILLBASE )
id = MC_SKILLRANGEMIN + id - MC_SKILLBASE;
- else
- if( id >= HM_SKILLBASE )
+ else if( id >= HM_SKILLBASE )
id = HM_SKILLRANGEMIN + id - HM_SKILLBASE;
- else
- ; // identity
// validate result
if( id <= 0 || id >= MAX_SKILL_DB )
@@ -1294,6 +1296,50 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int
status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER);
}
break;
+ case SO_EARTHGRAVE:
+ sc_start(bl, SC_BLEEDING, 5 * skilllv, skilllv, skill_get_time2(skillid, skilllv)); // Need official rate. [LimitLine]
+ break;
+ case SO_DIAMONDDUST:
+ rate = 5 + 5 * skilllv;
+ if( sc && sc->data[SC_COOLER_OPTION] )
+ rate += rate * sc->data[SC_COOLER_OPTION]->val2 / 100;
+ sc_start(bl, SC_CRYSTALIZE, rate, skilllv, skill_get_time2(skillid, skilllv));
+ break;
+ case SO_VARETYR_SPEAR:
+ sc_start(bl, SC_STUN, 5 + 5 * skilllv, skilllv, skill_get_time2(skillid, skilllv));
+ break;
+ case GN_SLINGITEM_RANGEMELEEATK:
+ if( sd ) {
+ switch( sd->itemid ) { // Starting SCs here instead of do it in skill_additional_effect to simplify the code.
+ case 13261:
+ sc_start(bl, SC_STUN, 100, skilllv, skill_get_time2(GN_SLINGITEM, skilllv));
+ sc_start(bl, SC_BLEEDING, 100, skilllv, skill_get_time2(GN_SLINGITEM, skilllv));
+ break;
+ case 13262:
+ sc_start(bl, SC_MELON_BOMB, 100, skilllv, skill_get_time(GN_SLINGITEM, skilllv)); // Reduces ASPD and moviment speed
+ break;
+ case 13264:
+ sc_start(bl, SC_BANANA_BOMB, 100, skilllv, skill_get_time(GN_SLINGITEM, skilllv)); // Reduces LUK タ?Needed confirm it, may be it's bugged in kRORE?
+ sc_start(bl, SC_BANANA_BOMB_SITDOWN, 75, skilllv, skill_get_time(GN_SLINGITEM_RANGEMELEEATK,skilllv)); // Sitdown for 3 seconds.
+ break;
+ }
+ sd->itemid = -1;
+ }
+ break;
+ case EL_WIND_SLASH: // Non confirmed rate.
+ sc_start(bl, SC_BLEEDING, 25, skilllv, skill_get_time(skillid,skilllv));
+ break;
+ case EL_STONE_HAMMER:
+ rate = 10 * skilllv;
+ sc_start(bl, SC_STUN, rate, skilllv, skill_get_time(skillid,skilllv));
+ break;
+ case EL_ROCK_CRUSHER:
+ case EL_ROCK_CRUSHER_ATK:
+ sc_start(bl,status_skill2sc(skillid),50,skilllv,skill_get_time(EL_ROCK_CRUSHER,skilllv));
+ break;
+ case EL_TYPOON_MIS:
+ sc_start(bl,SC_SILENCE,10*skilllv,skilllv,skill_get_time(skillid,skilllv));
+ break;
}
if (md && battle_config.summons_trigger_autospells && md->master_id && md->special_state.ai)
@@ -2303,8 +2349,32 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
break;
case LG_OVERBRAND_BRANDISH:
case LG_OVERBRAND_PLUSATK:
+ case EL_FIRE_BOMB:
+ case EL_FIRE_BOMB_ATK:
+ case EL_FIRE_WAVE:
+ case EL_FIRE_WAVE_ATK:
+ case EL_FIRE_MANTLE:
+ case EL_CIRCLE_OF_FIRE:
+ case EL_FIRE_ARROW:
+ case EL_ICE_NEEDLE:
+ case EL_WATER_SCREW:
+ case EL_WATER_SCREW_ATK:
+ case EL_WIND_SLASH:
+ case EL_TIDAL_WEAPON:
+ case EL_ROCK_CRUSHER:
+ case EL_ROCK_CRUSHER_ATK:
+ case EL_HURRICANE:
+ case EL_HURRICANE_ATK:
+ case EL_TYPOON_MIS:
+ case EL_TYPOON_MIS_ATK:
dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skillid,-1,5);
break;
+ case GN_SLINGITEM_RANGEMELEEATK:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,GN_SLINGITEM,-2,6);
+ break;
+ case EL_STONE_RAIN:
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skillid,-1,(flag&1)?8:5);
+ break;
case WM_SEVERE_RAINSTORM_MELEE:
dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_SEVERE_RAINSTORM,skilllv,5);
break;
@@ -2314,7 +2384,7 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
break;
case AB_DUPLELIGHT_MELEE:
case AB_DUPLELIGHT_MAGIC:
- dmg.amotion = 300;
+ dmg.amotion = 300;/* makes the damage value not overlap with previous damage (when displayed by the client) */
default:
if( flag&SD_ANIMATION && dmg.div_ < 2 ) //Disabling skill animation doesn't works on multi-hit.
type = 5;
@@ -2348,6 +2418,12 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
case WM_SEVERE_RAINSTORM_MELEE:
copy_skill = WM_SEVERE_RAINSTORM;
break;
+ case GN_CRAZYWEED_ATK:
+ copy_skill = GN_CRAZYWEED;
+ break;
+ case GN_HELLS_PLANT_ATK:
+ copy_skill = GN_HELLS_PLANT;
+ break;
case LG_OVERBRAND_BRANDISH:
case LG_OVERBRAND_PLUSATK:
copy_skill = LG_OVERBRAND;
@@ -2446,6 +2522,8 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
case SC_TRIANGLESHOT:
case LG_OVERBRAND:
case SR_KNUCKLEARROW:
+ case GN_WALLOFTHORN:
+ case EL_FIRE_MANTLE:
direction = unit_getdir(bl);// backwards
break;
case WL_CRIMSONROCK:
@@ -3051,6 +3129,10 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data)
case SR_KNUCKLEARROW:
skill_attack(BF_WEAPON, src, src, target, skl->skill_id, skl->skill_lv, tick, skl->flag|SD_LEVEL);
break;
+ case GN_SPORE_EXPLOSION:
+ map_foreachinrange(skill_area_sub, target, skill_get_splash(skl->skill_id, skl->skill_lv), BL_CHAR,
+ src, skl->skill_id, skl->skill_lv, 0, skl->flag|1|BCT_ENEMY, skill_castend_damage_id);
+ break;
default:
skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
break;
@@ -3315,6 +3397,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
case SR_GENTLETOUCH_QUIET:
case WM_SEVERE_RAINSTORM_MELEE:
case WM_GREAT_ECHO:
+ case GN_SLINGITEM_RANGEMELEEATK:
skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
break;
@@ -3538,8 +3621,10 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
case SR_RIDEINLIGHTNING:
case WM_REVERBERATION:
case WM_SOUND_OF_DESTRUCTION:
- if( flag&1 )
- { //Recursive invocation
+ case SO_VARETYR_SPEAR:
+ case GN_CART_TORNADO:
+ case GN_CARTCANNON:
+ if( flag&1 ) {//Recursive invocation
// skill_area_temp[0] holds number of targets in area
// skill_area_temp[1] holds the id of the original target
// skill_area_temp[2] counts how many targets have already been processed
@@ -3550,20 +3635,26 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
sflag |= SD_ANIMATION; // original target gets no animation (as well as all NPC skills)
heal = skill_attack(skill_get_type(skillid), src, src, bl, skillid, skilllv, tick, sflag);
- if( skillid == NPC_VAMPIRE_GIFT && heal > 0 )
- {
+ if( skillid == NPC_VAMPIRE_GIFT && heal > 0 ) {
clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1);
status_heal(src,heal,0,0);
}
- }
- else {
- if( skillid == NJ_BAKUENRYU || skillid == LG_EARTHDRIVE )
- clif_skill_nodamage(src,bl,skillid,skilllv,1);
- else if( skillid == LG_MOONSLASHER )
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
- //FIXME: Isn't EarthQuake a ground skill after all?
- else if( skillid == NPC_EARTHQUAKE )
- skill_addtimerskill(src,tick+250,src->id,0,0,skillid,skilllv,2,flag|BCT_ENEMY|SD_SPLASH|1);
+ } else {
+ switch ( skillid ) {
+ case NJ_BAKUENRYU:
+ case LG_EARTHDRIVE:
+ case GN_CARTCANNON:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case LG_MOONSLASHER:
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ break;
+ case NPC_EARTHQUAKE://FIXME: Isn't EarthQuake a ground skill after all?
+ skill_addtimerskill(src,tick+250,src->id,0,0,skillid,skilllv,2,flag|BCT_ENEMY|SD_SPLASH|1);
+ break;
+ default:
+ break;
+ }
skill_area_temp[0] = 0;
skill_area_temp[1] = bl->id;
@@ -3777,13 +3868,15 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
case NPC_SMOKING:
case GS_FLING:
case NJ_ZENYNAGE:
+ case GN_THORNS_TRAP:
+ case GN_BLOOD_SUCKER:
+ case GN_HELLS_PLANT_ATK:
skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag);
break;
/**
* Rune Knight
**/
- case RK_DRAGONBREATH:
- {
+ case RK_DRAGONBREATH: {
struct status_change *tsc = NULL;
if( (tsc = status_get_sc(bl)) && (tsc->data[SC_HIDING] )) {
clif_skill_nodamage(src,src,skillid,skilllv,1);
@@ -4237,7 +4330,135 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
sc_start(bl,status_skill2sc(skillid),100,skilllv,skill_get_time(skillid,skilllv));
break;
- case 0:
+ case SO_POISON_BUSTER: {
+ struct status_change *tsc = status_get_sc(bl);
+ if( tsc && tsc->data[SC_POISON] ) {
+ skill_attack(skill_get_type(skillid), src, src, bl, skillid, skilllv, tick, flag);
+ status_change_end(bl, SC_POISON, -1);
+ }
+ else if( sd )
+ clif_skill_fail(sd, skillid, 0, 0);
+ }
+ break;
+
+ case GN_SPORE_EXPLOSION:
+ if( flag&1 )
+ skill_attack(skill_get_type(skillid), src, src, bl, skillid, skilllv, tick, flag);
+ else {
+ clif_skill_nodamage(src, bl, skillid, 0, 1);
+ skill_addtimerskill(src, gettick() + skill_get_time(skillid, skilllv) - 1000, bl->id, 0, 0, skillid, skilllv, 0, 0);
+ }
+ break;
+
+ case GN_CRAZYWEED:
+ if( rand()%100 < 75 ) {
+ if( bl->type == BL_SKILL ) {
+ struct skill_unit *su = (struct skill_unit *)bl;
+ if( !su )
+ break;
+ if( skill_get_inf2(su->group->skill_id)&INF2_TRAP ) {// Still need confirm it.
+ skill_delunit(su);
+ break;
+ }
+
+ switch( su->group->skill_id ) {// Unconfirmed list, based on info from irowiki.
+ case GN_WALLOFTHORN:
+ case GN_THORNS_TRAP:
+ case SC_BLOODYLUST:
+ case SC_CHAOSPANIC:
+ case SC_MAELSTROM:
+ case WZ_FIREPILLAR:
+ case SA_LANDPROTECTOR:
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ case MG_SAFETYWALL:
+ case AL_PNEUMA:
+ skill_delunit(su);
+ break;
+ }
+ break;
+ }
+ else
+ skill_attack(BF_WEAPON,src,src,bl,GN_CRAZYWEED_ATK,skilllv,tick,flag);
+ }
+ break;
+
+ case EL_FIRE_BOMB:
+ case EL_FIRE_WAVE:
+ case EL_WATER_SCREW:
+ case EL_HURRICANE:
+ case EL_TYPOON_MIS:
+ if( flag&1 )
+ skill_attack(skill_get_type(skillid+1),src,src,bl,skillid+1,skilllv,tick,flag);
+ else {
+ int i = skill_get_splash(skillid,skilllv);
+ clif_skill_nodamage(src,battle_get_master(src),skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ if( rand()%100 < 30 )
+ map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(skill_get_type(skillid),src,src,bl,skillid,skilllv,tick,flag);
+ }
+ break;
+
+ case EL_ROCK_CRUSHER:
+ clif_skill_nodamage(src,battle_get_master(src),skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ if( rand()%100 < 50 )
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ else
+ skill_attack(BF_WEAPON,src,src,bl,EL_ROCK_CRUSHER_ATK,skilllv,tick,flag);
+ break;
+
+ case EL_STONE_RAIN:
+ if( flag&1 )
+ skill_attack(skill_get_type(skillid),src,src,bl,skillid,skilllv,tick,flag);
+ else {
+ int i = skill_get_splash(skillid,skilllv);
+ clif_skill_nodamage(src,battle_get_master(src),skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ if( rand()%100 < 30 )
+ map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(skill_get_type(skillid),src,src,bl,skillid,skilllv,tick,flag);
+ }
+ break;
+
+ case EL_FIRE_ARROW:
+ case EL_ICE_NEEDLE:
+ case EL_WIND_SLASH:
+ case EL_STONE_HAMMER:
+ clif_skill_nodamage(src,battle_get_master(src),skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ skill_attack(skill_get_type(skillid),src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case EL_TIDAL_WEAPON:
+ if( src->type == BL_ELEM ) {
+ struct elemental_data *ele = BL_CAST(BL_ELEM,src);
+ struct status_change *sc = status_get_sc(&ele->bl);
+ struct status_change *tsc = status_get_sc(bl);
+ sc_type type = status_skill2sc(skillid), type2;
+ type2 = type-1;
+
+ clif_skill_nodamage(src,battle_get_master(src),skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skillid);
+ }
+ if( rand()%100 < 50 )
+ skill_attack(skill_get_type(skillid),src,src,bl,skillid,skilllv,tick,flag);
+ else {
+ sc_start(src,type2,100,skilllv,skill_get_time(skillid,skilllv));
+ sc_start(battle_get_master(src),type,100,ele->bl.id,skill_get_time(skillid,skilllv));
+ }
+ clif_skill_nodamage(src,src,skillid,skilllv,1);
+ }
+ break;
+
+
+ case 0:/* no skill - basic/normal attack */
if(sd) {
if (flag & 3){
if (bl->id != skill_area_temp[1])
@@ -4895,9 +5116,19 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
case SR_GENTLETOUCH_ENERGYGAIN:
case SR_GENTLETOUCH_CHANGE:
case SR_GENTLETOUCH_REVITALIZE:
+ case GN_CARTBOOST:
clif_skill_nodamage(src,bl,skillid,skilllv,
sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)));
break;
+
+ case SO_STRIKING:
+ if (sd) {
+ int bonus = 25 + 10 * skilllv;
+ bonus += (pc_checkskill(sd, SA_FLAMELAUNCHER)+pc_checkskill(sd, SA_FROSTWEAPON)+pc_checkskill(sd, SA_LIGHTNINGLOADER)+pc_checkskill(sd, SA_SEISMICWEAPON))*5;
+ clif_skill_nodamage( src, bl, skillid, skilllv, sc_start2(bl, type, 100, skilllv, bonus, skill_get_time(skillid,skilllv)) );
+ }
+ break;
+
case NPC_STOP:
if( clif_skill_nodamage(src,bl,skillid,skilllv,
sc_start2(bl,type,100,skilllv,src->id,skill_get_time(skillid,skilllv)) ) )
@@ -5204,6 +5435,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
status_change_end(src,SC_OVERHEAT,-1);
break;
case SR_WINDMILL:
+ case GN_CART_TORNADO:
clif_skill_nodamage(src,bl,skillid,skilllv,1);
case SR_EARTHSHAKER:
case NC_INFRAREDSCAN:
@@ -6030,6 +6262,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
break;
case SA_CASTCANCEL:
+ case SO_SPELLFIST:
clif_skill_nodamage(src,bl,skillid,skilllv,1);
unit_skillcastcancel(src,1);
if(sd) {
@@ -6037,6 +6270,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
sp = sp * (90 - (skilllv-1)*20) / 100;
if(sp < 0) sp = 0;
status_zap(src, 0, sp);
+ if( skillid == SO_SPELLFIST )
+ sc_start4(src,type,100,skilllv+1,skilllv,(sd->skillid_old)?sd->skillid_old:MG_FIREBOLT,(sd->skilllv_old)?sd->skilllv_old:skilllv,skill_get_time(skillid,skilllv));
}
break;
case SA_SPELLBREAKER:
@@ -8079,6 +8314,240 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
}
break;
+ case SO_ARRULLO:
+ if( flag&1 )
+ sc_start2(bl, type, 88 + 2 * skilllv, skilllv, 1, skill_get_time(skillid, skilllv));
+ else {
+ clif_skill_nodamage(src, bl, skillid, 0, 1);
+ 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);
+ }
+ break;
+
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ if( sd ) {
+ int elemental_class = skill_get_elemental_type(skillid,skilllv);
+
+ // Remove previous elemental fisrt.
+ if( sd->ed && elemental_delete(sd->ed,0) ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ // Summoning the new one.
+ if( !elemental_create(sd,elemental_class,skill_get_time(skillid,skilllv)) ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SO_EL_CONTROL:
+ if( sd ) {
+ int mode = EL_MODE_PASSIVE; // Standard mode.
+ if( !sd->ed ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if( skilllv == 4 ) {// At level 4 delete elementals.
+ if( elemental_delete(sd->ed, 0) )
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ switch( skilllv ) {// Select mode bassed on skill level used.
+ case 1: mode = EL_MODE_PASSIVE; break;
+ case 2: mode = EL_MODE_ASSIST; break;
+ case 3: mode = EL_MODE_AGGRESSIVE; break;
+ }
+ if( !elemental_change_mode(sd->ed,mode) ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SO_EL_ACTION:
+ if( sd ) {
+ if( !sd->ed )
+ break;
+ elemental_action(sd->ed, bl, tick);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SO_EL_CURE:
+ if( sd ) {
+ struct elemental_data *ed = sd->ed;
+ int s_hp = sd->battle_status.hp * 10 / 100, s_sp = sd->battle_status.sp * 10 / 100;
+ int e_hp, e_sp;
+ if( !ed ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if( !status_charge(&sd->bl,s_hp,s_sp) ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ e_hp = ed->battle_status.max_hp * 10 / 100;
+ e_sp = ed->battle_status.max_sp * 10 / 100;
+ status_heal(&ed->bl,e_hp,e_sp,3);
+ clif_skill_nodamage(src,&ed->bl,skillid,skilllv,1);
+ }
+ break;
+
+ case GN_CHANGEMATERIAL:
+ case SO_EL_ANALYSIS:
+ if( sd ) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_skill_itemlistwindow(sd,skillid,skilllv);
+ }
+ break;
+
+ case GN_BLOOD_SUCKER:
+ if( skill_unitsetting(src, skillid, skilllv, bl->x, bl->y, 0) ) { // Do we need this unit setting? [Xazax]
+ clif_skill_nodamage(src, bl, skillid, skilllv, 1);
+ sc_start2(bl, type, 100, skilllv, src->id, skill_get_time(skillid,skilllv));
+ } else if( sd ) {
+ clif_skill_fail(sd, skillid, 0, 0);
+ break;
+ }
+ break;
+
+ case GN_MANDRAGORA:
+ if( flag&1 ) {
+ if ( clif_skill_nodamage(bl, src, skillid, skilllv,
+ sc_start(bl, type, 35 + 10 * skilllv, skilllv, skill_get_time(skillid, skilllv))) )
+ status_zap(bl, 0, status_get_max_sp(bl) / 100 * 25 + 5 * skilllv);
+ } else
+ 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);
+ break;
+
+ case GN_SLINGITEM:
+ if( sd ) {
+ short ammo_id;
+ i = sd->equip_index[EQI_AMMO];
+ if( i <= 0 )
+ break; // No ammo.
+ ammo_id = sd->inventory_data[i]->nameid;
+ if( ammo_id <= 0 )
+ break;
+ sd->itemid = ammo_id;
+ if( itemdb_is_GNbomb(ammo_id) ) {
+ if(battle_check_target(src,bl,BCT_ENEMY) > 0) {// Only attack if the target is an enemy.
+ if( ammo_id == 13263 )
+ map_foreachincell(skill_area_sub,bl->m,bl->x,bl->y,BL_CHAR,src,GN_SLINGITEM_RANGEMELEEATK,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(BF_WEAPON,src,src,bl,GN_SLINGITEM_RANGEMELEEATK,skilllv,tick,flag);
+ } else //Otherwise, it fails, shows animation and removes items.
+ clif_skill_fail(sd,GN_SLINGITEM_RANGEMELEEATK,0xa,0);
+ } else {
+ struct script_code *script = sd->inventory_data[i]->script;
+ if( !script )
+ break;
+ if( dstsd )
+ run_script(script,0,dstsd->bl.id,fake_nd->bl.id);
+ else
+ run_script(script,0,src->id,0);
+ }
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);// This packet is received twice actually, I think it is to show the animation.
+ break;
+
+ case GN_MIX_COOKING:
+ if( sd ) {
+ clif_cooking_list(sd,27,skillid,(skilllv == 2) ? 10 : 1,6);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case GN_MAKEBOMB:
+ if( sd ) {
+ clif_cooking_list(sd,28,skillid,(skilllv==2) ? 10 : 1,5);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case GN_S_PHARMACY:
+ if( sd ) {
+ sd->skillid_old = skillid;
+ sd->skilllv_old = skilllv;
+ clif_cooking_list(sd,29,skillid,1,6);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case EL_CIRCLE_OF_FIRE:
+ case EL_PYROTECHNIC:
+ case EL_HEATER:
+ case EL_TROPIC:
+ case EL_AQUAPLAY:
+ case EL_COOLER:
+ case EL_CHILLY_AIR:
+ case EL_GUST:
+ case EL_BLAST:
+ case EL_WILD_STORM:
+ case EL_PETROLOGY:
+ case EL_CURSED_SOIL:
+ case EL_UPHEAVAL:
+ case EL_FIRE_CLOAK:
+ case EL_WATER_DROP:
+ case EL_WIND_CURTAIN:
+ case EL_SOLID_SKIN:
+ case EL_STONE_SHIELD:
+ case EL_WIND_STEP: {
+ struct elemental_data *ele = BL_CAST(BL_ELEM, src);
+ if( ele ) {
+ sc_type type2 = type-1;
+ struct status_change *sc = status_get_sc(&ele->bl);
+
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skillid);
+ } else {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ if( skillid == EL_WIND_STEP ) // There aren't telemport, just push to the master.
+ skill_blown(src,bl,skill_get_blewcount(skillid,skilllv),(map_calc_dir(src,bl->x,bl->y)+4)%8,0);
+ sc_start(src,type2,100,skilllv,skill_get_time(skillid,skilllv));
+ sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv));
+ }
+ }
+ }
+ break;
+
+ case EL_FIRE_MANTLE:
+ case EL_WATER_BARRIER:
+ case EL_ZEPHYR:
+ case EL_POWER_OF_GAIA:
+ clif_skill_nodamage(src,src,skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ skill_unitsetting(src,skillid,skilllv,bl->x,bl->y,0);
+ break;
+
+ case EL_WATER_SCREEN: {
+ struct elemental_data *ele = BL_CAST(BL_ELEM, src);
+ if( ele ) {
+ struct status_change *sc = status_get_sc(&ele->bl);
+ sc_type type2 = type-1;
+
+ clif_skill_nodamage(src,src,skillid,skilllv,1);
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skillid);
+ } else {
+ // This not heals at the end.
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ sc_start(src,type2,100,skilllv,skill_get_time(skillid,skilllv));
+ sc_start(bl,type,100,src->id,skill_get_time(skillid,skilllv));
+ }
+ }
+ }
+ break;
+
default:
ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skillid);
clif_skill_nodamage(src,bl,skillid,skilllv,1);
@@ -8142,8 +8611,8 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
return 0;
}
- if(ud->skillid != SA_CASTCANCEL )
- {// otherwise handled in unit_skillcastcancel()
+ if(ud->skillid != SA_CASTCANCEL &&
+ !(ud->skillid == SO_SPELLFIST && (sd && (sd->skillid_old == MG_FIREBOLT || sd->skillid_old == MG_COLDBOLT || sd->skillid_old == MG_LIGHTNINGBOLT))) ) {// otherwise handled in unit_skillcastcancel()
if( ud->skilltimer != tid ) {
ShowError("skill_castend_id: Timer mismatch %d!=%d!\n", ud->skilltimer, tid);
ud->skilltimer = INVALID_TIMER;
@@ -8188,6 +8657,11 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
}
ud->skilltimer=tid;
return skill_castend_pos(tid,tick,id,data);
+ case GN_WALLOFTHORN:
+ ud->skillx = target->x;
+ ud->skilly = target->y;
+ ud->skilltimer = tid;
+ return skill_castend_pos(tid,tick,id,data);
}
if(ud->skillid == RG_BACKSTAP) {
@@ -8736,6 +9210,18 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk
case WM_REVERBERATION:
case WM_SEVERE_RAINSTORM:
case WM_POEMOFNETHERWORLD:
+ case SO_PSYCHIC_WAVE:
+ case SO_VACUUM_EXTREME:
+ case GN_WALLOFTHORN:
+ case GN_THORNS_TRAP:
+ case GN_DEMONIC_FIRE:
+ case GN_HELLS_PLANT:
+ case SO_EARTHGRAVE:
+ case SO_DIAMONDDUST:
+ case SO_FIRE_INSIGNIA:
+ case SO_WATER_INSIGNIA:
+ case SO_WIND_INSIGNIA:
+ case SO_EARTH_INSIGNIA:
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);
@@ -8768,6 +9254,12 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk
map_foreachinarea(skill_graffitiremover,src->m,x-i,y-i,x+i,y+i,BL_SKILL);
break;
+ case SO_CLOUD_KILL:
+ case SO_WARMER:
+ flag|=(skillid == SO_WARMER)?8:4;
+ skill_unitsetting(src,skillid,skilllv,x,y,0);
+ break;
+
case WZ_METEOR: {
int area = skill_get_splash(skillid, skilllv);
short tmpx = 0, tmpy = 0, x1 = 0, y1 = 0;
@@ -9131,6 +9623,55 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk
map_foreachinrange(skill_area_sub, src, skill_get_splash(skillid,skilllv),BL_CHAR, src, skillid, skilllv, tick, flag|BCT_ENEMY, skill_castend_damage_id);
break;
+ case GN_CRAZYWEED:
+ i = skill_get_splash(skillid,skilllv);
+ map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,BL_CHAR|BL_SKILL,
+ src,skillid,skilllv,tick,flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ break;
+
+ case GN_FIRE_EXPANSION: {
+ int i;
+ struct unit_data *ud = unit_bl2ud(src);
+
+ if( !ud ) break;
+
+ for( i = 0; i < MAX_SKILLUNITGROUP && ud->skillunit[i]; i ++ ) {
+ if( ud->skillunit[i]->skill_id == GN_DEMONIC_FIRE &&
+ distance_xy(x, y, ud->skillunit[i]->unit->bl.x, ud->skillunit[i]->unit->bl.y) < 4 ) {
+ switch( skilllv ) {
+ case 3:
+ ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_SMOKE_POWDER;
+ clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_SMOKE_POWDER);
+ break;
+ case 4:
+ ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_TEAR_GAS;
+ clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_TEAR_GAS);
+ break;
+ case 5:
+ map_foreachinarea(skill_area_sub, src->m,
+ ud->skillunit[i]->unit->bl.x - 3, ud->skillunit[i]->unit->bl.y - 3,
+ ud->skillunit[i]->unit->bl.x + 3, ud->skillunit[i]->unit->bl.y + 3, BL_CHAR,
+ src, CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : skilllv, tick, flag|BCT_ENEMY|1|SD_LEVEL, skill_castend_damage_id);
+ skill_delunit(ud->skillunit[i]->unit);
+ break;
+ default:
+ ud->skillunit[i]->unit->val2 = skilllv;
+ ud->skillunit[i]->unit->group->val2 = skilllv;
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case SO_FIREWALK:
+ case SO_ELECTRICWALK:
+ if( sc && sc->data[type] )
+ status_change_end(src,type,-1);
+ clif_skill_nodamage(src, src ,skillid, skilllv,
+ sc_start2(src, type, 100, skillid, skilllv, skill_get_time(skillid, skilllv)));
+ break;
default:
ShowWarning("skill_castend_pos2: Unknown skill used:%d\n",skillid);
@@ -9683,6 +10224,18 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli
if( map_getcell(src->m, x, y, CELL_CHKLANDPROTECTOR) )
return NULL;
break;
+ case SO_CLOUD_KILL:
+ skill_clear_group(src, 4);
+ break;
+ case SO_WARMER:
+ skill_clear_group(src, 8);
+ break;
+
+ case GN_WALLOFTHORN:
+ if( flag&1 )
+ limit = 3000;
+ val3 = (x<<16)|y;
+ break;
}
nullpo_retr(NULL, group=skill_initunitgroup(src,layout->count,skillid,skilllv,skill_get_unit_id(skillid,flag&1)+subunt, limit, interval));
@@ -9784,6 +10337,10 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli
case WM_REVERBERATION:
val1 = 1;
break;
+ case GN_WALLOFTHORN:
+ val1 = 1000 * skilllv; // Need official value. [LimitLine]
+ val2 = src->id;
+ break;
default:
if (group->state.song_dance&0x1)
val2 = unit_flag&(UF_DANCE|UF_SONG); //Store whether this is a song/dance
@@ -10084,7 +10641,10 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns
{
int count=0;
const int x = bl->x, y = bl->y;
-
+
+ if( sg->skill_id == GN_WALLOFTHORN && !map_flag_vs(bl->m) )
+ break;
+
//Take into account these hit more times than the timer interval can handle.
do
skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0);
@@ -10524,6 +11084,86 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns
sc_start(bl, type, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv));
}
break;
+ case UNT_THORNS_TRAP:
+ if( tsc ) {
+ if( !sg->val2 ) {
+ int sec = skill_get_time2(sg->skill_id, sg->skill_lv);
+ if( status_change_start(bl, type, 10000, sg->skill_lv, 0, 0, 0, sec, 0) ) {
+ const struct TimerData* td = tsc->data[type]?get_timer(tsc->data[type]->timer):NULL;
+ if( td )
+ sec = DIFF_TICK(td->tick, tick);
+ map_moveblock(bl, src->bl.x, src->bl.y, tick);
+ clif_fixpos(bl);
+ sg->val2 = bl->id;
+ } else
+ sec = 3000; // Couldn't trap it?
+ sg->limit = DIFF_TICK(tick, sg->tick) + sec;
+ } else if( tsc->data[SC_THORNSTRAP] && bl->id == sg->val2 )
+ skill_attack(skill_get_type(GN_THORNS_TRAP), ss, ss, bl, sg->skill_id, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION);
+ }
+ break;
+
+ case UNT_DEMONIC_FIRE: {
+ TBL_PC* sd = BL_CAST(BL_PC, ss);
+ switch( sg->val2 ) {
+ case 1:
+ case 2:
+ default:
+ sc_start(bl, SC_BURNING, 4 + 4 * sg->skill_lv, sg->skill_lv,
+ skill_get_time2(sg->skill_id, sg->skill_lv));
+ skill_attack(skill_get_type(sg->skill_id), ss, &src->bl, bl,
+ sg->skill_id, sg->skill_lv + 10 * sg->val2, tick, 0);
+ break;
+ case 3:
+ skill_attack(skill_get_type(CR_ACIDDEMONSTRATION), ss, &src->bl, bl,
+ CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : sg->skill_lv, tick, 0);
+ break;
+
+ }
+ }
+ break;
+
+ case UNT_FIRE_EXPANSION_SMOKE_POWDER:
+ sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_SMOKE_POWDER), 100, sg->skill_lv, 1000);
+ break;
+
+ case UNT_FIRE_EXPANSION_TEAR_GAS:
+ sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_TEAR_GAS), 100, sg->skill_lv, 1000);
+ break;
+
+ case UNT_HELLS_PLANT:
+ if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 )
+ skill_attack(skill_get_type(GN_HELLS_PLANT_ATK), ss, &src->bl, bl, GN_HELLS_PLANT_ATK, sg->skill_lv, tick, 0);
+ sg->limit = DIFF_TICK(tick, sg->tick) + 100;
+ break;
+
+ case UNT_CLOUD_KILL:
+ if(tsc && !tsc->data[type])
+ status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),8);
+ skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_WARMER:
+ if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON ) {
+ int hp = 125 * sg->skill_lv; // Officially is 125 * skill_lv.
+ struct status_change *ssc = status_get_sc(ss);
+ if( ssc && ssc->data[SC_HEATER_OPTION] )
+ hp += hp * ssc->data[SC_HEATER_OPTION]->val3 / 100;
+ status_heal(bl, hp, 0, 0);
+ if( tstatus->hp != tstatus->max_hp )
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, hp, 0);
+ sc_start(bl, SC_WARMER, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv));
+ }
+ break;
+
+ case UNT_VACUUM_EXTREME:
+ sc_start(bl, SC_VACUUM_EXTREME, 100, sg->skill_lv, sg->limit);
+ break;
+
+ case UNT_FIRE_MANTLE:
+ if( battle_check_target(&src->bl, bl, BCT_ENEMY) )
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
}
@@ -10639,6 +11279,9 @@ static int skill_unit_onleft (int skill_id, struct block_list *bl, unsigned int
case NJ_SUITON:
case SC_MAELSTROM:
case SC_BLOODYLUST:
+ case EL_WATER_BARRIER:
+ case EL_ZEPHYR:
+ case EL_POWER_OF_GAIA:
if (sce)
status_change_end(bl, type, INVALID_TIMER);
break;
@@ -10968,20 +11611,26 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh
return 1;
}
- if( sd->menuskill_id == AM_PHARMACY )
- {
- switch( skill )
- {
+ switch( sd->menuskill_id ) {
case AM_PHARMACY:
- case AC_MAKINGARROW:
- case BS_REPAIRWEAPON:
- case AM_TWILIGHT1:
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- return 0;
- }
+ switch( skill ) {
+ case AM_PHARMACY:
+ case AC_MAKINGARROW:
+ case BS_REPAIRWEAPON:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ return 0;
+ }
+ break;
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ case GN_CHANGEMATERIAL:
+ if( sd->menuskill_id != skill )
+ return 0;
+ break;
}
-
status = &sd->battle_status;
sc = &sd->sc;
if( !sc->count )
@@ -11041,10 +11690,8 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh
sd->state.arrow_atk = require.ammo?1:0;
// Check the skills that can be used while mounted on a warg
- if( pc_isridingwug(sd) )
- {
- switch( skill )
- {
+ if( pc_isridingwug(sd) ) {
+ switch( skill ) {
case HT_SKIDTRAP: case HT_LANDMINE: case HT_ANKLESNARE: case HT_SHOCKWAVE:
case HT_SANDMAN: case HT_FLASHER: case HT_FREEZINGTRAP: case HT_BLASTMINE:
case HT_CLAYMORETRAP: case HT_SPRINGTRAP: case RA_DETONATOR: case RA_CLUSTERBOMB:
@@ -11057,473 +11704,493 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh
}
// perform skill-specific checks (and actions)
- switch( skill )
- {
- case SA_CASTCANCEL:
- if(sd->ud.skilltimer == INVALID_TIMER) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case AL_WARP:
- if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza]
- clif_displaymessage(sd->fd, "Duel: Can't use warp in duel.");
- return 0;
- }
- break;
- case MO_CALLSPIRITS:
- if(sd->spiritball >= lv) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case MO_FINGEROFFENSIVE:
- case GS_FLING:
- case SR_RAMPAGEBLASTER:
- case SR_RIDEINLIGHTNING:
- if( sd->spiritball > 0 && sd->spiritball < require.spiritball )
- sd->spiritball_old = require.spiritball = sd->spiritball;
- else
- sd->spiritball_old = require.spiritball;
- break;
- case MO_CHAINCOMBO:
- if(!sc)
- return 0;
- if(sc->data[SC_BLADESTOP])
+ switch( skill ) {
+ case SA_CASTCANCEL:
+ case SO_SPELLFIST:
+ if(sd->ud.skilltimer == INVALID_TIMER) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
break;
- if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_TRIPLEATTACK)
+ case AL_WARP:
+ if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza]
+ clif_displaymessage(sd->fd, "Duel: Can't use warp in duel.");
+ return 0;
+ }
break;
- return 0;
- case MO_COMBOFINISH:
- if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_CHAINCOMBO))
- return 0;
- break;
- case CH_TIGERFIST:
- if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_COMBOFINISH))
- return 0;
- break;
- case CH_CHAINCRUSH:
- if(!(sc && sc->data[SC_COMBO]))
- return 0;
- if(sc->data[SC_COMBO]->val1 != MO_COMBOFINISH && sc->data[SC_COMBO]->val1 != CH_TIGERFIST)
+ case MO_CALLSPIRITS:
+ if(sd->spiritball >= lv) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case MO_FINGEROFFENSIVE:
+ case GS_FLING:
+ case SR_RAMPAGEBLASTER:
+ case SR_RIDEINLIGHTNING:
+ if( sd->spiritball > 0 && sd->spiritball < require.spiritball )
+ sd->spiritball_old = require.spiritball = sd->spiritball;
+ else
+ sd->spiritball_old = require.spiritball;
+ break;
+ case MO_CHAINCOMBO:
+ if(!sc)
+ return 0;
+ if(sc->data[SC_BLADESTOP])
+ break;
+ if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_TRIPLEATTACK)
+ break;
return 0;
- break;
- case MO_EXTREMITYFIST:
-// if(sc && sc->data[SC_EXTREMITYFIST]) //To disable Asura during the 5 min skill block uncomment this...
-// return 0;
- if( sc && sc->data[SC_BLADESTOP] )
+ case MO_COMBOFINISH:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_CHAINCOMBO))
+ return 0;
break;
- if( sc && sc->data[SC_COMBO] )
- {
- switch(sc->data[SC_COMBO]->val1) {
- case MO_COMBOFINISH:
- case CH_TIGERFIST:
- case CH_CHAINCRUSH:
+ case CH_TIGERFIST:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_COMBOFINISH))
+ return 0;
+ break;
+ case CH_CHAINCRUSH:
+ if(!(sc && sc->data[SC_COMBO]))
+ return 0;
+ if(sc->data[SC_COMBO]->val1 != MO_COMBOFINISH && sc->data[SC_COMBO]->val1 != CH_TIGERFIST)
+ return 0;
+ break;
+ case MO_EXTREMITYFIST:
+ // if(sc && sc->data[SC_EXTREMITYFIST]) //To disable Asura during the 5 min skill block uncomment this...
+ // return 0;
+ if( sc && sc->data[SC_BLADESTOP] )
+ break;
+ if( sc && sc->data[SC_COMBO] )
+ {
+ switch(sc->data[SC_COMBO]->val1) {
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ break;
+ default:
+ return 0;
+ }
+ }
+ else if( !unit_can_move(&sd->bl) )
+ { //Placed here as ST_MOVE_ENABLE should not apply if rooted or on a combo. [Skotlex]
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case TK_MISSION:
+ if( (sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON )
+ {// Cannot be used by Non-Taekwon classes
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case TK_READYCOUNTER:
+ case TK_READYDOWN:
+ case TK_READYSTORM:
+ case TK_READYTURN:
+ case TK_JUMPKICK:
+ if( (sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER )
+ {// Soul Linkers cannot use this skill
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case TK_TURNKICK:
+ case TK_STORMKICK:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER)
+ return 0; //Anti-Soul Linker check in case you job-changed with Stances active.
+ if(!(sc && sc->data[SC_COMBO]) || sc->data[SC_COMBO]->val1 == TK_JUMPKICK)
+ return 0; //Combo needs to be ready
+
+ if (sc->data[SC_COMBO]->val3) { //Kick chain
+ //Do not repeat a kick.
+ if (sc->data[SC_COMBO]->val3 != skill)
break;
- default:
+ status_change_end(&sd->bl, SC_COMBO, INVALID_TIMER);
+ return 0;
+ }
+ if(sc->data[SC_COMBO]->val1 != skill && !( sd && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON) )) { //Cancel combo wait.
+ unit_cancel_combo(&sd->bl);
+ return 0;
+ }
+ break; //Combo ready.
+ case BD_ADAPTATION:
+ {
+ int time;
+ if(!(sc && sc->data[SC_DANCING]))
+ {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ time = 1000*(sc->data[SC_DANCING]->val3>>16);
+ if (skill_get_time(
+ (sc->data[SC_DANCING]->val1&0xFFFF), //Dance Skill ID
+ (sc->data[SC_DANCING]->val1>>16)) //Dance Skill LV
+ - time < skill_get_time2(skill,lv))
+ {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
return 0;
+ }
}
- }
- else if( !unit_can_move(&sd->bl) )
- { //Placed here as ST_MOVE_ENABLE should not apply if rooted or on a combo. [Skotlex]
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
+ break;
- case TK_MISSION:
- if( (sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON )
- {// Cannot be used by Non-Taekwon classes
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
+ case PR_BENEDICTIO:
+ if (skill_check_pc_partner(sd, skill, &lv, 1, 0) < 2)
+ {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
- case TK_READYCOUNTER:
- case TK_READYDOWN:
- case TK_READYSTORM:
- case TK_READYTURN:
- case TK_JUMPKICK:
- if( (sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER )
- {// Soul Linkers cannot use this skill
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
+ case SL_SMA:
+ if(!(sc && sc->data[SC_SMA]))
+ return 0;
+ break;
- case TK_TURNKICK:
- case TK_STORMKICK:
- case TK_DOWNKICK:
- case TK_COUNTER:
- if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER)
- return 0; //Anti-Soul Linker check in case you job-changed with Stances active.
- if(!(sc && sc->data[SC_COMBO]) || sc->data[SC_COMBO]->val1 == TK_JUMPKICK)
- return 0; //Combo needs to be ready
+ case HT_POWER:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill))
+ return 0;
+ break;
- if (sc->data[SC_COMBO]->val3) { //Kick chain
- //Do not repeat a kick.
- if (sc->data[SC_COMBO]->val3 != skill)
- break;
- status_change_end(&sd->bl, SC_COMBO, INVALID_TIMER);
- return 0;
- }
- if(sc->data[SC_COMBO]->val1 != skill && !( sd && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON) )) { //Cancel combo wait.
- unit_cancel_combo(&sd->bl);
- return 0;
- }
- break; //Combo ready.
- case BD_ADAPTATION:
- {
- int time;
- if(!(sc && sc->data[SC_DANCING]))
+ case CG_HERMODE:
+ if(!npc_check_areanpc(1,sd->bl.m,sd->bl.x,sd->bl.y,skill_get_splash(skill, lv)))
{
clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
return 0;
}
- time = 1000*(sc->data[SC_DANCING]->val3>>16);
- if (skill_get_time(
- (sc->data[SC_DANCING]->val1&0xFFFF), //Dance Skill ID
- (sc->data[SC_DANCING]->val1>>16)) //Dance Skill LV
- - time < skill_get_time2(skill,lv))
+ break;
+ case CG_MOONLIT: //Check there's no wall in the range+1 area around the caster. [Skotlex]
+ {
+ int i,x,y,range = skill_get_splash(skill, lv)+1;
+ int size = range*2+1;
+ for (i=0;i<size*size;i++) {
+ x = sd->bl.x+(i%size-range);
+ y = sd->bl.y+(i/size-range);
+ if (map_getcell(sd->bl.m,x,y,CELL_CHKWALL)) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case PR_REDEMPTIO:
+ {
+ int exp;
+ if( ((exp = pc_nextbaseexp(sd)) > 0 && get_percentage(sd->status.base_exp, exp) < 1) ||
+ ((exp = pc_nextjobexp(sd)) > 0 && get_percentage(sd->status.job_exp, exp) < 1)) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0); //Not enough exp.
+ return 0;
+ }
+ break;
+ }
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ if (!party_skill_check(sd, sd->status.party_id, skill, lv))
{
clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
return 0;
}
- }
- break;
-
- case PR_BENEDICTIO:
- if (skill_check_pc_partner(sd, skill, &lv, 1, 0) < 2)
- {
+ break;
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ if (sc && sc->data[SC_MIRACLE])
+ break;
+ i = skill-SG_SUN_WARM;
+ if (sd->bl.m == sd->feel_map[i].m)
+ break;
clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
return 0;
- }
- break;
-
- case SL_SMA:
- if(!(sc && sc->data[SC_SMA]))
+ break;
+ case SG_SUN_COMFORT:
+ case SG_MOON_COMFORT:
+ case SG_STAR_COMFORT:
+ if (sc && sc->data[SC_MIRACLE])
+ break;
+ i = skill-SG_SUN_COMFORT;
+ if (sd->bl.m == sd->feel_map[i].m &&
+ (battle_config.allow_skill_without_day || sg_info[i].day_func()))
+ break;
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
return 0;
- break;
-
- case HT_POWER:
- if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill))
+ case SG_FUSION:
+ if (sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_STAR)
+ break;
+ //Auron insists we should implement SP consumption when you are not Soul Linked. [Skotlex]
+ //Only invoke on skill begin cast (instant cast skill). [Kevin]
+ if( require.sp > 0 )
+ {
+ if (status->sp < (unsigned int)require.sp)
+ clif_skill_fail(sd,skill,USESKILL_FAIL_SP_INSUFFICIENT,0);
+ else
+ status_zap(&sd->bl, 0, require.sp);
+ }
return 0;
- break;
+ case GD_BATTLEORDER:
+ case GD_REGENERATION:
+ case GD_RESTORE:
+ if (!map_flag_gvg2(sd->bl.m)) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case GD_EMERGENCYCALL:
+ // other checks were already done in skillnotok()
+ if (!sd->status.guild_id || !sd->state.gmaster_flag)
+ return 0;
+ break;
- case CG_HERMODE:
- if(!npc_check_areanpc(1,sd->bl.m,sd->bl.x,sd->bl.y,skill_get_splash(skill, lv)))
- {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case CG_MOONLIT: //Check there's no wall in the range+1 area around the caster. [Skotlex]
- {
- int i,x,y,range = skill_get_splash(skill, lv)+1;
- int size = range*2+1;
- for (i=0;i<size*size;i++) {
- x = sd->bl.x+(i%size-range);
- y = sd->bl.y+(i/size-range);
- if (map_getcell(sd->bl.m,x,y,CELL_CHKWALL)) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ case GS_GLITTERING:
+ if(sd->spiritball >= 10) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case NJ_ISSEN:
+ if (status->hp < 2) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case NJ_BUNSINJYUTSU:
+ if (!(sc && sc->data[SC_NEN])) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case NJ_ZENYNAGE:
+ if(sd->status.zeny < require.zeny) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_MONEY,0);
+ return 0;
+ }
+ break;
+ case PF_HPCONVERSION:
+ if (status->sp == status->max_sp)
+ return 0; //Unusable when at full SP.
+ break;
+ case AM_CALLHOMUN: //Can't summon if a hom is already out
+ if (sd->status.hom_id && sd->hd && !sd->hd->homunculus.vaporize) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case AM_REST: //Can't vapo homun if you don't have an active homunc or it's hp is < 80%
+ if (!merc_is_hom_active(sd->hd) || sd->hd->battle_status.hp < (sd->hd->battle_status.max_hp*80/100))
+ {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ 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, USESKILL_FAIL_ANCILLA_NUMOVER, 0);
return 0;
}
}
- }
- break;
- case PR_REDEMPTIO:
- {
- int exp;
- if( ((exp = pc_nextbaseexp(sd)) > 0 && get_percentage(sd->status.base_exp, exp) < 1) ||
- ((exp = pc_nextjobexp(sd)) > 0 && get_percentage(sd->status.job_exp, exp) < 1)) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0); //Not enough exp.
+ 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,USESKILL_FAIL_LEVEL,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,USESKILL_FAIL_NEED_ITEM,require.amount[0],require.itemid[0]);
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
return 0;
}
break;
- }
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- if (!party_skill_check(sd, sd->status.party_id, skill, lv))
- {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case SG_SUN_WARM:
- case SG_MOON_WARM:
- case SG_STAR_WARM:
- if (sc && sc->data[SC_MIRACLE])
+ 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,USESKILL_FAIL_SUMMON,0);
+ return 0;
+ }
+ }
break;
- i = skill-SG_SUN_WARM;
- if (sd->bl.m == sd->feel_map[i].m)
+ /**
+ * Guilotine Cross
+ **/
+ case GC_HALLUCINATIONWALK:
+ if( sc && (sc->data[SC_HALLUCINATIONWALK] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY]) ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
break;
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- break;
- case SG_SUN_COMFORT:
- case SG_MOON_COMFORT:
- case SG_STAR_COMFORT:
- if (sc && sc->data[SC_MIRACLE])
+ case GC_COUNTERSLASH:
+ case GC_WEAPONCRUSH:
+ if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == GC_WEAPONBLOCKING) ) {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_GC_WEAPONBLOCKING, 0);
+ return 0;
+ }
break;
- i = skill-SG_SUN_COMFORT;
- if (sd->bl.m == sd->feel_map[i].m &&
- (battle_config.allow_skill_without_day || sg_info[i].day_func()))
+ case GC_CROSSRIPPERSLASHER:
+ if( !(sc && sc->data[SC_ROLLINGCUTTER]) ) {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_CONDITION, 0);
+ return 0;
+ }
break;
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- case SG_FUSION:
- if (sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_STAR)
+ case GC_POISONSMOKE:
+ case GC_VENOMPRESSURE:
+ if( !(sc && sc->data[SC_POISONINGWEAPON]) ) {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_GC_POISONINGWEAPON, 0);
+ return 0;
+ }
break;
- //Auron insists we should implement SP consumption when you are not Soul Linked. [Skotlex]
- //Only invoke on skill begin cast (instant cast skill). [Kevin]
- if( require.sp > 0 )
- {
- if (status->sp < (unsigned int)require.sp)
- clif_skill_fail(sd,skill,USESKILL_FAIL_SP_INSUFFICIENT,0);
- else
- status_zap(&sd->bl, 0, require.sp);
- }
- return 0;
- case GD_BATTLEORDER:
- case GD_REGENERATION:
- case GD_RESTORE:
- if (!map_flag_gvg2(sd->bl.m)) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- case GD_EMERGENCYCALL:
- // other checks were already done in skillnotok()
- if (!sd->status.guild_id || !sd->state.gmaster_flag)
- return 0;
- break;
-
- case GS_GLITTERING:
- if(sd->spiritball >= 10) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
-
- case NJ_ISSEN:
- if (status->hp < 2) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- case NJ_BUNSINJYUTSU:
- if (!(sc && sc->data[SC_NEN])) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
-
- case NJ_ZENYNAGE:
- if(sd->status.zeny < require.zeny) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_MONEY,0);
- return 0;
- }
- break;
- case PF_HPCONVERSION:
- if (status->sp == status->max_sp)
- return 0; //Unusable when at full SP.
- break;
- case AM_CALLHOMUN: //Can't summon if a hom is already out
- if (sd->status.hom_id && sd->hd && !sd->hd->homunculus.vaporize) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case AM_REST: //Can't vapo homun if you don't have an active homunc or it's hp is < 80%
- if (!merc_is_hom_active(sd->hd) || sd->hd->battle_status.hp < (sd->hd->battle_status.max_hp*80/100))
- {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- 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, USESKILL_FAIL_ANCILLA_NUMOVER, 0);
+ /**
+ * Ranger
+ **/
+ case RA_SENSITIVEKEEN:
+ if(!pc_iswug(sd)) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_CONDITION,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,USESKILL_FAIL_LEVEL,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,USESKILL_FAIL_NEED_ITEM,require.amount[0],require.itemid[0]);
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,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,USESKILL_FAIL_SUMMON,0);
+ break;
+ case RA_WUGMASTERY:
+ if( pc_isfalcon(sd) || pc_isridingwug(sd) || sd->sc.data[SC__GROOMY]) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RA_WUGRIDER:
+ if( !pc_isridingwug(sd) && !pc_iswug(sd) ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RA_WUGDASH:
+ if(!pc_isridingwug(sd)) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Royal Guard
+ **/
+ case LG_BANDING:
+ if( sc && sc->data[SC_INSPIRATION] ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case LG_PRESTIGE:
+ if( sc && (sc->data[SC_BANDING] || sc->data[SC_INSPIRATION]) ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case LG_RAGEBURST:
+ if( sd->spiritball == 0 ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_SKILLINTERVAL,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,USESKILL_FAIL_LEVEL,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, USESKILL_FAIL_GC_WEAPONBLOCKING, 0);
- return 0;
- }
- break;
- case GC_CROSSRIPPERSLASHER:
- if( !(sc && sc->data[SC_ROLLINGCUTTER]) ) {
- clif_skill_fail(sd, skill, USESKILL_FAIL_CONDITION, 0);
- return 0;
- }
- break;
- case GC_POISONSMOKE:
- case GC_VENOMPRESSURE:
- if( !(sc && sc->data[SC_POISONINGWEAPON]) ) {
- clif_skill_fail(sd, skill, USESKILL_FAIL_GC_POISONINGWEAPON, 0);
- return 0;
- }
- break;
- /**
- * Ranger
- **/
- case RA_SENSITIVEKEEN:
- if(!pc_iswug(sd)) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_CONDITION,0);
- return 0;
- }
- break;
- case RA_WUGMASTERY:
- if( pc_isfalcon(sd) || pc_isridingwug(sd) || sd->sc.data[SC__GROOMY]) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case RA_WUGRIDER:
- if( !pc_isridingwug(sd) && !pc_iswug(sd) ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case RA_WUGDASH:
- if(!pc_isridingwug(sd)) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- /**
- * Royal Guard
- **/
- case LG_BANDING:
- if( sc && sc->data[SC_INSPIRATION] ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case LG_PRESTIGE:
- if( sc && (sc->data[SC_BANDING] || sc->data[SC_INSPIRATION]) ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case LG_RAGEBURST:
- if( sd->spiritball == 0 ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_SKILLINTERVAL,0);
- return 0;
- }
- sd->spiritball_old = require.spiritball = sd->spiritball;
- break;
- case LG_RAYOFGENESIS:
- if( sc && sc->data[SC_INSPIRATION] )
- return 1; // Don't check for partner.
- if( !(sc && sc->data[SC_BANDING]) ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL,0);
- return 0;
- } else if( skill_check_pc_partner(sd,skill,&lv,skill_get_range(skill,lv),0) < 1 )
- return 0; // Just fails, no msg here.
- break;
- case LG_HESPERUSLIT:
- if( !sc || !sc->data[SC_BANDING] ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case SR_FALLENEMPIRE:
- if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_DRAGONCOMBO) )
- return 0;
- break;
- case SR_CRESCENTELBOW:
- if( sc && sc->data[SC_CRESCENTELBOW] ) {
- clif_skill_fail(sd,skill,0,0);
- return 0;
- }
- break;
- case SR_CURSEDCIRCLE:
- if( sd->spiritball > 0 )
sd->spiritball_old = require.spiritball = sd->spiritball;
- else {
- clif_skill_fail(sd,skill,0,0);
- return 0;
- }
- break;
- case SR_GATEOFHELL:
- if( sd->spiritball > 0 )
- sd->spiritball_old = require.spiritball;
- break;
- case SC_MANHOLE:
- case SC_DIMENSIONDOOR:
- if( sc && sc->data[SC_MAGNETICFIELD] ) {
- clif_skill_fail(sd,skill,0,0);
- return 0;
- }
- break;
- case WM_GREAT_ECHO: {
- int count;
- count = skill_check_pc_partner(sd, skill, &lv, skill_get_splash(skill,lv), 0);
- if( count < 1 ) {
- clif_skill_fail(sd,skill,0x11,0);
+ break;
+ case LG_RAYOFGENESIS:
+ if( sc && sc->data[SC_INSPIRATION] )
+ return 1; // Don't check for partner.
+ if( !(sc && sc->data[SC_BANDING]) ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL,0);
return 0;
- } else
- require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party.
- }
- break;
+ } else if( skill_check_pc_partner(sd,skill,&lv,skill_get_range(skill,lv),0) < 1 )
+ return 0; // Just fails, no msg here.
+ break;
+ case LG_HESPERUSLIT:
+ if( !sc || !sc->data[SC_BANDING] ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case SR_FALLENEMPIRE:
+ if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_DRAGONCOMBO) )
+ return 0;
+ break;
+ case SR_CRESCENTELBOW:
+ if( sc && sc->data[SC_CRESCENTELBOW] ) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case SR_CURSEDCIRCLE:
+ if( sd->spiritball > 0 )
+ sd->spiritball_old = require.spiritball = sd->spiritball;
+ else {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case SR_GATEOFHELL:
+ if( sd->spiritball > 0 )
+ sd->spiritball_old = require.spiritball;
+ break;
+ case SC_MANHOLE:
+ case SC_DIMENSIONDOOR:
+ if( sc && sc->data[SC_MAGNETICFIELD] ) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case WM_GREAT_ECHO: {
+ int count;
+ count = skill_check_pc_partner(sd, skill, &lv, skill_get_splash(skill,lv), 0);
+ if( count < 1 ) {
+ clif_skill_fail(sd,skill,0x11,0);
+ return 0;
+ } else
+ require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party.
+ }
+ break;
+ case SO_FIREWALK:
+ case SO_ELECTRICWALK: // Can't be casted until you've walked all cells.
+ if( sc && sc->data[SC_PROPERTYWALK] &&
+ sc->data[SC_PROPERTYWALK]->val3 < skill_get_maxcount(sc->data[SC_PROPERTYWALK]->val1,sc->data[SC_PROPERTYWALK]->val2) ) {
+ clif_skill_fail(sd,skill,0x0,0);
+ return 0;
+ }
+ break;
+ case SO_EL_CONTROL:
+ if( !sd->status.ele_id || !sd->ed ) {
+ clif_skill_fail(sd,skill,0x00,0);
+ return 0;
+ }
+ break;
+ case RETURN_TO_ELDICASTES:
+ if( sd->sc.option&OPTION_MADOGEAR ) { //Cannot be used if Mado is equipped.
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
}
switch(require.state) {
@@ -11647,12 +12314,12 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh
/**
* Sorcerer
**/
- //case ST_ELEMENTALSPIRIT:
- // if(!sd->ed) {
- // clif_skill_fail(sd,skill,USESKILL_FAIL_EL_SUMMON,0);
- // return 0;
- // }
- // break;
+ case ST_ELEMENTALSPIRIT:
+ if(!sd->ed) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_EL_SUMMON,0);
+ return 0;
+ }
+ break;
}
if(require.mhp > 0 && get_percentage(status->hp, status->max_hp) > require.mhp) {
@@ -11704,18 +12371,25 @@ int skill_check_condition_castend(struct map_session_data* sd, short skill, shor
return 1;
}
- if( sd->menuskill_id == AM_PHARMACY )
- { // Cast start or cast end??
- switch( skill )
- {
+ switch( sd->menuskill_id ) { // Cast start or cast end??
case AM_PHARMACY:
- case AC_MAKINGARROW:
- case BS_REPAIRWEAPON:
- case AM_TWILIGHT1:
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- return 0;
- }
+ switch( skill ) {
+ case AM_PHARMACY:
+ case AC_MAKINGARROW:
+ case BS_REPAIRWEAPON:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ return 0;
+ }
+ break;
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ case GN_CHANGEMATERIAL:
+ if( sd->menuskill_id != skill )
+ return 0;
+ break;
}
if( sd->skillitem == skill ) // Casting finished (Item skill or Hocus-Pocus)
@@ -11994,6 +12668,17 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, short
if( itemid_isgemstone(skill_db[j].itemid[i]) && skill_check_pc_partner(sd,skill,&lv, 1, 0) )
continue;
break;
+ case GN_FIRE_EXPANSION:
+ if( i < 5 )
+ continue;
+ break;
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ if( i < 3 )
+ continue;
+ break;
}
req.itemid[i] = skill_db[j].itemid[i];
@@ -12083,6 +12768,12 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, short
if( sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_FALLENEMPIRE )
req.sp -= req.sp * 10 / 100;
break;
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ req.sp -= req.sp * (5 + 5 * pc_checkskill(sd,SO_EL_SYMPATHY)) / 100;
+ break;
}
return req;
@@ -12837,6 +13528,14 @@ int skill_clear_group (struct block_list *bl, int flag)
if (flag&1)
group[count++]= ud->skillunit[i];
break;
+ case SO_CLOUD_KILL:
+ if( flag&4 )
+ group[count++]= ud->skillunit[i];
+ break;
+ case SO_WARMER:
+ if( flag&8 )
+ group[count++]= ud->skillunit[i];
+ break;
default:
if (flag&2 && skill_get_inf2(ud->skillunit[i]->skill_id)&INF2_TRAP)
group[count++]= ud->skillunit[i];
@@ -12866,6 +13565,8 @@ struct skill_unit_group *skill_locate_element_field(struct block_list *bl)
case SA_VIOLENTGALE:
case SA_LANDPROTECTOR:
case NJ_SUITON:
+ case SO_WARMER:
+ case SO_CLOUD_KILL:
return ud->skillunit[i];
}
}
@@ -13906,7 +14607,7 @@ int skill_unit_move_sub (struct block_list* bl, va_list ap)
if( !unit->alive || target->prev == NULL )
return 0;
- if( unit->group->skill_id == PF_SPIDERWEB && flag&1 )
+ if( flag&1 && ( unit->group->skill_id == PF_SPIDERWEB || unit->group->skill_id == GN_THORNS_TRAP ) )
return 0; // Fiberlock is never supposed to trigger on skill_unit_move. [Inkfish]
dissonance = skill_dance_switch(unit, 0);
@@ -14182,7 +14883,7 @@ int skill_can_produce_mix (struct map_session_data *sd, int nameid, int trigger,
int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, int slot1, int slot2, int slot3, int qty)
{
int slot[3];
- int i,sc,ele,idx,equip,wlv,make_per,flag;
+ int i,sc,ele,idx,equip,wlv,make_per,flag = 0, firstQty = qty;
int num = -1; // exclude the recipe
struct status_data *status;
@@ -14267,7 +14968,7 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
}while( j>=0 && x>0 );
}
- if((equip=itemdb_isequip(nameid)))
+ if( (equip = (itemdb_isequip(nameid) && skill_id != GN_CHANGEMATERIAL && skill_id != GN_MAKEBOMB )) )
wlv = itemdb_wlv(nameid);
if(!equip) {
switch(skill_id){
@@ -14363,6 +15064,57 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
make_per = 3000 + 500 * pc_checkskill(sd,GC_RESEARCHNEWPOISON);
qty = 1+rnd()%pc_checkskill(sd,GC_RESEARCHNEWPOISON);
break;
+ case GN_MIX_COOKING:
+ make_per = 3000; //As I can see this is not affectd by dex or int
+ break;
+ case GN_MAKEBOMB:
+ // TODO: find a proper chance.
+ make_per = (5000 + 50*status->dex + 30*status->luk); //Custom rate value.
+ break;
+ case GN_CHANGEMATERIAL:
+ switch( nameid ) {
+ case 1010:
+ qty *= 8;
+ break;
+ case 1061:
+ qty *= 2;
+ break;
+ // Throwable potions
+ case 13275:
+ case 13276:
+ case 13277:
+ case 13278:
+ case 13279:
+ case 13280:
+ case 13281:
+ case 13282:
+ case 13283:
+ qty *= 10;
+ break;
+ }
+ make_per = 100000; //100% success rate.
+ break;
+ case GN_S_PHARMACY:
+ // Note: This is not the chosen skill level but the highest available. Need confirmation/fix.
+ switch( sd->skilllv_old ) {
+ case 6:
+ case 7:
+ case 8:
+ qty = 3;
+ break; //3 items to make at once.
+ case 9:
+ qty = 3 + rand()%3;
+ break; //3~5 items to make at once.
+ case 10:
+ qty = 4 + rand()%3;
+ break; //4~6 items to make at once.
+ default:
+ qty = 2;
+ break; //2 item to make at once.
+ }
+ make_per = 100000; //100% success rate.
+ sd->skillid_old = sd->skilllv_old = 0;
+ break;
default:
if (sd->menuskill_id == AM_PHARMACY &&
sd->menuskill_val > 10 && sd->menuskill_val <= 20)
@@ -14429,6 +15181,9 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
case AM_TWILIGHT1:
case AM_TWILIGHT2:
case AM_TWILIGHT3:
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
flag = battle_config.produce_item_name_input&0x2;
break;
case AL_HOLYWATER:
@@ -14465,36 +15220,43 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
} else {
int fame = 0;
tmp_item.amount = 0;
- for (i=0; i< qty; i++)
- { //Apply quantity modifiers.
- if (rnd()%10000 < make_per || qty == 1)
- { //Success
- tmp_item.amount++;
- if(nameid < 545 || nameid > 547)
- continue;
- if(skill_id != AM_PHARMACY &&
- skill_id != AM_TWILIGHT1 &&
- skill_id != AM_TWILIGHT2 &&
- skill_id != AM_TWILIGHT3)
- continue;
- //Add fame as needed.
- switch(++sd->potion_success_counter) {
- case 3:
- fame+=1; // Success to prepare 3 Condensed Potions in a row
- break;
- case 5:
- fame+=3; // Success to prepare 5 Condensed Potions in a row
- break;
- case 7:
- fame+=10; // Success to prepare 7 Condensed Potions in a row
- break;
- case 10:
- fame+=50; // Success to prepare 10 Condensed Potions in a row
- sd->potion_success_counter = 0;
- break;
- }
- } else //Failure
- sd->potion_success_counter = 0;
+ if( skill_id == GN_MIX_COOKING && firstQty > 1 ) {// Mix Cooking level 2.
+ // Success. As I see the chance as level 2 is global, not indiviual.
+ if( rand()%10000 < make_per )
+ tmp_item.amount = 5 + rand()%5;
+ } else {
+ for (i=0; i< qty; i++) { //Apply quantity modifiers.
+ if (rnd()%10000 < make_per || qty == 1) { //Success
+ tmp_item.amount++;
+ if(nameid < 545 || nameid > 547)
+ continue;
+ if( skill_id != AM_PHARMACY &&
+ skill_id != AM_TWILIGHT1 &&
+ skill_id != AM_TWILIGHT2 &&
+ skill_id != AM_TWILIGHT3 &&
+ skill_id != GN_MIX_COOKING &&
+ skill_id != GN_MAKEBOMB &&
+ skill_id != GN_S_PHARMACY )
+ continue;
+ //Add fame as needed.
+ switch(++sd->potion_success_counter) {
+ case 3:
+ fame+=1; // Success to prepare 3 Condensed Potions in a row
+ break;
+ case 5:
+ fame+=3; // Success to prepare 5 Condensed Potions in a row
+ break;
+ case 7:
+ fame+=10; // Success to prepare 7 Condensed Potions in a row
+ break;
+ case 10:
+ fame+=50; // Success to prepare 10 Condensed Potions in a row
+ sd->potion_success_counter = 0;
+ break;
+ }
+ } else //Failure
+ sd->potion_success_counter = 0;
+ }
}
if (fame)
pc_addfame(sd,fame);
@@ -14519,6 +15281,12 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
clif_produceeffect(sd,2,nameid);
clif_misceffect(&sd->bl,5);
break;
+ case GN_MAKEBOMB:
+ case GN_MIX_COOKING:
+ clif_msg_skill(sd,skill_id,0x627);
+ break;
+ case GN_S_PHARMACY:
+ break; // No effects here.
default: //Those that don't require a skill?
if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20)
{ //Cooking items.
@@ -14568,6 +15336,30 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
clif_produceeffect(sd,3,nameid);
clif_misceffect(&sd->bl,6);
break;
+ case GN_MIX_COOKING: {
+ struct item tmp_item;
+ const int products[5][2] = {{13265,6500},{13266,4000},{13267,3000},{13268,500},{12435,500}};
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = nameid;
+ do {
+ i = rand()%5;
+ tmp_item.nameid = products[i][0];
+ }
+ while( rand()%10000 >= products[i][1] );
+ tmp_item.amount = (firstQty > 1 )? 5 + rand()%5 : 1; // When it fails it gives a random amount of items.
+ tmp_item.identify = 1;
+ if( pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE) ) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ clif_msg_skill(sd,skill_id,0x628);
+ }
+ break;
+ case GN_S_PHARMACY:
+ break; // No effects here.
+ case GN_MAKEBOMB:
+ clif_msg_skill(sd,skill_id,0x628);
+ break;
default:
if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20 )
{ //Cooking items.
@@ -14785,6 +15577,114 @@ int skill_select_menu(struct map_session_data *sd,int flag,int skill_id) {
sc_start4(&sd->bl,SC__AUTOSHADOWSPELL,100,id,lv,prob,0,skill_get_time(SC_AUTOSHADOWSPELL,aslvl));
return 0;
}
+int skill_elementalanalysis(struct map_session_data* sd, int n, int skill_lv, unsigned short* item_list) {
+ int i;
+
+ nullpo_ret(sd);
+ nullpo_ret(item_list);
+
+ if( n <= 0 )
+ return 1;
+
+ for( i = 0; i < n; i++ ) {
+ int nameid, add_amount, del_amount, idx, product, flag;
+ struct item tmp_item;
+
+ idx = item_list[i*2+0]-2;
+ del_amount = item_list[i*2+1];
+
+ if( skill_lv == 2 )
+ del_amount -= (del_amount % 10);
+ add_amount = (skill_lv == 1) ? del_amount * (5 + rand()%5) : del_amount / 10 ;
+
+ if( (nameid = sd->status.inventory[idx].nameid) <= 0 || del_amount > sd->status.inventory[idx].amount ) {
+ clif_skill_fail(sd,SO_EL_ANALYSIS,0,0);
+ return 1;
+ }
+
+ switch( nameid ) {
+ // Level 1
+ case 994: product = 990; break; // Flame Heart -> Red Blood.
+ case 995: product = 991; break; // Mystic Frozen -> Crystal Blue.
+ case 996: product = 992; break; // Rough Wind -> Wind of Verdure.
+ case 997: product = 993; break; // Great Nature -> Green Live.
+ // Level 2
+ case 990: product = 994; break; // Red Blood -> Flame Heart.
+ case 991: product = 995; break; // Crystal Blue -> Mystic Frozen.
+ case 992: product = 996; break; // Wind of Verdure -> Rough Wind.
+ case 993: product = 997; break; // Green Live -> Great Nature.
+ default:
+ clif_skill_fail(sd,SO_EL_ANALYSIS,0,0);
+ return 1;
+ }
+
+ if( pc_delitem(sd,idx,del_amount,1,0,LOG_TYPE_CONSUME) ) {
+ clif_skill_fail(sd,SO_EL_ANALYSIS,0,0);
+ return 1;
+ }
+
+ if( skill_lv == 2 && rand()%100 < 25 ) { // At level 2 have a fail chance. You loose your items if it fails.
+ clif_skill_fail(sd,SO_EL_ANALYSIS,0,0);
+ return 1;
+ }
+
+
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = product;
+ tmp_item.amount = add_amount;
+ tmp_item.identify = 1;
+
+ if( tmp_item.amount ) {
+ if( (flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_CONSUME)) ) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list) {
+ int i, j, k, c, p, nameid, amount;
+
+ nullpo_ret(sd);
+ nullpo_ret(item_list);
+
+ // Search for objects that can be created.
+ for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) {
+ if( skill_produce_db[i].itemlv == 26 ) {
+ p = 0;
+ do {
+ c = 0;
+ // Verification of overlap between the objects required and the list submitted.
+ for( j = 0; j < MAX_PRODUCE_RESOURCE; j++ ) {
+ if( skill_produce_db[i].mat_id[j] > 0 ) {
+ for( k = 0; k < n; k++ ) {
+ int idx = item_list[k*2+0]-2;
+ nameid = sd->status.inventory[idx].nameid;
+ amount = item_list[k*2+1];
+
+ if( nameid == skill_produce_db[i].mat_id[j] && (amount-p*skill_produce_db[i].mat_amount[j]) >= skill_produce_db[i].mat_amount[j] )
+ c++; // match
+ }
+ }
+ else
+ break; // No more items required
+ }
+ p++;
+ } while(n == j && c == n);
+ p--;
+ if ( p > 0 ) {
+ skill_produce_mix(sd,GN_CHANGEMATERIAL,skill_produce_db[i].nameid,0,0,0,p);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
/**
* for Royal Guard's LG_TRAMPLE
**/
@@ -15071,146 +15971,162 @@ void skill_init_unit_layout (void)
for (i=0;i<MAX_SKILL_DB;i++) {
if (!skill_db[i].unit_id[0] || skill_db[i].unit_layout_type[0] != -1)
continue;
- switch (i) {
- case MG_FIREWALL:
- case WZ_ICEWALL:
- case WL_EARTHSTRAIN://Warlock
- // these will be handled later
- break;
- case PR_SANCTUARY:
- case NPC_EVILLAND:
- {
- static const int dx[] = {
- -1, 0, 1,-2,-1, 0, 1, 2,-2,-1,
- 0, 1, 2,-2,-1, 0, 1, 2,-1, 0, 1};
- static const int dy[]={
- -2,-2,-2,-1,-1,-1,-1,-1, 0, 0,
- 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2};
- skill_unit_layout[pos].count = 21;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
- }
- case PR_MAGNUS:
- {
- static const int dx[] = {
- -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
- 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
- -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,-1, 0, 1};
- static const int dy[] = {
- -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
- -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
- 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3};
- skill_unit_layout[pos].count = 33;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
- }
- case AS_VENOMDUST:
- {
- static const int dx[] = {-1, 0, 0, 0, 1};
- static const int dy[] = { 0,-1, 0, 1, 0};
- skill_unit_layout[pos].count = 5;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
- }
- case CR_GRANDCROSS:
- case NPC_GRANDDARKNESS:
- {
- static const int dx[] = {
- 0, 0,-1, 0, 1,-2,-1, 0, 1, 2,
- -4,-3,-2,-1, 0, 1, 2, 3, 4,-2,
- -1, 0, 1, 2,-1, 0, 1, 0, 0};
- static const int dy[] = {
- -4,-3,-2,-2,-2,-1,-1,-1,-1,-1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
- 1, 1, 1, 1, 2, 2, 2, 3, 4};
- skill_unit_layout[pos].count = 29;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
- }
- case PF_FOGWALL:
- {
- static const int dx[] = {
- -2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
- static const int dy[] = {
- -1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
- skill_unit_layout[pos].count = 15;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
- }
- case PA_GOSPEL:
- {
- static const int dx[] = {
- -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
- 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
- -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,
- -1, 0, 1};
- static const int dy[] = {
- -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
- -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
- 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
- 3, 3, 3};
- skill_unit_layout[pos].count = 33;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
+ if( i >= HM_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) {
+ int skill = i;
+
+ if( i >= EL_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) {
+ skill -= EL_SKILLRANGEMIN;
+ skill += EL_SKILLBASE;
}
- case NJ_KAENSIN:
- {
- static const int dx[] = {-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
- static const int dy[] = { 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2};
- skill_unit_layout[pos].count = 24;
+ if( skill == EL_FIRE_MANTLE ) {
+ static const int dx[] = {-1, 0, 1, 1, 1, 0,-1,-1};
+ static const int dy[] = { 1, 1, 1, 0,-1,-1,-1, 0};
+ skill_unit_layout[pos].count = 8;
memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
}
- case NJ_TATAMIGAESHI:
- {
- //Level 1 (count 4, cross of 3x3)
- static const int dx1[] = {-1, 1, 0, 0};
- static const int dy1[] = { 0, 0,-1, 1};
- //Level 2-3 (count 8, cross of 5x5)
- static const int dx2[] = {-2,-1, 1, 2, 0, 0, 0, 0};
- static const int dy2[] = { 0, 0, 0, 0,-2,-1, 1, 2};
- //Level 4-5 (count 12, cross of 7x7
- static const int dx3[] = {-3,-2,-1, 1, 2, 3, 0, 0, 0, 0, 0, 0};
- static const int dy3[] = { 0, 0, 0, 0, 0, 0,-3,-2,-1, 1, 2, 3};
- //lv1
- j = 0;
- skill_unit_layout[pos].count = 4;
- memcpy(skill_unit_layout[pos].dx,dx1,sizeof(dx1));
- memcpy(skill_unit_layout[pos].dy,dy1,sizeof(dy1));
- skill_db[i].unit_layout_type[j] = pos;
- //lv2/3
- j++;
- pos++;
- skill_unit_layout[pos].count = 8;
- memcpy(skill_unit_layout[pos].dx,dx2,sizeof(dx2));
- memcpy(skill_unit_layout[pos].dy,dy2,sizeof(dy2));
- skill_db[i].unit_layout_type[j] = pos;
- skill_db[i].unit_layout_type[++j] = pos;
- //lv4/5
- j++;
- pos++;
- skill_unit_layout[pos].count = 12;
- memcpy(skill_unit_layout[pos].dx,dx3,sizeof(dx3));
- memcpy(skill_unit_layout[pos].dy,dy3,sizeof(dy3));
- skill_db[i].unit_layout_type[j] = pos;
- skill_db[i].unit_layout_type[++j] = pos;
- //Fill in the rest using lv 5.
- for (;j<MAX_SKILL_LEVEL;j++)
- skill_db[i].unit_layout_type[j] = pos;
- //Skip, this way the check below will fail and continue to the next skill.
- pos++;
- break;
+ } else {
+ switch (i) {
+ case MG_FIREWALL:
+ case WZ_ICEWALL:
+ case WL_EARTHSTRAIN://Warlock
+ // these will be handled later
+ break;
+ case PR_SANCTUARY:
+ case NPC_EVILLAND: {
+ static const int dx[] = {
+ -1, 0, 1,-2,-1, 0, 1, 2,-2,-1,
+ 0, 1, 2,-2,-1, 0, 1, 2,-1, 0, 1};
+ static const int dy[]={
+ -2,-2,-2,-1,-1,-1,-1,-1, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2};
+ skill_unit_layout[pos].count = 21;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case PR_MAGNUS: {
+ static const int dx[] = {
+ -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
+ 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
+ -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,-1, 0, 1};
+ static const int dy[] = {
+ -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
+ -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3};
+ skill_unit_layout[pos].count = 33;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case AS_VENOMDUST: {
+ static const int dx[] = {-1, 0, 0, 0, 1};
+ static const int dy[] = { 0,-1, 0, 1, 0};
+ skill_unit_layout[pos].count = 5;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS: {
+ static const int dx[] = {
+ 0, 0,-1, 0, 1,-2,-1, 0, 1, 2,
+ -4,-3,-2,-1, 0, 1, 2, 3, 4,-2,
+ -1, 0, 1, 2,-1, 0, 1, 0, 0};
+ static const int dy[] = {
+ -4,-3,-2,-2,-2,-1,-1,-1,-1,-1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 1, 1, 1, 1, 2, 2, 2, 3, 4};
+ skill_unit_layout[pos].count = 29;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case PF_FOGWALL: {
+ static const int dx[] = {
+ -2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
+ static const int dy[] = {
+ -1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
+ skill_unit_layout[pos].count = 15;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case PA_GOSPEL: {
+ static const int dx[] = {
+ -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
+ 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
+ -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,
+ -1, 0, 1};
+ static const int dy[] = {
+ -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
+ -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
+ 3, 3, 3};
+ skill_unit_layout[pos].count = 33;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case NJ_KAENSIN: {
+ static const int dx[] = {-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
+ static const int dy[] = { 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2};
+ skill_unit_layout[pos].count = 24;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case NJ_TATAMIGAESHI: {
+ //Level 1 (count 4, cross of 3x3)
+ static const int dx1[] = {-1, 1, 0, 0};
+ static const int dy1[] = { 0, 0,-1, 1};
+ //Level 2-3 (count 8, cross of 5x5)
+ static const int dx2[] = {-2,-1, 1, 2, 0, 0, 0, 0};
+ static const int dy2[] = { 0, 0, 0, 0,-2,-1, 1, 2};
+ //Level 4-5 (count 12, cross of 7x7
+ static const int dx3[] = {-3,-2,-1, 1, 2, 3, 0, 0, 0, 0, 0, 0};
+ static const int dy3[] = { 0, 0, 0, 0, 0, 0,-3,-2,-1, 1, 2, 3};
+ //lv1
+ j = 0;
+ skill_unit_layout[pos].count = 4;
+ memcpy(skill_unit_layout[pos].dx,dx1,sizeof(dx1));
+ memcpy(skill_unit_layout[pos].dy,dy1,sizeof(dy1));
+ skill_db[i].unit_layout_type[j] = pos;
+ //lv2/3
+ j++;
+ pos++;
+ skill_unit_layout[pos].count = 8;
+ memcpy(skill_unit_layout[pos].dx,dx2,sizeof(dx2));
+ memcpy(skill_unit_layout[pos].dy,dy2,sizeof(dy2));
+ skill_db[i].unit_layout_type[j] = pos;
+ skill_db[i].unit_layout_type[++j] = pos;
+ //lv4/5
+ j++;
+ pos++;
+ skill_unit_layout[pos].count = 12;
+ memcpy(skill_unit_layout[pos].dx,dx3,sizeof(dx3));
+ memcpy(skill_unit_layout[pos].dy,dy3,sizeof(dy3));
+ skill_db[i].unit_layout_type[j] = pos;
+ skill_db[i].unit_layout_type[++j] = pos;
+ //Fill in the rest using lv 5.
+ for (;j<MAX_SKILL_LEVEL;j++)
+ skill_db[i].unit_layout_type[j] = pos;
+ //Skip, this way the check below will fail and continue to the next skill.
+ pos++;
+ }
+ break;
+ case GN_WALLOFTHORN: {
+ static const int dx[] = {-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2, 2, 2, 1, 0};
+ static const int dy[] = { 2, 2, 1, 0,-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2};
+ skill_unit_layout[pos].count = 16;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ default:
+ ShowError("unknown unit layout at skill %d\n",i);
+ break;
}
- default:
- ShowError("unknown unit layout at skill %d\n",i);
- break;
}
if (!skill_unit_layout[pos].count)
continue;
@@ -15364,6 +16280,21 @@ int skill_stasis_check(struct block_list *bl, int src_id, int skillid) {
return 0; // Can Cast anything else like Weapon Skills
}
+int skill_get_elemental_type( int skill_id , int skill_lv ) {
+ int type = 0;
+
+ switch( skill_id ) {
+ case SO_SUMMON_AGNI: type = 2114; break;
+ case SO_SUMMON_AQUA: type = 2117; break;
+ case SO_SUMMON_VENTUS: type = 2120; break;
+ case SO_SUMMON_TERA: type = 2123; break;
+ }
+
+ type += skill_lv - 1;
+
+ return type;
+}
+
/**
* reload stored skill cooldowns when a player logs in.
* @param sd the affected player structure
@@ -15408,8 +16339,8 @@ static bool skill_parse_row_skilldb(char* split[], int columns, int current)
int i;
if( (id >= GD_SKILLRANGEMIN && id <= GD_SKILLRANGEMAX)
|| (id >= HM_SKILLRANGEMIN && id <= HM_SKILLRANGEMAX)
- || (id >= MC_SKILLRANGEMIN && id <= MC_SKILLRANGEMAX) )
- {
+ || (id >= MC_SKILLRANGEMIN && id <= MC_SKILLRANGEMAX)
+ || (id >= EL_SKILLRANGEMIN && id <= EL_SKILLRANGEMAX) ) {
ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with guild/homun/mercenary skill mapping)!\n", id);
return false;
}
diff --git a/src/map/skill.h b/src/map/skill.h
index 1275b3910..e7108e2f7 100644
--- a/src/map/skill.h
+++ b/src/map/skill.h
@@ -1705,49 +1705,49 @@ enum {
UNT_EVILLAND,
UNT_DARK_RUNNER, //TODO
UNT_DARK_TRANSFER, //TODO
- UNT_EPICLESIS, //TODO
- UNT_EARTHSTRAIN, //TODO
- UNT_MANHOLE, //TODO
- UNT_DIMENSIONDOOR, //TODO
- UNT_CHAOSPANIC, //TODO
- UNT_MAELSTROM, //TODO
- UNT_BLOODYLUST, //TODO
- UNT_FEINTBOMB, //TODO
- UNT_MAGENTATRAP, //TODO
- UNT_COBALTTRAP, //TODO
- UNT_MAIZETRAP, //TODO
- UNT_VERDURETRAP, //TODO
- UNT_FIRINGTRAP, //TODO
- UNT_ICEBOUNDTRAP, //TODO
- UNT_ELECTRICSHOCKER, //TODO
- UNT_CLUSTERBOMB, //TODO
- UNT_REVERBERATION, //TODO
- UNT_SEVERE_RAINSTORM, //TODO
- UNT_FIREWALK, //TODO
- UNT_ELECTRICWALK, //TODO
- UNT_NETHERWORLD, //TODO
- UNT_PSYCHIC_WAVE, //TODO
- UNT_CLOUD_KILL, //TODO
- UNT_POISONSMOKE, //TODO
- UNT_NEUTRALBARRIER, //TODO
- UNT_STEALTHFIELD, //TODO
- UNT_WARMER, //TODO
- UNT_THORNS_TRAP, //TODO
- UNT_WALLOFTHORN, //TODO
- UNT_DEMONIC_FIRE, //TODO
- UNT_FIRE_EXPANSION_SMOKE_POWDER, //TODO
- UNT_FIRE_EXPANSION_TEAR_GAS, //TODO
- UNT_HELLS_PLANT, //TODO
- UNT_VACUUM_EXTREME, //TODO
- UNT_BANDING, //TODO
- UNT_FIRE_MANTLE, //TODO
- UNT_WATER_BARRIER, //TODO
- UNT_ZEPHYR, //TODO
- UNT_POWER_OF_GAIA, //TODO
- UNT_FIRE_INSIGNIA, //TODO
- UNT_WATER_INSIGNIA, //TODO
- UNT_WIND_INSIGNIA, //TODO
- UNT_EARTH_INSIGNIA, //TODO
+ UNT_EPICLESIS,
+ UNT_EARTHSTRAIN,
+ UNT_MANHOLE,
+ UNT_DIMENSIONDOOR,
+ UNT_CHAOSPANIC,
+ UNT_MAELSTROM,
+ UNT_BLOODYLUST,
+ UNT_FEINTBOMB,
+ UNT_MAGENTATRAP,
+ UNT_COBALTTRAP,
+ UNT_MAIZETRAP,
+ UNT_VERDURETRAP,
+ UNT_FIRINGTRAP,
+ UNT_ICEBOUNDTRAP,
+ UNT_ELECTRICSHOCKER,
+ UNT_CLUSTERBOMB,
+ UNT_REVERBERATION,
+ UNT_SEVERE_RAINSTORM,
+ UNT_FIREWALK,
+ UNT_ELECTRICWALK,
+ UNT_NETHERWORLD,
+ UNT_PSYCHIC_WAVE,
+ UNT_CLOUD_KILL,
+ UNT_POISONSMOKE,
+ UNT_NEUTRALBARRIER,
+ UNT_STEALTHFIELD,
+ UNT_WARMER,
+ UNT_THORNS_TRAP,
+ UNT_WALLOFTHORN,
+ UNT_DEMONIC_FIRE,
+ UNT_FIRE_EXPANSION_SMOKE_POWDER,
+ UNT_FIRE_EXPANSION_TEAR_GAS,
+ UNT_HELLS_PLANT,
+ UNT_VACUUM_EXTREME,
+ UNT_BANDING,
+ UNT_FIRE_MANTLE,
+ UNT_WATER_BARRIER,
+ UNT_ZEPHYR,
+ UNT_POWER_OF_GAIA,
+ UNT_FIRE_INSIGNIA,
+ UNT_WATER_INSIGNIA,
+ UNT_WIND_INSIGNIA,
+ UNT_EARTH_INSIGNIA,
UNT_POISON_MIST,
UNT_LAVA_SLIDE,
UNT_VOLCANIC_ASH,
@@ -1823,4 +1823,9 @@ enum gx_poison {
* Auto Shadow Spell (Shadow Chaser)
**/
int skill_select_menu(struct map_session_data *sd,int flag,int skill_id);
+
+int skill_elementalanalysis(struct map_session_data *sd, int n, int type, unsigned short *item_list); // Sorcerer Four Elemental Analisys.
+int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list); // Genetic Change Material.
+int skill_get_elemental_type(int skill_id, int skill_lv);
+
#endif /* _SKILL_H_ */
diff --git a/src/map/status.c b/src/map/status.c
index 162765aa3..972553d94 100644
--- a/src/map/status.c
+++ b/src/map/status.c
@@ -29,6 +29,7 @@
#include "unit.h"
#include "homunculus.h"
#include "mercenary.h"
+#include "elemental.h"
#include "vending.h"
#include <time.h>
@@ -645,28 +646,57 @@ void initChangeTables(void)
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 );
-
+ /**
+ * 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_DIAMONDDUST , SC_CRYSTALIZE , SI_COLD , SCB_NONE );//Will add flags in major balance update 8 [Rytech]
+ 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 );
+
+ // Elemental Spirit summoner's 'side' status changes.
+ set_sc( EL_CIRCLE_OF_FIRE , SC_CIRCLE_OF_FIRE_OPTION, SI_CIRCLE_OF_FIRE_OPTION, SCB_NONE );
+ set_sc( EL_FIRE_CLOAK , SC_FIRE_CLOAK_OPTION , SI_FIRE_CLOAK_OPTION , SCB_ALL );
+ set_sc( EL_WATER_SCREEN , SC_WATER_SCREEN_OPTION , SI_WATER_SCREEN_OPTION , SCB_NONE );
+ set_sc( EL_WATER_DROP , SC_WATER_DROP_OPTION , SI_WATER_DROP_OPTION , SCB_ALL );
+ set_sc( EL_WATER_BARRIER , SC_WATER_BARRIER , SI_WATER_BARRIER , SCB_MDEF|SCB_WATK|SCB_MATK|SCB_FLEE );
+ set_sc( EL_WIND_STEP , SC_WIND_STEP_OPTION , SI_WIND_STEP_OPTION , SCB_SPEED|SCB_FLEE );
+ set_sc( EL_WIND_CURTAIN , SC_WIND_CURTAIN_OPTION , SI_WIND_CURTAIN_OPTION , SCB_ALL );
+ set_sc( EL_ZEPHYR , SC_ZEPHYR , SI_ZEPHYR , SCB_FLEE );
+ set_sc( EL_SOLID_SKIN , SC_SOLID_SKIN_OPTION , SI_SOLID_SKIN_OPTION , SCB_DEF|SCB_MAXHP );
+ set_sc( EL_STONE_SHIELD , SC_STONE_SHIELD_OPTION , SI_STONE_SHIELD_OPTION , SCB_ALL );
+ set_sc( EL_POWER_OF_GAIA , SC_POWER_OF_GAIA , SI_POWER_OF_GAIA , SCB_MAXHP|SCB_DEF|SCB_SPEED );
+ set_sc( EL_PYROTECHNIC , SC_PYROTECHNIC_OPTION , SI_PYROTECHNIC_OPTION , SCB_WATK );
+ set_sc( EL_HEATER , SC_HEATER_OPTION , SI_HEATER_OPTION , SCB_WATK );
+ set_sc( EL_TROPIC , SC_TROPIC_OPTION , SI_TROPIC_OPTION , SCB_WATK );
+ set_sc( EL_AQUAPLAY , SC_AQUAPLAY_OPTION , SI_AQUAPLAY_OPTION , SCB_MATK );
+ set_sc( EL_COOLER , SC_COOLER_OPTION , SI_COOLER_OPTION , SCB_MATK );
+ set_sc( EL_CHILLY_AIR , SC_CHILLY_AIR_OPTION , SI_CHILLY_AIR_OPTION , SCB_MATK );
+ set_sc( EL_GUST , SC_GUST_OPTION , SI_GUST_OPTION , SCB_NONE );
+ set_sc( EL_BLAST , SC_BLAST_OPTION , SI_BLAST_OPTION , SCB_NONE );
+ set_sc( EL_WILD_STORM , SC_WILD_STORM_OPTION , SI_WILD_STORM_OPTION , SCB_NONE );
+ set_sc( EL_PETROLOGY , SC_PETROLOGY_OPTION , SI_PETROLOGY_OPTION , SCB_NONE );
+ set_sc( EL_CURSED_SOIL , SC_CURSED_SOIL_OPTION , SI_CURSED_SOIL_OPTION , SCB_NONE );
+ set_sc( EL_UPHEAVAL , SC_UPHEAVAL_OPTION , SI_UPHEAVAL_OPTION , SCB_NONE );
+ set_sc( EL_TIDAL_WEAPON , SC_TIDAL_WEAPON_OPTION , SI_TIDAL_WEAPON_OPTION , SCB_ALL );
+ set_sc( EL_ROCK_CRUSHER , SC_ROCK_CRUSHER , SI_ROCK_CRUSHER , SCB_DEF );
+ set_sc( EL_ROCK_CRUSHER_ATK, SC_ROCK_CRUSHER_ATK , SI_ROCK_CRUSHER_ATK , SCB_SPEED );
+
// Storing the target job rather than simply SC_SPIRIT simplifies code later on.
SkillStatusChangeTable[SL_ALCHEMIST] = (sc_type)MAPID_ALCHEMIST,
SkillStatusChangeTable[SL_MONK] = (sc_type)MAPID_MONK,
@@ -882,11 +912,20 @@ void initChangeTables(void)
StatusChangeFlagTable[SC_SIROMA_ICE_TEA] |= SCB_DEX;
StatusChangeFlagTable[SC_DROCERA_HERB_STEAMED] |= SCB_AGI;
StatusChangeFlagTable[SC_PUTTI_TAILS_NOODLES] |= SCB_LUK;
+ StatusChangeFlagTable[SC_BOOST500] |= SCB_ASPD;
+ StatusChangeFlagTable[SC_FULL_SWING_K] |= SCB_BATK;
+ StatusChangeFlagTable[SC_MANA_PLUS] |= SCB_MATK;
+ StatusChangeFlagTable[SC_MUSTLE_M] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_LIFE_FORCE_F] |= SCB_MAXSP;
+ StatusChangeFlagTable[SC_EXTRACT_WHITE_POTION_Z] |= SCB_REGEN;
+ StatusChangeFlagTable[SC_VITATA_500] |= SCB_REGEN;
+ StatusChangeFlagTable[SC_EXTRACT_SALAMINE_JUICE] |= SCB_ASPD;
#ifdef RENEWAL_EDP
// renewal 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;
}
@@ -1077,6 +1116,7 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s
case BL_MOB: mob_damage((TBL_MOB*)target, src, hp); break;
case BL_HOM: merc_damage((TBL_HOM*)target,src,hp,sp); break;
case BL_MER: mercenary_damage((TBL_MER*)target,src,hp,sp); break;
+ case BL_ELEM: elemental_damage((TBL_ELEM*)target,src,hp,sp); break;
}
if( target->type == BL_PC && ((TBL_PC*)target)->disguise && src )
@@ -1102,6 +1142,7 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s
case BL_MOB: flag = mob_dead((TBL_MOB*)target, src, flag&4?3:0); break;
case BL_HOM: flag = merc_hom_dead((TBL_HOM*)target,src); break;
case BL_MER: flag = mercenary_dead((TBL_MER*)target,src); break;
+ case BL_ELEM: flag = elemental_dead((TBL_ELEM*)target,src); break;
default: //Unhandled case, do nothing to object.
flag = 0;
break;
@@ -1239,6 +1280,7 @@ int status_heal(struct block_list *bl,int hp,int sp, int flag)
case BL_MOB: mob_heal((TBL_MOB*)bl,hp); break;
case BL_HOM: merc_hom_heal((TBL_HOM*)bl,hp,sp); break;
case BL_MER: mercenary_heal((TBL_MER*)bl,hp,sp); break;
+ case BL_ELEM: elemental_heal((TBL_ELEM*)bl,hp,sp); break;
}
return hp+sp;
@@ -1378,6 +1420,8 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, int
//on dead characters, said checks are left to skill.c [Skotlex]
if (target && status_isdead(target))
return 0;
+ if( src && (sc = status_get_sc(src)) && sc->data[SC_CRYSTALIZE] )
+ return 0;
}
if (skill_num == PA_PRESSURE && flag && target) {
@@ -1394,12 +1438,11 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, int
&& (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_num))
return 0;
- if (src) sc = status_get_sc(src);
+ if ( src ) sc = status_get_sc(src);
- if(sc && sc->count)
- {
- if(sc->opt1 >0 && sc->opt1 != OPT1_BURNING && skill_num != SR_GENTLETOUCH_CURE)
- { //Stuned/Frozen/etc
+ if( sc && sc->count ) {
+
+ if( sc->opt1 >0 && sc->opt1 != OPT1_BURNING && skill_num != SR_GENTLETOUCH_CURE ) { //Stuned/Frozen/etc
if (flag != 1) //Can't cast, casted stuff can't damage.
return 0;
if (!(skill_get_inf(skill_num)&INF_GROUND_SKILL))
@@ -1570,11 +1613,12 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, int
return 0;
case BL_HOM:
case BL_MER:
+ case BL_ELEM:
if( target->type == BL_HOM && skill_num && battle_config.hom_setting&0x1 && skill_get_inf(skill_num)&INF_SUPPORT_SKILL && battle_get_master(target) != src )
return 0; // Can't use support skills on Homunculus (only Master/Self)
if( target->type == BL_MER && (skill_num == PR_ASPERSIO || (skill_num >= SA_FLAMELAUNCHER && skill_num <= SA_SEISMICWEAPON)) && battle_get_master(target) != src )
return 0; // Can't use Weapon endow skills on Mercenary (only Master)
- if( target->type == BL_MER && skill_num == AM_POTIONPITCHER )
+ if( skill_num == AM_POTIONPITCHER && ( target->type == BL_MER || target->type == BL_ELEM) )
return 0; // Can't use Potion Pitcher on Mercenaries
default:
//Check for chase-walk/hiding/cloaking opponents.
@@ -2848,8 +2892,7 @@ int status_calc_pc_(struct map_session_data* sd, bool first)
}
if(sc->count){
- if(sc->data[SC_CONCENTRATE])
- { //Update the card-bonus data
+ if(sc->data[SC_CONCENTRATE]) { //Update the card-bonus data
sc->data[SC_CONCENTRATE]->val3 = sd->param_bonus[1]; //Agi
sc->data[SC_CONCENTRATE]->val4 = sd->param_bonus[4]; //Dex
}
@@ -2869,20 +2912,38 @@ int status_calc_pc_(struct map_session_data* sd, bool first)
sd->subele[ELE_HOLY] += sc->data[SC_PROVIDENCE]->val2;
sd->subrace[RC_DEMON] += sc->data[SC_PROVIDENCE]->val2;
}
- if(sc->data[SC_ARMOR_ELEMENT])
- { //This status change should grant card-type elemental resist.
+ if(sc->data[SC_ARMOR_ELEMENT]) { //This status change should grant card-type elemental resist.
sd->subele[ELE_WATER] += sc->data[SC_ARMOR_ELEMENT]->val1;
sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_ELEMENT]->val2;
sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_ELEMENT]->val3;
sd->subele[ELE_WIND] += sc->data[SC_ARMOR_ELEMENT]->val4;
}
- if(sc->data[SC_ARMOR_RESIST])
- { // Undead Scroll
+ if(sc->data[SC_ARMOR_RESIST]) { // Undead Scroll
sd->subele[ELE_WATER] += sc->data[SC_ARMOR_RESIST]->val1;
sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_RESIST]->val2;
sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_RESIST]->val3;
sd->subele[ELE_WIND] += sc->data[SC_ARMOR_RESIST]->val4;
}
+ if( sc->data[SC_FIRE_CLOAK_OPTION] ) {
+ i = sc->data[SC_FIRE_CLOAK_OPTION]->val2;
+ sd->subele[ELE_FIRE] += i;
+ sd->subele[ELE_EARTH] -= i;
+ }
+ if( sc->data[SC_WATER_DROP_OPTION] ) {
+ i = sc->data[SC_WATER_DROP_OPTION]->val2;
+ sd->subele[ELE_WATER] += i;
+ sd->subele[ELE_WIND] -= i;
+ }
+ if( sc->data[SC_WIND_CURTAIN_OPTION] ) {
+ i = sc->data[SC_WIND_CURTAIN_OPTION]->val2;
+ sd->subele[ELE_WIND] += i;
+ sd->subele[ELE_WATER] -= i;
+ }
+ if( sc->data[SC_STONE_SHIELD_OPTION] ) {
+ i = sc->data[SC_STONE_SHIELD_OPTION]->val2;
+ sd->subele[ELE_EARTH] += i;
+ sd->subele[ELE_FIRE] -= i;
+ }
}
status_cpy(&sd->battle_status, status);
@@ -3006,6 +3067,45 @@ int status_calc_homunculus_(struct homun_data *hd, bool first)
return 1;
}
+int status_calc_elemental_(struct elemental_data *ed, bool first) {
+ struct status_data *status = &ed->base_status;
+ struct s_elemental *ele = &ed->elemental;
+ struct map_session_data *sd = ed->master;
+
+ if( !sd )
+ return 0;
+
+ status->str = ele->str;
+ status->agi = ele->agi;
+ status->vit = ele->vit;
+ status->dex = ele->dex;
+ status->int_ = ele->int_;
+ status->luk = ele->luk;
+
+ if( first ) {
+ memcpy(status, &ed->db->status, sizeof(struct status_data));
+ status->mode = MD_CANMOVE|MD_CANATTACK;
+ status->max_hp += 4000 + 500 * pc_checkskill(sd,SO_EL_SYMPATHY);
+ status->max_sp += 300 + 50 * pc_checkskill(sd,SO_EL_SYMPATHY);
+ status->hp = status->max_hp;
+ status->sp = status->max_sp;
+ status->str += sd->base_status.str * 25 / 100;
+ status->agi += sd->base_status.agi * 25 / 100;
+ status->vit += sd->base_status.vit * 25 / 100;
+ status->int_ += sd->base_status.int_ * 25 / 100;
+ status->def += sd->base_status.dex * 25 / 100;
+ status->luk += sd->base_status.luk * 25 / 100;
+
+ status_calc_misc(&ed->bl, status, ed->db->lv);
+ memcpy(&ed->battle_status,status,sizeof(struct status_data));
+ } else {
+ status_calc_misc(&ed->bl, status, ed->db->lv);
+ status_cpy(&ed->battle_status, status);
+ }
+
+ return 0;
+}
+
static unsigned short status_calc_str(struct block_list *,struct status_change *,int);
static unsigned short status_calc_agi(struct block_list *,struct status_change *,int);
static unsigned short status_calc_vit(struct block_list *,struct status_change *,int);
@@ -3107,27 +3207,28 @@ void status_calc_regen(struct block_list *bl, struct status_data *status, struct
sregen->sp = cap_value(val, 0, SHRT_MAX);
}
- if( bl->type == BL_HOM )
- {
+ if( bl->type == BL_HOM ) {
struct homun_data *hd = (TBL_HOM*)bl;
- if( (skill = merc_hom_checkskill(hd,HAMI_SKIN)) > 0 )
- {
+ if( (skill = merc_hom_checkskill(hd,HAMI_SKIN)) > 0 ) {
val = regen->hp*(100+5*skill)/100;
regen->hp = cap_value(val, 1, SHRT_MAX);
}
- if( (skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0 )
- {
+ if( (skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0 ) {
val = regen->sp*(100+3*skill)/100;
regen->sp = cap_value(val, 1, SHRT_MAX);
}
- }
- else if( bl->type == BL_MER )
- {
+ } else if( bl->type == BL_MER ) {
val = (status->max_hp * status->vit / 10000 + 1) * 6;
regen->hp = cap_value(val, 1, SHRT_MAX);
val = (status->max_sp * (status->int_ + 10) / 750) + 1;
regen->sp = cap_value(val, 1, SHRT_MAX);
+ } else if( bl->type == BL_ELEM ) {
+ val = (status->max_hp * status->vit / 10000 + 1) * 6;
+ regen->hp = cap_value(val, 1, SHRT_MAX);
+
+ val = (status->max_sp * (status->int_ + 10) / 750) + 1;
+ regen->sp = cap_value(val, 1, SHRT_MAX);
}
}
@@ -3241,7 +3342,7 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
if(flag&SCB_VIT) {
status->vit = status_calc_vit(bl, sc, b_status->vit);
flag|=SCB_DEF2|SCB_MDEF2;
- if( bl->type&(BL_PC|BL_HOM|BL_MER) )
+ if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
flag |= SCB_MAXHP;
if( bl->type&BL_HOM )
flag |= SCB_DEF;
@@ -3250,7 +3351,7 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
if(flag&SCB_INT) {
status->int_ = status_calc_int(bl, sc, b_status->int_);
flag|=SCB_MATK|SCB_MDEF2;
- if( bl->type&(BL_PC|BL_HOM|BL_MER) )
+ if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
flag |= SCB_MAXSP;
if( bl->type&BL_HOM )
flag |= SCB_MDEF;
@@ -3582,15 +3683,14 @@ void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first)
status = status_get_status_data(bl);
memcpy(&b_status, status, sizeof(struct status_data));
- if( flag&SCB_BASE )
- {// calculate the object's base status too
- switch( bl->type )
- {
+ if( flag&SCB_BASE ) {// calculate the object's base status too
+ switch( bl->type ) {
case BL_PC: status_calc_pc_(BL_CAST(BL_PC,bl), first); break;
case BL_MOB: status_calc_mob_(BL_CAST(BL_MOB,bl), first); break;
case BL_PET: status_calc_pet_(BL_CAST(BL_PET,bl), first); break;
case BL_HOM: status_calc_homunculus_(BL_CAST(BL_HOM,bl), first); break;
case BL_MER: status_calc_mercenary_(BL_CAST(BL_MER,bl), first); break;
+ case BL_ELEM: status_calc_elemental_(BL_CAST(BL_ELEM,bl), first); break;
}
}
@@ -3671,17 +3771,11 @@ void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first)
clif_updatestatus(sd,SP_HP);
if(b_status.sp != status->sp)
clif_updatestatus(sd,SP_SP);
- }
- else
- if( bl->type == BL_HOM )
- {
+ } else if( bl->type == BL_HOM ) {
TBL_HOM* hd = BL_CAST(BL_HOM, bl);
if( hd->master && memcmp(&b_status, status, sizeof(struct status_data)) != 0 )
clif_hominfo(hd->master,hd,0);
- }
- else
- if( bl->type == BL_MER )
- {
+ } else if( bl->type == BL_MER ) {
TBL_MER* md = BL_CAST(BL_MER, bl);
if( b_status.rhw.atk != status->rhw.atk || b_status.rhw.atk2 != status->rhw.atk2 )
clif_mercenary_updatestatus(md->master, SP_ATK1);
@@ -3707,7 +3801,17 @@ void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first)
clif_mercenary_updatestatus(md->master, SP_HP);
if( b_status.sp != status->sp )
clif_mercenary_updatestatus(md->master, SP_SP);
- }
+ } else if( bl->type == BL_ELEM ) {
+ TBL_ELEM* ed = BL_CAST(BL_ELEM, bl);
+ if( b_status.max_hp != status->max_hp )
+ clif_elemental_updatestatus(ed->master, SP_MAXHP);
+ if( b_status.max_sp != status->max_sp )
+ clif_elemental_updatestatus(ed->master, SP_MAXSP);
+ if( b_status.hp != status->hp )
+ clif_elemental_updatestatus(ed->master, SP_HP);
+ if( b_status.sp != status->sp )
+ clif_mercenary_updatestatus(ed->master, SP_SP);
+ }
}
/*==========================================
@@ -4641,6 +4745,10 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha
val = max( val, 75 );
if( sc->data[SC_CLOAKINGEXCEED] )
val = max( val, sc->data[SC_CLOAKINGEXCEED]->val3);
+ if( sc->data[SC_HOVERING] )
+ val = max( val, 10 );
+ if( sc->data[SC_GN_CARTBOOST] )
+ val = max( val, sc->data[SC_GN_CARTBOOST]->val2 );
if( sc->data[SC_GT_REVITALIZE] )
val = max( val, sc->data[SC_GT_REVITALIZE]->val2 );
if( sc->data[SC_SWINGDANCE] )
@@ -5005,8 +5113,7 @@ static unsigned short status_calc_mode(struct block_list *bl, struct status_chan
return cap_value(mode,0,USHRT_MAX);
}
-const char* status_get_name(struct block_list *bl)
-{
+const char* status_get_name(struct block_list *bl) {
nullpo_ret(bl);
switch (bl->type) {
case BL_PC: return ((TBL_PC *)bl)->fakename[0] != '\0' ? ((TBL_PC*)bl)->fakename : ((TBL_PC*)bl)->status.name;
@@ -5022,17 +5129,16 @@ const char* status_get_name(struct block_list *bl)
* 対象のClassを返す(汎用)
* 戻りは整数で0以上
*------------------------------------------*/
-int status_get_class(struct block_list *bl)
-{
+int status_get_class(struct block_list *bl) {
nullpo_ret(bl);
- switch( bl->type )
- {
- case BL_PC: return ((TBL_PC*)bl)->status.class_;
- case BL_MOB: return ((TBL_MOB*)bl)->vd->class_; //Class used on all code should be the view class of the mob.
- case BL_PET: return ((TBL_PET*)bl)->pet.class_;
- case BL_HOM: return ((TBL_HOM*)bl)->homunculus.class_;
- case BL_MER: return ((TBL_MER*)bl)->mercenary.class_;
- case BL_NPC: return ((TBL_NPC*)bl)->class_;
+ switch( bl->type ) {
+ case BL_PC: return ((TBL_PC*)bl)->status.class_;
+ case BL_MOB: return ((TBL_MOB*)bl)->vd->class_; //Class used on all code should be the view class of the mob.
+ case BL_PET: return ((TBL_PET*)bl)->pet.class_;
+ case BL_HOM: return ((TBL_HOM*)bl)->homunculus.class_;
+ case BL_MER: return ((TBL_MER*)bl)->mercenary.class_;
+ case BL_NPC: return ((TBL_NPC*)bl)->class_;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->elemental.class_;
}
return 0;
}
@@ -5040,8 +5146,7 @@ int status_get_class(struct block_list *bl)
* 対象のレベルを返す(汎用)
* 戻りは整数で0以上
*------------------------------------------*/
-int status_get_lv(struct block_list *bl)
-{
+int status_get_lv(struct block_list *bl) {
nullpo_ret(bl);
switch (bl->type) {
case BL_PC: return ((TBL_PC*)bl)->status.base_level;
@@ -5049,6 +5154,7 @@ int status_get_lv(struct block_list *bl)
case BL_PET: return ((TBL_PET*)bl)->pet.level;
case BL_HOM: return ((TBL_HOM*)bl)->homunculus.level;
case BL_MER: return ((TBL_MER*)bl)->db->lv;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->db->lv;
}
return 1;
}
@@ -5060,6 +5166,7 @@ struct regen_data *status_get_regen_data(struct block_list *bl)
case BL_PC: return &((TBL_PC*)bl)->regen;
case BL_HOM: return &((TBL_HOM*)bl)->regen;
case BL_MER: return &((TBL_MER*)bl)->regen;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->regen;
default:
return NULL;
}
@@ -5075,6 +5182,7 @@ struct status_data *status_get_status_data(struct block_list *bl)
case BL_PET: return &((TBL_PET*)bl)->status;
case BL_HOM: return &((TBL_HOM*)bl)->battle_status;
case BL_MER: return &((TBL_MER*)bl)->battle_status;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->battle_status;
default:
return &dummy_status;
}
@@ -5089,6 +5197,7 @@ struct status_data *status_get_base_status(struct block_list *bl)
case BL_PET: return &((TBL_PET*)bl)->db->status;
case BL_HOM: return &((TBL_HOM*)bl)->base_status;
case BL_MER: return &((TBL_MER*)bl)->base_status;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->base_status;
default:
return NULL;
}
@@ -5111,115 +5220,120 @@ unsigned short status_get_speed(struct block_list *bl)
return status_get_status_data(bl)->speed;
}
-int status_get_party_id(struct block_list *bl)
-{
+int status_get_party_id(struct block_list *bl) {
nullpo_ret(bl);
switch (bl->type) {
- case BL_PC:
- return ((TBL_PC*)bl)->status.party_id;
- case BL_PET:
- if (((TBL_PET*)bl)->msd)
- return ((TBL_PET*)bl)->msd->status.party_id;
- break;
- case BL_MOB:
- {
- struct mob_data *md=(TBL_MOB*)bl;
- if( md->master_id>0 )
- {
- struct map_session_data *msd;
- if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
- return msd->status.party_id;
- return -md->master_id;
- }
- }
- break;
- case BL_HOM:
- if (((TBL_HOM*)bl)->master)
- return ((TBL_HOM*)bl)->master->status.party_id;
- break;
- case BL_MER:
- if (((TBL_MER*)bl)->master)
- return ((TBL_MER*)bl)->master->status.party_id;
- break;
- case BL_SKILL:
- return ((TBL_SKILL*)bl)->group->party_id;
+ case BL_PC:
+ return ((TBL_PC*)bl)->status.party_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->status.party_id;
+ break;
+ case BL_MOB: {
+ struct mob_data *md=(TBL_MOB*)bl;
+ if( md->master_id > 0 ) {
+ struct map_session_data *msd;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->status.party_id;
+ return -md->master_id;
+ }
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->status.party_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->status.party_id;
+ break;
+ case BL_SKILL:
+ return ((TBL_SKILL*)bl)->group->party_id;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->status.party_id;
+ break;
}
return 0;
}
-int status_get_guild_id(struct block_list *bl)
-{
+int status_get_guild_id(struct block_list *bl) {
nullpo_ret(bl);
switch (bl->type) {
- case BL_PC:
- return ((TBL_PC*)bl)->status.guild_id;
- case BL_PET:
- if (((TBL_PET*)bl)->msd)
- return ((TBL_PET*)bl)->msd->status.guild_id;
- break;
- case BL_MOB:
- {
- struct map_session_data *msd;
- struct mob_data *md = (struct mob_data *)bl;
- if (md->guardian_data) //Guardian's guild [Skotlex]
- return md->guardian_data->guild_id;
- if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
- return msd->status.guild_id; //Alchemist's mobs [Skotlex]
- }
- break;
- case BL_HOM:
- if (((TBL_HOM*)bl)->master)
- return ((TBL_HOM*)bl)->master->status.guild_id;
- break;
- case BL_MER:
- if (((TBL_MER*)bl)->master)
- return ((TBL_MER*)bl)->master->status.guild_id;
- break;
- case BL_NPC:
- if (((TBL_NPC*)bl)->subtype == SCRIPT)
- return ((TBL_NPC*)bl)->u.scr.guild_id;
- break;
- case BL_SKILL:
- return ((TBL_SKILL*)bl)->group->guild_id;
+ case BL_PC:
+ return ((TBL_PC*)bl)->status.guild_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->status.guild_id;
+ break;
+ case BL_MOB: {
+ struct map_session_data *msd;
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md->guardian_data) //Guardian's guild [Skotlex]
+ return md->guardian_data->guild_id;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->status.guild_id; //Alchemist's mobs [Skotlex]
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->status.guild_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->status.guild_id;
+ break;
+ case BL_NPC:
+ if (((TBL_NPC*)bl)->subtype == SCRIPT)
+ return ((TBL_NPC*)bl)->u.scr.guild_id;
+ break;
+ case BL_SKILL:
+ return ((TBL_SKILL*)bl)->group->guild_id;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->status.guild_id;
+ break;
}
return 0;
}
-int status_get_emblem_id(struct block_list *bl)
-{
+int status_get_emblem_id(struct block_list *bl) {
nullpo_ret(bl);
switch (bl->type) {
- case BL_PC:
- return ((TBL_PC*)bl)->guild_emblem_id;
- case BL_PET:
- if (((TBL_PET*)bl)->msd)
- return ((TBL_PET*)bl)->msd->guild_emblem_id;
- break;
- case BL_MOB:
- {
- struct map_session_data *msd;
- struct mob_data *md = (struct mob_data *)bl;
- if (md->guardian_data) //Guardian's guild [Skotlex]
- return md->guardian_data->emblem_id;
- if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
- return msd->guild_emblem_id; //Alchemist's mobs [Skotlex]
- }
- break;
- case BL_HOM:
- if (((TBL_HOM*)bl)->master)
- return ((TBL_HOM*)bl)->master->guild_emblem_id;
- break;
- case BL_MER:
- if (((TBL_MER*)bl)->master)
- return ((TBL_MER*)bl)->master->guild_emblem_id;
- break;
- case BL_NPC:
- if (((TBL_NPC*)bl)->subtype == SCRIPT && ((TBL_NPC*)bl)->u.scr.guild_id > 0) {
- struct guild *g = guild_search(((TBL_NPC*)bl)->u.scr.guild_id);
- if (g)
- return g->emblem_id;
- }
- break;
+ case BL_PC:
+ return ((TBL_PC*)bl)->guild_emblem_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->guild_emblem_id;
+ break;
+ case BL_MOB: {
+ struct map_session_data *msd;
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md->guardian_data) //Guardian's guild [Skotlex]
+ return md->guardian_data->emblem_id;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->guild_emblem_id; //Alchemist's mobs [Skotlex]
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->guild_emblem_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->guild_emblem_id;
+ break;
+ case BL_NPC:
+ if (((TBL_NPC*)bl)->subtype == SCRIPT && ((TBL_NPC*)bl)->u.scr.guild_id > 0) {
+ struct guild *g = guild_search(((TBL_NPC*)bl)->u.scr.guild_id);
+ if (g)
+ return g->emblem_id;
+ }
+ break;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->guild_emblem_id;
+ break;
}
return 0;
}
@@ -5271,6 +5385,7 @@ struct view_data* status_get_viewdata(struct block_list *bl)
case BL_NPC: return ((TBL_NPC*)bl)->vd;
case BL_HOM: return ((TBL_HOM*)bl)->vd;
case BL_MER: return ((TBL_MER*)bl)->vd;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->vd;
}
return NULL;
}
@@ -5287,6 +5402,8 @@ void status_set_viewdata(struct block_list *bl, int class_)
vd = merc_get_hom_viewdata(class_);
else if (merc_class(class_))
vd = merc_get_viewdata(class_);
+ else if (elemental_class(class_))
+ vd = elemental_get_viewdata(class_);
else
vd = NULL;
@@ -5394,6 +5511,15 @@ void status_set_viewdata(struct block_list *bl, int class_)
ShowError("status_set_viewdata (MERCENARY): No view data for class %d\n", class_);
}
break;
+ case BL_ELEM:
+ {
+ struct elemental_data *ed = (struct elemental_data*)bl;
+ if (vd)
+ ed->vd = vd;
+ else
+ ShowError("status_set_viewdata (ELEMENTAL): No view data for class %d\n", class_);
+ }
+ break;
}
vd = status_get_viewdata(bl);
if (vd && vd->cloth_color && (
@@ -5405,15 +5531,15 @@ void status_set_viewdata(struct block_list *bl, int class_)
}
/// Returns the status_change data of bl or NULL if it doesn't exist.
-struct status_change *status_get_sc(struct block_list *bl)
-{
+struct status_change *status_get_sc(struct block_list *bl) {
if( bl )
switch (bl->type) {
- case BL_PC: return &((TBL_PC*)bl)->sc;
- case BL_MOB: return &((TBL_MOB*)bl)->sc;
- case BL_NPC: return &((TBL_NPC*)bl)->sc;
- case BL_HOM: return &((TBL_HOM*)bl)->sc;
- case BL_MER: return &((TBL_MER*)bl)->sc;
+ case BL_PC: return &((TBL_PC*)bl)->sc;
+ case BL_MOB: return &((TBL_MOB*)bl)->sc;
+ case BL_NPC: return &((TBL_NPC*)bl)->sc;
+ case BL_HOM: return &((TBL_HOM*)bl)->sc;
+ case BL_MER: return &((TBL_MER*)bl)->sc;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->sc;
}
return NULL;
}
@@ -9336,8 +9462,7 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
break;
case SC_BLOODSUCKER:
- if( --(sce->val4) >= 0 )
- {
+ if( --(sce->val4) >= 0 ) {
struct block_list *src = map_id2bl(sce->val2);
int damage;
if( !src || (src && (status_isdead(src) || src->m != bl->m || distance_bl(src, bl) >= 12)) )
diff --git a/src/map/status.h b/src/map/status.h
index 9b90c88b7..fd4c32319 100644
--- a/src/map/status.h
+++ b/src/map/status.h
@@ -592,6 +592,8 @@ typedef enum sc_type {
SC_SOULCOLD, //510
SC_HAWKEYES,
SC_ODINS_POWER,
+
+
SC_MAX, //Automatically updated max, used in for's to check we are within bounds.
} sc_type;
@@ -1038,7 +1040,7 @@ enum si_type {
SI_COLD = 437,
SI_GLOOMYDAY = 438,
SI_SONGOFMANA = 439,
- SI_CLOUD_KILL = 440,
+ SI_CLOUDKILL = 440,
SI_DANCEWITHWUG = 441,
SI_RUSHWINDMILL = 442,
SI_ECHOSONG = 443,
@@ -1059,10 +1061,10 @@ enum si_type {
SI_FREEZE_SP = 458,
SI_GN_TRAINING_SWORD = 459,
SI_GN_REMODELING_CART = 460,
- SI_GN_CARTBOOST = 461,
+ SI_CARTSBOOST = 461,
SI_FIXEDCASTINGTM_REDUCE = 462,
- SI_THORNS_TRAP = 463,
- SI_BLOOD_SUCKER = 464,
+ SI_THORNTRAP = 463,
+ SI_BLOODSUCKER = 464,
SI_SPORE_EXPLOSION = 465,
SI_DEMONIC_FIRE = 466,
SI_FIRE_EXPANSION_SMOKE_POWDER = 467,
@@ -1132,11 +1134,11 @@ enum si_type {
SI_STONE_SHIELD = 531,
SI_STONE_SHIELD_OPTION = 532,
SI_POWER_OF_GAIA = 533,
- SI_EL_WAIT = 534,
- SI_EL_PASSIVE = 535,
- SI_EL_DEFENSIVE = 536,
- SI_EL_OFFENSIVE = 537,
- SI_EL_COST = 538,
+ // SI_EL_WAIT = 534,
+ // SI_EL_PASSIVE = 535,
+ // SI_EL_DEFENSIVE = 536,
+ // SI_EL_OFFENSIVE = 537,
+ // SI_EL_COST = 538,
SI_PYROTECHNIC = 539,
SI_PYROTECHNIC_OPTION = 540,
SI_HEATER = 541,
@@ -1671,6 +1673,7 @@ int status_change_clear_buffs(struct block_list* bl, int type);
#define status_calc_pc(sd, first) status_calc_bl_(&(sd)->bl, SCB_ALL, first)
#define status_calc_homunculus(hd, first) status_calc_bl_(&(hd)->bl, SCB_ALL, first)
#define status_calc_mercenary(md, first) status_calc_bl_(&(md)->bl, SCB_ALL, first)
+#define status_calc_elemental(ed, first) status_calc_bl_(&(ed)->bl, SCB_ALL, first)
void status_calc_bl_(struct block_list *bl, enum scb_flag flag, bool first);
int status_calc_mob_(struct mob_data* md, bool first);
@@ -1678,6 +1681,7 @@ int status_calc_pet_(struct pet_data* pd, bool first);
int status_calc_pc_(struct map_session_data* sd, bool first);
int status_calc_homunculus_(struct homun_data *hd, bool first);
int status_calc_mercenary_(struct mercenary_data *md, bool first);
+int status_calc_elemental_(struct elemental_data *ed, bool first);
void status_calc_misc(struct block_list *bl, struct status_data *status, int level);
void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen);
diff --git a/src/map/unit.c b/src/map/unit.c
index 6224af787..2dd33af10 100644
--- a/src/map/unit.c
+++ b/src/map/unit.c
@@ -16,6 +16,7 @@
#include "homunculus.h"
#include "instance.h"
#include "mercenary.h"
+#include "elemental.h"
#include "skill.h"
#include "clif.h"
#include "duel.h"
@@ -50,6 +51,7 @@ struct unit_data* unit_bl2ud(struct block_list *bl)
if( bl->type == BL_NPC) return &((struct npc_data*)bl)->ud;
if( bl->type == BL_HOM) return &((struct homun_data*)bl)->ud;
if( bl->type == BL_MER) return &((struct mercenary_data*)bl)->ud;
+ if( bl->type == BL_ELEM) return &((struct elemental_data*)bl)->ud;
return NULL;
}
@@ -2006,148 +2008,155 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
skill_cleartimerskill(bl);
}
- switch( bl->type )
- {
- case BL_PC:
- {
- struct map_session_data *sd = (struct map_session_data*)bl;
-
- //Leave/reject all invitations.
- if(sd->chatID)
- chat_leavechat(sd,0);
- if(sd->trade_partner)
- trade_tradecancel(sd);
- vending_closevending(sd);
- buyingstore_close(sd);
- searchstore_close(sd);
- if(sd->state.storage_flag == 1)
- storage_storage_quit(sd,0);
- else if (sd->state.storage_flag == 2)
- storage_guild_storage_quit(sd,0);
- sd->state.storage_flag = 0; //Force close it when being warped.
- if(sd->party_invite>0)
- party_reply_invite(sd,sd->party_invite,0);
- if(sd->guild_invite>0)
- guild_reply_invite(sd,sd->guild_invite,0);
- if(sd->guild_alliance>0)
- guild_reply_reqalliance(sd,sd->guild_alliance_account,0);
- if(sd->menuskill_id)
- sd->menuskill_id = sd->menuskill_val = 0;
- if( sd->touching_id )
- npc_touchnext_areanpc(sd,true);
-
- sd->npc_shopid = 0;
- sd->adopt_invite = 0;
+ switch( bl->type ) {
+ case BL_PC: {
+ struct map_session_data *sd = (struct map_session_data*)bl;
- if(sd->pvp_timer != INVALID_TIMER) {
- delete_timer(sd->pvp_timer,pc_calc_pvprank_timer);
- sd->pvp_timer = INVALID_TIMER;
- sd->pvp_rank = 0;
- }
- if(sd->duel_group > 0)
- duel_leave(sd->duel_group, sd);
+ //Leave/reject all invitations.
+ if(sd->chatID)
+ chat_leavechat(sd,0);
+ if(sd->trade_partner)
+ trade_tradecancel(sd);
+ vending_closevending(sd);
+ buyingstore_close(sd);
+ searchstore_close(sd);
+ if(sd->state.storage_flag == 1)
+ storage_storage_quit(sd,0);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storage_quit(sd,0);
+ sd->state.storage_flag = 0; //Force close it when being warped.
+ if(sd->party_invite>0)
+ party_reply_invite(sd,sd->party_invite,0);
+ if(sd->guild_invite>0)
+ guild_reply_invite(sd,sd->guild_invite,0);
+ if(sd->guild_alliance>0)
+ guild_reply_reqalliance(sd,sd->guild_alliance_account,0);
+ if(sd->menuskill_id)
+ sd->menuskill_id = sd->menuskill_val = 0;
+ if( sd->touching_id )
+ npc_touchnext_areanpc(sd,true);
+
+ sd->npc_shopid = 0;
+ sd->adopt_invite = 0;
+
+ if(sd->pvp_timer != INVALID_TIMER) {
+ delete_timer(sd->pvp_timer,pc_calc_pvprank_timer);
+ sd->pvp_timer = INVALID_TIMER;
+ sd->pvp_rank = 0;
+ }
+ if(sd->duel_group > 0)
+ duel_leave(sd->duel_group, sd);
- if(pc_issit(sd)) {
- pc_setstand(sd);
- skill_sit(sd,0);
- }
- party_send_dot_remove(sd);//minimap dot fix [Kevin]
- guild_send_dot_remove(sd);
- bg_send_dot_remove(sd);
+ if(pc_issit(sd)) {
+ pc_setstand(sd);
+ skill_sit(sd,0);
+ }
+ party_send_dot_remove(sd);//minimap dot fix [Kevin]
+ guild_send_dot_remove(sd);
+ bg_send_dot_remove(sd);
- if( map[bl->m].users <= 0 || sd->state.debug_remove_map )
- {// this is only place where map users is decreased, if the mobs were removed too soon then this function was executed too many times [FlavioJS]
- if( sd->debug_file == NULL || !(sd->state.debug_remove_map) )
+ if( map[bl->m].users <= 0 || sd->state.debug_remove_map )
+ {// this is only place where map users is decreased, if the mobs were removed too soon then this function was executed too many times [FlavioJS]
+ if( sd->debug_file == NULL || !(sd->state.debug_remove_map) )
+ {
+ sd->debug_file = "";
+ sd->debug_line = 0;
+ sd->debug_func = "";
+ }
+ ShowDebug("unit_remove_map: unexpected state when removing player AID/CID:%d/%d"
+ " (active=%d connect_new=%d rewarp=%d changemap=%d debug_remove_map=%d)"
+ " from map=%s (users=%d)."
+ " Previous call from %s:%d(%s), current call from %s:%d(%s)."
+ " Please report this!!!\n",
+ sd->status.account_id, sd->status.char_id,
+ sd->state.active, sd->state.connect_new, sd->state.rewarp, sd->state.changemap, sd->state.debug_remove_map,
+ map[bl->m].name, map[bl->m].users,
+ sd->debug_file, sd->debug_line, sd->debug_func, file, line, func);
+ }
+ else
+ if (--map[bl->m].users == 0 && battle_config.dynamic_mobs) //[Skotlex]
+ map_removemobs(bl->m);
+ if( !(sd->sc.option&OPTION_INVISIBLE) )
+ {// decrement the number of active pvp players on the map
+ --map[bl->m].users_pvp;
+ }
+ if( map[bl->m].instance_id )
{
- sd->debug_file = "";
- sd->debug_line = 0;
- sd->debug_func = "";
+ instance[map[bl->m].instance_id].users--;
+ instance_check_idle(map[bl->m].instance_id);
}
- ShowDebug("unit_remove_map: unexpected state when removing player AID/CID:%d/%d"
- " (active=%d connect_new=%d rewarp=%d changemap=%d debug_remove_map=%d)"
- " from map=%s (users=%d)."
- " Previous call from %s:%d(%s), current call from %s:%d(%s)."
- " Please report this!!!\n",
- sd->status.account_id, sd->status.char_id,
- sd->state.active, sd->state.connect_new, sd->state.rewarp, sd->state.changemap, sd->state.debug_remove_map,
- map[bl->m].name, map[bl->m].users,
- sd->debug_file, sd->debug_line, sd->debug_func, file, line, func);
- }
- else
- if (--map[bl->m].users == 0 && battle_config.dynamic_mobs) //[Skotlex]
- map_removemobs(bl->m);
- if( !(sd->sc.option&OPTION_INVISIBLE) )
- {// decrement the number of active pvp players on the map
- --map[bl->m].users_pvp;
- }
- if( map[bl->m].instance_id )
- {
- instance[map[bl->m].instance_id].users--;
- instance_check_idle(map[bl->m].instance_id);
- }
- sd->state.debug_remove_map = 1; // temporary state to track double remove_map's [FlavioJS]
- sd->debug_file = file;
- sd->debug_line = line;
- sd->debug_func = func;
+ sd->state.debug_remove_map = 1; // temporary state to track double remove_map's [FlavioJS]
+ sd->debug_file = file;
+ sd->debug_line = line;
+ sd->debug_func = func;
- break;
- }
- case BL_MOB:
- {
- struct mob_data *md = (struct mob_data*)bl;
- // Drop previous target mob_slave_keep_target: no.
- if (!battle_config.mob_slave_keep_target)
- md->target_id=0;
+ break;
+ }
+ case BL_MOB: {
+ struct mob_data *md = (struct mob_data*)bl;
+ // Drop previous target mob_slave_keep_target: no.
+ if (!battle_config.mob_slave_keep_target)
+ md->target_id=0;
- md->attacked_id=0;
- md->state.skillstate= MSS_IDLE;
+ md->attacked_id=0;
+ md->state.skillstate= MSS_IDLE;
- break;
- }
- case BL_PET:
- {
- struct pet_data *pd = (struct pet_data*)bl;
- if( pd->pet.intimate <= 0 && !(pd->msd && !pd->msd->state.active) )
- { //If logging out, this is deleted on unit_free
- clif_clearunit_area(bl,clrtype);
- map_delblock(bl);
- unit_free(bl,CLR_OUTSIGHT);
- map_freeblock_unlock();
- return 0;
+ break;
}
+ case BL_PET: {
+ struct pet_data *pd = (struct pet_data*)bl;
+ if( pd->pet.intimate <= 0 && !(pd->msd && !pd->msd->state.active) )
+ { //If logging out, this is deleted on unit_free
+ clif_clearunit_area(bl,clrtype);
+ map_delblock(bl);
+ unit_free(bl,CLR_OUTSIGHT);
+ map_freeblock_unlock();
+ return 0;
+ }
- break;
- }
- case BL_HOM:
- {
- struct homun_data *hd = (struct homun_data *)bl;
- ud->canact_tick = ud->canmove_tick; //It appears HOM do reset the can-act tick.
- if( !hd->homunculus.intimacy && !(hd->master && !hd->master->state.active) )
- { //If logging out, this is deleted on unit_free
- clif_emotion(bl, E_SOB);
- clif_clearunit_area(bl,clrtype);
- map_delblock(bl);
- unit_free(bl,CLR_OUTSIGHT);
- map_freeblock_unlock();
- return 0;
+ break;
}
- break;
- }
- case BL_MER:
- {
- struct mercenary_data *md = (struct mercenary_data *)bl;
- ud->canact_tick = ud->canmove_tick;
- if( mercenary_get_lifetime(md) <= 0 && !(md->master && !md->master->state.active) )
- {
- clif_clearunit_area(bl,clrtype);
- map_delblock(bl);
- unit_free(bl,CLR_OUTSIGHT);
- map_freeblock_unlock();
- return 0;
+ case BL_HOM: {
+ struct homun_data *hd = (struct homun_data *)bl;
+ ud->canact_tick = ud->canmove_tick; //It appears HOM do reset the can-act tick.
+ if( !hd->homunculus.intimacy && !(hd->master && !hd->master->state.active) )
+ { //If logging out, this is deleted on unit_free
+ clif_emotion(bl, E_SOB);
+ clif_clearunit_area(bl,clrtype);
+ map_delblock(bl);
+ unit_free(bl,CLR_OUTSIGHT);
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
}
- break;
- }
- default: ;// do nothing
+ case BL_MER: {
+ struct mercenary_data *md = (struct mercenary_data *)bl;
+ ud->canact_tick = ud->canmove_tick;
+ if( mercenary_get_lifetime(md) <= 0 && !(md->master && !md->master->state.active) )
+ {
+ clif_clearunit_area(bl,clrtype);
+ map_delblock(bl);
+ unit_free(bl,CLR_OUTSIGHT);
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+ }
+ case BL_ELEM: {
+ struct elemental_data *ed = (struct elemental_data *)bl;
+ ud->canact_tick = ud->canmove_tick;
+ if( elemental_get_lifetime(ed) <= 0 && !(ed->master && !ed->master->state.active) )
+ {
+ clif_clearunit_area(bl,clrtype);
+ map_delblock(bl);
+ unit_free(bl,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+ }
+ default: break;// do nothing
}
/**
* BL_MOB is handled by mob_dead unless the monster is not dead.
@@ -2391,6 +2400,24 @@ int unit_free(struct block_list *bl, clr_type clrtype)
merc_contract_stop(md);
break;
}
+ case BL_ELEM: {
+ struct elemental_data *ed = (TBL_ELEM*)bl;
+ struct map_session_data *sd = ed->master;
+ if( clrtype >= 0 ) {
+ if( elemental_get_lifetime(ed) > 0 )
+ elemental_save(ed);
+ else {
+ intif_elemental_delete(ed->elemental.elemental_id);
+ if( sd )
+ sd->status.ele_id = 0;
+ }
+ }
+ if( sd )
+ sd->ed = NULL;
+
+ elemental_summon_stop(ed);
+ break;
+ }
}
skill_clear_unitgroup(bl);