diff options
Diffstat (limited to 'src/map/party.cpp')
-rw-r--r-- | src/map/party.cpp | 790 |
1 files changed, 790 insertions, 0 deletions
diff --git a/src/map/party.cpp b/src/map/party.cpp new file mode 100644 index 0000000..1c4088a --- /dev/null +++ b/src/map/party.cpp @@ -0,0 +1,790 @@ +// $Id: party.c,v 1.2 2004/09/22 02:59:47 Akitasha Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "party.hpp" +#include "../common/db.hpp" +#include "../common/timer.hpp" +#include "../common/socket.hpp" +#include "../common/nullpo.hpp" +#include "pc.hpp" +#include "map.hpp" +#include "battle.hpp" +#include "intif.hpp" +#include "clif.hpp" +#include "skill.hpp" +#include "tmw.hpp" + +#ifdef MEMWATCH +#include "memwatch.hpp" +#endif + +#define PARTY_SEND_XYHP_INVERVAL 1000 // 座標やHP送信の間隔 + +static struct dbt *party_db; + +void party_send_xyhp_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data); +/*========================================== + * 終了 + *------------------------------------------ + */ +static void party_db_final (db_key_t key, db_val_t data, va_list ap) +{ + free (data); +} + +void do_final_party (void) +{ + if (party_db) + numdb_final (party_db, party_db_final); +} + +// 初期化 +void do_init_party (void) +{ + party_db = numdb_init (); + add_timer_interval (gettick () + PARTY_SEND_XYHP_INVERVAL, + party_send_xyhp_timer, 0, 0, + PARTY_SEND_XYHP_INVERVAL); +} + +// 検索 +struct party *party_search (int party_id) +{ + return (struct party *)numdb_search (party_db, party_id); +} + +void party_searchname_sub (db_key_t key, db_val_t data, va_list ap) +{ + struct party *p = (struct party *) data, **dst; + char *str; + str = va_arg (ap, char *); + dst = va_arg (ap, struct party **); + if (strcasecmp (p->name, str) == 0) + *dst = p; +} + +// パーティ名検索 +struct party *party_searchname (char *str) +{ + struct party *p = NULL; + numdb_foreach (party_db, party_searchname_sub, str, &p); + return p; +} + +/* Process a party creation request. */ +int party_create (struct map_session_data *sd, char *name) +{ + char pname[24]; + nullpo_retr (0, sd); + + strncpy (pname, name, 24); + pname[23] = '\0'; + tmw_TrimStr (pname); + + /* The party name is empty/invalid. */ + if (!*pname) + clif_party_created (sd, 1); + + /* Make sure the character isn't already in a party. */ + if (sd->status.party_id == 0) + intif_create_party (sd, pname); + else + clif_party_created (sd, 2); + + return 0; +} + +/* Relay the result of a party creation request. */ +int party_created (int account_id, int fail, int party_id, char *name) +{ + struct map_session_data *sd; + sd = map_id2sd (account_id); + + nullpo_retr (0, sd); + + /* The party name is valid and not already taken. */ + if (!fail) + { + struct party *p; + sd->status.party_id = party_id; + + if ((p = (struct party *)numdb_search (party_db, party_id)) != NULL) + { + printf ("party_created(): ID already exists!\n"); + exit (1); + } + + CREATE (p, struct party, 1); + p->party_id = party_id; + memcpy (p->name, name, 24); + numdb_insert (party_db, party_id, p); + + /* The party was created successfully. */ + clif_party_created (sd, 0); + } + + else + clif_party_created (sd, 1); + + return 0; +} + +// 情報要求 +int party_request_info (int party_id) +{ + return intif_request_partyinfo (party_id); +} + +// 所属キャラの確認 +int party_check_member (struct party *p) +{ + int i; + struct map_session_data *sd; + + nullpo_retr (0, p); + + for (i = 0; i < fd_max; i++) + { + if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) && sd->state.auth) + { + if (sd->status.party_id == p->party_id) + { + int j, f = 1; + for (j = 0; j < MAX_PARTY; j++) + { // パーティにデータがあるか確認 + if (p->member[j].account_id == sd->status.account_id) + { + if (strcmp (p->member[j].name, sd->status.name) == 0) + f = 0; // データがある + else + p->member[j].sd = NULL; // 同垢別キャラだった + } + } + if (f) + { + sd->status.party_id = 0; + if (battle_config.error_log) + printf ("party: check_member %d[%s] is not member\n", + sd->status.account_id, sd->status.name); + } + } + } + } + return 0; +} + +// 情報所得失敗(そのIDのキャラを全部未所属にする) +int party_recv_noinfo (int party_id) +{ + int i; + struct map_session_data *sd; + for (i = 0; i < fd_max; i++) + { + if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) && sd->state.auth) + { + if (sd->status.party_id == party_id) + sd->status.party_id = 0; + } + } + return 0; +} + +// 情報所得 +int party_recv_info (struct party *sp) +{ + struct party *p; + int i; + + nullpo_retr (0, sp); + + if ((p = (struct party *)numdb_search (party_db, sp->party_id)) == NULL) + { + CREATE (p, struct party, 1); + numdb_insert (party_db, sp->party_id, p); + + // 最初のロードなのでユーザーのチェックを行う + party_check_member (sp); + } + memcpy (p, sp, sizeof (struct party)); + + for (i = 0; i < MAX_PARTY; i++) + { // sdの設定 + struct map_session_data *sd = map_id2sd (p->member[i].account_id); + p->member[i].sd = (sd != NULL + && sd->status.party_id == p->party_id) ? sd : NULL; + } + + clif_party_info (p, -1); + + for (i = 0; i < MAX_PARTY; i++) + { // 設定情報の送信 +// struct map_session_data *sd = map_id2sd(p->member[i].account_id); + struct map_session_data *sd = p->member[i].sd; + if (sd != NULL && sd->party_sended == 0) + { + clif_party_option (p, sd, 0x100); + sd->party_sended = 1; + } + } + + return 0; +} + +/* Process party invitation from sd to account_id. */ +int party_invite (struct map_session_data *sd, int account_id) +{ + struct map_session_data *tsd = map_id2sd (account_id); + struct party *p = party_search (sd->status.party_id); + int i; + int full = 1; /* Indicates whether or not there's room for one more. */ + + nullpo_retr (0, sd); + + if (!tsd || !p || !tsd->fd) + return 0; + + if (!battle_config.invite_request_check) + { + /* Disallow the invitation under these conditions. */ + if (tsd->guild_invite > 0 || tsd->trade_partner || tsd->npc_id + || tsd->npc_shopid || pc_checkskill (tsd, NV_PARTY) < 1) + { + clif_party_inviteack (sd, tsd->status.name, 1); + return 0; + } + } + + /* The target player is already in a party, or has a pending invitation. */ + if (tsd->status.party_id > 0 || tsd->party_invite > 0) + { + clif_party_inviteack (sd, tsd->status.name, 0); + return 0; + } + + for (i = 0; i < MAX_PARTY; i++) + { + /* + * A character from the target account is already in the same party. + * The response isn't strictly accurate, as they're separate + * characters, but we're making do with what was already in place and + * leaving this (mostly) alone for now. + */ + if (p->member[i].account_id == account_id) + { + clif_party_inviteack (sd, tsd->status.name, 1); + return 0; + } + + if (!p->member[i].account_id) + full = 0; + } + + /* There isn't enough room for a new member. */ + if (full) + { + clif_party_inviteack (sd, tsd->status.name, 3); + return 0; + } + + /* Otherwise, relay the invitation to the target player. */ + tsd->party_invite = sd->status.party_id; + tsd->party_invite_account = sd->status.account_id; + + clif_party_invite (sd, tsd); + return 0; +} + +/* Process response to party invitation. */ +int party_reply_invite (struct map_session_data *sd, int account_id, int flag) +{ + nullpo_retr (0, sd); + + /* There is no pending invitation. */ + if (!sd->party_invite || !sd->party_invite_account) + return 0; + + /* + * Only one invitation can be pending, so these have to be the same. Since + * the client continues to send the wrong ID, and it's already managed on + * this side of things, the sent ID is being ignored. + */ + account_id = sd->party_invite_account; + + /* The invitation was accepted. */ + if (flag == 1) + intif_party_addmember (sd->party_invite, sd->status.account_id); + /* The invitation was rejected. */ + else + { + /* This is the player who sent the invitation. */ + struct map_session_data *tsd = NULL; + + sd->party_invite = 0; + sd->party_invite_account = 0; + + if ((tsd = map_id2sd (account_id))) + clif_party_inviteack (tsd, sd->status.name, 1); + } + return 0; +} + +// パーティが追加された +int party_member_added (int party_id, int account_id, int flag) +{ + struct map_session_data *sd = map_id2sd (account_id), *sd2; + struct party *p = party_search (party_id); + + if (sd == NULL) + { + if (flag == 0) + { + if (battle_config.error_log) + printf ("party: member added error %d is not online\n", + account_id); + intif_party_leave (party_id, account_id); // キャラ側に登録できなかったため脱退要求を出す + } + return 0; + } + sd2 = map_id2sd (sd->party_invite_account); + sd->party_invite = 0; + sd->party_invite_account = 0; + + if (p == NULL) + { + printf ("party_member_added: party %d not found.\n", party_id); + intif_party_leave (party_id, account_id); + return 0; + } + + if (flag == 1) + { // 失敗 + if (sd2 != NULL) + clif_party_inviteack (sd2, sd->status.name, 0); + return 0; + } + + // 成功 + sd->party_sended = 0; + sd->status.party_id = party_id; + + if (sd2 != NULL) + clif_party_inviteack (sd2, sd->status.name, 2); + + // いちおう競合確認 + party_check_conflict (sd); + + party_send_xy_clear (p); + + return 0; +} + +// パーティ除名要求 +int party_removemember (struct map_session_data *sd, int account_id, + char *name) +{ + struct party *p; + int i; + + nullpo_retr (0, sd); + + if ((p = party_search (sd->status.party_id)) == NULL) + return 0; + + for (i = 0; i < MAX_PARTY; i++) + { // リーダーかどうかチェック + if (p->member[i].account_id == sd->status.account_id) + if (p->member[i].leader == 0) + return 0; + } + + for (i = 0; i < MAX_PARTY; i++) + { // 所属しているか調べる + if (p->member[i].account_id == account_id) + { + intif_party_leave (p->party_id, account_id); + return 0; + } + } + return 0; +} + +// パーティ脱退要求 +int party_leave (struct map_session_data *sd) +{ + struct party *p; + int i; + + nullpo_retr (0, sd); + + if ((p = party_search (sd->status.party_id)) == NULL) + return 0; + + for (i = 0; i < MAX_PARTY; i++) + { // 所属しているか + if (p->member[i].account_id == sd->status.account_id) + { + intif_party_leave (p->party_id, sd->status.account_id); + return 0; + } + } + return 0; +} + +// パーティメンバが脱退した +int party_member_leaved (int party_id, int account_id, char *name) +{ + struct map_session_data *sd = map_id2sd (account_id); + struct party *p = party_search (party_id); + if (p != NULL) + { + int i; + for (i = 0; i < MAX_PARTY; i++) + if (p->member[i].account_id == account_id) + { + clif_party_leaved (p, sd, account_id, name, 0x00); + p->member[i].account_id = 0; + p->member[i].sd = NULL; + } + } + if (sd != NULL && sd->status.party_id == party_id) + { + sd->status.party_id = 0; + sd->party_sended = 0; + } + return 0; +} + +// パーティ解散通知 +int party_broken (int party_id) +{ + struct party *p; + int i; + if ((p = party_search (party_id)) == NULL) + return 0; + + for (i = 0; i < MAX_PARTY; i++) + { + if (p->member[i].sd != NULL) + { + clif_party_leaved (p, p->member[i].sd, + p->member[i].account_id, p->member[i].name, + 0x10); + p->member[i].sd->status.party_id = 0; + p->member[i].sd->party_sended = 0; + } + } + numdb_erase (party_db, party_id); + return 0; +} + +// パーティの設定変更要求 +int party_changeoption (struct map_session_data *sd, int exp, int item) +{ + struct party *p; + + nullpo_retr (0, sd); + + if (sd->status.party_id == 0 + || (p = party_search (sd->status.party_id)) == NULL) + return 0; + intif_party_changeoption (sd->status.party_id, sd->status.account_id, exp, + item); + return 0; +} + +// パーティの設定変更通知 +int party_optionchanged (int party_id, int account_id, int exp, int item, + int flag) +{ + struct party *p; + struct map_session_data *sd = map_id2sd (account_id); + if ((p = party_search (party_id)) == NULL) + return 0; + + if (!(flag & 0x01)) + p->exp = exp; + if (!(flag & 0x10)) + p->item = item; + clif_party_option (p, sd, flag); + return 0; +} + +// パーティメンバの移動通知 +int party_recv_movemap (int party_id, int account_id, char *map, int online, + int lv) +{ + struct party *p; + int i; + if ((p = party_search (party_id)) == NULL) + return 0; + for (i = 0; i < MAX_PARTY; i++) + { + struct party_member *m = &p->member[i]; + if (m == NULL) + { + printf ("party_recv_movemap nullpo?\n"); + return 0; + } + if (m->account_id == account_id) + { + memcpy (m->map, map, 16); + m->online = online; + m->lv = lv; + break; + } + } + if (i == MAX_PARTY) + { + if (battle_config.error_log) + printf ("party: not found member %d on %d[%s]", account_id, + party_id, p->name); + return 0; + } + + for (i = 0; i < MAX_PARTY; i++) + { // sd再設定 + struct map_session_data *sd = map_id2sd (p->member[i].account_id); + p->member[i].sd = (sd != NULL + && sd->status.party_id == p->party_id) ? sd : NULL; + } + + party_send_xy_clear (p); // 座標再通知要請 + + clif_party_info (p, -1); + return 0; +} + +// パーティメンバの移動 +int party_send_movemap (struct map_session_data *sd) +{ + struct party *p; + + nullpo_retr (0, sd); + + if (sd->status.party_id == 0) + return 0; + intif_party_changemap (sd, 1); + + if (sd->party_sended != 0) // もうパーティデータは送信済み + return 0; + + // 競合確認 + party_check_conflict (sd); + + // あるならパーティ情報送信 + if ((p = party_search (sd->status.party_id)) != NULL) + { + party_check_member (p); // 所属を確認する + if (sd->status.party_id == p->party_id) + { + clif_party_info (p, sd->fd); + clif_party_option (p, sd, 0x100); + sd->party_sended = 1; + } + } + + return 0; +} + +// パーティメンバのログアウト +int party_send_logout (struct map_session_data *sd) +{ + struct party *p; + + nullpo_retr (0, sd); + + if (sd->status.party_id > 0) + intif_party_changemap (sd, 0); + + // sdが無効になるのでパーティ情報から削除 + if ((p = party_search (sd->status.party_id)) != NULL) + { + int i; + for (i = 0; i < MAX_PARTY; i++) + if (p->member[i].sd == sd) + p->member[i].sd = NULL; + } + + return 0; +} + +// パーティメッセージ送信 +int party_send_message (struct map_session_data *sd, char *mes, int len) +{ + if (sd->status.party_id == 0) + return 0; + intif_party_message (sd->status.party_id, sd->status.account_id, mes, + len); + return 0; +} + +// パーティメッセージ受信 +int party_recv_message (int party_id, int account_id, char *mes, int len) +{ + struct party *p; + if ((p = party_search (party_id)) == NULL) + return 0; + clif_party_message (p, account_id, mes, len); + return 0; +} + +// パーティ競合確認 +int party_check_conflict (struct map_session_data *sd) +{ + nullpo_retr (0, sd); + + intif_party_checkconflict (sd->status.party_id, sd->status.account_id, + sd->status.name); + return 0; +} + +// 位置やHP通知用 +void party_send_xyhp_timer_sub (db_key_t key, db_val_t data, va_list ap) +{ + struct party *p = (struct party *) data; + int i; + + nullpo_retv (p); + + for (i = 0; i < MAX_PARTY; i++) + { + struct map_session_data *sd; + if ((sd = p->member[i].sd) != NULL) + { + // 座標通知 + if (sd->party_x != sd->bl.x || sd->party_y != sd->bl.y) + { + clif_party_xy (p, sd); + sd->party_x = sd->bl.x; + sd->party_y = sd->bl.y; + } + // HP通知 + if (sd->party_hp != sd->status.hp) + { + clif_party_hp (p, sd); + sd->party_hp = sd->status.hp; + } + + } + } +} + +// 位置やHP通知 +void party_send_xyhp_timer (timer_id tid, tick_t tick, custom_id_t id, custom_data_t data) +{ + numdb_foreach (party_db, party_send_xyhp_timer_sub, tick); +} + +// 位置通知クリア +int party_send_xy_clear (struct party *p) +{ + int i; + + nullpo_retr (0, p); + + for (i = 0; i < MAX_PARTY; i++) + { + struct map_session_data *sd; + if ((sd = p->member[i].sd) != NULL) + { + sd->party_x = -1; + sd->party_y = -1; + sd->party_hp = -1; + } + } + return 0; +} + +// HP通知の必要性検査用(map_foreachinmoveareaから呼ばれる) +int party_send_hp_check (struct block_list *bl, va_list ap) +{ + int party_id; + int *flag; + struct map_session_data *sd; + + nullpo_retr (0, bl); + nullpo_retr (0, ap); + nullpo_retr (0, sd = (struct map_session_data *) bl); + + party_id = va_arg (ap, int); + flag = va_arg (ap, int *); + + if (sd->status.party_id == party_id) + { + *flag = 1; + sd->party_hp = -1; + } + return 0; +} + +// 経験値公平分配 +int party_exp_share (struct party *p, int map, int base_exp, int job_exp) +{ + struct map_session_data *sd; + int i, c; + + nullpo_retr (0, p); + + for (i = c = 0; i < MAX_PARTY; i++) + if ((sd = p->member[i].sd) != NULL && sd->bl.m == map) + c++; + if (c == 0) + return 0; + for (i = 0; i < MAX_PARTY; i++) + if ((sd = p->member[i].sd) != NULL && sd->bl.m == map) + pc_gainexp (sd, base_exp / c + 1, job_exp / c + 1); + return 0; +} + +// 同じマップのパーティメンバー全体に処理をかける +// type==0 同じマップ +// !=0 画面内 +void party_foreachsamemap (int (*func) (struct block_list *, va_list), + struct map_session_data *sd, int type, ...) +{ + struct party *p; + va_list ap; + int i; + int x0, y0, x1, y1; + struct block_list *list[MAX_PARTY]; + int blockcount = 0; + + nullpo_retv (sd); + + if ((p = party_search (sd->status.party_id)) == NULL) + return; + + x0 = sd->bl.x - AREA_SIZE; + y0 = sd->bl.y - AREA_SIZE; + x1 = sd->bl.x + AREA_SIZE; + y1 = sd->bl.y + AREA_SIZE; + + va_start (ap, type); + + for (i = 0; i < MAX_PARTY; i++) + { + struct party_member *m = &p->member[i]; + if (m->sd != NULL) + { + if (sd->bl.m != m->sd->bl.m) + continue; + if (type != 0 && + (m->sd->bl.x < x0 || m->sd->bl.y < y0 || + m->sd->bl.x > x1 || m->sd->bl.y > y1)) + continue; + list[blockcount++] = &m->sd->bl; + } + } + + map_freeblock_lock (); // メモリからの解放を禁止する + + for (i = 0; i < blockcount; i++) + if (list[i]->prev) // 有効かどうかチェック + func (list[i], ap); + + map_freeblock_unlock (); // 解放を許可する + + va_end (ap); +} |