Skip to content

Commit 63b532e

Browse files
committed
bm: better end of battle detection. hyper bomber pickups improvements
There's a specific marker in cmd1/2/3 that indicates end of game detection, whether by winning or time out. Get rid of game timer and simplify end of game detection. Released pickups in hyper bomber mode now correctly reappear at random locations and can be picked up again.
1 parent f5475ba commit 63b532e

2 files changed

Lines changed: 67 additions & 92 deletions

File tree

bomberman.cpp

Lines changed: 60 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
using namespace std::chrono_literals;
2525

2626
BMRoom::BMRoom(Lobby& lobby, uint32_t id, const std::string& name, uint32_t attributes, Player *owner, asio::io_context& io_context)
27-
: Room(lobby, id, name, attributes, owner, io_context), timer(io_context), gameTimer(io_context)
27+
: Room(lobby, id, name, attributes, owner, io_context), timer(io_context)
2828
{
2929
// needed after the owner is added in the parent constructor
3030
updateSlots();
@@ -353,10 +353,10 @@ void BMRoom::playerInGame(Player *player)
353353
int idx = getPlayerIndex(player);
354354
if (idx >= 0)
355355
{
356-
states[idx].status = State::MapInfoSent;
356+
states[idx].status = State::InGame;
357357
inGame = true;
358358
for (unsigned i = 0; i < players.size(); i++)
359-
inGame = inGame && (states[i].status == State::MapInfoSent);
359+
inGame = inGame && (states[i].status == State::InGame);
360360
if (inGame)
361361
{
362362
DEBUG_LOG(Game::Bomberman, "%s: all MapInfo sent. sending game time info", player->getName().c_str());
@@ -372,7 +372,6 @@ void BMRoom::playerInGame(Player *player)
372372
packet.writeData(60u * 60u * (unsigned)getTimeLimit().count());
373373
packet.writeData(0u);
374374
Player::sendToAll(packet, players);
375-
startGameTimer();
376375
}
377376
}
378377
}
@@ -429,6 +428,14 @@ void BMRoom::savePlayerCoords(Player *player, const uint8_t *data)
429428
// cmd2
430429
void BMRoom::savePowerUps(Player *player, const uint8_t *data)
431430
{
431+
// States marked like (this) are set by the server
432+
// Appearing sequence:
433+
// hidden -> appearing -> visible
434+
// Successful pick up
435+
// claimed -> (granted) -> acquired
436+
// TODO: Denied pick up
437+
// Hyper bomber death sequence:
438+
// acquired -> released (with coords, unk1=1) -> (random) -> appearing -> visible
432439
for (unsigned i = 0; i < powerUps.size(); i++)
433440
{
434441
PowerUp pup(data + i * sizeof(pup));
@@ -442,13 +449,17 @@ void BMRoom::savePowerUps(Player *player, const uint8_t *data)
442449
if (pup.state == PowerUp::Visible) {
443450
powerUps[i] = pup;
444451
}
445-
else if (pup.state == PowerUp::Claimed) {
452+
else if (pup.state == PowerUp::Claimed)
453+
{
454+
// TODO wait and arbitrate
446455
pup.state = PowerUp::Granted;
447456
powerUps[i] = pup;
448457
}
449458
break;
450459
case PowerUp::Visible:
451-
if (pup.state == PowerUp::Claimed) {
460+
if (pup.state == PowerUp::Claimed)
461+
{
462+
// TODO wait and arbitrate
452463
pup.state = PowerUp::Granted;
453464
powerUps[i] = pup;
454465
}
@@ -466,19 +477,24 @@ void BMRoom::savePowerUps(Player *player, const uint8_t *data)
466477
powerUps[i] = pup;
467478
}
468479
break;
469-
case PowerUp::Acquired:
480+
case PowerUp::Acquired: // Picked up by a player
470481
// Only transitions to 0 (or 4?) should be allowed? And only by the item owner
471482
if (pup.state == PowerUp::Gone && pup.slot == powerUps[i].slot)
472483
powerUps[i] = pup;
473-
if (pup.state == PowerUp::Consumed && pup.slot == powerUps[i].slot)
474-
// transition to consumed?
484+
if (pup.state == PowerUp::Released && pup.slot == powerUps[i].slot)
485+
{
486+
// item has been released to a random location. Make it visible
487+
pup.state = PowerUp::Random;
475488
powerUps[i] = pup;
476-
else if (pup.state != PowerUp::Acquired && pup.slot == powerUps[i].slot)
489+
DEBUG_LOG(Game::Bomberman, "PowerUp Acquired -> Released (Random). Slot %d coords %d %d", pup.slot, pup.pos.x, pup.pos.y);
490+
}
491+
else if (pup.state != PowerUp::Acquired && pup.slot == powerUps[i].slot) {
477492
WARN_LOG(Game::Bomberman, "PowerUp transitioning from acquired to %x? by slot %d, owner %d",
478493
pup.state, pup.slot, powerUps[i].slot);
494+
}
479495
break;
480496

481-
case PowerUp::Granted:
497+
case PowerUp::Granted: // Granted to a player by the server
482498
if (pup.state == PowerUp::Acquired && pup.slot == powerUps[i].slot)
483499
powerUps[i] = pup;
484500
else if (pup.state != powerUps[i].state
@@ -488,15 +504,27 @@ void BMRoom::savePowerUps(Player *player, const uint8_t *data)
488504
WARN_LOG(Game::Bomberman, "PowerUp current state not handled: Granted -> %x", pup.state);
489505
break;
490506

491-
case PowerUp::Consumed:
492-
if (pup.state == PowerUp::Random && pup.slot == powerUps[i].slot) {
493-
// TODO choose random coords?
507+
case PowerUp::Released: // Released by a dead player in hyper bomber mode
508+
if (pup.state == PowerUp::Random && pup.slot == powerUps[i].slot)
509+
{
510+
// TODO Can this happen now? Can't find any occurrence
494511
powerUps[i] = pup;
495-
DEBUG_LOG(Game::Bomberman, "PowerUp Consumed -> Random. coords %d %d", pup.pos.x, pup.pos.y);
512+
DEBUG_LOG(Game::Bomberman, "PowerUp Released -> Random. coords %d %d", pup.pos.x, pup.pos.y);
496513
}
514+
else if (pup.state == PowerUp::Acquired) {
515+
powerUps[i] = pup;
516+
DEBUG_LOG(Game::Bomberman, "PowerUp Released -> Acquired. Slot %d coords %d %d", pup.slot, pup.pos.x, pup.pos.y);
517+
}
518+
else if (pup.state != powerUps[i].state && pup.slot == powerUps[i].slot)
519+
WARN_LOG(Game::Bomberman, "PowerUp current state not handled: Released -> %x", pup.state);
520+
break;
521+
522+
case PowerUp::Random:
523+
if ((pup.state == PowerUp::Appearing || pup.state == PowerUp::Visible)
524+
&& pup.slot == powerUps[i].slot)
525+
powerUps[i] = pup;
497526
else if (pup.state != powerUps[i].state && pup.slot == powerUps[i].slot)
498-
// TODO Consumed -> 5 after blowing self in hyp mode
499-
WARN_LOG(Game::Bomberman, "PowerUp current state not handled: Consumed -> %x", pup.state);
527+
WARN_LOG(Game::Bomberman, "PowerUp current state not handled: Random -> %x", pup.state);
500528
break;
501529

502530
default:
@@ -560,6 +588,7 @@ void BMRoom::writePlayersPos(Packet& packet)
560588
for (int i = pos + slots; i < 8; i++)
561589
memset(packet.advance(sizeof(CompactUser)), 0, sizeof(CompactUser));
562590
}
591+
563592
void BMRoom::writeTimestamp(Packet& packet)
564593
{
565594
uint32_t latest = 0;
@@ -573,7 +602,7 @@ void BMRoom::makeCmd1Packet(Player *player, Packet& packet)
573602
packet.init(Packet::REQ_CHAT);
574603
BMCmd cmd { BMCmd::BOMB_DATA, 0xC8 };
575604
packet.writeData(cmd.full);
576-
packet.writeData((uint16_t)0); // client id? mask?
605+
packet.writeData((uint16_t)(inGame ? 0 : EOG_MARK));
577606
writePlayersPos(packet);
578607
writeTimestamp(packet);
579608
for (const Bomb& bomb : bombs)
@@ -586,7 +615,7 @@ void BMRoom::makeCmd2Packet(Packet& packet)
586615
packet.init(Packet::REQ_CHAT);
587616
BMCmd cmd { BMCmd::MAP_DATA, 0xA4 };
588617
packet.writeData(cmd.full);
589-
packet.writeData((uint16_t)0); // client id? mask?
618+
packet.writeData((uint16_t)(inGame ? 0 : EOG_MARK));
590619
writePlayersPos(packet);
591620
for (const PowerUp& pup : powerUps)
592621
pup.writeTo(packet.advance(sizeof(PowerUp)));
@@ -598,34 +627,14 @@ void BMRoom::makeCmd3Packet(Packet& packet)
598627
packet.init(Packet::REQ_CHAT);
599628
BMCmd cmd { BMCmd::POS_DATA, 0x3C };
600629
packet.writeData(cmd.full);
601-
packet.writeData((uint16_t)0); // client id? mask?
630+
packet.writeData((uint16_t)(inGame ? 0 : EOG_MARK));
602631
writePlayersPos(packet);
603632
}
604633

605634
std::chrono::minutes BMRoom::getTimeLimit() const {
606635
return std::chrono::minutes(rules[3] + 1);
607636
}
608637

609-
void BMRoom::startGameTimer()
610-
{
611-
// Adding 2 sec to avoid ending the game before the game timer reaches 0
612-
gameTimer.expires_after(getTimeLimit() + 2s);
613-
gameTimer.async_wait([this](const std::error_code& ec) {
614-
if (ec)
615-
return;
616-
INFO_LOG(Game::Bomberman, "Game end: time limit");
617-
for (Player *player : players)
618-
{
619-
// force ending
620-
states[getPlayerIndex(player)].endOfGameMask = 7;
621-
Packet packet;
622-
sendEndOfGame(player, packet);
623-
if (!packet.empty())
624-
player->send(packet);
625-
}
626-
});
627-
}
628-
629638
void BMRoom::saveMapInfo(Player *player, const uint8_t *data, bool last)
630639
{
631640
int idx = getPlayerIndex(player);
@@ -640,49 +649,16 @@ void BMRoom::saveMapInfo(Player *player, const uint8_t *data, bool last)
640649
DEBUG_LOG(Game::Bomberman, "%d bomb(s) per player", bombsPerPlayer);
641650
}
642651

643-
bool BMRoom::checkEndOfGame(Player *player, uint8_t command)
652+
bool BMRoom::checkEndOfGame(Player *player, uint8_t command, uint8_t mark)
644653
{
654+
if (mark != EOG_MARK)
655+
return false;
645656
State& playerState = states[getPlayerIndex(player)];
646-
if (playerState.status != State::MapInfoSent)
657+
if (playerState.status != State::InGame)
647658
return false;
648-
const int mask = 1 << (command - 1);
649-
if (rules[0] == 0)
650-
{
651-
// Survival mode
652-
// Check if more than 1 player is alive
653-
int alivePlayers = 0;
654-
for (unsigned pl = 0; pl < players.size() && alivePlayers <= 1; pl++)
655-
{
656-
const int slots = getSlotCount(players[pl]);
657-
const State& state = states[pl];
658-
for (int slot = 0; slot < slots && alivePlayers <= 1; slot++)
659-
if (!state.dead[slot])
660-
alivePlayers++;
661-
}
662-
if (alivePlayers <= 1) {
663-
INFO_LOG(Game::Bomberman, "Game end: %d player remaining", alivePlayers);
664-
playerState.endOfGameMask |= mask;
665-
return playerState.endOfGameMask == 7;
666-
}
667-
}
668-
else
669-
{
670-
// Hyper-bomber mode
671-
for (unsigned pl = 0; pl < players.size(); pl++)
672-
{
673-
const int slots = getSlotCount(players[pl]);
674-
const State& state = states[pl];
675-
for (int slot = 0; slot < slots; slot++)
676-
// 80 appears briefly when a player respawns: ignore this case
677-
if (state.positions[slot].unk == 0x80 && !state.dead[slot])
678-
{
679-
INFO_LOG(Game::Bomberman, "Game end: player %d won", getPlayerPosition(players[pl]) + slot);
680-
playerState.endOfGameMask |= mask;
681-
return playerState.endOfGameMask == 7;
682-
}
683-
}
684-
}
685-
return false;
659+
INFO_LOG(Game::Bomberman, "[%s] Game end", player->getName().c_str());
660+
playerState.endOfGameMask |= 1 << (command - 1);
661+
return playerState.endOfGameMask == 7;
686662
}
687663

688664
void BMRoom::sendEndOfGame(Player *player, Packet& packet)
@@ -765,7 +741,7 @@ bool BombermanServer::handlePacket(Player *player, const uint8_t *data, size_t l
765741
room->saveBombState(player, data + 0x38);
766742
room->saveBrickMap(player, data + 0xC8);
767743
room->makeCmd1Packet(player, replyPacket);
768-
if (room->checkEndOfGame(player, BMCmd::BOMB_DATA))
744+
if (room->checkEndOfGame(player, BMCmd::BOMB_DATA, data[0x13]))
769745
{
770746
player->send(replyPacket);
771747
replyPacket.reset();
@@ -795,7 +771,7 @@ bool BombermanServer::handlePacket(Player *player, const uint8_t *data, size_t l
795771
room->savePowerUps(player, data + 0x34);
796772
room->saveBrickMap(player, data + 0xA4);
797773
room->makeCmd2Packet(replyPacket);
798-
if (room->checkEndOfGame(player, BMCmd::MAP_DATA))
774+
if (room->checkEndOfGame(player, BMCmd::MAP_DATA, data[0x13]))
799775
{
800776
player->send(replyPacket);
801777
replyPacket.reset();
@@ -814,7 +790,7 @@ bool BombermanServer::handlePacket(Player *player, const uint8_t *data, size_t l
814790
}
815791
room->savePlayerCoords(player, data + 0x14);
816792
room->makeCmd3Packet(replyPacket);
817-
if (room->checkEndOfGame(player, BMCmd::POS_DATA))
793+
if (room->checkEndOfGame(player, BMCmd::POS_DATA, data[0x13]))
818794
{
819795
player->send(replyPacket);
820796
replyPacket.reset();
@@ -1033,8 +1009,7 @@ bool BombermanServer::handlePacket(Player *player, const uint8_t *data, size_t l
10331009
replyPacket.init(Packet::REQ_CHAT);
10341010
replyPacket.writeData(cmd.full);
10351011
replyPacket.writeData((uint16_t)data[0x12]); // Client ID
1036-
replyPacket.writeData(0x10000000u); // LE int. ping value? only lsbyte used. 1,4,10,80,c8 is red
1037-
replyPacket.writeData(room->getSlotMask(player));
1012+
replyPacket.writeData(data + 0x14, 5);
10381013
break;
10391014

10401015
default:

bomberman.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,10 @@ struct PowerUp
162162
Visible = 3,
163163
Expired = 4,
164164
Acquired = 5,
165-
Unknown6 = 6,
166-
Unknown9 = 9, // Granted -> 9 (hyp mode, one occurrence)
165+
//Denied = 6, // ?
166+
Unknown9 = 9, // Granted -> 9 (hyp mode, few occurrences)
167167
Claimed = 0xb,
168-
Consumed = 0xd, // ???
168+
Released = 0xd,
169169
Granted = 0xe,
170170
Random = 0xf,
171171
};
@@ -274,7 +274,7 @@ class BMRoom : public Room
274274
void makeCmd1Packet(Player *player, Packet& packet);
275275
void makeCmd2Packet(Packet& packet);
276276
void makeCmd3Packet(Packet& packet);
277-
bool checkEndOfGame(Player *player, uint8_t command);
277+
bool checkEndOfGame(Player *player, uint8_t command, uint8_t marker);
278278
void sendEndOfGame(Player *player, Packet& packet);
279279

280280
private:
@@ -283,7 +283,6 @@ class BMRoom : public Room
283283
void broadcastKeyholder() const;
284284
void broadcastReadySlotMask() const;
285285
void writePlayersPos(Packet& packet);
286-
void startGameTimer();
287286
void writeTimestamp(Packet& packet);
288287
std::chrono::minutes getTimeLimit() const;
289288
uint32_t getDeadPlayers() const;
@@ -298,7 +297,7 @@ class BMRoom : public Room
298297
InRoom = 2,
299298
RulesAccepted = 3,
300299
MapInfoStarted = 4,
301-
MapInfoSent = 5,
300+
InGame = 5,
302301
GameEnd = 6,
303302
CompletedDeadBits = 8,
304303
};
@@ -311,7 +310,6 @@ class BMRoom : public Room
311310
std::array<State, 8> states;
312311
std::vector<int> slots; // slots used by each player
313312
asio::steady_timer timer;
314-
asio::steady_timer gameTimer;
315313
std::array<uint8_t, 9> rules {};
316314
uint16_t ruleSetter = 0; // client ID of the room owner with msb set (8000)
317315
bool inGame = false;
@@ -320,6 +318,8 @@ class BMRoom : public Room
320318
std::array<Bomb, 24> bombs;
321319
int bombsPerPlayer = 1;
322320
int gameNumber = 0;
321+
322+
static constexpr uint8_t EOG_MARK = 0x14;
323323
};
324324

325325
class BombermanServer : public LobbyServer

0 commit comments

Comments
 (0)