/*========================================== * Divorce players * only used if 'partner_id' is offline *------------------------------------------*/ int chrif_divorceack(int char_id, int partner_id) { struct map_session_data* sd; int i; if( !char_id || !partner_id ) return 0; if( (sd = map_charid2sd(char_id)) != NULL && sd->status.partner_id == partner_id ) { sd->status.partner_id = 0; for(i = 0; i < MAX_INVENTORY; i++) if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F) pc_delitem(sd, i, 1, 0, 0, LOG_TYPE_OTHER); } if( (sd = map_charid2sd(partner_id)) != NULL && sd->status.partner_id == char_id ) { sd->status.partner_id = 0; for(i = 0; i < MAX_INVENTORY; i++) if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F) pc_delitem(sd, i, 1, 0, 0, LOG_TYPE_OTHER); } return 0; }
int mail_removeitem(struct map_session_data *sd, short flag, int idx, int amount) { int i; nullpo_ret(sd); idx -= 2; if( idx < 0 || idx >= MAX_INVENTORY ) return false; if( amount <= 0 || amount > sd->inventory.u.items_inventory[idx].amount ) return false; ARR_FIND(0, MAIL_MAX_ITEM, i, sd->mail.item[i].index == idx && sd->mail.item[i].nameid > 0); if( i == MAIL_MAX_ITEM ){ return false; } if( flag ){ if( battle_config.mail_attachment_price > 0 ){ if( pc_payzeny( sd, battle_config.mail_attachment_price, LOG_TYPE_MAIL, NULL ) ){ return false; } } #if PACKETVER < 20150513 // With client update packet pc_delitem(sd, idx, amount, 1, 0, LOG_TYPE_MAIL); #else // RODEX refreshes the client inventory from the ACK packet pc_delitem(sd, idx, amount, 0, 0, LOG_TYPE_MAIL); #endif }else{ for( ; i < MAIL_MAX_ITEM-1; i++ ){ if (sd->mail.item[i + 1].nameid == 0) break; sd->mail.item[i].index = sd->mail.item[i+1].index; sd->mail.item[i].nameid = sd->mail.item[i+1].nameid; sd->mail.item[i].amount = sd->mail.item[i+1].amount; } for( ; i < MAIL_MAX_ITEM; i++ ){ sd->mail.item[i].index = 0; sd->mail.item[i].nameid = 0; sd->mail.item[i].amount = 0; } #if PACKETVER < 20150513 clif_additem(sd, idx, amount, 0); #else clif_mail_removeitem(sd, true, idx + 2, amount); #endif } return 1; }
int bg_member_removeskulls(struct map_session_data *sd) { int n; nullpo_ret(sd); if( (n = pc_search_inventory(sd,BLUE_SKULL)) >= 0 ) pc_delitem(sd,n,sd->status.inventory[n].amount,0,2,LOG_TYPE_CONSUME); if( (n = pc_search_inventory(sd,RED_SKULL)) >= 0 ) pc_delitem(sd,n,sd->status.inventory[n].amount,0,2,LOG_TYPE_CONSUME); if( (n = pc_search_inventory(sd,GREEN_SKULL)) >= 0 ) pc_delitem(sd,n,sd->status.inventory[n].amount,0,2,LOG_TYPE_CONSUME); return 1; }
static int pet_food(struct map_session_data *sd, struct pet_data *pd) { int i, food_id; food_id = pd->petDB->FoodID; i = pc_search_inventory(sd, food_id); if( i == INDEX_NOT_FOUND ) { clif_pet_food(sd, food_id, 0); return 1; } pc_delitem(sd, i, 1, 0, 0, LOG_TYPE_CONSUME); if( pd->pet.hungry > 90 ) pet_set_intimate(pd, pd->pet.intimate - pd->petDB->r_full); else { int add_intimate = 0; if( battle_config.pet_friendly_rate != 100 ) add_intimate = (pd->petDB->r_hungry * battle_config.pet_friendly_rate) / 100; else add_intimate = pd->petDB->r_hungry; if( pd->pet.hungry > 75 ) { add_intimate = add_intimate>>1; if( add_intimate <= 0 ) add_intimate = 1; } pet_set_intimate(pd, pd->pet.intimate + add_intimate); }
/** * Add an item to the storage from the inventory. * @param sd : player * @param stor : Storage data * @param index : inventory index to take the item from * @param amount : number of item to take * @return 0:fail, 1:success */ void storage_storageadd(struct map_session_data* sd, struct s_storage *stor, int index, int amount) { enum e_storage_add result; nullpo_retv(sd); result = storage_canAddItem(stor, index, sd->inventory.u.items_inventory, amount, MAX_INVENTORY); if (result == STORAGE_ADD_INVALID) return; else if (result == STORAGE_ADD_OK) { switch( storage_additem(sd, stor, &sd->inventory.u.items_inventory[index], amount) ){ case 0: pc_delitem(sd,index,amount,0,4,LOG_TYPE_STORAGE); return; case 1: break; case 2: result = STORAGE_ADD_NOROOM; break; } } clif_storageitemremoved(sd,index,0); clif_dropitem(sd,index,0); }
/*========================================== * Add an item to the storage from the inventory. *------------------------------------------ */ int storage_storageadd(struct map_session_data *sd,int index,int amount) { struct storage *stor; nullpo_retr(0, sd); nullpo_retr(0, stor=account2storage2(sd->status.account_id)); if((stor->storage_amount > MAX_STORAGE) || !stor->storage_status) return 0; // storage full / storage closed if(index<0 || index>=MAX_INVENTORY) return 0; if(sd->status.inventory[index].nameid <= 0) return 0; //No item on that spot if(amount < 1 || amount > sd->status.inventory[index].amount) return 0; // log_tostorage(sd, index, 0); if(storage_additem(sd,stor,&sd->status.inventory[index],amount)==0) // remove item from inventory pc_delitem(sd,index,amount,0); return 1; }
/// Invoked (from char-server) when a party member leaves the party. int party_member_withdraw(int party_id, int account_id, int char_id) { struct map_session_data* sd = map_id2sd(account_id); struct party_data* p = party_search(party_id); if( p ) { int i; ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id ); if( i < MAX_PARTY ) { clif_party_withdraw(p,sd,account_id,p->party.member[i].name,0x0); memset(&p->party.member[i], 0, sizeof(p->party.member[0])); memset(&p->data[i], 0, sizeof(p->data[0])); p->party.count--; party_check_state(p); } } if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id ) { int idxlist[MAX_INVENTORY]; //or malloc to reduce consumtion int j,i; j = pc_bound_chk(sd,3,idxlist); for(i=0;i<j;i++) pc_delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_OTHER); sd->status.party_id = 0; clif_charnameupdate(sd); //Update name display [Skotlex] //TODO: hp bars should be cleared too if( p->instance_id ) instance_check_kick(sd); } return 0; }
static int pet_food(struct map_session_data *sd, struct pet_data *pd) { int i,k; k=pd->petDB->FoodID; i=pc_search_inventory(sd,k); if(i < 0) { clif_pet_food(sd,k,0); return 1; } pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME); if( pd->pet.hungry > 90 ) pet_set_intimate(pd, pd->pet.intimate - pd->petDB->r_full); else { if( battle_config.pet_friendly_rate != 100 ) k = (pd->petDB->r_hungry * battle_config.pet_friendly_rate)/100; else k = pd->petDB->r_hungry; if( pd->pet.hungry > 75 ) { k = k >> 1; if( k <= 0 ) k = 1; } pet_set_intimate(pd, pd->pet.intimate + k); }
/*========================================== * Attempt to add an item in guild storage from inventory, then refresh it * @index : inventory idx * return * 0 : fail * 1 : succes *------------------------------------------*/ int storage_guild_storageadd(struct map_session_data* sd, int index, int amount) { struct guild_storage *stor; nullpo_ret(sd); nullpo_ret(stor=guild2storage2(sd->status.guild_id)); if( !stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE ) return 0; if( index<0 || index>=MAX_INVENTORY ) return 0; if( sd->status.inventory[index].nameid <= 0 ) return 0; if( amount < 1 || amount > sd->status.inventory[index].amount ) return 0; if( stor->lock ) { storage_guild_storageclose(sd); return 0; } if(guild_storage_additem(sd,stor,&sd->status.inventory[index],amount)==0) pc_delitem(sd,index,amount,0,4,LOG_TYPE_GSTORAGE); return 1; }
/** * Attempt to add an item in guild storage from inventory, then refresh it * @param sd : player * @param amount : number of item to delete */ void gstorage_storageadd(struct map_session_data *sd, int index, int amount) { struct guild_storage *stor; nullpo_retv(sd); nullpo_retv(stor = gstorage_get_storage(sd->status.guild_id)); if (!stor->opened || stor->opened != sd->status.char_id || stor->storage_amount > MAX_GUILD_STORAGE) return; if (index < 0 || index >= MAX_INVENTORY) return; if (!(sd->status.inventory[index].nameid)) return; if (amount < 1 || amount > sd->status.inventory[index].amount) return; if (stor->locked) { gstorage_storageclose(sd); return; } if (gstorage_additem(sd, stor, &sd->status.inventory[index], amount)) pc_delitem(sd, index, amount, 0, 4, LOG_TYPE_GSTORAGE); else { clif_storageitemremoved(sd, index, 0); clif_dropitem(sd, index, 0); } }
/** * Add an item to the storage from the inventory. * @param sd : player * @param index : inventory index to take the item from * @param amount : number of item to take * @return 0:fail, 1:success */ int storage_storageadd(struct map_session_data* sd, int index, int amount) { nullpo_ret(sd); if( sd->status.storage.storage_amount > sd->storage_size ) return 0; // storage full if( index < 0 || index >= MAX_INVENTORY ) return 0; if( sd->status.inventory[index].nameid <= 0 ) return 0; // No item on that spot if( amount < 1 || amount > sd->status.inventory[index].amount ) return 0; if( storage_additem(sd,&sd->status.inventory[index],amount) == 0 ) pc_delitem(sd,index,amount,0,4,LOG_TYPE_STORAGE); else { clif_dropitem(sd,index,0); return 0; } return 1; }
/** * Attempt to add an item in guild storage from inventory, then refresh it * @param sd : player * @param amount : number of item to delete */ void storage_guild_storageadd(struct map_session_data* sd, int index, int amount) { struct s_storage *stor; nullpo_retv(sd); nullpo_retv(stor = guild2storage2(sd->status.guild_id)); if( !stor->status || stor->amount > MAX_GUILD_STORAGE ) return; if( index < 0 || index >= MAX_INVENTORY ) return; if( sd->inventory.u.items_inventory[index].nameid == 0 ) return; if( amount < 1 || amount > sd->inventory.u.items_inventory[index].amount ) return; if( stor->lock ) { storage_guild_storageclose(sd); return; } if(storage_guild_additem(sd,stor,&sd->inventory.u.items_inventory[index],amount)) pc_delitem(sd,index,amount,0,4,LOG_TYPE_GSTORAGE); else { clif_storageitemremoved(sd,index,0); clif_dropitem(sd,index,0); } }
/** * Feed homunculus * @param sd * @param hd */ int hom_food(struct map_session_data *sd, struct homun_data *hd) { int i, foodID, emotion; nullpo_retr(1,sd); nullpo_retr(1,hd); if (hd->homunculus.vaporize) return 1; foodID = hd->homunculusDB->foodID; i = pc_search_inventory(sd,foodID); if (i < 0) { clif_hom_food(sd,foodID,0); return 1; } pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME); if ( hd->homunculus.hunger >= 91 ) { hom_decrease_intimacy(hd, 50); emotion = ET_KEK; } else if ( hd->homunculus.hunger >= 76 ) { hom_decrease_intimacy(hd, 5); emotion = ET_PROFUSELY_SWEAT; } else if ( hd->homunculus.hunger >= 26 ) { hom_increase_intimacy(hd, 75); emotion = ET_DELIGHT; } else if ( hd->homunculus.hunger >= 11 ) { hom_increase_intimacy(hd, 100); emotion = ET_DELIGHT; } else { hom_increase_intimacy(hd, 50); emotion = ET_DELIGHT; } hd->homunculus.hunger += 10; //dunno increase value for each food if(hd->homunculus.hunger > 100) hd->homunculus.hunger = 100; log_feeding(sd, LOG_FEED_HOMUNCULUS, foodID); clif_emotion(&hd->bl,emotion); clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger); clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100); clif_hom_food(sd,foodID,1); // Too much food :/ if(hd->homunculus.intimacy == 0) return hom_delete(sd->hd, ET_HUK); return 0; }
/** * Finalize hatching process and load pet to client. * @param account_id : account ID of owner * @param p : pet requesting * @param flag : 1:stop loading of pet * @return 0:success, 1:failure */ int pet_recv_petdata(int account_id,struct s_pet *p,int flag) { struct map_session_data *sd; sd = map_id2sd(account_id); if(sd == NULL) return 1; if(flag == 1) { sd->status.pet_id = 0; return 1; } if(p->incubate == 1) { int i; //Delete egg from inventory. [Skotlex] for (i = 0; i < MAX_INVENTORY; i++) { if(sd->status.inventory[i].card[0] == CARD0_PET && p->pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2])) break; } if(i >= MAX_INVENTORY) { ShowError("pet_recv_petdata: Hatching pet (%d:%s) aborted, couldn't find egg in inventory for removal!\n",p->pet_id, p->name); sd->status.pet_id = 0; return 1; } if (!pet_birth_process(sd,p)) //Pet hatched. Delete egg. pc_delitem(sd,i,1,0,0,LOG_TYPE_OTHER); } else { pet_data_init(sd,p); if(sd->pd && sd->bl.prev != NULL) { if(map_addblock(&sd->pd->bl)) return 1; clif_spawn(&sd->pd->bl); clif_send_petdata(sd,sd->pd,0,0); clif_send_petdata(sd,sd->pd,5,battle_config.pet_hair_style); clif_pet_equip_area(sd->pd); clif_send_petstatus(sd); } } return 0; }
int merc_hom_food(struct map_session_data *sd, struct homun_data *hd) { int i, foodID, emotion; char mes[255]; if(hd->homunculus.vaporize) return 1 ; foodID = hd->homunculusDB->foodID; i = pc_search_inventory(sd,foodID); if(i < 0) { clif_hom_food(sd,foodID,0); return 1; } pc_delitem(sd,i,1,0,0); if ( hd->homunculus.hunger >= 91 ) { merc_hom_decrease_intimacy(hd, 50); emotion = E_WAH; } else if ( hd->homunculus.hunger >= 76 ) { merc_hom_decrease_intimacy(hd, 5); emotion = E_SWT2; } else if ( hd->homunculus.hunger >= 26 ) { merc_hom_increase_intimacy(hd, 75); emotion = E_HO; } else if ( hd->homunculus.hunger >= 11 ) { merc_hom_increase_intimacy(hd, 100); emotion = E_HO; } else { merc_hom_increase_intimacy(hd, 50); emotion = E_HO; } hd->homunculus.hunger += 10; //dunno increase value for each food if(hd->homunculus.hunger > 100) hd->homunculus.hunger = 100; clif_emotion(&hd->bl,emotion); snprintf(mes, sizeof mes,msg_txt(908),hd->homunculus.name); clif_message(&hd->bl,mes); clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger); clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100); clif_hom_food(sd,foodID,1); // Too much food :/ if(hd->homunculus.intimacy == 0) return merc_hom_delete(sd->hd, E_OMG); return 0; }
int merc_hom_food(struct map_session_data *sd, struct homun_data *hd) { int i, foodID, emotion; if(hd->homunculus.vaporize) return 1 ; foodID = hd->homunculusDB->foodID; i = pc_search_inventory(sd,foodID); if(i < 0) { clif_hom_food(sd,foodID,0); return 1; } pc_delitem(sd,i,1,0); if ( hd->homunculus.hunger >= 91 ) { merc_hom_decrease_intimacy(hd, 50); emotion = 16; } else if ( hd->homunculus.hunger >= 76 ) { merc_hom_decrease_intimacy(hd, 5); emotion = 19; } else if ( hd->homunculus.hunger >= 26 ) { merc_hom_increase_intimacy(hd, 75); emotion = 2; } else if ( hd->homunculus.hunger >= 11 ) { merc_hom_increase_intimacy(hd, 100); emotion = 2; } else { merc_hom_increase_intimacy(hd, 50); emotion = 2; } hd->homunculus.hunger += 10; //dunno increase value for each food if(hd->homunculus.hunger > 100) hd->homunculus.hunger = 100; clif_emotion(&hd->bl,emotion) ; clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger); clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100); clif_hom_food(sd,foodID,1); // Too much food :/ if(hd->homunculus.intimacy == 0) return merc_hom_delete(sd->hd, 23); //omg return 0; }
/// Invoked (from char-server) when a party member leaves the party. int party_member_withdraw(int party_id, uint32 account_id, uint32 char_id) { struct map_session_data* sd = map_id2sd(account_id); struct party_data* p = party_search(party_id); if( p ) { int i; ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id ); if( i < MAX_PARTY ) { clif_party_withdraw(p,sd,account_id,p->party.member[i].name,0x0); memset(&p->party.member[i], 0, sizeof(p->party.member[0])); memset(&p->data[i], 0, sizeof(p->data[0])); p->party.count--; party_check_state(p); } } if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id ) { #ifdef BOUND_ITEMS int idxlist[MAX_INVENTORY]; //or malloc to reduce consumtion int j,i; party_trade_bound_cancel(sd); j = pc_bound_chk(sd,BOUND_PARTY,idxlist); for(i = 0; i < j; i++) pc_delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_BOUND_REMOVAL); #endif sd->status.party_id = 0; clif_charnameupdate(sd); //Update name display [Skotlex] //TODO: hp bars should be cleared too if( p->instance_id ) { int16 m = sd->bl.m; if( map[m].instance_id ) { // User was on the instance map if( map[m].save.map ) pc_setpos(sd, map[m].save.map, map[m].save.x, map[m].save.y, CLR_TELEPORT); else pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT); } } } return 0; }
int mail_removeitem(struct map_session_data *sd, short flag) { nullpo_ret(sd); if( sd->mail.amount ) { if (flag) // Item send pc_delitem(sd, sd->mail.index, sd->mail.amount, 1, 0, LOG_TYPE_MAIL); else clif_additem(sd, sd->mail.index, sd->mail.amount, 0); } sd->mail.nameid = 0; sd->mail.index = 0; sd->mail.amount = 0; return 1; }
// 作成可否 int guild_created(int account_id,int guild_id) { struct map_session_data *sd=map_id2sd(account_id); if(sd==NULL) return 0; if(!guild_id) { clif_guild_created(sd,2); // 作成失敗(同名ギルド存在) return 0; } //struct guild *g; sd->status.guild_id=guild_id; clif_guild_created(sd,0); if(battle_config.guild_emperium_check) pc_delitem(sd,pc_search_inventory(sd,714),1,0,0); // エンペリウム消耗 return 0; }
/*========================================== * 離婚情報同期要求 *------------------------------------------*/ int chrif_divorce(int char_id, int partner_id) { struct map_session_data* sd; int i; if (!char_id || !partner_id || (sd = map_charid2sd(partner_id)) == NULL || sd->status.partner_id != char_id) return 0; //離婚(相方は既にキャラが消えている筈なので) sd->status.partner_id = 0; //相方の結婚指輪を剥奪 for(i = 0; i < MAX_INVENTORY; i++) if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F) pc_delitem(sd, i, 1, 0); return 0; }
/*========================================== * カプラ倉庫へ入れる *------------------------------------------ */ void storage_storageadd(struct map_session_data *sd, int idx, int amount) { struct storage *stor; nullpo_retv(sd); nullpo_retv(stor = (struct storage *)numdb_search(storage_db,sd->status.account_id)); if(!stor->storage_status) return; if(idx < 0 || idx >= MAX_INVENTORY) return; if(itemdb_isstorageable(sd->status.inventory[idx].nameid) == 0) return; if(amount < 1 || amount > sd->status.inventory[idx].amount) return; if(storage_additem(sd, stor, &sd->status.inventory[idx], amount) == 0) pc_delitem(sd, idx, amount, 0, 4); return; }
int mail_removeitem(struct map_session_data *sd, short flag) { nullpo_ret(sd); if( sd->mail.amount ) { if (flag) { // Item send log_pick_pc(sd, LOG_TYPE_MAIL, sd->mail.nameid, -sd->mail.amount, &sd->status.inventory[sd->mail.index]); pc_delitem(sd, sd->mail.index, sd->mail.amount, 1, 0); } else clif_additem(sd, sd->mail.index, sd->mail.amount, 0); } sd->mail.nameid = 0; sd->mail.index = 0; sd->mail.amount = 0; return 1; }
int ext_storage_add(struct map_session_data *sd,int index,int amount) { nullpo_ret(sd); if( sd->status.ext_storage.storage_amount > MAX_EXTRA_STORAGE ) return 0; // storage full if( index < 0 || index >= MAX_INVENTORY ) return 0; if( sd->status.inventory[index].nameid <= 0 ) return 0; //No item on that spot if( amount < 1 || amount > sd->status.inventory[index].amount ) return 0; if( ext_storage_additem(sd,&sd->status.inventory[index],amount) == 0 ) pc_delitem(sd,index,amount,0,4); return 1; }
/** * Apply a pet's equipment. * @param sd : player requesting * @param index : index value of item * @return 0:success, 1:failure */ int pet_equipitem(struct map_session_data *sd,int index) { struct pet_data *pd; unsigned short nameid; nullpo_retr(1, sd); pd = sd->pd; if (!pd) return 1; nameid = sd->status.inventory[index].nameid; if(pd->petDB->AcceID == 0 || nameid != pd->petDB->AcceID || pd->pet.equip != 0) { clif_equipitemack(sd,0,0,0); return 1; } pc_delitem(sd,index,1,0,0,LOG_TYPE_OTHER); pd->pet.equip = nameid; status_set_viewdata(&pd->bl, pd->pet.class_); //Updates view_data. clif_pet_equip_area(pd); if (battle_config.pet_equip_required) { // Skotlex: start support timers if need unsigned int tick = gettick(); if (pd->s_skill && pd->s_skill->timer == INVALID_TIMER) { if (pd->s_skill->id) pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_skill_support_timer, sd->bl.id, 0); else pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_heal_timer, sd->bl.id, 0); } if (pd->bonus && pd->bonus->timer == INVALID_TIMER) pd->bonus->timer=add_timer(tick+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0); } return 0; }
int mail_removeitem(struct map_session_data *sd, short flag) { nullpo_retr(0,sd); if( sd->mail.amount ) { if (flag) { // Item send if(log_config.enable_logs&0x2000) log_pick_pc(sd, "E", sd->mail.nameid, -sd->mail.amount, &sd->status.inventory[sd->mail.index]); pc_delitem(sd, sd->mail.index, sd->mail.amount, 1); } else clif_additem(sd, sd->mail.index, sd->mail.amount, 0); } sd->mail.nameid = 0; sd->mail.index = 0; sd->mail.amount = 0; return 1; }
int merc_hom_dead(struct homun_data *hd, int flag) { struct map_session_data *sd = hd->master; struct s_homunculus *hom; struct item tmp_item; int rate; sd = hd->master; hd = sd->hd; hom = &hd->homunculus; rate =(hd->homunculus.class_) + 5530; merc_save(hd); sd = hd->master; pc_delitem(sd, pc_search_inventory(sd,690), 1, 0, 0); //sd->status.hom_id = 0; sd->status.lock = 0; merc_hom_hungry_timer_delete(hd); hd->homunculus.hp = 0; hom->char_id = sd->status.char_id; memset(&tmp_item,0,sizeof(tmp_item)); tmp_item.nameid = rate; tmp_item.identify = 1; tmp_item.card[0] = CARD0_HUN; tmp_item.card[1] = GetWord(hd->homunculus.hom_id,0); tmp_item.card[2] = GetWord(hd->homunculus.hom_id,1); tmp_item.card[3] = 1; if((flag = pc_additem(sd,&tmp_item,1))) { clif_additem(sd,0,0,flag); map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); } hd->homunculus.hp = 0; hd->homunculus.intimacy = 0; return unit_remove_map(&hd->bl,CLR_OUTSIGHT); }
/*========================================== * Žæˆø‹–‘ø(trade‰Ÿ‚µ) *------------------------------------------*/ void trade_tradecommit (struct map_session_data *sd) { struct map_session_data *tsd; int trade_i; int flag; if (!sd->state.trading || !sd->state.deal_locked) //Locked should be 1 (pressed ok) before you can press trade. return; if ( (tsd = map_id2sd (sd->trade_partner)) == NULL) { trade_tradecancel (sd); return; } sd->state.deal_locked = 2; if (tsd->state.deal_locked < 2) return; //Not yet time for trading. //Now is a good time (to save on resources) to check that the trade can indeed be made and it's not exploitable. // check exploit (trade more items that you have) if (impossible_trade_check (sd)) { trade_tradecancel (sd); return; } // check exploit (trade more items that you have) if (impossible_trade_check (tsd)) { trade_tradecancel (tsd); return; } // check for full inventory (can not add traded items) if (!trade_check (sd, tsd)) { // check the both players trade_tradecancel (sd); return; } // trade is accepted and correct. for (trade_i = 0; trade_i < 10; trade_i++) { int n; if (sd->deal.item[trade_i].amount) { n = sd->deal.item[trade_i].index; flag = pc_additem (tsd, &sd->status.inventory[n], sd->deal.item[trade_i].amount, LOG_TYPE_TRADE); if (flag == 0) pc_delitem (sd, n, sd->deal.item[trade_i].amount, 1, 6, LOG_TYPE_TRADE); else clif_additem (sd, n, sd->deal.item[trade_i].amount, 0); sd->deal.item[trade_i].index = 0; sd->deal.item[trade_i].amount = 0; } if (tsd->deal.item[trade_i].amount) { n = tsd->deal.item[trade_i].index; flag = pc_additem (sd, &tsd->status.inventory[n], tsd->deal.item[trade_i].amount, LOG_TYPE_TRADE); if (flag == 0) pc_delitem (tsd, n, tsd->deal.item[trade_i].amount, 1, 6, LOG_TYPE_TRADE); else clif_additem (tsd, n, tsd->deal.item[trade_i].amount, 0); tsd->deal.item[trade_i].index = 0; tsd->deal.item[trade_i].amount = 0; } } if (sd->deal.zeny) { pc_payzeny (sd , sd->deal.zeny, LOG_TYPE_TRADE, tsd); pc_getzeny (tsd, sd->deal.zeny, LOG_TYPE_TRADE, sd); sd->deal.zeny = 0; sd->deal.zeny = 0; } if (tsd->deal.zeny) { pc_payzeny (tsd, tsd->deal.zeny, LOG_TYPE_TRADE, sd); pc_getzeny (sd , tsd->deal.zeny, LOG_TYPE_TRADE, tsd); tsd->deal.zeny = 0; } sd->state.deal_locked = 0; sd->trade_partner = 0; sd->state.trading = 0; tsd->state.deal_locked = 0; tsd->trade_partner = 0; tsd->state.trading = 0; clif_tradecompleted (sd, 0); clif_tradecompleted (tsd, 0); // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players if (save_settings & 1) { chrif_save (sd, 0); chrif_save (tsd, 0); } }
void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count) { int zeny = 0; unsigned int i, weight, listidx, k; struct map_session_data* pl_sd; if( count == 0 ) {// nothing to do return; } if( !battle_config.feature_buying_store || pc_istrading(sd) ) {// not allowed to sell clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } if( !pc_can_give_items(pc_isGM(sd)) ) {// custom: GM is not allowed to sell clif_displaymessage(sd->fd, msg_txt(246)); clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } if( ( pl_sd = map_id2sd(account_id) ) == NULL || !pl_sd->state.buyingstore || pl_sd->buyer_id != buyer_id ) {// not online, not buying or not same store clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } if( !searchstore_queryremote(sd, account_id) && ( sd->bl.m != pl_sd->bl.m || !check_distance_bl(&sd->bl, &pl_sd->bl, AREA_SIZE) ) ) {// out of view range clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; } searchstore_clearremote(sd); if( pl_sd->status.zeny < pl_sd->buyingstore.zenylimit ) {// buyer lost zeny in the mean time? fix the limit pl_sd->buyingstore.zenylimit = pl_sd->status.zeny; } weight = pl_sd->weight; // check item list for( i = 0; i < count; i++ ) {// itemlist: <index>.W <name id>.W <amount>.W unsigned short nameid, amount; int index; index = RBUFW(itemlist,i*6+0)-2; nameid = RBUFW(itemlist,i*6+2); amount = RBUFW(itemlist,i*6+4); if( i ) {// duplicate check. as the client does this too, only malicious intent should be caught here ARR_FIND( 0, i, k, RBUFW(itemlist,k*6+0)-2 == index ); if( k != i ) {// duplicate ShowWarning("buyingstore_trade: Found duplicate item on selling list (prevnameid=%hu, prevamount=%hu, nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", RBUFW(itemlist,k*6+2), RBUFW(itemlist,k*6+4), nameid, amount, sd->status.account_id, sd->status.char_id); clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } } if( index < 0 || index >= ARRAYLENGTH(sd->status.inventory) || sd->inventory_data[index] == NULL || sd->status.inventory[index].nameid != nameid || sd->status.inventory[index].amount < amount ) {// invalid input clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } if( sd->status.inventory[index].expire_time || !itemdb_cantrade(&sd->status.inventory[index], pc_isGM(sd), pc_isGM(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) {// non-tradable item clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid ); if( listidx == pl_sd->buyingstore.slots || pl_sd->buyingstore.items[listidx].amount == 0 ) {// there is no such item or the buyer has already bought all of them clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } if( pl_sd->buyingstore.items[listidx].amount < amount ) {// buyer does not need that much of the item clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_COUNT, nameid); return; } if( pc_checkadditem(pl_sd, nameid, amount) == ADDITEM_OVERAMOUNT ) {// buyer does not have enough space for this item clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } if( amount*(unsigned int)sd->inventory_data[index]->weight > pl_sd->max_weight-weight ) {// normally this is not supposed to happen, as the total weight is // checked upon creation, but the buyer could have gained items clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; } weight+= amount*sd->inventory_data[index]->weight; if( amount*pl_sd->buyingstore.items[listidx].price > pl_sd->buyingstore.zenylimit-zeny ) {// buyer does not have enough zeny clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_ZENY, nameid); return; } zeny+= amount*pl_sd->buyingstore.items[listidx].price; } // process item list for( i = 0; i < count; i++ ) {// itemlist: <index>.W <name id>.W <amount>.W unsigned short nameid, amount; int index; index = RBUFW(itemlist,i*6+0)-2; nameid = RBUFW(itemlist,i*6+2); amount = RBUFW(itemlist,i*6+4); ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid ); zeny = amount*pl_sd->buyingstore.items[listidx].price; // log log_pick_pc(sd, LOG_TYPE_BUYING_STORE, nameid, -((int)amount), &sd->status.inventory[index]); log_pick_pc(pl_sd, LOG_TYPE_BUYING_STORE, nameid, amount, &sd->status.inventory[index]); log_zeny(sd, LOG_TYPE_BUYING_STORE, pl_sd, zeny); // move item pc_additem(pl_sd, &sd->status.inventory[index], amount); pc_delitem(sd, index, amount, 1, 0); pl_sd->buyingstore.items[listidx].amount-= amount; // pay up pc_payzeny(pl_sd, zeny); pc_getzeny(sd, zeny); pl_sd->buyingstore.zenylimit-= zeny; // notify clients clif_buyingstore_delete_item(sd, index, amount, pl_sd->buyingstore.items[listidx].price); clif_buyingstore_update_item(pl_sd, nameid, amount); } // check whether or not there is still something to buy ARR_FIND( 0, pl_sd->buyingstore.slots, i, pl_sd->buyingstore.items[i].amount != 0 ); if( i == pl_sd->buyingstore.slots ) {// everything was bought clif_buyingstore_trade_failed_buyer(pl_sd, BUYINGSTORE_TRADE_BUYER_NO_ITEMS); } else if( pl_sd->buyingstore.zenylimit == 0 ) {// zeny limit reached clif_buyingstore_trade_failed_buyer(pl_sd, BUYINGSTORE_TRADE_BUYER_ZENY); } else {// continue buying return; } // cannot continue buying buyingstore_close(pl_sd); // remove auto-trader if( pl_sd->state.autotrade ) { map_quit(pl_sd); } }
/*========================================== * Purchase item(s) from a shop *------------------------------------------*/ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const uint8* data, int count) { int i, j, cursor, w, new_ = 0, blank, vend_list[MAX_VENDING]; double z; struct s_vending vending[MAX_VENDING]; // against duplicate packets struct map_session_data* vsd = map_id2sd(aid); char output[256]; nullpo_retv(sd); if( vsd == NULL || !vsd->state.vending || vsd->bl.id == sd->bl.id ) return; // invalid shop if( vsd->vender_id != uid ) {// shop has changed clif_buyvending(sd, 0, 0, 6); // store information was incorrect return; } if( !searchstore_queryremote(sd, aid) && ( sd->bl.m != vsd->bl.m || !check_distance_bl(&sd->bl, &vsd->bl, AREA_SIZE) ) ) return; // shop too far away searchstore_clearremote(sd); if( count < 1 || count > MAX_VENDING || count > vsd->vend_num ) return; // invalid amount of purchased items blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory // duplicate item in vending to check hacker with multiple packets memcpy(&vending, &vsd->vending, sizeof(vsd->vending)); // copy vending list // some checks z = 0.; // zeny counter w = 0; // weight counter for( i = 0; i < count; i++ ) { short amount = *(uint16*)(data + 4*i + 0); short idx = *(uint16*)(data + 4*i + 2); idx -= 2; if( amount <= 0 ) return; // check of item index in the cart if( idx < 0 || idx >= MAX_CART ) return; ARR_FIND( 0, vsd->vend_num, j, vsd->vending[j].index == idx ); if( j == vsd->vend_num ) return; //picked non-existing item else vend_list[i] = j; z += ((double)vsd->vending[j].value * (double)amount); if( !vsd->vend_coin || vsd->vend_coin == battle_config.vending_zeny_id ) { // Normal Vending - Zeny Option if( z > (double)sd->status.zeny || z < 0. || z > (double)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 ) { clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow return; } } else if( battle_config.vending_cash_id && vsd->vend_coin == battle_config.vending_cash_id ) { // Cash Shop if( z > (double)sd->cashPoints || z < 0. || z > (double)MAX_ZENY ) { sprintf(output,msg_txt(915),itemdb_jname(vsd->vend_coin)); clif_displaymessage(sd->fd,output); return; } if( z + (double)vsd->cashPoints > (double)MAX_ZENY && !battle_config.vending_over_max ) { sprintf(output,msg_txt(916),itemdb_jname(vsd->vend_coin)); clif_displaymessage(sd->fd,output); return; } } w += itemdb_weight(vsd->status.cart[idx].nameid) * amount; if( w + sd->weight > sd->max_weight ) { clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight return; } //Check to see if cart/vend info is in sync. if( vending[j].amount > vsd->status.cart[idx].amount ) vending[j].amount = vsd->status.cart[idx].amount; // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples). // here, we check cumulative amounts if( vending[j].amount < amount ) { // send more quantity is not a hack (an other player can have buy items just before) clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity return; } vending[j].amount -= amount; switch( pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount) ) { case ADDITEM_EXIST: break; //We'd add this item to the existing one (in buyers inventory) case ADDITEM_NEW: new_++; if (new_ > blank) return; //Buyer has no space in his inventory break; case ADDITEM_OVERAMOUNT: return; //too many items } } // Payments if( !vsd->vend_coin || vsd->vend_coin == battle_config.vending_zeny_id ) { if( log_config.zeny > 0 ) log_zeny(vsd, "V", sd, (int)z); //Logs (V)ending Zeny [Lupus] pc_payzeny(sd, (int)z); if( battle_config.vending_tax || (vsd->state.autotrade && battle_config.at_tax && !pc_isPremium(vsd)) ) z -= z * ((battle_config.vending_tax + ((vsd->state.autotrade && !pc_isPremium(vsd)) ? battle_config.at_tax : 0)) / 10000.); pc_getzeny(vsd, (int)z); } else if( battle_config.vending_cash_id && vsd->vend_coin == battle_config.vending_cash_id ) { pc_paycash(sd,(int)z,0); pc_getcash(vsd,(int)z,0); } else { if( z < 0. || (i = pc_search_inventory(sd,vsd->vend_coin)) < 0 || z > (double)sd->status.inventory[i].amount ) { sprintf(output,msg_txt(915),itemdb_jname(vsd->vend_coin)); clif_displaymessage(sd->fd,output); return; } switch( pc_checkadditem(vsd,vsd->vend_coin,(int)z) ) { case ADDITEM_NEW: if( pc_inventoryblank(vsd) > 0 ) break; case ADDITEM_OVERAMOUNT: sprintf(output,msg_txt(916),itemdb_jname(vsd->vend_coin)); clif_displaymessage(sd->fd,output); return; } pc_additem(vsd,&sd->status.inventory[i],(int)z); pc_delitem(sd,i,(int)z,0,6); } for( i = 0; i < count; i++ ) { short amount = *(uint16*)(data + 4*i + 0); short idx = *(uint16*)(data + 4*i + 2); idx -= 2; //Logs sold (V)ending items [Lupus] if(log_config.enable_logs&0x4) { log_pick_pc(vsd, "V", vsd->status.cart[idx].nameid, -amount, &vsd->status.cart[idx], vsd->status.cart[idx].serial ); log_pick_pc( sd, "V", vsd->status.cart[idx].nameid, amount, &vsd->status.cart[idx], vsd->status.cart[idx].serial ); } // vending item pc_additem(sd, &vsd->status.cart[idx], amount); vsd->vending[vend_list[i]].amount -= amount; pc_cart_delitem(vsd, idx, amount, 0); clif_vendingreport(vsd, idx, amount); //print buyer's name if( battle_config.buyer_name ) { char temp[256]; sprintf(temp, msg_txt(265), sd->status.name); clif_disp_onlyself(vsd,temp,strlen(temp)); } } // compact the vending list for( i = 0, cursor = 0; i < vsd->vend_num; i++ ) { if( vsd->vending[i].amount == 0 ) continue; if( cursor != i ) // speedup { vsd->vending[cursor].index = vsd->vending[i].index; vsd->vending[cursor].amount = vsd->vending[i].amount; vsd->vending[cursor].value = vsd->vending[i].value; } cursor++; } vsd->vend_num = cursor; //Always save BOTH: buyer and customer if( save_settings&2 ) { chrif_save(sd,0); chrif_save(vsd,0); } //check for @AUTOTRADE users [durf] if( vsd->state.autotrade ) { //see if there is anything left in the shop ARR_FIND( 0, vsd->vend_num, i, vsd->vending[i].amount > 0 ); if( i == vsd->vend_num ) { //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex] vending_closevending(vsd); map_quit(vsd); //They have no reason to stay around anymore, do they? } } }
/*========================================== * 取引許諾(trade押し) *------------------------------------------ */ void trade_tradecommit(struct map_session_data *sd) { struct map_session_data *target_sd; nullpo_retv(sd); target_sd = map_id2sd(sd->trade.partner); if(target_sd && target_sd->bl.prev) { if (sd->state.deal_locked >= 1 && target_sd->state.deal_locked >= 1) { // both have pressed 'ok' if (sd->state.deal_locked < 2) // set locked to 2 sd->state.deal_locked = 2; if(target_sd->state.deal_locked == 2) { // the other one pressed 'trade' too int trade_i, idx, flag; // checks quantity of items if (!trade_check(sd, target_sd)) { // this function do like the real trade, but with virtual inventories trade_tradecancel(sd); return; } // check zenys if (sd->trade.zeny > sd->status.zeny || target_sd->trade.zeny > target_sd->status.zeny || sd->status.zeny - sd->trade.zeny + target_sd->trade.zeny > MAX_ZENY || target_sd->status.zeny - target_sd->trade.zeny + sd->trade.zeny > MAX_ZENY) { trade_tradecancel(sd); return; } // do trade // ---- of items for(trade_i = 0; trade_i < MAX_DEAL_ITEMS; trade_i++) { if(sd->trade.item_amount[trade_i] != 0) { idx = sd->trade.item_index[trade_i] - 2; if (itemdb_isdropable(sd->status.inventory[idx].nameid)) { flag = pc_additem(target_sd, &sd->status.inventory[idx], sd->trade.item_amount[trade_i]); if(flag==0) pc_delitem(sd, idx, sd->trade.item_amount[trade_i], 1, 0); else clif_additem(sd, idx, sd->trade.item_amount[trade_i], 0); } sd->trade.item_amount[trade_i] = 0; } sd->trade.item_index[trade_i] = 0; if(target_sd->trade.item_amount[trade_i] != 0) { idx = target_sd->trade.item_index[trade_i] - 2; if (itemdb_isdropable(target_sd->status.inventory[idx].nameid)) { flag = pc_additem(sd, &target_sd->status.inventory[idx], target_sd->trade.item_amount[trade_i]); if(flag==0) pc_delitem(target_sd, idx, target_sd->trade.item_amount[trade_i], 1, 0); else clif_additem(target_sd, idx, target_sd->trade.item_amount[trade_i], 0); } target_sd->trade.item_amount[trade_i] = 0; } target_sd->trade.item_index[trade_i] = 0; } // ---- of zenys if(sd->trade.zeny || target_sd->trade.zeny) { sd->status.zeny = sd->status.zeny - sd->trade.zeny + target_sd->trade.zeny; target_sd->status.zeny = target_sd->status.zeny - target_sd->trade.zeny + sd->trade.zeny; clif_updatestatus(sd, SP_ZENY); clif_updatestatus(target_sd, SP_ZENY); } sd->trade.zeny = 0; target_sd->trade.zeny = 0; // clean up variables sd->state.deal_locked = 0; sd->state.deal_mode = 0; sd->trade.partner = 0; target_sd->state.deal_locked = 0; target_sd->state.deal_mode = 0; target_sd->trade.partner = 0; clif_tradecompleted(sd,0); clif_tradecompleted(target_sd,0); // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players chrif_save(sd,0); chrif_save(target_sd,0); } } } else { trade_tradecancel(sd); } return; }