Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions agent_docs/dojo.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,9 @@ points:
field clock down. STOP_CLOCK leaves the HUD's clock be (v83 0x53184a
only closes the field clock's window).

The server sends SHOW_MOB_HP only to the player who hit the mob, so in
the party Dojo a member's monster gauge moves only with their own hits.
The server sends SHOW_MOB_HP to the player who hit the mob and to their
party on the map (Cosmic's rule), so in the party Dojo every member's
monster gauge moves with anyone's hits.

The energy bar sits at the left: `muruengRaid/energy`, its frame at
(20, 130) with the dark `Gage` strip stretched over the unfilled part of
Expand Down
12 changes: 9 additions & 3 deletions integration-harness/tests/party_hunting.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Parties: inviting, joining, party HP bars, sharing a kill's EXP on
//! Snail Hunting Ground I (40000), and its drops, party chat, and
//! disbanding.
//! Parties: inviting, joining, party HP bars, a mob's HP bar from a
//! member's hit, sharing a kill's EXP on Snail Hunting Ground I (40000),
//! and its drops, party chat, and disbanding.

use integration_harness::hunting::{attack, pickup};
use integration_harness::login_to_world_session;
Expand Down Expand Up @@ -103,6 +103,12 @@ async fn a_party_shares_hp_bars_exp_and_chat() {
until(&mut leader.connection, &mut l, |t| !t.gains.is_empty()).await;
until(&mut member.connection, &mut m, |t| !t.gains.is_empty()).await;
assert_eq!((l.gains[0], m.gains[0]), (2, 1));
// The member saw the snail's HP bar from the leader's first hit.
assert!(
m.field.hp_bars.iter().any(|&(oid, _)| oid == snail),
"{:?}",
m.field.hp_bars
);

// Its drops (in the harness, at least a card) are the party's for now:
// the member may take one at once.
Expand Down
2 changes: 1 addition & 1 deletion net/src/packet/build/world/mob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ pub fn build_kill_mob(object_id: i32, animation: u8) -> Result<Packet, NetworkEr
Ok(p)
}

/// SHOW_MONSTER_HP: the HP bar shown to the player who hit it.
/// SHOW_MONSTER_HP: the HP bar shown to the player who hit it and their party.
pub fn build_mob_hp(object_id: i32, percent: u8) -> Result<Packet, NetworkError> {
let mut p = Packet::new_empty();
p.write_short(SendOpcode::ShowMonsterHp as i16)?;
Expand Down
27 changes: 18 additions & 9 deletions runtime/src/actor/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,12 +536,7 @@ impl FieldActor {
self.apply_cast(&cast, now);
}
for hurt in self.mobs.hurt_by_statuses(now) {
if let (Some(occupant), Ok(packet)) = (
self.occupants.get(&hurt.from),
build_mob_hp(hurt.object_id, hurt.percent),
) {
occupant.handle.send_packet(packet);
}
self.show_mob_hp(hurt.from, hurt.object_id, hurt.percent);
self.show_boss_bar(self.mobs.bar_for(hurt.object_id));
}
self.mobs.recover(now);
Expand Down Expand Up @@ -1948,9 +1943,7 @@ impl FieldActor {
massacre.misses += 1;
}
Some(Ok(percent)) => {
if let Ok(packet) = build_mob_hp(object_id, percent) {
attacker.send_packet(packet);
}
self.show_mob_hp(from, object_id, percent);
if damage > 0 {
self.show_boss_bar(self.mobs.bar_for(object_id));
}
Expand Down Expand Up @@ -5162,6 +5155,22 @@ impl FieldActor {
}
}

/// The HP bar of a mob `from` hurt, for them and their party here
/// (Cosmic's rule), so a party's Dojo gauge follows every member's hits.
fn show_mob_hp(&self, from: ClientId, object_id: i32, percent: u8) {
let Some(party) = self.occupants.get(&from).map(|o| o.party) else {
return;
};
let Ok(packet) = build_mob_hp(object_id, percent) else {
return;
};
for (&client_id, occupant) in &self.occupants {
if client_id == from || (party.is_some() && occupant.party == party) {
occupant.handle.send_packet(packet.clone());
}
}
}

/// Send what `from` did to the others here who see them.
fn broadcast_by(&self, from: ClientId, packet: Packet) {
self.broadcast_seen_by(from, Some(from), packet);
Expand Down
Loading