diff options
Diffstat (limited to 'src/map')
-rw-r--r-- | src/map/Makefile.in | 4 | ||||
-rw-r--r-- | src/map/atcommand.c | 23 | ||||
-rw-r--r-- | src/map/battle.c | 5 | ||||
-rw-r--r-- | src/map/chrif.c | 6 | ||||
-rw-r--r-- | src/map/clif.c | 134 | ||||
-rw-r--r-- | src/map/clif.h | 13 | ||||
-rw-r--r-- | src/map/guild.c | 2 | ||||
-rw-r--r-- | src/map/homunculus.h | 15 | ||||
-rw-r--r-- | src/map/itemdb.c | 43 | ||||
-rw-r--r-- | src/map/itemdb.h | 4 | ||||
-rw-r--r-- | src/map/map.c | 2 | ||||
-rw-r--r-- | src/map/map.h | 4 | ||||
-rw-r--r-- | src/map/mob.c | 665 | ||||
-rw-r--r-- | src/map/mob.h | 16 | ||||
-rw-r--r-- | src/map/npc.c | 34 | ||||
-rw-r--r-- | src/map/npc.h | 22 | ||||
-rw-r--r-- | src/map/npc_chat.c | 4 | ||||
-rw-r--r-- | src/map/packets.h | 12 | ||||
-rw-r--r-- | src/map/packets_struct.h | 6 | ||||
-rw-r--r-- | src/map/pc.c | 117 | ||||
-rw-r--r-- | src/map/pc.h | 2 | ||||
-rw-r--r-- | src/map/pet.c | 2 | ||||
-rw-r--r-- | src/map/pet.h | 2 | ||||
-rw-r--r-- | src/map/script.c | 396 | ||||
-rw-r--r-- | src/map/script.h | 7 | ||||
-rw-r--r-- | src/map/skill.c | 96 | ||||
-rw-r--r-- | src/map/status.c | 112 | ||||
-rw-r--r-- | src/map/status.h | 14 | ||||
-rw-r--r-- | src/map/unit.c | 18 | ||||
-rw-r--r-- | src/map/vending.c | 12 |
30 files changed, 1087 insertions, 705 deletions
diff --git a/src/map/Makefile.in b/src/map/Makefile.in index 60d87522e..b5a3d4461 100644 --- a/src/map/Makefile.in +++ b/src/map/Makefile.in @@ -116,13 +116,13 @@ map-server: ../../map-server@EXEEXT@ ../../map-server@EXEEXT@: $(MAP_SERVER_SQL_DEPENDS) Makefile @echo " LD $(notdir $@)" @$(CC) @STATIC@ @LDFLAGS@ -o ../../map-server@EXEEXT@ $(MAP_OBJ) $(COMMON_D)/obj_sql/common_sql.a \ - $(COMMON_D)/obj_all/common.a $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) @LIBS@ @PCRE_LIBS@ @MYSQL_LIBS@ + $(COMMON_D)/obj_all/common.a $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) @LIBS@ @MYSQL_LIBS@ # map object files obj_sql/%.o: %.c $(MAP_H) $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) | obj_sql @echo " CC $<" - @$(CC) @CFLAGS@ @DEFS@ $(COMMON_INCLUDE) $(THIRDPARTY_INCLUDE) @PCRE_CFLAGS@ @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< + @$(CC) @CFLAGS@ @DEFS@ $(COMMON_INCLUDE) $(THIRDPARTY_INCLUDE) @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< # missing object files $(COMMON_D)/obj_all/common.a: diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 9d03dd057..bc539837d 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -260,24 +260,28 @@ ACMD(send) if (type >= MIN_PACKET_DB && type <= MAX_PACKET_DB) { int off = 2; + if (clif->packet(type) == NULL) { + // unknown packet - ERROR + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,905), type); // Unknown packet: 0x%x + clif->message(fd, atcmd_output); + return false; + } + if (len) { // show packet length safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,904), type, clif->packet(type)->len); // Packet 0x%x length: %d clif->message(fd, atcmd_output); return true; } - + len = clif->packet(type)->len; - if (len == 0) { - // unknown packet - ERROR - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,905), type); // Unknown packet: 0x%x - clif->message(fd, atcmd_output); - return false; - } else if (len == -1) { + + if (len == -1) { // dynamic packet len = SHRT_MAX-4; // maximum length off = 4; } + WFIFOHEAD(sd->fd, len); WFIFOW(sd->fd,0)=TOW(type); @@ -3917,7 +3921,7 @@ ACMD(mapinfo) strcat(atcmd_output, msg_fd(fd,1096)); // PartyLock | if (map->list[m_id].flag.guildlock) strcat(atcmd_output, msg_fd(fd,1097)); // GuildLock | - if (map->list[m_id].flag.noviewid) + if (map->list[m_id].flag.noviewid != EQP_NONE) strcat(atcmd_output, msg_fd(fd,1079)); // NoViewID | clif->message(fd, atcmd_output); @@ -8414,6 +8418,7 @@ ACMD(charcommands) atcommand->commands_sub(sd, fd, COMMAND_CHARCOMMAND); return true; } + /* for new mounts */ ACMD(cashmount) { @@ -8425,7 +8430,7 @@ ACMD(cashmount) clif->message(sd->fd,msg_fd(fd,1362)); // NOTICE: If you crash with mount your LUA is outdated. if (!sd->sc.data[SC_ALL_RIDING]) { clif->message(sd->fd,msg_fd(fd,1363)); // You have mounted. - sc_start(NULL,&sd->bl,SC_ALL_RIDING,100,0,-1); + sc_start(NULL,&sd->bl,SC_ALL_RIDING,100,25,-1); } else { clif->message(sd->fd,msg_fd(fd,1364)); // You have released your mount. status_change_end(&sd->bl, SC_ALL_RIDING, INVALID_TIMER); diff --git a/src/map/battle.c b/src/map/battle.c index 3b3ea2047..44adef051 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -4118,7 +4118,8 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list * #else short totaldef = tstatus->def2 + (short)status->get_def(target); #endif - if ( sd ) wd.damage += sd->bonus.arrow_atk; + if (sd != NULL) + wd.damage += sd->bonus.arrow_atk; md.damage = (int)(3 * (1 + wd.damage) * (5 + skill_lv) / 5.0f); md.damage -= totaldef; @@ -4583,7 +4584,7 @@ struct Damage battle_calc_weapon_attack(struct block_list *src,struct block_list skill_id == NJ_KIRIKAGE)) { short cri = sstatus->cri; - if (sd) { + if (sd != NULL) { // if show_katar_crit_bonus is enabled, it already done the calculation in status.c if (!battle_config.show_katar_crit_bonus && sd->status.weapon == W_KATAR) { cri <<= 1; diff --git a/src/map/chrif.c b/src/map/chrif.c index 1f7fbe96e..258d550d4 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -1394,16 +1394,16 @@ int chrif_parse(int fd) { } while (RFIFOREST(fd) >= 2) { + cmd = RFIFOW(fd,0); + if (VECTOR_LENGTH(HPM->packets[hpChrif_Parse]) > 0) { - int result = HPM->parse_packets(fd,hpChrif_Parse); + int result = HPM->parse_packets(fd,cmd,hpChrif_Parse); if (result == 1) continue; if (result == 2) return 0; } - cmd = RFIFOW(fd,0); - if (cmd < 0x2af8 || cmd >= 0x2af8 + ARRAYLENGTH(chrif->packet_len_table) || chrif->packet_len_table[cmd-0x2af8] == 0) { int result = intif->parse(fd); // Passed on to the intif diff --git a/src/map/clif.c b/src/map/clif.c index 827f57c45..3b2f255ef 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1010,7 +1010,7 @@ void clif_set_unit_idle(struct block_list* bl, struct map_session_data *tsd, enu #endif #if PACKETVER >= 20131223 p.AID = bl->id; - p.GID = (sd) ? sd->status.char_id : 0; // CCODE + p.GID = (sd) ? sd->status.char_id : 0; // CCODE #else p.GID = bl->id; #endif @@ -1151,7 +1151,7 @@ void clif_spawn_unit(struct block_list* bl, enum send_target target) { #endif #if PACKETVER >= 20131223 p.AID = bl->id; - p.GID = (sd) ? sd->status.char_id : 0; // CCODE + p.GID = (sd) ? sd->status.char_id : 0; // CCODE #else p.GID = bl->id; #endif @@ -1246,7 +1246,7 @@ void clif_set_unit_walking(struct block_list* bl, struct map_session_data *tsd, #endif #if PACKETVER >= 20131223 p.AID = bl->id; - p.GID = (tsd) ? tsd->status.char_id : 0; // CCODE + p.GID = (tsd) ? tsd->status.char_id : 0; // CCODE #else p.GID = bl->id; #endif @@ -2333,9 +2333,9 @@ void clif_add_random_options(unsigned char* buf, struct item* item) int i; nullpo_retv(buf); for (i = 0; i < 5; i++){ - WBUFW(buf,i*5+0) = 0; // OptIndex - WBUFW(buf,i*5+2) = 0; // Value - WBUFB(buf,i*5+4) = 0; // Param1 + WBUFW(buf,i*5+0) = 0; // OptIndex + WBUFW(buf,i*5+2) = 0; // Value + WBUFB(buf,i*5+4) = 0; // Param1 } } @@ -4330,7 +4330,7 @@ int clif_damage(struct block_list* src, struct block_list* dst, int sdelay, int p.leftDamage = damage2; } #if PACKETVER >= 20131223 - p.is_sp_damaged = 0; // [ToDo] IsSPDamage - Displays blue digits. + p.is_sp_damaged = 0; // TODO: IsSPDamage - Displays blue digits. #endif if(disguised(dst)) { @@ -5078,7 +5078,7 @@ int clif_skill_damage(struct block_list *src, struct block_list *dst, int64 tick // type 6 (ACTION_SKILL) skills. So we have to do a small // hack to set all type 6 to be sent as type 8 ACTION_ATTACK_MULTIPLE #if PACKETVER < 20131223 - WBUFB(buf, 32) = type; + WBUFB(buf, 32) = type; #else WBUFB(buf, 32) = (type == BDT_SKILL) ? BDT_MULTIHIT : type; #endif @@ -9859,10 +9859,8 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd) // Chat logging type 'O' / Global Chat logs->chat(LOG_CHAT_GLOBAL, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, message); -#ifdef PCRE_SUPPORT // trigger listening npcs map->foreachinrange(npc_chat->sub, &sd->bl, AREA_SIZE, BL_NPC, text, textlen, &sd->bl); -#endif } void clif_parse_MapMove(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -10398,7 +10396,7 @@ void clif_parse_EquipItem(int fd,struct map_session_data *sd) __attribute__((non /// 00a9 <index>.W <position>.W /// 0998 <index>.W <position>.L void clif_parse_EquipItem(int fd,struct map_session_data *sd) { - struct packet_equip_item *p = P2PTR(fd); + struct packet_equip_item *p = RP2PTR(fd); if(pc_isdead(sd)) { clif->clearunit_area(&sd->bl,CLR_DEAD); @@ -10874,6 +10872,25 @@ void clif_parse_ChangeCart(int fd,struct map_session_data *sd) pc->setcart(sd,type); } +/// Request to select cart's visual look for new cart design (CZ_SELECTCART). +/// 0980 <identity>.L <type>.B +void clif_parse_SelectCart(int fd, struct map_session_data *sd) +{ +#if PACKETVER >= 20150805 // RagexeRE + int type; + + if (!sd || !pc->checkskill(sd, MC_CARTDECORATE) || RFIFOL(fd, 2) != sd->status.account_id) + return; + + type = (int)RFIFOB(fd, 6); + + if (type <= MAX_BASE_CARTS || type > MAX_CARTS) + return; + + pc->setcart(sd, type); +#endif +} + void clif_parse_StatusUp(int fd,struct map_session_data *sd) __attribute__((nonnull (2))); /// Request to increase status (CZ_STATUS_CHANGE). /// 00bb <status id>.W <amount>.B @@ -11465,6 +11482,22 @@ void clif_parse_ItemIdentify(int fd,struct map_session_data *sd) clif_menuskill_clear(sd); } +/// Identifying item with right-click (CZ_REQ_ONECLICK_ITEMIDENTIFY). +/// 0A35 <index>.W +void clif_parse_OneClick_ItemIdentify(int fd, struct map_session_data *sd) +{ + int cmd = RFIFOW(fd,0); + short idx = RFIFOW(fd, packet_db[cmd].pos[0]) - 2; + int n; + + if (idx < 0 || idx >= MAX_INVENTORY || sd->inventory_data[idx] == NULL || sd->status.inventory[idx].nameid <= 0) + return; + + if ((n = pc->have_magnifier(sd) ) != INDEX_NOT_FOUND && + pc->delitem(sd, n, 1, 0, DELITEM_NORMAL, LOG_TYPE_OTHER) == 0) + skill->identify(sd, idx); +} + void clif_parse_SelectArrow(int fd,struct map_session_data *sd) __attribute__((nonnull (2))); /// Answer to arrow crafting item selection dialog (CZ_REQ_MAKINGARROW). /// 01ae <name id>.W @@ -13344,7 +13377,7 @@ void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd) __attribute /// 013f <item/mob name>.24B /// 09ce <item/mob name>.100B [Ind/Yommy<3] void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd) { - struct packet_gm_monster_item *p = P2PTR(fd); + struct packet_gm_monster_item *p = RP2PTR(fd); int i, count; char *item_monster_name; struct item_data *item_array[10]; @@ -17547,18 +17580,18 @@ void clif_maptypeproperty2(struct block_list *bl,enum send_target t) { p.PacketType = maptypeproperty2Type; p.type = 0x28; - p.flag.party = map->list[bl->m].flag.pvp ? 1 : 0; //PARTY - p.flag.guild = (map->list[bl->m].flag.battleground || map_flag_gvg(bl->m)) ? 1 : 0; // GUILD - p.flag.siege = (map->list[bl->m].flag.battleground || map_flag_gvg2(bl->m)) ? 1: 0; // SIEGE - p.flag.mineffect = map_flag_gvg(bl->m) ? 1 : ( (sd && sd->state.lesseffect) ? 1 : 0); // USE_SIMPLE_EFFECT - Forcing /mineffect in castles during WoE (probably redundant? I'm not sure) + p.flag.party = map->list[bl->m].flag.pvp ? 1 : 0; //PARTY + p.flag.guild = (map->list[bl->m].flag.battleground || map_flag_gvg(bl->m)) ? 1 : 0; // GUILD + p.flag.siege = (map->list[bl->m].flag.battleground || map_flag_gvg2(bl->m)) ? 1: 0; // SIEGE + p.flag.mineffect = map_flag_gvg(bl->m) ? 1 : ( (sd && sd->state.lesseffect) ? 1 : 0); // USE_SIMPLE_EFFECT - Forcing /mineffect in castles during WoE (probably redundant? I'm not sure) p.flag.nolockon = 0; // DISABLE_LOCKON - TODO - p.flag.countpk = map->list[bl->m].flag.pvp ? 1 : 0; // COUNT_PK - p.flag.nopartyformation = map->list[bl->m].flag.partylock ? 1 : 0; // NO_PARTY_FORMATION - p.flag.bg = map->list[bl->m].flag.battleground ? 1 : 0; // BATTLEFIELD + p.flag.countpk = map->list[bl->m].flag.pvp ? 1 : 0; // COUNT_PK + p.flag.nopartyformation = map->list[bl->m].flag.partylock ? 1 : 0; // NO_PARTY_FORMATION + p.flag.bg = map->list[bl->m].flag.battleground ? 1 : 0; // BATTLEFIELD p.flag.nocostume = (map->list[bl->m].flag.noviewid & EQP_COSTUME) ? 1 : 0; // DISABLE_COSTUMEITEM - Disables Costume Sprite p.flag.usecart = 1; // USECART - TODO p.flag.summonstarmiracle = 0; // SUNMOONSTAR_MIRACLE - TODO - p.flag.SpareBits = 0; // UNUSED + p.flag.SpareBits = 0; // UNUSED clif->send(&p,sizeof(p),bl,t); #endif @@ -17659,7 +17692,7 @@ void clif_bgqueue_notice_delete(struct map_session_data *sd, enum BATTLEGROUNDS_ void clif_parse_bgqueue_register(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); void clif_parse_bgqueue_register(int fd, struct map_session_data *sd) { - struct packet_bgqueue_register *p = P2PTR(fd); + struct packet_bgqueue_register *p = RP2PTR(fd); struct bg_arena *arena = NULL; if( !bg->queue_on ) return; /* temp, until feature is complete */ @@ -17697,7 +17730,7 @@ void clif_bgqueue_update_info(struct map_session_data *sd, unsigned char arena_i void clif_parse_bgqueue_checkstate(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); void clif_parse_bgqueue_checkstate(int fd, struct map_session_data *sd) { - struct packet_bgqueue_checkstate *p = P2PTR(fd); + struct packet_bgqueue_checkstate *p = RP2PTR(fd); nullpo_retv(sd); if ( sd->bg_queue.arena && sd->bg_queue.type ) { @@ -17708,7 +17741,7 @@ void clif_parse_bgqueue_checkstate(int fd, struct map_session_data *sd) { void clif_parse_bgqueue_revoke_req(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); void clif_parse_bgqueue_revoke_req(int fd, struct map_session_data *sd) { - struct packet_bgqueue_revoke_req *p = P2PTR(fd); + struct packet_bgqueue_revoke_req *p = RP2PTR(fd); if( sd->bg_queue.arena ) bg->queue_pc_cleanup(sd); @@ -17718,7 +17751,7 @@ void clif_parse_bgqueue_revoke_req(int fd, struct map_session_data *sd) { void clif_parse_bgqueue_battlebegin_ack(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); void clif_parse_bgqueue_battlebegin_ack(int fd, struct map_session_data *sd) { - struct packet_bgqueue_battlebegin_ack *p = P2PTR(fd); + struct packet_bgqueue_battlebegin_ack *p = RP2PTR(fd); struct bg_arena *arena; if( !bg->queue_on ) return; /* temp, until feature is complete */ @@ -17857,7 +17890,7 @@ void clif_cart_additem_ack(struct map_session_data *sd, int flag) { void clif_parse_BankDeposit(int fd, struct map_session_data* sd) __attribute__((nonnull (2))); /* Bank System [Yommy/Hercules] */ void clif_parse_BankDeposit(int fd, struct map_session_data* sd) { - struct packet_banking_deposit_req *p = P2PTR(fd); + struct packet_banking_deposit_req *p = RP2PTR(fd); int money; if (!battle_config.feature_banking) { @@ -17872,7 +17905,7 @@ void clif_parse_BankDeposit(int fd, struct map_session_data* sd) { void clif_parse_BankWithdraw(int fd, struct map_session_data* sd) __attribute__((nonnull (2))); void clif_parse_BankWithdraw(int fd, struct map_session_data* sd) { - struct packet_banking_withdraw_req *p = P2PTR(fd); + struct packet_banking_withdraw_req *p = RP2PTR(fd); int money; if (!battle_config.feature_banking) { @@ -18149,7 +18182,7 @@ void clif_npc_market_purchase_ack(struct map_session_data *sd, struct packet_npc void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd) { #if PACKETVER >= 20131223 - struct packet_npc_market_purchase *p = P2PTR(fd); + struct packet_npc_market_purchase *p = RP2PTR(fd); clif->npc_market_purchase_ack(sd,p,npc->market_buylist(sd,(p->PacketLength - 4) / sizeof(p->list[0]),p)); #endif @@ -18607,6 +18640,28 @@ void clif_dressroom_open(struct map_session_data *sd, int view) WFIFOSET(fd,packet_len(0xa02)); } +/// Request to select cart's visual look for new cart design (ZC_SELECTCART). +/// 097f <Length>.W <identity>.L <type>.B +void clif_selectcart(struct map_session_data *sd) +{ +#if PACKETVER >= 20150805 + int i = 0, fd; + + fd = sd->fd; + + WFIFOHEAD(fd, 8 + MAX_CARTDECORATION_CARTS); + WFIFOW(fd, 0) = 0x97f; + WFIFOW(fd, 2) = 8 + MAX_CARTDECORATION_CARTS; + WFIFOL(fd, 4) = sd->status.account_id; + + for (i = 0; i < MAX_CARTDECORATION_CARTS; i++) { + WFIFOB(fd, 8 + i) = MAX_BASE_CARTS + 1 + i; + } + + WFIFOSET(fd, 8 + MAX_CARTDECORATION_CARTS); +#endif +} + /* */ unsigned short clif_decrypt_cmd( int cmd, struct map_session_data *sd ) { if( sd ) { @@ -18688,21 +18743,21 @@ int clif_parse(int fd) { if (RFIFOREST(fd) < 2) return 0; + if (sd) + parse_cmd_func = sd->parse_cmd_func; + else + parse_cmd_func = clif->parse_cmd; + + cmd = parse_cmd_func(fd,sd); + if (VECTOR_LENGTH(HPM->packets[hpClif_Parse]) > 0) { - int result = HPM->parse_packets(fd,hpClif_Parse); + int result = HPM->parse_packets(fd,cmd,hpClif_Parse); if (result == 1) continue; if (result == 2) return 0; } - if( sd ) - parse_cmd_func = sd->parse_cmd_func; - else - parse_cmd_func = clif->parse_cmd; - - cmd = parse_cmd_func(fd,sd); - // filter out invalid / unsupported packets if (cmd > MAX_PACKET_DB || cmd < MIN_PACKET_DB || packet_db[cmd].len == 0) { ShowWarning("clif_parse: Received unsupported packet (packet 0x%04x (0x%04x), %"PRIuS" bytes received), disconnecting session #%d.\n", @@ -18814,6 +18869,11 @@ static void __attribute__ ((unused)) packetdb_addpacket(short cmd, int len, ...) return; } + if (cmd < MIN_PACKET_DB) { + ShowError("Packet Error: packet 0x%x is lower than the minimum allowed (0x%x), skipping...\n", cmd, MIN_PACKET_DB); + return; + } + packet_db[cmd].len = len; va_start(va,len); @@ -19419,6 +19479,8 @@ void clif_defaults(void) { clif->cancelmergeitem = clif_cancelmergeitem; clif->comparemergeitem = clif_comparemergeitem; clif->ackmergeitems = clif_ackmergeitems; + /* Cart Deco */ + clif->selectcart = clif_selectcart; /*------------------------ *- Parse Incoming Packet @@ -19467,6 +19529,7 @@ void clif_defaults(void) { clif->pGetItemFromCart = clif_parse_GetItemFromCart; clif->pRemoveOption = clif_parse_RemoveOption; clif->pChangeCart = clif_parse_ChangeCart; + clif->pSelectCart = clif_parse_SelectCart; clif->pStatusUp = clif_parse_StatusUp; clif->pSkillUp = clif_parse_SkillUp; clif->pUseSkillToId = clif_parse_UseSkillToId; @@ -19663,4 +19726,5 @@ void clif_defaults(void) { clif->add_random_options = clif_add_random_options; clif->pHotkeyRowShift = clif_parse_HotkeyRowShift; clif->dressroom_open = clif_dressroom_open; + clif->pOneClick_ItemIdentify = clif_parse_OneClick_ItemIdentify; } diff --git a/src/map/clif.h b/src/map/clif.h index 5a6b01d31..d68a09393 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -58,12 +58,11 @@ struct view_data; * Defines **/ #define packet_len(cmd) packet_db[cmd].len -#define P2PTR(fd) RFIFO2PTR(fd) #define clif_menuskill_clear(sd) ((sd)->menuskill_id = (sd)->menuskill_val = (sd)->menuskill_val2 = 0) #define clif_disp_onlyself(sd,mes,len) clif->disp_message( &(sd)->bl, (mes), (len), SELF ) #define MAX_ROULETTE_LEVEL 7 /** client-defined value **/ #define MAX_ROULETTE_COLUMNS 9 /** client-defined value **/ -#define RGB2BGR(c) ((c & 0x0000FF) << 16 | (c & 0x00FF00) | (c & 0xFF0000) >> 16) +#define RGB2BGR(c) (((c) & 0x0000FF) << 16 | ((c) & 0x00FF00) | ((c) & 0xFF0000) >> 16) #define COLOR_RED 0xff0000U #define COLOR_GREEN 0x00ff00U @@ -73,12 +72,6 @@ struct view_data; /** * Enumerations **/ -enum {// packet DB - MIN_PACKET_DB = 0x0064, - MAX_PACKET_DB = 0x0F00, - MAX_PACKET_POS = 20, -}; - typedef enum send_target { ALL_CLIENT, ALL_SAMEMAP, @@ -1338,6 +1331,10 @@ struct clif_interface { void (*add_random_options) (unsigned char* buf, struct item* item); void (*pHotkeyRowShift) (int fd, struct map_session_data *sd); void (*dressroom_open) (struct map_session_data *sd, int view); + void (*pOneClick_ItemIdentify) (int fd,struct map_session_data *sd); + /* Cart Deco */ + void(*selectcart) (struct map_session_data *sd); + void(*pSelectCart) (int fd, struct map_session_data *sd); }; #ifdef HERCULES_CORE diff --git a/src/map/guild.c b/src/map/guild.c index cba05638f..f4f0c0528 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -1117,7 +1117,7 @@ int guild_change_position(int guild_id,int idx,int mode,int exp_mode,const char nullpo_ret(name); exp_mode = cap_value(exp_mode, 0, battle_config.guild_exp_limit); - p.mode=mode&GPERM_BOTH; // Invite and Expel + p.mode=mode&GPERM_MASK; p.exp_mode=exp_mode; safestrncpy(p.name,name,NAME_LENGTH); return intif->guild_position(guild_id,idx,&p); diff --git a/src/map/homunculus.h b/src/map/homunculus.h index 1712c98a9..c2ce042ec 100644 --- a/src/map/homunculus.h +++ b/src/map/homunculus.h @@ -63,14 +63,15 @@ enum homun_id { #define homun_alive(x) ((x) && (x)->homunculus.vaporize == HOM_ST_ACTIVE && (x)->battle_status.hp > 0) #ifdef RENEWAL -#define HOMUN_LEVEL_STATWEIGHT_VALUE 0 -#define APPLY_HOMUN_LEVEL_STATWEIGHT()( \ - hom->str_value = hom->agi_value = \ - hom->vit_value = hom->int_value = \ - hom->dex_value = hom->luk_value = hom->level / 10 - HOMUN_LEVEL_STATWEIGHT_VALUE \ - ) +#define HOMUN_LEVEL_STATWEIGHT_VALUE 0 +#define APPLY_HOMUN_LEVEL_STATWEIGHT() \ + do { \ + hom->str_value = hom->agi_value = \ + hom->vit_value = hom->int_value = \ + hom->dex_value = hom->luk_value = hom->level / 10 - HOMUN_LEVEL_STATWEIGHT_VALUE; \ + } while (false) #else -#define APPLY_HOMUN_LEVEL_STATWEIGHT() +#define APPLY_HOMUN_LEVEL_STATWEIGHT() (void)0 #endif struct h_stats { diff --git a/src/map/itemdb.c b/src/map/itemdb.c index 048efd636..bd552dd16 100644 --- a/src/map/itemdb.c +++ b/src/map/itemdb.c @@ -1188,7 +1188,7 @@ void itemdb_read_chains(void) { int c = 0; config_setting_t *entry = NULL; - script->set_constant2(name,i-1,0); + script->set_constant2(name, i-1, false, false); itemdb->chains[count].qty = (unsigned short)libconfig->setting_length(itc); CREATE(itemdb->chains[count].items, struct item_chain_entry, libconfig->setting_length(itc)); @@ -1254,7 +1254,8 @@ int itemdb_combo_split_atoi (char *str, int *val) { /** * <combo{:combo{:combo:{..}}}>,<{ script }> **/ -void itemdb_read_combos() { +void itemdb_read_combos(void) +{ uint32 lines = 0, count = 0; char line[1024]; char filepath[256]; @@ -1384,17 +1385,17 @@ int itemdb_gendercheck(struct item_data *id) * This function is called after preparing the item entry data, and it takes * care of inserting it and cleaning up any remainders of the previous one. * - * @param *entry Pointer to the new item_data entry. Ownership is NOT taken, - * but the content is modified to reflect the validation. - * @param n Ordinal number of the entry, to be displayed in case of - * validation errors. - * @param *source Source of the entry (table or file name), to be displayed in - * case of validation errors. + * @param entry Pointer to the new item_data entry. Ownership is NOT taken, + * but the content is modified to reflect the validation. + * @param n Ordinal number of the entry, to be displayed in case of + * validation errors. + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. * @return Nameid of the validated entry, or 0 in case of failure. * - * Note: This is safe to call if the new entry is a copy of the old one (i.e. - * item_db2 inheritance), as it will make sure not to free any scripts still in - * use in the new entry. + * Note: This is safe to call if the new entry is a shallow copy of the old one + * (i.e. item_db2 inheritance), as it will make sure not to free any scripts + * still in use by the new entry. */ int itemdb_validate_entry(struct item_data *entry, int n, const char *source) { struct item_data *item; @@ -1543,13 +1544,13 @@ void itemdb_readdb_additional_fields(int itemid, config_setting_t *it, int n, co * Processes one itemdb entry from the libconfig backend, loading and inserting * it into the item database. * - * @param *it Libconfig setting entry. It is expected to be valid and it - * won't be freed (it is care of the caller to do so if - * necessary) - * @param n Ordinal number of the entry, to be displayed in case of - * validation errors. - * @param *source Source of the entry (file name), to be displayed in case of - * validation errors. + * @param it Libconfig setting entry. It is expected to be valid and it + * won't be freed (it is care of the caller to do so if + * necessary) + * @param n Ordinal number of the entry, to be displayed in case of + * validation errors. + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. * @return Nameid of the validated entry, or 0 in case of failure. */ int itemdb_readdb_libconfig_sub(config_setting_t *it, int n, const char *source) { @@ -1626,7 +1627,7 @@ int itemdb_readdb_libconfig_sub(config_setting_t *it, int n, const char *source) } else { // Use old entry as default struct item_data *old_entry = itemdb->load(id.nameid); - memcpy(&id, old_entry, sizeof(struct item_data)); + memcpy(&id, old_entry, sizeof(id)); } } @@ -1873,7 +1874,7 @@ bool itemdb_lookup_const(const config_setting_t *it, const char *name, int *valu * Reads from a libconfig-formatted itemdb file and inserts the found entries into the * item database, overwriting duplicate ones (i.e. item_db2 overriding item_db.) * - * @param *filename File name, relative to the database path. + * @param filename File name, relative to the database path. * @return The number of found entries. */ int itemdb_readdb_libconfig(const char *filename) { @@ -2148,7 +2149,7 @@ void itemdb_name_constants(void) { script->parser_current_file = "Item Database (Likely an invalid or conflicting AegisName)"; #endif // ENABLE_CASE_CHECK for( data = dbi_first(iter); dbi_exists(iter); data = dbi_next(iter) ) - script->set_constant2(data->name,data->nameid,0); + script->set_constant2(data->name, data->nameid, false, false); #ifdef ENABLE_CASE_CHECK script->parser_current_file = NULL; #endif // ENABLE_CASE_CHECK diff --git a/src/map/itemdb.h b/src/map/itemdb.h index d751451c6..8a0ec389d 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -64,6 +64,7 @@ enum item_itemid { ITEMID_BRANCH_OF_DEAD_TREE = 604, ITEMID_ANODYNE = 605, ITEMID_ALOEBERA = 606, + ITEMID_MAGNIFIER = 611, ITEMID_POISON_BOTTLE = 678, ITEMID_EMPTY_BOTTLE = 713, ITEMID_EMPERIUM = 714, @@ -136,6 +137,7 @@ enum item_itemid { ITEMID_MAGIC_CASTLE = 12308, ITEMID_BULGING_HEAD = 12309, ITEMID_THICK_MANUAL50 = 12312, + ITEMID_NOVICE_MAGNIFIER = 12325, ITEMID_ANCILLA = 12333, ITEMID_REPAIR_A = 12392, ITEMID_REPAIR_B = 12393, @@ -626,7 +628,7 @@ struct itemdb_interface { int (*isidentified) (int nameid); int (*isidentified2) (struct item_data *data); int (*combo_split_atoi) (char *str, int *val); - void (*read_combos) (); + void (*read_combos) (void); int (*gendercheck) (struct item_data *id); int (*validate_entry) (struct item_data *entry, int n, const char *source); void (*readdb_additional_fields) (int itemid, config_setting_t *it, int n, const char *source); diff --git a/src/map/map.c b/src/map/map.c index 1b922148b..3dad25fce 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -5774,9 +5774,7 @@ void map_load_defaults(void) { pet_defaults(); path_defaults(); quest_defaults(); -#ifdef PCRE_SUPPORT npc_chat_defaults(); -#endif } /** * --run-once handler diff --git a/src/map/map.h b/src/map/map.h index 4c74d352c..ff7ca2d38 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -716,7 +716,7 @@ struct map_data { unsigned noknockback : 1; unsigned notomb : 1; unsigned nocashshop : 1; - unsigned noviewid : 22; + uint32 noviewid; ///< noviewid (bitmask - @see enum equip_pos) } flag; struct point save; struct npc_data *npc[MAX_NPC_PER_MAP]; @@ -866,6 +866,7 @@ typedef struct elemental_data TBL_ELEM; * object is passed to BL_UCAST. It's declared as static inline to let the * compiler optimize out the function call overhead. */ +static inline struct block_list *BL_UCAST_(struct block_list *bl) __attribute__((unused)); static inline struct block_list *BL_UCAST_(struct block_list *bl) { return bl; @@ -894,6 +895,7 @@ static inline struct block_list *BL_UCAST_(struct block_list *bl) * object is passed to BL_UCAST. It's declared as static inline to let the * compiler optimize out the function call overhead. */ +static inline const struct block_list *BL_UCCAST_(const struct block_list *bl) __attribute__((unused)); static inline const struct block_list *BL_UCCAST_(const struct block_list *bl) { return bl; diff --git a/src/map/mob.c b/src/map/mob.c index 37da81a15..bc78c6098 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -2064,7 +2064,7 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage) { if (battle_config.show_mob_info&3) clif->charnameack (0, &md->bl); - + #if PACKETVER >= 20131223 // Resend ZC_NOTIFY_MOVEENTRY to Update the HP if (battle_config.show_monster_hp_bar) @@ -2482,15 +2482,15 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { if(mvp_sd && md->db->mexp > 0 && md->special_state.ai == AI_NONE) { int log_mvp[2] = {0}; unsigned int mexp; - double exp; + int64 exp; //mapflag: noexp check [Lorky] - if (map->list[m].flag.nobaseexp || type&2) - exp =1; - else { + if (map->list[m].flag.nobaseexp || type&2) { + exp = 1; + } else { exp = md->db->mexp; if (count > 1) - exp += exp*(battle_config.exp_bonus_attacker*(count-1))/100.; //[Gengar] + exp += apply_percentrate64(exp, battle_config.exp_bonus_attacker * (count-1), 100); //[Gengar] } mexp = (unsigned int)cap_value(exp, 1, UINT_MAX); @@ -3632,7 +3632,7 @@ int mob_makedummymobdb(int class_) //Adjusts the drop rate of item according to the criteria given. [Skotlex] unsigned int mob_drop_adjust(int baserate, int rate_adjust, unsigned short rate_min, unsigned short rate_max) { - double rate = baserate; + int64 rate = baserate; if (battle_config.logarithmic_drops && rate_adjust > 0 && rate_adjust != 100 && baserate > 0) //Logarithmic drops equation by Ishizu-Chan //Equation: Droprate(x,y) = x * (5 - log(x)) ^ (ln(y) / ln(5)) @@ -3640,7 +3640,7 @@ unsigned int mob_drop_adjust(int baserate, int rate_adjust, unsigned short rate_ rate = rate * pow((5.0 - log10(rate)), (log(rate_adjust/100.) / log(5.0))) + 0.5; else //Classical linear rate adjustment. - rate = rate * rate_adjust/100; + rate = apply_percentrate64(rate, rate_adjust, 100); return (unsigned int)cap_value(rate,rate_min,rate_max); } @@ -3678,30 +3678,45 @@ static inline int mob_parse_dbrow_cap_value(int class_, int min, int max, int va return value; } -void mob_read_db_stats_sub(struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t) +/** + * Processes the stats for a mob database entry. + * + * @param[in,out] entry The destination mob_db entry, already initialized + * (mob_id is expected to be already set). + * @param[in] t The libconfig entry. + */ +void mob_read_db_stats_sub(struct mob_db *entry, config_setting_t *t) { int i32; if (mob->lookup_const(t, "Str", &i32) && i32 >= 0) { - mstatus->str = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32); + entry->status.str = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32); } if (mob->lookup_const(t, "Agi", &i32) && i32 >= 0) { - mstatus->agi = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32); + entry->status.agi = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32); } if (mob->lookup_const(t, "Vit", &i32) && i32 >= 0) { - mstatus->vit = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32); + entry->status.vit = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32); } if (mob->lookup_const(t, "Int", &i32) && i32 >= 0) { - mstatus->int_ = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32); + entry->status.int_ = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32); } if (mob->lookup_const(t, "Dex", &i32) && i32 >= 0) { - mstatus->dex = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32); + entry->status.dex = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32); } if (mob->lookup_const(t, "Luk", &i32) && i32 >= 0) { - mstatus->luk = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32); + entry->status.luk = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32); } } -int mob_read_db_mode_sub(struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t) +/** + * Processes the mode for a mob_db entry. + * + * @param[in] entry The destination mob_db entry, already initialized. + * @param[in] t The libconfig entry. + * + * @return The parsed mode. + */ +int mob_read_db_mode_sub(struct mob_db *entry, config_setting_t *t) { int mode = 0; config_setting_t *t2; @@ -3740,7 +3755,14 @@ int mob_read_db_mode_sub(struct mob_db *entry, struct status_data *mstatus, int return mode; } -void mob_read_db_mvpdrops_sub(struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t) +/** + * Processes the MVP drops for a mob_db entry. + * + * @param[in,out] entry The destination mob_db entry, already initialized + * (mob_id is expected to be already set). + * @param[in] t The libconfig entry. + */ +void mob_read_db_mvpdrops_sub(struct mob_db *entry, config_setting_t *t) { config_setting_t *drop; int i = 0; @@ -3752,28 +3774,26 @@ void mob_read_db_mvpdrops_sub(struct mob_db *entry, struct status_data *mstatus, int rate_adjust = battle_config.item_rate_mvp; struct item_data* id = itemdb->search_name(name); int value = 0; - if (!id) - { - ShowWarning("mob_read_db: mvp drop item %s not found in monster %d\n", name, class_); - i ++; + if (!id) { + ShowWarning("mob_read_db: mvp drop item %s not found in monster %d\n", name, entry->mob_id); + i++; continue; } if (mob->get_const(drop, &i32) && i32 >= 0) { value = i32; } - if (value <= 0) - { - ShowWarning("mob_read_db: wrong drop chance %d for mvp drop item %s in monster %d\n", value, name, class_); - i ++; + if (value <= 0) { + ShowWarning("mob_read_db: wrong drop chance %d for mvp drop item %s in monster %d\n", value, name, entry->mob_id); + i++; continue; } entry->mvpitem[idx].nameid = id->nameid; if (!entry->mvpitem[idx].nameid) { entry->mvpitem[idx].p = 0; //No item.... - i ++; + i++; continue; } - mob->item_dropratio_adjust(entry->mvpitem[idx].nameid, class_, &rate_adjust); + mob->item_dropratio_adjust(entry->mvpitem[idx].nameid, entry->mob_id, &rate_adjust); entry->mvpitem[idx].p = mob->drop_adjust(value, rate_adjust, battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max); //calculate and store Max available drop chance of the MVP item @@ -3787,11 +3807,18 @@ void mob_read_db_mvpdrops_sub(struct mob_db *entry, struct status_data *mstatus, idx++; } if (idx == MAX_MVP_DROP && libconfig->setting_get_elem(t, i)) { - ShowWarning("mob_read_db: Too many mvp drops in mob %d\n", class_); + ShowWarning("mob_read_db: Too many mvp drops in mob %d\n", entry->mob_id); } } -void mob_read_db_drops_sub(struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t) +/** + * Processes the drops for a mob_db entry. + * + * @param[in,out] entry The destination mob_db entry, already initialized + * (mob_id, status.mode are expected to be already set). + * @param[in] t The libconfig entry. + */ +void mob_read_db_drops_sub(struct mob_db *entry, config_setting_t *t) { config_setting_t *drop; int i = 0; @@ -3805,73 +3832,72 @@ void mob_read_db_drops_sub(struct mob_db *entry, struct status_data *mstatus, in unsigned short ratemin, ratemax; struct item_data* id = itemdb->search_name(name); int value = 0; - if (!id) - { - ShowWarning("mob_read_db: drop item %s not found in monster %d\n", name, class_); - i ++; + if (!id) { + ShowWarning("mob_read_db: drop item %s not found in monster %d\n", name, entry->mob_id); + i++; continue; } if (mob->get_const(drop, &i32) && i32 >= 0) { value = i32; } - if (value <= 0) - { - ShowWarning("mob_read_db: wrong drop chance %d for drop item %s in monster %d\n", value, name, class_); - i ++; + if (value <= 0) { + ShowWarning("mob_read_db: wrong drop chance %d for drop item %s in monster %d\n", value, name, entry->mob_id); + i++; continue; } entry->dropitem[idx].nameid = id->nameid; if (!entry->dropitem[idx].nameid) { entry->dropitem[idx].p = 0; //No drop. - i ++; + i++; continue; } type = id->type; - if ((class_ >= MOBID_TREASURE_BOX1 && class_ <= MOBID_TREASURE_BOX40) || (class_ >= MOBID_TREASURE_BOX41 && class_ <= MOBID_TREASURE_BOX49)) { + if ((entry->mob_id >= MOBID_TREASURE_BOX1 && entry->mob_id <= MOBID_TREASURE_BOX40) + || (entry->mob_id >= MOBID_TREASURE_BOX41 && entry->mob_id <= MOBID_TREASURE_BOX49)) { //Treasure box drop rates [Skotlex] rate_adjust = battle_config.item_rate_treasure; ratemin = battle_config.item_drop_treasure_min; ratemax = battle_config.item_drop_treasure_max; - } - else switch (type) - { // Added support to restrict normal drops of MVP's [Reddozen] + } else { + switch (type) { // Added support to restrict normal drops of MVP's [Reddozen] case IT_HEALING: - rate_adjust = (mstatus->mode&MD_BOSS) ? battle_config.item_rate_heal_boss : battle_config.item_rate_heal; + rate_adjust = (entry->status.mode&MD_BOSS) ? battle_config.item_rate_heal_boss : battle_config.item_rate_heal; ratemin = battle_config.item_drop_heal_min; ratemax = battle_config.item_drop_heal_max; break; case IT_USABLE: case IT_CASH: - rate_adjust = (mstatus->mode&MD_BOSS) ? battle_config.item_rate_use_boss : battle_config.item_rate_use; + rate_adjust = (entry->status.mode&MD_BOSS) ? battle_config.item_rate_use_boss : battle_config.item_rate_use; ratemin = battle_config.item_drop_use_min; ratemax = battle_config.item_drop_use_max; break; case IT_WEAPON: case IT_ARMOR: case IT_PETARMOR: - rate_adjust = (mstatus->mode&MD_BOSS) ? battle_config.item_rate_equip_boss : battle_config.item_rate_equip; + rate_adjust = (entry->status.mode&MD_BOSS) ? battle_config.item_rate_equip_boss : battle_config.item_rate_equip; ratemin = battle_config.item_drop_equip_min; ratemax = battle_config.item_drop_equip_max; break; case IT_CARD: - rate_adjust = (mstatus->mode&MD_BOSS) ? battle_config.item_rate_card_boss : battle_config.item_rate_card; + rate_adjust = (entry->status.mode&MD_BOSS) ? battle_config.item_rate_card_boss : battle_config.item_rate_card; ratemin = battle_config.item_drop_card_min; ratemax = battle_config.item_drop_card_max; break; default: - rate_adjust = (mstatus->mode&MD_BOSS) ? battle_config.item_rate_common_boss : battle_config.item_rate_common; + rate_adjust = (entry->status.mode&MD_BOSS) ? battle_config.item_rate_common_boss : battle_config.item_rate_common; ratemin = battle_config.item_drop_common_min; ratemax = battle_config.item_drop_common_max; break; + } } - mob->item_dropratio_adjust(id->nameid, class_, &rate_adjust); + mob->item_dropratio_adjust(id->nameid, entry->mob_id, &rate_adjust); entry->dropitem[idx].p = mob->drop_adjust(value, rate_adjust, ratemin, ratemax); //calculate and store Max available drop chance of the item if (entry->dropitem[idx].p - && (class_ < MOBID_TREASURE_BOX1 || class_ > MOBID_TREASURE_BOX40) - && (class_ < MOBID_TREASURE_BOX41 || class_ > MOBID_TREASURE_BOX49)) { + && (entry->mob_id < MOBID_TREASURE_BOX1 || entry->mob_id > MOBID_TREASURE_BOX40) + && (entry->mob_id < MOBID_TREASURE_BOX41 || entry->mob_id > MOBID_TREASURE_BOX49)) { //Skip treasure chests. if (id->maxchance == -1 || (id->maxchance < entry->dropitem[idx].p) ) { id->maxchance = entry->dropitem[idx].p; //item has bigger drop chance or sold in shops @@ -3880,361 +3906,454 @@ void mob_read_db_drops_sub(struct mob_db *entry, struct status_data *mstatus, in if (id->mob[k].chance <= entry->dropitem[idx].p) break; } - if (k == MAX_SEARCH) - { + if (k == MAX_SEARCH) { i++; idx++; continue; } - if (id->mob[k].id != class_ && k != MAX_SEARCH - 1) + if (id->mob[k].id != entry->mob_id && k != MAX_SEARCH - 1) memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0])); id->mob[k].chance = entry->dropitem[idx].p; - id->mob[k].id = class_; + id->mob[k].id = entry->mob_id; } i++; idx++; } if (idx == MAX_MOB_DROP && libconfig->setting_get_elem(t, i)) { - ShowWarning("mob_read_db: Too many drops in mob %d\n", class_); + ShowWarning("mob_read_db: Too many drops in mob %d\n", entry->mob_id); } } -/*========================================== - * processes one mobdb entry - *------------------------------------------*/ -bool mob_read_db_sub(config_setting_t *mobt, int id, const char *source) +/** + * Validates a mob DB entry and inserts it into the database. + * This function is called after preparing the mob entry data, and it takes + * care of inserting it and cleaning up any remainders of the previous one (in + * case it is overwriting an existing entry). + * + * @param entry Pointer to the new mob_db entry. Ownership is NOT taken, but + * the content is modified to reflect the validation. + * @param n Ordinal number of the entry, to be displayed in case of + * validation errors. + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. + * @return Mob ID of the validated entry, or 0 in case of failure. + * + * Note: This is safe to call if the new entry is a shallow copy of the old one + * (i.e. mob_db2 inheritance), as it will make sure not to free any data still + * in use by the new entry. + */ +int mob_db_validate_entry(struct mob_db *entry, int n, const char *source) { - struct mob_db *entry = NULL, tmpEntry; - config_setting_t *t = NULL; - int i32 = 0, value = 0, class_ = 0; - struct status_data *mstatus; struct mob_data data; - const char *str = NULL; - double maxhp; - double exp; - bool inherit = false; - bool range2Updated = false; - bool range3Updated = false; - bool dmotionUpdated = false; - bool maxhpUpdated = false; - bool maxspUpdated = false; - entry = &tmpEntry; - if (!libconfig->setting_lookup_int(mobt, "Id", &class_)) { - ShowWarning("mob_read_db_sub: Missing id in \"%s\", entry #%d, skipping.\n", source, class_); - return false; + if (entry->mob_id <= 1000 || entry->mob_id > MAX_MOB_DB) { + ShowError("mob_db_validate_entry: Invalid monster ID %d, must be in range %d-%d.\n", entry->mob_id, 1000, MAX_MOB_DB); + return 0; + } + if (pc->db_checkid(entry->mob_id)) { + ShowError("mob_read_db_sub: Invalid monster ID %d, reserved for player classes.\n", entry->mob_id); + return 0; + } + if (entry->mob_id >= MOB_CLONE_START && entry->mob_id < MOB_CLONE_END) { + ShowError("mob_read_db_sub: Invalid monster ID %d. Range %d-%d is reserved for player clones. Please increase MAX_MOB_DB (%d).\n", + entry->mob_id, MOB_CLONE_START, MOB_CLONE_END-1, MAX_MOB_DB); + return 0; } - if (class_ <= 1000 || class_ > MAX_MOB_DB) { - ShowError("mob_read_db_sub: Invalid monster ID %d, must be in range %d-%d.\n", class_, 1000, MAX_MOB_DB); - return false; + entry->lv = cap_value(entry->lv, 1, USHRT_MAX); + + if (entry->status.max_sp < 1) + entry->status.max_sp = 1; + //Since mobs always respawn with full life... + entry->status.hp = entry->status.max_hp; + entry->status.sp = entry->status.max_sp; + + /* + * Disabled for renewal since difference of 0 and 1 still has an impact in the formulas + * Just in case there is a mishandled division by zero please let us know. [malufett] + */ +#ifndef RENEWAL + //All status should be min 1 to prevent divisions by zero from some skills. [Skotlex] + if (entry->status.str < 1) entry->status.str = 1; + if (entry->status.agi < 1) entry->status.agi = 1; + if (entry->status.vit < 1) entry->status.vit = 1; + if (entry->status.int_< 1) entry->status.int_= 1; + if (entry->status.dex < 1) entry->status.dex = 1; + if (entry->status.luk < 1) entry->status.luk = 1; +#endif + + if (entry->range2 < 1) + entry->range2 = 1; + +#if 0 // This code was (accidentally) never enabled. It'll stay commented out until it's proven to be needed. + //Tests showed that chase range is effectively 2 cells larger than expected [Playtester] + if (entry->range3 > 0) + entry->range3 += 2; +#endif // 0 + + if (entry->range3 < entry->range2) + entry->range3 = entry->range2; + + entry->status.size = cap_value(entry->status.size, 0, 2); + + entry->status.race = cap_value(entry->status.race, 0, RC_MAX - 1); + + if (entry->status.def_ele >= ELE_MAX) { + ShowWarning("mob_read_db_sub: Invalid element type %d for monster ID %d (max=%d).\n", entry->status.def_ele, entry->mob_id, ELE_MAX-1); + entry->status.def_ele = ELE_NEUTRAL; + entry->status.ele_lv = 1; } - if (pc->db_checkid(class_)) { - ShowError("mob_read_db_sub: Invalid monster ID %d, reserved for player classes.\n", class_); - return false; + if (entry->status.ele_lv < 1 || entry->status.ele_lv > 4) { + ShowWarning("mob_read_db_sub: Invalid element level %d for monster ID %d, must be in range 1-4.\n", entry->status.ele_lv, entry->mob_id); + entry->status.ele_lv = 1; } - if (class_ >= MOB_CLONE_START && class_ < MOB_CLONE_END) { - ShowError("mob_read_db_sub: Invalid monster ID %d. Range %d-%d is reserved for player clones. Please increase MAX_MOB_DB (%d).\n", class_, MOB_CLONE_START, MOB_CLONE_END-1, MAX_MOB_DB); - return false; + // If the attack animation is longer than the delay, the client crops the attack animation! + // On aegis there is no real visible effect of having a recharge-time less than amotion anyway. + if (entry->status.adelay < entry->status.amotion) + entry->status.adelay = entry->status.amotion; + + // Fill in remaining status data by using a dummy monster. + data.bl.type = BL_MOB; + data.level = entry->lv; + memcpy(&data.status, &entry->status, sizeof(struct status_data)); + status->calc_misc(&data.bl, &entry->status, entry->lv); + + // Finally insert monster's data into the database. + if (mob->db_data[entry->mob_id] == NULL) { + mob->db_data[entry->mob_id] = (struct mob_db*)aMalloc(sizeof(struct mob_db)); + } else { + //Copy over spawn data + memcpy(&entry->spawn, mob->db_data[entry->mob_id]->spawn, sizeof(entry->spawn)); } + memcpy(mob->db_data[entry->mob_id], entry, sizeof(struct mob_db)); + + return entry->mob_id; +} + +/** + * Processes one mobdb entry from the libconfig file, loading and inserting it + * into the mob database. + * + * @param mobt Libconfig setting entry. It is expected to be valid and it + * won't be freed (it is care of the caller to do so if + * necessary). + * @param n Ordinal number of the entry, to be displayed in case of + * validation errors. + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. + * @return Mob ID of the validated entry, or 0 in case of failure. + */ +int mob_read_db_sub(config_setting_t *mobt, int n, const char *source) +{ + struct mob_db md = { 0 }; + config_setting_t *t = NULL; + const char *str = NULL; + int i32 = 0; + bool inherit = false; + bool maxhpUpdated = false; + + nullpo_ret(mobt); + /* + * // Mandatory fields + * Id: ID + * SpriteName: "SPRITE_NAME" + * Name: "Mob name" + * JName: "Mob name" + * // Optional fields + * Lv: level + * Hp: health + * Sp: mana + * Exp: basic experience + * JExp: job experience + * AttackRange: attack range + * Attack: [attack1, attack2] + * Def: defence + * Mdef: magic defence + * Stats: { + * Str: strength + * Agi: agility + * Vit: vitality + * Int: intelligence + * Dex: dexterity + * Luk: luck + * } + * ViewRange: view range + * ChaseRange: chase range + * Size: size + * Race: race + * Element: (type, level) + * Mode: { + * CanMove: true/false + * Looter: true/false + * Aggressive: true/false + * Assist: true/false + * CastSensorIdle:true/false + * Boss: true/false + * Plant: true/false + * CanAttack: true/false + * Detector: true/false + * CastSensorChase: true/false + * ChangeChase: true/false + * Angry: true/false + * ChangeTargetMelee: true/false + * ChangeTargetChase: true/false + * TargetWeak: true/false + * } + * MoveSpeed: move speed + * AttackDelay: attack delay + * AttackMotion: attack motion + * DamageMotion: damage motion + * MvpExp: mvp experience + * MvpDrops: { + * AegisName: chance + * ... + * } + * Drops: { + * AegisName: chance + * ... + * } + */ + + if (!libconfig->setting_lookup_int(mobt, "Id", &i32)) { + ShowWarning("mob_read_db_sub: Missing id in \"%s\", entry #%d, skipping.\n", source, n); + return 0; + } + md.mob_id = i32; + md.vd.class_ = md.mob_id; if ((t = libconfig->setting_get_member(mobt, "Inherit")) && (inherit = libconfig->setting_get_bool(t))) { - if (!mob->db_data[class_]) { - ShowWarning("mob_read_db_sub: Trying to inherit nonexistent mob %d, default values will be used instead.\n", class_); + if (!mob->db_data[md.mob_id]) { + ShowWarning("mob_read_db_sub: Trying to inherit nonexistent mob %d, default values will be used instead.\n", md.mob_id); inherit = false; } else { // Use old entry as default - struct mob_db *old_entry = mob->db_data[class_]; - memcpy(entry, old_entry, sizeof(struct mob_db)); - inherit = true; + struct mob_db *old_entry = mob->db_data[md.mob_id]; + memcpy(&md, old_entry, sizeof(md)); } } - if (!inherit) { - memset(&tmpEntry, 0, sizeof(tmpEntry)); - } - - mstatus = &entry->status; - - entry->vd.class_ = class_; if (!libconfig->setting_lookup_string(mobt, "SpriteName", &str) || !*str ) { if (!inherit) { - ShowWarning("mob_read_db_sub: Missing SpriteName in mob %d of \"%s\", skipping.\n", class_, source); - return false; + ShowWarning("mob_read_db_sub: Missing SpriteName in mob %d of \"%s\", skipping.\n", md.mob_id, source); + return 0; } } else { - safestrncpy(entry->sprite, str, sizeof(entry->sprite)); + safestrncpy(md.sprite, str, sizeof(md.sprite)); } if (!libconfig->setting_lookup_string(mobt, "Name", &str) || !*str ) { if (!inherit) { - ShowWarning("mob_read_db_sub: Missing Name in mob %d of \"%s\", skipping.\n", class_, source); - return false; + ShowWarning("mob_read_db_sub: Missing Name in mob %d of \"%s\", skipping.\n", md.mob_id, source); + return 0; + } + } else { + safestrncpy(md.name, str, sizeof(md.name)); + } + + if (!libconfig->setting_lookup_string(mobt, "JName", &str) || !*str ) { + if (!inherit) { + safestrncpy(md.jname, md.name, sizeof(md.jname)); } } else { - safestrncpy(entry->name, str, sizeof(entry->name)); - safestrncpy(entry->jname, str, sizeof(entry->jname)); + safestrncpy(md.jname, str, sizeof(md.jname)); } if (mob->lookup_const(mobt, "Lv", &i32) && i32 >= 0) { - entry->lv = i32; - entry->lv = cap_value(entry->lv, 1, USHRT_MAX); + md.lv = i32; } else if (!inherit) { - entry->lv = 1; + md.lv = 1; } if (mob->lookup_const(mobt, "Hp", &i32) && i32 >= 0) { - mstatus->max_hp = i32; - maxhpUpdated = true; + md.status.max_hp = i32; + maxhpUpdated = true; // battle_config modifiers to max_hp are applied below } else if (!inherit) { - mstatus->max_hp = 1; - maxhpUpdated = true; + md.status.max_hp = 1; + maxhpUpdated = true; // battle_config modifiers to max_hp are applied below } if (mob->lookup_const(mobt, "Sp", &i32) && i32 >= 0) { - mstatus->max_sp = i32; - maxspUpdated = true; + md.status.max_sp = i32; } else if (!inherit) { - maxspUpdated = true; + md.status.max_sp = 1; } if (mob->lookup_const(mobt, "Exp", &i32) && i32 >= 0) { - exp = (double)(i32) * (double)battle_config.base_exp_rate / 100.; - entry->base_exp = (unsigned int)cap_value(exp, 0, UINT_MAX); + int64 exp = apply_percentrate64(i32, battle_config.base_exp_rate, 100); + md.base_exp = (unsigned int)cap_value(exp, 0, UINT_MAX); } if (mob->lookup_const(mobt, "JExp", &i32) && i32 >= 0) { - exp = (double)(i32) * (double)battle_config.job_exp_rate / 100.; - entry->job_exp = (unsigned int)cap_value(exp, 0, UINT_MAX); + int64 exp = apply_percentrate64(i32, battle_config.job_exp_rate, 100); + md.job_exp = (unsigned int)cap_value(exp, 0, UINT_MAX); } if (mob->lookup_const(mobt, "AttackRange", &i32) && i32 >= 0) { - mstatus->rhw.range = i32; + md.status.rhw.range = i32; } else { - mstatus->rhw.range = 1; + md.status.rhw.range = 1; } if ((t = libconfig->setting_get_member(mobt, "Attack"))) { if (config_setting_is_aggregate(t)) { if (libconfig->setting_length(t) >= 2) - mstatus->rhw.atk2 = libconfig->setting_get_int_elem(t, 1); + md.status.rhw.atk2 = libconfig->setting_get_int_elem(t, 1); if (libconfig->setting_length(t) >= 1) - mstatus->rhw.atk = libconfig->setting_get_int_elem(t, 0); + md.status.rhw.atk = libconfig->setting_get_int_elem(t, 0); } else if (mob->lookup_const(mobt, "Attack", &i32) && i32 >= 0) { - mstatus->rhw.atk = i32; - mstatus->rhw.atk2 = i32; + md.status.rhw.atk = i32; + md.status.rhw.atk2 = i32; } } if (mob->lookup_const(mobt, "Def", &i32) && i32 >= 0) { - mstatus->def = mob_parse_dbrow_cap_value(class_, DEFTYPE_MIN, DEFTYPE_MAX, i32); + md.status.def = mob_parse_dbrow_cap_value(md.mob_id, DEFTYPE_MIN, DEFTYPE_MAX, i32); } if (mob->lookup_const(mobt, "Mdef", &i32) && i32 >= 0) { - mstatus->mdef = mob_parse_dbrow_cap_value(class_, DEFTYPE_MIN, DEFTYPE_MAX, i32); + md.status.mdef = mob_parse_dbrow_cap_value(md.mob_id, DEFTYPE_MIN, DEFTYPE_MAX, i32); } if ((t = libconfig->setting_get_member(mobt, "Stats"))) { if (config_setting_is_group(t)) { - mob->read_db_stats_sub(entry, mstatus, class_, t); + mob->read_db_stats_sub(&md, t); } else if (mob->lookup_const(mobt, "Stats", &i32) && i32 >= 0) { - mstatus->str = mstatus->agi = mstatus->vit = mstatus->int_ = mstatus->dex = mstatus->luk = - mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32); + md.status.str = md.status.agi = md.status.vit = md.status.int_ = md.status.dex = md.status.luk = + mob_parse_dbrow_cap_value(md.mob_id, UINT16_MIN, UINT16_MAX, i32); } } - /* - * Disabled for renewal since difference of 0 and 1 still has an impact in the formulas - * Just in case there is a mishandled division by zero please let us know. [malufett] - */ -#ifndef RENEWAL - //All status should be min 1 to prevent divisions by zero from some skills. [Skotlex] - if (mstatus->str < 1) mstatus->str = 1; - if (mstatus->agi < 1) mstatus->agi = 1; - if (mstatus->vit < 1) mstatus->vit = 1; - if (mstatus->int_< 1) mstatus->int_= 1; - if (mstatus->dex < 1) mstatus->dex = 1; - if (mstatus->luk < 1) mstatus->luk = 1; -#endif - - //Tests showed that chase range is effectively 2 cells larger than expected [Playtester] - if (entry->range3 > 0) - entry->range3 += 2; - if (mob->lookup_const(mobt, "ViewRange", &i32) && i32 >= 0) { - entry->range2 = i32; - range2Updated = true; + if (battle_config.view_range_rate != 100) { + md.range2 = i32 * battle_config.view_range_rate / 100; + } else { + md.range2 = i32; + } } else if (!inherit) { - entry->range2 = 1; - range2Updated = true; + md.range2 = 1; } if (mob->lookup_const(mobt, "ChaseRange", &i32) && i32 >= 0) { - entry->range3 = i32; - range3Updated = true; - } else if (!inherit) { - entry->range3 = 1; - range3Updated = true; - } - if (range2Updated) { - if (battle_config.view_range_rate != 100) { - entry->range2 = entry->range2 * battle_config.view_range_rate / 100; - if (entry->range2 < 1) - entry->range2 = 1; - } - } - if (range3Updated) { if (battle_config.chase_range_rate != 100) { - entry->range3 = entry->range3 * battle_config.chase_range_rate / 100; - if (entry->range3 < entry->range2) - entry->range3 = entry->range2; + md.range3 = i32 * battle_config.chase_range_rate / 100; + } else { + md.range3 = i32; } + } else if (!inherit) { + md.range3 = 1; } if (mob->lookup_const(mobt, "Size", &i32) && i32 >= 0) { - mstatus->size = i32; - mstatus->size = cap_value(mstatus->size, 0, 2); + md.status.size = i32; } else if (!inherit) { - mstatus->size = 0; + md.status.size = 0; } if (mob->lookup_const(mobt, "Race", &i32) && i32 >= 0) { - mstatus->race = i32; - mstatus->race = cap_value(mstatus->race, 0, RC_MAX - 1); + md.status.race = i32; } else if (!inherit) { - mstatus->race = 0; + md.status.race = 0; } if ((t = libconfig->setting_get_member(mobt, "Element")) && config_setting_is_list(t)) { + int value = 0; if (mob->get_const(libconfig->setting_get_elem(t, 0), &i32) && mob->get_const(libconfig->setting_get_elem(t, 1), &value)) { - mstatus->def_ele = i32; - mstatus->ele_lv = value; - } - } else { - if (!inherit) { - ShowError("mob_read_db_sub: Missing element for monster ID %d.\n", class_); - return false; - } - } - - if (mstatus->def_ele >= ELE_MAX) { - if (!inherit) { - ShowError("mob_read_db_sub: Invalid element type %d for monster ID %d (max=%d).\n", mstatus->def_ele, class_, ELE_MAX-1); - return false; - } - } - if (mstatus->ele_lv < 1 || mstatus->ele_lv > 4) { - if (!inherit) { - ShowError("mob_read_db_sub: Invalid element level %d for monster ID %d, must be in range 1-4.\n", mstatus->ele_lv, class_); - return false; + md.status.def_ele = i32; + md.status.ele_lv = value; + } else if (!inherit) { + ShowWarning("mob_read_db_sub: Missing element for monster ID %d.\n", md.mob_id); + md.status.def_ele = ELE_NEUTRAL; + md.status.ele_lv = 1; } + } else if (!inherit) { + ShowWarning("mob_read_db_sub: Missing element for monster ID %d.\n", md.mob_id); + md.status.def_ele = ELE_NEUTRAL; + md.status.ele_lv = 1; } if ((t = libconfig->setting_get_member(mobt, "Mode"))) { if (config_setting_is_group(t)) { - mstatus->mode = mob->read_db_mode_sub(entry, mstatus, class_, t); + md.status.mode = mob->read_db_mode_sub(&md, t); } else if (mob->lookup_const(mobt, "Mode", &i32) && i32 >= 0) { - mstatus->mode = i32; + md.status.mode = i32; } } - if (!battle_config.monster_active_enable) - mstatus->mode &= ~MD_AGGRESSIVE; + md.status.mode &= ~MD_AGGRESSIVE; if (mob->lookup_const(mobt, "MoveSpeed", &i32) && i32 >= 0) { - mstatus->speed = i32; + md.status.speed = i32; } - mstatus->aspd_rate = 1000; + md.status.aspd_rate = 1000; if (mob->lookup_const(mobt, "AttackDelay", &i32) && i32 >= 0) { - mstatus->adelay = cap_value(i32, battle_config.monster_max_aspd*2, 4000); + md.status.adelay = cap_value(i32, battle_config.monster_max_aspd*2, 4000); } else if (!inherit) { - mstatus->adelay = 4000; + md.status.adelay = 4000; } if (mob->lookup_const(mobt, "AttackMotion", &i32) && i32 >= 0) { - mstatus->amotion = cap_value(i32, battle_config.monster_max_aspd, 2000); + md.status.amotion = cap_value(i32, battle_config.monster_max_aspd, 2000); } else if (!inherit) { - mstatus->amotion = 2000; + md.status.amotion = 2000; } - //If the attack animation is longer than the delay, the client crops the attack animation! - //On aegis there is no real visible effect of having a recharge-time less than amotion anyway. - if (mstatus->adelay < mstatus->amotion) - mstatus->adelay = mstatus->amotion; - if (mob->lookup_const(mobt, "DamageMotion", &i32) && i32 >= 0) { - mstatus->dmotion = i32; - dmotionUpdated = true; - } else if (!inherit) { - dmotionUpdated = true; + if (battle_config.monster_damage_delay_rate != 100) + md.status.dmotion = i32 * battle_config.monster_damage_delay_rate / 100; + else + md.status.dmotion = i32; } - if (dmotionUpdated && battle_config.monster_damage_delay_rate != 100) - mstatus->dmotion = mstatus->dmotion * battle_config.monster_damage_delay_rate / 100; - - // Fill in remaining status data by using a dummy monster. - data.bl.type = BL_MOB; - data.level = entry->lv; - memcpy(&data.status, mstatus, sizeof(struct status_data)); - status->calc_misc(&data.bl, mstatus, entry->lv); - // MVP EXP Bonus: MEXP - // Some new MVP's MEXP multiple by high exp-rate cause overflow. [LuzZza] if (mob->lookup_const(mobt, "MvpExp", &i32) && i32 >= 0) { - exp = (double)i32 * (double)battle_config.mvp_exp_rate / 100.; - entry->mexp = (unsigned int)cap_value(exp, 0, UINT_MAX); - } else if (!inherit) { - exp = 0; + // Some new MVP's MEXP multiple by high exp-rate cause overflow. [LuzZza] + int64 exp = apply_percentrate64(i32, battle_config.mvp_exp_rate, 100); + md.mexp = (unsigned int)cap_value(exp, 0, UINT_MAX); } if (maxhpUpdated) { - //Now that we know if it is an mvp or not, apply battle_config modifiers [Skotlex] - maxhp = (double)mstatus->max_hp; - if (entry->mexp > 0) { //Mvp - if (battle_config.mvp_hp_rate != 100) - maxhp = maxhp * (double)battle_config.mvp_hp_rate / 100.; + // Now that we know if it is an mvp or not, apply battle_config modifiers [Skotlex] + int64 maxhp = md.status.max_hp; + if (md.mexp > 0) { //Mvp + maxhp = apply_percentrate64(maxhp, battle_config.mvp_hp_rate, 100); } else { //Normal mob - if (battle_config.monster_hp_rate != 100) - maxhp = maxhp * (double)battle_config.monster_hp_rate / 100.; + maxhp = apply_percentrate64(maxhp, battle_config.monster_hp_rate, 100); } - mstatus->max_hp = (unsigned int)cap_value(maxhp, 1, UINT_MAX); + md.status.max_hp = (unsigned int)cap_value(maxhp, 1, UINT_MAX); } - if (maxspUpdated) { - if(mstatus->max_sp < 1) mstatus->max_sp = 1; - } - - //Since mobs always respawn with full life... - mstatus->hp = mstatus->max_hp; - mstatus->sp = mstatus->max_sp; if ((t = libconfig->setting_get_member(mobt, "MvpDrops"))) { if (config_setting_is_group(t)) { - mob->read_db_mvpdrops_sub(entry, mstatus, class_, t); + mob->read_db_mvpdrops_sub(&md, t); } } if ((t = libconfig->setting_get_member(mobt, "Drops"))) { if (config_setting_is_group(t)) { - mob->read_db_drops_sub(entry, mstatus, class_, t); + mob->read_db_drops_sub(&md, t); } } - mob->read_db_additional_fields(entry, class_, mobt, id, source); - // Finally insert monster's data into the database. - if (mob->db_data[class_] == NULL) - mob->db_data[class_] = (struct mob_db*)aMalloc(sizeof(struct mob_db)); - else - //Copy over spawn data - memcpy(&entry->spawn, mob->db_data[class_]->spawn, sizeof(entry->spawn)); + mob->read_db_additional_fields(&md, mobt, n, source); - memcpy(mob->db_data[class_], entry, sizeof(struct mob_db)); - return true; + return mob->db_validate_entry(&md, n, source); } -void mob_read_db_additional_fields(struct mob_db *entry, int class_, config_setting_t *it, int n, const char *source) +/** + * Processes any (plugin-defined) additional fields for a mob_db entry. + * + * @param[in,out] entry The destination mob_db entry, already initialized + * (mob_id, status.mode are expected to be already set). + * @param[in] t The libconfig entry. + * @param[in] n Ordinal number of the entry, to be displayed in case + * of validation errors. + * @param[in] source Source of the entry (file name), to be displayed in + * case of validation errors. + */ +void mob_read_db_additional_fields(struct mob_db *entry, config_setting_t *t, int n, const char *source) { // do nothing. plugins can do own work } @@ -4282,13 +4401,23 @@ void mob_readdb(void) { mob->name_constants(); } +/** + * Reads from a libconfig-formatted mobdb file and inserts the found entries + * into the mob database, overwriting duplicate ones (i.e. mob_db2 overriding + * mob_db.) + * + * @param filename File name, relative to the database path. + * @param ignore_missing Whether to ignore errors caused by a missing db file. + * @return the number of found entries. + */ int mob_read_libconfig(const char *filename, bool ignore_missing) { + bool duplicate[MAX_MOB_DB] = { 0 }; config_t mob_db_conf; char filepath[256]; config_setting_t *mdb; config_setting_t *t; - int i = 0; + int i = 0, count = 0; nullpo_ret(filename); sprintf(filepath, "%s/%s", map->db_path, filename); @@ -4298,15 +4427,28 @@ int mob_read_libconfig(const char *filename, bool ignore_missing) if (libconfig->read_file(&mob_db_conf, filepath) || !(mdb = libconfig->setting_get_member(mob_db_conf.root, "mob_db"))) { ShowError("can't read %s\n", filepath); - return -1; + return 0; } while ((t = libconfig->setting_get_elem(mdb, i++))) { - mob->read_db_sub(t, i - 1, filepath); + int mob_id = mob->read_db_sub(t, i - 1, filename); + + if (mob_id <= 0 || mob_id >= MAX_MOB_DB) + continue; + + count++; + + if (duplicate[mob_id]) { + ShowWarning("mob_read_libconfig:%s: duplicate entry of ID #%d (%s/%s)\n", + filename, mob_id, mob->db_data[mob_id]->sprite, mob->db_data[mob_id]->jname); + } else { + duplicate[mob_id] = true; + } } libconfig->destroy(&mob_db_conf); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, filepath); - return 0; + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename); + + return count; } void mob_name_constants(void) { @@ -4316,7 +4458,7 @@ void mob_name_constants(void) { #endif // ENABLE_CASE_CHECK for (i = 0; i < MAX_MOB_DB; i++) { if (mob->db_data[i] && !mob->is_clone(i)) - script->set_constant2(mob->db_data[i]->sprite, i, 0); + script->set_constant2(mob->db_data[i]->sprite, i, false, false); } #ifdef ENABLE_CASE_CHECK script->parser_current_file = NULL; @@ -4922,7 +5064,7 @@ void mob_reload(void) { /** * Clears spawn related information for a script reload. */ -void mob_clear_spawninfo() +void mob_clear_spawninfo(void) { int i; for (i = 0; i < MAX_MOB_DB; i++) @@ -5127,6 +5269,7 @@ void mob_defaults(void) { mob->item_dropratio_adjust = item_dropratio_adjust; mob->lookup_const = mob_lookup_const; mob->get_const = mob_get_const; + mob->db_validate_entry = mob_db_validate_entry; mob->readdb = mob_readdb; mob->read_libconfig = mob_read_libconfig; mob->read_db_additional_fields = mob_read_db_additional_fields; diff --git a/src/map/mob.h b/src/map/mob.h index 5485b2a91..9a5239b11 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -141,6 +141,7 @@ struct spawn_info { }; struct mob_db { + int mob_id; char sprite[NAME_LENGTH],name[NAME_LENGTH],jname[NAME_LENGTH]; unsigned int base_exp,job_exp; unsigned int mexp; @@ -511,13 +512,14 @@ struct mob_interface { void (*readdb) (void); bool (*lookup_const) (const config_setting_t *it, const char *name, int *value); bool (*get_const) (const config_setting_t *it, int *value); + int (*db_validate_entry) (struct mob_db *entry, int n, const char *source); int (*read_libconfig) (const char *filename, bool ignore_missing); - void (*read_db_additional_fields) (struct mob_db *entry, int class_, config_setting_t *it, int n, const char *source); - bool (*read_db_sub) (config_setting_t *mobt, int id, const char *source); - void (*read_db_drops_sub) (struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t); - void (*read_db_mvpdrops_sub) (struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t); - int (*read_db_mode_sub) (struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t); - void (*read_db_stats_sub) (struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t); + void (*read_db_additional_fields) (struct mob_db *entry, config_setting_t *it, int n, const char *source); + int (*read_db_sub) (config_setting_t *mobt, int id, const char *source); + void (*read_db_drops_sub) (struct mob_db *entry, config_setting_t *t); + void (*read_db_mvpdrops_sub) (struct mob_db *entry, config_setting_t *t); + int (*read_db_mode_sub) (struct mob_db *entry, config_setting_t *t); + void (*read_db_stats_sub) (struct mob_db *entry, config_setting_t *t); void (*name_constants) (void); bool (*readdb_mobavail) (char *str[], int columns, int current); int (*read_randommonster) (void); @@ -528,7 +530,7 @@ struct mob_interface { bool (*readdb_race2) (char *fields[], int columns, int current); bool (*readdb_itemratio) (char *str[], int columns, int current); void (*load) (bool minimal); - void (*clear_spawninfo) (); + void (*clear_spawninfo) (void); void (*destroy_mob_db) (int index); }; diff --git a/src/map/npc.c b/src/map/npc.c index 411e52c29..23b0b9555 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -1769,7 +1769,7 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po if( w + sd->weight > sd->max_weight ) return ERROR_TYPE_INVENTORY_WEIGHT; - if( (double)shop[i].value * amount > INT_MAX ) { + if ((int64)shop[i].value * amount > INT_MAX) { ShowWarning("npc_cashshop_buy: Item '%s' (%d) price overflow attempt!\n", item->name, nameid); ShowDebug("(NPC:'%s' (%s,%d,%d), player:'%s' (%d/%d), value:%d, amount:%d)\n", nd->exname, map->list[nd->bl.m].name, nd->bl.x, nd->bl.y, @@ -1812,7 +1812,7 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) { struct npc_data* nd; struct npc_item_list *shop = NULL; - double z; + int64 z; int i,j,w,skill_t,new_, idx = skill->get_index(MC_DISCOUNT); unsigned short shop_size = 0; @@ -1883,21 +1883,21 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) { value = pc->modifybuyvalue(sd,value); - z += (double)value * amount; + z += (int64)value * amount; w += itemdb_weight(nameid) * amount; } if( nd->master_nd != NULL ) //Script-based shops. return npc->buylist_sub(sd,n,item_list,nd->master_nd); - if( z > (double)sd->status.zeny ) + if (z > sd->status.zeny) return 1; // Not enough Zeny if( w + sd->weight > sd->max_weight ) return 2; // Too heavy if( pc->inventoryblank(sd) < new_ ) return 3; // Not enough space to store items - pc->payzeny(sd,(int)z,LOG_TYPE_NPC, NULL); + pc->payzeny(sd, (int)z, LOG_TYPE_NPC, NULL); for( i = 0; i < n; ++i ) { int nameid = item_list[i*2+1]; @@ -1921,10 +1921,10 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) { skill_t = sd->status.skill[idx].flag - SKILL_FLAG_REPLACED_LV_0; if( skill_t > 0 ) { - z = z * (double)skill_t * (double)battle_config.shop_exp/10000.; - if( z < 1 ) + z = apply_percentrate64(z, skill_t * battle_config.shop_exp, 10000); + if (z < 1) z = 1; - pc->gainexp(sd,NULL,0,(int)z, false); + pc->gainexp(sd, NULL, 0, (int)z, false); } } @@ -1937,7 +1937,7 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) { int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, struct packet_npc_market_purchase *p) { struct npc_data* nd; struct npc_item_list *shop = NULL; - double z; + int64 z; int i,j,w,new_; unsigned short shop_size = 0; @@ -1997,11 +1997,11 @@ int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, st return 1; } - z += (double)value * amount; + z += (int64)value * amount; w += itemdb_weight(nameid) * amount; } - if( z > (double)sd->status.zeny ) /* TODO find official response for this */ + if (z > sd->status.zeny) /* TODO find official response for this */ return 1; // Not enough Zeny if( w + sd->weight > sd->max_weight ) /* TODO find official response for this */ @@ -2098,7 +2098,7 @@ int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* item_li /// @param item_list 'n' pairs <index,amount> /// @return result code for clif->parse_NpcSellListSend int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) { - double z; + int64 z; int i,skill_t, skill_idx = skill->get_index(MC_OVERCHARGE); struct npc_data *nd; bool duplicates[MAX_INVENTORY] = { 0 }; @@ -2147,7 +2147,7 @@ int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) value = pc->modifysellvalue(sd, sd->inventory_data[idx]->value_sell); - z += (double)value*amount; + z += (int64)value * amount; } if( nd->master_nd ) { // Script-controlled shops @@ -2181,8 +2181,8 @@ int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) skill_t = sd->status.skill[skill_idx].flag - SKILL_FLAG_REPLACED_LV_0; if( skill_t > 0 ) { - z = z * (double)skill_t * (double)battle_config.shop_exp/10000.; - if( z < 1 ) + z = apply_percentrate64(z, skill_t * battle_config.shop_exp, 10000); + if (z < 1) z = 1; pc->gainexp(sd, NULL, 0, (int)z, false); } @@ -2276,9 +2276,7 @@ int npc_unload(struct npc_data* nd, bool single) if (nd->chat_id) // remove npc chatroom object and kick users chat->delete_npc_chat(nd); -#ifdef PCRE_SUPPORT npc_chat->finalize(nd); // deallocate npc PCRE data structures -#endif if (single && nd->path != NULL) { npc->releasepathreference(nd->path); @@ -2551,7 +2549,7 @@ void npc_parsename(struct npc_data* nd, const char* name, const char* start, con // Support for using Constants in place of NPC View IDs. int npc_parseview(const char* w4, const char* start, const char* buffer, const char* filepath) { int val = FAKE_NPC, i = 0; - char viewid[1024]; // Max size of name from const.txt, see script->read_constdb. + char viewid[1024]; // Max size of name from constants.conf, see script->read_constdb. // Extract view ID / constant while (w4[i] != '\0') { diff --git a/src/map/npc.h b/src/map/npc.h index 0b2729bcf..568ddfe87 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -309,53 +309,38 @@ void npc_defaults(void); HPShared struct npc_interface *npc; /* comes from npc_chat.c */ -#ifdef PCRE_SUPPORT #include <pcre/include/pcre.h> -#endif // PCRE_SUPPORT /** * Structure containing all info associated with a single pattern block */ struct pcrematch_entry { -#ifdef PCRE_SUPPORT struct pcrematch_entry* next; char* pattern; pcre* pcre_; pcre_extra* pcre_extra_; char* label; -#else // not PCRE_SUPPORT - UNAVAILABLE_STRUCT; -#endif // PCRE_SUPPORT }; /** * A set of patterns that can be activated and deactived with a single command */ struct pcrematch_set { -#ifdef PCRE_SUPPORT struct pcrematch_set* prev; struct pcrematch_set* next; struct pcrematch_entry* head; int setid; -#else // not PCRE_SUPPORT - UNAVAILABLE_STRUCT; -#endif // PCRE_SUPPORT }; /** * Entire data structure hung off a NPC */ struct npc_parse { -#ifdef PCRE_SUPPORT struct pcrematch_set* active; struct pcrematch_set* inactive; -#else // not PCRE_SUPPORT - UNAVAILABLE_STRUCT; -#endif // PCRE_SUPPORT }; struct npc_chat_interface { -#ifdef PCRE_SUPPORT int (*sub) (struct block_list* bl, va_list ap); void (*finalize) (struct npc_data* nd); void (*def_pattern) (struct npc_data* nd, int setid, const char* pattern, const char* label); @@ -365,9 +350,6 @@ struct npc_chat_interface { void (*activate_pcreset) (struct npc_data* nd, int setid); struct pcrematch_set* (*lookup_pcreset) (struct npc_data* nd, int setid); void (*finalize_pcrematch_entry) (struct pcrematch_entry* e); -#else // not PCRE_SUPPORT - UNAVAILABLE_STRUCT; -#endif // PCRE_SUPPORT }; /** @@ -376,7 +358,6 @@ struct npc_chat_interface { * should be moved into core/perhaps its own file once hpm is enhanced for login/char **/ struct pcre_interface { -#ifdef PCRE_SUPPORT pcre *(*compile) (const char *pattern, int options, const char **errptr, int *erroffset, const unsigned char *tableptr); pcre_extra *(*study) (const pcre *code, int options, const char **errptr); int (*exec) (const pcre *code, const pcre_extra *extra, PCRE_SPTR subject, int length, int startoffset, int options, int *ovector, int ovecsize); @@ -385,9 +366,6 @@ struct pcre_interface { void (*free_substring) (const char *stringptr); int (*copy_named_substring) (const pcre *code, const char *subject, int *ovector, int stringcount, const char *stringname, char *buffer, int buffersize); int (*get_substring) (const char *subject, int *ovector, int stringcount, int stringnumber, const char **stringptr); -#else // not PCRE_SUPPORT - UNAVAILABLE_STRUCT; -#endif // PCRE_SUPPORT }; /** diff --git a/src/map/npc_chat.c b/src/map/npc_chat.c index fef3ba99b..001baf3ea 100644 --- a/src/map/npc_chat.c +++ b/src/map/npc_chat.c @@ -20,8 +20,6 @@ */ #define HERCULES_CORE -#ifdef PCRE_SUPPORT - #include "npc.h" // struct npc_data #include "map/mob.h" // struct mob_data @@ -470,5 +468,3 @@ void npc_chat_defaults(void) { libpcre->copy_named_substring = pcre_copy_named_substring; libpcre->get_substring = pcre_get_substring; } - -#endif //PCRE_SUPPORT diff --git a/src/map/packets.h b/src/map/packets.h index 9b9c7945f..0badd94f5 100644 --- a/src/map/packets.h +++ b/src/map/packets.h @@ -2910,9 +2910,15 @@ packet(0x96e,-1,clif->ackmergeitems); packet(0x08A8,26,clif->pFriendsListAdd,2); packet(0x0817,5,clif->pHomMenu,2,4); packet(0x0923,36,clif->pStoragePassword,0); - packet(0x09e8,11,clif->pDull); // CZ_OPEN_MAILBOX - packet(0x0a2e,6,clif->pDull); // TITLE - packet(0x0a02,4); // ZC_DRESSROOM_OPEN + packet(0x09E8,11,clif->pDull); // CZ_OPEN_MAILBOX + packet(0x0A2E,6,clif->pDull); // TITLE + packet(0x0A02,4); // ZC_DRESSROOM_OPEN + packet(0x0A35,4,clif->pOneClick_ItemIdentify,2); +#endif + +#if PACKETVER >= 20150805 // RagexeRE + packet(0x097f,-1); // ZC_SELECTCART + packet(0x0980,7,clif->pSelectCart); // CZ_SELECTCART #endif /* PacketKeys: http://herc.ws/board/topic/1105-hercules-wpe-free-june-14th-patch/ */ diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index 43ff0737f..cc8389a6b 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -1157,9 +1157,9 @@ struct packet_hotkey { char Rotate; #endif struct { - char isSkill; // 0: Item, 1:Skill - unsigned int ID; // Item/Skill ID - short count; // Item Quantity/Skill Level + char isSkill; // 0: Item, 1:Skill + unsigned int ID; // Item/Skill ID + short count; // Item Quantity/Skill Level } hotkey[MAX_HOTKEYS]; #else // not HOTKEY_SAVING UNAVAILABLE_STRUCT; diff --git a/src/map/pc.c b/src/map/pc.c index 2dfd9519b..8d1df71a9 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -4156,34 +4156,39 @@ int pc_insert_card(struct map_session_data* sd, int idx_card, int idx_equip) /*========================================== * Update buying value by skills *------------------------------------------*/ -int pc_modifybuyvalue(struct map_session_data *sd,int orig_value) { - int skill_lv,val = orig_value,rate1 = 0,rate2 = 0; - if((skill_lv=pc->checkskill(sd,MC_DISCOUNT))>0) // merchant discount +int pc_modifybuyvalue(struct map_session_data *sd, int orig_value) +{ + int skill_lv, rate1 = 0, rate2 = 0; + if (orig_value <= 0) + return 0; + if ((skill_lv=pc->checkskill(sd,MC_DISCOUNT)) > 0) // merchant discount rate1 = 5+skill_lv*2-((skill_lv==10)? 1:0); - if((skill_lv=pc->checkskill(sd,RG_COMPULSION))>0) // rogue discount + if ((skill_lv=pc->checkskill(sd,RG_COMPULSION)) > 0) // rogue discount rate2 = 5+skill_lv*4; - if(rate1 < rate2) rate1 = rate2; - if(rate1) - val = (int)((double)orig_value*(double)(100-rate1)/100.); - if(val < 0) val = 0; - if(orig_value > 0 && val < 1) val = 1; - - return val; + if (rate1 < rate2) + rate1 = rate2; + if (rate1 != 0) + orig_value = apply_percentrate(orig_value, 100-rate1, 100); + if (orig_value < 1) + orig_value = 1; + return orig_value; } /*========================================== * Update selling value by skills *------------------------------------------*/ -int pc_modifysellvalue(struct map_session_data *sd,int orig_value) { - int skill_lv,val = orig_value,rate = 0; - if((skill_lv=pc->checkskill(sd,MC_OVERCHARGE))>0) //OverCharge +int pc_modifysellvalue(struct map_session_data *sd, int orig_value) +{ + int skill_lv, rate = 0; + if (orig_value <= 0) + return 0; + if ((skill_lv=pc->checkskill(sd,MC_OVERCHARGE)) > 0) //OverCharge rate = 5+skill_lv*2-((skill_lv==10)? 1:0); - if(rate) - val = (int)((double)orig_value*(double)(100+rate)/100.); - if(val < 0) val = 0; - if(orig_value > 0 && val < 1) val = 1; - - return val; + if (rate != 0) + orig_value = apply_percentrate(orig_value, 100+rate, 100); + if (orig_value < 1) + orig_value = 1; + return orig_value; } /*========================================== @@ -5259,7 +5264,7 @@ int pc_show_steal(struct block_list *bl,va_list ap) int pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 skill_lv) { int i,itemid,flag; - double rate; + int rate; struct status_data *sd_status, *md_status; struct mob_data *md = BL_CAST(BL_MOB, bl); struct item tmp_item; @@ -5284,18 +5289,22 @@ int pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 skil } // base skill success chance (percentual) - rate = (sd_status->dex - md_status->dex)/2 + skill_lv*6 + 4; - rate += sd->bonus.add_steal_rate; + rate = (sd_status->dex - md_status->dex)/2 + skill_lv*6 + 4 + sd->bonus.add_steal_rate; if( rate < 1 ) return 0; // Try dropping one item, in the order from first to last possible slot. // Droprate is affected by the skill success rate. - for( i = 0; i < MAX_STEAL_DROP; i++ ) - if (md->db->dropitem[i].nameid > 0 && (data = itemdb->exists(md->db->dropitem[i].nameid)) != NULL && rnd() % 10000 < md->db->dropitem[i].p * rate/100.) + for (i = 0; i < MAX_STEAL_DROP; i++) { + if (md->db->dropitem[i].nameid == 0) + continue; + if ((data = itemdb->exists(md->db->dropitem[i].nameid)) == NULL) + continue; + if (rnd() % 10000 < apply_percentrate(md->db->dropitem[i].p, rate, 100)) break; - if( i == MAX_STEAL_DROP ) + } + if (i == MAX_STEAL_DROP) return 0; itemid = md->db->dropitem[i].nameid; @@ -6612,16 +6621,16 @@ void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsigned in if (sd->sc.data[SC_OVERLAPEXPUP]) bonus += sd->sc.data[SC_OVERLAPEXPUP]->val1; - *base_exp = (unsigned int) cap_value(*base_exp + (double)*base_exp * bonus/100., 1, UINT_MAX); + *base_exp = (unsigned int) cap_value(*base_exp + apply_percentrate64(*base_exp, bonus, 100), 1, UINT_MAX); if (sd->sc.data[SC_CASH_PLUSONLYJOBEXP]) bonus += sd->sc.data[SC_CASH_PLUSONLYJOBEXP]->val1; - *job_exp = (unsigned int) cap_value(*job_exp + (double)*job_exp * bonus/100., 1, UINT_MAX); + *job_exp = (unsigned int) cap_value(*job_exp + apply_percentrate64(*job_exp, bonus, 100), 1, UINT_MAX); - if( sd->status.mod_exp != 100 ) { - *base_exp = (unsigned int) cap_value((double)*base_exp * sd->status.mod_exp/100., 1, UINT_MAX); - *job_exp = (unsigned int) cap_value((double)*job_exp * sd->status.mod_exp/100., 1, UINT_MAX); + if (sd->status.mod_exp != 100) { + *base_exp = (unsigned int) cap_value(apply_percentrate64(*base_exp, sd->status.mod_exp, 100), 1, UINT_MAX); + *job_exp = (unsigned int) cap_value(apply_percentrate64(*job_exp, sd->status.mod_exp, 100), 1, UINT_MAX); } } @@ -7713,18 +7722,18 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { && !map->list[sd->bl.m].flag.noexppenalty && !map_flag_gvg2(sd->bl.m) && !sd->sc.data[SC_BABY] && !sd->sc.data[SC_CASH_DEATHPENALTY] ) { - unsigned int base_penalty = 0; if (battle_config.death_penalty_base > 0) { + unsigned int base_penalty = 0; switch (battle_config.death_penalty_type) { case 1: - base_penalty = (unsigned int) ((double)pc->nextbaseexp(sd) * (double)battle_config.death_penalty_base/10000); + base_penalty = (unsigned int) apply_percentrate64(pc->nextbaseexp(sd), battle_config.death_penalty_base, 10000); break; case 2: - base_penalty = (unsigned int) ((double)sd->status.base_exp * (double)battle_config.death_penalty_base/10000); + base_penalty = (unsigned int) apply_percentrate64(sd->status.base_exp, battle_config.death_penalty_base, 10000); break; } - if(base_penalty) { + if (base_penalty != 0) { if (battle_config.pk_mode && src && src->type==BL_PC) base_penalty*=2; if( sd->status.mod_death != 100 ) @@ -7735,31 +7744,31 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { } if(battle_config.death_penalty_job > 0) { - base_penalty = 0; + unsigned int job_penalty = 0; switch (battle_config.death_penalty_type) { case 1: - base_penalty = (unsigned int) ((double)pc->nextjobexp(sd) * (double)battle_config.death_penalty_job/10000); + job_penalty = (unsigned int) apply_percentrate64(pc->nextjobexp(sd), battle_config.death_penalty_job, 10000); break; case 2: - base_penalty = (unsigned int) ((double)sd->status.job_exp * (double)battle_config.death_penalty_job/10000); + job_penalty = (unsigned int) apply_percentrate64(sd->status.job_exp, battle_config.death_penalty_job, 10000); break; } - if(base_penalty) { + if (job_penalty != 0) { if (battle_config.pk_mode && src && src->type==BL_PC) - base_penalty*=2; + job_penalty*=2; if( sd->status.mod_death != 100 ) - base_penalty = base_penalty * sd->status.mod_death / 100; - sd->status.job_exp -= min(sd->status.job_exp, base_penalty); + job_penalty = job_penalty * sd->status.mod_death / 100; + sd->status.job_exp -= min(sd->status.job_exp, job_penalty); clif->updatestatus(sd,SP_JOBEXP); } } - if(battle_config.zeny_penalty > 0 && !map->list[sd->bl.m].flag.nozenypenalty) { - base_penalty = (unsigned int)((double)sd->status.zeny * (double)battle_config.zeny_penalty / 10000.); - if(base_penalty) - pc->payzeny(sd, base_penalty, LOG_TYPE_PICKDROP_PLAYER, NULL); + if (battle_config.zeny_penalty > 0 && !map->list[sd->bl.m].flag.nozenypenalty) { + int zeny_penalty = apply_percentrate(sd->status.zeny, battle_config.zeny_penalty, 10000); + if (zeny_penalty != 0) + pc->payzeny(sd, zeny_penalty, LOG_TYPE_PICKDROP_PLAYER, NULL); } } @@ -11479,6 +11488,20 @@ bool pc_db_checkid(unsigned int class_) || (class_ >= JOB_REBELLION && class_ < JOB_MAX ); } +/** + * checks if player have any kind of magnifier in inventory + * @param sd map_session_data of Player + * @return index of magnifer, INDEX_NOT_FOUND if it is not found + */ +int pc_have_magnifier(struct map_session_data *sd) +{ + int n; + n = pc->search_inventory(sd, ITEMID_MAGNIFIER); + if (n == INDEX_NOT_FOUND) + n = pc->search_inventory(sd, ITEMID_NOVICE_MAGNIFIER); + return n; +} + void do_final_pc(void) { db_destroy(pc->itemcd_db); pc->at_db->destroy(pc->at_db,pc->autotrade_final); @@ -11846,4 +11869,6 @@ void pc_defaults(void) { pc->check_job_name = pc_check_job_name; pc->update_idle_time = pc_update_idle_time; + + pc->have_magnifier = pc_have_magnifier; } diff --git a/src/map/pc.h b/src/map/pc.h index 23b46a631..06bc5e5ae 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -1088,6 +1088,8 @@ END_ZEROED_BLOCK; /* End */ int (*check_job_name) (const char *name); void (*update_idle_time) (struct map_session_data* sd, enum e_battle_config_idletime type); + + int (*have_magnifier) (struct map_session_data *sd); }; #ifdef HERCULES_CORE diff --git a/src/map/pet.c b/src/map/pet.c index db8d0d1f1..c6f7e8cca 100644 --- a/src/map/pet.c +++ b/src/map/pet.c @@ -1178,7 +1178,7 @@ int pet_skill_support_timer(int tid, int64 tick, int id, intptr_t data) { /** * Loads (or reloads) the pet database. */ -int read_petdb() +int read_petdb(void) { const char *filename[] = { DBPATH"pet_db.txt", diff --git a/src/map/pet.h b/src/map/pet.h index 2442a381f..83e39a887 100644 --- a/src/map/pet.h +++ b/src/map/pet.h @@ -166,7 +166,7 @@ struct pet_interface { int (*skill_bonus_timer) (int tid, int64 tick, int id, intptr_t data); int (*recovery_timer) (int tid, int64 tick, int id, intptr_t data); int (*skill_support_timer) (int tid, int64 tick, int id, intptr_t data); - int (*read_db) (); + int (*read_db) (void); }; #ifdef HERCULES_CORE diff --git a/src/map/script.c b/src/map/script.c index befb85304..f3c839555 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -159,10 +159,8 @@ const char* script_op2name(int op) { RETURN_OP_NAME(C_SUB_POST); RETURN_OP_NAME(C_ADD_PRE); RETURN_OP_NAME(C_SUB_PRE); -#ifdef PCRE_SUPPORT RETURN_OP_NAME(C_RE_EQ); RETURN_OP_NAME(C_RE_NE); -#endif // PCRE_SUPPORT default: ShowDebug("script_op2name: unexpected op=%d\n", op); @@ -1379,6 +1377,10 @@ const char* parse_simpleexpr(const char *p) return pv; } + if (script->str_data[l].type == C_INT && script->str_data[l].deprecated) { + disp_warning_message("This constant is deprecated and it will be removed in a future version. Please see the script documentation and constants.conf for an alternative.\n", p); + } + p=script->skip_word(p); if( *p == '[' ) { // array(name[i] => getelementofarray(name,i) ) @@ -1442,10 +1444,8 @@ const char* script_parse_subexpr(const char* p,int limit) || (op=C_XOR, opl=4, len=1,*p=='^') // ^ || (op=C_EQ, opl=6, len=2,*p=='=' && p[1]=='=') // == || (op=C_NE, opl=6, len=2,*p=='!' && p[1]=='=') // != -#ifdef PCRE_SUPPORT || (op=C_RE_EQ, opl=6, len=2,*p=='~' && p[1]=='=') // ~= || (op=C_RE_NE, opl=6, len=2,*p=='~' && p[1]=='!') // ~! -#endif // PCRE_SUPPORT || (op=C_R_SHIFT,opl=8, len=2,*p=='>' && p[1]=='>') // >> || (op=C_GE, opl=7, len=2,*p=='>' && p[1]=='=') // >= || (op=C_GT, opl=7, len=1,*p=='>') // > @@ -2231,25 +2231,31 @@ bool script_get_constant(const char* name, int* value) return false; } value[0] = script->str_data[n].val; + if (script->str_data[n].deprecated) { + ShowWarning("The constant '%s' is deprecated and it will be removed in a future version. Please see the script documentation and constants.conf for an alternative.\n", name); + } return true; } /// Creates new constant or parameter with given value. -void script_set_constant(const char* name, int value, bool isparameter) { +void script_set_constant(const char *name, int value, bool is_parameter, bool is_deprecated) +{ int n = script->add_str(name); if( script->str_data[n].type == C_NOP ) {// new - script->str_data[n].type = isparameter ? C_PARAM : C_INT; + script->str_data[n].type = is_parameter ? C_PARAM : C_INT; script->str_data[n].val = value; + script->str_data[n].deprecated = is_deprecated ? 1 : 0; } else if( script->str_data[n].type == C_PARAM || script->str_data[n].type == C_INT ) {// existing parameter or constant ShowError("script_set_constant: Attempted to overwrite existing %s '%s' (old value=%d, new value=%d).\n", ( script->str_data[n].type == C_PARAM ) ? "parameter" : "constant", name, script->str_data[n].val, value); } else {// existing name - ShowError("script_set_constant: Invalid name for %s '%s' (already defined as %s).\n", isparameter ? "parameter" : "constant", name, script->op2name(script->str_data[n].type)); + ShowError("script_set_constant: Invalid name for %s '%s' (already defined as %s).\n", is_parameter ? "parameter" : "constant", name, script->op2name(script->str_data[n].type)); } } /* adds data to a existent constant in the database, inserted normally via parse */ -void script_set_constant2(const char *name, int value, bool isparameter) { +void script_set_constant2(const char *name, int value, bool is_parameter, bool is_deprecated) +{ int n = script->add_str(name); if( script->str_data[n].type == C_PARAM ) { @@ -2273,36 +2279,88 @@ void script_set_constant2(const char *name, int value, bool isparameter) { script->str_data[n].label = -1; } - script->str_data[n].type = isparameter ? C_PARAM : C_INT; + script->str_data[n].type = is_parameter ? C_PARAM : C_INT; script->str_data[n].val = value; - + script->str_data[n].deprecated = is_deprecated ? 1 : 0; } -/*========================================== - * Reading constant databases - * const.txt - *------------------------------------------*/ -void read_constdb(void) { - FILE *fp; - char line[1024],name[1024],val[1024]; - int type; - sprintf(line, "%s/const.txt", map->db_path); - fp=fopen(line, "r"); - if(fp==NULL) { - ShowError("can't read %s\n", line); - return ; +/** + * Loads the constants database from constants.conf + */ +void read_constdb(void) +{ + config_t constants_conf; + char filepath[256]; + config_setting_t *cdb; + config_setting_t *t; + int i = 0; + + sprintf(filepath, "%s/constants.conf", map->db_path); + + if (libconfig->read_file(&constants_conf, filepath) || !(cdb = libconfig->setting_get_member(constants_conf.root, "constants_db"))) { + ShowError("can't read %s\n", filepath); + return; } - while (fgets(line, sizeof(line), fp)) { - if (line[0] == '/' && line[1] == '/') + + while ((t = libconfig->setting_get_elem(cdb, i++))) { + bool is_parameter = false; + bool is_deprecated = false; + int value = 0; + const char *name = config_setting_name(t); + const char *p = name; + + while (*p != '\0') { + if (!ISALNUM(*p) && *p != '_') + break; + p++; + } + if (*p != '\0') { + ShowWarning("read_constdb: Invalid constant name %s. Skipping.\n", name); continue; - type = 0; - if (sscanf(line, "%1023[A-Za-z0-9_],%1023[-0-9xXA-Fa-f],%d", name, val, &type) >=2 - || sscanf(line, "%1023[A-Za-z0-9_] %1023[-0-9xXA-Fa-f] %d", name, val, &type) >=2 - ) { - script->set_constant(name, (int)strtol(val, NULL, 0), (bool)type); } + if (strcmp(name, "comment__") == 0) { + const char *comment = libconfig->setting_get_string(t); + if (comment == NULL) + continue; + if (*comment == '\0') + comment = NULL; + script->constdb_comment(comment); + continue; + } + if (config_setting_is_aggregate(t)) { + int i32; + if (!libconfig->setting_lookup_int(t, "Value", &i32)) { + ShowWarning("read_constdb: Invalid entry for %s. Skipping.\n", name); + continue; + } + value = i32; + if (libconfig->setting_lookup_bool(t, "Parameter", &i32)) { + if (i32 != 0) + is_parameter = true; + } + if (libconfig->setting_lookup_bool(t, "Deprecated", &i32)) { + if (i32 != 0) + is_deprecated = true; + } + } else { + value = libconfig->setting_get_int(t); + } + script->set_constant(name, value, is_parameter, is_deprecated); } - fclose(fp); + script->constdb_comment(NULL); + libconfig->destroy(&constants_conf); +} + +/** + * Sets the current constdb comment. + * + * This function does nothing (used by plugins only) + * + * @param comment The comment to set (NULL to unset) + */ +void script_constdb_comment(const char *comment) +{ + (void)comment; } // Standard UNIX tab size is 8 @@ -3699,7 +3757,6 @@ void op_2str(struct script_state* st, int op, const char* s1, const char* s2) case C_GE: a = (strcmp(s1,s2) >= 0); break; case C_LT: a = (strcmp(s1,s2) < 0); break; case C_LE: a = (strcmp(s1,s2) <= 0); break; -#ifdef PCRE_SUPPORT case C_RE_EQ: case C_RE_NE: { @@ -3764,7 +3821,6 @@ void op_2str(struct script_state* st, int op, const char* s1, const char* s2) libpcre->free(extra_regex); } break; -#endif // PCRE_SUPPORT case C_ADD: { char* buf = (char *)aMalloc((strlen(s1)+strlen(s2)+1)*sizeof(char)); @@ -3789,7 +3845,7 @@ void op_2str(struct script_state* st, int op, const char* s1, const char* s2) void op_2num(struct script_state* st, int op, int i1, int i2) { int ret; - double ret_double; + int64 ret64; switch( op ) { case C_AND: ret = i1 & i2; break; @@ -3821,25 +3877,21 @@ void op_2num(struct script_state* st, int op, int i1, int i2) ret = i1 % i2; break; default: - switch( op ) - {// operators that can overflow/underflow - case C_ADD: ret = i1 + i2; ret_double = (double)i1 + (double)i2; break; - case C_SUB: ret = i1 - i2; ret_double = (double)i1 - (double)i2; break; - case C_MUL: ret = i1 * i2; ret_double = (double)i1 * (double)i2; break; + switch (op) { // operators that can overflow/underflow + case C_ADD: ret = i1 + i2; ret64 = (int64)i1 + i2; break; + case C_SUB: ret = i1 - i2; ret64 = (int64)i1 - i2; break; + case C_MUL: ret = i1 * i2; ret64 = (int64)i1 * i2; break; default: ShowError("script:op_2num: unexpected number operator %s i1=%d i2=%d\n", script->op2name(op), i1, i2); script->reportsrc(st); script_pushnil(st); return; } - if( ret_double < (double)INT_MIN ) - { + if (ret64 < INT_MIN) { ShowWarning("script:op_2num: underflow detected op=%s i1=%d i2=%d\n", script->op2name(op), i1, i2); script->reportsrc(st); ret = INT_MIN; - } - else if( ret_double > (double)INT_MAX ) - { + } else if (ret64 > INT_MAX) { ShowWarning("script:op_2num: overflow detected op=%s i1=%d i2=%d\n", script->op2name(op), i1, i2); script->reportsrc(st); ret = INT_MAX; @@ -4361,10 +4413,8 @@ void run_script_main(struct script_state *st) { case C_LOR: case C_R_SHIFT: case C_L_SHIFT: -#ifdef PCRE_SUPPORT case C_RE_EQ: case C_RE_NE: -#endif // PCRE_SUPPORT script->op_2(st, c); break; @@ -9648,20 +9698,18 @@ BUILDIN(makepet) BUILDIN(getexp) { int base=0,job=0; - double bonus; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - base=script_getnum(st,2); - job =script_getnum(st,3); - if(base<0 || job<0) + base = script_getnum(st,2); + job = script_getnum(st,3); + if (base < 0 || job < 0) return true; // bonus for npc-given exp - bonus = battle_config.quest_exp_rate / 100.; - base = (int) cap_value(base * bonus, 0, INT_MAX); - job = (int) cap_value(job * bonus, 0, INT_MAX); + base = cap_value(apply_percentrate(base, battle_config.quest_exp_rate, 100), 0, INT_MAX); + job = cap_value(apply_percentrate(job, battle_config.quest_exp_rate, 100), 0, INT_MAX); pc->gainexp(sd, &sd->bl, base, job, true); @@ -11684,7 +11732,7 @@ BUILDIN(getmapflag) case MF_RESET: script_pushint(st,map->list[m].flag.reset); break; case MF_NOTOMB: script_pushint(st,map->list[m].flag.notomb); break; case MF_NOCASHSHOP: script_pushint(st,map->list[m].flag.nocashshop); break; - case MF_NOVIEWID: script_pushint(st,map->list[m].flag.noviewid); break; + case MF_NOVIEWID: script_pushint(st,map->list[m].flag.noviewid); break; } } @@ -11808,7 +11856,7 @@ BUILDIN(setmapflag) { case MF_RESET: map->list[m].flag.reset = 1; break; case MF_NOTOMB: map->list[m].flag.notomb = 1; break; case MF_NOCASHSHOP: map->list[m].flag.nocashshop = 1; break; - case MF_NOVIEWID: map->list[m].flag.noviewid = (val <= 0) ? 0 : val; break; + case MF_NOVIEWID: map->list[m].flag.noviewid = (val <= 0) ? EQP_NONE : val; break; } } @@ -11895,7 +11943,7 @@ BUILDIN(removemapflag) { case MF_RESET: map->list[m].flag.reset = 0; break; case MF_NOTOMB: map->list[m].flag.notomb = 0; break; case MF_NOCASHSHOP: map->list[m].flag.nocashshop = 0; break; - case MF_NOVIEWID: map->list[m].flag.noviewid = 0; break; + case MF_NOVIEWID: map->list[m].flag.noviewid = EQP_NONE; break; } } @@ -18450,7 +18498,7 @@ BUILDIN(setcashmount) if (sd->sc.data[SC_ALL_RIDING]) status_change_end(&sd->bl, SC_ALL_RIDING, INVALID_TIMER); else - sc_start(NULL,&sd->bl, SC_ALL_RIDING, 100, 0, -1); + sc_start(NULL,&sd->bl, SC_ALL_RIDING, 100, 25, -1); script_pushint(st,1);//in both cases, return 1. } return true; @@ -20067,7 +20115,6 @@ BUILDIN(_) { } // declarations that were supposed to be exported from npc_chat.c -#ifdef PCRE_SUPPORT BUILDIN(defpattern); BUILDIN(activatepset); BUILDIN(deactivatepset); @@ -20080,7 +20127,6 @@ BUILDIN(pcre_match) { script->op_2str(st, C_RE_EQ, input, regex); return true; } -#endif /** * Adds a built-in script function. @@ -20497,13 +20543,11 @@ void script_parse_builtin(void) { BUILDIN_DEF(getrefine,""), // returns the refined number of the current item, or an item with index specified [celest] BUILDIN_DEF(night,""), // sets the server to night time BUILDIN_DEF(day,""), // sets the server to day time -#ifdef PCRE_SUPPORT BUILDIN_DEF(defpattern,"iss"), // Define pattern to listen for [MouseJstr] BUILDIN_DEF(activatepset,"i"), // Activate a pattern set [MouseJstr] BUILDIN_DEF(deactivatepset,"i"), // Deactive a pattern set [MouseJstr] BUILDIN_DEF(deletepset,"i"), // Delete a pattern set [MouseJstr] BUILDIN_DEF(pcre_match,"ss"), -#endif BUILDIN_DEF(dispbottom,"s?"), //added from jA [Lupus] BUILDIN_DEF(getusersname,""), BUILDIN_DEF(recovery,""), @@ -20778,119 +20822,164 @@ void script_label_add(int key, int pos) { /** * Sets source-end constants for scripts to play with **/ -void script_hardcoded_constants(void) { - /* server defines */ - script->set_constant("PACKETVER",PACKETVER,false); - script->set_constant("MAX_LEVEL",MAX_LEVEL,false); - script->set_constant("MAX_STORAGE",MAX_STORAGE,false); - script->set_constant("MAX_GUILD_STORAGE",MAX_GUILD_STORAGE,false); - script->set_constant("MAX_CART",MAX_INVENTORY,false); - script->set_constant("MAX_INVENTORY",MAX_INVENTORY,false); - script->set_constant("MAX_ZENY",MAX_ZENY,false); - script->set_constant("MAX_BG_MEMBERS",MAX_BG_MEMBERS,false); - script->set_constant("MAX_CHAT_USERS",MAX_CHAT_USERS,false); - script->set_constant("MAX_REFINE",MAX_REFINE,false); - - /* status options */ - script->set_constant("Option_Nothing",OPTION_NOTHING,false); - script->set_constant("Option_Sight",OPTION_SIGHT,false); - script->set_constant("Option_Hide",OPTION_HIDE,false); - script->set_constant("Option_Cloak",OPTION_CLOAK,false); - script->set_constant("Option_Falcon",OPTION_FALCON,false); - script->set_constant("Option_Riding",OPTION_RIDING,false); - script->set_constant("Option_Invisible",OPTION_INVISIBLE,false); - script->set_constant("Option_Orcish",OPTION_ORCISH,false); - script->set_constant("Option_Wedding",OPTION_WEDDING,false); - script->set_constant("Option_Chasewalk",OPTION_CHASEWALK,false); - script->set_constant("Option_Flying",OPTION_FLYING,false); - script->set_constant("Option_Xmas",OPTION_XMAS,false); - script->set_constant("Option_Transform",OPTION_TRANSFORM,false); - script->set_constant("Option_Summer",OPTION_SUMMER,false); - script->set_constant("Option_Dragon1",OPTION_DRAGON1,false); - script->set_constant("Option_Wug",OPTION_WUG,false); - script->set_constant("Option_Wugrider",OPTION_WUGRIDER,false); - script->set_constant("Option_Madogear",OPTION_MADOGEAR,false); - script->set_constant("Option_Dragon2",OPTION_DRAGON2,false); - script->set_constant("Option_Dragon3",OPTION_DRAGON3,false); - script->set_constant("Option_Dragon4",OPTION_DRAGON4,false); - script->set_constant("Option_Dragon5",OPTION_DRAGON5,false); - script->set_constant("Option_Hanbok",OPTION_HANBOK,false); - script->set_constant("Option_Oktoberfest",OPTION_OKTOBERFEST,false); - - /* status option compounds */ - script->set_constant("Option_Dragon",OPTION_DRAGON,false); - script->set_constant("Option_Costume",OPTION_COSTUME,false); - - /* send_target */ - script->set_constant("ALL_CLIENT",ALL_CLIENT,false); - script->set_constant("ALL_SAMEMAP",ALL_SAMEMAP,false); - script->set_constant("AREA",AREA,false); - script->set_constant("AREA_WOS",AREA_WOS,false); - script->set_constant("AREA_WOC",AREA_WOC,false); - script->set_constant("AREA_WOSC",AREA_WOSC,false); - script->set_constant("AREA_CHAT_WOC",AREA_CHAT_WOC,false); - script->set_constant("CHAT",CHAT,false); - script->set_constant("CHAT_WOS",CHAT_WOS,false); - script->set_constant("PARTY",PARTY,false); - script->set_constant("PARTY_WOS",PARTY_WOS,false); - script->set_constant("PARTY_SAMEMAP",PARTY_SAMEMAP,false); - script->set_constant("PARTY_SAMEMAP_WOS",PARTY_SAMEMAP_WOS,false); - script->set_constant("PARTY_AREA",PARTY_AREA,false); - script->set_constant("PARTY_AREA_WOS",PARTY_AREA_WOS,false); - script->set_constant("GUILD",GUILD,false); - script->set_constant("GUILD_WOS",GUILD_WOS,false); - script->set_constant("GUILD_SAMEMAP",GUILD_SAMEMAP,false); - script->set_constant("GUILD_SAMEMAP_WOS",GUILD_SAMEMAP_WOS,false); - script->set_constant("GUILD_AREA",GUILD_AREA,false); - script->set_constant("GUILD_AREA_WOS",GUILD_AREA_WOS,false); - script->set_constant("GUILD_NOBG",GUILD_NOBG,false); - script->set_constant("DUEL",DUEL,false); - script->set_constant("DUEL_WOS",DUEL_WOS,false); - script->set_constant("SELF",SELF,false); - script->set_constant("BG",BG,false); - script->set_constant("BG_WOS",BG_WOS,false); - script->set_constant("BG_SAMEMAP",BG_SAMEMAP,false); - script->set_constant("BG_SAMEMAP_WOS",BG_SAMEMAP_WOS,false); - script->set_constant("BG_AREA",BG_AREA,false); - script->set_constant("BG_AREA_WOS",BG_AREA_WOS,false); - script->set_constant("BG_QUEUE",BG_QUEUE,false); - - /* Renewal */ +void script_hardcoded_constants(void) +{ + script->constdb_comment("Boolean"); + script->set_constant("true", 1, false, false); + script->set_constant("false", 0, false, false); + + script->constdb_comment("Server defines"); + script->set_constant("PACKETVER",PACKETVER,false, false); + script->set_constant("MAX_LEVEL",MAX_LEVEL,false, false); + script->set_constant("MAX_STORAGE",MAX_STORAGE,false, false); + script->set_constant("MAX_GUILD_STORAGE",MAX_GUILD_STORAGE,false, false); + script->set_constant("MAX_CART",MAX_INVENTORY,false, false); + script->set_constant("MAX_INVENTORY",MAX_INVENTORY,false, false); + script->set_constant("MAX_ZENY",MAX_ZENY,false, false); + script->set_constant("MAX_BG_MEMBERS",MAX_BG_MEMBERS,false, false); + script->set_constant("MAX_CHAT_USERS",MAX_CHAT_USERS,false, false); + script->set_constant("MAX_REFINE",MAX_REFINE,false, false); + + script->constdb_comment("status options"); + script->set_constant("Option_Nothing",OPTION_NOTHING,false, false); + script->set_constant("Option_Sight",OPTION_SIGHT,false, false); + script->set_constant("Option_Hide",OPTION_HIDE,false, false); + script->set_constant("Option_Cloak",OPTION_CLOAK,false, false); + script->set_constant("Option_Falcon",OPTION_FALCON,false, false); + script->set_constant("Option_Riding",OPTION_RIDING,false, false); + script->set_constant("Option_Invisible",OPTION_INVISIBLE,false, false); + script->set_constant("Option_Orcish",OPTION_ORCISH,false, false); + script->set_constant("Option_Wedding",OPTION_WEDDING,false, false); + script->set_constant("Option_Chasewalk",OPTION_CHASEWALK,false, false); + script->set_constant("Option_Flying",OPTION_FLYING,false, false); + script->set_constant("Option_Xmas",OPTION_XMAS,false, false); + script->set_constant("Option_Transform",OPTION_TRANSFORM,false, false); + script->set_constant("Option_Summer",OPTION_SUMMER,false, false); + script->set_constant("Option_Dragon1",OPTION_DRAGON1,false, false); + script->set_constant("Option_Wug",OPTION_WUG,false, false); + script->set_constant("Option_Wugrider",OPTION_WUGRIDER,false, false); + script->set_constant("Option_Madogear",OPTION_MADOGEAR,false, false); + script->set_constant("Option_Dragon2",OPTION_DRAGON2,false, false); + script->set_constant("Option_Dragon3",OPTION_DRAGON3,false, false); + script->set_constant("Option_Dragon4",OPTION_DRAGON4,false, false); + script->set_constant("Option_Dragon5",OPTION_DRAGON5,false, false); + script->set_constant("Option_Hanbok",OPTION_HANBOK,false, false); + script->set_constant("Option_Oktoberfest",OPTION_OKTOBERFEST,false, false); + + script->constdb_comment("status option compounds"); + script->set_constant("Option_Dragon",OPTION_DRAGON,false, false); + script->set_constant("Option_Costume",OPTION_COSTUME,false, false); + + script->constdb_comment("send_target"); + script->set_constant("ALL_CLIENT",ALL_CLIENT,false, false); + script->set_constant("ALL_SAMEMAP",ALL_SAMEMAP,false, false); + script->set_constant("AREA",AREA,false, false); + script->set_constant("AREA_WOS",AREA_WOS,false, false); + script->set_constant("AREA_WOC",AREA_WOC,false, false); + script->set_constant("AREA_WOSC",AREA_WOSC,false, false); + script->set_constant("AREA_CHAT_WOC",AREA_CHAT_WOC,false, false); + script->set_constant("CHAT",CHAT,false, false); + script->set_constant("CHAT_WOS",CHAT_WOS,false, false); + script->set_constant("PARTY",PARTY,false, false); + script->set_constant("PARTY_WOS",PARTY_WOS,false, false); + script->set_constant("PARTY_SAMEMAP",PARTY_SAMEMAP,false, false); + script->set_constant("PARTY_SAMEMAP_WOS",PARTY_SAMEMAP_WOS,false, false); + script->set_constant("PARTY_AREA",PARTY_AREA,false, false); + script->set_constant("PARTY_AREA_WOS",PARTY_AREA_WOS,false, false); + script->set_constant("GUILD",GUILD,false, false); + script->set_constant("GUILD_WOS",GUILD_WOS,false, false); + script->set_constant("GUILD_SAMEMAP",GUILD_SAMEMAP,false, false); + script->set_constant("GUILD_SAMEMAP_WOS",GUILD_SAMEMAP_WOS,false, false); + script->set_constant("GUILD_AREA",GUILD_AREA,false, false); + script->set_constant("GUILD_AREA_WOS",GUILD_AREA_WOS,false, false); + script->set_constant("GUILD_NOBG",GUILD_NOBG,false, false); + script->set_constant("DUEL",DUEL,false, false); + script->set_constant("DUEL_WOS",DUEL_WOS,false, false); + script->set_constant("SELF",SELF,false, false); + script->set_constant("BG",BG,false, false); + script->set_constant("BG_WOS",BG_WOS,false, false); + script->set_constant("BG_SAMEMAP",BG_SAMEMAP,false, false); + script->set_constant("BG_SAMEMAP_WOS",BG_SAMEMAP_WOS,false, false); + script->set_constant("BG_AREA",BG_AREA,false, false); + script->set_constant("BG_AREA_WOS",BG_AREA_WOS,false, false); + script->set_constant("BG_QUEUE",BG_QUEUE,false, false); + + script->constdb_comment("LOOK_ constants, use in setlook/changelook script commands"); + script->set_constant("LOOK_BASE", LOOK_BASE, false, false); + script->set_constant("LOOK_HAIR", LOOK_HAIR, false, false); + script->set_constant("LOOK_WEAPON", LOOK_WEAPON, false, false); + script->set_constant("LOOK_HEAD_BOTTOM", LOOK_HEAD_BOTTOM, false, false); + script->set_constant("LOOK_HEAD_TOP", LOOK_HEAD_TOP, false, false); + script->set_constant("LOOK_HEAD_MID", LOOK_HEAD_MID, false, false); + script->set_constant("LOOK_HAIR_COLOR", LOOK_HAIR_COLOR, false, false); + script->set_constant("LOOK_CLOTHES_COLOR", LOOK_CLOTHES_COLOR, false, false); + script->set_constant("LOOK_SHIELD", LOOK_SHIELD, false, false); + script->set_constant("LOOK_SHOES", LOOK_SHOES, false, false); + script->set_constant("LOOK_BODY", LOOK_BODY, false, false); + script->set_constant("LOOK_FLOOR", LOOK_FLOOR, false, false); + script->set_constant("LOOK_ROBE", LOOK_ROBE, false, false); + script->set_constant("LOOK_BODY2", LOOK_BODY2, false, false); + + script->constdb_comment("Equip Position in Bits, use with *getiteminfo type 5, or @inventorylist_equip"); + script->set_constant("EQP_HEAD_LOW", EQP_HEAD_LOW, false, false); + script->set_constant("EQP_HEAD_MID", EQP_HEAD_MID, false, false); + script->set_constant("EQP_HEAD_TOP", EQP_HEAD_TOP, false, false); + script->set_constant("EQP_HAND_R", EQP_HAND_R, false, false); + script->set_constant("EQP_HAND_L", EQP_HAND_L, false, false); + script->set_constant("EQP_ARMOR", EQP_ARMOR, false, false); + script->set_constant("EQP_SHOES", EQP_SHOES, false, false); + script->set_constant("EQP_GARMENT", EQP_GARMENT, false, false); + script->set_constant("EQP_ACC_L", EQP_ACC_L, false, false); + script->set_constant("EQP_ACC_R", EQP_ACC_R, false, false); + script->set_constant("EQP_COSTUME_HEAD_TOP", EQP_COSTUME_HEAD_TOP, false, false); + script->set_constant("EQP_COSTUME_HEAD_MID", EQP_COSTUME_HEAD_MID, false, false); + script->set_constant("EQP_COSTUME_HEAD_LOW", EQP_COSTUME_HEAD_LOW, false, false); + script->set_constant("EQP_COSTUME_GARMENT", EQP_COSTUME_GARMENT, false, false); + script->set_constant("EQP_AMMO", EQP_AMMO, false, false); + script->set_constant("EQP_SHADOW_ARMOR", EQP_SHADOW_ARMOR, false, false); + script->set_constant("EQP_SHADOW_WEAPON", EQP_SHADOW_WEAPON, false, false); + script->set_constant("EQP_SHADOW_SHIELD", EQP_SHADOW_SHIELD, false, false); + script->set_constant("EQP_SHADOW_SHOES", EQP_SHADOW_SHOES, false, false); + script->set_constant("EQP_SHADOW_ACC_R", EQP_SHADOW_ACC_R, false, false); + script->set_constant("EQP_SHADOW_ACC_L", EQP_SHADOW_ACC_L, false, false); + + script->constdb_comment("Renewal"); #ifdef RENEWAL - script->set_constant("RENEWAL", 1, false); + script->set_constant("RENEWAL", 1, false, false); #else - script->set_constant("RENEWAL", 0, false); + script->set_constant("RENEWAL", 0, false, false); #endif #ifdef RENEWAL_CAST - script->set_constant("RENEWAL_CAST", 1, false); + script->set_constant("RENEWAL_CAST", 1, false, false); #else - script->set_constant("RENEWAL_CAST", 0, false); + script->set_constant("RENEWAL_CAST", 0, false, false); #endif #ifdef RENEWAL_DROP - script->set_constant("RENEWAL_DROP", 1, false); + script->set_constant("RENEWAL_DROP", 1, false, false); #else - script->set_constant("RENEWAL_DROP", 0, false); + script->set_constant("RENEWAL_DROP", 0, false, false); #endif #ifdef RENEWAL_EXP - script->set_constant("RENEWAL_EXP", 1, false); + script->set_constant("RENEWAL_EXP", 1, false, false); #else - script->set_constant("RENEWAL_EXP", 0, false); + script->set_constant("RENEWAL_EXP", 0, false, false); #endif #ifdef RENEWAL_LVDMG - script->set_constant("RENEWAL_LVDMG", 1, false); + script->set_constant("RENEWAL_LVDMG", 1, false, false); #else - script->set_constant("RENEWAL_LVDMG", 0, false); + script->set_constant("RENEWAL_LVDMG", 0, false, false); #endif #ifdef RENEWAL_EDP - script->set_constant("RENEWAL_EDP", 1, false); + script->set_constant("RENEWAL_EDP", 1, false, false); #else - script->set_constant("RENEWAL_EDP", 0, false); + script->set_constant("RENEWAL_EDP", 0, false, false); #endif #ifdef RENEWAL_ASPD - script->set_constant("RENEWAL_ASPD", 1, false); + script->set_constant("RENEWAL_ASPD", 1, false, false); #else - script->set_constant("RENEWAL_ASPD", 0, false); + script->set_constant("RENEWAL_ASPD", 0, false, false); #endif + script->constdb_comment(NULL); } /** @@ -21068,6 +21157,7 @@ void script_defaults(void) { script->parse_expr = parse_expr; script->parse_line = parse_line; script->read_constdb = read_constdb; + script->constdb_comment = script_constdb_comment; script->print_line = script_print_line; script->errorwarning_sub = script_errorwarning_sub; script->set_reg = set_reg; diff --git a/src/map/script.h b/src/map/script.h index 5f71662c6..351ccd02a 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -233,10 +233,8 @@ typedef enum c_op { C_SUB_POST, // a-- C_ADD_PRE, // ++a C_SUB_PRE, // --a -#ifdef PCRE_SUPPORT C_RE_EQ, // ~= C_RE_NE, // ~! -#endif // PCRE_SUPPORT } c_op; /// Script queue options @@ -666,8 +664,8 @@ struct script_interface { struct script_data* (*push_str) (struct script_stack* stack, enum c_op type, char* str); struct script_data* (*push_copy) (struct script_stack* stack, int pos); void (*pop_stack) (struct script_state* st, int start, int end); - void (*set_constant) (const char* name, int value, bool isparameter); - void (*set_constant2) (const char *name, int value, bool isparameter); + void (*set_constant) (const char *name, int value, bool is_parameter, bool is_deprecated); + void (*set_constant2) (const char *name, int value, bool is_parameter, bool is_deprecated); bool (*get_constant) (const char* name, int* value); void (*label_add)(int key, int pos); void (*run) (struct script_code *rootscript, int pos, int rid, int oid); @@ -726,6 +724,7 @@ struct script_interface { const char* (*parse_expr) (const char *p); const char* (*parse_line) (const char *p); void (*read_constdb) (void); + void (*constdb_comment) (const char *comment); const char* (*print_line) (StringBuf *buf, const char *p, const char *mark, int line); void (*errorwarning_sub) (StringBuf *buf, const char *src, const char *file, int start_line, const char *error_msg, const char *error_pos); int (*set_reg) (struct script_state *st, struct map_session_data *sd, int64 num, const char *name, const void *value, struct reg_db *ref); diff --git a/src/map/skill.c b/src/map/skill.c index b3c264e43..c70b94cd5 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -4374,7 +4374,7 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1 clif->slide(src, x, y); clif->fixpos(src); // the official server send these two packets. skill->attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); - if ( rnd() % 100 < 4 * skill_lv && skill_id == GC_DARKILLUSION ) + if (rnd() % 100 < 4 * skill_lv && skill_id == GC_DARKILLUSION) skill->castend_damage_id(src, bl, GC_CROSSIMPACT, skill_lv, tick, flag); } } @@ -6047,7 +6047,14 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin break; case MC_CHANGECART: - clif->skill_nodamage(src,bl,skill_id,skill_lv,1); + clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); + break; + + case MC_CARTDECORATE: + if (sd) { + clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); + clif->selectcart(sd); + } break; case TK_MISSION: @@ -11770,6 +11777,10 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick sc_start4(ss,bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit); break; case UNT_APPLEIDUN: + // If Aegis, apple of idun doesn't update its effect + if (!battle_config.song_timer_reset && sc && sce) + return 0; + // Let it fall through case UNT_WHISTLE: case UNT_ASSASSINCROSS: case UNT_POEMBRAGI: @@ -11777,19 +11788,34 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick case UNT_DONTFORGETME: case UNT_FORTUNEKISS: case UNT_SERVICEFORYOU: + // Don't buff themselves without link! if (sg->src_id==bl->id && !(sc && sc->data[SC_SOULLINK] && sc->data[SC_SOULLINK]->val2 == SL_BARDDANCER)) return 0; if (!sc) return 0; if (!sce) sc_start4(ss,bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit); + // From here songs are already active else if (battle_config.song_timer_reset && sce->val4 == 1) { - //Readjust timers since the effect will not last long. + // eA style: + // Readjust timers since the effect will not last long. sce->val4 = 0; timer->delete(sce->timer, status->change_timer); sce->timer = timer->add(tick+sg->limit, status->change_timer, bl->id, type); + } else if (!battle_config.song_timer_reset) { + // Aegis style: + // Songs won't renew unless finished + const struct TimerData *td = timer->get(sce->timer); + if (DIFF_TICK32(td->tick, timer->gettick()) < sg->interval) { + // Update with new values as the current one will vanish soon + timer->delete(sce->timer, status->change_timer); + sce->timer = timer->add(tick+sg->limit, status->change_timer, bl->id, type); + sce->val1 = sg->skill_lv; // Why are we storing skill_lv as val1? + sce->val2 = sg->val1; + sce->val3 = sg->val2; + sce->val4 = 0; + } } - break; case UNT_FOGWALL: @@ -12194,18 +12220,25 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 if (md && md->class_ == MOBID_EMPELIUM) break; #endif - if ((sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER)) - || (!(battle_config.song_timer_reset) && tsc && tsc->data[type] && tsc->data[type]->val4 == 1)) + // Don't buff themselves! + if ((sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER))) break; - heal = skill->calc_heal(ss,bl,sg->skill_id, sg->skill_lv, true); - if( tsc && tsc->data[SC_AKAITSUKI] && heal ) - heal = ~heal + 1; - clif->skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); - status->heal(bl, heal, 0, 0); - - if (!battle_config.song_timer_reset) + // Aegis style + // Check if the remaining time is enough to survive the next update + if (!battle_config.song_timer_reset + && !(tsc && tsc->data[type] && tsc->data[type]->val4 == 1)) { + // Apple of Idun is not active. Start it now sc_start4(ss, bl, type, 100, sg->skill_lv, sg->val1, sg->val2, 0, sg->limit); + } + + if (tstatus->hp < tstatus->max_hp) { + heal = skill->calc_heal(ss,bl,sg->skill_id, sg->skill_lv, true); + if( tsc && tsc->data[SC_AKAITSUKI] && heal ) + heal = ~heal + 1; + clif->skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); + status->heal(bl, heal, 0, 0); + } } break; case UNT_POEMBRAGI: @@ -12215,12 +12248,30 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 case UNT_DONTFORGETME: case UNT_FORTUNEKISS: case UNT_SERVICEFORYOU: - if (battle_config.song_timer_reset - || (!(battle_config.song_timer_reset) && tsc && tsc->data[type] && tsc->data[type]->val4 == 1) - || (sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER)) - ) + // eA style: doesn't need this + if (battle_config.song_timer_reset) break; + // Don't let buff themselves! + if (sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER)) + break; + + // Aegis style + // Check if song has enough time to survive the next check + if (!(battle_config.song_timer_reset) && tsc && tsc->data[type] && tsc->data[type]->val4 == 1) { + const struct TimerData *td = timer->get(tsc->data[type]->timer); + if (DIFF_TICK32(td->tick, timer->gettick()) < sg->interval) { + // Update with new values as the current one will vanish + timer->delete(tsc->data[type]->timer, status->change_timer); + tsc->data[type]->timer = timer->add(tick+sg->limit, status->change_timer, bl->id, type); + tsc->data[type]->val1 = sg->skill_lv; + tsc->data[type]->val2 = sg->val1; + tsc->data[type]->val3 = sg->val2; + tsc->data[type]->val4 = 0; + } + break; // Had enough time or not, it now has. Exit + } + // Song was not active. Start it now sc_start4(ss, bl, type, 100, sg->skill_lv, sg->val1, sg->val2, 0, sg->limit); break; case UNT_TATAMIGAESHI: @@ -12742,8 +12793,9 @@ int skill_unit_onleft(uint16 skill_id, struct block_list *bl, int64 tick) { case DC_DONTFORGETME: case DC_FORTUNEKISS: case DC_SERVICEFORYOU: - if ((battle_config.song_timer_reset && sce) // athena style - || (!battle_config.song_timer_reset && sce && sce->val4 != 1) + + if ((battle_config.song_timer_reset && sce) // eAthena style: update everytime + || (!battle_config.song_timer_reset && sce && sce->val4 != 1) // Aegis style: update only when it was not a reduced effect ) { timer->delete(sce->timer, status->change_timer); //NOTE: It'd be nice if we could get the skill_lv for a more accurate extra time, but alas... @@ -14836,7 +14888,7 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 } if (sc->data[SC_MYSTICSCROLL]) VARCAST_REDUCTION(sc->data[SC_MYSTICSCROLL]->val1); - + // Fixed cast reduction bonuses if( sc->data[SC__LAZINESS] ) fixcast_r = max(fixcast_r, sc->data[SC__LAZINESS]->val2); @@ -18076,7 +18128,7 @@ int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick) return 0; else { int cursor; - /** somehow, the timer vanished. (bugreport:8367) **/ + /* somehow, the timer vanished. (bugreport:8367) */ ers_free(skill->cd_entry_ers, cd->entry[i]); cd->entry[i] = NULL; @@ -18788,7 +18840,7 @@ bool skill_parse_row_skilldb(char* split[], int columns, int current) { safestrncpy(skill->dbs->db[idx].name, trim(split[15]), sizeof(skill->dbs->db[idx].name)); safestrncpy(skill->dbs->db[idx].desc, trim(split[16]), sizeof(skill->dbs->db[idx].desc)); strdb_iput(skill->name2id_db, skill->dbs->db[idx].name, skill_id); - script->set_constant2(skill->dbs->db[idx].name,(int)skill_id,0); + script->set_constant2(skill->dbs->db[idx].name, (int)skill_id, false, false); return true; } diff --git a/src/map/status.c b/src/map/status.c index 5ada4ec51..26db1750b 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -862,38 +862,38 @@ void initChangeTables(void) { status->dbs->IconChangeTable[SC_M_LIFEPOTION] = SI_M_LIFEPOTION; status->dbs->IconChangeTable[SC_G_LIFEPOTION] = SI_G_LIFEPOTION; status->dbs->IconChangeTable[SC_MYSTICPOWDER] = SI_MYSTICPOWDER; - + // Eden Crystal Synthesis status->dbs->IconChangeTable[SC_QUEST_BUFF1] = SI_QUEST_BUFF1; status->dbs->IconChangeTable[SC_QUEST_BUFF2] = SI_QUEST_BUFF2; status->dbs->IconChangeTable[SC_QUEST_BUFF3] = SI_QUEST_BUFF3; - + // Geffen Magic Tournament status->dbs->IconChangeTable[SC_GEFFEN_MAGIC1] = SI_GEFFEN_MAGIC1; status->dbs->IconChangeTable[SC_GEFFEN_MAGIC2] = SI_GEFFEN_MAGIC2; status->dbs->IconChangeTable[SC_GEFFEN_MAGIC3] = SI_GEFFEN_MAGIC3; status->dbs->IconChangeTable[SC_FENRIR_CARD] = SI_FENRIR_CARD; - + // MVP Scrolls status->dbs->IconChangeTable[SC_MVPCARD_TAOGUNKA] = SI_MVPCARD_TAOGUNKA; status->dbs->IconChangeTable[SC_MVPCARD_MISTRESS] = SI_MVPCARD_MISTRESS; status->dbs->IconChangeTable[SC_MVPCARD_ORCHERO] = SI_MVPCARD_ORCHERO; status->dbs->IconChangeTable[SC_MVPCARD_ORCLORD] = SI_MVPCARD_ORCLORD; - + // Mercenary Bonus Effects status->dbs->IconChangeTable[SC_MER_FLEE] = SI_MER_FLEE; status->dbs->IconChangeTable[SC_MER_ATK] = SI_MER_ATK; status->dbs->IconChangeTable[SC_MER_HP] = SI_MER_HP; status->dbs->IconChangeTable[SC_MER_SP] = SI_MER_SP; status->dbs->IconChangeTable[SC_MER_HIT] = SI_MER_HIT; - + // Warlock Spheres status->dbs->IconChangeTable[SC_SUMMON1] = SI_SPHERE_1; status->dbs->IconChangeTable[SC_SUMMON2] = SI_SPHERE_2; status->dbs->IconChangeTable[SC_SUMMON3] = SI_SPHERE_3; status->dbs->IconChangeTable[SC_SUMMON4] = SI_SPHERE_4; status->dbs->IconChangeTable[SC_SUMMON5] = SI_SPHERE_5; - + // Warlock Preserved spells status->dbs->IconChangeTable[SC_SPELLBOOK1] = SI_SPELLBOOK1; status->dbs->IconChangeTable[SC_SPELLBOOK2] = SI_SPELLBOOK2; @@ -977,7 +977,7 @@ void initChangeTables(void) { status->dbs->IconChangeTable[SC_REBOUND] = SI_REBOUND; status->dbs->IconChangeTable[SC_ALL_RIDING] = SI_ALL_RIDING; status->dbs->IconChangeTable[SC_MONSTER_TRANSFORM] = SI_MONSTER_TRANSFORM; - + // Costumes status->dbs->IconChangeTable[SC_MOONSTAR] = SI_MOONSTAR; status->dbs->IconChangeTable[SC_SUPER_STAR] = SI_SUPER_STAR; @@ -993,7 +993,7 @@ void initChangeTables(void) { status->dbs->IconChangeTable[SC_TIME_ACCESSORY] = SI_TIME_ACCESSORY; status->dbs->IconChangeTable[SC_MAGICAL_FEATHER] = SI_MAGICAL_FEATHER; status->dbs->IconChangeTable[SC_BLOSSOM_FLUTTERING] = SI_BLOSSOM_FLUTTERING; - + // Other SC which are not necessarily associated to skills. status->dbs->ChangeFlagTable[SC_ATTHASTE_POTION1] |= SCB_ASPD; status->dbs->ChangeFlagTable[SC_ATTHASTE_POTION2] |= SCB_ASPD; @@ -1061,7 +1061,7 @@ void initChangeTables(void) { status->dbs->ChangeFlagTable[SC_PHI_DEMON] |= SCB_ALL; status->dbs->ChangeFlagTable[SC_MAGIC_CANDY] |= SCB_MATK | SCB_ALL; status->dbs->ChangeFlagTable[SC_MYSTICPOWDER] |= SCB_FLEE | SCB_LUK; - + // Cash Items status->dbs->ChangeFlagTable[SC_FOOD_STR_CASH] |= SCB_STR; status->dbs->ChangeFlagTable[SC_FOOD_AGI_CASH] |= SCB_AGI; @@ -1069,14 +1069,14 @@ void initChangeTables(void) { status->dbs->ChangeFlagTable[SC_FOOD_DEX_CASH] |= SCB_DEX; status->dbs->ChangeFlagTable[SC_FOOD_INT_CASH] |= SCB_INT; status->dbs->ChangeFlagTable[SC_FOOD_LUK_CASH] |= SCB_LUK; - + // Mercenary Bonus Effects status->dbs->ChangeFlagTable[SC_MER_FLEE] |= SCB_FLEE; status->dbs->ChangeFlagTable[SC_MER_ATK] |= SCB_WATK; status->dbs->ChangeFlagTable[SC_MER_HP] |= SCB_MAXHP; status->dbs->ChangeFlagTable[SC_MER_SP] |= SCB_MAXSP; status->dbs->ChangeFlagTable[SC_MER_HIT] |= SCB_HIT; - + // Guillotine Cross Poison Effects status->dbs->ChangeFlagTable[SC_PARALYSE] |= SCB_FLEE | SCB_SPEED | SCB_ASPD; status->dbs->ChangeFlagTable[SC_VENOMBLEED] |= SCB_MAXHP; @@ -1084,11 +1084,11 @@ void initChangeTables(void) { status->dbs->ChangeFlagTable[SC_DEATHHURT] |= SCB_REGEN; status->dbs->ChangeFlagTable[SC_PYREXIA] |= SCB_HIT | SCB_FLEE; status->dbs->ChangeFlagTable[SC_OBLIVIONCURSE] |= SCB_REGEN; - + // Royal Guard status status->dbs->ChangeFlagTable[SC_SHIELDSPELL_DEF] |= SCB_WATK; status->dbs->ChangeFlagTable[SC_SHIELDSPELL_REF] |= SCB_DEF; - + // Mechanic status status->dbs->ChangeFlagTable[SC_STEALTHFIELD_MASTER] |= SCB_SPEED; @@ -1116,7 +1116,7 @@ void initChangeTables(void) { status->dbs->ChangeFlagTable[SC_STOMACHACHE] |= SCB_STR | SCB_AGI | SCB_VIT | SCB_INT | SCB_DEX | SCB_LUK | SCB_SPEED; status->dbs->ChangeFlagTable[SC_PROMOTE_HEALTH_RESERCH] |= SCB_MAXHP | SCB_ALL; status->dbs->ChangeFlagTable[SC_ENERGY_DRINK_RESERCH] |= SCB_MAXSP | SCB_ALL; - + // Geffen Scrolls status->dbs->ChangeFlagTable[SC_SKELSCROLL] |= SCB_ALL; status->dbs->ChangeFlagTable[SC_DISTRUCTIONSCROLL] |= SCB_ALL; @@ -1135,24 +1135,24 @@ void initChangeTables(void) { status->dbs->ChangeFlagTable[SC_MTF_HITFLEE] |= SCB_HIT | SCB_FLEE; status->dbs->ChangeFlagTable[SC_MTF_MHP] |= SCB_MAXHP; status->dbs->ChangeFlagTable[SC_MTF_MSP] |= SCB_MAXSP; - + // Eden Crystal Synthesis status->dbs->ChangeFlagTable[SC_QUEST_BUFF1] |= SCB_BATK | SCB_MATK; status->dbs->ChangeFlagTable[SC_QUEST_BUFF2] |= SCB_BATK | SCB_MATK; status->dbs->ChangeFlagTable[SC_QUEST_BUFF3] |= SCB_BATK | SCB_MATK; - + // Geffen Magic Tournament status->dbs->ChangeFlagTable[SC_GEFFEN_MAGIC1] |= SCB_ALL; status->dbs->ChangeFlagTable[SC_GEFFEN_MAGIC2] |= SCB_ALL; status->dbs->ChangeFlagTable[SC_GEFFEN_MAGIC3] |= SCB_ALL; status->dbs->ChangeFlagTable[SC_FENRIR_CARD] |= SCB_MATK | SCB_ALL; - + // MVP Scrolls status->dbs->ChangeFlagTable[SC_MVPCARD_TAOGUNKA] |= SCB_MAXHP | SCB_DEF | SCB_MDEF; status->dbs->ChangeFlagTable[SC_MVPCARD_MISTRESS] |= SCB_ALL; status->dbs->ChangeFlagTable[SC_MVPCARD_ORCHERO] |= SCB_ALL; status->dbs->ChangeFlagTable[SC_MVPCARD_ORCLORD] |= SCB_ALL; - + // Costumes status->dbs->ChangeFlagTable[SC_MOONSTAR] |= SCB_NONE; status->dbs->ChangeFlagTable[SC_SUPER_STAR] |= SCB_NONE; @@ -1192,7 +1192,7 @@ void initChangeTables(void) { status->dbs->DisplayType[SC_BLOOD_SUCKER] = true; status->dbs->DisplayType[SC__SHADOWFORM] = true; status->dbs->DisplayType[SC_MONSTER_TRANSFORM] = true; - + // Costumes status->dbs->DisplayType[SC_MOONSTAR] = true; status->dbs->DisplayType[SC_SUPER_STAR] = true; @@ -2500,7 +2500,7 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) { } else if(sd->inventory_data[index]->type == IT_ARMOR) { int r = sd->status.inventory[index].refine; - + if ( (!battle_config.costume_refine_def && itemdb_is_costumeequip(sd->inventory_data[index]->equip)) || (!battle_config.shadow_refine_def && itemdb_is_shadowequip(sd->inventory_data[index]->equip)) ) @@ -3110,7 +3110,7 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) { sd->magic_addele[ELE_WIND] += 25; if (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3) sd->magic_addele[ELE_EARTH] += 25; - + // Geffen Scrolls if (sc->data[SC_SKELSCROLL]) { #ifdef RENEWAL @@ -3136,11 +3136,11 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) { } if (sc->data[SC_IMMUNITYSCROLL]) sd->subele[ELE_NEUTRAL] += sc->data[SC_IMMUNITYSCROLL]->val1; - + // Geffen Magic Tournament if (sc->data[SC_GEFFEN_MAGIC1]) { sd->right_weapon.addrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC1]->val1; - sd->left_weapon.addrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC1]->val1; + sd->left_weapon.addrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC1]->val1; } if (sc->data[SC_GEFFEN_MAGIC2]) sd->magic_addrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC2]->val1; @@ -4194,10 +4194,14 @@ int status_base_amotion_pc(struct map_session_data *sd, struct status_data *st) if ( sd->status.shield ) amotion += status->dbs->aspd_base[pc->class2idx(sd->status.class_)][MAX_WEAPON_TYPE]; switch ( sd->status.weapon ) { - case W_BOW: case W_MUSICAL: - case W_WHIP: case W_REVOLVER: - case W_RIFLE: case W_GATLING: - case W_SHOTGUN: case W_GRENADE: + case W_BOW: + case W_MUSICAL: + case W_WHIP: + case W_REVOLVER: + case W_RIFLE: + case W_GATLING: + case W_SHOTGUN: + case W_GRENADE: temp = st->dex * st->dex / 7.0f + st->agi * st->agi * 0.5f; break; default: @@ -4275,12 +4279,14 @@ unsigned short status_base_atk(const struct block_list *bl, const struct status_ str = (int)(dstr + (float)dex / 5 + (float)st->luk / 3 + (float)BL_UCCAST(BL_PC, bl)->status.base_level / 4); else if (bl->type == BL_MOB) str = dstr + BL_UCCAST(BL_MOB, bl)->level; - //else if (bl->type == BL_MER) // FIXME: What should go here? - // str = dstr + BL_UCCAST(BL_MER, bl)->level; -#else +#if 0 + else if (bl->type == BL_MER) // FIXME: What should go here? + str = dstr + BL_UCCAST(BL_MER, bl)->level; +#endif // 0 +#else // ! RENEWAL if (bl->type == BL_PC) str += dex / 5 + st->luk / 5; -#endif +#endif // RENEWAL return cap_value(str, 0, USHRT_MAX); } @@ -4817,7 +4823,7 @@ unsigned short status_calc_batk(struct block_list *bl, struct status_change *sc, batk += 100 * sc->data[SC_SATURDAY_NIGHT_FEVER]->val1; if (sc->data[SC_BATTLESCROLL]) batk += batk * sc->data[SC_BATTLESCROLL]->val1 / 100; - + // Eden Crystal Synthesis if (sc->data[SC_QUEST_BUFF1]) batk += sc->data[SC_QUEST_BUFF1]->val1; @@ -5002,7 +5008,7 @@ unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc, matk += sc->data[SC_MTF_MATK]->val1; if (sc->data[SC_MYSTICSCROLL]) matk += matk * sc->data[SC_MYSTICSCROLL]->val1 / 100; - + // Eden Crystal Synthesis if (sc->data[SC_QUEST_BUFF1]) matk += sc->data[SC_QUEST_BUFF1]->val1; @@ -5010,7 +5016,7 @@ unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc, matk += sc->data[SC_QUEST_BUFF2]->val1; if (sc->data[SC_QUEST_BUFF3]) matk += sc->data[SC_QUEST_BUFF3]->val1; - + // Geffen Magic Tournament if (sc->data[SC_FENRIR_CARD]) matk += sc->data[SC_FENRIR_CARD]->val1; @@ -5119,7 +5125,7 @@ signed short status_calc_hit(struct block_list *bl, struct status_change *sc, in hit += sc->data[SC_ACARAJE]->val1; if (sc->data[SC_BUCHEDENOEL]) hit += sc->data[SC_BUCHEDENOEL]->val3; - + return (short)cap_value(hit, 1, SHRT_MAX); } @@ -5540,8 +5546,10 @@ unsigned short status_calc_speed(struct block_list *bl, struct status_change *sc if(sc->data[SC_FUSION]) { val = 25; } else if (sd) { - if (pc_isridingpeco(sd) || pc_isridingdragon(sd) || sd->sc.data[SC_ALL_RIDING]) + if (pc_isridingpeco(sd) || pc_isridingdragon(sd)) val = 25;//Same bonus + else if (sd->sc.data[SC_ALL_RIDING]) + val = sd->sc.data[SC_ALL_RIDING]->val1; else if (pc_isridingwug(sd)) val = 15 + 5 * pc->checkskill(sd, RA_WUGRIDER); else if (pc_ismadogear(sd)) { @@ -5620,10 +5628,8 @@ unsigned short status_calc_speed(struct block_list *bl, struct status_change *sc val = max( val, sc->data[SC_MELON_BOMB]->val1 ); if (sc->data[SC_STOMACHACHE]) val = max(val, sc->data[SC_STOMACHACHE]->val2); - - if( sc->data[SC_MARSHOFABYSS] ) // It stacks to other statuses so always put this at the end. - val = max( 50, val + 10 * sc->data[SC_MARSHOFABYSS]->val1 ); - + if (sc->data[SC_MARSHOFABYSS]) // It stacks to other statuses so always put this at the end. + val = max(50, val + 10 * sc->data[SC_MARSHOFABYSS]->val1); if (sc->data[SC_MOVHASTE_POTION]) { // Doesn't affect the movement speed by Quagmire, Decrease Agi, Slow Grace [Frost] if (sc->data[SC_DEC_AGI] || sc->data[SC_QUAGMIRE] || sc->data[SC_DONTFORGETME]) return 0; @@ -8981,8 +8987,8 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t tick_time = 3000; // [GodLesZ] tick time break; case SC_CLOAKINGEXCEED: - val2 = ( val1 + 1 ) / 2; // Hits - val3 = 90 + val1 * 10; // Walk speed + val2 = (val1 + 1) / 2; // Hits + val3 = (val1 - 1) * 10; // Walk speed if (bl->type == BL_PC) val4 |= battle_config.pc_cloak_check_type&7; else @@ -12818,13 +12824,13 @@ bool status_readdb_sizefix(char* fields[], int columns, int current) /** * Processes a refine_db.conf entry. * - * @param *r Libconfig setting entry. It is expected to be valid and it - * won't be freed (it is care of the caller to do so if - * necessary) - * @param n Ordinal number of the entry, to be displayed in case of - * validation errors. - * @param *source Source of the entry (file name), to be displayed in case of - * validation errors. + * @param r Libconfig setting entry. It is expected to be valid and it + * won't be freed (it is care of the caller to do so if + * necessary) + * @param n Ordinal number of the entry, to be displayed in case of + * validation errors. + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. * @return # of the validated entry, or 0 in case of failure. */ int status_readdb_refine_libconfig_sub(config_setting_t *r, const char *name, const char *source) @@ -12835,7 +12841,7 @@ int status_readdb_refine_libconfig_sub(config_setting_t *r, const char *name, co nullpo_ret(r); nullpo_ret(name); nullpo_ret(source); - + if (strncmp(name, "Armors", 6) == 0) { type = REFINE_TYPE_ARMOR; } else if (strncmp(name, "WeaponLevel", 11) != 0 || !strspn(&name[strlen(name)-1], "0123456789") || (type = atoi(strncpy(lv, name+11, 2))) == REFINE_TYPE_ARMOR) { @@ -12924,14 +12930,14 @@ int status_readdb_refine_libconfig(const char *filename) { config_setting_t *r; char filepath[256]; int i = 0, count = 0,type = 0; - + sprintf(filepath, "%s/%s", map->db_path, filename); memset(&duplicate,0,sizeof(duplicate)); if( libconfig->read_file(&refine_db_conf, filepath) ) { ShowError("can't read %s\n", filepath); return 0; } - + while((r = libconfig->setting_get_elem(refine_db_conf.root,i++))) { char *name = config_setting_name(r); if((type=status->readdb_refine_libconfig_sub(r, name, filename))) { @@ -12943,7 +12949,7 @@ int status_readdb_refine_libconfig(const char *filename) { } libconfig->destroy(&refine_db_conf); ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename); - + return count; } diff --git a/src/map/status.h b/src/map/status.h index 51ca1e78b..be6d4c209 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -758,13 +758,13 @@ typedef enum sc_type { SC_MTF_MSP, SC_MTF_PUMPKIN, SC_MTF_HITFLEE, - + SC_LJOSALFAR, SC_MERMAID_LONGING, - + SC_ACARAJE, // 590 SC_TARGET_ASPD, - + // Geffen Scrolls SC_SKELSCROLL, SC_DISTRUCTIONSCROLL, @@ -775,25 +775,25 @@ typedef enum sc_type { SC_ARMORSCROLL, SC_FREYJASCROLL, SC_SOULSCROLL, // 600 - + // Eden Crystal Synthesis SC_QUEST_BUFF1, SC_QUEST_BUFF2, SC_QUEST_BUFF3, - + // Geffen Magic Tournament SC_GEFFEN_MAGIC1, SC_GEFFEN_MAGIC2, SC_GEFFEN_MAGIC3, SC_FENRIR_CARD, - + SC_ATKER_ASPD, SC_ATKER_MOVESPEED, SC_FOOD_CRITICALSUCCESSVALUE, // 610 SC_CUP_OF_BOZA, SC_OVERLAPEXPUP, SC_MORA_BUFF, - + // MVP Scrolls SC_MVPCARD_TAOGUNKA, SC_MVPCARD_MISTRESS, diff --git a/src/map/unit.c b/src/map/unit.c index 03334f7f3..bea0913d2 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -121,7 +121,7 @@ int unit_walktoxy_sub(struct block_list *bl) #ifdef OFFICIAL_WALKPATH if( !path->search_long(NULL, bl, bl->m, bl->x, bl->y, ud->to_x, ud->to_y, CELL_CHKNOPASS) // Check if there is an obstacle between - && wpd.path_len > 14 // Official number of walkable cells is 14 if and only if there is an obstacle between. [malufett] + && wpd.path_len > 14 // Official number of walkable cells is 14 if and only if there is an obstacle between. [malufett] && (bl->type != BL_NPC) ) // If type is a NPC, please disregard. return 0; #endif @@ -1976,6 +1976,7 @@ 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; + struct walkpath_data wpd; nullpo_retr(false, bl); nullpo_retr(false, tbl); @@ -2005,7 +2006,20 @@ bool unit_can_reach_bl(struct block_list *bl,struct block_list *tbl, int range, if (x) *x = tbl->x-dx; if (y) *y = tbl->y-dy; - return path->search(NULL,bl,bl->m,bl->x,bl->y,tbl->x-dx,tbl->y-dy,easy,CELL_CHKNOREACH); + + if (!path->search(&wpd,bl,bl->m,bl->x,bl->y,tbl->x-dx,tbl->y-dy,easy,CELL_CHKNOREACH)) + return false; + +#ifdef OFFICIAL_WALKPATH + if( !path->search_long(NULL, bl, bl->m, bl->x, bl->y, tbl->x-dx, tbl->y-dy, CELL_CHKNOPASS) // Check if there is an obstacle between + && wpd.path_len > 14 // Official number of walkable cells is 14 if and only if there is an obstacle between. [malufett] + && (bl->type != BL_NPC) ) // If type is a NPC, please disregard. + return false; +#endif + + return true; + + } /*========================================== * Calculates position of Pet/Mercenary/Homunculus/Elemental diff --git a/src/map/vending.c b/src/map/vending.c index 810e6b07a..6e74e6c3e 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -89,7 +89,7 @@ void vending_vendinglistreq(struct map_session_data* sd, unsigned int id) { *------------------------------------------*/ void vending_purchasereq(struct map_session_data* sd, int aid, unsigned int uid, const uint8* data, int count) { int i, j, cursor, w, new_ = 0, blank, vend_list[MAX_VENDING]; - double z; + int64 z; struct s_vending vend[MAX_VENDING]; // against duplicate packets struct map_session_data* vsd = map->id2sd(aid); @@ -116,7 +116,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, unsigned int uid, memcpy(&vend, &vsd->vending, sizeof(vsd->vending)); // copy vending list // some checks - z = 0.; // zeny counter + z = 0; // zeny counter w = 0; // weight counter for( i = 0; i < count; i++ ) { short amount = *(uint16*)(data + 4*i + 0); @@ -136,12 +136,12 @@ void vending_purchasereq(struct map_session_data* sd, int aid, unsigned int uid, else vend_list[i] = j; - z += ((double)vsd->vending[j].value * (double)amount); - if( z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY ) { + z += (int64)vsd->vending[j].value * amount; + if (z > sd->status.zeny || z < 0 || z > MAX_ZENY) { clif->buyvending(sd, idx, amount, 1); // you don't have enough zeny return; } - if( z + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max ) { + if (z > MAX_ZENY - vsd->status.zeny && !battle_config.vending_over_max) { clif->buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow return; @@ -181,7 +181,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, unsigned int uid, pc->payzeny(sd, (int)z, LOG_TYPE_VENDING, vsd); if( battle_config.vending_tax ) - z -= z * (battle_config.vending_tax/10000.); + z -= apply_percentrate64(z, battle_config.vending_tax, 10000); pc->getzeny(vsd, (int)z, LOG_TYPE_VENDING, sd); for( i = 0; i < count; i++ ) { |