diff options
Diffstat (limited to 'src/map/npc.c')
-rw-r--r-- | src/map/npc.c | 237 |
1 files changed, 157 insertions, 80 deletions
diff --git a/src/map/npc.c b/src/map/npc.c index dcfa52193..4b188c177 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -176,7 +176,7 @@ int npc_enable(const char* name, int flag) if (nd->class_ == WARP_CLASS || nd->class_ == FLAG_CLASS) { //Client won't display option changes for these classes [Toms] if (nd->sc.option&(OPTION_HIDE|OPTION_INVISIBLE)) - clif_clearunit_area(&nd->bl, 0); + clif_clearunit_area(&nd->bl, CLR_OUTSIGHT); else clif_spawn(&nd->bl); } else @@ -206,7 +206,7 @@ int npc_event_dequeue(struct map_session_data* sd) if(sd->npc_id) { //Current script is aborted. if(sd->state.using_fake_npc){ - clif_clearunit_single(sd->npc_id, 0, sd->fd); + clif_clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd); sd->state.using_fake_npc = 0; } if (sd->st) { @@ -866,7 +866,7 @@ int npc_touch_areanpc(struct map_session_data* sd, int m, int x, int y) case WARP: if( pc_ishiding(sd) ) break; // hidden chars cannot use warps - pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0); + pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,CLR_OUTSIGHT); break; case SCRIPT: if( npc_ontouch_event(sd,map[m].npc[i]) > 0 && npc_ontouch2_event(sd,map[m].npc[i]) > 0 ) @@ -923,7 +923,7 @@ int npc_touch_areanpc2(struct mob_data *md) xs = map_mapindex2mapid(map[m].npc[i]->u.warp.mapindex); if( m < 0 ) break; // Cannot Warp between map servers - if( unit_warp(&md->bl, xs, map[m].npc[i]->u.warp.x, map[m].npc[i]->u.warp.y, 0) == 0 ) + if( unit_warp(&md->bl, xs, map[m].npc[i]->u.warp.x, map[m].npc[i]->u.warp.y, CLR_OUTSIGHT) == 0 ) return 1; // Warped break; case SCRIPT: @@ -1142,14 +1142,24 @@ static int npc_buylist_sub(struct map_session_data* sd, int n, unsigned short* i { char npc_ev[NAME_LENGTH*2+3]; int i; - int regkey = add_str("@bought_nameid"); - int regkey2 = add_str("@bought_quantity"); - snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnBuyItem", nd->exname); - for(i=0;i<n;i++){ - pc_setreg(sd,regkey+(i<<24),(int)item_list[i*2+1]); - pc_setreg(sd,regkey2+(i<<24),(int)item_list[i*2]); + int key_nameid = 0; + int key_amount = 0; + + // 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)item_list[i*2+1], &key_nameid); + script_setarray_pc(sd, "@bought_quantity", i, (void*)(intptr)item_list[i*2], &key_amount); } + + // invoke event + snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnBuyItem", nd->exname); npc_event(sd, npc_ev, 0); + return 0; } /*========================================== @@ -1251,8 +1261,6 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) nd = npc_checknear(sd,map_id2bl(sd->npc_shopid)); if( nd == NULL ) return 3; - if( nd->master_nd != NULL ) //Script-based shops. - return npc_buylist_sub(sd,n,item_list,nd->master_nd); if( nd->subtype != SHOP ) return 3; @@ -1287,6 +1295,11 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) amount = item_list[i*2+0] = 1; } + if( nd->master_nd ) + {// Script-controlled shops decide by themselves, what can be bought and for what price. + continue; + } + switch( pc_checkadditem(sd,nameid,amount) ) { case ADDITEM_EXIST: @@ -1300,13 +1313,15 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) return 2; } - if( !itemdb_value_notdc(nameid) ) - value = pc_modifybuyvalue(sd,value); + value = pc_modifybuyvalue(sd,value); z += (double)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 ) return 1; // Not enough Zeny if( w + sd->weight > sd->max_weight ) @@ -1356,90 +1371,139 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) return 0; } -/*========================================== - * - *------------------------------------------*/ + +/// npc_selllist for script-controlled shops +static int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* item_list, struct npc_data* nd) +{ + char npc_ev[NAME_LENGTH*2+3]; + int i, idx; + int key_nameid = 0; + int key_amount = 0; + + // discard old contents + script_cleararray_pc(sd, "@sold_nameid", (void*)0); + script_cleararray_pc(sd, "@sold_quantity", (void*)0); + + // save list of to be sold items + for( i = 0; i < n; i++ ) + { + idx = item_list[i*2]-2; + + script_setarray_pc(sd, "@sold_nameid", i, (void*)(intptr)sd->status.inventory[idx].nameid, &key_nameid); + script_setarray_pc(sd, "@sold_quantity", i, (void*)(intptr)item_list[i*2+1], &key_amount); + } + + // invoke event + snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnSellItem", nd->exname); + npc_event(sd, npc_ev, 0); + return 0; +} + + +/// Player item selling to npc shop. +/// +/// @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; int i,skill; struct npc_data *nd; - + nullpo_retr(1, sd); nullpo_retr(1, item_list); - if ((nd = npc_checknear(sd,map_id2bl(sd->npc_shopid))) == NULL) + if( ( nd = npc_checknear(sd, map_id2bl(sd->npc_shopid)) ) == NULL || nd->subtype != SHOP ) + { return 1; - nd = nd->master_nd; //For OnSell triggers. + } - for(i=0,z=0;i<n;i++) { - int nameid, idx; - short qty; - idx = item_list[i*2]-2; - qty = (short)item_list[i*2+1]; - - if (idx <0 || idx >=MAX_INVENTORY || qty < 0) - break; - - nameid=sd->status.inventory[idx].nameid; - if (nameid == 0 || !sd->inventory_data[idx] || - sd->status.inventory[idx].amount < qty) - break; - - if (sd->inventory_data[idx]->flag.value_notoc) - z+=(double)qty*sd->inventory_data[idx]->value_sell; - else - z+=(double)qty*pc_modifysellvalue(sd,sd->inventory_data[idx]->value_sell); + z = 0; + + // verify the sell list + for( i = 0; i < n; i++ ) + { + int nameid, amount, idx, value; + + 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( idx >= MAX_INVENTORY || idx < 0 || amount < 0 ) { - if(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])); + return 1; + } + + nameid = sd->status.inventory[idx].nameid; + + if( !nameid || !sd->inventory_data[idx] || sd->status.inventory[idx].amount < amount ) + { + return 1; } - if(log_config.enable_logs&0x20) //Logs items, Sold to NPC (S)hop [Lupus] - log_pick_pc(sd, "S", nameid, -qty, &sd->status.inventory[idx]); + if( nd->master_nd ) + {// Script-controlled shops decide by themselves, what can be sold and at what price. + continue; + } + + value = pc_modifysellvalue(sd, sd->inventory_data[idx]->value_sell); + + z+= (double)value*amount; + } + + if( nd->master_nd ) + {// Script-controlled shops + return npc_selllist_sub(sd, n, item_list, nd->master_nd); + } + + // delete items + for( i = 0; i < n; i++ ) + { + int nameid, amount, idx; + + idx = item_list[i*2]-2; + amount = item_list[i*2+1]; + nameid = sd->status.inventory[idx].nameid; + + //Logs items, Sold to NPC (S)hop [Lupus] + if( log_config.enable_logs&0x20 ) + log_pick_pc(sd, "S", nameid, -amount, &sd->status.inventory[idx]); + //Logs - if(nd) { - pc_setreg(sd,add_str("@sold_nameid")+(i<<24),(int)sd->status.inventory[idx].nameid); - pc_setreg(sd,add_str("@sold_quantity")+(i<<24),qty); + if( sd->inventory_data[idx]->type == IT_PETEGG && sd->status.inventory[idx].card[0] == CARD0_PET ) + { + if( 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,qty,0,6); + + pc_delitem(sd, idx, amount, 0, 6); } - if (z > MAX_ZENY) z = MAX_ZENY; + if( z > MAX_ZENY ) + z = MAX_ZENY; - if(log_config.zeny) //Logs (S)hopping Zeny [Lupus] + //Logs (S)hopping Zeny [Lupus] + if( log_config.zeny ) log_zeny(sd, "S", sd, (int)z); + //Logs - pc_getzeny(sd,(int)z); - - if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_OVERCHARGE)) > 0) { - if (sd->status.skill[MC_OVERCHARGE].flag != 0) + pc_getzeny(sd, (int)z); + + // custom merchant shop exp bonus + if( battle_config.shop_exp > 0 && z > 0 && ( skill = pc_checkskill(sd,MC_OVERCHARGE) ) > 0) + { + if( sd->status.skill[MC_OVERCHARGE].flag != 0 ) skill = sd->status.skill[MC_OVERCHARGE].flag - 2; - if (skill > 0) { + if( skill > 0 ) + { z = z * (double)skill * (double)battle_config.shop_exp/10000.; - if (z < 1) + if( z < 1 ) z = 1; - pc_gainexp(sd,NULL,0,(int)z, false); + pc_gainexp(sd, NULL, 0, (int)z, false); } } - - if(nd) { - char npc_ev[NAME_LENGTH*2+3]; - snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnSellItem", nd->exname); - npc_event(sd, npc_ev, 0); - } - - if (i<n) { - //Error/Exploit... of some sort. If we return 1, the client will not mark - //any item as deleted even though a few were sold. In such a case, we - //have no recourse but to kick them out so their inventory will refresh - //correctly on relog. [Skotlex] - if (i) set_eof(sd->fd); - return 1; - } + return 0; } @@ -1451,7 +1515,7 @@ int npc_remove_map(struct npc_data* nd) if(nd->bl.prev == NULL || nd->bl.m < 0) return 1; //Not assigned to a map. m = nd->bl.m; - clif_clearunit_area(&nd->bl,2); + clif_clearunit_area(&nd->bl,CLR_RESPAWN); npc_unsetcells(nd); map_delblock(&nd->bl); //Remove npc from map[].npc list. [Skotlex] @@ -1875,8 +1939,8 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const id->name, nameid, value, (int)(value*0.75), id->value_sell, (int)(id->value_sell*1.24), filepath, strline(buffer,start-buffer)); } //for logs filters, atcommands and iteminfo script command - if( id->maxchance <= 0 ) - id->maxchance = 10000; //10000 (100% drop chance)would show that the item's sold in NPC Shop + if( id->maxchance == 0 ) + id->maxchance = -1; // -1 would show that the item's sold in NPC Shop items[i].nameid = nameid; items[i].value = value; @@ -2550,7 +2614,7 @@ void npc_setclass(struct npc_data* nd, short class_) if( nd->class_ == class_ ) return; - clif_clearunit_area(&nd->bl, 0);// fade out + clif_clearunit_area(&nd->bl, CLR_OUTSIGHT);// fade out nd->class_ = class_; status_set_viewdata(&nd->bl, class_); clif_spawn(&nd->bl);// fade in @@ -3129,7 +3193,12 @@ void npc_parsesrcfile(const char* filepath) {// Incorrect map, we must skip the script info... ShowError("npc_parsesrcfile: Unknown map '%s' in file '%s', line '%d'. Skipping line...\n", mapname, filepath, strline(buffer,p-buffer)); if( strcasecmp(w2,"script") == 0 && count > 3 ) - p = npc_skip_script(p,buffer,filepath); + { + if((p = npc_skip_script(p,buffer,filepath)) == NULL) + { + break; + } + } p = strchr(p,'\n');// next line continue; } @@ -3137,7 +3206,12 @@ void npc_parsesrcfile(const char* filepath) if( m < 0 ) {// "mapname" is not assigned to this server, we must skip the script info... if( strcasecmp(w2,"script") == 0 && count > 3 ) - p = npc_skip_script(p,buffer,filepath); + { + if((p = npc_skip_script(p,buffer,filepath)) == NULL) + { + break; + } + } p = strchr(p,'\n');// next line continue; } @@ -3270,7 +3344,7 @@ int npc_reload(void) npc_unload((struct npc_data *)bl); break; case BL_MOB: - unit_free(bl,0); + unit_free(bl,CLR_OUTSIGHT); break; } } @@ -3304,6 +3378,9 @@ int npc_reload(void) npc_warp = npc_shop = npc_script = 0; npc_mob = npc_cache_mob = npc_delay_mob = 0; + // reset mapflags + map_flags_init(); + //TODO: the following code is copy-pasted from do_init_npc(); clean it up // Reloading npcs now for (nsl = npc_src_files; nsl; nsl = nsl->next) @@ -3350,7 +3427,7 @@ int do_final_npc(void) if (bl->type == BL_NPC) npc_unload((struct npc_data *)bl); else if (bl->type&(BL_MOB|BL_PET|BL_HOM|BL_MER)) - unit_free(bl, 0); + unit_free(bl, CLR_OUTSIGHT); } } |