/*========================================== * join an existing chatroom *------------------------------------------*/ int chat_joinchat(struct map_session_data* sd, int chatid, const char* pass) { struct chat_data* cd; nullpo_retr(0, sd); cd = (struct chat_data*)map_id2bl(chatid); if( cd == NULL || cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->vender_id || sd->chatID || cd->users >= cd->limit ) { clif_joinchatfail(sd,0); return 0; } if( !cd->pub && strncmp(pass, cd->pass, sizeof(cd->pass)) != 0 && !(battle_config.gm_join_chat && pc_isGM(sd) >= battle_config.gm_join_chat) ) { clif_joinchatfail(sd,1); return 0; } pc_stop_walking(sd,1); cd->usersd[cd->users] = sd; cd->users++; pc_setchatid(sd,cd->bl.id); clif_joinchatok(sd,cd); // 新たに参加した人には全員のリスト clif_addchat(cd,sd); // 既に中に居た人には追加した人の報告 clif_dispchat(cd,0); // 周囲の人には人数変化報告 chat_triggerevent(cd); // イベント return 0; }
/*========================================== * join an existing chatroom *------------------------------------------*/ int chat_joinchat(struct map_session_data* sd, int chatid, const char* pass) { struct chat_data* cd; nullpo_ret(sd); cd = (struct chat_data*)map_id2bl(chatid); if( cd == NULL || cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->state.vending || sd->state.buyingstore || sd->chatID || cd->users >= cd->limit ) { clif_joinchatfail(sd,0); return 0; } if( !cd->pub && strncmp(pass, cd->pass, sizeof(cd->pass)) != 0 && !(battle_config.gm_join_chat && pc_isGM(sd) >= battle_config.gm_join_chat) ) { clif_joinchatfail(sd,1); return 0; } if( cd->upperBool && pc_jobid2mapid(sd->status.class_)&JOBL_UPPER ) { clif_joinchatfail(sd,7); return 0; } if( sd->status.base_level < cd->minLvl || sd->status.base_level > cd->maxLvl ) { if(sd->status.base_level < cd->minLvl) clif_joinchatfail(sd,5); else clif_joinchatfail(sd,6); return 0; } if( sd->status.zeny < cd->zeny ) { clif_joinchatfail(sd,4); return 0; } pc_stop_walking(sd,1); cd->usersd[cd->users] = sd; cd->users++; pc_setchatid(sd,cd->bl.id); clif_joinchatok(sd,cd); // 新たに参加した人には全員のリスト clif_addchat(cd,sd); // 既に中に居た人には追加した人の報告 clif_dispchat(cd,0); // 周囲の人には人数変化報告 chat_triggerevent(cd); // イベント return 0; }
/** * Join an existing chat room. * @param sd : player requesting * @param chatid : ID of the chat room * @param pass : password of chat room * @return 0 */ int chat_joinchat(struct map_session_data* sd, int chatid, const char* pass) { struct chat_data* cd; nullpo_ret(sd); cd = (struct chat_data*)map_id2bl(chatid); if( cd == NULL || cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->state.vending || sd->state.buyingstore || sd->chatID || ((cd->owner->type == BL_NPC) ? cd->users+1 : cd->users) >= cd->limit ) { clif_joinchatfail(sd,0); return 0; } if( !cd->pub && strncmp(pass, cd->pass, sizeof(cd->pass)) != 0 && !pc_has_permission(sd, PC_PERM_JOIN_ALL_CHAT) ) { clif_joinchatfail(sd,1); return 0; } if( sd->status.base_level < cd->minLvl || sd->status.base_level > cd->maxLvl ) { if(sd->status.base_level < cd->minLvl) clif_joinchatfail(sd,5); else clif_joinchatfail(sd,6); return 0; } if( sd->status.zeny < cd->zeny ) { clif_joinchatfail(sd,4); return 0; } if( cd->owner->type != BL_NPC && idb_exists(cd->kick_list,sd->status.char_id) ) { clif_joinchatfail(sd,2);//You have been kicked out of the room. return 0; } pc_stop_walking(sd,1); cd->usersd[cd->users] = sd; cd->users++; pc_setchatid(sd,cd->bl.id); clif_joinchatok(sd, cd); //To the person who newly joined the list of all clif_addchat(cd, sd); //Reports To the person who already in the chat clif_dispchat(cd, 0); //Reported number of changes to the people around if (cd->owner->type == BL_PC) achievement_update_objective(map_id2sd(cd->owner->id), AG_CHAT_COUNT, 1, cd->users); chat_triggerevent(cd); //Event return 0; }
/*========================================== * join an existing chatroom *------------------------------------------*/ bool chat_joinchat(struct map_session_data* sd, int chatid, const char* pass) { struct chat_data* cd; nullpo_ret(sd); nullpo_ret(pass); cd = map->id2cd(chatid); if (cd == NULL || cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->state.vending || sd->state.buyingstore || sd->chat_id != 0 || ((cd->owner->type == BL_NPC) ? cd->users+1 : cd->users) >= cd->limit ) { clif->joinchatfail(sd,0); // room full return false; } if( !cd->pub && strncmp(pass, cd->pass, sizeof(cd->pass)) != 0 && !pc_has_permission(sd, PC_PERM_JOIN_ALL_CHAT) ) { clif->joinchatfail(sd,1); // wrong password return false; } if( sd->status.base_level < cd->minLvl || sd->status.base_level > cd->maxLvl ) { if(sd->status.base_level < cd->minLvl) clif->joinchatfail(sd,5); // too low level else clif->joinchatfail(sd,6); // too high level return false; } if( sd->status.zeny < cd->zeny ) { clif->joinchatfail(sd,4); // not enough zeny return false; } if( cd->owner->type != BL_NPC && idb_exists(cd->kick_list,sd->status.char_id) ) { clif->joinchatfail(sd,2); // You have been kicked out of the room. return false; } pc_stop_walking(sd, STOPWALKING_FLAG_FIXPOS); cd->usersd[cd->users] = sd; cd->users++; pc_setchatid(sd,cd->bl.id); clif->joinchatok(sd, cd); //To the person who newly joined the list of all clif->addchat(cd, sd); //Reports To the person who already in the chat clif->dispchat(cd, 0); //Reported number of changes to the people around chat->trigger_event(cd); //Event return true; }
/** * Player chat room creation. * @param sd : player requesting * @param title : title of chat room * @param pass : password for chat room * @param limit : amount allowed to enter * @param pub : public or private * @return 0 */ int chat_createpcchat(struct map_session_data* sd, const char* title, const char* pass, int limit, bool pub) { struct chat_data* cd; nullpo_ret(sd); if( sd->chatID ) return 0; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex] if( sd->state.vending || sd->state.buyingstore ) // not chat, when you already have a store open return 0; if( map_getmapflag(sd->bl.m, MF_NOCHAT) ) { clif_displaymessage(sd->fd, msg_txt(sd,281)); return 0; //Can't create chatrooms on this map. } if( map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNOCHAT) ) { clif_displaymessage (sd->fd, msg_txt(sd,665)); return 0; } pc_stop_walking(sd,1); cd = chat_createchat(&sd->bl, title, pass, limit, pub, 0, "", 0, 1, MAX_LEVEL); if( cd ) { cd->users = 1; cd->usersd[0] = sd; pc_setchatid(sd,cd->bl.id); pc_stop_attack(sd); clif_createchat(sd,0); clif_dispchat(cd,0); if (status_isdead(&sd->bl)) achievement_update_objective(sd, AG_CHAT_DYING, 1, 1); else achievement_update_objective(sd, AG_CHAT_CREATE, 1, 1); } else clif_createchat(sd,1); return 0; }
/*========================================== * player chatroom creation *------------------------------------------*/ int chat_createpcchat(struct map_session_data* sd, const char* title, const char* pass, int limit, bool pub) { struct chat_data* cd; nullpo_ret(sd); if( sd->chatID ) return 0; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex] if( sd->state.vending || sd->state.buyingstore ) {// not chat, when you already have a store open return 0; } if( map[sd->bl.m].flag.nochat ) { clif_displaymessage(sd->fd, msg_txt(281)); return 0; //Can't create chatrooms on this map. } if( map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNOCHAT) ) { clif_displaymessage (sd->fd, "Can't create chat rooms in this Area."); return 0; } pc_stop_walking(sd,1); cd = chat_createchat(&sd->bl, title, pass, limit, pub, 0, "", 0, 1, MAX_LEVEL, 0); if( cd ) { cd->users = 1; cd->usersd[0] = sd; pc_setchatid(sd,cd->bl.id); clif_createchat(sd,0); clif_dispchat(cd,0); } else clif_createchat(sd,1); return 0; }
/*========================================== * チャットルーム作成 *------------------------------------------ */ int chat_createchat(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen) { struct chat_data *cd; nullpo_retr(0, sd); if (sd->chatID) return 0; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex] pc_stop_walking(sd,1); cd = (struct chat_data *) aCalloc(1,sizeof(struct chat_data)); cd->limit = limit; cd->pub = pub; cd->users = 1; memcpy(cd->pass,pass,8); cd->pass[7]= '\0'; //Overflow check... [Skotlex] if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1; memcpy(cd->title,title,titlelen); cd->title[titlelen]=0; cd->owner = (struct block_list **)(&cd->usersd[0]); cd->usersd[0] = sd; cd->bl.m = sd->bl.m; cd->bl.x = sd->bl.x; cd->bl.y = sd->bl.y; cd->bl.type = BL_CHAT; cd->bl.id = map_addobject(&cd->bl); if(cd->bl.id==0){ clif_createchat(sd,1); aFree(cd); return 0; } pc_setchatid(sd,cd->bl.id); clif_createchat(sd,0); clif_dispchat(cd,0); return 0; }
/*========================================== * player chatroom creation *------------------------------------------*/ bool chat_createpcchat(struct map_session_data* sd, const char* title, const char* pass, int limit, bool pub) { struct chat_data* cd; nullpo_ret(sd); nullpo_ret(title); nullpo_ret(pass); if( sd->chatID ) return false; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex] if( sd->state.vending || sd->state.buyingstore ) {// not chat, when you already have a store open return false; } if( map->list[sd->bl.m].flag.nochat ) { clif->message(sd->fd, msg_sd(sd,281)); return false; //Can't create chatrooms on this map. } if (map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y, CELL_CHKNOCHAT) ) { clif->message (sd->fd, msg_sd(sd,865)); // "Can't create chat rooms in this area." return false; } pc_stop_walking(sd, STOPWALKING_FLAG_FIXPOS); cd = chat->create(&sd->bl, title, pass, limit, pub, 0, "", 0, 1, MAX_LEVEL); if( cd ) { cd->users = 1; cd->usersd[0] = sd; pc_setchatid(sd,cd->bl.id); pc_stop_attack(sd); clif->createchat(sd,0); // 0 = success clif->dispchat(cd,0); return true; } clif->createchat(sd,1); // 1 = Room limit exceeded return false; }
/*========================================== * 既存チャットルームに参加 *------------------------------------------ */ int chat_joinchat (struct map_session_data *sd, int chatid, char* pass) { struct chat_data *cd; nullpo_retr(0, sd); cd = (struct chat_data*)map_id2bl(chatid); //No need for a nullpo check. The chatid was sent by the client, if they lag or mess with the packet //a wrong chat id can be received. [Skotlex] if (cd == NULL) return 1; if (cd->bl.m != sd->bl.m || sd->vender_id || sd->chatID || cd->limit <= cd->users) { clif_joinchatfail(sd,0); return 0; } //Allows Gm access to protected room with any password they want by valaris if ((cd->pub == 0 && strncmp(pass, (char *)cd->pass, 8) && (pc_isGM(sd) < battle_config.gm_join_chat || !battle_config.gm_join_chat)) || chatid == (int)sd->chatID) //Double Chat fix by Alex14, thx CHaNGeTe { clif_joinchatfail(sd,1); return 0; } pc_stop_walking(sd,1); cd->usersd[cd->users] = sd; cd->users++; pc_setchatid(sd,cd->bl.id); clif_joinchatok(sd,cd); // 新たに参加した人には全員のリスト clif_addchat(cd,sd); // 既に中に居た人には追加した人の報告 clif_dispchat(cd,0); // 周囲の人には人数変化報告 chat_triggerevent(cd); // イベント return 0; }
/*========================================== * Open shop * data := {<index>.w <amount>.w <value>.l}[count] *------------------------------------------*/ void vending_openvending(struct map_session_data* sd, const char* message, bool flag, const uint8* data, int count) { int i, j, char_id; int vending_skill_lvl; nullpo_retv(sd); if( !flag ) // cancelled return; // nothing to do if (pc_istrading(sd)) return; // can't have 2 shops at once vending_skill_lvl = pc_checkskill(sd, MC_VENDING); // skill level and cart check if( !vending_skill_lvl || !pc_iscarton(sd) ) { clif_skill_fail(sd, MC_VENDING, 0, 0); return; } // check number of items in shop if( count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl ) { // invalid item count clif_skill_fail(sd, MC_VENDING, 0, 0); return; } if((sd->bl.m == map_mapname2mapid("mercadores") && ( ((sd->bl.x != 65) && (sd->bl.x != 74) && (sd->bl.x != 85) && (sd->bl.x != 94) && (sd->bl.x != 34) && (sd->bl.x != 25) && (sd->bl.x != 14) && (sd->bl.x != 5) && (sd->bl.x != 44) && (sd->bl.x != 55)) ))) { clif_displaymessage(sd->fd,"Você não pode abrir lojas no meio da Sala."); return; } // filter out invalid items i = 0; for( j = 0; j < count; j++ ) { short index = *(uint16*)(data + 8*j + 0); short amount = *(uint16*)(data + 8*j + 2); unsigned int value = *(uint32*)(data + 8*j + 4); index -= 2; // offset adjustment (client says that the first cart position is 2) if( index < 0 || index >= MAX_CART // invalid position || pc_cartitem_amount(sd, index, amount) < 0 // invalid item or insufficient quantity //NOTE: official server does not do any of the following checks! || !sd->status.cart[index].identify // unidentified item || sd->status.cart[index].attribute == 1 // broken item || sd->status.cart[index].expire_time // It should not be in the cart but just in case || sd->status.cart[index].bound // Can't Trade Account bound items || ( sd->status.cart[index].card[0] == CARD0_CREATE && (char_id = MakeDWord(sd->status.cart[index].card[2],sd->status.cart[index].card[3])) > 0 && ((battle_config.bg_reserved_char_id && char_id == battle_config.bg_reserved_char_id) || (battle_config.ancient_reserved_char_id && char_id == battle_config.ancient_reserved_char_id)) ) || !itemdb_cantrade(&sd->status.cart[index], pc_isGM(sd), pc_isGM(sd)) ) // untradeable item continue; sd->vending[i].index = index; sd->vending[i].amount = amount; sd->vending[i].value = cap_value(value, 0, (unsigned int)battle_config.vending_max_value); i++; // item successfully added } if( i != j ) clif_displaymessage (sd->fd, msg_txt(266)); //"Some of your items cannot be vended and were removed from the shop." if( i == 0 ) { // no valid item found clif_skill_fail(sd, MC_VENDING, 0, 0); // custom reply packet return; } sd->state.vending = true; sd->vender_id = vending_getuid(); sd->vend_num = i; safestrncpy(sd->message, message, MESSAGE_SIZE); pc_stop_walking(sd,1); clif_openvending(sd,sd->bl.id,sd->vending); clif_showvendingboard(&sd->bl,message,0); if( battle_config.channel_announces&0x10 ) { char chat_message[256]; sprintf(chat_message, msg_txt(820), vending_chat_nick, sd->status.name, sd->message, map[sd->bl.m].name, sd->bl.x, sd->bl.y); clif_channel_message(server_channel[CHN_VENDING], chat_message, 27); } if( map[sd->bl.m].flag.vending_cell ) map_setcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_NOBOARDS, false); }
/*========================================== * Open shop * data := {<index>.w <amount>.w <value>.l}[count] *------------------------------------------*/ void vending_openvending(struct map_session_data* sd, const char* message, bool flag, const uint8* data, int count) { int i, j; int vending_skill_lvl; nullpo_retv(sd); if( !flag ) // cancelled return; // nothing to do if (pc_istrading(sd)) return; // can't have 2 shops at once vending_skill_lvl = pc_checkskill(sd, MC_VENDING); // skill level and cart check if( !vending_skill_lvl || !pc_iscarton(sd) ) { clif_skill_fail(sd, MC_VENDING, 0, 0, 0); return; } // check number of items in shop if( count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl ) { // invalid item count clif_skill_fail(sd, MC_VENDING, 0, 0, 0); return; } // filter out invalid items i = 0; for( j = 0; j < count; j++ ) { short index = *(uint16*)(data + 8*j + 0); short amount = *(uint16*)(data + 8*j + 2); unsigned int value = *(uint32*)(data + 8*j + 4); index -= 2; // offset adjustment (client says that the first cart position is 2) if( index < 0 || index >= MAX_CART // invalid position || pc_cartitem_amount(sd, index, amount) < 0 // invalid item or insufficient quantity //NOTE: official server does not do any of the following checks! || !sd->status.cart[index].identify // unidentified item || sd->status.cart[index].attribute == 1 // broken item || sd->status.cart[index].expire_time // It should not be in the cart but just in case || !itemdb_cantrade(&sd->status.cart[index], pc_isGM(sd), pc_isGM(sd)) ) // untradeable item continue; sd->vending[i].index = index; sd->vending[i].amount = amount; sd->vending[i].value = cap_value(value, 1, (unsigned int)battle_config.vending_max_value); i++; // item successfully added } if( i != j ) clif_displaymessage (sd->fd, msg_txt(266)); //"Some of your items cannot be vended and were removed from the shop." if( i == 0 ) { // no valid item found clif_skill_fail(sd, MC_VENDING, 0, 0, 0); // custom reply packet return; } sd->vender_id = sd->bl.id; sd->vend_num = i; safestrncpy(sd->message, message, MESSAGE_SIZE); pc_stop_walking(sd,1); clif_openvending(sd,sd->vender_id,sd->vending); clif_showvendingboard(&sd->bl,message,0); }
/*========================================== * PCのquit処理 map.c内分 * * quit処理の主体が違うような気もしてきた *------------------------------------------ */ int map_quit(struct map_session_data *sd) { if(sd->chatID) // チャットから出る chat_leavechat(sd); if(sd->trade_partner) // 取引を中断する trade_tradecancel(sd); if(sd->party_invite>0) // パーティ勧誘を拒否する party_reply_invite(sd,sd->party_invite_account,0); if(sd->guild_invite>0) // ギルド勧誘を拒否する guild_reply_invite(sd,sd->guild_invite,0); if(sd->guild_alliance>0) // ギルド同盟勧誘を拒否する guild_reply_reqalliance(sd,sd->guild_alliance_account,0); party_send_logout(sd); // パーティのログアウトメッセージ送信 guild_send_memberinfoshort(sd,0); // ギルドのログアウトメッセージ送信 pc_cleareventtimer(sd); // イベントタイマを破棄する storage_storage_quit(sd); // 倉庫を開いてるなら保存する skill_castcancel(&sd->bl,0); // 詠唱を中断する skill_status_change_clear(&sd->bl); // ステータス異常を解除する skill_clear_unitgroup(&sd->bl); // スキルユニットグループの削除 skill_cleartimerskill(&sd->bl); pc_stop_walking(sd,0); pc_stopattack(sd); pc_delinvincibletimer(sd); pc_delspiritball(sd,sd->spiritball,1); skill_gangsterparadise(sd,0); pc_calcstatus(sd,4); clif_clearchar_area(&sd->bl,2); if(sd->status.pet_id && sd->pd) { pet_remove_map(sd); if(sd->pet.intimate <= 0) { intif_delete_petdata(sd->status.pet_id); sd->status.pet_id = 0; sd->pd = NULL; sd->petDB = NULL; } else intif_save_petdata(sd->status.account_id,&sd->pet); } if(pc_isdead(sd)) pc_setrestartvalue(sd,2); pc_makesavestatus(sd); chrif_save(sd); storage_storage_save(sd); map_delblock(&sd->bl); numdb_erase(id_db,sd->bl.id); strdb_erase(nick_db,sd->status.name); return 0; }