diff options
Diffstat (limited to 'src/map/npc.c')
-rw-r--r-- | src/map/npc.c | 628 |
1 files changed, 385 insertions, 243 deletions
diff --git a/src/map/npc.c b/src/map/npc.c index 23b0b9555..2876ea595 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -47,6 +47,7 @@ #include "common/nullpo.h" #include "common/showmsg.h" #include "common/socket.h" +#include "common/sql.h" #include "common/strlib.h" #include "common/timer.h" #include "common/utils.h" @@ -61,17 +62,6 @@ struct npc_interface npc_s; struct npc_interface *npc; -static int npc_id=START_NPC_NUM; -static int npc_warp=0; -static int npc_shop=0; -static int npc_script=0; -static int npc_mob=0; -static int npc_delay_mob=0; -static int npc_cache_mob=0; - -static const char *npc_last_path; -static const char *npc_last_ref; -struct npc_path_data *npc_last_npd; //For holding the view data of npc classes. [Skotlex] static struct view_data npc_viewdb[MAX_NPC_CLASS]; @@ -131,15 +121,15 @@ bool npc_db_checkid(int id) /// Returns a new npc id that isn't being used in id_db. /// Fatal error if nothing is available. int npc_get_new_npc_id(void) { - if( npc_id >= START_NPC_NUM && !map->blid_exists(npc_id) ) - return npc_id++;// available + if (npc->npc_id >= START_NPC_NUM && !map->blid_exists(npc->npc_id)) + return npc->npc_id++;// available else {// find next id - int base_id = npc_id; - while( base_id != ++npc_id ) { - if( npc_id < START_NPC_NUM ) - npc_id = START_NPC_NUM; - if( !map->blid_exists(npc_id) ) - return npc_id++;// available + int base_id = npc->npc_id; + while (base_id != ++npc->npc_id) { + if (npc->npc_id < START_NPC_NUM) + npc->npc_id = START_NPC_NUM; + if (!map->blid_exists(npc->npc_id)) + return npc->npc_id++;// available } // full loop, nothing available ShowFatalError("npc_get_new_npc_id: All ids are taken. Exiting..."); @@ -176,6 +166,7 @@ int npc_ontouch_event(struct map_session_data *sd, struct npc_data *nd) { char name[EVENT_NAME_LENGTH]; + nullpo_retr(1, nd); if( nd->touching_id ) return 0; // Attached a player already. Can't trigger on anyone else. @@ -190,6 +181,8 @@ int npc_ontouch2_event(struct map_session_data *sd, struct npc_data *nd) { char name[EVENT_NAME_LENGTH]; + nullpo_retr(1, sd); + nullpo_retr(1, nd); if (sd->areanpc_id == nd->bl.id) return 0; @@ -201,6 +194,8 @@ int npc_onuntouch_event(struct map_session_data *sd, struct npc_data *nd) { char name[EVENT_NAME_LENGTH]; + nullpo_ret(sd); + nullpo_ret(nd); if (sd->areanpc_id != nd->bl.id) return 0; @@ -366,7 +361,7 @@ int npc_event_dequeue(struct map_session_data* sd) /** * @see DBCreateData */ -DBData npc_event_export_create(DBKey key, va_list args) +struct DBData npc_event_export_create(union DBKey key, va_list args) { struct linkdb_node** head_ptr; CREATE(head_ptr, struct linkdb_node*, 1); @@ -380,8 +375,12 @@ DBData npc_event_export_create(DBKey key, va_list args) *------------------------------------------*/ int npc_event_export(struct npc_data *nd, int i) { - char* lname = nd->u.scr.label_list[i].name; - int pos = nd->u.scr.label_list[i].pos; + char* lname; + int pos; + nullpo_ret(nd); + Assert_ret(i >= 0 && i < nd->u.scr.label_list_num); + lname = nd->u.scr.label_list[i].name; + pos = nd->u.scr.label_list[i].pos; if ((lname[0] == 'O' || lname[0] == 'o') && (lname[1] == 'N' || lname[1] == 'n')) { struct event_data *ev; struct linkdb_node **label_linkdb = NULL; @@ -434,6 +433,7 @@ void npc_event_doall_sub(void *key, void *data, va_list ap) // runs the specified event (supports both single-npc and global events) int npc_event_do(const char* name) { + nullpo_ret(name); if( name[0] == ':' && name[1] == ':' ) { return npc->event_doall(name+2); // skip leading "::" } @@ -538,9 +538,12 @@ void npc_event_do_oninit( bool reload ) int npc_timerevent_export(struct npc_data *nd, int i) { int t = 0, len = 0; - char *lname = nd->u.scr.label_list[i].name; - int pos = nd->u.scr.label_list[i].pos; - if (sscanf(lname, "OnTimer%d%n", &t, &len) == 1 && lname[len] == '\0') { + char *lname; + int pos; + nullpo_ret(nd); + lname = nd->u.scr.label_list[i].name; + pos = nd->u.scr.label_list[i].pos; + if (sscanf(lname, "OnTimer%d%n", &t, &len) == 1 && len < NAME_LENGTH && lname[len] == '\0') { // Timer event struct npc_timerevent_list *te = nd->u.scr.timer_event; int j, k = nd->u.scr.timeramount; @@ -580,6 +583,8 @@ int npc_timerevent(int tid, int64 tick, int id, intptr_t data) struct timer_event_data *ted = (struct timer_event_data*)data; struct map_session_data *sd=NULL; + nullpo_ret(ted); + if( nd == NULL ) { ShowError("npc_timerevent: NPC not found??\n"); return 0; @@ -732,6 +737,7 @@ void npc_timerevent_quit(struct map_session_data* sd) struct npc_data* nd; struct timer_event_data *ted; + nullpo_retv(sd); // Check timer existence if( sd->npc_timer_id == INVALID_TIMER ) return; @@ -763,6 +769,7 @@ void npc_timerevent_quit(struct map_session_data* sd) { int old_rid,old_timer; int64 old_tick; + nullpo_retv(ted); //Set timer related info. old_rid = (nd->u.scr.rid == sd->bl.id ? 0 : nd->u.scr.rid); // Detach rid if the last attached player logged off. @@ -831,6 +838,8 @@ int npc_settimerevent_tick(struct npc_data* nd, int newtimer) int npc_event_sub(struct map_session_data* sd, struct event_data* ev, const char* eventname) { + nullpo_retr(2, sd); + nullpo_retr(2, eventname); if ( sd->npc_id != 0 ) { //Enqueue the event trigger. @@ -915,9 +924,11 @@ int npc_touch_areanpc_sub(struct block_list *bl, va_list ap) { * If not, it unsets it and searches for another player in range. *------------------------------------------*/ int npc_touchnext_areanpc(struct map_session_data* sd, bool leavemap) { - struct npc_data *nd = map->id2nd(sd->touching_id); + struct npc_data *nd; short xs, ys; + nullpo_retr(1, sd); + nd = map->id2nd(sd->touching_id); if( !nd || nd->touching_id != sd->bl.id ) return 1; @@ -949,7 +960,7 @@ int npc_touch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y) int j, found_warp = 0; nullpo_retr(1, sd); - + Assert_retr(1, m >= 0 && m < map->count); #if 0 // Why not enqueue it? [Inkfish] if(sd->npc_id) return 1; @@ -1034,6 +1045,7 @@ int npc_untouch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y) { struct npc_data *nd = NULL; nullpo_retr(1, sd); + Assert_retr(1, m >= 0 && m < map->count); if (!sd->areanpc_id) return 0; @@ -1053,11 +1065,16 @@ int npc_untouch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y) // Return 1 if Warped int npc_touch_areanpc2(struct mob_data *md) { - int i, m = md->bl.m, x = md->bl.x, y = md->bl.y, id; + int i, m, x, y, id; char eventname[EVENT_NAME_LENGTH]; struct event_data* ev; int xs, ys; + nullpo_ret(md); + m = md->bl.m; + x = md->bl.x; + y = md->bl.y; + for( i = 0; i < map->list[m].npc_num; i++ ) { if( map->list[m].npc[i]->option&OPTION_INVISIBLE ) continue; @@ -1116,6 +1133,8 @@ int npc_check_areanpc(int flag, int16 m, int16 x, int16 y, int16 range) { int x0,y0,x1,y1; int xs,ys; + Assert_retr(1, m >= 0 && m < map->count); + if (range < 0) return 0; x0 = max(x-range, 0); y0 = max(y-range, 0); @@ -1187,6 +1206,7 @@ struct npc_data* npc_checknear(struct map_session_data* sd, struct block_list* b if (distance > nd->area_size) distance = nd->area_size; + nullpo_retr(NULL, bl); if (bl->m != sd->bl.m || bl->x < sd->bl.x - distance || bl->x > sd->bl.x + distance || bl->y < sd->bl.y - distance || bl->y > sd->bl.y + distance) @@ -1208,6 +1228,9 @@ int npc_globalmessage(const char* name, const char* mes) if (!nd) return 0; + nullpo_ret(name); + nullpo_ret(mes); + snprintf(temp, sizeof(temp), "%s : %s", name, mes); clif->GlobalMessage(&nd->bl,temp); @@ -1219,6 +1242,7 @@ void run_tomb(struct map_session_data* sd, struct npc_data* nd) { char buffer[200]; char time[10]; + nullpo_retv(nd); strftime(time, sizeof(time), "%H:%M", localtime(&nd->u.tomb.kill_time)); // TODO: Find exact color? @@ -1386,16 +1410,19 @@ int npc_buysellsel(struct map_session_data* sd, int id, int type) { /*========================================== * Cash Shop Buy List *------------------------------------------*/ -int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, unsigned short* item_list) { - int i, j, nameid, amount, new_, w, vt; +int npc_cashshop_buylist(struct map_session_data *sd, int points, struct itemlist *item_list) +{ + int i, j, new_, w, vt; struct npc_data *nd = NULL; struct npc_item_list *shop = NULL; unsigned short shop_size = 0; + nullpo_retr(ERROR_TYPE_SYSTEM, sd); + nullpo_retr(ERROR_TYPE_SYSTEM, item_list); if( sd->state.trading ) return ERROR_TYPE_EXCHANGE; - if( count <= 0 ) + if (VECTOR_LENGTH(*item_list) <= 0) return ERROR_TYPE_ITEM_ID; if( points < 0 ) @@ -1421,24 +1448,23 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, uns vt = 0; // Global Value // Validating Process ---------------------------------------------------- - for( i = 0; i < count; i++ ) { - nameid = item_list[i*2+1]; - amount = item_list[i*2+0]; + for (i = 0; i < VECTOR_LENGTH(*item_list); i++) { + struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); - if( !itemdb->exists(nameid) || amount <= 0 ) + if (!itemdb->exists(entry->id) || entry->amount <= 0) return ERROR_TYPE_ITEM_ID; - ARR_FIND(0,shop_size,j,shop[j].nameid == nameid); - if( j == shop_size || shop[j].value <= 0 ) + ARR_FIND(0,shop_size,j,shop[j].nameid == entry->id); + if (j == shop_size || shop[j].value <= 0) return ERROR_TYPE_ITEM_ID; - if( !itemdb->isstackable(nameid) && amount > 1 ) { + if (!itemdb->isstackable(entry->id) && entry->amount > 1) { ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of non-stackable item %d!\n", - sd->status.name, sd->status.account_id, sd->status.char_id, amount, nameid); - amount = item_list[i*2+0] = 1; + sd->status.name, sd->status.account_id, sd->status.char_id, entry->amount, entry->id); + entry->amount = 1; } - switch( pc->checkadditem(sd,nameid,amount) ) { + switch (pc->checkadditem(sd, entry->id, entry->amount)) { case ADDITEM_NEW: new_++; break; @@ -1446,8 +1472,8 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, uns return ERROR_TYPE_INVENTORY_WEIGHT; } - vt += shop[j].value * amount; - w += itemdb_weight(nameid) * amount; + vt += shop[j].value * entry->amount; + w += itemdb_weight(entry->id) * entry->amount; } if( w + sd->weight > sd->max_weight ) @@ -1468,18 +1494,16 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, uns pc->paycash(sd,vt,points); } // Delivery Process ---------------------------------------------------- - for( i = 0; i < count; i++ ) { + for (i = 0; i < VECTOR_LENGTH(*item_list); i++) { + struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); struct item item_tmp; - nameid = item_list[i*2+1]; - amount = item_list[i*2+0]; - memset(&item_tmp,0,sizeof(item_tmp)); - if( !pet->create_egg(sd,nameid) ) { - item_tmp.nameid = nameid; + if (!pet->create_egg(sd, entry->id)) { + item_tmp.nameid = entry->id; item_tmp.identify = 1; - pc->additem(sd,&item_tmp,amount,LOG_TYPE_NPC); + pc->additem(sd, &item_tmp, entry->amount, LOG_TYPE_NPC); } } @@ -1487,21 +1511,27 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, uns } //npc_buylist for script-controlled shops. -int npc_buylist_sub(struct map_session_data* sd, int n, unsigned short* item_list, struct npc_data* nd) +int npc_buylist_sub(struct map_session_data *sd, struct itemlist *item_list, struct npc_data *nd) { char npc_ev[EVENT_NAME_LENGTH]; int i; int key_nameid = 0; int key_amount = 0; + nullpo_ret(item_list); + nullpo_ret(nd); + // discard old contents script->cleararray_pc(sd, "@bought_nameid", (void*)0); script->cleararray_pc(sd, "@bought_quantity", (void*)0); // save list of bought items - for( i = 0; i < n; i++ ) { - script->setarray_pc(sd, "@bought_nameid", i, (void*)(intptr_t)item_list[i*2+1], &key_nameid); - script->setarray_pc(sd, "@bought_quantity", i, (void*)(intptr_t)item_list[i*2], &key_amount); + for (i = 0; i < VECTOR_LENGTH(*item_list); i++) { + struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); + intptr_t nameid = entry->id; + intptr_t amount = entry->amount; + script->setarray_pc(sd, "@bought_nameid", i, (void *)nameid, &key_nameid); + script->setarray_pc(sd, "@bought_quantity", i, (void *)amount, &key_amount); } // invoke event @@ -1513,8 +1543,9 @@ int npc_buylist_sub(struct map_session_data* sd, int n, unsigned short* item_lis /** * Loads persistent NPC Market Data from SQL **/ -void npc_market_fromsql(void) { - SqlStmt* stmt = SQL->StmtMalloc(map->mysql_handle); +void npc_market_fromsql(void) +{ + struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle); char name[NAME_LENGTH+1]; int itemid; int amount; @@ -1564,8 +1595,10 @@ void npc_market_fromsql(void) { * Saves persistent NPC Market Data into SQL **/ void npc_market_tosql(struct npc_data *nd, unsigned short index) { - if( SQL_ERROR == SQL->Query(map->mysql_handle, "REPLACE INTO `%s` VALUES ('%s','%d','%d')", - map->npc_market_data_db, nd->exname, nd->u.scr.shop->item[index].nameid, nd->u.scr.shop->item[index].qty) ) + nullpo_retv(nd); + Assert_retv(index < nd->u.scr.shop->items); + if (SQL_ERROR == SQL->Query(map->mysql_handle, "REPLACE INTO `%s` VALUES ('%s','%d','%u')", + map->npc_market_data_db, nd->exname, nd->u.scr.shop->item[index].nameid, nd->u.scr.shop->item[index].qty)) Sql_ShowDebug(map->mysql_handle); } /** @@ -1585,12 +1618,16 @@ void npc_market_delfromsql_sub(const char *npcname, unsigned short index) { * Removes persistent NPC Market Data from SQL **/ void npc_market_delfromsql(struct npc_data *nd, unsigned short index) { + nullpo_retv(nd); + Assert_retv(index == USHRT_MAX || index < nd->u.scr.shop->items); npc->market_delfromsql_sub(nd->exname, index == USHRT_MAX ? index : nd->u.scr.shop->item[index].nameid); } /** * Judges whether to allow and spawn a trader's window. **/ bool npc_trader_open(struct map_session_data *sd, struct npc_data *nd) { + nullpo_retr(false, sd); + nullpo_retr(false, nd); if( !nd->u.scr.shop || !nd->u.scr.shop->items ) return false; @@ -1629,7 +1666,7 @@ bool npc_trader_open(struct map_session_data *sd, struct npc_data *nd) { * @param master id of the original npc **/ void npc_trader_update(int master) { - DBIterator* iter; + struct DBIterator *iter; struct block_list* bl; struct npc_data *master_nd = map->id2nd(master); @@ -1656,6 +1693,9 @@ void npc_trader_count_funds(struct npc_data *nd, struct map_session_data *sd) { char evname[EVENT_NAME_LENGTH]; struct event_data *ev = NULL; + nullpo_retv(nd); + nullpo_retv(sd); + npc->trader_funds[0] = npc->trader_funds[1] = 0;/* clear */ switch( nd->u.scr.shop->type ) { @@ -1693,6 +1733,8 @@ bool npc_trader_pay(struct npc_data *nd, struct map_session_data *sd, int price, char evname[EVENT_NAME_LENGTH]; struct event_data *ev = NULL; + nullpo_retr(false, nd); + nullpo_retr(false, sd); npc->trader_ok = false;/* clear */ snprintf(evname, EVENT_NAME_LENGTH, "%s::OnPayFunds",nd->exname); @@ -1715,6 +1757,7 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po int i, price, w; unsigned short shop_size = 0; + nullpo_retr(ERROR_TYPE_SYSTEM, sd); if( amount <= 0 ) return ERROR_TYPE_ITEM_ID; @@ -1771,7 +1814,7 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po 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", + ShowDebug("(NPC:'%s' (%s,%d,%d), player:'%s' (%d/%d), value:%u, amount:%d)\n", nd->exname, map->list[nd->bl.m].name, nd->bl.x, nd->bl.y, sd->status.name, sd->status.account_id, sd->status.char_id, shop[i].value, amount); @@ -1805,11 +1848,15 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po return ERROR_TYPE_NONE; } -/// Player item purchase from npc shop. -/// -/// @param item_list 'n' pairs <amount,itemid> -/// @return result code for clif->parse_NpcBuyListSend -int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) { +/** + * Processes a player item purchase from npc shop. + * + * @param sd Buyer character. + * @param item_list List of items. + * @return result code for clif->parse_NpcBuyListSend. + */ +int npc_buylist(struct map_session_data *sd, struct itemlist *item_list) +{ struct npc_data* nd; struct npc_item_list *shop = NULL; int64 z; @@ -1838,30 +1885,29 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) { w = 0; new_ = 0; // process entries in buy list, one by one - for( i = 0; i < n; ++i ) { - int nameid, amount, value; + for (i = 0; i < VECTOR_LENGTH(*item_list); ++i) { + int value; + struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); // find this entry in the shop's sell list ARR_FIND( 0, shop_size, j, - item_list[i*2+1] == shop[j].nameid || //Normal items - item_list[i*2+1] == itemdb_viewid(shop[j].nameid) //item_avail replacement + entry->id == shop[j].nameid || //Normal items + entry->id == itemdb_viewid(shop[j].nameid) //item_avail replacement ); - - if( j == shop_size ) + if (j == shop_size) return 3; // no such item in shop - amount = item_list[i*2+0]; - nameid = item_list[i*2+1] = shop[j].nameid; //item_avail replacement + entry->id = shop[j].nameid; //item_avail replacement value = shop[j].value; - if( !itemdb->exists(nameid) ) + if (!itemdb->exists(entry->id)) return 3; // item no longer in itemdb - if( !itemdb->isstackable(nameid) && amount > 1 ) { + if (!itemdb->isstackable(entry->id) && entry->amount > 1) { //Exploit? You can't buy more than 1 of equipment types o.O ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of non-stackable item %d!\n", - sd->status.name, sd->status.account_id, sd->status.char_id, amount, nameid); - amount = item_list[i*2+0] = 1; + sd->status.name, sd->status.account_id, sd->status.char_id, entry->amount, entry->id); + entry->amount = 1; } if( nd->master_nd ) { @@ -1869,7 +1915,7 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) { continue; } - switch( pc->checkadditem(sd,nameid,amount) ) { + switch (pc->checkadditem(sd, entry->id, entry->amount)) { case ADDITEM_EXIST: break; @@ -1883,12 +1929,12 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) { value = pc->modifybuyvalue(sd,value); - z += (int64)value * amount; - w += itemdb_weight(nameid) * amount; + z += (int64)value * entry->amount; + w += itemdb_weight(entry->id) * entry->amount; } - if( nd->master_nd != NULL ) //Script-based shops. - return npc->buylist_sub(sd,n,item_list,nd->master_nd); + if (nd->master_nd != NULL) //Script-based shops. + return npc->buylist_sub(sd, item_list, nd->master_nd); if (z > sd->status.zeny) return 1; // Not enough Zeny @@ -1899,19 +1945,17 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) { pc->payzeny(sd, (int)z, LOG_TYPE_NPC, NULL); - for( i = 0; i < n; ++i ) { - int nameid = item_list[i*2+1]; - int amount = item_list[i*2+0]; - - if (itemdb_type(nameid) == IT_PETEGG) { - pet->create_egg(sd, nameid); + for (i = 0; i < VECTOR_LENGTH(*item_list); ++i) { + struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); + if (itemdb_type(entry->id) == IT_PETEGG) { + pet->create_egg(sd, entry->id); } else { struct item item_tmp; memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid = nameid; + item_tmp.nameid = entry->id; item_tmp.identify = 1; - pc->additem(sd,&item_tmp,amount,LOG_TYPE_NPC); + pc->additem(sd, &item_tmp, entry->amount, LOG_TYPE_NPC); } } @@ -1932,9 +1976,10 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) { } /** - * parses incoming npc market purchase list + * Processes incoming npc market purchase list **/ -int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, struct packet_npc_market_purchase *p) { +int npc_market_buylist(struct map_session_data *sd, struct itemlist *item_list) +{ struct npc_data* nd; struct npc_item_list *shop = NULL; int64 z; @@ -1942,11 +1987,11 @@ int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, st unsigned short shop_size = 0; nullpo_retr(1, sd); - nullpo_retr(1, p); + nullpo_retr(1, item_list); nd = npc->checknear(sd,map->id2bl(sd->npc_shopid)); - if( nd == NULL || nd->subtype != SCRIPT || !list_size || !nd->u.scr.shop || nd->u.scr.shop->type != NST_MARKET ) + if (nd == NULL || nd->subtype != SCRIPT || VECTOR_LENGTH(*item_list) == 0 || !nd->u.scr.shop || nd->u.scr.shop->type != NST_MARKET) return 1; shop = nd->u.scr.shop->item; @@ -1957,37 +2002,37 @@ int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, st new_ = 0; // process entries in buy list, one by one - for( i = 0; i < list_size; ++i ) { - int nameid, amount, value; + for (i = 0; i < VECTOR_LENGTH(*item_list); ++i) { + int value; + struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); // find this entry in the shop's sell list ARR_FIND( 0, shop_size, j, - p->list[i].ITID == shop[j].nameid || //Normal items - p->list[i].ITID == itemdb_viewid(shop[j].nameid) //item_avail replacement + entry->id == shop[j].nameid || //Normal items + entry->id == itemdb_viewid(shop[j].nameid) //item_avail replacement ); - - if( j == shop_size ) /* TODO find official response for this */ + if (j == shop_size) /* TODO find official response for this */ return 1; // no such item in shop - if( p->list[i].qty > shop[j].qty ) + entry->id = shop[j].nameid; //item_avail replacement + + if (entry->amount > (int)shop[j].qty) return 1; - amount = p->list[i].qty; - nameid = p->list[i].ITID = shop[j].nameid; //item_avail replacement value = shop[j].value; npc_market_qty[i] = j; - if( !itemdb->exists(nameid) ) /* TODO find official response for this */ + if (!itemdb->exists(entry->id)) /* TODO find official response for this */ return 1; // item no longer in itemdb - if( !itemdb->isstackable(nameid) && amount > 1 ) { + if (!itemdb->isstackable(entry->id) && entry->amount > 1) { //Exploit? You can't buy more than 1 of equipment types o.O ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of non-stackable item %d!\n", - sd->status.name, sd->status.account_id, sd->status.char_id, amount, nameid); - amount = p->list[i].qty = 1; + sd->status.name, sd->status.account_id, sd->status.char_id, entry->amount, entry->id); + entry->amount = 1; } - switch( pc->checkadditem(sd,nameid,amount) ) { + switch (pc->checkadditem(sd, entry->id, entry->amount)) { case ADDITEM_EXIST: break; case ADDITEM_NEW: @@ -1997,8 +2042,8 @@ int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, st return 1; } - z += (int64)value * amount; - w += itemdb_weight(nameid) * amount; + z += (int64)value * entry->amount; + w += itemdb_weight(entry->id) * entry->amount; } if (z > sd->status.zeny) /* TODO find official response for this */ @@ -2012,28 +2057,27 @@ int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, st pc->payzeny(sd,(int)z,LOG_TYPE_NPC, NULL); - for( i = 0; i < list_size; ++i ) { - int nameid = p->list[i].ITID; - int amount = p->list[i].qty; + for (i = 0; i < VECTOR_LENGTH(*item_list); ++i) { + struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); j = npc_market_qty[i]; - if( p->list[i].qty > shop[j].qty ) /* wohoo someone tampered with the packet. */ + if (entry->amount > (int)shop[j].qty) /* wohoo someone tampered with the packet. */ return 1; - shop[j].qty -= amount; + shop[j].qty -= entry->amount; npc->market_tosql(nd,j); - if (itemdb_type(nameid) == IT_PETEGG) { - pet->create_egg(sd, nameid); + if (itemdb_type(entry->id) == IT_PETEGG) { + pet->create_egg(sd, entry->id); } else { struct item item_tmp; memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid = nameid; + item_tmp.nameid = entry->id; item_tmp.identify = 1; - pc->additem(sd,&item_tmp,amount,LOG_TYPE_NPC); + pc->additem(sd, &item_tmp, entry->amount, LOG_TYPE_NPC); } } @@ -2041,10 +2085,12 @@ int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, st } /// npc_selllist for script-controlled shops -int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* item_list, struct npc_data* nd) +int npc_selllist_sub(struct map_session_data *sd, struct itemlist *item_list, struct npc_data *nd) { char npc_ev[EVENT_NAME_LENGTH]; char card_slot[NAME_LENGTH]; + char opt_index_str[NAME_LENGTH]; + char opt_value_str[NAME_LENGTH]; int i, j; int key_nameid = 0; int key_amount = 0; @@ -2052,6 +2098,12 @@ int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* item_li int key_attribute = 0; int key_identify = 0; int key_card[MAX_SLOTS]; + int key_opt_idx[MAX_ITEM_OPTIONS]; + int key_opt_value[MAX_ITEM_OPTIONS]; + + nullpo_ret(sd); + nullpo_ret(item_list); + nullpo_ret(nd); // discard old contents script->cleararray_pc(sd, "@sold_nameid", (void*)0); @@ -2068,21 +2120,39 @@ int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* item_li } // save list of to be sold items - for (i = 0; i < n; i++) { - int idx = item_list[i*2]-2; - - script->setarray_pc(sd, "@sold_nameid", i, (void*)(intptr_t)sd->status.inventory[idx].nameid, &key_nameid); - script->setarray_pc(sd, "@sold_quantity", i, (void*)(intptr_t)item_list[i*2+1], &key_amount); + for (i = 0; i < VECTOR_LENGTH(*item_list); i++) { + struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); + struct item *item = &sd->status.inventory[entry->id]; + intptr_t nameid = item->nameid; + intptr_t amount = entry->amount; + intptr_t refine = item->refine; + intptr_t attribute = item->attribute; + intptr_t identify = item->identify; + + script->setarray_pc(sd, "@sold_nameid", i, (void*)nameid, &key_nameid); + script->setarray_pc(sd, "@sold_quantity", i, (void*)amount, &key_amount); // process item based information into the arrays - script->setarray_pc(sd, "@sold_refine", i, (void*)(intptr_t)sd->status.inventory[idx].refine, &key_refine); - script->setarray_pc(sd, "@sold_attribute", i, (void*)(intptr_t)sd->status.inventory[idx].attribute, &key_attribute); - script->setarray_pc(sd, "@sold_identify", i, (void*)(intptr_t)sd->status.inventory[idx].identify, &key_identify); + script->setarray_pc(sd, "@sold_refine", i, (void*)refine, &key_refine); + script->setarray_pc(sd, "@sold_attribute", i, (void*)attribute, &key_attribute); + script->setarray_pc(sd, "@sold_identify", i, (void*)identify, &key_identify); for (j = 0; j < MAX_SLOTS; j++) { + intptr_t card = item->card[j]; // store each of the cards/special info from the item in the array snprintf(card_slot, sizeof(card_slot), "@sold_card%d", j + 1); - script->setarray_pc(sd, card_slot, i, (void*)(intptr_t)sd->status.inventory[idx].card[j], &key_card[j]); + script->setarray_pc(sd, card_slot, i, (void*)card, &key_card[j]); + } + + for (j = 0; j < MAX_ITEM_OPTIONS; j++) { + intptr_t opt_idx = item->option[j].index; + intptr_t opt_value = item->option[j].value; + + snprintf(opt_index_str, sizeof(opt_index_str), "@slot_opt_idx%d", j + 1); + script->setarray_pc(sd, opt_index_str, i, (void*)opt_idx, &key_opt_idx[j]); + + snprintf(opt_value_str, sizeof(opt_value_str), "@slot_opt_val%d", j + 1); + script->setarray_pc(sd, opt_value_str, i, (void*)opt_value, &key_opt_value[j]); } } @@ -2097,7 +2167,8 @@ 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) { +int npc_selllist(struct map_session_data *sd, struct itemlist *item_list) +{ int64 z; int i,skill_t, skill_idx = skill->get_index(MC_OVERCHARGE); struct npc_data *nd; @@ -2111,20 +2182,21 @@ int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) } if( nd->subtype != SHOP ) { - if( !(nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type == NST_ZENY) ) + if (!(nd->subtype == SCRIPT && nd->u.scr.shop && (nd->u.scr.shop->type == NST_ZENY || nd->u.scr.shop->type == NST_MARKET))) return 1; } z = 0; - // verify the sell list - for (i = 0; i < n; i++) { - int nameid, amount, idx, value; + if (sd->status.zeny >= MAX_ZENY && nd->master_nd == NULL) + return 1; - idx = item_list[i*2]-2; - amount = item_list[i*2+1]; + // verify the sell list + for (i = 0; i < VECTOR_LENGTH(*item_list); i++) { + struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); + int nameid, value, idx = entry->id; - if (idx >= MAX_INVENTORY || idx < 0 || amount < 0) { + if (idx >= MAX_INVENTORY || idx < 0 || entry->amount < 0) { return 1; } @@ -2136,7 +2208,7 @@ int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) nameid = sd->status.inventory[idx].nameid; - if (!nameid || !sd->inventory_data[idx] || sd->status.inventory[idx].amount < amount) { + if (!nameid || !sd->inventory_data[idx] || sd->status.inventory[idx].amount < entry->amount) { return 1; } @@ -2147,30 +2219,31 @@ 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 += (int64)value * amount; + z += (int64)value * entry->amount; } if( nd->master_nd ) { // Script-controlled shops - return npc->selllist_sub(sd, n, item_list, nd->master_nd); + return npc->selllist_sub(sd, item_list, nd->master_nd); } // delete items - for( i = 0; i < n; i++ ) { - int amount, idx; + for (i = 0; i < VECTOR_LENGTH(*item_list); i++) { + struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); + int idx = entry->id; - idx = item_list[i*2]-2; - amount = item_list[i*2+1]; - - if( sd->inventory_data[idx]->type == IT_PETEGG && sd->status.inventory[idx].card[0] == CARD0_PET ) { - if( pet->search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0 ) { + if (sd->inventory_data[idx]->type == IT_PETEGG && sd->status.inventory[idx].card[0] == CARD0_PET) { + if (pet->search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0) { intif->delete_petdata(MakeDWord(sd->status.inventory[idx].card[1], sd->status.inventory[idx].card[2])); } } - pc->delitem(sd, idx, amount, 0, DELITEM_SOLD, LOG_TYPE_NPC); + pc->delitem(sd, idx, entry->amount, 0, DELITEM_SOLD, LOG_TYPE_NPC); } - if( z > MAX_ZENY ) + if (z + sd->status.zeny > MAX_ZENY && nd->master_nd == NULL) + return 1; + + if (z > MAX_ZENY) z = MAX_ZENY; pc->getzeny(sd, (int)z, LOG_TYPE_NPC, NULL); @@ -2216,7 +2289,7 @@ int npc_remove_map(struct npc_data* nd) { /** * @see DBApply */ -int npc_unload_ev(DBKey key, DBData *data, va_list ap) +int npc_unload_ev(union DBKey key, struct DBData *data, va_list ap) { struct event_data* ev = DB->data2ptr(data); char* npcname = va_arg(ap, char *); @@ -2231,7 +2304,7 @@ int npc_unload_ev(DBKey key, DBData *data, va_list ap) /** * @see DBApply */ -int npc_unload_ev_label(DBKey key, DBData *data, va_list ap) +int npc_unload_ev_label(union DBKey key, struct DBData *data, va_list ap) { struct linkdb_node **label_linkdb = DB->data2ptr(data); struct npc_data* nd = va_arg(ap, struct npc_data *); @@ -2247,6 +2320,7 @@ int npc_unload_dup_sub(struct npc_data* nd, va_list args) { int src_id; + nullpo_ret(nd); src_id = va_arg(args, int); if (nd->src_id == src_id) npc->unload(nd, true); @@ -2255,6 +2329,7 @@ int npc_unload_dup_sub(struct npc_data* nd, va_list args) //Removes all npcs that are duplicates of the passed one. [Skotlex] void npc_unload_duplicates(struct npc_data* nd) { + nullpo_retv(nd); map->foreachnpc(npc->unload_dup_sub,nd->bl.id); } @@ -2372,23 +2447,22 @@ void npc_clearsrcfile(void) npc->src_files = NULL; } -/// Adds a npc source file (or removes all) -void npc_addsrcfile(const char* name) +/** + * Adds a npc source file. + * + * @param name The file name to add. + */ +void npc_addsrcfile(const char *name) { struct npc_src_list* file; struct npc_src_list* file_prev = NULL; - if( strcmpi(name, "clear") == 0 ) - { - npc->clearsrcfile(); - return; - } + nullpo_retv(name); // prevent multiple insert of source files file = npc->src_files; - while( file != NULL ) - { - if( strcmp(name, file->name) == 0 ) + while (file != NULL) { + if (strcmp(name, file->name) == 0) return;// found the file, no need to insert it again file_prev = file; file = file->next; @@ -2403,23 +2477,21 @@ void npc_addsrcfile(const char* name) file_prev->next = file; } -/// Removes a npc source file (or all) -void npc_delsrcfile(const char* name) +/** + * Removes a npc source file. + * + * @param name The file name to remove. + */ +void npc_delsrcfile(const char *name) { struct npc_src_list* file = npc->src_files; struct npc_src_list* file_prev = NULL; - if( strcmpi(name, "all") == 0 ) - { - npc->clearsrcfile(); - return; - } + nullpo_retv(name); - while( file != NULL ) - { - if( strcmp(file->name, name) == 0 ) - { - if( npc->src_files == file ) + while (file != NULL) { + if (strcmp(file->name, name) == 0) { + if (npc->src_files == file) npc->src_files = file->next; else file_prev->next = file->next; @@ -2442,10 +2514,10 @@ const char *npc_retainpathreference(const char *filepath) struct npc_path_data * npd = NULL; nullpo_ret(filepath); - if (npc_last_path == filepath) { - if (npc_last_npd != NULL) - npc_last_npd->references++; - return npc_last_ref; + if (npc->npc_last_path == filepath) { + if (npc->npc_last_npd != NULL) + npc->npc_last_npd->references++; + return npc->npc_last_ref; } if ((npd = strdb_get(npc->path_db,filepath)) == NULL) { @@ -2460,9 +2532,9 @@ const char *npc_retainpathreference(const char *filepath) npd->references++; - npc_last_npd = npd; - npc_last_ref = npd->path; - npc_last_path = filepath; + npc->npc_last_npd = npd; + npc->npc_last_ref = npd->path; + npc->npc_last_path = filepath; return npd->path; } @@ -2478,7 +2550,7 @@ void npc_releasepathreference(const char *filepath) nullpo_retv(filepath); - if (filepath != npc_last_ref) { + if (filepath != npc->npc_last_ref) { npd = strdb_get(npc->path_db, filepath); } @@ -2496,12 +2568,14 @@ void npc_parsename(struct npc_data* nd, const char* name, const char* start, con struct npc_data* dnd;// duplicate npc char newname[NAME_LENGTH]; + nullpo_retv(nd); + nullpo_retv(name); // parse name p = strstr(name,"::"); if( p ) { // <Display name>::<Unique name> size_t len = p-name; if( len > NAME_LENGTH ) { - ShowWarning("npc_parsename: Display name of '%s' is too long (len=%u) in file '%s', line '%d'. Truncating to %u characters.\n", name, (unsigned int)len, filepath, strline(buffer,start-buffer), NAME_LENGTH); + ShowWarning("npc_parsename: Display name of '%s' is too long (len=%u) in file '%s', line '%d'. Truncating to %d characters.\n", name, (unsigned int)len, filepath, strline(buffer,start-buffer), NAME_LENGTH); safestrncpy(nd->name, name, sizeof(nd->name)); } else { memcpy(nd->name, name, len); @@ -2509,12 +2583,12 @@ void npc_parsename(struct npc_data* nd, const char* name, const char* start, con } len = strlen(p+2); if( len > NAME_LENGTH ) - ShowWarning("npc_parsename: Unique name of '%s' is too long (len=%u) in file '%s', line '%d'. Truncating to %u characters.\n", name, (unsigned int)len, filepath, strline(buffer,start-buffer), NAME_LENGTH); + ShowWarning("npc_parsename: Unique name of '%s' is too long (len=%u) in file '%s', line '%d'. Truncating to %d characters.\n", name, (unsigned int)len, filepath, strline(buffer,start-buffer), NAME_LENGTH); safestrncpy(nd->exname, p+2, sizeof(nd->exname)); } else {// <Display name> size_t len = strlen(name); if( len > NAME_LENGTH ) - ShowWarning("npc_parsename: Name '%s' is too long (len=%u) in file '%s', line '%d'. Truncating to %u characters.\n", name, (unsigned int)len, filepath, strline(buffer,start-buffer), NAME_LENGTH); + ShowWarning("npc_parsename: Name '%s' is too long (len=%u) in file '%s', line '%d'. Truncating to %d characters.\n", name, (unsigned int)len, filepath, strline(buffer,start-buffer), NAME_LENGTH); safestrncpy(nd->name, name, sizeof(nd->name)); safestrncpy(nd->exname, name, sizeof(nd->exname)); } @@ -2551,6 +2625,7 @@ int npc_parseview(const char* w4, const char* start, const char* buffer, const c int val = FAKE_NPC, i = 0; char viewid[1024]; // Max size of name from constants.conf, see script->read_constdb. + nullpo_retr(FAKE_NPC, w4); // Extract view ID / constant while (w4[i] != '\0') { if (ISSPACE(w4[i]) || w4[i] == '/' || w4[i] == ',') @@ -2566,7 +2641,7 @@ int npc_parseview(const char* w4, const char* start, const char* buffer, const c { // Check if constant exists and get its value. if(!script->get_constant(viewid, &val)) { - ShowWarning("npc_parseview: Invalid NPC constant '%s' specified in file '%s', line'%d'. Defaulting to INVISIBLE_CLASS. \n", viewid, filepath, strline(buffer,start-buffer)); + ShowWarning("npc_parseview: Invalid NPC constant '%s' specified in file '%s', line '%d'. Defaulting to INVISIBLE_CLASS.\n", viewid, filepath, strline(buffer,start-buffer)); val = INVISIBLE_CLASS; } } else { @@ -2582,6 +2657,7 @@ int npc_parseview(const char* w4, const char* start, const char* buffer, const c // Checks if given view is an ID or constant. bool npc_viewisid(const char * viewid) { + nullpo_retr(false, viewid); if (atoi(viewid) != FAKE_NPC) { // Loop through view, looking for non-numeric character. while (*viewid) { @@ -2628,6 +2704,8 @@ struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short int i, flag = 0; struct npc_data *nd; + nullpo_retr(NULL, name); + nd = npc->create_npc(WARP, from_mapid, from_x, from_y, 0, battle_config.warp_point_debug ? WARP_DEBUG_CLASS : WARP_CLASS); safestrncpy(nd->exname, name, ARRAYLENGTH(nd->exname)); @@ -2676,6 +2754,9 @@ const char *npc_parse_warp(const char *w1, const char *w2, const char *w3, const char mapname[32], to_mapname[32]; struct npc_data *nd; + nullpo_retr(strchr(start,'\n'), w1); + nullpo_retr(strchr(start,'\n'), w4); + // w1=<from map name>,<fromX>,<fromY>,<facing> // w4=<spanx>,<spany>,<to map name>,<toX>,<toY> if( sscanf(w1, "%31[^,],%d,%d", mapname, &x, &y) != 3 @@ -2709,7 +2790,7 @@ const char *npc_parse_warp(const char *w1, const char *w2, const char *w3, const nd->u.warp.y = to_y; nd->u.warp.xs = xs; nd->u.warp.ys = ys; - npc_warp++; + npc->npc_warp++; npc->add_to_location(nd); @@ -2747,6 +2828,8 @@ const char *npc_parse_shop(const char *w1, const char *w2, const char *w3, const struct npc_data *nd; enum npc_subtype type; + nullpo_retr(strchr(start,'\n'), w1); + nullpo_retr(strchr(start,'\n'), w4); if( strcmp(w1,"-") == 0 ) { // 'floating' shop x = y = dir = 0; @@ -2762,7 +2845,7 @@ const char *npc_parse_shop(const char *w1, const char *w2, const char *w3, const } if (dir < 0 || dir > 7) { - ShowError("npc_parse_ship: Invalid NPC facing direction '%d' in file '%s', line '%d'.\n", dir, filepath, strline(buffer, start-buffer)); + ShowError("npc_parse_shop: Invalid NPC facing direction '%d' in file '%s', line '%d'.\n", dir, filepath, strline(buffer, start-buffer)); if (retval) *retval = EXIT_FAILURE; return strchr(start,'\n');//continue } @@ -2852,7 +2935,7 @@ const char *npc_parse_shop(const char *w1, const char *w2, const char *w3, const npc->parsename(nd, w3, start, buffer, filepath); nd->path = npc->retainpathreference(filepath); - ++npc_shop; + ++npc->npc_shop; npc->add_to_location(nd); return strchr(start,'\n');// continue @@ -2861,6 +2944,7 @@ const char *npc_parse_shop(const char *w1, const char *w2, const char *w3, const void npc_convertlabel_db(struct npc_label_list* label_list, const char *filepath) { int i; + nullpo_retv(label_list); for( i = 0; i < script->label_count; i++ ) { const char* lname = script->get_str(script->labels[i].key); int lpos = script->labels[i].pos; @@ -2987,6 +3071,7 @@ const char *npc_parse_script(const char *w1, const char *w2, const char *w3, con int label_list_num; struct npc_data* nd; + nullpo_retr(NULL, w1); if (strcmp(w1, "-") == 0) { // floating npc x = 0; @@ -3056,7 +3141,7 @@ const char *npc_parse_script(const char *w1, const char *w2, const char *w3, con if( options&NPO_TRADER ) nd->u.scr.trader = true; nd->u.scr.shop = NULL; - ++npc_script; + ++npc->npc_script; npc->add_to_location(nd); //----------------------------------------- @@ -3123,7 +3208,10 @@ bool npc_duplicate_script_sub(struct npc_data *nd, const struct npc_data *snd, i int i; bool retval = true; - ++npc_script; + nullpo_retr(false, nd); + nullpo_retr(false, snd); + + ++npc->npc_script; nd->u.scr.xs = xs; nd->u.scr.ys = ys; nd->u.scr.script = snd->u.scr.script; @@ -3167,7 +3255,10 @@ bool npc_duplicate_script_sub(struct npc_data *nd, const struct npc_data *snd, i */ bool npc_duplicate_shop_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options) { - ++npc_shop; + nullpo_retr(false, nd); + nullpo_retr(false, snd); + + ++npc->npc_shop; nd->u.shop.shop_item = snd->u.shop.shop_item; nd->u.shop.count = snd->u.shop.count; @@ -3182,7 +3273,10 @@ bool npc_duplicate_shop_sub(struct npc_data *nd, const struct npc_data *snd, int */ bool npc_duplicate_warp_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options) { - ++npc_warp; + nullpo_retr(false, nd); + nullpo_retr(false, snd); + + ++npc->npc_warp; nd->u.warp.xs = xs; nd->u.warp.ys = ys; nd->u.warp.mapindex = snd->u.warp.mapindex; @@ -3211,6 +3305,9 @@ bool npc_duplicate_warp_sub(struct npc_data *nd, const struct npc_data *snd, int */ bool npc_duplicate_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options) { + nullpo_retr(false, nd); + nullpo_retr(false, snd); + nd->src_id = snd->bl.id; switch (nd->subtype) { case SCRIPT: @@ -3276,6 +3373,8 @@ const char *npc_parse_duplicate(const char *w1, const char *w2, const char *w3, struct npc_data* dnd; end = strchr(start,'\n'); + nullpo_retr(end, w2); + nullpo_retr(end, w4); length = strlen(w2); // get the npc being duplicated @@ -3360,7 +3459,13 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) int dm = -1, im = -1, xs = -1, ys = -1; struct npc_data *nd = NULL; - if( m == -1 || map->list[m].instance_id == -1 ) + if (m == -1) + return 1; + + Assert_retr(1, m >= 0 && m < map->count); + nullpo_retr(1, snd); + + if (map->list[m].instance_id == -1) return 1; snprintf(newname, ARRAYLENGTH(newname), "dup_%d_%d", map->list[m].instance_id, snd->bl.id); @@ -3404,10 +3509,15 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) } //Set mapcell CELL_NPC to trigger event later -void npc_setcells(struct npc_data* nd) { - int16 m = nd->bl.m, x = nd->bl.x, y = nd->bl.y, xs, ys; +void npc_setcells(struct npc_data* nd) +{ + int16 m, x, y, xs, ys; int i,j; + nullpo_retv(nd); + m = nd->bl.m; + x = nd->bl.x; + y = nd->bl.y; switch(nd->subtype) { case WARP: xs = nd->u.warp.xs; @@ -3449,9 +3559,13 @@ int npc_unsetcells_sub(struct block_list *bl, va_list ap) } void npc_unsetcells(struct npc_data* nd) { - int16 m = nd->bl.m, x = nd->bl.x, y = nd->bl.y, xs, ys; + int16 m, x, y, xs, ys; int i,j, x0, x1, y0, y1; + nullpo_retv(nd); + m = nd->bl.m; + x = nd->bl.x; + y = nd->bl.y; switch(nd->subtype) { case WARP: xs = nd->u.warp.xs; @@ -3486,7 +3600,9 @@ void npc_unsetcells(struct npc_data* nd) { void npc_movenpc(struct npc_data* nd, int16 x, int16 y) { - const int16 m = nd->bl.m; + int16 m; + nullpo_retv(nd); + m = nd->bl.m; if (m < 0 || nd->bl.prev == NULL) return; //Not on a map. x = cap_value(x, 0, map->list[m].xs-1); @@ -3504,6 +3620,7 @@ void npc_movenpc(struct npc_data* nd, int16 x, int16 y) void npc_setdisplayname(struct npc_data* nd, const char* newname) { nullpo_retv(nd); + nullpo_retv(newname); safestrncpy(nd->name, newname, sizeof(nd->name)); if( map->list[nd->bl.m].users ) @@ -3538,6 +3655,7 @@ int npc_do_atcmd_event(struct map_session_data* sd, const char* command, const c size_t len; nullpo_ret(sd); + nullpo_ret(message); if( ev == NULL || (nd = ev->nd) == NULL ) { ShowError("npc_event: event not found [%s]\n", eventname); @@ -3561,7 +3679,7 @@ int npc_do_atcmd_event(struct map_session_data* sd, const char* command, const c } st = script->alloc_state(ev->nd->u.scr.script, ev->pos, sd->bl.id, ev->nd->bl.id); - script->setd_sub(st, NULL, ".@atcmd_command$", 0, (void *)command, NULL); + script->setd_sub(st, NULL, ".@atcmd_command$", 0, command, NULL); len = strlen(message); if (len) { @@ -3617,12 +3735,19 @@ int npc_do_atcmd_event(struct map_session_data* sd, const char* command, const c */ const char *npc_parse_function(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval) { - DBMap* func_db; - DBData old_data; + struct DBMap *func_db; + struct DBData old_data; struct script_code *scriptroot; const char* end; const char* script_start; + nullpo_retr(NULL, w1); + nullpo_retr(NULL, w2); + nullpo_retr(NULL, w3); + nullpo_retr(NULL, w4); + nullpo_retr(NULL, start); + nullpo_retr(NULL, retval); + script_start = strstr(start,"\t{"); end = strchr(start,'\n'); if( *w4 != '{' || script_start == NULL || (end != NULL && script_start > end) ) { @@ -3650,7 +3775,7 @@ const char *npc_parse_function(const char *w1, const char *w2, const char *w3, c struct script_code *oldscript = (struct script_code*)DB->data2ptr(&old_data); ShowWarning("npc_parse_function: Overwriting user function [%s] in file '%s', line '%d'.\n", w3, filepath, strline(buffer,start-buffer)); script->free_vars(oldscript->local.vars); - aFree(oldscript->script_buf); + VECTOR_CLEAR(oldscript->script_buf); aFree(oldscript); } @@ -3666,6 +3791,7 @@ void npc_parse_mob2(struct spawn_data* mobspawn) { int i; + nullpo_retv(mobspawn); for( i = mobspawn->active; i < mobspawn->num; ++i ) { struct mob_data* md = mob->spawn_dataset(mobspawn); md->spawn = mobspawn; @@ -3699,6 +3825,11 @@ const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, const struct spawn_data mobspawn, *data; struct mob_db* db; + nullpo_retr(strchr(start,'\n'), w1); + nullpo_retr(strchr(start,'\n'), w2); + nullpo_retr(strchr(start,'\n'), w3); + nullpo_retr(strchr(start,'\n'), w4); + memset(&mobspawn, 0, sizeof(struct spawn_data)); mobspawn.state.boss = (strcmp(w2,"boss_monster") == 0 ? 1 : 0); @@ -3841,7 +3972,7 @@ const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, const // spawn / cache the new mobs if( battle_config.dynamic_mobs && map->addmobtolist(data->m, data) >= 0 ) { data->state.dynamic = true; - npc_cache_mob += data->num; + npc->npc_cache_mob += data->num; // check if target map has players // (usually shouldn't occur when map server is just starting, @@ -3852,10 +3983,10 @@ const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, const } else { data->state.dynamic = false; npc->parse_mob2(data); - npc_delay_mob += data->num; + npc->npc_delay_mob += data->num; } - npc_mob++; + npc->npc_mob++; return strchr(start,'\n');// continue } @@ -3905,6 +4036,9 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co char mapname[32]; int state = 1; + nullpo_retr(strchr(start,'\n'), w1); + nullpo_retr(strchr(start,'\n'), w3); + // w1=<mapname> if( sscanf(w1, "%31[^,]", mapname) != 1 ) { @@ -4329,6 +4463,7 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co */ const char *npc_parse_unknown_object(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval) { + nullpo_retr(start, retval); ShowError("npc_parsesrcfile: Unable to parse, probably a missing or extra TAB in file '%s', line '%d'. Skipping line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4); start = strchr(start,'\n');// skip and continue *retval = EXIT_FAILURE; @@ -4353,6 +4488,8 @@ int npc_parsesrcfile(const char* filepath, bool runOnInit) { char* buffer; const char* p; + nullpo_retr(EXIT_FAILURE, filepath); + // read whole file to buffer fp = fopen(filepath, "rb"); if( fp == NULL ) { @@ -4536,7 +4673,7 @@ int npc_script_event(struct map_session_data* sd, enum npce_event type) if (type == NPCE_MAX) return 0; if (!sd) { - ShowError("npc_script_event: NULL sd. Event Type %d\n", type); + ShowError("npc_script_event: NULL sd. Event Type %u\n", type); return 0; } for (i = 0; i<script_event[type].event_count; i++) @@ -4561,11 +4698,10 @@ void npc_read_event_script(void) {"Kill NPC Event",script->config.kill_mob_event_name}, }; - for (i = 0; i < NPCE_MAX; i++) - { - DBIterator* iter; - DBKey key; - DBData *data; + for (i = 0; i < NPCE_MAX; i++) { + struct DBIterator *iter; + union DBKey key; + struct DBData *data; char name[64]="::"; safestrncpy(name+2,config[i].event_name,62); @@ -4608,9 +4744,10 @@ void npc_read_event_script(void) /** * @see DBApply */ -int npc_path_db_clear_sub(DBKey key, DBData *data, va_list args) +int npc_path_db_clear_sub(union DBKey key, struct DBData *data, va_list args) { struct npc_path_data *npd = DB->data2ptr(data); + nullpo_ret(npd); if (npd->path) aFree(npd->path); return 0; @@ -4619,7 +4756,7 @@ int npc_path_db_clear_sub(DBKey key, DBData *data, va_list args) /** * @see DBApply */ -int npc_ev_label_db_clear_sub(DBKey key, DBData *data, va_list args) +int npc_ev_label_db_clear_sub(union DBKey key, struct DBData *data, va_list args) { struct linkdb_node **label_linkdb = DB->data2ptr(data); linkdb_final(label_linkdb); // linked data (struct event_data*) is freed when clearing ev_db @@ -4646,12 +4783,12 @@ void npc_process_files( int npc_min ) { "\t-'"CL_WHITE"%d"CL_RESET"' Spawn sets\n" "\t-'"CL_WHITE"%d"CL_RESET"' Mobs Cached\n" "\t-'"CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n", - npc_id - npc_min, npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob); + npc->npc_id - npc_min, npc->npc_warp, npc->npc_shop, npc->npc_script, npc->npc_mob, npc->npc_cache_mob, npc->npc_delay_mob); } //Clear then reload npcs files int npc_reload(void) { - int npc_new_min = npc_id; + int npc_new_min = npc->npc_id; struct s_mapiterator* iter; struct block_list* bl; @@ -4667,9 +4804,9 @@ int npc_reload(void) { db_clear(npc->ev_db); npc->ev_label_db->clear(npc->ev_label_db, npc->ev_label_db_clear_sub); - npc_last_npd = NULL; - npc_last_path = NULL; - npc_last_ref = NULL; + npc->npc_last_npd = NULL; + npc->npc_last_path = NULL; + npc->npc_last_ref = NULL; //Remove all npcs/mobs. [Skotlex] iter = mapit_geteachiddb(); @@ -4709,8 +4846,8 @@ int npc_reload(void) { // clear mob spawn lookup index mob->clear_spawninfo(); - npc_warp = npc_shop = npc_script = 0; - npc_mob = npc_cache_mob = npc_delay_mob = 0; + npc->npc_warp = npc->npc_shop = npc->npc_script = 0; + npc->npc_mob = npc->npc_cache_mob = npc->npc_delay_mob = 0; // reset mapflags map->flags_init(); @@ -4748,11 +4885,14 @@ int npc_reload(void) { } //Unload all npc in the given file -bool npc_unloadfile( const char* filepath ) { - DBIterator * iter = db_iterator(npc->name_db); +bool npc_unloadfile(const char *filepath) +{ + struct DBIterator *iter = db_iterator(npc->name_db); struct npc_data* nd = NULL; bool found = false; + nullpo_retr(false, filepath); + for( nd = dbi_first(iter); dbi_exists(iter); nd = dbi_next(iter) ) { if( nd->path && strcasecmp(nd->path,filepath) == 0 ) { // FIXME: This can break in case-sensitive file systems found = true; @@ -4791,6 +4931,9 @@ int do_final_npc(void) { void npc_debug_warps_sub(struct npc_data* nd) { int16 m; + + nullpo_retv(nd); + if (nd->bl.type != BL_NPC || nd->subtype != WARP || nd->bl.m < 0) return; @@ -4827,32 +4970,26 @@ static void npc_debug_warps(void) { int do_init_npc(bool minimal) { int i; - memset(&npc->base_ud, 0, sizeof( struct unit_data) ); - npc->base_ud.bl = NULL; - npc->base_ud.walktimer = INVALID_TIMER; - npc->base_ud.skilltimer = INVALID_TIMER; - npc->base_ud.attacktimer = INVALID_TIMER; - npc->base_ud.attackabletime = - npc->base_ud.canact_tick = - npc->base_ud.canmove_tick = timer->gettick(); + unit->init_ud(&npc->base_ud); + npc->base_ud.bl = NULL; //Stock view data for normal npcs. memset(&npc_viewdb, 0, sizeof(npc_viewdb)); - npc_viewdb[0].class_ = INVISIBLE_CLASS; //Invisible class is stored here. + npc_viewdb[0].class = INVISIBLE_CLASS; //Invisible class is stored here. for( i = 1; i < MAX_NPC_CLASS; i++ ) - npc_viewdb[i].class_ = i; + npc_viewdb[i].class = i; for( i = MAX_NPC_CLASS2_START; i < MAX_NPC_CLASS2_END; i++ ) - npc_viewdb2[i - MAX_NPC_CLASS2_START].class_ = i; + npc_viewdb2[i - MAX_NPC_CLASS2_START].class = i; npc->ev_db = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, EVENT_NAME_LENGTH); npc->ev_label_db = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, NAME_LENGTH); npc->name_db = strdb_alloc(DB_OPT_BASE, NAME_LENGTH); npc->path_db = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, 0); - npc_last_npd = NULL; - npc_last_path = NULL; - npc_last_ref = NULL; + npc->npc_last_npd = NULL; + npc->npc_last_path = NULL; + npc->npc_last_ref = NULL; // Should be loaded before npc processing, otherwise labels could overwrite constant values // and lead to undefined behavior [Panikon] @@ -4881,12 +5018,6 @@ int do_init_npc(bool minimal) { timer->add_func_list(npc->timerevent,"npc_timerevent"); } - if( script->lang_export_fp ) { - ShowInfo("Lang exported to '%s'\n",script->lang_export_file); - fclose(script->lang_export_fp); - script->lang_export_fp = NULL; - } - // Init dummy NPC CREATE(npc->fake_nd, struct npc_data, 1); npc->fake_nd->bl.m = -1; @@ -4896,7 +5027,7 @@ int do_init_npc(bool minimal) { strcpy(npc->fake_nd->name,"FAKE_NPC"); memcpy(npc->fake_nd->exname, npc->fake_nd->name, 9); - npc_script++; + npc->npc_script++; npc->fake_nd->bl.type = BL_NPC; npc->fake_nd->subtype = SCRIPT; @@ -4910,6 +5041,17 @@ int do_init_npc(bool minimal) { void npc_defaults(void) { npc = &npc_s; + npc->npc_id = START_NPC_NUM; + npc->npc_warp = 0; + npc->npc_shop = 0; + npc->npc_script = 0; + npc->npc_mob = 0; + npc->npc_delay_mob = 0; + npc->npc_cache_mob = 0; + npc->npc_last_path = NULL; + npc->npc_last_ref = NULL; + npc->npc_last_npd = NULL; + npc->motd = NULL; npc->ev_db = NULL; npc->ev_label_db = NULL; |