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
8 changes: 5 additions & 3 deletions HeavenClient/Gameplay/Combat/Combat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1933,9 +1933,11 @@ namespace ms

void Combat::apply_damage_effect(const DamageEffect &effect)
{
Point<int16_t> head_position = mobs.get_mob_head_position(effect.target_oid);
damagenumbers.push_back(effect.number);
damagenumbers.back().set_anchor(head_position);
if (damage_layout::shows_number(effect.move_id))
{
damagenumbers.push_back(effect.number);
damagenumbers.back().set_anchor(mobs.get_mob_head_position(effect.target_oid));
}

const SpecialMove &move = get_move(effect.move_id);
mobs.apply_damage(effect.target_oid, effect.damage, effect.toleft, effect.user, move);
Expand Down
8 changes: 8 additions & 0 deletions HeavenClient/Gameplay/Combat/DamageLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,13 @@ namespace ms
{
return 15 + line * (close ? 15 : 30);
}

// Whether a hit by `skill` shows a number or a miss on the mob:
// Heaven's Hammer's and Hypnotize's show neither, whoever hits
// (0x5c9e7d).
inline bool shows_number(int32_t skill)
{
return skill != 1221011 && skill != 5221009;
}
}
}
16 changes: 12 additions & 4 deletions HeavenClient/Gameplay/MapleMap/Mob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,10 @@ namespace ms
}

// A box hits the player where they are once due; a jump attack only
// one standing on a foothold. A direct shot hits regardless.
// one standing on a foothold. A direct shot hits regardless. A
// dazzled mob's attacks hit no player (0x6663d2, 0x95825c).
bool strikes = !targetdead && !dazzled();

for (auto iter = pendinghits.begin(); iter != pendinghits.end();)
{
if (clock < iter->due)
Expand All @@ -1015,7 +1018,7 @@ namespace ms
bool hit = !iter->box || (mob_attack::intersects(*iter->box, targetbody) &&
(info.type != 0 || !info.jump || targetonground));

if (hit && !targetdead)
if (hit && strikes)
landed.push_back({ make_attack(iter->index, iter->left), attacks[iter->index].hit });

iter = pendinghits.erase(iter);
Expand All @@ -1039,7 +1042,7 @@ namespace ms
int32_t bx = static_cast<int32_t>(iter->from.x() + (iter->to.x() - iter->from.x()) * progress);
int32_t by = static_cast<int32_t>(iter->from.y() + (iter->to.y() - iter->from.y()) * progress);

if (mob_attack::contains(targetbody, bx, by) && !targetdead)
if (mob_attack::contains(targetbody, bx, by) && strikes)
landed.push_back({ make_attack(iter->index, iter->left), attacks[iter->index].hit });
}

Expand Down Expand Up @@ -2051,6 +2054,11 @@ namespace ms
statuses.count(MobStatParser::WEB);
}

bool Mob::dazzled() const
{
return statuses.count(MobStatParser::DAZZLE);
}

void Mob::show_status(int32_t reason, bool effects)
{
// A player's skill, or a mob skill's id and level (0x5d3d02).
Expand Down Expand Up @@ -2525,7 +2533,7 @@ namespace ms

bool Mob::deals_touch_damage() const
{
return touchdamage && !notattack;
return touchdamage && !notattack && !dazzled();
}

bool Mob::is_in_range(const Rectangle<int16_t> &range) const
Expand Down
2 changes: 2 additions & 0 deletions HeavenClient/Gameplay/MapleMap/Mob.h
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ namespace ms

// Whether a status holds the mob where it stands.
bool held() const;
// Whether Hypnotize keeps the mob from hurting players.
bool dazzled() const;

// Show a status's skill on the mob: its `mob` effect once, or for
// as long as the status lasts if it repeats, and its `mob0` for as
Expand Down
2 changes: 2 additions & 0 deletions HeavenClient/Net/Handlers/Helpers/MobStatParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ namespace ms
VENOM = 24,
BLIND = 25,
BURNED = 27,
// Hypnotize's: the mob hurts no player (0x6663d2, 0x95825c).
DAZZLE = 28,
PCOUNTER = 29,
MCOUNTER = 30,
DISABLE = 31,
Expand Down
4 changes: 4 additions & 0 deletions HeavenClient/tests/damage_layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ int main()
require(width == 96 && placed[0].left == 30, "a critical number starts right of its banner");
require(placed[0].top == 15 && placed[1].top == 22, "drops apply to odd digits too");

require(damage_layout::shows_number(5221004), "a hit shows its number");
require(!damage_layout::shows_number(5221009) && !damage_layout::shows_number(1221011),
"Hypnotize's and Heaven's Hammer's don't");

require(damage_layout::left(66) == -33 && damage_layout::left(67) == -33, "the canvas is centred, rounding left");

require(damage_layout::opacity(0) == 1.0f && damage_layout::opacity(400) == 1.0f, "opaque for 400 ms");
Expand Down
13 changes: 13 additions & 0 deletions HeavenClient/tests/mob_stat_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ int main()
require(reset.read_byte() == 9, "A reset reads past the burns' ids");
require(!MobStatParser::stops(burned), "Burns don't stop the mob");

// Hypnotize's Dazzle (28) is a plain status after the burns.
BuffParser::Flags dazzle{ 0, 0 };
dazzle.set(MobStatParser::DAZZLE);
Writer d;
d.status(1, 5221009, 21).put(int8_t(9));

InPacket dazzled(d.bytes.data(), d.bytes.size());
std::vector<MobStatParser::Status> hypnotized = MobStatParser::parse(dazzled, dazzle);
require(hypnotized.size() == 1 && hypnotized[0].stat == 28 && hypnotized[0].reason == 5221009 &&
hypnotized[0].duration == 21, "Dazzle is read as bit 28");
require(dazzled.read_byte() == 9, "And nothing more");
require(!MobStatParser::stops(dazzle), "Dazzle doesn't stop the mob");

std::cout << "mob stat parser tests passed\n";
return 0;
}
Expand Down
Loading