summaryrefslogtreecommitdiff
path: root/src/map/party.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/party.cpp')
-rw-r--r--src/map/party.cpp790
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);
+}