Lua: Add player objectives#4803
Conversation
| long pos_x = 0; | ||
| long pos_y = 0; | ||
| MapSubtlCoord pos_x = 0; | ||
| MapSubtlCoord pos_y = 0; |
There was a problem hiding this comment.
these are filled with subtile_coord_center, so the full MapCoord not a MapSubtlCoord
There was a problem hiding this comment.
Pull request overview
This PR extends the Lua scripting interface to support displaying objectives/information targeted at a specific player (instead of always broadcasting to all players), and tightens some message-id/coordinate typing in the engine helpers used by those Lua bindings.
Changes:
- Added new Lua global methods to display objectives/information to a single player (with optional zoom location or explicit subtile position).
- Updated several Lua API locals/params from
longto fixed-width/intended types (int32_t,MapSubtlCoord). - Added/updated Lua binding stub declarations in
gui.luafor the new functions.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/main.cpp | Updates objective/information helper signatures and adjusts local coordinate variable types used when creating events. |
| src/lua_api.c | Adds new Lua bindings for player-targeted objectives/information; updates various locals from long to int32_t / MapSubtlCoord. |
| src/keeperfx.hpp | Updates declarations to match the new helper function signatures. |
| config/fxdata/lua/bindings/gui.lua | Adds Lua stub/type-doc entries for the new bindings (editor hints / documentation). |
Comments suppressed due to low confidence (3)
config/fxdata/lua/bindings/gui.lua:82
- The
QuickPlayerInformationstub lists parameters as(player, slot, message, zoom_location), but the C binding expects(slot, player, message, zoom_location)(slot is arg 1 and player is arg 2). Please fix the stub signature/docs to match runtime behavior to prevent incorrect script calls and misleading editor hints.
---Works like Display_information, but instead of using a string from translations, allows to type it directly.
---@param slot integer Message slot selection. There are 256 quick message slots, and each message you're making should use a different one. Using one message slot twice will lead to the first message being lost.
---@param player Player Target player who receives the message.
---@param message string
---@param zoom_location? location
function QuickPlayerInformation(player,slot,message,zoom_location) end
src/lua_api.c:707
msg_textis fetched withlua_tostring(), which can return NULL for non-string/nil values; passing that intoprocess_objective()can lead to a crash when the string is later dereferenced. Use a checked string accessor (egluaL_checkstring()/luaL_checklstring()) or explicitly handle NULL before callingprocess_objective().
static int lua_Quick_player_objective_with_pos(lua_State* L)
{
const char* msg_text = lua_tostring(L, 1);
PlayerNumber plyr_idx = luaL_checkPlayerSingle(L, 2);
MapSubtlCoord stl_x = luaL_checkstl_x(L, 3);
MapSubtlCoord stl_y = luaL_checkstl_y(L, 4);
process_objective(msg_text, plyr_idx, 0, stl_x, stl_y);
return 0;
src/lua_api.c:720
msg_textis fetched withlua_tostring(), which can return NULL; using it as the%sargument tosnprintf()is undefined behavior and can crash. UseluaL_checkstring()(orluaL_optstring()with a safe default) before writing intogame.quick_messages.
static int lua_Quick_player_information_with_pos(lua_State* L)
{
int32_t slot = luaL_checkIntMinMax(L, 1, 0, QUICK_MESSAGES_COUNT - 1);
PlayerNumber plyr_idx = luaL_checkPlayerSingle(L, 2);
const char* msg_text = lua_tostring(L, 3);
MapSubtlCoord stl_x = luaL_checkstl_x(L, 4);
MapSubtlCoord stl_y = luaL_checkstl_y(L, 5);
snprintf(game.quick_messages[slot], MESSAGE_TEXT_LEN, "%s", msg_text);
set_quick_information(slot, plyr_idx, 0, stl_x, stl_y);
return 0;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| static int lua_Quick_player_objective(lua_State* L) | ||
| { | ||
| const char* msg_text = lua_tostring(L, 1); | ||
| PlayerNumber plyr_idx = luaL_checkPlayerSingle(L, 2); | ||
| TbMapLocation target = luaL_optLocation(L, 3); | ||
|
|
||
| process_objective(msg_text, plyr_idx, target, 0, 0); | ||
| return 0; |
| static int lua_Quick_player_information(lua_State* L) | ||
| { | ||
| int32_t slot = luaL_checkIntMinMax(L, 1, 0, QUICK_MESSAGES_COUNT - 1); | ||
| PlayerNumber plyr_idx = luaL_checkPlayerSingle(L, 2); | ||
| const char* msg_text = lua_tostring(L, 3); | ||
| TbMapLocation target = luaL_optLocation(L, 4); | ||
| snprintf(game.quick_messages[slot], MESSAGE_TEXT_LEN, "%s", msg_text); | ||
|
|
||
| set_quick_information(slot, plyr_idx, target, 0, 0); | ||
| return 0; |
|
lua_tostring comments from copilot to be scoped out, as it is every instance before this has the same structure. |
No description provided.