added 'undo last draw' feature. protocol version bump (12)

This commit is contained in:
Max-Wilhelm Bruker 2011-01-27 17:49:31 +01:00
parent c203d51f43
commit 7116382a96
25 changed files with 1763 additions and 1419 deletions

View file

@ -138,26 +138,32 @@ void CommandContainer::setResponse(ProtocolResponse *_resp)
resp = _resp;
}
void CommandContainer::enqueueGameEventPublic(GameEvent *event, int gameId)
void CommandContainer::enqueueGameEventPublic(GameEvent *event, int gameId, GameEventContext *context)
{
if (!gameEventQueuePublic)
gameEventQueuePublic = new GameEventContainer(QList<GameEvent *>(), gameId);
gameEventQueuePublic->addGameEvent(event);
if (context)
gameEventQueuePublic->setContext(context);
}
void CommandContainer::enqueueGameEventOmniscient(GameEvent *event, int gameId)
void CommandContainer::enqueueGameEventOmniscient(GameEvent *event, int gameId, GameEventContext *context)
{
if (!gameEventQueueOmniscient)
gameEventQueueOmniscient = new GameEventContainer(QList<GameEvent *>(), gameId);
gameEventQueueOmniscient->addGameEvent(event);
if (context)
gameEventQueueOmniscient->setContext(context);
}
void CommandContainer::enqueueGameEventPrivate(GameEvent *event, int gameId, int playerId)
void CommandContainer::enqueueGameEventPrivate(GameEvent *event, int gameId, int playerId, GameEventContext *context)
{
if (!gameEventQueuePrivate)
gameEventQueuePrivate = new GameEventContainer(QList<GameEvent *>(), gameId);
gameEventQueuePrivate->addGameEvent(event);
privatePlayerId = playerId;
if (context)
gameEventQueuePrivate->setContext(context);
}
Command_DeckUpload::Command_DeckUpload(DeckList *_deck, const QString &_path)

View file

@ -17,6 +17,7 @@ class ProtocolResponse;
class DeckList;
class GameEvent;
class GameEventContainer;
class GameEventContext;
class MoveCardToZone;
enum ItemId {
@ -55,7 +56,7 @@ private:
static void initializeHashAuto();
bool receiverMayDelete;
public:
static const int protocolVersion = 11;
static const int protocolVersion = 12;
static void initializeHash();
virtual int getItemId() const = 0;
bool getReceiverMayDelete() const { return receiverMayDelete; }
@ -132,11 +133,11 @@ public:
const QList<ProtocolItem *> &getItemQueue() const { return itemQueue; }
void enqueueItem(ProtocolItem *item) { itemQueue.append(item); }
GameEventContainer *getGameEventQueuePublic() const { return gameEventQueuePublic; }
void enqueueGameEventPublic(GameEvent *event, int gameId);
void enqueueGameEventPublic(GameEvent *event, int gameId, GameEventContext *context = 0);
GameEventContainer *getGameEventQueueOmniscient() const { return gameEventQueueOmniscient; }
void enqueueGameEventOmniscient(GameEvent *event, int gameId);
void enqueueGameEventOmniscient(GameEvent *event, int gameId, GameEventContext *context = 0);
GameEventContainer *getGameEventQueuePrivate() const { return gameEventQueuePrivate; }
void enqueueGameEventPrivate(GameEvent *event, int gameId, int playerId = -1);
void enqueueGameEventPrivate(GameEvent *event, int gameId, int playerId = -1, GameEventContext *context = 0);
int getPrivatePlayerId() const { return privatePlayerId; }
};

View file

@ -21,53 +21,55 @@ ItemId_Command_Shuffle = 1019,
ItemId_Command_Mulligan = 1020,
ItemId_Command_RollDie = 1021,
ItemId_Command_DrawCards = 1022,
ItemId_Command_FlipCard = 1023,
ItemId_Command_AttachCard = 1024,
ItemId_Command_CreateToken = 1025,
ItemId_Command_CreateArrow = 1026,
ItemId_Command_DeleteArrow = 1027,
ItemId_Command_SetCardAttr = 1028,
ItemId_Command_SetCardCounter = 1029,
ItemId_Command_IncCardCounter = 1030,
ItemId_Command_ReadyStart = 1031,
ItemId_Command_Concede = 1032,
ItemId_Command_IncCounter = 1033,
ItemId_Command_CreateCounter = 1034,
ItemId_Command_SetCounter = 1035,
ItemId_Command_DelCounter = 1036,
ItemId_Command_NextTurn = 1037,
ItemId_Command_SetActivePhase = 1038,
ItemId_Command_DumpZone = 1039,
ItemId_Command_StopDumpZone = 1040,
ItemId_Command_RevealCards = 1041,
ItemId_Event_Say = 1042,
ItemId_Event_Leave = 1043,
ItemId_Event_GameClosed = 1044,
ItemId_Event_Shuffle = 1045,
ItemId_Event_RollDie = 1046,
ItemId_Event_MoveCard = 1047,
ItemId_Event_FlipCard = 1048,
ItemId_Event_DestroyCard = 1049,
ItemId_Event_AttachCard = 1050,
ItemId_Event_CreateToken = 1051,
ItemId_Event_DeleteArrow = 1052,
ItemId_Event_SetCardAttr = 1053,
ItemId_Event_SetCardCounter = 1054,
ItemId_Event_SetCounter = 1055,
ItemId_Event_DelCounter = 1056,
ItemId_Event_SetActivePlayer = 1057,
ItemId_Event_SetActivePhase = 1058,
ItemId_Event_DumpZone = 1059,
ItemId_Event_StopDumpZone = 1060,
ItemId_Event_ServerMessage = 1061,
ItemId_Event_Message = 1062,
ItemId_Event_GameJoined = 1063,
ItemId_Event_UserLeft = 1064,
ItemId_Event_LeaveRoom = 1065,
ItemId_Event_RoomSay = 1066,
ItemId_Context_ReadyStart = 1067,
ItemId_Context_Concede = 1068,
ItemId_Context_DeckSelect = 1069,
ItemId_Command_UpdateServerMessage = 1070,
ItemId_Other = 1071
ItemId_Command_UndoDraw = 1023,
ItemId_Command_FlipCard = 1024,
ItemId_Command_AttachCard = 1025,
ItemId_Command_CreateToken = 1026,
ItemId_Command_CreateArrow = 1027,
ItemId_Command_DeleteArrow = 1028,
ItemId_Command_SetCardAttr = 1029,
ItemId_Command_SetCardCounter = 1030,
ItemId_Command_IncCardCounter = 1031,
ItemId_Command_ReadyStart = 1032,
ItemId_Command_Concede = 1033,
ItemId_Command_IncCounter = 1034,
ItemId_Command_CreateCounter = 1035,
ItemId_Command_SetCounter = 1036,
ItemId_Command_DelCounter = 1037,
ItemId_Command_NextTurn = 1038,
ItemId_Command_SetActivePhase = 1039,
ItemId_Command_DumpZone = 1040,
ItemId_Command_StopDumpZone = 1041,
ItemId_Command_RevealCards = 1042,
ItemId_Event_Say = 1043,
ItemId_Event_Leave = 1044,
ItemId_Event_GameClosed = 1045,
ItemId_Event_Shuffle = 1046,
ItemId_Event_RollDie = 1047,
ItemId_Event_MoveCard = 1048,
ItemId_Event_FlipCard = 1049,
ItemId_Event_DestroyCard = 1050,
ItemId_Event_AttachCard = 1051,
ItemId_Event_CreateToken = 1052,
ItemId_Event_DeleteArrow = 1053,
ItemId_Event_SetCardAttr = 1054,
ItemId_Event_SetCardCounter = 1055,
ItemId_Event_SetCounter = 1056,
ItemId_Event_DelCounter = 1057,
ItemId_Event_SetActivePlayer = 1058,
ItemId_Event_SetActivePhase = 1059,
ItemId_Event_DumpZone = 1060,
ItemId_Event_StopDumpZone = 1061,
ItemId_Event_ServerMessage = 1062,
ItemId_Event_Message = 1063,
ItemId_Event_GameJoined = 1064,
ItemId_Event_UserLeft = 1065,
ItemId_Event_LeaveRoom = 1066,
ItemId_Event_RoomSay = 1067,
ItemId_Context_ReadyStart = 1068,
ItemId_Context_Concede = 1069,
ItemId_Context_DeckSelect = 1070,
ItemId_Context_UndoDraw = 1071,
ItemId_Command_UpdateServerMessage = 1072,
ItemId_Other = 1073
};

View file

@ -114,6 +114,10 @@ Command_DrawCards::Command_DrawCards(int _gameId, int _number)
{
insertItem(new SerializableItem_Int("number", _number));
}
Command_UndoDraw::Command_UndoDraw(int _gameId)
: GameCommand("undo_draw", _gameId)
{
}
Command_FlipCard::Command_FlipCard(int _gameId, const QString &_zone, int _cardId, bool _faceDown)
: GameCommand("flip_card", _gameId)
{
@ -425,6 +429,10 @@ Context_DeckSelect::Context_DeckSelect(int _deckId)
{
insertItem(new SerializableItem_Int("deck_id", _deckId));
}
Context_UndoDraw::Context_UndoDraw()
: GameEventContext("undo_draw")
{
}
Command_UpdateServerMessage::Command_UpdateServerMessage()
: AdminCommand("update_server_message")
{
@ -453,6 +461,7 @@ void ProtocolItem::initializeHashAuto()
itemNameHash.insert("cmdmulligan", Command_Mulligan::newItem);
itemNameHash.insert("cmdroll_die", Command_RollDie::newItem);
itemNameHash.insert("cmddraw_cards", Command_DrawCards::newItem);
itemNameHash.insert("cmdundo_draw", Command_UndoDraw::newItem);
itemNameHash.insert("cmdflip_card", Command_FlipCard::newItem);
itemNameHash.insert("cmdattach_card", Command_AttachCard::newItem);
itemNameHash.insert("cmdcreate_token", Command_CreateToken::newItem);
@ -500,5 +509,6 @@ void ProtocolItem::initializeHashAuto()
itemNameHash.insert("game_event_contextready_start", Context_ReadyStart::newItem);
itemNameHash.insert("game_event_contextconcede", Context_Concede::newItem);
itemNameHash.insert("game_event_contextdeck_select", Context_DeckSelect::newItem);
itemNameHash.insert("game_event_contextundo_draw", Context_UndoDraw::newItem);
itemNameHash.insert("cmdupdate_server_message", Command_UpdateServerMessage::newItem);
}

View file

@ -20,6 +20,7 @@
2:mulligan
2:roll_die:i,sides
2:draw_cards:i,number
2:undo_draw
2:flip_card:s,zone:i,card_id:b,face_down
2:attach_card:s,start_zone:i,card_id:i,target_player_id:s,target_zone:i,target_card_id
2:create_token:s,zone:s,card_name:s,color:s,pt:s,annotation:b,destroy:i,x:i,y
@ -67,4 +68,5 @@
6:ready_start
6:concede
6:deck_select:i,deck_id
6:undo_draw
7:update_server_message

View file

@ -182,6 +182,13 @@ public:
static SerializableItem *newItem() { return new Command_DrawCards; }
int getItemId() const { return ItemId_Command_DrawCards; }
};
class Command_UndoDraw : public GameCommand {
Q_OBJECT
public:
Command_UndoDraw(int _gameId = -1);
static SerializableItem *newItem() { return new Command_UndoDraw; }
int getItemId() const { return ItemId_Command_UndoDraw; }
};
class Command_FlipCard : public GameCommand {
Q_OBJECT
public:
@ -634,6 +641,13 @@ public:
static SerializableItem *newItem() { return new Context_DeckSelect; }
int getItemId() const { return ItemId_Context_DeckSelect; }
};
class Context_UndoDraw : public GameEventContext {
Q_OBJECT
public:
Context_UndoDraw();
static SerializableItem *newItem() { return new Context_UndoDraw; }
int getItemId() const { return ItemId_Context_UndoDraw; }
};
class Command_UpdateServerMessage : public AdminCommand {
Q_OBJECT
public:

View file

@ -150,6 +150,8 @@ void Server_Player::clearZones()
while (arrowIterator.hasNext())
delete arrowIterator.next().value();
arrows.clear();
lastDrawList.clear();
}
ServerInfo_PlayerProperties *Server_Player::getProperties()
@ -199,6 +201,37 @@ bool Server_Player::deleteCounter(int counterId)
return true;
}
ResponseCode Server_Player::drawCards(CommandContainer *cont, int number)
{
Server_CardZone *deckZone = zones.value("deck");
Server_CardZone *handZone = zones.value("hand");
if (deckZone->cards.size() < number)
number = deckZone->cards.size();
QList<ServerInfo_Card *> cardListPrivate;
QList<ServerInfo_Card *> cardListOmniscient;
for (int i = 0; i < number; ++i) {
Server_Card *card = deckZone->cards.takeFirst();
handZone->cards.append(card);
lastDrawList.append(card->getId());
cardListPrivate.append(new ServerInfo_Card(card->getId(), card->getName()));
cardListOmniscient.append(new ServerInfo_Card(card->getId(), card->getName()));
}
cont->enqueueGameEventPrivate(new Event_DrawCards(playerId, cardListPrivate.size(), cardListPrivate), game->getGameId());
cont->enqueueGameEventOmniscient(new Event_DrawCards(playerId, cardListOmniscient.size(), cardListOmniscient), game->getGameId());
cont->enqueueGameEventPublic(new Event_DrawCards(playerId, cardListPrivate.size()), game->getGameId());
return RespOk;
}
ResponseCode Server_Player::undoDraw(CommandContainer *cont)
{
if (lastDrawList.isEmpty())
return RespContextError;
return moveCard(cont, zones.value("hand"), QList<int>() << lastDrawList.takeLast(), zones.value("deck"), 0, 0, false, false, false, true);
}
ResponseCode Server_Player::moveCard(CommandContainer *cont, const QString &_startZone, const QList<int> &_cardIds, int targetPlayerId, const QString &_targetZone, int x, int y, bool faceDown, bool tapped)
{
Server_CardZone *startzone = getZones().value(_startZone);
@ -233,7 +266,7 @@ public:
}
};
ResponseCode Server_Player::moveCard(CommandContainer *cont, Server_CardZone *startzone, const QList<int> &_cardIds, Server_CardZone *targetzone, int x, int y, bool faceDown, bool tapped, bool fixFreeSpaces)
ResponseCode Server_Player::moveCard(CommandContainer *cont, Server_CardZone *startzone, const QList<int> &_cardIds, Server_CardZone *targetzone, int x, int y, bool faceDown, bool tapped, bool fixFreeSpaces, bool undoingDraw)
{
// Disallow controller change to other zones than the table.
if (((targetzone->getType() != PublicZone) || !targetzone->hasCoords()) && (startzone->getPlayer() != targetzone->getPlayer()))
@ -262,6 +295,13 @@ ResponseCode Server_Player::moveCard(CommandContainer *cont, Server_CardZone *st
Server_Card *card = cardsToMove[cardIndex].first;
int originalPosition = cardsToMove[cardIndex].second;
int position = startzone->removeCard(card);
if (startzone->getName() == "hand") {
if (undoingDraw)
lastDrawList.removeAt(lastDrawList.indexOf(card->getId()));
else if (lastDrawList.contains(card->getId()))
lastDrawList.clear();
}
if ((startzone == targetzone) && !startzone->hasCoords()) {
if (!secondHalf && (originalPosition < x)) {
xIndex = -1;
@ -346,8 +386,8 @@ ResponseCode Server_Player::moveCard(CommandContainer *cont, Server_CardZone *st
int privatePosition = -1;
if (startzone->getType() == HiddenZone)
privatePosition = position;
cont->enqueueGameEventPrivate(new Event_MoveCard(getPlayerId(), privateOldCardId, privateCardName, startzone->getName(), privatePosition, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), newX, y, privateNewCardId, faceDown), game->getGameId());
cont->enqueueGameEventOmniscient(new Event_MoveCard(getPlayerId(), privateOldCardId, privateCardName, startzone->getName(), privatePosition, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), newX, y, privateNewCardId, faceDown), game->getGameId());
cont->enqueueGameEventPrivate(new Event_MoveCard(getPlayerId(), privateOldCardId, privateCardName, startzone->getName(), privatePosition, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), newX, y, privateNewCardId, faceDown), game->getGameId(), -1, undoingDraw ? new Context_UndoDraw : 0);
cont->enqueueGameEventOmniscient(new Event_MoveCard(getPlayerId(), privateOldCardId, privateCardName, startzone->getName(), privatePosition, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), newX, y, privateNewCardId, faceDown), game->getGameId(), undoingDraw ? new Context_UndoDraw : 0);
// Other players do not get to see the start and/or target position of the card if the respective
// part of the zone is being looked at. The information is not needed anyway because in hidden zones,
@ -361,9 +401,9 @@ ResponseCode Server_Player::moveCard(CommandContainer *cont, Server_CardZone *st
newX = -1;
if ((startzone->getType() == PublicZone) || (targetzone->getType() == PublicZone))
cont->enqueueGameEventPublic(new Event_MoveCard(getPlayerId(), oldCardId, publicCardName, startzone->getName(), position, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), newX, y, card->getId(), faceDown), game->getGameId());
cont->enqueueGameEventPublic(new Event_MoveCard(getPlayerId(), oldCardId, publicCardName, startzone->getName(), position, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), newX, y, card->getId(), faceDown), game->getGameId(), undoingDraw ? new Context_UndoDraw : 0);
else
cont->enqueueGameEventPublic(new Event_MoveCard(getPlayerId(), -1, QString(), startzone->getName(), position, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), newX, y, -1, false), game->getGameId());
cont->enqueueGameEventPublic(new Event_MoveCard(getPlayerId(), -1, QString(), startzone->getName(), position, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), newX, y, -1, false), game->getGameId(), undoingDraw ? new Context_UndoDraw : 0);
if (tapped)
setCardAttrHelper(cont, targetzone->getName(), card->getId(), "tapped", "1");

View file

@ -30,6 +30,7 @@ private:
QMap<QString, Server_CardZone *> zones;
QMap<int, Server_Counter *> counters;
QMap<int, Server_Arrow *> arrows;
QList<int> lastDrawList;
int playerId;
bool spectator;
int initialCards;
@ -75,8 +76,10 @@ public:
void clearZones();
void setupZones();
ResponseCode drawCards(CommandContainer *cont, int number);
ResponseCode undoDraw(CommandContainer *cont);
ResponseCode moveCard(CommandContainer *cont, const QString &_startZone, const QList<int> &_cardId, int _targetPlayer, const QString &_targetZone, int _x, int _y, bool _faceDown, bool _tapped);
ResponseCode moveCard(CommandContainer *cont, Server_CardZone *startzone, const QList<int> &_cardId, Server_CardZone *targetzone, int x, int y, bool faceDown, bool tapped, bool fixFreeSpaces = true);
ResponseCode moveCard(CommandContainer *cont, Server_CardZone *startzone, const QList<int> &_cardId, Server_CardZone *targetzone, int x, int y, bool faceDown, bool tapped, bool fixFreeSpaces = true, bool undoingDraw = false);
void unattachCard(CommandContainer *cont, Server_Card *card);
ResponseCode setCardAttrHelper(CommandContainer *cont, const QString &zone, int cardId, const QString &attrName, const QString &attrValue);

View file

@ -97,6 +97,7 @@ ResponseCode Server_ProtocolHandler::processCommandHelper(Command *command, Comm
case ItemId_Command_Mulligan: return cmdMulligan(static_cast<Command_Mulligan *>(command), cont, game, player);
case ItemId_Command_RollDie: return cmdRollDie(static_cast<Command_RollDie *>(command), cont, game, player);
case ItemId_Command_DrawCards: return cmdDrawCards(static_cast<Command_DrawCards *>(command), cont, game, player);
case ItemId_Command_UndoDraw: return cmdUndoDraw(static_cast<Command_UndoDraw *>(command), cont, game, player);
case ItemId_Command_MoveCard: return cmdMoveCard(static_cast<Command_MoveCard *>(command), cont, game, player);
case ItemId_Command_FlipCard: return cmdFlipCard(static_cast<Command_FlipCard *>(command), cont, game, player);
case ItemId_Command_AttachCard: return cmdAttachCard(static_cast<Command_AttachCard *>(command), cont, game, player);
@ -501,7 +502,7 @@ ResponseCode Server_ProtocolHandler::cmdMulligan(Command_Mulligan * /*cmd*/, Com
cont->enqueueGameEventPrivate(new Event_Shuffle(player->getPlayerId()), game->getGameId());
cont->enqueueGameEventPublic(new Event_Shuffle(player->getPlayerId()), game->getGameId());
drawCards(game, player, cont, number);
player->drawCards(cont, number);
return RespOk;
}
@ -515,7 +516,7 @@ ResponseCode Server_ProtocolHandler::cmdRollDie(Command_RollDie *cmd, CommandCon
return RespOk;
}
ResponseCode Server_ProtocolHandler::drawCards(Server_Game *game, Server_Player *player, CommandContainer *cont, int number)
ResponseCode Server_ProtocolHandler::cmdDrawCards(Command_DrawCards *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player)
{
if (player->getSpectator())
return RespFunctionNotAllowed;
@ -523,33 +524,20 @@ ResponseCode Server_ProtocolHandler::drawCards(Server_Game *game, Server_Player
if (!game->getGameStarted())
return RespGameNotStarted;
Server_CardZone *deck = player->getZones().value("deck");
Server_CardZone *hand = player->getZones().value("hand");
if (deck->cards.size() < number)
number = deck->cards.size();
QList<ServerInfo_Card *> cardListPrivate;
QList<ServerInfo_Card *> cardListOmniscient;
for (int i = 0; i < number; ++i) {
Server_Card *card = deck->cards.takeFirst();
hand->cards.append(card);
cardListPrivate.append(new ServerInfo_Card(card->getId(), card->getName()));
cardListOmniscient.append(new ServerInfo_Card(card->getId(), card->getName()));
}
cont->enqueueGameEventPrivate(new Event_DrawCards(player->getPlayerId(), cardListPrivate.size(), cardListPrivate), game->getGameId());
cont->enqueueGameEventOmniscient(new Event_DrawCards(player->getPlayerId(), cardListOmniscient.size(), cardListOmniscient), game->getGameId());
cont->enqueueGameEventPublic(new Event_DrawCards(player->getPlayerId(), cardListPrivate.size()), game->getGameId());
return RespOk;
return player->drawCards(cont, cmd->getNumber());
}
ResponseCode Server_ProtocolHandler::cmdDrawCards(Command_DrawCards *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player)
ResponseCode Server_ProtocolHandler::cmdUndoDraw(Command_UndoDraw *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player)
{
return drawCards(game, player, cont, cmd->getNumber());
if (player->getSpectator())
return RespFunctionNotAllowed;
if (!game->getGameStarted())
return RespGameNotStarted;
return player->undoDraw(cont);
}
ResponseCode Server_ProtocolHandler::cmdMoveCard(Command_MoveCard *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player)
{
if (player->getSpectator())

View file

@ -61,8 +61,8 @@ private:
ResponseCode cmdShuffle(Command_Shuffle *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player);
ResponseCode cmdMulligan(Command_Mulligan *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player);
ResponseCode cmdRollDie(Command_RollDie *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player);
ResponseCode drawCards(Server_Game *game, Server_Player *player, CommandContainer *cont, int number);
ResponseCode cmdDrawCards(Command_DrawCards *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player);
ResponseCode cmdUndoDraw(Command_UndoDraw *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player);
ResponseCode cmdMoveCard(Command_MoveCard *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player);
ResponseCode cmdFlipCard(Command_FlipCard *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player);
ResponseCode cmdAttachCard(Command_AttachCard *cmd, CommandContainer *cont, Server_Game *game, Server_Player *player);