/*========================================== * Divorce request from char server * triggered on account deletion or as an * ack from a map-server divorce request *------------------------------------------ */ static int chrif_divorce(CharId char_id, CharId partner_id) { dumb_ptr<map_session_data> sd = nullptr; if (!char_id || !partner_id) return 0; sd = map_nick2sd(map_charid2nick(char_id)); if (sd && sd->status.partner_id == partner_id) { sd->status.partner_id = CharId(); if (sd->npc_flags.divorce) { sd->npc_flags.divorce = 0; map_scriptcont(sd, sd->npc_id); } } sd = map_nick2sd(map_charid2nick(partner_id)); nullpo_retz(sd); if (sd->status.partner_id == char_id) sd->status.partner_id = CharId(); return 0; }
// パーティ除名要求 int party_removemember(dumb_ptr<map_session_data> sd, AccountId account_id) { PartyPair p; int i; nullpo_retz(sd); if (!(p = party_search(sd->status.party_id))) return 0; for (i = 0; i < MAX_PARTY; i++) { // リーダーかどうかチェック if (p->member[i].account_id == sd->status_key.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; }
/*========================================== * (x,y)が移動不可能地帯かどうか * flag 0x10000 遠距離攻撃判定 *------------------------------------------ */ static bool can_place(struct map_local *m, int x, int y) { nullpo_retz(m); return !bool(read_gatp(m, x, y) & MapCell::UNWALKABLE); }
// パーティメンバの移動 int party_send_movemap(dumb_ptr<map_session_data> sd) { PartyPair p; nullpo_retz(sd); if (!sd->status.party_id) 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))) { party_check_member(p); // 所属を確認する if (sd->status.party_id == p.party_id) { clif_party_info(p, sd->sess); clif_party_option(p, sd, 0x100); sd->party_sended = 1; } } return 0; }
/* Process response to party invitation. */ int party_reply_invite(dumb_ptr<map_session_data> sd, AccountId account_id, int flag) { nullpo_retz(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_key.account_id); /* The invitation was rejected. */ else { /* This is the player who sent the invitation. */ dumb_ptr<map_session_data> tsd = nullptr; sd->party_invite = PartyId(); sd->party_invite_account = AccountId(); if ((tsd = map_id2sd(account_to_block(account_id)))) clif_party_inviteack(tsd, sd->status_key.name, 1); } return 0; }
/*========================================== * 必要ならpathを追加/修正する *------------------------------------------ */ static int add_path(int *heap, struct tmp_path *tp, int x, int y, int dist, DIR dir, int before, int x1, int y1) { int i; nullpo_retz(heap); nullpo_retz(tp); i = calc_index(x, y); if (tp[i].x == x && tp[i].y == y) { if (tp[i].dist > dist) { tp[i].dist = dist; tp[i].dir = dir; tp[i].before = before; tp[i].cost = calc_cost(&tp[i], x1, y1); if (tp[i].flag) push_heap_path(heap, tp, i); else update_heap_path(heap, tp, i); tp[i].flag = 0; } return 0; } if (tp[i].x || tp[i].y) return 1; tp[i].x = x; tp[i].y = y; tp[i].dist = dist; tp[i].dir = dir; tp[i].before = before; tp[i].cost = calc_cost(&tp[i], x1, y1); tp[i].flag = 0; push_heap_path(heap, tp, i); return 0; }
/*========================================== * カプラ倉庫を開く *------------------------------------------ */ int storage_storageopen(dumb_ptr<map_session_data> sd) { nullpo_retz(sd); if (sd->state.storage_open) return 1; //Already open? P<Storage> stor = TRY_UNWRAP(storage_db.search(sd->status_key.account_id), { //Request storage. intif_request_storage(sd->status_key.account_id); return 1; });
// パーティの設定変更要求 int party_changeoption(dumb_ptr<map_session_data> sd, int exp, int item) { PartyPair p; nullpo_retz(sd); if (!sd->status.party_id || !(p = party_search(sd->status.party_id))) return 0; intif_party_changeoption(sd->status.party_id, sd->status_key.account_id, exp, item); return 0; }
/*========================================== * 現在の点のcost計算 *------------------------------------------ */ static int calc_cost(struct tmp_path *p, int x1, int y1) { int xd, yd; nullpo_retz(p); xd = x1 - p->x; if (xd < 0) xd = -xd; yd = y1 - p->y; if (yd < 0) yd = -yd; return (xd + yd) * 10 + p->dist; }
/* Process a party creation request. */ int party_create(dumb_ptr<map_session_data> sd, PartyName name) { nullpo_retz(sd); name = stringish<PartyName>(name.strip()); /* The party name is empty/invalid. */ if (!name) clif_party_created(sd, 1); /* Make sure the character isn't already in a party. */ if (!sd->status.party_id) intif_create_party(sd, name); else clif_party_created(sd, 2); return 0; }
/*========================================== * (x0,y0)から(x1,y1)へ1歩で移動可能か計算 *------------------------------------------ */ static int can_move(struct map_local *m, int x0, int y0, int x1, int y1) { nullpo_retz(m); if (x0 - x1 < -1 || x0 - x1 > 1 || y0 - y1 < -1 || y0 - y1 > 1) return 0; if (x1 < 0 || y1 < 0 || x1 >= m->xs || y1 >= m->ys) return 0; if (!can_place(m, x0, y0)) return 0; if (!can_place(m, x1, y1)) return 0; if (x0 == x1 || y0 == y1) return 1; if (!can_place(m, x0, y1) || !can_place(m, x1, y0)) return 0; return 1; }
/*========================================== * イベントキューのイベント処理 *------------------------------------------ */ int npc_event_dequeue(dumb_ptr<map_session_data> sd) { nullpo_retz(sd); sd->npc_id = BlockId(); if (!sd->eventqueuel.empty()) { if (!pc_addeventtimer(sd, 100_ms, sd->eventqueuel.front())) { PRINTF("npc_event_dequeue(): Event timer is full.\n"_fmt); return 0; } sd->eventqueuel.pop_front(); return 1; } return 0; }
// パーティメンバのログアウト int party_send_logout(dumb_ptr<map_session_data> sd) { PartyPair p; nullpo_retz(sd); if (sd->status.party_id) intif_party_changemap(sd, 0); // sdが無効になるのでパーティ情報から削除 if ((p = party_search(sd->status.party_id))) { int i; for (i = 0; i < MAX_PARTY; i++) if (dumb_ptr<map_session_data>(p->member[i].sd) == sd) p->member[i].sd = nullptr; } return 0; }
// パーティ脱退要求 int party_leave(dumb_ptr<map_session_data> sd) { PartyPair p; int i; nullpo_retz(sd); if (!(p = party_search(sd->status.party_id))) return 0; for (i = 0; i < MAX_PARTY; i++) { // 所属しているか if (p->member[i].account_id == sd->status_key.account_id) { intif_party_leave(p.party_id, sd->status_key.account_id); return 0; } } return 0; }
// 所属キャラの確認 static int party_check_member(PartyPair p) { nullpo_retz(p); for (io::FD i : iter_fds()) { Session *s = get_session(i); if (!s) continue; map_session_data *sd = static_cast<map_session_data *>(s->session_data.get()); if (sd && 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_key.account_id) { if (p->member[j].name == sd->status_key.name) f = 0; // データがある else { // I can prove it was already zeroed // p->member[j].sd = nullptr; // 同垢別キャラだった } } } if (f) { sd->status.party_id = PartyId(); if (battle_config.error_log) PRINTF("party: check_member %d[%s] is not member\n"_fmt, sd->status_key.account_id, sd->status_key.name); } } } } return 0; }
// 情報所得 int party_recv_info(const PartyPair sp) { int i; nullpo_retz(sp); PartyPair p = party_search(sp.party_id); if (!p) { p.party_most = party_db.init(sp.party_id); // 最初のロードなのでユーザーのチェックを行う *p.party_most = *sp.party_most; party_check_member(p); } else *p.party_most = *sp.party_most; for (i = 0; i < MAX_PARTY; i++) { // sdの設定 dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(p->member[i].account_id)); p->member[i].sd = (sd != nullptr && sd->status.party_id == p.party_id) ? sd.operator->() : nullptr; } clif_party_info(p, nullptr); for (i = 0; i < MAX_PARTY; i++) { // 設定情報の送信 // dumb_ptr<map_session_data> sd = map_id2sd(p->member[i].account_id); dumb_ptr<map_session_data> sd = dumb_ptr<map_session_data>(p->member[i].sd); if (sd != nullptr && sd->party_sended == 0) { clif_party_option(p, sd, 0x100); sd->party_sended = 1; } } return 0; }
// 経験値公平分配 int party_exp_share(PartyPair p, map_local *mapid, int base_exp, int job_exp) { dumb_ptr<map_session_data> sd; int i, c; nullpo_retz(p); for (i = c = 0; i < MAX_PARTY; i++) { sd = dumb_ptr<map_session_data>(p->member[i].sd); if (sd != nullptr && sd->bl_m == mapid) c++; } if (c == 0) return 0; for (i = 0; i < MAX_PARTY; i++) { sd = dumb_ptr<map_session_data>(p->member[i].sd); if (sd != nullptr && sd->bl_m == mapid) pc_gainexp_reason(sd, base_exp / c + 1, job_exp / c + 1, PC_GAINEXP_REASON::SHARING); } return 0; }
/*========================================== * path探索 (x0,y0)->(x1,y1) *------------------------------------------ */ int path_search(struct walkpath_data *wpd, map_local *m, int x0, int y0, int x1, int y1, int flag) { int heap[MAX_HEAP + 1]; int i, rp, x, y; int dx, dy; nullpo_retz(wpd); assert (m->gat); map_local *md = m; if (x1 < 0 || x1 >= md->xs || y1 < 0 || y1 >= md->ys || bool(read_gatp(md, x1, y1) & MapCell::UNWALKABLE)) return -1; // easy dx = (x1 - x0 < 0) ? -1 : 1; dy = (y1 - y0 < 0) ? -1 : 1; for (x = x0, y = y0, i = 0; x != x1 || y != y1;) { if (i >= sizeof(wpd->path)) return -1; if (x != x1 && y != y1) { if (!can_move(md, x, y, x + dx, y + dy)) break; x += dx; y += dy; wpd->path[i++] = (dx < 0) ? ((dy > 0) ? DIR::SW : DIR::NW) : ((dy < 0) ? DIR::NE : DIR::SE); } else if (x != x1) { if (!can_move(md, x, y, x + dx, y)) break; x += dx; wpd->path[i++] = (dx < 0) ? DIR::W : DIR::E; } else { // y!=y1 if (!can_move(md, x, y, x, y + dy)) break; y += dy; wpd->path[i++] = (dy > 0) ? DIR::S : DIR::N; } if (x == x1 && y == y1) { wpd->path_len = i; wpd->path_pos = 0; wpd->path_half = 0; return 0; } } if (flag & 1) return -1; struct tmp_path tp[MAX_WALKPATH * MAX_WALKPATH] {}; i = calc_index(x0, y0); tp[i].x = x0; tp[i].y = y0; tp[i].dist = 0; tp[i].dir = DIR::S; tp[i].before = 0; tp[i].cost = calc_cost(&tp[i], x1, y1); tp[i].flag = 0; heap[0] = 0; push_heap_path(heap, tp, calc_index(x0, y0)); while (1) { int e = 0; if (heap[0] == 0) return -1; rp = pop_heap_path(heap, tp); x = tp[rp].x; y = tp[rp].y; if (x == x1 && y == y1) { int len, j; for (len = 0, i = rp; len < 100 && i != calc_index(x0, y0); i = tp[i].before, len++); if (len == 100 || len >= sizeof(wpd->path)) return -1; wpd->path_len = len; wpd->path_pos = 0; wpd->path_half = 0; for (i = rp, j = len - 1; j >= 0; i = tp[i].before, j--) wpd->path[j] = tp[i].dir; return 0; } if (can_move(md, x, y, x + 1, y - 1)) e += add_path(heap, tp, x + 1, y - 1, tp[rp].dist + 14, DIR::NE, rp, x1, y1); if (can_move(md, x, y, x + 1, y)) e += add_path(heap, tp, x + 1, y, tp[rp].dist + 10, DIR::E, rp, x1, y1); if (can_move(md, x, y, x + 1, y + 1)) e += add_path(heap, tp, x + 1, y + 1, tp[rp].dist + 14, DIR::SE, rp, x1, y1); if (can_move(md, x, y, x, y + 1)) e += add_path(heap, tp, x, y + 1, tp[rp].dist + 10, DIR::S, rp, x1, y1); if (can_move(md, x, y, x - 1, y + 1)) e += add_path(heap, tp, x - 1, y + 1, tp[rp].dist + 14, DIR::SW, rp, x1, y1); if (can_move(md, x, y, x - 1, y)) e += add_path(heap, tp, x - 1, y, tp[rp].dist + 10, DIR::W, rp, x1, y1); if (can_move(md, x, y, x - 1, y - 1)) e += add_path(heap, tp, x - 1, y - 1, tp[rp].dist + 14, DIR::NW, rp, x1, y1); if (can_move(md, x, y, x, y - 1)) e += add_path(heap, tp, x, y - 1, tp[rp].dist + 10, DIR::N, rp, x1, y1); tp[rp].flag = 1; if (e || heap[0] >= MAX_HEAP - 5) return -1; } }
/* Process party invitation from sd to account_id. */ int party_invite(dumb_ptr<map_session_data> sd, AccountId account_id) { dumb_ptr<map_session_data> tsd = map_id2sd(account_to_block(account_id)); PartyPair p = party_search(sd->status.party_id); int i; int full = 1; /* Indicates whether or not there's room for one more. */ nullpo_retz(sd); if (!tsd || !p || !tsd->sess) return 0; if (!battle_config.invite_request_check) { /* Disallow the invitation under these conditions. */ if (tsd->trade_partner || tsd->npc_id || tsd->npc_shopid || pc_checkskill(tsd, SkillID::NV_PARTY) < 1) { clif_party_inviteack(sd, tsd->status_key.name, 1); return 0; } } /* The target player is already in a party, or has a pending invitation. */ if (tsd->status.party_id || tsd->party_invite) { clif_party_inviteack(sd, tsd->status_key.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_key.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_key.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_key.account_id; clif_party_invite(sd, tsd); return 0; }