diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/char/Makefile.in | 4 | ||||
-rw-r--r-- | src/char/char.c | 54 | ||||
-rw-r--r-- | src/char/int_elemental.c | 164 | ||||
-rw-r--r-- | src/char/int_elemental.h | 15 | ||||
-rw-r--r-- | src/char/inter.c | 6 | ||||
-rw-r--r-- | src/common/mmo.h | 19 | ||||
-rw-r--r-- | src/map/Makefile.in | 4 | ||||
-rw-r--r-- | src/map/atcommand.c | 3 | ||||
-rw-r--r-- | src/map/battle.c | 252 | ||||
-rw-r--r-- | src/map/chrif.c | 3 | ||||
-rw-r--r-- | src/map/clif.c | 305 | ||||
-rw-r--r-- | src/map/clif.h | 39 | ||||
-rw-r--r-- | src/map/elemental.c | 825 | ||||
-rw-r--r-- | src/map/elemental.h | 95 | ||||
-rw-r--r-- | src/map/intif.c | 97 | ||||
-rw-r--r-- | src/map/intif.h | 6 | ||||
-rw-r--r-- | src/map/itemdb.h | 1 | ||||
-rw-r--r-- | src/map/map.c | 11 | ||||
-rw-r--r-- | src/map/map.h | 6 | ||||
-rw-r--r-- | src/map/mob.c | 10 | ||||
-rw-r--r-- | src/map/npc_chat.c | 2 | ||||
-rw-r--r-- | src/map/pc.c | 11 | ||||
-rw-r--r-- | src/map/pc.h | 3 | ||||
-rw-r--r-- | src/map/script.c | 2 | ||||
-rw-r--r-- | src/map/skill.c | 2245 | ||||
-rw-r--r-- | src/map/skill.h | 91 | ||||
-rw-r--r-- | src/map/status.c | 477 | ||||
-rw-r--r-- | src/map/status.h | 22 | ||||
-rw-r--r-- | src/map/unit.c | 287 |
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); |