diff --git a/srcs/core/engine.cpp b/srcs/core/engine.cpp index 0f53298..c20fdc2 100644 --- a/srcs/core/engine.cpp +++ b/srcs/core/engine.cpp @@ -745,6 +745,12 @@ void Engine::run() { // ドラゴンを優先 (より遠くから当たる)。当たらなければゾンビ近接。 bool dragon_hit = impl_->dragon_mgr.playerMeleeAttack( pos.x, pos.y, pos.z, front.x, front.y, front.z); + // マルチプレイ: 命中を mob_host に通知して権威的ダメージを適用させる + if (dragon_hit && impl_->multiplayer) { + PktDragonHit pkt{DRAGON_MELEE_DAMAGE}; + impl_->net_client.sendRaw(PacketType::DragonHit, + &pkt, sizeof(pkt)); + } impl_->audio_mgr.playSe(SoundEvent::Attack); if (!dragon_hit) { int hit_idx = impl_->mob_mgr.playerMeleeAttack( @@ -928,6 +934,12 @@ void Engine::run() { ev.fb_x, ev.fb_y, ev.fb_z, ev.fb_vx, ev.fb_vy, ev.fb_vz); } + } else if (ev.kind == NetworkEvent::Kind::DragonHit) { + // mob_host のみが権威的にダメージを適用する。 + // 非ホストはローカルで楽観的に適用済みなので無視する。 + if (is_mob_host) { + impl_->dragon_mgr.applyDamage(ev.dragon_hit_damage); + } } } // Update walking animation phase for each remote player. diff --git a/srcs/network/client.cpp b/srcs/network/client.cpp index 6afe3da..43a3dec 100644 --- a/srcs/network/client.cpp +++ b/srcs/network/client.cpp @@ -208,6 +208,16 @@ void NetworkClient::handlePacket(PacketType type, const uint8_t* payload, out.push_back(ev); break; } + case PacketType::DragonHit: { + if (size < sizeof(PktDragonHit)) break; + PktDragonHit pkt; + std::memcpy(&pkt, payload, sizeof(pkt)); + NetworkEvent ev; + ev.kind = NetworkEvent::Kind::DragonHit; + ev.dragon_hit_damage = pkt.damage; + out.push_back(ev); + break; + } default: break; } diff --git a/srcs/network/client.hpp b/srcs/network/client.hpp index 1b66c9d..6f7d456 100644 --- a/srcs/network/client.hpp +++ b/srcs/network/client.hpp @@ -34,6 +34,7 @@ struct NetworkEvent { DragonUpdate, DragonFireball, PlayerDamage, + DragonHit, } kind; uint8_t player_id = 0; // PlayerDamage @@ -54,6 +55,8 @@ struct NetworkEvent { float dragon_yaw = 0, dragon_pitch = 0; float dragon_wing_phase = 0; float dragon_health = 0; + // DragonHit fields + float dragon_hit_damage = 0.0f; // DragonFireball fields (発射位置と速度) float fb_x = 0, fb_y = 0, fb_z = 0; float fb_vx = 0, fb_vy = 0, fb_vz = 0; diff --git a/srcs/network/packet.hpp b/srcs/network/packet.hpp index 0eaf552..687e686 100644 --- a/srcs/network/packet.hpp +++ b/srcs/network/packet.hpp @@ -16,6 +16,7 @@ enum class PacketType : uint8_t { DragonUpdate = 9, // C(host)→S; S→C relay to others: ドラゴンの状態 PlayerDamage = 10, // C(host)→S; S→該当クライアントへ転送: モブによるダメージ DragonFireball = 11, // C(host)→S; S→C relay: ファイアボール発射イベント + DragonHit = 12, // C→S; S→C relay(except sender): プレイヤーがドラゴンに命中 }; #pragma pack(push, 1) @@ -87,6 +88,12 @@ struct PktDragonFireball { float vx, vy, vz; // 速度 (ブロック/秒) }; +// DragonHit: プレイヤーがドラゴンに命中したことを mob_host に通知する。 +// mob_host はこれを受けて applyDamage() を呼び、次の DragonUpdate で全員に伝播する。 +struct PktDragonHit { + float damage; +}; + // PlayerDamage: ホストが計算したモブ→プレイヤーのダメージ。 // サーバーは target_id のクライアントにのみ転送し、受信側が自分の HP に適用する。 struct PktPlayerDamage { diff --git a/srcs/network/server.cpp b/srcs/network/server.cpp index bd64c89..d874e3d 100644 --- a/srcs/network/server.cpp +++ b/srcs/network/server.cpp @@ -232,6 +232,11 @@ void VoxServer::handlePacket(int from_fd, PacketType type, broadcast(PacketType::DragonFireball, payload, size, from_fd); break; } + case PacketType::DragonHit: { + // いずれかのプレイヤーがドラゴンに命中 → 送信者を除いて全員に中継 + broadcast(PacketType::DragonHit, payload, size, from_fd); + break; + } case PacketType::PlayerDamage: { // ホストが計算したモブダメージを対象プレイヤーにのみ転送 if (size < sizeof(PktPlayerDamage)) break;