Skip to content

Commit

Permalink
You can now grab cards from chat! The GM can grab any cards from chat…
Browse files Browse the repository at this point in the history
…, while players can only grab discarded cards from chat IF the deck settings allows it.

There has been very little testing on this with connected clients, but each of the features was individually tested and works. Haven't done much regression testing.
  • Loading branch information
Gtaray committed Jan 6, 2023
1 parent d8d75c2 commit cdd50c2
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 13 deletions.
7 changes: 7 additions & 0 deletions deckbox/deckbox_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@
<combobox_setting name="discard_to_deck" source="settings.discardtodeck">
<parameter>DECK_SETTING_DISCARD_TO_DECK</parameter>
</combobox_setting>

<setting_label name="players_can_grab_discards_label">
<static textres="deckbox_setting_label_allow_picking_from_discard" />
</setting_label>
<combobox_setting name="players_can_grab_discards" source="settings.playerscangrabdiscards">
<parameter>DECK_SETTING_PLAYERS_CAN_GRAB_DISCARDS</parameter>
</combobox_setting>
</sheetdata>
</windowclass>
</root>
29 changes: 27 additions & 2 deletions scripts/decked_out_events.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ DECKEDOUT_EVENT_CARD_PLAYED = "cardplayed";
DECKEDOUT_EVENT_CARD_DISCARDED = "carddiscarded";
DECKEDOUT_EVENT_CARD_GIVEN = "cardgiven";
DECKEDOUT_EVENT_CARD_DEALT = "carddealt";
DECKEDOUT_EVENT_DEALT_FROM_DISCARD = "dealtfromdiscard";
DECKEDOUT_EVENT_CARD_ADDED_TO_STORAGE = "cardaddedtostorage";
DECKEDOUT_EVENT_CARD_MOVED = "cardmoved";
DECKEDOUT_EVENT_CARD_PUT_BACK_IN_DECK = "cardputbackindeck";
Expand Down Expand Up @@ -324,12 +325,17 @@ end
---@param vCard databasenode
---@param vDeck databasenode
---@param sIdentity string
---@param bFacedown boolean
---@param tEventTrace table
---@return table tEventTrace
function raiseOnCardReturnedToDeckEvent(vCard, vDeck, sIdentity, tEventTrace)
function raiseOnCardReturnedToDeckEvent(vCard, vDeck, sIdentity, bFacedown, tEventTrace)
local tArgs = { sIdentity = sIdentity, sCardNode = vCard.getNodeName(), sDeckNode = vDeck.getNodeName() }
if bFacedown then
tArgs.bFacedown = "true";
end
return DeckedOutEvents.raiseEvent(
DeckedOutEvents.DECKEDOUT_EVENT_CARD_PUT_BACK_IN_DECK,
{ sIdentity = sIdentity, sCardNode = vCard.getNodeName(), sDeckNode = vDeck.getNodeName() },
tArgs,
tEventTrace
);
end
Expand Down Expand Up @@ -391,6 +397,25 @@ function raiseOnDealCardEvent(vCard, sIdentity, bFacedown, tEventTrace)
);
end

---Raises the onCardDealtFromDiscard event event.
---@param vCard databasenode Card that is added to hand, AFTER is is added to the hand
---@param sIdentity string Character identity (or 'gm') of the person whose hand the card is being added to
---@param tEventTrace table. Event trace table
---@return table tEventTrace Event trace table
function raiseOnCardDealtFromDiscardEvent(vCard, sIdentity, bFacedown, tEventTrace)
local tArgs = { sCardNode = vCard.getNodeName(), sIdentity = sIdentity, bFacedown = "false" };
if bFacedown then
tArgs.bFacedown = "true";
end

return DeckedOutEvents.raiseEvent(
DeckedOutEvents.DECKEDOUT_EVENT_DEALT_FROM_DISCARD,
tArgs,
tEventTrace,
true -- True because this event technically happens after moveCard, and by then the trace is already updated
);
end

---Raises the onCardFlipped event
---@param vCard databasenode
---@param sIdentity string Charater identity (or 'gm') of the person flipping the card
Expand Down
32 changes: 32 additions & 0 deletions scripts/decked_out_messages.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ function onInit()
DeckedOutEvents.registerEvent(DeckedOutEvents.DECKEDOUT_EVENT_HAND_DISCARD_RANDOM, { fCallback = printRandomCardDiscardedMessage, sTarget = "host" });
DeckedOutEvents.registerEvent(DeckedOutEvents.DECKEDOUT_EVENT_CARD_GIVEN, { fCallback = printCardGivenMessage, sTarget = "host" });
DeckedOutEvents.registerEvent(DeckedOutEvents.DECKEDOUT_EVENT_CARD_DEALT, { fCallback = printCardDealtMessage, sTarget = "host" });
DeckedOutEvents.registerEvent(DeckedOutEvents.DECKEDOUT_EVENT_DEALT_FROM_DISCARD, { fCallback = printCardDealtFromDiscardMessage, sTarget = "host" });
DeckedOutEvents.registerEvent(DeckedOutEvents.DECKEDOUT_EVENT_HAND_DISCARDED, { fCallback = printHandDiscardedMessage, sTarget = "host" });
DeckedOutEvents.registerEvent(DeckedOutEvents.DECKEDOUT_EVENT_MULTIPLE_CARDS_DEALT, { fCallback = printMultipleCardsDealtMessage, sTarget = "host" });
DeckedOutEvents.registerEvent(DeckedOutEvents.DECKEDOUT_EVENT_GROUP_DEAL, { fCallback = printGroupDealMessage, sTarget = "host" });
Expand Down Expand Up @@ -324,6 +325,37 @@ function printCardDealtMessage(tEventArgs, tEventTrace)
Comm.deliverOOBMessage(msg, "");
end

function printCardDealtFromDiscardMessage(tEventArgs, tEventTrace)
local vCard = DeckedOutUtilities.validateCard(tEventArgs.sCardNode);
if not vCard then return end

local sCardSource = CardManager.getCardSource(vCard);
if (not sCardSource) or sCardSource == "storage" then
return;
end

local bFacedown = tEventArgs.bFacedown == "true";

local msg = {};
msg.type = DeckedOutMessages.OOB_MSGTYPE_DECKEDOUT_STANDARD;
-- The GM should always be the card source here.
-- If we use value returned from getCardSource,
-- it will always say the PC since we dealt them the card prior to this event
msg.sender = tEventArgs.sIdentity;
msg.card_link = vCard.getNodeName();
msg.action = "deal";
msg.facedown = tEventArgs.bFacedown;
if bFacedown then
msg.text = Interface.getString("chat_msg_deal_card_from_discard_facedown");
else
msg.text = Interface.getString("chat_msg_deal_card_from_discard");
end
msg.text = string.format(msg.text, "[SENDER]", "[CARDNAME]");
msg.icon = "deal";

Comm.deliverOOBMessage(msg, "");
end

function printMultipleCardsDealtMessage(tEventArgs, tEventTrace)
-- If the event trace already contains the group deal cards event, then we don't want to print out any messages, so we bail
if DeckedOutEvents.doesEventTraceContain(tEventTrace, DeckedOutEvents.DECKEDOUT_EVENT_GROUP_DEAL) then
Expand Down
73 changes: 63 additions & 10 deletions scripts/manager_card.lua
Original file line number Diff line number Diff line change
Expand Up @@ -855,15 +855,43 @@ function onDropCard(draginfo, vDestination, sExtra)
return false;
end

-- Figure out if the card should be passed facedown
-- This happens before the card storage check because doing stuff
-- from card storage will reset the facedown flag
-- (since it doesn't preserve in storage)
local bFacedown = draginfo.getNumberData() == 0;

-- If this item was dragged from card storage (i.e. the chat) then do nothing
-- Items in chat should never be moved or handled by anything, they're read only
if CardStorage.doesCardComeFromStorage(sRecord) then
Debug.console("WARNING: Tried to drag/drop a card from chat. Card links in chat cannot be moved and are read-only.");
return false;
end
-- Check the deck setting for the card to see if players are allowed
-- to grab cards from chat. GMs can always do this, and they're allowed
-- to grab from anywhere, not just discards
local bAllowPlayerGrab = DeckManager.getDeckSetting(
CardManager.getDeckNodeFromCard(sRecord),
DeckManager.DECK_SETTING_PLAYERS_CAN_GRAB_DISCARDS) == "yes";

-- Do all the negative checks first for ease of readability.
if not Session.IsHost then
if not bAllowPlayerGrab then
Comm.addChatMessage( { font = "systemfont", text = "You are not allowed to grab cards from chat."});
return false;
end
if not CardStorage.isCardOriginADiscardPile(sRecord) then
Comm.addChatMessage( { font = "systemfont", text = "The card you are trying to drag from chat is not currently discarded."});
return false;
end
end

-- Figure out if the card should be passed facedown
local bFacedown = draginfo.getNumberData() == 0;
local cardnode = DB.findNode(CardStorage.getCardOrigin(sRecord));
if cardnode then
sRecord = cardnode.getNodeName();
bFacedown = DeckedOutUtilities.getFacedownHotkey();
else
Comm.addChatMessage( { font = "systemfont", text = "The card you're trying to grab cannot be located." } );
return false;
end
end

sDestPath = vDestination.getNodeName();

Expand Down Expand Up @@ -957,14 +985,39 @@ function handleAnyDrop(sSourceNode, sDestinationNode, sExtra, bFacedown)
DeckManager.DECK_SETTING_DEFAULT_DEAL_FACING)
bFacedown = bFacedown or bDefaultFacing == "facedown";

tEventTrace = DeckedOutEvents.addEventTrace(tEventTrace, DeckedOutEvents.DECKEDOUT_EVENT_CARD_DEALT);
local card = CardManager.addCardToHand(vCard, sReceivingIdentity, bFacedown, tEventTrace);
DeckedOutEvents.raiseOnDealCardEvent(card, sReceivingIdentity, bFacedown, tEventTrace)
-- If we're fishing this card from the discard pile, then we want to raise a different event
if CardManager.isCardDiscarded(vCard) then
tEventTrace = DeckedOutEvents.addEventTrace(tEventTrace, DeckedOutEvents.DECKEDOUT_EVENT_DEALT_FROM_DISCARD);
local card = CardManager.addCardToHand(vCard, sReceivingIdentity, bFacedown, tEventTrace);
DeckedOutEvents.raiseOnCardDealtFromDiscardEvent(card, sReceivingIdentity, bFacedown, tEventTrace)
else
tEventTrace = DeckedOutEvents.addEventTrace(tEventTrace, DeckedOutEvents.DECKEDOUT_EVENT_CARD_DEALT);
local card = CardManager.addCardToHand(vCard, sReceivingIdentity, bFacedown, tEventTrace);
DeckedOutEvents.raiseOnDealCardEvent(card, sReceivingIdentity, bFacedown, tEventTrace)
end
return true;
end
else
local card = CardManager.moveCard(vCard, vDestination, tEventTrace);
return true;
if StringManager.startsWith(vDestination.getNodeName(), "deckbox") then
-- We dropped onto the deck, so we fire the return to deck event
if vDestination.getName() == DeckManager.DECK_CARDS_PATH then
local sGiverIdentity = CardManager.getCardSource(vCard);
DeckedOutEvents.raiseOnCardReturnedToDeckEvent(
vCard,
CardManager.getDeckNodeFromCard(vCard),
sGiverIdentity,
bFacedown,
tEventTrace);
CardManager.moveCard(vCard, vDestination, tEventTrace);
return true;
elseif vDestination.getName() == DeckManager.DECK_DISCARD_PATH then
local sGiverIdentity = CardManager.getCardSource(vCard);
local card = discardCard(vCard, bFacedown, sGiverIdentity, tEventTrace);
end
else
local card = CardManager.moveCard(vCard, vDestination, tEventTrace);
return true;
end
end
end

Expand Down
10 changes: 10 additions & 0 deletions scripts/manager_deck.lua
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,9 @@ DECK_SETTING_AUTO_PLAY_FROM_DECK = "autoplayfromdeck";
-- Are cards that are discarded put directly back in the deck
-- Default: no
DECK_SETTING_DISCARD_TO_DECK = "discardtodeck";
-- Are players allowed to grab discarded cards from chat and drag them to their hand
-- Default: no
DECK_SETTING_PLAYERS_CAN_GRAB_DISCARDS = "playerscangrabdiscards"

local _tSettingOptions = {
[DECK_SETTING_DEFAULT_DEAL_FACING] = {
Expand Down Expand Up @@ -408,6 +411,13 @@ local _tSettingOptions = {
{ sTextRes = "deckbox_setting_option_yes", sValue = "yes" },
{ sTextRes = "deckbox_setting_option_no", sValue = "no" },
}
},
[DECK_SETTING_PLAYERS_CAN_GRAB_DISCARDS] = {
default = "no",
options = {
{ sTextRes = "deckbox_setting_option_yes", sValue = "yes" },
{ sTextRes = "deckbox_setting_option_no", sValue = "no" },
}
}
}

Expand Down
41 changes: 40 additions & 1 deletion scripts/manager_storage.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
CARD_STORAGE_PATH = "deckbox.storage";
CARD_ORIGIN_PATH = "origin"

-- Indexed by the token string, contains the DB node in storage
local _storage = {};
Expand All @@ -24,7 +25,12 @@ function addCardToStorage(vCard, tEventTrace)
if not vCard then return end

if CardStorage.isCardInStorage(vCard) then
return CardStorage.getCardFromStorage(vCard);
local storageCard = CardStorage.getCardFromStorage(vCard);

-- Update the card's origin every time we re-get it
CardStorage.setCardOrigin(storageCard, vCard.getNodeName());

return storageCard;
end

local newCard = DB.copyNode(vCard, DB.createChild(CardStorage.getCardStorageNode()));
Expand All @@ -33,6 +39,10 @@ function addCardToStorage(vCard, tEventTrace)
-- We don't want card facing to be stored
CardManager.deleteFacingNode(newCard);

-- Save the actual location of the card so that we can drag/drop from this entry
-- This is a crude way of getting cards back from the discard
CardStorage.setCardOrigin(newCard, vCard.getNodeName());

_storage[sToken] = newCard;

tEventTrace = DeckedOutEvents.raiseOnCardAddedToStorageEvent(newCard, tEventTrace);
Expand Down Expand Up @@ -63,6 +73,7 @@ function getCardFromStorage(vCard)
if not vCard then return end

local sToken = CardManager.getCardFront(vCard);

return _storage[sToken];
end

Expand All @@ -81,4 +92,32 @@ end
---@return string
function getCardStorageNode()
return DB.findNode(CardStorage.CARD_STORAGE_PATH);
end

---Sets the origin value of a card that's in storage
---@param storageCardNode databasenode
---@param sOrigin string
function setCardOrigin(storageCardNode, sOrigin)
DB.setValue(storageCardNode, CardStorage.CARD_ORIGIN_PATH, "string", sOrigin);
end

---Gets the origin value for a card that's in storage
---@param vCard databasenode|string
---@return string
function getCardOrigin(vCard)
local vCard = DeckedOutUtilities.validateCard(vCard);
if not vCard then return end

return DB.getValue(vCard, CardStorage.CARD_ORIGIN_PATH, "");
end

---Checks if the origin for a card in storage is a discard pile
---@param vCard databasenode|string
---@return boolean
function isCardOriginADiscardPile(vCard)
local vCard = DeckedOutUtilities.validateCard(vCard);
if not vCard then return end

local sOrigin = CardStorage.getCardOrigin(vCard);
return string.find(sOrigin, "." .. DeckManager.DECK_DISCARD_PATH .. ".") ~= nil;
end
3 changes: 3 additions & 0 deletions strings/cards_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@

<string name="deckbox_settings_header_misc">Miscellaneous</string>
<string name="deckbox_settings_label_discard_to_deck">Put discarded cards back into the deck?</string>
<string name="deckbox_setting_label_allow_picking_from_discard">Allow players to pick up cards that are discarded from chat?</string>

<string name="deckbox_settings_option_gm">The GM</string>
<string name="deckbox_settings_option_everyone">Everyone</string>
Expand Down Expand Up @@ -153,6 +154,8 @@
<string name="chat_msg_card_randomly_give_facedown">%s randomly gave %s to %s facedown</string>
<string name="chat_msg_deal_card">%s dealt %s to %s</string>
<string name="chat_msg_deal_card_facedown">%s dealt %s to %s facedown</string>
<string name="chat_msg_deal_card_from_discard">%s grabbed %s from the discard pile</string>
<string name="chat_msg_deal_card_from_discard_facedown">%s grabbed %s from the discard pile facedown</string>
<string name="chat_msg_deal_multiple_cards">%s dealt %s %s to %s</string>
<string name="chat_msg_group_deal">%s dealt %s %s to everyone</string>
<string name="chat_msg_card_put_back_in_deck">%s shuffled %s back into its deck</string>
Expand Down

0 comments on commit cdd50c2

Please sign in to comment.