1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Add new hotbar_slot_selected lua callback

This commit is contained in:
Zenon Seth 2025-05-13 14:31:13 +01:00
parent 1f9a3b5875
commit 7425126f6b
8 changed files with 169 additions and 2 deletions

View file

@ -618,6 +618,7 @@ core.registered_allow_player_inventory_actions, core.register_allow_player_inven
core.registered_on_rightclickplayers, core.register_on_rightclickplayer = make_registration()
core.registered_on_liquid_transformed, core.register_on_liquid_transformed = make_registration()
core.registered_on_mapblocks_changed, core.register_on_mapblocks_changed = make_registration()
core.registered_on_hotbar_slot_selected, core.register_on_hotbar_slot_selected = make_registration()
-- A bunch of registrations are read by the C++ side once on env init, so we cannot
-- allow them to change afterwards (see s_env.cpp).

View file

@ -6331,6 +6331,14 @@ Call these functions only at load time!
The set is a table where the keys are hashes and the values are `true`.
* `modified_block_count` is the number of entries in the set.
* Note: callbacks must be registered at mod load time.
* `core.register_on_hotbar_slot_selected(function(player, inventory, list_name, hotbar_slot, prev_hotbar_slot, is_from_scrolling))`
* Called when the player selects a hotbar slot, including re-selecting current slot
* `player`: `ObjectRef` - Player that selected their hotbar slot
* `inventory`: type `InvRef`
* `list_name`: string - the name of the hotbar list
* `hotbar_slot`: integer - the newly selected hotbar slot
* `prev_hotbar_slot`: integer - the previously selected hotbar slot, can be the same as `hotbar_slot` if the player selected the same slot
* `is_from_scrolling`: boolean - true if the input came from a scroll action, or the prev/next slot input keys
Setting-related
---------------
@ -7890,6 +7898,7 @@ For historical reasons, the use of an -s suffix in these names is inconsistent.
* `core.registered_on_modchannel_message`
* `core.registered_on_liquid_transformed`
* `core.registered_on_mapblocks_changed`
* `core.registered_on_hotbar_slot_selected`
Class reference
===============

View file

@ -2036,6 +2036,9 @@ void Game::processItemSelection(u16 *new_playeritem)
if (max_item == 0)
return;
max_item -= 1;
u16 prev_playeritem = *new_playeritem;
bool player_input_happened = false;
bool change_was_from_scrolling = false;
/* Item selection using mouse wheel
*/
@ -2053,6 +2056,10 @@ void Game::processItemSelection(u16 *new_playeritem)
if (wasKeyDown(KeyType::HOTBAR_PREV))
dir = 1;
if (dir != 0) {
player_input_happened = true;
change_was_from_scrolling = true;
}
if (dir < 0)
*new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0;
else if (dir > 0)
@ -2064,18 +2071,37 @@ void Game::processItemSelection(u16 *new_playeritem)
for (u16 i = 0; i <= max_item; i++) {
if (wasKeyDown((GameKeyType) (KeyType::SLOT_1 + i))) {
*new_playeritem = i;
player_input_happened = true;
change_was_from_scrolling = false;
break;
}
}
if (g_touchcontrols) {
std::optional<u16> selection = g_touchcontrols->getHotbarSelection();
if (selection)
if (selection) {
*new_playeritem = *selection;
player_input_happened = true;
change_was_from_scrolling = false;
}
}
// Clamp selection again in case it wasn't changed but max_item was
*new_playeritem = MYMIN(*new_playeritem, max_item);
if (player_input_happened) {
if (prev_playeritem != *new_playeritem) {
client->setPlayerItem(*new_playeritem);
}
// notify server of hotbar item selected
IHotbarSlotSelectedAction* a = new IHotbarSlotSelectedAction();
a->inv.setCurrentPlayer();
a->list = "main";
a->selected_slot = *new_playeritem;
a->prev_selected_slot = prev_playeritem;
a->from_scroll = change_was_from_scrolling;
client->inventoryAction(a);
}
}

View file

@ -104,6 +104,8 @@ InventoryAction *InventoryAction::deSerialize(std::istream &is)
a = new IDropAction(is);
} else if (type == "Craft") {
a = new ICraftAction(is);
} else if (type == "HotbarSlotSelected") {
a = new IHotbarSlotSelectedAction(is);
}
return a;
@ -1017,3 +1019,54 @@ bool getCraftingResult(Inventory *inv, ItemStack &result,
return found;
}
/*
IHotbarSlotSelectedAction
*/
IHotbarSlotSelectedAction::IHotbarSlotSelectedAction(std::istream& is)
{
std::string ts;
std::getline(is, ts, ' ');
inv.deSerialize(ts);
std::getline(is, list, ' ');
std::getline(is, ts, ' ');
selected_slot = stoi(ts);
std::getline(is, ts, ' ');
prev_selected_slot = stoi(ts);
std::getline(is, ts, ' ');
from_scroll = stoi(ts);
}
void IHotbarSlotSelectedAction::apply(InventoryManager* mgr, ServerActiveObject* player, IGameDef* gamedef)
{
Inventory* inv_hotbar = mgr->getInventory(inv);
if (!inv_hotbar) {
warningstream << "IHotbarSlotSelectedAction::apply(): FAIL: source inventory not found: "
<< "inv=\"" << inv.dump() << "\"" << std::endl;
return;
}
InventoryList* list_wield = inv_hotbar->getList(list);
/*
If the list doesn't exist
*/
if (!list_wield) {
warningstream << "IHotbarSlotSelectedAction::apply(): FAIL: source list not found: "
<< "list=\"" << list << "\"" << std::endl;
return;
}
ServerScripting* sa = PLAYER_TO_SA(player);
sa->on_hotbar_slot_selected(player, inv, list, selected_slot, prev_selected_slot, from_scroll);
}
void IHotbarSlotSelectedAction::clientApply(InventoryManager* mgr, IGameDef* gamedef)
{
// Optional operation that is run on the client to make lag less apparent.
}

View file

@ -109,7 +109,8 @@ public:
enum class IAction : u16 {
Move,
Drop,
Craft
Craft,
HotbarSlotSelected,
};
struct InventoryAction
@ -247,3 +248,35 @@ struct ICraftAction : public InventoryAction
bool getCraftingResult(Inventory *inv, ItemStack &result,
std::vector<ItemStack> &output_replacements,
bool decrementInput, IGameDef *gamedef);
struct IHotbarSlotSelectedAction : public InventoryAction
{
InventoryLocation inv;
std::string list;
s16 selected_slot = -1;
s16 prev_selected_slot = -1;
bool from_scroll = false;
IHotbarSlotSelectedAction() = default;
IHotbarSlotSelectedAction(std::istream& is);
IAction getType() const
{
return IAction::HotbarSlotSelected;
}
void serialize(std::ostream& os) const
{
os<<"HotbarSlotSelected ";
os<<inv.dump()<<" ";
os<<list<<" ";
os<<selected_slot<<" ";
os<<prev_selected_slot<<" ";
os<<(from_scroll ? "1" : "0");
}
void apply(InventoryManager* mgr, ServerActiveObject* player, IGameDef* gamedef);
void clientApply(InventoryManager* mgr, IGameDef* gamedef);
};

View file

@ -734,6 +734,14 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
if (!check_inv_access(ca->craft_inv))
return;
}
/*
Handle HotbarSlotSelected action special cases
*/
else if (a->getType() == IAction::HotbarSlotSelected) {
IHotbarSlotSelectedAction *ha = (IHotbarSlotSelectedAction*)a.get();
ha->inv.applyCurrentPlayer(player->getName());
// Note: `IHotbarSlotSelectedAction::clientApply` is empty, thus nothing to revert.
} else {
// Unknown action. Ignored.
return;

View file

@ -236,6 +236,35 @@ void ScriptApiPlayer::on_authplayer(const std::string &name, const std::string &
runCallbacks(3, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiPlayer::on_hotbar_slot_selected(
ServerActiveObject* player,
InventoryLocation& loc,
const std::string &list,
int hotbar_slot,
int prev_hotbar_slot,
bool is_from_scroll
)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_hotbar_slot_selected");
// param 1
objectrefGetOrCreate(L, player);
// param 2
InvRef::create(L, loc);
// param 3
lua_pushstring(L, list.c_str());
// param 4
lua_pushinteger(L, hotbar_slot + 1);
// param 5
lua_pushinteger(L, prev_hotbar_slot + 1);
// param 6
lua_pushboolean(L, is_from_scroll);
runCallbacks(6, RUN_CALLBACKS_MODE_LAST);
}
void ScriptApiPlayer::pushMoveArguments(
const MoveAction &ma, int count,
ServerActiveObject *player)

View file

@ -38,6 +38,14 @@ public:
void on_playerReceiveFields(ServerActiveObject *player,
const std::string &formname, const StringMap &fields);
void on_authplayer(const std::string &name, const std::string &ip, bool is_success);
void on_hotbar_slot_selected(
ServerActiveObject* player,
InventoryLocation& loc,
const std::string& list,
int hotbar_slot,
int prev_hotbar_slot,
bool is_from_scroll
);
// Player inventory callbacks
// Return number of accepted items to be moved