From a39f9ac8a26c27e8fec3d129a4668c5b501f11a9 Mon Sep 17 00:00:00 2001 From: aleos89 Date: Thu, 2 Oct 2014 12:49:04 -0400 Subject: [PATCH] Various cleanups. * Updated comments for chat.c and pet.c files. * Cleaned up various other comments and stylization. --- src/map/atcommand.c | 1 - src/map/cashshop.h | 7 +- src/map/chat.c | 169 +++++++---- src/map/guild.c | 1 - src/map/instance.h | 3 +- src/map/intif.c | 1 - src/map/intif.h | 2 - src/map/itemdb.h | 48 ++-- src/map/log.c | 1 - src/map/log.h | 16 +- src/map/mapreg.c | 1 - src/map/npc.c | 1 - src/map/npc.h | 4 +- src/map/npc_chat.c | 1 - src/map/pc_groups.c | 1 - src/map/pet.c | 644 ++++++++++++++++++++++++++++++++---------- src/map/quest.c | 74 ++--- src/map/script.c | 1 - src/map/searchstore.c | 108 +++++-- src/map/skill.c | 1 - src/map/storage.c | 133 +++++---- src/map/trade.c | 16 +- src/map/unit.c | 643 +++++++++++++++++++++++++++++------------ src/map/vending.c | 2 +- 24 files changed, 1320 insertions(+), 559 deletions(-) diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 3c53d50aaa..2f3882be4f 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -51,7 +51,6 @@ #include #include - #define ATCOMMAND_LENGTH 50 #define ACMD_FUNC(x) static int atcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message) diff --git a/src/map/cashshop.h b/src/map/cashshop.h index c97760b76a..e06c33f3ba 100644 --- a/src/map/cashshop.h +++ b/src/map/cashshop.h @@ -13,7 +13,8 @@ void cashshop_reloaddb( void ); bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, uint16* item_list ); // Taken from AEGIS -enum CASH_SHOP_TAB_CODE{ +enum CASH_SHOP_TAB_CODE +{ CASHSHOP_TAB_NEW = 0x0, CASHSHOP_TAB_POPULAR = 0x1, CASHSHOP_TAB_LIMITED = 0x2, @@ -26,7 +27,8 @@ enum CASH_SHOP_TAB_CODE{ }; // PACKET_ZC_SE_PC_BUY_CASHITEM_RESULT -enum CASHSHOP_BUY_RESULT{ +enum CASHSHOP_BUY_RESULT +{ CASHSHOP_RESULT_SUCCESS = 0x0, CASHSHOP_RESULT_ERROR_SYSTEM = 0x1, CASHSHOP_RESULT_ERROR_SHORTTAGE_CASH = 0x2, @@ -42,7 +44,6 @@ enum CASHSHOP_BUY_RESULT{ CASHSHOP_RESULT_ERROR_BUSY = 0xc, }; - struct cash_item_data{ unsigned short nameid; uint32 price; diff --git a/src/map/chat.c b/src/map/chat.c index aee8cae3a0..36e6f40cd3 100644 --- a/src/map/chat.c +++ b/src/map/chat.c @@ -19,7 +19,6 @@ #include #include - int chat_triggerevent(struct chat_data *cd); // forward declaration /// Initializes a chatroom object (common functionality for both pc and npc chatrooms). @@ -27,6 +26,7 @@ int chat_triggerevent(struct chat_data *cd); // forward declaration static struct chat_data* chat_createchat(struct block_list* bl, const char* title, const char* pass, int limit, bool pub, int trigger, const char* ev, int zeny, int minLvl, int maxLvl) { struct chat_data* cd; + nullpo_retr(NULL, bl); cd = (struct chat_data *) aMalloc(sizeof(struct chat_data)); @@ -51,8 +51,7 @@ static struct chat_data* chat_createchat(struct block_list* bl, const char* titl cd->bl.type = BL_CHAT; cd->bl.next = cd->bl.prev = NULL; - if( cd->bl.id == 0 ) - { + if( cd->bl.id == 0 ) { aFree(cd); cd = NULL; } @@ -65,30 +64,33 @@ static struct chat_data* chat_createchat(struct block_list* bl, const char* titl return cd; } -/*========================================== - * player chatroom creation - *------------------------------------------*/ +/** + * Player chat room creation. + * @param sd : player requesting + * @param title : title of chat room + * @param pass : password for chat room + * @param limit : amount allowed to enter + * @param pub : public or private + * @return 0 + */ int chat_createpcchat(struct map_session_data* sd, const char* title, const char* pass, int limit, bool pub) { struct chat_data* cd; + nullpo_ret(sd); if( sd->chatID ) return 0; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex] - if( sd->state.vending || sd->state.buyingstore ) - {// not chat, when you already have a store open + if( sd->state.vending || sd->state.buyingstore ) // not chat, when you already have a store open return 0; - } - if( map[sd->bl.m].flag.nochat ) - { + if( map[sd->bl.m].flag.nochat ) { clif_displaymessage(sd->fd, msg_txt(sd,281)); return 0; //Can't create chatrooms on this map. } - if( map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNOCHAT) ) - { + if( map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNOCHAT) ) { clif_displaymessage (sd->fd, msg_txt(sd,665)); return 0; } @@ -96,6 +98,7 @@ int chat_createpcchat(struct map_session_data* sd, const char* title, const char pc_stop_walking(sd,1); cd = chat_createchat(&sd->bl, title, pass, limit, pub, 0, "", 0, 1, MAX_LEVEL); + if( cd ) { cd->users = 1; cd->usersd[0] = sd; @@ -109,24 +112,27 @@ int chat_createpcchat(struct map_session_data* sd, const char* title, const char return 0; } -/*========================================== - * join an existing chatroom - *------------------------------------------*/ + /** + * Join an existing chat room. + * @param sd : player requesting + * @param chatid : ID of the chat room + * @param pass : password of chat room + * @return 0 + */ int chat_joinchat(struct map_session_data* sd, int chatid, const char* pass) { struct chat_data* cd; nullpo_ret(sd); + cd = (struct chat_data*)map_id2bl(chatid); - if( cd == NULL || cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->state.vending || sd->state.buyingstore || sd->chatID || ((cd->owner->type == BL_NPC) ? cd->users+1 : cd->users) >= cd->limit ) - { + if( cd == NULL || cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->state.vending || sd->state.buyingstore || sd->chatID || ((cd->owner->type == BL_NPC) ? cd->users+1 : cd->users) >= cd->limit ) { clif_joinchatfail(sd,0); return 0; } - if( !cd->pub && strncmp(pass, cd->pass, sizeof(cd->pass)) != 0 && !pc_has_permission(sd, PC_PERM_JOIN_ALL_CHAT) ) - { + if( !cd->pub && strncmp(pass, cd->pass, sizeof(cd->pass)) != 0 && !pc_has_permission(sd, PC_PERM_JOIN_ALL_CHAT) ) { clif_joinchatfail(sd,1); return 0; } @@ -165,13 +171,12 @@ int chat_joinchat(struct map_session_data* sd, int chatid, const char* pass) return 0; } - -/*========================================== - * Make player *sd leave a chatroom - * @param *sd : player pointer +/** + * Make player (sd) leave a chat room. + * @param sd : player requesting * @param kicked : for clif notification, kicked=1 or regular leave - * @return 0:sucess, 1:failed - *------------------------------------------*/ + * @return 0:success, 1:failed + */ int chat_leavechat(struct map_session_data* sd, bool kicked) { struct chat_data* cd; @@ -181,15 +186,14 @@ int chat_leavechat(struct map_session_data* sd, bool kicked) nullpo_retr(1, sd); cd = (struct chat_data*)map_id2bl(sd->chatID); - if( cd == NULL ) - { + + if( cd == NULL ) { pc_setchatid(sd, 0); return 1; } ARR_FIND( 0, cd->users, i, cd->usersd[i] == sd ); - if ( i == cd->users ) - { // Not found in the chatroom? + if ( i == cd->users ) { // Not found in the chatroom? pc_setchatid(sd, 0); return -1; } @@ -203,7 +207,6 @@ int chat_leavechat(struct map_session_data* sd, bool kicked) for( i = leavechar; i < cd->users; i++ ) cd->usersd[i] = cd->usersd[i+1]; - if( cd->users == 0 && cd->owner->type == BL_PC ) { // Delete empty chatroom struct skill_unit* unit; struct skill_unit_group* group; @@ -216,38 +219,39 @@ int chat_leavechat(struct map_session_data* sd, bool kicked) unit = map_find_skill_unit_oncell(&sd->bl, sd->bl.x, sd->bl.y, AL_WARP, NULL, 0); group = (unit != NULL) ? unit->group : NULL; + if (group != NULL) ext_skill_unit_onplace(unit, &sd->bl, group->tick); return 1; } - if( leavechar == 0 && cd->owner->type == BL_PC ) - { // Set and announce new owner + if( leavechar == 0 && cd->owner->type == BL_PC ) { // Set and announce new owner cd->owner = (struct block_list*) cd->usersd[0]; clif_changechatowner(cd, cd->usersd[0]); clif_clearchat(cd, 0); //Adjust Chat location after owner has been changed. map_delblock( &cd->bl ); - cd->bl.x=cd->usersd[0]->bl.x; - cd->bl.y=cd->usersd[0]->bl.y; + cd->bl.x = cd->usersd[0]->bl.x; + cd->bl.y = cd->usersd[0]->bl.y; + if(map_addblock( &cd->bl )) return 1; + clif_dispchat(cd,0); - } - else + } else clif_dispchat(cd,0); // refresh chatroom return 0; } -/*========================================== - * change a chatroom's owner - * @param *sd : player pointer - * @param *nextownername : string of new owner (name should be in chatroom) - * @return 0:sucess, 1:failure - *------------------------------------------*/ +/** + * Change a chat room's owner. + * @param sd : player requesting + * @param nextownername : string of new owner (name should be in chatroom) + * @return 0:success, 1:failure + */ int chat_changechatowner(struct map_session_data* sd, const char* nextownername) { struct chat_data* cd; @@ -257,6 +261,7 @@ int chat_changechatowner(struct map_session_data* sd, const char* nextownername) nullpo_retr(1, sd); cd = (struct chat_data*)map_id2bl(sd->chatID); + if( cd == NULL || (struct block_list*) sd != cd->owner ) return 1; @@ -280,6 +285,7 @@ int chat_changechatowner(struct map_session_data* sd, const char* nextownername) map_delblock( &cd->bl ); cd->bl.x = cd->owner->x; cd->bl.y = cd->owner->y; + if(map_addblock( &cd->bl )) return 1; @@ -289,9 +295,15 @@ int chat_changechatowner(struct map_session_data* sd, const char* nextownername) return 0; } -/*========================================== - * change a chatroom's status (title, etc) - *------------------------------------------*/ +/** + * Change a chat room's status (title, etc). + * @param sd : player requesting + * @param title : new title + * @param pass : new password + * @param limit : new limit + * @param pub : public or private + * @return 1:success, 0:failure + */ int chat_changechatstatus(struct map_session_data* sd, const char* title, const char* pass, int limit, bool pub) { struct chat_data* cd; @@ -299,7 +311,8 @@ int chat_changechatstatus(struct map_session_data* sd, const char* title, const nullpo_retr(1, sd); cd = (struct chat_data*)map_id2bl(sd->chatID); - if( cd==NULL || (struct block_list *)sd != cd->owner ) + + if( cd == NULL || (struct block_list *)sd != cd->owner ) return 1; safestrncpy(cd->title, title, CHATROOM_TITLE_SIZE); @@ -313,9 +326,12 @@ int chat_changechatstatus(struct map_session_data* sd, const char* title, const return 0; } -/*========================================== - * kick an user from a chatroom - *------------------------------------------*/ +/** + * Kick a member from a chat room. + * @param sd : player requesting + * @param kickusername : player name to be kicked + * @retur 1:success, 0:failure + */ int chat_kickchat(struct map_session_data* sd, const char* kickusername) { struct chat_data* cd; @@ -325,7 +341,7 @@ int chat_kickchat(struct map_session_data* sd, const char* kickusername) cd = (struct chat_data *)map_id2bl(sd->chatID); - if( cd==NULL || (struct block_list *)sd != cd->owner ) + if( cd == NULL || (struct block_list *)sd != cd->owner ) return -1; ARR_FIND( 0, cd->users, i, strncmp(cd->usersd[i]->status.name, kickusername, NAME_LENGTH) == 0 ); @@ -338,13 +354,27 @@ int chat_kickchat(struct map_session_data* sd, const char* kickusername) idb_put(cd->kick_list,cd->usersd[i]->status.char_id,(void*)1); chat_leavechat(cd->usersd[i],1); + return 0; } -/// Creates a chat room for the npc. +/** + * Creates a chat room for a NPC. + * @param nd : NPC requesting + * @param title : title of chat room + * @param limit : limit of users in chat room + * @param pub : public or private + * @param trigger : event trigger + * @param ev : event name + * @param zeny : zeny cost + * @param minLvl : minimum level to enter + * @param maxLvl : maximum level to enter + * @return 0 + */ int chat_createnpcchat(struct npc_data* nd, const char* title, int limit, bool pub, int trigger, const char* ev, int zeny, int minLvl, int maxLvl) { struct chat_data* cd; + nullpo_ret(nd); if( nd->chat_id ) { @@ -367,13 +397,18 @@ int chat_createnpcchat(struct npc_data* nd, const char* title, int limit, bool p return 0; } -/// Removes the chatroom from the npc. +/** + * Removes a chat room for a NPC. + * @param nd : NPC requesting + */ int chat_deletenpcchat(struct npc_data* nd) { struct chat_data *cd; + nullpo_ret(nd); cd = (struct chat_data*)map_id2bl(nd->chat_id); + if( cd == NULL ) return 0; @@ -387,39 +422,53 @@ int chat_deletenpcchat(struct npc_data* nd) return 0; } -/*========================================== - * Trigger npc event when we enter the chatroom - *------------------------------------------*/ + /** + * Trigger NPC event when entering the chat room. + * @param cd : chat room to trigger event + * @return 0 + */ int chat_triggerevent(struct chat_data *cd) { nullpo_ret(cd); if( cd->users >= cd->trigger && cd->npc_event[0] ) npc_event_do(cd->npc_event); + return 0; } -/// Enables the event of the chat room. -/// At most, 127 users are needed to trigger the event. + /** + * Enables the event of the chat room. + * At most, 127 users are needed to trigger the event. + * @param cd : chat room to trigger event + */ int chat_enableevent(struct chat_data* cd) { nullpo_ret(cd); cd->trigger &= 0x7f; chat_triggerevent(cd); + return 0; } -/// Disables the event of the chat room +/** + * Disables the event of the chat room. + * @param cd : chat room to trigger event + */ int chat_disableevent(struct chat_data* cd) { nullpo_ret(cd); cd->trigger |= 0x80; + return 0; } -/// Kicks all the users from the chat room. +/** + * Kicks all the users from the chat room. + * @param cd : chat room to trigger event + */ int chat_npckickall(struct chat_data* cd) { nullpo_ret(cd); diff --git a/src/map/guild.c b/src/map/guild.c index 3987a093ba..8ab4f40a30 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -29,7 +29,6 @@ #include #include - static DBMap* guild_db; // int guild_id -> struct guild* static DBMap* castle_db; // int castle_id -> struct guild_castle* static DBMap* guild_expcache_db; // int char_id -> struct guild_expcache* diff --git a/src/map/instance.h b/src/map/instance.h index 13660588f8..7d69749ea1 100644 --- a/src/map/instance.h +++ b/src/map/instance.h @@ -11,7 +11,8 @@ typedef enum instance_state { INSTANCE_FREE, INSTANCE_IDLE, INSTANCE_BUSY } instance_state; -struct instance_data { +struct instance_data +{ short type, cnt_map; int state; int party_id; diff --git a/src/map/intif.c b/src/map/intif.c index e9a21f7ba5..4e360a8768 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -32,7 +32,6 @@ #include #include - static const int packet_len_table[]={ -1,-1,27,-1, -1, 0,37,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3800-0x380f 0, 0, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, //0x3810 diff --git a/src/map/intif.h b/src/map/intif.h index b45dbca3d3..1af332b57b 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -30,7 +30,6 @@ int intif_request_registry(struct map_session_data *sd, int flag); int intif_request_guild_storage(int account_id, int guild_id); int intif_send_guild_storage(int account_id, struct guild_storage *gstor); - int intif_create_party(struct party_member *member,char *name,int item,int item2); int intif_request_partyinfo(int party_id, int char_id); @@ -43,7 +42,6 @@ int intif_party_message(int party_id, int account_id, const char *mes,int len); int intif_party_leaderchange(int party_id,int account_id,int char_id); int intif_party_sharelvlupdate(unsigned int share_lvl); - int intif_guild_create(const char *name, const struct guild_member *master); int intif_guild_request_info(int guild_id); int intif_guild_addmember(int guild_id, struct guild_member *m); diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 1b2de2ad12..5286a29374 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -33,7 +33,8 @@ #define itemdb_isspecial(i) (i == CARD0_FORGE || i == CARD0_CREATE || i == CARD0_PET) ///Enum of item id (for hardcoded purpose) -enum item_itemid { +enum item_itemid +{ ITEMID_RED_POTION = 501, ITEMID_YELLOW_POTION = 503, ITEMID_WHITE_POTION = 504, @@ -122,7 +123,8 @@ enum item_itemid { }; ///Mercenary Scrolls -enum mercenary_scroll_item_list { +enum mercenary_scroll_item_list +{ ITEMID_BOW_MERCENARY_SCROLL1 = 12153, ITEMID_BOW_MERCENARY_SCROLL2, ITEMID_BOW_MERCENARY_SCROLL3, @@ -156,7 +158,8 @@ enum mercenary_scroll_item_list { }; ///Rune Knight -enum rune_item_list { +enum rune_item_list +{ ITEMID_NAUTHIZ = 12725, ITEMID_RAIDO, ITEMID_BERKANA, @@ -170,7 +173,8 @@ enum rune_item_list { }; ///Mechanic -enum mechanic_item_list { +enum mechanic_item_list +{ ITEMID_ACCELERATOR = 2800, ITEMID_HOVERING_BOOSTER, ITEMID_SUICIDAL_DEVICE, @@ -192,7 +196,8 @@ enum mechanic_item_list { }; ///Genetic -enum genetic_item_list { +enum genetic_item_list +{ ITEMID_SEED_OF_HORNY_PLANT = 6210, ITEMID_BLOODSUCK_PLANT_SEED = 6211, ITEMID_BOMB_MUSHROOM_SPORE = 6212, @@ -247,7 +252,8 @@ enum genetic_item_list { }; ///Guillotine Cross -enum poison_item_list { +enum poison_item_list +{ ITEMID_PARALYSE = 12717, ITEMID_LEECHESEND, ITEMID_OBLIVIONCURSE, @@ -259,7 +265,8 @@ enum poison_item_list { }; ///Spell Books -enum spell_book_item_list { +enum spell_book_item_list +{ ITEMID_MAGIC_BOOK_FB = 6189, ITEMID_MAGIC_BOOK_CB, ITEMID_MAGIC_BOOK_LB, @@ -280,7 +287,8 @@ enum spell_book_item_list { }; ///Cash Food -enum cash_food_item_list { +enum cash_food_item_list +{ ITEMID_STR_DISH10_ = 12202, ITEMID_AGI_DISH10_, ITEMID_INT_DISH10_, @@ -290,12 +298,14 @@ enum cash_food_item_list { }; ///Item No Use List -enum item_nouse_list { +enum item_nouse_list +{ NOUSE_SITTING = 0x01, }; ///Item job -enum e_item_job { +enum e_item_job +{ ITEMJ_NORMAL = 0x01, ITEMJ_UPPER = 0x02, ITEMJ_BABY = 0x04, @@ -304,7 +314,8 @@ enum e_item_job { ITEMJ_THIRD_BABY = 0x20, }; -enum e_item_ammo { +enum e_item_ammo +{ AMMO_ARROW = 1, AMMO_THROWABLE_DAGGER, AMMO_BULLET, @@ -317,7 +328,8 @@ enum e_item_ammo { }; ///Item combo struct -struct item_combo { +struct item_combo +{ struct script_code *script; unsigned short *nameid;/* nameid array */ unsigned char count; @@ -327,7 +339,8 @@ struct item_combo { /// Struct of item group entry -struct s_item_group_entry { +struct s_item_group_entry +{ unsigned short nameid, /// Item ID duration, /// Duration if item as rental item (in minutes) amount; /// Amount of item will be obtained @@ -337,13 +350,15 @@ struct s_item_group_entry { }; /// Struct of random group -struct s_item_group_random { +struct s_item_group_random +{ struct s_item_group_entry *data; /// Random group entry unsigned short data_qty; /// Number of item in random group }; /// Struct of item group that will be used for db -struct s_item_group_db { +struct s_item_group_db +{ unsigned short id, /// Item Group ID must_qty; /// Number of must item at this group struct s_item_group_entry *must; /// Must item entry @@ -351,7 +366,8 @@ struct s_item_group_db { }; ///Main item data struct -struct item_data { +struct item_data +{ unsigned short nameid; char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH]; diff --git a/src/map/log.c b/src/map/log.c index 3a55efa869..359ec4d4da 100644 --- a/src/map/log.c +++ b/src/map/log.c @@ -17,7 +17,6 @@ #include #include - /// filters for item logging typedef enum e_log_filter { diff --git a/src/map/log.h b/src/map/log.h index 1970551548..71b27f6b03 100644 --- a/src/map/log.h +++ b/src/map/log.h @@ -10,7 +10,6 @@ struct map_session_data; struct mob_data; struct item; - typedef enum e_log_chat_type { LOG_CHAT_GLOBAL = 0x01, @@ -20,9 +19,7 @@ typedef enum e_log_chat_type LOG_CHAT_MAINCHAT = 0x10, // all LOG_CHAT_ALL = 0xFF, -} -e_log_chat_type; - +} e_log_chat_type; typedef enum e_log_pick_type { @@ -50,13 +47,13 @@ typedef enum e_log_pick_type LOG_TYPE_LOOT = LOG_TYPE_PICKDROP_MONSTER|LOG_TYPE_CONSUME, // all LOG_TYPE_ALL = 0xFFFFF, -} -e_log_pick_type; +} e_log_pick_type; -typedef enum e_log_cash_type{ +typedef enum e_log_cash_type +{ LOG_CASH_TYPE_CASH = 0x1, LOG_CASH_TYPE_KAFRA = 0x2 -}e_log_cash_type; +} e_log_cash_type; /// new logs void log_pick_pc(struct map_session_data* sd, e_log_pick_type type, int amount, struct item* itm); @@ -83,8 +80,7 @@ extern struct Log_Config int rare_items_log,refine_items_log,price_items_log,amount_items_log; //for filter int branch, mvpdrop, zeny, commands, npc, chat; char log_branch[64], log_pick[64], log_zeny[64], log_mvpdrop[64], log_gm[64], log_npc[64], log_chat[64], log_cash[64]; -} -log_config; +} log_config; #ifdef BETA_THREAD_TEST struct { diff --git a/src/map/mapreg.c b/src/map/mapreg.c index 7782f7f020..ca3b185bcb 100644 --- a/src/map/mapreg.c +++ b/src/map/mapreg.c @@ -20,7 +20,6 @@ static char mapreg_table[32] = "mapreg"; static bool mapreg_dirty = false; #define MAPREG_AUTOSAVE_INTERVAL (300*1000) - /// Looks up the value of an integer variable using its uid. int mapreg_readreg(int uid) { diff --git a/src/map/npc.c b/src/map/npc.c index 9bf6fd69fd..da86cdfb9e 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -36,7 +36,6 @@ #include #include - struct npc_data* fake_nd; // linked list of npc source files diff --git a/src/map/npc.h b/src/map/npc.h index 2ae2887c7d..0b56bb1b94 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -7,11 +7,11 @@ #include "map.h" // struct block_list #include "status.h" // struct status_change #include "unit.h" // struct unit_data + struct block_list; struct npc_data; struct view_data; - struct npc_timerevent_list { int timer,pos; }; @@ -76,8 +76,6 @@ struct npc_data { } u; }; - - #define START_NPC_NUM 110000000 enum actor_classes diff --git a/src/map/npc_chat.c b/src/map/npc_chat.c index 39a3a85842..92bbf6dda0 100644 --- a/src/map/npc_chat.c +++ b/src/map/npc_chat.c @@ -21,7 +21,6 @@ #include #include - /** * Written by MouseJstr in a vision... (2/21/2005) * diff --git a/src/map/pc_groups.c b/src/map/pc_groups.c index c5f79c04e3..838fcc5eb7 100644 --- a/src/map/pc_groups.c +++ b/src/map/pc_groups.c @@ -13,7 +13,6 @@ #include "pc_groups.h" #include "pc.h" // e_pc_permission - typedef struct GroupSettings GroupSettings; // Cached config settings/pointers for quick lookup diff --git a/src/map/pet.c b/src/map/pet.c index d27be6c9ca..1f9d130797 100644 --- a/src/map/pet.c +++ b/src/map/pet.c @@ -33,7 +33,6 @@ #include #include - #define MIN_PETTHINKTIME 100 struct s_pet_db pet_db[MAX_PET_DB]; @@ -41,6 +40,11 @@ struct s_pet_db pet_db[MAX_PET_DB]; static struct eri *item_drop_ers; //For loot drops delay structures. static struct eri *item_drop_list_ers; +/** + * Get the value of the pet's hunger. + * @param pd : pet requesting + * @return Pet's hunger value + */ int pet_hungry_val(struct pet_data *pd) { nullpo_ret(pd); @@ -57,25 +61,43 @@ int pet_hungry_val(struct pet_data *pd) return 0; } +/** + * Set the value of the pet's intimacy. + * @param pd : pet requesting + * @param value : new intimacy value + */ void pet_set_intimate(struct pet_data *pd, int value) { int intimate; struct map_session_data *sd; nullpo_retv(pd); + intimate = pd->pet.intimate; sd = pd->master; pd->pet.intimate = value; + if( (intimate >= battle_config.pet_equip_min_friendly && pd->pet.intimate < battle_config.pet_equip_min_friendly) || (intimate < battle_config.pet_equip_min_friendly && pd->pet.intimate >= battle_config.pet_equip_min_friendly) ) status_calc_pc(sd,SCO_NONE); } +/** + * Create a pet egg. + * @param sd : player requesting + * @param item_id : item ID of tamer + * @return 1:success, 0:failure + */ int pet_create_egg(struct map_session_data *sd, unsigned short item_id) { int pet_id = search_petDB_index(item_id, PET_EGG); - if (pet_id < 0) return 0; //No pet egg here. - if (!pc_inventoryblank(sd)) return 0; // Inventory full + + if (pet_id < 0) + return 0; //No pet egg here. + + if (!pc_inventoryblank(sd)) + return 0; // Inventory full + sd->catch_target_class = pet_db[pet_id].class_; intif_create_pet(sd->status.account_id, sd->status.char_id, (short)pet_db[pet_id].class_, @@ -83,22 +105,32 @@ int pet_create_egg(struct map_session_data *sd, unsigned short item_id) (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate, 100, 0, 1, pet_db[pet_id].jname); + return 1; } +/** + * Make pet drop target. + * @param pd : pet requesting + * @return 0 + */ int pet_unlocktarget(struct pet_data *pd) { nullpo_ret(pd); - pd->target_id=0; + pd->target_id = 0; pet_stop_attack(pd); pet_stop_walking(pd,1); + return 0; } -/*========================================== - * Pet Attack Skill [Skotlex] - *------------------------------------------*/ +/** + * Make pet use attack skill. + * @param pd : pet requesting + * @param target_id : ID of target + * @author [Skotlex] + */ int pet_attackskill(struct pet_data *pd, int target_id) { if (!battle_config.pet_status_support || !pd->a_skill || @@ -108,26 +140,36 @@ int pet_attackskill(struct pet_data *pd, int target_id) if (DIFF_TICK(pd->ud.canact_tick, gettick()) > 0) return 0; - if (rnd()%100 < (pd->a_skill->rate +pd->pet.intimate*pd->a_skill->bonusrate/1000)) - { //Skotlex: Use pet's skill + if (rnd()%100 < (pd->a_skill->rate +pd->pet.intimate*pd->a_skill->bonusrate/1000)) { // Skotlex: Use pet's skill int inf; struct block_list *bl; - bl=map_id2bl(target_id); + bl = map_id2bl(target_id); + if(bl == NULL || pd->bl.m != bl->m || bl->prev == NULL || status_isdead(bl) || !check_distance_bl(&pd->bl, bl, pd->db->range3)) return 0; inf = skill_get_inf(pd->a_skill->id); + if (inf & INF_GROUND_SKILL) unit_skilluse_pos(&pd->bl, bl->x, bl->y, pd->a_skill->id, pd->a_skill->lv); else //Offensive self skill? Could be stuff like GX. unit_skilluse_id(&pd->bl,(inf&INF_SELF_SKILL?pd->bl.id:bl->id), pd->a_skill->id, pd->a_skill->lv); + return 1; //Skill invoked. } + return 0; } +/** + * Make sure pet can attack from given config values. + * @param sd : player requesting (owner) + * @param bl : target + * @param type : pet's attack rate type + * @return 0 + */ int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type) { struct pet_data *pd; @@ -152,31 +194,38 @@ int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type) if(!type) { rate = pd->petDB->attack_rate; - rate = rate * pd->rate_fix/1000; + rate = rate * pd->rate_fix / 1000; + if(pd->petDB->attack_rate > 0 && rate <= 0) rate = 1; } else { rate = pd->petDB->defence_attack_rate; - rate = rate * pd->rate_fix/1000; + rate = rate * pd->rate_fix / 1000; + if(pd->petDB->defence_attack_rate > 0 && rate <= 0) rate = 1; } - if(rnd()%10000 < rate) - { + + if(rnd()%10000 < rate) { if(pd->target_id == 0 || rnd()%10000 < pd->petDB->change_target_rate) pd->target_id = bl->id; } return 0; } -/*========================================== - * Pet SC Check [Skotlex] - *------------------------------------------*/ + +/** + * Status change check for pets. + * @param sd : player requesting + * @param type : recovery type + * @author [Skotlex] + */ int pet_sc_check(struct map_session_data *sd, int type) { struct pet_data *pd; nullpo_ret(sd); + pd = sd->pd; if( pd == NULL @@ -191,13 +240,21 @@ int pet_sc_check(struct map_session_data *sd, int type) return 0; } +/** + * Update pet's hungry value and timer and adjust intimacy based on hunger. + * @param tid : current timer value + * @param tick : how often to update + * @param id : ID of pet owner + * @return 0 + */ static int pet_hungry(int tid, unsigned int tick, int id, intptr_t data) { struct map_session_data *sd; struct pet_data *pd; int interval; - sd=map_id2sd(id); + sd = map_id2sd(id); + if(!sd) return 1; @@ -205,50 +262,62 @@ static int pet_hungry(int tid, unsigned int tick, int id, intptr_t data) return 1; pd = sd->pd; - if(pd->pet_hungry_timer != tid){ + + if(pd->pet_hungry_timer != tid) { ShowError("pet_hungry_timer %d != %d\n",pd->pet_hungry_timer,tid); return 0; } + pd->pet_hungry_timer = INVALID_TIMER; if (pd->pet.intimate <= 0) return 1; //You lost the pet already, the rest is irrelevant. pd->pet.hungry--; - if( pd->pet.hungry < 0 ) - { + + if( pd->pet.hungry < 0 ) { pet_stop_attack(pd); pd->pet.hungry = 0; pet_set_intimate(pd, pd->pet.intimate - battle_config.pet_hungry_friendly_decrease); - if( pd->pet.intimate <= 0 ) - { + + if( pd->pet.intimate <= 0 ) { pd->pet.intimate = 0; pd->status.speed = pd->db->status.speed; } + status_calc_pet(pd,SCO_NONE); clif_send_petdata(sd,pd,1,pd->pet.intimate); } + clif_send_petdata(sd,pd,2,pd->pet.hungry); if(battle_config.pet_hungry_delay_rate != 100) interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100; else interval = pd->petDB->hungry_delay; + if(interval <= 0) interval = 1; + pd->pet_hungry_timer = add_timer(tick+interval,pet_hungry,sd->bl.id,0); return 0; } +/** + * Search pet database for given value and type. + * @param key : value to search for + * @param type : pet type to search for (Class, Catch, Egg, Equip, Food) + * @return Pet ID on success, -1 on failure + */ int search_petDB_index(int key,int type) { int i; - for( i = 0; i < MAX_PET_DB; i++ ) - { + for( i = 0; i < MAX_PET_DB; i++ ) { if(pet_db[i].class_ <= 0) continue; + switch(type) { case PET_CLASS: if(pet_db[i].class_ == key) return i; break; case PET_CATCH: if(pet_db[i].itemID == key) return i; break; @@ -259,12 +328,19 @@ int search_petDB_index(int key,int type) return -1; } } + return -1; } +/** + * Remove hunger timer from pet. + * @param pd : pet requesting + * @return 1 + */ int pet_hungry_timer_delete(struct pet_data *pd) { nullpo_ret(pd); + if(pd->pet_hungry_timer != INVALID_TIMER) { delete_timer(pd->pet_hungry_timer,pet_hungry); pd->pet_hungry_timer = INVALID_TIMER; @@ -273,12 +349,18 @@ int pet_hungry_timer_delete(struct pet_data *pd) return 1; } +/** + * Make a pet perform/dance. + * @param sd : player requesting + * @param pd : pet requesting + * @return 1 + */ static int pet_performance(struct map_session_data *sd, struct pet_data *pd) { int val; if (pd->pet.intimate > 900) - val = (pd->petDB->s_perfor > 0)? 4:3; + val = (pd->petDB->s_perfor > 0) ? 4 : 3; else if(pd->pet.intimate > 750) //TODO: this is way too high val = 2; else @@ -287,9 +369,16 @@ static int pet_performance(struct map_session_data *sd, struct pet_data *pd) pet_stop_walking(pd,2000<<8); clif_pet_performance(pd, rnd()%val + 1); pet_lootitem_drop(pd,NULL); + return 1; } +/** + * Return a pet to it's egg. + * @param sd : player requesting + * @param pd : pet requesting + * @return 1 + */ static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) { struct item tmp_item; @@ -303,10 +392,12 @@ static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) tmp_item.card[1] = GetWord(pd->pet.pet_id,0); tmp_item.card[2] = GetWord(pd->pet.pet_id,1); tmp_item.card[3] = pd->pet.rename_flag; + if((flag = pc_additem(sd,&tmp_item,1,LOG_TYPE_OTHER))) { clif_additem(sd,0,0,flag); map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); } + pd->pet.incubate = 1; unit_free(&pd->bl,CLR_OUTSIGHT); @@ -316,10 +407,15 @@ static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) return 1; } +/** + * Load initial pet data when hatching/creating. + * @param sd : player requesting + * @param pet : pet requesting + */ int pet_data_init(struct map_session_data *sd, struct s_pet *pet) { struct pet_data *pd; - int i=0,interval=0; + int i = 0, interval = 0; nullpo_retr(1, sd); @@ -327,16 +423,20 @@ int pet_data_init(struct map_session_data *sd, struct s_pet *pet) if(sd->status.account_id != pet->account_id || sd->status.char_id != pet->char_id) { sd->status.pet_id = 0; + return 1; } + if (sd->status.pet_id != pet->pet_id) { if (sd->status.pet_id) { //Wrong pet?? Set incubate to no and send it back for saving. pet->incubate = 1; intif_save_petdata(sd->status.account_id,pet); sd->status.pet_id = 0; + return 1; } + //The pet_id value was lost? odd... restore it. sd->status.pet_id = pet->pet_id; } @@ -344,8 +444,10 @@ int pet_data_init(struct map_session_data *sd, struct s_pet *pet) i = search_petDB_index(pet->class_,PET_CLASS); if(i < 0) { sd->status.pet_id = 0; + return 1; } + sd->pd = pd = (struct pet_data *)aCalloc(1,sizeof(struct pet_data)); pd->bl.type = BL_PET; pd->bl.id = npc_get_new_npc_id(); @@ -386,11 +488,18 @@ int pet_data_init(struct map_session_data *sd, struct s_pet *pet) if( interval <= 0 ) interval = 1; + pd->pet_hungry_timer = add_timer(gettick() + interval, pet_hungry, sd->bl.id, 0); pd->masterteleport_timer = INVALID_TIMER; + return 0; } +/** + * Begin hatching a pet. + * @param sd : player requesting + * @param pet : pet requesting + */ int pet_birth_process(struct map_session_data *sd, struct s_pet *pet) { nullpo_retr(1, sd); @@ -399,6 +508,7 @@ int pet_birth_process(struct map_session_data *sd, struct s_pet *pet) if(sd->status.pet_id && pet->incubate == 1) { sd->status.pet_id = 0; + return 1; } @@ -406,60 +516,82 @@ int pet_birth_process(struct map_session_data *sd, struct s_pet *pet) pet->account_id = sd->status.account_id; pet->char_id = sd->status.char_id; sd->status.pet_id = pet->pet_id; + if(pet_data_init(sd, pet)) { sd->status.pet_id = 0; + return 1; } intif_save_petdata(sd->status.account_id,pet); + if (save_settings&8) chrif_save(sd,0); //is it REALLY Needed to save the char for hatching a pet? [Skotlex] if(sd->bl.prev != NULL) { if(map_addblock(&sd->pd->bl)) return 1; + clif_spawn(&sd->pd->bl); clif_send_petdata(sd,sd->pd, 0,0); clif_send_petdata(sd,sd->pd, 5,battle_config.pet_hair_style); clif_pet_equip_area(sd->pd); clif_send_petstatus(sd); } + Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->master == sd); return 0; } +/** + * Finalize hatching process and load pet to client. + * @param account_id : account ID of owner + * @param p : pet requesting + * @param flag : 1:stop loading of pet + * @return 0:success, 1:failure + */ int pet_recv_petdata(int account_id,struct s_pet *p,int flag) { struct map_session_data *sd; sd = map_id2sd(account_id); + if(sd == NULL) return 1; + if(flag == 1) { sd->status.pet_id = 0; + return 1; } + if(p->incubate == 1) { int i; + //Delete egg from inventory. [Skotlex] for (i = 0; i < MAX_INVENTORY; i++) { if(sd->status.inventory[i].card[0] == CARD0_PET && p->pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2])) break; } + if(i >= MAX_INVENTORY) { ShowError("pet_recv_petdata: Hatching pet (%d:%s) aborted, couldn't find egg in inventory for removal!\n",p->pet_id, p->name); sd->status.pet_id = 0; + return 1; } + if (!pet_birth_process(sd,p)) //Pet hatched. Delete egg. pc_delitem(sd,i,1,0,0,LOG_TYPE_OTHER); } else { pet_data_init(sd,p); + if(sd->pd && sd->bl.prev != NULL) { if(map_addblock(&sd->pd->bl)) return 1; + clif_spawn(&sd->pd->bl); clif_send_petdata(sd,sd->pd,0,0); clif_send_petdata(sd,sd->pd,5,battle_config.pet_hair_style); @@ -471,6 +603,12 @@ int pet_recv_petdata(int account_id,struct s_pet *p,int flag) return 0; } +/** + * Selected pet egg to hatch. + * @param sd : player requesting + * @param egg_index : egg index value in inventory + * @return 0 + */ int pet_select_egg(struct map_session_data *sd,short egg_index) { nullpo_ret(sd); @@ -486,6 +624,12 @@ int pet_select_egg(struct map_session_data *sd,short egg_index) return 0; } +/** + * Display the success/failure roulette wheel when trying to catch monster. + * @param sd : player requesting + * @param target_class : monster ID of pet to catch + * @return 0 + */ int pet_catch_process1(struct map_session_data *sd,int target_class) { nullpo_ret(sd); @@ -496,6 +640,12 @@ int pet_catch_process1(struct map_session_data *sd,int target_class) return 0; } +/** + * Begin the actual catching process of a monster. + * @param sd : player requesting + * @param target_id : monster ID of pet to catch + * @return 0:success, 1:failure + */ int pet_catch_process2(struct map_session_data* sd, int target_id) { struct mob_data* md; @@ -504,8 +654,8 @@ int pet_catch_process2(struct map_session_data* sd, int target_id) nullpo_retr(1, sd); md = (struct mob_data*)map_id2bl(target_id); - if(!md || md->bl.type != BL_MOB || md->bl.prev == NULL) - { // Invalid inputs/state, abort capture. + + if(!md || md->bl.type != BL_MOB || md->bl.prev == NULL) { // Invalid inputs/state, abort capture. clif_pet_roulette(sd,0); sd->catch_target_class = -1; sd->itemid = sd->itemindex = -1; @@ -515,32 +665,34 @@ int pet_catch_process2(struct map_session_data* sd, int target_id) //FIXME: delete taming item here, if this was an item-invoked capture and the item was flagged as delay-consume [ultramage] i = search_petDB_index(md->mob_id,PET_CLASS); + //catch_target_class == 0 is used for universal lures (except bosses for now). [Skotlex] if (sd->catch_target_class == 0 && !(md->status.mode&MD_BOSS)) sd->catch_target_class = md->mob_id; + if(i < 0 || sd->catch_target_class != md->mob_id) { clif_emotion(&md->bl, E_AG); //mob will do /ag if wrong lure is used on them. clif_pet_roulette(sd,0); sd->catch_target_class = -1; + return 1; } pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - md->level)*30 + sd->battle_status.luk*20)*(200 - get_percentage(md->status.hp, md->status.max_hp))/100; - if(pet_catch_rate < 1) pet_catch_rate = 1; + if(pet_catch_rate < 1) + pet_catch_rate = 1; + if(battle_config.pet_catch_rate != 100) pet_catch_rate = (pet_catch_rate*battle_config.pet_catch_rate)/100; - if(rnd()%10000 < pet_catch_rate) - { + if(rnd()%10000 < pet_catch_rate) { unit_remove_map(&md->bl,CLR_OUTSIGHT); status_kill(&md->bl); clif_pet_roulette(sd,1); intif_create_pet(sd->status.account_id,sd->status.char_id,pet_db[i].class_,mob_db(pet_db[i].class_)->lv, pet_db[i].EggID,0,pet_db[i].intimate,100,0,1,pet_db[i].jname); - } - else - { + } else { clif_pet_roulette(sd,0); sd->catch_target_class = -1; } @@ -550,12 +702,12 @@ int pet_catch_process2(struct map_session_data* sd, int target_id) /** * Is invoked _only_ when a new pet has been created is a product of packet 0x3880 - * see mapif_pet_created@int_pet.c for more information - * Handles new pet data from inter-server and prepares item information - * to add pet egg - * - * pet_id - Should contain pet id otherwise means failure - * returns true on success + * see mapif_pet_created@int_pet.c for more information. + * Handles new pet data from inter-server and prepares item information to add pet egg. + * @param account_id : account ID of owner + * @param pet_class : class of pet + * @param pet_id : pet ID otherwise means failure + * @return true : success, false : failure **/ bool pet_get_egg(int account_id, short pet_class, int pet_id ) { struct map_session_data *sd; @@ -566,6 +718,7 @@ bool pet_get_egg(int account_id, short pet_class, int pet_id ) { return false; sd = map_id2sd(account_id); + if( sd == NULL ) return false; @@ -579,6 +732,7 @@ bool pet_get_egg(int account_id, short pet_class, int pet_id ) { if(i < 0) { intif_delete_petdata(pet_id); + return false; } @@ -589,6 +743,7 @@ bool pet_get_egg(int account_id, short pet_class, int pet_id ) { tmp_item.card[1] = GetWord(pet_id,0); tmp_item.card[2] = GetWord(pet_id,1); tmp_item.card[3] = 0; //New pets are not named. + if((ret = pc_additem(sd,&tmp_item,1,LOG_TYPE_PICKDROP_PLAYER))) { clif_additem(sd,0,0,ret); map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); @@ -601,6 +756,12 @@ static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd); static int pet_food(struct map_session_data *sd, struct pet_data *pd); static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap); +/** + * Pet menu options. + * @param sd : player requesting + * @param menunum : menu option chosen + * @return 0:success, 1:failure + */ int pet_menu(struct map_session_data *sd,int menunum) { struct item_data *egg_id; @@ -614,6 +775,7 @@ int pet_menu(struct map_session_data *sd,int menunum) return 1; egg_id = itemdb_exists(sd->pd->petDB->EggID); + if (egg_id) { if ((egg_id->flag.trade_restriction&0x01) && !pc_inventoryblank(sd)) { clif_displaymessage(sd->fd, msg_txt(sd, 451)); // You can't return your pet because your inventory is full. @@ -638,20 +800,29 @@ int pet_menu(struct map_session_data *sd,int menunum) pet_unequipitem(sd, sd->pd); break; } + return 0; } +/** + * Apply pet's new name. + * @param sd : player requesting + * @param name : new pet name + * @return 0:success, 1:failure + */ int pet_change_name(struct map_session_data *sd,char *name) { int i; struct pet_data *pd; + nullpo_retr(1, sd); pd = sd->pd; + if((pd == NULL) || (pd->pet.rename_flag == 1 && !battle_config.pet_rename)) return 1; - for(i=0;ipd; - if (!pd) return 0; + + if (!pd) + return 0; normalize_name(name," ");//bugreport:3032 if ( !flag || !strlen(name) ) { clif_displaymessage(sd->fd, msg_txt(sd,280)); // You cannot use this name for your pet. - clif_send_petstatus(sd); //Send status so client knows oet name change got rejected. + clif_send_petstatus(sd); //Send status so client knows pet name change got rejected. return 0; } + memcpy(pd->pet.name, name, NAME_LENGTH); clif_charnameack (0,&pd->bl); pd->pet.rename_flag = 1; clif_pet_equip_area(pd); clif_send_petstatus(sd); + return 1; } +/** + * Apply a pet's equipment. + * @param sd : player requesting + * @param index : index value of item + * @return 0:success, 1:failure + */ int pet_equipitem(struct map_session_data *sd,int index) { struct pet_data *pd; unsigned short nameid; nullpo_retr(1, sd); + pd = sd->pd; - if (!pd) return 1; + + if (!pd) + return 1; nameid = sd->status.inventory[index].nameid; @@ -699,16 +890,17 @@ int pet_equipitem(struct map_session_data *sd,int index) pd->pet.equip = nameid; status_set_viewdata(&pd->bl, pd->pet.class_); //Updates view_data. clif_pet_equip_area(pd); - if (battle_config.pet_equip_required) - { //Skotlex: start support timers if need + + if (battle_config.pet_equip_required) { // Skotlex: start support timers if need unsigned int tick = gettick(); - if (pd->s_skill && pd->s_skill->timer == INVALID_TIMER) - { + + if (pd->s_skill && pd->s_skill->timer == INVALID_TIMER) { if (pd->s_skill->id) pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_skill_support_timer, sd->bl.id, 0); else pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_heal_timer, sd->bl.id, 0); } + if (pd->bonus && pd->bonus->timer == INVALID_TIMER) pd->bonus->timer=add_timer(tick+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0); } @@ -716,6 +908,12 @@ int pet_equipitem(struct map_session_data *sd,int index) return 0; } +/** + * Remove a pet's equipment. + * @param sd : player requesting + * @param pd : pet requesting + * @return 0:success, 1:failure + */ static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd) { struct item tmp_item; @@ -732,15 +930,18 @@ static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd) memset(&tmp_item,0,sizeof(tmp_item)); tmp_item.nameid = nameid; tmp_item.identify = 1; + if((flag = pc_additem(sd,&tmp_item,1,LOG_TYPE_OTHER))) { clif_additem(sd,0,0,flag); map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); } + if( battle_config.pet_equip_required ) { // Skotlex: halt support timers if needed if( pd->state.skillbonus ) { pd->state.skillbonus = 0; status_calc_pc(sd,SCO_NONE); } + if( pd->s_skill && pd->s_skill->timer != INVALID_TIMER ) { if( pd->s_skill->id ) delete_timer(pd->s_skill->timer, pet_skill_support_timer); @@ -748,6 +949,7 @@ static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd) delete_timer(pd->s_skill->timer, pet_heal_timer); pd->s_skill->timer = INVALID_TIMER; } + if( pd->bonus && pd->bonus->timer != INVALID_TIMER ) { delete_timer(pd->bonus->timer, pet_skill_bonus_timer); pd->bonus->timer = INVALID_TIMER; @@ -757,16 +959,25 @@ static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd) return 0; } +/** + * Feed a pet and update intimacy. + * @param sd : playerr requesting + * @param pd : pet requesting + * @return 0:success, 1:failure + */ static int pet_food(struct map_session_data *sd, struct pet_data *pd) { int i,k; k = pd->petDB->FoodID; i = pc_search_inventory(sd,k); + if( i < 0 ) { clif_pet_food(sd,k,0); + return 1; } + pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME); if( pd->pet.hungry > 90 ) @@ -776,21 +987,26 @@ static int pet_food(struct map_session_data *sd, struct pet_data *pd) k = (pd->petDB->r_hungry * battle_config.pet_friendly_rate) / 100; else k = pd->petDB->r_hungry; + if( pd->pet.hungry > 75 ) { k = k >> 1; if( k <= 0 ) k = 1; } + pet_set_intimate(pd, pd->pet.intimate + k); } + if( pd->pet.intimate <= 0 ) { pd->pet.intimate = 0; pet_stop_attack(pd); pd->status.speed = pd->db->status.speed; } else if( pd->pet.intimate > 1000 ) pd->pet.intimate = 1000; + status_calc_pet(pd,SCO_NONE); pd->pet.hungry += pd->petDB->fullness; + if( pd->pet.hungry > 100 ) pd->pet.hungry = 100; @@ -801,6 +1017,12 @@ static int pet_food(struct map_session_data *sd, struct pet_data *pd) return 0; } +/** + * Let a pet walk around and update walk timer. + * @param pd : pet requesting + * @param tick : last walk time + * @return 1:success, 0:failure + */ static int pet_randomwalk(struct pet_data *pd,unsigned int tick) { nullpo_ret(pd); @@ -813,38 +1035,53 @@ static int pet_randomwalk(struct pet_data *pd,unsigned int tick) if(d < 5) d = 5; + for(i = 0; i < retrycount; i++) { int r = rnd(), x, y; x = pd->bl.x+r%(d*2+1)-d; y = pd->bl.y+r/(d*2+1)%(d*2+1)-d; + if(map_getcell(pd->bl.m,x,y,CELL_CHKPASS) && unit_walktoxy(&pd->bl,x,y,0)) { pd->move_fail_count = 0; break; } + if(i + 1 >= retrycount) { pd->move_fail_count++; + if(pd->move_fail_count > 1000) { ShowWarning("Pet can't move. Holding position %d, class = %d\n",pd->bl.id,pd->pet.class_); pd->move_fail_count = 0; pd->ud.canmove_tick = tick + 60000; + return 0; } } } + for(i = c = 0; i < pd->ud.walkpath.path_len; i++) { if(pd->ud.walkpath.path[i]&1) c += pd->status.speed*MOVE_DIAGONAL_COST/MOVE_COST; else c += pd->status.speed; } + pd->next_walktime = tick+rnd()%3000+3000+c; return 1; } + return 0; } +/** + * Pet AI decision making when supporting master. + * @param pd : pet requesting + * @param sd : player requesting + * @param tick : last support time + * @return 0 + */ static int pet_ai_sub_hard(struct pet_data *pd, struct map_session_data *sd, unsigned int tick) { struct block_list *target = NULL; @@ -854,69 +1091,77 @@ static int pet_ai_sub_hard(struct pet_data *pd, struct map_session_data *sd, uns if(DIFF_TICK(tick,pd->last_thinktime) < MIN_PETTHINKTIME) return 0; - pd->last_thinktime=tick; + + pd->last_thinktime = tick; if(pd->ud.attacktimer != INVALID_TIMER || pd->ud.skilltimer != INVALID_TIMER || pd->bl.m != sd->bl.m) return 0; if(pd->ud.walktimer != INVALID_TIMER && pd->ud.walkpath.path_pos <= 2) - return 0; //No thinking when you just started to walk. + return 0; // No thinking when you just started to walk. if(pd->pet.intimate <= 0) { - //Pet should just... well, random walk. + // Pet should just... well, random walk. pet_randomwalk(pd,tick); + return 0; } if (!check_distance_bl(&sd->bl, &pd->bl, pd->db->range3)) { - //Master too far, chase. + // Master too far, chase. if(pd->target_id) pet_unlocktarget(pd); + if(pd->ud.walktimer != INVALID_TIMER && pd->ud.target == sd->bl.id) - return 0; //Already walking to him + return 0; // Already walking to him + if (DIFF_TICK(tick, pd->ud.canmove_tick) < 0) - return 0; //Can't move yet. + return 0; // Can't move yet. + pd->status.speed = (sd->battle_status.speed>>1); + if(pd->status.speed == 0) pd->status.speed = 1; + if (!unit_walktobl(&pd->bl, &sd->bl, 3, 0)) pet_randomwalk(pd,tick); + return 0; } - //Return speed to normal. + // Return speed to normal. if (pd->status.speed != pd->petDB->speed) { if (pd->ud.walktimer != INVALID_TIMER) - return 0; //Wait until the pet finishes walking back to master. + return 0; // Wait until the pet finishes walking back to master. + pd->status.speed = pd->petDB->speed; pd->ud.state.change_walk_target = pd->ud.state.speed_changed = 1; } if (pd->target_id) { - target= map_id2bl(pd->target_id); + target = map_id2bl(pd->target_id); + if (!target || pd->bl.m != target->m || status_isdead(target) || - !check_distance_bl(&pd->bl, target, pd->db->range3)) - { + !check_distance_bl(&pd->bl, target, pd->db->range3)) { target = NULL; pet_unlocktarget(pd); } } - if(!target && pd->loot && pd->loot->count < pd->loot->max && DIFF_TICK(tick,pd->ud.canact_tick)>0) { - //Use half the pet's range of sight. - map_foreachinrange(pet_ai_sub_hard_lootsearch,&pd->bl, - pd->db->range2/2, BL_ITEM,pd,&target); + if(!target && pd->loot && pd->loot->count < pd->loot->max && DIFF_TICK(tick,pd->ud.canact_tick) > 0) { + // Use half the pet's range of sight. + map_foreachinrange(pet_ai_sub_hard_lootsearch, &pd->bl, pd->db->range2 / 2, BL_ITEM, pd, &target); } - if (!target) { - //Just walk around. + if (!target) { // Just walk around. if (check_distance_bl(&sd->bl, &pd->bl, 3)) - return 0; //Already next to master. + return 0; // Already next to master. if(pd->ud.walktimer != INVALID_TIMER && check_distance_blxy(&sd->bl, pd->ud.to_x,pd->ud.to_y, 3)) - return 0; //Already walking to him + return 0; // Already walking to him unit_calc_pos(&pd->bl, sd->bl.x, sd->bl.y, sd->ud.dir); + if(!unit_walktoxy(&pd->bl,pd->ud.to_x,pd->ud.to_y,0)) pet_randomwalk(pd,tick); @@ -927,45 +1172,64 @@ static int pet_ai_sub_hard(struct pet_data *pd, struct map_session_data *sd, uns (pd->ud.attacktimer != INVALID_TIMER || pd->ud.walktimer != INVALID_TIMER)) return 0; //Target already locked. - if (target->type != BL_ITEM) - { //enemy targetted - if(!battle_check_range(&pd->bl,target,pd->status.rhw.range)) - { //Chase + if (target->type != BL_ITEM) { // enemy targeted + if(!battle_check_range(&pd->bl,target,pd->status.rhw.range)) { // Chase if(!unit_walktobl(&pd->bl, target, pd->status.rhw.range, 2)) - pet_unlocktarget(pd); //Unreachable target. + pet_unlocktarget(pd); // Unreachable target. + return 0; } + //Continuous attack. unit_attack(&pd->bl, pd->target_id, 1); - } else { //Item Targeted, attempt loot - if (!check_distance_bl(&pd->bl, target, 1)) - { //Out of range - if(!unit_walktobl(&pd->bl, target, 1, 1)) //Unreachable target. + } else { // Item Targeted, attempt loot + if (!check_distance_bl(&pd->bl, target, 1)) { // Out of range + if(!unit_walktobl(&pd->bl, target, 1, 1)) // Unreachable target. pet_unlocktarget(pd); + return 0; - } else{ + } else { struct flooritem_data *fitem = (struct flooritem_data *)target; - if(pd->loot->count < pd->loot->max){ + + if(pd->loot->count < pd->loot->max) { memcpy(&pd->loot->item[pd->loot->count++],&fitem->item,sizeof(pd->loot->item[0])); pd->loot->weight += itemdb_weight(fitem->item.nameid)*fitem->item.amount; map_clearflooritem(target); } + //Target is unlocked regardless of whether it was picked or not. pet_unlocktarget(pd); } } + return 0; } +/** + * Call pet AI based on targets around. + * @param sd : player requesting + * @param ap + * tick : last search time + * @return 0 + */ static int pet_ai_sub_foreachclient(struct map_session_data *sd,va_list ap) { unsigned int tick = va_arg(ap,unsigned int); + if(sd->status.pet_id && sd->pd) pet_ai_sub_hard(sd->pd,sd,tick); return 0; } +/** + * Begin searching for targets. + * @param tid : (unused) + * @param tick : last search time + * @param id : (unused) + * @param data : data to pass to pet_ai_sub_foreachclient + * @return 0 + */ static int pet_ai_hard(int tid, unsigned int tick, int id, intptr_t data) { map_foreachpc(pet_ai_sub_foreachclient,tick); @@ -973,15 +1237,23 @@ static int pet_ai_hard(int tid, unsigned int tick, int id, intptr_t data) return 0; } +/** + * Make a pet search and grab loot if it can. + * @param bl : item data + * @param ap + * pd : pet requesting + * target : item + * @return 1:success, 0:failure + */ static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap) { struct pet_data* pd; struct flooritem_data *fitem = (struct flooritem_data *)bl; struct block_list **target; - int sd_charid =0; + int sd_charid = 0; - pd=va_arg(ap,struct pet_data *); - target=va_arg(ap,struct block_list**); + pd = va_arg(ap,struct pet_data *); + target = va_arg(ap,struct block_list**); sd_charid = fitem->first_get_charid; @@ -990,24 +1262,34 @@ static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap) if(unit_can_reach_bl(&pd->bl,bl, pd->db->range2, 1, NULL, NULL) && ((*target) == NULL || //New target closer than previous one. - !check_distance_bl(&pd->bl, *target, distance_bl(&pd->bl, bl)))) - { + !check_distance_bl(&pd->bl, *target, distance_bl(&pd->bl, bl)))) { (*target) = bl; pd->target_id = bl->id; + return 1; } return 0; } +/** + * Make a pet drop their looted items. + * @param tid : (unused) + * @param tick : (unused) + * @param data : items that were looted + * @return 0 + */ static int pet_delay_item_drop(int tid, unsigned int tick, int id, intptr_t data) { struct item_drop_list *list; struct item_drop *ditem; - list=(struct item_drop_list *)data; + + list = (struct item_drop_list *)data; ditem = list->item; + while (ditem) { struct item_drop *ditem_prev; + map_addflooritem(&ditem->item_data,ditem->item_data.amount, list->m,list->x,list->y, list->first_charid,list->second_charid,list->third_charid,4); @@ -1015,17 +1297,27 @@ static int pet_delay_item_drop(int tid, unsigned int tick, int id, intptr_t data ditem = ditem->next; ers_free(item_drop_ers, ditem_prev); } + ers_free(item_drop_list_ers, list); + return 0; } +/** + * Make a pet drop their looted items. + * @param pd : pet requesting + * @param sd : player requesting + * @return 1:success, 0:failure + */ int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd) { int i; struct item_drop_list *dlist; struct item_drop *ditem; + if(!pd || !pd->loot || !pd->loot->count) return 0; + dlist = ers_alloc(item_drop_list_ers, struct item_drop_list); dlist->m = pd->bl.m; dlist->x = pd->bl.x; @@ -1035,12 +1327,14 @@ int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd) dlist->third_charid = 0; dlist->item = NULL; - for(i=0;iloot->count;i++) { + for(i = 0; i < pd->loot->count; i++) { struct item *it; it = &pd->loot->item[i]; + if(sd){ unsigned char flag = 0; + if((flag = pc_additem(sd,it,it->amount,LOG_TYPE_PICKDROP_PLAYER))){ clif_additem(sd,0,0,flag); ditem = ers_alloc(item_drop_ers, struct item_drop); @@ -1048,14 +1342,14 @@ int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd) ditem->next = dlist->item; dlist->item = ditem; } - } - else { + } else { ditem = ers_alloc(item_drop_ers, struct item_drop); memcpy(&ditem->item_data, it, sizeof(struct item)); ditem->next = dlist->item; dlist->item = ditem; } } + //The smart thing to do is use pd->loot->max (thanks for pointing it out, Shinomori) memset(pd->loot->item,0,pd->loot->max * sizeof(struct item)); pd->loot->count = 0; @@ -1066,15 +1360,20 @@ int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd) add_timer(gettick()+540,pet_delay_item_drop,0,(intptr_t)dlist); else ers_free(item_drop_list_ers, dlist); + return 1; } -/*========================================== - * pet bonus giving skills [Valaris] / Rewritten by [Skotlex] - *------------------------------------------*/ +/** + * Pet bonus giving skills. + * @param tid : current timer value + * @param tick : next time to use skill + * @param id : ID of pet owner + * @author [Valaris], rewritten by [Skotlex] + */ int pet_skill_bonus_timer(int tid, unsigned int tick, int id, intptr_t data) { - struct map_session_data *sd=map_id2sd(id); + struct map_session_data *sd = map_id2sd(id); struct pet_data *pd; int bonus; int timer = 0; @@ -1082,11 +1381,12 @@ int pet_skill_bonus_timer(int tid, unsigned int tick, int id, intptr_t data) if(sd == NULL || sd->pd==NULL || sd->pd->bonus == NULL) return 1; - pd=sd->pd; + pd = sd->pd; if(pd->bonus->timer != tid) { ShowError("pet_skill_bonus_timer %d != %d\n",pd->bonus->timer,tid); pd->bonus->timer = INVALID_TIMER; + return 0; } @@ -1099,6 +1399,7 @@ int pet_skill_bonus_timer(int tid, unsigned int tick, int id, intptr_t data) timer = pd->bonus->duration*1000; // the duration for pet bonuses to be in effect } else { //Lost pet... pd->bonus->timer = INVALID_TIMER; + return 0; } @@ -1106,31 +1407,40 @@ int pet_skill_bonus_timer(int tid, unsigned int tick, int id, intptr_t data) pd->state.skillbonus = bonus; status_calc_pc(sd,SCO_NONE); } + // wait for the next timer pd->bonus->timer=add_timer(tick+timer,pet_skill_bonus_timer,sd->bl.id,0); + return 0; } -/*========================================== - * pet recovery skills [Valaris] / Rewritten by [Skotlex] - *------------------------------------------*/ +/** + * Pet recovery skills. + * @param tid : current timer value + * @param tick : next time to use skill + * @param id : ID of pet owner + * @param data : (unused) + * @return 0 + * @author [Valaris], rewritten by [Skotlex] + */ int pet_recovery_timer(int tid, unsigned int tick, int id, intptr_t data) { - struct map_session_data *sd=map_id2sd(id); + struct map_session_data *sd = map_id2sd(id); struct pet_data *pd; - if(sd==NULL || sd->pd == NULL || sd->pd->recovery == NULL) + if(sd == NULL || sd->pd == NULL || sd->pd->recovery == NULL) return 1; - pd=sd->pd; + pd = sd->pd; if(pd->recovery->timer != tid) { ShowError("pet_recovery_timer %d != %d\n",pd->recovery->timer,tid); + return 0; } - if(sd->sc.data[pd->recovery->type]) - { //Display a heal animation? + if(sd->sc.data[pd->recovery->type]) { + //Display a heal animation? //Detoxify is chosen for now. clif_skill_nodamage(&pd->bl,&sd->bl,TF_DETOXIFY,1,1); status_change_end(&sd->bl, pd->recovery->type, INVALID_TIMER); @@ -1142,20 +1452,27 @@ int pet_recovery_timer(int tid, unsigned int tick, int id, intptr_t data) return 0; } +/** + * Pet natural regeneration timer. + * @param tid : current timer value + * @param tick : next time to regenerate + * @param id : ID of pet owner + */ int pet_heal_timer(int tid, unsigned int tick, int id, intptr_t data) { - struct map_session_data *sd=map_id2sd(id); + struct map_session_data *sd = map_id2sd(id); struct status_data *status; struct pet_data *pd; unsigned int rate = 100; - if(sd==NULL || sd->pd == NULL || sd->pd->s_skill == NULL) + if(sd == NULL || sd->pd == NULL || sd->pd->s_skill == NULL) return 1; - pd=sd->pd; + pd = sd->pd; if(pd->s_skill->timer != tid) { ShowError("pet_heal_timer %d != %d\n",pd->s_skill->timer,tid); + return 0; } @@ -1167,40 +1484,51 @@ int pet_heal_timer(int tid, unsigned int tick, int id, intptr_t data) (rate = (pd->ud.skilltimer != INVALID_TIMER)) //Another skill is in effect ) { //Wait (how long? 1 sec for every 10% of remaining) pd->s_skill->timer=add_timer(gettick()+(rate>10?rate:10)*100,pet_heal_timer,sd->bl.id,0); + return 0; } + pet_stop_attack(pd); pet_stop_walking(pd,1); clif_skill_nodamage(&pd->bl,&sd->bl,AL_HEAL,pd->s_skill->lv,1); status_heal(&sd->bl, pd->s_skill->lv,0, 0); - pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0); + pd->s_skill->timer = add_timer(tick+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0); + return 0; } -/*========================================== - * pet support skills [Skotlex] - *------------------------------------------*/ +/** + * Pet support skills. + * @param tid : current timer value + * @param tick : next time to use skill + * @param id : ID of pet owner + * @param data : (unused) + * @author [Skotlex] + */ int pet_skill_support_timer(int tid, unsigned int tick, int id, intptr_t data) { - struct map_session_data *sd=map_id2sd(id); + struct map_session_data *sd = map_id2sd(id); struct pet_data *pd; struct status_data *status; short rate = 100; - if(sd==NULL || sd->pd == NULL || sd->pd->s_skill == NULL) + + if(sd == NULL || sd->pd == NULL || sd->pd->s_skill == NULL) return 1; - pd=sd->pd; + pd = sd->pd; if(pd->s_skill->timer != tid) { ShowError("pet_skill_support_timer %d != %d\n",pd->s_skill->timer,tid); + return 0; } status = status_get_status_data(&sd->bl); - if (DIFF_TICK(pd->ud.canact_tick, tick) > 0) - { //Wait until the pet can act again. + if (DIFF_TICK(pd->ud.canact_tick, tick) > 0) { + //Wait until the pet can act again. pd->s_skill->timer=add_timer(pd->ud.canact_tick,pet_skill_support_timer,sd->bl.id,0); + return 0; } @@ -1216,33 +1544,32 @@ int pet_skill_support_timer(int tid, unsigned int tick, int id, intptr_t data) pet_stop_attack(pd); pet_stop_walking(pd,1); pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0); + if (skill_get_inf(pd->s_skill->id) & INF_GROUND_SKILL) unit_skilluse_pos(&pd->bl, sd->bl.x, sd->bl.y, pd->s_skill->id, pd->s_skill->lv); else unit_skilluse_id(&pd->bl, sd->bl.id, pd->s_skill->id, pd->s_skill->lv); + return 0; } -/*========================================== - * Pet read db data - * pet_db.txt - * pet_db2.txt - *------------------------------------------*/ -void read_petdb(){ +/** + * Read pet databases. (pet_db.txt and pet_db2.txt) + */ +void read_petdb() +{ char* filename[] = {"pet_db.txt",DBIMPORT"/pet_db.txt"}; unsigned short nameid; int i,j; // Remove any previous scripts in case reloaddb was invoked. - for( j = 0; j < MAX_PET_DB; j++ ) - { - if( pet_db[j].pet_script ) - { + for( j = 0; j < MAX_PET_DB; j++ ) { + if( pet_db[j].pet_script ) { script_free_code(pet_db[j].pet_script); pet_db[j].pet_script = NULL; } - if( pet_db[j].equip_script ) - { + + if( pet_db[j].equip_script ) { script_free_code(pet_db[j].equip_script); pet_db[j].pet_script = NULL; } @@ -1252,80 +1579,84 @@ void read_petdb(){ memset(pet_db,0,sizeof(pet_db)); j = 0; // entry counter - for( i = 0; i < ARRAYLENGTH(filename); i++ ) - { + for( i = 0; i < ARRAYLENGTH(filename); i++ ) { char line[1024]; int lines, entries; FILE *fp; sprintf(line, "%s/%s", db_path, filename[i]); fp = fopen(line,"r"); + if( fp == NULL ) { - if( i == 0 ) ShowError("can't read %s\n",line); + if( i == 0 ) + ShowError("can't read %s\n",line); continue; } lines = entries = 0; - while( fgets(line, sizeof(line), fp) && j < MAX_PET_DB ) - { + + while( fgets(line, sizeof(line), fp) && j < MAX_PET_DB ) { char *str[22], *p; unsigned k; lines++; if(line[0] == '/' && line[1] == '/') continue; + memset(str, 0, sizeof(str)); p = line; + while( ISSPACE(*p) ) ++p; + if( *p == '\0' ) continue; // empty line - for( k = 0; k < 20; ++k ) - { + + for( k = 0; k < 20; ++k ) { str[k] = p; p = strchr(p,','); + if( p == NULL ) break; // comma not found + *p = '\0'; ++p; } - if( p == NULL ) - { + if( p == NULL ) { ShowError("read_petdb: Insufficient columns in line %d, skipping.\n", lines); continue; } // Pet Script - if( *p != '{' ) - { + if( *p != '{' ) { ShowError("read_petdb: Invalid format (Pet Script column) in line %d, skipping.\n", lines); continue; } str[20] = p; p = strstr(p+1,"},"); - if( p == NULL ) - { + + if( p == NULL ) { ShowError("read_petdb: Invalid format (Pet Script column) in line %d, skipping.\n", lines); continue; } + p[1] = '\0'; p += 2; // Equip Script - if( *p != '{' ) - { + if( *p != '{' ) { ShowError("read_petdb: Invalid format (Equip Script column) in line %d, skipping.\n", lines); continue; } + str[21] = p; if( (nameid = atoi(str[0])) <= 0 ) continue; - if( !mobdb_checkid(nameid) ) - { + if( !mobdb_checkid(nameid) ) { ShowWarning("pet_db reading: Invalid mob-class %hu, pet not read.\n", nameid); continue; } @@ -1357,6 +1688,7 @@ void read_petdb(){ if( *str[20] ) pet_db[j].pet_script = parse_script(str[20], filename[i], lines, 0); + if( *str[21] ) pet_db[j].equip_script = parse_script(str[21], filename[i], lines, 0); @@ -1366,15 +1698,17 @@ void read_petdb(){ if( j >= MAX_PET_DB ) ShowWarning("read_petdb: Reached max number of pets [%d]. Remaining pets were not read.\n ", MAX_PET_DB); + fclose(fp); ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' pets in '"CL_WHITE"%s/%s"CL_RESET"'.\n", entries, db_path, filename[i]); } } -/*========================================== - * Initialization process relationship skills - *------------------------------------------*/ -void do_init_pet(void){ +/** + * Initialization process of skill relationship. + */ +void do_init_pet(void) +{ read_petdb(); item_drop_ers = ers_new(sizeof(struct item_drop),"pet.c::item_drop_ers",ERS_OPT_NONE); @@ -1390,21 +1724,25 @@ void do_init_pet(void){ add_timer_interval(gettick()+MIN_PETTHINKTIME,pet_ai_hard,0,0,MIN_PETTHINKTIME); } -void do_final_pet(void){ +/** + * Pet destructor. + */ +void do_final_pet(void) +{ int i; - for( i = 0; i < MAX_PET_DB; i++ ) - { - if( pet_db[i].pet_script ) - { + + for( i = 0; i < MAX_PET_DB; i++ ) { + if( pet_db[i].pet_script ) { script_free_code(pet_db[i].pet_script); pet_db[i].pet_script = NULL; } - if( pet_db[i].equip_script ) - { + + if( pet_db[i].equip_script ) { script_free_code(pet_db[i].equip_script); pet_db[i].equip_script = NULL; } } + ers_destroy(item_drop_ers); ers_destroy(item_drop_list_ers); } diff --git a/src/map/quest.c b/src/map/quest.c index 70dad95ef8..2fe30046c9 100644 --- a/src/map/quest.c +++ b/src/map/quest.c @@ -34,8 +34,7 @@ /** * Searches a quest by ID. - * - * @param quest_id ID to lookup + * @param quest_id : ID to lookup * @return Quest entry (equals to &quest_dummy if the ID is invalid) */ struct quest_db *quest_db(int quest_id) @@ -48,8 +47,7 @@ struct quest_db *quest_db(int quest_id) /** * Sends quest info to the player on login. - * - * @param sd Player's data + * @param sd : Player's data * @return 0 in case of success, nonzero otherwise (i.e. the player has no quests) */ int quest_pc_login(TBL_PC *sd) @@ -71,11 +69,9 @@ int quest_pc_login(TBL_PC *sd) /** * Adds a quest to the player's list. - * * New quest will be added as Q_ACTIVE. - * - * @param sd Player's data - * @param quest_id ID of the quest to add. + * @param sd : Player's data + * @param quest_id : ID of the quest to add. * @return 0 in case of success, nonzero otherwise */ int quest_add(TBL_PC *sd, int quest_id) @@ -123,10 +119,9 @@ int quest_add(TBL_PC *sd, int quest_id) /** * Replaces a quest in a player's list with another one. - * - * @param sd Player's data - * @param qid1 Current quest to replace - * @param qid2 New quest to add + * @param sd : Player's data + * @param qid1 : Current quest to replace + * @param qid2 : New quest to add * @return 0 in case of success, nonzero otherwise */ int quest_change(TBL_PC *sd, int qid1, int qid2) @@ -177,9 +172,8 @@ int quest_change(TBL_PC *sd, int qid1, int qid2) /** * Removes a quest from a player's list - * - * @param sd Player's data - * @param quest_id ID of the quest to remove + * @param sd : Player's data + * @param quest_id : ID of the quest to remove * @return 0 in case of success, nonzero otherwise */ int quest_delete(TBL_PC *sd, int quest_id) @@ -217,11 +211,10 @@ int quest_delete(TBL_PC *sd, int quest_id) /** * Map iterator subroutine to update quest objectives for a party after killing a monster. - * * @see map_foreachinrange - * @param ap Argument list, expecting: - * int Party ID - * int Mob ID + * @param ap : Argument list, expecting: + * int Party ID + * int Mob ID */ int quest_update_objective_sub(struct block_list *bl, va_list ap) { @@ -246,9 +239,8 @@ int quest_update_objective_sub(struct block_list *bl, va_list ap) /** * Updates the quest objectives for a character after killing a monster. - * - * @param sd Character's data - * @param mob_id Monster ID + * @param sd : Character's data + * @param mob_id : Monster ID */ void quest_update_objective(TBL_PC *sd, int mob) { @@ -274,13 +266,12 @@ void quest_update_objective(TBL_PC *sd, int mob) /** * Updates a quest's state. - * - * Only status of active and inactive quests can be updated. Completed quests can't (for now). [Inkfish] - * - * @param sd Character's data - * @param quest_id Quest ID to update - * @param qs New quest state + * Only status of active and inactive quests can be updated. Completed quests can't (for now). + * @param sd : Character's data + * @param quest_id : Quest ID to update + * @param qs : New quest state * @return 0 in case of success, nonzero otherwise + * @author [Inkfish] */ int quest_update_status(TBL_PC *sd, int quest_id, enum quest_state status) { @@ -319,18 +310,17 @@ int quest_update_status(TBL_PC *sd, int quest_id, enum quest_state status) /** * Queries quest information for a character. - * - * @param sd Character's data - * @param quest_id Quest ID - * @param type Check type + * @param sd : Character's data + * @param quest_id : Quest ID + * @param type : Check type * @return -1 if the quest was not found, otherwise it depends on the type: - * HAVEQUEST: The quest's state - * PLAYTIME: 2 if the quest's timeout has expired - * 1 if the quest was completed - * 0 otherwise - * HUNTING: 2 if the quest has not been marked as completed yet, and its objectives have been fulfilled - * 1 if the quest's timeout has expired - * 0 otherwise + * HAVEQUEST: The quest's state + * PLAYTIME: 2 if the quest's timeout has expired + * 1 if the quest was completed + * 0 otherwise + * HUNTING: 2 if the quest has not been marked as completed yet, and its objectives have been fulfilled + * 1 if the quest's timeout has expired + * 0 otherwise */ int quest_check(TBL_PC *sd, int quest_id, enum quest_check_type type) { @@ -367,7 +357,6 @@ int quest_check(TBL_PC *sd, int quest_id, enum quest_check_type type) /** * Loads quests from the quest db. - * * @return Number of loaded quests, or -1 if the file couldn't be read. */ int quest_read_db(void) @@ -453,11 +442,10 @@ int quest_read_db(void) /** * Map iterator to ensures a player has no invalid quest log entries. - * * Any entries that are no longer in the db are removed. - * * @see map_foreachpc - * @param ap Ignored + * @param sd : Character's data + * @param ap : Ignored */ int quest_reload_check_sub(struct map_session_data *sd, va_list ap) { diff --git a/src/map/script.c b/src/map/script.c index 7e04ac596a..38146d6aff 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -73,7 +73,6 @@ #include "../common/mutex.h" #endif - /////////////////////////////////////////////////////////////////////////////// //## TODO possible enhancements: [FlavioJS] // - 'callfunc' supporting labels in the current npc "::LabelName" diff --git a/src/map/searchstore.c b/src/map/searchstore.c index 957c1c4203..b6b6270fcd 100644 --- a/src/map/searchstore.c +++ b/src/map/searchstore.c @@ -10,8 +10,7 @@ #include "pc.h" // struct map_session_data #include "searchstore.h" // struct s_search_store_info - -/// failure constants for clif functions +/// Failure constants for clif functions enum e_searchstore_failure { SSI_FAILED_NOTHING_SEARCH_ITEM = 0, // "No matching stores were found." @@ -21,13 +20,14 @@ enum e_searchstore_failure SSI_FAILED_SSILIST_CLICK_TO_OPEN_STORE = 4, // "No sale (purchase) information available." }; +/// Search type constants enum e_searchstore_searchtype { SEARCHTYPE_VENDING = 0, SEARCHTYPE_BUYING_STORE = 1, }; - +/// Search effect constants enum e_searchstore_effecttype { EFFECTTYPE_NORMAL = 0, @@ -35,11 +35,15 @@ enum e_searchstore_effecttype EFFECTTYPE_MAX }; -/// type for shop search function +/// Type for shop search function typedef bool (*searchstore_search_t)(struct map_session_data* sd, unsigned short nameid); typedef bool (*searchstore_searchall_t)(struct map_session_data* sd, const struct s_search_store_search* s); -/// retrieves search function by type +/** + * Retrieves search function by type. + * @param type : type of search to conduct + * @return : search type + */ static searchstore_search_t searchstore_getsearchfunc(unsigned char type) { switch( type ) { @@ -50,7 +54,11 @@ static searchstore_search_t searchstore_getsearchfunc(unsigned char type) return NULL; } -/// retrieves search-all function by type +/** + * Retrieves search-all function by type. + * @param type : type of search to conduct + * @return : search type + */ static searchstore_searchall_t searchstore_getsearchallfunc(unsigned char type) { switch( type ) { @@ -61,7 +69,12 @@ static searchstore_searchall_t searchstore_getsearchallfunc(unsigned char type) return NULL; } -/// checks if the player has a store by type +/** + * Checks if the player has a store by type. + * @param sd : player requesting + * @param type : type of search to conduct + * @return : store type + */ static bool searchstore_hasstore(struct map_session_data* sd, unsigned char type) { switch( type ) { @@ -72,7 +85,12 @@ static bool searchstore_hasstore(struct map_session_data* sd, unsigned char type return false; } -/// returns player's store id by type +/** + * Returns player's store ID by type. + * @param sd : player requesting + * @param type : type of search to conduct + * @return : store ID + */ static int searchstore_getstoreid(struct map_session_data* sd, unsigned char type) { switch( type ) { @@ -83,6 +101,13 @@ static int searchstore_getstoreid(struct map_session_data* sd, unsigned char typ return 0; } +/** + * Send request to open Search Store. + * @param sd : player requesting + * @param uses : uses left to open + * @param effect : shop type + * @return : true : opened, false : failed to open + */ bool searchstore_open(struct map_session_data* sd, unsigned int uses, unsigned short effect) { if( !battle_config.feature_search_stores || sd->searchstore.open ) @@ -100,6 +125,17 @@ bool searchstore_open(struct map_session_data* sd, unsigned int uses, unsigned s return true; } +/** + * Query and present the results for the item. + * @param sd : player requesting + * @param type : shop type + * @param min_price : minimum zeny price + * @param max_price : maximum zeny price + * @param itemlist : list with stored item results + * @param item_count : amount of items in itemlist + * @param cardlist : list with stored cards (cards attached to items) + * @param card_count : amount of items in cardlist + */ void searchstore_query(struct map_session_data* sd, unsigned char type, unsigned int min_price, unsigned int max_price, const unsigned short* itemlist, unsigned int item_count, const unsigned short* cardlist, unsigned int card_count) { unsigned int i; @@ -204,7 +240,11 @@ void searchstore_query(struct map_session_data* sd, unsigned char type, unsigned } } -/// checks whether or not more results are available for the client +/** + * Checks whether or not more results are available for the client. + * @param sd : player requesting + * @return : true : more items to search, false : no more items + */ bool searchstore_querynext(struct map_session_data* sd) { if( sd->searchstore.count && ( sd->searchstore.count-1 )/SEARCHSTORE_RESULTS_PER_PAGE < sd->searchstore.pages ) @@ -213,12 +253,14 @@ bool searchstore_querynext(struct map_session_data* sd) return false; } +/** + * Get and display the results for the next page. + * @param sd : player requesting + */ void searchstore_next(struct map_session_data* sd) { - if( !battle_config.feature_search_stores || !sd->searchstore.open || sd->searchstore.count <= sd->searchstore.pages*SEARCHSTORE_RESULTS_PER_PAGE ) - {// nothing (more) to display + if( !battle_config.feature_search_stores || !sd->searchstore.open || sd->searchstore.count <= sd->searchstore.pages*SEARCHSTORE_RESULTS_PER_PAGE ) // nothing (more) to display return; - } // present results clif_search_store_info_ack(sd); @@ -227,6 +269,10 @@ void searchstore_next(struct map_session_data* sd) sd->searchstore.pages++; } +/** + * Prepare to clear information for closing of window. + * @param sd : player requesting + */ void searchstore_clear(struct map_session_data* sd) { searchstore_clearremote(sd); @@ -240,7 +286,10 @@ void searchstore_clear(struct map_session_data* sd) sd->searchstore.pages = 0; } - +/** + * Close the Search Store window. + * @param sd : player requesting + */ void searchstore_close(struct map_session_data* sd) { if( sd->searchstore.open ) { @@ -251,7 +300,13 @@ void searchstore_close(struct map_session_data* sd) } } - +/** + * Process the actions (click) for the Search Store window. + * @param sd : player requesting + * @param account_id : account ID of owner's shop + * @param store_id : store ID created by client + * @param nameid : item being searched + */ void searchstore_click(struct map_session_data* sd, int account_id, int store_id, unsigned short nameid) { unsigned int i; @@ -311,19 +366,38 @@ void searchstore_click(struct map_session_data* sd, int account_id, int store_id } } -/// checks whether or not sd has opened account_id's shop remotely +/** + * Checks whether or not sd has opened account_id's shop remotely. + * @param sd : player requesting + * @param account_id : account ID of owner's shop + * @return : true : shop opened, false : shop not opened + */ bool searchstore_queryremote(struct map_session_data* sd, int account_id) { return (bool)( sd->searchstore.open && sd->searchstore.count && sd->searchstore.remote_id == account_id ); } -/// removes range-check bypassing for remotely opened stores +/** + * Removes range-check bypassing for remotely opened stores. + * @param sd : player requesting + */ void searchstore_clearremote(struct map_session_data* sd) { sd->searchstore.remote_id = 0; } -/// receives results from a store-specific callback +/** + * Receives results from a store-specific callback. + * @param sd : player requesting + * @param store_id : store ID generated by the client + * @param account_id : account ID of owner's shop + * @param store_name : name of store + * @param nameid : item being searched + * @param amount : count of item + * @param price : zeny price of item + * @param card : card in the item + * @param refine : refine of the item + */ bool searchstore_result(struct map_session_data* sd, int store_id, int account_id, const char* store_name, unsigned short nameid, unsigned short amount, unsigned int price, const unsigned short* card, unsigned char refine) { struct s_search_store_info_item* ssitem; diff --git a/src/map/skill.c b/src/map/skill.c index dc2575165a..110f658231 100755 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -41,7 +41,6 @@ #include #include - #define SKILLUNITTIMER_INTERVAL 100 // ranges reserved for mapping skill ids to skilldb offsets diff --git a/src/map/storage.c b/src/map/storage.c index ca50077ab9..7ca0928a66 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -23,7 +23,6 @@ #include #include - static DBMap* guild_storage_db; ///Databases of guild_storage : int guild_id -> struct guild_storage* /** @@ -44,6 +43,7 @@ static int storage_comp_item(const void *_i1, const void *_i2) return 1; else if (!(i2->nameid) || !(i2->amount)) return -1; + return i1->nameid - i2->nameid; } @@ -89,6 +89,7 @@ void do_final_storage(void) static int storage_reconnect_sub(DBKey key, DBData *data, va_list ap) { struct guild_storage *stor = db_data2ptr(data); + if (stor->dirty && stor->storage_status == 0) //Save closed storages. storage_guild_storagesave(0, stor->guild_id,0); @@ -116,8 +117,7 @@ int storage_storageopen(struct map_session_data *sd) if(sd->state.storage_flag) return 1; //Already open? - if( !pc_can_give_items(sd) ) - { //check is this GM level is allowed to put items to storage + if( !pc_can_give_items(sd) ) { // check is this GM level is allowed to put items to storage clif_displaymessage(sd->fd, msg_txt(sd,246)); return 1; } @@ -126,6 +126,7 @@ int storage_storageopen(struct map_session_data *sd) storage_sortitem(sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items)); clif_storagelist(sd, sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items)); clif_updatestorageamount(sd, sd->status.storage.storage_amount, sd->storage_size); + return 0; } @@ -142,12 +143,14 @@ int compare_item(struct item *a, struct item *b) a->refine == b->refine && a->attribute == b->attribute && a->expire_time == b->expire_time && - a->bound == b->bound ) - { + a->bound == b->bound ) { int i; + for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++); + return (i == MAX_SLOTS); } + return 0; } @@ -169,13 +172,10 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data, data = itemdb_search(item_data->nameid); - if( data->stack.storage && amount > data->stack.amount ) - {// item stack limitation + if( data->stack.storage && amount > data->stack.amount ) // item stack limitation return 1; - } - if( !itemdb_canstore(item_data, pc_get_group_level(sd)) ) - { //Check if item is storable. [Skotlex] + if( !itemdb_canstore(item_data, pc_get_group_level(sd)) ) { // Check if item is storable. [Skotlex] clif_displaymessage (sd->fd, msg_txt(sd,264)); return 1; } @@ -185,16 +185,15 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data, return 1; } - if( itemdb_isstackable2(data) ) - {//Stackable - for( i = 0; i < sd->storage_size; i++ ) - { - if( compare_item(&stor->items[i], item_data) ) - {// existing items found, stack them + if( itemdb_isstackable2(data) ) { // Stackable + for( i = 0; i < sd->storage_size; i++ ) { + if( compare_item(&stor->items[i], item_data) ) { // existing items found, stack them if( amount > MAX_AMOUNT - stor->items[i].amount || ( data->stack.storage && amount > data->stack.amount - stor->items[i].amount ) ) return 1; + stor->items[i].amount += amount; clif_storageitemadded(sd,&stor->items[i],i,amount); + return 0; } } @@ -228,13 +227,18 @@ int storage_delitem(struct map_session_data* sd, int n, int amount) return 1; sd->status.storage.items[n].amount -= amount; - if( sd->status.storage.items[n].amount == 0 ) - { + + if( sd->status.storage.items[n].amount == 0 ) { memset(&sd->status.storage.items[n],0,sizeof(sd->status.storage.items[0])); sd->status.storage.storage_amount--; - if( sd->state.storage_flag == 1 ) clif_updatestorageamount(sd, sd->status.storage.storage_amount, sd->storage_size); + + if( sd->state.storage_flag == 1 ) + clif_updatestorageamount(sd, sd->status.storage.storage_amount, sd->storage_size); } - if( sd->state.storage_flag == 1 ) clif_storageitemremoved(sd,n,amount); + + if( sd->state.storage_flag == 1 ) + clif_storageitemremoved(sd,n,amount); + return 0; } @@ -338,6 +342,7 @@ void storage_storageaddfromcart(struct map_session_data* sd, int index, int amou void storage_storagegettocart(struct map_session_data* sd, int index, int amount) { unsigned char flag; + nullpo_retv(sd); if( index < 0 || index >= sd->storage_size ) @@ -379,8 +384,8 @@ void storage_storageclose(struct map_session_data* sd) * Force closing the storage for player without displaying result * (exemple when quitting the game) * @param sd : player to close storage - * @param flag : \n - * 1: Character is quitting \n + * @param flag : + * 1: Character is quitting * 2(x): Character is changing map-servers */ void storage_storage_quit(struct map_session_data* sd, int flag) @@ -403,8 +408,10 @@ void storage_storage_quit(struct map_session_data* sd, int flag) static DBData create_guildstorage(DBKey key, va_list args) { struct guild_storage *gs = NULL; + gs = (struct guild_storage *) aCalloc(sizeof(struct guild_storage), 1); gs->guild_id=key.i; + return db_ptr2data(gs); } @@ -417,8 +424,10 @@ static DBData create_guildstorage(DBKey key, va_list args) struct guild_storage *guild2storage(int guild_id) { struct guild_storage *gs = NULL; + if(guild_search(guild_id) != NULL) gs = idb_ensure(guild_storage_db,guild_id,create_guildstorage); + return gs; } @@ -441,13 +450,14 @@ struct guild_storage *guild2storage2(int guild_id) int guild_storage_delete(int guild_id) { idb_remove(guild_storage_db,guild_id); + return 0; } /** * Attempt to open guild storage for player * @param sd : player - * @return * 0 : success, 1 : fail, 2 : no guild found + * @return 0 : success, 1 : fail, 2 : no guild found */ int storage_guild_storageopen(struct map_session_data* sd) { @@ -470,6 +480,7 @@ int storage_guild_storageopen(struct map_session_data* sd) intif_request_guild_storage(sd->status.account_id,sd->status.guild_id); return 0; } + if(gstor->storage_status) return 1; @@ -481,6 +492,7 @@ int storage_guild_storageopen(struct map_session_data* sd) storage_sortitem(gstor->items, ARRAYLENGTH(gstor->items)); clif_storagelist(sd, gstor->items, ARRAYLENGTH(gstor->items)); clif_updatestorageamount(sd, gstor->storage_amount, MAX_GUILD_STORAGE); + return 0; } @@ -506,13 +518,10 @@ char guild_storage_additem(struct map_session_data* sd, struct guild_storage* st data = itemdb_search(item_data->nameid); - if( data->stack.guildstorage && amount > data->stack.amount ) - {// item stack limitation + if( data->stack.guildstorage && amount > data->stack.amount ) // item stack limitation return 1; - } - if( !itemdb_canguildstore(item_data, pc_get_group_level(sd)) || item_data->expire_time ) - { //Check if item is storable. [Skotlex] + if( !itemdb_canguildstore(item_data, pc_get_group_level(sd)) || item_data->expire_time ) { // Check if item is storable. [Skotlex] clif_displaymessage (sd->fd, msg_txt(sd,264)); return 1; } @@ -522,22 +531,25 @@ char guild_storage_additem(struct map_session_data* sd, struct guild_storage* st return 1; } - if(itemdb_isstackable2(data)){ //Stackable - for(i=0;iitems[i], item_data)) { if( amount > MAX_AMOUNT - stor->items[i].amount || ( data->stack.guildstorage && amount > data->stack.amount - stor->items[i].amount ) ) return 1; + stor->items[i].amount+=amount; clif_storageitemadded(sd,&stor->items[i],i,amount); stor->dirty = 1; + return 0; } } } - //Add item - for(i=0;iitems[i].nameid;i++); - if(i>=MAX_GUILD_STORAGE) + //Add item + for(i = 0; i < MAX_GUILD_STORAGE && stor->items[i].nameid; i++); + + if(i >= MAX_GUILD_STORAGE) return 1; memcpy(&stor->items[i],item_data,sizeof(stor->items[0])); @@ -546,6 +558,7 @@ char guild_storage_additem(struct map_session_data* sd, struct guild_storage* st clif_storageitemadded(sd,&stor->items[i],i,amount); clif_updatestorageamount(sd, stor->storage_amount, MAX_GUILD_STORAGE); stor->dirty = 1; + return 0; } @@ -562,17 +575,20 @@ int guild_storage_delitem(struct map_session_data* sd, struct guild_storage* sto nullpo_retr(1, sd); nullpo_retr(1, stor); - if(stor->items[n].nameid==0 || stor->items[n].amountitems[n].nameid == 0 || stor->items[n].amount < amount) return 1; - stor->items[n].amount-=amount; - if(stor->items[n].amount==0){ + stor->items[n].amount -= amount; + + if(stor->items[n].amount == 0) { memset(&stor->items[n],0,sizeof(stor->items[0])); stor->storage_amount--; clif_updatestorageamount(sd, stor->storage_amount, MAX_GUILD_STORAGE); } + clif_storageitemremoved(sd,n,amount); stor->dirty = 1; + return 0; } @@ -587,7 +603,7 @@ void storage_guild_storageadd(struct map_session_data* sd, int index, int amount struct guild_storage *stor; nullpo_retv(sd); - nullpo_retv(stor=guild2storage2(sd->status.guild_id)); + nullpo_retv(stor = guild2storage2(sd->status.guild_id)); if( !stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE ) return; @@ -606,7 +622,7 @@ void storage_guild_storageadd(struct map_session_data* sd, int index, int amount return; } - if(guild_storage_additem(sd,stor,&sd->status.inventory[index],amount)==0) + if(guild_storage_additem(sd,stor,&sd->status.inventory[index],amount) == 0) pc_delitem(sd,index,amount,0,4,LOG_TYPE_GSTORAGE); else { clif_storageitemremoved(sd,index,0); @@ -627,7 +643,7 @@ void storage_guild_storageget(struct map_session_data* sd, int index, int amount unsigned char flag = 0; nullpo_retv(sd); - nullpo_retv(stor=guild2storage2(sd->status.guild_id)); + nullpo_retv(stor = guild2storage2(sd->status.guild_id)); if(!stor->storage_status) return; @@ -648,7 +664,7 @@ void storage_guild_storageget(struct map_session_data* sd, int index, int amount if((flag = pc_additem(sd,&stor->items[index],amount,LOG_TYPE_GSTORAGE)) == 0) guild_storage_delitem(sd,stor,index,amount); - else {//inform fail + else { // inform fail clif_storageitemremoved(sd,index,0); clif_additem(sd,0,0,flag); } @@ -666,7 +682,7 @@ void storage_guild_storageaddfromcart(struct map_session_data* sd, int index, in struct guild_storage *stor; nullpo_retv(sd); - nullpo_retv(stor=guild2storage2(sd->status.guild_id)); + nullpo_retv(stor = guild2storage2(sd->status.guild_id)); if( !stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE ) return; @@ -680,7 +696,7 @@ void storage_guild_storageaddfromcart(struct map_session_data* sd, int index, in if( amount < 1 || amount > sd->status.cart[index].amount ) return; - if(guild_storage_additem(sd,stor,&sd->status.cart[index],amount)==0) + if(guild_storage_additem(sd,stor,&sd->status.cart[index],amount) == 0) pc_cart_delitem(sd,index,amount,0,LOG_TYPE_GSTORAGE); else { clif_storageitemremoved(sd,index,0); @@ -692,7 +708,7 @@ void storage_guild_storageaddfromcart(struct map_session_data* sd, int index, in * Attempt to retrieve an item from guild storage to cart, then refresh it * @param sd : player * @param index : index of item in storage - * @param amount : number of item to transfert + * @param amount : number of item to transfer * @return 1:fail, 0:success */ void storage_guild_storagegettocart(struct map_session_data* sd, int index, int amount) @@ -701,7 +717,7 @@ void storage_guild_storagegettocart(struct map_session_data* sd, int index, int struct guild_storage *stor; nullpo_retv(sd); - nullpo_retv(stor=guild2storage2(sd->status.guild_id)); + nullpo_retv(stor = guild2storage2(sd->status.guild_id)); if(!stor->storage_status) return; @@ -734,33 +750,35 @@ int storage_guild_storagesave(int account_id, int guild_id, int flag) { struct guild_storage *stor = guild2storage2(guild_id); - if(stor) - { + if(stor) { if (flag) //Char quitting, close it. stor->storage_status = 0; + if (stor->dirty) intif_send_guild_storage(account_id,stor); + return 1; } + return 0; } /** * ACK save of guild storage * @param guild_id : guild to use the storage - * @return 0 : fail (no storage), 1 : succes + * @return 0 : fail (no storage), 1 : success */ int storage_guild_storagesaved(int guild_id) { struct guild_storage *stor; - if((stor=guild2storage2(guild_id)) != NULL) { - if (stor->dirty && stor->storage_status == 0) - { //Storage has been correctly saved. + if((stor = guild2storage2(guild_id)) != NULL) { + if (stor->dirty && stor->storage_status == 0) // Storage has been correctly saved. stor->dirty = 0; - } + return 1; } + return 0; } @@ -774,17 +792,18 @@ int storage_guild_storageclose(struct map_session_data* sd) struct guild_storage *stor; nullpo_ret(sd); - nullpo_ret(stor=guild2storage2(sd->status.guild_id)); + nullpo_ret(stor = guild2storage2(sd->status.guild_id)); clif_storageclose(sd); - if (stor->storage_status) - { + if (stor->storage_status) { if (save_settings&4) chrif_save(sd, 0); //This one also saves the storage. [Skotlex] else storage_guild_storagesave(sd->status.account_id, sd->status.guild_id,0); + stor->storage_status=0; } + sd->state.storage_flag = 0; return 0; @@ -803,13 +822,14 @@ int storage_guild_storage_quit(struct map_session_data* sd, int flag) nullpo_ret(sd); nullpo_ret(stor=guild2storage2(sd->status.guild_id)); - if(flag) - { //Only during a guild break flag is 1 (don't save storage) + if(flag) { // Only during a guild break flag is 1 (don't save storage) sd->state.storage_flag = 0; stor->storage_status = 0; clif_storageclose(sd); + if (save_settings&4) chrif_save(sd,0); + return 0; } @@ -819,6 +839,7 @@ int storage_guild_storage_quit(struct map_session_data* sd, int flag) else storage_guild_storagesave(sd->status.account_id,sd->status.guild_id,1); } + sd->state.storage_flag = 0; stor->storage_status = 0; diff --git a/src/map/trade.c b/src/map/trade.c index bc48a584b4..d5d7e4df54 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -91,13 +91,13 @@ void trade_traderequest(struct map_session_data *sd, struct map_session_data *ta /** * Reply to a trade-request. * @param sd : player receiving the trade request answer - * @param type : answer code \n - * 0: Char is too far \n - * 1: Character does not exist \n - * 2: Trade failed \n - * 3: Accept \n - * 4: Cancel \n - * Weird enough, the client should only send 3/4 \n + * @param type : answer code + * 0: Char is too far + * 1: Character does not exist + * 2: Trade failed + * 3: Accept + * 4: Cancel + * Weird enough, the client should only send 3/4 * and the server is the one that can reply 0~2 */ void trade_tradeack(struct map_session_data *sd, int type) @@ -168,7 +168,7 @@ void trade_tradeack(struct map_session_data *sd, int type) /** * Check here hacker for duplicate item in trade * normal client refuse to have 2 same types of item (except equipment) in same trade window - * normal client authorise only no equiped item and only from inventory + * normal client authorise only no equipped item and only from inventory * This function could end player connection if too much hack is detected * @param sd : player to check * @return -1:zeny hack, 0:all fine, 1:item hack diff --git a/src/map/unit.c b/src/map/unit.c index 7fb7ff366b..0c6d0cfb67 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -41,7 +41,6 @@ #include #include - // Directions values // 1 0 7 // 2 . 6 @@ -227,12 +226,12 @@ int unit_check_start_teleport_timer(struct block_list *sbl) if(msd && max_dist) { int *msd_tid = unit_get_masterteleport_timer(sbl); - if(msd_tid == NULL) return 0; + if(msd_tid == NULL) + return 0; if (!check_distance_bl(&msd->bl, sbl, max_dist)) { if(*msd_tid == INVALID_TIMER || *msd_tid == 0) *msd_tid = add_timer(gettick()+3000,unit_teleport_timer,sbl->id,max_dist); - } - else { + } else { if(*msd_tid && *msd_tid != INVALID_TIMER) delete_timer(*msd_tid,unit_teleport_timer); *msd_tid = INVALID_TIMER; // Cancel recall @@ -260,28 +259,36 @@ static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data TBL_MOB *md=NULL; bl = map_id2bl(id); + if(bl == NULL) return 0; - switch(bl->type){ //svoid useless cast, we can only be 1 type + + switch(bl->type) { // svoid useless cast, we can only be 1 type case BL_PC: sd = BL_CAST(BL_PC, bl); break; case BL_MOB: md = BL_CAST(BL_MOB, bl); break; } + ud = unit_bl2ud(bl); - if(ud == NULL) return 0; + if(ud == NULL) + return 0; - if(ud->walktimer != tid){ + if(ud->walktimer != tid) { ShowError("unit_walk_timer mismatch %d != %d\n",ud->walktimer,tid); return 0; } + ud->walktimer = INVALID_TIMER; - if (bl->prev == NULL) return 0; // Stop moved because it is missing from the block_list + + if (bl->prev == NULL) + return 0; // Stop moved because it is missing from the block_list if(ud->walkpath.path_pos>=ud->walkpath.path_len) return 0; if(ud->walkpath.path[ud->walkpath.path_pos]>=8) return 1; + x = bl->x; y = bl->y; @@ -311,39 +318,38 @@ static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data ud->walktimer = INVALID_TIMER; switch(bl->type) { - case BL_PC: { - if( sd->touching_id ) - npc_touchnext_areanpc(sd,false); - if(map_getcell(bl->m,x,y,CELL_CHKNPC)) { - npc_touch_areanpc(sd,bl->m,x,y); - if (bl->prev == NULL) // Script could have warped char, abort remaining of the function. - return 0; - } else - sd->areanpc_id=0; - pc_cell_basilica(sd); - } - break; - case BL_MOB: - if( map_getcell(bl->m,x,y,CELL_CHKNPC) ) { - if( npc_touch_areanpc2(md) ) - return 0; // Warped - } else - md->areanpc_id = 0; - if (md->min_chase > md->db->range3) md->min_chase--; - // Walk skills are triggered regardless of target due to the idle-walk mob state. - // But avoid triggering on stop-walk calls. - if(tid != INVALID_TIMER && - !(ud->walk_count%WALK_SKILL_INTERVAL) && - mobskill_use(md, tick, -1)) - { - if (!(ud->skill_id == NPC_SELFDESTRUCTION && ud->skilltimer != INVALID_TIMER)) { // Skill used, abort walking - clif_fixpos(bl); // Fix position as walk has been cancelled. - return 0; + case BL_PC: + if( sd->touching_id ) + npc_touchnext_areanpc(sd,false); + if(map_getcell(bl->m,x,y,CELL_CHKNPC)) { + npc_touch_areanpc(sd,bl->m,x,y); + if (bl->prev == NULL) // Script could have warped char, abort remaining of the function. + return 0; + } else + sd->areanpc_id=0; + pc_cell_basilica(sd); + break; + case BL_MOB: + if( map_getcell(bl->m,x,y,CELL_CHKNPC) ) { + if( npc_touch_areanpc2(md) ) + return 0; // Warped + } else + md->areanpc_id = 0; + if (md->min_chase > md->db->range3) + md->min_chase--; + // Walk skills are triggered regardless of target due to the idle-walk mob state. + // But avoid triggering on stop-walk calls. + if(tid != INVALID_TIMER && + !(ud->walk_count%WALK_SKILL_INTERVAL) && + mobskill_use(md, tick, -1)) { + if (!(ud->skill_id == NPC_SELFDESTRUCTION && ud->skilltimer != INVALID_TIMER)) { // Skill used, abort walking + clif_fixpos(bl); // Fix position as walk has been cancelled. + return 0; + } + // Resend walk packet for proper Self Destruction display. + clif_move(ud); } - // Resend walk packet for proper Self Destruction display. - clif_move(ud); - } - break; + break; } if(tid == INVALID_TIMER) // A directly invoked timer is from battle_stop_walking, therefore the rest is irrelevant. @@ -353,7 +359,8 @@ static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data return unit_walktoxy_sub(bl); ud->walkpath.path_pos++; - if(ud->walkpath.path_pos>=ud->walkpath.path_len) + + if(ud->walkpath.path_pos >= ud->walkpath.path_len) i = -1; else if(ud->walkpath.path[ud->walkpath.path_pos]&1) i = status_get_speed(bl)*14/10; @@ -368,16 +375,18 @@ static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data // Keep trying to run. if ( !(unit_run(bl) || unit_wugdash(bl,sd)) ) ud->state.running = 0; - } - else if (ud->target_to) { + } else if (ud->target_to) { // Update target trajectory. struct block_list *tbl = map_id2bl(ud->target_to); if (!tbl || !status_check_visibility(bl, tbl)) { // Cancel chase. ud->to_x = bl->x; ud->to_y = bl->y; + if (tbl && bl->type == BL_MOB && mob_warpchase((TBL_MOB*)bl, tbl) ) return 0; + ud->target_to = 0; + return 0; } if (tbl->m == bl->m && check_distance_bl(bl, tbl, ud->chaserange)) { // Reached destination. @@ -390,12 +399,14 @@ static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data } } else { // Update chase-path unit_walktobl(bl, tbl, ud->chaserange, ud->state.walk_easy|(ud->state.attack_continue?2:0)); + return 0; } } else { // Stopped walking. Update to_x and to_y to current location [Skotlex] ud->to_x = bl->x; ud->to_y = bl->y; } + return 0; } @@ -413,7 +424,9 @@ int unit_delay_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data) if (!bl || bl->prev == NULL) return 0; + unit_walktoxy(bl, (short)((data>>16)&0xffff), (short)(data&0xffff), 0); + return 1; } @@ -436,6 +449,7 @@ int unit_delay_walktobl_timer(int tid, unsigned int tick, int id, intptr_t data) unit_walktobl(bl, tbl, 0, 0); ud->target_to = 0; } + return 1; } @@ -462,7 +476,8 @@ int unit_walktoxy( struct block_list *bl, short x, short y, unsigned char flag) ud = unit_bl2ud(bl); - if (ud == NULL) return 0; + if (ud == NULL) + return 0; if (bl->type == BL_PC) sd = BL_CAST(BL_PC, bl); @@ -480,11 +495,11 @@ int unit_walktoxy( struct block_list *bl, short x, short y, unsigned char flag) if ((wpd.path_len > battle_config.max_walk_path) && (bl->type != BL_NPC)) return 0; - if (flag&4){ + if (flag&4) { unit_unattackable(bl); unit_stop_attack(bl); - if(DIFF_TICK(ud->canmove_tick, gettick()) > 0 && DIFF_TICK(ud->canmove_tick, gettick()) < 2000) - { // Delay walking command. [Skotlex] + + if(DIFF_TICK(ud->canmove_tick, gettick()) > 0 && DIFF_TICK(ud->canmove_tick, gettick()) < 2000) { // Delay walking command. [Skotlex] add_timer(ud->canmove_tick+1, unit_delay_walktoxy_timer, bl->id, (x<<16)|(y&0xFFFF)); return 1; } @@ -515,10 +530,14 @@ int unit_walktoxy( struct block_list *bl, short x, short y, unsigned char flag) } // Start timer to recall summon - if (sd && sd->md) unit_check_start_teleport_timer(&sd->md->bl); - if (sd && sd->ed) unit_check_start_teleport_timer(&sd->ed->bl); - if (sd && sd->hd) unit_check_start_teleport_timer(&sd->hd->bl); - if (sd && sd->pd) unit_check_start_teleport_timer(&sd->pd->bl); + if (sd && sd->md) + unit_check_start_teleport_timer(&sd->md->bl); + if (sd && sd->ed) + unit_check_start_teleport_timer(&sd->ed->bl); + if (sd && sd->hd) + unit_check_start_teleport_timer(&sd->hd->bl); + if (sd && sd->pd) + unit_check_start_teleport_timer(&sd->pd->bl); return unit_walktoxy_sub(bl); } @@ -558,6 +577,7 @@ static int unit_walktobl_sub(int tid, unsigned int tick, int id, intptr_t data) set_mobstate(bl, ud->state.attack_continue); } } + return 0; } @@ -580,7 +600,9 @@ int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, unsi nullpo_ret(tbl); ud = unit_bl2ud(bl); - if( ud == NULL) return 0; + + if(ud == NULL) + return 0; if (!(status_get_mode(bl)&MD_CANMOVE)) return 0; @@ -589,6 +611,7 @@ int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, unsi ud->to_x = bl->x; ud->to_y = bl->y; ud->target_to = 0; + return 0; } @@ -605,6 +628,7 @@ int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, unsi if(ud->walktimer != INVALID_TIMER) { ud->state.change_walk_target = 1; set_mobstate(bl, flag&2); + return 1; } @@ -623,8 +647,10 @@ int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, unsi if (unit_walktoxy_sub(bl)) { set_mobstate(bl, flag&2); + return 1; } + return 0; } @@ -645,6 +671,7 @@ int unit_run(struct block_list *bl) if (!unit_can_move(bl)) { status_change_end(bl, SC_RUN, INVALID_TIMER); + return 0; } @@ -655,7 +682,7 @@ int unit_run(struct block_list *bl) // Determine destination cell to_x = bl->x; to_y = bl->y; - for(i=0;im,to_x+dir_x,to_y+dir_y,CELL_CHKPASS)) break; @@ -678,16 +705,20 @@ int unit_run(struct block_list *bl) skill_blown(bl,bl,skill_get_blewcount(TK_RUN,lv),unit_getdir(bl),0); clif_fixpos(bl); // Why is a clif_slide (skill_blown) AND a fixpos needed? Ask Aegis. clif_status_change(bl, SI_BUMP, 0, 0, 0, 0, 0); + return 0; } + if (unit_walktoxy(bl, to_x, to_y, 1)) return 1; + // There must be an obstacle nearby. Attempt walking one cell at a time. do { to_x -= dir_x; to_y -= dir_y; } while (--i > 0 && !unit_walktoxy(bl, to_x, to_y, 1)); - if (i==0) { + + if (i == 0) { // Copy-paste from above clif_status_change(bl, SI_BUMP, 1, 0, 0, 0, 0); @@ -698,8 +729,10 @@ int unit_run(struct block_list *bl) skill_blown(bl,bl,skill_get_blewcount(TK_RUN,lv),unit_getdir(bl),0); clif_fixpos(bl); clif_status_change(bl, SI_BUMP, 0, 0, 0, 0, 0); + return 0; } + return 1; } @@ -733,7 +766,7 @@ int unit_wugdash(struct block_list *bl, struct map_session_data *sd) to_x = bl->x; to_y = bl->y; - for(i=0;im,to_x+dir_x,to_y+dir_y,CELL_CHKPASS)) break; @@ -748,20 +781,23 @@ int unit_wugdash(struct block_list *bl, struct map_session_data *sd) unit_bl2ud(bl)->state.running = 0; status_change_end(bl,SC_WUGDASH,INVALID_TIMER); - if( sd ){ + if( sd ) { clif_fixpos(bl); skill_castend_damage_id(bl, &sd->bl, RA_WUGDASH, lv, gettick(), SD_LEVEL); } + return 0; } + if (unit_walktoxy(bl, to_x, to_y, 1)) return 1; + do { to_x -= dir_x; to_y -= dir_y; } while (--i > 0 && !unit_walktoxy(bl, to_x, to_y, 1)); - if (i==0) { + if (i==0) { unit_bl2ud(bl)->state.running = 0; status_change_end(bl,SC_WUGDASH,INVALID_TIMER); @@ -769,8 +805,10 @@ int unit_wugdash(struct block_list *bl, struct map_session_data *sd) clif_fixpos(bl); skill_castend_damage_id(bl, &sd->bl, RA_WUGDASH, lv, gettick(), SD_LEVEL); } + return 0; } + return 1; } @@ -784,8 +822,10 @@ int unit_wugdash(struct block_list *bl, struct map_session_data *sd) int unit_escape(struct block_list *bl, struct block_list *target, short dist) { uint8 dir = map_calc_dir(target, bl->x, bl->y); + while( dist > 0 && map_getcell(bl->m, bl->x + dist*dirx[dir], bl->y + dist*diry[dir], CELL_CHKNOREACH) ) dist--; + return ( dist > 0 && unit_walktoxy(bl, bl->x + dist*dirx[dir], bl->y + dist*diry[dir], 0) ); } @@ -806,10 +846,12 @@ int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int easy, bool struct map_session_data *sd = NULL; nullpo_ret(bl); + sd = BL_CAST(BL_PC, bl); ud = unit_bl2ud(bl); - if( ud == NULL) return 0; + if(ud == NULL) + return 0; unit_stop_walking(bl,1); unit_stop_attack(bl); @@ -837,8 +879,10 @@ int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int easy, bool if(sd) { if( sd->touching_id ) npc_touchnext_areanpc(sd,false); + if(map_getcell(bl->m,bl->x,bl->y,CELL_CHKNPC)) { npc_touch_areanpc(sd,bl->m,bl->x,bl->y); + if (bl->prev == NULL) // Script could have warped char, abort remaining of the function. return 0; } else @@ -848,16 +892,19 @@ int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int easy, bool // Check if pet needs to be teleported. [Skotlex] int flag = 0; struct block_list* pbl = &sd->pd->bl; + if( !checkpath && !path_search(NULL,pbl->m,pbl->x,pbl->y,dst_x,dst_y,0,CELL_CHKNOPASS) ) flag = 1; else if (!check_distance_bl(&sd->bl, pbl, AREA_SIZE)) // Too far, teleport. flag = 2; + if( flag ) { unit_movepos(pbl,sd->bl.x,sd->bl.y, 0, 0); clif_slide(pbl,pbl->x,pbl->y); } } } + return 1; } @@ -870,13 +917,21 @@ int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int easy, bool int unit_setdir(struct block_list *bl,unsigned char dir) { struct unit_data *ud; - nullpo_ret(bl ); + + nullpo_ret(bl); + ud = unit_bl2ud(bl); - if (!ud) return 0; + + if (!ud) + return 0; + ud->dir = dir; + if (bl->type == BL_PC) ((TBL_PC *)bl)->head_dir = 0; + clif_changed_dir(bl, AREA); + return 0; } @@ -888,9 +943,14 @@ int unit_setdir(struct block_list *bl,unsigned char dir) uint8 unit_getdir(struct block_list *bl) { struct unit_data *ud; - nullpo_ret(bl ); + + nullpo_ret(bl); + ud = unit_bl2ud(bl); - if (!ud) return 0; + + if (!ud) + return 0; + return ud->dir; } @@ -919,9 +979,8 @@ int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag) nx = result>>16; ny = result&0xffff; - if(!su) { + if(!su) unit_stop_walking(bl, 0); - } if( sd ) { sd->ud.to_x = nx; @@ -939,15 +998,13 @@ int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag) skill_unit_move_unit_group(su->group, bl->m, dx, dy); else skill_unit_move_unit(bl, nx, ny); - } - else + } else map_moveblock(bl, nx, ny, gettick()); map_foreachinmovearea(clif_insight, bl, AREA_SIZE, -dx, -dy, bl->type == BL_PC ? BL_ALL : BL_PC, bl); - if(!(flag&1)) { + if(!(flag&1)) clif_blown(bl); - } if(sd) { if(sd->touching_id) @@ -980,7 +1037,9 @@ int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag) int unit_warp(struct block_list *bl,short m,short x,short y,clr_type type) { struct unit_data *ud; + nullpo_ret(bl); + ud = unit_bl2ud(bl); if(bl->prev==NULL || !ud) @@ -991,12 +1050,14 @@ int unit_warp(struct block_list *bl,short m,short x,short y,clr_type type) // animation, it messes up with unit_remove_map! [Skotlex] return 1; - if( m<0 ) m=bl->m; + if( m < 0 ) + m = bl->m; switch (bl->type) { case BL_MOB: if (map[bl->m].flag.monster_noteleport && ((TBL_MOB*)bl)->master_id == 0) return 1; + if (m != bl->m && map[m].flag.nobranch && battle_config.mob_warp&4 && !(((TBL_MOB *)bl)->master_id)) return 1; break; @@ -1006,7 +1067,7 @@ int unit_warp(struct block_list *bl,short m,short x,short y,clr_type type) break; } - if (x<0 || y<0) { // Random map position. + if (x < 0 || y < 0) { // Random map position. if (!map_search_freecell(NULL, m, &x, &y, -1, -1, 1)) { ShowWarning("unit_warp failed. Unit Id:%d/Type:%d, target position map %d (%s) at [%d,%d]\n", bl->id, bl->type, m, map[m].name, x, y); return 2; @@ -1031,12 +1092,13 @@ int unit_warp(struct block_list *bl,short m,short x,short y,clr_type type) battle_config.clear_unit_onwarp&bl->type) skill_clear_unitgroup(bl); - bl->x=ud->to_x=x; - bl->y=ud->to_y=y; - bl->m=m; + bl->x = ud->to_x = x; + bl->y = ud->to_y = y; + bl->m = m; if(map_addblock(bl)) return 4; //error on adding bl to map + clif_spawn(bl); skill_unit_move(bl,gettick(),1); @@ -1058,11 +1120,14 @@ int unit_stop_walking(struct block_list *bl,int type) struct unit_data *ud; const struct TimerData* td; unsigned int tick; + nullpo_ret(bl); ud = unit_bl2ud(bl); + if(!ud || ud->walktimer == INVALID_TIMER) return 0; + // NOTE: We are using timer data after deleting it because we know the // delete_timer function does not mess with it. If the function's // behaviour changes in the future, this code could break! @@ -1071,6 +1136,7 @@ int unit_stop_walking(struct block_list *bl,int type) ud->walktimer = INVALID_TIMER; ud->state.change_walk_target = 0; tick = gettick(); + if( (type&0x02 && !ud->walkpath.path_pos) // Force moving at least one cell. || (type&0x04 && td && DIFF_TICK(td->tick, tick) <= td->data/2) // Enough time has passed to cover half-cell ) { @@ -1085,14 +1151,16 @@ int unit_stop_walking(struct block_list *bl,int type) ud->walkpath.path_pos = 0; ud->to_x = bl->x; ud->to_y = bl->y; + if(bl->type == BL_PET && type&~0xff) ud->canmove_tick = gettick() + (type>>8); - // Readded, the check in unit_set_walkdelay means dmg during running won't fall through to this place in code [Kevin] + // Re-added, the check in unit_set_walkdelay means dmg during running won't fall through to this place in code [Kevin] if (ud->state.running) { status_change_end(bl, SC_RUN, INVALID_TIMER); status_change_end(bl, SC_WUGDASH, INVALID_TIMER); } + return 1; } @@ -1121,8 +1189,12 @@ int unit_skilluse_id(struct block_list *src, int target_id, uint16 skill_id, uin int unit_is_walking(struct block_list *bl) { struct unit_data *ud = unit_bl2ud(bl); + nullpo_ret(bl); - if(!ud) return 0; + + if(!ud) + return 0; + return (ud->walktimer != INVALID_TIMER); } @@ -1140,6 +1212,7 @@ int unit_can_move(struct block_list *bl) { struct status_change *sc; nullpo_ret(bl); + ud = unit_bl2ud(bl); sc = status_get_sc(bl); sd = BL_CAST(BL_PC, bl); @@ -1179,8 +1252,8 @@ int unit_can_move(struct block_list *bl) { if ((sc->option & OPTION_HIDE) && (!sd || pc_checkskill(sd, RG_TUNNELDRIVE) <= 0)) return 0; - } + return 1; } @@ -1204,10 +1277,10 @@ int unit_resume_running(int tid, unsigned int tick, int id, intptr_t data) clif_skill_nodamage(ud->bl,ud->bl,TK_RUN,ud->skill_lv, sc_start4(ud->bl,ud->bl,status_skill2sc(TK_RUN),100,ud->skill_lv,unit_getdir(ud->bl),0,0,0)); - if (sd) clif_walkok(sd); + if (sd) + clif_walkok(sd); return 0; - } /** @@ -1223,7 +1296,9 @@ int unit_resume_running(int tid, unsigned int tick, int id, intptr_t data) int unit_set_walkdelay(struct block_list *bl, unsigned int tick, int delay, int type) { struct unit_data *ud = unit_bl2ud(bl); - if (delay <= 0 || !ud) return 0; + + if (delay <= 0 || !ud) + return 0; // /MvP mobs have no walk delay if( bl->type == BL_MOB && (((TBL_MOB*)bl)->status.mode&MD_BOSS) ) @@ -1237,21 +1312,25 @@ int unit_set_walkdelay(struct block_list *bl, unsigned int tick, int delay, int if (!unit_can_move(bl)) return 0; } + ud->canmove_tick = tick + delay; + if (ud->walktimer != INVALID_TIMER) { // Stop walking, if chasing, readjust timers. - if (delay == 1) { // Minimal delay (walk-delay) disabled. Just stop walking. + if (delay == 1) // Minimal delay (walk-delay) disabled. Just stop walking. unit_stop_walking(bl,4); - } else { + else { // Resume running after can move again [Kevin] - if(ud->state.running) { + if(ud->state.running) add_timer(ud->canmove_tick, unit_resume_running, bl->id, (intptr_t)ud); - } else { + else { unit_stop_walking(bl,2|4); + if(ud->target) add_timer(ud->canmove_tick+1, unit_walktobl_sub, bl->id, ud->target); } } } + return 1; } @@ -1262,7 +1341,7 @@ int unit_set_walkdelay(struct block_list *bl, unsigned int tick, int delay, int * @param skill_id: Skill ID * @param skill_lv: Skill Level * @param casttime: Initial cast time before cast time reductions - * @param castcancel: Whether or not the skill can be cancelled by interuption (hit) + * @param castcancel: Whether or not the skill can be cancelled by interruption (hit) * @return Success(1); Fail(0); */ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, uint16 skill_lv, int casttime, int castcancel) @@ -1283,8 +1362,11 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui sd = BL_CAST(BL_PC, src); ud = unit_bl2ud(src); - if(ud == NULL) return 0; + if(ud == NULL) + return 0; + sc = status_get_sc(src); + if (sc && !sc->count) sc = NULL; // Unneeded @@ -1292,8 +1374,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui if (sc && sc->data[SC_COMBO] && skill_is_combo(skill_id) && (sc->data[SC_COMBO]->val1 == skill_id || - (sd?skill_check_condition_castbegin(sd,skill_id,skill_lv):0) )) - { + (sd?skill_check_condition_castbegin(sd,skill_id,skill_lv):0) )) { if (sc->data[SC_COMBO]->val2) target_id = sc->data[SC_COMBO]->val2; else @@ -1301,13 +1382,12 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui if( skill_get_inf(skill_id)&INF_SELF_SKILL && skill_get_nk(skill_id)&NK_NO_DAMAGE )// exploit fix target_id = src->id; + combo = 1; - } - else if ( target_id == src->id && + } else if ( target_id == src->id && skill_get_inf(skill_id)&INF_SELF_SKILL && (skill_get_inf2(skill_id)&INF2_NO_TARGET_SELF || - (skill_id == RL_QD_SHOT && sc && sc->data[SC_QD_SHOT_READY])) ) - { + (skill_id == RL_QD_SHOT && sc && sc->data[SC_QD_SHOT_READY])) ) { target_id = ud->target; // Auto-select target. [Skotlex] combo = 1; } @@ -1328,30 +1408,36 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui case WE_FEMALE: if (!sd->status.partner_id) return 0; + target = (struct block_list*)map_charid2sd(sd->status.partner_id); + if (!target) { clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); return 0; } break; } + if (target) target_id = target->id; - } else if (src->type == BL_HOM) + } else if (src->type == BL_HOM) { switch(skill_id) { // Homun-auto-target skills. case HLIF_HEAL: case HLIF_AVOID: case HAMI_DEFENCE: case HAMI_CASTLE: target = battle_get_master(src); - if (!target) return 0; + + if (!target) + return 0; + target_id = target->id; break; case MH_SONIC_CRAW: - case MH_TINDER_BREAKER: - { + case MH_TINDER_BREAKER: { int skill_id2 = ((skill_id==MH_SONIC_CRAW)?MH_MIDNIGHT_FRENZY:MH_EQC); - if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id2){ // It's a combo + + if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id2) { // It's a combo target_id = sc->data[SC_COMBO]->val2; combo = 1; casttime = -1; @@ -1359,6 +1445,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui break; } } + } if( !target ) // Choose default target target = map_id2bl(target_id); @@ -1383,10 +1470,12 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui if(skill_get_inf2(skill_id)&INF2_NO_NEARNPC && skill_isNotOk_npcRange(src,skill_id,skill_lv,target->x,target->y)) { if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + return 0; } tstatus = status_get_status_data(target); + // Record the status of the previous skill) if(sd) { switch(skill_id) { @@ -1402,6 +1491,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); return 0; } + sd->skill_id_old = skill_id; break; case WL_WHITEIMPRISON: @@ -1419,6 +1509,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui case CR_DEVOTION: if (target->type == BL_PC) { uint8 i = 0, count = min(skill_lv, MAX_DEVOTION); + ARR_FIND(0, count, i, sd->devotion[i] == target_id); if (i == count) { ARR_FIND(0, count, i, sd->devotion[i] == 0); @@ -1429,9 +1520,9 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui } } break; - case RL_C_MARKER: - { + case RL_C_MARKER: { uint8 i = 0; + ARR_FIND(0, MAX_SKILL_CRIMSON_MARKER, i, sd->c_marker[i] == target_id); if (i == MAX_SKILL_CRIMSON_MARKER) { ARR_FIND(0, MAX_SKILL_CRIMSON_MARKER, i, sd->c_marker[i] == 0); @@ -1443,11 +1534,12 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui } break; } + if (!skill_check_condition_castbegin(sd, skill_id, skill_lv)) return 0; } - if( src->type == BL_MOB ) + if( src->type == BL_MOB ) { switch( skill_id ) { case NPC_SUMMONSLAVE: case NPC_SUMMONMONSTER: @@ -1455,6 +1547,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui if( ((TBL_MOB*)src)->master_id && ((TBL_MOB*)src)->special_state.ai ) return 0; } + } if (src->type == BL_NPC) // NPC-objects can override cast distance range = AREA_SIZE; // Maximum visible distance before NPC goes out of sight @@ -1470,9 +1563,8 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui } else if( src->type == BL_MER && skill_id == MA_REMOVETRAP ) { if( !battle_check_range(battle_get_master(src), target, range + 1) ) return 0; // Aegis calc remove trap based on Master position, ignoring mercenary O.O - } else if( !battle_check_range(src, target, range + (skill_id == RG_CLOSECONFINE?0:2)) ) { + } else if( !battle_check_range(src, target, range + (skill_id == RG_CLOSECONFINE?0:2)) ) return 0; // Arrow-path check failed. - } } if (!combo) // Stop attack on non-combo skills [Skotlex] @@ -1526,12 +1618,14 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui if( sc && sc->data[SC_BASILICA] ) casttime = -1; // No Casting time on basilica cancel break; - case KN_CHARGEATK: - { + case KN_CHARGEATK: { unsigned int k = (distance_bl(src,target)-1)/3; // +100% every 3 cells of distance - if( k > 2 ) k = 2; // ...but hard-limited to 300%. + + if( k > 2 ) + k = 2; // ...but hard-limited to 300%. + casttime += casttime * k; - } + } break; case GD_EMERGENCYCALL: // Emergency Call double cast when the user has learned Leap [Daegaladh] if( sd && pc_checkskill(sd,TK_HIGHJUMP) ) @@ -1573,18 +1667,22 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui if(!ud->state.running) // Need TK_RUN or WUGDASH handler to be done before that, see bugreport:6026 unit_stop_walking(src,1);// Even though this is not how official works but this will do the trick. bugreport:6829 + // In official this is triggered even if no cast time. clif_skillcasting(src, src->id, target_id, 0,0, skill_id, skill_get_ele(skill_id, skill_lv), casttime); + if (sd && target->type == BL_MOB) { TBL_MOB *md = (TBL_MOB*)target; mobskill_event(md, src, tick, -1); // Cast targetted skill event. + if (tstatus->mode&(MD_CASTSENSOR_IDLE|MD_CASTSENSOR_CHASE) && battle_check_target(target, src, BCT_ENEMY) > 0) { switch (md->state.skillstate) { case MSS_RUSH: case MSS_FOLLOW: if (!(tstatus->mode&MD_CASTSENSOR_CHASE)) break; + md->target_id = src->id; md->state.aggressive = (tstatus->mode&MD_ANGRY)?1:0; md->min_chase = md->db->range3; @@ -1593,6 +1691,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui case MSS_WALK: if (!(tstatus->mode&MD_CASTSENSOR_IDLE)) break; + md->target_id = src->id; md->state.aggressive = (tstatus->mode&MD_ANGRY)?1:0; md->min_chase = md->db->range3; @@ -1606,6 +1705,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui if( !sd || sd->skillitem != skill_id || skill_get_cast(skill_id,skill_lv) ) ud->canact_tick = tick + casttime + 100; + if( sd ) { switch( skill_id ) { case CG_ARROWVULCAN: @@ -1613,6 +1713,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui break; } } + ud->skilltarget = target_id; ud->skillx = 0; ud->skilly = 0; @@ -1623,16 +1724,21 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui // These 3 status do not stack, so it's efficient to use if-else if( sc->data[SC_CLOAKING] && !(sc->data[SC_CLOAKING]->val4&4) && skill_id != AS_CLOAKING ) { status_change_end(src, SC_CLOAKING, INVALID_TIMER); - if (!src->prev) return 0; // Warped away! + + if (!src->prev) + return 0; // Warped away! } else if( sc->data[SC_CLOAKINGEXCEED] && !(sc->data[SC_CLOAKINGEXCEED]->val4&4) && skill_id != GC_CLOAKINGEXCEED ) { status_change_end(src,SC_CLOAKINGEXCEED, INVALID_TIMER); - if (!src->prev) return 0; + + if (!src->prev) + return 0; } } if( casttime > 0 ) { ud->skilltimer = add_timer( tick+casttime, skill_castend_id, src->id, 0 ); + if( sd && (pc_checkskill(sd,SA_FREECAST) > 0 || skill_id == LG_EXEEDBREAK) ) status_calc_bl(&sd->bl, SCB_SPEED); } else @@ -1684,23 +1790,30 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui nullpo_ret(src); - if (!src->prev) return 0; // Not on the map - if(status_isdead(src)) return 0; + if (!src->prev) + return 0; // Not on the map + + if(status_isdead(src)) + return 0; sd = BL_CAST(BL_PC, src); ud = unit_bl2ud(src); - if(ud == NULL) return 0; + + if(ud == NULL) + return 0; if(ud->skilltimer != INVALID_TIMER) // Normally not needed since clif.c checks for it, but at/char/script commands don't! [Skotlex] return 0; sc = status_get_sc(src); + if (sc && !sc->count) sc = NULL; if( sd ) { if( skill_isNotOk(skill_id, sd) || !skill_check_condition_castbegin(sd, skill_id, skill_lv) ) return 0; + /** * Pneuma cannot be cancelled past this point, the client displays the animation even, * if we cancel it from nodamage_id, so it has to be here for it to not display the animation. @@ -1723,11 +1836,14 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui if(skill_get_inf2(skill_id)&INF2_NO_NEARNPC && skill_isNotOk_npcRange(src,skill_id,skill_lv,skill_x,skill_y)) { if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + return 0; } if( map_getcell(src->m, skill_x, skill_y, CELL_CHKWALL) ) { // Can't cast ground targeted spells on wall cells - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) + clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + return 0; } @@ -1745,8 +1861,7 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui if( skill_get_state(ud->skill_id) == ST_MOVE_ENABLE ) { if( !unit_can_reach_bl(src, &bl, range + 1, 1, NULL, NULL) ) return 0; // Walk-path check failed. - } - else if( !battle_check_range(src, &bl, range + 1) ) + }else if( !battle_check_range(src, &bl, range + 1) ) return 0; // Arrow-path check failed. unit_stop_attack(src); @@ -1765,6 +1880,7 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui ud->state.skillcastcancel = castcancel&&casttime>0?1:0; if( !sd || sd->skillitem != skill_id || skill_get_cast(skill_id,skill_lv) ) ud->canact_tick = tick + casttime + 100; + // if( sd ) // { // switch( skill_id ) @@ -1773,6 +1889,7 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui // sd->canequip_tick = tick + casttime; // } // } + ud->skill_id = skill_id; ud->skill_lv = skill_lv; ud->skillx = skill_x; @@ -1783,18 +1900,25 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui // These 3 status do not stack, so it's efficient to use if-else if (sc->data[SC_CLOAKING] && !(sc->data[SC_CLOAKING]->val4&4)) { status_change_end(src, SC_CLOAKING, INVALID_TIMER); - if (!src->prev) return 0; // Warped away! + + if (!src->prev) + return 0; // Warped away! } else if (sc->data[SC_CLOAKINGEXCEED] && !(sc->data[SC_CLOAKINGEXCEED]->val4&4)) { status_change_end(src, SC_CLOAKINGEXCEED, INVALID_TIMER); - if (!src->prev) return 0; + + if (!src->prev) + return 0; } } unit_stop_walking(src,1); + // In official this is triggered even if no cast time. clif_skillcasting(src, src->id, 0, skill_x, skill_y, skill_id, skill_get_ele(skill_id, skill_lv), casttime); + if( casttime > 0 ) { ud->skilltimer = add_timer( tick+casttime, skill_castend_pos, src->id, 0 ); + if( (sd && pc_checkskill(sd,SA_FREECAST) > 0) || skill_id == LG_EXEEDBREAK) status_calc_bl(&sd->bl, SCB_SPEED); } else { @@ -1804,6 +1928,7 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui if( sd ) sd->canlog_tick = gettick(); + return 1; } @@ -1823,11 +1948,13 @@ int unit_set_target(struct unit_data* ud, int target_id) if( ud->target && (target = map_id2bl(ud->target)) && (ux = unit_bl2ud(target)) && ux->target_count > 0 ) ux->target_count --; + if( target_id && (target = map_id2bl(target_id)) && (ux = unit_bl2ud(target)) ) ux->target_count ++; } ud->target = target_id; + return 0; } @@ -1839,6 +1966,7 @@ int unit_set_target(struct unit_data* ud, int target_id) int unit_stop_attack(struct block_list *bl) { struct unit_data *ud = unit_bl2ud(bl); + nullpo_ret(bl); if(!ud || ud->attacktimer == INVALID_TIMER) @@ -1847,6 +1975,7 @@ int unit_stop_attack(struct block_list *bl) delete_timer( ud->attacktimer, unit_attack_timer ); ud->attacktimer = INVALID_TIMER; unit_set_target(ud, 0); + return 0; } @@ -1858,6 +1987,7 @@ int unit_stop_attack(struct block_list *bl) int unit_unattackable(struct block_list *bl) { struct unit_data *ud = unit_bl2ud(bl); + if (ud) { ud->state.attack_continue = 0; ud->target_to = 0; @@ -1868,6 +1998,7 @@ int unit_unattackable(struct block_list *bl) mob_unlocktarget((struct mob_data*)bl, gettick()) ; else if(bl->type == BL_PET) pet_unlocktarget((struct pet_data*)bl); + return 0; } @@ -1886,30 +2017,35 @@ int unit_attack(struct block_list *src,int target_id,int continuous) nullpo_ret(ud = unit_bl2ud(src)); target = map_id2bl(target_id); - if( target==NULL || status_isdead(target) ) { + if( target == NULL || status_isdead(target) ) { unit_unattackable(src); return 1; } if( src->type == BL_PC ) { TBL_PC* sd = (TBL_PC*)src; + if( target->type == BL_NPC ) { // Monster npcs [Valaris] npc_click(sd,(TBL_NPC*)target); // Submitted by leinsirk10 [Celest] return 0; } + if( pc_is90overweight(sd) || pc_isridingwug(sd) ) { // Overweight or mounted on warg - stop attacking unit_stop_attack(src); return 0; } + if( !pc_can_attack(sd, target_id) ) { unit_stop_attack(src); return 0; } } + if( battle_check_target(src,target,BCT_ENEMY) <= 0 || !status_check_skilluse(src, target, 0, 0) ) { unit_unattackable(src); return 1; } + ud->state.attack_continue = continuous; unit_set_target(ud, target_id); @@ -1924,8 +2060,7 @@ int unit_attack(struct block_list *src,int target_id,int continuous) if(src->type == BL_MOB) ((TBL_MOB*)src)->state.skillstate = ((TBL_MOB*)src)->state.aggressive?MSS_ANGRY:MSS_BERSERK; - if(DIFF_TICK(ud->attackabletime, gettick()) > 0) - // Do attack next time it is possible. [Skotlex] + if(DIFF_TICK(ud->attackabletime, gettick()) > 0) // Do attack next time it is possible. [Skotlex] ud->attacktimer=add_timer(ud->attackabletime,unit_attack_timer,src->id,0); else // Attack NOW. unit_attack_timer(INVALID_TIMER, gettick(), src->id, 0); @@ -1957,6 +2092,7 @@ int unit_cancel_combo(struct block_list *bl) delete_timer(ud->attacktimer, unit_attack_timer); ud->attacktimer=add_timer(ud->attackabletime,unit_attack_timer,bl->id,0); + return 1; } @@ -1990,35 +2126,44 @@ bool unit_can_reach_pos(struct block_list *bl,int x,int y, int easy) */ bool unit_can_reach_bl(struct block_list *bl,struct block_list *tbl, int range, int easy, short *x, short *y) { - short dx,dy; + short dx, dy; + nullpo_retr(false, bl); nullpo_retr(false, tbl); if( bl->m != tbl->m) return false; - if( bl->x==tbl->x && bl->y==tbl->y ) + if( bl->x == tbl->x && bl->y == tbl->y ) return true; - if(range>0 && !check_distance_bl(bl, tbl, range)) + if(range > 0 && !check_distance_bl(bl, tbl, range)) return false; // It judges whether it can adjoin or not. - dx=tbl->x - bl->x; - dy=tbl->y - bl->y; - dx=(dx>0)?1:((dx<0)?-1:0); - dy=(dy>0)?1:((dy<0)?-1:0); + dx = tbl->x - bl->x; + dy = tbl->y - bl->y; + dx = (dx > 0) ? 1 : ((dx < 0) ? -1 : 0); + dy = (dy > 0) ? 1 : ((dy < 0) ? -1 : 0); if (map_getcell(tbl->m,tbl->x-dx,tbl->y-dy,CELL_CHKNOPASS)) { // Look for a suitable cell to place in. int i; - for(i=0;i<9 && map_getcell(tbl->m,tbl->x-dirx[i],tbl->y-diry[i],CELL_CHKNOPASS);i++); - if (i==9) return false; // No valid cells. + + for(i = 0; i < 9 && map_getcell(tbl->m,tbl->x-dirx[i],tbl->y-diry[i],CELL_CHKNOPASS); i++); + + if (i == 9) + return false; // No valid cells. + dx = dirx[i]; dy = diry[i]; } - if (x) *x = tbl->x-dx; - if (y) *y = tbl->y-dy; + if (x) + *x = tbl->x-dx; + + if (y) + *y = tbl->y-dy; + return path_search(NULL,bl->m,bl->x,bl->y,tbl->x-dx,tbl->y-dy,easy,CELL_CHKNOREACH); } @@ -2034,6 +2179,7 @@ int unit_calc_pos(struct block_list *bl, int tx, int ty, uint8 dir) { int dx, dy, x, y; struct unit_data *ud = unit_bl2ud(bl); + nullpo_ret(ud); if(dir > 7) @@ -2049,32 +2195,54 @@ int unit_calc_pos(struct block_list *bl, int tx, int ty, uint8 dir) y = ty + dy; if( !unit_can_reach_pos(bl, x, y, 0) ) { - if( dx > 0 ) x--; else if( dx < 0 ) x++; - if( dy > 0 ) y--; else if( dy < 0 ) y++; + if( dx > 0 ) + x--; + else if( dx < 0 ) + x++; + + if( dy > 0 ) + y--; + else if( dy < 0 ) + y++; + if( !unit_can_reach_pos(bl, x, y, 0) ) { int i; + for( i = 0; i < 12; i++ ) { int k = rnd()%8; // Pick a Random Dir + dx = -dirx[k] * 2; dy = -diry[k] * 2; x = tx + dx; y = ty + dy; + if( unit_can_reach_pos(bl, x, y, 0) ) break; else { - if( dx > 0 ) x--; else if( dx < 0 ) x++; - if( dy > 0 ) y--; else if( dy < 0 ) y++; + if( dx > 0 ) + x--; + else if( dx < 0 ) + x++; + + if( dy > 0 ) + y--; + else if( dy < 0 ) + y++; + if( unit_can_reach_pos(bl, x, y, 0) ) break; } } + if( i == 12 ) { x = tx; y = tx; // Exactly Master Position + if( !unit_can_reach_pos(bl, x, y, 0) ) return 1; } } } + ud->to_x = x; ud->to_y = y; @@ -2097,8 +2265,9 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t struct mob_data *md = NULL; int range; - if( (ud=unit_bl2ud(src))==NULL ) + if( (ud = unit_bl2ud(src)) == NULL ) return 0; + if( ud->attacktimer != tid ) { ShowError("unit_attack_timer %d != %d\n",ud->attacktimer,tid); return 0; @@ -2107,7 +2276,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t sd = BL_CAST(BL_PC, src); md = BL_CAST(BL_MOB, src); ud->attacktimer = INVALID_TIMER; - target=map_id2bl(ud->target); + target = map_id2bl(ud->target); if( src == NULL || src->prev == NULL || target==NULL || target->prev == NULL ) return 0; @@ -2126,6 +2295,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t if( src->m != target->m ) { if( src->type == BL_MOB && mob_warpchase((TBL_MOB*)src, target) ) return 1; // Follow up. + return 0; } @@ -2135,15 +2305,20 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t if( !battle_config.sdelay_attack_enable && DIFF_TICK(ud->canact_tick,tick) > 0 && !(sd && pc_checkskill(sd,SA_FREECAST) > 0) ) { // Attacking when under cast delay has restrictions: if( tid == INVALID_TIMER ) { // Requested attack. - if(sd) clif_skill_fail(sd,1,USESKILL_FAIL_SKILLINTERVAL,0); + if(sd) + clif_skill_fail(sd,1,USESKILL_FAIL_SKILLINTERVAL,0); + return 0; } + // Otherwise, we are in a combo-attack, delay this until your canact time is over. [Skotlex] if( ud->state.attack_continue ) { if( DIFF_TICK(ud->canact_tick, ud->attackabletime) > 0 ) ud->attackabletime = ud->canact_tick; + ud->attacktimer=add_timer(ud->attackabletime,unit_attack_timer,src->id,0); } + return 1; } @@ -2152,40 +2327,51 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t if( unit_is_walking(target) ) range++; // Extra range when chasing + if( !check_distance_bl(src,target,range) ) { // Chase if required. if(sd) clif_movetoattack(sd,target); else if(ud->state.attack_continue) unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy|2); + return 1; } + if( !battle_check_range(src,target,range) ) { // Within range, but no direct line of attack if( ud->state.attack_continue ) { - if(ud->chaserange > 2) ud->chaserange-=2; + if(ud->chaserange > 2) + ud->chaserange-=2; + unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy|2); } + return 1; } + // Sync packet only for players. // Non-players use the sync packet on the walk timer. [Skotlex] - if (tid == INVALID_TIMER && sd) clif_fixpos(src); + if (tid == INVALID_TIMER && sd) + clif_fixpos(src); if( DIFF_TICK(ud->attackabletime,tick) <= 0 ) { - if (battle_config.attack_direction_change && (src->type&battle_config.attack_direction_change)) { + if (battle_config.attack_direction_change && (src->type&battle_config.attack_direction_change)) ud->dir = map_calc_dir(src, target->x,target->y ); - } + if(ud->walktimer != INVALID_TIMER) unit_stop_walking(src,1); + if(md) { if (mobskill_use(md,tick,-1)) return 1; + if (sstatus->mode&MD_ASSIST && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME) { // Link monsters nearby [Skotlex] md->last_linktime = tick; map_foreachinrange(mob_linksearch, src, md->db->range2, BL_MOB, md->mob_id, target, tick); } } + if(src->type == BL_PET && pet_attackskill((TBL_PET*)src, target->id)) return 1; @@ -2194,7 +2380,9 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t if(sd && sd->status.pet_id > 0 && sd->pd && battle_config.pet_attack_support) pet_target_check(sd,target,0); + map_freeblock_unlock(); + /** * Applied when you're unable to attack (e.g. out of ammo) * We should stop here otherwise timer keeps on and this happens endlessly @@ -2203,6 +2391,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t return 1; ud->attackabletime = tick + sstatus->adelay; + // You can't move if you can't attack neither. if (src->type&battle_config.attack_walk_delay) unit_set_walkdelay(src, tick, sstatus->amotion, 1); @@ -2211,6 +2400,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t if(ud->state.attack_continue) { if (src->type == BL_PC && battle_config.idletime_option&IDLE_ATTACK) ((TBL_PC*)src)->idletime = last_tick; + ud->attacktimer = add_timer(ud->attackabletime,unit_attack_timer,src->id,0); } @@ -2231,9 +2421,12 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t static int unit_attack_timer(int tid, unsigned int tick, int id, intptr_t data) { struct block_list *bl; + bl = map_id2bl(id); + if(bl && unit_attack_timer_sub(bl, tid, tick) == 0) unit_unattackable(bl); + return 0; } @@ -2249,10 +2442,11 @@ int unit_skillcastcancel(struct block_list *bl, char type) { struct map_session_data *sd = NULL; struct unit_data *ud = unit_bl2ud( bl); - unsigned int tick=gettick(); - int ret=0, skill_id; + unsigned int tick = gettick(); + int ret = 0, skill_id; nullpo_ret(bl); + if (!ud || ud->skilltimer == INVALID_TIMER) return 0; // Nothing to cancel. @@ -2278,7 +2472,8 @@ int unit_skillcastcancel(struct block_list *bl, char type) ret=delete_timer( ud->skilltimer, skill_castend_pos ); else ret=delete_timer( ud->skilltimer, skill_castend_id ); - if(ret<0) + + if(ret < 0) ShowError("delete timer error : skill_id : %d\n",ret); ud->skilltimer = INVALID_TIMER; @@ -2294,9 +2489,11 @@ int unit_skillcastcancel(struct block_list *bl, char type) } } - if(bl->type==BL_MOB) ((TBL_MOB*)bl)->skill_idx = -1; + if(bl->type==BL_MOB) + ((TBL_MOB*)bl)->skill_idx = -1; clif_skillcastcancel(bl); + return 1; } @@ -2307,6 +2504,7 @@ int unit_skillcastcancel(struct block_list *bl, char type) void unit_dataset(struct block_list *bl) { struct unit_data *ud; + nullpo_retv(ud = unit_bl2ud(bl)); memset( ud, 0, sizeof( struct unit_data) ); @@ -2327,8 +2525,10 @@ void unit_dataset(struct block_list *bl) int unit_counttargeted(struct block_list* bl) { struct unit_data* ud; + if( bl && (ud = unit_bl2ud(bl)) ) return ud->target_count; + return 0; } @@ -2342,16 +2542,18 @@ int unit_changeviewsize(struct block_list *bl,short size) { nullpo_ret(bl); - size=(size<0)?-1:(size>0)?1:0; + size = (size < 0) ? -1 : (size > 0) ? 1 : 0; - if(bl->type == BL_PC) { - ((TBL_PC*)bl)->state.size=size; - } else if(bl->type == BL_MOB) { - ((TBL_MOB*)bl)->special_state.size=size; - } else + if(bl->type == BL_PC) + ((TBL_PC*)bl)->state.size = size; + else if(bl->type == BL_MOB) + ((TBL_MOB*)bl)->special_state.size = size; + else return 0; - if(size!=0) + + if(size != 0) clif_specialeffect(bl,421+size, AREA); + return 0; } @@ -2369,6 +2571,7 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, { struct unit_data *ud = unit_bl2ud(bl); struct status_change *sc = status_get_sc(bl); + nullpo_ret(ud); if(bl->prev == NULL) @@ -2380,13 +2583,16 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, if (ud->walktimer != INVALID_TIMER) unit_stop_walking(bl,0); + if (ud->attacktimer != INVALID_TIMER) unit_stop_attack(bl); + if (ud->skilltimer != INVALID_TIMER) unit_skillcastcancel(bl,0); // Do not reset can-act delay. [Skotlex] ud->attackabletime = ud->canmove_tick /*= ud->canact_tick*/ = gettick(); + if(sc && sc->count ) { // map-change/warp dispells. status_change_end(bl, SC_BLADESTOP, INVALID_TIMER); status_change_end(bl, SC_BASILICA, INVALID_TIMER); @@ -2433,15 +2639,20 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, if(sd->shadowform_id) { // If shadow target has leave the map struct block_list *d_bl = map_id2bl(sd->shadowform_id); + if( d_bl ) status_change_end(d_bl,SC__SHADOWFORM,INVALID_TIMER); } + // Leave/reject all invitations. if(sd->chatID) chat_leavechat(sd,0); + if(sd->trade_partner) trade_tradecancel(sd); + searchstore_close(sd); + if (sd->menuskill_id != AL_TELEPORT) { //bugreport:8027 if (sd->state.storage_flag == 1) storage_storage_quit(sd,0); @@ -2450,14 +2661,19 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, sd->state.storage_flag = 0; //Force close it when being warped. } - if(sd->party_invite>0) + + if(sd->party_invite > 0) party_reply_invite(sd,sd->party_invite,0); - if(sd->guild_invite>0) + + if(sd->guild_invite > 0) guild_reply_invite(sd,sd->guild_invite,0); - if(sd->guild_alliance>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); @@ -2475,12 +2691,13 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, 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, false)) { + if(pc_issit(sd) && pc_setstand(sd, false)) skill_sit(sd,0); - } + party_send_dot_remove(sd);// minimap dot fix [Kevin] guild_send_dot_remove(sd); bg_send_dot_remove(sd); @@ -2493,6 +2710,7 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* 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)." @@ -2505,47 +2723,51 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, } else if (--map[bl->m].users == 0 && battle_config.dynamic_mobs) map_removemobs(bl->m); - if( !(sd->sc.option&OPTION_INVISIBLE) ) { // Decrement the number of active pvp players on the map + + if( !(sd->sc.option&OPTION_INVISIBLE) ) // Decrement the number of active pvp players on the map --map[bl->m].users_pvp; - } + if( sd->state.hpmeter_visible ) { map[bl->m].hpmeter_visible--; sd->state.hpmeter_visible = 0; } + 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; 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->master && !pd->master->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); @@ -2553,42 +2775,52 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, map_delblock(bl); unit_free(bl,CLR_OUTSIGHT); map_freeblock_unlock(); + return 0; } 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; } 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 + default: + break;// do nothing } // /BL_MOB is handled by mob_dead unless the monster is not dead. if( bl->type != BL_MOB || !status_isdead(bl) ) clif_clearunit_area(bl,clrtype); + map_delblock(bl); map_freeblock_unlock(); + return 1; } @@ -2608,10 +2840,13 @@ void unit_remove_map_pc(struct map_session_data *sd, clr_type clrtype) if(sd->pd) unit_remove_map(&sd->pd->bl, clrtype); + if(hom_is_active(sd->hd)) unit_remove_map(&sd->hd->bl, clrtype); + if(sd->md) unit_remove_map(&sd->md->bl, clrtype); + if(sd->ed) unit_remove_map(&sd->ed->bl, clrtype); } @@ -2623,10 +2858,18 @@ void unit_remove_map_pc(struct map_session_data *sd, clr_type clrtype) */ void unit_free_pc(struct map_session_data *sd) { - if (sd->pd) unit_free(&sd->pd->bl,CLR_OUTSIGHT); - if (sd->hd) unit_free(&sd->hd->bl,CLR_OUTSIGHT); - if (sd->md) unit_free(&sd->md->bl,CLR_OUTSIGHT); - if (sd->ed) unit_free(&sd->ed->bl,CLR_OUTSIGHT); + if (sd->pd) + unit_free(&sd->pd->bl,CLR_OUTSIGHT); + + if (sd->hd) + unit_free(&sd->hd->bl,CLR_OUTSIGHT); + + if (sd->md) + unit_free(&sd->md->bl,CLR_OUTSIGHT); + + if (sd->ed) + unit_free(&sd->ed->bl,CLR_OUTSIGHT); + unit_free(&sd->bl,CLR_TELEPORT); } @@ -2641,9 +2884,11 @@ void unit_free_pc(struct map_session_data *sd) int unit_free(struct block_list *bl, clr_type clrtype) { struct unit_data *ud = unit_bl2ud( bl ); + nullpo_ret(ud); map_freeblock_lock(); + if( bl->prev ) // Players are supposed to logout with a "warp" effect. unit_remove_map(bl, clrtype); @@ -2676,6 +2921,7 @@ int unit_free(struct block_list *bl, clr_type clrtype) pc_cleareventtimer(sd); pc_inventory_rental_clear(sd); pc_delspiritball(sd,sd->spiritball,1); + for(i = 1; i < 5; i++) pc_del_talisman(sd, sd->talisman[i], i); @@ -2684,50 +2930,60 @@ int unit_free(struct block_list *bl, clr_type clrtype) sd->reg = NULL; sd->reg_num = 0; } + if( sd->regstr ) { int j; + for( j = 0; j < sd->regstr_num; ++j ) if( sd->regstr[j].data ) aFree(sd->regstr[j].data); + aFree(sd->regstr); sd->regstr = NULL; sd->regstr_num = 0; } + if( sd->st && sd->st->state != RUN ) {// free attached scripts that are waiting script_free_state(sd->st); sd->st = NULL; sd->npc_id = 0; } + if( sd->combos.count ) { aFree(sd->combos.bonus); aFree(sd->combos.id); sd->combos.count = 0; } - /* [Ind] */ - if( sd->sc_display_count ) { + + if( sd->sc_display_count ) { /* [Ind] */ for( i = 0; i < sd->sc_display_count; i++ ) ers_free(pc_sc_display_ers, sd->sc_display[i]); + sd->sc_display_count = 0; aFree(sd->sc_display); sd->sc_display = NULL; } + if( sd->quest_log != NULL ) { aFree(sd->quest_log); sd->quest_log = NULL; sd->num_quests = sd->avail_quests = 0; } + pc_itemgrouphealrate_clear(sd); break; } - case BL_PET: - { + case BL_PET: { struct pet_data *pd = (struct pet_data*)bl; struct map_session_data *sd = pd->master; + pet_hungry_timer_delete(pd); + if( pd->a_skill ) { aFree(pd->a_skill); pd->a_skill = NULL; } + if( pd->s_skill ) { if (pd->s_skill->timer != INVALID_TIMER) { if (pd->s_skill->id) @@ -2735,68 +2991,88 @@ int unit_free(struct block_list *bl, clr_type clrtype) else delete_timer(pd->s_skill->timer, pet_heal_timer); } + aFree(pd->s_skill); pd->s_skill = NULL; } + if( pd->recovery ) { if(pd->recovery->timer != INVALID_TIMER) delete_timer(pd->recovery->timer, pet_recovery_timer); + aFree(pd->recovery); pd->recovery = NULL; } + if( pd->bonus ) { if (pd->bonus->timer != INVALID_TIMER) delete_timer(pd->bonus->timer, pet_skill_bonus_timer); + aFree(pd->bonus); pd->bonus = NULL; } + if( pd->loot ) { pet_lootitem_drop(pd,sd); + if (pd->loot->item) aFree(pd->loot->item); + aFree (pd->loot); pd->loot = NULL; } + if( pd->pet.intimate > 0 ) intif_save_petdata(pd->pet.account_id,&pd->pet); else { // Remove pet. intif_delete_petdata(pd->pet.pet_id); - if (sd) sd->status.pet_id = 0; + + if (sd) + sd->status.pet_id = 0; } + if( sd ) sd->pd = NULL; break; } - case BL_MOB: - { + case BL_MOB: { struct mob_data *md = (struct mob_data*)bl; + if( md->spawn_timer != INVALID_TIMER ) { delete_timer(md->spawn_timer,mob_delayspawn); md->spawn_timer = INVALID_TIMER; } + if( md->deletetimer != INVALID_TIMER ) { delete_timer(md->deletetimer,mob_timer_delete); md->deletetimer = INVALID_TIMER; } + if( md->lootitem ) { aFree(md->lootitem); - md->lootitem=NULL; + md->lootitem = NULL; } + if( md->guardian_data ) { struct guild_castle* gc = md->guardian_data->castle; + if( md->guardian_data->number >= 0 && md->guardian_data->number < MAX_GUARDIANS ) gc->guardian[md->guardian_data->number].id = 0; else { int i; + ARR_FIND(0, gc->temp_guardians_max, i, gc->temp_guardians[i] == md->bl.id); if( i < gc->temp_guardians_max ) gc->temp_guardians[i] = 0; } + aFree(md->guardian_data); md->guardian_data = NULL; } + if( md->spawn ) { md->spawn->active--; + if( !md->spawn->state.dynamic ) { // Permanently remove the mob if( --md->spawn->num == 0 ) { // Last freed mob is responsible for deallocating the group's spawn data. aFree(md->spawn); @@ -2804,12 +3080,15 @@ int unit_free(struct block_list *bl, clr_type clrtype) } } } + if( md->base_status) { aFree(md->base_status); md->base_status = NULL; } + if( mob_is_clone(md->mob_id) ) mob_clone_delete(md); + if( md->tomb_nid ) mvptomb_destroy(md); break; @@ -2818,29 +3097,35 @@ int unit_free(struct block_list *bl, clr_type clrtype) { struct homun_data *hd = (TBL_HOM*)bl; struct map_session_data *sd = hd->master; + hom_hungry_timer_delete(hd); + if( hd->homunculus.intimacy > 0 ) hom_save(hd); else { intif_homunculus_requestdelete(hd->homunculus.hom_id); + if( sd ) sd->status.hom_id = 0; } + if( sd ) sd->hd = NULL; break; } - case BL_MER: - { + case BL_MER: { struct mercenary_data *md = (TBL_MER*)bl; struct map_session_data *sd = md->master; + if( mercenary_get_lifetime(md) > 0 ) mercenary_save(md); else { intif_mercenary_delete(md->mercenary.mercenary_id); + if( sd ) sd->status.mer_id = 0; } + if( sd ) sd->md = NULL; @@ -2850,13 +3135,16 @@ int unit_free(struct block_list *bl, clr_type clrtype) case BL_ELEM: { struct elemental_data *ed = (TBL_ELEM*)bl; struct map_session_data *sd = ed->master; + 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; @@ -2868,9 +3156,12 @@ int unit_free(struct block_list *bl, clr_type clrtype) skill_clear_unitgroup(bl); status_change_clear(bl,1); map_deliddb(bl); + if( bl->type != BL_PC ) // Players are handled by map_quit map_freeblock(bl); + map_freeblock_unlock(); + return 0; } diff --git a/src/map/vending.c b/src/map/vending.c index a393011309..520311b685 100755 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -293,7 +293,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui * Player setup a new shop * @param sd : player opening the shop * @param message : shop title - * @param data : itemlist data \n + * @param data : itemlist data * data := {.w .w .l}[count] * @param count : number of different items * @return 0 If success, 1 - Cannot open (die, not state.prevend, trading), 2 - No cart, 3 - Count issue, 4 - Cart data isn't saved yet, 5 - No valid item found